• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Classes
  • Files

NuppelVideoPlayer.cpp

Go to the documentation of this file.
00001 // -*- Mode: c++ -*-
00002 
00003 // Std C headers
00004 #include <cstdio>
00005 #include <cstdlib>
00006 #include <cassert>
00007 #include <cerrno>
00008 #include <ctime>
00009 #include <cmath>
00010 
00011 // POSIX headers
00012 #include <unistd.h>
00013 #include <fcntl.h>
00014 #include <sched.h>
00015 #include <sys/time.h>
00016 
00017 // C++ headers
00018 #include <algorithm>
00019 #include <iostream>
00020 using namespace std;
00021 
00022 // Qt headers
00023 #include <qapplication.h>
00024 #include <qstringlist.h>
00025 #include <qdeepcopy.h>
00026 #include <qmap.h>
00027 
00028 // MythTV headers
00029 #include "config.h"
00030 #include "mythdbcon.h"
00031 #include "dialogbox.h"
00032 #include "NuppelVideoPlayer.h"
00033 #include "audiooutput.h"
00034 #include "recordingprofile.h"
00035 #include "osdtypes.h"
00036 #include "osdsurface.h"
00037 #include "osdtypeteletext.h"
00038 #include "remoteutil.h"
00039 #include "programinfo.h"
00040 #include "mythcontext.h"
00041 #include "fifowriter.h"
00042 #include "filtermanager.h"
00043 #include "util.h"
00044 #include "livetvchain.h"
00045 #include "decoderbase.h"
00046 #include "nuppeldecoder.h"
00047 #include "avformatdecoder.h"
00048 #include "dummydecoder.h"
00049 #include "jobqueue.h"
00050 #include "DVDRingBuffer.h"
00051 #include "NuppelVideoRecorder.h"
00052 #include "tv_play.h"
00053 #include "interactivetv.h"
00054 #include "util-osx-cocoa.h"
00055 
00056 extern "C" {
00057 #include "vbitext/vbi.h"
00058 #include "vsync.h"
00059 }
00060 
00061 #include "remoteencoder.h"
00062 
00063 #include "videoout_null.h"
00064 
00065 #ifdef USING_IVTV
00066 #include "videoout_ivtv.h"
00067 #include "ivtvdecoder.h"
00068 #endif
00069 
00070 #ifdef USING_DIRECTX
00071 #include "videoout_dx.h"
00072 #undef GetFreeSpace
00073 #undef GetFileSize
00074 #ifdef CONFIG_CYGWIN
00075 #undef DialogBox
00076 #endif
00077 #endif
00078 
00079 #ifndef HAVE_ROUND
00080 #define round(x) ((int) ((x) + 0.5))
00081 #endif
00082 
00083 #ifdef CONFIG_DARWIN
00084 extern "C" {
00085 int isnan(double);
00086 }
00087 #endif
00088 
00089 #define FAST_RESTART 0
00090 #define LOC QString("NVP: ")
00091 #define LOC_ERR QString("NVP, Error: ")
00092 
00093 QString track_type_to_string(uint type)
00094 {
00095     QString str = QObject::tr("Track");
00096 
00097     if (kTrackTypeAudio == type)
00098         str = QObject::tr("Audio track");
00099     else if (kTrackTypeSubtitle == type)
00100         str = QObject::tr("Subtitle track");
00101     else if (kTrackTypeCC608 == type)
00102         str = QObject::tr("CC", "EIA-608 closed captions");
00103     else if (kTrackTypeCC708 == type)
00104         str = QObject::tr("ATSC CC", "EIA-708 closed captions");
00105     else if (kTrackTypeTeletextCaptions == type)
00106         str = QObject::tr("TT CC", "Teletext closed captions");
00107     else if (kTrackTypeTeletextMenu == type)
00108         str = QObject::tr("TT Menu", "Teletext Menu");
00109     return str;
00110 }
00111 
00112 int type_string_to_track_type(const QString &str)
00113 {
00114     int ret = -1;
00115 
00116     if (str.left(5) == "AUDIO")
00117         ret = kTrackTypeAudio;
00118     else if (str.left(8) == "SUBTITLE")
00119         ret = kTrackTypeSubtitle;
00120     else if (str.left(5) == "CC608")
00121         ret = kTrackTypeCC608;
00122     else if (str.left(5) == "CC708")
00123         ret = kTrackTypeCC708;
00124     else if (str.left(3) == "TTC")
00125         ret = kTrackTypeTeletextCaptions;
00126     else if (str.left(3) == "TTM")
00127         ret = kTrackTypeTeletextMenu;
00128     return ret;
00129 }
00130 
00131 uint track_type_to_display_mode[kTrackTypeCount+2] =
00132 {
00133     kDisplayNone,
00134     kDisplayAVSubtitle,
00135     kDisplayCC608,
00136     kDisplayCC708,
00137     kDisplayTeletextCaptions,
00138     kDisplayNone,
00139     kDisplayNUVTeletextCaptions,
00140 };
00141 
00142 NuppelVideoPlayer::NuppelVideoPlayer(QString inUseID, const ProgramInfo *info)
00143     : decoder(NULL),                decoder_change_lock(true),
00144       videoOutput(NULL),            nvr_enc(NULL), 
00145       m_playbackinfo(NULL),
00146       // Window stuff
00147       parentWidget(NULL), embedid(0), embx(-1), emby(-1), embw(-1), embh(-1),
00148       embed_saved_scan_type((FrameScanType)-2),
00149       embed_saved_scan_lock(false),
00150       // State
00151       eof(false),
00152       m_double_framerate(false),    m_double_process(false),
00153       m_can_double(false),          m_deint_possible(true),
00154       paused(false),
00155       pausevideo(false),            actuallypaused(false),
00156       video_actually_paused(false), playing(false),
00157       decoder_thread_alive(true),   killplayer(false),
00158       killvideo(false),             livetv(false),
00159       watchingrecording(false),     editmode(false),
00160       resetvideo(false),            using_null_videoout(false),
00161       no_audio_in(false),           no_audio_out(false),
00162       transcoding(false),
00163       hasFullPositionMap(false),    limitKeyRepeat(false),
00164       errored(false),
00165       // Bookmark stuff
00166       bookmarkseek(0),              previewFromBookmark(false),
00167       // Seek
00168       fftime(0),                    seekamountpos(4),
00169       seekamount(30),               exactseeks(false),
00170       // Playback misc.
00171       videobuf_retries(0),          framesPlayed(0),
00172       totalFrames(0),               totalLength(0),
00173       rewindtime(0),                m_recusage(inUseID),
00174       // Input Video Attributes
00175       video_disp_dim(0,0), video_dim(0,0),
00176       video_frame_rate(29.97f), video_aspect(4.0f / 3.0f),
00177       forced_video_aspect(-1),
00178       m_scan(kScan_Interlaced),     m_scan_locked(false),
00179       m_scan_tracker(0),
00180       keyframedist(30),
00181       // RingBuffer stuff
00182       filename("output.nuv"), weMadeBuffer(false), ringBuffer(NULL),
00183       // Prebuffering (RingBuffer) control
00184       prebuffering(false), prebuffer_tries(0),
00185 
00186       // General Caption/Teletext/Subtitle support
00187       db_prefer708(true),
00188       textDisplayMode(kDisplayNone),
00189       prevTextDisplayMode(kDisplayNone),
00190       // Support for analog captions and teletext
00191       vbimode(VBIMode::None),       
00192       ttPageNum(0x888),             ccmode(CC_CC1),
00193       wtxt(0), rtxt(0), text_size(0), ccline(""), cccol(0), ccrow(0),
00194       // Support for captions, teletext, etc. decoded by libav
00195       textDesired(false),
00196       osdHasSubtitles(false),       osdSubtitlesExpireAt(-1),
00197 
00198       // MHEG/MHI Interactive TV visible in OSD
00199       itvVisible(false),
00200       interactiveTV(NULL),
00201       itvEnabled(false),
00202 
00203       // OSD stuff
00204       osd(NULL),                    timedisplay(NULL),
00205       dialogname(""),               dialogtype(0),
00206       // Audio stuff
00207       audioOutput(NULL),
00208       audio_main_device(QString::null),
00209       audio_passthru_device(QString::null),
00210       audio_channels(2),            audio_bits(-1),
00211       audio_samplerate(44100),      audio_stretchfactor(1.0f),
00212       audio_codec(NULL),
00213       // Picture-in-Picture
00214       pipplayer(NULL), setpipplayer(NULL), needsetpipplayer(false),
00215       // Preview window support
00216       argb_buf(NULL),               argb_size(0,0),
00217       yuv2argb_conv(yuv2rgb_init_mmx(32, MODE_RGB)),
00218       yuv_need_copy(false),         yuv_desired_size(0,0),
00219       yuv_scaler(NULL),             yuv_frame_scaled(NULL),
00220       yuv_scaler_in_size(0,0),      yuv_scaler_out_size(0,0),
00221       // Filters
00222       videoFiltersForProgram(""),   videoFiltersOverride(""),
00223       postfilt_width(0),            postfilt_height(0),
00224       videoFilters(NULL),           FiltMan(new FilterManager()),
00225       // Commercial filtering
00226       skipcommercials(0),           autocommercialskip(0),
00227       commrewindamount(0),
00228       commnotifyamount(0),          lastCommSkipDirection(0),
00229       lastCommSkipTime(0/*1970*/),  lastCommSkipStart(0),
00230       lastSkipTime(0 /*1970*/),
00231       deleteframe(0),
00232       hasdeletetable(false),        hasblanktable(false),
00233       hascommbreaktable(false),
00234       deleteIter(deleteMap.end()),  blankIter(blankMap.end()),
00235       commBreakIter(commBreakMap.end()),
00236       forcePositionMapSync(false),
00237       // Playback (output) speed control
00238       decoder_lock(true),
00239       next_play_speed(1.0f),        next_normal_speed(true),
00240       play_speed(1.0f),             normal_speed(true),
00241       frame_interval((int)(1000000.0f / 30)), ffrew_skip(1),
00242       // Audio and video synchronization stuff
00243       videosync(NULL),              delay(0),
00244       vsynctol(30/4),               avsync_delay(0),
00245       avsync_adjustment(0),         avsync_avg(0),
00246       avsync_oldavg(0),             refreshrate(0),
00247       lastsync(false),              m_playing_slower(false),
00248       m_stored_audio_stretchfactor(1.0),
00249       audio_paused(false),
00250       // Audio warping stuff
00251       usevideotimebase(false), 
00252       warpfactor(1.0f),             warpfactor_avg(1.0f),
00253       warplbuff(NULL),              warprbuff(NULL),
00254       warpbuffsize(0),
00255       // Time Code stuff
00256       prevtc(0),
00257       tc_avcheck_framecounter(0),   tc_diff_estimate(0),
00258       savedAudioTimecodeOffset(0),
00259       // LiveTVChain stuff
00260       livetvchain(NULL),            m_tv(NULL), 
00261       isDummy(false),               
00262       // DVD stuff
00263       hidedvdbutton(true), need_change_dvd_track(0),
00264       dvd_stillframe_showing(false),
00265       // Debugging variables
00266       output_jmeter(NULL)
00267 {
00268     vbimode = VBIMode::Parse(gContext->GetSetting("VbiFormat"));
00269 
00270     if (info)
00271         SetPlaybackInfo(new ProgramInfo(*info));
00272 
00273     commrewindamount = gContext->GetNumSetting("CommRewindAmount",0);
00274     commnotifyamount = gContext->GetNumSetting("CommNotifyAmount",0);
00275     decode_extra_audio=gContext->GetNumSetting("DecodeExtraAudio", 0);
00276     itvEnabled       = gContext->GetNumSetting("EnableMHEG", 0);
00277     db_prefer708     = gContext->GetNumSetting("Prefer708Captions", 1);
00278 
00279     lastIgnoredManualSkip = QDateTime::currentDateTime().addSecs(-10);
00280 
00281     bzero(&txtbuffers, sizeof(txtbuffers));
00282     bzero(&tc_lastval, sizeof(tc_lastval));
00283     bzero(&tc_wrap,    sizeof(tc_wrap));
00284 
00285     // Get VBI page number
00286     QString mypage = gContext->GetSetting("VBIpageNr", "888");
00287     bool valid = false;
00288     uint tmp = mypage.toInt(&valid, 16);
00289     ttPageNum = (valid) ? tmp : ttPageNum;
00290 
00291     text_size = 8 * (sizeof(teletextsubtitle) + VT_WIDTH);
00292     for (int i = 0; i < MAXTBUFFER; i++)
00293         txtbuffers[i].buffer = new unsigned char[text_size + 1];
00294 }
00295 
00296 NuppelVideoPlayer::~NuppelVideoPlayer(void)
00297 {
00298     if (audioOutput)
00299     {
00300         delete audioOutput;
00301         audioOutput = NULL;
00302     }
00303 
00304     SetPlaybackInfo(NULL);
00305 
00306     if (weMadeBuffer && ringBuffer)
00307     {
00308         delete ringBuffer;
00309         ringBuffer = NULL;
00310     }
00311 
00312     if (osdHasSubtitles || !nonDisplayedAVSubtitles.empty())
00313         ClearSubtitles();
00314 
00315     if (osd)
00316     {
00317         delete osd;
00318         osd = NULL;
00319     }
00320     
00321     for (int i = 0; i < MAXTBUFFER; i++)
00322     {
00323         if (txtbuffers[i].buffer)
00324         {
00325             delete [] txtbuffers[i].buffer;
00326             txtbuffers[i].buffer = NULL;
00327         }
00328     }
00329 
00330     SetDecoder(NULL);
00331 
00332     if (interactiveTV)
00333     {
00334         delete interactiveTV;
00335         interactiveTV = NULL;
00336     }
00337 
00338     if (FiltMan)
00339     {
00340         delete FiltMan;
00341         FiltMan = NULL;
00342     }
00343 
00344     if (videoFilters)
00345     {
00346         delete videoFilters;
00347         videoFilters = NULL;
00348     }
00349 
00350     if (videosync)
00351     {
00352         delete videosync;
00353         videosync = NULL;
00354     }
00355 
00356     if (videoOutput)
00357     {
00358         delete videoOutput;
00359         videoOutput = NULL;
00360     }
00361 
00362     if (argb_buf)
00363     {
00364         delete [] argb_buf;
00365         argb_buf = NULL;
00366     }
00367 
00368     if (output_jmeter)
00369     {
00370         delete output_jmeter;
00371         output_jmeter = NULL;
00372     }
00373 
00374     ShutdownYUVResize();
00375 }
00376 
00377 void NuppelVideoPlayer::SetWatchingRecording(bool mode)
00378 {
00379     QMutexLocker locker(&decoder_change_lock);
00380 
00381     watchingrecording = mode;
00382     if (GetDecoder())
00383         GetDecoder()->setWatchingRecording(mode);
00384 }
00385 
00386 void NuppelVideoPlayer::SetRecorder(RemoteEncoder *recorder)
00387 {
00388     nvr_enc = recorder;
00389     if (GetDecoder())
00390         GetDecoder()->setRecorder(recorder);
00391 }
00392 
00393 void NuppelVideoPlayer::SetAudioInfo(const QString &main_device,
00394                                      const QString &passthru_device,
00395                                      uint           samplerate)
00396 {
00397     audio_main_device = audio_passthru_device = QString::null;
00398 
00399     if (!main_device.isEmpty())
00400         audio_main_device     = QDeepCopy<QString>(main_device);
00401 
00402     if (!passthru_device.isEmpty())
00403         audio_passthru_device = QDeepCopy<QString>(passthru_device);
00404 
00405     audio_samplerate      = (int)samplerate;
00406 }
00407 
00408 void NuppelVideoPlayer::PauseDecoder(void)
00409 {
00410     decoder_lock.lock();
00411     next_play_speed = 0.0;
00412     next_normal_speed = false;
00413     decoder_lock.unlock();
00414 
00415     if (!actuallypaused)
00416     {
00417         while (!decoderThreadPaused.wait(4000))
00418         {
00419             if (eof)
00420                 return;
00421             VERBOSE(VB_IMPORTANT, "Waited too long for decoder to pause");
00422         }
00423     }
00424 }
00425 
00426 void NuppelVideoPlayer::Pause(bool waitvideo)
00427 {
00428     PauseDecoder();
00429 
00430     //cout << "stopping other threads" << endl;
00431     internalPauseLock.lock();
00432     PauseVideo(waitvideo);
00433     internalPauseLock.unlock();
00434 
00435     if (audioOutput)
00436     {
00437         audio_paused = true;
00438         audioOutput->Pause(true);
00439     }
00440     if (ringBuffer)
00441         ringBuffer->Pause();
00442 
00443     QMutexLocker locker(&decoder_change_lock);
00444 
00445     if (GetDecoder() && videoOutput)
00446     {
00447         //cout << "updating frames played" << endl;
00448         if (using_null_videoout || IsIVTVDecoder())
00449             GetDecoder()->UpdateFramesPlayed();
00450         else
00451             framesPlayed = videoOutput->GetFramesPlayed();
00452     }
00453 }
00454 
00455 bool NuppelVideoPlayer::Play(float speed, bool normal, bool unpauseaudio)
00456 {
00457     VERBOSE(VB_PLAYBACK, LOC +
00458             QString("Play(%1, normal %2, unpause audio %3)")
00459             .arg(speed,5,'f',1).arg(normal).arg(unpauseaudio));
00460 
00461     internalPauseLock.lock();
00462     if (editmode)
00463     {
00464         internalPauseLock.unlock();
00465         VERBOSE(VB_IMPORTANT, LOC + "Ignoring Play(), in edit mode.");
00466         return false;
00467     }
00468     UnpauseVideo();
00469     internalPauseLock.unlock();
00470 
00471     if (audioOutput && unpauseaudio)
00472         audio_paused = false;
00473     if (ringBuffer)
00474         ringBuffer->Unpause();
00475 
00476     decoder_lock.lock();
00477     next_play_speed = speed;
00478     next_normal_speed = normal;
00479     decoder_lock.unlock();
00480     return true;
00481 }
00482 
00483 bool NuppelVideoPlayer::IsPaused(bool *is_pause_still_possible) const
00484 {
00485     bool rbf_playing = (ringBuffer != NULL) && !ringBuffer->isPaused();
00486     bool aud_playing = (audioOutput != NULL) && !audioOutput->GetPause();
00487     if (is_pause_still_possible)
00488     {
00489         bool decoder_pausing = (0.0f == next_play_speed) && !next_normal_speed;
00490         bool video_pausing = pausevideo;
00491         bool rbuf_paused = !rbf_playing;
00492         *is_pause_still_possible = 
00493             decoder_pausing && video_pausing && rbuf_paused;
00494     }
00495 
00496     return (actuallypaused && !rbf_playing && !aud_playing &&
00497             IsVideoActuallyPaused());
00498 }
00499 
00500 void NuppelVideoPlayer::PauseVideo(bool wait)
00501 {
00502     QMutexLocker locker(&pauseUnpauseLock);
00503     video_actually_paused = false;
00504     pausevideo = true;
00505 
00506     for (uint i = 0; wait && !video_actually_paused; i++)
00507     {
00508         videoThreadPaused.wait(&pauseUnpauseLock, 250);
00509 
00510         if (video_actually_paused || eof)
00511             break;
00512 
00513         if ((i % 10) == 9)
00514             VERBOSE(VB_IMPORTANT, "Waited too long for video out to pause");
00515     }
00516 }
00517 
00518 void NuppelVideoPlayer::UnpauseVideo(bool wait)
00519 {
00520     QMutexLocker locker(&pauseUnpauseLock);
00521     pausevideo = false;
00522 
00523     for (uint i = 0; wait && video_actually_paused; i++)
00524     {
00525         videoThreadUnpaused.wait(&pauseUnpauseLock, 250);
00526 
00527         if (!video_actually_paused || eof)
00528             break;
00529 
00530         if ((i % 10) == 9)
00531             VERBOSE(VB_IMPORTANT, "Waited too long for video out to unpause");
00532     }
00533 }
00534 
00535 void NuppelVideoPlayer::SetVideoActuallyPaused(bool val)
00536 {
00537     QMutexLocker locker(&pauseUnpauseLock);
00538     video_actually_paused = val;
00539 
00540     if (val)
00541         videoThreadPaused.wakeAll();
00542     else
00543         videoThreadUnpaused.wakeAll();
00544 }
00545 
00546 bool NuppelVideoPlayer::IsVideoActuallyPaused(void) const
00547 {
00548     QMutexLocker locker(&pauseUnpauseLock);
00549     return video_actually_paused;
00550 }
00551 
00552 void NuppelVideoPlayer::SetPlaybackInfo(ProgramInfo *pginfo)
00553 {
00554     if (m_playbackinfo)
00555     {
00556         m_playbackinfo->MarkAsInUse(false);
00557         delete m_playbackinfo;
00558         videoFiltersForProgram = QString::null;
00559     }
00560 
00561     m_playbackinfo = pginfo;
00562 
00563     if (m_playbackinfo)
00564     {
00565         m_playbackinfo->MarkAsInUse(true, m_recusage);
00566         videoFiltersForProgram =
00567             QDeepCopy<QString>(m_playbackinfo->chanOutputFilters);
00568     }
00569 }
00570 
00571 void NuppelVideoPlayer::SetPrebuffering(bool prebuffer)
00572 {
00573     prebuffering_lock.lock();
00574 
00575     if (prebuffer != prebuffering)
00576     {
00577         prebuffering = prebuffer;
00578         if (audioOutput && !paused)
00579         {
00580             if (prebuffering)
00581                 audioOutput->Pause(prebuffering);
00582             audio_paused = prebuffering;
00583         }
00584     }
00585 
00586     if (!prebuffering)
00587         prebuffering_wait.wakeAll();
00588 
00589     prebuffering_lock.unlock();
00590 }
00591 
00592 bool NuppelVideoPlayer::InitVideo(void)
00593 {
00594     if (using_null_videoout)
00595     {
00596         videoOutput = new VideoOutputNull();
00597         if (!videoOutput->Init(video_disp_dim.width(), video_disp_dim.height(),
00598                                video_aspect, 0, 0, 0, 0, 0, 0))
00599         {
00600             errored = true;
00601             return false;
00602         }
00603     }
00604     else
00605     {
00606         QWidget *widget = parentWidget;
00607 
00608         if (!widget)
00609         {
00610             MythMainWindow *window = gContext->GetMainWindow();
00611             assert(window);
00612 
00613             QObject *playbackwin = window->child("video playback window");
00614 
00615             QWidget *widget = (QWidget *)playbackwin;
00616 
00617             if (!widget)
00618             {
00619                 VERBOSE(VB_IMPORTANT, "Couldn't find 'tv playback' widget");
00620                 widget = window->currentWidget();
00621                 assert(widget);
00622             }
00623         }
00624 
00625         if (!widget)
00626         {
00627             errored = true;
00628             return false;
00629         }
00630 
00631         const QRect display_rect(0, 0, widget->width(), widget->height());
00632 
00633         videoOutput = VideoOutput::Create(
00634             GetDecoder()->GetCodecDecoderName(),
00635             GetDecoder()->GetVideoCodecID(),
00636             GetDecoder()->GetVideoCodecPrivate(),
00637             video_disp_dim, video_aspect,
00638             widget->winId(), display_rect, 0 /*embedid*/);
00639 
00640         if (!videoOutput)
00641         {
00642             errored = true;
00643             return false;
00644         }
00645 
00646         bool db_scale = true;
00647         if (ringBuffer->isDVD())
00648             db_scale = false;
00649 
00650         videoOutput->SetVideoScalingAllowed(db_scale);
00651 
00652         // We need to tell it this for automatic deinterlacer settings
00653         videoOutput->SetVideoFrameRate(video_frame_rate * play_speed);
00654 
00655         if (videoOutput->hasMCAcceleration() && !decode_extra_audio)
00656         {
00657             VERBOSE(VB_IMPORTANT, LOC +
00658                     "Forcing decode extra audio option on. "
00659                     "\n\t\t\tXvMC playback requires it.");
00660             decode_extra_audio = true;
00661             if (GetDecoder())
00662                 GetDecoder()->SetLowBuffers(decode_extra_audio);
00663         }
00664     }
00665 
00666     if (embedid > 0)
00667     {
00668         videoOutput->EmbedInWidget(embedid, embx, emby, embw, embh);
00669     }
00670 
00671     SetCaptionsEnabled(gContext->GetNumSetting("DefaultCCMode"), false);
00672 
00673     InitFilters();
00674 
00675     return true;
00676 }
00677 
00678 void NuppelVideoPlayer::ReinitOSD(void)
00679 {
00680     if (videoOutput && !using_null_videoout)
00681     {
00682         QRect visible, total;
00683         float aspect, scaling;
00684         if (osd)
00685         {
00686             videoOutput->GetOSDBounds(total, visible, aspect, scaling, osd->GetThemeAspect());
00687             osd->Reinit(total, frame_interval, visible, aspect, scaling);
00688         }
00689 
00690         if (GetInteractiveTV())
00691         {
00692             GetInteractiveTV()->Reinit(total);
00693             itvVisible = false;
00694         }
00695     }
00696 }
00697 
00698 void NuppelVideoPlayer::ReinitVideo(void)
00699 {
00700     vidExitLock.lock();
00701     videofiltersLock.lock();
00702 
00703     float aspect = (forced_video_aspect > 0) ? forced_video_aspect : video_aspect;
00704 
00705     videoOutput->InputChanged(video_disp_dim, aspect, 
00706                               GetDecoder()->GetVideoCodecID(),
00707                               GetDecoder()->GetVideoCodecPrivate());
00708 
00709     // We need to tell it this for automatic deinterlacer settings
00710     videoOutput->SetVideoFrameRate(video_frame_rate * play_speed);
00711 
00712     if (videoOutput->IsErrored())
00713     {
00714         VERBOSE(VB_IMPORTANT, "ReinitVideo(): videoOutput->IsErrored()");
00715         if (!using_null_videoout)
00716         {
00717             qApp->lock();
00718             DialogBox *dlg = new DialogBox(
00719                 gContext->GetMainWindow(),
00720                 QObject::tr("Failed to Reinit Video."));
00721 
00722             dlg->AddButton(QObject::tr("Return to menu."));
00723             dlg->exec();
00724             dlg->deleteLater();
00725             qApp->unlock();
00726         }
00727         errored = true;
00728     }
00729     else
00730     {
00731         ReinitOSD();
00732     }
00733 
00734     videofiltersLock.unlock();
00735     vidExitLock.unlock();
00736 
00737     ClearAfterSeek();
00738 
00739     if (textDisplayMode)
00740     {
00741         DisableCaptions(textDisplayMode, false);
00742         SetCaptionsEnabled(true, false);
00743     }
00744 
00745     InitFilters();
00746 
00747     if (ringBuffer->InDVDMenuOrStillFrame())
00748         ringBuffer->DVD()->SetRunSeekCellStart(true);
00749 }
00750 
00751 QString NuppelVideoPlayer::ReinitAudio(void)
00752 {
00753     QString errMsg = QString::null;
00754 
00755     if ((audio_bits <= 0) || (audio_channels <= 0) || (audio_samplerate <= 0))
00756     {
00757         VERBOSE(VB_IMPORTANT, LOC +
00758                 QString("Disabling Audio, params(%1,%2,%3)")
00759                 .arg(audio_bits).arg(audio_channels).arg(audio_samplerate));
00760 
00761         no_audio_in = no_audio_out = true;
00762         if (audioOutput)
00763         {
00764             delete audioOutput;
00765             audioOutput = NULL;
00766         }
00767         return errMsg;
00768     }
00769 
00770     no_audio_in = false;
00771 
00772     if (!audioOutput && !using_null_videoout)
00773     {
00774         bool setVolume = gContext->GetNumSetting("MythControlsVolume", 1);
00775         audioOutput = AudioOutput::OpenAudio(audio_main_device,
00776                                              audio_passthru_device,
00777                                              audio_bits, audio_channels,
00778                                              audio_samplerate,
00779                                              AUDIOOUTPUT_VIDEO,
00780                                              setVolume, audio_passthru);
00781         if (!audioOutput)
00782             errMsg = QObject::tr("Unable to create AudioOutput.");
00783         else
00784             errMsg = audioOutput->GetError();
00785 
00786         if (!errMsg.isEmpty())
00787         {
00788             VERBOSE(VB_IMPORTANT, LOC + "Disabling Audio" +
00789                     QString(", reason is: %1").arg(errMsg));
00790             if (audioOutput)
00791             {
00792                 delete audioOutput;
00793                 audioOutput = NULL;
00794             }
00795             no_audio_out = true;
00796         }
00797         else if (no_audio_out)
00798         {
00799             VERBOSE(VB_IMPORTANT, LOC + "Enabling Audio");
00800             no_audio_out = false;
00801         }
00802     }
00803 
00804     if (audioOutput)
00805     {
00806         audioOutput->Reconfigure(audio_bits, audio_channels,
00807                                  audio_samplerate, audio_passthru,
00808                                  audio_codec);
00809         errMsg = audioOutput->GetError();
00810         if (!errMsg.isEmpty())
00811             audioOutput->SetStretchFactor(audio_stretchfactor);
00812     }
00813 
00814     return errMsg;
00815 }
00816 
00817 static inline QString toQString(FrameScanType scan) {
00818     switch (scan) {
00819         case kScan_Ignore: return QString("Ignore Scan");
00820         case kScan_Detect: return QString("Detect Scan");
00821         case kScan_Interlaced:  return QString("Interlaced Scan");
00822         case kScan_Progressive: return QString("Progressive Scan");
00823         default: return QString("Unknown Scan");
00824     }
00825 }
00826 
00827 FrameScanType NuppelVideoPlayer::detectInterlace(FrameScanType newScan,
00828                                                  FrameScanType scan,
00829                                                  float fps, int video_height)
00830 {
00831     QString dbg = QString("detectInterlace(") + toQString(newScan) +
00832         QString(", ") + toQString(scan) + QString(", ") + QString("%1").arg(fps) +
00833         QString(", ") + QString("%1").arg(video_height) + QString(") ->");
00834 
00835     if (kScan_Ignore != newScan || kScan_Detect == scan)
00836     {
00837         // The scanning mode should be decoded from the stream, but if it
00838         // isn't, we have to guess.
00839 
00840         scan = kScan_Interlaced; // default to interlaced
00841         if (720 == video_height) // ATSC 720p
00842             scan = kScan_Progressive;
00843         else if (fps > 45) // software deinterlacing
00844             scan = kScan_Progressive;
00845 
00846         if (kScan_Detect != newScan)
00847             scan = newScan;
00848     };
00849 
00850     VERBOSE(VB_PLAYBACK, dbg+toQString(scan));
00851 
00852     return scan;
00853 }
00854 
00855 void NuppelVideoPlayer::SetKeyframeDistance(int keyframedistance)
00856 {
00857     keyframedist = (keyframedistance > 0) ? keyframedistance : keyframedist;
00858 }
00859 
00863 void NuppelVideoPlayer::FallbackDeint(void)
00864 {
00865      m_double_framerate = false;
00866      m_double_process   = false;
00867 
00868      if (videosync)
00869          videosync->SetFrameInterval(frame_interval, false);
00870 
00871      if (osd && !IsIVTVDecoder())
00872          osd->SetFrameInterval(frame_interval);
00873 
00874      if (videoOutput)
00875          videoOutput->FallbackDeint();
00876 }
00877 
00878 void NuppelVideoPlayer::AutoDeint(VideoFrame *frame)
00879 {
00880     if (!frame || m_scan_locked)
00881         return;
00882 
00883     if (frame->interlaced_frame) 
00884     {
00885         if (m_scan_tracker < 0) 
00886         {
00887             VERBOSE(VB_PLAYBACK, LOC + "interlaced frame seen after "
00888                     << abs(m_scan_tracker) << " progressive frames");
00889             m_scan_tracker = 2;
00890         }
00891         m_scan_tracker++;
00892     } 
00893     else 
00894     {
00895         if (m_scan_tracker > 0) 
00896         {
00897             VERBOSE(VB_PLAYBACK, LOC + "progressive frame seen after "
00898                     << m_scan_tracker << " interlaced  frames");
00899             m_scan_tracker = 0;
00900         }
00901         m_scan_tracker--;
00902     }
00903         
00904     if ((m_scan_tracker % 400) == 0) 
00905     {
00906         QString type = (m_scan_tracker < 0) ? "progressive" : "interlaced";
00907         VERBOSE(VB_PLAYBACK, LOC + QString("%1 %2 frames seen.")
00908                 .arg(abs(m_scan_tracker)).arg(type));
00909     }
00910 
00911     int min_count = (ringBuffer->isDVD()) ? 0 : 2;
00912     if (abs(m_scan_tracker) <= min_count)
00913         return;
00914 
00915     SetScanType((m_scan_tracker > min_count) ? kScan_Interlaced : kScan_Progressive);
00916     m_scan_locked  = false;
00917 }
00918 
00919 void NuppelVideoPlayer::SetScanType(FrameScanType scan)
00920 {
00921     QMutexLocker locker(&videofiltersLock);
00922 
00923     if (!videoOutput || !videosync)
00924         return; // hopefully this will be called again later...
00925 
00926     m_scan_locked = (scan != kScan_Detect);
00927 
00928     if (scan == m_scan)
00929         return;
00930 
00931     bool interlaced = is_interlaced(scan);
00932     if (interlaced && !m_deint_possible)
00933     {
00934         m_scan = scan;
00935         return;
00936     }
00937 
00938     m_double_process = videoOutput->IsExtraProcessingRequired();
00939 
00940     if (interlaced || m_double_process)
00941     {
00942         m_deint_possible = videoOutput->SetDeinterlacingEnabled(true);
00943         if (!m_deint_possible)
00944         {
00945             VERBOSE(VB_IMPORTANT, "Failed to enable deinterlacing");
00946             m_scan = scan;
00947             return;
00948         }
00949         if (videoOutput->NeedsDoubleFramerate())
00950         {
00951             m_double_framerate = true;
00952             videosync->SetFrameInterval(frame_interval, true);
00953             // Make sure video sync can double frame rate
00954             m_can_double = videosync->UsesFieldInterval();
00955             if (!m_can_double)
00956             {
00957                 VERBOSE(VB_IMPORTANT, "Video sync method can't support double "
00958                         "framerate (refresh rate too low for bob deint)");
00959                 FallbackDeint();
00960             }
00961         }
00962         VERBOSE(VB_PLAYBACK, "Enabled deinterlacing");
00963     }
00964     else //progressive but !double_process
00965     {
00966         if (kScan_Progressive == scan)
00967         {
00968             if (m_double_framerate) 
00969             {
00970                 m_double_framerate = false;
00971                 videosync->SetFrameInterval(frame_interval, false);
00972             }
00973             videoOutput->SetDeinterlacingEnabled(false);
00974             VERBOSE(VB_PLAYBACK, "Disabled deinterlacing");
00975         }
00976     }
00977 
00978     // double_process can switch on/off
00979     if (osd && !IsIVTVDecoder())
00980     {
00981         osd->SetFrameInterval(
00982             (m_double_framerate && m_double_process) ?
00983             (frame_interval>>1) : frame_interval);
00984     }
00985 
00986     m_scan = scan;
00987 }
00988 
00989 void NuppelVideoPlayer::SetVideoParams(int width, int height, double fps,
00990                                        int keyframedistance, float aspect,
00991                                        FrameScanType scan, bool video_codec_changed)
00992 {
00993     if (width == 0 || height == 0 || isnan(aspect) || isnan(fps))
00994         return;
00995 
00996     if ((video_disp_dim == QSize(width, height)) &&
00997         (video_aspect == aspect) && (video_frame_rate == fps   ) &&
00998         ((keyframedistance <= 0) || (keyframedistance == keyframedist)) &&
00999         !video_codec_changed)
01000     {
01001         return;
01002     }
01003 
01004     if ((width > 0) && (height > 0))
01005     {
01006         video_dim      = QSize((width + 15) & ~0xf, (height + 15) & ~0xf);
01007         video_disp_dim = QSize(width, height);
01008     }
01009     video_aspect = (aspect > 0.0f) ? aspect : video_aspect;
01010     keyframedist = (keyframedistance > 0) ? keyframedistance : keyframedist;
01011 
01012     if (fps > 0.0f && fps < 121.0f)
01013     {
01014         video_frame_rate = fps;
01015         float temp_speed = (play_speed == 0.0f) ?
01016             audio_stretchfactor : play_speed;
01017         frame_interval = (int)(1000000.0f / video_frame_rate / temp_speed);
01018     }
01019 
01020     if (videoOutput)
01021         ReinitVideo();
01022 
01023     if (IsErrored())
01024         return;
01025 
01026     SetScanType(detectInterlace(scan, m_scan, video_frame_rate,
01027                                 video_disp_dim.height()));
01028     m_scan_locked  = false;
01029     m_scan_tracker = (m_scan == kScan_Interlaced) ? 2 : 0;
01030 }
01031 
01032 void NuppelVideoPlayer::SetFileLength(int total, int frames)
01033 {
01034     totalLength = total;
01035     totalFrames = frames;
01036 }
01037 
01038 void NuppelVideoPlayer::OpenDummy(void)
01039 {
01040     isDummy = true;
01041 
01042     if (!videoOutput)
01043     {
01044         SetVideoParams(720, 576, 25.00, 15);
01045     }
01046 
01047     SetDecoder(new DummyDecoder(this, m_playbackinfo));
01048 }
01049 
01050 int NuppelVideoPlayer::OpenFile(bool skipDsp, uint retries,
01051                                 bool allow_libmpeg2)
01052 {
01053     isDummy = false;
01054 
01055     if (livetvchain)
01056     {
01057         if (livetvchain->GetCardType(livetvchain->GetCurPos()) == "DUMMY")
01058         {
01059             OpenDummy();
01060             return 0;
01061         }
01062     }
01063 
01064     if (!skipDsp)
01065     {
01066         if (!ringBuffer)
01067         {
01068             QString msg("");
01069 
01070             if (m_playbackinfo)
01071             {
01072                 msg = QString("\n\t\t\tm_playbackinfo filename is %1")
01073                     .arg(m_playbackinfo->GetRecordBasename());
01074             }
01075 
01076             VERBOSE(VB_IMPORTANT, LOC + "OpenFile() Warning, "
01077                     "old player exited before new ring buffer created. " + 
01078                     QString("\n\t\t\tRingBuffer will use filename '%1'.")
01079                     .arg(filename) + msg);
01080 
01081             ringBuffer = new RingBuffer(filename, false, true, retries);
01082             weMadeBuffer = true;
01083             livetv = false;
01084         }
01085         else
01086             livetv = ringBuffer->LiveMode();
01087 
01088         if (!ringBuffer->IsOpen())
01089         {
01090             VERBOSE(VB_IMPORTANT,
01091                     QString("NVP::OpenFile(): Error, file not found: %1")
01092                     .arg(ringBuffer->GetFilename().ascii()));
01093             return -1;
01094         }
01095     }
01096 
01097     if (!ringBuffer)
01098         return -1;
01099 
01100     ringBuffer->Start();
01101     char testbuf[kDecoderProbeBufferSize];
01102     ringBuffer->Unpause(); // so we can read testbuf if we were paused
01103 
01104     int readsize = 2048;
01105     if (ringBuffer->Peek(testbuf, readsize) != readsize)
01106     {
01107         VERBOSE(VB_IMPORTANT,
01108                 QString("NVP::OpenFile(): Error, couldn't read file: %1")
01109                 .arg(ringBuffer->GetFilename()));
01110         return -1;
01111     }
01112 
01113     // delete any pre-existing recorder
01114     SetDecoder(NULL);
01115 
01116     if (NuppelDecoder::CanHandle(testbuf, readsize))
01117         SetDecoder(new NuppelDecoder(this, m_playbackinfo));
01118 #ifdef USING_IVTV
01119     else if (!using_null_videoout && IvtvDecoder::CanHandle(
01120                  testbuf, ringBuffer->GetFilename(), readsize))
01121     {
01122         SetDecoder(new IvtvDecoder(this, m_playbackinfo));
01123         no_audio_out = true; // no audio with ivtv.
01124         audio_bits = 16;
01125         audio_samplerate = 44100;
01126         audio_channels = 2;
01127     }
01128     else if (IsIVTVDecoder())
01129     {
01130         VERBOSE(VB_IMPORTANT,
01131                 QString("NVP: Couldn't open '%1' with ivtv decoder")
01132                 .arg(ringBuffer->GetFilename()));
01133         return -1;
01134     }
01135 #endif
01136     else if (AvFormatDecoder::CanHandle(testbuf, ringBuffer->GetFilename(),
01137                                         readsize))
01138         SetDecoder(new AvFormatDecoder(this, m_playbackinfo,
01139                                        using_null_videoout, allow_libmpeg2));
01140 
01141     if (!GetDecoder())
01142     {
01143         VERBOSE(VB_IMPORTANT, 
01144                 QString("NVP: Couldn't find a matching decoder for: %1").
01145                 arg(ringBuffer->GetFilename()));
01146         return -1;
01147     } 
01148     else if (GetDecoder()->IsErrored())
01149     {
01150         VERBOSE(VB_IMPORTANT, 
01151                 "NVP: NuppelDecoder encountered error during creation.");
01152         SetDecoder(NULL);
01153         return -1;
01154     }
01155 
01156     GetDecoder()->setExactSeeks(exactseeks);
01157     GetDecoder()->setLiveTVMode(livetv);
01158     GetDecoder()->setRecorder(nvr_enc);
01159     GetDecoder()->setWatchingRecording(watchingrecording);
01160     GetDecoder()->setTranscoding(transcoding);
01161     GetDecoder()->SetLowBuffers(decode_extra_audio && !using_null_videoout);
01162 
01163     eof = false;
01164 
01165     // Set 'no_video_decode' to true for audio only decodeing
01166     bool no_video_decode = false;
01167 
01168     // We want to locate decoder for video even if using_null_videoout
01169     // is true, only disable if no_video_decode is true.
01170     int ret = GetDecoder()->OpenFile(ringBuffer, no_video_decode, testbuf,
01171                                      readsize);
01172 
01173     if (ret < 0)
01174     {
01175         VERBOSE(VB_IMPORTANT, QString("Couldn't open decoder for: %1")
01176                 .arg(ringBuffer->GetFilename()));
01177         return -1;
01178     }
01179 
01180     if (audio_bits == -1)
01181         no_audio_in = no_audio_out = true;
01182 
01183     if (ret > 0)
01184     {
01185         hasFullPositionMap = true;
01186 
01187         LoadCutList();
01188 
01189         if (!deleteMap.isEmpty())
01190         {
01191             hasdeletetable = true;
01192             deleteIter = deleteMap.begin();
01193         }
01194     }
01195   
01196 
01197     if (ringBuffer->isDVD())
01198         ringBuffer->DVD()->JumpToTitle(true);
01199 
01200     bookmarkseek = GetBookmark();
01201 
01202     return IsErrored() ? -1 : 0;
01203 }
01204 
01205 void NuppelVideoPlayer::InitFilters(void)
01206 {
01207     QString filters = "";
01208     if (videoOutput)
01209         filters = videoOutput->GetFilters();
01210 
01211 #if 0
01212     VERBOSE(VB_PLAYBACK, LOC +
01213             QString("InitFilters() vo '%1' prog '%2' over '%3'")
01214             .arg(filters).arg(videoFiltersForProgram)
01215             .arg(videoFiltersOverride));
01216 #endif
01217 
01218     if (!videoFiltersForProgram.isEmpty())
01219     {
01220         if (videoFiltersForProgram[0] != '+')
01221         {
01222             filters = QDeepCopy<QString>(videoFiltersForProgram);
01223         }
01224         else
01225         {
01226             if ((filters.length() > 1) && (filters.right(1) != ","))
01227                 filters += ",";
01228             filters += QDeepCopy<QString>(videoFiltersForProgram.mid(1));
01229         }
01230     }
01231 
01232     if (!videoFiltersOverride.isEmpty())
01233         filters = QDeepCopy<QString>(videoFiltersOverride);
01234 
01235     videofiltersLock.lock();
01236 
01237     if (videoFilters)
01238     {
01239         delete videoFilters;
01240         videoFilters = NULL;
01241     }
01242 
01243     if (!filters.isEmpty())
01244     {
01245         VideoFrameType itmp = FMT_YV12;
01246         VideoFrameType otmp = FMT_YV12;
01247         int btmp;
01248         postfilt_width = video_dim.width();
01249         postfilt_height = video_dim.height();
01250 
01251         videoFilters = FiltMan->LoadFilters(
01252             filters, itmp, otmp, postfilt_width, postfilt_height, btmp);
01253     }
01254 
01255     videofiltersLock.unlock();
01256 
01257     VERBOSE(VB_PLAYBACK, LOC + QString("LoadFilters('%1'..) -> ")
01258             .arg(filters)<<videoFilters);
01259 }
01260 
01261 int NuppelVideoPlayer::tbuffer_numvalid(void)
01262 {
01263     /* thread safe, returns number of valid slots in the text buffer */
01264     int ret;
01265     text_buflock.lock();
01266 
01267     if (wtxt >= rtxt)
01268         ret = wtxt - rtxt;
01269     else
01270         ret = MAXTBUFFER - (rtxt - wtxt);
01271 
01272     text_buflock.unlock();
01273     return ret;
01274 }
01275 
01276 int NuppelVideoPlayer::tbuffer_numfree(void)
01277 {
01278     return MAXTBUFFER - tbuffer_numvalid() - 1;
01279     /* There's one wasted slot, because the case when rtxt = wtxt is
01280        interpreted as an empty buffer. So the fullest the buffer can be is
01281        MAXTBUFFER - 1. */
01282 }
01283 
01297 VideoFrame *NuppelVideoPlayer::GetNextVideoFrame(bool allow_unsafe)
01298 {
01299     return videoOutput->GetNextFreeFrame(false, allow_unsafe);
01300 }
01301 
01305 void NuppelVideoPlayer::ReleaseNextVideoFrame(VideoFrame *buffer,
01306                                               long long timecode)
01307 {
01308     if (!ringBuffer->InDVDMenuOrStillFrame())
01309         WrapTimecode(timecode, TC_VIDEO);
01310     buffer->timecode = timecode;
01311 
01312     videoOutput->ReleaseFrame(buffer);
01313 }
01314 
01318 void NuppelVideoPlayer::DiscardVideoFrame(VideoFrame *buffer)
01319 {
01320     if (videoOutput)
01321         videoOutput->DiscardFrame(buffer);
01322 }
01323 
01335 void NuppelVideoPlayer::DiscardVideoFrames(bool next_frame_keyframe)
01336 {
01337     if (videoOutput)
01338         videoOutput->DiscardFrames(next_frame_keyframe);
01339 }
01340 
01341 void NuppelVideoPlayer::DrawSlice(VideoFrame *frame, int x, int y, int w, int h)
01342 {
01343     videoOutput->DrawSlice(frame, x, y, w, h);
01344 }
01345 
01346 void NuppelVideoPlayer::AddTextData(unsigned char *buffer, int len,
01347                                     long long timecode, char type)
01348 {
01349     WrapTimecode(timecode, TC_CC);
01350 
01351     if (!(textDisplayMode & kDisplayNUVCaptions))
01352         return;
01353 
01354     if (!tbuffer_numfree())
01355     {
01356         VERBOSE(VB_IMPORTANT, "NVP::AddTextData(): Text buffer overflow");
01357         return;
01358     }
01359 
01360     if (len > text_size)
01361         len = text_size;
01362 
01363     txtbuffers[wtxt].timecode = timecode;
01364     txtbuffers[wtxt].type = type;
01365     txtbuffers[wtxt].len = len;
01366     memset(txtbuffers[wtxt].buffer, 0, text_size);
01367     memcpy(txtbuffers[wtxt].buffer, buffer, len);
01368 
01369     text_buflock.lock();
01370     wtxt = (wtxt+1) % MAXTBUFFER;
01371     text_buflock.unlock();
01372 }
01373 
01374 void NuppelVideoPlayer::CheckPrebuffering(void)
01375 {
01376     if (IsIVTVDecoder())
01377         return;
01378 
01379     if ((videoOutput->hasMCAcceleration()   ||
01380          videoOutput->hasIDCTAcceleration() ||
01381          videoOutput->hasVLDAcceleration()) &&
01382         (videoOutput->EnoughPrebufferedFrames()))
01383     {
01384         SetPrebuffering(false);
01385     }
01386 
01387 #if FAST_RESTART
01388     if (videoOutput->EnoughPrebufferedFrames())
01389         SetPrebuffering(false);
01390 #else
01391     if (videoOutput->EnoughDecodedFrames())
01392         SetPrebuffering(false);
01393 #endif
01394 }
01395 
01396 bool NuppelVideoPlayer::GetFrameNormal(int onlyvideo)
01397 {
01398     if (!GetDecoder()->GetFrame(onlyvideo))
01399         return false;
01400 
01401     CheckPrebuffering();
01402 
01403     if ((play_speed > 1.01f) && (audio_stretchfactor > 1.01f) && 
01404          livetv && IsNearEnd())
01405     {
01406         VERBOSE(VB_PLAYBACK, LOC + "Near end, Slowing down playback.");
01407         Play(1.0f, true, true);
01408     }
01409 
01410     return true;
01411 }
01412 
01413 bool NuppelVideoPlayer::GetFrameFFREW(void)
01414 {
01415     bool stopFFREW = false;
01416 
01417     if (ringBuffer->isDVD() && GetDecoder())
01418         GetDecoder()->UpdateDVDFramesPlayed();
01419 
01420     if (ffrew_skip > 0)
01421     {
01422         long long delta = GetDecoder()->GetFramesRead() - framesPlayed;
01423         long long real_skip = CalcMaxFFTime(ffrew_skip + delta) - delta;
01424         if (real_skip >= 0)
01425         {
01426             long long frame = GetDecoder()->GetFramesRead() + real_skip;
01427             GetDecoder()->DoFastForward(frame, false);
01428         }
01429         stopFFREW = (CalcMaxFFTime(100, false) < 100);
01430     }
01431     else if (CalcRWTime(-ffrew_skip) >= 0)
01432     {
01433         long long curFrame  = GetDecoder()->GetFramesRead();
01434         bool      toBegin   = -curFrame > ffrew_skip;
01435         long long real_skip = (toBegin) ? -curFrame : ffrew_skip;
01436         GetDecoder()->DoRewind(curFrame + real_skip, false);
01437         if (ringBuffer->isDVD())
01438             stopFFREW = (ringBuffer->DVD()->GetCurrentTime() < 2);
01439         else
01440             stopFFREW = framesPlayed <= keyframedist;
01441     }
01442 
01443     if (stopFFREW)
01444     {
01445         float stretch = (ffrew_skip > 0) ? 1.0f : audio_stretchfactor;
01446         Play(stretch, true, true);
01447     }
01448 
01449     bool ret = GetDecoder()->GetFrame(1);
01450     CheckPrebuffering();
01451     return ret;
01452 }
01453 
01454 bool NuppelVideoPlayer::GetFrame(int onlyvideo, bool unsafe)
01455 {
01456     bool ret = false;
01457 
01458     // Wait for frames to be available for decoding onto
01459     if ((!IsIVTVDecoder()) &&
01460         !videoOutput->EnoughFreeFrames()        &&
01461         !unsafe)
01462     {
01463         //VERBOSE(VB_PLAYBACK, "Waiting for video buffer to drain.");
01464         SetPrebuffering(false);
01465         if (!videoOutput->WaitForAvailable(10) &&
01466             !videoOutput->EnoughFreeFrames())
01467         {
01468             if (++videobuf_retries >= 200)
01469             {
01470                 VERBOSE(VB_IMPORTANT, LOC +
01471                         "Timed out waiting for free video buffers.");
01472                 videobuf_retries = 0;
01473             }
01474             return false;
01475         }
01476         videobuf_retries = 0;
01477     }
01478 
01479     // Decode the correct frame
01480     if (!GetDecoder())
01481         VERBOSE(VB_IMPORTANT, LOC + "GetFrame() called with NULL decoder.");
01482     else if (ffrew_skip == 1)
01483         ret = GetFrameNormal(onlyvideo);
01484     else
01485         ret = GetFrameFFREW(); 
01486 
01487     return ret;
01488 }
01489 
01490 VideoFrame *NuppelVideoPlayer::GetCurrentFrame(int &w, int &h)
01491 {
01492     w = video_dim.width();
01493     h = video_dim.height();
01494 
01495     VideoFrame *retval = NULL;
01496 
01497     vidExitLock.lock();
01498     if (videoOutput)
01499         retval = videoOutput->GetLastShownFrame();
01500 
01501     if (!retval)
01502         vidExitLock.unlock();
01503 
01504     return retval;
01505 }
01506 
01507 void NuppelVideoPlayer::ReleaseCurrentFrame(VideoFrame *frame)
01508 {
01509     if (frame)
01510         vidExitLock.unlock();
01511 }
01512 
01513 void NuppelVideoPlayer::ShutdownYUVResize(void)
01514 {
01515     if (yuv_frame_scaled)
01516     {
01517         delete [] yuv_frame_scaled;
01518         yuv_frame_scaled = NULL;
01519     }
01520 
01521     if (yuv_scaler)
01522     {
01523         img_resample_close(yuv_scaler);
01524         yuv_scaler = NULL;
01525     }
01526 
01527     yuv_scaler_in_size  = QSize(0,0);
01528     yuv_scaler_out_size = QSize(0,0);
01529 }
01530 
01542 const unsigned char *NuppelVideoPlayer::GetScaledFrame(QSize &size)
01543 {
01544     QMutexLocker locker(&yuv_lock);
01545     yuv_desired_size = size = QSize(size.width() & ~0x7, size.height() & ~0x7);
01546 
01547     if ((size.width() > 0) && (size.height() > 0))
01548     {
01549         yuv_need_copy = true;
01550         while (yuv_wait.wait(locker.mutex(), 50) && yuv_need_copy);
01551         return yuv_frame_scaled;
01552     }
01553     return NULL;
01554 }
01555 
01567 const QImage &NuppelVideoPlayer::GetARGBFrame(QSize &size)
01568 {   
01569     unsigned char *yuv_buf = (unsigned char*) GetScaledFrame(size);
01570     if (!yuv_buf)
01571         return argb_scaled_img;
01572 
01573     if (argb_size != size)
01574     { 
01575         if (argb_buf)
01576             delete [] argb_buf;
01577         argb_buf = new unsigned char[(size.width() * size.height() * 4) + 128];
01578         argb_size = QSize(size.width(), size.height());
01579     }
01580 
01581     uint w = argb_size.width(), h = argb_size.height();
01582     yuv2argb_conv(argb_buf,
01583                   yuv_buf, yuv_buf + (w * h), yuv_buf + (w * h * 5 / 4),
01584                   w, h, w * 4, w, w / 2, 0);
01585 
01586     argb_scaled_img = QImage(argb_buf, argb_size.width(), argb_size.height(),
01587                              32, NULL, 65536 * 65536, QImage::LittleEndian);
01588 
01589     return argb_scaled_img;
01590 }
01591 
01592 void NuppelVideoPlayer::EmbedInWidget(WId wid, int x, int y, int w, int h)
01593 {
01594     if (videoOutput)
01595     {
01596         // BEGIN HACK -- Temporarily disable deinterlacing
01597         // Note: this should no longer be needed after mythtv-vid merge
01598         if ((int)embed_saved_scan_type <= kScan_Ignore)
01599         {
01600             embed_saved_scan_type = m_scan;
01601             embed_saved_scan_lock = m_scan_locked;
01602             SetScanType(kScan_Progressive);
01603         }
01604         // END HACK
01605 
01606         videoOutput->EmbedInWidget(wid, x, y, w, h);
01607     }
01608     else
01609     {
01610         embedid = wid;
01611         embx = x;
01612         emby = y;
01613         embw = w;
01614         embh = h;
01615     }
01616 }
01617 
01618 void NuppelVideoPlayer::StopEmbedding(void)
01619 {
01620     if (videoOutput)
01621     {
01622         videoOutput->StopEmbedding();
01623 
01624         // BEGIN HACK -- Temporarily disable deinterlacing
01625         // Note: this should no longer be needed after mythtv-vid merge
01626         if ((int)embed_saved_scan_type >= kScan_Ignore)
01627         {
01628             SetScanType(embed_saved_scan_type);
01629             m_scan_locked = embed_saved_scan_lock;
01630             embed_saved_scan_type = (FrameScanType) -2;
01631         }
01632         // END HACK
01633 
01634         ReinitOSD();
01635     }
01636 }
01637 
01638 void NuppelVideoPlayer::DrawUnusedRects(bool sync)
01639 {
01640     if (videoOutput)
01641         videoOutput->DrawUnusedRects(sync);
01642 }
01643 
01644 void NuppelVideoPlayer::ResetCaptions(uint mode_override)
01645 {
01646     uint origMode   = textDisplayMode;
01647     uint mode       = (mode_override) ? mode_override : origMode;
01648     textDisplayMode = kDisplayNone;
01649 
01650     // resets EIA-608 and Teletext NUV Captions
01651     if (mode & kDisplayNUVCaptions)
01652         ResetCC();
01653 
01654     // resets EIA-708
01655     uint i = (mode & kDisplayCC708) ? 1 : 64;
01656     for (; i < 64; i++)
01657         DeleteWindows(i, 0xff);
01658 
01659     textDisplayMode = origMode;
01660 }
01661 
01662 // caller has decoder_changed_lock
01663 void NuppelVideoPlayer::DisableCaptions(uint mode, bool osd_msg)
01664 {
01665     textDisplayMode &= ~mode;
01666 
01667     ResetCaptions(mode);
01668 
01669     if (!osd || !osd_msg)
01670         return;
01671 
01672     QString msg = "";
01673     if (kDisplayNUVTeletextCaptions & mode)
01674         msg += QObject::tr("TXT CAP");
01675     if (kDisplayTeletextCaptions & mode)
01676     {
01677         msg += decoder->GetTrackDesc(kTrackTypeTeletextCaptions,
01678                                      GetTrack(kTrackTypeTeletextCaptions));
01679 
01680         DisableTeletext();
01681     }
01682     if (kDisplayAVSubtitle & mode)
01683     {
01684         msg += decoder->GetTrackDesc(kTrackTypeSubtitle,
01685                                      GetTrack(kTrackTypeSubtitle));
01686         if (ringBuffer->isDVD())
01687             ringBuffer->DVD()->SetTrack(kTrackTypeSubtitle, -1);
01688     }
01689     if (kDisplayTextSubtitle & mode)
01690     {
01691         msg += QObject::tr("Text subtitles");
01692     }
01693     if (kDisplayCC608 & mode)
01694     {
01695         msg += decoder->GetTrackDesc(kTrackTypeCC608,
01696                                      GetTrack(kTrackTypeCC608));
01697     }
01698     if (kDisplayCC708 & mode)
01699     {
01700         msg += decoder->GetTrackDesc(kTrackTypeCC708,
01701                                      GetTrack(kTrackTypeCC708));
01702     }
01703 
01704     if (! msg.isEmpty())
01705     {
01706         msg += " " + QObject::tr("Off");
01707         osd->SetSettingsText(msg, 3 /* seconds until message timeout */);
01708     }
01709 }
01710 
01711 // caller has decoder_changed_lock
01712 void NuppelVideoPlayer::EnableCaptions(uint mode, bool osd_msg)
01713 {
01714     QString msg = "";
01715     if (kDisplayAVSubtitle & mode)
01716     {
01717         msg += decoder->GetTrackDesc(kTrackTypeSubtitle,
01718                                      GetTrack(kTrackTypeSubtitle));
01719 
01720         if (ringBuffer->isDVD() && osd_msg)
01721             ringBuffer->DVD()->SetTrack(kTrackTypeSubtitle, 
01722                                         GetTrack(kTrackTypeSubtitle));
01723     }
01724     if (kDisplayTextSubtitle & mode)
01725     {
01726         msg += QObject::tr("Text Subtitles");
01727     }
01728     if (kDisplayNUVTeletextCaptions & mode)
01729         msg += QObject::tr("TXT") + QString(" %1").arg(ttPageNum, 3, 16);
01730     if (kDisplayCC608 & mode)
01731     {
01732         msg += decoder->GetTrackDesc(kTrackTypeCC608,
01733                                      GetTrack(kTrackTypeCC608));
01734     }
01735     if (kDisplayCC708 & mode)
01736     {
01737         msg += decoder->GetTrackDesc(kTrackTypeCC708,
01738                                      GetTrack(kTrackTypeCC708));
01739     }
01740     if (kDisplayTeletextCaptions & mode)
01741     {
01742         msg += decoder->GetTrackDesc(kTrackTypeTeletextCaptions,
01743                                      GetTrack(kTrackTypeTeletextCaptions));
01744 
01745         int page = decoder->GetTrackLanguageIndex(
01746             kTrackTypeTeletextCaptions,
01747             GetTrack(kTrackTypeTeletextCaptions));
01748 
01749         TeletextViewer *tt_view = NULL;
01750         if (osd && (tt_view = osd->GetTeletextViewer()) && (page > 0))
01751         {
01752             EnableTeletext();
01753             tt_view->SetPage(page, -1);
01754             textDisplayMode = kDisplayTeletextCaptions;
01755         }
01756     }
01757 
01758     msg += " " + QObject::tr("On");
01759 
01760     textDisplayMode = mode;
01761     if (osd && osd_msg)
01762         osd->SetSettingsText(msg, 3 /* seconds until message timeout */);
01763 
01764 }
01765 
01766 void NuppelVideoPlayer::EnableTeletext(void)
01767 {
01768     if (!GetOSD())
01769         return;
01770 
01771     OSDSet         *oset    = GetOSD()->GetSet("teletext");
01772     TeletextViewer *tt_view = GetOSD()->GetTeletextViewer();
01773     if (oset && tt_view)
01774     {
01775         decoder->SetTeletextDecoderViewer(tt_view);
01776         tt_view->SetDisplaying(true);
01777         tt_view->SetPage(0x100, -1);
01778         oset->Display();
01779         osd->SetVisible(oset, 0);
01780         prevTextDisplayMode = textDisplayMode;
01781         textDisplayMode = kDisplayTeletextMenu;
01782     }
01783 }
01784 
01785 void NuppelVideoPlayer::DisableTeletext(void)
01786 {
01787     if (!GetOSD())
01788         return;
01789 
01790     TeletextViewer *tt_view = GetOSD()->GetTeletextViewer();
01791     if (tt_view)
01792         tt_view->SetDisplaying(false);
01793     GetOSD()->HideSet("teletext");
01794 
01795     textDisplayMode = kDisplayNone;
01796 
01797     /* If subtitles are enabled before the teletext menu was displayed,
01798        re-enabled them. */
01799     if (prevTextDisplayMode & kDisplayAllCaptions)
01800         EnableCaptions(prevTextDisplayMode, false);
01801 }
01802 
01803 void NuppelVideoPlayer::ResetTeletext(void)
01804 {
01805     if (!GetOSD())
01806         return;
01807 
01808     TeletextViewer *tt_view = GetOSD()->GetTeletextViewer();
01809     if (tt_view)
01810         tt_view->Reset();
01811 }
01812 
01813 bool NuppelVideoPlayer::ToggleCaptions(void)
01814 {
01815     SetCaptionsEnabled(!((bool)textDisplayMode));
01816     return textDisplayMode;
01817 }
01818 
01819 bool NuppelVideoPlayer::ToggleCaptions(uint type)
01820 {
01821     uint mode = track_type_to_display_mode[type];
01822     uint origMode = textDisplayMode;
01823 
01824     QMutexLocker locker(&decoder_change_lock);
01825 
01826     if (textDisplayMode)
01827         DisableCaptions(textDisplayMode, origMode & mode);
01828 
01829     if (origMode & mode)
01830         return textDisplayMode;
01831 
01832     if (kDisplayNUVTeletextCaptions & mode)
01833         EnableCaptions(kDisplayNUVTeletextCaptions);
01834 
01835     if (kDisplayCC608 & mode)
01836         EnableCaptions(kDisplayCC608);
01837 
01838     if (kDisplayCC708 & mode)
01839         EnableCaptions(kDisplayCC708);
01840 
01841     if (kDisplayAVSubtitle & mode)
01842         EnableCaptions(kDisplayAVSubtitle);
01843     
01844     if (kDisplayTextSubtitle & mode)
01845         EnableCaptions(kDisplayTextSubtitle);
01846     
01847     if (kDisplayTeletextCaptions & mode)
01848         EnableCaptions(kDisplayTeletextCaptions);
01849 
01850     return textDisplayMode;
01851 }
01852 
01853 void NuppelVideoPlayer::SetCaptionsEnabled(bool enable, bool osd_msg)
01854 {
01855     uint origMode = textDisplayMode;
01856 
01857     textDesired = enable;
01858 
01859     QMutexLocker locker(&decoder_change_lock);
01860 
01861     if (!enable)
01862     {
01863         DisableCaptions(origMode, osd_msg);
01864         return;
01865     }
01866     
01867     // figure out which text type to enable..
01868     bool captions_found = true;
01869     if (decoder->GetTrackCount(kTrackTypeSubtitle))
01870         EnableCaptions(kDisplayAVSubtitle, osd_msg);
01871     else if (textSubtitles.GetSubtitleCount() > 0)
01872         EnableCaptions(kDisplayTextSubtitle, osd_msg);
01873     else if (db_prefer708 && decoder->GetTrackCount(kTrackTypeCC708))
01874         EnableCaptions(kDisplayCC708, osd_msg);
01875     else if (decoder->GetTrackCount(kTrackTypeTeletextCaptions))
01876         EnableCaptions(kDisplayTeletextCaptions, osd_msg);
01877     else if (vbimode == VBIMode::PAL_TT)
01878         EnableCaptions(kDisplayNUVTeletextCaptions, osd_msg);
01879     else if (vbimode == VBIMode::NTSC_CC)
01880     {
01881         if (decoder->GetTrackCount(kTrackTypeCC608))
01882             EnableCaptions(kDisplayCC608, osd_msg);
01883         else
01884             captions_found = false;
01885     }
01886     else if (!db_prefer708 && decoder->GetTrackCount(kTrackTypeCC708))
01887         EnableCaptions(kDisplayCC708, osd_msg);
01888     else 
01889         captions_found = false;
01890 
01891     if (!captions_found && osd && osd_msg)
01892     {
01893         QString msg = QObject::tr(
01894             "No captions", "CC/Teletext/Subtitle text not available");
01895         osd->SetSettingsText(msg, 3 /* seconds until message timeout */);
01896     }
01897 
01898 
01899     // Reset captions, disable old captions on change
01900     ResetCaptions(origMode);
01901     if (origMode != textDisplayMode)
01902         DisableCaptions(origMode, false);
01903 }
01904 
01908 void NuppelVideoPlayer::SetTeletextPage(uint page)
01909 {
01910     QMutexLocker locker(&decoder_change_lock);
01911 
01912     DisableCaptions(textDisplayMode);
01913     ttPageNum = page;
01914     textDisplayMode &= ~kDisplayAllCaptions;
01915     textDisplayMode |= kDisplayNUVTeletextCaptions;
01916 }
01917 
01918 bool NuppelVideoPlayer::HandleTeletextAction(const QString &action)
01919 {
01920     if (!(textDisplayMode & kDisplayTeletextMenu) || !GetOSD())
01921         return false;
01922 
01923     bool handled = true;
01924 
01925     TeletextViewer *tt = GetOSD()->GetTeletextViewer();
01926     if (!tt)
01927         return false;
01928 
01929     if (action == "NEXTPAGE")
01930         tt->KeyPress(TTKey::kNextPage);
01931     else if (action == "PREVPAGE")
01932         tt->KeyPress(TTKey::kPrevPage);
01933     else if (action == "NEXTSUBPAGE")
01934         tt->KeyPress(TTKey::kNextSubPage);
01935     else if (action == "PREVSUBPAGE")
01936         tt->KeyPress(TTKey::kPrevSubPage);
01937     else if (action == "TOGGLEBACKGROUND")
01938         tt->KeyPress(TTKey::kTransparent);
01939     else if (action == "MENURED")
01940         tt->KeyPress(TTKey::kFlofRed);
01941     else if (action == "MENUGREEN")
01942         tt->KeyPress(TTKey::kFlofGreen);
01943     else if (action == "MENUYELLOW")
01944         tt->KeyPress(TTKey::kFlofYellow);
01945     else if (action == "MENUBLUE")
01946         tt->KeyPress(TTKey::kFlofBlue);
01947     else if (action == "MENUWHITE")
01948         tt->KeyPress(TTKey::kFlofWhite);
01949     else if (action == "REVEAL")
01950         tt->KeyPress(TTKey::kRevealHidden);
01951     else if (action == "0" || action == "1" || action == "2" ||
01952              action == "3" || action == "4" || action == "5" ||
01953              action == "6" || action == "7" || action == "8" ||
01954              action == "9")
01955         tt->KeyPress(action.toInt());
01956     else if (action == "MENU" || action == "TOGGLETT" ||
01957              action == "ESCAPE")
01958         DisableTeletext();
01959     else
01960         handled = false;
01961 
01962     return handled;
01963 }
01964 
01968 void NuppelVideoPlayer::ShowText(void)
01969 {
01970     VideoFrame *last = videoOutput->GetLastShownFrame();
01971 
01972     // check if subtitles need to be updated on the OSD
01973     if (osd && tbuffer_numvalid() && txtbuffers[rtxt].timecode &&
01974         (last && txtbuffers[rtxt].timecode <= last->timecode))
01975     {
01976         if (txtbuffers[rtxt].type == 'T')
01977         {
01978             // display full page of teletext
01979             //
01980             // all formatting is always defined in the page itself,
01981             // if scrolling is needed for live closed captions this
01982             // is handled by the broadcaster:
01983             // the pages are then very often transmitted (sometimes as often as
01984             // every 2 frames) with small differences between them
01985             unsigned char *inpos = txtbuffers[rtxt].buffer;
01986             int pagenr;
01987             memcpy(&pagenr, inpos, sizeof(int));
01988             inpos += sizeof(int);
01989 
01990             if (pagenr == (ttPageNum<<16))
01991             {
01992                 // show teletext subtitles
01993                 osd->ClearAllCCText();
01994                 (*inpos)++;
01995                 while (*inpos)
01996                 {
01997                     struct teletextsubtitle st;
01998                     memcpy(&st, inpos, sizeof(st));
01999                     inpos += sizeof(st);
02000                     QString s((const char*) inpos);
02001                     osd->AddCCText(s, st.row, st.col, st.fg, true);
02002                     inpos += st.len;
02003                 }
02004             }
02005         }
02006         else if (txtbuffers[rtxt].type == 'C')
02007         {
02008             UpdateCC(txtbuffers[rtxt].buffer);
02009         }
02010 
02011         text_buflock.lock();
02012         if (rtxt != wtxt) // if a seek occurred, rtxt == wtxt, in this case do
02013                           // nothing
02014             rtxt = (rtxt + 1) % MAXTBUFFER;
02015         text_buflock.unlock();
02016     }
02017 }
02018 
02022 void NuppelVideoPlayer::ResetCC(void)
02023 {
02024     ccline = "";
02025     cccol = 0;
02026     ccrow = 0;
02027     if (osd)
02028         osd->ClearAllCCText();
02029 }
02030 
02034 void NuppelVideoPlayer::UpdateCC(unsigned char *inpos)
02035 {
02036     struct ccsubtitle subtitle;
02037 
02038     memcpy(&subtitle, inpos, sizeof(subtitle));
02039     inpos += sizeof(ccsubtitle);
02040 
02041     // skip undisplayed streams
02042     if ((subtitle.resumetext & CC_MODE_MASK) != ccmode)
02043         return;
02044 
02045     if (subtitle.row == 0)
02046         subtitle.row = 1;
02047 
02048     if (subtitle.clr)
02049     {
02050         //printf ("erase displayed memory\n");
02051         ResetCC();
02052         if (!subtitle.len)
02053             return;
02054     }
02055 
02056 //    if (subtitle.len || !subtitle.clr)
02057     {
02058         unsigned char *end = inpos + subtitle.len;
02059         int row = 0;
02060         int linecont = (subtitle.resumetext & CC_LINE_CONT);
02061 
02062         vector<ccText*> *ccbuf = new vector<ccText*>;
02063         vector<ccText*>::iterator ccp;
02064         ccText *tmpcc = NULL;
02065         int replace = linecont;
02066         int scroll = 0;
02067         bool scroll_prsv = false;
02068         int scroll_yoff = 0;
02069         int scroll_ymax = 15;
02070 
02071         do
02072         {
02073             if (linecont)
02074             {
02075                 // append to last line; needs to be redrawn
02076                 replace = 1;
02077                 // backspace into existing line if needed
02078                 int bscnt = 0;
02079                 while ((inpos < end) && *inpos != 0 && (char)*inpos == '\b')
02080                 {
02081                     bscnt++;
02082                     inpos++;
02083                 }
02084                 if (bscnt)
02085                     ccline.remove(ccline.length() - bscnt, bscnt);
02086             }
02087             else
02088             {
02089                 // new line:  count spaces to calculate column position
02090                 row++;
02091                 cccol = 0;
02092                 ccline = "";
02093                 while ((inpos < end) && *inpos != 0 && (char)*inpos == ' ')
02094                 {
02095                     inpos++;
02096                     cccol++;
02097                 }
02098             }
02099 
02100             ccrow = subtitle.row;
02101             unsigned char *cur = inpos;
02102 
02103             //null terminate at EOL
02104             while (cur < end && *cur != '\n' && *cur != 0)
02105                 cur++;
02106             *cur = 0;
02107 
02108             if (*inpos != 0 || linecont)
02109             {
02110                 if (linecont)
02111                     ccline += QString::fromUtf8((const char *)inpos, -1);
02112                 else
02113                     ccline = QString::fromUtf8((const char *)inpos, -1);
02114                 tmpcc = new ccText();
02115                 tmpcc->text = ccline;
02116                 tmpcc->x = cccol;
02117                 tmpcc->y = ccrow;
02118                 tmpcc->color = 0;
02119                 tmpcc->teletextmode = false;
02120                 ccbuf->push_back(tmpcc);
02121 #if 0
02122                 if (ccbuf->size() > 4)
02123                 {
02124                     printf("CC overflow:  ");
02125                     printf("%d %d %s\n", cccol, ccrow, ccline.ascii());
02126                 }
02127 #endif
02128             }
02129             subtitle.row++;
02130             inpos = cur + 1;
02131             linecont = 0;
02132         } while (inpos < end);
02133 
02134         // adjust row position
02135         if (subtitle.resumetext & CC_TXT_MASK)
02136         {
02137             // TXT mode
02138             // - can use entire 15 rows
02139             // - scroll up when reaching bottom
02140             if (ccrow > 15)
02141             {
02142                 if (row)
02143                     scroll = ccrow - 15;
02144                 if (tmpcc)
02145                     tmpcc->y = 15;
02146             }
02147         }
02148         else if (subtitle.rowcount == 0 || row > 1)
02149         {
02150             // multi-line text
02151             // - fix display of old (badly-encoded) files
02152             if (ccrow > 15)
02153             {
02154                 ccp = ccbuf->begin();
02155                 for (; ccp != ccbuf->end(); ccp++)
02156                 {
02157                     tmpcc = *ccp;
02158                     tmpcc->y -= (ccrow - 15);
02159                 }
02160             }
02161         }
02162         else
02163         {
02164             // scrolling text
02165             // - scroll up previous lines if adding new line
02166             // - if caption is at bottom, row address is for last
02167             // row
02168             // - if caption is at top, row address is for first row (?)
02169             if (subtitle.rowcount > 4)
02170                 subtitle.rowcount = 4;
02171             if (ccrow < subtitle.rowcount)
02172             {
02173                 ccrow = subtitle.rowcount;
02174                 if (tmpcc)
02175                     tmpcc->y = ccrow;
02176             }
02177             if (row)
02178             {
02179                 scroll = row;
02180                 scroll_prsv = true;
02181                 scroll_yoff = ccrow - subtitle.rowcount;
02182                 scroll_ymax = ccrow;
02183             }
02184         }
02185 
02186         if (osd)
02187             osd->UpdateCCText(ccbuf, replace, scroll,
02188                               scroll_prsv, scroll_yoff, scroll_ymax);
02189         delete ccbuf;
02190     }
02191 }
02192 
02194 #define MAXWARPDIFF 0.0005f
02196 #define WARPMULTIPLIER 1000000000
02198 #define WARPAVLEN (video_frame_rate * 600)
02199 
02200 #define WARPCLIP    0.1f
02201 
02202 #define MAXDIVERGE  3.0f
02203 
02205 #define DIVERGELIMIT 30.0f
02206 
02207 float NuppelVideoPlayer::WarpFactor(void)
02208 {
02209     // Calculate a new warp factor
02210     float   divergence;
02211     float   rate;
02212     float   newwarp = 1;
02213     float   warpdiff;
02214 
02215     // Number of frames the audio is out by
02216     divergence = (float)avsync_avg / (float)frame_interval;
02217     // Number of frames divergence is changing by per frame
02218     rate = (float)(avsync_avg - avsync_oldavg) / (float)frame_interval;
02219     avsync_oldavg = avsync_avg;
02220     newwarp = warpfactor_avg * (1 + ((divergence + rate) / 125));
02221 
02222     // Clip the amount changed so we don't get big frequency variations
02223     warpdiff = newwarp / warpfactor;
02224     if (warpdiff > (1 + MAXWARPDIFF))
02225         newwarp = warpfactor * (1 + MAXWARPDIFF);
02226     else if (warpdiff < (1 - MAXWARPDIFF))
02227         newwarp = warpfactor * (1 - MAXWARPDIFF);
02228 
02229     warpfactor = newwarp;
02230 
02231     // Clip final warp factor
02232     if (warpfactor < (1 - WARPCLIP))
02233         warpfactor = 1 - WARPCLIP;
02234     else if (warpfactor > (1 + (WARPCLIP * 2)))
02235         warpfactor = 1 + (WARPCLIP * 2);
02236 
02237     // Keep a 10 minute average
02238     warpfactor_avg = (warpfactor + (warpfactor_avg * (WARPAVLEN - 1))) /
02239                       WARPAVLEN;
02240 
02241     VERBOSE(VB_PLAYBACK|VB_TIMESTAMP,
02242             LOC + QString("A/V Divergence: %1, Rate: %2, Warpfactor: %3, "
02243                           "warpfactor_avg: %4")
02244             .arg(divergence).arg(rate).arg(warpfactor).arg(warpfactor_avg));
02245 
02246     return divergence;
02247 }
02248 
02249 void NuppelVideoPlayer::InitAVSync(void)
02250 {
02251     videosync->Start();
02252 
02253     avsync_adjustment = 0;
02254 
02255     if (usevideotimebase)
02256     {
02257         warpfactor_avg = gContext->GetNumSetting("WarpFactor", 0);
02258         if (warpfactor_avg)
02259             warpfactor_avg /= WARPMULTIPLIER;
02260         else
02261             warpfactor_avg = 1;
02262         // Reset the warpfactor if it's obviously bogus
02263         if (warpfactor_avg < (1 - WARPCLIP))
02264             warpfactor_avg = 1;
02265         if (warpfactor_avg > (1 + (WARPCLIP * 2)) )
02266             warpfactor_avg = 1;
02267 
02268         warpfactor = warpfactor_avg;
02269     }
02270 
02271     refreshrate = videoOutput->GetRefreshRate();
02272     if (refreshrate <= 0)
02273         refreshrate = frame_interval;
02274     vsynctol = refreshrate / 4;
02275 
02276     if (!using_null_videoout)
02277     {
02278         if (usevideotimebase)
02279             VERBOSE(VB_PLAYBACK, "Using video as timebase");
02280         else
02281             VERBOSE(VB_PLAYBACK, "Using audio as timebase");
02282 
02283         QString timing_type = videosync->getName();
02284 
02285         QString msg = QString("Video timing method: %1").arg(timing_type);
02286         VERBOSE(VB_GENERAL, msg);
02287         msg = QString("Refresh rate: %1, frame interval: %2")
02288                        .arg(refreshrate).arg(frame_interval);
02289         VERBOSE(VB_PLAYBACK, msg);
02290         nice(-19);
02291     }
02292 }
02293 
02294 void NuppelVideoPlayer::AVSync(void)
02295 {
02296     float diverge = 0.0f;
02297 
02298     VideoFrame *buffer = videoOutput->GetLastShownFrame();
02299     if (!buffer)
02300     {
02301         VERBOSE(VB_IMPORTANT, LOC_ERR + "AVSync: No video buffer");
02302         return;
02303     }
02304     if (videoOutput->IsErrored())
02305     {
02306         VERBOSE(VB_IMPORTANT, LOC_ERR + "AVSync: "
02307                 "Unknown error in videoOutput, aborting playback.");
02308         errored = true;
02309         return;
02310     }
02311 
02312     // The warp calculation is only valid at "normal_speed" playback.
02313     if (normal_speed)
02314     {
02315         diverge = WarpFactor();
02316         // If we are WAY out of sync, we can't really adjust this much
02317         // so just adjust by DIVERGELIMIT and hope lip-sync remains.
02318         diverge = max(diverge, -DIVERGELIMIT);
02319         diverge = min(diverge, +DIVERGELIMIT);
02320     }
02321 
02322     FrameScanType ps = m_scan;
02323     if (kScan_Detect == m_scan || kScan_Ignore == m_scan)
02324         ps = kScan_Progressive;
02325 
02326     if (diverge < -MAXDIVERGE)
02327     {
02328         // If video is way behind of audio, adjust for it...
02329         QString dbg = QString("Video is %1 frames behind audio (too slow), ")
02330             .arg(-diverge);
02331 
02332         // Reset A/V Sync
02333         lastsync = true;
02334  
02335         if (buffer && !using_null_videoout &&
02336             (videoOutput->hasMCAcceleration()   ||
02337              videoOutput->hasIDCTAcceleration() ||
02338              videoOutput->hasVLDAcceleration()))
02339         {
02340             // If we are using hardware decoding, so we've already done the
02341             // decoding; display the frame, but don't wait for A/V Sync.
02342             videoOutput->PrepareFrame(buffer, kScan_Intr2ndField);
02343             videoOutput->Show(kScan_Intr2ndField);
02344             VERBOSE(VB_PLAYBACK, LOC + dbg + "skipping A/V wait.");
02345         }
02346         else
02347         {
02348             // If we are using software decoding, skip this frame altogether.
02349             VERBOSE(VB_PLAYBACK, LOC + dbg + "dropping frame to catch up.");
02350         }
02351     }
02352     else if (!using_null_videoout)
02353     {
02354         // if we get here, we're actually going to do video output
02355         if (buffer)
02356             videoOutput->PrepareFrame(buffer, ps);
02357 
02358         videosync->WaitForFrame(avsync_adjustment);
02359         if (!resetvideo)
02360             videoOutput->Show(ps);
02361 
02362         if (videoOutput->IsErrored())
02363         {
02364             VERBOSE(VB_IMPORTANT, "NVP: Error condition detected "
02365                     "in videoOutput after Show(), aborting playback.");
02366             errored = true;
02367             return;
02368         }
02369 
02370         if (m_double_framerate)
02371         {
02372             //second stage of deinterlacer processing
02373             if (m_double_process && ps != kScan_Progressive)
02374             {
02375                 videofiltersLock.lock();
02376                 if (ringBuffer->isDVD() &&
02377                     ringBuffer->DVD()->InStillFrame() &&
02378                     videoOutput->ValidVideoFrames() < 3)
02379                 {
02380                     videoOutput->ProcessFrame(buffer, NULL, NULL, pipplayer);
02381                 }
02382                 else
02383                 {
02384                     videoOutput->ProcessFrame(
02385                         buffer, osd, videoFilters, pipplayer);
02386                 }
02387                 videofiltersLock.unlock();
02388             }
02389 
02390             ps = (kScan_Intr2ndField == ps) ?
02391                 kScan_Interlaced : kScan_Intr2ndField;
02392 
02393             if (buffer)
02394                 videoOutput->PrepareFrame(buffer, ps);
02395 
02396             // Display the second field
02397             videosync->AdvanceTrigger();
02398             videosync->WaitForFrame(0);
02399             if (!resetvideo)
02400             {
02401                 videoOutput->Show(ps);
02402             }
02403         }
02404     }
02405     else
02406     {
02407         videosync->WaitForFrame(0);
02408     }
02409 
02410     if (output_jmeter && output_jmeter->RecordCycleTime())
02411     {
02412         VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, QString("A/V avsync_delay: %1, "
02413                 "avsync_avg: %2, warpfactor: %3, warpfactor_avg: %4")
02414                 .arg(avsync_delay / 1000).arg(avsync_avg / 1000)
02415                 .arg(warpfactor).arg(warpfactor_avg));
02416     }
02417 
02418     videosync->AdvanceTrigger();
02419     avsync_adjustment = 0;
02420 
02421     if (diverge > MAXDIVERGE)
02422     {
02423         // If audio is way behind of video, adjust for it...
02424         // by cutting the frame rate in half for the length of this frame
02425 
02426         avsync_adjustment = frame_interval;
02427         lastsync = true;
02428         VERBOSE(VB_PLAYBACK, LOC + 
02429                 QString("Video is %1 frames ahead of audio,\n"
02430                         "\t\t\tdoubling video frame interval to slow down.").arg(diverge));
02431     }
02432 
02433     if (audioOutput && normal_speed)
02434     {
02435         long long currentaudiotime = audioOutput->GetAudiotime();
02436 #if 0
02437         VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, QString(
02438                     "A/V timecodes audio %1 video %2 frameinterval %3 "
02439                     "avdel %4 avg %5 tcoffset %6")
02440                 .arg(currentaudiotime)
02441                 .arg(buffer->timecode)
02442                 .arg(frame_interval)
02443                 .arg(buffer->timecode - currentaudiotime)
02444                 .arg(avsync_avg)
02445                 .arg(tc_wrap[TC_AUDIO])
02446                  );
02447 #endif
02448         if (currentaudiotime != 0 && buffer->timecode != 0)
02449         { // currentaudiotime == 0 after a seek
02450             // The time at the start of this frame (ie, now) is given by
02451             // last->timecode
02452             int delta = (int)((buffer->timecode - prevtc)/play_speed) - (frame_interval / 1000);
02453             prevtc = buffer->timecode;
02454             //cerr << delta << " ";
02455 
02456             // If the timecode is off by a frame (dropped frame) wait to sync
02457             if (delta > (int) frame_interval / 1200 &&
02458                 delta < (int) frame_interval / 1000 * 3)
02459             {
02460                 //cerr << "+ ";
02461                 videosync->AdvanceTrigger();
02462                 if (m_double_framerate)
02463                     videosync->AdvanceTrigger();
02464             }
02465 
02466             avsync_delay = (buffer->timecode - currentaudiotime) * 1000;//usec
02467             // prevents major jitter when pts resets during dvd title
02468             if (avsync_delay > 2000000 && ringBuffer->isDVD())
02469                 avsync_delay = 90000;
02470             avsync_avg = (avsync_delay + (avsync_avg * 3)) / 4;
02471             if (!usevideotimebase)
02472             {
02473                 /* If the audio time codes and video diverge, shift
02474                    the video by one interlaced field (1/2 frame) */
02475 
02476                 if (!lastsync)
02477                 {
02478                     if (avsync_avg > frame_interval * 3 / 2)
02479                     {
02480                         avsync_adjustment = refreshrate;
02481                         lastsync = true;
02482                     }
02483                     else if (avsync_avg < 0 - frame_interval * 3 / 2)
02484                     {
02485                         avsync_adjustment = -refreshrate;
02486                         lastsync = true;
02487                     }
02488                 }
02489                 else
02490                     lastsync = false;
02491             }
02492         }
02493         else
02494         {
02495             avsync_avg = 0;
02496             avsync_oldavg = 0;
02497         }
02498     }
02499 }
02500 
02501 void NuppelVideoPlayer::ShutdownAVSync(void)
02502 {
02503     if (usevideotimebase)
02504     {
02505         gContext->SaveSetting("WarpFactor",
02506             (int)(warpfactor_avg * WARPMULTIPLIER));
02507 
02508         if (warplbuff)
02509         {
02510             free(warplbuff);
02511             warplbuff = NULL;
02512         }
02513 
02514         if (warprbuff)
02515         {
02516             free(warprbuff);
02517             warprbuff = NULL;
02518         }
02519         warpbuffsize = 0;
02520     }
02521 }
02522 
02523 void NuppelVideoPlayer::DisplayPauseFrame(void)
02524 {
02525     if (!video_actually_paused)
02526         videoOutput->UpdatePauseFrame();
02527 
02528     if (resetvideo)
02529     {
02530         videoOutput->UpdatePauseFrame();
02531         resetvideo = false;
02532     }
02533 
02534     SetVideoActuallyPaused(true);
02535 
02536     if (videoOutput->IsErrored())
02537     {
02538         errored = true;
02539         return;
02540     }
02541 
02542     DisplayDVDButton();
02543 
02544     videofiltersLock.lock();
02545     videoOutput->ProcessFrame(NULL, osd, videoFilters, pipplayer);
02546     videofiltersLock.unlock();
02547 
02548     videoOutput->PrepareFrame(NULL, kScan_Ignore);
02549     videoOutput->Show(kScan_Ignore);
02550     videosync->Start();
02551 }
02552 
02553 bool NuppelVideoPlayer::PrebufferEnoughFrames(void)
02554 {
02555     prebuffering_lock.lock();
02556     if (prebuffering)
02557     {
02558         if (ringBuffer->InDVDMenuOrStillFrame() && 
02559             prebuffer_tries > 3)
02560         {
02561             prebuffering = false;
02562             prebuffer_tries = 0;
02563             prebuffering_lock.unlock();
02564             return true;
02565         }
02566 
02567         if (!ringBuffer->InDVDMenuOrStillFrame() && 
02568             !audio_paused && audioOutput)
02569         {
02570            if (prebuffering)
02571                 audioOutput->Pause(prebuffering);
02572             audio_paused = prebuffering;
02573         }
02574 
02575         VERBOSE(VB_PLAYBACK, LOC + QString("Waiting for prebuffer.. %1 %2")
02576                 .arg(prebuffer_tries).arg(videoOutput->GetFrameStatus()));
02577         if (!prebuffering_wait.wait(&prebuffering_lock,
02578                                     frame_interval * 4 / 1000))
02579         {
02580             // timed out.. do we need to know?
02581         }
02582         ++prebuffer_tries;
02583         if (prebuffering && (prebuffer_tries >= 10))
02584         {
02585             VERBOSE(VB_IMPORTANT, LOC + "Prebuffer wait timed out 10 times.");
02586             if (!videoOutput->EnoughFreeFrames())
02587             {
02588                 VERBOSE(VB_IMPORTANT, LOC + "Prebuffer wait timed out, and"
02589                         "\n\t\t\tthere are not enough free frames. "
02590                         "Discarding buffered frames.");
02591                 // This call will result in some ugly frames, but allows us
02592                 // to recover from serious problems if frames get leaked.
02593                 DiscardVideoFrames(true);
02594             }
02595             prebuffer_tries = 0;
02596         }
02597         prebuffering_lock.unlock();
02598         videosync->Start();
02599 
02600         return false;
02601     }
02602     prebuffering_lock.unlock();
02603 
02604     //VERBOSE(VB_PLAYBACK, LOC + "fs: " + videoOutput->GetFrameStatus());
02605     if (!videoOutput->EnoughPrebufferedFrames())
02606     {
02607         VERBOSE(VB_GENERAL, LOC + "prebuffering pause");
02608         if (videoOutput)
02609             videoOutput->CheckFrameStates();
02610 
02611         SetPrebuffering(true);
02612 #if FAST_RESTART
02613         if (!m_playing_slower && audio_channels <= 2)
02614         {
02615             m_stored_audio_stretchfactor = GetAudioStretchFactor();
02616             Play(m_stored_audio_stretchfactor * 0.8, true);
02617             m_playing_slower = true;
02618             VERBOSE(VB_GENERAL, "playing slower due to falling behind...");
02619         }
02620 #endif
02621         return false;
02622     }
02623 
02624 #if FAST_RESTART
02625     if (m_playing_slower && videoOutput->EnoughDecodedFrames())
02626     {
02627         Play(m_stored_audio_stretchfactor, true);
02628         m_playing_slower = false;
02629         VERBOSE(VB_GENERAL, "playing at normal speed from falling behind...");
02630     }
02631 #endif
02632 
02633     prebuffering_lock.lock();
02634     prebuffer_tries = 0;
02635     prebuffering_lock.unlock();
02636 
02637     return true;
02638 }
02639 
02640 void NuppelVideoPlayer::DisplayNormalFrame(void)
02641 {
02642     SetVideoActuallyPaused(false);
02643     resetvideo = false;
02644 
02645     if (!ringBuffer->InDVDMenuOrStillFrame() ||
02646         (ringBuffer->DVD()->NumMenuButtons() > 0 && 
02647         ringBuffer->DVD()->GetChapterLength() > 3))
02648     {
02649         if (!PrebufferEnoughFrames())
02650         {
02651             // When going to switch channels
02652             if (paused)
02653             {
02654                 usleep(frame_interval);
02655                 DisplayPauseFrame();
02656             }
02657             return;
02658         }
02659     }
02660 
02661     videoOutput->StartDisplayingFrame();
02662 
02663     VideoFrame *frame = videoOutput->GetLastShownFrame();
02664 
02665     if (yuv_need_copy)
02666     {
02667         // We need to make a preview copy of the frame...
02668         QMutexLocker locker(&yuv_lock);
02669         QSize vsize = video_dim;
02670         if ((vsize            != yuv_scaler_in_size) ||
02671             (yuv_desired_size != yuv_scaler_out_size))
02672         {
02673             ShutdownYUVResize();
02674 
02675             uint sz = yuv_desired_size.width() * yuv_desired_size.height();
02676             yuv_frame_scaled = new unsigned char[(sz * 3 / 2) + 128];
02677 
02678             yuv_scaler_in_size  = vsize;
02679             yuv_scaler_out_size = yuv_desired_size;
02680 
02681             yuv_scaler = img_resample_init(
02682                 yuv_scaler_out_size.width(), yuv_scaler_out_size.height(),
02683                 yuv_scaler_in_size.width(),  yuv_scaler_in_size.height());
02684         }
02685 
02686         AVPicture img_out, img_in;
02687         avpicture_fill(&img_out, yuv_frame_scaled, PIX_FMT_YUV420P,
02688                        yuv_scaler_out_size.width(),
02689                        yuv_scaler_out_size.height());
02690         avpicture_fill(&img_in, frame->buf, PIX_FMT_YUV420P,
02691                        yuv_scaler_in_size.width(),
02692                        yuv_scaler_in_size.height());
02693 
02694         img_resample(yuv_scaler, &img_out, &img_in);
02695         yuv_need_copy = false;
02696         yuv_wait.wakeAll();
02697     }
02698 
02699     DisplayDVDButton();
02700 
02701     // handle Interactive TV
02702     if (GetInteractiveTV() && GetDecoder())
02703     {
02704         QMutexLocker locker(&itvLock);
02705 
02706         OSD *osd = GetOSD();
02707         if (osd)
02708         {
02709             OSDSet *itvosd = osd->GetSet("interactive");
02710 
02711             if (itvosd)
02712             {
02713                 bool visible = false;
02714                 if (interactiveTV->ImageHasChanged() || !itvVisible)
02715                 {
02716                     interactiveTV->UpdateOSD(itvosd);
02717                     visible = true;
02718                     itvVisible = true;
02719                 }
02720 
02721                 if (visible)
02722                     osd->SetVisible(itvosd, 0);
02723             }
02724         }
02725     }
02726 
02727     // handle EIA-608 and Teletext
02728     if (textDisplayMode & kDisplayNUVCaptions)
02729         ShowText();
02730 
02731     // handle DVB/DVD subtitles decoded by ffmpeg (in AVSubtitle format)
02732     if (ffrew_skip == 1)
02733     {
02734         if (textDisplayMode & kDisplayAVSubtitle) 
02735             DisplayAVSubtitles();
02736         else if (textDisplayMode & kDisplayTextSubtitle)
02737             DisplayTextSubtitles();
02738         else if (osdHasSubtitles) 
02739             ClearSubtitles();
02740         else
02741             ExpireSubtitles();
02742     }
02743 
02744     // handle scan type changes
02745     AutoDeint(frame);
02746 
02747     videofiltersLock.lock();
02748     if (ringBuffer->isDVD() &&
02749         ringBuffer->DVD()->InStillFrame() &&
02750         videoOutput->ValidVideoFrames() < 3)
02751     {
02752         videoOutput->ProcessFrame(frame, NULL, NULL, pipplayer);
02753     }
02754     else
02755         videoOutput->ProcessFrame(frame, osd, videoFilters, pipplayer);
02756     videofiltersLock.unlock();
02757 
02758     if (audioOutput && !audio_paused && audioOutput->GetPause())
02759         audioOutput->Pause(false);
02760 
02761     AVSync();
02762 
02763     videoOutput->DoneDisplayingFrame();
02764 }
02765 
02766 void NuppelVideoPlayer::OutputVideoLoop(void)
02767 {
02768     delay = 0;
02769     avsync_delay = 0;
02770     avsync_avg = 0;
02771     avsync_oldavg = 0;
02772     refreshrate = 0;
02773     lastsync = false;
02774 
02775     usevideotimebase = gContext->GetNumSetting("UseVideoTimebase", 0);
02776 
02777     if ((print_verbose_messages & VB_PLAYBACK) != 0)
02778         output_jmeter = new Jitterometer("video_output", 100);
02779     else
02780         output_jmeter = NULL;
02781 
02782     refreshrate = frame_interval;
02783 
02784     float temp_speed = (play_speed == 0.0) ? audio_stretchfactor : play_speed;
02785     uint fr_int = (int)(1000000.0 / video_frame_rate / temp_speed);
02786     uint rf_int = 0;
02787     if (videoOutput)
02788         rf_int = videoOutput->GetRefreshRate();
02789 
02790     // Default to Interlaced playback to allocate the deinterlacer structures
02791     // Enable autodetection of interlaced/progressive from video stream
02792     // And initialoze m_scan_tracker to 2 which will immediately switch to
02793     // progressive if the first frame is progressive in AutoDeint().
02794     m_scan             = kScan_Interlaced;
02795     m_scan_locked      = false;
02796     m_double_framerate = false;
02797     m_can_double       = false;
02798     m_scan_tracker     = 2;
02799 
02800     if (using_null_videoout)
02801     {
02802         videosync = new USleepVideoSync(videoOutput, (int)fr_int, 0, false);
02803     }
02804     else if (videoOutput)
02805     {
02806         // Set up deinterlacing in the video output method
02807         m_double_framerate =
02808             (videoOutput->SetupDeinterlace(true) &&
02809              videoOutput->NeedsDoubleFramerate());
02810 
02811         m_double_process = videoOutput->IsExtraProcessingRequired();
02812 
02813         videosync = VideoSync::BestMethod(
02814             videoOutput, fr_int, rf_int, m_double_framerate);
02815 
02816         // Make sure video sync can do it
02817         if (videosync != NULL && m_double_framerate)
02818         {
02819             videosync->SetFrameInterval(frame_interval, m_double_framerate);
02820             m_can_double = videosync->UsesFieldInterval();
02821             if (!m_can_double)
02822             {
02823                 VERBOSE(VB_IMPORTANT, "Video sync method can't support double "
02824                         "framerate (refresh rate too low for bob deint)");
02825                 FallbackDeint();
02826             }
02827 
02828             if (osd && !IsIVTVDecoder())
02829             {
02830                 osd->SetFrameInterval(
02831                     (m_double_framerate && m_double_process) ?
02832                     (frame_interval>>1) : frame_interval);
02833             }
02834         }
02835     }
02836     if (!videosync)
02837     {
02838         videosync = new BusyWaitVideoSync(
02839             videoOutput, (int)fr_int, (int)rf_int, m_double_framerate);
02840     }
02841 
02842     InitAVSync();
02843 
02844     videosync->Start();
02845 
02846     while (!killvideo)
02847     {
02848         if (needsetpipplayer)
02849         {
02850             pipplayer = setpipplayer;
02851             needsetpipplayer = false;
02852         }
02853 
02854         if (ringBuffer->isDVD())
02855         {
02856             int nbframes = videoOutput->ValidVideoFrames();
02857 
02858             if (nbframes < 2) 
02859             {
02860                 bool isWaiting  = ringBuffer->DVD()->IsWaiting();
02861 
02862                 if (isWaiting)
02863                 {
02864                     ringBuffer->DVD()->WaitSkip();
02865                     continue;
02866                 }
02867 
02868                 if (ringBuffer->InDVDMenuOrStillFrame())
02869                 {
02870                     if (nbframes == 0)
02871                     {
02872                         VERBOSE(VB_PLAYBACK, LOC_ERR + 
02873                                 "In DVD Menu: No video frames in queue");
02874                         if (pausevideo)
02875                             UnpauseVideo();
02876                         usleep(10000);
02877                         continue;
02878                     }
02879 
02880                     if (!pausevideo && nbframes == 1)
02881                     {
02882                         dvd_stillframe_showing = true;
02883                         PauseVideo(false);
02884                     }
02885                 }
02886             }
02887                 
02888             if (dvd_stillframe_showing && nbframes > 1)
02889             {
02890                 UnpauseVideo();
02891                 dvd_stillframe_showing = false;
02892                 continue;
02893             }
02894         }
02895 
02896         if (pausevideo || isDummy)
02897         {
02898             usleep(frame_interval);
02899             DisplayPauseFrame();
02900         }
02901         else
02902             DisplayNormalFrame();
02903     }
02904 
02905     {
02906         QMutexLocker locker(&vidExitLock);
02907         delete videosync;
02908         videosync = NULL;
02909 
02910         delete videoOutput;
02911         videoOutput = NULL;
02912     }
02913 
02914     ShutdownAVSync();
02915 }
02916 
02917 #ifdef USING_IVTV
02918 
02925 void NuppelVideoPlayer::IvtvVideoLoop(void)
02926 {
02927     refreshrate = frame_interval;
02928     int delay = frame_interval;
02929 
02930     VideoOutputIvtv *vidout = (VideoOutputIvtv *)videoOutput;
02931     vidout->SetFPS(GetFrameRate());
02932 
02933     while (!killvideo)
02934     {
02935         if (needsetpipplayer)
02936         {
02937             pipplayer = setpipplayer;
02938             needsetpipplayer = false;
02939         }
02940 
02941         resetvideo = false;
02942         SetVideoActuallyPaused(pausevideo);
02943 
02944         if (pausevideo)
02945         {
02946             videofiltersLock.lock();
02947             videoOutput->ProcessFrame(NULL, osd, videoFilters, pipplayer);
02948             videofiltersLock.unlock();
02949         }
02950         else
02951         {
02952             // handle EIA-608 and Teletext
02953             if (textDisplayMode & kDisplayNUVCaptions)
02954                 ShowText();
02955 
02956             videofiltersLock.lock();
02957             videoOutput->ProcessFrame(NULL, osd, videoFilters, pipplayer);
02958             videofiltersLock.unlock();
02959         }
02960 
02961         usleep(delay);
02962     }
02963 
02964     // no need to lock this, we don't have any video frames
02965     delete videoOutput;
02966     videoOutput = NULL;
02967 }
02968 #endif
02969 
02970 void *NuppelVideoPlayer::kickoffOutputVideoLoop(void *player)
02971 {
02972     NuppelVideoPlayer *nvp = (NuppelVideoPlayer *)player;
02973 
02974 #ifdef USING_IVTV
02975     if (nvp->IsIVTVDecoder())
02976     {
02977         nvp->IvtvVideoLoop();
02978         return NULL;
02979     }
02980 #endif
02981 
02982     // OS X needs a garbage collector allocated..
02983     void *video_thread_pool = CreateOSXCocoaPool();
02984     nvp->OutputVideoLoop();
02985     DeleteOSXCocoaPool(video_thread_pool);
02986 
02987     return NULL;
02988 }
02989 
02990 bool NuppelVideoPlayer::FastForward(float seconds)
02991 {
02992     if (!videoOutput)
02993         return false;
02994 
02995     if (ringBuffer->isDVD() && GetDecoder())
02996         GetDecoder()->UpdateDVDFramesPlayed();
02997 
02998     if (fftime <= 0)
02999         fftime = (int)(seconds * video_frame_rate);
03000 
03001     if (osdHasSubtitles || !nonDisplayedAVSubtitles.empty())
03002        ClearSubtitles();
03003 
03004     return fftime > CalcMaxFFTime(fftime, false);
03005 }
03006 
03007 bool NuppelVideoPlayer::Rewind(float seconds)
03008 {
03009     if (!videoOutput)
03010         return false;
03011 
03012     if (ringBuffer->isDVD() && GetDecoder())
03013        GetDecoder()->UpdateDVDFramesPlayed();
03014 
03015     if (rewindtime <= 0)
03016         rewindtime = (int)(seconds * video_frame_rate);
03017 
03018     if (osdHasSubtitles || !nonDisplayedAVSubtitles.empty())
03019        ClearSubtitles();
03020 
03021     return rewindtime >= framesPlayed;
03022 }
03023 
03024 void NuppelVideoPlayer::SkipCommercials(int direction)
03025 {
03026     if (skipcommercials == 0)
03027         skipcommercials = direction;
03028 }
03029 
03030 void NuppelVideoPlayer::ResetPlaying(void)
03031 {
03032     ClearAfterSeek();
03033 
03034     ffrew_skip = 1;
03035 
03036     if (!ringBuffer->isDVD())
03037         framesPlayed = 0;
03038 
03039     GetDecoder()->Reset();
03040     errored |= GetDecoder()->IsErrored();
03041 }
03042 
03043 void NuppelVideoPlayer::CheckTVChain(void)
03044 {
03045     bool last = !(livetvchain->HasNext());
03046     SetWatchingRecording(last);
03047 }
03048 
03049 void NuppelVideoPlayer::SwitchToProgram(void)
03050 {
03051     if (!IsReallyNearEnd())
03052         return;
03053     VERBOSE(VB_PLAYBACK, "SwitchToProgram(void)");
03054 
03055     bool discontinuity = false, newtype = false;
03056     int newid = -1;
03057     ProgramInfo *pginfo = livetvchain->GetSwitchProgram(discontinuity, newtype,
03058                                                         newid);
03059     if (!pginfo)
03060         return;
03061 
03062     bool newIsDummy = livetvchain->GetCardType(newid) == "DUMMY";
03063 
03064     SetPlaybackInfo(pginfo);
03065 
03066     ringBuffer->Pause();
03067     ringBuffer->WaitForPause();
03068 
03069     if (newIsDummy)
03070     {
03071         OpenDummy();
03072         ResetPlaying();
03073         DoPause();
03074         eof = false;
03075         return;
03076     }
03077 
03078     ringBuffer->OpenFile(pginfo->GetPlaybackURL(),
03079                          10 /* retries -- about 5 seconds */);
03080     if (!ringBuffer->IsOpen())
03081     {
03082         VERBOSE(VB_IMPORTANT, LOC_ERR + "SwitchToProgram's OpenFile failed.");
03083         eof = true;
03084         errored = true;
03085         return;
03086     }
03087 
03088     if (eof)
03089     {
03090         discontinuity = true;
03091         ClearSubtitles();
03092     }
03093 
03094     livetvchain->SetProgram(pginfo);
03095 
03096     if (discontinuity || newtype)
03097     {
03098         GetDecoder()->SetProgramInfo(pginfo);
03099 
03100         ringBuffer->Reset(true);
03101         if (newtype)
03102             errored = (OpenFile() >= 0) ? errored : true;
03103         else
03104             ResetPlaying();
03105     }
03106     else
03107     {
03108         GetDecoder()->SetReadAdjust(ringBuffer->SetAdjustFilesize());
03109         GetDecoder()->SetWaitForChange();
03110         if (m_tv)
03111             m_tv->SetIgnoreKeys(true);
03112     }
03113     if (IsErrored())
03114     {
03115         VERBOSE(VB_IMPORTANT, LOC_ERR + "SwitchToProgram failed.");
03116         eof = true;
03117         return;
03118     }
03119 
03120     // the bitrate is reset by ringBuffer->OpenFile()...
03121     ringBuffer->UpdateRawBitrate(GetDecoder()->GetRawBitrate());
03122 
03123     ringBuffer->Unpause();
03124 
03125     if (discontinuity || newtype)
03126     {
03127         if (m_tv)
03128             m_tv->SetCurrentlyPlaying(pginfo);
03129 
03130         CheckTVChain();
03131         GetDecoder()->SyncPositionMap();
03132     }
03133 
03134     eof = false;
03135 }
03136 
03137 void NuppelVideoPlayer::FileChangedCallback(void)
03138 {
03139     VERBOSE(VB_PLAYBACK, "FileChangedCallback");
03140 
03141     ringBuffer->Pause();
03142     ringBuffer->WaitForPause();
03143 
03144     if (dynamic_cast<AvFormatDecoder *>(GetDecoder()))
03145         ringBuffer->Reset(false, true);
03146     else
03147         ringBuffer->Reset(false, true, true);
03148 
03149     ringBuffer->Unpause();
03150 
03151     if (m_tv)
03152         m_tv->SetIgnoreKeys(false);
03153 
03154     livetvchain->SetProgram(m_playbackinfo);
03155     GetDecoder()->SetProgramInfo(m_playbackinfo);
03156     if (m_tv)
03157         m_tv->SetCurrentlyPlaying(m_playbackinfo);
03158 
03159     CheckTVChain();
03160     GetDecoder()->SyncPositionMap();
03161 }
03162 
03163 void NuppelVideoPlayer::JumpToProgram(void)
03164 {
03165     VERBOSE(VB_PLAYBACK, "JumpToProgram(void)");
03166     bool discontinuity = false, newtype = false;
03167     int newid = -1;
03168     ProgramInfo *pginfo = livetvchain->GetSwitchProgram(discontinuity, newtype,
03169                                                         newid);
03170     if (!pginfo)
03171         return;
03172 
03173     long long nextpos = livetvchain->GetJumpPos();
03174     bool newIsDummy = livetvchain->GetCardType(newid) == "DUMMY";
03175     
03176     SetPlaybackInfo(pginfo);
03177 
03178     ringBuffer->Pause();
03179     ringBuffer->WaitForPause();
03180 
03181     ClearSubtitles();
03182 
03183     livetvchain->SetProgram(pginfo);
03184 
03185     ringBuffer->Reset(true);
03186 
03187     if (newIsDummy)
03188     {
03189         OpenDummy();
03190         ResetPlaying();
03191         DoPause();
03192         eof = false;
03193         return;
03194     }
03195 
03196     ringBuffer->OpenFile(pginfo->GetPlaybackURL());
03197     if (!ringBuffer->IsOpen())
03198     {
03199         VERBOSE(VB_IMPORTANT, LOC_ERR + "JumpToProgram's OpenFile failed.");
03200         eof = true;
03201         errored = true;
03202         return;
03203     }
03204 
03205     bool wasDummy = isDummy;
03206     if (newtype || wasDummy)
03207         errored = (OpenFile() >= 0) ? errored : true;
03208     else
03209         ResetPlaying();
03210 
03211     if (wasDummy)
03212         DoPlay();
03213 
03214     if (errored || !GetDecoder())
03215     {
03216         VERBOSE(VB_IMPORTANT, LOC_ERR + "JumpToProgram failed.");
03217         errored = true;
03218         return;
03219     }
03220 
03221     // the bitrate is reset by ringBuffer->OpenFile()...
03222     ringBuffer->UpdateRawBitrate(GetDecoder()->GetRawBitrate());
03223 
03224     ringBuffer->Unpause();
03225     ringBuffer->IgnoreLiveEOF(false);
03226 
03227     GetDecoder()->SetProgramInfo(pginfo);
03228     if (m_tv)
03229         m_tv->SetCurrentlyPlaying(pginfo);
03230 
03231     CheckTVChain();
03232     GetDecoder()->SyncPositionMap();
03233 
03234     if (nextpos < 0)
03235         nextpos += totalFrames;
03236     if (nextpos < 0)
03237         nextpos = 0;
03238 
03239     if (nextpos > 10)
03240     {
03241         bool seeks = exactseeks;
03242         GetDecoder()->setExactSeeks(false);
03243         fftime = nextpos;
03244         DoFastForward();
03245         fftime = 0;
03246         GetDecoder()->setExactSeeks(seeks);
03247     }
03248 
03249     eof = false;
03250 }
03251 
03252 void NuppelVideoPlayer::StartPlaying(void)
03253 {
03254     killplayer = false;
03255     framesPlayed = 0;
03256 
03257     if (OpenFile() < 0)
03258         return;
03259 
03260     if (ringBuffer->isDVD())
03261         ringBuffer->DVD()->SetParent(this);
03262 
03263     if (!no_audio_out ||
03264         (IsIVTVDecoder() &&
03265          !gContext->GetNumSetting("PVR350InternalAudioOnly")))
03266     {
03267         QString errMsg = ReinitAudio();
03268         DialogCode ret = kDialogCodeButton0;
03269         if ((errMsg != QString::null) && !using_null_videoout &&
03270             gContext->GetNumSetting("AudioNag", 1))
03271         {
03272             DialogBox *dlg = new DialogBox(gContext->GetMainWindow(), errMsg);
03273 
03274             QString noaudio  = QObject::tr("Continue WITHOUT AUDIO!");
03275             QString dontask  = noaudio + " " + 
03276                 QObject::tr("And, never ask again.");
03277             QString neverask = noaudio + " " +
03278                 QObject::tr("And, don't ask again in this session.");
03279             QString quit     = QObject::tr("Return to menu.");
03280 
03281             dlg->AddButton(noaudio);
03282             dlg->AddButton(dontask);
03283             dlg->AddButton(neverask);
03284             dlg->AddButton(quit);
03285 
03286             qApp->lock();
03287             ret = dlg->exec();
03288             dlg->deleteLater();
03289             qApp->unlock();
03290         }
03291             
03292         if (kDialogCodeButton1 == ret)
03293             gContext->SaveSetting("AudioNag", 0);
03294         if (kDialogCodeButton2 == ret)
03295             gContext->SetSetting("AudioNag", 0);
03296         else if ((kDialogCodeButton3 == ret) || (kDialogCodeRejected == ret))
03297             return;
03298     }
03299 
03300     if (audioOutput)
03301     {
03302         audio_paused = true;
03303         audioOutput->Pause(true);
03304         audioOutput->SetStretchFactor(audio_stretchfactor);
03305     }
03306 
03307     next_play_speed = audio_stretchfactor;
03308 
03309     if (!InitVideo())
03310     {
03311         VERBOSE(VB_IMPORTANT, "Unable to initialize video.");
03312         if (!using_null_videoout)
03313         {
03314             qApp->lock();
03315             DialogBox *dialog = new DialogBox(
03316                 gContext->GetMainWindow(),
03317                 QObject::tr("Unable to initialize video."));
03318             dialog->AddButton(QObject::tr("Return to menu."));
03319             dialog->exec();
03320             dialog->deleteLater();
03321             qApp->unlock();
03322         }
03323 
03324         if (audioOutput)
03325         {
03326             delete audioOutput;
03327             audioOutput = NULL;
03328         }
03329         no_audio_out = true;
03330         return;
03331     }
03332 
03333     if (!using_null_videoout)
03334     {
03335         QRect visible, total;
03336         float aspect, scaling;
03337 
03338         osd = new OSD();
03339 
03340         videoOutput->GetOSDBounds(total, visible, aspect, scaling, osd->GetThemeAspect());
03341         osd->Init(total, frame_interval, visible, aspect, scaling);
03342 
03343         videoOutput->InitOSD(osd);
03344 
03345         osd->SetCC708Service(&CC708services[1]);
03346     
03347         TeletextViewer *tt_view = GetOSD()->GetTeletextViewer();
03348         if (tt_view)
03349         {
03350             decoder->SetTeletextDecoderViewer(tt_view);
03351             tt_view->SetDisplaying(false);
03352         }
03353         GetOSD()->HideSet("teletext");
03354 
03355         if (GetInteractiveTV())
03356             GetInteractiveTV()->Reinit(total);
03357     }
03358 
03359     playing = true;
03360 
03361     rewindtime = fftime = 0;
03362     skipcommercials = 0;
03363 
03364     ClearAfterSeek();
03365 
03366     /* This thread will fill the video and audio buffers, it does all CPU
03367        intensive operations. We fork two other threads which do nothing but
03368        write to the audio and video output devices.  These should use a
03369        minimum of CPU. */
03370 
03371     pthread_t output_video, decoder_thread;
03372 
03373     decoder_thread = pthread_self();
03374     pthread_create(&output_video, NULL, kickoffOutputVideoLoop, this);
03375 
03376 
03377     if (!using_null_videoout && !ringBuffer->isDVD())
03378     {
03379         // Request that the video output thread run with realtime priority.
03380         // If mythyv/mythfrontend was installed SUID root, this will work.
03381 #ifndef CONFIG_DARWIN
03382         gContext->addPrivRequest(MythPrivRequest::MythRealtime, &output_video);
03383 #endif
03384 
03385         // Use realtime prio for decoder thread as well
03386         //gContext->addPrivRequest(MythPrivRequest::MythRealtime, &decoder_thread);
03387     }
03388 
03389     if (bookmarkseek > 30)
03390     {
03391         GetFrame(audioOutput == NULL || !normal_speed);
03392 
03393         bool seeks = exactseeks;
03394 
03395         GetDecoder()->setExactSeeks(false);
03396 
03397         fftime = bookmarkseek;
03398         if (ringBuffer->isDVD())
03399             GetDVDBookmark();
03400         DoFastForward();
03401         fftime = 0;
03402 
03403         GetDecoder()->setExactSeeks(seeks);
03404 
03405         if (gContext->GetNumSetting("ClearSavedPosition", 1))
03406         {
03407             if (ringBuffer->isDVD())
03408                 SetDVDBookmark(0);
03409             else
03410                 m_playbackinfo->SetBookmark(0);
03411         }
03412     }
03413 
03414     commBreakMapLock.lock();
03415     LoadCommBreakList();
03416     if (!commBreakMap.isEmpty())
03417     {
03418         hascommbreaktable = true;
03419         SetCommBreakIter();
03420     }
03421     commBreakMapLock.unlock();
03422 
03423     if (isDummy)
03424     {
03425         DoPause();
03426     }
03427 
03428     while (!killplayer && !errored)
03429     {
03430         if (m_playbackinfo)
03431             m_playbackinfo->UpdateInUseMark();
03432 
03433         if (isDummy && livetvchain && livetvchain->HasNext())
03434         {
03435             livetvchain->JumpToNext(true, 1);
03436             JumpToProgram();
03437         }
03438         else if ((!paused || eof) && livetvchain && !GetDecoder()->GetWaitForChange())
03439         {
03440             if (livetvchain->NeedsToSwitch())
03441                 SwitchToProgram();
03442         }
03443 
03444         if (livetvchain && livetvchain->NeedsToJump() && 
03445             !GetDecoder()->GetWaitForChange())
03446         {
03447             JumpToProgram();
03448         }
03449 
03450         if (forcePositionMapSync)
03451         {
03452             forcePositionMapSync = false;
03453             GetDecoder()->SyncPositionMap();
03454         }
03455 
03456         if (IsErrored() || (nvr_enc && nvr_enc->GetErrorStatus()))
03457         {
03458             VERBOSE(VB_IMPORTANT, LOC_ERR + "Unknown error, exiting decoder");
03459             errored = killplayer = true;
03460             break;
03461         }
03462 
03463         if (play_speed != next_play_speed && 
03464             (!livetvchain || (livetvchain && !livetvchain->NeedsToJump())))
03465         {
03466             decoder_lock.lock();
03467 
03468             play_speed = next_play_speed;
03469             normal_speed = next_normal_speed;
03470             VERBOSE(VB_PLAYBACK, LOC + "Changing speed to " << play_speed);
03471 
03472             if (play_speed == 0.0)
03473             {
03474                 DoPause();
03475                 decoderThreadPaused.wakeAll();
03476             }
03477             else
03478             {
03479                 ringBuffer->UpdatePlaySpeed(play_speed);
03480                 DoPlay();
03481             }
03482 
03483             decoder_lock.unlock();
03484             continue;
03485         }
03486 
03487         if (eof)
03488         {
03489             if (livetvchain)
03490             {
03491                 if (!paused && livetvchain->HasNext())
03492                 {
03493                     VERBOSE(VB_IMPORTANT, "LiveTV forcing JumpTo 1");
03494                     livetvchain->JumpToNext(true, 1);
03495                     continue;
03496                 }
03497                 if (!paused)
03498                     VERBOSE(VB_PLAYBACK, "Ignoring livetv eof in decoder loop");
03499                 usleep(50000);
03500             }
03501             else
03502                 break;
03503         }
03504 
03505         if (isDummy)
03506         {
03507             decoderThreadPaused.wakeAll();
03508             usleep(500);
03509             continue;
03510         }
03511 
03512         if (rewindtime < 0)
03513             rewindtime = 0;
03514         if (fftime < 0)
03515             fftime = 0;
03516 
03517         if (paused)
03518         {
03519             decoderThreadPaused.wakeAll();
03520 
03521             if (rewindtime > 0)
03522             {
03523                 rewindtime = CalcRWTime(rewindtime);
03524                 if (rewindtime > 0)
03525                 {
03526                     DoRewind();
03527 
03528                     GetFrame(audioOutput == NULL || !normal_speed);
03529                     resetvideo = true;
03530                     while (resetvideo)
03531                         usleep(1000);
03532                 }
03533                 rewindtime = 0;
03534             }
03535             else if (fftime > 0)
03536             {
03537                 fftime = CalcMaxFFTime(fftime);
03538                 if (fftime > 0)
03539                 {
03540                     DoFastForward();
03541 
03542                     GetFrame(audioOutput == NULL || !normal_speed);
03543                     resetvideo = true;
03544                     while (resetvideo)
03545                         usleep(1000);
03546                 }
03547                 fftime = 0;
03548             }
03549             else if (need_change_dvd_track)
03550             {
03551                 DoChangeDVDTrack();
03552                 GetFrame(audioOutput == NULL || !normal_speed);
03553                 resetvideo = true;
03554                 while (resetvideo)
03555                     usleep(1000);
03556                 need_change_dvd_track = 0;
03557             }
03558             else if (livetvchain && livetvchain->NeedsToJump())
03559             {
03560                 JumpToProgram();
03561 
03562                 GetFrame(audioOutput == NULL || !normal_speed);
03563                 resetvideo = true;
03564                 while (resetvideo)
03565                     usleep(1000);
03566             }
03567             else
03568             {
03569                 //printf("startplaying waiting for unpause\n");
03570                 usleep(500);
03571             }
03572             continue;
03573         }
03574 
03575         if (rewindtime > 0 && ffrew_skip == 1)
03576         {
03577             rewindtime = CalcRWTime(rewindtime);
03578 
03579             if (rewindtime >= 1)
03580             {
03581                 QMutexLocker locker(&internalPauseLock);
03582 
03583                 PauseVideo(true);
03584                 DoRewind();
03585                 UnpauseVideo(true);
03586             }
03587             rewindtime = 0;
03588         }
03589 
03590         if (fftime > 0 && ffrew_skip == 1)
03591         {
03592             fftime = CalcMaxFFTime(fftime);
03593 
03594             if (fftime >= 5)
03595             {
03596                 QMutexLocker locker(&internalPauseLock);
03597 
03598                 PauseVideo(true);
03599 
03600                 if (fftime >= 5)
03601                     DoFastForward();
03602 
03603                 if (eof)
03604                     continue;
03605 
03606                 UnpauseVideo(true);
03607             }
03608 
03609             fftime = 0;
03610         }
03611 
03612         if (need_change_dvd_track)
03613         {
03614             QMutexLocker locker(&internalPauseLock);
03615 
03616             PauseVideo(true);
03617             DoChangeDVDTrack();
03618             UnpauseVideo(true);
03619 
03620             need_change_dvd_track = 0;
03621         }
03622 
03623         if (skipcommercials != 0 && ffrew_skip == 1)
03624         {
03625             QMutexLocker locker(&internalPauseLock);
03626 
03627             PauseVideo(true);
03628             DoSkipCommercials(skipcommercials);
03629             UnpauseVideo(true);
03630 
03631             skipcommercials = 0;
03632             continue;
03633         }
03634 
03635         GetFrame(audioOutput == NULL || !normal_speed);
03636 
03637         if (using_null_videoout || IsIVTVDecoder())
03638             GetDecoder()->UpdateFramesPlayed();
03639         else
03640             framesPlayed = videoOutput->GetFramesPlayed();
03641 
03642         if (ffrew_skip != 1)
03643             continue;
03644 
03645         if (!hasdeletetable && autocommercialskip)
03646             AutoCommercialSkip();
03647 
03648         if (hasdeletetable && deleteIter.data() == 1 &&
03649             framesPlayed >= deleteIter.key())
03650