688 lines
18 KiB
C++
688 lines
18 KiB
C++
|
/*
|
||
|
* The contents of this file are subject to the Mozilla Public
|
||
|
* License Version 1.1 (the "License"); you may not use this file
|
||
|
* except in compliance with the License. You may obtain a copy of
|
||
|
* the License at http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS
|
||
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||
|
* implied. See the License for the specific language governing
|
||
|
* rights and limitations under the License.
|
||
|
*
|
||
|
* The Original Code is MPEG4IP.
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is Cisco Systems Inc.
|
||
|
* Portions created by Cisco Systems Inc. are
|
||
|
* Copyright (C) Cisco Systems Inc. 2001-2002. All Rights Reserved.
|
||
|
*
|
||
|
* Portions created by Ximpo Group Ltd. are
|
||
|
* Copyright (C) Ximpo Group Ltd. 2003, 2004. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
* Dave Mackie dmackie@cisco.com
|
||
|
* Bill May wmay@cisco.com
|
||
|
* Alix Marchandise-Franquet alix@cisco.com
|
||
|
* Ximpo Group Ltd. mp4v2@ximpo.com
|
||
|
*/
|
||
|
|
||
|
#include "mp4common.h"
|
||
|
|
||
|
extern "C" char* MP4PrintAudioInfo(
|
||
|
MP4FileHandle mp4File,
|
||
|
MP4TrackId trackId)
|
||
|
{
|
||
|
static const char* mpeg4AudioNames[] =
|
||
|
{
|
||
|
"MPEG-4 AAC main",
|
||
|
"MPEG-4 AAC LC",
|
||
|
"MPEG-4 AAC SSR",
|
||
|
"MPEG-4 AAC LTP",
|
||
|
"MPEG-4 AAC HE",
|
||
|
"MPEG-4 AAC Scalable",
|
||
|
"MPEG-4 TwinVQ",
|
||
|
"MPEG-4 CELP",
|
||
|
"MPEG-4 HVXC",
|
||
|
NULL, NULL,
|
||
|
"MPEG-4 TTSI",
|
||
|
"MPEG-4 Main Synthetic",
|
||
|
"MPEG-4 Wavetable Syn",
|
||
|
"MPEG-4 General MIDI",
|
||
|
"MPEG-4 Algo Syn and Audio FX",
|
||
|
"MPEG-4 ER AAC LC",
|
||
|
NULL,
|
||
|
"MPEG-4 ER AAC LTP",
|
||
|
"MPEG-4 ER AAC Scalable",
|
||
|
"MPEG-4 ER TwinVQ",
|
||
|
"MPEG-4 ER BSAC",
|
||
|
"MPEG-4 ER ACC LD",
|
||
|
"MPEG-4 ER CELP",
|
||
|
"MPEG-4 ER HVXC",
|
||
|
"MPEG-4 ER HILN",
|
||
|
"MPEG-4 ER Parametric",
|
||
|
"MPEG-4 SSC",
|
||
|
"MPEG-4 PS",
|
||
|
"MPEG-4 MPEG Surround",
|
||
|
NULL,
|
||
|
"MPEG-4 Layer-1",
|
||
|
"MPEG-4 Layer-2",
|
||
|
"MPEG-4 Layer-3",
|
||
|
"MPEG-4 DST",
|
||
|
"MPEG-4 Audio Lossless",
|
||
|
"MPEG-4 SLS",
|
||
|
"MPEG-4 SLS non-core",
|
||
|
};
|
||
|
|
||
|
static const u_int8_t mpegAudioTypes[] =
|
||
|
{
|
||
|
MP4_MPEG2_AAC_MAIN_AUDIO_TYPE, // 0x66
|
||
|
MP4_MPEG2_AAC_LC_AUDIO_TYPE, // 0x67
|
||
|
MP4_MPEG2_AAC_SSR_AUDIO_TYPE, // 0x68
|
||
|
MP4_MPEG2_AUDIO_TYPE, // 0x69
|
||
|
MP4_MPEG1_AUDIO_TYPE, // 0x6B
|
||
|
// private types
|
||
|
MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE,
|
||
|
MP4_VORBIS_AUDIO_TYPE,
|
||
|
MP4_ALAW_AUDIO_TYPE,
|
||
|
MP4_ULAW_AUDIO_TYPE,
|
||
|
MP4_G723_AUDIO_TYPE,
|
||
|
MP4_PCM16_BIG_ENDIAN_AUDIO_TYPE,
|
||
|
};
|
||
|
static const char* mpegAudioNames[] =
|
||
|
{
|
||
|
"MPEG-2 AAC Main",
|
||
|
"MPEG-2 AAC LC",
|
||
|
"MPEG-2 AAC SSR",
|
||
|
"MPEG-2 Audio (13818-3)",
|
||
|
"MPEG-1 Audio (11172-3)",
|
||
|
// private types
|
||
|
"PCM16 (little endian)",
|
||
|
"Vorbis",
|
||
|
"G.711 aLaw",
|
||
|
"G.711 uLaw",
|
||
|
"G.723.1",
|
||
|
"PCM16 (big endian)",
|
||
|
};
|
||
|
u_int8_t numMpegAudioTypes =
|
||
|
sizeof(mpegAudioTypes) / sizeof(u_int8_t);
|
||
|
|
||
|
const char* typeName = "Unknown";
|
||
|
bool foundType = false;
|
||
|
u_int8_t type = 0;
|
||
|
const char *media_data_name;
|
||
|
|
||
|
media_data_name = MP4GetTrackMediaDataName(mp4File, trackId);
|
||
|
u_int32_t timeScale = 0;
|
||
|
if (media_data_name == NULL)
|
||
|
{
|
||
|
typeName = "Unknown - no media data name";
|
||
|
}
|
||
|
else if (strcasecmp(media_data_name, "samr") == 0)
|
||
|
{
|
||
|
typeName = "AMR";
|
||
|
foundType = true;
|
||
|
}
|
||
|
else if (strcasecmp(media_data_name, "sawb") == 0)
|
||
|
{
|
||
|
typeName = "AMR-WB";
|
||
|
foundType = true;
|
||
|
}
|
||
|
else if (strcasecmp(media_data_name, "mp4a") == 0)
|
||
|
{
|
||
|
|
||
|
type = MP4GetTrackEsdsObjectTypeId(mp4File, trackId);
|
||
|
switch (type)
|
||
|
{
|
||
|
case MP4_INVALID_AUDIO_TYPE:
|
||
|
typeName = "AAC from .mov";
|
||
|
foundType = true;
|
||
|
break;
|
||
|
case MP4_MPEG4_AUDIO_TYPE:
|
||
|
{
|
||
|
type = MP4GetTrackAudioMpeg4Type(mp4File, trackId);
|
||
|
if (type == MP4_MPEG4_INVALID_AUDIO_TYPE ||
|
||
|
type > NUM_ELEMENTS_IN_ARRAY(mpeg4AudioNames) ||
|
||
|
mpeg4AudioNames[type - 1] == NULL)
|
||
|
{
|
||
|
typeName = "MPEG-4 Unknown Profile";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (type == 2)
|
||
|
{
|
||
|
u_int8_t* pAacConfig = NULL;
|
||
|
u_int32_t aacConfigLength;
|
||
|
|
||
|
MP4GetTrackESConfiguration(mp4File,
|
||
|
trackId,
|
||
|
&pAacConfig,
|
||
|
&aacConfigLength);
|
||
|
|
||
|
if (aacConfigLength >= 5 && (pAacConfig[4] >> 7) == 1)
|
||
|
{
|
||
|
int samplingRates[]={96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000,-1};
|
||
|
type = 5;
|
||
|
int index = (pAacConfig[4] >> 3) & 0x7;
|
||
|
timeScale = samplingRates[index];
|
||
|
}
|
||
|
MP4Free(pAacConfig);
|
||
|
}
|
||
|
typeName = mpeg4AudioNames[type - 1];
|
||
|
foundType = true;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
// fall through
|
||
|
default:
|
||
|
for (u_int8_t i = 0; i < numMpegAudioTypes; i++)
|
||
|
{
|
||
|
if (type == mpegAudioTypes[i])
|
||
|
{
|
||
|
typeName = mpegAudioNames[i];
|
||
|
foundType = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
typeName = media_data_name;
|
||
|
foundType = true;
|
||
|
}
|
||
|
|
||
|
if (!timeScale)
|
||
|
timeScale = MP4GetTrackTimeScale(mp4File, trackId);
|
||
|
|
||
|
MP4Duration trackDuration =
|
||
|
MP4GetTrackDuration(mp4File, trackId);
|
||
|
|
||
|
double msDuration =
|
||
|
UINT64_TO_DOUBLE(MP4ConvertFromTrackDuration(mp4File, trackId,
|
||
|
trackDuration, MP4_MSECS_TIME_SCALE));
|
||
|
|
||
|
|
||
|
char *sInfo = (char*)MP4Malloc(256);
|
||
|
|
||
|
// type duration avgBitrate samplingFrequency
|
||
|
if (foundType)
|
||
|
snprintf(sInfo, 256,
|
||
|
"%s",
|
||
|
typeName);
|
||
|
else
|
||
|
snprintf(sInfo, 256,
|
||
|
"%s(%u)",
|
||
|
typeName,
|
||
|
type);
|
||
|
|
||
|
return sInfo;
|
||
|
}
|
||
|
static const struct
|
||
|
{
|
||
|
uint8_t profile;
|
||
|
const char *name;
|
||
|
}
|
||
|
VisualProfileToName[] = {
|
||
|
{ MPEG4_SP_L1, "MPEG-4 Simple @ L1"},
|
||
|
{ MPEG4_SP_L2, "MPEG-4 Simple @ L2" },
|
||
|
{ MPEG4_SP_L3, "MPEG-4 Simple @ L3" },
|
||
|
{ MPEG4_SP_L0, "MPEG-4 Simple @ L0" },
|
||
|
{ MPEG4_SSP_L1, "MPEG-4 Simple Scalable @ L1"},
|
||
|
{ MPEG4_SSP_L2, "MPEG-4 Simple Scalable @ L2" },
|
||
|
{ MPEG4_CP_L1, "MPEG-4 Core @ L1"},
|
||
|
{ MPEG4_CP_L2, "MPEG-4 Core @ L2"},
|
||
|
{ MPEG4_MP_L2, "MPEG-4 Main @ L2"},
|
||
|
{ MPEG4_MP_L3, "MPEG-4 Main @ L3"},
|
||
|
{ MPEG4_MP_L4, "MPEG-4 Main @ L4"},
|
||
|
{ MPEG4_NBP_L2, "MPEG-4 N-bit @ L2"},
|
||
|
{ MPEG4_STP_L1, "MPEG-4 Scalable Texture @ L1"},
|
||
|
{ MPEG4_SFAP_L1, "MPEG-4 Simple Face Anim @ L1"},
|
||
|
{ MPEG4_SFAP_L2, "MPEG-4 Simple Face Anim @ L2"},
|
||
|
{ MPEG4_SFBAP_L1, "MPEG-4 Simple FBA @ L1"},
|
||
|
{ MPEG4_SFBAP_L2, "MPEG-4 Simple FBA @ L2"},
|
||
|
{ MPEG4_BATP_L1, "MPEG-4 Basic Anim Text @ L1"},
|
||
|
{ MPEG4_BATP_L2, "MPEG-4 Basic Anim Text @ L2"},
|
||
|
{ MPEG4_HP_L1, "MPEG-4 Hybrid @ L1"},
|
||
|
{ MPEG4_HP_L2, "MPEG-4 Hybrid @ L2"},
|
||
|
{ MPEG4_ARTSP_L1, "MPEG-4 Adv RT Simple @ L1"},
|
||
|
{ MPEG4_ARTSP_L2, "MPEG-4 Adv RT Simple @ L2"},
|
||
|
{ MPEG4_ARTSP_L3, "MPEG-4 Adv RT Simple @ L3"},
|
||
|
{ MPEG4_ARTSP_L4, "MPEG-4 Adv RT Simple @ L4"},
|
||
|
{ MPEG4_CSP_L1, "MPEG-4 Core Scalable @ L1"},
|
||
|
{ MPEG4_CSP_L2, "MPEG-4 Core Scalable @ L2"},
|
||
|
{ MPEG4_CSP_L3, "MPEG-4 Core Scalable @ L3"},
|
||
|
{ MPEG4_ACEP_L1, "MPEG-4 Adv Coding Efficieny @ L1"},
|
||
|
{ MPEG4_ACEP_L2, "MPEG-4 Adv Coding Efficieny @ L2"},
|
||
|
{ MPEG4_ACEP_L3, "MPEG-4 Adv Coding Efficieny @ L3"},
|
||
|
{ MPEG4_ACEP_L4, "MPEG-4 Adv Coding Efficieny @ L4"},
|
||
|
{ MPEG4_ACP_L1, "MPEG-4 Adv Core Profile @ L1"},
|
||
|
{ MPEG4_ACP_L2, "MPEG-4 Adv Core Profile @ L2"},
|
||
|
{ MPEG4_AST_L1, "MPEG-4 Adv Scalable Texture @ L1"},
|
||
|
{ MPEG4_AST_L2, "MPEG-4 Adv Scalable Texture @ L2"},
|
||
|
{ MPEG4_AST_L3, "MPEG-4 Adv Scalable Texture @ L3"},
|
||
|
{ MPEG4_S_STUDIO_P_L1, "MPEG-4 Simple Studio @ L1"},
|
||
|
{ MPEG4_S_STUDIO_P_L2, "MPEG-4 Simple Studio @ L2"},
|
||
|
{ MPEG4_S_STUDIO_P_L3, "MPEG-4 Simple Studio @ L3"},
|
||
|
{ MPEG4_S_STUDIO_P_L4, "MPEG-4 Simple Studio @ L4"},
|
||
|
{ MPEG4_C_STUDIO_P_L1, "MPEG-4 Core Studio @ L1"},
|
||
|
{ MPEG4_C_STUDIO_P_L2, "MPEG-4 Core Studio @ L2"},
|
||
|
{ MPEG4_C_STUDIO_P_L3, "MPEG-4 Core Studio @ L3"},
|
||
|
{ MPEG4_C_STUDIO_P_L4, "MPEG-4 Core Studio @ L4"},
|
||
|
{ MPEG4_ASP_L0, "MPEG-4 Adv Simple@L0"},
|
||
|
{ MPEG4_ASP_L1, "MPEG-4 Adv Simple@L1"},
|
||
|
{ MPEG4_ASP_L2, "MPEG-4 Adv Simple@L2"},
|
||
|
{ MPEG4_ASP_L3, "MPEG-4 Adv Simple@L3"},
|
||
|
{ MPEG4_ASP_L4, "MPEG-4 Adv Simple@L4"},
|
||
|
{ MPEG4_ASP_L5, "MPEG-4 Adv Simple@L5"},
|
||
|
{ MPEG4_ASP_L3B, "MPEG-4 Adv Simple@L3b"},
|
||
|
{ MPEG4_FGSP_L0, "MPEG-4 FGS @ L0" },
|
||
|
{ MPEG4_FGSP_L1, "MPEG-4 FGS @ L1" },
|
||
|
{ MPEG4_FGSP_L2, "MPEG-4 FGS @ L2" },
|
||
|
{ MPEG4_FGSP_L3, "MPEG-4 FGS @ L3" },
|
||
|
{ MPEG4_FGSP_L4, "MPEG-4 FGS @ L4" },
|
||
|
{ MPEG4_FGSP_L5, "MPEG-4 FGS @ L5" }
|
||
|
};
|
||
|
|
||
|
static const char *Mpeg4VisualProfileName(uint8_t visual_profile)
|
||
|
{
|
||
|
size_t size = sizeof(VisualProfileToName) / sizeof(*VisualProfileToName);
|
||
|
|
||
|
for (size_t ix = 0; ix < size; ix++)
|
||
|
{
|
||
|
if (visual_profile == VisualProfileToName[ix].profile)
|
||
|
{
|
||
|
return (VisualProfileToName[ix].name);
|
||
|
}
|
||
|
}
|
||
|
return (NULL);
|
||
|
}
|
||
|
extern "C" char* MP4PrintVideoInfo(
|
||
|
MP4FileHandle mp4File,
|
||
|
MP4TrackId trackId)
|
||
|
{
|
||
|
|
||
|
static const u_int8_t mpegVideoTypes[] =
|
||
|
{
|
||
|
MP4_MPEG2_SIMPLE_VIDEO_TYPE, // 0x60
|
||
|
MP4_MPEG2_MAIN_VIDEO_TYPE, // 0x61
|
||
|
MP4_MPEG2_SNR_VIDEO_TYPE, // 0x62
|
||
|
MP4_MPEG2_SPATIAL_VIDEO_TYPE, // 0x63
|
||
|
MP4_MPEG2_HIGH_VIDEO_TYPE, // 0x64
|
||
|
MP4_MPEG2_442_VIDEO_TYPE, // 0x65
|
||
|
MP4_MPEG1_VIDEO_TYPE, // 0x6A
|
||
|
MP4_JPEG_VIDEO_TYPE, // 0x6C
|
||
|
MP4_YUV12_VIDEO_TYPE,
|
||
|
MP4_H263_VIDEO_TYPE,
|
||
|
MP4_H261_VIDEO_TYPE,
|
||
|
};
|
||
|
static const char* mpegVideoNames[] =
|
||
|
{
|
||
|
"MPEG-2 Simple",
|
||
|
"MPEG-2 Main",
|
||
|
"MPEG-2 SNR",
|
||
|
"MPEG-2 Spatial",
|
||
|
"MPEG-2 High",
|
||
|
"MPEG-2 4:2:2",
|
||
|
"MPEG-1",
|
||
|
"JPEG",
|
||
|
"YUV12",
|
||
|
"H.263",
|
||
|
"H.261",
|
||
|
};
|
||
|
u_int8_t numMpegVideoTypes =
|
||
|
sizeof(mpegVideoTypes) / sizeof(u_int8_t);
|
||
|
bool foundTypeName = false;
|
||
|
const char* typeName = "Unknown";
|
||
|
|
||
|
const char *media_data_name;
|
||
|
char originalFormat[8];
|
||
|
char oformatbuffer[32];
|
||
|
originalFormat[0] = 0;
|
||
|
*oformatbuffer = 0;
|
||
|
uint8_t type = 0;
|
||
|
|
||
|
media_data_name = MP4GetTrackMediaDataName(mp4File, trackId);
|
||
|
// encv 264b
|
||
|
if (media_data_name && strcasecmp(media_data_name, "encv") == 0)
|
||
|
{
|
||
|
if (MP4GetTrackMediaDataOriginalFormat(mp4File,
|
||
|
trackId,
|
||
|
originalFormat,
|
||
|
sizeof(originalFormat)) == false)
|
||
|
media_data_name = NULL;
|
||
|
}
|
||
|
|
||
|
char typebuffer[80];
|
||
|
if (media_data_name == NULL)
|
||
|
{
|
||
|
typeName = "Unknown - no media data name";
|
||
|
foundTypeName = true;
|
||
|
}
|
||
|
else if ((strcasecmp(media_data_name, "avc1") == 0) ||
|
||
|
(strcasecmp(originalFormat, "264b") == 0))
|
||
|
{
|
||
|
// avc
|
||
|
uint8_t profile, level;
|
||
|
char profileb[20], levelb[20];
|
||
|
if (MP4GetTrackH264ProfileLevel(mp4File, trackId,
|
||
|
&profile, &level))
|
||
|
{
|
||
|
if (profile == 44)
|
||
|
{
|
||
|
strcpy(profileb, "CAVLC 4:4:4");
|
||
|
}
|
||
|
else if (profile == 66)
|
||
|
{
|
||
|
strcpy(profileb, "Baseline");
|
||
|
}
|
||
|
else if (profile == 77)
|
||
|
{
|
||
|
strcpy(profileb, "Main");
|
||
|
}
|
||
|
else if (profile == 88)
|
||
|
{
|
||
|
strcpy(profileb, "Extended");
|
||
|
}
|
||
|
else if (profile == 100)
|
||
|
{
|
||
|
strcpy(profileb, "High");
|
||
|
}
|
||
|
else if (profile == 110)
|
||
|
{
|
||
|
strcpy(profileb, "High 10");
|
||
|
}
|
||
|
else if (profile == 122)
|
||
|
{
|
||
|
strcpy(profileb, "High 4:2:2");
|
||
|
}
|
||
|
else if (profile == 144 || profile == 244)
|
||
|
{
|
||
|
strcpy(profileb, "High 4:4:4");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
snprintf(profileb, 20, "Unknown Profile %x", profile);
|
||
|
}
|
||
|
switch (level)
|
||
|
{
|
||
|
case 10: case 20: case 30: case 40: case 50:
|
||
|
snprintf(levelb, 20, "%u", level / 10);
|
||
|
break;
|
||
|
case 11: case 12: case 13:
|
||
|
case 21: case 22:
|
||
|
case 31: case 32:
|
||
|
case 41: case 42:
|
||
|
case 51:
|
||
|
snprintf(levelb, 20, "%u.%u", level / 10, level % 10);
|
||
|
break;
|
||
|
default:
|
||
|
snprintf(levelb, 20, "unknown level %x", level);
|
||
|
break;
|
||
|
}
|
||
|
if (originalFormat != NULL && originalFormat[0] != '\0')
|
||
|
snprintf(oformatbuffer, 32, "(%s) ", originalFormat);
|
||
|
snprintf(typebuffer, sizeof(typebuffer), "H.264 %s%s@%s",
|
||
|
oformatbuffer, profileb, levelb);
|
||
|
typeName = typebuffer;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
typeName = "H.264 - profile/level error";
|
||
|
}
|
||
|
foundTypeName = true;
|
||
|
}
|
||
|
else if (strcasecmp(media_data_name, "s263") == 0)
|
||
|
{
|
||
|
// 3gp h.263
|
||
|
typeName = "H.263";
|
||
|
foundTypeName = true;
|
||
|
}
|
||
|
else if ((strcasecmp(media_data_name, "mp4v") == 0) ||
|
||
|
(strcasecmp(media_data_name, "encv") == 0))
|
||
|
{
|
||
|
// note encv might needs it's own field eventually.
|
||
|
type = MP4GetTrackEsdsObjectTypeId(mp4File, trackId);
|
||
|
if (type == MP4_MPEG4_VIDEO_TYPE)
|
||
|
{
|
||
|
type = MP4GetVideoProfileLevel(mp4File, trackId);
|
||
|
typeName = Mpeg4VisualProfileName(type);
|
||
|
if (typeName == NULL)
|
||
|
{
|
||
|
typeName = "MPEG-4 Unknown Profile";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
foundTypeName = true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (u_int8_t i = 0; i < numMpegVideoTypes; i++)
|
||
|
{
|
||
|
if (type == mpegVideoTypes[i])
|
||
|
{
|
||
|
typeName = mpegVideoNames[i];
|
||
|
foundTypeName = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
typeName = media_data_name;
|
||
|
foundTypeName = true; // we don't have a type value to display
|
||
|
}
|
||
|
|
||
|
MP4Duration trackDuration =
|
||
|
MP4GetTrackDuration(mp4File, trackId);
|
||
|
|
||
|
double msDuration =
|
||
|
UINT64_TO_DOUBLE(MP4ConvertFromTrackDuration(mp4File, trackId,
|
||
|
trackDuration, MP4_MSECS_TIME_SCALE));
|
||
|
|
||
|
|
||
|
// Note not all mp4 implementations set width and height correctly
|
||
|
// The real answer can be buried inside the ES configuration info
|
||
|
u_int16_t width = MP4GetTrackVideoWidth(mp4File, trackId);
|
||
|
|
||
|
u_int16_t height = MP4GetTrackVideoHeight(mp4File, trackId);
|
||
|
|
||
|
double fps = MP4GetTrackVideoFrameRate(mp4File, trackId);
|
||
|
|
||
|
char *sInfo = (char*)MP4Malloc(256);
|
||
|
|
||
|
// type duration avgBitrate frameSize frameRate
|
||
|
if (foundTypeName)
|
||
|
{
|
||
|
sprintf(sInfo,
|
||
|
"%s",
|
||
|
typeName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sprintf(sInfo,
|
||
|
"%s(%u)",
|
||
|
typeName,
|
||
|
type);
|
||
|
}
|
||
|
|
||
|
return sInfo;
|
||
|
}
|
||
|
static char* PrintCntlInfo(
|
||
|
MP4FileHandle mp4File,
|
||
|
MP4TrackId trackId)
|
||
|
{
|
||
|
const char *media_data_name = MP4GetTrackMediaDataName(mp4File, trackId);
|
||
|
const char *typeName = "Unknown";
|
||
|
|
||
|
if (media_data_name == NULL)
|
||
|
{
|
||
|
typeName = "Unknown - no media data name";
|
||
|
}
|
||
|
else if (strcasecmp(media_data_name, "href") == 0)
|
||
|
{
|
||
|
typeName = "ISMA Href";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
typeName = media_data_name;
|
||
|
}
|
||
|
|
||
|
MP4Duration trackDuration =
|
||
|
MP4GetTrackDuration(mp4File, trackId);
|
||
|
|
||
|
double msDuration =
|
||
|
UINT64_TO_DOUBLE(MP4ConvertFromTrackDuration(mp4File, trackId,
|
||
|
trackDuration, MP4_MSECS_TIME_SCALE));
|
||
|
char *sInfo = (char *)MP4Malloc(256);
|
||
|
|
||
|
snprintf(sInfo, 256,
|
||
|
"%u\tcontrol\t%s, %.3f secs\r\n",
|
||
|
trackId,
|
||
|
typeName,
|
||
|
msDuration / 1000.0);
|
||
|
return sInfo;
|
||
|
}
|
||
|
|
||
|
|
||
|
static char* PrintHintInfo(
|
||
|
MP4FileHandle mp4File,
|
||
|
MP4TrackId trackId)
|
||
|
{
|
||
|
MP4TrackId referenceTrackId =
|
||
|
MP4GetHintTrackReferenceTrackId(mp4File, trackId);
|
||
|
|
||
|
char* payloadName = NULL;
|
||
|
if (!MP4GetHintTrackRtpPayload(mp4File, trackId, &payloadName))
|
||
|
return NULL;
|
||
|
|
||
|
char *sInfo = (char*)MP4Malloc(256);
|
||
|
|
||
|
snprintf(sInfo, 256,
|
||
|
"%u\thint\tPayload %s for track %u\r\n",
|
||
|
trackId,
|
||
|
payloadName,
|
||
|
referenceTrackId);
|
||
|
|
||
|
free(payloadName);
|
||
|
|
||
|
return sInfo;
|
||
|
}
|
||
|
#if 0
|
||
|
static char* PrintTrackInfo(
|
||
|
MP4FileHandle mp4File,
|
||
|
MP4TrackId trackId)
|
||
|
{
|
||
|
char* trackInfo = NULL;
|
||
|
|
||
|
const char* trackType =
|
||
|
MP4GetTrackType(mp4File, trackId);
|
||
|
if (trackType == NULL) return NULL;
|
||
|
|
||
|
if (!strcmp(trackType, MP4_AUDIO_TRACK_TYPE))
|
||
|
{
|
||
|
trackInfo = PrintAudioInfo(mp4File, trackId);
|
||
|
}
|
||
|
else if (!strcmp(trackType, MP4_VIDEO_TRACK_TYPE))
|
||
|
{
|
||
|
trackInfo = PrintVideoInfo(mp4File, trackId);
|
||
|
}
|
||
|
else if (!strcmp(trackType, MP4_HINT_TRACK_TYPE))
|
||
|
{
|
||
|
trackInfo = PrintHintInfo(mp4File, trackId);
|
||
|
}
|
||
|
else if (strcmp(trackType, MP4_CNTL_TRACK_TYPE) == 0)
|
||
|
{
|
||
|
trackInfo = PrintCntlInfo(mp4File, trackId);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
trackInfo = (char*)MP4Malloc(256);
|
||
|
if (!strcmp(trackType, MP4_OD_TRACK_TYPE))
|
||
|
{
|
||
|
snprintf(trackInfo, 256,
|
||
|
"%u\tod\tObject Descriptors\r\n",
|
||
|
trackId);
|
||
|
}
|
||
|
else if (!strcmp(trackType, MP4_SCENE_TRACK_TYPE))
|
||
|
{
|
||
|
snprintf(trackInfo, 256,
|
||
|
"%u\tscene\tBIFS\r\n",
|
||
|
trackId);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
snprintf(trackInfo, 256,
|
||
|
"%u\t%s\r\n",
|
||
|
trackId, trackType);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return trackInfo;
|
||
|
}
|
||
|
|
||
|
extern "C" char* MP4Info(
|
||
|
MP4FileHandle mp4File,
|
||
|
MP4TrackId trackId)
|
||
|
{
|
||
|
char* info = NULL;
|
||
|
|
||
|
if (MP4_IS_VALID_FILE_HANDLE(mp4File))
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
if (trackId == MP4_INVALID_TRACK_ID)
|
||
|
{
|
||
|
uint buflen = 4 * 1024;
|
||
|
info = (char*)MP4Calloc(buflen);
|
||
|
|
||
|
buflen -= snprintf(info, buflen,
|
||
|
"Track\tType\tInfo\r\n");
|
||
|
|
||
|
u_int32_t numTracks = MP4GetNumberOfTracks(mp4File);
|
||
|
|
||
|
for (u_int32_t i = 0; i < numTracks; i++)
|
||
|
{
|
||
|
trackId = MP4FindTrackId(mp4File, i);
|
||
|
char* trackInfo = PrintTrackInfo(mp4File, trackId);
|
||
|
strncat(info, trackInfo, buflen);
|
||
|
uint newlen = strlen(trackInfo);
|
||
|
if (newlen > buflen) buflen = 0;
|
||
|
else buflen -= newlen;
|
||
|
MP4Free(trackInfo);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
info = PrintTrackInfo(mp4File, trackId);
|
||
|
}
|
||
|
}
|
||
|
catch (MP4Error* e)
|
||
|
{
|
||
|
delete e;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return info;
|
||
|
}
|
||
|
|
||
|
extern "C" char* MP4FileInfo(
|
||
|
const MP4_FILENAME_CHAR* fileName,
|
||
|
MP4TrackId trackId)
|
||
|
{
|
||
|
MP4FileHandle mp4File =
|
||
|
MP4Read(fileName);
|
||
|
|
||
|
if (!mp4File)
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
char* info = MP4Info(mp4File, trackId);
|
||
|
|
||
|
MP4Close(mp4File);
|
||
|
|
||
|
return info; // caller should free this
|
||
|
}
|
||
|
|
||
|
#endif
|