vdr-plugin-softhddevice-drm-gles 1.7.3-98a2ef7
videofilter.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
14extern "C" {
15#include <libavcodec/avcodec.h>
16#include <libavfilter/buffersink.h>
17#include <libavfilter/buffersrc.h>
18#include <libavutil/opt.h>
19}
20
21#include <vdr/remux.h>
22#include <vdr/thread.h>
23
24#include "logger.h"
25#include "misc.h"
26#include "videofilter.h"
27#include "videorender.h"
28
38 : cThread(name),
39 m_pRender(videoRender),
40 m_frameOutput(frameOutput),
41 m_pDrmBufferQueue(drmBufferQueue)
42{
43}
44
51{
52 int ret;
54
55#if LIBAVFILTER_BUILD >= AV_VERSION_INT(10, 6, 100)
56 ret = av_opt_set_array(m_pBuffersinkCtx, "pixel_formats",
58#else
61#endif
62
63 if (ret < 0)
64 LOGFATAL("video filter: %s: Cannot set output pixel format (%d)", __FUNCTION__, ret);
65}
66
75{
76 int ret;
77 char args[512];
78 const char *filterDescr = NULL;
80 if (!m_pFilterGraph)
81 LOGFATAL("video filter: %s: Cannot alloc filter graph", __FUNCTION__);
82
84
85 const AVFilter *buffersrc = avfilter_get_by_name("buffer");
86 const AVFilter *buffersink = avfilter_get_by_name("buffersink");
87
88#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7,16,100)
90#endif
91
92 // interlaced and non-trickspeed AV_PIX_FMT_DRM_PRIME (hardware decoded) -> hardware deinterlacer
93 // interlaced and non-trickspeed AV_PIX_FMT_YUV420P (software decoded) -> software deinterlacer
94 // progressive and trickspeed AV_PIX_FMT_YUV420P (software decoded) -> scale filter (for NV12 output)
95 // progressive and trickspeed AV_PIX_FMT_DRM_PRIME (hardware decoded) doesn't get to the FilterHandlerThread
97 if (frame->format == AV_PIX_FMT_DRM_PRIME) {
98 filterDescr = "deinterlace_v4l2m2m";
99 } else if (frame->format == AV_PIX_FMT_YUV420P) {
100 filterDescr = "bwdif=1:-1:0";
101 }
102 } else if (frame->format == AV_PIX_FMT_YUV420P) {
103 filterDescr = "scale";
104 } else
105 LOGFATAL("video filter: %s: Unexpected pixel format: %d", __FUNCTION__, frame->format);
106
108 if (!m_pBuffersinkCtx)
109 LOGFATAL("video filter: %s: Cannot create buffer sink", __FUNCTION__);
110
111 // if we have a 576i stream without a valid sample_aspect_ratio (0/1) force it to be 64/45
112 // wich "stretches" a 576i stream to 1920/1080 size
113 // this was observed with h264 coded 576i vodafone DVB-C channels and some mpeg2 576i material
114 AVRational sar = videoCtx->sample_aspect_ratio;
115 if (videoCtx->sample_aspect_ratio.num == 0 && videoCtx->height == 576) {
116 sar = (AVRational){64, 45};
117 LOGDEBUG2(L_CODEC, "video filter: %s: Observed 576i material with a sar 0/1, stretch it with sar %d/%d", __FUNCTION__, sar.num, sar.den);
118 }
119
120 if (frame->format == AV_PIX_FMT_DRM_PRIME) {
122
124 if (!m_pBuffersrcCtx)
125 LOGFATAL("video filter: %s: Cannot create buffer src", __FUNCTION__);
126
128 memset(par, 0, sizeof(*par));
129
130 par->format = AV_PIX_FMT_DRM_PRIME;
131 par->hw_frames_ctx = frame->hw_frames_ctx;
132 par->time_base = videoCtx->pkt_timebase;
133 par->width = videoCtx->width;
134 par->height = videoCtx->height;
135 par->sample_aspect_ratio = sar;
136
137 LOGDEBUG2(L_CODEC, "video filter: %s: filter=\"%s\" fmt %d, hw ctx %p, tb %d/%d, wxh %dx%d, sar %d/%d",
139 par->format, par->hw_frames_ctx, par->time_base.num, par->time_base.den,
140 par->width, par->height, par->sample_aspect_ratio.num, par->sample_aspect_ratio.den);
141
143 if (ret < 0)
144 LOGFATAL("video filter: %s: Cannot av_buffersrc_parameters_set (%d)", __FUNCTION__, ret);
145
146 av_free(par);
147
149 if (ret < 0)
150 LOGFATAL("video filter: %s: Cannot initialize buffer src (%d)", __FUNCTION__, ret);
151 } else {
153
154 snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
155 videoCtx->width, videoCtx->height, frame->format,
156 videoCtx->pkt_timebase.num ? videoCtx->pkt_timebase.num : 1,
157 videoCtx->pkt_timebase.num ? videoCtx->pkt_timebase.den : 1,
158 sar.num, sar.den);
159
160 LOGDEBUG2(L_CODEC, "video filter: %s: filter=\"%s\" args=\"%s\"", __FUNCTION__, filterDescr, args);
161
163 if (ret < 0)
164 LOGFATAL("video filter: %s: Cannot create buffer src", __FUNCTION__);
165 }
166
168 if (ret < 0)
169 LOGFATAL("video filter: %s: Cannot initialize buffer sink (%d)", __FUNCTION__, ret);
170
173
174 outputs->name = av_strdup("in");
175 outputs->filter_ctx = m_pBuffersrcCtx;
176 outputs->pad_idx = 0;
177 outputs->next = NULL;
178
179 inputs->name = av_strdup("out");
180 inputs->filter_ctx = m_pBuffersinkCtx;
181 inputs->pad_idx = 0;
182 inputs->next = NULL;
183
185 if (ret < 0) {
186 LOGFATAL("video filter: %s: avfilter_graph_parse_ptr failed (%d)", __FUNCTION__, ret);
187 }
188
191
193 if (ret < 0)
194 LOGFATAL("video filter: %s: avfilter_graph_config failed (%d)", __FUNCTION__, ret);
195
196 Start();
197}
198
205{
206 LOGDEBUG("video filter: thread started");
207
208 while (Running()) {
209 if (m_frames.IsEmpty()) {
210 usleep(1000);
211 continue;
212 }
213
214 AVFrame *frame = m_frames.Pop();
215
217 if (isInterlacedFrame(frame))
219
220 // add frame to filter
221 int ret;
223 LOGWARNING("video filter: %s: can't add_frame: %s", __FUNCTION__, av_err2str(ret));
224
225 av_frame_free(&frame);
226
227 // get filtered frames
228 while (Running()) {
230 if (!filtFrame)
231 LOGFATAL("video filter: %s: can't allocate frame", __FUNCTION__);
232
234
235 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
237 break;
238 } else if (ret < 0) {
239 LOGERROR("video filter: %s: can't get filtered frame: %s", __FUNCTION__, av_err2str(ret));
241 break;
242 }
243
244 while (Running() && m_pDrmBufferQueue->IsFull())
245 usleep(1000);
246
247 if (Running()) {
248 // The sink's timebase may differ from the source's one.
249 // For example it's 1/180000 in case of bwdif deinterlacer.
250 // Rescale the pts and duration and set the original timebase again.
251 constexpr AVRational timebase { .num = 1, .den = PTSTICKS };
253
254 if (filtFrame->pts != AV_NOPTS_VALUE)
256 if (filtFrame->duration > 0)
257 filtFrame->duration = av_rescale_q(filtFrame->duration, sinkTimebase, timebase);
258
259 filtFrame->time_base = timebase;
260
262 } else
264 }
265 }
266 LOGDEBUG("video filter: thread stopped");
267}
268
273{
274 m_frames.Push(frame);
275}
276
281{
282 if (!Active())
283 return;
284
285 LOGDEBUG("video filter: stopping thread");
286 Cancel(2);
288
289 while (!m_frames.IsEmpty()) {
290 AVFrame *frame = m_frames.Pop();
291 av_frame_free(&frame);
292 }
293
295}
T * Pop(void)
Pop an element from the back of the queue.
Definition queue.h:57
bool IsEmpty(void)
Check if the queue is empty.
Definition queue.h:99
bool IsFull(void)
Check if the queue is full.
Definition queue.h:110
bool Push(T *element)
Push an element to the front of the queue.
Definition queue.h:40
cQueue< cDrmBuffer > * m_pDrmBufferQueue
pointer to renderer's DRM buffer queue
Definition videofilter.h:60
void SetFilterOutputPixFormat(AVPixelFormat)
Setup the filter output pixel format.
cVideoFilter(cVideoRender *, cQueue< cDrmBuffer > *, const char *, std::function< void(AVFrame *)>)
Initiate Filter.
int m_numFramesToFilter
number of frames to be filtered
Definition videofilter.h:61
AVFilterGraph * m_pFilterGraph
filter graph
Definition videofilter.h:54
cQueue< AVFrame > m_frames
queue for frames to be filtered
Definition videofilter.h:58
void InitAndStart(const AVCodecContext *, AVFrame *, bool)
Init and start the video filter thread.
void Stop(void)
Stops the filter thread and does a cleanup.
void Action(void)
Main filter thread loop.
AVFilterContext * m_pBuffersinkCtx
buffer sink context
Definition videofilter.h:56
std::function< void(AVFrame *)> m_frameOutput
function to output the frame
Definition videofilter.h:59
AVFilterContext * m_pBuffersrcCtx
buffer src context
Definition videofilter.h:55
void PushFrame(AVFrame *)
Puts a frame in the buffer to be filtered.
Video Renderer.
#define LOGDEBUG2
log to LOG_DEBUG and add a prefix
Definition logger.h:48
#define LOGDEBUG
log to LOG_DEBUG
Definition logger.h:46
#define LOGERROR
log to LOG_ERR
Definition logger.h:40
#define AV_NOPTS_VALUE
Definition misc.h:76
#define LOGWARNING
log to LOG_WARN
Definition logger.h:42
#define LOGFATAL
log to LOG_ERR and abort
Definition logger.h:38
static bool isInterlacedFrame(AVFrame *frame)
Check, if this is an interlaced frame.
Definition misc.h:88
#define av_err2str(err)
Definition misc.h:114
@ L_CODEC
codec logs
Definition logger.h:65
Logger Header File.
Misc Functions.
Deinterlace and Scaling Filters Header File.
Video Renderer (Display) Header File.