Fix race between initial segment and instant rate change events Change-Id: I9d2153f3e4515453d07c1a1618dfacd2f3143bfb Signed-off-by: Eugene Mutavchi <Ievgen_Mutavchi@comcast.com>
diff --git a/src/third_party/starboard/rdk/shared/player/player_internal.cc b/src/third_party/starboard/rdk/shared/player/player_internal.cc index 795b0f4..392967b 100644 --- a/src/third_party/starboard/rdk/shared/player/player_internal.cc +++ b/src/third_party/starboard/rdk/shared/player/player_internal.cc
@@ -51,7 +51,8 @@ namespace player { static constexpr int kMaxNumberOfSamplesPerWrite = 1; -static const char kCustomInstantRateChange[] = "custom-instant-rate-change"; +static const char kCustomInstantRateChangeEventName[] = "custom-instant-rate-change"; +static const char kDidReceiveFirstSegmentMsgName[] = "did-receive-first-segment"; // static int Player::MaxNumberOfSamplesPerWrite() { @@ -430,6 +431,26 @@ GstPad* target_pad = gst_element_get_static_pad(src_elem, "src"); GstPad* pad = gst_ghost_pad_new(name, target_pad); + GstMessage *msg = gst_message_new_application( + GST_OBJECT(appsrc), gst_structure_new_empty(kDidReceiveFirstSegmentMsgName)); + gst_pad_add_probe ( + pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, + [](GstPad * pad, GstPadProbeInfo * info, gpointer data) -> GstPadProbeReturn { + GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info); + if ( GST_EVENT_TYPE(event) == GST_EVENT_SEGMENT ) { + GstPad *peer_pad = gst_pad_get_peer(pad); + if (peer_pad) { + gst_pad_send_event(peer_pad, gst_event_ref(event)); + gst_event_replace ( + (GstEvent **) &info->data, + gst_event_new_sink_message(kDidReceiveFirstSegmentMsgName, GST_MESSAGE(data))); + gst_object_unref(peer_pad); + } + return GST_PAD_PROBE_REMOVE; + } + return GST_PAD_PROBE_OK; + }, msg, [](gpointer data) { gst_message_unref(GST_MESSAGE(data)); }); + auto proxypad = GST_PAD(gst_proxy_pad_get_internal(GST_PROXY_PAD(pad))); gst_flow_combiner_add_pad(src->priv->flow_combiner, proxypad); gst_pad_set_chain_function(proxypad, static_cast<GstPadChainFunction>(gst_cobalt_src_chain_with_parent)); @@ -1205,7 +1226,8 @@ GstCaps* audio_caps_ { nullptr }; GstCaps* video_caps_ { nullptr }; SbTime buf_target_min_ts_ { kSbTimeMax }; - bool did_instant_rate_change_ { false }; + bool need_instant_rate_change_ { false }; + int need_first_segment_ack_ { static_cast<int>(MediaType::kBoth) }; }; struct PlayerRegistry @@ -1343,6 +1365,8 @@ has_enough_data_ &= ~static_cast<int>(MediaType::kVideo); } + need_first_segment_ack_ = has_enough_data_; + if (audio_codec_ != kSbMediaAudioCodecNone) { auto caps = CodecToGstCaps(audio_codec_, &audio_sample_info_); if (!caps.empty() && caps[0].c_str()) { @@ -2228,7 +2252,7 @@ ChangePipelineState(GST_STATE_PLAYING); } - if (rate != .0 && (rate != 1. || did_instant_rate_change_)) { + if (rate != .0 && (rate != 1. || need_instant_rate_change_)) { { ::starboard::ScopedLock lock(mutex_); if (is_seek_pending_) { @@ -2236,7 +2260,7 @@ rate_ = rate; return true; } - if (GST_STATE(pipeline_) < GST_STATE_PLAYING) { + if (GST_STATE(pipeline_) < GST_STATE_PLAYING || need_first_segment_ack_) { GST_DEBUG_OBJECT(pipeline_, "===> Set rate postponed"); pending_rate_ = rate; return true; @@ -2244,7 +2268,7 @@ pending_rate_ = .0; } - // TODO: instant rate change support + // TODO: remove special handling of amlhalasink GstElement* sink = nullptr; g_object_get(pipeline_, "audio-sink", &sink, nullptr); if (sink && g_str_has_prefix(GST_ELEMENT_NAME(sink), "amlhalasink")) { @@ -2258,15 +2282,15 @@ GST_DEBUG_OBJECT(pipeline_, "===> Sent new segment, success = %s", success ? "true" : "false"); gst_segment_free(segment); g_object_unref(sink); - - did_instant_rate_change_ |= success; } else { - GstStructure* s = gst_structure_new(kCustomInstantRateChange, "rate", G_TYPE_DOUBLE, rate, NULL); + GstStructure* s = gst_structure_new( + kCustomInstantRateChangeEventName, "rate", G_TYPE_DOUBLE, rate, NULL); success = gst_element_send_event( pipeline_, gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM_OOB, s)); - did_instant_rate_change_ |= success; } + + need_instant_rate_change_ = ( rate != 1. ); } if (success) { @@ -2343,6 +2367,19 @@ GST_INFO_OBJECT(pipeline_, "Ignore state change due to forced stop"); return false; } + + GstState current, pending; + current = pending = GST_STATE_VOID_PENDING; + gst_element_get_state(pipeline_, ¤t, &pending, 0); + if (current == state || pending == state) { + GST_DEBUG_OBJECT( + pipeline_, "Rejected state change to %s from %s with %s pending", + gst_element_state_get_name(state), + gst_element_state_get_name(current), + gst_element_state_get_name(pending)); + return true; + } + if (state == GST_STATE_PLAYING) { GstClockTime seek_pos_ns = GST_CLOCK_TIME_NONE; SbTime min_ts = kSbTimeMax; @@ -2584,6 +2621,24 @@ source_setup_id_ = -1; } } + else if (gst_structure_has_name(structure, kDidReceiveFirstSegmentMsgName)) { + if (GST_MESSAGE_SRC(message) == GST_OBJECT(audio_appsrc_) || GST_MESSAGE_SRC(message) == GST_OBJECT(video_appsrc_)) { + bool should_set_rate = false; + double rate = 0.; + auto type = GST_MESSAGE_SRC(message) == GST_OBJECT(audio_appsrc_) ? MediaType::kAudio : MediaType::kVideo; + + mutex_.Acquire(); + need_first_segment_ack_ &= ~ static_cast<int>(type); + should_set_rate = (need_first_segment_ack_ == 0 && pending_rate_ != .0 && !is_seek_pending_); + rate = pending_rate_; + mutex_.Release(); + + if (should_set_rate) { + GST_INFO("Sending pending SetRate(rate=%lf)", rate); + SetRate(rate); + } + } + } } void PlayerImpl::ConfigureLimitedVideo() {