LCOV - code coverage report
Current view: top level - media_tools - route_dmx.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 783 999 78.4 %
Date: 2021-04-29 23:48:07 Functions: 37 38 97.4 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2018-2020
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / Media Tools ROUTE (ATSC3, DVB-I) demux sub-project
       9             :  *
      10             :  *  GPAC is free software; you can redistribute it and/or modify
      11             :  *  it under the terms of the GNU Lesser General Public License as published by
      12             :  *  the Free Software Foundation; either version 2, or (at your option)
      13             :  *  any later version.
      14             :  *
      15             :  *  GPAC is distributed in the hope that it will be useful,
      16             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :  *  GNU Lesser General Public License for more details.
      19             :  *
      20             :  *  You should have received a copy of the GNU Lesser General Public
      21             :  *  License along with this library; see the file COPYING.  If not, write to
      22             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      23             :  *
      24             :  */
      25             : 
      26             : #include <gpac/route.h>
      27             : 
      28             : #ifndef GPAC_DISABLE_ROUTE
      29             : 
      30             : #include <gpac/network.h>
      31             : #include <gpac/bitstream.h>
      32             : #include <gpac/xml.h>
      33             : #include <gpac/thread.h>
      34             : 
      35             : #define GF_ROUTE_SOCK_SIZE      0x80000
      36             : 
      37             : typedef struct
      38             : {
      39             :         u8 codepoint;
      40             :         u8 format_id;
      41             :         u8 frag;
      42             :         u8 order;
      43             :         u32 src_fec_payload_id;
      44             : } GF_ROUTELCTReg;
      45             : 
      46             : typedef struct
      47             : {
      48             :         char *filename;
      49             :         u32 toi;
      50             :         u32 crc;
      51             : } GF_ROUTELCTFile;
      52             : 
      53             : typedef struct
      54             : {
      55             :         u32 tsi;
      56             :         char *toi_template;
      57             :         GF_List *static_files;
      58             : 
      59             :         GF_ROUTELCTReg CPs[8];
      60             :         u32 nb_cps;
      61             :         u32 last_dispatched_tsi, last_dispatched_toi;
      62             :         Bool tsi_init;
      63             : } GF_ROUTELCTChannel;
      64             : 
      65             : typedef enum
      66             : {
      67             :         GF_LCT_OBJ_INIT=0,
      68             :         GF_LCT_OBJ_RECEPTION,
      69             :         GF_LCT_OBJ_DONE_ERR,
      70             :         GF_LCT_OBJ_DONE,
      71             :         GF_LCT_OBJ_DISPATCHED,
      72             : } GF_LCTObjectStatus;
      73             : 
      74             : typedef struct
      75             : {
      76             :         u32 toi, tsi;
      77             :         u32 total_length;
      78             :         //fragment reaggregation
      79             :         char *payload;
      80             :         u32 nb_bytes, nb_recv_bytes, alloc_size;
      81             :         u32 nb_frags, nb_alloc_frags, nb_recv_frags;
      82             :         GF_LCTFragInfo *frags;
      83             :         GF_LCTObjectStatus status;
      84             :         u32 download_time_ms;
      85             :         u32 last_gather_time;
      86             :     Bool closed_flag;
      87             : 
      88             :         GF_ROUTELCTChannel *rlct;
      89             :         GF_ROUTELCTFile *rlct_file;
      90             : 
      91             :         u32 prev_start_offset;
      92             : 
      93             :     char solved_path[GF_MAX_PATH];
      94             :     
      95             :     GF_Blob blob;
      96             :         
      97             :         void *udta;
      98             : } GF_LCTObject;
      99             : 
     100             : 
     101             : 
     102             : typedef struct
     103             : {
     104             :         GF_Socket *sock;
     105             : 
     106             :         GF_List *channels;
     107             : } GF_ROUTESession;
     108             : 
     109             : typedef enum
     110             : {
     111             :         GF_ROUTE_TUNE_OFF=0,
     112             :         GF_ROUTE_TUNE_ON,
     113             :         GF_ROUTE_TUNE_SLS_ONLY,
     114             : } GF_ROUTETuneMode;
     115             : 
     116             : typedef struct
     117             : {
     118             :         u32 service_id;
     119             :         u32 protocol;
     120             :         u32 mpd_version, stsid_version;
     121             :         GF_Socket *sock;
     122             :         u32 secondary_sockets;
     123             :         GF_List *objects;
     124             :         GF_LCTObject *last_active_obj;
     125             : 
     126             :         u32 port;
     127             :         char *dst_ip;
     128             :         u32 last_dispatched_toi_on_tsi_zero;
     129             :         u32 stsid_crc;
     130             : 
     131             :         GF_List *route_sessions;
     132             :         GF_ROUTETuneMode tune_mode;
     133             :         void *udta;
     134             : } GF_ROUTEService;
     135             : 
     136             : struct __gf_routedmx {
     137             :         const char *ip_ifce;
     138             :         GF_Socket *atsc_sock;
     139             :         u8 *buffer;
     140             :         u32 buffer_size;
     141             :         u8 *unz_buffer;
     142             :         u32 unz_buffer_size;
     143             : 
     144             :         u32 reorder_timeout;
     145             :         Bool force_reorder;
     146             :     Bool progressive_dispatch;
     147             :     
     148             :         u32 slt_version, rrt_version, systime_version, aeat_version;
     149             :         GF_List *services;
     150             : 
     151             :         GF_List *object_reservoir;
     152             :         GF_BitStream *bs;
     153             : 
     154             :         GF_DOMParser *dom;
     155             :         u32 service_autotune;
     156             :         Bool tune_all_sls;
     157             : 
     158             :         GF_SockGroup *active_sockets;
     159             : 
     160             : 
     161             :         void (*on_event)(void *udta, GF_ROUTEEventType evt, u32 evt_param, GF_ROUTEEventFileInfo *info);
     162             :         void *udta;
     163             : 
     164             :         u32 debug_tsi;
     165             : 
     166             :         u64 nb_packets;
     167             :         u64 total_bytes_recv;
     168             :         u64 first_pck_time, last_pck_time;
     169             : 
     170             :     //for now use a single mutex for all blob access
     171             :     GF_Mutex *blob_mx;
     172             : 
     173             : };
     174             : 
     175           7 : static void gf_route_static_files_del(GF_List *files)
     176             : {
     177          21 :         while (gf_list_count(files)) {
     178           7 :                 GF_ROUTELCTFile *rf = gf_list_pop_back(files);
     179           7 :                 gf_free(rf->filename);
     180           7 :                 gf_free(rf);
     181             :         }
     182           7 :         gf_list_del(files);
     183           7 : }
     184             : 
     185           6 : static void gf_route_route_session_del(GF_ROUTESession *rs)
     186             : {
     187           6 :         if (rs->sock) gf_sk_del(rs->sock);
     188          13 :         while (gf_list_count(rs->channels)) {
     189           7 :                 GF_ROUTELCTChannel *lc = gf_list_pop_back(rs->channels);
     190           7 :                 gf_route_static_files_del(lc->static_files);
     191           7 :                 gf_free(lc->toi_template);
     192           7 :                 gf_free(lc);
     193             :         }
     194           6 :         gf_list_del(rs->channels);
     195           6 :         gf_free(rs);
     196           6 : }
     197             : 
     198          29 : static void gf_route_lct_obj_del(GF_LCTObject *o)
     199             : {
     200          29 :         if (o->frags) gf_free(o->frags);
     201          29 :         if (o->payload) gf_free(o->payload);
     202          29 :         gf_free(o);
     203          29 : }
     204             : 
     205           6 : static void gf_route_service_del(GF_ROUTEDmx *routedmx, GF_ROUTEService *s)
     206             : {
     207           6 :         if (s->sock) {
     208           6 :                 gf_sk_group_unregister(routedmx->active_sockets, s->sock);
     209           6 :                 gf_sk_del(s->sock);
     210             :         }
     211          12 :         while (gf_list_count(s->route_sessions)) {
     212           6 :                 GF_ROUTESession *rsess = gf_list_pop_back(s->route_sessions);
     213           6 :                 gf_route_route_session_del(rsess);
     214             :         }
     215           6 :         gf_list_del(s->route_sessions);
     216             : 
     217          23 :         while (gf_list_count(s->objects)) {
     218          17 :                 GF_LCTObject *o = gf_list_pop_back(s->objects);
     219          17 :                 gf_route_lct_obj_del(o);
     220             :         }
     221           6 :         gf_list_del(s->objects);
     222           6 :         if (s->dst_ip) gf_free(s->dst_ip);
     223           6 :         gf_free(s);
     224           6 : }
     225             : 
     226             : GF_EXPORT
     227           6 : void gf_route_dmx_del(GF_ROUTEDmx *routedmx)
     228             : {
     229           6 :         if (routedmx->buffer) gf_free(routedmx->buffer);
     230           6 :         if (routedmx->unz_buffer) gf_free(routedmx->unz_buffer);
     231           6 :         if (routedmx->atsc_sock) gf_sk_del(routedmx->atsc_sock);
     232           6 :     if (routedmx->dom) gf_xml_dom_del(routedmx->dom);
     233           6 :     if (routedmx->blob_mx) gf_mx_del(routedmx->blob_mx);
     234           6 :         if (routedmx->services) {
     235          12 :                 while (gf_list_count(routedmx->services)) {
     236           6 :                         GF_ROUTEService *s = gf_list_pop_back(routedmx->services);
     237           6 :                         gf_route_service_del(routedmx, s);
     238             :                 }
     239           6 :                 gf_list_del(routedmx->services);
     240             :         }
     241           6 :         if (routedmx->active_sockets) gf_sk_group_del(routedmx->active_sockets);
     242           6 :         if (routedmx->object_reservoir) {
     243          18 :                 while (gf_list_count(routedmx->object_reservoir)) {
     244          12 :                         GF_LCTObject *obj = gf_list_pop_back(routedmx->object_reservoir);
     245          12 :                         gf_route_lct_obj_del(obj);
     246             :                 }
     247           6 :                 gf_list_del(routedmx->object_reservoir);
     248             :         }
     249           6 :         if (routedmx->bs) gf_bs_del(routedmx->bs);
     250           6 :         gf_free(routedmx);
     251           6 : }
     252             : 
     253           6 : static GF_ROUTEDmx *gf_route_dmx_new_internal(const char *ifce, u32 sock_buffer_size, Bool is_atsc,
     254             :                                                           void (*on_event)(void *udta, GF_ROUTEEventType evt, u32 evt_param, GF_ROUTEEventFileInfo *info),
     255             :                                                           void *udta)
     256             : {
     257             :         GF_ROUTEDmx *routedmx;
     258             :         GF_Err e;
     259           6 :         GF_SAFEALLOC(routedmx, GF_ROUTEDmx);
     260           6 :         if (!routedmx) {
     261           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Failed to allocate ROUTE demuxer\n"));
     262             :                 return NULL;
     263             :         }
     264           6 :         routedmx->ip_ifce = ifce;
     265           6 :         routedmx->dom = gf_xml_dom_new();
     266           6 :         if (!routedmx->dom) {
     267           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Failed to allocate DOM parser\n" ));
     268           0 :                 gf_route_dmx_del(routedmx);
     269           0 :                 return NULL;
     270             :         }
     271           6 :         routedmx->services = gf_list_new();
     272           6 :         if (!routedmx->services) {
     273           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Failed to allocate ROUTE service list\n" ));
     274           0 :                 gf_route_dmx_del(routedmx);
     275           0 :                 return NULL;
     276             :         }
     277           6 :         routedmx->object_reservoir = gf_list_new();
     278           6 :         if (!routedmx->object_reservoir) {
     279           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Failed to allocate ROUTE object reservoir\n" ));
     280           0 :                 gf_route_dmx_del(routedmx);
     281           0 :                 return NULL;
     282             :         }
     283           6 :     routedmx->blob_mx = gf_mx_new("ROUTEBlob");
     284           6 :     if (!routedmx->blob_mx) {
     285           0 :         GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Failed to allocate ROUTE blob mutex\n" ));
     286           0 :         gf_route_dmx_del(routedmx);
     287           0 :         return NULL;
     288             :     }
     289             : 
     290           6 :         if (!sock_buffer_size) sock_buffer_size = GF_ROUTE_SOCK_SIZE;
     291           6 :         routedmx->unz_buffer_size = sock_buffer_size;
     292             :         //we store one UDP packet, or realloc to store LLS signaling so starting with 10k should be enough in most cases
     293           6 :         routedmx->buffer_size = 10000;
     294           6 :         routedmx->buffer = gf_malloc(routedmx->buffer_size);
     295           6 :         if (!routedmx->buffer) {
     296           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Failed to allocate socket buffer\n"));
     297           0 :                 gf_route_dmx_del(routedmx);
     298           0 :                 return NULL;
     299             :         }
     300           6 :         routedmx->unz_buffer = gf_malloc(routedmx->unz_buffer_size);
     301           6 :         if (!routedmx->unz_buffer) {
     302           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Failed to allocate socket buffer\n"));
     303           0 :                 gf_route_dmx_del(routedmx);
     304           0 :                 return NULL;
     305             :         }
     306             : 
     307           6 :         routedmx->active_sockets = gf_sk_group_new();
     308           6 :         if (!routedmx->active_sockets) {
     309           0 :                 gf_route_dmx_del(routedmx);
     310           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Failed to create socket group\n"));
     311             :                 return NULL;
     312             :         }
     313             :         //create static bs
     314           6 :         routedmx->bs = gf_bs_new((char*)&e, 1, GF_BITSTREAM_READ);
     315             : 
     316           6 :         routedmx->reorder_timeout = 5000;
     317             : 
     318           6 :         routedmx->on_event = on_event;
     319           6 :         routedmx->udta = udta;
     320             : 
     321           6 :         if (!is_atsc)
     322             :                 return routedmx;
     323             : 
     324           2 :         routedmx->atsc_sock = gf_sk_new(GF_SOCK_TYPE_UDP);
     325           2 :         if (!routedmx->atsc_sock) {
     326           0 :                 gf_route_dmx_del(routedmx);
     327           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Failed to create UDP socket\n"));
     328             :                 return NULL;
     329             :         }
     330           2 :         gf_sk_group_register(routedmx->active_sockets, routedmx->atsc_sock);
     331             : 
     332           2 :         gf_sk_set_usec_wait(routedmx->atsc_sock, 1);
     333           2 :         e = gf_sk_setup_multicast(routedmx->atsc_sock, GF_ATSC_MCAST_ADDR, GF_ATSC_MCAST_PORT, 1, GF_FALSE, (char *) ifce);
     334           2 :         if (e) {
     335           0 :                 gf_route_dmx_del(routedmx);
     336           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Failed to bind to multicast address on interface %s\n", ifce ? ifce : "default"));
     337             :                 return NULL;
     338             :         }
     339           2 :         gf_sk_set_buffer_size(routedmx->atsc_sock, GF_FALSE, sock_buffer_size);
     340             :         //gf_sk_set_block_mode(routedmx->sock, GF_TRUE);
     341           2 :         return routedmx;
     342             : }
     343             : 
     344           2 : static void gf_route_register_service_sockets(GF_ROUTEDmx *routedmx, GF_ROUTEService *s, Bool do_register)
     345             : {
     346             :     u32 i;
     347             :     GF_ROUTESession *rsess;
     348           2 :     if (do_register) gf_sk_group_register(routedmx->active_sockets, s->sock);
     349           0 :     else gf_sk_group_unregister(routedmx->active_sockets, s->sock);
     350             : 
     351           4 :     if (!s->secondary_sockets) return;
     352             : 
     353           0 :     i=0;
     354           0 :     while ((rsess = gf_list_enum(s->route_sessions, &i))) {
     355           0 :         if (! rsess->sock) continue;
     356           0 :         if (do_register) gf_sk_group_register(routedmx->active_sockets, rsess->sock);
     357           0 :         else gf_sk_group_unregister(routedmx->active_sockets, rsess->sock);
     358             :     }
     359             : }
     360             : 
     361           6 : static void gf_route_create_service(GF_ROUTEDmx *routedmx, const char *dst_ip, u32 dst_port, u32 service_id, u32 protocol)
     362             : {
     363             :         GF_ROUTEService *service;
     364             :         GF_Err e;
     365             : 
     366           6 :         GF_LOG(GF_LOG_INFO, GF_LOG_ROUTE, ("[ROUTE] Setting up service %d destination IP %s port %d\n", service_id, dst_ip, dst_port));
     367             : 
     368           6 :         GF_SAFEALLOC(service, GF_ROUTEService);
     369           6 :         if (!service) {
     370           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Failed to allocate service %d\n", service_id));
     371             :                 return;
     372             :         }
     373           6 :         service->service_id = service_id;
     374           6 :         service->protocol = protocol;
     375             : 
     376           6 :         service->sock = gf_sk_new(GF_SOCK_TYPE_UDP);
     377           6 :         gf_sk_set_usec_wait(service->sock, 1);
     378           6 :         e = gf_sk_setup_multicast(service->sock, dst_ip, dst_port, 0, GF_FALSE, (char*) routedmx->ip_ifce);
     379           6 :         if (e) {
     380           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Failed to setup multicast on %s:%d for service %d\n", dst_ip, dst_port, service_id));
     381           0 :                 gf_route_service_del(routedmx, service);
     382           0 :                 return;
     383             :         }
     384           6 :         gf_sk_set_buffer_size(service->sock, GF_FALSE, routedmx->unz_buffer_size);
     385             :         //gf_sk_set_block_mode(service->sock, GF_TRUE);
     386             : 
     387           6 :         service->dst_ip = gf_strdup(dst_ip);
     388           6 :         service->port = dst_port;
     389           6 :         service->objects = gf_list_new();
     390           6 :         service->route_sessions = gf_list_new();
     391             : 
     392           6 :         gf_list_add(routedmx->services, service);
     393             : 
     394           6 :         if (routedmx->atsc_sock) {
     395           2 :                 if (routedmx->service_autotune==0xFFFFFFFF) service->tune_mode = GF_ROUTE_TUNE_ON;
     396           2 :                 else if (routedmx->service_autotune==0xFFFFFFFE) {
     397           2 :                         service->tune_mode = GF_ROUTE_TUNE_ON;
     398           2 :                         routedmx->service_autotune -= 1;
     399             :                 }
     400           0 :                 else if (routedmx->service_autotune==service_id) service->tune_mode = GF_ROUTE_TUNE_ON;
     401           0 :                 else if (routedmx->tune_all_sls) service->tune_mode = GF_ROUTE_TUNE_SLS_ONLY;
     402             : 
     403             :                 //we are tuning, register socket
     404           2 :         if (service->tune_mode != GF_ROUTE_TUNE_OFF) {
     405             :                         //call gf_route_register_service_sockets rather than gf_sk_group_register for coverage purpose only
     406           2 :             gf_route_register_service_sockets(routedmx, service, GF_TRUE);
     407             :         }
     408             :         } else {
     409           4 :                 service->tune_mode = GF_ROUTE_TUNE_ON;
     410           4 :                 routedmx->service_autotune = service_id;
     411           4 :                 gf_sk_group_register(routedmx->active_sockets, service->sock);
     412             :         }
     413             : 
     414           6 :         if (routedmx->on_event) routedmx->on_event(routedmx->udta, GF_ROUTE_EVT_SERVICE_FOUND, service_id, NULL);
     415             : }
     416             : 
     417             : GF_EXPORT
     418           2 : GF_ROUTEDmx *gf_route_atsc_dmx_new(const char *ifce, u32 sock_buffer_size,
     419             :                                                                    void (*on_event)(void *udta, GF_ROUTEEventType evt, u32 evt_param, GF_ROUTEEventFileInfo *info),
     420             :                                                                    void *udta)
     421             : {
     422           2 :         return gf_route_dmx_new_internal(ifce, sock_buffer_size, GF_TRUE, on_event, udta);
     423             : 
     424             : }
     425             : GF_EXPORT
     426           4 : GF_ROUTEDmx *gf_route_dmx_new(const char *ip, u32 port, const char *ifce, u32 sock_buffer_size,
     427             :                                                           void (*on_event)(void *udta, GF_ROUTEEventType evt, u32 evt_param, GF_ROUTEEventFileInfo *info),
     428             :                                                           void *udta)
     429             : {
     430           4 :         GF_ROUTEDmx *routedmx = gf_route_dmx_new_internal(ifce, sock_buffer_size, GF_FALSE, on_event, udta);
     431           4 :         if (!routedmx) return NULL;
     432           4 :         gf_route_create_service(routedmx, ip, port, 1, 1);
     433           4 :         return routedmx;
     434             : }
     435             : 
     436             : GF_EXPORT
     437           2 : GF_Err gf_route_atsc3_tune_in(GF_ROUTEDmx *routedmx, u32 serviceID, Bool tune_all_sls)
     438             : {
     439             :         u32 i;
     440             :         GF_ROUTEService *s;
     441           2 :         if (!routedmx) return GF_BAD_PARAM;
     442           2 :         routedmx->service_autotune = serviceID;
     443           2 :         routedmx->tune_all_sls = tune_all_sls;
     444           2 :         i=0;
     445           4 :         while ((s = gf_list_enum(routedmx->services, &i))) {
     446           0 :                 GF_ROUTETuneMode prev_mode = s->tune_mode;
     447           0 :                 if (s->service_id==serviceID) s->tune_mode = GF_ROUTE_TUNE_ON;
     448           0 :                 else if (serviceID==0xFFFFFFFF) s->tune_mode = GF_ROUTE_TUNE_ON;
     449           0 :                 else if ((s->tune_mode!=GF_ROUTE_TUNE_ON) && (serviceID==0xFFFFFFFE)) {
     450           0 :                         s->tune_mode = GF_ROUTE_TUNE_ON;
     451           0 :                         serviceID = s->service_id;
     452             :                 } else {
     453           0 :                         s->tune_mode = tune_all_sls ? GF_ROUTE_TUNE_SLS_ONLY : GF_ROUTE_TUNE_OFF;
     454             :                 }
     455             :                 //we were previously not tuned
     456           0 :                 if (prev_mode == GF_ROUTE_TUNE_OFF) {
     457             :                         //we are now tuned, register sockets
     458           0 :                         if (s->tune_mode != GF_ROUTE_TUNE_OFF) {
     459           0 :                                 gf_route_register_service_sockets(routedmx, s, GF_TRUE);
     460             :                         }
     461             :                 }
     462             :                 //we were previously tuned
     463             :                 else {
     464             :                         //we are now not tuned, unregister sockets
     465           0 :                         if (s->tune_mode == GF_ROUTE_TUNE_OFF) {
     466           0 :                                 gf_route_register_service_sockets(routedmx, s, GF_FALSE);
     467             :                         }
     468             :                 }
     469             :         }
     470             :         return GF_OK;
     471             : }
     472             : 
     473             : GF_EXPORT
     474           6 : GF_Err gf_route_set_reorder(GF_ROUTEDmx *routedmx, Bool force_reorder, u32 timeout_ms)
     475             : {
     476           6 :         if (!routedmx) return GF_BAD_PARAM;
     477           6 :         routedmx->reorder_timeout = timeout_ms;
     478           6 :         routedmx->force_reorder = force_reorder;
     479           6 :         return GF_OK;
     480             : }
     481             : 
     482             : GF_EXPORT
     483           6 : GF_Err gf_route_set_allow_progressive_dispatch(GF_ROUTEDmx *routedmx, Bool allow_progressive)
     484             : {
     485           6 :     if (!routedmx) return GF_BAD_PARAM;
     486           6 :     routedmx->progressive_dispatch = allow_progressive;
     487           6 :     return GF_OK;
     488             : }
     489             : 
     490           2 : static GF_Err gf_route_dmx_process_slt(GF_ROUTEDmx *routedmx, GF_XMLNode *root)
     491             : {
     492             :         GF_XMLNode *n;
     493           2 :         u32 i=0;
     494             : 
     495           8 :         while ( ( n = gf_list_enum(root->content, &i)) ) {
     496           6 :                 if (n->type != GF_XML_NODE_TYPE) continue;
     497             :                 //setup service
     498           2 :                 if (!strcmp(n->name, "Service")) {
     499             :                         GF_XMLAttribute *att;
     500             :                         GF_XMLNode *m;
     501           2 :                         u32 j=0;
     502             :                         const char *dst_ip=NULL;
     503             :                         u32 dst_port = 0;
     504             :                         u32 protocol = 0;
     505           2 :                         u32 service_id=0;
     506          16 :                         while ( ( att = gf_list_enum(n->attributes, &j)) ) {
     507          14 :                                 if (!strcmp(att->name, "serviceId")) sscanf(att->value, "%u", &service_id);
     508             :                         }
     509             : 
     510           2 :                         j=0;
     511           8 :                         while ( ( m = gf_list_enum(n->content, &j)) ) {
     512           6 :                                 if (m->type != GF_XML_NODE_TYPE) continue;
     513           2 :                                 if (!strcmp(m->name, "BroadcastSvcSignaling")) {
     514           2 :                                         u32 k=0;
     515          10 :                                         while ( ( att = gf_list_enum(m->attributes, &k)) ) {
     516          10 :                                                 if (!strcmp(att->name, "slsProtocol")) protocol = atoi(att->value);
     517           8 :                                                 if (!strcmp(att->name, "slsDestinationIpAddress")) dst_ip = att->value;
     518           8 :                                                 else if (!strcmp(att->name, "slsDestinationUdpPort")) dst_port = atoi(att->value);
     519             :                                                 //don't care about the rest
     520             :                                         }
     521             :                                 }
     522             :                         }
     523             : 
     524           2 :                         if (!dst_ip || !dst_port) {
     525           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] No service destination IP or port found for service %d - ignoring service\n", service_id));
     526           0 :                                 continue;
     527             :                         }
     528           2 :                         if (protocol==2) {
     529           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] ATSC service %d using MMTP protocol is not supported - ignoring\n", service_id));
     530           0 :                                 continue;
     531             :                         }
     532           2 :                         if (protocol!=1) {
     533           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Unknown ATSC signaling protocol %d for service %d - ignoring\n", protocol, service_id));
     534           0 :                                 continue;
     535             :                         }
     536             : 
     537             :                         //todo - remove existing service ?
     538           2 :                         gf_route_create_service(routedmx, dst_ip, dst_port, service_id, protocol);
     539             : 
     540             :                 }
     541             :         }
     542           2 :         GF_LOG(GF_LOG_INFO, GF_LOG_ROUTE, ("[ROUTE] Done scaning all services\n"));
     543           2 :         if (routedmx->on_event) routedmx->on_event(routedmx->udta, GF_ROUTE_EVT_SERVICE_SCAN, 0, NULL);
     544           2 :         return GF_OK;
     545             : }
     546             : 
     547             : #ifndef GPAC_DISABLE_LOG
     548             : static const char *get_lct_obj_status_name(GF_LCTObjectStatus status)
     549             : {
     550           4 :         switch (status) {
     551             :         case GF_LCT_OBJ_INIT: return "init";
     552           0 :         case GF_LCT_OBJ_RECEPTION: return "reception";
     553           0 :         case GF_LCT_OBJ_DONE_ERR: return "done_error";
     554           1 :         case GF_LCT_OBJ_DONE: return "done";
     555           3 :         case GF_LCT_OBJ_DISPATCHED: return "dispatched";
     556             :         }
     557             :         return "unknown";
     558             : }
     559             : #endif // GPAC_DISABLE_LOG
     560             : 
     561          26 : static void gf_route_obj_to_reservoir(GF_ROUTEDmx *routedmx, GF_ROUTEService *s, GF_LCTObject *obj)
     562             : {
     563             : 
     564             :         assert (obj->status != GF_LCT_OBJ_RECEPTION);
     565             : 
     566          26 :     if (routedmx->on_event && obj->solved_path[0]) {
     567             :         GF_ROUTEEventFileInfo finfo;
     568             :         memset(&finfo, 0, sizeof(GF_ROUTEEventFileInfo));
     569          16 :                 finfo.filename = obj->solved_path;
     570          16 :                 finfo.udta = obj->udta;
     571          16 :         routedmx->on_event(routedmx->udta, GF_ROUTE_EVT_FILE_DELETE, s->service_id, &finfo);
     572             :     }
     573             :     
     574             :         //remove other objects
     575          30 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d : moving object tsi %u toi %u to reservoir (status %s)\n", s->service_id, obj->tsi, obj->toi, get_lct_obj_status_name(obj->status) ));
     576             : 
     577             : #ifndef GPAC_DISABLE_LOG
     578          26 :         if (gf_log_tool_level_on(GF_LOG_ROUTE, GF_LOG_DEBUG)){
     579           4 :                 u32 i, count = gf_list_count(s->objects);
     580           4 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d : active objects TOIs for tsi %u: ", s->service_id, obj->tsi));
     581          17 :                 for (i=0;i<count;i++) {
     582          17 :                         GF_LCTObject *o = gf_list_get(s->objects, i);
     583          17 :                         if (o==obj) continue;
     584          13 :                         if (o->tsi != obj->tsi) continue;
     585           7 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, (" %u", o->toi));
     586             :                 }
     587           4 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("\n"));
     588             :         }
     589             : #endif
     590             : 
     591          26 :         if (s->last_active_obj==obj) s->last_active_obj = NULL;
     592          26 :     obj->closed_flag = GF_FALSE;
     593          26 :         obj->nb_bytes = 0;
     594          26 :         obj->nb_frags = GF_FALSE;
     595          26 :         obj->nb_recv_frags = 0;
     596          26 :         obj->rlct = NULL;
     597          26 :         obj->rlct_file = NULL;
     598          26 :         obj->toi = 0;
     599          26 :     obj->tsi = 0;
     600          26 :         obj->udta = NULL;
     601          26 :     obj->solved_path[0] = 0;
     602          26 :         obj->total_length = 0;
     603          26 :         obj->prev_start_offset = 0;
     604          26 :         obj->download_time_ms = 0;
     605          26 :         obj->last_gather_time = 0;
     606          26 :         obj->status = GF_LCT_OBJ_INIT;
     607          26 :         gf_list_del_item(s->objects, obj);
     608          26 :         gf_list_add(routedmx->object_reservoir, obj);
     609             : 
     610          26 : }
     611             : 
     612         525 : static GF_Err gf_route_dmx_push_object(GF_ROUTEDmx *routedmx, GF_ROUTEService *s, GF_LCTObject *obj, Bool final_push, Bool partial, Bool updated, u64 bytes_done)
     613             : {
     614             :     char *filepath;
     615             :     Bool is_init = GF_FALSE;
     616             : 
     617         525 :     if (obj->rlct_file) {
     618          11 :         filepath = obj->rlct_file->filename ? obj->rlct_file->filename : "ghost-init.mp4";
     619             :         is_init = GF_TRUE;
     620             :         assert(final_push);
     621             :     } else {
     622         514 :         if (!obj->solved_path[0])
     623          19 :             sprintf(obj->solved_path, obj->rlct->toi_template, obj->toi);
     624         514 :         filepath = obj->solved_path;
     625             :     }
     626             : #ifndef GPAC_DISABLE_LOG
     627         525 :     if (partial) {
     628         506 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d got file %s (TSI %u TOI %u) size %d in %d ms (%d bytes in %d fragments)\n", s->service_id, filepath, obj->tsi, obj->toi, obj->total_length, obj->download_time_ms, obj->nb_bytes, obj->nb_recv_frags));
     629             :     } else {
     630          19 :         GF_LOG(GF_LOG_INFO, GF_LOG_ROUTE, ("[ROUTE] Service %d got file %s (TSI %u TOI %u) size %d in %d ms\n", s->service_id, filepath, obj->tsi, obj->toi, obj->total_length, obj->download_time_ms));
     631             :     }
     632             : #endif
     633             : 
     634         525 :     if (routedmx->on_event) {
     635             :         GF_ROUTEEventType evt_type;
     636             :         GF_ROUTEEventFileInfo finfo;
     637             :         memset(&finfo, 0, sizeof(GF_ROUTEEventFileInfo));
     638         525 :         finfo.filename = filepath;
     639         525 :                 obj->blob.data = obj->payload;
     640         525 :                 obj->blob.flags = 0;
     641         525 :                 if (final_push) {
     642          28 :                         if (!obj->total_length)
     643           2 :                                 obj->total_length = obj->alloc_size;
     644          28 :                         if (partial)
     645           9 :                                 obj->blob.flags |= GF_BLOB_CORRUPTED;
     646          28 :                         obj->blob.size = (u32) obj->total_length;
     647             :                 } else {
     648         497 :                         obj->blob.flags = GF_BLOB_IN_TRANSFER;
     649         497 :                         obj->blob.size = (u32) bytes_done;
     650             :                 }
     651         525 :                 finfo.blob = &obj->blob;         
     652         525 :         finfo.total_size = obj->total_length;
     653         525 :         finfo.tsi = obj->tsi;
     654         525 :         finfo.toi = obj->toi;
     655         525 :                 finfo.updated = updated;
     656         525 :                 finfo.udta = obj->udta;
     657         525 :         finfo.download_ms = obj->download_time_ms;
     658         525 :         if (is_init)
     659             :             evt_type = GF_ROUTE_EVT_FILE;
     660         514 :         else if (final_push) {
     661             :             evt_type = GF_ROUTE_EVT_DYN_SEG;
     662          17 :             finfo.nb_frags = obj->nb_frags;
     663          17 :             finfo.frags = obj->frags;
     664             :                         assert(obj->total_length <= obj->alloc_size);
     665             :         }
     666             :         else
     667             :             evt_type = GF_ROUTE_EVT_DYN_SEG_FRAG;
     668             : 
     669         525 :                 routedmx->on_event(routedmx->udta, evt_type, s->service_id, &finfo);
     670             :                 //store udta cookie
     671         525 :                 obj->udta = finfo.udta;
     672           0 :         } else if (final_push) {
     673             :                 //keep static files active, move other to reservoir
     674           0 :                 if (!obj->rlct_file)
     675           0 :                         gf_route_obj_to_reservoir(routedmx, s, obj);
     676             :         }
     677         525 :     return GF_OK;
     678             : }
     679             : 
     680          31 : static GF_Err gf_route_dmx_process_object(GF_ROUTEDmx *routedmx, GF_ROUTEService *s, GF_LCTObject *obj)
     681             : {
     682             :         Bool partial = GF_FALSE;
     683             :         Bool updated = GF_TRUE;
     684             : 
     685          31 :         if (!obj->rlct) {
     686           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d : internal error, no LCT ROUTE channel defined for object TSI %u TOI %u\n", s->service_id, obj->tsi, obj->toi));
     687             :                 return GF_SERVICE_ERROR;
     688             :         }
     689             :         assert(obj->status>GF_LCT_OBJ_RECEPTION);
     690             : 
     691          31 :         if (obj->status==GF_LCT_OBJ_DONE_ERR) {
     692           9 :                 if (obj->rlct->tsi_init) {
     693           2 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_ROUTE, ("[ROUTE] Service %d : object TSI %u TOI %u partial received only\n", s->service_id, obj->tsi, obj->toi));
     694             :                 }
     695             :                 partial = GF_TRUE;
     696             :         }
     697          31 :         obj->rlct->tsi_init = GF_TRUE;
     698          31 :         if (obj->status == GF_LCT_OBJ_DISPATCHED) return GF_OK;
     699          28 :         obj->status = GF_LCT_OBJ_DISPATCHED;
     700             : 
     701          28 :         if (obj->rlct_file) {
     702          11 :                 u32 crc = gf_crc_32(obj->payload, obj->total_length);
     703          11 :                 if (crc != obj->rlct_file->crc) {
     704           7 :                         obj->rlct_file->crc = crc;
     705             :                         updated = GF_TRUE;
     706             :                 } else {
     707             :                         updated = GF_FALSE;
     708             :                 }
     709             :         }
     710          28 :         return gf_route_dmx_push_object(routedmx, s, obj, GF_TRUE, partial, updated, 0);
     711             : }
     712             : 
     713          39 : static GF_Err gf_route_service_flush_object(GF_ROUTEService *s, GF_LCTObject *obj)
     714             : {
     715             :         u32 i;
     716             :         u64 start_offset = 0;
     717          39 :         obj->status = GF_LCT_OBJ_DONE;
     718          32 :         for (i=0; i<obj->nb_frags; i++) {
     719          39 :                 if (start_offset != obj->frags[i].offset) {
     720           7 :                         obj->status = GF_LCT_OBJ_DONE_ERR;
     721             :                         break;
     722             :                 }
     723          32 :                 start_offset += obj->frags[i].size;
     724             :         }
     725          39 :         if (start_offset != obj->total_length) {
     726           9 :                 obj->status = GF_LCT_OBJ_DONE_ERR;
     727             :         }
     728          39 :         obj->download_time_ms = gf_sys_clock() - obj->download_time_ms;
     729          39 :         return GF_EOS;
     730             : }
     731             : 
     732        1325 : static GF_Err gf_route_service_gather_object(GF_ROUTEDmx *routedmx, GF_ROUTEService *s, u32 tsi, u32 toi, u32 start_offset, char *data, u32 size, u32 total_len, Bool close_flag, Bool in_order, GF_ROUTELCTChannel *rlct, GF_LCTObject **gather_obj)
     733             : {
     734             :         Bool inserted, done;
     735             :         u32 i, count;
     736             :     Bool do_push = GF_FALSE;
     737        1325 :         GF_LCTObject *obj = s->last_active_obj;
     738             : 
     739        1325 :         if (routedmx->force_reorder)
     740             :                 in_order = GF_FALSE;
     741             : 
     742             :         //in case last packet(s) are duplicated after we sent the object, skip them
     743        1325 :         if (rlct) {
     744        1314 :                 if ((tsi==rlct->last_dispatched_tsi) && (toi==rlct->last_dispatched_toi)) {
     745           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d TSI %u TOI %u LCT fragment on already dispatched object, skipping\n", s->service_id, tsi, toi));
     746             :                         return GF_OK;
     747             :                 }
     748             :         } else {
     749          11 :                 if (!tsi && (toi==s->last_dispatched_toi_on_tsi_zero)) {
     750           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d TSI %u TOI %u LCT fragment on already dispatched object, skipping\n", s->service_id, tsi, toi));
     751             :                         return GF_OK;
     752             :                 }
     753             :         }
     754             : 
     755        1325 :         if (!obj || (obj->tsi!=tsi) || (obj->toi!=toi)) {
     756         137 :                 count = gf_list_count(s->objects);
     757         446 :                 for (i=0; i<count; i++) {
     758         403 :                         obj = gf_list_get(s->objects, i);
     759         403 :                         if ((obj->toi == toi) && (obj->tsi==tsi)) break;
     760             : 
     761         309 :                         if (!tsi && !obj->tsi && ((obj->toi&0xFFFFFF00) == (toi&0xFFFFFF00)) ) {
     762             :                                 //change in version of bundle but same other flags: reuse this one
     763           0 :                                 obj->nb_frags = obj->nb_recv_frags = 0;
     764           0 :                                 obj->nb_bytes = obj->nb_recv_bytes = 0;
     765           0 :                                 obj->total_length = total_len;
     766           0 :                                 obj->toi = toi;
     767           0 :                                 obj->status = GF_LCT_OBJ_INIT;
     768           0 :                                 break;
     769             :                         }
     770             :                         obj = NULL;
     771             :                 }
     772             :         }
     773        1325 :         if (!obj) {
     774          43 :                 obj = gf_list_pop_back(routedmx->object_reservoir);
     775          43 :                 if (!obj) {
     776          29 :                         GF_SAFEALLOC(obj, GF_LCTObject);
     777          29 :                         if (!obj) {
     778           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d failed to allocate LCT object TSI %u TOI %u\n", s->service_id, toi, tsi ));
     779             :                                 return GF_OUT_OF_MEM;
     780             :                         }
     781          29 :                         obj->nb_alloc_frags = 10;
     782          29 :                         obj->frags = gf_malloc(sizeof(GF_LCTFragInfo)*obj->nb_alloc_frags);
     783          29 :             obj->blob.mx = routedmx->blob_mx;
     784             :                 }
     785          43 :                 obj->toi = toi;
     786          43 :                 obj->tsi = tsi;
     787          43 :                 obj->status = GF_LCT_OBJ_INIT;
     788          43 :                 obj->total_length = total_len;
     789          43 :                 if (tsi && rlct) {
     790          32 :                         count = gf_list_count(rlct->static_files);
     791          32 :                         obj->rlct = rlct;
     792          32 :                         obj->rlct_file = NULL;
     793          53 :                         for (i=0; i<count; i++) {
     794          32 :                                 GF_ROUTELCTFile *rf = gf_list_get(rlct->static_files, i);
     795          32 :                                 if (rf->toi == toi) {
     796          11 :                                         obj->rlct_file = rf;
     797          11 :                                         break;
     798             :                                 }
     799             :                         }
     800             :                 }
     801             : 
     802          43 :                 if (!total_len) {
     803           7 :                         GF_LOG(GF_LOG_INFO, GF_LOG_ROUTE, ("[ROUTE] Service %d object TSI %u TOI %u started without total-length assigned !\n", s->service_id, tsi, toi ));
     804             :                 } else {
     805          36 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d starting object TSI %u TOI %u total-length %d\n", s->service_id, tsi, toi, total_len));
     806             : 
     807             :                 }
     808          43 :                 obj->download_time_ms = gf_sys_clock();
     809          43 :                 gf_list_add(s->objects, obj);
     810        1282 :         } else if (!obj->total_length && total_len) {
     811           3 :                 GF_LOG(GF_LOG_INFO, GF_LOG_ROUTE, ("[ROUTE] Service %d object TSI %u TOI %u was started without total-length assigned, assigning to %u\n", s->service_id, tsi, toi, total_len));
     812             :         
     813           3 :         if (obj->alloc_size < total_len) {
     814           3 :             gf_mx_p(routedmx->blob_mx);
     815           3 :             obj->payload = gf_realloc(obj->payload, total_len);
     816           3 :             obj->alloc_size = total_len;
     817           3 :             obj->blob.size = total_len;
     818           3 :             obj->blob.data = obj->payload;
     819           3 :             gf_mx_v(routedmx->blob_mx);
     820             :         }
     821           3 :                 obj->total_length = total_len;
     822        1279 :         } else if (total_len && (obj->total_length != total_len)) {
     823           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_ROUTE, ("[ROUTE] Service %d object TSI %u TOI %u mismatch in total-length %u  assigned, %u redeclared\n", s->service_id, tsi, toi, obj->total_length, total_len));
     824             :         }
     825        1325 :         if (s->last_active_obj != obj) {
     826             :                 //last object had EOS and not completed
     827         137 :                 if (s->last_active_obj && s->last_active_obj->closed_flag && (s->last_active_obj->status<GF_LCT_OBJ_DONE_ERR)) {
     828             :                         GF_LCTObject *o = s->last_active_obj;
     829           0 :                         if (o->tsi) {
     830           0 :                                 gf_route_service_flush_object(s, o);
     831           0 :                                 gf_route_dmx_process_object(routedmx, s, o);
     832             :                         } else {
     833           0 :                                 gf_route_obj_to_reservoir(routedmx, s, o);
     834             :                         }
     835             :                 }
     836             :                 //note that if not in order and no timeout, we wait forever !
     837         137 :                 else if (in_order || routedmx->reorder_timeout) {
     838         137 :                         count = gf_list_count(s->objects);
     839         447 :                         for (i=0; i<count; i++) {
     840             :                                 u32 new_count;
     841         447 :                                 GF_LCTObject *o = gf_list_get(s->objects, i);
     842         447 :                                 if (o==obj) break;
     843             :                                 //we can only detect losses if a new TOI on the same TSI is found
     844         310 :                                 if (o->tsi != obj->tsi) continue;
     845          86 :                                 if (o->status>=GF_LCT_OBJ_DONE_ERR) continue;
     846             : 
     847           9 :                                 if (!in_order) {
     848           0 :                                         u32 ellapsed = gf_sys_clock() - o->last_gather_time;
     849           0 :                                         if (ellapsed < routedmx->reorder_timeout)
     850           0 :                                                 continue;
     851             : 
     852           0 :                                         GF_LOG(GF_LOG_WARNING, GF_LOG_ROUTE, ("[ROUTE] Service %d object TSI %u TOI %u timeout after %d ms - forcing dispatch\n", s->service_id, o->tsi, o->toi, ellapsed ));
     853           9 :                                 } else if (o->rlct && !o->rlct->tsi_init) {
     854           7 :                                         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d object TSI %u TOI %u incomplete (tune-in) - forcing dispatch\n", s->service_id, o->tsi, o->toi, toi ));
     855             :                                 } else {
     856           2 :                                         GF_LOG(GF_LOG_WARNING, GF_LOG_ROUTE, ("[ROUTE] Service %d object TSI %u TOI %u not completely received but in-order delivery signaled and new TOI %u - forcing dispatch\n", s->service_id, o->tsi, o->toi, toi ));
     857             :                                 }
     858             : 
     859           9 :                                 if (o->tsi && o->nb_frags) {
     860           9 :                                         gf_route_service_flush_object(s, o);
     861           9 :                                         gf_route_dmx_process_object(routedmx, s, o);
     862             :                                 } else {
     863           0 :                                         gf_route_obj_to_reservoir(routedmx, s, o);
     864             :                                 }
     865           9 :                                 new_count = gf_list_count(s->objects);
     866             :                                 //objects purged
     867           9 :                                 if (new_count<count) {
     868             :                                         i=-1;
     869             :                                         count = new_count;
     870             :                                 }
     871             :                         }
     872             :                 }
     873         137 :                 s->last_active_obj = obj;
     874             :         }
     875        1325 :         *gather_obj = obj;
     876             :         assert(obj->toi == toi);
     877             :         assert(obj->tsi == tsi);
     878             : 
     879             :         //keep receiving if we are done with errors
     880        1325 :         if (obj->status >= GF_LCT_OBJ_DONE) {
     881           3 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d object TSI %u TOI %u already received - skipping\n", s->service_id, tsi, toi ));
     882             :                 return GF_EOS;
     883             :         }
     884        1322 :         obj->last_gather_time = gf_sys_clock();
     885             : 
     886        1322 :     if (!size) {
     887             :         goto check_done;
     888             :     }
     889        1322 :         obj->nb_recv_bytes += size;
     890             : 
     891             :         inserted = GF_FALSE;
     892        1322 :         for (i=0; i<obj->nb_frags; i++) {
     893        1279 :                 if ((obj->frags[i].offset <= start_offset) && (obj->frags[i].offset + obj->frags[i].size >= start_offset + size) ) {
     894             :             //data already received
     895             :                         goto check_done;
     896             :                 }
     897             : 
     898             :                 //insert fragment
     899        1279 :                 if (obj->frags[i].offset > start_offset) {
     900             :                         //check overlap (not sure if this is legal)
     901           0 :                         if (start_offset + size > obj->frags[i].offset) {
     902           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_ROUTE, ("[ROUTE] Service %d Overlapping LCT fragment, not supported\n", s->service_id));
     903             :                                 return GF_NOT_SUPPORTED;
     904             :                         }
     905           0 :                         if (obj->nb_frags==obj->nb_alloc_frags) {
     906           0 :                                 obj->nb_alloc_frags *= 2;
     907           0 :                                 obj->frags = gf_realloc(obj->frags, sizeof(GF_LCTFragInfo)*obj->nb_alloc_frags);
     908             :                         }
     909           0 :                         memmove(&obj->frags[i+1], &obj->frags[i], sizeof(GF_LCTFragInfo) * (obj->nb_frags - i)  );
     910           0 :                         obj->frags[i].offset = start_offset;
     911           0 :                         obj->frags[i].size = size;
     912           0 :                         obj->nb_bytes += size;
     913           0 :                         obj->nb_frags++;
     914             :                         inserted = GF_TRUE;
     915           0 :             if (!i)
     916           0 :                 do_push = start_offset ? GF_FALSE : routedmx->progressive_dispatch;
     917             :                         break;
     918             :                 }
     919             :                 //expand fragment
     920        1279 :                 if (obj->frags[i].offset + obj->frags[i].size == start_offset) {
     921        1279 :                         obj->frags[i].size += size;
     922        1279 :                         obj->nb_bytes += size;
     923             :                         inserted = GF_TRUE;
     924        1279 :             if (!i)
     925        1279 :                 do_push = obj->frags[0].offset ? GF_FALSE : routedmx->progressive_dispatch;
     926             :                         break;
     927             :                 }
     928             :         }
     929             : 
     930             :         if (!inserted) {
     931          43 :                 if (obj->nb_frags==obj->nb_alloc_frags) {
     932           0 :                         obj->nb_alloc_frags *= 2;
     933           0 :                         obj->frags = gf_realloc(obj->frags, sizeof(GF_LCTFragInfo)*obj->nb_alloc_frags);
     934             :                 }
     935          43 :                 obj->frags[obj->nb_frags].offset = start_offset;
     936          43 :                 obj->frags[obj->nb_frags].size = size;
     937          43 :                 obj->nb_frags++;
     938          43 :                 obj->nb_bytes += size;
     939          43 :         if (obj->nb_frags==1)
     940          43 :             do_push = start_offset ? GF_FALSE : routedmx->progressive_dispatch;
     941             :         }
     942        1322 :         obj->nb_recv_frags++;
     943        1322 :         obj->status = GF_LCT_OBJ_RECEPTION;
     944             : 
     945             :         assert(obj->toi == toi);
     946             :         assert(obj->tsi == tsi);
     947        1322 :         if (start_offset + size > obj->alloc_size) {
     948         687 :                 obj->alloc_size = start_offset + size;
     949             :                 //use total size if available
     950         687 :                 if (obj->alloc_size < obj->total_length)
     951          11 :                         obj->alloc_size = obj->total_length;
     952             :                 //for signaling objects, we set byte after last to 0 to use string functions
     953         687 :                 if (!tsi)
     954           7 :                         obj->alloc_size++;
     955         687 :         gf_mx_p(routedmx->blob_mx);
     956         687 :                 obj->payload = gf_realloc(obj->payload, obj->alloc_size+1);
     957         687 :                 obj->payload[obj->alloc_size] = 0;
     958         687 :         obj->blob.data = obj->payload;
     959         687 :         gf_mx_v(routedmx->blob_mx);
     960             :     }
     961             :         assert(obj->alloc_size >= start_offset + size);
     962             : 
     963        1322 :         memcpy(obj->payload + start_offset, data, size);
     964        1322 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d TSI %u TOI %u append LCT fragment, offset %d total size %d recv bytes %d - offset diff since last %d\n", s->service_id, obj->tsi, obj->toi, start_offset, obj->total_length, obj->nb_bytes, (s32) start_offset - (s32) obj->prev_start_offset));
     965             : 
     966        1322 :         obj->prev_start_offset = start_offset;
     967             :         assert(obj->toi == toi);
     968             :         assert(obj->tsi == tsi);
     969             :     
     970             :     //not a file (uses templates->segment) and can push
     971        1322 :     if (do_push && !obj->rlct_file && obj->rlct) {
     972         497 :         gf_route_dmx_push_object(routedmx, s, obj, GF_FALSE, GF_TRUE, GF_FALSE, obj->frags[0].size);
     973             :     } else {
     974         872 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d TSI %u TOI %u: %d bytes inserted on non-first fragment (%d totals), cannot push\n", s->service_id, obj->tsi, obj->toi, size, obj->nb_frags));
     975             :     }
     976             : 
     977        2100 : check_done:
     978             :         //check if we are done
     979             :         done = GF_FALSE;
     980        1322 :         if (obj->total_length) {
     981         521 :                 if (obj->nb_bytes >= obj->total_length) {
     982             :                         done = GF_TRUE;
     983             :                 }
     984         491 :                 else if (close_flag) {
     985           7 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d object TSI %u TOI %u closed flag found (object not yet completed)\n", s->service_id, tsi, toi ));
     986             :                 }
     987             :         } else {
     988         801 :                 if (close_flag) obj->closed_flag = GF_TRUE;
     989             :         }
     990             :         if (!done) return GF_OK;
     991             : 
     992          30 :         s->last_active_obj = NULL;
     993          30 :         if (obj->rlct) {
     994          19 :                 obj->rlct->last_dispatched_tsi = obj->tsi;
     995          19 :                 obj->rlct->last_dispatched_toi = obj->toi;
     996             :         } else {
     997          11 :                 s->last_dispatched_toi_on_tsi_zero = obj->toi;
     998             :         }
     999          30 :         return gf_route_service_flush_object(s, obj);
    1000             : }
    1001             : 
    1002          11 : static GF_Err gf_route_service_setup_dash(GF_ROUTEDmx *routedmx, GF_ROUTEService *s, char *content, char *content_location)
    1003             : {
    1004          11 :         u32 len = (u32) strlen(content);
    1005             : 
    1006          11 :         if (s->tune_mode==GF_ROUTE_TUNE_SLS_ONLY) {
    1007           0 :                 s->tune_mode = GF_ROUTE_TUNE_OFF;
    1008             :                 //unregister sockets
    1009           0 :                 gf_route_register_service_sockets(routedmx, s, GF_FALSE);
    1010             :         }
    1011             : 
    1012          11 :         if (routedmx->on_event) {
    1013             :                 GF_ROUTEEventFileInfo finfo;
    1014             :         GF_Blob blob;
    1015             :                 memset(&finfo, 0, sizeof(GF_ROUTEEventFileInfo));
    1016             :         memset(&blob, 0, sizeof(GF_Blob));
    1017          11 :         blob.data = content;
    1018          11 :         blob.size = len;
    1019          11 :         finfo.blob = &blob;
    1020          11 :         finfo.total_size = len;
    1021          11 :                 finfo.filename = content_location;
    1022          11 :                 GF_LOG(GF_LOG_INFO, GF_LOG_ROUTE, ("[ROUTE] Service %d received MPD file %s\n", s->service_id, content_location ));
    1023          11 :                 routedmx->on_event(routedmx->udta, GF_ROUTE_EVT_MPD, s->service_id, &finfo);
    1024             :         } else {
    1025           0 :                 GF_LOG(GF_LOG_INFO, GF_LOG_ROUTE, ("[ROUTE] Service %d received MPD file %s content:\n%s\n", s->service_id, content_location, content ));
    1026             :         }
    1027          11 :         return GF_OK;
    1028             : }
    1029             : 
    1030           5 : static GF_Err gf_route_service_parse_mbms_enveloppe(GF_ROUTEDmx *routedmx, GF_ROUTEService *s, char *content, char *content_location, u32 *stsid_version, u32 *mpd_version)
    1031             : {
    1032             :         u32 i, j;
    1033             :         GF_Err e;
    1034             :         GF_XMLAttribute *att;
    1035             :         GF_XMLNode *it, *root;
    1036             : 
    1037           5 :         e = gf_xml_dom_parse_string(routedmx->dom, content);
    1038           5 :         root = gf_xml_dom_get_root(routedmx->dom);
    1039           5 :         if (e || !root) {
    1040           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d failed to parse S-TSID: %s\n", s->service_id, gf_error_to_string(e) ));
    1041             :                 return e;
    1042             :         }
    1043             : 
    1044           5 :         i=0;
    1045          40 :         while ((it = gf_list_enum(root->content, &i))) {
    1046             :                 const char *content_type = NULL;
    1047             :                 /*const char *uri = NULL;*/
    1048             :                 u32 version = 0;
    1049             : 
    1050          35 :                 if (strcmp(it->name, "item")) continue;
    1051             : 
    1052          15 :                 j=0;
    1053          60 :                 while ((att = gf_list_enum(it->attributes, &j))) {
    1054          45 :                         if (!stricmp(att->name, "contentType")) content_type = att->value;
    1055             :                         /*else if (!stricmp(att->name, "metadataURI")) uri = att->value;*/
    1056          45 :                         else if (!stricmp(att->name, "version")) version = atoi(att->value);
    1057             :                 }
    1058          15 :                 if (!content_type) continue;
    1059          15 :         if (!strcmp(content_type, "application/s-tsid") || !strcmp(content_type, "application/route-s-tsid+xml"))
    1060           5 :             *stsid_version = version;
    1061          10 :                 else if (!strcmp(content_type, "application/dash+xml"))
    1062           5 :             *mpd_version = version;
    1063             :         }
    1064             :         return GF_OK;
    1065             : }
    1066             : 
    1067           6 : static GF_Err gf_route_service_setup_stsid(GF_ROUTEDmx *routedmx, GF_ROUTEService *s, char *content, char *content_location)
    1068             : {
    1069             :         GF_Err e;
    1070             :         GF_XMLAttribute *att;
    1071             :         GF_XMLNode *rs, *ls, *srcf, *efdt, *node, *root;
    1072             :         u32 i, j, k, crc, nb_lct_channels=0;
    1073             : 
    1074           6 :         crc = gf_crc_32(content, (u32) strlen(content) );
    1075           6 :         if (!s->stsid_crc) {
    1076           6 :                 s->stsid_crc = crc;
    1077           0 :         } else if (s->stsid_crc != crc) {
    1078           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d update of S-TSID not yet supported, skipping\n", s->service_id));
    1079             :                 return GF_NOT_SUPPORTED;
    1080             :         } else {
    1081             :                 return GF_OK;
    1082             :         }
    1083             : 
    1084           6 :         e = gf_xml_dom_parse_string(routedmx->dom, content);
    1085           6 :         root = gf_xml_dom_get_root(routedmx->dom);
    1086           6 :         if (e || !root) {
    1087           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d failed to parse S-TSID: %s\n", s->service_id, gf_error_to_string(e) ));
    1088             :                 return e;
    1089             :         }
    1090           6 :         i=0;
    1091          24 :         while ((rs = gf_list_enum(root->content, &i))) {
    1092          18 :                 char *dst_ip = s->dst_ip;
    1093          18 :                 u32 dst_port = s->port;
    1094             :                 char *file_template = NULL;
    1095             :                 GF_ROUTESession *rsess;
    1096             :                 GF_ROUTELCTChannel *rlct;
    1097          18 :                 u32 tsi = 0;
    1098          30 :                 if (rs->type != GF_XML_NODE_TYPE) continue;
    1099           6 :                 if (strcmp(rs->name, "RS")) continue;
    1100             : 
    1101           6 :                 j=0;
    1102          24 :                 while ((att = gf_list_enum(rs->attributes, &j))) {
    1103          18 :                         if (!stricmp(att->name, "dIpAddr")) dst_ip = att->value;
    1104          18 :                         else if (!stricmp(att->name, "dPort")) dst_port = atoi(att->value);
    1105             :                 }
    1106             : 
    1107           6 :                 GF_SAFEALLOC(rsess, GF_ROUTESession);
    1108           6 :                 if (!rsess) return GF_OUT_OF_MEM;
    1109             : 
    1110           6 :                 rsess->channels = gf_list_new();
    1111             : 
    1112             :                 //need a new socket for the session
    1113           6 :                 if ((strcmp(s->dst_ip, dst_ip)) || (s->port != dst_port) ) {
    1114           0 :                         rsess->sock = gf_sk_new(GF_SOCK_TYPE_UDP);
    1115           0 :                         gf_sk_set_usec_wait(rsess->sock, 1);
    1116           0 :                         e = gf_sk_setup_multicast(rsess->sock, dst_ip, dst_port, 0, GF_FALSE, (char *) routedmx->ip_ifce);
    1117           0 :                         if (e) {
    1118           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d  failed to setup mcast for route session on %s:%d\n", s->service_id, dst_ip, dst_port));
    1119             :                                 return e;
    1120             :                         }
    1121           0 :                         gf_sk_set_buffer_size(rsess->sock, GF_FALSE, routedmx->unz_buffer_size);
    1122             :                         //gf_sk_set_block_mode(rsess->sock, GF_TRUE);
    1123           0 :                         s->secondary_sockets++;
    1124           0 :                         if (s->tune_mode == GF_ROUTE_TUNE_ON) gf_sk_group_register(routedmx->active_sockets, rsess->sock);
    1125             :                 }
    1126           6 :                 gf_list_add(s->route_sessions, rsess);
    1127             : 
    1128           6 :                 j=0;
    1129          26 :                 while ((ls = gf_list_enum(rs->content, &j))) {
    1130             :                         GF_List *static_files;
    1131             :                         char *sep;
    1132          20 :                         if (ls->type != GF_XML_NODE_TYPE) continue;
    1133           7 :                         if (strcmp(ls->name, "LS")) continue;
    1134             : 
    1135             :                         //extract TSI
    1136           7 :                         k=0;
    1137          17 :                         while ((att = gf_list_enum(ls->attributes, &k))) {
    1138          10 :                                 if (!strcmp(att->name, "tsi")) sscanf(att->value, "%u", &tsi);
    1139             :                         }
    1140           7 :                         if (!tsi) {
    1141           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d missing TSI in LS/ROUTE session\n", s->service_id));
    1142             :                                 return GF_NON_COMPLIANT_BITSTREAM;
    1143             :                         }
    1144           7 :                         k=0;
    1145             :                         srcf = NULL;
    1146          14 :                         while ((srcf = gf_list_enum(ls->content, &k))) {
    1147          14 :                                 if ((srcf->type == GF_XML_NODE_TYPE) && !strcmp(srcf->name, "SrcFlow")) break;
    1148             :                                 srcf = NULL;
    1149             :                         }
    1150           7 :                         if (!srcf) {
    1151           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d missing srcFlow in LS/ROUTE session\n", s->service_id));
    1152             :                                 return GF_NON_COMPLIANT_BITSTREAM;
    1153             :                         }
    1154             :                         //enum srcf for efdt
    1155           7 :                         k=0;
    1156             :                         efdt = NULL;
    1157          56 :                         while ((node = gf_list_enum(srcf->content, &k))) {
    1158          49 :                                 if (node->type != GF_XML_NODE_TYPE) continue;
    1159          21 :                                 if (!strcmp(node->name, "EFDT")) efdt = node;
    1160             :                         }
    1161           7 :                         if (!efdt) {
    1162           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d missing EFDT element in LS/ROUTE session, not supported\n", s->service_id));
    1163             :                                 return GF_NOT_SUPPORTED;
    1164             :                         }
    1165             : 
    1166           7 :                         static_files = gf_list_new();
    1167             : 
    1168           7 :                         k=0;
    1169          28 :                         while ((node = gf_list_enum(efdt->content, &k))) {
    1170          21 :                                 if (node->type != GF_XML_NODE_TYPE) continue;
    1171             :                                 //Korean version
    1172           7 :                                 if (!strcmp(node->name, "FileTemplate")) {
    1173           0 :                                         GF_XMLNode *cnode = gf_list_get(node->content, 0);
    1174           0 :                                         if (cnode->type==GF_XML_TEXT_TYPE) file_template = cnode->name;
    1175             :                                 }
    1176           7 :                                 else if (!strcmp(node->name, "FDTParameters")) {
    1177           0 :                                         u32 l=0;
    1178             :                                         GF_XMLNode *fdt = NULL;
    1179           0 :                                         while ((fdt = gf_list_enum(node->content, &l))) {
    1180             :                                                 GF_ROUTELCTFile *rf;
    1181           0 :                                                 if (fdt->type != GF_XML_NODE_TYPE) continue;
    1182           0 :                                                 if (strstr(fdt->name, "File")==NULL) continue;
    1183             : 
    1184           0 :                                                 GF_SAFEALLOC(rf, GF_ROUTELCTFile)
    1185           0 :                                                 if (rf) {
    1186           0 :                                                         u32 n=0;
    1187           0 :                                                         while ((att = gf_list_enum(fdt->attributes, &n))) {
    1188           0 :                                                                 if (!strcmp(att->name, "Content-Location")) rf->filename = gf_strdup(att->value);
    1189           0 :                                                                 else if (!strcmp(att->name, "TOI")) sscanf(att->value, "%u", &rf->toi);
    1190             :                                                         }
    1191           0 :                                                         if (!rf->filename) {
    1192           0 :                                                                 gf_free(rf);
    1193             :                                                         } else {
    1194           0 :                                                                 gf_list_add(static_files, rf);
    1195             :                                                         }
    1196             :                                                 }
    1197             :                                         }
    1198             :                                 }
    1199             :                                 //US version
    1200           7 :                                 else if (!strcmp(node->name, "FDT-Instance")) {
    1201           7 :                                         u32 l=0;
    1202             :                                         GF_XMLNode *fdt = NULL;
    1203          35 :                                         while ((att = gf_list_enum(node->attributes, &l))) {
    1204          28 :                                                 if (strstr(att->name, "fileTemplate")) file_template = att->value;
    1205             :                                         }
    1206           7 :                                         l=0;
    1207          28 :                                         while ((fdt = gf_list_enum(node->content, &l))) {
    1208             :                                                 GF_ROUTELCTFile *rf;
    1209          21 :                                                 if (fdt->type != GF_XML_NODE_TYPE) continue;
    1210           7 :                                                 if (strstr(fdt->name, "File")==NULL) continue;
    1211             : 
    1212           7 :                                                 GF_SAFEALLOC(rf, GF_ROUTELCTFile)
    1213           7 :                                                 if (rf) {
    1214           7 :                                                         u32 n=0;
    1215          21 :                                                         while ((att = gf_list_enum(fdt->attributes, &n))) {
    1216          14 :                                                                 if (!strcmp(att->name, "Content-Location")) rf->filename = gf_strdup(att->value);
    1217           7 :                                                                 else if (!strcmp(att->name, "TOI")) sscanf(att->value, "%u", &rf->toi);
    1218             :                                                         }
    1219           7 :                                                         if (!rf->filename) {
    1220           0 :                                                                 gf_free(rf);
    1221             :                                                         } else {
    1222           7 :                                                                 gf_list_add(static_files, rf);
    1223             :                                                         }
    1224             :                                                 }
    1225             :                                         }
    1226             :                                 }
    1227             :                         }
    1228             : 
    1229           7 :                         if (!gf_list_count(static_files)) {
    1230             :                                 GF_ROUTELCTFile *rf;
    1231           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_ROUTE, ("[ROUTE] Service %d missing init file name in LS/ROUTE session, could be problematic - will consider any TOI %u (-1) present as a ghost init segment\n", s->service_id, (u32)-1));
    1232             :                                 //force an init at -1, some streams still have the init not declared but send on TOI -1
    1233             :                                 // interpreting it as a regular segment would break clock setup
    1234           0 :                                 GF_SAFEALLOC(rf, GF_ROUTELCTFile)
    1235           0 :                                 rf->toi = (u32) -1;
    1236           0 :                                 gf_list_add(static_files, rf);
    1237             :                         }
    1238           7 :                         if (!file_template) {
    1239           0 :                                 GF_LOG(GF_LOG_INFO, GF_LOG_ROUTE, ("[ROUTE] Service %d missing file TOI template in LS/ROUTE session, static content only\n", s->service_id));
    1240             :                         } else {
    1241           7 :                                 sep = strstr(file_template, "$TOI");
    1242           7 :                                 if (sep) sep = strchr(sep+3, '$');
    1243             : 
    1244           7 :                                 if (!sep) {
    1245           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d wrong TOI template %s in LS/ROUTE session\n", s->service_id, file_template));
    1246           0 :                                         gf_route_static_files_del(static_files);
    1247             :                                         return GF_NOT_SUPPORTED;
    1248             :                                 }
    1249             :                         }
    1250           7 :                         nb_lct_channels++;
    1251             : 
    1252             :                         //OK setup LCT channel for route
    1253           7 :                         GF_SAFEALLOC(rlct, GF_ROUTELCTChannel);
    1254           7 :                         if (!rlct) {
    1255           0 :                                 gf_route_static_files_del(static_files);
    1256             :                                 return GF_OUT_OF_MEM;
    1257             :                         }
    1258           7 :                         rlct->static_files = static_files;
    1259           7 :                         rlct->tsi = tsi;
    1260           7 :                         rlct->toi_template = NULL;
    1261           7 :                         if (file_template) {
    1262           7 :                                 sep = strstr(file_template, "$TOI");
    1263           7 :                                 sep[0] = 0;
    1264           7 :                                 gf_dynstrcat(&rlct->toi_template, file_template, NULL);
    1265           7 :                                 sep[0] = '$';
    1266             : 
    1267           7 :                                 if (sep[4]=='$') {
    1268           5 :                                         gf_dynstrcat(&rlct->toi_template, "%d", NULL);
    1269           5 :                                         sep += 5;
    1270             :                                 } else {
    1271           2 :                                         char *sep_end = strchr(sep+3, '$');
    1272           2 :                                         sep_end[0] = 0;
    1273           2 :                                         gf_dynstrcat(&rlct->toi_template, sep+4, NULL);
    1274           2 :                                         sep_end[0] = '$';
    1275           2 :                                         sep = sep_end + 1;
    1276             :                                 }
    1277           7 :                                 gf_dynstrcat(&rlct->toi_template, sep, NULL);
    1278             :                         }
    1279             : 
    1280             :                         //fill in payloads
    1281           7 :                         k=0;
    1282             :                         efdt = NULL;
    1283          56 :                         while ((node = gf_list_enum(srcf->content, &k))) {
    1284          49 :                                 if (node->type != GF_XML_NODE_TYPE) continue;
    1285          21 :                                 if (!strcmp(node->name, "Payload")) {
    1286           7 :                                         u32 l=0;
    1287             :                                         GF_ROUTELCTReg *lreg;
    1288           7 :                                         lreg = &rlct->CPs[rlct->nb_cps];
    1289           7 :                                         lreg->order = 1; //default
    1290          35 :                                         while ((att = gf_list_enum(node->attributes, &l))) {
    1291          35 :                                                 if (!strcmp(att->name, "codePoint")) lreg->codepoint = (u8) atoi(att->value);
    1292          28 :                                                 else if (!strcmp(att->name, "formatId")) lreg->format_id = (u8) atoi(att->value);
    1293          21 :                                                 else if (!strcmp(att->name, "frag")) lreg->frag = (u8) atoi(att->value);
    1294           7 :                                                 else if (!strcmp(att->name, "order")) {
    1295           7 :                                                         if (!strcmp(att->value, "true")) lreg->order = 1;
    1296           0 :                                                         else lreg->order = 0;
    1297             :                                                 }
    1298           0 :                                                 else if (!strcmp(att->name, "srcFecPayloadId")) lreg->src_fec_payload_id = (u8) atoi(att->value);
    1299             :                                         }
    1300           7 :                                         if (lreg->src_fec_payload_id) {
    1301           0 :                                                 GF_LOG(GF_LOG_WARNING, GF_LOG_ROUTE, ("[ROUTE] Service %d payload format indicates srcFecPayloadId %d (reserved), assuming 0\n", s->service_id, lreg->src_fec_payload_id));
    1302             : 
    1303             :                                         }
    1304           7 :                                         if (lreg->format_id != 1) {
    1305           0 :                                                 if (lreg->format_id && (lreg->format_id<5)) {
    1306           0 :                                                         GF_LOG(GF_LOG_WARNING, GF_LOG_ROUTE, ("[ROUTE] Service %d payload formatId %d not supported\n", s->service_id, lreg->format_id));
    1307             :                                                 } else {
    1308           0 :                                                         GF_LOG(GF_LOG_WARNING, GF_LOG_ROUTE, ("[ROUTE] Service %d payload formatId %d reserved, assuming 1\n", s->service_id, lreg->format_id));
    1309             :                                                 }
    1310             :                                         }
    1311           7 :                                         rlct->nb_cps++;
    1312           7 :                                         if (rlct->nb_cps==8) {
    1313           0 :                                                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d more payload formats than supported (8 max)\n", s->service_id));
    1314           0 :                                                 break;
    1315             :                                         }
    1316             :                                 }
    1317             :                         }
    1318             : 
    1319           7 :                         gf_list_add(rsess->channels, rlct);
    1320             :                 }
    1321             :         }
    1322             : 
    1323           6 :         if (!nb_lct_channels) {
    1324           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d does not have any supported LCT channels\n", s->service_id));
    1325             :                 return GF_NOT_SUPPORTED;
    1326             :         }
    1327             :         return GF_OK;
    1328             : }
    1329             : 
    1330          11 : static GF_Err gf_route_dmx_process_service_signaling(GF_ROUTEDmx *routedmx, GF_ROUTEService *s, GF_LCTObject *object, u8 cc, u32 stsid_version, u32 mpd_version)
    1331             : {
    1332             :         char *payload, *boundary=NULL, *sep;
    1333             :         char szContentType[100], szContentLocation[1024];
    1334             :         u32 payload_size;
    1335             :         GF_Err e;
    1336             : 
    1337             :         //uncompress bundle
    1338          11 :         if (object->toi & 0x80000000 /*(1<<31)*/ ) {
    1339             :                 u32 raw_size;
    1340          11 :                 if (object->total_length > routedmx->buffer_size) {
    1341           0 :                         routedmx->buffer_size = object->total_length;
    1342           0 :                         routedmx->buffer = gf_realloc(routedmx->buffer, object->total_length);
    1343           0 :                         if (!routedmx->buffer) return GF_OUT_OF_MEM;
    1344             :                 }
    1345          11 :                 memcpy(routedmx->buffer, object->payload, object->total_length);
    1346          11 :                 raw_size = routedmx->unz_buffer_size;
    1347          11 :                 e = gf_gz_decompress_payload(routedmx->buffer, object->total_length, &routedmx->unz_buffer, &raw_size);
    1348          11 :                 if (e) {
    1349           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d failed to decompress signaling bundle: %s\n", s->service_id, gf_error_to_string(e) ));
    1350             :                         return e;
    1351             :                 }
    1352          11 :                 if (raw_size > routedmx->unz_buffer_size) routedmx->unz_buffer_size = raw_size;
    1353          11 :                 payload = routedmx->unz_buffer;
    1354             :                 payload_size = raw_size;
    1355             :         } else {
    1356           0 :                 payload = object->payload;
    1357           0 :                 payload_size = object->total_length;
    1358             :         }
    1359          11 :         payload[payload_size] = 0;
    1360             : 
    1361          11 :         GF_LOG(GF_LOG_INFO, GF_LOG_ROUTE, ("[ROUTE] Service %d got TSI 0 config package:\n%s\n", s->service_id, payload ));
    1362             : 
    1363             :         //check for multipart
    1364          11 :         if (!strncmp(payload, "Content-Type: multipart/", 24)) {
    1365          11 :                 sep = strstr(payload, "boundary=\"");
    1366          11 :                 if (!sep) {
    1367           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d cannot find multipart boundary in package:\n%s\n", s->service_id, payload ));
    1368             :                         return GF_NON_COMPLIANT_BITSTREAM;
    1369             :                 }
    1370          11 :                 payload = sep + 10;
    1371          11 :                 sep = strstr(payload, "\"");
    1372          11 :                 if (!sep) {
    1373           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d multipart boundary not properly formatted in package:\n%s\n", s->service_id, payload ));
    1374             :                         return GF_NON_COMPLIANT_BITSTREAM;
    1375             :                 }
    1376          11 :                 sep[0] = 0;
    1377          11 :                 boundary = gf_strdup(payload);
    1378          11 :                 sep[0] = '\"';
    1379          11 :                 payload = sep+1;
    1380             :         }
    1381             : 
    1382             :         //extract all content
    1383          32 :         while (1) {
    1384             :                 char *content;
    1385             :                 //multipart, check for start and end
    1386          43 :                 if (boundary) {
    1387          43 :                         sep = strstr(payload, boundary);
    1388          43 :                         if (!sep) break;
    1389          43 :                         payload = sep + strlen(boundary) + 2;
    1390          43 :                         sep = strstr(payload, boundary);
    1391          43 :                         if (!sep) break;
    1392          32 :                         sep[0] = 0;
    1393             :                 } else {
    1394             :                         sep = NULL;
    1395             :                 }
    1396             : 
    1397             :                 //extract headers
    1398          96 :                 while (strncmp(payload, "\r\n\r\n", 4)) {
    1399             :                         u32 i=0;
    1400          64 :                         while (strchr("\n\r", payload[0]) != NULL) payload++;
    1401        2144 :                         while (strchr("\r\n", payload[i]) == NULL) i++;
    1402             : 
    1403          64 :                         if (!strnicmp(payload, "Content-Type: ", 14)) {
    1404          32 :                                 u32 copy = MIN(i-14, 100);
    1405          32 :                                 strncpy(szContentType, payload+14, copy);
    1406          32 :                                 szContentType[copy]=0;
    1407             :                                 payload += i;
    1408             :                         }
    1409          32 :                         else if (!strnicmp(payload, "Content-Location: ", 18)) {
    1410          32 :                                 u32 copy = MIN(i-18, 1024);
    1411          32 :                                 strncpy(szContentLocation, payload+18, copy);
    1412          32 :                                 szContentLocation[copy]=0;
    1413             :                                 payload += i;
    1414             :                         } else {
    1415           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_ROUTE, ("[ROUTE] Service %d unrecognized header entity in package:\n%s\n", s->service_id, payload ));
    1416             :                         }
    1417             :                 }
    1418          32 :                 payload += 4;
    1419          32 :                 content = boundary ? strstr(payload, "\r\n--") : strstr(payload, "\r\n\r\n");
    1420          32 :                 if (content) {
    1421          32 :                         content[0] = 0;
    1422          32 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d package type %s location %s content:\n%s\n", s->service_id, szContentType, szContentLocation, payload ));
    1423             :                 } else {
    1424           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d %s not properly formatted in package:\n%s\n", s->service_id, boundary ? "multipart boundary" : "entity", payload ));
    1425           0 :                         if (sep && boundary) sep[0] = boundary[0];
    1426           0 :                         gf_free(boundary);
    1427           0 :                         return GF_NON_COMPLIANT_BITSTREAM;
    1428             :                 }
    1429             : 
    1430          32 :                 if (!strcmp(szContentType, "application/mbms-envelope+xml")) {
    1431           5 :                         e = gf_route_service_parse_mbms_enveloppe(routedmx, s, payload, szContentLocation, &stsid_version, &mpd_version);
    1432             : 
    1433           5 :                         if (e || !stsid_version || !mpd_version) {
    1434           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d MBMS envelope error %s, S-TSID version %d MPD version %d\n",s->service_id, gf_error_to_string(e), stsid_version, mpd_version ));
    1435           0 :                                 gf_free(boundary);
    1436           0 :                                 return e ? e : GF_SERVICE_ERROR;
    1437             :                         }
    1438          27 :                 } else if (!strcmp(szContentType, "application/mbms-user-service-description+xml")) {
    1439          27 :                 } else if (!strcmp(szContentType, "application/dash+xml")
    1440          16 :                         || !strcmp(szContentType, "video/vnd.3gpp.mpd")
    1441          16 :                         || !strcmp(szContentType, "audio/mpegurl")
    1442          16 :                         || !strcmp(szContentType, "video/mpegurl")
    1443             :                 ) {
    1444          11 :                         if (!s->mpd_version || (mpd_version && (mpd_version+1 != s->mpd_version))) {
    1445          11 :                                 s->mpd_version = mpd_version+1;
    1446          11 :                                 gf_route_service_setup_dash(routedmx, s, payload, szContentLocation);
    1447             :                         } else {
    1448           0 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d same MPD version, ignoring\n",s->service_id));
    1449             :                         }
    1450             :                 }
    1451             :                 //Korean and US version have different mime types
    1452          16 :                 else if (!strcmp(szContentType, "application/s-tsid") || !strcmp(szContentType, "application/route-s-tsid+xml")) {
    1453          11 :                         if (!s->stsid_version || (stsid_version && (stsid_version+1 != s->stsid_version))) {
    1454           6 :                                 s->stsid_version = stsid_version+1;
    1455           6 :                                 gf_route_service_setup_stsid(routedmx, s, payload, szContentLocation);
    1456             :                         } else {
    1457           5 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d same S-TSID version, ignoring\n",s->service_id));
    1458             :                         }
    1459             :                 }
    1460          32 :                 if (!sep) break;
    1461          32 :                 sep[0] = boundary[0];
    1462             :                 payload = sep;
    1463             :         }
    1464             : 
    1465          11 :         gf_free(boundary);
    1466          11 :         return GF_OK;
    1467             : }
    1468             : 
    1469             : 
    1470        1481 : static GF_Err gf_route_dmx_process_service(GF_ROUTEDmx *routedmx, GF_ROUTEService *s, GF_ROUTESession *route_sess)
    1471             : {
    1472             :         GF_Err e;
    1473             :         u32 nb_read, v, C, psi, S, O, H, /*Res, A,*/ B, hdr_len, cp, cc, tsi, toi, pos;
    1474             :         u32 /*a_G=0, a_U=0,*/ a_S=0, a_M=0/*, a_A=0, a_H=0, a_D=0*/;
    1475             :         u64 tol_size=0;
    1476             :         Bool in_order = GF_TRUE;
    1477             :         u32 start_offset;
    1478             :         GF_ROUTELCTChannel *rlct=NULL;
    1479        1481 :         GF_LCTObject *gather_object=NULL;
    1480             : 
    1481        1481 :         if (route_sess) {
    1482           0 :                 e = gf_sk_receive_no_select(route_sess->sock, routedmx->buffer, routedmx->buffer_size, &nb_read);
    1483             :         } else {
    1484        1481 :                 e = gf_sk_receive_no_select(s->sock, routedmx->buffer, routedmx->buffer_size, &nb_read);
    1485             :         }
    1486             : 
    1487        1481 :         if (e != GF_OK) return e;
    1488             :         assert(nb_read);
    1489             : 
    1490        1481 :         routedmx->nb_packets++;
    1491        1481 :         routedmx->total_bytes_recv += nb_read;
    1492        1481 :         routedmx->last_pck_time = gf_sys_clock_high_res();
    1493        1481 :         if (!routedmx->first_pck_time) routedmx->first_pck_time = routedmx->last_pck_time;
    1494             : 
    1495        1481 :         e = gf_bs_reassign_buffer(routedmx->bs, routedmx->buffer, nb_read);
    1496        1481 :         if (e != GF_OK) return e;
    1497             : 
    1498             :         //parse LCT header
    1499        1481 :         v = gf_bs_read_int(routedmx->bs, 4);
    1500        1481 :         C = gf_bs_read_int(routedmx->bs, 2);
    1501        1481 :         psi = gf_bs_read_int(routedmx->bs, 2);
    1502        1481 :         S = gf_bs_read_int(routedmx->bs, 1);
    1503        1481 :         O = gf_bs_read_int(routedmx->bs, 2);
    1504        1481 :         H = gf_bs_read_int(routedmx->bs, 1);
    1505        1481 :         /*Res = */gf_bs_read_int(routedmx->bs, 2);
    1506        1481 :         /*A = */gf_bs_read_int(routedmx->bs, 1);
    1507        1481 :         B = gf_bs_read_int(routedmx->bs, 1);
    1508        1481 :         hdr_len = gf_bs_read_int(routedmx->bs, 8);
    1509        1481 :         cp = gf_bs_read_int(routedmx->bs, 8);
    1510             : 
    1511        1481 :         if (v!=1) {
    1512           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d : wrong LCT header version %d\n", s->service_id, v));
    1513             :                 return GF_NON_COMPLIANT_BITSTREAM;
    1514             :         }
    1515        1481 :         else if (C!=0) {
    1516           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d : wrong ROUTE LCT header C %d, expecting 0\n", s->service_id, C));
    1517             :                 return GF_NON_COMPLIANT_BITSTREAM;
    1518             :         }
    1519        1481 :         else if ((psi!=0) && (psi!=2) ) {
    1520           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d : wrong ROUTE LCT header PSI %d, expecting b00 or b10\n", s->service_id, psi));
    1521             :                 return GF_NON_COMPLIANT_BITSTREAM;
    1522             :         }
    1523        1481 :         else if (S!=1) {
    1524           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d : wrong ROUTE LCT header S, shall be 1\n", s->service_id));
    1525             :                 return GF_NON_COMPLIANT_BITSTREAM;
    1526             :         }
    1527        1481 :         else if (O!=1) {
    1528           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d : wrong ROUTE LCT header 0, shall be b01\n", s->service_id));
    1529             :                 return GF_NON_COMPLIANT_BITSTREAM;
    1530             :         }
    1531        1481 :         else if (H!=0) {
    1532           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d : wrong ROUTE LCT header H, shall be 0\n", s->service_id));
    1533             :                 return GF_NON_COMPLIANT_BITSTREAM;
    1534             :         }
    1535        1481 :         if (hdr_len<4) {
    1536           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Service %d : wrong ROUTE LCT header len %d, shall be at least 4 0\n", s->service_id, hdr_len));
    1537             :                 return GF_NON_COMPLIANT_BITSTREAM;
    1538             :         }
    1539             : 
    1540        1481 :         if (psi==0) {
    1541           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_ROUTE, ("[ROUTE] Service %d : FEC ROUTE not implemented\n", s->service_id));
    1542             :                 return GF_NOT_SUPPORTED;
    1543             :         }
    1544             : 
    1545        1481 :         cc = gf_bs_read_u32(routedmx->bs);
    1546        1481 :         tsi = gf_bs_read_u32(routedmx->bs);
    1547        1481 :         toi = gf_bs_read_u32(routedmx->bs);
    1548        1481 :         hdr_len-=4;
    1549             : 
    1550             :         //filter TSI if not 0 (service TSI) and debug mode set
    1551        1481 :         if (routedmx->debug_tsi && tsi && (tsi!=routedmx->debug_tsi)) return GF_OK;
    1552             : 
    1553             :         //look for TSI 0 first
    1554        1481 :         if (tsi!=0) {
    1555             :                 Bool cp_found = GF_FALSE;
    1556        1461 :                 u32 i=0;
    1557             :                 Bool in_session = GF_FALSE;
    1558        1608 :                 if (s->tune_mode==GF_ROUTE_TUNE_SLS_ONLY) return GF_OK;
    1559             : 
    1560        1461 :                 if (s->last_active_obj && (s->last_active_obj->tsi==tsi)) {
    1561             :                         in_session = GF_TRUE;
    1562        1197 :                         rlct = s->last_active_obj->rlct;
    1563             :                 } else {
    1564             :                         GF_ROUTESession *rsess;
    1565             :                         i=0;
    1566         264 :                         while ((rsess = gf_list_enum(s->route_sessions, &i))) {
    1567         117 :                                 u32 j=0;
    1568         281 :                                 while ((rlct = gf_list_enum(rsess->channels, &j))) {
    1569         164 :                                         if (rlct->tsi == tsi) {
    1570             :                                                 in_session = GF_TRUE;
    1571             :                                                 break;
    1572             :                                         }
    1573             :                                         rlct = NULL;
    1574             :                                 }
    1575         117 :                                 if (in_session) break;
    1576             :                         }
    1577             :                 }
    1578         264 :                 if (!in_session) {
    1579         147 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d : no session with TSI %u defined, skipping packet (TOI %u)\n", s->service_id, tsi, toi));
    1580             :                         return GF_OK;
    1581             :                 }
    1582        2628 :                 for (i=0; rlct && i<rlct->nb_cps; i++) {
    1583        1314 :                         if (rlct->CPs[i].codepoint==cp) {
    1584           0 :                                 in_order = rlct->CPs[i].order;
    1585             :                                 cp_found = GF_TRUE;
    1586             :                                 break;
    1587             :                         }
    1588             :                 }
    1589             :                 if (!cp_found) {
    1590        1314 :                         if ((cp==0) || (cp==2) || (cp>=9) ) {
    1591           0 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d : unsupported code point %d, skipping packet (TOI %u)\n", s->service_id, cp, toi));
    1592             :                                 return GF_OK;
    1593             :                         }
    1594             :                 }
    1595             :         } else {
    1596             :                 //check TOI for TSI 0
    1597             :                 //a_G = (toi & 0x80000000) /*(1<<31)*/ ? 1 : 0;
    1598             :                 //a_U = (toi & (1<<16)) ? 1 : 0;
    1599          20 :                 a_S = (toi & (1<<17)) ? 1 : 0;
    1600          20 :                 a_M = (toi & (1<<18)) ? 1 : 0;
    1601             :                 /*a_A = (toi & (1<<19)) ? 1 : 0;
    1602             :                 a_H = (toi & (1<<22)) ? 1 : 0;
    1603             :                 a_D = (toi & (1<<23)) ? 1 : 0;*/
    1604          20 :                 v = toi & 0xFF;
    1605             :                 //skip known version
    1606          20 :                 if (a_M && (s->mpd_version == v+1)) a_M = 0;
    1607          20 :                 if (a_S && (s->stsid_version == v+1)) a_S = 0;
    1608             : 
    1609             : 
    1610             :                 //for now we only care about S and M
    1611          20 :                 if (!a_S && ! a_M) {
    1612           9 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d : SLT bundle without MPD or S-TSID, skipping packet\n", s->service_id));
    1613             :                         return GF_OK;
    1614             :                 }
    1615             :         }
    1616             : 
    1617             :         //parse extensions
    1618        1849 :         while (hdr_len) {
    1619         524 :                 u8 hel=0, het = gf_bs_read_u8(routedmx->bs);
    1620         524 :                 if (het<128) hel = gf_bs_read_u8(routedmx->bs);
    1621             : 
    1622         524 :                 switch (het) {
    1623           0 :                 case GF_LCT_EXT_FDT:
    1624           0 :                         /*flute_version = */gf_bs_read_int(routedmx->bs, 4);
    1625           0 :                         /*fdt_instance_id = */gf_bs_read_int(routedmx->bs, 20);
    1626           0 :                         break;
    1627             : 
    1628           0 :                 case GF_LCT_EXT_CENC:
    1629           0 :                         /*content_encodind = */gf_bs_read_int(routedmx->bs, 8);
    1630           0 :                         /*reserved = */gf_bs_read_int(routedmx->bs, 16);
    1631           0 :                         break;
    1632             : 
    1633         524 :                 case GF_LCT_EXT_TOL24:
    1634         524 :                         tol_size = gf_bs_read_int(routedmx->bs, 24);
    1635         524 :                         break;
    1636             : 
    1637           0 :                 case GF_LCT_EXT_TOL48:
    1638           0 :                         if (hel!=2) {
    1639           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_ROUTE, ("[ROUTE] Service %d : wrong HEL %d for TOL48 LCT extension, expecting 2\n", s->service_id, hel));
    1640           0 :                                 continue;
    1641             :                         }
    1642           0 :                         tol_size = gf_bs_read_long_int(routedmx->bs, 48);
    1643           0 :                         break;
    1644             : 
    1645           0 :                 default:
    1646           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d : unsupported header extension HEL %d HET %d, ignoring\n", s->service_id, hel, het));
    1647             :                         break;
    1648             :                 }
    1649         524 :                 if (hdr_len<hel) {
    1650           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_ROUTE, ("[ROUTE] Service %d : wrong HEL %d for LCT extension %d, remaining header size %d\n", s->service_id, hel, het, hdr_len));
    1651           0 :                         continue;
    1652             :                 }
    1653         524 :                 if (hel) hdr_len -= hel;
    1654         524 :                 else hdr_len -= 1;
    1655             :         }
    1656             : 
    1657        1325 :         start_offset = gf_bs_read_u32(routedmx->bs);
    1658        1325 :         pos = (u32) gf_bs_get_position(routedmx->bs);
    1659             : 
    1660        1325 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] Service %d : LCT packet TSI %u TOI %u size %d startOffset %u TOL "LLU"\n", s->service_id, tsi, toi, nb_read-pos, start_offset, tol_size));
    1661             : 
    1662        1325 :         e = gf_route_service_gather_object(routedmx, s, tsi, toi, start_offset, routedmx->buffer + pos, nb_read-pos, (u32) tol_size, B, in_order, rlct, &gather_object);
    1663             : 
    1664        1325 :         if (e==GF_EOS) {
    1665          33 :                 if (!tsi) {
    1666          11 :                         if (gather_object->status==GF_LCT_OBJ_DONE_ERR) {
    1667           0 :                                 gf_route_obj_to_reservoir(routedmx, s, gather_object);
    1668           0 :                                 return GF_OK;
    1669             :                         }
    1670             :                         //we don't assign version here, we use mbms envelope for that since the bundle may have several
    1671             :                         //packages
    1672             : 
    1673          11 :                         gf_route_dmx_process_service_signaling(routedmx, s, gather_object, cc, a_S ? v : 0, a_M ? v : 0);
    1674             :                         //we don't release the LCT object, so that we can disard future versions
    1675             :                 } else {
    1676          22 :                         gf_route_dmx_process_object(routedmx, s, gather_object);
    1677             :                 }
    1678             :         }
    1679             : 
    1680             :         return GF_OK;
    1681             : }
    1682             : 
    1683          18 : static GF_Err gf_route_dmx_process_lls(GF_ROUTEDmx *routedmx)
    1684             : {
    1685             :         u32 read;
    1686             :         GF_Err e;
    1687             :         const char *name=NULL;
    1688             :         u32 lls_table_id, lls_group_id, lls_group_count, lls_table_version;
    1689          18 :         u32 raw_size = routedmx->unz_buffer_size;
    1690             :         GF_XMLNode *root;
    1691             : 
    1692          18 :         e = gf_sk_receive_no_select(routedmx->atsc_sock, routedmx->buffer, routedmx->buffer_size, &read);
    1693          18 :         if (e)
    1694             :                 return e;
    1695             : 
    1696          18 :         routedmx->nb_packets++;
    1697          18 :         routedmx->total_bytes_recv += read;
    1698          18 :         routedmx->last_pck_time = gf_sys_clock_high_res();
    1699          18 :         if (!routedmx->first_pck_time) routedmx->first_pck_time = routedmx->last_pck_time;
    1700             : 
    1701          18 :         lls_table_id = routedmx->buffer[0];
    1702          18 :         lls_group_id = routedmx->buffer[1];
    1703          18 :         lls_group_count = 1 + routedmx->buffer[2];
    1704          18 :         lls_table_version = routedmx->buffer[3];
    1705             : 
    1706          18 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] LLSTable size %d version %d: ID %d (%s) - group ID %d - group count %d\n", read, lls_table_id, lls_table_version, (lls_table_id==1) ? "SLT" : (lls_table_id==2) ? "RRT" : (lls_table_id==3) ? "SystemTime" : (lls_table_id==4) ? "AEAT" : "Reserved", lls_group_id, lls_group_count));
    1707          18 :         switch (lls_table_id) {
    1708           9 :         case 1:
    1709           9 :                 if (routedmx->slt_version== 1+lls_table_version) return GF_OK;
    1710           2 :                 routedmx->slt_version = 1+lls_table_version;
    1711             :                 name="SLT";
    1712           2 :                 break;
    1713           0 :         case 2:
    1714           0 :                 if (routedmx->rrt_version== 1+lls_table_version) return GF_OK;
    1715           0 :                 routedmx->rrt_version = 1+lls_table_version;
    1716             :                 name="RRT";
    1717           0 :         break;
    1718           9 :         case 3:
    1719           9 :                 if (routedmx->systime_version== 1+lls_table_version) return GF_OK;
    1720           2 :                 routedmx->systime_version = 1+lls_table_version;
    1721             :                 name="SysTime";
    1722           2 :         break;
    1723           0 :         case 4:
    1724           0 :                 if (routedmx->aeat_version== 1+lls_table_version) return GF_OK;
    1725           0 :                 routedmx->aeat_version = 1+lls_table_version;
    1726             :                 name="AEAT";
    1727           0 :         break;
    1728             :         default:
    1729             :                 return GF_OK;
    1730             :         }
    1731             : 
    1732           4 :         e = gf_gz_decompress_payload(&routedmx->buffer[4], read-4, &routedmx->unz_buffer, &raw_size);
    1733           4 :         if (e) {
    1734           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Failed to decompress %s table: %s\n", name, gf_error_to_string(e) ));
    1735             :                 return e;
    1736             :         }
    1737             :         //realloc happened
    1738           4 :         if (routedmx->unz_buffer_size<raw_size) routedmx->unz_buffer_size = raw_size;
    1739           4 :         routedmx->unz_buffer[raw_size]=0;
    1740           4 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_ROUTE, ("[ROUTE] %s table - payload:\n%s\n", name, routedmx->unz_buffer));
    1741             : 
    1742             : 
    1743           4 :         e = gf_xml_dom_parse_string(routedmx->dom, routedmx->unz_buffer);
    1744           4 :         if (e) {
    1745           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Failed to parse SLT XML: %s\n", gf_error_to_string(e) ));
    1746             :                 return e;
    1747             :         }
    1748             : 
    1749           4 :         root = gf_xml_dom_get_root(routedmx->dom);
    1750           4 :         if (!root) {
    1751           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_ROUTE, ("[ROUTE] Failed to get XML root for %s table\n", name ));
    1752             :                 return e;
    1753             :         }
    1754             : 
    1755           4 :         switch (lls_table_id) {
    1756           2 :         case 1:
    1757           2 :                 return gf_route_dmx_process_slt(routedmx, root);
    1758             :         case 2:
    1759             :         case 3:
    1760             :         case 4:
    1761             :         default:
    1762             :                 break;
    1763             :         }
    1764             :         return GF_OK;
    1765             : }
    1766             : 
    1767             : GF_EXPORT
    1768       52995 : GF_Err gf_route_dmx_process(GF_ROUTEDmx *routedmx)
    1769             : {
    1770             :         u32 i, count;
    1771             :         GF_Err e;
    1772             : 
    1773             :         //check all active sockets
    1774       52995 :         e = gf_sk_group_select(routedmx->active_sockets, 10, GF_SK_SELECT_READ);
    1775       52995 :         if (e) return e;
    1776             : 
    1777        1499 :         if (routedmx->atsc_sock) {
    1778         374 :                 if (gf_sk_group_sock_is_set(routedmx->active_sockets, routedmx->atsc_sock, GF_SK_SELECT_READ)) {
    1779          18 :                         e = gf_route_dmx_process_lls(routedmx);
    1780          18 :                         if (e) return e;
    1781             :                 }
    1782             :         }
    1783             : 
    1784        1499 :         count = gf_list_count(routedmx->services);
    1785        2996 :         for (i=0; i<count; i++) {
    1786             :                 u32 j;
    1787             :                 GF_ROUTESession *rsess;
    1788        1497 :                 GF_ROUTEService *s = (GF_ROUTEService *)gf_list_get(routedmx->services, i);
    1789        2994 :                 if (s->tune_mode==GF_ROUTE_TUNE_OFF) continue;
    1790             : 
    1791        1497 :                 if (gf_sk_group_sock_is_set(routedmx->active_sockets, s->sock, GF_SK_SELECT_READ)) {
    1792        1481 :                         e = gf_route_dmx_process_service(routedmx, s, NULL);
    1793        1481 :                         if (e) return e;
    1794             :                 }
    1795        1497 :                 if (s->tune_mode!=GF_ROUTE_TUNE_ON) continue;
    1796        1497 :                 if (!s->secondary_sockets) continue;
    1797             : 
    1798           0 :                 j=0;
    1799           0 :                 while ((rsess = (GF_ROUTESession *)gf_list_enum(s->route_sessions, &j) )) {
    1800           0 :                         if (gf_sk_group_sock_is_set(routedmx->active_sockets, rsess->sock, GF_SK_SELECT_READ)) {
    1801           0 :                                 e = gf_route_dmx_process_service(routedmx, s, rsess);
    1802           0 :                                 if (e) return e;
    1803             :                         }
    1804             :                 }
    1805             : 
    1806             :         }
    1807             :         return GF_OK;
    1808             : }
    1809             : 
    1810             : GF_EXPORT
    1811           0 : Bool gf_route_dmx_find_atsc3_service(GF_ROUTEDmx *routedmx, u32 service_id)
    1812             : {
    1813           0 :         u32 i=0;
    1814             :         GF_ROUTEService *s;
    1815           0 :         while ((s = gf_list_enum(routedmx->services, &i))) {
    1816           0 :                 if (s->service_id != service_id) continue;
    1817             :                 return GF_TRUE;
    1818             :         }
    1819             :         return GF_FALSE;
    1820             : }
    1821             : 
    1822             : 
    1823             : GF_EXPORT
    1824          40 : u32 gf_route_dmx_get_object_count(GF_ROUTEDmx *routedmx, u32 service_id)
    1825             : {
    1826          40 :         u32 i=0;
    1827             :         GF_ROUTEService *s;
    1828          80 :         while ((s = gf_list_enum(routedmx->services, &i))) {
    1829          40 :                 if (s->service_id != service_id) continue;
    1830          40 :                 return gf_list_count(s->objects);
    1831             :         }
    1832             :         return 0;
    1833             : }
    1834             : 
    1835             : GF_EXPORT
    1836          14 : void gf_route_dmx_remove_object_by_name(GF_ROUTEDmx *routedmx, u32 service_id, char *fileName, Bool purge_previous)
    1837             : {
    1838          14 :         u32 i=0;
    1839             :         GF_ROUTEService *s=NULL;
    1840             :         GF_LCTObject *obj = NULL;
    1841          28 :         while ((s = gf_list_enum(routedmx->services, &i))) {
    1842          14 :                 if (s->service_id == service_id) break;
    1843             :                 s = NULL;
    1844             :         }
    1845          28 :         if (!s) return;
    1846          14 :         i=0;
    1847          50 :         while ((obj = gf_list_enum(s->objects, &i))) {
    1848             :                 u32 toi;
    1849          36 :                 if (obj->rlct && (sscanf(fileName, obj->rlct->toi_template, &toi) == 1)) {
    1850           8 :                         if (toi == obj->toi) {
    1851           8 :                                 GF_ROUTELCTChannel *rlct = obj->rlct;
    1852             :                                 //we likely have a loop here
    1853           8 :                                 if (obj == s->last_active_obj) break;
    1854             :                                 //obj being received do not destroy
    1855           8 :                                 if (obj->status == GF_LCT_OBJ_RECEPTION) break;
    1856             : 
    1857             : 
    1858           8 :                                 gf_route_obj_to_reservoir(routedmx, s, obj);
    1859           8 :                                 if (purge_previous) {
    1860           8 :                                         i=0;
    1861          41 :                                         while ((obj = gf_list_enum(s->objects, &i))) {
    1862          25 :                                                 if (obj->rlct != rlct) continue;
    1863          12 :                                                 if (obj->rlct_file) continue;
    1864           8 :                                                 if (obj->toi<toi) {
    1865           0 :                                                         i--;
    1866             :                                                         //we likely have a loop here
    1867          14 :                                                         if (obj == s->last_active_obj) return;
    1868           0 :                                                         gf_route_obj_to_reservoir(routedmx, s, obj);
    1869             :                                                 }
    1870             :                                         }
    1871             :                                 }
    1872             :                                 return;
    1873             :                         }
    1874             :                 }
    1875          28 :                 else if (obj->rlct && obj->rlct_file && obj->rlct_file->filename && !strcmp(fileName, obj->rlct_file->filename)) {
    1876           6 :                         gf_route_obj_to_reservoir(routedmx, s, obj);
    1877           6 :                         return;
    1878             :                 }
    1879             :         }
    1880           0 :         GF_LOG(GF_LOG_WARNING, GF_LOG_ROUTE, ("[ROUTE] Failed to remove object %s from service, object not found\n", fileName));
    1881             : }
    1882             : 
    1883             : 
    1884             : GF_EXPORT
    1885          12 : Bool gf_route_dmx_remove_first_object(GF_ROUTEDmx *routedmx, u32 service_id)
    1886             : {
    1887          12 :         u32 i=0;
    1888             :         GF_ROUTEService *s=NULL;
    1889             :         GF_LCTObject *obj = NULL;
    1890          24 :         while ((s = gf_list_enum(routedmx->services, &i))) {
    1891          12 :                 if (s->service_id == service_id) break;
    1892             :                 s = NULL;
    1893             :         }
    1894          12 :         if (!s) return GF_FALSE;
    1895             : 
    1896          12 :         i=0;
    1897          33 :         while ( (obj = gf_list_enum(s->objects, &i))) {
    1898          21 :                 if (obj == s->last_active_obj) continue;
    1899             :                 //object is active, abort
    1900          20 :                 if (obj->status<=GF_LCT_OBJ_RECEPTION) break;
    1901             :                 //keep static files active
    1902          17 :                 if (obj->rlct_file)
    1903           8 :                         continue;
    1904             : 
    1905           9 :                 gf_route_obj_to_reservoir(routedmx, s, obj);
    1906           9 :                 return GF_TRUE;
    1907             :         }
    1908             :         return GF_FALSE;
    1909             : }
    1910             : 
    1911             : GF_EXPORT
    1912           6 : void gf_route_dmx_purge_objects(GF_ROUTEDmx *routedmx, u32 service_id)
    1913             : {
    1914           6 :         u32 i=0;
    1915             :         GF_ROUTEService *s=NULL;
    1916             :         GF_LCTObject *obj = NULL;
    1917          12 :         while ((s = gf_list_enum(routedmx->services, &i))) {
    1918           6 :                 if (s->service_id == service_id) break;
    1919             :                 s = NULL;
    1920             :         }
    1921           6 :         if (!s) return;
    1922             : 
    1923           6 :         i=0;
    1924          29 :         while ((obj = gf_list_enum(s->objects, &i))) {
    1925             :                 //only purge non signaling objects
    1926          17 :                 if (!obj->tsi) continue;
    1927             :                 //if object is being received keep it
    1928          11 :                 if (s->last_active_obj == obj) continue;
    1929             :                 //if object is static file keep it - this may need refinement in case we had init segment updates
    1930           8 :                 if (obj->rlct_file) continue;
    1931             :                 //obj being received do not destroy
    1932           4 :                 if (obj->status == GF_LCT_OBJ_RECEPTION) continue;
    1933             :                 //trash
    1934           3 :                 gf_route_obj_to_reservoir(routedmx, s, obj);
    1935             :         }
    1936             : }
    1937             : 
    1938             : GF_EXPORT
    1939           8 : void gf_route_dmx_set_service_udta(GF_ROUTEDmx *routedmx, u32 service_id, void *udta)
    1940             : {
    1941           8 :         u32 i=0;
    1942             :         GF_ROUTEService *s=NULL;
    1943          16 :         while ((s = gf_list_enum(routedmx->services, &i))) {
    1944           8 :                 if (s->service_id == service_id) {
    1945           8 :                         s->udta = udta;
    1946           8 :                         return;
    1947             :                 }
    1948             :         }
    1949             : }
    1950             : 
    1951             : GF_EXPORT
    1952           8 : void *gf_route_dmx_get_service_udta(GF_ROUTEDmx *routedmx, u32 service_id)
    1953             : {
    1954           8 :         u32 i=0;
    1955             :         GF_ROUTEService *s=NULL;
    1956          16 :         while ((s = gf_list_enum(routedmx->services, &i))) {
    1957           8 :                 if (s->service_id == service_id) {
    1958           8 :                         return s->udta;
    1959             :                 }
    1960             :         }
    1961             :         return NULL;
    1962             : }
    1963             : 
    1964             : GF_EXPORT
    1965          12 : u64 gf_route_dmx_get_first_packet_time(GF_ROUTEDmx *routedmx)
    1966             : {
    1967          12 :         return routedmx ? routedmx->first_pck_time : 0;
    1968             : }
    1969             : 
    1970             : GF_EXPORT
    1971          12 : u64 gf_route_dmx_get_last_packet_time(GF_ROUTEDmx *routedmx)
    1972             : {
    1973          12 :         return routedmx ? routedmx->last_pck_time : 0;
    1974             : }
    1975             : 
    1976             : GF_EXPORT
    1977          12 : u64 gf_route_dmx_get_nb_packets(GF_ROUTEDmx *routedmx)
    1978             : {
    1979          12 :         return routedmx ? routedmx->nb_packets : 0;
    1980             : }
    1981             : 
    1982             : GF_EXPORT
    1983          12 : u64 gf_route_dmx_get_recv_bytes(GF_ROUTEDmx *routedmx)
    1984             : {
    1985          12 :         return routedmx ? routedmx->total_bytes_recv : 0;
    1986             : }
    1987             : 
    1988             : GF_EXPORT
    1989           1 : void gf_route_dmx_debug_tsi(GF_ROUTEDmx *routedmx, u32 tsi)
    1990             : {
    1991           1 :         if (routedmx) routedmx->debug_tsi = tsi;
    1992           1 : }
    1993             : 
    1994             : #endif /* GPAC_DISABLE_ROUTE */

Generated by: LCOV version 1.13