|
MythTV
0.26-pre
|
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: */
1.7.6.1