MythTV  master
vaapi2context.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 "vaapi2context.h"
28 #include "videooutbase.h"
29 #include "mythplayer.h"
30 #include "mythhwcontext.h"
31 
32 extern "C" {
33  #include "libavutil/pixfmt.h"
34  #include "libavutil/hwcontext.h"
35  #include "libavcodec/avcodec.h"
36  #include "libavfilter/avfilter.h"
37  #include "libavformat/avformat.h"
38  #include "libavfilter/buffersrc.h"
39 }
40 
41 #define LOC QString("VAAPI2: ")
42 
44 {
45  CloseFilters();
46 }
47 
48 // Currently this will only set up the filter after an interlaced frame.
49 // If we need other filters apart from deinterlace filters we will
50 // need to make a change here.
51 
52 int Vaapi2Context::FilteredReceiveFrame(AVCodecContext *ctx, AVFrame *frame)
53 {
54  int ret = 0;
55 
56  while (true)
57  {
58  if (m_filterGraph)
59  {
60  ret = av_buffersink_get_frame(m_bufferSinkCtx, frame);
61  if (ret >= 0)
62  {
63  if (priorPts[0] && ptsUsed == priorPts[1])
64  {
65  frame->pts = priorPts[1] + (priorPts[1] - priorPts[0])/2;
66  frame->scte_cc_len = 0;
67  frame->atsc_cc_len = 0;
68  av_frame_remove_side_data(frame, AV_FRAME_DATA_A53_CC);
69  }
70  else
71  {
72  frame->pts = priorPts[1];
73  ptsUsed = priorPts[1];
74  }
75  }
76  if (ret != AVERROR(EAGAIN))
77  break;
78  }
79 
80  // EAGAIN or no filter graph
81  ret = avcodec_receive_frame(ctx, frame);
82  if (ret < 0)
83  break;
84  priorPts[0]=priorPts[1];
85  priorPts[1]=frame->pts;
86  if (frame->interlaced_frame || m_filterGraph)
87  {
89  || width != frame->width
90  || height != frame->height)
91  {
92  // bypass any frame of unknown format
93  if (frame->format < 0)
94  break;
95  ret = InitDeinterlaceFilter(ctx, frame);
96  if (ret < 0)
97  {
98  LOG(VB_GENERAL, LOG_ERR, LOC + "InitDeinterlaceFilter failed - continue without filters");
99  break;
100  }
101  }
102  if (m_filterGraph)
103  {
104  ret = av_buffersrc_add_frame(m_bufferSrcCtx, frame);
105  if (ret < 0)
106  break;
107  }
108  else
109  break;
110  }
111  else
112  break;
113  }
114 
115  return ret;
116 }
117 
118 int Vaapi2Context::InitDeinterlaceFilter(AVCodecContext *ctx, AVFrame *frame)
119 {
120  QMutexLocker lock(&contextLock);
121  char args[512];
122  int ret = 0;
123  CloseFilters();
124  width = frame->width;
125  height = frame->height;
126  m_filtersInitialized = true;
127  if (!player || !m_stream)
128  {
129  LOG(VB_GENERAL, LOG_ERR, LOC + "Player or stream is not set up in MythCodecContext");
130  return -1;
131  }
132  if (m_doubleRate && !player->CanSupportDoubleRate())
133  {
134  QString request = deinterlacername;
135  deinterlacername = GetFallbackDeint();
136  LOG(VB_PLAYBACK, LOG_INFO, LOC
137  + QString("Deinterlacer %1 requires double rate, switching to %2 instead.")
138  .arg(request).arg(deinterlacername));
139  if (!isCodecDeinterlacer(deinterlacername))
140  deinterlacername.clear();
141  m_doubleRate = deinterlacername.contains("doublerate");
142 
143  // if the fallback is a non-vaapi - deinterlace will be turned off
144  // and the videoout methods can take over.
145  }
146  QString filters;
147  if (isValidDeinterlacer(deinterlacername))
148  filters = GetDeinterlaceFilter();
149 
150  if (filters.isEmpty())
151  {
152  LOG(VB_GENERAL, LOG_INFO, LOC +
153  "Disabled hardware decoder based deinterlacer.");
154  return ret;
155  }
156  const AVFilter *buffersrc = avfilter_get_by_name("buffer");
157  const AVFilter *buffersink = avfilter_get_by_name("buffersink");
158  AVFilterInOut *outputs = avfilter_inout_alloc();
159  AVFilterInOut *inputs = avfilter_inout_alloc();
160  AVRational time_base = m_stream->time_base;
161  AVBufferSrcParameters* params = nullptr;
162 
163  m_filterGraph = avfilter_graph_alloc();
164  if (!outputs || !inputs || !m_filterGraph)
165  {
166  ret = AVERROR(ENOMEM);
167  goto end;
168  }
169 
170  /* buffer video source: the decoded frames from the decoder will be inserted here. */
171  snprintf(args, sizeof(args),
172  "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
173  frame->width, frame->height, frame->format, // ctx->pix_fmt,
174  time_base.num, time_base.den,
175  ctx->sample_aspect_ratio.num, ctx->sample_aspect_ratio.den);
176 
177  // isInterlaced = frame->interlaced_frame;
178 
179  ret = avfilter_graph_create_filter(&m_bufferSrcCtx, buffersrc, "in",
180  args, nullptr, m_filterGraph);
181  if (ret < 0)
182  {
183  LOG(VB_GENERAL, LOG_ERR, LOC + "avfilter_graph_create_filter failed for buffer source");
184  goto end;
185  }
186 
187  params = av_buffersrc_parameters_alloc();
188  if (m_hwFramesCtx)
189  av_buffer_unref(&m_hwFramesCtx);
190  m_hwFramesCtx = av_buffer_ref(frame->m_hwFramesCtx);
191  params->m_hwFramesCtx = m_hwFramesCtx;
192 
193  ret = av_buffersrc_parameters_set(m_bufferSrcCtx, params);
194 
195  if (ret < 0)
196  {
197  LOG(VB_GENERAL, LOG_ERR, LOC + "av_buffersrc_parameters_set failed");
198  goto end;
199  }
200 
201  av_freep(&params);
202 
203  /* buffer video sink: to terminate the filter chain. */
204  ret = avfilter_graph_create_filter(&m_bufferSinkCtx, buffersink, "out",
205  nullptr, nullptr, m_filterGraph);
206  if (ret < 0)
207  {
208  LOG(VB_GENERAL, LOG_ERR, LOC + "avfilter_graph_create_filter failed for buffer sink");
209  goto end;
210  }
211 
212  /*
213  * Set the endpoints for the filter graph. The filter_graph will
214  * be linked to the graph described by filters_descr.
215  */
216 
217  /*
218  * The buffer source output must be connected to the input pad of
219  * the first filter described by filters_descr; since the first
220  * filter input label is not specified, it is set to "in" by
221  * default.
222  */
223  outputs->name = av_strdup("in");
224  outputs->filter_ctx = m_bufferSrcCtx;
225  outputs->pad_idx = 0;
226  outputs->next = nullptr;
227 
228  /*
229  * The buffer sink input must be connected to the output pad of
230  * the last filter described by filters_descr; since the last
231  * filter output label is not specified, it is set to "out" by
232  * default.
233  */
234  inputs->name = av_strdup("out");
235  inputs->filter_ctx = m_bufferSinkCtx;
236  inputs->pad_idx = 0;
237  inputs->next = nullptr;
238 
239  if ((ret = avfilter_graph_parse_ptr(m_filterGraph, filters.toLocal8Bit(),
240  &inputs, &outputs,nullptr)) < 0)
241  {
242  LOG(VB_GENERAL, LOG_ERR, LOC
243  + QString("avfilter_graph_parse_ptr failed for %1").arg(filters));
244  goto end;
245  }
246 
247  if ((ret = avfilter_graph_config(m_filterGraph, nullptr)) < 0)
248  {
249  LOG(VB_GENERAL, LOG_ERR, LOC
250  + QString("avfilter_graph_config failed"));
251  goto end;
252  }
253 
254  LOG(VB_GENERAL, LOG_INFO, LOC +
255  QString("Enabled hardware decoder based deinterlace filter '%1': <%2>.")
256  .arg(deinterlacername).arg(filters));
257 end:
258  if (ret < 0)
259  {
260  avfilter_graph_free(&m_filterGraph);
261  m_filterGraph = nullptr;
262  m_doubleRate = false;
263  }
264  avfilter_inout_free(&inputs);
265  avfilter_inout_free(&outputs);
266 
267  return ret;
268 }
269 
271 {
272  avfilter_graph_free(&m_filterGraph);
273  m_filterGraph = nullptr;
274  m_bufferSinkCtx = nullptr;
275  m_bufferSrcCtx = nullptr;
276  m_filtersInitialized = false;
277  m_ptsUsed = 0;
278  m_priorPts[0] = 0;
279  m_priorPts[1] = 0;
280  // isInterlaced = 0;
281  m_width = 0;
282  m_height = 0;
283 
284  if (m_hwFramesCtx)
285  av_buffer_unref(&m_hwFramesCtx);
286 }
#define LOC
virtual int FilteredReceiveFrame(AVCodecContext *ctx, AVFrame *frame) override
Retrieve and process/filter AVFrame.
AVFilterContext * m_bufferSinkCtx
Definition: vaapi2context.h:52
struct AVFrame AVFrame
AVFilterGraph * m_filterGraph
Definition: vaapi2context.h:54
int InitDeinterlaceFilter(AVCodecContext *ctx, AVFrame *frame) override
AVFilterContext * m_bufferSrcCtx
Definition: vaapi2context.h:53
bool m_filtersInitialized
Definition: vaapi2context.h:55
int64_t m_priorPts[2]
Definition: vaapi2context.h:57
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
int64_t m_ptsUsed
Definition: vaapi2context.h:58
~Vaapi2Context() override
AVStream * m_stream
Definition: vaapi2context.h:51
AVBufferRef * m_hwFramesCtx
Definition: vaapi2context.h:56