root/src/isomedia/hinting.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ghnt_New
  2. ghnt_del
  3. ghnt_Read
  4. ghnt_Write
  5. ghnt_Size
  6. gf_isom_hint_sample_new
  7. gf_isom_hint_sample_del
  8. gf_isom_hint_sample_read
  9. gf_isom_hint_sample_write
  10. gf_isom_hint_sample_size
  11. gf_isom_hint_pck_new
  12. gf_isom_hint_pck_del
  13. gf_isom_hint_pck_read
  14. gf_isom_hint_pck_write
  15. gf_isom_hint_pck_size
  16. gf_isom_hint_pck_offset
  17. gf_isom_hint_pck_add_dte
  18. gf_isom_hint_pck_length
  19. New_EmptyDTE
  20. New_ImmediateDTE
  21. New_SampleDTE
  22. New_StreamDescDTE
  23. NewDTE
  24. Del_EmptyDTE
  25. Del_ImmediateDTE
  26. Del_SampleDTE
  27. Del_StreamDescDTE
  28. DelDTE
  29. Read_EmptyDTE
  30. Read_ImmediateDTE
  31. Read_SampleDTE
  32. Read_StreamDescDTE
  33. ReadDTE
  34. Write_EmptyDTE
  35. Write_ImmediateDTE
  36. Write_SampleDTE
  37. Write_StreamDescDTE
  38. WriteDTE
  39. OffsetDTE
  40. gf_isom_hint_rtp_new
  41. gf_isom_hint_rtp_del
  42. gf_isom_hint_rtp_read
  43. gf_isom_hint_rtp_offset
  44. gf_isom_hint_rtp_length
  45. gf_isom_hint_rtp_size
  46. gf_isom_hint_rtp_write
  47. gf_isom_hint_rtcp_new
  48. gf_isom_hint_rtcp_del
  49. gf_isom_hint_rtcp_read
  50. gf_isom_hint_rtcp_length
  51. gf_isom_hint_rtcp_size
  52. gf_isom_hint_rtcp_write
  53. gf_isom_reset_hint_reader
  54. gf_isom_load_next_hint_sample
  55. gf_isom_get_data_sample
  56. gf_isom_next_hint_packet

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Jean Le Feuvre
 *                      Copyright (c) Telecom ParisTech 2000-2012
 *                                      All rights reserved
 *
 *  This file is part of GPAC / ISO Media File Format sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <gpac/internal/isomedia_dev.h>

#if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_HINTING)

GF_Box *ghnt_New()
{
        GF_HintSampleEntryBox *tmp;
        GF_SAFEALLOC(tmp, GF_HintSampleEntryBox);
        if (tmp == NULL) return NULL;

        //this type is used internally for protocols that share the same base entry
        //currently only RTP uses this, but a flexMux could use this entry too...
        tmp->type = GF_ISOM_BOX_TYPE_GHNT;
        tmp->HintTrackVersion = 1;
        tmp->LastCompatibleVersion = 1;
        return (GF_Box *)tmp;
}

void ghnt_del(GF_Box *s)
{
        GF_HintSampleEntryBox *ptr;

        ptr = (GF_HintSampleEntryBox *)s;
        if (ptr->hint_sample) gf_isom_hint_sample_del(ptr->hint_sample);
        gf_free(ptr);
}

GF_Err ghnt_Read(GF_Box *s, GF_BitStream *bs)
{
        GF_Err e;
        GF_HintSampleEntryBox *ptr = (GF_HintSampleEntryBox *)s;
        if (ptr == NULL) return GF_BAD_PARAM;

        if (ptr->size < 16) return GF_ISOM_INVALID_FILE;

        e = gf_isom_base_sample_entry_read((GF_SampleEntryBox *)ptr, bs);
        if (e) return e;

        ptr->HintTrackVersion = gf_bs_read_u16(bs);
        ptr->LastCompatibleVersion = gf_bs_read_u16(bs);

        ptr->size -= 12;
        if ((s->type == GF_ISOM_BOX_TYPE_RTP_STSD) || (s->type == GF_ISOM_BOX_TYPE_SRTP_STSD) || (s->type == GF_ISOM_BOX_TYPE_RRTP_STSD) || (s->type == GF_ISOM_BOX_TYPE_RTCP_STSD)) {
                ptr->MaxPacketSize = gf_bs_read_u32(bs);
                ptr->size -= 4;
        } else if (s->type == GF_ISOM_BOX_TYPE_FDP_STSD) {
                ptr->partition_entry_ID = gf_bs_read_u16(bs);
                ptr->FEC_overhead = gf_bs_read_u16(bs);
                ptr->size -= 4;

        }
        return gf_isom_box_array_read(s, bs, gf_isom_box_add_default);
}

#ifndef GPAC_DISABLE_ISOM_WRITE

GF_Err ghnt_Write(GF_Box *s, GF_BitStream *bs)
{
        GF_Err e;
        GF_HintSampleEntryBox *ptr = (GF_HintSampleEntryBox *)s;

        e = gf_isom_box_write_header(s, bs);
        if (e) return e;
        gf_bs_write_data(bs, ptr->reserved, 6);
        gf_bs_write_u16(bs, ptr->dataReferenceIndex);
        gf_bs_write_u16(bs, ptr->HintTrackVersion);
        gf_bs_write_u16(bs, ptr->LastCompatibleVersion);
        gf_bs_write_u32(bs, ptr->MaxPacketSize);
        return GF_OK;
}

GF_Err ghnt_Size(GF_Box *s)
{
        GF_HintSampleEntryBox *ptr = (GF_HintSampleEntryBox *)s;
        ptr->size += 16;
        return GF_OK;
}


#endif /*GPAC_DISABLE_ISOM_WRITE*/


GF_HintSample *gf_isom_hint_sample_new(u32 ProtocolType)
{
        GF_HintSample *tmp;
        switch (ProtocolType) {
        case GF_ISOM_BOX_TYPE_RTP_STSD:
        case GF_ISOM_BOX_TYPE_SRTP_STSD:
        case GF_ISOM_BOX_TYPE_RRTP_STSD:
        case GF_ISOM_BOX_TYPE_RTCP_STSD:
                break;
        case GF_ISOM_BOX_TYPE_FDP_STSD:
                return (GF_HintSample *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FDSA);
                break;
        default:
                return NULL;
        }
        GF_SAFEALLOC(tmp, GF_HintSample);
        if (!tmp) return NULL;
        tmp->packetTable = gf_list_new();
        tmp->hint_subtype = ProtocolType;
        return tmp;
}

void gf_isom_hint_sample_del(GF_HintSample *ptr)
{
        GF_HintPacket *pck;

        if (ptr->hint_subtype==GF_ISOM_BOX_TYPE_FDP_STSD) {
                gf_isom_box_del((GF_Box*)ptr);
                return;
        }

        while (gf_list_count(ptr->packetTable)) {
                pck = (GF_HintPacket *)gf_list_get(ptr->packetTable, 0);
                gf_isom_hint_pck_del(pck);
                gf_list_rem(ptr->packetTable, 0);
        }
        gf_list_del(ptr->packetTable);
        if (ptr->AdditionalData) gf_free(ptr->AdditionalData);

        if (ptr->sample_cache) {
                while (gf_list_count(ptr->sample_cache)) {
                        GF_HintDataCache *hdc = (GF_HintDataCache *)gf_list_get(ptr->sample_cache, 0);
                        gf_list_rem(ptr->sample_cache, 0);
                        if (hdc->samp) gf_isom_sample_del(&hdc->samp);
                        gf_free(hdc);
                }
                gf_list_del(ptr->sample_cache);
        }
        if (ptr->extra_data)
                gf_isom_box_del((GF_Box*)ptr->extra_data);
        if (ptr->other_boxes)
                gf_isom_box_array_del(ptr->other_boxes);

        gf_free(ptr);
}

GF_Err gf_isom_hint_sample_read(GF_HintSample *ptr, GF_BitStream *bs, u32 sampleSize)
{
        u16 i;
        u32 type;
        GF_HintPacket *pck;
        GF_Err e;
        char *szName = (ptr->hint_subtype==GF_ISOM_BOX_TYPE_RTCP_STSD) ? "RTCP" : "RTP";
        u64 sizeIn, sizeOut;

        sizeIn = gf_bs_available(bs);

        switch (ptr->hint_subtype) {
        case GF_ISOM_BOX_TYPE_RTP_STSD:
        case GF_ISOM_BOX_TYPE_SRTP_STSD:
        case GF_ISOM_BOX_TYPE_RRTP_STSD:
        case GF_ISOM_BOX_TYPE_RTCP_STSD:
                break;
        case GF_ISOM_BOX_TYPE_FDP_STSD:
                ptr->size = gf_bs_read_u32(bs);
                type = gf_bs_read_u32(bs);
                if (type != GF_ISOM_BOX_TYPE_FDSA) {
                        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso] invalid FDT sample, top box type %s not fdsa\n", gf_4cc_to_str(type) ));
                        return GF_ISOM_INVALID_MEDIA;
                }
                return gf_isom_box_read((GF_Box*)ptr, bs);
        default:
                return GF_NOT_SUPPORTED;
        }

        ptr->packetCount = gf_bs_read_u16(bs);
        ptr->reserved = gf_bs_read_u16(bs);
        if (ptr->packetCount>=sampleSize) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso] broken %s sample: %d packet_count indicated but only %d bytes in samples\n", szName, ptr->packetCount, sampleSize));
                return GF_ISOM_INVALID_MEDIA;
        }
        
        for (i = 0; i < ptr->packetCount; i++) {
                if (! gf_bs_available(bs) ) {
                        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso] %s hint sample has no more data but still %d entries to read\n", szName, ptr->packetCount-i));
                        return GF_ISOM_INVALID_MEDIA;
                }
                pck = gf_isom_hint_pck_new(ptr->hint_subtype);
                pck->trackID = ptr->trackID;
                pck->sampleNumber = ptr->sampleNumber;
                gf_list_add(ptr->packetTable, pck);

                e = gf_isom_hint_pck_read(pck, bs);
                if (e) return e;
        }

        if (ptr->hint_subtype==GF_ISOM_BOX_TYPE_RTCP_STSD) return GF_OK;


        sizeOut = gf_bs_available(bs) - sizeIn;

        //do we have some more data after the packets ??
        if ((u32)sizeOut < sampleSize) {
                ptr->dataLength = sampleSize - (u32)sizeOut;
                ptr->AdditionalData = (char*)gf_malloc(sizeof(char) * ptr->dataLength);
                gf_bs_read_data(bs, ptr->AdditionalData, ptr->dataLength);
        }
        return GF_OK;
}


#ifndef GPAC_DISABLE_ISOM_WRITE

GF_Err gf_isom_hint_sample_write(GF_HintSample *ptr, GF_BitStream *bs)
{
        u32 count, i;
        GF_HintPacket *pck;
        GF_Err e;

        if (ptr->hint_subtype==GF_ISOM_BOX_TYPE_FDP_STSD) {
                e = gf_isom_box_size((GF_Box*)ptr);
                if (!e) e = gf_isom_box_write((GF_Box*)ptr, bs);
                return e;
        }

        count = gf_list_count(ptr->packetTable);
        gf_bs_write_u16(bs, count);
        gf_bs_write_u16(bs, ptr->reserved);
        //write the packet table
        for (i=0; i<count; i++) {
                pck = (GF_HintPacket *)gf_list_get(ptr->packetTable, i);
                e = gf_isom_hint_pck_write(pck, bs);
                if (e) return e;
        }
        //write additional data
        if (ptr->AdditionalData) {
                gf_bs_write_data(bs, ptr->AdditionalData, ptr->dataLength);
        }
        return GF_OK;
}


