vdr-plugin-softhddevice-drm-gles 1.6.2
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/thread.h>
22
23#include "logger.h"
24#include "misc.h"
25#include "videofilter.h"
26#include "videorender.h"
27
36 : cThread(name),
37 m_pRender(videoRender),
38 m_frameOutput(frameOutput),
39 m_pDrmBufferQueue(drmBufferQueue)
40{
41}
42
49{
50 int ret;
52
53#if LIBAVFILTER_BUILD >= AV_VERSION_INT(10, 6, 100)
54 ret = av_opt_set_array(m_pBuffersinkCtx, "pixel_formats",
56#else
59#endif
60
61 if (ret < 0)
62 LOGFATAL("video filter: %s: Cannot set output pixel format (%d)", __FUNCTION__, ret);
63}
64
73{
74 int ret;
75 char args[512];
76 const char *filterDescr = NULL;
78 if (!m_pFilterGraph)
79 LOGFATAL("video filter: %s: Cannot alloc filter graph", __FUNCTION__);
80
82 m_filterBug = false;
83
84 const AVFilter *buffersrc = avfilter_get_by_name("buffer");
85 const AVFilter *buffersink = avfilter_get_by_name("buffersink");
86
87#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7,16,100)
89#endif
90
91 // interlaced and non-trickspeed AV_PIX_FMT_DRM_PRIME (hardware decoded) -> hardware deinterlacer
92 // interlaced and non-trickspeed AV_PIX_FMT_YUV420P (software decoded) -> software deinterlacer
93 // progressive and trickspeed AV_PIX_FMT_YUV420P (software decoded) -> scale filter (for NV12 output)
94 // progressive and trickspeed AV_PIX_FMT_DRM_PRIME (hardware decoded) doesn't get to the FilterHandlerThread
96 if (frame->format == AV_PIX_FMT_DRM_PRIME) {
97 filterDescr = "deinterlace_v4l2m2m";
98 } else if (frame->format == AV_PIX_FMT_YUV420P) {
99 filterDescr = "bwdif=1:-1:0";
100 m_filterBug = true;
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 if (filtFrame->format == AV_PIX_FMT_NV12 && m_filterBug) // scale filter or sw deinterlacer, no prime data, always returns NV12
249 filtFrame->pts /= 2; // ffmpeg bug
250
252 } else
254 }
255 }
256 LOGDEBUG("video filter: thread stopped");
257}
258
263{
264 m_frames.Push(frame);
265}
266
271{
272 if (!Active())
273 return;
274
275 LOGDEBUG("video filter: stopping thread");
276 Cancel(2);
277 m_filterBug = false;
279
280 while (!m_frames.IsEmpty()) {
281 AVFrame *frame = m_frames.Pop();
282 av_frame_free(&frame);
283 }
284
286}
287
T * Pop(void)
Pop an element from the back of the queue.
Definition queue.h:60
bool IsEmpty(void)
Check if the queue is empty.
Definition queue.h:102
bool IsFull(void)
Check if the queue is full.
Definition queue.h:113
bool Push(T *element)
Push an element to the front of the queue.
Definition queue.h:43
cQueue< cDrmBuffer > * m_pDrmBufferQueue
pointer to renderer's DRM buffer queue
Definition videofilter.h:58
int m_numFramesToFilter
number of frames to be filtered
Definition videofilter.h:59
AVFilterGraph * m_pFilterGraph
filter graph
Definition videofilter.h:51
cQueue< AVFrame > m_frames
queue for frames to be filtered
Definition videofilter.h:56
AVFilterContext * m_pBuffersinkCtx
buffer sink context
Definition videofilter.h:53
std::function< void(AVFrame *)> m_frameOutput
function to output the frame
Definition videofilter.h:57
bool m_filterBug
flag for a ffmpeg bug
Definition videofilter.h:55
AVFilterContext * m_pBuffersrcCtx
buffer src context
Definition videofilter.h:52
Video Renderer.
void SetFilterOutputPixFormat(AVPixelFormat)
Setup the filter output pixel format.
cVideoFilter(cVideoRender *, cQueue< cDrmBuffer > *, const char *, std::function< void(AVFrame *)>)
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.
void PushFrame(AVFrame *)
Puts a frame in the buffer to be filtered.
#define LOGDEBUG2
log to LOG_DEBUG and add a prefix
Definition logger.h:42
#define LOGDEBUG
log to LOG_DEBUG
Definition logger.h:40
#define LOGERROR
log to LOG_ERR
Definition logger.h:34
#define LOGWARNING
log to LOG_WARN
Definition logger.h:36
#define LOGFATAL
log to LOG_ERR and abort
Definition logger.h:32
static bool isInterlacedFrame(AVFrame *frame)
Check, if this is an interlaced frame.
Definition misc.h:86
#define av_err2str(err)
Definition misc.h:112
@ L_CODEC
codec logs
Definition logger.h:56
Logger Header File.
Misc Functions.
Deinterlace and Scaling Filters Header File.
Video Renderer (Display) Header File.