예제 #1
0
void MP4DescriptorProperty::Generate()
{
	// generate a default descriptor
	// if it is mandatory, and single
	if (m_mandatory && m_onlyOne) {
		MP4Descriptor* pDescriptor = 
			AddDescriptor(m_tagsStart);
		pDescriptor->Generate();
	}
}
예제 #2
0
MP4Descriptor* MP4DescriptorProperty::AddDescriptor(u_int8_t tag)
{
	// check that tag is in expected range
	ASSERT(tag >= m_tagsStart && tag <= m_tagsEnd);

	MP4Descriptor* pDescriptor = CreateDescriptor(tag);
	ASSERT(pDescriptor);

	m_pDescriptors.Add(pDescriptor);
	pDescriptor->SetParentAtom(m_pParentAtom);

	return pDescriptor;
}
예제 #3
0
void MP4DescriptorProperty::Read(MP4File* pFile, u_int32_t index)
{
	ASSERT(index == 0);

	if (m_implicit) {
		return;
	}

	u_int64_t start = pFile->GetPosition();

	while (true) {
		// enforce size limitation
		if (m_sizeLimit && pFile->GetPosition() >= start + m_sizeLimit) {
			break;
		}

		u_int8_t tag;
		try {
			pFile->PeekBytes(&tag, 1);
		}
		catch (MP4Error* e) {
			if (pFile->GetPosition() >= pFile->GetSize()) {
				// EOF
				delete e;
				break;
			}
			throw e;
		}

		// check if tag is in desired range
		if (tag < m_tagsStart || tag > m_tagsEnd) {
			break;
		}

		MP4Descriptor* pDescriptor = 
			AddDescriptor(tag);

		pDescriptor->Read(pFile);
	}

	// warnings
	if (m_mandatory && m_pDescriptors.Size() == 0) {
		VERBOSE_READ(pFile->GetVerbosity(),
			printf("Warning: Mandatory descriptor 0x%02x missing\n",
				m_tagsStart));
	} else if (m_onlyOne && m_pDescriptors.Size() > 1) {
		VERBOSE_READ(pFile->GetVerbosity(),
			printf("Warning: Descriptor 0x%02x has more than one instance\n",
				m_tagsStart));
	}
}
예제 #4
0
MP4Descriptor* CreateOCIDescriptor(u_int8_t tag) 
{
	MP4Descriptor* pDescriptor = NULL;

	switch (tag) {
	case MP4ContentClassDescrTag:
		pDescriptor = new MP4ContentClassDescriptor();
		break;
	case MP4KeywordDescrTag:
		pDescriptor = new MP4KeywordDescriptor();
		break;
	case MP4RatingDescrTag:
		pDescriptor = new MP4RatingDescriptor();
		break;
	case MP4LanguageDescrTag:
		pDescriptor = new MP4LanguageDescriptor();
		break;
	case MP4ShortTextDescrTag:
		pDescriptor = new MP4ShortTextDescriptor();
		break;
	case MP4ExpandedTextDescrTag:
		pDescriptor = new MP4ExpandedTextDescriptor();
		break;
	case MP4ContentCreatorDescrTag:
	case MP4OCICreatorDescrTag:
		pDescriptor = new MP4CreatorDescriptor(tag);
		break;
	case MP4ContentCreationDescrTag:
	case MP4OCICreationDescrTag:
		pDescriptor = new MP4CreationDescriptor(tag);
		break;
	case MP4SmpteCameraDescrTag:
		pDescriptor = new MP4SmpteCameraDescriptor();
		break;
	}

	if (pDescriptor == NULL) {
		if (tag >= MP4OCIDescrTagsStart && tag <= MP4OCIDescrTagsEnd) {
			pDescriptor = new MP4UnknownOCIDescriptor();
			pDescriptor->SetTag(tag);
		}
	}

	return pDescriptor;
}
예제 #5
0
MP4Descriptor* MP4QosQualifierProperty::CreateDescriptor(u_int8_t tag)
{
    MP4Descriptor* pDescriptor = NULL;
    switch (tag) {
#if 0
    case MP4MaxDelayQosTag:
        pDescriptor = new MP4MaxDelayQosQualifier();
        break;
    case MP4PrefMaxDelayQosTag:
        pDescriptor = new MP4PrefMaxDelayQosQualifier();
        break;
    case MP4LossProbQosTag:
        pDescriptor = new MP4LossProbQosQualifier();
        break;
    case MP4MaxGapLossQosTag:
        pDescriptor = new MP4MaxGapLossQosQualifier();
        break;
    case MP4MaxAUSizeQosTag:
        pDescriptor = new MP4MaxAUSizeQosQualifier();
        break;
    case MP4AvgAUSizeQosTag:
        pDescriptor = new MP4AvgAUSizeQosQualifier();
        break;
    case MP4MaxAURateQosTag:
        pDescriptor = new MP4MaxAURateQosQualifier();
        break;
#else
    case MP4MaxDelayQosTag:
    case MP4PrefMaxDelayQosTag:
    case MP4LossProbQosTag:
    case MP4MaxGapLossQosTag:
    case MP4MaxAUSizeQosTag:
    case MP4AvgAUSizeQosTag:
    case MP4MaxAURateQosTag:
        pDescriptor = new MP4QosDescriptorBase(tag);
        break;
#endif
    default:
        pDescriptor = new MP4UnknownQosQualifier();
        pDescriptor->SetTag(tag);
    }

    return pDescriptor;
}
예제 #6
0
MP4Descriptor* MP4QosQualifierProperty::CreateDescriptor(MP4Atom &parentAtom, uint8_t tag)
{
    MP4Descriptor* pDescriptor = NULL;
    switch (tag) {
    case MP4MaxDelayQosTag:
    case MP4PrefMaxDelayQosTag:
    case MP4LossProbQosTag:
    case MP4MaxGapLossQosTag:
    case MP4MaxAUSizeQosTag:
    case MP4AvgAUSizeQosTag:
    case MP4MaxAURateQosTag:
        pDescriptor = new MP4QosDescriptorBase(parentAtom, tag);
        break;
    default:
        pDescriptor = new MP4UnknownQosQualifier(parentAtom);
        pDescriptor->SetTag(tag);
    }

    return pDescriptor;
}
예제 #7
0
MP4Descriptor* MP4DescriptorProperty::CreateDescriptor(u_int8_t tag) 
{
	MP4Descriptor* pDescriptor = NULL;

	switch (tag) {
	case MP4ESDescrTag:
		pDescriptor = new MP4ESDescriptor();
		break;
	case MP4DecConfigDescrTag:
		pDescriptor = new MP4DecConfigDescriptor();
		break;
	case MP4DecSpecificDescrTag:
		pDescriptor = new MP4DecSpecificDescriptor();
		break;
	case MP4SLConfigDescrTag:
		pDescriptor = new MP4SLConfigDescriptor();
		break;
	case MP4ContentIdDescrTag:
		pDescriptor = new MP4ContentIdDescriptor();
		break;
	case MP4SupplContentIdDescrTag:
		pDescriptor = new MP4SupplContentIdDescriptor();
		break;
	case MP4IPIPtrDescrTag:
		pDescriptor = new MP4IPIPtrDescriptor();
		break;
	case MP4IPMPPtrDescrTag:
		pDescriptor = new MP4IPMPPtrDescriptor();
		break;
	case MP4IPMPDescrTag:
		pDescriptor = new MP4IPMPDescriptor();
		break;
	case MP4QosDescrTag:
		pDescriptor = new MP4QosDescriptor();
		break;
	case MP4RegistrationDescrTag:
		pDescriptor = new MP4RegistrationDescriptor();
		break;
	case MP4ESIDIncDescrTag:
		pDescriptor = new MP4ESIDIncDescriptor();
		break;
	case MP4ESIDRefDescrTag:
		pDescriptor = new MP4ESIDRefDescriptor();
		break;
	case MP4IODescrTag:
	case MP4FileIODescrTag:
		pDescriptor = new MP4IODescriptor();
		pDescriptor->SetTag(tag);
		break;
	case MP4ODescrTag:
	case MP4FileODescrTag:
		pDescriptor = new MP4ODescriptor();
		pDescriptor->SetTag(tag);
		break;
	case MP4ExtProfileLevelDescrTag:
		pDescriptor = new MP4ExtProfileLevelDescriptor();
		break;
	}

	if (pDescriptor == NULL) {
		if (tag >= MP4OCIDescrTagsStart && tag <= MP4OCIDescrTagsEnd) {
			pDescriptor = CreateOCIDescriptor(tag);
		}

		if (tag >= MP4ExtDescrTagsStart && tag <= MP4ExtDescrTagsEnd) {
			pDescriptor = new MP4ExtensionDescriptor();
			pDescriptor->SetTag(tag);
		}
	}

	return pDescriptor;
}
예제 #8
0
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;
        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;
}
예제 #9
0
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;
        pOd->FindProperty("objectDescriptorId",
                          (MP4Property**)&pOdIdProperty);
        pOdIdProperty->SetValue(odId);

        MP4DescriptorProperty* pEsIdsDescriptorProperty = NULL;
        pOd->FindProperty("esIds",
                          (MP4Property**)&pEsIdsDescriptorProperty);
        ASSERT(pEsIdsDescriptorProperty);

        pEsIdsDescriptorProperty->SetTags(MP4ESIDRefDescrTag);

        MP4Descriptor *pRefDescriptor =
            pEsIdsDescriptorProperty->AddDescriptor(MP4ESIDRefDescrTag);
        pRefDescriptor->Generate();

        MP4Integer16Property* pRefIndexProperty = NULL;
        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;
}
예제 #10
0
MP4Descriptor* MP4File::CreateESD(
    MP4DescriptorProperty* pEsProperty,
    u_int32_t esid,
    u_int8_t objectType,
    u_int8_t streamType,
    u_int32_t bufferSize,
    u_int32_t bitrate,
    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();

    pEsd->FindProperty("ESID",
                       (MP4Property**)&pInt);
    pInt->SetValue(esid);

    pEsd->FindProperty("decConfigDescr.objectTypeId",
                       (MP4Property**)&pInt);
    pInt->SetValue(objectType);

    pEsd->FindProperty("decConfigDescr.streamType",
                       (MP4Property**)&pInt);
    pInt->SetValue(streamType);

    pEsd->FindProperty("decConfigDescr.bufferSizeDB",
                       (MP4Property**)&pInt);
    pInt->SetValue(bufferSize);

    pEsd->FindProperty("decConfigDescr.maxBitrate",
                       (MP4Property**)&pInt);
    pInt->SetValue(bitrate);

    pEsd->FindProperty("decConfigDescr.avgBitrate",
                       (MP4Property**)&pInt);
    pInt->SetValue(bitrate);

    MP4DescriptorProperty* pConfigDescrProperty;
    pEsd->FindProperty("decConfigDescr.decSpecificInfo",
                       (MP4Property**)&pConfigDescrProperty);

    MP4Descriptor* pConfigDescr =
        pConfigDescrProperty->AddDescriptor(MP4DecSpecificDescrTag);
    pConfigDescr->Generate();

    pConfigDescrProperty->FindProperty("decSpecificInfo[0].info",
                                       (MP4Property**)&pBytes);
    pBytes->SetValue(pConfig, configLength);

    pEsd->FindProperty("slConfigDescr.predefined",
                       (MP4Property**)&pInt);
    // changed 12/5/02 from plugfest to value 0
    pInt->SetValue(0);

    pEsd->FindProperty("slConfig.useAccessUnitEndFlag",
                       (MP4Property **)&pBits);
    pBits->SetValue(1);

    if (url) {
        pEsd->FindProperty("URLFlag",
                           (MP4Property**)&pInt);
        pInt->SetValue(1);

        pEsd->FindProperty("URL",
                           (MP4Property**)&pString);
        pString->SetValue(url);
    }

    return pEsd;
}
예제 #11
0
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));
}
예제 #12
0
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));
}