Exemplo n.º 1
0
void RP_ConfirmChannelConnect(RTPStream *ch, GF_Err e)
{
	GF_NetworkCommand com;

	/*in case the channel has been disconnected while SETUP was issued&processed. We also could
	clean up the command stack*/
	if (!ch->channel) return;

	gf_service_connect_ack(ch->owner->service, ch->channel, e);
	if (e != GF_OK || !ch->rtp_ch) return;

	/*success, overwrite SL config*/
	memset(&com, 0, sizeof(GF_NetworkCommand));
	com.command_type =	GF_NET_CHAN_RECONFIG;
	com.base.on_channel = ch->channel;

	gf_rtp_depacketizer_get_slconfig(ch->depacketizer, &com.cfg.sl_config);
	/*reconfig*/
	gf_service_command(ch->owner->service, &com, GF_OK);

	/*ISMACryp config*/
	if (ch->depacketizer->flags & GF_RTP_HAS_ISMACRYP) {
		memset(&com, 0, sizeof(GF_NetworkCommand));
		com.base.on_channel = ch->channel;
		com.command_type = GF_NET_CHAN_DRM_CFG;
		com.drm_cfg.scheme_type = ch->depacketizer->isma_scheme;
		com.drm_cfg.scheme_version = 1;
		/*not transported in SDP!!!*/
		com.drm_cfg.scheme_uri = NULL;
		com.drm_cfg.kms_uri = ch->depacketizer->key;
		gf_service_command(ch->owner->service, &com, GF_OK);
	}
}
Exemplo n.º 2
0
void isor_send_cenc_config(ISOMChannel *ch)
{
	GF_NetworkCommand com;
	u32 i;

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

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

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

	/*fill PSSH in the structure. We will free it in CENC_Setup*/
	for (i=0; i<com.drm_cfg.PSSH_count; i++) {
		GF_NetComDRMConfigPSSH *pssh = &com.drm_cfg.PSSHs[i];
		gf_isom_get_pssh_info(ch->owner->mov, i+1, pssh->SystemID, &pssh->KID_count, (const bin128 **) & pssh->KIDs, (const u8 **) &pssh->private_data, &pssh->private_data_size);
	}
	//fixme - check MSE and EME
#if 0
	if (read->input->query_proxy && read->input->proxy_udta) {
		read->input->query_proxy(read->input, &com);
	} else
#endif
		gf_service_command(ch->owner->service, &com, GF_OK);
	//free our PSSH
	if (com.drm_cfg.PSSHs) gf_free(com.drm_cfg.PSSHs);
}
Exemplo n.º 3
0
void RP_SendMessage(GF_ClientService *service, GF_Err e, const char *message)
{
	GF_NetworkCommand com;
	memset(&com, 0, sizeof(com));
	com.command_type = GF_NET_SERVICE_EVENT;
	com.send_event.evt.type = GF_EVENT_MESSAGE;
	com.send_event.evt.message.message = message;
	com.send_event.evt.message.error = e;
	gf_service_command(service, &com, GF_OK);
}
Exemplo n.º 4
0
u32 RP_Thread(void *param)
{
    u32 i;
    GF_NetworkCommand com;
    RTSPSession *sess;
    RTPStream *ch;
    RTPClient *rtp = (RTPClient *)param;

    rtp->th_state = 1;
    com.command_type = GF_NET_CHAN_BUFFER_QUERY;
    while (rtp->th_state) {
        gf_mx_p(rtp->mx);

        /*fecth data on udp*/
        i=0;
        while ((ch = (RTPStream *)gf_list_enum(rtp->channels, &i))) {
            if ((ch->flags & RTP_EOS) || (ch->status!=RTP_Running) ) continue;
            /*for interleaved channels don't read too fast, query the buffer occupancy*/
            if (ch->flags & RTP_INTERLEAVED) {
                com.base.on_channel = ch->channel;
                gf_service_command(rtp->service, &com, GF_OK);
                /*if no buffering, use a default value (3 sec of data should do it)*/
                if (!com.buffer.max) com.buffer.max = 3000;
                if (com.buffer.occupancy <= com.buffer.max) ch->rtsp->flags |= RTSP_TCP_FLUSH;
            } else {
                RP_ReadStream(ch);
            }
        }

        /*and process commands / flush TCP*/
        i=0;
        while ((sess = (RTSPSession *)gf_list_enum(rtp->sessions, &i))) {
            RP_ProcessCommands(sess);

            if (sess->connect_error) {
                gf_service_connect_ack(sess->owner->service, NULL, sess->connect_error);
                sess->connect_error = 0;
            }

        }

        gf_mx_v(rtp->mx);

        gf_sleep(1);
    }

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

    rtp->th_state = 2;
    return 0;
}
Exemplo n.º 5
0
static void AC3_RegulateDataRate(AC3Reader *read)
{
	GF_NetworkCommand com;

	memset(&com, 0, sizeof(GF_NetworkCommand));
	com.command_type = GF_NET_CHAN_BUFFER_QUERY;
	com.base.on_channel = read->ch;
	while (read->ch) {
		gf_service_command(read->service, &com, GF_OK);
		if (com.buffer.occupancy < com.buffer.max) break;
		gf_sleep(2);
	}
}
Exemplo n.º 6
0
Arquivo: saf_in.c Projeto: Bevara/GPAC
static void SAF_Regulate(SAFIn *read)
{
	GF_NetworkCommand com;
	SAFChannel *ch;

	com.command_type = GF_NET_CHAN_BUFFER_QUERY;
	/*sleep untill the buffer occupancy is too low - note that this work because all streams in this
	demuxer are synchronized*/
	while (read->run_state) {
		u32 min_occ = (u32) -1;
		u32 i=0;
		while ( (ch = (SAFChannel *)gf_list_enum(read->channels, &i))) {
			com.base.on_channel = ch->ch;
			gf_service_command(read->service, &com, GF_OK);
			if (com.buffer.occupancy < ch->buffer_min) return;
			if (com.buffer.occupancy) min_occ = MIN(min_occ, com.buffer.occupancy - ch->buffer_min);
		}
		if (min_occ == (u32) -1) break;
		GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[SAF] Regulating SAF demux - sleeping for %d ms\n", min_occ));
		gf_sleep(min_occ);
	}
}
Exemplo n.º 7
0
static GF_Err MPD_ClientQuery(GF_InputService *ifce, GF_NetworkCommand *param)
{
	u32 i;
	GF_Err e;
	GF_MPD_In *mpdin = (GF_MPD_In *) ifce->proxy_udta;
	if (!param || !ifce || !ifce->proxy_udta) return GF_BAD_PARAM;

	mpdin->in_seek = 0;

	/*gets byte range of init segment (for local validation)*/
	if (param->command_type==GF_NET_SERVICE_QUERY_INIT_RANGE) {
		param->url_query.next_url = NULL;
		param->url_query.start_range = 0;
		param->url_query.end_range = 0;

		for (i=0; i<gf_dash_get_group_count(mpdin->dash); i++) {
			GF_MPDGroup *group;
			if (!gf_dash_is_group_selectable(mpdin->dash, i)) continue;
			group = gf_dash_get_group_udta(mpdin->dash, i);
			if (group->segment_ifce == ifce) {
				gf_dash_group_get_segment_init_url(mpdin->dash, i, &param->url_query.start_range, &param->url_query.end_range);
				param->url_query.current_download = 0;
				return GF_OK;
			}
		}
		return GF_SERVICE_ERROR;
	}

	/*gets URL and byte range of next segment - if needed, adds bitstream switching segment info*/
	if (param->command_type==GF_NET_SERVICE_QUERY_NEXT) {
		Bool group_done;
		u32 nb_segments_cached;
		u32 group_idx=0;
		GF_MPDGroup *group=NULL;
		const char *src_url;
		Bool discard_first_cache_entry = param->url_query.drop_first_segment;
		Bool check_current_download = param->url_query.current_download;
		u32 timer = gf_sys_clock();
		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MPD_IN] Received Service Query Next request from input service %s\n", ifce->module_name));


		param->url_query.current_download = 0;
		param->url_query.discontinuity_type = 0;

		for (i=0; i<gf_dash_get_group_count(mpdin->dash); i++) {
			if (!gf_dash_is_group_selected(mpdin->dash, i)) continue;
			group = gf_dash_get_group_udta(mpdin->dash, i);
			if (group->segment_ifce == ifce) {
				group_idx = i;
				break;
			}
			group=NULL;
		}

		if (!group) {
			return GF_SERVICE_ERROR;
		}

		if (group->in_seek) {
			group->in_seek = 0;
			param->url_query.discontinuity_type = 2;
			discard_first_cache_entry = 0;
		}

		//update group idx
		if (group->idx != group_idx) {
			group->idx = group_idx;
			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MPD_IN] New AdaptationSet detected after MPD update ?\n"));
		}

		if (discard_first_cache_entry) {
			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MPD_IN] Discarding first segment in cache\n"));
			gf_dash_group_discard_segment(mpdin->dash, group_idx);
		}

		while (gf_dash_is_running(mpdin->dash) ) {
			group_done=0;
			nb_segments_cached = gf_dash_group_get_num_segments_ready(mpdin->dash, group_idx, &group_done);
			if (nb_segments_cached>=1)
				break;

			if (group_done) {
				if (!gf_dash_get_period_switch_status(mpdin->dash) && !gf_dash_in_last_period(mpdin->dash) ) {
					GF_NetworkCommand com;
					param->url_query.in_end_of_period = 1;
					memset(&com, 0, sizeof(GF_NetworkCommand));
					com.command_type = GF_NET_BUFFER_QUERY;
					if (gf_dash_get_period_switch_status(mpdin->dash) != 1) {
						gf_service_command(mpdin->service, &com, GF_OK);
						//we only switch period once no more data is in our buffers
						if (!com.buffer.occupancy) {
							param->url_query.in_end_of_period = 0;
							gf_dash_request_period_switch(mpdin->dash);
						}
					}
					if (param->url_query.in_end_of_period)
						return GF_BUFFER_TOO_SMALL;
				} else {
					return GF_EOS;
				}
			}

			if (check_current_download && mpdin->use_low_latency) {
				Bool is_switched=GF_FALSE;
				gf_dash_group_probe_current_download_segment_location(mpdin->dash, group_idx, &param->url_query.next_url, NULL, &param->url_query.next_url_init_or_switch_segment, &src_url, &is_switched);

				if (param->url_query.next_url) {
					param->url_query.current_download = 1;
					param->url_query.has_new_data = group->has_new_data;
					param->url_query.discontinuity_type = is_switched ? 1 : 0;
					if (gf_dash_group_loop_detected(mpdin->dash, group_idx))
						param->url_query.discontinuity_type = 2;
					group->has_new_data = 0;
					return GF_OK;
				}
				return GF_BUFFER_TOO_SMALL;
			}
			return GF_BUFFER_TOO_SMALL;
		}

		param->url_query.current_download = 0;
		nb_segments_cached = gf_dash_group_get_num_segments_ready(mpdin->dash, group_idx, &group_done);
		if (nb_segments_cached < 1) {
			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[MPD_IN] No more file in cache, EOS\n"));
			return GF_EOS;
		} else {
			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MPD_IN] Had to wait for %u ms for the only cache file to be downloaded\n", (gf_sys_clock() - timer)));
		}

		e = gf_dash_group_get_next_segment_location(mpdin->dash, group_idx, param->url_query.dependent_representation_index, &param->url_query.next_url, &param->url_query.start_range, &param->url_query.end_range,
		        NULL, &param->url_query.next_url_init_or_switch_segment, &param->url_query.switch_start_range , &param->url_query.switch_end_range,
		        &src_url, &param->url_query.has_next);
		if (e)
			return e;

		if (gf_dash_group_loop_detected(mpdin->dash, group_idx))
			param->url_query.discontinuity_type = 2;


