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 .
*
2004-10-16 14:39:45 +02:00
* $ Id : dvbdevice . c 1.96 2004 / 10 / 16 14 : 38 : 24 kls Exp $
2002-08-04 14:57:29 +02:00
*/
# include "dvbdevice.h"
# include <errno.h>
extern " C " {
# ifdef boolean
# define HAVE_BOOLEAN
# endif
# include <jpeglib.h>
# undef boolean
}
# 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
2004-02-24 10:15:46 +01:00
//#define WAIT_FOR_LOCK_AFTER_TUNING 1
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
static const char * DvbName ( const char * Name , int n )
2002-08-04 14:57:29 +02:00
{
static char buffer [ PATH_MAX ] ;
2002-08-10 14:58:25 +02:00
snprintf ( buffer , sizeof ( buffer ) , " %s%d/%s%d " , DEV_DVB_ADAPTER , n , Name , 0 ) ;
2002-08-04 14:57:29 +02:00
return buffer ;
}
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
{
2002-08-10 14:58:25 +02:00
const char * FileName = DvbName ( 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 :
2003-02-16 11:20:55 +01:00
enum eTunerStatus { tsIdle , tsSet , tsTuned , tsLocked , tsCam } ;
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 ;
bool active ;
2003-09-06 13:22:24 +02:00
bool useCa ;
2003-02-16 15:10:39 +01:00
time_t startTime ;
2002-12-08 09:55:26 +01:00
eTunerStatus tunerStatus ;
2004-10-16 09:36:28 +02:00
cCondWait newSet ;
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 ;
2003-09-06 13:22:24 +02:00
void Set ( const cChannel * Channel , bool Tune , bool UseCa ) ;
2004-01-04 12:30:00 +01:00
bool Locked ( void ) { return tunerStatus > = tsLocked ; }
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 ;
active = false ;
2003-09-06 13:22:24 +02:00
useCa = false ;
2002-12-08 09:55:26 +01:00
tunerStatus = tsIdle ;
2003-02-16 15:10:39 +01:00
startTime = time ( NULL ) ;
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 ( )
{
active = false ;
tunerStatus = tsIdle ;
2004-10-16 09:36:28 +02:00
newSet . Signal ( ) ;
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
}
2003-09-06 13:22:24 +02:00
void cDvbTuner : : Set ( const cChannel * Channel , bool Tune , bool UseCa )
2002-12-08 09:55:26 +01:00
{
2004-10-16 09:36:28 +02:00
Lock ( ) ;
2003-01-06 14:44:27 +01:00
if ( Tune )
tunerStatus = tsSet ;
2004-01-04 12:30:00 +01:00
else if ( tunerStatus = = tsCam )
2003-02-16 15:10:39 +01:00
tunerStatus = tsTuned ;
2003-09-06 13:22:24 +02:00
useCa = UseCa ;
2004-01-04 12:30:00 +01:00
if ( Channel - > Ca ( ) & & tunerStatus ! = tsCam )
2003-02-16 15:10:39 +01:00
startTime = time ( NULL ) ;
2003-04-19 14:46:58 +02:00
channel = * Channel ;
2004-10-16 09:36:28 +02:00
Unlock ( ) ;
newSet . Signal ( ) ;
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 ;
}
int volt = ( channel . Polarization ( ) = = ' v ' | | channel . Polarization ( ) = = ' V ' ) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18 ;
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 )
{
active = true ;
while ( active ) {
2004-10-16 09:36:28 +02:00
Lock ( ) ;
2002-12-08 09:55:26 +01:00
if ( tunerStatus = = tsSet )
tunerStatus = SetFrontend ( ) ? tsTuned : tsIdle ;
if ( tunerStatus = = tsTuned ) {
fe_status_t status = fe_status_t ( 0 ) ;
CHECK ( ioctl ( fd_frontend , FE_READ_STATUS , & status ) ) ;
if ( status & FE_HAS_LOCK )
tunerStatus = tsLocked ;
}
2003-02-16 11:20:55 +01:00
if ( tunerStatus ! = tsIdle ) {
dvb_frontend_event event ;
if ( ioctl ( fd_frontend , FE_GET_EVENT , & event ) = = 0 ) {
if ( event . status & FE_REINIT ) {
tunerStatus = tsSet ;
esyslog ( " ERROR: frontend %d was reinitialized - re-tuning " , cardIndex ) ;
}
2002-12-08 09:55:26 +01:00
}
2004-01-04 12:30:00 +01:00
}
if ( ciHandler ) {
if ( ciHandler - > Process ( ) & & useCa ) {
if ( tunerStatus = = tsLocked ) {
for ( int Slot = 0 ; Slot < ciHandler - > NumSlots ( ) ; Slot + + ) {
2004-01-25 14:04:03 +01:00
cCiCaPmt CaPmt ( channel . Source ( ) , channel . Transponder ( ) , channel . Sid ( ) , ciHandler - > GetCaSystemIds ( Slot ) ) ;
2004-01-04 12:30:00 +01:00
if ( CaPmt . Valid ( ) ) {
CaPmt . AddPid ( channel . Vpid ( ) , 2 ) ;
CaPmt . AddPid ( channel . Apid1 ( ) , 4 ) ;
CaPmt . AddPid ( channel . Apid2 ( ) , 4 ) ;
CaPmt . AddPid ( channel . Dpid1 ( ) , 0 ) ;
if ( ciHandler - > SetCaPmt ( CaPmt , Slot ) ) {
tunerStatus = tsCam ;
startTime = 0 ;
2003-04-18 12:48:49 +02:00
}
2004-01-04 12:30:00 +01:00
}
}
2003-02-09 11:54:22 +01:00
}
2003-01-06 14:44:27 +01:00
}
2004-01-04 12:30:00 +01:00
else if ( tunerStatus > tsLocked )
tunerStatus = tsLocked ;
2003-01-06 14:44:27 +01:00
}
2004-10-16 09:36:28 +02:00
Unlock ( ) ;
2003-02-09 11:54:22 +01:00
// in the beginning we loop more often to let the CAM connection start up fast
2004-10-16 09:36:28 +02:00
newSet . Wait ( ( ciHandler & & ( time ( NULL ) - startTime < 20 ) ) ? 100 : 1000 ) ;
2002-12-08 09:55:26 +01:00
}
}
// --- cDvbDevice ------------------------------------------------------------
2003-10-04 12:42:58 +02:00
int cDvbDevice : : devVideoOffset = - 1 ;
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 ;
2002-08-15 11:16:34 +02:00
playMode = pmNone ;
2004-10-16 12:57:51 +02:00
aPid1 = aPid2 = 0 ;
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:
SetVideoFormat ( Setup . VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3 ) ;
// 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 ;
2003-01-06 14:44:27 +01:00
ciHandler = cCiHandler : : CreateCiHandler ( DvbName ( DEV_DVB_CA , n ) ) ;
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 ( ) ) ) {
2002-08-10 14:58:25 +02:00
if ( Probe ( DvbName ( 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 ;
}
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 ;
}
2002-08-04 14:57:29 +02:00
bool cDvbDevice : : GrabImage ( const char * FileName , bool Jpeg , int Quality , int SizeX , int SizeY )
{
2003-10-04 12:42:58 +02:00
if ( devVideoIndex < 0 )
return false ;
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 ) ;
if ( videoDev < 0 )
LOG_ERROR_STR ( buffer ) ;
2002-08-04 14:57:29 +02:00
if ( videoDev > = 0 ) {
int result = 0 ;
struct video_mbuf mbuf ;
result | = ioctl ( videoDev , VIDIOCGMBUF , & mbuf ) ;
if ( result = = 0 ) {
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 ;
result | = ioctl ( videoDev , VIDIOCGCAP , & vc ) ;
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 ;
result | = ioctl ( videoDev , VIDIOCMCAPTURE , & vm ) ;
result | = ioctl ( videoDev , VIDIOCSYNC , & vm . frame ) ;
// 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
2002-08-04 14:57:29 +02:00
if ( Quality < 0 )
Quality = 255 ; //XXX is this 'best'???
2002-09-04 17:26:02 +02:00
2002-08-04 14:57:29 +02:00
isyslog ( " grabbing to %s (%s %d %d %d) " , FileName , Jpeg ? " JPEG " : " PNM " , Quality , vm . width , vm . height ) ;
FILE * f = fopen ( FileName , " wb " ) ;
if ( f ) {
if ( Jpeg ) {
// write JPEG file:
struct jpeg_compress_struct cinfo ;
struct jpeg_error_mgr jerr ;
cinfo . err = jpeg_std_error ( & jerr ) ;
jpeg_create_compress ( & cinfo ) ;
jpeg_stdio_dest ( & cinfo , f ) ;
cinfo . image_width = vm . width ;
cinfo . image_height = vm . height ;
cinfo . input_components = 3 ;
cinfo . in_color_space = JCS_RGB ;
2002-09-04 17:26:02 +02:00
2002-08-04 14:57:29 +02:00
jpeg_set_defaults ( & cinfo ) ;
jpeg_set_quality ( & cinfo , Quality , true ) ;
jpeg_start_compress ( & cinfo , true ) ;
2002-09-04 17:26:02 +02:00
2002-08-04 14:57:29 +02:00
int rs = vm . width * 3 ;
JSAMPROW rp [ vm . height ] ;
for ( int k = 0 ; k < vm . height ; k + + )
rp [ k ] = & mem [ rs * k ] ;
jpeg_write_scanlines ( & cinfo , rp , vm . height ) ;
jpeg_finish_compress ( & cinfo ) ;
jpeg_destroy_compress ( & cinfo ) ;
}
else {
// write PNM file:
if ( fprintf ( f , " P6 \n %d \n %d \n 255 \n " , vm . width , vm . height ) < 0 | |
fwrite ( mem , vm . width * vm . height * 3 , 1 , f ) < 0 ) {
LOG_ERROR_STR ( FileName ) ;
result | = 1 ;
}
}
fclose ( f ) ;
}
else {
LOG_ERROR_STR ( FileName ) ;
result | = 1 ;
}
munmap ( mem , msize ) ;
}
else
result | = 1 ;
}
close ( videoDev ) ;
return result = = 0 ;
}
return false ;
}
void cDvbDevice : : SetVideoFormat ( bool VideoFormat16_9 )
{
if ( HasDecoder ( ) )
CHECK ( ioctl ( fd_video , VIDEO_SET_FORMAT , VideoFormat16_9 ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3 ) ) ;
}
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
}
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 ) ;
2002-08-04 14:57:29 +02:00
if ( Handle - > handle < 0 )
return false ;
}
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 )
{
const char * FileName = DvbName ( DEV_DVB_DEMUX , CardIndex ( ) ) ;
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 ;
}
2003-05-02 14:30:03 +02:00
void cDvbDevice : : TurnOffLiveMode ( void )
{
// 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 ) ) ;
// Turn off live PIDs:
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 ;
return true ;
}
2004-01-04 12:30:00 +01:00
bool cDvbDevice : : ProvidesTransponder ( const cChannel * Channel ) const
{
return ProvidesSource ( Channel - > Source ( ) ) & & ( ( Channel - > Source ( ) & cSource : : st_Mask ) ! = cSource : : stSat | | Diseqcs . Get ( Channel - > Source ( ) , Channel - > Frequency ( ) , Channel - > Polarization ( ) ) ) ;
}
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
2004-02-08 14:08:03 +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-06-12 15:07:29 +02:00
if ( Channel - > Vpid ( ) & & ! HasPid ( Channel - > Vpid ( ) ) | | Channel - > Apid1 ( ) & & ! HasPid ( Channel - > Apid1 ( ) ) ) {
2002-12-13 15:35:00 +01:00
# ifdef DO_MULTIPLE_RECORDINGS
2004-06-19 13:48:25 +02:00
if ( Ca ( ) > CACONFBASE | | Channel - > Ca ( ) > CACONFBASE )
2003-04-19 14:46:58 +02:00
needsDetachReceivers = ! ciHandler // only LL-firmware can do non-live CA channels
| | Ca ( ) ! = Channel - > Ca ( ) ;
2002-09-14 11:51:51 +02:00
else if ( ! IsPrimaryDevice ( ) )
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 )
{
2003-04-19 14:46:58 +02:00
bool IsEncrypted = Channel - > Ca ( ) > CACONFBASE & & ! ciHandler ; // only LL-firmware can do non-live CA channels
2002-10-26 11:08:12 +02:00
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-10-26 11:08:12 +02:00
| | IsEncrypted & & pidHandles [ ptVideo ] . pid ! = Channel - > Vpid ( ) // CA channels can only be decrypted in "live" mode
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
) ;
2002-10-26 11:08:12 +02:00
bool StartTransferMode = IsPrimaryDevice ( ) & & ! IsEncrypted & & ! DoTune
2002-10-06 10:25:42 +02:00
& & ( LiveView & & HasPid ( Channel - > Vpid ( ) ) & & pidHandles [ ptVideo ] . pid ! = Channel - > Vpid ( ) // the PID is already set as DMX_PES_OTHER
| | ! LiveView & & pidHandles [ ptVideo ] . pid = = Channel - > Vpid ( ) // 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
) ;
bool TurnOnLivePIDs = HasDecoder ( ) & & ! StartTransferMode
2002-10-26 11:08:12 +02:00
& & ( IsEncrypted // CA channels can only be decrypted in "live" mode
2002-09-15 10:51:44 +02:00
| | 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
2003-09-06 13:22:24 +02:00
// XXX 1.3: use the same mechanism as below (!EITScanner.UsesDevice(this))
2003-05-24 13:38:28 +02:00
if ( EITScanner . Active ( ) ) {
StartTransferMode = false ;
TurnOnLivePIDs = false ;
}
2002-09-15 10:51:44 +02:00
// Turn off live PIDs if necessary:
2003-05-02 14:30:03 +02:00
if ( TurnOffLivePIDs )
TurnOffLiveMode ( ) ;
2002-08-04 14:57:29 +02:00
2004-01-04 12:30:00 +01:00
dvbTuner - > Set ( Channel , DoTune , ! EITScanner . UsesDevice ( this ) ) ; //XXX 1.3: this is an ugly hack - find a cleaner solution//XXX
2002-08-04 14:57:29 +02:00
2004-02-24 10:15:46 +01:00
# ifdef WAIT_FOR_LOCK_AFTER_TUNING
2004-02-08 11:13:36 +01:00
//XXX TODO preliminary fix for the "Unknown picture type" error
time_t t0 = time ( NULL ) ;
while ( ! dvbTuner - > Locked ( ) & & time ( NULL ) - t0 < 5 )
usleep ( 100 ) ;
2004-02-24 10:15:46 +01:00
# endif
2002-08-04 14:57:29 +02:00
// PID settings:
2002-09-15 10:51:44 +02:00
if ( TurnOnLivePIDs ) {
2002-10-12 14:29:46 +02:00
aPid1 = Channel - > Apid1 ( ) ;
aPid2 = Channel - > Apid2 ( ) ;
2004-01-04 12:30:00 +01:00
if ( ! ( AddPid ( Channel - > Ppid ( ) , ptPcr ) & & AddPid ( Channel - > Apid1 ( ) , ptAudio ) & & AddPid ( Channel - > Vpid ( ) , ptVideo ) ) ) { //XXX+ dolby dpid1!!! (if audio plugins are attached)
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
}
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 )
2002-10-26 10:12:34 +02:00
cControl : : Launch ( new cTransferControl ( this , Channel - > Vpid ( ) , Channel - > Apid1 ( ) , Channel - > Apid2 ( ) , Channel - > Dpid1 ( ) , Channel - > Dpid2 ( ) ) ) ;
2002-08-04 14:57:29 +02:00
return true ;
}
2004-01-04 12:30:00 +01:00
bool cDvbDevice : : HasLock ( void )
{
2004-01-05 12:29:26 +01:00
return dvbTuner ? dvbTuner - > Locked ( ) : false ;
2004-01-04 12:30:00 +01:00
}
2002-08-04 14:57:29 +02:00
void cDvbDevice : : SetVolumeDevice ( int Volume )
{
if ( HasDecoder ( ) ) {
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:
int i , Vol ;
for ( i = 1 , Vol = 0 ; i < Volume ; i + + )
Vol + = ( 256 - i ) * ( 256 - i ) ;
am . volume_left = am . volume_right = Vol / 21718 ;
2002-08-04 14:57:29 +02:00
CHECK ( ioctl ( fd_audio , AUDIO_SET_MIXER , & am ) ) ;
}
}
2002-10-12 14:29:46 +02:00
int cDvbDevice : : NumAudioTracksDevice ( void ) const
{
int n = 0 ;
if ( aPid1 )
n + + ;
2003-04-19 14:46:58 +02:00
if ( Ca ( ) < = MAXDEVICES & & aPid2 & & aPid1 ! = aPid2 ) // a CA recording session blocks switching live audio tracks
2002-10-12 14:29:46 +02:00
n + + ;
return n ;
}
const char * * cDvbDevice : : GetAudioTracksDevice ( int * CurrentTrack ) const
{
2002-11-01 11:26:28 +01:00
if ( NumAudioTracksDevice ( ) ) {
2002-10-12 14:29:46 +02:00
if ( CurrentTrack )
* CurrentTrack = ( pidHandles [ ptAudio ] . pid = = aPid1 ) ? 0 : 1 ;
static const char * audioTracks1 [ ] = { " Audio 1 " , NULL } ;
static const char * audioTracks2 [ ] = { " Audio 1 " , " Audio 2 " , NULL } ;
2002-11-01 11:26:28 +01:00
return NumAudioTracksDevice ( ) > 1 ? audioTracks2 : audioTracks1 ;
2002-10-12 14:29:46 +02:00
}
return NULL ;
}
void cDvbDevice : : SetAudioTrackDevice ( int Index )
{
2002-11-01 11:26:28 +01:00
if ( 0 < = Index & & Index < NumAudioTracksDevice ( ) ) {
2002-12-14 10:59:34 +01:00
int Pid = Index ? aPid2 : aPid1 ;
pidHandles [ ptAudio ] . pid = Pid ;
SetPid ( & pidHandles [ ptAudio ] , ptAudio , true ) ;
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
2003-04-19 14:46:58 +02:00
return cDevice : : CanReplay ( ) & & ( Ca ( ) < = MAXDEVICES | | ciHandler ) ; // with non-LL-firmware we can only replay if there is no CA recording going on
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 :
2003-05-02 14:30:03 +02:00
if ( playMode = = pmNone )
TurnOffLiveMode ( ) ;
// continue with next...
2002-09-08 15:04:33 +02:00
case pmAudioOnlyBlack :
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 )
{
/* Using the VIDEO_STILLPICTURE ioctl call would be the
correct way to display a still frame , but unfortunately this
doesn ' t work with frames from VDR . So let ' s do pretty much the
same here as in DVB / driver / dvb . c ' s play_iframe ( ) - I have absolutely
no idea why it works this way , but doesn ' t work with VIDEO_STILLPICTURE .
If anybody ever finds out what could be changed so that VIDEO_STILLPICTURE
could be used , please let me know !
kls 2002 - 03 - 23
2003-08-30 11:48:45 +02:00
2003 - 08 - 30 : apparently the driver can ' t handle PES data , so Oliver Endriss
< o . endriss @ gmx . de > has changed this to strip all PES headers
and send pure ES data to the driver . Seems to work just fine !
Let ' s drop the VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES stuff
once this has proven to work in all cases .
2002-08-04 14:57:29 +02:00
*/
2003-08-30 11:48:45 +02:00
# define VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
2002-08-04 14:57:29 +02:00
# ifdef VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
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
# else
# define MIN_IFRAME 400000
for ( int i = MIN_IFRAME / Length + 1 ; i > 0 ; i - - ) {
safe_write ( fd_video , Data , Length ) ;
usleep ( 1 ) ; // allows the buffer to be displayed in case the progress display is active
}
# endif
}
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 )
{
2002-09-08 15:04:33 +02:00
int fd = ( playMode = = pmAudioOnly | | playMode = = pmAudioOnlyBlack ) ? fd_audio : fd_video ;
2002-08-15 11:16:34 +02:00
if ( fd > = 0 )
return write ( fd , Data , Length ) ;
2002-08-04 14:57:29 +02:00
return - 1 ;
}
2002-11-03 11:53:58 +01:00
void cDvbDevice : : PlayAudio ( const uchar * Data , int Length )
2002-08-04 14:57:29 +02:00
{
2002-11-03 11:53:58 +01:00
//XXX actually this function will only be needed to implement replaying AC3 over the DVB card's S/PDIF
cDevice : : PlayAudio ( Data , Length ) ;
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
}