GF_EXPORT const char *gf_isom_get_payt_info(GF_ISOFile *the_file, u32 trackNumber, u32 index, u32 *payID) { u32 i, count; GF_TrackBox *trak; GF_UserDataMap *map; GF_HintInfoBox *hinf; GF_PAYTBox *payt; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !index) return NULL; if (!CheckHintFormat(trak, GF_4CC('r', 't', 'p', ' '))) return NULL; map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HINF, NULL); if (!map) return NULL; if (gf_list_count(map->boxList) != 1) return NULL; hinf = (GF_HintInfoBox *)gf_list_get(map->boxList, 0); count = 0; i = 0; while ((payt = gf_list_enum(hinf->boxList, &i))) { if (payt->type == GF_ISOM_BOX_TYPE_PAYT) { count++; if (count == index) { if (payID) *payID=payt->payloadCode; return payt->payloadString; } } } return NULL; }
//remove all SDP info at the track level GF_Err gf_isom_sdp_clean_track(GF_ISOFile *the_file, u32 trackNumber) { GF_TrackBox *trak; GF_UserDataMap *map; GF_HintTrackInfoBox *hnti; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak) return GF_BAD_PARAM; //currently, only RTP hinting supports SDP if (!CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM; map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HNTI, NULL); if (!map) return GF_ISOM_INVALID_FILE; //we should have only one HNTI in the UDTA if (gf_list_count(map->boxList) != 1) return GF_ISOM_INVALID_FILE; hnti = (GF_HintTrackInfoBox *)gf_list_get(map->boxList, 0); if (!hnti->SDP) return GF_OK; //and free the SDP gf_free(((GF_SDPBox *)hnti->SDP)->sdpText); ((GF_SDPBox *)hnti->SDP)->sdpText = NULL; return GF_OK; }
GF_EXPORT u32 gf_isom_get_payt_count(GF_ISOFile *the_file, u32 trackNumber) { u32 i, count; GF_TrackBox *trak; GF_UserDataMap *map; GF_HintInfoBox *hinf; GF_PAYTBox *payt; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak) return 0; if (!CheckHintFormat(trak, GF_4CC('r', 't', 'p', ' '))) return 0; map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HINF, NULL); if (!map) return 0; if (gf_list_count(map->boxList) != 1) return 0; hinf = (GF_HintInfoBox *)gf_list_get(map->boxList, 0); count = 0; i = 0; while ((payt = gf_list_enum(hinf->boxList, &i))) { if (payt->type == GF_ISOM_BOX_TYPE_PAYT) count++; } return count; }
GF_Err gf_isom_rtp_packet_begin(GF_ISOFile *the_file, u32 trackNumber, s32 relativeTime, u8 PackingBit, u8 eXtensionBit, u8 MarkerBit, u8 PayloadType, u8 B_frame, u8 IsRepeatedPacket, u16 SequenceNumber) { GF_TrackBox *trak; GF_HintSampleEntryBox *entry; GF_RTPPacket *pck; u32 dataRefIndex; GF_Err e; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM; e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &dataRefIndex); if (e) return e; if (!entry->hint_sample) return GF_BAD_PARAM; pck = (GF_RTPPacket *) gf_isom_hint_pck_new(entry->hint_sample->HintType); pck->P_bit = PackingBit ? 1 : 0; pck->X_bit = eXtensionBit ? 1 : 0; pck->M_bit = MarkerBit ? 1 : 0; pck->payloadType = PayloadType; pck->SequenceNumber = SequenceNumber; pck->B_bit = B_frame ? 1 : 0; pck->R_bit = IsRepeatedPacket ? 1 : 0; pck->relativeTransTime = relativeTime; return gf_list_add(entry->hint_sample->packetTable, pck); }
//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 GF_Err gf_isom_rtp_packet_set_offset(GF_ISOFile *the_file, u32 trackNumber, s32 timeOffset) { GF_RTPOBox *rtpo; GF_TrackBox *trak; GF_HintSampleEntryBox *entry; GF_RTPPacket *pck; u32 dataRefIndex, i; GF_Err e; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM; e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &dataRefIndex); if (e) return e; if (!entry->hint_sample) return GF_BAD_PARAM; pck = (GF_RTPPacket *)gf_list_get(entry->hint_sample->packetTable, gf_list_count(entry->hint_sample->packetTable) - 1); if (!pck) return GF_BAD_PARAM; //look in the TLV i=0; while ((rtpo = (GF_RTPOBox *)gf_list_enum(pck->TLV, &i))) { if (rtpo->type == GF_ISOM_BOX_TYPE_RTPO) { rtpo->timeOffset = timeOffset; return GF_OK; } } //not found, add it rtpo = (GF_RTPOBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_RTPO); rtpo->timeOffset = timeOffset; return gf_list_add(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 GF_Err gf_isom_rtp_set_time_sequence_offset(GF_ISOFile *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 SequenceNumberOffset) { GF_TrackBox *trak; GF_HintSampleEntryBox *hdesc; u32 i, count; GF_SeqOffHintEntryBox *ent; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM; //OK, create a new HintSampleDesc hdesc = (GF_HintSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, HintDescriptionIndex - 1); count = gf_list_count(hdesc->HintDataTable); for (i=0; i< count; i++) { ent = (GF_SeqOffHintEntryBox *)gf_list_get(hdesc->HintDataTable, i); if (ent->type == GF_ISOM_BOX_TYPE_SNRO) { ent->SeqOffset = SequenceNumberOffset; return GF_OK; } } //we have to create a new entry... ent = (GF_SeqOffHintEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SNRO); ent->SeqOffset = SequenceNumberOffset; return gf_list_add(hdesc->HintDataTable, ent); }
GF_Err gf_isom_rtp_packet_set_flags(GF_ISOFile *the_file, u32 trackNumber, u8 PackingBit, u8 eXtensionBit, u8 MarkerBit, u8 disposable_packet, u8 IsRepeatedPacket) { GF_TrackBox *trak; GF_HintSampleEntryBox *entry; GF_RTPPacket *pck; u32 dataRefIndex, ind; GF_Err e; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM; e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &dataRefIndex); if (e) return e; if (!entry->hint_sample) return GF_BAD_PARAM; ind = gf_list_count(entry->hint_sample->packetTable); if (!ind) return GF_BAD_PARAM; pck = (GF_RTPPacket *)gf_list_get(entry->hint_sample->packetTable, ind-1); pck->P_bit = PackingBit ? 1 : 0; pck->X_bit = eXtensionBit ? 1 : 0; pck->M_bit = MarkerBit ? 1 : 0; pck->B_bit = disposable_packet ? 1 : 0; pck->R_bit = IsRepeatedPacket ? 1 : 0; return GF_OK; }
M4Err M4H_RTP_NewPacket(M4File *the_file, u32 trackNumber, s32 relativeTime, u8 PackingBit, u8 eXtensionBit, u8 MarkerBit, u8 PayloadType, u8 B_frame, u8 IsRepeatedPacket, u16 SequenceNumber) { TrackAtom *trak; HintSampleEntryAtom *entry; RTPPacket *pck; u32 dataRefIndex; 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 = (RTPPacket *) New_HintPacket(entry->w_sample->HintType); pck->P_bit = PackingBit ? 1 : 0; pck->X_bit = eXtensionBit ? 1 : 0; pck->M_bit = MarkerBit ? 1 : 0; pck->payloadType = PayloadType; pck->SequenceNumber = SequenceNumber; pck->B_bit = B_frame ? 1 : 0; pck->R_bit = IsRepeatedPacket ? 1 : 0; pck->relativeTransTime = relativeTime; return ChainAddEntry(entry->w_sample->packetTable, pck); }
//remove all SDP info at the track level M4Err M4H_SDP_CleanTrack(M4File *the_file, u32 trackNumber) { TrackAtom *trak; UserDataMap *map; HintTrackInfoAtom *hnti; 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) return M4OK; //and free the SDP free(((SDPAtom *)hnti->SDP)->sdpText); ((SDPAtom *)hnti->SDP)->sdpText = NULL; return M4OK; }
M4Err M4H_RTP_SetPacketFlags(M4File *the_file, u32 trackNumber, u8 PackingBit, u8 eXtensionBit, u8 MarkerBit, u8 B_frame, u8 IsRepeatedPacket) { TrackAtom *trak; HintSampleEntryAtom *entry; RTPPacket *pck; u32 dataRefIndex, ind; 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; ind = ChainGetCount(entry->w_sample->packetTable); if (!ind) return M4BadParam; pck = ChainGetEntry(entry->w_sample->packetTable, ind-1); pck->P_bit = PackingBit ? 1 : 0; pck->X_bit = eXtensionBit ? 1 : 0; pck->M_bit = MarkerBit ? 1 : 0; pck->B_bit = B_frame ? 1 : 0; pck->R_bit = IsRepeatedPacket ? 1 : 0; 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 GF_Err gf_isom_new_hint_description(GF_ISOFile *the_file, u32 trackNumber, s32 HintTrackVersion, s32 LastCompatibleVersion, u8 Rely, u32 *HintDescriptionIndex) { GF_Err e; u32 drefIndex; GF_TrackBox *trak; GF_HintSampleEntryBox *hdesc; GF_RelyHintBox *relyA; e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); if (e) return e; trak = gf_isom_get_track_from_file(the_file, trackNumber); *HintDescriptionIndex = 0; if (!trak || !IsHintTrack(trak)) return GF_BAD_PARAM; //OK, create a new HintSampleDesc hdesc = (GF_HintSampleEntryBox *) gf_isom_box_new(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_AddBox(trak->Media->information->sampleTable->SampleDescription, (GF_Box *) hdesc); if (e) return e; *HintDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); //RTP needs a default timeScale... use the media one. if (CheckHintFormat(trak, GF_ISOM_HINT_RTP)) { e = gf_isom_rtp_set_timescale(the_file, trackNumber, *HintDescriptionIndex, trak->Media->mediaHeader->timeScale); if (e) return e; } if (!Rely) return GF_OK; //we need a rely box (common to all protocols) relyA = (GF_RelyHintBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_RELY); if (Rely == 1) { relyA->prefered = 1; } else { relyA->required = 1; } return gf_list_add(hdesc->HintDataTable, relyA); }
//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); }
//add an SDP line to the SDP container at the track level (media-specific SDP info) GF_EXPORT GF_Err gf_isom_sdp_add_track_line(GF_ISOFile *the_file, u32 trackNumber, const char *text) { GF_TrackBox *trak; GF_UserDataMap *map; GF_HintTrackInfoBox *hnti; GF_SDPBox *sdp; GF_Err e; char *buf; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak) return GF_BAD_PARAM; //currently, only RTP hinting supports SDP if (!CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM; map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HNTI, NULL); if (!map) return GF_ISOM_INVALID_FILE; //we should have only one HNTI in the UDTA if (gf_list_count(map->other_boxes) != 1) return GF_ISOM_INVALID_FILE; hnti = (GF_HintTrackInfoBox *)gf_list_get(map->other_boxes, 0); if (!hnti->SDP) { e = hnti_AddBox(hnti, gf_isom_box_new(GF_ISOM_BOX_TYPE_SDP)); if (e) return e; } sdp = (GF_SDPBox *) hnti->SDP; if (!sdp->sdpText) { sdp->sdpText = (char *)gf_malloc(sizeof(char) * (strlen(text) + 3)); strcpy(sdp->sdpText, text); strcat(sdp->sdpText, "\r\n"); return GF_OK; } buf = (char *)gf_malloc(sizeof(char) * (strlen(sdp->sdpText) + strlen(text) + 3)); strcpy(buf, sdp->sdpText); strcat(buf, text); strcat(buf, "\r\n"); gf_free(sdp->sdpText); ReorderSDP(buf, GF_FALSE); sdp->sdpText = buf; return GF_OK; }
//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; }