9#include "h264vaapiencoder_p.h"
14#include <libavcodec/avcodec.h>
15#include <libavfilter/buffersink.h>
16#include <libavfilter/buffersrc.h>
19#include "logging_record.h"
21H264VAAPIEncoder::H264VAAPIEncoder(H264Profile profile, PipeWireProduce *produce)
22 : HardwareEncoder(produce)
27bool H264VAAPIEncoder::initialize(
const QSize &size)
29 if (!createDrmContext(size)) {
33 m_avFilterGraph = avfilter_graph_alloc();
34 if (!m_avFilterGraph) {
35 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Could not create filter graph";
39 int ret = avfilter_graph_create_filter(&m_inputFilter,
40 avfilter_get_by_name(
"buffer"),
42 "width=1:height=1:pix_fmt=drm_prime:time_base=1/1",
46 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Failed to create the buffer filter";
50 auto parameters = av_buffersrc_parameters_alloc();
52 qFatal(
"Failed to allocate memory");
55 parameters->format = AV_PIX_FMT_DRM_PRIME;
56 parameters->width = size.
width();
57 parameters->height = size.
height();
58 parameters->time_base = {1, 1000};
59 parameters->hw_frames_ctx = m_drmFramesContext;
61 av_buffersrc_parameters_set(m_inputFilter, parameters);
65 ret = avfilter_graph_create_filter(&m_outputFilter, avfilter_get_by_name(
"buffersink"),
"out",
nullptr,
nullptr, m_avFilterGraph);
67 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Could not create buffer output filter";
71 auto inputs = avfilter_inout_alloc();
73 qFatal(
"Failed to allocate memory");
75 inputs->name = av_strdup(
"in");
76 inputs->filter_ctx = m_inputFilter;
78 inputs->next =
nullptr;
80 auto outputs = avfilter_inout_alloc();
82 qFatal(
"Failed to allocate memory");
84 outputs->name = av_strdup(
"out");
85 outputs->filter_ctx = m_outputFilter;
87 outputs->next =
nullptr;
89 ret = avfilter_graph_parse(m_avFilterGraph,
"hwmap=mode=direct:derive_device=vaapi,scale_vaapi=format=nv12:mode=fast", outputs, inputs, NULL);
91 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Failed creating filter graph";
95 for (
auto i = 0u; i < m_avFilterGraph->nb_filters; ++i) {
96 m_avFilterGraph->filters[i]->hw_device_ctx = av_buffer_ref(m_drmContext);
99 ret = avfilter_graph_config(m_avFilterGraph,
nullptr);
101 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Failed configuring filter graph";
105 auto codec = avcodec_find_encoder_by_name(
"h264_vaapi");
107 qCWarning(PIPEWIRERECORD_LOGGING) <<
"h264_vaapi codec not found";
111 m_avCodecContext = avcodec_alloc_context3(codec);
112 if (!m_avCodecContext) {
113 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Could not allocate video codec context";
118 m_avCodecContext->width = size.
width();
119 m_avCodecContext->height = size.
height();
120 m_avCodecContext->max_b_frames = 0;
121 m_avCodecContext->gop_size = 100;
122 m_avCodecContext->pix_fmt = AV_PIX_FMT_VAAPI;
123 m_avCodecContext->time_base = AVRational{1, 1000};
126 m_avCodecContext->global_quality = percentageToAbsoluteQuality(m_quality);
128 m_avCodecContext->global_quality = 35;
132 case H264Profile::Baseline:
133 m_avCodecContext->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE;
135 case H264Profile::Main:
136 m_avCodecContext->profile = FF_PROFILE_H264_MAIN;
138 case H264Profile::High:
139 m_avCodecContext->profile = FF_PROFILE_H264_HIGH;
143 AVDictionary *options =
nullptr;
145 applyEncodingPreference(options);
151 m_avCodecContext->hw_frames_ctx = av_buffer_ref(av_buffersink_get_hw_frames_ctx(m_outputFilter));
153 if (
int result = avcodec_open2(m_avCodecContext, codec, &options); result < 0) {
154 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Could not open codec" << av_err2str(ret);
161int H264VAAPIEncoder::percentageToAbsoluteQuality(
const std::optional<quint8> &quality)
167 constexpr int MinQuality = 51 + 6 * 6;
168 return std::max(1,
int(MinQuality - (m_quality.value() / 100.0) * MinQuality));
171void H264VAAPIEncoder::applyEncodingPreference(AVDictionary *options)
173 HardwareEncoder::applyEncodingPreference(options);
175 av_dict_set(&options,
"flags",
"+mv4", 0);
177 av_dict_set(&options,
"-flags",
"+loop", 0);
bool isEmpty() const const