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