/*static*/ status_t FSUtils::CompareFileContent(BPositionIO& content1, BPositionIO& content2, bool& _equal) { // get and compare content size off_t size1; status_t error = content1.GetSize(&size1); if (error != B_OK) return error; off_t size2; error = content2.GetSize(&size2); if (error != B_OK) return error; if (size1 != size2) { _equal = false; return B_OK; } if (size1 == 0) { _equal = true; return B_OK; } // allocate a data buffer uint8* buffer1 = new(std::nothrow) uint8[2 * kCompareDataBufferSize]; if (buffer1 == NULL) return B_NO_MEMORY; ArrayDeleter<uint8> bufferDeleter(buffer1); uint8* buffer2 = buffer1 + kCompareDataBufferSize; // compare the data off_t offset = 0; while (offset < size1) { size_t toCompare = std::min(size_t(size1 - offset), kCompareDataBufferSize); ssize_t bytesRead = content1.ReadAt(offset, buffer1, toCompare); if (bytesRead < 0) return bytesRead; if ((size_t)bytesRead != toCompare) return B_ERROR; bytesRead = content2.ReadAt(offset, buffer2, toCompare); if (bytesRead < 0) return bytesRead; if ((size_t)bytesRead != toCompare) return B_ERROR; if (memcmp(buffer1, buffer2, toCompare) != 0) { _equal = false; return B_OK; } offset += bytesRead; } _equal = true; return B_OK; }
/*static*/ off_t AVFormatWriter::_Seek(void* cookie, off_t offset, int whence) { TRACE_IO("AVFormatWriter::_Seek(%p, %lld, %d)\n", cookie, offset, whence); AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie); BPositionIO* positionIO = dynamic_cast<BPositionIO*>(writer->fTarget); if (positionIO == NULL) return -1; // Support for special file size retrieval API without seeking anywhere: if (whence == AVSEEK_SIZE) { off_t size; if (positionIO->GetSize(&size) == B_OK) return size; return -1; } off_t position = positionIO->Seek(offset, whence); TRACE_IO(" position: %lld\n", position); if (position < 0) return -1; return position; }
SharedBitmap::SharedBitmap(BPositionIO& data) : BReferenceable(), fResourceID(-1), fBuffer(NULL), fSize(0), fMimeType() { status_t status = data.GetSize(&fSize); const off_t kMaxSize = 1024 * 1024; if (status == B_OK && fSize > 0 && fSize <= kMaxSize) { fBuffer = new(std::nothrow) uint8[fSize]; if (fBuffer != NULL) { data.Seek(0, SEEK_SET); off_t bytesRead = 0; size_t chunkSize = std::min((off_t)4096, fSize); while (bytesRead < fSize) { ssize_t read = data.Read(fBuffer + bytesRead, chunkSize); if (read > 0) bytesRead += read; else break; } if (bytesRead != fSize) { delete[] fBuffer; fBuffer = NULL; fSize = 0; } } else fSize = 0; } else { fprintf(stderr, "SharedBitmap(): Stream too large: %" B_PRIi64 ", max: %" B_PRIi64 "\n", fSize, kMaxSize); } fBitmap[0] = NULL; fBitmap[1] = NULL; fBitmap[2] = NULL; }
status_t StreamBase::Seek(uint32 flags, int64* frame, bigtime_t* time) { BAutolock _(fStreamLock); if (fContext == NULL || fStream == NULL) return B_NO_INIT; TRACE_SEEK("StreamBase::Seek(%ld,%s%s%s%s, %lld, " "%lld)\n", VirtualIndex(), (flags & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "", (flags & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "", (flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "", (flags & B_MEDIA_SEEK_CLOSEST_FORWARD) ? " B_MEDIA_SEEK_CLOSEST_FORWARD" : "", *frame, *time); double frameRate = FrameRate(); if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) { // Seeking is always based on time, initialize it when client seeks // based on frame. *time = (bigtime_t)(*frame * 1000000.0 / frameRate + 0.5); } int64_t timeStamp = *time; int searchFlags = AVSEEK_FLAG_BACKWARD; if ((flags & B_MEDIA_SEEK_CLOSEST_FORWARD) != 0) searchFlags = 0; if (fSeekByBytes) { searchFlags |= AVSEEK_FLAG_BYTE; BAutolock _(fSourceLock); int64_t fileSize; if (fSource->GetSize(&fileSize) != B_OK) return B_NOT_SUPPORTED; int64_t duration = Duration(); if (duration == 0) return B_NOT_SUPPORTED; timeStamp = int64_t(fileSize * ((double)timeStamp / duration)); if ((flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) != 0) { timeStamp -= 65536; if (timeStamp < 0) timeStamp = 0; } bool seekAgain = true; bool seekForward = true; bigtime_t lastFoundTime = -1; int64_t closestTimeStampBackwards = -1; while (seekAgain) { if (avformat_seek_file(fContext, -1, INT64_MIN, timeStamp, INT64_MAX, searchFlags) < 0) { TRACE(" avformat_seek_file() (by bytes) failed.\n"); return B_ERROR; } seekAgain = false; // Our last packet is toast in any case. Read the next one so we // know where we really seeked. fReusePacket = false; if (_NextPacket(true) == B_OK) { while (fPacket.pts == kNoPTSValue) { fReusePacket = false; if (_NextPacket(true) != B_OK) return B_ERROR; } if (fPacket.pos >= 0) timeStamp = fPacket.pos; bigtime_t foundTime = _ConvertFromStreamTimeBase(fPacket.pts); if (foundTime != lastFoundTime) { lastFoundTime = foundTime; if (foundTime > *time) { if (closestTimeStampBackwards >= 0) { timeStamp = closestTimeStampBackwards; seekAgain = true; seekForward = false; continue; } int64_t diff = int64_t(fileSize * ((double)(foundTime - *time) / (2 * duration))); if (diff < 8192) break; timeStamp -= diff; TRACE_SEEK(" need to seek back (%lld) (time: %.2f " "-> %.2f)\n", timeStamp, *time / 1000000.0, foundTime / 1000000.0); if (timeStamp < 0) foundTime = 0; else { seekAgain = true; continue; } } else if (seekForward && foundTime < *time - 100000) { closestTimeStampBackwards = timeStamp; int64_t diff = int64_t(fileSize * ((double)(*time - foundTime) / (2 * duration))); if (diff < 8192) break; timeStamp += diff; TRACE_SEEK(" need to seek forward (%lld) (time: " "%.2f -> %.2f)\n", timeStamp, *time / 1000000.0, foundTime / 1000000.0); if (timeStamp > duration) foundTime = duration; else { seekAgain = true; continue; } } } TRACE_SEEK(" found time: %lld -> %lld (%.2f)\n", *time, foundTime, foundTime / 1000000.0); *time = foundTime; *frame = (uint64)(*time * frameRate / 1000000LL + 0.5); TRACE_SEEK(" seeked frame: %lld\n", *frame); } else { TRACE_SEEK(" _NextPacket() failed!\n"); return B_ERROR; } } } else { // We may not get a PTS from the next packet after seeking, so // we try to get an expected time from the index. int64_t streamTimeStamp = _ConvertToStreamTimeBase(*time); int index = av_index_search_timestamp(fStream, streamTimeStamp, searchFlags); if (index < 0) { TRACE(" av_index_search_timestamp() failed\n"); } else { if (index > 0) { const AVIndexEntry& entry = fStream->index_entries[index]; streamTimeStamp = entry.timestamp; } else { // Some demuxers use the first index entry to store some // other information, like the total playing time for example. // Assume the timeStamp of the first entry is alays 0. // TODO: Handle start-time offset? streamTimeStamp = 0; } bigtime_t foundTime = _ConvertFromStreamTimeBase(streamTimeStamp); bigtime_t timeDiff = foundTime > *time ? foundTime - *time : *time - foundTime; if (timeDiff > 1000000 && (fStreamBuildsIndexWhileReading || index == fStream->nb_index_entries - 1)) { // If the stream is building the index on the fly while parsing // it, we only have entries in the index for positions already // decoded, i.e. we cannot seek into the future. In that case, // just assume that we can seek where we want and leave // time/frame unmodified. Since successfully seeking one time // will generate index entries for the seeked to position, we // need to remember this in fStreamBuildsIndexWhileReading, // since when seeking back there will be later index entries, // but we still want to ignore the found entry. fStreamBuildsIndexWhileReading = true; TRACE_SEEK(" Not trusting generic index entry. " "(Current count: %d)\n", fStream->nb_index_entries); } else { // If we found a reasonably time, write it into *time. // After seeking, we will try to read the sought time from // the next packet. If the packet has no PTS value, we may // still have a more accurate time from the index lookup. *time = foundTime; } } if (avformat_seek_file(fContext, -1, INT64_MIN, timeStamp, INT64_MAX, searchFlags) < 0) { TRACE(" avformat_seek_file() failed.\n"); // Try to fall back to av_seek_frame() timeStamp = _ConvertToStreamTimeBase(timeStamp); if (av_seek_frame(fContext, fStream->index, timeStamp, searchFlags) < 0) { TRACE(" avformat_seek_frame() failed as well.\n"); // Fall back to seeking to the beginning by bytes timeStamp = 0; if (av_seek_frame(fContext, fStream->index, timeStamp, AVSEEK_FLAG_BYTE) < 0) { TRACE(" avformat_seek_frame() by bytes failed as " "well.\n"); // Do not propagate error in any case. We fail if we can't // read another packet. } else *time = 0; } } // Our last packet is toast in any case. Read the next one so // we know where we really sought. bigtime_t foundTime = *time; fReusePacket = false; if (_NextPacket(true) == B_OK) { if (fPacket.pts != kNoPTSValue) foundTime = _ConvertFromStreamTimeBase(fPacket.pts); else TRACE_SEEK(" no PTS in packet after seeking\n"); } else TRACE_SEEK(" _NextPacket() failed!\n"); *time = foundTime; TRACE_SEEK(" sought time: %.2fs\n", *time / 1000000.0); *frame = (uint64)(*time * frameRate / 1000000.0 + 0.5); TRACE_SEEK(" sought frame: %lld\n", *frame); } return B_OK; }