ffmpegdecoder: improved index reliability
This commit is contained in:
@@ -292,31 +292,7 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode)
|
||||
input_linesize[i] = frame_->linesize[i];
|
||||
}
|
||||
|
||||
QFile save_frame(GetIndexFilename().append(QString::number(frame_->pts)));
|
||||
if (save_frame.open(QFile::WriteOnly)) {
|
||||
|
||||
// Save frame to media index
|
||||
int cached_buffer_sz = av_image_get_buffer_size(static_cast<AVPixelFormat>(frame_->format),
|
||||
frame_->width,
|
||||
frame_->height,
|
||||
1);
|
||||
|
||||
QByteArray cached_frame(cached_buffer_sz, Qt::Uninitialized);
|
||||
|
||||
av_image_copy_to_buffer(reinterpret_cast<uint8_t*>(cached_frame.data()),
|
||||
cached_frame.size(),
|
||||
frame_->data,
|
||||
frame_->linesize,
|
||||
static_cast<AVPixelFormat>(frame_->format),
|
||||
frame_->width,
|
||||
frame_->height,
|
||||
1);
|
||||
|
||||
save_frame.write(qCompress(cached_frame, 1));
|
||||
save_frame.close();
|
||||
|
||||
DiskManager::instance()->CreatedFile(save_frame.fileName(), QByteArray());
|
||||
}
|
||||
CacheFrameToDisk(frame_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -887,6 +863,8 @@ void FFmpegDecoder::UnconditionalVideoIndex(AVPacket* pkt, AVFrame* frame)
|
||||
ret = GetFrame(pkt, frame);
|
||||
|
||||
if (ret >= 0) {
|
||||
//CacheFrameToDisk(frame);
|
||||
|
||||
video_stream->append_frame_index(frame->pts);
|
||||
} else {
|
||||
// Assume we've reached the end of the file
|
||||
@@ -894,6 +872,8 @@ void FFmpegDecoder::UnconditionalVideoIndex(AVPacket* pkt, AVFrame* frame)
|
||||
}
|
||||
}
|
||||
|
||||
video_stream->append_frame_index(VideoStream::kEndTimestamp);
|
||||
|
||||
// Save index to file
|
||||
if (!video_stream->save_frame_index(GetIndexFilename())) {
|
||||
qWarning() << QStringLiteral("Failed to save index for %1").arg(stream()->footage()->filename());
|
||||
@@ -953,25 +933,19 @@ int64_t FFmpegDecoder::GetClosestTimestampInIndex(const int64_t &ts)
|
||||
|
||||
bool index_is_being_created = false;
|
||||
|
||||
// Check if the frame index has been populated
|
||||
if (video_stream->is_frame_index_empty()) {
|
||||
// Check if an index is being created right now
|
||||
if (video_stream->index_process_lock()->tryLock()) {
|
||||
|
||||
// If not, check if one is being created right now
|
||||
if (video_stream->index_process_lock()->tryLock()) {
|
||||
|
||||
// If not, make an index
|
||||
// If not, check if the frame index has been populated
|
||||
if (!video_stream->is_frame_index_ready()) {
|
||||
// If not, make a frame index
|
||||
ValidateVideoIndex();
|
||||
|
||||
video_stream->index_process_lock()->unlock();
|
||||
|
||||
// If the index is still empty, the video must just be empty
|
||||
if (video_stream->is_frame_index_empty()) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// The index is being created in another thread, wait until we have more information
|
||||
index_is_being_created = true;
|
||||
}
|
||||
|
||||
video_stream->index_process_lock()->unlock();
|
||||
|
||||
} else {
|
||||
index_is_being_created = true;
|
||||
}
|
||||
|
||||
int64_t closest_ts = -1;
|
||||
@@ -987,7 +961,7 @@ int64_t FFmpegDecoder::GetClosestTimestampInIndex(const int64_t &ts)
|
||||
|
||||
closest_ts = video_stream->get_closest_timestamp_in_frame_index(ts);
|
||||
|
||||
} while (index_is_being_created);
|
||||
} while (closest_ts < 0 && index_is_being_created);
|
||||
|
||||
return closest_ts;
|
||||
}
|
||||
@@ -996,11 +970,11 @@ void FFmpegDecoder::ValidateVideoIndex()
|
||||
{
|
||||
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(stream());
|
||||
|
||||
if (video_stream->is_frame_index_empty()) {
|
||||
if (!video_stream->is_frame_index_ready()) {
|
||||
video_stream->load_frame_index(GetIndexFilename());
|
||||
}
|
||||
|
||||
if (video_stream->is_frame_index_empty()) {
|
||||
if (!video_stream->is_frame_index_ready()) {
|
||||
// Reset state
|
||||
Seek(0);
|
||||
|
||||
@@ -1015,3 +989,32 @@ void FFmpegDecoder::Seek(int64_t timestamp)
|
||||
avcodec_flush_buffers(codec_ctx_);
|
||||
av_seek_frame(fmt_ctx_, avstream_->index, timestamp, AVSEEK_FLAG_BACKWARD);
|
||||
}
|
||||
|
||||
void FFmpegDecoder::CacheFrameToDisk(AVFrame *f)
|
||||
{
|
||||
QFile save_frame(GetIndexFilename().append(QString::number(f->pts)));
|
||||
if (save_frame.open(QFile::WriteOnly)) {
|
||||
|
||||
// Save frame to media index
|
||||
int cached_buffer_sz = av_image_get_buffer_size(static_cast<AVPixelFormat>(f->format),
|
||||
f->width,
|
||||
f->height,
|
||||
1);
|
||||
|
||||
QByteArray cached_frame(cached_buffer_sz, Qt::Uninitialized);
|
||||
|
||||
av_image_copy_to_buffer(reinterpret_cast<uint8_t*>(cached_frame.data()),
|
||||
cached_frame.size(),
|
||||
f->data,
|
||||
f->linesize,
|
||||
static_cast<AVPixelFormat>(f->format),
|
||||
f->width,
|
||||
f->height,
|
||||
1);
|
||||
|
||||
save_frame.write(qCompress(cached_frame, 1));
|
||||
save_frame.close();
|
||||
|
||||
DiskManager::instance()->CreatedFile(save_frame.fileName(), QByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,8 @@ private:
|
||||
|
||||
void Seek(int64_t timestamp);
|
||||
|
||||
void CacheFrameToDisk(AVFrame* f);
|
||||
|
||||
AVFormatContext* fmt_ctx_;
|
||||
AVCodecContext* codec_ctx_;
|
||||
AVStream* avstream_;
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
#include <QFile>
|
||||
|
||||
const int64_t VideoStream::kEndTimestamp = AV_NOPTS_VALUE;
|
||||
|
||||
VideoStream::VideoStream()
|
||||
{
|
||||
set_type(kVideo);
|
||||
@@ -48,12 +50,22 @@ int64_t VideoStream::get_closest_timestamp_in_frame_index(const int64_t &ts)
|
||||
{
|
||||
QMutexLocker locker(&index_access_lock_);
|
||||
|
||||
if (frame_index_.isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ts <= 0) {
|
||||
return frame_index_.first();
|
||||
}
|
||||
|
||||
int index_size = frame_index_.size();
|
||||
|
||||
if (frame_index_.last() == kEndTimestamp) {
|
||||
index_size--;
|
||||
}
|
||||
|
||||
// Use index to find closest frame in file
|
||||
for (int i=0;i<frame_index_.size();i++) {
|
||||
for (int i=0;i<index_size;i++) {
|
||||
int64_t this_ts = frame_index_.at(i);
|
||||
|
||||
if (this_ts == ts) {
|
||||
@@ -63,7 +75,13 @@ int64_t VideoStream::get_closest_timestamp_in_frame_index(const int64_t &ts)
|
||||
}
|
||||
}
|
||||
|
||||
return frame_index_.last();
|
||||
if (frame_index_.last() == kEndTimestamp) {
|
||||
// Index is done
|
||||
return frame_index_.last();
|
||||
} else {
|
||||
// Index is not done yet
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoStream::clear_frame_index()
|
||||
@@ -80,11 +98,11 @@ void VideoStream::append_frame_index(const int64_t &ts)
|
||||
frame_index_.append(ts);
|
||||
}
|
||||
|
||||
bool VideoStream::is_frame_index_empty()
|
||||
bool VideoStream::is_frame_index_ready()
|
||||
{
|
||||
QMutexLocker locker(&index_access_lock_);
|
||||
|
||||
return frame_index_.isEmpty();
|
||||
return !frame_index_.isEmpty() && frame_index_.last() == VideoStream::kEndTimestamp;
|
||||
}
|
||||
|
||||
int64_t VideoStream::last_frame_index_timestamp()
|
||||
@@ -110,6 +128,8 @@ bool VideoStream::load_frame_index(const QString &s)
|
||||
index_file.size());
|
||||
|
||||
index_file.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -127,6 +147,7 @@ bool VideoStream::save_frame_index(const QString &s)
|
||||
frame_index_.size() * static_cast<int>(sizeof(int64_t)));
|
||||
|
||||
index_file.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ class VideoStream : public ImageStream
|
||||
public:
|
||||
VideoStream();
|
||||
|
||||
static const int64_t kEndTimestamp;
|
||||
|
||||
virtual QString description() const override;
|
||||
|
||||
/**
|
||||
@@ -41,7 +43,8 @@ public:
|
||||
int64_t get_closest_timestamp_in_frame_index(const int64_t& ts);
|
||||
void clear_frame_index();
|
||||
void append_frame_index(const int64_t& ts);
|
||||
bool is_frame_index_empty();
|
||||
//bool is_frame_index_empty();
|
||||
bool is_frame_index_ready();
|
||||
int64_t last_frame_index_timestamp();
|
||||
|
||||
bool load_frame_index(const QString& s);
|
||||
|
||||
@@ -75,6 +75,7 @@ void TaskManager::CreateInstance()
|
||||
void TaskManager::DestroyInstance()
|
||||
{
|
||||
delete instance_;
|
||||
instance_ = nullptr;
|
||||
}
|
||||
|
||||
TaskManager *TaskManager::instance()
|
||||
|
||||
Reference in New Issue
Block a user