root/modules/isom_in/isom_cache.c

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

DEFINITIONS

This source file includes following definitions.
  1. ISOW_Open
  2. ISOW_Close
  3. ISOW_Write
  4. ISOW_ServiceCommand
  5. ISOW_ChannelGetSLP
  6. ISOW_ChannelReleaseSLP
  7. isow_load_cache
  8. isow_delete_cache

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Jean Le Feuvre
 *                      Copyright (c) Telecom ParisTech 2000-2012
 *                                      All rights reserved
 *
 *  This file is part of GPAC / MP4 cache module
 *
 *  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 "isom_in.h"

#ifndef GPAC_DISABLE_ISOM_WRITE

static GF_Err ISOW_Open(GF_StreamingCache *mc, GF_ClientService *serv, const char *location_and_name, Bool keep_existing_files)
{
        char szRoot[GF_MAX_PATH], szPath[GF_MAX_PATH], *ext;
        ISOMReader *cache = (ISOMReader *)mc->priv;
        if (cache->mov || cache->service) return GF_BAD_PARAM;

        strcpy(szRoot, location_and_name);
        ext = strrchr(szRoot, '.');
        if (ext) ext[0] = 0;
        strcpy(szPath, szRoot);
        strcat(szPath, ".mp4");
        if (keep_existing_files) {
                FILE *f = gf_fopen(szPath, "rb");
                if (f) {
                        u32 i=0;
                        gf_fclose(f);
                        while (1) {
                                sprintf(szPath, "%s_%04d.mp4", szRoot, i);
                                f = gf_fopen(szPath, "rb");
                                if (!f) break;
                                gf_fclose(f);
                                i++;
                        }
                }
        }

        /*create a new movie in write mode (eg no editing)*/
        cache->mov = gf_isom_open(szPath, GF_ISOM_OPEN_WRITE, NULL);
        if (!cache->mov) return gf_isom_last_error(NULL);
        cache->service = serv;
        return GF_OK;
}