u32 gf_isom_hint_sample_size(GF_HintSample *ptr)
{
        u32 size, count, i;
        GF_HintPacket *pck;

        if (ptr->hint_subtype==GF_ISOM_BOX_TYPE_FDP_STSD) {
                gf_isom_box_size((GF_Box*)ptr);
                size = (u32) ptr->size;
        } else {
                size = 4;
                count = gf_list_count(ptr->packetTable);
                for (i=0; i<count; i++) {
                        pck = (GF_HintPacket *)gf_list_get(ptr->packetTable, i);
                        size += gf_isom_hint_pck_size(pck);
                }
                size += ptr->dataLength;
        }
        return size;
}

#endif /*GPAC_DISABLE_ISOM_WRITE*/



GF_HintPacket *gf_isom_hint_pck_new(u32 HintType)
{
        GF_HintPacket *pck;
        switch (HintType) {
        case GF_ISOM_BOX_TYPE_RTP_STSD:
        case GF_ISOM_BOX_TYPE_SRTP_STSD:
        case GF_ISOM_BOX_TYPE_RRTP_STSD:
                pck = (GF_HintPacket *) gf_isom_hint_rtp_new();
                if (pck) pck->hint_subtype = HintType;
                return pck;
        case GF_ISOM_BOX_TYPE_RTCP_STSD:
                pck = (GF_HintPacket *) gf_isom_hint_rtcp_new();
                if (pck) pck->hint_subtype = HintType;
                return pck;
        default:
                return NULL;
        }
}

void gf_isom_hint_pck_del(GF_HintPacket *ptr)
{
        if (!ptr) return;
        switch (ptr->hint_subtype) {
        case GF_ISOM_BOX_TYPE_RTP_STSD:
        case GF_ISOM_BOX_TYPE_SRTP_STSD:
        case GF_ISOM_BOX_TYPE_RRTP_STSD:
                gf_isom_hint_rtp_del((GF_RTPPacket *)ptr);
                break;
        case GF_ISOM_BOX_TYPE_RTCP_STSD:
                gf_isom_hint_rtcp_del((GF_RTCPPacket *)ptr);
                break;
        default:
                break;
        }
}

GF_Err gf_isom_hint_pck_read(GF_HintPacket *ptr, GF_BitStream *bs)
{
        if (!ptr) return GF_BAD_PARAM;
        switch (ptr->hint_subtype) {
        case GF_ISOM_BOX_TYPE_RTP_STSD:
        case GF_ISOM_BOX_TYPE_SRTP_STSD:
        case GF_ISOM_BOX_TYPE_RRTP_STSD:
                return gf_isom_hint_rtp_read((GF_RTPPacket *)ptr, bs);
        case GF_ISOM_BOX_TYPE_RTCP_STSD:
                return gf_isom_hint_rtcp_read((GF_RTCPPacket *)ptr, bs);
        default:
                return GF_NOT_SUPPORTED;
        }
}

#ifndef GPAC_DISABLE_ISOM_WRITE

GF_Err gf_isom_hint_pck_write(GF_HintPacket *ptr, GF_BitStream *bs)
{
        if (!ptr) return GF_BAD_PARAM;
        switch (ptr->hint_subtype) {
        case GF_ISOM_BOX_TYPE_RTP_STSD:
        case GF_ISOM_BOX_TYPE_SRTP_STSD:
        case GF_ISOM_BOX_TYPE_RRTP_STSD:
                return gf_isom_hint_rtp_write((GF_RTPPacket *)ptr, bs);
        case GF_ISOM_BOX_TYPE_RTCP_STSD:
                return gf_isom_hint_rtcp_write((GF_RTCPPacket *)ptr, bs);
        default:
                return GF_NOT_SUPPORTED;
        }
}

u32 gf_isom_hint_pck_size(GF_HintPacket *ptr)
{
        if (!ptr) return GF_BAD_PARAM;
        switch (ptr->hint_subtype) {
        case GF_ISOM_BOX_TYPE_RTP_STSD:
        case GF_ISOM_BOX_TYPE_SRTP_STSD:
        case GF_ISOM_BOX_TYPE_RRTP_STSD:
                return gf_isom_hint_rtp_size((GF_RTPPacket *)ptr);
        case GF_ISOM_BOX_TYPE_RTCP_STSD:
                return gf_isom_hint_rtcp_size((GF_RTCPPacket *)ptr);
        default:
                return 0;
        }
}

GF_Err gf_isom_hint_pck_offset(GF_HintPacket *ptr, u32 offset, u32 HintSampleNumber)
{
        if (!ptr) return GF_BAD_PARAM;
        switch (ptr->hint_subtype) {
        case GF_ISOM_BOX_TYPE_RTP_STSD:
        case GF_ISOM_BOX_TYPE_SRTP_STSD:
        case GF_ISOM_BOX_TYPE_RRTP_STSD:
                return gf_isom_hint_rtp_offset((GF_RTPPacket *)ptr, offset, HintSampleNumber);
        case GF_ISOM_BOX_TYPE_RTCP_STSD:
                return GF_BAD_PARAM;
        default:
                return GF_NOT_SUPPORTED;
        }
}

GF_Err gf_isom_hint_pck_add_dte(GF_HintPacket *ptr, GF_GenericDTE *dte, u8 AtBegin)
{
        if (!ptr) return GF_BAD_PARAM;
        switch (ptr->hint_subtype) {
        case GF_ISOM_BOX_TYPE_RTP_STSD:
        case GF_ISOM_BOX_TYPE_SRTP_STSD:
        case GF_ISOM_BOX_TYPE_RRTP_STSD:
                if (AtBegin)
                        return gf_list_insert( ((GF_RTPPacket *)ptr)->DataTable, dte, 0);
                else
                        return gf_list_add( ((GF_RTPPacket *)ptr)->DataTable, dte);

        case GF_ISOM_BOX_TYPE_RTCP_STSD:
                return GF_BAD_PARAM;
        default:
                return GF_NOT_SUPPORTED;
        }
}

