00001 #include <iostream>
00002 #include <cassert>
00003 #include <sys/types.h>
00004 #include <sys/stat.h>
00005 #include <fcntl.h>
00006 #include <sys/ioctl.h>
00007 #include <unistd.h>
00008
00009 using namespace std;
00010
00011 #include "mythconfig.h"
00012 #include "ivtvdecoder.h"
00013 #include "RingBuffer.h"
00014 #include "NuppelVideoPlayer.h"
00015 #include "remoteencoder.h"
00016 #include "programinfo.h"
00017 #include "mythcontext.h"
00018 #include "mythdbcon.h"
00019
00020 #include "videoout_ivtv.h"
00021 #include "videodev_myth.h"
00022
00023 #define LOC QString("IVD: ")
00024 #define LOC_ERR QString("IVD Error: ")
00025
00026
00027
00028 DevInfoMap IvtvDecoder::devInfo;
00029 QMutex IvtvDecoder::devInfoLock;
00030 const uint IvtvDecoder::vidmax = 131072;
00031
00032 IvtvDecoder::IvtvDecoder(NuppelVideoPlayer *parent, ProgramInfo *pginfo)
00033 : DecoderBase(parent, pginfo),
00034
00035 validvpts(false), gotvideo(false),
00036 gopset(false), needPlay(false),
00037 mpeg_state(0xffffffff),
00038
00039 prevgoppos(0), firstgoppos(0),
00040
00041 vidbuf(new unsigned char[vidmax]),
00042
00043 vidread(0), vidwrite(0),
00044 vidfull(0), vidframes(0),
00045 frame_decoded(0), videoPlayed(0),
00046 lastStartFrame(0), laststartpos(0),
00047
00048 nexttoqueue(1), lastdequeued(0)
00049 {
00050 lastResetTime.start();
00051 fps = 29.97f;
00052 bitrate = 8000;
00053 lastKey = 0;
00054 }
00055
00056 IvtvDecoder::~IvtvDecoder()
00057 {
00058 if (vidbuf)
00059 delete[] vidbuf;
00060 }
00061
00062 void IvtvDecoder::SeekReset(long long newkey, uint skipframes,
00063 bool needFlush, bool discardFrames)
00064 {
00065 VERBOSE(VB_PLAYBACK, LOC +
00066 QString("SeekReset(%1, %2, %3 flush, %4 discard)")
00067 .arg(newkey).arg(skipframes)
00068 .arg((needFlush) ? "do" : "don't")
00069 .arg((discardFrames) ? "do" : "don't"));
00070
00071 DecoderBase::SeekReset(newkey, skipframes, needFlush, discardFrames);
00072
00073 skipframes = (exactseeks) ? skipframes : 0;
00074 vidread = vidwrite = vidfull = 0;
00075 mpeg_state = 0xffffffff;
00076 ateof = false;
00077
00078 framesRead = newkey;
00079 framesPlayed = newkey;
00080
00081 VideoOutput *vo = GetNVP()->getVideoOutput();
00082 VideoOutputIvtv *videoout = (VideoOutputIvtv*) vo;
00083
00084 if (needFlush)
00085 {
00086 needPlay = true;
00087 lastStartFrame = newkey;
00088 nexttoqueue = 1;
00089 lastdequeued = 0;
00090 vidframes = 0;
00091 queuedlist.clear();
00092
00093 videoout->Stop(false );
00094 videoout->Flush();
00095
00096 videoout->Start(0, skipframes + 5 );
00097
00098 if (GetNVP()->GetFFRewSkip() != 1)
00099 videoout->Play();
00100 else
00101 {
00102 if (GetNVP()->IsPaused())
00103 {
00104 videoout->Pause();
00105 do
00106 ReadWrite(1);
00107 while (videoout->GetFramesPlayed() < 1 && !ateof);
00108 StepFrames(newkey, skipframes);
00109 }
00110 else
00111 {
00112 videoout->Play();
00113 bool done = false;
00114 while (!done)
00115 {
00116 ReadWrite(1);
00117 done = ateof;
00118 done |= (uint)videoout->GetFramesPlayed() > skipframes;
00119 done |= !skipframes;
00120 }
00121 }
00122 }
00123
00124
00125
00126 framesPlayed = newkey + skipframes;
00127 videoPlayed = framesPlayed+1;
00128 }
00129 else
00130 {
00131
00132
00133
00134 if (needPlay && videoout->GetFramesPlayed())
00135 {
00136 videoout->Play();
00137 needPlay = false;
00138 }
00139 }
00140 }
00141
00142 bool IvtvDecoder::GetDeviceWorks(QString dev)
00143 {
00144 QMutexLocker locker(&devInfoLock);
00145 DevInfoMap::const_iterator it = devInfo.find(dev);
00146 if (it != devInfo.end())
00147 return (*it).works;
00148 return false;
00149 }
00150
00151 bool IvtvDecoder::GetDeviceNTSC(QString dev)
00152 {
00153 QMutexLocker locker(&devInfoLock);
00154 DevInfoMap::const_iterator it = devInfo.find(dev);
00155 if (it != devInfo.end())
00156 return (*it).ntsc;
00157 return true;
00158 }
00159
00160 void IvtvDecoder::SetDeviceInfo(QString dev, bool works, bool ntsc)
00161 {
00162 QString tmpStr = QDeepCopy<QString>(dev);
00163 DeviceInfo tmp;
00164 tmp.works = works;
00165 tmp.ntsc = ntsc;
00166
00167 QMutexLocker locker(&devInfoLock);
00168 devInfo[tmpStr] = tmp;
00169 }
00170
00177 bool IvtvDecoder::CheckDevice(void)
00178 {
00179 if (!gContext->GetNumSetting("PVR350OutputEnable", 0))
00180 return false;
00181
00182 QString videodev = gContext->GetSetting("PVR350VideoDev");
00183
00184 if (GetDeviceWorks(videodev))
00185 return true;
00186
00187 int testfd = open(videodev.ascii(), O_RDWR);
00188 if (testfd < 0)
00189 return false;
00190
00191 bool ok = false;
00192
00193 struct v4l2_capability vcap;
00194 bzero(&vcap, sizeof(vcap));
00195 if (ioctl(testfd, VIDIOC_QUERYCAP, &vcap) < 0)
00196 {
00197 VERBOSE(VB_IMPORTANT, LOC_ERR +
00198 QString("Failed to query capabilities of '%1'")
00199 .arg(videodev) + ENO);
00200 }
00201 else
00202 {
00203 if (vcap.version < 0x00000109)
00204 VERBOSE(VB_IMPORTANT, QString("IVTV driver is version %1, "
00205 "version %2 (or later) is required")
00206 .arg(vcap.version, 0, 16)
00207 .arg(0x00000109, 0, 16));
00208 else if (vcap.capabilities & V4L2_CAP_VIDEO_OUTPUT)
00209 ok = true;
00210 }
00211
00212 v4l2_std_id std = V4L2_STD_NTSC;
00213 bool ntsc = true;
00214
00215 if (ioctl(testfd, VIDIOC_G_STD, &std) < 0)
00216 {
00217 VERBOSE(VB_IMPORTANT, LOC_ERR +
00218 QString("Failed to determine video standard used by '%1'.")
00219 .arg(videodev) + ENO);
00220 }
00221 else
00222 {
00223 if (std & V4L2_STD_625_50)
00224 ntsc = false;
00225 }
00226
00227 close(testfd);
00228
00229 if (!ok)
00230 return false;
00231
00232 SetDeviceInfo(videodev, true, ntsc);
00233 return true;
00234 }
00235
00236 bool IvtvDecoder::CanHandle(char testbuf[kDecoderProbeBufferSize],
00237 const QString &filename, int testbufsize)
00238 {
00239 if (!CheckDevice())
00240 return false;
00241
00242 avcodeclock.lock();
00243 av_register_all();
00244 avcodeclock.unlock();
00245
00246 AVProbeData probe;
00247
00248 probe.filename = (char *)(filename.ascii());
00249 probe.buf = (unsigned char *)testbuf;
00250 probe.buf_size = testbufsize;
00251
00252 AVInputFormat *fmt = av_probe_input_format(&probe, true);
00253
00254 if (!strcmp(fmt->name, "mpeg"))
00255 return true;
00256 return false;
00257 }
00258
00259 int IvtvDecoder::OpenFile(RingBuffer *rbuffer, bool novideo,
00260 char testbuf[kDecoderProbeBufferSize],
00261 int)
00262 {
00263 (void)novideo;
00264 (void)testbuf;
00265
00266 ringBuffer = rbuffer;
00267
00268 keyframedist = -1;
00269 positionMapType = MARK_UNSET;
00270
00271 QString videodev = gContext->GetSetting("PVR350VideoDev");
00272 bool ntsc = GetDeviceNTSC(videodev);
00273
00274 GetNVP()->SetVideoParams(720 , (ntsc) ? 480 : 576 ,
00275 (ntsc) ? 29.97f : 25.0f, keyframedist, 1.33);
00276
00277 fps = (ntsc) ? 29.97f : 25.0f;
00278
00279 ringBuffer->UpdateRawBitrate(GetRawBitrate());
00280
00281 if (m_playbackinfo || livetv || watchingrecording)
00282 {
00283 recordingHasPositionMap = SyncPositionMap();
00284 if (recordingHasPositionMap && !livetv && !watchingrecording)
00285 {
00286 hasFullPositionMap = true;
00287 gopset = true;
00288 }
00289 }
00290
00291 if (!recordingHasPositionMap)
00292 {
00293 int bitrate = 8000;
00294 float bytespersec = (float)bitrate / 8 / 2;
00295 float secs = ringBuffer->GetRealFileSize() * 1.0 / bytespersec;
00296
00297 GetNVP()->SetFileLength((int)(secs), (int)(secs * fps));
00298 }
00299
00300 if (hasFullPositionMap)
00301 {
00302 VERBOSE(VB_PLAYBACK, "Position map found");
00303 }
00304 else if (recordingHasPositionMap)
00305 {
00306 VERBOSE(VB_PLAYBACK, "Partial position map found");
00307 }
00308
00309 return hasFullPositionMap;
00310 }
00311
00312 #define VID_START 0x000001e0
00313 #define VID_END 0x000001ef
00314
00315 #define SEQ_START 0x000001b3
00316 #define GOP_START 0x000001b8
00317 #define PICTURE_START 0x00000100
00318 #define SLICE_MIN 0x00000101
00319 #define SLICE_MAX 0x000001af
00320
00321 int IvtvDecoder::MpegPreProcessPkt(unsigned char *buf, int len,
00322 long long startpos, long stopframe)
00323 {
00324 unsigned char *bufptr = buf;
00325 unsigned int v = 0;
00326
00327 while (bufptr < buf + len)
00328 {
00329 v = *bufptr++;
00330
00331 if (mpeg_state == 0x000001)
00332 {
00333 mpeg_state = ((mpeg_state << 8) | v) & 0xFFFFFF;
00334 if (mpeg_state >= SLICE_MIN && mpeg_state <= SLICE_MAX)
00335 continue;
00336
00337 if (mpeg_state >= VID_START && mpeg_state <= VID_END)
00338 {
00339 laststartpos = (bufptr - buf) + startpos - 4;
00340 continue;
00341 }
00342
00343 switch (mpeg_state)
00344 {
00345 case GOP_START:
00346 {
00347 int frameNum = framesRead;
00348
00349 if (!gopset && frameNum > 0)
00350 {
00351 if ((firstgoppos > 0) && (keyframedist != 1))
00352 {
00353 keyframedist = frameNum - firstgoppos;
00354
00355 if ((keyframedist == 15) ||
00356 (keyframedist == 12))
00357 positionMapType = MARK_GOP_START;
00358 else
00359 positionMapType = MARK_GOP_BYFRAME;
00360
00361 gopset = true;
00362 GetNVP()->SetKeyframeDistance(keyframedist);
00363 VERBOSE(VB_PLAYBACK,
00364 QString("keyframedist changed to %1")
00365 .arg(keyframedist));
00366 }
00367 else
00368 firstgoppos = frameNum;
00369 }
00370
00371 lastKey = frameNum;
00372 if (!hasFullPositionMap)
00373 {
00374 long long last_frame = 0;
00375 if (!m_positionMap.empty())
00376 last_frame =
00377 m_positionMap[m_positionMap.size() - 1].index;
00378 if (keyframedist > 1)
00379 last_frame *= keyframedist;
00380 if (framesRead > last_frame && keyframedist > 0)
00381 {
00382 if (m_positionMap.capacity() ==
00383 m_positionMap.size())
00384 m_positionMap.reserve(m_positionMap.size() + 60);
00385 PosMapEntry entry = {lastKey / keyframedist,
00386 lastKey, laststartpos};
00387 m_positionMap.push_back(entry);
00388 }
00389
00390 if ((framesRead > 150) &&
00391 (!recordingHasPositionMap) &&
00392 (!livetv))
00393 {
00394 int bitrate = (int)((laststartpos * 8 * fps) /
00395 (framesRead - 1));
00396 float bytespersec = (float)bitrate / 8;
00397 float secs = ringBuffer->GetRealFileSize() * 1.0 /
00398 bytespersec;
00399 GetNVP()->SetFileLength((int)(secs),
00400 (int)(secs * fps));
00401 }
00402
00403 }
00404
00405 break;
00406 }
00407 case PICTURE_START:
00408 {
00409 if (bufptr + 1 >= buf + len)
00410 VERBOSE(VB_IMPORTANT, "ivtv picture start overflow, "
00411 "please inform mythtv-dev@mythtv.org");
00412 int type = (bufptr[1] >> 3) & 7;
00413 if (type >= 1 && type <= 3)
00414 {
00415 if ((long)framesRead < stopframe)
00416 {
00417 queuedlist.push_back(IvtvQueuedFrame(++vidframes,
00418 ++framesRead));
00419
00420
00421 }
00422 else
00423 {
00424 ++framesRead;
00425
00426
00427 int res = bufptr - buf - 4;
00428 if (res < 0)
00429 {
00430 VERBOSE(VB_IMPORTANT,
00431 "ivtv picture start underflow, "
00432 "please inform mythtv-dev@mythtv.org");
00433 res = 0;
00434 }
00435 return res;
00436 }
00437 }
00438 break;
00439 }
00440 default:
00441 break;
00442 }
00443 continue;
00444 }
00445 mpeg_state = ((mpeg_state << 8) | v) & 0xFFFFFF;
00446 }
00447
00448 return bufptr - buf;
00449 }
00450
00451 bool IvtvDecoder::ReadWrite(int onlyvideo, long stopframe)
00452 {
00453 if (ateof)
00454 return false;
00455
00456 gotvideo = false;
00457 frame_decoded = 0;
00458
00459 int count, total = 0;
00460 bool canwrite = false;
00461
00462 VideoOutputIvtv *videoout = (VideoOutputIvtv*) GetNVP()->getVideoOutput();
00463
00464 if (onlyvideo < 0)
00465 {
00466 vidread = vidwrite = vidfull = 0;
00467 }
00468 else if (vidfull || vidread != vidwrite)
00469 {
00470 int ret = videoout->Poll(50);
00471
00472 if (ret==0)
00473 VERBOSE(VB_PLAYBACK, LOC + "write0 !canwrite");
00474
00475 canwrite = ret > 0;
00476 if (canwrite && vidwrite >= vidread)
00477 {
00478 count = vidmax - vidwrite;
00479 count = videoout->WriteBuffer(&vidbuf[vidwrite], count);
00480 if (count < 0)
00481 {
00482 ateof = true;
00483 VERBOSE(VB_PLAYBACK, LOC + "write1 ateof");
00484 }
00485 else if (count > 0)
00486 {
00487 vidwrite = (vidwrite + count) & (vidmax - 1);
00488 vidfull = 0;
00489 total += count;
00490 #ifdef EXTRA_DEBUG
00491 VERBOSE(VB_PLAYBACK, LOC +
00492 QString("write1 cnt(%1) rd(%2) wr(%3) full(%4)")
00493 .arg(count, 5).arg(vidread, 5).arg(vidwrite, 5)
00494 .arg(vidfull));
00495 #endif // EXTRA_DEBUG
00496 }
00497 }
00498
00499 if (canwrite && vidwrite < vidread)
00500 {
00501 count = vidread - vidwrite;
00502 count = videoout->WriteBuffer(&vidbuf[vidwrite], count);
00503 if (count < 0)
00504 {
00505 ateof = true;
00506 VERBOSE(VB_PLAYBACK, LOC + "write2 ateof");
00507 }
00508 else if (count > 0)
00509 {
00510 vidwrite = (vidwrite + count) & (vidmax - 1);
00511 vidfull = 0;
00512 total += count;
00513 #ifdef EXTRA_DEBUG
00514 VERBOSE(VB_PLAYBACK, LOC +
00515 QString("write2 cnt(%1) rd(%2) wr(%3) full(%4)")
00516 .arg(count, 5).arg(vidread, 5).arg(vidwrite, 5)
00517 .arg(vidfull));
00518 #endif // EXTRA_DEBUG
00519 }
00520 }
00521 }
00522
00523 int size = 0;
00524 if ((long)framesRead <= stopframe && (!vidfull || vidread != vidwrite))
00525 {
00526 long long startpos = ringBuffer->GetReadPosition();
00527 if (waitingForChange)
00528 {
00529 #ifdef EXTRA_DEBUG
00530 VERBOSE(VB_PLAYBACK,
00531 "startpos: "<<startpos
00532 <<" readAdjust: "<<readAdjust);
00533 #endif // EXTRA_DEBUG
00534
00535 if (startpos + 4 >= readAdjust)
00536 {
00537 FileChanged();
00538 startpos = ringBuffer->GetReadPosition();
00539 }
00540 }
00541
00542 if (vidread >= vidwrite)
00543 {
00544 size = vidmax - vidread;
00545 count = ringBuffer->Read(&vidbuf[vidread], size);
00546 if (count > 0)
00547 {
00548 count = MpegPreProcessPkt(&vidbuf[vidread], count,
00549 startpos, stopframe);
00550 vidread = (vidread + count) & (vidmax - 1);
00551 vidfull = (vidread == vidwrite);
00552 total += count;
00553 #ifdef EXTRA_DEBUG
00554 VERBOSE(VB_PLAYBACK, LOC +
00555 QString("read1 cnt(%1) rd(%2) wr(%3) full(%4)")
00556 .arg(count, 5).arg(vidread, 5).arg(vidwrite, 5)
00557 .arg(vidfull));
00558 #endif // EXTRA_DEBUG
00559 }
00560 }
00561
00562 if (vidread < vidwrite)
00563 {
00564 size = vidwrite - vidread;
00565 count = ringBuffer->Read(&vidbuf[vidread], size);
00566 if (count > 0)
00567 {
00568 count = MpegPreProcessPkt(&vidbuf[vidread], count,
00569 startpos, stopframe);
00570 vidread = (vidread + count) & (vidmax - 1);
00571 vidfull = (vidread == vidwrite);
00572 total += count;
00573 #ifdef EXTRA_DEBUG
00574 VERBOSE(VB_PLAYBACK, LOC +
00575 QString("read2 cnt(%1) rd(%2) wr(%3) full(%4)")
00576 .arg(count, 5).arg(vidread, 5).arg(vidwrite, 5)
00577 .arg(vidfull));
00578 #endif // EXTRA_DEBUG
00579 }
00580 }
00581
00582 if (total == 0 && (vidread != vidwrite) && canwrite)
00583 {
00584 ateof = true;
00585 #ifdef EXTRA_DEBUG
00586 VERBOSE(VB_PLAYBACK, LOC +
00587 QString("read3 cnt(%1) rd(%2) wr(%3) full(%4) size(%5)")
00588 .arg(count, 5).arg(vidread, 5).arg(vidwrite, 5)
00589 .arg(vidfull).arg(size) + " ateof");
00590 #endif // EXTRA_DEBUG
00591 }
00592
00593 bool was_set = needReset;
00594 needReset = !total && !canwrite;
00595 if (needReset && !was_set)
00596 needResetTimer.start();
00597 }
00598
00599 #ifdef EXTRA_DEBUG
00600 if (needReset)
00601 {
00602 VERBOSE(VB_PLAYBACK, LOC + "needReset "<<needResetTimer.elapsed()
00603 <<" livetv("<<(bool)GetNVP()->GetTVChain()<<")");
00604 usleep(50000);
00605 }
00606 #endif // EXTRA_DEBUG
00607
00608
00609
00610 ateof |= needReset && (size > 0) && !(bool)GetNVP()->GetTVChain();
00611
00612 if (needReset && needResetTimer.elapsed() > 1000)
00613 {
00614
00615 if (lastResetTime.elapsed() < 1200)
00616 {
00617 VERBOSE(VB_IMPORTANT, LOC + "RESET -- aborted "
00618 <<lastResetTime.elapsed());
00619 ateof = true;
00620 return false;
00621 }
00622 needReset = false;
00623 lastResetTime.start();
00624 VERBOSE(VB_IMPORTANT, LOC + "*********************************");
00625 VERBOSE(VB_IMPORTANT, LOC + "RESET -- begin");
00626 lastdequeued = 0;
00627 vidframes = 0;
00628 queuedlist.clear();
00629 videoout->Stop(false );
00630 videoout->Flush();
00631 videoout->Start(0 , 0 );
00632 videoout->Play();
00633 VERBOSE(VB_IMPORTANT, LOC + "RESET -- end");
00634 VERBOSE(VB_IMPORTANT, LOC + "*********************************");
00635 }
00636
00637 if ((long)framesRead <= stopframe)
00638 return (total > 0);
00639 else
00640 return (vidfull || vidread != vidwrite);
00641 }
00642
00643 bool IvtvDecoder::GetFrame(int onlyvideo)
00644 {
00645 long long last_read = framesRead;
00646
00647 if (GetNVP()->GetFFRewSkip() == 1 || onlyvideo < 0)
00648 ReadWrite(onlyvideo, LONG_MAX);
00649 else
00650 {
00651 long stopframe = framesRead + 1;
00652 while (ReadWrite(onlyvideo, stopframe))
00653 ;
00654 }
00655
00656 if (ateof && !GetNVP()->GetEditMode())
00657 {
00658 VERBOSE(VB_PLAYBACK, LOC + QString("NVP::SetEof() at frame %1")
00659 .arg(framesRead));
00660 GetNVP()->SetEof();
00661 }
00662
00663 framesPlayed = framesRead - 1;
00664
00665 return (framesRead != last_read);
00666 }
00667
00668 bool IvtvDecoder::DoFastForward(long long desiredFrame, bool doflush)
00669 {
00670 long long number = desiredFrame - videoPlayed;
00671
00672 if (GetNVP()->IsPaused() && number < keyframedist)
00673 {
00674 StepFrames(videoPlayed, number+1);
00675 framesPlayed = desiredFrame + 1;
00676 videoPlayed = framesPlayed;
00677 GetNVP()->SetFramesPlayed(videoPlayed);
00678 return !ateof;
00679 }
00680
00681 return DecoderBase::DoFastForward(desiredFrame, doflush);
00682 }
00683
00684 void IvtvDecoder::UpdateFramesPlayed(void)
00685 {
00686 VideoOutputIvtv *videoout = (VideoOutputIvtv*)(GetNVP()->getVideoOutput());
00687
00688 int rawframes = videoout->GetFramesPlayed();
00689
00690 if (rawframes < lastdequeued)
00691 {
00692 VERBOSE(VB_IMPORTANT, QString("IVTV rawframes decreased! "
00693 "Did the decoder reset?"));
00694 }
00695 else
00696 {
00697 while (rawframes != lastdequeued)
00698 {
00699 if (queuedlist.empty())
00700 {
00701 VERBOSE(VB_IMPORTANT, QString("IVTV framelist is empty!"));
00702 break;
00703 }
00704 lastdequeued = queuedlist.front().raw;
00705 videoPlayed = queuedlist.front().actual;
00706 queuedlist.pop_front();
00707
00708
00709
00710 }
00711 }
00712
00713 GetNVP()->SetFramesPlayed(videoPlayed);
00714 }
00715
00716 bool IvtvDecoder::StepFrames(long long start, long long count)
00717 {
00718 VideoOutputIvtv *videoout = (VideoOutputIvtv*)(GetNVP()->getVideoOutput());
00719
00720 long long step, cur = 0, last = start;
00721
00722
00723
00724 for (step = 0; step < count && !ateof; step++)
00725 {
00726 while (ReadWrite(1))
00727 ;
00728
00729
00730 videoout->Step();
00731 usleep(1000);
00732
00733 int tries;
00734 const int maxtries = 500;
00735 const int restep = 25;
00736
00737 for (tries = 0; tries < maxtries && !ateof; tries++)
00738 {
00739 while (ReadWrite(1))
00740 ;
00741
00742 cur = lastStartFrame + videoout->GetFramesPlayed();
00743 if (cur > last)
00744 break;
00745
00746 if (tries && !(tries % restep))
00747 {
00748 videoout->Pause();
00749 VERBOSE(VB_IMPORTANT, QString(" extra step %1 at %2")
00750 .arg(step)
00751 .arg(last));
00752 videoout->Step();
00753 usleep(1000);
00754 }
00755 }
00756
00757 videoout->Pause();
00758
00759
00760
00761 if (tries >= maxtries)
00762 {
00763 VERBOSE(VB_IMPORTANT, QString("IvtvDecoder timed out while "
00764 "stepping, giving up"));
00765 break;
00766 }
00767
00768 last = cur;
00769 }
00770
00771
00772
00773 return true;
00774 }
00775