Skip to content
Draft
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
312b2bd
VPLAY-11618 Handle empty language in HLS manifest
DomSyna Nov 19, 2025
c973919
VPLAY-11618 Handle empty language in HLS manifest
DomSyna Nov 19, 2025
8eac239
VPLAY-11719 Accept IB/OOB subtitle preference
kd-syna Nov 19, 2025
1bed13b
VPLAY-11707 Subtitles displayed for channels when subtitles are disabled
anshephe Nov 19, 2025
43bf95c
VPLAY-11618 Handle empty language in HLS manifest
DomSyna Nov 20, 2025
5c2c374
VPLAY-11618 Handle empty language in HLS manifest
DomSyna Nov 20, 2025
4a5fa53
VPLAY-11719 Add temporary test code.
kd-syna Nov 20, 2025
1ec4e04
Whitespace cleanup
anshephe Nov 20, 2025
ad79145
VPLAY-11618 Handle empty language in HLS manifest
DomSyna Nov 20, 2025
f65b983
VPLAY-11618 Handle empty language in HLS manifest
DomSyna Nov 20, 2025
5991094
VPLAY-11618 Handle empty language in HLS manifest
DomSyna Nov 20, 2025
a6a0e3f
VPLAY-11618 Handle empty language in HLS manifest
DomSyna Nov 20, 2025
b650add
Apply suggestion from @Copilot
DomSyna Nov 20, 2025
b88ce85
Apply suggestion from @Copilot
DomSyna Nov 20, 2025
4d75445
Apply suggestion from @Copilot
DomSyna Nov 20, 2025
ba9217f
Apply suggestion from @Copilot
DomSyna Nov 20, 2025
e517fe1
Apply suggestion from @Copilot
DomSyna Nov 20, 2025
d555416
Apply suggestion from @Copilot
DomSyna Nov 20, 2025
5e1c92c
Apply suggestion from @Copilot
DomSyna Nov 20, 2025
9aaf020
VPLAY-11618 Handle empty language in HLS manifest
DomSyna Nov 20, 2025
a5f81d9
Apply suggestion from @Copilot
DomSyna Nov 20, 2025
9fd722d
VPLAY-11719 test code
kd-syna Nov 20, 2025
ebcc7f0
VPLAY-11618 Handle empty language in HLS manifest
DomSyna Nov 20, 2025
f0aea51
VPLAY-11719 Typo
kd-syna Nov 20, 2025
fbf9026
Added load() call to atomic variables
anshephe Nov 20, 2025
e79d7f0
Merge branch 'dev_sprint_25_2' into feature/VPLAY-11618-text_lang
DomSyna Nov 21, 2025
4496533
Merge branch 'dev_sprint_25_2' into feature/VPLAY-11707
anshephe Nov 24, 2025
262a3d7
Apply suggestions from code review
anshephe Nov 24, 2025
af8b9bc
VPLAY-11618 Handle empty language in HLS manifest
DomSyna Nov 24, 2025
be634e1
VPLAY-11618 Handle empty language in HLS manifest
DomSyna Nov 24, 2025
35fb263
VPLAY-11618 Handle empty language in HLS manifest
DomSyna Nov 24, 2025
a2d8fd5
Updated following review comments
anshephe Nov 25, 2025
3dbeaf6
VPLAY-11618 Handle empty language in HLS manifest
DomSyna Nov 25, 2025
d104595
Update fragmentcollector_hls.cpp
DomSyna Nov 25, 2025
a87b616
VPLAY-11719 Add some logging
kd-syna Nov 25, 2025
6e4bd77
VPLAY-11719 Remove testing code
kd-syna Nov 26, 2025
b5aac2c
Merge remote-tracking branch 'origin/feature/VPLAY-11707' into featur…
anshephe Nov 27, 2025
6d51328
Merge remote-tracking branch 'origin/feature/VPLAY-11618-text_lang' i…
anshephe Nov 27, 2025
13e9b79
Merge remote-tracking branch 'origin/feature/VPLAY-11719_subtitle-sub…
kd-syna Nov 27, 2025
06623c2
Merge pull request #739 from rdkcentral/feature/RDK-59937_federated_2511
psiva01 Dec 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 128 additions & 41 deletions fragmentcollector_hls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -580,13 +580,19 @@ AAMPStatusType StreamAbstractionAAMP_HLS::ParseMainManifest()
{
mediaInfoStore.emplace_back(MediaInfo{});
ptr.ParseAttrList(ParseMediaAttributeCallback, this);
if( mediaInfoStore[mMediaCount].language.empty() )
{ // handle non-compliant manifest missing language attribute
mediaInfoStore[mMediaCount].language = mediaInfoStore[mMediaCount].name;
}
if (mediaInfoStore[mMediaCount].type == eMEDIATYPE_AUDIO && !mediaInfoStore[mMediaCount].language.empty() )
if (mediaInfoStore[mMediaCount].type == eMEDIATYPE_AUDIO)
{
mLangList.insert(GetLanguageCode(mMediaCount));
auto& audioMedia = mediaInfoStore[mMediaCount];
if (audioMedia.language.empty())
{ // handle non-compliant manifest missing language attribute
AAMPLOG_WARN("Audio track %d missing language, using name '%s' as fallback",
mMediaCount, audioMedia.name.c_str());
audioMedia.language = audioMedia.name;
}
if (!audioMedia.language.empty())
{
mLangList.insert(GetLanguageCode(mMediaCount));
}
}
mMediaCount++;
}
Expand Down Expand Up @@ -3431,28 +3437,47 @@ AAMPStatusType StreamAbstractionAAMP_HLS::Init(TuneType tuneType)

if(rate == AAMP_NORMAL_PLAY_RATE)
{
// Step 1: Configure the Audio for the playback .Get the audio index/group
// Configure the Audio for the playback .Get the audio index/group
ConfigureAudioTrack();
}

// Step 3: Based on the audio selection done , configure the profiles required
// Based on the audio selection done , configure the profiles required
ConfigureVideoProfiles();

if(rate == AAMP_NORMAL_PLAY_RATE)
{
// Step 2: Configure Subtitle track for the playback
ConfigureTextTrack();

// Generate audio and text track structures
PopulateAudioAndTextTracks();

// Select preferred text track based on user language preferences
TextTrackInfo selectedTextTrack;
if (SelectPreferredTextTrack(selectedTextTrack))
{
AAMPLOG_INFO("Selected text track - lang:%s, name:%s, rendition:%s",
selectedTextTrack.language.c_str(),
selectedTextTrack.name.c_str(),
selectedTextTrack.rendition.c_str());
aamp->SetPreferredTextTrack(std::move(selectedTextTrack));
}
else
{
AAMPLOG_WARN("No text track matched user preferences, will use default selection");
}

// Configure Subtitle track for the playback
ConfigureTextTrack();

// Check for text track changes and notify
NotifyTextTrackChanges();
Comment on lines +3453 to +3472
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The function SelectPreferredTextTrack is called before ConfigureTextTrack, and the selected track is stored via aamp->SetPreferredTextTrack(). However, ConfigureTextTrack() (line 3469) retrieves this track and converts it to currentTextTrackProfileIndex. Then NotifyTextTrackChanges() (line 3472) checks if the track changed and sends a notification.

The issue is that for a new tune (first time), aamp->mCurrentTextTrackIndex will be -1 (initial/uninitialized), and currentTextTrackProfileIndex will be set to the newly selected track index. According to the logic in NotifyTextTrackChanges() (line 7341), the condition aamp->mCurrentTextTrackIndex != -1 will be false, so no notification will be sent on the first tune. This means the first text track selection won't trigger a notification event, which may or may not be intended behavior. Consider if an initial text track selection should also trigger a notification to inform the application.

Copilot uses AI. Check for mistakes.

if(ISCONFIGSET(eAAMPConfig_useRialtoSink) && (currentTextTrackProfileIndex == -1))
{
AAMPLOG_INFO("usingRialtoSink - No default text track is selected,configure default text track for rialto");
SelectSubtitleTrack();
}
}



currentProfileIndex = GetDesiredProfile(false);
HlsStreamInfo *streamInfo = (HlsStreamInfo*)GetStreamInfo(currentProfileIndex);
long bandwidthBitsPerSecond = streamInfo->bandwidthBitsPerSecond;
Expand Down Expand Up @@ -7109,6 +7134,11 @@ void StreamAbstractionAAMP_HLS::ConfigureTextTrack()
}
}
}

if(currentTextTrackProfileIndex > -1 )
{
aamp->mIsInbandCC = mediaInfoStore[currentTextTrackProfileIndex].isCC;
}
AAMPLOG_WARN("TextTrack Selected :%d", currentTextTrackProfileIndex);
}
/**
Expand Down Expand Up @@ -7286,21 +7316,12 @@ void StreamAbstractionAAMP_HLS::PopulateAudioAndTextTracks()
{
aamp->NotifyAudioTracksChanged();
}

tracksChanged = false;
if (-1 != aamp->mCurrentTextTrackIndex && aamp->mCurrentTextTrackIndex != currentTextTrackProfileIndex)
{
tracksChanged = true;
}
aamp->mCurrentTextTrackIndex = currentTextTrackProfileIndex;
if (tracksChanged)
{
aamp->NotifyTextTracksChanged();
}

// Update closed caption track info
std::vector<TextTrackInfo> textTracksCopy;
std::copy_if(begin(mTextTracks), end(mTextTracks), back_inserter(textTracksCopy), [](const TextTrackInfo& e){return e.isCC;});
std::vector<CCTrackInfo> updatedTextTracks;
aamp->UpdateCCTrackInfo(textTracksCopy,updatedTextTracks);
aamp->UpdateCCTrackInfo(textTracksCopy, updatedTextTracks);
PlayerCCManager::GetInstance()->updateLastTextTracks(updatedTextTracks);
}
else
Expand All @@ -7311,6 +7332,25 @@ void StreamAbstractionAAMP_HLS::PopulateAudioAndTextTracks()

}

/**
* @brief Check for text track changes and send notification events
*/
void StreamAbstractionAAMP_HLS::NotifyTextTrackChanges()
{
// Check if text track has changed from a valid previous selection
bool tracksChanged = (aamp->mCurrentTextTrackIndex != -1 &&
aamp->mCurrentTextTrackIndex != currentTextTrackProfileIndex);

// Update current track index
aamp->mCurrentTextTrackIndex = currentTextTrackProfileIndex;

// Send notification if track changed
if (tracksChanged)
{
aamp->NotifyTextTracksChanged();
}
}

/**
* @brief Function to update seek position
*/
Expand Down Expand Up @@ -7538,41 +7578,88 @@ void StreamAbstractionAAMP_HLS::SelectSubtitleTrack()
AAMPLOG_INFO("using RialtoSink TextTrack Selected %d", currentTextTrackProfileIndex);
}

