diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index 285d2827f9..b35bee108f 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -6590,6 +6590,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 * @@ -6748,34 +6763,43 @@ 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 (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()); + } 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)); + + TextTrackInfo textTrack = TextTrackInfo(true, schemeId); textTrack.setInstreamId(id); textTrack.setLanguage(lang); textTrack.setType("captions"); - tTracks.push_back(std::move(textTrack)); + 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()); + } } 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)); + + TextTrackInfo textTrack = TextTrackInfo(true, 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 1eb79339c4..161f4e76c3 100644 --- a/fragmentcollector_mpd.h +++ b/fragmentcollector_mpd.h @@ -560,6 +560,18 @@ 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 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); + /** * @brief Send a manifest content error event and return its status. */ diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp index bea502337a..5e80ee8ef0 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp @@ -3597,6 +3597,170 @@ R"( EXPECT_EQ(status, eAAMPSTATUS_OK); } +/* + * 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, CCMultipleAdaptation_Test1) +{ + + AAMPStatusType status; + static const char *manifest = +R"( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)"; + // Initialize MPD. The video initialization segment is cached. + 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) +{ + AAMPStatusType status; + static const char *manifest = +R"( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)"; + // Initialize MPD. The video initialization segment is cached. + 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 @@ -4505,7 +4669,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);