root/modules/isom_in/read.c

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

DEFINITIONS

This source file includes following definitions.
  1. isor_get_channel
  2. isor_delete_channel
  3. isor_is_local
  4. ISOR_RegisterMimeTypes
  5. ISOR_CanHandleURL
  6. send_proxy_command
  7. isor_check_buffer_level
  8. isor_net_io
  9. isor_setup_download
  10. ISOR_ConnectService
  11. ISOR_CloseService
  12. check_mpeg4_systems
  13. get_track_id
  14. ISOR_GetServiceDesc
  15. isor_send_cenc_config
  16. ISOR_ConnectChannel
  17. ISOR_DisconnectChannel
  18. ISOR_ChannelGetSLP
  19. ISOR_ChannelReleaseSLP
  20. gf_channel_switch_quality
  21. ISOR_ServiceCommand
  22. ISOR_CanHandleURLInService
  23. isor_client_load
  24. isor_client_del

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

#include <gpac/ismacryp.h>

ISOMChannel *isor_get_channel(ISOMReader *reader, LPNETCHANNEL channel)
{
        u32 i=0;
        ISOMChannel *ch;
        while ((ch = (ISOMChannel *)gf_list_enum(reader->channels, &i))) {
                if (ch->channel == channel) return ch;
        }
        return NULL;
}

static void isor_delete_channel(ISOMReader *reader, ISOMChannel *ch)
{
        u32 i=0;
        ISOMChannel *ch2;
        while ((ch2 = (ISOMChannel *)gf_list_enum(reader->channels, &i))) {
                if (ch2 == ch) {
                        isor_reset_reader(ch);
                        gf_free(ch);
                        gf_list_rem(reader->channels, i-1);
                        return;
                }
        }
}

static GFINLINE Bool isor_is_local(const char *url)
{
        if (!strnicmp(url, "file://", 7)) return GF_TRUE;
        if (!strnicmp(url, "gmem://", 7)) return GF_TRUE;
        if (!strnicmp(url, "isobmff://", 10)) return GF_TRUE;
        if (strstr(url, "://")) return GF_FALSE;
        /*the rest is local (mounted on FS)*/
        return GF_TRUE;
}

static const char * ISOR_MIME_TYPES[] = {
        /* First one is specific because we probe */
        "application/x-isomedia", "*", "IsoMedia Files",
        "video/mp4", "mp4 mpg4", "MPEG-4 Movies",
        "audio/mp4", "m4a mp4 mpg4", "MPEG-4 Music",
        "application/mp4", "m4i mp4 mpg4", "MPEG-4 Applications",
        "video/3gpp", "3gp 3gpp", "3GPP/MMS Movies",
        "audio/3gpp", "3gp 3gpp", "3GPP/MMS Music",
        "video/3gpp2", "3g2 3gp2", "3GPP2/MMS Movies",
        "audio/3gpp2", "3g2 3gp2", "3GPP2/MMS Music",
        "video/iso.segment", "iso", "ISOBMF Fragments",
        "audio/iso.segment", "iso", "ISOBMF Fragments",
        NULL
};

u32 ISOR_RegisterMimeTypes(const GF_InputService *plug) {
        u32 i;
        for (i = 0 ; ISOR_MIME_TYPES[i]; i+=3)
                gf_service_register_mime(plug, ISOR_MIME_TYPES[i], ISOR_MIME_TYPES[i+1], ISOR_MIME_TYPES[i+2]);
        return i/3;
}

Bool ISOR_CanHandleURL(GF_InputService *plug, const char *url)
{
        char *ext;
        if (!strnicmp(url, "rtsp://", 7)) return GF_FALSE;
        ext = (char *)strrchr(url, '.');
        {
                u32 i;
                /* We don't start at 0 since first one is specific */
                for (i = 3 ; ISOR_MIME_TYPES[i]; i+=3)
                        if (gf_service_check_mime_register(plug, ISOR_MIME_TYPES[i], ISOR_MIME_TYPES[i+1], ISOR_MIME_TYPES[i+2], ext))
                                return GF_TRUE;
        }

        if (ext && gf_isom_probe_file(url)) {
                gf_service_check_mime_register(plug, ISOR_MIME_TYPES[0], ext+1, ISOR_MIME_TYPES[2], ext);
                return GF_TRUE;
        }
        return GF_FALSE;
}

void send_proxy_command(ISOMReader *read, Bool is_disconnect, Bool is_add_media, GF_Err e, GF_Descriptor *desc, LPNETCHANNEL channel)
{
        GF_NetworkCommand command;
        command.command_type = GF_NET_SERVICE_STATUS_PROXY;
        command.status.e = e;
        command.status.channel = channel;
        command.status.is_disconnect = is_disconnect;
        command.status.is_add_media = is_add_media;
        command.status.desc = desc;
        read->input->query_proxy(read->input, &command);
}

