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