MythTV  0.26-pre
decoderbase.cpp
Go to the documentation of this file.
00001 #include <unistd.h>
00002 #include <math.h>
00003 
00004 #include <algorithm>
00005 using namespace std;
00006 
00007 #include "mythconfig.h"
00008 
00009 #include "mythplayer.h"
00010 #include "remoteencoder.h"
00011 #include "mythdbcon.h"
00012 #include "mythlogging.h"
00013 #include "decoderbase.h"
00014 #include "programinfo.h"
00015 #include "livetvchain.h"
00016 #include "iso639.h"
00017 #include "DVD/dvdringbuffer.h"
00018 #include "Bluray/bdringbuffer.h"
00019 
00020 #define LOC QString("Dec: ")
00021 
00022 DecoderBase::DecoderBase(MythPlayer *parent, const ProgramInfo &pginfo)
00023     : m_parent(parent), m_playbackinfo(new ProgramInfo(pginfo)),
00024       m_audio(m_parent->GetAudio()), ringBuffer(NULL),
00025 
00026       current_width(640), current_height(480),
00027       current_aspect(1.33333), fps(29.97),
00028       bitrate(4000),
00029 
00030       framesPlayed(0), framesRead(0), totalDuration(0),
00031       lastKey(0), keyframedist(-1), indexOffset(0),
00032 
00033       ateof(false), exitafterdecoded(false), transcoding(false),
00034 
00035       hasFullPositionMap(false), recordingHasPositionMap(false),
00036       posmapStarted(false), positionMapType(MARK_UNSET),
00037 
00038       m_positionMapLock(QMutex::Recursive),
00039       dontSyncPositionMap(false),
00040 
00041       seeksnap(-1), livetv(false), watchingrecording(false),
00042 
00043       hasKeyFrameAdjustTable(false), lowbuffers(false),
00044       getrawframes(false), getrawvideo(false),
00045       errored(false), waitingForChange(false), readAdjust(0),
00046       justAfterChange(false),
00047       decodeAllSubtitles(false),
00048       // language preference
00049       languagePreference(iso639_get_language_key_list())
00050 {
00051     ResetTracks();
00052     tracks[kTrackTypeAudio].push_back(StreamInfo(0, 0, 0, 0, 0));
00053     tracks[kTrackTypeCC608].push_back(StreamInfo(0, 0, 0, 1, 0));
00054     tracks[kTrackTypeCC608].push_back(StreamInfo(0, 0, 2, 3, 0));
00055 }
00056 
00057 DecoderBase::~DecoderBase()
00058 {
00059     if (m_playbackinfo)
00060         delete m_playbackinfo;
00061 }
00062 
00063 void DecoderBase::SetProgramInfo(const ProgramInfo &pginfo)
00064 {
00065     if (m_playbackinfo)
00066         delete m_playbackinfo;
00067     m_playbackinfo = new ProgramInfo(pginfo);
00068 }
00069 
00070 void DecoderBase::Reset(bool reset_video_data, bool seek_reset, bool reset_file)
00071 {
00072     LOG(VB_PLAYBACK, LOG_INFO, LOC +
00073         QString("Reset: Video %1, Seek %2, File %3")
00074             .arg(reset_video_data).arg(seek_reset).arg(reset_file));
00075 
00076     if (seek_reset)
00077     {
00078         SeekReset(0, 0, true, true);
00079     }
00080 
00081     if (reset_video_data)
00082     {
00083         ResetPosMap();
00084         framesPlayed = 0;
00085         framesRead = 0;
00086         totalDuration = 0;
00087         dontSyncPositionMap = false;
00088     }
00089 
00090     if (reset_file)
00091     {
00092         waitingForChange = false;
00093         SetEof(false);
00094     }
00095 }
00096 
00097 void DecoderBase::SeekReset(long long, uint, bool, bool)
00098 {
00099     readAdjust = 0;
00100 }
00101 
00102 void DecoderBase::setWatchingRecording(bool mode)
00103 {
00104     bool wereWatchingRecording = watchingrecording;
00105 
00106     // When we switch from WatchingRecording to WatchingPreRecorded,
00107     // re-get the positionmap
00108     posmapStarted = false;
00109     watchingrecording = mode;
00110 
00111     if (wereWatchingRecording && !watchingrecording)
00112         SyncPositionMap();
00113 }
00114 
00115 bool DecoderBase::PosMapFromDb(void)
00116 {
00117     if (!m_playbackinfo)
00118         return false;
00119 
00120     // Overwrites current positionmap with entire contents of database
00121     frm_pos_map_t posMap;
00122 
00123     if (ringBuffer->IsDVD())
00124     {
00125         long long totframes;
00126         keyframedist = 15;
00127         fps = ringBuffer->DVD()->GetFrameRate();
00128         if (fps < 26 && fps > 24)
00129            keyframedist = 12;
00130         totframes = (long long)(ringBuffer->DVD()->GetTotalTimeOfTitle() * fps);
00131         posMap[totframes] = ringBuffer->DVD()->GetTotalReadPosition();
00132     }
00133     else if (ringBuffer->IsBD())
00134     {
00135         long long totframes;
00136         keyframedist = 15;
00137         fps = ringBuffer->BD()->GetFrameRate();
00138         if (fps < 26 && fps > 24)
00139            keyframedist = 12;
00140         totframes = (long long)(ringBuffer->BD()->GetTotalTimeOfTitle() * fps);
00141         posMap[totframes] = ringBuffer->BD()->GetTotalReadPosition();
00142 #if 0
00143         LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
00144             QString("%1 TotalTimeOfTitle() in ticks, %2 TotalReadPosition() "
00145                     "in bytes, %3 is fps")
00146                 .arg(ringBuffer->BD()->GetTotalTimeOfTitle())
00147                 .arg(ringBuffer->BD()->GetTotalReadPosition()).arg(fps));
00148 #endif
00149     }
00150     else if ((positionMapType == MARK_UNSET) ||
00151         (keyframedist == -1))
00152     {
00153         m_playbackinfo->QueryPositionMap(posMap, MARK_GOP_BYFRAME);
00154         if (!posMap.empty())
00155         {
00156             positionMapType = MARK_GOP_BYFRAME;
00157             if (keyframedist == -1)
00158                 keyframedist = 1;
00159         }
00160         else
00161         {
00162             m_playbackinfo->QueryPositionMap(posMap, MARK_GOP_START);
00163             if (!posMap.empty())
00164             {
00165                 positionMapType = MARK_GOP_START;
00166                 if (keyframedist == -1)
00167                 {
00168                     keyframedist = 15;
00169                     if (fps < 26 && fps > 24)
00170                         keyframedist = 12;
00171                 }
00172             }
00173             else
00174             {
00175                 m_playbackinfo->QueryPositionMap(posMap, MARK_KEYFRAME);
00176                 if (!posMap.empty())
00177                 {
00178                     // keyframedist should be set in the fileheader so no
00179                     // need to try to determine it in this case
00180                     positionMapType = MARK_KEYFRAME;
00181                 }
00182             }
00183         }
00184     }
00185     else
00186     {
00187         m_playbackinfo->QueryPositionMap(posMap, positionMapType);
00188     }
00189 
00190     if (posMap.empty())
00191         return false; // no position map in recording
00192 
00193     QMutexLocker locker(&m_positionMapLock);
00194     m_positionMap.clear();
00195     m_positionMap.reserve(posMap.size());
00196 
00197     for (frm_pos_map_t::const_iterator it = posMap.begin();
00198          it != posMap.end(); ++it)
00199     {
00200         PosMapEntry e = {it.key(), it.key() * keyframedist, *it};
00201         m_positionMap.push_back(e);
00202     }
00203 
00204     if (!m_positionMap.empty() && !ringBuffer->IsDisc())
00205         indexOffset = m_positionMap[0].index;
00206 
00207     if (!m_positionMap.empty())
00208     {
00209         LOG(VB_PLAYBACK, LOG_INFO, LOC +
00210             QString("Position map filled from DB to: %1")
00211                 .arg(m_positionMap.back().index));
00212     }
00213 
00214     return true;
00215 }
00216 
00225 bool DecoderBase::PosMapFromEnc(void)
00226 {
00227     if (!m_parent || keyframedist < 1)
00228         return false;
00229 
00230     unsigned long long start = 0;
00231     {
00232         QMutexLocker locker(&m_positionMapLock);
00233         if (!m_positionMap.empty())
00234             start = m_positionMap.back().index + 1;
00235     }
00236 
00237     QMap<long long, long long> posMap;
00238     if (!m_parent->PosMapFromEnc(start, posMap))
00239         return false;
00240 
00241     QMutexLocker locker(&m_positionMapLock);
00242 
00243     // append this new position map to class's
00244     m_positionMap.reserve(m_positionMap.size() + posMap.size());
00245     long long last_index = m_positionMap.back().index;
00246     for (QMap<long long,long long>::const_iterator it = posMap.begin();
00247          it != posMap.end(); ++it)
00248     {
00249         if (it.key() <= last_index)
00250             continue; // we released the m_positionMapLock for a few ms...
00251 
00252         PosMapEntry e = {it.key(), it.key() * keyframedist, *it};
00253         m_positionMap.push_back(e);
00254     }
00255 
00256     if (!m_positionMap.empty() && !ringBuffer->IsDisc())
00257         indexOffset = m_positionMap[0].index;
00258 
00259     if (!m_positionMap.empty())
00260     {
00261         LOG(VB_PLAYBACK, LOG_INFO, LOC +
00262             QString("Position map filled from Encoder to: %1")
00263                 .arg(m_positionMap.back().index));
00264     }
00265 
00266     return true;
00267 }
00268 
00269 unsigned long DecoderBase::GetPositionMapSize(void) const
00270 {
00271     QMutexLocker locker(&m_positionMapLock);
00272     return (unsigned long) m_positionMap.size();
00273 }
00274 
00297 bool DecoderBase::SyncPositionMap(void)
00298 {
00299     LOG(VB_PLAYBACK, LOG_INFO, LOC +
00300         QString("Resyncing position map. posmapStarted = %1"
00301                 " livetv(%2) watchingRec(%3)")
00302             .arg((int) posmapStarted).arg(livetv).arg(watchingrecording));
00303 
00304     if (dontSyncPositionMap)
00305         return false;
00306 
00307     unsigned long old_posmap_size = GetPositionMapSize();
00308     unsigned long new_posmap_size = old_posmap_size;
00309 
00310     if (livetv || watchingrecording)
00311     {
00312         if (!posmapStarted)
00313         {
00314             // starting up -- try first from database
00315             PosMapFromDb();
00316             new_posmap_size = GetPositionMapSize();
00317             LOG(VB_PLAYBACK, LOG_INFO, LOC +
00318                 QString("SyncPositionMap watchingrecording, from DB: "
00319                         "%1 entries") .arg(new_posmap_size));
00320         }
00321         // always try to get more from encoder
00322         if (!PosMapFromEnc())
00323         {
00324             LOG(VB_PLAYBACK, LOG_INFO, LOC +
00325                 QString("SyncPositionMap watchingrecording no entries "
00326                         "from encoder, try DB"));
00327             PosMapFromDb(); // try again from db
00328         }
00329 
00330         new_posmap_size = GetPositionMapSize();
00331         LOG(VB_PLAYBACK, LOG_INFO, LOC +
00332             QString("SyncPositionMap watchingrecording total: %1 entries")
00333                 .arg(new_posmap_size));
00334     }
00335     else
00336     {
00337         // watching prerecorded ... just get from db
00338         if (!posmapStarted)
00339         {
00340             PosMapFromDb();
00341 
00342             new_posmap_size = GetPositionMapSize();
00343             LOG(VB_PLAYBACK, LOG_INFO, LOC +
00344                 QString("SyncPositionMap prerecorded, from DB: %1 entries")
00345                     .arg(new_posmap_size));
00346         }
00347     }
00348 
00349     bool ret_val = new_posmap_size > old_posmap_size;
00350 
00351     if (ret_val && keyframedist > 0)
00352     {
00353         long long totframes = 0;
00354         int length = 0;
00355 
00356         if (ringBuffer->IsDVD())
00357         {
00358             length = ringBuffer->DVD()->GetTotalTimeOfTitle();
00359             QMutexLocker locker(&m_positionMapLock);
00360             totframes = m_positionMap.back().index;
00361         }
00362         else if (ringBuffer->IsBD())
00363         {
00364             length = ringBuffer->BD()->GetTotalTimeOfTitle();
00365             QMutexLocker locker(&m_positionMapLock);
00366             totframes = m_positionMap.back().index;
00367         }
00368         else
00369         {
00370             QMutexLocker locker(&m_positionMapLock);
00371             totframes = m_positionMap.back().index * keyframedist;
00372             if (fps)
00373                 length = (int)((totframes * 1.0) / fps);
00374         }
00375 
00376         m_parent->SetFileLength(length, totframes);
00377         m_parent->SetKeyframeDistance(keyframedist);
00378         posmapStarted = true;
00379 
00380         LOG(VB_PLAYBACK, LOG_INFO, LOC +
00381             QString("SyncPositionMap, new totframes: %1, new length: %2, "
00382                     "posMap size: %3")
00383                 .arg(totframes).arg(length).arg(new_posmap_size));
00384     }
00385     recordingHasPositionMap |= (0 != new_posmap_size);
00386     return ret_val;
00387 }
00388 
00389 // returns true iff found exactly
00390 // searches position if search_pos, index otherwise
00391 bool DecoderBase::FindPosition(long long desired_value, bool search_adjusted,
00392                                int &lower_bound, int &upper_bound)
00393 {
00394     QMutexLocker locker(&m_positionMapLock);
00395     // Binary search
00396     long long size  = (long long) m_positionMap.size();
00397     long long lower = -1;
00398     long long upper = size;
00399 
00400     if (!search_adjusted && keyframedist > 0)
00401         desired_value /= keyframedist;
00402 
00403     while (upper - 1 > lower)
00404     {
00405         long long i = (upper + lower) / 2;
00406         long long value;
00407         if (search_adjusted)
00408             value = m_positionMap[i].adjFrame;
00409         else
00410             value = m_positionMap[i].index - indexOffset;
00411         if (value == desired_value)
00412         {
00413             // found it
00414             upper_bound = i;
00415             lower_bound = i;
00416 
00417             LOG(VB_PLAYBACK, LOG_INFO, LOC +
00418                 QString("FindPosition(%1, search%2 adjusted)")
00419                     .arg(desired_value).arg((search_adjusted) ? "" : " not") +
00420                 QString(" --> [%1:%2(%3)]")
00421                     .arg(i).arg(GetKey(m_positionMap[i]))
00422                     .arg(m_positionMap[i].pos));
00423 
00424             return true;
00425         }
00426         else if (value > desired_value)
00427             upper = i;
00428         else
00429             lower = i;
00430     }
00431     // Did not find it exactly -- return bounds
00432 
00433     if (search_adjusted)
00434     {
00435         while (lower >= 0 && m_positionMap[lower].adjFrame > desired_value)
00436             lower--;
00437         while (upper < size && m_positionMap[upper].adjFrame < desired_value)
00438             upper++;
00439     }
00440     else
00441     {
00442         while (lower >= 0 &&
00443                (m_positionMap[lower].index - indexOffset) > desired_value)
00444             lower--;
00445         while (upper < size &&
00446                (m_positionMap[upper].index - indexOffset) < desired_value)
00447             upper++;
00448     }
00449     // keep in bounds
00450     lower = max(lower, 0LL);
00451     upper = min(upper, size - 1LL);
00452 
00453     upper_bound = upper;
00454     lower_bound = lower;
00455 
00456     LOG(VB_PLAYBACK, LOG_INFO, LOC +
00457         QString("FindPosition(%1, search%3 adjusted)")
00458             .arg(desired_value).arg((search_adjusted) ? "" : " not") +
00459         QString(" --> \n\t\t\t[%1:%2(%3),%4:%5(%6)]")
00460             .arg(lower_bound).arg(GetKey(m_positionMap[lower_bound]))
00461             .arg(m_positionMap[lower_bound].pos)
00462             .arg(upper_bound).arg(GetKey(m_positionMap[upper_bound]))
00463             .arg(m_positionMap[upper_bound].pos));
00464 
00465     return false;
00466 }
00467 
00468 uint64_t DecoderBase::SavePositionMapDelta(uint64_t first, uint64_t last)
00469 {
00470     MythTimer ttm, ctm, stm;
00471     ttm.start();
00472 
00473     QMutexLocker locker(&m_positionMapLock);
00474     MarkTypes type = positionMapType;
00475     uint64_t saved = 0;
00476 
00477     if (!m_playbackinfo || (positionMapType == MARK_UNSET))
00478         return saved;
00479 
00480     ctm.start();
00481     frm_pos_map_t posMap;
00482     for (uint i = 0; i < m_positionMap.size(); i++)
00483     {
00484         if ((uint64_t)m_positionMap[i].index < first)
00485             continue;
00486         if ((uint64_t)m_positionMap[i].index > last)
00487             break;
00488 
00489         posMap[m_positionMap[i].index] = m_positionMap[i].pos;
00490         saved++;
00491     }
00492 
00493     locker.unlock();
00494 
00495     stm.start();
00496     m_playbackinfo->SavePositionMapDelta(posMap, type);
00497 
00498 #if 0
00499     LOG(VB_GENERAL, LOG_DEBUG, LOC +
00500         QString("Saving position map [%1,%2] w/%3 keyframes, "
00501                 "took (%4,%5,%6) ms")
00502             .arg(first).arg(last).arg(saved)
00503             .arg(ttm.elapsed())
00504             .arg(ctm.elapsed()-stm.elapsed()).arg(stm.elapsed()));
00505 #endif
00506 
00507     return saved;
00508 }
00509 
00510 bool DecoderBase::DoRewind(long long desiredFrame, bool discardFrames)
00511 {
00512     LOG(VB_PLAYBACK, LOG_INFO, LOC +
00513         QString("DoRewind(%1 (%2), %3 discard frames)")
00514             .arg(desiredFrame).arg(framesPlayed)
00515             .arg((discardFrames) ? "do" : "don't"));
00516 
00517     if (!DoRewindSeek(desiredFrame))
00518         return false;
00519 
00520     framesPlayed = lastKey;
00521     framesRead = lastKey;
00522 
00523     // Do any Extra frame-by-frame seeking for exactseeks mode
00524     // And flush pre-seek frame if we are allowed to and need to..
00525     int normalframes = desiredFrame - (framesPlayed - 1) > seeksnap
00526         ? desiredFrame - framesPlayed : 0;
00527     normalframes = max(normalframes, 0);
00528     SeekReset(lastKey, normalframes, true, discardFrames);
00529 
00530     if (ringBuffer->IsDisc() || discardFrames)
00531         m_parent->SetFramesPlayed(framesPlayed+1);
00532 
00533     return true;
00534 }
00535 
00536 long long DecoderBase::GetKey(const PosMapEntry &e) const
00537 {
00538     long long kf = (ringBuffer->IsDisc()) ? 1LL : keyframedist;
00539     return (hasKeyFrameAdjustTable) ? e.adjFrame :(e.index - indexOffset) * kf;
00540 }
00541 
00542 bool DecoderBase::DoRewindSeek(long long desiredFrame)
00543 {
00544     ConditionallyUpdatePosMap(desiredFrame);
00545 
00546     if (!GetPositionMapSize())
00547     {
00548         LOG(VB_GENERAL, LOG_ERR, LOC + "PosMap is empty, can't seek");
00549         return false;
00550     }
00551 
00552     // Find keyframe <= desiredFrame, store in lastKey (frames)
00553     int pre_idx, post_idx;
00554     FindPosition(desiredFrame, hasKeyFrameAdjustTable, pre_idx, post_idx);
00555 
00556     PosMapEntry e;
00557     {
00558         QMutexLocker locker(&m_positionMapLock);
00559         PosMapEntry e_pre  = m_positionMap[pre_idx];
00560         PosMapEntry e_post = m_positionMap[post_idx];
00561         int pos_idx = pre_idx;
00562         e = e_pre;
00563         if (((uint64_t) (GetKey(e_post) - desiredFrame)) <= seeksnap &&
00564             framesPlayed - 1 > GetKey(e_post) &&
00565             GetKey(e_post) - desiredFrame <= desiredFrame - GetKey(e_pre))
00566         {
00567             // Snap to the right if e_post is within snap distance and
00568             // is at least as close a snap as e_pre.  Take into
00569             // account that if framesPlayed has already reached
00570             // e_post, we should only snap to the left.
00571             pos_idx = post_idx;
00572             e = e_post;
00573         }
00574         lastKey = GetKey(e);
00575 
00576         // ??? Don't rewind past the beginning of the file
00577         while (e.pos < 0)
00578         {
00579             pos_idx++;
00580             if (pos_idx >= (int)m_positionMap.size())
00581                 return false;
00582 
00583             e = m_positionMap[pos_idx];
00584             lastKey = GetKey(e);
00585         }
00586     }
00587 
00588     ringBuffer->Seek(e.pos, SEEK_SET);
00589 
00590     return true;
00591 }
00592 
00593 void DecoderBase::ResetPosMap(void)
00594 {
00595     QMutexLocker locker(&m_positionMapLock);
00596     posmapStarted = false;
00597     m_positionMap.clear();
00598 }
00599 
00600 long long DecoderBase::GetLastFrameInPosMap(void) const
00601 {
00602     long long last_frame = 0;
00603 
00604     QMutexLocker locker(&m_positionMapLock);
00605     if (!m_positionMap.empty())
00606         last_frame = GetKey(m_positionMap.back());
00607 
00608     return last_frame;
00609 }
00610 
00611 long long DecoderBase::ConditionallyUpdatePosMap(long long desiredFrame)
00612 {
00613     long long last_frame = GetLastFrameInPosMap();
00614 
00615     if (desiredFrame < 0)
00616         return last_frame;
00617 
00618     // Resync keyframe map if we are trying to seek to a frame
00619     // not yet equalled or exceeded in the seek map.
00620     if (desiredFrame < last_frame)
00621         return last_frame;
00622 
00623     LOG(VB_PLAYBACK, LOG_INFO, LOC +
00624         "ConditionallyUpdatePosMap: Not enough info in positionMap," +
00625         QString("\n\t\t\twe need frame %1 but highest we have is %2.")
00626             .arg(desiredFrame).arg(last_frame));
00627 
00628     SyncPositionMap();
00629 
00630     last_frame = GetLastFrameInPosMap();
00631 
00632     if (desiredFrame > last_frame)
00633     {
00634         LOG(VB_PLAYBACK, LOG_INFO, LOC +
00635             "ConditionallyUpdatePosMap: Still not "
00636             "enough info in positionMap after sync, " +
00637             QString("\n\t\t\twe need frame %1 but highest we have "
00638                     "is %2. Will attempt to seek frame-by-frame")
00639                 .arg(desiredFrame).arg(last_frame));
00640     }
00641 
00642     return last_frame;
00643 }
00644 
00654 bool DecoderBase::DoFastForward(long long desiredFrame, bool discardFrames)
00655 {
00656     LOG(VB_PLAYBACK, LOG_INFO, LOC +
00657         QString("DoFastForward(%1 (%2), %3 discard frames)")
00658             .arg(desiredFrame).arg(framesPlayed)
00659             .arg((discardFrames) ? "do" : "don't"));
00660 
00661     if (ringBuffer->IsDVD() &&
00662         !ringBuffer->IsInDiscMenuOrStillFrame() &&
00663         ringBuffer->DVD()->TitleTimeLeft() < 5)
00664     {
00665         return false;
00666     }
00667     // Rewind if we have already played the desiredFrame. The +1 is for
00668     // MPEG4 NUV files, which need to decode an extra frame sometimes.
00669     // This shouldn't effect how this works in general because this is
00670     // only triggered on the first keyframe/frame skip when paused. At
00671     // that point the decoding is more than one frame ahead of display.
00672     if (desiredFrame+1 < framesPlayed)
00673         return DoRewind(desiredFrame, discardFrames);
00674     desiredFrame = max(desiredFrame, framesPlayed);
00675 
00676     // Save rawframe state, for later restoration...
00677     bool oldrawstate = getrawframes;
00678     getrawframes = false;
00679 
00680     ConditionallyUpdatePosMap(desiredFrame);
00681 
00682     // Fetch last keyframe in position map
00683     long long last_frame = GetLastFrameInPosMap();
00684 
00685     // If the desiredFrame is past the end of the position map,
00686     // do some frame-by-frame seeking until we get to it.
00687     bool needflush = false;
00688     if (desiredFrame > last_frame)
00689     {
00690         LOG(VB_GENERAL, LOG_NOTICE, LOC +
00691             QString("DoFastForward(): desiredFrame(%1) > last_frame(%2)")
00692                 .arg(desiredFrame).arg(last_frame));
00693 
00694         if (desiredFrame - last_frame > 32)
00695         {
00696             LOG(VB_GENERAL, LOG_ERR, LOC + "DoFastForward(): "
00697                     "Desired frame is way past the end of the keyframe map!"
00698                     "\n\t\t\tSeeking to last keyframe instead.");
00699             desiredFrame = last_frame;
00700         }
00701 
00702         needflush = true;
00703 
00704         // Handle non-frame-by-frame seeking
00705         DoFastForwardSeek(last_frame, needflush);
00706 
00707         exitafterdecoded = true; // don't actualy get a frame
00708         while ((desiredFrame > last_frame) && !ateof)
00709         {
00710             GetFrame(kDecodeNothing); // don't need to return frame...
00711             SyncPositionMap();
00712             last_frame = GetLastFrameInPosMap();
00713         }
00714         exitafterdecoded = false; // allow frames to be returned again
00715 
00716         if (ateof)
00717         {
00718             // Re-enable rawframe state if it was enabled before FF
00719             getrawframes = oldrawstate;
00720             return false;
00721         }
00722     }
00723 
00724     {
00725         QMutexLocker locker(&m_positionMapLock);
00726         if (m_positionMap.empty())
00727         {
00728             // Re-enable rawframe state if it was enabled before FF
00729             getrawframes = oldrawstate;
00730             return false;
00731         }
00732     }
00733 
00734     // Handle non-frame-by-frame seeking
00735     DoFastForwardSeek(desiredFrame, needflush);
00736 
00737     // Do any Extra frame-by-frame seeking for exactseeks mode
00738     // And flush pre-seek frame if we are allowed to and need to..
00739     int normalframes = desiredFrame - (framesPlayed - 1) > seeksnap
00740         ? desiredFrame - framesPlayed : 0;
00741     normalframes = max(normalframes, 0);
00742     SeekReset(lastKey, normalframes, needflush, discardFrames);
00743 
00744     if (discardFrames)
00745         m_parent->SetFramesPlayed(framesPlayed+1);
00746 
00747     // Re-enable rawframe state if it was enabled before FF
00748     getrawframes = oldrawstate;
00749 
00750     return true;
00751 }
00752 
00767 void DecoderBase::DoFastForwardSeek(long long desiredFrame, bool &needflush)
00768 {
00769     int pre_idx, post_idx;
00770     FindPosition(desiredFrame, hasKeyFrameAdjustTable, pre_idx, post_idx);
00771 
00772     // if exactseeks, use keyframe <= desiredFrame
00773 
00774     PosMapEntry e, e_pre, e_post;
00775     {
00776         QMutexLocker locker(&m_positionMapLock);
00777         e_pre  = m_positionMap[pre_idx];
00778         e_post = m_positionMap[post_idx];
00779     }
00780     e = e_pre;
00781     if (((uint64_t) (GetKey(e_post) - desiredFrame)) <= seeksnap &&
00782         (framesPlayed - 1 >= GetKey(e_pre) ||
00783          GetKey(e_post) - desiredFrame < desiredFrame - GetKey(e_pre)))
00784     {
00785         // Snap to the right if e_post is within snap distance and is
00786         // a closer snap than e_pre.  Take into account that if
00787         // framesPlayed has already reached e_pre, we should only snap
00788         // to the right.
00789         e = e_post;
00790     }
00791     lastKey = GetKey(e);
00792 
00793     if (framesPlayed < lastKey)
00794     {
00795         ringBuffer->Seek(e.pos, SEEK_SET);
00796         needflush    = true;
00797         framesPlayed = lastKey;
00798         framesRead = lastKey;
00799     }
00800 }
00801 
00802 void DecoderBase::UpdateFramesPlayed(void)
00803 {
00804     m_parent->SetFramesPlayed(framesPlayed);
00805 }
00806 
00807 void DecoderBase::FileChanged(void)
00808 {
00809     ResetPosMap();
00810     framesPlayed = 0;
00811     framesRead = 0;
00812     totalDuration = 0;
00813 
00814     waitingForChange = false;
00815     justAfterChange = true;
00816 
00817     m_parent->FileChangedCallback();
00818 }
00819 
00820 void DecoderBase::SetReadAdjust(long long adjust)
00821 {
00822     readAdjust = adjust;
00823 }
00824 
00825 void DecoderBase::SetWaitForChange(void)
00826 {
00827     waitingForChange = true;
00828 }
00829 
00830 bool DecoderBase::GetWaitForChange(void) const
00831 {
00832     return waitingForChange;
00833 }
00834 
00835 QStringList DecoderBase::GetTracks(uint type) const
00836 {
00837     QStringList list;
00838 
00839     QMutexLocker locker(avcodeclock);
00840 
00841     for (uint i = 0; i < tracks[type].size(); i++)
00842         list += GetTrackDesc(type, i);
00843 
00844     return list;
00845 }
00846 
00847 int DecoderBase::GetTrackLanguageIndex(uint type, uint trackNo) const
00848 {
00849     if (trackNo >= tracks[type].size())
00850         return 0;
00851 
00852     return tracks[type][trackNo].language_index;
00853 }
00854 
00855 QString DecoderBase::GetTrackDesc(uint type, uint trackNo) const
00856 {
00857     if (trackNo >= tracks[type].size())
00858         return "";
00859 
00860     QMutexLocker locker(avcodeclock);
00861 
00862     QString type_msg = toString((TrackType)type);
00863     int lang = tracks[type][trackNo].language;
00864     int hnum = trackNo + 1;
00865     if (kTrackTypeCC608 == type)
00866         hnum = tracks[type][trackNo].stream_id;
00867 
00868     if (!lang)
00869         return type_msg + QString(" %1").arg(hnum);
00870     else
00871     {
00872         QString lang_msg = iso639_key_toName(lang);
00873         return type_msg + QString(" %1: %2").arg(hnum).arg(lang_msg);
00874     }
00875 }
00876 
00877 int DecoderBase::SetTrack(uint type, int trackNo)
00878 {
00879     if (trackNo >= (int)tracks[type].size())
00880         return false;
00881 
00882     QMutexLocker locker(avcodeclock);
00883 
00884     currentTrack[type] = max(-1, trackNo);
00885 
00886     if (currentTrack[type] < 0)
00887         selectedTrack[type].av_stream_index = -1;
00888     else
00889     {
00890         wantedTrack[type]   = tracks[type][currentTrack[type]];
00891         selectedTrack[type] = tracks[type][currentTrack[type]];
00892     }
00893 
00894     return currentTrack[type];
00895 }
00896 
00897 StreamInfo DecoderBase::GetTrackInfo(uint type, uint trackNo) const
00898 {
00899     QMutexLocker locker(avcodeclock);
00900 
00901     if (trackNo >= tracks[type].size())
00902     {
00903         StreamInfo si;
00904         return si;
00905     }
00906 
00907     return tracks[type][trackNo];
00908 }
00909 
00910 bool DecoderBase::InsertTrack(uint type, const StreamInfo &info)
00911 {
00912     QMutexLocker locker(avcodeclock);
00913 
00914     for (uint i = 0; i < tracks[type].size(); i++)
00915         if (info.stream_id == tracks[type][i].stream_id)
00916             return false;
00917 
00918     tracks[type].push_back(info);
00919 
00920     if (m_parent)
00921         m_parent->TracksChanged(type);
00922 
00923     return true;
00924 }
00925 
00941 int DecoderBase::AutoSelectTrack(uint type)
00942 {
00943     uint numStreams = tracks[type].size();
00944 
00945     if ((currentTrack[type] >= 0) &&
00946         (currentTrack[type] < (int)numStreams))
00947     {
00948         return true; // track already selected
00949     }
00950 
00951     if (!numStreams)
00952     {
00953         currentTrack[type] = -1;
00954         selectedTrack[type].av_stream_index = -1;
00955         return false; // no tracks available
00956     }
00957 
00958     int selTrack = (1 == numStreams) ? 0 : -1;
00959 
00960     if ((selTrack < 0) &&
00961         wantedTrack[type].language>=-1 && numStreams)
00962     {
00963         LOG(VB_PLAYBACK, LOG_INFO, LOC + "Trying to reselect track");
00964         // Try to reselect user selected track stream.
00965         // This should find the stream after a commercial
00966         // break and in some cases after a channel change.
00967         int  wlang = wantedTrack[type].language;
00968         uint windx = wantedTrack[type].language_index;
00969         for (uint i = 0; i < numStreams; i++)
00970         {
00971             if (wlang == tracks[type][i].language)
00972                 selTrack = i;
00973             if (windx == tracks[type][i].language_index)
00974                 break;
00975         }
00976     }
00977 
00978     if (selTrack < 0 && numStreams)
00979     {
00980         LOG(VB_PLAYBACK, LOG_INFO, LOC + "Trying to select track (w/lang)");
00981         // Find first track stream that matches a language in
00982         // order of most preferred to least preferred language.
00983         vector<int>::iterator it = languagePreference.begin();
00984         for (; it != languagePreference.end() && (selTrack < 0); ++it)
00985         {
00986             for (uint i = 0; i < numStreams; i++)
00987             {
00988                 if (*it == tracks[type][i].language)
00989                 {
00990                     selTrack = i;
00991                     break;
00992                 }
00993             }
00994         }
00995     }
00996 
00997     if (selTrack < 0 && numStreams)
00998     {
00999         LOG(VB_PLAYBACK, LOG_INFO, LOC + "Selecting first track");
01000         selTrack = 0;
01001     }
01002 
01003     int oldTrack = currentTrack[type];
01004     currentTrack[type] = (selTrack < 0) ? -1 : selTrack;
01005     StreamInfo tmp = tracks[type][currentTrack[type]];
01006     selectedTrack[type] = tmp;
01007 
01008     if (wantedTrack[type].av_stream_index < 0)
01009         wantedTrack[type] = tmp;
01010 
01011     int lang = tracks[type][currentTrack[type]].language;
01012     LOG(VB_PLAYBACK, LOG_INFO, LOC +
01013         QString("Selected track #%1 in the %2 language(%3)")
01014             .arg(currentTrack[type]+1)
01015             .arg(iso639_key_toName(lang)).arg(lang));
01016 
01017     if (m_parent && (oldTrack != currentTrack[type]))
01018         m_parent->TracksChanged(type);
01019 
01020     return selTrack;
01021 }
01022 
01023 QString toString(TrackType type)
01024 {
01025     QString str = QObject::tr("Track");
01026 
01027     if (kTrackTypeAudio == type)
01028         str = QObject::tr("Audio track");
01029     else if (kTrackTypeVideo == type)
01030         str = QObject::tr("Video track");
01031     else if (kTrackTypeSubtitle == type)
01032         str = QObject::tr("Subtitle track");
01033     else if (kTrackTypeCC608 == type)
01034         str = QObject::tr("CC", "EIA-608 closed captions");
01035     else if (kTrackTypeCC708 == type)
01036         str = QObject::tr("ATSC CC", "EIA-708 closed captions");
01037     else if (kTrackTypeTeletextCaptions == type)
01038         str = QObject::tr("TT CC", "Teletext closed captions");
01039     else if (kTrackTypeTeletextMenu == type)
01040         str = QObject::tr("TT Menu", "Teletext Menu");
01041     else if (kTrackTypeRawText == type)
01042         str = QObject::tr("Text", "Text stream");
01043     else if (kTrackTypeTextSubtitle == type)
01044         str = QObject::tr("TXT File", "Text File");
01045     return str;
01046 }
01047 
01048 int to_track_type(const QString &str)
01049 {
01050     int ret = -1;
01051 
01052     if (str.left(5) == "AUDIO")
01053         ret = kTrackTypeAudio;
01054     else if (str.left(5) == "VIDEO")
01055         ret = kTrackTypeVideo;
01056     else if (str.left(8) == "SUBTITLE")
01057         ret = kTrackTypeSubtitle;
01058     else if (str.left(5) == "CC608")
01059         ret = kTrackTypeCC608;
01060     else if (str.left(5) == "CC708")
01061         ret = kTrackTypeCC708;
01062     else if (str.left(3) == "TTC")
01063         ret = kTrackTypeTeletextCaptions;
01064     else if (str.left(3) == "TTM")
01065         ret = kTrackTypeTeletextMenu;
01066     else if (str.left(3) == "TFL")
01067         ret = kTrackTypeTextSubtitle;
01068     else if (str.left(7) == "RAWTEXT")
01069         ret = kTrackTypeRawText;
01070     return ret;
01071 }
01072 
01073 void DecoderBase::SaveTotalDuration(void)
01074 {
01075     if (!m_playbackinfo || !totalDuration)
01076         return;
01077 
01078     m_playbackinfo->SaveTotalDuration(totalDuration);
01079 }
01080 
01081 void DecoderBase::SaveTotalFrames(void)
01082 {
01083     if (!m_playbackinfo || !framesRead)
01084         return;
01085 
01086     m_playbackinfo->SaveTotalFrames(framesRead);
01087 }
01088 
01089 
01090 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends