From 5798349f9242fab652d1b1df1d630bade8896263 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 11 Jan 2020 03:20:46 +1100 Subject: [PATCH] began hybrid indexing system This index system will have awareness of the disk cache state and be able to fill in and store frames as necessary while keeping the disk cache under user defined limits. --- app/codec/ffmpeg/ffmpegdecoder.cpp | 144 +++++++++++++++++++++++++---- app/codec/ffmpeg/ffmpegdecoder.h | 15 ++- app/project/item/footage/stream.h | 3 + 3 files changed, 144 insertions(+), 18 deletions(-) diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 4d82434a8..de607b0ad 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -42,6 +42,11 @@ extern "C" { FFmpegDecoder::FFmpegDecoder() : fmt_ctx_(nullptr), codec_ctx_(nullptr), +#ifndef CACHE_EVERY_FRAME + scale_ctx_(nullptr), + pkt_(nullptr), + frame_(nullptr), +#endif opts_(nullptr) { } @@ -143,6 +148,22 @@ bool FFmpegDecoder::Open() // We should never get here, but just in case... qFatal("Invalid output format"); } + +#ifndef CACHE_EVERY_FRAME + scale_ctx_ = sws_getContext(avstream_->codecpar->width, + avstream_->codecpar->height, + static_cast(avstream_->codecpar->format), + avstream_->codecpar->width, + avstream_->codecpar->height, + ideal_pix_fmt_, + 0, + nullptr, + nullptr, + nullptr); + + pkt_ = av_packet_alloc(); + frame_ = av_frame_alloc(); +#endif } // All allocation succeeded so we set the state to open @@ -169,10 +190,11 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode) return nullptr; } +#ifdef CACHE_EVERY_FRAME QFile compressed_frame(GetIndexFilename().append(QString::number(target_ts))); if (compressed_frame.open(QFile::ReadOnly)) { // Read data - QByteArray frame_loader = qUncompress(compressed_frame.readAll()); + QByteArray frame_loader = compressed_frame.readAll(); // Frame was valid, now we convert it to a native Olive frame FramePtr frame_container = Frame::Create(); @@ -187,6 +209,66 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode) return frame_container; } +#else + int64_t second_ts = qRound64(av_q2d(av_inv_q(avstream_->time_base))); + + bool got_frame = (frame_->pts == target_ts); + + if (!got_frame) { + if (frame_->pts < target_ts - 2*second_ts || frame_->pts > target_ts) { + Seek(target_ts); + } + + int64_t seek_ts = target_ts; + + int ret; + + while (true) { + ret = GetFrame(pkt_, frame_); + + if (ret < 0) { + FFmpegError(ret); + break; + } + + if (frame_->pts > target_ts) { + // Seek failed, try again + seek_ts -= second_ts; + Seek(seek_ts); + continue; + } + + if (frame_->pts == target_ts) { + // We found the frame we want + got_frame = true; + break; + } + } + } + + if (got_frame) { + FramePtr output_frame = Frame::Create(); + output_frame->set_width(avstream_->codecpar->width); + output_frame->set_height(avstream_->codecpar->height); + output_frame->set_format(native_pix_fmt_); + output_frame->set_timestamp(Timecode::timestamp_to_time(target_ts, avstream_->time_base)); + output_frame->set_sample_aspect_ratio(av_guess_sample_aspect_ratio(fmt_ctx_, avstream_, nullptr)); + output_frame->allocate(); + + uint8_t* output_data = reinterpret_cast(output_frame->data()); + int output_linesize = output_frame->width() * kRGBAChannels * PixelService::BytesPerChannel(native_pix_fmt_); + + sws_scale(scale_ctx_, + frame_->data, + frame_->linesize, + 0, + frame_->height, + &output_data, + &output_linesize); + + return output_frame; + } +#endif return nullptr; } @@ -201,9 +283,7 @@ FramePtr FFmpegDecoder::RetrieveAudio(const rational &timecode, const rational & return nullptr; } - if (!LoadIndex()) { - Index(); - } + ValidateIndex(); Conform(params); @@ -233,17 +313,34 @@ void FFmpegDecoder::Close() { frame_index_.clear(); - if (opts_ != nullptr) { + if (opts_) { av_dict_free(&opts_); opts_ = nullptr; } - if (codec_ctx_ != nullptr) { +#ifndef CACHE_EVERY_FRAME + if (frame_) { + av_frame_free(&frame_); + frame_ = nullptr; + } + + if (pkt_) { + av_packet_free(&pkt_); + pkt_ = nullptr; + } + + if (scale_ctx_) { + sws_freeContext(scale_ctx_); + scale_ctx_ = nullptr; + } +#endif + + if (codec_ctx_) { avcodec_free_context(&codec_ctx_); codec_ctx_ = nullptr; } - if (fmt_ctx_ != nullptr) { + if (fmt_ctx_) { avformat_close_input(&fmt_ctx_); fmt_ctx_ = nullptr; } @@ -278,9 +375,7 @@ void FFmpegDecoder::Conform(const AudioRenderingParams ¶ms) return; } - if (!LoadIndex()) { - Index(); - } + ValidateIndex(); // Get indexed WAV file WaveInput input(GetIndexFilename()); @@ -502,10 +597,7 @@ bool FFmpegDecoder::Probe(Footage *f) Open(); // Use index to find duration - // FIXME: Does nothing for sound - if (!LoadIndex()) { - Index(); - } + ValidateIndex(); // Use last frame index as the duration // FIXME: Does this skip the last frame? @@ -618,6 +710,17 @@ QString FFmpegDecoder::GetConformedFilename(const AudioRenderingParams ¶ms) return index_fn; } +void FFmpegDecoder::ValidateIndex() +{ + stream()->index_lock_.lock(); + + if (!LoadIndex()) { + Index(); + } + + stream()->index_lock_.unlock(); +} + bool FFmpegDecoder::LoadIndex() { switch (avstream_->codecpar->codec_type) { @@ -776,6 +879,7 @@ void FFmpegDecoder::IndexVideo(AVPacket* pkt, AVFrame* frame) // Iterate through every single frame and get each timestamp // NOTE: Expects no frames to have been read so far +#ifdef CACHE_EVERY_FRAME SwsContext* scale_ctx = sws_getContext(avstream_->codecpar->width, avstream_->codecpar->height, static_cast(avstream_->codecpar->format), @@ -786,6 +890,7 @@ void FFmpegDecoder::IndexVideo(AVPacket* pkt, AVFrame* frame) nullptr, nullptr, nullptr); +#endif int ret; @@ -793,6 +898,7 @@ void FFmpegDecoder::IndexVideo(AVPacket* pkt, AVFrame* frame) ret = GetFrame(pkt, frame); if (ret >= 0) { +#ifdef CACHE_EVERY_FRAME // Save frame int buffer_size = PixelService::GetBufferSize(native_pix_fmt_, avstream_->codecpar->width, avstream_->codecpar->height); @@ -802,6 +908,7 @@ void FFmpegDecoder::IndexVideo(AVPacket* pkt, AVFrame* frame) int line_size = avstream_->codecpar->width * kRGBAChannels; // Perform pixel conversion + sws_scale(scale_ctx, frame->data, frame->linesize, @@ -812,9 +919,10 @@ void FFmpegDecoder::IndexVideo(AVPacket* pkt, AVFrame* frame) QFile compressed_frame(GetIndexFilename().append(QString::number(frame->pts))); if (compressed_frame.open(QFile::WriteOnly)) { - compressed_frame.write(qCompress(frame_save, 9)); + compressed_frame.write(frame_save); compressed_frame.close(); } +#endif frame_index_.append(frame->pts); } else { @@ -823,7 +931,9 @@ void FFmpegDecoder::IndexVideo(AVPacket* pkt, AVFrame* frame) } } +#ifdef CACHE_EVERY_FRAME sws_freeContext(scale_ctx); +#endif // Save index to file SaveIndex(); @@ -877,8 +987,8 @@ int FFmpegDecoder::GetFrame(AVPacket *pkt, AVFrame *frame) int64_t FFmpegDecoder::GetClosestTimestampInIndex(const int64_t &ts) { // Index now if we haven't already - if (frame_index_.isEmpty() && !LoadIndex()) { - Index(); + if (frame_index_.isEmpty()) { + ValidateIndex(); } if (frame_index_.isEmpty()) { diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index 8520062f8..57dc1d409 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -33,6 +33,8 @@ extern "C" { #include "codec/decoder.h" #include "codec/waveoutput.h" +//#define CACHE_EVERY_FRAME + /** * @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder */ @@ -117,6 +119,8 @@ private: */ QString GetConformedFilename(const AudioRenderingParams ¶ms); + void ValidateIndex(); + /** * @brief Used internally to load a frame index into frame_index_ * @@ -142,11 +146,20 @@ private: AVFormatContext* fmt_ctx_; AVCodecContext* codec_ctx_; AVStream* avstream_; - AVDictionary* opts_; AVPixelFormat ideal_pix_fmt_; PixelFormat::Format native_pix_fmt_; +#ifndef CACHE_EVERY_FRAME + SwsContext* scale_ctx_; + + AVPacket* pkt_; + + AVFrame* frame_; +#endif + + AVDictionary* opts_; + QVector frame_index_; }; diff --git a/app/project/item/footage/stream.h b/app/project/item/footage/stream.h index 51d33e2cc..ed08d811d 100644 --- a/app/project/item/footage/stream.h +++ b/app/project/item/footage/stream.h @@ -23,6 +23,7 @@ #include #include +#include #include #include "common/rational.h" @@ -97,6 +98,8 @@ public: StreamID ToID() const; + QMutex index_lock_; + protected: virtual void FootageSetEvent(Footage*){}