]> git.sesse.net Git - vlc/blob - src/misc/block.c
Add some comments
[vlc] / src / misc / block.c
1 /*****************************************************************************
2  * block.c: Data blocks management functions
3  *****************************************************************************
4  * Copyright (C) 2003-2004 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc/vlc.h>
32 #include "vlc_block.h"
33
34 /*****************************************************************************
35  * Block functions.
36  *****************************************************************************/
37 /* private */
38 struct block_sys_t
39 {
40     block_t     self;
41     size_t      i_allocated_buffer;
42     uint8_t     p_allocated_buffer[0];
43 };
44
45 #ifndef NDEBUG
46 static void BlockNoRelease( block_t *b )
47 {
48     fprintf( stderr, "block %p has no release callback! This is a bug!\n", b );
49     abort();
50 }
51 #endif
52
53 void block_Init( block_t *restrict b, void *buf, size_t size )
54 {
55     /* Fill all fields to their default */
56     b->p_next = b->p_prev = NULL;
57     b->i_flags = 0;
58     b->i_pts = b->i_dts = b->i_length = 0;
59     b->i_rate = 0;
60     b->p_buffer = buf;
61     b->i_buffer = size;
62 #ifndef NDEBUG
63     b->pf_release = BlockNoRelease;
64 #endif
65 }
66
67 static void BlockRelease( block_t *p_block )
68 {
69     free( p_block );
70 }
71
72 #define BLOCK_PADDING_SIZE 32
73
74 block_t *block_Alloc( size_t i_size )
75 {
76     /* We do only one malloc
77      * TODO bench if doing 2 malloc but keeping a pool of buffer is better
78      * 16 -> align on 16
79      * 2 * BLOCK_PADDING_SIZE -> pre + post padding
80      */
81     const size_t i_alloc = i_size + 2 * BLOCK_PADDING_SIZE + 16;
82     block_sys_t *p_sys = malloc( sizeof( *p_sys ) + i_alloc );
83
84     if( p_sys == NULL )
85         return NULL;
86
87     /* Fill opaque data */
88     p_sys->i_allocated_buffer = i_alloc;
89
90     block_Init( &p_sys->self, p_sys->p_allocated_buffer + BLOCK_PADDING_SIZE
91                 + 16 - ((uintptr_t)p_sys->p_allocated_buffer % 16 ), i_size );
92     p_sys->self.pf_release    = BlockRelease;
93
94     return &p_sys->self;
95 }
96
97 block_t *block_Realloc( block_t *p_block, ssize_t i_prebody, size_t i_body )
98 {
99     block_sys_t *p_sys = (block_sys_t *)p_block;
100     ssize_t i_buffer_size;
101
102     if( p_block->pf_release != BlockRelease )
103     {
104         /* Special case when pf_release if overloaded
105          * TODO if used one day, then implement it in a smarter way */
106         block_t *p_dup = block_Duplicate( p_block );
107         block_Release( p_block );
108         if( !p_dup )
109             return NULL;
110
111         p_block = p_dup;
112     }
113
114     i_buffer_size = i_prebody + i_body;
115
116     if( i_buffer_size <= 0 )
117     {
118         block_Release( p_block );
119         return NULL;
120     }
121
122     /* Adjust reserved header if there is enough room */
123     if( p_block->p_buffer - i_prebody > p_sys->p_allocated_buffer &&
124         p_block->p_buffer - i_prebody < p_sys->p_allocated_buffer +
125         p_sys->i_allocated_buffer )
126     {
127         p_block->p_buffer -= i_prebody;
128         p_block->i_buffer += i_prebody;
129         i_prebody = 0;
130     }
131
132     /* Adjust payload size if there is enough room */
133     if( p_block->p_buffer + i_body < p_sys->p_allocated_buffer +
134         p_sys->i_allocated_buffer )
135     {
136         p_block->i_buffer = i_buffer_size;
137         i_body = 0;
138     }
139
140     /* Not enough room, reallocate the buffer */
141     if( i_body > 0 || i_prebody > 0 )
142     {
143         /* FIXME: this is really dumb, we should use realloc() */
144         block_t *p_rea = block_New( NULL, i_buffer_size );
145
146         if( p_rea )
147         {
148             p_rea->i_dts     = p_block->i_dts;
149             p_rea->i_pts     = p_block->i_pts;
150             p_rea->i_flags   = p_block->i_flags;
151             p_rea->i_length  = p_block->i_length;
152             p_rea->i_rate    = p_block->i_rate;
153             p_rea->i_samples = p_block->i_samples;
154
155             memcpy( p_rea->p_buffer + i_prebody, p_block->p_buffer,
156                     __MIN( p_block->i_buffer, p_rea->i_buffer - i_prebody ) );
157         }
158
159         block_Release( p_block );
160
161         return p_rea;
162     }
163
164     return p_block;
165 }
166
167 #ifdef HAVE_MMAP
168 # include <sys/mman.h>
169
170 typedef struct block_mmap_t
171 {
172     block_t     self;
173     void       *base_addr;
174     size_t      length;
175 } block_mmap_t;
176
177 static void block_mmap_Release (block_t *block)
178 {
179     block_mmap_t *p_sys = (block_mmap_t *)block;
180
181     munmap (p_sys->base_addr, p_sys->length);
182     free (p_sys);
183 }
184
185 block_t *block_mmap_Alloc (void *addr, size_t length)
186 {
187     if (addr == MAP_FAILED)
188         return NULL;
189
190     block_mmap_t *block = malloc (sizeof (*block));
191     if (block == NULL)
192     {
193         munmap (addr, length);
194         return NULL;
195     }
196
197     block_Init (&block->self, (uint8_t *)addr, length);
198     block->self.pf_release = block_mmap_Release;
199     block->base_addr = addr;
200     block->length = length;
201     return &block->self;
202 }
203 #endif
204
205 /*****************************************************************************
206  * block_fifo_t management
207  *****************************************************************************/
208 struct block_fifo_t
209 {
210     vlc_mutex_t         lock;                         /* fifo data lock */
211     vlc_cond_t          wait;         /* fifo data conditional variable */
212
213     block_t             *p_first;
214     block_t             **pp_last;
215     size_t              i_depth;
216     size_t              i_size;
217     vlc_bool_t          b_force_wake;
218 };
219
220 block_fifo_t *__block_FifoNew( vlc_object_t *p_obj )
221 {
222     (void)p_obj;
223
224     block_fifo_t *p_fifo;
225
226     p_fifo = malloc( sizeof( block_fifo_t ) );
227     if( !p_fifo ) return NULL;
228     vlc_mutex_init( p_obj, &p_fifo->lock );
229     vlc_cond_init( p_obj, &p_fifo->wait );
230     p_fifo->p_first = NULL;
231     p_fifo->pp_last = &p_fifo->p_first;
232     p_fifo->i_depth = p_fifo->i_size = 0;
233     p_fifo->b_force_wake = VLC_FALSE;
234
235     return p_fifo;
236 }
237
238 void block_FifoRelease( block_fifo_t *p_fifo )
239 {
240     block_FifoEmpty( p_fifo );
241     vlc_cond_destroy( &p_fifo->wait );
242     vlc_mutex_destroy( &p_fifo->lock );
243     free( p_fifo );
244 }
245
246 void block_FifoEmpty( block_fifo_t *p_fifo )
247 {
248     block_t *b;
249
250     vlc_mutex_lock( &p_fifo->lock );
251     for( b = p_fifo->p_first; b != NULL; )
252     {
253         block_t *p_next;
254
255         p_next = b->p_next;
256         block_Release( b );
257         b = p_next;
258     }
259
260     p_fifo->i_depth = p_fifo->i_size = 0;
261     p_fifo->p_first = NULL;
262     p_fifo->pp_last = &p_fifo->p_first;
263     vlc_mutex_unlock( &p_fifo->lock );
264 }
265
266 size_t block_FifoPut( block_fifo_t *p_fifo, block_t *p_block )
267 {
268     size_t i_size = 0;
269     vlc_mutex_lock( &p_fifo->lock );
270
271     do
272     {
273         i_size += p_block->i_buffer;
274
275         *p_fifo->pp_last = p_block;
276         p_fifo->pp_last = &p_block->p_next;
277         p_fifo->i_depth++;
278         p_fifo->i_size += p_block->i_buffer;
279
280         p_block = p_block->p_next;
281
282     } while( p_block );
283
284     /* warn there is data in this fifo */
285     vlc_cond_signal( &p_fifo->wait );
286     vlc_mutex_unlock( &p_fifo->lock );
287
288     return i_size;
289 }
290
291 void block_FifoWake( block_fifo_t *p_fifo )
292 {
293     vlc_mutex_lock( &p_fifo->lock );
294     if( p_fifo->p_first == NULL )
295         p_fifo->b_force_wake = VLC_TRUE;
296     vlc_cond_signal( &p_fifo->wait );
297     vlc_mutex_unlock( &p_fifo->lock );
298 }
299
300 block_t *block_FifoGet( block_fifo_t *p_fifo )
301 {
302     block_t *b;
303
304     vlc_mutex_lock( &p_fifo->lock );
305
306     /* Remember vlc_cond_wait() may cause spurious wakeups
307      * (on both Win32 and POSIX) */
308     while( ( p_fifo->p_first == NULL ) && !p_fifo->b_force_wake )
309     {
310         vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
311     }
312
313     b = p_fifo->p_first;
314
315     p_fifo->b_force_wake = VLC_FALSE;
316     if( b == NULL )
317     {
318         /* Forced wakeup */
319         vlc_mutex_unlock( &p_fifo->lock );
320         return NULL;
321     }
322
323     p_fifo->p_first = b->p_next;
324     p_fifo->i_depth--;
325     p_fifo->i_size -= b->i_buffer;
326
327     if( p_fifo->p_first == NULL )
328     {
329         p_fifo->pp_last = &p_fifo->p_first;
330     }
331
332     vlc_mutex_unlock( &p_fifo->lock );
333
334     b->p_next = NULL;
335     return b;
336 }
337
338 block_t *block_FifoShow( block_fifo_t *p_fifo )
339 {
340     block_t *b;
341
342     vlc_mutex_lock( &p_fifo->lock );
343
344     if( p_fifo->p_first == NULL )
345     {
346         vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
347     }
348
349     b = p_fifo->p_first;
350
351     vlc_mutex_unlock( &p_fifo->lock );
352
353     return( b );
354 }
355
356 size_t block_FifoSize( const block_fifo_t *p_fifo )
357 {
358     return p_fifo->i_size;
359 }
360
361 size_t block_FifoCount( const block_fifo_t *p_fifo )
362 {
363     return p_fifo->i_depth;
364 }