From 09e9e932a2f84c8d9e3defda135246b2d082b364 Mon Sep 17 00:00:00 2001 From: aahmed878 Date: Tue, 21 Jul 2026 16:32:45 +0530 Subject: [PATCH] RDKEMW-22033: [8.5 RDK][PlayerInterface][ROGERS RTK IUV2][8.6][8.4]multiqueue Crash observed when switching between linear channels --- middleware/InterfacePlayerRDK.cpp | 46 +++++++++--- .../test/utests/fakes/FakeGStreamer.cpp | 12 +++ middleware/test/utests/mocks/MockGStreamer.h | 3 + .../InterfacePlayerFunctionTests.cpp | 75 +++++++++++++++++++ 4 files changed, 126 insertions(+), 10 deletions(-) diff --git a/middleware/InterfacePlayerRDK.cpp b/middleware/InterfacePlayerRDK.cpp index 61ab72590c..cc441fb827 100644 --- a/middleware/InterfacePlayerRDK.cpp +++ b/middleware/InterfacePlayerRDK.cpp @@ -1393,6 +1393,18 @@ void InterfacePlayerRDK::TearDownStream(int type) else if (mediaType == eGST_MEDIATYPE_SUBTITLE) { g_clear_object(&interfacePlayerPriv->gstPrivateContext->subtitle_sink); + pthread_mutex_lock(&stream->sourceLock); + if (stream->sinkbin) + { + MW_LOG_WARN("InterfacePlayerRDK::TearDownStream: CC sinkbin still assigned, clearing"); + g_clear_object(&stream->sinkbin); + } + if (stream->source) + { + MW_LOG_WARN("InterfacePlayerRDK::TearDownStream: CC source still assigned, clearing"); + g_clear_object(&stream->source); + } + pthread_mutex_unlock(&stream->sourceLock); } tearDownCb(false, mediaType); MW_LOG_MIL("InterfacePlayerRDK::TearDownStream: exit mediaType = %d", mediaType); @@ -2274,7 +2286,7 @@ int InterfacePlayerRDK::SetupStream(int streamId, void *playerInstance, std::st gst_element_add_pad(subtitlebin, gst_ghost_pad_new("sink", gst_element_get_static_pad(vipertransform, "sink"))); g_object_set(stream->sinkbin, "text-sink", subtitlebin, NULL); - interfacePlayerPriv->gstPrivateContext->subtitle_sink = textsink; + interfacePlayerPriv->gstPrivateContext->subtitle_sink = GST_ELEMENT(gst_object_ref(textsink)); MW_LOG_MIL("using rialtomsesubtitlesink muted=%d sink=%p", interfacePlayerPriv->gstPrivateContext->subtitleMuted, interfacePlayerPriv->gstPrivateContext->subtitle_sink); g_object_set(textsink, "mute", interfacePlayerPriv->gstPrivateContext->subtitleMuted ? TRUE : FALSE, NULL); } @@ -2343,7 +2355,7 @@ int InterfacePlayerRDK::SetupStream(int streamId, void *playerInstance, std::st MW_LOG_INFO("setting has-drm=false for clear HLS/TS playback"); g_object_set(vidsink, "has-drm", FALSE, NULL); } - interfacePlayerPriv->gstPrivateContext->video_sink = vidsink; + interfacePlayerPriv->gstPrivateContext->video_sink = GST_ELEMENT(gst_object_ref(vidsink)); // RDKEMW-18286: Set show-video-window=FALSE at sink creation time. // This is the earliest possible point. The Rialto delegate will queue @@ -2374,7 +2386,7 @@ int InterfacePlayerRDK::SetupStream(int streamId, void *playerInstance, std::st { MW_LOG_INFO("Created rialtomseaudiosink : %s",GST_ELEMENT_NAME(audSink)); g_object_set(stream->sinkbin, "audio-sink", audSink, NULL); - interfacePlayerPriv->gstPrivateContext->audio_sink = audSink; + interfacePlayerPriv->gstPrivateContext->audio_sink = GST_ELEMENT(gst_object_ref(audSink)); } else { @@ -3235,16 +3247,30 @@ void InterfacePlayerPriv::SendNewSegmentEvent(int type, GstClockTime startPts ,G if (gstPrivateContext->usingRialtoSink) { GstCaps *currentCaps = gst_app_src_get_caps(GST_APP_SRC(stream->source)); - GstSample *sample = gst_sample_new (nullptr, currentCaps, &segment, nullptr); - - MW_LOG_INFO("Pushing sample with segment for mediaType[%d]. start %" G_GUINT64_FORMAT " stop %" G_GUINT64_FORMAT" rate %f applied_rate %f", mediaType, segment.start, segment.stop, segment.rate, segment.applied_rate); - if (GST_FLOW_OK != gst_app_src_push_sample(GST_APP_SRC(stream->source), sample)) + if (currentCaps != NULL) { - MW_LOG_ERR("Failed to push sample with segment for mediaType[%d]", mediaType); + GstSample *sample = gst_sample_new (nullptr, currentCaps, &segment, nullptr); + if (sample != NULL) + { + MW_LOG_INFO("Pushing sample with segment for mediaType[%d]. start %" G_GUINT64_FORMAT " stop %" G_GUINT64_FORMAT" rate %f applied_rate %f", mediaType, segment.start, segment.stop, segment.rate, segment.applied_rate); + if (GST_FLOW_OK != gst_app_src_push_sample(GST_APP_SRC(stream->source), sample)) + { + MW_LOG_ERR("Failed to push sample with segment for mediaType[%d]", mediaType); + } + gst_sample_unref(sample); + } + else + { + MW_LOG_ERR("Failed to create sample for mediaType[%d]", mediaType); + } + gst_caps_unref(currentCaps); + } + else + { + MW_LOG_WARN("Cannot push segment for mediaType[%d] - caps not yet set on appsrc", mediaType); } - gst_sample_unref(sample); - gst_caps_unref(currentCaps); } + else { MW_LOG_INFO("Sending segment event for mediaType[%d]. start %" G_GUINT64_FORMAT " stop %" G_GUINT64_FORMAT" rate %f applied_rate %f", mediaType, segment.start, segment.stop, segment.rate, segment.applied_rate); diff --git a/middleware/test/utests/fakes/FakeGStreamer.cpp b/middleware/test/utests/fakes/FakeGStreamer.cpp index f1a13ee4a8..75a898fe55 100644 --- a/middleware/test/utests/fakes/FakeGStreamer.cpp +++ b/middleware/test/utests/fakes/FakeGStreamer.cpp @@ -1010,18 +1010,30 @@ GstPad * gst_ghost_pad_new (const gchar * name, GstPad * target) GstCaps *gst_app_src_get_caps(GstAppSrc *appsrc) { TRACE_FUNC(); + if (g_mockGStreamer != nullptr) + { + return g_mockGStreamer->gst_app_src_get_caps(appsrc); + } return NULL; } GstSample *gst_sample_new (GstBuffer * buffer, GstCaps * caps, const GstSegment * segment, GstStructure * info) { TRACE_FUNC(); + if (g_mockGStreamer != nullptr) + { + return g_mockGStreamer->gst_sample_new(buffer, caps, segment, info); + } return NULL; } GstFlowReturn gst_app_src_push_sample (GstAppSrc * appsrc, GstSample * sample) { TRACE_FUNC(); + if (g_mockGStreamer != nullptr) + { + return g_mockGStreamer->gst_app_src_push_sample(appsrc, sample); + } return GST_FLOW_OK; } diff --git a/middleware/test/utests/mocks/MockGStreamer.h b/middleware/test/utests/mocks/MockGStreamer.h index c6fd2d00cb..ce76262f17 100644 --- a/middleware/test/utests/mocks/MockGStreamer.h +++ b/middleware/test/utests/mocks/MockGStreamer.h @@ -85,6 +85,9 @@ class MockGStreamer MOCK_METHOD(void, gst_segment_init, (GstSegment *segment, GstFormat format)); MOCK_METHOD(GstEvent *, gst_event_new_segment, (GstSegment *segment)); MOCK_METHOD(GstEvent*, gst_event_new_custom, (GstEventType type, GstStructure* structure), ()); + MOCK_METHOD(GstCaps *, gst_app_src_get_caps, (GstAppSrc *appsrc)); + MOCK_METHOD(GstSample *, gst_sample_new, (GstBuffer *buffer, GstCaps *caps, const GstSegment *segment, GstStructure *info)); + MOCK_METHOD(GstFlowReturn, gst_app_src_push_sample, (GstAppSrc *appsrc, GstSample *sample)); MOCK_METHOD(void, gst_caps_set_simple, (GstCaps *, const char *)); MOCK_METHOD(GstBuffer*, gst_buffer_new_allocate, (GstAllocator *allocator, gsize size, GstAllocationParams *params)); MOCK_METHOD(void, gst_structure_set, (GstStructure * structure, const char * fieldname)); diff --git a/middleware/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp b/middleware/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp index f69b8230c5..2268a778b0 100644 --- a/middleware/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp +++ b/middleware/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp @@ -1915,6 +1915,81 @@ TEST_F(InterfacePlayerTests, SendNewSegmentEvent_VideoMediaType) mInterfacePrivatePlayer->SendNewSegmentEvent(mediaType, startPts, stopPts); //failure } +TEST_F(InterfacePlayerTests, SendNewSegmentEvent_RialtoSink_CapsNull) +{ + GstMediaType mediaType = eGST_MEDIATYPE_VIDEO; + GstClockTime startPts = 1000; + GstClockTime stopPts = 2000; + mPlayerContext->stream[mediaType].format = GST_FORMAT_ISO_BMFF; + mPlayerContext->usingRialtoSink = true; + + // gst_app_src_get_caps returns NULL - segment cannot be pushed + EXPECT_CALL(*g_mockGStreamer, gst_segment_init(_, GST_FORMAT_TIME)) + .Times(1); + EXPECT_CALL(*g_mockGStreamer, gst_app_src_get_caps(_)) + .WillOnce(Return(nullptr)); + + // gst_sample_new and gst_app_src_push_sample should NOT be called + EXPECT_CALL(*g_mockGStreamer, gst_sample_new(_, _, _, _)) + .Times(0); + EXPECT_CALL(*g_mockGStreamer, gst_app_src_push_sample(_, _)) + .Times(0); + + mInterfacePrivatePlayer->SendNewSegmentEvent(mediaType, startPts, stopPts); +} + +TEST_F(InterfacePlayerTests, SendNewSegmentEvent_RialtoSink_PushSampleSuccess) +{ + GstMediaType mediaType = eGST_MEDIATYPE_VIDEO; + GstClockTime startPts = 1000; + GstClockTime stopPts = 2000; + mPlayerContext->stream[mediaType].format = GST_FORMAT_ISO_BMFF; + mPlayerContext->usingRialtoSink = true; + + GstCaps fakeCaps = {}; + GstSample fakeSample = {}; + + EXPECT_CALL(*g_mockGStreamer, gst_segment_init(_, GST_FORMAT_TIME)) + .Times(1); + EXPECT_CALL(*g_mockGStreamer, gst_app_src_get_caps(_)) + .WillOnce(Return(&fakeCaps)); + EXPECT_CALL(*g_mockGStreamer, gst_sample_new(nullptr, &fakeCaps, _, nullptr)) + .WillOnce(Return(&fakeSample)); + EXPECT_CALL(*g_mockGStreamer, gst_app_src_push_sample(_, &fakeSample)) + .WillOnce(Return(GST_FLOW_OK)); + // gst_sample_unref and gst_caps_unref expand to gst_mini_object_unref + EXPECT_CALL(*g_mockGStreamer, gst_mini_object_unref(_)) + .Times(2); + + mInterfacePrivatePlayer->SendNewSegmentEvent(mediaType, startPts, stopPts); +} + +TEST_F(InterfacePlayerTests, SendNewSegmentEvent_RialtoSink_PushSampleFailure) +{ + GstMediaType mediaType = eGST_MEDIATYPE_VIDEO; + GstClockTime startPts = 1000; + GstClockTime stopPts = 2000; + mPlayerContext->stream[mediaType].format = GST_FORMAT_ISO_BMFF; + mPlayerContext->usingRialtoSink = true; + + GstCaps fakeCaps = {}; + GstSample fakeSample = {}; + + EXPECT_CALL(*g_mockGStreamer, gst_segment_init(_, GST_FORMAT_TIME)) + .Times(1); + EXPECT_CALL(*g_mockGStreamer, gst_app_src_get_caps(_)) + .WillOnce(Return(&fakeCaps)); + EXPECT_CALL(*g_mockGStreamer, gst_sample_new(nullptr, &fakeCaps, _, nullptr)) + .WillOnce(Return(&fakeSample)); + EXPECT_CALL(*g_mockGStreamer, gst_app_src_push_sample(_, &fakeSample)) + .WillOnce(Return(GST_FLOW_ERROR)); + // gst_sample_unref and gst_caps_unref still called even on push failure + EXPECT_CALL(*g_mockGStreamer, gst_mini_object_unref(_)) + .Times(2); + + mInterfacePrivatePlayer->SendNewSegmentEvent(mediaType, startPts, stopPts); +} + TEST_F(InterfacePlayerTests, Queue_and_ClearProtectionEvent) { std::string formatType = "cenc";