MythTV  0.27pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
decoderbase.cpp
Go to the documentation of this file.
1 #include <unistd.h>
2 #include <math.h>
3 
4 #include <algorithm>
5 using namespace std;
6 
7 #include "mythconfig.h"
8 
9 #include "mythplayer.h"
10 #include "remoteencoder.h"
11 #include "mythdbcon.h"
12 #include "mythlogging.h"
13 #include "decoderbase.h"
14 #include "programinfo.h"
15 #include "livetvchain.h"
16 #include "iso639.h"
17 #include "DVD/dvdringbuffer.h"
18 #include "Bluray/bdringbuffer.h"
19 
20 #define LOC QString("Dec: ")
21 
23  : m_parent(parent), m_playbackinfo(new ProgramInfo(pginfo)),
24  m_audio(m_parent->GetAudio()), ringBuffer(NULL),
25 
26  current_width(640), current_height(480),
27  current_aspect(1.33333), fps(29.97),
28  bitrate(4000),
29 
30  framesPlayed(0), framesRead(0),
31  totalDuration(AVRationalInit(0)),
32  lastKey(0), keyframedist(-1), indexOffset(0),
33  trackTotalDuration(false),
34 
35  ateof(kEofStateNone), exitafterdecoded(false), transcoding(false),
36 
37  hasFullPositionMap(false), recordingHasPositionMap(false),
38  posmapStarted(false), positionMapType(MARK_UNSET),
39 
40  m_positionMapLock(QMutex::Recursive),
41  dontSyncPositionMap(false),
42 
43  seeksnap(UINT64_MAX), livetv(false), watchingrecording(false),
44 
45  hasKeyFrameAdjustTable(false), lowbuffers(false),
46  getrawframes(false), getrawvideo(false),
47  errored(false), waitingForChange(false), readAdjust(0),
48  justAfterChange(false),
49  video_inverted(false),
50  decodeAllSubtitles(false),
51  // language preference
52  languagePreference(iso639_get_language_key_list())
53 {
54  ResetTracks();
55  tracks[kTrackTypeAudio].push_back(StreamInfo(0, 0, 0, 0, 0));
56  tracks[kTrackTypeCC608].push_back(StreamInfo(0, 0, 0, 1, 0));
57  tracks[kTrackTypeCC608].push_back(StreamInfo(0, 0, 2, 3, 0));
58 }
59 
61 {
62  if (m_playbackinfo)
63  delete m_playbackinfo;
64 }
65 
67 {
68  if (m_playbackinfo)
69  delete m_playbackinfo;
70  m_playbackinfo = new ProgramInfo(pginfo);
71 }
72 
73 void DecoderBase::Reset(bool reset_video_data, bool seek_reset, bool reset_file)
74 {
75  LOG(VB_PLAYBACK, LOG_INFO, LOC +
76  QString("Reset: Video %1, Seek %2, File %3")
77  .arg(reset_video_data).arg(seek_reset).arg(reset_file));
78 
79  if (seek_reset)
80  {
81  SeekReset(0, 0, true, true);
82  }
83 
84  if (reset_video_data)
85  {
86  ResetPosMap();
87  framesPlayed = 0;
88  framesRead = 0;
90  dontSyncPositionMap = false;
91  }
92 
93  if (reset_file)
94  {
95  waitingForChange = false;
97  }
98 }
99 
100 void DecoderBase::SeekReset(long long, uint, bool, bool)
101 {
102  readAdjust = 0;
103 }
104 
106 {
107  bool wereWatchingRecording = watchingrecording;
108 
109  // When we switch from WatchingRecording to WatchingPreRecorded,
110  // re-get the positionmap
111  posmapStarted = false;
113 
114  if (wereWatchingRecording && !watchingrecording)
115  SyncPositionMap();
116 }
117 
119 {
120  if (!m_playbackinfo)
121  return false;
122 
123  // Overwrites current positionmap with entire contents of database
124  frm_pos_map_t posMap, durMap;
125 
126  if (ringBuffer && ringBuffer->IsDVD())
127  {
128  long long totframes;
129  keyframedist = 15;
130  fps = ringBuffer->DVD()->GetFrameRate();
131  if (fps < 26 && fps > 24)
132  keyframedist = 12;
133  totframes = (long long)(ringBuffer->DVD()->GetTotalTimeOfTitle() * fps);
134  posMap[totframes] = ringBuffer->DVD()->GetTotalReadPosition();
135  }
136  else if (ringBuffer && ringBuffer->IsBD())
137  {
138  long long totframes;
139  keyframedist = 15;
140  fps = ringBuffer->BD()->GetFrameRate();
141  if (fps < 26 && fps > 24)
142  keyframedist = 12;
143  totframes = (long long)(ringBuffer->BD()->GetTotalTimeOfTitle() * fps);
144  posMap[totframes] = ringBuffer->BD()->GetTotalReadPosition();
145 #if 0
146  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
147  QString("%1 TotalTimeOfTitle() in ticks, %2 TotalReadPosition() "
148  "in bytes, %3 is fps")
149  .arg(ringBuffer->BD()->GetTotalTimeOfTitle())
150  .arg(ringBuffer->BD()->GetTotalReadPosition()).arg(fps));
151 #endif
152  }
153  else if ((positionMapType == MARK_UNSET) ||
154  (keyframedist == -1))
155  {
157  if (!posMap.empty())
158  {
160  if (keyframedist == -1)
161  keyframedist = 1;
162  }
163  else
164  {
166  if (!posMap.empty())
167  {
169  if (keyframedist == -1)
170  {
171  keyframedist = 15;
172  if (fps < 26 && fps > 24)
173  keyframedist = 12;
174  }
175  }
176  else
177  {
179  if (!posMap.empty())
180  {
181  // keyframedist should be set in the fileheader so no
182  // need to try to determine it in this case
184  }
185  }
186  }
187  }
188  else
189  {
191  }
192 
193  if (posMap.empty())
194  return false; // no position map in recording
195 
197 
198  QMutexLocker locker(&m_positionMapLock);
199  m_positionMap.clear();
200  m_positionMap.reserve(posMap.size());
201 
202  for (frm_pos_map_t::const_iterator it = posMap.begin();
203  it != posMap.end(); ++it)
204  {
205  PosMapEntry e = {it.key(), it.key() * keyframedist, *it};
206  m_positionMap.push_back(e);
207  }
208 
209  if (!m_positionMap.empty() && !(ringBuffer && ringBuffer->IsDisc()))
210  indexOffset = m_positionMap[0].index;
211 
212  if (!m_positionMap.empty())
213  {
214  LOG(VB_PLAYBACK, LOG_INFO, LOC +
215  QString("Position map filled from DB to: %1")
216  .arg(m_positionMap.back().index));
217  }
218 
219  uint64_t last = 0;
220  for (frm_pos_map_t::const_iterator it = durMap.begin();
221  it != durMap.end(); ++it)
222  {
223  m_frameToDurMap[it.key()] = it.value();
224  m_durToFrameMap[it.value()] = it.key();
225  last = it.key();
226  }
227 
228  if (!m_durToFrameMap.empty())
229  {
230  LOG(VB_PLAYBACK, LOG_INFO, LOC +
231  QString("Duration map filled from DB to: %1").arg(last));
232  }
233 
234  return true;
235 }
236 
246 {
247  if (!m_parent || keyframedist < 1)
248  return false;
249 
250  unsigned long long start = 0;
251  {
252  QMutexLocker locker(&m_positionMapLock);
253  if (!m_positionMap.empty())
254  start = m_positionMap.back().index + 1;
255  }
256 
257  frm_pos_map_t posMap, durMap;
258  if (!m_parent->PosMapFromEnc(start, posMap, durMap))
259  return false;
260 
261  QMutexLocker locker(&m_positionMapLock);
262 
263  // append this new position map to class's
264  m_positionMap.reserve(m_positionMap.size() + posMap.size());
265  uint64_t last_index = m_positionMap.back().index;
266  for (frm_pos_map_t::const_iterator it = posMap.begin();
267  it != posMap.end(); ++it)
268  {
269  if (it.key() <= last_index)
270  continue;
271 
272  PosMapEntry e = {it.key(), it.key() * keyframedist, *it};
273  m_positionMap.push_back(e);
274  }
275 
276  if (!m_positionMap.empty() && !(ringBuffer && ringBuffer->IsDisc()))
277  indexOffset = m_positionMap[0].index;
278 
279  if (!m_positionMap.empty())
280  {
281  LOG(VB_PLAYBACK, LOG_INFO, LOC +
282  QString("Position map filled from Encoder to: %1")
283  .arg(m_positionMap.back().index));
284  }
285 
286  bool isEmpty = m_frameToDurMap.empty();
287  if (!isEmpty)
288  {
289  frm_pos_map_t::const_iterator it = m_frameToDurMap.end();
290  --it;
291  last_index = it.key();
292  }
293  for (frm_pos_map_t::const_iterator it = durMap.begin();
294  it != durMap.end(); ++it)
295  {
296  if (!isEmpty && it.key() <= last_index)
297  continue; // we released the m_positionMapLock for a few ms...
298  m_frameToDurMap[it.key()] = it.value();
299  m_durToFrameMap[it.value()] = it.key();
300  }
301 
302  if (!m_frameToDurMap.empty())
303  {
304  frm_pos_map_t::const_iterator it = m_frameToDurMap.end();
305  --it;
306  LOG(VB_PLAYBACK, LOG_INFO, LOC +
307  QString("Duration map filled from Encoder to: %1").arg(it.key()));
308  }
309 
310  return true;
311 }
312 
313 unsigned long DecoderBase::GetPositionMapSize(void) const
314 {
315  QMutexLocker locker(&m_positionMapLock);
316  return (unsigned long) m_positionMap.size();
317 }
318 
342 {
343  LOG(VB_PLAYBACK, LOG_INFO, LOC +
344  QString("Resyncing position map. posmapStarted = %1"
345  " livetv(%2) watchingRec(%3)")
346  .arg((int) posmapStarted).arg(livetv).arg(watchingrecording));
347 
349  return false;
350 
351  unsigned long old_posmap_size = GetPositionMapSize();
352  unsigned long new_posmap_size = old_posmap_size;
353 
354  if (livetv || watchingrecording)
355  {
356  if (!posmapStarted)
357  {
358  // starting up -- try first from database
359  PosMapFromDb();
360  new_posmap_size = GetPositionMapSize();
361  LOG(VB_PLAYBACK, LOG_INFO, LOC +
362  QString("SyncPositionMap watchingrecording, from DB: "
363  "%1 entries") .arg(new_posmap_size));
364  }
365  // always try to get more from encoder
366  if (!PosMapFromEnc())
367  {
368  LOG(VB_PLAYBACK, LOG_INFO, LOC +
369  QString("SyncPositionMap watchingrecording no entries "
370  "from encoder, try DB"));
371  PosMapFromDb(); // try again from db
372  }
373 
374  new_posmap_size = GetPositionMapSize();
375  LOG(VB_PLAYBACK, LOG_INFO, LOC +
376  QString("SyncPositionMap watchingrecording total: %1 entries")
377  .arg(new_posmap_size));
378  }
379  else
380  {
381  // watching prerecorded ... just get from db
382  if (!posmapStarted)
383  {
384  PosMapFromDb();
385 
386  new_posmap_size = GetPositionMapSize();
387  LOG(VB_PLAYBACK, LOG_INFO, LOC +
388  QString("SyncPositionMap prerecorded, from DB: %1 entries")
389  .arg(new_posmap_size));
390  }
391  }
392 
393  bool ret_val = new_posmap_size > old_posmap_size;
394 
395  if (ret_val && keyframedist > 0)
396  {
397  long long totframes = 0;
398  int length = 0;
399 
400  if (ringBuffer && ringBuffer->IsDVD())
401  {
402  length = ringBuffer->DVD()->GetTotalTimeOfTitle();
403  QMutexLocker locker(&m_positionMapLock);
404  totframes = m_positionMap.back().index;
405  }
406  else if (ringBuffer && ringBuffer->IsBD())
407  {
408  length = ringBuffer->BD()->GetTotalTimeOfTitle();
409  QMutexLocker locker(&m_positionMapLock);
410  totframes = m_positionMap.back().index;
411  }
412  else
413  {
414  QMutexLocker locker(&m_positionMapLock);
415  totframes = m_positionMap.back().index * keyframedist;
416  if (fps)
417  length = (int)((totframes * 1.0) / fps);
418  }
419 
420  m_parent->SetFileLength(length, totframes);
422  posmapStarted = true;
423 
424  LOG(VB_PLAYBACK, LOG_INFO, LOC +
425  QString("SyncPositionMap, new totframes: %1, new length: %2, "
426  "posMap size: %3")
427  .arg(totframes).arg(length).arg(new_posmap_size));
428  }
429  recordingHasPositionMap |= (0 != new_posmap_size);
430  {
431  QMutexLocker locker(&m_positionMapLock);
432  m_lastPositionMapUpdate = QDateTime::currentDateTime();
433  }
434  return ret_val;
435 }
436 
437 // returns true iff found exactly
438 // searches position if search_pos, index otherwise
439 bool DecoderBase::FindPosition(long long desired_value, bool search_adjusted,
440  int &lower_bound, int &upper_bound)
441 {
442  QMutexLocker locker(&m_positionMapLock);
443  // Binary search
444  long long size = (long long) m_positionMap.size();
445  long long lower = -1;
446  long long upper = size;
447 
448  if (!search_adjusted && keyframedist > 0)
449  desired_value /= keyframedist;
450 
451  while (upper - 1 > lower)
452  {
453  long long i = (upper + lower) / 2;
454  long long value;
455  if (search_adjusted)
456  value = m_positionMap[i].adjFrame;
457  else
458  value = m_positionMap[i].index - indexOffset;
459  if (value == desired_value)
460  {
461  // found it
462  upper_bound = i;
463  lower_bound = i;
464 
465  LOG(VB_PLAYBACK, LOG_INFO, LOC +
466  QString("FindPosition(%1, search%2 adjusted)")
467  .arg(desired_value).arg((search_adjusted) ? "" : " not") +
468  QString(" --> [%1:%2(%3)]")
469  .arg(i).arg(GetKey(m_positionMap[i]))
470  .arg(m_positionMap[i].pos));
471 
472  return true;
473  }
474  else if (value > desired_value)
475  upper = i;
476  else
477  lower = i;
478  }
479  // Did not find it exactly -- return bounds
480 
481  if (search_adjusted)
482  {
483  while (lower >= 0 && m_positionMap[lower].adjFrame > desired_value)
484  lower--;
485  while (upper < size && m_positionMap[upper].adjFrame < desired_value)
486  upper++;
487  }
488  else
489  {
490  while (lower >= 0 &&
491  (m_positionMap[lower].index - indexOffset) > desired_value)
492  lower--;
493  while (upper < size &&
494  (m_positionMap[upper].index - indexOffset) < desired_value)
495  upper++;
496  }
497  // keep in bounds
498  lower = max(lower, 0LL);
499  upper = min(upper, size - 1LL);
500 
501  upper_bound = upper;
502  lower_bound = lower;
503  bool empty = m_positionMap.empty();
504 
505  LOG(VB_PLAYBACK, LOG_INFO, LOC +
506  QString("FindPosition(%1, search%3 adjusted)")
507  .arg(desired_value).arg((search_adjusted) ? "" : " not") +
508  QString(" --> \n\t\t\t[%1:%2(%3),%4:%5(%6)]")
509  .arg(lower_bound)
510  .arg(empty ? -1 : GetKey(m_positionMap[lower_bound]))
511  .arg(m_positionMap[lower_bound].pos)
512  .arg(upper_bound)
513  .arg(empty ? -1 : GetKey(m_positionMap[upper_bound]))
514  .arg(empty ? -1 : m_positionMap[upper_bound].pos));
515 
516  return false;
517 }
518 
519 uint64_t DecoderBase::SavePositionMapDelta(uint64_t first, uint64_t last)
520 {
521  MythTimer ttm, ctm, stm;
522  ttm.start();
523 
524  QMutexLocker locker(&m_positionMapLock);
526  uint64_t saved = 0;
527 
529  return saved;
530 
531  ctm.start();
532  frm_pos_map_t posMap;
533  for (uint i = 0; i < m_positionMap.size(); i++)
534  {
535  if ((uint64_t)m_positionMap[i].index < first)
536  continue;
537  if ((uint64_t)m_positionMap[i].index > last)
538  break;
539 
540  posMap[m_positionMap[i].index] = m_positionMap[i].pos;
541  saved++;
542  }
543 
544  frm_pos_map_t durMap;
545  for (frm_pos_map_t::const_iterator it = m_frameToDurMap.begin();
546  it != m_frameToDurMap.end(); ++it)
547  {
548  if (it.key() < first)
549  continue;
550  if (it.key() > last)
551  break;
552  durMap[it.key()] = it.value();
553  }
554 
555  locker.unlock();
556 
557  stm.start();
558  m_playbackinfo->SavePositionMapDelta(posMap, type);
560 
561 #if 0
562  LOG(VB_GENERAL, LOG_DEBUG, LOC +
563  QString("Saving position map [%1,%2] w/%3 keyframes, "
564  "took (%4,%5,%6) ms")
565  .arg(first).arg(last).arg(saved)
566  .arg(ttm.elapsed())
567  .arg(ctm.elapsed()-stm.elapsed()).arg(stm.elapsed()));
568 #endif
569 
570  return saved;
571 }
572 
573 bool DecoderBase::DoRewind(long long desiredFrame, bool discardFrames)
574 {
575  LOG(VB_PLAYBACK, LOG_INFO, LOC +
576  QString("DoRewind(%1 (%2), %3 discard frames)")
577  .arg(desiredFrame).arg(framesPlayed)
578  .arg((discardFrames) ? "do" : "don't"));
579 
580  if (!DoRewindSeek(desiredFrame))
581  return false;
582 
585 
586  // Do any Extra frame-by-frame seeking for exactseeks mode
587  // And flush pre-seek frame if we are allowed to and need to..
588  int normalframes = (uint64_t)(desiredFrame - (framesPlayed - 1)) > seeksnap
589  ? desiredFrame - framesPlayed : 0;
590  normalframes = max(normalframes, 0);
591  SeekReset(lastKey, normalframes, true, discardFrames);
592 
593  if (discardFrames || (ringBuffer && ringBuffer->IsDisc()))
595 
596  return true;
597 }
598 
599 long long DecoderBase::GetKey(const PosMapEntry &e) const
600 {
601  long long kf = (ringBuffer && ringBuffer->IsDisc()) ?
602  1LL : keyframedist;
603  return (hasKeyFrameAdjustTable) ? e.adjFrame :(e.index - indexOffset) * kf;
604 }
605 
606 bool DecoderBase::DoRewindSeek(long long desiredFrame)
607 {
608  ConditionallyUpdatePosMap(desiredFrame);
609 
610  if (!GetPositionMapSize())
611  {
612  LOG(VB_GENERAL, LOG_ERR, LOC + "PosMap is empty, can't seek");
613  return false;
614  }
615 
616  if (!ringBuffer)
617  {
618  LOG(VB_GENERAL, LOG_ERR, LOC + "No ringBuffer yet, can't seek");
619  return false;
620  }
621 
622  // Find keyframe <= desiredFrame, store in lastKey (frames)
623  int pre_idx, post_idx;
624  FindPosition(desiredFrame, hasKeyFrameAdjustTable, pre_idx, post_idx);
625 
626  PosMapEntry e;
627  {
628  QMutexLocker locker(&m_positionMapLock);
629  PosMapEntry e_pre = m_positionMap[pre_idx];
630  PosMapEntry e_post = m_positionMap[post_idx];
631  int pos_idx = pre_idx;
632  e = e_pre;
633  if (((uint64_t) (GetKey(e_post) - desiredFrame)) <= seeksnap &&
634  framesPlayed - 1 > GetKey(e_post) &&
635  GetKey(e_post) - desiredFrame <= desiredFrame - GetKey(e_pre))
636  {
637  // Snap to the right if e_post is within snap distance and
638  // is at least as close a snap as e_pre. Take into
639  // account that if framesPlayed has already reached
640  // e_post, we should only snap to the left.
641  pos_idx = post_idx;
642  e = e_post;
643  }
644  lastKey = GetKey(e);
645 
646  // ??? Don't rewind past the beginning of the file
647  while (e.pos < 0)
648  {
649  pos_idx++;
650  if (pos_idx >= (int)m_positionMap.size())
651  return false;
652 
653  e = m_positionMap[pos_idx];
654  lastKey = GetKey(e);
655  }
656  }
657 
658  ringBuffer->Seek(e.pos, SEEK_SET);
659 
660  return true;
661 }
662 
664 {
665  QMutexLocker locker(&m_positionMapLock);
666  posmapStarted = false;
667  m_positionMap.clear();
668 }
669 
671 {
672  long long last_frame = 0;
673 
674  QMutexLocker locker(&m_positionMapLock);
675  if (!m_positionMap.empty())
676  last_frame = GetKey(m_positionMap.back());
677 
678  return last_frame;
679 }
680 
681 long long DecoderBase::ConditionallyUpdatePosMap(long long desiredFrame)
682 {
683  long long last_frame = GetLastFrameInPosMap();
684 
685  if (desiredFrame < 0)
686  return last_frame;
687 
688  // Resync keyframe map if we are trying to seek to a frame
689  // not yet equalled or exceeded in the seek map.
690  if (desiredFrame < last_frame)
691  return last_frame;
692 
693  LOG(VB_PLAYBACK, LOG_INFO, LOC +
694  "ConditionallyUpdatePosMap: Not enough info in positionMap," +
695  QString("\n\t\t\twe need frame %1 but highest we have is %2.")
696  .arg(desiredFrame).arg(last_frame));
697 
698  SyncPositionMap();
699 
700  last_frame = GetLastFrameInPosMap();
701 
702  if (desiredFrame > last_frame)
703  {
704  LOG(VB_PLAYBACK, LOG_INFO, LOC +
705  "ConditionallyUpdatePosMap: Still not "
706  "enough info in positionMap after sync, " +
707  QString("\n\t\t\twe need frame %1 but highest we have "
708  "is %2. Will attempt to seek frame-by-frame")
709  .arg(desiredFrame).arg(last_frame));
710  }
711 
712  return last_frame;
713 }
714 
724 bool DecoderBase::DoFastForward(long long desiredFrame, bool discardFrames)
725 {
726  LOG(VB_PLAYBACK, LOG_INFO, LOC +
727  QString("DoFastForward(%1 (%2), %3 discard frames)")
728  .arg(desiredFrame).arg(framesPlayed)
729  .arg((discardFrames) ? "do" : "don't"));
730 
731  if (!ringBuffer)
732  {
733  LOG(VB_GENERAL, LOG_ERR, LOC + "No ringBuffer yet, can't fast forward");
734  return false;
735  }
736 
737  if (ringBuffer->IsDVD() &&
739  ringBuffer->DVD()->TitleTimeLeft() < 5)
740  {
741  return false;
742  }
743  // Rewind if we have already played the desiredFrame. The +1 is for
744  // MPEG4 NUV files, which need to decode an extra frame sometimes.
745  // This shouldn't effect how this works in general because this is
746  // only triggered on the first keyframe/frame skip when paused. At
747  // that point the decoding is more than one frame ahead of display.
748  if (desiredFrame+1 < framesPlayed)
749  return DoRewind(desiredFrame, discardFrames);
750  desiredFrame = max(desiredFrame, framesPlayed);
751 
752  // Save rawframe state, for later restoration...
753  bool oldrawstate = getrawframes;
754  getrawframes = false;
755 
756  ConditionallyUpdatePosMap(desiredFrame);
757 
758  // Fetch last keyframe in position map
759  long long last_frame = GetLastFrameInPosMap();
760 
761  // If the desiredFrame is past the end of the position map,
762  // do some frame-by-frame seeking until we get to it.
763  bool needflush = false;
764  if (desiredFrame > last_frame)
765  {
766  LOG(VB_GENERAL, LOG_NOTICE, LOC +
767  QString("DoFastForward(): desiredFrame(%1) > last_frame(%2)")
768  .arg(desiredFrame).arg(last_frame));
769 
770  if (desiredFrame - last_frame > 32)
771  {
772  LOG(VB_GENERAL, LOG_ERR, LOC + "DoFastForward(): "
773  "Desired frame is way past the end of the keyframe map!"
774  "\n\t\t\tSeeking to last keyframe instead.");
775  desiredFrame = last_frame;
776  }
777 
778  needflush = true;
779 
780  // Handle non-frame-by-frame seeking
781  DoFastForwardSeek(last_frame, needflush);
782 
783  exitafterdecoded = true; // don't actualy get a frame
784  while ((desiredFrame > last_frame) && !ateof)
785  {
786  GetFrame(kDecodeNothing); // don't need to return frame...
787  SyncPositionMap();
788  last_frame = GetLastFrameInPosMap();
789  }
790  exitafterdecoded = false; // allow frames to be returned again
791 
792  if (ateof)
793  {
794  // Re-enable rawframe state if it was enabled before FF
795  getrawframes = oldrawstate;
796  return false;
797  }
798  }
799 
800  {
801  QMutexLocker locker(&m_positionMapLock);
802  if (m_positionMap.empty())
803  {
804  // Re-enable rawframe state if it was enabled before FF
805  getrawframes = oldrawstate;
806  return false;
807  }
808  }
809 
810  // Handle non-frame-by-frame seeking
811  DoFastForwardSeek(desiredFrame, needflush);
812 
813  // Do any Extra frame-by-frame seeking for exactseeks mode
814  // And flush pre-seek frame if we are allowed to and need to..
815  int normalframes = (uint64_t)(desiredFrame - (framesPlayed - 1)) > seeksnap
816  ? desiredFrame - framesPlayed : 0;
817  normalframes = max(normalframes, 0);
818  SeekReset(lastKey, normalframes, needflush, discardFrames);
819 
820  if (discardFrames)
822 
823  // Re-enable rawframe state if it was enabled before FF
824  getrawframes = oldrawstate;
825 
826  return true;
827 }
828 
843 void DecoderBase::DoFastForwardSeek(long long desiredFrame, bool &needflush)
844 {
845  if (!ringBuffer)
846  {
847  LOG(VB_GENERAL, LOG_ERR, LOC +
848  "No ringBuffer yet, can't fast forward seek");
849  return;
850  }
851 
852  int pre_idx, post_idx;
853  FindPosition(desiredFrame, hasKeyFrameAdjustTable, pre_idx, post_idx);
854 
855  // if exactseeks, use keyframe <= desiredFrame
856 
857  PosMapEntry e, e_pre, e_post;
858  {
859  QMutexLocker locker(&m_positionMapLock);
860  e_pre = m_positionMap[pre_idx];
861  e_post = m_positionMap[post_idx];
862  }
863  e = e_pre;
864  if (((uint64_t) (GetKey(e_post) - desiredFrame)) <= seeksnap &&
865  (framesPlayed - 1 >= GetKey(e_pre) ||
866  GetKey(e_post) - desiredFrame < desiredFrame - GetKey(e_pre)))
867  {
868  // Snap to the right if e_post is within snap distance and is
869  // a closer snap than e_pre. Take into account that if
870  // framesPlayed has already reached e_pre, we should only snap
871  // to the right.
872  e = e_post;
873  }
874  lastKey = GetKey(e);
875 
876  if (framesPlayed < lastKey)
877  {
878  ringBuffer->Seek(e.pos, SEEK_SET);
879  needflush = true;
882  }
883 }
884 
886 {
888 }
889 
891 {
892  ResetPosMap();
893  framesPlayed = 0;
894  framesRead = 0;
896 
897  waitingForChange = false;
898  justAfterChange = true;
899 
901 }
902 
903 void DecoderBase::SetReadAdjust(long long adjust)
904 {
905  readAdjust = adjust;
906 }
907 
909 {
910  waitingForChange = true;
911 }
912 
914 {
915  return waitingForChange;
916 }
917 
918 QStringList DecoderBase::GetTracks(uint type) const
919 {
920  QStringList list;
921 
922  QMutexLocker locker(avcodeclock);
923 
924  for (uint i = 0; i < tracks[type].size(); i++)
925  list += GetTrackDesc(type, i);
926 
927  return list;
928 }
929 
931 {
932  if (trackNo >= tracks[type].size())
933  return 0;
934 
935  return tracks[type][trackNo].language_index;
936 }
937 
938 QString DecoderBase::GetTrackDesc(uint type, uint trackNo) const
939 {
940  if (trackNo >= tracks[type].size())
941  return "";
942 
943  QMutexLocker locker(avcodeclock);
944 
945  QString type_msg = toString((TrackType)type);
946  int lang = tracks[type][trackNo].language;
947  int hnum = trackNo + 1;
948  if (kTrackTypeCC608 == type)
949  hnum = tracks[type][trackNo].stream_id;
950 
951  if (!lang)
952  return type_msg + QString(" %1").arg(hnum);
953  else
954  {
955  QString lang_msg = iso639_key_toName(lang);
956  return type_msg + QString(" %1: %2").arg(hnum).arg(lang_msg);
957  }
958 }
959 
960 int DecoderBase::SetTrack(uint type, int trackNo)
961 {
962  if (trackNo >= (int)tracks[type].size())
963  return false;
964 
965  QMutexLocker locker(avcodeclock);
966 
967  currentTrack[type] = max(-1, trackNo);
968 
969  if (currentTrack[type] < 0)
971  else
972  {
974  selectedTrack[type] = tracks[type][currentTrack[type]];
975  }
976 
977  return currentTrack[type];
978 }
979 
981 {
982  QMutexLocker locker(avcodeclock);
983 
984  if (trackNo >= tracks[type].size())
985  {
986  StreamInfo si;
987  return si;
988  }
989 
990  return tracks[type][trackNo];
991 }
992 
994 {
995  QMutexLocker locker(avcodeclock);
996 
997  for (uint i = 0; i < tracks[type].size(); i++)
998  if (info.stream_id == tracks[type][i].stream_id)
999  return false;
1000 
1001  tracks[type].push_back(info);
1002 
1003  if (m_parent)
1004  m_parent->TracksChanged(type);
1005 
1006  return true;
1007 }
1008 
1025 {
1026  uint numStreams = tracks[type].size();
1027 
1028  if ((currentTrack[type] >= 0) &&
1029  (currentTrack[type] < (int)numStreams))
1030  {
1031  return true; // track already selected
1032  }
1033 
1034  if (!numStreams)
1035  {
1036  currentTrack[type] = -1;
1038  return false; // no tracks available
1039  }
1040 
1041  int selTrack = (1 == numStreams) ? 0 : -1;
1042 
1043  if ((selTrack < 0) &&
1044  wantedTrack[type].language>=-1 && numStreams)
1045  {
1046  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Trying to reselect track");
1047  // Try to reselect user selected track stream.
1048  // This should find the stream after a commercial
1049  // break and in some cases after a channel change.
1050  int wlang = wantedTrack[type].language;
1052  for (uint i = 0; i < numStreams; i++)
1053  {
1054  if (wlang == tracks[type][i].language)
1055  selTrack = i;
1056  if (windx == tracks[type][i].language_index)
1057  break;
1058  }
1059  }
1060 
1061  if (selTrack < 0 && numStreams)
1062  {
1063  // Select the best track. Primary attribute is to favor a
1064  // forced track. Secondary attribute is language preference,
1065  // in order of most preferred to least preferred language.
1066  // Third attribute is track order, preferring the earliest
1067  // track.
1068  LOG(VB_PLAYBACK, LOG_INFO,
1069  LOC + "Trying to select track (w/lang & forced)");
1070  const int kForcedWeight = (1 << 20);
1071  const int kLanguageWeight = (1 << 10);
1072  const int kPositionWeight = (1 << 0);
1073  int bestScore = -1;
1074  selTrack = 0;
1075  for (uint i = 0; i < numStreams; i++)
1076  {
1077  int forced = (type == kTrackTypeSubtitle &&
1078  tracks[type][i].forced &&
1080  int position = numStreams - i;
1081  int language = 0;
1082  for (uint j = 0;
1083  (language == 0) && (j < languagePreference.size()); ++j)
1084  {
1085  if (tracks[type][i].language == languagePreference[j])
1086  language = languagePreference.size() - j;
1087  }
1088  int score = kForcedWeight * forced
1089  + kLanguageWeight * language
1090  + kPositionWeight * position;
1091  if (score > bestScore)
1092  {
1093  bestScore = score;
1094  selTrack = i;
1095  }
1096  }
1097  }
1098 
1099  int oldTrack = currentTrack[type];
1100  currentTrack[type] = (selTrack < 0) ? -1 : selTrack;
1102  selectedTrack[type] = tmp;
1103 
1104  if (wantedTrack[type].av_stream_index < 0)
1105  wantedTrack[type] = tmp;
1106 
1107  int lang = tracks[type][currentTrack[type]].language;
1108  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1109  QString("Selected track #%1 in the %2 language(%3)")
1110  .arg(currentTrack[type]+1)
1111  .arg(iso639_key_toName(lang)).arg(lang));
1112 
1113  if (m_parent && (oldTrack != currentTrack[type]))
1114  m_parent->TracksChanged(type);
1115 
1116  return selTrack;
1117 }
1118 
1120 {
1121  QString str = QObject::tr("Track");
1122 
1123  if (kTrackTypeAudio == type)
1124  str = QObject::tr("Audio track");
1125  else if (kTrackTypeVideo == type)
1126  str = QObject::tr("Video track");
1127  else if (kTrackTypeSubtitle == type)
1128  str = QObject::tr("Subtitle track");
1129  else if (kTrackTypeCC608 == type)
1130  str = QObject::tr("CC", "EIA-608 closed captions");
1131  else if (kTrackTypeCC708 == type)
1132  str = QObject::tr("ATSC CC", "EIA-708 closed captions");
1133  else if (kTrackTypeTeletextCaptions == type)
1134  str = QObject::tr("TT CC", "Teletext closed captions");
1135  else if (kTrackTypeTeletextMenu == type)
1136  str = QObject::tr("TT Menu", "Teletext Menu");
1137  else if (kTrackTypeRawText == type)
1138  str = QObject::tr("Text", "Text stream");
1139  else if (kTrackTypeTextSubtitle == type)
1140  str = QObject::tr("TXT File", "Text File");
1141  return str;
1142 }
1143 
1144 int to_track_type(const QString &str)
1145 {
1146  int ret = -1;
1147 
1148  if (str.startsWith("AUDIO"))
1149  ret = kTrackTypeAudio;
1150  else if (str.startsWith("VIDEO"))
1151  ret = kTrackTypeVideo;
1152  else if (str.startsWith("SUBTITLE"))
1153  ret = kTrackTypeSubtitle;
1154  else if (str.startsWith("CC608"))
1155  ret = kTrackTypeCC608;
1156  else if (str.startsWith("CC708"))
1157  ret = kTrackTypeCC708;
1158  else if (str.startsWith("TTC"))
1160  else if (str.startsWith("TTM"))
1161  ret = kTrackTypeTeletextMenu;
1162  else if (str.startsWith("TFL"))
1163  ret = kTrackTypeTextSubtitle;
1164  else if (str.startsWith("RAWTEXT"))
1165  ret = kTrackTypeRawText;
1166  return ret;
1167 }
1168 
1170 {
1171  QString str;
1172 
1173  switch (type)
1174  {
1176  str = QObject::tr("Audio Description",
1177  "On-screen events described for the visually impaired");
1178  break;
1179  case kAudioTypeCleanEffects :
1180  str = QObject::tr("Clean Effects",
1181  "No dialog, background audio only");
1182  break;
1184  str = QObject::tr("Hearing Impaired",
1185  "Clear dialog for the hearing impaired");
1186  break;
1187  case kAudioTypeSpokenSubs :
1188  str = QObject::tr("Spoken Subtitles",
1189  "Subtitles are read out for the visually impaired");
1190  break;
1191  case kAudioTypeCommentary :
1192  str = QObject::tr("Commentary", "Director/Cast commentary track");
1193  break;
1194  case kAudioTypeNormal :
1195  default:
1196  str = QObject::tr("Normal", "Ordinary audio track");
1197  break;
1198  }
1199 
1200  return str;
1201 }
1202 
1204 {
1205  if (!m_playbackinfo || av_q2d(totalDuration) == 0)
1206  return;
1207 
1208  m_playbackinfo->SaveTotalDuration(1000000 * av_q2d(totalDuration));
1209 }
1210 
1212 {
1213  if (!m_playbackinfo || !framesRead)
1214  return;
1215 
1217 }
1218 
1219 // Linearly interpolate the value for a given key in the map. If the
1220 // key is outside the range of keys in the map, linearly extrapolate
1221 // using the fallback ratio.
1223  uint64_t key,
1224  float fallback_ratio)
1225 {
1226  uint64_t key1, key2;
1227  uint64_t val1, val2;
1228 
1229  frm_pos_map_t::const_iterator lower = map.lowerBound(key);
1230  // QMap::lowerBound() finds a key >= the given key. We want one
1231  // <= the given key, so back up one element upon > condition.
1232  if (lower != map.begin() && (lower == map.end() || lower.key() > key))
1233  --lower;
1234  if (lower == map.end() || lower.key() > key)
1235  {
1236  key1 = 0;
1237  val1 = 0;
1238  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
1239  QString("TranslatePosition(key=%1): extrapolating to (0,0)")
1240  .arg(key));
1241  }
1242  else
1243  {
1244  key1 = lower.key();
1245  val1 = lower.value();
1246  }
1247  // Find the next key >= the given key. QMap::lowerBound() is
1248  // precisely correct in this case.
1249  frm_pos_map_t::const_iterator upper = map.lowerBound(key);
1250  if (upper == map.end())
1251  {
1252  // Extrapolate from (key1,val1) based on fallback_ratio
1253  key2 = key;
1254  val2 = val1 + fallback_ratio * (key2 - key1) + 0.5;
1255  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
1256  QString("TranslatePosition(key=%1, ratio=%2): "
1257  "extrapolating to (%3,%4)")
1258  .arg(key).arg(fallback_ratio).arg(key2).arg(val2));
1259  return val2;
1260  }
1261  else
1262  {
1263  key2 = upper.key();
1264  val2 = upper.value();
1265  }
1266  if (key1 == key2) // this happens for an exact keyframe match
1267  return val2; // can also set key2 = key1 + 1 avoid dividing by zero
1268 
1269  return val1 + (double) (key - key1) * (val2 - val1) / (key2 - key1) + 0.5;
1270 }
1271 
1272 // Convert from an absolute frame number (not cutlist adjusted) to its
1273 // cutlist-adjusted position in milliseconds.
1274 uint64_t DecoderBase::TranslatePositionFrameToMs(uint64_t position,
1275  float fallback_framerate,
1276  const frm_dir_map_t &cutlist)
1277 {
1278  QMutexLocker locker(&m_positionMapLock);
1279  // Accurate calculation of duration requires an up-to-date
1280  // duration map. However, the last frame (total duration) will
1281  // almost always appear to be past the end of the duration map, so
1282  // we limit duration map syncing to once every 3 seconds (a
1283  // somewhat arbitrary value).
1284  if (!m_frameToDurMap.empty())
1285  {
1286  frm_pos_map_t::const_iterator it = m_frameToDurMap.end();
1287  --it;
1288  if (position > it.key())
1289  {
1290  if (!m_lastPositionMapUpdate.isValid() ||
1291  (QDateTime::currentDateTime() >
1292  m_lastPositionMapUpdate.addSecs(3)))
1293  SyncPositionMap();
1294  }
1295  }
1296  return TranslatePositionAbsToRel(cutlist, position, m_frameToDurMap,
1297  1000 / fallback_framerate);
1298 }
1299 
1300 // Convert from a cutlist-adjusted position in milliseconds to its
1301 // absolute frame number (not cutlist-adjusted).
1303  float fallback_framerate,
1304  const frm_dir_map_t &cutlist)
1305 {
1306  QMutexLocker locker(&m_positionMapLock);
1307  // Convert relative position in milliseconds (cutlist-adjusted) to
1308  // its absolute position in milliseconds (not cutlist-adjusted).
1309  uint64_t ms = TranslatePositionRelToAbs(cutlist, dur_ms, m_frameToDurMap,
1310  1000 / fallback_framerate);
1311  // Convert absolute position in milliseconds to its absolute frame
1312  // number.
1313  return TranslatePosition(m_durToFrameMap, ms, fallback_framerate / 1000);
1314 }
1315 
1316 // Convert from an "absolute" (not cutlist-adjusted) value to its
1317 // "relative" (cutlist-adjusted) mapped value. Usually the position
1318 // argument is a frame number, the map argument maps frames to
1319 // milliseconds, the fallback_ratio is 1000/framerate_fps, and the
1320 // return value is in milliseconds.
1321 //
1322 // If the map and fallback_ratio arguments are omitted, it simply
1323 // converts from an absolute frame number to a relative
1324 // (cutlist-adjusted) frame number.
1325 uint64_t
1327  uint64_t absPosition, // frames
1328  const frm_pos_map_t &map, // frame->ms
1329  float fallback_ratio)
1330 {
1331  uint64_t subtraction = 0;
1332  uint64_t startOfCutRegion = 0;
1333  bool withinCut = false;
1334  bool first = true;
1335  for (frm_dir_map_t::const_iterator i = deleteMap.begin();
1336  i != deleteMap.end(); ++i)
1337  {
1338  if (first)
1339  withinCut = (i.value() == MARK_CUT_END);
1340  first = false;
1341  if (i.key() > absPosition)
1342  break;
1343  uint64_t mappedKey = TranslatePosition(map, i.key(), fallback_ratio);
1344  if (i.value() == MARK_CUT_START && !withinCut)
1345  {
1346  withinCut = true;
1347  startOfCutRegion = mappedKey;
1348  }
1349  else if (i.value() == MARK_CUT_END && withinCut)
1350  {
1351  withinCut = false;
1352  subtraction += (mappedKey - startOfCutRegion);
1353  }
1354  }
1355  uint64_t mappedPos = TranslatePosition(map, absPosition, fallback_ratio);
1356  if (withinCut)
1357  subtraction += (mappedPos - startOfCutRegion);
1358  return mappedPos - subtraction;
1359 }
1360 
1361 // Convert from a "relative" (cutlist-adjusted) value to its
1362 // "absolute" (not cutlist-adjusted) mapped value. Usually the
1363 // position argument is in milliseconds, the map argument maps frames
1364 // to milliseconds, the fallback_ratio is 1000/framerate_fps, and the
1365 // return value is also in milliseconds. Upon return, if necessary,
1366 // the result may need a separate, non-cutlist adjusted conversion
1367 // from milliseconds to frame number, using the inverse
1368 // millisecond-to-frame map and the inverse fallback_ratio; see for
1369 // example TranslatePositionMsToFrame().
1370 //
1371 // If the map and fallback_ratio arguments are omitted, it simply
1372 // converts from a relatve (cutlist-adjusted) frame number to an
1373 // absolute frame number.
1374 uint64_t
1376  uint64_t relPosition, // ms
1377  const frm_pos_map_t &map, // frame->ms
1378  float fallback_ratio)
1379 {
1380  uint64_t addition = 0;
1381  uint64_t startOfCutRegion = 0;
1382  bool withinCut = false;
1383  bool first = true;
1384  for (frm_dir_map_t::const_iterator i = deleteMap.begin();
1385  i != deleteMap.end(); ++i)
1386  {
1387  if (first)
1388  withinCut = (i.value() == MARK_CUT_END);
1389  first = false;
1390  uint64_t mappedKey = TranslatePosition(map, i.key(), fallback_ratio);
1391  if (i.value() == MARK_CUT_START && !withinCut)
1392  {
1393  withinCut = true;
1394  startOfCutRegion = mappedKey;
1395  if (relPosition + addition <= startOfCutRegion)
1396  break;
1397  }
1398  else if (i.value() == MARK_CUT_END && withinCut)
1399  {
1400  withinCut = false;
1401  addition += (mappedKey - startOfCutRegion);
1402  }
1403  }
1404  return relPosition + addition;
1405 }
1406 
1407 
1408 /* vim: set expandtab tabstop=4 shiftwidth=4: */