/* * 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