MythTV master
mythv4l2m2mcontext.cpp
Go to the documentation of this file.
1// Sys
2#include <sys/ioctl.h>
3
4// Qt
5#include <QDir>
6#include <algorithm>
7
8// MythTV
9#include "libmythbase/mythconfig.h"
13
14#include "avformatdecoder.h"
15#include "fourcc.h"
16#include "mythplayerui.h"
17#include "v4l2util.h"
18
19#if CONFIG_EGL
21#endif
23
24#if CONFIG_MMAL
26#endif
27
28// FFmpeg
29extern "C" {
30#include "libavcodec/defs.h"
31#include "libavutil/opt.h"
32}
33
34#define LOC QString("V4L2_M2M: ")
35
44 : MythDRMPRIMEContext(Parent, CodecID)
45{
46}
47
49{
51}
52
54 const AVCodec **Codec,
55 const QString &Decoder,
56 AVStream *Stream,
57 uint StreamType)
58{
59 bool decodeonly = Decoder == "v4l2-dec";
60 auto success = static_cast<MythCodecID>((decodeonly ? kCodec_MPEG1_V4L2_DEC : kCodec_MPEG1_V4L2) + (StreamType - 1));
61 auto failure = static_cast<MythCodecID>(kCodec_MPEG1 + (StreamType - 1));
62
63 // not us
64 if (!Decoder.startsWith("v4l2"))
65 return failure;
66
67 if (!decodeonly)
68 if (!FrameTypeIsSupported(*Context, FMT_DRMPRIME))
69 return failure;
70
71 // supported by device driver?
73 switch ((*Codec)->id)
74 {
75 case AV_CODEC_ID_MPEG1VIDEO: mythprofile = MythCodecContext::MPEG1; break;
76 case AV_CODEC_ID_MPEG2VIDEO: mythprofile = MythCodecContext::MPEG2; break;
77 case AV_CODEC_ID_MPEG4: mythprofile = MythCodecContext::MPEG4; break;
78 case AV_CODEC_ID_H263: mythprofile = MythCodecContext::H263; break;
79 case AV_CODEC_ID_H264: mythprofile = MythCodecContext::H264; break;
80 case AV_CODEC_ID_VC1: mythprofile = MythCodecContext::VC1; break;
81 case AV_CODEC_ID_VP8: mythprofile = MythCodecContext::VP8; break;
82 case AV_CODEC_ID_VP9: mythprofile = MythCodecContext::VP9; break;
83 case AV_CODEC_ID_HEVC: mythprofile = MythCodecContext::HEVC; break;
84 default: break;
85 }
86
87 if (mythprofile == MythCodecContext::NoProfile)
88 return failure;
89
90 bool request = false;
91 const auto & standard = MythV4L2M2MContext::GetStandardProfiles();
92 if (!standard.contains(mythprofile))
93 {
95 if (!requests.contains(mythprofile))
96 return failure;
97 request = true;
98 }
99
100#if CONFIG_MMAL
101 // If MMAL is available, assume this is a Raspberry Pi and check the supported
102 // video sizes
103 if (!MythMMALContext::CheckCodecSize((*Context)->width, (*Context)->height, mythprofile))
104 return failure;
105 // As for MMAL, don't try and decode 10bit H264
106 if ((*Codec)->id == AV_CODEC_ID_H264)
107 {
108 if ((*Context)->profile == AV_PROFILE_H264_HIGH_10 ||
109 (*Context)->profile == AV_PROFILE_H264_HIGH_10_INTRA)
110 {
111 return failure;
112 }
113 }
114#endif
115
116 if (request)
117 {
118 (*Context)->pix_fmt = AV_PIX_FMT_DRM_PRIME;
119 return success;
120 }
121
122 return MythDRMPRIMEContext::GetPrimeCodec(Context, Codec, Stream,
123 success, failure, "v4l2m2m",
124 decodeonly ? (*Context)->pix_fmt : AV_PIX_FMT_DRM_PRIME);
125}
126
127int MythV4L2M2MContext::HwDecoderInit(AVCodecContext *Context)
128{
129 if (!Context)
130 return -1;
131
133 return 0;
134
136}
137
138void MythV4L2M2MContext::InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering)
139{
140 // Fairly circular check of whether our codec id is using the request API.
141 // N.B. As for other areas of this class, this assumes there is no overlap
142 // between standard and request API codec support - though both can be used
143 // but for different codecs (as is expected on the Pi 4)
145 switch (m_codecID)
146 {
147 case kCodec_MPEG2_V4L2: profile = MPEG2; break;
148 case kCodec_H264_V4L2: profile = H264; break;
149 case kCodec_VP8_V4L2: profile = VP8; break;
150 case kCodec_VP9_V4L2: profile = VP9; break;
151 case kCodec_HEVC_V4L2: profile = HEVC; break;
152 default: break;
153 }
154
156
158 {
159 DirectRendering = false;
160 return;
161 }
162
164 {
165 DirectRendering = false; // Surely true ?? And then an issue for regular V4L2 as well
166 Context->get_format = MythV4L2M2MContext::GetV4L2RequestFormat;
167 return;
168 }
169
170 MythDRMPRIMEContext::InitVideoCodec(Context, SelectedStream, DirectRendering);
171}
172
173bool MythV4L2M2MContext::RetrieveFrame(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame)
174{
176 return GetBuffer(Context, Frame, AvFrame, 0);
177
178 if (m_request)
179 return MythDRMPRIMEContext::GetDRMBuffer(Context, Frame, AvFrame, 0);
180
181 return MythDRMPRIMEContext::RetrieveFrame(Context, Frame, AvFrame);
182}
183
189void MythV4L2M2MContext::SetDecoderOptions(AVCodecContext* Context, const AVCodec* Codec)
190{
191 if (m_request)
192 return;
193
194 if (!(Context && Codec))
195 return;
196
197 if (!(Codec->priv_class && Context->priv_data))
198 return;
199
200 // best guess currently - this matches the number of capture buffers to the
201 // number of output buffers - and hence to the number of video buffers for
202 // direct rendering
203 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Setting number of capture buffers to 6");
204 av_opt_set_int(Context->priv_data, "num_capture_buffers", 6, 0);
205}
206
213bool MythV4L2M2MContext::GetBuffer(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame, int /*Flags*/)
214{
215 // Sanity checks
216 if (!Context || !AvFrame || !Frame)
217 return false;
218
219 // Ensure we can render this format
220 auto * decoder = static_cast<AvFormatDecoder*>(Context->opaque);
221 auto type = MythAVUtil::PixelFormatToFrameType(static_cast<AVPixelFormat>(AvFrame->format));
222 const auto * supported = Frame->m_renderFormats;
223 // No fallback currently (unlikely)
224#ifdef __cpp_lib_ranges_contains
225 if (!std::ranges::contains(*supported, type))
226#else
227 auto found = std::ranges::find(*supported, type);
228 if (found == supported->end())
229#endif
230 return false;
231
232 // Re-allocate if necessary
233 if ((Frame->m_type != type) || (Frame->m_width != AvFrame->width) || (Frame->m_height != AvFrame->height))
234 if (!VideoBuffers::ReinitBuffer(Frame, type, decoder->GetVideoCodecID(), AvFrame->width, AvFrame->height))
235 return false;
236
237 // Copy data
238 uint count = MythVideoFrame::GetNumPlanes(Frame->m_type);
239 for (uint plane = 0; plane < count; ++plane)
240 {
241 MythVideoFrame::CopyPlane(Frame->m_buffer + Frame->m_offsets[plane],Frame->m_pitches[plane],
242 AvFrame->data[plane], AvFrame->linesize[plane],
243 MythVideoFrame::GetPitchForPlane(Frame->m_type, AvFrame->width, plane),
244 MythVideoFrame::GetHeightForPlane(Frame->m_type, AvFrame->height, plane));
245 }
246
247 return true;
248}
249
250#ifndef V4L2_PIX_FMT_HEVC
251#define V4L2_PIX_FMT_HEVC v4l2_fourcc('H', 'E', 'V', 'C')
252#endif
253
254#ifndef V4L2_PIX_FMT_VP9
255#define V4L2_PIX_FMT_VP9 v4l2_fourcc('V', 'P', '9', '0')
256#endif
257
258#ifndef V4L2_PIX_FMT_NV12_COL128
259#define V4L2_PIX_FMT_NV12_COL128 v4l2_fourcc('N', 'C', '1', '2')
260#endif
261
262#ifndef V4L2_PIX_FMT_NV12_10_COL128
263#define V4L2_PIX_FMT_NV12_10_COL128 v4l2_fourcc('N', 'C', '3', '0')
264#endif
265
267{
268 static const std::vector<V4L2Mapping> s_map
269 {{
270 { V4L2_PIX_FMT_MPEG1, MythCodecContext::MPEG1 },
271 { V4L2_PIX_FMT_MPEG2, MythCodecContext::MPEG2 },
272 { V4L2_PIX_FMT_MPEG4, MythCodecContext::MPEG4 },
273 { V4L2_PIX_FMT_H263, MythCodecContext::H263 },
274 { V4L2_PIX_FMT_H264, MythCodecContext::H264 },
275 { V4L2_PIX_FMT_VC1_ANNEX_G, MythCodecContext::VC1 },
276 { V4L2_PIX_FMT_VP8, MythCodecContext::VP8 },
279 }};
280
281 static QRecursiveMutex lock;
282 static bool s_initialised = false;
283 static V4L2Profiles s_profiles;
284
285 QMutexLocker locker(&lock);
286 if (!s_initialised)
287 s_profiles = GetProfiles(s_map);
288 s_initialised = true;
289 return s_profiles;
290}
291
292V4L2Profiles MythV4L2M2MContext::GetProfiles(const std::vector<V4L2Mapping>& Profiles)
293{
294 static const std::vector<uint32_t> s_formats
295 {
296 V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420, V4L2_PIX_FMT_YUV420M,
297 V4L2_PIX_FMT_YVU420M, V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_NV12M,
298 V4L2_PIX_FMT_NV21, V4L2_PIX_FMT_NV21M, V4L2_PIX_FMT_NV12_COL128,
300 };
301
302 V4L2Profiles result;
303
304 const QString root("/dev/");
305 QDir dir(root);
306 QStringList namefilters;
307 namefilters.append("video*");
308 auto devices = dir.entryList(namefilters, QDir::Files |QDir::System);
309 for (const QString& device : std::as_const(devices))
310 {
311 V4L2util v4l2dev(root + device);
312 uint32_t caps = v4l2dev.GetCapabilities();
313 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Device: %1 Driver: '%2' Capabilities: 0x%3")
314 .arg(v4l2dev.GetDeviceName(), v4l2dev.GetDriverName(), QString::number(caps, 16)));
315
316 // check capture and output support
317 // these mimic the device checks in v4l2_m2m.c
318 bool mplanar = ((caps & (V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE)) != 0U) &&
319 ((caps & V4L2_CAP_STREAMING) != 0U);
320 bool mplanarm2m = (caps & V4L2_CAP_VIDEO_M2M_MPLANE) != 0U;
321 bool splanar = ((caps & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT)) != 0U) &&
322 ((caps & V4L2_CAP_STREAMING) != 0U);
323 bool splanarm2m = (caps & V4L2_CAP_VIDEO_M2M) != 0U;
324
325 if (!(mplanar || mplanarm2m || splanar || splanarm2m))
326 continue;
327
328 v4l2_buf_type capturetype = V4L2_BUF_TYPE_VIDEO_CAPTURE;
329 v4l2_buf_type outputtype = V4L2_BUF_TYPE_VIDEO_OUTPUT;
330
331 if (mplanar || mplanarm2m)
332 {
333 capturetype = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
334 outputtype = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
335 }
336
337 // check codec support
338 QSize dummy{0, 0};
339
340 for (const auto & profile : Profiles)
341 {
342 bool found = false;
343 uint32_t v4l2pixfmt = profile.first;
344 auto mythprofile = profile.second;
345 struct v4l2_fmtdesc fdesc {};
346 memset(&fdesc, 0, sizeof(fdesc));
347
348 // check output first
349 fdesc.type = outputtype;
350 while (!found)
351 {
352 int res = ioctl(v4l2dev.FD(), VIDIOC_ENUM_FMT, &fdesc);
353 if (res)
354 break;
355 if (fdesc.pixelformat == v4l2pixfmt)
356 found = true;
357 fdesc.index++;
358 }
359
360 if (found)
361 {
362 QStringList pixformats;
363 bool foundfmt = false;
364 // check capture
365 memset(&fdesc, 0, sizeof(fdesc));
366 fdesc.type = capturetype;
367 while (true)
368 {
369 int res = ioctl(v4l2dev.FD(), VIDIOC_ENUM_FMT, &fdesc);
370 if (res)
371 break;
372 pixformats.append(fourcc_str(static_cast<int>(fdesc.pixelformat)));
373#ifdef __cpp_lib_ranges_contains
374 if (std::ranges::contains(s_formats, fdesc.pixelformat))
375#else
376 if (std::ranges::find(s_formats, fdesc.pixelformat) != s_formats.cend())
377#endif
378 {
379 if (!result.contains(mythprofile))
380 result.append(mythprofile);
381 foundfmt = true;
382 break;
383 }
384 fdesc.index++;
385 }
386
387 if (!foundfmt)
388 {
389 if (pixformats.isEmpty())
390 pixformats.append("None");
391 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Codec '%1' has no supported formats (Supported: %2)")
392 .arg(MythCodecContext::GetProfileDescription(mythprofile, dummy), pixformats.join((","))));
393 }
394 }
395 }
396 }
397
398 return result;
399}
400
401void MythV4L2M2MContext::GetDecoderList(QStringList &Decoders)
402{
403 const auto & profiles = MythV4L2M2MContext::GetStandardProfiles();
404 if (!profiles.isEmpty())
405 {
406 QSize size(0, 0);
407 Decoders.append("V4L2:");
409 Decoders.append(MythCodecContext::GetProfileDescription(profile, size));
410 }
411
413 if (!requests.isEmpty())
414 {
415 QSize size(0, 0);
416 Decoders.append("V4L2 Request:");
418 Decoders.append(MythCodecContext::GetProfileDescription(profile, size));
419 }
420
421}
422
423bool MythV4L2M2MContext::HaveV4L2Codecs(bool Reinit /*=false*/)
424{
425 static QRecursiveMutex lock;
426 QMutexLocker locker(&lock);
427 static bool s_checked = false;
428 static bool s_available = false;
429
430 if (s_checked && !Reinit)
431 return s_available;
432 s_checked = true;
433
434 const auto & standard = MythV4L2M2MContext::GetStandardProfiles();
435 const auto & request = MythV4L2M2MContext::GetRequestProfiles();
436 if (standard.isEmpty() && request.isEmpty())
437 {
438 LOG(VB_GENERAL, LOG_INFO, LOC + "No V4L2 decoders found");
439 return s_available;
440 }
441
442 LOG(VB_GENERAL, LOG_INFO, LOC + "Supported/available V4L2 decoders:");
443 s_available = true;
444 QSize size {0, 0};
445 for (auto profile : std::as_const(standard))
446 LOG(VB_GENERAL, LOG_INFO, LOC + MythCodecContext::GetProfileDescription(profile, size));
447 for (auto profile : std::as_const(request))
448 LOG(VB_GENERAL, LOG_INFO, LOC + MythCodecContext::GetProfileDescription(profile, size) + "(Request)");
449 return s_available;
450}
451
452#ifndef V4L2_PIX_FMT_MPEG2_SLICE
453#define V4L2_PIX_FMT_MPEG2_SLICE v4l2_fourcc('M', 'G', '2', 'S')
454#endif
455
456#ifndef V4L2_PIX_FMT_H264_SLICE
457#define V4L2_PIX_FMT_H264_SLICE v4l2_fourcc('S', '2', '6', '4')
458#endif
459
460#ifndef V4L2_PIX_FMT_VP8_FRAME
461#define V4L2_PIX_FMT_VP8_FRAME v4l2_fourcc('V', 'P', '8', 'F')
462#endif
463
464#ifndef V4L2_PIX_FMT_VP9_FRAME
465#define V4L2_PIX_FMT_VP9_FRAME v4l2_fourcc('V', 'P', '9', 'F')
466#endif
467
468#ifndef V4L2_PIX_FMT_HEVC_SLICE
469#define V4L2_PIX_FMT_HEVC_SLICE v4l2_fourcc('S', '2', '6', '5')
470#endif
471
473{
474 static const std::vector<V4L2Mapping> s_map
475 {{
481 }};
482
483 static QRecursiveMutex lock;
484 static bool s_initialised = false;
485 static V4L2Profiles s_profiles;
486
487 QMutexLocker locker(&lock);
488 if (!s_initialised)
489 s_profiles = GetProfiles(s_map);
490 s_initialised = true;
491 return s_profiles;
492}
493
494AVPixelFormat MythV4L2M2MContext::GetV4L2RequestFormat(AVCodecContext *Context, const AVPixelFormat *PixFmt)
495{
496 while (*PixFmt != AV_PIX_FMT_NONE)
497 {
498 if (*PixFmt == AV_PIX_FMT_DRM_PRIME)
499 {
501 "V4L2 request context creation") >= 0)
502 {
503 return AV_PIX_FMT_DRM_PRIME;
504 }
505 }
506 PixFmt++;
507 }
508 return AV_PIX_FMT_NONE;
509}
510
512{
513 if (!Context || !gCoreContext->IsUIThread())
514 return -1;
515
516 // N.B. Interop support should already have been checked
517 // Create the device context
518 auto * hwdeviceref = MythCodecContext::CreateDevice(AV_HWDEVICE_TYPE_DRM, nullptr);
519 if (!hwdeviceref)
520 return -1;
521
522 Context->hw_device_ctx = hwdeviceref;
523 return 0;
524}
AVFrame AVFrame
A decoder for media files.
static VideoFrameType PixelFormatToFrameType(AVPixelFormat Fmt)
Definition: mythavutil.cpp:75
static bool FrameTypeIsSupported(AVCodecContext *Context, VideoFrameType Format)
static AVBufferRef * CreateDevice(AVHWDeviceType Type, MythInteropGPU *Interop, const QString &Device=QString())
static int InitialiseDecoder2(AVCodecContext *Context, CreateHWDecoder Callback, const QString &Debug)
Initialise a hardware decoder that is NOT expected to use AVHWFramesContext.
static QString GetProfileDescription(CodecProfile Profile, QSize Size, VideoFrameType Format=FMT_NONE, uint ColorDepth=0)
MythCodecID m_codecID
A generic context handler for codecs that return AV_PIX_FMT_DRM_PRIME frames.
void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering) override
bool GetDRMBuffer(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame, int)
static MythCodecID GetPrimeCodec(AVCodecContext **Context, const AVCodec **Codec, AVStream *Stream, MythCodecID Successs, MythCodecID Failure, const QString &CodecName, AVPixelFormat Format)
int HwDecoderInit(AVCodecContext *Context) override
bool RetrieveFrame(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame) override
static bool CheckCodecSize(int Width, int Height, MythCodecContext::CodecProfile Profile)
static MythCodecID GetSupportedCodec(AVCodecContext **Context, const AVCodec **Codec, const QString &Decoder, AVStream *Stream, uint StreamType)
static int InitialiseV4L2RequestContext(AVCodecContext *Context)
void SetDecoderOptions(AVCodecContext *Context, const AVCodec *Codec) override
Reduce the number of capture buffers.
static const V4L2Profiles & GetRequestProfiles()
static V4L2Profiles GetProfiles(const std::vector< V4L2Mapping > &Profiles)
static enum AVPixelFormat GetV4L2RequestFormat(AVCodecContext *Context, const AVPixelFormat *PixFmt)
static bool HaveV4L2Codecs(bool Reinit=false)
static void GetDecoderList(QStringList &Decoders)
bool RetrieveFrame(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame) override
void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering) override
static const V4L2Profiles & GetStandardProfiles()
bool DecoderWillResetOnFlush() override
static bool GetBuffer(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame, int)
Retrieve a frame from CPU memory.
MythV4L2M2MContext(DecoderBase *Parent, MythCodecID CodecID)
int HwDecoderInit(AVCodecContext *Context) override
static uint GetNumPlanes(VideoFrameType Type)
Definition: mythframe.h:213
static int GetPitchForPlane(VideoFrameType Type, int Width, uint Plane)
Definition: mythframe.h:303
static int GetHeightForPlane(VideoFrameType Type, int Height, uint Plane)
Definition: mythframe.h:257
static void CopyPlane(uint8_t *To, int ToPitch, const uint8_t *From, int FromPitch, int PlaneWidth, int PlaneHeight)
Definition: mythframe.cpp:192
QString GetDriverName(void) const
Definition: v4l2util.cpp:685
int FD(void) const
Definition: v4l2util.h:28
QString GetDeviceName(void) const
Definition: v4l2util.cpp:680
uint32_t GetCapabilities(void) const
Definition: v4l2util.cpp:675
static bool ReinitBuffer(MythVideoFrame *Frame, VideoFrameType Type, MythCodecID CodecID, int Width, int Height)
unsigned int uint
Definition: compat.h:60
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
static const char * fourcc_str(int i)
Definition: fourcc.h:25
static bool codec_is_v4l2_dec(MythCodecID id)
Definition: mythcodecid.h:360
MythCodecID
Definition: mythcodecid.h:14
@ kCodec_MPEG1_V4L2
Definition: mythcodecid.h:216
@ kCodec_VP9_V4L2
Definition: mythcodecid.h:224
@ kCodec_MPEG1
Definition: mythcodecid.h:24
@ kCodec_H264_V4L2
Definition: mythcodecid.h:220
@ kCodec_HEVC_V4L2
Definition: mythcodecid.h:225
@ kCodec_MPEG1_V4L2_DEC
Definition: mythcodecid.h:232
@ kCodec_VP8_V4L2
Definition: mythcodecid.h:223
@ kCodec_MPEG2_V4L2
Definition: mythcodecid.h:217
static bool codec_is_v4l2(MythCodecID id)
Definition: mythcodecid.h:358
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
@ FMT_DRMPRIME
Definition: mythframe.h:63
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
#define V4L2_PIX_FMT_VP8_FRAME
#define V4L2_PIX_FMT_H264_SLICE
#define LOC
#define V4L2_PIX_FMT_HEVC_SLICE
#define V4L2_PIX_FMT_VP9_FRAME
#define V4L2_PIX_FMT_NV12_COL128
#define V4L2_PIX_FMT_MPEG2_SLICE
#define V4L2_PIX_FMT_NV12_10_COL128
#define V4L2_PIX_FMT_HEVC
#define V4L2_PIX_FMT_VP9
QList< MythCodecContext::CodecProfile > V4L2Profiles