long long FileRingBuffer::Seek(long long pos, int whence, bool has_lock) { LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(%1,%2,%3)") .arg(pos).arg((SEEK_SET==whence)?"SEEK_SET": ((SEEK_CUR==whence)?"SEEK_CUR":"SEEK_END")) .arg(has_lock?"locked":"unlocked")); long long ret = -1; StopReads(); // lockForWrite takes priority over lockForRead, so this will // take priority over the lockForRead in the read ahead thread. if (!has_lock) rwlock.lockForWrite(); StartReads(); if (writemode) { ret = WriterSeek(pos, whence, true); if (!has_lock) rwlock.unlock(); return ret; } poslock.lockForWrite(); // Optimize no-op seeks if (readaheadrunning && ((whence == SEEK_SET && pos == readpos) || (whence == SEEK_CUR && pos == 0))) { ret = readpos; poslock.unlock(); if (!has_lock) rwlock.unlock(); return ret; } // only valid for SEEK_SET & SEEK_CUR long long new_pos = (SEEK_SET==whence) ? pos : readpos + pos; #if 1 // Optimize short seeks where the data for // them is in our ringbuffer already. if (readaheadrunning && (SEEK_SET==whence || SEEK_CUR==whence)) { rbrlock.lockForWrite(); rbwlock.lockForRead(); LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(): rbrpos: %1 rbwpos: %2" "\n\t\t\treadpos: %3 internalreadpos: %4") .arg(rbrpos).arg(rbwpos) .arg(readpos).arg(internalreadpos)); bool used_opt = false; if ((new_pos < readpos)) { int min_safety = max(fill_min, readblocksize); int free = ((rbwpos >= rbrpos) ? rbrpos + bufferSize : rbrpos) - rbwpos; int internal_backbuf = (rbwpos >= rbrpos) ? rbrpos : rbrpos - rbwpos; internal_backbuf = min(internal_backbuf, free - min_safety); long long sba = readpos - new_pos; LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(): internal_backbuf: %1 sba: %2") .arg(internal_backbuf).arg(sba)); if (internal_backbuf >= sba) { rbrpos = (rbrpos>=sba) ? rbrpos - sba : bufferSize + rbrpos - sba; used_opt = true; LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(): OPT1 rbrpos: %1 rbwpos: %2" "\n\t\t\treadpos: %3 internalreadpos: %4") .arg(rbrpos).arg(rbwpos) .arg(new_pos).arg(internalreadpos)); } } else if ((new_pos >= readpos) && (new_pos <= internalreadpos)) { rbrpos = (rbrpos + (new_pos - readpos)) % bufferSize; used_opt = true; LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(): OPT2 rbrpos: %1 sba: %2") .arg(rbrpos).arg(readpos - new_pos)); } rbwlock.unlock(); rbrlock.unlock(); if (used_opt) { if (ignorereadpos >= 0) { // seek should always succeed since we were at this position int ret; if (remotefile) ret = remotefile->Seek(internalreadpos, SEEK_SET); else { ret = lseek64(fd2, internalreadpos, SEEK_SET); posix_fadvise(fd2, internalreadpos, 128*1024, POSIX_FADV_WILLNEED); } LOG(VB_FILE, LOG_INFO, LOC + QString("Seek to %1 from ignore pos %2 returned %3") .arg(internalreadpos).arg(ignorereadpos).arg(ret)); ignorereadpos = -1; } readpos = new_pos; poslock.unlock(); generalWait.wakeAll(); ateof = false; readsallowed = false; if (!has_lock) rwlock.unlock(); return new_pos; } } #endif #if 1 // This optimizes the seek end-250000, read, seek 0, read portion // of the pattern ffmpeg performs at the start of playback to // determine the pts. // If the seek is a SEEK_END or is a seek where the position // changes over 100 MB we check the file size and if the // destination point is within 300000 bytes of the end of // the file we enter a special mode where the read ahead // buffer stops reading data and all reads are made directly // until another seek is performed. The point of all this is // to avoid flushing out the buffer that still contains all // the data the final seek 0, read will need just to read the // last 250000 bytes. A further optimization would be to buffer // the 250000 byte read, which is currently performed in 32KB // blocks (inefficient with RemoteFile). if ((remotefile || fd2 >= 0) && (ignorereadpos < 0)) { long long off_end = 0xDEADBEEF; if (SEEK_END == whence) { off_end = pos; if (remotefile) { new_pos = remotefile->GetFileSize() - off_end; } else { QFileInfo fi(filename); new_pos = fi.size() - off_end; } } else { if (remotefile) { off_end = remotefile->GetFileSize() - new_pos; } else { QFileInfo fi(filename); off_end = fi.size() - new_pos; } } if (off_end != 0xDEADBEEF) { LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(): Offset from end: %1").arg(off_end)); } if (off_end == 250000) { LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(): offset from end: %1").arg(off_end) + "\n\t\t\t -- ignoring read ahead thread until next seek."); ignorereadpos = new_pos; errno = EINVAL; long long ret; if (remotefile) ret = remotefile->Seek(ignorereadpos, SEEK_SET); else ret = lseek64(fd2, ignorereadpos, SEEK_SET); if (ret < 0) { int tmp_eno = errno; QString cmd = QString("Seek(%1, SEEK_SET) ign ") .arg(ignorereadpos); ignorereadpos = -1; LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO); // try to return to former position.. if (remotefile) ret = remotefile->Seek(internalreadpos, SEEK_SET); else ret = lseek64(fd2, internalreadpos, SEEK_SET); if (ret < 0) { QString cmd = QString("Seek(%1, SEEK_SET) int ") .arg(internalreadpos); LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO); } else { QString cmd = QString("Seek(%1, %2) int ") .arg(internalreadpos) .arg((SEEK_SET == whence) ? "SEEK_SET" : ((SEEK_CUR == whence) ?"SEEK_CUR" : "SEEK_END")); LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " succeeded"); } ret = -1; errno = tmp_eno; } else { ateof = false; readsallowed = false; } poslock.unlock(); generalWait.wakeAll(); if (!has_lock) rwlock.unlock(); return ret; } } #endif // Here we perform a normal seek. When successful we // need to call ResetReadAhead(). A reset means we will // need to refill the buffer, which takes some time. if (remotefile) { ret = remotefile->Seek(pos, whence, readpos); if (ret<0) errno = EINVAL; } else { ret = lseek64(fd2, pos, whence); } if (ret >= 0) { readpos = ret; ignorereadpos = -1; if (readaheadrunning) ResetReadAhead(readpos); readAdjust = 0; } else { QString cmd = QString("Seek(%1, %2)").arg(pos) .arg((whence == SEEK_SET) ? "SEEK_SET" : ((whence == SEEK_CUR) ? "SEEK_CUR" : "SEEK_END")); LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO); } poslock.unlock(); generalWait.wakeAll(); if (!has_lock) rwlock.unlock(); return ret; }
long long BDRingBuffer::Seek(long long pos, int whence, bool has_lock) { VERBOSE(VB_FILE, LOC + QString("Seek(%1,%2,%3)") .arg(pos).arg((SEEK_SET==whence)?"SEEK_SET": ((SEEK_CUR==whence)?"SEEK_CUR":"SEEK_END")) .arg(has_lock?"locked":"unlocked")); long long ret = -1; // lockForWrite takes priority over lockForRead, so this will // take priority over the lockForRead in the read ahead thread. if (!has_lock) rwlock.lockForWrite(); poslock.lockForWrite(); // Optimize no-op seeks if (readaheadrunning && ((whence == SEEK_SET && pos == readpos) || (whence == SEEK_CUR && pos == 0))) { ret = readpos; poslock.unlock(); if (!has_lock) rwlock.unlock(); return ret; } // only valid for SEEK_SET & SEEK_CUR long long new_pos = (SEEK_SET==whence) ? pos : readpos + pos; // Here we perform a normal seek. When successful we // need to call ResetReadAhead(). A reset means we will // need to refill the buffer, which takes some time. if ((SEEK_END == whence) || ((SEEK_CUR == whence) && new_pos != 0)) { errno = EINVAL; ret = -1; } else { Seek(new_pos); ret = new_pos; } if (ret >= 0) { readpos = ret; ignorereadpos = -1; if (readaheadrunning) ResetReadAhead(readpos); readAdjust = 0; } else { QString cmd = QString("Seek(%1, %2)").arg(pos) .arg((SEEK_SET == whence) ? "SEEK_SET" : ((SEEK_CUR == whence) ?"SEEK_CUR" : "SEEK_END")); VERBOSE(VB_IMPORTANT, LOC_ERR + cmd + " Failed" + ENO); } poslock.unlock(); generalWait.wakeAll(); if (!has_lock) rwlock.unlock(); return ret; }
long long BDRingBuffer::SeekInternal(long long pos, int whence) { long long ret = -1; poslock.lockForWrite(); // Optimize no-op seeks if (readaheadrunning && ((whence == SEEK_SET && pos == readpos) || (whence == SEEK_CUR && pos == 0))) { ret = readpos; poslock.unlock(); return ret; } // only valid for SEEK_SET & SEEK_CUR long long new_pos = (SEEK_SET==whence) ? pos : readpos + pos; // Here we perform a normal seek. When successful we // need to call ResetReadAhead(). A reset means we will // need to refill the buffer, which takes some time. if ((SEEK_END == whence) || ((SEEK_CUR == whence) && new_pos != 0)) { errno = EINVAL; ret = -1; } else { SeekInternal(new_pos); m_currentTime = bd_tell_time(bdnav); ret = new_pos; } if (ret >= 0) { readpos = ret; ignorereadpos = -1; if (readaheadrunning) ResetReadAhead(readpos); readAdjust = 0; } else { QString cmd = QString("Seek(%1, %2)").arg(pos) .arg((whence == SEEK_SET) ? "SEEK_SET" : ((whence == SEEK_CUR) ?"SEEK_CUR" : "SEEK_END")); LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO); } poslock.unlock(); generalWait.wakeAll(); return ret; }