From 2e4543a5f582de61626af323e87a4bf095a2b9ea Mon Sep 17 00:00:00 2001 From: Patrick Conduit Date: Thu, 2 Apr 2026 17:21:07 +0100 Subject: [PATCH 01/15] Initial attempt --- fragmentcollector_mpd.cpp | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index fcd2a95119..a1a866a041 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -1179,7 +1179,7 @@ bool StreamAbstractionAAMP_MPD::PushNextFragment( class MediaStreamContext *pMed { isCurrentAdCancelled = mCdaiObject->mCurAds->at(mCdaiObject->mCurAdIdx).cancelled; } - // Check if the ad fragment is within source period for live stream or cancelled ad break is within source period. + // Check if the ad fragment is within source period for live stream or cancelled ad break is within source period. // If not, skip the fragment to avoid potential issues. CheckForAdTerminate() mark EOS for stream at boundaries. if (((liveEdgePeriodPlayback || isCurrentAdCancelled) && !baselineSourcePeriodCheck)) { @@ -2748,7 +2748,7 @@ AAMPStatusType StreamAbstractionAAMP_MPD::GetMPDFromManifest( ManifestDownloadRe this->mpd = tmpMPD; // Parse for generic parameters mMPDParseHelper = mpdDnldResp->GetMPDParseHelper(); - + // this flag for current state of manifest ( Linear to VOD can happen) if((mMPDParseHelper->IsLiveManifest() != mIsLiveManifest) && !init ) { @@ -5980,25 +5980,35 @@ void StreamAbstractionAAMP_MPD::ParseTrackInformation(IAdaptationSet *adaptation while (delim != std::string::npos) { ParseCCStreamIDAndLang(value.substr(0, delim), id, lang); - AAMPLOG_WARN("StreamAbstractionAAMP_MPD: CC Track - lang:%s, isCC:1, group:%s, id:%s", - lang.c_str(), schemeId.c_str(), id.c_str()); + TextTrackInfo textTrack = TextTrackInfo(true, schemeId); textTrack.setInstreamId(id); textTrack.setLanguage(lang); textTrack.setType("captions"); - tTracks.push_back(std::move(textTrack)); + if (std::find(tTracks.begin(), tTracks.end(), textTrack) == tTracks.end()) + { + AAMPLOG_INFO("StreamAbstractionAAMP_MPD: CC Track - lang:%s, isCC:1, group:%s, id:%s", + lang.c_str(), schemeId.c_str(), id.c_str()); + // textTrack not in tTracks, add it + tTracks.push_back(std::move(textTrack)); + } value = value.substr(delim + 1); delim = value.find(';'); } ParseCCStreamIDAndLang(std::move(value), id, lang); lang = Getiso639map_NormalizeLanguageCode(lang,aamp->GetLangCodePreference()); - AAMPLOG_WARN("StreamAbstractionAAMP_MPD: CC Track - lang:%s, isCC:1, group:%s, id:%s", - lang.c_str(), schemeId.c_str(), id.c_str()); + TextTrackInfo textTrack = TextTrackInfo(true, std::move(schemeId)); textTrack.setInstreamId(id); textTrack.setLanguage(lang); textTrack.setType("captions"); - tTracks.push_back(std::move(textTrack)); + if (std::find(tTracks.begin(), tTracks.end(), textTrack) == tTracks.end()) + { + AAMPLOG_INFO("StreamAbstractionAAMP_MPD: CC Track - lang:%s, isCC:1, group:%s, id:%s", + lang.c_str(), schemeId.c_str(), id.c_str()); + // textTrack not in tTracks, add it + tTracks.push_back(std::move(textTrack)); + } } else { From ad57e30f2266296e336b59a47c1e34f01517a663 Mon Sep 17 00:00:00 2001 From: Patrick Conduit Date: Tue, 7 Apr 2026 10:47:42 +0100 Subject: [PATCH 02/15] Add L1 --- fragmentcollector_mpd.cpp | 30 ++- fragmentcollector_mpd.h | 7 + .../FunctionalTests.cpp | 173 +++++++++++++++++- 3 files changed, 201 insertions(+), 9 deletions(-) diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index a1a866a041..6c8814c4a6 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -5822,6 +5822,21 @@ Accessibility StreamAbstractionAAMP_MPD::getAccessibilityNode(void* adaptationSe return accessibilityNode; } +/** + * @fn AddIfUnique + * @brief Adds a text track to the list if it is not already in list + * @return true if the text track was added, false otherwise + */ +bool StreamAbstractionAAMP_MPD::AddIfUnique(std::vector &tTracks, TextTrackInfo& value) +{ + if (std::find(tTracks.begin(), tTracks.end(), value) == tTracks.end()) + { + tTracks.push_back(std::move(value)); + return true; + } + return false; +} + /** * @fn ParseTrackInformation * @@ -5985,12 +6000,10 @@ void StreamAbstractionAAMP_MPD::ParseTrackInformation(IAdaptationSet *adaptation textTrack.setInstreamId(id); textTrack.setLanguage(lang); textTrack.setType("captions"); - if (std::find(tTracks.begin(), tTracks.end(), textTrack) == tTracks.end()) + if (AddIfUnique(tTracks, textTrack)) { AAMPLOG_INFO("StreamAbstractionAAMP_MPD: CC Track - lang:%s, isCC:1, group:%s, id:%s", lang.c_str(), schemeId.c_str(), id.c_str()); - // textTrack not in tTracks, add it - tTracks.push_back(std::move(textTrack)); } value = value.substr(delim + 1); delim = value.find(';'); @@ -6002,22 +6015,23 @@ void StreamAbstractionAAMP_MPD::ParseTrackInformation(IAdaptationSet *adaptation textTrack.setInstreamId(id); textTrack.setLanguage(lang); textTrack.setType("captions"); - if (std::find(tTracks.begin(), tTracks.end(), textTrack) == tTracks.end()) + if (AddIfUnique(tTracks, textTrack)) { AAMPLOG_INFO("StreamAbstractionAAMP_MPD: CC Track - lang:%s, isCC:1, group:%s, id:%s", lang.c_str(), schemeId.c_str(), id.c_str()); - // textTrack not in tTracks, add it - tTracks.push_back(std::move(textTrack)); } } else { // value = empty is highly discouraged as per spec, just added as fallback - AAMPLOG_WARN("StreamAbstractionAAMP_MPD: CC Track - group:%s, isCC:1", schemeId.c_str()); + TextTrackInfo textTrack = TextTrackInfo(true, std::move(schemeId)); textTrack.setAccessibilityItem(accessibilityNode); textTrack.setAvailable(true); - tTracks.push_back(std::move(textTrack)); + if (AddIfUnique(tTracks, textTrack)) + { + AAMPLOG_INFO("StreamAbstractionAAMP_MPD: CC Track - group:%s, isCC:1", schemeId.c_str()); + } } } } diff --git a/fragmentcollector_mpd.h b/fragmentcollector_mpd.h index 938b478ee9..dcfd16a62f 100644 --- a/fragmentcollector_mpd.h +++ b/fragmentcollector_mpd.h @@ -560,6 +560,13 @@ class StreamAbstractionAAMP_MPD : public StreamAbstractionAAMP void clearFirstPTS(void) override; protected: + /** + * @fn AddIfUnique + * @brief Adds a text track to the list if it is not already in list + * @return true if the text track was added, false otherwise + */ + bool AddIfUnique(std::vector &tTracks, TextTrackInfo& value); + /** * @fn StartFromAampLocalTsb * diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp index bc54c57f7c..e802270ea6 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp @@ -2663,6 +2663,177 @@ R"( EXPECT_EQ(status, eAAMPSTATUS_OK); } +/* + * Multiple adaption sets all with the same CC language. Check that we only get one CC entry listed + * in the subtitle tracks, not 3 entries for the same language. + */ +TEST_F(FunctionalTests, CCMultipleAdaption_Test1) +{ + std::string fragmentUrl; + AAMPStatusType status; + static const char *manifest = +R"( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)"; + // Initialize MPD. The video initialization segment is cached. + fragmentUrl = std::string(TEST_BASE_URL) + std::string("manifest/track-video-repid-video00-tc-0-header.mp4"); + //EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + // .WillOnce(Return(true)); + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + status = InitializeMPD(manifest); + std::vector textTracks = mStreamAbstractionAAMP_MPD->GetAvailableTextTracks(); + //To verify whether the CC attribute parsed from video + ASSERT_EQ(1,textTracks.size()); + EXPECT_EQ(status, eAAMPSTATUS_OK); +} + +/* + * Multiple adaption sets 2x CC languages. Check that we only get one CC entry listed for each language + */ +TEST_F(FunctionalTests, CCMultipleAdaption_Test2) +{ + std::string fragmentUrl; + AAMPStatusType status; + static const char *manifest = +R"( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)"; + // Initialize MPD. The video initialization segment is cached. + fragmentUrl = std::string(TEST_BASE_URL) + std::string("manifest/track-video-repid-video00-tc-0-header.mp4"); + //EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + // .WillOnce(Return(true)); + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + status = InitializeMPD(manifest); + std::vector textTracks = mStreamAbstractionAAMP_MPD->GetAvailableTextTracks(); + //To verify whether the CC attribute parsed from video + ASSERT_EQ(2,textTracks.size()); + ASSERT_EQ("en",textTracks[0].language); + ASSERT_EQ("fr",textTracks[1].language); + EXPECT_EQ(status, eAAMPSTATUS_OK); +} /* * @brief Test to make sure ChunkMode is turned off for non-LLD streams. * This test checks that ChunkMode (used for lld) is not @@ -3569,7 +3740,7 @@ TEST_F(StreamAbstractionAAMP_MPDTest, Stop_NotifiesVideoTsbWaiters) mStreamAbstractionAAMP_MPD->InitTsbReader(eTUNETYPE_SEEKTOLIVE); EXPECT_CALL(*g_mockPrivateInstanceAAMP, IsLocalAAMPTsbInjection()).WillOnce(Return(true)); - // Start the TSB reader thread + // Start the TSB reader thread mStreamAbstractionAAMP_MPD->Start(); EXPECT_CALL(*g_mockTSBSessionManager, NotifyVideoTsbWaiters()).Times(1); From d4ab4978e046ad1b47ca3daf71d5eb9c77692bbf Mon Sep 17 00:00:00 2001 From: Patrick Conduit Date: Tue, 7 Apr 2026 11:06:06 +0100 Subject: [PATCH 03/15] Remove unused --- .../tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp index e802270ea6..7f0a22d71c 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp @@ -2669,7 +2669,7 @@ R"( */ TEST_F(FunctionalTests, CCMultipleAdaption_Test1) { - std::string fragmentUrl; + AAMPStatusType status; static const char *manifest = R"( @@ -2734,9 +2734,6 @@ R"( )"; // Initialize MPD. The video initialization segment is cached. - fragmentUrl = std::string(TEST_BASE_URL) + std::string("manifest/track-video-repid-video00-tc-0-header.mp4"); - //EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) - // .WillOnce(Return(true)); EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) .WillRepeatedly(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); @@ -2752,7 +2749,6 @@ R"( */ TEST_F(FunctionalTests, CCMultipleAdaption_Test2) { - std::string fragmentUrl; AAMPStatusType status; static const char *manifest = R"( @@ -2820,9 +2816,6 @@ R"( )"; // Initialize MPD. The video initialization segment is cached. - fragmentUrl = std::string(TEST_BASE_URL) + std::string("manifest/track-video-repid-video00-tc-0-header.mp4"); - //EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) - // .WillOnce(Return(true)); EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) .WillRepeatedly(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); From 2a6b1762fd7a3ac632fe8deba2e16ba2a1197de8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 10:13:32 +0000 Subject: [PATCH 04/15] Fix empty schemeId in log messages after std::move Agent-Logs-Url: https://github.com/rdkcentral/aamp/sessions/60a3eee3-2d5d-4874-8707-f5ebf04fef2d Co-authored-by: cpc005 <61162093+cpc005@users.noreply.github.com> --- fragmentcollector_mpd.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index 6c8814c4a6..a8c732ac74 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -6011,21 +6011,21 @@ void StreamAbstractionAAMP_MPD::ParseTrackInformation(IAdaptationSet *adaptation ParseCCStreamIDAndLang(std::move(value), id, lang); lang = Getiso639map_NormalizeLanguageCode(lang,aamp->GetLangCodePreference()); - TextTrackInfo textTrack = TextTrackInfo(true, std::move(schemeId)); + TextTrackInfo textTrack = TextTrackInfo(true, schemeId); textTrack.setInstreamId(id); textTrack.setLanguage(lang); textTrack.setType("captions"); if (AddIfUnique(tTracks, textTrack)) { AAMPLOG_INFO("StreamAbstractionAAMP_MPD: CC Track - lang:%s, isCC:1, group:%s, id:%s", - lang.c_str(), schemeId.c_str(), id.c_str()); + lang.c_str(), schemeId.c_str(), id.c_str()); } } else { // value = empty is highly discouraged as per spec, just added as fallback - TextTrackInfo textTrack = TextTrackInfo(true, std::move(schemeId)); + TextTrackInfo textTrack = TextTrackInfo(true, schemeId); textTrack.setAccessibilityItem(accessibilityNode); textTrack.setAvailable(true); if (AddIfUnique(tTracks, textTrack)) From 9168ddd34e9f8660c15b1741a0c5c5b400f6f555 Mon Sep 17 00:00:00 2001 From: pstroffolino Date: Mon, 13 Apr 2026 15:33:07 -0400 Subject: [PATCH 05/15] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp index 7f0a22d71c..2bd8389efb 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp @@ -2664,10 +2664,10 @@ R"( } /* - * Multiple adaption sets all with the same CC language. Check that we only get one CC entry listed + * Multiple adaptation sets all with the same CC language. Check that we only get one CC entry listed * in the subtitle tracks, not 3 entries for the same language. */ -TEST_F(FunctionalTests, CCMultipleAdaption_Test1) +TEST_F(FunctionalTests, CCMultipleAdaptation_Test1) { AAMPStatusType status; From a803daa855596386c6fc1a1ffdc888450036a912 Mon Sep 17 00:00:00 2001 From: cpc005 <61162093+cpc005@users.noreply.github.com> Date: Mon, 3 Aug 2026 13:32:00 +0100 Subject: [PATCH 06/15] Update fragmentcollector_mpd.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- fragmentcollector_mpd.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fragmentcollector_mpd.h b/fragmentcollector_mpd.h index dcfd16a62f..06212241ad 100644 --- a/fragmentcollector_mpd.h +++ b/fragmentcollector_mpd.h @@ -562,10 +562,15 @@ class StreamAbstractionAAMP_MPD : public StreamAbstractionAAMP protected: /** * @fn AddIfUnique - * @brief Adds a text track to the list if it is not already in list - * @return true if the text track was added, false otherwise + * @brief Adds a text track to the list if it is not already present. + * + * @param[in,out] tTracks Collection of text tracks to update. + * @param[in] value Text track passed by value. The function may move from + * this local copy when inserting into @p tTracks. + * + * @return true if the text track was added, false otherwise. */ - bool AddIfUnique(std::vector &tTracks, TextTrackInfo& value); + bool AddIfUnique(std::vector &tTracks, TextTrackInfo value); /** * @fn StartFromAampLocalTsb From 17e6ca9ba11044f7ea110b1f8282b5e086381789 Mon Sep 17 00:00:00 2001 From: Patrick Conduit Date: Mon, 3 Aug 2026 14:02:19 +0100 Subject: [PATCH 07/15] review comments --- fragmentcollector_mpd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index 32fc6620df..6d46d306a8 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -6416,7 +6416,7 @@ Accessibility StreamAbstractionAAMP_MPD::getAccessibilityNode(void* adaptationSe * @brief Adds a text track to the list if it is not already in list * @return true if the text track was added, false otherwise */ -bool StreamAbstractionAAMP_MPD::AddIfUnique(std::vector &tTracks, TextTrackInfo& value) +bool StreamAbstractionAAMP_MPD::AddIfUnique(std::vector &tTracks, TextTrackInfo value) { if (std::find(tTracks.begin(), tTracks.end(), value) == tTracks.end()) { From 04f3db42e73612d1a009481f2a1ecd2ef040548a Mon Sep 17 00:00:00 2001 From: Patrick Conduit Date: Thu, 2 Apr 2026 17:21:07 +0100 Subject: [PATCH 08/15] Initial attempt --- fragmentcollector_mpd.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index a994c4bea3..9eb2f1ec2b 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -6724,25 +6724,35 @@ void StreamAbstractionAAMP_MPD::ParseTrackInformation(IAdaptationSet *adaptation while (delim != std::string::npos) { ParseCCStreamIDAndLang(value.substr(0, delim), id, lang); - AAMPLOG_WARN("StreamAbstractionAAMP_MPD: CC Track - lang:%s, isCC:1, group:%s, id:%s", - lang.c_str(), schemeId.c_str(), id.c_str()); + TextTrackInfo textTrack = TextTrackInfo(true, schemeId); textTrack.setInstreamId(id); textTrack.setLanguage(lang); textTrack.setType("captions"); - tTracks.push_back(std::move(textTrack)); + if (std::find(tTracks.begin(), tTracks.end(), textTrack) == tTracks.end()) + { + AAMPLOG_INFO("StreamAbstractionAAMP_MPD: CC Track - lang:%s, isCC:1, group:%s, id:%s", + lang.c_str(), schemeId.c_str(), id.c_str()); + // textTrack not in tTracks, add it + tTracks.push_back(std::move(textTrack)); + } value = value.substr(delim + 1); delim = value.find(';'); } ParseCCStreamIDAndLang(std::move(value), id, lang); lang = Getiso639map_NormalizeLanguageCode(lang,aamp->GetLangCodePreference()); - AAMPLOG_WARN("StreamAbstractionAAMP_MPD: CC Track - lang:%s, isCC:1, group:%s, id:%s", - lang.c_str(), schemeId.c_str(), id.c_str()); + TextTrackInfo textTrack = TextTrackInfo(true, std::move(schemeId)); textTrack.setInstreamId(id); textTrack.setLanguage(lang); textTrack.setType("captions"); - tTracks.push_back(std::move(textTrack)); + if (std::find(tTracks.begin(), tTracks.end(), textTrack) == tTracks.end()) + { + AAMPLOG_INFO("StreamAbstractionAAMP_MPD: CC Track - lang:%s, isCC:1, group:%s, id:%s", + lang.c_str(), schemeId.c_str(), id.c_str()); + // textTrack not in tTracks, add it + tTracks.push_back(std::move(textTrack)); + } } else { From 05a4ca3a3a61a063140187537a711e8a2afd438a Mon Sep 17 00:00:00 2001 From: Patrick Conduit Date: Tue, 7 Apr 2026 10:47:42 +0100 Subject: [PATCH 09/15] Add L1 --- fragmentcollector_mpd.cpp | 30 ++- fragmentcollector_mpd.h | 7 + .../FunctionalTests.cpp | 173 +++++++++++++++++- 3 files changed, 201 insertions(+), 9 deletions(-) diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index 9eb2f1ec2b..13412cb286 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -6566,6 +6566,21 @@ Accessibility StreamAbstractionAAMP_MPD::getAccessibilityNode(void* adaptationSe return accessibilityNode; } +/** + * @fn AddIfUnique + * @brief Adds a text track to the list if it is not already in list + * @return true if the text track was added, false otherwise + */ +bool StreamAbstractionAAMP_MPD::AddIfUnique(std::vector &tTracks, TextTrackInfo& value) +{ + if (std::find(tTracks.begin(), tTracks.end(), value) == tTracks.end()) + { + tTracks.push_back(std::move(value)); + return true; + } + return false; +} + /** * @fn ParseTrackInformation * @@ -6729,12 +6744,10 @@ void StreamAbstractionAAMP_MPD::ParseTrackInformation(IAdaptationSet *adaptation textTrack.setInstreamId(id); textTrack.setLanguage(lang); textTrack.setType("captions"); - if (std::find(tTracks.begin(), tTracks.end(), textTrack) == tTracks.end()) + if (AddIfUnique(tTracks, textTrack)) { AAMPLOG_INFO("StreamAbstractionAAMP_MPD: CC Track - lang:%s, isCC:1, group:%s, id:%s", lang.c_str(), schemeId.c_str(), id.c_str()); - // textTrack not in tTracks, add it - tTracks.push_back(std::move(textTrack)); } value = value.substr(delim + 1); delim = value.find(';'); @@ -6746,22 +6759,23 @@ void StreamAbstractionAAMP_MPD::ParseTrackInformation(IAdaptationSet *adaptation textTrack.setInstreamId(id); textTrack.setLanguage(lang); textTrack.setType("captions"); - if (std::find(tTracks.begin(), tTracks.end(), textTrack) == tTracks.end()) + if (AddIfUnique(tTracks, textTrack)) { AAMPLOG_INFO("StreamAbstractionAAMP_MPD: CC Track - lang:%s, isCC:1, group:%s, id:%s", lang.c_str(), schemeId.c_str(), id.c_str()); - // textTrack not in tTracks, add it - tTracks.push_back(std::move(textTrack)); } } else { // value = empty is highly discouraged as per spec, just added as fallback - AAMPLOG_WARN("StreamAbstractionAAMP_MPD: CC Track - group:%s, isCC:1", schemeId.c_str()); + TextTrackInfo textTrack = TextTrackInfo(true, std::move(schemeId)); textTrack.setAccessibilityItem(accessibilityNode); textTrack.setAvailable(true); - tTracks.push_back(std::move(textTrack)); + if (AddIfUnique(tTracks, textTrack)) + { + AAMPLOG_INFO("StreamAbstractionAAMP_MPD: CC Track - group:%s, isCC:1", schemeId.c_str()); + } } } } diff --git a/fragmentcollector_mpd.h b/fragmentcollector_mpd.h index b2b120147a..16628e770f 100644 --- a/fragmentcollector_mpd.h +++ b/fragmentcollector_mpd.h @@ -560,6 +560,13 @@ class StreamAbstractionAAMP_MPD : public StreamAbstractionAAMP void clearFirstPTS(void) override; protected: + /** + * @fn AddIfUnique + * @brief Adds a text track to the list if it is not already in list + * @return true if the text track was added, false otherwise + */ + bool AddIfUnique(std::vector &tTracks, TextTrackInfo& value); + /** * @fn StartFromAampLocalTsb * diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp index 0308bf0e41..eb9da95113 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp @@ -3136,6 +3136,177 @@ R"( EXPECT_EQ(status, eAAMPSTATUS_OK); } +/* + * Multiple adaption sets all with the same CC language. Check that we only get one CC entry listed + * in the subtitle tracks, not 3 entries for the same language. + */ +TEST_F(FunctionalTests, CCMultipleAdaption_Test1) +{ + std::string fragmentUrl; + AAMPStatusType status; + static const char *manifest = +R"( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)"; + // Initialize MPD. The video initialization segment is cached. + fragmentUrl = std::string(TEST_BASE_URL) + std::string("manifest/track-video-repid-video00-tc-0-header.mp4"); + //EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + // .WillOnce(Return(true)); + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + status = InitializeMPD(manifest); + std::vector textTracks = mStreamAbstractionAAMP_MPD->GetAvailableTextTracks(); + //To verify whether the CC attribute parsed from video + ASSERT_EQ(1,textTracks.size()); + EXPECT_EQ(status, eAAMPSTATUS_OK); +} + +/* + * Multiple adaption sets 2x CC languages. Check that we only get one CC entry listed for each language + */ +TEST_F(FunctionalTests, CCMultipleAdaption_Test2) +{ + std::string fragmentUrl; + AAMPStatusType status; + static const char *manifest = +R"( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)"; + // Initialize MPD. The video initialization segment is cached. + fragmentUrl = std::string(TEST_BASE_URL) + std::string("manifest/track-video-repid-video00-tc-0-header.mp4"); + //EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + // .WillOnce(Return(true)); + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + status = InitializeMPD(manifest); + std::vector textTracks = mStreamAbstractionAAMP_MPD->GetAvailableTextTracks(); + //To verify whether the CC attribute parsed from video + ASSERT_EQ(2,textTracks.size()); + ASSERT_EQ("en",textTracks[0].language); + ASSERT_EQ("fr",textTracks[1].language); + EXPECT_EQ(status, eAAMPSTATUS_OK); +} /* * @brief Test to make sure ChunkMode is turned off for non-LLD streams. * This test checks that ChunkMode (used for lld) is not @@ -4042,7 +4213,7 @@ TEST_F(StreamAbstractionAAMP_MPDTest, Stop_NotifiesVideoTsbWaiters) mStreamAbstractionAAMP_MPD->InitTsbReader(eTUNETYPE_SEEKTOLIVE); EXPECT_CALL(*g_mockPrivateInstanceAAMP, IsLocalAAMPTsbInjection()).WillOnce(Return(true)); - // Start the TSB reader thread + // Start the TSB reader thread mStreamAbstractionAAMP_MPD->Start(); EXPECT_CALL(*g_mockTSBSessionManager, NotifyVideoTsbWaiters()).Times(1); From 223521861407851aeea35040cd601c3a9324aa7d Mon Sep 17 00:00:00 2001 From: Patrick Conduit Date: Tue, 7 Apr 2026 11:06:06 +0100 Subject: [PATCH 10/15] Remove unused --- .../tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp index eb9da95113..3b1281f4d5 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp @@ -3142,7 +3142,7 @@ R"( */ TEST_F(FunctionalTests, CCMultipleAdaption_Test1) { - std::string fragmentUrl; + AAMPStatusType status; static const char *manifest = R"( @@ -3207,9 +3207,6 @@ R"( )"; // Initialize MPD. The video initialization segment is cached. - fragmentUrl = std::string(TEST_BASE_URL) + std::string("manifest/track-video-repid-video00-tc-0-header.mp4"); - //EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) - // .WillOnce(Return(true)); EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) .WillRepeatedly(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); @@ -3225,7 +3222,6 @@ R"( */ TEST_F(FunctionalTests, CCMultipleAdaption_Test2) { - std::string fragmentUrl; AAMPStatusType status; static const char *manifest = R"( @@ -3293,9 +3289,6 @@ R"( )"; // Initialize MPD. The video initialization segment is cached. - fragmentUrl = std::string(TEST_BASE_URL) + std::string("manifest/track-video-repid-video00-tc-0-header.mp4"); - //EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) - // .WillOnce(Return(true)); EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) .WillRepeatedly(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); From 9ab41e7cc92423b59c5a999648d568b103e4649a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 10:13:32 +0000 Subject: [PATCH 11/15] Fix empty schemeId in log messages after std::move Agent-Logs-Url: https://github.com/rdkcentral/aamp/sessions/60a3eee3-2d5d-4874-8707-f5ebf04fef2d Co-authored-by: cpc005 <61162093+cpc005@users.noreply.github.com> --- fragmentcollector_mpd.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index 13412cb286..7f40ecb4ed 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -6755,21 +6755,21 @@ void StreamAbstractionAAMP_MPD::ParseTrackInformation(IAdaptationSet *adaptation ParseCCStreamIDAndLang(std::move(value), id, lang); lang = Getiso639map_NormalizeLanguageCode(lang,aamp->GetLangCodePreference()); - TextTrackInfo textTrack = TextTrackInfo(true, std::move(schemeId)); + TextTrackInfo textTrack = TextTrackInfo(true, schemeId); textTrack.setInstreamId(id); textTrack.setLanguage(lang); textTrack.setType("captions"); if (AddIfUnique(tTracks, textTrack)) { AAMPLOG_INFO("StreamAbstractionAAMP_MPD: CC Track - lang:%s, isCC:1, group:%s, id:%s", - lang.c_str(), schemeId.c_str(), id.c_str()); + lang.c_str(), schemeId.c_str(), id.c_str()); } } else { // value = empty is highly discouraged as per spec, just added as fallback - TextTrackInfo textTrack = TextTrackInfo(true, std::move(schemeId)); + TextTrackInfo textTrack = TextTrackInfo(true, schemeId); textTrack.setAccessibilityItem(accessibilityNode); textTrack.setAvailable(true); if (AddIfUnique(tTracks, textTrack)) From 62e6c93f7ee0fa4a182a6811cd7739f8290032fd Mon Sep 17 00:00:00 2001 From: pstroffolino Date: Mon, 13 Apr 2026 15:33:07 -0400 Subject: [PATCH 12/15] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp index 3b1281f4d5..5dc71ea8df 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp @@ -3137,10 +3137,10 @@ R"( } /* - * Multiple adaption sets all with the same CC language. Check that we only get one CC entry listed + * Multiple adaptation sets all with the same CC language. Check that we only get one CC entry listed * in the subtitle tracks, not 3 entries for the same language. */ -TEST_F(FunctionalTests, CCMultipleAdaption_Test1) +TEST_F(FunctionalTests, CCMultipleAdaptation_Test1) { AAMPStatusType status; From b09b391f57eaa729d10c5aac8acf697f659b909c Mon Sep 17 00:00:00 2001 From: cpc005 <61162093+cpc005@users.noreply.github.com> Date: Mon, 3 Aug 2026 13:32:00 +0100 Subject: [PATCH 13/15] Update fragmentcollector_mpd.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- fragmentcollector_mpd.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fragmentcollector_mpd.h b/fragmentcollector_mpd.h index 16628e770f..79b5545122 100644 --- a/fragmentcollector_mpd.h +++ b/fragmentcollector_mpd.h @@ -562,10 +562,15 @@ class StreamAbstractionAAMP_MPD : public StreamAbstractionAAMP protected: /** * @fn AddIfUnique - * @brief Adds a text track to the list if it is not already in list - * @return true if the text track was added, false otherwise + * @brief Adds a text track to the list if it is not already present. + * + * @param[in,out] tTracks Collection of text tracks to update. + * @param[in] value Text track passed by value. The function may move from + * this local copy when inserting into @p tTracks. + * + * @return true if the text track was added, false otherwise. */ - bool AddIfUnique(std::vector &tTracks, TextTrackInfo& value); + bool AddIfUnique(std::vector &tTracks, TextTrackInfo value); /** * @fn StartFromAampLocalTsb From 61227793022289547a938a1359a2f387c5b0e827 Mon Sep 17 00:00:00 2001 From: Patrick Conduit Date: Mon, 3 Aug 2026 14:02:19 +0100 Subject: [PATCH 14/15] review comments --- fragmentcollector_mpd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index 7f40ecb4ed..10250ed456 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -6571,7 +6571,7 @@ Accessibility StreamAbstractionAAMP_MPD::getAccessibilityNode(void* adaptationSe * @brief Adds a text track to the list if it is not already in list * @return true if the text track was added, false otherwise */ -bool StreamAbstractionAAMP_MPD::AddIfUnique(std::vector &tTracks, TextTrackInfo& value) +bool StreamAbstractionAAMP_MPD::AddIfUnique(std::vector &tTracks, TextTrackInfo value) { if (std::find(tTracks.begin(), tTracks.end(), value) == tTracks.end()) { From cafbe40e4d31f2406d0310db57f2af72a0035f09 Mon Sep 17 00:00:00 2001 From: Patrick Conduit Date: Wed, 21 Oct 2026 15:29:12 +0100 Subject: [PATCH 15/15] Fix merge error --- fragmentcollector_mpd.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fragmentcollector_mpd.h b/fragmentcollector_mpd.h index 35a1172bf8..161f4e76c3 100644 --- a/fragmentcollector_mpd.h +++ b/fragmentcollector_mpd.h @@ -571,6 +571,8 @@ class StreamAbstractionAAMP_MPD : public StreamAbstractionAAMP * @return true if the text track was added, false otherwise. */ bool AddIfUnique(std::vector &tTracks, TextTrackInfo value); + + /** * @brief Send a manifest content error event and return its status. */ AAMPStatusType ReportManifestContentError();