• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The WebM project authors. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the LICENSE file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS.  All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 //
9 // This sample application demonstrates how to use the matroska parser
10 // library, which allows clients to handle a matroska format file.
11 
12 #include "sample_muxer_metadata.h"
13 
14 #include <cstdio>
15 #include <string>
16 
17 #include "mkvmuxer/mkvmuxer.h"
18 #include "webvtt/vttreader.h"
19 
SampleMuxerMetadata()20 SampleMuxerMetadata::SampleMuxerMetadata() : segment_(NULL) {}
21 
Init(mkvmuxer::Segment * segment)22 bool SampleMuxerMetadata::Init(mkvmuxer::Segment* segment) {
23   if (segment == NULL || segment_ != NULL)
24     return false;
25 
26   segment_ = segment;
27   return true;
28 }
29 
Load(const char * file,Kind kind)30 bool SampleMuxerMetadata::Load(const char* file, Kind kind) {
31   if (kind == kChapters)
32     return LoadChapters(file);
33 
34   uint64_t track_num;
35 
36   if (!AddTrack(kind, &track_num)) {
37     printf("Unable to add track for WebVTT file \"%s\"\n", file);
38     return false;
39   }
40 
41   return Parse(file, kind, track_num);
42 }
43 
AddChapters()44 bool SampleMuxerMetadata::AddChapters() {
45   typedef cue_list_t::const_iterator iter_t;
46   iter_t i = chapter_cues_.begin();
47   const iter_t j = chapter_cues_.end();
48 
49   while (i != j) {
50     const cue_t& chapter = *i++;
51 
52     if (!AddChapter(chapter))
53       return false;
54   }
55 
56   return true;
57 }
58 
Write(int64_t time_ns)59 bool SampleMuxerMetadata::Write(int64_t time_ns) {
60   typedef cues_set_t::iterator iter_t;
61 
62   iter_t i = cues_set_.begin();
63   const iter_t j = cues_set_.end();
64 
65   while (i != j) {
66     const cues_set_t::value_type& v = *i;
67 
68     if (time_ns >= 0 && v > time_ns)
69       return true;  // nothing else to do just yet
70 
71     if (!v.Write(segment_)) {
72       printf("\nCould not add metadata.\n");
73       return false;  // error
74     }
75 
76     cues_set_.erase(i++);
77   }
78 
79   return true;
80 }
81 
LoadChapters(const char * file)82 bool SampleMuxerMetadata::LoadChapters(const char* file) {
83   if (!chapter_cues_.empty()) {
84     printf("Support for more than one chapters file is not yet implemented\n");
85     return false;
86   }
87 
88   cue_list_t cues;
89 
90   if (!ParseChapters(file, &cues))
91     return false;
92 
93   // TODO(matthewjheaney): support more than one chapters file
94   chapter_cues_.swap(cues);
95 
96   return true;
97 }
98 
ParseChapters(const char * file,cue_list_t * cues_ptr)99 bool SampleMuxerMetadata::ParseChapters(const char* file,
100                                         cue_list_t* cues_ptr) {
101   cue_list_t& cues = *cues_ptr;
102   cues.clear();
103 
104   libwebvtt::VttReader r;
105   int e = r.Open(file);
106 
107   if (e) {
108     printf("Unable to open WebVTT file: \"%s\"\n", file);
109     return false;
110   }
111 
112   libwebvtt::Parser p(&r);
113   e = p.Init();
114 
115   if (e < 0) {  // error
116     printf("Error parsing WebVTT file: \"%s\"\n", file);
117     return false;
118   }
119 
120   libwebvtt::Time t;
121   t.hours = -1;
122 
123   for (;;) {
124     cue_t c;
125     e = p.Parse(&c);
126 
127     if (e < 0) {  // error
128       printf("Error parsing WebVTT file: \"%s\"\n", file);
129       return false;
130     }
131 
132     if (e > 0)  // EOF
133       return true;
134 
135     if (c.start_time < t) {
136       printf("bad WebVTT cue timestamp (out-of-order)\n");
137       return false;
138     }
139 
140     if (c.stop_time < c.start_time) {
141       printf("bad WebVTT cue timestamp (stop < start)\n");
142       return false;
143     }
144 
145     t = c.start_time;
146     cues.push_back(c);
147   }
148 }
149 
AddChapter(const cue_t & cue)150 bool SampleMuxerMetadata::AddChapter(const cue_t& cue) {
151   // TODO(matthewjheaney): support language and country
152 
153   mkvmuxer::Chapter* const chapter = segment_->AddChapter();
154 
155   if (chapter == NULL) {
156     printf("Unable to add chapter\n");
157     return false;
158   }
159 
160   if (cue.identifier.empty()) {
161     chapter->set_id(NULL);
162   } else {
163     const char* const id = cue.identifier.c_str();
164     if (!chapter->set_id(id)) {
165       printf("Unable to set chapter id\n");
166       return false;
167     }
168   }
169 
170   typedef libwebvtt::presentation_t time_ms_t;
171   const time_ms_t start_time_ms = cue.start_time.presentation();
172   const time_ms_t stop_time_ms = cue.stop_time.presentation();
173 
174   enum { kNsPerMs = 1000000 };
175   const uint64_t start_time_ns = start_time_ms * kNsPerMs;
176   const uint64_t stop_time_ns = stop_time_ms * kNsPerMs;
177 
178   chapter->set_time(*segment_, start_time_ns, stop_time_ns);
179 
180   typedef libwebvtt::Cue::payload_t::const_iterator iter_t;
181   iter_t i = cue.payload.begin();
182   const iter_t j = cue.payload.end();
183 
184   std::string title;
185 
186   for (;;) {
187     title += *i++;
188 
189     if (i == j)
190       break;
191 
192     enum { kLF = '\x0A' };
193     title += kLF;
194   }
195 
196   if (!chapter->add_string(title.c_str(), NULL, NULL)) {
197     printf("Unable to set chapter title\n");
198     return false;
199   }
200 
201   return true;
202 }
203 
AddTrack(Kind kind,uint64_t * track_num)204 bool SampleMuxerMetadata::AddTrack(Kind kind, uint64_t* track_num) {
205   *track_num = 0;
206 
207   // Track number value 0 means "let muxer choose track number"
208   mkvmuxer::Track* const track = segment_->AddTrack(0);
209 
210   if (track == NULL)  // error
211     return false;
212 
213   // Return the track number value chosen by the muxer
214   *track_num = track->number();
215 
216   int type;
217   const char* codec_id;
218 
219   switch (kind) {
220     case kSubtitles:
221       type = 0x11;
222       codec_id = "D_WEBVTT/SUBTITLES";
223       break;
224 
225     case kCaptions:
226       type = 0x11;
227       codec_id = "D_WEBVTT/CAPTIONS";
228       break;
229 
230     case kDescriptions:
231       type = 0x21;
232       codec_id = "D_WEBVTT/DESCRIPTIONS";
233       break;
234 
235     case kMetadata:
236       type = 0x21;
237       codec_id = "D_WEBVTT/METADATA";
238       break;
239 
240     default:
241       return false;
242   }
243 
244   track->set_type(type);
245   track->set_codec_id(codec_id);
246 
247   // TODO(matthewjheaney): set name and language
248 
249   return true;
250 }
251 
Parse(const char * file,Kind,uint64_t track_num)252 bool SampleMuxerMetadata::Parse(const char* file, Kind /* kind */,
253                                 uint64_t track_num) {
254   libwebvtt::VttReader r;
255   int e = r.Open(file);
256 
257   if (e) {
258     printf("Unable to open WebVTT file: \"%s\"\n", file);
259     return false;
260   }
261 
262   libwebvtt::Parser p(&r);
263 
264   e = p.Init();
265 
266   if (e < 0) {  // error
267     printf("Error parsing WebVTT file: \"%s\"\n", file);
268     return false;
269   }
270 
271   SortableCue cue;
272   cue.track_num = track_num;
273 
274   libwebvtt::Time t;
275   t.hours = -1;
276 
277   for (;;) {
278     cue_t& c = cue.cue;
279     e = p.Parse(&c);
280 
281     if (e < 0) {  // error
282       printf("Error parsing WebVTT file: \"%s\"\n", file);
283       return false;
284     }
285 
286     if (e > 0)  // EOF
287       return true;
288 
289     if (c.start_time >= t) {
290       t = c.start_time;
291     } else {
292       printf("bad WebVTT cue timestamp (out-of-order)\n");
293       return false;
294     }
295 
296     if (c.stop_time < c.start_time) {
297       printf("bad WebVTT cue timestamp (stop < start)\n");
298       return false;
299     }
300 
301     cues_set_.insert(cue);
302   }
303 }
304 
MakeFrame(const cue_t & c,std::string * pf)305 void SampleMuxerMetadata::MakeFrame(const cue_t& c, std::string* pf) {
306   pf->clear();
307   WriteCueIdentifier(c.identifier, pf);
308   WriteCueSettings(c.settings, pf);
309   WriteCuePayload(c.payload, pf);
310 }
311 
WriteCueIdentifier(const std::string & identifier,std::string * pf)312 void SampleMuxerMetadata::WriteCueIdentifier(const std::string& identifier,
313                                              std::string* pf) {
314   pf->append(identifier);
315   pf->push_back('\x0A');  // LF
316 }
317 
WriteCueSettings(const cue_t::settings_t & settings,std::string * pf)318 void SampleMuxerMetadata::WriteCueSettings(const cue_t::settings_t& settings,
319                                            std::string* pf) {
320   if (settings.empty()) {
321     pf->push_back('\x0A');  // LF
322     return;
323   }
324 
325   typedef cue_t::settings_t::const_iterator iter_t;
326 
327   iter_t i = settings.begin();
328   const iter_t j = settings.end();
329 
330   for (;;) {
331     const libwebvtt::Setting& setting = *i++;
332 
333     pf->append(setting.name);
334     pf->push_back(':');
335     pf->append(setting.value);
336 
337     if (i == j)
338       break;
339 
340     pf->push_back(' ');  // separate settings with whitespace
341   }
342 
343   pf->push_back('\x0A');  // LF
344 }
345 
WriteCuePayload(const cue_t::payload_t & payload,std::string * pf)346 void SampleMuxerMetadata::WriteCuePayload(const cue_t::payload_t& payload,
347                                           std::string* pf) {
348   typedef cue_t::payload_t::const_iterator iter_t;
349 
350   iter_t i = payload.begin();
351   const iter_t j = payload.end();
352 
353   while (i != j) {
354     const std::string& line = *i++;
355     pf->append(line);
356     pf->push_back('\x0A');  // LF
357   }
358 }
359 
Write(mkvmuxer::Segment * segment) const360 bool SampleMuxerMetadata::SortableCue::Write(mkvmuxer::Segment* segment) const {
361   // Cue start time expressed in milliseconds
362   const int64_t start_ms = cue.start_time.presentation();
363 
364   // Cue start time expressed in nanoseconds (MKV time)
365   const int64_t start_ns = start_ms * 1000000;
366 
367   // Cue stop time expressed in milliseconds
368   const int64_t stop_ms = cue.stop_time.presentation();
369 
370   // Cue stop time expressed in nanonseconds
371   const int64_t stop_ns = stop_ms * 1000000;
372 
373   // Metadata blocks always specify the block duration.
374   const int64_t duration_ns = stop_ns - start_ns;
375 
376   std::string frame;
377   MakeFrame(cue, &frame);
378 
379   typedef const uint8_t* data_t;
380   const data_t buf = reinterpret_cast<data_t>(frame.data());
381   const uint64_t len = frame.length();
382 
383   mkvmuxer::Frame muxer_frame;
384   if (!muxer_frame.Init(buf, len))
385     return 0;
386   muxer_frame.set_track_number(track_num);
387   muxer_frame.set_timestamp(start_ns);
388   muxer_frame.set_duration(duration_ns);
389   muxer_frame.set_is_key(true);  // All metadata frames are keyframes.
390   return segment->AddGenericFrame(&muxer_frame);
391 }