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