]> git.sesse.net Git - vlc/blob - src/input/input_netlist.c
-Merged DVD netlist with input netlist to remove duplicated code.
[vlc] / src / input / input_netlist.c
1 /*****************************************************************************
2  * dvd_netlist.c: netlist management v2
3  *****************************************************************************
4  * There is only one major change from input_netlist.c (1) : data is now a
5  * pointer to an offset in iovec ; and iovec has a reference counter. It
6  * will only be given back to netlist when refcount is zero.
7  *****************************************************************************
8  * Copyright (C) 1998, 1999, 2000, 2001 VideoLAN
9  * $Id: input_netlist.c,v 1.43 2001/11/11 01:32:03 stef Exp $
10  *
11  * Authors: Henri Fallon <henri@videolan.org>
12  *          Stéphane Borel <stef@videolan.org>
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  * 
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
27  *****************************************************************************/
28
29 /*****************************************************************************
30  * Preamble
31  *****************************************************************************/
32 #include "defs.h"
33
34 #include <stdlib.h>
35 #include <string.h>                                    /* memcpy(), memset() */
36 #include <sys/types.h>
37
38 #ifdef HAVE_UNISTD_H
39 #   include <unistd.h>
40 #endif
41
42 #if defined( WIN32 )
43 #   include <io.h>                                                 /* read() */
44 #else
45 #   include <sys/uio.h>                                      /* struct iovec */
46 #endif
47
48 #include "config.h"
49 #include "common.h"
50 #include "threads.h"                                                /* mutex */
51 #include "mtime.h"
52 #include "intf_msg.h"                                           /* intf_*Msg */
53
54 #if defined( WIN32 )
55 #   include "input_iovec.h"
56 #endif
57
58 #include "stream_control.h"
59 #include "input_ext-intf.h"
60 #include "input_ext-dec.h"
61 #include "input_ext-plugins.h"
62
63 /*****************************************************************************
64  * Local prototypes
65  *****************************************************************************/
66
67 /*****************************************************************************
68  * input_NetlistInit: allocates netlist buffers and init indexes
69  * ---
70  * Changes from input_NetList: we have to give the length of the buffer which
71  * is different from i_nb_data now, since we may have several data pointers
72  * in one iovec. Thus we can only delete an iovec when its refcount is 0.
73  * We only received a buffer with a GetIovec whereas NewPacket gives a pointer.
74  *****************************************************************************/
75 int input_NetlistInit( input_thread_t * p_input,
76                        int i_nb_iovec, int i_nb_data, int i_nb_pes,
77                        size_t i_buffer_size, int i_read_once )
78 {
79     unsigned int        i_loop;
80     netlist_t *         p_netlist;
81
82     /* First we allocate and initialise our netlist struct */
83     p_input->p_method_data = malloc(sizeof(netlist_t));
84     if ( p_input->p_method_data == NULL )
85     {
86         intf_ErrMsg("Unable to malloc the netlist struct");
87         return (-1);
88     }
89
90     p_netlist = (netlist_t *) p_input->p_method_data;
91                 
92     /* Nb of packets read once by input */
93     p_netlist->i_read_once = i_read_once;
94     
95     /* In order to optimize netlist, we are taking i_nb_data a 2^i 
96      * so that modulo is an "&".
97      * This is not changing i_nb data outside this function except in 
98      * the netlist_t struct */ 
99     /* As i_loop is unsigned int, and i_ns_data int, this shouldn't be a 
100      * problem */
101     for( i_loop = 1; i_loop < i_nb_data; i_loop *= 2 )
102     {
103         ;
104     }
105
106     intf_DbgMsg( "Netlist : Required %i byte, got %u",i_nb_data,i_loop );
107     i_nb_data = i_loop;
108
109     /* Same thing for i_nb_pes */
110     for( i_loop = 1; i_loop < i_nb_pes; i_loop *= 2 )
111     {
112         ;
113     }
114
115     intf_DbgMsg( "Netlist : Required %i byte, got %u",i_nb_pes,i_loop );
116     i_nb_pes = i_loop;
117
118      /* Same thing for i_nb_iovec */
119     for( i_loop = 1; i_loop < i_nb_iovec; i_loop *= 2 )
120     {
121         ;
122     }
123
124     intf_DbgMsg( "Netlist : Required %i byte, got %u",i_nb_iovec,i_loop );
125     i_nb_iovec = i_loop;
126    
127     /* allocate the buffers */ 
128     p_netlist->p_buffers = malloc( i_nb_iovec *i_buffer_size );
129     if ( p_netlist->p_buffers == NULL )
130     {
131         intf_ErrMsg ("Unable to malloc in netlist initialization (1)");
132         free( p_netlist );
133         return -1;
134     }
135     
136     /* table of pointers to data packets */
137     p_netlist->p_data = malloc( i_nb_data *sizeof(data_packet_t) );
138     if ( p_netlist->p_data == NULL )
139     {
140         intf_ErrMsg ("Unable to malloc in netlist initialization (2)");
141         free( p_netlist->p_buffers );
142         free( p_netlist );
143         return -1;
144     }
145     
146     /* table of pointer to PES packets */
147     p_netlist->p_pes = malloc( i_nb_pes *sizeof(pes_packet_t) );
148     if ( p_netlist->p_pes == NULL )
149     {
150         intf_ErrMsg ("Unable to malloc in netlist initialization (3)");
151         free( p_netlist->p_buffers );
152         free( p_netlist->p_data );
153         free( p_netlist );
154         return -1;
155     }
156     
157     /* allocate the FIFOs : tables of free pointers */
158     p_netlist->pp_free_data = 
159                         malloc( i_nb_data *sizeof(data_packet_t *) );
160     if ( p_netlist->pp_free_data == NULL )
161     {
162         intf_ErrMsg ("Unable to malloc in netlist initialization (4)");
163         free( p_netlist->p_buffers );
164         free( p_netlist->p_data );
165         free( p_netlist->p_pes );
166         free( p_netlist );
167         return -1;
168     }
169     p_netlist->pp_free_pes = 
170                         malloc( i_nb_pes *sizeof(pes_packet_t *) );
171     if ( p_netlist->pp_free_pes == NULL )
172     {
173         intf_ErrMsg ("Unable to malloc in netlist initialization (5)");
174         free( p_netlist->p_buffers );
175         free( p_netlist->p_data );
176         free( p_netlist->p_pes );
177         free( p_netlist->pp_free_data );
178         free( p_netlist );
179         return -1;
180     }
181     
182     p_netlist->p_free_iovec =
183         malloc( (i_nb_iovec + p_netlist->i_read_once) * sizeof(struct iovec) );
184     if ( p_netlist->p_free_iovec == NULL )
185     {
186         intf_ErrMsg ("Unable to malloc in DVD netlist initialization (6)");
187         free( p_netlist->p_buffers );
188         free( p_netlist->p_data );
189         free( p_netlist->p_pes );
190         free( p_netlist->pp_free_data );
191         free( p_netlist->pp_free_pes );
192         free( p_netlist );
193         return -1;
194     }
195
196     /* table for reference counter of iovecs */
197     p_netlist->pi_refcount = malloc( i_nb_iovec *sizeof(int) );
198     if ( p_netlist->pi_refcount == NULL )
199     {
200         intf_ErrMsg ("Unable to malloc in DVD netlist initialization (7)");
201         free( p_netlist->p_buffers );
202         free( p_netlist->p_data );
203         free( p_netlist->p_pes );
204         free( p_netlist->pp_free_data );
205         free( p_netlist->pp_free_pes );
206         free( p_netlist->p_free_iovec );
207         free( p_netlist );
208         return -1;
209     }
210
211     /* Fill the data FIFO */
212     for ( i_loop = 0; i_loop < i_nb_data; i_loop++ )
213     {
214         p_netlist->pp_free_data[i_loop] = 
215             p_netlist->p_data + i_loop;
216
217         /* by default, one data packet for one buffer */
218         if( i_nb_data == i_nb_iovec )
219         {
220             p_netlist->pp_free_data[i_loop]->p_buffer =
221                 p_netlist->p_buffers + i_loop * i_buffer_size;
222
223             p_netlist->pp_free_data[i_loop]->p_payload_start =
224                 p_netlist->pp_free_data[i_loop]->p_buffer;
225
226             p_netlist->pp_free_data[i_loop]->p_payload_end =
227                 p_netlist->pp_free_data[i_loop]->p_buffer + i_buffer_size;
228         }
229     }
230
231     /* Fill the PES FIFO */
232     for ( i_loop = 0; i_loop < i_nb_pes ; i_loop++ )
233     {
234         p_netlist->pp_free_pes[i_loop] = 
235             p_netlist->p_pes + i_loop;
236     }
237    
238     /* Deal with the iovec */
239     for ( i_loop = 0; i_loop < i_nb_iovec; i_loop++ )
240     {
241         p_netlist->p_free_iovec[i_loop].iov_base = 
242             p_netlist->p_buffers + i_loop * i_buffer_size;
243    
244         p_netlist->p_free_iovec[i_loop].iov_len = i_buffer_size;
245     }
246
247     /* initialize reference counters */
248     memset( p_netlist->pi_refcount, 0, i_nb_iovec *sizeof(int) );
249    
250     /* vlc_mutex_init */
251     vlc_mutex_init (&p_netlist->lock);
252     
253     /* initialize indexes */
254     p_netlist->i_iovec_start = 0;
255     p_netlist->i_iovec_end = i_nb_iovec - 1;
256
257     p_netlist->i_data_start = 0;
258     p_netlist->i_data_end = i_nb_data - 1;
259
260     p_netlist->i_pes_start = 0;
261     p_netlist->i_pes_end = i_nb_pes - 1;
262
263     /* we give (nb - 1) to use & instead of %
264      * if you really need nb you have to add 1 */
265     p_netlist->i_nb_iovec = i_nb_iovec - 1;
266     p_netlist->i_nb_data = i_nb_data - 1;
267     p_netlist->i_nb_pes = i_nb_pes - 1;
268     p_netlist->i_buffer_size = i_buffer_size;
269
270     return 0; /* Everything went all right */
271 }
272
273 /*****************************************************************************
274  * input_NetlistGetiovec: returns an iovec pointer for a readv() operation
275  *****************************************************************************
276  * We return an iovec vector, so that readv can read many packets at a time.
277  * pp_data will be set to direct to the fifo pointer in DVDMviovec, which
278  * will allow us to get the corresponding data_packet.
279  *****************************************************************************/
280 struct iovec * input_NetlistGetiovec( void * p_method_data )
281 {
282     netlist_t *     p_netlist;
283
284     /* cast */
285     p_netlist = (netlist_t *)p_method_data;
286     
287     /* check that we have enough free iovec */
288     if( (
289      (p_netlist->i_iovec_end - p_netlist->i_iovec_start)
290         & p_netlist->i_nb_iovec ) < p_netlist->i_read_once )
291     {
292         intf_WarnMsg( 12, "input info: waiting for free iovec" );
293         msleep( INPUT_IDLE_SLEEP );
294
295         while( (
296          (p_netlist->i_iovec_end - p_netlist->i_iovec_start)
297             & p_netlist->i_nb_iovec ) < p_netlist->i_read_once )
298         {
299             msleep( INPUT_IDLE_SLEEP );
300         }
301
302         intf_WarnMsg( 12, "input info: found free iovec" );
303     }
304
305     if( (
306      (p_netlist->i_data_end - p_netlist->i_data_start)
307         & p_netlist->i_nb_data ) < p_netlist->i_read_once )
308     {
309         intf_WarnMsg( 12, "input info: waiting for free data packet" );
310         msleep( INPUT_IDLE_SLEEP );
311
312         while( (
313          (p_netlist->i_data_end - p_netlist->i_data_start)
314             & p_netlist->i_nb_data ) < p_netlist->i_read_once )
315         {
316             msleep( INPUT_IDLE_SLEEP );
317         }
318
319         intf_WarnMsg( 12, "input info: found free data packet" );
320     }
321
322     /* readv only takes contiguous buffers 
323      * so, as a solution, we chose to have a FIFO a bit longer
324      * than i_nb_data, and copy the begining of the FIFO to its end
325      * if the readv needs to go after the end */
326     if( p_netlist->i_nb_iovec - p_netlist->i_iovec_start + 1 <
327                                                     p_netlist->i_read_once )
328     {
329         memcpy( &p_netlist->p_free_iovec[p_netlist->i_nb_iovec + 1], 
330                 p_netlist->p_free_iovec, 
331                 (p_netlist->i_read_once -
332                     (p_netlist->i_nb_iovec + 1 - p_netlist->i_iovec_start))
333                     * sizeof(struct iovec)
334               );
335
336     }
337
338     return p_netlist->p_free_iovec + p_netlist->i_iovec_start;
339
340 }
341
342 /*****************************************************************************
343  * input_NetlistMviovec: move the iovec pointer by one after a readv()
344  *  operation and gives a data_packet corresponding to iovec in p_data
345  *****************************************************************************/
346 void input_NetlistMviovec( void * p_method_data, int i_nb_iovec,
347                            struct data_packet_s ** pp_data )
348 {
349     netlist_t *         p_netlist;
350     unsigned int        i_loop = 0;
351
352     /* cast */
353     p_netlist = (netlist_t *)p_method_data;
354     
355     /* lock */
356     vlc_mutex_lock( &p_netlist->lock );
357
358     /* Fills a table of pointers to packets associated with the io_vec's */
359     while( i_loop < i_nb_iovec )
360     {
361         pp_data[i_loop] = p_netlist->pp_free_data[p_netlist->i_data_start];
362         
363         pp_data[i_loop]->p_buffer =
364                     p_netlist->p_free_iovec[p_netlist->i_iovec_start].iov_base;
365         
366         pp_data[i_loop]->pi_refcount = p_netlist->pi_refcount +
367                                        p_netlist->i_iovec_start;
368         (*pp_data[i_loop]->pi_refcount)++;
369
370         p_netlist->i_iovec_start ++;
371         p_netlist->i_iovec_start &= p_netlist->i_nb_iovec;
372
373         p_netlist->i_data_start ++;
374         p_netlist->i_data_start &= p_netlist->i_nb_data;
375
376         i_loop ++;
377     }
378
379     /* unlock */
380     vlc_mutex_unlock( &p_netlist->lock );
381     
382 }
383
384 /*****************************************************************************
385  * input_NetlistNewPtr: returns a free data_packet_t
386  * Gives a pointer ; its fields need to be initialized
387  *****************************************************************************/
388 struct data_packet_s * input_NetlistNewPtr( void * p_method_data )
389 {    
390     netlist_t *             p_netlist; 
391     struct data_packet_s *  p_return;
392     
393     /* cast */
394     p_netlist = (netlist_t *)p_method_data; 
395
396     /* lock */
397     vlc_mutex_lock ( &p_netlist->lock );
398         
399     /* check */
400     if ( p_netlist->i_data_start == p_netlist->i_data_end )
401     {
402         intf_ErrMsg("Empty Data FIFO in netlist. Unable to allocate memory");
403         return ( NULL );
404     }
405     
406     p_return = (p_netlist->pp_free_data[p_netlist->i_data_start]);
407
408     p_netlist->i_data_start++;
409     p_netlist->i_data_start &= p_netlist->i_nb_data;
410
411     /* unlock */
412     vlc_mutex_unlock (&p_netlist->lock);
413
414     return ( p_return );
415 }
416
417 /*****************************************************************************
418  * input_NetlistNewPacket: returns a free data_packet_t, and takes
419  * a corresponding storage iovec.
420  *****************************************************************************/
421 struct data_packet_s * input_NetlistNewPacket( void * p_method_data,
422                                                size_t i_buffer_size )
423 {
424     netlist_t *             p_netlist;
425     struct data_packet_s *  p_packet;
426
427     /* cast */
428     p_netlist = (netlist_t *)p_method_data;
429
430 #ifdef DEBUG
431     if( i_buffer_size > p_netlist->i_buffer_size )
432     {
433         /* This should not happen */
434         intf_ErrMsg( "Netlist packet too small !" );
435         return NULL;
436     }
437 #endif
438     
439     /* lock */
440     vlc_mutex_lock( &p_netlist->lock );
441
442      /* check */
443     if ( p_netlist->i_iovec_start == p_netlist->i_iovec_end )
444     {
445         intf_ErrMsg("Empty io_vec FIFO in netlist. Unable to allocate memory");
446         return ( NULL );
447     }
448
449     if ( p_netlist->i_data_start == p_netlist->i_data_end )
450     {
451         intf_ErrMsg("Empty Data FIFO in netlist. Unable to allocate memory");
452         return ( NULL );
453     }
454
455
456     /* Gives an io_vec and associated data */
457     p_packet = p_netlist->pp_free_data[p_netlist->i_data_start];
458         
459     p_packet->p_buffer =
460               p_netlist->p_free_iovec[p_netlist->i_iovec_start].iov_base;
461         
462     p_packet->p_payload_start = p_packet->p_buffer;
463         
464     p_packet->p_payload_end =
465               p_packet->p_buffer + i_buffer_size;
466
467     p_packet->p_next = NULL;
468     p_packet->b_discard_payload = 0;
469
470     p_packet->pi_refcount = p_netlist->pi_refcount + p_netlist->i_iovec_start;
471     (*p_packet->pi_refcount)++;
472
473     p_netlist->i_iovec_start ++;
474     p_netlist->i_iovec_start &= p_netlist->i_nb_iovec;
475
476     p_netlist->i_data_start ++;
477     p_netlist->i_data_start &= p_netlist->i_nb_data;
478
479     /* unlock */
480     vlc_mutex_unlock( &p_netlist->lock );
481
482     return p_packet;
483 }
484
485 /*****************************************************************************
486  * input_NetlistNewPES: returns a free pes_packet_t
487  *****************************************************************************/
488 struct pes_packet_s * input_NetlistNewPES( void * p_method_data )
489 {
490     netlist_t *         p_netlist;
491     pes_packet_t *      p_return;
492     
493     /* cast */ 
494     p_netlist = (netlist_t *)p_method_data;
495     
496     /* lock */
497     vlc_mutex_lock ( &p_netlist->lock );
498     
499     /* check */
500     if ( p_netlist->i_pes_start == p_netlist->i_pes_end )
501     {
502         intf_ErrMsg("Empty PES FIFO in netlist - Unable to allocate memory");
503         return ( NULL );
504     }
505
506     /* allocate */
507     p_return = p_netlist->pp_free_pes[p_netlist->i_pes_start];
508     p_netlist->i_pes_start++;
509     p_netlist->i_pes_start &= p_netlist->i_nb_pes; 
510    
511     /* unlock */
512     vlc_mutex_unlock (&p_netlist->lock);
513     
514     /* initialize PES */
515     p_return->b_data_alignment = 0;
516     p_return->b_discontinuity = 0; 
517     p_return->i_pts = 0;
518     p_return->i_dts = 0;
519     p_return->i_pes_size = 0;
520     p_return->p_first = NULL;
521
522     return ( p_return );
523 }
524
525 /*****************************************************************************
526  * input_NetlistDeletePacket: puts a data_packet_t back into the netlist
527  *****************************************************************************/
528 void input_NetlistDeletePacket( void * p_method_data, data_packet_t * p_data )
529 {
530     netlist_t * p_netlist;
531     
532     /* cast */
533     p_netlist = (netlist_t *) p_method_data;
534
535     /* lock */
536     vlc_mutex_lock ( &p_netlist->lock );
537
538    /* Delete data_packet */
539     p_netlist->i_data_end ++;
540     p_netlist->i_data_end &= p_netlist->i_nb_data;
541     
542     p_data->p_payload_start = p_data->p_buffer;
543     p_data->p_payload_end = p_data->p_buffer + p_netlist->i_buffer_size;
544         
545     p_netlist->pp_free_data[p_netlist->i_data_end] = p_data;
546
547     p_data->p_next = NULL;
548     p_data->b_discard_payload = 0;
549
550     /* Update reference counter */
551     (*p_data->pi_refcount)--;
552
553     if( (*p_data->pi_refcount) <= 0 )
554     {
555
556         p_netlist->i_iovec_end++;
557         p_netlist->i_iovec_end &= p_netlist->i_nb_iovec;
558         p_netlist->p_free_iovec[p_netlist->i_iovec_end].iov_base =
559                                                             p_data->p_buffer;
560     }
561  
562     /* unlock */
563     vlc_mutex_unlock (&p_netlist->lock);
564 }
565
566 /*****************************************************************************
567  * input_NetlistDeletePES: puts a pes_packet_t back into the netlist
568  *****************************************************************************/
569 void input_NetlistDeletePES( void * p_method_data, pes_packet_t * p_pes )
570 {
571     netlist_t *         p_netlist; 
572     data_packet_t *     p_current_packet;
573     data_packet_t *     p_next_packet;
574     
575     /* cast */
576     p_netlist = (netlist_t *)p_method_data;
577
578     /* lock */
579     vlc_mutex_lock ( &p_netlist->lock );
580
581     /* delete free  p_pes->p_first, p_next ... */
582     p_current_packet = p_pes->p_first;
583     while ( p_current_packet != NULL )
584     {
585         /* copy of NetListDeletePacket, duplicate code avoid many locks */
586
587         p_netlist->i_data_end ++;
588         p_netlist->i_data_end &= p_netlist->i_nb_data;
589
590         /* re initialize */
591         p_current_packet->p_payload_start = p_current_packet->p_buffer;
592         p_current_packet->p_payload_end = p_current_packet->p_buffer
593             + p_netlist->i_buffer_size;
594         
595         p_netlist->pp_free_data[p_netlist->i_data_end] = p_current_packet;
596
597         /* Update reference counter */
598         (*p_current_packet->pi_refcount)--;
599
600         if( (*p_current_packet->pi_refcount) <= 0 )
601         {
602             (*p_current_packet->pi_refcount) = 0;
603             p_netlist->i_iovec_end++;
604             p_netlist->i_iovec_end &= p_netlist->i_nb_iovec;
605             p_netlist->p_free_iovec[p_netlist->i_iovec_end].iov_base =
606                     p_current_packet->p_buffer;
607         }
608     
609         p_next_packet = p_current_packet->p_next;
610         p_current_packet->p_next = NULL;
611         p_current_packet->b_discard_payload = 0;
612         p_current_packet = p_next_packet;
613     }
614  
615     /* delete our current PES packet */
616     p_netlist->i_pes_end ++;
617     p_netlist->i_pes_end &= p_netlist->i_nb_pes;
618     p_netlist->pp_free_pes[p_netlist->i_pes_end] = p_pes;
619     
620     /* unlock */
621     vlc_mutex_unlock (&p_netlist->lock);
622
623 }
624
625 /*****************************************************************************
626  * input_NetlistEnd: frees all allocated structures
627  *****************************************************************************/
628 void input_NetlistEnd( input_thread_t * p_input )
629 {
630     netlist_t * p_netlist;
631
632     /* cast */
633     p_netlist = ( netlist_t * ) p_input->p_method_data;
634
635     /* destroy the mutex lock */
636     vlc_mutex_destroy( &p_netlist->lock );
637     
638     /* free the FIFO, the buffer, and the netlist structure */
639     free( p_netlist->pi_refcount );
640     free( p_netlist->p_free_iovec );
641     free( p_netlist->pp_free_pes );
642     free( p_netlist->pp_free_data );
643     free( p_netlist->p_pes );
644     free( p_netlist->p_data );
645     free( p_netlist->p_buffers );
646
647     /* free the netlist */
648     free( p_netlist );
649 }