1 /*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include <stdio.h>
20 #include <string.h>
21
22 #include "libavutil/mem.h"
23 #include "libavutil/pixdesc.h"
24
25 #include "formats.h"
26 #include "opencl.h"
27
opencl_filter_set_device(AVFilterContext * avctx,AVBufferRef * device)28 static int opencl_filter_set_device(AVFilterContext *avctx,
29 AVBufferRef *device)
30 {
31 OpenCLFilterContext *ctx = avctx->priv;
32
33 av_buffer_unref(&ctx->device_ref);
34
35 ctx->device_ref = av_buffer_ref(device);
36 if (!ctx->device_ref)
37 return AVERROR(ENOMEM);
38
39 ctx->device = (AVHWDeviceContext*)ctx->device_ref->data;
40 ctx->hwctx = ctx->device->hwctx;
41
42 return 0;
43 }
44
ff_opencl_filter_config_input(AVFilterLink * inlink)45 int ff_opencl_filter_config_input(AVFilterLink *inlink)
46 {
47 AVFilterContext *avctx = inlink->dst;
48 OpenCLFilterContext *ctx = avctx->priv;
49 AVHWFramesContext *input_frames;
50 int err;
51
52 if (!inlink->hw_frames_ctx) {
53 av_log(avctx, AV_LOG_ERROR, "OpenCL filtering requires a "
54 "hardware frames context on the input.\n");
55 return AVERROR(EINVAL);
56 }
57
58 // Extract the device and default output format from the first input.
59 if (avctx->inputs[0] != inlink)
60 return 0;
61
62 input_frames = (AVHWFramesContext*)inlink->hw_frames_ctx->data;
63 if (input_frames->format != AV_PIX_FMT_OPENCL)
64 return AVERROR(EINVAL);
65
66 err = opencl_filter_set_device(avctx, input_frames->device_ref);
67 if (err < 0)
68 return err;
69
70 // Default output parameters match input parameters.
71 if (ctx->output_format == AV_PIX_FMT_NONE)
72 ctx->output_format = input_frames->sw_format;
73 if (!ctx->output_width)
74 ctx->output_width = inlink->w;
75 if (!ctx->output_height)
76 ctx->output_height = inlink->h;
77
78 return 0;
79 }
80
ff_opencl_filter_config_output(AVFilterLink * outlink)81 int ff_opencl_filter_config_output(AVFilterLink *outlink)
82 {
83 AVFilterContext *avctx = outlink->src;
84 OpenCLFilterContext *ctx = avctx->priv;
85 AVBufferRef *output_frames_ref = NULL;
86 AVHWFramesContext *output_frames;
87 int err;
88
89 av_buffer_unref(&outlink->hw_frames_ctx);
90
91 if (!ctx->device_ref) {
92 if (!avctx->hw_device_ctx) {
93 av_log(avctx, AV_LOG_ERROR, "OpenCL filtering requires an "
94 "OpenCL device.\n");
95 return AVERROR(EINVAL);
96 }
97
98 err = opencl_filter_set_device(avctx, avctx->hw_device_ctx);
99 if (err < 0)
100 return err;
101 }
102
103 output_frames_ref = av_hwframe_ctx_alloc(ctx->device_ref);
104 if (!output_frames_ref) {
105 err = AVERROR(ENOMEM);
106 goto fail;
107 }
108 output_frames = (AVHWFramesContext*)output_frames_ref->data;
109
110 output_frames->format = AV_PIX_FMT_OPENCL;
111 output_frames->sw_format = ctx->output_format;
112 output_frames->width = ctx->output_width;
113 output_frames->height = ctx->output_height;
114
115 err = av_hwframe_ctx_init(output_frames_ref);
116 if (err < 0) {
117 av_log(avctx, AV_LOG_ERROR, "Failed to initialise output "
118 "frames: %d.\n", err);
119 goto fail;
120 }
121
122 outlink->hw_frames_ctx = output_frames_ref;
123 outlink->w = ctx->output_width;
124 outlink->h = ctx->output_height;
125
126 return 0;
127 fail:
128 av_buffer_unref(&output_frames_ref);
129 return err;
130 }
131
ff_opencl_filter_init(AVFilterContext * avctx)132 int ff_opencl_filter_init(AVFilterContext *avctx)
133 {
134 OpenCLFilterContext *ctx = avctx->priv;
135
136 ctx->output_format = AV_PIX_FMT_NONE;
137
138 return 0;
139 }
140
ff_opencl_filter_uninit(AVFilterContext * avctx)141 void ff_opencl_filter_uninit(AVFilterContext *avctx)
142 {
143 OpenCLFilterContext *ctx = avctx->priv;
144 cl_int cle;
145
146 if (ctx->program) {
147 cle = clReleaseProgram(ctx->program);
148 if (cle != CL_SUCCESS)
149 av_log(avctx, AV_LOG_ERROR, "Failed to release "
150 "program: %d.\n", cle);
151 }
152
153 av_buffer_unref(&ctx->device_ref);
154 }
155
ff_opencl_filter_load_program(AVFilterContext * avctx,const char ** program_source_array,int nb_strings)156 int ff_opencl_filter_load_program(AVFilterContext *avctx,
157 const char **program_source_array,
158 int nb_strings)
159 {
160 OpenCLFilterContext *ctx = avctx->priv;
161 cl_int cle;
162
163 ctx->program = clCreateProgramWithSource(ctx->hwctx->context, nb_strings,
164 program_source_array,
165 NULL, &cle);
166 if (!ctx->program) {
167 av_log(avctx, AV_LOG_ERROR, "Failed to create program: %d.\n", cle);
168 return AVERROR(EIO);
169 }
170
171 cle = clBuildProgram(ctx->program, 1, &ctx->hwctx->device_id,
172 NULL, NULL, NULL);
173 if (cle != CL_SUCCESS) {
174 av_log(avctx, AV_LOG_ERROR, "Failed to build program: %d.\n", cle);
175
176 if (cle == CL_BUILD_PROGRAM_FAILURE) {
177 char *log;
178 size_t log_length;
179
180 clGetProgramBuildInfo(ctx->program, ctx->hwctx->device_id,
181 CL_PROGRAM_BUILD_LOG, 0, NULL, &log_length);
182
183 log = av_malloc(log_length);
184 if (log) {
185 cle = clGetProgramBuildInfo(ctx->program,
186 ctx->hwctx->device_id,
187 CL_PROGRAM_BUILD_LOG,
188 log_length, log, NULL);
189 if (cle == CL_SUCCESS)
190 av_log(avctx, AV_LOG_ERROR, "Build log:\n%s\n", log);
191 }
192
193 av_free(log);
194 }
195
196 clReleaseProgram(ctx->program);
197 ctx->program = NULL;
198 return AVERROR(EIO);
199 }
200
201 return 0;
202 }
203
ff_opencl_filter_load_program_from_file(AVFilterContext * avctx,const char * filename)204 int ff_opencl_filter_load_program_from_file(AVFilterContext *avctx,
205 const char *filename)
206 {
207 FILE *file;
208 char *src = NULL;
209 size_t pos, len, rb;
210 const char *src_const;
211 int err;
212
213 file = avpriv_fopen_utf8(filename, "r");
214 if (!file) {
215 av_log(avctx, AV_LOG_ERROR, "Unable to open program "
216 "source file \"%s\".\n", filename);
217 return AVERROR(ENOENT);
218 }
219
220 len = 1 << 16;
221 pos = 0;
222
223 err = av_reallocp(&src, len);
224 if (err < 0)
225 goto fail;
226
227 err = snprintf(src, len, "#line 1 \"%s\"\n", filename);
228 if (err < 0) {
229 err = AVERROR(errno);
230 goto fail;
231 }
232 if (err > len / 2) {
233 err = AVERROR(EINVAL);
234 goto fail;
235 }
236 pos = err;
237
238 while (1) {
239 rb = fread(src + pos, 1, len - pos - 1, file);
240 if (rb == 0 && ferror(file)) {
241 err = AVERROR(EIO);
242 goto fail;
243 }
244 pos += rb;
245 if (pos + 1 < len)
246 break;
247 len <<= 1;
248 err = av_reallocp(&src, len);
249 if (err < 0)
250 goto fail;
251 }
252 src[pos] = 0;
253
254 src_const = src;
255
256 err = ff_opencl_filter_load_program(avctx, &src_const, 1);
257 fail:
258 fclose(file);
259 av_freep(&src);
260 return err;
261 }
262
ff_opencl_filter_work_size_from_image(AVFilterContext * avctx,size_t * work_size,AVFrame * frame,int plane,int block_alignment)263 int ff_opencl_filter_work_size_from_image(AVFilterContext *avctx,
264 size_t *work_size,
265 AVFrame *frame, int plane,
266 int block_alignment)
267 {
268 cl_mem image;
269 cl_mem_object_type type;
270 size_t width, height;
271 cl_int cle;
272
273 if (frame->format != AV_PIX_FMT_OPENCL) {
274 av_log(avctx, AV_LOG_ERROR, "Invalid frame format %s, "
275 "opencl required.\n", av_get_pix_fmt_name(frame->format));
276 return AVERROR(EINVAL);
277 }
278
279 image = (cl_mem)frame->data[plane];
280 if (!image) {
281 av_log(avctx, AV_LOG_ERROR, "Plane %d required but not set.\n",
282 plane);
283 return AVERROR(EINVAL);
284 }
285
286 cle = clGetMemObjectInfo(image, CL_MEM_TYPE, sizeof(type),
287 &type, NULL);
288 if (cle != CL_SUCCESS) {
289 av_log(avctx, AV_LOG_ERROR, "Failed to query object type of "
290 "plane %d: %d.\n", plane, cle);
291 return AVERROR_UNKNOWN;
292 }
293 if (type != CL_MEM_OBJECT_IMAGE2D) {
294 av_log(avctx, AV_LOG_ERROR, "Plane %d is not a 2D image.\n",
295 plane);
296 return AVERROR(EINVAL);
297 }
298
299 cle = clGetImageInfo(image, CL_IMAGE_WIDTH, sizeof(size_t),
300 &width, NULL);
301 if (cle != CL_SUCCESS) {
302 av_log(avctx, AV_LOG_ERROR, "Failed to query plane %d width: %d.\n",
303 plane, cle);
304 return AVERROR_UNKNOWN;
305 }
306
307 cle = clGetImageInfo(image, CL_IMAGE_HEIGHT, sizeof(size_t),
308 &height, NULL);
309 if (cle != CL_SUCCESS) {
310 av_log(avctx, AV_LOG_ERROR, "Failed to query plane %d height: %d.\n",
311 plane, cle);
312 return AVERROR_UNKNOWN;
313 }
314
315 if (block_alignment) {
316 width = FFALIGN(width, block_alignment);
317 height = FFALIGN(height, block_alignment);
318 }
319
320 work_size[0] = width;
321 work_size[1] = height;
322
323 return 0;
324 }
325
ff_opencl_print_const_matrix_3x3(AVBPrint * buf,const char * name_str,double mat[3][3])326 void ff_opencl_print_const_matrix_3x3(AVBPrint *buf, const char *name_str,
327 double mat[3][3])
328 {
329 int i, j;
330 av_bprintf(buf, "__constant float %s[9] = {\n", name_str);
331 for (i = 0; i < 3; i++) {
332 for (j = 0; j < 3; j++)
333 av_bprintf(buf, " %.5ff,", mat[i][j]);
334 av_bprintf(buf, "\n");
335 }
336 av_bprintf(buf, "};\n");
337 }
338
ff_opencl_get_event_time(cl_event event)339 cl_ulong ff_opencl_get_event_time(cl_event event) {
340 cl_ulong time_start;
341 cl_ulong time_end;
342
343 clGetEventProfilingInfo(event, CL_PROFILING_COMMAND_START, sizeof(time_start), &time_start, NULL);
344 clGetEventProfilingInfo(event, CL_PROFILING_COMMAND_END, sizeof(time_end), &time_end, NULL);
345
346 return time_end - time_start;
347 }
348