|
MythTV
0.26-pre
|
00001 // ANSI C headers 00002 #include <cmath> 00003 #include <cstdio> 00004 #include <cstdlib> 00005 #include <cerrno> 00006 00007 // POSIX C headers 00008 #include <sys/types.h> 00009 #include <sys/time.h> 00010 #include <unistd.h> 00011 #include <fcntl.h> 00012 00013 // Qt headers 00014 #include <QFile> 00015 #include <QDateTime> 00016 00017 #include "ThreadedFileWriter.h" 00018 #include "fileringbuffer.h" 00019 #include "streamingringbuffer.h" 00020 #include "livetvchain.h" 00021 #include "mythcontext.h" 00022 #include "ringbuffer.h" 00023 #include "mythconfig.h" 00024 #include "remotefile.h" 00025 #include "compat.h" 00026 #include "mythmiscutil.h" 00027 #include "mythlogging.h" 00028 #include "DVD/dvdringbuffer.h" 00029 #include "Bluray/bdringbuffer.h" 00030 #include "HLS/httplivestreambuffer.h" 00031 00032 // about one second at 35mbit 00033 #define BUFFER_SIZE_MINIMUM 4 * 1024 * 1024 00034 #define BUFFER_FACTOR_NETWORK 2 00035 #define BUFFER_FACTOR_BITRATE 2 00036 #define BUFFER_FACTOR_MATROSKA 2 00037 00038 const int RingBuffer::kDefaultOpenTimeout = 2000; // ms 00039 const int RingBuffer::kLiveTVOpenTimeout = 10000; 00040 00041 #define CHUNK 32768 /* readblocksize increments */ 00042 00043 #define LOC QString("RingBuf(%1): ").arg(filename) 00044 00045 QMutex RingBuffer::subExtLock; 00046 QStringList RingBuffer::subExt; 00047 QStringList RingBuffer::subExtNoCheck; 00048 00049 /* 00050 Locking relations: 00051 rwlock->poslock->rbrlock->rbwlock 00052 00053 A child should never lock any of the parents without locking 00054 the parent lock before the child lock. 00055 void RingBuffer::Example1() 00056 { 00057 poslock.lockForWrite(); 00058 rwlock.lockForRead(); // error! 00059 blah(); // <- does not implicitly aquire any locks 00060 rwlock.unlock(); 00061 poslock.unlock(); 00062 } 00063 void RingBuffer::Example2() 00064 { 00065 rwlock.lockForRead(); 00066 rbrlock.lockForWrite(); // ok! 00067 blah(); // <- does not implicitly aquire any locks 00068 rbrlock.unlock(); 00069 rwlock.unlock(); 00070 } 00071 */ 00072 00099 RingBuffer *RingBuffer::Create( 00100 const QString &xfilename, bool write, 00101 bool usereadahead, int timeout_ms, bool stream_only) 00102 { 00103 QString lfilename = xfilename; 00104 QString lower = lfilename.toLower(); 00105 00106 if (write) 00107 return new FileRingBuffer(lfilename, write, usereadahead, timeout_ms); 00108 00109 bool dvddir = false; 00110 bool bddir = false; 00111 bool httpurl = lower.startsWith("http://") || lower.startsWith("https://"); 00112 bool mythurl = lower.startsWith("myth://"); 00113 bool bdurl = lower.startsWith("bd:"); 00114 bool dvdurl = lower.startsWith("dvd:"); 00115 bool dvdext = lower.endsWith(".img") || lower.endsWith(".iso"); 00116 00117 if (httpurl) 00118 { 00119 if (HLSRingBuffer::TestForHTTPLiveStreaming(lfilename)) 00120 { 00121 return new HLSRingBuffer(lfilename); 00122 } 00123 return new StreamingRingBuffer(lfilename); 00124 } 00125 if (!stream_only && mythurl) 00126 { 00127 struct stat fileInfo; 00128 if ((RemoteFile::Exists(lfilename, &fileInfo)) && 00129 (S_ISDIR(fileInfo.st_mode))) 00130 { 00131 if (RemoteFile::Exists(lfilename + "/VIDEO_TS")) 00132 dvddir = true; 00133 else if (RemoteFile::Exists(lfilename + "/BDMV")) 00134 bddir = true; 00135 } 00136 } 00137 else if (!stream_only && !mythurl) 00138 { 00139 if (QFile::exists(lfilename + "/VIDEO_TS")) 00140 dvddir = true; 00141 else if (QFile::exists(lfilename + "/BDMV")) 00142 bddir = true; 00143 } 00144 00145 if (!stream_only && (dvdurl || dvddir || dvdext)) 00146 { 00147 if (lfilename.left(4) == "dvd:") // URI "dvd:" + path 00148 lfilename.remove(0,4); // e.g. "dvd:/dev/dvd" 00149 00150 if (!(mythurl || QFile::exists(lfilename))) 00151 lfilename = "/dev/dvd"; 00152 LOG(VB_PLAYBACK, LOG_INFO, "Trying DVD at " + lfilename); 00153 00154 return new DVDRingBuffer(lfilename); 00155 } 00156 else if (!stream_only && (bdurl || bddir)) 00157 { 00158 if (lfilename.left(3) == "bd:") // URI "bd:" + path 00159 lfilename.remove(0,3); // e.g. "bd:/videos/ET" 00160 00161 if (!(mythurl || QFile::exists(lfilename))) 00162 lfilename = "/dev/dvd"; 00163 LOG(VB_PLAYBACK, LOG_INFO, "Trying BD at " + lfilename); 00164 00165 return new BDRingBuffer(lfilename); 00166 } 00167 00168 return new FileRingBuffer( 00169 lfilename, write, usereadahead, timeout_ms); 00170 } 00171 00172 RingBuffer::RingBuffer(RingBufferType rbtype) : 00173 MThread("RingBuffer"), 00174 type(rbtype), 00175 readpos(0), writepos(0), 00176 internalreadpos(0), ignorereadpos(-1), 00177 rbrpos(0), rbwpos(0), 00178 stopreads(false), safefilename(QString()), 00179 filename(), subtitlefilename(), 00180 tfw(NULL), fd2(-1), 00181 writemode(false), remotefile(NULL), 00182 bufferSize(BUFFER_SIZE_MINIMUM), 00183 low_buffers(false), 00184 fileismatroska(false), unknownbitrate(false), 00185 startreadahead(false), readAheadBuffer(NULL), 00186 readaheadrunning(false), reallyrunning(false), 00187 request_pause(false), paused(false), 00188 ateof(false), readsallowed(false), 00189 setswitchtonext(false), 00190 rawbitrate(8000), playspeed(1.0f), 00191 fill_threshold(65536), fill_min(-1), 00192 readblocksize(CHUNK), wanttoread(0), 00193 numfailures(0), commserror(false), 00194 oldfile(false), livetvchain(NULL), 00195 ignoreliveeof(false), readAdjust(0), 00196 bitrateMonitorEnabled(false) 00197 { 00198 { 00199 QMutexLocker locker(&subExtLock); 00200 if (subExt.empty()) 00201 { 00202 // Possible subtitle file extensions '.srt', '.sub', '.txt' 00203 subExt += ".srt"; 00204 subExt += ".sub"; 00205 subExt += ".txt"; 00206 00207 // Extensions for which a subtitle file should not exist 00208 subExtNoCheck = subExt; 00209 subExtNoCheck += ".gif"; 00210 subExtNoCheck += ".png"; 00211 } 00212 } 00213 } 00214 00218 RingBuffer::~RingBuffer(void) 00219 { 00220 KillReadAheadThread(); 00221 00222 rwlock.lockForWrite(); 00223 00224 if (readAheadBuffer) // this only runs if thread is terminated 00225 { 00226 delete [] readAheadBuffer; 00227 readAheadBuffer = NULL; 00228 } 00229 00230 if (tfw) 00231 { 00232 tfw->Flush(); 00233 delete tfw; 00234 tfw = NULL; 00235 } 00236 00237 rwlock.unlock(); 00238 00239 wait(); 00240 } 00241 00245 void RingBuffer::Reset(bool full, bool toAdjust, bool resetInternal) 00246 { 00247 LOG(VB_FILE, LOG_INFO, LOC + QString("Reset(%1,%2,%3)") 00248 .arg(full).arg(toAdjust).arg(resetInternal)); 00249 00250 rwlock.lockForWrite(); 00251 poslock.lockForWrite(); 00252 00253 numfailures = 0; 00254 commserror = false; 00255 setswitchtonext = false; 00256 00257 writepos = 0; 00258 readpos = (toAdjust) ? (readpos - readAdjust) : 0; 00259 00260 if (readpos != 0) 00261 { 00262 LOG(VB_GENERAL, LOG_ERR, LOC + 00263 QString("RingBuffer::Reset() nonzero readpos. toAdjust: %1 " 00264 "readpos: %2 readAdjust: %3") 00265 .arg(toAdjust).arg(readpos).arg(readAdjust)); 00266 } 00267 00268 readAdjust = 0; 00269 readpos = (readpos < 0) ? 0 : readpos; 00270 00271 if (full) 00272 ResetReadAhead(readpos); 00273 00274 if (resetInternal) 00275 internalreadpos = readpos; 00276 00277 generalWait.wakeAll(); 00278 poslock.unlock(); 00279 rwlock.unlock(); 00280 } 00281 00287 void RingBuffer::UpdateRawBitrate(uint raw_bitrate) 00288 { 00289 LOG(VB_FILE, LOG_INFO, LOC + 00290 QString("UpdateRawBitrate(%1Kb)").arg(raw_bitrate)); 00291 00292 // an audio only stream could be as low as 64Kb (DVB radio) and 00293 // an MHEG only stream is likely to be reported as 0Kb 00294 if (raw_bitrate < 64) 00295 { 00296 LOG(VB_FILE, LOG_INFO, LOC + 00297 QString("Bitrate too low - setting to 64Kb")); 00298 raw_bitrate = 64; 00299 } 00300 00301 rwlock.lockForWrite(); 00302 rawbitrate = raw_bitrate; 00303 CalcReadAheadThresh(); 00304 rwlock.unlock(); 00305 } 00306 00311 void RingBuffer::UpdatePlaySpeed(float play_speed) 00312 { 00313 rwlock.lockForWrite(); 00314 playspeed = play_speed; 00315 CalcReadAheadThresh(); 00316 rwlock.unlock(); 00317 } 00318 00324 void RingBuffer::SetBufferSizeFactors(bool estbitrate, bool matroska) 00325 { 00326 rwlock.lockForWrite(); 00327 unknownbitrate = estbitrate; 00328 fileismatroska = matroska; 00329 rwlock.unlock(); 00330 CreateReadAheadBuffer(); 00331 } 00332 00340 void RingBuffer::CalcReadAheadThresh(void) 00341 { 00342 uint estbitrate = 0; 00343 00344 readsallowed = false; 00345 readblocksize = max(readblocksize, CHUNK); 00346 00347 // loop without sleeping if the buffered data is less than this 00348 fill_threshold = 7 * bufferSize / 8; 00349 00350 const uint KB2 = 2*1024; 00351 const uint KB4 = 4*1024; 00352 const uint KB8 = 8*1024; 00353 const uint KB32 = 32*1024; 00354 const uint KB64 = 64*1024; 00355 const uint KB128 = 128*1024; 00356 const uint KB256 = 256*1024; 00357 const uint KB512 = 512*1024; 00358 00359 estbitrate = (uint) max(abs(rawbitrate * playspeed), 00360 0.5f * rawbitrate); 00361 estbitrate = min(rawbitrate * 3, estbitrate); 00362 int const rbs = (estbitrate > 18000) ? KB512 : 00363 (estbitrate > 9000) ? KB256 : 00364 (estbitrate > 5000) ? KB128 : 00365 (estbitrate > 2500) ? KB64 : 00366 (estbitrate >= 500) ? KB32 : 00367 (estbitrate > 250) ? KB8 : 00368 (estbitrate > 125) ? KB4 : KB2; 00369 if (rbs < CHUNK) 00370 readblocksize = rbs; 00371 else 00372 readblocksize = max(rbs,readblocksize); 00373 00374 // minumum seconds of buffering before allowing read 00375 float secs_min = 0.25; 00376 // set the minimum buffering before allowing ffmpeg read 00377 fill_min = (uint) ((estbitrate * secs_min) * 0.125f); 00378 // make this a multiple of ffmpeg block size.. 00379 if (fill_min >= CHUNK || rbs >= CHUNK) 00380 { 00381 if (low_buffers) 00382 { 00383 LOG(VB_GENERAL, LOG_INFO, LOC + 00384 "Buffering optimisations disabled."); 00385 } 00386 low_buffers = false; 00387 fill_min = ((fill_min / CHUNK) + 1) * CHUNK; 00388 } 00389 else 00390 { 00391 low_buffers = true; 00392 LOG(VB_GENERAL, LOG_WARNING, "Enabling buffering optimisations " 00393 "for low bitrate stream."); 00394 } 00395 00396 LOG(VB_FILE, LOG_INFO, LOC + 00397 QString("CalcReadAheadThresh(%1 Kb)\n\t\t\t -> " 00398 "threshhold(%2 KB) min read(%3 KB) blk size(%4 KB)") 00399 .arg(estbitrate).arg(fill_threshold/1024) 00400 .arg(fill_min/1024).arg(readblocksize/1024)); 00401 } 00402 00403 bool RingBuffer::IsNearEnd(double fps, uint vvf) const 00404 { 00405 rwlock.lockForRead(); 00406 int sz = ReadBufAvail(); 00407 uint rbs = readblocksize; 00408 // telecom kilobytes (i.e. 1000 per k not 1024) 00409 uint tmp = (uint) max(abs(rawbitrate * playspeed), 0.5f * rawbitrate); 00410 uint kbits_per_sec = min(rawbitrate * 3, tmp); 00411 rwlock.unlock(); 00412 00413 // WARNING: readahead_frames can greatly overestimate or underestimate 00414 // the number of frames available in the read ahead buffer 00415 // when rh_frames is less than the keyframe distance. 00416 double bytes_per_frame = kbits_per_sec * (1000.0/8.0) / fps; 00417 double readahead_frames = sz / bytes_per_frame; 00418 00419 bool near_end = ((vvf + readahead_frames) < 20.0) || (sz < rbs*1.5); 00420 00421 LOG(VB_PLAYBACK, LOG_INFO, LOC + "IsReallyNearEnd()" + 00422 QString(" br(%1KB)").arg(kbits_per_sec/8) + 00423 QString(" sz(%1KB)").arg(sz / 1000) + 00424 QString(" vfl(%1)").arg(vvf) + 00425 QString(" frh(%1)").arg(((uint)readahead_frames)) + 00426 QString(" ne:%1").arg(near_end)); 00427 00428 return near_end; 00429 } 00430 00433 int RingBuffer::ReadBufFree(void) const 00434 { 00435 rbrlock.lockForRead(); 00436 rbwlock.lockForRead(); 00437 int ret = ((rbwpos >= rbrpos) ? rbrpos + bufferSize : rbrpos) - rbwpos - 1; 00438 rbwlock.unlock(); 00439 rbrlock.unlock(); 00440 return ret; 00441 } 00442 00445 int RingBuffer::ReadBufAvail(void) const 00446 { 00447 rbrlock.lockForRead(); 00448 rbwlock.lockForRead(); 00449 int ret = (rbwpos >= rbrpos) ? rbwpos - rbrpos : bufferSize - rbrpos + rbwpos; 00450 rbwlock.unlock(); 00451 rbrlock.unlock(); 00452 return ret; 00453 } 00454 00466 void RingBuffer::ResetReadAhead(long long newinternal) 00467 { 00468 LOG(VB_FILE, LOG_INFO, LOC + 00469 QString("ResetReadAhead(internalreadpos = %1->%2)") 00470 .arg(internalreadpos).arg(newinternal)); 00471 00472 rbrlock.lockForWrite(); 00473 rbwlock.lockForWrite(); 00474 00475 CalcReadAheadThresh(); 00476 rbrpos = 0; 00477 rbwpos = 0; 00478 internalreadpos = newinternal; 00479 ateof = false; 00480 readsallowed = false; 00481 setswitchtonext = false; 00482 generalWait.wakeAll(); 00483 00484 rbwlock.unlock(); 00485 rbrlock.unlock(); 00486 } 00487 00502 void RingBuffer::Start(void) 00503 { 00504 bool do_start = true; 00505 00506 rwlock.lockForWrite(); 00507 if (!startreadahead) 00508 { 00509 do_start = false; 00510 } 00511 else if (writemode) 00512 { 00513 LOG(VB_GENERAL, LOG_WARNING, LOC + "Not starting read ahead thread, " 00514 "this is a write only RingBuffer"); 00515 do_start = false; 00516 } 00517 else if (readaheadrunning) 00518 { 00519 LOG(VB_GENERAL, LOG_WARNING, LOC + "Not starting read ahead thread, " 00520 "already running"); 00521 do_start = false; 00522 } 00523 00524 if (!do_start) 00525 { 00526 rwlock.unlock(); 00527 return; 00528 } 00529 00530 StartReads(); 00531 00532 MThread::start(); 00533 00534 while (readaheadrunning && !reallyrunning) 00535 generalWait.wait(&rwlock); 00536 00537 rwlock.unlock(); 00538 } 00539 00543 void RingBuffer::KillReadAheadThread(void) 00544 { 00545 while (isRunning()) 00546 { 00547 rwlock.lockForWrite(); 00548 readaheadrunning = false; 00549 StopReads(); 00550 generalWait.wakeAll(); 00551 rwlock.unlock(); 00552 MThread::wait(5000); 00553 } 00554 } 00555 00560 void RingBuffer::StopReads(void) 00561 { 00562 LOG(VB_FILE, LOG_INFO, LOC + "StopReads()"); 00563 stopreads = true; 00564 generalWait.wakeAll(); 00565 } 00566 00571 void RingBuffer::StartReads(void) 00572 { 00573 LOG(VB_FILE, LOG_INFO, LOC + "StartReads()"); 00574 stopreads = false; 00575 generalWait.wakeAll(); 00576 } 00577 00582 void RingBuffer::Pause(void) 00583 { 00584 LOG(VB_FILE, LOG_INFO, LOC + "Pause()"); 00585 StopReads(); 00586 00587 rwlock.lockForWrite(); 00588 request_pause = true; 00589 rwlock.unlock(); 00590 } 00591 00596 void RingBuffer::Unpause(void) 00597 { 00598 LOG(VB_FILE, LOG_INFO, LOC + "Unpause()"); 00599 StartReads(); 00600 00601 rwlock.lockForWrite(); 00602 request_pause = false; 00603 generalWait.wakeAll(); 00604 rwlock.unlock(); 00605 } 00606 00608 bool RingBuffer::isPaused(void) const 00609 { 00610 rwlock.lockForRead(); 00611 bool ret = !readaheadrunning || paused; 00612 rwlock.unlock(); 00613 return ret; 00614 } 00615 00619 void RingBuffer::WaitForPause(void) 00620 { 00621 MythTimer t; 00622 t.start(); 00623 00624 rwlock.lockForRead(); 00625 while (readaheadrunning && !paused && request_pause) 00626 { 00627 generalWait.wait(&rwlock, 1000); 00628 if (readaheadrunning && !paused && request_pause && t.elapsed() > 1000) 00629 { 00630 LOG(VB_GENERAL, LOG_WARNING, LOC + 00631 QString("Waited %1 ms for ringbuffer pause..") 00632 .arg(t.elapsed())); 00633 } 00634 } 00635 rwlock.unlock(); 00636 } 00637 00638 bool RingBuffer::PauseAndWait(void) 00639 { 00640 const uint timeout = 500; // ms 00641 00642 if (request_pause) 00643 { 00644 if (!paused) 00645 { 00646 rwlock.unlock(); 00647 rwlock.lockForWrite(); 00648 00649 if (request_pause) 00650 { 00651 paused = true; 00652 generalWait.wakeAll(); 00653 } 00654 00655 rwlock.unlock(); 00656 rwlock.lockForRead(); 00657 } 00658 00659 if (request_pause && paused && readaheadrunning) 00660 generalWait.wait(&rwlock, timeout); 00661 } 00662 00663 if (!request_pause && paused) 00664 { 00665 rwlock.unlock(); 00666 rwlock.lockForWrite(); 00667 00668 if (!request_pause) 00669 { 00670 paused = false; 00671 generalWait.wakeAll(); 00672 } 00673 00674 rwlock.unlock(); 00675 rwlock.lockForRead(); 00676 } 00677 00678 return request_pause || paused; 00679 } 00680 00681 void RingBuffer::CreateReadAheadBuffer(void) 00682 { 00683 rwlock.lockForWrite(); 00684 poslock.lockForWrite(); 00685 00686 uint oldsize = bufferSize; 00687 uint newsize = BUFFER_SIZE_MINIMUM; 00688 if (remotefile) 00689 { 00690 newsize *= BUFFER_FACTOR_NETWORK; 00691 if (fileismatroska) 00692 newsize *= BUFFER_FACTOR_MATROSKA; 00693 if (unknownbitrate) 00694 newsize *= BUFFER_FACTOR_BITRATE; 00695 } 00696 00697 // N.B. Don't try and make it smaller - bad things happen... 00698 if (readAheadBuffer && oldsize >= newsize) 00699 { 00700 poslock.unlock(); 00701 rwlock.unlock(); 00702 return; 00703 } 00704 00705 bufferSize = newsize; 00706 if (readAheadBuffer) 00707 { 00708 char* newbuffer = new char[bufferSize + 1024]; 00709 memcpy(newbuffer, readAheadBuffer + rbwpos, oldsize - rbwpos); 00710 memcpy(newbuffer + (oldsize - rbwpos), readAheadBuffer, rbwpos); 00711 delete [] readAheadBuffer; 00712 readAheadBuffer = newbuffer; 00713 rbrpos = (rbrpos > rbwpos) ? (rbrpos - rbwpos) : 00714 (rbrpos + oldsize - rbwpos); 00715 rbwpos = oldsize; 00716 } 00717 else 00718 { 00719 readAheadBuffer = new char[bufferSize + 1024]; 00720 } 00721 CalcReadAheadThresh(); 00722 poslock.unlock(); 00723 rwlock.unlock(); 00724 00725 LOG(VB_FILE, LOG_INFO, LOC + QString("Created readAheadBuffer: %1Mb") 00726 .arg(newsize >> 20)); 00727 } 00728 00729 void RingBuffer::run(void) 00730 { 00731 RunProlog(); 00732 00733 // These variables are used to adjust the read block size 00734 struct timeval lastread, now; 00735 int readtimeavg = 300; 00736 bool ignore_for_read_timing = true; 00737 00738 gettimeofday(&lastread, NULL); // this is just to keep gcc happy 00739 00740 CreateReadAheadBuffer(); 00741 rwlock.lockForWrite(); 00742 poslock.lockForWrite(); 00743 request_pause = false; 00744 ResetReadAhead(0); 00745 readaheadrunning = true; 00746 reallyrunning = true; 00747 generalWait.wakeAll(); 00748 poslock.unlock(); 00749 rwlock.unlock(); 00750 00751 // NOTE: this must loop at some point hold only 00752 // a read lock on rwlock, so that other functions 00753 // such as reset and seek can take priority. 00754 00755 rwlock.lockForRead(); 00756 00757 LOG(VB_FILE, LOG_INFO, LOC + 00758 QString("Initial readblocksize %1K & fill_min %2K") 00759 .arg(readblocksize/1024).arg(fill_min/1024)); 00760 00761 while (readaheadrunning) 00762 { 00763 if (PauseAndWait()) 00764 { 00765 ignore_for_read_timing = true; 00766 continue; 00767 } 00768 00769 long long totfree = ReadBufFree(); 00770 00771 const uint KB32 = 32*1024; 00772 // These are conditions where we don't want to go through 00773 // the loop if they are true. 00774 if (((totfree < KB32) && readsallowed) || 00775 (ignorereadpos >= 0) || commserror || stopreads) 00776 { 00777 ignore_for_read_timing |= 00778 (ignorereadpos >= 0) || commserror || stopreads; 00779 generalWait.wait(&rwlock, (stopreads) ? 50 : 1000); 00780 continue; 00781 } 00782 00783 // These are conditions where we want to sleep to allow 00784 // other threads to do stuff. 00785 if (setswitchtonext || (ateof && readsallowed)) 00786 { 00787 ignore_for_read_timing = true; 00788 generalWait.wait(&rwlock, 1000); 00789 totfree = ReadBufFree(); 00790 } 00791 00792 int read_return = -1; 00793 if (totfree >= KB32 && !commserror && 00794 !ateof && !setswitchtonext) 00795 { 00796 // limit the read size 00797 if (readblocksize > totfree) 00798 totfree = (int)(totfree / KB32) * KB32; // must be multiple of 32KB 00799 else 00800 totfree = readblocksize; 00801 00802 // adapt blocksize 00803 gettimeofday(&now, NULL); 00804 if (!ignore_for_read_timing) 00805 { 00806 int readinterval = (now.tv_sec - lastread.tv_sec ) * 1000 + 00807 (now.tv_usec - lastread.tv_usec) / 1000; 00808 readtimeavg = (readtimeavg * 9 + readinterval) / 10; 00809 00810 if (readtimeavg < 150 && 00811 (uint)readblocksize < (BUFFER_SIZE_MINIMUM >>2) && 00812 readblocksize >= CHUNK /* low_buffers */) 00813 { 00814 int old_block_size = readblocksize; 00815 readblocksize = 3 * readblocksize / 2; 00816 readblocksize = ((readblocksize+CHUNK-1) / CHUNK) * CHUNK; 00817 LOG(VB_FILE, LOG_INFO, LOC + 00818 QString("Avg read interval was %1 msec. " 00819 "%2K -> %3K block size") 00820 .arg(readtimeavg) 00821 .arg(old_block_size/1024) 00822 .arg(readblocksize/1024)); 00823 readtimeavg = 225; 00824 } 00825 else if (readtimeavg > 300 && readblocksize > CHUNK) 00826 { 00827 readblocksize -= CHUNK; 00828 LOG(VB_FILE, LOG_INFO, LOC + 00829 QString("Avg read interval was %1 msec. " 00830 "%2K -> %3K block size") 00831 .arg(readtimeavg) 00832 .arg((readblocksize+CHUNK)/1024) 00833 .arg(readblocksize/1024)); 00834 readtimeavg = 225; 00835 } 00836 } 00837 ignore_for_read_timing = (totfree < readblocksize) ? true : false; 00838 lastread = now; 00839 00840 rbwlock.lockForRead(); 00841 if (rbwpos + totfree > bufferSize) 00842 { 00843 totfree = bufferSize - rbwpos; 00844 LOG(VB_FILE, LOG_DEBUG, LOC + 00845 "Shrinking read, near end of buffer"); 00846 } 00847 00848 if (internalreadpos == 0) 00849 { 00850 totfree = max(fill_min, readblocksize); 00851 LOG(VB_FILE, LOG_DEBUG, LOC + 00852 "Reading enough data to start playback"); 00853 } 00854 00855 LOG(VB_FILE, LOG_DEBUG, LOC + 00856 QString("safe_read(...@%1, %2) -- begin") 00857 .arg(rbwpos).arg(totfree)); 00858 00859 MythTimer sr_timer; 00860 sr_timer.start(); 00861 00862 int rbwposcopy = rbwpos; 00863 00864 // FileRingBuffer::safe_read(RemoteFile*...) acquires poslock; 00865 // so we need to unlock this here to preserve locking order. 00866 rbwlock.unlock(); 00867 00868 read_return = safe_read(readAheadBuffer + rbwposcopy, totfree); 00869 00870 int sr_elapsed = sr_timer.elapsed(); 00871 uint64_t bps = !sr_elapsed ? 1000000001 : 00872 (uint64_t)(((double)read_return * 8000.0) / 00873 (double)sr_elapsed); 00874 LOG(VB_FILE, LOG_INFO, LOC + 00875 QString("safe_read(...@%1, %2) -> %3, took %4 ms %5") 00876 .arg(rbwposcopy).arg(totfree).arg(read_return) 00877 .arg(sr_elapsed) 00878 .arg(QString("(%1Mbps)").arg((double)bps / 1000000.0))); 00879 UpdateStorageRate(bps); 00880 00881 if (read_return >= 0) 00882 { 00883 poslock.lockForWrite(); 00884 rbwlock.lockForWrite(); 00885 if (rbwposcopy == rbwpos) 00886 { 00887 internalreadpos += read_return; 00888 rbwpos = (rbwpos + read_return) % bufferSize; 00889 LOG(VB_FILE, LOG_DEBUG, 00890 LOC + QString("rbwpos += %1K requested %2K in read") 00891 .arg(read_return/1024,3).arg(totfree/1024,3)); 00892 } 00893 rbwlock.unlock(); 00894 poslock.unlock(); 00895 } 00896 } 00897 00898 int used = bufferSize - ReadBufFree(); 00899 00900 bool reads_were_allowed = readsallowed; 00901 00902 if ((0 == read_return) || (numfailures > 5) || 00903 (readsallowed != (used >= fill_min || ateof || 00904 setswitchtonext || commserror))) 00905 { 00906 // If readpos changes while the lock is released 00907 // we should not handle the 0 read_return now. 00908 long long old_readpos = readpos; 00909 00910 rwlock.unlock(); 00911 rwlock.lockForWrite(); 00912 00913 commserror |= (numfailures > 5); 00914 00915 readsallowed = used >= fill_min || ateof || 00916 setswitchtonext || commserror; 00917 00918 if (0 == read_return && old_readpos == readpos) 00919 { 00920 if (livetvchain) 00921 { 00922 if (!setswitchtonext && !ignoreliveeof && 00923 livetvchain->HasNext()) 00924 { 00925 livetvchain->SwitchToNext(true); 00926 setswitchtonext = true; 00927 } 00928 } 00929 else 00930 { 00931 LOG(VB_FILE, LOG_DEBUG, 00932 LOC + "setting ateof (read_return == 0)"); 00933 ateof = true; 00934 } 00935 } 00936 00937 rwlock.unlock(); 00938 rwlock.lockForRead(); 00939 used = bufferSize - ReadBufFree(); 00940 } 00941 00942 LOG(VB_FILE, LOG_DEBUG, LOC + "@ end of read ahead loop"); 00943 00944 if (!readsallowed || commserror || ateof || setswitchtonext || 00945 (wanttoread <= used && wanttoread > 0)) 00946 { 00947 // To give other threads a good chance to handle these 00948 // conditions, even if they are only requesting a read lock 00949 // like us, yield (currently implemented with short usleep). 00950 generalWait.wakeAll(); 00951 rwlock.unlock(); 00952 usleep(5 * 1000); 00953 rwlock.lockForRead(); 00954 } 00955 else 00956 { 00957 // yield if we have nothing to do... 00958 if (!request_pause && reads_were_allowed && 00959 (used >= fill_threshold || ateof || setswitchtonext)) 00960 { 00961 generalWait.wait(&rwlock, 50); 00962 } 00963 else if (readsallowed) 00964 { // if reads are allowed release the lock and yield so the 00965 // reader gets a chance to read before the buffer is full. 00966 generalWait.wakeAll(); 00967 rwlock.unlock(); 00968 usleep(5 * 1000); 00969 rwlock.lockForRead(); 00970 } 00971 } 00972 } 00973 00974 rwlock.unlock(); 00975 00976 rwlock.lockForWrite(); 00977 rbrlock.lockForWrite(); 00978 rbwlock.lockForWrite(); 00979 00980 rbrpos = 0; 00981 rbwpos = 0; 00982 reallyrunning = false; 00983 readsallowed = false; 00984 delete [] readAheadBuffer; 00985 00986 readAheadBuffer = NULL; 00987 rbwlock.unlock(); 00988 rbrlock.unlock(); 00989 rwlock.unlock(); 00990 00991 RunEpilog(); 00992 } 00993 00994 long long RingBuffer::SetAdjustFilesize(void) 00995 { 00996 rwlock.lockForWrite(); 00997 poslock.lockForRead(); 00998 readAdjust += internalreadpos; 00999 long long ra = readAdjust; 01000 poslock.unlock(); 01001 rwlock.unlock(); 01002 return ra; 01003 } 01004 01005 int RingBuffer::Peek(void *buf, int count) 01006 { 01007 int ret = ReadPriv(buf, count, true); 01008 if (ret != count) 01009 { 01010 LOG(VB_GENERAL, LOG_WARNING, LOC + 01011 QString("Peek() requested %1 bytes, but only returning %2") 01012 .arg(count).arg(ret)); 01013 } 01014 return ret; 01015 } 01016 01017 bool RingBuffer::WaitForReadsAllowed(void) 01018 { 01019 MythTimer t; 01020 t.start(); 01021 01022 while (!readsallowed && !stopreads && 01023 !request_pause && !commserror && readaheadrunning) 01024 { 01025 generalWait.wait(&rwlock, 1000); 01026 if (!readsallowed && t.elapsed() > 1000) 01027 { 01028 LOG(VB_GENERAL, LOG_WARNING, LOC + 01029 "Taking too long to be allowed to read.."); 01030 01031 if (t.elapsed() > 10000) 01032 { 01033 LOG(VB_GENERAL, LOG_ERR, LOC + "Took more than 10 seconds to " 01034 "be allowed to read, aborting."); 01035 return false; 01036 } 01037 } 01038 } 01039 01040 return readsallowed; 01041 } 01042 01043 bool RingBuffer::WaitForAvail(int count) 01044 { 01045 int avail = ReadBufAvail(); 01046 count = (ateof && avail < count) ? avail : count; 01047 01048 if (livetvchain && setswitchtonext && avail < count) 01049 { 01050 LOG(VB_GENERAL, LOG_INFO, LOC + 01051 "Checking to see if there's a new livetv program to switch to.."); 01052 livetvchain->ReloadAll(); 01053 return false; 01054 } 01055 01056 // Make sure that if the read ahead thread is sleeping and 01057 // it should be reading that we start reading right away. 01058 if ((avail < count) && !stopreads && 01059 !request_pause && !commserror && readaheadrunning) 01060 { 01061 generalWait.wakeAll(); 01062 } 01063 01064 MythTimer t; 01065 t.start(); 01066 while ((avail < count) && !stopreads && 01067 !request_pause && !commserror && readaheadrunning) 01068 { 01069 wanttoread = count; 01070 generalWait.wait(&rwlock, 250); 01071 avail = ReadBufAvail(); 01072 01073 if (ateof && avail < count) 01074 count = avail; 01075 01076 if (avail < count) 01077 { 01078 int elapsed = t.elapsed(); 01079 if (elapsed > 500 && low_buffers && avail >= fill_min) 01080 count = avail; 01081 else if (((elapsed > 250) && (elapsed < 500)) || 01082 ((elapsed > 500) && (elapsed < 750)) || 01083 ((elapsed > 1000) && (elapsed < 1250)) || 01084 ((elapsed > 2000) && (elapsed < 2250)) || 01085 ((elapsed > 4000) && (elapsed < 4250)) || 01086 ((elapsed > 8000) && (elapsed < 8250)) || 01087 ((elapsed > 9000))) 01088 { 01089 LOG(VB_GENERAL, LOG_INFO, LOC + "Waited " + 01090 QString("%1").arg((elapsed / 250) * 0.25f, 3, 'f', 1) + 01091 " seconds for data \n\t\t\tto become available..." + 01092 QString(" %2 < %3") .arg(avail).arg(count)); 01093 } 01094 01095 if (elapsed > 16000) 01096 { 01097 LOG(VB_GENERAL, LOG_ERR, LOC + "Waited " + 01098 QString("%1").arg(elapsed/1000) + 01099 " seconds for data, aborting."); 01100 return false; 01101 } 01102 } 01103 } 01104 01105 wanttoread = 0; 01106 01107 return avail >= count; 01108 } 01109 01110 int RingBuffer::ReadDirect(void *buf, int count, bool peek) 01111 { 01112 long long old_pos = 0; 01113 if (peek) 01114 { 01115 poslock.lockForRead(); 01116 old_pos = (ignorereadpos >= 0) ? ignorereadpos : readpos; 01117 poslock.unlock(); 01118 } 01119 01120 MythTimer timer; 01121 timer.start(); 01122 int ret = safe_read(buf, count); 01123 int elapsed = timer.elapsed(); 01124 uint64_t bps = !elapsed ? 1000000001 : 01125 (uint64_t)(((float)ret * 8000.0) / (float)elapsed); 01126 UpdateStorageRate(bps); 01127 01128 poslock.lockForWrite(); 01129 if (ignorereadpos >= 0 && ret > 0) 01130 { 01131 if (peek) 01132 { 01133 // seek should always succeed since we were at this position 01134 if (remotefile) 01135 remotefile->Seek(old_pos, SEEK_SET); 01136 else if (fd2 >= 0) 01137 lseek64(fd2, old_pos, SEEK_SET); 01138 } 01139 else 01140 { 01141 ignorereadpos += ret; 01142 } 01143 poslock.unlock(); 01144 return ret; 01145 } 01146 poslock.unlock(); 01147 01148 if (peek && ret > 0) 01149 { 01150 if ((IsDVD() || IsBD()) && old_pos != 0) 01151 { 01152 LOG(VB_GENERAL, LOG_ERR, LOC + 01153 "DVD and Blu-Ray do not support arbitrary " 01154 "peeks except when read-ahead is enabled." 01155 "\n\t\t\tWill seek to beginning of video."); 01156 old_pos = 0; 01157 } 01158 01159 long long new_pos = Seek(old_pos, SEEK_SET, true); 01160 01161 if (new_pos != old_pos) 01162 { 01163 LOG(VB_GENERAL, LOG_ERR, LOC + 01164 QString("Peek() Failed to return from new " 01165 "position %1 to old position %2, now " 01166 "at position %3") 01167 .arg(old_pos - ret).arg(old_pos).arg(new_pos)); 01168 } 01169 } 01170 01171 return ret; 01172 } 01173 01182 int RingBuffer::ReadPriv(void *buf, int count, bool peek) 01183 { 01184 QString loc_desc = QString("ReadPriv(..%1, %2)") 01185 .arg(count).arg(peek?"peek":"normal"); 01186 LOG(VB_FILE, LOG_DEBUG, LOC + loc_desc + 01187 QString(" @%1 -- begin").arg(rbrpos)); 01188 01189 rwlock.lockForRead(); 01190 if (writemode) 01191 { 01192 LOG(VB_GENERAL, LOG_ERR, LOC + loc_desc + 01193 ": Attempt to read from a write only file"); 01194 errno = EBADF; 01195 rwlock.unlock(); 01196 return -1; 01197 } 01198 01199 if (commserror) 01200 { 01201 LOG(VB_GENERAL, LOG_ERR, LOC + loc_desc + 01202 ": Attempt to read after commserror set"); 01203 errno = EIO; 01204 rwlock.unlock(); 01205 return -1; 01206 } 01207 01208 if (request_pause || stopreads || !readaheadrunning || (ignorereadpos>=0)) 01209 { 01210 rwlock.unlock(); 01211 rwlock.lockForWrite(); 01212 // we need a write lock so the read-ahead thread 01213 // can't start mucking with the read position. 01214 // If the read ahead thread was started while we 01215 // didn't hold the lock, we proceed with a normal 01216 // read from the buffer, otherwise we read directly. 01217 if (request_pause || stopreads || 01218 !readaheadrunning || (ignorereadpos >= 0)) 01219 { 01220 int ret = ReadDirect(buf, count, peek); 01221 LOG(VB_FILE, LOG_DEBUG, LOC + loc_desc + 01222 QString(": ReadDirect checksum %1") 01223 .arg(qChecksum((char*)buf,count))); 01224 rwlock.unlock(); 01225 return ret; 01226 } 01227 rwlock.unlock(); 01228 rwlock.lockForRead(); 01229 } 01230 01231 if (!WaitForReadsAllowed()) 01232 { 01233 LOG(VB_FILE, LOG_NOTICE, LOC + loc_desc + ": !WaitForReadsAllowed()"); 01234 rwlock.unlock(); 01235 stopreads = true; // this needs to be outside the lock 01236 rwlock.lockForWrite(); 01237 wanttoread = 0; 01238 rwlock.unlock(); 01239 return 0; 01240 } 01241 01242 if (!WaitForAvail(count)) 01243 { 01244 LOG(VB_FILE, LOG_NOTICE, LOC + loc_desc + ": !WaitForAvail()"); 01245 rwlock.unlock(); 01246 stopreads = true; // this needs to be outside the lock 01247 rwlock.lockForWrite(); 01248 ateof = true; 01249 wanttoread = 0; 01250 rwlock.unlock(); 01251 return 0; 01252 } 01253 01254 count = min(ReadBufAvail(), count); 01255 01256 if (count <= 0) 01257 { 01258 // this can happen under a few conditions but the most 01259 // notable is an exit from the read ahead thread or 01260 // the end of the file stream has been reached. 01261 LOG(VB_FILE, LOG_NOTICE, LOC + loc_desc + ": ReadBufAvail() == 0"); 01262 rwlock.unlock(); 01263 return count; 01264 } 01265 01266 if (peek) 01267 rbrlock.lockForRead(); 01268 else 01269 rbrlock.lockForWrite(); 01270 01271 LOG(VB_FILE, LOG_DEBUG, LOC + loc_desc + " -- copying data"); 01272 01273 if (rbrpos + count > (int) bufferSize) 01274 { 01275 int firstsize = bufferSize - rbrpos; 01276 int secondsize = count - firstsize; 01277 01278 memcpy(buf, readAheadBuffer + rbrpos, firstsize); 01279 memcpy((char *)buf + firstsize, readAheadBuffer, secondsize); 01280 } 01281 else 01282 { 01283 memcpy(buf, readAheadBuffer + rbrpos, count); 01284 } 01285 LOG(VB_FILE, LOG_DEBUG, LOC + loc_desc + QString(" -- checksum %1") 01286 .arg(qChecksum((char*)buf,count))); 01287 01288 if (!peek) 01289 { 01290 rbrpos = (rbrpos + count) % bufferSize; 01291 generalWait.wakeAll(); 01292 } 01293 rbrlock.unlock(); 01294 rwlock.unlock(); 01295 01296 return count; 01297 } 01298 01307 int RingBuffer::Read(void *buf, int count) 01308 { 01309 int ret = ReadPriv(buf, count, false); 01310 if (ret > 0) 01311 { 01312 poslock.lockForWrite(); 01313 readpos += ret; 01314 poslock.unlock(); 01315 } 01316 01317 UpdateDecoderRate(ret); 01318 return ret; 01319 } 01320 01321 QString RingBuffer::BitrateToString(uint64_t rate, bool hz) 01322 { 01323 QString msg; 01324 float bitrate; 01325 int range = 0; 01326 if (rate < 1) 01327 { 01328 return "-"; 01329 } 01330 else if (rate > 1000000000) 01331 { 01332 return QObject::tr(">1Gbps"); 01333 } 01334 else if (rate >= 1000000) 01335 { 01336 msg = hz ? QObject::tr("%1MHz") : QObject::tr("%1Mbps"); 01337 bitrate = (float)rate / (1000000.0); 01338 range = hz ? 3 : 1; 01339 } 01340 else if (rate >= 1000) 01341 { 01342 msg = hz ? QObject::tr("%1kHz") : QObject::tr("%1kbps"); 01343 bitrate = (float)rate / 1000.0; 01344 range = hz ? 1 : 0; 01345 } 01346 else 01347 { 01348 msg = hz ? QObject::tr("%1Hz") : QObject::tr("%1bps"); 01349 bitrate = (float)rate; 01350 } 01351 return msg.arg(bitrate, 0, 'f', range); 01352 } 01353 01354 QString RingBuffer::GetDecoderRate(void) 01355 { 01356 return BitrateToString(UpdateDecoderRate()); 01357 } 01358 01359 QString RingBuffer::GetStorageRate(void) 01360 { 01361 return BitrateToString(UpdateStorageRate()); 01362 } 01363 01364 QString RingBuffer::GetAvailableBuffer(void) 01365 { 01366 if (type == kRingBuffer_DVD || type == kRingBuffer_BD) 01367 return "N/A"; 01368 01369 int avail = (rbwpos >= rbrpos) ? rbwpos - rbrpos : bufferSize - rbrpos + rbwpos; 01370 return QString("%1%").arg((int)(((float)avail / (float)bufferSize) * 100.0)); 01371 } 01372 01373 uint64_t RingBuffer::UpdateDecoderRate(uint64_t latest) 01374 { 01375 if (!bitrateMonitorEnabled) 01376 return 0; 01377 01378 // TODO use QDateTime once we've moved to Qt 4.7 01379 static QTime midnight = QTime(0, 0, 0); 01380 QTime now = QTime::currentTime(); 01381 qint64 age = midnight.msecsTo(now); 01382 qint64 oldest = age - 1000; 01383 01384 decoderReadLock.lock(); 01385 if (latest) 01386 decoderReads.insert(age, latest); 01387 01388 uint64_t total = 0; 01389 QMutableMapIterator<qint64,uint64_t> it(decoderReads); 01390 while (it.hasNext()) 01391 { 01392 it.next(); 01393 if (it.key() < oldest || it.key() > age) 01394 it.remove(); 01395 else 01396 total += it.value(); 01397 } 01398 01399 uint64_t average = (uint64_t)((double)total * 8.0); 01400 decoderReadLock.unlock(); 01401 01402 LOG(VB_FILE, LOG_INFO, LOC + QString("Decoder read speed: %1 %2") 01403 .arg(average).arg(decoderReads.size())); 01404 return average; 01405 } 01406 01407 uint64_t RingBuffer::UpdateStorageRate(uint64_t latest) 01408 { 01409 if (!bitrateMonitorEnabled) 01410 return 0; 01411 01412 // TODO use QDateTime once we've moved to Qt 4.7 01413 static QTime midnight = QTime(0, 0, 0); 01414 QTime now = QTime::currentTime(); 01415 qint64 age = midnight.msecsTo(now); 01416 qint64 oldest = age - 1000; 01417 01418 storageReadLock.lock(); 01419 if (latest) 01420 storageReads.insert(age, latest); 01421 01422 uint64_t total = 0; 01423 QMutableMapIterator<qint64,uint64_t> it(storageReads); 01424 while (it.hasNext()) 01425 { 01426 it.next(); 01427 if (it.key() < oldest || it.key() > age) 01428 it.remove(); 01429 else 01430 total += it.value(); 01431 } 01432 01433 int size = storageReads.size(); 01434 storageReadLock.unlock(); 01435 01436 uint64_t average = size ? (uint64_t)(((double)total) / (double)size) : 0; 01437 01438 LOG(VB_FILE, LOG_INFO, LOC + QString("Average storage read speed: %1 %2") 01439 .arg(average).arg(storageReads.size())); 01440 return average; 01441 } 01442 01447 int RingBuffer::Write(const void *buf, uint count) 01448 { 01449 rwlock.lockForRead(); 01450 01451 if (!writemode) 01452 { 01453 LOG(VB_GENERAL, LOG_ERR, LOC + "Tried to write to a read only file."); 01454 rwlock.unlock(); 01455 return -1; 01456 } 01457 01458 if (!tfw && !remotefile) 01459 { 01460 rwlock.unlock(); 01461 return -1; 01462 } 01463 01464 int ret = -1; 01465 if (tfw) 01466 ret = tfw->Write(buf, count); 01467 else 01468 ret = remotefile->Write(buf, count); 01469 01470 if (ret > 0) 01471 { 01472 poslock.lockForWrite(); 01473 writepos += ret; 01474 poslock.unlock(); 01475 } 01476 01477 rwlock.unlock(); 01478 01479 return ret; 01480 } 01481 01485 void RingBuffer::Sync(void) 01486 { 01487 rwlock.lockForRead(); 01488 if (tfw) 01489 tfw->Sync(); 01490 rwlock.unlock(); 01491 } 01492 01495 long long RingBuffer::WriterSeek(long long pos, int whence, bool has_lock) 01496 { 01497 long long ret = -1; 01498 01499 if (!has_lock) 01500 rwlock.lockForRead(); 01501 01502 poslock.lockForWrite(); 01503 01504 if (tfw) 01505 { 01506 ret = tfw->Seek(pos, whence); 01507 writepos = ret; 01508 } 01509 01510 poslock.unlock(); 01511 01512 if (!has_lock) 01513 rwlock.unlock(); 01514 01515 return ret; 01516 } 01517 01522 void RingBuffer::WriterFlush(void) 01523 { 01524 rwlock.lockForRead(); 01525 if (tfw) 01526 { 01527 tfw->Flush(); 01528 tfw->Sync(); 01529 } 01530 rwlock.unlock(); 01531 } 01532 01536 void RingBuffer::SetWriteBufferMinWriteSize(int newMinSize) 01537 { 01538 rwlock.lockForRead(); 01539 if (tfw) 01540 tfw->SetWriteBufferMinWriteSize(newMinSize); 01541 rwlock.unlock(); 01542 } 01543 01559 void RingBuffer::SetOldFile(bool is_old) 01560 { 01561 LOG(VB_FILE, LOG_INFO, LOC + QString("SetOldFile(%1)").arg(is_old)); 01562 rwlock.lockForWrite(); 01563 oldfile = is_old; 01564 rwlock.unlock(); 01565 } 01566 01568 QString RingBuffer::GetFilename(void) const 01569 { 01570 rwlock.lockForRead(); 01571 QString tmp = filename; 01572 rwlock.unlock(); 01573 return tmp; 01574 } 01575 01576 QString RingBuffer::GetSubtitleFilename(void) const 01577 { 01578 rwlock.lockForRead(); 01579 QString tmp = subtitlefilename; 01580 rwlock.unlock(); 01581 return tmp; 01582 } 01583 01587 long long RingBuffer::GetWritePosition(void) const 01588 { 01589 poslock.lockForRead(); 01590 long long ret = writepos; 01591 poslock.unlock(); 01592 return ret; 01593 } 01594 01599 bool RingBuffer::LiveMode(void) const 01600 { 01601 rwlock.lockForRead(); 01602 bool ret = (livetvchain); 01603 rwlock.unlock(); 01604 return ret; 01605 } 01606 01611 void RingBuffer::SetLiveMode(LiveTVChain *chain) 01612 { 01613 rwlock.lockForWrite(); 01614 livetvchain = chain; 01615 rwlock.unlock(); 01616 } 01617 01619 void RingBuffer::IgnoreLiveEOF(bool ignore) 01620 { 01621 rwlock.lockForWrite(); 01622 ignoreliveeof = ignore; 01623 rwlock.unlock(); 01624 } 01625 01626 const DVDRingBuffer *RingBuffer::DVD(void) const 01627 { 01628 return dynamic_cast<const DVDRingBuffer*>(this); 01629 } 01630 01631 const BDRingBuffer *RingBuffer::BD(void) const 01632 { 01633 return dynamic_cast<const BDRingBuffer*>(this); 01634 } 01635 01636 DVDRingBuffer *RingBuffer::DVD(void) 01637 { 01638 return dynamic_cast<DVDRingBuffer*>(this); 01639 } 01640 01641 BDRingBuffer *RingBuffer::BD(void) 01642 { 01643 return dynamic_cast<BDRingBuffer*>(this); 01644 } 01645 01646 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1.7.6.1