void isor_check_buffer_level(ISOMReader *read)
{
        Double dld_time_remaining, mov_rate;
        GF_NetworkCommand com;
        u32 i, total, done, Bps;
        u64 dur;
        GF_NetIOStatus status;
        Bool do_buffer = GF_FALSE;
        if (!read->dnload) return;
        if (!read->mov) return;

        gf_dm_sess_get_stats(read->dnload, NULL, NULL, &total, &done, &Bps, &status);
        if (!Bps) return;


        gf_mx_p(read->segment_mutex);

        dld_time_remaining = total-done;
        dld_time_remaining /= Bps;

        mov_rate = total;
        if (read->frag_type) {
                u64 bytesMissing=0;
                gf_isom_refresh_fragmented(read->mov, &bytesMissing, NULL);
                mov_rate = (Double) (done + bytesMissing);
        }
        dur = gf_isom_get_duration(read->mov);
        if (dur) {
                mov_rate /= dur;
                mov_rate *= gf_isom_get_timescale(read->mov);
        }

        for (i=0; i<gf_list_count(read->channels); i++) {
                ISOMChannel *ch = gf_list_get(read->channels, i);
                Double time_remain_ch = (Double) gf_isom_get_media_duration(read->mov, ch->track);
                u32 buffer_level=0;
                if (total==done) {
                        time_remain_ch = 0;
                        do_buffer = GF_FALSE;
                } else if (ch->last_state == GF_EOS) {
                        time_remain_ch = 0;
                        do_buffer = GF_TRUE;
                } else {
                        u64 data_offset;
                        u32 di, sn = ch->sample_num ? ch->sample_num : 1;
                        GF_ISOSample *samp = gf_isom_get_sample_info(read->mov, ch->track, sn, &di, &data_offset);
                        if (!samp) {
                                continue;
                        }

                        data_offset += samp->dataLength;

#if 1
                        //we don't have enough data
                        if (((data_offset + ch->buffer_min * mov_rate/1000 > done))) {
                                do_buffer = GF_TRUE;
                        }
                        //we have enough buffer
                        else if ((data_offset + ch->buffer_max * mov_rate/1000 <= done)) {
                                do_buffer = GF_FALSE;
                        } else {
                                do_buffer = ch->buffering;
                        }
                        buffer_level = (u32) ( (done - data_offset) / mov_rate * 1000);
#else
                        //we only send buffer on/off based on remainging playback time in channel
                        time_remain_ch -= (samp->DTS + samp->CTS_Offset);
                        if (time_remain_ch<0) time_remain_ch=0;

                        time_remain_ch /= ch->time_scale;
                        if (time_remain_ch && (time_remain_ch < dld_time_remaining)) {
                                do_buffer = GF_TRUE;
                                if (!read->remain_at_buffering_start || (read->remain_at_buffering_start < dld_time_remaining)) {
                                        buffer_level = 0;
                                        read->remain_at_buffering_start = dld_time_remaining;
                                } else {
                                        buffer_level = (u32) (100 * (read->remain_at_buffering_start - dld_time_remaining) / (read->remain_at_buffering_start - time_remain_ch) );
                                }
                        } else {
                                do_buffer = GF_FALSE;
                        }
#endif

                        gf_isom_sample_del(&samp);
                }

                if (do_buffer != ch->buffering) {
                        GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[IsoMedia] Buffering %s at %d: %g sec still to download and %g sec still to play on track %d (movie rate %g - download rate %g kbps)\n", do_buffer ? "on" : "off", gf_sys_clock(), dld_time_remaining , time_remain_ch, ch->track_id, mov_rate*8/1000, Bps*8.0/1000));

                        memset(&com, 0, sizeof(GF_NetworkCommand));
                        com.command_type = do_buffer ? GF_NET_CHAN_PAUSE : GF_NET_CHAN_RESUME;
                        com.buffer.on_channel = ch->channel;
                        com.buffer.min = ch->buffer_min;
                        com.buffer.max = ch->buffer_max;
                        gf_service_command(read->service, &com, GF_OK);
                        ch->buffering = do_buffer;
                        read->buffering = do_buffer;
                } else if (ch->buffering) {
                        memset(&com, 0, sizeof(GF_NetworkCommand));
                        com.command_type = GF_NET_CHAN_BUFFER;
                        com.buffer.on_channel = ch->channel;
                        com.buffer.min = ch->buffer_min;
                        com.buffer.max = ch->buffer_max;
                        com.buffer.occupancy = buffer_level;
                        gf_service_command(read->service, &com, GF_OK);
                }
        }
        gf_mx_v(read->segment_mutex);
}

void isor_net_io(void *cbk, GF_NETIO_Parameter *param)
{
        GF_Err e;
        u32 size = 0;
        char *local_name;
        ISOMReader *read = (ISOMReader *) cbk;

        /*handle service message*/
        if (!read->buffering)
                gf_service_download_update_stats(read->dnload);

        if (param->msg_type==GF_NETIO_DATA_TRANSFERED) {
                e = GF_EOS;
        } else if (param->msg_type==GF_NETIO_DATA_EXCHANGE) {
                e = GF_OK;
                size = param->size;
        } else {
                e = param->error;
        }

        if (e<GF_OK) {
                /*error opening service*/
                if (!read->mov) {
                        /* if there is an intermediate between this module and the terminal, report to it */
                        if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
                                send_proxy_command(read, GF_FALSE, GF_FALSE, e, NULL, NULL);
                        } else {
                                gf_service_connect_ack(read->service, NULL, e);
                        }
                }
                return;
        }

        /*open file if not done yet (bad interleaving)*/
        if (e==GF_EOS) {
                const char *local_name;
                if (read->mov) return;
                local_name = gf_dm_sess_get_cache_name(read->dnload);
                if (!local_name) {
                        if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
                                send_proxy_command(read, GF_FALSE, GF_FALSE, GF_SERVICE_ERROR, NULL, NULL);
                        } else {
                                gf_service_connect_ack(read->service, NULL, GF_SERVICE_ERROR);
                        }
                        return;
                }
                read->mov = gf_isom_open(local_name, GF_ISOM_OPEN_READ, NULL);
                if (!read->mov) gf_isom_last_error(NULL);
                else read->time_scale = gf_isom_get_timescale(read->mov);
                read->frag_type = gf_isom_is_fragmented(read->mov) ? 1 : 0;
                if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
                        send_proxy_command(read, GF_FALSE, GF_FALSE, GF_OK, NULL, NULL);
                } else {
                        gf_service_connect_ack(read->service, NULL, GF_OK);
                }
                if (read->no_service_desc) isor_declare_objects(read);
        }

        if (!size) return;

        /*service is opened, nothing to do*/
        if (read->mov) {
                isor_check_buffer_level(read);

                /*end of chunk*/
                if (read->frag_type && (param->reply==1) ) {
                        u64 bytesMissing = 0;
                        gf_mx_p(read->segment_mutex);
                        gf_isom_refresh_fragmented(read->mov, &bytesMissing, NULL);
                        gf_mx_v(read->segment_mutex);
                }
                return;
        }

        /*try to open the service*/
        local_name = (char *)gf_dm_sess_get_cache_name(read->dnload);
        if (!local_name) {
                if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
                        send_proxy_command(read, GF_FALSE, GF_FALSE, GF_SERVICE_ERROR, NULL, NULL);
                } else {
                        gf_service_connect_ack(read->service, NULL, GF_SERVICE_ERROR);
                }
                return;
        }

        /*not enogh data yet*/
        if (read->missing_bytes && (read->missing_bytes > size) ) {
                read->missing_bytes -= size;
                return;
        }

        e = gf_isom_open_progressive(local_name, 0, 0, &read->mov, &read->missing_bytes);
        switch (e) {
        case GF_ISOM_INCOMPLETE_FILE:
                return;
        case GF_OK:
                break;
        default:
                if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
                        send_proxy_command(read, GF_FALSE, GF_FALSE, e, NULL, NULL);
                } else {
                        gf_service_connect_ack(read->service, NULL, e);
                }
                return;
        }
        read->frag_type = gf_isom_is_fragmented(read->mov) ? 1 : 0;

        /*ok let's go, we can setup the decoders */
        read->time_scale = gf_isom_get_timescale(read->mov);
        if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
                send_proxy_command(read, GF_FALSE, GF_FALSE, GF_OK, NULL, NULL);
        } else {
                gf_service_connect_ack(read->service, NULL, GF_OK);
        }

        if (read->no_service_desc) isor_declare_objects(read);
}


