vdr-plugin-softhddevice-drm-gles 1.5.9-20e15de
codec_video.cpp
Go to the documentation of this file.
1
25extern "C" {
26#include <libavcodec/avcodec.h>
27#include <libavcodec/bsf.h>
28//#include <libavutil/opt.h>
29#include <libavutil/pixdesc.h>
30}
31
32#include <vdr/thread.h>
33
34#include "codec_video.h"
35#include "logger.h"
36#include "misc.h"
37#include "videostream.h"
38
39//#define NUM_CAPTURE_BUFFERS 10
40//#define NUM_OUTPUT_BUFFERS 10
41
42/******************************************************************************
43 * static functions
44 *****************************************************************************/
45
54static enum AVPixelFormat GetFormat(AVCodecContext * videoCtx,
55 const enum AVPixelFormat *fmt)
56{
57 while (*fmt != AV_PIX_FMT_NONE) {
58 LOGDEBUG2(L_CODEC, "videocodec: %s: PixelFormat: %s videoCtx->pix_fmt: %s sw_pix_fmt: %s Codecname: %s",
59 __FUNCTION__,
60 av_get_pix_fmt_name(*fmt), av_get_pix_fmt_name(videoCtx->pix_fmt),
61 av_get_pix_fmt_name(videoCtx->sw_pix_fmt), videoCtx->codec->name);
62 if (*fmt == AV_PIX_FMT_DRM_PRIME) {
63 return AV_PIX_FMT_DRM_PRIME;
64 }
65
66 if (*fmt == AV_PIX_FMT_YUV420P) {
67 return AV_PIX_FMT_YUV420P;
68 }
69 fmt++;
70 }
71 LOGWARNING("videocodec: %s: No pixel format found! Set default format.", __FUNCTION__);
72
73 return avcodec_default_get_format(videoCtx, fmt);
74}
75
83static const AVCodecHWConfig *FindHWConfig(const AVCodec *codec)
84{
85 const AVCodecHWConfig *config = NULL;
86 for (int n = 0; (config = avcodec_get_hw_config(codec, n)); n++)
87 {
88 if (!(config->pix_fmt == AV_PIX_FMT_DRM_PRIME))
89 continue;
90
91 if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) ||
92 (config->methods & AV_CODEC_HW_CONFIG_METHOD_INTERNAL))
93 return config;
94 }
95
96 return NULL;
97}
98
107static const AVCodec *FindHWDecoder(enum AVCodecID codecId)
108{
109 const AVCodec *codec;
110 void *i = 0;
111
112 while ((codec = av_codec_iterate(&i))) {
113 if (!av_codec_is_decoder(codec))
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
134static const AVCodec *FindSWDecoder(enum AVCodecID codecId)
135{
136 return avcodec_find_decoder(codecId);
137}
138
139/******************************************************************************
140 * cVideoDecoder class
141 *****************************************************************************/
142
150cVideoDecoder::cVideoDecoder(int hardwareQuirks, const char *identifier)
151 : m_identifier(identifier),
152 m_hardwareQuirks(hardwareQuirks)
153{
154 av_log_set_callback(cSoftHdLogger::LogFFmpegCallback);
155
156#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58,18,100)
157 avcodec_register_all(); // register all formats and codecs
158#endif
159}
160
174int cVideoDecoder::Open(enum AVCodecID codecId, AVCodecParameters * par,
175 AVRational timebase, bool forceSoftwareDecoder,
176 int width, int height)
177{
178 m_mutex.Lock();
179 if (m_pVideoCtx != nullptr) {
180 m_mutex.Unlock();
181 return 0;
182 }
183
184 const AVCodec *codec = nullptr;
185 m_isHardwareDecoder = false;
186
187 bool swCodecForced = forceSoftwareDecoder;
188 if ((m_hardwareQuirks & QUIRK_CODEC_DISABLE_MPEG_HW && codecId == AV_CODEC_ID_MPEG2VIDEO) ||
189 (m_hardwareQuirks & QUIRK_CODEC_DISABLE_H264_HW && codecId == AV_CODEC_ID_H264))
190 swCodecForced = true;
191
192 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: Try to open decoder for codec \"%s\"%s", m_identifier, __FUNCTION__,
193 avcodec_get_name(codecId), swCodecForced ? " (sw decoding forced)" : "");
194
195 if (!swCodecForced)
196 codec = FindHWDecoder(codecId);
197
198 if (!codec) {
199 if (!swCodecForced)
200 LOGDEBUG2(L_CODEC, "videocodec: %s: no HW decoder found for codec \"%s\", try software decoder%s", __FUNCTION__, avcodec_get_name(codecId), swCodecForced ? " (forced)" : "");
201 codec = FindSWDecoder(codecId);
202 } else {
203 m_isHardwareDecoder = true;
204 }
205
206 if (!codec) {
207 LOGERROR("videocodec: %s: %s: Could not find any decoder for codec \"%s\"!", m_identifier, __FUNCTION__, avcodec_get_name(codecId));
208 m_mutex.Unlock();
209 return -1;
210 }
211
212 m_pVideoCtx = avcodec_alloc_context3(codec);
213 if (!m_pVideoCtx) {
214 LOGERROR("videocodec: %s: %s: can't alloc codec context!", m_identifier, __FUNCTION__);
215 m_mutex.Unlock();
216 return -1;
217 }
218
219 const AVCodecHWConfig *config = m_isHardwareDecoder ? FindHWConfig(codec) : NULL;
220 static AVBufferRef *hwDeviceCtx = NULL;
221
222 if (config && (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)) {
223 const char *type_name = av_hwdevice_get_type_name(config->device_type);
224 if (av_hwdevice_ctx_create(&hwDeviceCtx, config->device_type, NULL, NULL, 0) < 0) {
225 avcodec_free_context(&m_pVideoCtx);
226 LOGERROR("videocodec: %s: %s: Error creating HW context %s", m_identifier, __FUNCTION__,
227 type_name ? type_name : "unknown");
228 m_mutex.Unlock();
229 return -1;
230 }
231 m_pVideoCtx->hw_device_ctx = hwDeviceCtx;
232 m_pVideoCtx->pix_fmt = AV_PIX_FMT_DRM_PRIME;
233 }
234
235 if (par) {
236 if ((avcodec_parameters_to_context(m_pVideoCtx, par)) < 0)
237 LOGERROR("videocodec: %s: %s: insert parameters to context failed!", m_identifier, __FUNCTION__);
238 }
239
240 m_pVideoCtx->codec_id = codecId;
241 m_pVideoCtx->get_format = GetFormat;
242 m_pVideoCtx->opaque = this;
243 m_pVideoCtx->pkt_timebase.num = 1;
244 m_pVideoCtx->pkt_timebase.den = 90000;
245
246 if (av_q2d(timebase) > 0)
247 m_pVideoCtx->pkt_timebase = timebase;
248
249 if (codecId == AV_CODEC_ID_H264) {
250 if (par) {
251 m_pVideoCtx->coded_width = par->width;
252 m_pVideoCtx->coded_height = par->height;
253 m_pVideoCtx->width = par->width;
254 m_pVideoCtx->height = par->height;
255 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: Set width %d and height %d from par", m_identifier, __FUNCTION__, par->width, par->height);
256 } else if (width && height) {
257 m_pVideoCtx->coded_width = width;
258 m_pVideoCtx->coded_height = height;
259 m_pVideoCtx->width = width;
260 m_pVideoCtx->height = height;
261 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: Set width %d and height %d forced", m_identifier, __FUNCTION__, width, height);
262 }
263 }
264
265 if (codec->capabilities & AV_CODEC_CAP_FRAME_THREADS ||
266 AV_CODEC_CAP_SLICE_THREADS) {
267 m_pVideoCtx->thread_count = !m_isHardwareDecoder ? 4 : 1;
268 }
269
270 if (codec->capabilities & AV_CODEC_CAP_SLICE_THREADS)
271 m_pVideoCtx->thread_type = FF_THREAD_SLICE;
272/*
273 if (strstr(codec->name, "_v4l2")) {
274 if (av_opt_set_int(m_pVideoCtx->priv_data, "num_capture_buffers", NUM_CAPTURE_BUFFERS, 0) < 0) {
275 LOGERROR("videocodec: %s: can't set %d num_capture_buffers", __FUNCTION__, NUM_CAPTURE_BUFFERS);
276 }
277 LOGDEBUG2(L_CODEC, "cVideoDecoder::Open: set num_capture_buffers %d", NUM_CAPTURE_BUFFERS);
278 if (av_opt_set_int(m_pVideoCtx->priv_data, "num_output_buffers", NUM_OUTPUT_BUFFERS, 0) < 0) {
279 LOGERROR("videocodec: %s: can't set %d num_output_buffers", __FUNCTION__, NUM_OUTPUT_BUFFERS);
280 }
281 LOGDEBUG2(L_CODEC, "videocodec: %s: set num_output_buffers %d", __FUNCTION__, NUM_OUTPUT_BUFFERS);
282 }
283*/
284 int err = avcodec_open2(m_pVideoCtx, m_pVideoCtx->codec, NULL);
285 if (err < 0) {
286 avcodec_free_context(&m_pVideoCtx);
287 if (!m_isHardwareDecoder) {
288 LOGERROR("videocodec: %s: %s: Error opening the decoder: %s", m_identifier, __FUNCTION__, av_err2str(err));
289 m_mutex.Unlock();
290 return -1;
291 }
292 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: Could not open hw decoder \"%s\", force using software decoder",
293 m_identifier, __FUNCTION__, codec->long_name ? codec->long_name : codec->name);
294
295 m_mutex.Unlock();
296 return Open(codecId, par, timebase, true, 0, 0);
297 }
298
299 LOGINFO("videocodec: %s: %s (%s) for codec \"%s\" opened%s, using %s decoding with %d threads%s",
301 codec->long_name ? codec->long_name : codec->name,
302 codec->name,
303 avcodec_get_name(codecId),
304 swCodecForced ? " (sw decoding forced)" : "",
305 m_isHardwareDecoder ? "hardware" : "software",
306 m_pVideoCtx->thread_count,
307 m_isHardwareDecoder ? " 🤩" : "");
308
309 m_pCodecString = codec->long_name ? codec->long_name : codec->name;
312 m_mutex.Unlock();
313 return 0;
314}
315
320{
321 m_mutex.Lock();
322 if (m_pVideoCtx != nullptr) {
323 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: m_pVideoCtx %p", m_identifier, __FUNCTION__, m_pVideoCtx);
324 m_lastCodedWidth = m_pVideoCtx->coded_width;
325 m_lastCodedHeight = m_pVideoCtx->coded_height;
326 avcodec_free_context(&m_pVideoCtx);
327 m_pVideoCtx = nullptr;
328 }
329 m_mutex.Unlock();
331}
332
341int cVideoDecoder::GetExtraData(const AVPacket * avpkt)
342{
343 AVBSFContext *bsfCtx;
344 const AVBitStreamFilter *f;
345 size_t extradataSize;
346 uint8_t *extradata;
347 int ret = 0;
348
349 f = av_bsf_get_by_name("extract_extradata");
350 if (!f) {
351 LOGERROR("videocodec: %s: %s: extradata av_bsf_get_by_name failed!", m_identifier, __FUNCTION__);
352 return -1;
353 }
354
355 ret = av_bsf_alloc(f, &bsfCtx);
356 if (ret < 0) {
357 LOGERROR("videocodec: %s: %s: extradata av_bsf_alloc failed!", m_identifier, __FUNCTION__);
358 return ret;
359 }
360
361 bsfCtx->par_in->codec_id = m_pVideoCtx->codec_id;
362
363 ret = av_bsf_init(bsfCtx);
364 if (ret < 0) {
365 LOGERROR("videocodec: %s: %s: extradata av_bsf_init failed!", m_identifier, __FUNCTION__);
366 av_bsf_free(&bsfCtx);
367 return ret;
368 }
369
370 AVPacket *dstPkt = av_packet_alloc();
371 AVPacket *pktRef = dstPkt;
372
373 if (!dstPkt) {
374 LOGERROR("videocodec: %s: %s: extradata av_packet_alloc failed!", m_identifier, __FUNCTION__);
375 av_bsf_free(&bsfCtx);
376 return -1;
377 }
378
379 ret = av_packet_ref(pktRef, avpkt);
380 if (ret < 0) {
381 LOGERROR("videocodec: %s: %s: extradata av_packet_ref failed!", m_identifier, __FUNCTION__);
382 av_packet_free(&dstPkt);
383 av_bsf_free(&bsfCtx);
384 return ret;
385 }
386
387 ret = av_bsf_send_packet(bsfCtx, pktRef);
388 if (ret < 0) {
389 LOGERROR("videocodec: %s: %s: extradata av_bsf_send_packet failed!", m_identifier, __FUNCTION__);
390 av_packet_unref(pktRef);
391 av_packet_free(&dstPkt);
392 av_bsf_free(&bsfCtx);
393 return ret;
394 }
395
396 ret = av_bsf_receive_packet(bsfCtx, pktRef);
397 if (ret < 0) {
398 LOGERROR("videocodec: %s: %s: extradata av_bsf_receive_packet failed!", m_identifier, __FUNCTION__);
399 av_packet_unref(pktRef);
400 av_packet_free(&dstPkt);
401 av_bsf_free(&bsfCtx);
402 return ret;
403 }
404
405 extradata = av_packet_get_side_data(pktRef, AV_PKT_DATA_NEW_EXTRADATA, &extradataSize);
406
407 m_pVideoCtx->extradata = (uint8_t *)av_mallocz(extradataSize + AV_INPUT_BUFFER_PADDING_SIZE);
408 memcpy(m_pVideoCtx->extradata, extradata, extradataSize);
409 m_pVideoCtx->extradata_size = extradataSize;
410
411 av_packet_unref(pktRef);
412 av_packet_free(&dstPkt);
413 av_bsf_free(&bsfCtx);
414 return ret;
415}
416
427int cVideoDecoder::SendPacket(const AVPacket *avpkt)
428{
429 int ret = 0;
430
431 m_mutex.Lock();
432 if (m_pVideoCtx == nullptr) {
433 m_mutex.Unlock();
434 return AVERROR(EINVAL);
435 }
436
437 // force a flush, if avpkt is NULL, this initiates a decoder drain
438 if (!avpkt) {
439 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: send NULL packet, flush reqeusted", m_identifier, __FUNCTION__);
440 avcodec_send_packet(m_pVideoCtx, NULL);
441 m_mutex.Unlock();
442 return 0;
443 }
444
445 if (!avpkt->size) {
446 m_mutex.Unlock();
447 return AVERROR(EINVAL);
448 }
449
450 // get extradata, if not yet done
451 if (!m_pVideoCtx->extradata_size) {
452 if (!GetExtraData(avpkt))
453 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: set extradata %p %d", m_identifier, __FUNCTION__, m_pVideoCtx->extradata, m_pVideoCtx->extradata_size);
454 }
455
456 ret = avcodec_send_packet(m_pVideoCtx, avpkt);
457 if (ret) {
458 if (ret != AVERROR(EAGAIN))
459 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: send_packet ret: %s", m_identifier, __FUNCTION__, av_err2str(ret));
460 m_mutex.Unlock();
461 return ret;
462 }
463
465 LOGDEBUG2(L_PACKET, "videocodec: %s: %s: %6d PTS %s <<---", m_identifier, __FUNCTION__, m_cntPacketsSent, Timestamp2String(avpkt->pts, 90));
466 m_mutex.Unlock();
467 return 0;
468}
469
482int cVideoDecoder::ReceiveFrame(AVFrame **frame)
483{
484 int ret;
485 AVFrame *pFrame;
486
487 m_mutex.Lock();
488 if (m_pVideoCtx == nullptr) {
489 m_mutex.Unlock();
490 return AVERROR(EINVAL);
491 }
492
493 if (!(pFrame = av_frame_alloc())) {
494 m_mutex.Unlock();
495 LOGFATAL("videocodec: %s: %s: can't allocate decoder frame", m_identifier, __FUNCTION__);
496 }
497
498 ret = avcodec_receive_frame(m_pVideoCtx, pFrame);
499
500 if (ret) {
501 if (ret == AVERROR_EOF)
502 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: receive_frame ret: AVERROR_EOF", m_identifier, __FUNCTION__);
503 else if (ret != AVERROR(EAGAIN))
504 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: receive_frame ret: %s", m_identifier, __FUNCTION__, av_err2str(ret));
505 av_frame_free(&pFrame);
506 m_mutex.Unlock();
507 return ret;
508 }
509
510 if (pFrame->flags == AV_FRAME_FLAG_CORRUPT)
511 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: AV_FRAME_FLAG_CORRUPT", m_identifier, __FUNCTION__);
512
513 // codec artifacts workaround for amlogic H264:
514 // skip QUIRK_CODEC_SKIP_NUM_FRAMES key frames
515 if (m_pVideoCtx->codec_id == AV_CODEC_ID_H264 &&
517 if (IsKeyFrame(pFrame)) {
518 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: artifact workaround - skip %s I-frame nr %d", m_identifier, __FUNCTION__,
519 isInterlacedFrame(pFrame) ? "interlaced" : "progressive", m_cntStartKeyFrames);
520
523 }
524
525 av_frame_free(&pFrame);
526 m_mutex.Unlock();
527 return AVERROR(EAGAIN);
528 }
529
530 *frame = pFrame;
531
533 LOGDEBUG2(L_PACKET, "videocodec: %s: %s: %6d PTS %s --->> (%2d)%s", m_identifier, __FUNCTION__,
535 isInterlacedFrame(pFrame) ? " I" : "");
536 m_mutex.Unlock();
537 return 0;
538}
539
559int cVideoDecoder::ReopenCodec(enum AVCodecID codecId, AVCodecParameters *par,
560 AVRational timebase, int forceSoftwareDecoding)
561{
562 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: m_pVideoCtx %p", m_identifier, __FUNCTION__, m_pVideoCtx);
563 Close();
564 if (Open(codecId, par, timebase, forceSoftwareDecoding, m_lastCodedWidth, m_lastCodedHeight))
565 return -1;
566 m_cntStartKeyFrames = 0; // currently unused, because we have no hardware which needs both quirks
568
569 return 0;
570}
571
576{
577 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: m_pVideoCtx %p", m_identifier, __FUNCTION__, m_pVideoCtx);
578 m_mutex.Lock();
579 if (m_pVideoCtx)
580 avcodec_flush_buffers(m_pVideoCtx);
582 m_mutex.Unlock();
583}
584
592bool cVideoDecoder::IsKeyFrame(AVFrame *frame)
593{
594#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(58,7,100)
595 return frame->key_frame;
596#else
597 return frame->flags & AV_FRAME_FLAG_KEY;
598#endif
599}
static void LogFFmpegCallback(void *, int, const char *, va_list)
Callback for ffmpeg logs.
Definition logger.cpp:249
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:59
bool m_isHardwareDecoder
true, if this is a hardware decoder
Definition codec_video.h:61
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:54
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:55
void FlushBuffers(void)
Flush the video decoder.
cMutex m_mutex
mutex to lock codec context (TODO: is this needed?)
Definition codec_video.h:52
AVCodecContext * m_pVideoCtx
video codec context
Definition codec_video.h:49
int m_cntPacketsSent
number of packets sent to decoder
Definition codec_video.h:53
int m_lastCodedWidth
save coded width while closing for a directly reopen
Definition codec_video.h:58
const char * m_pCodecString
codec (long) name string
Definition codec_video.h:51
bool IsKeyFrame(AVFrame *)
Check, if this is a key frame.
const char * m_identifier
identifier for logging
Definition codec_video.h:50
int ReceiveFrame(AVFrame **)
Receive a decoded a video frame.
void Close(void)
Close video decoder.
int m_hardwareQuirks
hardware specific quirks needed for decoder
Definition codec_video.h:60
cVideoDecoder(int, const char *)
cVideoDecoder constructor
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)
Video decoder header file.
Logger class header file.
#define LOGDEBUG2
Definition logger.h:45
#define LOGERROR
Definition logger.h:41
#define L_CODEC
Definition logger.h:58
#define LOGWARNING
Definition logger.h:42
#define L_PACKET
Definition logger.h:65
#define LOGINFO
Definition logger.h:43
#define LOGFATAL
Logger macros.
Definition logger.h:40
Misc function header file.
static bool isInterlacedFrame(AVFrame *frame)
Check, if this is an interlaced frame.
Definition misc.h:81
static const char * Timestamp2String(int64_t ts, uint8_t divisor)
Workaround for av_err2str() not working with C++.
Definition misc.h:119
Videostream class header file.
#define QUIRK_CODEC_DISABLE_MPEG_HW
set, if disable mpeg hardware decoder
Definition videostream.h:44
#define QUIRK_CODEC_DISABLE_H264_HW
set, if disable h264 hardware decoder
Definition videostream.h:45
#define QUIRK_CODEC_SKIP_NUM_FRAMES
skip QUIRK_CODEC_SKIP_NUM_FRAMES, in case QUIRK_CODEC_SKIP_FIRST_FRAMES is set
Definition videostream.h:43
#define QUIRK_CODEC_SKIP_FIRST_FRAMES
set, if codec should skip first I-Frames
Definition videostream.h:42