2002-08-04 14:57:29 +02:00
/*
* dvbdevice . c : The DVB device interface
*
* See the main source file ' vdr . c ' for copyright information and
* how to reach the author .
*
2005-12-29 14:51:59 +01:00
* $ Id : dvbdevice . c 1.143 2005 / 12 / 29 13 : 49 : 09 kls Exp $
2002-08-04 14:57:29 +02:00
*/
# include "dvbdevice.h"
# include <errno.h>
# include <limits.h>
# include <linux/videodev.h>
2002-08-10 14:58:25 +02:00
# include <linux/dvb/audio.h>
2002-09-04 17:26:02 +02:00
# include <linux/dvb/dmx.h>
2002-08-10 14:58:25 +02:00
# include <linux/dvb/frontend.h>
# include <linux/dvb/video.h>
2002-08-04 14:57:29 +02:00
# include <sys/ioctl.h>
# include <sys/mman.h>
2002-10-06 10:25:42 +02:00
# include "channels.h"
# include "diseqc.h"
2002-08-04 14:57:29 +02:00
# include "dvbosd.h"
2003-05-24 13:38:28 +02:00
# include "eitscan.h"
2002-08-04 14:57:29 +02:00
# include "player.h"
# include "receiver.h"
# include "status.h"
# include "transfer.h"
2002-11-03 12:31:51 +01:00
# define DO_REC_AND_PLAY_ON_PRIMARY_DEVICE 1
2002-11-16 12:36:50 +01:00
# define DO_MULTIPLE_RECORDINGS 1
2005-11-26 13:39:47 +01:00
//#define DO_MULTIPLE_CA_CHANNELS
2002-11-03 12:31:51 +01:00
2002-08-04 14:57:29 +02:00
# define DEV_VIDEO " / dev / video"
2002-08-10 14:58:25 +02:00
# define DEV_DVB_ADAPTER " / dev / dvb / adapter"
# define DEV_DVB_OSD "osd"
# define DEV_DVB_FRONTEND "frontend"
# define DEV_DVB_DVR "dvr"
# define DEV_DVB_DEMUX "demux"
# define DEV_DVB_VIDEO "video"
# define DEV_DVB_AUDIO "audio"
2003-01-06 14:44:27 +01:00
# define DEV_DVB_CA "ca"
2002-08-10 14:58:25 +02:00
2004-11-27 10:28:51 +01:00
class cDvbName {
private :
char buffer [ PATH_MAX ] ;
public :
cDvbName ( const char * Name , int n ) {
snprintf ( buffer , sizeof ( buffer ) , " %s%d/%s%d " , DEV_DVB_ADAPTER , n , Name , 0 ) ;
}
const char * operator * ( ) { return buffer ; }
} ;
2002-08-04 14:57:29 +02:00
2002-08-10 14:58:25 +02:00
static int DvbOpen ( const char * Name , int n , int Mode , bool ReportError = false )
2002-08-04 14:57:29 +02:00
{
2004-11-27 10:28:51 +01:00
const char * FileName = * cDvbName ( Name , n ) ;
2002-08-04 14:57:29 +02:00
int fd = open ( FileName , Mode ) ;
if ( fd < 0 & & ReportError )
LOG_ERROR_STR ( FileName ) ;
return fd ;
}
2002-12-08 09:55:26 +01:00
// --- cDvbTuner -------------------------------------------------------------
class cDvbTuner : public cThread {
private :
2005-11-26 13:39:47 +01:00
enum eTunerStatus { tsIdle , tsSet , tsTuned , tsLocked } ;
2002-12-08 09:55:26 +01:00
int fd_frontend ;
int cardIndex ;
fe_type_t frontendType ;
2003-01-06 14:44:27 +01:00
cCiHandler * ciHandler ;
2002-12-08 09:55:26 +01:00
cChannel channel ;
const char * diseqcCommands ;
eTunerStatus tunerStatus ;
2004-10-23 10:04:01 +02:00
cMutex mutex ;
cCondVar locked ;
2005-08-21 09:25:51 +02:00
cCondVar newSet ;
2004-10-30 14:21:13 +02:00
bool GetFrontendEvent ( dvb_frontend_event & Event , int TimeoutMs = 0 ) ;
2002-12-08 09:55:26 +01:00
bool SetFrontend ( void ) ;
virtual void Action ( void ) ;
public :
2003-01-06 14:44:27 +01:00
cDvbTuner ( int Fd_Frontend , int CardIndex , fe_type_t FrontendType , cCiHandler * CiHandler ) ;
2002-12-08 09:55:26 +01:00
virtual ~ cDvbTuner ( ) ;
bool IsTunedTo ( const cChannel * Channel ) const ;
2005-11-26 13:39:47 +01:00
void Set ( const cChannel * Channel , bool Tune ) ;
2004-10-23 10:04:01 +02:00
bool Locked ( int TimeoutMs = 0 ) ;
2002-12-08 09:55:26 +01:00
} ;
2003-01-06 14:44:27 +01:00
cDvbTuner : : cDvbTuner ( int Fd_Frontend , int CardIndex , fe_type_t FrontendType , cCiHandler * CiHandler )
2002-12-08 09:55:26 +01:00
{
fd_frontend = Fd_Frontend ;
cardIndex = CardIndex ;
frontendType = FrontendType ;
2003-01-06 14:44:27 +01:00
ciHandler = CiHandler ;
2002-12-08 09:55:26 +01:00
diseqcCommands = NULL ;
tunerStatus = tsIdle ;
2004-06-05 14:37:26 +02:00
if ( frontendType = = FE_QPSK )
CHECK ( ioctl ( fd_frontend , FE_SET_VOLTAGE , SEC_VOLTAGE_13 ) ) ; // must explicitly turn on LNB power
2003-10-18 12:29:08 +02:00
SetDescription ( " tuner on device %d " , cardIndex + 1 ) ;
2002-12-08 09:55:26 +01:00
Start ( ) ;
}
cDvbTuner : : ~ cDvbTuner ( )
{
tunerStatus = tsIdle ;
2005-08-21 09:25:51 +02:00
newSet . Broadcast ( ) ;
locked . Broadcast ( ) ;
2002-12-08 09:55:26 +01:00
Cancel ( 3 ) ;
}
bool cDvbTuner : : IsTunedTo ( const cChannel * Channel ) const
{
2004-01-04 12:30:00 +01:00
return tunerStatus ! = tsIdle & & channel . Source ( ) = = Channel - > Source ( ) & & channel . Transponder ( ) = = Channel - > Transponder ( ) ;
2002-12-08 09:55:26 +01:00
}
2005-11-26 13:39:47 +01:00
void cDvbTuner : : Set ( const cChannel * Channel , bool Tune )
2002-12-08 09:55:26 +01:00
{
2005-08-21 09:25:51 +02:00
cMutexLock MutexLock ( & mutex ) ;
2003-01-06 14:44:27 +01:00
if ( Tune )
tunerStatus = tsSet ;
2003-04-19 14:46:58 +02:00
channel = * Channel ;
2005-08-21 09:25:51 +02:00
newSet . Broadcast ( ) ;
2002-12-08 09:55:26 +01:00
}
2004-10-23 10:04:01 +02:00
bool cDvbTuner : : Locked ( int TimeoutMs )
{
2005-08-21 09:25:51 +02:00
bool isLocked = ( tunerStatus > = tsLocked ) ;
if ( isLocked | | ! TimeoutMs )
return isLocked ;
2004-10-23 10:04:01 +02:00
cMutexLock MutexLock ( & mutex ) ;
if ( TimeoutMs & & tunerStatus < tsLocked )
locked . TimedWait ( mutex , TimeoutMs ) ;
return tunerStatus > = tsLocked ;
}
2004-10-30 14:21:13 +02:00
bool cDvbTuner : : GetFrontendEvent ( dvb_frontend_event & Event , int TimeoutMs )
{
if ( TimeoutMs ) {
struct pollfd pfd ;
pfd . fd = fd_frontend ;
pfd . events = POLLIN | POLLPRI ;
do {
int stat = poll ( & pfd , 1 , TimeoutMs ) ;
if ( stat = = 1 )
break ;
if ( stat < 0 ) {
if ( errno = = EINTR )
continue ;
esyslog ( " ERROR: frontend %d poll failed: %m " , cardIndex ) ;
}
return false ;
} while ( 0 ) ;
}
do {
int stat = ioctl ( fd_frontend , FE_GET_EVENT , & Event ) ;
if ( stat = = 0 )
return true ;
if ( stat < 0 ) {
if ( errno = = EINTR )
continue ;
}
} while ( 0 ) ;
return false ;
}
2002-12-08 09:55:26 +01:00
static unsigned int FrequencyToHz ( unsigned int f )
{
while ( f & & f < 1000000 )
f * = 1000 ;
return f ;
}
bool cDvbTuner : : SetFrontend ( void )
{
dvb_frontend_parameters Frontend ;
memset ( & Frontend , 0 , sizeof ( Frontend ) ) ;
switch ( frontendType ) {
case FE_QPSK : { // DVB-S
unsigned int frequency = channel . Frequency ( ) ;
if ( Setup . DiSEqC ) {
cDiseqc * diseqc = Diseqcs . Get ( channel . Source ( ) , channel . Frequency ( ) , channel . Polarization ( ) ) ;
if ( diseqc ) {
if ( diseqc - > Commands ( ) & & ( ! diseqcCommands | | strcmp ( diseqcCommands , diseqc - > Commands ( ) ) ! = 0 ) ) {
cDiseqc : : eDiseqcActions da ;
for ( char * CurrentAction = NULL ; ( da = diseqc - > Execute ( & CurrentAction ) ) ! = cDiseqc : : daNone ; ) {
switch ( da ) {
case cDiseqc : : daNone : break ;
case cDiseqc : : daToneOff : CHECK ( ioctl ( fd_frontend , FE_SET_TONE , SEC_TONE_OFF ) ) ; break ;
case cDiseqc : : daToneOn : CHECK ( ioctl ( fd_frontend , FE_SET_TONE , SEC_TONE_ON ) ) ; break ;
case cDiseqc : : daVoltage13 : CHECK ( ioctl ( fd_frontend , FE_SET_VOLTAGE , SEC_VOLTAGE_13 ) ) ; break ;
case cDiseqc : : daVoltage18 : CHECK ( ioctl ( fd_frontend , FE_SET_VOLTAGE , SEC_VOLTAGE_18 ) ) ; break ;
case cDiseqc : : daMiniA : CHECK ( ioctl ( fd_frontend , FE_DISEQC_SEND_BURST , SEC_MINI_A ) ) ; break ;
case cDiseqc : : daMiniB : CHECK ( ioctl ( fd_frontend , FE_DISEQC_SEND_BURST , SEC_MINI_B ) ) ; break ;
case cDiseqc : : daCodes : {
int n = 0 ;
uchar * codes = diseqc - > Codes ( n ) ;
if ( codes ) {
struct dvb_diseqc_master_cmd cmd ;
memcpy ( cmd . msg , codes , min ( n , int ( sizeof ( cmd . msg ) ) ) ) ;
cmd . msg_len = n ;
CHECK ( ioctl ( fd_frontend , FE_DISEQC_SEND_MASTER_CMD , & cmd ) ) ;
}
}
break ;
}
}
diseqcCommands = diseqc - > Commands ( ) ;
}
frequency - = diseqc - > Lof ( ) ;
}
else {
esyslog ( " ERROR: no DiSEqC parameters found for channel %d " , channel . Number ( ) ) ;
return false ;
}
}
else {
int tone = SEC_TONE_OFF ;
if ( frequency < ( unsigned int ) Setup . LnbSLOF ) {
frequency - = Setup . LnbFrequLo ;
tone = SEC_TONE_OFF ;
}
else {
frequency - = Setup . LnbFrequHi ;
tone = SEC_TONE_ON ;
}
2005-01-09 13:18:15 +01:00
int volt = ( channel . Polarization ( ) = = ' v ' | | channel . Polarization ( ) = = ' V ' | | channel . Polarization ( ) = = ' r ' | | channel . Polarization ( ) = = ' R ' ) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18 ;
2002-12-08 09:55:26 +01:00
CHECK ( ioctl ( fd_frontend , FE_SET_VOLTAGE , volt ) ) ;
CHECK ( ioctl ( fd_frontend , FE_SET_TONE , tone ) ) ;
}
2003-03-30 11:44:40 +02:00
frequency = abs ( frequency ) ; // Allow for C-band, where the frequency is less than the LOF
2002-12-08 09:55:26 +01:00
Frontend . frequency = frequency * 1000UL ;
Frontend . inversion = fe_spectral_inversion_t ( channel . Inversion ( ) ) ;
Frontend . u . qpsk . symbol_rate = channel . Srate ( ) * 1000UL ;
Frontend . u . qpsk . fec_inner = fe_code_rate_t ( channel . CoderateH ( ) ) ;
}
break ;
case FE_QAM : { // DVB-C
// Frequency and symbol rate:
Frontend . frequency = FrequencyToHz ( channel . Frequency ( ) ) ;
Frontend . inversion = fe_spectral_inversion_t ( channel . Inversion ( ) ) ;
Frontend . u . qam . symbol_rate = channel . Srate ( ) * 1000UL ;
Frontend . u . qam . fec_inner = fe_code_rate_t ( channel . CoderateH ( ) ) ;
Frontend . u . qam . modulation = fe_modulation_t ( channel . Modulation ( ) ) ;
}
break ;
case FE_OFDM : { // DVB-T
// Frequency and OFDM paramaters:
Frontend . frequency = FrequencyToHz ( channel . Frequency ( ) ) ;
Frontend . inversion = fe_spectral_inversion_t ( channel . Inversion ( ) ) ;
Frontend . u . ofdm . bandwidth = fe_bandwidth_t ( channel . Bandwidth ( ) ) ;
Frontend . u . ofdm . code_rate_HP = fe_code_rate_t ( channel . CoderateH ( ) ) ;
Frontend . u . ofdm . code_rate_LP = fe_code_rate_t ( channel . CoderateL ( ) ) ;
Frontend . u . ofdm . constellation = fe_modulation_t ( channel . Modulation ( ) ) ;
Frontend . u . ofdm . transmission_mode = fe_transmit_mode_t ( channel . Transmission ( ) ) ;
Frontend . u . ofdm . guard_interval = fe_guard_interval_t ( channel . Guard ( ) ) ;
Frontend . u . ofdm . hierarchy_information = fe_hierarchy_t ( channel . Hierarchy ( ) ) ;
}
break ;
default :
esyslog ( " ERROR: attempt to set channel with unknown DVB frontend type " ) ;
return false ;
}
if ( ioctl ( fd_frontend , FE_SET_FRONTEND , & Frontend ) < 0 ) {
esyslog ( " ERROR: frontend %d: %m " , cardIndex ) ;
return false ;
}
return true ;
}
void cDvbTuner : : Action ( void )
{
2004-10-30 14:21:13 +02:00
dvb_frontend_event event ;
2005-08-14 11:24:57 +02:00
while ( Running ( ) ) {
2005-08-21 09:25:51 +02:00
bool hasEvent = GetFrontendEvent ( event , 1 ) ;
cMutexLock MutexLock ( & mutex ) ;
switch ( tunerStatus ) {
case tsIdle :
break ;
case tsSet :
if ( hasEvent )
continue ;
tunerStatus = SetFrontend ( ) ? tsTuned : tsIdle ;
continue ;
case tsTuned :
case tsLocked :
if ( hasEvent ) {
if ( event . status & FE_REINIT ) {
tunerStatus = tsSet ;
esyslog ( " ERROR: frontend %d was reinitialized - re-tuning " , cardIndex ) ;
}
if ( event . status & FE_HAS_LOCK ) {
tunerStatus = tsLocked ;
locked . Broadcast ( ) ;
}
continue ;
}
}
2005-11-26 13:39:47 +01:00
if ( ciHandler )
ciHandler - > Process ( ) ;
2004-10-30 14:21:13 +02:00
if ( tunerStatus ! = tsTuned )
2005-11-26 13:39:47 +01:00
newSet . TimedWait ( mutex , 1000 ) ;
2002-12-08 09:55:26 +01:00
}
}
// --- cDvbDevice ------------------------------------------------------------
2003-10-04 12:42:58 +02:00
int cDvbDevice : : devVideoOffset = - 1 ;
2005-02-13 14:26:37 +01:00
bool cDvbDevice : : setTransferModeForDolbyDigital = true ;
2003-10-04 12:42:58 +02:00
2002-08-04 14:57:29 +02:00
cDvbDevice : : cDvbDevice ( int n )
{
2002-12-08 09:55:26 +01:00
dvbTuner = NULL ;
2002-10-11 13:23:44 +02:00
frontendType = fe_type_t ( - 1 ) ; // don't know how else to initialize this - there is no FE_UNKNOWN
2002-09-08 14:17:51 +02:00
spuDecoder = NULL ;
2004-12-17 14:55:49 +01:00
digitalAudio = false ;
2002-08-15 11:16:34 +02:00
playMode = pmNone ;
2002-08-04 14:57:29 +02:00
// Devices that are present on all card types:
2003-12-22 13:29:24 +01:00
int fd_frontend = DvbOpen ( DEV_DVB_FRONTEND , n , O_RDWR | O_NONBLOCK ) ;
2003-10-17 15:36:13 +02:00
2002-08-04 14:57:29 +02:00
// Devices that are only present on cards with decoders:
2002-08-10 14:58:25 +02:00
fd_osd = DvbOpen ( DEV_DVB_OSD , n , O_RDWR ) ;
fd_video = DvbOpen ( DEV_DVB_VIDEO , n , O_RDWR | O_NONBLOCK ) ;
fd_audio = DvbOpen ( DEV_DVB_AUDIO , n , O_RDWR | O_NONBLOCK ) ;
2003-11-07 14:16:25 +01:00
fd_stc = DvbOpen ( DEV_DVB_DEMUX , n , O_RDWR ) ;
2002-09-04 17:26:02 +02:00
2002-08-04 14:57:29 +02:00
// The DVR device (will be opened and closed as needed):
fd_dvr = - 1 ;
2003-10-04 12:42:58 +02:00
// The offset of the /dev/video devices:
if ( devVideoOffset < 0 ) { // the first one checks this
FILE * f = NULL ;
char buffer [ PATH_MAX ] ;
for ( int ofs = 0 ; ofs < 100 ; ofs + + ) {
snprintf ( buffer , sizeof ( buffer ) , " /proc/video/dev/video%d " , ofs ) ;
if ( ( f = fopen ( buffer , " r " ) ) ! = NULL ) {
if ( fgets ( buffer , sizeof ( buffer ) , f ) ) {
if ( strstr ( buffer , " DVB Board " ) ) { // found the _first_ DVB card
devVideoOffset = ofs ;
dsyslog ( " video device offset is %d " , devVideoOffset ) ;
break ;
}
}
else
break ;
fclose ( f ) ;
}
else
break ;
}
if ( devVideoOffset < 0 )
devVideoOffset = 0 ;
if ( f )
fclose ( f ) ;
}
devVideoIndex = ( devVideoOffset > = 0 & & HasDecoder ( ) ) ? devVideoOffset + + : - 1 ;
2002-08-04 14:57:29 +02:00
// Video format:
2005-02-20 11:06:51 +01:00
SetVideoFormat ( Setup . VideoFormat ) ;
2002-08-04 14:57:29 +02:00
// We only check the devices that must be present - the others will be checked before accessing them://XXX
if ( fd_frontend > = 0 ) {
2002-08-10 14:58:25 +02:00
dvb_frontend_info feinfo ;
2002-12-08 09:55:26 +01:00
if ( ioctl ( fd_frontend , FE_GET_INFO , & feinfo ) > = 0 ) {
2002-08-04 14:57:29 +02:00
frontendType = feinfo . type ;
2004-11-27 10:28:51 +01:00
ciHandler = cCiHandler : : CreateCiHandler ( * cDvbName ( DEV_DVB_CA , n ) ) ;
2003-01-06 14:44:27 +01:00
dvbTuner = new cDvbTuner ( fd_frontend , CardIndex ( ) , frontendType , ciHandler ) ;
2002-12-08 09:55:26 +01:00
}
2002-08-04 14:57:29 +02:00
else
LOG_ERROR ;
}
else
esyslog ( " ERROR: can't open DVB device %d " , n ) ;
2003-12-22 13:29:24 +01:00
StartSectionHandler ( ) ;
2002-08-04 14:57:29 +02:00
}
cDvbDevice : : ~ cDvbDevice ( )
{
2002-09-08 14:17:51 +02:00
delete spuDecoder ;
2002-12-08 09:55:26 +01:00
delete dvbTuner ;
2002-08-04 14:57:29 +02:00
// We're not explicitly closing any device files here, since this sometimes
// caused segfaults. Besides, the program is about to terminate anyway...
}
bool cDvbDevice : : Probe ( const char * FileName )
{
if ( access ( FileName , F_OK ) = = 0 ) {
dsyslog ( " probing %s " , FileName ) ;
int f = open ( FileName , O_RDONLY ) ;
if ( f > = 0 ) {
close ( f ) ;
return true ;
}
else if ( errno ! = ENODEV & & errno ! = EINVAL )
LOG_ERROR_STR ( FileName ) ;
}
else if ( errno ! = ENOENT )
LOG_ERROR_STR ( FileName ) ;
return false ;
}
bool cDvbDevice : : Initialize ( void )
{
int found = 0 ;
int i ;
for ( i = 0 ; i < MAXDVBDEVICES ; i + + ) {
if ( UseDevice ( NextCardIndex ( ) ) ) {
2004-11-27 10:28:51 +01:00
if ( Probe ( * cDvbName ( DEV_DVB_FRONTEND , i ) ) ) {
2002-08-04 14:57:29 +02:00
new cDvbDevice ( i ) ;
found + + ;
}
else
break ;
}
else
NextCardIndex ( 1 ) ; // skips this one
}
NextCardIndex ( MAXDVBDEVICES - i ) ; // skips the rest
if ( found > 0 )
isyslog ( " found %d video device%s " , found , found > 1 ? " s " : " " ) ;
else
isyslog ( " no DVB device found " ) ;
return found > 0 ;
}
void cDvbDevice : : MakePrimaryDevice ( bool On )
{
2003-04-12 12:20:07 +02:00
if ( HasDecoder ( ) )
2004-05-16 10:35:36 +02:00
new cDvbOsdProvider ( fd_osd ) ;
2002-08-04 14:57:29 +02:00
}
bool cDvbDevice : : HasDecoder ( void ) const
{
return fd_video > = 0 & & fd_audio > = 0 ;
}
2005-08-21 08:56:49 +02:00
bool cDvbDevice : : Ready ( void )
{
if ( ciHandler ) {
ciHandler - > Process ( ) ;
return ciHandler - > Ready ( ) ;
}
return true ;
}
2004-01-04 12:30:00 +01:00
int cDvbDevice : : ProvidesCa ( const cChannel * Channel ) const
{
if ( Channel - > Ca ( ) > = 0x0100 & & ciHandler ) {
unsigned short ids [ MAXCAIDS + 1 ] ;
for ( int i = 0 ; i < = MAXCAIDS ; i + + ) // '<=' copies the terminating 0!
ids [ i ] = Channel - > Ca ( i ) ;
return ciHandler - > ProvidesCa ( ids ) ;
}
return cDevice : : ProvidesCa ( Channel ) ;
}
2002-09-08 14:17:51 +02:00
cSpuDecoder * cDvbDevice : : GetSpuDecoder ( void )
{
if ( ! spuDecoder & & IsPrimaryDevice ( ) )
spuDecoder = new cDvbSpuDecoder ( ) ;
return spuDecoder ;
}
2005-12-29 14:51:59 +01:00
uchar * cDvbDevice : : GrabImage ( int & Size , bool Jpeg , int Quality , int SizeX , int SizeY )
2002-08-04 14:57:29 +02:00
{
2003-10-04 12:42:58 +02:00
if ( devVideoIndex < 0 )
2005-12-29 14:51:59 +01:00
return NULL ;
2002-09-15 13:14:42 +02:00
char buffer [ PATH_MAX ] ;
2003-10-04 12:42:58 +02:00
snprintf ( buffer , sizeof ( buffer ) , " %s%d " , DEV_VIDEO , devVideoIndex ) ;
2002-09-15 13:14:42 +02:00
int videoDev = open ( buffer , O_RDWR ) ;
2002-08-04 14:57:29 +02:00
if ( videoDev > = 0 ) {
2005-12-29 14:51:59 +01:00
uchar * result = NULL ;
2002-08-04 14:57:29 +02:00
struct video_mbuf mbuf ;
2005-12-29 14:51:59 +01:00
if ( ioctl ( videoDev , VIDIOCGMBUF , & mbuf ) = = 0 ) {
2002-08-04 14:57:29 +02:00
int msize = mbuf . size ;
unsigned char * mem = ( unsigned char * ) mmap ( 0 , msize , PROT_READ | PROT_WRITE , MAP_SHARED , videoDev , 0 ) ;
if ( mem & & mem ! = ( unsigned char * ) - 1 ) {
// set up the size and RGB
struct video_capability vc ;
2005-12-29 14:51:59 +01:00
if ( ioctl ( videoDev , VIDIOCGCAP , & vc ) = = 0 ) {
struct video_mmap vm ;
vm . frame = 0 ;
if ( ( SizeX > 0 ) & & ( SizeX < = vc . maxwidth ) & &
( SizeY > 0 ) & & ( SizeY < = vc . maxheight ) ) {
vm . width = SizeX ;
vm . height = SizeY ;
}
else {
vm . width = vc . maxwidth ;
vm . height = vc . maxheight ;
}
vm . format = VIDEO_PALETTE_RGB24 ;
if ( ioctl ( videoDev , VIDIOCMCAPTURE , & vm ) = = 0 & & ioctl ( videoDev , VIDIOCSYNC , & vm . frame ) = = 0 ) {
// make RGB out of BGR:
int memsize = vm . width * vm . height ;
unsigned char * mem1 = mem ;
for ( int i = 0 ; i < memsize ; i + + ) {
unsigned char tmp = mem1 [ 2 ] ;
mem1 [ 2 ] = mem1 [ 0 ] ;
mem1 [ 0 ] = tmp ;
mem1 + = 3 ;
}
2002-09-04 17:26:02 +02:00
2005-12-29 14:51:59 +01:00
if ( Quality < 0 )
Quality = 100 ;
isyslog ( " grabbing to %s %d %d %d " , Jpeg ? " JPEG " : " PNM " , Quality , vm . width , vm . height ) ;
if ( Jpeg ) {
// convert to JPEG:
result = RgbToJpeg ( mem , vm . width , vm . height , Size , Quality ) ;
if ( ! result )
esyslog ( " ERROR: failed to convert image to JPEG " ) ;
2005-12-29 11:24:02 +01:00
}
else {
2005-12-29 14:51:59 +01:00
// convert to PNM:
char buf [ 32 ] ;
snprintf ( buf , sizeof ( buf ) , " P6 \n %d \n %d \n 255 \n " , vm . width , vm . height ) ;
int l = strlen ( buf ) ;
int bytes = memsize * 3 ;
Size = l + bytes ;
result = MALLOC ( uchar , Size ) ;
if ( result ) {
memcpy ( result , buf , l ) ;
memcpy ( result + l , mem , bytes ) ;
}
else
esyslog ( " ERROR: failed to convert image to PNM " ) ;
2002-08-04 14:57:29 +02:00
}
}
}
munmap ( mem , msize ) ;
}
else
2005-12-29 14:51:59 +01:00
esyslog ( " ERROR: failed to memmap video device " ) ;
2002-08-04 14:57:29 +02:00
}
close ( videoDev ) ;
2005-12-29 14:51:59 +01:00
return result ;
2002-08-04 14:57:29 +02:00
}
2005-12-29 14:51:59 +01:00
else
LOG_ERROR_STR ( buffer ) ;
return NULL ;
2002-08-04 14:57:29 +02:00
}
2005-02-20 12:01:05 +01:00
void cDvbDevice : : SetVideoDisplayFormat ( eVideoDisplayFormat VideoDisplayFormat )
{
cDevice : : SetVideoDisplayFormat ( VideoDisplayFormat ) ;
if ( HasDecoder ( ) ) {
if ( Setup . VideoFormat ) {
2005-03-13 12:34:45 +01:00
CHECK ( ioctl ( fd_video , VIDEO_SET_DISPLAY_FORMAT , VIDEO_LETTER_BOX ) ) ;
2005-02-20 12:01:05 +01:00
}
else {
switch ( VideoDisplayFormat ) {
case vdfPanAndScan :
CHECK ( ioctl ( fd_video , VIDEO_SET_DISPLAY_FORMAT , VIDEO_PAN_SCAN ) ) ;
break ;
case vdfLetterBox :
CHECK ( ioctl ( fd_video , VIDEO_SET_DISPLAY_FORMAT , VIDEO_LETTER_BOX ) ) ;
break ;
case vdfCenterCutOut :
CHECK ( ioctl ( fd_video , VIDEO_SET_DISPLAY_FORMAT , VIDEO_CENTER_CUT_OUT ) ) ;
break ;
}
}
}
}
2002-08-04 14:57:29 +02:00
void cDvbDevice : : SetVideoFormat ( bool VideoFormat16_9 )
{
2005-02-20 12:01:05 +01:00
if ( HasDecoder ( ) ) {
2002-08-04 14:57:29 +02:00
CHECK ( ioctl ( fd_video , VIDEO_SET_FORMAT , VideoFormat16_9 ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3 ) ) ;
2005-02-20 13:39:49 +01:00
SetVideoDisplayFormat ( eVideoDisplayFormat ( Setup . VideoDisplayFormat ) ) ;
2005-02-20 12:01:05 +01:00
}
2002-08-04 14:57:29 +02:00
}
2003-08-15 13:05:50 +02:00
eVideoSystem cDvbDevice : : GetVideoSystem ( void )
{
2004-06-05 16:33:35 +02:00
eVideoSystem VideoSystem = vsPAL ;
2003-08-15 13:05:50 +02:00
video_size_t vs ;
if ( ioctl ( fd_video , VIDEO_GET_SIZE , & vs ) = = 0 ) {
if ( vs . h = = 480 | | vs . h = = 240 )
2004-06-05 16:33:35 +02:00
VideoSystem = vsNTSC ;
2003-08-15 13:05:50 +02:00
}
else
LOG_ERROR ;
2004-06-05 16:33:35 +02:00
return VideoSystem ;
2003-08-15 13:05:50 +02:00
}
2005-11-11 16:13:19 +01:00
bool cDvbDevice : : SetAudioBypass ( bool On )
{
return ioctl ( fd_audio , AUDIO_SET_BYPASS_MODE , On ) = = 0 ;
}
2003-04-26 11:58:54 +02:00
// ptAudio ptVideo ptPcr ptTeletext ptDolby ptOther
dmx_pes_type_t PesTypes [ ] = { DMX_PES_AUDIO , DMX_PES_VIDEO , DMX_PES_PCR , DMX_PES_TELETEXT , DMX_PES_OTHER , DMX_PES_OTHER } ;
2002-08-04 14:57:29 +02:00
bool cDvbDevice : : SetPid ( cPidHandle * Handle , int Type , bool On )
{
if ( Handle - > pid ) {
2002-10-11 13:23:44 +02:00
dmx_pes_filter_params pesFilterParams ;
2002-09-14 11:51:51 +02:00
memset ( & pesFilterParams , 0 , sizeof ( pesFilterParams ) ) ;
2002-08-04 14:57:29 +02:00
if ( On ) {
if ( Handle - > handle < 0 ) {
2002-08-10 14:58:25 +02:00
Handle - > handle = DvbOpen ( DEV_DVB_DEMUX , CardIndex ( ) , O_RDWR | O_NONBLOCK , true ) ;
2005-06-05 13:37:37 +02:00
if ( Handle - > handle < 0 ) {
LOG_ERROR ;
2002-08-04 14:57:29 +02:00
return false ;
2005-06-05 13:37:37 +02:00
}
2002-08-04 14:57:29 +02:00
}
2002-09-14 11:51:51 +02:00
pesFilterParams . pid = Handle - > pid ;
pesFilterParams . input = DMX_IN_FRONTEND ;
pesFilterParams . output = ( Type < = ptTeletext & & Handle - > used < = 1 ) ? DMX_OUT_DECODER : DMX_OUT_TS_TAP ;
2002-10-11 13:23:44 +02:00
pesFilterParams . pes_type = PesTypes [ Type < ptOther ? Type : ptOther ] ;
2002-09-14 11:51:51 +02:00
pesFilterParams . flags = DMX_IMMEDIATE_START ;
if ( ioctl ( Handle - > handle , DMX_SET_PES_FILTER , & pesFilterParams ) < 0 ) {
LOG_ERROR ;
return false ;
}
2002-08-04 14:57:29 +02:00
}
2002-09-14 13:26:16 +02:00
else if ( ! Handle - > used ) {
2002-08-04 14:57:29 +02:00
CHECK ( ioctl ( Handle - > handle , DMX_STOP ) ) ;
2002-09-14 13:26:16 +02:00
if ( Type < = ptTeletext ) {
2002-09-04 17:26:02 +02:00
pesFilterParams . pid = 0x1FFF ;
pesFilterParams . input = DMX_IN_FRONTEND ;
pesFilterParams . output = DMX_OUT_DECODER ;
2002-10-11 13:23:44 +02:00
pesFilterParams . pes_type = PesTypes [ Type ] ;
2002-09-04 17:26:02 +02:00
pesFilterParams . flags = DMX_IMMEDIATE_START ;
CHECK ( ioctl ( Handle - > handle , DMX_SET_PES_FILTER , & pesFilterParams ) ) ;
2002-09-14 11:51:51 +02:00
if ( PesTypes [ Type ] = = DMX_PES_VIDEO ) // let's only do this once
SetPlayMode ( pmNone ) ; // necessary to switch a PID from DMX_PES_VIDEO/AUDIO to DMX_PES_OTHER
2002-08-04 14:57:29 +02:00
}
2002-11-15 14:04:11 +01:00
close ( Handle - > handle ) ;
Handle - > handle = - 1 ;
2002-08-04 14:57:29 +02:00
}
}
return true ;
}
2003-12-22 13:29:24 +01:00
int cDvbDevice : : OpenFilter ( u_short Pid , u_char Tid , u_char Mask )
{
2004-11-27 10:28:51 +01:00
const char * FileName = * cDvbName ( DEV_DVB_DEMUX , CardIndex ( ) ) ;
2003-12-22 13:29:24 +01:00
int f = open ( FileName , O_RDWR | O_NONBLOCK ) ;
if ( f > = 0 ) {
dmx_sct_filter_params sctFilterParams ;
memset ( & sctFilterParams , 0 , sizeof ( sctFilterParams ) ) ;
sctFilterParams . pid = Pid ;
sctFilterParams . timeout = 0 ;
sctFilterParams . flags = DMX_IMMEDIATE_START ;
sctFilterParams . filter . filter [ 0 ] = Tid ;
sctFilterParams . filter . mask [ 0 ] = Mask ;
if ( ioctl ( f , DMX_SET_FILTER , & sctFilterParams ) > = 0 )
return f ;
else {
2004-01-10 12:21:41 +01:00
esyslog ( " ERROR: can't set filter (pid=%d, tid=%02X, mask=%02X): %m " , Pid , Tid , Mask ) ;
2003-12-22 13:29:24 +01:00
close ( f ) ;
}
}
else
esyslog ( " ERROR: can't open filter handle on '%s' " , FileName ) ;
return - 1 ;
}
2004-11-07 10:31:59 +01:00
void cDvbDevice : : TurnOffLiveMode ( bool LiveView )
2003-05-02 14:30:03 +02:00
{
2004-11-07 10:31:59 +01:00
if ( LiveView ) {
// Avoid noise while switching:
CHECK ( ioctl ( fd_audio , AUDIO_SET_MUTE , true ) ) ;
CHECK ( ioctl ( fd_video , VIDEO_SET_BLANK , true ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_CLEAR_BUFFER ) ) ;
CHECK ( ioctl ( fd_video , VIDEO_CLEAR_BUFFER ) ) ;
}
2003-05-02 14:30:03 +02:00
// Turn off live PIDs:
2005-06-05 13:37:37 +02:00
DetachAll ( pidHandles [ ptAudio ] . pid ) ;
DetachAll ( pidHandles [ ptVideo ] . pid ) ;
DetachAll ( pidHandles [ ptPcr ] . pid ) ;
DetachAll ( pidHandles [ ptTeletext ] . pid ) ;
2003-05-02 14:30:03 +02:00
DelPid ( pidHandles [ ptAudio ] . pid ) ;
DelPid ( pidHandles [ ptVideo ] . pid ) ;
DelPid ( pidHandles [ ptPcr ] . pid , ptPcr ) ;
DelPid ( pidHandles [ ptTeletext ] . pid ) ;
DelPid ( pidHandles [ ptDolby ] . pid ) ;
}
2002-10-06 10:25:42 +02:00
bool cDvbDevice : : ProvidesSource ( int Source ) const
{
int type = Source & cSource : : st_Mask ;
return type = = cSource : : stNone
| | type = = cSource : : stCable & & frontendType = = FE_QAM
| | type = = cSource : : stSat & & frontendType = = FE_QPSK
| | type = = cSource : : stTerr & & frontendType = = FE_OFDM ;
}
2004-01-04 12:30:00 +01:00
bool cDvbDevice : : ProvidesTransponder ( const cChannel * Channel ) const
{
2005-05-16 15:23:43 +02:00
return ProvidesSource ( Channel - > Source ( ) ) & & ( ! cSource : : IsSat ( Channel - > Source ( ) ) | | ! Setup . DiSEqC | | Diseqcs . Get ( Channel - > Source ( ) , Channel - > Frequency ( ) , Channel - > Polarization ( ) ) ) ;
2004-01-04 12:30:00 +01:00
}
2002-09-29 13:40:45 +02:00
bool cDvbDevice : : ProvidesChannel ( const cChannel * Channel , int Priority , bool * NeedsDetachReceivers ) const
2002-08-04 14:57:29 +02:00
{
2002-09-04 17:26:02 +02:00
bool result = false ;
bool hasPriority = Priority < 0 | | Priority > this - > Priority ( ) ;
2003-08-24 14:49:53 +02:00
bool needsDetachReceivers = false ;
2002-09-04 17:26:02 +02:00
2005-12-28 12:28:37 +01:00
if ( ProvidesSource ( Channel - > Source ( ) ) & & ProvidesCa ( Channel ) ) {
2002-12-13 15:35:00 +01:00
result = hasPriority ;
2004-05-22 15:36:09 +02:00
if ( Priority > = 0 & & Receiving ( true ) ) {
2002-12-08 09:55:26 +01:00
if ( dvbTuner - > IsTunedTo ( Channel ) ) {
2004-12-17 14:55:49 +01:00
if ( Channel - > Vpid ( ) & & ! HasPid ( Channel - > Vpid ( ) ) | | Channel - > Apid ( 0 ) & & ! HasPid ( Channel - > Apid ( 0 ) ) ) {
2002-12-13 15:35:00 +01:00
# ifdef DO_MULTIPLE_RECORDINGS
2005-11-26 13:39:47 +01:00
# ifndef DO_MULTIPLE_CA_CHANNELS
2004-06-19 13:48:25 +02:00
if ( Ca ( ) > CACONFBASE | | Channel - > Ca ( ) > CACONFBASE )
2005-03-05 16:13:26 +01:00
needsDetachReceivers = Ca ( ) ! = Channel - > Ca ( ) ;
2005-11-26 13:39:47 +01:00
else
# endif
if ( ! IsPrimaryDevice ( ) )
2002-09-14 11:51:51 +02:00
result = true ;
2002-11-03 12:31:51 +01:00
# ifdef DO_REC_AND_PLAY_ON_PRIMARY_DEVICE
else
result = Priority > = Setup . PrimaryLimit ;
2002-12-13 15:35:00 +01:00
# endif
2002-09-04 17:26:02 +02:00
# endif
}
else
result = ! IsPrimaryDevice ( ) | | Priority > = Setup . PrimaryLimit ;
}
2003-08-24 14:49:53 +02:00
else
needsDetachReceivers = true ;
2002-09-04 17:26:02 +02:00
}
}
2002-09-06 14:10:17 +02:00
if ( NeedsDetachReceivers )
* NeedsDetachReceivers = needsDetachReceivers ;
2002-09-04 17:26:02 +02:00
return result ;
}
bool cDvbDevice : : SetChannelDevice ( const cChannel * Channel , bool LiveView )
{
2002-12-08 09:55:26 +01:00
bool DoTune = ! dvbTuner - > IsTunedTo ( Channel ) ;
2002-08-04 14:57:29 +02:00
2002-09-15 10:51:44 +02:00
bool TurnOffLivePIDs = HasDecoder ( )
& & ( DoTune
2002-11-15 14:04:11 +01:00
| | ! IsPrimaryDevice ( )
| | LiveView // for a new live view the old PIDs need to be turned off
| | pidHandles [ ptVideo ] . pid = = Channel - > Vpid ( ) // for recording the PIDs must be shifted from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
2002-09-15 10:51:44 +02:00
) ;
2005-03-05 16:13:26 +01:00
bool StartTransferMode = IsPrimaryDevice ( ) & & ! DoTune
2005-02-08 14:18:18 +01:00
& & ( LiveView & & HasPid ( Channel - > Vpid ( ) ? Channel - > Vpid ( ) : Channel - > Apid ( 0 ) ) & & ( pidHandles [ ptVideo ] . pid ! = Channel - > Vpid ( ) | | pidHandles [ ptAudio ] . pid ! = Channel - > Apid ( 0 ) ) // the PID is already set as DMX_PES_OTHER
| | ! LiveView & & ( pidHandles [ ptVideo ] . pid = = Channel - > Vpid ( ) | | pidHandles [ ptAudio ] . pid = = Channel - > Apid ( 0 ) ) // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
2002-09-15 10:51:44 +02:00
) ;
2005-03-05 16:13:26 +01:00
bool TurnOnLivePIDs = HasDecoder ( ) & & ! StartTransferMode & & LiveView ;
2002-08-04 14:57:29 +02:00
2002-11-16 12:36:50 +01:00
# ifndef DO_MULTIPLE_RECORDINGS
TurnOffLivePIDs = TurnOnLivePIDs = true ;
StartTransferMode = false ;
# endif
2002-09-15 10:51:44 +02:00
// Turn off live PIDs if necessary:
2003-05-02 14:30:03 +02:00
if ( TurnOffLivePIDs )
2004-11-07 10:31:59 +01:00
TurnOffLiveMode ( LiveView ) ;
2002-08-04 14:57:29 +02:00
2004-10-24 08:50:15 +02:00
// Set the tuner:
2004-10-23 10:04:01 +02:00
2005-11-26 13:39:47 +01:00
dvbTuner - > Set ( Channel , DoTune ) ;
2002-08-04 14:57:29 +02:00
2004-10-24 08:50:15 +02:00
// If this channel switch was requested by the EITScanner we don't wait for
// a lock and don't set any live PIDs (the EITScanner will wait for the lock
// by itself before setting any filters):
2005-11-26 13:39:47 +01:00
if ( EITScanner . UsesDevice ( this ) ) //XXX
2004-10-24 08:50:15 +02:00
return true ;
2002-08-04 14:57:29 +02:00
// PID settings:
2002-09-15 10:51:44 +02:00
if ( TurnOnLivePIDs ) {
2005-11-11 16:13:19 +01:00
SetAudioBypass ( false ) ;
2005-01-06 14:56:08 +01:00
if ( ! ( AddPid ( Channel - > Ppid ( ) , ptPcr ) & & AddPid ( Channel - > Vpid ( ) , ptVideo ) & & AddPid ( Channel - > Apid ( 0 ) , ptAudio ) ) ) {
2002-10-06 10:25:42 +02:00
esyslog ( " ERROR: failed to set PIDs for channel %d on device %d " , Channel - > Number ( ) , CardIndex ( ) + 1 ) ;
2002-09-15 10:51:44 +02:00
return false ;
2002-08-04 14:57:29 +02:00
}
2005-12-03 11:51:05 +01:00
//XXX quick workaround for additional live audio PIDs:
if ( ciHandler ) {
ciHandler - > SetPid ( Channel - > Apid ( 1 ) , true ) ;
ciHandler - > SetPid ( Channel - > Dpid ( 0 ) , true ) ;
}
2002-09-15 10:51:44 +02:00
if ( IsPrimaryDevice ( ) )
2002-10-06 10:25:42 +02:00
AddPid ( Channel - > Tpid ( ) , ptTeletext ) ;
2004-06-12 14:42:55 +02:00
CHECK ( ioctl ( fd_audio , AUDIO_SET_MUTE , true ) ) ; // actually one would expect 'false' here, but according to Marco Schl<68> <6C> ler <marco@lordzodiac.de> this works
// to avoid missing audio after replaying a DVD; with 'false' there is an audio disturbance when switching
// between two channels on the same transponder on DVB-S
2002-09-15 10:51:44 +02:00
CHECK ( ioctl ( fd_audio , AUDIO_SET_AV_SYNC , true ) ) ;
2002-08-04 14:57:29 +02:00
}
2002-09-15 10:51:44 +02:00
else if ( StartTransferMode )
2005-01-16 14:40:47 +01:00
cControl : : Launch ( new cTransferControl ( this , Channel - > Vpid ( ) , Channel - > Apids ( ) , Channel - > Dpids ( ) , Channel - > Spids ( ) ) ) ;
2002-08-04 14:57:29 +02:00
return true ;
}
2004-10-30 15:10:50 +02:00
bool cDvbDevice : : HasLock ( int TimeoutMs )
2004-01-04 12:30:00 +01:00
{
2004-10-30 15:10:50 +02:00
return dvbTuner ? dvbTuner - > Locked ( TimeoutMs ) : false ;
2004-01-04 12:30:00 +01:00
}
2005-01-06 13:50:17 +01:00
int cDvbDevice : : GetAudioChannelDevice ( void )
{
if ( HasDecoder ( ) ) {
audio_status_t as ;
CHECK ( ioctl ( fd_audio , AUDIO_GET_STATUS , & as ) ) ;
return as . channel_select ;
}
return 0 ;
}
void cDvbDevice : : SetAudioChannelDevice ( int AudioChannel )
{
if ( HasDecoder ( ) )
CHECK ( ioctl ( fd_audio , AUDIO_CHANNEL_SELECT , AudioChannel ) ) ;
}
2002-08-04 14:57:29 +02:00
void cDvbDevice : : SetVolumeDevice ( int Volume )
{
if ( HasDecoder ( ) ) {
2005-02-06 12:31:36 +01:00
if ( digitalAudio )
Volume = 0 ;
2002-10-11 13:23:44 +02:00
audio_mixer_t am ;
2004-10-16 14:39:45 +02:00
// conversion for linear volume response:
2004-10-17 09:12:16 +02:00
am . volume_left = am . volume_right = 2 * Volume - Volume * Volume / 255 ;
2002-08-04 14:57:29 +02:00
CHECK ( ioctl ( fd_audio , AUDIO_SET_MIXER , & am ) ) ;
}
}
2004-12-17 14:55:49 +01:00
void cDvbDevice : : SetDigitalAudioDevice ( bool On )
2002-10-12 14:29:46 +02:00
{
2004-12-17 14:55:49 +01:00
if ( digitalAudio ! = On ) {
if ( digitalAudio )
cCondWait : : SleepMs ( 1000 ) ; // Wait until any leftover digital data has been flushed
digitalAudio = On ;
2005-02-06 12:31:36 +01:00
SetVolumeDevice ( On | | IsMute ( ) ? 0 : CurrentVolume ( ) ) ;
2002-10-12 14:29:46 +02:00
}
}
2005-02-13 14:26:37 +01:00
void cDvbDevice : : SetTransferModeForDolbyDigital ( bool On )
{
setTransferModeForDolbyDigital = On ;
}
2004-12-17 14:55:49 +01:00
void cDvbDevice : : SetAudioTrackDevice ( eTrackType Type )
2002-10-12 14:29:46 +02:00
{
2004-12-17 14:55:49 +01:00
const tTrackId * TrackId = GetTrack ( Type ) ;
if ( TrackId & & TrackId - > id ) {
2005-11-11 16:13:19 +01:00
SetAudioBypass ( false ) ;
if ( IS_AUDIO_TRACK ( Type ) | | ( IS_DOLBY_TRACK ( Type ) & & SetAudioBypass ( true ) ) ) {
2005-02-19 12:18:18 +01:00
if ( pidHandles [ ptAudio ] . pid & & pidHandles [ ptAudio ] . pid ! = TrackId - > id ) {
2005-06-05 13:37:37 +02:00
DetachAll ( pidHandles [ ptAudio ] . pid ) ;
2005-02-08 11:27:42 +01:00
pidHandles [ ptAudio ] . pid = TrackId - > id ;
SetPid ( & pidHandles [ ptAudio ] , ptAudio , true ) ;
}
2004-12-17 14:55:49 +01:00
}
else if ( IS_DOLBY_TRACK ( Type ) ) {
2005-02-13 14:26:37 +01:00
if ( ! setTransferModeForDolbyDigital )
return ;
2004-12-17 14:55:49 +01:00
// Currently this works only in Transfer Mode
cChannel * Channel = Channels . GetByNumber ( CurrentChannel ( ) ) ;
2005-01-02 15:11:44 +01:00
if ( Channel )
SetChannelDevice ( Channel , false ) ; // this implicitly starts Transfer Mode
2004-12-17 14:55:49 +01:00
}
2002-10-12 14:29:46 +02:00
}
}
2002-10-26 11:51:37 +02:00
bool cDvbDevice : : CanReplay ( void ) const
{
2002-11-16 12:36:50 +01:00
# ifndef DO_REC_AND_PLAY_ON_PRIMARY_DEVICE
if ( Receiving ( ) )
return false ;
# endif
2005-03-05 16:13:26 +01:00
return cDevice : : CanReplay ( ) ;
2002-10-26 11:51:37 +02:00
}
2002-08-15 11:16:34 +02:00
bool cDvbDevice : : SetPlayMode ( ePlayMode PlayMode )
2002-08-04 14:57:29 +02:00
{
2002-08-15 11:16:34 +02:00
if ( PlayMode ! = pmExtern_THIS_SHOULD_BE_AVOIDED & & fd_video < 0 & & fd_audio < 0 ) {
// reopen the devices
fd_video = DvbOpen ( DEV_DVB_VIDEO , CardIndex ( ) , O_RDWR | O_NONBLOCK ) ;
fd_audio = DvbOpen ( DEV_DVB_AUDIO , CardIndex ( ) , O_RDWR | O_NONBLOCK ) ;
SetVideoFormat ( Setup . VideoFormat ) ;
2002-08-04 14:57:29 +02:00
}
2002-08-15 11:16:34 +02:00
switch ( PlayMode ) {
case pmNone :
2002-08-16 09:27:53 +02:00
// special handling to return from PCM replay:
CHECK ( ioctl ( fd_video , VIDEO_SET_BLANK , true ) ) ;
CHECK ( ioctl ( fd_video , VIDEO_SELECT_SOURCE , VIDEO_SOURCE_MEMORY ) ) ;
CHECK ( ioctl ( fd_video , VIDEO_PLAY ) ) ;
2002-08-15 11:16:34 +02:00
CHECK ( ioctl ( fd_video , VIDEO_STOP , true ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_STOP , true ) ) ;
CHECK ( ioctl ( fd_video , VIDEO_CLEAR_BUFFER ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_CLEAR_BUFFER ) ) ;
CHECK ( ioctl ( fd_video , VIDEO_SELECT_SOURCE , VIDEO_SOURCE_DEMUX ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_SELECT_SOURCE , AUDIO_SOURCE_DEMUX ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_SET_AV_SYNC , true ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_SET_MUTE , false ) ) ;
break ;
case pmAudioVideo :
2004-11-06 13:17:35 +01:00
case pmAudioOnlyBlack :
2003-05-02 14:30:03 +02:00
if ( playMode = = pmNone )
2004-11-07 10:31:59 +01:00
TurnOffLiveMode ( true ) ;
2002-08-15 11:16:34 +02:00
CHECK ( ioctl ( fd_video , VIDEO_SET_BLANK , true ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_SELECT_SOURCE , AUDIO_SOURCE_MEMORY ) ) ;
2002-09-08 15:04:33 +02:00
CHECK ( ioctl ( fd_audio , AUDIO_SET_AV_SYNC , PlayMode = = pmAudioVideo ) ) ;
2002-08-15 11:16:34 +02:00
CHECK ( ioctl ( fd_audio , AUDIO_PLAY ) ) ;
CHECK ( ioctl ( fd_video , VIDEO_SELECT_SOURCE , VIDEO_SOURCE_MEMORY ) ) ;
CHECK ( ioctl ( fd_video , VIDEO_PLAY ) ) ;
break ;
case pmAudioOnly :
CHECK ( ioctl ( fd_video , VIDEO_SET_BLANK , true ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_STOP , true ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_CLEAR_BUFFER ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_SELECT_SOURCE , AUDIO_SOURCE_MEMORY ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_SET_AV_SYNC , false ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_PLAY ) ) ;
CHECK ( ioctl ( fd_video , VIDEO_SET_BLANK , false ) ) ;
break ;
2004-05-23 10:12:44 +02:00
case pmVideoOnly :
CHECK ( ioctl ( fd_video , VIDEO_SET_BLANK , true ) ) ;
CHECK ( ioctl ( fd_video , VIDEO_STOP , true ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_SELECT_SOURCE , AUDIO_SOURCE_DEMUX ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_SET_AV_SYNC , false ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_PLAY ) ) ;
CHECK ( ioctl ( fd_video , VIDEO_CLEAR_BUFFER ) ) ;
CHECK ( ioctl ( fd_video , VIDEO_SELECT_SOURCE , VIDEO_SOURCE_MEMORY ) ) ;
CHECK ( ioctl ( fd_video , VIDEO_PLAY ) ) ;
break ;
2002-08-15 11:16:34 +02:00
case pmExtern_THIS_SHOULD_BE_AVOIDED :
close ( fd_video ) ;
close ( fd_audio ) ;
fd_video = fd_audio = - 1 ;
break ;
}
playMode = PlayMode ;
return true ;
2002-08-04 14:57:29 +02:00
}
2003-11-07 14:16:25 +01:00
int64_t cDvbDevice : : GetSTC ( void )
{
if ( fd_stc > = 0 ) {
struct dmx_stc stc ;
stc . num = 0 ;
if ( ioctl ( fd_stc , DMX_GET_STC , & stc ) = = - 1 ) {
esyslog ( " ERROR: stc %d: %m " , CardIndex ( ) + 1 ) ;
return - 1 ;
}
return stc . stc / stc . base ;
}
return - 1 ;
}
2002-08-04 14:57:29 +02:00
void cDvbDevice : : TrickSpeed ( int Speed )
{
if ( fd_video > = 0 )
CHECK ( ioctl ( fd_video , VIDEO_SLOWMOTION , Speed ) ) ;
}
void cDvbDevice : : Clear ( void )
{
if ( fd_video > = 0 )
CHECK ( ioctl ( fd_video , VIDEO_CLEAR_BUFFER ) ) ;
if ( fd_audio > = 0 )
CHECK ( ioctl ( fd_audio , AUDIO_CLEAR_BUFFER ) ) ;
2003-03-30 12:42:23 +02:00
cDevice : : Clear ( ) ;
2002-08-04 14:57:29 +02:00
}
void cDvbDevice : : Play ( void )
{
2002-09-08 15:04:33 +02:00
if ( playMode = = pmAudioOnly | | playMode = = pmAudioOnlyBlack ) {
if ( fd_audio > = 0 )
CHECK ( ioctl ( fd_audio , AUDIO_CONTINUE ) ) ;
}
else {
if ( fd_audio > = 0 )
CHECK ( ioctl ( fd_audio , AUDIO_SET_AV_SYNC , true ) ) ;
if ( fd_video > = 0 )
CHECK ( ioctl ( fd_video , VIDEO_CONTINUE ) ) ;
}
2003-03-30 12:42:23 +02:00
cDevice : : Play ( ) ;
2002-08-04 14:57:29 +02:00
}
void cDvbDevice : : Freeze ( void )
{
2002-09-08 15:04:33 +02:00
if ( playMode = = pmAudioOnly | | playMode = = pmAudioOnlyBlack ) {
if ( fd_audio > = 0 )
CHECK ( ioctl ( fd_audio , AUDIO_PAUSE ) ) ;
}
else {
if ( fd_audio > = 0 )
CHECK ( ioctl ( fd_audio , AUDIO_SET_AV_SYNC , false ) ) ;
if ( fd_video > = 0 )
CHECK ( ioctl ( fd_video , VIDEO_FREEZE ) ) ;
}
2003-03-30 12:42:23 +02:00
cDevice : : Freeze ( ) ;
2002-08-04 14:57:29 +02:00
}
void cDvbDevice : : Mute ( void )
{
if ( fd_audio > = 0 ) {
CHECK ( ioctl ( fd_audio , AUDIO_SET_AV_SYNC , false ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_SET_MUTE , true ) ) ;
}
2003-03-30 12:42:23 +02:00
cDevice : : Mute ( ) ;
2002-08-04 14:57:29 +02:00
}
void cDvbDevice : : StillPicture ( const uchar * Data , int Length )
{
2003-08-30 11:48:45 +02:00
if ( Data [ 0 ] = = 0x00 & & Data [ 1 ] = = 0x00 & & Data [ 2 ] = = 0x01 & & ( Data [ 3 ] & 0xF0 ) = = 0xE0 ) {
// PES data
char * buf = MALLOC ( char , Length ) ;
if ( ! buf )
return ;
int i = 0 ;
int blen = 0 ;
2003-10-17 13:47:03 +02:00
while ( i < Length - 6 ) {
if ( Data [ i ] = = 0x00 & & Data [ i + 1 ] = = 0x00 & & Data [ i + 2 ] = = 0x01 ) {
2003-08-30 11:48:45 +02:00
int len = Data [ i + 4 ] * 256 + Data [ i + 5 ] ;
2003-10-17 13:47:03 +02:00
if ( ( Data [ i + 3 ] & 0xF0 ) = = 0xE0 ) { // video packet
// skip PES header
int offs = i + 6 ;
// skip header extension
if ( ( Data [ i + 6 ] & 0xC0 ) = = 0x80 ) {
// MPEG-2 PES header
2003-10-19 12:59:16 +02:00
if ( Data [ i + 8 ] > = Length )
break ;
2003-10-17 13:47:03 +02:00
offs + = 3 ;
offs + = Data [ i + 8 ] ;
len - = 3 ;
len - = Data [ i + 8 ] ;
2003-10-24 15:46:33 +02:00
if ( len < 0 | | offs + len > Length )
2003-10-19 12:59:16 +02:00
break ;
2003-10-17 13:47:03 +02:00
}
else {
// MPEG-1 PES header
while ( offs < Length & & len > 0 & & Data [ offs ] = = 0xFF ) {
offs + + ;
len - - ;
}
2003-10-19 12:59:16 +02:00
if ( offs < = Length - 2 & & len > = 2 & & ( Data [ offs ] & 0xC0 ) = = 0x40 ) {
2003-10-17 13:47:03 +02:00
offs + = 2 ;
len - = 2 ;
}
2003-10-19 12:59:16 +02:00
if ( offs < = Length - 5 & & len > = 5 & & ( Data [ offs ] & 0xF0 ) = = 0x20 ) {
2003-10-17 13:47:03 +02:00
offs + = 5 ;
len - = 5 ;
}
2003-10-19 12:59:16 +02:00
else if ( offs < = Length - 10 & & len > = 10 & & ( Data [ offs ] & 0xF0 ) = = 0x30 ) {
2003-10-17 13:47:03 +02:00
offs + = 10 ;
len - = 10 ;
}
2003-10-19 12:59:16 +02:00
else if ( offs < Length & & len > 0 ) {
2003-10-17 13:47:03 +02:00
offs + + ;
len - - ;
}
}
if ( blen + len > Length ) // invalid PES length field
break ;
memcpy ( & buf [ blen ] , & Data [ offs ] , len ) ;
i = offs + len ;
blen + = len ;
2003-08-30 11:48:45 +02:00
}
2003-10-17 13:47:03 +02:00
else if ( Data [ i + 3 ] > = 0xBD & & Data [ i + 3 ] < = 0xDF ) // other PES packets
i + = len + 6 ;
else
i + + ;
2003-08-30 11:48:45 +02:00
}
else
i + + ;
}
video_still_picture sp = { buf , blen } ;
CHECK ( ioctl ( fd_video , VIDEO_STILLPICTURE , & sp ) ) ;
free ( buf ) ;
}
else {
// non-PES data
video_still_picture sp = { ( char * ) Data , Length } ;
CHECK ( ioctl ( fd_video , VIDEO_STILLPICTURE , & sp ) ) ;
}
2002-08-04 14:57:29 +02:00
}
2002-08-16 09:22:29 +02:00
bool cDvbDevice : : Poll ( cPoller & Poller , int TimeoutMs )
2002-08-15 10:13:03 +02:00
{
2002-09-08 15:04:33 +02:00
Poller . Add ( ( playMode = = pmAudioOnly | | playMode = = pmAudioOnlyBlack ) ? fd_audio : fd_video , true ) ;
2002-08-16 09:22:29 +02:00
return Poller . Poll ( TimeoutMs ) ;
2002-08-15 10:13:03 +02:00
}
2004-06-19 08:58:14 +02:00
bool cDvbDevice : : Flush ( int TimeoutMs )
{
//TODO actually this function should wait until all buffered data has been processed by the card, but how?
return true ;
}
2002-08-04 14:57:29 +02:00
int cDvbDevice : : PlayVideo ( const uchar * Data , int Length )
{
2005-01-16 12:02:39 +01:00
return WriteAllOrNothing ( fd_video , Data , Length , 1000 , 10 ) ;
2002-08-04 14:57:29 +02:00
}
2004-12-17 14:55:49 +01:00
int cDvbDevice : : PlayAudio ( const uchar * Data , int Length )
2002-08-04 14:57:29 +02:00
{
2005-01-16 12:02:39 +01:00
return WriteAllOrNothing ( fd_audio , Data , Length , 1000 , 10 ) ;
2002-08-04 14:57:29 +02:00
}
bool cDvbDevice : : OpenDvr ( void )
{
CloseDvr ( ) ;
2002-08-10 14:58:25 +02:00
fd_dvr = DvbOpen ( DEV_DVB_DVR , CardIndex ( ) , O_RDONLY | O_NONBLOCK , true ) ;
2002-09-08 09:03:10 +02:00
if ( fd_dvr > = 0 )
tsBuffer = new cTSBuffer ( fd_dvr , MEGABYTE ( 2 ) , CardIndex ( ) + 1 ) ;
2002-08-04 14:57:29 +02:00
return fd_dvr > = 0 ;
}
void cDvbDevice : : CloseDvr ( void )
{
if ( fd_dvr > = 0 ) {
2002-09-08 09:03:10 +02:00
delete tsBuffer ;
tsBuffer = NULL ;
2004-10-16 09:36:28 +02:00
close ( fd_dvr ) ;
fd_dvr = - 1 ;
2002-08-04 14:57:29 +02:00
}
}
2002-09-08 09:03:10 +02:00
bool cDvbDevice : : GetTSPacket ( uchar * & Data )
2002-08-04 14:57:29 +02:00
{
2002-09-08 09:03:10 +02:00
if ( tsBuffer ) {
2004-10-16 09:36:28 +02:00
Data = tsBuffer - > Get ( ) ;
2002-09-08 09:03:10 +02:00
return true ;
2002-08-04 14:57:29 +02:00
}
2002-09-08 09:03:10 +02:00
return false ;
2002-08-04 14:57:29 +02:00
}