diff --git a/contrib/src/ffmpeg/rules.mak b/contrib/src/ffmpeg/rules.mak index dd18fc40f289a564b685abd211bc2d38610a05d2..20b8a018f8104d72341b0162bf31f883c66457f8 100644 --- a/contrib/src/ffmpeg/rules.mak +++ b/contrib/src/ffmpeg/rules.mak @@ -177,7 +177,10 @@ FFMPEGCONF += \ --enable-hwaccel=mpeg4_vaapi \ --enable-hwaccel=h263_vaapi \ --enable-hwaccel=vp8_vaapi \ - --enable-hwaccel=mjpeg_vaapi + --enable-hwaccel=mjpeg_vaapi \ + --enable-encoder=h264_vaapi \ + --enable-encoder=vp8_vaapi \ + --enable-encoder=mjpeg_vaapi endif endif @@ -194,6 +197,7 @@ FFMPEGCONF += \ --enable-hwaccel=h263_videotoolbox \ --enable-hwaccel=h264_videotoolbox \ --enable-hwaccel=mpeg4_videotoolbox \ + --enable-encoder=h264_videotoolbox \ --disable-securetransport endif diff --git a/src/media/media_decoder.cpp b/src/media/media_decoder.cpp index 767e397ec06effb36c9dbc9f9b32d1c19471725e..40e2f05b410d14d7fa522535fe5140257ec182c4 100644 --- a/src/media/media_decoder.cpp +++ b/src/media/media_decoder.cpp @@ -61,10 +61,6 @@ MediaDecoder::MediaDecoder() : MediaDecoder::~MediaDecoder() { -#ifdef RING_ACCEL - if (decoderCtx_ && decoderCtx_->hw_device_ctx) - av_buffer_unref(&decoderCtx_->hw_device_ctx); -#endif if (decoderCtx_) avcodec_free_context(&decoderCtx_); if (inputCtx_) @@ -231,8 +227,11 @@ MediaDecoder::setupStream(AVMediaType mediaType) #ifdef RING_ACCEL if (mediaType == AVMEDIA_TYPE_VIDEO) { if (enableAccel_) { - accel_ = video::HardwareAccel::setupDecoder(decoderCtx_); - decoderCtx_->opaque = accel_.get(); + accel_ = video::HardwareAccel::setupDecoder(decoderCtx_->codec_id); + if (accel_) { + accel_->setDetails(decoderCtx_, &options_); + decoderCtx_->opaque = accel_.get(); + } } else if (Manager::instance().videoPreferences.getDecodingAccelerated()) { RING_WARN() << "Hardware decoding disabled because of previous failure"; } else { @@ -479,7 +478,7 @@ MediaDecoder::getStream(std::string name) const #ifdef RING_ACCEL // accel_ is null if not using accelerated codecs if (accel_) - ms.format = AV_PIX_FMT_NV12; // TODO option me! + ms.format = accel_->getSoftwareFormat(); #endif return ms; } diff --git a/src/media/media_encoder.cpp b/src/media/media_encoder.cpp index 0e38a413f902ba10dd97add85a38afe564aceff1..affc929814c50e47bb23ee64979e85893fc63787 100644 --- a/src/media/media_encoder.cpp +++ b/src/media/media_encoder.cpp @@ -25,9 +25,15 @@ #include "media_encoder.h" #include "media_buffer.h" +#include "client/ring_signal.h" #include "fileutils.h" -#include "string_utils.h" #include "logger.h" +#include "manager.h" +#include "string_utils.h" + +#ifdef RING_ACCEL +#include "video/accel.h" +#endif extern "C" { #include <libavutil/parseutils.h> @@ -171,6 +177,10 @@ MediaEncoder::openOutput(const std::string& filename, const std::string& format) avformat_alloc_output_context2(&outputCtx_, nullptr, nullptr, filename.c_str()); else avformat_alloc_output_context2(&outputCtx_, nullptr, format.c_str(), filename.c_str()); + +#ifdef RING_ACCEL + enableAccel_ = Manager::instance().videoPreferences.getEncodingAccelerated(); +#endif } int @@ -178,21 +188,44 @@ MediaEncoder::addStream(const SystemCodecInfo& systemCodecInfo) { AVCodec* outputCodec = nullptr; AVCodecContext* encoderCtx = nullptr; - /* find the video encoder */ - if (systemCodecInfo.avcodecId == AV_CODEC_ID_H263) - // For H263 encoding, we force the use of AV_CODEC_ID_H263P (H263-1998) - // H263-1998 can manage all frame sizes while H263 don't - // AV_CODEC_ID_H263 decoder will be used for decoding - outputCodec = avcodec_find_encoder(AV_CODEC_ID_H263P); - else - outputCodec = avcodec_find_encoder(static_cast<AVCodecID>(systemCodecInfo.avcodecId)); +#ifdef RING_ACCEL + if (systemCodecInfo.mediaType == MEDIA_VIDEO) { + if (enableAccel_) { + if (accel_ = video::HardwareAccel::setupEncoder( + static_cast<AVCodecID>(systemCodecInfo.avcodecId), device_.width, device_.height)) { + outputCodec = avcodec_find_encoder_by_name(accel_->getCodecName().c_str()); + } + } else { + RING_WARN() << "Hardware encoding disabled"; + } + } +#endif + if (!outputCodec) { - RING_ERR("Encoder \"%s\" not found!", systemCodecInfo.name.c_str()); - throw MediaEncoderException("No output encoder"); + /* find the video encoder */ + if (systemCodecInfo.avcodecId == AV_CODEC_ID_H263) + // For H263 encoding, we force the use of AV_CODEC_ID_H263P (H263-1998) + // H263-1998 can manage all frame sizes while H263 don't + // AV_CODEC_ID_H263 decoder will be used for decoding + outputCodec = avcodec_find_encoder(AV_CODEC_ID_H263P); + else + outputCodec = avcodec_find_encoder(static_cast<AVCodecID>(systemCodecInfo.avcodecId)); + if (!outputCodec) { + RING_ERR("Encoder \"%s\" not found!", systemCodecInfo.name.c_str()); + throw MediaEncoderException("No output encoder"); + } } encoderCtx = prepareEncoderContext(outputCodec, systemCodecInfo.mediaType == MEDIA_VIDEO); encoders_.push_back(encoderCtx); + +#ifdef RING_ACCEL + if (accel_) { + accel_->setDetails(encoderCtx, &options_); + encoderCtx->opaque = accel_.get(); + } +#endif + auto maxBitrate = 1000 * std::atoi(libav_utils::getDictValue(options_, "max_rate")); auto bufSize = 2 * maxBitrate; // as recommended (TODO: make it customizable) auto crf = std::atoi(libav_utils::getDictValue(options_, "crf")); @@ -201,6 +234,12 @@ MediaEncoder::addStream(const SystemCodecInfo& systemCodecInfo) if (systemCodecInfo.avcodecId == AV_CODEC_ID_H264) { auto profileLevelId = libav_utils::getDictValue(options_, "parameters"); extractProfileLevelID(profileLevelId, encoderCtx); +#ifdef RING_ACCEL + if (accel_) + // limit the bitrate else it will easily go up to a few MiB/s + encoderCtx->bit_rate = maxBitrate; + else +#endif forcePresetX264(encoderCtx); // For H264 : // Streaming => VBV (constrained encoding) + CRF (Constant Rate Factor) @@ -274,7 +313,15 @@ MediaEncoder::addStream(const SystemCodecInfo& systemCodecInfo) // allocate buffers for both scaled (pre-encoder) and encoded frames const int width = encoderCtx->width; const int height = encoderCtx->height; - const int format = encoderCtx->pix_fmt; + int format = encoderCtx->pix_fmt; +#ifdef RING_ACCEL + if (accel_) { + // hardware encoders require a specific pixel format + auto desc = av_pix_fmt_desc_get(encoderCtx->pix_fmt); + if (desc && (desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) + format = accel_->getSoftwareFormat(); + } +#endif scaledFrameBufferSize_ = videoFrameSize(format, width, height); if (scaledFrameBufferSize_ < 0) throw MediaEncoderException(("Could not compute buffer size: " + libav_utils::getError(scaledFrameBufferSize_)).c_str()); @@ -339,7 +386,11 @@ MediaEncoder::encode(VideoFrame& input, bool is_keyframe, scaler_.scale_with_aspect(input, scaledFrame_); - auto frame = scaledFrame_.pointer(); + // Copy frame so the VideoScaler can still use the software frame (input) + VideoFrame copy; + copy.copyFrom(scaledFrame_); + + auto frame = copy.pointer(); AVCodecContext* enc = encoders_[currentStreamIdx_]; // ideally, time base is the inverse of framerate, but this may not always be the case if (enc->framerate.num == enc->time_base.den && enc->framerate.den == enc->time_base.num) @@ -355,6 +406,19 @@ MediaEncoder::encode(VideoFrame& input, bool is_keyframe, frame->key_frame = 0; } +#ifdef RING_ACCEL + // NOTE needs to be at same scope as call to encode + std::unique_ptr<VideoFrame> framePtr; + if (accel_) { + framePtr = accel_->transfer(copy); + if (!framePtr) { + RING_ERR() << "Hardware encoding failure"; + return -1; + } + frame = framePtr->pointer(); + } +#endif + return encode(frame, currentStreamIdx_); } #endif // RING_VIDEO @@ -465,10 +529,7 @@ MediaEncoder::prepareEncoderContext(AVCodec* outputCodec, bool is_video) { AVCodecContext* encoderCtx = avcodec_alloc_context3(outputCodec); - auto encoderName = encoderCtx->av_class->item_name ? - encoderCtx->av_class->item_name(encoderCtx) : nullptr; - if (encoderName == nullptr) - encoderName = "encoder?"; + auto encoderName = outputCodec->name; // guaranteed to be non null if AVCodec is not null encoderCtx->thread_count = std::min(std::thread::hardware_concurrency(), is_video ? 16u : 4u); RING_DBG("[%s] Using %d threads", encoderName, encoderCtx->thread_count); @@ -506,7 +567,11 @@ MediaEncoder::prepareEncoderContext(AVCodec* outputCodec, bool is_video) // emit one intra frame every gop_size frames encoderCtx->max_b_frames = 0; - encoderCtx->pix_fmt = AV_PIX_FMT_YUV420P; // TODO: option me ! + encoderCtx->pix_fmt = AV_PIX_FMT_YUV420P; +#ifdef RING_ACCEL + if (accel_) + encoderCtx->pix_fmt = accel_->getFormat(); +#endif // Fri Jul 22 11:37:59 EDT 2011:tmatth:XXX: DON'T set this, we want our // pps and sps to be sent in-band for RTP @@ -617,6 +682,20 @@ MediaEncoder::useCodec(const ring::AccountCodecInfo* codec) const noexcept return codec_.get() == codec; } +#ifdef RING_ACCEL +void +MediaEncoder::enableAccel(bool enableAccel) +{ + enableAccel_ = enableAccel; + emitSignal<DRing::ConfigurationSignal::HardwareEncodingChanged>(enableAccel_); + if (!enableAccel_) { + accel_.reset(); + for (auto enc : encoders_) + enc->opaque = nullptr; + } +} +#endif + unsigned MediaEncoder::getStreamCount() const { @@ -637,7 +716,12 @@ MediaEncoder::getStream(const std::string& name, int streamIdx) const return {}; auto enc = encoders_[streamIdx]; // TODO set firstTimestamp - return MediaStream(name, enc); + auto ms = MediaStream(name, enc); +#ifdef RING_ACCEL + if (accel_) + ms.format = accel_->getSoftwareFormat(); +#endif + return ms; } void diff --git a/src/media/media_encoder.h b/src/media/media_encoder.h index 9487a69e8dd711c80935286e47a2e7de1f83185b..0a6d598de6a9a710309880a8b147b04b35f70eae 100644 --- a/src/media/media_encoder.h +++ b/src/media/media_encoder.h @@ -50,6 +50,12 @@ namespace ring { struct MediaDescription; struct AccountCodecInfo; +#ifdef RING_ACCEL +namespace video { +class HardwareAccel; +} +#endif + class MediaEncoderException : public std::runtime_error { public: MediaEncoderException(const char *msg) : std::runtime_error(msg) {} @@ -94,6 +100,10 @@ public: bool useCodec(const AccountCodecInfo* codec) const noexcept; +#ifdef RING_ACCEL + void enableAccel(bool enableAccel); +#endif + unsigned getStreamCount() const; MediaStream getStream(const std::string& name, int streamIdx = -1) const; @@ -116,6 +126,11 @@ private: std::vector<uint8_t> scaledFrameBuffer_; int scaledFrameBufferSize_ = 0; +#ifdef RING_ACCEL + bool enableAccel_ = true; + std::unique_ptr<video::HardwareAccel> accel_; +#endif + protected: void readConfig(AVDictionary** dict, AVCodecContext* encoderCtx); AVDictionary *options_ = nullptr; diff --git a/src/media/video/accel.cpp b/src/media/video/accel.cpp index f1c3a010ce0717b5f241a21b7949fc7571632235..1f0502ef2d1e7d616caa97ee7965b0a516ae7e68 100644 --- a/src/media/video/accel.cpp +++ b/src/media/video/accel.cpp @@ -37,6 +37,7 @@ struct HardwareAPI { std::string name; AVPixelFormat format; + AVPixelFormat swFormat; std::vector<AVCodecID> supportedCodecs; }; @@ -52,7 +53,7 @@ getFormatCb(AVCodecContext* codecCtx, const AVPixelFormat* formats) // found hardware format for codec with api RING_DBG() << "Found compatible hardware format for " << avcodec_get_name(static_cast<AVCodecID>(accel->getCodecId())) - << " with " << accel->getName(); + << " decoder with " << accel->getName(); return formats[i]; } } @@ -61,54 +62,146 @@ getFormatCb(AVCodecContext* codecCtx, const AVPixelFormat* formats) return fallback; } -HardwareAccel::HardwareAccel(AVCodecID id, const std::string& name, AVPixelFormat format) +HardwareAccel::HardwareAccel(AVCodecID id, const std::string& name, AVPixelFormat format, AVPixelFormat swFormat, CodecType type) : id_(id) , name_(name) , format_(format) + , swFormat_(swFormat) + , type_(type) {} +HardwareAccel::~HardwareAccel() +{ + if (deviceCtx_) + av_buffer_unref(&deviceCtx_); + if (framesCtx_) + av_buffer_unref(&framesCtx_); +} + +std::string +HardwareAccel::getCodecName() const +{ + if (type_ == CODEC_DECODER) { + return avcodec_get_name(id_); + } else if (type_ == CODEC_ENCODER) { + std::stringstream ss; + ss << avcodec_get_name(id_) << '_' << name_; + return ss.str(); + } + return ""; +} + std::unique_ptr<VideoFrame> HardwareAccel::transfer(const VideoFrame& frame) { - auto input = frame.pointer(); - if (input->format != format_) { - RING_ERR("Frame format mismatch: expected %s, got %s", - av_get_pix_fmt_name(static_cast<AVPixelFormat>(format_)), - av_get_pix_fmt_name(static_cast<AVPixelFormat>(input->format))); + int ret = 0; + if (type_ == CODEC_DECODER) { + auto input = frame.pointer(); + if (input->format != format_) { + RING_ERR() << "Frame format mismatch: expected " + << av_get_pix_fmt_name(format_) << ", got " + << av_get_pix_fmt_name(static_cast<AVPixelFormat>(input->format)); + return nullptr; + } + + return transferToMainMemory(frame, AV_PIX_FMT_NV12); + } else if (type_ == CODEC_ENCODER) { + auto input = frame.pointer(); + if (input->format != swFormat_) { + RING_ERR() << "Frame format mismatch: expected " + << av_get_pix_fmt_name(swFormat_) << ", got " + << av_get_pix_fmt_name(static_cast<AVPixelFormat>(input->format)); + return nullptr; + } + + auto framePtr = std::make_unique<VideoFrame>(); + auto hwFrame = framePtr->pointer(); + + if ((ret = av_hwframe_get_buffer(framesCtx_, hwFrame, 0)) < 0) { + RING_ERR() << "Failed to allocate hardware buffer: " << libav_utils::getError(ret); + return nullptr; + } + + if (!hwFrame->hw_frames_ctx) { + RING_ERR() << "Failed to allocate hardware buffer: Cannot allocate memory"; + return nullptr; + } + + if ((ret = av_hwframe_transfer_data(hwFrame, input, 0)) < 0) { + RING_ERR() << "Failed to push frame to GPU: " << libav_utils::getError(ret); + return nullptr; + } + + hwFrame->pts = input->pts; // transfer does not copy timestamp + return framePtr; + } else { + RING_ERR() << "Invalid hardware accelerator"; return nullptr; } +} - return transferToMainMemory(frame, AV_PIX_FMT_NV12); +void +HardwareAccel::setDetails(AVCodecContext* codecCtx, AVDictionary** /*d*/) +{ + if (type_ == CODEC_DECODER) { + codecCtx->hw_device_ctx = av_buffer_ref(deviceCtx_); + codecCtx->get_format = getFormatCb; + codecCtx->thread_safe_callbacks = 1; + } else if (type_ == CODEC_ENCODER) { + codecCtx->hw_device_ctx = av_buffer_ref(deviceCtx_); + codecCtx->hw_frames_ctx = av_buffer_ref(framesCtx_); + } } -static int -initDevice(const HardwareAPI& api, AVCodecContext* codecCtx) +bool +HardwareAccel::initDevice() { int ret = 0; - AVBufferRef* hardwareDeviceCtx = nullptr; - auto hwType = av_hwdevice_find_type_by_name(api.name.c_str()); + auto hwType = av_hwdevice_find_type_by_name(name_.c_str()); #ifdef HAVE_VAAPI_ACCEL_DRM // default DRM device may not work on multi GPU computers, so check all possible values - if (api.name == "vaapi") { + if (name_ == "vaapi") { const std::string path = "/dev/dri/"; auto files = ring::fileutils::readDirectory(path); // renderD* is preferred over card* std::sort(files.rbegin(), files.rend()); for (auto& entry : files) { std::string deviceName = path + entry; - if ((ret = av_hwdevice_ctx_create(&hardwareDeviceCtx, hwType, deviceName.c_str(), nullptr, 0)) >= 0) { - codecCtx->hw_device_ctx = hardwareDeviceCtx; - return ret; + if ((ret = av_hwdevice_ctx_create(&deviceCtx_, hwType, deviceName.c_str(), nullptr, 0)) >= 0) { + return true; } } } #endif // default device (nullptr) works for most cases - if ((ret = av_hwdevice_ctx_create(&hardwareDeviceCtx, hwType, nullptr, nullptr, 0)) >= 0) { - codecCtx->hw_device_ctx = hardwareDeviceCtx; + ret = av_hwdevice_ctx_create(&deviceCtx_, hwType, nullptr, nullptr, 0); + return ret >= 0; +} + +bool +HardwareAccel::initFrame(int width, int height) +{ + int ret = 0; + if (!deviceCtx_) { + RING_ERR() << "Cannot initialize hardware frames without a valid hardware device"; + return false; } - return ret; + framesCtx_ = av_hwframe_ctx_alloc(deviceCtx_); + if (!framesCtx_) + return false; + + auto ctx = reinterpret_cast<AVHWFramesContext*>(framesCtx_->data); + ctx->format = format_; + ctx->sw_format = swFormat_; + ctx->width = width; + ctx->height = height; + ctx->initial_pool_size = 20; // TODO try other values + + if ((ret = av_hwframe_ctx_init(framesCtx_)) < 0) + av_buffer_unref(&framesCtx_); + + return ret >= 0; } std::unique_ptr<VideoFrame> @@ -137,25 +230,50 @@ HardwareAccel::transferToMainMemory(const VideoFrame& frame, AVPixelFormat desir } std::unique_ptr<HardwareAccel> -HardwareAccel::setupDecoder(AVCodecContext* codecCtx) +HardwareAccel::setupDecoder(AVCodecID id) { static const HardwareAPI apiList[] = { - { "vaapi", AV_PIX_FMT_VAAPI, { AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4, AV_CODEC_ID_VP8, AV_CODEC_ID_MJPEG } }, - { "vdpau", AV_PIX_FMT_VDPAU, { AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4 } }, - { "videotoolbox", AV_PIX_FMT_VIDEOTOOLBOX, { AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4 } }, + { "vaapi", AV_PIX_FMT_VAAPI, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4, AV_CODEC_ID_VP8, AV_CODEC_ID_MJPEG } }, + { "vdpau", AV_PIX_FMT_VDPAU, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4 } }, + { "videotoolbox", AV_PIX_FMT_VIDEOTOOLBOX, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4 } }, }; for (const auto& api : apiList) { - if (std::find(api.supportedCodecs.begin(), api.supportedCodecs.end(), codecCtx->codec_id) != api.supportedCodecs.end()) { - if (initDevice(api, codecCtx) >= 0) { - codecCtx->get_format = getFormatCb; - codecCtx->thread_safe_callbacks = 1; - RING_DBG() << "Attempting to use hardware accelerated decoding with " << api.name; - return std::make_unique<HardwareAccel>(codecCtx->codec_id, api.name, api.format); + if (std::find(api.supportedCodecs.begin(), api.supportedCodecs.end(), id) != api.supportedCodecs.end()) { + auto accel = std::make_unique<HardwareAccel>(id, api.name, api.format, api.swFormat, CODEC_DECODER); + if (accel->initDevice()) { + RING_DBG() << "Attempting to use hardware decoder " << accel->getCodecName() << " with " << api.name; + return accel; + } + } + } + + return nullptr; +} + +std::unique_ptr<HardwareAccel> +HardwareAccel::setupEncoder(AVCodecID id, int width, int height) +{ + static const HardwareAPI apiList[] = { + { "vaapi", AV_PIX_FMT_VAAPI, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_MJPEG, AV_CODEC_ID_VP8 } }, + { "videotoolbox", AV_PIX_FMT_VIDEOTOOLBOX, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264 } }, + }; + + for (auto api : apiList) { + const auto& it = std::find(api.supportedCodecs.begin(), api.supportedCodecs.end(), id); + if (it != api.supportedCodecs.end()) { + auto accel = std::make_unique<HardwareAccel>(id, api.name, api.format, api.swFormat, CODEC_ENCODER); + const auto& codecName = accel->getCodecName(); + if (avcodec_find_encoder_by_name(codecName.c_str())) { + if (accel->initDevice() && accel->initFrame(width, height)) { + RING_DBG() << "Attempting to use hardware encoder " << codecName; + return accel; + } } } } + RING_WARN() << "Not using hardware encoding"; return nullptr; } diff --git a/src/media/video/accel.h b/src/media/video/accel.h index e0f06855ac3e1883deea47b84398bcaaa5b964a5..85fdbe88c620ee37bf6e8b7e60f280512c4609b5 100644 --- a/src/media/video/accel.h +++ b/src/media/video/accel.h @@ -21,6 +21,7 @@ #pragma once #include "libav_deps.h" +#include "media_codec.h" #include <memory> #include <string> @@ -36,7 +37,12 @@ public: /** * Static factory method for hardware decoding. */ - static std::unique_ptr<HardwareAccel> setupDecoder(AVCodecContext* codecCtx); + static std::unique_ptr<HardwareAccel> setupDecoder(AVCodecID id); + + /** + * Static factory method for hardware encoding. + */ + static std::unique_ptr<HardwareAccel> setupEncoder(AVCodecID id, int width, int height); /** * Transfers a hardware decoded frame back to main memory. Should be called after @@ -50,25 +56,72 @@ public: /** * Made public so std::unique_ptr can access it. Should not be called. */ - HardwareAccel(AVCodecID id, const std::string& name, AVPixelFormat format); + HardwareAccel(AVCodecID id, const std::string& name, AVPixelFormat format, AVPixelFormat swFormat, CodecType type); + /** + * Dereferences hardware contexts. + */ + ~HardwareAccel(); + + /** + * Codec that is being accelerated. + */ AVCodecID getCodecId() const { return id_; }; + + /** + * Name of the hardware layer/API being used. + */ std::string getName() const { return name_; }; + + /** + * Hardware format. + */ AVPixelFormat getFormat() const { return format_; }; + /** + * Software format. For encoding it is the format expected by the hardware. For decoding + * it is the format output by the hardware. + */ + AVPixelFormat getSoftwareFormat() const { return swFormat_; } + + /** + * Gets the name of the codec. + * Decoding: equivalent to avcodec_get_name(id_) + * Encoding: avcodec_get_name(id_) + '_' + name_ + */ + std::string getCodecName() const; + + /** + * Set some extra details in the codec context. Should be called after a successful + * setup (setupDecoder or setupEncoder). + * For decoding, sets the hw_device_ctx and get_format callback. For encoding, sets + * hw_device_ctx and hw_frames_ctx, and may set some hardware specific options in + * the dictionary. + */ + void setDetails(AVCodecContext* codecCtx, AVDictionary** d); + /** * Transfers a hardware decoded frame back to main memory. Should be called after - * the frame is decoded using avcodec_send_packet/avcodec_receive_frame. + * the frame is decoded using avcodec_send_packet/avcodec_receive_frame or before + * the frame is encoded using avcodec_send_frame/avcodec_receive_packet. * - * @frame: Refrerence to the decoded hardware frame. - * @returns: Software frame. + * @frame: Hardware frame when decoding, software frame when encoding. + * @returns: Software frame when decoding, hardware frame when encoding. */ std::unique_ptr<VideoFrame> transfer(const VideoFrame& frame); private: - AVCodecID id_; + bool initDevice(); + bool initFrame(int width, int height); + + AVCodecID id_ {AV_CODEC_ID_NONE}; std::string name_; - AVPixelFormat format_; + AVPixelFormat format_ {AV_PIX_FMT_NONE}; + AVPixelFormat swFormat_ {AV_PIX_FMT_NONE}; + CodecType type_ {CODEC_NONE}; + + AVBufferRef* deviceCtx_ {nullptr}; + AVBufferRef* framesCtx_ {nullptr}; }; }} // namespace ring::video diff --git a/src/preferences.cpp b/src/preferences.cpp index b421996f6c886ec1d54ae9f5d0ef9311e194ad2d..30349a2962b7c688897fb3018a17b81b9562cda8 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -138,6 +138,7 @@ static const char * const TOGGLE_PICKUP_HANGUP_SHORT_KEY = "togglePickupHangup"; // video preferences constexpr const char * const VideoPreferences::CONFIG_LABEL; static const char * const DECODING_ACCELERATED_KEY = "decodingAccelerated"; +static const char * const ENCODING_ACCELERATED_KEY = "encodingAccelerated"; #endif static const char * const DFT_PULSE_LENGTH_STR = "250"; /** Default DTMF length */ @@ -562,6 +563,7 @@ void ShortcutPreferences::unserialize(const YAML::Node &in) #ifdef RING_VIDEO VideoPreferences::VideoPreferences() : decodingAccelerated_(true) + , encodingAccelerated_(false) { } @@ -570,6 +572,7 @@ void VideoPreferences::serialize(YAML::Emitter &out) out << YAML::Key << CONFIG_LABEL << YAML::Value << YAML::BeginMap; #ifdef RING_ACCEL out << YAML::Key << DECODING_ACCELERATED_KEY << YAML::Value << decodingAccelerated_; + out << YAML::Key << ENCODING_ACCELERATED_KEY << YAML::Value << encodingAccelerated_; #endif getVideoDeviceMonitor().serialize(out); out << YAML::EndMap; @@ -582,7 +585,11 @@ void VideoPreferences::unserialize(const YAML::Node &in) // value may or may not be present try { parseValue(node, DECODING_ACCELERATED_KEY, decodingAccelerated_); - } catch (...) { decodingAccelerated_ = true; } + parseValue(node, ENCODING_ACCELERATED_KEY, encodingAccelerated_); + } catch (...) { + decodingAccelerated_ = true; + encodingAccelerated_ = false; + } #endif getVideoDeviceMonitor().unserialize(in); } diff --git a/src/preferences.h b/src/preferences.h index cf68a3bc9a52973a997b957b98e4f0f8855f0b40..4e6b170e3719cc6edce42a4abc89b60ed495fa0c 100644 --- a/src/preferences.h +++ b/src/preferences.h @@ -466,8 +466,18 @@ class VideoPreferences : public Serializable { emitSignal<DRing::ConfigurationSignal::HardwareDecodingChanged>(decodingAccelerated_); } + bool getEncodingAccelerated() const { + return encodingAccelerated_; + } + + void setEncodingAccelerated(bool encodingAccelerated) { + encodingAccelerated_ = encodingAccelerated; + emitSignal<DRing::ConfigurationSignal::HardwareEncodingChanged>(encodingAccelerated_); + } + private: bool decodingAccelerated_; + bool encodingAccelerated_; constexpr static const char* const CONFIG_LABEL = "video"; }; #endif // RING_VIDEO