void MP4BytesProperty::SetValue(const u_int8_t* pValue, u_int32_t valueSize, u_int32_t index) { if (m_readOnly) { throw new MP4Error(EACCES, "property is read-only", m_name); } if (m_fixedValueSize) { if (valueSize > m_fixedValueSize) { throw new MP4Error("value size exceeds fixed value size", "MP4BytesProperty::SetValue"); } if (m_values[index] == NULL) { m_values[index] = (u_int8_t*)MP4Calloc(m_fixedValueSize); m_valueSizes[index] = m_fixedValueSize; } if (pValue) { memcpy(m_values[index], pValue, valueSize); } } else { MP4Free(m_values[index]); if (pValue) { m_values[index] = (u_int8_t*)MP4Malloc(valueSize); memcpy(m_values[index], pValue, valueSize); m_valueSizes[index] = valueSize; } else { m_values[index] = NULL; m_valueSizes[index] = 0; } } }
void MP4Track::ReadSampleFragment( MP4SampleId sampleId, u_int32_t sampleOffset, u_int16_t sampleLength, u_int8_t* pDest) { if (sampleId == MP4_INVALID_SAMPLE_ID) { throw new MP4Error("invalid sample id", "MP4Track::ReadSampleFragment"); } if (sampleId != m_cachedReadSampleId) { MP4Free(m_pCachedReadSample); m_pCachedReadSample = NULL; m_cachedReadSampleSize = 0; m_cachedReadSampleId = MP4_INVALID_SAMPLE_ID; ReadSample( sampleId, &m_pCachedReadSample, &m_cachedReadSampleSize); m_cachedReadSampleId = sampleId; } if (sampleOffset + sampleLength > m_cachedReadSampleSize) { throw new MP4Error("offset and/or length are too large", "MP4Track::ReadSampleFragment"); } memcpy(pDest, &m_pCachedReadSample[sampleOffset], sampleLength); }
MP4BytesProperty::~MP4BytesProperty() { u_int32_t count = GetCount(); for (u_int32_t i = 0; i < count; i++) { MP4Free(m_values[i]); } }
void MP4Track::WriteChunkBuffer() { if (m_chunkBufferSize == 0) { return; } u_int64_t chunkOffset = m_pFile->GetPosition(); // write chunk buffer m_pFile->WriteBytes(m_pChunkBuffer, m_chunkBufferSize); VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(), printf("WriteChunk: track %u offset 0x"LLX" size %u (0x%x) numSamples %u\n", m_trackId, chunkOffset, m_chunkBufferSize, m_chunkBufferSize, m_chunkSamples)); UpdateSampleToChunk(m_writeSampleId, m_pChunkCountProperty->GetValue() + 1, m_chunkSamples); UpdateChunkOffsets(chunkOffset); // clean up chunk buffer MP4Free(m_pChunkBuffer); m_pChunkBuffer = NULL; m_chunkBufferSize = 0; m_chunkSamples = 0; m_chunkDuration = 0; }
void MP4BytesProperty::Read(MP4File* pFile, u_int32_t index) { if (m_implicit) { return; } MP4Free(m_values[index]); m_values[index] = (u_int8_t*)MP4Malloc(m_valueSizes[index]); pFile->ReadBytes(m_values[index], m_valueSizes[index]); }
void MP4SdpAtom::Read() { // read sdp string, length is implicit in size of atom u_int64_t size = GetEnd() - m_pFile->GetPosition(); char* data = (char*)MP4Malloc(size + 1); ASSERT(data != NULL); m_pFile->ReadBytes((u_int8_t*)data, size); data[size] = '\0'; ((MP4StringProperty*)m_pProperties[0])->SetValue(data); MP4Free(data); }
void MP4StringProperty::Read(MP4File* pFile, u_int32_t index) { if (m_implicit) { return; } if (m_useCountedFormat) { m_values[index] = pFile->ReadCountedString( (m_useUnicode ? 2 : 1), m_useExpandedCount); } else if (m_fixedLength) { MP4Free(m_values[index]); m_values[index] = (char*)MP4Calloc(m_fixedLength + 1); pFile->ReadBytes((u_int8_t*)m_values[index], m_fixedLength); } else { m_values[index] = pFile->ReadString(); } }
void MP4StringProperty::SetValue(const char* value, u_int32_t index) { if (m_readOnly) { throw new MP4Error(EACCES, "property is read-only", m_name); } MP4Free(m_values[index]); if (m_fixedLength) { m_values[index] = (char*)MP4Calloc(m_fixedLength + 1); if (value) { strncpy(m_values[index], value, m_fixedLength); } } else { if (value) { m_values[index] = MP4Stralloc(value); } else { m_values[index] = NULL; } } }
void MP4Track::ReadChunk(MP4ChunkId chunkId, u_int8_t** ppChunk, u_int32_t* pChunkSize) { ASSERT(chunkId); ASSERT(ppChunk); ASSERT(pChunkSize); u_int64_t chunkOffset = m_pChunkOffsetProperty->GetValue(chunkId - 1); *pChunkSize = GetChunkSize(chunkId); *ppChunk = (u_int8_t*)MP4Malloc(*pChunkSize); VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(), printf("ReadChunk: track %u id %u offset 0x"LLX" size %u (0x%x)\n", m_trackId, chunkId, chunkOffset, *pChunkSize, *pChunkSize)); u_int64_t oldPos = m_pFile->GetPosition(); // only used in mode == 'w' try { m_pFile->SetPosition(chunkOffset); m_pFile->ReadBytes(*ppChunk, *pChunkSize); } catch (MP4Error* e) { // let's not leak memory MP4Free(*ppChunk); *ppChunk = NULL; if (m_pFile->GetMode() == 'w') { m_pFile->SetPosition(oldPos); } throw e; } if (m_pFile->GetMode() == 'w') { m_pFile->SetPosition(oldPos); } }
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\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 = wcslen(trackInfo); if (newlen > buflen) buflen = 0; else buflen -= newlen; MP4Free(trackInfo); } } else { info = PrintTrackInfo(mp4File, trackId); } } catch (MP4Error* e) { delete e; } } return info; }
MP4Track::~MP4Track() { MP4Free(m_pCachedReadSample); MP4Free(m_pChunkBuffer); }
void MP4File::Make3GPCompliant(const char* fileName, char* majorBrand, u_int32_t minorVersion, char** supportedBrands, u_int32_t supportedBrandsCount, bool deleteIodsAtom) { char brand[5] = "3gp5"; char* _3gpSupportedBrands[1] = { (char*)&brand }; if (majorBrand) { if (!supportedBrands || !supportedBrandsCount) { throw new MP4Error("Invalid parameters", "MP4File::Make3GPCompliant"); } } m_fileName = MP4Stralloc(fileName); m_mode = 'r'; // first load meta-info into memory Open("rb"); ReadFromFile(); CacheProperties(); // of moov atom // now switch over to writing the new file MP4Free(m_fileName); // create a temporary file m_fileName = MP4Stralloc(TempFileName()); MakeFtypAtom( majorBrand ? majorBrand : (char*)brand, majorBrand ? minorVersion : _3GP_MINOR_VERSION, majorBrand ? supportedBrands : (char**)_3gpSupportedBrands, majorBrand ? supportedBrandsCount : 1); if (deleteIodsAtom) { // Delete the iods atom, if it exists.... MP4Atom* iodsAtom = m_pRootAtom->FindAtom("moov.iods"); if (iodsAtom) { MP4Atom* moovAtom = m_pRootAtom->FindAtom("moov"); ASSERT(moovAtom); moovAtom->DeleteChildAtom(iodsAtom); } } FILE* pReadFile = m_pFile; m_pFile = NULL; m_mode = 'w'; Open("wb"); SetIntegerProperty("moov.mvhd.modificationTime", MP4GetAbsTimestamp()); // writing meta info in the optimal order ((MP4RootAtom*)m_pRootAtom)->BeginOptimalWrite(); // write data in optimal order RewriteMdat(pReadFile, m_pFile); // finish writing ((MP4RootAtom*)m_pRootAtom)->FinishOptimalWrite(); // cleanup fclose(m_pFile); m_pFile = NULL; fclose(pReadFile); // move temporary file into place Rename(m_fileName, fileName); }
/*! \brief Read property from file. \param pFile input, file handle. \param index input, index to read. */ void Read(MP4File* pFile, u_int32_t index = 0) { MP4Free(m_values[index]); m_values[index] = (char*)MP4Calloc(m_fixedLength + 1); (void)pFile->ReadBytes((u_int8_t*)m_values[index], m_fixedLength); }
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 {
void MP4File::MakeIsmaCompliant(bool addIsmaComplianceSdp) { ProtectWriteOperation("MP4MakeIsmaCompliant"); if (m_useIsma) { // already done return; } m_useIsma = true; // find first audio and/or video tracks MP4TrackId audioTrackId = MP4_INVALID_TRACK_ID; try { audioTrackId = FindTrackId(0, MP4_AUDIO_TRACK_TYPE); } catch (MP4Error* e) { delete e; } MP4TrackId videoTrackId = MP4_INVALID_TRACK_ID; try { videoTrackId = FindTrackId(0, MP4_VIDEO_TRACK_TYPE); } catch (MP4Error* e) { delete e; } u_int64_t fileMsDuration = ConvertFromMovieDuration(GetDuration(), MP4_MSECS_TIME_SCALE); // delete any existing OD track if (m_odTrackId != MP4_INVALID_TRACK_ID) { DeleteTrack(m_odTrackId); } AddODTrack(); SetODProfileLevel(0xFF); if (audioTrackId != MP4_INVALID_TRACK_ID) { AddTrackToOd(audioTrackId); } if (videoTrackId != MP4_INVALID_TRACK_ID) { AddTrackToOd(videoTrackId); } // delete any existing scene track MP4TrackId sceneTrackId = MP4_INVALID_TRACK_ID; try { sceneTrackId = FindTrackId(0, MP4_SCENE_TRACK_TYPE); } catch (MP4Error *e) { delete e; } if (sceneTrackId != MP4_INVALID_TRACK_ID) { DeleteTrack(sceneTrackId); } // add scene track sceneTrackId = AddSceneTrack(); SetSceneProfileLevel(0xFF); SetGraphicsProfileLevel(0xFF); SetTrackIntegerProperty(sceneTrackId, "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr.objectTypeId", MP4SystemsV2ObjectType); SetTrackESConfiguration(sceneTrackId, BifsV2Config, sizeof(BifsV2Config)); u_int8_t* pBytes = NULL; u_int64_t numBytes = 0; // write OD Update Command CreateIsmaODUpdateCommandFromFileForFile( m_odTrackId, audioTrackId, videoTrackId, &pBytes, &numBytes); WriteSample(m_odTrackId, pBytes, numBytes, fileMsDuration); MP4Free(pBytes); pBytes = NULL; // write BIFS Scene Replace Command CreateIsmaSceneCommand( MP4_IS_VALID_TRACK_ID(audioTrackId), MP4_IS_VALID_TRACK_ID(videoTrackId), &pBytes, &numBytes); WriteSample(sceneTrackId, pBytes, numBytes, fileMsDuration); MP4Free(pBytes); pBytes = NULL; // add session level sdp CreateIsmaIodFromFile( m_odTrackId, sceneTrackId, audioTrackId, videoTrackId, &pBytes, &numBytes); char* iodBase64 = MP4ToBase64(pBytes, numBytes); char* sdpBuf = (char*)MP4Calloc(strlen(iodBase64) + 256); if (addIsmaComplianceSdp) { strcpy(sdpBuf, "a=isma-compliance:1,1.0,1\015\012"); } sprintf(&sdpBuf[strlen(sdpBuf)], "a=mpeg4-iod: \042data:application/mpeg4-iod;base64,%s\042\015\012", iodBase64); SetSessionSdp(sdpBuf); VERBOSE_ISMA(GetVerbosity(), printf("IOD SDP = %s\n", sdpBuf)); MP4Free(iodBase64); iodBase64 = NULL; MP4Free(pBytes); pBytes = NULL; MP4Free(sdpBuf); sdpBuf = NULL; }
void MP4File::CreateIsmaIodFromParams( u_int8_t videoProfile, u_int32_t videoBitrate, u_int8_t* videoConfig, u_int32_t videoConfigLength, u_int8_t audioProfile, u_int32_t audioBitrate, u_int8_t* audioConfig, u_int32_t audioConfigLength, u_int8_t** ppIodBytes, u_int64_t* pIodNumBytes) { MP4IntegerProperty* pInt; u_int8_t* pBytes = NULL; u_int64_t numBytes; // Create the IOD MP4Descriptor* pIod = new MP4IODescriptor(); pIod->SetTag(MP4IODescrTag); pIod->Generate(); // Set audio and video profileLevels pIod->FindProperty("audioProfileLevelId", (MP4Property**)&pInt); pInt->SetValue(audioProfile); pIod->FindProperty("visualProfileLevelId", (MP4Property**)&pInt); pInt->SetValue(videoProfile); // Mutate esIds from MP4ESIDIncDescrTag to MP4ESDescrTag MP4DescriptorProperty* pEsProperty; pIod->FindProperty("esIds", (MP4Property**)&pEsProperty); pEsProperty->SetTags(MP4ESDescrTag); // Add ES Descriptors // Scene CreateIsmaSceneCommand( (audioProfile != 0xFF), (videoProfile != 0xFF), &pBytes, &numBytes); VERBOSE_ISMA(GetVerbosity(), printf("Scene data =\n"); MP4HexDump(pBytes, numBytes)); char* sceneCmdBase64 = MP4ToBase64(pBytes, numBytes); char* urlBuf = (char*)MP4Malloc(strlen(sceneCmdBase64) + 64); sprintf(urlBuf, "data:application/mpeg4-bifs-au;base64,%s", sceneCmdBase64); VERBOSE_ISMA(GetVerbosity(), printf("Scene data URL = \042%s\042\n", urlBuf)); /* MP4Descriptor* pSceneEsd = */ CreateESD( pEsProperty, 201, // esid MP4SystemsV2ObjectType, MP4SceneDescriptionStreamType, numBytes, // bufferSize numBytes * 8, // bitrate BifsV2Config, sizeof(BifsV2Config), urlBuf); MP4Free(sceneCmdBase64); sceneCmdBase64 = NULL; MP4Free(urlBuf); urlBuf = NULL; MP4Free(pBytes); pBytes = NULL; // OD // Video MP4DescriptorProperty* pVideoEsdProperty = new MP4DescriptorProperty(); pVideoEsdProperty->SetTags(MP4ESDescrTag); /* MP4Descriptor* pVideoEsd = */ CreateESD( pVideoEsdProperty, 20, // esid MP4_MPEG4_VIDEO_TYPE, MP4VisualStreamType, videoBitrate / 8, // bufferSize videoBitrate, videoConfig, videoConfigLength, NULL); // Audio MP4DescriptorProperty* pAudioEsdProperty = new MP4DescriptorProperty(); pAudioEsdProperty->SetTags(MP4ESDescrTag); /* MP4Descriptor* pAudioEsd = */ CreateESD( pAudioEsdProperty, 10, // esid MP4_MPEG4_AUDIO_TYPE, MP4AudioStreamType, audioBitrate / 8, // bufferSize audioBitrate, audioConfig, audioConfigLength, NULL); CreateIsmaODUpdateCommandForStream( pAudioEsdProperty, pVideoEsdProperty, &pBytes, &numBytes); // cleanup temporary descriptor properties delete pAudioEsdProperty; delete pVideoEsdProperty; VERBOSE_ISMA(GetVerbosity(), printf("OD data = %llu bytes\n", numBytes); MP4HexDump(pBytes, numBytes)); char* odCmdBase64 = MP4ToBase64(pBytes, numBytes); urlBuf = (char*)MP4Malloc(strlen(odCmdBase64) + 64); sprintf(urlBuf, "data:application/mpeg4-od-au;base64,%s", odCmdBase64); VERBOSE_ISMA(GetVerbosity(), printf("OD data URL = \042%s\042\n", urlBuf)); /* MP4Descriptor* pOdEsd = */ CreateESD( pEsProperty, 101, MP4SystemsV1ObjectType, MP4ObjectDescriptionStreamType, numBytes, // bufferSize numBytes * 8, // bitrate NULL, // config 0, // configLength urlBuf); MP4Free(odCmdBase64); odCmdBase64 = NULL; MP4Free(pBytes); pBytes = NULL; MP4Free(urlBuf); urlBuf = NULL; // finally get the whole thing written to a memory pIod->WriteToMemory(this, ppIodBytes, pIodNumBytes); delete pIod; VERBOSE_ISMA(GetVerbosity(), printf("IOD data =\n"); MP4HexDump(*ppIodBytes, *pIodNumBytes)); }
void MP4File::CreateIsmaIodFromFile( MP4TrackId odTrackId, MP4TrackId sceneTrackId, MP4TrackId audioTrackId, MP4TrackId videoTrackId, u_int8_t** ppBytes, u_int64_t* pNumBytes) { MP4Descriptor* pIod = new MP4IODescriptor(); pIod->SetTag(MP4IODescrTag); pIod->Generate(); MP4Atom* pIodsAtom = FindAtom("moov.iods"); ASSERT(pIodsAtom); MP4DescriptorProperty* pSrcIod = (MP4DescriptorProperty*)pIodsAtom->GetProperty(2); CloneIntegerProperty(pIod, pSrcIod, "objectDescriptorId"); CloneIntegerProperty(pIod, pSrcIod, "ODProfileLevelId"); CloneIntegerProperty(pIod, pSrcIod, "sceneProfileLevelId"); CloneIntegerProperty(pIod, pSrcIod, "audioProfileLevelId"); CloneIntegerProperty(pIod, pSrcIod, "visualProfileLevelId"); CloneIntegerProperty(pIod, pSrcIod, "graphicsProfileLevelId"); // mutate esIds from MP4ESIDIncDescrTag to MP4ESDescrTag MP4DescriptorProperty* pEsProperty; pIod->FindProperty("esIds", (MP4Property**)&pEsProperty); pEsProperty->SetTags(MP4ESDescrTag); MP4IntegerProperty* pSetProperty; MP4IntegerProperty* pSceneESID; MP4IntegerProperty* pOdESID; // OD MP4Descriptor* pOdEsd = pEsProperty->AddDescriptor(MP4ESDescrTag); pOdEsd->Generate(); pOdEsd->FindProperty("ESID", (MP4Property**)&pOdESID); // we set the OD ESID to a non-zero unique value pOdESID->SetValue(m_odTrackId); pOdEsd->FindProperty("URLFlag", (MP4Property**)&pSetProperty); pSetProperty->SetValue(1); u_int8_t* pBytes; u_int64_t numBytes; CreateIsmaODUpdateCommandFromFileForStream( audioTrackId, videoTrackId, &pBytes, &numBytes); VERBOSE_ISMA(GetVerbosity(), printf("OD data =\n"); MP4HexDump(pBytes, numBytes)); char* odCmdBase64 = MP4ToBase64(pBytes, numBytes); char* urlBuf = (char*)MP4Malloc(strlen(odCmdBase64) + 64); sprintf(urlBuf, "data:application/mpeg4-od-au;base64,%s", odCmdBase64); MP4StringProperty* pUrlProperty; pOdEsd->FindProperty("URL", (MP4Property**)&pUrlProperty); pUrlProperty->SetValue(urlBuf); VERBOSE_ISMA(GetVerbosity(), printf("OD data URL = \042%s\042\n", urlBuf)); MP4Free(odCmdBase64); odCmdBase64 = NULL; MP4Free(pBytes); pBytes = NULL; MP4Free(urlBuf); urlBuf = NULL; MP4DescriptorProperty* pSrcDcd = NULL; // HACK temporarily point to scene decoder config FindProperty(MakeTrackName(odTrackId, "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr"), (MP4Property**)&pSrcDcd); ASSERT(pSrcDcd); MP4Property* pOrgOdEsdProperty = pOdEsd->GetProperty(8); pOdEsd->SetProperty(8, pSrcDcd); // bufferSizeDB needs to be set appropriately MP4BitfieldProperty* pBufferSizeProperty = NULL; pOdEsd->FindProperty("decConfigDescr.bufferSizeDB", (MP4Property**)&pBufferSizeProperty); ASSERT(pBufferSizeProperty); pBufferSizeProperty->SetValue(numBytes); // SL config needs to change from 2 (file) to 1 (null) pOdEsd->FindProperty("slConfigDescr.predefined", (MP4Property**)&pSetProperty); pSetProperty->SetValue(1); // Scene MP4Descriptor* pSceneEsd = pEsProperty->AddDescriptor(MP4ESDescrTag); pSceneEsd->Generate(); pSceneEsd->FindProperty("ESID", (MP4Property**)&pSceneESID); // we set the Scene ESID to a non-zero unique value pSceneESID->SetValue(sceneTrackId); pSceneEsd->FindProperty("URLFlag", (MP4Property**)&pSetProperty); pSetProperty->SetValue(1); CreateIsmaSceneCommand( MP4_IS_VALID_TRACK_ID(audioTrackId), MP4_IS_VALID_TRACK_ID(videoTrackId), &pBytes, &numBytes); VERBOSE_ISMA(GetVerbosity(), printf("Scene data =\n"); MP4HexDump(pBytes, numBytes)); char *sceneCmdBase64 = MP4ToBase64(pBytes, numBytes); urlBuf = (char*)MP4Malloc(strlen(sceneCmdBase64) + 64); sprintf(urlBuf, "data:application/mpeg4-bifs-au;base64,%s", sceneCmdBase64); pSceneEsd->FindProperty("URL", (MP4Property**)&pUrlProperty); pUrlProperty->SetValue(urlBuf); VERBOSE_ISMA(GetVerbosity(), printf("Scene data URL = \042%s\042\n", urlBuf)); MP4Free(sceneCmdBase64); sceneCmdBase64 = NULL; MP4Free(urlBuf); urlBuf = NULL; MP4Free(pBytes); pBytes = NULL; // HACK temporarily point to scene decoder config FindProperty(MakeTrackName(sceneTrackId, "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr"), (MP4Property**)&pSrcDcd); ASSERT(pSrcDcd); MP4Property* pOrgSceneEsdProperty = pSceneEsd->GetProperty(8); pSceneEsd->SetProperty(8, pSrcDcd); // bufferSizeDB needs to be set pBufferSizeProperty = NULL; pSceneEsd->FindProperty("decConfigDescr.bufferSizeDB", (MP4Property**)&pBufferSizeProperty); ASSERT(pBufferSizeProperty); pBufferSizeProperty->SetValue(numBytes); // SL config needs to change from 2 (file) to 1 (null) pSceneEsd->FindProperty("slConfigDescr.predefined", (MP4Property**)&pSetProperty); pSetProperty->SetValue(1); // finally get the whole thing written to a memory pIod->WriteToMemory(this, ppBytes, pNumBytes); // now carefully replace esd properties before destroying pOdEsd->SetProperty(8, pOrgOdEsdProperty); pSceneEsd->SetProperty(8, pOrgSceneEsdProperty); pSceneESID->SetValue(0); // restore 0 value pOdESID->SetValue(0); delete pIod; VERBOSE_ISMA(GetVerbosity(), printf("IOD data =\n"); MP4HexDump(*ppBytes, *pNumBytes)); }
// // Clone - clone my properties to destination atom // // this method simplifies duplicating avcC atom properties from // source to destination file using a single API rather than // having to copy each property. This API encapsulates the object // so the application layer need not concern with each property // thereby isolating any future changes to atom properties. // // ---------------------------------------- // property description // ---------------------------------------- // // 0 configurationVersion // 1 AVCProfileIndication // 2 profile_compatibility // 3 AVCLevelIndication // 4 reserved // 5 lengthSizeMinusOne // 6 reserved // 7 number of SPS // 8 SPS entries // 9 number of PPS // 10 PPS entries // // void MP4HvcCAtom::Clone(MP4HvcCAtom *dstAtom) { #if 1 //cwm MP4Property *dstProperty; MP4TableProperty *pTable; uint16_t i16; uint64_t i32; uint64_t i64; uint8_t *tmp; // source pointer Property I16 MP4Integer16Property *spPI16; // source pointer Property Bytes MP4BytesProperty *spPB; // dest pointer Property I16 MP4Integer16Property *dpPI16; // dest pointer Property Bytes MP4BytesProperty *dpPB; // start with defaults and reserved fields dstAtom->Generate(); // 0, 4, 6 are now generated from defaults // leaving 1, 2, 3, 5, 7, 8, 9, 10 to export dstProperty=dstAtom->GetProperty(1); ((MP4Integer8Property *)dstProperty)->SetValue( 0x12);//((MP4Integer8Property *)m_pProperties[1])->GetValue() dstProperty=dstAtom->GetProperty(2); ((MP4Integer8Property *)dstProperty)->SetValue( 0x34);//((MP4Integer8Property *)m_pProperties[2])->GetValue() dstProperty=dstAtom->GetProperty(3); ((MP4Integer8Property *)dstProperty)->SetValue( ((MP4Integer8Property *)m_pProperties[3])->GetValue()); dstProperty=dstAtom->GetProperty(5); ((MP4BitfieldProperty *)dstProperty)->SetValue( ((MP4BitfieldProperty *)m_pProperties[5])->GetValue()); // // 7 and 8 are related SPS (one set of sequence parameters) // // first the count bitfield // dstProperty=dstAtom->GetProperty(7); dstProperty->SetReadOnly(false); ((MP4BitfieldProperty *)dstProperty)->SetValue( ((MP4BitfieldProperty *)m_pProperties[7])->GetValue()); dstProperty->SetReadOnly(true); // next export SPS Length and NAL bytes */ // first source pointers pTable = (MP4TableProperty *) m_pProperties[8]; spPI16 = (MP4Integer16Property *)pTable->GetProperty(0); spPB = (MP4BytesProperty *)pTable->GetProperty(1); // now dest pointers dstProperty=dstAtom->GetProperty(8); pTable = (MP4TableProperty *) dstProperty; dpPI16 = (MP4Integer16Property *)pTable->GetProperty(0); dpPB = (MP4BytesProperty *)pTable->GetProperty(1); // sps length i16 = spPI16->GetValue(); i64 = i16; // FIXME - this leaves m_maxNumElements =2 // but src atom m_maxNumElements is 1 dpPI16->InsertValue(i64, 0); // export byte array i32 = i16; // copy bytes to local buffer tmp = (uint8_t *)MP4Malloc(i32); ASSERT(tmp != NULL); spPB->CopyValue(tmp, 0); // set element count dpPB->SetCount(1); // copy bytes dpPB->SetValue(tmp, i32, 0); MP4Free((void *)tmp); // // 9 and 10 are related PPS (one set of picture parameters) // // first the integer8 count // dstProperty=dstAtom->GetProperty(9); dstProperty->SetReadOnly(false); ((MP4Integer8Property *)dstProperty)->SetValue( ((MP4Integer8Property *)m_pProperties[9])->GetValue()); dstProperty->SetReadOnly(true); // next export PPS Length and NAL bytes */ // first source pointers pTable = (MP4TableProperty *) m_pProperties[10]; spPI16 = (MP4Integer16Property *)pTable->GetProperty(0); spPB = (MP4BytesProperty *)pTable->GetProperty(1); // now dest pointers dstProperty=dstAtom->GetProperty(10); pTable = (MP4TableProperty *) dstProperty; dpPI16 = (MP4Integer16Property *)pTable->GetProperty(0); dpPB = (MP4BytesProperty *)pTable->GetProperty(1); #if 1//cwm 500 // pps length i16 = spPI16->GetValue(); i64 = i16; dpPI16->InsertValue(i64, 0); // export byte array i32 = i16; // copy bytes to local buffer tmp = (uint8_t *)MP4Malloc(i32); ASSERT(tmp != NULL); spPB->CopyValue(tmp, 0); // set element count dpPB->SetCount(1); // copy bytes dpPB->SetValue(tmp, i32, 0); MP4Free((void *)tmp); #endif //cwm 500 #endif //cwm }
ALACSource::ALACSource(const std::shared_ptr<FILE> &fp) : m_position(0), m_fp(fp) { try { int fd = fileno(m_fp.get()); { util::FilePositionSaver _(fd); _lseeki64(fd, 0, SEEK_SET); char buf[8]; if (read(fd, buf, 8) != 8 || std::memcmp(&buf[4], "ftyp", 4)) throw std::runtime_error("Not an MP4 file"); } static MP4FDReadProvider provider; std::string name = strutil::format("%d", fd); m_file.Read(name.c_str(), &provider); m_track_id = m_file.FindTrackId(0, MP4_AUDIO_TRACK_TYPE); const char *type = m_file.GetTrackMediaDataName(m_track_id); if (std::strcmp(type, "alac")) throw std::runtime_error("Not an ALAC file"); const char *alacprop, *chanprop; const char *brand = m_file.GetStringProperty("ftyp.majorBrand"); if (!std::strcmp(brand, "qt ")) { // throw std::runtime_error("Not supported format"); alacprop = "mdia.minf.stbl.stsd.alac.wave.alac.decoderConfig"; chanprop = "mdia.minf.stbl.stsd.alac.wave.chan.data"; } else { alacprop = "mdia.minf.stbl.stsd.alac.alac.decoderConfig"; chanprop = "mdia.minf.stbl.stsd.alac.chan.data"; } std::vector<uint8_t> alac, chan; uint8_t *value; uint32_t size; m_file.GetTrackBytesProperty(m_track_id, alacprop, &value, &size); std::copy(value + 4, value + size, std::back_inserter(alac)); MP4Free(value); value = 0; try { m_file.GetTrackBytesProperty(m_track_id, chanprop, &value, &size); std::copy(value + 4, value + size, std::back_inserter(chan)); MP4Free(value); } catch (...) {} if (alac.size() != 24 || (chan.size() && chan.size() < 12)) throw std::runtime_error("ALACSource: invalid magic cookie"); uint32_t timeScale; std::memcpy(&timeScale, &alac[20], 4); timeScale = util::b2host32(timeScale); m_asbd = cautil::buildASBDForPCM(timeScale, alac[9], alac[5], kAudioFormatFlagIsSignedInteger, kAudioFormatFlagIsAlignedHigh); m_oasbd = cautil::buildASBDForPCM2(timeScale, alac[9], alac[5], 32, kAudioFormatFlagIsSignedInteger); m_buffer.units_per_packet = m_asbd.mBytesPerFrame; AudioChannelLayout acl = { 0 }; if (chan.size()) { util::fourcc tag(reinterpret_cast<const char*>(&chan[0])); util::fourcc bitmap(reinterpret_cast<const char*>(&chan[4])); acl.mChannelLayoutTag = tag; acl.mChannelBitmap = bitmap; chanmap::getChannels(&acl, &m_chanmap); } m_decoder = std::shared_ptr<ALACDecoder>(new ALACDecoder()); CHECKCA(m_decoder->Init(&alac[0], alac.size())); m_length = m_file.GetTrackDuration(m_track_id); mp4a::fetchTags(m_file, &m_tags); } catch (mp4v2::impl::Exception *e) { handle_mp4error(e); } }
ALACSource::ALACSource(const std::wstring &path) : m_position(0) { try { m_file.Read(w2m(path, utf8_codecvt_facet()).c_str(), 0); m_track_id = m_file.FindTrackId(0, MP4_AUDIO_TRACK_TYPE); const char *type = m_file.GetTrackMediaDataName(m_track_id); if (std::strcmp(type, "alac")) throw std::runtime_error("Not an ALAC file"); const char *alacprop, *chanprop; const char *brand = m_file.GetStringProperty("ftyp.majorBrand"); if (!std::strcmp(brand, "qt ")) { // throw std::runtime_error("Not supported format"); alacprop = "mdia.minf.stbl.stsd.alac.wave.alac.decoderConfig"; chanprop = "mdia.minf.stbl.stsd.alac.wave.chan.data"; } else { alacprop = "mdia.minf.stbl.stsd.alac.alac.decoderConfig"; chanprop = "mdia.minf.stbl.stsd.alac.chan.data"; } std::vector<uint8_t> alac, chan; uint8_t *value; uint32_t size; m_file.GetTrackBytesProperty(m_track_id, alacprop, &value, &size); std::copy(value + 4, value + size, std::back_inserter(alac)); MP4Free(value); value = 0; try { m_file.GetTrackBytesProperty(m_track_id, chanprop, &value, &size); std::copy(value + 4, value + size, std::back_inserter(chan)); MP4Free(value); } catch (...) {} if (alac.size() != 24 || (chan.size() && chan.size() < 12)) throw std::runtime_error("ALACSource: invalid magic cookie"); std::memset(&m_format, 0, sizeof m_format); m_format.m_type = SampleFormat::kIsSignedInteger; m_format.m_endian = SampleFormat::kIsLittleEndian; m_format.m_nchannels = alac[9]; m_format.m_bitsPerSample = alac[5]; if (m_format.m_bitsPerSample == 20) m_format.m_bitsPerSample = 24; uint32_t timeScale; std::memcpy(&timeScale, &alac[20], 4); timeScale = b2host32(timeScale); m_format.m_rate = timeScale; AudioChannelLayout acl = { 0 }; if (chan.size()) { fourcc tag(reinterpret_cast<const char*>(&chan[0])); fourcc bitmap(reinterpret_cast<const char*>(&chan[4])); acl.mChannelLayoutTag = tag; acl.mChannelBitmap = bitmap; chanmap::GetChannels(&acl, &m_chanmap); } m_decoder = x::shared_ptr<ALACDecoder>(new ALACDecoder()); CHECKCA(m_decoder->Init(&alac[0], alac.size())); setRange(0, m_file.GetTrackDuration(m_track_id)); mp4a::fetchTags(m_file, &m_tags); } catch (mp4v2::impl::Exception *e) { handle_mp4error(e); } }
void MP4RtpHintTrack::ReadPacket( u_int16_t packetIndex, u_int8_t** ppBytes, u_int32_t* pNumBytes, u_int32_t ssrc, bool addHeader, bool addPayload) { if (m_pReadHint == NULL) { throw new MP4Error("no hint has been read", "MP4ReadRtpPacket"); } if (!addHeader && !addPayload) { throw new MP4Error("no data requested", "MP4ReadRtpPacket"); } MP4RtpPacket* pPacket = m_pReadHint->GetPacket(packetIndex); *pNumBytes = 0; if (addHeader) { *pNumBytes += 12; } if (addPayload) { *pNumBytes += pPacket->GetDataSize(); } // if needed, allocate the packet memory bool buffer_malloc = false; if (*ppBytes == NULL) { *ppBytes = (u_int8_t*)MP4Malloc(*pNumBytes); buffer_malloc = true; } try { u_int8_t* pDest = *ppBytes; if (addHeader) { *pDest++ = 0x80 | (pPacket->GetPBit() << 5) | (pPacket->GetXBit() << 4); *pDest++ = (pPacket->GetMBit() << 7) | pPacket->GetPayload(); *((u_int16_t*)pDest) = htons(m_rtpSequenceStart + pPacket->GetSequenceNumber()); pDest += 2; *((u_int32_t*)pDest) = htonl(m_rtpTimestampStart + (u_int32_t)m_readHintTimestamp); pDest += 4; *((u_int32_t*)pDest) = htonl(ssrc); pDest += 4; } if (addPayload) { pPacket->GetData(pDest); } } catch (MP4Error* e) { if (buffer_malloc) { MP4Free(*ppBytes); *ppBytes = NULL; } throw e; } VERBOSE_READ_HINT(m_pFile->GetVerbosity(), printf("ReadPacket: %u ", packetIndex); MP4HexDump(*ppBytes, *pNumBytes););
void MP4Track::ReadSample( MP4SampleId sampleId, u_int8_t** ppBytes, u_int32_t* pNumBytes, MP4Timestamp* pStartTime, MP4Duration* pDuration, MP4Duration* pRenderingOffset, bool* pIsSyncSample) { if (sampleId == MP4_INVALID_SAMPLE_ID) { throw new MP4Error("sample id can't be zero", "MP4Track::ReadSample"); } // handle unusual case of wanting to read a sample // that is still sitting in the write chunk buffer if (m_pChunkBuffer && sampleId >= m_writeSampleId - m_chunkSamples) { WriteChunkBuffer(); } FILE* pFile = GetSampleFile(sampleId); if (pFile == (FILE*)-1) { throw new MP4Error("sample is located in an inaccessible file", "MP4Track::ReadSample"); } u_int64_t fileOffset = GetSampleFileOffset(sampleId); u_int32_t sampleSize = GetSampleSize(sampleId); if (*ppBytes != NULL && *pNumBytes < sampleSize) { throw new MP4Error("sample buffer is too small", "MP4Track::ReadSample"); } *pNumBytes = sampleSize; VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(), printf("ReadSample: track %u id %u offset 0x"LLX" size %u (0x%x)\n", m_trackId, sampleId, fileOffset, *pNumBytes, *pNumBytes)); bool bufferMalloc = false; if (*ppBytes == NULL) { *ppBytes = (u_int8_t*)MP4Malloc(*pNumBytes); bufferMalloc = true; } u_int64_t oldPos = m_pFile->GetPosition(pFile); // only used in mode == 'w' try { m_pFile->SetPosition(fileOffset, pFile); m_pFile->ReadBytes(*ppBytes, *pNumBytes, pFile); if (pStartTime || pDuration) { GetSampleTimes(sampleId, pStartTime, pDuration); VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(), printf("ReadSample: start "LLU" duration "LLD"\n", (pStartTime ? *pStartTime : 0), (pDuration ? *pDuration : 0))); } if (pRenderingOffset) { *pRenderingOffset = GetSampleRenderingOffset(sampleId); VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(), printf("ReadSample: renderingOffset "LLD"\n", *pRenderingOffset)); } if (pIsSyncSample) { *pIsSyncSample = IsSyncSample(sampleId); VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(), printf("ReadSample: isSyncSample %u\n", *pIsSyncSample)); } } catch (MP4Error* e) { if (bufferMalloc) { // let's not leak memory MP4Free(*ppBytes); *ppBytes = NULL; } if (m_pFile->GetMode() == 'w') { m_pFile->SetPosition(oldPos, pFile); } throw e; } if (m_pFile->GetMode() == 'w') { m_pFile->SetPosition(oldPos, pFile); } }
/** Action for exporting chapters from the <b>job.file</b> * * * @param job the job to process * @return mp4v2::util::SUCCESS if successful, mp4v2::util::FAILURE otherwise */ bool ChapterUtility::actionExport( JobContext& job ) { job.fileHandle = MP4Read( job.file.c_str() ); if( job.fileHandle == MP4_INVALID_FILE_HANDLE ) { return herrf( "unable to open for read: %s\n", job.file.c_str() ); } // get the list of chapters MP4Chapter_t* chapters = 0; uint32_t chapterCount = 0; MP4ChapterType chtp = MP4GetChapters( job.fileHandle, &chapters, &chapterCount, _ChapterType ); if (0 == chapterCount) { return herrf( "File \"%s\" does not contain chapters of type %s\n", job.file.c_str(), getChapterTypeName( chtp ).c_str() ); } // build the filename string outName = job.file; if( _ChapterFile.empty() ) { FileSystem::pathnameStripExtension( outName ); outName.append( ".chapters.txt" ); } else { outName = _ChapterFile; } ostringstream oss; oss << "Exporting " << chapterCount << " " << getChapterTypeName( chtp ); oss << " chapters from file " << '"' << job.file << '"' << " into chapter file " << '"' << outName << '"' << endl; verbose1f( "%s", oss.str().c_str() ); if( dryrunAbort() ) { // free up the memory MP4Free(chapters); return SUCCESS; } // open the file File out( outName, File::MODE_CREATE ); if( openFileForWriting( out ) ) { // free up the memory MP4Free(chapters); return FAILURE; } // write the chapters #if defined( _WIN32 ) static const char* LINEND = "\r\n"; #else static const char* LINEND = "\n"; #endif File::Size nout; bool failure = SUCCESS; int width = 2; if( CHPT_FMT_COMMON == _ChapterFormat && (chapterCount / 100) >= 1 ) { width = 3; } Timecode duration( 0, CHAPTERTIMESCALE ); duration.setFormat( Timecode::DECIMAL ); for( uint32_t i = 0; i < chapterCount; ++i ) { // print the infos ostringstream oss; switch( _ChapterFormat ) { case CHPT_FMT_COMMON: oss << "CHAPTER" << setw( width ) << setfill( '0' ) << i+1 << '=' << duration.svalue << LINEND << "CHAPTER" << setw( width ) << setfill( '0' ) << i+1 << "NAME=" << chapters[i].title << LINEND; break; case CHPT_FMT_NATIVE: default: oss << duration.svalue << ' ' << chapters[i].title << LINEND; } string str = oss.str(); if( out.write( str.c_str(), str.size(), nout ) ) { failure = herrf( "write to %s failed: %s\n", outName.c_str(), sys::getLastErrorStr() ); break; } // add the duration of this chapter to the sum (the start time of the next chapter) duration += Timecode(chapters[i].duration, CHAPTERTIMESCALE); } out.close(); if( failure ) { verbose1f( "removing file %s\n", outName.c_str() ); ::remove( outName.c_str() ); } // free up the memory MP4Free(chapters); return SUCCESS; }
/** * Log a buffer as ascii-hex * * @param indent the number of spaces to indent the buffer * * @param verbosity the level of detail the message contains * * @param pBytes the buffer to log * * @param numBytes the number of bytes to log * * @param format the format string to use to process the * remaining arguments, where the format + remaining args * describe @p pBytes. The resulting string should not * contain a newline. Only the first 255 characters of the * resulting string (not including the NUL terminator) make * it to the log callback or stdout. */ void Log::hexDump( uint8_t indent, MP4LogLevel verbosity_, const uint8_t* pBytes, uint32_t numBytes, const char* format, ... ) { va_list ap; ASSERT(pBytes || (numBytes == 0)); ASSERT(format); if (verbosity_ > this->_verbosity) { // We're not set verbose enough to log this return; } // Build the description by processing format and the // remaining args. Since we don't have asprintf, pick // an arbitrary length for the string and use snprintf. // To save a memory allocation, only do this if there's // a non-empty format string or non-zero indent char *desc = NULL; if (format[0] || indent) { desc = (char *)MP4Calloc(256 + indent); sprintf(desc,"%*c",indent,' '); va_start(ap,format); vsnprintf(desc + indent,255,format,ap); va_end(ap); } // From here we can use the C++ standard lib classes and // build a string for each line for (uint32_t i = 0;(i < numBytes);i += 16) { // ios_base::ate means at end. With out this desc // gets overwritten with each << operation ostringstream oneLine(desc ? desc : "",ios_base::ate); // Append the byte offset this line starts with as // an 8 character, leading 0, hex number. Leave the // fill character set to 0 for the remaining // operations oneLine << ':' << hex << setw(8) << setfill('0') << std::right << i << setw(0) << setfill(' ') << ": "; uint32_t curlen = min((uint32_t)16,numBytes - i); const uint8_t *b = pBytes + i; uint32_t j; for (j = 0;(j < curlen);j++) { oneLine << hex << setw(2) << setfill('0') << right << static_cast<uint32_t>(b[j]); oneLine << setw(0) << setfill(' ') << ' '; } for (; j < 16; j++) { oneLine << " "; } b = pBytes + i; for (j = 0;(j < curlen);j++) { if (isprint(static_cast<int>(b[j]))) { oneLine << static_cast<char>(b[j]); } else { oneLine << '.'; } } // We can either call the callback directly or use // the Log::printf function. To call the callback // directly, we need a va_list. (I think) we need // and extra function call to build that, so we may // as well call Log::printf. It's going to // double-check the verbosity and the callback // function pointer, but that seems OK (13-feb-09, // dbyron) this->printf(verbosity_,"%s",oneLine.str().c_str()); } if (desc) { MP4Free(desc); desc = NULL; } }