dvd_netlist.c 19.3 KB
Newer Older
1 2
/*****************************************************************************
 * dvd_netlist.c: Specific netlist for DVD packets
3
 *****************************************************************************
4 5 6 7 8 9
 * The original is in src/input.
 * There is only one major change from input_netlist.c : data is now a
 * pointer to an offset in iovec ; and iovec has a reference counter. It
 * will only be given back to netlist when refcount is zero.
 *****************************************************************************
 * 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 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 *
 * Authors: Henri Fallon <henri@videolan.org>
 *          Stphane Borel <stef@videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include "defs.h"

#include <stdlib.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
36
#include <string.h>                                    /* memcpy(), memset() */
37
#include <sys/types.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
38 39

#ifdef HAVE_UNISTD_H
Sam Hocevar's avatar
 
Sam Hocevar committed
40
#   include <unistd.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
41 42
#endif

Sam Hocevar's avatar
 
Sam Hocevar committed
43
#if defined( WIN32 )
Sam Hocevar's avatar
 
Sam Hocevar committed
44
#   include <io.h>                                                 /* read() */
Sam Hocevar's avatar
 
Sam Hocevar committed
45
#else
Sam Hocevar's avatar
 
Sam Hocevar committed
46
#   include <sys/uio.h>                                      /* struct iovec */
Sam Hocevar's avatar
 
Sam Hocevar committed
47
#endif
48 49 50 51 52 53 54

#include "config.h"
#include "common.h"
#include "threads.h"                                                /* mutex */
#include "mtime.h"
#include "intf_msg.h"                                           /* intf_*Msg */

Sam Hocevar's avatar
 
Sam Hocevar committed
55 56 57 58
#if defined( WIN32 )
#   include "input_iovec.h"
#endif

59 60 61
#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
62
#include "input_ext-plugins.h"
63 64 65

#include "dvd_netlist.h"

Sam Hocevar's avatar
 
Sam Hocevar committed
66 67 68
#include "modules.h"
#include "modules_export.h"

69 70 71 72 73 74 75 76 77 78 79 80 81 82
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/

/*****************************************************************************
 * DVDNetlistInit: allocates netlist buffers and init indexes
 * ---
 * Changes from input_NetList: we have to give the length of the buffer which
 * is different from i_nb_data now, since we may have several data pointers
 * in one iovec. Thus we can only delete an iovec when its refcount is 0.
 * We only received a buffer with a GetIovec whereas NewPacket gives a pointer.
 *
 * Warning: i_nb_iovec, i_nb_data, i_nb_pes have to be 2^x
 *****************************************************************************/
83 84
dvd_netlist_t * DVDNetlistInit( int i_nb_iovec, int i_nb_data, int i_nb_pes,
                                size_t i_buffer_size, int i_read_once )
