2 * This file is part of FFmpeg.
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.
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.
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
21 #include <vdpau/vdpau.h>
22 #include <vdpau/vdpau_x11.h>
28 #include "libavcodec/vdpau.h"
30 #include "libavutil/avassert.h"
31 #include "libavutil/buffer.h"
32 #include "libavutil/frame.h"
33 #include "libavutil/pixfmt.h"
35 typedef struct VDPAUContext
{
40 VdpGetProcAddress
*get_proc_address
;
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
;
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
;
58 enum AVPixelFormat pix_fmt
;
59 VdpYCbCrFormat vdpau_format
;
62 int vdpau_api_ver
= 2;
64 static void vdpau_uninit(AVCodecContext
*s
)
66 InputStream
*ist
= s
->opaque
;
67 VDPAUContext
*ctx
= ist
->hwaccel_ctx
;
69 ist
->hwaccel_uninit
= NULL
;
70 ist
->hwaccel_get_buffer
= NULL
;
71 ist
->hwaccel_retrieve_data
= NULL
;
73 if (ctx
->decoder_destroy
)
74 ctx
->decoder_destroy(ctx
->decoder
);
76 if (ctx
->device_destroy
)
77 ctx
->device_destroy(ctx
->device
);
80 XCloseDisplay(ctx
->dpy
);
82 av_frame_free(&ctx
->tmp_frame
);
84 av_freep(&ist
->hwaccel_ctx
);
85 av_freep(&s
->hwaccel_context
);
88 static void vdpau_release_buffer(void *opaque
, uint8_t *data
)
90 VdpVideoSurface surface
= *(VdpVideoSurface
*)data
;
91 VDPAUContext
*ctx
= opaque
;
93 ctx
->video_surface_destroy(surface
);
97 static int vdpau_get_buffer(AVCodecContext
*s
, AVFrame
*frame
, int flags
)
99 InputStream
*ist
= s
->opaque
;
100 VDPAUContext
*ctx
= ist
->hwaccel_ctx
;
101 VdpVideoSurface
*surface
;
104 av_assert0(frame
->format
== AV_PIX_FMT_VDPAU
);
106 surface
= av_malloc(sizeof(*surface
));
108 return AVERROR(ENOMEM
);
110 frame
->buf
[0] = av_buffer_create((uint8_t*)surface
, sizeof(*surface
),
111 vdpau_release_buffer
, ctx
,
112 AV_BUFFER_FLAG_READONLY
);
113 if (!frame
->buf
[0]) {
115 return AVERROR(ENOMEM
);
118 // properly we should keep a pool of surfaces instead of creating
119 // them anew for each frame, but since we don't care about speed
120 // much in this code, we don't bother
121 err
= ctx
->video_surface_create(ctx
->device
, VDP_CHROMA_TYPE_420
,
122 frame
->width
, frame
->height
, surface
);
123 if (err
!= VDP_STATUS_OK
) {
124 av_log(NULL
, AV_LOG_ERROR
, "Error allocating a VDPAU video surface: %s\n",
125 ctx
->get_error_string(err
));
126 av_buffer_unref(&frame
->buf
[0]);
127 return AVERROR_UNKNOWN
;
130 frame
->data
[3] = (uint8_t*)(uintptr_t)*surface
;
135 static int vdpau_retrieve_data(AVCodecContext
*s
, AVFrame
*frame
)
137 VdpVideoSurface surface
= (VdpVideoSurface
)(uintptr_t)frame
->data
[3];
138 InputStream
*ist
= s
->opaque
;
139 VDPAUContext
*ctx
= ist
->hwaccel_ctx
;
141 int ret
, chroma_type
;
143 err
= ctx
->video_surface_get_parameters(surface
, &chroma_type
,
144 &ctx
->tmp_frame
->width
,
145 &ctx
->tmp_frame
->height
);
146 if (err
!= VDP_STATUS_OK
) {
147 av_log(NULL
, AV_LOG_ERROR
, "Error getting surface parameters: %s\n",
148 ctx
->get_error_string(err
));
149 return AVERROR_UNKNOWN
;
151 ctx
->tmp_frame
->format
= ctx
->pix_fmt
;
153 ret
= av_frame_get_buffer(ctx
->tmp_frame
, 32);
157 ctx
->tmp_frame
->width
= frame
->width
;
158 ctx
->tmp_frame
->height
= frame
->height
;
160 err
= ctx
->video_surface_get_bits(surface
, ctx
->vdpau_format
,
161 (void * const *)ctx
->tmp_frame
->data
,
162 ctx
->tmp_frame
->linesize
);
163 if (err
!= VDP_STATUS_OK
) {
164 av_log(NULL
, AV_LOG_ERROR
, "Error retrieving frame data from VDPAU: %s\n",
165 ctx
->get_error_string(err
));
166 ret
= AVERROR_UNKNOWN
;
170 if (ctx
->vdpau_format
== VDP_YCBCR_FORMAT_YV12
)
171 FFSWAP(uint8_t*, ctx
->tmp_frame
->data
[1], ctx
->tmp_frame
->data
[2]);
173 ret
= av_frame_copy_props(ctx
->tmp_frame
, frame
);
177 av_frame_unref(frame
);
178 av_frame_move_ref(frame
, ctx
->tmp_frame
);
182 av_frame_unref(ctx
->tmp_frame
);
186 static const int vdpau_formats
[][2] = {
187 { VDP_YCBCR_FORMAT_YV12
, AV_PIX_FMT_YUV420P
},
188 { VDP_YCBCR_FORMAT_NV12
, AV_PIX_FMT_NV12
},
189 { VDP_YCBCR_FORMAT_YUYV
, AV_PIX_FMT_YUYV422
},
190 { VDP_YCBCR_FORMAT_UYVY
, AV_PIX_FMT_UYVY422
},
193 static int vdpau_alloc(AVCodecContext
*s
)
195 InputStream
*ist
= s
->opaque
;
196 int loglevel
= (ist
->hwaccel_id
== HWACCEL_AUTO
) ? AV_LOG_VERBOSE
: AV_LOG_ERROR
;
197 AVVDPAUContext
*vdpau_ctx
;
199 const char *display
, *vendor
;
203 ctx
= av_mallocz(sizeof(*ctx
));
205 return AVERROR(ENOMEM
);
207 ist
->hwaccel_ctx
= ctx
;
208 ist
->hwaccel_uninit
= vdpau_uninit
;
209 ist
->hwaccel_get_buffer
= vdpau_get_buffer
;
210 ist
->hwaccel_retrieve_data
= vdpau_retrieve_data
;
212 ctx
->tmp_frame
= av_frame_alloc();
216 ctx
->dpy
= XOpenDisplay(ist
->hwaccel_device
);
218 av_log(NULL
, loglevel
, "Cannot open the X11 display %s.\n",
219 XDisplayName(ist
->hwaccel_device
));
222 display
= XDisplayString(ctx
->dpy
);
224 err
= vdp_device_create_x11(ctx
->dpy
, XDefaultScreen(ctx
->dpy
), &ctx
->device
,
225 &ctx
->get_proc_address
);
226 if (err
!= VDP_STATUS_OK
) {
227 av_log(NULL
, loglevel
, "VDPAU device creation on X11 display %s failed.\n",
232 #define GET_CALLBACK(id, result) \
235 err = ctx->get_proc_address(ctx->device, id, &tmp); \
236 if (err != VDP_STATUS_OK) { \
237 av_log(NULL, loglevel, "Error getting the " #id " callback.\n"); \
243 GET_CALLBACK(VDP_FUNC_ID_GET_ERROR_STRING
, get_error_string
);
244 GET_CALLBACK(VDP_FUNC_ID_GET_INFORMATION_STRING
, get_information_string
);
245 GET_CALLBACK(VDP_FUNC_ID_DEVICE_DESTROY
, device_destroy
);
246 if (vdpau_api_ver
== 1) {
247 GET_CALLBACK(VDP_FUNC_ID_DECODER_CREATE
, decoder_create
);
248 GET_CALLBACK(VDP_FUNC_ID_DECODER_DESTROY
, decoder_destroy
);
249 GET_CALLBACK(VDP_FUNC_ID_DECODER_RENDER
, decoder_render
);
251 GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_CREATE
, video_surface_create
);
252 GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY
, video_surface_destroy
);
253 GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR
, video_surface_get_bits
);
254 GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_GET_PARAMETERS
, video_surface_get_parameters
);
255 GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_QUERY_GET_PUT_BITS_Y_CB_CR_CAPABILITIES
,
256 video_surface_query
);
258 for (i
= 0; i
< FF_ARRAY_ELEMS(vdpau_formats
); i
++) {
260 err
= ctx
->video_surface_query(ctx
->device
, VDP_CHROMA_TYPE_420
,
261 vdpau_formats
[i
][0], &supported
);
262 if (err
!= VDP_STATUS_OK
) {
263 av_log(NULL
, loglevel
,
264 "Error querying VDPAU surface capabilities: %s\n",
265 ctx
->get_error_string(err
));
271 if (i
== FF_ARRAY_ELEMS(vdpau_formats
)) {
272 av_log(NULL
, loglevel
,
273 "No supported VDPAU format for retrieving the data.\n");
274 return AVERROR(EINVAL
);
276 ctx
->vdpau_format
= vdpau_formats
[i
][0];
277 ctx
->pix_fmt
= vdpau_formats
[i
][1];
279 if (vdpau_api_ver
== 1) {
280 vdpau_ctx
= av_vdpau_alloc_context();
283 vdpau_ctx
->render
= ctx
->decoder_render
;
285 s
->hwaccel_context
= vdpau_ctx
;
287 if (av_vdpau_bind_context(s
, ctx
->device
, ctx
->get_proc_address
, 0))
290 ctx
->get_information_string(&vendor
);
291 av_log(NULL
, AV_LOG_VERBOSE
, "Using VDPAU -- %s -- on X11 display %s, "
292 "to decode input stream #%d:%d.\n", vendor
,
293 display
, ist
->file_index
, ist
->st
->index
);
298 av_log(NULL
, loglevel
, "VDPAU init failed for stream #%d:%d.\n",
299 ist
->file_index
, ist
->st
->index
);
301 return AVERROR(EINVAL
);
304 static int vdpau_old_init(AVCodecContext
*s
)
306 InputStream
*ist
= s
->opaque
;
307 int loglevel
= (ist
->hwaccel_id
== HWACCEL_AUTO
) ? AV_LOG_VERBOSE
: AV_LOG_ERROR
;
308 AVVDPAUContext
*vdpau_ctx
;
313 if (!ist
->hwaccel_ctx
) {
314 ret
= vdpau_alloc(s
);
318 ctx
= ist
->hwaccel_ctx
;
319 vdpau_ctx
= s
->hwaccel_context
;
321 ret
= av_vdpau_get_profile(s
, &profile
);
323 av_log(NULL
, loglevel
, "No known VDPAU decoder profile for this stream.\n");
324 return AVERROR(EINVAL
);
328 ctx
->decoder_destroy(ctx
->decoder
);
330 err
= ctx
->decoder_create(ctx
->device
, profile
,
331 s
->coded_width
, s
->coded_height
,
333 if (err
!= VDP_STATUS_OK
) {
334 av_log(NULL
, loglevel
, "Error creating the VDPAU decoder: %s\n",
335 ctx
->get_error_string(err
));
336 return AVERROR_UNKNOWN
;
339 vdpau_ctx
->decoder
= ctx
->decoder
;
341 ist
->hwaccel_get_buffer
= vdpau_get_buffer
;
342 ist
->hwaccel_retrieve_data
= vdpau_retrieve_data
;
347 int vdpau_init(AVCodecContext
*s
)
349 InputStream
*ist
= s
->opaque
;
351 if (vdpau_api_ver
== 1)
352 return vdpau_old_init(s
);
354 if (!ist
->hwaccel_ctx
) {
355 int ret
= vdpau_alloc(s
);
360 ist
->hwaccel_get_buffer
= vdpau_get_buffer
;
361 ist
->hwaccel_retrieve_data
= vdpau_retrieve_data
;