void isor_setup_download(GF_InputService *plug, const char *url)
{
        ISOMReader *read = (ISOMReader *) plug->priv;
        read->dnload = gf_service_download_new(read->service, url, 0, isor_net_io, read);
        if (!read->dnload) {
                if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
                        send_proxy_command(read, GF_FALSE, GF_FALSE, GF_NOT_SUPPORTED, NULL, NULL);
                } else {
                        gf_service_connect_ack(read->service, NULL, GF_NOT_SUPPORTED);
                }
        } else {
                /*start our download (threaded)*/
                gf_dm_sess_process(read->dnload);
        }
        /*service confirm is done once IOD can be fetched*/
}

GF_Err ISOR_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url)
{
        char szURL[2048];
        char *tmp;
        ISOMReader *read;
        if (!plug || !plug->priv || !serv) return GF_SERVICE_ERROR;
        read = (ISOMReader *) plug->priv;

        read->input = plug;
        read->service = serv;

        if (read->dnload) gf_service_download_del(read->dnload);
        read->dnload = NULL;

        if (!url) return GF_URL_ERROR;

        strcpy(szURL, url);
        tmp = strrchr(szURL, '.');
        if (tmp) {
                tmp = strchr(tmp, '#');
                if (tmp) {
                        if (!strnicmp(tmp, "#trackID=", 9)) {
                                read->play_only_track_id = atoi(tmp+9);
                        } else {
                                read->play_only_track_id = atoi(tmp+1);
                        }
                        tmp[0] = 0;
                }
        }

        if (isor_is_local(szURL)) {
                GF_Err e;
                u64 start_range, end_range;
                start_range = end_range = 0;
                if (plug->query_proxy) {
                        GF_NetworkCommand param;
                        param.command_type = GF_NET_SERVICE_QUERY_INIT_RANGE;
                        if (read->input->query_proxy(read->input, &param)==GF_OK) {
                                start_range = param.url_query.start_range;
                                end_range = param.url_query.end_range;
                        }
                }
                e = gf_isom_open_progressive(szURL, start_range, end_range, &read->mov, &read->missing_bytes);
                if (e != GF_OK) {
                        GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[IsoMedia] error while opening %s, error=%s\n", szURL, gf_error_to_string(e)));
                        if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
                                send_proxy_command(read, 0, 0, e, NULL, NULL);
                        } else {
                                gf_service_connect_ack(read->service, NULL, e);
                        }
                        return GF_OK;
                }
                read->frag_type = gf_isom_is_fragmented(read->mov) ? 1 : 0;
                read->seg_opened = 2;

                read->time_scale = gf_isom_get_timescale(read->mov);
                /*reply to user*/
                if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
                        send_proxy_command(read, GF_FALSE, GF_FALSE, GF_OK, NULL, NULL);
                } else {
                        gf_service_connect_ack(read->service, NULL, GF_OK);
                }

                if (read->no_service_desc) isor_declare_objects(read);

        } else {
                /*setup downloader*/
                isor_setup_download(plug, szURL);
        }
        return GF_OK;
}

GF_Err ISOR_CloseService(GF_InputService *plug)
{
        GF_Err reply;
        ISOMReader *read;
        if (!plug || !plug->priv) return GF_SERVICE_ERROR;
        read = (ISOMReader *) plug->priv;
        reply = GF_OK;

        read->disconnected = GF_TRUE;

        while (gf_list_count(read->channels)) {
                ISOMChannel *ch = (ISOMChannel *)gf_list_get(read->channels, 0);
                gf_list_rem(read->channels, 0);
                isor_delete_channel(read, ch);
        }

        if (read->dnload) gf_service_download_del(read->dnload);
        read->dnload = NULL;

        if (read->mov) gf_isom_close(read->mov);
        read->mov = NULL;

        if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
                send_proxy_command(read, GF_TRUE, GF_FALSE, reply, NULL, NULL);
        } else {
                gf_service_disconnect_ack(read->service, NULL, reply);
        }
        return GF_OK;
}

static Bool check_mpeg4_systems(GF_InputService *plug, GF_ISOFile *mov)
{
        char *opt, *bname, *br, *next;
        u32 i, count, brand;
        Bool has_mpeg4;
        GF_Err e;
        e = gf_isom_get_brand_info(mov, &brand, &i, &count);
        /*no brand == MP4 v1*/
        if (e || !brand) return GF_TRUE;

        has_mpeg4 = GF_FALSE;
        if ((brand==GF_ISOM_BRAND_MP41) || (brand==GF_ISOM_BRAND_MP42)) has_mpeg4 = GF_TRUE;

        opt = (char*) gf_modules_get_option((GF_BaseInterface *)plug, "ISOReader", "IgnoreMPEG-4ForBrands");
        if (!opt) {
                gf_modules_set_option((GF_BaseInterface *)plug, "ISOReader", "IgnoreMPEG-4ForBrands", "nd*");
                opt = (char*) gf_modules_get_option((GF_BaseInterface *)plug, "ISOReader", "IgnoreMPEG-4ForBrands");
        }

        for (i=0; i<count; i++) {
                e = gf_isom_get_alternate_brand(mov, i+1, &brand);
                if (e) return GF_FALSE;
                if ((brand==GF_ISOM_BRAND_MP41) || (brand==GF_ISOM_BRAND_MP42)) {
                        has_mpeg4 = GF_TRUE;
                        continue;
                }
                bname = (char*)gf_4cc_to_str(brand);
                br = opt;
                while (br) {
                        Bool ignore = GF_FALSE;
                        u32 orig_len, len;
                        next = strchr(br, ' ');
                        if (next) next[0] = 0;
                        len = orig_len = (u32) strlen(br);

                        while (len) {
                                if (br[len-1]=='*') {
                                        br[len-1]=0;
                                        len--;
                                } else {
                                        break;
                                }
                        }
                        /*ignor all brands*/
                        if (!len) ignore = GF_TRUE;
                        else if (!strncmp(bname, br, len)) ignore = GF_TRUE;

                        while (len<orig_len) {
                                br[len] = '*';
                                len++;
                        }
                        if (next) {
                                next[0] = ' ';
                                br = next + 1;
                        } else {
                                br = NULL;
                        }
                        if (ignore) return GF_FALSE;
                }
        }
        return has_mpeg4;
}

