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); } }
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); }
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); }
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; }
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); } }
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); } }
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, ¶m->url_query.start_range, ¶m->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, ¶m->url_query.next_url, NULL, ¶m->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, ¶m->url_query.next_url, ¶m->url_query.start_range, ¶m->url_query.end_range, NULL, ¶m->url_query.next_url_init_or_switch_segment, ¶m->url_query.switch_start_range , ¶m->url_query.switch_end_range, &src_url, ¶m->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; }
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); } }
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; } }
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); } }
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; }
/* 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; } }
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); } } }
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; }
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); }
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_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; }
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); } }
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; }
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; }