u32 gf_isom_hint_pck_length(GF_HintPacket *ptr)
{
        if (!ptr) return 0;
        switch (ptr->hint_subtype) {
        case GF_ISOM_BOX_TYPE_RTP_STSD:
        case GF_ISOM_BOX_TYPE_SRTP_STSD:
        case GF_ISOM_BOX_TYPE_RRTP_STSD:
                return gf_isom_hint_rtp_length((GF_RTPPacket *)ptr);
        case GF_ISOM_BOX_TYPE_RTCP_STSD:
                return gf_isom_hint_rtcp_length((GF_RTCPPacket *)ptr);
        default:
                return 0;
        }
}


#endif /*GPAC_DISABLE_ISOM_WRITE*/



/********************************************************************
                Creation of DataTable entries in the RTP sample
********************************************************************/

GF_GenericDTE *New_EmptyDTE()
{
        GF_EmptyDTE *dte = (GF_EmptyDTE *)gf_malloc(sizeof(GF_EmptyDTE));
        dte->source = 0;
        return (GF_GenericDTE *)dte;
}

GF_GenericDTE *New_ImmediateDTE()
{
        GF_ImmediateDTE *dte;
        GF_SAFEALLOC(dte, GF_ImmediateDTE);
        if (dte) {
                dte->source = 1;
                dte->dataLength = 0;
        }
        return (GF_GenericDTE *)dte;
}

GF_GenericDTE *New_SampleDTE()
{
        GF_SampleDTE *dte = (GF_SampleDTE *)gf_malloc(sizeof(GF_SampleDTE));
        dte->source = 2;
        //can be -1 in QT , so init at -2
        dte->trackRefIndex = (s8) -2;
        dte->dataLength = 0;
        dte->sampleNumber = 0;
        dte->samplesPerComp = 1;
        dte->byteOffset = 0;
        dte->bytesPerComp = 1;
        return (GF_GenericDTE *)dte;
}

GF_GenericDTE *New_StreamDescDTE()
{
        GF_StreamDescDTE *dte = (GF_StreamDescDTE *)gf_malloc(sizeof(GF_StreamDescDTE));
        dte->source = 3;
        dte->byteOffset = 0;
        dte->dataLength = 0;
        dte->reserved = 0;
        dte->streamDescIndex = 0;
        //can be -1 in QT , so init at -2
        dte->trackRefIndex = (s8) -2;
        return (GF_GenericDTE *)dte;
}

//creation of DTEs
GF_GenericDTE *NewDTE(u8 type)
{
        switch (type) {
        case 0:
                return New_EmptyDTE();
        case 1:
                return New_ImmediateDTE();
        case 2:
                return New_SampleDTE();
        case 3:
                return New_StreamDescDTE();
        default:
                return NULL;
        }
}

/********************************************************************
                Deletion of DataTable entries in the RTP sample
********************************************************************/
void Del_EmptyDTE(GF_EmptyDTE *dte)
{
        gf_free(dte);
}

void Del_ImmediateDTE(GF_ImmediateDTE *dte)
{
        gf_free(dte);
}

void Del_SampleDTE(GF_SampleDTE *dte)
{
        gf_free(dte);
}

void Del_StreamDescDTE(GF_StreamDescDTE *dte)
{
        gf_free(dte);
}

//deletion of DTEs
void DelDTE(GF_GenericDTE *dte)
{
        switch (dte->source) {
        case 0:
                Del_EmptyDTE((GF_EmptyDTE *)dte);
                break;
        case 1:
                Del_ImmediateDTE((GF_ImmediateDTE *)dte);
                break;
        case 2:
                Del_SampleDTE((GF_SampleDTE *)dte);
                break;
        case 3:
                Del_StreamDescDTE((GF_StreamDescDTE *)dte);
                break;
        default:
                return;
        }
}



/********************************************************************
                Reading of DataTable entries in the RTP sample
********************************************************************/
GF_Err Read_EmptyDTE(GF_EmptyDTE *dte, GF_BitStream *bs)
{
        char empty[15];
        //empty but always 15 bytes !!!
        gf_bs_read_data(bs, empty, 15);
        return GF_OK;
}

GF_Err Read_ImmediateDTE(GF_ImmediateDTE *dte, GF_BitStream *bs)
{
        dte->dataLength = gf_bs_read_u8(bs);
        if (dte->dataLength > 14) return GF_ISOM_INVALID_FILE;
        gf_bs_read_data(bs, dte->data, dte->dataLength);
        if (dte->dataLength < 14) gf_bs_skip_bytes(bs, 14 - dte->dataLength);
        return GF_OK;
}

GF_Err Read_SampleDTE(GF_SampleDTE *dte, GF_BitStream *bs)
{
        dte->trackRefIndex = (s8) gf_bs_read_u8(bs);
        dte->dataLength = gf_bs_read_u16(bs);
        dte->sampleNumber = gf_bs_read_u32(bs);
        dte->byteOffset = gf_bs_read_u32(bs);
        dte->bytesPerComp = gf_bs_read_u16(bs);
        dte->samplesPerComp = gf_bs_read_u16(bs);
        if (dte->bytesPerComp != 1) {
                GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso] hint packet constructor with bytesperblock %d, not 1\n", dte->bytesPerComp));
        }
        if (dte->samplesPerComp != 1) {
                GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso] hint packet constructor with samplesperblock %d, not 1\n", dte->bytesPerComp));
        }
        return GF_OK;
}

