diff --git a/src/media/media_decoder.cpp b/src/media/media_decoder.cpp index 4c68aaee1668a8fbc4fdabcee00b728efb82c37e..2006644e620d9b689acf117b78a650e8b05cc1d4 100644 --- a/src/media/media_decoder.cpp +++ b/src/media/media_decoder.cpp @@ -279,20 +279,7 @@ MediaDecoder::decode(VideoFrame& result) if (frameFinished) { frame->format = (AVPixelFormat) correctPixFmt(frame->format); -#ifdef RING_ACCEL - if (!accel_.name.empty()) { - ret = video::transferFrameData(accel_, decoderCtx_, result); - if (ret < 0) { - ++accelFailures_; - if (accelFailures_ >= MAX_ACCEL_FAILURES) { - RING_ERR("Hardware decoding failure"); - accelFailures_ = 0; // reset error count for next time - fallback_ = true; - return Status::RestartRequired; - } - } - } -#endif + auto packetTimestamp = frame->pts; // in stream time base frame->pts = av_rescale_q_rnd(av_gettime() - startTime_, {1, AV_TIME_BASE}, decoderCtx_->time_base, @@ -385,10 +372,11 @@ MediaDecoder::enableAccel(bool enableAccel) emitSignal<DRing::ConfigurationSignal::HardwareDecodingChanged>(enableAccel_); if (!enableAccel) { accel_ = {}; - if (decoderCtx_->hw_device_ctx) - av_buffer_unref(&decoderCtx_->hw_device_ctx); - if (decoderCtx_) + if (decoderCtx_) { + if (decoderCtx_->hw_device_ctx) + av_buffer_unref(&decoderCtx_->hw_device_ctx); decoderCtx_->opaque = nullptr; + } } } #endif @@ -413,12 +401,6 @@ MediaDecoder::flush(VideoFrame& result) if (frameFinished) { av_packet_unref(&inpacket); -#ifdef RING_ACCEL - // flush is called when closing the stream - // so don't restart the media decoder - if (!accel_.name.empty() && accelFailures_ < MAX_ACCEL_FAILURES) - video::transferFrameData(accel_, decoderCtx_, result); -#endif return Status::FrameFinished; } diff --git a/src/media/media_recorder.cpp b/src/media/media_recorder.cpp index 1b466dec061a269637728c97694dd32aba8cd0bd..6b25378753a7c7e049136be9a59d950f51c6cb96 100644 --- a/src/media/media_recorder.cpp +++ b/src/media/media_recorder.cpp @@ -26,6 +26,9 @@ #include "media_recorder.h" #include "system_codec_container.h" #include "thread_pool.h" +#ifdef RING_ACCEL +#include "video/accel.h" +#endif #include <algorithm> #include <iomanip> @@ -165,14 +168,25 @@ MediaRecorder::onFrame(const std::string& name, const std::shared_ptr<MediaFrame return; // copy frame to not mess with the original frame's pts (does not actually copy frame data) - MediaFrame clone; + std::unique_ptr<MediaFrame> clone; const auto& ms = streams_[name]->info; - clone.copyFrom(*frame); - clone.pointer()->pts -= ms.firstTimestamp; + if (ms.isVideo) { +#ifdef RING_ACCEL + clone = video::transferToMainMemory(*std::static_pointer_cast<VideoFrame>(frame), + static_cast<AVPixelFormat>(ms.format)); +#else + clone = std::make_unique<MediaFrame>(); + clone->copyFrom(*frame); +#endif + } else { + clone = std::make_unique<MediaFrame>(); + clone->copyFrom(*frame); + } + clone->pointer()->pts -= ms.firstTimestamp; if (ms.isVideo) - videoFilter_->feedInput(clone.pointer(), name); + videoFilter_->feedInput(clone->pointer(), name); else - audioFilter_->feedInput(clone.pointer(), name); + audioFilter_->feedInput(clone->pointer(), name); } int diff --git a/src/media/video/accel.cpp b/src/media/video/accel.cpp index 7600a795f75f89c7fd40f024a21c3b9c35610f3c..13e14293d1027e44dfef84fe52fc823481bba1bf 100644 --- a/src/media/video/accel.cpp +++ b/src/media/video/accel.cpp @@ -69,22 +69,37 @@ transferFrameData(HardwareAccel accel, AVCodecContext* /*codecCtx*/, VideoFrame& return -1; } - // FFmpeg requires a second frame in which to transfer the data from the GPU buffer to the main memory - auto container = std::unique_ptr<VideoFrame>(new VideoFrame()); - auto output = container->pointer(); + auto output = transferToMainMemory(frame, AV_PIX_FMT_NV12); + if (!output) + return -1; - auto pts = input->pts; - // most hardware accelerations output NV12, so skip extra conversions - output->format = AV_PIX_FMT_NV12; - int ret = av_hwframe_transfer_data(output, input, 0); - output->pts = pts; + frame.copyFrom(*output); // copy to input so caller receives extracted image data + return 0; +} - // move output into input so the caller receives extracted image data - // but we have to delete input's data first - av_frame_unref(input); - av_frame_move_ref(input, output); +std::unique_ptr<VideoFrame> +transferToMainMemory(const VideoFrame& frame, AVPixelFormat desiredFormat) +{ + auto input = frame.pointer(); + auto out = std::make_unique<VideoFrame>(); - return ret; + auto desc = av_pix_fmt_desc_get(static_cast<AVPixelFormat>(input->format)); + if (desc && not (desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { + out->copyFrom(frame); + return out; + } + + auto output = out->pointer(); + output->format = desiredFormat; + + int ret = av_hwframe_transfer_data(output, input, 0); + if (ret < 0) { + out->copyFrom(frame); + return out; + } + + output->pts = input->pts; + return out; } static int diff --git a/src/media/video/accel.h b/src/media/video/accel.h index a15b3ad271a4f1291282ebfd9d4aa74db8aa2249..d90af46855de398601dc3103d65b8c13e6255a35 100644 --- a/src/media/video/accel.h +++ b/src/media/video/accel.h @@ -35,5 +35,6 @@ struct HardwareAccel { const HardwareAccel setupHardwareDecoding(AVCodecContext* codecCtx); int transferFrameData(HardwareAccel accel, AVCodecContext* codecCtx, VideoFrame& frame); +std::unique_ptr<VideoFrame> transferToMainMemory(const VideoFrame& frame, AVPixelFormat desiredFormat); }} // namespace ring::video diff --git a/src/media/video/sinkclient.cpp b/src/media/video/sinkclient.cpp index ae3d37c93345c6119f2e031e46b80098ee0b42e4..9d84983d6c534fa2c17bd3d44b1cf7768d69c999 100644 --- a/src/media/video/sinkclient.cpp +++ b/src/media/video/sinkclient.cpp @@ -39,6 +39,10 @@ #include "video_scaler.h" #include "smartools.h" +#ifdef RING_ACCEL +#include "accel.h" +#endif + #ifndef _WIN32 #include <sys/mman.h> #endif @@ -85,7 +89,7 @@ class ShmHolder return openedName_; } - void renderFrame(VideoFrame& src) noexcept; + void renderFrame(const VideoFrame& src) noexcept; private: bool resizeArea(std::size_t desired_length) noexcept; @@ -220,7 +224,7 @@ ShmHolder::resizeArea(std::size_t frameSize) noexcept } void -ShmHolder::renderFrame(VideoFrame& src) noexcept +ShmHolder::renderFrame(const VideoFrame& src) noexcept { const auto width = src.width(); const auto height = src.height(); @@ -332,35 +336,46 @@ SinkClient::update(Observable<std::shared_ptr<MediaFrame>>* /*obs*/, } #endif -#if HAVE_SHM - shm_->renderFrame(f); -#endif - if (avTarget_.push) { auto outFrame = std::make_unique<VideoFrame>(); outFrame->copyFrom(f); avTarget_.push(std::move(outFrame)); } - if (target_.pull) { - VideoFrame dst; - const int width = f.width(); - const int height = f.height(); + + bool doTransfer = (target_.pull != nullptr); +#if HAVE_SHM + doTransfer |= (shm_ != nullptr); +#endif + + if (doTransfer) { +#ifdef RING_ACCEL + auto framePtr = transferToMainMemory(f, AV_PIX_FMT_NV12); + const auto& swFrame = *framePtr; +#else + const auto& swFrame = f; +#endif +#if HAVE_SHM + shm_->renderFrame(swFrame); +#endif + if (target_.pull) { + VideoFrame dst; + const int width = swFrame.width(); + const int height = swFrame.height(); #if defined(__ANDROID__) || (defined(__APPLE__) && !TARGET_OS_IPHONE) - const int format = AV_PIX_FMT_RGBA; + const int format = AV_PIX_FMT_RGBA; #else - const int format = AV_PIX_FMT_BGRA; + const int format = AV_PIX_FMT_BGRA; #endif - - const auto bytes = videoFrameSize(format, width, height); - - if (bytes > 0) { - if (auto buffer_ptr = target_.pull(bytes)) { - buffer_ptr->format = format; - buffer_ptr->width = width; - buffer_ptr->height = height; - dst.setFromMemory(buffer_ptr->ptr, format, width, height); - scaler_->scale(f, dst); - target_.push(std::move(buffer_ptr)); + const auto bytes = videoFrameSize(format, width, height); + if (bytes > 0) { + if (auto buffer_ptr = target_.pull(bytes)) { + buffer_ptr->format = format; + buffer_ptr->width = width; + buffer_ptr->height = height; + dst.setFromMemory(buffer_ptr->ptr, format, width, height); + scaler_->scale(swFrame, dst); + target_.push(std::move(buffer_ptr)); + } } } } diff --git a/src/media/video/video_mixer.cpp b/src/media/video/video_mixer.cpp index 1700fb20fc74a6054f0e5218e0c4ee17222c0548..f4e434d2be38ae03b7325635ef635bea0e03a303 100644 --- a/src/media/video/video_mixer.cpp +++ b/src/media/video/video_mixer.cpp @@ -26,6 +26,9 @@ #include "manager.h" #include "sinkclient.h" #include "logger.h" +#ifdef RING_ACCEL +#include "accel.h" +#endif #include <cmath> #include <unistd.h> @@ -169,6 +172,13 @@ VideoMixer::render_frame(VideoFrame& output, const VideoFrame& input, int index) if (!width_ or !height_ or !input.pointer()) return; +#ifdef RING_ACCEL + auto framePtr = transferToMainMemory(input, AV_PIX_FMT_NV12); + const auto& swFrame = *framePtr; +#else + const auto& swFrame = input; +#endif + const int n = sources_.size(); const int zoom = ceil(sqrt(n)); int cell_width = width_ / zoom; @@ -176,7 +186,7 @@ VideoMixer::render_frame(VideoFrame& output, const VideoFrame& input, int index) int xoff = (index % zoom) * cell_width; int yoff = (index / zoom) * cell_height; - scaler_.scale_and_pad(input, output, xoff, yoff, cell_width, cell_height, true); + scaler_.scale_and_pad(swFrame, output, xoff, yoff, cell_width, cell_height, true); } void diff --git a/src/media/video/video_sender.cpp b/src/media/video/video_sender.cpp index 3a62163a7abb694fb2886a3c3bb345e22889fca6..1ffa256c2bf395df265713a330b3c0d3cdac87fb 100644 --- a/src/media/video/video_sender.cpp +++ b/src/media/video/video_sender.cpp @@ -27,6 +27,10 @@ #include "logger.h" #include "manager.h" #include "smartools.h" +#include "sip/sipcall.h" +#ifdef RING_ACCEL +#include "accel.h" +#endif #include <map> #include <unistd.h> @@ -84,7 +88,13 @@ VideoSender::encodeAndSendVideo(VideoFrame& input_frame) if (is_keyframe) --forceKeyFrame_; - if (videoEncoder_->encode(input_frame, is_keyframe, frameNumber_++) < 0) +#ifdef RING_ACCEL + auto framePtr = transferToMainMemory(input_frame, AV_PIX_FMT_NV12); + auto& swFrame = *framePtr; +#else + auto& swFrame = input_frame; +#endif + if (videoEncoder_->encode(swFrame, is_keyframe, frameNumber_++) < 0) RING_ERR("encoding failed"); } }