static GF_Err ISOW_Close(GF_StreamingCache *mc, Bool delete_cache)
{
        GF_Err e;
        ISOMReader *cache = (ISOMReader *)mc->priv;
        if (!cache->mov || !cache->service) return GF_BAD_PARAM;

        while (gf_list_count(cache->channels)) {
                ISOMChannel *ch = (ISOMChannel *)gf_list_get(cache->channels, 0);
                gf_list_rem(cache->channels, 0);
                if (ch->cache_sample) {
                        gf_isom_add_sample(cache->mov, ch->track, 1, ch->cache_sample);
                        gf_isom_sample_del(&ch->cache_sample);
                }
                gf_free(ch);
        }
        if (delete_cache) {
                gf_isom_delete(cache->mov);
                e = GF_OK;
        } else {
                e = gf_isom_close(cache->mov);
        }
        cache->mov = NULL;
        cache->service = NULL;
        return e;
}
static GF_Err ISOW_Write(GF_StreamingCache *mc, LPNETCHANNEL ch, char *data, u32 data_size, GF_SLHeader *sl_hdr)
{
        ISOMChannel *mch;
        GF_ESD *esd;
        u32 di, mtype;
        u64 DTS, CTS;
        ISOMReader *cache = (ISOMReader *)mc->priv;
        if (!cache->mov || !cache->service) return GF_BAD_PARAM;

        mch = isor_get_channel(cache, ch);
        if (!mch) {
                Bool mapped;
                GF_NetworkCommand com;
                com.base.on_channel = ch;
                com.base.command_type = GF_NET_CHAN_GET_ESD;
                gf_service_command(cache->service, &com, GF_OK);
                if (!com.cache_esd.esd) return GF_SERVICE_ERROR;

                esd = (GF_ESD *)com.cache_esd.esd;
                switch (esd->decoderConfig->streamType) {
                case GF_STREAM_OD:
                        mtype = GF_ISOM_MEDIA_OD;
                        break;
                case GF_STREAM_SCENE:
                        mtype = GF_ISOM_MEDIA_SCENE;
                        break;
                case GF_STREAM_VISUAL:
                        mtype = GF_ISOM_MEDIA_VISUAL;
                        break;
                case GF_STREAM_AUDIO:
                        mtype = GF_ISOM_MEDIA_AUDIO;
                        break;
                case GF_STREAM_MPEG7:
                        mtype = GF_ISOM_MEDIA_MPEG7;
                        break;
                case GF_STREAM_OCI:
                        mtype = GF_ISOM_MEDIA_OCI;
                        break;
                case GF_STREAM_IPMP:
                        mtype = GF_ISOM_MEDIA_IPMP;
                        break;
                case GF_STREAM_MPEGJ:
                        mtype = GF_ISOM_MEDIA_MPEGJ;
                        break;
                case GF_STREAM_TEXT:
                        mtype = GF_ISOM_MEDIA_TEXT;
                        break;
                default:
                        return GF_NOT_SUPPORTED;
                }
                GF_SAFEALLOC(mch, ISOMChannel);
                if (!mch) {
                        return GF_OUT_OF_MEM;
                }
                mch->time_scale = esd->slConfig->timestampResolution;
                mch->streamType = esd->decoderConfig->streamType;
                mch->track = gf_isom_new_track(cache->mov, com.cache_esd.esd->ESID, mtype, mch->time_scale);
                mch->is_playing = GF_TRUE;
                mch->channel = ch;
                mch->owner = cache;
                gf_isom_set_track_enabled(cache->mov, mch->track, 1);
                /*translate 3GP streams to MP4*/
                mapped = GF_FALSE;
                if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_MEDIA_GENERIC) {
                        char szCode[5];
                        strncpy(szCode, esd->decoderConfig->decoderSpecificInfo->data, 4);
                        szCode[4]=0;
                        if (!stricmp(szCode, "samr") || !stricmp(szCode, "amr ") || !stricmp(szCode, "sawb")) {
                                GF_3GPConfig amrc;
                                mapped = GF_TRUE;
                                memset(&amrc, 0, sizeof(GF_3GPConfig));

                                amrc.frames_per_sample = (u32) esd->decoderConfig->decoderSpecificInfo->data[13];
                                amrc.type = (!stricmp(szCode, "sawb")) ? GF_ISOM_SUBTYPE_3GP_AMR_WB : GF_ISOM_SUBTYPE_3GP_AMR;
                                amrc.vendor = GF_4CC('G','P','A','C');
                                gf_isom_3gp_config_new(cache->mov, mch->track, &amrc, NULL, NULL, &di);
                        } else if (!stricmp(szCode, "h263")) {
                                GF_3GPConfig h263c;
                                memset(&h263c, 0, sizeof(GF_3GPConfig));
                                h263c.type = GF_ISOM_SUBTYPE_3GP_H263;
                                h263c.vendor = GF_4CC('G','P','A','C');
                                gf_isom_3gp_config_new(cache->mov, mch->track, &h263c, NULL, NULL, &di);
                                mapped = GF_TRUE;
                        }
                }
                if (!mapped) gf_isom_new_mpeg4_description(cache->mov, mch->track, esd, NULL, NULL, &di);
                if (com.cache_esd.is_iod_stream) gf_isom_add_track_to_root_od(cache->mov, mch->track);
                gf_list_add(cache->channels, mch);
        }

        /*first sample, cache it*/
        if (!mch->cache_sample) {
                mch->cache_seed_ts = sl_hdr->decodingTimeStamp;
                mch->cache_sample = gf_isom_sample_new();
                mch->cache_sample->IsRAP = sl_hdr->randomAccessPointFlag;
                mch->cache_sample->dataLength = data_size;
                mch->cache_sample->data = (char*)gf_malloc(sizeof(char)*data_size);
                memcpy(mch->cache_sample->data, data, sizeof(char)*data_size);
                return GF_OK;
        }

        /*adjust DTS/CTS*/
        DTS = sl_hdr->decodingTimeStamp - mch->cache_seed_ts;

        if ((mch->streamType==GF_STREAM_VISUAL) && (DTS<=mch->cache_sample->DTS)) {
                assert(DTS>mch->prev_dts);
                CTS = mch->cache_sample->DTS + mch->cache_sample->CTS_Offset;
                mch->cache_sample->CTS_Offset = 0;

                /*first time, shift all CTS*/
                if (!mch->frame_cts_offset) {
                        u32 i, count = gf_isom_get_sample_count(cache->mov, mch->track);
                        mch->frame_cts_offset = (u32) (DTS-mch->prev_dts);
                        for (i=0; i<count; i++) {
                                gf_isom_modify_cts_offset(cache->mov, mch->track, i+1, mch->frame_cts_offset);
                        }
                        mch->cache_sample->CTS_Offset += mch->frame_cts_offset;
                }
                mch->cache_sample->DTS = mch->prev_dts + mch->frame_cts_offset;
                mch->cache_sample->CTS_Offset += (u32) (CTS-mch->cache_sample->DTS);
        }
        /*deal with reference picture insertion: if no CTS offset and biggest CTS until now, this is
        a reference insertion - we must check that in order to make sure we have strictly increasing DTSs*/
        if (mch->max_cts && !mch->cache_sample->CTS_Offset && (mch->cache_sample->DTS+mch->cache_sample->CTS_Offset > mch->max_cts)) {
                assert(mch->cache_sample->DTS > mch->prev_dts + mch->frame_cts_offset);
                CTS = mch->cache_sample->DTS + mch->cache_sample->CTS_Offset;
                mch->cache_sample->DTS = mch->prev_dts + mch->frame_cts_offset;
                mch->cache_sample->CTS_Offset = (u32) (CTS-mch->cache_sample->DTS);
        }
        if (mch->cache_sample->CTS_Offset)
                mch->max_cts = mch->cache_sample->DTS+mch->cache_sample->CTS_Offset;

        /*add cache*/
        gf_isom_add_sample(cache->mov, mch->track, 1, mch->cache_sample);
        assert(!mch->prev_dts || (mch->prev_dts < mch->cache_sample->DTS));
        mch->prev_dts = mch->cache_sample->DTS;
        mch->duration = MAX(mch->max_cts, mch->prev_dts);
        gf_isom_sample_del(&mch->cache_sample);

        /*store sample*/
        mch->cache_sample = gf_isom_sample_new();
        mch->cache_sample->IsRAP = sl_hdr->randomAccessPointFlag;
        mch->cache_sample->DTS = DTS + mch->frame_cts_offset;
        mch->cache_sample->CTS_Offset = (u32) (sl_hdr->compositionTimeStamp - mch->cache_seed_ts - DTS);
        mch->cache_sample->dataLength = data_size;
        mch->cache_sample->data = (char*)gf_malloc(sizeof(char)*data_size);
        memcpy(mch->cache_sample->data, data, sizeof(char)*data_size);
        return GF_OK;
}
static GF_Err ISOW_ServiceCommand(GF_StreamingCache *mc, GF_NetworkCommand *com)
{
        ISOMReader *cache = (ISOMReader *)mc->priv;
        if (!cache->mov || !cache->service) return GF_BAD_PARAM;

        return GF_OK;
}
static GF_Err ISOW_ChannelGetSLP(GF_StreamingCache *mc, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data)
{
        ISOMReader *cache = (ISOMReader *)mc->priv;
        if (!cache->mov || !cache->service) return GF_BAD_PARAM;

        return GF_OK;
}
static GF_Err ISOW_ChannelReleaseSLP(GF_StreamingCache *mc, LPNETCHANNEL channel)
{
        ISOMReader *cache = (ISOMReader *)mc->priv;
        if (!cache->mov || !cache->service) return GF_BAD_PARAM;

        return GF_OK;
}

