MythTV  master
mythmmalcontext.cpp
Go to the documentation of this file.
1 // MythTV
3 #include "mythmmalcontext.h"
4 
5 // FFmpeg
6 extern "C" {
7 #include "libavutil/opt.h"
8 }
9 
10 #define LOC QString("MMAL: ")
11 
13  : MythCodecContext(Parent, Codec)
14 {
15 }
16 
18 {
19  if (m_interop)
21 }
22 
24 {
25  switch (Profile)
26  {
31  if (Width > 1920 || Height > 1088)
32  return false;
33  break;
34  default: break;
35  }
36  return true;
37 }
38 
40  AVCodec **Codec,
41  const QString &Decoder,
42  AVStream *Stream,
43  uint StreamType)
44 {
45  bool decodeonly = Decoder == "mmal-dec";
46  MythCodecID success = static_cast<MythCodecID>((decodeonly ? kCodec_MPEG1_MMAL_DEC : kCodec_MPEG1_MMAL) + (StreamType - 1));
47  MythCodecID failure = static_cast<MythCodecID>(kCodec_MPEG1 + (StreamType - 1));
48 
49  if (!Decoder.startsWith("mmal"))
50  return failure;
51 
52  // Only MPEG2, MPEG4, VC1 and H264 supported (and HEVC will never be supported)
54  switch ((*Codec)->id)
55  {
56  case AV_CODEC_ID_MPEG2VIDEO: mythprofile = MythCodecContext::MPEG2; break;
57  case AV_CODEC_ID_MPEG4: mythprofile = MythCodecContext::MPEG4; break;
58  case AV_CODEC_ID_VC1: mythprofile = MythCodecContext::VC1; break;
59  case AV_CODEC_ID_H264:
60  if ((*Context)->profile == FF_PROFILE_H264_HIGH_10 ||
61  (*Context)->profile == FF_PROFILE_H264_HIGH_10_INTRA)
62  {
63  return failure;
64  }
65  mythprofile = MythCodecContext::H264; break;
66  default: break;
67  }
68 
69  if (mythprofile == MythCodecContext::NoProfile)
70  return failure;
71 
72  // Check size
73  if (!MythMMALContext::CheckCodecSize((*Context)->width, (*Context)->height, mythprofile))
74  return failure;
75 
76  // check actual decoder support
77  const MMALProfiles& profiles = MythMMALContext::GetProfiles();
78  if (!profiles.contains(mythprofile))
79  return failure;
80 
81  if (!decodeonly)
82  {
83  // If called from outside of the main thread, we need a MythPlayer instance to
84  // process the callback interop check callback - which may fail otherwise
85  MythPlayer* player = nullptr;
86  if (!gCoreContext->IsUIThread())
87  {
88  AvFormatDecoder* decoder = reinterpret_cast<AvFormatDecoder*>((*Context)->opaque);
89  if (decoder)
90  player = decoder->GetPlayer();
91  }
92 
93  // direct rendering needs interop support
95  return failure;
96  }
97 
98  // look for a decoder
99  QString name = QString((*Codec)->name) + "_mmal";
100  if (name == "mpeg2video_mmal")
101  name = "mpeg2_mmal";
102  AVCodec *codec = avcodec_find_decoder_by_name(name.toLocal8Bit());
103  if (!codec)
104  {
105  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to find %1").arg(name));
106  return failure;
107  }
108 
109  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Found MMAL/FFmpeg decoder '%1'").arg(name));
110  *Codec = codec;
111  gCodecMap->freeCodecContext(Stream);
112  *Context = gCodecMap->getCodecContext(Stream, *Codec);
113  (*Context)->pix_fmt = decodeonly ? (*Context)->pix_fmt : AV_PIX_FMT_MMAL;
114  return success;
115 }
116 
117 void MythMMALContext::InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering)
118 {
120  {
121  DirectRendering = false;
122  return;
123  }
124  else if (codec_is_mmal(m_codecID))
125  {
126  Context->get_format = MythMMALContext::GetFormat;
127  DirectRendering = false;
128  return;
129  }
130 
131  MythCodecContext::InitVideoCodec(Context, SelectedStream, DirectRendering);
132 }
133 
135 {
137  return GetBuffer(Context, Frame, AvFrame, 0);
138  else if (codec_is_mmal(m_codecID))
139  return GetBuffer2(Context, Frame, AvFrame, 0);
140  return false;
141 }
142 
143 
145 {
146  if (!Context)
147  return -1;
148 
150  return 0;
151 
152  if (!codec_is_mmal(m_codecID) || Context->pix_fmt != AV_PIX_FMT_MMAL)
153  return -1;
154 
157  return m_interop ? 0 : -1;
158 }
159 
160 void MythMMALContext::SetDecoderOptions(AVCodecContext *Context, AVCodec *Codec)
161 {
162  if (!(codec_is_mmal(m_codecID)))
163  return;
164  if (!(Context && Codec))
165  return;
166  if (!(Codec->priv_class && Context->priv_data))
167  return;
168  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Setting number of extra buffers to 8");
169  av_opt_set(Context->priv_data, "extra_buffers", "8", 0);
170 }
171 
172 bool MythMMALContext::GetBuffer(AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame, int)
173 {
174  // Sanity checks
175  if (!Context || !AvFrame || !Frame)
176  return false;
177 
178  // Ensure we can render this format
179  AvFormatDecoder *decoder = static_cast<AvFormatDecoder*>(Context->opaque);
180  VideoFrameType type = PixelFormatToFrameType(static_cast<AVPixelFormat>(AvFrame->format));
181  VideoFrameType* supported = decoder->GetPlayer()->DirectRenderFormats();
182  bool found = false;
183  while (*supported != FMT_NONE)
184  {
185  if (*supported == type)
186  {
187  found = true;
188  break;
189  }
190  supported++;
191  }
192 
193  // No fallback currently (unlikely)
194  if (!found)
195  return false;
196 
197  // Re-allocate if necessary
198  if ((Frame->codec != type) || (Frame->width != AvFrame->width) || (Frame->height != AvFrame->height))
199  if (!VideoBuffers::ReinitBuffer(Frame, type, decoder->GetVideoCodecID(), AvFrame->width, AvFrame->height))
200  return false;
201 
202  // Copy data
203  uint count = planes(Frame->codec);
204  for (uint plane = 0; plane < count; ++plane)
205  copyplane(Frame->buf + Frame->offsets[plane], Frame->pitches[plane], AvFrame->data[plane], AvFrame->linesize[plane],
206  pitch_for_plane(Frame->codec, AvFrame->width, plane), height_for_plane(Frame->codec, AvFrame->height, plane));
207 
208  AvFrame->reordered_opaque = Context->reordered_opaque;
209  return true;
210 }
211 
212 bool MythMMALContext::GetBuffer2(AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame, int)
213 {
214  // Sanity checks
215  if (!Context || !AvFrame || !Frame || !m_interop)
216  {
217  LOG(VB_GENERAL, LOG_ERR, LOC + "Error");
218  return false;
219  }
220 
221  // MMAL?
222  if (Frame->codec != FMT_MMAL || static_cast<AVPixelFormat>(AvFrame->format) != AV_PIX_FMT_MMAL)
223  {
224  LOG(VB_GENERAL, LOG_ERR, LOC + "Not an MMAL frame");
225  return false;
226  }
227 
228  Frame->width = AvFrame->width;
229  Frame->height = AvFrame->height;
230  Frame->pix_fmt = Context->pix_fmt;
231  Frame->sw_pix_fmt = Context->sw_pix_fmt;
232  Frame->directrendering = 1;
233  AvFrame->opaque = Frame;
234  AvFrame->reordered_opaque = Context->reordered_opaque;
235 
236  // Frame->data[3] holds MMAL_BUFFER_HEADER_T
237  Frame->buf = AvFrame->data[3];
238  // Retain the buffer so it is not released before we display it
239  Frame->priv[0] = reinterpret_cast<unsigned char*>(av_buffer_ref(AvFrame->buf[0]));
240  // Add interop
241  Frame->priv[1] = reinterpret_cast<unsigned char*>(m_interop);
242  // Set the release method
243  AvFrame->buf[1] = av_buffer_create(reinterpret_cast<uint8_t*>(Frame), 0, MythCodecContext::ReleaseBuffer,
244  static_cast<AvFormatDecoder*>(Context->opaque), 0);
245  return true;
246 }
247 
248 AVPixelFormat MythMMALContext::GetFormat(AVCodecContext*, const AVPixelFormat *PixFmt)
249 {
250  while (*PixFmt != AV_PIX_FMT_NONE)
251  {
252  if (*PixFmt == AV_PIX_FMT_MMAL)
253  return AV_PIX_FMT_MMAL;
254  PixFmt++;
255  }
256  return AV_PIX_FMT_NONE;
257 }
258 
260 {
261  static QMutex lock(QMutex::Recursive);
262  QMutexLocker locker(&lock);
263  static bool s_checked = false;
264  static bool s_available = false;
265 
266  if (s_checked)
267  return s_available;
268  s_checked = true;
269 
270  const MMALProfiles& profiles = MythMMALContext::GetProfiles();
271  if (profiles.isEmpty())
272  return s_available;
273 
274  LOG(VB_GENERAL, LOG_INFO, LOC + "Supported/available MMAL decoders:");
275  s_available = true;
276  QSize size{0, 0};
277  foreach (auto profile, profiles)
278  LOG(VB_GENERAL, LOG_INFO, LOC + MythCodecContext::GetProfileDescription(profile, size));
279  return s_available;
280 }
281 
282 void MythMMALContext::GetDecoderList(QStringList &Decoders)
283 {
284  const MMALProfiles& profiles = MythMMALContext::GetProfiles();
285  if (profiles.isEmpty())
286  return;
287 
288  QSize size(0, 0);
289  Decoders.append("MMAL:");
290  for (MythCodecContext::CodecProfile profile : profiles)
291  Decoders.append(MythCodecContext::GetProfileDescription(profile, size));
292 }
293 
294 // Broadcom
295 extern "C" {
296 #include "interface/vmcs_host/vc_vchi_gencmd.h"
297 }
298 
300 {
301  static QMutex lock(QMutex::Recursive);
302  static bool s_initialised = false;
303  static MMALProfiles s_profiles;
304 
305  QMutexLocker locker(&lock);
306  if (s_initialised)
307  return s_profiles;
308  s_initialised = true;
309 
310  static const QPair<QString, MythCodecContext::CodecProfile> s_map[] =
311  {
312  { "MPG2", MythCodecContext::MPEG2 },
313  { "MPG4", MythCodecContext::MPEG4 },
314  { "WVC1", MythCodecContext::VC1 },
315  { "H264", MythCodecContext::H264 }
316  };
317 
318  vcos_init();
319  VCHI_INSTANCE_T vchi_instance;
320  if (vchi_initialise(&vchi_instance) != 0)
321  return s_profiles;
322  if (vchi_connect(nullptr, 0, vchi_instance) != 0)
323  return s_profiles;
324  VCHI_CONNECTION_T *vchi_connection = nullptr;
325  vc_vchi_gencmd_init(vchi_instance, &vchi_connection, 1 );
326 
327  for (auto profile : s_map)
328  {
329  char command[32];
330  char* response = nullptr;
331  int responsesize = 0;
332  QString msg = QString("codec_enabled %1").arg(profile.first);
333  if (!vc_gencmd(command, sizeof(command), msg.toLocal8Bit().constData()))
334  vc_gencmd_string_property(command, profile.first.toLocal8Bit().constData(), &response, &responsesize);
335 
336  if (response && responsesize && qstrcmp(response, "enabled") == 0)
337  s_profiles.append(profile.second);
338  }
339 
340  vc_gencmd_stop();
341  vchi_disconnect(vchi_instance);
342 
343  return s_profiles;
344 }
static bool CheckCodecSize(int Width, int Height, MythCodecContext::CodecProfile Profile)
MythPlayer * GetPlayer()
Definition: decoderbase.h:154
static const MMALProfiles & GetProfiles(void)
static void ReleaseBuffer(void *Opaque, uint8_t *Data)
#define codec_is_mmal(id)
Definition: mythcodecid.h:341
QHash< QString, Action * > Context
Definition: action.h:77
void SetDecoderOptions(AVCodecContext *Context, AVCodec *Codec) override
static Type GetInteropType(VideoFrameType Format, MythPlayer *Player)
Check whether we support direct rendering for the given VideoFrameType.
MythCodecID
Definition: mythcodecid.h:10
static bool HaveMMAL(void)
#define codec_is_mmal_dec(id)
Definition: mythcodecid.h:342
static MythCodecID GetSupportedCodec(AVCodecContext **Context, AVCodec **Codec, const QString &Decoder, AVStream *Stream, uint StreamType)
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.
static int pitch_for_plane(VideoFrameType Type, int Width, uint Plane)
Definition: mythframe.h:342
static uint planes(VideoFrameType Type)
Definition: mythframe.h:567
void freeCodecContext(const AVStream *stream)
Definition: mythavutil.cpp:557
~MythMMALContext() override
MythCodecID m_codecID
static bool ReinitBuffer(VideoFrame *Frame, VideoFrameType Type, MythCodecID CodecID, int Width, int Height)
bool GetBuffer2(AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame, int)
static int height_for_plane(VideoFrameType Type, int Height, uint Plane)
Definition: mythframe.h:437
MythMMALInterop * m_interop
VideoFrameType * DirectRenderFormats(void)
Return a list of frame types that can be rendered directly.
Definition: mythplayer.cpp:878
MythCodecMap * gCodecMap
This global variable contains the MythCodecMap instance for the app.
Definition: mythavutil.cpp:508
AVCodecContext * getCodecContext(const AVStream *stream, const AVCodec *pCodec=nullptr, bool nullCodec=false)
Definition: mythavutil.cpp:515
virtual void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering)
A decoder for video files.
static enum AVPixelFormat GetFormat(AVCodecContext *, const AVPixelFormat *PixFmt)
QList< MythCodecContext::CodecProfile > MMALProfiles
void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering) override
unsigned int uint
Definition: compat.h:140
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
static void DestroyInterop(MythOpenGLInterop *Interop)
static MythRenderOpenGL * GetOpenGLRender(void)
static void GetDecoderList(QStringList &Decoders)
int HwDecoderInit(AVCodecContext *Context) override
MythCodecID GetVideoCodecID(void) const override
static MythMMALInterop * Create(MythRenderOpenGL *Context, Type InteropType)
#define LOC
bool RetrieveFrame(AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame) override
MythMMALContext(DecoderBase *Parent, MythCodecID Codec)
static bool GetBuffer(AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame, int)