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; }
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; }
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: { 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); } 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: gf_dash_switch_quality(mpdin->dash, com->switch_quality.up, mpdin->immediate_switch); 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: /* we are interactive (that's the whole point of MPD) */ return GF_OK; 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: /* Ignore the duration given by the input service and use the one given in the MPD Note: the duration of the initial segment will be 0 anyway (in MP4).*/ com->duration.duration = gf_dash_get_duration(mpdin->dash); return GF_OK; case GF_NET_CHAN_PLAY: /*don't seek if this command is the first PLAY request of objects declared by the subservice*/ if (!com->play.initial_broadcast_play) { 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)); if (!gf_dash_in_period_setup(mpdin->dash) && !com->play.dash_segment_switch && ! mpdin->in_seek) { //Bool skip_seek; 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 = (mpdin->previous_start_range==com->play.start_range) ? 1 : 0; mpdin->previous_start_range = com->play.start_range; gf_dash_seek(mpdin->dash, com->play.start_range); } /*For MPEG-2 TS or formats not using Init Seg: since objects are declared and started once the first segment is playing, we will stay in playback_start_range!=-1 until next segment (because we won't have a query_next), which will prevent seeking until then ... we force a reset of playback_start_range to allow seeking asap*/ else if (mpdin->in_seek && (com->play.start_range==0)) { // mpdin->in_seek = 0; } else if (gf_dash_in_period_setup(mpdin->dash) && (com->play.start_range==0)) { com->play.start_range = gf_dash_get_playback_start_range(mpdin->dash); } idx = MPD_GetGroupIndexForChannel(mpdin, com->play.on_channel); if (idx>=0) { gf_dash_group_select(mpdin->dash, idx, GF_TRUE); gf_dash_set_group_done(mpdin->dash, idx, 0); com->play.dash_segment_switch = gf_dash_group_segment_switch_forced(mpdin->dash, idx); } /*don't forward commands, we are killing the service anyway ...*/ if (gf_dash_get_period_switch_status(mpdin->dash) ) return GF_OK; } else { idx = MPD_GetGroupIndexForChannel(mpdin, com->play.on_channel); if (idx>=0) gf_dash_group_select(mpdin->dash, idx, GF_TRUE); com->play.start_range = gf_dash_group_get_start_range(mpdin->dash, idx); } 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); } } 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: return segment_ifce->ServiceCommand(segment_ifce, com); default: return segment_ifce->ServiceCommand(segment_ifce, com); } }
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, ×cale); 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, ×cale); 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); } }
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; }