Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
52867 views
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 <stdint.h>
20
21
#include <vdpau/vdpau.h>
22
#include <vdpau/vdpau_x11.h>
23
24
#include <X11/Xlib.h>
25
26
#include "ffmpeg.h"
27
28
#include "libavcodec/vdpau.h"
29
30
#include "libavutil/avassert.h"
31
#include "libavutil/buffer.h"
32
#include "libavutil/frame.h"
33
#include "libavutil/pixfmt.h"
34
35
typedef struct VDPAUContext {
36
Display *dpy;
37
38
VdpDevice device;
39
VdpDecoder decoder;
40
VdpGetProcAddress *get_proc_address;
41
42
VdpGetErrorString *get_error_string;
43
VdpGetInformationString *get_information_string;
44
VdpDeviceDestroy *device_destroy;
45
#if 1 // for ffmpegs older vdpau API, not the oldest though
46
VdpDecoderCreate *decoder_create;
47
VdpDecoderDestroy *decoder_destroy;
48
VdpDecoderRender *decoder_render;
49
#endif
50
VdpVideoSurfaceCreate *video_surface_create;
51
VdpVideoSurfaceDestroy *video_surface_destroy;
52
VdpVideoSurfaceGetBitsYCbCr *video_surface_get_bits;
53
VdpVideoSurfaceGetParameters *video_surface_get_parameters;
54
VdpVideoSurfaceQueryGetPutBitsYCbCrCapabilities *video_surface_query;
55
56
AVFrame *tmp_frame;
57
58
enum AVPixelFormat pix_fmt;
59
VdpYCbCrFormat vdpau_format;
60
} VDPAUContext;
61
62
int vdpau_api_ver = 2;
63
64
static void vdpau_uninit(AVCodecContext *s)
65
{
66
InputStream *ist = s->opaque;
67
VDPAUContext *ctx = ist->hwaccel_ctx;
68
69
ist->hwaccel_uninit = NULL;
70
ist->hwaccel_get_buffer = NULL;
71
ist->hwaccel_retrieve_data = NULL;
72
73
if (ctx->decoder_destroy)
74
ctx->decoder_destroy(ctx->decoder);
75
76
if (ctx->device_destroy)
77
ctx->device_destroy(ctx->device);
78
79
if (ctx->dpy)
80
XCloseDisplay(ctx->dpy);
81
82
av_frame_free(&ctx->tmp_frame);
83
84
av_freep(&ist->hwaccel_ctx);
85
av_freep(&s->hwaccel_context);
86
}
87
88
static void vdpau_release_buffer(void *opaque, uint8_t *data)
89
{
90
VdpVideoSurface surface = *(VdpVideoSurface*)data;
91
VDPAUContext *ctx = opaque;
92
93
ctx->video_surface_destroy(surface);
94
av_freep(&data);
95
}
96
97
static int vdpau_get_buffer(AVCodecContext *s, AVFrame *frame, int flags)
98
{
99
InputStream *ist = s->opaque;
100
VDPAUContext *ctx = ist->hwaccel_ctx;
101
VdpVideoSurface *surface;
102
VdpStatus err;
103
VdpChromaType chroma;
104
uint32_t width, height;
105
106
av_assert0(frame->format == AV_PIX_FMT_VDPAU);
107
108
if (av_vdpau_get_surface_parameters(s, &chroma, &width, &height))
109
return AVERROR(ENOSYS);
110
111
surface = av_malloc(sizeof(*surface));
112
if (!surface)
113
return AVERROR(ENOMEM);
114
115
frame->buf[0] = av_buffer_create((uint8_t*)surface, sizeof(*surface),
116
vdpau_release_buffer, ctx,
117
AV_BUFFER_FLAG_READONLY);
118
if (!frame->buf[0]) {
119
av_freep(&surface);
120
return AVERROR(ENOMEM);
121
}
122
123
// properly we should keep a pool of surfaces instead of creating
124
// them anew for each frame, but since we don't care about speed
125
// much in this code, we don't bother
126
err = ctx->video_surface_create(ctx->device, chroma, width, height,
127
surface);
128
if (err != VDP_STATUS_OK) {
129
av_log(NULL, AV_LOG_ERROR, "Error allocating a VDPAU video surface: %s\n",
130
ctx->get_error_string(err));
131
av_buffer_unref(&frame->buf[0]);
132
return AVERROR_UNKNOWN;
133
}
134
135
frame->data[3] = (uint8_t*)(uintptr_t)*surface;
136
137
return 0;
138
}
139
140
static int vdpau_retrieve_data(AVCodecContext *s, AVFrame *frame)
141
{
142
VdpVideoSurface surface = (VdpVideoSurface)(uintptr_t)frame->data[3];
143
InputStream *ist = s->opaque;
144
VDPAUContext *ctx = ist->hwaccel_ctx;
145
VdpStatus err;
146
int ret, chroma_type;
147
148
err = ctx->video_surface_get_parameters(surface, &chroma_type,
149
&ctx->tmp_frame->width,
150
&ctx->tmp_frame->height);
151
if (err != VDP_STATUS_OK) {
152
av_log(NULL, AV_LOG_ERROR, "Error getting surface parameters: %s\n",
153
ctx->get_error_string(err));
154
return AVERROR_UNKNOWN;
155
}
156
ctx->tmp_frame->format = ctx->pix_fmt;
157
158
ret = av_frame_get_buffer(ctx->tmp_frame, 32);
159
if (ret < 0)
160
return ret;
161
162
ctx->tmp_frame->width = frame->width;
163
ctx->tmp_frame->height = frame->height;
164
165
err = ctx->video_surface_get_bits(surface, ctx->vdpau_format,
166
(void * const *)ctx->tmp_frame->data,
167
ctx->tmp_frame->linesize);
168
if (err != VDP_STATUS_OK) {
169
av_log(NULL, AV_LOG_ERROR, "Error retrieving frame data from VDPAU: %s\n",
170
ctx->get_error_string(err));
171
ret = AVERROR_UNKNOWN;
172
goto fail;
173
}
174
175
if (ctx->vdpau_format == VDP_YCBCR_FORMAT_YV12)
176
FFSWAP(uint8_t*, ctx->tmp_frame->data[1], ctx->tmp_frame->data[2]);
177
178
ret = av_frame_copy_props(ctx->tmp_frame, frame);
179
if (ret < 0)
180
goto fail;
181
182
av_frame_unref(frame);
183
av_frame_move_ref(frame, ctx->tmp_frame);
184
return 0;
185
186
fail:
187
av_frame_unref(ctx->tmp_frame);
188
return ret;
189
}
190
191
static const int vdpau_formats[][2] = {
192
{ VDP_YCBCR_FORMAT_YV12, AV_PIX_FMT_YUV420P },
193
{ VDP_YCBCR_FORMAT_NV12, AV_PIX_FMT_NV12 },
194
{ VDP_YCBCR_FORMAT_YUYV, AV_PIX_FMT_YUYV422 },
195
{ VDP_YCBCR_FORMAT_UYVY, AV_PIX_FMT_UYVY422 },
196
};
197
198
static int vdpau_alloc(AVCodecContext *s)
199
{
200
InputStream *ist = s->opaque;
201
int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;
202
AVVDPAUContext *vdpau_ctx;
203
VDPAUContext *ctx;
204
const char *display, *vendor;
205
VdpStatus err;
206
int i;
207
208
ctx = av_mallocz(sizeof(*ctx));
209
if (!ctx)
210
return AVERROR(ENOMEM);
211
212
ist->hwaccel_ctx = ctx;
213
ist->hwaccel_uninit = vdpau_uninit;
214
ist->hwaccel_get_buffer = vdpau_get_buffer;
215
ist->hwaccel_retrieve_data = vdpau_retrieve_data;
216
217
ctx->tmp_frame = av_frame_alloc();
218
if (!ctx->tmp_frame)
219
goto fail;
220
221
ctx->dpy = XOpenDisplay(ist->hwaccel_device);
222
if (!ctx->dpy) {
223
av_log(NULL, loglevel, "Cannot open the X11 display %s.\n",
224
XDisplayName(ist->hwaccel_device));
225
goto fail;
226
}
227
display = XDisplayString(ctx->dpy);
228
229
err = vdp_device_create_x11(ctx->dpy, XDefaultScreen(ctx->dpy), &ctx->device,
230
&ctx->get_proc_address);
231
if (err != VDP_STATUS_OK) {
232
av_log(NULL, loglevel, "VDPAU device creation on X11 display %s failed.\n",
233
display);
234
goto fail;
235
}
236
237
#define GET_CALLBACK(id, result) \
238
do { \
239
void *tmp; \
240
err = ctx->get_proc_address(ctx->device, id, &tmp); \
241
if (err != VDP_STATUS_OK) { \
242
av_log(NULL, loglevel, "Error getting the " #id " callback.\n"); \
243
goto fail; \
244
} \
245
ctx->result = tmp; \
246
} while (0)
247
248
GET_CALLBACK(VDP_FUNC_ID_GET_ERROR_STRING, get_error_string);
249
GET_CALLBACK(VDP_FUNC_ID_GET_INFORMATION_STRING, get_information_string);
250
GET_CALLBACK(VDP_FUNC_ID_DEVICE_DESTROY, device_destroy);
251
if (vdpau_api_ver == 1) {
252
GET_CALLBACK(VDP_FUNC_ID_DECODER_CREATE, decoder_create);
253
GET_CALLBACK(VDP_FUNC_ID_DECODER_DESTROY, decoder_destroy);
254
GET_CALLBACK(VDP_FUNC_ID_DECODER_RENDER, decoder_render);
255
}
256
GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_CREATE, video_surface_create);
257
GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY, video_surface_destroy);
258
GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR, video_surface_get_bits);
259
GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_GET_PARAMETERS, video_surface_get_parameters);
260
GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_QUERY_GET_PUT_BITS_Y_CB_CR_CAPABILITIES,
261
video_surface_query);
262
263
for (i = 0; i < FF_ARRAY_ELEMS(vdpau_formats); i++) {
264
VdpBool supported;
265
err = ctx->video_surface_query(ctx->device, VDP_CHROMA_TYPE_420,
266
vdpau_formats[i][0], &supported);
267
if (err != VDP_STATUS_OK) {
268
av_log(NULL, loglevel,
269
"Error querying VDPAU surface capabilities: %s\n",
270
ctx->get_error_string(err));
271
goto fail;
272
}
273
if (supported)
274
break;
275
}
276
if (i == FF_ARRAY_ELEMS(vdpau_formats)) {
277
av_log(NULL, loglevel,
278
"No supported VDPAU format for retrieving the data.\n");
279
return AVERROR(EINVAL);
280
}
281
ctx->vdpau_format = vdpau_formats[i][0];
282
ctx->pix_fmt = vdpau_formats[i][1];
283
284
if (vdpau_api_ver == 1) {
285
vdpau_ctx = av_vdpau_alloc_context();
286
if (!vdpau_ctx)
287
goto fail;
288
vdpau_ctx->render = ctx->decoder_render;
289
290
s->hwaccel_context = vdpau_ctx;
291
} else
292
if (av_vdpau_bind_context(s, ctx->device, ctx->get_proc_address,
293
AV_HWACCEL_FLAG_IGNORE_LEVEL))
294
goto fail;
295
296
ctx->get_information_string(&vendor);
297
av_log(NULL, AV_LOG_VERBOSE, "Using VDPAU -- %s -- on X11 display %s, "
298
"to decode input stream #%d:%d.\n", vendor,
299
display, ist->file_index, ist->st->index);
300
301
return 0;
302
303
fail:
304
av_log(NULL, loglevel, "VDPAU init failed for stream #%d:%d.\n",
305
ist->file_index, ist->st->index);
306
vdpau_uninit(s);
307
return AVERROR(EINVAL);
308
}
309
310
static int vdpau_old_init(AVCodecContext *s)
311
{
312
InputStream *ist = s->opaque;
313
int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;
314
AVVDPAUContext *vdpau_ctx;
315
VDPAUContext *ctx;
316
VdpStatus err;
317
int profile, ret;
318
319
if (!ist->hwaccel_ctx) {
320
ret = vdpau_alloc(s);
321
if (ret < 0)
322
return ret;
323
}
324
ctx = ist->hwaccel_ctx;
325
vdpau_ctx = s->hwaccel_context;
326
327
ret = av_vdpau_get_profile(s, &profile);
328
if (ret < 0) {
329
av_log(NULL, loglevel, "No known VDPAU decoder profile for this stream.\n");
330
return AVERROR(EINVAL);
331
}
332
333
if (ctx->decoder)
334
ctx->decoder_destroy(ctx->decoder);
335
336
err = ctx->decoder_create(ctx->device, profile,
337
s->coded_width, s->coded_height,
338
16, &ctx->decoder);
339
if (err != VDP_STATUS_OK) {
340
av_log(NULL, loglevel, "Error creating the VDPAU decoder: %s\n",
341
ctx->get_error_string(err));
342
return AVERROR_UNKNOWN;
343
}
344
345
vdpau_ctx->decoder = ctx->decoder;
346
347
ist->hwaccel_get_buffer = vdpau_get_buffer;
348
ist->hwaccel_retrieve_data = vdpau_retrieve_data;
349
350
return 0;
351
}
352
353
int vdpau_init(AVCodecContext *s)
354
{
355
InputStream *ist = s->opaque;
356
357
if (vdpau_api_ver == 1)
358
return vdpau_old_init(s);
359
360
if (!ist->hwaccel_ctx) {
361
int ret = vdpau_alloc(s);
362
if (ret < 0)
363
return ret;
364
}
365
366
ist->hwaccel_get_buffer = vdpau_get_buffer;
367
ist->hwaccel_retrieve_data = vdpau_retrieve_data;
368
369
return 0;
370
}
371
372