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 */
|