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