static u32 get_track_id(GF_ISOFile *mov, u32 media_type, u32 idx)
{
        u32 i, count, cur;
        cur=0;
        count = gf_isom_get_track_count(mov);
        for (i=0; i<count; i++) {
                if (gf_isom_get_media_type(mov, i+1) != media_type) continue;
                if (!idx) return gf_isom_get_track_id(mov, i+1);
                cur++;
                if (cur==idx) return gf_isom_get_track_id(mov, i+1);
        }
        return GF_FALSE;
}

/*fixme, this doesn't work properly with respect to @expect_type*/
static GF_Descriptor *ISOR_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url)
{
        u32 count, nb_st, i, trackID;
        GF_ESD *esd;
        ISOMReader *read;
        GF_InitialObjectDescriptor *iod;
        if (!plug || !plug->priv) return NULL;
        read = (ISOMReader *) plug->priv;
        if (!read->mov) return NULL;

        /*no matter what always read text as TTUs*/
        gf_isom_text_set_streaming_mode(read->mov, 1);

        if (!sub_url) {
                trackID = read->play_only_track_id;
                read->play_only_track_id = 0;
        } else {
                char *ext = (char *)strrchr(sub_url, '#');
                if (!ext) {
                        trackID = 0;
                } else {
                        if (!strnicmp(ext, "#trackID=", 9)) trackID = atoi(ext+9);
                        else if (!stricmp(ext, "#video")) trackID = get_track_id(read->mov, GF_ISOM_MEDIA_VISUAL, 0);
                        else if (!strnicmp(ext, "#video", 6)) {
                                trackID = atoi(ext+6);
                                trackID = get_track_id(read->mov, GF_ISOM_MEDIA_VISUAL, trackID);
                        }
                        else if (!stricmp(ext, "#audio")) trackID = get_track_id(read->mov, GF_ISOM_MEDIA_AUDIO, 0);
                        else if (!strnicmp(ext, "#audio", 6)) {
                                trackID = atoi(ext+6);
                                trackID = get_track_id(read->mov, GF_ISOM_MEDIA_AUDIO, trackID);
                        }
                        else trackID = atoi(ext+1);

                        /*if trackID is 0, assume this is a fragment identifier*/
                }
        }

        if (!trackID && (expect_type!=GF_MEDIA_OBJECT_SCENE) && (expect_type!=GF_MEDIA_OBJECT_UNDEF)) {
                for (i=0; i<gf_isom_get_track_count(read->mov); i++) {
                        u32 type = gf_isom_get_media_type(read->mov, i+1);
                        if (
                            ((type==GF_ISOM_MEDIA_VISUAL) && (expect_type==GF_MEDIA_OBJECT_VIDEO))
                            || ((type==GF_ISOM_MEDIA_AUDIO) && (expect_type==GF_MEDIA_OBJECT_AUDIO)) ) {
                                trackID = gf_isom_get_track_id(read->mov, i+1);
                                break;
                        }

                }
        }
        if (trackID && (expect_type!=GF_MEDIA_OBJECT_SCENE) ) {
                u32 track = gf_isom_get_track_by_id(read->mov, trackID);
                if (!track) return NULL;
                esd = gf_media_map_esd(read->mov, track);
                esd->OCRESID = 0;
                iod = (GF_InitialObjectDescriptor *) gf_isom_get_root_od(read->mov);
                if (!iod) {
                        iod = (GF_InitialObjectDescriptor *) gf_odf_desc_new(GF_ODF_IOD_TAG);
                        iod->OD_profileAndLevel = iod->audio_profileAndLevel = iod->graphics_profileAndLevel = iod->scene_profileAndLevel = iod->visual_profileAndLevel = 0xFE;
                } else {
                        while (gf_list_count(iod->ESDescriptors)) {
                                GF_ESD *old = (GF_ESD *)gf_list_get(iod->ESDescriptors, 0);
                                gf_odf_desc_del((GF_Descriptor *) old);
                                gf_list_rem(iod->ESDescriptors, 0);
                        }
                }
                gf_list_add(iod->ESDescriptors, esd);
                isor_emulate_chapters(read->mov, iod);
                return (GF_Descriptor *) iod;
        }

        iod = NULL;
        if (check_mpeg4_systems(plug, read->mov)) {
                iod = (GF_InitialObjectDescriptor *) gf_isom_get_root_od(read->mov);
                if (!iod) {
#ifndef GPAC_DISABLE_LOG
                        GF_Err e = gf_isom_last_error(read->mov);
                        if (e) {
                                GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[IsoMedia] Cannot fetch MPEG-4 IOD (error %s) - generating one\n", gf_error_to_string(e) ));
                        } else {
                                GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] No MPEG-4 IOD found in file - generating one\n"));
                        }
#endif
                }
        }
        if (!iod) return isor_emulate_iod(read);

        count = gf_list_count(iod->ESDescriptors);
        if (!count) {
                gf_odf_desc_del((GF_Descriptor*) iod);
                return isor_emulate_iod(read);
        }
        if (count==1) {
                esd = (GF_ESD *)gf_list_get(iod->ESDescriptors, 0);
                switch (esd->decoderConfig->streamType) {
                case GF_STREAM_SCENE:
                case GF_STREAM_PRIVATE_SCENE:
                        break;
                case GF_STREAM_VISUAL:
                        if (expect_type!=GF_MEDIA_OBJECT_VIDEO) {
                                gf_odf_desc_del((GF_Descriptor*) iod);
                                return isor_emulate_iod(read);
                        }
                        break;
                case GF_STREAM_AUDIO:
                        /*we need a fake scene graph*/
                        if (expect_type!=GF_MEDIA_OBJECT_AUDIO) {
                                gf_odf_desc_del((GF_Descriptor*) iod);
                                return isor_emulate_iod(read);
                        }
                        break;
                default:
                        gf_odf_desc_del((GF_Descriptor*) iod);
                        return NULL;
                }
        }
        /*check IOD is not badly formed (eg mixing audio, video or text streams)*/
        nb_st = 0;
        for (i=0; i<count; i++) {
                esd = (GF_ESD *)gf_list_get(iod->ESDescriptors, i);
                switch (esd->decoderConfig->streamType) {
                case GF_STREAM_VISUAL:
                        nb_st |= 1;
                        break;
                case GF_STREAM_AUDIO:
                        nb_st |= 2;
                        break;
                case GF_STREAM_TEXT:
                        nb_st |= 4;
                        break;
                }
        }
        if ( (nb_st & 1) + (nb_st & 2) + (nb_st & 4) > 1) {
                gf_odf_desc_del((GF_Descriptor*) iod);
                return isor_emulate_iod(read);
        }

        isor_emulate_chapters(read->mov, iod);
        return (GF_Descriptor *) iod;
}


