• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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