85
{
86 87
    unsigned int        i_loop;
    dvd_netlist_t *     p_netlist;
88 89

    /* First we allocate and initialise our netlist struct */
90 91
    p_netlist = malloc( sizeof(dvd_netlist_t) );
    if ( p_netlist == NULL )
92 93
    {
        intf_ErrMsg("Unable to malloc the DVD netlist struct");
94 95
        free( p_netlist );
        return NULL;
96 97 98 99 100 101 102 103 104 105
    }
    
    /* Nb of packets read once by input */
    p_netlist->i_read_once = i_read_once;
    
    /* allocate the buffers */ 
    p_netlist->p_buffers = malloc( i_nb_iovec *i_buffer_size );
    if ( p_netlist->p_buffers == NULL )
    {
        intf_ErrMsg ("Unable to malloc in DVD netlist initialization (1)");
106 107 108
        free( p_netlist->p_buffers );
        free( p_netlist );
        return NULL;
109 110 111 112 113 114 115
    }
    
    /* table of pointers to data packets */
    p_netlist->p_data = malloc( i_nb_data *sizeof(data_packet_t) );
    if ( p_netlist->p_data == NULL )
    {
        intf_ErrMsg ("Unable to malloc in DVD netlist initialization (2)");
116 117 118 119
        free( p_netlist->p_buffers );
        free( p_netlist->p_data );
        free( p_netlist );
        return NULL;
120 121 122 123 124 125 126
    }
    
    /* table of pointer to PES packets */
    p_netlist->p_pes = malloc( i_nb_pes *sizeof(pes_packet_t) );
    if ( p_netlist->p_pes == NULL )
    {
        intf_ErrMsg ("Unable to malloc in DVD netlist initialization (3)");
127 128 129 130 131
        free( p_netlist->p_buffers );
        free( p_netlist->p_data );
        free( p_netlist->p_pes );
        free( p_netlist );
        return NULL;
132 133 134 135 136 137 138 139
    }
    
    /* allocate the FIFOs : tables of free pointers */
    p_netlist->pp_free_data = 
                        malloc( i_nb_data *sizeof(data_packet_t *) );
    if ( p_netlist->pp_free_data == NULL )
    {
        intf_ErrMsg ("Unable to malloc in DVD netlist initialization (4)");
140 141 142 143 144 145
        free( p_netlist->p_buffers );
        free( p_netlist->p_data );
        free( p_netlist->p_pes );
        free( p_netlist->pp_free_data );
        free( p_netlist );
        return NULL;
146 147 148 149 150 151
    }
    p_netlist->pp_free_pes = 
                        malloc( i_nb_pes *sizeof(pes_packet_t *) );
    if ( p_netlist->pp_free_pes == NULL )
    {
        intf_ErrMsg ("Unable to malloc in DVD netlist initialization (5)");
152 153 154 155 156 157 158
        free( p_netlist->p_buffers );
        free( p_netlist->p_data );
        free( p_netlist->p_pes );
        free( p_netlist->pp_free_data );
        free( p_netlist->pp_free_pes );
        free( p_netlist );
        return NULL;
159 160 161 162 163 164 165
    }
    
    p_netlist->p_free_iovec =
        malloc( (i_nb_iovec + p_netlist->i_read_once) * sizeof(struct iovec) );
    if ( p_netlist->p_free_iovec == NULL )
    {
        intf_ErrMsg ("Unable to malloc in DVD netlist initialization (6)");
166 167 168 169 170 171 172 173
        free( p_netlist->p_buffers );
        free( p_netlist->p_data );
        free( p_netlist->p_pes );
        free( p_netlist->pp_free_data );
        free( p_netlist->pp_free_pes );
        free( p_netlist->p_free_iovec );
        free( p_netlist );
        return NULL;
174 175 176 177 178 179 180
    }

    /* table for reference counter of iovecs */
    p_netlist->pi_refcount = malloc( i_nb_iovec *sizeof(int) );
    if ( p_netlist->pi_refcount == NULL )
    {
        intf_ErrMsg ("Unable to malloc in DVD netlist initialization (7)");
181 182 183 184 185 186 187 188 189
        free( p_netlist->p_buffers );
        free( p_netlist->p_data );
        free( p_netlist->p_pes );
        free( p_netlist->pp_free_data );
        free( p_netlist->pp_free_pes );
        free( p_netlist->p_free_iovec );
        free( p_netlist->pi_refcount );
        free( p_netlist );
        return NULL;
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
    }

    /* Fill the data FIFO */
    for ( i_loop = 0; i_loop < i_nb_data; i_loop++ )
    {
        p_netlist->pp_free_data[i_loop] = 
            p_netlist->p_data + i_loop;
    }

    /* Fill the PES FIFO */
    for ( i_loop = 0; i_loop < i_nb_pes ; i_loop++ )
    {
        p_netlist->pp_free_pes[i_loop] = 
            p_netlist->p_pes + i_loop;
    }
   
    /* Deal with the iovec */
    for ( i_loop = 0; i_loop < i_nb_iovec; i_loop++ )
    {
        p_netlist->p_free_iovec[i_loop].iov_base = 
            p_netlist->p_buffers + i_loop * i_buffer_size;
   
        p_netlist->p_free_iovec[i_loop].iov_len = i_buffer_size;
    }

    /* initialize reference counters */
    memset( p_netlist->pi_refcount, 0, i_nb_iovec *sizeof(int) );
   
    /* vlc_mutex_init */
    vlc_mutex_init (&p_netlist->lock);
    
    /* initialize indexes */
    p_netlist->i_iovec_start = 0;
    p_netlist->i_iovec_end = i_nb_iovec - 1;

    p_netlist->i_data_start = 0;
    p_netlist->i_data_end = i_nb_data - 1;

    p_netlist->i_pes_start = 0;
    p_netlist->i_pes_end = i_nb_pes - 1;

    /* we give (nb - 1) to use & instead of %
     * if you really need nb you have to add 1 */
    p_netlist->i_nb_iovec = i_nb_iovec - 1;
    p_netlist->i_nb_data = i_nb_data - 1;
    p_netlist->i_nb_pes = i_nb_pes - 1;
    p_netlist->i_buffer_size = i_buffer_size;

238
    return p_netlist; /* Everything went all right */
239 240 241 242 243
}

