• 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 #include <inttypes.h>
9 #include <stdint.h>
10 
11 #include <cstdlib>
12 #include <cstring>
13 #include <limits>
14 #include <memory>
15 #include <queue>
16 #include <string>
17 #include <vector>
18 
19 #include "common/hdr_util.h"
20 #include "common/indent.h"
21 #include "common/vp9_header_parser.h"
22 #include "common/vp9_level_stats.h"
23 #include "common/webm_constants.h"
24 #include "common/webm_endian.h"
25 
26 #include "mkvparser/mkvparser.h"
27 #include "mkvparser/mkvreader.h"
28 
29 namespace {
30 
31 using libwebm::Indent;
32 using libwebm::kNanosecondsPerSecond;
33 using libwebm::kNanosecondsPerSecondi;
34 using mkvparser::ContentEncoding;
35 using std::string;
36 using std::wstring;
37 
38 const char VERSION_STRING[] = "1.0.4.5";
39 
40 struct Options {
41   Options();
42 
43   // Returns true if |value| matches -|option| or -no|option|.
44   static bool MatchesBooleanOption(const string& option, const string& value);
45 
46   // Set all of the member variables to |value|.
47   void SetAll(bool value);
48 
49   bool output_video;
50   bool output_audio;
51   bool output_size;
52   bool output_offset;
53   bool output_seconds;
54   bool output_ebml_header;
55   bool output_segment;
56   bool output_seekhead;
57   bool output_segment_info;
58   bool output_tracks;
59   bool output_clusters;
60   bool output_blocks;
61   bool output_codec_info;
62   bool output_clusters_size;
63   bool output_encrypted_info;
64   bool output_cues;
65   bool output_frame_stats;
66   bool output_vp9_level;
67 };
68 
Options()69 Options::Options()
70     : output_video(true),
71       output_audio(true),
72       output_size(false),
73       output_offset(false),
74       output_seconds(true),
75       output_ebml_header(true),
76       output_segment(true),
77       output_seekhead(false),
78       output_segment_info(true),
79       output_tracks(true),
80       output_clusters(false),
81       output_blocks(false),
82       output_codec_info(false),
83       output_clusters_size(false),
84       output_encrypted_info(false),
85       output_cues(false),
86       output_frame_stats(false),
87       output_vp9_level(false) {}
88 
SetAll(bool value)89 void Options::SetAll(bool value) {
90   output_video = value;
91   output_audio = value;
92   output_size = value;
93   output_offset = value;
94   output_ebml_header = value;
95   output_seconds = value;
96   output_segment = value;
97   output_segment_info = value;
98   output_tracks = value;
99   output_clusters = value;
100   output_blocks = value;
101   output_codec_info = value;
102   output_clusters_size = value;
103   output_encrypted_info = value;
104   output_cues = value;
105   output_frame_stats = value;
106   output_vp9_level = value;
107 }
108 
MatchesBooleanOption(const string & option,const string & value)109 bool Options::MatchesBooleanOption(const string& option, const string& value) {
110   const string opt = "-" + option;
111   const string noopt = "-no" + option;
112   return value == opt || value == noopt;
113 }
114 
115 struct FrameStats {
FrameStats__anon2b6df5130111::FrameStats116   FrameStats()
117       : frames(0),
118         displayed_frames(0),
119         first_altref(true),
120         frames_since_last_altref(0),
121         minimum_altref_distance(std::numeric_limits<int>::max()),
122         min_altref_end_ns(0),
123         max_window_size(0),
124         max_window_end_ns(0) {}
125 
126   int frames;
127   int displayed_frames;
128 
129   bool first_altref;
130   int frames_since_last_altref;
131   int minimum_altref_distance;
132   int64_t min_altref_end_ns;
133 
134   std::queue<int64_t> window;
135   int64_t max_window_size;
136   int64_t max_window_end_ns;
137 };
138 
Usage()139 void Usage() {
140   printf("Usage: webm_info [options] -i input\n");
141   printf("\n");
142   printf("Main options:\n");
143   printf("  -h | -?               show help\n");
144   printf("  -v                    show version\n");
145   printf("  -all                  Enable all output options.\n");
146   printf("  -video                Output video tracks (true)\n");
147   printf("  -audio                Output audio tracks (true)\n");
148   printf("  -size                 Output element sizes (false)\n");
149   printf("  -offset               Output element offsets (false)\n");
150   printf("  -times_seconds        Output times as seconds (true)\n");
151   printf("  -ebml_header          Output EBML header (true)\n");
152   printf("  -segment              Output Segment (true)\n");
153   printf("  -seekhead             Output SeekHead (false)\n");
154   printf("  -segment_info         Output SegmentInfo (true)\n");
155   printf("  -tracks               Output Tracks (true)\n");
156   printf("  -clusters             Output Clusters (false)\n");
157   printf("  -blocks               Output Blocks (false)\n");
158   printf("  -codec_info           Output video codec information (false)\n");
159   printf("  -clusters_size        Output Total Clusters size (false)\n");
160   printf("  -encrypted_info       Output encrypted frame info (false)\n");
161   printf("  -cues                 Output Cues entries (false)\n");
162   printf("  -frame_stats          Output frame stats (VP9)(false)\n");
163   printf("  -vp9_level            Output VP9 level(false)\n");
164   printf("\nOutput options may be negated by prefixing 'no'.\n");
165 }
166 
167 // TODO(fgalligan): Add support for non-ascii.
UTF8ToWideString(const char * str)168 wstring UTF8ToWideString(const char* str) {
169   wstring wstr;
170 
171   if (str == NULL)
172     return wstr;
173 
174   string temp_str(str, strlen(str));
175   wstr.assign(temp_str.begin(), temp_str.end());
176 
177   return wstr;
178 }
179 
ToString(const char * str)180 string ToString(const char* str) { return string((str == NULL) ? "" : str); }
181 
OutputEBMLHeader(const mkvparser::EBMLHeader & ebml,FILE * o,Indent * indent)182 void OutputEBMLHeader(const mkvparser::EBMLHeader& ebml, FILE* o,
183                       Indent* indent) {
184   fprintf(o, "EBML Header:\n");
185   indent->Adjust(libwebm::kIncreaseIndent);
186   fprintf(o, "%sEBMLVersion       : %lld\n", indent->indent_str().c_str(),
187           ebml.m_version);
188   fprintf(o, "%sEBMLReadVersion   : %lld\n", indent->indent_str().c_str(),
189           ebml.m_readVersion);
190   fprintf(o, "%sEBMLMaxIDLength   : %lld\n", indent->indent_str().c_str(),
191           ebml.m_maxIdLength);
192   fprintf(o, "%sEBMLMaxSizeLength : %lld\n", indent->indent_str().c_str(),
193           ebml.m_maxSizeLength);
194   fprintf(o, "%sDoc Type          : %s\n", indent->indent_str().c_str(),
195           ebml.m_docType);
196   fprintf(o, "%sDocTypeVersion    : %lld\n", indent->indent_str().c_str(),
197           ebml.m_docTypeVersion);
198   fprintf(o, "%sDocTypeReadVersion: %lld\n", indent->indent_str().c_str(),
199           ebml.m_docTypeReadVersion);
200   indent->Adjust(libwebm::kDecreaseIndent);
201 }
202 
OutputSegment(const mkvparser::Segment & segment,const Options & options,FILE * o)203 void OutputSegment(const mkvparser::Segment& segment, const Options& options,
204                    FILE* o) {
205   fprintf(o, "Segment:");
206   if (options.output_offset)
207     fprintf(o, "  @: %lld", segment.m_element_start);
208   if (options.output_size)
209     fprintf(o, "  size: %lld",
210             segment.m_size + segment.m_start - segment.m_element_start);
211   fprintf(o, "\n");
212 }
213 
OutputSeekHead(const mkvparser::Segment & segment,const Options & options,FILE * o,Indent * indent)214 bool OutputSeekHead(const mkvparser::Segment& segment, const Options& options,
215                     FILE* o, Indent* indent) {
216   const mkvparser::SeekHead* const seekhead = segment.GetSeekHead();
217   if (!seekhead) {
218     // SeekHeads are optional.
219     return true;
220   }
221 
222   fprintf(o, "%sSeekHead:", indent->indent_str().c_str());
223   if (options.output_offset)
224     fprintf(o, "  @: %lld", seekhead->m_element_start);
225   if (options.output_size)
226     fprintf(o, "  size: %lld", seekhead->m_element_size);
227   fprintf(o, "\n");
228 
229   indent->Adjust(libwebm::kIncreaseIndent);
230 
231   for (int i = 0; i < seekhead->GetCount(); ++i) {
232     const mkvparser::SeekHead::Entry* const entry = seekhead->GetEntry(i);
233     if (!entry) {
234       fprintf(stderr, "Error retrieving SeekHead entry #%d\n", i);
235       return false;
236     }
237 
238     fprintf(o, "%sEntry[%d]", indent->indent_str().c_str(), i);
239     if (options.output_offset)
240       fprintf(o, "  @: %lld", entry->element_start);
241     if (options.output_size)
242       fprintf(o, "  size: %lld", entry->element_size);
243     fprintf(o, "\n");
244 
245     indent->Adjust(libwebm::kIncreaseIndent);
246     std::string entry_indent = indent->indent_str();
247     // TODO(jzern): 1) known ids could be stringified. 2) ids could be
248     // reencoded to EBML for ease of lookup.
249     fprintf(o, "%sSeek ID       : %llx\n", entry_indent.c_str(), entry->id);
250     fprintf(o, "%sSeek position : %lld\n", entry_indent.c_str(), entry->pos);
251     indent->Adjust(libwebm::kDecreaseIndent);
252   }
253 
254   for (int i = 0; i < seekhead->GetVoidElementCount(); ++i) {
255     const mkvparser::SeekHead::VoidElement* const entry =
256         seekhead->GetVoidElement(i);
257     if (!entry) {
258       fprintf(stderr, "Error retrieving SeekHead void element #%d\n", i);
259       return false;
260     }
261 
262     fprintf(o, "%sVoid element[%d]", indent->indent_str().c_str(), i);
263     if (options.output_offset)
264       fprintf(o, "  @: %lld", entry->element_start);
265     if (options.output_size)
266       fprintf(o, "  size: %lld", entry->element_size);
267     fprintf(o, "\n");
268   }
269 
270   indent->Adjust(libwebm::kDecreaseIndent);
271   return true;
272 }
273 
OutputSegmentInfo(const mkvparser::Segment & segment,const Options & options,FILE * o,Indent * indent)274 bool OutputSegmentInfo(const mkvparser::Segment& segment,
275                        const Options& options, FILE* o, Indent* indent) {
276   const mkvparser::SegmentInfo* const segment_info = segment.GetInfo();
277   if (!segment_info) {
278     fprintf(stderr, "SegmentInfo was NULL.\n");
279     return false;
280   }
281 
282   const int64_t timecode_scale = segment_info->GetTimeCodeScale();
283   const int64_t duration_ns = segment_info->GetDuration();
284   const wstring title = UTF8ToWideString(segment_info->GetTitleAsUTF8());
285   const wstring muxing_app =
286       UTF8ToWideString(segment_info->GetMuxingAppAsUTF8());
287   const wstring writing_app =
288       UTF8ToWideString(segment_info->GetWritingAppAsUTF8());
289 
290   fprintf(o, "%sSegmentInfo:", indent->indent_str().c_str());
291   if (options.output_offset)
292     fprintf(o, "  @: %lld", segment_info->m_element_start);
293   if (options.output_size)
294     fprintf(o, "  size: %lld", segment_info->m_element_size);
295   fprintf(o, "\n");
296 
297   indent->Adjust(libwebm::kIncreaseIndent);
298   fprintf(o, "%sTimecodeScale : %" PRId64 " \n", indent->indent_str().c_str(),
299           timecode_scale);
300   if (options.output_seconds)
301     fprintf(o, "%sDuration(secs): %g\n", indent->indent_str().c_str(),
302             duration_ns / kNanosecondsPerSecond);
303   else
304     fprintf(o, "%sDuration(nano): %" PRId64 "\n", indent->indent_str().c_str(),
305             duration_ns);
306 
307   if (!title.empty())
308     fprintf(o, "%sTitle         : %ls\n", indent->indent_str().c_str(),
309             title.c_str());
310   if (!muxing_app.empty())
311     fprintf(o, "%sMuxingApp     : %ls\n", indent->indent_str().c_str(),
312             muxing_app.c_str());
313   if (!writing_app.empty())
314     fprintf(o, "%sWritingApp    : %ls\n", indent->indent_str().c_str(),
315             writing_app.c_str());
316   indent->Adjust(libwebm::kDecreaseIndent);
317   return true;
318 }
319 
OutputTracks(const mkvparser::Segment & segment,const Options & options,FILE * o,Indent * indent)320 bool OutputTracks(const mkvparser::Segment& segment, const Options& options,
321                   FILE* o, Indent* indent) {
322   const mkvparser::Tracks* const tracks = segment.GetTracks();
323   if (!tracks) {
324     fprintf(stderr, "Tracks was NULL.\n");
325     return false;
326   }
327 
328   fprintf(o, "%sTracks:", indent->indent_str().c_str());
329   if (options.output_offset)
330     fprintf(o, "  @: %lld", tracks->m_element_start);
331   if (options.output_size)
332     fprintf(o, "  size: %lld", tracks->m_element_size);
333   fprintf(o, "\n");
334 
335   unsigned int i = 0;
336   const unsigned long j = tracks->GetTracksCount();
337   while (i != j) {
338     const mkvparser::Track* const track = tracks->GetTrackByIndex(i++);
339     if (track == NULL)
340       continue;
341 
342     indent->Adjust(libwebm::kIncreaseIndent);
343     fprintf(o, "%sTrack:", indent->indent_str().c_str());
344     if (options.output_offset)
345       fprintf(o, "  @: %lld", track->m_element_start);
346     if (options.output_size)
347       fprintf(o, "  size: %lld", track->m_element_size);
348     fprintf(o, "\n");
349 
350     const int64_t track_type = track->GetType();
351     const int64_t track_number = track->GetNumber();
352     const wstring track_name = UTF8ToWideString(track->GetNameAsUTF8());
353 
354     indent->Adjust(libwebm::kIncreaseIndent);
355     fprintf(o, "%sTrackType   : %" PRId64 "\n", indent->indent_str().c_str(),
356             track_type);
357     fprintf(o, "%sTrackNumber : %" PRId64 "\n", indent->indent_str().c_str(),
358             track_number);
359     if (!track_name.empty())
360       fprintf(o, "%sName        : %ls\n", indent->indent_str().c_str(),
361               track_name.c_str());
362 
363     const char* const codec_id = track->GetCodecId();
364     if (codec_id)
365       fprintf(o, "%sCodecID     : %s\n", indent->indent_str().c_str(),
366               codec_id);
367 
368     const wstring codec_name = UTF8ToWideString(track->GetCodecNameAsUTF8());
369     if (!codec_name.empty())
370       fprintf(o, "%sCodecName   : %ls\n", indent->indent_str().c_str(),
371               codec_name.c_str());
372 
373     size_t private_size;
374     const unsigned char* const private_data =
375         track->GetCodecPrivate(private_size);
376     if (private_data) {
377       fprintf(o, "%sPrivateData(size): %d\n", indent->indent_str().c_str(),
378               static_cast<int>(private_size));
379 
380       if (track_type == mkvparser::Track::kVideo) {
381         const std::string codec_id = ToString(track->GetCodecId());
382         const std::string v_vp9 = "V_VP9";
383         if (codec_id == v_vp9) {
384           libwebm::Vp9CodecFeatures features;
385           if (!libwebm::ParseVpxCodecPrivate(private_data,
386                                              static_cast<int32_t>(private_size),
387                                              &features)) {
388             fprintf(stderr, "Error parsing VpxCodecPrivate.\n");
389             return false;
390           }
391           if (features.profile != -1)
392             fprintf(o, "%sVP9 profile            : %d\n",
393                     indent->indent_str().c_str(), features.profile);
394           if (features.level != -1)
395             fprintf(o, "%sVP9 level              : %d\n",
396                     indent->indent_str().c_str(), features.level);
397           if (features.bit_depth != -1)
398             fprintf(o, "%sVP9 bit_depth          : %d\n",
399                     indent->indent_str().c_str(), features.bit_depth);
400           if (features.chroma_subsampling != -1)
401             fprintf(o, "%sVP9 chroma subsampling : %d\n",
402                     indent->indent_str().c_str(), features.chroma_subsampling);
403         }
404       }
405     }
406 
407     const uint64_t default_duration = track->GetDefaultDuration();
408     if (default_duration > 0)
409       fprintf(o, "%sDefaultDuration: %" PRIu64 "\n",
410               indent->indent_str().c_str(), default_duration);
411 
412     if (track->GetContentEncodingCount() > 0) {
413       // Only check the first content encoding.
414       const ContentEncoding* const encoding =
415           track->GetContentEncodingByIndex(0);
416       if (!encoding) {
417         printf("Could not get first ContentEncoding.\n");
418         return false;
419       }
420 
421       fprintf(o, "%sContentEncodingOrder : %lld\n",
422               indent->indent_str().c_str(), encoding->encoding_order());
423       fprintf(o, "%sContentEncodingScope : %lld\n",
424               indent->indent_str().c_str(), encoding->encoding_scope());
425       fprintf(o, "%sContentEncodingType  : %lld\n",
426               indent->indent_str().c_str(), encoding->encoding_type());
427 
428       if (encoding->GetEncryptionCount() > 0) {
429         // Only check the first encryption.
430         const ContentEncoding::ContentEncryption* const encryption =
431             encoding->GetEncryptionByIndex(0);
432         if (!encryption) {
433           printf("Could not get first ContentEncryption.\n");
434           return false;
435         }
436 
437         fprintf(o, "%sContentEncAlgo       : %lld\n",
438                 indent->indent_str().c_str(), encryption->algo);
439 
440         if (encryption->key_id_len > 0) {
441           fprintf(o, "%sContentEncKeyID      : ", indent->indent_str().c_str());
442           for (int k = 0; k < encryption->key_id_len; ++k) {
443             fprintf(o, "0x%02x, ", encryption->key_id[k]);
444           }
445           fprintf(o, "\n");
446         }
447 
448         if (encryption->signature_len > 0) {
449           fprintf(o, "%sContentSignature     : 0x",
450                   indent->indent_str().c_str());
451           for (int k = 0; k < encryption->signature_len; ++k) {
452             fprintf(o, "%x", encryption->signature[k]);
453           }
454           fprintf(o, "\n");
455         }
456 
457         if (encryption->sig_key_id_len > 0) {
458           fprintf(o, "%sContentSigKeyID      : 0x",
459                   indent->indent_str().c_str());
460           for (int k = 0; k < encryption->sig_key_id_len; ++k) {
461             fprintf(o, "%x", encryption->sig_key_id[k]);
462           }
463           fprintf(o, "\n");
464         }
465 
466         fprintf(o, "%sContentSigAlgo       : %lld\n",
467                 indent->indent_str().c_str(), encryption->sig_algo);
468         fprintf(o, "%sContentSigHashAlgo   : %lld\n",
469                 indent->indent_str().c_str(), encryption->sig_hash_algo);
470 
471         const ContentEncoding::ContentEncAESSettings& aes =
472             encryption->aes_settings;
473         fprintf(o, "%sCipherMode           : %lld\n",
474                 indent->indent_str().c_str(), aes.cipher_mode);
475       }
476     }
477 
478     if (track_type == mkvparser::Track::kVideo) {
479       const mkvparser::VideoTrack* const video_track =
480           static_cast<const mkvparser::VideoTrack*>(track);
481       const int64_t width = video_track->GetWidth();
482       const int64_t height = video_track->GetHeight();
483       const int64_t display_width = video_track->GetDisplayWidth();
484       const int64_t display_height = video_track->GetDisplayHeight();
485       const int64_t display_unit = video_track->GetDisplayUnit();
486       const double frame_rate = video_track->GetFrameRate();
487       fprintf(o, "%sPixelWidth  : %" PRId64 "\n", indent->indent_str().c_str(),
488               width);
489       fprintf(o, "%sPixelHeight : %" PRId64 "\n", indent->indent_str().c_str(),
490               height);
491       if (frame_rate > 0.0)
492         fprintf(o, "%sFrameRate   : %g\n", indent->indent_str().c_str(),
493                 video_track->GetFrameRate());
494       if (display_unit > 0 || display_width != width ||
495           display_height != height) {
496         fprintf(o, "%sDisplayWidth  : %" PRId64 "\n",
497                 indent->indent_str().c_str(), display_width);
498         fprintf(o, "%sDisplayHeight : %" PRId64 "\n",
499                 indent->indent_str().c_str(), display_height);
500         fprintf(o, "%sDisplayUnit   : %" PRId64 "\n",
501                 indent->indent_str().c_str(), display_unit);
502       }
503 
504       const mkvparser::Colour* const colour = video_track->GetColour();
505       if (colour) {
506         // TODO(fgalligan): Add support for Colour's address and size.
507         fprintf(o, "%sColour:\n", indent->indent_str().c_str());
508         indent->Adjust(libwebm::kIncreaseIndent);
509 
510         const int64_t matrix_coefficients = colour->matrix_coefficients;
511         const int64_t bits_per_channel = colour->bits_per_channel;
512         const int64_t chroma_subsampling_horz = colour->chroma_subsampling_horz;
513         const int64_t chroma_subsampling_vert = colour->chroma_subsampling_vert;
514         const int64_t cb_subsampling_horz = colour->cb_subsampling_horz;
515         const int64_t cb_subsampling_vert = colour->cb_subsampling_vert;
516         const int64_t chroma_siting_horz = colour->chroma_siting_horz;
517         const int64_t chroma_siting_vert = colour->chroma_siting_vert;
518         const int64_t range = colour->range;
519         const int64_t transfer_characteristics =
520             colour->transfer_characteristics;
521         const int64_t primaries = colour->primaries;
522         const int64_t max_cll = colour->max_cll;
523         const int64_t max_fall = colour->max_fall;
524         if (matrix_coefficients != mkvparser::Colour::kValueNotPresent)
525           fprintf(o, "%sMatrixCoefficients      : %" PRId64 "\n",
526                   indent->indent_str().c_str(), matrix_coefficients);
527         if (bits_per_channel != mkvparser::Colour::kValueNotPresent)
528           fprintf(o, "%sBitsPerChannel          : %" PRId64 "\n",
529                   indent->indent_str().c_str(), bits_per_channel);
530         if (chroma_subsampling_horz != mkvparser::Colour::kValueNotPresent)
531           fprintf(o, "%sChromaSubsamplingHorz   : %" PRId64 "\n",
532                   indent->indent_str().c_str(), chroma_subsampling_horz);
533         if (chroma_subsampling_vert != mkvparser::Colour::kValueNotPresent)
534           fprintf(o, "%sChromaSubsamplingVert   : %" PRId64 "\n",
535                   indent->indent_str().c_str(), chroma_subsampling_vert);
536         if (cb_subsampling_horz != mkvparser::Colour::kValueNotPresent)
537           fprintf(o, "%sCbSubsamplingHorz       : %" PRId64 "\n",
538                   indent->indent_str().c_str(), cb_subsampling_horz);
539         if (cb_subsampling_vert != mkvparser::Colour::kValueNotPresent)
540           fprintf(o, "%sCbSubsamplingVert       : %" PRId64 "\n",
541                   indent->indent_str().c_str(), cb_subsampling_vert);
542         if (chroma_siting_horz != mkvparser::Colour::kValueNotPresent)
543           fprintf(o, "%sChromaSitingHorz        : %" PRId64 "\n",
544                   indent->indent_str().c_str(), chroma_siting_horz);
545         if (chroma_siting_vert != mkvparser::Colour::kValueNotPresent)
546           fprintf(o, "%sChromaSitingVert        : %" PRId64 "\n",
547                   indent->indent_str().c_str(), chroma_siting_vert);
548         if (range != mkvparser::Colour::kValueNotPresent)
549           fprintf(o, "%sRange                   : %" PRId64 "\n",
550                   indent->indent_str().c_str(), range);
551         if (transfer_characteristics != mkvparser::Colour::kValueNotPresent)
552           fprintf(o, "%sTransferCharacteristics : %" PRId64 "\n",
553                   indent->indent_str().c_str(), transfer_characteristics);
554         if (primaries != mkvparser::Colour::kValueNotPresent)
555           fprintf(o, "%sPrimaries               : %" PRId64 "\n",
556                   indent->indent_str().c_str(), primaries);
557         if (max_cll != mkvparser::Colour::kValueNotPresent)
558           fprintf(o, "%sMaxCLL                  : %" PRId64 "\n",
559                   indent->indent_str().c_str(), max_cll);
560         if (max_fall != mkvparser::Colour::kValueNotPresent)
561           fprintf(o, "%sMaxFALL                 : %" PRId64 "\n",
562                   indent->indent_str().c_str(), max_fall);
563 
564         const mkvparser::MasteringMetadata* const metadata =
565             colour->mastering_metadata;
566         if (metadata) {
567           // TODO(fgalligan): Add support for MasteringMetadata's address and
568           // size.
569           fprintf(o, "%sMasteringMetadata:\n", indent->indent_str().c_str());
570           indent->Adjust(libwebm::kIncreaseIndent);
571 
572           const mkvparser::PrimaryChromaticity* const red = metadata->r;
573           const mkvparser::PrimaryChromaticity* const green = metadata->g;
574           const mkvparser::PrimaryChromaticity* const blue = metadata->b;
575           const mkvparser::PrimaryChromaticity* const white =
576               metadata->white_point;
577           const float max = metadata->luminance_max;
578           const float min = metadata->luminance_min;
579           if (red) {
580             fprintf(o, "%sPrimaryRChromaticityX   : %g\n",
581                     indent->indent_str().c_str(), red->x);
582             fprintf(o, "%sPrimaryRChromaticityY   : %g\n",
583                     indent->indent_str().c_str(), red->y);
584           }
585           if (green) {
586             fprintf(o, "%sPrimaryGChromaticityX   : %g\n",
587                     indent->indent_str().c_str(), green->x);
588             fprintf(o, "%sPrimaryGChromaticityY   : %g\n",
589                     indent->indent_str().c_str(), green->y);
590           }
591           if (blue) {
592             fprintf(o, "%sPrimaryBChromaticityX   : %g\n",
593                     indent->indent_str().c_str(), blue->x);
594             fprintf(o, "%sPrimaryBChromaticityY   : %g\n",
595                     indent->indent_str().c_str(), blue->y);
596           }
597           if (white) {
598             fprintf(o, "%sWhitePointChromaticityX : %g\n",
599                     indent->indent_str().c_str(), white->x);
600             fprintf(o, "%sWhitePointChromaticityY : %g\n",
601                     indent->indent_str().c_str(), white->y);
602           }
603           if (max != mkvparser::MasteringMetadata::kValueNotPresent)
604             fprintf(o, "%sLuminanceMax            : %g\n",
605                     indent->indent_str().c_str(), max);
606           if (min != mkvparser::MasteringMetadata::kValueNotPresent)
607             fprintf(o, "%sLuminanceMin            : %g\n",
608                     indent->indent_str().c_str(), min);
609           indent->Adjust(libwebm::kDecreaseIndent);
610         }
611         indent->Adjust(libwebm::kDecreaseIndent);
612       }
613 
614       const mkvparser::Projection* const projection =
615           video_track->GetProjection();
616       if (projection) {
617         fprintf(o, "%sProjection:\n", indent->indent_str().c_str());
618         indent->Adjust(libwebm::kIncreaseIndent);
619 
620         const int projection_type = static_cast<int>(projection->type);
621         const int kTypeNotPresent =
622             static_cast<int>(mkvparser::Projection::kTypeNotPresent);
623         const float kValueNotPresent = mkvparser::Projection::kValueNotPresent;
624         if (projection_type != kTypeNotPresent)
625           fprintf(o, "%sProjectionType            : %d\n",
626                   indent->indent_str().c_str(), projection_type);
627         if (projection->private_data)
628           fprintf(o, "%sProjectionPrivate(size)   : %d\n",
629                   indent->indent_str().c_str(),
630                   static_cast<int>(projection->private_data_length));
631         if (projection->pose_yaw != kValueNotPresent)
632           fprintf(o, "%sProjectionPoseYaw         : %g\n",
633                   indent->indent_str().c_str(), projection->pose_yaw);
634         if (projection->pose_pitch != kValueNotPresent)
635           fprintf(o, "%sProjectionPosePitch       : %g\n",
636                   indent->indent_str().c_str(), projection->pose_pitch);
637         if (projection->pose_roll != kValueNotPresent)
638           fprintf(o, "%sProjectionPoseRoll         : %g\n",
639                   indent->indent_str().c_str(), projection->pose_roll);
640         indent->Adjust(libwebm::kDecreaseIndent);
641       }
642     } else if (track_type == mkvparser::Track::kAudio) {
643       const mkvparser::AudioTrack* const audio_track =
644           static_cast<const mkvparser::AudioTrack*>(track);
645       const int64_t channels = audio_track->GetChannels();
646       const int64_t bit_depth = audio_track->GetBitDepth();
647       const uint64_t codec_delay = audio_track->GetCodecDelay();
648       const uint64_t seek_preroll = audio_track->GetSeekPreRoll();
649       fprintf(o, "%sChannels         : %" PRId64 "\n",
650               indent->indent_str().c_str(), channels);
651       if (bit_depth > 0)
652         fprintf(o, "%sBitDepth         : %" PRId64 "\n",
653                 indent->indent_str().c_str(), bit_depth);
654       fprintf(o, "%sSamplingFrequency: %g\n", indent->indent_str().c_str(),
655               audio_track->GetSamplingRate());
656       if (codec_delay)
657         fprintf(o, "%sCodecDelay       : %" PRIu64 "\n",
658                 indent->indent_str().c_str(), codec_delay);
659       if (seek_preroll)
660         fprintf(o, "%sSeekPreRoll      : %" PRIu64 "\n",
661                 indent->indent_str().c_str(), seek_preroll);
662     }
663     indent->Adjust(libwebm::kDecreaseIndent * 2);
664   }
665 
666   return true;
667 }
668 
669 // libvpx reference: vp9/vp9_dx_iface.c
ParseSuperframeIndex(const uint8_t * data,size_t data_sz,uint32_t sizes[8],int * count)670 void ParseSuperframeIndex(const uint8_t* data, size_t data_sz,
671                           uint32_t sizes[8], int* count) {
672   const uint8_t marker = data[data_sz - 1];
673   *count = 0;
674 
675   if ((marker & 0xe0) == 0xc0) {
676     const int frames = (marker & 0x7) + 1;
677     const int mag = ((marker >> 3) & 0x3) + 1;
678     const size_t index_sz = 2 + mag * frames;
679 
680     if (data_sz >= index_sz && data[data_sz - index_sz] == marker) {
681       // found a valid superframe index
682       const uint8_t* x = data + data_sz - index_sz + 1;
683 
684       for (int i = 0; i < frames; ++i) {
685         uint32_t this_sz = 0;
686 
687         for (int j = 0; j < mag; ++j) {
688           this_sz |= (*x++) << (j * 8);
689         }
690         sizes[i] = this_sz;
691       }
692       *count = frames;
693     }
694   }
695 }
696 
PrintVP9Info(const uint8_t * data,int size,FILE * o,int64_t time_ns,FrameStats * stats,vp9_parser::Vp9HeaderParser * parser,vp9_parser::Vp9LevelStats * level_stats)697 void PrintVP9Info(const uint8_t* data, int size, FILE* o, int64_t time_ns,
698                   FrameStats* stats, vp9_parser::Vp9HeaderParser* parser,
699                   vp9_parser::Vp9LevelStats* level_stats) {
700   if (size < 1)
701     return;
702 
703   uint32_t sizes[8];
704   int i = 0, count = 0;
705   ParseSuperframeIndex(data, size, sizes, &count);
706 
707   // Remove all frames that are less than window size.
708   while (!stats->window.empty() &&
709          stats->window.front() < (time_ns - (kNanosecondsPerSecondi - 1)))
710     stats->window.pop();
711 
712   do {
713     const size_t frame_length = (count > 0) ? sizes[i] : size;
714     if (frame_length > static_cast<size_t>(std::numeric_limits<int>::max()) ||
715         static_cast<int>(frame_length) > size) {
716       fprintf(o, " invalid VP9 frame size (%u)\n",
717               static_cast<uint32_t>(frame_length));
718       return;
719     }
720     if (!parser->ParseUncompressedHeader(data, frame_length))
721       return;
722     level_stats->AddFrame(*parser, time_ns);
723 
724     // const int frame_marker = (data[0] >> 6) & 0x3;
725     const int version = parser->profile();
726     const int key = parser->key();
727     const int altref_frame = parser->altref();
728     const int error_resilient_mode = parser->error_resilient_mode();
729     const int row_tiles = parser->row_tiles();
730     const int column_tiles = parser->column_tiles();
731     const int frame_parallel_mode = parser->frame_parallel_mode();
732 
733     if (key &&
734         !(size >= 4 && data[1] == 0x49 && data[2] == 0x83 && data[3] == 0x42)) {
735       fprintf(o, " invalid VP9 signature");
736       return;
737     }
738 
739     stats->window.push(time_ns);
740     ++stats->frames;
741 
742     if (altref_frame) {
743       const int delta_altref = stats->frames_since_last_altref;
744       if (stats->first_altref) {
745         stats->first_altref = false;
746       } else if (delta_altref < stats->minimum_altref_distance) {
747         stats->minimum_altref_distance = delta_altref;
748         stats->min_altref_end_ns = time_ns;
749       }
750       stats->frames_since_last_altref = 0;
751     } else {
752       ++stats->frames_since_last_altref;
753       ++stats->displayed_frames;
754     }
755 
756     if (count > 0) {
757       fprintf(o, " packed [%d]: {", i);
758     }
759 
760     fprintf(o, " key:%d v:%d altref:%d errm:%d rt:%d ct:%d fpm:%d", key,
761             version, altref_frame, error_resilient_mode, row_tiles,
762             column_tiles, frame_parallel_mode);
763 
764     if (key && size > 4) {
765       fprintf(o, " cs:%d", parser->color_space());
766     }
767 
768     if (count > 0) {
769       fprintf(o, " size: %u }", sizes[i]);
770       data += sizes[i];
771       size -= sizes[i];
772     }
773     ++i;
774   } while (i < count);
775 
776   if (stats->max_window_size < static_cast<int64_t>(stats->window.size())) {
777     stats->max_window_size = stats->window.size();
778     stats->max_window_end_ns = time_ns;
779   }
780 }
781 
PrintVP8Info(const uint8_t * data,int size,FILE * o)782 void PrintVP8Info(const uint8_t* data, int size, FILE* o) {
783   if (size < 3)
784     return;
785 
786   const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16);
787   const int key = !(bits & 0x1);
788   const int altref_frame = !((bits >> 4) & 0x1);
789   const int version = (bits >> 1) & 0x7;
790   const int partition_length = (bits >> 5) & 0x7FFFF;
791   if (key &&
792       !(size >= 6 && data[3] == 0x9d && data[4] == 0x01 && data[5] == 0x2a)) {
793     fprintf(o, " invalid VP8 signature");
794     return;
795   }
796   fprintf(o, " key:%d v:%d altref:%d partition_length:%d", key, version,
797           altref_frame, partition_length);
798 }
799 
800 // Prints the partition offsets of the sub-sample encryption. |data| must point
801 // to an encrypted frame just after the signal byte. Returns the number of
802 // bytes read from the sub-sample partition information.
PrintSubSampleEncryption(const uint8_t * data,int size,FILE * o)803 int PrintSubSampleEncryption(const uint8_t* data, int size, FILE* o) {
804   int read_end = sizeof(uint64_t);
805 
806   // Skip past IV.
807   if (size < read_end)
808     return 0;
809   data += sizeof(uint64_t);
810 
811   // Read number of partitions.
812   read_end += sizeof(uint8_t);
813   if (size < read_end)
814     return 0;
815   const int num_partitions = data[0];
816   data += sizeof(uint8_t);
817 
818   // Read partitions.
819   for (int i = 0; i < num_partitions; ++i) {
820     read_end += sizeof(uint32_t);
821     if (size < read_end)
822       return 0;
823     uint32_t partition_offset;
824     memcpy(&partition_offset, data, sizeof(partition_offset));
825     partition_offset = libwebm::bigendian_to_host(partition_offset);
826     fprintf(o, " off[%d]:%u", i, partition_offset);
827     data += sizeof(uint32_t);
828   }
829 
830   return read_end;
831 }
832 
OutputCluster(const mkvparser::Cluster & cluster,const mkvparser::Tracks & tracks,const Options & options,FILE * o,mkvparser::MkvReader * reader,Indent * indent,int64_t * clusters_size,FrameStats * stats,vp9_parser::Vp9HeaderParser * parser,vp9_parser::Vp9LevelStats * level_stats)833 bool OutputCluster(const mkvparser::Cluster& cluster,
834                    const mkvparser::Tracks& tracks, const Options& options,
835                    FILE* o, mkvparser::MkvReader* reader, Indent* indent,
836                    int64_t* clusters_size, FrameStats* stats,
837                    vp9_parser::Vp9HeaderParser* parser,
838                    vp9_parser::Vp9LevelStats* level_stats) {
839   if (clusters_size) {
840     // Load the Cluster.
841     const mkvparser::BlockEntry* block_entry;
842     long status = cluster.GetFirst(block_entry);
843     if (status) {
844       fprintf(stderr, "Could not get first Block of Cluster.\n");
845       return false;
846     }
847 
848     *clusters_size += cluster.GetElementSize();
849   }
850 
851   if (options.output_clusters) {
852     const int64_t time_ns = cluster.GetTime();
853     const int64_t duration_ns = cluster.GetLastTime() - cluster.GetFirstTime();
854 
855     fprintf(o, "%sCluster:", indent->indent_str().c_str());
856     if (options.output_offset)
857       fprintf(o, "  @: %lld", cluster.m_element_start);
858     if (options.output_size)
859       fprintf(o, "  size: %lld", cluster.GetElementSize());
860     fprintf(o, "\n");
861     indent->Adjust(libwebm::kIncreaseIndent);
862     if (options.output_seconds)
863       fprintf(o, "%sTimecode (sec) : %g\n", indent->indent_str().c_str(),
864               time_ns / kNanosecondsPerSecond);
865     else
866       fprintf(o, "%sTimecode (nano): %" PRId64 "\n",
867               indent->indent_str().c_str(), time_ns);
868     if (options.output_seconds)
869       fprintf(o, "%sDuration (sec) : %g\n", indent->indent_str().c_str(),
870               duration_ns / kNanosecondsPerSecond);
871     else
872       fprintf(o, "%sDuration (nano): %" PRId64 "\n",
873               indent->indent_str().c_str(), duration_ns);
874 
875     fprintf(o, "%s# Blocks       : %ld\n", indent->indent_str().c_str(),
876             cluster.GetEntryCount());
877   }
878 
879   if (options.output_blocks) {
880     const mkvparser::BlockEntry* block_entry;
881     long status = cluster.GetFirst(block_entry);
882     if (status) {
883       fprintf(stderr, "Could not get first Block of Cluster.\n");
884       return false;
885     }
886 
887     std::vector<unsigned char> vector_data;
888     while (block_entry != NULL && !block_entry->EOS()) {
889       const mkvparser::Block* const block = block_entry->GetBlock();
890       if (!block) {
891         fprintf(stderr, "Could not getblock entry.\n");
892         return false;
893       }
894 
895       const unsigned int track_number =
896           static_cast<unsigned int>(block->GetTrackNumber());
897       const mkvparser::Track* track = tracks.GetTrackByNumber(track_number);
898       if (!track) {
899         fprintf(stderr, "Could not get Track.\n");
900         return false;
901       }
902 
903       const int64_t track_type = track->GetType();
904       if ((track_type == mkvparser::Track::kVideo && options.output_video) ||
905           (track_type == mkvparser::Track::kAudio && options.output_audio)) {
906         const int64_t time_ns = block->GetTime(&cluster);
907         const bool is_key = block->IsKey();
908 
909         if (block_entry->GetKind() == mkvparser::BlockEntry::kBlockGroup) {
910           fprintf(o, "%sBlockGroup:\n", indent->indent_str().c_str());
911           indent->Adjust(libwebm::kIncreaseIndent);
912         }
913 
914         fprintf(o, "%sBlock: type:%s frame:%s", indent->indent_str().c_str(),
915                 track_type == mkvparser::Track::kVideo ? "V" : "A",
916                 is_key ? "I" : "P");
917         if (options.output_seconds)
918           fprintf(o, " secs:%5g", time_ns / kNanosecondsPerSecond);
919         else
920           fprintf(o, " nano:%10" PRId64, time_ns);
921 
922         if (options.output_offset)
923           fprintf(o, " @_payload: %lld", block->m_start);
924         if (options.output_size)
925           fprintf(o, " size_payload: %lld", block->m_size);
926 
927         const uint8_t KEncryptedBit = 0x1;
928         const uint8_t kSubSampleBit = 0x2;
929         const int kSignalByteSize = 1;
930         bool encrypted_stream = false;
931         if (options.output_encrypted_info) {
932           if (track->GetContentEncodingCount() > 0) {
933             // Only check the first content encoding.
934             const ContentEncoding* const encoding =
935                 track->GetContentEncodingByIndex(0);
936             if (encoding) {
937               if (encoding->GetEncryptionCount() > 0) {
938                 const ContentEncoding::ContentEncryption* const encryption =
939                     encoding->GetEncryptionByIndex(0);
940                 if (encryption) {
941                   const ContentEncoding::ContentEncAESSettings& aes =
942                       encryption->aes_settings;
943                   if (aes.cipher_mode == 1) {
944                     encrypted_stream = true;
945                   }
946                 }
947               }
948             }
949           }
950 
951           if (encrypted_stream) {
952             const mkvparser::Block::Frame& frame = block->GetFrame(0);
953             if (frame.len > static_cast<int>(vector_data.size())) {
954               vector_data.resize(frame.len + 1024);
955             }
956 
957             unsigned char* data = &vector_data[0];
958             if (frame.Read(reader, data) < 0) {
959               fprintf(stderr, "Could not read frame.\n");
960               return false;
961             }
962 
963             const bool encrypted_frame = !!(data[0] & KEncryptedBit);
964             const bool sub_sample_encrypt = !!(data[0] & kSubSampleBit);
965             fprintf(o, " enc: %d", encrypted_frame ? 1 : 0);
966             fprintf(o, " sub: %d", sub_sample_encrypt ? 1 : 0);
967 
968             if (encrypted_frame) {
969               uint64_t iv;
970               memcpy(&iv, data + kSignalByteSize, sizeof(iv));
971               fprintf(o, " iv: %" PRIx64, iv);
972             }
973           }
974         }
975 
976         if (options.output_codec_info) {
977           const int frame_count = block->GetFrameCount();
978 
979           if (frame_count > 1) {
980             fprintf(o, "\n");
981             indent->Adjust(libwebm::kIncreaseIndent);
982           }
983 
984           for (int i = 0; i < frame_count; ++i) {
985             if (track_type == mkvparser::Track::kVideo) {
986               const mkvparser::Block::Frame& frame = block->GetFrame(i);
987               if (frame.len > static_cast<int>(vector_data.size())) {
988                 vector_data.resize(frame.len + 1024);
989               }
990 
991               unsigned char* data = &vector_data[0];
992               if (frame.Read(reader, data) < 0) {
993                 fprintf(stderr, "Could not read frame.\n");
994                 return false;
995               }
996 
997               if (frame_count > 1)
998                 fprintf(o, "\n%sVP8 data     :", indent->indent_str().c_str());
999 
1000               bool encrypted_frame = false;
1001               bool sub_sample_encrypt = false;
1002               int frame_size = static_cast<int>(frame.len);
1003 
1004               int frame_offset = 0;
1005               if (encrypted_stream) {
1006                 if (data[0] & KEncryptedBit) {
1007                   encrypted_frame = true;
1008                   if (data[0] & kSubSampleBit) {
1009                     sub_sample_encrypt = true;
1010                     data += kSignalByteSize;
1011                     frame_size -= kSignalByteSize;
1012                     frame_offset =
1013                         PrintSubSampleEncryption(data, frame_size, o);
1014                   }
1015                 } else {
1016                   frame_offset = kSignalByteSize;
1017                 }
1018               }
1019 
1020               if (!encrypted_frame || sub_sample_encrypt) {
1021                 data += frame_offset;
1022                 frame_size -= frame_offset;
1023 
1024                 const string codec_id = ToString(track->GetCodecId());
1025                 if (codec_id == "V_VP8") {
1026                   PrintVP8Info(data, frame_size, o);
1027                 } else if (codec_id == "V_VP9") {
1028                   PrintVP9Info(data, frame_size, o, time_ns, stats, parser,
1029                                level_stats);
1030                 }
1031               }
1032             }
1033           }
1034 
1035           if (frame_count > 1)
1036             indent->Adjust(libwebm::kDecreaseIndent);
1037         }
1038 
1039         if (block_entry->GetKind() == mkvparser::BlockEntry::kBlockGroup) {
1040           const int64_t discard_padding = block->GetDiscardPadding();
1041           if (discard_padding != 0) {
1042             fprintf(o, "\n%sDiscardPadding: %10" PRId64,
1043                     indent->indent_str().c_str(), discard_padding);
1044           }
1045           indent->Adjust(libwebm::kDecreaseIndent);
1046         }
1047 
1048         fprintf(o, "\n");
1049       }
1050 
1051       status = cluster.GetNext(block_entry, block_entry);
1052       if (status) {
1053         printf("\n Could not get next block of cluster.\n");
1054         return false;
1055       }
1056     }
1057   }
1058 
1059   if (options.output_clusters)
1060     indent->Adjust(libwebm::kDecreaseIndent);
1061 
1062   return true;
1063 }
1064 
OutputCues(const mkvparser::Segment & segment,const mkvparser::Tracks & tracks,const Options & options,FILE * o,Indent * indent)1065 bool OutputCues(const mkvparser::Segment& segment,
1066                 const mkvparser::Tracks& tracks, const Options& options,
1067                 FILE* o, Indent* indent) {
1068   const mkvparser::Cues* const cues = segment.GetCues();
1069   if (cues == NULL)
1070     return true;
1071 
1072   // Load all of the cue points.
1073   while (!cues->DoneParsing())
1074     cues->LoadCuePoint();
1075 
1076   // Confirm that the input has cue points.
1077   const mkvparser::CuePoint* const first_cue = cues->GetFirst();
1078   if (first_cue == NULL) {
1079     fprintf(o, "%sNo cue points.\n", indent->indent_str().c_str());
1080     return true;
1081   }
1082 
1083   // Input has cue points, dump them:
1084   fprintf(o, "%sCues:", indent->indent_str().c_str());
1085   if (options.output_offset)
1086     fprintf(o, " @:%lld", cues->m_element_start);
1087   if (options.output_size)
1088     fprintf(o, " size:%lld", cues->m_element_size);
1089   fprintf(o, "\n");
1090 
1091   const mkvparser::CuePoint* cue_point = first_cue;
1092   int cue_point_num = 1;
1093   const int num_tracks = static_cast<int>(tracks.GetTracksCount());
1094   indent->Adjust(libwebm::kIncreaseIndent);
1095 
1096   do {
1097     for (int track_num = 0; track_num < num_tracks; ++track_num) {
1098       const mkvparser::Track* const track = tracks.GetTrackByIndex(track_num);
1099       const mkvparser::CuePoint::TrackPosition* const track_pos =
1100           cue_point->Find(track);
1101 
1102       if (track_pos != NULL) {
1103         const char track_type =
1104             (track->GetType() == mkvparser::Track::kVideo) ? 'V' : 'A';
1105         fprintf(o, "%sCue Point:%d type:%c track:%d",
1106                 indent->indent_str().c_str(), cue_point_num, track_type,
1107                 static_cast<int>(track->GetNumber()));
1108 
1109         if (options.output_seconds) {
1110           fprintf(o, " secs:%g",
1111                   cue_point->GetTime(&segment) / kNanosecondsPerSecond);
1112         } else {
1113           fprintf(o, " nano:%lld", cue_point->GetTime(&segment));
1114         }
1115 
1116         if (options.output_blocks)
1117           fprintf(o, " block:%lld", track_pos->m_block);
1118 
1119         if (options.output_offset)
1120           fprintf(o, " @:%lld", track_pos->m_pos);
1121 
1122         fprintf(o, "\n");
1123       }
1124     }
1125 
1126     cue_point = cues->GetNext(cue_point);
1127     ++cue_point_num;
1128   } while (cue_point != NULL);
1129 
1130   indent->Adjust(libwebm::kDecreaseIndent);
1131   return true;
1132 }
1133 
1134 }  // namespace
1135 
main(int argc,char * argv[])1136 int main(int argc, char* argv[]) {
1137   string input;
1138   Options options;
1139 
1140   const int argc_check = argc - 1;
1141   for (int i = 1; i < argc; ++i) {
1142     if (!strcmp("-h", argv[i]) || !strcmp("-?", argv[i])) {
1143       Usage();
1144       return EXIT_SUCCESS;
1145     } else if (!strcmp("-v", argv[i])) {
1146       printf("version: %s\n", VERSION_STRING);
1147     } else if (!strcmp("-i", argv[i]) && i < argc_check) {
1148       input = argv[++i];
1149     } else if (!strcmp("-all", argv[i])) {
1150       options.SetAll(true);
1151     } else if (Options::MatchesBooleanOption("video", argv[i])) {
1152       options.output_video = !strcmp("-video", argv[i]);
1153     } else if (Options::MatchesBooleanOption("audio", argv[i])) {
1154       options.output_audio = !strcmp("-audio", argv[i]);
1155     } else if (Options::MatchesBooleanOption("size", argv[i])) {
1156       options.output_size = !strcmp("-size", argv[i]);
1157     } else if (Options::MatchesBooleanOption("offset", argv[i])) {
1158       options.output_offset = !strcmp("-offset", argv[i]);
1159     } else if (Options::MatchesBooleanOption("times_seconds", argv[i])) {
1160       options.output_seconds = !strcmp("-times_seconds", argv[i]);
1161     } else if (Options::MatchesBooleanOption("ebml_header", argv[i])) {
1162       options.output_ebml_header = !strcmp("-ebml_header", argv[i]);
1163     } else if (Options::MatchesBooleanOption("segment", argv[i])) {
1164       options.output_segment = !strcmp("-segment", argv[i]);
1165     } else if (Options::MatchesBooleanOption("seekhead", argv[i])) {
1166       options.output_seekhead = !strcmp("-seekhead", argv[i]);
1167     } else if (Options::MatchesBooleanOption("segment_info", argv[i])) {
1168       options.output_segment_info = !strcmp("-segment_info", argv[i]);
1169     } else if (Options::MatchesBooleanOption("tracks", argv[i])) {
1170       options.output_tracks = !strcmp("-tracks", argv[i]);
1171     } else if (Options::MatchesBooleanOption("clusters", argv[i])) {
1172       options.output_clusters = !strcmp("-clusters", argv[i]);
1173     } else if (Options::MatchesBooleanOption("blocks", argv[i])) {
1174       options.output_blocks = !strcmp("-blocks", argv[i]);
1175     } else if (Options::MatchesBooleanOption("codec_info", argv[i])) {
1176       options.output_codec_info = !strcmp("-codec_info", argv[i]);
1177     } else if (Options::MatchesBooleanOption("clusters_size", argv[i])) {
1178       options.output_clusters_size = !strcmp("-clusters_size", argv[i]);
1179     } else if (Options::MatchesBooleanOption("encrypted_info", argv[i])) {
1180       options.output_encrypted_info = !strcmp("-encrypted_info", argv[i]);
1181     } else if (Options::MatchesBooleanOption("cues", argv[i])) {
1182       options.output_cues = !strcmp("-cues", argv[i]);
1183     } else if (Options::MatchesBooleanOption("frame_stats", argv[i])) {
1184       options.output_frame_stats = !strcmp("-frame_stats", argv[i]);
1185     } else if (Options::MatchesBooleanOption("vp9_level", argv[i])) {
1186       options.output_vp9_level = !strcmp("-vp9_level", argv[i]);
1187     }
1188   }
1189 
1190   if (argc < 3 || input.empty()) {
1191     Usage();
1192     return EXIT_FAILURE;
1193   }
1194 
1195   std::unique_ptr<mkvparser::MkvReader> reader(
1196       new (std::nothrow) mkvparser::MkvReader());  // NOLINT
1197   if (reader->Open(input.c_str())) {
1198     fprintf(stderr, "Error opening file:%s\n", input.c_str());
1199     return EXIT_FAILURE;
1200   }
1201 
1202   long long int pos = 0;
1203   std::unique_ptr<mkvparser::EBMLHeader> ebml_header(
1204       new (std::nothrow) mkvparser::EBMLHeader());  // NOLINT
1205   if (ebml_header->Parse(reader.get(), pos) < 0) {
1206     fprintf(stderr, "Error parsing EBML header.\n");
1207     return EXIT_FAILURE;
1208   }
1209 
1210   Indent indent(0);
1211   FILE* out = stdout;
1212 
1213   if (options.output_ebml_header)
1214     OutputEBMLHeader(*ebml_header.get(), out, &indent);
1215 
1216   mkvparser::Segment* temp_segment;
1217   if (mkvparser::Segment::CreateInstance(reader.get(), pos, temp_segment)) {
1218     fprintf(stderr, "Segment::CreateInstance() failed.\n");
1219     return EXIT_FAILURE;
1220   }
1221   std::unique_ptr<mkvparser::Segment> segment(temp_segment);
1222 
1223   if (segment->Load() < 0) {
1224     fprintf(stderr, "Segment::Load() failed.\n");
1225     return EXIT_FAILURE;
1226   }
1227 
1228   if (options.output_segment) {
1229     OutputSegment(*(segment.get()), options, out);
1230     indent.Adjust(libwebm::kIncreaseIndent);
1231   }
1232 
1233   if (options.output_seekhead)
1234     if (!OutputSeekHead(*(segment.get()), options, out, &indent))
1235       return EXIT_FAILURE;
1236 
1237   if (options.output_segment_info)
1238     if (!OutputSegmentInfo(*(segment.get()), options, out, &indent))
1239       return EXIT_FAILURE;
1240 
1241   if (options.output_tracks)
1242     if (!OutputTracks(*(segment.get()), options, out, &indent))
1243       return EXIT_FAILURE;
1244 
1245   const mkvparser::Tracks* const tracks = segment->GetTracks();
1246   if (!tracks) {
1247     fprintf(stderr, "Could not get Tracks.\n");
1248     return EXIT_FAILURE;
1249   }
1250 
1251   // If Cues are before the clusters output them first.
1252   if (options.output_cues) {
1253     const mkvparser::Cluster* cluster = segment->GetFirst();
1254     const mkvparser::Cues* const cues = segment->GetCues();
1255     if (cluster != NULL && cues != NULL) {
1256       if (cues->m_element_start < cluster->m_element_start) {
1257         if (!OutputCues(*segment, *tracks, options, out, &indent)) {
1258           return EXIT_FAILURE;
1259         }
1260         options.output_cues = false;
1261       }
1262     }
1263   }
1264 
1265   if (options.output_clusters)
1266     fprintf(out, "%sClusters (count):%ld\n", indent.indent_str().c_str(),
1267             segment->GetCount());
1268 
1269   int64_t clusters_size = 0;
1270   FrameStats stats;
1271   vp9_parser::Vp9HeaderParser parser;
1272   vp9_parser::Vp9LevelStats level_stats;
1273   const mkvparser::Cluster* cluster = segment->GetFirst();
1274   while (cluster != NULL && !cluster->EOS()) {
1275     if (!OutputCluster(*cluster, *tracks, options, out, reader.get(), &indent,
1276                        &clusters_size, &stats, &parser, &level_stats))
1277       return EXIT_FAILURE;
1278     cluster = segment->GetNext(cluster);
1279   }
1280 
1281   if (options.output_clusters_size)
1282     fprintf(out, "%sClusters (size):%" PRId64 "\n", indent.indent_str().c_str(),
1283             clusters_size);
1284 
1285   if (options.output_cues)
1286     if (!OutputCues(*segment, *tracks, options, out, &indent))
1287       return EXIT_FAILURE;
1288 
1289   // TODO(fgalligan): Add support for VP8.
1290   if (options.output_frame_stats &&
1291       stats.minimum_altref_distance != std::numeric_limits<int>::max()) {
1292     const double actual_fps =
1293         stats.frames /
1294         (segment->GetInfo()->GetDuration() / kNanosecondsPerSecond);
1295     const double displayed_fps =
1296         stats.displayed_frames /
1297         (segment->GetInfo()->GetDuration() / kNanosecondsPerSecond);
1298     fprintf(out, "\nActual fps:%g  Displayed fps:%g\n", actual_fps,
1299             displayed_fps);
1300 
1301     fprintf(out, "Minimum Altref Distance:%d  at:%g seconds\n",
1302             stats.minimum_altref_distance,
1303             stats.min_altref_end_ns / kNanosecondsPerSecond);
1304 
1305     // TODO(fgalligan): Add support for window duration other than 1 second.
1306     const double sec_end = stats.max_window_end_ns / kNanosecondsPerSecond;
1307     const double sec_start =
1308         stats.max_window_end_ns > kNanosecondsPerSecondi ? sec_end - 1.0 : 0.0;
1309     fprintf(out, "Maximum Window:%g-%g seconds  Window fps:%" PRId64 "\n",
1310             sec_start, sec_end, stats.max_window_size);
1311   }
1312 
1313   if (options.output_vp9_level) {
1314     level_stats.set_duration(segment->GetInfo()->GetDuration());
1315     const vp9_parser::Vp9Level level = level_stats.GetLevel();
1316     fprintf(out, "VP9 Level:%d\n", level);
1317     fprintf(
1318         out,
1319         "mlsr:%" PRId64 " mlps:%" PRId64 " mlpb:%" PRId64
1320         " abr:%g mcs:%g cr:%g mct:%d"
1321         " mad:%d mrf:%d\n",
1322         level_stats.GetMaxLumaSampleRate(), level_stats.GetMaxLumaPictureSize(),
1323         level_stats.GetMaxLumaPictureBreadth(), level_stats.GetAverageBitRate(),
1324         level_stats.GetMaxCpbSize(), level_stats.GetCompressionRatio(),
1325         level_stats.GetMaxColumnTiles(), level_stats.GetMinimumAltrefDistance(),
1326         level_stats.GetMaxReferenceFrames());
1327   }
1328   return EXIT_SUCCESS;
1329 }
1330