M4Err UpdateSample(MediaAtom *mdia, u32 sampleNumber, u32 size, u32 CTS, u64 offset, u8 isRap) { u32 i; SampleTableAtom *stbl = mdia->information->sampleTable; //set size, offset, RAP, CTS ... stbl_SetSampleSize(stbl->SampleSize, sampleNumber, size); stbl_SetChunkOffset(mdia, sampleNumber, offset); //do we have a CTS? if (stbl->CompositionOffset) { stbl_SetSampleCTS(stbl, sampleNumber, CTS); } else { //do we need one ?? if (CTS) { stbl->CompositionOffset = (CompositionOffsetAtom *) CreateAtom(CompositionOffsetAtomType); stbl_AddCTS(stbl, sampleNumber, CTS); } } //do we have a sync ??? if (stbl->SyncSample) { stbl_SetSampleRAP(stbl->SyncSample, sampleNumber, isRap); } else { //do we need one if (! isRap) { stbl->SyncSample = (SyncSampleAtom *) CreateAtom(SyncSampleAtomType); //what a pain: all the sample we had have to be sync ... for (i=0; i<stbl->SampleSize->sampleCount; i++) { if (i+1 != sampleNumber) stbl_AddRAP(stbl->SyncSample, i+1); } } } return M4OK; }
M4Err Media_AddSample(MediaAtom *mdia, u64 data_offset, M4Sample *sample, u32 StreamDescIndex, u32 syncShadowNumber) { M4Err e; SampleTableAtom *stbl; u32 sampleNumber, i; if (!mdia || !sample) return M4BadParam; stbl = mdia->information->sampleTable; //get a valid sampleNumber for this new guy e = stbl_AddDTS(stbl, sample->DTS, &sampleNumber, mdia->mediaHeader->timeScale); if (e) return e; //add size e = stbl_AddSize(stbl->SampleSize, sampleNumber, sample->dataLength); if (e) return e; //adds CTS offset if (sample->CTS_Offset) { //if we don't have a CTS table, add it... if (!stbl->CompositionOffset) stbl->CompositionOffset = (CompositionOffsetAtom *) CreateAtom(CompositionOffsetAtomType); //then add our CTS (the prev samples with no CTS offset will be automatically added... e = stbl_AddCTS(stbl, sampleNumber, sample->CTS_Offset); if (e) return e; } else if (stbl->CompositionOffset) { e = stbl_AddCTS(stbl, sampleNumber, sample->CTS_Offset); if (e) return e; } //The first non sync sample we see must create a syncTable if (sample->IsRAP) { //insert it only if we have a sync table if (stbl->SyncSample) { e = stbl_AddRAP(stbl->SyncSample, sampleNumber); if (e) return e; } } else { //non-sync sample. Create a SyncSample table if needed if (!stbl->SyncSample) { stbl->SyncSample = (SyncSampleAtom *) CreateAtom(SyncSampleAtomType); //all the prev samples are sync for (i=0; i<stbl->SampleSize->sampleCount; i++) { if (i+1 != sampleNumber) { e = stbl_AddRAP(stbl->SyncSample, i+1); if (e) return e; } } } } //and update the chunks e = stbl_AddChunkOffset(mdia, sampleNumber, StreamDescIndex, data_offset); if (e) return e; if (!syncShadowNumber) return M4OK; if (!stbl->ShadowSync) stbl->ShadowSync = (ShadowSyncAtom *) CreateAtom(ShadowSyncAtomType); return stbl_AddShadow(mdia->information->sampleTable->ShadowSync, sampleNumber, syncShadowNumber); }
M4Err Media_CreateDataRef(DataReferenceAtom *dref, char *URLname, char *URNname, u32 *dataRefIndex) { M4Err e; DataEntryURLAtom *entry; M4Err dref_AddDataEntry(DataReferenceAtom *ptr, Atom *entry); if (!URLname && !URNname) { //THIS IS SELF CONTAIN, create a regular entry if needed entry = (DataEntryURLAtom *) CreateAtom(DataEntryURLAtomType); entry->location = NULL; entry->flags = 0; entry->flags |= 1; e = dref_AddDataEntry(dref, (Atom *)entry); if (e) return e; *dataRefIndex = ChainGetCount(dref->atomList); return M4OK; } else if (!URNname && URLname) { //THIS IS URL entry = (DataEntryURLAtom *) CreateAtom(DataEntryURLAtomType); entry->flags = 0; entry->location = (char*)malloc(strlen(URLname)+1); if (! entry->location) { DelAtom((Atom *)entry); return M4OutOfMem; } strcpy(entry->location, URLname); e = dref_AddDataEntry(dref, (Atom *)entry); if (e) return e; *dataRefIndex = ChainGetCount(dref->atomList); return M4OK; } else { //THIS IS URN entry = (DataEntryURLAtom *) CreateAtom(DataEntryURNAtomType); ((DataEntryURNAtom *)entry)->flags = 0; ((DataEntryURNAtom *)entry)->nameURN = (char*)malloc(strlen(URNname)+1); if (! ((DataEntryURNAtom *)entry)->nameURN) { DelAtom((Atom *)entry); return M4OutOfMem; } strcpy(((DataEntryURNAtom *)entry)->nameURN, URNname); //check for URL if (URLname) { ((DataEntryURNAtom *)entry)->location = (char*)malloc(strlen(URLname)+1); if (! ((DataEntryURNAtom *)entry)->location) { DelAtom((Atom *)entry); return M4OutOfMem; } strcpy(((DataEntryURNAtom *)entry)->location, URLname); } e = dref_AddDataEntry(dref, (Atom *)entry); if (e) return e; *dataRefIndex = ChainGetCount(dref->atomList); return M4OK; } return M4OK; }
//set the time offset of this packet. This enables packets to be placed in the hint track //in decoding order, but have their presentation time-stamp in the transmitted //packet be in a different order. Typically used for MPEG video with B-frames M4Err M4H_RTP_SetPacketTimeOffset(M4File *the_file, u32 trackNumber, s32 timeOffset) { RtpoAtom *rtpo; TrackAtom *trak; HintSampleEntryAtom *entry; RTPPacket *pck; u32 dataRefIndex, i; M4Err e; trak = GetTrackFromFile(the_file, trackNumber); if (!trak || !CheckHintFormat(trak, M4_Hint_RTP)) return M4BadParam; e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (SampleEntryAtom **) &entry, &dataRefIndex); if (e) return e; if (!entry->w_sample) return M4BadParam; pck = ChainGetEntry(entry->w_sample->packetTable, ChainGetCount(entry->w_sample->packetTable) - 1); if (!pck) return M4BadParam; //look in the TLV for (i=0; i<ChainGetCount(pck->TLV); i++) { rtpo = ChainGetEntry(pck->TLV, i); if (rtpo->type == rtpoAtomType) { rtpo->timeOffset = timeOffset; return M4OK; } } //not found, add it rtpo = (RtpoAtom *) CreateAtom(rtpoAtomType); rtpo->timeOffset = timeOffset; return ChainAddEntry(pck->TLV, rtpo); }
//sets the RTP SequenceNumber Offset that the server will add to the packets //if not set, the server adds a random offset M4Err M4H_RTP_SetSequenceNumberOffset(M4File *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 SequenceNumberOffset) { TrackAtom *trak; HintSampleEntryAtom *hdesc; u32 i, count; SeqOffHintEntry *ent; trak = GetTrackFromFile(the_file, trackNumber); if (!trak || !CheckHintFormat(trak, M4_Hint_RTP)) return M4BadParam; //OK, create a new HintSampleDesc hdesc = ChainGetEntry(trak->Media->information->sampleTable->SampleDescription->atomList, HintDescriptionIndex - 1); count = ChainGetCount(hdesc->HintDataTable); for (i=0; i< count; i++) { ent = ChainGetEntry(hdesc->HintDataTable, i); if (ent->type == snroHintEntryType) { ent->SeqOffset = SequenceNumberOffset; return M4OK; } } //we have to create a new entry... ent = (SeqOffHintEntry *) CreateAtom(snroHintEntryType); ent->SeqOffset = SequenceNumberOffset; return ChainAddEntry(hdesc->HintDataTable, ent); }
//to use with internally supported protocols M4Err M4H_NewHintDescription(M4File *the_file, u32 trackNumber, s32 HintTrackVersion, s32 LastCompatibleVersion, u8 Rely, u32 *HintDescriptionIndex) { M4Err e; u32 drefIndex; TrackAtom *trak; HintSampleEntryAtom *hdesc; RelyHintEntry *relyA; M4Err stsd_AddAtom(SampleDescriptionAtom *ptr, Atom *a); trak = GetTrackFromFile(the_file, trackNumber); *HintDescriptionIndex = 0; if (!trak || !IsHintTrack(trak)) return M4BadParam; //OK, create a new HintSampleDesc hdesc = (HintSampleEntryAtom *) CreateAtom(GetHintFormat(trak)); if (HintTrackVersion > 0) hdesc->HintTrackVersion = HintTrackVersion; if (LastCompatibleVersion > 0) hdesc->LastCompatibleVersion = LastCompatibleVersion; //create a data reference - WE ONLY DEAL WITH SELF-CONTAINED HINT TRACKS e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, NULL, NULL, &drefIndex); if (e) return e; hdesc->dataReferenceIndex = drefIndex; //add the entry to our table... e = stsd_AddAtom(trak->Media->information->sampleTable->SampleDescription, (Atom *) hdesc); if (e) return e; *HintDescriptionIndex = ChainGetCount(trak->Media->information->sampleTable->SampleDescription->atomList); //RTP needs a default timeScale... use the media one. if (CheckHintFormat(trak, M4_Hint_RTP)) { e = M4H_RTP_SetTimeScale(the_file, trackNumber, *HintDescriptionIndex, trak->Media->mediaHeader->timeScale); if (e) return e; } if (!Rely) return M4OK; //we need a rely atom (common to all protocols) relyA = (RelyHintEntry *) CreateAtom(relyHintEntryType); if (Rely == 1) { relyA->prefered = 1; } else { relyA->required = 1; } return ChainAddEntry(hdesc->HintDataTable, relyA); }
M4Err M4_AMR_NewStreamConfig(M4File *the_file, u32 trackNumber, LPAMRCONFIGURATION param, char *URLname, char *URNname, u32 *outDescriptionIndex) { TrackAtom *trak; M4Err e; u32 dataRefIndex; TrackReferenceTypeAtom *dpnd; TrackReferenceAtom *tref; AMRSampleEntryAtom *entry; e = CanAccessMovie((M4Movie *)the_file, M4_OPEN_WRITE); if (e) return e; trak = GetTrackFromFile(the_file, trackNumber); if (!trak || !trak->Media || !param) return M4BadParam; dpnd = NULL; tref = NULL; //get or create the data ref e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); if (e) return e; if (!dataRefIndex) { e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); if (e) return e; } trak->Media->mediaHeader->modificationTime = GetMP4Time(); //create a new entry entry = (AMRSampleEntryAtom *) CreateAtom(param->WideBandAMR ? WB_AMRSampleEntryAtomType : AMRSampleEntryAtomType); if (!entry) return M4OutOfMem; entry->amr_info = (AMRConfigAtom *) CreateAtom(AMRConfigAtomType); if (!entry->amr_info) { DelAtom((Atom *) entry); return M4OutOfMem; } entry->samplerate_hi = trak->Media->mediaHeader->timeScale; entry->dataReferenceIndex = dataRefIndex; entry->amr_info->decoder_version = param->decoder_version; entry->amr_info->vendor = param->vendor; entry->amr_info->frames_per_sample = param->frames_per_sample; entry->amr_info->mode_change_period = param->mode_change_period; entry->amr_info->mode_set = param->mode_set; e = ChainAddEntry(trak->Media->information->sampleTable->SampleDescription->atomList, entry); *outDescriptionIndex = ChainGetCount(trak->Media->information->sampleTable->SampleDescription->atomList); return e; }
// RFC3501: astring = 1*ASTRING-CHAR / string // string = quoted / literal // This function leaves us off with fCurrentTokenPlaceHolder immediately after // the end of the Astring. Call AdvanceToNextToken() to get the token after it. char *nsIMAPGenericParser::CreateAstring() { if (*fNextToken == '{') return CreateLiteral(); // literal else if (*fNextToken == '"') return CreateQuoted(); // quoted else return CreateAtom(true); // atom }
void MP4StblAtom::Generate() { // as usual MP4Atom::Generate(); // but we also need one of the chunk offset atoms MP4Atom* pChunkOffsetAtom; if (m_File.Use64Bits(GetType())) { pChunkOffsetAtom = CreateAtom(m_File, this, "co64"); } else { pChunkOffsetAtom = CreateAtom(m_File, this, "stco"); } AddChildAtom(pChunkOffsetAtom); // and ask it to self generate pChunkOffsetAtom->Generate(); }
M4Err M4_H263_NewStreamConfig(M4File *the_file, u32 trackNumber, LPH263CONFIGURATION param, char *URLname, char *URNname, u32 *outDescriptionIndex) { TrackAtom *trak; M4Err e; u32 dataRefIndex; TrackReferenceTypeAtom *dpnd; TrackReferenceAtom *tref; H263SampleEntryAtom *entry; e = CanAccessMovie((M4Movie *)the_file, M4_OPEN_WRITE); if (e) return e; trak = GetTrackFromFile(the_file, trackNumber); if (!trak || !trak->Media || !param) return M4BadParam; dpnd = NULL; tref = NULL; //get or create the data ref e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); if (e) return e; if (!dataRefIndex) { e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); if (e) return e; } trak->Media->mediaHeader->modificationTime = GetMP4Time(); //create a new entry entry = (H263SampleEntryAtom *) CreateAtom(H263SampleEntryAtomType); if (!entry) return M4OutOfMem; entry->h263_config = (H263ConfigAtom *) CreateAtom(H263ConfigAtomType); if (!entry->h263_config) { DelAtom((Atom *) entry); return M4OutOfMem; } entry->dataReferenceIndex = dataRefIndex; entry->h263_config->decoder_version = param->decoder_version; entry->h263_config->vendor = param->vendor; entry->h263_config->Level = param->Level; entry->h263_config->Profile = param->Profile; e = ChainAddEntry(trak->Media->information->sampleTable->SampleDescription->atomList, entry); *outDescriptionIndex = ChainGetCount(trak->Media->information->sampleTable->SampleDescription->atomList); return e; }
//set shadowing on/off M4Err M4_SetSyncShadowEnabled(M4File *the_file, u32 trackNumber, u8 SyncShadowEnabled) { TrackAtom *trak; SampleTableAtom *stbl; if (((M4Movie *)the_file)->openMode == M4_OPEN_READ) return M4InvalidMP4Mode; trak = GetTrackFromFile(the_file, trackNumber); if (!trak) return M4BadParam; stbl = trak->Media->information->sampleTable; if (SyncShadowEnabled) { if (!stbl->ShadowSync) stbl->ShadowSync = (ShadowSyncAtom *) CreateAtom(ShadowSyncAtomType); } else { if (stbl->ShadowSync) DelAtom((Atom *) stbl->ShadowSync); } return M4OK; }
//add an SDP line to the SDP container at the track level (media-specific SDP info) M4Err M4H_SDP_TrackAddLine(M4File *the_file, u32 trackNumber, const char *text) { TrackAtom *trak; UserDataMap *map; HintTrackInfoAtom *hnti; SDPAtom *sdp; M4Err e; char *buf; M4Err hnti_AddAtom(HintTrackInfoAtom *hnti, Atom *a); trak = GetTrackFromFile(the_file, trackNumber); if (!trak) return M4BadParam; //currently, only RTP hinting supports SDP if (!CheckHintFormat(trak, M4_Hint_RTP)) return M4BadParam; map = udta_getEntry(trak->udta, HintTrackInfoAtomType); if (!map) return M4InvalidMP4File; //we should have only one HNTI in the UDTA if (ChainGetCount(map->atomList) != 1) return M4InvalidMP4File; hnti = ChainGetEntry(map->atomList, 0); if (!hnti->SDP) { e = hnti_AddAtom(hnti, CreateAtom(SDPAtomType)); if (e) return e; } sdp = (SDPAtom *) hnti->SDP; if (!sdp->sdpText) { sdp->sdpText = malloc(sizeof(char) * (strlen(text) + 3)); strcpy(sdp->sdpText, text); strcat(sdp->sdpText, "\r\n"); return M4OK; } buf = malloc(sizeof(char) * (strlen(sdp->sdpText) + strlen(text) + 3)); strcpy(buf, sdp->sdpText); strcat(buf, text); strcat(buf, "\r\n"); free(sdp->sdpText); sdp->sdpText = buf; return M4OK; }
M4Err stbl_AddOffset(Atom **a, u64 offset) { ChunkOffsetAtom *stco; ChunkLargeOffsetAtom *co64; u32 i; if ((*a)->type == ChunkOffsetAtomType) { stco = (ChunkOffsetAtom *) *a; //if dataOffset is bigger than 0xFFFFFFFF, move to LARGE offset if (offset > 0xFFFFFFFF) { co64 = (ChunkLargeOffsetAtom *) CreateAtom(ChunkLargeOffsetAtomType); if (!co64) return M4OutOfMem; co64->entryCount = stco->entryCount + 1; co64->offsets = (u64*)malloc(co64->entryCount * sizeof(u64)); if (!co64->offsets) { DelAtom((Atom *)co64); return M4OutOfMem; } for (i = 0; i< co64->entryCount - 1; i++) { co64->offsets[i] = (u64) stco->offsets[i]; } co64->offsets[i] = offset; //delete the atom... DelAtom(*a); *a = (Atom *)co64; return M4OK; } //OK, stick with regular... stco->offsets = (u32*)realloc(stco->offsets, (stco->entryCount + 1) * sizeof(u32)); if (!stco->offsets) return M4OutOfMem; stco->offsets[stco->entryCount] = (u32) offset; stco->entryCount += 1; } else { //this is a large offset co64 = (ChunkLargeOffsetAtom *) *a; co64->offsets = (u64*)realloc(co64->offsets, (co64->entryCount + 1) * sizeof(u64)); if (!co64->offsets) return M4OutOfMem; co64->offsets[co64->entryCount] = offset; co64->entryCount += 1; } return M4OK; }
M4Err NewMedia(MediaAtom **mdia, u32 MediaType, u32 TimeScale) { MediaHeaderAtom *mdhd; Atom *mediaInfo; HandlerAtom *hdlr; MediaInformationAtom *minf; DataInformationAtom *dinf; SampleTableAtom *stbl; SampleDescriptionAtom *stsd; DataReferenceAtom *dref; char *str; M4Err stbl_AddAtom(SampleTableAtom *ptr, Atom *a); M4Err mdia_AddAtom(MediaAtom *ptr, Atom *a); M4Err dinf_AddAtom(DataInformationAtom *ptr, Atom *a); M4Err minf_AddAtom(MediaInformationAtom *ptr, Atom *a); M4Err e; if (*mdia || !mdia) return M4BadParam; e = M4OK; minf = NULL; mdhd = NULL; hdlr = NULL; dinf = NULL; stbl = NULL; stsd = NULL; dref = NULL; mediaInfo = NULL; //first create the media *mdia = (MediaAtom *) CreateAtom(MediaAtomType); mdhd = (MediaHeaderAtom *) CreateAtom(MediaHeaderAtomType); //"handler name" is for debugging purposes. Let's stick our name here ;) switch (MediaType) { case M4_VisualMediaType: mediaInfo = CreateAtom(VideoMediaHeaderAtomType); str = "GPAC ISO Video Handler"; break; case M4_AudioMediaType: mediaInfo = CreateAtom(SoundMediaHeaderAtomType); str = "GPAC ISO Audio Handler"; break; case M4_HintMediaType: mediaInfo = CreateAtom(HintMediaHeaderAtomType); str = "GPAC ISO Hint Handler"; break; case M4_ODMediaType: mediaInfo = CreateAtom(MPEGMediaHeaderAtomType); str = "GPAC MPEG-4 OD Handler"; break; case M4_OCRMediaType: mediaInfo = CreateAtom(MPEGMediaHeaderAtomType); str = "GPAC MPEG-4 OCR Handler"; break; case M4_BIFSMediaType: mediaInfo = CreateAtom(MPEGMediaHeaderAtomType); str = "GPAC MPEG-4 BIFS Handler"; break; case M4_MPEG7MediaType: mediaInfo = CreateAtom(MPEGMediaHeaderAtomType); str = "GPAC MPEG-4 MPEG-7 Handler"; break; case M4_OCIMediaType: mediaInfo = CreateAtom(MPEGMediaHeaderAtomType); str = "GPAC MPEG-4 OCI Handler"; break; case M4_IPMPMediaType: mediaInfo = CreateAtom(MPEGMediaHeaderAtomType); str = "GPAC MPEG-4 IPMP Handler"; break; case M4_MPEGJMediaType: mediaInfo = CreateAtom(MPEGMediaHeaderAtomType); str = "GPAC MPEG-4 MPEG-J Handler"; break; case M4_TimedTextMediaType: mediaInfo = CreateAtom(MPEGMediaHeaderAtomType); str = "GPAC Streaming Text Handler"; break; default: mediaInfo = CreateAtom(MPEGMediaHeaderAtomType); str = "GPAC IsoMedia Handler"; break; } hdlr = (HandlerAtom *) CreateAtom(HandlerAtomType); minf = (MediaInformationAtom *) CreateAtom(MediaInformationAtomType); mdhd->timeScale = TimeScale; hdlr->handlerType = MediaType; hdlr->nameLength = strlen(str) + 1; hdlr->nameUTF8 = (char*)malloc(sizeof(char) * hdlr->nameLength); if (!hdlr->nameUTF8) { e = M4OutOfMem; goto err_exit; } strcpy(hdlr->nameUTF8, str); //first set-up the sample table... stbl = (SampleTableAtom *) CreateAtom(SampleTableAtomType); dinf = (DataInformationAtom *) CreateAtom(DataInformationAtomType); //add all our sub-atoms in the sample table (only the mandatory ones) stbl->SampleDescription = (SampleDescriptionAtom *) CreateAtom(SampleDescriptionAtomType); stbl->ChunkOffset = CreateAtom(ChunkOffsetAtomType); //by default create a regular table stbl->SampleSize = (SampleSizeAtom *) CreateAtom(SampleSizeAtomType); stbl->SampleToChunk = (SampleToChunkAtom *) CreateAtom(SampleToChunkAtomType); stbl->TimeToSample = (TimeToSampleAtom *) CreateAtom(TimeToSampleAtomType); //Create a data reference WITHOUT DATA ENTRY (we don't know anything yet about the media Data) dref = (DataReferenceAtom *) CreateAtom(DataReferenceAtomType); e = dinf_AddAtom(dinf, (Atom *)dref); if (e) goto err_exit; e = minf_AddAtom(minf, (Atom *) mediaInfo); if (e) goto err_exit; e = minf_AddAtom(minf, (Atom *) stbl); if (e) goto err_exit; e = minf_AddAtom(minf, (Atom *) dinf); if (e) goto err_exit; e = mdia_AddAtom(*mdia, (Atom *) mdhd); if (e) goto err_exit; e = mdia_AddAtom(*mdia, (Atom *) minf); if (e) goto err_exit; e = mdia_AddAtom(*mdia, (Atom *) hdlr); if (e) goto err_exit; return M4OK; err_exit: if (mdhd) DelAtom((Atom *)mdhd); if (minf) DelAtom((Atom *)minf); if (hdlr) { if (hdlr->nameUTF8) free(hdlr->nameUTF8); DelAtom((Atom *)hdlr); } return e; }
M4Err ParseAtom(Atom **outAtom, BitStream *bs, u64 *read) { u32 type; u64 size; u8 uuid[16]; M4Err e; Atom *newAtom; char name[5]; name[4] = 0; e = M4OK; if ((bs == NULL) || (outAtom == NULL) ) return M4BadParam; *outAtom = NULL; //read atom header size = (u64) BS_ReadInt(bs, 32); *read = 4; /*fix for some atoms found in some old hinted files*/ if ((size >= 2) && (size <= 4)) { size = 4; type = VoidAtomType; } else { type = BS_ReadInt(bs, 32); *read += 4; /*no size means till end of file - EXCEPT FOR some old QuickTime atoms...*/ if (type == totlAtomType) size = 12; if (!size) size = BS_Available(bs) + 4; } /*handle uuid*/ memset(uuid, 0, 16); if (type == ExtendedAtomType ) { BS_ReadData(bs, (unsigned char *) uuid, 16); *read += 16; } //handle large atom if (size == 1) { size = BS_ReadInt(bs, 64); *read += 8; } if ( size - *read < 0 ) return M4InvalidMP4File; //OK, create the atom based on the type newAtom = CreateAtom(type); if (! newAtom) return M4OutOfMem; //OK, init and read this atom newAtom->size = size; memcpy(newAtom->uuid, uuid, 16); if (!newAtom->type) newAtom->type = type; if (newAtom->size - *read > BS_Available(bs) ) { *outAtom = newAtom; return M4UncompleteFile; } //we need a special reading for these atoms... if (newAtom->type == DegradationPriorityAtomType) { *outAtom = newAtom; return M4OK; } #if 0 /*perf test*/ switch (type) { case SampleTableAtomType: case TimeToSampleAtomType: case SampleToChunkAtomType: case CompactSampleSizeAtomType: case SampleSizeAtomType: case ChunkOffsetAtomType: case ChunkLargeOffsetAtomType: now = M4_GetSysClock(); e = ReadAtom(newAtom, bs); M4_MP4TypeToString(newAtom->type, name); printf("Read atom %s in %d ms\n", name, M4_GetSysClock() - now); break; default: e = ReadAtom(newAtom, bs, read); break; } #else e = ReadAtom(newAtom, bs, read); #endif //DON'T DELETE THE MDAT ATOM IF UNCOMPLETE... if (e < 0 && e != M4UncompleteFile) { DelAtom(newAtom); *outAtom = NULL; return e; } *outAtom = newAtom; return e; }
M4Err M4H_SetupHintTrack(M4File *the_file, u32 trackNumber, u32 HintType) { M4Err e; TrackAtom *trak; TrackReferenceAtom *tref; TrackReferenceTypeAtom *dpnd; HintMediaHeaderAtom *hmhd; //UDTA related ... UserDataAtom *udta; M4Err tref_AddAtom(TrackReferenceAtom *ptr, Atom *a); M4Err trak_AddAtom(TrackAtom *ptr, Atom *a); M4Err moov_AddAtom(MovieAtom *ptr, Atom *a); M4Err udta_AddAtom(UserDataAtom *ptr, Atom *a); //what do we support switch (HintType) { case M4_Hint_RTP: break; default: return M4NotSupported; } trak = GetTrackFromFile(the_file, trackNumber); if (!trak) return M4_GetLastError(the_file); if (((M4Movie *)the_file)->openMode != M4_OPEN_EDIT) return M4InvalidMP4Mode; //check we have a hint ... if ( !IsHintTrack(trak)) { return M4BadParam; } hmhd = (HintMediaHeaderAtom *)trak->Media->information->InfoHeader; //make sure the subtype was not already defined if (hmhd->subType) return M4HintPresent; //store the HintTrack format for later use... hmhd->subType = HintType; //hint tracks always have a tref and everything ... if (!trak->References) { if (!trak->References) { tref = (TrackReferenceAtom *) CreateAtom(TrackReferenceAtomType); e = trak_AddAtom(trak, (Atom *)tref); if (e) return e; } } tref = trak->References; //do we have a hint reference on this trak ??? e = Track_FindRef(trak, HintTrackReferenceAtomType, &dpnd); if (e) return e; //if yes, return false (existing hint track...) if (dpnd) return M4HintPresent; //create our dep dpnd = (TrackReferenceTypeAtom *) CreateAtom(HintTrackReferenceAtomType); e = tref_AddAtom(tref, (Atom *) dpnd); if (e) return e; //for RTP, we need to do some UDTA-related stuff... if (HintType != M4_Hint_RTP) return M4OK; if (!trak->udta) { //create one udta = (UserDataAtom *) CreateAtom(UserDataAtomType); e = trak_AddAtom(trak, (Atom *) udta); if (e) return e; } udta = trak->udta; //end-of-udta marker for Darwin... // if (!udta->voidAtom) { // e = udta_AddAtom(udta, CreateAtom(VoidAtomType)); // if (e) return e; // } //HNTI e = udta_AddAtom(udta, CreateAtom(HintTrackInfoAtomType)); if (e) return e; /* //NAME e = udta_AddAtom(udta, CreateAtom(nameAtomType)); if (e) return e; //HINF return udta_AddAtom(udta, CreateAtom(HintInfoAtomType)); */ return M4OK; }
// Update the dependancies when an OD AU is inserted in the file M4Err Media_ParseODFrame(MediaAtom *mdia, M4Sample *sample) { TrackReferenceAtom *tref; TrackReferenceTypeAtom *mpod; M4Err e; ODCommand *com; LPODCODEC ODencode; LPODCODEC ODdecode; u32 i, j; //the commands we proceed ESDescriptorUpdate *esdU, *esdU2; ESDescriptorRemove *esdR, *esdR2; ObjectDescriptorUpdate *odU, *odU2; //the desc they contain ObjectDescriptor *od; M4F_ObjectDescriptor *m4_od; ESDescriptor *esd; ES_ID_Ref *ref; Descriptor *desc; M4Err reftype_AddRefTrack(TrackReferenceTypeAtom *ref, u32 trackID, u16 *outRefIndex); M4Err trak_AddAtom(TrackAtom *ptr, Atom *a); M4Err tref_AddAtom(TrackReferenceAtom *ptr, Atom *a); if (!mdia || !sample || !sample->data || !sample->dataLength) return M4BadParam; //First find the references, and create them if none tref = mdia->mediaTrack->References; if (!tref) { tref = (TrackReferenceAtom *) CreateAtom(TrackReferenceAtomType); e = trak_AddAtom(mdia->mediaTrack, (Atom *) tref); if (e) return e; } //then find the OD reference, and create it if none e = Track_FindRef(mdia->mediaTrack, ODTrackReferenceAtomType, &mpod); if (e) return e; if (!mpod) { mpod = (TrackReferenceTypeAtom *) CreateAtom(ODTrackReferenceAtomType); e = tref_AddAtom(tref, (Atom *)mpod); if (e) return e; } //OK, create our codecs ODencode = OD_NewCodec(OD_WRITE); if (!ODencode) return M4OutOfMem; ODdecode = OD_NewCodec(OD_READ); if (!ODdecode) return M4OutOfMem; e = OD_SetBuffer(ODdecode, sample->data, sample->dataLength); if (e) goto err_exit; e = OD_DecodeAU(ODdecode); if (e) goto err_exit; while (1) { com = OD_GetCommand(ODdecode); if (!com) break; //check our commands switch (com->tag) { //Rewrite OD Update case ODUpdate_Tag: //duplicate our command odU = (ObjectDescriptorUpdate *) com; odU2 = (ObjectDescriptorUpdate *) OD_NewCommand(ODUpdate_Tag); for (i = 0; i < ChainGetCount(odU->objectDescriptors); i++) { desc = (Descriptor*)ChainGetEntry(odU->objectDescriptors, i); //both OD and IODs are accepted switch (desc->tag) { case ObjectDescriptor_Tag: case InitialObjectDescriptor_Tag: break; default: e = M4InvalidDescriptor; goto err_exit; } //get the esd e = OD_DuplicateDescriptor(desc, (Descriptor **)&od); if (e) goto err_exit; if (desc->tag == ObjectDescriptor_Tag) { m4_od = (M4F_ObjectDescriptor *) malloc(sizeof(M4F_ObjectDescriptor)); m4_od->tag = MP4_OD_Tag; } else { m4_od = (M4F_ObjectDescriptor *) malloc(sizeof(M4F_InitialObjectDescriptor)); m4_od->tag = MP4_IOD_Tag; //copy PL ((M4F_InitialObjectDescriptor *)m4_od)->inlineProfileFlag = ((InitialObjectDescriptor *)od)->inlineProfileFlag; ((M4F_InitialObjectDescriptor *)m4_od)->graphics_profileAndLevel = ((InitialObjectDescriptor *)od)->graphics_profileAndLevel; ((M4F_InitialObjectDescriptor *)m4_od)->audio_profileAndLevel = ((InitialObjectDescriptor *)od)->audio_profileAndLevel; ((M4F_InitialObjectDescriptor *)m4_od)->OD_profileAndLevel = ((InitialObjectDescriptor *)od)->OD_profileAndLevel; ((M4F_InitialObjectDescriptor *)m4_od)->scene_profileAndLevel = ((InitialObjectDescriptor *)od)->scene_profileAndLevel; ((M4F_InitialObjectDescriptor *)m4_od)->visual_profileAndLevel = ((InitialObjectDescriptor *)od)->visual_profileAndLevel; } //in OD stream only ref desc are accepted m4_od->ES_ID_RefDescriptors = NewChain(); m4_od->ES_ID_IncDescriptors = NULL; //TO DO: check that a given sampleDescription exists m4_od->extensionDescriptors = od->extensionDescriptors; od->extensionDescriptors = NULL; m4_od->IPMPDescriptorPointers = od->IPMPDescriptorPointers; od->IPMPDescriptorPointers = NULL; m4_od->OCIDescriptors = od->OCIDescriptors; od->OCIDescriptors = NULL; m4_od->URLString = od->URLString; od->URLString = NULL; m4_od->objectDescriptorID = od->objectDescriptorID; for (j = 0; j < ChainGetCount(od->ESDescriptors); j++) { esd =(ESDescriptor*)ChainGetEntry(od->ESDescriptors, j); ref = (ES_ID_Ref *) OD_NewDescriptor(ES_ID_RefTag); //1 to 1 mapping trackID and ESID. Add this track to MPOD //if track does not exist, this will be remove while reading the OD stream e = reftype_AddRefTrack(mpod, esd->ESID, &ref->trackRef); e = OD_AddDescToDesc((Descriptor *)m4_od, (Descriptor *)ref); if (e) goto err_exit; } //delete our desc OD_DeleteDescriptor((Descriptor **)&od); //and add the new one to our command ChainAddEntry(odU2->objectDescriptors, m4_od); } //delete the command OD_DeleteCommand((ODCommand **)&odU); //and add the new one to the codec OD_AddCommand(ODencode, (ODCommand *)odU2); break; //Rewrite ESD Update case ESDUpdate_Tag: esdU = (ESDescriptorUpdate *) com; esdU2 = (ESDescriptorUpdate *) OD_NewCommand(ESDUpdate_Tag); esdU2->ODID = esdU->ODID; for (i = 0; i< ChainGetCount(esdU->ESDescriptors); i++) { esd = (ESDescriptor*)ChainGetEntry(esdU->ESDescriptors, i); ref = (ES_ID_Ref *) OD_NewDescriptor(ES_ID_RefTag); //1 to 1 mapping trackID and ESID e = reftype_AddRefTrack(mpod, esd->ESID, &ref->trackRef); e = ChainAddEntry(esdU2->ESDescriptors, ref); if (e) goto err_exit; } OD_DeleteCommand((ODCommand **)&esdU); OD_AddCommand(ODencode, (ODCommand *)esdU2); break; //Brand new case: the ESRemove has to be rewritten too according to the specs... case ESDRemove_Tag: esdR = (ESDescriptorRemove *) com; esdR2 = (ESDescriptorRemove *) OD_NewCommand(ESDRemove_Tag); //change the tag for the file format esdR2->tag = ESDRemove_Ref_Tag; esdR2->ODID = esdR->ODID; esdR2->NbESDs = esdR->NbESDs; //alloc our stuff esdR2->ES_ID = (unsigned short*)malloc(sizeof(u32) * esdR->NbESDs); if (!esdR2->ES_ID) { e = M4OutOfMem; goto err_exit; } for (i = 0; i < esdR->NbESDs; i++) { //1 to 1 mapping trackID and ESID e = reftype_AddRefTrack(mpod, esdR->ES_ID[i], &esdR2->ES_ID[i]); if (e) goto err_exit; } OD_DeleteCommand(&com); OD_AddCommand(ODencode, (ODCommand *)esdR2); break; //Add the command as is default: e = OD_AddCommand(ODencode, com); if (e) goto err_exit; } } //encode our new AU e = OD_EncodeAU(ODencode); if (e) goto err_exit; //and set the buffer in the sample free(sample->data); sample->data = NULL; sample->dataLength = 0; e = OD_GetEncodedAU(ODencode, &sample->data, &sample->dataLength); err_exit: OD_DeleteCodec(ODencode); OD_DeleteCodec(ODdecode); return e; }
M4Err Track_SetStreamDescriptor(TrackAtom *trak, u32 StreamDescriptionIndex, u32 DataReferenceIndex, ESDescriptor *esd, u32 *outStreamIndex) { M4Err e; MPEGSampleEntryAtom *entry; MPEGVisualSampleEntryAtom *entry_v; MPEGAudioSampleEntryAtom *entry_a; TrackReferenceAtom *tref; TrackReferenceTypeAtom *dpnd; u16 tmpRef; M4Err reftype_AddRefTrack(TrackReferenceTypeAtom *ref, u32 trackID, u16 *outRefIndex); M4Err tref_AddAtom(TrackReferenceAtom *ptr, Atom *a); M4Err stsd_AddAtom(SampleDescriptionAtom *ptr, Atom *a); M4Err trak_AddAtom(TrackAtom *ptr, Atom *a); entry = NULL; tref = NULL; if (!trak || !esd || (!outStreamIndex && !DataReferenceIndex) ) return M4BadParam; if (!Track_IsMPEG4Stream(trak->Media->handler->handlerType)) return M4InvalidMP4Media; esd->ESID = 0; //set SL to predefined if no url if (esd->URLString == NULL) { esd->slConfig->predefined = SLPredef_MP4; esd->slConfig->durationFlag = 0; esd->slConfig->useTimestampsFlag = 1; } //get the REF atom if needed if (esd->dependsOnESID || esd->OCRESID ) { if (!trak->References) { tref = (TrackReferenceAtom *) CreateAtom(TrackReferenceAtomType); e = trak_AddAtom(trak, (Atom *)tref); if (e) return e; } tref = trak->References; } //Update Stream dependancies e = Track_FindRef(trak, M4_StreamDependence_Ref, &dpnd); if (e) return e; if (!dpnd && esd->dependsOnESID) { dpnd = (TrackReferenceTypeAtom *) CreateAtom(StreamDependenceAtomType); e = tref_AddAtom(tref, (Atom *) dpnd); if (e) return e; e = reftype_AddRefTrack(dpnd, esd->dependsOnESID, NULL); if (e) return e; } else if (dpnd && !esd->dependsOnESID) { Track_RemoveRef(trak, StreamDependenceAtomType); } esd->dependsOnESID = 0; //Update Clock dependancies e = Track_FindRef(trak, M4_OCR_Ref, &dpnd); if (e) return e; if (!dpnd && esd->OCRESID) { dpnd = (TrackReferenceTypeAtom *) CreateAtom(OCRReferenceAtomType); e = tref_AddAtom(tref, (Atom *) dpnd); if (e) return e; e = reftype_AddRefTrack(dpnd, esd->OCRESID, NULL); if (e) return e; } else if (dpnd && !esd->OCRESID) { Track_RemoveRef(trak, OCRReferenceAtomType); } else if (dpnd && esd->OCRESID) { if (dpnd->trackIDCount != 1) return M4InvalidMP4Media; dpnd->trackIDs[0] = esd->OCRESID; } esd->OCRESID = 0; //brand new case: we have to change the IPI desc if (esd->ipiPtr) { e = Track_FindRef(trak, M4_IPI_Ref, &dpnd); if (e) return e; if (!dpnd) { tmpRef = 0; dpnd = (TrackReferenceTypeAtom *) CreateAtom(IPIReferenceAtomType); e = tref_AddAtom(tref, (Atom *) dpnd); if (e) return e; e = reftype_AddRefTrack(dpnd, esd->ipiPtr->IPI_ES_Id, &tmpRef); if (e) return e; //and replace the tag and value... esd->ipiPtr->IPI_ES_Id = tmpRef; esd->ipiPtr->tag = IPI_DescPtr_Tag; } else { //Watch out! ONLY ONE IPI dependancy is allowed per stream if (dpnd->trackIDCount != 1) return M4InvalidMP4Media; //if an existing one is there, what shall we do ??? //donno, erase it dpnd->trackIDs[0] = esd->ipiPtr->IPI_ES_Id; //and replace the tag and value... esd->ipiPtr->IPI_ES_Id = 1; esd->ipiPtr->tag = IPI_DescPtr_Tag; } } //we have a streamDescritpionIndex, use it if (StreamDescriptionIndex) { entry = (MPEGSampleEntryAtom*)ChainGetEntry(trak->Media->information->sampleTable->SampleDescription->atomList, StreamDescriptionIndex - 1); if (!entry) return M4InvalidMP4File; switch (entry->type) { case MPEGSampleEntryAtomType: //OK, delete the previous ESD e = OD_DeleteDescriptor((Descriptor **) &entry->esd->desc); if (e) return e; entry->esd->desc = esd; break; case MPEGVisualSampleEntryAtomType: entry_v = (MPEGVisualSampleEntryAtom*) entry; //OK, delete the previous ESD e = OD_DeleteDescriptor((Descriptor **) &entry_v->esd->desc); if (e) return e; entry_v->esd->desc = esd; break; case MPEGAudioSampleEntryAtomType: entry_a = (MPEGAudioSampleEntryAtom*) entry; //OK, delete the previous ESD e = OD_DeleteDescriptor((Descriptor **) &entry_a->esd->desc); if (e) return e; entry_a->esd->desc = esd; break; } } else { //need to check we're not in URL mode where only ONE description is allowed... StreamDescriptionIndex = ChainGetCount(trak->Media->information->sampleTable->SampleDescription->atomList); if (StreamDescriptionIndex) { entry = (MPEGSampleEntryAtom*)ChainGetEntry(trak->Media->information->sampleTable->SampleDescription->atomList, StreamDescriptionIndex - 1); if (!entry) return M4InvalidMP4File; if (entry->esd->desc->URLString) return M4BadParam; } //OK, check the handler and create the entry switch (trak->Media->handler->handlerType) { case M4_VisualMediaType: entry_v = (MPEGVisualSampleEntryAtom *) CreateAtom(MPEGVisualSampleEntryAtomType); if (!entry_v) return M4OutOfMem; //create an esdAtom entry_v->esd = (ESDAtom *) CreateAtom(ESDAtomType); entry_v->esd->desc = esd; //type cast possible now entry = (MPEGSampleEntryAtom*) entry_v; break; case M4_AudioMediaType: entry_a = (MPEGAudioSampleEntryAtom *) CreateAtom(MPEGAudioSampleEntryAtomType); entry_a->samplerate_hi = trak->Media->mediaHeader->timeScale; if (!entry_a) return M4OutOfMem; //create an esdAtom entry_a->esd = (ESDAtom *) CreateAtom(ESDAtomType); entry_a->esd->desc = esd; //type cast possible now entry = (MPEGSampleEntryAtom*) entry_a; break; case M4_ODMediaType: case M4_OCRMediaType: case M4_BIFSMediaType: case M4_MPEG7MediaType: case M4_OCIMediaType: case M4_IPMPMediaType: case M4_MPEGJMediaType: entry = (MPEGSampleEntryAtom *) CreateAtom(MPEGSampleEntryAtomType); //create an esdAtom entry->esd = (ESDAtom *) CreateAtom(ESDAtomType); entry->esd->desc = esd; break; } entry->dataReferenceIndex = DataReferenceIndex; //and add the entry to our table... e = stsd_AddAtom(trak->Media->information->sampleTable->SampleDescription, (Atom *) entry); if (e) return e; if(outStreamIndex) *outStreamIndex = ChainGetCount(trak->Media->information->sampleTable->SampleDescription->atomList); } return M4OK; }
//This functions unpack the offset for easy editing, eg each sample //is contained in one chunk... M4Err stbl_UnpackOffsets(SampleTableAtom *stbl) { M4Err e; u8 isEdited; u32 i, chunkNumber, sampleDescIndex; u64 dataOffset; stscEntry *ent; ChunkOffsetAtom *stco_tmp; ChunkLargeOffsetAtom *co64_tmp; SampleToChunkAtom *stsc_tmp; if (!stbl) return M4InvalidMP4File; //we should have none of the mandatory atoms (allowed in the spec) if (!stbl->ChunkOffset && !stbl->SampleDescription && !stbl->SampleSize && !stbl->SampleToChunk && !stbl->TimeToSample) return M4OK; //or all the mandatory ones ... if (!stbl->ChunkOffset || !stbl->SampleDescription || !stbl->SampleSize || !stbl->SampleToChunk || !stbl->TimeToSample) return M4InvalidMP4File; //do we need to unpack? Not if we have only one sample per chunk. if (stbl->SampleSize->sampleCount == ChainGetCount(stbl->SampleToChunk->entryList)) return M4OK; //create a new SampleToChunk table stsc_tmp = (SampleToChunkAtom *) CreateAtom(SampleToChunkAtomType); //check the offset type and create a new table... if (stbl->ChunkOffset->type == ChunkOffsetAtomType) { co64_tmp = NULL; stco_tmp = (ChunkOffsetAtom *) CreateAtom(ChunkOffsetAtomType); stco_tmp->entryCount = stbl->SampleSize->sampleCount; stco_tmp->offsets = (u32*)malloc(stco_tmp->entryCount * sizeof(u32)); } else if (stbl->ChunkOffset->type == ChunkLargeOffsetAtomType) { stco_tmp = NULL; co64_tmp = (ChunkLargeOffsetAtom *) CreateAtom(ChunkLargeOffsetAtomType); co64_tmp->entryCount = stbl->SampleSize->sampleCount; co64_tmp->offsets = (u64*)malloc(co64_tmp->entryCount * sizeof(u64)); } else { return M4InvalidMP4File; } ent = NULL; //OK write our two tables... for (i = 0; i < stbl->SampleSize->sampleCount; i++) { //get the data info for the sample e = stbl_GetSampleInfos(stbl, i+1, &dataOffset, &chunkNumber, &sampleDescIndex, &isEdited); if (e) goto err_exit; ent = (stscEntry*)malloc(sizeof(stscEntry)); ent->isEdited = 0; ent->sampleDescriptionIndex = sampleDescIndex; //here's the trick: each sample is in ONE chunk ent->firstChunk = i+1; ent->nextChunk = i+2; ent->samplesPerChunk = 1; e = ChainAddEntry(stsc_tmp->entryList, ent); if (e) goto err_exit; if (stco_tmp) { stco_tmp->offsets[i] = (u32) dataOffset; } else { co64_tmp->offsets[i] = dataOffset; } } //close the list if (ent) ent->nextChunk = 0; //done, remove our previous tables DelAtom(stbl->ChunkOffset); DelAtom((Atom *)stbl->SampleToChunk); //and set these ones... if (stco_tmp) { stbl->ChunkOffset = (Atom *)stco_tmp; } else { stbl->ChunkOffset = (Atom *)co64_tmp; } stbl->SampleToChunk = stsc_tmp; stbl->SampleToChunk->currentEntry = (stscEntry*)ChainGetEntry(stbl->SampleToChunk->entryList, 0); stbl->SampleToChunk->currentIndex = 0; stbl->SampleToChunk->currentChunk = 0; stbl->SampleToChunk->firstSampleInCurrentChunk = 0; return M4OK; err_exit: if (stco_tmp) DelAtom((Atom *) stco_tmp); if (co64_tmp) DelAtom((Atom *) co64_tmp); if (stsc_tmp) DelAtom((Atom *) stsc_tmp); return e; }
//add an SDP line to the SDP container at the movie level (presentation SDP info) //NOTE: the \r\n end of line for SDP is automatically inserted M4Err M4H_SDP_MovieAddLine(M4File *the_file, const char *text) { M4Movie *mov; UserDataMap *map; RTPAtom *rtp; M4Err e; HintTrackInfoAtom *hnti; char *buf; M4Err udta_AddAtom(UserDataAtom *ptr, Atom *a); M4Err hnti_AddAtom(HintTrackInfoAtom *hnti, Atom *a); M4Err moov_AddAtom(MovieAtom *moov, Atom *a); mov = (M4Movie *)the_file; //check if we have a udta ... if (!mov->moov->udta) { e = moov_AddAtom(mov->moov, CreateAtom(UserDataAtomType)); if (e) return e; } //find a hnti in the udta map = udta_getEntry(mov->moov->udta, HintTrackInfoAtomType); if (!map) { e = udta_AddAtom(mov->moov->udta, CreateAtom(HintTrackInfoAtomType)); if (e) return e; map = udta_getEntry(mov->moov->udta, HintTrackInfoAtomType); } //there should be one and only one hnti if (!ChainGetCount(map->atomList) ) { e = udta_AddAtom(mov->moov->udta, CreateAtom(HintTrackInfoAtomType)); if (e) return e; } else if (ChainGetCount(map->atomList) < 1) return M4InvalidMP4File; hnti = ChainGetEntry(map->atomList, 0); if (!hnti->SDP) { //we have to create it by hand, as we have a duplication of atom type //(RTPSampleEntryAtom and RTPAtom have the same type...) rtp = malloc(sizeof(RTPAtom)); rtp->subType = SDPAtomType; rtp->type = RTPAtomType; rtp->sdpText = NULL; hnti_AddAtom(hnti, (Atom *)rtp); } rtp = (RTPAtom *) hnti->SDP; if (!rtp->sdpText) { rtp->sdpText = malloc(sizeof(char) * (strlen(text) + 3)); strcpy(rtp->sdpText, text); strcat(rtp->sdpText, "\r\n"); return M4OK; } buf = malloc(sizeof(char) * (strlen(rtp->sdpText) + strlen(text) + 3)); strcpy(buf, rtp->sdpText); strcat(buf, text); strcat(buf, "\r\n"); free(rtp->sdpText); rtp->sdpText = buf; return M4OK; }