void isor_send_cenc_config(ISOMChannel *ch)
{
        GF_NetworkCommand com;
        u32 i;

        memset(&com, 0, sizeof(GF_NetworkCommand));
        com.base.on_channel = ch->channel;
        com.command_type = GF_NET_CHAN_DRM_CFG;
        ch->is_encrypted = GF_TRUE;

        gf_isom_get_cenc_info(ch->owner->mov, ch->track, 1, NULL, &com.drm_cfg.scheme_type, &com.drm_cfg.scheme_version, NULL);

        com.drm_cfg.PSSH_count = gf_isom_get_pssh_count(ch->owner->mov);
        com.drm_cfg.PSSHs = gf_malloc(sizeof(GF_NetComDRMConfigPSSH)*(com.drm_cfg.PSSH_count) );

        /*fill PSSH in the structure. We will free it in CENC_Setup*/
        for (i=0; i<com.drm_cfg.PSSH_count; i++) {
                GF_NetComDRMConfigPSSH *pssh = &com.drm_cfg.PSSHs[i];
                gf_isom_get_pssh_info(ch->owner->mov, i+1, pssh->SystemID, &pssh->KID_count, (const bin128 **) & pssh->KIDs, (const u8 **) &pssh->private_data, &pssh->private_data_size);
        }
        //fixme - check MSE and EME
#if 0
        if (read->input->query_proxy && read->input->proxy_udta) {
                read->input->query_proxy(read->input, &com);
        } else
#endif
                gf_service_command(ch->owner->service, &com, GF_OK);
        //free our PSSH
        if (com.drm_cfg.PSSHs) gf_free(com.drm_cfg.PSSHs);
}



GF_Err ISOR_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream)
{
        u32 ESID;
        ISOMChannel *ch;
        GF_NetworkCommand com;
        u32 track;
        Bool is_esd_url;
        GF_Err e;
        ISOMReader *read;
        if (!plug || !plug->priv) return GF_SERVICE_ERROR;
        read = (ISOMReader *) plug->priv;

        track = 0;
        ch = NULL;
        is_esd_url = GF_FALSE;
        e = GF_OK;
        if (upstream) {
                e = GF_ISOM_INVALID_FILE;
                goto exit;
        }
        if (!read->mov) return GF_SERVICE_ERROR;

        if (strstr(url, "ES_ID")) {
                sscanf(url, "ES_ID=%ud", &ESID);
        } else {
                /*handle url like mypath/myfile.mp4#trackID*/
                char *track_id = (char *)strrchr(url, '.');
                if (track_id) {
                        track_id = (char *)strchr(url, '#');
                        if (track_id) track_id ++;
                }
                is_esd_url = GF_TRUE;

                ESID = 0;
                /*if only one track ok*/
                if (gf_isom_get_track_count(read->mov)==1) ESID = gf_isom_get_track_id(read->mov, 1);
                else if (track_id) {
                        ESID = atoi(track_id);
                        track = gf_isom_get_track_by_id(read->mov, (u32) ESID);
                        if (!track) ESID = 0;
                }

        }
        if (!ESID) {
                e = GF_NOT_SUPPORTED;
                goto exit;
        }

        /*a channel cannot be open twice, it has to be closed before - NOTE a track is NOT a channel and the user can open
        several times the same track as long as a dedicated channel is used*/
        ch = isor_get_channel(read, channel);
        if (ch) {
                e = GF_SERVICE_ERROR;
                goto exit;
        }
        track = gf_isom_get_track_by_id(read->mov, (u32) ESID);
        if (!track) {
                e = GF_STREAM_NOT_FOUND;
                goto exit;
        }

        GF_SAFEALLOC(ch, ISOMChannel);
        if (!ch) {
                e = GF_OUT_OF_MEM;
                goto exit;
        }
        ch->owner = read;
        ch->channel = channel;
        gf_list_add(read->channels, ch);
        ch->track = track;
        ch->track_id = gf_isom_get_track_id(read->mov, ch->track);
        switch (gf_isom_get_media_type(ch->owner->mov, ch->track)) {
        case GF_ISOM_MEDIA_OCR:
                ch->streamType = GF_STREAM_OCR;
                break;
        case GF_ISOM_MEDIA_SCENE:
                ch->streamType = GF_STREAM_SCENE;
                break;
        case GF_ISOM_MEDIA_VISUAL:
                gf_isom_get_reference(ch->owner->mov, ch->track, GF_ISOM_REF_BASE, 1, &ch->base_track);
                //use base track only if avc/svc or hevc/lhvc. If avc+lhvc we need different rules
                if ( gf_isom_get_avc_svc_type(ch->owner->mov, ch->base_track, 1) == GF_ISOM_AVCTYPE_AVC_ONLY) {
                        if ( gf_isom_get_hevc_lhvc_type(ch->owner->mov, ch->track, 1) >= GF_ISOM_HEVCTYPE_HEVC_ONLY) {
                                ch->base_track=0;
                        }
                }
                
                ch->next_track = 0;
                /*in scalable mode add SPS/PPS in-band*/
                ch->nalu_extract_mode = GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG /*| GF_ISOM_NALU_EXTRACT_ANNEXB_FLAG*/;
                gf_isom_set_nalu_extract_mode(ch->owner->mov, ch->track, ch->nalu_extract_mode);
                break;
        }

        ch->has_edit_list = gf_isom_get_edit_list_type(ch->owner->mov, ch->track, &ch->dts_offset) ? GF_TRUE : GF_FALSE;
        ch->has_rap = (gf_isom_has_sync_points(ch->owner->mov, ch->track)==1) ? GF_TRUE : GF_FALSE;
        ch->time_scale = gf_isom_get_media_timescale(ch->owner->mov, ch->track);

exit:
        if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
                send_proxy_command(read, GF_FALSE, GF_FALSE, e, NULL, channel);
        } else {
                gf_service_connect_ack(read->service, channel, e);
        }
        /*if esd url reconfig SL layer*/
        if (!e && is_esd_url) {
                GF_ESD *esd;
                memset(&com, 0, sizeof(GF_NetworkCommand));
                com.base.on_channel = channel;
                com.command_type = GF_NET_CHAN_RECONFIG;
                esd = gf_isom_get_esd(read->mov, ch->track, 1);
                if (esd) {
                        memcpy(&com.cfg.sl_config, esd->slConfig, sizeof(GF_SLConfig));
                        gf_odf_desc_del((GF_Descriptor *)esd);
                } else {
                        com.cfg.sl_config.tag = GF_ODF_SLC_TAG;
                        com.cfg.sl_config.timestampLength = 32;
                        com.cfg.sl_config.timestampResolution = ch->time_scale;
                        com.cfg.sl_config.useRandomAccessPointFlag = 1;
                }
                if (read->input->query_proxy && read->input->proxy_udta) {
                        read->input->query_proxy(read->input, &com);
                } else {
                        gf_service_command(read->service, &com, GF_OK);
                }
        }
        if (!e && track && gf_isom_is_track_encrypted(read->mov, track)) {
                memset(&com, 0, sizeof(GF_NetworkCommand));
                com.base.on_channel = channel;
                com.command_type = GF_NET_CHAN_DRM_CFG;
                ch->is_encrypted = GF_TRUE;
                if (gf_isom_is_ismacryp_media(read->mov, track, 1)) {
                        gf_isom_get_ismacryp_info(read->mov, track, 1, NULL, &com.drm_cfg.scheme_type, &com.drm_cfg.scheme_version, &com.drm_cfg.scheme_uri, &com.drm_cfg.kms_uri, NULL, NULL, NULL);
                        if (read->input->query_proxy && read->input->proxy_udta) {
                                read->input->query_proxy(read->input, &com);
                        } else {
                                gf_service_command(read->service, &com, GF_OK);
                        }
                } else if (gf_isom_is_omadrm_media(read->mov, track, 1)) {
                        gf_isom_get_omadrm_info(read->mov, track, 1, NULL, &com.drm_cfg.scheme_type, &com.drm_cfg.scheme_version, &com.drm_cfg.contentID, &com.drm_cfg.kms_uri, &com.drm_cfg.oma_drm_textual_headers, &com.drm_cfg.oma_drm_textual_headers_len, NULL, &com.drm_cfg.oma_drm_crypt_type, NULL, NULL, NULL);

                        gf_media_get_file_hash(gf_isom_get_filename(read->mov), com.drm_cfg.hash);
                        if (read->input->query_proxy && read->input->proxy_udta) {
                                read->input->query_proxy(read->input, &com);
                        } else {
                                gf_service_command(read->service, &com, GF_OK);
                        }
                } else if (gf_isom_is_cenc_media(read->mov, track, 1)) {
                        ch->is_cenc = GF_TRUE;
                        isor_send_cenc_config(ch);
                }
        }
        return e;
}

