From c49f136864225576813803cf65c7474a929845e4 Mon Sep 17 00:00:00 2001
From: Philippe Gorley <gorley.philippe@gmail.com>
Date: Fri, 30 Sep 2016 14:32:59 -0400
Subject: [PATCH] video: fix hardware acceleration bugs

Now correctly takes into account user setting. Will no longer try
to retrieve data from the GPU buffer if acceleration has failed.
Uses hardware acceleration when flushing the video stream.

Change-Id: Id7787a181b3822e8c7da0e8c2ce2cdfa302a3ddd
---
 src/media/media_decoder.cpp    | 25 +++++++++++++++++++------
 src/media/video/accel.cpp      | 10 ++++++----
 src/media/video/accel.h        |  2 +-
 src/media/video/v4l2/vaapi.cpp |  4 ++--
 4 files changed, 28 insertions(+), 13 deletions(-)

diff --git a/src/media/media_decoder.cpp b/src/media/media_decoder.cpp
index fdc54f795d..d939b99a49 100644
--- a/src/media/media_decoder.cpp
+++ b/src/media/media_decoder.cpp
@@ -96,7 +96,7 @@ int MediaDecoder::openInput(const DeviceParams& params)
     RING_DBG("Trying to open device %s with format %s, pixel format %s, size %dx%d, rate %lf", params.input.c_str(),
                                                         params.format.c_str(), params.pixel_format.c_str(), params.width, params.height, params.framerate.real());
 
-    enableAccel_ = (params.enableAccel == "1");
+    enableAccel_ = (params.enableAccel != "0");
 
     int ret = avformat_open_input(
         &inputCtx_,
@@ -267,8 +267,10 @@ int MediaDecoder::setupFromVideoData()
     decoderCtx_->thread_count = std::thread::hardware_concurrency();
 
 #ifdef RING_ACCEL
-    accel_ = video::makeHardwareAccel(decoderCtx_);
-    decoderCtx_->opaque = accel_.get();
+    if (enableAccel_) {
+        accel_ = video::makeHardwareAccel(decoderCtx_);
+        decoderCtx_->opaque = accel_.get();
+    }
 #endif // RING_ACCEL
 
     // find the decoder for the video stream
@@ -334,8 +336,12 @@ MediaDecoder::decode(VideoFrame& result)
     if (frameFinished) {
         frame->format = (AVPixelFormat) correctPixFmt(frame->format);
 #if defined(RING_VIDEO) && defined(RING_ACCEL)
-        if (accel_ && !accel_->extractData(decoderCtx_, result))
-            return Status::DecodeError;
+        if (accel_) {
+            if (!accel_->hasFailed())
+                accel_->extractData(decoderCtx_, result);
+            else
+                return Status::DecodeError;
+        }
 #endif // RING_ACCEL
         if (emulateRate_ and frame->pkt_pts != AV_NOPTS_VALUE) {
             auto frame_time = getTimeBase()*(frame->pkt_pts - avStream_->start_time);
@@ -418,8 +424,15 @@ MediaDecoder::flush(VideoFrame& result)
     if (len <= 0)
         return Status::DecodeError;
 
-    if (frameFinished)
+    if (frameFinished) {
+#ifdef RING_ACCEL
+        // flush is called when closing the stream
+        // so don't restart the media decoder
+        if (accel_ && !accel_->hasFailed())
+            accel_->extractData(decoderCtx_, result);
+#endif // RING_ACCEL
         return Status::FrameFinished;
+    }
 
     return Status::Success;
 }
diff --git a/src/media/video/accel.cpp b/src/media/video/accel.cpp
index 118a28e077..636c5bac20 100644
--- a/src/media/video/accel.cpp
+++ b/src/media/video/accel.cpp
@@ -57,7 +57,7 @@ getFormatCb(AVCodecContext* codecCtx, const AVPixelFormat* formats)
         }
     }
 
-    accel->fail(true);
+    accel->fail(codecCtx, true);
     RING_WARN("Falling back to software decoding");
     codecCtx->get_format = avcodec_default_get_format;
     codecCtx->get_buffer2 = avcodec_default_get_buffer2;
@@ -80,7 +80,7 @@ allocateBufferCb(AVCodecContext* codecCtx, AVFrame* frame, int flags)
             return 0;
         }
 
-        accel->fail();
+        accel->fail(codecCtx, false);
     }
 
     return avcodec_default_get_buffer2(codecCtx, frame, flags);
@@ -140,13 +140,15 @@ HardwareAccel::HardwareAccel(const AccelInfo& info)
 }
 
 void
-HardwareAccel::fail(bool forceFallback)
+HardwareAccel::fail(AVCodecContext* codecCtx, bool forceFallback)
 {
     ++failCount_;
     if (failCount_ >= MAX_ACCEL_FAILURES || forceFallback) {
+        RING_ERR("Hardware acceleration failure");
         fallback_ = true;
         failCount_ = 0;
-        // force reinit of media decoder to correctly set thread count
+        codecCtx->get_format = avcodec_default_get_format;
+        codecCtx->get_buffer2 = avcodec_default_get_buffer2;
     }
 }
 
diff --git a/src/media/video/accel.h b/src/media/video/accel.h
index f0507b6d22..fd6f9083a2 100644
--- a/src/media/video/accel.h
+++ b/src/media/video/accel.h
@@ -60,7 +60,7 @@ class HardwareAccel {
         void setHeight(int height) { height_ = height; }
         void setProfile(int profile) { profile_ = profile; }
 
-        void fail(bool forceFallback = false);
+        void fail(AVCodecContext* codecCtx, bool forceFallback);
         void succeed() { failCount_ = 0; } // call on success of allocateBuffer or extractData
 
     public: // must be implemented by derived classes
diff --git a/src/media/video/v4l2/vaapi.cpp b/src/media/video/v4l2/vaapi.cpp
index 6139e5abaa..3d3cec3b8d 100644
--- a/src/media/video/v4l2/vaapi.cpp
+++ b/src/media/video/v4l2/vaapi.cpp
@@ -83,7 +83,7 @@ VaapiAccel::extractData(AVCodecContext* codecCtx, VideoFrame& container)
         av_frame_unref(input);
         av_frame_move_ref(input, output);
     } catch (const std::runtime_error& e) {
-        fail();
+        fail(codecCtx, false);
         RING_ERR("%s", e.what());
         return false;
     }
@@ -117,7 +117,7 @@ VaapiAccel::init(AVCodecContext* codecCtx)
         { AV_CODEC_ID_H264, h264 },
         { AV_CODEC_ID_MPEG4, mpeg4 },
         { AV_CODEC_ID_H263, h263 },
-        { AV_CODEC_ID_H263P, h263 } // no clue if this'll work, #ffmpeg isn't answering me
+        { AV_CODEC_ID_H263P, h263 }
     };
 
     VAStatus status;
-- 
GitLab