Commit d6c80f22 authored by Christophe Massiot's avatar Christophe Massiot

* modules/access/dvb: Partial EN 50 221 implementation. This activates

   native support for CAM modules (without using an external program).
   When used in conjunction with --programs, it also allows to descramble
   several services with one professional CAM.
parent b2147ab9
SOURCES_dvb = \ SOURCES_dvb = \
access.c \ access.c \
linux_dvb.c \ linux_dvb.c \
en50221.c \
dvb.h \ dvb.h \
$(NULL) $(NULL)
...@@ -58,9 +58,6 @@ static void Close( vlc_object_t *p_this ); ...@@ -58,9 +58,6 @@ static void Close( vlc_object_t *p_this );
#define DEVICE_TEXT N_("Device number to use on adapter") #define DEVICE_TEXT N_("Device number to use on adapter")
#define DEVICE_LONGTEXT "" #define DEVICE_LONGTEXT ""
#define CAM_TEXT N_("Use CAM")
#define CAM_LONGTEXT ""
#define FREQ_TEXT N_("Transponder/multiplex frequency") #define FREQ_TEXT N_("Transponder/multiplex frequency")
#define FREQ_LONGTEXT N_("In kHz for DVB-S or Hz for DVB-C/T") #define FREQ_LONGTEXT N_("In kHz for DVB-S or Hz for DVB-C/T")
...@@ -131,7 +128,6 @@ vlc_module_begin(); ...@@ -131,7 +128,6 @@ vlc_module_begin();
VLC_FALSE ); VLC_FALSE );
add_integer( "dvb-device", 0, NULL, DEVICE_TEXT, DEVICE_LONGTEXT, add_integer( "dvb-device", 0, NULL, DEVICE_TEXT, DEVICE_LONGTEXT,
VLC_TRUE ); VLC_TRUE );
add_bool( "dvb-cam", 0, NULL, CAM_TEXT, CAM_LONGTEXT, VLC_FALSE );
add_integer( "dvb-frequency", 11954000, NULL, FREQ_TEXT, FREQ_LONGTEXT, add_integer( "dvb-frequency", 11954000, NULL, FREQ_TEXT, FREQ_LONGTEXT,
VLC_FALSE ); VLC_FALSE );
add_integer( "dvb-inversion", 2, NULL, INVERSION_TEXT, INVERSION_LONGTEXT, add_integer( "dvb-inversion", 2, NULL, INVERSION_TEXT, INVERSION_LONGTEXT,
...@@ -190,7 +186,7 @@ vlc_module_end(); ...@@ -190,7 +186,7 @@ vlc_module_end();
static block_t *Block( access_t * ); static block_t *Block( access_t * );
static int Control( access_t *, int, va_list ); static int Control( access_t *, int, va_list );
#define SATELLITE_READ_ONCE 3 #define DVB_READ_ONCE 3
#define TS_PACKET_SIZE 188 #define TS_PACKET_SIZE 188
static void FilterUnset( access_t *, int i_max ); static void FilterUnset( access_t *, int i_max );
...@@ -274,13 +270,7 @@ static int Open( vlc_object_t *p_this ) ...@@ -274,13 +270,7 @@ static int Open( vlc_object_t *p_this )
FilterSet( p_access, 0x0, OTHER_TYPE ); FilterSet( p_access, 0x0, OTHER_TYPE );
} }
p_sys->b_cam = var_GetBool( p_access, "dvb-cam" ); E_(CAMOpen)( p_access );
if ( p_sys->b_cam )
{
msg_Dbg( p_access, "initing CAM..." );
if ( E_(CAMOpen)( p_access ) < 0 )
p_sys->b_cam = VLC_FALSE;
}
return VLC_SUCCESS; return VLC_SUCCESS;
} }
...@@ -297,9 +287,7 @@ static void Close( vlc_object_t *p_this ) ...@@ -297,9 +287,7 @@ static void Close( vlc_object_t *p_this )
E_(DVRClose)( p_access ); E_(DVRClose)( p_access );
E_(FrontendClose)( p_access ); E_(FrontendClose)( p_access );
E_(CAMClose)( p_access );
if ( p_sys->b_cam )
E_(CAMClose)( p_access );
free( p_sys ); free( p_sys );
} }
...@@ -310,40 +298,52 @@ static void Close( vlc_object_t *p_this ) ...@@ -310,40 +298,52 @@ static void Close( vlc_object_t *p_this )
static block_t *Block( access_t *p_access ) static block_t *Block( access_t *p_access )
{ {
access_sys_t *p_sys = p_access->p_sys; access_sys_t *p_sys = p_access->p_sys;
struct timeval timeout;
fd_set fds;
int i_ret;
block_t *p_block; block_t *p_block;
/* Initialize file descriptor set */ for ( ; ; )
FD_ZERO( &fds );
FD_SET( p_sys->i_handle, &fds );
/* We'll wait 0.5 second if nothing happens */
timeout.tv_sec = 0;
timeout.tv_usec = 500000;
/* Find if some data is available */
while( (i_ret = select( p_sys->i_handle + 1, &fds, NULL, NULL, &timeout )) == 0 ||
(i_ret < 0 && errno == EINTR) )
{ {
struct timeval timeout;
fd_set fds;
int i_ret;
/* Initialize file descriptor set */
FD_ZERO( &fds ); FD_ZERO( &fds );
FD_SET( p_sys->i_handle, &fds ); FD_SET( p_sys->i_handle, &fds );
/* We'll wait 0.5 second if nothing happens */
timeout.tv_sec = 0; timeout.tv_sec = 0;
timeout.tv_usec = 500000; timeout.tv_usec = 500000;
if( p_access->b_die ) /* Find if some data is available */
i_ret = select( p_sys->i_handle + 1, &fds, NULL, NULL, &timeout );
if ( p_access->b_die )
return NULL; return NULL;
}
if ( i_ret < 0 ) if ( i_ret < 0 && errno == EINTR )
{ continue;
msg_Err( p_access, "select error (%s)", strerror(errno) );
return NULL; if ( i_ret < 0 )
{
msg_Err( p_access, "select error (%s)", strerror(errno) );
return NULL;
}
if ( p_sys->i_ca_handle && mdate() > p_sys->i_ca_next_event )
{
E_(CAMPoll)( p_access );
p_sys->i_ca_next_event = mdate() + p_sys->i_ca_timeout;
}
if ( FD_ISSET( p_sys->i_handle, &fds ) )
{
break;
}
} }
p_block = block_New( p_access, SATELLITE_READ_ONCE * TS_PACKET_SIZE ); p_block = block_New( p_access, DVB_READ_ONCE * TS_PACKET_SIZE );
if( ( p_block->i_buffer = read( p_sys->i_handle, p_block->p_buffer, SATELLITE_READ_ONCE * TS_PACKET_SIZE ) ) <= 0 ) if( ( p_block->i_buffer = read( p_sys->i_handle, p_block->p_buffer,
DVB_READ_ONCE * TS_PACKET_SIZE ) ) <= 0 )
{ {
msg_Err( p_access, "read failed (%s)", strerror(errno) ); msg_Err( p_access, "read failed (%s)", strerror(errno) );
block_Release( p_block ); block_Release( p_block );
...@@ -376,7 +376,7 @@ static int Control( access_t *p_access, int i_query, va_list args ) ...@@ -376,7 +376,7 @@ static int Control( access_t *p_access, int i_query, va_list args )
/* */ /* */
case ACCESS_GET_MTU: case ACCESS_GET_MTU:
pi_int = (int*)va_arg( args, int * ); pi_int = (int*)va_arg( args, int * );
*pi_int = SATELLITE_READ_ONCE * TS_PACKET_SIZE; *pi_int = DVB_READ_ONCE * TS_PACKET_SIZE;
break; break;
case ACCESS_GET_PTS_DELAY: case ACCESS_GET_PTS_DELAY:
...@@ -405,26 +405,16 @@ static int Control( access_t *p_access, int i_query, va_list args ) ...@@ -405,26 +405,16 @@ static int Control( access_t *p_access, int i_query, va_list args )
break; break;
case ACCESS_SET_PRIVATE_ID_CA: case ACCESS_SET_PRIVATE_ID_CA:
if ( p_sys->b_cam ) {
{ uint8_t **pp_capmts;
int i_program; int i_nb_capmts;
uint16_t i_vpid, i_apid1, i_apid2, i_apid3;
uint8_t i_cad_length;
uint8_t *p_cad;
i_program = (int)va_arg( args, int );
i_vpid = (int16_t)va_arg( args, int );
i_apid1 = (uint16_t)va_arg( args, int );
i_apid2 = (uint16_t)va_arg( args, int );
i_apid3 = (uint16_t)va_arg( args, int );
i_cad_length = (uint8_t)va_arg( args, int );
p_cad = (uint8_t *)va_arg( args, uint8_t * );
E_(CAMSet)( p_access, i_program, i_vpid, i_apid1, i_apid2,
i_apid3, i_cad_length, p_cad );
}
break;
pp_capmts = (uint8_t **)va_arg( args, uint8_t ** );
i_nb_capmts = (int)va_arg( args, int );
E_(CAMSet)( p_access, pp_capmts, i_nb_capmts );
break;
}
default: default:
msg_Warn( p_access, "unimplemented query in control" ); msg_Warn( p_access, "unimplemented query in control" );
return VLC_EGENERIC; return VLC_EGENERIC;
...@@ -509,7 +499,6 @@ static void VarInit( access_t *p_access ) ...@@ -509,7 +499,6 @@ static void VarInit( access_t *p_access )
/* */ /* */
var_Create( p_access, "dvb-adapter", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); var_Create( p_access, "dvb-adapter", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); var_Create( p_access, "dvb-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-cam", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-frequency", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); var_Create( p_access, "dvb-frequency", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-inversion", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); var_Create( p_access, "dvb-inversion", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_access, "dvb-probe", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); var_Create( p_access, "dvb-probe", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
...@@ -575,7 +564,6 @@ static int ParseMRL( access_t *p_access ) ...@@ -575,7 +564,6 @@ static int ParseMRL( access_t *p_access )
{ {
GET_OPTION_INT("adapter") GET_OPTION_INT("adapter")
else GET_OPTION_INT("device") else GET_OPTION_INT("device")
else GET_OPTION_BOOL("cam")
else GET_OPTION_INT("frequency") else GET_OPTION_INT("frequency")
else GET_OPTION_INT("inversion") else GET_OPTION_INT("inversion")
else GET_OPTION_BOOL("probe") else GET_OPTION_BOOL("probe")
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#define DMX "/dev/dvb/adapter%d/demux%d" #define DMX "/dev/dvb/adapter%d/demux%d"
#define FRONTEND "/dev/dvb/adapter%d/frontend%d" #define FRONTEND "/dev/dvb/adapter%d/frontend%d"
#define DVR "/dev/dvb/adapter%d/dvr%d" #define DVR "/dev/dvb/adapter%d/dvr%d"
#define CA "/dev/dvb/adapter%d/ca%d"
/***************************************************************************** /*****************************************************************************
* Local structures * Local structures
...@@ -43,7 +44,19 @@ typedef struct ...@@ -43,7 +44,19 @@ typedef struct
typedef struct frontend_t frontend_t; typedef struct frontend_t frontend_t;
#define MAX_DEMUX 24 typedef struct
{
int i_slot;
int i_resource_id;
void (* pf_handle)( access_t *, int, uint8_t *, int );
void (* pf_close)( access_t *, int );
void (* pf_manage)( access_t *, int );
void *p_sys;
} en50221_session_t;
#define MAX_DEMUX 48
#define MAX_CI_SLOTS 16
#define MAX_SESSIONS 32
struct access_sys_t struct access_sys_t
{ {
...@@ -51,8 +64,16 @@ struct access_sys_t ...@@ -51,8 +64,16 @@ struct access_sys_t
demux_handle_t p_demux_handles[MAX_DEMUX]; demux_handle_t p_demux_handles[MAX_DEMUX];
frontend_t *p_frontend; frontend_t *p_frontend;
vlc_bool_t b_budget_mode; vlc_bool_t b_budget_mode;
vlc_bool_t b_cam;
int i_cam_handle; /* CA management */
int i_ca_handle;
int i_nb_slots;
vlc_bool_t pb_active_slot[MAX_CI_SLOTS];
vlc_bool_t pb_tc_has_data[MAX_CI_SLOTS];
en50221_session_t p_sessions[MAX_SESSIONS];
mtime_t i_ca_timeout, i_ca_next_event;
uint8_t **pp_capmts;
int i_nb_capmts;
}; };
#define VIDEO0_TYPE 1 #define VIDEO0_TYPE 1
...@@ -77,6 +98,12 @@ int E_(DVROpen)( access_t * ); ...@@ -77,6 +98,12 @@ int E_(DVROpen)( access_t * );
void E_(DVRClose)( access_t * ); void E_(DVRClose)( access_t * );
int E_(CAMOpen)( access_t * ); int E_(CAMOpen)( access_t * );
int E_(CAMSet)( access_t *, uint16_t, uint16_t, uint16_t, uint16_t, uint16_t, int E_(CAMPoll)( access_t * );
uint16_t, uint8_t * ); int E_(CAMSet)( access_t *, uint8_t **, int );
void E_(CAMClose)( access_t * ); void E_(CAMClose)( access_t * );
int E_(en50221_Init)( access_t * );
int E_(en50221_Poll)( access_t * );
int E_(en50221_SetCAPMT)( access_t *, uint8_t **, int );
void E_(en50221_End)( access_t * );
/*****************************************************************************
* en50221.c : implementation of the transport, session and applications
* layers of EN 50 221
*****************************************************************************
* Copyright (C) 2004 VideoLAN
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Based on code from libdvbci Copyright (C) 2000 Klaus Schmidinger
*
* 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.
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include "dvb.h"
#undef DEBUG_TPDU
static void ResourceManagerOpen( access_t * p_access, int i_session_id );
static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
static void DateTimeOpen( access_t * p_access, int i_session_id );
static void MMIOpen( access_t * p_access, int i_session_id );
/*****************************************************************************
* Utility functions
*****************************************************************************/
#define SIZE_INDICATOR 0x80
static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
{
*pi_length = *p_data++;
if ( (*pi_length & SIZE_INDICATOR) != 0 )
{
int l = *pi_length & ~SIZE_INDICATOR;
int i;
*pi_length = 0;
for ( i = 0; i < l; i++ )
*pi_length = (*pi_length << 8) | *p_data++;
}
return p_data;
}
static uint8_t *SetLength( uint8_t *p_data, int i_length )
{
uint8_t *p = p_data;
if ( i_length < 128 )
{
*p++ = i_length;
}
else if ( i_length < 256 )
{
*p++ = SIZE_INDICATOR | 0x1;
*p++ = i_length;
}
else if ( i_length < 65536 )
{
*p++ = SIZE_INDICATOR | 0x2;
*p++ = i_length >> 8;
*p++ = i_length & 0xff;
}
else if ( i_length < 16777216 )
{
*p++ = SIZE_INDICATOR | 0x3;
*p++ = i_length >> 16;
*p++ = (i_length >> 8) & 0xff;
*p++ = i_length & 0xff;
}
else
{
*p++ = SIZE_INDICATOR | 0x4;
*p++ = i_length >> 24;
*p++ = (i_length >> 16) & 0xff;
*p++ = (i_length >> 8) & 0xff;
*p++ = i_length & 0xff;
}
return p;
}
/*
* Transport layer
*/
#define MAX_TPDU_SIZE 2048
#define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
#define DATA_INDICATOR 0x80
#define T_SB 0x80
#define T_RCV 0x81
#define T_CREATE_TC 0x82
#define T_CTC_REPLY 0x83
#define T_DELETE_TC 0x84
#define T_DTC_REPLY 0x85
#define T_REQUEST_TC 0x86
#define T_NEW_TC 0x87
#define T_TC_ERROR 0x88
#define T_DATA_LAST 0xA0
#define T_DATA_MORE 0xA1
static void Dump( vlc_bool_t b_outgoing, uint8_t *p_data, int i_size )
{
#ifdef DEBUG_TPDU
int i;
#define MAX_DUMP 256
fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
for ( i = 0; i < i_size && i < MAX_DUMP; i++)
fprintf(stderr, "%02X ", p_data[i]);
fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
#endif
}
/*****************************************************************************
* TPDUSend
*****************************************************************************/
static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
const uint8_t *p_content, int i_length )
{
access_sys_t *p_sys = p_access->p_sys;
uint8_t i_tcid = i_slot + 1;
uint8_t p_data[MAX_TPDU_SIZE];
int i_size;
i_size = 0;
p_data[0] = i_slot;
p_data[1] = i_tcid;
p_data[2] = i_tag;
switch ( i_tag )
{
case T_RCV:
case T_CREATE_TC:
case T_CTC_REPLY:
case T_DELETE_TC:
case T_DTC_REPLY:
case T_REQUEST_TC:
p_data[3] = 1; /* length */
p_data[4] = i_tcid;
i_size = 5;
break;
case T_NEW_TC:
case T_TC_ERROR:
p_data[3] = 2; /* length */
p_data[4] = i_tcid;
p_data[5] = p_content[0];
i_size = 6;
break;
case T_DATA_LAST:
case T_DATA_MORE:
{
/* i_length <= MAX_TPDU_DATA */
uint8_t *p = p_data + 3;
p = SetLength( p, i_length + 1 );
*p++ = i_tcid;
if ( i_length )
memcpy( p, p_content, i_length );
i_size = i_length + (p - p_data);
}
break;
default:
break;
}
Dump( VLC_TRUE, p_data, i_size );
if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
{
msg_Err( p_access, "cannot write to CAM device (%s)",
strerror(errno) );
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
/*****************************************************************************
* TPDURecv
*****************************************************************************/
#define CAM_READ_TIMEOUT 3500 // ms
static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
uint8_t *p_data, int *pi_size )
{
access_sys_t *p_sys = p_access->p_sys;
uint8_t i_tcid = i_slot + 1;
int i_size;
struct pollfd pfd[1];
pfd[0].fd = p_sys->i_ca_handle;
pfd[0].events = POLLIN;
if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
{
msg_Err( p_access, "cannot poll from CAM device" );
return VLC_EGENERIC;
}
if ( pi_size == NULL )
{
p_data = malloc( MAX_TPDU_SIZE );
}
for ( ; ; )
{
i_size = read( p_sys->i_ca_handle, p_data, MAX_TPDU_SIZE );
if ( i_size >= 0 || errno != EINTR )
break;
}
if ( i_size < 5 )
{
msg_Err( p_access, "cannot read from CAM device (%d:%s)", i_size,
strerror(errno) );
return VLC_EGENERIC;
}
if ( p_data[1] != i_tcid )
{
msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
p_data[1], i_tcid );
return VLC_EGENERIC;
}
*pi_tag = p_data[2];
p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
&& p_data[i_size - 4] == T_SB
&& p_data[i_size - 3] == 2
&& (p_data[i_size - 1] & DATA_INDICATOR))
? VLC_TRUE : VLC_FALSE;
Dump( VLC_FALSE, p_data, i_size );
if ( pi_size == NULL )
free( p_data );
else
*pi_size = i_size;
return VLC_SUCCESS;
}
/*
* Session layer
*/
#define ST_SESSION_NUMBER 0x90
#define ST_OPEN_SESSION_REQUEST 0x91
#define ST_OPEN_SESSION_RESPONSE 0x92
#define ST_CREATE_SESSION 0x93
#define ST_CREATE_SESSION_RESPONSE 0x94
#define ST_CLOSE_SESSION_REQUEST 0x95
#define ST_CLOSE_SESSION_RESPONSE 0x96
#define SS_OK 0x00
#define SS_NOT_ALLOCATED 0xF0
#define RI_RESOURCE_MANAGER 0x00010041
#define RI_APPLICATION_INFORMATION 0x00020041
#define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
#define RI_HOST_CONTROL 0x00200041
#define RI_DATE_TIME 0x00240041
#define RI_MMI 0x00400041
static int ResourceIdToInt( uint8_t *p_data )
{
return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
| ((int)p_data[2] << 8) | p_data[3];
}
/*****************************************************************************
* SPDUSend
*****************************************************************************/
static int SPDUSend( access_t * p_access, int i_session_id,
uint8_t *p_data, int i_size )
{
access_sys_t *p_sys = p_access->p_sys;
uint8_t *p_spdu = malloc( i_size + 4 );
uint8_t *p = p_spdu;
uint8_t i_tag;
uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
*p++ = ST_SESSION_NUMBER;
*p++ = 0x02;
*p++ = (i_session_id >> 8);
*p++ = i_session_id & 0xff;
memcpy( p, p_data, i_size );
i_size += 4;
p = p_spdu;
while ( i_size > 0 )
{
if ( i_size > MAX_TPDU_DATA )
{
if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
MAX_TPDU_DATA ) != VLC_SUCCESS )
{
msg_Err( p_access, "couldn't send TPDU on session %d",
i_session_id );
free( p_spdu );
return VLC_EGENERIC;
}
p += MAX_TPDU_DATA;
i_size -= MAX_TPDU_DATA;
}
else
{
if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
!= VLC_SUCCESS )
{
msg_Err( p_access, "couldn't send TPDU on session %d",
i_session_id );
free( p_spdu );
return VLC_EGENERIC;
}
i_size = 0;
}
if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
|| i_tag != T_SB )
{
msg_Err( p_access, "couldn't recv TPDU on session %d",
i_session_id );
free( p_spdu );
return VLC_EGENERIC;
}
}
free( p_spdu );
return VLC_SUCCESS;
}
/*****************************************************************************
* SessionOpen
*****************************************************************************/
static void SessionOpen( access_t * p_access, uint8_t i_slot,
uint8_t *p_spdu, int i_size )
{
access_sys_t *p_sys = p_access->p_sys;
int i_session_id;
int i_resource_id = ResourceIdToInt( &p_spdu[2] );
uint8_t p_response[16];
int i_status = SS_NOT_ALLOCATED;
uint8_t i_tag;
for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
{
if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
break;
}
if ( i_session_id == MAX_SESSIONS )
{
msg_Err( p_access, "too many sessions !" );
return;
}
p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
if ( i_resource_id == RI_RESOURCE_MANAGER
|| i_resource_id == RI_APPLICATION_INFORMATION
|| i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
|| i_resource_id == RI_DATE_TIME
|| i_resource_id == RI_MMI )
{
i_status = SS_OK;
}
p_response[0] = ST_OPEN_SESSION_RESPONSE;
p_response[1] = 0x7;
p_response[2] = i_status;
p_response[3] = p_spdu[2];
p_response[4] = p_spdu[3];
p_response[5] = p_spdu[4];
p_response[6] = p_spdu[5];
p_response[7] = i_session_id >> 8;
p_response[8] = i_session_id & 0xff;
if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
VLC_SUCCESS )
{
msg_Err( p_access,
"SessionOpen: couldn't send TPDU on slot %d", i_slot );
return;
}
if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
{
msg_Err( p_access,
"SessionOpen: couldn't recv TPDU on slot %d", i_slot );
return;
}
switch ( i_resource_id )
{
case RI_RESOURCE_MANAGER:
ResourceManagerOpen( p_access, i_session_id ); break;
case RI_APPLICATION_INFORMATION:
ApplicationInformationOpen( p_access, i_session_id ); break;
case RI_CONDITIONAL_ACCESS_SUPPORT:
ConditionalAccessOpen( p_access, i_session_id ); break;
case RI_DATE_TIME:
DateTimeOpen( p_access, i_session_id ); break;
case RI_MMI:
MMIOpen( p_access, i_session_id ); break;
case RI_HOST_CONTROL:
default:
msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
}
}
/*****************************************************************************
* SessionClose
*****************************************************************************/
static void SessionClose( access_t * p_access, int i_session_id )
{
access_sys_t *p_sys = p_access->p_sys;
uint8_t p_response[16];
uint8_t i_tag;
uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
p_response[0] = ST_CLOSE_SESSION_RESPONSE;
p_response[1] = 0x3;
p_response[2] = SS_OK;
p_response[3] = i_session_id >> 8;
p_response[4] = i_session_id & 0xff;
if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
VLC_SUCCESS )
{
msg_Err( p_access,
"SessionOpen: couldn't send TPDU on slot %d", i_slot );
return;
}
if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
{
msg_Err( p_access,
"SessionOpen: couldn't recv TPDU on slot %d", i_slot );
return;
}
}
/*****************************************************************************
* SPDUHandle
*****************************************************************************/
static void SPDUHandle( access_t * p_access, uint8_t i_slot,
uint8_t *p_spdu, int i_size )
{
access_sys_t *p_sys = p_access->p_sys;
int i_session_id;
switch ( p_spdu[0] )
{
case ST_SESSION_NUMBER:
if ( i_size <= 4 )
return;
i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
p_spdu + 4, i_size - 4 );
break;
case ST_OPEN_SESSION_REQUEST:
if ( i_size != 6 || p_spdu[1] != 0x4 )
return;
SessionOpen( p_access, i_slot, p_spdu, i_size );
break;
case ST_CLOSE_SESSION_REQUEST:
i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
SessionClose( p_access, i_session_id );
break;
default:
break;
}
}
/*
* Application layer
*/
#define AOT_NONE 0x000000
#define AOT_PROFILE_ENQ 0x9F8010
#define AOT_PROFILE 0x9F8011
#define AOT_PROFILE_CHANGE 0x9F8012
#define AOT_APPLICATION_INFO_ENQ 0x9F8020
#define AOT_APPLICATION_INFO 0x9F8021
#define AOT_ENTER_MENU 0x9F8022
#define AOT_CA_INFO_ENQ 0x9F8030
#define AOT_CA_INFO 0x9F8031
#define AOT_CA_PMT 0x9F8032
#define AOT_CA_PMT_REPLY 0x9F8033
#define AOT_TUNE 0x9F8400
#define AOT_REPLACE 0x9F8401
#define AOT_CLEAR_REPLACE 0x9F8402
#define AOT_ASK_RELEASE 0x9F8403
#define AOT_DATE_TIME_ENQ 0x9F8440
#define AOT_DATE_TIME 0x9F8441
#define AOT_CLOSE_MMI 0x9F8800
#define AOT_DISPLAY_CONTROL 0x9F8801
#define AOT_DISPLAY_REPLY 0x9F8802
#define AOT_TEXT_LAST 0x9F8803
#define AOT_TEXT_MORE 0x9F8804
#define AOT_KEYPAD_CONTROL 0x9F8805
#define AOT_KEYPRESS 0x9F8806
#define AOT_ENQ 0x9F8807
#define AOT_ANSW 0x9F8808
#define AOT_MENU_LAST 0x9F8809
#define AOT_MENU_MORE 0x9F880A
#define AOT_MENU_ANSW 0x9F880B
#define AOT_LIST_LAST 0x9F880C
#define AOT_LIST_MORE 0x9F880D
#define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
#define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
#define AOT_DISPLAY_MESSAGE 0x9F8810
#define AOT_SCENE_END_MARK 0x9F8811
#define AOT_SCENE_DONE 0x9F8812
#define AOT_SCENE_CONTROL 0x9F8813
#define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
#define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
#define AOT_FLUSH_DOWNLOAD 0x9F8816
#define AOT_DOWNLOAD_REPLY 0x9F8817
#define AOT_COMMS_CMD 0x9F8C00
#define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
#define AOT_COMMS_REPLY 0x9F8C02
#define AOT_COMMS_SEND_LAST 0x9F8C03
#define AOT_COMMS_SEND_MORE 0x9F8C04
#define AOT_COMMS_RCV_LAST 0x9F8C05
#define AOT_COMMS_RCV_MORE 0x9F8C06
/*****************************************************************************
* APDUGetTag
*****************************************************************************/
static int APDUGetTag( const uint8_t *p_apdu, int i_size )
{
if ( i_size >= 3 )
{
int i, t = 0;
for ( i = 0; i < 3; i++ )
t = (t << 8) | *p_apdu++;
return t;
}
return AOT_NONE;
}
/*****************************************************************************
* APDUGetLength
*****************************************************************************/
static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
{
return GetLength( &p_apdu[3], pi_size );
}
/*****************************************************************************
* APDUSend
*****************************************************************************/
static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
uint8_t *p_data, int i_size )
{
uint8_t *p_apdu = malloc( i_size + 12 );
uint8_t *p = p_apdu;
int i_ret;
*p++ = (i_tag >> 16);
*p++ = (i_tag >> 8) & 0xff;
*p++ = i_tag & 0xff;
p = SetLength( p, i_size );
if ( i_size )
memcpy( p, p_data, i_size );
i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
free( p_apdu );
return i_ret;
}
/*****************************************************************************
* ResourceManagerHandle
*****************************************************************************/
static void ResourceManagerHandle( access_t * p_access, int i_session_id,
uint8_t *p_apdu, int i_size )
{
int i_tag = APDUGetTag( p_apdu, i_size );
switch ( i_tag )
{
case AOT_PROFILE_ENQ:
{
int resources[] = { htonl(RI_RESOURCE_MANAGER),
htonl(RI_APPLICATION_INFORMATION),
htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
htonl(RI_DATE_TIME),
htonl(RI_MMI)
};
APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
sizeof(resources) );
break;
}
case AOT_PROFILE:
APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
break;
default:
msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
i_tag );
}
}
/*****************************************************************************
* ResourceManagerOpen
*****************************************************************************/
static void ResourceManagerOpen( access_t * p_access, int i_session_id )
{
access_sys_t *p_sys = p_access->p_sys;
msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
}
/*****************************************************************************
* ApplicationInformationHandle
*****************************************************************************/
static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
uint8_t *p_apdu, int i_size )
{
int i_tag = APDUGetTag( p_apdu, i_size );
switch ( i_tag )
{
case AOT_APPLICATION_INFO:
{
int i_type, i_manufacturer, i_code;
int l = 0;
uint8_t *d = APDUGetLength( p_apdu, &l );
if ( l < 4 ) break;
p_apdu[l + 3] = '\0';
i_type = *d++;
i_manufacturer = ((int)d[0] << 8) | d[1];
d += 2;
i_code = ((int)d[0] << 8) | d[1];
d += 2;
d = GetLength( d, &l );
d[l] = '\0';
msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
d, i_type, i_manufacturer, i_code );
break;
}
default:
msg_Err( p_access,
"unexpected tag in ApplicationInformationHandle (0x%x)",
i_tag );
}
}
/*****************************************************************************
* ApplicationInformationOpen
*****************************************************************************/
static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
{
access_sys_t *p_sys = p_access->p_sys;
msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
}
/*****************************************************************************
* ConditionalAccessHandle
*****************************************************************************/
static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
uint8_t *p_apdu, int i_size )
{
access_sys_t *p_sys = p_access->p_sys;
int i_tag = APDUGetTag( p_apdu, i_size );
switch ( i_tag )
{
case AOT_CA_INFO:
{
if ( p_sys->i_nb_capmts )
{
int i;
msg_Dbg( p_access, "sending CAPMT on session %d", i_session_id );
for ( i = 0; i < p_sys->i_nb_capmts; i++ )
{
int i_size;
uint8_t *p;
p = GetLength( &p_sys->pp_capmts[i][3], &i_size );
SPDUSend( p_access, i_session_id, p_sys->pp_capmts[i],
i_size + (p - p_sys->pp_capmts[i]) );
}
p_sys->i_ca_timeout = 100000;
}
break;
}
default:
msg_Err( p_access,
"unexpected tag in ConditionalAccessHandle (0x%x)",
i_tag );
}
}
/*****************************************************************************
* ConditionalAccessOpen
*****************************************************************************/
static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
{
access_sys_t *p_sys = p_access->p_sys;
msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
}
typedef struct
{
int i_interval;
mtime_t i_last;
} date_time_t;
/*****************************************************************************
* DateTimeSend
*****************************************************************************/
static void DateTimeSend( access_t * p_access, int i_session_id )
{
access_sys_t *p_sys = p_access->p_sys;
date_time_t *p_date =
(date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
time_t t = time(NULL);
struct tm tm_gmt;
struct tm tm_loc;
if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
{
int Y = tm_gmt.tm_year;
int M = tm_gmt.tm_mon + 1;
int D = tm_gmt.tm_mday;
int L = (M == 1 || M == 2) ? 1 : 0;
int MJD = 14956 + D + (int)((Y - L) * 365.25)
+ (int)((M + 1 + L * 12) * 30.6001);
uint8_t p_response[7];
#define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
p_response[0] = htons(MJD) >> 8;
p_response[1] = htons(MJD) & 0xff;
p_response[2] = DEC2BCD(tm_gmt.tm_hour);
p_response[3] = DEC2BCD(tm_gmt.tm_min);
p_response[4] = DEC2BCD(tm_gmt.tm_sec);
p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
p_date->i_last = mdate();
}
}
/*****************************************************************************
* DateTimeHandle
*****************************************************************************/
static void DateTimeHandle( access_t * p_access, int i_session_id,
uint8_t *p_apdu, int i_size )
{
access_sys_t *p_sys = p_access->p_sys;
date_time_t *p_date =
(date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
int i_tag = APDUGetTag( p_apdu, i_size );
switch ( i_tag )
{
case AOT_DATE_TIME_ENQ:
{
int l;
const uint8_t *d = APDUGetLength( p_apdu, &l );
if ( l > 0 )
{
p_date->i_interval = *d;
msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
p_date->i_interval );
}
else
p_date->i_interval = 0;
DateTimeSend( p_access, i_session_id );
break;
}
default:
msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
}
}
/*****************************************************************************
* DateTimeManage
*****************************************************************************/
static void DateTimeManage( access_t * p_access, int i_session_id )
{
access_sys_t *p_sys = p_access->p_sys;
date_time_t *p_date =
(date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
if ( p_date->i_interval
&& mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
{
DateTimeSend( p_access, i_session_id );
}
}
/*****************************************************************************
* DateTimeOpen
*****************************************************************************/
static void DateTimeOpen( access_t * p_access, int i_session_id )
{
access_sys_t *p_sys = p_access->p_sys;
msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
DateTimeSend( p_access, i_session_id );
}
/*****************************************************************************
* MMIHandle
*****************************************************************************/
static void MMIHandle( access_t * p_access, int i_session_id,
uint8_t *p_apdu, int i_size )
{
int i_tag = APDUGetTag( p_apdu, i_size );
switch ( i_tag )
{
default:
msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
}
}
/*****************************************************************************
* MMIOpen
*****************************************************************************/
static void MMIOpen( access_t * p_access, int i_session_id )
{
access_sys_t *p_sys = p_access->p_sys;
msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
}
/*
* External entry points
*/
/*****************************************************************************
* en50221_Init : Open the transport layer
*****************************************************************************/
#define MAX_TC_RETRIES 20
int E_(en50221_Init)( access_t * p_access )
{
access_sys_t *p_sys = p_access->p_sys;
int i_slot, i_active_slots = 0;
for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
{
int i;
if ( !p_sys->pb_active_slot[i_slot] )
continue;
p_sys->pb_active_slot[i_slot] = VLC_FALSE;
if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
!= VLC_SUCCESS )
{
msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
i_slot );
continue;
}
/* This is out of the spec */
for ( i = 0; i < MAX_TC_RETRIES; i++ )
{
uint8_t i_tag;
if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
&& i_tag == T_CTC_REPLY )
{
p_sys->pb_active_slot[i_slot] = VLC_TRUE;
i_active_slots++;
break;
}
if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
!= VLC_SUCCESS )
{
msg_Err( p_access,
"en50221_Init: couldn't send TPDU on slot %d",
i_slot );
continue;
}
}
}
p_sys->i_ca_timeout = 1000;
return i_active_slots;
}
/*****************************************************************************
* en50221_Poll : Poll the CAM for TPDUs
*****************************************************************************/
int E_(en50221_Poll)( access_t * p_access )
{
access_sys_t *p_sys = p_access->p_sys;
int i_slot;
int i_session_id;
for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
{
uint8_t i_tag;
if ( !p_sys->pb_active_slot[i_slot] )
continue;
if ( !p_sys->pb_tc_has_data[i_slot] )
{
if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
VLC_SUCCESS )
{
msg_Err( p_access,
"en50221_Poll: couldn't send TPDU on slot %d",
i_slot );
continue;
}
if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
VLC_SUCCESS )
{
msg_Err( p_access,
"en50221_Poll: couldn't recv TPDU on slot %d",
i_slot );
continue;
}
}
while ( p_sys->pb_tc_has_data[i_slot] )
{
uint8_t p_tpdu[MAX_TPDU_SIZE];
int i_size, i_session_size;
uint8_t *p_session;
if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
{
msg_Err( p_access,
"en50221_Poll: couldn't send TPDU on slot %d",
i_slot );
continue;
}
if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
VLC_SUCCESS )
{
msg_Err( p_access,
"en50221_Poll: couldn't recv TPDU on slot %d",
i_slot );
continue;
}
p_session = GetLength( &p_tpdu[3], &i_session_size );
if ( i_session_size <= 1 )
continue;
p_session++;
i_session_size--;
if ( i_tag != T_DATA_LAST )
{
msg_Err( p_access,
"en50221_Poll: fragmented TPDU not supported" );
break;
}
SPDUHandle( p_access, i_slot, p_session, i_session_size );
}
}
for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
{
if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
&& p_sys->p_sessions[i_session_id - 1].pf_manage )
{
p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
i_session_id );
}
}
return VLC_SUCCESS;
}
/*****************************************************************************
* en50221_SetCAPMT :
*****************************************************************************/
int E_(en50221_SetCAPMT)( access_t * p_access, uint8_t **pp_capmts,
int i_nb_capmts )
{
access_sys_t *p_sys = p_access->p_sys;
int i_session_id;
for ( i_session_id = 0; i_session_id < MAX_SESSIONS; i_session_id++ )
{
int i;
if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
!= RI_CONDITIONAL_ACCESS_SUPPORT )
continue;
msg_Dbg( p_access, "sending CAPMT on session %d", i_session_id );
for ( i = 0; i < i_nb_capmts; i++ )
{
int i_size;
uint8_t *p;
p = GetLength( &pp_capmts[i][3], &i_size );
SPDUSend( p_access, i_session_id, pp_capmts[i],
i_size + (p - pp_capmts[i]) );
}
p_sys->i_ca_timeout = 100000;
}
if ( p_sys->i_nb_capmts )
{
int i;
for ( i = 0; i < p_sys->i_nb_capmts; i++ )
{
free( p_sys->pp_capmts[i] );
}
free( p_sys->pp_capmts );
}
p_sys->pp_capmts = pp_capmts;
p_sys->i_nb_capmts = i_nb_capmts;
return VLC_SUCCESS;
}
/*****************************************************************************
* en50221_End :
*****************************************************************************/
void E_(en50221_End)( access_t * p_access )
{
access_sys_t *p_sys = p_access->p_sys;
if ( p_sys->i_nb_capmts )
{
int i;
for ( i = 0; i < p_sys->i_nb_capmts; i++ )
{
free( p_sys->pp_capmts[i] );
}
free( p_sys->pp_capmts );
}
/* TODO */
}
/***************************************************************************** /*****************************************************************************
* dvb.c : functions to control a DVB card under Linux with v4l2 * linux_dvb.c : functions to control a DVB card under Linux with v4l2
***************************************************************************** *****************************************************************************
* Copyright (C) 1998-2004 VideoLAN * Copyright (C) 1998-2004 VideoLAN
* *
...@@ -42,13 +42,12 @@ ...@@ -42,13 +42,12 @@
#include <linux/dvb/version.h> #include <linux/dvb/version.h>
#include <linux/dvb/dmx.h> #include <linux/dvb/dmx.h>
#include <linux/dvb/frontend.h> #include <linux/dvb/frontend.h>
#include <linux/dvb/ca.h>
#include <linux/errno.h>
#include "dvb.h" #include "dvb.h"
#include "network.h"
#define DMX_BUFFER_SIZE (1024 * 1024) #define DMX_BUFFER_SIZE (1024 * 1024)
#define CA_MAX_STATE_RETRY 5
/* /*
* Frontends * Frontends
...@@ -1108,60 +1107,121 @@ void E_(DVRClose)( access_t * p_access ) ...@@ -1108,60 +1107,121 @@ void E_(DVRClose)( access_t * p_access )
/* /*
* CAM device * CAM device
*
* This uses the external cam_set program from libdvb-0.5.4
*/ */
/***************************************************************************** /*****************************************************************************
* CAMOpen : * CAMOpen :
*****************************************************************************/ *****************************************************************************/
int E_(CAMOpen)( access_t * p_access ) int E_(CAMOpen)( access_t *p_access )
{ {
access_sys_t *p_sys = p_access->p_sys; access_sys_t *p_sys = p_access->p_sys;
char ca[128];
int i_adapter, i_device, i_slot, i_active_slots = 0;
ca_caps_t caps;
i_adapter = var_GetInteger( p_access, "dvb-adapter" );
i_device = var_GetInteger( p_access, "dvb-device" );
if( snprintf( ca, sizeof(ca), CA, i_adapter, i_device ) >= (int)sizeof(ca) )
{
msg_Err( p_access, "snprintf() truncated string for CA" );
ca[sizeof(ca) - 1] = '\0';
}
msg_Dbg( p_access, "Opening device %s", ca );
if( (p_sys->i_ca_handle = open(ca, O_RDWR | O_NONBLOCK)) < 0 )
{
msg_Err( p_access, "CAMInit: opening device failed (%s)",
strerror(errno) );
return VLC_EGENERIC;
}
if ( ioctl( p_sys->i_ca_handle, CA_GET_CAP, &caps ) != 0
|| caps.slot_num == 0 || caps.slot_type != CA_CI_LINK )
{
msg_Err( p_access, "CAMInit: no compatible CAM module" );
close( p_sys->i_ca_handle );
p_sys->i_ca_handle = 0;
return VLC_EGENERIC;
}
p_sys->i_nb_slots = caps.slot_num;
memset( p_sys->pb_active_slot, 0, sizeof(vlc_bool_t) * MAX_CI_SLOTS );
for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
{
ca_slot_info_t sinfo;
int i;
if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
{
msg_Err( p_access, "CAMInit: couldn't reset slot %d", i_slot );
continue;
}
for ( i = 0; i < CA_MAX_STATE_RETRY; i++ )
{
msleep(100000);
sinfo.num = i_slot;
if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
{
msg_Err( p_access, "CAMInit: couldn't get info on slot %d",
i_slot );
continue;
}
if ( sinfo.flags & CA_CI_MODULE_READY )
{
p_sys->pb_active_slot[i_slot] = VLC_TRUE;
}
}
}
p_sys->i_cam_handle = net_OpenTCP( p_access, "localhost", 4711 ); i_active_slots = E_(en50221_Init)( p_access );
if ( p_sys->i_cam_handle < 0 )
msg_Dbg( p_access, "CAMInit: found a CI handler with %d slots, %d active",
p_sys->i_nb_slots, i_active_slots );
if ( !i_active_slots )
{ {
return -VLC_EGENERIC; close( p_sys->i_ca_handle );
p_sys->i_ca_handle = 0;
return VLC_EGENERIC;
} }
return VLC_SUCCESS; return VLC_SUCCESS;
} }
/***************************************************************************** /*****************************************************************************
* CAMSet : * CAMPoll :
*****************************************************************************/ *****************************************************************************/
int E_(CAMSet)( access_t * p_access, uint16_t i_program, uint16_t i_vpid, int E_(CAMPoll)( access_t * p_access )
uint16_t i_apid1, uint16_t i_apid2, uint16_t i_apid3,
uint16_t i_cad_length, uint8_t *p_cad )
{ {
access_sys_t *p_sys = p_access->p_sys; access_sys_t *p_sys = p_access->p_sys;
uint8_t p_str[12];
memcpy( p_str, &i_program, 2 );
memcpy( p_str + 2, &i_vpid, 2 );
memcpy( p_str + 4, &i_apid1, 2 );
memcpy( p_str + 6, &i_apid2, 2 );
memcpy( p_str + 8, &i_apid3, 2 );
memcpy( p_str + 10, &i_cad_length, 2 );
if ( net_Write( p_access, p_sys->i_cam_handle, p_str, 12 ) != 12 ) if ( p_sys->i_ca_handle == 0 )
{ {
msg_Err( p_access, "write 1 failed (%s)", strerror(errno) ); return VLC_EGENERIC;
return -VLC_EGENERIC;
} }
if ( i_cad_length ) return E_(en50221_Poll)( p_access );
}
/*****************************************************************************
* CAMSet :
*****************************************************************************/
int E_(CAMSet)( access_t * p_access, uint8_t **pp_capmts, int i_nb_capmts )
{
access_sys_t *p_sys = p_access->p_sys;
if ( p_sys->i_ca_handle == 0 )
{ {
if ( net_Write( p_access, p_sys->i_cam_handle, p_cad, i_cad_length ) return VLC_EGENERIC;
!= i_cad_length )
{
msg_Err( p_access, "write 2 failed (%s) %d", strerror(errno),
i_cad_length );
return -VLC_EGENERIC;
}
} }
E_(en50221_SetCAPMT)( p_access, pp_capmts, i_nb_capmts );
return VLC_SUCCESS; return VLC_SUCCESS;
} }
...@@ -1172,9 +1232,11 @@ void E_(CAMClose)( access_t * p_access ) ...@@ -1172,9 +1232,11 @@ void E_(CAMClose)( access_t * p_access )
{ {
access_sys_t *p_sys = p_access->p_sys; access_sys_t *p_sys = p_access->p_sys;
if ( p_sys->i_cam_handle ) E_(en50221_End)( p_access );
if ( p_sys->i_ca_handle )
{ {
close( p_sys->i_cam_handle ); close( p_sys->i_ca_handle );
} }
} }
...@@ -183,8 +183,6 @@ typedef struct ...@@ -183,8 +183,6 @@ typedef struct
} iod_descriptor_t; } iod_descriptor_t;
#define MAX_CAD 10
typedef struct typedef struct
{ {
dvbpsi_handle handle; dvbpsi_handle handle;
...@@ -196,10 +194,9 @@ typedef struct ...@@ -196,10 +194,9 @@ typedef struct
/* IOD stuff (mpeg4) */ /* IOD stuff (mpeg4) */
iod_descriptor_t *iod; iod_descriptor_t *iod;
/* Conditional Access descriptor */ /* Conditional Access PMT (EN 50 221) */
int i_nb_cad; uint8_t *p_capmt;
uint8_t *cad[MAX_CAD]; int i_capmt_size;
uint8_t i_cad_length[MAX_CAD];
} ts_prg_psi_t; } ts_prg_psi_t;
...@@ -277,6 +274,7 @@ struct demux_sys_t ...@@ -277,6 +274,7 @@ struct demux_sys_t
vlc_bool_t b_dvb_control; vlc_bool_t b_dvb_control;
int i_dvb_program; int i_dvb_program;
vlc_list_t *p_programs_list;
}; };
static int Demux ( demux_t *p_demux ); static int Demux ( demux_t *p_demux );
...@@ -302,6 +300,8 @@ static void PCRHandle( demux_t *p_demux, ts_pid_t *, block_t * ); ...@@ -302,6 +300,8 @@ static void PCRHandle( demux_t *p_demux, ts_pid_t *, block_t * );
static iod_descriptor_t *IODNew( int , uint8_t * ); static iod_descriptor_t *IODNew( int , uint8_t * );
static void IODFree( iod_descriptor_t * ); static void IODFree( iod_descriptor_t * );
static void DVBCAPMTSend( demux_t *p_demux );
#define TS_PACKET_SIZE_188 188 #define TS_PACKET_SIZE_188 188
#define TS_PACKET_SIZE_192 192 #define TS_PACKET_SIZE_192 192
#define TS_PACKET_SIZE_204 204 #define TS_PACKET_SIZE_204 204
...@@ -472,7 +472,7 @@ static int Open( vlc_object_t *p_this ) ...@@ -472,7 +472,7 @@ static int Open( vlc_object_t *p_this )
{ {
ts_pid_t *pmt = &p_sys->pid[i_pid]; ts_pid_t *pmt = &p_sys->pid[i_pid];
msg_Dbg( p_demux, "extra pmt specified (pid=0x%x)", i_pid ); msg_Dbg( p_demux, "extra pmt specified (pid=%d)", i_pid );
PIDInit( pmt, VLC_TRUE, NULL ); PIDInit( pmt, VLC_TRUE, NULL );
/* FIXME we should also ask for a number */ /* FIXME we should also ask for a number */
pmt->psi->prg[0]->handle = pmt->psi->prg[0]->handle =
...@@ -512,7 +512,7 @@ static int Open( vlc_object_t *p_this ) ...@@ -512,7 +512,7 @@ static int Open( vlc_object_t *p_this )
{ {
pid->es->fmt.i_id = i_pid; pid->es->fmt.i_id = i_pid;
} }
msg_Dbg( p_demux, " * es pid=0x%x type=0x%x " msg_Dbg( p_demux, " * es pid=%d type=%d "
"fcc=%4.4s", i_pid, i_stream_type, "fcc=%4.4s", i_pid, i_stream_type,
(char*)&pid->es->fmt.i_codec ); (char*)&pid->es->fmt.i_codec );
pid->es->id = es_out_Add( p_demux->out, pid->es->id = es_out_Add( p_demux->out,
...@@ -610,7 +610,7 @@ static void Close( vlc_object_t *p_this ) ...@@ -610,7 +610,7 @@ static void Close( vlc_object_t *p_this )
if( pid->b_seen ) if( pid->b_seen )
{ {
msg_Dbg( p_demux, " - pid[0x%x] seen", pid->i_pid ); msg_Dbg( p_demux, " - pid[%d] seen", pid->i_pid );
} }
if( p_sys->b_dvb_control && pid->i_pid > 0 ) if( p_sys->b_dvb_control && pid->i_pid > 0 )
...@@ -632,6 +632,14 @@ static void Close( vlc_object_t *p_this ) ...@@ -632,6 +632,14 @@ static void Close( vlc_object_t *p_this )
} }
if( p_sys->i_pmt ) free( p_sys->pmt ); if( p_sys->i_pmt ) free( p_sys->pmt );
if ( p_sys->p_programs_list )
{
vlc_value_t val;
val.p_list = p_sys->p_programs_list;
var_Change( p_demux, "programs", VLC_VAR_FREELIST, &val, NULL );
}
free( p_sys ); free( p_sys );
} }
...@@ -744,7 +752,7 @@ static int Demux( demux_t *p_demux ) ...@@ -744,7 +752,7 @@ static int Demux( demux_t *p_demux )
{ {
if( !p_pid->b_seen ) if( !p_pid->b_seen )
{ {
msg_Dbg( p_demux, "pid[0x%x] unknown", p_pid->i_pid ); msg_Dbg( p_demux, "pid[%d] unknown", p_pid->i_pid );
} }
/* We have to handle PCR if present */ /* We have to handle PCR if present */
PCRHandle( p_demux, p_pid, p_pkt ); PCRHandle( p_demux, p_pid, p_pkt );
...@@ -829,9 +837,11 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) ...@@ -829,9 +837,11 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
{ {
uint16_t i_vpid = 0, i_apid1 = 0, i_apid2 = 0, i_apid3 = 0; uint16_t i_vpid = 0, i_apid1 = 0, i_apid2 = 0, i_apid3 = 0;
ts_prg_psi_t *p_prg = NULL; ts_prg_psi_t *p_prg = NULL;
vlc_list_t *p_list;
i_int = (int)va_arg( args, int ); i_int = (int)va_arg( args, int );
msg_Dbg( p_demux, "DEMUX_SET_GROUP %d", i_int ); p_list = (vlc_list_t *)va_arg( args, vlc_list_t * );
msg_Dbg( p_demux, "DEMUX_SET_GROUP %d %p", i_int, p_list );
if( p_sys->b_dvb_control && i_int > 0 && i_int != p_sys->i_dvb_program ) if( p_sys->b_dvb_control && i_int > 0 && i_int != p_sys->i_dvb_program )
{ {
...@@ -907,6 +917,10 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) ...@@ -907,6 +917,10 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
stream_Control( p_demux->s, STREAM_CONTROL_ACCESS, stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
ACCESS_SET_PRIVATE_ID_STATE, i_pmt_pid, ACCESS_SET_PRIVATE_ID_STATE, i_pmt_pid,
VLC_TRUE ); VLC_TRUE );
stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
ACCESS_SET_PRIVATE_ID_STATE, p_prg->i_pid_pcr,
VLC_TRUE );
for( i = 2; i < 8192; i++ ) for( i = 2; i < 8192; i++ )
{ {
ts_pid_t *pid = &p_sys->pid[i]; ts_pid_t *pid = &p_sys->pid[i];
...@@ -937,22 +951,14 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) ...@@ -937,22 +951,14 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
} }
/* Set CAM descrambling */ /* Set CAM descrambling */
for ( i = 0; i < p_prg->i_nb_cad; i++ ) DVBCAPMTSend( p_demux );
{
stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
ACCESS_SET_PRIVATE_ID_CA, p_prg->i_number,
i_vpid, i_apid1, i_apid2, i_apid3,
p_prg->i_cad_length[i],
p_prg->cad[i] );
}
stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
ACCESS_SET_PRIVATE_ID_CA, p_prg->i_number,
0, 0, 0, 0, 0, NULL );
stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
ACCESS_SET_PRIVATE_ID_CA, 0,
0, 0, 0, 0, 0, NULL );
} }
} }
else
{
p_sys->i_dvb_program = -1;
p_sys->p_programs_list = p_list;
}
return VLC_SUCCESS; return VLC_SUCCESS;
} }
...@@ -996,7 +1002,8 @@ static void PIDInit( ts_pid_t *pid, vlc_bool_t b_psi, ts_psi_t *p_owner ) ...@@ -996,7 +1002,8 @@ static void PIDInit( ts_pid_t *pid, vlc_bool_t b_psi, ts_psi_t *p_owner )
prg->i_pid_pcr = -1; prg->i_pid_pcr = -1;
prg->i_pid_pmt = -1; prg->i_pid_pmt = -1;
prg->iod = NULL; prg->iod = NULL;
prg->i_nb_cad = 0; prg->p_capmt = NULL;
prg->i_capmt_size = 0;
prg->handle = NULL; prg->handle = NULL;
TAB_APPEND( pid->psi->i_prg, pid->psi->prg, prg ); TAB_APPEND( pid->psi->i_prg, pid->psi->prg, prg );
...@@ -1027,11 +1034,10 @@ static void PIDClean( es_out_t *out, ts_pid_t *pid ) ...@@ -1027,11 +1034,10 @@ static void PIDClean( es_out_t *out, ts_pid_t *pid )
if( pid->psi->handle ) dvbpsi_DetachPMT( pid->psi->handle ); if( pid->psi->handle ) dvbpsi_DetachPMT( pid->psi->handle );
for( i = 0; i < pid->psi->i_prg; i++ ) for( i = 0; i < pid->psi->i_prg; i++ )
{ {
int j;
if( pid->psi->prg[i]->iod ) if( pid->psi->prg[i]->iod )
IODFree( pid->psi->prg[i]->iod ); IODFree( pid->psi->prg[i]->iod );
for ( j = 0; j < pid->psi->prg[i]->i_nb_cad; j++ ) if ( pid->psi->prg[i]->i_capmt_size )
free( pid->psi->prg[i]->cad[j] ); free( pid->psi->prg[i]->p_capmt );
if( pid->psi->prg[i]->handle ) if( pid->psi->prg[i]->handle )
dvbpsi_DetachPMT( pid->psi->prg[i]->handle ); dvbpsi_DetachPMT( pid->psi->prg[i]->handle );
free( pid->psi->prg[i] ); free( pid->psi->prg[i] );
...@@ -1098,7 +1104,7 @@ static void ParsePES( demux_t *p_demux, ts_pid_t *pid ) ...@@ -1098,7 +1104,7 @@ static void ParsePES( demux_t *p_demux, ts_pid_t *pid )
if( header[0] != 0 || header[1] != 0 || header[2] != 1 ) if( header[0] != 0 || header[1] != 0 || header[2] != 1 )
{ {
if( !p_demux->p_sys->b_silent ) if( !p_demux->p_sys->b_silent )
msg_Warn( p_demux, "invalid header [0x%x:%x:%x:%x] (pid: 0x%x)", msg_Warn( p_demux, "invalid header [0x%x:%x:%x:%x] (pid: %d)",
header[0], header[1],header[2],header[3], pid->i_pid ); header[0], header[1],header[2],header[3], pid->i_pid );
block_ChainRelease( p_pes ); block_ChainRelease( p_pes );
return; return;
...@@ -1335,7 +1341,7 @@ static vlc_bool_t GatherPES( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk ) ...@@ -1335,7 +1341,7 @@ static vlc_bool_t GatherPES( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk )
int i_diff; int i_diff;
#if 0 #if 0
msg_Dbg( p_demux, "pid=0x%x unit_start=%d adaptation=%d payload=%d " msg_Dbg( p_demux, "pid=%d unit_start=%d adaptation=%d payload=%d "
"cc=0x%x", pid->i_pid, b_unit_start, b_adaptation, "cc=0x%x", pid->i_pid, b_unit_start, b_adaptation,
b_payload, i_cc ); b_payload, i_cc );
#endif #endif
...@@ -1346,7 +1352,7 @@ static vlc_bool_t GatherPES( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk ) ...@@ -1346,7 +1352,7 @@ static vlc_bool_t GatherPES( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk )
if( p[1]&0x80 ) if( p[1]&0x80 )
{ {
msg_Dbg( p_demux, "transport_error_indicator set (pid=0x%x)", msg_Dbg( p_demux, "transport_error_indicator set (pid=%d)",
pid->i_pid ); pid->i_pid );
} }
...@@ -1369,7 +1375,7 @@ static vlc_bool_t GatherPES( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk ) ...@@ -1369,7 +1375,7 @@ static vlc_bool_t GatherPES( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk )
{ {
if( p[5]&0x80 ) if( p[5]&0x80 )
{ {
msg_Warn( p_demux, "discontinuity_indicator (pid=0x%x) " msg_Warn( p_demux, "discontinuity_indicator (pid=%d) "
"ignored", pid->i_pid ); "ignored", pid->i_pid );
} }
} }
...@@ -1392,7 +1398,7 @@ static vlc_bool_t GatherPES( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk ) ...@@ -1392,7 +1398,7 @@ static vlc_bool_t GatherPES( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk )
{ {
if( pid->i_cc == 0xff ) if( pid->i_cc == 0xff )
{ {
msg_Warn( p_demux, "first packet for pid=0x%x cc=0x%x", msg_Warn( p_demux, "first packet for pid=%d cc=0x%x",
pid->i_pid, i_cc ); pid->i_pid, i_cc );
pid->i_cc = i_cc; pid->i_cc = i_cc;
} }
...@@ -1906,6 +1912,27 @@ static void IODFree( iod_descriptor_t *p_iod ) ...@@ -1906,6 +1912,27 @@ static void IODFree( iod_descriptor_t *p_iod )
** libdvbpsi callbacks ** libdvbpsi callbacks
**************************************************************************** ****************************************************************************
****************************************************************************/ ****************************************************************************/
static vlc_bool_t DVBProgramIsSelected( demux_t *p_demux, uint16_t i_pgrm )
{
demux_sys_t *p_sys = p_demux->p_sys;
if ( !p_sys->b_dvb_control ) return VLC_FALSE;
if ( p_sys->i_dvb_program == -1 && p_sys->p_programs_list == NULL )
return VLC_TRUE;
if ( p_sys->i_dvb_program == i_pgrm ) return VLC_TRUE;
if ( p_sys->p_programs_list != NULL )
{
int i;
for ( i = 0; i < p_sys->p_programs_list->i_count; i++ )
{
if ( i_pgrm == p_sys->p_programs_list->p_values[i].i_int )
return VLC_TRUE;
}
}
return VLC_FALSE;
}
static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
{ {
demux_sys_t *p_sys = p_demux->p_sys; demux_sys_t *p_sys = p_demux->p_sys;
...@@ -1914,12 +1941,11 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) ...@@ -1914,12 +1941,11 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
ts_pid_t *pmt = NULL; ts_pid_t *pmt = NULL;
ts_prg_psi_t *prg = NULL; ts_prg_psi_t *prg = NULL;
int i_cad_length = 0;
ts_pid_t **pp_clean = NULL; ts_pid_t **pp_clean = NULL;
int i_clean = 0, i; int i_clean = 0, i;
/* CA descriptor */
uint16_t i_vpid = 0, i_apid1 = 0, i_apid2 = 0, i_apid3 = 0;
msg_Dbg( p_demux, "PMTCallBack called" ); msg_Dbg( p_demux, "PMTCallBack called" );
/* First find this PMT declared in PAT */ /* First find this PMT declared in PAT */
...@@ -1961,13 +1987,6 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) ...@@ -1961,13 +1987,6 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
if( pid->b_valid && pid->p_owner == pmt->psi && if( pid->b_valid && pid->p_owner == pmt->psi &&
pid->i_owner_number == prg->i_number && pid->psi == NULL ) pid->i_owner_number == prg->i_number && pid->psi == NULL )
{ {
if( p_sys->b_dvb_control && ( p_sys->i_dvb_program < 0 ||
p_sys->i_dvb_program == prg->i_number ) )
{
stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
ACCESS_SET_PRIVATE_ID_STATE, i, VLC_FALSE );
}
TAB_APPEND( i_clean, pp_clean, pid ); TAB_APPEND( i_clean, pp_clean, pid );
} }
} }
...@@ -1976,15 +1995,29 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) ...@@ -1976,15 +1995,29 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
IODFree( prg->iod ); IODFree( prg->iod );
prg->iod = NULL; prg->iod = NULL;
} }
for ( i = 0; i < prg->i_nb_cad; i++ ) if ( prg->i_capmt_size )
free( prg->cad[i] ); free( prg->p_capmt );
prg->i_nb_cad = 0; prg->i_capmt_size = 0;
msg_Dbg( p_demux, "new PMT program number=%d version=%d pid_pcr=0x%x", msg_Dbg( p_demux, "new PMT program number=%d version=%d pid_pcr=%d",
p_pmt->i_program_number, p_pmt->i_version, p_pmt->i_pcr_pid ); p_pmt->i_program_number, p_pmt->i_version, p_pmt->i_pcr_pid );
prg->i_pid_pcr = p_pmt->i_pcr_pid; prg->i_pid_pcr = p_pmt->i_pcr_pid;
prg->i_version = p_pmt->i_version; prg->i_version = p_pmt->i_version;
if( DVBProgramIsSelected( p_demux, prg->i_number ) )
{
/* Set demux filter */
stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
ACCESS_SET_PRIVATE_ID_STATE, prg->i_pid_pcr,
VLC_TRUE );
}
else if ( p_sys->b_dvb_control )
{
msg_Warn( p_demux, "skipping program (not selected)" );
dvbpsi_DeletePMT(p_pmt);
return;
}
/* Parse descriptor */ /* Parse descriptor */
for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next ) for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
{ {
...@@ -1998,13 +2031,7 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) ...@@ -1998,13 +2031,7 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
else if( p_dr->i_tag == 0x9 ) else if( p_dr->i_tag == 0x9 )
{ {
msg_Dbg( p_demux, " * descriptor : CA (0x9)" ); msg_Dbg( p_demux, " * descriptor : CA (0x9)" );
i_cad_length += p_dr->i_length + 2;
prg->cad[prg->i_nb_cad] = malloc( p_dr->i_length + 2 );
prg->cad[prg->i_nb_cad][0] = 0x9;
prg->cad[prg->i_nb_cad][1] = p_dr->i_length;
memcpy( prg->cad[prg->i_nb_cad] + 2, p_dr->p_data, p_dr->i_length );
prg->i_cad_length[prg->i_nb_cad] = p_dr->i_length + 2;
prg->i_nb_cad++;
} }
else else
{ {
...@@ -2012,6 +2039,31 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) ...@@ -2012,6 +2039,31 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
} }
} }
if ( i_cad_length )
{
prg->p_capmt = malloc( 6 + i_cad_length );
prg->i_capmt_size = 6 + i_cad_length;
prg->p_capmt[0] = p_pmt->i_program_number >> 8;
prg->p_capmt[1] = p_pmt->i_program_number & 0xff;
prg->p_capmt[2] = (p_pmt->i_version << 1) | 0x1;
prg->p_capmt[3] = (i_cad_length + 1) >> 8;
prg->p_capmt[4] = (i_cad_length + 1) & 0xff;
prg->p_capmt[5] = 0x1; /* ok_descrambling */
i = 6;
for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
{
if( p_dr->i_tag == 0x9 )
{
prg->p_capmt[i] = 0x9;
prg->p_capmt[i+1] = p_dr->i_length;
memcpy( &prg->p_capmt[i+2], p_dr->p_data, p_dr->i_length );
i += p_dr->i_length + 2;
}
}
}
for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next ) for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
{ {
ts_pid_t tmp_pid, *old_pid = 0, *pid = &tmp_pid; ts_pid_t tmp_pid, *old_pid = 0, *pid = &tmp_pid;
...@@ -2028,7 +2080,7 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) ...@@ -2028,7 +2080,7 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
if( !old_pid && p_sys->pid[p_es->i_pid].b_valid ) if( !old_pid && p_sys->pid[p_es->i_pid].b_valid )
{ {
msg_Warn( p_demux, "pmt error: pid=0x%x already defined", msg_Warn( p_demux, "pmt error: pid=%d already defined",
p_es->i_pid ); p_es->i_pid );
continue; continue;
} }
...@@ -2039,15 +2091,6 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) ...@@ -2039,15 +2091,6 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
pid->i_pid = p_es->i_pid; pid->i_pid = p_es->i_pid;
pid->b_seen = p_sys->pid[p_es->i_pid].b_seen; pid->b_seen = p_sys->pid[p_es->i_pid].b_seen;
if ( pid->es->fmt.i_cat == VIDEO_ES && !i_vpid )
i_vpid = p_es->i_pid;
if ( pid->es->fmt.i_cat == AUDIO_ES && !i_apid1 )
i_apid1 = p_es->i_pid;
else if ( pid->es->fmt.i_cat == AUDIO_ES && !i_apid2 )
i_apid2 = p_es->i_pid;
else if ( pid->es->fmt.i_cat == AUDIO_ES && !i_apid3 )
i_apid3 = p_es->i_pid;
if( p_es->i_type == 0x10 || p_es->i_type == 0x11 || if( p_es->i_type == 0x10 || p_es->i_type == 0x11 ||
p_es->i_type == 0x12 ) p_es->i_type == 0x12 )
{ {
...@@ -2058,7 +2101,6 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) ...@@ -2058,7 +2101,6 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
if( p_dr && p_dr->i_length == 2 ) if( p_dr && p_dr->i_length == 2 )
{ {
int i;
int i_es_id = ( p_dr->p_data[0] << 8 ) | p_dr->p_data[1]; int i_es_id = ( p_dr->p_data[0] << 8 ) | p_dr->p_data[1];
msg_Warn( p_demux, "found SL_descriptor es_id=%d", i_es_id ); msg_Warn( p_demux, "found SL_descriptor es_id=%d", i_es_id );
...@@ -2164,7 +2206,7 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) ...@@ -2164,7 +2206,7 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
for( p_dr = p_es->p_first_descriptor; p_dr != NULL; for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
p_dr = p_dr->p_next ) p_dr = p_dr->p_next )
{ {
msg_Dbg( p_demux, " * es pid=0x%x type=0x%x dr->i_tag=0x%x", msg_Dbg( p_demux, " * es pid=%d type=%d dr->i_tag=0x%x",
p_es->i_pid, p_es->i_type, p_dr->i_tag ); p_es->i_pid, p_es->i_type, p_dr->i_tag );
if( p_dr->i_tag == 0x6a ) if( p_dr->i_tag == 0x6a )
...@@ -2191,6 +2233,10 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) ...@@ -2191,6 +2233,10 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
msg_Dbg( p_demux, " * Teletext descriptor" ); msg_Dbg( p_demux, " * Teletext descriptor" );
pid->es->fmt.i_cat = SPU_ES; pid->es->fmt.i_cat = SPU_ES;
pid->es->fmt.i_codec = VLC_FOURCC( 't', 'e', 'l', 'x' ); pid->es->fmt.i_codec = VLC_FOURCC( 't', 'e', 'l', 'x' );
pid->es->fmt.i_extra = p_dr->i_length;
pid->es->fmt.p_extra = malloc( p_dr->i_length );
memcpy( pid->es->fmt.p_extra, p_dr->p_data,
p_dr->i_length );
} }
#ifdef _DVBPSI_DR_59_H_ #ifdef _DVBPSI_DR_59_H_
else if( p_dr->i_tag == 0x59 ) else if( p_dr->i_tag == 0x59 )
...@@ -2314,14 +2360,12 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) ...@@ -2314,14 +2360,12 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
pid->es->fmt.i_group = p_pmt->i_program_number; pid->es->fmt.i_group = p_pmt->i_program_number;
if( pid->es->fmt.i_cat == UNKNOWN_ES ) if( pid->es->fmt.i_cat == UNKNOWN_ES )
{ {
msg_Dbg( p_demux, " * es pid=0x%x type=0x%x *unknown*", msg_Dbg( p_demux, " * es pid=%d type=%d *unknown*",
p_es->i_pid, p_es->i_type ); p_es->i_pid, p_es->i_type );
} }
else if( !p_sys->b_udp_out ) else if( !p_sys->b_udp_out )
{ {
int i; msg_Dbg( p_demux, " * es pid=%d type=%d fcc=%4.4s",
msg_Dbg( p_demux, " * es pid=0x%x type=0x%x fcc=%4.4s",
p_es->i_pid, p_es->i_type, (char*)&pid->es->fmt.i_codec ); p_es->i_pid, p_es->i_type, (char*)&pid->es->fmt.i_codec );
if( p_sys->b_es_id_pid ) pid->es->fmt.i_id = p_es->i_pid; if( p_sys->b_es_id_pid ) pid->es->fmt.i_id = p_es->i_pid;
...@@ -2365,6 +2409,7 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) ...@@ -2365,6 +2409,7 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
} }
} }
i_cad_length = 0;
/* Add ES to the list */ /* Add ES to the list */
if( old_pid ) if( old_pid )
{ {
...@@ -2379,14 +2424,7 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) ...@@ -2379,14 +2424,7 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
if( p_dr->i_tag == 0x9 ) if( p_dr->i_tag == 0x9 )
{ {
msg_Dbg( p_demux, " * descriptor : CA (0x9)" ); msg_Dbg( p_demux, " * descriptor : CA (0x9)" );
i_cad_length += p_dr->i_length + 2;
prg->cad[prg->i_nb_cad] = malloc( p_dr->i_length + 2 );
prg->cad[prg->i_nb_cad][0] = 0x9;
prg->cad[prg->i_nb_cad][1] = p_dr->i_length;
memcpy( prg->cad[prg->i_nb_cad] + 2, p_dr->p_data,
p_dr->i_length );
prg->i_cad_length[prg->i_nb_cad] = p_dr->i_length + 2;
prg->i_nb_cad++;
} }
else else
{ {
...@@ -2395,8 +2433,64 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) ...@@ -2395,8 +2433,64 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
} }
} }
if( p_sys->b_dvb_control && ( p_sys->i_dvb_program < 0 || if ( i_cad_length )
p_sys->i_dvb_program == prg->i_number ) ) {
if ( !prg->i_capmt_size )
{
prg->p_capmt = malloc( 5 + 6 + i_cad_length );
prg->i_capmt_size = 5 + 6 + i_cad_length;
prg->p_capmt[0] = p_pmt->i_program_number >> 8;
prg->p_capmt[1] = p_pmt->i_program_number & 0xff;
prg->p_capmt[2] = (p_pmt->i_version << 1) | 0x1;
prg->p_capmt[3] = 0; /* cad length */
prg->p_capmt[4] = 0;
i = 5;
}
else
{
prg->p_capmt = realloc( prg->p_capmt,
prg->i_capmt_size + 6 + i_cad_length );
i = prg->i_capmt_size;
prg->i_capmt_size += 6 + i_cad_length;
}
prg->p_capmt[i] = p_es->i_type;
prg->p_capmt[i+1] = p_es->i_pid >> 8;
prg->p_capmt[i+2] = p_es->i_pid & 0xff;
prg->p_capmt[i+3] = (i_cad_length + 1) >> 8;
prg->p_capmt[i+4] = (i_cad_length + 1) & 0xff;
prg->p_capmt[i+5] = 0x1; /* ok_descrambling */
i += 6;
for( p_dr = p_es->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
{
if( p_dr->i_tag == 0x9 )
{
prg->p_capmt[i] = 0x9;
prg->p_capmt[i+1] = p_dr->i_length;
memcpy( &prg->p_capmt[i+2], p_dr->p_data, p_dr->i_length );
i += p_dr->i_length + 2;
}
}
}
else if ( prg->i_capmt_size )
{
prg->p_capmt = realloc( prg->p_capmt,
prg->i_capmt_size + 5 );
i = prg->i_capmt_size;
prg->i_capmt_size += 5;
prg->p_capmt[i] = p_es->i_type;
prg->p_capmt[i+1] = p_es->i_pid >> 8;
prg->p_capmt[i+2] = p_es->i_pid & 0xff;
prg->p_capmt[i+3] = 0;
prg->p_capmt[i+4] = 0;
i += 5;
}
if( DVBProgramIsSelected( p_demux, prg->i_number ) )
{ {
/* Set demux filter */ /* Set demux filter */
stream_Control( p_demux->s, STREAM_CONTROL_ACCESS, stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
...@@ -2407,26 +2501,23 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) ...@@ -2407,26 +2501,23 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
dvbpsi_DeletePMT( p_pmt ); dvbpsi_DeletePMT( p_pmt );
for( i = 0; i < i_clean; i++ ) PIDClean( p_demux->out, pp_clean[i] ); for ( i = 0; i < i_clean; i++ )
if( i_clean ) free( pp_clean );
if( p_sys->b_dvb_control &&
( p_sys->i_dvb_program < 0 || p_sys->i_dvb_program == prg->i_number ) )
{ {
/* Set CAM descrambling */ if( DVBProgramIsSelected( p_demux, prg->i_number ) )
for ( i = 0; i < prg->i_nb_cad; i++ )
{ {
stream_Control( p_demux->s, STREAM_CONTROL_ACCESS, stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
ACCESS_SET_PRIVATE_ID_CA, prg->i_number, ACCESS_SET_PRIVATE_ID_STATE, pp_clean[i]->i_pid,
i_vpid, i_apid1, i_apid2, i_apid3, VLC_FALSE );
prg->i_cad_length[i], prg->cad[i] );
} }
stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
ACCESS_SET_PRIVATE_ID_CA, prg->i_number, PIDClean( p_demux->out, pp_clean[i] );
0, 0, 0, 0, 0, NULL ); }
stream_Control( p_demux->s, STREAM_CONTROL_ACCESS, if( i_clean ) free( pp_clean );
ACCESS_SET_PRIVATE_ID_CA, 0,
0, 0, 0, 0, 0, NULL ); if( DVBProgramIsSelected( p_demux, prg->i_number ) )
{
/* Set CAM descrambling */
DVBCAPMTSend( p_demux );
} }
} }
...@@ -2447,7 +2538,7 @@ static void PATCallBack( demux_t *p_demux, dvbpsi_pat_t *p_pat ) ...@@ -2447,7 +2538,7 @@ static void PATCallBack( demux_t *p_demux, dvbpsi_pat_t *p_pat )
return; return;
} }
msg_Dbg( p_demux, "new PAT ts_id=0x%x version=%d current_next=%d", msg_Dbg( p_demux, "new PAT ts_id=%d version=%d current_next=%d",
p_pat->i_ts_id, p_pat->i_version, p_pat->b_current_next ); p_pat->i_ts_id, p_pat->i_version, p_pat->b_current_next );
/* Clean old */ /* Clean old */
...@@ -2541,7 +2632,7 @@ static void PATCallBack( demux_t *p_demux, dvbpsi_pat_t *p_pat ) ...@@ -2541,7 +2632,7 @@ static void PATCallBack( demux_t *p_demux, dvbpsi_pat_t *p_pat )
for( p_program = p_pat->p_first_program; p_program != NULL; for( p_program = p_pat->p_first_program; p_program != NULL;
p_program = p_program->p_next ) p_program = p_program->p_next )
{ {
msg_Dbg( p_demux, " * number=%d pid=0x%x", p_program->i_number, msg_Dbg( p_demux, " * number=%d pid=%d", p_program->i_number,
p_program->i_pid ); p_program->i_pid );
if( p_program->i_number != 0 ) if( p_program->i_number != 0 )
{ {
...@@ -2601,3 +2692,118 @@ static void PATCallBack( demux_t *p_demux, dvbpsi_pat_t *p_pat ) ...@@ -2601,3 +2692,118 @@ static void PATCallBack( demux_t *p_demux, dvbpsi_pat_t *p_pat )
dvbpsi_DeletePAT( p_pat ); dvbpsi_DeletePAT( p_pat );
} }
static void DVBCAPMTSend( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
int i_nb_capmts = 0;
int i;
for( i = 0; i < p_sys->i_pmt; i++ )
{
ts_pid_t *pmt = p_sys->pmt[i];
int i_prg;
for( i_prg = 0; i_prg < pmt->psi->i_prg; i_prg++ )
{
if( DVBProgramIsSelected( p_demux, pmt->psi->prg[i_prg]->i_number )
&& pmt->psi->prg[i_prg]->i_capmt_size )
{
i_nb_capmts++;
}
}
}
if ( i_nb_capmts )
{
uint8_t **pp_capmts = malloc( i_nb_capmts * sizeof(uint8_t *) );
int i_current_capmt = 0;
for( i = 0; i < p_sys->i_pmt; i++ )
{
ts_pid_t *pmt = p_sys->pmt[i];
int i_prg;
for( i_prg = 0; i_prg < pmt->psi->i_prg; i_prg++ )
{
if( DVBProgramIsSelected( p_demux, pmt->psi->prg[i_prg]->i_number )
&& pmt->psi->prg[i_prg]->i_capmt_size )
{
uint8_t *p_capmt = malloc( pmt->psi->prg[i_prg]->i_capmt_size + 10 );
int i_pos = 0;
pp_capmts[i_current_capmt] = p_capmt;
p_capmt[i_pos] = 0x9F;
p_capmt[i_pos+1] = 0x80;
p_capmt[i_pos+2] = 0x32;
i_pos += 3;
if ( (pmt->psi->prg[i_prg]->i_capmt_size + 1) < 128 )
{
p_capmt[i_pos] = (pmt->psi->prg[i_prg]->i_capmt_size + 1);
i_pos++;
}
else if ( (pmt->psi->prg[i_prg]->i_capmt_size + 1) < 256 )
{
p_capmt[i_pos] = 0x81;
p_capmt[i_pos+1] = (pmt->psi->prg[i_prg]->i_capmt_size + 1);
i_pos += 2;
}
else if ( (pmt->psi->prg[i_prg]->i_capmt_size + 1) < 65536 )
{
p_capmt[i_pos] = 0x82;
p_capmt[i_pos+1] =
(pmt->psi->prg[i_prg]->i_capmt_size + 1) >> 8;
p_capmt[i_pos+2] =
(pmt->psi->prg[i_prg]->i_capmt_size + 1) & 0xff;
i_pos += 3;
}
else if ( (pmt->psi->prg[i_prg]->i_capmt_size + 1) < 16777216 )
{
p_capmt[i_pos] = 0x83;
p_capmt[i_pos+1] =
(pmt->psi->prg[i_prg]->i_capmt_size + 1) >> 16;
p_capmt[i_pos+2] =
((pmt->psi->prg[i_prg]->i_capmt_size + 1) >> 8) & 0xff;
p_capmt[i_pos+3] =
(pmt->psi->prg[i_prg]->i_capmt_size + 1) & 0xff;
i_pos += 4;
}
else
{
p_capmt[i_pos] = 0x84;
p_capmt[i_pos+1] =
(pmt->psi->prg[i_prg]->i_capmt_size + 1) >> 24;
p_capmt[i_pos+2] =
((pmt->psi->prg[i_prg]->i_capmt_size + 1) >> 16) & 0xff;
p_capmt[i_pos+3] =
((pmt->psi->prg[i_prg]->i_capmt_size + 1) >> 8) & 0xff;
p_capmt[i_pos+4] =
(pmt->psi->prg[i_prg]->i_capmt_size + 1) & 0xff;
i_pos += 5;
}
if ( i_nb_capmts > 1 )
{
if ( i_current_capmt == 0 )
p_capmt[i_pos] = 0x1; /* first */
else if ( i_current_capmt == i_nb_capmts - 1 )
p_capmt[i_pos] = 0x2; /* last */
else
p_capmt[i_pos] = 0x0; /* more */
}
else
p_capmt[i_pos] = 0x3; /* only */
i_pos++;
i_current_capmt++;
memcpy( &p_capmt[i_pos], pmt->psi->prg[i_prg]->p_capmt,
pmt->psi->prg[i_prg]->i_capmt_size );
}
}
}
stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
ACCESS_SET_PRIVATE_ID_CA, pp_capmts, i_nb_capmts );
}
}
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