GF_Err ISOR_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel)
{
        ISOMChannel *ch;
        GF_Err e;
        ISOMReader *read;
        if (!plug || !plug->priv) return GF_SERVICE_ERROR;
        read = (ISOMReader *) plug->priv;
        if (!read->mov) return GF_SERVICE_ERROR;

        gf_mx_p(read->segment_mutex);
        e = GF_OK;
        ch = isor_get_channel(read, channel);
        assert(ch);
        if (!ch) {
                e = GF_STREAM_NOT_FOUND;
                goto exit;
        }
        /*signal the service is broken but still process the delete*/
        isor_delete_channel(read, ch);
        assert(!isor_get_channel(read, channel));

exit:
        if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
                send_proxy_command(read, 1, 0, e, NULL, channel);
        } else {
                gf_service_disconnect_ack(read->service, channel, e);
        }
        gf_mx_v(read->segment_mutex);
        return e;
}

GF_Err ISOR_ChannelGetSLP(GF_InputService *plug, 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)
{
        ISOMChannel *ch;
        ISOMReader *read;
        if (!plug || !plug->priv) return GF_SERVICE_ERROR;
        /*cannot read native SL-PDUs*/
        if (!out_sl_hdr) return GF_NOT_SUPPORTED;
        read = (ISOMReader *) plug->priv;
        if (!read->mov) return GF_SERVICE_ERROR;

        *out_data_ptr = NULL;
        *out_data_size = 0;
        *sl_compressed = GF_FALSE;
        *out_reception_status = GF_OK;
        *is_new_data = GF_FALSE;
        ch = isor_get_channel(read, channel);
        if (!ch) return GF_STREAM_NOT_FOUND;
        if (!ch->is_playing) return GF_OK;

        *is_new_data = GF_FALSE;
        if (!ch->sample) {
                /*get sample*/
                gf_mx_p(read->segment_mutex);
                isor_reader_get_sample(ch);
                gf_mx_v(read->segment_mutex);
                *is_new_data = ch->sample ? GF_TRUE : GF_FALSE;
        }

        if (ch->sample) {
                *out_data_ptr = ch->sample->data;
                *out_data_size = ch->sample->dataLength;
                *out_sl_hdr = ch->current_slh;
        }
        *out_reception_status = ch->last_state;
        if (read->waiting_for_data)
                *out_reception_status = GF_BUFFER_TOO_SMALL;

        return GF_OK;
}

GF_Err ISOR_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel)
{
        ISOMChannel *ch;
        ISOMReader *read;
        if (!plug || !plug->priv) return GF_SERVICE_ERROR;
        read = (ISOMReader *) plug->priv;
        if (!read->mov) return GF_SERVICE_ERROR;
        ch = isor_get_channel(read, channel);
        if (!ch) return GF_STREAM_NOT_FOUND;
        if (!ch->is_playing) return GF_SERVICE_ERROR;

        if (ch->sample) {
                isor_reader_release_sample(ch);
                /*release sample*/
        }
        return GF_OK;
}