#ifndef GPAC_DISABLE_LOG
		{
			u32 timer2 = gf_sys_clock() - timer ;
			if (timer2 > 1000) {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MPD_IN] Waiting for download to end took a long time : %u ms\n", timer2));
			}
			if (param->url_query.end_range) {
				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[MPD_IN] Next Segment is %s bytes "LLD"-"LLD"\n", src_url, param->url_query.start_range, param->url_query.end_range));
			} else {
				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[MPD_IN] Next Segment is %s\n", src_url));
			}
			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MPD_IN] Waited %d ms - Elements in cache: %u/%u\n\tCache file name %s\n\tsegment start time %g sec\n", timer2, gf_dash_group_get_num_segments_ready(mpdin->dash, group_idx, &group_done), gf_dash_group_get_max_segments_in_cache(mpdin->dash, group_idx), param->url_query.next_url, gf_dash_group_current_segment_start_time(mpdin->dash, group_idx)  ));
		}
#endif
	}


	return GF_OK;
}
Exemplo n.º 8
0
void mpdin_data_packet(GF_ClientService *service, LPNETCHANNEL ns, char *data, u32 data_size, GF_SLHeader *hdr, GF_Err reception_status)
{
	s32 i;
	GF_MPD_In *mpdin = (GF_MPD_In*) service->ifce->priv;
	GF_Channel *ch;
	GF_MPDGroup *group;
	Bool do_map_time = 0;
	if (!ns || !hdr) {
		mpdin->fn_data_packet(service, ns, data, data_size, hdr, reception_status);
		return;
	}

	ch = (GF_Channel *) ns;
	assert(ch->odm && ch->odm->OD);

	i = gf_dash_get_group_idx_from_service(mpdin,  (GF_InputService *) ch->odm->OD->service_ifce);
	if (i<0) {
		mpdin->fn_data_packet(service, ns, data, data_size, hdr, reception_status);
		return;
	}

	group = gf_dash_get_group_udta(mpdin->dash, i);
	//if sync is based on timestamps do not adjust the timestamps back
	if (! group->is_timestamp_based) {
		if (!group->pto_setup) {
			Double scale;
			s64 start, dur;
			u64 pto;
			gf_dash_group_get_presentation_time_offset(mpdin->dash, i, &pto, &group->timescale);
			group->pto = (s64) pto;
			group->pto_setup = 1;

			if (group->timescale && (group->timescale != ch->esd->slConfig->timestampResolution)) {
				group->pto *= ch->esd->slConfig->timestampResolution;
				group->pto /= group->timescale;
			}
			scale = ch->esd->slConfig->timestampResolution;
			scale /= 1000;
			dur = (u64) (scale * gf_dash_get_period_duration(mpdin->dash) );
			if (dur) {
				group->max_cts_in_period = group->pto + dur;
			} else {
				group->max_cts_in_period = 0;
			}

			start = (u64) (scale * gf_dash_get_period_start(mpdin->dash) );
			group->pto -= start;
		}
		//filter any packet outside the current period
		if (group->max_cts_in_period && (s64) hdr->compositionTimeStamp > group->max_cts_in_period) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Packet timestamp "LLU" larger than max CTS in period "LLU" - skipping\n", hdr->compositionTimeStamp, group->max_cts_in_period));
			return;
		}

		//remap timestamps to our timeline
		if ((s64) hdr->decodingTimeStamp >= group->pto)
			hdr->decodingTimeStamp -= group->pto;
		else {
			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Packet DTS "LLU" less than PTO "LLU" - forcing CTS to 0\n", hdr->compositionTimeStamp, group->pto));
			hdr->decodingTimeStamp = 0;
		}
		if ((s64) hdr->compositionTimeStamp >= group->pto)
			hdr->compositionTimeStamp -= group->pto;
		else {
			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Packet CTS "LLU" less than PTO "LLU" - forcing CTS to 0\n", hdr->compositionTimeStamp, group->pto));
			hdr->compositionTimeStamp = 0;
		}
	} else if (!group->pto_setup) {
		do_map_time = 1;
		group->pto_setup = 1;
	}

	mpdin->fn_data_packet(service, ns, data, data_size, hdr, reception_status);

	if (do_map_time) {
		GF_NetworkCommand com;
		memset(&com, 0, sizeof(com));
		com.command_type = GF_NET_CHAN_SET_MEDIA_TIME;
		com.map_time.media_time = mpdin->media_start_range;
		com.map_time.timestamp = hdr->compositionTimeStamp;
		com.base.on_channel =  ns;
		gf_service_command(service, &com, GF_OK);
	}
}
Exemplo n.º 9
0
static GF_Err AC3_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com)
{
	AC3Reader *read = plug->priv;


	if (com->base.command_type==GF_NET_SERVICE_INFO) {
		com->info.name = read->icy_track_name ? read->icy_track_name : read->icy_name;
		com->info.comment = read->icy_genre;
		return GF_OK;
	}

	if (!com->base.on_channel) {
		/*if live session we may cache*/
		if (read->is_live && (com->command_type==GF_NET_IS_CACHABLE)) return GF_OK;
		return GF_NOT_SUPPORTED;
	}
	switch (com->command_type) {
	case GF_NET_CHAN_SET_PULL:
		if ((read->ch == com->base.on_channel) && read->is_live) return GF_NOT_SUPPORTED;
		return GF_OK;
	case GF_NET_CHAN_INTERACTIVE:
		if ((read->ch == com->base.on_channel) && read->is_live) return GF_NOT_SUPPORTED;
		return GF_OK;
	case GF_NET_CHAN_BUFFER:
		if ((read->ch == com->base.on_channel) && read->is_live) {
			if (com->buffer.max<1000) com->buffer.max = 1000;
			com->buffer.min = com->buffer.max/2;
		}
		return GF_OK;
	case GF_NET_CHAN_SET_PADDING:
		read->pad_bytes = com->pad.padding_bytes;
		return GF_OK;
	case GF_NET_CHAN_DURATION:
		com->duration.duration = read->duration;
		com->duration.duration /= read->sample_rate;
		return GF_OK;
	case GF_NET_CHAN_PLAY:
		read->start_range = com->play.start_range;
		read->end_range = com->play.end_range;
		read->current_time = 0;
		if (read->stream) gf_f64_seek(read->stream, 0, SEEK_SET);

		if (read->ch == com->base.on_channel) {
			read->done = 0;
			/*PLAY after complete download, estimate duration*/
			if (!read->is_remote && !read->duration) {
				AC3_ConfigureFromFile(read);
				if (read->duration) {
					GF_NetworkCommand rcfg;
					rcfg.base.on_channel = read->ch;
					rcfg.base.command_type = GF_NET_CHAN_DURATION;
					rcfg.duration.duration = read->duration;
					rcfg.duration.duration /= read->sample_rate;
					gf_service_command(read->service, &rcfg, GF_OK);
				}
			}
		}
		return GF_OK;
	case GF_NET_CHAN_STOP:
		return GF_OK;
	default:
		return GF_OK;
	}
}
Exemplo n.º 10
0
void AC3_NetIO(void *cbk, GF_NETIO_Parameter *param)
{
	GF_Err e;
	const char *szCache;
	u32 total_size, bytes_done;
	AC3Reader *read = (AC3Reader *) cbk;

	e = param->error;
	/*done*/
	if (param->msg_type==GF_NETIO_DATA_TRANSFERED) {
		if (read->stream) {
			read->is_remote = 0;
			e = GF_EOS;
		} else if (!read->needs_connection) {
			return;
		}
	} else if (param->msg_type==GF_NETIO_PARSE_HEADER) {
		if (!strcmp(param->name, "icy-name")) {
			if (read->icy_name) gf_free(read->icy_name);
			read->icy_name = gf_strdup(param->value);
		}
		if (!strcmp(param->name, "icy-genre")) {
			if (read->icy_genre) gf_free(read->icy_genre);
			read->icy_genre = gf_strdup(param->value);
		}
		if (!strcmp(param->name, "icy-meta")) {
			GF_NetworkCommand com;
			char *meta;
			if (read->icy_track_name) gf_free(read->icy_track_name);
			read->icy_track_name = NULL;
			meta = param->value;
			while (meta && meta[0]) {
				char *sep = strchr(meta, ';');
				if (sep) sep[0] = 0;

				if (!strnicmp(meta, "StreamTitle=", 12)) {
					read->icy_track_name = gf_strdup(meta+12);
				}
				if (!sep) break;
				sep[0] = ';';
				meta = sep+1;
			}

			com.base.command_type = GF_NET_SERVICE_INFO;
			gf_service_command(read->service, &com, GF_OK);
		}
		return;
	} else {
		/*handle service message*/
		gf_service_download_update_stats(read->dnload);
		if (param->msg_type!=GF_NETIO_DATA_EXCHANGE) return;
	}

	/*data fetching or EOS*/
	if (e >= GF_OK) {
		if (read->needs_connection) {
			gf_dm_sess_get_stats(read->dnload, NULL, NULL, &total_size, NULL, NULL, NULL);
			if (!total_size) read->is_live = 1;
		}
		if (read->is_live) {
			if (!e) AC3_OnLiveData(read, param->data, param->size);
			return;
		}
		if (read->stream) return;

		/*open service*/
		szCache = gf_dm_sess_get_cache_name(read->dnload);
		if (!szCache) e = GF_IO_ERR;
		else {
			read->stream = gf_f64_open((char *) szCache, "rb");
			if (!read->stream) e = GF_SERVICE_ERROR;
			else {
				/*if full file at once (in cache) parse duration*/
				if (e==GF_EOS) read->is_remote = 0;
				e = GF_OK;
				/*not enough data*/
				if (!AC3_ConfigureFromFile(read)) {
					/*get amount downloaded and check*/
					gf_dm_sess_get_stats(read->dnload, NULL, NULL, NULL, &bytes_done, NULL, NULL);
					if (bytes_done>10*1024) {
						e = GF_CORRUPTED_DATA;
					} else {
						fclose(read->stream);
						read->stream = NULL;
						return;
					}
				}
			}
		}
	}
	/*OK confirm*/
	if (read->needs_connection) {
		read->needs_connection = 0;
		gf_service_connect_ack(read->service, NULL, e);
		if (!e) AC3_SetupObject(read);
	}
}
Exemplo n.º 11
0
static void gf_rtp_switch_quality(RTPClient *rtp, Bool switch_up)
{
    u32 i,count;
    RTPStream *ch, *cur_ch;
    GF_NetworkCommand com;

    count = gf_list_count(rtp->channels);
    /*find the current stream*/
    ch = cur_ch = NULL;
    for (i = 0; i < count; i++) {
        cur_ch = (RTPStream *) gf_list_get(rtp->channels, i);
        if (cur_ch->mid != rtp->cur_mid) {
            cur_ch=NULL;
            continue;
        }
        break;
    }
    if (!cur_ch) return;

    if (switch_up)
    {
        /*this is the highest stream*/
        if (!cur_ch->next_stream)
        {
            cur_ch->status = RTP_Running;
            return;
        }
        else
        {
            for (i = 0; i < count; i++) {
                ch = (RTPStream *) gf_list_get(rtp->channels, i);
                if (ch->mid == cur_ch->next_stream)
                {
                    /*resume streaming next channel*/
                    gf_mx_p(rtp->mx);
                    RP_InitStream(ch, 0);
                    gf_mx_v(rtp->mx);
                    ch->status = RTP_Running;
                    rtp->cur_mid = ch->mid;
                    break;
                }

            }
        }
    }
    else
    {
        /*this is the lowest stream i.e base layer*/
        if (!cur_ch->prev_stream)
        {
            cur_ch->status = RTP_Running;
            return;
        }
        else
        {
            for (i = 0; i < count; i++) {
                ch = (RTPStream *) gf_list_get(rtp->channels, i);
                if (ch->mid == cur_ch->prev_stream)
                {
                    /*stop streaming current channel*/
                    gf_rtp_stop(cur_ch->rtp_ch);
                    cur_ch->status = RTP_Connected;
                    com.command_type = GF_NET_CHAN_RESET;
                    com.base.on_channel = cur_ch;
                    gf_service_command(rtp->service, &com, GF_OK);
                    rtp->cur_mid = ch->mid;
                    break;
                }
            }
        }
    }
    GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("Switch from ES%d to ES %d\n", cur_ch->mid, ch->mid));
    return;
}
Exemplo n.º 12
0
/* Callback functions used by a media parser when parsing events happens */
GF_Err gf_mse_proxy(GF_InputService *parser, GF_NetworkCommand *command)
{
	if (!parser || !command || !parser->proxy_udta) {
		return GF_BAD_PARAM;
	} else {
		GF_HTML_SourceBuffer *sb = (GF_HTML_SourceBuffer *)parser->proxy_udta;
		switch (command->command_type) {
		case GF_NET_SERVICE_QUERY_INIT_RANGE:
			break;
		case GF_NET_SERVICE_QUERY_NEXT:
			/* The parser is asking for the next media segment in the buffer,
			   check for the media time and give the right one */
		{
			GF_HTML_ArrayBuffer *buffer;
			/* The input buffer should not be modified by append operations at the same time, no need to protect access */
			buffer = (GF_HTML_ArrayBuffer *)gf_list_get(sb->input_buffer, 0);
			if (buffer) {
				command->url_query.discontinuity_type = 0;
				command->url_query.current_download = GF_FALSE;
				command->url_query.start_range = 0;
				command->url_query.end_range = 0;
				command->url_query.switch_start_range = 0;
				command->url_query.switch_end_range = 0;
				command->url_query.next_url_init_or_switch_segment = NULL;
				if (buffer->is_init) {
					GF_HTML_ArrayBuffer *next = (GF_HTML_ArrayBuffer *)gf_list_get(sb->input_buffer, 1);
					command->url_query.discontinuity_type = 1;
					if (next) {
						GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] Next segment to parse %s with init \n", next->url, buffer->url));
						command->url_query.next_url = next->url;
						command->url_query.next_url_init_or_switch_segment = buffer->url;
						gf_list_rem(sb->input_buffer, 0);
						gf_list_rem(sb->input_buffer, 0);
					} else {
						GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] Only one init segment to parse %s, need to wait\n", buffer->url));
						command->url_query.next_url = NULL;
					}
				} else {
					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] Next segment to parse %s\n", buffer->url));
					command->url_query.next_url = buffer->url;
					gf_list_rem(sb->input_buffer, 0);
				}
				sb->prev_buffer = buffer;
			} else {
				command->url_query.next_url = NULL;
				command->url_query.discontinuity_type = 0;
			}
		}
		break;
		case GF_NET_SERVICE_STATUS_PROXY:
			/* The parser is informing the proxy about its status changes:
			    - new track found
				- all tracks parsed
				- connect/disconnect
				- */
			if (command->status.is_add_media) {
				if (command->status.desc) {
					gf_mse_source_buffer_store_track_desc(sb, (GF_ObjectDescriptor *)command->status.desc);
				} else {
					/* this is the last add media, we can switch updating to false */
					/* the first init segment was correctly processed */
					gf_mse_source_buffer_set_update(sb, GF_FALSE);
					/* TODO: set active tracks and send addsourcebuffer event */
					/* TODO: send media loadedmetadata event */
				}
				gf_service_declare_media(sb->mediasource->service, command->status.desc, (command->status.desc ? GF_TRUE : GF_FALSE));
			}
			/* general connection/disconnection messages from the media parser (not track related) */
			else if (!command->status.channel) {
				/* connection message */
				if (!command->status.is_disconnect) {
					if (command->status.e == GF_OK) {
						/* nothing needs to be done. Setup is done with final add media */
						sb->parser_connected = GF_TRUE;
						sb->mediasource->durationType = DURATION_INFINITY;
						gf_mse_source_buffer_setup_tracks(sb);
					} else {
						/* wrong first init segment */
						/* TODO: fire an error event */
					}
					gf_service_connect_ack(sb->mediasource->service, command->status.channel, command->status.e);
				} else {
					gf_service_disconnect_ack(sb->mediasource->service, command->status.channel, command->status.e);
				}
			}
			/* channel (track related) specific connection/disconnection messages from the media parser */
			else {
				if (!command->status.is_disconnect) {
					gf_service_connect_ack(sb->mediasource->service, command->status.channel, command->status.e);
				} else {
					gf_service_disconnect_ack(sb->mediasource->service, command->status.channel, command->status.e);
				}
			}
			break;
		default:
			gf_service_command(sb->mediasource->service, command, GF_OK);
			break;
		}
		return GF_OK;
	}
}
Exemplo n.º 13
0
void RP_ProcessRTP(RTPStream *ch, char *pck, u32 size)
{
	GF_NetworkCommand com;
	GF_Err e;
	GF_RTPHeader hdr;
	u32 PayloadStart;
	ch->rtp_bytes += size;

	/*first decode RTP*/
	e = gf_rtp_decode_rtp(ch->rtp_ch, pck, size, &hdr, &PayloadStart);

	/*corrupted or NULL data*/
	if (e || (PayloadStart >= size)) {
		//gf_service_send_packet(ch->owner->service, ch->channel, NULL, 0, NULL, GF_CORRUPTED_DATA);
		return;
	}

	/*if we must notify some timing, do it now. If the channel has no range, this should NEVER be called*/
	if (ch->check_rtp_time /*&& gf_rtp_is_active(ch->rtp_ch)*/) {
		Double ch_time;

		/*it may happen that we still receive packets from a previous "play" request. If this is the case,
		filter until we reach the indicated rtptime*/
		if (ch->rtp_ch->rtp_time
		        && (ch->rtp_ch->rtp_first_SN > hdr.SequenceNumber)
		        && (ch->rtp_ch->rtp_time < hdr.TimeStamp)
		   ) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[RTP] Rejecting too early packet (TS %d vs signaled rtp time %d - diff %d ms)\n",
			                                    hdr.TimeStamp, ch->rtp_ch->rtp_time, ((hdr.TimeStamp - ch->rtp_ch->rtp_time)*1000) / ch->rtp_ch->TimeScale));
			return;
		}

		ch_time = gf_rtp_get_current_time(ch->rtp_ch);

		/*this is the first packet on the channel (no PAUSE)*/
		if (ch->check_rtp_time == RTP_SET_TIME_RTP) {
			/*Note: in a SEEK with RTSP, the rtp-info time given by the server is
			the rtp time of the desired range. But the server may (and should) send from
			the previous I frame on video, so the time of the first rtp packet after
			a SEEK can actually be less than CurrentStart. We don't drop these
			packets in order to see the maximum video. We could drop it, this would mean
			wait for next RAP...*/

			memset(&com, 0, sizeof(com));
			com.command_type = GF_NET_CHAN_MAP_TIME;
			com.base.on_channel = ch->channel;
			if (ch->rtsp) {
				com.map_time.media_time = ch->current_start + ch_time;
			} else {
				com.map_time.media_time = 0;
			}

			com.map_time.timestamp = hdr.TimeStamp;
			com.map_time.reset_buffers = 0;
			gf_service_command(ch->owner->service, &com, GF_OK);

			GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTP] Mapping RTP Time seq %d TS %d Media Time %g - rtp info seq %d TS %d\n",
			                                 hdr.SequenceNumber, hdr.TimeStamp, com.map_time.media_time, ch->rtp_ch->rtp_first_SN, ch->rtp_ch->rtp_time
			                                ));

			/*skip RTCP clock init when RTSP is used*/
			if (ch->rtsp) ch->rtcp_init = 1;

