Commit 12932733 authored by Philippe Gorley's avatar Philippe Gorley Committed by Adrien Béraud

accel: add hardware encoding

Adds VAAPI and VideoToolbox hardware encoders.

Abstracts hardware related field accesses from the encoder and decoder
to put them in HardwareAccel. They are freed in ~HardwareAccel.

Limits bitrate when hardware encoding h264, else it can easily go up to
a few MiB/s.

Change-Id: I7d847d8ab3e4c9692341f038ce6d5dd76562e606
parent fda668f9
......@@ -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
......
......@@ -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;
}
......
......@@ -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
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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
......@@ -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);
}
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment