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