vdr-plugin-softhddevice-drm-gles 1.7.3-98a2ef7
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 <vdr/remux.h>
27
28#include "codec_video.h"
29#include "logger.h"
30#include "misc.h"
31
32//#define NUM_CAPTURE_BUFFERS 10
33//#define NUM_OUTPUT_BUFFERS 10
34
35/********************************************************************************
36 * Static functions
37 *******************************************************************************/
38
52 const enum AVPixelFormat *fmt)
53{
54 while (*fmt != AV_PIX_FMT_NONE) {
55 LOGDEBUG2(L_CODEC, "videocodec: %s: PixelFormat: %s videoCtx->pix_fmt: %s sw_pix_fmt: %s Codecname: %s",
58 av_get_pix_fmt_name(videoCtx->sw_pix_fmt), videoCtx->codec->name);
59 if (*fmt == AV_PIX_FMT_DRM_PRIME) {
61 }
62
63 if (*fmt == AV_PIX_FMT_YUV420P) {
64 return AV_PIX_FMT_YUV420P;
65 }
66 fmt++;
67 }
68 LOGWARNING("videocodec: %s: No pixel format found! Set default format.", __FUNCTION__);
69
71}
72
83{
84 const AVCodecHWConfig *config = NULL;
85 for (int n = 0; (config = avcodec_get_hw_config(codec, n)); n++)
86 {
87 if (!(config->pix_fmt == AV_PIX_FMT_DRM_PRIME))
88 continue;
89
90 if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) ||
91 (config->methods & AV_CODEC_HW_CONFIG_METHOD_INTERNAL))
92 return config;
93 }
94
95 return NULL;
96}
97
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
135static const AVCodec *FindSWDecoder(enum AVCodecID codecId)
136{
137 return avcodec_find_decoder(codecId);
138}
139
147 : m_identifier(identifier)
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
169{
170 std::unique_lock<std::mutex> lock(m_mutex);
171
172 if (m_pVideoCtx != nullptr)
173 return 0;
174
175 const AVCodec *codec = nullptr;
176 m_isHardwareDecoder = false;
177
178 auto now = std::chrono::steady_clock::now();
179 auto durationSinceChannelSwitchMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - LOGGER->GetChannelSwitchStartTime()).count();
180 auto durationSinceFirstPacketMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - LOGGER->GetChannelSwitchDataReceivedTime()).count();
181 LOGDEBUG2(L_AV_SYNC, "TRACE: +%5dms decoder open (+%5dms after first data was received)", durationSinceChannelSwitchMs, durationSinceFirstPacketMs);
182
183 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: Try to open decoder for codec \"%s\"%s", m_identifier, __FUNCTION__,
184 avcodec_get_name(codecId), forceSoftwareDecoder ? " (sw decoding forced)" : "");
185
187 codec = FindHWDecoder(codecId);
188
189 if (codec) {
190 m_isHardwareDecoder = true;
191 } else {
193 LOGDEBUG2(L_CODEC, "videocodec: %s: no HW decoder found for codec \"%s\", try software decoder%s", __FUNCTION__, avcodec_get_name(codecId), forceSoftwareDecoder ? " (forced)" : "");
194 codec = FindSWDecoder(codecId);
195 }
196
197 if (!codec) {
198 LOGERROR("videocodec: %s: %s: Could not find any decoder for codec \"%s\"!", m_identifier, __FUNCTION__, avcodec_get_name(codecId));
199 return -1;
200 }
201
203 if (!m_pVideoCtx) {
204 LOGERROR("videocodec: %s: %s: can't alloc codec context!", m_identifier, __FUNCTION__);
205 return -1;
206 }
207
209 static AVBufferRef *hwDeviceCtx = NULL;
210
211 if (config && (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)) {
212 const char *type_name = av_hwdevice_get_type_name(config->device_type);
213 if (av_hwdevice_ctx_create(&hwDeviceCtx, config->device_type, NULL, NULL, 0) < 0) {
215 LOGERROR("videocodec: %s: %s: Error creating HW context %s", m_identifier, __FUNCTION__,
216 type_name ? type_name : "unknown");
217 return -1;
218 }
219 m_pVideoCtx->hw_device_ctx = hwDeviceCtx;
221 }
222
224 LOGERROR("videocodec: %s: %s: insert parameters to context failed!", m_identifier, __FUNCTION__);
225
226 m_pVideoCtx->codec_id = codecId;
227 m_pVideoCtx->get_format = GetFormat;
228 m_pVideoCtx->opaque = this;
229 m_pVideoCtx->pkt_timebase.num = 1;
230 m_pVideoCtx->pkt_timebase.den = PTSTICKS;
231
232 if (av_q2d(timebase) > 0)
233 m_pVideoCtx->pkt_timebase = timebase;
234
235 if (codecId == AV_CODEC_ID_H264 && par) {
236 m_pVideoCtx->coded_width = par->width;
237 m_pVideoCtx->coded_height = par->height;
238 m_pVideoCtx->width = par->width;
239 m_pVideoCtx->height = par->height;
240 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: Set width %d and height %d from par", m_identifier, __FUNCTION__, par->width, par->height);
241 }
242
244 m_pVideoCtx->thread_count = !m_isHardwareDecoder ? 4 : 1;
245
246 if (codec->capabilities & AV_CODEC_CAP_SLICE_THREADS)
247 m_pVideoCtx->thread_type = FF_THREAD_SLICE;
248/*
249 if (strstr(codec->name, "_v4l2")) {
250 if (av_opt_set_int(m_pVideoCtx->priv_data, "num_capture_buffers", NUM_CAPTURE_BUFFERS, 0) < 0) {
251 LOGERROR("videocodec: %s: can't set %d num_capture_buffers", __FUNCTION__, NUM_CAPTURE_BUFFERS);
252 }
253 LOGDEBUG2(L_CODEC, "cVideoDecoder::Open: set num_capture_buffers %d", NUM_CAPTURE_BUFFERS);
254 if (av_opt_set_int(m_pVideoCtx->priv_data, "num_output_buffers", NUM_OUTPUT_BUFFERS, 0) < 0) {
255 LOGERROR("videocodec: %s: can't set %d num_output_buffers", __FUNCTION__, NUM_OUTPUT_BUFFERS);
256 }
257 LOGDEBUG2(L_CODEC, "videocodec: %s: set num_output_buffers %d", __FUNCTION__, NUM_OUTPUT_BUFFERS);
258 }
259*/
261 if (err < 0) {
263 if (!m_isHardwareDecoder) {
264 LOGERROR("videocodec: %s: %s: Error opening the decoder: %s", m_identifier, __FUNCTION__, av_err2str(err));
265 return -1;
266 }
267 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: Could not open hw decoder \"%s\", force using software decoder",
268 m_identifier, __FUNCTION__, codec->long_name ? codec->long_name : codec->name);
269
270 // unlock here, otherwise we run into a deadlock
271 lock.unlock();
272 return Open(codecId, par, timebase, true);
273 }
274
275 LOGDEBUG("videocodec: %s: %s (%s) for codec \"%s\" opened%s, using %s decoding with %d threads%s",
277 codec->long_name ? codec->long_name : codec->name,
278 codec->name,
279 avcodec_get_name(codecId),
280 forceSoftwareDecoder ? " (sw decoding forced)" : "",
281 m_isHardwareDecoder ? "hardware" : "software",
282 m_pVideoCtx->thread_count,
283 m_isHardwareDecoder ? " 🤩" : "");
284
285 m_pCodecString = codec->long_name ? codec->long_name : codec->name;
287
288 now = std::chrono::steady_clock::now();
289 durationSinceChannelSwitchMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - LOGGER->GetChannelSwitchStartTime()).count();
290 durationSinceFirstPacketMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - LOGGER->GetChannelSwitchDataReceivedTime()).count();
291 LOGDEBUG2(L_AV_SYNC, "TRACE: +%5dms decoder opened (+%5dms after first data was received)", durationSinceChannelSwitchMs, durationSinceFirstPacketMs);
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);
306 m_pVideoCtx = nullptr;
307 }
309}
310
320{
322 const AVBitStreamFilter *f;
323 size_t extradataSize;
325 int ret = 0;
326
327 f = av_bsf_get_by_name("extract_extradata");
328 if (!f) {
329 LOGERROR("videocodec: %s: %s: extradata av_bsf_get_by_name failed!", m_identifier, __FUNCTION__);
330 return -1;
331 }
332
333 ret = av_bsf_alloc(f, &bsfCtx);
334 if (ret < 0) {
335 LOGERROR("videocodec: %s: %s: extradata av_bsf_alloc failed!", m_identifier, __FUNCTION__);
336 return ret;
337 }
338
339 bsfCtx->par_in->codec_id = m_pVideoCtx->codec_id;
340
342 if (ret < 0) {
343 LOGERROR("videocodec: %s: %s: extradata av_bsf_init failed!", m_identifier, __FUNCTION__);
345 return ret;
346 }
347
350
351 if (!dstPkt) {
352 LOGERROR("videocodec: %s: %s: extradata av_packet_alloc failed!", m_identifier, __FUNCTION__);
354 return -1;
355 }
356
358 if (ret < 0) {
359 LOGERROR("videocodec: %s: %s: extradata av_packet_ref failed!", m_identifier, __FUNCTION__);
362 return ret;
363 }
364
366 if (ret < 0) {
367 LOGERROR("videocodec: %s: %s: extradata av_bsf_send_packet failed!", m_identifier, __FUNCTION__);
371 return ret;
372 }
373
375 if (ret < 0) {
376 LOGERROR("videocodec: %s: %s: extradata av_bsf_receive_packet failed!", m_identifier, __FUNCTION__);
380 return ret;
381 }
382
384
387 m_pVideoCtx->extradata_size = extradataSize;
388
392 return ret;
393}
394
406{
407 std::lock_guard<std::mutex> lock(m_mutex);
408
409 int ret = 0;
410
411 if (m_pVideoCtx == nullptr)
412 return AVERROR(EINVAL);
413
414 // force a flush, if avpkt is NULL, this initiates a decoder drain
415 if (!avpkt) {
416 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: send NULL packet, flush requested", m_identifier, __FUNCTION__);
418 return 0;
419 }
420
421 if (!avpkt->size)
422 return AVERROR(EINVAL);
423
424 // get extradata, if not yet done
425 if (!m_pVideoCtx->extradata_size) {
426 if (!GetExtraData(avpkt))
427 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: set extradata %p %d", m_identifier, __FUNCTION__, m_pVideoCtx->extradata, m_pVideoCtx->extradata_size);
428 }
429
431 if (ret) {
432 if (ret != AVERROR(EAGAIN))
433 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: send_packet ret: %s", m_identifier, __FUNCTION__, av_err2str(ret));
434 return ret;
435 }
436
437 if (!m_cntPacketsSent) {
438 auto now = std::chrono::steady_clock::now();
439 auto durationSinceChannelSwitchMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - LOGGER->GetChannelSwitchStartTime()).count();
440 auto durationSinceFirstPacketMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - LOGGER->GetChannelSwitchDataReceivedTime()).count();
441 LOGDEBUG2(L_AV_SYNC, "TRACE: +%5dms decoder first packet sent (+%5dms after first data was received)", durationSinceChannelSwitchMs, durationSinceFirstPacketMs);
442 }
443
445
446 LOGDEBUG2(L_PACKET, "videocodec: %s: %s: %6d PTS %s <<---", m_identifier, __FUNCTION__, m_cntPacketsSent, Timestamp2StringPts(avpkt->pts));
447
448 return 0;
449}
450
464{
465 std::lock_guard<std::mutex> lock(m_mutex);
466
467 int ret;
469
470 if (m_pVideoCtx == nullptr)
471 return AVERROR(EINVAL);
472
473 if (!(pFrame = av_frame_alloc()))
474 LOGFATAL("videocodec: %s: %s: can't allocate decoder frame", m_identifier, __FUNCTION__);
475
477
478 if (ret) {
479 if (ret == AVERROR_EOF)
480 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: receive_frame ret: AVERROR_EOF", m_identifier, __FUNCTION__);
481 else if (ret != AVERROR(EAGAIN))
482 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: receive_frame ret: %s", m_identifier, __FUNCTION__, av_err2str(ret));
484 return ret;
485 }
486
487 if (pFrame->flags == AV_FRAME_FLAG_CORRUPT)
488 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: AV_FRAME_FLAG_CORRUPT", m_identifier, __FUNCTION__);
489
490 *frame = pFrame;
491
492 if (!m_cntFramesReceived) {
493 auto now = std::chrono::steady_clock::now();
494 auto durationSinceChannelSwitchMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - LOGGER->GetChannelSwitchStartTime()).count();
495 auto durationSinceFirstPacketMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - LOGGER->GetChannelSwitchDataReceivedTime()).count();
496 LOGDEBUG2(L_AV_SYNC, "TRACE: +%5dms decoder first frame received (+%5dms after first data was received)", durationSinceChannelSwitchMs, durationSinceFirstPacketMs);
497 }
498
500 LOGDEBUG2(L_PACKET, "videocodec: %s: %s: %6d PTS %s --->> (%2d)%s", m_identifier, __FUNCTION__,
502 isInterlacedFrame(pFrame) ? " I" : "");
503
504 return 0;
505}
506
528{
529 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: m_pVideoCtx %p", m_identifier, __FUNCTION__, m_pVideoCtx);
530 Close();
531 if (Open(codecId, par, timebase, forceSoftwareDecoding))
532 return -1;
533
534 return 0;
535}
536
543{
544 std::lock_guard<std::mutex> lock(m_mutex);
545
546 LOGDEBUG2(L_CODEC, "videocodec: %s: %s: m_pVideoCtx %p", m_identifier, __FUNCTION__, m_pVideoCtx);
547
548 if (m_pVideoCtx)
550
552}
static void LogFFmpegCallback(void *, int, const char *, va_list)
Callback for ffmpeg logs.
Definition logger.cpp:231
cVideoDecoder(const char *)
Create a new video decoder.
int ReopenCodec(enum AVCodecID, AVCodecParameters *, AVRational, int)
Reopen the video decoder.
bool m_isHardwareDecoder
true, if this is a hardware decoder
Definition codec_video.h:59
int SendPacket(const AVPacket *)
Send a video packet to be decoded.
int m_cntFramesReceived
number of decoded frames received from decoder
Definition codec_video.h:58
int GetExtraData(const AVPacket *)
Get extradata from avpkt.
int Open(enum AVCodecID, AVCodecParameters *, AVRational, bool)
Open the video decoder.
void FlushBuffers(void)
Flush the video decoder buffers.
void ResetPacketCounter(void)
Definition codec_video.h:62
std::mutex m_mutex
mutex to lock codec context
Definition codec_video.h:56
AVCodecContext * m_pVideoCtx
video codec context
Definition codec_video.h:53
int m_cntPacketsSent
number of packets sent to decoder
Definition codec_video.h:57
const char * m_pCodecString
codec (long) name string
Definition codec_video.h:55
const char * m_identifier
identifier for logging
Definition codec_video.h:54
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:48
#define LOGDEBUG
log to LOG_DEBUG
Definition logger.h:46
#define LOGERROR
log to LOG_ERR
Definition logger.h:40
#define LOGWARNING
log to LOG_WARN
Definition logger.h:42
#define LOGGER
shortcut to the logger
Definition logger.h:51
#define LOGFATAL
log to LOG_ERR and abort
Definition logger.h:38
static const char * Timestamp2StringPts(int64_t ts)
Input PTS is in PTSTICKS.
Definition misc.h:149
static bool isInterlacedFrame(AVFrame *frame)
Check, if this is an interlaced frame.
Definition misc.h:88
#define av_err2str(err)
Definition misc.h:114
@ L_PACKET
decoder packet/frame tracking logs
Definition logger.h:72
@ L_AV_SYNC
audio/video sync logs
Definition logger.h:61
@ L_CODEC
codec logs
Definition logger.h:65
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.