int AacPcm::getInfos(MediaInfo *infos) { if(!infos) return 1; if(hDecoder) { SHOW_INFO() return 0; } IsAAC=strcmpi(infos->getFilename()+lstrlen(infos->getFilename())-4,".aac")==0; if(!IsAAC) // MP4 file --------------------------------------------------------------------- { MP4Duration length; unsigned __int32 buffer_size; mp4AudioSpecificConfig mp4ASC; if(!(mp4File=MP4Read(infos->getFilename(), 0))) ERROR_getInfos("Error opening file"); if((track=GetAACTrack(mp4File))<0) ERROR_getInfos(0); //"Unable to find correct AAC sound track"); if(!(hDecoder=faacDecOpen())) ERROR_getInfos("Error initializing decoder library"); MP4GetTrackESConfiguration(mp4File, track, (unsigned __int8 **)&buffer, &buffer_size); if(!buffer) ERROR_getInfos("MP4GetTrackESConfiguration"); AudioSpecificConfig(buffer, buffer_size, &mp4ASC); Channels=mp4ASC.channelsConfiguration; if(faacDecInit2(hDecoder, buffer, buffer_size, &Samplerate, &Channels) < 0) ERROR_getInfos("Error initializing decoder library"); FREE_ARRAY(buffer); length=MP4GetTrackDuration(mp4File, track); len_ms=(DWORD)MP4ConvertFromTrackDuration(mp4File, track, length, MP4_MSECS_TIME_SCALE); file_info.bitrate=MP4GetTrackBitRate(mp4File, track); file_info.version=MP4GetTrackAudioType(mp4File, track)==MP4_MPEG4_AUDIO_TYPE ? 4 : 2; numSamples=MP4GetTrackNumberOfSamples(mp4File, track); sampleId=1; } else // AAC file ------------------------------------------------------------------------------ { DWORD read, tmp; BYTE Channels4Raw=0; if(!(aacFile=fopen(infos->getFilename(),"rb"))) ERROR_getInfos("Error opening file"); // use bufferized stream setvbuf(aacFile,NULL,_IOFBF,32767); // get size of file fseek(aacFile, 0, SEEK_END); src_size=ftell(aacFile); fseek(aacFile, 0, SEEK_SET); if(!(buffer=(BYTE *)malloc(FAAD_STREAMSIZE))) ERROR_getInfos("Memory allocation error: buffer") tmp=src_size<FAAD_STREAMSIZE ? src_size : FAAD_STREAMSIZE; read=fread(buffer, 1, tmp, aacFile); if(read==tmp) { bytes_read=read; bytes_into_buffer=read; } else ERROR_getInfos("Read failed!") if(tagsize=id3v2_tag(buffer)) { if(tagsize>(long)src_size) ERROR_getInfos("Corrupt stream!"); if(tagsize<bytes_into_buffer) { bytes_into_buffer-=tagsize; memcpy(buffer,buffer+tagsize,bytes_into_buffer); } else { bytes_read=tagsize; bytes_into_buffer=0; if(tagsize>bytes_into_buffer) fseek(aacFile, tagsize, SEEK_SET); } if(src_size<bytes_read+FAAD_STREAMSIZE-bytes_into_buffer) tmp=src_size-bytes_read; else tmp=FAAD_STREAMSIZE-bytes_into_buffer; read=fread(buffer+bytes_into_buffer, 1, tmp, aacFile); if(read==tmp) { bytes_read+=read; bytes_into_buffer+=read; } else ERROR_getInfos("Read failed!"); } if(get_AAC_format((char *)infos->getFilename(), &file_info, &seek_table, &seek_table_length, 0)) ERROR_getInfos("get_AAC_format"); IsSeekable=file_info.headertype==ADTS && seek_table && seek_table_length>0; BlockSeeking=!IsSeekable; if(!(hDecoder=faacDecOpen())) ERROR_getInfos("Can't open library"); if(file_info.headertype==RAW) { faacDecConfiguration config; config.defSampleRate=atoi(cfg_samplerate); switch(cfg_profile[1]) { case 'a': config.defObjectType=MAIN; break; case 'o': config.defObjectType=LOW; break; case 'S': config.defObjectType=SSR; break; case 'T': config.defObjectType=LTP; break; } switch(cfg_bps[0]) { case '1': config.outputFormat=FAAD_FMT_16BIT; break; case '2': config.outputFormat=FAAD_FMT_24BIT; break; case '3': config.outputFormat=FAAD_FMT_32BIT; break; case 'F': config.outputFormat=FAAD_FMT_24BIT; break; } faacDecSetConfiguration(hDecoder, &config); if(!FindBitrate) { AacPcm *NewInst; if(!(NewInst=new AacPcm())) ERROR_getInfos("Memory allocation error: NewInst"); NewInst->FindBitrate=TRUE; if(NewInst->getInfos(infos)) ERROR_getInfos(0); Channels4Raw=NewInst->frameInfo.channels; file_info.bitrate=NewInst->file_info.bitrate*Channels4Raw; delete NewInst; } else { DWORD Samples, BytesConsumed; if((bytes_consumed=faacDecInit(hDecoder,buffer,bytes_into_buffer,&Samplerate,&Channels))<0) ERROR_getInfos("Can't init library"); bytes_into_buffer-=bytes_consumed; if(!processData(infos,0,0)) ERROR_getInfos(0); Samples=frameInfo.samples/sizeof(short); BytesConsumed=frameInfo.bytesconsumed; processData(infos,0,0); if(BytesConsumed<frameInfo.bytesconsumed) BytesConsumed=frameInfo.bytesconsumed; file_info.bitrate=(BytesConsumed*8*Samplerate)/Samples; if(!file_info.bitrate) file_info.bitrate=1000; // try to continue decoding return 0; } } if((bytes_consumed=faacDecInit(hDecoder, buffer, bytes_into_buffer, &Samplerate, &Channels))<0) ERROR_getInfos("faacDecInit failed!") bytes_into_buffer-=bytes_consumed; if(Channels4Raw) Channels=Channels4Raw; len_ms=(DWORD)((1000*((float)src_size*8))/file_info.bitrate); } SHOW_INFO(); return 0; }
unsigned int GetBitRate() const { return MP4GetTrackBitRate(handle, video_track_id); }
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; }
bool MP4Metadata::ReadMetadata(CFErrorRef *error) { // Start from scratch CFDictionaryRemoveAllValues(mMetadata); CFDictionaryRemoveAllValues(mChangedMetadata); UInt8 buf [PATH_MAX]; if(!CFURLGetFileSystemRepresentation(mURL, FALSE, buf, PATH_MAX)) return false; // Open the file for reading MP4FileHandle file = MP4Read(reinterpret_cast<const char *>(buf)); if(MP4_INVALID_FILE_HANDLE == file) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 32, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mURL); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” is not a valid MPEG-4 file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not an MPEG-4 file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioMetadataErrorDomain, AudioMetadataFileFormatNotRecognizedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } return false; } // Read the properties if(0 < MP4GetNumberOfTracks(file)) { // Should be type 'soun', media data name'mp4a' MP4TrackId trackID = MP4FindTrackId(file, 0); // Verify this is an MPEG-4 audio file if(MP4_INVALID_TRACK_ID == trackID || strncmp("soun", MP4GetTrackType(file, trackID), 4)) { MP4Close(file), file = NULL; if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 32, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mURL); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” is not a valid MPEG-4 file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not an MPEG-4 file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioMetadataErrorDomain, AudioMetadataFileFormatNotSupportedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } return false; } MP4Duration mp4Duration = MP4GetTrackDuration(file, trackID); uint32_t mp4TimeScale = MP4GetTrackTimeScale(file, trackID); CFNumberRef totalFrames = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &mp4Duration); CFDictionarySetValue(mMetadata, kPropertiesTotalFramesKey, totalFrames); CFRelease(totalFrames), totalFrames = NULL; CFNumberRef sampleRate = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &mp4TimeScale); CFDictionarySetValue(mMetadata, kPropertiesSampleRateKey, sampleRate); CFRelease(sampleRate), sampleRate = NULL; double length = static_cast<double>(mp4Duration / mp4TimeScale); CFNumberRef duration = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &length); CFDictionarySetValue(mMetadata, kPropertiesDurationKey, duration); CFRelease(duration), duration = NULL; // "mdia.minf.stbl.stsd.*[0].channels" int channels = MP4GetTrackAudioChannels(file, trackID); CFNumberRef channelsPerFrame = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &channels); CFDictionaryAddValue(mMetadata, kPropertiesChannelsPerFrameKey, channelsPerFrame); CFRelease(channelsPerFrame), channelsPerFrame = NULL; // ALAC files if(MP4HaveTrackAtom(file, trackID, "mdia.minf.stbl.stsd.alac")) { CFDictionarySetValue(mMetadata, kPropertiesFormatNameKey, CFSTR("Apple Lossless")); uint64_t sampleSize; uint8_t *decoderConfig; uint32_t decoderConfigSize; if(MP4GetTrackBytesProperty(file, trackID, "mdia.minf.stbl.stsd.alac.alac.decoderConfig", &decoderConfig, &decoderConfigSize) && 28 <= decoderConfigSize) { // The ALAC magic cookie seems to have the following layout (28 bytes, BE): // Byte 10: Sample size // Bytes 25-28: Sample rate CFNumberRef bitsPerChannel = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt8Type, decoderConfig + 9); CFDictionaryAddValue(mMetadata, kPropertiesBitsPerChannelKey, bitsPerChannel); CFRelease(bitsPerChannel), bitsPerChannel = NULL; double losslessBitrate = static_cast<double>(mp4TimeScale * channels * sampleSize) / 1000; CFNumberRef bitrate = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &losslessBitrate); CFDictionarySetValue(mMetadata, kPropertiesBitrateKey, bitrate); CFRelease(bitrate), bitrate = NULL; free(decoderConfig), decoderConfig = NULL; } else if(MP4GetTrackIntegerProperty(file, trackID, "mdia.minf.stbl.stsd.alac.sampleSize", &sampleSize)) { CFNumberRef bitsPerChannel = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &sampleSize); CFDictionaryAddValue(mMetadata, kPropertiesBitsPerChannelKey, bitsPerChannel); CFRelease(bitsPerChannel), bitsPerChannel = NULL; double losslessBitrate = static_cast<double>(mp4TimeScale * channels * sampleSize) / 1000; CFNumberRef bitrate = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &losslessBitrate); CFDictionarySetValue(mMetadata, kPropertiesBitrateKey, bitrate); CFRelease(bitrate), bitrate = NULL; } } // AAC files if(MP4HaveTrackAtom(file, trackID, "mdia.minf.stbl.stsd.mp4a")) { CFDictionarySetValue(mMetadata, kPropertiesFormatNameKey, CFSTR("AAC")); // "mdia.minf.stbl.stsd.*.esds.decConfigDescr.avgBitrate" uint32_t trackBitrate = MP4GetTrackBitRate(file, trackID) / 1000; CFNumberRef bitrate = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &trackBitrate); CFDictionaryAddValue(mMetadata, kPropertiesBitrateKey, bitrate); CFRelease(bitrate), bitrate = NULL; } } // No valid tracks in file else { MP4Close(file), file = NULL; if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 32, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mURL); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” is not a valid MPEG-4 file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not an MPEG-4 file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioMetadataErrorDomain, AudioMetadataFileFormatNotSupportedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } return false; } // Read the tags const MP4Tags *tags = MP4TagsAlloc(); if(NULL == tags) { MP4Close(file), file = NULL; if(error) *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, ENOMEM, NULL); return false; } MP4TagsFetch(tags, file); // Album title if(tags->album) { CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, tags->album, kCFStringEncodingUTF8); CFDictionarySetValue(mMetadata, kMetadataAlbumTitleKey, str); CFRelease(str), str = NULL; } // Artist if(tags->artist) { CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, tags->artist, kCFStringEncodingUTF8); CFDictionarySetValue(mMetadata, kMetadataArtistKey, str); CFRelease(str), str = NULL; } // Album Artist if(tags->albumArtist) { CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, tags->albumArtist, kCFStringEncodingUTF8); CFDictionarySetValue(mMetadata, kMetadataAlbumArtistKey, str); CFRelease(str), str = NULL; } // Genre if(tags->genre) { CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, tags->genre, kCFStringEncodingUTF8); CFDictionarySetValue(mMetadata, kMetadataGenreKey, str); CFRelease(str), str = NULL; } // Release date if(tags->releaseDate) { CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, tags->releaseDate, kCFStringEncodingUTF8); CFDictionarySetValue(mMetadata, kMetadataReleaseDateKey, str); CFRelease(str), str = NULL; } // Composer if(tags->composer) { CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, tags->composer, kCFStringEncodingUTF8); CFDictionarySetValue(mMetadata, kMetadataComposerKey, str); CFRelease(str), str = NULL; } // Comment if(tags->comments) { CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, tags->comments, kCFStringEncodingUTF8); CFDictionarySetValue(mMetadata, kMetadataCommentKey, str); CFRelease(str), str = NULL; } // Track title if(tags->name) { CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, tags->name, kCFStringEncodingUTF8); CFDictionarySetValue(mMetadata, kMetadataTitleKey, str); CFRelease(str), str = NULL; } // Track number if(tags->track) { if(tags->track->index) { CFNumberRef num = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt16Type, &tags->track->index); CFDictionarySetValue(mMetadata, kMetadataTrackNumberKey, num); CFRelease(num), num = NULL; } if(tags->track->total) { CFNumberRef num = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt16Type, &tags->track->total); CFDictionarySetValue(mMetadata, kMetadataTrackTotalKey, num); CFRelease(num), num = NULL; } } // Disc number if(tags->disk) { if(tags->disk->index) { CFNumberRef num = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt16Type, &tags->disk->index); CFDictionarySetValue(mMetadata, kMetadataDiscNumberKey, num); CFRelease(num), num = NULL; } if(tags->disk->total) { CFNumberRef num = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt16Type, &tags->disk->total); CFDictionarySetValue(mMetadata, kMetadataDiscTotalKey, num); CFRelease(num), num = NULL; } } // Compilation if(tags->compilation) CFDictionarySetValue(mMetadata, kMetadataCompilationKey, *(tags->compilation) ? kCFBooleanTrue : kCFBooleanFalse); // BPM if(tags->tempo) { } // Lyrics if(tags->lyrics) { CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, tags->lyrics, kCFStringEncodingUTF8); CFDictionarySetValue(mMetadata, kMetadataLyricsKey, str); CFRelease(str), str = NULL; } // Album art if(tags->artworkCount) { for(uint32_t i = 0; i < tags->artworkCount; ++i) { CFDataRef data = CFDataCreate(kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(tags->artwork[i].data), tags->artwork[i].size); CFDictionarySetValue(mMetadata, kAlbumArtFrontCoverKey, data); CFRelease(data), data = NULL; } } // ReplayGain // Reference loudness MP4ItmfItemList *items = MP4ItmfGetItemsByMeaning(file, "com.apple.iTunes", "replaygain_reference_loudness"); if(NULL != items) { float referenceLoudnessValue; if(1 <= items->size && 1 <= items->elements[0].dataList.size && sscanf(reinterpret_cast<const char *>(items->elements[0].dataList.elements[0].value), "%f", &referenceLoudnessValue)) { CFNumberRef referenceLoudness = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &referenceLoudnessValue); CFDictionaryAddValue(mMetadata, kReplayGainReferenceLoudnessKey, referenceLoudness); CFRelease(referenceLoudness), referenceLoudness = NULL; } MP4ItmfItemListFree(items), items = NULL; } // Track gain items = MP4ItmfGetItemsByMeaning(file, "com.apple.iTunes", "replaygain_track_gain"); if(NULL != items) { float trackGainValue; if(1 <= items->size && 1 <= items->elements[0].dataList.size && sscanf(reinterpret_cast<const char *>(items->elements[0].dataList.elements[0].value), "%f", &trackGainValue)) { CFNumberRef trackGain = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &trackGainValue); CFDictionaryAddValue(mMetadata, kReplayGainTrackGainKey, trackGain); CFRelease(trackGain), trackGain = NULL; } MP4ItmfItemListFree(items), items = NULL; } // Track peak items = MP4ItmfGetItemsByMeaning(file, "com.apple.iTunes", "replaygain_track_peak"); if(NULL != items) { float trackPeakValue; if(1 <= items->size && 1 <= items->elements[0].dataList.size && sscanf(reinterpret_cast<const char *>(items->elements[0].dataList.elements[0].value), "%f", &trackPeakValue)) { CFNumberRef trackPeak = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &trackPeakValue); CFDictionaryAddValue(mMetadata, kReplayGainTrackPeakKey, trackPeak); CFRelease(trackPeak), trackPeak = NULL; } MP4ItmfItemListFree(items), items = NULL; } // Album gain items = MP4ItmfGetItemsByMeaning(file, "com.apple.iTunes", "replaygain_album_gain"); if(NULL != items) { float albumGainValue; if(1 <= items->size && 1 <= items->elements[0].dataList.size && sscanf(reinterpret_cast<const char *>(items->elements[0].dataList.elements[0].value), "%f", &albumGainValue)) { CFNumberRef albumGain = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &albumGainValue); CFDictionaryAddValue(mMetadata, kReplayGainAlbumGainKey, albumGain); CFRelease(albumGain), albumGain = NULL; } MP4ItmfItemListFree(items), items = NULL; } // Album peak items = MP4ItmfGetItemsByMeaning(file, "com.apple.iTunes", "replaygain_album_peak"); if(NULL != items) { float albumPeakValue; if(1 <= items->size && 1 <= items->elements[0].dataList.size && sscanf(reinterpret_cast<const char *>(items->elements[0].dataList.elements[0].value), "%f", &albumPeakValue)) { CFNumberRef albumPeak = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &albumPeakValue); CFDictionaryAddValue(mMetadata, kReplayGainAlbumPeakKey, albumPeak); CFRelease(albumPeak), albumPeak = NULL; } MP4ItmfItemListFree(items), items = NULL; } // Clean up MP4TagsFree(tags), tags = NULL; MP4Close(file), file = NULL; return true; }
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; }
DWORD MP4Streamer::GetVideoBitrate() { return MP4GetTrackBitRate(video->mp4,video->track); }