bool StreamAbstractionAAMP_HLS::SelectPreferredTextTrack(TextTrackInfo& selectedTextTrack)
/**
* @brief Select best text track based on user preferences
*
* Scoring algorithm:
* - Base: 1 point for any track
* - Language: (list_size - position) * AAMP_LANGUAGE_SCORE (prioritizes order)
* - Rendition: AAMP_ROLE_SCORE
* - Name: AAMP_TYPE_SCORE
*
* @param[out] selectedTextTrack The best matching track
* @return true if a track was selected, false otherwise
*/
bool StreamAbstractionAAMP_HLS::SelectPreferredTextTrack(TextTrackInfo &selectedTextTrack)
{
bool bestTrackFound = false;
unsigned long long bestScore = 0;

std::vector<TextTrackInfo> availableTracks = GetAvailableTextTracks();
if (availableTracks.empty())
{
AAMPLOG_WARN("No text tracks available");
return false;
}

unsigned long long bestScore = 0;
const auto& languageVectorToCheck = (aamp->preferredTextLanguagesList.empty()) ? aamp->preferredSubtitleLanguageVctr : aamp->preferredTextLanguagesList;

for (const auto& track : availableTracks)
for (const auto &track : availableTracks)
{
unsigned long long score = 1; // Default score for each track
unsigned long long score = 1; // Base score for any track

// Check for language match
if (!aamp->preferredTextLanguagesString.empty() && track.language == aamp->preferredTextLanguagesString)
// Score language preference (higher priority = higher score)
if (!languageVectorToCheck.empty())
{
score += AAMP_LANGUAGE_SCORE; // Add score for language match
std::string normalizedTrackLanguage =
track.language.empty() ? "" : Getiso639map_NormalizeLanguageCode(track.language, aamp->GetLangCodePreference());
AAMPLOG_TRACE("Track '%s' lang='%s' (normalized='%s')", track.name.c_str(), track.language.c_str(), normalizedTrackLanguage.c_str());

auto iter = std::find(languageVectorToCheck.cbegin(),
languageVectorToCheck.cend(),
normalizedTrackLanguage);
if (iter != languageVectorToCheck.cend())
{
size_t position = std::distance(languageVectorToCheck.cbegin(), iter);
size_t priorityMultiplier = languageVectorToCheck.size() - position;
score += priorityMultiplier * AAMP_LANGUAGE_SCORE;

AAMPLOG_TRACE("Track '%s' lang='%s' (normalized='%s') matches position %zu (bonus: %llu)",
track.name.c_str(), track.language.c_str(), normalizedTrackLanguage.c_str(),
position, priorityMultiplier * AAMP_LANGUAGE_SCORE);
}
}

if( !aamp->preferredTextRenditionString.empty() && aamp->preferredTextRenditionString.compare(track.rendition) == 0)
// Score rendition preference
if (!aamp->preferredTextRenditionString.empty() &&
aamp->preferredTextRenditionString == track.rendition)
{
score += AAMP_ROLE_SCORE; // Add score for rendition match
score += AAMP_ROLE_SCORE;
}

// Check for name match
if( !aamp->preferredTextNameString.empty() && aamp->preferredTextNameString.compare(track.name) == 0)
// Score name preference
if (!aamp->preferredTextNameString.empty() &&
aamp->preferredTextNameString == track.name)
{
score += AAMP_TYPE_SCORE; // Add score for name match
score += AAMP_TYPE_SCORE;
}
if(score > bestScore)

// Update best if this score is higher
if (score > bestScore)
{
bestTrackFound = true;
bestScore = score;
selectedTextTrack = track;

AAMPLOG_INFO("New best text track: lang=%s, rendition=%s, name=%s, score=%llu",
track.language.c_str(), track.rendition.c_str(),
track.name.c_str(), score);
}
}
return bestTrackFound;

if (bestScore == 0)
{
AAMPLOG_WARN("No suitable text track found");
}

Comment on lines +7671 to +7675
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable bestScore is initialized to 0, and the function returns (bestScore > 0). However, every track gets a base score of 1 (line 7607). This means that if there are any available tracks at all, bestScore will always be at least 1, making the check on line 7657 (if (bestScore == 0)) unreachable. The warning "No suitable text track found" will never be logged. Consider either removing this dead code or adjusting the logic if a score of 1 (base score only, with no preference matches) should be considered "no suitable track".

Suggested change
if (bestScore == 0)
{
AAMPLOG_WARN("No suitable text track found");
}

Copilot uses AI. Check for mistakes.
return (bestScore > 0);
}

/*
Expand Down
9 changes: 9 additions & 0 deletions fragmentcollector_hls.h
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,15 @@ class StreamAbstractionAAMP_HLS : public StreamAbstractionAAMP
* @return void
***************************************************************************/
void PopulateAudioAndTextTracks();

/***************************************************************************
* @fn NotifyTextTrackChanges
* @brief Check for text track changes and send notification events
*
* @return void
***************************************************************************/
void NotifyTextTrackChanges();

/***************************************************************************
* @fn ConfigureAudioTrack
*
Expand Down
Loading
Loading