]> git.sesse.net Git - vlc/blob - src/misc/block.c
Add fourcc for TSC2
[vlc] / src / misc / block.c
1 /*****************************************************************************
2  * block.c: Data blocks management functions
3  *****************************************************************************
4  * Copyright (C) 2003-2004 VLC authors and VideoLAN
5  * Copyright (C) 2007-2009 RĂ©mi Denis-Courmont
6  *
7  * Authors: Laurent Aimar <fenrir@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * 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 <sys/stat.h>
32 #include <assert.h>
33 #include <errno.h>
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37
38 #include <vlc_common.h>
39 #include <vlc_block.h>
40 #include <vlc_fs.h> /* For 64-bits lseek() definition */
41
42 /**
43  * @section Block handling functions.
44  */
45
46 #ifndef NDEBUG
47 static void BlockNoRelease( block_t *b )
48 {
49     fprintf( stderr, "block %p has no release callback! This is a bug!\n", b );
50     abort();
51 }
52
53 static void block_Check (block_t *block)
54 {
55     while (block != NULL)
56     {
57         unsigned char *start = block->p_start;
58         unsigned char *end = block->p_start + block->i_size;
59         unsigned char *bufstart = block->p_buffer;
60         unsigned char *bufend = block->p_buffer + block->i_buffer;
61
62         assert (block->pf_release != BlockNoRelease);
63         assert (start <= end);
64         assert (bufstart <= bufend);
65         assert (bufstart >= start);
66         assert (bufend <= end);
67
68         block = block->p_next;
69     }
70 }
71
72 static void block_Invalidate (block_t *block)
73 {
74     block->p_next = NULL;
75     block_Check (block);
76     block->pf_release = BlockNoRelease;
77     barrier (); /* prevent compiler from optimizing this assignment out */
78 }
79 #else
80 # define block_Check(b) ((void)(b))
81 # define block_Invalidate(b) ((void)(b))
82 #endif
83
84 void block_Init( block_t *restrict b, void *buf, size_t size )
85 {
86     /* Fill all fields to their default */
87     b->p_next = NULL;
88     b->p_buffer = buf;
89     b->i_buffer = size;
90     b->p_start = buf;
91     b->i_size = size;
92     b->i_flags = 0;
93     b->i_nb_samples = 0;
94     b->i_pts =
95     b->i_dts = VLC_TS_INVALID;
96     b->i_length = 0;
97 #ifndef NDEBUG
98     b->pf_release = BlockNoRelease;
99 #endif
100 }
101
102 static void block_generic_Release (block_t *block)
103 {
104     /* That is always true for blocks allocated with block_Alloc(). */
105     assert (block->p_start == (unsigned char *)(block + 1));
106     block_Invalidate (block);
107     free (block);
108 }
109
110 static void BlockMetaCopy( block_t *restrict out, const block_t *in )
111 {
112     out->p_next    = in->p_next;
113     out->i_nb_samples = in->i_nb_samples;
114     out->i_dts     = in->i_dts;
115     out->i_pts     = in->i_pts;
116     out->i_flags   = in->i_flags;
117     out->i_length  = in->i_length;
118 }
119
120 /* Memory alignment (must be a multiple of sizeof(void*) and a power of two) */
121 #define BLOCK_ALIGN        16
122 /* Initial reserved header and footer size (must be multiple of alignment) */
123 #define BLOCK_PADDING      32
124 /* Maximum size of reserved footer before we release with realloc() */
125 #define BLOCK_WASTE_SIZE   2048
126
127 block_t *block_Alloc (size_t size)
128 {
129     /* 2 * BLOCK_PADDING: pre + post padding */
130     const size_t alloc = sizeof (block_t) + BLOCK_ALIGN + (2 * BLOCK_PADDING)
131                        + size;
132     if (unlikely(alloc <= size))
133         return NULL;
134
135     block_t *b = malloc (alloc);
136     if (unlikely(b == NULL))
137         return NULL;
138
139     block_Init (b, b + 1, alloc - sizeof (*b));
140     static_assert ((BLOCK_PADDING % BLOCK_ALIGN) == 0,
141                    "BLOCK_PADDING must be a multiple of BLOCK_ALIGN");
142     b->p_buffer += BLOCK_PADDING + BLOCK_ALIGN - 1;
143     b->p_buffer = (void *)(((uintptr_t)b->p_buffer) & ~(BLOCK_ALIGN - 1));
144     b->i_buffer = size;
145     b->pf_release = block_generic_Release;
146     return b;
147 }
148
149 block_t *block_Realloc( block_t *p_block, ssize_t i_prebody, size_t i_body )
150 {
151     size_t requested = i_prebody + i_body;
152
153     block_Check( p_block );
154
155     /* Corner case: empty block requested */
156     if( i_prebody <= 0 && i_body <= (size_t)(-i_prebody) )
157     {
158         block_Release( p_block );
159         return NULL;
160     }
161
162     assert( p_block->p_start <= p_block->p_buffer );
163     assert( p_block->p_start + p_block->i_size
164                                     >= p_block->p_buffer + p_block->i_buffer );
165
166     /* Corner case: the current payload is discarded completely */
167     if( i_prebody <= 0 && p_block->i_buffer <= (size_t)-i_prebody )
168          p_block->i_buffer = 0; /* discard current payload */
169     if( p_block->i_buffer == 0 )
170     {
171         if( requested <= p_block->i_size )
172         {   /* Enough room: recycle buffer */
173             size_t extra = p_block->i_size - requested;
174
175             p_block->p_buffer = p_block->p_start + (extra / 2);
176             p_block->i_buffer = requested;
177             return p_block;
178         }
179         /* Not enough room: allocate a new buffer */
180         block_t *p_rea = block_Alloc( requested );
181         if( p_rea )
182             BlockMetaCopy( p_rea, p_block );
183         block_Release( p_block );
184         return p_rea;
185     }
186
187     /* First, shrink payload */
188
189     /* Pull payload start */
190     if( i_prebody < 0 )
191     {
192         assert( p_block->i_buffer >= (size_t)-i_prebody );
193         p_block->p_buffer -= i_prebody;
194         p_block->i_buffer += i_prebody;
195         i_body += i_prebody;
196         i_prebody = 0;
197     }
198
199     /* Trim payload end */
200     if( p_block->i_buffer > i_body )
201         p_block->i_buffer = i_body;
202
203     uint8_t *p_start = p_block->p_start;
204     uint8_t *p_end = p_start + p_block->i_size;
205
206     /* Second, reallocate the buffer if we lack space. This is done now to
207      * minimize the payload size for memory copy. */
208     assert( i_prebody >= 0 );
209     if( (size_t)(p_block->p_buffer - p_start) < (size_t)i_prebody
210      || (size_t)(p_end - p_block->p_buffer) < i_body )
211     {
212         block_t *p_rea = block_Alloc( requested );
213         if( p_rea )
214         {
215             BlockMetaCopy( p_rea, p_block );
216             p_rea->p_buffer += i_prebody;
217             p_rea->i_buffer -= i_prebody;
218             memcpy( p_rea->p_buffer, p_block->p_buffer, p_block->i_buffer );
219         }
220         block_Release( p_block );
221         if( p_rea == NULL )
222             return NULL;
223         p_block = p_rea;
224     }
225     else
226     /* We have a very large reserved footer now? Release some of it.
227      * XXX it might not preserve the alignment of p_buffer */
228     if( p_end - (p_block->p_buffer + i_body) > BLOCK_WASTE_SIZE )
229     {
230         block_t *p_rea = block_Alloc( requested );
231         if( p_rea )
232         {
233             BlockMetaCopy( p_rea, p_block );
234             p_rea->p_buffer += i_prebody;
235             p_rea->i_buffer -= i_prebody;
236             memcpy( p_rea->p_buffer, p_block->p_buffer, p_block->i_buffer );
237             block_Release( p_block );
238             p_block = p_rea;
239         }
240     }
241
242     /* NOTE: p_start and p_end are corrupted from this point */
243
244     /* Third, expand payload */
245
246     /* Push payload start */
247     if( i_prebody > 0 )
248     {
249         p_block->p_buffer -= i_prebody;
250         p_block->i_buffer += i_prebody;
251         i_body += i_prebody;
252         i_prebody = 0;
253     }
254
255     /* Expand payload to requested size */
256     p_block->i_buffer = i_body;
257
258     return p_block;
259 }
260
261
262 static void block_heap_Release (block_t *block)
263 {
264     block_Invalidate (block);
265     free (block->p_start);
266     free (block);
267 }
268
269 /**
270  * Creates a block from a heap allocation.
271  * This is provided by LibVLC so that manually heap-allocated blocks can safely
272  * be deallocated even after the origin plugin has been unloaded from memory.
273  *
274  * When block_Release() is called, VLC will free() the specified pointer.
275  *
276  * @param ptr base address of the heap allocation (will be free()'d)
277  * @param length bytes length of the heap allocation
278  * @return NULL in case of error (ptr free()'d in that case), or a valid
279  * block_t pointer.
280  */
281 block_t *block_heap_Alloc (void *addr, size_t length)
282 {
283     block_t *block = malloc (sizeof (*block));
284     if (block == NULL)
285     {
286         free (addr);
287         return NULL;
288     }
289
290     block_Init (block, addr, length);
291     block->pf_release = block_heap_Release;
292     return block;
293 }
294
295 #ifdef HAVE_MMAP
296 # include <sys/mman.h>
297
298 static void block_mmap_Release (block_t *block)
299 {
300     block_Invalidate (block);
301     munmap (block->p_start, block->i_size);
302     free (block);
303 }
304
305 /**
306  * Creates a block from a virtual address memory mapping (mmap).
307  * This is provided by LibVLC so that mmap blocks can safely be deallocated
308  * even after the allocating plugin has been unloaded from memory.
309  *
310  * @param addr base address of the mapping (as returned by mmap)
311  * @param length length (bytes) of the mapping (as passed to mmap)
312  * @return NULL if addr is MAP_FAILED, or an error occurred (in the later
313  * case, munmap(addr, length) is invoked before returning).
314  */
315 block_t *block_mmap_Alloc (void *addr, size_t length)
316 {
317     if (addr == MAP_FAILED)
318         return NULL;
319
320     block_t *block = malloc (sizeof (*block));
321     if (block == NULL)
322     {
323         munmap (addr, length);
324         return NULL;
325     }
326
327     block_Init (block, addr, length);
328     block->pf_release = block_mmap_Release;
329     return block;
330 }
331 #else
332 block_t *block_mmap_Alloc (void *addr, size_t length)
333 {
334     (void)addr; (void)length; return NULL;
335 }
336 #endif
337
338
339 #ifdef WIN32
340 # include <io.h>
341 # ifdef UNDER_CE
342 #  define _get_osfhandle(a) ((long) (a))
343 # endif
344
345 static
346 ssize_t pread (int fd, void *buf, size_t count, off_t offset)
347 {
348     HANDLE handle = (HANDLE)(intptr_t)_get_osfhandle (fd);
349     if (handle == INVALID_HANDLE_VALUE)
350         return -1;
351
352     OVERLAPPED olap; olap.Offset = offset; olap.OffsetHigh = (offset >> 32);
353     DWORD written;
354     /* This braindead API will override the file pointer even if we specify
355      * an explicit read offset... So do not expect this to mix well with
356      * regular read() calls. */
357     if (ReadFile (handle, buf, count, &written, &olap))
358         return written;
359     return -1;
360 }
361 #elif !defined( HAVE_PREAD )
362 static
363 ssize_t pread(int fd, const void * buf, size_t size, off_t offset) {
364     off_t offs0;
365     ssize_t rd;
366     if ((offs0 = lseek(fd, 0, SEEK_CUR)) == (off_t)-1) return -1;
367     if (lseek(fd, offset, SEEK_SET) == (off_t)-1) return -1;
368     rd = read(fd, (void *)buf, size);
369     if (lseek(fd, offs0, SEEK_SET) == (off_t)-1) return -1;
370     return rd;
371 }
372
373 static
374 ssize_t pwrite(int fd, const void * buf, size_t size, off_t offset) {
375     off_t offs0;
376     ssize_t wr;
377     if ((offs0 = lseek(fd, 0, SEEK_CUR)) == (off_t)-1) return -1;
378     if (lseek(fd, offset, SEEK_SET) == (off_t)-1) return -1;
379     wr = write(fd, (void *)buf, size);
380     if (lseek(fd, offs0, SEEK_SET) == (off_t)-1) return -1;
381     return wr;
382 }
383 #endif
384
385 /**
386  * Loads a file into a block of memory. If possible a private file mapping is
387  * created. Otherwise, the file is read normally. On 32-bits platforms, this
388  * function will not work for very large files, due to memory space
389  * constraints. Cancellation point.
390  *
391  * @param fd file descriptor to load from
392  * @return a new block with the file content at p_buffer, and file length at
393  * i_buffer (release it with block_Release()), or NULL upon error (see errno).
394  */
395 block_t *block_File (int fd)
396 {
397     size_t length;
398     struct stat st;
399
400     /* First, get the file size */
401     if (fstat (fd, &st))
402         return NULL;
403
404     /* st_size is meaningful for regular files, shared memory and typed memory.
405      * It's also meaning for symlinks, but that's not possible with fstat().
406      * In other cases, it's undefined, and we should really not go further. */
407 #ifndef S_TYPEISSHM
408 # define S_TYPEISSHM( buf ) (0)
409 #endif
410     if (S_ISDIR (st.st_mode))
411     {
412         errno = EISDIR;
413         return NULL;
414     }
415     if (!S_ISREG (st.st_mode) && !S_TYPEISSHM (&st))
416     {
417         errno = ESPIPE;
418         return NULL;
419     }
420
421     /* Prevent an integer overflow in mmap() and malloc() */
422     if (st.st_size >= SIZE_MAX)
423     {
424         errno = ENOMEM;
425         return NULL;
426     }
427     length = (size_t)st.st_size;
428
429 #ifdef HAVE_MMAP
430     if (length > 0)
431     {
432         void *addr;
433
434         addr = mmap (NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
435         if (addr != MAP_FAILED)
436             return block_mmap_Alloc (addr, length);
437     }
438 #endif
439
440     /* If mmap() is not implemented by the OS _or_ the filesystem... */
441     block_t *block = block_Alloc (length);
442     if (block == NULL)
443         return NULL;
444     block_cleanup_push (block);
445
446     for (size_t i = 0; i < length;)
447     {
448         ssize_t len = pread (fd, block->p_buffer + i, length - i, i);
449         if (len == -1)
450         {
451             block_Release (block);
452             block = NULL;
453             break;
454         }
455         i += len;
456     }
457     vlc_cleanup_pop ();
458     return block;
459 }
460
461 /**
462  * @section Thread-safe block queue functions
463  */
464
465 /**
466  * Internal state for block queues
467  */
468 struct block_fifo_t
469 {
470     vlc_mutex_t         lock;                         /* fifo data lock */
471     vlc_cond_t          wait;      /**< Wait for data */
472     vlc_cond_t          wait_room; /**< Wait for queue depth to shrink */
473
474     block_t             *p_first;
475     block_t             **pp_last;
476     size_t              i_depth;
477     size_t              i_size;
478     bool          b_force_wake;
479 };
480
481 block_fifo_t *block_FifoNew( void )
482 {
483     block_fifo_t *p_fifo = malloc( sizeof( block_fifo_t ) );
484     if( !p_fifo )
485         return NULL;
486
487     vlc_mutex_init( &p_fifo->lock );
488     vlc_cond_init( &p_fifo->wait );
489     vlc_cond_init( &p_fifo->wait_room );
490     p_fifo->p_first = NULL;
491     p_fifo->pp_last = &p_fifo->p_first;
492     p_fifo->i_depth = p_fifo->i_size = 0;
493     p_fifo->b_force_wake = false;
494
495     return p_fifo;
496 }
497
498 void block_FifoRelease( block_fifo_t *p_fifo )
499 {
500     block_FifoEmpty( p_fifo );
501     vlc_cond_destroy( &p_fifo->wait_room );
502     vlc_cond_destroy( &p_fifo->wait );
503     vlc_mutex_destroy( &p_fifo->lock );
504     free( p_fifo );
505 }
506
507 void block_FifoEmpty( block_fifo_t *p_fifo )
508 {
509     block_t *block;
510
511     vlc_mutex_lock( &p_fifo->lock );
512     block = p_fifo->p_first;
513     if (block != NULL)
514     {
515         p_fifo->i_depth = p_fifo->i_size = 0;
516         p_fifo->p_first = NULL;
517         p_fifo->pp_last = &p_fifo->p_first;
518     }
519     vlc_cond_broadcast( &p_fifo->wait_room );
520     vlc_mutex_unlock( &p_fifo->lock );
521
522     while (block != NULL)
523     {
524         block_t *buf;
525
526         buf = block->p_next;
527         block_Release (block);
528         block = buf;
529     }
530 }
531
532 /**
533  * Wait until the FIFO gets below a certain size (if needed).
534  *
535  * Note that if more than one thread writes to the FIFO, you cannot assume that
536  * the FIFO is actually below the requested size upon return (since another
537  * thread could have refilled it already). This is typically not an issue, as
538  * this function is meant for (relaxed) congestion control.
539  *
540  * This function may be a cancellation point and it is cancel-safe.
541  *
542  * @param fifo queue to wait on
543  * @param max_depth wait until the queue has no more than this many blocks
544  *                  (use SIZE_MAX to ignore this constraint)
545  * @param max_size wait until the queue has no more than this many bytes
546  *                  (use SIZE_MAX to ignore this constraint)
547  * @return nothing.
548  */
549 void block_FifoPace (block_fifo_t *fifo, size_t max_depth, size_t max_size)
550 {
551     vlc_testcancel ();
552
553     vlc_mutex_lock (&fifo->lock);
554     while ((fifo->i_depth > max_depth) || (fifo->i_size > max_size))
555     {
556          mutex_cleanup_push (&fifo->lock);
557          vlc_cond_wait (&fifo->wait_room, &fifo->lock);
558          vlc_cleanup_pop ();
559     }
560     vlc_mutex_unlock (&fifo->lock);
561 }
562
563 /**
564  * Immediately queue one block at the end of a FIFO.
565  * @param fifo queue
566  * @param block head of a block list to queue (may be NULL)
567  * @return total number of bytes appended to the queue
568  */
569 size_t block_FifoPut( block_fifo_t *p_fifo, block_t *p_block )
570 {
571     size_t i_size = 0, i_depth = 0;
572     block_t *p_last;
573
574     if (p_block == NULL)
575         return 0;
576     for (p_last = p_block; ; p_last = p_last->p_next)
577     {
578         i_size += p_last->i_buffer;
579         i_depth++;
580         if (!p_last->p_next)
581             break;
582     }
583
584     vlc_mutex_lock (&p_fifo->lock);
585     *p_fifo->pp_last = p_block;
586     p_fifo->pp_last = &p_last->p_next;
587     p_fifo->i_depth += i_depth;
588     p_fifo->i_size += i_size;
589     /* We queued at least one block: wake up one read-waiting thread */
590     vlc_cond_signal( &p_fifo->wait );
591     vlc_mutex_unlock( &p_fifo->lock );
592
593     return i_size;
594 }
595
596 void block_FifoWake( block_fifo_t *p_fifo )
597 {
598     vlc_mutex_lock( &p_fifo->lock );
599     if( p_fifo->p_first == NULL )
600         p_fifo->b_force_wake = true;
601     vlc_cond_broadcast( &p_fifo->wait );
602     vlc_mutex_unlock( &p_fifo->lock );
603 }
604
605 /**
606  * Dequeue the first block from the FIFO. If necessary, wait until there is
607  * one block in the queue. This function is (always) cancellation point.
608  *
609  * @return a valid block, or NULL if block_FifoWake() was called.
610  */
611 block_t *block_FifoGet( block_fifo_t *p_fifo )
612 {
613     block_t *b;
614
615     vlc_testcancel( );
616
617     vlc_mutex_lock( &p_fifo->lock );
618     mutex_cleanup_push( &p_fifo->lock );
619
620     /* Remember vlc_cond_wait() may cause spurious wakeups
621      * (on both Win32 and POSIX) */
622     while( ( p_fifo->p_first == NULL ) && !p_fifo->b_force_wake )
623         vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
624
625     vlc_cleanup_pop();
626     b = p_fifo->p_first;
627
628     p_fifo->b_force_wake = false;
629     if( b == NULL )
630     {
631         /* Forced wakeup */
632         vlc_mutex_unlock( &p_fifo->lock );
633         return NULL;
634     }
635
636     p_fifo->p_first = b->p_next;
637     p_fifo->i_depth--;
638     p_fifo->i_size -= b->i_buffer;
639
640     if( p_fifo->p_first == NULL )
641     {
642         p_fifo->pp_last = &p_fifo->p_first;
643     }
644
645     /* We don't know how many threads can queue new packets now. */
646     vlc_cond_broadcast( &p_fifo->wait_room );
647     vlc_mutex_unlock( &p_fifo->lock );
648
649     b->p_next = NULL;
650     return b;
651 }
652
653 /**
654  * Peeks the first block in the FIFO.
655  * If necessary, wait until there is one block.
656  * This function is (always) a cancellation point.
657  *
658  * @warning This function leaves the block in the FIFO.
659  * You need to protect against concurrent threads who could dequeue the block.
660  * Preferrably, there should be only one thread reading from the FIFO.
661  *
662  * @return a valid block.
663  */
664 block_t *block_FifoShow( block_fifo_t *p_fifo )
665 {
666     block_t *b;
667
668     vlc_testcancel( );
669
670     vlc_mutex_lock( &p_fifo->lock );
671     mutex_cleanup_push( &p_fifo->lock );
672
673     while( p_fifo->p_first == NULL )
674         vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
675
676     b = p_fifo->p_first;
677
678     vlc_cleanup_run ();
679     return b;
680 }
681
682 /* FIXME: not thread-safe */
683 size_t block_FifoSize( const block_fifo_t *p_fifo )
684 {
685     return p_fifo->i_size;
686 }
687
688 /* FIXME: not thread-safe */
689 size_t block_FifoCount( const block_fifo_t *p_fifo )
690 {
691     return p_fifo->i_depth;
692 }