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 VdpDecoderCreate
*decoder_create
;
46 VdpDecoderDestroy
*decoder_destroy
;
47 VdpDecoderRender
*decoder_render
;
48 VdpVideoSurfaceCreate
*video_surface_create
;
49 VdpVideoSurfaceDestroy
*video_surface_destroy
;
50 VdpVideoSurfaceGetBitsYCbCr
*video_surface_get_bits
;
51 VdpVideoSurfaceGetParameters
*video_surface_get_parameters
;
52 VdpVideoSurfaceQueryGetPutBitsYCbCrCapabilities
*video_surface_query
;
56 enum AVPixelFormat pix_fmt
;
57 VdpYCbCrFormat vdpau_format
;
60 static void vdpau_uninit(AVCodecContext
*s
)
62 InputStream
*ist
= s
->opaque
;
63 VDPAUContext
*ctx
= ist
->hwaccel_ctx
;
65 ist
->hwaccel_uninit
= NULL
;
66 ist
->hwaccel_get_buffer
= NULL
;
67 ist
->hwaccel_retrieve_data
= NULL
;
69 if (ctx
->decoder_destroy
)
70 ctx
->decoder_destroy(ctx
->decoder
);
72 if (ctx
->device_destroy
)
73 ctx
->device_destroy(ctx
->device
);
76 XCloseDisplay(ctx
->dpy
);
78 av_frame_free(&ctx
->tmp_frame
);
80 av_freep(&ist
->hwaccel_ctx
);
81 av_freep(&s
->hwaccel_context
);
84 static void vdpau_release_buffer(void *opaque
, uint8_t *data
)
86 VdpVideoSurface surface
= *(VdpVideoSurface
*)data
;
87 VDPAUContext
*ctx
= opaque
;
89 ctx
->video_surface_destroy(surface
);
93 static int vdpau_get_buffer(AVCodecContext
*s
, AVFrame
*frame
, int flags
)
95 InputStream
*ist
= s
->opaque
;
96 VDPAUContext
*ctx
= ist
->hwaccel_ctx
;
97 VdpVideoSurface
*surface
;
100 av_assert0(frame
->format
== AV_PIX_FMT_VDPAU
);
102 surface
= av_malloc(sizeof(*surface
));
104 return AVERROR(ENOMEM
);
106 frame
->buf
[0] = av_buffer_create((uint8_t*)surface
, sizeof(*surface
),
107 vdpau_release_buffer
, ctx
,
108 AV_BUFFER_FLAG_READONLY
);
109 if (!frame
->buf
[0]) {
111 return AVERROR(ENOMEM
);
114 // properly we should keep a pool of surfaces instead of creating
115 // them anew for each frame, but since we don't care about speed
116 // much in this code, we don't bother
117 err
= ctx
->video_surface_create(ctx
->device
, VDP_CHROMA_TYPE_420
,
118 frame
->width
, frame
->height
, surface
);
119 if (err
!= VDP_STATUS_OK
) {
120 av_log(NULL
, AV_LOG_ERROR
, "Error allocating a VDPAU video surface: %s\n",
121 ctx
->get_error_string(err
));
122 av_buffer_unref(&frame
->buf
[0]);
123 return AVERROR_UNKNOWN
;
126 frame
->data
[3] = (uint8_t*)(uintptr_t)*surface
;
131 static int vdpau_retrieve_data(AVCodecContext
*s
, AVFrame
*frame
)
133 VdpVideoSurface surface
= (VdpVideoSurface
)(uintptr_t)frame
->data
[3];
134 InputStream
*ist
= s
->opaque
;
135 VDPAUContext
*ctx
= ist
->hwaccel_ctx
;
137 int ret
, chroma_type
;
139 err
= ctx
->video_surface_get_parameters(surface
, &chroma_type
,
140 &ctx
->tmp_frame
->width
,
141 &ctx
->tmp_frame
->height
);
142 if (err
!= VDP_STATUS_OK
) {
143 av_log(NULL
, AV_LOG_ERROR
, "Error getting surface parameters: %s\n",
144 ctx
->get_error_string(err
));
145 return AVERROR_UNKNOWN
;
147 ctx
->tmp_frame
->format
= ctx
->pix_fmt
;
149 ret
= av_frame_get_buffer(ctx
->tmp_frame
, 32);
153 ctx
->tmp_frame
->width
= frame
->width
;
154 ctx
->tmp_frame
->height
= frame
->height
;
156 err
= ctx
->video_surface_get_bits(surface
, ctx
->vdpau_format
,
157 (void * const *)ctx
->tmp_frame
->data
,
158 ctx
->tmp_frame
->linesize
);
159 if (err
!= VDP_STATUS_OK
) {
160 av_log(NULL
, AV_LOG_ERROR
, "Error retrieving frame data from VDPAU: %s\n",
161 ctx
->get_error_string(err
));
162 ret
= AVERROR_UNKNOWN
;
166 if (ctx
->vdpau_format
== VDP_YCBCR_FORMAT_YV12
)
167 FFSWAP(uint8_t*, ctx
->tmp_frame
->data
[1], ctx
->tmp_frame
->data
[2]);
169 ret
= av_frame_copy_props(ctx
->tmp_frame
, frame
);
173 av_frame_unref(frame
);
174 av_frame_move_ref(frame
, ctx
->tmp_frame
);
178 av_frame_unref(ctx
->tmp_frame
);
182 static const int vdpau_formats
[][2] = {
183 { VDP_YCBCR_FORMAT_YV12
, AV_PIX_FMT_YUV420P
},
184 { VDP_YCBCR_FORMAT_NV12
, AV_PIX_FMT_NV12
},
185 { VDP_YCBCR_FORMAT_YUYV
, AV_PIX_FMT_YUYV422
},
186 { VDP_YCBCR_FORMAT_UYVY
, AV_PIX_FMT_UYVY422
},
189 static int vdpau_alloc(AVCodecContext
*s
)
191 InputStream
*ist
= s
->opaque
;
192 int loglevel
= (ist
->hwaccel_id
== HWACCEL_AUTO
) ? AV_LOG_VERBOSE
: AV_LOG_ERROR
;
193 AVVDPAUContext
*vdpau_ctx
;
195 const char *display
, *vendor
;
199 ctx
= av_mallocz(sizeof(*ctx
));
201 return AVERROR(ENOMEM
);
203 ist
->hwaccel_ctx
= ctx
;
204 ist
->hwaccel_uninit
= vdpau_uninit
;
205 ist
->hwaccel_get_buffer
= vdpau_get_buffer
;
206 ist
->hwaccel_retrieve_data
= vdpau_retrieve_data
;
208 ctx
->tmp_frame
= av_frame_alloc();
212 ctx
->dpy
= XOpenDisplay(ist
->hwaccel_device
);
214 av_log(NULL
, loglevel
, "Cannot open the X11 display %s.\n",
215 XDisplayName(ist
->hwaccel_device
));
218 display
= XDisplayString(ctx
->dpy
);
220 err
= vdp_device_create_x11(ctx
->dpy
, XDefaultScreen(ctx
->dpy
), &ctx
->device
,
221 &ctx
->get_proc_address
);
222 if (err
!= VDP_STATUS_OK
) {
223 av_log(NULL
, loglevel
, "VDPAU device creation on X11 display %s failed.\n",
228 #define GET_CALLBACK(id, result) \
231 err = ctx->get_proc_address(ctx->device, id, &tmp); \
232 if (err != VDP_STATUS_OK) { \
233 av_log(NULL, loglevel, "Error getting the " #id " callback.\n"); \
239 GET_CALLBACK(VDP_FUNC_ID_GET_ERROR_STRING
, get_error_string
);
240 GET_CALLBACK(VDP_FUNC_ID_GET_INFORMATION_STRING
, get_information_string
);
241 GET_CALLBACK(VDP_FUNC_ID_DEVICE_DESTROY
, device_destroy
);
242 GET_CALLBACK(VDP_FUNC_ID_DECODER_CREATE
, decoder_create
);
243 GET_CALLBACK(VDP_FUNC_ID_DECODER_DESTROY
, decoder_destroy
);
244 GET_CALLBACK(VDP_FUNC_ID_DECODER_RENDER
, decoder_render
);
245 GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_CREATE
, video_surface_create
);
246 GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY
, video_surface_destroy
);
247 GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR
, video_surface_get_bits
);
248 GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_GET_PARAMETERS
, video_surface_get_parameters
);
249 GET_CALLBACK(VDP_FUNC_ID_VIDEO_SURFACE_QUERY_GET_PUT_BITS_Y_CB_CR_CAPABILITIES
,
250 video_surface_query
);
252 for (i
= 0; i
< FF_ARRAY_ELEMS(vdpau_formats
); i
++) {
254 err
= ctx
->video_surface_query(ctx
->device
, VDP_CHROMA_TYPE_420
,
255 vdpau_formats
[i
][0], &supported
);
256 if (err
!= VDP_STATUS_OK
) {
257 av_log(NULL
, loglevel
,
258 "Error querying VDPAU surface capabilities: %s\n",
259 ctx
->get_error_string(err
));
265 if (i
== FF_ARRAY_ELEMS(vdpau_formats
)) {
266 av_log(NULL
, loglevel
,
267 "No supported VDPAU format for retrieving the data.\n");
268 return AVERROR(EINVAL
);
270 ctx
->vdpau_format
= vdpau_formats
[i
][0];
271 ctx
->pix_fmt
= vdpau_formats
[i
][1];
273 vdpau_ctx
= av_vdpau_alloc_context();
276 vdpau_ctx
->render
= ctx
->decoder_render
;
278 s
->hwaccel_context
= vdpau_ctx
;
280 ctx
->get_information_string(&vendor
);
281 av_log(NULL
, AV_LOG_VERBOSE
, "Using VDPAU -- %s -- on X11 display %s, "
282 "to decode input stream #%d:%d.\n", vendor
,
283 display
, ist
->file_index
, ist
->st
->index
);
288 av_log(NULL
, loglevel
, "VDPAU init failed for stream #%d:%d.\n",
289 ist
->file_index
, ist
->st
->index
);
291 return AVERROR(EINVAL
);
294 int vdpau_init(AVCodecContext
*s
)
296 InputStream
*ist
= s
->opaque
;
297 int loglevel
= (ist
->hwaccel_id
== HWACCEL_AUTO
) ? AV_LOG_VERBOSE
: AV_LOG_ERROR
;
298 AVVDPAUContext
*vdpau_ctx
;
303 if (!ist
->hwaccel_ctx
) {
304 ret
= vdpau_alloc(s
);
308 ctx
= ist
->hwaccel_ctx
;
309 vdpau_ctx
= s
->hwaccel_context
;
311 ret
= av_vdpau_get_profile(s
, &profile
);
313 av_log(NULL
, loglevel
, "No known VDPAU decoder profile for this stream.\n");
314 return AVERROR(EINVAL
);
318 ctx
->decoder_destroy(ctx
->decoder
);
320 err
= ctx
->decoder_create(ctx
->device
, profile
,
321 s
->coded_width
, s
->coded_height
,
323 if (err
!= VDP_STATUS_OK
) {
324 av_log(NULL
, loglevel
, "Error creating the VDPAU decoder: %s\n",
325 ctx
->get_error_string(err
));
326 return AVERROR_UNKNOWN
;
329 vdpau_ctx
->decoder
= ctx
->decoder
;
331 ist
->hwaccel_get_buffer
= vdpau_get_buffer
;
332 ist
->hwaccel_retrieve_data
= vdpau_retrieve_data
;