MythTV  master
mythcodeccontext.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017 MythTV Developers <mythtv-dev@mythtv.org>
3 //
4 // This is part of MythTV (https://www.mythtv.org)
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 //
24 
25 #include "mythcorecontext.h"
26 #include "mythlogging.h"
27 #include "mythcodeccontext.h"
28 #include "videooutbase.h"
29 #include "mythplayer.h"
30 #ifdef USING_VAAPI2
31 #include "vaapi2context.h"
32 #endif
33 
34 extern "C" {
35  #include "libavutil/pixfmt.h"
36  #include "libavutil/hwcontext.h"
37  #include "libavcodec/avcodec.h"
38  #include "libavfilter/avfilter.h"
39  #include "libavfilter/buffersink.h"
40  #include "libavfilter/buffersrc.h"
41  #include "libavformat/avformat.h"
42  #include "libavutil/opt.h"
43  #include "libavutil/buffer.h"
44 }
45 
46 #define LOC QString("MythCodecContext: ")
47 
49  stream(nullptr),
50  buffersink_ctx(nullptr),
51  buffersrc_ctx(nullptr),
52  filter_graph(nullptr),
53  filtersInitialized(false),
54  hw_frames_ctx(nullptr),
55  player(nullptr),
56  ptsUsed(0),
57  doublerate(false)
58 {
59  priorPts[0] = 0;
60  priorPts[1] = 0;
61 }
62 
64 {
65  CloseFilters();
66 }
67 
68 // static
70 {
71  MythCodecContext *mctx = nullptr;
72 #ifdef USING_VAAPI2
73  if (codec_is_vaapi2(codec))
74  mctx = new Vaapi2Context();
75 #else
76  Q_UNUSED(codec);
77 #endif
78  if (!mctx)
79  mctx = new MythCodecContext();
80  return mctx;
81 }
82 
83 // static
84 QStringList MythCodecContext::GetDeinterlacers(QString decodername)
85 {
86  QStringList ret;
87 #ifdef USING_VAAPI2
88  if (decodername == "vaapi2")
89  {
90  ret.append("vaapi2default");
91  ret.append("vaapi2bob");
92  ret.append("vaapi2weave");
93  ret.append("vaapi2motion_adaptive");
94  ret.append("vaapi2motion_compensated");
95  ret.append("vaapi2doubleratedefault");
96  ret.append("vaapi2doubleratebob");
97  ret.append("vaapi2doublerateweave");
98  ret.append("vaapi2doubleratemotion_adaptive");
99  ret.append("vaapi2doubleratemotion_compensated");
100 
101 /*
102  Explanation of vaapi2 deinterlacing modes.
103  "mode", "Deinterlacing mode",
104  "default", "Use the highest-numbered (and therefore possibly most advanced) deinterlacing algorithm",
105  "bob", "Use the bob deinterlacing algorithm",
106  "weave", "Use the weave deinterlacing algorithm",
107  "motion_adaptive", "Use the motion adaptive deinterlacing algorithm",
108  "motion_compensated", "Use the motion compensated deinterlacing algorithm",
109 
110  "rate", "Generate output at frame rate or field rate",
111  "frame", "Output at frame rate (one frame of output for each field-pair)",
112  "field", "Output at field rate (one frame of output for each field)",
113 
114  "auto", "Only deinterlace fields, passing frames through unchanged",
115  1 = enabled
116  0 = disabled
117 */
118 
119  }
120 #else
121  Q_UNUSED(decodername);
122 #endif
123  return ret;
124 }
125 // static - Find if a deinterlacer is codec-provided
126 bool MythCodecContext::isCodecDeinterlacer(QString decodername)
127 {
128  return (decodername.startsWith("vaapi2"));
129 }
130 
131 
132 // Currently this will only set up the filter after an interlaced frame.
133 // If we need other filters apart from deinterlace filters we will
134 // need to make a change here.
135 
136 int MythCodecContext::FilteredReceiveFrame(AVCodecContext *ctx, AVFrame *frame)
137 {
138  int ret = 0;
139 
140  while (1)
141  {
142  if (filter_graph)
143  {
144  ret = av_buffersink_get_frame(buffersink_ctx, frame);
145  if (ret >= 0)
146  {
147  if (priorPts[0] && ptsUsed == priorPts[1])
148  {
149  frame->pts = priorPts[1] + (priorPts[1] - priorPts[0])/2;
150  frame->scte_cc_len = 0;
151  frame->atsc_cc_len = 0;
152  av_frame_remove_side_data(frame, AV_FRAME_DATA_A53_CC);
153  }
154  else
155  {
156  frame->pts = priorPts[1];
157  ptsUsed = priorPts[1];
158  }
159  }
160  if (ret != AVERROR(EAGAIN))
161  break;
162  }
163 
164  // EAGAIN or no filter graph
165  ret = avcodec_receive_frame(ctx, frame);
166  if (ret < 0)
167  break;
168  priorPts[0]=priorPts[1];
169  priorPts[1]=frame->pts;
170  if (frame->interlaced_frame || filter_graph)
171  {
172  if (!filtersInitialized
173  || width != frame->width
174  || height != frame->height)
175  {
176  // bypass any frame of unknown format
177  if (frame->format < 0)
178  break;
179  ret = InitDeinterlaceFilter(ctx, frame);
180  if (ret < 0)
181  {
182  LOG(VB_GENERAL, LOG_ERR, LOC + "InitDeinterlaceFilter failed - continue without filters");
183  break;
184  }
185  }
186  if (filter_graph)
187  {
188  ret = av_buffersrc_add_frame(buffersrc_ctx, frame);
189  if (ret < 0)
190  break;
191  }
192  else
193  break;
194  }
195  else
196  break;
197  }
198 
199  return ret;
200 }
201 
202 // Setup or change deinterlacer.
203 // Same usage as VideoOutBase::SetupDeinterlace
204 // enable - true to enable, false to disable
205 // name - empty to use video profile deinterlacers, otherwise
206 // use the supplied name.
207 // return true if the deinterlacer was found as a hardware deinterlacer.
208 // return false if the deinterlacer is nnt a hardware deinterlacer,
209 // and a videououtput deinterlacer should be tried instead.
210 
211 bool MythCodecContext::setDeinterlacer(bool enable, QString name)
212 {
213  QMutexLocker lock(&contextLock);
214  // Code to disable interlace
215  if (!enable)
216  {
217  if (deinterlacername.isEmpty())
218  return true;
219  else
220  {
221  deinterlacername.clear();
222  doublerate = false;
223  filtersInitialized = false;
224  return true;
225  }
226  }
227 
228  // Code to enable or change interlace
229  if (name.isEmpty())
230  {
231  if (deinterlacername.isEmpty())
232  {
233  VideoOutput *vo = nullptr;
234  VideoDisplayProfile *vdisp_profile = nullptr;
235  if (player)
236  vo = player->GetVideoOutput();
237  if (vo)
238  vdisp_profile = vo->GetProfile();
239  if (vdisp_profile)
240  name = vdisp_profile->GetFilteredDeint(QString());
241  }
242  else
244  }
245  bool ret = true;
247  name.clear();
248 
249  if (name.isEmpty())
250  ret = false;
251 
252  if (deinterlacername == name)
253  return ret;
254 
256  doublerate = deinterlacername.contains("doublerate");
257  filtersInitialized = false;
258  return ret;
259 }
260 
262 {
263  deinterlacername.clear();
264  doublerate = false;
265  return setDeinterlacer(true);
266 }
267 
269 {
270  return setDeinterlacer(true,GetFallbackDeint());
271 }
272 
274 {
275 
276  VideoOutput *vo = nullptr;
277  VideoDisplayProfile *vdisp_profile = nullptr;
278  if (player)
279  vo = player->GetVideoOutput();
280  if (vo)
281  vdisp_profile = vo->GetProfile();
282  if (vdisp_profile)
283  return vdisp_profile->GetFallbackDeinterlacer();
284  return QString();
285 }
286 
287 int MythCodecContext::InitDeinterlaceFilter(AVCodecContext *ctx, AVFrame *frame)
288 {
289  QMutexLocker lock(&contextLock);
290  char args[512];
291  int ret = 0;
292  CloseFilters();
293  width = frame->width;
294  height = frame->height;
295  filtersInitialized = true;
296  if (!player || !stream)
297  {
298  LOG(VB_GENERAL, LOG_ERR, LOC + "Player or stream is not set up in MythCodecContext");
299  return -1;
300  }
302  {
303  QString request = deinterlacername;
305  LOG(VB_PLAYBACK, LOG_INFO, LOC
306  + QString("Deinterlacer %1 requires double rate, switching to %2 instead.")
307  .arg(request).arg(deinterlacername));
309  deinterlacername.clear();
310  doublerate = deinterlacername.contains("doublerate");
311 
312  // if the fallback is a non-vaapi - deinterlace will be turned off
313  // and the videoout methods can take over.
314  }
315  QString filters;
317  filters = GetDeinterlaceFilter();
318 
319  if (filters.isEmpty())
320  {
321  LOG(VB_GENERAL, LOG_INFO, LOC +
322  "Disabled hardware decoder based deinterlacer.");
323  return ret;
324  }
325  const AVFilter *buffersrc = avfilter_get_by_name("buffer");
326  const AVFilter *buffersink = avfilter_get_by_name("buffersink");
327  AVFilterInOut *outputs = avfilter_inout_alloc();
328  AVFilterInOut *inputs = avfilter_inout_alloc();
329  AVRational time_base = stream->time_base;
330  AVBufferSrcParameters* params = nullptr;
331 
332  filter_graph = avfilter_graph_alloc();
333  if (!outputs || !inputs || !filter_graph)
334  {
335  ret = AVERROR(ENOMEM);
336  goto end;
337  }
338 
339  /* buffer video source: the decoded frames from the decoder will be inserted here. */
340  snprintf(args, sizeof(args),
341  "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
342  frame->width, frame->height, frame->format, // ctx->pix_fmt,
343  time_base.num, time_base.den,
344  ctx->sample_aspect_ratio.num, ctx->sample_aspect_ratio.den);
345 
346  // isInterlaced = frame->interlaced_frame;
347 
348  ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
349  args, nullptr, filter_graph);
350  if (ret < 0)
351  {
352  LOG(VB_GENERAL, LOG_ERR, LOC + "avfilter_graph_create_filter failed for buffer source");
353  goto end;
354  }
355 
356  params = av_buffersrc_parameters_alloc();
357  if (hw_frames_ctx)
358  av_buffer_unref(&hw_frames_ctx);
359  hw_frames_ctx = av_buffer_ref(frame->hw_frames_ctx);
360  params->hw_frames_ctx = hw_frames_ctx;
361 
362  ret = av_buffersrc_parameters_set(buffersrc_ctx, params);
363 
364  if (ret < 0)
365  {
366  LOG(VB_GENERAL, LOG_ERR, LOC + "av_buffersrc_parameters_set failed");
367  goto end;
368  }
369 
370  av_freep(&params);
371 
372  /* buffer video sink: to terminate the filter chain. */
373  ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
374  nullptr, nullptr, filter_graph);
375  if (ret < 0)
376  {
377  LOG(VB_GENERAL, LOG_ERR, LOC + "avfilter_graph_create_filter failed for buffer sink");
378  goto end;
379  }
380 
381  /*
382  * Set the endpoints for the filter graph. The filter_graph will
383  * be linked to the graph described by filters_descr.
384  */
385 
386  /*
387  * The buffer source output must be connected to the input pad of
388  * the first filter described by filters_descr; since the first
389  * filter input label is not specified, it is set to "in" by
390  * default.
391  */
392  outputs->name = av_strdup("in");
393  outputs->filter_ctx = buffersrc_ctx;
394  outputs->pad_idx = 0;
395  outputs->next = nullptr;
396 
397  /*
398  * The buffer sink input must be connected to the output pad of
399  * the last filter described by filters_descr; since the last
400  * filter output label is not specified, it is set to "out" by
401  * default.
402  */
403  inputs->name = av_strdup("out");
404  inputs->filter_ctx = buffersink_ctx;
405  inputs->pad_idx = 0;
406  inputs->next = nullptr;
407 
408  if ((ret = avfilter_graph_parse_ptr(filter_graph, filters.toLocal8Bit(),
409  &inputs, &outputs,nullptr)) < 0)
410  {
411  LOG(VB_GENERAL, LOG_ERR, LOC
412  + QString("avfilter_graph_parse_ptr failed for %1").arg(filters));
413  goto end;
414  }
415 
416  if ((ret = avfilter_graph_config(filter_graph, nullptr)) < 0)
417  {
418  LOG(VB_GENERAL, LOG_ERR, LOC
419  + QString("avfilter_graph_config failed"));
420  goto end;
421  }
422 
423  LOG(VB_GENERAL, LOG_INFO, LOC +
424  QString("Enabled hardware decoder based deinterlace filter '%1': <%2>.")
425  .arg(deinterlacername).arg(filters));
426 end:
427  if (ret < 0)
428  {
429  avfilter_graph_free(&filter_graph);
430  filter_graph = nullptr;
431  doublerate = false;
432  }
433  avfilter_inout_free(&inputs);
434  avfilter_inout_free(&outputs);
435 
436  return ret;
437 }
438 
440 {
441  avfilter_graph_free(&filter_graph);
442  filter_graph = nullptr;
443  buffersink_ctx = nullptr;
444  buffersrc_ctx = nullptr;
445  filtersInitialized = false;
446  ptsUsed = 0;
447  priorPts[0] = 0;
448  priorPts[1] = 0;
449  // isInterlaced = 0;
450  width = 0;
451  height = 0;
452 
453  if (hw_frames_ctx)
454  av_buffer_unref(&hw_frames_ctx);
455 }
bool setDeinterlacer(bool enable, QString name=QString())
VideoOutput * GetVideoOutput(void)
Definition: mythplayer.h:244
static bool isCodecDeinterlacer(QString decodername)
AVFilterContext * buffersink_ctx
MythCodecID
Definition: mythcodecid.h:10
struct AVFrame AVFrame
QString GetFilteredDeint(const QString &override)
QString GetFallbackDeinterlacer(void) const
VideoDisplayProfile * GetProfile()
Definition: videooutbase.h:79
#define LOC
AVFilterGraph * filter_graph
virtual ~MythCodecContext()
This class serves as the base class for all video output methods.
Definition: videooutbase.h:46
int InitDeinterlaceFilter(AVCodecContext *ctx, AVFrame *frame)
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
MythPlayer * player
virtual bool isValidDeinterlacer(QString)
virtual int FilteredReceiveFrame(AVCodecContext *ctx, AVFrame *frame)
const char * name
Definition: ParseText.cpp:339
virtual QStringList GetDeinterlacers(void)
static MythCodecContext * createMythCodecContext(MythCodecID codec)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
AVFilterContext * buffersrc_ctx
virtual QString GetDeinterlaceFilter()
bool CanSupportDoubleRate(void)
#define codec_is_vaapi2(id)
Definition: mythcodecid.h:131
AVBufferRef * hw_frames_ctx
QString GetFallbackDeint(void)