extern "C" u_int8_t MP4AV_AudioGetChannels( MP4FileHandle mp4File, MP4TrackId audioTrackId) { u_int8_t audioType = MP4GetTrackEsdsObjectTypeId(mp4File, audioTrackId); if (audioType == MP4_INVALID_AUDIO_TYPE) { return 0; } if (MP4_IS_MP3_AUDIO_TYPE(audioType)) { MP4AV_Mp3Header mp3Hdr = GetMp3Header(mp4File, audioTrackId); if (mp3Hdr == 0) { return 0; } return MP4AV_Mp3GetChannels(mp3Hdr); } else if (MP4_IS_AAC_AUDIO_TYPE(audioType)) { u_int8_t* pAacConfig = NULL; u_int32_t aacConfigLength; MP4GetTrackESConfiguration( mp4File, audioTrackId, &pAacConfig, &aacConfigLength); if (pAacConfig == NULL || aacConfigLength < 2) { return 0; } u_int8_t channels = MP4AV_AacConfigGetChannels(pAacConfig); free(pAacConfig); return channels; } else if ((audioType == MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE) || (audioType == MP4_PCM16_BIG_ENDIAN_AUDIO_TYPE)) { u_int32_t samplesPerFrame = MP4GetSampleSize(mp4File, audioTrackId, 1) / 2; MP4Duration frameDuration = MP4GetSampleDuration(mp4File, audioTrackId, 1); if (frameDuration == 0) { return 0; } // assumes track time scale == sampling rate return samplesPerFrame / frameDuration; } return 0; }
int covert_aac_to_mp4(char *inputFileName, char *mp4FileName) { int Mp4TimeScale = 90000; int allMpeg4Streams = 0; MP4FileHandle mp4File; FILE* inFile; const char *type; MP4TrackId createdTrackId = MP4_INVALID_TRACK_ID; mp4File = MP4Create(mp4FileName, 0, 0); if (mp4File) { MP4SetTimeScale(mp4File, Mp4TimeScale); } else { return 1; } inFile = fopen(inputFileName, "rb"); if (inFile == NULL) { MP4Close(mp4File); return 2; } createdTrackId = AacCreator(mp4File, inFile); if (createdTrackId == MP4_INVALID_TRACK_ID) { fclose(inFile); MP4Close(mp4File); return 3; } type = MP4GetTrackType(mp4File, createdTrackId); if (!strcmp(type, MP4_AUDIO_TRACK_TYPE)) { allMpeg4Streams &= (MP4GetTrackEsdsObjectTypeId(mp4File, createdTrackId) == MP4_MPEG4_AUDIO_TYPE); } if (inFile) { fclose(inFile); } MP4Close(mp4File); MP4MakeIsmaCompliant(mp4FileName, 0, allMpeg4Streams); return 0; }
extern "C" u_int16_t MP4AV_AudioGetSamplingWindow( MP4FileHandle mp4File, MP4TrackId audioTrackId) { u_int8_t audioType = MP4GetTrackEsdsObjectTypeId(mp4File, audioTrackId); if (audioType == MP4_INVALID_AUDIO_TYPE) { return 0; } if (MP4_IS_MP3_AUDIO_TYPE(audioType)) { MP4AV_Mp3Header mp3Hdr = GetMp3Header(mp4File, audioTrackId); return MP4AV_Mp3GetHdrSamplingWindow(mp3Hdr); } else if (MP4_IS_AAC_AUDIO_TYPE(audioType)) { u_int8_t* pAacConfig = NULL; u_int32_t aacConfigLength; MP4GetTrackESConfiguration( mp4File, audioTrackId, &pAacConfig, &aacConfigLength); if (pAacConfig == NULL || aacConfigLength < 2) { return 0; } u_int32_t samplingWindow = MP4AV_AacConfigGetSamplingWindow(pAacConfig); free(pAacConfig); return samplingWindow; } else if ((audioType == MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE)|| (audioType == MP4_PCM16_BIG_ENDIAN_AUDIO_TYPE)) { MP4Duration frameDuration = MP4GetSampleDuration(mp4File, audioTrackId, 1); // assumes track time scale == sampling rate // and constant frame size was used return frameDuration; } return 0; }
extern "C" u_int32_t MP4AV_AudioGetSamplingRate( MP4FileHandle mp4File, MP4TrackId audioTrackId) { u_int8_t audioType = MP4GetTrackEsdsObjectTypeId(mp4File, audioTrackId); if (audioType == MP4_INVALID_AUDIO_TYPE) { return 0; } if (MP4_IS_MP3_AUDIO_TYPE(audioType)) { MP4AV_Mp3Header mp3Hdr = GetMp3Header(mp4File, audioTrackId); if (mp3Hdr == 0) { return 0; } return MP4AV_Mp3GetHdrSamplingRate(mp3Hdr); } else if (MP4_IS_AAC_AUDIO_TYPE(audioType)) { u_int8_t* pAacConfig = NULL; u_int32_t aacConfigLength; MP4GetTrackESConfiguration( mp4File, audioTrackId, &pAacConfig, &aacConfigLength); if (pAacConfig == NULL || aacConfigLength < 2) { return 0; } u_int32_t samplingRate = MP4AV_AacConfigGetSamplingRate(pAacConfig); free(pAacConfig); return samplingRate; } else if ((audioType == MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE)|| (audioType == MP4_PCM16_BIG_ENDIAN_AUDIO_TYPE)) { return MP4GetTrackTimeScale(mp4File, audioTrackId); } return 0; }
/* * getType: * * Returns a format/sub-format information. Taken from mp4.h/mp4info. */ static void getType(MP4FileHandle file, MP4TrackId trackId, const char **format, const char **subformat ) { unsigned i; const char *media_data_name = MP4GetTrackMediaDataName(file, trackId); *format = _("Audio"); *subformat = _("Unknown"); if (media_data_name == NULL) { ; } else if (strcasecmp(media_data_name, "samr") == 0) { *subformat = "AMR"; } else if (strcasecmp(media_data_name, "sawb") == 0) { *subformat = "AMR-WB"; } else if (strcasecmp(media_data_name, "mp4a") == 0) { u_int8_t type = MP4GetTrackEsdsObjectTypeId(file, trackId); if( type == MP4_MPEG4_AUDIO_TYPE ) { u_int8_t* pAacConfig = NULL; u_int32_t aacConfigLength; MP4GetTrackESConfiguration(file, trackId, &pAacConfig, &aacConfigLength); if (pAacConfig != NULL) { type = aacConfigLength >= 2 ? ((pAacConfig[0] >> 3) & 0x1f) : 0; free(pAacConfig); for (i = 0; i < NUMBER_OF(MP4AudioProfileToName); i++) { if (type == MP4AudioProfileToName[i].profile) { *format = MP4AudioProfileToName[i].format; *subformat = MP4AudioProfileToName[i].subformat; return; } } }
//#define DEBUG_G711 1 extern "C" bool G711Hinter (MP4FileHandle mp4file, MP4TrackId trackid, uint16_t maxPayloadSize) { uint32_t numSamples; uint8_t audioType; MP4SampleId sampleId; uint32_t sampleSize; MP4TrackId hintTrackId; uint8_t payload; uint32_t bytes_this_hint; uint32_t sampleOffset; numSamples = MP4GetTrackNumberOfSamples(mp4file, trackid); if (numSamples == 0) return false; audioType = MP4GetTrackEsdsObjectTypeId(mp4file, trackid); if (audioType != MP4_ALAW_AUDIO_TYPE && audioType != MP4_ULAW_AUDIO_TYPE) return false; hintTrackId = MP4AddHintTrack(mp4file, trackid); if (hintTrackId == MP4_INVALID_TRACK_ID) { return false; } const char *type; if (audioType == MP4_ALAW_AUDIO_TYPE) { payload = 8; type = "PCMA"; } else { payload = 0; type = "PCMU"; } MP4SetHintTrackRtpPayload(mp4file, hintTrackId, type, &payload, 0,NULL, false); MP4Duration sampleDuration; bool have_skip; sampleId = 1; sampleSize = MP4GetSampleSize(mp4file, trackid, sampleId); sampleDuration = MP4GetSampleDuration(mp4file, trackid, sampleId); have_skip = sampleDuration != sampleSize; sampleOffset = 0; bytes_this_hint = 0; if (maxPayloadSize > 160) maxPayloadSize = 160; while (1) { if (bytes_this_hint == 0) { #ifdef DEBUG_G711 printf("Adding hint/packet\n"); #endif MP4AddRtpHint(mp4file, hintTrackId); MP4AddRtpPacket(mp4file, hintTrackId, false); // marker bit 0 } uint16_t bytes_left_this_packet; bytes_left_this_packet = maxPayloadSize - bytes_this_hint; if (sampleSize >= bytes_left_this_packet) { MP4AddRtpSampleData(mp4file, hintTrackId, sampleId, sampleOffset, bytes_left_this_packet); bytes_this_hint += bytes_left_this_packet; sampleSize -= bytes_left_this_packet; sampleOffset += bytes_left_this_packet; #ifdef DEBUG_G711 printf("Added sample with %u bytes\n", bytes_left_this_packet); #endif } else { MP4AddRtpSampleData(mp4file, hintTrackId, sampleId, sampleOffset, sampleSize); bytes_this_hint += sampleSize; #ifdef DEBUG_G711 printf("Added sample with %u bytes\n", sampleSize); #endif sampleSize = 0; } if (bytes_this_hint >= maxPayloadSize) { // Write the hint // duration is bytes written MP4WriteRtpHint(mp4file, hintTrackId, bytes_this_hint); #ifdef DEBUG_G711 printf("Finished packet - bytes %u\n", bytes_this_hint); #endif bytes_this_hint = 0; } if (sampleSize == 0) { // next sample if (have_skip && bytes_this_hint != 0) { #ifdef DEBUG_G711 printf("duration - ending packet - bytes %u\n", bytes_this_hint); #endif MP4WriteRtpHint(mp4file, hintTrackId, bytes_this_hint); bytes_this_hint = 0; } sampleId++; if (sampleId > numSamples) { // finish it and exit if (bytes_this_hint != 0) { MP4WriteRtpHint(mp4file, hintTrackId, bytes_this_hint); } return true; } sampleSize = MP4GetSampleSize(mp4file, trackid, sampleId); sampleDuration = MP4GetSampleDuration(mp4file, trackid, sampleId); have_skip = sampleDuration != sampleSize; #ifdef DEBUG_G711 printf("Next sample %u - size %u %u\n", sampleId, sampleSize, have_skip); #endif sampleOffset = 0; } } return true; // will never reach here }
int CMp4File::create_media (CPlayerSession *psptr, int have_audio_driver, control_callback_vft_t *cc_vft) { uint video_count, video_offset; uint text_count, text_offset; uint audio_count, audio_offset; MP4TrackId trackId; video_query_t *vq; audio_query_t *aq; text_query_t *tq; uint ix; codec_plugin_t *plugin; int ret_value = 0; uint8_t *foo; u_int32_t bufsize; uint32_t verb = MP4GetVerbosity(m_mp4file); MP4SetVerbosity(m_mp4file, verb & ~(MP4_DETAILS_ERROR)); video_count = MP4GetNumberOfTracks(m_mp4file, MP4_VIDEO_TRACK_TYPE); audio_count = MP4GetNumberOfTracks(m_mp4file, MP4_AUDIO_TRACK_TYPE); text_count = MP4GetNumberOfTracks(m_mp4file, MP4_CNTL_TRACK_TYPE); mp4f_message(LOG_DEBUG, "cntl tracks %u", text_count); MP4SetVerbosity(m_mp4file, verb); if (video_count == 0 && audio_count == 0 && text_count == 0) { psptr->set_message("No audio, video or control tracks in file"); return -1; } if (video_count > 0) { vq = (video_query_t *)malloc(sizeof(video_query_t) * video_count); memset(vq, 0, sizeof(video_query_t) * video_count); } else { vq = NULL; } if (have_audio_driver && audio_count > 0) { aq = (audio_query_t *)malloc(sizeof(audio_query_t) * audio_count); memset(aq, 0, sizeof(audio_query_t) * audio_count); } else { aq = NULL; } if (text_count > 0) { tq = (text_query_t *)malloc(sizeof(text_query_t) * text_count); memset(tq, 0, sizeof(text_query_t) * text_count); } else { tq = NULL; } for (ix = 0, video_offset = 0; ix < video_count; ix++) { trackId = MP4FindTrackId(m_mp4file, ix, MP4_VIDEO_TRACK_TYPE); const char *media_data_name; media_data_name = MP4GetTrackMediaDataName(m_mp4file, trackId); // for now, treat mp4v and encv the same vq[video_offset].track_id = trackId; vq[video_offset].stream_type = STREAM_TYPE_MP4_FILE; vq[video_offset].compressor = media_data_name; if (strcasecmp(media_data_name, "mp4v") == 0 || strcasecmp(media_data_name, "encv") == 0) { uint8_t video_type = MP4GetTrackEsdsObjectTypeId(m_mp4file, trackId); uint8_t profileID = MP4GetVideoProfileLevel(m_mp4file, trackId); mp4f_message(LOG_DEBUG, "MP4 - got track %x profile ID %d", trackId, profileID); MP4SetVerbosity(m_mp4file, verb & ~(MP4_DETAILS_ERROR)); MP4GetTrackESConfiguration(m_mp4file, trackId, &foo, &bufsize); MP4SetVerbosity(m_mp4file, verb); vq[video_offset].type = video_type; vq[video_offset].profile = profileID; vq[video_offset].fptr = NULL; vq[video_offset].config = foo; vq[video_offset].config_len = bufsize; } else if (strcasecmp(media_data_name, "avc1") == 0) { uint8_t profile, level; uint8_t **seqheader, **pictheader; uint32_t *pictheadersize, *seqheadersize; uint32_t ix; MP4GetTrackH264ProfileLevel(m_mp4file, trackId, &profile, &level); MP4GetTrackH264SeqPictHeaders(m_mp4file, trackId, &seqheader, &seqheadersize, &pictheader, &pictheadersize); bufsize = 0; for (ix = 0; seqheadersize[ix] != 0; ix++) { bufsize += seqheadersize[ix] + 4; } for (ix = 0; pictheadersize[ix] != 0; ix++) { bufsize += pictheadersize[ix] + 4; } foo = (uint8_t *)malloc(bufsize + 4); memset(foo, 0, bufsize + 4); uint32_t copied = 0; // headers do not have the byte stream start code stored in the file for (ix = 0; seqheadersize[ix] != 0; ix++) { foo[copied] = 0; foo[copied + 1] = 0; foo[copied + 2] = 0; foo[copied + 3] = 1; copied += 4; // add header memcpy(foo + copied, seqheader[ix], seqheadersize[ix]); copied += seqheadersize[ix]; free(seqheader[ix]); } free(seqheader); free(seqheadersize); for (ix = 0; pictheadersize[ix] != 0; ix++) { foo[copied] = 0; foo[copied + 1] = 0; foo[copied + 2] = 0; foo[copied + 3] = 1; copied += 4; // add header memcpy(foo + copied, pictheader[ix], pictheadersize[ix]); copied += pictheadersize[ix]; free(pictheader[ix]); } free(pictheader); free(pictheadersize); vq[video_offset].type = level; vq[video_offset].profile = profile; vq[video_offset].fptr = NULL; vq[video_offset].config = foo; vq[video_offset].config_len = bufsize; } else { MP4GetTrackVideoMetadata(m_mp4file, trackId, &foo, &bufsize); vq[video_offset].config = foo; vq[video_offset].config_len = bufsize; } plugin = check_for_video_codec(vq[video_offset].stream_type, vq[video_offset].compressor, NULL, vq[video_offset].type, vq[video_offset].profile, vq[video_offset].config, vq[video_offset].config_len, &config); if (plugin == NULL) { psptr->set_message("Can't find plugin for video %s type %d, profile %d", vq[video_offset].compressor, vq[video_offset].type, vq[video_offset].profile); m_illegal_video_codec++; ret_value = 1; // possibly memleak for foo here } else { vq[video_offset].h = MP4GetTrackVideoHeight(m_mp4file, trackId); vq[video_offset].w = MP4GetTrackVideoWidth(m_mp4file, trackId); vq[video_offset].frame_rate = MP4GetTrackVideoFrameRate(m_mp4file, trackId); vq[video_offset].enabled = 0; vq[video_offset].reference = NULL; video_offset++; } } audio_offset = 0; if (have_audio_driver) { for (ix = 0; ix < audio_count; ix++) { trackId = MP4FindTrackId(m_mp4file, ix, MP4_AUDIO_TRACK_TYPE); const char *media_data_name; media_data_name = MP4GetTrackMediaDataName(m_mp4file, trackId); aq[audio_offset].track_id = trackId; aq[audio_offset].stream_type = STREAM_TYPE_MP4_FILE; aq[audio_offset].compressor = media_data_name; if (strcasecmp(media_data_name, "mp4a") == 0 || strcasecmp(media_data_name, "enca") == 0) { uint8_t *userdata = NULL; u_int32_t userdata_size; aq[audio_offset].type = MP4GetTrackEsdsObjectTypeId(m_mp4file, trackId); MP4SetVerbosity(m_mp4file, verb & ~(MP4_DETAILS_ERROR)); aq[audio_offset].profile = MP4GetAudioProfileLevel(m_mp4file); MP4GetTrackESConfiguration(m_mp4file, trackId, &userdata, &userdata_size); MP4SetVerbosity(m_mp4file, verb); aq[audio_offset].config = userdata; aq[audio_offset].config_len = userdata_size; } plugin = check_for_audio_codec(aq[audio_offset].stream_type, aq[audio_offset].compressor, NULL, aq[audio_offset].type, aq[audio_offset].profile, aq[audio_offset].config, aq[audio_offset].config_len, &config); if (plugin != NULL) { aq[audio_offset].fptr = NULL; aq[audio_offset].sampling_freq = MP4GetTrackTimeScale(m_mp4file, trackId); MP4SetVerbosity(m_mp4file, verb & ~(MP4_DETAILS_ERROR)); aq[audio_offset].chans = MP4GetTrackAudioChannels(m_mp4file, trackId); MP4SetVerbosity(m_mp4file, verb); aq[audio_offset].enabled = 0; aq[audio_offset].reference = NULL; audio_offset++; m_have_audio = true; } else { m_illegal_audio_codec++; ret_value = 1; } } } else { if (audio_count) ret_value = 1; } text_offset = 0; for (ix = 0; ix < text_count; ix++) { trackId = MP4FindTrackId(m_mp4file, ix, MP4_CNTL_TRACK_TYPE); const char *media_data_name; media_data_name = MP4GetTrackMediaDataName(m_mp4file, trackId); tq[text_offset].track_id = trackId; tq[text_offset].stream_type = STREAM_TYPE_MP4_FILE; tq[text_offset].compressor = media_data_name; plugin = check_for_text_codec(tq[text_offset].stream_type, tq[text_offset].compressor, NULL, NULL, 0, &config); if (plugin != NULL) { tq[text_offset].fptr = NULL; tq[text_offset].enabled = 0; tq[text_offset].reference = NULL; text_offset++; } else { m_illegal_text_codec++; ret_value = 1; } } if (video_offset == 0 && audio_offset == 0 && text_offset == 0) { psptr->set_message("No playable codecs in mp4 file"); return -1; } if (cc_vft && cc_vft->media_list_query != NULL) { (cc_vft->media_list_query)(psptr, video_offset, vq, audio_offset, aq, text_offset, tq); } else { if (video_offset > 0) { vq[0].enabled = 1; } if (audio_offset > 0) { aq[0].enabled = 1; } if (text_offset > 0) { tq[0].enabled = 1; } } int vidret, audret, textret; uint start_desc = 1; vidret = create_video(psptr, vq, video_offset, start_desc); free(vq); if (vidret < 0) { free(aq); free(tq); return -1; } audret = create_audio(psptr, aq, audio_offset, start_desc); free(aq); textret = create_text(psptr, tq, text_offset, start_desc); free(tq); if (audret < 0 || textret < 0) ret_value = -1; char *name; verb = MP4GetVerbosity(m_mp4file); MP4SetVerbosity(m_mp4file, verb & ~(MP4_DETAILS_ERROR)); if (MP4GetMetadataName(m_mp4file, &name) && name != NULL) { psptr->set_session_desc(0, name); free(name); } MP4SetVerbosity(m_mp4file, verb); return (ret_value); }
extern "C" bool MP4AV_RfcIsmaHinter( MP4FileHandle mp4File, MP4TrackId mediaTrackId, bool interleave, u_int16_t maxPayloadSize) { // gather information, and check for validity u_int32_t numSamples = MP4GetTrackNumberOfSamples(mp4File, mediaTrackId); if (numSamples == 0) { return false; } u_int32_t timeScale = MP4GetTrackTimeScale(mp4File, mediaTrackId); if (timeScale == 0) { return false; } u_int8_t audioType = MP4GetTrackEsdsObjectTypeId(mp4File, mediaTrackId); if (audioType != MP4_MPEG4_AUDIO_TYPE && !MP4_IS_AAC_AUDIO_TYPE(audioType)) { return false; } u_int8_t mpeg4AudioType = MP4GetTrackAudioMpeg4Type(mp4File, mediaTrackId); if (audioType == MP4_MPEG4_AUDIO_TYPE) { // check that track contains either MPEG-4 AAC or CELP if (!MP4_IS_MPEG4_AAC_AUDIO_TYPE(mpeg4AudioType) && mpeg4AudioType != MP4_MPEG4_CELP_AUDIO_TYPE) { return false; } } MP4Duration sampleDuration = MP4AV_GetAudioSampleDuration(mp4File, mediaTrackId); if (sampleDuration == MP4_INVALID_DURATION) { return false; } /* get the ES configuration */ u_int8_t* pConfig = NULL; u_int32_t configSize; uint8_t channels; if (MP4GetTrackESConfiguration(mp4File, mediaTrackId, &pConfig, &configSize) == false) return false; if (!pConfig) { return false; } channels = MP4AV_AacConfigGetChannels(pConfig); /* convert ES Config into ASCII form */ char* sConfig = MP4BinaryToBase16(pConfig, configSize); free(pConfig); if (!sConfig) { return false; } /* create the appropriate SDP attribute */ uint sdpBufLen = strlen(sConfig) + 256; char* sdpBuf = (char*)malloc(sdpBufLen); if (!sdpBuf) { free(sConfig); return false; } // now add the hint track MP4TrackId hintTrackId = MP4AddHintTrack(mp4File, mediaTrackId); if (hintTrackId == MP4_INVALID_TRACK_ID) { free(sConfig); free(sdpBuf); return false; } u_int8_t payloadNumber = MP4_SET_DYNAMIC_PAYLOAD; char buffer[10]; if (channels != 1) { snprintf(buffer, sizeof(buffer), "%u", channels); } if (MP4SetHintTrackRtpPayload(mp4File, hintTrackId, "mpeg4-generic", &payloadNumber, 0, channels != 1 ? buffer : NULL) == false) { MP4DeleteTrack(mp4File, hintTrackId); free(sConfig); free(sdpBuf); return false; } MP4Duration maxLatency; bool OneByteHeader = false; if (mpeg4AudioType == MP4_MPEG4_CELP_AUDIO_TYPE) { snprintf(sdpBuf, sdpBufLen, "a=fmtp:%u " "streamtype=5; profile-level-id=15; mode=CELP-vbr; config=%s; " "SizeLength=6; IndexLength=2; IndexDeltaLength=2; Profile=0;" "\015\012", payloadNumber, sConfig); // 200 ms max latency for ISMA profile 1 maxLatency = timeScale / 5; OneByteHeader = true; } else { // AAC snprintf(sdpBuf, sdpBufLen, "a=fmtp:%u " "streamtype=5; profile-level-id=15; mode=AAC-hbr; config=%s; " "SizeLength=13; IndexLength=3; IndexDeltaLength=3;" "\015\012", payloadNumber, sConfig); // 500 ms max latency for ISMA profile 1 maxLatency = timeScale / 2; } /* add this to the track's sdp */ bool val = MP4AppendHintTrackSdp(mp4File, hintTrackId, sdpBuf); free(sConfig); free(sdpBuf); if (val == false) { MP4DeleteTrack(mp4File, hintTrackId); return false; } u_int32_t samplesPerPacket = 0; if (interleave) { u_int32_t maxSampleSize = MP4GetTrackMaxSampleSize(mp4File, mediaTrackId); // compute how many maximum size samples would fit in a packet samplesPerPacket = (maxPayloadSize - 2) / (maxSampleSize + 2); // can't interleave if this number is 0 or 1 if (samplesPerPacket < 2) { interleave = false; } } bool rc; if (interleave) { u_int32_t samplesPerGroup = maxLatency / sampleDuration; u_int32_t stride; stride = samplesPerGroup / samplesPerPacket; if (OneByteHeader && stride > 3) stride = 3; if (!OneByteHeader && stride > 7) stride = 7; #if 0 printf("max latency %llu sampleDuration %llu spg %u spp %u strid %u\n", maxLatency, sampleDuration, samplesPerGroup, samplesPerPacket, stride); #endif rc = MP4AV_AudioInterleaveHinter( mp4File, mediaTrackId, hintTrackId, sampleDuration, stride, // stride samplesPerPacket, // bundle maxPayloadSize, MP4AV_RfcIsmaConcatenator); } else { rc = MP4AV_AudioConsecutiveHinter( mp4File, mediaTrackId, hintTrackId, sampleDuration, 2, // perPacketHeaderSize 2, // perSampleHeaderSize maxLatency / sampleDuration, // maxSamplesPerPacket maxPayloadSize, MP4GetSampleSize, MP4AV_RfcIsmaConcatenator, MP4AV_RfcIsmaFragmenter); } if (!rc) { MP4DeleteTrack(mp4File, hintTrackId); return false; } return true; }
bool LoadFileAAC(FILE_INFO *pFile) { MP4FileHandle h = MP4Read(GetFullPath(pFile), 0); if (h == MP4_INVALID_FILE_HANDLE) { return false; } char* value; char* buff; u_int16_t no, total; if (MP4GetMetadataName(h, &value) == true) { if (UTF8toSJIS(value, &buff) == true) { SetTrackNameSI(pFile, buff); free(buff); } } if (MP4GetMetadataArtist(h, &value) == true) { if (UTF8toSJIS(value, &buff) == true) { SetArtistNameSI(pFile, buff); free(buff); } } if (MP4GetMetadataWriter(h, &value) == true) { if (UTF8toSJIS(value, &buff) == true) { SetComposerSI(pFile, buff); free(buff); } } if (MP4GetMetadataComment(h, &value) == true) { if (UTF8toSJIS(value, &buff) == true) { SetCommentSI(pFile, buff); free(buff); } } if (MP4GetMetadataTool(h, &value) == true) { if (UTF8toSJIS(value, &buff) == true) { SetSoftwareSI(pFile, buff); free(buff); } } if (MP4GetMetadataYear(h, &value) == true) { if (UTF8toSJIS(value, &buff) == true) { SetYearSI(pFile, buff); free(buff); } } if (MP4GetMetadataAlbum(h, &value) == true) { if (UTF8toSJIS(value, &buff) == true) { SetAlbumNameSI(pFile, buff); free(buff); } } if (MP4GetMetadataAlbumArtist(h, &value) == true) { /* 取得できるようにmp4v2.dllを変更 */ if (UTF8toSJIS(value, &buff) == true) { SetAlbumArtistSI(pFile, buff); free(buff); } } if (MP4GetMetadataTrack(h, &no, &total) == true) { char trackNo[10]; if (total > 0) { sprintf(trackNo, "%d/%d", no, total); } else { sprintf(trackNo, "%d", no); } SetTrackNumberSI(pFile, trackNo); } if (MP4GetMetadataDisk(h, &no, &total) == true) { char diskNo[10]; if (total > 0) { sprintf(diskNo, "%d/%d", no, total); } else { sprintf(diskNo, "%d", no); } SetDiskNumberSI(pFile, diskNo); } if (MP4GetMetadataGenre(h, &value) == true) { /* 取得できるようにmp4v2.dllを変更 */ if (UTF8toSJIS(value, &buff) == true) { SetGenreSI(pFile, buff); free(buff); } } if (MP4GetMetadataGrouping(h, &value) == true) { /* 取得できるようにmp4v2.dllに追加 */ if (UTF8toSJIS(value, &buff) == true) { SetKeywordSI(pFile, buff); free(buff); } } CString strOther = ""; { u_int16_t tempo; if (MP4GetMetadataTempo(h, &tempo) == true) { if (tempo > 0) { char buff[10]; sprintf(buff, " %dBPM", tempo); strOther += buff; } } } { u_int8_t cpl; if (MP4GetMetadataCompilation(h, &cpl) == true) { if (cpl == 1) { strOther += " コンピレーションの一部"; } } } //MP4TrackId trackId = MP4FindTrackId(pFile, 0, MP4_AUDIO_TRACK_TYPE); //SetAudioFormat(pFile, MP4Info(h)); // mp4info.cpp PrintAudioInfo() MP4TrackId trackId = MP4FindTrackId(h, 0); static const char* mpeg4AudioNames[] = { "MPEG-4 AAC main", "MPEG-4 AAC LC", "MPEG-4 AAC SSR", "MPEG-4 AAC LTP", NULL, "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", }; static 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 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)", "PCM16 (little endian)", "Vorbis", "G.711 aLaw", "G.711 uLaw", "G.723.1", "PCM16 (big endian)", }; static u_int8_t numMpegAudioTypes = sizeof(mpegAudioTypes) / sizeof(u_int8_t); u_int8_t type = MP4GetTrackEsdsObjectTypeId(h, trackId); const char* typeName = "Unknown"; if (type == MP4_MPEG4_AUDIO_TYPE) { u_int8_t* pAacConfig = NULL; u_int32_t aacConfigLength; MP4GetTrackESConfiguration(h, trackId, &pAacConfig, &aacConfigLength); if (pAacConfig != NULL && aacConfigLength >= 2) { type = (pAacConfig[0] >> 3) & 0x1f; if (type == 0 || type == 5 || type == 10 || type == 11 || type == 18 || type >= 28) { typeName = "MPEG-4"; } else { typeName = mpeg4AudioNames[type - 1]; } MP4Free(pAacConfig); } else {
static char* PrintAudioInfo( 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); 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 { 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; } u_int32_t timeScale = MP4GetTrackTimeScale(mp4File, trackId); MP4Duration trackDuration = MP4GetTrackDuration(mp4File, trackId); double msDuration = UINT64_TO_DOUBLE(MP4ConvertFromTrackDuration(mp4File, trackId, trackDuration, MP4_MSECS_TIME_SCALE)); u_int32_t avgBitRate = MP4GetTrackBitRate(mp4File, trackId); char *sInfo = (char*)MP4Malloc(256); // type duration avgBitrate samplingFrequency if (foundType) snprintf(sInfo, 256, "%u\taudio\t%s%s, %.3f secs, %u kbps, %u Hz\n", trackId, MP4IsIsmaCrypMediaTrack(mp4File, trackId) ? "enca - " : "", typeName, msDuration / 1000.0, (avgBitRate + 500) / 1000, timeScale); else snprintf(sInfo, 256, "%u\taudio\t%s%s(%u), %.3f secs, %u kbps, %u Hz\n", trackId, MP4IsIsmaCrypMediaTrack(mp4File, trackId) ? "enca - " : "", typeName, type, msDuration / 1000.0, (avgBitRate + 500) / 1000, timeScale); return sInfo; }
static char* PrintVideoInfo( 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 (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 == 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) { 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), "H264 %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)); u_int32_t avgBitRate = MP4GetTrackBitRate(mp4File, trackId); // 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, "%u\tvideo\t%s%s, %.3f secs, %u kbps, %ux%u @ %f fps\n", trackId, MP4IsIsmaCrypMediaTrack(mp4File, trackId) ? "encv - " : "", typeName, msDuration / 1000.0, (avgBitRate + 500) / 1000, width, height, fps ); } else { sprintf(sInfo, "%u\tvideo\t%s(%u), %.3f secs, %u kbps, %ux%u @ %f fps\n", trackId, typeName, type, msDuration / 1000.0, (avgBitRate + 500) / 1000, width, height, fps ); } return sInfo; }
static void DumpTrack (MP4FileHandle mp4file, MP4TrackId tid, bool dump_off, bool dump_rend) { uint32_t numSamples; MP4SampleId sid; uint8_t *buffer; uint32_t max_frame_size; uint32_t timescale; uint64_t msectime; const char *media_data_name; uint32_t len_size = 0; uint8_t video_type = 0; numSamples = MP4GetTrackNumberOfSamples(mp4file, tid); max_frame_size = MP4GetTrackMaxSampleSize(mp4file, tid) + 4; media_data_name = MP4GetTrackMediaDataName(mp4file, tid); if (strcasecmp(media_data_name, "avc1") == 0) { MP4GetTrackH264LengthSize(mp4file, tid, &len_size); } else if (strcasecmp(media_data_name, "mp4v") == 0) { video_type = MP4GetTrackEsdsObjectTypeId(mp4file, tid); } buffer = (uint8_t *)malloc(max_frame_size); if (buffer == NULL) { printf("couldn't get buffer\n"); return; } timescale = MP4GetTrackTimeScale(mp4file, tid); printf("mp4file %s, track %d, samples %d, timescale %d\n", Mp4FileName, tid, numSamples, timescale); for (sid = 1; sid <= numSamples; sid++) { MP4Timestamp sampleTime; MP4Duration sampleDuration, sampleRenderingOffset; bool isSyncSample = FALSE; bool ret; u_int8_t *temp; uint32_t this_frame_size = max_frame_size; temp = buffer; ret = MP4ReadSample(mp4file, tid, sid, &temp, &this_frame_size, &sampleTime, &sampleDuration, &sampleRenderingOffset, &isSyncSample); msectime = sampleTime; msectime *= TO_U64(1000); msectime /= timescale; printf("sampleId %6d, size %5u time "U64"("U64")", sid, MP4GetSampleSize(mp4file, tid, sid), sampleTime, msectime); if (dump_rend) printf(" %6"U64F, sampleRenderingOffset); if (strcasecmp(media_data_name, "mp4v") == 0) { if (MP4_IS_MPEG4_VIDEO_TYPE(video_type)) ParseMpeg4(temp, this_frame_size, dump_off); } else if (strcasecmp(media_data_name, "avc1") == 0) { ParseH264(temp, this_frame_size, len_size, dump_off); } printf("\n"); } }
int main (int argc, char *argv[]) { int len = 0; char *allargs = NULL, *step; argc--; argv++; while (argc > 0 && strcasestr(*argv, ".mp4") != NULL) { MP4FileHandle mp4File; mp4File = MP4Read(*argv, MP4_DETAILS_ERROR); if (MP4_IS_VALID_FILE_HANDLE(mp4File)) { MP4TrackId tid; uint32_t ix = 0; do { uint32_t verb = MP4GetVerbosity(mp4File); MP4SetVerbosity(mp4File, verb & ~(MP4_DETAILS_ERROR)); tid = MP4FindTrackId(mp4File, ix, MP4_VIDEO_TRACK_TYPE); MP4SetVerbosity(mp4File, verb); if (MP4_IS_VALID_TRACK_ID(tid)) { uint8_t type = MP4GetTrackEsdsObjectTypeId(mp4File, tid); if (type == MP4_MPEG4_VIDEO_TYPE) { uint8_t *foo; uint32_t bufsize; MP4GetTrackESConfiguration(mp4File, tid, &foo, &bufsize); if (foo != NULL && bufsize != 0) { printf("%s\n", *argv); decode(foo, bufsize); free(foo); } else { fprintf(stderr, "%s - track %d - can't find esds\n", *argv, tid); } } else { fprintf(stderr, "%s - track %d is not MPEG4 - type %u\n", *argv, tid, type); } } ix++; } while (MP4_IS_VALID_TRACK_ID(tid)); } else { fprintf(stderr, "%s is not a valid mp4 file\n", *argv); } argc--; argv++; } if (argc > 0) { len = 1; while (argc > 0) { len += strlen(*argv); if (allargs == NULL) { allargs = (char *)malloc(len); allargs[0] = '\0'; } else allargs = (char *)realloc(allargs, len); strcat(allargs, *argv); argv++; argc--; } if ((len - 1) & 0x1) { fprintf(stderr, "odd length VOL\n"); exit(1); } len /= 2; uint8_t *vol = (uint8_t *)malloc(len), *write; write = vol; step = allargs; int ix; for (ix = 0; ix < len; ix++) { *write = 0; *write = tohex(*step) << 4; step++; *write |= tohex(*step); step++; write++; } printf("decoding vol \"%s\"\n", allargs); decode(vol, len); } return(0); }
static char* PrintAudioInfo( 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", }; 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); 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: { u_int8_t* pAacConfig = NULL; u_int32_t aacConfigLength; MP4GetTrackESConfiguration(mp4File, trackId, &pAacConfig, &aacConfigLength); if (pAacConfig != NULL && aacConfigLength >= 2) { type = (pAacConfig[0] >> 3) & 0x1f; if (type == 0 || /* type == 5 || */ type == 10 || type == 11 || type == 18 || type >= 28) { typeName = "MPEG-4 Unknown Profile"; } else { typeName = mpeg4AudioNames[type - 1]; foundType = true; } free(pAacConfig); } else { typeName = "MPEG-4 (no GAConfig)"; foundType = true; } break; }
static GtkWidget* CreateMp4TrackMenu( GtkWidget* menu, char type, const char* source, u_int32_t* pIndex, u_int32_t* pNumber, u_int32_t** ppValues) { *pIndex = 0; u_int32_t newTrackNumber = 1; MP4FileHandle mp4File = MP4Read(source); char* trackType = NULL; if (mp4File) { if (type == 'V') { trackType = MP4_VIDEO_TRACK_TYPE; } else { trackType = MP4_AUDIO_TRACK_TYPE; } newTrackNumber = MP4GetNumberOfTracks(mp4File, trackType); } u_int32_t* newTrackValues = (u_int32_t*)malloc(sizeof(u_int32_t) * newTrackNumber); char** newTrackNames = (char**)malloc(sizeof(char*) * newTrackNumber); if (!mp4File) { newTrackValues[0] = 0; newTrackNames[0] = strdup(""); } else { for (u_int8_t i = 0; i < newTrackNumber; i++) { MP4TrackId trackId = MP4FindTrackId(mp4File, i, trackType); char* trackName = "Unknown"; char buf[64]; if (trackType == MP4_VIDEO_TRACK_TYPE) { u_int8_t videoType = MP4GetTrackEsdsObjectTypeId(mp4File, trackId); switch (videoType) { case MP4_MPEG1_VIDEO_TYPE: trackName = "MPEG1"; break; case MP4_MPEG2_SIMPLE_VIDEO_TYPE: case MP4_MPEG2_MAIN_VIDEO_TYPE: case MP4_MPEG2_SNR_VIDEO_TYPE: case MP4_MPEG2_SPATIAL_VIDEO_TYPE: case MP4_MPEG2_HIGH_VIDEO_TYPE: case MP4_MPEG2_442_VIDEO_TYPE: trackName = "MPEG2"; break; case MP4_MPEG4_VIDEO_TYPE: trackName = "MPEG4"; break; case MP4_YUV12_VIDEO_TYPE: trackName = "YUV12"; break; case MP4_H263_VIDEO_TYPE: trackName = "H263"; break; case MP4_H261_VIDEO_TYPE: trackName = "H261"; break; } snprintf(buf, sizeof(buf), "%u - %s %u x %u %.2f fps %u kbps", trackId, trackName, MP4GetTrackVideoWidth(mp4File, trackId), MP4GetTrackVideoHeight(mp4File, trackId), MP4GetTrackVideoFrameRate(mp4File, trackId), (MP4GetTrackBitRate(mp4File, trackId) + 500) / 1000); } else { // audio u_int8_t audioType = MP4GetTrackEsdsObjectTypeId(mp4File, trackId); switch (audioType) { case MP4_MPEG1_AUDIO_TYPE: case MP4_MPEG2_AUDIO_TYPE: trackName = "MPEG (MP3)"; break; case MP4_MPEG2_AAC_MAIN_AUDIO_TYPE: case MP4_MPEG2_AAC_LC_AUDIO_TYPE: case MP4_MPEG2_AAC_SSR_AUDIO_TYPE: case MP4_MPEG4_AUDIO_TYPE: trackName = "AAC"; break; case MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE: trackName = "PCM16 LITTLE ENDIAN"; break; case MP4_PCM16_BIG_ENDIAN_AUDIO_TYPE: trackName = "PCM16 BIG ENDIAN"; case MP4_AC3_AUDIO_TYPE: trackName = "AC3"; break; case MP4_VORBIS_AUDIO_TYPE: trackName = "Ogg Vorbis"; break; case MP4_ALAW_AUDIO_TYPE: trackName = "G711 aLaw"; break; case MP4_ULAW_AUDIO_TYPE: trackName = "G711 uLaw"; break; } snprintf(buf, sizeof(buf), "%u - %s %u kbps", trackId, trackName, (MP4GetTrackBitRate(mp4File, trackId) + 500) / 1000); } newTrackValues[i] = trackId; newTrackNames[i] = strdup(buf); } MP4Close(mp4File); } // (re)create the menu menu = CreateOptionMenu( menu, newTrackNames, newTrackNumber, *pIndex, GTK_SIGNAL_FUNC(on_track_menu_activate)); // free up old names for (u_int8_t i = 0; i < *pNumber; i++) { free(trackNames[i]); } free(trackNames); free(*ppValues); *pNumber = newTrackNumber; trackNames = newTrackNames; *ppValues = newTrackValues; return menu; }