vdr-plugin-softhddevice-drm-gles 1.6.2
codec_video.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
17extern "C" {
18#include <libavcodec/avcodec.h>
19#include <libavcodec/bsf.h>
20//#include <libavutil/opt.h>
21#include <libavutil/pixdesc.h>
22}
23
24#include <vdr/thread.h>
25
26#include "codec_video.h"
27#include "logger.h"
28#include "misc.h"
29#include "videostream.h"
30
31//#define NUM_CAPTURE_BUFFERS 10
32//#define NUM_OUTPUT_BUFFERS 10
33
41/********************************************************************************
42 * Static functions
43 *******************************************************************************/
44
56 const enum AVPixelFormat *fmt)
57{
58 while (*fmt != AV_PIX_FMT_NONE) {
59 LOGDEBUG2(L_CODEC, "videocodec: %s: PixelFormat: %s videoCtx->pix_fmt: %s sw_pix_fmt: %s Codecname: %s",
62 av_get_pix_fmt_name(videoCtx->sw_pix_fmt), videoCtx->codec->name);
63 if (*fmt == AV_PIX_FMT_DRM_PRIME) {
65 }
66
67 if (*fmt == AV_PIX_FMT_YUV420P) {
68 return AV_PIX_FMT_YUV420P;
69 }
70 fmt++;
71 }
72 LOGWARNING("videocodec: %s: No pixel format found! Set default format.", __FUNCTION__);
73
75}
76
85{
86 const AVCodecHWConfig *config = NULL;
87 for (int n = 0; (config = avcodec_get_hw_config(codec, n)); n++)
88 {
89 if (!(config->pix_fmt == AV_PIX_FMT_DRM_PRIME))
90 continue;
91
92 if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) ||
93 (config->methods & AV_CODEC_HW_CONFIG_METHOD_INTERNAL))
94 return config;
95 }
96
97 return NULL;
98}
99
107static const AVCodec *FindHWDecoder(enum AVCodecID codecId)
108{
109 const AVCodec *codec;
110 void *i = 0;
111
112 while ((codec = av_codec_iterate(&i))) {
114 continue;
115 if (codec->id != codecId)
116 continue;
117
118 const AVCodecHWConfig *config = FindHWConfig(codec);
119 if (config)
120 return codec;
121 }
122
123 return NULL;
124}
125
133static const AVCodec *FindSWDecoder(enum AVCodecID codecId)
134{
135 return avcodec_find_decoder(codecId);
136}
137
146 : m_identifier(identifier),
147 m_hardwareQuirks(hardwareQuirks)
148{
150
151#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58,18,100)
152 avcodec_register_all(); // register all formats and codecs
153#endif
154}
155
171 int width, int height)
172{
173 m_mutex.Lock();
174 if (m_pVideoCtx != nullptr) {
175 m_mutex.Unlock();
176 return 0;
177 }
178
179 const AVCodec *codec = nullptr;
180 m_isHardwareDecoder = false;
181
185 swCodecForced = true;
186
187 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: Try to open decoder for codec \"%s\"%s", m_identifier, __FUNCTION__,
188 avcodec_get_name(codecId), swCodecForced ? " (sw decoding forced)" : "");
189
190 if (!swCodecForced)
191 codec = FindHWDecoder(codecId);
192
193 if (!codec) {
194 if (!swCodecForced)
195 LOGDEBUG2(L_CODEC, "videocodec: %s: no HW decoder found for codec \"%s\", try software decoder%s", __FUNCTION__, avcodec_get_name(codecId), swCodecForced ? " (forced)" : "");
196 codec = FindSWDecoder(codecId);
197 } else {
198 m_isHardwareDecoder = true;
199 }
200
201 if (!codec) {
202 LOGERROR("videocodec: %s: %s: Could not find any decoder for codec \"%s\"!", m_identifier, __FUNCTION__, avcodec_get_name(codecId));
203 m_mutex.Unlock();
204 return -1;
205 }
206
208 if (!m_pVideoCtx) {
209 LOGERROR("videocodec: %s: %s: can't alloc codec context!", m_identifier, __FUNCTION__);
210 m_mutex.Unlock();
211 return -1;
212 }
213
215 static AVBufferRef *hwDeviceCtx = NULL;
216
217 if (config && (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)) {
218 const char *type_name = av_hwdevice_get_type_name(config->device_type);
219 if (av_hwdevice_ctx_create(&hwDeviceCtx, config->device_type, NULL, NULL, 0) < 0) {
221 LOGERROR("videocodec: %s: %s: Error creating HW context %s", m_identifier, __FUNCTION__,
222 type_name ? type_name : "unknown");
223 m_mutex.Unlock();
224 return -1;
225 }
226 m_pVideoCtx->hw_device_ctx = hwDeviceCtx;
228 }
229
230 if (par) {
232 LOGERROR("videocodec: %s: %s: insert parameters to context failed!", m_identifier, __FUNCTION__);
233 }
234
235 m_pVideoCtx->codec_id = codecId;
236 m_pVideoCtx->get_format = GetFormat;
237 m_pVideoCtx->opaque = this;
238 m_pVideoCtx->pkt_timebase.num = 1;
239 m_pVideoCtx->pkt_timebase.den = 90000;
240
241 if (av_q2d(timebase) > 0)
242 m_pVideoCtx->pkt_timebase = timebase;
243
244 if (codecId == AV_CODEC_ID_H264) {
245 if (par) {
246 m_pVideoCtx->coded_width = par->width;
247 m_pVideoCtx->coded_height = par->height;
248 m_pVideoCtx->width = par->width;
249 m_pVideoCtx->height = par->height;
250 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: Set width %d and height %d from par", m_identifier, __FUNCTION__, par->width, par->height);
251 } else if (width && height) {
252 m_pVideoCtx->coded_width = width;
253 m_pVideoCtx->coded_height = height;
254 m_pVideoCtx->width = width;
255 m_pVideoCtx->height = height;
256 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: Set width %d and height %d forced", m_identifier, __FUNCTION__, width, height);
257 }
258 }
259
260 if (codec->capabilities & AV_CODEC_CAP_FRAME_THREADS ||
262 m_pVideoCtx->thread_count = !m_isHardwareDecoder ? 4 : 1;
263 }
264
265 if (codec->capabilities & AV_CODEC_CAP_SLICE_THREADS)
266 m_pVideoCtx->thread_type = FF_THREAD_SLICE;
267/*
268 if (strstr(codec->name, "_v4l2")) {
269 if (av_opt_set_int(m_pVideoCtx->priv_data, "num_capture_buffers", NUM_CAPTURE_BUFFERS, 0) < 0) {
270 LOGERROR("videocodec: %s: can't set %d num_capture_buffers", __FUNCTION__, NUM_CAPTURE_BUFFERS);
271 }
272 LOGDEBUG2(L_CODEC, "cVideoDecoder::Open: set num_capture_buffers %d", NUM_CAPTURE_BUFFERS);
273 if (av_opt_set_int(m_pVideoCtx->priv_data, "num_output_buffers", NUM_OUTPUT_BUFFERS, 0) < 0) {
274 LOGERROR("videocodec: %s: can't set %d num_output_buffers", __FUNCTION__, NUM_OUTPUT_BUFFERS);
275 }
276 LOGDEBUG2(L_CODEC, "videocodec: %s: set num_output_buffers %d", __FUNCTION__, NUM_OUTPUT_BUFFERS);
277 }
278*/
280 if (err < 0) {
282 if (!m_isHardwareDecoder) {
283 LOGERROR("videocodec: %s: %s: Error opening the decoder: %s", m_identifier, __FUNCTION__, av_err2str(err));
284 m_mutex.Unlock();
285 return -1;
286 }
287 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: Could not open hw decoder \"%s\", force using software decoder",
288 m_identifier, __FUNCTION__, codec->long_name ? codec->long_name : codec->name);
289
290 m_mutex.Unlock();
291 return Open(codecId, par, timebase, true, 0, 0);
292 }
293
294 LOGINFO("videocodec: %s: %s (%s) for codec \"%s\" opened%s, using %s decoding with %d threads%s",
296 codec->long_name ? codec->long_name : codec->name,
297 codec->name,
298 avcodec_get_name(codecId),
299 swCodecForced ? " (sw decoding forced)" : "",
300 m_isHardwareDecoder ? "hardware" : "software",
301 m_pVideoCtx->thread_count,
302 m_isHardwareDecoder ? " 🤩" : "");
303
304 m_pCodecString = codec->long_name ? codec->long_name : codec->name;
307 m_mutex.Unlock();
308 return 0;
309}
310
315{
316 m_mutex.Lock();
317 if (m_pVideoCtx != nullptr) {
318 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: m_pVideoCtx %p", m_identifier, __FUNCTION__, m_pVideoCtx);
319 m_lastCodedWidth = m_pVideoCtx->coded_width;
320 m_lastCodedHeight = m_pVideoCtx->coded_height;
322 m_pVideoCtx = nullptr;
323 }
324 m_mutex.Unlock();
326}
327
337{
339 const AVBitStreamFilter *f;
340 size_t extradataSize;
342 int ret = 0;
343
344 f = av_bsf_get_by_name("extract_extradata");
345 if (!f) {
346 LOGERROR("videocodec: %s: %s: extradata av_bsf_get_by_name failed!", m_identifier, __FUNCTION__);
347 return -1;
348 }
349
350 ret = av_bsf_alloc(f, &bsfCtx);
351 if (ret < 0) {
352 LOGERROR("videocodec: %s: %s: extradata av_bsf_alloc failed!", m_identifier, __FUNCTION__);
353 return ret;
354 }
355
356 bsfCtx->par_in->codec_id = m_pVideoCtx->codec_id;
357
359 if (ret < 0) {
360 LOGERROR("videocodec: %s: %s: extradata av_bsf_init failed!", m_identifier, __FUNCTION__);
362 return ret;
363 }
364
367
368 if (!dstPkt) {
369 LOGERROR("videocodec: %s: %s: extradata av_packet_alloc failed!", m_identifier, __FUNCTION__);
371 return -1;
372 }
373
375 if (ret < 0) {
376 LOGERROR("videocodec: %s: %s: extradata av_packet_ref failed!", m_identifier, __FUNCTION__);
379 return ret;
380 }
381
383 if (ret < 0) {
384 LOGERROR("videocodec: %s: %s: extradata av_bsf_send_packet failed!", m_identifier, __FUNCTION__);
388 return ret;
389 }
390
392 if (ret < 0) {
393 LOGERROR("videocodec: %s: %s: extradata av_bsf_receive_packet failed!", m_identifier, __FUNCTION__);
397 return ret;
398 }
399
401
404 m_pVideoCtx->extradata_size = extradataSize;
405
409 return ret;
410}
411
423{
424 int ret = 0;
425
426 m_mutex.Lock();
427 if (m_pVideoCtx == nullptr) {
428 m_mutex.Unlock();
429 return AVERROR(EINVAL);
430 }
431
432 // force a flush, if avpkt is NULL, this initiates a decoder drain
433 if (!avpkt) {
434 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: send NULL packet, flush reqeusted", m_identifier, __FUNCTION__);
436 m_mutex.Unlock();
437 return 0;
438 }
439
440 if (!avpkt->size) {
441 m_mutex.Unlock();
442 return AVERROR(EINVAL);
443 }
444
445 // get extradata, if not yet done
446 if (!m_pVideoCtx->extradata_size) {
447 if (!GetExtraData(avpkt))
448 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: set extradata %p %d", m_identifier, __FUNCTION__, m_pVideoCtx->extradata, m_pVideoCtx->extradata_size);
449 }
450
452 if (ret) {
453 if (ret != AVERROR(EAGAIN))
454 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: send_packet ret: %s", m_identifier, __FUNCTION__, av_err2str(ret));
455 m_mutex.Unlock();
456 return ret;
457 }
458
460 LOGDEBUG2(L_PACKET, "videocodec: %s: %s: %6d PTS %s <<---", m_identifier, __FUNCTION__, m_cntPacketsSent, Timestamp2String(avpkt->pts, 90));
461 m_mutex.Unlock();
462 return 0;
463}
464
478{
479 int ret;
481
482 m_mutex.Lock();
483 if (m_pVideoCtx == nullptr) {
484 m_mutex.Unlock();
485 return AVERROR(EINVAL);
486 }
487
488 if (!(pFrame = av_frame_alloc())) {
489 m_mutex.Unlock();
490 LOGFATAL("videocodec: %s: %s: can't allocate decoder frame", m_identifier, __FUNCTION__);
491 }
492
494
495 if (ret) {
496 if (ret == AVERROR_EOF)
497 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: receive_frame ret: AVERROR_EOF", m_identifier, __FUNCTION__);
498 else if (ret != AVERROR(EAGAIN))
499 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: receive_frame ret: %s", m_identifier, __FUNCTION__, av_err2str(ret));
501 m_mutex.Unlock();
502 return ret;
503 }
504
505 if (pFrame->flags == AV_FRAME_FLAG_CORRUPT)
506 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: AV_FRAME_FLAG_CORRUPT", m_identifier, __FUNCTION__);
507
508 // codec artifacts workaround for amlogic H264:
509 // skip QUIRK_CODEC_SKIP_NUM_FRAMES key frames
510 if (m_pVideoCtx->codec_id == AV_CODEC_ID_H264 &&
512 if (IsKeyFrame(pFrame)) {
513 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: artifact workaround - skip %s I-frame nr %d", m_identifier, __FUNCTION__,
514 isInterlacedFrame(pFrame) ? "interlaced" : "progressive", m_cntStartKeyFrames);
515
518 }
519
521 m_mutex.Unlock();
522 return AVERROR(EAGAIN);
523 }
524
525 *frame = pFrame;
526
528 LOGDEBUG2(L_PACKET, "videocodec: %s: %s: %6d PTS %s --->> (%2d)%s", m_identifier, __FUNCTION__,
530 isInterlacedFrame(pFrame) ? " I" : "");
531 m_mutex.Unlock();
532 return 0;
533}
534
556{
557 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: m_pVideoCtx %p", m_identifier, __FUNCTION__, m_pVideoCtx);
558 Close();
560 return -1;
561 m_cntStartKeyFrames = 0; // currently unused, because we have no hardware which needs both quirks
563
564 return 0;
565}
566
573{
574 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: m_pVideoCtx %p", m_identifier, __FUNCTION__, m_pVideoCtx);
575 m_mutex.Lock();
576 if (m_pVideoCtx)
579 m_mutex.Unlock();
580}
581
590{
591#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(58,7,100)
592 return frame->key_frame;
593#else
594 return frame->flags & AV_FRAME_FLAG_KEY;
595#endif
596}
597
int m_lastCodedHeight
save coded height while closing for a directly reopen
Definition codec_video.h:58
bool m_isHardwareDecoder
true, if this is a hardware decoder
Definition codec_video.h:60
int m_cntFramesReceived
number of decoded frames received from decoder
Definition codec_video.h:53
int m_cntStartKeyFrames
number of keyframes arrived while starting the coded (needed for amlogic h264 decoder in order to dro...
Definition codec_video.h:54
cMutex m_mutex
mutex to lock codec context (TODO: is this needed?)
Definition codec_video.h:51
AVCodecContext * m_pVideoCtx
video codec context
Definition codec_video.h:48
int m_cntPacketsSent
number of packets sent to decoder
Definition codec_video.h:52
int m_lastCodedWidth
save coded width while closing for a directly reopen
Definition codec_video.h:57
const char * m_pCodecString
codec (long) name string
Definition codec_video.h:50
const char * m_identifier
identifier for logging
Definition codec_video.h:49
int m_hardwareQuirks
hardware specific quirks needed for decoder
Definition codec_video.h:59
Video Decoder Header File.
#define LOGDEBUG2
log to LOG_DEBUG and add a prefix
Definition logger.h:42
#define LOGERROR
log to LOG_ERR
Definition logger.h:34
static void LogFFmpegCallback(void *, int, const char *, va_list)
Callback for ffmpeg logs.
Definition logger.cpp:248
#define LOGWARNING
log to LOG_WARN
Definition logger.h:36
#define LOGINFO
log to LOG_INFO
Definition logger.h:38
#define LOGFATAL
log to LOG_ERR and abort
Definition logger.h:32
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:63
@ L_CODEC
codec logs
Definition logger.h:56
@ QUIRK_CODEC_SKIP_FIRST_FRAMES
set, if codec should skip first I-Frames
Definition videostream.h:48
@ QUIRK_CODEC_SKIP_NUM_FRAMES
skip QUIRK_CODEC_SKIP_NUM_FRAMES, in case QUIRK_CODEC_SKIP_FIRST_FRAMES is set
Definition videostream.h:49
@ QUIRK_CODEC_DISABLE_MPEG_HW
set, if disable mpeg hardware decoder
Definition videostream.h:50
@ QUIRK_CODEC_DISABLE_H264_HW
set, if disable h264 hardware decoder
Definition videostream.h:51
int ReopenCodec(enum AVCodecID, AVCodecParameters *, AVRational, int)
Reopen the video decoder.
int SendPacket(const AVPacket *)
Send a video packet to be decoded.
int Open(enum AVCodecID, AVCodecParameters *, AVRational, bool, int, int)
Open the video decoder.
static const AVCodecHWConfig * FindHWConfig(const AVCodec *codec)
Find a hardware based video decoder config.
int GetExtraData(const AVPacket *)
Get extradata from avpkt.
static enum AVPixelFormat GetFormat(AVCodecContext *videoCtx, const enum AVPixelFormat *fmt)
Callback to negotiate the PixelFormat.
void FlushBuffers(void)
Flush the video decoder buffers.
bool IsKeyFrame(AVFrame *)
Check, if this is a key frame.
int ReceiveFrame(AVFrame **)
Receive a decoded a video frame.
static const AVCodec * FindHWDecoder(enum AVCodecID codecId)
Find a suitable video codec (hardware decoding)
void Close(void)
Close video decoder.
cVideoDecoder(int, const char *)
Create a new video decoder.
static const AVCodec * FindSWDecoder(enum AVCodecID codecId)
Find a suitable video codec (software decoding)
Logger Header File.
Misc Functions.
Video Input Stream Header File.