MythTV master
decoderbase.cpp
Go to the documentation of this file.
1
2#include <algorithm>
3
6
8#include "DVD/mythdvdbuffer.h"
9#include "decoderbase.h"
10#include "mythcodeccontext.h"
11#include "mythplayer.h"
12#include "programinfo.h"
13
14#define LOC QString("Dec: ")
15
17 : m_parent(parent), m_playbackInfo(new ProgramInfo(pginfo)),
18 m_audio(m_parent->GetAudio()),
19
20 // language preference
21 m_languagePreference(iso639_get_language_key_list())
22{
24 m_tracks[kTrackTypeAudio].emplace_back(0, 0, 0, 0, kAudioTypeNormal);
25 m_tracks[kTrackTypeCC608].emplace_back(0, 1);
26 m_tracks[kTrackTypeCC608].emplace_back(0, 3);
27}
28
30{
31 delete m_playbackInfo;
32}
33
35{
36 if (RenderFormats != nullptr)
37 m_renderFormats = RenderFormats;
38}
39
41{
42 delete m_playbackInfo;
43 m_playbackInfo = new ProgramInfo(pginfo);
44}
45
46void DecoderBase::Reset(bool reset_video_data, bool seek_reset, bool reset_file)
47{
48 LOG(VB_PLAYBACK, LOG_INFO, LOC +
49 QString("Reset: Video %1, Seek %2, File %3")
50 .arg(reset_video_data).arg(seek_reset).arg(reset_file));
51
52 if (seek_reset)
53 SeekReset(0, 0, true, true);
54
55 if (reset_video_data)
56 {
59 m_frameCounter += 100;
60 m_fpsSkip = 0;
61 m_framesRead = 0;
64 }
65
66 if (reset_file)
67 {
68 m_waitingForChange = false;
70 }
71}
72
73void DecoderBase::SeekReset(long long /*newkey*/, uint /*skipFrames*/,
74 bool /*doFlush*/, bool /*discardFrames*/)
75{
76 m_readAdjust = 0;
77 m_frameCounter += 100; // NB don't just set to 0
78}
79
81{
82 bool wereWatchingRecording = m_watchingRecording;
83
84 // When we switch from WatchingRecording to WatchingPreRecorded,
85 // re-get the positionmap
86 m_posmapStarted = false;
88
89 if (wereWatchingRecording && !m_watchingRecording)
91}
92
94{
95 if (!m_playbackInfo)
96 return false;
97
98 // Overwrites current positionmap with entire contents of database
99 frm_pos_map_t posMap;
100 frm_pos_map_t durMap;
101
103 {
104 m_keyframeDist = 15;
106 if (m_fps < 26 && m_fps > 24)
107 m_keyframeDist = 12;
108 auto totframes =
109 (long long)(m_ringBuffer->DVD()->GetTotalTimeOfTitle().count() * m_fps);
110 posMap[totframes] = m_ringBuffer->DVD()->GetTotalReadPosition();
111 }
112 else if (m_ringBuffer && m_ringBuffer->IsBD())
113 {
114 m_keyframeDist = 15;
116 if (m_fps < 26 && m_fps > 24)
117 m_keyframeDist = 12;
118 auto totframes =
119 (long long)(m_ringBuffer->BD()->GetTotalTimeOfTitle().count() * m_fps);
120 posMap[totframes] = m_ringBuffer->BD()->GetTotalReadPosition();
121#if 0
122 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
123 QString("%1 TotalTimeOfTitle() in ticks, %2 TotalReadPosition() "
124 "in bytes, %3 is fps")
126 .arg(m_ringBuffer->BD()->GetTotalReadPosition()).arg(m_fps));
127#endif
128 }
129 else if ((m_positionMapType == MARK_UNSET) ||
130 (m_keyframeDist == -1))
131 {
133 if (!posMap.empty())
134 {
136 if (m_keyframeDist == -1)
137 m_keyframeDist = 1;
138 }
139 else
140 {
142 if (!posMap.empty())
143 {
145 if (m_keyframeDist == -1)
146 {
147 m_keyframeDist = 15;
148 if (m_fps < 26 && m_fps > 24)
149 m_keyframeDist = 12;
150 }
151 }
152 else
153 {
155 if (!posMap.empty())
156 {
157 // keyframedist should be set in the fileheader so no
158 // need to try to determine it in this case
160 }
161 }
162 }
163 }
164 else
165 {
167 }
168
169 if (posMap.empty())
170 return false; // no position map in recording
171
173
174 QMutexLocker locker(&m_positionMapLock);
175 m_positionMap.clear();
176 m_positionMap.reserve(posMap.size());
177 m_frameToDurMap.clear();
178 m_durToFrameMap.clear();
179
180 for (auto it = posMap.cbegin(); it != posMap.cend(); ++it)
181 {
182 PosMapEntry e = {.index=it.key(),
183 .adjFrame=it.key() * m_keyframeDist,
184 .pos=*it};
185 m_positionMap.push_back(e);
186 }
187
188 if (!m_positionMap.empty() && !(m_ringBuffer && m_ringBuffer->IsDisc()))
189 m_indexOffset = m_positionMap[0].index;
190
191 if (!m_positionMap.empty())
192 {
193 LOG(VB_PLAYBACK, LOG_INFO, LOC +
194 QString("Position map filled from DB to: %1")
195 .arg(m_positionMap.back().index));
196 }
197
198 uint64_t last = 0;
199 for (auto it = durMap.cbegin(); it != durMap.cend(); ++it)
200 {
201 m_frameToDurMap[it.key()] = it.value();
202 m_durToFrameMap[it.value()] = it.key();
203 last = it.key();
204 }
205
206 if (!m_durToFrameMap.empty())
207 {
208 LOG(VB_PLAYBACK, LOG_INFO, LOC +
209 QString("Duration map filled from DB to: %1").arg(last));
210 }
211
212 return true;
213}
214
224{
225 if (!m_parent || m_keyframeDist < 1)
226 return false;
227
228 unsigned long long start = 0;
229 {
230 QMutexLocker locker(&m_positionMapLock);
231 if (!m_positionMap.empty())
232 start = m_positionMap.back().index + 1;
233 }
234
235 frm_pos_map_t posMap;
236 frm_pos_map_t durMap;
237 if (!m_parent->PosMapFromEnc(start, posMap, durMap))
238 return false;
239
240 QMutexLocker locker(&m_positionMapLock);
241
242 // append this new position map to class's
243 m_positionMap.reserve(m_positionMap.size() + posMap.size());
244
245 long long last_index = 0;
246 if (!m_positionMap.empty())
247 last_index = m_positionMap.back().index;
248 for (auto it = posMap.cbegin(); it != posMap.cend(); ++it)
249 {
250 if (it.key() <= last_index)
251 continue;
252
253 PosMapEntry e = {.index=it.key(),
254 .adjFrame=it.key() * m_keyframeDist,
255 .pos=*it};
256 m_positionMap.push_back(e);
257 }
258
259 if (!m_positionMap.empty() && !(m_ringBuffer && m_ringBuffer->IsDisc()))
260 m_indexOffset = m_positionMap[0].index;
261
262 if (!m_positionMap.empty())
263 {
264 LOG(VB_PLAYBACK, LOG_INFO, LOC +
265 QString("Position map filled from Encoder to: %1")
266 .arg(m_positionMap.back().index));
267 }
268
269 bool isEmpty = m_frameToDurMap.empty();
270 if (!isEmpty)
271 {
272 frm_pos_map_t::const_iterator it = m_frameToDurMap.cend();
273 --it;
274 last_index = it.key();
275 }
276 for (frm_pos_map_t::const_iterator it = durMap.cbegin();
277 it != durMap.cend(); ++it)
278 {
279 if (!isEmpty && it.key() <= last_index)
280 continue; // we released the m_positionMapLock for a few ms...
281 m_frameToDurMap[it.key()] = it.value();
282 m_durToFrameMap[it.value()] = it.key();
283 }
284
285 if (!m_frameToDurMap.empty())
286 {
287 frm_pos_map_t::const_iterator it = m_frameToDurMap.cend();
288 --it;
289 LOG(VB_PLAYBACK, LOG_INFO, LOC +
290 QString("Duration map filled from Encoder to: %1").arg(it.key()));
291 }
292
293 return true;
294}
295
296unsigned long DecoderBase::GetPositionMapSize(void) const
297{
298 QMutexLocker locker(&m_positionMapLock);
299 return m_positionMap.size();
300}
301
325{
326 LOG(VB_PLAYBACK, LOG_INFO, LOC +
327 QString("Resyncing position map. posmapStarted = %1"
328 " livetv(%2) watchingRec(%3)")
329 .arg((int) m_posmapStarted).arg(m_livetv).arg(m_watchingRecording));
330
332 return false;
333
334 unsigned long old_posmap_size = GetPositionMapSize();
335 unsigned long new_posmap_size = old_posmap_size;
336
338 {
339 if (!m_posmapStarted)
340 {
341 // starting up -- try first from database
342 PosMapFromDb();
343 new_posmap_size = GetPositionMapSize();
344 LOG(VB_PLAYBACK, LOG_INFO, LOC +
345 QString("SyncPositionMap watchingrecording, from DB: "
346 "%1 entries") .arg(new_posmap_size));
347 }
348 // always try to get more from encoder
349 if (!PosMapFromEnc())
350 {
351 LOG(VB_PLAYBACK, LOG_INFO, LOC +
352 QString("SyncPositionMap watchingrecording no entries "
353 "from encoder, try DB"));
354 PosMapFromDb(); // try again from db
355 }
356
357 new_posmap_size = GetPositionMapSize();
358 LOG(VB_PLAYBACK, LOG_INFO, LOC +
359 QString("SyncPositionMap watchingrecording total: %1 entries")
360 .arg(new_posmap_size));
361 }
362 else
363 {
364 // watching prerecorded ... just get from db
365 if (!m_posmapStarted)
366 {
367 PosMapFromDb();
368
369 new_posmap_size = GetPositionMapSize();
370 LOG(VB_PLAYBACK, LOG_INFO, LOC +
371 QString("SyncPositionMap prerecorded, from DB: %1 entries")
372 .arg(new_posmap_size));
373 }
374 }
375
376 bool ret_val = new_posmap_size > old_posmap_size;
377
378 if (ret_val && m_keyframeDist > 0)
379 {
380 long long totframes = 0;
381 std::chrono::seconds length = 0s;
382
384 {
385 length = m_ringBuffer->DVD()->GetTotalTimeOfTitle();
386 QMutexLocker locker(&m_positionMapLock);
387 totframes = m_positionMap.back().index;
388 }
389 else if (m_ringBuffer && m_ringBuffer->IsBD())
390 {
391 length = m_ringBuffer->BD()->GetTotalTimeOfTitle();
392 QMutexLocker locker(&m_positionMapLock);
393 totframes = m_positionMap.back().index;
394 }
395 else
396 {
397 QMutexLocker locker(&m_positionMapLock);
398 totframes = m_positionMap.back().index * m_keyframeDist;
399 if (m_fps != 0.0)
400 length = secondsFromFloat((totframes * 1.0) / m_fps);
401 }
402
403 m_parent->SetFileLength(length, totframes);
405 m_posmapStarted = true;
406
407 LOG(VB_PLAYBACK, LOG_INFO, LOC +
408 QString("SyncPositionMap, new totframes: %1, new length: %2, "
409 "posMap size: %3")
410 .arg(totframes).arg(length.count()).arg(new_posmap_size));
411 }
412 m_recordingHasPositionMap |= (0 != new_posmap_size);
413 {
414 QMutexLocker locker(&m_positionMapLock);
415 m_lastPositionMapUpdate = QDateTime::currentDateTime();
416 }
417 return ret_val;
418}
419
420// returns true iff found exactly
421// searches position if search_pos, index otherwise
422bool DecoderBase::FindPosition(long long desired_value, bool search_adjusted,
423 int &lower_bound, int &upper_bound)
424{
425 QMutexLocker locker(&m_positionMapLock);
426 // Binary search
427 auto size = (long long) m_positionMap.size();
428 long long lower = -1;
429 long long upper = size;
430
431 if (!search_adjusted && m_keyframeDist > 0)
432 desired_value /= m_keyframeDist;
433
434 while (upper - 1 > lower)
435 {
436 long long i = (upper + lower) / 2;
437 long long value = 0;
438 if (search_adjusted)
439 value = m_positionMap[i].adjFrame;
440 else
441 value = m_positionMap[i].index - m_indexOffset;
442 if (value == desired_value)
443 {
444 // found it
445 upper_bound = i;
446 lower_bound = i;
447
448 LOG(VB_PLAYBACK, LOG_INFO, LOC +
449 QString("FindPosition(%1, search%2 adjusted)")
450 .arg(desired_value).arg((search_adjusted) ? "" : " not") +
451 QString(" --> [%1:%2(%3)]")
452 .arg(i).arg(GetKey(m_positionMap[i]))
453 .arg(m_positionMap[i].pos));
454
455 return true;
456 }
457 if (value > desired_value)
458 upper = i;
459 else
460 lower = i;
461 }
462 // Did not find it exactly -- return bounds
463
464 if (search_adjusted)
465 {
466 while (lower >= 0 && m_positionMap[lower].adjFrame > desired_value)
467 lower--;
468 while (upper < size && m_positionMap[upper].adjFrame < desired_value)
469 upper++;
470 }
471 else
472 {
473 while (lower >= 0 &&
474 (m_positionMap[lower].index - m_indexOffset) > desired_value)
475 lower--;
476 while (upper < size &&
477 (m_positionMap[upper].index - m_indexOffset) < desired_value)
478 upper++;
479 }
480 // keep in bounds
481 lower = std::max(lower, 0LL);
482 upper = std::min(upper, size - 1LL);
483
484 upper_bound = upper;
485 lower_bound = lower;
486 bool empty = m_positionMap.empty();
487
488 LOG(VB_PLAYBACK, LOG_INFO, LOC +
489 QString("FindPosition(%1, search%3 adjusted)")
490 .arg(desired_value).arg((search_adjusted) ? "" : " not") +
491 QString(" --> \n\t\t\t[%1:%2(%3),%4:%5(%6)]")
492 .arg(lower_bound)
493 .arg(empty ? -1 : GetKey(m_positionMap[lower_bound]))
494 .arg(empty ? -1 : m_positionMap[lower_bound].pos)
495 .arg(upper_bound)
496 .arg(empty ? -1 : GetKey(m_positionMap[upper_bound]))
497 .arg(empty ? -1 : m_positionMap[upper_bound].pos));
498
499 return false;
500}
501
502uint64_t DecoderBase::SavePositionMapDelta(long long first, long long last)
503{
504 MythTimer ttm;
505 MythTimer ctm;
506 MythTimer stm;
507 ttm.start();
508
509 QMutexLocker locker(&m_positionMapLock);
511 uint64_t saved = 0;
512
514 return saved;
515
516 ctm.start();
517 frm_pos_map_t posMap;
518 for (auto & entry : m_positionMap)
519 {
520 if (entry.index < first)
521 continue;
522 if (entry.index > last)
523 break;
524
525 posMap[entry.index] = entry.pos;
526 saved++;
527 }
528
529 frm_pos_map_t durMap;
530 for (auto it = m_frameToDurMap.cbegin(); it != m_frameToDurMap.cend(); ++it)
531 {
532 if (it.key() < first)
533 continue;
534 if (it.key() > last)
535 break;
536 durMap[it.key()] = it.value();
537 }
538
539 locker.unlock();
540
541 stm.start();
544
545#if 0
546 LOG(VB_GENERAL, LOG_DEBUG, LOC +
547 QString("Saving position map [%1,%2] w/%3 keyframes, "
548 "took (%4,%5,%6) ms")
549 .arg(first).arg(last).arg(saved)
550 .arg(ttm.elapsed().count())
551 .arg(ctm.elapsed().count()-stm.elapsed().count())
552 .arg(stm.elapsed().count()));
553#endif
554
555 return saved;
556}
557
558bool DecoderBase::DoRewind(long long desiredFrame, bool discardFrames)
559{
560 LOG(VB_PLAYBACK, LOG_INFO, LOC +
561 QString("DoRewind(%1 (%2), %3 discard frames)")
562 .arg(desiredFrame).arg(m_framesPlayed)
563 .arg((discardFrames) ? "do" : "don't"));
564
565 if (!DoRewindSeek(desiredFrame))
566 return false;
567
569 m_fpsSkip = 0;
571
572 // Do any Extra frame-by-frame seeking for exactseeks mode
573 // And flush pre-seek frame if we are allowed to and need to..
574 int normalframes = (uint64_t)(desiredFrame - (m_framesPlayed - 1)) > m_seekSnap
575 ? desiredFrame - m_framesPlayed : 0;
576 normalframes = std::max(normalframes, 0);
577 SeekReset(m_lastKey, normalframes, true, discardFrames);
578
579 if (discardFrames || (m_ringBuffer && m_ringBuffer->IsDisc()))
581
582 return true;
583}
584
585long long DecoderBase::GetKey(const PosMapEntry &e) const
586{
587 long long kf = (m_ringBuffer && m_ringBuffer->IsDisc()) ?
588 1LL : m_keyframeDist;
589 return (m_hasKeyFrameAdjustTable) ? e.adjFrame :(e.index - m_indexOffset) * kf;
590}
591
592bool DecoderBase::DoRewindSeek(long long desiredFrame)
593{
594 ConditionallyUpdatePosMap(desiredFrame);
595
596 if (!GetPositionMapSize())
597 {
598 LOG(VB_GENERAL, LOG_ERR, LOC + "PosMap is empty, can't seek");
599 return false;
600 }
601
602 if (!m_ringBuffer)
603 {
604 LOG(VB_GENERAL, LOG_ERR, LOC + "No ringBuffer yet, can't seek");
605 return false;
606 }
607
608 // Find keyframe <= desiredFrame, store in lastKey (frames)
609 int pre_idx = 0;
610 int post_idx = 0;
611 FindPosition(desiredFrame, m_hasKeyFrameAdjustTable, pre_idx, post_idx);
612
613 PosMapEntry e {};
614 {
615 QMutexLocker locker(&m_positionMapLock);
616 PosMapEntry e_pre = m_positionMap[pre_idx];
617 PosMapEntry e_post = m_positionMap[post_idx];
618 int pos_idx = pre_idx;
619 e = e_pre;
620 if (((uint64_t) (GetKey(e_post) - desiredFrame)) <= m_seekSnap &&
621 m_framesPlayed - 1 > GetKey(e_post) &&
622 GetKey(e_post) - desiredFrame <= desiredFrame - GetKey(e_pre))
623 {
624 // Snap to the right if e_post is within snap distance and
625 // is at least as close a snap as e_pre. Take into
626 // account that if framesPlayed has already reached
627 // e_post, we should only snap to the left.
628 pos_idx = post_idx;
629 e = e_post;
630 }
631 m_lastKey = GetKey(e);
632
633 // ??? Don't rewind past the beginning of the file
634 while (e.pos < 0)
635 {
636 pos_idx++;
637 if (pos_idx >= (int)m_positionMap.size())
638 return false;
639
640 e = m_positionMap[pos_idx];
641 m_lastKey = GetKey(e);
642 }
643 }
644
645 m_ringBuffer->Seek(e.pos, SEEK_SET);
646
647 return true;
648}
649
651{
652 QMutexLocker locker(&m_positionMapLock);
653 m_posmapStarted = false;
654 m_positionMap.clear();
655 m_frameToDurMap.clear();
656 m_durToFrameMap.clear();
657}
658
660{
661 long long last_frame = 0;
662
663 QMutexLocker locker(&m_positionMapLock);
664 if (!m_positionMap.empty())
665 last_frame = GetKey(m_positionMap.back());
666
667 return last_frame;
668}
669
670long long DecoderBase::ConditionallyUpdatePosMap(long long desiredFrame)
671{
672 long long last_frame = GetLastFrameInPosMap();
673
674 if (desiredFrame < 0)
675 return last_frame;
676
677 // Resync keyframe map if we are trying to seek to a frame
678 // not yet equalled or exceeded in the seek map.
679 if (desiredFrame < last_frame)
680 return last_frame;
681
682 LOG(VB_PLAYBACK, LOG_INFO, LOC +
683 "ConditionallyUpdatePosMap: Not enough info in positionMap," +
684 QString("\n\t\t\twe need frame %1 but highest we have is %2.")
685 .arg(desiredFrame).arg(last_frame));
686
688
689 last_frame = GetLastFrameInPosMap();
690
691 if (desiredFrame > last_frame)
692 {
693 LOG(VB_PLAYBACK, LOG_INFO, LOC +
694 "ConditionallyUpdatePosMap: Still not "
695 "enough info in positionMap after sync, " +
696 QString("\n\t\t\twe need frame %1 but highest we have "
697 "is %2. Will attempt to seek frame-by-frame")
698 .arg(desiredFrame).arg(last_frame));
699 }
700
701 return last_frame;
702}
703
713bool DecoderBase::DoFastForward(long long desiredFrame, bool discardFrames)
714{
715 LOG(VB_PLAYBACK, LOG_INFO, LOC +
716 QString("DoFastForward(%1 (%2), %3 discard frames)")
717 .arg(desiredFrame).arg(m_framesPlayed)
718 .arg((discardFrames) ? "do" : "don't"));
719
720 if (!m_ringBuffer)
721 {
722 LOG(VB_GENERAL, LOG_ERR, LOC + "No ringBuffer yet, can't fast forward");
723 return false;
724 }
725
726 if (m_ringBuffer->IsDVD() &&
728 m_ringBuffer->DVD()->TitleTimeLeft() < 5s)
729 {
730 return false;
731 }
732 // Rewind if we have already played the desiredFrame. The +1 is for
733 // MPEG4 NUV files, which need to decode an extra frame sometimes.
734 // This shouldn't effect how this works in general because this is
735 // only triggered on the first keyframe/frame skip when paused. At
736 // that point the decoding is more than one frame ahead of display.
737 if (desiredFrame+1 < m_framesPlayed)
738 return DoRewind(desiredFrame, discardFrames);
739 desiredFrame = std::max(desiredFrame, m_framesPlayed);
740
741 ConditionallyUpdatePosMap(desiredFrame);
742
743 // Fetch last keyframe in position map
744 long long last_frame = GetLastFrameInPosMap();
745
746 // If the desiredFrame is past the end of the position map,
747 // do some frame-by-frame seeking until we get to it.
748 bool needflush = false;
749 if (desiredFrame > last_frame)
750 {
751 LOG(VB_GENERAL, LOG_NOTICE, LOC +
752 QString("DoFastForward(): desiredFrame(%1) > last_frame(%2)")
753 .arg(desiredFrame).arg(last_frame));
754
755 if (desiredFrame - last_frame > 32)
756 {
757 LOG(VB_GENERAL, LOG_ERR, LOC + "DoFastForward(): "
758 "Desired frame is way past the end of the keyframe map!"
759 "\n\t\t\tSeeking to last keyframe instead.");
760 desiredFrame = last_frame;
761 }
762
763 needflush = true;
764
765 // Handle non-frame-by-frame seeking
766 DoFastForwardSeek(last_frame, needflush);
767
768 m_exitAfterDecoded = true; // don't actualy get a frame
769 while ((desiredFrame > last_frame) && !m_atEof)
770 {
771 bool retry = false;
772 GetFrame(kDecodeNothing, retry); // don't need to return frame...
774 last_frame = GetLastFrameInPosMap();
775 }
776 m_exitAfterDecoded = false; // allow frames to be returned again
777
778 if (m_atEof)
779 {
780 return false;
781 }
782 }
783
784 {
785 QMutexLocker locker(&m_positionMapLock);
786 if (m_positionMap.empty())
787 {
788 return false;
789 }
790 }
791
792 // Handle non-frame-by-frame seeking
793 DoFastForwardSeek(desiredFrame, needflush);
794
795 // Do any Extra frame-by-frame seeking for exactseeks mode
796 // And flush pre-seek frame if we are allowed to and need to..
797 int normalframes = (uint64_t)(desiredFrame - (m_framesPlayed - 1)) > m_seekSnap
798 ? desiredFrame - m_framesPlayed : 0;
799 normalframes = std::max(normalframes, 0);
800 SeekReset(m_lastKey, normalframes, needflush, discardFrames);
801
802 if (discardFrames || m_transcoding)
804
805 return true;
806}
807
822void DecoderBase::DoFastForwardSeek(long long desiredFrame, bool &needflush)
823{
824 if (!m_ringBuffer)
825 {
826 LOG(VB_GENERAL, LOG_ERR, LOC +
827 "No ringBuffer yet, can't fast forward seek");
828 return;
829 }
830
831 int pre_idx = 0;
832 int post_idx = 0;
833 FindPosition(desiredFrame, m_hasKeyFrameAdjustTable, pre_idx, post_idx);
834
835 // if exactseeks, use keyframe <= desiredFrame
836
837 PosMapEntry e {};
838 PosMapEntry e_pre {};
839 PosMapEntry e_post {};
840 {
841 QMutexLocker locker(&m_positionMapLock);
842 e_pre = m_positionMap[pre_idx];
843 e_post = m_positionMap[post_idx];
844 }
845 e = e_pre;
846 if (((uint64_t) (GetKey(e_post) - desiredFrame)) <= m_seekSnap &&
847 (m_framesPlayed - 1 >= GetKey(e_pre) ||
848 GetKey(e_post) - desiredFrame < desiredFrame - GetKey(e_pre)))
849 {
850 // Snap to the right if e_post is within snap distance and is
851 // a closer snap than e_pre. Take into account that if
852 // framesPlayed has already reached e_pre, we should only snap
853 // to the right.
854 e = e_post;
855 }
856 m_lastKey = GetKey(e);
857
859 {
860 m_ringBuffer->Seek(e.pos, SEEK_SET);
861 needflush = true;
863 m_fpsSkip = 0;
865 }
866}
867
869{
871}
872
874{
875 ResetPosMap();
876 m_framesPlayed = 0;
877 m_framesRead = 0;
879
880 m_waitingForChange = false;
881 m_justAfterChange = true;
882
884}
885
886void DecoderBase::SetReadAdjust(long long adjust)
887{
888 m_readAdjust = adjust;
889}
890
892{
893 m_waitingForChange = true;
894}
895
897{
898 return m_waitingForChange;
899}
900
902{
903 QMutexLocker locker(&m_trackLock);
904 return static_cast<uint>(m_tracks[Type].size());
905}
906
908{
909 m_trackLock.lock();
910 m_decodeAllSubtitles = DecodeAll;
911 m_trackLock.unlock();
912}
913
915{
916 QMutexLocker locker(&m_trackLock);
917 QStringList list;
918 for (size_t i = 0; i < m_tracks[Type].size(); i++)
919 list += GetTrackDesc(Type, static_cast<uint>(i));
920 return list;
921}
922
924{
925 QMutexLocker locker(&m_trackLock);
926 if (TrackNo >= m_tracks[Type].size())
927 return 0;
928 return static_cast<int>(m_tracks[Type][TrackNo].m_language_index);
929}
930
931QString DecoderBase::GetTrackDesc(uint Type, uint TrackNo)
932{
933 QMutexLocker locker(&m_trackLock);
934 if (TrackNo >= m_tracks[Type].size())
935 return "";
936
937 QString type_msg = toString(static_cast<TrackType>(Type));
938 int lang = m_tracks[Type][TrackNo].m_language;
939 int hnum = static_cast<int>(TrackNo + 1);
940 if (kTrackTypeCC608 == Type)
941 hnum = m_tracks[Type][TrackNo].m_stream_id;
942
943 if (!lang)
944 return type_msg + QString(" %1").arg(hnum);
945 QString lang_msg = iso639_key_toName(lang);
946 return type_msg + QString(" %1: %2").arg(hnum).arg(lang_msg);
947}
948
950{
951 QMutexLocker locker(&m_trackLock);
952 return m_currentTrack[Type];
953}
954
955int DecoderBase::SetTrack(uint Type, int TrackNo)
956{
957 QMutexLocker locker(&m_trackLock);
958 if (TrackNo >= static_cast<int>(m_tracks[Type].size()))
959 return -1;
960
961 m_currentTrack[Type] = std::max(-1, TrackNo);
962 if (m_currentTrack[Type] < 0)
963 {
964 m_selectedTrack[Type].m_av_stream_index = -1;
965 }
966 else
967 {
968 m_wantedTrack[Type] = m_tracks[Type][static_cast<size_t>(m_currentTrack[Type])];
969 m_selectedTrack[Type] = m_tracks[Type][static_cast<size_t>(m_currentTrack[Type])];
970 if (Type == kTrackTypeSubtitle)
971 {
972 // Rechoose the associated forced track, preferring the same language
973 int forcedTrackIndex = BestTrack(Type, true, m_selectedTrack[Type].m_language);
974 if (m_tracks[Type][forcedTrackIndex].m_forced)
975 m_selectedForcedTrack[Type] = m_tracks[Type][forcedTrackIndex];
976 }
977 }
978
979 return m_currentTrack[Type];
980}
981
983{
984 QMutexLocker locker(&m_trackLock);
985 if (TrackNo >= m_tracks[Type].size())
986 {
987 StreamInfo si;
988 return si;
989 }
990 return m_tracks[Type][TrackNo];
991}
992
994{
995 QMutexLocker locker(&m_trackLock);
996
997 int next_track = -1;
998 int size = static_cast<int>(m_tracks[Type].size());
999 if (size)
1000 {
1001 if (Dir > 0)
1002 next_track = (std::max(-1, m_currentTrack[Type]) + 1) % size;
1003 else
1004 next_track = (std::max(+0, m_currentTrack[Type]) + size - 1) % size;
1005 }
1006 return SetTrack(Type, next_track);
1007}
1008
1010{
1011 QMutexLocker locker(&m_trackLock);
1012
1013 int next_track = -1;
1014 int size = static_cast<int>(m_tracks[Type].size());
1015 if (size)
1016 next_track = (std::max(0, m_currentTrack[Type]) + 1) % size;
1017 return next_track;
1018}
1019
1038int DecoderBase::BestTrack(uint Type, bool forcedPreferred, int preferredLanguage)
1039{
1040 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Trying to select track (w/lang & %1forced)")
1041 .arg(forcedPreferred ? "" : "!"));
1042 const int kForcedWeight = forcedPreferred ? (1 << 20) : -(1 << 20);
1043 const int kLanguageWeight = (1 << 10);
1044 const int kPositionWeight = (1 << 0);
1045 int bestScore = -1;
1046 int selTrack = -1;
1047 uint numStreams = static_cast<uint>(m_tracks[Type].size());
1048
1049 for (uint i = 0; i < numStreams; i++)
1050 {
1051 bool forced = (Type == kTrackTypeSubtitle &&
1052 m_tracks[Type][i].m_forced);
1053 int position = static_cast<int>(numStreams) - static_cast<int>(i);
1054 int language = 0;
1055 if (preferredLanguage != 0 && m_tracks[Type][i].m_language == preferredLanguage)
1056 {
1057 language = static_cast<int>(m_languagePreference.size()) + 1;
1058 }
1059 for (uint j = 0; (language == 0) && (j < m_languagePreference.size()); ++j)
1060 {
1061 if (m_tracks[Type][i].m_language == m_languagePreference[j])
1062 language = static_cast<int>(m_languagePreference.size()) - static_cast<int>(j);
1063 }
1064 int score = (1 << 20) +
1065 (kForcedWeight * static_cast<int>(forced)) +
1066 (kLanguageWeight * language) +
1067 (kPositionWeight * position);
1068 if (score > bestScore)
1069 {
1070 bestScore = score;
1071 selTrack = static_cast<int>(i);
1072 }
1073 }
1074
1075 return selTrack;
1076}
1077
1094{
1095 QMutexLocker locker(&m_trackLock);
1096
1097 uint numStreams = static_cast<uint>(m_tracks[Type].size());
1098
1099 if ((m_currentTrack[Type] >= 0) && (m_currentTrack[Type] < static_cast<int>(numStreams)))
1100 return m_currentTrack[Type]; // track already selected
1101
1102 if (!numStreams)
1103 {
1104 m_currentTrack[Type] = -1;
1105 m_selectedTrack[Type].m_av_stream_index = -1;
1106 return -1;
1107 }
1108
1109 int selTrack = (1 == numStreams) ? 0 : -1;
1110
1111 if ((selTrack < 0) && m_wantedTrack[Type].m_language>=-1)
1112 {
1113 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Trying to reselect track");
1114 // Try to reselect user selected track stream.
1115 // This should find the stream after a commercial
1116 // break and in some cases after a channel change.
1117 int wlang = m_wantedTrack[Type].m_language;
1118 uint windx = m_wantedTrack[Type].m_language_index;
1119 for (uint i = 0; i < numStreams; i++)
1120 {
1121 if (wlang == m_tracks[Type][i].m_language)
1122 {
1123 selTrack = static_cast<int>(i);
1124 if (windx == m_tracks[Type][i].m_language_index)
1125 break;
1126 }
1127 }
1128 }
1129
1130 if (selTrack < 0)
1131 {
1132 // Find best track favoring forced.
1133 selTrack = BestTrack(Type, true);
1134
1135 if (Type == kTrackTypeSubtitle)
1136 {
1137 if (m_tracks[Type][selTrack].m_forced)
1138 {
1139 // A forced AV Subtitle tracks is handled without the user
1140 // explicitly enabling subtitles. Try to find a good non-forced
1141 // track that can be swapped to in the case the user does
1142 // explicitly enable subtitles.
1143 int nonForcedTrack = BestTrack(Type, false);
1144
1145 if (!m_tracks[Type][nonForcedTrack].m_forced)
1146 {
1147 m_selectedForcedTrack[Type] = m_tracks[Type][selTrack];
1148 selTrack = nonForcedTrack;
1149 }
1150 }
1151 }
1152 }
1153
1154 int oldTrack = m_currentTrack[Type];
1155 m_currentTrack[Type] = selTrack;
1156 StreamInfo tmp = m_tracks[Type][static_cast<size_t>(m_currentTrack[Type])];
1157 m_selectedTrack[Type] = tmp;
1158
1159 if (m_wantedTrack[Type].m_av_stream_index < 0)
1160 m_wantedTrack[Type] = tmp;
1161
1162 int lang = m_tracks[Type][static_cast<size_t>(m_currentTrack[Type])].m_language;
1163 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Selected track #%1 (type %2) in the %3 language(%4)")
1164 .arg(m_currentTrack[Type]+1).arg(Type).arg(iso639_key_toName(lang)).arg(lang));
1165
1166 if (m_parent && (oldTrack != m_currentTrack[Type]))
1167 m_parent->tracksChanged(Type);
1168
1169 return selTrack;
1170}
1171
1173{
1174 for (uint i = 0; i < kTrackTypeCount; i++)
1175 AutoSelectTrack(i);
1176}
1177
1179{
1180 QMutexLocker locker(&m_trackLock);
1181 std::ranges::fill(m_currentTrack, -1);
1182}
1183
1185{
1186 QString str = QObject::tr("Track");
1187
1188 if (kTrackTypeAudio == type)
1189 str = QObject::tr("Audio track");
1190 else if (kTrackTypeVideo == type)
1191 str = QObject::tr("Video track");
1192 else if (kTrackTypeSubtitle == type)
1193 str = QObject::tr("Subtitle track");
1194 else if (kTrackTypeCC608 == type)
1195 str = QObject::tr("CC", "EIA-608 closed captions");
1196 else if (kTrackTypeCC708 == type)
1197 str = QObject::tr("ATSC CC", "EIA-708 closed captions");
1198 else if (kTrackTypeTeletextCaptions == type)
1199 str = QObject::tr("TT CC", "Teletext closed captions");
1200 else if (kTrackTypeTeletextMenu == type)
1201 str = QObject::tr("TT Menu", "Teletext Menu");
1202 else if (kTrackTypeRawText == type)
1203 str = QObject::tr("Text", "Text stream");
1204 else if (kTrackTypeTextSubtitle == type)
1205 str = QObject::tr("TXT File", "Text File");
1206 return str;
1207}
1208
1209int to_track_type(const QString &str)
1210{
1211 int ret = -1;
1212
1213 if (str.startsWith("AUDIO"))
1214 ret = kTrackTypeAudio;
1215 else if (str.startsWith("VIDEO"))
1216 ret = kTrackTypeVideo;
1217 else if (str.startsWith("SUBTITLE"))
1218 ret = kTrackTypeSubtitle;
1219 else if (str.startsWith("CC608"))
1220 ret = kTrackTypeCC608;
1221 else if (str.startsWith("CC708"))
1222 ret = kTrackTypeCC708;
1223 else if (str.startsWith("TTC"))
1225 else if (str.startsWith("TTM"))
1227 else if (str.startsWith("TFL"))
1229 else if (str.startsWith("RAWTEXT"))
1230 ret = kTrackTypeRawText;
1231 return ret;
1232}
1233
1235{
1236 QString str;
1237
1238 switch (type)
1239 {
1241 str = QObject::tr("Audio Description",
1242 "On-screen events described for the visually impaired");
1243 break;
1245 str = QObject::tr("Clean Effects",
1246 "No dialog, background audio only");
1247 break;
1249 str = QObject::tr("Hearing Impaired",
1250 "Clear dialog for the hearing impaired");
1251 break;
1253 str = QObject::tr("Spoken Subtitles",
1254 "Subtitles are read out for the visually impaired");
1255 break;
1257 str = QObject::tr("Commentary", "Director/Cast commentary track");
1258 break;
1259 case kAudioTypeNormal :
1260 default:
1261 str = QObject::tr("Normal", "Ordinary audio track");
1262 break;
1263 }
1264
1265 return str;
1266}
1267
1269{
1271 return;
1272
1273 m_playbackInfo->SaveTotalDuration(std::chrono::milliseconds{m_totalDuration.toFixed(1000)});
1274}
1275
1277{
1279 return;
1280
1282}
1283
1284// Linearly interpolate the value for a given key in the map. If the
1285// key is outside the range of keys in the map, linearly extrapolate
1286// using the fallback ratio.
1288 long long key,
1289 float fallback_ratio)
1290{
1291 uint64_t key1 = 0;
1292 uint64_t key2 = 0;
1293 uint64_t val1 = 0;
1294 uint64_t val2 = 0;
1295
1296 frm_pos_map_t::const_iterator lower = map.lowerBound(key);
1297 // QMap::lowerBound() finds a key >= the given key. We want one
1298 // <= the given key, so back up one element upon > condition.
1299 if (lower != map.begin() && (lower == map.end() || lower.key() > key))
1300 --lower;
1301 if (lower == map.end() || lower.key() > key)
1302 {
1303 key1 = 0;
1304 val1 = 0;
1305 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
1306 QString("TranslatePosition(key=%1): extrapolating to (0,0)")
1307 .arg(key));
1308 }
1309 else
1310 {
1311 key1 = lower.key();
1312 val1 = lower.value();
1313 }
1314 // Find the next key >= the given key. QMap::lowerBound() is
1315 // precisely correct in this case.
1316 frm_pos_map_t::const_iterator upper = map.lowerBound(key);
1317 if (upper == map.end())
1318 {
1319 // Extrapolate from (key1,val1) based on fallback_ratio
1320 key2 = key;
1321 val2 = llroundf(val1 + (fallback_ratio * (key2 - key1)));
1322 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
1323 QString("TranslatePosition(key=%1, ratio=%2): "
1324 "extrapolating to (%3,%4)")
1325 .arg(key).arg(fallback_ratio).arg(key2).arg(val2));
1326 return val2;
1327 }
1328 key2 = upper.key();
1329 val2 = upper.value();
1330 if (key1 == key2) // this happens for an exact keyframe match
1331 return val2; // can also set key2 = key1 + 1 avoid dividing by zero
1332
1333 return llround(val1 + ((double) (key - key1) * (val2 - val1) / (key2 - key1)));
1334}
1335
1336// Convert from an absolute frame number (not cutlist adjusted) to its
1337// cutlist-adjusted position in milliseconds.
1338std::chrono::milliseconds DecoderBase::TranslatePositionFrameToMs(long long position,
1339 float fallback_framerate,
1340 const frm_dir_map_t &cutlist)
1341{
1342 QMutexLocker locker(&m_positionMapLock);
1343 // Accurate calculation of duration requires an up-to-date
1344 // duration map. However, the last frame (total duration) will
1345 // almost always appear to be past the end of the duration map, so
1346 // we limit duration map syncing to once every 3 seconds (a
1347 // somewhat arbitrary value).
1348 if (!m_frameToDurMap.empty())
1349 {
1350 frm_pos_map_t::const_iterator it = m_frameToDurMap.cend();
1351 --it;
1352 if (position > it.key())
1353 {
1354 if (!m_lastPositionMapUpdate.isValid() ||
1355 (QDateTime::currentDateTime() >
1356 m_lastPositionMapUpdate.addSecs(3)))
1358 }
1359 }
1360 return std::chrono::milliseconds(TranslatePositionAbsToRel(cutlist, position, m_frameToDurMap,
1361 1000 / fallback_framerate));
1362}
1363
1364// Convert from a cutlist-adjusted position in milliseconds to its
1365// absolute frame number (not cutlist-adjusted).
1366uint64_t DecoderBase::TranslatePositionMsToFrame(std::chrono::milliseconds dur_ms,
1367 float fallback_framerate,
1368 const frm_dir_map_t &cutlist)
1369{
1370 QMutexLocker locker(&m_positionMapLock);
1371 // Convert relative position in milliseconds (cutlist-adjusted) to
1372 // its absolute position in milliseconds (not cutlist-adjusted).
1373 uint64_t ms = TranslatePositionRelToAbs(cutlist, dur_ms.count(), m_frameToDurMap,
1374 1000 / fallback_framerate);
1375 // Convert absolute position in milliseconds to its absolute frame
1376 // number.
1377 return TranslatePosition(m_durToFrameMap, ms, fallback_framerate / 1000);
1378}
1379
1380// Convert from an "absolute" (not cutlist-adjusted) value to its
1381// "relative" (cutlist-adjusted) mapped value. Usually the position
1382// argument is a frame number, the map argument maps frames to
1383// milliseconds, the fallback_ratio is 1000/framerate_fps, and the
1384// return value is in milliseconds.
1385//
1386// If the map and fallback_ratio arguments are omitted, it simply
1387// converts from an absolute frame number to a relative
1388// (cutlist-adjusted) frame number.
1389uint64_t
1391 uint64_t absPosition, // frames
1392 const frm_pos_map_t &map, // frame->ms
1393 float fallback_ratio)
1394{
1395 uint64_t subtraction = 0;
1396 uint64_t startOfCutRegion = 0;
1397 bool withinCut = false;
1398 bool first = true;
1399 for (frm_dir_map_t::const_iterator i = deleteMap.begin();
1400 i != deleteMap.end(); ++i)
1401 {
1402 if (first)
1403 withinCut = (i.value() == MARK_CUT_END);
1404 first = false;
1405 if (i.key() > absPosition)
1406 break;
1407 uint64_t mappedKey = TranslatePosition(map, i.key(), fallback_ratio);
1408 if (i.value() == MARK_CUT_START && !withinCut)
1409 {
1410 withinCut = true;
1411 startOfCutRegion = mappedKey;
1412 }
1413 else if (i.value() == MARK_CUT_END && withinCut)
1414 {
1415 withinCut = false;
1416 subtraction += (mappedKey - startOfCutRegion);
1417 }
1418 }
1419 uint64_t mappedPos = TranslatePosition(map, absPosition, fallback_ratio);
1420 if (withinCut)
1421 subtraction += (mappedPos - startOfCutRegion);
1422 return mappedPos - subtraction;
1423}
1424
1425// Convert from a "relative" (cutlist-adjusted) value to its
1426// "absolute" (not cutlist-adjusted) mapped value. Usually the
1427// position argument is in milliseconds, the map argument maps frames
1428// to milliseconds, the fallback_ratio is 1000/framerate_fps, and the
1429// return value is also in milliseconds. Upon return, if necessary,
1430// the result may need a separate, non-cutlist adjusted conversion
1431// from milliseconds to frame number, using the inverse
1432// millisecond-to-frame map and the inverse fallback_ratio; see for
1433// example TranslatePositionMsToFrame().
1434//
1435// If the map and fallback_ratio arguments are omitted, it simply
1436// converts from a relatve (cutlist-adjusted) frame number to an
1437// absolute frame number.
1438uint64_t
1440 uint64_t relPosition, // ms
1441 const frm_pos_map_t &map, // frame->ms
1442 float fallback_ratio)
1443{
1444 uint64_t addition = 0;
1445 uint64_t startOfCutRegion = 0;
1446 bool withinCut = false;
1447 bool first = true;
1448 for (frm_dir_map_t::const_iterator i = deleteMap.begin();
1449 i != deleteMap.end(); ++i)
1450 {
1451 if (first)
1452 withinCut = (i.value() == MARK_CUT_END);
1453 first = false;
1454 uint64_t mappedKey = TranslatePosition(map, i.key(), fallback_ratio);
1455 if (i.value() == MARK_CUT_START && !withinCut)
1456 {
1457 withinCut = true;
1458 startOfCutRegion = mappedKey;
1459 if (relPosition + addition <= startOfCutRegion)
1460 break;
1461 }
1462 else if (i.value() == MARK_CUT_END && withinCut)
1463 {
1464 withinCut = false;
1465 addition += (mappedKey - startOfCutRegion);
1466 }
1467 }
1468 return relPosition + addition;
1469}
1470
1475AVPixelFormat DecoderBase::GetBestVideoFormat(AVPixelFormat* Formats, const VideoFrameTypes* RenderFormats)
1476{
1477 for (AVPixelFormat *format = Formats; *format != AV_PIX_FMT_NONE; format++)
1478 {
1479 for (auto fmt : *RenderFormats)
1480 if (MythAVUtil::FrameTypeToPixelFormat(fmt) == *format)
1481 return *format;
1482 }
1483 return AV_PIX_FMT_NONE;
1484}
1485
1486/* vim: set expandtab tabstop=4 shiftwidth=4: */
MythAVRational m_totalDuration
Definition: decoderbase.h:293
bool m_posmapStarted
Definition: decoderbase.h:312
QRecursiveMutex m_positionMapLock
Definition: decoderbase.h:315
uint64_t m_frameCounter
Definition: decoderbase.h:292
virtual uint GetTrackCount(uint Type)
MarkTypes m_positionMapType
Definition: decoderbase.h:313
frm_pos_map_t m_frameToDurMap
Definition: decoderbase.h:317
virtual void SetWatchingRecording(bool mode)
Definition: decoderbase.cpp:80
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)
Definition: decoderbase.cpp:40
bool m_watchingRecording
Definition: decoderbase.h:324
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
Definition: decoderbase.h:322
virtual QString GetTrackDesc(uint Type, uint TrackNo)
double m_fps
Definition: decoderbase.h:282
long long m_framesRead
Definition: decoderbase.h:291
virtual int SetTrack(uint Type, int TrackNo)
virtual void UpdateFramesPlayed(void)
virtual ~DecoderBase()
Definition: decoderbase.cpp:29
void SetReadAdjust(long long adjust)
MythMediaBuffer * m_ringBuffer
Definition: decoderbase.h:280
QRecursiveMutex m_trackLock
Definition: decoderbase.h:337
bool m_transcoding
Definition: decoderbase.h:308
void SetRenderFormats(const VideoFrameTypes *RenderFormats)
Definition: decoderbase.cpp:34
bool m_decodeAllSubtitles
Definition: decoderbase.h:338
void FileChanged(void)
virtual bool PosMapFromEnc(void)
Queries encoder for position map data that has not been committed to the DB yet.
void AutoSelectTracks(void)
bool m_exitAfterDecoded
Definition: decoderbase.h:307
std::vector< int > m_languagePreference
language preferences for auto-selection of streams
Definition: decoderbase.h:346
void SaveTotalDuration(void)
virtual void Reset(bool reset_video_data, bool seek_reset, bool reset_file)
Definition: decoderbase.cpp:46
virtual void ResetPosMap(void)
virtual void SetEofState(EofState eof)
Definition: decoderbase.h:131
long long GetLastFrameInPosMap(void) const
long long m_readAdjust
Definition: decoderbase.h:332
void ResetTracks(void)
int GetTrack(uint Type)
static uint64_t TranslatePosition(const frm_pos_map_t &map, long long key, float fallback_ratio)
std::array< StreamInfo, kTrackTypeCount > m_wantedTrack
Definition: decoderbase.h:341
MythPlayer * m_parent
Definition: decoderbase.h:277
frm_pos_map_t m_durToFrameMap
Definition: decoderbase.h:318
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)
long long m_indexOffset
Definition: decoderbase.h:296
int m_keyframeDist
Definition: decoderbase.h:294
bool GetWaitForChange(void) const
DecoderBase(MythPlayer *parent, const ProgramInfo &pginfo)
Definition: decoderbase.cpp:16
virtual bool FindPosition(long long desired_value, bool search_adjusted, int &lower_bound, int &upper_bound)
const VideoFrameTypes * m_renderFormats
Definition: decoderbase.h:349
bool m_waitingForChange
Definition: decoderbase.h:330
virtual bool PosMapFromDb(void)
Definition: decoderbase.cpp:93
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)
bool m_justAfterChange
Definition: decoderbase.h:331
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
Definition: decoderbase.h:326
virtual void SeekReset(long long newkey, uint skipFrames, bool doFlush, bool discardFrames)
Definition: decoderbase.cpp:73
std::array< sinfo_vec_t, kTrackTypeCount > m_tracks
Definition: decoderbase.h:340
virtual bool SyncPositionMap(void)
Updates the position map used for skipping frames.
virtual int AutoSelectTrack(uint Type)
Select best track.
long long m_lastKey
Definition: decoderbase.h:295
virtual QStringList GetTracks(uint Type)
EofState m_atEof
Definition: decoderbase.h:300
int NextTrack(uint Type)
QDateTime m_lastPositionMapUpdate
Definition: decoderbase.h:319
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
Definition: decoderbase.h:343
long long m_framesPlayed
Definition: decoderbase.h:290
std::vector< PosMapEntry > m_positionMap
Definition: decoderbase.h:316
std::array< int, kTrackTypeCount > m_currentTrack
Definition: decoderbase.h:339
virtual bool DoRewindSeek(long long desiredFrame)
void SetDecodeAllSubtitles(bool DecodeAll)
bool m_recordingHasPositionMap
Definition: decoderbase.h:311
uint64_t m_seekSnap
Definition: decoderbase.h:321
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
Definition: decoderbase.h:278
std::chrono::milliseconds TranslatePositionFrameToMs(long long position, float fallback_framerate, const frm_dir_map_t &cutlist)
std::array< StreamInfo, kTrackTypeCount > m_selectedTrack
Definition: decoderbase.h:342
C++ wrapper for FFmpeg libavutil AVRational.
long long toFixed(long long base) const
Convert the rational number to fixed point.
bool isValid() const
bool isNonzero() const
static AVPixelFormat FrameTypeToPixelFormat(VideoFrameType Type)
Definition: mythavutil.cpp:31
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.
bool IsDisc(void) const
bool IsDVD(void) const
long long Seek(long long Position, int Whence, bool HasLock=false)
const MythDVDBuffer * DVD(void) const
virtual bool IsInDiscMenuOrStillFrame(void) const
const MythBDBuffer * BD(void) const
bool IsBD(void) const
void SetFramesPlayed(uint64_t played)
Definition: mythplayer.cpp:556
void SetKeyframeDistance(int keyframedistance)
Definition: mythplayer.cpp:313
virtual void tracksChanged(uint TrackType)
Definition: mythplayer.h:205
void FileChangedCallback()
Definition: mythplayer.cpp:938
void SetFileLength(std::chrono::seconds total, int frames)
Definition: mythplayer.cpp:381
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.
Definition: mythtimer.h:14
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:91
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
Holds information on recordings and videos.
Definition: programinfo.h:74
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
unsigned int uint
Definition: compat.h:60
#define LOC
Definition: decoderbase.cpp:14
int to_track_type(const QString &str)
QString toString(TrackType type)
AudioTrackType
Definition: decoderbase.h:56
@ kAudioTypeCommentary
Definition: decoderbase.h:62
@ kAudioTypeAudioDescription
Definition: decoderbase.h:58
@ kAudioTypeSpokenSubs
Definition: decoderbase.h:61
@ kAudioTypeHearingImpaired
Definition: decoderbase.h:60
@ kAudioTypeNormal
Definition: decoderbase.h:57
@ kAudioTypeCleanEffects
Definition: decoderbase.h:59
@ kEofStateNone
Definition: decoderbase.h:69
TrackType
Track types.
Definition: decoderbase.h:27
@ kTrackTypeCC608
Definition: decoderbase.h:32
@ kTrackTypeRawText
Definition: decoderbase.h:36
@ kTrackTypeSubtitle
Definition: decoderbase.h:31
@ kTrackTypeTextSubtitle
Definition: decoderbase.h:42
@ kTrackTypeCount
Definition: decoderbase.h:38
@ kTrackTypeTeletextMenu
Definition: decoderbase.h:35
@ kTrackTypeCC708
Definition: decoderbase.h:33
@ kTrackTypeTeletextCaptions
Definition: decoderbase.h:34
@ kTrackTypeAudio
Definition: decoderbase.h:29
@ kTrackTypeVideo
Definition: decoderbase.h:30
@ kDecodeNothing
Definition: decoderbase.h:49
QString iso639_key_toName(int iso639_2)
Converts a canonical key to language name in English.
Definition: iso639.cpp:114
std::vector< int > iso639_get_language_key_list(void)
Definition: iso639.cpp:61
ISO 639-1 and ISO 639-2 support functions.
std::chrono::seconds secondsFromFloat(T value)
Helper function for convert a floating point number to a duration.
Definition: mythchrono.h:68
std::vector< VideoFrameType > VideoFrameTypes
Definition: mythframe.h:82
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MarkTypes
Definition: programtypes.h:46
@ MARK_CUT_START
Definition: programtypes.h:55
@ MARK_KEYFRAME
Definition: programtypes.h:61
@ MARK_GOP_BYFRAME
Definition: programtypes.h:63
@ MARK_CUT_END
Definition: programtypes.h:54
@ MARK_UNSET
Definition: programtypes.h:49
@ MARK_DURATION_MS
Definition: programtypes.h:73
@ MARK_GOP_START
Definition: programtypes.h:60
QMap< uint64_t, MarkTypes > frm_dir_map_t
Frame # -> Mark map.
Definition: programtypes.h:117
QMap< long long, long long > frm_pos_map_t
Frame # -> File offset map.
Definition: programtypes.h:44