From 9959d661a02b8d140dfc43b2aa1c6bfa40a5af18 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 19 Jul 2019 22:55:20 -0700 Subject: Restructure codec send/receive calls In particular, after an initial fill of the codec's internal buffer, each receive_frame call is followed by one or more send_packet calls. For asynchronous codecs, this has the effect of letting the codec work while the handler thread is waiting for an AVFrame structure to become available or waiting for more decoded data to be needed. For synchronous codecs, this makes the send_packet calls use up time that would be spent waiting. --- examples/alffplay.cpp | 139 +++++++++++++++++++++++--------------------------- 1 file changed, 64 insertions(+), 75 deletions(-) diff --git a/examples/alffplay.cpp b/examples/alffplay.cpp index 466c4576..1f2c7d6c 100644 --- a/examples/alffplay.cpp +++ b/examples/alffplay.cpp @@ -188,6 +188,21 @@ class PacketQueue { size_t mTotalSize{0}; bool mFinished{false}; + AVPacket *getPacket(std::unique_lock &lock) + { + while(mPackets.empty() && !mFinished) + mCondVar.wait(lock); + return mPackets.empty() ? nullptr : &mPackets.front(); + } + + void pop() + { + AVPacket *pkt = &mPackets.front(); + mTotalSize -= pkt->size; + av_packet_unref(pkt); + mPackets.pop_front(); + } + public: ~PacketQueue() { @@ -197,6 +212,22 @@ public: mTotalSize = 0; } + void sendTo(AVCodecContext *codecctx) + { + std::unique_lock lock{mMutex}; + AVPacket *lastpkt{}; + while((lastpkt=getPacket(lock)) != nullptr) + { + const int ret{avcodec_send_packet(codecctx, lastpkt)}; + if(ret == AVERROR(EAGAIN)) return; + if(ret < 0) + std::cerr<< "Failed to send packet: "< &lock) - { - while(mPackets.empty() && !mFinished) - mCondVar.wait(lock); - return mPackets.empty() ? nullptr : &mPackets.front(); - } - bool put(const AVPacket *pkt) { { @@ -232,16 +256,6 @@ public: mCondVar.notify_one(); return true; } - - void pop() - { - AVPacket *pkt = &mPackets.front(); - mTotalSize -= pkt->size; - av_packet_unref(pkt); - mPackets.pop_front(); - } - - std::mutex &getMutex() noexcept { return mMutex; } }; @@ -547,33 +561,21 @@ int AudioState::decodeFrame() { while(!mMovie.mQuit.load(std::memory_order_relaxed)) { - { - std::unique_lock lock{mPackets.getMutex()}; - AVPacket *lastpkt{}; - while((lastpkt=mPackets.getPacket(lock)) != nullptr) - { - const int ret{avcodec_send_packet(mCodecCtx.get(), lastpkt)}; - if(ret == AVERROR(EAGAIN)) break; - if(ret < 0) - std::cerr<< "Failed to send packet: "<nb_samples <= 0) - { - av_frame_unref(mDecodedFrame.get()); continue; - } /* If provided, update w/ pts */ if(mDecodedFrame->best_effort_timestamp != AV_NOPTS_VALUE) @@ -1006,6 +1008,9 @@ int AudioState::handler() #endif samples = av_malloc(buffer_len); + /* Prefill the codec buffer. */ + mPackets.sendTo(mCodecCtx.get()); + srclock.lock(); while(alGetError() == AL_NO_ERROR && !mMovie.mQuit.load(std::memory_order_relaxed) && mConnected.test_and_set(std::memory_order_relaxed)) @@ -1305,39 +1310,20 @@ int VideoState::handler() [](Picture &pict) -> void { pict.mFrame = AVFramePtr{av_frame_alloc()}; }); + /* Prefill the codec buffer. */ + mPackets.sendTo(mCodecCtx.get()); + while(!mMovie.mQuit.load(std::memory_order_relaxed)) { - { - std::unique_lock lock{mPackets.getMutex()}; - AVPacket *lastpkt{}; - while((lastpkt=mPackets.getPacket(lock)) != nullptr) - { - const int ret{avcodec_send_packet(mCodecCtx.get(), lastpkt)}; - if(ret == AVERROR(EAGAIN)) break; - if(ret < 0) - std::cerr<< "Failed to send packet: "<mFrame.get()}; + const int ret{avcodec_receive_frame(mCodecCtx.get(), decoded_frame)}; + if(ret == AVERROR_EOF) break; + if(ret == 0) { - size_t write_idx{mPictQWrite.load(std::memory_order_relaxed)}; - Picture *vp{&mPictQ[write_idx]}; - - /* Decode video frame. */ - AVFrame *decoded_frame{vp->mFrame.get()}; - const int ret{avcodec_receive_frame(mCodecCtx.get(), decoded_frame)}; - if(ret == AVERROR_EOF) goto finished; - if(ret == AVERROR(EAGAIN)) break; - if(ret < 0) - { - std::cerr<< "Failed to receive frame: "<best_effort_timestamp != AV_NOPTS_VALUE) mCurrentPts = std::chrono::duration_cast( @@ -1354,18 +1340,21 @@ int VideoState::handler() */ write_idx = (write_idx+1)%mPictQ.size(); mPictQWrite.store(write_idx, std::memory_order_release); + } + else if(ret != AVERROR(EAGAIN)) + std::cerr<< "Failed to receive frame: "< lock{mPictQMutex}; - while(write_idx == mPictQRead.load(std::memory_order_acquire) && - !mMovie.mQuit.load(std::memory_order_relaxed)) - mPictQCond.wait(lock); - } + mPackets.sendTo(mCodecCtx.get()); + + if(write_idx == mPictQRead.load(std::memory_order_acquire)) + { + /* Wait until we have space for a new pic */ + std::unique_lock lock{mPictQMutex}; + while(write_idx == mPictQRead.load(std::memory_order_acquire) && + !mMovie.mQuit.load(std::memory_order_relaxed)) + mPictQCond.wait(lock); } } -finished: mEOS = true; std::unique_lock lock{mPictQMutex}; -- cgit v1.2.3