Commit a779f6f7 authored by Jean-Baptiste Kempf's avatar Jean-Baptiste Kempf

Remove BD access module

parent fd3e08b3
......@@ -238,6 +238,7 @@ Removed modules
* Mac OS X Dialog Provider (use VLCKit instead)
* iOS Dialog Provider (use MobileVLCKit / TVVLCKit instead)
* QuickTime decoder module (use native codecs)
* BD access module (use libbluray)
Changes between 2.2.0 and 2.2.1:
......
......@@ -5,7 +5,6 @@ $Id$
* aa: Ascii art video output
* access_alsa: Alsa access module
* access_archive: libarchive based access and stream filter
* access_bd: Blu-Ray Disc access
* access_concat: concatenated access
* access_eyetv: Access module to connect to our plugin running within EyeTV
* access_imem: memory bitstream access module
......
......@@ -294,10 +294,6 @@ liblibbluray_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(accessdir)'
access_LTLIBRARIES += $(LTLIBlibbluray)
EXTRA_LTLIBRARIES += liblibbluray_plugin.la
libaccess_bd_plugin_la_SOURCES = access/bd/bd.c access/bd/mpls.c access/bd/mpls.h access/bd/clpi.c access/bd/clpi.h
access_LTLIBRARIES += libaccess_bd_plugin.la
### Digital TV ###
libdtv_plugin_la_SOURCES = \
......
/*****************************************************************************
* bd.c: BluRay Disc support (uncrypted)
*****************************************************************************
* Copyright (C) 2009 VLC authors and VideoLAN
* $Id$
*
* Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <limits.h>
#include <sys/stat.h>
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_input.h>
#include <vlc_access.h>
#include <vlc_demux.h>
#include <vlc_fs.h>
#include <vlc_bits.h>
#include <assert.h>
#include "mpls.h"
#include "clpi.h"
/*****************************************************************************
* Module descriptor
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close( vlc_object_t * );
vlc_module_begin ()
set_shortname( N_("BD") )
set_description( N_("Blu-ray Disc Input") )
set_category( CAT_INPUT )
set_subcategory( SUBCAT_INPUT_ACCESS )
set_capability( "access_demux", 60 )
add_shortcut( "bd", "file" )
set_callbacks( Open, Close )
vlc_module_end ()
/*****************************************************************************
* Documentation
* I have used:
* - http://www.stebbins.biz/source/bdtools.tgz
* - hdcookbook java code
* - BDInfo source code
*****************************************************************************/
/*****************************************************************************
* Local prototypes
*****************************************************************************/
struct demux_sys_t
{
char *psz_base;
bool b_shortname;
/* */
int i_mpls;
bd_mpls_t **pp_mpls;
/* */
int i_clpi;
bd_clpi_t **pp_clpi;
/* */
int i_title;
input_title_t **pp_title;
/* */
es_out_t *p_out;
/* Current state */
const bd_clpi_t *p_clpi;
int i_clpi_ep;
vlc_demux_chained_t *p_parser;
stream_t *p_m2ts;
int i_play_item;
int i_packet;
int i_packet_start;
int i_packet_stop;
int i_packet_headers;
int64_t i_atc_initial;
int64_t i_atc_current;
int64_t i_atc_wrap;
int64_t i_atc_last;
};
static int Control( demux_t *, int, va_list );
static int Demux( demux_t * );
static char *FindPathBase( const char *, bool *pb_shortname );
static int LoadPlaylist( demux_t * );
static int LoadClip( demux_t * );
static void ReorderPlaylist( demux_t * );
static void InitTitles( demux_t * );
static int SetTitle( demux_t *, int );
static int SetChapter( demux_t *, int );
static int64_t GetTime( demux_t * );
static double GetPosition( demux_t * );
static int SetTime( demux_t *, int64_t );
static int SetPosition( demux_t *, double );
static int SetPlayItem( demux_t *p_demux, int i_mpls, int i_play_item );
static void ClosePlayItem( demux_t * );
/* */
static int64_t GetClpiPacket( demux_t *p_demux, int *pi_ep, const bd_mpls_clpi_t *p_mpls_clpi, int64_t i_time /* in 45kHz */ );
static es_out_t *EsOutNew( demux_t *p_demux );
//#define BD_DEBUG
/*****************************************************************************
* Open:
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
demux_t *p_demux = (demux_t*)p_this;
demux_sys_t *p_sys;
if( p_demux->psz_file == NULL )
return VLC_EGENERIC;
if( *p_demux->psz_access &&
strcmp( p_demux->psz_access, "bd" ) &&
strcmp( p_demux->psz_access, "file" ) )
return VLC_EGENERIC;
/* */
bool b_shortname;
char *psz_base = FindPathBase( p_demux->psz_file, &b_shortname );
if( !psz_base )
return VLC_EGENERIC;
msg_Dbg( p_demux, "Using path '%s'", psz_base );
/* Fill p_demux field */
p_demux->p_sys = p_sys = malloc( sizeof(*p_sys) );
if( !p_sys )
{
free( psz_base );
return VLC_EGENERIC;
}
p_sys->psz_base = psz_base;
p_sys->b_shortname = b_shortname;
TAB_INIT( p_sys->i_mpls, p_sys->pp_mpls );
TAB_INIT( p_sys->i_clpi, p_sys->pp_clpi );
TAB_INIT( p_sys->i_title, p_sys->pp_title );
p_demux->info.i_title = -1;
p_sys->p_clpi = NULL;
p_sys->i_clpi_ep = -1;
p_sys->p_parser = NULL;
p_sys->p_m2ts = NULL;
p_sys->i_play_item = -1;
p_sys->i_packet = -1;
p_sys->i_packet_start = -1;
p_sys->i_packet_stop = -1;
p_sys->i_packet_headers = -1;
p_sys->p_out = EsOutNew( p_demux );
if( !p_sys->p_out )
goto error;
p_demux->pf_control = Control;
p_demux->pf_demux = Demux;
/* Load all clip/playlist files */
LoadClip( p_demux );
LoadPlaylist( p_demux );
/* Reorder playlist to have the most significant first
* (as we don't have menu support, no idea how to find the main title */
ReorderPlaylist( p_demux );
/* Setup variables (for TS demuxer) */
var_Create( p_demux, "ts-es-id-pid", VLC_VAR_BOOL );
var_SetBool( p_demux, "ts-es-id-pid", true );
/* */
InitTitles( p_demux );
if( SetTitle( p_demux, 0 ) )
goto error;
return VLC_SUCCESS;
error:
Close( VLC_OBJECT(p_demux) );
return VLC_EGENERIC;
}
/*****************************************************************************
* 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;
/* */
ClosePlayItem( p_demux );
/* */
es_out_Delete( p_sys->p_out );
/* Titles */
for( int i = 0; i < p_sys->i_title; i++ )
vlc_input_title_Delete( p_sys->pp_title[i] );
TAB_CLEAN( p_sys->i_title, p_sys->pp_title );
/* CLPI */
for( int i = 0; i < p_sys->i_clpi; i++ )
{
bd_clpi_t *p_clpi = p_sys->pp_clpi[i];
bd_clpi_Clean( p_clpi );
free( p_clpi );
}
TAB_CLEAN( p_sys->i_clpi, p_sys->pp_clpi );
/* MPLS */
for( int i = 0; i < p_sys->i_mpls; i++ )
{
bd_mpls_t *p_mpls = p_sys->pp_mpls[i];
bd_mpls_Clean( p_mpls );
free( p_mpls );
}
TAB_CLEAN( p_sys->i_mpls, p_sys->pp_mpls );
free( p_sys->psz_base );
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;
switch( i_query )
{
case DEMUX_GET_TIME:
{
int64_t *pi_time = (int64_t*)va_arg( args, int64_t * );
*pi_time = GetTime( p_demux );
return VLC_SUCCESS;;
}
case DEMUX_GET_POSITION:
{
double *pf_position = (double*)va_arg( args, double * );
*pf_position = GetPosition( p_demux );
return VLC_SUCCESS;
}
case DEMUX_SET_TIME:
{
int64_t i_time = (int64_t)va_arg( args, int64_t );
return SetTime( p_demux, i_time );
}
case DEMUX_SET_POSITION:
{
double f_position = (double)va_arg( args, double );
return SetPosition( p_demux, f_position );
}
case DEMUX_GET_LENGTH:
{
int64_t *pi_length = (int64_t*)va_arg( args, int64_t * );
*pi_length = p_sys->pp_title[p_demux->info.i_title]->i_length;
return VLC_SUCCESS;
}
/* Special for access_demux */
case DEMUX_CAN_PAUSE:
case DEMUX_CAN_SEEK:
case DEMUX_CAN_CONTROL_PACE:
{
bool *pb_bool = (bool*)va_arg( args, bool * );
*pb_bool = true;
return VLC_SUCCESS;
}
case DEMUX_SET_PAUSE_STATE:
return VLC_SUCCESS;
case DEMUX_GET_TITLE_INFO:
{
input_title_t ***ppp_title = (input_title_t***)va_arg( args, input_title_t*** );
int *pi_int = (int*)va_arg( args, int* );
int *pi_title_offset = (int*)va_arg( args, int* );
int *pi_chapter_offset = (int*)va_arg( args, int* );
/* */
*pi_title_offset = 0;
*pi_chapter_offset = 0;
/* Duplicate title infos */
*pi_int = p_sys->i_title;
*ppp_title = calloc( p_sys->i_title, sizeof(input_title_t *) );
for( int i = 0; i < p_sys->i_title; i++ )
(*ppp_title)[i] = vlc_input_title_Duplicate( p_sys->pp_title[i] );
return VLC_SUCCESS;
}
case DEMUX_SET_TITLE:
{
int i_title = (int)va_arg( args, int );
if( SetTitle( p_demux, i_title ) )
return VLC_EGENERIC;
return VLC_SUCCESS;
}
case DEMUX_SET_SEEKPOINT:
{
int i_chapter = (int)va_arg( args, int );
if( SetChapter( p_demux, i_chapter ) )
return VLC_EGENERIC;
return VLC_SUCCESS;
}
case DEMUX_GET_PTS_DELAY:
{
int64_t *pi_delay = (int64_t*)va_arg( args, int64_t * );
*pi_delay =
INT64_C(1000) * var_InheritInteger( p_demux, "disc-caching" );
return VLC_SUCCESS;
}
case DEMUX_GET_META:
default:
return VLC_EGENERIC;
}
}
/*****************************************************************************
* Demux:
*****************************************************************************/
#define BD_TS_PACKET_HEADER (4)
#define BD_TS_PACKET_SIZE (192)
static int Demux( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
if( !p_sys->p_m2ts )
return -1;
/* */
if( p_sys->i_packet == p_sys->i_packet_start )
{
vlc_stream_Seek( p_sys->p_m2ts, 0 );
block_t *p_block = vlc_stream_Block( p_sys->p_m2ts,
p_sys->i_packet_headers * (int64_t)BD_TS_PACKET_SIZE + BD_TS_PACKET_HEADER );
if( p_block )
{
p_block->i_buffer -= BD_TS_PACKET_HEADER;
p_block->p_buffer += BD_TS_PACKET_HEADER;
vlc_demux_chained_Send( p_sys->p_parser, p_block );
}
vlc_stream_Seek( p_sys->p_m2ts, p_sys->i_packet_start * (int64_t)BD_TS_PACKET_SIZE );
}
/* */
const int i_packets = __MIN( 5, p_sys->i_packet_stop - p_sys->i_packet );
if( i_packets <= 0 )
{
const int i_title = p_demux->info.i_title;
const bd_mpls_t *p_mpls = p_sys->pp_mpls[i_title];
if( p_sys->i_play_item < p_mpls->i_play_item )
{
if( !SetPlayItem( p_demux, i_title, p_sys->i_play_item + 1 ) )
return 1;
msg_Warn( p_demux, "Failed to switch to the next play item" );
}
/* */
if( SetTitle( p_demux, i_title + 1 ) )
return 0; /* EOF */
return 1;
}
/* XXX
* we ensure that the TS packet start at the begining of the buffer,
* it ensure proper TS parsing */
block_t *p_block = block_Alloc( i_packets * BD_TS_PACKET_SIZE + BD_TS_PACKET_HEADER );
if( !p_block )
return -1;
const int i_read = vlc_stream_Read( p_sys->p_m2ts, p_block->p_buffer, p_block->i_buffer - BD_TS_PACKET_HEADER );
if( i_read <= 0 )
{
msg_Err( p_demux, "Error reading current title" );
return -1;
}
if( i_read > 4 )
{
const int64_t i_atc = GetDWBE( p_block->p_buffer ) & ( (1 << 30) - 1 );
if( i_atc < p_sys->i_atc_last )
p_sys->i_atc_wrap += 1 << 30;
p_sys->i_atc_last = i_atc;
if( p_sys->i_atc_initial < 0 )
p_sys->i_atc_initial = i_atc + p_sys->i_atc_wrap;
p_sys->i_atc_current = i_atc + p_sys->i_atc_wrap;
}
p_block->i_buffer = i_read;
p_block->p_buffer += BD_TS_PACKET_HEADER;
vlc_demux_chained_Send( p_sys->p_parser, p_block );
p_sys->i_packet += i_read / BD_TS_PACKET_SIZE;
/* Update EP */
if( p_sys->p_clpi->i_ep_map > 0 )
{
const int i_old_clpi_ep = p_sys->i_clpi_ep;
const bd_clpi_ep_map_t *p_ep_map = &p_sys->p_clpi->p_ep_map[0];
for( ; p_sys->i_clpi_ep+1 < p_ep_map->i_ep; p_sys->i_clpi_ep++ )
{
const bd_clpi_ep_t *p_ep = &p_ep_map->p_ep[p_sys->i_clpi_ep+1];
if( p_ep->i_packet > p_sys->i_packet )
break;
}
if( i_old_clpi_ep != p_sys->i_clpi_ep )
{
/* We have changed of EP */
p_sys->i_atc_initial = p_sys->i_atc_current; /* FIXME not exact */
/* Update seekpoint */
const input_title_t *p_title = p_sys->pp_title[p_demux->info.i_title];
const int64_t i_time = GetTime( p_demux );
for( ; p_demux->info.i_seekpoint+1 < p_title->i_seekpoint; p_demux->info.i_seekpoint++ )
{
const seekpoint_t *p_seekpoint = p_title->seekpoint[p_demux->info.i_seekpoint+1];
if( p_seekpoint->i_time_offset > i_time )
break;
p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
}
}
}
return 1;
}
/*****************************************************************************
*
*****************************************************************************/
#define BD_45KHZ INT64_C(45000)
static void InitTitles( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
/* */
for( int i = 0; i < p_sys->i_mpls; i++ )
{
const bd_mpls_t *p_mpls = p_sys->pp_mpls[i];
input_title_t *t = vlc_input_title_New();
if( !t )
break;
/* */
t->i_length = 0;
for( int j = 0; j < p_mpls->i_play_item; j++ )
{
const bd_mpls_play_item_t *p_item = &p_mpls->p_play_item[j];
t->i_length += ( p_item->i_out_time - p_item->i_in_time ) * CLOCK_FREQ / BD_45KHZ;
}
#ifdef BD_DEBUG
{
char psz_length[MSTRTIME_MAX_SIZE];
msg_Warn( p_demux, "TITLE[%d] %s", i, secstotimestr( psz_length, t->i_length / CLOCK_FREQ ) );
}
#endif
/* Seekpoint */
for( int j = 0; j < p_mpls->i_mark; j++ )
{
bd_mpls_mark_t *p_mark = &p_mpls->p_mark[j];
if( p_mark->i_type == BD_MPLS_MARK_TYPE_BOOKMARK &&
p_mark->i_play_item_id >= 0 && p_mark->i_play_item_id < p_mpls->i_play_item )
{
seekpoint_t *s = vlc_seekpoint_New();
if( !s )
break;
for( int k = 0; k <= p_mark->i_play_item_id; k++ )
{
const bd_mpls_play_item_t *p_item = &p_mpls->p_play_item[k];
int64_t i_out_time;
if( k == p_mark->i_play_item_id )
i_out_time = p_mark->i_time;
else
i_out_time = p_item->i_out_time;
s->i_time_offset += ( i_out_time - p_item->i_in_time ) * CLOCK_FREQ / BD_45KHZ;
}
#ifdef BD_DEBUG
{
char psz_time[MSTRTIME_MAX_SIZE];
msg_Warn( p_demux, " SEEKPOINT[%d] %s", j, secstotimestr( psz_time, s->i_time_offset / CLOCK_FREQ ) );
}
#endif
TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
}
}
if( t->i_seekpoint <= 0 )
{
seekpoint_t *s = vlc_seekpoint_New();
if( s )
TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
}
TAB_APPEND( p_sys->i_title, p_sys->pp_title, t );
}
}
static int SetTitle( demux_t *p_demux, int i_title )
{
demux_sys_t *p_sys = p_demux->p_sys;
if( i_title < 0 || i_title >= p_sys->i_title )
return VLC_EGENERIC;
if( SetPlayItem( p_demux, i_title, 0 ) )
return VLC_EGENERIC;
/* */
p_demux->info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
p_demux->info.i_title = i_title;
p_demux->info.i_seekpoint = 0;
return VLC_SUCCESS;
}
static int SetChapter( demux_t *p_demux, int i_chapter )
{
demux_sys_t *p_sys = p_demux->p_sys;
const int i_title = p_demux->info.i_title;
const input_title_t *p_title = p_sys->pp_title[i_title];
if( i_chapter < 0 || i_chapter > p_title->i_seekpoint )
return VLC_EGENERIC;
if( SetTime( p_demux, p_title->seekpoint[i_chapter]->i_time_offset ) )
return VLC_EGENERIC;
return VLC_SUCCESS;
}
static int SetPlayItem( demux_t *p_demux, int i_mpls, int i_play_item )
{
demux_sys_t *p_sys = p_demux->p_sys;
/* FIXME TODO do not reopen everything when avoidable
* XXX becarefull that then the es_out wrapper need some sort of
* locking !!! */
/* */
const bool b_same_mpls = i_mpls == p_demux->info.i_title;
//const bool b_same_play_item = b_same_mpls &&
// i_play_item == p_sys->i_play_item;
/* */
const bd_mpls_t *p_mpls = p_sys->pp_mpls[i_mpls];
/* */
if( i_play_item < 0 || i_play_item >= p_mpls->i_play_item )
return VLC_EGENERIC;
const bd_mpls_play_item_t *p_item = &p_mpls->p_play_item[i_play_item];
const bd_mpls_clpi_t *p_mpls_clpi = &p_item->clpi;
const bd_clpi_t *p_clpi = NULL;
for( int i_clpi = 0; i_clpi < p_sys->i_clpi && !p_clpi; i_clpi++ )
{
if( p_sys->pp_clpi[i_clpi]->i_id == p_mpls_clpi->i_id )
p_clpi = p_sys->pp_clpi[i_clpi];
}
assert(p_clpi);
const bool b_same_clpi = b_same_mpls && p_sys->p_clpi->i_id == p_clpi->i_id;
stream_t *p_m2ts = NULL;
if( !b_same_clpi )
{
char *psz_m2ts;
if( asprintf( &psz_m2ts, "%s/STREAM/%05d.%s",
p_sys->psz_base, p_mpls_clpi->i_id, p_sys->b_shortname ? "MTS" : "m2ts" ) < 0 )
return VLC_EGENERIC;
p_m2ts = vlc_stream_NewMRL( p_demux, psz_m2ts );
if( !p_m2ts )
{
msg_Err( p_demux, "Failed to open %s", psz_m2ts );
free( psz_m2ts );
return VLC_EGENERIC;
}
free( psz_m2ts );
}
/* TODO avoid reopenning the parser when unneeded.
* - b_same_play_item is too strict, we should check the play_items connection.
* - a way to completely flush the demuxer is also needed !
*/
//const bool b_same_parser = b_same_play_item && false;
vlc_demux_chained_t *p_parser = vlc_demux_chained_New( VLC_OBJECT(p_demux), "ts", p_sys->p_out );
if( !p_parser )
{
msg_Err( p_demux, "Failed to create TS demuxer" );
if( p_m2ts )
vlc_stream_Delete( p_m2ts );
return VLC_EGENERIC;
}
/* */
if( !p_m2ts )
{
msg_Dbg( p_demux, "Reusing stream file" );
p_m2ts = p_sys->p_m2ts;
p_sys->p_m2ts = NULL;
}
/* */
ClosePlayItem( p_demux );
/* */
p_sys->p_clpi = p_clpi;
p_sys->p_parser = p_parser;
p_sys->p_m2ts = p_m2ts;
p_sys->i_play_item = i_play_item;
p_sys->i_packet_start = GetClpiPacket( p_demux, &p_sys->i_clpi_ep, p_mpls_clpi, p_item->i_in_time );
if( p_sys->i_packet_start < 0 )
{
p_sys->i_packet_start = 0;
p_sys->i_clpi_ep = 0;
}
p_sys->i_packet_stop = GetClpiPacket( p_demux, NULL, p_mpls_clpi, p_item->i_out_time );
if( p_sys->i_packet_stop < 0 )
p_sys->i_packet_stop = stream_Size( p_m2ts ) / BD_TS_PACKET_SIZE;
p_sys->i_packet = p_sys->i_packet_start;
/* This is a hack to detect the number of packet to send before any data
* to have the PAT/PMT. I have no idea if it is the right, but seems to work.
* I used a limits of 10 packets, sufficient if it is really only headers */
p_sys->i_packet_headers = 0;
if( p_clpi->i_ep_map > 0 )
{
const bd_clpi_ep_map_t *p_ep_map = &p_clpi->p_ep_map[0];
if( p_ep_map->i_ep > 0 )
p_sys->i_packet_headers = __MIN( p_ep_map->p_ep[0].i_packet, 10 );
}
p_sys->i_atc_initial = -1;
p_sys->i_atc_current = -1;
p_sys->i_atc_last = -1;
p_sys->i_atc_wrap = 0;
return VLC_SUCCESS;
}
static void ClosePlayItem( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
if( p_sys->p_m2ts )
vlc_stream_Delete( p_sys->p_m2ts );
if( p_sys->p_parser )
vlc_demux_chained_Delete( p_sys->p_parser );
es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
}