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