| /* |
| * The contents of this file are subject to the Mozilla Public |
| * License Version 1.1 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of |
| * the License at http://www.mozilla.org/MPL/ |
| * |
| * Software distributed under the License is distributed on an "AS |
| * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or |
| * implied. See the License for the specific language governing |
| * rights and limitations under the License. |
| * |
| * The Original Code is MPEG4IP. |
| * |
| * The Initial Developer of the Original Code is Cisco Systems Inc. |
| * Portions created by Cisco Systems Inc. are |
| * Copyright (C) Cisco Systems Inc. 2001 - 2004. All Rights Reserved. |
| * |
| * 3GPP features implementation is based on 3GPP's TS26.234-v5.60, |
| * and was contributed by Ximpo Group Ltd. |
| * |
| * Portions created by Ximpo Group Ltd. are |
| * Copyright (C) Ximpo Group Ltd. 2003, 2004. All Rights Reserved. |
| * |
| * Contributor(s): |
| * Dave Mackie dmackie@cisco.com |
| * Alix Marchandise-Franquet alix@cisco.com |
| * Ximpo Group Ltd. mp4v2@ximpo.com |
| */ |
| |
| #include "src/impl.h" |
| |
| namespace mp4v2 { |
| namespace impl { |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| static const uint8_t BifsV2Config[3] = { |
| 0x00, 0x00, 0x60 // IsCommandStream = 1, PixelMetric = 1 |
| }; |
| |
| void MP4File::MakeIsmaCompliant(bool addIsmaComplianceSdp) |
| { |
| ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__); |
| |
| if (m_useIsma) { |
| // already done |
| return; |
| } |
| |
| // find first audio and/or video tracks |
| |
| MP4TrackId audioTrackId = MP4_INVALID_TRACK_ID; |
| try { |
| audioTrackId = FindTrackId(0, MP4_AUDIO_TRACK_TYPE); |
| } |
| catch ( Exception *x ) { |
| log.errorf(*x); |
| delete x; |
| } |
| MP4TrackId videoTrackId = MP4_INVALID_TRACK_ID; |
| try { |
| videoTrackId = FindTrackId(0, MP4_VIDEO_TRACK_TYPE); |
| } |
| catch (Exception* x) { |
| log.errorf(*x); |
| delete x; |
| } |
| if (audioTrackId == MP4_INVALID_TRACK_ID && |
| videoTrackId == MP4_INVALID_TRACK_ID) return; |
| |
| const char *audio_media_data_name, *video_media_data_name; |
| uint8_t videoProfile = 0xff; |
| if (audioTrackId != MP4_INVALID_TRACK_ID) { |
| audio_media_data_name = MP4GetTrackMediaDataName(this, audioTrackId); |
| if (!(ATOMID(audio_media_data_name) == ATOMID("mp4a") || |
| ATOMID(audio_media_data_name) == ATOMID("enca"))) { |
| log.errorf("%s: \"%s\": can't make ISMA compliant when file contains an %s track", |
| __FUNCTION__, GetFilename().c_str(), audio_media_data_name); |
| return; |
| } |
| } |
| // |
| // Note - might have to check for avc1 here... |
| if (videoTrackId != MP4_INVALID_TRACK_ID) { |
| video_media_data_name = MP4GetTrackMediaDataName(this, videoTrackId); |
| if (!(ATOMID(video_media_data_name) == ATOMID("mp4v") || |
| ATOMID(video_media_data_name) == ATOMID("encv"))) { |
| log.errorf("%s: \"%s\": can't make ISMA compliant when file contains an %s track", __FUNCTION__, |
| GetFilename().c_str(), video_media_data_name); |
| return; |
| } |
| MP4LogLevel verb = log.verbosity; |
| log.setVerbosity(MP4_LOG_NONE); |
| videoProfile = MP4GetVideoProfileLevel(this, videoTrackId); |
| log.setVerbosity(verb); |
| } |
| |
| m_useIsma = true; |
| |
| uint64_t fileMsDuration = 0; |
| fileMsDuration = |
| ConvertFromMovieDuration(GetDuration(), MP4_MSECS_TIME_SCALE); |
| |
| // delete any existing OD track |
| if (m_odTrackId != MP4_INVALID_TRACK_ID) { |
| DeleteTrack(m_odTrackId); |
| } |
| |
| if (m_pRootAtom->FindAtom("moov.iods") == NULL) { |
| (void)AddChildAtom("moov", "iods"); |
| } |
| (void)AddODTrack(); |
| SetODProfileLevel(0xFF); |
| |
| if (audioTrackId != MP4_INVALID_TRACK_ID) { |
| AddTrackToOd(audioTrackId); |
| MP4SetAudioProfileLevel(this, 0xf); |
| } |
| if (videoTrackId != MP4_INVALID_TRACK_ID) { |
| AddTrackToOd(videoTrackId); |
| MP4SetVideoProfileLevel(this, videoProfile); |
| } |
| |
| // delete any existing scene track |
| MP4TrackId sceneTrackId = MP4_INVALID_TRACK_ID; |
| try { |
| sceneTrackId = FindTrackId(0, MP4_SCENE_TRACK_TYPE); |
| } |
| catch (Exception *x) { |
| log.errorf(*x); |
| delete x; |
| } |
| 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)); |
| |
| uint8_t* pBytes = NULL; |
| uint64_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); |
| |
| uint32_t sdpBufLen = (uint32_t)strlen(iodBase64) + 256; |
| uint32_t used; |
| char* sdpBuf = (char*)MP4Calloc(sdpBufLen); |
| |
| if (addIsmaComplianceSdp) { |
| strncpy(sdpBuf, "a=isma-compliance:1,1.0,1\015\012", sdpBufLen); |
| } |
| |
| used = (uint32_t)strlen(sdpBuf); |
| sdpBufLen -= used; |
| snprintf(&sdpBuf[used], sdpBufLen, |
| "a=mpeg4-iod: \042data:application/mpeg4-iod;base64,%s\042\015\012", |
| iodBase64); |
| |
| SetSessionSdp(sdpBuf); |
| |
| log.verbose1f("\"%s\": IOD SDP = %s", GetFilename().c_str(), sdpBuf); |
| |
| MP4Free(iodBase64); |
| iodBase64 = NULL; |
| MP4Free(pBytes); |
| pBytes = NULL; |
| MP4Free(sdpBuf); |
| sdpBuf = NULL; |
| } |
| |
| static void CloneIntegerProperty( |
| MP4Descriptor* pDest, |
| MP4DescriptorProperty* pSrc, |
| const char* name) |
| { |
| MP4IntegerProperty* pGetProperty; |
| MP4IntegerProperty* pSetProperty; |
| |
| if (!pSrc->FindProperty(name, (MP4Property**)&pGetProperty)) return; |
| if (!pDest->FindProperty(name, (MP4Property**)&pSetProperty)) return; |
| pSetProperty->SetValue(pGetProperty->GetValue()); |
| } |
| |
| void MP4File::CreateIsmaIodFromFile( |
| MP4TrackId odTrackId, |
| MP4TrackId sceneTrackId, |
| MP4TrackId audioTrackId, |
| MP4TrackId videoTrackId, |
| uint8_t** ppBytes, |
| uint64_t* pNumBytes) |
| { |
| MP4Atom* pIodsAtom = FindAtom("moov.iods"); |
| ASSERT(pIodsAtom); |
| MP4DescriptorProperty* pSrcIod = |
| (MP4DescriptorProperty*)pIodsAtom->GetProperty(2); |
| |
| MP4Descriptor* pIod = new MP4IODescriptor(*pIodsAtom); |
| pIod->SetTag(MP4IODescrTag); |
| pIod->Generate(); |
| |
| 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; |
| if (!pIod->FindProperty("esIds", (MP4Property**)&pEsProperty)) return; |
| pEsProperty->SetTags(MP4ESDescrTag); |
| |
| MP4IntegerProperty* pSetProperty; |
| MP4IntegerProperty* pSceneESID; |
| MP4IntegerProperty* pOdESID; |
| |
| // OD |
| MP4Descriptor* pOdEsd = |
| pEsProperty->AddDescriptor(MP4ESDescrTag); |
| pOdEsd->Generate(); |
| |
| if (!pOdEsd->FindProperty("ESID", (MP4Property**)&pOdESID)) return; |
| |
| // we set the OD ESID to a non-zero unique value |
| pOdESID->SetValue(m_odTrackId); |
| |
| if (pOdEsd->FindProperty("URLFlag", |
| (MP4Property**)&pSetProperty)) |
| pSetProperty->SetValue(1); |
| |
| uint8_t* pBytes; |
| uint64_t numBytes; |
| |
| CreateIsmaODUpdateCommandFromFileForStream( |
| audioTrackId, |
| videoTrackId, |
| &pBytes, |
| &numBytes); |
| |
| log.hexDump(0, MP4_LOG_VERBOSE1, pBytes, numBytes, "\"%s\": OD data", |
| GetFilename().c_str() ); |
| |
| char* odCmdBase64 = MP4ToBase64(pBytes, numBytes); |
| |
| uint32_t urlBufLen = (uint32_t)strlen(odCmdBase64) + 64; |
| char* urlBuf = (char*)MP4Malloc(urlBufLen); |
| |
| snprintf(urlBuf, urlBufLen, |
| "data:application/mpeg4-od-au;base64,%s", |
| odCmdBase64); |
| |
| MP4StringProperty* pUrlProperty; |
| if (pOdEsd->FindProperty("URL", |
| (MP4Property**)&pUrlProperty)) |
| pUrlProperty->SetValue(urlBuf); |
| |
| log.verbose1f("\"%s\": OD data URL = \042%s\042", GetFilename().c_str(), |
| urlBuf); |
| |
| MP4Free(odCmdBase64); |
| odCmdBase64 = NULL; |
| MP4Free(pBytes); |
| pBytes = NULL; |
| MP4Free(urlBuf); |
| urlBuf = NULL; |
| |
| MP4DescriptorProperty* pSrcDcd = NULL; |
| |
| // HACK temporarily point to scene decoder config |
| (void)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; |
| if (pOdEsd->FindProperty("decConfigDescr.bufferSizeDB", |
| (MP4Property**)&pBufferSizeProperty)) { |
| ASSERT(pBufferSizeProperty); |
| pBufferSizeProperty->SetValue(numBytes); |
| } |
| |
| // SL config needs to change from 2 (file) to 1 (null) |
| if (pOdEsd->FindProperty("slConfigDescr.predefined", |
| (MP4Property**)&pSetProperty)) |
| pSetProperty->SetValue(1); |
| |
| |
| // Scene |
| MP4Descriptor* pSceneEsd = |
| pEsProperty->AddDescriptor(MP4ESDescrTag); |
| pSceneEsd->Generate(); |
| |
| if (pSceneEsd->FindProperty("ESID", |
| (MP4Property**)&pSceneESID)) { |
| // we set the Scene ESID to a non-zero unique value |
| pSceneESID->SetValue(sceneTrackId); |
| } |
| |
| if (pSceneEsd->FindProperty("URLFlag", |
| (MP4Property**)&pSetProperty)) |
| pSetProperty->SetValue(1); |
| |
| CreateIsmaSceneCommand( |
| MP4_IS_VALID_TRACK_ID(audioTrackId), |
| MP4_IS_VALID_TRACK_ID(videoTrackId), |
| &pBytes, |
| &numBytes); |
| |
| log.hexDump(0, MP4_LOG_VERBOSE1, pBytes, numBytes, "\"%s\": Scene data", |
| GetFilename().c_str() ); |
| |
| char *sceneCmdBase64 = MP4ToBase64(pBytes, numBytes); |
| |
| urlBuf = (char*)MP4Malloc(strlen(sceneCmdBase64) + 64); |
| snprintf(urlBuf, strlen(sceneCmdBase64) + 64, |
| "data:application/mpeg4-bifs-au;base64,%s", |
| sceneCmdBase64); |
| |
| if (pSceneEsd->FindProperty("URL", |
| (MP4Property**)&pUrlProperty)) |
| pUrlProperty->SetValue(urlBuf); |
| |
| log.verbose1f("\"%s\": Scene data URL = \042%s\042", |
| GetFilename().c_str(), urlBuf); |
| |
| MP4Free(sceneCmdBase64); |
| sceneCmdBase64 = NULL; |
| MP4Free(urlBuf); |
| urlBuf = NULL; |
| MP4Free(pBytes); |
| pBytes = NULL; |
| |
| // HACK temporarily point to scene decoder config |
| ASSERT(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; |
| if (pSceneEsd->FindProperty("decConfigDescr.bufferSizeDB", |
| (MP4Property**)&pBufferSizeProperty)) { |
| ASSERT(pBufferSizeProperty); |
| pBufferSizeProperty->SetValue(numBytes); |
| } |
| |
| // SL config needs to change from 2 (file) to 1 (null) |
| if (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; |
| |
| log.hexDump(0, MP4_LOG_VERBOSE1, *ppBytes, *pNumBytes, "\"%s\": IOD data", |
| GetFilename().c_str() ); |
| } |
| |
| void MP4File::CreateIsmaIodFromParams( |
| uint8_t videoProfile, |
| uint32_t videoBitrate, |
| uint8_t* videoConfig, |
| uint32_t videoConfigLength, |
| uint8_t audioProfile, |
| uint32_t audioBitrate, |
| uint8_t* audioConfig, |
| uint32_t audioConfigLength, |
| uint8_t** ppIodBytes, |
| uint64_t* pIodNumBytes) |
| { |
| MP4IntegerProperty* pInt; |
| uint8_t* pBytes = NULL; |
| uint64_t numBytes; |
| |
| // Descriptor constructors need a parent atom. In this |
| // case we don't have one so create a dummy one. This |
| // is OK as we only build the descriptor and then dump |
| // its contents to a buffer |
| MP4Atom dummyParent(*this); |
| |
| // Create the IOD |
| MP4Descriptor* pIod = new MP4IODescriptor(dummyParent); |
| pIod->SetTag(MP4IODescrTag); |
| pIod->Generate(); |
| |
| // Set audio and video profileLevels |
| if (pIod->FindProperty("audioProfileLevelId", |
| (MP4Property**)&pInt)) |
| pInt->SetValue(audioProfile); |
| |
| if (pIod->FindProperty("visualProfileLevelId", |
| (MP4Property**)&pInt)) |
| pInt->SetValue(videoProfile); |
| |
| // Mutate esIds from MP4ESIDIncDescrTag to MP4ESDescrTag |
| MP4DescriptorProperty* pEsProperty; |
| if (!pIod->FindProperty("esIds", (MP4Property**)&pEsProperty)) return; |
| pEsProperty->SetTags(MP4ESDescrTag); |
| |
| // Add ES Descriptors |
| |
| // Scene |
| CreateIsmaSceneCommand( |
| (audioProfile != 0xFF), |
| (videoProfile != 0xFF), |
| &pBytes, |
| &numBytes); |
| |
| log.hexDump(0, MP4_LOG_VERBOSE1, pBytes, numBytes, "\"%s\": Scene data", |
| GetFilename().c_str() ); |
| |
| char* sceneCmdBase64 = MP4ToBase64(pBytes, numBytes); |
| |
| char* urlBuf = |
| (char*)MP4Malloc(strlen(sceneCmdBase64) + 64); |
| snprintf(urlBuf, strlen(sceneCmdBase64) + 64, |
| "data:application/mpeg4-bifs-au;base64,%s", |
| sceneCmdBase64); |
| |
| log.verbose1f("\"%s\": Scene data URL = \042%s\042", GetFilename().c_str(), |
| urlBuf); |
| |
| /* MP4Descriptor* pSceneEsd = */ |
| CreateESD( |
| pEsProperty, |
| 201, // esid |
| MP4SystemsV2ObjectType, |
| MP4SceneDescriptionStreamType, |
| numBytes, // bufferSize |
| numBytes * 8, // bitrate |
| BifsV2Config, |
| sizeof(BifsV2Config), |
| urlBuf); |
| MP4Free(urlBuf); |
| urlBuf = NULL; |
| |
| MP4Free(sceneCmdBase64); |
| sceneCmdBase64 = NULL; |
| MP4Free(pBytes); |
| pBytes = NULL; |
| |
| // OD |
| |
| // Video |
| MP4DescriptorProperty* pVideoEsdProperty = |
| new MP4DescriptorProperty(dummyParent); |
| 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(dummyParent); |
| 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; |
| |
| log.hexDump(0, MP4_LOG_VERBOSE1,pBytes, numBytes,"\"%s\": OD data = %" PRIu64 " bytes", |
| GetFilename().c_str(), numBytes); |
| |
| char* odCmdBase64 = MP4ToBase64(pBytes, numBytes); |
| |
| urlBuf = (char*)MP4Malloc(strlen(odCmdBase64) + 64); |
| if (urlBuf != NULL) { |
| snprintf(urlBuf, strlen(odCmdBase64) + 64, |
| "data:application/mpeg4-od-au;base64,%s", |
| odCmdBase64); |
| |
| log.verbose1f("\"%s\": OD data URL = \042%s\042", GetFilename().c_str(), urlBuf); |
| |
| /* MP4Descriptor* pOdEsd = */ |
| CreateESD( |
| pEsProperty, |
| 101, |
| MP4SystemsV1ObjectType, |
| MP4ObjectDescriptionStreamType, |
| numBytes, // bufferSize |
| numBytes * 8, // bitrate |
| NULL, // config |
| 0, // configLength |
| urlBuf); |
| |
| MP4Free(urlBuf); |
| urlBuf = NULL; |
| } |
| MP4Free(odCmdBase64); |
| odCmdBase64 = NULL; |
| MP4Free(pBytes); |
| pBytes = NULL; |
| |
| // finally get the whole thing written to a memory |
| pIod->WriteToMemory(*this, ppIodBytes, pIodNumBytes); |
| |
| delete pIod; |
| |
| log.hexDump(0, MP4_LOG_VERBOSE1, *ppIodBytes, *pIodNumBytes,"\"%s\": IOD data", |
| GetFilename().c_str() ); |
| } |
| |
| void MP4File::CreateESD( |
| MP4DescriptorProperty* pEsProperty, |
| uint32_t esid, |
| uint8_t objectType, |
| uint8_t streamType, |
| uint32_t bufferSize, |
| uint32_t bitrate, |
| const uint8_t* pConfig, |
| uint32_t configLength, |
| char* url) |
| { |
| MP4IntegerProperty* pInt; |
| MP4StringProperty* pString; |
| MP4BytesProperty* pBytes; |
| MP4BitfieldProperty* pBits; |
| |
| MP4Descriptor* pEsd = |
| pEsProperty->AddDescriptor(MP4ESDescrTag); |
| pEsd->Generate(); |
| |
| if (pEsd->FindProperty("ESID", |
| (MP4Property**)&pInt)) |
| pInt->SetValue(esid); |
| |
| if (pEsd->FindProperty("decConfigDescr.objectTypeId", |
| (MP4Property**)&pInt)) |
| pInt->SetValue(objectType); |
| |
| if (pEsd->FindProperty("decConfigDescr.streamType", |
| (MP4Property**)&pInt)) |
| pInt->SetValue(streamType); |
| |
| if (pEsd->FindProperty("decConfigDescr.bufferSizeDB", |
| (MP4Property**)&pInt)) |
| pInt->SetValue(bufferSize); |
| |
| if (pEsd->FindProperty("decConfigDescr.maxBitrate", |
| (MP4Property**)&pInt)) |
| pInt->SetValue(bitrate); |
| |
| if (pEsd->FindProperty("decConfigDescr.avgBitrate", |
| (MP4Property**)&pInt)) |
| pInt->SetValue(bitrate); |
| |
| MP4DescriptorProperty* pConfigDescrProperty; |
| if (pEsd->FindProperty("decConfigDescr.decSpecificInfo", |
| (MP4Property**)&pConfigDescrProperty)) { |
| |
| MP4Descriptor* pConfigDescr = |
| pConfigDescrProperty->AddDescriptor(MP4DecSpecificDescrTag); |
| pConfigDescr->Generate(); |
| |
| if (pConfigDescrProperty->FindProperty("decSpecificInfo[0].info", |
| (MP4Property**)&pBytes)) |
| pBytes->SetValue(pConfig, configLength); |
| } |
| |
| if (pEsd->FindProperty("slConfigDescr.predefined", |
| (MP4Property**)&pInt)) |
| // changed 12/5/02 from plugfest to value 0 |
| pInt->SetValue(0); |
| |
| if (pEsd->FindProperty("slConfig.useAccessUnitEndFlag", |
| (MP4Property **)&pBits)) |
| pBits->SetValue(1); |
| |
| if (url) { |
| if (pEsd->FindProperty("URLFlag", |
| (MP4Property**)&pInt)) |
| pInt->SetValue(1); |
| |
| if (pEsd->FindProperty("URL", |
| (MP4Property**)&pString)) |
| pString->SetValue(url); |
| } |
| |
| //return pEsd; |
| } |
| |
| void MP4File::CreateIsmaODUpdateCommandFromFileForFile( |
| MP4TrackId odTrackId, |
| MP4TrackId audioTrackId, |
| MP4TrackId videoTrackId, |
| uint8_t** ppBytes, |
| uint64_t* pNumBytes) |
| { |
| // Descriptor constructors need a parent atom. In this |
| // case we don't have one so create a dummy one. This |
| // is OK as we only build the descriptor and then dump |
| // its contents to a buffer |
| MP4Atom dummyParent(*this); |
| MP4Descriptor* pCommand = CreateODCommand(dummyParent, MP4ODUpdateODCommandTag); |
| pCommand->Generate(); |
| |
| for (uint8_t i = 0; i < 2; i++) { |
| MP4TrackId trackId; |
| uint16_t odId; |
| |
| if (i == 0) { |
| trackId = audioTrackId; |
| odId = 10; |
| } else { |
| trackId = videoTrackId; |
| odId = 20; |
| } |
| |
| if (trackId == MP4_INVALID_TRACK_ID) { |
| continue; |
| } |
| |
| MP4DescriptorProperty* pOdDescrProperty = |
| (MP4DescriptorProperty*)(pCommand->GetProperty(0)); |
| |
| pOdDescrProperty->SetTags(MP4FileODescrTag); |
| |
| MP4Descriptor* pOd = |
| pOdDescrProperty->AddDescriptor(MP4FileODescrTag); |
| |
| pOd->Generate(); |
| |
| MP4BitfieldProperty* pOdIdProperty = NULL; |
| if (pOd->FindProperty("objectDescriptorId", |
| (MP4Property**)&pOdIdProperty)) |
| pOdIdProperty->SetValue(odId); |
| |
| MP4DescriptorProperty* pEsIdsDescriptorProperty = NULL; |
| ASSERT(pOd->FindProperty("esIds", |
| (MP4Property**)&pEsIdsDescriptorProperty)); |
| ASSERT(pEsIdsDescriptorProperty); |
| |
| pEsIdsDescriptorProperty->SetTags(MP4ESIDRefDescrTag); |
| |
| MP4Descriptor *pRefDescriptor = |
| pEsIdsDescriptorProperty->AddDescriptor(MP4ESIDRefDescrTag); |
| pRefDescriptor->Generate(); |
| |
| MP4Integer16Property* pRefIndexProperty = NULL; |
| ASSERT(pRefDescriptor->FindProperty("refIndex", |
| (MP4Property**)&pRefIndexProperty)); |
| ASSERT(pRefIndexProperty); |
| |
| uint32_t mpodIndex = FindTrackReference( |
| MakeTrackName(odTrackId, "tref.mpod"), trackId); |
| ASSERT(mpodIndex != 0); |
| |
| pRefIndexProperty->SetValue(mpodIndex); |
| } |
| |
| pCommand->WriteToMemory(*this, ppBytes, pNumBytes); |
| |
| delete pCommand; |
| } |
| |
| void MP4File::CreateIsmaODUpdateCommandFromFileForStream( |
| MP4TrackId audioTrackId, |
| MP4TrackId videoTrackId, |
| uint8_t** ppBytes, |
| uint64_t* pNumBytes) |
| { |
| MP4DescriptorProperty* pAudioEsd = NULL; |
| MP4Integer8Property* pAudioSLConfigPredef = NULL; |
| MP4BitfieldProperty* pAudioAccessUnitEndFlag = NULL; |
| int oldAudioUnitEndFlagValue = 0; |
| MP4DescriptorProperty* pVideoEsd = NULL; |
| MP4Integer8Property* pVideoSLConfigPredef = NULL; |
| MP4BitfieldProperty* pVideoAccessUnitEndFlag = NULL; |
| int oldVideoUnitEndFlagValue = 0; |
| MP4IntegerProperty* pAudioEsdId = NULL; |
| MP4IntegerProperty* pVideoEsdId = NULL; |
| |
| if (audioTrackId != MP4_INVALID_TRACK_ID) { |
| // changed mp4a to * to handle enca case |
| MP4Atom* pEsdsAtom = |
| FindAtom(MakeTrackName(audioTrackId, |
| "mdia.minf.stbl.stsd.*.esds")); |
| ASSERT(pEsdsAtom); |
| |
| pAudioEsd = (MP4DescriptorProperty*)(pEsdsAtom->GetProperty(2)); |
| // ESID is 0 for file, stream needs to be non-ze |
| ASSERT(pAudioEsd->FindProperty("ESID", |
| (MP4Property**)&pAudioEsdId)); |
| |
| ASSERT(pAudioEsdId); |
| pAudioEsdId->SetValue(audioTrackId); |
| |
| // SL config needs to change from 2 (file) to 1 (null) |
| if (pAudioEsd->FindProperty("slConfigDescr.predefined", |
| (MP4Property**)&pAudioSLConfigPredef)) { |
| ASSERT(pAudioSLConfigPredef); |
| pAudioSLConfigPredef->SetValue(0); |
| } |
| |
| if (pAudioEsd->FindProperty("slConfigDescr.useAccessUnitEndFlag", |
| (MP4Property **)&pAudioAccessUnitEndFlag)) { |
| oldAudioUnitEndFlagValue = |
| pAudioAccessUnitEndFlag->GetValue(); |
| pAudioAccessUnitEndFlag->SetValue(1); |
| } |
| } |
| |
| if (videoTrackId != MP4_INVALID_TRACK_ID) { |
| // changed mp4v to * to handle encv case |
| MP4Atom* pEsdsAtom = |
| FindAtom(MakeTrackName(videoTrackId, |
| "mdia.minf.stbl.stsd.*.esds")); |
| ASSERT(pEsdsAtom); |
| |
| pVideoEsd = (MP4DescriptorProperty*)(pEsdsAtom->GetProperty(2)); |
| ASSERT(pVideoEsd->FindProperty("ESID", |
| (MP4Property**)&pVideoEsdId)); |
| |
| ASSERT(pVideoEsdId); |
| pVideoEsdId->SetValue(videoTrackId); |
| |
| // SL config needs to change from 2 (file) to 1 (null) |
| ASSERT(pVideoEsd->FindProperty("slConfigDescr.predefined", |
| (MP4Property **)&pVideoSLConfigPredef)); |
| ASSERT(pVideoSLConfigPredef); |
| pVideoSLConfigPredef->SetValue(0); |
| |
| if (pVideoEsd->FindProperty("slConfigDescr.useAccessUnitEndFlag", |
| (MP4Property **)&pVideoAccessUnitEndFlag)) { |
| oldVideoUnitEndFlagValue = |
| pVideoAccessUnitEndFlag->GetValue(); |
| pVideoAccessUnitEndFlag->SetValue(1); |
| } |
| } |
| |
| CreateIsmaODUpdateCommandForStream( |
| pAudioEsd, pVideoEsd, ppBytes, pNumBytes); |
| log.hexDump(0, MP4_LOG_VERBOSE1, *ppBytes, *pNumBytes, |
| "\"%s\": After CreateImsaODUpdateCommandForStream len %" PRIu64, |
| GetFilename().c_str(), *pNumBytes); |
| |
| // return SL config values to 2 (file) |
| // return ESID values to 0 |
| if (pAudioSLConfigPredef) { |
| pAudioSLConfigPredef->SetValue(2); |
| } |
| if (pAudioEsdId) { |
| pAudioEsdId->SetValue(0); |
| } |
| if (pAudioAccessUnitEndFlag) { |
| pAudioAccessUnitEndFlag->SetValue(oldAudioUnitEndFlagValue ); |
| } |
| if (pVideoEsdId) { |
| pVideoEsdId->SetValue(0); |
| } |
| if (pVideoSLConfigPredef) { |
| pVideoSLConfigPredef->SetValue(2); |
| } |
| if (pVideoAccessUnitEndFlag) { |
| pVideoAccessUnitEndFlag->SetValue(oldVideoUnitEndFlagValue ); |
| } |
| } |
| |
| void MP4File::CreateIsmaODUpdateCommandForStream( |
| MP4DescriptorProperty* pAudioEsdProperty, |
| MP4DescriptorProperty* pVideoEsdProperty, |
| uint8_t** ppBytes, |
| uint64_t* pNumBytes) |
| { |
| MP4Descriptor* pAudioOd = NULL; |
| MP4Descriptor* pVideoOd = NULL; |
| |
| // Descriptor constructors need a parent atom. In this |
| // case we don't have one so create a dummy one. This |
| // is OK as we only build the descriptor and then dump |
| // its contents to a buffer |
| MP4Atom dummyParent(*this); |
| MP4Descriptor* pCommand = |
| CreateODCommand(dummyParent, MP4ODUpdateODCommandTag); |
| pCommand->Generate(); |
| |
| for (uint8_t i = 0; i < 2; i++) { |
| uint16_t odId; |
| MP4DescriptorProperty* pEsdProperty = NULL; |
| |
| if (i == 0) { |
| odId = 10; |
| pEsdProperty = pAudioEsdProperty; |
| } else { |
| odId = 20; |
| pEsdProperty = pVideoEsdProperty; |
| } |
| |
| if (pEsdProperty == NULL) { |
| continue; |
| } |
| |
| MP4DescriptorProperty* pOdDescrProperty = |
| (MP4DescriptorProperty*)(pCommand->GetProperty(0)); |
| |
| pOdDescrProperty->SetTags(MP4ODescrTag); |
| |
| MP4Descriptor* pOd = |
| pOdDescrProperty->AddDescriptor(MP4ODescrTag); |
| pOd->Generate(); |
| |
| if (i == 0) { |
| pAudioOd = pOd; |
| } else { |
| pVideoOd = pOd; |
| } |
| |
| MP4BitfieldProperty* pOdIdProperty = NULL; |
| if (pOd->FindProperty("objectDescriptorId", |
| (MP4Property**)&pOdIdProperty)) { |
| pOdIdProperty->SetValue(odId); |
| } |
| |
| delete (MP4DescriptorProperty*)pOd->GetProperty(4); |
| pOd->SetProperty(4, pEsdProperty); |
| } |
| |
| // serialize OD command |
| pCommand->WriteToMemory(*this, ppBytes, pNumBytes); |
| |
| // detach from esd descriptor params |
| if (pAudioOd) { |
| pAudioOd->SetProperty(4, NULL); |
| } |
| if (pVideoOd) { |
| pVideoOd->SetProperty(4, NULL); |
| } |
| |
| // then destroy |
| delete pCommand; |
| } |
| |
| void MP4File::CreateIsmaSceneCommand( |
| bool hasAudio, |
| bool hasVideo, |
| uint8_t** ppBytes, |
| uint64_t* pNumBytes) |
| { |
| // from ISMA 1.0 Tech Spec Appendix E |
| static const uint8_t bifsAudioOnly[] = { |
| 0xC0, 0x10, 0x12, |
| 0x81, 0x30, 0x2A, 0x05, 0x6D, 0xC0 |
| }; |
| static const uint8_t bifsVideoOnly[] = { |
| 0xC0, 0x10, 0x12, |
| 0x61, 0x04, |
| 0x1F, 0xC0, 0x00, 0x00, |
| 0x1F, 0xC0, 0x00, 0x00, |
| 0x44, 0x28, 0x22, 0x82, 0x9F, 0x80 |
| }; |
| static const uint8_t bifsAudioVideo[] = { |
| 0xC0, 0x10, 0x12, |
| 0x81, 0x30, 0x2A, 0x05, 0x6D, 0x26, |
| 0x10, 0x41, 0xFC, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, |
| 0x04, 0x42, 0x82, 0x28, 0x29, 0xF8 |
| }; |
| |
| if (hasAudio && hasVideo) { |
| *pNumBytes = sizeof(bifsAudioVideo); |
| *ppBytes = (uint8_t*)MP4Malloc(*pNumBytes); |
| memcpy(*ppBytes, bifsAudioVideo, sizeof(bifsAudioVideo)); |
| |
| } else if (hasAudio) { |
| *pNumBytes = sizeof(bifsAudioOnly); |
| *ppBytes = (uint8_t*)MP4Malloc(*pNumBytes); |
| memcpy(*ppBytes, bifsAudioOnly, sizeof(bifsAudioOnly)); |
| |
| } else if (hasVideo) { |
| *pNumBytes = sizeof(bifsVideoOnly); |
| *ppBytes = (uint8_t*)MP4Malloc(*pNumBytes); |
| memcpy(*ppBytes, bifsVideoOnly, sizeof(bifsVideoOnly)); |
| } else { |
| *pNumBytes = 0; |
| *ppBytes = NULL; |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| } |
| } // namespace mp4v2::impl |