Beispiel #1
0
Bool MPD_CanHandleURLInService(GF_InputService *plug, const char *url)
{
	/**
	* May arrive when using pid:// URLs into a TS stream
	*/
	GF_MPD_In *mpdin = (GF_MPD_In*) plug->priv;
	GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MPD_IN] Received Can Handle URL In Service (%p) request from terminal for %s\n", mpdin->service, url));
	if (!plug || !plug->priv) return GF_SERVICE_ERROR;
	if (gf_dash_get_url(mpdin->dash) && !strcmp(gf_dash_get_url(mpdin->dash) , url)) {
		return 1;
	} else {
		GF_MPDGroup *mudta;
		GF_InputService *segment_ifce = NULL;
		u32 i;
		for (i=0;i<gf_dash_get_group_count(mpdin->dash); i++) {
			if (!gf_dash_is_group_selected(mpdin->dash, i)) continue;

			mudta = gf_dash_get_group_udta(mpdin->dash, i);
			segment_ifce = mudta->segment_ifce;
			if (mudta && mudta->segment_ifce && mudta->segment_ifce->CanHandleURLInService) {
				return mudta->segment_ifce->CanHandleURLInService(plug, url);
			}
		}
		return 0;
	}
}
Beispiel #2
0
GF_Err mpdin_dash_io_setup_from_url(GF_DASHFileIO *dashio, GF_DASHFileIOSession session, const char *url, s32 group_idx)
{
	if (group_idx>=0) {
		GF_MPD_In *mpdin = (GF_MPD_In *)dashio->udta;
		GF_MPDGroup *group = gf_dash_get_group_udta(mpdin->dash, group_idx);
		if (group && !group->netio_assigned) {
			group->netio_assigned = GF_TRUE;
			group->sess = (GF_DownloadSession *)session;
			gf_dm_sess_reassign((GF_DownloadSession *)session, 0xFFFFFFFF, mpdin_dash_segment_netio, group);
		}
	}
	return gf_dm_sess_setup_from_url((GF_DownloadSession *)session, url);
}
Beispiel #3
0
s32 MPD_GetGroupIndexForChannel(GF_MPD_In *mpdin, LPNETCHANNEL channel)
{
	u32 i;
	GF_InputService *ifce = MPD_GetInputServiceForChannel(mpdin, channel);
	if (!ifce) return -1;

	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 == ifce) return i;
	}
	return -1;
}
Beispiel #4
0
static s32 gf_dash_get_group_idx_from_service(GF_MPD_In *mpdin, GF_InputService *ifce)
{
	s32 i;

	for (i=0; (u32) 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 == ifce) {
			return i;
		}
	}
	return -1;
}
Beispiel #5
0
GF_InputService *MPD_GetInputServiceForChannel(GF_MPD_In *mpdin, LPNETCHANNEL channel)
{
	GF_Channel *ch;
	if (!channel) {
		if (gf_dash_is_group_selected(mpdin->dash, 0)) {
			GF_MPDGroup *mudta = gf_dash_get_group_udta(mpdin->dash, 0);
			return mudta ? mudta->segment_ifce : NULL;
		}
		return NULL;
	}

	ch = (GF_Channel *) channel;
	assert(ch->odm && ch->odm->OD);
	return (GF_InputService *) ch->odm->OD->service_ifce;
}
Beispiel #6
0
GF_InputService *MPD_GetInputServiceForChannel(GF_MPD_In *mpdin, LPNETCHANNEL channel)
{
	GF_Channel *ch;
	if (!channel) {
		u32 i;
		for (i=0; i<gf_dash_get_group_count(mpdin->dash); i++) {
			if (gf_dash_is_group_selectable(mpdin->dash, i)) {
				GF_MPDGroup *mudta = gf_dash_get_group_udta(mpdin->dash, i);
				if (mudta && mudta->segment_ifce) return mudta->segment_ifce;
			}
		}
		return NULL;
	}

	ch = (GF_Channel *) channel;
	assert(ch->odm && ch->odm->OD);
	return (GF_InputService *) ch->odm->OD->service_ifce;
}
Beispiel #7
0
static GF_Descriptor *MPD_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url)
{
	u32 i;
	GF_MPD_In *mpdin = (GF_MPD_In*) plug->priv;
    GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MPD_IN] Received Service Description request from terminal for %s\n", sub_url));
	for (i=0; i<gf_dash_get_group_count(mpdin->dash); i++) {
		GF_Descriptor *desc;
		GF_MPDGroup *mudta;
		if (!gf_dash_is_group_selected(mpdin->dash, i))
			continue;
		mudta = gf_dash_get_group_udta(mpdin->dash, i);
		if (!mudta) continue;
		if (mudta->service_descriptor_fetched) continue;

		desc = mudta->segment_ifce->GetServiceDescriptor(mudta->segment_ifce, expect_type, sub_url);
		if (desc) mudta->service_descriptor_fetched = 1;
		return desc;
	}
	return NULL;
}
Beispiel #8
0
GF_DASHFileIOSession mpdin_dash_io_create(GF_DASHFileIO *dashio, Bool persistent, const char *url, s32 group_idx)
{
	GF_MPDGroup *group = NULL;
	GF_DownloadSession *sess;
	u32 flags = GF_NETIO_SESSION_NOT_THREADED;
	GF_MPD_In *mpdin = (GF_MPD_In *)dashio->udta;
	if (mpdin->memory_storage)
		flags |= GF_NETIO_SESSION_MEMORY_CACHE;

	if (persistent) flags |= GF_NETIO_SESSION_PERSISTENT;

	if (group_idx>=0) {
		group = gf_dash_get_group_udta(mpdin->dash, group_idx);
	}
	if (group) {
		group->netio_assigned = GF_TRUE;
		group->sess = sess = gf_service_download_new(mpdin->service, url, flags, mpdin_dash_segment_netio, group);
	} else {
		sess = gf_service_download_new(mpdin->service, url, flags, NULL, NULL);
	}
	return (GF_DASHFileIOSession ) sess;
}
Beispiel #9
0
static GF_Err MPD_ClientQuery(GF_InputService *ifce, GF_NetworkCommand *param)
{
	u32 i;
	GF_MPD_In *mpdin = (GF_MPD_In *) ifce->proxy_udta;
    if (!param || !ifce || !ifce->proxy_udta) return GF_BAD_PARAM;

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

		mpdin->in_seek = 0;

		for (i=0; i<gf_dash_get_group_count(mpdin->dash); i++) {
			GF_MPDGroup *group;
			if (!gf_dash_is_group_selected(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);
				return GF_OK;
			}
		}
		return GF_SERVICE_ERROR;
	}

	/*gets URL and byte range of next segment - if needed, adds butstream 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 = 1;
        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.discontinuity_type = 0;
		if (mpdin->in_seek) {
			mpdin->in_seek = 0;
			param->url_query.discontinuity_type = 2;
			discard_first_cache_entry = 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;
		}

		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>=2)
				break;

			if (group_done) {
				if (!gf_dash_get_period_switch_status(mpdin->dash) && !gf_dash_in_last_period(mpdin->dash) ) {
					GF_NetworkCommand com;
					memset(&com, 0, sizeof(GF_NetworkCommand));
					com.command_type = GF_NET_BUFFER_QUERY;
					while (gf_dash_get_period_switch_status(mpdin->dash) != 1) {
						gf_term_on_command(mpdin->service, &com, GF_OK);
						if (!com.buffer.occupancy) {
							gf_dash_request_period_switch(mpdin->dash);
							break;
						}
						gf_sleep(20);
					}
				}
				return GF_EOS;
			}
            gf_sleep(16);
        }

		nb_segments_cached = gf_dash_group_get_num_segments_ready(mpdin->dash, group_idx, &group_done);
        if (nb_segments_cached < 2) {
            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)));
        }

		if (discard_first_cache_entry) {
			gf_dash_group_discard_segment(mpdin->dash, group_idx);
		}

		gf_dash_group_get_next_segment_location(mpdin->dash, group_idx, &param->url_query.next_url, &param->url_query.start_range, &param->url_query.end_range,
								&param->url_query.next_url_init_or_switch_segment, &param->url_query.switch_start_range , &param->url_query.switch_end_range,
								&src_url);

        {
            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] Switching segment playback to %s (Media Range: "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] Switching segment playback to %s\n", src_url));
			}
            GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[MPD_IN] segment start time %g sec\n", gf_dash_group_current_segment_start_time(mpdin->dash, group_idx) ));

			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MPD_IN] Waited %d ms - Elements in cache: %u/%u\n\tCache file name %s\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 ));
        }
	    return GF_OK;
    }


    return GF_OK;
}
Beispiel #10
0
GF_Err mpdin_dash_io_on_dash_event(GF_DASHFileIO *dashio, GF_DASHEventType dash_evt, 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) {
	        gf_term_on_connect(mpdin->service, NULL, error_code);
			mpdin->connection_ack_sent=1;
		}
		return GF_OK;
	}

	if (dash_evt==GF_DASH_EVENT_SELECT_GROUPS) {
		const char *opt;
		for (i=0; i<gf_dash_get_group_count(mpdin->dash); i++) {
			/*todo: select groups based on user criteria*/
			gf_dash_group_select(mpdin->dash, i, 1);
		}
		opt = gf_modules_get_option((GF_BaseInterface *)mpdin->plug, "Systems", "Language3CC");
		if (opt && strcmp(opt, "und"))
			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;
			if (!gf_dash_is_group_selected(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 {
				/*connect our media service*/
				GF_MPDGroup *group = gf_dash_get_group_udta(mpdin->dash, i);
				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 {
					u32 w, h;
					group->service_connected = 1;
					w = h = 0;
					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;
					}
				}
			}
		}

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

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

		mpdin->service->subservice_disconnect = 1;
		gf_term_on_disconnect(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;
}
Beispiel #11
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;
}
Beispiel #12
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;
}
Beispiel #13
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);
	}
}
Beispiel #14
0
GF_Err MPD_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com)
{
	s32 idx;
	GF_Err e;
	GF_MPD_In *mpdin = (GF_MPD_In*) plug->priv;
	GF_InputService *segment_ifce = NULL;

	if (!plug || !plug->priv || !com ) return GF_SERVICE_ERROR;

	segment_ifce = MPD_GetInputServiceForChannel(mpdin, com->base.on_channel);

	switch (com->command_type) {
	case GF_NET_SERVICE_INFO:
	{
		s32 idx;
		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MPD_IN] Received Info command from terminal on Service (%p)\n", mpdin->service));

		e = GF_OK;
		if (segment_ifce) {
			/* defer to the real input service */
			e = segment_ifce->ServiceCommand(segment_ifce, com);
		}

		if (e!= GF_OK || !com->info.name || 2 > strlen(com->info.name)) {
			gf_dash_get_info(mpdin->dash, &com->info.name, &com->info.comment);
		}
		idx = MPD_GetGroupIndexForChannel(mpdin, com->play.on_channel);
		if (idx>=0) {
			//gather role and co
			if (!com->info.role) {
				gf_dash_group_enum_descriptor(mpdin->dash, idx, GF_MPD_DESC_ROLE, 0, NULL, NULL, &com->info.role);
			}
			if (!com->info.accessibility) {
				gf_dash_group_enum_descriptor(mpdin->dash, idx, GF_MPD_DESC_ACCESSIBILITY, 0, NULL, NULL, &com->info.accessibility);
			}
			if (!com->info.rating) {
				gf_dash_group_enum_descriptor(mpdin->dash, idx, GF_MPD_DESC_RATING, 0, NULL, NULL, &com->info.rating);
			}
		}
		return GF_OK;
	}
	/*we could get it from MPD*/
	case GF_NET_SERVICE_HAS_AUDIO:
	case GF_NET_SERVICE_FLUSH_DATA:
		if (segment_ifce) {
			/* defer to the real input service */
			return segment_ifce->ServiceCommand(segment_ifce, com);
		}
		return GF_NOT_SUPPORTED;

	case GF_NET_SERVICE_HAS_FORCED_VIDEO_SIZE:
		com->par.width = mpdin->use_max_res ? mpdin->width : 0;
		com->par.height = mpdin->use_max_res ? mpdin->height : 0;
		return GF_OK;

	case GF_NET_SERVICE_QUALITY_SWITCH:
		if (com->switch_quality.set_auto) {
			gf_dash_set_automatic_switching(mpdin->dash, 1);
		} else if (com->base.on_channel) {
			segment_ifce = MPD_GetInputServiceForChannel(mpdin, com->base.on_channel);
			if (!segment_ifce) return GF_NOT_SUPPORTED;
			idx = MPD_GetGroupIndexForChannel(mpdin, com->play.on_channel);
			if (idx < 0) return GF_BAD_PARAM;

			gf_dash_set_automatic_switching(mpdin->dash, 0);
			gf_dash_group_select_quality(mpdin->dash, idx, com->switch_quality.ID);
		} else {
			gf_dash_switch_quality(mpdin->dash, com->switch_quality.up, mpdin->immediate_switch);
		}
		return GF_OK;

	case GF_NET_GET_TIMESHIFT:
		com->timeshift.time = gf_dash_get_timeshift_buffer_pos(mpdin->dash);
		return GF_OK;

	default:
		break;
	}
	/*not supported*/
	if (!com->base.on_channel) return GF_NOT_SUPPORTED;

	segment_ifce = MPD_GetInputServiceForChannel(mpdin, com->base.on_channel);
	if (!segment_ifce) return GF_NOT_SUPPORTED;

	switch (com->command_type) {
	case GF_NET_CHAN_INTERACTIVE:
		/* TODO - we are interactive if not live without timeshift */
		return GF_OK;

	case GF_NET_GET_STATS:
	{
		idx = MPD_GetGroupIndexForChannel(mpdin, com->base.on_channel);
		if (idx < 0) return GF_BAD_PARAM;
		com->net_stats.bw_down = 8 * gf_dash_group_get_download_rate(mpdin->dash, idx);
	}
	return GF_OK;

	case GF_NET_SERVICE_QUALITY_QUERY:
	{
		GF_DASHQualityInfo qinfo;
		GF_Err e;
		u32 count;
		idx = MPD_GetGroupIndexForChannel(mpdin, com->quality_query.on_channel);
		if (idx < 0) return GF_BAD_PARAM;
		count = gf_dash_group_get_num_qualities(mpdin->dash, idx);
		if (!com->quality_query.index) {
			com->quality_query.index = count;
			return GF_OK;
		}
		if (com->quality_query.index>count) return GF_BAD_PARAM;

		e = gf_dash_group_get_quality_info(mpdin->dash, idx, com->quality_query.index-1, &qinfo);
		if (e) return e;

		com->quality_query.bandwidth = qinfo.bandwidth;
		com->quality_query.ID = qinfo.ID;
		com->quality_query.mime = qinfo.mime;
		com->quality_query.codec = qinfo.codec;
		com->quality_query.width = qinfo.width;
		com->quality_query.height = qinfo.height;
		com->quality_query.interlaced = qinfo.interlaced;
		if (qinfo.fps_den) {
			com->quality_query.fps = qinfo.fps_num;
			com->quality_query.fps /= qinfo.fps_den;
		}
		com->quality_query.par_num = qinfo.par_num;
		com->quality_query.par_den = qinfo.par_den;
		com->quality_query.sample_rate = qinfo.sample_rate;
		com->quality_query.nb_channels = qinfo.nb_channels;
		com->quality_query.disabled = qinfo.disabled;
		com->quality_query.is_selected = qinfo.is_selected;
		com->quality_query.automatic = gf_dash_get_automatic_switching(mpdin->dash);
		return GF_OK;
	}

	break;

	case GF_NET_CHAN_BUFFER:
		/*get it from MPD minBufferTime - if not in low latency mode, indicate the value given in MPD (not possible to fetch segments earlier) - to be more precise we should get the min segment duration for this group*/
		if (!mpdin->use_low_latency && (mpdin->buffer_mode>=MPDIN_BUFFER_MIN) ) {
			u32 max = gf_dash_get_min_buffer_time(mpdin->dash);
			if (max>com->buffer.max)
				com->buffer.max = max;

			if (! gf_dash_is_dynamic_mpd(mpdin->dash)) {
				com->buffer.min = 1;
			}
		}
		return GF_OK;

	case GF_NET_CHAN_DURATION:
		com->duration.duration = gf_dash_get_duration(mpdin->dash);
		idx = MPD_GetGroupIndexForChannel(mpdin, com->play.on_channel);
		if (idx >= 0) {
			com->duration.time_shift_buffer = gf_dash_group_get_time_shift_buffer_depth(mpdin->dash, idx);
		}
		return GF_OK;

	case GF_NET_CHAN_PLAY:

		idx = MPD_GetGroupIndexForChannel(mpdin, com->play.on_channel);
		if (idx < 0) return GF_BAD_PARAM;

		//adjust play range from media timestamps to MPD time
		if (com->play.timestamp_based) {
			u32 timescale;
			u64 pto;
			Double offset;
			GF_MPDGroup *group = gf_dash_get_group_udta(mpdin->dash, idx);

			if (com->play.timestamp_based==1) {
				gf_dash_group_get_presentation_time_offset(mpdin->dash, idx, &pto, &timescale);
				offset = (Double) pto;
				offset /= timescale;
				com->play.start_range -= offset;
				if (com->play.start_range < 0) com->play.start_range = 0;
			}

			group->is_timestamp_based = 1;
			group->pto_setup = 0;
			mpdin->media_start_range = com->play.start_range;
		} else {
			GF_MPDGroup *group = gf_dash_get_group_udta(mpdin->dash, idx);
			group->is_timestamp_based = 0;
			group->pto_setup = 0;
			if (com->play.start_range<0) com->play.start_range = 0;
		}

		//we cannot handle seek request outside of a period being setup, this messes up our internal service setup
		//we postpone the seek and will request it later on ...
		if (gf_dash_in_period_setup(mpdin->dash)) {
			u64 p_end = gf_dash_get_period_duration(mpdin->dash);
			if (p_end) {
				p_end += gf_dash_get_period_start(mpdin->dash);
				if (p_end<1000*com->play.start_range) {
					mpdin->seek_request = com->play.start_range;
					return GF_OK;
				}
			}
		}


		gf_dash_set_speed(mpdin->dash, com->play.speed);

		/*don't seek if this command is the first PLAY request of objects declared by the subservice*/
		if (! mpdin->in_seek && (!com->play.initial_broadcast_play || (com->play.start_range>2.0) ) ) {
			Bool skip_seek;
			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MPD_IN] Received Play command from terminal on channel %p on Service (%p)\n", com->base.on_channel, mpdin->service));

			mpdin->in_seek = 1;

			/*if start range request is the same as previous one, don't process it
			- this happens at period switch when new objects are declared*/
			skip_seek = 0;
			if (com->play.initial_broadcast_play && (mpdin->previous_start_range==com->play.start_range))
				skip_seek = 1;

			mpdin->previous_start_range = com->play.start_range;

			if (!skip_seek) {
				if (com->play.end_range<=0) {
					u32 ms = (u32) ( 1000 * (-com->play.end_range) );
					if (ms<1000) ms = 0;
					gf_dash_set_timeshift(mpdin->dash, ms);
				}
				gf_dash_seek(mpdin->dash, com->play.start_range);

				//we have issued a seek request, mark the group as seeking
				if (mpdin->in_seek) {
					//group->in_seek = 1;
				}
				//and check if current segment playback should be aborted
				com->play.dash_segment_switch = gf_dash_group_segment_switch_forced(mpdin->dash, idx);
			}

			//to remove once we manage to keep the service alive
			/*don't forward commands if a switch of period is to be scheduled, we are killing the service anyway ...*/
			if (gf_dash_get_period_switch_status(mpdin->dash) ) return GF_OK;
		}

		gf_dash_group_select(mpdin->dash, idx, GF_TRUE);
		gf_dash_set_group_done(mpdin->dash, (u32) idx, 0);

		//adjust start range from MPD time to media time
		{
			u64 pto;
			u32 timescale;
			com->play.start_range = gf_dash_group_get_start_range(mpdin->dash, idx);
			gf_dash_group_get_presentation_time_offset(mpdin->dash, idx, &pto, &timescale);
			com->play.start_range += ((Double)pto ) / timescale;
		}

		return segment_ifce->ServiceCommand(segment_ifce, com);

	case GF_NET_CHAN_STOP:
	{
		s32 idx = MPD_GetGroupIndexForChannel(mpdin, com->play.on_channel);
		if (idx>=0) {
			gf_dash_set_group_done(mpdin->dash, (u32) idx, 1);
		}
		mpdin->previous_start_range = -1;
	}
	return segment_ifce->ServiceCommand(segment_ifce, com);

	/*we could get it from MPD*/
	case GF_NET_CHAN_GET_PIXEL_AR:
		/* defer to the real input service */
		return segment_ifce->ServiceCommand(segment_ifce, com);

	case GF_NET_CHAN_SET_SPEED:
		gf_dash_set_speed(mpdin->dash, com->play.speed);
		return segment_ifce->ServiceCommand(segment_ifce, com);

	default:
		return segment_ifce->ServiceCommand(segment_ifce, com);
	}
}
Beispiel #15
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) {
			gf_term_on_connect(mpdin->service, NULL, error_code);
			mpdin->connection_ack_sent= GF_TRUE;
		}
		return GF_OK;
	}

	if (dash_evt==GF_DASH_EVENT_SELECT_GROUPS) {
		//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
		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->connection_ack_sent) {
			gf_term_on_connect(mpdin->service, NULL, GF_OK);
			mpdin->connection_ack_sent=1;
		}
		return GF_OK;
	}

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

		mpdin->service->subservice_disconnect = 1;
		gf_term_on_disconnect(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_buffering(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;
}