MythTV  master
mythv4l2m2mcontext.cpp
Go to the documentation of this file.
1 // Qt
2 #include <QDir>
3 
4 // MythTV
5 #include "mythlogging.h"
6 #include "v4l2util.h"
7 #include "fourcc.h"
8 #include "avformatdecoder.h"
10 #ifdef USING_EGL
12 #endif
13 #include "mythv4l2m2mcontext.h"
14 
15 #ifdef USING_MMAL
16 #include "mythmmalcontext.h"
17 #endif
18 
19 // Sys
20 #include <sys/ioctl.h>
21 
22 // FFmpeg
23 extern "C" {
24 #include "libavutil/opt.h"
25 }
26 
27 #define LOC QString("V4L2_M2M: ")
28 
29 static bool s_useV4L2Request = !qgetenv("MYTHTV_V4L2_REQUEST").isEmpty();
30 
39  : MythDRMPRIMEContext(Parent, CodecID)
40 {
41 }
42 
44 {
45  return codec_is_v4l2(m_codecID);
46 }
47 
49  AVCodec **Codec,
50  const QString &Decoder,
51  AVStream *Stream,
52  uint StreamType)
53 {
54  bool decodeonly = Decoder == "v4l2-dec";
55  auto success = static_cast<MythCodecID>((decodeonly ? kCodec_MPEG1_V4L2_DEC : kCodec_MPEG1_V4L2) + (StreamType - 1));
56  auto failure = static_cast<MythCodecID>(kCodec_MPEG1 + (StreamType - 1));
57 
58  // not us
59  if (!Decoder.startsWith("v4l2"))
60  return failure;
61 
62  // supported by device driver?
64  switch ((*Codec)->id)
65  {
66  case AV_CODEC_ID_MPEG1VIDEO: mythprofile = MythCodecContext::MPEG1; break;
67  case AV_CODEC_ID_MPEG2VIDEO: mythprofile = MythCodecContext::MPEG2; break;
68  case AV_CODEC_ID_MPEG4: mythprofile = MythCodecContext::MPEG4; break;
69  case AV_CODEC_ID_H263: mythprofile = MythCodecContext::H263; break;
70  case AV_CODEC_ID_H264: mythprofile = MythCodecContext::H264; break;
71  case AV_CODEC_ID_VC1: mythprofile = MythCodecContext::VC1; break;
72  case AV_CODEC_ID_VP8: mythprofile = MythCodecContext::VP8; break;
73  case AV_CODEC_ID_VP9: mythprofile = MythCodecContext::VP9; break;
74  case AV_CODEC_ID_HEVC: mythprofile = MythCodecContext::HEVC; break;
75  default: break;
76  }
77 
78  if (mythprofile == MythCodecContext::NoProfile)
79  return failure;
80 
82  if (!profiles.contains(mythprofile))
83  return failure;
84 
85 #ifdef USING_MMAL
86  // If MMAL is available, assume this is a Raspberry Pi and check the supported
87  // video sizes
88  if (!MythMMALContext::CheckCodecSize((*Context)->width, (*Context)->height, mythprofile))
89  return failure;
90  // As for MMAL, don't try and decode 10bit H264
91  if ((*Codec)->id == AV_CODEC_ID_H264)
92  {
93  if ((*Context)->profile == FF_PROFILE_H264_HIGH_10 ||
94  (*Context)->profile == FF_PROFILE_H264_HIGH_10_INTRA)
95  {
96  return failure;
97  }
98  }
99 #endif
100 
101  if (s_useV4L2Request && !decodeonly)
102  {
103  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Forcing support for %1 v42l_request")
104  .arg(ff_codec_id_string((*Context)->codec_id)));
105  (*Context)->pix_fmt = AV_PIX_FMT_DRM_PRIME;
106  return success;
107  }
108 
109  return MythDRMPRIMEContext::GetPrimeCodec(Context, Codec, Stream,
110  success, failure, "v4l2m2m",
111  decodeonly ? (*Context)->pix_fmt : AV_PIX_FMT_DRM_PRIME);
112 }
113 
115 {
116  if (!Context)
117  return -1;
119  return 0;
121  return 0;
123 }
124 
125 void MythV4L2M2MContext::InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering)
126 {
128  {
130  return;
131  }
132 
134  {
135  DirectRendering = false;
136  return;
137  }
138  return MythDRMPRIMEContext::InitVideoCodec(Context, SelectedStream, DirectRendering);
139 }
140 
142 {
144  return MythCodecContext::GetBuffer2(Context, Frame, AvFrame, 0);
145 
147  return GetBuffer(Context, Frame, AvFrame, 0);
149 }
150 
156 void MythV4L2M2MContext::SetDecoderOptions(AVCodecContext* Context, AVCodec* Codec)
157 {
159  return;
160 
161  if (!(Context && Codec))
162  return;
163  if (!(Codec->priv_class && Context->priv_data))
164  return;
165 
166  // best guess currently - this matches the number of capture buffers to the
167  // number of output buffers - and hence to the number of video buffers for
168  // direct rendering
169  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Setting number of capture buffers to 6");
170  av_opt_set_int(Context->priv_data, "num_capture_buffers", 6, 0);
171 }
172 
179 bool MythV4L2M2MContext::GetBuffer(AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame, int /*Flags*/)
180 {
181  // Sanity checks
182  if (!Context || !AvFrame || !Frame)
183  return false;
184 
185  // Ensure we can render this format
186  auto *decoder = static_cast<AvFormatDecoder*>(Context->opaque);
187  VideoFrameType type = PixelFormatToFrameType(static_cast<AVPixelFormat>(AvFrame->format));
188  VideoFrameType* supported = decoder->GetPlayer()->DirectRenderFormats();
189  bool found = false;
190  while (*supported != FMT_NONE)
191  {
192  if (*supported == type)
193  {
194  found = true;
195  break;
196  }
197  supported++;
198  }
199 
200  // No fallback currently (unlikely)
201  if (!found)
202  return false;
203 
204  // Re-allocate if necessary
205  if ((Frame->codec != type) || (Frame->width != AvFrame->width) || (Frame->height != AvFrame->height))
206  if (!VideoBuffers::ReinitBuffer(Frame, type, decoder->GetVideoCodecID(), AvFrame->width, AvFrame->height))
207  return false;
208 
209  // Copy data
210  uint count = planes(Frame->codec);
211  for (uint plane = 0; plane < count; ++plane)
212  copyplane(Frame->buf + Frame->offsets[plane], Frame->pitches[plane], AvFrame->data[plane], AvFrame->linesize[plane],
213  pitch_for_plane(Frame->codec, AvFrame->width, plane), height_for_plane(Frame->codec, AvFrame->height, plane));
214 
215  return true;
216 }
217 
219 {
220  static const QPair<uint32_t, MythCodecContext::CodecProfile> s_map[] =
221  {
222  { V4L2_PIX_FMT_MPEG1, MythCodecContext::MPEG1 },
223  { V4L2_PIX_FMT_MPEG2, MythCodecContext::MPEG2 },
224  { V4L2_PIX_FMT_MPEG4, MythCodecContext::MPEG4 },
225  { V4L2_PIX_FMT_H263, MythCodecContext::H263 },
226  { V4L2_PIX_FMT_H264, MythCodecContext::H264 },
227  { V4L2_PIX_FMT_VC1_ANNEX_G, MythCodecContext::VC1 },
228  { V4L2_PIX_FMT_VP8, MythCodecContext::VP8 },
229  { V4L2_PIX_FMT_VP9, MythCodecContext::VP9 },
230  { V4L2_PIX_FMT_HEVC, MythCodecContext::HEVC }
231  };
232 
233  static QMutex lock(QMutex::Recursive);
234  static bool s_initialised = false;
235  static V4L2Profiles s_profiles;
236 
237  QMutexLocker locker(&lock);
238  if (s_initialised)
239  return s_profiles;
240  s_initialised = true;
241 
242  if (s_useV4L2Request)
243  {
244  LOG(VB_GENERAL, LOG_INFO, LOC + "V4L2Request support endabled - assuming all available");
245  for (auto profile : s_map)
246  s_profiles.append(profile.second);
247  return s_profiles;
248  }
249 
250  const QString root("/dev/");
251  QDir dir(root);
252  QStringList namefilters;
253  namefilters.append("video*");
254  QStringList devices = dir.entryList(namefilters, QDir::Files |QDir::System);
255  foreach (QString device, devices)
256  {
257  V4L2util v4l2dev(root + device);
258  uint32_t caps = v4l2dev.GetCapabilities();
259  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Device: %1 Driver: '%2' Capabilities: 0x%3")
260  .arg(v4l2dev.GetDeviceName()).arg(v4l2dev.GetDriverName()).arg(caps, 0, 16));
261 
262  // check capture and output support
263  // these mimic the device checks in v4l2_m2m.c
264  bool mplanar = (caps & (V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE) &&
265  caps & V4L2_CAP_STREAMING);
266  bool mplanarm2m = caps & V4L2_CAP_VIDEO_M2M_MPLANE;
267  bool splanar = (caps & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT) &&
268  caps & V4L2_CAP_STREAMING);
269  bool splanarm2m = caps & V4L2_CAP_VIDEO_M2M;
270 
271  if (!(mplanar || mplanarm2m || splanar || splanarm2m))
272  continue;
273 
274  v4l2_buf_type capturetype = V4L2_BUF_TYPE_VIDEO_CAPTURE;
275  v4l2_buf_type outputtype = V4L2_BUF_TYPE_VIDEO_OUTPUT;
276 
277  if (mplanar || mplanarm2m)
278  {
279  capturetype = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
280  outputtype = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
281  }
282 
283  // check codec support
284  QStringList debug;
285  QSize dummy{0, 0};
286  for (auto profile : s_map)
287  {
288  bool found = false;
289  uint32_t v4l2pixfmt = profile.first;
290  MythCodecContext::CodecProfile mythprofile = profile.second;
291  struct v4l2_fmtdesc fdesc {};
292  memset(&fdesc, 0, sizeof(fdesc));
293 
294  // check output first
295  fdesc.type = outputtype;
296  while (!found)
297  {
298  int res = ioctl(v4l2dev.FD(), VIDIOC_ENUM_FMT, &fdesc);
299  if (res)
300  break;
301  if (fdesc.pixelformat == v4l2pixfmt)
302  found = true;
303  fdesc.index++;
304  }
305 
306  if (found)
307  {
308  QStringList pixformats;
309  bool foundfmt = false;
310  // check capture
311  memset(&fdesc, 0, sizeof(fdesc));
312  fdesc.type = capturetype;
313  while (true)
314  {
315  int res = ioctl(v4l2dev.FD(), VIDIOC_ENUM_FMT, &fdesc);
316  if (res)
317  break;
318  pixformats.append(fourcc_str(static_cast<int>(fdesc.pixelformat)));
319 
320  // this is a bit of a shortcut
321  if (fdesc.pixelformat == V4L2_PIX_FMT_YUV420 ||
322  fdesc.pixelformat == V4L2_PIX_FMT_YVU420 ||
323  fdesc.pixelformat == V4L2_PIX_FMT_YUV420M ||
324  fdesc.pixelformat == V4L2_PIX_FMT_YVU420M ||
325  fdesc.pixelformat == V4L2_PIX_FMT_NV12 ||
326  fdesc.pixelformat == V4L2_PIX_FMT_NV12M ||
327  fdesc.pixelformat == V4L2_PIX_FMT_NV21 ||
328  fdesc.pixelformat == V4L2_PIX_FMT_NV21M)
329  {
330  if (!s_profiles.contains(mythprofile))
331  s_profiles.append(mythprofile);
332  foundfmt = true;
333  break;
334  }
335  fdesc.index++;
336  }
337 
338  if (!foundfmt)
339  {
340  if (pixformats.isEmpty())
341  pixformats.append("None");
342  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Codec '%1' has no supported formats (Supported: %2)")
343  .arg(MythCodecContext::GetProfileDescription(mythprofile, dummy)).arg(pixformats.join((","))));
344  }
345  }
346  }
347  }
348 
349  return s_profiles;
350 }
351 
352 void MythV4L2M2MContext::GetDecoderList(QStringList &Decoders)
353 {
354  const V4L2Profiles& profiles = MythV4L2M2MContext::GetProfiles();
355  if (profiles.isEmpty())
356  return;
357 
358  QSize size(0, 0);
359  Decoders.append("V4L2:");
360  for (MythCodecContext::CodecProfile profile : profiles)
361  Decoders.append(MythCodecContext::GetProfileDescription(profile, size));
362 }
363 
365 {
366  static QMutex lock(QMutex::Recursive);
367  QMutexLocker locker(&lock);
368  static bool s_checked = false;
369  static bool s_available = false;
370 
371  if (s_checked)
372  return s_available;
373  s_checked = true;
374 
375  const V4L2Profiles& profiles = MythV4L2M2MContext::GetProfiles();
376  if (profiles.isEmpty())
377  {
378  LOG(VB_GENERAL, LOG_INFO, LOC + "No V4L2 decoders found");
379  return s_available;
380  }
381 
382  LOG(VB_GENERAL, LOG_INFO, LOC + "Supported/available V4L2 decoders:");
383  s_available = true;
384  QSize size{0, 0};
385  foreach (auto profile, profiles)
386  LOG(VB_GENERAL, LOG_INFO, LOC + MythCodecContext::GetProfileDescription(profile, size));
387  return s_available;
388 }
389 
390 AVPixelFormat MythV4L2M2MContext::GetV4L2RequestFormat(AVCodecContext *Context, const AVPixelFormat *PixFmt)
391 {
392  while (*PixFmt != AV_PIX_FMT_NONE)
393  {
394  if (*PixFmt == AV_PIX_FMT_DRM_PRIME)
395  {
397  "V4L2 request context creation") >= 0)
398  return AV_PIX_FMT_DRM_PRIME;
399  }
400  PixFmt++;
401  }
402  return AV_PIX_FMT_NONE;
403 }
404 
406 {
407  if (!Context || !gCoreContext->IsUIThread())
408  return -1;
409 
410  // We need a render device
412  if (!render)
413  return -1;
414 
415  // The interop must have a reference to the player so it can be deleted
416  // from the main thread.
417  MythPlayer *player = nullptr;
418  auto *decoder = reinterpret_cast<AvFormatDecoder*>(Context->opaque);
419  if (decoder)
420  player = decoder->GetPlayer();
421  if (!player)
422  return -1;
423 
424  // Check interop support
427  return -1;
428 
429  // Create interop
430  MythOpenGLInterop *interop = nullptr;
431 #ifdef USING_EGL
432  interop = MythDRMPRIMEInterop::Create(render, type);
433 #endif
434  if (!interop)
435  return -1;
436 
437  // Set the player required to process interop release
438  interop->SetPlayer(player);
439 
440  // Allocate the device context
441  AVBufferRef* hwdeviceref = MythCodecContext::CreateDevice(AV_HWDEVICE_TYPE_DRM, interop);
442  if (!hwdeviceref)
443  {
444  interop->DecrRef();
445  return -1;
446  }
447 
448  auto* hwdevicecontext = reinterpret_cast<AVHWDeviceContext*>(hwdeviceref->data);
449  if (!hwdevicecontext || (hwdevicecontext && !hwdevicecontext->hwctx))
450  {
451  interop->DecrRef();
452  return -1;
453  }
454 
455  // Initialise device context
456  if (av_hwdevice_ctx_init(hwdeviceref) < 0)
457  {
458  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialise device context");
459  av_buffer_unref(&hwdeviceref);
460  interop->DecrRef();
461  return -1;
462  }
463 
464  Context->hw_device_ctx = hwdeviceref;
465  return 0;
466 }
static bool CheckCodecSize(int Width, int Height, MythCodecContext::CodecProfile Profile)
void SetPlayer(MythPlayer *Player)
QHash< QString, Action * > Context
Definition: action.h:77
static Type GetInteropType(VideoFrameType Format, MythPlayer *Player)
Check whether we support direct rendering for the given VideoFrameType.
MythCodecID
Definition: mythcodecid.h:10
static int InitialiseV4L2RequestContext(AVCodecContext *Context)
QList< MythCodecContext::CodecProfile > V4L2Profiles
static MythCodecID GetSupportedCodec(AVCodecContext **Context, AVCodec **Codec, const QString &Decoder, AVStream *Stream, uint StreamType)
static int InitialiseDecoder(AVCodecContext *Context, CreateHWDecoder Callback, const QString &Debug)
Initialise a hardware decoder that is expected to use AVHWFramesContext.
bool DecoderWillResetOnFlush(void) override
static QString GetProfileDescription(CodecProfile Profile, QSize Size, VideoFrameType Format=FMT_NONE, uint ColorDepth=0)
struct AVFrame AVFrame
VideoFrameType PixelFormatToFrameType(AVPixelFormat fmt)
Definition: mythavutil.cpp:68
VideoFrameType
Definition: mythframe.h:23
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define codec_is_v4l2_dec(id)
Definition: mythcodecid.h:339
static char * fourcc_str(int i)
Definition: fourcc.h:25
static int pitch_for_plane(VideoFrameType Type, int Width, uint Plane)
Definition: mythframe.h:342
bool RetrieveFrame(AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame) override
static uint planes(VideoFrameType Type)
Definition: mythframe.h:567
uint32_t GetCapabilities(void) const
MythCodecID m_codecID
static bool ReinitBuffer(VideoFrame *Frame, VideoFrameType Type, MythCodecID CodecID, int Width, int Height)
A generic context handler for codecs that return AV_PIX_FMT_DRM_PRIME frames.
static int height_for_plane(VideoFrameType Type, int Height, uint Plane)
Definition: mythframe.h:437
static MythDRMPRIMEInterop * Create(MythRenderOpenGL *Context, Type InteropType)
int HwDecoderInit(AVCodecContext *Context) override
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
static bool s_useV4L2Request
unsigned int uint
Definition: compat.h:140
QString GetDriverName(void) const
#define LOC
static void GetDecoderList(QStringList &Decoders)
static bool HaveV4L2Codecs(void)
static const V4L2Profiles & GetProfiles(void)
static void copyplane(uint8_t *dst, int dst_pitch, const uint8_t *src, int src_pitch, int width, int height)
Definition: mythframe.h:537
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QString GetDeviceName(void) const
static enum AVPixelFormat GetV4L2RequestFormat(AVCodecContext *Context, const AVPixelFormat *PixFmt)
VERBOSE_PREAMBLE Most debug(nodatabase, notimestamp, noextra)") VERBOSE_MAP(VB_GENERAL
void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering) override
MythV4L2M2MContext(DecoderBase *Parent, MythCodecID CodecID)
static bool GetBuffer2(struct AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame, int Flags)
A generic hardware buffer initialisation method when AVHWFramesContext is NOT used.
static bool GetBuffer(AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame, int)
Retrieve a frame from CPU memory.
static MythRenderOpenGL * GetOpenGLRender(void)
static MythCodecID GetPrimeCodec(AVCodecContext **Context, AVCodec **Codec, AVStream *Stream, MythCodecID Successs, MythCodecID Failure, const QString &CodecName, AVPixelFormat Format)
int FD(void) const
Definition: v4l2util.h:26
bool RetrieveFrame(AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame) override
static AVBufferRef * CreateDevice(AVHWDeviceType Type, MythOpenGLInterop *Interop, const QString &Device=QString())
#define codec_is_v4l2(id)
Definition: mythcodecid.h:338
int HwDecoderInit(AVCodecContext *Context) override
void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering) override
void SetDecoderOptions(AVCodecContext *Context, AVCodec *Codec) override
Reduce the number of capture buffers.