]> git.sesse.net Git - vlc/blob - src/misc/block.c
9d07f63fa4d96d72734a0b7f5780093735aa862f
[vlc] / src / misc / block.c
1 /*****************************************************************************
2  * block.c: Data blocks management functions
3  *****************************************************************************
4  * Copyright (C) 2003-2004 VideoLAN
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28 #include <stdarg.h>
29
30 #include <vlc/vlc.h>
31 #include "vlc_block.h"
32
33 /* private */
34 struct block_sys_t
35 {
36     vlc_mutex_t lock;
37
38     uint8_t     *p_allocated_buffer;
39     int         i_allocated_buffer;
40
41     vlc_bool_t  b_modify;       /* has it been put in modified state */
42     int         i_duplicated;   /* how many times has the content been
43                                  * duplicated */
44
45 };
46
47 static void BlockRelease( block_t *p_block )
48 {
49     vlc_mutex_lock( &p_block->p_sys->lock );
50
51     p_block->p_sys->i_duplicated--;
52     if( p_block->p_sys->i_duplicated < 0 )
53     {
54         vlc_mutex_unlock( &p_block->p_sys->lock );
55         vlc_mutex_destroy( &p_block->p_sys->lock );
56         free( p_block->p_sys->p_allocated_buffer );
57         free( p_block->p_sys );
58         free( p_block );
59
60         return;
61     }
62
63     vlc_mutex_unlock( &p_block->p_sys->lock );
64     free( p_block );
65 }
66
67 static block_t *__BlockDupContent( block_t *p_block )
68 {
69     block_t *p_dup;
70
71     p_dup = block_New( p_block->p_manager, p_block->i_buffer );
72     memcpy( p_dup->p_buffer, p_block->p_buffer, p_block->i_buffer );
73     p_dup->i_flags         = p_block->i_flags;
74     p_dup->i_pts           = p_block->i_pts;
75     p_dup->i_dts           = p_block->i_dts;
76     p_dup->i_length        = p_block->i_length;
77     p_dup->i_rate          = p_block->i_rate;
78
79     return p_dup;
80 }
81
82 static block_t *BlockModify( block_t *p_block, vlc_bool_t b_will_modify )
83 {
84     block_t *p_mod = p_block;   /* by default */
85
86     vlc_mutex_lock( &p_block->p_sys->lock );
87
88     if( p_block->p_sys->b_modify == b_will_modify )
89     {
90         vlc_mutex_unlock( &p_block->p_sys->lock );
91         return p_block;
92     }
93
94     if( p_block->p_sys->i_duplicated == 0 )
95     {
96         p_block->p_sys->b_modify = b_will_modify;
97         vlc_mutex_unlock( &p_block->p_sys->lock );
98         return p_block;
99     }
100
101     /* FIXME we could avoid that
102      * we just need to create a new p_sys with new mem FIXME */
103     p_mod = __BlockDupContent( p_block );
104     vlc_mutex_unlock( &p_block->p_sys->lock );
105
106     BlockRelease( p_block );
107
108     return p_mod;
109 }
110
111 static block_t *BlockDuplicate( block_t *p_block )
112 {
113     block_t *p_dup;
114
115     vlc_mutex_lock( &p_block->p_sys->lock );
116     if( !p_block->p_sys->b_modify )
117     {
118         p_block->p_sys->i_duplicated++;
119         vlc_mutex_unlock( &p_block->p_sys->lock );
120         p_dup = block_NewEmpty();
121         memcpy( p_dup, p_block, sizeof( block_t ) );
122         p_dup->p_next = NULL;
123         return p_dup;
124     }
125     p_dup = __BlockDupContent( p_block );
126     vlc_mutex_unlock( &p_block->p_sys->lock );
127
128     return p_dup;
129 }
130
131 static block_t *BlockRealloc( block_t *p_block, int i_prebody, int i_body )
132 {
133     int i_buffer_size = i_prebody + i_body;
134
135     if( i_body < 0 || i_buffer_size <= 0 ) return NULL;
136
137     vlc_mutex_lock( &p_block->p_sys->lock );
138
139     if( i_prebody < ( p_block->p_buffer - p_block->p_sys->p_allocated_buffer +
140                       p_block->p_sys->i_allocated_buffer ) ||
141         p_block->p_buffer - i_prebody > p_block->p_sys->p_allocated_buffer )
142     {
143         p_block->p_buffer -= i_prebody;
144         p_block->i_buffer += i_prebody;
145         i_prebody = 0;
146     }
147     if( p_block->p_buffer + i_body < p_block->p_sys->p_allocated_buffer +
148         p_block->p_sys->i_allocated_buffer )
149     {
150         p_block->i_buffer = i_buffer_size;
151         i_body = 0;
152     }
153
154     if( i_body > 0 || i_prebody > 0 )
155     {
156         block_t *p_rea = block_New( p_block->p_manager, i_buffer_size );
157
158         p_rea->i_dts   = p_block->i_dts;
159         p_rea->i_pts   = p_block->i_pts;
160         p_rea->i_flags = p_block->i_flags;
161         p_rea->i_length= p_block->i_length;
162         p_rea->i_rate  = p_block->i_rate;
163
164         memcpy( p_rea->p_buffer + i_prebody, p_block->p_buffer,
165                 __MIN( p_block->i_buffer, p_rea->i_buffer - i_prebody ) );
166
167         vlc_mutex_unlock( &p_block->p_sys->lock );
168         block_Release( p_block );
169
170         return p_rea;
171     }
172
173     vlc_mutex_unlock( &p_block->p_sys->lock );
174
175     return p_block;
176 }
177
178 /*****************************************************************************
179  * Standard block management
180  *
181  *****************************************************************************/
182 /* to be used by other block management */
183 block_t *block_NewEmpty( void )
184 {
185     block_t *p_block;
186
187     p_block = malloc( sizeof( block_t ) );
188     memset( p_block, 0, sizeof( block_t ) );
189
190     p_block->p_next         = NULL;
191     p_block->i_flags        = 0;
192     p_block->i_pts          = 0;
193     p_block->i_dts          = 0;
194     p_block->i_length       = 0;
195     p_block->i_rate         = 0;
196
197     p_block->i_buffer       = 0;
198     p_block->p_buffer       = NULL;
199
200     p_block->pf_release     = NULL;
201     p_block->pf_duplicate   = NULL;
202     p_block->pf_modify      = NULL;
203     p_block->pf_realloc     = NULL;
204
205     p_block->p_manager      = NULL;
206     p_block->p_sys = NULL;
207     return p_block;
208 }
209
210 block_t *__block_New( vlc_object_t *p_obj, int i_size )
211 {
212     block_t     *p_block;
213     block_sys_t *p_sys;
214
215     p_block = block_NewEmpty();
216
217     p_block->pf_release     = BlockRelease;
218     p_block->pf_duplicate   = BlockDuplicate;
219     p_block->pf_modify      = BlockModify;
220     p_block->pf_realloc     = BlockRealloc;
221
222     /* that should be ok (no comunication between multiple p_vlc) */
223     p_block->p_manager      = VLC_OBJECT( p_obj->p_vlc );
224
225     p_block->p_sys = p_sys = malloc( sizeof( block_sys_t ) );
226     vlc_mutex_init( p_obj, &p_sys->lock );
227
228     /* XXX align on 16 and add 32 prebuffer/posbuffer bytes */
229     p_sys->i_allocated_buffer = i_size + 32 + 32 + 16;
230     p_sys->p_allocated_buffer = malloc( p_sys->i_allocated_buffer );
231     p_block->i_buffer         = i_size;
232     p_block->p_buffer         = &p_sys->p_allocated_buffer[32+15-((long)p_sys->p_allocated_buffer % 16 )];
233
234     p_sys->i_duplicated = 0;
235     p_sys->b_modify = VLC_TRUE;
236
237     return p_block;
238 }
239
240 void block_ChainAppend( block_t **pp_list, block_t *p_block )
241 {
242     if( *pp_list == NULL )
243     {
244         *pp_list = p_block;
245     }
246     else
247     {
248         block_t *p = *pp_list;
249
250         while( p->p_next )
251         {
252             p = p->p_next;
253         }
254         p->p_next = p_block;
255     }
256 }
257
258 void block_ChainLastAppend( block_t ***ppp_last, block_t *p_block  )
259 {
260     block_t *p_last = p_block;
261
262     /* Append the block */
263     **ppp_last = p_block;
264
265     /* Update last pointer */
266     while( p_last->p_next ) p_last = p_last->p_next;
267     *ppp_last = &p_last->p_next;
268 }
269
270 void block_ChainRelease( block_t *p_block )
271 {
272     while( p_block )
273     {
274         block_t *p_next;
275         p_next = p_block->p_next;
276         p_block->pf_release( p_block );
277         p_block = p_next;
278     }
279 }
280
281 int block_ChainExtract( block_t *p_list, void *p_data, int i_max )
282 {
283     block_t *b;
284     int     i_total = 0;
285     uint8_t *p = p_data;
286
287     for( b = p_list; b != NULL; b = b->p_next )
288     {
289         int i_copy;
290
291         i_copy = __MIN( i_max, b->i_buffer );
292         if( i_copy > 0 )
293         {
294             memcpy( p, b->p_buffer, i_copy );
295             i_max   -= i_copy;
296             i_total += i_copy;
297             p       += i_copy;
298
299             if( i_max == 0 )
300             {
301                 return i_total;
302             }
303         }
304     }
305     return i_total;
306 }
307
308 block_t *block_ChainGather( block_t *p_list )
309 {
310     int     i_total = 0;
311     block_t *b, *g;
312
313     if( p_list->p_next == NULL )
314     {
315         /* only one, so no need */
316         return p_list;
317     }
318
319     for( b = p_list; b != NULL; b = b->p_next )
320     {
321         i_total += b->i_buffer;
322     }
323
324     g = block_New( p_list->p_manager, i_total );
325     block_ChainExtract( p_list, g->p_buffer, g->i_buffer );
326
327     g->i_flags = p_list->i_flags;
328     g->i_pts   = p_list->i_pts;
329     g->i_dts   = p_list->i_dts;
330
331     /* free p_list */
332     block_ChainRelease( p_list );
333     return g;
334 }
335
336 /*****************************************************************************
337  * block_fifo_t management
338  *****************************************************************************/
339 block_fifo_t *__block_FifoNew( vlc_object_t *p_obj )
340 {
341     block_fifo_t *p_fifo;
342
343     p_fifo = malloc( sizeof( vlc_object_t ) );
344     vlc_mutex_init( p_obj, &p_fifo->lock );
345     vlc_cond_init( p_obj, &p_fifo->wait );
346     p_fifo->i_depth = 0;
347     p_fifo->p_first = NULL;
348     p_fifo->pp_last = &p_fifo->p_first;
349
350     return p_fifo;
351 }
352
353 void block_FifoRelease( block_fifo_t *p_fifo )
354 {
355     block_FifoEmpty( p_fifo );
356     vlc_cond_destroy( &p_fifo->wait );
357     vlc_mutex_destroy( &p_fifo->lock );
358     free( p_fifo );
359 }
360
361 void block_FifoEmpty( block_fifo_t *p_fifo )
362 {
363     block_t *b;
364
365     vlc_mutex_lock( &p_fifo->lock );
366     for( b = p_fifo->p_first; b != NULL; )
367     {
368         block_t *p_next;
369
370         p_next = b->p_next;
371         block_Release( b );
372         b = p_next;
373     }
374
375     p_fifo->i_depth = 0;
376     p_fifo->p_first = NULL;
377     p_fifo->pp_last = &p_fifo->p_first;
378     vlc_mutex_unlock( &p_fifo->lock );
379 }
380
381 int block_FifoPut( block_fifo_t *p_fifo, block_t *p_block )
382 {
383     int i_size = 0;
384     vlc_mutex_lock( &p_fifo->lock );
385
386     do
387     {
388         i_size += p_block->i_buffer;
389
390         *p_fifo->pp_last = p_block;
391         p_fifo->pp_last = &p_block->p_next;
392         p_fifo->i_depth++;
393
394         p_block = p_block->p_next;
395
396     } while( p_block );
397
398     /* warn there is data in this fifo */
399     vlc_cond_signal( &p_fifo->wait );
400     vlc_mutex_unlock( &p_fifo->lock );
401
402     return i_size;
403 }
404
405 block_t *block_FifoGet( block_fifo_t *p_fifo )
406 {
407     block_t *b;
408
409     vlc_mutex_lock( &p_fifo->lock );
410
411     /* We do a while here because there is a race condition in the
412      * win32 implementation of vlc_cond_wait() (We can't be sure the fifo
413      * hasn't been emptied again since we were signaled). */
414     while( p_fifo->p_first == NULL )
415     {
416         vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
417     }
418
419     b = p_fifo->p_first;
420
421     p_fifo->p_first = b->p_next;
422     p_fifo->i_depth--;
423
424     if( p_fifo->p_first == NULL )
425     {
426         p_fifo->pp_last = &p_fifo->p_first;
427     }
428
429     vlc_mutex_unlock( &p_fifo->lock );
430
431     b->p_next = NULL;
432     return( b );
433 }
434
435 block_t *block_FifoShow( block_fifo_t *p_fifo )
436 {
437     block_t *b;
438
439     vlc_mutex_lock( &p_fifo->lock );
440
441     if( p_fifo->p_first == NULL )
442     {
443         vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
444     }
445
446     b = p_fifo->p_first;
447
448     vlc_mutex_unlock( &p_fifo->lock );
449
450     return( b );
451
452 }
453