GF_Err Read_StreamDescDTE(GF_StreamDescDTE *dte, GF_BitStream *bs)
{
        dte->trackRefIndex = gf_bs_read_u8(bs);
        dte->dataLength = gf_bs_read_u16(bs);
        dte->streamDescIndex = gf_bs_read_u32(bs);
        dte->byteOffset = gf_bs_read_u32(bs);
        dte->reserved = gf_bs_read_u32(bs);
        return GF_OK;
}

GF_Err ReadDTE(GF_GenericDTE *dte, GF_BitStream *bs)
{
        switch (dte->source) {
        case 0:
                //nothing to o, it is an empty entry
                return Read_EmptyDTE((GF_EmptyDTE *)dte, bs);
        case 1:
                return Read_ImmediateDTE((GF_ImmediateDTE *)dte, bs);
        case 2:
                return Read_SampleDTE((GF_SampleDTE *)dte, bs);
        case 3:
                return Read_StreamDescDTE((GF_StreamDescDTE *)dte, bs);
        default:
                return GF_ISOM_INVALID_FILE;
        }
}

/********************************************************************
                Writing of DataTable entries in the RTP sample
********************************************************************/
GF_Err Write_EmptyDTE(GF_EmptyDTE *dte, GF_BitStream *bs)
{
        gf_bs_write_u8(bs, dte->source);
        //empty but always 15 bytes !!!
        gf_bs_write_data(bs, "empty hint DTE", 15);
        return GF_OK;
}

GF_Err Write_ImmediateDTE(GF_ImmediateDTE *dte, GF_BitStream *bs)
{
        char data[14];
        gf_bs_write_u8(bs, dte->source);
        gf_bs_write_u8(bs, dte->dataLength);
        gf_bs_write_data(bs, dte->data, dte->dataLength);
        if (dte->dataLength < 14) {
                memset(data, 0, 14);
                gf_bs_write_data(bs, data, 14 - dte->dataLength);
        }
        return GF_OK;
}

GF_Err Write_SampleDTE(GF_SampleDTE *dte, GF_BitStream *bs)
{
        gf_bs_write_u8(bs, dte->source);
        gf_bs_write_u8(bs, dte->trackRefIndex);
        gf_bs_write_u16(bs, dte->dataLength);
        gf_bs_write_u32(bs, dte->sampleNumber);
        gf_bs_write_u32(bs, dte->byteOffset);
        gf_bs_write_u16(bs, dte->bytesPerComp);
        gf_bs_write_u16(bs, dte->samplesPerComp);
        return GF_OK;
}

GF_Err Write_StreamDescDTE(GF_StreamDescDTE *dte, GF_BitStream *bs)
{
        gf_bs_write_u8(bs, dte->source);

        gf_bs_write_u8(bs, dte->trackRefIndex);
        gf_bs_write_u16(bs, dte->dataLength);
        gf_bs_write_u32(bs, dte->streamDescIndex);
        gf_bs_write_u32(bs, dte->byteOffset);
        gf_bs_write_u32(bs, dte->reserved);
        return GF_OK;
}

GF_Err WriteDTE(GF_GenericDTE *dte, GF_BitStream *bs)
{
        switch (dte->source) {
        case 0:
                //nothing to do, it is an empty entry
                return Write_EmptyDTE((GF_EmptyDTE *)dte, bs);
        case 1:
                return Write_ImmediateDTE((GF_ImmediateDTE *)dte, bs);
        case 2:
                return Write_SampleDTE((GF_SampleDTE *)dte, bs);
        case 3:
                return Write_StreamDescDTE((GF_StreamDescDTE *)dte, bs);
        default:
                return GF_ISOM_INVALID_FILE;
        }
}

GF_Err OffsetDTE(GF_GenericDTE *dte, u32 offset, u32 HintSampleNumber)
{
        GF_SampleDTE *sDTE;
        //offset shifting is only true for intra sample reference
        switch (dte->source) {
        case 2:
                break;
        default:
                return GF_OK;
        }

        sDTE = (GF_SampleDTE *)dte;
        //we only adjust for intra HintTrack reference
        if (sDTE->trackRefIndex != (s8) -1) return GF_OK;
        //and in the same sample
        if (sDTE->sampleNumber != HintSampleNumber) return GF_OK;
        sDTE->byteOffset += offset;
        return GF_OK;
}

GF_RTPPacket *gf_isom_hint_rtp_new()
{
        GF_RTPPacket *tmp;
        GF_SAFEALLOC(tmp, GF_RTPPacket);
        if (!tmp) return NULL;
        tmp->TLV = gf_list_new();
        tmp->DataTable = gf_list_new();
        return tmp;
}

void gf_isom_hint_rtp_del(GF_RTPPacket *ptr)
{
        GF_GenericDTE *p;
        //the DTE
        while (gf_list_count(ptr->DataTable)) {
                p = (GF_GenericDTE *)gf_list_get(ptr->DataTable, 0);
                DelDTE(p);
                gf_list_rem(ptr->DataTable, 0);
        }
        gf_list_del(ptr->DataTable);
        //the TLV
        gf_isom_box_array_del(ptr->TLV);
        gf_free(ptr);
}

