1 /*
2 * The contents of this file are subject to the Mozilla Public
3 * License Version 1.1 (the "License"); you may not use this file
4 * except in compliance with the License. You may obtain a copy of
5 * the License at http://www.mozilla.org/MPL/
6 *
7 * Software distributed under the License is distributed on an "AS
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9 * implied. See the License for the specific language governing
10 * rights and limitations under the License.
11 *
12 * The Original Code is MPEG4IP.
13 *
14 * The Initial Developer of the Original Code is Cisco Systems Inc.
15 * Portions created by Cisco Systems Inc. are
16 * Copyright (C) Cisco Systems Inc. 2001-2002. All Rights Reserved.
17 *
18 * Portions created by Ximpo Group Ltd. are
19 * Copyright (C) Ximpo Group Ltd. 2003, 2004. All Rights Reserved.
20 *
21 * Contributor(s):
22 * Dave Mackie dmackie@cisco.com
23 * Bill May wmay@cisco.com
24 * Alix Marchandise-Franquet alix@cisco.com
25 * Ximpo Group Ltd. mp4v2@ximpo.com
26 */
27
28 #include "mp4common.h"
29
PrintAudioInfo(MP4FileHandle mp4File,MP4TrackId trackId)30 static char* PrintAudioInfo(
31 MP4FileHandle mp4File,
32 MP4TrackId trackId)
33 {
34 static const char* mpeg4AudioNames[] = {
35 "MPEG-4 AAC main",
36 "MPEG-4 AAC LC",
37 "MPEG-4 AAC SSR",
38 "MPEG-4 AAC LTP",
39 "MPEG-4 AAC HE",
40 "MPEG-4 AAC Scalable",
41 "MPEG-4 TwinVQ",
42 "MPEG-4 CELP",
43 "MPEG-4 HVXC",
44 NULL, NULL,
45 "MPEG-4 TTSI",
46 "MPEG-4 Main Synthetic",
47 "MPEG-4 Wavetable Syn",
48 "MPEG-4 General MIDI",
49 "MPEG-4 Algo Syn and Audio FX",
50 "MPEG-4 ER AAC LC",
51 NULL,
52 "MPEG-4 ER AAC LTP",
53 "MPEG-4 ER AAC Scalable",
54 "MPEG-4 ER TwinVQ",
55 "MPEG-4 ER BSAC",
56 "MPEG-4 ER ACC LD",
57 "MPEG-4 ER CELP",
58 "MPEG-4 ER HVXC",
59 "MPEG-4 ER HILN",
60 "MPEG-4 ER Parametric",
61 "MPEG-4 SSC",
62 "MPEG-4 PS",
63 "MPEG-4 MPEG Surround",
64 NULL,
65 "MPEG-4 Layer-1",
66 "MPEG-4 Layer-2",
67 "MPEG-4 Layer-3",
68 "MPEG-4 DST",
69 "MPEG-4 Audio Lossless",
70 "MPEG-4 SLS",
71 "MPEG-4 SLS non-core",
72 };
73
74 static const u_int8_t mpegAudioTypes[] = {
75 MP4_MPEG2_AAC_MAIN_AUDIO_TYPE, // 0x66
76 MP4_MPEG2_AAC_LC_AUDIO_TYPE, // 0x67
77 MP4_MPEG2_AAC_SSR_AUDIO_TYPE, // 0x68
78 MP4_MPEG2_AUDIO_TYPE, // 0x69
79 MP4_MPEG1_AUDIO_TYPE, // 0x6B
80 // private types
81 MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE,
82 MP4_VORBIS_AUDIO_TYPE,
83 MP4_ALAW_AUDIO_TYPE,
84 MP4_ULAW_AUDIO_TYPE,
85 MP4_G723_AUDIO_TYPE,
86 MP4_PCM16_BIG_ENDIAN_AUDIO_TYPE,
87 };
88 static const char* mpegAudioNames[] = {
89 "MPEG-2 AAC Main",
90 "MPEG-2 AAC LC",
91 "MPEG-2 AAC SSR",
92 "MPEG-2 Audio (13818-3)",
93 "MPEG-1 Audio (11172-3)",
94 // private types
95 "PCM16 (little endian)",
96 "Vorbis",
97 "G.711 aLaw",
98 "G.711 uLaw",
99 "G.723.1",
100 "PCM16 (big endian)",
101 };
102 u_int8_t numMpegAudioTypes =
103 sizeof(mpegAudioTypes) / sizeof(u_int8_t);
104
105 const char* typeName = "Unknown";
106 bool foundType = false;
107 u_int8_t type = 0;
108 const char *media_data_name;
109
110 media_data_name = MP4GetTrackMediaDataName(mp4File, trackId);
111
112 if (media_data_name == NULL) {
113 typeName = "Unknown - no media data name";
114 } else if (strcasecmp(media_data_name, "samr") == 0) {
115 typeName = "AMR";
116 foundType = true;
117 } else if (strcasecmp(media_data_name, "sawb") == 0) {
118 typeName = "AMR-WB";
119 foundType = true;
120 } else if (strcasecmp(media_data_name, "mp4a") == 0) {
121
122 type = MP4GetTrackEsdsObjectTypeId(mp4File, trackId);
123 switch (type) {
124 case MP4_INVALID_AUDIO_TYPE:
125 typeName = "AAC from .mov";
126 foundType = true;
127 break;
128 case MP4_MPEG4_AUDIO_TYPE: {
129
130 type = MP4GetTrackAudioMpeg4Type(mp4File, trackId);
131 if (type == MP4_MPEG4_INVALID_AUDIO_TYPE ||
132 type > NUM_ELEMENTS_IN_ARRAY(mpeg4AudioNames) ||
133 mpeg4AudioNames[type - 1] == NULL) {
134 typeName = "MPEG-4 Unknown Profile";
135 } else {
136 typeName = mpeg4AudioNames[type - 1];
137 foundType = true;
138 }
139 break;
140 }
141 // fall through
142 default:
143 for (u_int8_t i = 0; i < numMpegAudioTypes; i++) {
144 if (type == mpegAudioTypes[i]) {
145 typeName = mpegAudioNames[i];
146 foundType = true;
147 break;
148 }
149 }
150 }
151 } else {
152 typeName = media_data_name;
153 foundType = true;
154 }
155
156 u_int32_t timeScale =
157 MP4GetTrackTimeScale(mp4File, trackId);
158
159 MP4Duration trackDuration =
160 MP4GetTrackDuration(mp4File, trackId);
161
162 double msDuration =
163 UINT64_TO_DOUBLE(MP4ConvertFromTrackDuration(mp4File, trackId,
164 trackDuration, MP4_MSECS_TIME_SCALE));
165
166 u_int32_t avgBitRate =
167 MP4GetTrackBitRate(mp4File, trackId);
168
169 char *sInfo = (char*)MP4Malloc(256);
170
171 // type duration avgBitrate samplingFrequency
172 if (foundType)
173 snprintf(sInfo, 256,
174 "%u\taudio\t%s%s, %.3f secs, %u kbps, %u Hz\n",
175 trackId,
176 MP4IsIsmaCrypMediaTrack(mp4File, trackId) ? "enca - " : "",
177 typeName,
178 msDuration / 1000.0,
179 (avgBitRate + 500) / 1000,
180 timeScale);
181 else
182 snprintf(sInfo, 256,
183 "%u\taudio\t%s%s(%u), %.3f secs, %u kbps, %u Hz\n",
184 trackId,
185 MP4IsIsmaCrypMediaTrack(mp4File, trackId) ? "enca - " : "",
186 typeName,
187 type,
188 msDuration / 1000.0,
189 (avgBitRate + 500) / 1000,
190 timeScale);
191
192 return sInfo;
193 }
194 static const struct {
195 uint8_t profile;
196 const char *name;
197 } VisualProfileToName[] = {
198 { MPEG4_SP_L1, "MPEG-4 Simple @ L1"},
199 { MPEG4_SP_L2, "MPEG-4 Simple @ L2" },
200 { MPEG4_SP_L3, "MPEG-4 Simple @ L3" },
201 { MPEG4_SP_L0, "MPEG-4 Simple @ L0" },
202 { MPEG4_SSP_L1, "MPEG-4 Simple Scalable @ L1"},
203 { MPEG4_SSP_L2, "MPEG-4 Simple Scalable @ L2" },
204 { MPEG4_CP_L1, "MPEG-4 Core @ L1"},
205 { MPEG4_CP_L2, "MPEG-4 Core @ L2"},
206 { MPEG4_MP_L2, "MPEG-4 Main @ L2"},
207 { MPEG4_MP_L3, "MPEG-4 Main @ L3"},
208 { MPEG4_MP_L4, "MPEG-4 Main @ L4"},
209 { MPEG4_NBP_L2, "MPEG-4 N-bit @ L2"},
210 { MPEG4_STP_L1, "MPEG-4 Scalable Texture @ L1"},
211 { MPEG4_SFAP_L1, "MPEG-4 Simple Face Anim @ L1"},
212 { MPEG4_SFAP_L2, "MPEG-4 Simple Face Anim @ L2"},
213 { MPEG4_SFBAP_L1, "MPEG-4 Simple FBA @ L1"},
214 { MPEG4_SFBAP_L2, "MPEG-4 Simple FBA @ L2"},
215 { MPEG4_BATP_L1, "MPEG-4 Basic Anim Text @ L1"},
216 { MPEG4_BATP_L2, "MPEG-4 Basic Anim Text @ L2"},
217 { MPEG4_HP_L1, "MPEG-4 Hybrid @ L1"},
218 { MPEG4_HP_L2, "MPEG-4 Hybrid @ L2"},
219 { MPEG4_ARTSP_L1, "MPEG-4 Adv RT Simple @ L1"},
220 { MPEG4_ARTSP_L2, "MPEG-4 Adv RT Simple @ L2"},
221 { MPEG4_ARTSP_L3, "MPEG-4 Adv RT Simple @ L3"},
222 { MPEG4_ARTSP_L4, "MPEG-4 Adv RT Simple @ L4"},
223 { MPEG4_CSP_L1, "MPEG-4 Core Scalable @ L1"},
224 { MPEG4_CSP_L2, "MPEG-4 Core Scalable @ L2"},
225 { MPEG4_CSP_L3, "MPEG-4 Core Scalable @ L3"},
226 { MPEG4_ACEP_L1, "MPEG-4 Adv Coding Efficieny @ L1"},
227 { MPEG4_ACEP_L2, "MPEG-4 Adv Coding Efficieny @ L2"},
228 { MPEG4_ACEP_L3, "MPEG-4 Adv Coding Efficieny @ L3"},
229 { MPEG4_ACEP_L4, "MPEG-4 Adv Coding Efficieny @ L4"},
230 { MPEG4_ACP_L1, "MPEG-4 Adv Core Profile @ L1"},
231 { MPEG4_ACP_L2, "MPEG-4 Adv Core Profile @ L2"},
232 { MPEG4_AST_L1, "MPEG-4 Adv Scalable Texture @ L1"},
233 { MPEG4_AST_L2, "MPEG-4 Adv Scalable Texture @ L2"},
234 { MPEG4_AST_L3, "MPEG-4 Adv Scalable Texture @ L3"},
235 { MPEG4_S_STUDIO_P_L1, "MPEG-4 Simple Studio @ L1"},
236 { MPEG4_S_STUDIO_P_L2, "MPEG-4 Simple Studio @ L2"},
237 { MPEG4_S_STUDIO_P_L3, "MPEG-4 Simple Studio @ L3"},
238 { MPEG4_S_STUDIO_P_L4, "MPEG-4 Simple Studio @ L4"},
239 { MPEG4_C_STUDIO_P_L1, "MPEG-4 Core Studio @ L1"},
240 { MPEG4_C_STUDIO_P_L2, "MPEG-4 Core Studio @ L2"},
241 { MPEG4_C_STUDIO_P_L3, "MPEG-4 Core Studio @ L3"},
242 { MPEG4_C_STUDIO_P_L4, "MPEG-4 Core Studio @ L4"},
243 { MPEG4_ASP_L0, "MPEG-4 Adv Simple@L0"},
244 { MPEG4_ASP_L1, "MPEG-4 Adv Simple@L1"},
245 { MPEG4_ASP_L2, "MPEG-4 Adv Simple@L2"},
246 { MPEG4_ASP_L3, "MPEG-4 Adv Simple@L3"},
247 { MPEG4_ASP_L4, "MPEG-4 Adv Simple@L4"},
248 { MPEG4_ASP_L5, "MPEG-4 Adv Simple@L5"},
249 { MPEG4_ASP_L3B, "MPEG-4 Adv Simple@L3b"},
250 { MPEG4_FGSP_L0, "MPEG-4 FGS @ L0" },
251 { MPEG4_FGSP_L1, "MPEG-4 FGS @ L1" },
252 { MPEG4_FGSP_L2, "MPEG-4 FGS @ L2" },
253 { MPEG4_FGSP_L3, "MPEG-4 FGS @ L3" },
254 { MPEG4_FGSP_L4, "MPEG-4 FGS @ L4" },
255 { MPEG4_FGSP_L5, "MPEG-4 FGS @ L5" }
256 };
257
Mpeg4VisualProfileName(uint8_t visual_profile)258 static const char *Mpeg4VisualProfileName (uint8_t visual_profile)
259 {
260 size_t size = sizeof(VisualProfileToName) / sizeof(*VisualProfileToName);
261
262 for (size_t ix = 0; ix < size; ix++) {
263 if (visual_profile == VisualProfileToName[ix].profile) {
264 return (VisualProfileToName[ix].name);
265 }
266 }
267 return (NULL);
268 }
PrintVideoInfo(MP4FileHandle mp4File,MP4TrackId trackId)269 static char* PrintVideoInfo(
270 MP4FileHandle mp4File,
271 MP4TrackId trackId)
272 {
273
274 static const u_int8_t mpegVideoTypes[] = {
275 MP4_MPEG2_SIMPLE_VIDEO_TYPE, // 0x60
276 MP4_MPEG2_MAIN_VIDEO_TYPE, // 0x61
277 MP4_MPEG2_SNR_VIDEO_TYPE, // 0x62
278 MP4_MPEG2_SPATIAL_VIDEO_TYPE, // 0x63
279 MP4_MPEG2_HIGH_VIDEO_TYPE, // 0x64
280 MP4_MPEG2_442_VIDEO_TYPE, // 0x65
281 MP4_MPEG1_VIDEO_TYPE, // 0x6A
282 MP4_JPEG_VIDEO_TYPE, // 0x6C
283 MP4_YUV12_VIDEO_TYPE,
284 MP4_H263_VIDEO_TYPE,
285 MP4_H261_VIDEO_TYPE,
286 };
287 static const char* mpegVideoNames[] = {
288 "MPEG-2 Simple",
289 "MPEG-2 Main",
290 "MPEG-2 SNR",
291 "MPEG-2 Spatial",
292 "MPEG-2 High",
293 "MPEG-2 4:2:2",
294 "MPEG-1",
295 "JPEG",
296 "YUV12",
297 "H.263",
298 "H.261",
299 };
300 u_int8_t numMpegVideoTypes =
301 sizeof(mpegVideoTypes) / sizeof(u_int8_t);
302 bool foundTypeName = false;
303 const char* typeName = "Unknown";
304
305 const char *media_data_name;
306 char originalFormat[8];
307 char oformatbuffer[32];
308 originalFormat[0] = 0;
309 *oformatbuffer = 0;
310 uint8_t type = 0;
311
312 media_data_name = MP4GetTrackMediaDataName(mp4File, trackId);
313 // encv 264b
314 if (strcasecmp(media_data_name, "encv") == 0) {
315 if (MP4GetTrackMediaDataOriginalFormat(mp4File,
316 trackId,
317 originalFormat,
318 sizeof(originalFormat)) == false)
319 media_data_name = NULL;
320
321 }
322
323 char typebuffer[80];
324 if (media_data_name == NULL) {
325 typeName = "Unknown - no media data name";
326 foundTypeName = true;
327 } else if ((strcasecmp(media_data_name, "avc1") == 0) ||
328 (strcasecmp(originalFormat, "264b") == 0)) {
329 // avc
330 uint8_t profile, level;
331 char profileb[20], levelb[20];
332 if (MP4GetTrackH264ProfileLevel(mp4File, trackId,
333 &profile, &level)) {
334 if (profile == 66) {
335 strcpy(profileb, "Baseline");
336 } else if (profile == 77) {
337 strcpy(profileb, "Main");
338 } else if (profile == 88) {
339 strcpy(profileb, "Extended");
340 } else if (profile == 100) {
341 strcpy(profileb, "High");
342 } else if (profile == 110) {
343 strcpy(profileb, "High 10");
344 } else if (profile == 122) {
345 strcpy(profileb, "High 4:2:2");
346 } else if (profile == 144) {
347 strcpy(profileb, "High 4:4:4");
348 } else {
349 snprintf(profileb, 20, "Unknown Profile %x", profile);
350 }
351 switch (level) {
352 case 10: case 20: case 30: case 40: case 50:
353 snprintf(levelb, 20, "%u", level / 10);
354 break;
355 case 11: case 12: case 13:
356 case 21: case 22:
357 case 31: case 32:
358 case 41: case 42:
359 case 51:
360 snprintf(levelb, 20, "%u.%u", level / 10, level % 10);
361 break;
362 default:
363 snprintf(levelb, 20, "unknown level %x", level);
364 break;
365 }
366 if (originalFormat != NULL && originalFormat[0] != '\0')
367 snprintf(oformatbuffer, 32, "(%s) ", originalFormat);
368 snprintf(typebuffer, sizeof(typebuffer), "H264 %s%s@%s",
369 oformatbuffer, profileb, levelb);
370 typeName = typebuffer;
371 } else {
372 typeName = "H.264 - profile/level error";
373 }
374 foundTypeName = true;
375 } else if (strcasecmp(media_data_name, "s263") == 0) {
376 // 3gp h.263
377 typeName = "H.263";
378 foundTypeName = true;
379 } else if ((strcasecmp(media_data_name, "mp4v") == 0) ||
380 (strcasecmp(media_data_name, "encv") == 0)) {
381 // note encv might needs it's own field eventually.
382 type = MP4GetTrackEsdsObjectTypeId(mp4File, trackId);
383 if (type == MP4_MPEG4_VIDEO_TYPE) {
384 type = MP4GetVideoProfileLevel(mp4File, trackId);
385 typeName = Mpeg4VisualProfileName(type);
386 if (typeName == NULL) {
387 typeName = "MPEG-4 Unknown Profile";
388 } else {
389 foundTypeName = true;
390 }
391 } else {
392 for (u_int8_t i = 0; i < numMpegVideoTypes; i++) {
393 if (type == mpegVideoTypes[i]) {
394 typeName = mpegVideoNames[i];
395 foundTypeName = true;
396 break;
397 }
398 }
399 }
400 } else {
401 typeName = media_data_name;
402 foundTypeName = true; // we don't have a type value to display
403 }
404
405 MP4Duration trackDuration =
406 MP4GetTrackDuration(mp4File, trackId);
407
408 double msDuration =
409 UINT64_TO_DOUBLE(MP4ConvertFromTrackDuration(mp4File, trackId,
410 trackDuration, MP4_MSECS_TIME_SCALE));
411
412 u_int32_t avgBitRate =
413 MP4GetTrackBitRate(mp4File, trackId);
414
415 // Note not all mp4 implementations set width and height correctly
416 // The real answer can be buried inside the ES configuration info
417 u_int16_t width = MP4GetTrackVideoWidth(mp4File, trackId);
418
419 u_int16_t height = MP4GetTrackVideoHeight(mp4File, trackId);
420
421 double fps = MP4GetTrackVideoFrameRate(mp4File, trackId);
422
423 char *sInfo = (char*)MP4Malloc(256);
424
425 // type duration avgBitrate frameSize frameRate
426 if (foundTypeName) {
427 sprintf(sInfo,
428 "%u\tvideo\t%s%s, %.3f secs, %u kbps, %ux%u @ %f fps\n",
429 trackId,
430 MP4IsIsmaCrypMediaTrack(mp4File, trackId) ? "encv - " : "",
431 typeName,
432 msDuration / 1000.0,
433 (avgBitRate + 500) / 1000,
434 width,
435 height,
436 fps
437 );
438 } else {
439 sprintf(sInfo,
440 "%u\tvideo\t%s(%u), %.3f secs, %u kbps, %ux%u @ %f fps\n",
441 trackId,
442 typeName,
443 type,
444 msDuration / 1000.0,
445 (avgBitRate + 500) / 1000,
446 width,
447 height,
448 fps
449 );
450 }
451
452 return sInfo;
453 }
PrintCntlInfo(MP4FileHandle mp4File,MP4TrackId trackId)454 static char* PrintCntlInfo(
455 MP4FileHandle mp4File,
456 MP4TrackId trackId)
457 {
458 const char *media_data_name = MP4GetTrackMediaDataName(mp4File, trackId);
459 const char *typeName = "Unknown";
460
461 if (media_data_name == NULL) {
462 typeName = "Unknown - no media data name";
463 } else if (strcasecmp(media_data_name, "href") == 0) {
464 typeName = "ISMA Href";
465 } else {
466 typeName = media_data_name;
467 }
468
469 MP4Duration trackDuration =
470 MP4GetTrackDuration(mp4File, trackId);
471
472 double msDuration =
473 UINT64_TO_DOUBLE(MP4ConvertFromTrackDuration(mp4File, trackId,
474 trackDuration, MP4_MSECS_TIME_SCALE));
475 char *sInfo = (char *)MP4Malloc(256);
476
477 snprintf(sInfo, 256,
478 "%u\tcontrol\t%s, %.3f secs\n",
479 trackId,
480 typeName,
481 msDuration / 1000.0);
482 return sInfo;
483 }
484
485
PrintHintInfo(MP4FileHandle mp4File,MP4TrackId trackId)486 static char* PrintHintInfo(
487 MP4FileHandle mp4File,
488 MP4TrackId trackId)
489 {
490 MP4TrackId referenceTrackId =
491 MP4GetHintTrackReferenceTrackId(mp4File, trackId);
492
493 char* payloadName = NULL;
494 if (!MP4GetHintTrackRtpPayload(mp4File, trackId, &payloadName))
495 return NULL;
496
497 char *sInfo = (char*)MP4Malloc(256);
498
499 snprintf(sInfo, 256,
500 "%u\thint\tPayload %s for track %u\n",
501 trackId,
502 payloadName,
503 referenceTrackId);
504
505 free(payloadName);
506
507 return sInfo;
508 }
509
PrintTrackInfo(MP4FileHandle mp4File,MP4TrackId trackId)510 static char* PrintTrackInfo(
511 MP4FileHandle mp4File,
512 MP4TrackId trackId)
513 {
514 char* trackInfo = NULL;
515
516 const char* trackType =
517 MP4GetTrackType(mp4File, trackId);
518 if (trackType == NULL) return NULL;
519
520 if (!strcmp(trackType, MP4_AUDIO_TRACK_TYPE)) {
521 trackInfo = PrintAudioInfo(mp4File, trackId);
522 } else if (!strcmp(trackType, MP4_VIDEO_TRACK_TYPE)) {
523 trackInfo = PrintVideoInfo(mp4File, trackId);
524 } else if (!strcmp(trackType, MP4_HINT_TRACK_TYPE)) {
525 trackInfo = PrintHintInfo(mp4File, trackId);
526 } else if (strcmp(trackType, MP4_CNTL_TRACK_TYPE) == 0) {
527 trackInfo = PrintCntlInfo(mp4File, trackId);
528 } else {
529 trackInfo = (char*)MP4Malloc(256);
530 if (!strcmp(trackType, MP4_OD_TRACK_TYPE)) {
531 snprintf(trackInfo, 256,
532 "%u\tod\tObject Descriptors\n",
533 trackId);
534 } else if (!strcmp(trackType, MP4_SCENE_TRACK_TYPE)) {
535 snprintf(trackInfo, 256,
536 "%u\tscene\tBIFS\n",
537 trackId);
538 } else {
539 snprintf(trackInfo, 256,
540 "%u\t%s\n",
541 trackId, trackType);
542 }
543 }
544
545 return trackInfo;
546 }
547
MP4Info(MP4FileHandle mp4File,MP4TrackId trackId)548 extern "C" char* MP4Info(
549 MP4FileHandle mp4File,
550 MP4TrackId trackId)
551 {
552 char* info = NULL;
553
554 if (MP4_IS_VALID_FILE_HANDLE(mp4File)) {
555 try {
556 if (trackId == MP4_INVALID_TRACK_ID) {
557 uint buflen = 4 * 1024;
558 info = (char*)MP4Calloc(buflen);
559
560 buflen -= snprintf(info, buflen,
561 "Track\tType\tInfo\n");
562
563 u_int32_t numTracks = MP4GetNumberOfTracks(mp4File);
564
565 for (u_int32_t i = 0; i < numTracks; i++) {
566 trackId = MP4FindTrackId(mp4File, i);
567 char* trackInfo = PrintTrackInfo(mp4File, trackId);
568 strncat(info, trackInfo, buflen);
569 uint newlen = strlen(trackInfo);
570 if (newlen > buflen) buflen = 0;
571 else buflen -= newlen;
572 MP4Free(trackInfo);
573 }
574 } else {
575 info = PrintTrackInfo(mp4File, trackId);
576 }
577 }
578 catch (MP4Error* e) {
579 delete e;
580 }
581 }
582
583 return info;
584 }
585
MP4FileInfo(const char * fileName,MP4TrackId trackId)586 extern "C" char* MP4FileInfo(
587 const char* fileName,
588 MP4TrackId trackId)
589 {
590 MP4FileHandle mp4File =
591 MP4Read(fileName);
592
593 if (!mp4File) {
594 return NULL;
595 }
596
597 char* info = MP4Info(mp4File, trackId);
598
599 MP4Close(mp4File);
600
601 return info; // caller should free this
602 }
603
604