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