GF_Err gf_isom_hint_rtp_read(GF_RTPPacket *ptr, GF_BitStream *bs)
{
        GF_Err e;
        u8 hasTLV, type;
        u16 i, count;
        u32 TLVsize, tempSize;
        GF_GenericDTE *dte;
        GF_Box *a;

        ptr->relativeTransTime = gf_bs_read_u32(bs);
        //RTP Header
        //1- reserved fields
        gf_bs_read_int(bs, 2);
        ptr->P_bit = gf_bs_read_int(bs, 1);
        ptr->X_bit = gf_bs_read_int(bs, 1);
        gf_bs_read_int(bs, 4);
        ptr->M_bit = gf_bs_read_int(bs, 1);
        ptr->payloadType = gf_bs_read_int(bs, 7);

        ptr->SequenceNumber = gf_bs_read_u16(bs);
        gf_bs_read_int(bs, 13);
        hasTLV = gf_bs_read_int(bs, 1);
        ptr->B_bit = gf_bs_read_int(bs, 1);
        ptr->R_bit = gf_bs_read_int(bs, 1);
        count = gf_bs_read_u16(bs);

        //read the TLV
        if (hasTLV) {
                tempSize = 4;   //TLVsize includes its field length
                TLVsize = gf_bs_read_u32(bs);
                while (tempSize < TLVsize) {
                        e = gf_isom_box_parse(&a, bs);
                        if (e) return e;
                        gf_list_add(ptr->TLV, a);
                        tempSize += (u32) a->size;
                }
                if (tempSize != TLVsize) return GF_ISOM_INVALID_FILE;
        }

        //read the DTEs
        for (i=0; i<count; i++) {
                Bool add_it = 0;
                type = gf_bs_read_u8(bs);
                dte = NewDTE(type);
                if (!dte) {
                        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso] invalid DTE code %d in hint sample %d of trackID %d\n", type, ptr->sampleNumber, ptr->trackID));
                        return GF_ISOM_INVALID_FILE;
                }
                e = ReadDTE(dte, bs);
                if (e) return e;
                /*little opt, remove empty dte*/
                switch (type) {
                case 1:
                        if ( ((GF_ImmediateDTE *)dte)->dataLength) add_it = 1;
                        break;
                case 2:
                        if ( ((GF_SampleDTE *)dte)->dataLength) add_it = 1;
                        break;
                case 3:
                        if ( ((GF_StreamDescDTE *)dte)->dataLength) add_it = 1;
                        break;
                }
                if (add_it)
                        gf_list_add(ptr->DataTable, dte);
                else
                        DelDTE(dte);
        }
        return GF_OK;
}

GF_Err gf_isom_hint_rtp_offset(GF_RTPPacket *ptr, u32 offset, u32 HintSampleNumber)
{
        u32 count, i;
        GF_GenericDTE *dte;
        GF_Err e;

        count = gf_list_count(ptr->DataTable);
        for (i=0; i<count; i++) {
                dte = (GF_GenericDTE *)gf_list_get(ptr->DataTable, i);
                e = OffsetDTE(dte, offset, HintSampleNumber);
                if (e) return e;
        }
        return GF_OK;
}

//Gets the REAL size of the packet once rebuild, but without CSRC fields in the
//header
u32 gf_isom_hint_rtp_length(GF_RTPPacket *ptr)
{
        u32 size, count, i;
        GF_GenericDTE *dte;

        //64 bit header
        size = 8;
        //32 bit SSRC
        size += 4;
        count = gf_list_count(ptr->DataTable);
        for (i=0; i<count; i++) {
                dte = (GF_GenericDTE *)gf_list_get(ptr->DataTable, i);
                switch (dte->source) {
                case 0:
                        break;
                case 1:
                        size += ((GF_ImmediateDTE *)dte)->dataLength;
                        break;
                case 2:
                        size += ((GF_SampleDTE *)dte)->dataLength;
                        break;
                case 3:
                        size += ((GF_StreamDescDTE *)dte)->dataLength;
                        break;
                }
        }
        return size;
}


#ifndef GPAC_DISABLE_ISOM_WRITE

u32 gf_isom_hint_rtp_size(GF_RTPPacket *ptr)
{
        GF_Box none;
        u32 size, count;
        //the RTP Header size and co
        size = 12;
        //the extra table size
        count = gf_list_count(ptr->TLV);
        if (count) {
                none.size = 4;  //WE INCLUDE THE SIZE FIELD LENGTH
                none.type = 0;
                //REMEMBER THAT TLV ENTRIES ARE 4-BYTES ALIGNED !!!
                gf_isom_box_array_size(&none, ptr->TLV);
                size += (u32) none.size;
        }
        //the DTE (each entry is 16 bytes)
        count = gf_list_count(ptr->DataTable);
        size += count * 16;
        return size;
}

GF_Err gf_isom_hint_rtp_write(GF_RTPPacket *ptr, GF_BitStream *bs)
{
        GF_Err e;
        u32 TLVcount, DTEcount, i;
        GF_Box none;
        GF_GenericDTE *dte;

        gf_bs_write_u32(bs, ptr->relativeTransTime);
        //RTP Header
//      gf_bs_write_int(bs, 2, 2);
        //version is 2
        gf_bs_write_int(bs, 2, 2);
        gf_bs_write_int(bs, ptr->P_bit, 1);
        gf_bs_write_int(bs, ptr->X_bit, 1);
        gf_bs_write_int(bs, 0, 4);
        gf_bs_write_int(bs, ptr->M_bit, 1);
        gf_bs_write_int(bs, ptr->payloadType, 7);

        gf_bs_write_u16(bs, ptr->SequenceNumber);
        gf_bs_write_int(bs, 0, 13);
        TLVcount = gf_list_count(ptr->TLV);
        DTEcount = gf_list_count(ptr->DataTable);
        gf_bs_write_int(bs, TLVcount ? 1 : 0, 1);
        gf_bs_write_int(bs, ptr->B_bit, 1);
        gf_bs_write_int(bs, ptr->R_bit, 1);

        gf_bs_write_u16(bs, DTEcount);

        if (TLVcount) {
                //first write the size of the table ...
                none.size = 4;  //WE INCLUDE THE SIZE FIELD LENGTH
                none.type = 0;
                gf_isom_box_array_size(&none, ptr->TLV);
                gf_bs_write_u32(bs, (u32) none.size);
                e = gf_isom_box_array_write(&none, ptr->TLV, bs);
                if (e) return e;
        }
        //write the DTE...
        for (i = 0; i < DTEcount; i++) {
                dte = (GF_GenericDTE *)gf_list_get(ptr->DataTable, i);
                e = WriteDTE(dte, bs);
                if (e) return e;
        }
        return GF_OK;
}