/*switch channel quality. Return next channel or current channel if error*/
static
u32 gf_channel_switch_quality(ISOMChannel *ch, GF_ISOFile *the_file, Bool switch_up)
{
        u32 i, count, next_track, trackID, cur_track;
        s32 ref_count;

        cur_track = ch->next_track ? ch->next_track : ch->track;
        count = gf_isom_get_track_count(the_file);
        trackID = gf_isom_get_track_id(the_file, cur_track);
        next_track = 0;

        if (switch_up)
        {
                for (i = 0; i < count; i++)
                {
                        ref_count = gf_isom_get_reference_count(the_file, i+1, GF_ISOM_REF_SCAL);
                        if (ref_count < 0)
                                return cur_track; //error
                        else if (ref_count == 0)
                                continue;
                        /*next track is the one that has the last reference of type GF_ISOM_REF_SCAL refers to this current track*/
                        else if ((u32)ref_count == gf_isom_has_track_reference(the_file, i+1, GF_ISOM_REF_SCAL, trackID))
                        {
                                next_track = i+1;
                                break;
                        }
                }
                /*this is the highest quality*/
                if (!next_track)
                        return cur_track;
        }
        else
        {
                if (cur_track == ch->base_track)
                        return cur_track;
                ref_count = gf_isom_get_reference_count(the_file, cur_track, GF_ISOM_REF_SCAL);
                if (ref_count <= 0)
                        return cur_track;
                gf_isom_get_reference(the_file, cur_track, GF_ISOM_REF_SCAL, ref_count, &next_track);
                if (!next_track)
                        return cur_track;
        }

        /*in scalable mode add SPS/PPS in-band*/
        gf_isom_set_nalu_extract_mode(the_file, next_track, ch->nalu_extract_mode);

        return next_track;
}


GF_Err ISOR_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com)
{
        Double track_dur, media_dur;
        ISOMChannel *ch;
        ISOMReader *read;
        u32 count, i;

        if (!plug || !plug->priv || !com) return GF_SERVICE_ERROR;
        read = (ISOMReader *) plug->priv;
        if (read->disconnected) return GF_OK;

        if (com->command_type==GF_NET_SERVICE_INFO) {
                u32 tag_len;
                const char *tag;
                if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_NAME, &tag, &tag_len)==GF_OK) com->info.name = tag;
                if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_ARTIST, &tag, &tag_len)==GF_OK) com->info.artist = tag;
                if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_ALBUM, &tag, &tag_len)==GF_OK) com->info.album = tag;
                if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_COMMENT, &tag, &tag_len)==GF_OK) com->info.comment = tag;
                if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_TRACK, &tag, &tag_len)==GF_OK) {
                        com->info.track_info = (((tag[2]<<8)|tag[3]) << 16) | ((tag[4]<<8)|tag[5]);
                }
                if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_COMPOSER, &tag, &tag_len)==GF_OK) com->info.composer = tag;
                if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_WRITER, &tag, &tag_len)==GF_OK) com->info.writer = tag;
                if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_GENRE, &tag, &tag_len)==GF_OK) {
                        if (tag[0]) {
                                com->info.genre = 0;
                        } else {
                                com->info.genre = (tag[0]<<8) | tag[1];
                        }
                }
                return GF_OK;
        }
        if (com->command_type==GF_NET_SERVICE_HAS_AUDIO) {
                u32 i, count;
                count = gf_isom_get_track_count(read->mov);
                for (i=0; i<count; i++) {
                        if (gf_isom_get_media_type(read->mov, i+1) == GF_ISOM_MEDIA_AUDIO) return GF_OK;
                }
                return GF_NOT_SUPPORTED;
        }

        if (com->command_type == GF_NET_SERVICE_QUALITY_SWITCH)
        {
                count = gf_list_count(read->channels);
                for (i = 0; i < count; i++)
                {
                        ch = (ISOMChannel *)gf_list_get(read->channels, i);
                        if (ch->base_track && gf_isom_needs_layer_reconstruction(read->mov)) {
                                ch->next_track = gf_channel_switch_quality(ch, read->mov, com->switch_quality.up);
                        }
                }
                return GF_OK;
        }
        if (com->command_type == GF_NET_SERVICE_PROXY_DATA_RECEIVE) {
                isor_flush_data(read, 1, com->proxy_data.is_chunk);
                return GF_OK;
        }
        if (com->command_type == GF_NET_SERVICE_FLUSH_DATA) {
                if (read->nb_playing && plug->query_proxy)
                        isor_flush_data(read, 0, 0);
                return GF_OK;
        }
        if (com->command_type == GF_NET_SERVICE_CAN_REVERSE_PLAYBACK)
                return GF_OK;

        if (!com->base.on_channel) return GF_NOT_SUPPORTED;

        ch = isor_get_channel(read, com->base.on_channel);
        if (!ch) return GF_STREAM_NOT_FOUND;

        switch (com->command_type) {
        case GF_NET_CHAN_SET_PADDING:
                if (!ch->track) return GF_OK;
                gf_isom_set_sample_padding(read->mov, ch->track, com->pad.padding_bytes);
                return GF_OK;
        case GF_NET_CHAN_SET_PULL:
                //we don't pull in DASH base services, we flush as soon as we have a complete segment
#ifndef DASH_USE_PULL
                if (read->input->proxy_udta && !read->input->proxy_type)
                        return GF_NOT_SUPPORTED;
#endif

                ch->is_pulling = 1;
                return GF_OK;
        case GF_NET_CHAN_INTERACTIVE:
                return GF_OK;
        case GF_NET_CHAN_BUFFER:
                //dash or HTTP, do rebuffer if not disabled
                if (plug->query_proxy) {
                } else if (read->dnload) {
                        ch->buffer_min = com->buffer.min;
                        ch->buffer_max = com->buffer.max;
                } else {
                        com->buffer.max = com->buffer.min = 0;
                }
                return GF_OK;
        case GF_NET_CHAN_DURATION:
                if (!ch->track) {
                        com->duration.duration = 0;
                        return GF_OK;
                }
                ch->duration = gf_isom_get_track_duration(read->mov, ch->track);
                track_dur = (Double) (s64) ch->duration;
                track_dur /= read->time_scale;
                if (gf_isom_get_edit_segment_count(read->mov, ch->track)) {
                        com->duration.duration = (Double) track_dur;
                        ch->duration = (u32) (track_dur * ch->time_scale);
                } else {
                        /*some file indicate a wrong TrackDuration, get the longest*/
                        ch->duration = gf_isom_get_media_duration(read->mov, ch->track);
                        media_dur = (Double) (s64) ch->duration;
                        media_dur /= ch->time_scale;
                        com->duration.duration = MAX(track_dur, media_dur);
                }
                return GF_OK;
        case GF_NET_CHAN_PLAY:

                gf_mx_p(read->segment_mutex);
                isor_reset_reader(ch);
                ch->speed = com->play.speed;
                read->reset_frag_state = 1;
                if (read->frag_type)
                        read->frag_type = 1;
                gf_mx_v(read->segment_mutex);

                ch->start = ch->end = 0;
                if (com->play.speed>0) {
                        Double t;
                        if (com->play.start_range>=0) {
                                t = com->play.start_range;
                                t *= ch->time_scale;
                                ch->start = (u64) t;
                        }
                        if (com->play.end_range >= com->play.start_range) {
                                t = com->play.end_range;
                                t *= ch->time_scale;
                                ch->end = (u64) t;
                        }
                } else if (com->play.speed<0) {
                        Double end = com->play.end_range;
                        if (end==-1) end = 0;
                        ch->start = (u64) (s64) (com->play.start_range * ch->time_scale);
                        if (end <= com->play.start_range)
                                ch->end = (u64) (s64) (end  * ch->time_scale);
                }
                ch->is_playing = 1;
                if (com->play.dash_segment_switch) ch->wait_for_segment_switch = 1;
                GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] Starting channel playback "LLD" to "LLD" (%g to %g)\n", ch->start, ch->end, com->play.start_range, com->play.end_range));

                //and check buffer level on play request
                isor_check_buffer_level(read);
                read->nb_playing++;
                return GF_OK;
        case GF_NET_CHAN_STOP:
                if (read->nb_playing) read->nb_playing--;
                isor_reset_reader(ch);
                return GF_OK;

        case GF_NET_CHAN_SET_SPEED:
                gf_mx_p(read->segment_mutex);
                ch->speed = com->play.speed;
                gf_mx_v(read->segment_mutex);
                return GF_OK;
        /*nothing to do on MP4 for channel config*/
        case GF_NET_CHAN_CONFIG:
                return GF_OK;
        case GF_NET_CHAN_GET_PIXEL_AR:
                return gf_isom_get_pixel_aspect_ratio(read->mov, ch->track, 1, &com->par.hSpacing, &com->par.vSpacing);
        case GF_NET_CHAN_GET_DSI:
        {
                /*it may happen that there are conflicting config when using ESD URLs...*/
                GF_DecoderConfig *dcd = gf_isom_get_decoder_config(read->mov, ch->track, 1);
                com->get_dsi.dsi = NULL;
                com->get_dsi.dsi_len = 0;
                if (dcd) {
                        if (dcd->decoderSpecificInfo) {
                                com->get_dsi.dsi = dcd->decoderSpecificInfo->data;
                                com->get_dsi.dsi_len = dcd->decoderSpecificInfo->dataLength;
                                dcd->decoderSpecificInfo->data = NULL;
                        }
                        gf_odf_desc_del((GF_Descriptor *) dcd);
                }
                return GF_OK;
        }
        case GF_NET_CHAN_NALU_MODE:
                ch->nalu_extract_mode = GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG;

                //when this is set, we work in real scalable (eg N streams reassembled by the player) so only extract the layer. This wll need refinements if we plan to support
                //several scalable layers ...
                if (com->nalu_mode.extract_mode>=1) {
                        if (com->nalu_mode.extract_mode==2) {
                                ch->disable_seek = 1;
                        }
                        ch->nalu_extract_mode |= GF_ISOM_NALU_EXTRACT_ANNEXB_FLAG | GF_ISOM_NALU_EXTRACT_VDRD_FLAG | GF_ISOM_NALU_EXTRACT_LAYER_ONLY;
                }
                gf_isom_set_nalu_extract_mode(ch->owner->mov, ch->track, ch->nalu_extract_mode);
                break;
        default:
                break;
        }
        return GF_NOT_SUPPORTED;
}

