Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / ISOBMFF reader filter
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 "isoffin.h"
27 :
28 : #ifndef GPAC_DISABLE_ISOM
29 :
30 : #include <gpac/crypt_tools.h>
31 : #include <gpac/media_tools.h>
32 :
33 0 : ISOMChannel *isor_get_channel(ISOMReader *reader, GF_FilterPid *pid)
34 : {
35 76195 : u32 i=0;
36 : ISOMChannel *ch;
37 235616 : while ((ch = (ISOMChannel *)gf_list_enum(reader->channels, &i))) {
38 162041 : if (ch->pid == pid) return ch;
39 : }
40 : return NULL;
41 : }
42 :
43 :
44 677 : static GFINLINE Bool isor_is_local(const char *url)
45 : {
46 677 : if (!strnicmp(url, "file://", 7)) return GF_TRUE;
47 677 : if (!strnicmp(url, "gmem://", 7)) return GF_TRUE;
48 623 : if (!strnicmp(url, "gfio://", 7)) return GF_TRUE;
49 622 : if (!strnicmp(url, "isobmff://", 10)) return GF_TRUE;
50 617 : if (strstr(url, "://")) return GF_FALSE;
51 : /*the rest is local (mounted on FS)*/
52 617 : return GF_TRUE;
53 : }
54 :
55 :
56 677 : static GF_Err isoffin_setup(GF_Filter *filter, ISOMReader *read)
57 : {
58 : char szURL[2048];
59 : char *tmp, *src;
60 : GF_Err e;
61 : const GF_PropertyValue *prop;
62 677 : if (!read) return GF_SERVICE_ERROR;
63 :
64 677 : if (read->pid) {
65 677 : prop = gf_filter_pid_get_property(read->pid, GF_PROP_PID_FILEPATH);
66 : assert(prop);
67 677 : src = prop->value.string;
68 : } else {
69 0 : src = read->src;
70 : }
71 677 : if (!src) return GF_SERVICE_ERROR;
72 :
73 677 : read->src_crc = gf_crc_32(src, (u32) strlen(src));
74 :
75 : strcpy(szURL, src);
76 677 : tmp = gf_file_ext_start(szURL);
77 677 : if (tmp) {
78 : Bool truncate = GF_TRUE;
79 618 : tmp = strchr(tmp, '#');
80 618 : if (!tmp && read->pid) {
81 617 : prop = gf_filter_pid_get_property(read->pid, GF_PROP_PID_URL);
82 617 : if (prop && prop->value.string) {
83 617 : tmp = gf_file_ext_start(prop->value.string);
84 617 : if (tmp) tmp = strchr(tmp, '#');
85 : truncate = GF_FALSE;
86 : }
87 : }
88 618 : if (tmp) {
89 1 : if (!strnicmp(tmp, "#audio", 6)) {
90 0 : read->play_only_first_media = GF_ISOM_MEDIA_AUDIO;
91 1 : } else if (!strnicmp(tmp, "#video", 6)) {
92 1 : read->play_only_first_media = GF_ISOM_MEDIA_VISUAL;
93 0 : } else if (!strnicmp(tmp, "#auxv", 5)) {
94 0 : read->play_only_first_media = GF_ISOM_MEDIA_AUXV;
95 0 : } else if (!strnicmp(tmp, "#pict", 5)) {
96 0 : read->play_only_first_media = GF_ISOM_MEDIA_PICT;
97 0 : } else if (!strnicmp(tmp, "#text", 5)) {
98 0 : read->play_only_first_media = GF_ISOM_MEDIA_TEXT;
99 0 : } else if (!strnicmp(tmp, "#trackID=", 9)) {
100 0 : read->play_only_track_id = atoi(tmp+9);
101 0 : } else if (!strnicmp(tmp, "#ID=", 4)) {
102 0 : read->play_only_track_id = atoi(tmp+4);
103 : } else {
104 0 : read->play_only_track_id = atoi(tmp+1);
105 : }
106 1 : if (truncate) tmp[0] = 0;
107 : }
108 : }
109 :
110 677 : if (! isor_is_local(szURL)) {
111 : return GF_NOT_SUPPORTED;
112 : }
113 677 : read->start_range = read->end_range = 0;
114 677 : prop = gf_filter_pid_get_property(read->pid, GF_PROP_PID_FILE_RANGE);
115 677 : if (prop) {
116 14 : read->start_range = prop->value.lfrac.num;
117 14 : read->end_range = prop->value.lfrac.den;
118 : }
119 :
120 677 : e = gf_isom_open_progressive(szURL, read->start_range, read->end_range, read->sigfrag, &read->mov, &read->missing_bytes);
121 :
122 677 : if (e == GF_ISOM_INCOMPLETE_FILE) {
123 25 : read->moov_not_loaded = 1;
124 25 : return GF_OK;
125 : }
126 652 : read->input_loaded = GF_TRUE;
127 :
128 652 : if (e != GF_OK) {
129 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[IsoMedia] error while opening %s, error=%s\n", szURL,gf_error_to_string(e)));
130 0 : gf_filter_setup_failure(filter, e);
131 0 : return e;
132 : }
133 652 : read->frag_type = gf_isom_is_fragmented(read->mov) ? 1 : 0;
134 652 : if (!read->frag_type && read->sigfrag) {
135 : e = GF_BAD_PARAM;
136 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[IsoMedia] sigfrag requested but file %s is not fragmented\n", szURL));
137 0 : gf_filter_setup_failure(filter, e);
138 0 : return e;
139 : }
140 :
141 652 : read->time_scale = gf_isom_get_timescale(read->mov);
142 652 : if (!read->input_loaded && read->frag_type)
143 0 : read->refresh_fragmented = GF_TRUE;
144 :
145 652 : if (read->strtxt)
146 1 : gf_isom_text_set_streaming_mode(read->mov, GF_TRUE);
147 :
148 652 : return isor_declare_objects(read);
149 : }
150 :
151 1184 : static void isoffin_delete_channel(ISOMChannel *ch)
152 : {
153 1184 : isor_reset_reader(ch);
154 1184 : if (ch->nal_bs) gf_bs_del(ch->nal_bs);
155 1184 : if (ch->avcc) gf_odf_avc_cfg_del(ch->avcc);
156 1184 : if (ch->hvcc) gf_odf_hevc_cfg_del(ch->hvcc);
157 1184 : if (ch->vvcc) gf_odf_vvc_cfg_del(ch->vvcc);
158 1184 : gf_free(ch);
159 1184 : }
160 :
161 13 : static void isoffin_disconnect(ISOMReader *read)
162 : {
163 13 : read->disconnected = GF_TRUE;
164 57 : while (gf_list_count(read->channels)) {
165 31 : ISOMChannel *ch = (ISOMChannel *)gf_list_get(read->channels, 0);
166 31 : gf_list_rem(read->channels, 0);
167 31 : if (ch->pid)
168 31 : gf_filter_pid_remove(ch->pid);
169 31 : isoffin_delete_channel(ch);
170 : }
171 :
172 13 : if (read->mov) gf_isom_close(read->mov);
173 13 : read->mov = NULL;
174 :
175 13 : read->pid = NULL;
176 13 : }
177 :
178 3013 : static GF_Err isoffin_reconfigure(GF_Filter *filter, ISOMReader *read, const char *next_url)
179 : {
180 : const GF_PropertyValue *prop;
181 : u32 i, count;
182 : Bool is_new_mov = GF_FALSE;
183 : u64 tfdt;
184 : // GF_ISOTrackID trackID;
185 : GF_ISOSegOpenMode flags=0;
186 : GF_Err e;
187 :
188 3013 : prop = gf_filter_pid_get_property(read->pid, GF_PROP_PID_FILE_CACHED);
189 3013 : if (prop && prop->value.boolean)
190 2515 : read->input_loaded = GF_TRUE;
191 :
192 3013 : read->refresh_fragmented = GF_FALSE;
193 3013 : read->full_segment_flush = GF_TRUE;
194 3013 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] reconfigure triggered, URL %s\n", next_url));
195 :
196 : //no need to lock blob if next_url is a blob, all parsing and probing functions below will lock the blob if any
197 :
198 3013 : switch (gf_isom_probe_file_range(next_url, read->start_range, read->end_range)) {
199 : //this is a fragment
200 2961 : case 3:
201 2961 : gf_isom_release_segment(read->mov, 1);
202 2961 : gf_isom_reset_fragment_info(read->mov, GF_TRUE);
203 :
204 2961 : if (read->no_order_check) flags |= GF_ISOM_SEGMENT_NO_ORDER_FLAG;
205 : #ifdef FILTER_FIXME
206 : if (scalable_segment) flags |= GF_ISOM_SEGMENT_SCALABLE_FLAG;
207 : #endif
208 2961 : e = gf_isom_open_segment(read->mov, next_url, read->start_range, read->end_range, flags);
209 2961 : if (!read->input_loaded && (e==GF_ISOM_INCOMPLETE_FILE)) {
210 : e = GF_OK;
211 : }
212 : //always refresh fragmented files, since we could have a full moof+mdat in buffer (not incomplete file)
213 : //but still further fragments to be pushed
214 2961 : if (!read->start_range && !read->end_range)
215 2680 : read->refresh_fragmented = GF_TRUE;
216 2961 : read->seg_name_changed = GF_TRUE;
217 :
218 20516 : for (i=0; i<gf_list_count(read->channels); i++) {
219 17555 : ISOMChannel *ch = gf_list_get(read->channels, i);
220 17555 : if (ch->last_state==GF_EOS)
221 17555 : ch->last_state=GF_OK;
222 : }
223 :
224 : #ifndef GPAC_DISABLE_LOG
225 2961 : if (e<0) {
226 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[IsoMedia] Error opening new segment %s at UTC "LLU": %s\n", next_url, gf_net_get_utc(), gf_error_to_string(e) ));
227 2961 : } else if (read->end_range) {
228 281 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Playing new range in %s: "LLU"-"LLU"\n", next_url, read->start_range, read->end_range));
229 : } else {
230 2680 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] playing new segment %s\n", next_url));
231 : }
232 : #endif
233 : break;
234 : //this is a movie, reload
235 52 : case 2:
236 : case 1:
237 : //get tfdt of next segment (cumulated sample dur since moov load)
238 : //if the next segment has a tfdt or a tfrx, this will be ignored
239 : //otherwise this value will be used as base tfdt for next segment
240 52 : tfdt = gf_isom_get_smooth_next_tfdt(read->mov, 1);
241 52 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Switching between files - opening new init segment %s (time offset="LLU") - range "LLU"-"LLU"\n", next_url, tfdt, read->start_range, read->end_range));
242 :
243 52 : if (gf_isom_is_smooth_streaming_moov(read->mov)) {
244 1 : char *tfdt_val = strstr(next_url, "tfdt=");
245 : //smooth addressing, replace tfdt=0000000000000000 with proper value
246 1 : if (tfdt_val) {
247 1 : sprintf(tfdt_val+5, LLX, tfdt);
248 : } else {
249 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[IsoMedia] Error finding init time for init segment %s at UTC "LLU"\n", next_url, gf_net_get_utc() ));
250 : }
251 : }
252 :
253 52 : if (read->mov) gf_isom_close(read->mov);
254 52 : e = gf_isom_open_progressive(next_url, read->start_range, read->end_range, read->sigfrag, &read->mov, &read->missing_bytes);
255 :
256 : //init seg not completely downloaded, retry at next packet
257 52 : if (!read->input_loaded && (e==GF_ISOM_INCOMPLETE_FILE)) {
258 0 : read->src_crc = 0;
259 0 : read->moov_not_loaded = 2;
260 0 : return GF_OK;
261 : }
262 :
263 52 : read->moov_not_loaded = 0;
264 52 : if (e < 0) {
265 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[IsoMedia] Error opening init segment %s at UTC "LLU": %s\n", next_url, gf_net_get_utc(), gf_error_to_string(e) ));
266 : }
267 52 : if (read->sigfrag)
268 0 : gf_isom_enable_traf_map_templates(read->mov);
269 :
270 : is_new_mov = GF_TRUE;
271 : break;
272 : //empty file
273 : case 4:
274 : return GF_OK;
275 0 : default:
276 0 : if (!read->mov) {
277 : return GF_NOT_SUPPORTED;
278 : }
279 : e = GF_ISOM_INVALID_FILE;
280 : break;
281 : }
282 :
283 3013 : gf_filter_post_process_task(filter);
284 :
285 3013 : count = gf_list_count(read->channels);
286 :
287 3013 : if (e<0) {
288 0 : count = gf_list_count(read->channels);
289 0 : gf_isom_release_segment(read->mov, 1);
290 0 : read->invalid_segment = GF_TRUE;
291 : //error opening the segment, reset everything ...
292 0 : gf_isom_reset_fragment_info(read->mov, GF_FALSE);
293 0 : for (i=0; i<count; i++) {
294 0 : ISOMChannel *ch = gf_list_get(read->channels, i);
295 0 : if (ch) {
296 0 : ch->sample_num = 0;
297 0 : ch->eos_sent = GF_FALSE;
298 : }
299 : }
300 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[IsoMedia] Error opening current segment %s: %s\n", next_url, gf_error_to_string(e) ));
301 : return GF_OK;
302 : }
303 : //segment is the first in our cache, we may need a refresh
304 3013 : if (!read->input_loaded) {
305 498 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Opening current segment in progressive mode (download in progress)\n"));
306 : } else {
307 2515 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Opening current segment in non-progressive mode (completely downloaded)\n"));
308 : }
309 :
310 3013 : isor_check_producer_ref_time(read);
311 :
312 20620 : for (i=0; i<count; i++) {
313 17607 : ISOMChannel *ch = gf_list_get(read->channels, i);
314 17607 : ch->last_state = GF_OK;
315 17607 : ch->eos_sent = GF_FALSE;
316 :
317 : //old code from master, currently no longer used
318 : //in filters we don't use extractors for the time being, we only do implicit reconstruction at the decoder side
319 : #if 0
320 : if (ch->base_track) {
321 : if (scalable_segment)
322 : trackID = gf_isom_get_highest_track_in_scalable_segment(read->mov, ch->base_track);
323 : if (trackID) {
324 : ch->track_id = trackID;
325 : ch->track = gf_isom_get_track_by_id(read->mov, ch->track_id);
326 : }
327 : } else {
328 : ch->track = ch->base_track;
329 : ch->track_id = gf_isom_get_track_id(read->mov, ch->track);
330 : }
331 : }
332 : #endif
333 :
334 17607 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Track %d - cur sample %d - new sample count %d\n", ch->track, ch->sample_num, gf_isom_get_sample_count(ch->owner->mov, ch->track) ));
335 :
336 : //TODO: signal all discontinuities here
337 17607 : if (is_new_mov) {
338 52 : ch->track = gf_isom_get_track_by_id(read->mov, ch->track_id);
339 52 : if (!ch->track) {
340 0 : if (gf_isom_get_track_count(read->mov)==1) {
341 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Mismatch between track IDs of different representations\n"));
342 0 : ch->track = 1;
343 : } else {
344 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[IsoMedia] Mismatch between track IDs of different representations\n"));
345 : }
346 : }
347 :
348 : /*we changed our moov structure, sample_num now starts from 0*/
349 52 : ch->sample_num = 0;
350 : //this may happen if we reload moov before initializing the channel
351 52 : if (!ch->last_sample_desc_index)
352 1 : ch->last_sample_desc_index = 1;
353 : //and update channel config
354 52 : isor_update_channel_config(ch);
355 :
356 : /*restore NAL extraction mode*/
357 52 : gf_isom_set_nalu_extract_mode(read->mov, ch->track, ch->nalu_extract_mode);
358 :
359 52 : if (ch->is_cenc) {
360 7 : isor_set_crypt_config(ch);
361 : }
362 : }
363 :
364 17607 : ch->last_state = GF_OK;
365 : }
366 : return e;
367 : }
368 :
369 3682 : GF_Err isoffin_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
370 : {
371 : const GF_PropertyValue *prop;
372 3682 : ISOMReader *read = gf_filter_get_udta(filter);
373 :
374 3682 : if (is_remove) {
375 13 : isoffin_disconnect(read);
376 13 : return GF_OK;
377 : }
378 : //check if we have a file path; if not, this is a pure stream of boxes (no local file cache)
379 3669 : prop = gf_filter_pid_get_property(pid, GF_PROP_PID_FILEPATH);
380 3669 : if (!prop || !prop->value.string) {
381 4 : if (!read->mem_load_mode)
382 4 : read->mem_load_mode = 1;
383 4 : if (!read->pid) read->pid = pid;
384 4 : read->input_loaded = GF_FALSE;
385 4 : return GF_OK;
386 : }
387 :
388 3665 : if (read->pid && prop->value.string) {
389 : const char *next_url = prop->value.string;
390 : u64 sr, er;
391 3013 : u32 crc = gf_crc_32(next_url, (u32) strlen(next_url) );
392 :
393 : sr = er = 0;
394 3013 : prop = gf_filter_pid_get_property(read->pid, GF_PROP_PID_FILE_RANGE);
395 3013 : if (prop) {
396 281 : sr = prop->value.lfrac.num;
397 281 : er = prop->value.lfrac.den;
398 : }
399 :
400 : //if eos is signaled, don't check for crc since we might have the same blob address (same alloc)
401 3013 : if (!read->eos_signaled && (read->src_crc == crc) && (read->start_range==sr) && (read->end_range==er)) {
402 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] same URL crc and range for %s, skipping reconfigure\n", next_url));
403 : return GF_OK;
404 : }
405 3013 : read->src_crc = crc;
406 3013 : read->start_range = sr;
407 3013 : read->end_range = er;
408 3013 : read->input_loaded = GF_FALSE;
409 3013 : read->eos_signaled = GF_FALSE;
410 :
411 : //we need to reconfigure
412 3013 : return isoffin_reconfigure(filter, read, next_url);
413 : }
414 :
415 652 : read->pid = pid;
416 652 : prop = gf_filter_pid_get_property(pid, GF_PROP_PID_FILE_CACHED);
417 652 : if (prop && prop->value.boolean) {
418 : GF_FilterEvent evt;
419 588 : read->input_loaded = GF_TRUE;
420 588 : GF_FEVT_INIT(evt, GF_FEVT_PLAY_HINT, pid);
421 588 : evt.play.full_file_only=1;
422 588 : gf_filter_pid_send_event(pid, &evt);
423 : }
424 652 : return isoffin_setup(filter, read);
425 : }
426 :
427 947 : GF_Err isoffin_initialize(GF_Filter *filter)
428 : {
429 947 : ISOMReader *read = gf_filter_get_udta(filter);
430 : GF_Err e = GF_OK;
431 947 : read->filter = filter;
432 947 : read->channels = gf_list_new();
433 :
434 947 : if (read->xps_check==MP4DMX_XPS_AUTO) {
435 947 : read->xps_check = (read->smode==MP4DMX_SPLIT_EXTRACTORS) ? MP4DMX_XPS_KEEP : MP4DMX_XPS_REMOVE;
436 : }
437 :
438 947 : if (read->src) {
439 0 : read->input_loaded = GF_TRUE;
440 0 : return isoffin_setup(filter, read);
441 : }
442 947 : else if (read->mov) {
443 291 : read->extern_mov = GF_TRUE;
444 291 : read->input_loaded = GF_TRUE;
445 291 : read->frag_type = gf_isom_is_fragmented(read->mov) ? 1 : 0;
446 291 : read->time_scale = gf_isom_get_timescale(read->mov);
447 :
448 291 : if (read->sigfrag) {
449 3 : gf_isom_enable_traf_map_templates(read->mov);
450 : }
451 :
452 291 : if (read->catseg) {
453 3 : e = gf_isom_open_segment(read->mov, read->catseg, 0, 0, 0);
454 : }
455 3 : if (!e)
456 291 : e = isor_declare_objects(read);
457 :
458 291 : gf_filter_post_process_task(filter);
459 : }
460 : return e;
461 : }
462 :
463 :
464 947 : static void isoffin_finalize(GF_Filter *filter)
465 : {
466 947 : ISOMReader *read = (ISOMReader *) gf_filter_get_udta(filter);
467 :
468 947 : read->disconnected = GF_TRUE;
469 :
470 3047 : while (gf_list_count(read->channels)) {
471 1153 : ISOMChannel *ch = (ISOMChannel *)gf_list_get(read->channels, 0);
472 1153 : gf_list_rem(read->channels, 0);
473 1153 : isoffin_delete_channel(ch);
474 : }
475 947 : gf_list_del(read->channels);
476 :
477 947 : if (!read->extern_mov && read->mov) gf_isom_close(read->mov);
478 947 : read->mov = NULL;
479 :
480 947 : if (read->mem_blob.data) gf_free(read->mem_blob.data);
481 947 : if (read->mem_url) gf_free(read->mem_url);
482 947 : }
483 :
484 206 : void isor_declare_pssh(ISOMChannel *ch)
485 : {
486 : u32 i, PSSH_count;
487 : u8 *psshd;
488 206 : GF_BitStream *pssh_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
489 : u32 s;
490 :
491 206 : PSSH_count = gf_isom_get_pssh_count(ch->owner->mov);
492 206 : gf_bs_write_u32(pssh_bs, PSSH_count);
493 :
494 : /*fill PSSH in the structure. We will free it in CENC_Setup*/
495 688 : for (i=0; i<PSSH_count; i++) {
496 : bin128 SystemID;
497 : u32 version;
498 : u32 KID_count;
499 : bin128 *KIDs;
500 : u32 private_data_size;
501 : u8 *private_data;
502 276 : gf_isom_get_pssh_info(ch->owner->mov, i+1, SystemID, &version, &KID_count, (const bin128 **) & KIDs, (const u8 **) &private_data, &private_data_size);
503 :
504 276 : gf_bs_write_data(pssh_bs, SystemID, 16);
505 276 : gf_bs_write_u32(pssh_bs, version);
506 276 : gf_bs_write_u32(pssh_bs, KID_count);
507 535 : for (s=0; s<KID_count; s++) {
508 259 : gf_bs_write_data(pssh_bs, KIDs[s], 16);
509 : }
510 276 : gf_bs_write_u32(pssh_bs, private_data_size);
511 276 : gf_bs_write_data(pssh_bs, private_data, private_data_size);
512 : }
513 206 : gf_bs_get_content(pssh_bs, &psshd, &s);
514 206 : gf_bs_del(pssh_bs);
515 206 : gf_filter_pid_set_property(ch->pid, GF_PROP_PID_CENC_PSSH, & PROP_DATA_NO_COPY(psshd, s) );
516 206 : }
517 :
518 215 : void isor_set_crypt_config(ISOMChannel *ch)
519 : {
520 215 : GF_ISOFile *mov = ch->owner->mov;
521 215 : u32 track = ch->track;
522 : u32 scheme_type, scheme_version, i, count;
523 : const char *kms_uri, *scheme_uri;
524 215 : Bool selectiveEncryption=0;
525 215 : u32 IVLength=0;
526 215 : u32 KeyIndicationLength=0;
527 215 : const char *txtHdr=NULL;
528 215 : const char *contentID=NULL;
529 215 : u32 txtHdrLen=0;
530 215 : u64 plainTextLen=0;
531 215 : u32 crypt_type=0;
532 215 : u32 stsd_idx = ch->owner->stsd ? ch->owner->stsd : 1;
533 :
534 215 : if (!ch->is_encrypted) return;
535 :
536 215 : scheme_type = scheme_version = 0;
537 215 : kms_uri = scheme_uri = NULL;
538 :
539 : /*ugly fix to detect when an stsd uses both clear and encrypted sample descriptions*/
540 215 : count = gf_isom_get_sample_description_count(ch->owner->mov, ch->track);
541 215 : if (count>1) {
542 : u32 first_crypted_stsd = 0;
543 : u32 nb_same_mtype = 1;
544 : u32 nb_clear=0, nb_encrypted=0;
545 : u32 base_subtype = 0;
546 : Bool first_is_clear = GF_FALSE;
547 18 : for (i=0; i<count; i++) {
548 12 : u32 mtype = gf_isom_get_media_subtype(ch->owner->mov, ch->track, i+1);
549 12 : if ( gf_isom_is_media_encrypted(ch->owner->mov, track, i+1)) {
550 6 : gf_isom_get_original_format_type(ch->owner->mov, ch->track, i+1, &mtype);
551 6 : nb_encrypted++;
552 6 : if (!first_crypted_stsd) first_crypted_stsd = i+1;
553 : } else {
554 6 : nb_clear++;
555 6 : if (!i) first_is_clear = GF_TRUE;
556 : }
557 12 : if (!i) base_subtype = mtype;
558 6 : else if (base_subtype==mtype) {
559 6 : nb_same_mtype++;
560 : }
561 : }
562 6 : if ((nb_same_mtype==count) && (nb_clear==nb_encrypted)) {
563 6 : gf_filter_pid_set_property(ch->pid, GF_PROP_PID_CENC_STSD_MODE, &PROP_UINT(first_is_clear ? 1 : 2) );
564 : stsd_idx = first_crypted_stsd;
565 : }
566 :
567 : }
568 :
569 215 : if (gf_isom_is_ismacryp_media(mov, track, stsd_idx)) {
570 12 : gf_isom_get_ismacryp_info(mov, track, stsd_idx, NULL, &scheme_type, &scheme_version, &scheme_uri, &kms_uri, &selectiveEncryption, &IVLength, &KeyIndicationLength);
571 203 : } else if (gf_isom_is_omadrm_media(mov, track, stsd_idx)) {
572 : //u8 hash[20];
573 0 : gf_isom_get_omadrm_info(mov, track, stsd_idx, NULL, &scheme_type, &scheme_version, &contentID, &kms_uri, &txtHdr, &txtHdrLen, &plainTextLen, &crypt_type, &selectiveEncryption, &IVLength, &KeyIndicationLength);
574 :
575 : //gf_media_get_file_hash(gf_isom_get_filename(mov), hash);
576 203 : } else if (gf_isom_is_cenc_media(mov, track, stsd_idx)) {
577 193 : ch->is_cenc = GF_TRUE;
578 :
579 193 : gf_isom_get_cenc_info(ch->owner->mov, ch->track, stsd_idx, NULL, &scheme_type, &scheme_version);
580 :
581 : //if no PSSH declared, DO update the properties (PSSH is not mandatory)
582 10 : } else if (gf_isom_is_adobe_protection_media(mov, track, stsd_idx)) {
583 : u32 ofmt;
584 10 : scheme_version = 1;
585 10 : scheme_type = GF_ISOM_ADOBE_SCHEME;
586 10 : const char *metadata = NULL;
587 :
588 10 : gf_isom_get_adobe_protection_info(mov, track, stsd_idx, &ofmt, &scheme_type, &scheme_version, &metadata);
589 10 : if (metadata)
590 10 : gf_filter_pid_set_property(ch->pid, GF_PROP_PID_ADOBE_CRYPT_META, &PROP_DATA((char *)metadata, (u32) strlen(metadata) ) );
591 : }
592 :
593 215 : gf_filter_pid_set_property(ch->pid, GF_PROP_PID_PROTECTION_SCHEME_TYPE, &PROP_4CC(scheme_type) );
594 215 : gf_filter_pid_set_property(ch->pid, GF_PROP_PID_PROTECTION_SCHEME_VERSION, &PROP_UINT(scheme_version) );
595 215 : if (scheme_uri) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_PROTECTION_SCHEME_URI, &PROP_STRING((char*) scheme_uri) );
596 215 : if (kms_uri) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_PROTECTION_KMS_URI, &PROP_STRING((char*) kms_uri) );
597 :
598 215 : if (selectiveEncryption) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_ISMA_SELECTIVE_ENC, &PROP_BOOL(GF_TRUE) );
599 215 : if (IVLength) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_ISMA_IV_LENGTH, &PROP_UINT(IVLength) );
600 215 : if (KeyIndicationLength) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_ISMA_KI_LENGTH, &PROP_UINT(KeyIndicationLength) );
601 215 : if (crypt_type) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_OMA_CRYPT_TYPE, &PROP_UINT(crypt_type) );
602 215 : if (contentID) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_OMA_CID, &PROP_STRING(contentID) );
603 215 : if (txtHdr) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_OMA_TXT_HDR, &PROP_STRING(txtHdr) );
604 215 : if (plainTextLen) gf_filter_pid_set_property(ch->pid, GF_PROP_PID_OMA_CLEAR_LEN, &PROP_LONGUINT(plainTextLen) );
605 :
606 215 : if (ch->is_cenc) {
607 : const u8 *key_info;
608 : u32 key_info_size;
609 : u32 container_type;
610 :
611 193 : isor_declare_pssh(ch);
612 :
613 193 : gf_isom_cenc_get_default_info(ch->owner->mov, ch->track, stsd_idx, &container_type, &ch->pck_encrypted, &ch->crypt_byte_block, &ch->skip_byte_block, &key_info, &key_info_size);
614 :
615 193 : gf_filter_pid_set_property(ch->pid, GF_PROP_PID_CENC_STORE, &PROP_4CC(container_type) );
616 :
617 193 : gf_filter_pid_set_property(ch->pid, GF_PROP_PID_ENCRYPTED, &PROP_BOOL(ch->pck_encrypted) );
618 :
619 193 : if (ch->skip_byte_block || ch->crypt_byte_block) {
620 39 : gf_filter_pid_set_property(ch->pid, GF_PROP_PID_CENC_PATTERN, &PROP_FRAC_INT(ch->skip_byte_block, ch->crypt_byte_block) );
621 : }
622 193 : gf_filter_pid_set_property(ch->pid, GF_PROP_PID_CENC_KEY_INFO, &PROP_DATA((u8 *)key_info, key_info_size) );
623 193 : ch->key_info_crc = gf_crc_32(key_info, key_info_size);
624 : }
625 : }
626 :
627 :
628 1184 : ISOMChannel *isor_create_channel(ISOMReader *read, GF_FilterPid *pid, u32 track, u32 item_id, Bool force_no_extractors)
629 : {
630 : ISOMChannel *ch;
631 : const GF_PropertyValue *p;
632 : s64 ts_shift;
633 1184 : if (!read->mov) return NULL;
634 :
635 1184 : GF_SAFEALLOC(ch, ISOMChannel);
636 1184 : if (!ch) {
637 : return NULL;
638 : }
639 1184 : ch->owner = read;
640 1184 : ch->pid = pid;
641 1184 : ch->to_init = GF_TRUE;
642 1184 : gf_list_add(read->channels, ch);
643 1184 : ch->track = track;
644 1184 : ch->item_id = item_id;
645 :
646 1184 : ch->nalu_extract_mode = 0;
647 1184 : ch->track_id = gf_isom_get_track_id(read->mov, ch->track);
648 1184 : switch (gf_isom_get_media_type(ch->owner->mov, ch->track)) {
649 0 : case GF_ISOM_MEDIA_OCR:
650 0 : ch->streamType = GF_STREAM_OCR;
651 0 : break;
652 20 : case GF_ISOM_MEDIA_SCENE:
653 20 : ch->streamType = GF_STREAM_SCENE;
654 20 : break;
655 751 : case GF_ISOM_MEDIA_VISUAL:
656 : case GF_ISOM_MEDIA_AUXV:
657 : case GF_ISOM_MEDIA_PICT:
658 751 : gf_isom_get_reference(ch->owner->mov, ch->track, GF_ISOM_REF_BASE, 1, &ch->base_track);
659 : //use base track only if avc/svc or hevc/lhvc. If avc+lhvc we need different rules
660 751 : if ( gf_isom_get_avc_svc_type(ch->owner->mov, ch->base_track, 1) == GF_ISOM_AVCTYPE_AVC_ONLY) {
661 4 : if ( gf_isom_get_hevc_lhvc_type(ch->owner->mov, ch->track, 1) >= GF_ISOM_HEVCTYPE_HEVC_ONLY) {
662 0 : ch->base_track=0;
663 : }
664 : }
665 751 : ch->next_track = 0;
666 : /*in scalable mode add SPS/PPS in-band*/
667 751 : if (ch->base_track)
668 6 : ch->nalu_extract_mode = GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG /*| GF_ISOM_NALU_EXTRACT_ANNEXB_FLAG*/;
669 : break;
670 : }
671 1184 : if (!read->noedit) {
672 1107 : ch->ts_offset = 0;
673 1107 : ch->has_edit_list = gf_isom_get_edit_list_type(ch->owner->mov, ch->track, &ch->ts_offset) ? GF_TRUE : GF_FALSE;
674 1107 : if (!ch->has_edit_list && ch->ts_offset) {
675 : //if >0 this is a hold, we signal positive delay
676 : //if <0 this is a skip, we signal negative delay
677 218 : gf_filter_pid_set_property(pid, GF_PROP_PID_DELAY, &PROP_LONGSINT( ch->ts_offset) );
678 : }
679 : } else
680 77 : ch->has_edit_list = GF_FALSE;
681 :
682 1184 : ch->has_rap = (gf_isom_has_sync_points(ch->owner->mov, ch->track)==1) ? GF_TRUE : GF_FALSE;
683 1184 : gf_filter_pid_set_property(pid, GF_PROP_PID_HAS_SYNC, &PROP_BOOL(ch->has_rap) );
684 : //some fragmented files do not advertize a sync sample table (legal) so we need to update as soon as we fetch a fragment
685 : //to see if we are all-intra (as detected here) or not
686 1184 : if (!ch->has_rap && ch->owner->frag_type)
687 255 : ch->check_has_rap = GF_TRUE;
688 1184 : ch->time_scale = gf_isom_get_media_timescale(ch->owner->mov, ch->track);
689 :
690 1184 : ts_shift = gf_isom_get_cts_to_dts_shift(ch->owner->mov, ch->track);
691 1184 : if (ts_shift) {
692 6 : gf_filter_pid_set_property(pid, GF_PROP_PID_CTS_SHIFT, &PROP_UINT((u32) ts_shift) );
693 : }
694 :
695 1184 : if (!track || !gf_isom_is_track_encrypted(read->mov, track)) {
696 976 : if (force_no_extractors) {
697 0 : ch->nalu_extract_mode = GF_ISOM_NALU_EXTRACT_LAYER_ONLY;
698 : } else {
699 976 : switch (read->smode) {
700 132 : case MP4DMX_SPLIT_EXTRACTORS:
701 132 : ch->nalu_extract_mode = GF_ISOM_NALU_EXTRACT_INSPECT | GF_ISOM_NALU_EXTRACT_TILE_ONLY;
702 132 : break;
703 844 : case MP4DMX_SPLIT:
704 844 : ch->nalu_extract_mode = GF_ISOM_NALU_EXTRACT_LAYER_ONLY | GF_ISOM_NALU_EXTRACT_TILE_ONLY;
705 844 : break;
706 : default:
707 : break;
708 : }
709 : }
710 :
711 976 : if (ch->nalu_extract_mode) {
712 976 : gf_isom_set_nalu_extract_mode(ch->owner->mov, ch->track, ch->nalu_extract_mode);
713 : }
714 : return ch;
715 : }
716 208 : if (ch->owner->nocrypt) {
717 0 : ch->is_encrypted = GF_FALSE;
718 0 : return ch;
719 : }
720 208 : ch->is_encrypted = GF_TRUE;
721 208 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
722 208 : if (p) gf_filter_pid_set_property(pid, GF_PROP_PID_ORIG_STREAM_TYPE, &PROP_UINT(p->value.uint) );
723 :
724 208 : gf_filter_pid_set_property(pid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_ENCRYPTED) );
725 :
726 208 : isor_set_crypt_config(ch);
727 :
728 208 : if (ch->nalu_extract_mode) {
729 0 : if (ch->is_encrypted) {
730 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[IsoMedia] using sample NAL rewrite with encryption is not yet supported, patch welcome\n"));
731 : } else {
732 0 : gf_isom_set_nalu_extract_mode(ch->owner->mov, ch->track, ch->nalu_extract_mode);
733 : }
734 : }
735 : return ch;
736 : }
737 :
738 : /*switch channel quality. Return next channel or current channel if error*/
739 : static
740 0 : u32 isoffin_channel_switch_quality(ISOMChannel *ch, GF_ISOFile *the_file, Bool switch_up)
741 : {
742 : u32 i, count, next_track, trackID, cur_track;
743 : s32 ref_count;
744 :
745 0 : cur_track = ch->next_track ? ch->next_track : ch->track;
746 0 : count = gf_isom_get_track_count(the_file);
747 0 : trackID = gf_isom_get_track_id(the_file, cur_track);
748 0 : next_track = 0;
749 :
750 0 : if (switch_up) {
751 0 : for (i = 0; i < count; i++) {
752 0 : ref_count = gf_isom_get_reference_count(the_file, i+1, GF_ISOM_REF_SCAL);
753 0 : if (ref_count < 0)
754 : return cur_track; //error
755 0 : if (ref_count == 0)
756 0 : continue;
757 : /*next track is the one that has the last reference of type GF_ISOM_REF_SCAL refers to this current track*/
758 0 : if ((u32)ref_count == gf_isom_has_track_reference(the_file, i+1, GF_ISOM_REF_SCAL, trackID)) {
759 0 : next_track = i+1;
760 0 : break;
761 : }
762 : }
763 : /*this is the highest quality*/
764 0 : if (!next_track) {
765 0 : ch->playing = GF_TRUE;
766 0 : ref_count = gf_isom_get_reference_count(the_file, ch->track, GF_ISOM_REF_BASE);
767 0 : trackID = 0;
768 0 : if (ref_count) {
769 0 : gf_isom_get_reference(the_file, ch->track, GF_ISOM_REF_BASE, 1, &trackID);
770 0 : for (i=0; i<gf_list_count(ch->owner->channels) && trackID; i++) {
771 0 : ISOMChannel *base = gf_list_get(ch->owner->channels, i);
772 0 : if (base->track_id==trackID) {
773 : u32 sample_desc_index;
774 : u64 resume_at;
775 : GF_Err e;
776 : //try to locate sync after current time in base
777 0 : resume_at = base->static_sample->DTS;
778 0 : resume_at *= ch->time_scale;
779 0 : resume_at /= base->time_scale;
780 0 : e = gf_isom_get_sample_for_media_time(ch->owner->mov, ch->track, resume_at, &sample_desc_index, GF_ISOM_SEARCH_SYNC_FORWARD, &ch->static_sample, &ch->sample_num, &ch->sample_data_offset);
781 : //found, rewind so that next fetch is the sync
782 0 : if (e==GF_OK) {
783 0 : ch->sample = NULL;
784 : }
785 : //no further sync found, realign with base timescale
786 0 : else if (e==GF_EOS) {
787 0 : e = gf_isom_get_sample_for_media_time(ch->owner->mov, ch->track, resume_at, &sample_desc_index, GF_ISOM_SEARCH_FORWARD, &ch->static_sample, &ch->sample_num, &ch->sample_data_offset);
788 : }
789 : //unknown state, realign sample num with base
790 0 : if (e<0) {
791 0 : ch->sample_num = base->sample_num;
792 : }
793 : break;
794 : }
795 : }
796 : }
797 : return cur_track;
798 : }
799 : } else {
800 0 : if (cur_track == ch->base_track)
801 : return cur_track;
802 0 : ref_count = gf_isom_get_reference_count(the_file, cur_track, GF_ISOM_REF_SCAL);
803 0 : if (ref_count <= 0)
804 : return cur_track;
805 0 : gf_isom_get_reference(the_file, cur_track, GF_ISOM_REF_SCAL, ref_count, &next_track);
806 0 : if (!next_track)
807 : return cur_track;
808 :
809 0 : if (ch->track != next_track) {
810 0 : ch->playing = GF_FALSE;
811 0 : ch->eos_sent = GF_TRUE;
812 0 : gf_filter_pid_set_eos(ch->pid);
813 : }
814 : }
815 :
816 : /*in scalable mode add SPS/PPS in-band*/
817 0 : if (ch->owner->smode)
818 0 : gf_isom_set_nalu_extract_mode(the_file, next_track, ch->nalu_extract_mode);
819 :
820 0 : return next_track;
821 : }
822 :
823 76195 : static Bool isoffin_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
824 : {
825 : u32 count, i;
826 : Bool cancel_event = GF_TRUE;
827 : ISOMChannel *ch;
828 76195 : ISOMReader *read = gf_filter_get_udta(filter);
829 :
830 76195 : if (!read || read->disconnected) return GF_FALSE;
831 :
832 76195 : if (evt->base.type == GF_FEVT_QUALITY_SWITCH) {
833 0 : count = gf_list_count(read->channels);
834 0 : for (i = 0; i < count; i++) {
835 0 : ch = (ISOMChannel *)gf_list_get(read->channels, i);
836 0 : if (ch->base_track && gf_isom_needs_layer_reconstruction(read->mov)) {
837 : /*ch->next_track = */ //old code, see not in isoffin_reconfigure
838 0 : isoffin_channel_switch_quality(ch, read->mov, evt->quality_switch.up);
839 : }
840 : }
841 : return GF_TRUE;
842 : }
843 :
844 76195 : if (!evt->base.on_pid) return GF_FALSE;
845 :
846 : ch = isor_get_channel(read, evt->base.on_pid);
847 76195 : if (!ch)
848 : return GF_FALSE;
849 :
850 2620 : switch (evt->base.type) {
851 1207 : case GF_FEVT_PLAY:
852 1207 : isor_reset_reader(ch);
853 1207 : ch->eos_sent = GF_FALSE;
854 1207 : ch->speed = evt->play.speed;
855 1207 : ch->initial_play_seen = GF_TRUE;
856 1207 : read->reset_frag_state = 1;
857 : //it can happen that input_is_stop is still TRUE because we did not get called back after the stop - reset to FALSE since we now play
858 1207 : read->input_is_stop = GF_FALSE;
859 1207 : if (read->frag_type)
860 262 : read->frag_type = 1;
861 :
862 1207 : ch->start = ch->end = 0;
863 1207 : if (evt->play.speed>=0) {
864 : Double t;
865 1205 : if (evt->play.start_range>=0) {
866 : t = evt->play.start_range;
867 1205 : t *= ch->time_scale;
868 1205 : ch->start = (u64) t;
869 : }
870 1205 : if (evt->play.end_range >= evt->play.start_range) {
871 1197 : ch->end = (u64) -1;
872 1197 : if (evt->play.end_range<FLT_MAX) {
873 : t = evt->play.end_range;
874 1191 : t *= ch->time_scale;
875 1191 : ch->end = (u64) t;
876 : }
877 : }
878 : } else {
879 2 : Double end = evt->play.end_range;
880 2 : if (end==-1) end = 0;
881 2 : ch->start = (u64) (s64) (evt->play.start_range * ch->time_scale);
882 2 : if (end <= evt->play.start_range)
883 2 : ch->end = (u64) (s64) (end * ch->time_scale);
884 : }
885 1207 : ch->playing = GF_TRUE;
886 1207 : ch->sample_num = evt->play.from_pck;
887 :
888 1207 : ch->sap_only = evt->play.drop_non_ref ? GF_TRUE : GF_FALSE;
889 :
890 1207 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[IsoMedia] Starting channel playback "LLD" to "LLD" (%g to %g)\n", ch->start, ch->end, evt->play.start_range, evt->play.end_range));
891 :
892 1207 : if (!read->nb_playing)
893 1005 : gf_isom_reset_seq_num(read->mov);
894 :
895 1207 : if (read->is_partial_download) read->input_loaded = GF_FALSE;
896 :
897 1207 : if (evt->play.no_byterange_forward) {
898 : //new segment will be loaded, reset
899 242 : gf_isom_reset_tables(read->mov, GF_TRUE);
900 242 : gf_isom_reset_data_offset(read->mov, NULL);
901 242 : read->refresh_fragmented = GF_TRUE;
902 242 : read->mem_blob.size = 0;
903 : //send play event
904 : cancel_event = GF_FALSE;
905 965 : } else if (!read->nb_playing && read->pid && !read->input_loaded) {
906 : GF_FilterEvent fevt;
907 : Bool is_sidx_seek = GF_FALSE;
908 4 : u64 max_offset = GF_FILTER_NO_BO;
909 4 : count = gf_list_count(read->channels);
910 :
911 : //try sidx
912 4 : if (read->frag_type) {
913 : u32 ts;
914 1 : u64 dur=0;
915 1 : GF_Err e = gf_isom_get_file_offset_for_time(read->mov, evt->play.start_range, &max_offset);
916 1 : if (e==GF_OK) {
917 1 : if (evt->play.start_range>0)
918 0 : gf_isom_reset_tables(read->mov, GF_TRUE);
919 :
920 : is_sidx_seek = GF_TRUE;
921 : //in case we loaded moov but not sidx, update duration
922 1 : if ((gf_isom_get_sidx_duration(read->mov, &dur, &ts)==GF_OK) && dur) {
923 1 : dur *= read->time_scale;
924 1 : dur /= ts;
925 1 : if (ch->duration != dur) {
926 1 : ch->duration = dur;
927 1 : gf_filter_pid_set_property(ch->pid, GF_PROP_PID_DURATION, &PROP_FRAC64_INT(ch->duration, read->time_scale));
928 : }
929 : }
930 : }
931 : }
932 :
933 : if (!is_sidx_seek) {
934 3 : for (i=0; i< count; i++) {
935 : u32 mode, sample_desc_index, sample_num;
936 : u64 data_offset;
937 : GF_Err e;
938 : u64 time;
939 3 : ch = gf_list_get(read->channels, i);
940 3 : mode = ch->disable_seek ? GF_ISOM_SEARCH_BACKWARD : GF_ISOM_SEARCH_SYNC_BACKWARD;
941 3 : time = (u64) (evt->play.start_range * ch->time_scale);
942 :
943 : /*take care of seeking out of the track range*/
944 3 : if (!read->frag_type && (ch->duration < time)) {
945 0 : e = gf_isom_get_sample_for_movie_time(read->mov, ch->track, ch->duration, &sample_desc_index, mode, NULL, &sample_num, &data_offset);
946 : } else {
947 3 : e = gf_isom_get_sample_for_movie_time(read->mov, ch->track, time, &sample_desc_index, mode, NULL, &sample_num, &data_offset);
948 : }
949 3 : if ((e == GF_OK) && (data_offset<max_offset))
950 3 : max_offset = data_offset;
951 : }
952 : }
953 :
954 4 : if ((evt->play.start_range || read->is_partial_download) && (max_offset != GF_FILTER_NO_BO) ) {
955 :
956 : //send a seek request
957 0 : read->is_partial_download = GF_TRUE;
958 0 : read->wait_for_source = GF_TRUE;
959 0 : read->refresh_fragmented = GF_TRUE;
960 :
961 0 : GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, read->pid);
962 0 : fevt.seek.start_offset = max_offset;
963 0 : gf_filter_pid_send_event(read->pid, &fevt);
964 0 : gf_isom_set_byte_offset(read->mov, is_sidx_seek ? 0 : max_offset);
965 :
966 : }
967 : }
968 : //always request a process task upon a play
969 1207 : gf_filter_post_process_task(read->filter);
970 1207 : read->nb_playing++;
971 : //cancel event unless dash mode
972 1207 : return cancel_event;
973 :
974 285 : case GF_FEVT_STOP:
975 285 : if (read->nb_playing) read->nb_playing--;
976 285 : isor_reset_reader(ch);
977 : //don't send a stop if some of our channels are still waiting for initial play
978 764 : for (i=0; i<gf_list_count(read->channels); i++) {
979 517 : ISOMChannel *a_ch = gf_list_get(read->channels, i);
980 517 : if (ch==a_ch) continue;
981 251 : if (!a_ch->initial_play_seen) return GF_TRUE;
982 : }
983 : //cancel event if nothing playing
984 247 : if (read->nb_playing) return GF_TRUE;
985 215 : read->input_is_stop = GF_TRUE;
986 215 : return GF_FALSE;
987 :
988 214 : case GF_FEVT_SET_SPEED:
989 : case GF_FEVT_RESUME:
990 214 : ch->speed = evt->play.speed;
991 214 : if (ch->sap_only && !evt->play.drop_non_ref) {
992 0 : ch->sap_only = 2;
993 : } else {
994 214 : ch->sap_only = evt->play.drop_non_ref ? GF_TRUE : GF_FALSE;
995 : }
996 : //cancel event
997 : return GF_TRUE;
998 : default:
999 : break;
1000 : }
1001 : //by default don't cancel event
1002 : return GF_FALSE;
1003 : }
1004 :
1005 3981 : static void isoffin_push_buffer(GF_Filter *filter, ISOMReader *read, const u8 *pck_data, u32 data_size)
1006 : {
1007 : u64 bytes_missing;
1008 : GF_Err e;
1009 :
1010 3981 : if (!read->mem_url) {
1011 : char szPath[200];
1012 4 : sprintf(szPath, "gmem://%p", &read->mem_blob);
1013 4 : read->mem_url = gf_strdup(szPath);
1014 : }
1015 3981 : read->mem_blob.data = gf_realloc(read->mem_blob.data, read->mem_blob.size + data_size);
1016 3981 : memcpy(read->mem_blob.data + read->mem_blob.size, pck_data, data_size);
1017 3981 : read->mem_blob.size += data_size;
1018 :
1019 3981 : if (read->mem_load_mode==1) {
1020 : u32 box_type;
1021 8 : e = gf_isom_open_progressive_ex(read->mem_url, 0, 0, GF_FALSE, &read->mov, &bytes_missing, &box_type);
1022 :
1023 8 : if (e && (e != GF_ISOM_INCOMPLETE_FILE)) {
1024 0 : gf_filter_setup_failure(filter, e);
1025 0 : read->mem_load_mode = 0;
1026 0 : read->in_error = e;
1027 0 : return;
1028 : }
1029 8 : if (!read->mov) {
1030 4 : switch (box_type) {
1031 0 : case GF_4CC('m','d','a','t'):
1032 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[IsoMedia] non fragmented ISOBMFF with moof after mdat and no underlying file cache (pipe or other stream input), not supported !\n"));
1033 0 : gf_filter_setup_failure(filter, GF_NOT_SUPPORTED);
1034 0 : read->mem_load_mode = 0;
1035 0 : read->in_error = GF_NOT_SUPPORTED;
1036 0 : break;
1037 4 : default:
1038 4 : read->moov_not_loaded = 1;
1039 4 : break;
1040 : }
1041 : return;
1042 : }
1043 :
1044 4 : read->frag_type = gf_isom_is_fragmented(read->mov) ? 1 : 0;
1045 4 : read->time_scale = gf_isom_get_timescale(read->mov);
1046 4 : isor_declare_objects(read);
1047 4 : read->mem_load_mode = 2;
1048 4 : read->moov_not_loaded = 0;
1049 4 : return;
1050 : }
1051 : //refresh file
1052 3973 : gf_isom_refresh_fragmented(read->mov, &bytes_missing, read->mem_url);
1053 :
1054 3973 : if ((read->mem_load_mode==2) && bytes_missing)
1055 3756 : read->force_fetch = GF_TRUE;
1056 :
1057 : }
1058 :
1059 30768 : static void isoffin_purge_mem(ISOMReader *read, u64 min_offset)
1060 : {
1061 : u32 i, count;
1062 : u64 top_offset;
1063 : u32 nb_bytes_to_purge;
1064 : u64 bytes_missing;
1065 :
1066 : //purge every
1067 30768 : if (read->mstore_purge && (min_offset - read->last_min_offset < read->mstore_purge))
1068 30693 : return;
1069 :
1070 361 : if (read->frag_type) {
1071 : //get position of current box being parsed - if new offset is greater than this box we cannot remove
1072 : //bytes (we would trash the top-level box header)
1073 82 : gf_isom_get_current_top_box_offset(read->mov, &top_offset);
1074 82 : if (top_offset<min_offset) {
1075 : return;
1076 : }
1077 : }
1078 354 : read->last_min_offset = min_offset;
1079 :
1080 : assert(min_offset>=read->bytes_removed);
1081 : //min_offset is given in absolute file position
1082 354 : nb_bytes_to_purge = (u32) (min_offset - read->bytes_removed);
1083 : assert(nb_bytes_to_purge<=read->mem_blob.size);
1084 :
1085 354 : memmove(read->mem_blob.data, read->mem_blob.data+nb_bytes_to_purge, read->mem_blob.size - nb_bytes_to_purge);
1086 354 : read->mem_blob.size -= nb_bytes_to_purge;
1087 354 : read->bytes_removed += nb_bytes_to_purge;
1088 354 : gf_isom_set_removed_bytes(read->mov, read->bytes_removed);
1089 :
1090 354 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[IsoMedia] mem mode %d bytes in mem, "LLU" bytes trashed since start\n", read->mem_blob.size, read->bytes_removed));
1091 :
1092 : //force a refresh
1093 354 : gf_isom_refresh_fragmented(read->mov, &bytes_missing, read->mem_url);
1094 :
1095 354 : if (!read->frag_type)
1096 : return;
1097 :
1098 : //fragmented file, cleanup sample tables
1099 75 : count = gf_list_count(read->channels);
1100 150 : for (i=0; i<count; i++) {
1101 75 : ISOMChannel *ch = gf_list_get(read->channels, i);
1102 : u32 num_samples;
1103 75 : u32 prev_samples = gf_isom_get_sample_count(read->mov, ch->track);
1104 : //don't run this too often
1105 75 : if (ch->sample_num<=1+read->mstore_samples) continue;
1106 :
1107 75 : num_samples = ch->sample_num-1;
1108 75 : if (num_samples>=prev_samples) continue;
1109 :
1110 75 : if (gf_isom_purge_samples(read->mov, ch->track, num_samples) == GF_OK)
1111 75 : ch->sample_num = 1;
1112 :
1113 75 : num_samples = gf_isom_get_sample_count(read->mov, ch->track);
1114 : assert(ch->sample_num<=num_samples);
1115 75 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[IsoMedia] mem mode %d samples now in track %d (prev %d)\n", num_samples, ch->track_id, prev_samples));
1116 : }
1117 : }
1118 :
1119 293308 : static GF_Err isoffin_process(GF_Filter *filter)
1120 : {
1121 293308 : ISOMReader *read = gf_filter_get_udta(filter);
1122 293308 : u32 i, count = gf_list_count(read->channels);
1123 : Bool is_active = GF_FALSE;
1124 : Bool in_is_eos = GF_FALSE;
1125 : Bool check_forced_end = GF_FALSE;
1126 : Bool has_new_data = GF_FALSE;
1127 : u64 min_offset_plus_one = 0;
1128 : u32 nb_forced_end=0;
1129 293308 : if (read->in_error)
1130 : return read->in_error;
1131 :
1132 293308 : if (read->pid) {
1133 : Bool fetch_input = GF_TRUE;
1134 :
1135 : //we failed at loading the init segment during a dash switch, retry
1136 245291 : if (!read->is_partial_download && !read->mem_load_mode && (read->moov_not_loaded==2) ) {
1137 0 : isoffin_configure_pid(filter, read->pid, GF_FALSE);
1138 0 : if (read->moov_not_loaded) return GF_OK;
1139 : }
1140 245291 : if (read->mem_load_mode==2) {
1141 30771 : if (!read->force_fetch && read->mem_blob.size > read->mstore_size) {
1142 : fetch_input = GF_FALSE;
1143 : }
1144 30771 : read->force_fetch = GF_FALSE;
1145 : }
1146 319305 : while (fetch_input) {
1147 308624 : GF_FilterPacket *pck = gf_filter_pid_get_packet(read->pid);
1148 308624 : if (!pck) {
1149 : //we issued a seek, wait for the first packet to be received before fetching channels
1150 : //otherwise we could end up reading from the wrong cache
1151 234610 : if (read->wait_for_source) {
1152 : //something went wrong during the seek request
1153 0 : if (gf_filter_pid_is_eos(read->pid))
1154 : return GF_EOS;
1155 0 : return GF_OK;
1156 : }
1157 : break;
1158 : }
1159 74014 : read->wait_for_source = GF_FALSE;
1160 :
1161 74014 : if (read->mem_load_mode) {
1162 : u32 data_size;
1163 3981 : const u8 *pck_data = gf_filter_pck_get_data(pck, &data_size);
1164 3981 : isoffin_push_buffer(filter, read, pck_data, data_size);
1165 : }
1166 : //we just had a switch but init seg is not completely done: input packet is only a part of the init, drop it
1167 70033 : else if (read->moov_not_loaded==2) {
1168 0 : gf_filter_pid_drop_packet(read->pid);
1169 0 : return GF_OK;
1170 : }
1171 74014 : gf_filter_pid_drop_packet(read->pid);
1172 : has_new_data = GF_TRUE;
1173 74014 : if (read->in_error)
1174 : return read->in_error;
1175 : }
1176 245291 : if (gf_filter_pid_is_eos(read->pid)) {
1177 132820 : read->input_loaded = GF_TRUE;
1178 : in_is_eos = GF_TRUE;
1179 : }
1180 245291 : if (read->input_is_stop) {
1181 121 : read->input_loaded = GF_TRUE;
1182 : in_is_eos = GF_TRUE;
1183 121 : read->input_is_stop = GF_FALSE;
1184 : }
1185 245291 : if (!read->frag_type && read->input_loaded) {
1186 : in_is_eos = GF_TRUE;
1187 : }
1188 : //segment is invalid, wait for eos on input an send eos on all channels
1189 245291 : if (read->invalid_segment) {
1190 0 : if (!in_is_eos) return GF_OK;
1191 0 : read->invalid_segment = GF_FALSE;
1192 :
1193 0 : for (i=0; i<count; i++) {
1194 0 : ISOMChannel *ch = gf_list_get(read->channels, i);
1195 0 : if (!ch->playing) {
1196 0 : continue;
1197 : }
1198 0 : if (!ch->eos_sent) {
1199 0 : ch->eos_sent = GF_TRUE;
1200 0 : gf_filter_pid_set_eos(ch->pid);
1201 : }
1202 : }
1203 0 : read->eos_signaled = GF_TRUE;
1204 0 : return GF_EOS;
1205 : }
1206 48017 : } else if (read->extern_mov) {
1207 : in_is_eos = GF_TRUE;
1208 48017 : read->input_loaded = GF_TRUE;
1209 : }
1210 293308 : if (read->moov_not_loaded==1) {
1211 29 : if (read->mem_load_mode)
1212 : return GF_OK;
1213 25 : read->moov_not_loaded = GF_FALSE;
1214 25 : return isoffin_setup(filter, read);
1215 : }
1216 :
1217 293279 : if (read->refresh_fragmented) {
1218 : const GF_PropertyValue *prop;
1219 :
1220 3959 : if (in_is_eos) {
1221 1251 : read->refresh_fragmented = GF_FALSE;
1222 : } else {
1223 2708 : prop = gf_filter_pid_get_property(read->pid, GF_PROP_PID_FILE_CACHED);
1224 2708 : if (prop && prop->value.boolean)
1225 1595 : read->refresh_fragmented = GF_FALSE;
1226 : }
1227 :
1228 3959 : if (has_new_data) {
1229 3366 : u64 bytesMissing=0;
1230 : GF_Err e;
1231 : const char *new_url = NULL;
1232 3366 : prop = gf_filter_pid_get_property(read->pid, GF_PROP_PID_FILEPATH);
1233 3366 : if (prop) new_url = prop->value.string;
1234 :
1235 3366 : e = gf_isom_refresh_fragmented(read->mov, &bytesMissing, new_url);
1236 :
1237 3366 : if (e && (e!= GF_ISOM_INCOMPLETE_FILE)) {
1238 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[IsoMedia] Failed to refresh current segment: %s\n", gf_error_to_string(e) ));
1239 0 : read->refresh_fragmented = GF_FALSE;
1240 : } else {
1241 3366 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Refreshing current segment at UTC "LLU" - "LLU" bytes still missing - input is EOS %d\n", gf_net_get_utc(), bytesMissing, in_is_eos));
1242 : }
1243 :
1244 3366 : if (!read->refresh_fragmented && (e==GF_ISOM_INCOMPLETE_FILE)) {
1245 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[IsoMedia] Incomplete Segment received - "LLU" bytes missing but EOF found\n", bytesMissing ));
1246 : }
1247 :
1248 : #ifndef GPAC_DISABLE_LOG
1249 3366 : if (gf_log_tool_level_on(GF_LOG_DASH, GF_LOG_DEBUG)) {
1250 71 : for (i=0; i<count; i++) {
1251 71 : ISOMChannel *ch = gf_list_get(read->channels, i);
1252 71 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] refresh track %d fragment - cur sample %d - new sample count %d\n", ch->track, ch->sample_num, gf_isom_get_sample_count(ch->owner->mov, ch->track) ));
1253 : }
1254 : }
1255 : #endif
1256 3366 : isor_check_producer_ref_time(read);
1257 3366 : if (!read->frag_type)
1258 0 : read->refresh_fragmented = GF_FALSE;
1259 : }
1260 : }
1261 :
1262 503094 : for (i=0; i<count; i++) {
1263 : u8 *data;
1264 : u32 nb_pck=50;
1265 : ISOMChannel *ch;
1266 503094 : ch = gf_list_get(read->channels, i);
1267 503094 : if (!ch->playing) {
1268 5553 : nb_forced_end++;
1269 5553 : continue;
1270 : }
1271 : //eos not sent on this channel, we are active
1272 497541 : if (!ch->eos_sent)
1273 : is_active = GF_TRUE;
1274 :
1275 894459 : while (nb_pck) {
1276 893993 : ch->sample_data_offset = 0;
1277 893993 : if (!read->full_segment_flush && gf_filter_pid_would_block(ch->pid) )
1278 : break;
1279 :
1280 642432 : if (ch->item_id) {
1281 118 : isor_reader_get_sample_from_item(ch);
1282 : } else {
1283 642314 : isor_reader_get_sample(ch);
1284 : }
1285 :
1286 642432 : if (read->stsd && (ch->last_sample_desc_index != read->stsd) && ch->sample) {
1287 0 : isor_reader_release_sample(ch);
1288 0 : continue;
1289 : }
1290 642432 : if (ch->sample) {
1291 : u32 sample_dur;
1292 : u8 dep_flags;
1293 : u8 *subs_buf;
1294 : u32 subs_buf_size;
1295 : GF_FilterPacket *pck;
1296 396918 : if (ch->needs_pid_reconfig) {
1297 5 : isor_update_channel_config(ch);
1298 5 : ch->needs_pid_reconfig = GF_FALSE;
1299 : }
1300 :
1301 : //we have at least two samples, update GF_PROP_PID_HAS_SYNC if needed
1302 396918 : if (ch->check_has_rap && (gf_isom_get_sample_count(ch->owner->mov, ch->track)>1) && (gf_isom_has_sync_points(ch->owner->mov, ch->track)==1)) {
1303 187 : ch->check_has_rap = GF_FALSE;
1304 187 : ch->has_rap = GF_TRUE;
1305 187 : gf_filter_pid_set_property(ch->pid, GF_PROP_PID_HAS_SYNC, &PROP_BOOL(ch->has_rap) );
1306 : }
1307 :
1308 : //strip param sets from payload, trigger reconfig if needed
1309 396918 : isor_reader_check_config(ch);
1310 :
1311 396918 : if (read->nodata) {
1312 0 : pck = gf_filter_pck_new_shared(ch->pid, NULL, ch->sample->dataLength, NULL);
1313 0 : if (!pck) return GF_OUT_OF_MEM;
1314 : } else {
1315 396918 : pck = gf_filter_pck_new_alloc(ch->pid, ch->sample->dataLength, &data);
1316 396918 : if (!pck) return GF_OUT_OF_MEM;
1317 :
1318 396918 : memcpy(data, ch->sample->data, ch->sample->dataLength);
1319 : }
1320 396918 : gf_filter_pck_set_dts(pck, ch->dts);
1321 396918 : gf_filter_pck_set_cts(pck, ch->cts);
1322 396918 : if (ch->sample->IsRAP==-1) {
1323 0 : gf_filter_pck_set_sap(pck, GF_FILTER_SAP_1);
1324 0 : ch->redundant = 1;
1325 : } else {
1326 396918 : gf_filter_pck_set_sap(pck, (GF_FilterSAPType) ch->sample->IsRAP);
1327 : }
1328 :
1329 396918 : if (ch->sap_3)
1330 355 : gf_filter_pck_set_sap(pck, GF_FILTER_SAP_3);
1331 396563 : else if (ch->sap_4_type) {
1332 93 : gf_filter_pck_set_sap(pck, (ch->sap_4_type==GF_ISOM_SAMPLE_PREROLL) ? GF_FILTER_SAP_4_PROL : GF_FILTER_SAP_4);
1333 93 : gf_filter_pck_set_roll_info(pck, ch->roll);
1334 : }
1335 :
1336 396918 : sample_dur = ch->au_duration;
1337 396918 : if (ch->sample->nb_pack)
1338 482 : sample_dur *= ch->sample->nb_pack;
1339 396918 : gf_filter_pck_set_duration(pck, sample_dur);
1340 396918 : gf_filter_pck_set_seek_flag(pck, ch->seek_flag);
1341 :
1342 396918 : dep_flags = ch->isLeading;
1343 396918 : dep_flags <<= 2;
1344 396918 : dep_flags |= ch->dependsOn;
1345 396918 : dep_flags <<= 2;
1346 396918 : dep_flags |= ch->dependedOn;
1347 396918 : dep_flags <<= 2;
1348 396918 : dep_flags |= ch->redundant;
1349 :
1350 396918 : if (dep_flags)
1351 26972 : gf_filter_pck_set_dependency_flags(pck, dep_flags);
1352 :
1353 396918 : gf_filter_pck_set_crypt_flags(pck, ch->pck_encrypted ? GF_FILTER_PCK_CRYPT : 0);
1354 396918 : gf_filter_pck_set_seq_num(pck, ch->sample_num);
1355 :
1356 :
1357 396918 : subs_buf = gf_isom_sample_get_subsamples_buffer(read->mov, ch->track, ch->sample_num, &subs_buf_size);
1358 396918 : if (subs_buf) {
1359 768 : gf_filter_pck_set_property(pck, GF_PROP_PCK_SUBS, &PROP_DATA_NO_COPY(subs_buf, subs_buf_size) );
1360 : }
1361 :
1362 396918 : if (ch->sai_buffer && ch->pck_encrypted) {
1363 : assert(ch->sai_buffer_size);
1364 33013 : gf_filter_pck_set_property(pck, GF_PROP_PCK_CENC_SAI, &PROP_DATA(ch->sai_buffer, ch->sai_buffer_size) );
1365 : }
1366 :
1367 396918 : if (read->sigfrag) {
1368 : GF_ISOFragmentBoundaryInfo finfo;
1369 7323 : if (gf_isom_sample_is_fragment_start(read->mov, ch->track, ch->sample_num, &finfo) ) {
1370 : u64 start=0;
1371 195 : u32 traf_start = finfo.seg_start_plus_one ? 2 : 1;
1372 :
1373 195 : if (finfo.seg_start_plus_one)
1374 87 : gf_filter_pck_set_property(pck, GF_PROP_PCK_CUE_START, &PROP_BOOL(GF_TRUE));
1375 :
1376 195 : gf_filter_pck_set_property(pck, GF_PROP_PCK_FRAG_START, &PROP_UINT(traf_start));
1377 :
1378 195 : start = finfo.frag_start;
1379 195 : if (finfo.seg_start_plus_one) start = finfo.seg_start_plus_one-1;
1380 195 : gf_filter_pck_set_property(pck, GF_PROP_PCK_FRAG_RANGE, &PROP_FRAC64_INT(start, finfo.mdat_end));
1381 195 : if (finfo.moof_template) {
1382 87 : gf_filter_pck_set_property(pck, GF_PROP_PCK_MOOF_TEMPLATE, &PROP_DATA((u8 *)finfo.moof_template, finfo.moof_template_size));
1383 : }
1384 195 : if (finfo.sidx_end) {
1385 87 : gf_filter_pck_set_property(pck, GF_PROP_PCK_SIDX_RANGE, &PROP_FRAC64_INT(finfo.sidx_start , finfo.sidx_end));
1386 : }
1387 :
1388 195 : if (read->seg_name_changed) {
1389 81 : const GF_PropertyValue *p = gf_filter_pid_get_property(read->pid, GF_PROP_PID_URL);
1390 81 : read->seg_name_changed = GF_FALSE;
1391 81 : if (p && p->value.string) {
1392 81 : gf_filter_pck_set_property(pck, GF_PROP_PID_URL, &PROP_STRING(p->value.string));
1393 : }
1394 : }
1395 : }
1396 : }
1397 396918 : if (ch->sender_ntp) {
1398 0 : gf_filter_pck_set_property(pck, GF_PROP_PCK_SENDER_NTP, &PROP_LONGUINT(ch->sender_ntp));
1399 0 : if (ch->ntp_at_server_ntp) {
1400 0 : gf_filter_pck_set_property(pck, GF_PROP_PCK_RECEIVER_NTP, &PROP_LONGUINT(ch->ntp_at_server_ntp));
1401 : }
1402 : }
1403 396918 : ch->eos_sent = GF_FALSE;
1404 396918 : gf_filter_pck_send(pck);
1405 396918 : isor_reader_release_sample(ch);
1406 :
1407 396918 : ch->last_valid_sample_data_offset = ch->sample_data_offset;
1408 396918 : nb_pck--;
1409 245514 : } else if (ch->last_state==GF_EOS) {
1410 243981 : if (ch->playing == 2) {
1411 0 : if (in_is_eos) {
1412 0 : ch->playing = GF_FALSE;
1413 : } else {
1414 0 : nb_forced_end++;
1415 : check_forced_end = GF_TRUE;
1416 : }
1417 : }
1418 243981 : if (in_is_eos && !ch->eos_sent) {
1419 : void *tfrf;
1420 : const void *gf_isom_get_tfrf(GF_ISOFile *movie, u32 trackNumber);
1421 :
1422 18669 : ch->eos_sent = GF_TRUE;
1423 18669 : read->eos_signaled = GF_TRUE;
1424 :
1425 18669 : tfrf = (void *) gf_isom_get_tfrf(read->mov, ch->track);
1426 18669 : if (tfrf) {
1427 0 : gf_filter_pid_set_info_str(ch->pid, "smooth_tfrf", &PROP_POINTER(tfrf) );
1428 : } else {
1429 18669 : gf_filter_pid_set_info_str(ch->pid, "smooth_tfrf", NULL );
1430 : }
1431 :
1432 18669 : gf_filter_pid_set_eos(ch->pid);
1433 : }
1434 : break;
1435 : } else {
1436 1533 : read->force_fetch = GF_TRUE;
1437 1533 : break;
1438 : }
1439 : }
1440 497541 : if (!min_offset_plus_one || (min_offset_plus_one - 1 > ch->last_valid_sample_data_offset))
1441 300245 : min_offset_plus_one = 1 + ch->last_valid_sample_data_offset;
1442 : }
1443 293279 : if (read->mem_load_mode && min_offset_plus_one) {
1444 30768 : isoffin_purge_mem(read, min_offset_plus_one-1);
1445 : }
1446 :
1447 : //we reached end of playback due to play range request, we must send eos - however for safety reason with DASH, we first need to cancel the input
1448 293279 : if (read->pid && check_forced_end && (nb_forced_end==count)) {
1449 : //abort input
1450 : GF_FilterEvent evt;
1451 0 : GF_FEVT_INIT(evt, GF_FEVT_STOP, read->pid);
1452 0 : gf_filter_pid_send_event(read->pid, &evt);
1453 : }
1454 :
1455 :
1456 293279 : if (!is_active) {
1457 : return GF_EOS;
1458 : }
1459 : //if (in_is_eos)
1460 : // gf_filter_ask_rt_reschedule(filter, 1);
1461 279774 : return GF_OK;
1462 :
1463 : }
1464 :
1465 3074 : static const char *isoffin_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
1466 : {
1467 3074 : if (gf_isom_probe_data(data, size)) {
1468 715 : *score = GF_FPROBE_SUPPORTED;
1469 715 : return "video/mp4";
1470 : }
1471 : return NULL;
1472 : }
1473 :
1474 : #define OFFS(_n) #_n, offsetof(ISOMReader, _n)
1475 :
1476 : static const GF_FilterArgs ISOFFInArgs[] =
1477 : {
1478 : { OFFS(src), "location of source content (only used when explicitly loading the demuxer)", GF_PROP_NAME, NULL, NULL, 0},
1479 : { OFFS(allt), "load all tracks even if unknown", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1480 : { OFFS(noedit), "do not use edit lists", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1481 : { OFFS(itt), "convert all items of root meta into a single PID", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1482 : { OFFS(itemid), "keep item IDs in PID properties", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
1483 : { OFFS(smode), "load mode for scalable/tile tracks\n"
1484 : "- split: each track is declared, extractors are removed\n"
1485 : "- splitx: each track is declared, extractors are kept\n"
1486 : "- single: a single track is declared (highest level for scalable, tile base for tiling)", GF_PROP_UINT, "split", "split|splitx|single", GF_FS_ARG_HINT_ADVANCED},
1487 : { OFFS(alltk), "declare all tracks even disabled ones", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1488 : { OFFS(frame_size), "frame size for raw audio samples (dispatches frame_size samples per packet)", GF_PROP_UINT, "1024", NULL, GF_FS_ARG_HINT_ADVANCED},
1489 : { OFFS(expart), "expose cover art as a dedicated video pid", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1490 : { OFFS(sigfrag), "signal fragment and segment boundaries of source on output packets", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1491 :
1492 : { OFFS(tkid), "declare only track based on given param\n"
1493 : "- integer value: declares track with the given ID\n"
1494 : "- audio: declares first audio track\n"
1495 : "- video: declares first video track\n"
1496 : "- 4CC: declares first track with matching 4CC for handler type", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
1497 : { OFFS(stsd), "only extract sample mapped to the given sample description index. 0 means no filter", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_EXPERT},
1498 : { OFFS(mov), "pointer to a read/edit ISOBMF file used internally by importers and exporters", GF_PROP_POINTER, NULL, NULL, GF_FS_ARG_HINT_HIDE},
1499 : { OFFS(analyze), "skip reformat of decoder config and SEI and dispatch all NAL in input order - shall only be used with inspect filter analyze mode!", GF_PROP_UINT, "off", "off|on|bs|full", GF_FS_ARG_HINT_HIDE},
1500 : { OFFS(catseg), "append the given segment to the movie at init time (only local file supported)", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_HIDE},
1501 : { OFFS(nocrypt), "signal encrypted tracks as non encrypted (mostly used for export)", GF_PROP_BOOL, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
1502 : { OFFS(mstore_size), "target buffer size in bytes", GF_PROP_UINT, "1000000", NULL, GF_FS_ARG_HINT_EXPERT},
1503 : { OFFS(mstore_purge), "minimum size in bytes between memory purges when reading from memory stream (pipe etc...), 0 means purge as soon as possible", GF_PROP_UINT, "50000", NULL, GF_FS_ARG_HINT_EXPERT},
1504 : { OFFS(mstore_samples), "minimum number of samples to be present before purging sample tables when reading from memory stream (pipe etc...), 0 means purge as soon as possible", GF_PROP_UINT, "50", NULL, GF_FS_ARG_HINT_EXPERT},
1505 : { OFFS(strtxt), "load text tracks (apple/tx3g) as MPEG-4 streaming text tracks", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
1506 : { OFFS(xps_check), "parameter sets extraction mode from AVC/HEVC/VVC samples\n"
1507 : "- keep: do not inspect sample (assumes input file is compliant when generating DASH/HLS/CMAF)\n"
1508 : "- rem: removes all inband xPS and notify configuration changes accordingly\n"
1509 : "- auto: resolves to `keep` for `smode=splix` (dasher mode), `rem` otherwise"
1510 : , GF_PROP_UINT, "auto", "auto|keep|rem", GF_FS_ARG_HINT_EXPERT},
1511 : { OFFS(nodata), "do not load sample data", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
1512 : {0}
1513 : };
1514 :
1515 : static const GF_FilterCapability ISOFFInCaps[] =
1516 : {
1517 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
1518 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "mp4|mpg4|m4a|m4i|3gp|3gpp|3g2|3gp2|iso|m4s|heif|heic|avci|mj2|mov|qt"),
1519 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "application/x-isomedia|application/mp4|video/mp4|audio/mp4|video/3gpp|audio/3gpp|video/3gp2|audio/3gp2|video/iso.segment|audio/iso.segment|image/heif|image/heic|image/avci|video/quicktime"),
1520 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
1521 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
1522 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_SCENE),
1523 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_OD),
1524 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_TEXT),
1525 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_METADATA),
1526 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_ENCRYPTED),
1527 :
1528 : CAP_BOOL(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
1529 : //we don't set output cap for streamtype FILE for now.
1530 : };
1531 :
1532 : GF_FilterRegister ISOFFInRegister = {
1533 : .name = "mp4dmx",
1534 : GF_FS_SET_DESCRIPTION("ISOBMFF/QT demuxer")
1535 : GF_FS_SET_HELP("This filter demultiplexes ISOBMF and QT files (regular or fragmented).\n"
1536 : "# Track Selection\n"
1537 : "The filter can use fragment identifiers of source to select a single track for playback. The allowed fragments are:\n"
1538 : " - #audio: only use the first audio track\n"
1539 : " - #video: only use the first video track\n"
1540 : " - #auxv: only use the first auxiliary video track\n"
1541 : " - #pict: only use the first picture track\n"
1542 : " - #text: only use the first text track\n"
1543 : " - #trackID=VAL: only use the track with given ID\n"
1544 : " - #ID=VAL: only use the track with given ID\n"
1545 : " - #VAL: only use the track with given ID\n"
1546 : "\n"
1547 : "# Scalable Tracks\n"
1548 : "When scalable tracks are present in a file, the reader can operate in 3 modes using [-smode]() option:\n"\
1549 : "- smode=single: resolves all extractors to extract a single bitstream from a scalable set. The highest level is used\n"\
1550 : "In this mode, there is no enhancement decoder config, only a base one resulting from the merge of the configs\n"\
1551 : "- smode=split: all extractors are removed and every track of the scalable set is declared. In this mode, each enhancement track has no base decoder config\n"
1552 : "and an enhancement decoder config.\n"\
1553 : "- smode=splitx: extractors are kept in the bitstream, and every track of the scalable set is declared. In this mode, each enhancement track has a base decoder config\n"
1554 : " (copied from base) and an enhancement decoder config. This is mostly used for DASHing content.\n"\
1555 : "Warning: smode=splitx will result in extractor NAL units still present in the output bitstream, which shall only be true if the output is ISOBMFF based\n")
1556 : .private_size = sizeof(ISOMReader),
1557 : .args = ISOFFInArgs,
1558 : .initialize = isoffin_initialize,
1559 : .finalize = isoffin_finalize,
1560 : .process = isoffin_process,
1561 : .configure_pid = isoffin_configure_pid,
1562 : SETCAPS(ISOFFInCaps),
1563 : .process_event = isoffin_process_event,
1564 : .probe_data = isoffin_probe_data
1565 : };
1566 :
1567 :
1568 : #endif /*GPAC_DISABLE_ISOM*/
1569 :
1570 2877 : const GF_FilterRegister *isoffin_register(GF_FilterSession *session)
1571 : {
1572 : #ifdef GPAC_DISABLE_ISOM
1573 : return NULL;
1574 : #else
1575 2877 : return &ISOFFInRegister;
1576 : #endif /*GPAC_DISABLE_ISOM*/
1577 : }
1578 :
|