blob: 66521e1e90daa2192d0d7f9244d1d88e698be95b [file] [log] [blame]
Sergey Ulanov0f7815b2014-01-16 11:31:13 -08001// 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
Matthew Heaney7ef225d2012-08-14 16:40:33 -070012#include "sample_muxer_metadata.h"
Tom Fineganb6d8d922016-03-09 14:07:22 -080013
14#include <cstdio>
Matthew Heaney7ef225d2012-08-14 16:40:33 -070015#include <string>
Tom Fineganb6d8d922016-03-09 14:07:22 -080016
Tom Finegan504e0f22016-03-21 11:20:48 -070017#include "mkvmuxer/mkvmuxer.h"
Tom Finegan5f1065e2016-03-17 15:09:46 -070018#include "webvtt/vttreader.h"
Matthew Heaney7ef225d2012-08-14 16:40:33 -070019
Tom Finegane64bf752016-03-18 09:32:52 -070020namespace libwebm {
21
Vignesh Venkatasubramanian1a0130d2014-04-14 12:09:20 -070022SampleMuxerMetadata::SampleMuxerMetadata() : segment_(NULL) {}
Matthew Heaney7ef225d2012-08-14 16:40:33 -070023
Matthew Heaneycb8899a2012-10-08 12:50:41 -070024bool SampleMuxerMetadata::Init(mkvmuxer::Segment* segment) {
25 if (segment == NULL || segment_ != NULL)
26 return false;
27
28 segment_ = segment;
29 return true;
Matthew Heaney7ef225d2012-08-14 16:40:33 -070030}
31
32bool SampleMuxerMetadata::Load(const char* file, Kind kind) {
Matthew Heaneyad54bfb2012-10-22 16:20:20 -070033 if (kind == kChapters)
34 return LoadChapters(file);
35
Tom Finegan12f6dc32016-03-16 21:14:26 -070036 uint64_t track_num;
Matthew Heaney7ef225d2012-08-14 16:40:33 -070037
38 if (!AddTrack(kind, &track_num)) {
39 printf("Unable to add track for WebVTT file \"%s\"\n", file);
40 return false;
41 }
42
43 return Parse(file, kind, track_num);
44}
45
Matthew Heaneyad54bfb2012-10-22 16:20:20 -070046bool SampleMuxerMetadata::AddChapters() {
47 typedef cue_list_t::const_iterator iter_t;
48 iter_t i = chapter_cues_.begin();
49 const iter_t j = chapter_cues_.end();
50
51 while (i != j) {
52 const cue_t& chapter = *i++;
53
54 if (!AddChapter(chapter))
55 return false;
56 }
57
58 return true;
59}
60
Tom Finegan12f6dc32016-03-16 21:14:26 -070061bool SampleMuxerMetadata::Write(int64_t time_ns) {
Matthew Heaney7ef225d2012-08-14 16:40:33 -070062 typedef cues_set_t::iterator iter_t;
63
64 iter_t i = cues_set_.begin();
65 const iter_t j = cues_set_.end();
66
67 while (i != j) {
68 const cues_set_t::value_type& v = *i;
69
70 if (time_ns >= 0 && v > time_ns)
71 return true; // nothing else to do just yet
72
73 if (!v.Write(segment_)) {
74 printf("\nCould not add metadata.\n");
75 return false; // error
76 }
77
78 cues_set_.erase(i++);
79 }
80
81 return true;
82}
83
Matthew Heaneyad54bfb2012-10-22 16:20:20 -070084bool SampleMuxerMetadata::LoadChapters(const char* file) {
85 if (!chapter_cues_.empty()) {
86 printf("Support for more than one chapters file is not yet implemented\n");
87 return false;
88 }
89
90 cue_list_t cues;
91
92 if (!ParseChapters(file, &cues))
93 return false;
94
95 // TODO(matthewjheaney): support more than one chapters file
96 chapter_cues_.swap(cues);
97
98 return true;
99}
100
Vignesh Venkatasubramanian1a0130d2014-04-14 12:09:20 -0700101bool SampleMuxerMetadata::ParseChapters(const char* file,
102 cue_list_t* cues_ptr) {
Matthew Heaneyad54bfb2012-10-22 16:20:20 -0700103 cue_list_t& cues = *cues_ptr;
104 cues.clear();
105
106 libwebvtt::VttReader r;
107 int e = r.Open(file);
108
109 if (e) {
110 printf("Unable to open WebVTT file: \"%s\"\n", file);
111 return false;
112 }
113
114 libwebvtt::Parser p(&r);
115 e = p.Init();
116
117 if (e < 0) { // error
118 printf("Error parsing WebVTT file: \"%s\"\n", file);
119 return false;
120 }
121
122 libwebvtt::Time t;
123 t.hours = -1;
124
125 for (;;) {
126 cue_t c;
127 e = p.Parse(&c);
128
129 if (e < 0) { // error
130 printf("Error parsing WebVTT file: \"%s\"\n", file);
131 return false;
132 }
133
134 if (e > 0) // EOF
135 return true;
136
137 if (c.start_time < t) {
138 printf("bad WebVTT cue timestamp (out-of-order)\n");
139 return false;
140 }
141
142 if (c.stop_time < c.start_time) {
143 printf("bad WebVTT cue timestamp (stop < start)\n");
144 return false;
145 }
146
147 t = c.start_time;
148 cues.push_back(c);
149 }
150}
151
152bool SampleMuxerMetadata::AddChapter(const cue_t& cue) {
153 // TODO(matthewjheaney): support language and country
154
155 mkvmuxer::Chapter* const chapter = segment_->AddChapter();
156
157 if (chapter == NULL) {
158 printf("Unable to add chapter\n");
159 return false;
160 }
161
162 if (cue.identifier.empty()) {
163 chapter->set_id(NULL);
164 } else {
165 const char* const id = cue.identifier.c_str();
166 if (!chapter->set_id(id)) {
167 printf("Unable to set chapter id\n");
168 return false;
169 }
170 }
171
172 typedef libwebvtt::presentation_t time_ms_t;
173 const time_ms_t start_time_ms = cue.start_time.presentation();
174 const time_ms_t stop_time_ms = cue.stop_time.presentation();
175
176 enum { kNsPerMs = 1000000 };
Tom Finegan12f6dc32016-03-16 21:14:26 -0700177 const uint64_t start_time_ns = start_time_ms * kNsPerMs;
178 const uint64_t stop_time_ns = stop_time_ms * kNsPerMs;
Matthew Heaneyad54bfb2012-10-22 16:20:20 -0700179
180 chapter->set_time(*segment_, start_time_ns, stop_time_ns);
181
182 typedef libwebvtt::Cue::payload_t::const_iterator iter_t;
183 iter_t i = cue.payload.begin();
184 const iter_t j = cue.payload.end();
185
Vignesh Venkatasubramanian4ac7b752013-06-13 15:25:03 -0700186 std::string title;
Matthew Heaneyad54bfb2012-10-22 16:20:20 -0700187
188 for (;;) {
189 title += *i++;
190
191 if (i == j)
192 break;
193
194 enum { kLF = '\x0A' };
195 title += kLF;
196 }
197
198 if (!chapter->add_string(title.c_str(), NULL, NULL)) {
199 printf("Unable to set chapter title\n");
200 return false;
201 }
202
203 return true;
204}
205
Tom Finegan12f6dc32016-03-16 21:14:26 -0700206bool SampleMuxerMetadata::AddTrack(Kind kind, uint64_t* track_num) {
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700207 *track_num = 0;
208
209 // Track number value 0 means "let muxer choose track number"
210 mkvmuxer::Track* const track = segment_->AddTrack(0);
211
212 if (track == NULL) // error
213 return false;
214
215 // Return the track number value chosen by the muxer
216 *track_num = track->number();
217
218 int type;
219 const char* codec_id;
220
221 switch (kind) {
Matthew Heaneycb8899a2012-10-08 12:50:41 -0700222 case kSubtitles:
223 type = 0x11;
224 codec_id = "D_WEBVTT/SUBTITLES";
225 break;
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700226
Matthew Heaneycb8899a2012-10-08 12:50:41 -0700227 case kCaptions:
228 type = 0x11;
229 codec_id = "D_WEBVTT/CAPTIONS";
230 break;
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700231
Matthew Heaneycb8899a2012-10-08 12:50:41 -0700232 case kDescriptions:
233 type = 0x21;
234 codec_id = "D_WEBVTT/DESCRIPTIONS";
235 break;
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700236
Matthew Heaneycb8899a2012-10-08 12:50:41 -0700237 case kMetadata:
238 type = 0x21;
239 codec_id = "D_WEBVTT/METADATA";
240 break;
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700241
Matthew Heaneycb8899a2012-10-08 12:50:41 -0700242 default:
243 return false;
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700244 }
245
246 track->set_type(type);
247 track->set_codec_id(codec_id);
248
249 // TODO(matthewjheaney): set name and language
250
251 return true;
252}
253
Vignesh Venkatasubramanian1a0130d2014-04-14 12:09:20 -0700254bool SampleMuxerMetadata::Parse(const char* file, Kind /* kind */,
Tom Finegan12f6dc32016-03-16 21:14:26 -0700255 uint64_t track_num) {
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700256 libwebvtt::VttReader r;
257 int e = r.Open(file);
258
259 if (e) {
260 printf("Unable to open WebVTT file: \"%s\"\n", file);
261 return false;
262 }
263
264 libwebvtt::Parser p(&r);
265
266 e = p.Init();
267
268 if (e < 0) { // error
269 printf("Error parsing WebVTT file: \"%s\"\n", file);
270 return false;
271 }
272
273 SortableCue cue;
274 cue.track_num = track_num;
275
276 libwebvtt::Time t;
277 t.hours = -1;
278
279 for (;;) {
280 cue_t& c = cue.cue;
281 e = p.Parse(&c);
282
283 if (e < 0) { // error
284 printf("Error parsing WebVTT file: \"%s\"\n", file);
285 return false;
286 }
287
288 if (e > 0) // EOF
289 return true;
290
291 if (c.start_time >= t) {
292 t = c.start_time;
293 } else {
294 printf("bad WebVTT cue timestamp (out-of-order)\n");
295 return false;
296 }
297
298 if (c.stop_time < c.start_time) {
299 printf("bad WebVTT cue timestamp (stop < start)\n");
300 return false;
301 }
302
303 cues_set_.insert(cue);
304 }
305}
306
Vignesh Venkatasubramanian09dd90f2013-06-12 11:23:35 -0700307void SampleMuxerMetadata::MakeFrame(const cue_t& c, std::string* pf) {
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700308 pf->clear();
309 WriteCueIdentifier(c.identifier, pf);
310 WriteCueSettings(c.settings, pf);
311 WriteCuePayload(c.payload, pf);
312}
313
Vignesh Venkatasubramanian1a0130d2014-04-14 12:09:20 -0700314void SampleMuxerMetadata::WriteCueIdentifier(const std::string& identifier,
315 std::string* pf) {
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700316 pf->append(identifier);
317 pf->push_back('\x0A'); // LF
318}
319
Vignesh Venkatasubramanian1a0130d2014-04-14 12:09:20 -0700320void SampleMuxerMetadata::WriteCueSettings(const cue_t::settings_t& settings,
321 std::string* pf) {
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700322 if (settings.empty()) {
323 pf->push_back('\x0A'); // LF
324 return;
325 }
326
327 typedef cue_t::settings_t::const_iterator iter_t;
328
329 iter_t i = settings.begin();
330 const iter_t j = settings.end();
331
332 for (;;) {
333 const libwebvtt::Setting& setting = *i++;
334
335 pf->append(setting.name);
336 pf->push_back(':');
337 pf->append(setting.value);
338
339 if (i == j)
340 break;
341
342 pf->push_back(' '); // separate settings with whitespace
343 }
344
345 pf->push_back('\x0A'); // LF
346}
347
Vignesh Venkatasubramanian1a0130d2014-04-14 12:09:20 -0700348void SampleMuxerMetadata::WriteCuePayload(const cue_t::payload_t& payload,
349 std::string* pf) {
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700350 typedef cue_t::payload_t::const_iterator iter_t;
351
352 iter_t i = payload.begin();
353 const iter_t j = payload.end();
354
355 while (i != j) {
Vignesh Venkatasubramanian09dd90f2013-06-12 11:23:35 -0700356 const std::string& line = *i++;
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700357 pf->append(line);
358 pf->push_back('\x0A'); // LF
359 }
360}
361
Vignesh Venkatasubramanian1a0130d2014-04-14 12:09:20 -0700362bool SampleMuxerMetadata::SortableCue::Write(mkvmuxer::Segment* segment) const {
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700363 // Cue start time expressed in milliseconds
Tom Finegan12f6dc32016-03-16 21:14:26 -0700364 const int64_t start_ms = cue.start_time.presentation();
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700365
366 // Cue start time expressed in nanoseconds (MKV time)
Tom Finegan12f6dc32016-03-16 21:14:26 -0700367 const int64_t start_ns = start_ms * 1000000;
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700368
369 // Cue stop time expressed in milliseconds
Tom Finegan12f6dc32016-03-16 21:14:26 -0700370 const int64_t stop_ms = cue.stop_time.presentation();
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700371
372 // Cue stop time expressed in nanonseconds
Tom Finegan12f6dc32016-03-16 21:14:26 -0700373 const int64_t stop_ns = stop_ms * 1000000;
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700374
375 // Metadata blocks always specify the block duration.
Tom Finegan12f6dc32016-03-16 21:14:26 -0700376 const int64_t duration_ns = stop_ns - start_ns;
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700377
Vignesh Venkatasubramanian09dd90f2013-06-12 11:23:35 -0700378 std::string frame;
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700379 MakeFrame(cue, &frame);
380
Tom Finegan12f6dc32016-03-16 21:14:26 -0700381 typedef const uint8_t* data_t;
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700382 const data_t buf = reinterpret_cast<data_t>(frame.data());
Tom Finegan12f6dc32016-03-16 21:14:26 -0700383 const uint64_t len = frame.length();
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700384
Vignesh Venkatasubramaniana9e48192015-05-07 16:25:18 -0700385 mkvmuxer::Frame muxer_frame;
386 if (!muxer_frame.Init(buf, len))
387 return 0;
388 muxer_frame.set_track_number(track_num);
389 muxer_frame.set_timestamp(start_ns);
390 muxer_frame.set_duration(duration_ns);
391 muxer_frame.set_is_key(true); // All metadata frames are keyframes.
392 return segment->AddGenericFrame(&muxer_frame);
Matthew Heaney7ef225d2012-08-14 16:40:33 -0700393}
Tom Finegane64bf752016-03-18 09:32:52 -0700394
395} // namespace libwebm