static Bool ISOR_CanHandleURLInService(GF_InputService *plug, const char *url)
{
        char szURL[2048], *sep;
        ISOMReader *read = (ISOMReader *)plug->priv;
        const char *this_url = gf_service_get_url(read->service);
        if (!this_url || !url) return GF_FALSE;

        if (!strcmp(this_url, url)) return GF_TRUE;

        strcpy(szURL, this_url);
        sep = strrchr(szURL, '#');
        if (sep) sep[0] = 0;

        /*direct addressing in service*/
        if (url[0] == '#') return GF_TRUE;
        if (strnicmp(szURL, url, sizeof(char)*strlen(szURL))) return GF_FALSE;
        return GF_TRUE;
}

GF_InputService *isor_client_load()
{
        ISOMReader *reader;
        GF_InputService *plug;
        GF_SAFEALLOC(plug, GF_InputService);
        if (!plug) return NULL;
        GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC IsoMedia Reader", "gpac distribution")

        GF_SAFEALLOC(reader, ISOMReader);
        if (!reader) {
                gf_free(plug);
                return NULL;
        }
        reader->channels = gf_list_new();
        reader->segment_mutex = gf_mx_new("ISO Segment");

        plug->priv = reader;
        
        plug->RegisterMimeTypes = ISOR_RegisterMimeTypes;
        plug->CanHandleURL = ISOR_CanHandleURL;
        plug->ConnectService = ISOR_ConnectService;
        plug->CloseService = ISOR_CloseService;
        plug->GetServiceDescriptor = ISOR_GetServiceDesc;
        plug->ConnectChannel = ISOR_ConnectChannel;
        plug->DisconnectChannel = ISOR_DisconnectChannel;
        plug->ServiceCommand = ISOR_ServiceCommand;
        plug->CanHandleURLInService = ISOR_CanHandleURLInService;
        /*we do support pull mode*/
        plug->ChannelGetSLP = ISOR_ChannelGetSLP;
        plug->ChannelReleaseSLP = ISOR_ChannelReleaseSLP;
        return plug;
}

void isor_client_del(GF_BaseInterface *bi)
{
        GF_InputService *plug = (GF_InputService *) bi;
        ISOMReader *read = (ISOMReader *)plug->priv;

        if (read->segment_mutex) gf_mx_del(read->segment_mutex);
        gf_list_del(read->channels);
        gf_free(read);
        gf_free(bi);
}

#endif /*GPAC_DISABLE_ISOM*/

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