]> git.sesse.net Git - ffmpeg/blob - libavutil/hwcontext_cuda.c
build: Generate pkg-config files from Make and not from configure
[ffmpeg] / libavutil / hwcontext_cuda.c
1 /*
2  * This file is part of Libav.
3  *
4  * Libav 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  * Libav 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 Libav; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 #include "buffer.h"
20 #include "common.h"
21 #include "hwcontext.h"
22 #include "hwcontext_internal.h"
23 #include "hwcontext_cuda.h"
24 #include "mem.h"
25 #include "pixdesc.h"
26 #include "pixfmt.h"
27
28 typedef struct CUDAFramesContext {
29     int shift_width, shift_height;
30 } CUDAFramesContext;
31
32 static const enum AVPixelFormat supported_formats[] = {
33     AV_PIX_FMT_NV12,
34     AV_PIX_FMT_YUV420P,
35     AV_PIX_FMT_P010,
36     AV_PIX_FMT_YUV444P,
37     AV_PIX_FMT_YUV444P16,
38 };
39
40 static void cuda_buffer_free(void *opaque, uint8_t *data)
41 {
42     AVHWFramesContext *ctx = opaque;
43     AVCUDADeviceContext *hwctx = ctx->device_ctx->hwctx;
44
45     CUcontext dummy;
46
47     cuCtxPushCurrent(hwctx->cuda_ctx);
48
49     cuMemFree((CUdeviceptr)data);
50
51     cuCtxPopCurrent(&dummy);
52 }
53
54 static AVBufferRef *cuda_pool_alloc(void *opaque, int size)
55 {
56     AVHWFramesContext     *ctx = opaque;
57     AVCUDADeviceContext *hwctx = ctx->device_ctx->hwctx;
58
59     AVBufferRef *ret = NULL;
60     CUcontext dummy = NULL;
61     CUdeviceptr data;
62     CUresult err;
63
64     err = cuCtxPushCurrent(hwctx->cuda_ctx);
65     if (err != CUDA_SUCCESS) {
66         av_log(ctx, AV_LOG_ERROR, "Error setting current CUDA context\n");
67         return NULL;
68     }
69
70     err = cuMemAlloc(&data, size);
71     if (err != CUDA_SUCCESS)
72         goto fail;
73
74     ret = av_buffer_create((uint8_t*)data, size, cuda_buffer_free, ctx, 0);
75     if (!ret) {
76         cuMemFree(data);
77         goto fail;
78     }
79
80 fail:
81     cuCtxPopCurrent(&dummy);
82     return ret;
83 }
84
85 static int cuda_frames_init(AVHWFramesContext *ctx)
86 {
87     CUDAFramesContext *priv = ctx->internal->priv;
88     int i;
89
90     for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) {
91         if (ctx->sw_format == supported_formats[i])
92             break;
93     }
94     if (i == FF_ARRAY_ELEMS(supported_formats)) {
95         av_log(ctx, AV_LOG_ERROR, "Pixel format '%s' is not supported\n",
96                av_get_pix_fmt_name(ctx->sw_format));
97         return AVERROR(ENOSYS);
98     }
99
100     av_pix_fmt_get_chroma_sub_sample(ctx->sw_format, &priv->shift_width, &priv->shift_height);
101
102     if (!ctx->pool) {
103         int size;
104
105         switch (ctx->sw_format) {
106         case AV_PIX_FMT_NV12:
107         case AV_PIX_FMT_YUV420P:
108             size = ctx->width * ctx->height * 3 / 2;
109             break;
110         case AV_PIX_FMT_P010:
111             size = ctx->width * ctx->height * 3;
112             break;
113         case AV_PIX_FMT_YUV444P:
114             size = ctx->width * ctx->height * 3;
115             break;
116         case AV_PIX_FMT_YUV444P16:
117             size = ctx->width * ctx->height * 6;
118             break;
119         }
120
121         ctx->internal->pool_internal = av_buffer_pool_init2(size, ctx, cuda_pool_alloc, NULL);
122         if (!ctx->internal->pool_internal)
123             return AVERROR(ENOMEM);
124     }
125
126     return 0;
127 }
128
129 static int cuda_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
130 {
131     frame->buf[0] = av_buffer_pool_get(ctx->pool);
132     if (!frame->buf[0])
133         return AVERROR(ENOMEM);
134
135     switch (ctx->sw_format) {
136     case AV_PIX_FMT_NV12:
137         frame->data[0]     = frame->buf[0]->data;
138         frame->data[1]     = frame->data[0] + ctx->width * ctx->height;
139         frame->linesize[0] = ctx->width;
140         frame->linesize[1] = ctx->width;
141         break;
142     case AV_PIX_FMT_YUV420P:
143         frame->data[0]     = frame->buf[0]->data;
144         frame->data[2]     = frame->data[0] + ctx->width * ctx->height;
145         frame->data[1]     = frame->data[2] + ctx->width * ctx->height / 4;
146         frame->linesize[0] = ctx->width;
147         frame->linesize[1] = ctx->width / 2;
148         frame->linesize[2] = ctx->width / 2;
149         break;
150     case AV_PIX_FMT_P010:
151         frame->data[0]     = frame->buf[0]->data;
152         frame->data[1]     = frame->data[0] + 2 * ctx->width * ctx->height;
153         frame->linesize[0] = 2 * ctx->width;
154         frame->linesize[1] = 2 * ctx->width;
155         break;
156     case AV_PIX_FMT_YUV444P:
157         frame->data[0]     = frame->buf[0]->data;
158         frame->data[1]     = frame->data[0] + ctx->width * ctx->height;
159         frame->data[2]     = frame->data[1] + ctx->width * ctx->height;
160         frame->linesize[0] = ctx->width;
161         frame->linesize[1] = ctx->width;
162         frame->linesize[2] = ctx->width;
163         break;
164     case AV_PIX_FMT_YUV444P16:
165         frame->data[0]     = frame->buf[0]->data;
166         frame->data[1]     = frame->data[0] + 2 * ctx->width * ctx->height;
167         frame->data[2]     = frame->data[1] + 2 * ctx->width * ctx->height;
168         frame->linesize[0] = 2 * ctx->width;
169         frame->linesize[1] = 2 * ctx->width;
170         frame->linesize[2] = 2 * ctx->width;
171         break;
172     default:
173         av_frame_unref(frame);
174         return AVERROR_BUG;
175     }
176
177     frame->format = AV_PIX_FMT_CUDA;
178     frame->width  = ctx->width;
179     frame->height = ctx->height;
180
181     return 0;
182 }
183
184 static int cuda_transfer_get_formats(AVHWFramesContext *ctx,
185                                      enum AVHWFrameTransferDirection dir,
186                                      enum AVPixelFormat **formats)
187 {
188     enum AVPixelFormat *fmts;
189
190     fmts = av_malloc_array(2, sizeof(*fmts));
191     if (!fmts)
192         return AVERROR(ENOMEM);
193
194     fmts[0] = ctx->sw_format;
195     fmts[1] = AV_PIX_FMT_NONE;
196
197     *formats = fmts;
198
199     return 0;
200 }
201
202 static int cuda_transfer_data_from(AVHWFramesContext *ctx, AVFrame *dst,
203                                    const AVFrame *src)
204 {
205     CUDAFramesContext           *priv = ctx->internal->priv;
206     AVCUDADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
207
208     CUcontext dummy;
209     CUresult err;
210     int i;
211
212     err = cuCtxPushCurrent(device_hwctx->cuda_ctx);
213     if (err != CUDA_SUCCESS)
214         return AVERROR_UNKNOWN;
215
216     for (i = 0; i < FF_ARRAY_ELEMS(src->data) && src->data[i]; i++) {
217         CUDA_MEMCPY2D cpy = {
218             .srcMemoryType = CU_MEMORYTYPE_DEVICE,
219             .dstMemoryType = CU_MEMORYTYPE_HOST,
220             .srcDevice     = (CUdeviceptr)src->data[i],
221             .dstHost       = dst->data[i],
222             .srcPitch      = src->linesize[i],
223             .dstPitch      = dst->linesize[i],
224             .WidthInBytes  = FFMIN(src->linesize[i], dst->linesize[i]),
225             .Height        = src->height >> (i ? priv->shift_height : 0),
226         };
227
228         err = cuMemcpy2D(&cpy);
229         if (err != CUDA_SUCCESS) {
230             av_log(ctx, AV_LOG_ERROR, "Error transferring the data from the CUDA frame\n");
231             return AVERROR_UNKNOWN;
232         }
233     }
234
235     cuCtxPopCurrent(&dummy);
236
237     return 0;
238 }
239
240 static int cuda_transfer_data_to(AVHWFramesContext *ctx, AVFrame *dst,
241                                  const AVFrame *src)
242 {
243     CUDAFramesContext           *priv = ctx->internal->priv;
244     AVCUDADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
245
246     CUcontext dummy;
247     CUresult err;
248     int i;
249
250     err = cuCtxPushCurrent(device_hwctx->cuda_ctx);
251     if (err != CUDA_SUCCESS)
252         return AVERROR_UNKNOWN;
253
254     for (i = 0; i < FF_ARRAY_ELEMS(src->data) && src->data[i]; i++) {
255         CUDA_MEMCPY2D cpy = {
256             .srcMemoryType = CU_MEMORYTYPE_HOST,
257             .dstMemoryType = CU_MEMORYTYPE_DEVICE,
258             .srcHost       = src->data[i],
259             .dstDevice     = (CUdeviceptr)dst->data[i],
260             .srcPitch      = src->linesize[i],
261             .dstPitch      = dst->linesize[i],
262             .WidthInBytes  = FFMIN(src->linesize[i], dst->linesize[i]),
263             .Height        = src->height >> (i ? priv->shift_height : 0),
264         };
265
266         err = cuMemcpy2D(&cpy);
267         if (err != CUDA_SUCCESS) {
268             av_log(ctx, AV_LOG_ERROR, "Error transferring the data from the CUDA frame\n");
269             return AVERROR_UNKNOWN;
270         }
271     }
272
273     cuCtxPopCurrent(&dummy);
274
275     return 0;
276 }
277
278 static void cuda_device_free(AVHWDeviceContext *ctx)
279 {
280     AVCUDADeviceContext *hwctx = ctx->hwctx;
281     cuCtxDestroy(hwctx->cuda_ctx);
282 }
283
284 static int cuda_device_create(AVHWDeviceContext *ctx, const char *device,
285                               AVDictionary *opts, int flags)
286 {
287     AVCUDADeviceContext *hwctx = ctx->hwctx;
288     CUdevice cu_device;
289     CUcontext dummy;
290     CUresult err;
291     int device_idx = 0;
292
293     if (device)
294         device_idx = strtol(device, NULL, 0);
295
296     err = cuInit(0);
297     if (err != CUDA_SUCCESS) {
298         av_log(ctx, AV_LOG_ERROR, "Could not initialize the CUDA driver API\n");
299         return AVERROR_UNKNOWN;
300     }
301
302     err = cuDeviceGet(&cu_device, device_idx);
303     if (err != CUDA_SUCCESS) {
304         av_log(ctx, AV_LOG_ERROR, "Could not get the device number %d\n", device_idx);
305         return AVERROR_UNKNOWN;
306     }
307
308     err = cuCtxCreate(&hwctx->cuda_ctx, 0, cu_device);
309     if (err != CUDA_SUCCESS) {
310         av_log(ctx, AV_LOG_ERROR, "Error creating a CUDA context\n");
311         return AVERROR_UNKNOWN;
312     }
313
314     cuCtxPopCurrent(&dummy);
315
316     ctx->free = cuda_device_free;
317
318     return 0;
319 }
320
321 const HWContextType ff_hwcontext_type_cuda = {
322     .type                 = AV_HWDEVICE_TYPE_CUDA,
323     .name                 = "CUDA",
324
325     .device_hwctx_size    = sizeof(AVCUDADeviceContext),
326     .frames_priv_size     = sizeof(CUDAFramesContext),
327
328     .device_create        = cuda_device_create,
329     .frames_init          = cuda_frames_init,
330     .frames_get_buffer    = cuda_get_buffer,
331     .transfer_get_formats = cuda_transfer_get_formats,
332     .transfer_data_to     = cuda_transfer_data_to,
333     .transfer_data_from   = cuda_transfer_data_from,
334
335     .pix_fmts             = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, AV_PIX_FMT_NONE },
336 };