vdr-plugin-softhddevice-drm-gles 1.6.7
videostream.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
17#include <functional>
18#include <set>
19#include <string>
20#include <vector>
21
22extern "C" {
23#include <libavcodec/avcodec.h>
24#include <libavformat/avformat.h>
25#include <libavutil/timestamp.h>
26}
27
28#include <vdr/thread.h>
29
30#include "codec_video.h"
31#include "config.h"
32#include "h264parser.h"
33#include "hardwaredevice.h"
34#include "logger.h"
35#include "misc.h"
36#include "queue.h"
37#include "videofilter.h"
38#include "videostream.h"
39#include "videorender.h"
40
41/*****************************************************************************
42 * cVideoStream class
43 ****************************************************************************/
44
49 : cThread(isPipStream ? "shd PIP decode" : "shd main decode"),
50 m_pConfig(config),
51 m_pDecoder(nullptr),
52 m_pRender(render),
53 m_identifier(isPipStream ? "PIP" : "main"),
54 m_frameOutput(frameOutput),
55 m_pDrmBufferQueue(drmBufferQueue),
56 m_videoFilter(render, m_pDrmBufferQueue, isPipStream ? "shd PIP filter" : "shd main filter", frameOutput),
57 m_hardwareQuirks(hardwareQuirks),
58 m_userDisabledDeinterlacer(config->ConfigDisableDeint),
59 m_deinterlacerDeactivated(isPipStream ? true : false),
60 m_startDecodingWithIFrame(config->ConfigDecoderNeedsIFrame),
61 m_parseH264Dimensions(config->ConfigParseH264Dimensions)
62{
64
65 LOGDEBUG("videostream %s: %s", __FUNCTION__, m_identifier);
67 LOGDEBUG2(L_CODEC, "videostream %s: %s: fallback to sw decoder after %d packets sent", __FUNCTION__, m_identifier, m_decoderFallbackToSwNumPkts);
68}
69
71{
72 LOGDEBUG("videostream %s: %s", __FUNCTION__, m_identifier);
73}
74
82{
83 m_packets.Push(nullptr);
84}
85
97{
98 if (avpkt->pts != AV_NOPTS_VALUE)
99 m_inputPts = avpkt->pts;
100
101 return m_packets.Push(avpkt);
102}
103
105{
106 return m_inputPts * 1000 * av_q2d(m_timebase);
107}
108
113{
114 LOGDEBUG("videostream %s: %s:", m_identifier, __FUNCTION__);
115
116 Stop();
117
118 if (m_pDecoder) {
119 m_pDecoder->Close();
120 delete(m_pDecoder);
121 m_pDecoder = nullptr;
122 }
123
125}
126
131{
132 LOGDEBUG("videostream %s: %s: packets %d", m_identifier, __FUNCTION__, m_packets.Size());
133
134 while (!m_packets.IsEmpty()) {
137 }
138
140}
141
155
160{
161 LOGDEBUG2(L_CODEC, "videostream %s: %s", m_identifier, __FUNCTION__);
162
164 m_pDecoder->Close();
165 m_pPar = nullptr;
166 m_numIFrames = 0;
167 m_maxFrameNum = 1;
168 m_naluTypesAtStart.clear();
169 m_dpbFrames.clear();
171 m_framerate = 0.0;
172}
173
181{
182 LOGDEBUG2(L_CODEC, "videostream %s: %s", m_identifier, __FUNCTION__);
183
186 LOGFATAL("videostream %s: %s: Could not reopen the decoder (flush)!", m_identifier, __FUNCTION__);
187 } else {
189 }
190}
191
205{
206 int minPkts = m_interlaced ? m_trickpkts : 1;
207
210 if (m_sentTrickPkts >= minPkts) {
212 m_sentTrickPkts = 0;
213 }
214 }
215}
216
221{
222 int width = 0;
223 int height = 0;
224
228
234 if (!h264Packet.HasSPS()) {
235 LOGDEBUG2(L_CODEC, "videostream %s: %s: No SPS in the packet!", m_identifier, __FUNCTION__);
236 h264Packet.PrintStreamData();
237 }
238
239 // start decoding with an I-Frame only
240 if (!h264Packet.IsISlice() && m_startDecodingWithIFrame) {
241 h264Packet.PrintNalUnits();
242 LOGDEBUG2(L_CODEC, "videostream %s: %s: Skip h264 packet, no I-Frame!", m_identifier, __FUNCTION__);
245 return;
246 }
247
248 // amlogic h264 decoder needs width an height for correct decoder open
250 width = h264Packet.GetWidth();
251 height = h264Packet.GetHeight();
252 LOGDEBUG2(L_CODEC, "videostream %s: %s: Parsed width %d height %d", m_identifier, __FUNCTION__, width, height);
253 }
254 }
255
256 if (m_pDecoder->Open(m_codecId, m_pPar, m_timebase, false, width, height))
257 LOGFATAL("videostream %s: %s: Could not open the decoder!", m_identifier, __FUNCTION__);
258
259 m_pConfig->CurrentDecoderType = m_pDecoder->IsHardwareDecoder() ? "hardware" : "software";
261 m_newStream = false;
264}
265
270{
275
276 if (h264Packet.HasSPS()) {
277 m_maxFrameNum = 1 << (h264Packet.GetLog2MaxFrameNumMinus4() + 4);
278 m_log2MaxFrameNumMinus4 = h264Packet.GetLog2MaxFrameNumMinus4();
279 }
280
281 if (h264Packet.HasPPS()) {
282 m_ppsNumRefIdxL0DefaultActiveMinus1 = h264Packet.GetPpsNumRefIdxL0DefaultActiveMinus1();
283 m_ppsNumRefIdxL1DefaultActiveMinus1 = h264Packet.GetPpsNumRefIdxL1DefaultActiveMinus1();
284 }
285
286 int frameNumber = h264Packet.GetFrameNum();
287
288 if (h264Packet.IsReference()) {
289 h264Packet.AddFrameNumber(frameNumber);
290 m_dpbFrames.insert(frameNumber);
291 } else {
292 h264Packet.AddFrameNumber(-1);
293 }
294
295 bool dropPacket = false;
296 if (h264Packet.IsPSlice() || h264Packet.IsBSlice()) {
297 int numRefL0 = h264Packet.GetNumRefIdxL0Active();
298 int numRefL1 = h264Packet.GetNumRefIdxL1Active();
299
300 for (auto& mod : h264Packet.GetRefMods()) {
301 if (mod.idc != 0 && mod.idc != 1)
302 continue;
303
304 int activeRefs = (mod.list == 0) ? numRefL0 : numRefL1;
305 if (activeRefs <= 0)
306 continue;
307
308 // Compute the short-term reference frame number
309 int modRef = -1;
310 int diff = mod.abs_diff_pic_num_minus1 + 1;
311
312 if (mod.idc == 0) { // subtraction
314 } else if (mod.idc == 1) { // addition
316 }
317
318 // Check if this reference exists in the DPB
319 if (m_dpbFrames.find(modRef) == m_dpbFrames.end()) {
320 h264Packet.AddInvalidReference(modRef, frameNumber);
321 } else {
322 h264Packet.AddValidReference(modRef);
323 }
324 }
325
326 // only print invalid references for better readability
327 h264Packet.BuildInvalidReferenceString(frameNumber);
328 // h264Packet.BuildValidReferenceString();
329
330 if (h264Packet.HasInvalidBackwardReferences() && h264Packet.IsPSlice() && m_numIFrames < m_dropInvalidPackets) {
331 LOGDEBUG2(L_CODEC, "videostream %s: %s: invalid backward reference, drop P-Frame %d", m_identifier, __FUNCTION__, frameNumber);
332 dropPacket = true;
333 }
334 }
335
336 if (h264Packet.IsISlice())
337 m_numIFrames++;
338
339 m_naluTypesAtStart.push_back(h264Packet.GetNalUnitString());
340
341 return dropPacket;
342}
343
348{
349 AVFrame *frame = nullptr;
350 int ret = 0;
351
353 return;
354
355 if (m_newStream)
356 OpenDecoder();
357
359
360 // log H.264 frames up to the given number of I-Frames
361 bool dropPacket = false;
365 } else if (m_logPackets) {
366 LOGDEBUG("videostream %s: %s: parsed H.264 stream:", m_identifier, __FUNCTION__);
367 for (std::size_t i = 0; i < m_naluTypesAtStart.size(); i++) {
368 LOGDEBUG("[H264] (%02d) %s", i, m_naluTypesAtStart[i].c_str());
369 }
370 m_logPackets = 0;
371 }
372 }
373
374 // send packet to decoder
375 if (!dropPacket) {
377
378 if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
379 avpkt = m_packets.Pop();
381 m_isResend = false;
382 } else {
383 m_isResend = true;
384 }
385
386 if (!ret && m_pRender->IsTrickSpeed())
388 } else {
389 avpkt = m_packets.Pop();
391 m_isResend = false;
392 }
393
394 // receive frame from decoder
395 ret = m_pDecoder->ReceiveFrame(&frame);
396 if (ret == 0) {
397 RenderFrame(frame);
398 } else if (ret == AVERROR_EOF) {
399 FlushDecoder();
400 m_sentTrickPkts = 0;
401 }
402
404 // log maximum number of packets needed for the hw decoder to deliver a frame
407
408 // fallback to software decoder if configured and hw decoder fails
410 LOGWARNING("videostream %s: %s: Could not decode frame after %d packets sent, fallback to software decoder!", m_identifier, __FUNCTION__, m_decoderFallbackToSwNumPkts);
412 LOGFATAL("videostream %s: %s: Could not reopen the decoder (sw fallback)!", m_identifier, __FUNCTION__);
413 }
414 }
415}
416
424void cVideoStream::GetVideoSize(int *width, int *height, double *aspect_ratio)
425{
427
428 if (m_pDecoder && videoCtx) {
429 *width = videoCtx->coded_width;
430 *height = videoCtx->coded_height;
431 *aspect_ratio = *width / (double)*height;
432 } else {
433 *width = 0;
434 *height = 0;
435 *aspect_ratio = 1.0;
436 }
437}
438
447 m_newStream = true;
448 m_trickpkts = codecId == AV_CODEC_ID_MPEG2VIDEO ? 1 : 2;
450 m_codecId = codecId;
451 m_pPar = par;
452}
453
454/*****************************************************************************
455 * Thread
456 ****************************************************************************/
457
462{
463 LOGDEBUG("videostream: decoding thread started");
464 while(Running()) {
465 m_mutex.lock();
466
467 DecodeInput();
468
469 m_mutex.unlock();
470
471 usleep(1000);
472 }
473 LOGDEBUG("videostream: decoding thread stopped");
474}
475
480{
481 if (!Active())
482 return;
483
484 LOGDEBUG("videostream: stopping decoding thread");
485 Cancel(2);
486}
487
492 if (m_videoFilter.Active())
494
496 m_useDeinterlacer = false;
498}
499
509{
510 if (frame->decode_error_flags || frame->flags & AV_FRAME_FLAG_CORRUPT)
511 LOGWARNING("videostream: %s: %s: error_flag or FRAME_FLAG_CORRUPT", m_identifier, __FUNCTION__);
512
513 if (!m_framerate) {
514 if (av_q2d(m_pDecoder->GetContext()->framerate) > 0.0) {
515 // the decoder has a valid framerate, use it
516 m_framerate = av_q2d(m_pDecoder->GetContext()->framerate);
517 } else if (frame->pts != AV_NOPTS_VALUE) {
518 // The decoder has no valid framerate, calculate it from pts and timebase
519 // @todo We are only using the first two frames here.
520 // If there are issues in the stream this might not be correct.
521 if (m_lastPts != AV_NOPTS_VALUE) {
522 int64_t delta = frame->pts - m_lastPts;
523 m_framerate = 1.0 / (delta * av_q2d(m_timebase));
524 }
525 m_lastPts = frame->pts;
526 }
527 }
528
529 // Filter thread will only be started, if the lambda function returns true
531 m_timebase = m_pDecoder->GetContext()->pkt_timebase;
532
533 // Enable the deinterlacer only if:
534 // --------------------------------
535 // - The user did not disable the deinterlacer
536 // - The deinterlacer is not temporarily deactivated (trickspeed and still picture)
537 // - A hardware quirk does not forbid using the deinterlacer
538 // - It is an interlaced stream, determined by:
539 // - The codec is different from HEVC (always progressive)
540 // - The framerate is lower or equal to 30fps
541 // - Or, if the frame's interlaced flag is set
542 // - the display cannot handle an interlaced mode
543 // - the display can handle an interlaced mode, but the user doesn't want to use it
544 //
545 // General interlaced stream detection:
546 // ------------------------------------
547 // 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").
548
550 (m_pDecoder->GetContext()->codec_id != AV_CODEC_ID_HEVC &&
551 m_pDecoder->GetContext()->framerate.num > 0 &&
552 av_q2d(m_pDecoder->GetContext()->framerate) < 30.1) || isInterlacedFrame(frame); // account for rounding errors when comparing double
553
554 // don't use the deinterlacer, if display mode should follow video including interlacing
556
557 // test, if display can handle the requested mode
558 // if the interlaced stream should be deinterlaced, the new mode will have doubled framerate and is progressive
559 sDrmMode mode = { m_pDecoder->GetContext()->coded_width,
560 m_pDecoder->GetContext()->coded_height,
564
566 m_interlaced &&
571
573 LOGDEBUG("videostream: %s: %s: deinterlacer disabled by user configuration", m_identifier, __FUNCTION__);
574
575 // Use the filter thread if:
576 // - AV_PIX_FMT_YUV420P, interlaced -> software deinterlacer (bwdif filter)
577 // - AV_PIX_FMT_YUV420P, progressive -> scale filter to get NV12 frames
578 // - AV_PIX_FMT_DRM_PRIME, interlaced, hw deinterlacer available -> hw deinterlacer
579 if (frame->format == AV_PIX_FMT_YUV420P || (frame->format == AV_PIX_FMT_DRM_PRIME && m_useDeinterlacer))
581
583 }
584
585 // if interlaced material was forced to be deinterlaced, we have doubled framerate and progressive mode now
588
589 // reset the display mode if needed
590 if (m_framerate &&
595
597 m_pDecoder->GetContext()->coded_height,
600
602 }
603
604 if (m_videoFilter.Active())
606 else {
607 // AV_PIX_FMT_DRM_PRIME, interlaced, hw deinterlacer not available
608 // AV_PIX_FMT_DRM_PRIME, progressive
609 // -> put the frame directly into render buffer
611 m_frameOutput(frame);
612 }
613}
H.264 Parser.
Definition h264parser.h:45
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
size_t Size(void)
Get the current size of the queue.
Definition queue.h:121
T * Peek(void)
Get a reference to the back element.
Definition queue.h:75
Plugin Configuration.
Definition config.h:49
int ConfigVideoDisplayMode
display mode (enum ConfigDisplayMode)
Definition config.h:60
int ConfigParseH264StreamStart
log frames at stream start up to the given number of I-Frames
Definition config.h:103
int ConfigDecoderFallbackToSwNumPkts
maximum number of packets sent before fallback to sw decoder
Definition config.h:100
void SetDecoderNeedsMaxPackets(int)
Definition config.cpp:150
const char * CurrentDecoderType
current decoder type: "hardware" or "software"
Definition config.h:127
const char * CurrentDecoderName
current decoder name
Definition config.h:126
bool ConfigDecoderFallbackToSw
fallback to software decoder if the hardware decoder fails
Definition config.h:99
int GetDecoderNeedsMaxPackets(void)
Definition config.cpp:156
sDrmMode CurrentVideoDrmMode
currently used video drm mode
Definition config.h:136
int ConfigDropInvalidH264PFrames
drop P-Frames with invalid references on stream start up to the given number of I-Frames
Definition config.h:104
Video Decoder.
Definition codec_video.h:37
int ReopenCodec(enum AVCodecID, AVCodecParameters *, AVRational, int)
Reopen the video decoder.
AVCodecContext * GetContext(void)
Definition codec_video.h:46
int SendPacket(const AVPacket *)
Send a video packet to be decoded.
int Open(enum AVCodecID, AVCodecParameters *, AVRational, bool, int, int)
Open the video decoder.
void SetSkipKeyFramesNum(int num)
Definition codec_video.h:51
bool IsHardwareDecoder(void)
Definition codec_video.h:47
int GetFramesReceived(void)
Definition codec_video.h:50
void FlushBuffers(void)
Flush the video decoder buffers.
int ReceiveFrame(AVFrame **)
Receive a decoded a video frame.
int GetPacketsSent(void)
Definition codec_video.h:49
const char * Name(void)
Definition codec_video.h:48
void Close(void)
Close video decoder.
int GetNumFramesToFilter(void)
Definition videofilter.h:48
void InitAndStart(const AVCodecContext *, AVFrame *, bool)
Init and start the video filter thread.
void Stop(void)
Stops the filter thread and does a cleanup.
bool IsInputBufferFull(void)
Definition videofilter.h:47
void PushFrame(AVFrame *)
Puts a frame in the buffer to be filtered.
Video Renderer.
void SetDisplayMode(int)
Wrapper to set the display mode.
bool IsForwardTrickspeed(void)
bool IsTrickSpeed(void)
bool CanHandleMode(sDrmMode *)
Wrapper to check, if drm can handle the display mode.
std::mutex m_mutex
mutex for decoding thread control
bool m_interlaced
flag for interlaced stream
void GetVideoSize(int *, int *, double *)
Get video size and aspect ratio.
bool ParseH264Packet(AVPacket *)
Parse an H.264 packet.
void OpenDecoder(void)
Open the decoder including an H.264 parsing if needed.
int m_dropInvalidPackets
drop P-Frames with invalid references until the given number of I-Frames arrived
std::function< void(AVFrame *)> m_frameOutput
function to output the frame
const char * m_identifier
identifier string for logging
cSoftHdConfig * m_pConfig
plugin config
Definition videostream.h:97
int64_t m_inputPts
PTS of the first packet in the input buffer.
double m_framerate
current stream framerate
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?
int m_logPackets
parse and log all frames until the number of given I-Frames arrived
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
int m_ppsNumRefIdxL1DefaultActiveMinus1
cache NumRefIdxL1DefaultActiveMinuns1 from a previous PPS parsing
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
bool m_useDeinterlacer
set, if the deinterlacer is used
int m_log2MaxFrameNumMinus4
cache Log2MaxFrameNumMinus4 from a previous SPS parsing
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.
bool m_isResend
track, if we already tried to send the AVPacket to the decoder if so, skip the parsing
void Stop(void)
Stop the decoding thread.
cQueue< cDrmBuffer > * m_pDrmBufferQueue
pointer to renderer's DRM buffer queue
virtual void Action(void)
Decoding thread loop, which periodically tries to decode input.
void CloseDecoder(void)
Close the decoder.
int m_hardwareQuirks
hardware specific quirks
cVideoDecoder * m_pDecoder
video decoder
Definition videostream.h:98
cVideoFilter m_videoFilter
pointer to deinterlace/scaling video filter thread
int m_maxFrameNum
= 1 << Log2MaxFrameNumMinus4 + 4
void Exit(void)
Exit video stream.
cVideoStream(cVideoRender *, int, cQueue< cDrmBuffer > *, cSoftHdConfig *, bool, std::function< void(AVFrame *)>)
Create a video stream.
virtual ~cVideoStream(void)
std::vector< std::string > m_naluTypesAtStart
array of strings to log the H.264 frames at stream start
void CancelFilterThread(void)
Stop filter thread.
int m_ppsNumRefIdxL0DefaultActiveMinus1
cache NumRefIdxL0DefaultActiveMinuns1 from a previous PPS parsing
cVideoRender * m_pRender
video renderer
Definition videostream.h:99
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 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 m_lastPts
helper PTS to calculate a framerate at stream start
int64_t GetInputPtsMs(void)
virtual void SetDeinterlacerDeactivated(bool deactivate)
Definition videostream.h:85
int m_numIFrames
counter for the arriving I-Frames at H.264 stream start
std::atomic< struct AVRational > m_timebase
current codec timebase
std::set< int > m_dpbFrames
private set of reference frames (internal short-time decoded picture buffer)
Video Decoder Header File.
Plugin Configuration Header File.
@ CONFIG_DISPLAY_MODE_FOLLOW_VIDEO_INTERLACED
Definition config.h:40
#define LOGDEBUG2
log to LOG_DEBUG and add a prefix
Definition logger.h:47
#define LOGDEBUG
log to LOG_DEBUG
Definition logger.h:45
#define AV_NOPTS_VALUE
Definition misc.h:74
#define LOGWARNING
log to LOG_WARN
Definition logger.h:41
#define LOGFATAL
log to LOG_ERR and abort
Definition logger.h:37
static bool isInterlacedFrame(AVFrame *frame)
Check, if this is an interlaced frame.
Definition misc.h:86
@ QUIRK_CODEC_FLUSH_WORKAROUND
set, if we have to close and reopen the codec instead of avcodec_flush_buffers (rpi)
@ QUIRK_CODEC_SKIP_FIRST_FRAMES
set, if codec should skip first I-Frames
@ QUIRK_CODEC_SKIP_NUM_FRAMES
skip QUIRK_CODEC_SKIP_NUM_FRAMES, in case QUIRK_CODEC_SKIP_FIRST_FRAMES is set
@ QUIRK_CODEC_NEEDS_DIMENSION_PARSE
set, if codec needs some infos for init (coded_width and coded_height)
@ QUIRK_NO_HW_DEINT
set, if no hw deinterlacer available
@ L_CODEC
codec logs
Definition logger.h:61
H.264 Parser Header File.
Describes a hardware device.
Logger Header File.
Misc Functions.
Thread-safe Queue.
Holds possible display configurations.
Definition config.h:30
int width
display width
Definition config.h:31
bool interlaced
is this an interlaced mode?
Definition config.h:34
int height
display height
Definition config.h:32
double refreshRateHz
display refresh rate
Definition config.h:33
Deinterlace and Scaling Filters Header File.
Video Renderer (Display) Header File.
Video Input Stream Header File.