//			if (ch->depacketizer->payt==GF_RTP_PAYT_H264_AVC) ch->depacketizer->flags |= GF_RTP_AVC_WAIT_RAP;
		}
		/*this is RESUME on channel, filter packet based on time (darwin seems to send
		couple of packet before)
		do not fetch if we're below 10 ms or <0, because this means we already have
		this packet - as the PAUSE is issued with the RTP currentTime*/
		else if (ch_time <= 0.021) {
			return;
		}
		ch->check_rtp_time = RTP_SET_TIME_NONE;
	}

	gf_rtp_depacketizer_process(ch->depacketizer, &hdr, pck + PayloadStart, size - PayloadStart);

	/*last check: signal EOS if we're close to end range in case the server do not send RTCP BYE*/
	if ((ch->flags & RTP_HAS_RANGE) && !(ch->flags & RTP_EOS) ) {
		/*also check last CTS*/
		Double ts = (Double) ((u32) ch->depacketizer->sl_hdr.compositionTimeStamp - hdr.TimeStamp);
		ts /= gf_rtp_get_clockrate(ch->rtp_ch);
		if (ABSDIFF(ch->range_end, (ts + ch->current_start + gf_rtp_get_current_time(ch->rtp_ch)) ) < 0.2) {
			ch->flags |= RTP_EOS;
			ch->stat_stop_time = gf_sys_clock();
			gf_service_send_packet(ch->owner->service, ch->channel, NULL, 0, NULL, GF_EOS);
		}
	}
}
Exemplo n.º 14
0
GF_Err ISOR_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream)
{
	u32 ESID;
	ISOMChannel *ch;
	GF_NetworkCommand com;
	u32 track;
	Bool is_esd_url;
	GF_Err e;
	ISOMReader *read;
	if (!plug || !plug->priv) return GF_SERVICE_ERROR;
	read = (ISOMReader *) plug->priv;

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

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

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

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

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

	GF_SAFEALLOC(ch, ISOMChannel);
	ch->owner = read;
	ch->channel = channel;
	gf_list_add(read->channels, ch);
	ch->track = track;
	ch->track_id = gf_isom_get_track_id(read->mov, ch->track);
	switch (gf_isom_get_media_type(ch->owner->mov, ch->track)) {
	case GF_ISOM_MEDIA_OCR:
		ch->streamType = GF_STREAM_OCR;
		break;
	case GF_ISOM_MEDIA_SCENE:
		ch->streamType = GF_STREAM_SCENE;
		break;
	case GF_ISOM_MEDIA_VISUAL:
		gf_isom_get_reference(ch->owner->mov, ch->track, GF_ISOM_REF_BASE, 1, &ch->base_track);
		ch->next_track = 0;
		/*in scalable mode add SPS/PPS in-band*/
		ch->nalu_extract_mode = GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG /*| GF_ISOM_NALU_EXTRACT_ANNEXB_FLAG*/;
		gf_isom_set_nalu_extract_mode(ch->owner->mov, ch->track, ch->nalu_extract_mode);
		break;
	}

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

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

			gf_media_get_file_hash(gf_isom_get_filename(read->mov), com.drm_cfg.hash);
			if (read->input->query_proxy && read->input->proxy_udta) {
				read->input->query_proxy(read->input, &com);
			} else {
				gf_service_command(read->service, &com, GF_OK);
			}
		} else if (gf_isom_is_cenc_media(read->mov, track, 1)) {
			ch->is_cenc = GF_TRUE;
			isor_send_cenc_config(ch);
		}
	}
	return e;
}
Exemplo n.º 15
0
void isor_check_buffer_level(ISOMReader *read)
{
	Double dld_time_remaining, mov_rate;
	GF_NetworkCommand com;
	u32 i, total, done, Bps;
	u64 dur;
	GF_NetIOStatus status;
	Bool do_buffer = GF_FALSE;
	if (!read->dnload) return;
	if (!read->mov) return;

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


	gf_mx_p(read->segment_mutex);

	dld_time_remaining = total-done;
	dld_time_remaining /= Bps;

	//we add 30 seconds to smooth out bitrate variations ..;
	dld_time_remaining += 30;

	mov_rate = total;
	dur = gf_isom_get_duration(read->mov);
	if (dur) {
		mov_rate /= dur;
		mov_rate *= gf_isom_get_timescale(read->mov);
	}

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

			data_offset += samp->dataLength;

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

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

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

			memset(&com, 0, sizeof(GF_NetworkCommand));
			com.command_type = do_buffer ? GF_NET_CHAN_PAUSE : GF_NET_CHAN_RESUME;
			com.buffer.on_channel = ch->channel;
			com.buffer.min = ch->buffer_min;
			com.buffer.max = ch->buffer_max;
			gf_service_command(read->service, &com, GF_OK);
			ch->buffering = do_buffer;
			read->buffering = do_buffer;
		} else if (ch->buffering) {
			memset(&com, 0, sizeof(GF_NetworkCommand));
			com.command_type = GF_NET_CHAN_BUFFER;
			com.buffer.on_channel = ch->channel;
			com.buffer.min = ch->buffer_min;
			com.buffer.max = ch->buffer_max;
			com.buffer.occupancy = buffer_level;
			gf_service_command(read->service, &com, GF_OK);
		}
	}
	gf_mx_v(read->segment_mutex);
}
Exemplo n.º 16
0
GF_Err mpdin_dash_io_on_dash_event(GF_DASHFileIO *dashio, GF_DASHEventType dash_evt, s32 group_idx, GF_Err error_code)
{
	GF_Err e;
	u32 i;
	GF_MPD_In *mpdin = (GF_MPD_In *)dashio->udta;

	if (dash_evt==GF_DASH_EVENT_PERIOD_SETUP_ERROR) {
		if (!mpdin->connection_ack_sent) {
			mpdin->fn_connect_ack(mpdin->service, NULL, error_code);
			mpdin->connection_ack_sent= GF_TRUE;
		}
		return GF_OK;
	}

	if (dash_evt==GF_DASH_EVENT_SELECT_GROUPS) {
		const char *opt;
		//configure buffer in dynamic mode without low latency: we indicate how much the player will buffer
		if (gf_dash_is_dynamic_mpd(mpdin->dash) && !mpdin->use_low_latency) {
			u32 buffer_ms = 0;
			const char *opt = gf_modules_get_option((GF_BaseInterface *)mpdin->plug, "Network", "BufferLength");
			if (opt) buffer_ms = atoi(opt);

			//use min buffer from MPD
			if (mpdin->buffer_mode>=MPDIN_BUFFER_MIN) {
				u32 mpd_buffer_ms = gf_dash_get_min_buffer_time(mpdin->dash);
				if (mpd_buffer_ms > buffer_ms)
					buffer_ms = mpd_buffer_ms;
			}

			if (buffer_ms) {
				gf_dash_set_user_buffer(mpdin->dash, buffer_ms);
			}
		}
		//let the player decide which group to play: we declare everything

		//however select the default languague
		opt = gf_modules_get_option((GF_BaseInterface *)mpdin->plug, "Systems", "LanguageName");
		if (opt)
			gf_dash_groups_set_language(mpdin->dash, opt);

		return GF_OK;
	}

	/*for all selected groups, create input service and connect to init/first segment*/
	if (dash_evt==GF_DASH_EVENT_CREATE_PLAYBACK) {
		/*select input services if possible*/
		for (i=0; i<gf_dash_get_group_count(mpdin->dash); i++) {
			const char *mime, *init_segment;
			//let the player decide which group to play
			if (!gf_dash_is_group_selectable(mpdin->dash, i))
				continue;

			mime = gf_dash_group_get_segment_mime(mpdin->dash, i);
			init_segment = gf_dash_group_get_segment_init_url(mpdin->dash, i, NULL, NULL);
			e = MPD_LoadMediaService(mpdin, i, mime, init_segment);
			if (e != GF_OK) {
				gf_dash_group_select(mpdin->dash, i, 0);
			} else {
				u32 w, h;
				/*connect our media service*/
				GF_MPDGroup *group = gf_dash_get_group_udta(mpdin->dash, i);
				gf_dash_group_get_video_info(mpdin->dash, i, &w, &h);
				if (w && h && w>mpdin->width && h>mpdin->height) {
					mpdin->width = w;
					mpdin->height = h;
				}

				e = group->segment_ifce->ConnectService(group->segment_ifce, mpdin->service, init_segment);
				if (e) {
					GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[MPD_IN] Unable to connect input service to %s\n", init_segment));
					gf_dash_group_select(mpdin->dash, i, 0);
				} else {
					group->service_connected = 1;
				}
				if (mpdin->closed) return GF_OK;
			}
		}

		if (!mpdin->connection_ack_sent) {
			mpdin->fn_connect_ack(mpdin->service, NULL, GF_OK);
			mpdin->connection_ack_sent=1;
		}

		//we had a seek outside of the period we were setting up, during period setup !
		//request the seek again from the player
		if (mpdin->seek_request>=0) {
			GF_NetworkCommand com;
			memset(&com, 0, sizeof(GF_NetworkCommand));
			com.command_type = GF_NET_SERVICE_SEEK;
			com.play.start_range = mpdin->seek_request;
			mpdin->seek_request = 0;
			gf_service_command(mpdin->service, &com, GF_OK);
		}
		return GF_OK;
	}

	/*for all running services, stop service*/
	if (dash_evt==GF_DASH_EVENT_DESTROY_PLAYBACK) {

		mpdin->service->subservice_disconnect = 1;
		gf_service_disconnect_ack(mpdin->service, NULL, GF_OK);
		mpdin->service->subservice_disconnect = 2;

		for (i=0; i<gf_dash_get_group_count(mpdin->dash); i++) {
			GF_MPDGroup *group = gf_dash_get_group_udta(mpdin->dash, i);
			if (!group) continue;
			if (group->segment_ifce) {
				if (group->service_connected) {
					group->segment_ifce->CloseService(group->segment_ifce);
					group->service_connected = 0;
				}
				gf_modules_close_interface((GF_BaseInterface *) group->segment_ifce);
			}
			gf_free(group);
			gf_dash_set_group_udta(mpdin->dash, i, NULL);
		}
		mpdin->service->subservice_disconnect = 0;
		return GF_OK;
	}

	if (dash_evt==GF_DASH_EVENT_BUFFERING) {
		u32 tot, done;
		gf_dash_get_buffer_info(mpdin->dash, &tot, &done);
		fprintf(stderr, "DASH: Buffering %g%% out of %d ms\n", (100.0*done)/tot, tot);
		return GF_OK;
	}
	if (dash_evt==GF_DASH_EVENT_SEGMENT_AVAILABLE) {
		if (group_idx>=0) {
			GF_MPDGroup *group = gf_dash_get_group_udta(mpdin->dash, group_idx);
			if (group) MPD_NotifyData(group, 0);
		}
		return GF_OK;
	}
	if (dash_evt==GF_DASH_EVENT_QUALITY_SWITCH) {
		if (group_idx>=0) {
			GF_MPDGroup *group = gf_dash_get_group_udta(mpdin->dash, group_idx);
			if (group) {
				GF_NetworkCommand com;
				memset(&com, 0, sizeof(GF_NetworkCommand) );

				com.command_type = GF_NET_SERVICE_EVENT;
				com.send_event.evt.type = GF_EVENT_QUALITY_SWITCHED;
				gf_service_command(mpdin->service, &com, GF_OK);
			}
		}
		return GF_OK;
	}
	if (dash_evt==GF_DASH_EVENT_TIMESHIFT_OVERFLOW) {
		GF_NetworkCommand com;
		com.command_type = GF_NET_SERVICE_EVENT;
		com.send_event.evt.type = (group_idx>=0) ? GF_EVENT_TIMESHIFT_OVERFLOW : GF_EVENT_TIMESHIFT_UNDERRUN;
		gf_service_command(mpdin->service, &com, GF_OK);
	}
	if (dash_evt==GF_DASH_EVENT_TIMESHIFT_UPDATE) {
		GF_NetworkCommand com;
		com.command_type = GF_NET_SERVICE_EVENT;
		com.send_event.evt.type = GF_EVENT_TIMESHIFT_UPDATE;
		gf_service_command(mpdin->service, &com, GF_OK);
	}

	return GF_OK;
}
Exemplo n.º 17
0
GF_Err MPD_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url)
{
	GF_MPD_In *mpdin = (GF_MPD_In*) plug->priv;
	const char *opt;
	GF_Err e;
	s32 shift_utc_ms, debug_adaptation_set;
	u32 max_cache_duration, auto_switch_count, init_timeshift;
	Bool use_server_utc;
	GF_DASHInitialSelectionMode first_select_mode;
	Bool keep_files, disable_switching;

	GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MPD_IN] Received Service Connection request (%p) from terminal for %s\n", serv, url));

	if (!mpdin || !serv || !url)
		return GF_BAD_PARAM;

	mpdin->service = serv;
	mpdin->seek_request = -1;

	mpdin->dash_io.udta = mpdin;
	mpdin->dash_io.delete_cache_file = mpdin_dash_io_delete_cache_file;
	mpdin->dash_io.create = mpdin_dash_io_create;
	mpdin->dash_io.del = mpdin_dash_io_del;
	mpdin->dash_io.abort = mpdin_dash_io_abort;
	mpdin->dash_io.setup_from_url = mpdin_dash_io_setup_from_url;
	mpdin->dash_io.set_range = mpdin_dash_io_set_range;
	mpdin->dash_io.init = mpdin_dash_io_init;
	mpdin->dash_io.run = mpdin_dash_io_run;
	mpdin->dash_io.get_url = mpdin_dash_io_get_url;
	mpdin->dash_io.get_cache_name = mpdin_dash_io_get_cache_name;
	mpdin->dash_io.get_mime = mpdin_dash_io_get_mime;
	mpdin->dash_io.get_header_value = mpdin_dash_io_get_header_value;
	mpdin->dash_io.get_utc_start_time = mpdin_dash_io_get_utc_start_time;
	mpdin->dash_io.get_bytes_per_sec = mpdin_dash_io_get_bytes_per_sec;
	mpdin->dash_io.get_total_size = mpdin_dash_io_get_total_size;
	mpdin->dash_io.get_bytes_done = mpdin_dash_io_get_bytes_done;
	mpdin->dash_io.on_dash_event = mpdin_dash_io_on_dash_event;

	max_cache_duration = 0;
	opt = gf_modules_get_option((GF_BaseInterface *)plug, "Network", "BufferLength");
	if (opt) max_cache_duration = atoi(opt);

	auto_switch_count = 0;
	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "AutoSwitchCount");
	if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "AutoSwitchCount", "0");
	if (opt) auto_switch_count = atoi(opt);

	keep_files = 0;
	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "KeepFiles");
	if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "KeepFiles", "no");
	if (opt && !strcmp(opt, "yes")) keep_files = 1;

	disable_switching = 0;
	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "DisableSwitching");
	if (opt && !strcmp(opt, "yes")) disable_switching = 1;

	first_select_mode = 0;
	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "StartRepresentation");
	if (!opt) {
		gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "StartRepresentation", "minBandwidth");
		opt = "minBandwidth";
	}
	if (opt && !strcmp(opt, "maxBandwidth")) first_select_mode = GF_DASH_SELECT_BANDWIDTH_HIGHEST;
	else if (opt && !strcmp(opt, "minQuality")) first_select_mode = GF_DASH_SELECT_QUALITY_LOWEST;
	else if (opt && !strcmp(opt, "maxQuality")) first_select_mode = GF_DASH_SELECT_QUALITY_HIGHEST;
	else first_select_mode = GF_DASH_SELECT_BANDWIDTH_LOWEST;

	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "MemoryStorage");
	if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "MemoryStorage", "yes");
	mpdin->memory_storage = (opt && !strcmp(opt, "yes")) ? 1 : 0;

	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "UseMaxResolution");
	if (!opt) {
#if defined(_WIN32_WCE) || defined(GPAC_ANDROID) || defined(GPAC_IPHONE)
		opt = "yes";
#else
		opt = "no";
#endif
		gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "UseMaxResolution", opt);
	}
	mpdin->use_max_res = !strcmp(opt, "yes") ? 1 : 0;

	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "ImmediateSwitching");
	if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "ImmediateSwitching", "no");
	mpdin->immediate_switch = (opt && !strcmp(opt, "yes")) ? 1 : 0;

	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "BufferingMode");
	if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "BufferingMode", "minBuffer");

	if (opt && !strcmp(opt, "segments")) mpdin->buffer_mode = MPDIN_BUFFER_SEGMENTS;
	else if (opt && !strcmp(opt, "none")) mpdin->buffer_mode = MPDIN_BUFFER_NONE;
	else mpdin->buffer_mode = MPDIN_BUFFER_MIN;


	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "LowLatency");
	if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "LowLatency", "no");

	if (opt && !strcmp(opt, "chunk") ) mpdin->use_low_latency = 1;
	else if (opt && !strcmp(opt, "always") ) mpdin->use_low_latency = 2;
	else mpdin->use_low_latency = 0;

	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "AllowAbort");
	if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "AllowAbort", "no");
	mpdin->allow_http_abort = (opt && !strcmp(opt, "yes")) ? GF_TRUE : GF_FALSE;

	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "ShiftClock");
	if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "ShiftClock", "0");
	shift_utc_ms = opt ? atoi(opt) : 0;

	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "UseServerUTC");
	if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "UseServerUTC", "yes");
	use_server_utc = (opt && !strcmp(opt, "yes")) ? 1 : 0;

	mpdin->in_seek = 0;
	mpdin->previous_start_range = 0;

	init_timeshift = 0;
	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "InitialTimeshift");
	if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "InitialTimeshift", "0");
	if (opt) init_timeshift = atoi(opt);

	//override all service callbacks
	mpdin->fn_connect_ack = serv->fn_connect_ack;
	serv->fn_connect_ack = mpdin_connect_ack;
	mpdin->fn_data_packet = serv->fn_data_packet;
	serv->fn_data_packet = mpdin_data_packet;

	mpdin->dash = gf_dash_new(&mpdin->dash_io, max_cache_duration, auto_switch_count, keep_files, disable_switching, first_select_mode, (mpdin->buffer_mode == MPDIN_BUFFER_SEGMENTS) ? 1 : 0, init_timeshift);

	if (!mpdin->dash) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[MPD_IN] Error - cannot create DASH Client for %s\n", url));
		mpdin->fn_connect_ack(mpdin->service, NULL, GF_IO_ERR);
		return GF_OK;
	}

	gf_dash_set_utc_shift(mpdin->dash, shift_utc_ms);
	gf_dash_enable_utc_drift_compensation(mpdin->dash, use_server_utc);

	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "UseScreenResolution");
	//default mode is no for the time being
	if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "UseScreenResolution", "no");
	if (!opt || !strcmp(opt, "yes")) {
		GF_NetworkCommand com;
		memset(&com, 0, sizeof(GF_NetworkCommand));
		com.base.command_type = GF_NET_SERVICE_MEDIA_CAP_QUERY;
		gf_service_command(serv, &com, GF_OK);

		if (com.mcaps.width && com.mcaps.height) {
			gf_dash_set_max_resolution(mpdin->dash, com.mcaps.width, com.mcaps.height, com.mcaps.display_bit_depth);
		}
	}

	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "TimeBetween404");
	if (opt) {
		gf_dash_set_min_timeout_between_404(mpdin->dash, atoi(opt));
	}

	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "SegmentExpirationThreshold");
	if (opt) {
		gf_dash_set_segment_expiration_threshold(mpdin->dash, atoi(opt));
	}

	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "SwitchProbeCount");
	if (opt) {
		gf_dash_set_switching_probe_count(mpdin->dash, atoi(opt));
	} else {
		gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "SwitchProbeCount", "1");
	}


	opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "DebugAdaptationSet");
	if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "DebugAdaptationSet", "-1");
	debug_adaptation_set = opt ? atoi(opt) : -1;

	gf_dash_debug_group(mpdin->dash, debug_adaptation_set);

	/*dash thread starts at the end of gf_dash_open */
	e = gf_dash_open(mpdin->dash, url);
	if (e) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[MPD_IN] Error - cannot initialize DASH Client for %s: %s\n", url, gf_error_to_string(e) ));
		mpdin->fn_connect_ack(mpdin->service, NULL, e);
		return GF_OK;
	}
	return GF_OK;
}
Exemplo n.º 18
0
void RP_ProcessRTCP(RTPStream *ch, char *pck, u32 size)
{
	Bool has_sr;
	GF_Err e;

	if (ch->status == RTP_Connected) return;

	ch->rtcp_bytes += size;

	e = gf_rtp_decode_rtcp(ch->rtp_ch, pck, size, &has_sr);
	if (e<0) return;

	/*update sync if on pure RTP*/
	if (!ch->rtcp_init && has_sr) {
		Double ntp_clock;

		ntp_clock = ch->rtp_ch->last_SR_NTP_sec;
		ntp_clock += ((Double)ch->rtp_ch->last_SR_NTP_frac)/0xFFFFFFFF;

		if (!ch->owner->last_ntp) {
			//add safety in case this RTCP report is received before another report
			//that was supposed to come in earlier (with earlier NTP)
			//Double safety_offset, time = ch->rtp_ch->last_SR_rtp_time;
			//time /= ch->rtp_ch->TimeScale;
			//safety_offset = time/2;
			ch->owner->last_ntp = ntp_clock;
		}

		if (ntp_clock >= ch->owner->last_ntp) {
			ntp_clock -= ch->owner->last_ntp;
		} else {
			ntp_clock = 0;
		}

		//assert(ch->rtp_ch->last_SR_rtp_time >= (u64) (ntp_clock * ch->rtp_ch->TimeScale));
		ch->ts_offset = ch->rtp_ch->last_SR_rtp_time;
		ch->ts_offset -= (s64) (ntp_clock * ch->rtp_ch->TimeScale);

#if 0
		GF_NetworkCommand com;
		memset(&com, 0, sizeof(com));
		com.command_type = GF_NET_CHAN_MAP_TIME;
		com.base.on_channel = ch->channel;
		com.map_time.media_time = ntp;

		if (com.map_time.media_time >= ch->owner->last_ntp) {
			com.map_time.media_time -= ch->owner->last_ntp;
		} else {
			com.map_time.media_time = 0;
		}
		com.map_time.timestamp = ch->rtp_ch->last_SR_rtp_time;
		com.map_time.reset_buffers = 1;
		gf_service_command(ch->owner->service, &com, GF_OK);
#endif

		GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTCP] At %d Using Sender Report to map RTP TS %d to NTP clock %g - new TS offset "LLD" \n",
		                                 gf_sys_clock(), ch->rtp_ch->last_SR_rtp_time, ntp_clock, ch->ts_offset
		                                ));

		ch->rtcp_init = 1;
		ch->check_rtp_time = RTP_SET_TIME_NONE;
	}

	if (e == GF_EOS) {
		ch->flags |= RTP_EOS;
		ch->stat_stop_time = gf_sys_clock();
		gf_service_send_packet(ch->owner->service, ch->channel, NULL, 0, NULL, GF_EOS);
	}
}
Exemplo n.º 19
0
static u32 FFDemux_Run(void *par)
{
	AVPacket pkt;
	s64 seek_to;
	GF_NetworkCommand com;
	GF_NetworkCommand map;
	GF_SLHeader slh;
	FFDemux *ffd = (FFDemux *) par;

	memset(&map, 0, sizeof(GF_NetworkCommand));
	map.command_type = GF_NET_CHAN_MAP_TIME;

	memset(&com, 0, sizeof(GF_NetworkCommand));
	com.command_type = GF_NET_BUFFER_QUERY;

	memset(&slh, 0, sizeof(GF_SLHeader));

	slh.compositionTimeStampFlag = slh.decodingTimeStampFlag = 1;

	while (ffd->is_running) {
		//nothing connected, wait
		if (!ffd->video_ch && !ffd->audio_ch) {
			gf_sleep(100);
			continue;
		}

		if ((ffd->seek_time>=0) && ffd->seekable) {
			seek_to = (s64) (AV_TIME_BASE*ffd->seek_time);
			av_seek_frame(ffd->ctx, -1, seek_to, AVSEEK_FLAG_BACKWARD);
			ffd->seek_time = -1;
		}
		pkt.stream_index = -1;
		/*EOF*/
		if (av_read_frame(ffd->ctx, &pkt) <0) break;
		if (pkt.pts == AV_NOPTS_VALUE) pkt.pts = pkt.dts;
		if (!pkt.dts) pkt.dts = pkt.pts;

		slh.compositionTimeStamp = pkt.pts;
		slh.decodingTimeStamp = pkt.dts;

		gf_mx_p(ffd->mx);
		/*blindly send audio as soon as video is init*/
		if (ffd->audio_ch && (pkt.stream_index == ffd->audio_st) ) {
			slh.compositionTimeStamp *= ffd->audio_tscale.num;
			slh.decodingTimeStamp *= ffd->audio_tscale.num;

			gf_service_send_packet(ffd->service, ffd->audio_ch, (char *) pkt.data, pkt.size, &slh, GF_OK);
		}
		else if (ffd->video_ch && (pkt.stream_index == ffd->video_st)) {
			slh.compositionTimeStamp *= ffd->video_tscale.num;
			slh.decodingTimeStamp *= ffd->video_tscale.num;
			slh.randomAccessPointFlag = pkt.flags&AV_PKT_FLAG_KEY ? 1 : 0;
			gf_service_send_packet(ffd->service, ffd->video_ch, (char *) pkt.data, pkt.size, &slh, GF_OK);
		}
		gf_mx_v(ffd->mx);
		av_free_packet(&pkt);

		/*sleep untill the buffer occupancy is too low - note that this work because all streams in this
		demuxer are synchronized*/
		while (ffd->audio_run || ffd->video_run) {
			gf_service_command(ffd->service, &com, GF_OK);
			if (com.buffer.occupancy < com.buffer.max)
				break;

			gf_sleep(1);
		}

		if (!ffd->audio_run && !ffd->video_run) break;
	}
	/*signal EOS*/
	if (ffd->audio_ch) gf_service_send_packet(ffd->service, ffd->audio_ch, NULL, 0, NULL, GF_EOS);
	if (ffd->video_ch) gf_service_send_packet(ffd->service, ffd->video_ch, NULL, 0, NULL, GF_EOS);
	ffd->is_running = 2;

	return 0;
}
Exemplo n.º 20
0
static GF_Err ISOW_Write(GF_StreamingCache *mc, LPNETCHANNEL ch, char *data, u32 data_size, GF_SLHeader *sl_hdr)
{
	ISOMChannel *mch;
	GF_ESD *esd;
	u32 di, mtype;
	u64 DTS, CTS;
	ISOMReader *cache = (ISOMReader *)mc->priv;
	if (!cache->mov || !cache->service) return GF_BAD_PARAM;

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

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

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

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

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

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

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

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

	/*store sample*/
	mch->cache_sample = gf_isom_sample_new();
	mch->cache_sample->IsRAP = sl_hdr->randomAccessPointFlag;
	mch->cache_sample->DTS = DTS + mch->frame_cts_offset;
	mch->cache_sample->CTS_Offset = (u32) (sl_hdr->compositionTimeStamp - mch->cache_seed_ts - DTS);
	mch->cache_sample->dataLength = data_size;
	mch->cache_sample->data = (char*)gf_malloc(sizeof(char)*data_size);
	memcpy(mch->cache_sample->data, data, sizeof(char)*data_size);
	return GF_OK;
}