Commit 51e76e12 authored by Gildas Bazin's avatar Gildas Bazin

* modules/access/dvdread, modules/access/dvdread.c:

  + major rewrite of the dvdread module.
  + dvdread is now an access_demux module.
parent 9eb3e68f
......@@ -1211,71 +1211,70 @@ if test "${enable_livedotcom}" = "yes"; then
fi
fi
dnl
dnl dnl
dnl dnl dvdread module: check for libdvdread
dnl dnl
dnl AC_ARG_ENABLE(dvdread,
dnl [ --enable-dvdread dvdread input module (default disabled)])
dnl if test "${enable_dvdread}" != "no"
dnl then
dnl AC_ARG_WITH(dvdread,
dnl [ --with-dvdread=PATH libdvdread headers and libraries])
dnl AC_ARG_WITH(dvdread-tree,
dnl [ --with-dvdread-tree=PATH libdvdread tree for static linking])
dnl if test -z "${with_dvdread}"
dnl then
dnl if test -z "${with_dvdread_tree}"
dnl then
dnl AC_CHECK_HEADERS(dvdread/dvd_reader.h,
dnl [ AX_ADD_PLUGINS([dvdread])
dnl AX_ADD_LDFLAGS([dvdread],[-ldvdread ${LDFLAGS_dvdcss}])
dnl ],[
dnl if test -n "${enable_dvdread}"
dnl then
dnl AC_MSG_WARN([Please get libdvdread from http://www.dtek.chalmers.se/groups/dvd/downloads.shtml])
dnl AC_MSG_ERROR([cannot find libdvdread headers])
dnl fi
dnl ])
dnl else
dnl AC_MSG_CHECKING(for libdvdread.a in ${with_dvdread_tree})
dnl real_dvdread_tree="`cd ${with_dvdread_tree} 2>/dev/null && pwd`"
dnl if test -z "${real_dvdread_tree}"
dnl then
dnl dnl The given directory can't be found
dnl AC_MSG_RESULT(no)
dnl AC_MSG_ERROR([cannot cd to ${with_dvdread_tree}])
dnl fi
dnl if test -f "${real_dvdread_tree}/dvdread/.libs/libdvdread.a"
dnl then
dnl dnl Use a custom libdvdread
dnl AC_MSG_RESULT(${real_dvdread_tree}/dvdread/.libs/libdvdread.a)
dnl AX_ADD_BUILTINS([dvdread])
dnl AX_ADD_LDFLAGS([dvdread],[-L${real_dvdread_tree}/dvdread/.libs -ldvdread ${LDFLAGS_dvdcss}])
dnl AX_ADD_CPPFLAGS([dvdread],[-I${real_dvdread_tree}])
dnl else
dnl dnl The given libdvdread wasn't built
dnl AC_MSG_RESULT(no)
dnl AC_MSG_ERROR([cannot find ${real_dvdread_tree}/dvdread/.libs/libdvdread.a, make sure you compiled libdvdread in ${with_dvdread_tree}])
dnl fi
dnl fi
dnl else
dnl AC_MSG_CHECKING(for dvdread headers in ${with_dvdread})
dnl if test -f ${with_dvdread}/include/dvdread/dvd_reader.h
dnl then
dnl dnl Use ${with_dvdread}/include/dvdread/dvd_reader.h
dnl AC_MSG_RESULT(yes)
dnl AX_ADD_PLUGINS([dvdread])
dnl AX_ADD_LDFLAGS([dvdread],[-L${with_dvdread}/lib -ldvdread ${LDFLAGS_dvdcss}])
dnl AX_ADD_CPPFLAGS([dvdread],[-I${with_dvdread}/include])
dnl else
dnl dnl No libdvdread could be found, sorry
dnl AC_MSG_RESULT(no)
dnl AC_MSG_ERROR([cannot find ${with_dvdread}/include/dvdread/dvd_reader.h])
dnl fi
dnl fi
dnl fi
dnl dvdread module: check for libdvdread
dnl
AC_ARG_ENABLE(dvdread,
[ --enable-dvdread dvdread input module (default disabled)])
if test "${enable_dvdread}" != "no"
then
AC_ARG_WITH(dvdread,
[ --with-dvdread=PATH libdvdread headers and libraries])
AC_ARG_WITH(dvdread-tree,
[ --with-dvdread-tree=PATH libdvdread tree for static linking])
if test -z "${with_dvdread}"
then
if test -z "${with_dvdread_tree}"
then
AC_CHECK_HEADERS(dvdread/dvd_reader.h,
[ AX_ADD_PLUGINS([dvdread])
AX_ADD_LDFLAGS([dvdread],[-ldvdread ${LDFLAGS_dvdcss}])
],[
if test -n "${enable_dvdread}"
then
AC_MSG_WARN([Please get libdvdread from http://www.dtek.chalmers.se/groups/dvd/downloads.shtml])
AC_MSG_ERROR([cannot find libdvdread headers])
fi
])
else
AC_MSG_CHECKING(for libdvdread.a in ${with_dvdread_tree})
real_dvdread_tree="`cd ${with_dvdread_tree} 2>/dev/null && pwd`"
if test -z "${real_dvdread_tree}"
then
dnl The given directory can't be found
AC_MSG_RESULT(no)
AC_MSG_ERROR([cannot cd to ${with_dvdread_tree}])
fi
if test -f "${real_dvdread_tree}/dvdread/.libs/libdvdread.a"
then
dnl Use a custom libdvdread
AC_MSG_RESULT(${real_dvdread_tree}/dvdread/.libs/libdvdread.a)
AX_ADD_BUILTINS([dvdread])
AX_ADD_LDFLAGS([dvdread],[-L${real_dvdread_tree}/dvdread/.libs -ldvdread ${LDFLAGS_dvdcss}])
AX_ADD_CPPFLAGS([dvdread],[-I${real_dvdread_tree}])
else
dnl The given libdvdread wasn't built
AC_MSG_RESULT(no)
AC_MSG_ERROR([cannot find ${real_dvdread_tree}/dvdread/.libs/libdvdread.a, make sure you compiled libdvdread in ${with_dvdread_tree}])
fi
fi
else
AC_MSG_CHECKING(for dvdread headers in ${with_dvdread})
if test -f ${with_dvdread}/include/dvdread/dvd_reader.h
then
dnl Use ${with_dvdread}/include/dvdread/dvd_reader.h
AC_MSG_RESULT(yes)
AX_ADD_PLUGINS([dvdread])
AX_ADD_LDFLAGS([dvdread],[-L${with_dvdread}/lib -ldvdread ${LDFLAGS_dvdcss}])
AX_ADD_CPPFLAGS([dvdread],[-I${with_dvdread}/include])
else
dnl No libdvdread could be found, sorry
AC_MSG_RESULT(no)
AC_MSG_ERROR([cannot find ${with_dvdread}/include/dvdread/dvd_reader.h])
fi
fi
fi
dnl
dnl libdvdnav plugin
......
......@@ -4,8 +4,8 @@ SOURCES_access_udp = udp.c
SOURCES_access_tcp = tcp.c
SOURCES_access_http = http.c
SOURCES_access_ftp = ftp.c
SOURCES_dvdread = dvdread.c
SOURCES_slp = slp.c
SOURCES_access2 = access2.c
SOURCES_cdda = \
cdda.c \
vcd/cdrom.c \
......
/*****************************************************************************
* dvdread.c : DvdRead input module for vlc
*****************************************************************************
* Copyright (C) 2001-2004 VideoLAN
* $Id$
*
* Authors: Stphane Borel <stef@via.ecp.fr>
* Gildas Bazin <gbazin@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 <stdio.h>
#include <stdlib.h> /* malloc(), free() */
#include <string.h> /* strdup() */
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "iso_lang.h"
#include "../demux/ps.h"
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <dvdread/dvd_reader.h>
#include <dvdread/ifo_types.h>
#include <dvdread/ifo_read.h>
#include <dvdread/nav_read.h>
#include <dvdread/nav_print.h>
/*****************************************************************************
* Module descriptor
*****************************************************************************/
#define CACHING_TEXT N_("caching value in ms")
#define CACHING_LONGTEXT N_( \
"Allows you to modify the default caching value for DVDread streams. " \
"This value should be set in millisecond units." )
#define CSSMETHOD_TEXT N_("Method used by libdvdcss for decryption")
#define CSSMETHOD_LONGTEXT N_( \
"Set the method used by libdvdcss for key decryption.\n" \
"title: decrypted title key is guessed from the encrypted sectors of " \
"the stream. Thus it should work with a file as well as the " \
"DVD device. But it sometimes takes much time to decrypt a title " \
"key and may even fail. With this method, the key is only checked "\
"at the beginning of each title, so it won't work if the key " \
"changes in the middle of a title.\n" \
"disc: the disc key is first cracked, then all title keys can be " \
"decrypted instantly, which allows us to check them often.\n" \
"key: the same as \"disc\" if you don't have a file with player keys " \
"at compilation time. If you do, the decryption of the disc key " \
"will be faster with this method. It is the one that was used by " \
"libcss.\n" \
"The default method is: key.")
static char *psz_css_list[] = { "title", "disc", "key" };
static char *psz_css_list_text[] = { N_("title"), N_("Disc"), N_("Key") };
static int Open ( vlc_object_t * );
static void Close( vlc_object_t * );
vlc_module_begin();
set_description( _("DVDRead Input") );
add_integer( "dvdread-caching", DEFAULT_PTS_DELAY / 1000, NULL,
CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
add_string( "dvdread-css-method", NULL, NULL, CSSMETHOD_TEXT,
CSSMETHOD_LONGTEXT, VLC_TRUE );
change_string_list( psz_css_list, psz_css_list_text, 0 );
set_capability( "access_demux", 0 );
add_shortcut( "dvd" );
add_shortcut( "dvdread" );
set_callbacks( Open, Close );
vlc_module_end();
/* how many blocks DVDRead will read in each loop */
#define DVD_BLOCK_READ_ONCE 64
/*****************************************************************************
* Local prototypes
*****************************************************************************/
struct demux_sys_t
{
/* DVDRead state */
dvd_reader_t *p_dvdread;
dvd_file_t *p_title;
ifo_handle_t *p_vmg_file;
ifo_handle_t *p_vts_file;
unsigned int i_title;
unsigned int i_chapter, i_chapters;
unsigned int i_angle, i_angles;
tt_srpt_t *p_tt_srpt;
pgc_t *p_cur_pgc;
dsi_t dsi_pack;
int i_ttn;
unsigned int i_pack_len;
unsigned int i_cur_block;
unsigned int i_next_vobu;
/* Current title start/end blocks */
unsigned int i_title_start_block;
unsigned int i_title_end_block;
unsigned int i_title_blocks;
unsigned int i_title_offset;
unsigned int i_title_start_cell;
unsigned int i_cur_cell;
unsigned int i_next_cell;
/* track */
ps_track_t tk[PS_TK_COUNT];
/* for spu variables */
input_thread_t *p_input;
/* FIXME */
uint8_t alpha[4];
uint32_t clut[16];
/* */
int i_aspect;
int i_titles;
input_title_t **titles;
};
static char *ParseCL( vlc_object_t *, char *, vlc_bool_t, int *, int *, int *);
static int Control ( demux_t *, int, va_list );
static int Demux ( demux_t * );
static int DemuxBlock( demux_t *, uint8_t *, int );
enum
{
AR_SQUARE_PICTURE = 1, /* square pixels */
AR_3_4_PICTURE = 2, /* 3:4 picture (TV) */
AR_16_9_PICTURE = 3, /* 16:9 picture (wide screen) */
AR_221_1_PICTURE = 4, /* 2.21:1 picture (movie) */
};
static void DemuxTitles( demux_t *, int *, int *, int * );
static void ESNew( demux_t *, int, int );
static int DvdReadSetArea ( demux_t *, int, int, int );
static void DvdReadSeek ( demux_t *, int );
static void DvdReadHandleDSI( demux_t *, uint8_t * );
static void DvdReadFindCell ( demux_t * );
/*****************************************************************************
* Open:
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
demux_t *p_demux = (demux_t*)p_this;
demux_sys_t *p_sys;
int i_title, i_chapter, i_angle;
char *psz_name;
char *psz_dvdcss_env;
dvd_reader_t *p_dvdread;
ifo_handle_t *p_vmg_file;
psz_name = ParseCL( VLC_OBJECT(p_demux), p_demux->psz_path, VLC_TRUE,
&i_title, &i_chapter, &i_angle );
if( !psz_name )
{
return VLC_EGENERIC;
}
/* Override environment variable DVDCSS_METHOD with config option
* (FIXME: this creates a small memory leak) */
psz_dvdcss_env = config_GetPsz( p_demux, "dvdread-css-method" );
if( psz_dvdcss_env && *psz_dvdcss_env )
{
char *psz_env;
psz_env = malloc( strlen("DVDCSS_METHOD=") +
strlen( psz_dvdcss_env ) + 1 );
if( !psz_env )
{
free( psz_dvdcss_env );
return VLC_ENOMEM;
}
sprintf( psz_env, "%s%s", "DVDCSS_METHOD=", psz_dvdcss_env );
putenv( psz_env );
}
if( psz_dvdcss_env ) free( psz_dvdcss_env );
/* Open dvdread */
if( !(p_dvdread = DVDOpen( psz_name )) )
{
msg_Err( p_demux, "DVDRead cannot open source: %s", psz_name );
free( psz_name );
return VLC_EGENERIC;
}
free( psz_name );
/* Ifo allocation & initialisation */
if( !( p_vmg_file = ifoOpen( p_dvdread, 0 ) ) )
{
msg_Warn( p_demux, "cannot open VMG info" );
return VLC_EGENERIC;
}
msg_Dbg( p_demux, "VMG opened" );
/* Fill p_demux field */
p_demux->pf_demux = Demux;
p_demux->pf_control = Control;
p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
memset( p_sys, 0, sizeof( demux_sys_t ) );
ps_track_init( p_sys->tk );
p_sys->i_aspect = -1;
p_sys->p_dvdread = p_dvdread;
p_sys->p_vmg_file = p_vmg_file;
p_sys->p_title = NULL;
p_sys->p_vts_file = NULL;
p_sys->i_title = p_sys->i_chapter = -1;
p_sys->i_angle = i_angle;
DemuxTitles( p_demux, &i_title, &i_chapter, &i_angle );
DvdReadSetArea( p_demux, i_title - 1, i_chapter, i_angle );
/* Update default_pts to a suitable value for dvdread access */
var_Create( p_demux, "dvdread-caching",
VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
return VLC_SUCCESS;
}
/*****************************************************************************
* Close:
*****************************************************************************/
static void Close( vlc_object_t *p_this )
{
demux_t *p_demux = (demux_t*)p_this;
demux_sys_t *p_sys = p_demux->p_sys;
int i;
for( i = 0; i < PS_TK_COUNT; i++ )
{
ps_track_t *tk = &p_sys->tk[i];
if( tk->b_seen )
{
es_format_Clean( &tk->fmt );
if( tk->es ) es_out_Del( p_demux->out, tk->es );
}
}
/* Close libdvdread */
if( p_sys->p_title ) DVDCloseFile( p_sys->p_title );
if( p_sys->p_vts_file ) ifoClose( p_sys->p_vts_file );
if( p_sys->p_vmg_file ) ifoClose( p_sys->p_vmg_file );
DVDClose( p_sys->p_dvdread );
free( p_sys );
}
/*****************************************************************************
* Control:
*****************************************************************************/
static int Control( demux_t *p_demux, int i_query, va_list args )
{
demux_sys_t *p_sys = p_demux->p_sys;
double f, *pf;
vlc_bool_t *pb;
int64_t *pi64;
input_title_t ***ppp_title;
int *pi_int;
int i;
switch( i_query )
{
case DEMUX_GET_POSITION:
{
pf = (double*) va_arg( args, double* );
if( p_sys->i_title_blocks > 0 )
*pf = (double)p_sys->i_title_offset / p_sys->i_title_blocks;
else
*pf = 0.0;
return VLC_SUCCESS;
}
case DEMUX_SET_POSITION:
{
f = (double)va_arg( args, double );
DvdReadSeek( p_demux, f * p_sys->i_title_blocks );
return VLC_SUCCESS;
}
/* Special for access_demux */
case DEMUX_CAN_PAUSE:
case DEMUX_CAN_CONTROL_PACE:
/* TODO */
pb = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
*pb = VLC_TRUE;
return VLC_SUCCESS;
case DEMUX_SET_PAUSE_STATE:
return VLC_SUCCESS;
case DEMUX_GET_TITLE_INFO:
ppp_title = (input_title_t***)va_arg( args, input_title_t*** );
pi_int = (int*)va_arg( args, int* );
/* Duplicate title infos */
*pi_int = p_sys->i_titles;
*ppp_title = malloc( sizeof(input_title_t **) * p_sys->i_titles );
for( i = 0; i < p_sys->i_titles; i++ )
{
(*ppp_title)[i] = vlc_input_title_Duplicate(p_sys->titles[i]);
}
return VLC_SUCCESS;
case DEMUX_SET_TITLE:
i = (int)va_arg( args, int );
if( DvdReadSetArea( p_demux, i, 0, -1 ) != VLC_SUCCESS )
{
msg_Warn( p_demux, "cannot set title/chapter" );
return VLC_EGENERIC;
}
p_demux->info.i_update |=
INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
p_demux->info.i_title = i;
p_demux->info.i_seekpoint = 0;
return VLC_SUCCESS;
case DEMUX_SET_SEEKPOINT:
i = (int)va_arg( args, int );
if( DvdReadSetArea( p_demux, -1, i, -1 ) != VLC_SUCCESS )
{
msg_Warn( p_demux, "cannot set title/chapter" );
return VLC_EGENERIC;
}
p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
p_demux->info.i_seekpoint = i;
return VLC_SUCCESS;
case DEMUX_GET_PTS_DELAY:
pi64 = (int64_t*)va_arg( args, int64_t * );
*pi64 = (int64_t)var_GetInteger( p_demux, "dvdread-caching" )*1000;
return VLC_SUCCESS;
/* TODO implement others */
default:
return VLC_EGENERIC;
}
}
/*****************************************************************************
* Demux:
*****************************************************************************/
static int Demux( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
uint8_t p_buffer[DVD_VIDEO_LB_LEN * DVD_BLOCK_READ_ONCE];
int i_blocks_once, i_read;
int i;
/*
* Playback by cell in this pgc, starting at the cell for our chapter.
*/
/*
* Check end of pack, and select the following one
*/
if( !p_sys->i_pack_len )
{
/* Read NAV packet */
if( DVDReadBlocks( p_sys->p_title, p_sys->i_next_vobu,
1, p_buffer ) != 1 )
{
msg_Err( p_demux, "read failed for block %d", p_sys->i_next_vobu );
return -1;
}
/* Basic check to be sure we don't have a empty title
* go to next title if so */
//assert( p_buffer[41] == 0xbf && p_buffer[1027] == 0xbf );
/* Parse the contained dsi packet */
DvdReadHandleDSI( p_demux, p_buffer );
/* End of title */
if( p_sys->i_next_vobu > p_sys->i_title_end_block )
{
if( p_sys->i_title + 1 >= p_sys->i_titles ) return 0; /* EOF */
DvdReadSetArea( p_demux, p_sys->i_title + 1, 0, -1 );
}
if( p_sys->i_pack_len <= 0 )
{
msg_Err( p_demux, "i_pack_len <= 0. This shouldn't happen!" );
return 0; /* EOF */
}
/* FIXME: Ugly kludge: we send the pack block to the input for it
* sometimes has a zero scr and restart the sync */
p_sys->i_cur_block++;
p_sys->i_title_offset++;
DemuxBlock( p_demux, p_buffer, DVD_VIDEO_LB_LEN );
}
if( p_sys->i_cur_block > p_sys->i_title_end_block )
{
if( p_sys->i_title + 1 >= p_sys->i_titles ) return 0; /* EOF */
DvdReadSetArea( p_demux, p_sys->i_title + 1, 0, -1 );
}
/*
* Read actual data
*/
i_blocks_once = __MIN( p_sys->i_pack_len, DVD_BLOCK_READ_ONCE );
p_sys->i_pack_len -= i_blocks_once;
/* Reads from DVD */
i_read = DVDReadBlocks( p_sys->p_title, p_sys->i_cur_block,
i_blocks_once, p_buffer );
if( i_read != i_blocks_once )
{
msg_Err( p_demux, "read failed for %d/%d blocks at 0x%02x",
i_read, i_blocks_once, p_sys->i_cur_block );
return -1;
}
p_sys->i_cur_block += i_read;
p_sys->i_title_offset += i_read;
#if 0
msg_Dbg( p_demux, "i_blocks: %d len: %d current: 0x%02x",
i_read, p_sys->i_pack_len, p_sys->i_cur_block );
#endif
for( i = 0; i < i_read; i++ )
{
DemuxBlock( p_demux, p_buffer + i * DVD_VIDEO_LB_LEN,
DVD_VIDEO_LB_LEN );
}
#undef p_pgc
return 1;
}
/*****************************************************************************
* DemuxBlock: demux a given block
*****************************************************************************/
static int DemuxBlock( demux_t *p_demux, uint8_t *pkt, int i_pkt )
{
demux_sys_t *p_sys = p_demux->p_sys;
uint8_t *p = pkt;
while( p < &pkt[i_pkt] )
{
int i_size = ps_pkt_size( p, &pkt[i_pkt] - p );
block_t *p_pkt;
if( i_size <= 0 )
{
break;
}
/* Create a block */
p_pkt = block_New( p_demux, i_size );
memcpy( p_pkt->p_buffer, p, i_size);
/* Parse it and send it */
switch( 0x100 | p[3] )
{
case 0x1b9:
case 0x1bb:
case 0x1bc:
#ifdef DVDREAD_DEBUG
if( p[3] == 0xbc )
{
msg_Warn( p_demux, "received a PSM packet" );
}
else if( p[3] == 0xbb )
{
msg_Warn( p_demux, "received a SYSTEM packet" );
}
#endif
block_Release( p_pkt );
break;
case 0x1ba:
{
int64_t i_scr;
int i_mux_rate;
if( !ps_pkt_parse_pack( p_pkt, &i_scr, &i_mux_rate ) )
{
es_out_Control( p_demux->out, ES_OUT_SET_PCR, i_scr );
}
block_Release( p_pkt );
break;
}
default:
{
int i_id = ps_pkt_id( p_pkt );
if( i_id >= 0xc0 )
{
ps_track_t *tk = &p_sys->tk[PS_ID_TO_TK(i_id)];
if( !tk->b_seen )
{
ESNew( p_demux, i_id, 0 );
}
if( tk->b_seen && tk->es &&
!ps_pkt_parse_pes( p_pkt, tk->i_skip ) )
{
es_out_Send( p_demux->out, tk->es, p_pkt );
}
else
{
block_Release( p_pkt );
}
}
else
{
block_Release( p_pkt );
}
break;
}
}
p += i_size;
}
return VLC_SUCCESS;
}
/*****************************************************************************
* ESNew: register a new elementary stream
*****************************************************************************/
static void ESNew( demux_t *p_demux, int i_id, int i_lang )
{
demux_sys_t *p_sys = p_demux->p_sys;
ps_track_t *tk = &p_sys->tk[PS_ID_TO_TK(i_id)];
char psz_language[3];
if( tk->b_seen ) return;
if( ps_track_fill( tk, i_id ) )
{
msg_Warn( p_demux, "unknown codec for id=0x%x", i_id );
return;
}
psz_language[0] = psz_language[1] = psz_language[2] = 0;
if( i_lang && i_lang != 0xffff )
{
psz_language[0] = (i_lang >> 8)&0xff;
psz_language[1] = (i_lang )&0xff;
}
/* Add a new ES */
if( tk->fmt.i_cat == VIDEO_ES )
{
if( p_sys->i_aspect >= 0 )
{
tk->fmt.video.i_aspect = p_sys->i_aspect;
}
}
else if( tk->fmt.i_cat == AUDIO_ES )
{
int i_audio = -1;
/* find the audio number PLEASE find another way */
if( (i_id&0xbdf8) == 0xbd88 ) /* dts */
{
i_audio = i_id&0x07;
}
else if( (i_id&0xbdf0) == 0xbd80 ) /* a52 */
{
i_audio = i_id&0xf;
}
else if( (i_id&0xbdf0) == 0xbda0 ) /* lpcm */
{
i_audio = i_id&0x1f;
}
else if( ( i_id&0xe0 ) == 0xc0 ) /* mpga */
{
i_audio = i_id&0x1f;
}
if( psz_language[0] ) tk->fmt.psz_language = strdup( psz_language );
}
else if( tk->fmt.i_cat == SPU_ES )
{
/* Palette */
tk->fmt.subs.spu.palette[0] = 0xBeef;
memcpy( &tk->fmt.subs.spu.palette[1], p_sys->clut,
16 * sizeof( uint32_t ) );
if( psz_language[0] ) tk->fmt.psz_language = strdup( psz_language );
}
tk->es = es_out_Add( p_demux->out, &tk->fmt );
tk->b_seen = VLC_TRUE;
}
/*****************************************************************************
* DvdReadSetArea: initialize input data for title x, chapter y.
* It should be called for each user navigation request.
*****************************************************************************
* Take care that i_title and i_chapter start from 0.
*****************************************************************************/
static int DvdReadSetArea( demux_t *p_demux, int i_title, int i_chapter,
int i_angle )
{
demux_sys_t *p_sys = p_demux->p_sys;
int pgc_id = 0;
int pgn = 0;
int i;
vlc_value_t val;
#define p_pgc p_sys->p_cur_pgc
#define p_vmg p_sys->p_vmg_file
#define p_vts p_sys->p_vts_file
if( i_title >= 0 && i_title < p_sys->i_titles &&
i_title != p_sys->i_title )
{
int i_start_cell, i_end_cell;
if( p_sys->p_title != NULL ) DVDCloseFile( p_sys->p_title );
if( p_vts != NULL ) ifoClose( p_vts );
p_sys->i_title = i_title;
/*
* We have to load all title information
*/
msg_Dbg( p_demux, "open VTS %d, for title %d",
p_vmg->tt_srpt->title[i_title].title_set_nr, i_title + 1 );
/* Ifo vts */
if( !( p_vts = ifoOpen( p_sys->p_dvdread,
p_vmg->tt_srpt->title[i_title].title_set_nr ) ) )
{
msg_Err( p_demux, "fatal error in vts ifo" );
return VLC_EGENERIC;
}
/* Title position inside the selected vts */
p_sys->i_ttn = p_vmg->tt_srpt->title[i_title].vts_ttn;
/* Find title start/end */
pgc_id = p_vts->vts_ptt_srpt->title[p_sys->i_ttn - 1].ptt[0].pgcn;
pgn = p_vts->vts_ptt_srpt->title[p_sys->i_ttn - 1].ptt[0].pgn;
p_pgc = p_vts->vts_pgcit->pgci_srp[pgc_id - 1].pgc;
p_sys->i_title_start_cell =
i_start_cell = p_pgc->program_map[pgn - 1] - 1;
p_sys->i_title_start_block =
p_pgc->cell_playback[i_start_cell].first_sector;
i_end_cell = p_pgc->nr_of_cells - 1;
p_sys->i_title_end_block =
p_pgc->cell_playback[i_end_cell].last_sector;
p_sys->i_title_offset = 0;
p_sys->i_title_blocks = 0;
for( i = i_start_cell; i <= i_end_cell; i++ )
{
p_sys->i_title_blocks += p_pgc->cell_playback[i].last_sector -
p_pgc->cell_playback[i].first_sector + 1;
}
msg_Dbg( p_demux, "title %d vts_title %d pgc %d pgn %d "
"start %d end %d blocks: %d",
i_title + 1, p_sys->i_ttn, pgc_id, pgn,
p_sys->i_title_start_block, p_sys->i_title_end_block,
p_sys->i_title_blocks );
/*
* Set properties for current chapter
*/
p_sys->i_chapter = 0;
p_sys->i_chapters =
p_vts->vts_ptt_srpt->title[p_sys->i_ttn - 1].nr_of_ptts;
pgc_id = p_vts->vts_ptt_srpt->title[
p_sys->i_ttn - 1].ptt[p_sys->i_chapter].pgcn;
pgn = p_vts->vts_ptt_srpt->title[
p_sys->i_ttn - 1].ptt[p_sys->i_chapter].pgn;
p_pgc = p_vts->vts_pgcit->pgci_srp[pgc_id - 1].pgc;
p_sys->i_pack_len = 0;
p_sys->i_next_cell =
p_sys->i_cur_cell = p_pgc->program_map[pgn - 1] - 1;
DvdReadFindCell( p_demux );
p_sys->i_next_vobu = p_sys->i_cur_block =
p_pgc->cell_playback[p_sys->i_cur_cell].first_sector;
/*
* Angle management
*/
p_sys->i_angles = p_vmg->tt_srpt->title[i_title].nr_of_angles;
if( p_sys->i_angle > p_sys->i_angles ) p_sys->i_angle = 1;
/*
* We've got enough info, time to open the title set data.
*/
if( !( p_sys->p_title = DVDOpenFile( p_sys->p_dvdread,
p_vmg->tt_srpt->title[i_title].title_set_nr,
DVD_READ_TITLE_VOBS ) ) )
{
msg_Err( p_demux, "cannot open title (VTS_%02d_1.VOB)",
p_vmg->tt_srpt->title[i_title].title_set_nr );
return VLC_EGENERIC;
}
//IfoPrintTitle( p_demux );
/*
* Destroy obsolete ES by reinitializing program 0
* and find all ES in title with ifo data
*/
es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
for( i = 0; i < PS_TK_COUNT; i++ )
{
ps_track_t *tk = &p_sys->tk[i];
if( tk->b_seen )
{
es_format_Clean( &tk->fmt );
if( tk->es ) es_out_Del( p_demux->out, tk->es );
}
tk->b_seen = VLC_FALSE;
}
if( p_demux->info.i_title != i_title )
{
p_demux->info.i_update |=
INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
p_demux->info.i_title = i_title;
p_demux->info.i_seekpoint = 0;
}
/* TODO: re-add angles */
ESNew( p_demux, 0xe0, 0 ); /* Video, FIXME ? */
#define audio_control \
p_sys->p_vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc->audio_control[i-1]
/* Audio ES, in the order they appear in the .ifo */
for( i = 1; i <= p_vts->vtsi_mat->nr_of_vts_audio_streams; i++ )
{
int i_position = 0;
uint16_t i_id;
//IfoPrintAudio( p_demux, i );
/* Audio channel is active if first byte is 0x80 */
if( audio_control & 0x8000 )
{
i_position = ( audio_control & 0x7F00 ) >> 8;
msg_Dbg( p_demux, "audio position %d", i_position );
switch( p_vts->vtsi_mat->vts_audio_attr[i - 1].audio_format )
{
case 0x00: /* A52 */
i_id = (0x80 + i_position) | 0xbd00;
break;
case 0x02:
case 0x03: /* MPEG audio */
i_id = 0xc000 + i_position;
break;
case 0x04: /* LPCM */
i_id = (0xa0 + i_position) | 0xbd00;
break;
case 0x06: /* DTS */
i_id = (0x88 + i_position) | 0xbd00;
break;
default:
i_id = 0;
msg_Err( p_demux, "unknown audio type %.2x",
p_vts->vtsi_mat->vts_audio_attr[i - 1].audio_format );
}
ESNew( p_demux, i_id, p_sys->p_vts_file->vtsi_mat->
vts_audio_attr[i - 1].lang_code );
}
}
#undef audio_control
#define spu_control \
p_sys->p_vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc->subp_control[i-1]
/* Sub Picture ES */
for( i = 1; i <= p_vts->vtsi_mat->nr_of_vts_subp_streams; i++ )
{
int i_position = 0;
uint16_t i_id;
//IfoPrintSpu( p_sys, i );
msg_Dbg( p_demux, "spu %d 0x%02x", i, spu_control );
if( spu_control & 0x80000000 )
{
/* there are several streams for one spu */
if( p_vts->vtsi_mat->vts_video_attr.display_aspect_ratio )
{
/* 16:9 */
switch( p_vts->vtsi_mat->vts_video_attr.permitted_df )
{
case 1:
i_position = spu_control & 0xff;
break;
case 2:
i_position = ( spu_control >> 8 ) & 0xff;
break;
default:
i_position = ( spu_control >> 16 ) & 0xff;
break;
}
}
else
{
/* 4:3 */
i_position = ( spu_control >> 24 ) & 0x7F;
}
i_id = (0x20 + i_position) | 0xbd00;
ESNew( p_demux, i_id, p_sys->p_vts_file->vtsi_mat->
vts_subp_attr[i - 1].lang_code );
}
}
#undef spu_control
}
else
{
/* TODO: update input ? */
}
/*
* Chapter selection
*/
if( i_chapter >= 0 && i_chapter <= p_sys->i_chapters &&
i_chapter != p_sys->i_chapter )
{
pgc_id = p_vts->vts_ptt_srpt->title[
p_sys->i_ttn - 1].ptt[i_chapter].pgcn;
pgn = p_vts->vts_ptt_srpt->title[
p_sys->i_ttn - 1].ptt[i_chapter].pgn;
p_pgc = p_vts->vts_pgcit->pgci_srp[pgc_id - 1].pgc;
p_sys->i_cur_cell = p_pgc->program_map[pgn - 1] - 1;
p_sys->i_chapter = i_chapter;
DvdReadFindCell( p_demux );
p_sys->i_title_offset = 0;
for( i = p_sys->i_title_start_cell; i < p_sys->i_cur_cell; i++ )
{
p_sys->i_title_offset += p_pgc->cell_playback[i].last_sector -
p_pgc->cell_playback[i].first_sector + 1;
}
p_sys->i_pack_len = 0;
p_sys->i_next_vobu = p_sys->i_cur_block =
p_pgc->cell_playback[p_sys->i_cur_cell].first_sector;
if( p_demux->info.i_seekpoint != i_chapter )
{
p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
p_demux->info.i_seekpoint = i_chapter;
}
}
#undef p_pgc
#undef p_vts
#undef p_vmg
return VLC_SUCCESS;
}
/*****************************************************************************
* DvdReadSeek : Goes to a given position on the stream.
*****************************************************************************
* This one is used by the input and translate chronological position from
* input to logical position on the device.
*****************************************************************************/
static void DvdReadSeek( demux_t *p_demux, int i_block_offset )
{
demux_sys_t *p_sys = p_demux->p_sys;
unsigned int i_tmp;
unsigned int i_chapter = 0;
unsigned int i_cell = 0;
unsigned int i_vobu = 0;
unsigned int i_sub_cell = 0;
int i_block;
#define p_pgc p_sys->p_cur_pgc
#define p_vts p_sys->p_vts_file
/* Find cell */
p_sys->i_title_offset = i_block = i_block_offset;
while( i_block >= (int)p_pgc->cell_playback[i_cell].last_sector -
(int)p_pgc->cell_playback[i_cell].first_sector + 1 )
{
i_cell++;
i_block -= p_pgc->cell_playback[i_cell].last_sector -
p_pgc->cell_playback[i_cell].first_sector + 1;
}
i_block += p_pgc->cell_playback[i_cell].first_sector;
/* Find chapter */
do
{
int pgc_id, pgn;
i_chapter++;
pgc_id = p_vts->vts_ptt_srpt->title[
p_sys->i_ttn - 1].ptt[i_chapter].pgcn;
pgn = p_vts->vts_ptt_srpt->title[
p_sys->i_ttn - 1].ptt[i_chapter].pgn;
i_tmp = p_vts->vts_pgcit->pgci_srp[pgc_id - 1].pgc->program_map[pgn-1];
} while( i_tmp <= i_cell );
/* Find vobu */
while( p_vts->vts_vobu_admap->vobu_start_sectors[i_vobu] <= i_block )
{
i_vobu++;
}
/* Find sub_cell */
while( p_vts->vts_c_adt->cell_adr_table[i_sub_cell].start_sector <
p_vts->vts_vobu_admap->vobu_start_sectors[i_vobu-1] )
{
i_sub_cell++;
}
#if 1
msg_Dbg( p_demux, "cell %d i_sub_cell %d chapter %d vobu %d "
"cell_sector %d vobu_sector %d sub_cell_sector %d",
i_cell, i_sub_cell,i_chapter, i_vobu,
p_sys->p_cur_pgc->cell_playback[i_cell].first_sector,
p_vts->vts_vobu_admap->vobu_start_sectors[i_vobu],
p_vts->vts_c_adt->cell_adr_table[i_sub_cell - 1].start_sector);
#endif
p_sys->i_cur_block = i_block;
p_sys->i_next_vobu = p_vts->vts_vobu_admap->vobu_start_sectors[i_vobu];
p_sys->i_pack_len = p_sys->i_next_vobu - i_block;
p_sys->i_cur_cell = i_cell;
p_sys->i_chapter = i_chapter;
DvdReadFindCell( p_demux );
#undef p_vts
#undef p_pgc
return;
}
/*****************************************************************************
* DvdReadHandleDSI
*****************************************************************************/
static void DvdReadHandleDSI( demux_t *p_demux, uint8_t *p_data )
{
demux_sys_t *p_sys = p_demux->p_sys;
navRead_DSI( &p_sys->dsi_pack, &p_data[DSI_START_BYTE] );
/*
* Determine where we go next. These values are the ones we mostly
* care about.
*/
p_sys->i_cur_block = p_sys->dsi_pack.dsi_gi.nv_pck_lbn;
/*
* If we're not at the end of this cell, we can determine the next
* VOBU to display using the VOBU_SRI information section of the
* DSI. Using this value correctly follows the current angle,
* avoiding the doubled scenes in The Matrix, and makes our life
* really happy.
*/
if( p_sys->dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL )
{
switch( ( p_sys->dsi_pack.sml_pbi.category & 0xf000 ) >> 12 )
{
case 0x4:
/* Interleaved unit with no angle */
if( p_sys->dsi_pack.sml_pbi.ilvu_sa != 0 )
{
p_sys->i_next_vobu = p_sys->i_cur_block +
p_sys->dsi_pack.sml_pbi.ilvu_sa;
p_sys->i_pack_len = p_sys->dsi_pack.sml_pbi.ilvu_ea;
}
else
{
p_sys->i_next_vobu = p_sys->i_cur_block +
p_sys->dsi_pack.dsi_gi.vobu_ea + 1;
p_sys->i_pack_len = p_sys->dsi_pack.dsi_gi.vobu_ea;
}
break;
case 0x5:
/* vobu is end of ilvu */
if( p_sys->dsi_pack.sml_agli.data[p_sys->i_angle-1].address )
{
p_sys->i_next_vobu = p_sys->i_cur_block +
p_sys->dsi_pack.sml_agli.data[p_sys->i_angle-1].address;
p_sys->i_pack_len = p_sys->dsi_pack.sml_pbi.ilvu_ea;
break;
}
case 0x6:
/* vobu is beginning of ilvu */
case 0x9:
/* next scr is 0 */
case 0xa:
/* entering interleaved section */
case 0x8:
/* non interleaved cells in interleaved section */
default:
p_sys->i_next_vobu = p_sys->i_cur_block +
( p_sys->dsi_pack.vobu_sri.next_vobu & 0x7fffffff );
p_sys->i_pack_len = p_sys->dsi_pack.dsi_gi.vobu_ea;
break;
}
}
else
{
p_sys->i_cur_cell = p_sys->i_next_cell;
DvdReadFindCell( p_demux );
p_sys->i_pack_len = p_sys->dsi_pack.dsi_gi.vobu_ea;
p_sys->i_next_vobu =
p_sys->p_cur_pgc->cell_playback[p_sys->i_cur_cell].first_sector;
}
#if 0
msg_Dbg( p_input, 12, "scr %d lbn 0x%02x vobu_ea %d vob_id %d c_id %d",
p_sys->dsi_pack.dsi_gi.nv_pck_scr,
p_sys->dsi_pack.dsi_gi.nv_pck_lbn,
p_sys->dsi_pack.dsi_gi.vobu_ea,
p_sys->dsi_pack.dsi_gi.vobu_vob_idn,
p_sys->dsi_pack.dsi_gi.vobu_c_idn );
msg_Dbg( p_input, 12, "cat 0x%02x ilvu_ea %d ilvu_sa %d size %d",
p_sys->dsi_pack.sml_pbi.category,
p_sys->dsi_pack.sml_pbi.ilvu_ea,
p_sys->dsi_pack.sml_pbi.ilvu_sa,
p_sys->dsi_pack.sml_pbi.size );
msg_Dbg( p_input, 12, "next_vobu %d next_ilvu1 %d next_ilvu2 %d",
p_sys->dsi_pack.vobu_sri.next_vobu & 0x7fffffff,
p_sys->dsi_pack.sml_agli.data[ p_sys->i_angle - 1 ].address,
p_sys->dsi_pack.sml_agli.data[ p_sys->i_angle ].address);
#endif
}
/*****************************************************************************
* DvdReadFindCell
*****************************************************************************/
static void DvdReadFindCell( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
pgc_t *p_pgc;
int pgc_id, pgn;
int i = 0;
#define cell p_sys->p_cur_pgc->cell_playback
if( cell[p_sys->i_cur_cell].block_type == BLOCK_TYPE_ANGLE_BLOCK )
{
#if 0
p_sys->i_next_cell = p_sys->i_cur_cell + p_sys->i_angle_nb;
p_sys->i_cur_cell += p_sys->i_angle - 1;
#else
p_sys->i_cur_cell += p_sys->i_angle - 1;
while( cell[p_sys->i_cur_cell+i].block_mode != BLOCK_MODE_LAST_CELL )
{
i++;
}
p_sys->i_next_cell = p_sys->i_cur_cell + i + 1;
#endif
}
else
{
p_sys->i_next_cell = p_sys->i_cur_cell + 1;
}
#undef cell
pgc_id = p_sys->p_vts_file->vts_ptt_srpt->title[
p_sys->i_ttn - 1].ptt[p_sys->i_chapter].pgcn;
pgn = p_sys->p_vts_file->vts_ptt_srpt->title[
p_sys->i_ttn - 1].ptt[p_sys->i_chapter].pgn;
p_pgc = p_sys->p_vts_file->vts_pgcit->pgci_srp[pgc_id - 1].pgc;
if( p_pgc->program_map[pgn - 1] <= p_sys->i_cur_cell )
{
p_sys->i_chapter++;
if( p_sys->i_chapter < p_sys->i_chapters &&
p_demux->info.i_seekpoint != p_sys->i_chapter )
{
p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
p_demux->info.i_seekpoint = p_sys->i_chapter;
}
}
}
/*****************************************************************************
* DemuxTitles: get the titles/chapters structure
*****************************************************************************/
static void DemuxTitles( demux_t *p_demux,
int *pi_title, int *pi_chapter, int *pi_angle )
{
demux_sys_t *p_sys = p_demux->p_sys;
input_title_t *t;
seekpoint_t *s;
int32_t i_titles;
int i;
/* Find out number of titles/chapters */
#define tt_srpt p_sys->p_vmg_file->tt_srpt
i_titles = tt_srpt->nr_of_srpts;
msg_Dbg( p_demux, "number of titles: %d", i_titles );
for( i = 0; i < i_titles; i++ )
{
int32_t i_chapters = 0;
int j;
i_chapters = tt_srpt->title[i].nr_of_ptts;
msg_Dbg( p_demux, "title %d has %d chapters", i, i_chapters );
t = vlc_input_title_New();
for( j = 0; j < __MAX( i_chapters, 1 ); j++ )
{
s = vlc_seekpoint_New();
TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
}
TAB_APPEND( p_sys->i_titles, p_sys->titles, t );
}
/* Set forced title/chapter/angle */
*pi_title = (*pi_title >= 1 && *pi_title <= i_titles) ? *pi_title : 1;
*pi_chapter = (*pi_chapter >= 0 && *pi_chapter <
tt_srpt->title[*pi_title].nr_of_ptts) ? *pi_chapter : 0;
#undef tt_srpt
}
/*****************************************************************************
* ParseCL: parse command line
*****************************************************************************/
static char *ParseCL( vlc_object_t *p_this, char *psz_name, vlc_bool_t b_force,
int *i_title, int *i_chapter, int *i_angle )
{
char *psz_parser, *psz_source, *psz_next;
psz_source = strdup( psz_name );
if( psz_source == NULL ) return NULL;
*i_title = 0;
*i_chapter = 0;
*i_angle = 1;
/* Start with the end, because you could have :
* dvdnav:/Volumes/my@toto/VIDEO_TS@1,1
* (yes, this is kludgy). */
for( psz_parser = psz_source + strlen(psz_source) - 1;
psz_parser >= psz_source && *psz_parser != '@';
psz_parser-- );
if( psz_parser >= psz_source && *psz_parser == '@' )
{
/* Found options */
*psz_parser = '\0';
++psz_parser;
*i_title = (int)strtol( psz_parser, &psz_next, 10 );
if( *psz_next )
{
psz_parser = psz_next + 1;
*i_chapter = (int)strtol( psz_parser, &psz_next, 10 );
if( *psz_next )
{
*i_angle = (int)strtol( psz_next + 1, NULL, 10 );
}
}
}
*i_title = *i_title >= 0 ? *i_title : 0;
*i_chapter = *i_chapter ? *i_chapter : 0;
*i_angle = *i_angle ? *i_angle : 1;
if( !*psz_source )
{
free( psz_source );
if( !b_force )
{
return NULL;
}
psz_source = config_GetPsz( p_this, "dvd" );
if( !psz_source ) return NULL;
}
#ifdef WIN32
if( psz_source[0] && psz_source[1] == ':' &&
psz_source[2] == '\\' && psz_source[3] == '\0' )
{
psz_source[2] = '\0';
}
#endif
msg_Dbg( p_this, "dvdroot=%s title=%d chapter=%d angle=%d",
psz_source, *i_title, *i_chapter, *i_angle );
return psz_source;
}
SOURCES_dvdread = \
dvdread.c \
input.c \
input.h \
$(NULL)
/*****************************************************************************
* dvdread.c : DvdRead input module for vlc
*****************************************************************************
* Copyright (C) 2001 VideoLAN
* $Id: dvdread.c,v 1.7 2003/06/17 16:09:16 gbazin Exp $
*
* Authors: Samuel Hocevar <sam@zoy.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 <stdlib.h> /* malloc(), free() */
#include <string.h> /* strdup() */
#include <vlc/vlc.h>
/*****************************************************************************
* External prototypes
*****************************************************************************/
int E_(OpenDVD) ( vlc_object_t * );
void E_(CloseDVD) ( vlc_object_t * );
int E_(InitDVD) ( vlc_object_t * );
void E_(EndDVD) ( vlc_object_t * );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("DVD input (using libdvdread)") );
set_capability( "access", 110 );
set_callbacks( E_(OpenDVD), E_(CloseDVD) );
add_submodule();
set_capability( "demux", 0 );
set_callbacks( E_(InitDVD), E_(EndDVD) );
vlc_module_end();
/*****************************************************************************
* input.c: DvdRead plugin.
*****************************************************************************
* This plugins should handle all the known specificities of the DVD format,
* especially the 2048 bytes logical block size.
* It depends on: libdvdread for ifo files and block reading.
*****************************************************************************
* Copyright (C) 2001, 2003 VideoLAN
* $Id: input.c,v 1.24 2004/03/03 20:39:51 gbazin Exp $
*
* Author: Stphane Borel <stef@via.ecp.fr>
*
* Some code taken form the play_title.c by Billy Biggs <vektor@dumbterm.net>
* in libdvdread.
*
* 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 <stdio.h>
#include <stdlib.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "../../demux/mpeg/system.h"
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#ifdef STRNCASECMP_IN_STRINGS_H
# include <strings.h>
#endif
#if defined( WIN32 )
# include <io.h> /* read() */
#endif
#include <dvdread/dvd_reader.h>
#include <dvdread/ifo_types.h>
#include <dvdread/ifo_read.h>
#include <dvdread/nav_read.h>
#include <dvdread/nav_print.h>
#include "input.h"
#include "iso_lang.h"
/* how many blocks DVDRead will read in each loop */
#define DVD_BLOCK_READ_ONCE 64
/*****************************************************************************
* Private structure
*****************************************************************************/
struct demux_sys_t
{
module_t * p_module;
mpeg_demux_t mpeg;
};
/*****************************************************************************
* Local prototypes
*****************************************************************************/
/* called from outside */
static int DvdReadDemux ( input_thread_t * );
static int DvdReadRewind ( input_thread_t * );
static int DvdReadSetArea ( input_thread_t *, input_area_t * );
static int DvdReadSetProgram ( input_thread_t *, pgrm_descriptor_t * );
static ssize_t DvdReadRead ( input_thread_t *, byte_t *, size_t );
static void DvdReadSeek ( input_thread_t *, off_t );
/* called only from here */
static void DvdReadLauchDecoders( input_thread_t * p_input );
static void DvdReadHandleDSI( thread_dvd_data_t * p_dvd, uint8_t * p_data );
static void DvdReadFindCell ( thread_dvd_data_t * p_dvd );
/*
* Data demux functions
*/
/*****************************************************************************
* InitDVD: initialize DVD structures
*****************************************************************************/
int E_(InitDVD) ( vlc_object_t *p_this )
{
input_thread_t *p_input = (input_thread_t *)p_this;
demux_sys_t * p_demux;
if( p_input->stream.i_method != INPUT_METHOD_DVD )
{
return VLC_EGENERIC;
}
p_demux = p_input->p_demux_data = malloc( sizeof(demux_sys_t ) );
if( p_demux == NULL )
{
return VLC_ENOMEM;
}
p_input->p_private = (void*)&p_demux->mpeg;
p_demux->p_module = module_Need( p_input, "mpeg-system", NULL, 0 );
if( p_demux->p_module == NULL )
{
free( p_input->p_demux_data );
return VLC_ENOMOD;
}
p_input->pf_demux = DvdReadDemux;
p_input->pf_demux_control = demux_vaControlDefault;
p_input->pf_rewind = NULL;
vlc_mutex_lock( &p_input->stream.stream_lock );
DvdReadLauchDecoders( p_input );
vlc_mutex_unlock( &p_input->stream.stream_lock );
return VLC_SUCCESS;
}
/*****************************************************************************
* EndDVD: end DVD structures
*****************************************************************************/
void E_(EndDVD) ( vlc_object_t *p_this )
{
input_thread_t *p_input = (input_thread_t *)p_this;
module_Unneed( p_input, p_input->p_demux_data->p_module );
free( p_input->p_demux_data );
}
/*****************************************************************************
* DvdReadDemux
*****************************************************************************/
#define PEEK( SIZE ) \
i_result = input_Peek( p_input, &p_peek, SIZE ); \
if( i_result == -1 ) \
{ \
return -1; \
} \
else if( i_result < SIZE ) \
{ \
/* EOF */ \
return 0; \
}
static int DvdReadDemux( input_thread_t * p_input )
{
int i;
byte_t * p_peek;
data_packet_t * p_data;
ssize_t i_result;
int i_packet_size;
/* Read headers to compute payload length */
for( i = 0 ; i < DVD_BLOCK_READ_ONCE ; i++ )
{
/* Read what we believe to be a packet header. */
PEEK( 4 );
/* Default header */
if( U32_AT( p_peek ) != 0x1BA )
{
/* That's the case for all packets, except pack header. */
i_packet_size = U16_AT( p_peek + 4 );
}
else
{
/* MPEG-2 Pack header. */
i_packet_size = 8;
}
/* Fetch a packet of the appropriate size. */
i_result = input_SplitBuffer( p_input, &p_data, i_packet_size + 6 );
if( i_result <= 0 )
{
return( i_result );
}
/* In MPEG-2 pack headers we still have to read stuffing bytes. */
if( (p_data->p_demux_start[3] == 0xBA) && (i_packet_size == 8) )
{
ssize_t i_stuffing = (p_data->p_demux_start[13] & 0x7);
/* Force refill of the input buffer - though we don't care
* about p_peek. Please note that this is unoptimized. */
PEEK( i_stuffing );
p_input->p_current_data += i_stuffing;
}
p_input->p_demux_data->mpeg.pf_demux_ps( p_input, p_data );
}
return i;
}
/*****************************************************************************
* DVDRewind : reads a stream backward
*****************************************************************************/
static int DvdReadRewind( input_thread_t * p_input )
{
return -1;
}
/*
* Data access functions
*/
/*****************************************************************************
* OpenDVD: open libdvdread
*****************************************************************************/
int E_(OpenDVD) ( vlc_object_t *p_this )
{
input_thread_t * p_input = (input_thread_t *)p_this;
char * psz_parser;
char * psz_source;
char * psz_next;
struct stat stat_info;
thread_dvd_data_t * p_dvd;
dvd_reader_t * p_dvdread;
input_area_t * p_area;
unsigned int i_title = 1;
unsigned int i_chapter = 1;
unsigned int i_angle = 1;
unsigned int i;
psz_source = strdup( p_input->psz_name );
if( psz_source == NULL )
{
return VLC_ENOMEM;
}
p_input->pf_read = DvdReadRead;
p_input->pf_seek = DvdReadSeek;
p_input->pf_set_area = DvdReadSetArea;
p_input->pf_set_program = DvdReadSetProgram;
/* Start with the end, because you could have :
* dvdread:/Volumes/my@toto/VIDEO_TS@1,1
* (yes, this is kludgy). */
for ( psz_parser = psz_source + strlen(psz_source) - 1;
psz_parser >= psz_source && *psz_parser != '@';
psz_parser-- );
if( psz_parser >= psz_source && *psz_parser == '@' )
{
/* Found options */
*psz_parser = '\0';
++psz_parser;
i_title = (int)strtol( psz_parser, &psz_next, 10 );
if( *psz_next )
{
psz_parser = psz_next + 1;
i_chapter = (int)strtol( psz_parser, &psz_next, 10 );
if( *psz_next )
{
i_angle = (int)strtol( psz_next + 1, NULL, 10 );
}
}
i_title = i_title ? i_title : 1;
i_chapter = i_chapter ? i_chapter : 1;
i_angle = i_angle ? i_angle : 1;
}
if( !*psz_source )
{
free( psz_source );
if( !p_input->psz_access )
{
return VLC_EGENERIC;
}
psz_source = config_GetPsz( p_input, "dvd" );
if( !psz_source ) return VLC_EGENERIC;
}
if( stat( psz_source, &stat_info ) == -1 )
{
msg_Warn( p_input, "cannot stat() source `%s' (%s)",
psz_source, strerror(errno) );
free( psz_source );
return VLC_EGENERIC;
}
if( !S_ISBLK(stat_info.st_mode) &&
!S_ISCHR(stat_info.st_mode) &&
!S_ISDIR(stat_info.st_mode) )
{
msg_Warn( p_input, "dvdread module discarded (not a valid source)" );
free( psz_source );
return VLC_EGENERIC;
}
msg_Dbg( p_input, "dvdroot=%s title=%d chapter=%d angle=%d",
psz_source, i_title, i_chapter, i_angle );
p_dvdread = DVDOpen( psz_source );
/* free allocated strings */
free( psz_source );
if( ! p_dvdread )
{
msg_Err( p_input, "libdvdcss cannot open source" );
return VLC_EGENERIC;
}
/* set up input */
p_input->i_mtu = 0;
p_dvd = malloc( sizeof(thread_dvd_data_t) );
if( p_dvd == NULL )
{
msg_Err( p_input, "out of memory" );
return VLC_ENOMEM;
}
p_dvd->p_dvdread = p_dvdread;
p_dvd->p_title = NULL;
p_dvd->p_vts_file = NULL;
p_input->p_access_data = (void *)p_dvd;
/* Ifo allocation & initialisation */
if( ! ( p_dvd->p_vmg_file = ifoOpen( p_dvd->p_dvdread, 0 ) ) )
{
msg_Warn( p_input, "cannot open VMG info" );
free( p_dvd );
return VLC_EGENERIC;
}
msg_Dbg( p_input, "VMG opened" );
/* Set stream and area data */
vlc_mutex_lock( &p_input->stream.stream_lock );
p_input->stream.i_method = INPUT_METHOD_DVD;
/* If we are here we can control the pace... */
p_input->stream.b_pace_control = VLC_TRUE;
p_input->stream.b_seekable = VLC_TRUE;
p_input->stream.p_selected_area->i_size = 0;
p_input->stream.p_selected_area->i_tell = 0;
/* Initialize ES structures */
input_InitStream( p_input, sizeof( stream_ps_data_t ) );
/* disc input method */
p_input->stream.i_method = INPUT_METHOD_DVD;
#define tt_srpt p_dvd->p_vmg_file->tt_srpt
msg_Dbg( p_input, "number of titles: %d", tt_srpt->nr_of_srpts );
#define area p_input->stream.pp_areas
/* We start from 1 here since the default area 0
* is reserved for video_ts.vob */
for( i = 1 ; i <= tt_srpt->nr_of_srpts ; i++ )
{
/* Titles are Program Chains */
input_AddArea( p_input, i, tt_srpt->title[i-1].nr_of_ptts );
/* Absolute start offset and size
* We can only set that with vts ifo, so we do it during the
* first call to DVDSetArea */
area[i]->i_start = 0;
area[i]->i_size = 0;
/* Default Chapter */
area[i]->i_part = 1;
area[i]->i_plugin_data = tt_srpt->title[i-1].title_set_nr;
}
#undef area
p_dvd->i_title = i_title <= tt_srpt->nr_of_srpts ? i_title : 1;
#undef tt_srpt
p_area = p_input->stream.pp_areas[p_dvd->i_title];
p_dvd->i_chapter = i_chapter;
p_dvd->i_chapter = i_chapter < p_area->i_part_nb ? i_chapter : 1;
p_area->i_part = p_dvd->i_chapter;
p_dvd->i_angle = i_angle;
/* set title, chapter, audio and subpic */
if( DvdReadSetArea( p_input, p_area ) )
{
vlc_mutex_unlock( &p_input->stream.stream_lock );
return VLC_EGENERIC;
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
if( !p_input->psz_demux || !*p_input->psz_demux )
{
p_input->psz_demux = "dvdread";
}
return VLC_SUCCESS;
}
/*****************************************************************************
* CloseDVD: close libdvdread
*****************************************************************************/
void E_(CloseDVD) ( vlc_object_t *p_this )
{
input_thread_t * p_input = (input_thread_t *)p_this;
thread_dvd_data_t * p_dvd = (thread_dvd_data_t *)p_input->p_access_data;
/* This is a very nasty side-effect in the DVD plug-in : language
* selection here influences language selection of other streams. So
* unset those variables (may not be what the user wants).
* FIXME FIXME FIXME FIXME FIXME FIXME FIXME --Meuuh */
config_PutInt( p_input, "audio-channel", -1 );
config_PutInt( p_input, "spu-channel", -1 );
/* close libdvdread */
DVDCloseFile( p_dvd->p_title );
ifoClose( p_dvd->p_vts_file );
ifoClose( p_dvd->p_vmg_file );
DVDClose( p_dvd->p_dvdread );
free( p_dvd );
p_input->p_access_data = NULL;
}
/*****************************************************************************
* DvdReadSetProgram: Does nothing, a DVD is mono-program
*****************************************************************************/
static int DvdReadSetProgram( input_thread_t * p_input,
pgrm_descriptor_t * p_program )
{
if( p_input->stream.p_selected_program != p_program )
{
thread_dvd_data_t * p_dvd;
vlc_value_t val;
p_dvd = (thread_dvd_data_t*)(p_input->p_access_data);
p_dvd->i_angle = p_program->i_number;
memcpy( p_program, p_input->stream.p_selected_program,
sizeof(pgrm_descriptor_t) );
p_program->i_number = p_dvd->i_angle;
p_input->stream.p_selected_program = p_program;
msg_Dbg( p_input, "angle %d selected", p_dvd->i_angle );
/* Update the navigation variables without triggering a callback */
val.i_int = p_program->i_number;
var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
}
return VLC_SUCCESS;
}
#define p_pgc p_dvd->p_cur_pgc
/*****************************************************************************
* DvdReadSetArea: initialize input data for title x, chapter y.
* It should be called for each user navigation request.
*****************************************************************************
* Take care that i_title starts from 0 (vmg) and i_chapter start from 1.
* Note that you have to take the lock before entering here.
*****************************************************************************/
static int DvdReadSetArea( input_thread_t * p_input, input_area_t * p_area )
{
thread_dvd_data_t * p_dvd;
int pgc_id = 0;
int pgn = 0;
vlc_value_t val;
p_dvd = (thread_dvd_data_t*)p_input->p_access_data;
/* we can't use the interface slider until initilization is complete */
p_input->stream.b_seekable = VLC_FALSE;
if( p_area != p_input->stream.p_selected_area )
{
es_descriptor_t * p_es;
unsigned int i_cell = 0;
unsigned int i_audio_nb = 0;
unsigned int i_spu_nb = 0;
unsigned int i;
#define p_vmg p_dvd->p_vmg_file
#define p_vts p_dvd->p_vts_file
if( p_dvd->p_title != NULL )
{
DVDCloseFile( p_dvd->p_title );
}
if( p_vts != NULL )
{
ifoClose( p_vts );
}
/* Reset the Chapter position of the old title */
p_input->stream.p_selected_area->i_part = 1;
/*
* We have to load all title information
*/
/* Change the default area */
p_input->stream.p_selected_area = p_area;
msg_Dbg( p_input, "open VTS %d, for title %d",
p_vmg->tt_srpt->title[ p_area->i_id - 1 ].title_set_nr,
p_area->i_id );
/* ifo vts */
if( ! ( p_vts = ifoOpen( p_dvd->p_dvdread,
p_vmg->tt_srpt->title[ p_area->i_id - 1 ].title_set_nr ) ) )
{
msg_Err( p_input, "fatal error in vts ifo" );
ifoClose( p_vmg );
DVDClose( p_dvd->p_dvdread );
return VLC_EGENERIC;
}
/* title position inside the selected vts */
p_dvd->i_ttn = p_vmg->tt_srpt->title[ p_area->i_id - 1 ].vts_ttn;
/*
* Set selected title start
*/
pgc_id = p_vts->vts_ptt_srpt->title[p_dvd->i_ttn-1].ptt[0].pgcn;
pgn = p_vts->vts_ptt_srpt->title[p_dvd->i_ttn-1].ptt[0].pgn;
p_pgc = p_vts->vts_pgcit->pgci_srp[ pgc_id - 1 ].pgc;
i_cell = p_pgc->program_map[ pgn - 1 ] - 1;
p_area->i_start =
LB2OFF( p_dvd->p_cur_pgc->cell_playback[ i_cell ].first_sector );
msg_Dbg( p_input, "start %d vts_title %d pgc %d pgn %d",
p_area->i_id, p_dvd->i_ttn, pgc_id, pgn );
/*
* Find title end
*/
i_cell = p_dvd->p_cur_pgc->nr_of_cells - 1;
p_dvd->i_end_block = p_pgc->cell_playback[ i_cell ].last_sector;
p_area->i_size = LB2OFF( p_dvd->i_end_block )- p_area->i_start;
msg_Dbg( p_input, "start "I64Fd" size "I64Fd" end %d",
p_area->i_start , p_area->i_size, p_dvd->i_end_block );
/*
* Set properties for current chapter
*/
/* Remeber current chapter */
p_dvd->i_chapter = p_area->i_part;
p_dvd->b_eoc = VLC_FALSE;
pgc_id = p_vts->vts_ptt_srpt->title[
p_dvd->i_ttn-1].ptt[p_area->i_part-1].pgcn;
pgn = p_vts->vts_ptt_srpt->title[
p_dvd->i_ttn-1].ptt[p_area->i_part-1].pgn;
p_pgc = p_vts->vts_pgcit->pgci_srp[pgc_id-1].pgc;
p_dvd->i_pack_len = 0;
p_dvd->i_next_cell = p_dvd->i_cur_cell = p_pgc->program_map[pgn-1] - 1;
DvdReadFindCell( p_dvd );
p_dvd->i_next_vobu = p_dvd->i_cur_block =
p_pgc->cell_playback[p_dvd->i_cur_cell].first_sector;
/*
* Angle management
*/
p_dvd->i_angle_nb = p_vmg->tt_srpt->title[p_area->i_id-1].nr_of_angles;
if( p_dvd->i_angle > p_dvd->i_angle_nb )
{
p_dvd->i_angle = 1;
}
/*
* We've got enough info, time to open the title set data.
*/
if( ! ( p_dvd->p_title = DVDOpenFile( p_dvd->p_dvdread,
p_vmg->tt_srpt->title[ p_area->i_id - 1 ].title_set_nr,
DVD_READ_TITLE_VOBS ) ) )
{
msg_Err( p_input, "cannot open title (VTS_%02d_1.VOB)",
p_vmg->tt_srpt->title[p_area->i_id-1].title_set_nr );
ifoClose( p_vts );
ifoClose( p_vmg );
DVDClose( p_dvd->p_dvdread );
return VLC_EGENERIC;
}
// IfoPrintTitle( p_dvd );
/*
* Destroy obsolete ES by reinitializing program 0
* and find all ES in title with ifo data
*/
if( p_input->stream.pp_programs != NULL )
{
/* We don't use input_EndStream here since
* we keep area structures */
while( p_input->stream.i_es_number )
{
input_DelES( p_input, p_input->stream.pp_es[0] );
}
while( p_input->stream.i_pgrm_number )
{
input_DelProgram( p_input, p_input->stream.pp_programs[0] );
}
if( p_input->stream.pp_selected_es )
{
free( p_input->stream.pp_selected_es );
p_input->stream.pp_selected_es = NULL;
}
p_input->stream.i_selected_es_number = 0;
}
input_AddProgram( p_input, 1, sizeof( stream_ps_data_t ) );
p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
for( i = 1 ; i < p_dvd->i_angle_nb ; i++ )
{
input_AddProgram( p_input, i+1, 0 );
}
DvdReadSetProgram( p_input,
p_input->stream.pp_programs[p_dvd->i_angle-1] );
/* No PSM to read in DVD mode, we already have all information */
p_input->stream.p_selected_program->b_is_ok = VLC_TRUE;
p_es = NULL;
/* ES 0 -> video MPEG2 */
// IfoPrintVideo( p_dvd );
p_es = input_AddES( p_input, NULL, 0xe0, VIDEO_ES, NULL, 0 );
p_es->i_stream_id = 0xe0;
p_es->i_fourcc = VLC_FOURCC('m','p','g','v');
#define audio_control \
p_dvd->p_vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc->audio_control[i-1]
/* Audio ES, in the order they appear in .ifo */
for( i = 1 ; i <= p_vts->vtsi_mat->nr_of_vts_audio_streams ; i++ )
{
int i_position = 0;
uint16_t i_id;
// IfoPrintAudio( p_dvd, i );
/* audio channel is active if first byte is 0x80 */
if( audio_control & 0x8000 )
{
i_audio_nb++;
i_position = ( audio_control & 0x7F00 ) >> 8;
msg_Dbg( p_input, "audio position %d", i_position );
switch( p_vts->vtsi_mat->vts_audio_attr[i-1].audio_format )
{
case 0x00: /* A52 */
i_id = ( ( 0x80 + i_position ) << 8 ) | 0xbd;
p_es = input_AddES( p_input, NULL, i_id, AUDIO_ES,
DecodeLanguage(
p_vts->vtsi_mat->vts_audio_attr[i-1].lang_code ), 0 );
p_es->i_stream_id = 0xbd;
p_es->i_fourcc = VLC_FOURCC('a','5','2','b');
break;
case 0x02:
case 0x03: /* MPEG audio */
i_id = 0xc0 + i_position;
p_es = input_AddES( p_input, NULL, i_id, AUDIO_ES,
DecodeLanguage(
p_vts->vtsi_mat->vts_audio_attr[i-1].lang_code ), 0 );
p_es->i_stream_id = i_id;
p_es->i_fourcc = VLC_FOURCC('m','p','g','a');
break;
case 0x04: /* LPCM */
i_id = ( ( 0xa0 + i_position ) << 8 ) | 0xbd;
p_es = input_AddES( p_input, NULL, i_id, AUDIO_ES,
DecodeLanguage(
p_vts->vtsi_mat->vts_audio_attr[i-1].lang_code ), 0 );
p_es->i_stream_id = i_id;
p_es->i_fourcc = VLC_FOURCC('l','p','c','b');
break;
case 0x06: /* DTS */
i_id = ( ( 0x88 + i_position ) << 8 ) | 0xbd;
msg_Err( p_input, "DTS audio not handled yet"
"(0x%x)", i_id );
break;
default:
i_id = 0;
msg_Err( p_input, "unknown audio type %.2x",
p_vts->vtsi_mat->vts_audio_attr[i-1].audio_format );
}
}
}
#undef audio_control
#define spu_control \
p_dvd->p_vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc->subp_control[i-1]
/* Sub Picture ES */
for( i = 1 ; i <= p_vts->vtsi_mat->nr_of_vts_subp_streams; i++ )
{
int i_position = 0;
uint16_t i_id;
// IfoPrintSpu( p_dvd, i );
msg_Dbg( p_input, "spu %d 0x%02x", i, spu_control );
if( spu_control & 0x80000000 )
{
i_spu_nb++;
/* there are several streams for one spu */
if( p_vts->vtsi_mat->vts_video_attr.display_aspect_ratio )
{
/* 16:9 */
switch( p_vts->vtsi_mat->vts_video_attr.permitted_df )
{
case 1:
i_position = spu_control & 0xff;
break;
case 2:
i_position = ( spu_control >> 8 ) & 0xff;
break;
default:
i_position = ( spu_control >> 16 ) & 0xff;
break;
}
}
else
{
/* 4:3 */
i_position = ( spu_control >> 24 ) & 0x7F;
}
i_id = ( ( 0x20 + i_position ) << 8 ) | 0xbd;
p_es = input_AddES( p_input, NULL, i_id, SPU_ES,
DecodeLanguage(
p_vts->vtsi_mat->vts_subp_attr[i-1].lang_code ), 0 );
p_es->i_stream_id = 0xbd;
p_es->i_fourcc = VLC_FOURCC('s','p','u','b');
}
}
#undef spu_control
/* FIXME: hack to check that the demuxer is ready, and set
* the decoders */
if( p_input->p_demux )
{
DvdReadLauchDecoders( p_input );
}
/* Update the navigation variables without triggering a callback */
val.i_int = p_area->i_id;
var_Change( p_input, "title", VLC_VAR_SETVALUE, &val, NULL );
var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
for( i = 1; i <= p_area->i_part_nb; i++ )
{
val.i_int = i;
var_Change( p_input, "chapter", VLC_VAR_ADDCHOICE, &val, NULL );
}
} /* i_title >= 0 */
else
{
p_area = p_input->stream.p_selected_area;
}
/*
* Chapter selection
*/
if( p_area->i_part != p_dvd->i_chapter )
{
if( ( p_area->i_part > 0 ) &&
( p_area->i_part <= p_area->i_part_nb ))
{
p_dvd->i_ttn = p_vmg->tt_srpt->title[p_area->i_id-1].vts_ttn;
pgc_id = p_vts->vts_ptt_srpt->title[
p_dvd->i_ttn-1].ptt[p_area->i_part-1].pgcn;
pgn = p_vts->vts_ptt_srpt->title[
p_dvd->i_ttn-1].ptt[p_area->i_part-1].pgn;
p_pgc = p_vts->vts_pgcit->pgci_srp[ pgc_id - 1 ].pgc;
p_dvd->i_cur_cell = p_pgc->program_map[ pgn - 1 ] - 1;
p_dvd->i_chapter = p_area->i_part;
DvdReadFindCell( p_dvd );
p_dvd->i_pack_len = 0;
p_dvd->i_next_vobu = p_dvd->i_cur_block =
p_pgc->cell_playback[p_dvd->i_cur_cell].first_sector;
}
else
{
p_area->i_part = p_dvd->i_chapter;
}
}
#undef p_vts
#undef p_vmg
/* warn interface that something has changed */
p_area->i_tell = LB2OFF( p_dvd->i_next_vobu ) - p_area->i_start;
p_input->stream.b_seekable = VLC_TRUE;
p_input->stream.b_changed = VLC_TRUE;
/* Update the navigation variables without triggering a callback */
val.i_int = p_area->i_part;
var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val, NULL );
return VLC_SUCCESS;
}
/*****************************************************************************
* DvdReadRead: reads data packets.
*****************************************************************************
* Returns -1 in case of error, 0 in case of EOF, otherwise the number of
* bytes.
*****************************************************************************/
static ssize_t DvdReadRead( input_thread_t * p_input,
byte_t * p_buffer, size_t i_count )
{
thread_dvd_data_t * p_dvd;
byte_t * p_buf;
unsigned int i_blocks_once;
unsigned int i_blocks;
int i_read;
int i_read_total;
vlc_bool_t b_eot = VLC_FALSE;
p_dvd = (thread_dvd_data_t *)p_input->p_access_data;
p_buf = p_buffer;
/*
* Playback by cell in this pgc, starting at the cell for our chapter.
*/
i_blocks = OFF2LB( i_count );
i_read_total = 0;
i_read = 0;
while( i_blocks )
{
/*
* End of pack, we select the following one
*/
if( ! p_dvd->i_pack_len )
{
/*
* Read NAV packet.
*/
if( ( i_read = DVDReadBlocks( p_dvd->p_title, p_dvd->i_next_vobu,
1, p_buf ) ) != 1 )
{
msg_Err( p_input, "read failed for block %d",
p_dvd->i_next_vobu );
return -1;
}
/* basic check to be sure we don't have a empty title
* go to next title if so */
//assert( p_buffer[41] == 0xbf && p_buffer[1027] == 0xbf );
/*
* Parse the contained dsi packet.
*/
DvdReadHandleDSI( p_dvd, p_buf );
/* End of title */
if( p_dvd->i_next_vobu >= p_dvd->i_end_block + 1 )
{
b_eot = 1;
break;
}
assert( p_dvd->i_pack_len < 1024 );
/* FIXME: Ugly kludge: we send the pack block to the input for it
* sometimes has a zero scr and restart the sync */
p_dvd->i_cur_block ++;
//p_dvd->i_pack_len++;
i_read_total++;
p_buf += DVD_VIDEO_LB_LEN;
i_blocks--;
}
/*
* Compute the number of blocks to read
*/
i_blocks_once = __MIN( p_dvd->i_pack_len, i_blocks );
p_dvd->i_pack_len -= i_blocks_once;
/* Reads from DVD */
i_read = DVDReadBlocks( p_dvd->p_title, p_dvd->i_cur_block,
i_blocks_once, p_buf );
if( (unsigned int)i_read != i_blocks_once )
{
msg_Err( p_input, "read failed for %d/%d blocks at 0x%02x",
i_read, i_blocks_once, p_dvd->i_cur_block );
return -1;
}
i_blocks -= i_read;
i_read_total += i_read;
p_dvd->i_cur_block += i_read;
p_buf += LB2OFF( i_read );
}
/*
msg_Dbg( p_input, "i_blocks: %d len: %d current: 0x%02x", i_read, p_dvd->i_pack_len, p_dvd->i_cur_block );
*/
vlc_mutex_lock( &p_input->stream.stream_lock );
if( p_dvd->b_eoc )
{
/* We modify i_part only at end of chapter not to erase
* some modification from the interface */
p_input->stream.p_selected_area->i_part = p_dvd->i_chapter;
p_dvd->b_eoc = VLC_FALSE;
}
if( ( LB2OFF( p_dvd->i_cur_block )
- p_input->stream.p_selected_area->i_start )
>= p_input->stream.p_selected_area->i_size || b_eot )
{
if( ( p_input->stream.p_selected_area->i_id + 1 ) >=
p_input->stream.i_area_nb )
{
/* EOF */
vlc_mutex_unlock( &p_input->stream.stream_lock );
return 0;
}
/* EOT */
msg_Dbg( p_input, "new title" );
DvdReadSetArea( p_input, p_input->stream.pp_areas[
p_input->stream.p_selected_area->i_id+1] );
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
return LB2OFF( i_read_total );
}
#undef p_pgc
/*****************************************************************************
* DvdReadSeek : Goes to a given position on the stream.
*****************************************************************************
* This one is used by the input and translate chronological position from
* input to logical position on the device.
* The lock should be taken before calling this function.
*****************************************************************************/
static void DvdReadSeek( input_thread_t * p_input, off_t i_off )
{
thread_dvd_data_t * p_dvd;
unsigned int i_lb;
unsigned int i_tmp;
unsigned int i_chapter = 0;
unsigned int i_cell = 0;
unsigned int i_vobu = 0;
unsigned int i_sub_cell = 0;
vlc_mutex_lock( &p_input->stream.stream_lock );
i_off += p_input->stream.p_selected_area->i_start;
vlc_mutex_unlock( &p_input->stream.stream_lock );
i_lb = OFF2LB( i_off );
p_dvd = ( thread_dvd_data_t * )p_input->p_access_data;
/* find cell */
while( p_dvd->p_cur_pgc->cell_playback[i_cell].last_sector < i_lb )
{
i_cell++;
}
/* find chapter */
do
{
pgc_t * p_pgc;
int pgc_id, pgn;
i_chapter++;
pgc_id = p_dvd->p_vts_file->vts_ptt_srpt->title[
p_dvd->i_ttn-1].ptt[i_chapter-1].pgcn;
pgn = p_dvd->p_vts_file->vts_ptt_srpt->title[
p_dvd->i_ttn-1].ptt[i_chapter-1].pgn;
p_pgc = p_dvd->p_vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc;
i_tmp = p_pgc->program_map[pgn-1];
} while( i_tmp <= i_cell );
/* find vobu */
while( p_dvd->p_vts_file->vts_vobu_admap->vobu_start_sectors[i_vobu]
<= i_lb )
{
i_vobu++;
}
/* find sub_cell */
while( p_dvd->p_vts_file->vts_c_adt->cell_adr_table[i_sub_cell].start_sector <
p_dvd->p_vts_file->vts_vobu_admap->vobu_start_sectors[i_vobu-1] )
{
i_sub_cell++;
}
/*
msg_Dbg( p_input, "cell %d i_sub_cell %d chapter %d vobu %d cell_sector %d vobu_sector %d sub_cell_sector %d",
i_cell, i_sub_cell,i_chapter, i_vobu,
p_dvd->p_cur_pgc->cell_playback[i_cell].first_sector,
p_dvd->p_vts_file->vts_vobu_admap->vobu_start_sectors[i_vobu],
p_dvd->p_vts_file->vts_c_adt->cell_adr_table[i_sub_cell-1].start_sector);
*/
p_dvd->i_cur_block = i_lb;
p_dvd->i_next_vobu =
p_dvd->p_vts_file->vts_vobu_admap->vobu_start_sectors[i_vobu];
p_dvd->i_pack_len = p_dvd->i_next_vobu - i_lb;
p_dvd->i_cur_cell = i_cell;
p_dvd->i_chapter = i_chapter;
DvdReadFindCell( p_dvd );
vlc_mutex_lock( &p_input->stream.stream_lock );
p_input->stream.p_selected_area->i_tell =
LB2OFF ( p_dvd->i_cur_block )
- p_input->stream.p_selected_area->i_start;
p_input->stream.p_selected_area->i_part = p_dvd->i_chapter;
vlc_mutex_unlock( &p_input->stream.stream_lock );
return;
}
/*****************************************************************************
* DvdReadHandleDSI
*****************************************************************************/
static void DvdReadHandleDSI( thread_dvd_data_t * p_dvd, uint8_t * p_data )
{
navRead_DSI( &(p_dvd->dsi_pack), &(p_data[ DSI_START_BYTE ]) );
/*
* Determine where we go next. These values are the ones we mostly
* care about.
*/
p_dvd->i_cur_block = p_dvd->dsi_pack.dsi_gi.nv_pck_lbn;
/*
* If we're not at the end of this cell, we can determine the next
* VOBU to display using the VOBU_SRI information section of the
* DSI. Using this value correctly follows the current angle,
* avoiding the doubled scenes in The Matrix, and makes our life
* really happy.
*/
if( p_dvd->dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL )
{
#if 1
switch( ( p_dvd->dsi_pack.sml_pbi.category & 0xf000 ) >> 12 )
{
case 0x4:
/* interleaved unit with no angle */
if( p_dvd->dsi_pack.sml_pbi.ilvu_sa != 0 )
{
p_dvd->i_next_vobu = p_dvd->i_cur_block +
p_dvd->dsi_pack.sml_pbi.ilvu_sa;
p_dvd->i_pack_len = p_dvd->dsi_pack.sml_pbi.ilvu_ea;
}
else
{
p_dvd->i_next_vobu = p_dvd->i_cur_block +
p_dvd->dsi_pack.dsi_gi.vobu_ea + 1;
p_dvd->i_pack_len = p_dvd->dsi_pack.dsi_gi.vobu_ea;
}
break;
case 0x5:
/* vobu is end of ilvu */
if( p_dvd->dsi_pack.sml_agli.data[p_dvd->i_angle-1].address )
{
p_dvd->i_next_vobu = p_dvd->i_cur_block +
p_dvd->dsi_pack.sml_agli.data[p_dvd->i_angle-1].address;
p_dvd->i_pack_len = p_dvd->dsi_pack.sml_pbi.ilvu_ea;
break;
}
case 0x6:
/* vobu is beginning of ilvu */
case 0x9:
/* next scr is 0 */
case 0xa:
/* entering interleaved section */
case 0x8:
/* non interleaved cells in interleaved section */
default:
p_dvd->i_next_vobu = p_dvd->i_cur_block +
( p_dvd->dsi_pack.vobu_sri.next_vobu & 0x7fffffff );
p_dvd->i_pack_len = p_dvd->dsi_pack.dsi_gi.vobu_ea;
break;
}
#else
p_dvd->i_next_vobu = p_dvd->i_cur_block +
( p_dvd->dsi_pack.vobu_sri.next_vobu & 0x7fffffff );
p_dvd->i_pack_len = p_dvd->dsi_pack.dsi_gi.vobu_ea;
#endif
}
else
{
p_dvd->i_cur_cell = p_dvd->i_next_cell;
DvdReadFindCell( p_dvd );
p_dvd->i_pack_len = p_dvd->dsi_pack.dsi_gi.vobu_ea;
p_dvd->i_next_vobu =
p_dvd->p_cur_pgc->cell_playback[p_dvd->i_cur_cell].first_sector;
}
#if 0
msg_Dbg( p_input, 12, "scr %d lbn 0x%02x vobu_ea %d vob_id %d c_id %d",
p_dvd->dsi_pack.dsi_gi.nv_pck_scr,
p_dvd->dsi_pack.dsi_gi.nv_pck_lbn,
p_dvd->dsi_pack.dsi_gi.vobu_ea,
p_dvd->dsi_pack.dsi_gi.vobu_vob_idn,
p_dvd->dsi_pack.dsi_gi.vobu_c_idn );
msg_Dbg( p_input, 12, "cat 0x%02x ilvu_ea %d ilvu_sa %d size %d",
p_dvd->dsi_pack.sml_pbi.category,
p_dvd->dsi_pack.sml_pbi.ilvu_ea,
p_dvd->dsi_pack.sml_pbi.ilvu_sa,
p_dvd->dsi_pack.sml_pbi.size );
msg_Dbg( p_input, 12, "next_vobu %d next_ilvu1 %d next_ilvu2 %d",
p_dvd->dsi_pack.vobu_sri.next_vobu & 0x7fffffff,
p_dvd->dsi_pack.sml_agli.data[ p_dvd->i_angle - 1 ].address,
p_dvd->dsi_pack.sml_agli.data[ p_dvd->i_angle ].address);
#endif
}
/*****************************************************************************
* DvdReadFindCell
*****************************************************************************/
static void DvdReadFindCell( thread_dvd_data_t * p_dvd )
{
int pgc_id, pgn;
int i = 0;
pgc_t * p_pgc;
#define cell p_dvd->p_cur_pgc->cell_playback
if( cell[p_dvd->i_cur_cell].block_type == BLOCK_TYPE_ANGLE_BLOCK )
{
#if 0
p_dvd->i_next_cell = p_dvd->i_cur_cell + p_dvd->i_angle_nb;
p_dvd->i_cur_cell += p_dvd->i_angle - 1;
#else
p_dvd->i_cur_cell += p_dvd->i_angle - 1;
while( cell[p_dvd->i_cur_cell+i].block_mode != BLOCK_MODE_LAST_CELL )
{
i++;
}
p_dvd->i_next_cell = p_dvd->i_cur_cell + i + 1;
#endif
}
else
{
p_dvd->i_next_cell = p_dvd->i_cur_cell + 1;
}
#undef cell
pgc_id = p_dvd->p_vts_file->vts_ptt_srpt->title[
p_dvd->i_ttn-1].ptt[p_dvd->i_chapter-1].pgcn;
pgn = p_dvd->p_vts_file->vts_ptt_srpt->title[
p_dvd->i_ttn-1].ptt[p_dvd->i_chapter-1].pgn;
p_pgc = p_dvd->p_vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc;
if( p_pgc->program_map[pgn-1] <= p_dvd->i_cur_cell )
{
p_dvd->i_chapter++;
p_dvd->b_eoc = VLC_TRUE;
}
}
/*****************************************************************************
* DvdReadLaunchDecoders
*****************************************************************************/
static void DvdReadLauchDecoders( input_thread_t * p_input )
{
thread_dvd_data_t * p_dvd = (thread_dvd_data_t*)(p_input->p_access_data);
int i_audio, i_spu;
input_SelectES( p_input, p_input->stream.pp_es[0] );
/* For audio: first one if none or a not existing one specified */
i_audio = config_GetInt( p_input, "audio-channel" );
if( i_audio < 0 /*|| i_audio > i_audio_nb*/ )
{
config_PutInt( p_input, "audio-channel", 1 );
i_audio = 1;
}
if( i_audio > 0/* && i_audio_nb > 0*/ )
{
if( config_GetInt( p_input, "audio-type" ) == REQUESTED_A52 )
{
int i_a52 = i_audio;
while( ( p_input->stream.pp_es[i_a52]->i_fourcc !=
VLC_FOURCC('a','5','2','b') ) && ( i_a52 <=
p_dvd->p_vts_file->vtsi_mat->nr_of_vts_audio_streams ) )
{
i_a52++;
}
if( p_input->stream.pp_es[i_a52]->i_fourcc
== VLC_FOURCC('a','5','2','b') )
{
input_SelectES( p_input, p_input->stream.pp_es[i_a52] );
}
}
else
{
input_SelectES( p_input, p_input->stream.pp_es[i_audio] );
}
}
/* for spu, default is none */
i_spu = config_GetInt( p_input, "spu-channel" );
if( i_spu < 0 /*|| i_spu > i_spu_nb*/ )
{
config_PutInt( p_input, "spu-channel", 0 );
i_spu = 0;
}
if( i_spu > 0 /*&& i_spu_nb > 0*/ )
{
i_spu += p_dvd->p_vts_file->vtsi_mat->nr_of_vts_audio_streams;
input_SelectES( p_input, p_input->stream.pp_es[i_spu] );
}
}
/*****************************************************************************
* input.h: thread structure of the DVD plugin
*****************************************************************************
* Copyright (C) 1999-2001 VideoLAN
* $Id: input.h,v 1.2 2003/01/23 15:52:04 sam Exp $
*
* Author: Stphane Borel <stef@via.ecp.fr>
*
* 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
*****************************************************************************/
/* Logical block size for DVD-VIDEO */
#define LB2OFF(x) ((off_t)(x) * (off_t)(DVD_VIDEO_LB_LEN))
#define OFF2LB(x) ((x) >> 11)
/*****************************************************************************
* thread_dvd_data_t: extension of input_thread_t for DVD specificity.
*****************************************************************************/
typedef struct thread_dvd_data_s
{
dvd_reader_t * p_dvdread;
dvd_file_t * p_title;
ifo_handle_t * p_vmg_file;
ifo_handle_t * p_vts_file;
unsigned int i_title;
unsigned int i_chapter;
unsigned int i_angle;
unsigned int i_angle_nb;
tt_srpt_t * p_tt_srpt;
pgc_t * p_cur_pgc;
dsi_t dsi_pack;
int i_ttn;
unsigned int i_pack_len;
unsigned int i_cur_block;
unsigned int i_next_vobu;
unsigned int i_end_block;
unsigned int i_cur_cell;
unsigned int i_next_cell;
vlc_bool_t b_eoc;
} thread_dvd_data_t;
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment