5#include "libmythbase/mythconfig.h"
15#define LOC QString("Dec: ")
18 : m_parent(parent), m_playbackInfo(new
ProgramInfo(pginfo)),
19 m_audio(m_parent->GetAudio()),
37 if (RenderFormats !=
nullptr)
49 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
50 QString(
"Reset: Video %1, Seek %2, File %3")
51 .arg(reset_video_data).arg(seek_reset).arg(reset_file));
107 if (m_fps < 26 && m_fps > 24)
117 if (m_fps < 26 && m_fps > 24)
123 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
124 QString(
"%1 TotalTimeOfTitle() in ticks, %2 TotalReadPosition() "
125 "in bytes, %3 is fps")
149 if (m_fps < 26 && m_fps > 24)
181 for (
auto it = posMap.cbegin(); it != posMap.cend(); ++it)
192 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
193 QString(
"Position map filled from DB to: %1")
198 for (
auto it = durMap.cbegin(); it != durMap.cend(); ++it)
207 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
208 QString(
"Duration map filled from DB to: %1").arg(last));
227 unsigned long long start = 0;
244 long long last_index = 0;
247 for (
auto it = posMap.cbegin(); it != posMap.cend(); ++it)
249 if (it.key() <= last_index)
261 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
262 QString(
"Position map filled from Encoder to: %1")
271 last_index = it.key();
273 for (frm_pos_map_t::const_iterator it = durMap.cbegin();
274 it != durMap.cend(); ++it)
276 if (!isEmpty && it.key() <= last_index)
286 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
287 QString(
"Duration map filled from Encoder to: %1").arg(it.key()));
323 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
324 QString(
"Resyncing position map. posmapStarted = %1"
325 " livetv(%2) watchingRec(%3)")
332 unsigned long new_posmap_size = old_posmap_size;
341 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
342 QString(
"SyncPositionMap watchingrecording, from DB: "
343 "%1 entries") .arg(new_posmap_size));
348 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
349 QString(
"SyncPositionMap watchingrecording no entries "
350 "from encoder, try DB"));
355 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
356 QString(
"SyncPositionMap watchingrecording total: %1 entries")
357 .arg(new_posmap_size));
367 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
368 QString(
"SyncPositionMap prerecorded, from DB: %1 entries")
369 .arg(new_posmap_size));
373 bool ret_val = new_posmap_size > old_posmap_size;
377 long long totframes = 0;
378 std::chrono::seconds length = 0s;
404 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
405 QString(
"SyncPositionMap, new totframes: %1, new length: %2, "
407 .arg(totframes).arg(length.count()).arg(new_posmap_size));
420 int &lower_bound,
int &upper_bound)
425 long long lower = -1;
426 long long upper = size;
431 while (upper - 1 > lower)
433 long long i = (upper + lower) / 2;
439 if (value == desired_value)
445 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
446 QString(
"FindPosition(%1, search%2 adjusted)")
447 .arg(desired_value).arg((search_adjusted) ?
"" :
" not") +
448 QString(
" --> [%1:%2(%3)]")
454 if (value > desired_value)
463 while (lower >= 0 &&
m_positionMap[lower].adjFrame > desired_value)
465 while (upper < size &&
m_positionMap[upper].adjFrame < desired_value)
473 while (upper < size &&
478 lower = std::max(lower, 0LL);
479 upper = std::min(upper, size - 1LL);
485 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
486 QString(
"FindPosition(%1, search%3 adjusted)")
487 .arg(desired_value).arg((search_adjusted) ?
"" :
" not") +
488 QString(
" --> \n\t\t\t[%1:%2(%3),%4:%5(%6)]")
517 if (entry.index < first)
519 if (entry.index > last)
522 posMap[entry.index] = entry.pos;
529 if (it.key() < first)
533 durMap[it.key()] = it.value();
543 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
544 QString(
"Saving position map [%1,%2] w/%3 keyframes, "
545 "took (%4,%5,%6) ms")
546 .arg(first).arg(last).arg(saved)
557 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
558 QString(
"DoRewind(%1 (%2), %3 discard frames)")
560 .arg((discardFrames) ?
"do" :
"don't"));
573 normalframes = std::max(normalframes, 0);
595 LOG(VB_GENERAL, LOG_ERR,
LOC +
"PosMap is empty, can't seek");
601 LOG(VB_GENERAL, LOG_ERR,
LOC +
"No ringBuffer yet, can't seek");
615 int pos_idx = pre_idx;
619 GetKey(e_post) - desiredFrame <= desiredFrame -
GetKey(e_pre))
658 long long last_frame = 0;
671 if (desiredFrame < 0)
676 if (desiredFrame < last_frame)
679 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
680 "ConditionallyUpdatePosMap: Not enough info in positionMap," +
681 QString(
"\n\t\t\twe need frame %1 but highest we have is %2.")
682 .arg(desiredFrame).arg(last_frame));
688 if (desiredFrame > last_frame)
690 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
691 "ConditionallyUpdatePosMap: Still not "
692 "enough info in positionMap after sync, " +
693 QString(
"\n\t\t\twe need frame %1 but highest we have "
694 "is %2. Will attempt to seek frame-by-frame")
695 .arg(desiredFrame).arg(last_frame));
712 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
713 QString(
"DoFastForward(%1 (%2), %3 discard frames)")
715 .arg((discardFrames) ?
"do" :
"don't"));
719 LOG(VB_GENERAL, LOG_ERR,
LOC +
"No ringBuffer yet, can't fast forward");
735 return DoRewind(desiredFrame, discardFrames);
745 bool needflush =
false;
746 if (desiredFrame > last_frame)
748 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
749 QString(
"DoFastForward(): desiredFrame(%1) > last_frame(%2)")
750 .arg(desiredFrame).arg(last_frame));
752 if (desiredFrame - last_frame > 32)
754 LOG(VB_GENERAL, LOG_ERR,
LOC +
"DoFastForward(): "
755 "Desired frame is way past the end of the keyframe map!"
756 "\n\t\t\tSeeking to last keyframe instead.");
757 desiredFrame = last_frame;
766 while ((desiredFrame > last_frame) && !
m_atEof)
796 normalframes = std::max(normalframes, 0);
823 LOG(VB_GENERAL, LOG_ERR,
LOC +
824 "No ringBuffer yet, can't fast forward seek");
845 GetKey(e_post) - desiredFrame < desiredFrame -
GetKey(e_pre)))
915 for (
size_t i = 0; i <
m_tracks[Type].size(); i++)
923 if (TrackNo >=
m_tracks[Type].size())
925 return static_cast<int>(
m_tracks[Type][TrackNo].m_language_index);
931 if (TrackNo >=
m_tracks[Type].size())
935 int lang =
m_tracks[Type][TrackNo].m_language;
936 int hnum =
static_cast<int>(TrackNo + 1);
938 hnum =
m_tracks[Type][TrackNo].m_stream_id;
941 return type_msg + QString(
" %1").arg(hnum);
943 return type_msg + QString(
" %1: %2").arg(hnum).arg(lang_msg);
955 if (TrackNo >=
static_cast<int>(
m_tracks[Type].size()))
971 if (
m_tracks[Type][forcedTrackIndex].m_forced)
982 if (TrackNo >=
m_tracks[Type].size())
995 int size =
static_cast<int>(
m_tracks[Type].size());
1001 next_track = (std::max(+0,
m_currentTrack[Type]) + size - 1) % size;
1010 int next_track = -1;
1011 int size =
static_cast<int>(
m_tracks[Type].size());
1037 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Trying to select track (w/lang & %1forced)")
1038 .arg(forcedPreferred ?
"" :
"!"));
1039 const int kForcedWeight = forcedPreferred ? (1 << 20) : -(1 << 20);
1040 const int kLanguageWeight = (1 << 10);
1041 const int kPositionWeight = (1 << 0);
1046 for (
uint i = 0; i < numStreams; i++)
1050 int position =
static_cast<int>(numStreams) -
static_cast<int>(i);
1052 if (preferredLanguage != 0 &&
m_tracks[Type][i].m_language == preferredLanguage)
1061 int score = (1 << 20) +
1062 (kForcedWeight *
static_cast<int>(forced)) +
1063 (kLanguageWeight * language) +
1064 (kPositionWeight * position);
1065 if (score > bestScore)
1068 selTrack =
static_cast<int>(i);
1106 int selTrack = (1 == numStreams) ? 0 : -1;
1110 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Trying to reselect track");
1116 for (
uint i = 0; i < numStreams; i++)
1118 if (wlang ==
m_tracks[Type][i].m_language)
1120 selTrack =
static_cast<int>(i);
1121 if (windx ==
m_tracks[Type][i].m_language_index)
1134 if (
m_tracks[Type][selTrack].m_forced)
1140 int nonForcedTrack =
BestTrack(Type,
false);
1142 if (!
m_tracks[Type][nonForcedTrack].m_forced)
1145 selTrack = nonForcedTrack;
1160 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Selected track #%1 (type %2) in the %3 language(%4)")
1183 QString str = QObject::tr(
"Track");
1186 str = QObject::tr(
"Audio track");
1188 str = QObject::tr(
"Video track");
1190 str = QObject::tr(
"Subtitle track");
1192 str = QObject::tr(
"CC",
"EIA-608 closed captions");
1194 str = QObject::tr(
"ATSC CC",
"EIA-708 closed captions");
1196 str = QObject::tr(
"TT CC",
"Teletext closed captions");
1198 str = QObject::tr(
"TT Menu",
"Teletext Menu");
1200 str = QObject::tr(
"Text",
"Text stream");
1202 str = QObject::tr(
"TXT File",
"Text File");
1210 if (str.startsWith(
"AUDIO"))
1212 else if (str.startsWith(
"VIDEO"))
1214 else if (str.startsWith(
"SUBTITLE"))
1216 else if (str.startsWith(
"CC608"))
1218 else if (str.startsWith(
"CC708"))
1220 else if (str.startsWith(
"TTC"))
1222 else if (str.startsWith(
"TTM"))
1224 else if (str.startsWith(
"TFL"))
1226 else if (str.startsWith(
"RAWTEXT"))
1238 str = QObject::tr(
"Audio Description",
1239 "On-screen events described for the visually impaired");
1242 str = QObject::tr(
"Clean Effects",
1243 "No dialog, background audio only");
1246 str = QObject::tr(
"Hearing Impaired",
1247 "Clear dialog for the hearing impaired");
1250 str = QObject::tr(
"Spoken Subtitles",
1251 "Subtitles are read out for the visually impaired");
1254 str = QObject::tr(
"Commentary",
"Director/Cast commentary track");
1258 str = QObject::tr(
"Normal",
"Ordinary audio track");
1286 float fallback_ratio)
1293 frm_pos_map_t::const_iterator lower = map.lowerBound(key);
1296 if (lower != map.begin() && (lower == map.end() || lower.key() > key))
1298 if (lower == map.end() || lower.key() > key)
1302 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1303 QString(
"TranslatePosition(key=%1): extrapolating to (0,0)")
1309 val1 = lower.value();
1313 frm_pos_map_t::const_iterator upper = map.lowerBound(key);
1314 if (upper == map.end())
1318 val2 = llroundf(val1 + (fallback_ratio * (key2 - key1)));
1319 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1320 QString(
"TranslatePosition(key=%1, ratio=%2): "
1321 "extrapolating to (%3,%4)")
1322 .arg(key).arg(fallback_ratio).arg(key2).arg(val2));
1326 val2 = upper.value();
1330 return llround(val1 + ((
double) (key - key1) * (val2 - val1) / (key2 - key1)));
1336 float fallback_framerate,
1349 if (position > it.key())
1352 (QDateTime::currentDateTime() >
1358 1000 / fallback_framerate));
1364 float fallback_framerate,
1371 1000 / fallback_framerate);
1388 uint64_t absPosition,
1390 float fallback_ratio)
1392 uint64_t subtraction = 0;
1393 uint64_t startOfCutRegion = 0;
1394 bool withinCut =
false;
1396 for (frm_dir_map_t::const_iterator i = deleteMap.begin();
1397 i != deleteMap.end(); ++i)
1402 if (i.key() > absPosition)
1408 startOfCutRegion = mappedKey;
1413 subtraction += (mappedKey - startOfCutRegion);
1418 subtraction += (mappedPos - startOfCutRegion);
1419 return mappedPos - subtraction;
1437 uint64_t relPosition,
1439 float fallback_ratio)
1441 uint64_t addition = 0;
1442 uint64_t startOfCutRegion = 0;
1443 bool withinCut =
false;
1445 for (frm_dir_map_t::const_iterator i = deleteMap.begin();
1446 i != deleteMap.end(); ++i)
1455 startOfCutRegion = mappedKey;
1456 if (relPosition + addition <= startOfCutRegion)
1462 addition += (mappedKey - startOfCutRegion);
1465 return relPosition + addition;
1474 for (AVPixelFormat *format = Formats; *format != AV_PIX_FMT_NONE; format++)
1476 for (
auto fmt : *RenderFormats)
1480 return AV_PIX_FMT_NONE;
MythAVRational m_totalDuration
QRecursiveMutex m_positionMapLock
virtual uint GetTrackCount(uint Type)
MarkTypes m_positionMapType
frm_pos_map_t m_frameToDurMap
virtual void SetWatchingRecording(bool mode)
virtual void DoFastForwardSeek(long long desiredFrame, bool &needflush)
Seeks to the keyframe just before the desiredFrame if exact seeks is enabled, or the frame just after...
int ChangeTrack(uint Type, int Dir)
void SetProgramInfo(const ProgramInfo &pginfo)
long long GetKey(const PosMapEntry &entry) const
virtual bool GetFrame(DecodeType Type, bool &Retry)=0
Demux, preprocess and possibly decode a frame of video/audio.
bool m_dontSyncPositionMap
virtual QString GetTrackDesc(uint Type, uint TrackNo)
virtual int SetTrack(uint Type, int TrackNo)
virtual void UpdateFramesPlayed(void)
void SetReadAdjust(long long adjust)
MythMediaBuffer * m_ringBuffer
QRecursiveMutex m_trackLock
void SetRenderFormats(const VideoFrameTypes *RenderFormats)
bool m_decodeAllSubtitles
virtual bool PosMapFromEnc(void)
Queries encoder for position map data that has not been committed to the DB yet.
void AutoSelectTracks(void)
std::vector< int > m_languagePreference
language preferences for auto-selection of streams
void SaveTotalDuration(void)
virtual void Reset(bool reset_video_data, bool seek_reset, bool reset_file)
virtual void ResetPosMap(void)
virtual void SetEofState(EofState eof)
long long GetLastFrameInPosMap(void) const
static uint64_t TranslatePosition(const frm_pos_map_t &map, long long key, float fallback_ratio)
std::array< StreamInfo, kTrackTypeCount > m_wantedTrack
frm_pos_map_t m_durToFrameMap
static uint64_t TranslatePositionAbsToRel(const frm_dir_map_t &deleteMap, uint64_t absPosition, const frm_pos_map_t &map=frm_pos_map_t(), float fallback_ratio=1.0)
void SetWaitForChange(void)
uint64_t SavePositionMapDelta(long long first_frame, long long last_frame)
bool GetWaitForChange(void) const
DecoderBase(MythPlayer *parent, const ProgramInfo &pginfo)
virtual bool FindPosition(long long desired_value, bool search_adjusted, int &lower_bound, int &upper_bound)
const VideoFrameTypes * m_renderFormats
virtual bool PosMapFromDb(void)
virtual bool DoFastForward(long long desiredFrame, bool discardFrames=true)
Skips ahead or rewinds to desiredFrame.
uint64_t TranslatePositionMsToFrame(std::chrono::milliseconds dur_ms, float fallback_framerate, const frm_dir_map_t &cutlist)
void SaveTotalFrames(void)
long long ConditionallyUpdatePosMap(long long desiredFrame)
static AVPixelFormat GetBestVideoFormat(AVPixelFormat *Formats, const VideoFrameTypes *RenderFormats)
Find a suitable frame format that is mutually acceptable to the decoder and render device.
bool m_hasKeyFrameAdjustTable
virtual void SeekReset(long long newkey, uint skipFrames, bool doFlush, bool discardFrames)
std::array< sinfo_vec_t, kTrackTypeCount > m_tracks
virtual bool SyncPositionMap(void)
Updates the position map used for skipping frames.
virtual int AutoSelectTrack(uint Type)
Select best track.
virtual QStringList GetTracks(uint Type)
QDateTime m_lastPositionMapUpdate
StreamInfo GetTrackInfo(uint Type, uint TrackNo)
virtual int GetTrackLanguageIndex(uint Type, uint TrackNo)
unsigned long GetPositionMapSize(void) const
static uint64_t TranslatePositionRelToAbs(const frm_dir_map_t &deleteMap, uint64_t relPosition, const frm_pos_map_t &map=frm_pos_map_t(), float fallback_ratio=1.0)
std::array< StreamInfo, kTrackTypeCount > m_selectedForcedTrack
std::vector< PosMapEntry > m_positionMap
std::array< int, kTrackTypeCount > m_currentTrack
virtual bool DoRewindSeek(long long desiredFrame)
void SetDecodeAllSubtitles(bool DecodeAll)
bool m_recordingHasPositionMap
virtual bool DoRewind(long long desiredFrame, bool discardFrames=true)
int BestTrack(uint Type, bool forcedPreferred, int preferredLanguage=0)
Determine the best track according to weights.
ProgramInfo * m_playbackInfo
std::chrono::milliseconds TranslatePositionFrameToMs(long long position, float fallback_framerate, const frm_dir_map_t &cutlist)
std::array< StreamInfo, kTrackTypeCount > m_selectedTrack
C++ wrapper for FFmpeg libavutil AVRational.
long long toFixed(long long base) const
Convert the rational number to fixed point.
static AVPixelFormat FrameTypeToPixelFormat(VideoFrameType Type)
uint64_t GetTotalReadPosition(void)
std::chrono::seconds GetTotalTimeOfTitle(void) const
double GetFrameRate(void)
long long GetTotalReadPosition(void) const
std::chrono::seconds GetTotalTimeOfTitle(void) const
get the total time of the title in seconds 90000 ticks = 1 sec
std::chrono::seconds TitleTimeLeft(void) const
returns seconds left in the title
double GetFrameRate(void)
used by DecoderBase for the total frame number calculation for position map support and ffw/rew.
void SetFramesPlayed(uint64_t played)
void SetKeyframeDistance(int keyframedistance)
virtual void tracksChanged(uint TrackType)
void FileChangedCallback()
void SetFileLength(std::chrono::seconds total, int frames)
bool PosMapFromEnc(uint64_t start, frm_pos_map_t &posMap, frm_pos_map_t &durMap)
A QElapsedTimer based timer to replace use of QTime as a timer.
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
void start(void)
starts measuring elapsed time.
Holds information on recordings and videos.
void QueryPositionMap(frm_pos_map_t &posMap, MarkTypes type) const
void SaveTotalDuration(std::chrono::milliseconds duration)
Store the Total Duration at frame 0 in the recordedmarkup table.
void SaveTotalFrames(int64_t frames)
Store the Total Frames at frame 0 in the recordedmarkup table.
void SavePositionMapDelta(frm_pos_map_t &posMap, MarkTypes type) const
int to_track_type(const QString &str)
QString toString(TrackType type)
@ kAudioTypeAudioDescription
@ kAudioTypeHearingImpaired
@ kTrackTypeTeletextCaptions
QString iso639_key_toName(int iso639_2)
Converts a canonical key to language name in English.
std::vector< int > iso639_get_language_key_list(void)
ISO 639-1 and ISO 639-2 support functions.
std::enable_if_t< std::is_floating_point_v< T >, std::chrono::seconds > secondsFromFloat(T value)
Helper function for convert a floating point number to a duration.
std::vector< VideoFrameType > VideoFrameTypes
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
QMap< uint64_t, MarkTypes > frm_dir_map_t
Frame # -> Mark map.
QMap< long long, long long > frm_pos_map_t
Frame # -> File offset map.