1 /*****************************************************************
2 |
3 | AP4 - MP4 File Info
4 |
5 | Copyright 2002-2015 Axiomatic Systems, LLC
6 |
7 |
8 | This file is part of Bento4/AP4 (MP4 Atom Processing Library).
9 |
10 | Unless you have obtained Bento4 under a difference license,
11 | this version of Bento4 is Bento4|GPL.
12 | Bento4|GPL is free software; you can redistribute it and/or modify
13 | it under the terms of the GNU General Public License as published by
14 | the Free Software Foundation; either version 2, or (at your option)
15 | any later version.
16 |
17 | Bento4|GPL is distributed in the hope that it will be useful,
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | GNU General Public License for more details.
21 |
22 | You should have received a copy of the GNU General Public License
23 | along with Bento4|GPL; see the file COPYING. If not, write to the
24 | Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
25 | 02111-1307, USA.
26 |
27 ****************************************************************/
28
29 /*----------------------------------------------------------------------
30 | includes
31 +---------------------------------------------------------------------*/
32 #include <stdio.h>
33 #include <stdlib.h>
34
35 #include "Ap4.h"
36 #include "Ap4BitStream.h"
37 #include "Ap4Mp4AudioInfo.h"
38 #include "Ap4HevcParser.h"
39
40 /*----------------------------------------------------------------------
41 | constants
42 +---------------------------------------------------------------------*/
43 #define BANNER "MP4 File Info - Version 1.3.4\n"\
44 "(Bento4 Version " AP4_VERSION_STRING ")\n"\
45 "(c) 2002-2017 Axiomatic Systems, LLC"
46
47 /*----------------------------------------------------------------------
48 | globals
49 +---------------------------------------------------------------------*/
50 typedef struct {
51 AP4_UI64 sample_count;
52 AP4_UI64 duration;
53 double bitrate;
54 } MediaInfo;
55
56 typedef enum {
57 TEXT_FORMAT,
58 JSON_FORMAT
59 } OutputFormat;
60
61 struct Options {
62 OutputFormat format;
63 } Options;
64
65 /*----------------------------------------------------------------------
66 | PrintUsageAndExit
67 +---------------------------------------------------------------------*/
68 static void
PrintUsageAndExit()69 PrintUsageAndExit()
70 {
71 fprintf(stderr,
72 BANNER
73 "\n\nusage: mp4info [options] <input>\n"
74 "Options:\n"
75 " --verbose: show extended information when available\n"
76 " --format <format>: display the information in this format.\n"
77 " <format> is: text (default) or json\n"
78 " --show-layout: show sample layout\n"
79 " --show-samples: show sample details\n"
80 " --show-sample-data: show sample data\n"
81 " --fast: skip some details that are slow to compute\n");
82 exit(1);
83 }
84
85 /*----------------------------------------------------------------------
86 | ShowPayload
87 +---------------------------------------------------------------------*/
88 static void
ShowPayload(AP4_Atom & atom,bool ascii=false)89 ShowPayload(AP4_Atom& atom, bool ascii = false)
90 {
91 AP4_UI64 payload_size = atom.GetSize()-8;
92 if (payload_size <= 1024) {
93 AP4_MemoryByteStream* payload = new AP4_MemoryByteStream();
94 atom.Write(*payload);
95 if (ascii) {
96 // ascii
97 payload->WriteUI08(0); // terminate with a NULL character
98 printf("%s", (const char*)payload->GetData()+atom.GetHeaderSize());
99 } else {
100 // hex
101 for (unsigned int i=0; i<payload_size; i++) {
102 printf("%02x", (unsigned char)payload->GetData()[atom.GetHeaderSize()+i]);
103 }
104 }
105 payload->Release();
106 }
107 }
108
109 /*----------------------------------------------------------------------
110 | ShowData
111 +---------------------------------------------------------------------*/
112 static void
ShowData(const AP4_DataBuffer & data)113 ShowData(const AP4_DataBuffer& data)
114 {
115 for (unsigned int i=0; i<data.GetDataSize(); i++) {
116 printf("%02x", (unsigned char)data.GetData()[i]);
117 }
118 }
119
120 /*----------------------------------------------------------------------
121 | ShowProtectionSchemeInfo_Text
122 +---------------------------------------------------------------------*/
123 static void
ShowProtectionSchemeInfo_Text(AP4_UI32 scheme_type,AP4_ContainerAtom & schi,bool verbose)124 ShowProtectionSchemeInfo_Text(AP4_UI32 scheme_type, AP4_ContainerAtom& schi, bool verbose)
125 {
126 if (scheme_type == AP4_PROTECTION_SCHEME_TYPE_IAEC) {
127 printf(" iAEC Scheme Info:\n");
128 AP4_IkmsAtom* ikms = AP4_DYNAMIC_CAST(AP4_IkmsAtom, schi.FindChild("iKMS"));
129 if (ikms) {
130 printf(" KMS URI: %s\n", ikms->GetKmsUri().GetChars());
131 }
132 AP4_IsfmAtom* isfm = AP4_DYNAMIC_CAST(AP4_IsfmAtom, schi.FindChild("iSFM"));
133 if (isfm) {
134 printf(" Selective Encryption: %s\n", isfm->GetSelectiveEncryption()?"yes":"no");
135 printf(" Key Indicator Length: %d\n", isfm->GetKeyIndicatorLength());
136 printf(" IV Length: %d\n", isfm->GetIvLength());
137 }
138 AP4_IsltAtom* islt = AP4_DYNAMIC_CAST(AP4_IsltAtom, schi.FindChild("iSLT"));
139 if (islt) {
140 printf(" Salt: ");
141 for (unsigned int i=0; i<8; i++) {
142 printf("%02x",islt->GetSalt()[i]);
143 }
144 printf("\n");
145 }
146 } else if (scheme_type == AP4_PROTECTION_SCHEME_TYPE_OMA) {
147 printf(" odkm Scheme Info:\n");
148 AP4_OdafAtom* odaf = AP4_DYNAMIC_CAST(AP4_OdafAtom, schi.FindChild("odkm/odaf"));
149 if (odaf) {
150 printf(" Selective Encryption: %s\n", odaf->GetSelectiveEncryption()?"yes":"no");
151 printf(" Key Indicator Length: %d\n", odaf->GetKeyIndicatorLength());
152 printf(" IV Length: %d\n", odaf->GetIvLength());
153 }
154 AP4_OhdrAtom* ohdr = AP4_DYNAMIC_CAST(AP4_OhdrAtom, schi.FindChild("odkm/ohdr"));
155 if (ohdr) {
156 const char* encryption_method = "";
157 switch (ohdr->GetEncryptionMethod()) {
158 case AP4_OMA_DCF_ENCRYPTION_METHOD_NULL: encryption_method = "NULL"; break;
159 case AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CTR: encryption_method = "AES-CTR"; break;
160 case AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CBC: encryption_method = "AES-CBC"; break;
161 default: encryption_method = "UNKNOWN"; break;
162 }
163 printf(" Encryption Method: %s\n", encryption_method);
164 printf(" Content ID: %s\n", ohdr->GetContentId().GetChars());
165 printf(" Rights Issuer URL: %s\n", ohdr->GetRightsIssuerUrl().GetChars());
166
167 const AP4_DataBuffer& headers = ohdr->GetTextualHeaders();
168 AP4_Size data_len = headers.GetDataSize();
169 if (data_len) {
170 AP4_Byte* textual_headers_string;
171 AP4_Byte* curr;
172 AP4_DataBuffer output_buffer;
173 output_buffer.SetDataSize(data_len+1);
174 AP4_CopyMemory(output_buffer.UseData(), headers.GetData(), data_len);
175 curr = textual_headers_string = output_buffer.UseData();
176 textual_headers_string[data_len] = '\0';
177 while(curr < textual_headers_string+data_len) {
178 if ('\0' == *curr) {
179 *curr = '\n';
180 }
181 curr++;
182 }
183 printf(" Textual Headers: \n%s\n", (const char*)textual_headers_string);
184 }
185 }
186 } else if (scheme_type == AP4_PROTECTION_SCHEME_TYPE_ITUNES) {
187 printf(" itun Scheme Info:\n");
188 AP4_Atom* name = schi.FindChild("name");
189 if (name) {
190 printf(" Name: ");
191 ShowPayload(*name, true);
192 printf("\n");
193 }
194 AP4_Atom* user = schi.FindChild("user");
195 if (user) {
196 printf(" User ID: ");
197 ShowPayload(*user);
198 printf("\n");
199 }
200 AP4_Atom* key = schi.FindChild("key ");
201 if (key) {
202 printf(" Key ID: ");
203 ShowPayload(*key);
204 printf("\n");
205 }
206 AP4_Atom* iviv = schi.FindChild("iviv");
207 if (iviv) {
208 printf(" IV: ");
209 ShowPayload(*iviv);
210 printf("\n");
211 }
212 } else if (scheme_type == AP4_PROTECTION_SCHEME_TYPE_MARLIN_ACBC ||
213 scheme_type == AP4_PROTECTION_SCHEME_TYPE_MARLIN_ACGK) {
214 printf(" Marlin IPMP ACBC/ACGK Scheme Info:\n");
215 AP4_NullTerminatedStringAtom* octopus_id = AP4_DYNAMIC_CAST(AP4_NullTerminatedStringAtom, schi.FindChild("8id "));
216 if (octopus_id) {
217 printf(" Content ID: %s\n", octopus_id->GetValue().GetChars());
218 }
219 }
220
221 if (verbose) {
222 printf(" Protection System Details:\n");
223 AP4_ByteStream* output = NULL;
224 AP4_FileByteStream::Create("-stdout", AP4_FileByteStream::STREAM_MODE_WRITE, output);
225 AP4_PrintInspector inspector(*output, 4);
226 schi.Inspect(inspector);
227 output->Release();
228 }
229 }
230
231 /*----------------------------------------------------------------------
232 | ShowProtectionSchemeInfo_Json
233 +---------------------------------------------------------------------*/
234 static void
ShowProtectionSchemeInfo_Json(AP4_UI32,AP4_ContainerAtom &,bool)235 ShowProtectionSchemeInfo_Json(AP4_UI32 /* scheme_type */, AP4_ContainerAtom& /* schi */, bool /* verbose */)
236 {
237 // not implemented yet
238 }
239
240 /*----------------------------------------------------------------------
241 | ShowProtectedSampleDescription_Text
242 +---------------------------------------------------------------------*/
243 static void
ShowProtectedSampleDescription_Text(AP4_ProtectedSampleDescription & desc,bool verbose)244 ShowProtectedSampleDescription_Text(AP4_ProtectedSampleDescription& desc, bool verbose)
245 {
246 printf(" [ENCRYPTED]\n");
247 char coding[5];
248 AP4_FormatFourChars(coding, desc.GetFormat());
249 printf(" Coding: %s\n", coding);
250 AP4_UI32 st = desc.GetSchemeType();
251 printf(" Scheme Type: %c%c%c%c\n",
252 (char)((st>>24) & 0xFF),
253 (char)((st>>16) & 0xFF),
254 (char)((st>> 8) & 0xFF),
255 (char)((st ) & 0xFF));
256 printf(" Scheme Version: %d\n", desc.GetSchemeVersion());
257 printf(" Scheme URI: %s\n", desc.GetSchemeUri().GetChars());
258 AP4_ProtectionSchemeInfo* scheme_info = desc.GetSchemeInfo();
259 if (scheme_info == NULL) return;
260 AP4_ContainerAtom* schi = scheme_info->GetSchiAtom();
261 if (schi == NULL) return;
262 ShowProtectionSchemeInfo_Text(desc.GetSchemeType(), *schi, verbose);
263 }
264
265 /*----------------------------------------------------------------------
266 | ShowProtectedSampleDescription_Json
267 +---------------------------------------------------------------------*/
268 static void
ShowProtectedSampleDescription_Json(AP4_ProtectedSampleDescription & desc,bool verbose)269 ShowProtectedSampleDescription_Json(AP4_ProtectedSampleDescription& desc, bool verbose)
270 {
271 printf("\"protection\":{\n");
272 char coding[5];
273 AP4_FormatFourChars(coding, desc.GetFormat());
274 printf("\"coding\":\"%s\",\n", coding);
275 AP4_UI32 st = desc.GetSchemeType();
276 printf("\"scheme_type\":\"%c%c%c%c\",\n",
277 (char)((st>>24) & 0xFF),
278 (char)((st>>16) & 0xFF),
279 (char)((st>> 8) & 0xFF),
280 (char)((st ) & 0xFF));
281 printf("\"scheme_version\":%d,\n", desc.GetSchemeVersion());
282 printf("\"scheme_uri\":\"%s\"", desc.GetSchemeUri().GetChars());
283 AP4_ProtectionSchemeInfo* scheme_info = desc.GetSchemeInfo();
284 if (scheme_info) {
285 AP4_ContainerAtom* schi = scheme_info->GetSchiAtom();
286 if (schi) {
287 ShowProtectionSchemeInfo_Json(desc.GetSchemeType(), *schi, verbose);
288 }
289 }
290 printf("}");
291 }
292
293 /*----------------------------------------------------------------------
294 | ShowMpegAudioSampleDescription
295 +---------------------------------------------------------------------*/
296 static void
ShowMpegAudioSampleDescription(AP4_MpegAudioSampleDescription & mpeg_audio_desc)297 ShowMpegAudioSampleDescription(AP4_MpegAudioSampleDescription& mpeg_audio_desc)
298 {
299 AP4_MpegAudioSampleDescription::Mpeg4AudioObjectType object_type =
300 mpeg_audio_desc.GetMpeg4AudioObjectType();
301 const char* object_type_string = AP4_MpegAudioSampleDescription::GetMpeg4AudioObjectTypeString(object_type);
302
303 switch (Options.format) {
304 case TEXT_FORMAT:
305 printf(" MPEG-4 Audio Object Type: %d (%s)\n", object_type, object_type_string);
306 break;
307
308 case JSON_FORMAT:
309 printf("\"mpeg_4_audio_object_type\":%d,\n", object_type);
310 printf("\"mpeg_4_audio_object_type_name\":\"%s\"", object_type_string);
311 break;
312 }
313
314 // Decoder Specific Info
315 const AP4_DataBuffer& dsi = mpeg_audio_desc.GetDecoderInfo();
316 if (dsi.GetDataSize()) {
317 AP4_Mp4AudioDecoderConfig dec_config;
318 AP4_Result result = dec_config.Parse(dsi.GetData(), dsi.GetDataSize());
319 if (AP4_SUCCEEDED(result)) {
320 switch (Options.format) {
321 case TEXT_FORMAT:
322 printf(" MPEG-4 Audio Decoder Config:\n");
323 printf(" Sampling Frequency: %d\n", dec_config.m_SamplingFrequency);
324 printf(" Channels: %d\n", dec_config.m_ChannelCount);
325 if (dec_config.m_Extension.m_ObjectType) {
326 object_type_string = AP4_MpegAudioSampleDescription::GetMpeg4AudioObjectTypeString(
327 dec_config.m_Extension.m_ObjectType);
328
329 printf(" Extension:\n");
330 printf(" Object Type: %s\n", object_type_string);
331 printf(" SBR Present: %s\n", dec_config.m_Extension.m_SbrPresent?"yes":"no");
332 printf(" PS Present: %s\n", dec_config.m_Extension.m_PsPresent?"yes":"no");
333 printf(" Sampling Frequency: %d\n", dec_config.m_Extension.m_SamplingFrequency);
334 }
335 break;
336
337 case JSON_FORMAT:
338 printf(",\n");
339 printf("\"mpeg_4_audio_decoder_config\":{\n");
340 printf(" \"sampling_frequency\":%d,\n", dec_config.m_SamplingFrequency);
341 printf(" \"channels\":%d\n", dec_config.m_ChannelCount);
342 if (dec_config.m_Extension.m_ObjectType) {
343 object_type_string = AP4_MpegAudioSampleDescription::GetMpeg4AudioObjectTypeString(
344 dec_config.m_Extension.m_ObjectType);
345
346 printf(",\n");
347 printf(" \"extension\":{\n");
348 printf(" \"object_type\":%d,\n", dec_config.m_Extension.m_ObjectType);
349 printf(" \"object_type_name\":\"%s\",\n", object_type_string);
350 printf(" \"sbr_present\":%s,\n", dec_config.m_Extension.m_SbrPresent?"true":"false");
351 printf(" \"ps_present\":%s,\n", dec_config.m_Extension.m_PsPresent?"true":"false");
352 printf(" \"sampling_frequency\":%d\n", dec_config.m_Extension.m_SamplingFrequency);
353 printf(" }\n");
354 }
355 printf("}");
356 break;
357 }
358 }
359 }
360 }
361
362 /*----------------------------------------------------------------------
363 | ShowSampleDescription_Text
364 +---------------------------------------------------------------------*/
365 static void
ShowSampleDescription_Text(AP4_SampleDescription & description,bool verbose)366 ShowSampleDescription_Text(AP4_SampleDescription& description, bool verbose)
367 {
368 AP4_SampleDescription* desc = &description;
369 if (desc->GetType() == AP4_SampleDescription::TYPE_PROTECTED) {
370 AP4_ProtectedSampleDescription* prot_desc = AP4_DYNAMIC_CAST(AP4_ProtectedSampleDescription, desc);
371 if (prot_desc) {
372 ShowProtectedSampleDescription_Text(*prot_desc, verbose);
373 desc = prot_desc->GetOriginalSampleDescription();
374 }
375 }
376
377 if (verbose) {
378 printf(" Bytes: ");
379 AP4_Atom* details = desc->ToAtom();
380 ShowPayload(*details, false);
381 printf("\n");
382 delete details;
383 }
384
385 char coding[5];
386 AP4_FormatFourChars(coding, desc->GetFormat());
387 printf( " Coding: %s", coding);
388 const char* format_name = AP4_GetFormatName(desc->GetFormat());
389 if (format_name) {
390 printf(" (%s)\n", format_name);
391 } else {
392 printf("\n");
393 }
394 AP4_String codec;
395 desc->GetCodecString(codec);
396 printf( " Codec String: %s\n", codec.GetChars());
397
398 switch (desc->GetType()) {
399 case AP4_SampleDescription::TYPE_MPEG: {
400 // MPEG sample description
401 AP4_MpegSampleDescription* mpeg_desc = AP4_DYNAMIC_CAST(AP4_MpegSampleDescription, desc);
402
403 printf(" Stream Type: %s\n", mpeg_desc->GetStreamTypeString(mpeg_desc->GetStreamType()));
404 printf(" Object Type: %s\n", mpeg_desc->GetObjectTypeString(mpeg_desc->GetObjectTypeId()));
405 printf(" Max Bitrate: %d\n", mpeg_desc->GetMaxBitrate());
406 printf(" Avg Bitrate: %d\n", mpeg_desc->GetAvgBitrate());
407 printf(" Buffer Size: %d\n", mpeg_desc->GetBufferSize());
408
409 if (mpeg_desc->GetObjectTypeId() == AP4_OTI_MPEG4_AUDIO ||
410 mpeg_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_LC ||
411 mpeg_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_MAIN) {
412 AP4_MpegAudioSampleDescription* mpeg_audio_desc = AP4_DYNAMIC_CAST(AP4_MpegAudioSampleDescription, mpeg_desc);
413 if (mpeg_audio_desc) ShowMpegAudioSampleDescription(*mpeg_audio_desc);
414 }
415 break;
416 }
417
418 case AP4_SampleDescription::TYPE_AVC: {
419 // AVC specifics
420 AP4_AvcSampleDescription* avc_desc = AP4_DYNAMIC_CAST(AP4_AvcSampleDescription, desc);
421 const char* profile_name = AP4_AvccAtom::GetProfileName(avc_desc->GetProfile());
422 printf(" AVC Profile: %d", avc_desc->GetProfile());
423 if (profile_name) {
424 printf(" (%s)\n", profile_name);
425 } else {
426 printf("\n");
427 }
428 printf(" AVC Profile Compat: %x\n", avc_desc->GetProfileCompatibility());
429 printf(" AVC Level: %d\n", avc_desc->GetLevel());
430 printf(" AVC NALU Length Size: %d\n", avc_desc->GetNaluLengthSize());
431 printf(" AVC SPS: [");
432 const char* sep = "";
433 for (unsigned int i=0; i<avc_desc->GetSequenceParameters().ItemCount(); i++) {
434 printf("%s", sep);
435 ShowData(avc_desc->GetSequenceParameters()[i]);
436 sep = ", ";
437 }
438 printf("]\n");
439 printf(" AVC PPS: [");
440 sep = "";
441 for (unsigned int i=0; i<avc_desc->GetPictureParameters().ItemCount(); i++) {
442 printf("%s", sep);
443 ShowData(avc_desc->GetPictureParameters()[i]);
444 sep = ", ";
445 }
446 printf("]\n");
447 break;
448 }
449
450 case AP4_SampleDescription::TYPE_HEVC: {
451 // HEVC specifics
452 AP4_HevcSampleDescription* hevc_desc = AP4_DYNAMIC_CAST(AP4_HevcSampleDescription, desc);
453 const char* profile_name = AP4_HvccAtom::GetProfileName(hevc_desc->GetGeneralProfileSpace(), hevc_desc->GetGeneralProfile());
454 printf(" HEVC Profile Space: %d\n", hevc_desc->GetGeneralProfileSpace());
455 printf(" HEVC Profile: %d", hevc_desc->GetGeneralProfile());
456 if (profile_name) printf(" (%s)", profile_name);
457 printf("\n");
458 printf(" HEVC Profile Compat: %x\n", hevc_desc->GetGeneralProfileCompatibilityFlags());
459 printf(" HEVC Level: %d.%d\n", hevc_desc->GetGeneralLevel()/30, (hevc_desc->GetGeneralLevel()%30)/3);
460 printf(" HEVC Tier: %d\n", hevc_desc->GetGeneralTierFlag());
461 printf(" HEVC Chroma Format: %d", hevc_desc->GetChromaFormat());
462 const char* chroma_format_name = AP4_HvccAtom::GetChromaFormatName(hevc_desc->GetChromaFormat());
463 if (chroma_format_name) printf(" (%s)", chroma_format_name);
464 printf("\n");
465 printf(" HEVC Chroma Bit Depth: %d\n", hevc_desc->GetChromaBitDepth());
466 printf(" HEVC Luma Bit Depth: %d\n", hevc_desc->GetLumaBitDepth());
467 printf(" HEVC Average Frame Rate: %d\n", hevc_desc->GetAverageFrameRate());
468 printf(" HEVC Constant Frame Rate: %d\n", hevc_desc->GetConstantFrameRate());
469 printf(" HEVC NALU Length Size: %d\n", hevc_desc->GetNaluLengthSize());
470 printf(" HEVC Sequences:\n");
471 for (unsigned int i=0; i<hevc_desc->GetSequences().ItemCount(); i++) {
472 const AP4_HvccAtom::Sequence& seq = hevc_desc->GetSequences()[i];
473 printf(" {\n");
474 printf(" Array Completeness=%d\n", seq.m_ArrayCompleteness);
475 printf(" Type=%d", seq.m_NaluType);
476 const char* nalu_type_name = AP4_HevcNalParser::NaluTypeName(seq.m_NaluType);
477 if (nalu_type_name) {
478 printf(" (%s)", nalu_type_name);
479 }
480 printf("\n");
481 const char* sep = "";
482 for (unsigned int j=0; j<seq.m_Nalus.ItemCount(); j++) {
483 printf("%s ", sep);
484 ShowData(seq.m_Nalus[j]);
485 sep = "\n";
486 }
487 printf("\n }\n");
488 }
489 break;
490 }
491
492 case AP4_SampleDescription::TYPE_AV1: {
493 // AV1 specifics
494 AP4_Av1SampleDescription* av1_desc = AP4_DYNAMIC_CAST(AP4_Av1SampleDescription, desc);
495 const char* profile_name = AP4_Av1cAtom::GetProfileName(av1_desc->GetSeqProfile());
496 printf(" AV1 Profile: %d", av1_desc->GetSeqProfile());
497 if (profile_name) {
498 printf(" (%s)\n", profile_name);
499 } else {
500 printf("\n");
501 }
502 printf(" AV1 Level: %d.%d\n",
503 2 + (av1_desc->GetSeqLevelIdx0() / 4),
504 av1_desc->GetSeqLevelIdx0() % 4);
505 printf(" AV1 Tier: %d\n", av1_desc->GetSeqTier0());
506 break;
507 }
508
509 case AP4_SampleDescription::TYPE_SUBTITLES: {
510 // Subtitles
511 AP4_SubtitleSampleDescription* subt_desc = AP4_DYNAMIC_CAST(AP4_SubtitleSampleDescription, desc);
512 printf(" Subtitles:\n");
513 printf(" Namespace: %s\n", subt_desc->GetNamespace().GetChars());
514 printf(" Schema Location: %s\n", subt_desc->GetSchemaLocation().GetChars());
515 printf(" Image Mime Type: %s\n", subt_desc->GetImageMimeType().GetChars());
516 break;
517 }
518
519 default:
520 break;
521 }
522
523 AP4_AudioSampleDescription* audio_desc =
524 AP4_DYNAMIC_CAST(AP4_AudioSampleDescription, desc);
525 if (audio_desc) {
526 // Audio sample description
527 printf(" Sample Rate: %d\n", audio_desc->GetSampleRate());
528 printf(" Sample Size: %d\n", audio_desc->GetSampleSize());
529 printf(" Channels: %d\n", audio_desc->GetChannelCount());
530 }
531 AP4_VideoSampleDescription* video_desc =
532 AP4_DYNAMIC_CAST(AP4_VideoSampleDescription, desc);
533 if (video_desc) {
534 // Video sample description
535 printf(" Width: %d\n", video_desc->GetWidth());
536 printf(" Height: %d\n", video_desc->GetHeight());
537 printf(" Depth: %d\n", video_desc->GetDepth());
538 }
539
540 switch (desc->GetFormat()) {
541 case AP4_SAMPLE_FORMAT_AC_3: {
542 // Dolby Digital specifics
543 AP4_Dac3Atom* dac3 = AP4_DYNAMIC_CAST(AP4_Dac3Atom, desc->GetDetails().GetChild(AP4_ATOM_TYPE_DAC3));
544 if (dac3) {
545 printf(" AC-3 Data Rate: %d\n", dac3->GetDataRate());
546 printf(" AC-3 Stream:\n");
547 printf(" fscod = %d\n", dac3->GetStreamInfo().fscod);
548 printf(" bsid = %d\n", dac3->GetStreamInfo().bsid);
549 printf(" bsmod = %d\n", dac3->GetStreamInfo().bsmod);
550 printf(" acmod = %d\n", dac3->GetStreamInfo().acmod);
551 printf(" lfeon = %d\n", dac3->GetStreamInfo().lfeon);
552 printf(" AC-3 dac3 payload: [");
553 ShowData(dac3->GetRawBytes());
554 printf("]\n");
555 }
556 break;
557 }
558
559 case AP4_SAMPLE_FORMAT_EC_3: {
560 // Dolby Digital Plus specifics
561 AP4_Dec3Atom* dec3 = AP4_DYNAMIC_CAST(AP4_Dec3Atom, desc->GetDetails().GetChild(AP4_ATOM_TYPE_DEC3));
562 if (dec3) {
563 printf(" E-AC-3 Data Rate: %d\n", dec3->GetDataRate());
564 for (unsigned int i=0; i<dec3->GetSubStreams().ItemCount(); i++) {
565 printf(" E-AC-3 Substream %d:\n", i);
566 printf(" fscod = %d\n", dec3->GetSubStreams()[i].fscod);
567 printf(" bsid = %d\n", dec3->GetSubStreams()[i].bsid);
568 printf(" bsmod = %d\n", dec3->GetSubStreams()[i].bsmod);
569 printf(" acmod = %d\n", dec3->GetSubStreams()[i].acmod);
570 printf(" lfeon = %d\n", dec3->GetSubStreams()[i].lfeon);
571 printf(" num_dep_sub = %d\n", dec3->GetSubStreams()[i].num_dep_sub);
572 printf(" chan_loc = %d\n", dec3->GetSubStreams()[i].chan_loc);
573 }
574 if (dec3->GetFlagEC3ExtensionTypeA()){
575 printf(" Dolby Digital Plus with Dolby Atmos: Yes\n");
576 printf(" Dolby Atmos Complexity Index: %d\n", dec3->GetComplexityIndexTypeA());
577 } else {
578 printf(" Dolby Digital Plus with Dolby Atmos: No\n");
579 }
580 printf(" E-AC-3 dec3 payload: [");
581 ShowData(dec3->GetRawBytes());
582 printf("]\n");
583 }
584 break;
585 }
586
587 case AP4_SAMPLE_FORMAT_AC_4: {
588 // Dolby AC-4 specifics
589 AP4_Dac4Atom* dac4 = AP4_DYNAMIC_CAST(AP4_Dac4Atom, desc->GetDetails().GetChild(AP4_ATOM_TYPE_DAC4));
590 if (dac4) {
591 const AP4_Dac4Atom::Ac4Dsi& dsi = dac4->GetDsi();
592 unsigned short self_contained = 0;
593 if (dsi.ac4_dsi_version == 1) {
594 for (unsigned int i = 0; i < dsi.d.v1.n_presentations; i++) {
595 AP4_Dac4Atom::Ac4Dsi::PresentationV1& presentation = dsi.d.v1.presentations[i];
596 if (presentation.presentation_version == 1 || presentation.presentation_version == 2) {
597 printf(" AC-4 Presentation %d:\n", i);
598
599 char presentation_type [64];
600 char presentation_codec[64];
601 char presentation_lang [64 + 1] = "un";
602 AP4_FormatString(presentation_codec,
603 sizeof(presentation_codec),
604 "ac-4.%02x.%02x.%02x",
605 dsi.d.v1.bitstream_version,
606 presentation.presentation_version,
607 presentation.d.v1.mdcompat);
608 for (int sg = 0; sg < presentation.d.v1.n_substream_groups; sg++){
609 if ( presentation.d.v1.substream_groups[sg].d.v1.b_content_type &&
610 (presentation.d.v1.substream_groups[sg].d.v1.content_classifier == 0 ||presentation.d.v1.substream_groups[sg].d.v1.content_classifier == 4) &&
611 presentation.d.v1.substream_groups[sg].d.v1.b_language_indicator){
612 memcpy(presentation_lang, presentation.d.v1.substream_groups[sg].d.v1.language_tag_bytes, presentation.d.v1.substream_groups[sg].d.v1.n_language_tag_bytes);
613 presentation_lang[presentation.d.v1.substream_groups[sg].d.v1.n_language_tag_bytes] = '\0';
614 }
615 }
616 if (presentation.d.v1.b_multi_pid == 0) { self_contained ++; }
617 if (presentation.d.v1.b_presentation_channel_coded == 1) {
618 AP4_FormatString(presentation_type, sizeof(presentation_type), "Channel based");
619 if (presentation.d.v1.dsi_presentation_ch_mode >= 11 && presentation.d.v1.dsi_presentation_ch_mode <= 15) {
620 AP4_FormatString(presentation_type, sizeof(presentation_type), "Channel based immsersive");
621 }
622 } else {
623 AP4_FormatString(presentation_type, sizeof(presentation_type), "Object based");
624 }
625 if (presentation.presentation_version == 2) {
626 printf(" Stream Type = Immersive stereo\n");
627 }else {
628 printf(" Stream Type = %s\n", presentation_type);
629 }
630 printf(" presentation_id = %d\n", presentation.d.v1.b_presentation_id? presentation.d.v1.presentation_id : -1);
631 printf(" Codec String = %s\n", presentation_codec);
632 printf(" presentation_channel_mask_v1 = 0x%x\n", presentation.d.v1.presentation_channel_mask_v1);
633 printf(" Dolby Atmos source = %s\n", presentation.d.v1.dolby_atmos_indicator? "Yes": "No");
634 printf(" Language = %s\n", presentation_lang);
635 printf(" Self Contained = %s\n", presentation.d.v1.b_multi_pid? "No": "Yes");
636 }
637 }
638 }
639
640 printf(" Self Contained: %s\n", (self_contained == dsi.d.v1.n_presentations) ? "Yes": ((self_contained == 0)? "No": "Part"));
641 printf(" AC-4 dac4 payload: [");
642 ShowData(dac4->GetRawBytes());
643 printf("]\n");
644 }
645 break;
646 }
647
648 // VPx Specifics
649 case AP4_SAMPLE_FORMAT_VP8:
650 case AP4_SAMPLE_FORMAT_VP9:
651 case AP4_SAMPLE_FORMAT_VP10: {
652 AP4_VpccAtom* vpcc = AP4_DYNAMIC_CAST(AP4_VpccAtom, desc->GetDetails().GetChild(AP4_ATOM_TYPE_VPCC));
653 if (vpcc) {
654 printf(" Profile: %d\n", vpcc->GetProfile());
655 printf(" Level: %d\n", vpcc->GetLevel());
656 printf(" Bit Depth: %d\n", vpcc->GetBitDepth());
657 printf(" Chroma Subsampling: %d\n", vpcc->GetChromaSubsampling());
658 printf(" Colour Primaries: %d\n", vpcc->GetColourPrimaries());
659 printf(" Transfer Characteristics: %d\n", vpcc->GetTransferCharacteristics());
660 printf(" Matrix Coefficients: %d\n", vpcc->GetMatrixCoefficients());
661 printf(" Video Full Range Flag: %s\n", vpcc->GetVideoFullRangeFlag() ? "true" : "false");
662 }
663 break;
664 }
665 }
666
667 // Dolby Vision specifics
668 AP4_DvccAtom* dvcc = AP4_DYNAMIC_CAST(AP4_DvccAtom, desc->GetDetails().GetChild(AP4_ATOM_TYPE_DVCC));
669 if (dvcc) {
670 /* Dolby Vision */
671 printf(" Dolby Vision:\n");
672 printf(" Version: %d.%d\n", dvcc->GetDvVersionMajor(), dvcc->GetDvVersionMinor());
673 const char* profile_name = AP4_DvccAtom::GetProfileName(dvcc->GetDvProfile());
674 if (profile_name) {
675 printf(" Profile: %s\n", profile_name);
676 } else {
677 printf(" Profile: %d\n", dvcc->GetDvProfile());
678 }
679 printf(" Level: %d\n", dvcc->GetDvLevel());
680 printf(" RPU Present: %s\n", dvcc->GetRpuPresentFlag()?"true":"false");
681 printf(" EL Present: %s\n", dvcc->GetElPresentFlag()?"true":"false");
682 printf(" BL Present: %s\n", dvcc->GetBlPresentFlag()?"true":"false");
683 printf(" BL Signal Compatibility ID: %d\n", dvcc->GetDvBlSignalCompatibilityID());
684 }
685 }
686
687 /*----------------------------------------------------------------------
688 | ShowSampleDescription_Json
689 +---------------------------------------------------------------------*/
690 static void
ShowSampleDescription_Json(AP4_SampleDescription & description,bool verbose)691 ShowSampleDescription_Json(AP4_SampleDescription& description, bool verbose)
692 {
693 printf("{\n");
694 AP4_SampleDescription* desc = &description;
695 if (desc->GetType() == AP4_SampleDescription::TYPE_PROTECTED) {
696 AP4_ProtectedSampleDescription* prot_desc = AP4_DYNAMIC_CAST(AP4_ProtectedSampleDescription, desc);
697 if (prot_desc) {
698 ShowProtectedSampleDescription_Json(*prot_desc, verbose);
699 printf(",\n");
700 desc = prot_desc->GetOriginalSampleDescription();
701 }
702 }
703 char coding[5];
704 AP4_FormatFourChars(coding, desc->GetFormat());
705 const char* format_name = AP4_GetFormatName(desc->GetFormat());
706 printf("\"coding\":\"%s\",\n", coding);
707 printf("\"coding_name\":\"%s\",\n", format_name ? format_name : "");
708 AP4_String codec;
709 desc->GetCodecString(codec);
710 printf("\"codecs_string\":\"%s\"", codec.GetChars());
711
712 switch (desc->GetType()) {
713 case AP4_SampleDescription::TYPE_MPEG: {
714 // MPEG sample description
715 AP4_MpegSampleDescription* mpeg_desc = AP4_DYNAMIC_CAST(AP4_MpegSampleDescription, desc);
716
717 printf(",\n");
718 printf("\"stream_type\":%d,\n", mpeg_desc->GetStreamType());
719 printf("\"stream_type_name\":\"%s\",\n", mpeg_desc->GetStreamTypeString(mpeg_desc->GetStreamType()));
720 printf("\"object_type\":%d,\n", mpeg_desc->GetObjectTypeId());
721 printf("\"object_type_name\":\"%s\",\n", mpeg_desc->GetObjectTypeString(mpeg_desc->GetObjectTypeId()));
722 printf("\"max_bitrate\":%d,\n", mpeg_desc->GetMaxBitrate());
723 printf("\"average_bitrate\":%d,\n", mpeg_desc->GetAvgBitrate());
724 printf("\"buffer_size\":%d,\n", mpeg_desc->GetBufferSize());
725 printf("\"decoder_info\": \"");
726 ShowData(mpeg_desc->GetDecoderInfo());
727 printf("\"");
728 if (mpeg_desc->GetObjectTypeId() == AP4_OTI_MPEG4_AUDIO ||
729 mpeg_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_LC ||
730 mpeg_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_MAIN) {
731 AP4_MpegAudioSampleDescription* mpeg_audio_desc = AP4_DYNAMIC_CAST(AP4_MpegAudioSampleDescription, mpeg_desc);
732 if (mpeg_audio_desc) {
733 printf(",\n");
734 ShowMpegAudioSampleDescription(*mpeg_audio_desc);
735 }
736 }
737 break;
738 }
739
740 case AP4_SampleDescription::TYPE_AVC: {
741 // AVC specifics
742 AP4_AvcSampleDescription* avc_desc = AP4_DYNAMIC_CAST(AP4_AvcSampleDescription, desc);
743 printf(",\n");
744 printf("\"avc_profile\":%d,\n", avc_desc->GetProfile());
745 const char* profile_name = AP4_AvccAtom::GetProfileName(avc_desc->GetProfile());
746 if (profile_name) printf("\"avc_profile_name\":\"%s\",\n", profile_name);
747 printf("\"avc_profile_compat\":%d,\n", avc_desc->GetProfileCompatibility());
748 printf("\"avc_level\":%d,\n", avc_desc->GetLevel());
749 printf("\"avc_nalu_length_size\":%d,\n", avc_desc->GetNaluLengthSize());
750 printf("\"avc_sps\": [");
751 const char* sep = "";
752 for (unsigned int i=0; i<avc_desc->GetSequenceParameters().ItemCount(); i++) {
753 printf("%s", sep);
754 printf("\"");
755 ShowData(avc_desc->GetSequenceParameters()[i]);
756 printf("\"");
757 sep = ", ";
758 }
759 printf("],\n");
760 printf("\"avc_pps\": [");
761 sep = "";
762 for (unsigned int i=0; i<avc_desc->GetPictureParameters().ItemCount(); i++) {
763 printf("%s", sep);
764 printf("\"");
765 ShowData(avc_desc->GetPictureParameters()[i]);
766 printf("\"");
767 sep = ", ";
768 }
769 printf("]");
770 break;
771 }
772
773 case AP4_SampleDescription::TYPE_HEVC: {
774 // HEVC Specifics
775 AP4_HevcSampleDescription* hevc_desc = AP4_DYNAMIC_CAST(AP4_HevcSampleDescription, desc);
776 printf(",\n");
777 printf("\"hevc_profile_space\":%d,\n", hevc_desc->GetGeneralProfileSpace());
778 printf("\"hevc_profile\":%d,\n", hevc_desc->GetGeneralProfile());
779 const char* profile_name = AP4_HvccAtom::GetProfileName(hevc_desc->GetGeneralProfileSpace(), hevc_desc->GetGeneralProfile());
780 if (profile_name) printf("\"hevc_profile_name\":\"%s\",\n", profile_name);
781 printf("\"hevc_profile_compat\":%d,\n", hevc_desc->GetGeneralProfileCompatibilityFlags());
782 printf("\"hevc_constraints\":%llu,\n", hevc_desc->GetGeneralConstraintIndicatorFlags());
783 printf("\"hevc_level\":%d,\n", hevc_desc->GetGeneralLevel());
784 printf("\"hevc_level_name\":\"%d.%d\",\n", hevc_desc->GetGeneralLevel()/30, (hevc_desc->GetGeneralLevel()%30)/3);
785 printf("\"hevc_tier\":%d,\n", hevc_desc->GetGeneralTierFlag());
786 printf("\"hevc_chroma_format\":%d,\n", hevc_desc->GetChromaFormat());
787 const char* chroma_format_name = AP4_HvccAtom::GetChromaFormatName(hevc_desc->GetChromaFormat());
788 if (chroma_format_name) printf("\"hevc_chroma_format_name\":\"%s\",\n", chroma_format_name);
789 printf("\"hevc_chroma_bit_depth\":%d,\n", hevc_desc->GetChromaBitDepth());
790 printf("\"hevc_lunma_bit_depth\":%d,\n", hevc_desc->GetLumaBitDepth());
791 printf("\"hevc_average_frame_rate\":%d,\n", hevc_desc->GetAverageFrameRate());
792 printf("\"hevc_constant_frame_rate\":%d,\n", hevc_desc->GetConstantFrameRate());
793 printf("\"hevc_nalu_length_size\":%d,\n", hevc_desc->GetNaluLengthSize());
794 printf("\"hevc_sequences\": [\n");
795 const char* seq_sep = "";
796 for (unsigned int i=0; i<hevc_desc->GetSequences().ItemCount(); i++) {
797 const AP4_HvccAtom::Sequence& seq = hevc_desc->GetSequences()[i];
798 printf("%s {\n", seq_sep);
799 printf(" \"array_completeness\":%d,\n", seq.m_ArrayCompleteness);
800 printf(" \"type\":%d,\n", seq.m_NaluType);
801 const char* nalu_type_name = AP4_HevcNalParser::NaluTypeName(seq.m_NaluType);
802 if (nalu_type_name) {
803 printf(" \"type_name\":\"%s\",\n", nalu_type_name);
804 }
805 printf(" \"data\":[");
806 const char* sep = "";
807 for (unsigned int j=0; j<seq.m_Nalus.ItemCount(); j++) {
808 printf("%s", sep);
809 printf("\"");
810 ShowData(seq.m_Nalus[j]);
811 printf("\"");
812 sep = ", ";
813 }
814 printf("]\n }");
815 seq_sep = ",\n";
816 }
817 printf("]");
818 break;
819 }
820
821 case AP4_SampleDescription::TYPE_AV1: {
822 // AV1 specifics
823 AP4_Av1SampleDescription* av1_desc = AP4_DYNAMIC_CAST(AP4_Av1SampleDescription, desc);
824 printf(",\n");
825 printf("\"av1_seq_profile\":%d,\n", av1_desc->GetSeqProfile());
826 const char* profile_name = AP4_Av1cAtom::GetProfileName(av1_desc->GetSeqProfile());
827 if (profile_name) printf("\"av1_seq_profile_name\":\"%s\",\n", profile_name);
828 printf("\"av1_seq_level_idx_0\":%d,\n", av1_desc->GetSeqLevelIdx0());
829 printf("\"av1_seq_tier_0\":%d", av1_desc->GetSeqTier0());
830 break;
831 }
832
833 case AP4_SampleDescription::TYPE_SUBTITLES: {
834 // Subtitles
835 printf(",\n");
836 printf("\"subtitles\": {\n");
837 AP4_SubtitleSampleDescription* subt_desc = AP4_DYNAMIC_CAST(AP4_SubtitleSampleDescription, desc);
838 printf(" \"namespace\": \"%s\",\n", subt_desc->GetNamespace().GetChars());
839 printf(" \"schema_location\": \"%s\",\n", subt_desc->GetSchemaLocation().GetChars());
840 printf(" \"image_mime_type\": \"%s\"\n", subt_desc->GetImageMimeType().GetChars());
841 printf("}");
842 break;
843 }
844
845 default:
846 break;
847 }
848
849 AP4_AudioSampleDescription* audio_desc = AP4_DYNAMIC_CAST(AP4_AudioSampleDescription, desc);
850 if (audio_desc) {
851 // Audio sample description
852 printf(",\n");
853 printf("\"sample_rate\":%d,\n", audio_desc->GetSampleRate());
854 printf("\"sample_size\":%d,\n", audio_desc->GetSampleSize());
855 printf("\"channels\":%d", audio_desc->GetChannelCount());
856 }
857
858 AP4_VideoSampleDescription* video_desc =
859 AP4_DYNAMIC_CAST(AP4_VideoSampleDescription, desc);
860 if (video_desc) {
861 // Video sample description
862 printf(",\n");
863 printf("\"width\":%d,\n", video_desc->GetWidth());
864 printf("\"height\":%d,\n", video_desc->GetHeight());
865 printf("\"depth\":%d", video_desc->GetDepth());
866 }
867
868 switch (desc->GetFormat()) {
869 case AP4_SAMPLE_FORMAT_AC_3: {
870 // Dolby Digital specifics
871 AP4_Dac3Atom* dac3 = AP4_DYNAMIC_CAST(AP4_Dac3Atom, desc->GetDetails().GetChild(AP4_ATOM_TYPE('d', 'a', 'c', '3')));
872 if (dac3) {
873 printf(",\n");
874 printf("\"dolby_digital_info\": {\n");
875 printf(" \"dac3_payload\": \"");
876 ShowData(dac3->GetRawBytes());
877 printf("\",\n");
878 printf(" \"data_rate\": %d,\n", dac3->GetDataRate());
879 printf(" \"stream_info\": {\n");
880 printf(" \"fscod\": %d,\n", dac3->GetStreamInfo().fscod);
881 printf(" \"bsid\": %d,\n", dac3->GetStreamInfo().bsid);
882 printf(" \"bsmod\": %d,\n", dac3->GetStreamInfo().bsmod);
883 printf(" \"acmod\": %d,\n", dac3->GetStreamInfo().acmod);
884 printf(" \"lfeon\": %d\n", dac3->GetStreamInfo().lfeon);
885 printf(" }\n");
886 printf("}");
887 }
888 break;
889 }
890
891 case AP4_SAMPLE_FORMAT_EC_3: {
892 // Dolby Digital Plus specifics
893 AP4_Dec3Atom* dec3 = AP4_DYNAMIC_CAST(AP4_Dec3Atom, desc->GetDetails().GetChild(AP4_ATOM_TYPE('d', 'e', 'c', '3')));
894 if (dec3) {
895 printf(",\n");
896 printf("\"dolby_digital_info\": {\n");
897 printf(" \"dec3_payload\": \"");
898 ShowData(dec3->GetRawBytes());
899 printf("\",\n");
900 printf(" \"data_rate\": %d,\n", dec3->GetDataRate());
901 if (dec3->GetFlagEC3ExtensionTypeA()){
902 printf(" \"Dolby Digital Plus with Dolby Atmos\": \"Yes\",\n");
903 printf(" \"Dolby Atmos Complexity Index\": %d,\n", dec3->GetComplexityIndexTypeA());
904 } else {
905 printf(" \"Dolby Digital Plus with Dolby Atmos\": \"No\",\n");
906 }
907 printf(" \"substreams\": [\n");
908 const char* sep = "";
909 for (unsigned int i=0; i<dec3->GetSubStreams().ItemCount(); i++) {
910 printf("%s {\n", sep);
911 printf(" \"fscod\": %d,\n", dec3->GetSubStreams()[i].fscod);
912 printf(" \"bsid\": %d,\n", dec3->GetSubStreams()[i].bsid);
913 printf(" \"bsmod\": %d,\n", dec3->GetSubStreams()[i].bsmod);
914 printf(" \"acmod\": %d,\n", dec3->GetSubStreams()[i].acmod);
915 printf(" \"lfeon\": %d,\n", dec3->GetSubStreams()[i].lfeon);
916 printf(" \"num_dep_sub\": %d,\n", dec3->GetSubStreams()[i].num_dep_sub);
917 printf(" \"chan_loc\": %d\n", dec3->GetSubStreams()[i].chan_loc);
918 printf(" }");
919 sep = ",\n";
920 }
921 printf("\n ]\n}");
922 }
923 break;
924 }
925
926 case AP4_SAMPLE_FORMAT_AC_4: {
927 // Dolby AC-4 specifics
928 AP4_Dac4Atom* dac4 = AP4_DYNAMIC_CAST(AP4_Dac4Atom, desc->GetDetails().GetChild(AP4_ATOM_TYPE_DAC4));
929 if (dac4) {
930 printf(",\n");
931 printf("\"dolby_ac4_info\": {\n");
932
933 printf(" \"presentations\": [\n");
934 const AP4_Dac4Atom::Ac4Dsi& dsi = dac4->GetDsi();
935 unsigned short self_contained = 0;
936 if (dsi.ac4_dsi_version == 1) {
937 const char* separator = "";
938 for (unsigned int i = 0; i < dsi.d.v1.n_presentations; i++) {
939 AP4_Dac4Atom::Ac4Dsi::PresentationV1& presentation = dsi.d.v1.presentations[i];
940 if (presentation.presentation_version == 1 || presentation.presentation_version == 2) {
941 printf("%s { ", separator);
942
943 char presentation_type [64];
944 char presentation_codec[64];
945 char presentation_lang [64 + 1] = "un";
946 AP4_FormatString(presentation_codec,
947 sizeof(presentation_codec),
948 "ac-4.%02x.%02x.%02x",
949 dsi.d.v1.bitstream_version,
950 presentation.presentation_version,
951 presentation.d.v1.mdcompat);
952 for (int sg = 0; sg < presentation.d.v1.n_substream_groups; sg++){
953 if ( presentation.d.v1.substream_groups[sg].d.v1.b_content_type &&
954 (presentation.d.v1.substream_groups[sg].d.v1.content_classifier == 0 ||presentation.d.v1.substream_groups[sg].d.v1.content_classifier == 4) &&
955 presentation.d.v1.substream_groups[sg].d.v1.b_language_indicator){
956 memcpy(presentation_lang, presentation.d.v1.substream_groups[sg].d.v1.language_tag_bytes, presentation.d.v1.substream_groups[sg].d.v1.n_language_tag_bytes);
957 presentation_lang[presentation.d.v1.substream_groups[sg].d.v1.n_language_tag_bytes] = '\0';
958 }
959 }
960 if (presentation.d.v1.b_multi_pid == 0) { self_contained ++; }
961 if (presentation.d.v1.b_presentation_channel_coded == 1) {
962 AP4_FormatString(presentation_type, sizeof(presentation_type), "Channel based");
963 if (presentation.d.v1.dsi_presentation_ch_mode >= 11 && presentation.d.v1.dsi_presentation_ch_mode <= 15) {
964 AP4_FormatString(presentation_type, sizeof(presentation_type), "Channel based immsersive");
965 }
966 } else {
967 AP4_FormatString(presentation_type, sizeof(presentation_type), "Object based");
968 }
969 if (presentation.presentation_version == 2) {
970 printf("\"Stream Type\": \"Immersive stereo\", ");
971 } else {
972 printf("\"Stream Type\": \"%s\", ", presentation_type);
973 }
974 printf("\"presentation_id\": %d, ", presentation.d.v1.b_presentation_id? presentation.d.v1.presentation_id : -1);
975 printf("\"presentation_channel_mask_v1\": %u, ",presentation.d.v1.presentation_channel_mask_v1);
976 printf("\"Dolby Atmos source\": \"%s\", ", presentation.d.v1.dolby_atmos_indicator? "Yes": "No");
977 printf("\"Language\": \"%s\", ", presentation_lang);
978 printf("\"Self Contained\": \"%s\"} ", presentation.d.v1.b_multi_pid? "No": "Yes");
979 separator = ",\n";
980 }
981 }
982 }
983 printf("\n ],\n");
984
985 printf(" \"Self Contained\": \"%s\",\n", (self_contained == dsi.d.v1.n_presentations) ? "Yes": ((self_contained == 0)? "No": "Part"));
986 printf(" \"dac4_payload\": \"");
987 ShowData(dac4->GetRawBytes());
988 printf("\"\n}");
989 }
990 break;
991 }
992
993 case AP4_SAMPLE_FORMAT_VP8:
994 case AP4_SAMPLE_FORMAT_VP9:
995 case AP4_SAMPLE_FORMAT_VP10: {
996 // VPx Specifics
997 AP4_VpccAtom* vpcc = AP4_DYNAMIC_CAST(AP4_VpccAtom, desc->GetDetails().GetChild(AP4_ATOM_TYPE_VPCC));
998 if (vpcc) {
999 printf(",\n");
1000 printf("\"vpx_info\": {\n");
1001 printf(" \"profile\":%d,\n", vpcc->GetProfile());
1002 printf(" \"level\":%d,\n", vpcc->GetLevel());
1003 printf(" \"bit_depth\":%d,\n", vpcc->GetBitDepth());
1004 printf(" \"chroma_subsampling\":%d,\n", vpcc->GetChromaSubsampling());
1005 printf(" \"colour_primaries\":%d,\n", vpcc->GetColourPrimaries());
1006 printf(" \"transfer_characteristics\":%d,\n", vpcc->GetTransferCharacteristics());
1007 printf(" \"matrix_coefficients\":%d,\n", vpcc->GetMatrixCoefficients());
1008 printf(" \"video_full_range_flag\":%s\n", vpcc->GetVideoFullRangeFlag() ? "true" : "false");
1009 printf("}");
1010 }
1011 break;
1012 }
1013 }
1014
1015 // Dolby Vision specifics
1016 AP4_DvccAtom* dvcc = AP4_DYNAMIC_CAST(AP4_DvccAtom, desc->GetDetails().GetChild(AP4_ATOM_TYPE_DVCC));
1017 if (dvcc) {
1018 /* Dolby Vision */
1019 printf(",\n");
1020 printf("\"dolby_vision\": {\n");
1021 printf(" \"version_major\": %d,\n", dvcc->GetDvVersionMajor());
1022 printf(" \"version_minor\": %d,\n", dvcc->GetDvVersionMinor());
1023 printf(" \"profile\": %d,\n", dvcc->GetDvProfile());
1024 const char* profile_name = AP4_DvccAtom::GetProfileName(dvcc->GetDvProfile());
1025 printf(" \"profile_name\": \"%s\",\n", profile_name?profile_name:"");
1026 printf(" \"level\": %d,\n", dvcc->GetDvLevel());
1027 printf(" \"rpu_present\": %s,\n", dvcc->GetRpuPresentFlag()?"true":"false");
1028 printf(" \"el_present\": %s,\n", dvcc->GetElPresentFlag()?"true":"false");
1029 printf(" \"bl_present\": %s,\n", dvcc->GetBlPresentFlag()?"true":"false");
1030 printf(" \"dv_bl_signal_compatibility_id\": %d\n", dvcc->GetDvBlSignalCompatibilityID());
1031 printf("}");
1032 }
1033
1034 printf("\n}");
1035 }
1036
1037 /*----------------------------------------------------------------------
1038 | ShowSampleDescription
1039 +---------------------------------------------------------------------*/
1040 static void
ShowSampleDescription(AP4_SampleDescription & description,bool verbose)1041 ShowSampleDescription(AP4_SampleDescription& description, bool verbose)
1042 {
1043 switch (Options.format) {
1044 case TEXT_FORMAT: ShowSampleDescription_Text(description, verbose); break;
1045 case JSON_FORMAT: ShowSampleDescription_Json(description, verbose); break;
1046 }
1047 }
1048
1049 /*----------------------------------------------------------------------
1050 | ShowDcfInfo
1051 +---------------------------------------------------------------------*/
1052 static void
ShowDcfInfo(AP4_File & file)1053 ShowDcfInfo(AP4_File& file)
1054 {
1055 AP4_FtypAtom* ftyp = file.GetFileType();
1056 if (ftyp == NULL) return;
1057 printf("OMA DCF File, version=%d\n", ftyp->GetMinorVersion());
1058 if (ftyp->GetMinorVersion() != 2) return;
1059
1060 AP4_OdheAtom* odhe = AP4_DYNAMIC_CAST(AP4_OdheAtom, file.FindChild("odrm/odhe"));
1061 if (odhe) {
1062 printf("Content Type: %s\n", odhe->GetContentType().GetChars());
1063 }
1064 AP4_OhdrAtom* ohdr = AP4_DYNAMIC_CAST(AP4_OhdrAtom, file.FindChild("odrm/odhe/ohdr"));
1065 if (ohdr) {
1066 printf("Encryption Method: ");
1067 switch (ohdr->GetEncryptionMethod()) {
1068 case AP4_OMA_DCF_ENCRYPTION_METHOD_NULL: printf("NULL\n"); break;
1069 case AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CBC: printf("AES-128-CBC\n"); break;
1070 case AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CTR: printf("AES-128-CTR\n"); break;
1071 default: printf("%d\n", ohdr->GetEncryptionMethod());
1072 }
1073 printf("Padding Scheme: ");
1074 switch (ohdr->GetPaddingScheme()) {
1075 case AP4_OMA_DCF_PADDING_SCHEME_NONE: printf("NONE\n"); break;
1076 case AP4_OMA_DCF_PADDING_SCHEME_RFC_2630: printf("RFC 2630\n"); break;
1077 default: printf("%d\n", ohdr->GetPaddingScheme());
1078 }
1079 printf("Content ID: %s\n", ohdr->GetContentId().GetChars());
1080 printf("Rights Issuer URL: %s\n", ohdr->GetRightsIssuerUrl().GetChars());
1081 printf("Textual Headers:\n");
1082
1083 AP4_Size headers_size = ohdr->GetTextualHeaders().GetDataSize();
1084 const char* headers = (const char*)ohdr->GetTextualHeaders().GetData();
1085 while (headers_size) {
1086 printf(" %s\n", headers);
1087 AP4_Size header_len = (AP4_Size)strlen(headers);
1088 headers_size -= header_len+1;
1089 headers += header_len+1;
1090 }
1091 AP4_GrpiAtom* grpi = AP4_DYNAMIC_CAST(AP4_GrpiAtom, ohdr->GetChild(AP4_ATOM_TYPE_GRPI));
1092 if (grpi) {
1093 printf("Group ID: %s\n", grpi->GetGroupId().GetChars());
1094 }
1095 }
1096 }
1097
1098 /*----------------------------------------------------------------------
1099 | ReadGolomb
1100 +---------------------------------------------------------------------*/
1101 static unsigned int
ReadGolomb(AP4_BitStream & bits)1102 ReadGolomb(AP4_BitStream& bits)
1103 {
1104 unsigned int leading_zeros = 0;
1105 while (bits.ReadBit() == 0) {
1106 leading_zeros++;
1107 }
1108 if (leading_zeros) {
1109 return (1<<leading_zeros)-1+bits.ReadBits(leading_zeros);
1110 } else {
1111 return 0;
1112 }
1113 }
1114
1115 /*----------------------------------------------------------------------
1116 | ShowAvcInfo
1117 +---------------------------------------------------------------------*/
1118 static void
ShowAvcInfo(const AP4_DataBuffer & sample_data,AP4_AvcSampleDescription * avc_desc)1119 ShowAvcInfo(const AP4_DataBuffer& sample_data, AP4_AvcSampleDescription* avc_desc)
1120 {
1121 const unsigned char* data = sample_data.GetData();
1122 AP4_Size size = sample_data.GetDataSize();
1123
1124 while (size >= avc_desc->GetNaluLengthSize()) {
1125 unsigned int nalu_length = 0;
1126 if (avc_desc->GetNaluLengthSize() == 1) {
1127 nalu_length = *data++;
1128 --size;
1129 } else if (avc_desc->GetNaluLengthSize() == 2) {
1130 nalu_length = AP4_BytesToUInt16BE(data);
1131 data += 2;
1132 size -= 2;
1133 } else if (avc_desc->GetNaluLengthSize() == 4) {
1134 nalu_length = AP4_BytesToUInt32BE(data);
1135 data += 4;
1136 size -= 4;
1137 } else {
1138 return;
1139 }
1140 if (nalu_length <= size) {
1141 size -= nalu_length;
1142 } else {
1143 size = 0;
1144 }
1145
1146 switch (*data & 0x1F) {
1147 case 1: {
1148 AP4_BitStream bits;
1149 bits.WriteBytes(data+1, 8);
1150 ReadGolomb(bits);
1151 unsigned int slice_type = ReadGolomb(bits);
1152 switch (slice_type) {
1153 case 0: printf("<P>"); break;
1154 case 1: printf("<B>"); break;
1155 case 2: printf("<I>"); break;
1156 case 3: printf("<SP>"); break;
1157 case 4: printf("<SI>"); break;
1158 case 5: printf("<P>"); break;
1159 case 6: printf("<B>"); break;
1160 case 7: printf("<I>"); break;
1161 case 8: printf("<SP>"); break;
1162 case 9: printf("<SI>"); break;
1163 default: printf("<S/%d>", slice_type); break;
1164 }
1165 return; // only show first slice type
1166 }
1167
1168 case 5:
1169 printf("<I>");
1170 return;
1171 }
1172
1173 data += nalu_length;
1174 }
1175 }
1176
1177 /*----------------------------------------------------------------------
1178 | ShowSample_Text
1179 +---------------------------------------------------------------------*/
1180 static void
ShowSample_Text(AP4_Track & track,AP4_Sample & sample,AP4_DataBuffer & sample_data,unsigned int index,bool verbose,bool show_sample_data,AP4_AvcSampleDescription * avc_desc)1181 ShowSample_Text(AP4_Track& track,
1182 AP4_Sample& sample,
1183 AP4_DataBuffer& sample_data,
1184 unsigned int index,
1185 bool verbose,
1186 bool show_sample_data,
1187 AP4_AvcSampleDescription* avc_desc)
1188 {
1189 printf("[%02d.%06d] size=%6d duration=%6d",
1190 track.GetId(),
1191 index+1,
1192 (int)sample.GetSize(),
1193 (int)sample.GetDuration());
1194 if (verbose) {
1195 printf(" (%6d ms) offset=%10lld dts=%10lld (%10lld ms) cts=%10lld (%10lld ms) [%d]",
1196 (int)AP4_ConvertTime(sample.GetDuration(), track.GetMediaTimeScale(), 1000),
1197 sample.GetOffset(),
1198 sample.GetDts(),
1199 AP4_ConvertTime(sample.GetDts(), track.GetMediaTimeScale(), 1000),
1200 sample.GetCts(),
1201 AP4_ConvertTime(sample.GetCts(), track.GetMediaTimeScale(), 1000),
1202 sample.GetDescriptionIndex());
1203 }
1204 if (sample.IsSync()) {
1205 printf(" [S] ");
1206 } else {
1207 printf(" ");
1208 }
1209 if (avc_desc || show_sample_data) {
1210 sample.ReadData(sample_data);
1211 }
1212 if (avc_desc) {
1213 ShowAvcInfo(sample_data, avc_desc);
1214 }
1215 if (show_sample_data) {
1216 unsigned int show = sample_data.GetDataSize();
1217 if (!verbose) {
1218 if (show > 12) show = 12; // max first 12 chars
1219 }
1220
1221 for (unsigned int i=0; i<show; i++) {
1222 if (verbose) {
1223 if (i%16 == 0) {
1224 printf("\n%06d: ", i);
1225 }
1226 }
1227 printf("%02x", sample_data.GetData()[i]);
1228 if (verbose) printf(" ");
1229 }
1230 if (show != sample_data.GetDataSize()) {
1231 printf("...");
1232 }
1233 }
1234 }
1235
1236 /*----------------------------------------------------------------------
1237 | ScanMedia
1238 +---------------------------------------------------------------------*/
1239 static void
ScanMedia(AP4_Movie & movie,AP4_Track & track,AP4_ByteStream & stream,MediaInfo & info)1240 ScanMedia(AP4_Movie& movie, AP4_Track& track, AP4_ByteStream& stream, MediaInfo& info)
1241 {
1242 AP4_UI64 total_size = 0;
1243 AP4_UI64 total_duration = 0;
1244
1245 AP4_UI64 position;
1246 stream.Tell(position);
1247 stream.Seek(0);
1248 AP4_LinearReader reader(movie, &stream);
1249 reader.EnableTrack(track.GetId());
1250
1251 info.sample_count = 0;
1252
1253 AP4_Sample sample;
1254 if (movie.HasFragments()) {
1255 AP4_DataBuffer sample_data;
1256 for(unsigned int i=0; ; i++) {
1257 AP4_UI32 track_id = 0;
1258 AP4_Result result = reader.ReadNextSample(sample, sample_data, track_id);
1259 if (AP4_SUCCEEDED(result)) {
1260 total_size += sample.GetSize();
1261 total_duration += sample.GetDuration();
1262 ++info.sample_count;
1263 } else {
1264 break;
1265 }
1266 }
1267 } else {
1268 info.sample_count = track.GetSampleCount();
1269 for (unsigned int i=0; i<track.GetSampleCount(); i++) {
1270 if (AP4_SUCCEEDED(track.GetSample(i, sample))) {
1271 total_size += sample.GetSize();
1272 }
1273 }
1274 total_duration = track.GetMediaDuration();
1275 }
1276 info.duration = total_duration;
1277
1278 double duration_ms = (double)AP4_ConvertTime(total_duration, track.GetMediaTimeScale(), 1000);
1279 if (duration_ms) {
1280 info.bitrate = 8.0*1000.0*(double)total_size/duration_ms;
1281 } else {
1282 info.bitrate = 0.0;
1283 }
1284 }
1285
1286 /*----------------------------------------------------------------------
1287 | ShowTrackInfo_Text
1288 +---------------------------------------------------------------------*/
1289 static void
ShowTrackInfo_Text(AP4_Movie & movie,AP4_Track & track,AP4_ByteStream & stream,bool show_samples,bool show_sample_data,bool verbose,bool fast)1290 ShowTrackInfo_Text(AP4_Movie& movie, AP4_Track& track, AP4_ByteStream& stream, bool show_samples, bool show_sample_data, bool verbose, bool fast)
1291 {
1292 printf(" flags: %d", track.GetFlags());
1293 if (track.GetFlags() & AP4_TRACK_FLAG_ENABLED) {
1294 printf(" ENABLED");
1295 }
1296 if (track.GetFlags() & AP4_TRACK_FLAG_IN_MOVIE) {
1297 printf(" IN-MOVIE");
1298 }
1299 if (track.GetFlags() & AP4_TRACK_FLAG_IN_PREVIEW) {
1300 printf(" IN-PREVIEW");
1301 }
1302 printf("\n");
1303 printf(" id: %d\n", track.GetId());
1304 printf(" type: ");
1305 switch (track.GetType()) {
1306 case AP4_Track::TYPE_AUDIO: printf("Audio\n"); break;
1307 case AP4_Track::TYPE_VIDEO: printf("Video\n"); break;
1308 case AP4_Track::TYPE_HINT: printf("Hint\n"); break;
1309 case AP4_Track::TYPE_SYSTEM: printf("System\n"); break;
1310 case AP4_Track::TYPE_TEXT: printf("Text\n"); break;
1311 case AP4_Track::TYPE_JPEG: printf("JPEG\n"); break;
1312 case AP4_Track::TYPE_SUBTITLES: printf("Subtitles\n"); break;
1313 default: {
1314 char hdlr[5];
1315 AP4_FormatFourChars(hdlr, track.GetHandlerType());
1316 printf("Unknown [");
1317 printf("%s", hdlr);
1318 printf("]\n");
1319 break;
1320 }
1321 }
1322 printf(" duration: %d ms\n", track.GetDurationMs());
1323 printf(" language: %s\n", track.GetTrackLanguage());
1324 printf(" media:\n");
1325 printf(" sample count: %d\n", track.GetSampleCount());
1326 printf(" timescale: %d\n", track.GetMediaTimeScale());
1327 printf(" duration: %lld (media timescale units)\n", track.GetMediaDuration());
1328 printf(" duration: %d (ms)\n", (AP4_UI32)AP4_ConvertTime(track.GetMediaDuration(), track.GetMediaTimeScale(), 1000));
1329 if (!fast) {
1330 MediaInfo media_info;
1331 ScanMedia(movie, track, stream, media_info);
1332 printf(" bitrate (computed): %.3f Kbps\n", media_info.bitrate/1000.0);
1333 if (movie.HasFragments()) {
1334 printf(" sample count with fragments: %lld\n", media_info.sample_count);
1335 printf(" duration with fragments: %lld\n", media_info.duration);
1336 printf(" duration with fragments: %d (ms)\n", (AP4_UI32)AP4_ConvertTime(media_info.duration, track.GetMediaTimeScale(), 1000));
1337 }
1338 }
1339 if (track.GetWidth() || track.GetHeight()) {
1340 printf(" display width: %f\n", (float)track.GetWidth()/65536.0);
1341 printf(" display height: %f\n", (float)track.GetHeight()/65536.0);
1342 }
1343 if (track.GetType() == AP4_Track::TYPE_VIDEO && track.GetSampleCount()) {
1344 printf(" frame rate (computed): %.3f\n", (float)track.GetSampleCount()/
1345 ((float)track.GetMediaDuration()/(float)track.GetMediaTimeScale()));
1346 }
1347
1348 // show all sample descriptions
1349 AP4_AvcSampleDescription* avc_desc = NULL;
1350 for (unsigned int desc_index=0;
1351 AP4_SampleDescription* sample_desc = track.GetSampleDescription(desc_index);
1352 desc_index++) {
1353 printf(" Sample Description %d\n", desc_index);
1354 ShowSampleDescription(*sample_desc, verbose);
1355 avc_desc = AP4_DYNAMIC_CAST(AP4_AvcSampleDescription, sample_desc);
1356 }
1357
1358 // show samples if requested
1359 if (show_samples) {
1360 AP4_Sample sample;
1361 AP4_DataBuffer sample_data;
1362 AP4_Ordinal index = 0;
1363 while (AP4_SUCCEEDED(track.GetSample(index, sample))) {
1364 if (avc_desc || show_sample_data) {
1365 sample.ReadData(sample_data);
1366 }
1367
1368 ShowSample_Text(track, sample, sample_data, index, verbose, show_sample_data, avc_desc);
1369 printf("\n");
1370 index++;
1371 }
1372 }
1373 }
1374
1375 /*----------------------------------------------------------------------
1376 | ShowTrackInfo_Json
1377 +---------------------------------------------------------------------*/
1378 static void
ShowTrackInfo_Json(AP4_Movie & movie,AP4_Track & track,AP4_ByteStream & stream,bool,bool,bool verbose,bool fast)1379 ShowTrackInfo_Json(AP4_Movie& movie, AP4_Track& track, AP4_ByteStream& stream, bool /*show_samples*/, bool /*show_sample_data*/, bool verbose, bool fast)
1380 {
1381 printf("{\n");
1382 printf(" \"flags\":%d,\n", track.GetFlags());
1383 printf(" \"flag_names\":[");
1384 const char* sep = "";
1385 if (track.GetFlags() & AP4_TRACK_FLAG_ENABLED) {
1386 printf("\"ENABLED\"");
1387 sep = " ,";
1388 }
1389 if (track.GetFlags() & AP4_TRACK_FLAG_IN_MOVIE) {
1390 printf("%s\"IN-MOVIE\"", sep);
1391 sep = " ,";
1392 }
1393 if (track.GetFlags() & AP4_TRACK_FLAG_IN_PREVIEW) {
1394 printf("%s\"IN-PREVIEW\"", sep);
1395 }
1396 printf("],\n");
1397 printf(" \"id\":%d,\n", track.GetId());
1398 printf(" \"type\":");
1399 switch (track.GetType()) {
1400 case AP4_Track::TYPE_AUDIO: printf("\"Audio\""); break;
1401 case AP4_Track::TYPE_VIDEO: printf("\"Video\""); break;
1402 case AP4_Track::TYPE_HINT: printf("\"Hint\""); break;
1403 case AP4_Track::TYPE_SYSTEM: printf("\"System\""); break;
1404 case AP4_Track::TYPE_TEXT: printf("\"Text\""); break;
1405 case AP4_Track::TYPE_JPEG: printf("\"JPEG\""); break;
1406 case AP4_Track::TYPE_SUBTITLES: printf("\"Subtitles\""); break;
1407 default: {
1408 char hdlr[5];
1409 AP4_FormatFourChars(hdlr, track.GetHandlerType());
1410 printf("\"Unknown [");
1411 printf("%s", hdlr);
1412 printf("]\"");
1413 break;
1414 }
1415 }
1416 printf(",\n");
1417 printf(" \"duration_ms\":%d,\n", track.GetDurationMs());
1418 printf(" \"language\":\"%s\",\n", track.GetTrackLanguage());
1419 printf(" \"media\":{\n");
1420 printf(" \"sample_count\":%d,\n", track.GetSampleCount());
1421 printf(" \"timescale\":%d,\n", track.GetMediaTimeScale());
1422 printf(" \"duration\":%lld,\n", track.GetMediaDuration());
1423 printf(" \"duration_ms\":%d", (AP4_UI32)AP4_ConvertTime(track.GetMediaDuration(), track.GetMediaTimeScale(), 1000));
1424 if (!fast) {
1425 MediaInfo media_info;
1426 ScanMedia(movie, track, stream, media_info);
1427 printf(",\n");
1428 printf(" \"bitrate\":%.3f", media_info.bitrate/1000.0);
1429 if (movie.HasFragments()) {
1430 printf(",\n");
1431 printf(" \"sample_count_with_fragments\":%lld,\n", media_info.sample_count);
1432 printf(" \"duration_with_fragments\":%lld,\n", media_info.duration);
1433 printf(" \"duration_with_fragments_ms\":%d", (AP4_UI32)AP4_ConvertTime(media_info.duration, track.GetMediaTimeScale(), 1000));
1434 }
1435 printf("\n");
1436 } else {
1437 printf("\n");
1438 }
1439 printf(" },\n");
1440 if (track.GetWidth() || track.GetHeight()) {
1441 printf(" \"display_width\":%f,\n", (float)track.GetWidth()/65536.0);
1442 printf(" \"display_height\":%f,\n", (float)track.GetHeight()/65536.0);
1443 }
1444 if (track.GetType() == AP4_Track::TYPE_VIDEO && track.GetSampleCount()) {
1445 printf(" \"frame_rate\":%.3f,\n", (float)1000*track.GetSampleCount()/
1446 (float)track.GetDurationMs());
1447 }
1448
1449 // show all sample descriptions
1450 printf(" \"sample_descriptions\":[\n");
1451 //AP4_AvcSampleDescription* avc_desc = NULL;
1452 sep = "";
1453 for (unsigned int desc_index=0;
1454 AP4_SampleDescription* sample_desc = track.GetSampleDescription(desc_index);
1455 desc_index++) {
1456 printf("%s", sep);
1457 ShowSampleDescription(*sample_desc, verbose);
1458 sep = ",\n";
1459 //if (sample_desc->GetFormat() == AP4_SAMPLE_FORMAT_AVC1) {
1460 // avc_desc = AP4_DYNAMIC_CAST(AP4_AvcSampleDescription, sample_desc);
1461 //}
1462 }
1463 printf(" ]\n");
1464 printf("}");
1465 }
1466
1467 /*----------------------------------------------------------------------
1468 | ShowTrackInfo
1469 +---------------------------------------------------------------------*/
1470 static void
ShowTrackInfo(AP4_Movie & movie,AP4_Track & track,AP4_ByteStream & stream,bool show_samples,bool show_sample_data,bool verbose,bool fast)1471 ShowTrackInfo(AP4_Movie& movie, AP4_Track& track, AP4_ByteStream& stream, bool show_samples, bool show_sample_data, bool verbose, bool fast)
1472 {
1473 switch (Options.format) {
1474 case TEXT_FORMAT:
1475 ShowTrackInfo_Text(movie, track, stream, show_samples, show_sample_data, verbose, fast);
1476 break;
1477
1478 case JSON_FORMAT:
1479 ShowTrackInfo_Json(movie, track, stream, show_samples, show_sample_data, verbose, fast);
1480 break;
1481 }
1482 }
1483
1484 /*----------------------------------------------------------------------
1485 | ShowMovieInfo
1486 +---------------------------------------------------------------------*/
1487 static void
ShowMovieInfo(AP4_Movie & movie)1488 ShowMovieInfo(AP4_Movie& movie)
1489 {
1490 switch (Options.format) {
1491 case TEXT_FORMAT:
1492 printf("Movie:\n");
1493 printf(" duration: %lld (media timescale units)\n", movie.GetDuration());
1494 printf(" duration: %d (ms)\n", movie.GetDurationMs());
1495 printf(" time scale: %d\n", movie.GetTimeScale());
1496 printf(" fragments: %s\n", movie.HasFragments()?"yes":"no");
1497 printf("\n");
1498 break;
1499
1500 case JSON_FORMAT:
1501 printf("\"movie\":{\n");
1502 printf(" \"duration_ms\":%d,\n", movie.GetDurationMs());
1503 printf(" \"duration\":%lld,\n", movie.GetDuration());
1504 printf(" \"time_scale\":%d,\n", movie.GetTimeScale());
1505 printf(" \"fragments\":%s\n", movie.HasFragments()?"true":"false");
1506 printf("},\n");
1507 }
1508 }
1509
1510 /*----------------------------------------------------------------------
1511 | ShowFileInfo
1512 +---------------------------------------------------------------------*/
1513 static void
ShowFileInfo(AP4_File & file)1514 ShowFileInfo(AP4_File& file)
1515 {
1516 AP4_FtypAtom* file_type = file.GetFileType();
1517 if (file_type == NULL) return;
1518 char four_cc[5];
1519
1520 AP4_FormatFourChars(four_cc, file_type->GetMajorBrand());
1521 switch (Options.format) {
1522 case TEXT_FORMAT:
1523 printf("File:\n");
1524 printf(" major brand: %s\n", four_cc);
1525 printf(" minor version: %x\n", file_type->GetMinorVersion());
1526 break;
1527
1528 case JSON_FORMAT:
1529 printf("\"file\":{\n");
1530 printf(" \"major_brand\":\"%s\",\n", four_cc);
1531 printf(" \"minor_version\":%d,\n", file_type->GetMinorVersion());
1532 break;
1533 }
1534
1535 // compatible brands
1536 if (Options.format == JSON_FORMAT) printf(" \"compatible_brands\":[");
1537 const char* sep = "";
1538 for (unsigned int i=0; i<file_type->GetCompatibleBrands().ItemCount(); i++) {
1539 AP4_UI32 cb = file_type->GetCompatibleBrands()[i];
1540 if (cb == 0) continue;
1541 AP4_FormatFourChars(four_cc, cb);
1542 switch (Options.format) {
1543 case TEXT_FORMAT:
1544 printf(" compatible brand: %s\n", four_cc);
1545 break;
1546
1547 case JSON_FORMAT:
1548 printf("%s\"%s\"", sep, four_cc);
1549 sep = ", ";
1550 break;
1551 }
1552 }
1553
1554 if (Options.format == JSON_FORMAT) {
1555 printf("],\n");
1556 }
1557
1558 // fast-start
1559 switch (Options.format) {
1560 case TEXT_FORMAT:
1561 printf(" fast start: %s\n\n", file.IsMoovBeforeMdat() ? "yes" : "no");
1562 break;
1563
1564 case JSON_FORMAT:
1565 printf(" \"fast_start\":%s\n},\n", file.IsMoovBeforeMdat() ? "true" : "false");
1566 break;
1567 }
1568 }
1569
1570
1571 /*----------------------------------------------------------------------
1572 | ShowTracks
1573 +---------------------------------------------------------------------*/
1574 static void
ShowTracks(AP4_Movie & movie,AP4_List<AP4_Track> & tracks,AP4_ByteStream & stream,bool show_samples,bool show_sample_data,bool verbose,bool fast)1575 ShowTracks(AP4_Movie& movie, AP4_List<AP4_Track>& tracks, AP4_ByteStream& stream, bool show_samples, bool show_sample_data, bool verbose, bool fast)
1576 {
1577 if (Options.format == JSON_FORMAT) printf("\"tracks\":[\n");
1578 int index=1;
1579 for (AP4_List<AP4_Track>::Item* track_item = tracks.FirstItem();
1580 track_item;
1581 track_item = track_item->GetNext(), ++index) {
1582 if (Options.format == TEXT_FORMAT) {
1583 printf("Track %d:\n", index);
1584 }
1585 if (Options.format == JSON_FORMAT && index > 1) printf(",\n");
1586 ShowTrackInfo(movie, *track_item->GetData(), stream, show_samples, show_sample_data, verbose, fast);
1587 }
1588 if (Options.format == JSON_FORMAT) printf("]\n");
1589 }
1590
1591 /*----------------------------------------------------------------------
1592 | ShowMarlinTracks
1593 +---------------------------------------------------------------------*/
1594 static void
ShowMarlinTracks(AP4_File & file,AP4_ByteStream & stream,AP4_List<AP4_Track> & tracks,bool show_samples,bool show_sample_data,bool verbose,bool fast)1595 ShowMarlinTracks(AP4_File& file, AP4_ByteStream& stream, AP4_List<AP4_Track>& tracks, bool show_samples, bool show_sample_data, bool verbose, bool fast)
1596 {
1597 if (Options.format != TEXT_FORMAT) return;
1598
1599 AP4_List<AP4_MarlinIpmpParser::SinfEntry> sinf_entries;
1600 AP4_Result result = AP4_MarlinIpmpParser::Parse(file, stream, sinf_entries);
1601 if (AP4_FAILED(result)) {
1602 printf("WARNING: cannot parse Marlin IPMP info\n");
1603 ShowTracks(*file.GetMovie(), tracks, stream, show_samples, show_sample_data, verbose, fast);
1604 return;
1605 }
1606 int index=1;
1607 for (AP4_List<AP4_Track>::Item* track_item = tracks.FirstItem();
1608 track_item;
1609 track_item = track_item->GetNext(), ++index) {
1610 printf("Track %d:\n", index);
1611 AP4_Track* track = track_item->GetData();
1612 ShowTrackInfo(*file.GetMovie(), *track, stream, show_samples, show_sample_data, verbose, fast);
1613
1614 for (AP4_List<AP4_MarlinIpmpParser::SinfEntry>::Item* sinf_entry_item = sinf_entries.FirstItem();
1615 sinf_entry_item;
1616 sinf_entry_item = sinf_entry_item->GetNext()) {
1617 AP4_MarlinIpmpParser::SinfEntry* sinf_entry = sinf_entry_item->GetData();
1618 if (sinf_entry->m_TrackId == track->GetId()) {
1619 printf(" [ENCRYPTED]\n");
1620 if (sinf_entry->m_Sinf == NULL) {
1621 printf("WARNING: NULL sinf entry for track ID %d\n", track->GetId());
1622 } else {
1623 AP4_ContainerAtom* schi = AP4_DYNAMIC_CAST(AP4_ContainerAtom, sinf_entry->m_Sinf->GetChild(AP4_ATOM_TYPE_SCHI));
1624 AP4_SchmAtom* schm = AP4_DYNAMIC_CAST(AP4_SchmAtom, sinf_entry->m_Sinf->GetChild(AP4_ATOM_TYPE_SCHM));
1625 if (schm && schi) {
1626 AP4_UI32 scheme_type = schm->GetSchemeType();
1627 printf(" Scheme Type: %c%c%c%c\n",
1628 (char)((scheme_type>>24) & 0xFF),
1629 (char)((scheme_type>>16) & 0xFF),
1630 (char)((scheme_type>> 8) & 0xFF),
1631 (char)((scheme_type ) & 0xFF));
1632 printf(" Scheme Version: %d\n", schm->GetSchemeVersion());
1633 ShowProtectionSchemeInfo_Text(scheme_type, *schi, verbose);
1634 } else {
1635 if (schm == NULL) printf("WARNING: schm atom is NULL\n");
1636 if (schi == NULL) printf("WARNING: schi atom is NULL\n");
1637 }
1638 }
1639 }
1640 }
1641 }
1642 sinf_entries.DeleteReferences();
1643 }
1644
1645 /*----------------------------------------------------------------------
1646 | ShowSampleLayout
1647 +---------------------------------------------------------------------*/
1648 static void
ShowSampleLayout(AP4_List<AP4_Track> & tracks,bool)1649 ShowSampleLayout(AP4_List<AP4_Track>& tracks, bool /* verbose */)
1650 {
1651 AP4_Array<int> cursors;
1652 cursors.SetItemCount(tracks.ItemCount());
1653 for (unsigned int i=0; i<tracks.ItemCount(); i++) {
1654 cursors[i] = 0;
1655 }
1656
1657 AP4_Sample sample;
1658 AP4_UI64 sample_offset = 0;
1659 AP4_UI32 sample_size = 0;
1660 AP4_UI64 sample_dts = 0;
1661 bool sample_is_sync = false;
1662 bool indicator = true;
1663 AP4_Track* previous_track = NULL;
1664 for (unsigned int i=0;;i++) {
1665 AP4_UI64 min_offset = (AP4_UI64)(-1);
1666 int chosen_index = -1;
1667 AP4_Track* chosen_track = NULL;
1668 AP4_Ordinal index = 0;
1669 for (AP4_List<AP4_Track>::Item* track_item = tracks.FirstItem();
1670 track_item;
1671 track_item = track_item->GetNext()) {
1672 AP4_Track* track = track_item->GetData();
1673 AP4_Result result = track->GetSample(cursors[index], sample);
1674 if (AP4_SUCCEEDED(result)) {
1675 if (sample.GetOffset() < min_offset) {
1676 chosen_index = index;
1677 chosen_track = track;
1678 sample_offset = sample.GetOffset();
1679 sample_size = sample.GetSize();
1680 sample_dts = sample.GetDts();
1681 sample_is_sync = sample.IsSync();
1682 min_offset = sample_offset;
1683 }
1684 }
1685 index++;
1686 }
1687
1688 // stop if we've exhausted all samples
1689 if (chosen_index == -1) break;
1690
1691 // update the cursor for the chosen track
1692 cursors[chosen_index] = cursors[chosen_index]+1;
1693
1694 // see if we've changed tracks
1695 if (previous_track != chosen_track) {
1696 previous_track = chosen_track;
1697 indicator = !indicator;
1698 }
1699
1700 // show the chosen track/sample
1701 char track_type = ' ';
1702 switch (chosen_track->GetType()) {
1703 case AP4_Track::TYPE_AUDIO: track_type = 'A'; break;
1704 case AP4_Track::TYPE_VIDEO: track_type = 'V'; break;
1705 case AP4_Track::TYPE_HINT: track_type = 'H'; break;
1706 case AP4_Track::TYPE_TEXT: track_type = 'T'; break;
1707 case AP4_Track::TYPE_SYSTEM: track_type = 'S'; break;
1708 case AP4_Track::TYPE_SUBTITLES: track_type = 'U'; break;
1709 default: track_type = ' '; break;
1710 }
1711 AP4_UI64 sample_dts_ms = AP4_ConvertTime(sample_dts, chosen_track->GetMediaTimeScale(), 1000);
1712 printf("%c %08d [%c] (%d)%c size=%6d, offset=%8lld, dts=%lld (%lld ms)\n",
1713 indicator?'|':' ',
1714 i,
1715 track_type,
1716 chosen_track->GetId(),
1717 sample_is_sync?'*':' ',
1718 sample_size,
1719 sample_offset,
1720 sample_dts,
1721 sample_dts_ms);
1722 }
1723 }
1724
1725 /*----------------------------------------------------------------------
1726 | ShowFragments_Text
1727 +---------------------------------------------------------------------*/
1728 static void
ShowFragments_Text(AP4_Movie & movie,bool verbose,bool show_sample_data,AP4_ByteStream * stream)1729 ShowFragments_Text(AP4_Movie& movie, bool verbose, bool show_sample_data, AP4_ByteStream* stream)
1730 {
1731 stream->Seek(0);
1732 AP4_LinearReader reader(movie, stream);
1733 AP4_List<AP4_Track>::Item* track_item = movie.GetTracks().FirstItem();
1734 AP4_Array<unsigned int> counters;
1735 while (track_item) {
1736 reader.EnableTrack(track_item->GetData()->GetId());
1737 track_item = track_item->GetNext();
1738 counters.Append(0);
1739 }
1740
1741 AP4_Sample sample;
1742 AP4_DataBuffer sample_data;
1743 AP4_UI32 prev_track_id = 0;
1744 unsigned int* counter = &counters[0];
1745 for (;;) {
1746 AP4_UI32 track_id = 0;
1747 AP4_Result result = reader.ReadNextSample(sample, sample_data, track_id);
1748 if (AP4_SUCCEEDED(result)) {
1749 AP4_Track* track = movie.GetTrack(track_id);
1750
1751 AP4_SampleDescription* sample_desc = track->GetSampleDescription(sample.GetDescriptionIndex());
1752 AP4_AvcSampleDescription* avc_desc = AP4_DYNAMIC_CAST(AP4_AvcSampleDescription, sample_desc);
1753
1754 if (track_id != prev_track_id) {
1755 printf("Track %d:\n", track_id);
1756 prev_track_id = track_id;
1757
1758 // find the right counter for this track
1759 unsigned int counter_index = 0;
1760 for (AP4_List<AP4_Track>::Item* track_item = movie.GetTracks().FirstItem();
1761 track_item;
1762 track_item = track_item->GetNext(), ++counter_index) {
1763 if (track_item->GetData()->GetId() == track_id) {
1764 if (counter_index < counters.ItemCount()) {
1765 counter = &counters[counter_index];
1766 }
1767 break;
1768 }
1769 }
1770 }
1771
1772 ShowSample_Text(*track, sample, sample_data, *counter, verbose, show_sample_data, avc_desc);
1773 printf("\n");
1774
1775 ++*counter;
1776 } else {
1777 break;
1778 }
1779 }
1780 }
1781
1782 /*----------------------------------------------------------------------
1783 | main
1784 +---------------------------------------------------------------------*/
1785 int
main(int argc,char ** argv)1786 main(int argc, char** argv)
1787 {
1788 if (argc < 2) {
1789 PrintUsageAndExit();
1790 }
1791 Options.format = TEXT_FORMAT;
1792 const char* filename = NULL;
1793 bool verbose = false;
1794 bool show_samples = false;
1795 bool show_sample_data = false;
1796 bool show_layout = false;
1797 bool fast = false;
1798
1799 while (char* arg = *++argv) {
1800 if (!strcmp(arg, "--verbose")) {
1801 verbose = true;
1802 } else if (!strcmp(arg, "--format")) {
1803 if (argv) {
1804 arg = *++argv;
1805 if (!strcmp(arg, "text")) {
1806 Options.format = TEXT_FORMAT;
1807 } else if (!strcmp(arg, "json")) {
1808 Options.format = JSON_FORMAT;
1809 } else {
1810 fprintf(stderr, "ERROR: unsupported format\n");
1811 return 1;
1812 }
1813 } else {
1814 fprintf(stderr, "ERROR: missing argument after '--format' option\n");
1815 return 1;
1816 }
1817
1818 } else if (!strcmp(arg, "--fast")) {
1819 fast = true;
1820 } else if (!strcmp(arg, "--show-samples")) {
1821 show_samples = true;
1822 } else if (!strcmp(arg, "--show-sample-data")) {
1823 show_samples = true;
1824 show_sample_data = true;
1825 } else if (!strcmp(arg, "--show-layout")) {
1826 show_layout = true;
1827 } else {
1828 if (filename == NULL) {
1829 filename = arg;
1830 } else {
1831 fprintf(stderr, "ERROR: unexpected argument '%s'\n", arg);
1832 return 1;
1833 }
1834 }
1835 }
1836 if (filename == NULL) {
1837 fprintf(stderr, "ERROR: filename missing\n");
1838 return 1;
1839 }
1840
1841 AP4_ByteStream* input = NULL;
1842 AP4_Result result = AP4_FileByteStream::Create(filename,
1843 AP4_FileByteStream::STREAM_MODE_READ,
1844 input);
1845 if (AP4_FAILED(result)) {
1846 fprintf(stderr, "ERROR: cannot open input file %s (%d)\n", filename, result);
1847 return 1;
1848 }
1849
1850 if (Options.format == JSON_FORMAT) printf("{\n");
1851
1852 AP4_File* file = new AP4_File(*input, true);
1853 ShowFileInfo(*file);
1854
1855 AP4_Movie* movie = file->GetMovie();
1856 AP4_FtypAtom* ftyp = file->GetFileType();
1857 if (movie) {
1858 ShowMovieInfo(*movie);
1859
1860 AP4_List<AP4_Track>& tracks = movie->GetTracks();
1861 if (Options.format == TEXT_FORMAT) {
1862 printf("Found %d Tracks\n", tracks.ItemCount());
1863 }
1864
1865 if (ftyp && ftyp->GetMajorBrand() == AP4_MARLIN_BRAND_MGSV) {
1866 ShowMarlinTracks(*file, *input, tracks, show_samples, show_sample_data, verbose, fast);
1867 } else {
1868 ShowTracks(*movie, tracks, *input, show_samples, show_sample_data, verbose, fast);
1869 }
1870
1871 if (show_layout) {
1872 ShowSampleLayout(tracks, verbose);
1873 }
1874
1875 if (movie->HasFragments() && show_samples) {
1876 if (Options.format == TEXT_FORMAT) {
1877 ShowFragments_Text(*movie, verbose, show_sample_data, input);
1878 }
1879 }
1880 } else {
1881 // check if this is a DCF file
1882 if (ftyp && ftyp->GetMajorBrand() == AP4_OMA_DCF_BRAND_ODCF) {
1883 ShowDcfInfo(*file);
1884 } else {
1885 if (Options.format == TEXT_FORMAT) {
1886 printf("No movie found in the file\n");
1887 } else {
1888 printf("\"movie\":{}\n");
1889 }
1890 }
1891 }
1892
1893 if (Options.format == JSON_FORMAT) printf("}\n");
1894
1895 input->Release();
1896 delete file;
1897
1898 return 0;
1899 }
1900