mirror of
https://github.com/WinampDesktop/winamp.git
synced 2024-09-24 15:54:12 +00:00
943 lines
24 KiB
C++
943 lines
24 KiB
C++
|
/*
|
||
|
* 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 "mp4common.h"
|
||
|
|
||
|
static const u_int8_t BifsV2Config[3] = {
|
||
|
0x00, 0x00, 0x60 // IsCommandStream = 1, PixelMetric = 1
|
||
|
};
|
||
|
|
||
|
void MP4File::MakeIsmaCompliant(bool addIsmaComplianceSdp)
|
||
|
{
|
||
|
ProtectWriteOperation("MP4MakeIsmaCompliant");
|
||
|
|
||
|
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 (MP4Error* e) {
|
||
|
delete e;
|
||
|
}
|
||
|
|
||
|
MP4TrackId videoTrackId = MP4_INVALID_TRACK_ID;
|
||
|
try {
|
||
|
videoTrackId = FindTrackId(0, MP4_VIDEO_TRACK_TYPE);
|
||
|
}
|
||
|
catch (MP4Error* e) {
|
||
|
delete e;
|
||
|
}
|
||
|
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"))) {
|
||
|
VERBOSE_ERROR(m_verbosity,
|
||
|
printf("MakeIsmaCompliant:can't make ISMA compliant when file contains an %s track\n", 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"))) {
|
||
|
VERBOSE_ERROR(m_verbosity,
|
||
|
printf("MakeIsmaCompliant:can't make ISMA compliant when file contains an %s track\n", video_media_data_name);
|
||
|
);
|
||
|
return;
|
||
|
}
|
||
|
uint32_t verb = GetVerbosity();
|
||
|
SetVerbosity(verb & ~MP4_DETAILS_ERROR);
|
||
|
videoProfile = MP4GetVideoProfileLevel(this, videoTrackId);
|
||
|
SetVerbosity(verb);
|
||
|
}
|
||
|
|
||
|
m_useIsma = true;
|
||
|
|
||
|
u_int64_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->FindAtomMP4("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 (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);
|
||
|
|
||
|
uint sdpBufLen = (uint)strlen(iodBase64) + 256;
|
||
|
uint used;
|
||
|
char* sdpBuf = (char*)MP4Calloc(sdpBufLen);
|
||
|
|
||
|
if (addIsmaComplianceSdp) {
|
||
|
strncpy(sdpBuf, "a=isma-compliance:1,1.0,1\015\012", sdpBufLen);
|
||
|
}
|
||
|
|
||
|
used = (uint)strlen(sdpBuf);
|
||
|
sdpBufLen -= used;
|
||
|
snprintf(&sdpBuf[used], sdpBufLen,
|
||
|
"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;
|
||
|
}
|
||
|
|
||
|
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,
|
||
|
u_int8_t** ppBytes,
|
||
|
u_int64_t* pNumBytes)
|
||
|
{
|
||
|
MP4Descriptor* pIod = new MP4IODescriptor();
|
||
|
pIod->SetTag(MP4IODescrTag);
|
||
|
pIod->Generate();
|
||
|
|
||
|
MP4Atom* pIodsAtom = FindAtomMP4File("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;
|
||
|
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);
|
||
|
|
||
|
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);
|
||
|
|
||
|
uint urlBufLen = (uint)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);
|
||
|
|
||
|
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
|
||
|
(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);
|
||
|
|
||
|
VERBOSE_ISMA(GetVerbosity(),
|
||
|
printf("Scene data =\n"); MP4HexDump(pBytes, numBytes));
|
||
|
|
||
|
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);
|
||
|
|
||
|
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
|
||
|
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;
|
||
|
|
||
|
VERBOSE_ISMA(GetVerbosity(),
|
||
|
printf("IOD data =\n"); MP4HexDump(*ppBytes, *pNumBytes));
|
||
|
}
|
||
|
|
||
|
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
|
||
|
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);
|
||
|
|
||
|
VERBOSE_ISMA(GetVerbosity(),
|
||
|
printf("Scene data =\n"); MP4HexDump(pBytes, numBytes));
|
||
|
|
||
|
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);
|
||
|
|
||
|
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(urlBuf);
|
||
|
urlBuf = NULL;
|
||
|
|
||
|
MP4Free(sceneCmdBase64);
|
||
|
sceneCmdBase64 = 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 = "U64" bytes\n", numBytes); MP4HexDump(pBytes, 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);
|
||
|
|
||
|
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(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;
|
||
|
|
||
|
VERBOSE_ISMA(GetVerbosity(),
|
||
|
printf("IOD data =\n"); MP4HexDump(*ppIodBytes, *pIodNumBytes));
|
||
|
}
|
||
|
|
||
|
void MP4File::CreateESD(
|
||
|
MP4DescriptorProperty* pEsProperty,
|
||
|
u_int32_t esid,
|
||
|
u_int8_t objectType,
|
||
|
u_int8_t streamType,
|
||
|
u_int32_t bufferSize,
|
||
|
u_int32_t bitrate,
|
||
|
const u_int8_t* pConfig,
|
||
|
u_int32_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,
|
||
|
u_int8_t** ppBytes,
|
||
|
u_int64_t* pNumBytes)
|
||
|
{
|
||
|
MP4Descriptor* pCommand = CreateODCommand(MP4ODUpdateODCommandTag);
|
||
|
pCommand->Generate();
|
||
|
|
||
|
for (u_int8_t i = 0; i < 2; i++) {
|
||
|
MP4TrackId trackId;
|
||
|
u_int16_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);
|
||
|
|
||
|
u_int32_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,
|
||
|
u_int8_t** ppBytes,
|
||
|
u_int64_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 =
|
||
|
FindAtomMP4File(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 =
|
||
|
FindAtomMP4File(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);
|
||
|
VERBOSE_ISMA(GetVerbosity(),
|
||
|
printf("After CreateImsaODUpdateCommandForStream len "U64" =\n", *pNumBytes); MP4HexDump(*ppBytes, *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,
|
||
|
u_int8_t** ppBytes,
|
||
|
u_int64_t* pNumBytes)
|
||
|
{
|
||
|
MP4Descriptor* pAudioOd = NULL;
|
||
|
MP4Descriptor* pVideoOd = NULL;
|
||
|
|
||
|
MP4Descriptor* pCommand =
|
||
|
CreateODCommand(MP4ODUpdateODCommandTag);
|
||
|
pCommand->Generate();
|
||
|
|
||
|
for (u_int8_t i = 0; i < 2; i++) {
|
||
|
u_int16_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,
|
||
|
u_int8_t** ppBytes,
|
||
|
u_int64_t* pNumBytes)
|
||
|
{
|
||
|
// from ISMA 1.0 Tech Spec Appendix E
|
||
|
static const u_int8_t bifsAudioOnly[] = {
|
||
|
0xC0, 0x10, 0x12,
|
||
|
0x81, 0x30, 0x2A, 0x05, 0x6D, 0xC0
|
||
|
};
|
||
|
static const u_int8_t bifsVideoOnly[] = {
|
||
|
0xC0, 0x10, 0x12,
|
||
|
0x61, 0x04,
|
||
|
0x1F, 0xC0, 0x00, 0x00,
|
||
|
0x1F, 0xC0, 0x00, 0x00,
|
||
|
0x44, 0x28, 0x22, 0x82, 0x9F, 0x80
|
||
|
};
|
||
|
static const u_int8_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 = (u_int8_t*)MP4Malloc(*pNumBytes);
|
||
|
memcpy(*ppBytes, bifsAudioVideo, sizeof(bifsAudioVideo));
|
||
|
|
||
|
} else if (hasAudio) {
|
||
|
*pNumBytes = sizeof(bifsAudioOnly);
|
||
|
*ppBytes = (u_int8_t*)MP4Malloc(*pNumBytes);
|
||
|
memcpy(*ppBytes, bifsAudioOnly, sizeof(bifsAudioOnly));
|
||
|
|
||
|
} else if (hasVideo) {
|
||
|
*pNumBytes = sizeof(bifsVideoOnly);
|
||
|
*ppBytes = (u_int8_t*)MP4Malloc(*pNumBytes);
|
||
|
memcpy(*ppBytes, bifsVideoOnly, sizeof(bifsVideoOnly));
|
||
|
} else {
|
||
|
*pNumBytes = 0;
|
||
|
*ppBytes = NULL;
|
||
|
}
|
||
|
}
|
||
|
|