vdr-plugin-softhddevice-drm-gles 1.5.9-20e15de
threads.cpp
Go to the documentation of this file.
1
28extern "C" {
29#include <libavcodec/avcodec.h>
30#include <libavfilter/buffersink.h>
31#include <libavfilter/buffersrc.h>
32#include <libavutil/opt.h>
33}
34
35#include "vdr/thread.h"
36
37#include "audio.h"
38#include "logger.h"
39#include "misc.h"
40#include "threads.h"
41#include "videorender.h"
42#include "videostream.h"
43
44/*****************************************************************************
45 * cDecodingThread class
46 *
47 * This thread decodes the video data
48 ****************************************************************************/
50 : cThread(name),
51 m_pStream(stream)
52{
53 Start();
54}
55
57{
58 LOGDEBUG("threads: decoding thread started");
59 while(Running()) {
60 m_mutex.lock();
61
63
64 m_mutex.unlock();
65
66 usleep(1000);
67 }
68 LOGDEBUG("threads: decoding thread stopped");
69}
70
72{
73 if (!Active())
74 return;
75
76 LOGDEBUG("threads: stopping decoding thread");
77 Cancel(2);
78}
79
80/*****************************************************************************
81 * cDisplayThread class
82 *
83 * This thread is responsible for displaying the video and osd
84 ****************************************************************************/
86 : cThread("softhd display"),
87 m_pRender(render)
88{
89 Start();
90}
91
93{
94 LOGDEBUG("threads: display thread started");
95 while(Running()) {
96 m_mutex.lock();
97
98 bool scheduleImmediately = m_pRender->DisplayFrame();
99
100 m_mutex.unlock();
101
103
104 if (scheduleImmediately)
105 usleep(100); // yield thread. give control also to threads with lower priority.
106 else
107 usleep(1000);
108 }
109 LOGDEBUG("threads: display thread stopped");
110}
111
113{
114 if (!Active())
115 return;
116
117 LOGDEBUG("threads: stopping display thread");
118 Cancel(2);
119}
120
121/*****************************************************************************
122 * cAudioThread class
123 *
124 * This thread is decodes the audio data and moves it to hardware
125 ****************************************************************************/
127 : cThread("softhd audio"),
128 m_pAudio(audio)
129{
130 Start();
131}
132
134{
135 LOGDEBUG("threads: audio thread started");
136 while (Running()) {
139
140 usleep(10000);
141 }
142 LOGDEBUG("threads: audio thread stopped");
143}
144
146{
147 if (!Active())
148 return;
149
150 LOGDEBUG("threads: stopping audio thread");
151 Cancel(2);
152}
153
154/*****************************************************************************
155 * cFilterThread class
156 *
157 * This thread handles video filters like deinterlacer or scale filter
158 ****************************************************************************/
159cFilterThread::cFilterThread(cVideoRender *videoRender, cQueue<cDrmBuffer> *drmBufferQueue, const char *name, std::function<void(AVFrame *)> frameOutput)
160 : cThread(name),
161 m_pRender(videoRender),
162 m_frameOutput(frameOutput),
163 m_pDrmBufferQueue(drmBufferQueue)
164{
165}
166
173{
174 int ret;
175 enum AVPixelFormat pixFmts[] = { pixFmt, AV_PIX_FMT_NONE };
176
177#if LIBAVFILTER_BUILD >= AV_VERSION_INT(10, 6, 100)
178 ret = av_opt_set_array(m_pBuffersinkCtx, "pixel_formats",
179 AV_OPT_SEARCH_CHILDREN, 0, sizeof(pixFmts) / sizeof(pixFmts[0]) - 1, AV_OPT_TYPE_PIXEL_FMT, pixFmts);
180#else
181 ret = av_opt_set_int_list(m_pBuffersinkCtx, "pix_fmts",
182 pixFmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
183#endif
184
185 if (ret < 0)
186 LOGFATAL("filter thread: %s: Cannot set output pixel format (%d)", __FUNCTION__, ret);
187}
188
196void cFilterThread::InitAndStart(const AVCodecContext *videoCtx, AVFrame *frame, bool enableDeinterlacer)
197{
198 int ret;
199 char args[512];
200 const char *filterDescr = NULL;
201 m_pFilterGraph = avfilter_graph_alloc();
202 if (!m_pFilterGraph)
203 LOGFATAL("filter thread: %s: Cannot alloc filter graph", __FUNCTION__);
204
206 m_filterBug = false;
207
208 const AVFilter *buffersrc = avfilter_get_by_name("buffer");
209 const AVFilter *buffersink = avfilter_get_by_name("buffersink");
210
211#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7,16,100)
212 avfilter_register_all();
213#endif
214
215 // interlaced and non-trickspeed AV_PIX_FMT_DRM_PRIME (hardware decoded) -> hardware deinterlacer
216 // interlaced and non-trickspeed AV_PIX_FMT_YUV420P (software decoded) -> software deinterlacer
217 // progressive and trickspeed AV_PIX_FMT_YUV420P (software decoded) -> scale filter (for NV12 output)
218 // progressive and trickspeed AV_PIX_FMT_DRM_PRIME (hardware decoded) doesn't get to the FilterHandlerThread
219 if (enableDeinterlacer) {
220 if (frame->format == AV_PIX_FMT_DRM_PRIME) {
221 filterDescr = "deinterlace_v4l2m2m";
222 } else if (frame->format == AV_PIX_FMT_YUV420P) {
223 filterDescr = "bwdif=1:-1:0";
224 m_filterBug = true;
225 }
226 } else if (frame->format == AV_PIX_FMT_YUV420P) {
227 filterDescr = "scale";
228 } else
229 LOGFATAL("filter thread: %s: Unexpected pixel format: %d", __FUNCTION__, frame->format);
230
231 m_pBuffersinkCtx = avfilter_graph_alloc_filter(m_pFilterGraph, buffersink, "out");
232 if (!m_pBuffersinkCtx)
233 LOGFATAL("filter thread: %s: Cannot create buffer sink", __FUNCTION__);
234
235 // if we have a 576i stream without a valid sample_aspect_ratio (0/1) force it to be 64/45
236 // wich "stretches" a 576i stream to 1920/1080 size
237 // this was observed with h264 coded 576i vodafone DVB-C channels and some mpeg2 576i material
238 AVRational sar = videoCtx->sample_aspect_ratio;
239 if (videoCtx->sample_aspect_ratio.num == 0 && videoCtx->height == 576) {
240 sar = (AVRational){64, 45};
241 LOGDEBUG2(L_CODEC, "filter thread: %s: Observed 576i material with a sar 0/1, stretch it with sar %d/%d", __FUNCTION__, sar.num, sar.den);
242 }
243
244 if (frame->format == AV_PIX_FMT_DRM_PRIME) {
245 SetFilterOutputPixFormat(AV_PIX_FMT_DRM_PRIME);
246
247 m_pBuffersrcCtx = avfilter_graph_alloc_filter(m_pFilterGraph, buffersrc, "in");
248 if (!m_pBuffersrcCtx)
249 LOGFATAL("filter thread: %s: Cannot create buffer src", __FUNCTION__);
250
251 AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
252 memset(par, 0, sizeof(*par));
253
254 par->format = AV_PIX_FMT_DRM_PRIME;
255 par->hw_frames_ctx = frame->hw_frames_ctx;
256 par->time_base = videoCtx->pkt_timebase;
257 par->width = videoCtx->width;
258 par->height = videoCtx->height;
259 par->sample_aspect_ratio = sar;
260
261 LOGDEBUG2(L_CODEC, "filter thread: %s: filter=\"%s\" fmt %d, hw ctx %p, tb %d/%d, wxh %dx%d, sar %d/%d",
262 __FUNCTION__, filterDescr,
263 par->format, par->hw_frames_ctx, par->time_base.num, par->time_base.den,
264 par->width, par->height, par->sample_aspect_ratio.num, par->sample_aspect_ratio.den);
265
266 ret = av_buffersrc_parameters_set(m_pBuffersrcCtx, par);
267 if (ret < 0)
268 LOGFATAL("filter thread: %s: Cannot av_buffersrc_parameters_set (%d)", __FUNCTION__, ret);
269
270 av_free(par);
271
272 ret = avfilter_init_dict(m_pBuffersrcCtx, NULL);
273 if (ret < 0)
274 LOGFATAL("filter thread: %s: Cannot initialize buffer src (%d)", __FUNCTION__, ret);
275 } else {
276 SetFilterOutputPixFormat(AV_PIX_FMT_NV12);
277
278 snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
279 videoCtx->width, videoCtx->height, frame->format,
280 videoCtx->pkt_timebase.num ? videoCtx->pkt_timebase.num : 1,
281 videoCtx->pkt_timebase.num ? videoCtx->pkt_timebase.den : 1,
282 sar.num, sar.den);
283
284 LOGDEBUG2(L_CODEC, "filter thread: %s: filter=\"%s\" args=\"%s\"", __FUNCTION__, filterDescr, args);
285
286 ret = avfilter_graph_create_filter(&m_pBuffersrcCtx, buffersrc, "in", args, NULL, m_pFilterGraph);
287 if (ret < 0)
288 LOGFATAL("filter thread: %s: Cannot create buffer src", __FUNCTION__);
289 }
290
291 ret = avfilter_init_dict(m_pBuffersinkCtx, NULL);
292 if (ret < 0)
293 LOGFATAL("filter thread: %s: Cannot initialize buffer sink (%d)", __FUNCTION__, ret);
294
295 AVFilterInOut *outputs = avfilter_inout_alloc();
296 AVFilterInOut *inputs = avfilter_inout_alloc();
297
298 outputs->name = av_strdup("in");
299 outputs->filter_ctx = m_pBuffersrcCtx;
300 outputs->pad_idx = 0;
301 outputs->next = NULL;
302
303 inputs->name = av_strdup("out");
304 inputs->filter_ctx = m_pBuffersinkCtx;
305 inputs->pad_idx = 0;
306 inputs->next = NULL;
307
308 ret = avfilter_graph_parse_ptr(m_pFilterGraph, filterDescr, &inputs, &outputs, NULL);
309 if (ret < 0) {
310 LOGFATAL("filter thread: %s: avfilter_graph_parse_ptr failed (%d)", __FUNCTION__, ret);
311 }
312
313 avfilter_inout_free(&inputs);
314 avfilter_inout_free(&outputs);
315
316 ret = avfilter_graph_config(m_pFilterGraph, NULL);
317 if (ret < 0)
318 LOGFATAL("filter thread: %s: avfilter_graph_config failed (%d)", __FUNCTION__, ret);
319
320 Start();
321}
322
324{
325 LOGDEBUG("threads: video filter thread started");
326
327 while (Running()) {
328 if (m_frames.IsEmpty()) {
329 usleep(1000);
330 continue;
331 }
332
333 AVFrame *frame = m_frames.Pop();
334
336 if (isInterlacedFrame(frame))
338
339 // add frame to filter
340 int ret;
341 if ((ret = av_buffersrc_add_frame_flags(m_pBuffersrcCtx, frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0)
342 LOGWARNING("filter thread: %s: can't add_frame: %s", __FUNCTION__, av_err2str(ret));
343
344 av_frame_free(&frame);
345
346 // get filtered frames
347 while (Running()) {
348 AVFrame *filtFrame = av_frame_alloc();
349 if (!filtFrame)
350 LOGFATAL("filter thread: %s: can't allocate frame", __FUNCTION__);
351
352 ret = av_buffersink_get_frame(m_pBuffersinkCtx, filtFrame);
353
354 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
355 av_frame_free(&filtFrame);
356 break;
357 } else if (ret < 0) {
358 LOGERROR("filter thread: %s: can't get filtered frame: %s", __FUNCTION__, av_err2str(ret));
359 av_frame_free(&filtFrame);
360 break;
361 }
362
363 while (Running() && m_pDrmBufferQueue->IsFull())
364 usleep(1000);
365
366 if (Running()) {
367 if (filtFrame->format == AV_PIX_FMT_NV12 && m_filterBug) // scale filter or sw deinterlacer, no prime data, always returns NV12
368 filtFrame->pts /= 2; // ffmpeg bug
369
370 m_frameOutput(filtFrame);
371 } else
372 av_frame_free(&filtFrame);
373 }
374 }
375 LOGDEBUG("threads: filter thread stopped");
376}
377
381void cFilterThread::PushFrame(AVFrame *frame)
382{
383 m_frames.Push(frame);
384}
385
387{
388 if (!Active())
389 return;
390
391 LOGDEBUG("threads: stopping filter thread");
392 Cancel(2);
393 m_filterBug = false;
395
396 while (!m_frames.IsEmpty()) {
397 AVFrame *frame = m_frames.Pop();
398 av_frame_free(&frame);
399 }
400
401 avfilter_graph_free(&m_pFilterGraph);
402}
Audio and alsa module header file.
cSoftHdAudio * m_pAudio
Definition threads.h:90
cAudioThread(cSoftHdAudio *)
Definition threads.cpp:126
void Stop(void)
Definition threads.cpp:145
virtual void Action(void)
Definition threads.cpp:133
std::mutex m_mutex
Definition threads.h:54
cDecodingThread(cVideoStream *, const char *)
Definition threads.cpp:49
void Stop(void)
Definition threads.cpp:71
virtual void Action(void)
Definition threads.cpp:56
cVideoStream * m_pStream
Definition threads.h:55
cVideoRender * m_pRender
Definition threads.h:74
cDisplayThread(cVideoRender *)
Definition threads.cpp:85
virtual void Action(void)
Definition threads.cpp:92
std::mutex m_mutex
Definition threads.h:73
void Stop(void)
Definition threads.cpp:112
void InitAndStart(const AVCodecContext *, AVFrame *, bool)
Init and start the video filter thread.
Definition threads.cpp:196
void Action(void)
Definition threads.cpp:323
void Stop(void)
Definition threads.cpp:386
AVFilterContext * m_pBuffersinkCtx
Definition threads.h:114
void PushFrame(AVFrame *)
Put a frame in the buffer to be filtered.
Definition threads.cpp:381
cQueue< cDrmBuffer > * m_pDrmBufferQueue
pointer to renderer's DRM buffer queue
Definition threads.h:119
void SetFilterOutputPixFormat(AVPixelFormat)
Setup the filter output pixel format.
Definition threads.cpp:172
AVFilterContext * m_pBuffersrcCtx
Definition threads.h:113
bool m_filterBug
flag for a ffmpeg bug
Definition threads.h:116
AVFilterGraph * m_pFilterGraph
Definition threads.h:112
cFilterThread(cVideoRender *, cQueue< cDrmBuffer > *, const char *, std::function< void(AVFrame *)>)
Definition threads.cpp:159
int m_numFramesToFilter
number of frames to be filtered
Definition threads.h:120
std::function< void(AVFrame *)> m_frameOutput
function to output the frame
Definition threads.h:118
cQueue< AVFrame > m_frames
queue for frames to be filtered
Definition threads.h:117
Thread-safe queue class.
Definition queue.h:36
T * Pop(void)
Pop an element from the back of the queue.
Definition queue.h:64
bool IsEmpty(void)
Check if the queue is empty.
Definition queue.h:106
bool IsFull(void)
Check if the queue is full.
Definition queue.h:117
bool Push(T *element)
Push an element to the front of the queue.
Definition queue.h:47
cSoftHdAudio - Audio class
Definition audio.h:51
void ProcessEvents(void)
Process queued events and forward to event receiver.
Definition audio.cpp:1571
bool CyclicCall(void)
Cyclic audio playback call.
Definition audio.cpp:1109
cVideoRender - Video render class
void ProcessEvents(void)
Process queued events and forward to event receiver.
bool DisplayFrame()
Display the frame (video and/or osd)
cVideoStream - Video stream class
Definition videostream.h:55
void DecodeInput(void)
Decodes a reassembled codec packet.
Logger class header file.
#define LOGDEBUG2
Definition logger.h:45
#define LOGDEBUG
Definition logger.h:44
#define LOGERROR
Definition logger.h:41
#define L_CODEC
Definition logger.h:58
#define LOGWARNING
Definition logger.h:42
#define LOGFATAL
Logger macros.
Definition logger.h:40
Misc function header file.
static bool isInterlacedFrame(AVFrame *frame)
Check, if this is an interlaced frame.
Definition misc.h:81
Thread classes header file.
Rendering class header file.
Videostream class header file.