#endif /*GPAC_DISABLE_ISOM_WRITE*/



GF_RTCPPacket *gf_isom_hint_rtcp_new()
{
        GF_RTCPPacket *tmp;
        GF_SAFEALLOC(tmp, GF_RTCPPacket);
        return tmp;
}

void gf_isom_hint_rtcp_del(GF_RTCPPacket *ptr)
{
        if(ptr->data) gf_free(ptr->data);
        gf_free(ptr);
}

GF_Err gf_isom_hint_rtcp_read(GF_RTCPPacket *ptr, GF_BitStream *bs)
{
        //RTCP Header

        ptr->Version = gf_bs_read_int(bs, 2);
        ptr->Padding = gf_bs_read_int(bs, 1);
        ptr->Count = gf_bs_read_int(bs, 5);
        ptr->PayloadType = gf_bs_read_u8(bs);
        ptr->length = 4 * gf_bs_read_u16(bs);
        if (ptr->length<4) return GF_ISOM_INVALID_MEDIA;

        //remove header size
        if (gf_bs_available(bs) < ptr->length) {
                GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso] RTCP hint packet has more data (%d) than available\n", ptr->length ));
                return GF_ISOM_INVALID_MEDIA;
        }
        ptr->data = gf_malloc(sizeof(char) * ptr->length);
        gf_bs_read_data(bs, ptr->data, ptr->length);
        return GF_OK;
}


//Gets the REAL size of the packet once rebuild, but without CSRC fields in the
//header
u32 gf_isom_hint_rtcp_length(GF_RTCPPacket *ptr)
{
        return 4 * (ptr->length + 1);
}


#ifndef GPAC_DISABLE_ISOM_WRITE

u32 gf_isom_hint_rtcp_size(GF_RTCPPacket *ptr)
{
        return 4 * (ptr->length + 1);
}

GF_Err gf_isom_hint_rtcp_write(GF_RTCPPacket *ptr, GF_BitStream *bs)
{
        //RTP Header
        gf_bs_write_int(bs, ptr->Version, 2);
        gf_bs_write_int(bs, ptr->Padding, 1);
        gf_bs_write_int(bs, ptr->Count, 5);
        gf_bs_write_u8(bs, ptr->PayloadType);
        gf_bs_write_u16(bs, 4*ptr->length);
        gf_bs_write_data(bs, ptr->data, ptr->length);
        return GF_OK;
}

#endif /*GPAC_DISABLE_ISOM_WRITE*/

GF_EXPORT
GF_Err gf_isom_reset_hint_reader(GF_ISOFile *the_file, u32 trackNumber, u32 sample_start, u32 ts_offset, u32 sn_offset, u32 ssrc)
{
        GF_Err e;
        GF_TrackBox *trak;
        GF_HintSampleEntryBox *entry;

        trak = gf_isom_get_track_from_file(the_file, trackNumber);
        if (!trak) return GF_BAD_PARAM;

        if (!sample_start) return GF_BAD_PARAM;
        if (sample_start>=trak->Media->information->sampleTable->SampleSize->sampleCount) return GF_BAD_PARAM;

        e = Media_GetSampleDesc(trak->Media, 1, (GF_SampleEntryBox **) &entry, NULL);
        if (e) return e;
        switch (entry->type) {
        case GF_ISOM_BOX_TYPE_RTP_STSD:
        case GF_ISOM_BOX_TYPE_SRTP_STSD:
        case GF_ISOM_BOX_TYPE_RRTP_STSD:
                break;
        default:
                return GF_NOT_SUPPORTED;
        }

        entry->hint_ref = NULL;
        e = Track_FindRef(trak, GF_ISOM_REF_HINT, &entry->hint_ref);
        if (e) return e;

        entry->cur_sample = sample_start;
        entry->pck_sn = 1 + sn_offset;
        entry->ssrc = ssrc;
        entry->ts_offset = ts_offset;
        if (entry->hint_sample) gf_isom_hint_sample_del(entry->hint_sample);
        entry->hint_sample = NULL;
        return GF_OK;
}

static GF_Err gf_isom_load_next_hint_sample(GF_ISOFile *the_file, u32 trackNumber, GF_TrackBox *trak, GF_HintSampleEntryBox *entry)
{
        GF_BitStream *bs;
        u32 descIdx;
        GF_ISOSample *samp;

        if (!entry->cur_sample) return GF_BAD_PARAM;
        if (entry->cur_sample>trak->Media->information->sampleTable->SampleSize->sampleCount) return GF_EOS;

        samp = gf_isom_get_sample(the_file, trackNumber, entry->cur_sample, &descIdx);
        if (!samp) return GF_IO_ERR;
        entry->cur_sample++;

        if (entry->hint_sample) gf_isom_hint_sample_del(entry->hint_sample);

        bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ);
        entry->hint_sample = gf_isom_hint_sample_new(entry->type);
        gf_isom_hint_sample_read(entry->hint_sample, bs, samp->dataLength);
        gf_bs_del(bs);
        entry->hint_sample->TransmissionTime = samp->DTS;
        gf_isom_sample_del(&samp);
        entry->hint_sample->sample_cache = gf_list_new();
        return GF_OK;
}

static GF_ISOSample *gf_isom_get_data_sample(GF_HintSample *hsamp, GF_TrackBox *trak, u32 sample_num)
{
        GF_ISOSample *samp;
        GF_HintDataCache *hdc;
        u32 i, count;
        count = gf_list_count(hsamp->sample_cache);
        for (i=0; i<count; i++) {
                hdc = (GF_HintDataCache *)gf_list_get(hsamp->sample_cache, i);
                if ((hdc->sample_num==sample_num) && (hdc->trak==trak)) return hdc->samp;
        }

        samp = gf_isom_sample_new();
        Media_GetSample(trak->Media, sample_num, &samp, &i, 0, NULL);
        if (!samp) return NULL;
        GF_SAFEALLOC(hdc, GF_HintDataCache);
        if (!hdc) return NULL;
        hdc->samp = samp;
        hdc->sample_num = sample_num;
        hdc->trak = trak;
        /*we insert all new samples, since they're more likely to be fetched next (except for audio
        interleaving and other multiplex)*/
        gf_list_insert(hsamp->sample_cache, hdc, 0);
        return samp;
}

GF_EXPORT
GF_Err gf_isom_next_hint_packet(GF_ISOFile *the_file, u32 trackNumber, char **pck_data, u32 *pck_size, Bool *disposable, Bool *repeated, u32 *trans_ts, u32 *sample_num)
{
        GF_RTPPacket *pck;
        GF_Err e;
        GF_BitStream *bs;
        GF_TrackBox *trak, *ref_trak;
        GF_HintSampleEntryBox *entry;
        u32 i, count, ts;
        s32 cts_off;

        *pck_data = NULL;
        *pck_size = 0;
        if (trans_ts) *trans_ts = 0;
        if (disposable) *disposable = 0;
        if (repeated) *repeated = 0;
        if (sample_num) *sample_num = 0;

        trak = gf_isom_get_track_from_file(the_file, trackNumber);
        if (!trak) return GF_BAD_PARAM;
        e = Media_GetSampleDesc(trak->Media, 1, (GF_SampleEntryBox **) &entry, NULL);
        if (e) return e;
        switch (entry->type) {
        case GF_ISOM_BOX_TYPE_RTP_STSD:
        case GF_ISOM_BOX_TYPE_SRTP_STSD:
        case GF_ISOM_BOX_TYPE_RRTP_STSD:
                break;
        default:
                return GF_NOT_SUPPORTED;
        }

        if (!entry->hint_sample) {
                e = gf_isom_load_next_hint_sample(the_file, trackNumber, trak, entry);
                if (e) return e;
        }
        pck = (GF_RTPPacket *)gf_list_get(entry->hint_sample->packetTable, 0);
        gf_list_rem(entry->hint_sample->packetTable, 0);
        if (!pck) return GF_BAD_PARAM;

        bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
        /*write RTP header*/
        gf_bs_write_int(bs, 2, 2);      /*version*/
        gf_bs_write_int(bs, pck->P_bit, 1);     /*P bit*/
        gf_bs_write_int(bs, pck->X_bit, 1);     /*X bit*/
        gf_bs_write_int(bs, 0, 4);      /*CSRC count*/
        gf_bs_write_int(bs, pck->M_bit, 1);     /*M bit*/
        gf_bs_write_int(bs, pck->payloadType, 7);       /*payt*/
        gf_bs_write_u16(bs, entry->pck_sn);     /*seq num*/
        entry->pck_sn++;

        /*look for CTS offset in TLV*/
        cts_off = 0;
        count = gf_list_count(pck->TLV);
        for (i=0; i<count; i++) {
                GF_RTPOBox *rtpo = (GF_RTPOBox *)gf_list_get(pck->TLV, i);
                if (rtpo->type == GF_ISOM_BOX_TYPE_RTPO) {
                        cts_off = rtpo->timeOffset;
                        break;
                }
        }
        /*TS - TODO check TS wrapping*/
        ts = (u32) (entry->hint_sample->TransmissionTime + pck->relativeTransTime + entry->ts_offset + cts_off);
        gf_bs_write_u32(bs, ts );
        gf_bs_write_u32(bs, entry->ssrc);       /*SSRC*/

        /*then build all data*/
        count = gf_list_count(pck->DataTable);
        for (i=0; i<count; i++) {
                GF_GenericDTE *dte = (GF_GenericDTE *)gf_list_get(pck->DataTable, i);
                switch (dte->source) {
                /*empty*/
                case 0:
                        break;
                /*immediate data*/
                case 1:
                        gf_bs_write_data(bs, ((GF_ImmediateDTE *)dte)->data, ((GF_ImmediateDTE *)dte)->dataLength);
                        break;
                /*sample data*/
                case 2:
                {
                        GF_ISOSample *samp;
                        GF_SampleDTE *sdte = (GF_SampleDTE *)dte;
                        /*get track if not this one*/
                        if (sdte->trackRefIndex != (s8) -1) {
                                if (!entry->hint_ref || !entry->hint_ref->trackIDs) {
                                        gf_isom_hint_rtp_del(pck);
                                        gf_bs_del(bs);
                                        return GF_ISOM_INVALID_FILE;
                                }
                                ref_trak = gf_isom_get_track_from_id(trak->moov, entry->hint_ref->trackIDs[(u32)sdte->trackRefIndex]);
                        } else {
                                ref_trak = trak;
                        }
                        samp = gf_isom_get_data_sample(entry->hint_sample, ref_trak, sdte->sampleNumber);
                        if (!samp) {
                                gf_isom_hint_rtp_del(pck);
                                gf_bs_del(bs);
                                return GF_IO_ERR;
                        }
                        gf_bs_write_data(bs, samp->data + sdte->byteOffset, sdte->dataLength);
                }
                break;
                /*sample desc data - currently NOT SUPPORTED !!!*/
                case 3:
                        break;
                }
        }
        if (trans_ts) *trans_ts = ts;
        if (disposable) *disposable = pck->B_bit;
        if (repeated) *repeated = pck->R_bit;
        if (sample_num) *sample_num = entry->cur_sample-1;

        gf_bs_get_content(bs, pck_data, pck_size);
        gf_bs_del(bs);
        gf_isom_hint_rtp_del(pck);
        if (!gf_list_count(entry->hint_sample->packetTable)) {
                gf_isom_hint_sample_del(entry->hint_sample);
                entry->hint_sample = NULL;
        }
        return GF_OK;
}

#endif /* !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_HINTING)*/

/* [<][>][^][v][top][bottom][index][help] */