vdr-plugin-softhddevice-drm-gles 1.5.9-20e15de
videostream.cpp
Go to the documentation of this file.
1
25#include <functional>
26#include <string>
27
28extern "C" {
29#include <libavcodec/avcodec.h>
30#include <libavformat/avformat.h>
31#include <libavutil/timestamp.h>
32}
33
34#include "codec_video.h"
35#include "config.h"
36#include "h264parser.h"
37#include "logger.h"
38#include "misc.h"
39#include "queue.h"
40#include "threads.h"
41#include "videostream.h"
42#include "videorender.h"
43
53static size_t ReadLineFromFile(char *buf, size_t size, const char * file)
54{
55 FILE *fd = NULL;
56 size_t character;
57
58 fd = fopen(file, "r");
59 if (fd == NULL) {
60 LOGERROR("videostream: %s: Can't open %s", __FUNCTION__, file);
61 return 0;
62 }
63
64 character = getline(&buf, &size, fd);
65
66 fclose(fd);
67
68 return character;
69}
70
76static int ReadHWPlatform(void)
77{
78 char *txt_buf;
79 char *read_ptr;
80 size_t bufsize = 128;
81 size_t read_size;
82
83 txt_buf = (char *) calloc(bufsize, sizeof(char));
84 int hardwareQuirks = 0;
85
86 read_size = ReadLineFromFile(txt_buf, bufsize, "/sys/firmware/devicetree/base/compatible");
87 if (!read_size) {
88 free((void *)txt_buf);
89 return 0;
90 }
91
92 read_ptr = txt_buf;
93 // be aware: device tree string can contain \x0 bytes, so every C-string function
94 // thinks, we already reached the string's terminating null bytes
95 // so copy the string into a temporary string without the "\0"
96 char *_txt_buf = (char *) calloc(bufsize, sizeof(char));
97 char *_read_ptr = _txt_buf;
98 for (size_t i = 0; i < bufsize; i++) {
99 if (memcmp(read_ptr, "\0", sizeof(char))) {
100 memcpy(_read_ptr, read_ptr, sizeof(char));
101 _read_ptr++;
102 }
103 read_ptr++;
104 }
105
106 read_ptr = txt_buf;
107 LOGDEBUG2(L_DRM, "videostream: %s: found \"%s\", set hardware quirks", __FUNCTION__, _txt_buf);
108
109 while(read_size) {
110 if (strstr(read_ptr, "bcm2836")) {
111 LOGDEBUG2(L_DRM, "videostream: %s: bcm2836 (Raspberry Pi 2 Model B) found", __FUNCTION__);
112 hardwareQuirks |= QUIRK_CODEC_FLUSH_WORKAROUND;
113 break;
114 }
115 if (strstr(read_ptr, "bcm2837")) {
116 LOGDEBUG2(L_DRM, "videostream: %s: bcm2837 (Raspberry Pi 2 Model B v1.2/ 3 Model B, Raspberry Pi 3 Compute Module 3) found", __FUNCTION__);
117 hardwareQuirks |= QUIRK_CODEC_FLUSH_WORKAROUND;
118 break;
119 }
120 if (strstr(read_ptr, "bcm2711")) {
121 LOGDEBUG2(L_DRM, "videostream: %s: bcm2711 (Raspberry Pi 4 Model B, Compute Module 4, Pi 400) found", __FUNCTION__);
122 hardwareQuirks |= QUIRK_CODEC_FLUSH_WORKAROUND;
123 break;
124 }
125 if (strstr(read_ptr, "bcm2712")) {
126 LOGDEBUG2(L_DRM, "videostream: %s: bcm2712 (Raspberry Pi 5, Compute Module 5, Pi 500) found", __FUNCTION__);
127 hardwareQuirks |= QUIRK_CODEC_FLUSH_WORKAROUND;
128 break;
129 }
130 if (strstr(read_ptr, "amlogic")) {
131 LOGDEBUG2(L_DRM, "videostream: %s: amlogic found, disable HW deinterlacer", __FUNCTION__);
132 hardwareQuirks |= QUIRK_CODEC_NEEDS_EXT_INIT
135 break;
136 }
137
138 read_size -= (strlen(read_ptr) + 1);
139 read_ptr = (char *)&read_ptr[(strlen(read_ptr) + 1)];
140 }
141 free((void *)_txt_buf);
142 free((void *)txt_buf);
143
144 return hardwareQuirks;
145}
146
147/*****************************************************************************
148 * cVideoStream class
149 ****************************************************************************/
150
154cVideoStream::cVideoStream(cVideoRender *render, cQueue<cDrmBuffer> *drmBufferQueue, cSoftHdConfig *config, bool isPipStream, std::function<void(AVFrame *)> frameOutput)
155 : m_pConfig(config),
156 m_pDecoder(nullptr),
157 m_pRender(render),
158 m_identifier(isPipStream ? "PIP" : "main"),
159 m_frameOutput(frameOutput),
160 m_pDrmBufferQueue(drmBufferQueue),
161 m_userDisabledDeinterlacer(config->ConfigDisableDeint),
162 m_deinterlacerDeactivated(isPipStream ? true : false),
163 m_startDecodingWithIFrame(config->ConfigDecoderNeedsIFrame),
164 m_parseH264Dimensions(config->ConfigParseH264Dimensions)
165{
166 m_filterThreadName = "shd " + std::string(m_identifier) + " filter";
167 m_pFilterThread = new cFilterThread(render, m_pDrmBufferQueue, m_filterThreadName.c_str(), frameOutput);
170
171 LOGDEBUG("videostream %s: %s", __FUNCTION__, m_identifier);
173 LOGDEBUG2(L_CODEC, "videostream %s: %s: fallback to sw decoder after %d packets sent", __FUNCTION__, m_identifier, m_decoderFallbackToSwNumPkts);
174}
175
180{
181 LOGDEBUG("videostream %s:", __FUNCTION__);
182
183 if (m_pFilterThread)
184 delete m_pFilterThread;
185}
186
194{
195 m_packets.Push(nullptr);
196}
197
208bool cVideoStream::PushAvPacket(AVPacket *avpkt)
209{
210 if (avpkt->pts != AV_NOPTS_VALUE)
211 m_inputPts = avpkt->pts;
212
213 return m_packets.Push(avpkt);
214}
215
217{
218 return m_inputPts * 1000 * av_q2d(m_timebase);
219}
220
225{
226 LOGDEBUG("videostream %s: %s:", m_identifier, __FUNCTION__);
227
229
230 if (m_pDecoder) {
231 m_pDecoder->Close();
232 delete(m_pDecoder);
233 m_pDecoder = nullptr;
234 }
235
237}
238
243{
244 LOGDEBUG("videostream %s: %s: packets %d", m_identifier, __FUNCTION__, m_packets.Size());
245
246 while (!m_packets.IsEmpty()) {
247 AVPacket *avpkt = m_packets.Pop();
248 av_packet_free(&avpkt);
249 }
250
252}
253
258{
259 LOGDEBUG2(L_CODEC, "videostream %s: %s", m_identifier, __FUNCTION__);
260
262
263 m_decodingThreadName = "shd " + std::string(m_identifier) + " decode";
265}
266
271{
272 LOGDEBUG2(L_CODEC, "videostream %s: %s", m_identifier, __FUNCTION__);
273
274 m_codecId = AV_CODEC_ID_NONE;
275 m_pDecoder->Close();
276 m_pPar = nullptr;
277}
278
286{
287 LOGDEBUG2(L_CODEC, "videostream %s: %s", m_identifier, __FUNCTION__);
288
291 LOGFATAL("videostream %s: %s: Could not reopen the decoder (flush)!", m_identifier, __FUNCTION__);
292 } else {
294 }
295}
296
310{
311 int minPkts = m_interlaced ? m_trickpkts : 1;
312
315 if (m_sentTrickPkts >= minPkts) {
316 m_pDecoder->SendPacket(NULL);
317 m_sentTrickPkts = 0;
318 }
319 }
320}
321
326{
327 int width = 0;
328 int height = 0;
329
330 bool needsParsing = m_startDecodingWithIFrame ||
333
334 if (needsParsing && m_codecId == AV_CODEC_ID_H264) {
335 cH264Parser h264Packet(m_packets.Peek());
336
337 // start decoding with an I-Frame only
338 if (!h264Packet.IsIFrame() && m_startDecodingWithIFrame) {
339 LOGDEBUG2(L_CODEC, "videostream %s: %s: Skip h264 packet, no I-Frame!", m_identifier, __FUNCTION__);
340 AVPacket *avpkt = m_packets.Pop();
341 av_packet_free(&avpkt);
342 return;
343 }
344
345 // amlogic h264 decoder needs width an height for correct decoder open
347 width = h264Packet.GetWidth();
348 height = h264Packet.GetHeight();
349 LOGDEBUG2(L_CODEC, "videostream %s: %s: Parsed width %d height %d", m_identifier, __FUNCTION__, width, height);
350 }
351 }
352
353 if (m_pDecoder->Open(m_codecId, m_pPar, m_timebase, false, width, height))
354 LOGFATAL("videostream %s: %s: Could not open the decoder!", m_identifier, __FUNCTION__);
355
356 m_pConfig->CurrentDecoderType = m_pDecoder->IsHardwareDecoder() ? "hardware" : "software";
358 m_newStream = false;
359}
360
365{
366 AVFrame *frame = nullptr;
367 int ret = 0;
368
370 return;
371
372 if (m_newStream)
373 OpenDecoder();
374
375 // send packet to decoder
376 AVPacket *avpkt = m_packets.Peek();
377
378 ret = m_pDecoder->SendPacket(avpkt);
379
380 if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
381 avpkt = m_packets.Pop();
382 av_packet_free(&avpkt);
383 }
384
385 if (!ret && m_pRender->IsTrickSpeed())
387
388 // receive frame from decoder
389 ret = m_pDecoder->ReceiveFrame(&frame);
390 if (ret == 0) {
391 RenderFrame(frame);
392 } else if (ret == AVERROR_EOF) {
393 FlushDecoder();
394 m_sentTrickPkts = 0;
395 }
396
398 // log maximum number of packets needed for the hw decoder to deliver a frame
401
402 // fallback to software decoder if configured and hw decoder fails
404 LOGWARNING("videostream %s: %s: Could not decode frame after %d packets sent, fallback to software decoder!", m_identifier, __FUNCTION__, m_decoderFallbackToSwNumPkts);
406 LOGFATAL("videostream %s: %s: Could not reopen the decoder (sw fallback)!", m_identifier, __FUNCTION__);
407 }
408 }
409}
410
418void cVideoStream::GetVideoSize(int *width, int *height, double *aspect_ratio)
419{
420 AVCodecContext *videoCtx = m_pDecoder->GetContext();
421
422 if (m_pDecoder && videoCtx) {
423 *width = videoCtx->coded_width;
424 *height = videoCtx->coded_height;
425 *aspect_ratio = *width / (double)*height;
426 } else {
427 *width = 0;
428 *height = 0;
429 *aspect_ratio = 1.0;
430 }
431}
432
440void cVideoStream::Open(AVCodecID codecId, AVCodecParameters *par, AVRational timebase) {
441 m_newStream = true;
442 m_trickpkts = codecId == AV_CODEC_ID_MPEG2VIDEO ? 1 : 2;
443 m_timebase = timebase;
444 m_codecId = codecId;
445 m_pPar = par;
446}
447
448/*****************************************************************************
449 * Thread
450 ****************************************************************************/
451
456{
457 LOGDEBUG("videostream %s: %s", m_identifier, __FUNCTION__);
458
459 if (m_pDecodingThread->Active())
461
463 delete m_pDecodingThread;
464}
465
476
485void cVideoStream::RenderFrame(AVFrame * frame)
486{
487 if (frame->decode_error_flags || frame->flags & AV_FRAME_FLAG_CORRUPT)
488 LOGWARNING("videostream: %s: %s: error_flag or FRAME_FLAG_CORRUPT", m_identifier, __FUNCTION__);
489
490 // Filter thread will only be started, if the lambda function returns true
492 m_timebase = m_pDecoder->GetContext()->pkt_timebase;
493
494 // Enable the deinterlacer only if:
495 // - The user did not disable the deinterlacer
496 // - The deinterlacer is not temporarily deactivated (trickspeed and still picture)
497 // - A hardware quirk does not forbid using the deinterlacer
498 // - It is an interlaced stream, determined by:
499 // - The codec is different from HEVC (always progressive)
500 // - The framerate is lower or equal to 30fps
501 // - Or, if the frame's interlaced flag is set
502 // We cannot solely rely on the frame's interlaced flag, because the deinterlacer shall also be enabled with mixed progressive/interlaced streams (e.g. TV station "ProSieben").
503
505 (m_pDecoder->GetContext()->codec_id != AV_CODEC_ID_HEVC &&
506 m_pDecoder->GetContext()->framerate.num > 0 &&
507 av_q2d(m_pDecoder->GetContext()->framerate) < 30.1) || isInterlacedFrame(frame); // account for rounding errors when comparing double
508
509 bool useDeinterlacer =
514
516 LOGDEBUG("videostream: %s: %s: deinterlacer disabled by user configuration", m_identifier, __FUNCTION__);
517
518 // Use the filter thread if:
519 // - AV_PIX_FMT_YUV420P, interlaced -> software deinterlacer (bwdif filter)
520 // - AV_PIX_FMT_YUV420P, progressive -> scale filter to get NV12 frames
521 // - AV_PIX_FMT_DRM_PRIME, interlaced, hw deinterlacer available -> hw deinterlacer
522 if (frame->format == AV_PIX_FMT_YUV420P || (frame->format == AV_PIX_FMT_DRM_PRIME && useDeinterlacer))
523 m_pFilterThread->InitAndStart(m_pDecoder->GetContext(), frame, useDeinterlacer);
524
526 }
527
528 if (m_pFilterThread->Active())
530 else {
531 // AV_PIX_FMT_DRM_PRIME, interlaced, hw deinterlacer not available
532 // AV_PIX_FMT_DRM_PRIME, progressive
533 // -> put the frame directly into render buffer
535 m_frameOutput(frame);
536 }
537}
Decoding thread class.
Definition threads.h:46
void Stop(void)
Definition threads.cpp:71
Filter thread class.
Definition threads.h:100
void InitAndStart(const AVCodecContext *, AVFrame *, bool)
Init and start the video filter thread.
Definition threads.cpp:196
int GetNumFramesToFilter(void)
Definition threads.h:107
void Stop(void)
Definition threads.cpp:386
void PushFrame(AVFrame *)
Put a frame in the buffer to be filtered.
Definition threads.cpp:381
bool IsInputBufferFull(void)
Definition threads.h:106
cH264Parser - H264 Parser class
Definition h264parser.h:40
int GetWidth(void)
Definition h264parser.h:43
bool IsIFrame(void)
int GetHeight(void)
Definition h264parser.h:44
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
size_t Size(void)
Get the current size of the queue.
Definition queue.h:128
T * Peek(void)
Get a reference to the back element.
Definition queue.h:82
int ConfigDecoderFallbackToSwNumPkts
maximum number of packets sent before fallback to sw decoder
Definition config.h:64
void SetDecoderNeedsMaxPackets(int)
Definition config.cpp:143
const char * CurrentDecoderType
Definition config.h:85
const char * CurrentDecoderName
Definition config.h:84
bool ConfigDecoderFallbackToSw
fallback to software decoder if the hardware decoder fails
Definition config.h:63
int GetDecoderNeedsMaxPackets(void)
Definition config.cpp:149
cVideoDecoder - VideoDecoder class
Definition codec_video.h:33
int ReopenCodec(enum AVCodecID, AVCodecParameters *, AVRational, int)
Reopen the video decoder.
AVCodecContext * GetContext(void)
Definition codec_video.h:42
int SendPacket(const AVPacket *)
Send a video packet to be decoded.
int Open(enum AVCodecID, AVCodecParameters *, AVRational, bool, int, int)
Open the video decoder.
bool IsHardwareDecoder(void)
Definition codec_video.h:43
int GetFramesReceived(void)
Definition codec_video.h:46
void FlushBuffers(void)
Flush the video decoder.
int ReceiveFrame(AVFrame **)
Receive a decoded a video frame.
int GetPacketsSent(void)
Definition codec_video.h:45
const char * Name(void)
Definition codec_video.h:44
void Close(void)
Close video decoder.
cVideoRender - Video render class
bool IsForwardTrickspeed(void)
bool IsTrickSpeed(void)
cDecodingThread * m_pDecodingThread
pointer to decoding thread
bool m_interlaced
flag for interlaced stream
void GetVideoSize(int *, int *, double *)
Get video size and aspect ratio.
void OpenDecoder(void)
Open the decoder including an H.264 parsing if needed.
cFilterThread * m_pFilterThread
pointer to deinterlace filter thread
std::function< void(AVFrame *)> m_frameOutput
function to output the frame
const char * m_identifier
identifier string for logging
cSoftHdConfig * m_pConfig
plugin config
int64_t m_inputPts
PTS of the first packet in the input buffer.
bool m_deinterlacerDeactivated
set, if the deinterlacer should be disabled temporarily (trickspeed, stillpicture,...
int m_sentTrickPkts
how many avpkt have been sent to the decoder in trickspeed mode?
std::string m_decodingThreadName
decoding thread name string (persists for object lifetime)
void Flush(void)
Flushes the video stream by finalizing any pending data.
bool m_parseH264Dimensions
parse width and height when starting an h264 stream
void StartDecoder()
Start the decoder.
void CheckForcingFrameDecode(void)
Check, if we need to force the decoder to decode the frame (force a decoder drain)
bool m_startDecodingWithIFrame
wait for an I-Frame to start h264 decoding
void ClearVdrCoreToDecoderQueue(void)
Clears all video stream data, which is buffered to be decoded.
bool m_userDisabledDeinterlacer
set, if the user configured the deinterlace to be disabled
volatile bool m_newStream
flag for new stream
enum AVCodecID m_codecId
current codec id
bool m_checkFilterThreadNeeded
set, if we have to check, if filter thread is needed at start of playback
AVCodecParameters * m_pPar
current codec parameters
int m_decoderFallbackToSwNumPkts
fallback to sw decoder if hw decoder fails after the given number of packets sent
void DecodeInput(void)
Decodes a reassembled codec packet.
cQueue< cDrmBuffer > * m_pDrmBufferQueue
pointer to renderer's DRM buffer queue
void CloseDecoder(void)
Close the decoder.
int m_hardwareQuirks
hardware specific quirks
cVideoDecoder * m_pDecoder
video decoder
void Exit(void)
Exit video stream.
virtual ~cVideoStream(void)
cVideoStream destructor
void CancelFilterThread(void)
Stop filter thread.
cVideoRender * m_pRender
video renderer
int m_trickpkts
how many avpkt does the decoder need in trickspeed mode?
void RenderFrame(AVFrame *)
Render a frame.
cQueue< AVPacket > m_packets
AVPackets queue.
void ExitDecodingThread(void)
Stop decoding thread.
void FlushDecoder(void)
Flush the decoder.
void Open(AVCodecID, AVCodecParameters *=nullptr, AVRational={ .num=1,.den=90000 })
Open a video codec.
bool PushAvPacket(AVPacket *avpkt)
Pushes a pre-assembled AVPacket directly to the processing queue.
int64_t GetInputPtsMs(void)
std::string m_filterThreadName
filter thread name string (persists for object lifetime)
virtual void SetDeinterlacerDeactivated(bool deactivate)
Definition videostream.h:92
std::atomic< struct AVRational > m_timebase
current codec timebase
cVideoStream(cVideoRender *, cQueue< cDrmBuffer > *, cSoftHdConfig *, bool, std::function< void(AVFrame *)>)
cVideoStream constructor
Video decoder header file.
SoftHdDevice config header file.
H264 parser header file.
Logger class header file.
#define LOGDEBUG2
Definition logger.h:45
#define LOGDEBUG
Definition logger.h:44
#define LOGERROR
Definition logger.h:41
#define L_DRM
Definition logger.h:57
#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.
#define AV_NOPTS_VALUE
Definition misc.h:69
static bool isInterlacedFrame(AVFrame *frame)
Check, if this is an interlaced frame.
Definition misc.h:81
Thread-safe queue header file.
Thread classes header file.
Rendering class header file.
static size_t ReadLineFromFile(char *buf, size_t size, const char *file)
Helper function to read a line from a given file.
static int ReadHWPlatform(void)
Helper function to find out which platform we are on.
Videostream class header file.
#define QUIRK_CODEC_FLUSH_WORKAROUND
set, if we have to close and reopen the codec instead of avcodec_flush_buffers (rpi)
Definition videostream.h:40
#define QUIRK_CODEC_NEEDS_EXT_INIT
set, if codec needs some infos for init (coded_width and coded_height)
Definition videostream.h:41
#define QUIRK_NO_HW_DEINT
set, if no hw deinterlacer available
Definition videostream.h:39
#define QUIRK_CODEC_SKIP_FIRST_FRAMES
set, if codec should skip first I-Frames
Definition videostream.h:42