FFmpeg  4.4.7
vf_neighbor_opencl.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018 Danil Iashchenko
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include "libavutil/common.h"
22 #include "libavutil/imgutils.h"
23 #include "libavutil/mem.h"
24 #include "libavutil/opt.h"
25 #include "libavutil/pixdesc.h"
26 #include "libavutil/avstring.h"
27 
28 
29 #include "avfilter.h"
30 #include "internal.h"
31 #include "opencl.h"
32 #include "opencl_source.h"
33 #include "video.h"
34 
35 typedef struct NeighborOpenCLContext {
37 
39  cl_kernel kernel;
40  cl_command_queue command_queue;
41 
42  char *matrix_str[4];
43 
44  cl_float threshold[4];
45  cl_int coordinates;
46  cl_mem coord;
47 
49 
51 {
52  NeighborOpenCLContext *ctx = avctx->priv;
53  const char *kernel_name;
54  cl_int cle;
55  int err;
56 
58  if (err < 0)
59  goto fail;
60 
61  ctx->command_queue = clCreateCommandQueue(ctx->ocf.hwctx->context,
62  ctx->ocf.hwctx->device_id,
63  0, &cle);
64  CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to create OpenCL "
65  "command queue %d.\n", cle);
66 
67  if (!strcmp(avctx->filter->name, "erosion_opencl")){
68  kernel_name = "erosion_global";
69  } else if (!strcmp(avctx->filter->name, "dilation_opencl")){
70  kernel_name = "dilation_global";
71  } else {
72  err = AVERROR_BUG;
73  goto fail;
74  }
75  ctx->kernel = clCreateKernel(ctx->ocf.program, kernel_name, &cle);
76  CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to create "
77  "kernel %d.\n", cle);
78 
79  ctx->initialised = 1;
80  return 0;
81 
82 fail:
83  if (ctx->command_queue)
84  clReleaseCommandQueue(ctx->command_queue);
85  if (ctx->kernel)
86  clReleaseKernel(ctx->kernel);
87  return err;
88 }
89 
91 {
92  NeighborOpenCLContext *ctx = avctx->priv;
93  cl_int matrix[9];
94  cl_mem buffer;
95  cl_int cle;
96  int i;
97 
98  for (i = 0; i < 4; i++) {
99  ctx->threshold[i] /= 255.0;
100  }
101 
102  matrix[4] = 0;
103  for (i = 0; i < 8; i++) {
104  if (ctx->coordinates & (1 << i)) {
105  matrix[i > 3 ? i + 1: i] = 1;
106  }
107  }
108  buffer = clCreateBuffer(ctx->ocf.hwctx->context,
109  CL_MEM_READ_ONLY |
110  CL_MEM_COPY_HOST_PTR |
111  CL_MEM_HOST_NO_ACCESS,
112  9 * sizeof(cl_int), matrix, &cle);
113  if (!buffer) {
114  av_log(avctx, AV_LOG_ERROR, "Failed to create matrix buffer: "
115  "%d.\n", cle);
116  return AVERROR(EIO);
117  }
118  ctx->coord = buffer;
119 
120  return 0;
121 }
122 
123 
125 {
126  AVFilterContext *avctx = inlink->dst;
127  AVFilterLink *outlink = avctx->outputs[0];
128  NeighborOpenCLContext *ctx = avctx->priv;
129  AVFrame *output = NULL;
130  cl_int cle;
131  size_t global_work[2];
132  cl_mem src, dst;
133  int err, p;
134  size_t origin[3] = {0, 0, 0};
135  size_t region[3] = {0, 0, 1};
136 
137  av_log(ctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n",
138  av_get_pix_fmt_name(input->format),
139  input->width, input->height, input->pts);
140 
141  if (!input->hw_frames_ctx)
142  return AVERROR(EINVAL);
143 
144  if (!ctx->initialised) {
145  err = neighbor_opencl_init(avctx);
146  if (err < 0)
147  goto fail;
148 
150  if (err < 0)
151  goto fail;
152 
153  }
154 
155  output = ff_get_video_buffer(outlink, outlink->w, outlink->h);
156  if (!output) {
157  err = AVERROR(ENOMEM);
158  goto fail;
159  }
160 
161  for (p = 0; p < FF_ARRAY_ELEMS(output->data); p++) {
162  src = (cl_mem) input->data[p];
163  dst = (cl_mem)output->data[p];
164 
165  if (!dst)
166  break;
167 
168  if (ctx->threshold[p] == 0) {
169  err = ff_opencl_filter_work_size_from_image(avctx, region, output, p, 0);
170  if (err < 0)
171  goto fail;
172 
173  cle = clEnqueueCopyImage(ctx->command_queue, src, dst,
174  origin, origin, region, 0, NULL, NULL);
175  CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to copy plane %d: %d.\n",
176  p, cle);
177  } else {
178  CL_SET_KERNEL_ARG(ctx->kernel, 0, cl_mem, &dst);
179  CL_SET_KERNEL_ARG(ctx->kernel, 1, cl_mem, &src);
180  CL_SET_KERNEL_ARG(ctx->kernel, 2, cl_float, &ctx->threshold[p]);
181  CL_SET_KERNEL_ARG(ctx->kernel, 3, cl_mem, &ctx->coord);
182 
183  err = ff_opencl_filter_work_size_from_image(avctx, global_work, output, p, 0);
184  if (err < 0)
185  goto fail;
186 
187  av_log(avctx, AV_LOG_DEBUG, "Run kernel on plane %d "
188  "(%"SIZE_SPECIFIER"x%"SIZE_SPECIFIER").\n",
189  p, global_work[0], global_work[1]);
190 
191  cle = clEnqueueNDRangeKernel(ctx->command_queue, ctx->kernel, 2, NULL,
192  global_work, NULL,
193  0, NULL, NULL);
194  CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to enqueue "
195  "kernel: %d.\n", cle);
196  }
197  }
198 
199  cle = clFinish(ctx->command_queue);
200  CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to finish command queue: %d.\n", cle);
201 
202  err = av_frame_copy_props(output, input);
203  if (err < 0)
204  goto fail;
205 
206  av_frame_free(&input);
207 
208  av_log(ctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
209  av_get_pix_fmt_name(output->format),
210  output->width, output->height, output->pts);
211 
212  return ff_filter_frame(outlink, output);
213 
214 fail:
215  clFinish(ctx->command_queue);
216  av_frame_free(&input);
217  av_frame_free(&output);
218  return err;
219 }
220 
222 {
223  NeighborOpenCLContext *ctx = avctx->priv;
224  cl_int cle;
225 
226  clReleaseMemObject(ctx->coord);
227 
228  if (ctx->kernel) {
229  cle = clReleaseKernel(ctx->kernel);
230  if (cle != CL_SUCCESS)
231  av_log(avctx, AV_LOG_ERROR, "Failed to release "
232  "kernel: %d.\n", cle);
233  }
234 
235  if (ctx->command_queue) {
236  cle = clReleaseCommandQueue(ctx->command_queue);
237  if (cle != CL_SUCCESS)
238  av_log(avctx, AV_LOG_ERROR, "Failed to release "
239  "command queue: %d.\n", cle);
240  }
241 
243 }
244 
246  {
247  .name = "default",
248  .type = AVMEDIA_TYPE_VIDEO,
249  .filter_frame = &neighbor_opencl_filter_frame,
250  .config_props = &ff_opencl_filter_config_input,
251  },
252  { NULL }
253 };
254 
256  {
257  .name = "default",
258  .type = AVMEDIA_TYPE_VIDEO,
259  .config_props = &ff_opencl_filter_config_output,
260  },
261  { NULL }
262 };
263 
264 #define OFFSET(x) offsetof(NeighborOpenCLContext, x)
265 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
266 
267 #if CONFIG_EROSION_OPENCL_FILTER
268 
269 static const AVOption erosion_opencl_options[] = {
270  { "threshold0", "set threshold for 1st plane", OFFSET(threshold[0]), AV_OPT_TYPE_FLOAT, {.dbl=65535.0}, 0.0, 65535, FLAGS },
271  { "threshold1", "set threshold for 2nd plane", OFFSET(threshold[1]), AV_OPT_TYPE_FLOAT, {.dbl=65535.0}, 0.0, 65535, FLAGS },
272  { "threshold2", "set threshold for 3rd plane", OFFSET(threshold[2]), AV_OPT_TYPE_FLOAT, {.dbl=65535.0}, 0.0, 65535, FLAGS },
273  { "threshold3", "set threshold for 4th plane", OFFSET(threshold[3]), AV_OPT_TYPE_FLOAT, {.dbl=65535.0}, 0.0, 65535, FLAGS },
274  { "coordinates", "set coordinates", OFFSET(coordinates), AV_OPT_TYPE_INT, {.i64=255}, 0, 255, FLAGS },
275  { NULL }
276 };
277 
278 AVFILTER_DEFINE_CLASS(erosion_opencl);
279 
281  .name = "erosion_opencl",
282  .description = NULL_IF_CONFIG_SMALL("Apply erosion effect"),
283  .priv_size = sizeof(NeighborOpenCLContext),
284  .priv_class = &erosion_opencl_class,
290  .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
291 };
292 
293 #endif /* CONFIG_EROSION_OPENCL_FILTER */
294 
295 #if CONFIG_DILATION_OPENCL_FILTER
296 
297 static const AVOption dilation_opencl_options[] = {
298  { "threshold0", "set threshold for 1st plane", OFFSET(threshold[0]), AV_OPT_TYPE_FLOAT, {.dbl=65535.0}, 0.0, 65535, FLAGS },
299  { "threshold1", "set threshold for 2nd plane", OFFSET(threshold[1]), AV_OPT_TYPE_FLOAT, {.dbl=65535.0}, 0.0, 65535, FLAGS },
300  { "threshold2", "set threshold for 3rd plane", OFFSET(threshold[2]), AV_OPT_TYPE_FLOAT, {.dbl=65535.0}, 0.0, 65535, FLAGS },
301  { "threshold3", "set threshold for 4th plane", OFFSET(threshold[3]), AV_OPT_TYPE_FLOAT, {.dbl=65535.0}, 0.0, 65535, FLAGS },
302  { "coordinates", "set coordinates", OFFSET(coordinates), AV_OPT_TYPE_INT, {.i64=255}, 0, 255, FLAGS },
303  { NULL }
304 };
305 
306 AVFILTER_DEFINE_CLASS(dilation_opencl);
307 
309  .name = "dilation_opencl",
310  .description = NULL_IF_CONFIG_SMALL("Apply dilation effect"),
311  .priv_size = sizeof(NeighborOpenCLContext),
312  .priv_class = &dilation_opencl_class,
318  .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
319 };
320 
321 #endif /* CONFIG_DILATION_OPENCL_FILTER */
static int query_formats(AVFilterContext *ctx)
Definition: aeval.c:243
static const AVFilterPad inputs[]
Definition: af_acontrast.c:193
static const AVFilterPad outputs[]
Definition: af_acontrast.c:203
AVFilter ff_vf_dilation_opencl
AVFilter ff_vf_erosion_opencl
#define av_cold
Definition: attributes.h:88
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1096
Main libavfilter public API header.
static av_cold int init(AVCodecContext *avctx)
Definition: avrndec.c:31
#define fail()
Definition: checkasm.h:133
common internal and external API header
#define NULL
Definition: coverity.c:32
static av_cold int uninit(AVCodecContext *avctx)
Definition: crystalhd.c:279
@ AV_OPT_TYPE_INT
Definition: opt.h:225
@ AV_OPT_TYPE_FLOAT
Definition: opt.h:228
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition: error.h:50
#define AVERROR(e)
Definition: error.h:43
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:203
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:658
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:215
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:194
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
misc image utilities
int i
Definition: input.c:407
#define FF_FILTER_FLAG_HWFRAME_AWARE
The filter is aware of hardware frames, and any hardware frame context should not be automatically pr...
Definition: internal.h:339
#define AVFILTER_DEFINE_CLASS(fname)
Definition: internal.h:288
common internal API header
#define SIZE_SPECIFIER
Definition: internal.h:193
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:117
Memory handling functions.
const char * ff_opencl_source_neighbor
Definition: neighbor.c:2
void ff_opencl_filter_uninit(AVFilterContext *avctx)
Uninitialise an OpenCL filter context.
Definition: opencl.c:156
int ff_opencl_filter_load_program(AVFilterContext *avctx, const char **program_source_array, int nb_strings)
Load a new OpenCL program from strings in memory.
Definition: opencl.c:171
int ff_opencl_filter_config_input(AVFilterLink *inlink)
Check that the input link contains a suitable hardware frames context and extract the device from it.
Definition: opencl.c:60
int ff_opencl_filter_init(AVFilterContext *avctx)
Initialise an OpenCL filter context.
Definition: opencl.c:147
int ff_opencl_filter_work_size_from_image(AVFilterContext *avctx, size_t *work_size, AVFrame *frame, int plane, int block_alignment)
Find the work size needed needed for a given plane of an image.
Definition: opencl.c:278
int ff_opencl_filter_config_output(AVFilterLink *outlink)
Create a suitable hardware frames context for the output.
Definition: opencl.c:96
int ff_opencl_filter_query_formats(AVFilterContext *avctx)
Return that all inputs and outputs support only AV_PIX_FMT_OPENCL.
Definition: opencl.c:28
#define CL_SET_KERNEL_ARG(kernel, arg_num, type, arg)
set argument to specific Kernel.
Definition: opencl.h:61
#define CL_FAIL_ON_ERROR(errcode,...)
A helper macro to handle OpenCL errors.
Definition: opencl.h:74
AVOptions.
const char * av_get_pix_fmt_name(enum AVPixelFormat pix_fmt)
Return the short name for a pixel format, NULL in case pix_fmt is unknown.
Definition: pixdesc.c:2489
static char buffer[20]
Definition: seek.c:32
#define FF_ARRAY_ELEMS(a)
An instance of a filter.
Definition: avfilter.h:341
const AVFilter * filter
the AVFilter of which this is an instance
Definition: avfilter.h:344
void * priv
private data for use by the filter
Definition: avfilter.h:356
AVFilterLink ** outputs
array of pointers to output links
Definition: avfilter.h:353
A filter pad used for either input or output.
Definition: internal.h:54
const char * name
Pad name.
Definition: internal.h:60
Filter definition.
Definition: avfilter.h:145
const char * name
Filter name.
Definition: avfilter.h:149
This structure describes decoded (raw) audio or video data.
Definition: frame.h:318
int64_t pts
Presentation timestamp in time_base units (time when frame should be shown to user).
Definition: frame.h:411
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:332
int width
Definition: frame.h:376
AVBufferRef * hw_frames_ctx
For hwaccel-format frames, this should be a reference to the AVHWFramesContext describing the frame.
Definition: frame.h:657
int height
Definition: frame.h:376
int format
format of the frame, -1 if unknown or unset Values correspond to enum AVPixelFormat for video frames,...
Definition: frame.h:391
AVOption.
Definition: opt.h:248
cl_command_queue command_queue
OpenCLFilterContext ocf
#define av_log(a,...)
#define src
Definition: vp8dsp.c:255
AVFormatContext * ctx
Definition: movenc.c:48
static const AVFilterPad neighbor_opencl_outputs[]
static int neighbor_opencl_filter_frame(AVFilterLink *inlink, AVFrame *input)
#define FLAGS
static int neighbor_opencl_make_filter_params(AVFilterContext *avctx)
static const AVFilterPad neighbor_opencl_inputs[]
#define OFFSET(x)
static av_cold void neighbor_opencl_uninit(AVFilterContext *avctx)
static int neighbor_opencl_init(AVFilterContext *avctx)
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition: video.c:104