From: Christophe Massiot Date: Fri, 22 Oct 2004 13:53:18 +0000 (+0000) Subject: * modules/access/dvb: Partial EN 50 221 implementation. This activates X-Git-Tag: 0.8.0~74 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=d6c80f229dea57b4b9c4f7e8c9cde5dea8d10c82;p=vlc * 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. --- diff --git a/modules/access/dvb/Modules.am b/modules/access/dvb/Modules.am index eb5cc04abd..af40f7cafe 100644 --- a/modules/access/dvb/Modules.am +++ b/modules/access/dvb/Modules.am @@ -1,5 +1,6 @@ SOURCES_dvb = \ access.c \ linux_dvb.c \ + en50221.c \ dvb.h \ $(NULL) diff --git a/modules/access/dvb/access.c b/modules/access/dvb/access.c index 8a3515af52..558974eaf0 100644 --- a/modules/access/dvb/access.c +++ b/modules/access/dvb/access.c @@ -58,9 +58,6 @@ static void Close( vlc_object_t *p_this ); #define DEVICE_TEXT N_("Device number to use on adapter") #define DEVICE_LONGTEXT "" -#define CAM_TEXT N_("Use CAM") -#define CAM_LONGTEXT "" - #define FREQ_TEXT N_("Transponder/multiplex frequency") #define FREQ_LONGTEXT N_("In kHz for DVB-S or Hz for DVB-C/T") @@ -131,7 +128,6 @@ vlc_module_begin(); VLC_FALSE ); add_integer( "dvb-device", 0, NULL, DEVICE_TEXT, DEVICE_LONGTEXT, VLC_TRUE ); - add_bool( "dvb-cam", 0, NULL, CAM_TEXT, CAM_LONGTEXT, VLC_FALSE ); add_integer( "dvb-frequency", 11954000, NULL, FREQ_TEXT, FREQ_LONGTEXT, VLC_FALSE ); add_integer( "dvb-inversion", 2, NULL, INVERSION_TEXT, INVERSION_LONGTEXT, @@ -190,7 +186,7 @@ vlc_module_end(); static block_t *Block( access_t * ); static int Control( access_t *, int, va_list ); -#define SATELLITE_READ_ONCE 3 +#define DVB_READ_ONCE 3 #define TS_PACKET_SIZE 188 static void FilterUnset( access_t *, int i_max ); @@ -274,13 +270,7 @@ static int Open( vlc_object_t *p_this ) FilterSet( p_access, 0x0, OTHER_TYPE ); } - p_sys->b_cam = var_GetBool( p_access, "dvb-cam" ); - if ( p_sys->b_cam ) - { - msg_Dbg( p_access, "initing CAM..." ); - if ( E_(CAMOpen)( p_access ) < 0 ) - p_sys->b_cam = VLC_FALSE; - } + E_(CAMOpen)( p_access ); return VLC_SUCCESS; } @@ -297,9 +287,7 @@ static void Close( vlc_object_t *p_this ) E_(DVRClose)( p_access ); E_(FrontendClose)( p_access ); - - if ( p_sys->b_cam ) - E_(CAMClose)( p_access ); + E_(CAMClose)( p_access ); free( p_sys ); } @@ -310,40 +298,52 @@ static void Close( vlc_object_t *p_this ) static block_t *Block( access_t *p_access ) { access_sys_t *p_sys = p_access->p_sys; - struct timeval timeout; - fd_set fds; - int i_ret; block_t *p_block; - /* Initialize file descriptor set */ - 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) ) + for ( ; ; ) { + struct timeval timeout; + fd_set fds; + int i_ret; + + /* Initialize file descriptor set */ 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; - 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; - } - if ( i_ret < 0 ) - { - msg_Err( p_access, "select error (%s)", strerror(errno) ); - return NULL; + if ( i_ret < 0 && errno == EINTR ) + continue; + + 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 ); - if( ( p_block->i_buffer = read( p_sys->i_handle, p_block->p_buffer, SATELLITE_READ_ONCE * TS_PACKET_SIZE ) ) <= 0 ) + 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, + DVB_READ_ONCE * TS_PACKET_SIZE ) ) <= 0 ) { msg_Err( p_access, "read failed (%s)", strerror(errno) ); block_Release( p_block ); @@ -376,7 +376,7 @@ static int Control( access_t *p_access, int i_query, va_list args ) /* */ case ACCESS_GET_MTU: pi_int = (int*)va_arg( args, int * ); - *pi_int = SATELLITE_READ_ONCE * TS_PACKET_SIZE; + *pi_int = DVB_READ_ONCE * TS_PACKET_SIZE; break; case ACCESS_GET_PTS_DELAY: @@ -405,26 +405,16 @@ static int Control( access_t *p_access, int i_query, va_list args ) break; case ACCESS_SET_PRIVATE_ID_CA: - if ( p_sys->b_cam ) - { - int i_program; - 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; + { + uint8_t **pp_capmts; + int i_nb_capmts; + 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: msg_Warn( p_access, "unimplemented query in control" ); return VLC_EGENERIC; @@ -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-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-inversion", VLC_VAR_INTEGER | 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 ) { GET_OPTION_INT("adapter") else GET_OPTION_INT("device") - else GET_OPTION_BOOL("cam") else GET_OPTION_INT("frequency") else GET_OPTION_INT("inversion") else GET_OPTION_BOOL("probe") diff --git a/modules/access/dvb/dvb.h b/modules/access/dvb/dvb.h index 47ec46fa37..d586dcfb2f 100644 --- a/modules/access/dvb/dvb.h +++ b/modules/access/dvb/dvb.h @@ -30,6 +30,7 @@ #define DMX "/dev/dvb/adapter%d/demux%d" #define FRONTEND "/dev/dvb/adapter%d/frontend%d" #define DVR "/dev/dvb/adapter%d/dvr%d" +#define CA "/dev/dvb/adapter%d/ca%d" /***************************************************************************** * Local structures @@ -43,7 +44,19 @@ typedef struct 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 { @@ -51,8 +64,16 @@ struct access_sys_t demux_handle_t p_demux_handles[MAX_DEMUX]; frontend_t *p_frontend; 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 @@ -77,6 +98,12 @@ int E_(DVROpen)( access_t * ); void E_(DVRClose)( access_t * ); int E_(CAMOpen)( access_t * ); -int E_(CAMSet)( access_t *, uint16_t, uint16_t, uint16_t, uint16_t, uint16_t, - uint16_t, uint8_t * ); +int E_(CAMPoll)( access_t * ); +int E_(CAMSet)( access_t *, uint8_t **, int ); 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 * ); + diff --git a/modules/access/dvb/en50221.c b/modules/access/dvb/en50221.c new file mode 100644 index 0000000000..bcf7977b42 --- /dev/null +++ b/modules/access/dvb/en50221.c @@ -0,0 +1,1118 @@ +/***************************************************************************** + * en50221.c : implementation of the transport, session and applications + * layers of EN 50 221 + ***************************************************************************** + * Copyright (C) 2004 VideoLAN + * + * Authors: Christophe Massiot + * 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 +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 */ +} diff --git a/modules/access/dvb/linux_dvb.c b/modules/access/dvb/linux_dvb.c index bee0e33a8c..4658a9559e 100644 --- a/modules/access/dvb/linux_dvb.c +++ b/modules/access/dvb/linux_dvb.c @@ -1,5 +1,5 @@ /***************************************************************************** - * 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 * @@ -42,13 +42,12 @@ #include #include #include - -#include +#include #include "dvb.h" -#include "network.h" #define DMX_BUFFER_SIZE (1024 * 1024) +#define CA_MAX_STATE_RETRY 5 /* * Frontends @@ -1108,60 +1107,121 @@ void E_(DVRClose)( access_t * p_access ) /* * CAM device - * - * This uses the external cam_set program from libdvb-0.5.4 */ /***************************************************************************** * CAMOpen : *****************************************************************************/ -int E_(CAMOpen)( access_t * p_access ) +int E_(CAMOpen)( access_t *p_access ) { 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 ); - if ( p_sys->i_cam_handle < 0 ) + i_active_slots = E_(en50221_Init)( p_access ); + + 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; } /***************************************************************************** - * CAMSet : + * CAMPoll : *****************************************************************************/ -int E_(CAMSet)( access_t * p_access, uint16_t i_program, uint16_t i_vpid, - uint16_t i_apid1, uint16_t i_apid2, uint16_t i_apid3, - uint16_t i_cad_length, uint8_t *p_cad ) +int E_(CAMPoll)( access_t * p_access ) { 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 ) - != i_cad_length ) - { - msg_Err( p_access, "write 2 failed (%s) %d", strerror(errno), - i_cad_length ); - return -VLC_EGENERIC; - } + return VLC_EGENERIC; } + E_(en50221_SetCAPMT)( p_access, pp_capmts, i_nb_capmts ); + return VLC_SUCCESS; } @@ -1172,9 +1232,11 @@ void E_(CAMClose)( access_t * p_access ) { 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 ); } } diff --git a/modules/demux/ts.c b/modules/demux/ts.c index 04a5f0b5f6..63f9ab5d56 100644 --- a/modules/demux/ts.c +++ b/modules/demux/ts.c @@ -183,8 +183,6 @@ typedef struct } iod_descriptor_t; -#define MAX_CAD 10 - typedef struct { dvbpsi_handle handle; @@ -196,10 +194,9 @@ typedef struct /* IOD stuff (mpeg4) */ iod_descriptor_t *iod; - /* Conditional Access descriptor */ - int i_nb_cad; - uint8_t *cad[MAX_CAD]; - uint8_t i_cad_length[MAX_CAD]; + /* Conditional Access PMT (EN 50 221) */ + uint8_t *p_capmt; + int i_capmt_size; } ts_prg_psi_t; @@ -277,6 +274,7 @@ struct demux_sys_t vlc_bool_t b_dvb_control; int i_dvb_program; + vlc_list_t *p_programs_list; }; static int Demux ( demux_t *p_demux ); @@ -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 void IODFree( iod_descriptor_t * ); +static void DVBCAPMTSend( demux_t *p_demux ); + #define TS_PACKET_SIZE_188 188 #define TS_PACKET_SIZE_192 192 #define TS_PACKET_SIZE_204 204 @@ -472,7 +472,7 @@ static int Open( vlc_object_t *p_this ) { 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 ); /* FIXME we should also ask for a number */ pmt->psi->prg[0]->handle = @@ -512,7 +512,7 @@ static int Open( vlc_object_t *p_this ) { 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, (char*)&pid->es->fmt.i_codec ); pid->es->id = es_out_Add( p_demux->out, @@ -610,7 +610,7 @@ static void Close( vlc_object_t *p_this ) 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 ) @@ -632,6 +632,14 @@ static void Close( vlc_object_t *p_this ) } 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 ); } @@ -744,7 +752,7 @@ static int Demux( demux_t *p_demux ) { 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 */ PCRHandle( p_demux, p_pid, p_pkt ); @@ -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; ts_prg_psi_t *p_prg = NULL; + vlc_list_t *p_list; 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 ) { @@ -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, ACCESS_SET_PRIVATE_ID_STATE, i_pmt_pid, 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++ ) { 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 ) } /* Set CAM descrambling */ - for ( i = 0; i < p_prg->i_nb_cad; i++ ) - { - 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 ); + DVBCAPMTSend( p_demux ); } } + else + { + p_sys->i_dvb_program = -1; + p_sys->p_programs_list = p_list; + } return VLC_SUCCESS; } @@ -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_pmt = -1; prg->iod = NULL; - prg->i_nb_cad = 0; + prg->p_capmt = NULL; + prg->i_capmt_size = 0; prg->handle = NULL; 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 ) if( pid->psi->handle ) dvbpsi_DetachPMT( pid->psi->handle ); for( i = 0; i < pid->psi->i_prg; i++ ) { - int j; if( pid->psi->prg[i]->iod ) IODFree( pid->psi->prg[i]->iod ); - for ( j = 0; j < pid->psi->prg[i]->i_nb_cad; j++ ) - free( pid->psi->prg[i]->cad[j] ); + if ( pid->psi->prg[i]->i_capmt_size ) + free( pid->psi->prg[i]->p_capmt ); if( pid->psi->prg[i]->handle ) dvbpsi_DetachPMT( pid->psi->prg[i]->handle ); free( pid->psi->prg[i] ); @@ -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( !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 ); block_ChainRelease( p_pes ); return; @@ -1335,7 +1341,7 @@ static vlc_bool_t GatherPES( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk ) int i_diff; #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, b_payload, i_cc ); #endif @@ -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 ) { - msg_Dbg( p_demux, "transport_error_indicator set (pid=0x%x)", + msg_Dbg( p_demux, "transport_error_indicator set (pid=%d)", pid->i_pid ); } @@ -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 ) { - msg_Warn( p_demux, "discontinuity_indicator (pid=0x%x) " + msg_Warn( p_demux, "discontinuity_indicator (pid=%d) " "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 ) { 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_cc = i_cc; } @@ -1906,6 +1912,27 @@ static void IODFree( iod_descriptor_t *p_iod ) ** 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 ) { 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 ) ts_pid_t *pmt = NULL; ts_prg_psi_t *prg = NULL; + + int i_cad_length = 0; ts_pid_t **pp_clean = NULL; 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" ); /* First find this PMT declared in PAT */ @@ -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 && 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 ); } } @@ -1976,15 +1995,29 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) IODFree( prg->iod ); prg->iod = NULL; } - for ( i = 0; i < prg->i_nb_cad; i++ ) - free( prg->cad[i] ); - prg->i_nb_cad = 0; + if ( prg->i_capmt_size ) + free( prg->p_capmt ); + 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 ); prg->i_pid_pcr = p_pmt->i_pcr_pid; 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 */ 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 ) else if( p_dr->i_tag == 0x9 ) { msg_Dbg( p_demux, " * descriptor : CA (0x9)" ); - - 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++; + i_cad_length += p_dr->i_length + 2; } else { @@ -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 ) { 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 ) 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 ); continue; } @@ -2039,15 +2091,6 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) pid->i_pid = p_es->i_pid; 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 || p_es->i_type == 0x12 ) { @@ -2058,7 +2101,6 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) 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]; 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 ) for( p_dr = p_es->p_first_descriptor; p_dr != NULL; 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 ); if( p_dr->i_tag == 0x6a ) @@ -2191,6 +2233,10 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) msg_Dbg( p_demux, " * Teletext descriptor" ); pid->es->fmt.i_cat = SPU_ES; 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_ else if( p_dr->i_tag == 0x59 ) @@ -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; 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 ); } else if( !p_sys->b_udp_out ) { - int i; - - msg_Dbg( p_demux, " * es pid=0x%x type=0x%x fcc=%4.4s", + msg_Dbg( p_demux, " * es pid=%d type=%d fcc=%4.4s", 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; @@ -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 */ if( old_pid ) { @@ -2379,14 +2424,7 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) if( p_dr->i_tag == 0x9 ) { msg_Dbg( p_demux, " * descriptor : CA (0x9)" ); - - 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++; + i_cad_length += p_dr->i_length + 2; } else { @@ -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 || - p_sys->i_dvb_program == prg->i_number ) ) + if ( i_cad_length ) + { + 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 */ stream_Control( p_demux->s, STREAM_CONTROL_ACCESS, @@ -2407,26 +2501,23 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt ) dvbpsi_DeletePMT( p_pmt ); - for( i = 0; i < i_clean; i++ ) PIDClean( p_demux->out, pp_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 ) ) + for ( i = 0; i < i_clean; i++ ) { - /* Set CAM descrambling */ - for ( i = 0; i < prg->i_nb_cad; i++ ) + if( DVBProgramIsSelected( p_demux, prg->i_number ) ) { stream_Control( p_demux->s, STREAM_CONTROL_ACCESS, - ACCESS_SET_PRIVATE_ID_CA, prg->i_number, - i_vpid, i_apid1, i_apid2, i_apid3, - prg->i_cad_length[i], prg->cad[i] ); + ACCESS_SET_PRIVATE_ID_STATE, pp_clean[i]->i_pid, + VLC_FALSE ); } - stream_Control( p_demux->s, STREAM_CONTROL_ACCESS, - ACCESS_SET_PRIVATE_ID_CA, 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 ); + + PIDClean( p_demux->out, pp_clean[i] ); + } + if( i_clean ) free( pp_clean ); + + 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 ) 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 ); /* Clean old */ @@ -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; 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 ); if( p_program->i_number != 0 ) { @@ -2601,3 +2692,118 @@ static void PATCallBack( demux_t *p_demux, dvbpsi_pat_t *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 ); + } +}