Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve forced subtitle handling #821

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 9 additions & 1 deletion mythtv/libs/libmythtv/captions/subtitlereader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,19 @@ void SubtitleReader::SeekFrame(int64_t ts, int flags)

bool SubtitleReader::AddAVSubtitle(AVSubtitle &subtitle,
bool fix_position,
bool is_selected_forced_track,
bool allow_forced,
bool isExternal)
bool isExternal)
{
bool enableforced = false;
bool forced = false;

if (m_avSubtitlesEnabled && is_selected_forced_track)
{
FreeAVSubtitle(subtitle);
return enableforced;
}

for (unsigned i = 0; i < subtitle.num_rects; i++)
{
forced = forced || static_cast<bool>(subtitle.rects[i]->flags & AV_SUBTITLE_FLAG_FORCED);
Expand Down
3 changes: 2 additions & 1 deletion mythtv/libs/libmythtv/captions/subtitlereader.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ class SubtitleReader : public QObject

AVSubtitles* GetAVSubtitles(void) { return &m_avSubtitles; }
bool AddAVSubtitle(AVSubtitle& subtitle, bool fix_position,
bool allow_forced, bool isExternal);
bool is_selected_forced_track, bool allow_forced,
bool isExternal);
void ClearAVSubtitles(void);
static void FreeAVSubtitle(AVSubtitle &sub);

Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythtv/captions/textsubtitleparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ int TextSubtitleParser::ReadNextSubtitle(void)
sub.start_display_time = av_q2d(m_stream->time_base) * m_pkt->dts * 1000;
sub.end_display_time = av_q2d(m_stream->time_base) * (m_pkt->dts + m_pkt->duration) * 1000;

m_parent->AddAVSubtitle(sub, m_decCtx->codec_id == AV_CODEC_ID_XSUB, false, true);
m_parent->AddAVSubtitle(sub, m_decCtx->codec_id == AV_CODEC_ID_XSUB, false, false, true);
return ret;
}

Expand Down
13 changes: 10 additions & 3 deletions mythtv/libs/libmythtv/decoders/avformatdecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3916,7 +3916,9 @@ bool AvFormatDecoder::ProcessSubtitlePacket(AVStream *curstream, AVPacket *pkt)

m_trackLock.lock();
int subIdx = m_selectedTrack[kTrackTypeSubtitle].m_av_stream_index;
bool isForcedTrack = m_selectedTrack[kTrackTypeSubtitle].m_forced;
int forcedSubIdx = m_selectedForcedTrack[kTrackTypeSubtitle].m_av_stream_index;
bool mainTrackIsForced = m_selectedTrack[kTrackTypeSubtitle].m_forced;
bool isForcedTrack = false;
m_trackLock.unlock();

int gotSubtitles = 0;
Expand All @@ -3941,7 +3943,8 @@ bool AvFormatDecoder::ProcessSubtitlePacket(AVStream *curstream, AVPacket *pkt)
}
}
}
else if (m_decodeAllSubtitles || pkt->stream_index == subIdx)
else if (m_decodeAllSubtitles || pkt->stream_index == subIdx
|| pkt->stream_index == forcedSubIdx)
{
m_avCodecLock.lock();
AVCodecContext *ctx = m_codecMap.GetCodecContext(curstream);
Expand All @@ -3950,6 +3953,9 @@ bool AvFormatDecoder::ProcessSubtitlePacket(AVStream *curstream, AVPacket *pkt)

subtitle.start_display_time += pts;
subtitle.end_display_time += pts;

if (pkt->stream_index != subIdx)
isForcedTrack = true;
}

if (gotSubtitles)
Expand All @@ -3969,7 +3975,8 @@ bool AvFormatDecoder::ProcessSubtitlePacket(AVStream *curstream, AVPacket *pkt)

bool forcedon = m_parent->GetSubReader(pkt->stream_index)->AddAVSubtitle(
subtitle, curstream->codecpar->codec_id == AV_CODEC_ID_XSUB,
m_parent->GetAllowForcedSubtitles(), false);
isForcedTrack,
(m_parent->GetAllowForcedSubtitles() && !mainTrackIsForced), false);
m_parent->EnableForcedSubtitles(forcedon || isForcedTrack);
}

Expand Down
109 changes: 81 additions & 28 deletions mythtv/libs/libmythtv/decoders/decoderbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,13 @@ int DecoderBase::SetTrack(uint Type, int TrackNo)
{
m_wantedTrack[Type] = m_tracks[Type][static_cast<size_t>(m_currentTrack[Type])];
m_selectedTrack[Type] = m_tracks[Type][static_cast<size_t>(m_currentTrack[Type])];
if (Type == kTrackTypeSubtitle)
{
// Rechoose the associated forced track, preferring the same language
int forcedTrackIndex = BestTrack(Type, true, m_selectedTrack[Type].m_language);
if (m_tracks[Type][forcedTrackIndex].m_forced)
m_selectedForcedTrack[Type] = m_tracks[Type][forcedTrackIndex];
}
}

return m_currentTrack[Type];
Expand Down Expand Up @@ -1036,6 +1043,64 @@ bool DecoderBase::InsertTrack(uint Type, const StreamInfo &Info)
return true;
}

/** \fn DecoderBase::BestTrack(uint, bool)
* \brief Determine the best track according to weights
*
* Select the best track. Primary attribute is to favor or disfavor
* a forced track. Secondary attribute is language preference,
* in order of most preferred to least preferred language.
* Third attribute is track order, preferring the earliesttrack.
*
* Whether to favor or disfavor forced is controlled by the second
* parameter.
*
* A preferredlanguage can be specified as third parameter, which
* will override the user's preferrence list.
*
* This function must not be called without taking m_trackLock
*
* \return the highest weighted track, or -1 if none.
*/
int DecoderBase::BestTrack(uint Type, bool forcedPreferred, int preferredLanguage)
{
LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Trying to select track (w/lang & %1forced)")
.arg(forcedPreferred ? "" : "!"));
const int kForcedWeight = forcedPreferred ? (1 << 20) : -(1 << 20);
const int kLanguageWeight = (1 << 10);
const int kPositionWeight = (1 << 0);
int bestScore = -1;
int selTrack = -1;
uint numStreams = static_cast<uint>(m_tracks[Type].size());

for (uint i = 0; i < numStreams; i++)
{
bool forced = (Type == kTrackTypeSubtitle &&
m_tracks[Type][i].m_forced);
int position = static_cast<int>(numStreams) - static_cast<int>(i);
int language = 0;
if (preferredLanguage != 0 && m_tracks[Type][i].m_language == preferredLanguage)
{
language = static_cast<int>(m_languagePreference.size()) + 1;
}
for (uint j = 0; (language == 0) && (j < m_languagePreference.size()); ++j)
{
if (m_tracks[Type][i].m_language == m_languagePreference[j])
language = static_cast<int>(m_languagePreference.size()) - static_cast<int>(j);
}
int score = (1 << 20) +
(kForcedWeight * static_cast<int>(forced)) +
(kLanguageWeight * language) +
(kPositionWeight * position);
if (score > bestScore)
{
bestScore = score;
selTrack = static_cast<int>(i);
}
}

return selTrack;
}

/** \fn DecoderBase::AutoSelectTrack(uint)
* \brief Select best track.
*
Expand Down Expand Up @@ -1090,36 +1155,24 @@ int DecoderBase::AutoSelectTrack(uint Type)

if (selTrack < 0)
{
// Select the best track. Primary attribute is to favor a
// forced track. Secondary attribute is language preference,
// in order of most preferred to least preferred language.
// Third attribute is track order, preferring the earliest
// track.
LOG(VB_PLAYBACK, LOG_INFO, LOC + "Trying to select track (w/lang & forced)");
const int kForcedWeight = (1 << 20);
const int kLanguageWeight = (1 << 10);
const int kPositionWeight = (1 << 0);
int bestScore = -1;
selTrack = 0;
for (uint i = 0; i < numStreams; i++)
// Find best track favoring forced.
selTrack = BestTrack(Type, true);

if (Type == kTrackTypeSubtitle)
{
bool forced = (Type == kTrackTypeSubtitle &&
m_tracks[Type][i].m_forced &&
m_parent->ForcedSubtitlesFavored());
int position = static_cast<int>(numStreams) - static_cast<int>(i);
int language = 0;
for (uint j = 0; (language == 0) && (j < m_languagePreference.size()); ++j)
{
if (m_tracks[Type][i].m_language == m_languagePreference[j])
language = static_cast<int>(m_languagePreference.size()) - static_cast<int>(j);
}
int score = (kForcedWeight * static_cast<int>(forced)) +
(kLanguageWeight * language) +
(kPositionWeight * position);
if (score > bestScore)
if (m_tracks[Type][selTrack].m_forced)
{
bestScore = score;
selTrack = static_cast<int>(i);
// A forced AV Subtitle tracks is handled without the user
// explicitly enabling subtitles. Try to find a good non-forced
// track that can be swapped to in the case the user does
// explicitly enable subtitles.
int nonForcedTrack = BestTrack(Type, false);

if (!m_tracks[Type][nonForcedTrack].m_forced)
{
m_selectedForcedTrack[Type] = m_tracks[Type][selTrack];
selTrack = nonForcedTrack;
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions mythtv/libs/libmythtv/decoders/decoderbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ class DecoderBase
static AVPixelFormat GetBestVideoFormat(AVPixelFormat* Formats, const VideoFrameTypes* RenderFormats);

protected:
int BestTrack(uint Type, bool forcedPreferred, int preferredLanguage = 0);
virtual int AutoSelectTrack(uint Type);
void AutoSelectTracks(void);
void ResetTracks(void);
Expand Down Expand Up @@ -360,6 +361,7 @@ class DecoderBase
std::array<sinfo_vec_t,kTrackTypeCount> m_tracks;
std::array<StreamInfo, kTrackTypeCount> m_wantedTrack;
std::array<StreamInfo, kTrackTypeCount> m_selectedTrack;
std::array<StreamInfo, kTrackTypeCount> m_selectedForcedTrack;

/// language preferences for auto-selection of streams
std::vector<int> m_languagePreference;
Expand Down
3 changes: 0 additions & 3 deletions mythtv/libs/libmythtv/mythplayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,6 @@ class MTV_PUBLIC MythPlayer : public QObject

// Public Audio/Subtitle/EIA-608/EIA-708 stream selection - thread safe
void EnableForcedSubtitles(bool enable);
bool ForcedSubtitlesFavored(void) const {
return m_allowForcedSubtitles && !m_captionsEnabledbyDefault;
}
// How to handle forced Subtitles (i.e. when in a movie someone speaks
// in a different language than the rest of the movie, subtitles are
// forced on even if the user doesn't have them turned on.)
Expand Down