1 /*****************************************************************************
2  * va_surface.c: libavcodec Generic Video Acceleration helpers
3  *****************************************************************************
4  * Copyright (C) 2009 Geoffroy Couprie
5  * Copyright (C) 2009 Laurent Aimar
6  * Copyright (C) 2015 Steve Lhomme
7  *
8  * Authors: Geoffroy Couprie <geal@videolan.org>
9  *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
10  *          Steve Lhomme <robux4@gmail.com>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29 
30 #include <assert.h>
31 
32 #include <vlc_common.h>
33 #include <vlc_codecs.h>
34 #include <vlc_codec.h>
35 #include <vlc_picture.h>
36 
37 struct picture_sys_t {
38     void *dummy;
39 };
40 #include "va_surface_internal.h"
41 
42 #include "avcodec.h"
43 
44 struct vlc_va_surface_t {
45     atomic_uintptr_t     refcount;
46 };
47 
DestroyVideoDecoder(vlc_va_t * va,va_pool_t * va_pool)48 static void DestroyVideoDecoder(vlc_va_t *va, va_pool_t *va_pool)
49 {
50     for (unsigned i = 0; i < va_pool->surface_count; i++)
51         va_surface_Release(va_pool->surface[i]->va_surface);
52     va_pool->pf_destroy_surfaces(va);
53     va_pool->surface_count = 0;
54 }
55 
56 /* */
va_pool_SetupDecoder(vlc_va_t * va,va_pool_t * va_pool,const AVCodecContext * avctx,unsigned count,int alignment)57 int va_pool_SetupDecoder(vlc_va_t *va, va_pool_t *va_pool, const AVCodecContext *avctx, unsigned count, int alignment)
58 {
59     int err = VLC_ENOMEM;
60     unsigned i = va_pool->surface_count;
61 
62     if (avctx->coded_width <= 0 || avctx->coded_height <= 0)
63         return VLC_EGENERIC;
64 
65     assert((alignment & (alignment - 1)) == 0); /* power of 2 */
66 #define ALIGN(x, y) (((x) + ((y) - 1)) & ~((y) - 1))
67     int surface_width  = ALIGN(avctx->coded_width,  alignment);
68     int surface_height = ALIGN(avctx->coded_height, alignment);
69 
70     if (avctx->coded_width != surface_width || avctx->coded_height != surface_height)
71         msg_Warn( va, "surface dimensions (%dx%d) differ from avcodec dimensions (%dx%d)",
72                   surface_width, surface_height,
73                   avctx->coded_width, avctx->coded_height);
74 
75     if ( va_pool->surface_count >= count &&
76          va_pool->surface_width == surface_width &&
77          va_pool->surface_height == surface_height )
78     {
79         msg_Dbg(va, "reusing surface pool");
80         err = VLC_SUCCESS;
81         goto done;
82     }
83 
84     /* */
85     DestroyVideoDecoder(va, va_pool);
86 
87     /* */
88     msg_Dbg(va, "va_pool_SetupDecoder id %d %dx%d count: %d", avctx->codec_id, avctx->coded_width, avctx->coded_height, count);
89 
90     if (count > MAX_SURFACE_COUNT)
91         return VLC_EGENERIC;
92 
93     /* FIXME transmit a video_format_t by VaSetup directly */
94     video_format_t fmt;
95     memset(&fmt, 0, sizeof(fmt));
96     fmt.i_width  = surface_width;
97     fmt.i_height = surface_height;
98     fmt.i_frame_rate      = avctx->framerate.num;
99     fmt.i_frame_rate_base = avctx->framerate.den;
100 
101     err = va_pool->pf_create_decoder_surfaces(va, avctx->codec_id, &fmt, count);
102     if (err == VLC_SUCCESS)
103     {
104         va_pool->surface_width  = surface_width;
105         va_pool->surface_height = surface_height;
106     }
107 
108 done:
109     va_pool->surface_count = i;
110     if (err == VLC_SUCCESS)
111         va_pool->pf_setup_avcodec_ctx(va);
112 
113     return err;
114 }
115 
va_pool_SetupSurfaces(vlc_va_t * va,va_pool_t * va_pool,unsigned count)116 int va_pool_SetupSurfaces(vlc_va_t *va, va_pool_t *va_pool, unsigned count)
117 {
118     int err = VLC_ENOMEM;
119     unsigned i = va_pool->surface_count;
120 
121     for (i = 0; i < count; i++) {
122         struct vlc_va_surface_t *p_surface = malloc(sizeof(*p_surface));
123         if (unlikely(p_surface==NULL))
124             goto done;
125         va_pool->surface[i] = va_pool->pf_new_surface_context(va, i);
126         if (unlikely(va_pool->surface[i]==NULL))
127         {
128             free(p_surface);
129             goto done;
130         }
131         va_pool->surface[i]->va_surface = p_surface;
132         atomic_init(&va_pool->surface[i]->va_surface->refcount, 1);
133     }
134     err = VLC_SUCCESS;
135 
136 done:
137     va_pool->surface_count = i;
138     if (err == VLC_SUCCESS)
139         va_pool->pf_setup_avcodec_ctx(va);
140 
141     return err;
142 }
143 
GetSurface(va_pool_t * va_pool)144 static picture_context_t *GetSurface(va_pool_t *va_pool)
145 {
146     for (unsigned i = 0; i < va_pool->surface_count; i++) {
147         struct va_pic_context *surface = va_pool->surface[i];
148         uintptr_t expected = 1;
149 
150         if (atomic_compare_exchange_strong(&surface->va_surface->refcount, &expected, 2))
151         {
152             picture_context_t *field = surface->s.copy(&surface->s);
153             /* the copy should have added an extra reference */
154             atomic_fetch_sub(&surface->va_surface->refcount, 1);
155             return field;
156         }
157     }
158     return NULL;
159 }
160 
va_pool_Get(va_pool_t * va_pool,picture_t * pic)161 int va_pool_Get(va_pool_t *va_pool, picture_t *pic)
162 {
163     unsigned tries = (CLOCK_FREQ + VOUT_OUTMEM_SLEEP) / VOUT_OUTMEM_SLEEP;
164     picture_context_t *field;
165 
166     if (va_pool->surface_count == 0)
167         return VLC_ENOITEM;
168 
169     while ((field = GetSurface(va_pool)) == NULL)
170     {
171         if (--tries == 0)
172             return VLC_ENOITEM;
173         /* Pool empty. Wait for some time as in src/input/decoder.c.
174          * XXX: Both this and the core should use a semaphore or a CV. */
175         msleep(VOUT_OUTMEM_SLEEP);
176     }
177     pic->context = field;
178     return VLC_SUCCESS;
179 }
180 
va_surface_AddRef(vlc_va_surface_t * surface)181 void va_surface_AddRef(vlc_va_surface_t *surface)
182 {
183     atomic_fetch_add(&surface->refcount, 1);
184 }
185 
va_surface_Release(vlc_va_surface_t * surface)186 void va_surface_Release(vlc_va_surface_t *surface)
187 {
188     if (atomic_fetch_sub(&surface->refcount, 1) != 1)
189         return;
190     free(surface);
191 }
192 
va_pool_Close(vlc_va_t * va,va_pool_t * va_pool)193 void va_pool_Close(vlc_va_t *va, va_pool_t *va_pool)
194 {
195     DestroyVideoDecoder(va, va_pool);
196     va_pool->pf_destroy_video_service(va);
197     if (va_pool->pf_destroy_device_manager)
198         va_pool->pf_destroy_device_manager(va);
199     va_pool->pf_destroy_device(va);
200 }
201 
va_pool_Open(vlc_va_t * va,va_pool_t * va_pool)202 int va_pool_Open(vlc_va_t *va, va_pool_t *va_pool)
203 {
204     /* */
205     if (va_pool->pf_create_device(va)) {
206         msg_Err(va, "Failed to create device");
207         goto error;
208     }
209     msg_Dbg(va, "CreateDevice succeed");
210 
211     if (va_pool->pf_create_device_manager &&
212         va_pool->pf_create_device_manager(va) != VLC_SUCCESS) {
213         msg_Err(va, "CreateDeviceManager failed");
214         goto error;
215     }
216 
217     if (va_pool->pf_create_video_service(va)) {
218         msg_Err(va, "CreateVideoService failed");
219         goto error;
220     }
221 
222     return VLC_SUCCESS;
223 
224 error:
225     return VLC_EGENERIC;
226 }
227 
228