1 /*****************************************************************************
2 * avcodec.c: VDPAU decoder for libav
3 *****************************************************************************
4 * Copyright (C) 2012-2013 Rémi Denis-Courmont
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include <string.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <assert.h>
29
30 #include <libavutil/mem.h>
31 #include <libavcodec/avcodec.h>
32 #include <libavcodec/vdpau.h>
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_fourcc.h>
36 #include <vlc_picture.h>
37 #include <vlc_atomic.h>
38 #include <vlc_xlib.h>
39 #include "vlc_vdpau.h"
40 #include "../../codec/avcodec/va.h"
41
42 struct vlc_va_sys_t
43 {
44 vdp_t *vdp;
45 VdpDevice device;
46 VdpChromaType type;
47 uint32_t width;
48 uint32_t height;
49 vlc_vdp_video_field_t *pool[];
50 };
51
CreateSurface(vlc_va_t * va)52 static vlc_vdp_video_field_t *CreateSurface(vlc_va_t *va)
53 {
54 vlc_va_sys_t *sys = va->sys;
55 VdpVideoSurface surface;
56 VdpStatus err;
57
58 err = vdp_video_surface_create(sys->vdp, sys->device, sys->type,
59 sys->width, sys->height, &surface);
60 if (err != VDP_STATUS_OK)
61 {
62 msg_Err(va, "%s creation failure: %s", "video surface",
63 vdp_get_error_string(sys->vdp, err));
64 return NULL;
65 }
66
67 vlc_vdp_video_field_t *field = vlc_vdp_video_create(sys->vdp, surface);
68 if (unlikely(field == NULL))
69 vdp_video_surface_destroy(sys->vdp, surface);
70 return field;
71 }
72
GetSurface(vlc_va_t * va)73 static vlc_vdp_video_field_t *GetSurface(vlc_va_t *va)
74 {
75 vlc_va_sys_t *sys = va->sys;
76 vlc_vdp_video_field_t *f;
77
78 for (unsigned i = 0; (f = sys->pool[i]) != NULL; i++)
79 {
80 uintptr_t expected = 1;
81
82 if (atomic_compare_exchange_strong(&f->frame->refs, &expected, 2))
83 {
84 vlc_vdp_video_field_t *field = vlc_vdp_video_copy(f);
85 atomic_fetch_sub(&f->frame->refs, 1);
86 return field;
87 }
88 }
89 return NULL;
90 }
91
Lock(vlc_va_t * va,picture_t * pic,uint8_t ** data)92 static int Lock(vlc_va_t *va, picture_t *pic, uint8_t **data)
93 {
94 vlc_vdp_video_field_t *field;
95 unsigned tries = (CLOCK_FREQ + VOUT_OUTMEM_SLEEP) / VOUT_OUTMEM_SLEEP;
96
97 while ((field = GetSurface(va)) == NULL)
98 {
99 if (--tries == 0)
100 return VLC_ENOMEM;
101 /* Pool empty. Wait for some time as in src/input/decoder.c.
102 * XXX: Both this and the core should use a semaphore or a CV. */
103 msleep(VOUT_OUTMEM_SLEEP);
104 }
105
106 pic->context = &field->context;
107 *data = (void *)(uintptr_t)field->frame->surface;
108 return VLC_SUCCESS;
109 }
110
Open(vlc_va_t * va,AVCodecContext * avctx,enum PixelFormat pix_fmt,const es_format_t * fmt,picture_sys_t * p_sys)111 static int Open(vlc_va_t *va, AVCodecContext *avctx, enum PixelFormat pix_fmt,
112 const es_format_t *fmt, picture_sys_t *p_sys)
113 {
114 if (pix_fmt != AV_PIX_FMT_VDPAU)
115 return VLC_EGENERIC;
116
117 (void) fmt;
118 (void) p_sys;
119 void *func;
120 VdpStatus err;
121 VdpChromaType type;
122 uint32_t width, height;
123
124 if (av_vdpau_get_surface_parameters(avctx, &type, &width, &height))
125 return VLC_EGENERIC;
126
127 switch (type)
128 {
129 case VDP_CHROMA_TYPE_420:
130 case VDP_CHROMA_TYPE_422:
131 case VDP_CHROMA_TYPE_444:
132 break;
133 default:
134 msg_Err(va, "unsupported chroma type %"PRIu32, type);
135 return VLC_EGENERIC;
136 }
137
138 if (!vlc_xlib_init(VLC_OBJECT(va)))
139 {
140 msg_Err(va, "Xlib is required for VDPAU");
141 return VLC_EGENERIC;
142 }
143
144 unsigned refs = avctx->refs + 2 * avctx->thread_count + 5;
145 vlc_va_sys_t *sys = malloc(sizeof (*sys)
146 + (refs + 1) * sizeof (sys->pool[0]));
147 if (unlikely(sys == NULL))
148 return VLC_ENOMEM;
149
150 sys->type = type;
151 sys->width = width;
152 sys->height = height;
153
154 err = vdp_get_x11(NULL, -1, &sys->vdp, &sys->device);
155 if (err != VDP_STATUS_OK)
156 {
157 free(sys);
158 return VLC_EGENERIC;
159 }
160
161 unsigned flags = AV_HWACCEL_FLAG_ALLOW_HIGH_DEPTH;
162
163 err = vdp_get_proc_address(sys->vdp, sys->device,
164 VDP_FUNC_ID_GET_PROC_ADDRESS, &func);
165 if (err != VDP_STATUS_OK)
166 goto error;
167
168 if (av_vdpau_bind_context(avctx, sys->device, func, flags))
169 goto error;
170 va->sys = sys;
171
172 unsigned i = 0;
173 while (i < refs)
174 {
175 sys->pool[i] = CreateSurface(va);
176 if (sys->pool[i] == NULL)
177 break;
178 i++;
179 }
180 sys->pool[i] = NULL;
181
182 if (i < avctx->refs + 3u)
183 {
184 msg_Err(va, "not enough video RAM");
185 while (i > 0)
186 vlc_vdp_video_destroy(sys->pool[--i]);
187 goto error;
188 }
189
190 if (i < refs)
191 msg_Warn(va, "video RAM low (allocated %u of %u buffers)",
192 i, refs);
193
194 const char *infos;
195 if (vdp_get_information_string(sys->vdp, &infos) != VDP_STATUS_OK)
196 infos = "VDPAU";
197
198 va->description = infos;
199 va->get = Lock;
200 return VLC_SUCCESS;
201
202 error:
203 vdp_release_x11(sys->vdp);
204 free(sys);
205 return VLC_EGENERIC;
206 }
207
Close(vlc_va_t * va,void ** hwctx)208 static void Close(vlc_va_t *va, void **hwctx)
209 {
210 vlc_va_sys_t *sys = va->sys;
211
212 for (unsigned i = 0; sys->pool[i] != NULL; i++)
213 vlc_vdp_video_destroy(sys->pool[i]);
214 vdp_release_x11(sys->vdp);
215 av_freep(hwctx);
216 free(sys);
217 }
218
219 vlc_module_begin()
220 set_description(N_("VDPAU video decoder"))
221 set_capability("hw decoder", 100)
222 set_category(CAT_INPUT)
223 set_subcategory(SUBCAT_INPUT_VCODEC)
224 set_callbacks(Open, Close)
225 add_shortcut("vdpau")
226 vlc_module_end()
227