vdr-plugin-softhddevice-drm-gles 1.6.4-d0291bb
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:", __FUNCTION__);
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();
170}
171
179{
180 LOGDEBUG2(L_CODEC, "videostream %s: %s", m_identifier, __FUNCTION__);
181
184 LOGFATAL("videostream %s: %s: Could not reopen the decoder (flush)!", m_identifier, __FUNCTION__);
185 } else {
187 }
188}
189
203{
204 int minPkts = m_interlaced ? m_trickpkts : 1;
205
208 if (m_sentTrickPkts >= minPkts) {
210 m_sentTrickPkts = 0;
211 }
212 }
213}
214
219{
220 int width = 0;
221 int height = 0;
222
226
232 if (!h264Packet.HasSPS()) {
233 LOGDEBUG2(L_CODEC, "videostream %s: %s: No SPS in the packet!", m_identifier, __FUNCTION__);
234 h264Packet.PrintStreamData();
235 }
236
237 // start decoding with an I-Frame only
238 if (!h264Packet.IsISlice() && m_startDecodingWithIFrame) {
239 h264Packet.PrintNalUnits();
240 LOGDEBUG2(L_CODEC, "videostream %s: %s: Skip h264 packet, no I-Frame!", m_identifier, __FUNCTION__);
243 return;
244 }
245
246 // amlogic h264 decoder needs width an height for correct decoder open
248 width = h264Packet.GetWidth();
249 height = h264Packet.GetHeight();
250 LOGDEBUG2(L_CODEC, "videostream %s: %s: Parsed width %d height %d", m_identifier, __FUNCTION__, width, height);
251 }
252 }
253
254 if (m_pDecoder->Open(m_codecId, m_pPar, m_timebase, false, width, height))
255 LOGFATAL("videostream %s: %s: Could not open the decoder!", m_identifier, __FUNCTION__);
256
257 m_pConfig->CurrentDecoderType = m_pDecoder->IsHardwareDecoder() ? "hardware" : "software";
259 m_newStream = false;
262}
263
268{
273
274 if (h264Packet.HasSPS()) {
275 m_maxFrameNum = 1 << (h264Packet.GetLog2MaxFrameNumMinus4() + 4);
276 m_log2MaxFrameNumMinus4 = h264Packet.GetLog2MaxFrameNumMinus4();
277 }
278
279 if (h264Packet.HasPPS()) {
280 m_ppsNumRefIdxL0DefaultActiveMinus1 = h264Packet.GetPpsNumRefIdxL0DefaultActiveMinus1();
281 m_ppsNumRefIdxL1DefaultActiveMinus1 = h264Packet.GetPpsNumRefIdxL1DefaultActiveMinus1();
282 }
283
284 int frameNumber = h264Packet.GetFrameNum();
285
286 if (h264Packet.IsReference()) {
287 h264Packet.AddFrameNumber(frameNumber);
288 m_dpbFrames.insert(frameNumber);
289 } else {
290 h264Packet.AddFrameNumber(-1);
291 }
292
293 bool dropPacket = false;
294 if (h264Packet.IsPSlice() || h264Packet.IsBSlice()) {
295 int numRefL0 = h264Packet.GetNumRefIdxL0Active();
296 int numRefL1 = h264Packet.GetNumRefIdxL1Active();
297
298 for (auto& mod : h264Packet.GetRefMods()) {
299 if (mod.idc != 0 && mod.idc != 1)
300 continue;
301
302 int activeRefs = (mod.list == 0) ? numRefL0 : numRefL1;
303 if (activeRefs <= 0)
304 continue;
305
306 // Compute the short-term reference frame number
307 int modRef = -1;
308 int diff = mod.abs_diff_pic_num_minus1 + 1;
309
310 if (mod.idc == 0) { // subtraction
312 } else if (mod.idc == 1) { // addition
314 }
315
316 // Check if this reference exists in the DPB
317 if (m_dpbFrames.find(modRef) == m_dpbFrames.end()) {
318 h264Packet.AddInvalidReference(modRef, frameNumber);
319 } else {
320 h264Packet.AddValidReference(modRef);
321 }
322 }
323
324 // only print invalid references for better readability
325 h264Packet.BuildInvalidReferenceString(frameNumber);
326 // h264Packet.BuildValidReferenceString();
327
328 if (h264Packet.HasInvalidBackwardReferences() && h264Packet.IsPSlice() && m_numIFrames < m_dropInvalidPackets) {
329 LOGDEBUG2(L_CODEC, "videostream %s: %s: invalid backward reference, drop P-Frame %d", m_identifier, __FUNCTION__, frameNumber);
330 dropPacket = true;
331 }
332 }
333
334 if (h264Packet.IsISlice())
335 m_numIFrames++;
336
337 m_naluTypesAtStart.push_back(h264Packet.GetNalUnitString());
338
339 return dropPacket;
340}
341
346{
347 AVFrame *frame = nullptr;
348 int ret = 0;
349
351 return;
352
353 if (m_newStream)
354 OpenDecoder();
355
357
358 // log H.264 frames up to the given number of I-Frames
359 bool dropPacket = false;
363 } else if (m_logPackets) {
364 LOGDEBUG("videostream %s: %s: parsed H.264 stream:", m_identifier, __FUNCTION__);
365 for (std::size_t i = 0; i < m_naluTypesAtStart.size(); i++) {
366 LOGDEBUG("[H264] (%02d) %s", i, m_naluTypesAtStart[i].c_str());
367 }
368 m_logPackets = 0;
369 }
370 }
371
372 // send packet to decoder
373 if (!dropPacket) {
375
376 if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
377 avpkt = m_packets.Pop();
379 m_isResend = false;
380 } else {
381 m_isResend = true;
382 }
383
384 if (!ret && m_pRender->IsTrickSpeed())
386 } else {
387 avpkt = m_packets.Pop();
389 m_isResend = false;
390 }
391
392 // receive frame from decoder
393 ret = m_pDecoder->ReceiveFrame(&frame);
394 if (ret == 0) {
395 RenderFrame(frame);
396 } else if (ret == AVERROR_EOF) {
397 FlushDecoder();
398 m_sentTrickPkts = 0;
399 }
400
402 // log maximum number of packets needed for the hw decoder to deliver a frame
405
406 // fallback to software decoder if configured and hw decoder fails
408 LOGWARNING("videostream %s: %s: Could not decode frame after %d packets sent, fallback to software decoder!", m_identifier, __FUNCTION__, m_decoderFallbackToSwNumPkts);
410 LOGFATAL("videostream %s: %s: Could not reopen the decoder (sw fallback)!", m_identifier, __FUNCTION__);
411 }
412 }
413}
414
422void cVideoStream::GetVideoSize(int *width, int *height, double *aspect_ratio)
423{
425
426 if (m_pDecoder && videoCtx) {
427 *width = videoCtx->coded_width;
428 *height = videoCtx->coded_height;
429 *aspect_ratio = *width / (double)*height;
430 } else {
431 *width = 0;
432 *height = 0;
433 *aspect_ratio = 1.0;
434 }
435}
436
445 m_newStream = true;
446 m_trickpkts = codecId == AV_CODEC_ID_MPEG2VIDEO ? 1 : 2;
448 m_codecId = codecId;
449 m_pPar = par;
450}
451
452/*****************************************************************************
453 * Thread
454 ****************************************************************************/
455
460{
461 LOGDEBUG("videostream: decoding thread started");
462 while(Running()) {
463 m_mutex.lock();
464
465 DecodeInput();
466
467 m_mutex.unlock();
468
469 usleep(1000);
470 }
471 LOGDEBUG("videostream: decoding thread stopped");
472}
473
478{
479 if (!Active())
480 return;
481
482 LOGDEBUG("videostream: stopping decoding thread");
483 Cancel(2);
484}
485
496
506{
507 if (frame->decode_error_flags || frame->flags & AV_FRAME_FLAG_CORRUPT)
508 LOGWARNING("videostream: %s: %s: error_flag or FRAME_FLAG_CORRUPT", m_identifier, __FUNCTION__);
509
510 // Filter thread will only be started, if the lambda function returns true
512 m_timebase = m_pDecoder->GetContext()->pkt_timebase;
513
514 // Enable the deinterlacer only if:
515 // - The user did not disable the deinterlacer
516 // - The deinterlacer is not temporarily deactivated (trickspeed and still picture)
517 // - A hardware quirk does not forbid using the deinterlacer
518 // - It is an interlaced stream, determined by:
519 // - The codec is different from HEVC (always progressive)
520 // - The framerate is lower or equal to 30fps
521 // - Or, if the frame's interlaced flag is set
522 // 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").
523
525 (m_pDecoder->GetContext()->codec_id != AV_CODEC_ID_HEVC &&
526 m_pDecoder->GetContext()->framerate.num > 0 &&
527 av_q2d(m_pDecoder->GetContext()->framerate) < 30.1) || isInterlacedFrame(frame); // account for rounding errors when comparing double
528
529 bool useDeinterlacer =
534
536 LOGDEBUG("videostream: %s: %s: deinterlacer disabled by user configuration", m_identifier, __FUNCTION__);
537
538 // Use the filter thread if:
539 // - AV_PIX_FMT_YUV420P, interlaced -> software deinterlacer (bwdif filter)
540 // - AV_PIX_FMT_YUV420P, progressive -> scale filter to get NV12 frames
541 // - AV_PIX_FMT_DRM_PRIME, interlaced, hw deinterlacer available -> hw deinterlacer
542 if (frame->format == AV_PIX_FMT_YUV420P || (frame->format == AV_PIX_FMT_DRM_PRIME && useDeinterlacer))
544
546 }
547
548 if (m_videoFilter.Active())
550 else {
551 // AV_PIX_FMT_DRM_PRIME, interlaced, hw deinterlacer not available
552 // AV_PIX_FMT_DRM_PRIME, progressive
553 // -> put the frame directly into render buffer
555 m_frameOutput(frame);
556 }
557}
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:29
int ConfigParseH264StreamStart
log frames at stream start up to the given number of I-Frames
Definition config.h:82
int ConfigDecoderFallbackToSwNumPkts
maximum number of packets sent before fallback to sw decoder
Definition config.h:79
void SetDecoderNeedsMaxPackets(int)
Definition config.cpp:148
const char * CurrentDecoderType
current decoder type: "hardware" or "software"
Definition config.h:104
const char * CurrentDecoderName
current decoder name
Definition config.h:103
bool ConfigDecoderFallbackToSw
fallback to software decoder if the hardware decoder fails
Definition config.h:78
int GetDecoderNeedsMaxPackets(void)
Definition config.cpp:154
int ConfigDropInvalidH264PFrames
drop P-Frames with invalid references on stream start up to the given number of I-Frames
Definition config.h:83
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.
bool IsForwardTrickspeed(void)
bool IsTrickSpeed(void)
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.
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
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 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.
#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.
Deinterlace and Scaling Filters Header File.
Video Renderer (Display) Header File.
Video Input Stream Header File.