* Seeking by bytes works mostly correctly now. In some
mpgs, the video does not recover, though. * Remember the last reported keyframe information, so we avoid rounding artifacts. Not as effective, since we cannot use stream time-base for seeking, but have to use it for finding the keyframes. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@38706 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
2e54e93fbf
commit
6df16a01ed
|
@ -129,10 +129,10 @@ public:
|
|||
const void** infoBuffer,
|
||||
size_t* infoSize) const;
|
||||
|
||||
status_t Seek(uint32 flags, int64* frame,
|
||||
bigtime_t* time);
|
||||
status_t FindKeyFrame(uint32 flags, int64* frame,
|
||||
bigtime_t* time) const;
|
||||
status_t Seek(uint32 flags, int64* frame,
|
||||
bigtime_t* time);
|
||||
|
||||
status_t GetNextChunk(const void** chunkBuffer,
|
||||
size_t* chunkSize,
|
||||
|
@ -180,6 +180,13 @@ private:
|
|||
|
||||
mutable bool fStreamBuildsIndexWhileReading;
|
||||
bool fSeekByBytes;
|
||||
|
||||
struct KeyframeInfo {
|
||||
bigtime_t time;
|
||||
int64 frame;
|
||||
int64 streamTimeStamp;
|
||||
};
|
||||
mutable KeyframeInfo fLastReportedKeyframe;
|
||||
};
|
||||
|
||||
|
||||
|
@ -202,6 +209,10 @@ AVFormatReader::StreamCookie::StreamCookie(BPositionIO* source,
|
|||
{
|
||||
memset(&fFormat, 0, sizeof(media_format));
|
||||
av_new_packet(&fPacket, 0);
|
||||
|
||||
fLastReportedKeyframe.time = 0;
|
||||
fLastReportedKeyframe.frame = 0;
|
||||
fLastReportedKeyframe.streamTimeStamp = 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -286,9 +297,10 @@ AVFormatReader::StreamCookie::Open()
|
|||
return B_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
fStreamBuildsIndexWhileReading
|
||||
= (inputFormat->flags & AVFMT_GENERIC_INDEX) != 0;
|
||||
fSeekByBytes = (inputFormat->flags & AVFMT_TS_DISCONT) != 0;
|
||||
fStreamBuildsIndexWhileReading
|
||||
= (inputFormat->flags & AVFMT_GENERIC_INDEX) != 0
|
||||
|| fSeekByBytes;
|
||||
|
||||
TRACE("AVFormatReader::StreamCookie::Open() - "
|
||||
"av_find_stream_info() success! Seeking by bytes: %d\n",
|
||||
|
@ -880,81 +892,6 @@ AVFormatReader::StreamCookie::GetStreamInfo(int64* frameCount,
|
|||
}
|
||||
|
||||
|
||||
status_t
|
||||
AVFormatReader::StreamCookie::Seek(uint32 flags, int64* frame,
|
||||
bigtime_t* time)
|
||||
{
|
||||
if (fContext == NULL || fStream == NULL)
|
||||
return B_NO_INIT;
|
||||
|
||||
TRACE_SEEK("AVFormatReader::StreamCookie::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);
|
||||
|
||||
// Seeking is always based on time, initialize it when client seeks
|
||||
// based on frame.
|
||||
double frameRate = FrameRate();
|
||||
if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0)
|
||||
*time = (bigtime_t)(*frame * 1000000.0 / frameRate + 0.5);
|
||||
|
||||
int64_t timeStamp = *time;
|
||||
int64_t minTimeStamp = timeStamp - 1000000;
|
||||
if (minTimeStamp < 0)
|
||||
minTimeStamp = 0;
|
||||
int64_t maxTimeStamp = timeStamp + 1000000;
|
||||
|
||||
TRACE_SEEK(" time: %.5fs -> %lld, current DTS: %lld (time_base: %d/%d)\n",
|
||||
*time / 1000000.0, timeStamp, fStream->cur_dts, fStream->time_base.num,
|
||||
fStream->time_base.den);
|
||||
|
||||
int searchFlags = AVSEEK_FLAG_BACKWARD;
|
||||
if ((flags & B_MEDIA_SEEK_CLOSEST_FORWARD) != 0)
|
||||
searchFlags = 0;
|
||||
|
||||
if (fSeekByBytes) {
|
||||
searchFlags |= AVSEEK_FLAG_BYTE;
|
||||
BAutolock _(fStreamLock);
|
||||
off_t fileSize;
|
||||
if (fSource->GetSize(&fileSize) != B_OK)
|
||||
return B_NOT_SUPPORTED;
|
||||
int64_t duration = Duration();
|
||||
if (duration == 0)
|
||||
return B_NOT_SUPPORTED;
|
||||
|
||||
timeStamp = fileSize * timeStamp / duration;
|
||||
minTimeStamp = INT64_MIN;
|
||||
maxTimeStamp = INT64_MAX;
|
||||
}
|
||||
|
||||
if (avformat_seek_file(fContext, -1, minTimeStamp, timeStamp,
|
||||
maxTimeStamp, searchFlags) < 0) {
|
||||
TRACE(" avformat_seek_file() failed.\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (fPacket.pts != kNoPTSValue)
|
||||
*time = _ConvertFromStreamTimeBase(fPacket.pts);
|
||||
TRACE_SEEK(" seeked time: %.2fs\n", *time / 1000000.0);
|
||||
if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) {
|
||||
*frame = *time * frameRate / 1000000LL + 0.5;
|
||||
TRACE_SEEK(" seeked frame: %lld\n", *frame);
|
||||
}
|
||||
} else {
|
||||
TRACE_SEEK(" _NextPacket() failed!\n");
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AVFormatReader::StreamCookie::FindKeyFrame(uint32 flags, int64* frame,
|
||||
bigtime_t* time) const
|
||||
|
@ -970,9 +907,6 @@ AVFormatReader::StreamCookie::FindKeyFrame(uint32 flags, int64* frame,
|
|||
(flags & B_MEDIA_SEEK_CLOSEST_FORWARD) ? " B_MEDIA_SEEK_CLOSEST_FORWARD" : "",
|
||||
*frame, *time);
|
||||
|
||||
if (fSeekByBytes)
|
||||
return B_OK;
|
||||
|
||||
double frameRate = FrameRate();
|
||||
if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0)
|
||||
*time = (bigtime_t)(*frame * 1000000.0 / frameRate + 0.5);
|
||||
|
@ -988,7 +922,7 @@ AVFormatReader::StreamCookie::FindKeyFrame(uint32 flags, int64* frame,
|
|||
|
||||
int index = av_index_search_timestamp(fStream, timeStamp, searchFlags);
|
||||
if (index < 0) {
|
||||
TRACE_FIND(" av_index_search_timestamp() failed.\n");
|
||||
TRACE(" av_index_search_timestamp() failed.\n");
|
||||
// Best is to assume we can somehow seek to the time/frame
|
||||
// and leave them as they are.
|
||||
} else {
|
||||
|
@ -1030,13 +964,182 @@ AVFormatReader::StreamCookie::FindKeyFrame(uint32 flags, int64* frame,
|
|||
} else
|
||||
*time = foundTime;
|
||||
}
|
||||
|
||||
|
||||
TRACE_FIND(" found time: %.2fs (%lld)\n", *time / 1000000.0, timeStamp);
|
||||
if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) {
|
||||
*frame = int64_t(*time * frameRate / 1000000.0 + 0.5);
|
||||
TRACE_FIND(" found frame: %lld\n", *frame);
|
||||
}
|
||||
|
||||
fLastReportedKeyframe.frame = *frame;
|
||||
fLastReportedKeyframe.time = *time;
|
||||
fLastReportedKeyframe.streamTimeStamp = *time;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AVFormatReader::StreamCookie::Seek(uint32 flags, int64* frame,
|
||||
bigtime_t* time)
|
||||
{
|
||||
if (fContext == NULL || fStream == NULL)
|
||||
return B_NO_INIT;
|
||||
|
||||
TRACE_SEEK("AVFormatReader::StreamCookie::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);
|
||||
|
||||
int64_t timeStamp;
|
||||
|
||||
// Seeking is always based on time, initialize it when client seeks
|
||||
// based on frame.
|
||||
double frameRate = FrameRate();
|
||||
if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) {
|
||||
*time = (bigtime_t)(*frame * 1000000.0 / frameRate + 0.5);
|
||||
if (fLastReportedKeyframe.frame == *frame)
|
||||
timeStamp = fLastReportedKeyframe.streamTimeStamp;
|
||||
else
|
||||
timeStamp = *time;
|
||||
} else {
|
||||
if (fLastReportedKeyframe.time == *time)
|
||||
timeStamp = fLastReportedKeyframe.streamTimeStamp;
|
||||
else
|
||||
timeStamp = *time;
|
||||
}
|
||||
|
||||
TRACE_SEEK(" time: %.5fs -> %lld, current DTS: %lld (time_base: %d/%d)\n",
|
||||
*time / 1000000.0, timeStamp, fStream->cur_dts, fStream->time_base.num,
|
||||
fStream->time_base.den);
|
||||
|
||||
int searchFlags = AVSEEK_FLAG_BACKWARD;
|
||||
if ((flags & B_MEDIA_SEEK_CLOSEST_FORWARD) != 0)
|
||||
searchFlags = 0;
|
||||
|
||||
if (fSeekByBytes) {
|
||||
searchFlags = AVSEEK_FLAG_BYTE;
|
||||
BAutolock _(fStreamLock);
|
||||
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));
|
||||
|
||||
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;
|
||||
if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) {
|
||||
*frame = *time * frameRate / 1000000LL + 0.5;
|
||||
TRACE_SEEK(" seeked frame: %lld\n", *frame);
|
||||
}
|
||||
} else {
|
||||
TRACE_SEEK(" _NextPacket() failed!\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
int64_t minTimeStamp = timeStamp - 1000000;
|
||||
if (minTimeStamp < 0)
|
||||
minTimeStamp = 0;
|
||||
int64_t maxTimeStamp = timeStamp + 1000000;
|
||||
|
||||
if (avformat_seek_file(fContext, -1, minTimeStamp, timeStamp,
|
||||
maxTimeStamp, searchFlags) < 0) {
|
||||
TRACE(" avformat_seek_file() failed.\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (fPacket.pts != kNoPTSValue) {
|
||||
*time = _ConvertFromStreamTimeBase(fPacket.pts);
|
||||
TRACE_SEEK(" seeked time: %.2fs\n", *time / 1000000.0);
|
||||
if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) {
|
||||
*frame = *time * frameRate / 1000000LL + 0.5;
|
||||
TRACE_SEEK(" seeked frame: %lld\n", *frame);
|
||||
}
|
||||
} else
|
||||
TRACE_SEEK(" no PTS in packet after seeking\n");
|
||||
} else
|
||||
TRACE_SEEK(" _NextPacket() failed!\n");
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue