vdr-plugin-softhddevice-drm-gles 1.6.4-d0291bb
codec_video.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
17#include <mutex>
18
19extern "C" {
20#include <libavcodec/avcodec.h>
21#include <libavcodec/bsf.h>
22//#include <libavutil/opt.h>
23#include <libavutil/pixdesc.h>
24}
25
26#include "codec_video.h"
27#include "logger.h"
28#include "misc.h"
29
30//#define NUM_CAPTURE_BUFFERS 10
31//#define NUM_OUTPUT_BUFFERS 10
32
33/********************************************************************************
34 * Static functions
35 *******************************************************************************/
36
50 const enum AVPixelFormat *fmt)
51{
52 while (*fmt != AV_PIX_FMT_NONE) {
53 LOGDEBUG2(L_CODEC, "videocodec: %s: PixelFormat: %s videoCtx->pix_fmt: %s sw_pix_fmt: %s Codecname: %s",
56 av_get_pix_fmt_name(videoCtx->sw_pix_fmt), videoCtx->codec->name);
57 if (*fmt == AV_PIX_FMT_DRM_PRIME) {
59 }
60
61 if (*fmt == AV_PIX_FMT_YUV420P) {
62 return AV_PIX_FMT_YUV420P;
63 }
64 fmt++;
65 }
66 LOGWARNING("videocodec: %s: No pixel format found! Set default format.", __FUNCTION__);
67
69}
70
81{
82 const AVCodecHWConfig *config = NULL;
83 for (int n = 0; (config = avcodec_get_hw_config(codec, n)); n++)
84 {
85 if (!(config->pix_fmt == AV_PIX_FMT_DRM_PRIME))
86 continue;
87
88 if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) ||
89 (config->methods & AV_CODEC_HW_CONFIG_METHOD_INTERNAL))
90 return config;
91 }
92
93 return NULL;
94}
95
105static const AVCodec *FindHWDecoder(enum AVCodecID codecId)
106{
107 const AVCodec *codec;
108 void *i = 0;
109
110 while ((codec = av_codec_iterate(&i))) {
112 continue;
113 if (codec->id != codecId)
114 continue;
115
116 const AVCodecHWConfig *config = FindHWConfig(codec);
117 if (config)
118 return codec;
119 }
120
121 return NULL;
122}
123
133static const AVCodec *FindSWDecoder(enum AVCodecID codecId)
134{
135 return avcodec_find_decoder(codecId);
136}
137
145 : m_identifier(identifier)
146{
148
149#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58,18,100)
150 avcodec_register_all(); // register all formats and codecs
151#endif
152}
153
169 int width, int height)
170{
171 std::unique_lock<std::mutex> lock(m_mutex);
172
173 if (m_pVideoCtx != nullptr)
174 return 0;
175
176 const AVCodec *codec = nullptr;
177 m_isHardwareDecoder = false;
178
179 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: Try to open decoder for codec \"%s\"%s", m_identifier, __FUNCTION__,
180 avcodec_get_name(codecId), forceSoftwareDecoder ? " (sw decoding forced)" : "");
181
183 codec = FindHWDecoder(codecId);
184
185 if (codec) {
186 m_isHardwareDecoder = true;
187 } else {
189 LOGDEBUG2(L_CODEC, "videocodec: %s: no HW decoder found for codec \"%s\", try software decoder%s", __FUNCTION__, avcodec_get_name(codecId), forceSoftwareDecoder ? " (forced)" : "");
190 codec = FindSWDecoder(codecId);
191 }
192
193 if (!codec) {
194 LOGERROR("videocodec: %s: %s: Could not find any decoder for codec \"%s\"!", m_identifier, __FUNCTION__, avcodec_get_name(codecId));
195 return -1;
196 }
197
199 if (!m_pVideoCtx) {
200 LOGERROR("videocodec: %s: %s: can't alloc codec context!", m_identifier, __FUNCTION__);
201 return -1;
202 }
203
205 static AVBufferRef *hwDeviceCtx = NULL;
206
207 if (config && (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)) {
208 const char *type_name = av_hwdevice_get_type_name(config->device_type);
209 if (av_hwdevice_ctx_create(&hwDeviceCtx, config->device_type, NULL, NULL, 0) < 0) {
211 LOGERROR("videocodec: %s: %s: Error creating HW context %s", m_identifier, __FUNCTION__,
212 type_name ? type_name : "unknown");
213 return -1;
214 }
215 m_pVideoCtx->hw_device_ctx = hwDeviceCtx;
217 }
218
220 LOGERROR("videocodec: %s: %s: insert parameters to context failed!", m_identifier, __FUNCTION__);
221
222 m_pVideoCtx->codec_id = codecId;
223 m_pVideoCtx->get_format = GetFormat;
224 m_pVideoCtx->opaque = this;
225 m_pVideoCtx->pkt_timebase.num = 1;
226 m_pVideoCtx->pkt_timebase.den = 90000;
227
228 if (av_q2d(timebase) > 0)
229 m_pVideoCtx->pkt_timebase = timebase;
230
231 if (codecId == AV_CODEC_ID_H264) {
232 if (par) {
233 m_pVideoCtx->coded_width = par->width;
234 m_pVideoCtx->coded_height = par->height;
235 m_pVideoCtx->width = par->width;
236 m_pVideoCtx->height = par->height;
237 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: Set width %d and height %d from par", m_identifier, __FUNCTION__, par->width, par->height);
238 } else if (width && height) {
239 m_pVideoCtx->coded_width = width;
240 m_pVideoCtx->coded_height = height;
241 m_pVideoCtx->width = width;
242 m_pVideoCtx->height = height;
243 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: Set width %d and height %d forced", m_identifier, __FUNCTION__, width, height);
244 }
245 }
246
248 m_pVideoCtx->thread_count = !m_isHardwareDecoder ? 4 : 1;
249
250 if (codec->capabilities & AV_CODEC_CAP_SLICE_THREADS)
251 m_pVideoCtx->thread_type = FF_THREAD_SLICE;
252/*
253 if (strstr(codec->name, "_v4l2")) {
254 if (av_opt_set_int(m_pVideoCtx->priv_data, "num_capture_buffers", NUM_CAPTURE_BUFFERS, 0) < 0) {
255 LOGERROR("videocodec: %s: can't set %d num_capture_buffers", __FUNCTION__, NUM_CAPTURE_BUFFERS);
256 }
257 LOGDEBUG2(L_CODEC, "cVideoDecoder::Open: set num_capture_buffers %d", NUM_CAPTURE_BUFFERS);
258 if (av_opt_set_int(m_pVideoCtx->priv_data, "num_output_buffers", NUM_OUTPUT_BUFFERS, 0) < 0) {
259 LOGERROR("videocodec: %s: can't set %d num_output_buffers", __FUNCTION__, NUM_OUTPUT_BUFFERS);
260 }
261 LOGDEBUG2(L_CODEC, "videocodec: %s: set num_output_buffers %d", __FUNCTION__, NUM_OUTPUT_BUFFERS);
262 }
263*/
265 if (err < 0) {
267 if (!m_isHardwareDecoder) {
268 LOGERROR("videocodec: %s: %s: Error opening the decoder: %s", m_identifier, __FUNCTION__, av_err2str(err));
269 return -1;
270 }
271 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: Could not open hw decoder \"%s\", force using software decoder",
272 m_identifier, __FUNCTION__, codec->long_name ? codec->long_name : codec->name);
273
274 // unlock here, otherwise we run into a deadlock
275 lock.unlock();
276 return Open(codecId, par, timebase, true, 0, 0);
277 }
278
279 LOGINFO("videocodec: %s: %s (%s) for codec \"%s\" opened%s, using %s decoding with %d threads%s",
281 codec->long_name ? codec->long_name : codec->name,
282 codec->name,
283 avcodec_get_name(codecId),
284 forceSoftwareDecoder ? " (sw decoding forced)" : "",
285 m_isHardwareDecoder ? "hardware" : "software",
286 m_pVideoCtx->thread_count,
287 m_isHardwareDecoder ? " 🤩" : "");
288
289 m_pCodecString = codec->long_name ? codec->long_name : codec->name;
292
293 return 0;
294}
295
300{
301 std::lock_guard<std::mutex> lock(m_mutex);
302
303 if (m_pVideoCtx != nullptr) {
304 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: m_pVideoCtx %p", m_identifier, __FUNCTION__, m_pVideoCtx);
305 m_lastCodedWidth = m_pVideoCtx->coded_width;
306 m_lastCodedHeight = m_pVideoCtx->coded_height;
308 m_pVideoCtx = nullptr;
309 }
311}
312
322{
324 const AVBitStreamFilter *f;
325 size_t extradataSize;
327 int ret = 0;
328
329 f = av_bsf_get_by_name("extract_extradata");
330 if (!f) {
331 LOGERROR("videocodec: %s: %s: extradata av_bsf_get_by_name failed!", m_identifier, __FUNCTION__);
332 return -1;
333 }
334
335 ret = av_bsf_alloc(f, &bsfCtx);
336 if (ret < 0) {
337 LOGERROR("videocodec: %s: %s: extradata av_bsf_alloc failed!", m_identifier, __FUNCTION__);
338 return ret;
339 }
340
341 bsfCtx->par_in->codec_id = m_pVideoCtx->codec_id;
342
344 if (ret < 0) {
345 LOGERROR("videocodec: %s: %s: extradata av_bsf_init failed!", m_identifier, __FUNCTION__);
347 return ret;
348 }
349
352
353 if (!dstPkt) {
354 LOGERROR("videocodec: %s: %s: extradata av_packet_alloc failed!", m_identifier, __FUNCTION__);
356 return -1;
357 }
358
360 if (ret < 0) {
361 LOGERROR("videocodec: %s: %s: extradata av_packet_ref failed!", m_identifier, __FUNCTION__);
364 return ret;
365 }
366
368 if (ret < 0) {
369 LOGERROR("videocodec: %s: %s: extradata av_bsf_send_packet failed!", m_identifier, __FUNCTION__);
373 return ret;
374 }
375
377 if (ret < 0) {
378 LOGERROR("videocodec: %s: %s: extradata av_bsf_receive_packet failed!", m_identifier, __FUNCTION__);
382 return ret;
383 }
384
386
389 m_pVideoCtx->extradata_size = extradataSize;
390
394 return ret;
395}
396
408{
409 std::lock_guard<std::mutex> lock(m_mutex);
410
411 int ret = 0;
412
413 if (m_pVideoCtx == nullptr)
414 return AVERROR(EINVAL);
415
416 // force a flush, if avpkt is NULL, this initiates a decoder drain
417 if (!avpkt) {
418 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: send NULL packet, flush reqeusted", m_identifier, __FUNCTION__);
420 return 0;
421 }
422
423 if (!avpkt->size)
424 return AVERROR(EINVAL);
425
426 // get extradata, if not yet done
427 if (!m_pVideoCtx->extradata_size) {
428 if (!GetExtraData(avpkt))
429 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: set extradata %p %d", m_identifier, __FUNCTION__, m_pVideoCtx->extradata, m_pVideoCtx->extradata_size);
430 }
431
433 if (ret) {
434 if (ret != AVERROR(EAGAIN))
435 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: send_packet ret: %s", m_identifier, __FUNCTION__, av_err2str(ret));
436 return ret;
437 }
438
440 LOGDEBUG2(L_PACKET, "videocodec: %s: %s: %6d PTS %s <<---", m_identifier, __FUNCTION__, m_cntPacketsSent, Timestamp2String(avpkt->pts, 90));
441
442 return 0;
443}
444
458{
459 std::lock_guard<std::mutex> lock(m_mutex);
460
461 int ret;
463
464 if (m_pVideoCtx == nullptr)
465 return AVERROR(EINVAL);
466
467 if (!(pFrame = av_frame_alloc()))
468 LOGFATAL("videocodec: %s: %s: can't allocate decoder frame", m_identifier, __FUNCTION__);
469
471
472 if (ret) {
473 if (ret == AVERROR_EOF)
474 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: receive_frame ret: AVERROR_EOF", m_identifier, __FUNCTION__);
475 else if (ret != AVERROR(EAGAIN))
476 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: receive_frame ret: %s", m_identifier, __FUNCTION__, av_err2str(ret));
478 return ret;
479 }
480
481 if (pFrame->flags == AV_FRAME_FLAG_CORRUPT)
482 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: AV_FRAME_FLAG_CORRUPT", m_identifier, __FUNCTION__);
483
484 // Codec artifacts workaround for amlogic H264:
485 // Skip m_skipKeyFramesNum Key-Frames at stream start.
486 // m_skipKeyFramesNum can be set with SetSkipKeyFramesNum()
488 if (IsKeyFrame(pFrame)) {
489 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: artifact workaround - skip %s Keyframe nr %d", m_identifier, __FUNCTION__,
490 isInterlacedFrame(pFrame) ? "interlaced" : "progressive", m_cntStartKeyFrames);
491
494 }
495
497 return AVERROR(EAGAIN);
498 }
499
500 *frame = pFrame;
501
503 LOGDEBUG2(L_PACKET, "videocodec: %s: %s: %6d PTS %s --->> (%2d)%s", m_identifier, __FUNCTION__,
505 isInterlacedFrame(pFrame) ? " I" : "");
506
507 return 0;
508}
509
531{
532 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: m_pVideoCtx %p", m_identifier, __FUNCTION__, m_pVideoCtx);
533 Close();
535 return -1;
536 m_cntStartKeyFrames = 0; // currently unused, because we have no hardware which needs both quirks
538
539 return 0;
540}
541
548{
549 std::lock_guard<std::mutex> lock(m_mutex);
550
551 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: m_pVideoCtx %p", m_identifier, __FUNCTION__, m_pVideoCtx);
552
553 if (m_pVideoCtx)
555
557}
558
567{
568#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(58,7,100)
569 return frame->key_frame;
570#else
571 return frame->flags & AV_FRAME_FLAG_KEY;
572#endif
573}
static void LogFFmpegCallback(void *, int, const char *, va_list)
Callback for ffmpeg logs.
Definition logger.cpp:241
cVideoDecoder(const char *)
Create a new video decoder.
int ReopenCodec(enum AVCodecID, AVCodecParameters *, AVRational, int)
Reopen the video decoder.
int m_lastCodedHeight
save coded height while closing for a directly reopen
Definition codec_video.h:64
int m_skipKeyFramesNum
number of Keyframes (= I-Frames in VDR) to be skipped at stream start (hardware specific quirk)
Definition codec_video.h:65
bool m_isHardwareDecoder
true, if this is a hardware decoder
Definition codec_video.h:66
int SendPacket(const AVPacket *)
Send a video packet to be decoded.
int Open(enum AVCodecID, AVCodecParameters *, AVRational, bool, int, int)
Open the video decoder.
int m_cntFramesReceived
number of decoded frames received from decoder
Definition codec_video.h:59
int GetExtraData(const AVPacket *)
Get extradata from avpkt.
int m_cntStartKeyFrames
number of keyframes arrived while starting the coded (needed for amlogic h264 decoder in order to dro...
Definition codec_video.h:60
void FlushBuffers(void)
Flush the video decoder buffers.
std::mutex m_mutex
mutex to lock codec context
Definition codec_video.h:57
AVCodecContext * m_pVideoCtx
video codec context
Definition codec_video.h:54
int m_cntPacketsSent
number of packets sent to decoder
Definition codec_video.h:58
int m_lastCodedWidth
save coded width while closing for a directly reopen
Definition codec_video.h:63
const char * m_pCodecString
codec (long) name string
Definition codec_video.h:56
bool IsKeyFrame(AVFrame *)
Check, if this is a key frame.
const char * m_identifier
identifier for logging
Definition codec_video.h:55
int ReceiveFrame(AVFrame **)
Receive a decoded a video frame.
void Close(void)
Close video decoder.
Video Decoder Header File.
#define LOGDEBUG2
log to LOG_DEBUG and add a prefix
Definition logger.h:47
#define LOGERROR
log to LOG_ERR
Definition logger.h:39
#define LOGWARNING
log to LOG_WARN
Definition logger.h:41
#define LOGINFO
log to LOG_INFO
Definition logger.h:43
#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
static const char * Timestamp2String(int64_t ts, uint8_t divisor)
Nice time-stamp string.
Definition misc.h:127
#define av_err2str(err)
Definition misc.h:112
@ L_PACKET
decoder packet/frame tracking logs
Definition logger.h:68
@ L_CODEC
codec logs
Definition logger.h:61
static const AVCodecHWConfig * FindHWConfig(const AVCodec *codec)
Find a hardware based video decoder config.
static enum AVPixelFormat GetFormat(AVCodecContext *videoCtx, const enum AVPixelFormat *fmt)
Callback to negotiate the PixelFormat.
static const AVCodec * FindHWDecoder(enum AVCodecID codecId)
Find a suitable video codec (hardware decoding)
static const AVCodec * FindSWDecoder(enum AVCodecID codecId)
Find a suitable video codec (software decoding)
Logger Header File.
Misc Functions.