1 // Copyright (c) 2011 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 #include <stdint.h>
9
10 #include <cstdio>
11 #include <cstdlib>
12 #include <cstring>
13 #include <list>
14 #include <memory>
15 #include <string>
16
17 // libwebm common includes.
18 #include "common/file_util.h"
19 #include "common/hdr_util.h"
20
21 // libwebm mkvparser includes
22 #include "mkvparser/mkvparser.h"
23 #include "mkvparser/mkvreader.h"
24
25 // libwebm mkvmuxer includes
26 #include "mkvmuxer/mkvmuxer.h"
27 #include "mkvmuxer/mkvmuxertypes.h"
28 #include "mkvmuxer/mkvwriter.h"
29
30 #include "sample_muxer_metadata.h"
31
32 namespace {
33
Usage()34 void Usage() {
35 printf("Usage: mkvmuxer_sample -i input -o output [options]\n");
36 printf("\n");
37 printf("Main options:\n");
38 printf(" -h | -? show help\n");
39 printf(" -video <int> >0 outputs video\n");
40 printf(" -audio <int> >0 outputs audio\n");
41 printf(" -live <int> >0 puts the muxer into live mode\n");
42 printf(" 0 puts the muxer into file mode\n");
43 printf(" -output_cues <int> >0 outputs cues element\n");
44 printf(" -cues_on_video_track <int> >0 outputs cues on video track\n");
45 printf(" -cues_on_audio_track <int> >0 outputs cues on audio track\n");
46 printf(" -max_cluster_duration <double> in seconds\n");
47 printf(" -max_cluster_size <int> in bytes\n");
48 printf(" -switch_tracks <int> >0 switches tracks in output\n");
49 printf(" -audio_track_number <int> >0 Changes the audio track number\n");
50 printf(" -video_track_number <int> >0 Changes the video track number\n");
51 printf(" -chunking <string> Chunk output\n");
52 printf(" -copy_tags <int> >0 Copies the tags\n");
53 printf(" -accurate_cluster_duration <int> ");
54 printf(">0 Writes the last frame in each cluster with Duration\n");
55 printf(" -fixed_size_cluster_timecode <int> ");
56 printf(">0 Writes the cluster timecode using exactly 8 bytes\n");
57 printf(" -copy_input_duration >0 Copies the input duration\n");
58 printf("\n");
59 printf("Video options:\n");
60 printf(" -display_width <int> Display width in pixels\n");
61 printf(" -display_height <int> Display height in pixels\n");
62 printf(" -pixel_width <int> Override pixel width\n");
63 printf(" -pixel_height <int> Override pixel height\n");
64 printf(" -projection_type <int> Set/override projection type:\n");
65 printf(" 0: Rectangular\n");
66 printf(" 1: Equirectangular\n");
67 printf(" 2: Cube map\n");
68 printf(" 3: Mesh\n");
69 printf(" -projection_file <string> Override projection private data");
70 printf(" with contents of this file\n");
71 printf(" -projection_pose_yaw <float> Projection pose yaw\n");
72 printf(" -projection_pose_pitch <float> Projection pose pitch\n");
73 printf(" -projection_pose_roll <float> Projection pose roll\n");
74 printf(" -stereo_mode <int> 3D video mode\n");
75 printf("\n");
76 printf("VP9 options:\n");
77 printf(" -profile <int> VP9 profile\n");
78 printf(" -level <int> VP9 level\n");
79 printf("\n");
80 printf("Cues options:\n");
81 printf(" -output_cues_block_number <int> >0 outputs cue block number\n");
82 printf(" -cues_before_clusters <int> >0 puts Cues before Clusters\n");
83 printf("\n");
84 printf("Metadata options:\n");
85 printf(" -webvtt-subtitles <vttfile> ");
86 printf("add WebVTT subtitles as metadata track\n");
87 printf(" -webvtt-captions <vttfile> ");
88 printf("add WebVTT captions as metadata track\n");
89 printf(" -webvtt-descriptions <vttfile> ");
90 printf("add WebVTT descriptions as metadata track\n");
91 printf(" -webvtt-metadata <vttfile> ");
92 printf("add WebVTT subtitles as metadata track\n");
93 printf(" -webvtt-chapters <vttfile> ");
94 printf("add WebVTT chapters as MKV chapters element\n");
95 }
96
97 struct MetadataFile {
98 const char* name;
99 SampleMuxerMetadata::Kind kind;
100 };
101
102 typedef std::list<MetadataFile> metadata_files_t;
103
104 // Cache the WebVTT filenames specified as command-line args.
LoadMetadataFiles(const metadata_files_t & files,SampleMuxerMetadata * metadata)105 bool LoadMetadataFiles(const metadata_files_t& files,
106 SampleMuxerMetadata* metadata) {
107 typedef metadata_files_t::const_iterator iter_t;
108
109 iter_t i = files.begin();
110 const iter_t j = files.end();
111
112 while (i != j) {
113 const metadata_files_t::value_type& v = *i++;
114
115 if (!metadata->Load(v.name, v.kind))
116 return false;
117 }
118
119 return true;
120 }
121
ParseArgWebVTT(char * argv[],int * argv_index,int argc_check,metadata_files_t * metadata_files)122 int ParseArgWebVTT(char* argv[], int* argv_index, int argc_check,
123 metadata_files_t* metadata_files) {
124 int& i = *argv_index;
125
126 enum { kCount = 5 };
127 struct Arg {
128 const char* name;
129 SampleMuxerMetadata::Kind kind;
130 };
131 const Arg args[kCount] = {
132 {"-webvtt-subtitles", SampleMuxerMetadata::kSubtitles},
133 {"-webvtt-captions", SampleMuxerMetadata::kCaptions},
134 {"-webvtt-descriptions", SampleMuxerMetadata::kDescriptions},
135 {"-webvtt-metadata", SampleMuxerMetadata::kMetadata},
136 {"-webvtt-chapters", SampleMuxerMetadata::kChapters}};
137
138 for (int idx = 0; idx < kCount; ++idx) {
139 const Arg& arg = args[idx];
140
141 if (strcmp(arg.name, argv[i]) != 0) // no match
142 continue;
143
144 ++i; // consume arg name here
145
146 if (i > argc_check) {
147 printf("missing value for %s\n", arg.name);
148 return -1; // error
149 }
150
151 MetadataFile f;
152 f.name = argv[i]; // arg value is consumed via caller's loop idx
153 f.kind = arg.kind;
154
155 metadata_files->push_back(f);
156 return 1; // successfully parsed WebVTT arg
157 }
158
159 return 0; // not a WebVTT arg
160 }
161
CopyVideoProjection(const mkvparser::Projection & parser_projection,mkvmuxer::Projection * muxer_projection)162 bool CopyVideoProjection(const mkvparser::Projection& parser_projection,
163 mkvmuxer::Projection* muxer_projection) {
164 typedef mkvmuxer::Projection::ProjectionType MuxerProjType;
165 const int kTypeNotPresent = mkvparser::Projection::kTypeNotPresent;
166 if (parser_projection.type != kTypeNotPresent) {
167 muxer_projection->set_type(
168 static_cast<MuxerProjType>(parser_projection.type));
169 }
170 if (parser_projection.private_data &&
171 parser_projection.private_data_length > 0) {
172 if (!muxer_projection->SetProjectionPrivate(
173 parser_projection.private_data,
174 parser_projection.private_data_length)) {
175 return false;
176 }
177 }
178
179 const float kValueNotPresent = mkvparser::Projection::kValueNotPresent;
180 if (parser_projection.pose_yaw != kValueNotPresent)
181 muxer_projection->set_pose_yaw(parser_projection.pose_yaw);
182 if (parser_projection.pose_pitch != kValueNotPresent)
183 muxer_projection->set_pose_pitch(parser_projection.pose_pitch);
184 if (parser_projection.pose_roll != kValueNotPresent)
185 muxer_projection->set_pose_roll(parser_projection.pose_roll);
186 return true;
187 }
188 } // end namespace
189
main(int argc,char * argv[])190 int main(int argc, char* argv[]) {
191 char* input = NULL;
192 char* output = NULL;
193
194 // Segment variables
195 bool output_video = true;
196 bool output_audio = true;
197 bool live_mode = false;
198 bool output_cues = true;
199 bool cues_before_clusters = false;
200 bool cues_on_video_track = true;
201 bool cues_on_audio_track = false;
202 uint64_t max_cluster_duration = 0;
203 uint64_t max_cluster_size = 0;
204 bool switch_tracks = false;
205 int audio_track_number = 0; // 0 tells muxer to decide.
206 int video_track_number = 0; // 0 tells muxer to decide.
207 bool chunking = false;
208 bool copy_tags = false;
209 const char* chunk_name = NULL;
210 bool accurate_cluster_duration = false;
211 bool fixed_size_cluster_timecode = false;
212 bool copy_input_duration = false;
213
214 bool output_cues_block_number = true;
215
216 uint64_t display_width = 0;
217 uint64_t display_height = 0;
218 uint64_t pixel_width = 0;
219 uint64_t pixel_height = 0;
220 uint64_t stereo_mode = 0;
221 const char* projection_file = 0;
222 int64_t projection_type = mkvparser::Projection::kTypeNotPresent;
223 float projection_pose_roll = mkvparser::Projection::kValueNotPresent;
224 float projection_pose_pitch = mkvparser::Projection::kValueNotPresent;
225 float projection_pose_yaw = mkvparser::Projection::kValueNotPresent;
226 int vp9_profile = -1; // No profile set.
227 int vp9_level = -1; // No level set.
228
229 metadata_files_t metadata_files;
230
231 const int argc_check = argc - 1;
232 for (int i = 1; i < argc; ++i) {
233 char* end;
234
235 if (!strcmp("-h", argv[i]) || !strcmp("-?", argv[i])) {
236 Usage();
237 return EXIT_SUCCESS;
238 } else if (!strcmp("-i", argv[i]) && i < argc_check) {
239 input = argv[++i];
240 } else if (!strcmp("-o", argv[i]) && i < argc_check) {
241 output = argv[++i];
242 } else if (!strcmp("-video", argv[i]) && i < argc_check) {
243 output_video = strtol(argv[++i], &end, 10) == 0 ? false : true;
244 } else if (!strcmp("-audio", argv[i]) && i < argc_check) {
245 output_audio = strtol(argv[++i], &end, 10) == 0 ? false : true;
246 } else if (!strcmp("-live", argv[i]) && i < argc_check) {
247 live_mode = strtol(argv[++i], &end, 10) == 0 ? false : true;
248 } else if (!strcmp("-output_cues", argv[i]) && i < argc_check) {
249 output_cues = strtol(argv[++i], &end, 10) == 0 ? false : true;
250 } else if (!strcmp("-cues_before_clusters", argv[i]) && i < argc_check) {
251 cues_before_clusters = strtol(argv[++i], &end, 10) == 0 ? false : true;
252 } else if (!strcmp("-cues_on_video_track", argv[i]) && i < argc_check) {
253 cues_on_video_track = strtol(argv[++i], &end, 10) == 0 ? false : true;
254 if (cues_on_video_track)
255 cues_on_audio_track = false;
256 } else if (!strcmp("-cues_on_audio_track", argv[i]) && i < argc_check) {
257 cues_on_audio_track = strtol(argv[++i], &end, 10) == 0 ? false : true;
258 if (cues_on_audio_track)
259 cues_on_video_track = false;
260 } else if (!strcmp("-max_cluster_duration", argv[i]) && i < argc_check) {
261 const double seconds = strtod(argv[++i], &end);
262 max_cluster_duration = static_cast<uint64_t>(seconds * 1000000000.0);
263 } else if (!strcmp("-max_cluster_size", argv[i]) && i < argc_check) {
264 max_cluster_size = strtol(argv[++i], &end, 10);
265 } else if (!strcmp("-switch_tracks", argv[i]) && i < argc_check) {
266 switch_tracks = strtol(argv[++i], &end, 10) == 0 ? false : true;
267 } else if (!strcmp("-audio_track_number", argv[i]) && i < argc_check) {
268 audio_track_number = static_cast<int>(strtol(argv[++i], &end, 10));
269 } else if (!strcmp("-video_track_number", argv[i]) && i < argc_check) {
270 video_track_number = static_cast<int>(strtol(argv[++i], &end, 10));
271 } else if (!strcmp("-chunking", argv[i]) && i < argc_check) {
272 chunking = true;
273 chunk_name = argv[++i];
274 } else if (!strcmp("-copy_tags", argv[i]) && i < argc_check) {
275 copy_tags = strtol(argv[++i], &end, 10) == 0 ? false : true;
276 } else if (!strcmp("-accurate_cluster_duration", argv[i]) &&
277 i < argc_check) {
278 accurate_cluster_duration =
279 strtol(argv[++i], &end, 10) == 0 ? false : true;
280 } else if (!strcmp("-fixed_size_cluster_timecode", argv[i]) &&
281 i < argc_check) {
282 fixed_size_cluster_timecode =
283 strtol(argv[++i], &end, 10) == 0 ? false : true;
284 } else if (!strcmp("-copy_input_duration", argv[i]) && i < argc_check) {
285 copy_input_duration = strtol(argv[++i], &end, 10) == 0 ? false : true;
286 } else if (!strcmp("-display_width", argv[i]) && i < argc_check) {
287 display_width = strtol(argv[++i], &end, 10);
288 } else if (!strcmp("-display_height", argv[i]) && i < argc_check) {
289 display_height = strtol(argv[++i], &end, 10);
290 } else if (!strcmp("-pixel_width", argv[i]) && i < argc_check) {
291 pixel_width = strtol(argv[++i], &end, 10);
292 } else if (!strcmp("-pixel_height", argv[i]) && i < argc_check) {
293 pixel_height = strtol(argv[++i], &end, 10);
294 } else if (!strcmp("-stereo_mode", argv[i]) && i < argc_check) {
295 stereo_mode = strtol(argv[++i], &end, 10);
296 } else if (!strcmp("-projection_type", argv[i]) && i < argc_check) {
297 projection_type = strtol(argv[++i], &end, 10);
298 } else if (!strcmp("-projection_file", argv[i]) && i < argc_check) {
299 projection_file = argv[++i];
300 } else if (!strcmp("-projection_pose_roll", argv[i]) && i < argc_check) {
301 projection_pose_roll = strtof(argv[++i], &end);
302 } else if (!strcmp("-projection_pose_pitch", argv[i]) && i < argc_check) {
303 projection_pose_pitch = strtof(argv[++i], &end);
304 } else if (!strcmp("-projection_pose_yaw", argv[i]) && i < argc_check) {
305 projection_pose_yaw = strtof(argv[++i], &end);
306 } else if (!strcmp("-profile", argv[i]) && i < argc_check) {
307 vp9_profile = static_cast<int>(strtol(argv[++i], &end, 10));
308 } else if (!strcmp("-level", argv[i]) && i < argc_check) {
309 vp9_level = static_cast<int>(strtol(argv[++i], &end, 10));
310 } else if (!strcmp("-output_cues_block_number", argv[i]) &&
311 i < argc_check) {
312 output_cues_block_number =
313 strtol(argv[++i], &end, 10) == 0 ? false : true;
314 } else if (int e = ParseArgWebVTT(argv, &i, argc_check, &metadata_files)) {
315 if (e < 0)
316 return EXIT_FAILURE;
317 }
318 }
319
320 if (input == NULL || output == NULL) {
321 Usage();
322 return EXIT_FAILURE;
323 }
324
325 // Get parser header info
326 mkvparser::MkvReader reader;
327
328 if (reader.Open(input)) {
329 printf("\n Filename is invalid or error while opening.\n");
330 return EXIT_FAILURE;
331 }
332
333 long long pos = 0;
334 mkvparser::EBMLHeader ebml_header;
335 long long ret = ebml_header.Parse(&reader, pos);
336 if (ret) {
337 printf("\n EBMLHeader::Parse() failed.");
338 return EXIT_FAILURE;
339 }
340
341 mkvparser::Segment* parser_segment_;
342 ret = mkvparser::Segment::CreateInstance(&reader, pos, parser_segment_);
343 if (ret) {
344 printf("\n Segment::CreateInstance() failed.");
345 return EXIT_FAILURE;
346 }
347
348 const std::unique_ptr<mkvparser::Segment> parser_segment(parser_segment_);
349 ret = parser_segment->Load();
350 if (ret < 0) {
351 printf("\n Segment::Load() failed.");
352 return EXIT_FAILURE;
353 }
354
355 const mkvparser::SegmentInfo* const segment_info = parser_segment->GetInfo();
356 if (segment_info == NULL) {
357 printf("\n Segment::GetInfo() failed.");
358 return EXIT_FAILURE;
359 }
360 const long long timeCodeScale = segment_info->GetTimeCodeScale();
361
362 // Set muxer header info
363 mkvmuxer::MkvWriter writer;
364
365 const std::string temp_file =
366 cues_before_clusters ? libwebm::GetTempFileName() : output;
367 if (!writer.Open(temp_file.c_str())) {
368 printf("\n Filename is invalid or error while opening.\n");
369 return EXIT_FAILURE;
370 }
371
372 // Set Segment element attributes
373 mkvmuxer::Segment muxer_segment;
374
375 if (!muxer_segment.Init(&writer)) {
376 printf("\n Could not initialize muxer segment!\n");
377 return EXIT_FAILURE;
378 }
379
380 muxer_segment.AccurateClusterDuration(accurate_cluster_duration);
381 muxer_segment.UseFixedSizeClusterTimecode(fixed_size_cluster_timecode);
382
383 if (live_mode)
384 muxer_segment.set_mode(mkvmuxer::Segment::kLive);
385 else
386 muxer_segment.set_mode(mkvmuxer::Segment::kFile);
387
388 if (chunking)
389 muxer_segment.SetChunking(true, chunk_name);
390
391 if (max_cluster_duration > 0)
392 muxer_segment.set_max_cluster_duration(max_cluster_duration);
393 if (max_cluster_size > 0)
394 muxer_segment.set_max_cluster_size(max_cluster_size);
395 muxer_segment.OutputCues(output_cues);
396
397 // Set SegmentInfo element attributes
398 mkvmuxer::SegmentInfo* const info = muxer_segment.GetSegmentInfo();
399 info->set_timecode_scale(timeCodeScale);
400 info->set_writing_app("mkvmuxer_sample");
401
402 const mkvparser::Tags* const tags = parser_segment->GetTags();
403 if (copy_tags && tags) {
404 for (int i = 0; i < tags->GetTagCount(); i++) {
405 const mkvparser::Tags::Tag* const tag = tags->GetTag(i);
406 mkvmuxer::Tag* muxer_tag = muxer_segment.AddTag();
407
408 for (int j = 0; j < tag->GetSimpleTagCount(); j++) {
409 const mkvparser::Tags::SimpleTag* const simple_tag =
410 tag->GetSimpleTag(j);
411 muxer_tag->add_simple_tag(simple_tag->GetTagName(),
412 simple_tag->GetTagString());
413 }
414 }
415 }
416
417 // Set Tracks element attributes
418 const mkvparser::Tracks* const parser_tracks = parser_segment->GetTracks();
419 unsigned long i = 0;
420 uint64_t vid_track = 0; // no track added
421 uint64_t aud_track = 0; // no track added
422
423 using mkvparser::Track;
424
425 while (i != parser_tracks->GetTracksCount()) {
426 unsigned long track_num = i++;
427 if (switch_tracks)
428 track_num = i % parser_tracks->GetTracksCount();
429
430 const Track* const parser_track = parser_tracks->GetTrackByIndex(track_num);
431
432 if (parser_track == NULL)
433 continue;
434
435 // TODO(fgalligan): Add support for language to parser.
436 const char* const track_name = parser_track->GetNameAsUTF8();
437
438 const long long track_type = parser_track->GetType();
439
440 if (track_type == Track::kVideo && output_video) {
441 // Get the video track from the parser
442 const mkvparser::VideoTrack* const pVideoTrack =
443 static_cast<const mkvparser::VideoTrack*>(parser_track);
444 const long long width = pVideoTrack->GetWidth();
445 const long long height = pVideoTrack->GetHeight();
446
447 // Add the video track to the muxer
448 vid_track = muxer_segment.AddVideoTrack(static_cast<int>(width),
449 static_cast<int>(height),
450 video_track_number);
451 if (!vid_track) {
452 printf("\n Could not add video track.\n");
453 return EXIT_FAILURE;
454 }
455
456 mkvmuxer::VideoTrack* const video = static_cast<mkvmuxer::VideoTrack*>(
457 muxer_segment.GetTrackByNumber(vid_track));
458 if (!video) {
459 printf("\n Could not get video track.\n");
460 return EXIT_FAILURE;
461 }
462
463 if (pVideoTrack->GetColour()) {
464 mkvmuxer::Colour muxer_colour;
465 if (!libwebm::CopyColour(*pVideoTrack->GetColour(), &muxer_colour))
466 return EXIT_FAILURE;
467 if (!video->SetColour(muxer_colour))
468 return EXIT_FAILURE;
469 }
470
471 if (pVideoTrack->GetProjection() ||
472 projection_type != mkvparser::Projection::kTypeNotPresent) {
473 mkvmuxer::Projection muxer_projection;
474 const mkvparser::Projection* const parser_projection =
475 pVideoTrack->GetProjection();
476 typedef mkvmuxer::Projection::ProjectionType MuxerProjType;
477 if (parser_projection &&
478 !CopyVideoProjection(*parser_projection, &muxer_projection)) {
479 printf("\n Unable to copy video projection.\n");
480 return EXIT_FAILURE;
481 }
482 // Override the values that came from parser if set on command line.
483 if (projection_type != mkvparser::Projection::kTypeNotPresent) {
484 muxer_projection.set_type(
485 static_cast<MuxerProjType>(projection_type));
486 if (projection_type == mkvparser::Projection::kRectangular &&
487 projection_file != NULL) {
488 printf("\n Rectangular projection must not have private data.\n");
489 return EXIT_FAILURE;
490 } else if ((projection_type == mkvparser::Projection::kCubeMap ||
491 projection_type == mkvparser::Projection::kMesh) &&
492 projection_file == NULL) {
493 printf("\n Mesh or CubeMap projection must have private data.\n");
494 return EXIT_FAILURE;
495 }
496 if (projection_file != NULL) {
497 std::string contents;
498 if (!libwebm::GetFileContents(projection_file, &contents) ||
499 contents.size() == 0) {
500 printf("\n Failed to read file \"%s\" or file is empty\n",
501 projection_file);
502 return EXIT_FAILURE;
503 }
504 if (!muxer_projection.SetProjectionPrivate(
505 reinterpret_cast<uint8_t*>(&contents[0]),
506 contents.size())) {
507 printf("\n Failed to SetProjectionPrivate of length %zu.\n",
508 contents.size());
509 return EXIT_FAILURE;
510 }
511 }
512 }
513 const float kValueNotPresent = mkvparser::Projection::kValueNotPresent;
514 if (projection_pose_yaw != kValueNotPresent)
515 muxer_projection.set_pose_yaw(projection_pose_yaw);
516 if (projection_pose_pitch != kValueNotPresent)
517 muxer_projection.set_pose_pitch(projection_pose_pitch);
518 if (projection_pose_roll != kValueNotPresent)
519 muxer_projection.set_pose_roll(projection_pose_roll);
520
521 if (!video->SetProjection(muxer_projection))
522 return EXIT_FAILURE;
523 }
524
525 if (track_name)
526 video->set_name(track_name);
527
528 video->set_codec_id(pVideoTrack->GetCodecId());
529
530 if (display_width > 0)
531 video->set_display_width(display_width);
532 if (display_height > 0)
533 video->set_display_height(display_height);
534 if (pixel_width > 0)
535 video->set_pixel_width(pixel_width);
536 if (pixel_height > 0)
537 video->set_pixel_height(pixel_height);
538 if (stereo_mode > 0)
539 video->SetStereoMode(stereo_mode);
540
541 const double rate = pVideoTrack->GetFrameRate();
542 if (rate > 0.0) {
543 video->set_frame_rate(rate);
544 }
545
546 size_t parser_private_size;
547 const unsigned char* const parser_private_data =
548 pVideoTrack->GetCodecPrivate(parser_private_size);
549
550 if (!strcmp(video->codec_id(), mkvmuxer::Tracks::kAv1CodecId)) {
551 if (parser_private_data == NULL || parser_private_size == 0) {
552 printf("AV1 input track has no CodecPrivate. %s is invalid.", input);
553 return EXIT_FAILURE;
554 }
555 }
556
557 if (!strcmp(video->codec_id(), mkvmuxer::Tracks::kVp9CodecId) &&
558 (vp9_profile >= 0 || vp9_level >= 0)) {
559 const int kMaxVp9PrivateSize = 6;
560 unsigned char vp9_private_data[kMaxVp9PrivateSize];
561 int vp9_private_size = 0;
562 if (vp9_profile >= 0) {
563 if (vp9_profile < 0 || vp9_profile > 3) {
564 printf("\n VP9 profile(%d) is not valid.\n", vp9_profile);
565 return EXIT_FAILURE;
566 }
567 const uint8_t kVp9ProfileId = 1;
568 const uint8_t kVp9ProfileIdLength = 1;
569 vp9_private_data[vp9_private_size++] = kVp9ProfileId;
570 vp9_private_data[vp9_private_size++] = kVp9ProfileIdLength;
571 vp9_private_data[vp9_private_size++] = vp9_profile;
572 }
573
574 if (vp9_level >= 0) {
575 const int kNumLevels = 14;
576 const int levels[kNumLevels] = {10, 11, 20, 21, 30, 31, 40,
577 41, 50, 51, 52, 60, 61, 62};
578 bool level_is_valid = false;
579 for (int i = 0; i < kNumLevels; ++i) {
580 if (vp9_level == levels[i]) {
581 level_is_valid = true;
582 break;
583 }
584 }
585 if (!level_is_valid) {
586 printf("\n VP9 level(%d) is not valid.\n", vp9_level);
587 return EXIT_FAILURE;
588 }
589 const uint8_t kVp9LevelId = 2;
590 const uint8_t kVp9LevelIdLength = 1;
591 vp9_private_data[vp9_private_size++] = kVp9LevelId;
592 vp9_private_data[vp9_private_size++] = kVp9LevelIdLength;
593 vp9_private_data[vp9_private_size++] = vp9_level;
594 }
595 if (!video->SetCodecPrivate(vp9_private_data, vp9_private_size)) {
596 printf("\n Could not add video private data.\n");
597 return EXIT_FAILURE;
598 }
599 } else if (parser_private_data && parser_private_size > 0) {
600 if (!video->SetCodecPrivate(parser_private_data, parser_private_size)) {
601 printf("\n Could not add video private data.\n");
602 return EXIT_FAILURE;
603 }
604 }
605 } else if (track_type == Track::kAudio && output_audio) {
606 // Get the audio track from the parser
607 const mkvparser::AudioTrack* const pAudioTrack =
608 static_cast<const mkvparser::AudioTrack*>(parser_track);
609 const long long channels = pAudioTrack->GetChannels();
610 const double sample_rate = pAudioTrack->GetSamplingRate();
611
612 // Add the audio track to the muxer
613 aud_track = muxer_segment.AddAudioTrack(static_cast<int>(sample_rate),
614 static_cast<int>(channels),
615 audio_track_number);
616 if (!aud_track) {
617 printf("\n Could not add audio track.\n");
618 return EXIT_FAILURE;
619 }
620
621 mkvmuxer::AudioTrack* const audio = static_cast<mkvmuxer::AudioTrack*>(
622 muxer_segment.GetTrackByNumber(aud_track));
623 if (!audio) {
624 printf("\n Could not get audio track.\n");
625 return EXIT_FAILURE;
626 }
627
628 if (track_name)
629 audio->set_name(track_name);
630
631 audio->set_codec_id(pAudioTrack->GetCodecId());
632
633 size_t private_size;
634 const unsigned char* const private_data =
635 pAudioTrack->GetCodecPrivate(private_size);
636 if (private_size > 0) {
637 if (!audio->SetCodecPrivate(private_data, private_size)) {
638 printf("\n Could not add audio private data.\n");
639 return EXIT_FAILURE;
640 }
641 }
642
643 const long long bit_depth = pAudioTrack->GetBitDepth();
644 if (bit_depth > 0)
645 audio->set_bit_depth(bit_depth);
646
647 if (pAudioTrack->GetCodecDelay())
648 audio->set_codec_delay(pAudioTrack->GetCodecDelay());
649 if (pAudioTrack->GetSeekPreRoll())
650 audio->set_seek_pre_roll(pAudioTrack->GetSeekPreRoll());
651 }
652 }
653
654 // We have created all the video and audio tracks. If any WebVTT
655 // files were specified as command-line args, then parse them and
656 // add a track to the output file corresponding to each metadata
657 // input file.
658
659 SampleMuxerMetadata metadata;
660
661 if (!metadata.Init(&muxer_segment)) {
662 printf("\n Could not initialize metadata cache.\n");
663 return EXIT_FAILURE;
664 }
665
666 if (!LoadMetadataFiles(metadata_files, &metadata))
667 return EXIT_FAILURE;
668
669 if (!metadata.AddChapters())
670 return EXIT_FAILURE;
671
672 // Set Cues element attributes
673 mkvmuxer::Cues* const cues = muxer_segment.GetCues();
674 cues->set_output_block_number(output_cues_block_number);
675 if (cues_on_video_track && vid_track)
676 muxer_segment.CuesTrack(vid_track);
677 if (cues_on_audio_track && aud_track)
678 muxer_segment.CuesTrack(aud_track);
679
680 // Write clusters
681 unsigned char* data = NULL;
682 long data_len = 0;
683
684 const mkvparser::Cluster* cluster = parser_segment->GetFirst();
685
686 while (cluster != NULL && !cluster->EOS()) {
687 const mkvparser::BlockEntry* block_entry;
688
689 long status = cluster->GetFirst(block_entry);
690
691 if (status) {
692 printf("\n Could not get first block of cluster.\n");
693 return EXIT_FAILURE;
694 }
695
696 while (block_entry != NULL && !block_entry->EOS()) {
697 const mkvparser::Block* const block = block_entry->GetBlock();
698 const long long trackNum = block->GetTrackNumber();
699 const mkvparser::Track* const parser_track =
700 parser_tracks->GetTrackByNumber(static_cast<unsigned long>(trackNum));
701
702 // When |parser_track| is NULL, it means that the track number in the
703 // Block is invalid (i.e.) the was no TrackEntry corresponding to the
704 // track number. So we reject the file.
705 if (!parser_track) {
706 return EXIT_FAILURE;
707 }
708
709 const long long track_type = parser_track->GetType();
710 const long long time_ns = block->GetTime(cluster);
711
712 // Flush any metadata frames to the output file, before we write
713 // the current block.
714 if (!metadata.Write(time_ns))
715 return EXIT_FAILURE;
716
717 if ((track_type == Track::kAudio && output_audio) ||
718 (track_type == Track::kVideo && output_video)) {
719 const int frame_count = block->GetFrameCount();
720
721 for (int i = 0; i < frame_count; ++i) {
722 const mkvparser::Block::Frame& frame = block->GetFrame(i);
723
724 if (frame.len > data_len) {
725 delete[] data;
726 data = new unsigned char[frame.len];
727 if (!data)
728 return EXIT_FAILURE;
729 data_len = frame.len;
730 }
731
732 if (frame.Read(&reader, data))
733 return EXIT_FAILURE;
734
735 mkvmuxer::Frame muxer_frame;
736 if (!muxer_frame.Init(data, frame.len))
737 return EXIT_FAILURE;
738 muxer_frame.set_track_number(track_type == Track::kAudio ? aud_track
739 : vid_track);
740 if (block->GetDiscardPadding())
741 muxer_frame.set_discard_padding(block->GetDiscardPadding());
742 muxer_frame.set_timestamp(time_ns);
743 muxer_frame.set_is_key(block->IsKey());
744 if (!muxer_segment.AddGenericFrame(&muxer_frame)) {
745 printf("\n Could not add frame.\n");
746 return EXIT_FAILURE;
747 }
748 }
749 }
750
751 status = cluster->GetNext(block_entry, block_entry);
752
753 if (status) {
754 printf("\n Could not get next block of cluster.\n");
755 return EXIT_FAILURE;
756 }
757 }
758
759 cluster = parser_segment->GetNext(cluster);
760 }
761
762 // We have exhausted all video and audio frames in the input file.
763 // Flush any remaining metadata frames to the output file.
764 if (!metadata.Write(-1))
765 return EXIT_FAILURE;
766
767 if (copy_input_duration) {
768 const double input_duration =
769 static_cast<double>(segment_info->GetDuration()) / timeCodeScale;
770 muxer_segment.set_duration(input_duration);
771 }
772
773 if (!muxer_segment.Finalize()) {
774 printf("Finalization of segment failed.\n");
775 return EXIT_FAILURE;
776 }
777
778 reader.Close();
779 writer.Close();
780
781 if (cues_before_clusters) {
782 if (reader.Open(temp_file.c_str())) {
783 printf("\n Filename is invalid or error while opening.\n");
784 return EXIT_FAILURE;
785 }
786 if (!writer.Open(output)) {
787 printf("\n Filename is invalid or error while opening.\n");
788 return EXIT_FAILURE;
789 }
790 if (!muxer_segment.CopyAndMoveCuesBeforeClusters(&reader, &writer)) {
791 printf("\n Unable to copy and move cues before clusters.\n");
792 return EXIT_FAILURE;
793 }
794 reader.Close();
795 writer.Close();
796 remove(temp_file.c_str());
797 }
798
799 delete[] data;
800
801 return EXIT_SUCCESS;
802 }
803