/*****************************************************************************
 * DVDGetiovec: returns an iovec pointer for a readv() operation
 *****************************************************************************
244 245 246
 * We return an iovec vector, so that readv can read many packets at a time.
 * pp_data will be set to direct to the fifo pointer in DVDMviovec, which
 * will allow us to get the corresponding data_packet.
247 248 249
 *****************************************************************************/
struct iovec * DVDGetiovec( void * p_method_data )
{
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 */
Sam Hocevar's avatar
 
Sam Hocevar committed
256
    if( (
257 258 259
     (p_netlist->i_iovec_end - p_netlist->i_iovec_start)
        & p_netlist->i_nb_iovec ) < p_netlist->i_read_once )
    {
260 261
        intf_WarnMsg( 12, "input info: waiting for free iovec" );
        msleep( INPUT_IDLE_SLEEP );
Sam Hocevar's avatar
 
Sam Hocevar committed
262 263 264 265 266 267 268 269 270

        while( (
         (p_netlist->i_iovec_end - p_netlist->i_iovec_start)
            & p_netlist->i_nb_iovec ) < p_netlist->i_read_once )
        {
            msleep( INPUT_IDLE_SLEEP );
        }

        intf_WarnMsg( 12, "input info: found free iovec" );
271 272
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
273
    if( (
274 275 276
     (p_netlist->i_data_end - p_netlist->i_data_start)
        & p_netlist->i_nb_data ) < p_netlist->i_read_once )
    {
277 278
        intf_WarnMsg( 12, "input info: waiting for free data packet" );
        msleep( INPUT_IDLE_SLEEP );
Sam Hocevar's avatar
 
Sam Hocevar committed
279 280 281 282 283 284 285 286 287

        while( (
         (p_netlist->i_data_end - p_netlist->i_data_start)
            & p_netlist->i_nb_data ) < p_netlist->i_read_once )
        {
            msleep( INPUT_IDLE_SLEEP );
        }

        intf_WarnMsg( 12, "input info: found free data packet" );
288
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
289

290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
    /* readv only takes contiguous buffers 
     * so, as a solution, we chose to have a FIFO a bit longer
     * than i_nb_data, and copy the begining of the FIFO to its end
     * if the readv needs to go after the end */
    if( p_netlist->i_nb_iovec - p_netlist->i_iovec_start + 1 <
                                                    p_netlist->i_read_once )
    {
        memcpy( &p_netlist->p_free_iovec[p_netlist->i_nb_iovec + 1], 
                p_netlist->p_free_iovec, 
                (p_netlist->i_read_once -
                    (p_netlist->i_nb_iovec + 1 - p_netlist->i_iovec_start))
                    * sizeof(struct iovec)
              );

    }

    return p_netlist->p_free_iovec + p_netlist->i_iovec_start;

}

/*****************************************************************************
 * DVDMviovec: move the iovec pointer by one after a readv() operation and
 * gives a data_packet corresponding to iovec in p_data
 *****************************************************************************/
void DVDMviovec( void * p_method_data, int i_nb_iovec,
                 struct data_packet_s ** pp_data )
{
317 318
    dvd_netlist_t *     p_netlist;
    unsigned int        i_loop = 0;
319 320

    /* cast */
321
    p_netlist = (dvd_netlist_t *)p_method_data;
322 323 324 325 326 327 328 329 330 331 332 333 334 335
    
    /* lock */
    vlc_mutex_lock( &p_netlist->lock );

    /* Fills a table of pointers to packets associated with the io_vec's */
    while( i_loop < i_nb_iovec )
    {
        pp_data[i_loop] = p_netlist->pp_free_data[p_netlist->i_data_start];
        
        pp_data[i_loop]->p_buffer =
                    p_netlist->p_free_iovec[p_netlist->i_iovec_start].iov_base;
        
        pp_data[i_loop]->pi_refcount = p_netlist->pi_refcount +
                                       p_netlist->i_iovec_start;
336

337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
        p_netlist->i_iovec_start ++;
        p_netlist->i_iovec_start &= p_netlist->i_nb_iovec;

        p_netlist->i_data_start ++;
        p_netlist->i_data_start &= p_netlist->i_nb_data;

        i_loop ++;
    }

    /* unlock */
    vlc_mutex_unlock( &p_netlist->lock );
    
}

/*****************************************************************************
 * DVDNewPtr: returns a free data_packet_t
 * Gives a pointer ; its fields need to be initialized
 *****************************************************************************/
struct data_packet_s * DVDNewPtr( void * p_method_data )
{    
357 358
    dvd_netlist_t *         p_netlist; 
    struct data_packet_s *  p_return;
359 360
    
    /* cast */
361
    p_netlist = (dvd_netlist_t *)p_method_data; 
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390

    /* lock */
    vlc_mutex_lock ( &p_netlist->lock );
        
    /* check */
    if ( p_netlist->i_data_start == p_netlist->i_data_end )
    {
        intf_ErrMsg("Empty Data FIFO in netlist. Unable to allocate memory");
        return ( NULL );
    }
    
    p_return = (p_netlist->pp_free_data[p_netlist->i_data_start]);

    p_netlist->i_data_start++;
    p_netlist->i_data_start &= p_netlist->i_nb_data;

    /* unlock */
    vlc_mutex_unlock (&p_netlist->lock);

    return ( p_return );
}

/*****************************************************************************
 * DVDNewPacket: returns a free data_packet_t, and takes a corresponding
 * storage iovec
 *****************************************************************************/
struct data_packet_s * DVDNewPacket( void * p_method_data,
                                     size_t i_buffer_size )
{
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 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
    
    /* lock */
    vlc_mutex_lock( &p_netlist->lock );

     /* check */
    if ( p_netlist->i_iovec_start == p_netlist->i_iovec_end )
    {
        intf_ErrMsg("Empty io_vec FIFO in netlist. Unable to allocate memory");
        return ( NULL );
    }

    if ( p_netlist->i_data_start == p_netlist->i_data_end )
    {
        intf_ErrMsg("Empty Data FIFO in netlist. Unable to allocate memory");
        return ( NULL );
    }


    /* Gives an io_vec and associated data */
    p_packet = p_netlist->pp_free_data[p_netlist->i_data_start];
        
    p_packet->p_buffer =
418
              p_netlist->p_free_iovec[p_netlist->i_iovec_start].iov_base;
419 420 421 422
        
    p_packet->p_payload_start = p_packet->p_buffer;
        
    p_packet->p_payload_end =
423 424 425 426
              p_packet->p_buffer + i_buffer_size;

    p_packet->p_next = NULL;
    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 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447

    p_netlist->i_iovec_start ++;
    p_netlist->i_iovec_start &= p_netlist->i_nb_iovec;

    p_netlist->i_data_start ++;
    p_netlist->i_data_start &= p_netlist->i_nb_data;

    /* unlock */
    vlc_mutex_unlock( &p_netlist->lock );

    return p_packet;
}

/*****************************************************************************
 * DVDNewPES: returns a free pes_packet_t
 *****************************************************************************/
struct pes_packet_s * DVDNewPES( void * p_method_data )
{
448 449
    dvd_netlist_t *     p_netlist;
    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 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
    
    /* lock */
    vlc_mutex_lock ( &p_netlist->lock );
    
    /* check */
    if ( p_netlist->i_pes_start == p_netlist->i_pes_end )
    {
        intf_ErrMsg("Empty PES FIFO in netlist - Unable to allocate memory");
        return ( NULL );
    }

    /* allocate */
    p_return = p_netlist->pp_free_pes[p_netlist->i_pes_start];
    p_netlist->i_pes_start++;
    p_netlist->i_pes_start &= p_netlist->i_nb_pes; 
   
    /* unlock */
    vlc_mutex_unlock (&p_netlist->lock);
    
    /* initialize PES */
474 475 476 477
    p_return->b_data_alignment = 0;
    p_return->b_discontinuity = 0; 
    p_return->i_pts = 0;
    p_return->i_dts = 0;
478 479
    p_return->i_pes_size = 0;
    p_return->p_first = NULL;
480

481 482 483 484 485 486 487 488
    return ( p_return );
}

/*****************************************************************************
 * DVDDeletePacket: puts a data_packet_t back into the netlist
 *****************************************************************************/
void DVDDeletePacket( void * p_method_data, data_packet_t * p_data )
{
489
    dvd_netlist_t * p_netlist;
490 491
    
    /* cast */
492
    p_netlist = (dvd_netlist_t *) p_method_data;
493 494 495 496 497 498 499 500 501 502 503

    /* lock */
    vlc_mutex_lock ( &p_netlist->lock );


   /* Delete data_packet */
    p_netlist->i_data_end ++;
    p_netlist->i_data_end &= p_netlist->i_nb_data;
    
    p_netlist->pp_free_data[p_netlist->i_data_end] = p_data;

504 505 506
    p_data->p_next = NULL;
    p_data->b_discard_payload = 0;

507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
    /* Update reference counter */
    (*p_data->pi_refcount)--;

    if( (*p_data->pi_refcount) == 0 )
    {

        p_netlist->i_iovec_end++;
        p_netlist->i_iovec_end &= p_netlist->i_nb_iovec;
        p_netlist->p_free_iovec[p_netlist->i_iovec_end].iov_base =
                                                            p_data->p_buffer;
    }
 
    /* unlock */
    vlc_mutex_unlock (&p_netlist->lock);
}

/*****************************************************************************
 * DVDDeletePES: puts a pes_packet_t back into the netlist
 *****************************************************************************/
void DVDDeletePES( void * p_method_data, pes_packet_t * p_pes )
{
528 529 530
    dvd_netlist_t *     p_netlist; 
    data_packet_t *     p_current_packet;
    data_packet_t *     p_next_packet;
531 532
    
    /* cast */
533
    p_netlist = (dvd_netlist_t *)p_method_data;
534 535 536 537 538 539 540 541 542 543 544 545 546

    /* lock */
    vlc_mutex_lock ( &p_netlist->lock );

    /* delete free  p_pes->p_first, p_next ... */
    p_current_packet = p_pes->p_first;
    while ( p_current_packet != NULL )
    {
        /* copy of NetListDeletePacket, duplicate code avoid many locks */

        p_netlist->i_data_end ++;
        p_netlist->i_data_end &= p_netlist->i_nb_data;

547
        /* re initialize */
548 549 550 551 552 553 554
        p_current_packet->p_payload_start = p_current_packet->p_buffer;
        
        p_netlist->pp_free_data[p_netlist->i_data_end] = p_current_packet;

        /* Update reference counter */
        (*p_current_packet->pi_refcount)--;

555
        if( (*p_current_packet->pi_refcount) <= 0 )
556 557 558 559 560 561 562 563 564
        {
            p_netlist->i_iovec_end++;
            p_netlist->i_iovec_end &= p_netlist->i_nb_iovec;
            p_netlist->p_free_iovec[p_netlist->i_iovec_end].iov_base =
                    p_current_packet->p_buffer;
        }
    
        p_next_packet = p_current_packet->p_next;
        p_current_packet->p_next = NULL;
565
        p_current_packet->b_discard_payload = 0;
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
        p_current_packet = p_next_packet;
    }
 
    /* delete our current PES packet */
    p_netlist->i_pes_end ++;
    p_netlist->i_pes_end &= p_netlist->i_nb_pes;
    p_netlist->pp_free_pes[p_netlist->i_pes_end] = p_pes;
    
    /* unlock */
    vlc_mutex_unlock (&p_netlist->lock);

}

/*****************************************************************************
 * DVDNetlistEnd: frees all allocated structures
 *****************************************************************************/
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 590 591
    free( p_netlist->p_free_iovec );
    free( p_netlist->pp_free_pes );
    free( p_netlist->pp_free_data );
592 593 594
    free( p_netlist->p_pes );
    free( p_netlist->p_data );
    free( p_netlist->p_buffers );
595 596

    /* free the netlist */
597
    free( p_netlist );
598
}