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