diff --git a/app/decoder/ffmpeg/ffmpegdecoder.cpp b/app/decoder/ffmpeg/ffmpegdecoder.cpp index 36661a702..02f58a3e3 100644 --- a/app/decoder/ffmpeg/ffmpegdecoder.cpp +++ b/app/decoder/ffmpeg/ffmpegdecoder.cpp @@ -36,6 +36,8 @@ FFmpegDecoder::FFmpegDecoder() : fmt_ctx_(nullptr), codec_ctx_(nullptr), opts_(nullptr), + frame_(nullptr), + pkt_(nullptr), scale_ctx_(nullptr), resample_ctx_(nullptr) { @@ -156,6 +158,25 @@ bool FFmpegDecoder::Open() // FIXME: Fill this in } + // Allocate a packet for reading + pkt_ = av_packet_alloc(); + + // Handle failure to create packet + if (pkt_ == nullptr) { + Error(tr("Failed to allocate AVPacket")); + return false; + } + + // Allocate a frame for decoding + frame_ = av_frame_alloc(); + + // Handle failure to create frame + if (frame_ == nullptr) { + Error(tr("Failed to allocate AVFrame")); + return false; + } + + // All allocation succeeded so we set the state to open open_ = true; return true; @@ -170,6 +191,24 @@ FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &lengt // Convert timecode to AVStream timebase int64_t target_ts = qFloor(timecode.toDouble() * rational(avstream_->time_base).flipped().toDouble()); + // Index now if we haven't already + if (frame_index_.isEmpty()) { + qDebug() << "No index exists... starting index"; + Index(); + } + + qDebug() << "Graph requested timecode:" << target_ts; + + // Use index to find closest frame in file + for (int i=1;i target_ts) { + target_ts = frame_index_.at(i - 1); + break; + } + } + + qDebug() << " The closest in stream is:" << target_ts; + // Seek to it avcodec_flush_buffers(codec_ctx_); av_seek_frame(fmt_ctx_, avstream_->index, target_ts, AVSEEK_FLAG_BACKWARD); @@ -178,68 +217,30 @@ FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &lengt AVPacket pkt; av_init_packet(&pkt); - // Allocate a new frame to place decoded data - AVFrame* dec_frame = av_frame_alloc(); - // Cache FFmpeg error code returns - int ret; - - // Variable set if FFmpeg signals file has finished - bool eof = false; + int ret = 0; // FFmpeg frame retrieve loop - while ((ret = avcodec_receive_frame(codec_ctx_, dec_frame)) == AVERROR(EAGAIN) && !eof) { + while (ret >= 0 && frame_->pts != target_ts) { + ret = GetFrame(); - av_frame_unref(dec_frame); - - // Find next packet in the correct stream index - do { - // Free buffer in packet if there is one - if (pkt.buf != nullptr) { - av_packet_unref(&pkt); - } - - ret = av_read_frame(fmt_ctx_, &pkt); - } while (pkt.stream_index != avstream_->index && ret >= 0); - - if (ret == AVERROR_EOF) { - // Don't break so that receive gets called again, but don't try to read again - eof = true; - - // Send a null packet to signal end of - avcodec_send_packet(codec_ctx_, nullptr); - } else if (ret < 0) { - // Handle other error - break; - } else { - // Successful read, send the packet - ret = avcodec_send_packet(codec_ctx_, &pkt); - - // We don't need the packet anymore, so free it - // FIXME: Is this true??? - av_packet_unref(&pkt); - - if (ret < 0) { - break; - } - } + qDebug() << " Read frame" << frame_->pts; } - //qDebug() << dec_frame->pts << ","; + qDebug() << " The frame we have is:" << frame_->pts; // Handle any errors received during the frame retrieve process if (ret < 0) { qWarning() << tr("Failed to retrieve frame from FFmpeg decoder: %1").arg(ret); - av_frame_free(&dec_frame); return nullptr; } // Frame was valid, now we create an Olive frame to place the data into FramePtr frame_container = std::make_shared(); - frame_container->set_width(dec_frame->width); - frame_container->set_height(dec_frame->height); + frame_container->set_width(frame_->width); + frame_container->set_height(frame_->height); frame_container->set_format(output_fmt_); // FIXME: Hardcoded value - frame_container->set_timestamp(rational(dec_frame->pts * avstream_->time_base.num, avstream_->time_base.den)); + frame_container->set_timestamp(rational(frame_->pts * avstream_->time_base.num, avstream_->time_base.den)); frame_container->allocate(); // Convert pixel format/linesize if necessary @@ -248,16 +249,13 @@ FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &lengt // Perform pixel conversion sws_scale(scale_ctx_, - dec_frame->data, - dec_frame->linesize, + frame_->data, + frame_->linesize, 0, - dec_frame->height, + frame_->height, &dst_data, &dst_linesize); - // Don't need AVFrame anymore - av_frame_free(&dec_frame); - Q_UNUSED(timecode) Q_UNUSED(length) @@ -268,6 +266,8 @@ FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &lengt void FFmpegDecoder::Close() { + frame_index_.clear(); + if (scale_ctx_ != nullptr) { sws_freeContext(scale_ctx_); scale_ctx_ = nullptr; @@ -278,6 +278,16 @@ void FFmpegDecoder::Close() resample_ctx_ = nullptr; } + if (pkt_ != nullptr) { + av_packet_free(&pkt_); + pkt_ = nullptr; + } + + if (frame_ != nullptr) { + av_frame_free(&frame_); + frame_ = nullptr; + } + if (opts_ != nullptr) { av_dict_free(&opts_); opts_ = nullptr; @@ -410,6 +420,73 @@ void FFmpegDecoder::Error(const QString &s) Close(); } +void FFmpegDecoder::Index() +{ + // This should be unnecessary, but just in case... + frame_index_.clear(); + + // Iterate through every single frame and get each timestamp + // NOTE: Expects no frames to have been read so far + + int ret = 0; + + while (true) { + ret = GetFrame(); + + if (ret < 0) { + break; + } else { + frame_index_.append(frame_->pts); + qDebug() << " Indexed" << frame_->pts; + } + } +} + +int FFmpegDecoder::GetFrame() +{ + bool eof = false; + + int ret; + + // Clear any previous frames + av_frame_unref(frame_); + + while ((ret = avcodec_receive_frame(codec_ctx_, frame_)) == AVERROR(EAGAIN) && !eof) { + + // Find next packet in the correct stream index + do { + // Free buffer in packet if there is one + av_packet_unref(pkt_); + + // Read packet from file + ret = av_read_frame(fmt_ctx_, pkt_); + } while (pkt_->stream_index != avstream_->index && ret >= 0); + + if (ret == AVERROR_EOF) { + // Don't break so that receive gets called again, but don't try to read again + eof = true; + + // Send a null packet to signal end of + avcodec_send_packet(codec_ctx_, nullptr); + } else if (ret < 0) { + // Handle other error + break; + } else { + // Successful read, send the packet + ret = avcodec_send_packet(codec_ctx_, pkt_); + + // We don't need the packet anymore, so free it + av_packet_unref(pkt_); + + if (ret < 0) { + break; + } + } + } + + return ret; +} + AVPixelFormat FFmpegDecoder::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt) { AVPixelFormat possible_pix_fmts[] = { diff --git a/app/decoder/ffmpeg/ffmpegdecoder.h b/app/decoder/ffmpeg/ffmpegdecoder.h index 3c84e1041..543f9e400 100644 --- a/app/decoder/ffmpeg/ffmpegdecoder.h +++ b/app/decoder/ffmpeg/ffmpegdecoder.h @@ -49,12 +49,17 @@ private: void FFmpegErr(int error_code); void Error(const QString& s); + void Index(); + int GetFrame(); + AVPixelFormat GetCompatiblePixelFormat(const AVPixelFormat& pix_fmt); AVFormatContext* fmt_ctx_; AVCodecContext* codec_ctx_; AVStream* avstream_; AVDictionary* opts_; + AVFrame* frame_; + AVPacket* pkt_; SwsContext* scale_ctx_; SwrContext* resample_ctx_;