GF_BaseInterface *isow_load_cache()
{
        ISOMReader *cache;
        GF_StreamingCache *plug;
        GF_SAFEALLOC(plug, GF_StreamingCache);
        if (!plug) return NULL;
        GF_REGISTER_MODULE_INTERFACE(plug, GF_STREAMING_MEDIA_CACHE, "GPAC IsoMedia Cache", "gpac distribution")

        GF_SAFEALLOC(cache, ISOMReader);
        if (!cache) {
                gf_free(plug);
                return NULL;
        }
        cache->channels = gf_list_new();
        plug->priv = cache;

        plug->Open = ISOW_Open;
        plug->Close = ISOW_Close;
        plug->Write = ISOW_Write;
        plug->ChannelGetSLP = ISOW_ChannelGetSLP;
        plug->ChannelReleaseSLP = ISOW_ChannelReleaseSLP;
        plug->ServiceCommand = ISOW_ServiceCommand;

        return (GF_BaseInterface *) plug;
}

void isow_delete_cache(GF_BaseInterface *bi)
{
        GF_StreamingCache *mc = (GF_StreamingCache*) bi;
        ISOMReader *cache = (ISOMReader *)mc->priv;
        gf_list_del(cache->channels);
        gf_free(cache);
        gf_free(bi);
}

#endif /*GPAC_DISABLE_ISOM_WRITE*/


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