2009-12-31 15:38:18 +01:00
/*
* dvbsdffdevice . h : The DVB SD Full Featured device interface
*
* See the README file for copyright information and how to reach the author .
*
2010-09-12 11:29:00 +02:00
* $ Id : dvbsdffdevice . c 2.27 2010 / 09 / 12 11 : 29 : 00 kls Exp $
2009-12-31 15:38:18 +01:00
*/
# include "dvbsdffdevice.h"
# include <errno.h>
# include <limits.h>
# include <linux/videodev2.h>
# include <linux/dvb/audio.h>
# include <linux/dvb/dmx.h>
# include <linux/dvb/video.h>
# include <sys/ioctl.h>
# include <sys/mman.h>
2010-09-12 11:29:00 +02:00
# include <vdr/eitscan.h>
# include <vdr/transfer.h>
2009-12-31 15:38:18 +01:00
# include "dvbsdffosd.h"
// --- cDvbSdFfDevice --------------------------------------------------------
int cDvbSdFfDevice : : devVideoOffset = - 1 ;
2010-01-04 14:16:11 +01:00
cDvbSdFfDevice : : cDvbSdFfDevice ( int Adapter , int Frontend )
: cDvbDevice ( Adapter , Frontend )
2009-12-31 15:38:18 +01:00
{
spuDecoder = NULL ;
digitalAudio = false ;
playMode = pmNone ;
// Devices that are only present on cards with decoders:
2010-01-04 14:16:11 +01:00
fd_osd = DvbOpen ( DEV_DVB_OSD , adapter , frontend , O_RDWR ) ;
fd_video = DvbOpen ( DEV_DVB_VIDEO , adapter , frontend , O_RDWR | O_NONBLOCK ) ;
fd_audio = DvbOpen ( DEV_DVB_AUDIO , adapter , frontend , O_RDWR | O_NONBLOCK ) ;
fd_stc = DvbOpen ( DEV_DVB_DEMUX , adapter , frontend , O_RDWR ) ;
2009-12-31 15:38:18 +01: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 ? devVideoOffset + + : - 1 ;
// Video format:
SetVideoFormat ( Setup . VideoFormat ) ;
}
cDvbSdFfDevice : : ~ cDvbSdFfDevice ( )
{
delete spuDecoder ;
// We're not explicitly closing any device files here, since this sometimes
// caused segfaults. Besides, the program is about to terminate anyway...
}
void cDvbSdFfDevice : : MakePrimaryDevice ( bool On )
{
if ( On )
new cDvbOsdProvider ( fd_osd ) ;
2010-01-01 15:15:36 +01:00
cDvbDevice : : MakePrimaryDevice ( On ) ;
2009-12-31 15:38:18 +01:00
}
bool cDvbSdFfDevice : : HasDecoder ( void ) const
{
return true ;
}
cSpuDecoder * cDvbSdFfDevice : : GetSpuDecoder ( void )
{
if ( ! spuDecoder & & IsPrimaryDevice ( ) )
spuDecoder = new cDvbSpuDecoder ( ) ;
return spuDecoder ;
}
uchar * cDvbSdFfDevice : : GrabImage ( int & Size , bool Jpeg , int Quality , int SizeX , int SizeY )
{
if ( devVideoIndex < 0 )
return NULL ;
char buffer [ PATH_MAX ] ;
snprintf ( buffer , sizeof ( buffer ) , " %s%d " , DEV_VIDEO , devVideoIndex ) ;
int videoDev = open ( buffer , O_RDWR ) ;
if ( videoDev > = 0 ) {
uchar * result = NULL ;
// set up the size and RGB
v4l2_format fmt ;
memset ( & fmt , 0 , sizeof ( fmt ) ) ;
fmt . type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
fmt . fmt . pix . width = SizeX ;
fmt . fmt . pix . height = SizeY ;
fmt . fmt . pix . pixelformat = V4L2_PIX_FMT_BGR24 ;
fmt . fmt . pix . field = V4L2_FIELD_ANY ;
if ( ioctl ( videoDev , VIDIOC_S_FMT , & fmt ) = = 0 ) {
v4l2_requestbuffers reqBuf ;
memset ( & reqBuf , 0 , sizeof ( reqBuf ) ) ;
reqBuf . count = 2 ;
reqBuf . type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
reqBuf . memory = V4L2_MEMORY_MMAP ;
if ( ioctl ( videoDev , VIDIOC_REQBUFS , & reqBuf ) > = 0 ) {
v4l2_buffer mbuf ;
memset ( & mbuf , 0 , sizeof ( mbuf ) ) ;
mbuf . type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
mbuf . memory = V4L2_MEMORY_MMAP ;
if ( ioctl ( videoDev , VIDIOC_QUERYBUF , & mbuf ) = = 0 ) {
int msize = mbuf . length ;
unsigned char * mem = ( unsigned char * ) mmap ( 0 , msize , PROT_READ | PROT_WRITE , MAP_SHARED , videoDev , 0 ) ;
if ( mem & & mem ! = ( unsigned char * ) - 1 ) {
v4l2_buffer buf ;
memset ( & buf , 0 , sizeof ( buf ) ) ;
buf . type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
buf . memory = V4L2_MEMORY_MMAP ;
buf . index = 0 ;
if ( ioctl ( videoDev , VIDIOC_QBUF , & buf ) = = 0 ) {
v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
if ( ioctl ( videoDev , VIDIOC_STREAMON , & type ) = = 0 ) {
memset ( & buf , 0 , sizeof ( buf ) ) ;
buf . type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
buf . memory = V4L2_MEMORY_MMAP ;
buf . index = 0 ;
if ( ioctl ( videoDev , VIDIOC_DQBUF , & buf ) = = 0 ) {
if ( ioctl ( videoDev , VIDIOC_STREAMOFF , & type ) = = 0 ) {
// make RGB out of BGR:
int memsize = fmt . fmt . pix . width * fmt . fmt . pix . 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 ;
}
if ( Quality < 0 )
Quality = 100 ;
dsyslog ( " grabbing to %s %d %d %d " , Jpeg ? " JPEG " : " PNM " , Quality , fmt . fmt . pix . width , fmt . fmt . pix . height ) ;
if ( Jpeg ) {
// convert to JPEG:
result = RgbToJpeg ( mem , fmt . fmt . pix . width , fmt . fmt . pix . height , Size , Quality ) ;
if ( ! result )
esyslog ( " ERROR: failed to convert image to JPEG " ) ;
}
else {
// convert to PNM:
char buf [ 32 ] ;
snprintf ( buf , sizeof ( buf ) , " P6 \n %d \n %d \n 255 \n " , fmt . fmt . pix . width , fmt . fmt . pix . 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 " ) ;
}
}
else
esyslog ( " ERROR: video device VIDIOC_STREAMOFF failed " ) ;
}
else
esyslog ( " ERROR: video device VIDIOC_DQBUF failed " ) ;
}
else
esyslog ( " ERROR: video device VIDIOC_STREAMON failed " ) ;
}
else
esyslog ( " ERROR: video device VIDIOC_QBUF failed " ) ;
munmap ( mem , msize ) ;
}
else
esyslog ( " ERROR: failed to memmap video device " ) ;
}
else
esyslog ( " ERROR: video device VIDIOC_QUERYBUF failed " ) ;
}
else
esyslog ( " ERROR: video device VIDIOC_REQBUFS failed " ) ;
}
else
esyslog ( " ERROR: video device VIDIOC_S_FMT failed " ) ;
close ( videoDev ) ;
return result ;
}
else
LOG_ERROR_STR ( buffer ) ;
return NULL ;
}
void cDvbSdFfDevice : : SetVideoDisplayFormat ( eVideoDisplayFormat VideoDisplayFormat )
{
cDevice : : SetVideoDisplayFormat ( VideoDisplayFormat ) ;
if ( Setup . VideoFormat ) {
CHECK ( ioctl ( fd_video , VIDEO_SET_DISPLAY_FORMAT , VIDEO_LETTER_BOX ) ) ;
}
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 ;
default : esyslog ( " ERROR: unknown video display format %d " , VideoDisplayFormat ) ;
}
}
}
void cDvbSdFfDevice : : SetVideoFormat ( bool VideoFormat16_9 )
{
CHECK ( ioctl ( fd_video , VIDEO_SET_FORMAT , VideoFormat16_9 ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3 ) ) ;
SetVideoDisplayFormat ( eVideoDisplayFormat ( Setup . VideoDisplayFormat ) ) ;
}
eVideoSystem cDvbSdFfDevice : : GetVideoSystem ( void )
{
eVideoSystem VideoSystem = vsPAL ;
if ( fd_video > = 0 ) {
video_size_t vs ;
if ( ioctl ( fd_video , VIDEO_GET_SIZE , & vs ) = = 0 ) {
if ( vs . h = = 480 | | vs . h = = 240 )
VideoSystem = vsNTSC ;
}
else
LOG_ERROR ;
}
return VideoSystem ;
}
void cDvbSdFfDevice : : GetVideoSize ( int & Width , int & Height , double & VideoAspect )
{
if ( fd_video > = 0 ) {
video_size_t vs ;
if ( ioctl ( fd_video , VIDEO_GET_SIZE , & vs ) = = 0 ) {
Width = vs . w ;
Height = vs . h ;
switch ( vs . aspect_ratio ) {
default :
case VIDEO_FORMAT_4_3 : VideoAspect = 4.0 / 3.0 ; break ;
case VIDEO_FORMAT_16_9 : VideoAspect = 16.0 / 9.0 ; break ;
case VIDEO_FORMAT_221_1 : VideoAspect = 2.21 ; break ;
}
return ;
}
else
LOG_ERROR ;
}
cDevice : : GetVideoSize ( Width , Height , VideoAspect ) ;
}
void cDvbSdFfDevice : : GetOsdSize ( int & Width , int & Height , double & PixelAspect )
{
if ( fd_video > = 0 ) {
video_size_t vs ;
if ( ioctl ( fd_video , VIDEO_GET_SIZE , & vs ) = = 0 ) {
Width = 720 ;
if ( vs . h ! = 480 & & vs . h ! = 240 )
Height = 576 ; // PAL
else
Height = 480 ; // NTSC
switch ( Setup . VideoFormat ? vs . aspect_ratio : VIDEO_FORMAT_4_3 ) {
default :
case VIDEO_FORMAT_4_3 : PixelAspect = 4.0 / 3.0 ; break ;
case VIDEO_FORMAT_221_1 : // FF DVB cards only distinguish between 4:3 and 16:9
case VIDEO_FORMAT_16_9 : PixelAspect = 16.0 / 9.0 ; break ;
}
PixelAspect / = double ( Width ) / Height ;
return ;
}
else
LOG_ERROR ;
}
cDevice : : GetOsdSize ( Width , Height , PixelAspect ) ;
}
bool cDvbSdFfDevice : : SetAudioBypass ( bool On )
{
if ( setTransferModeForDolbyDigital ! = 1 )
return false ;
return ioctl ( fd_audio , AUDIO_SET_BYPASS_MODE , On ) = = 0 ;
}
// ptAudio ptVideo ptPcr ptTeletext ptDolby ptOther
static dmx_pes_type_t PesTypes [ ] = { DMX_PES_AUDIO , DMX_PES_VIDEO , DMX_PES_PCR , DMX_PES_TELETEXT , DMX_PES_OTHER , DMX_PES_OTHER } ;
bool cDvbSdFfDevice : : SetPid ( cPidHandle * Handle , int Type , bool On )
{
if ( Handle - > pid ) {
dmx_pes_filter_params pesFilterParams ;
memset ( & pesFilterParams , 0 , sizeof ( pesFilterParams ) ) ;
if ( On ) {
if ( Handle - > handle < 0 ) {
2010-01-04 14:16:11 +01:00
Handle - > handle = DvbOpen ( DEV_DVB_DEMUX , adapter , frontend , O_RDWR | O_NONBLOCK , true ) ;
2009-12-31 15:38:18 +01:00
if ( Handle - > handle < 0 ) {
LOG_ERROR ;
return false ;
}
}
pesFilterParams . pid = Handle - > pid ;
pesFilterParams . input = DMX_IN_FRONTEND ;
pesFilterParams . output = ( Type < = ptTeletext & & Handle - > used < = 1 ) ? DMX_OUT_DECODER : DMX_OUT_TS_TAP ;
pesFilterParams . pes_type = PesTypes [ Type < ptOther ? Type : ptOther ] ;
pesFilterParams . flags = DMX_IMMEDIATE_START ;
if ( ioctl ( Handle - > handle , DMX_SET_PES_FILTER , & pesFilterParams ) < 0 ) {
LOG_ERROR ;
return false ;
}
}
else if ( ! Handle - > used ) {
CHECK ( ioctl ( Handle - > handle , DMX_STOP ) ) ;
if ( Type < = ptTeletext ) {
pesFilterParams . pid = 0x1FFF ;
pesFilterParams . input = DMX_IN_FRONTEND ;
pesFilterParams . output = DMX_OUT_DECODER ;
pesFilterParams . pes_type = PesTypes [ Type ] ;
pesFilterParams . flags = DMX_IMMEDIATE_START ;
CHECK ( ioctl ( Handle - > handle , DMX_SET_PES_FILTER , & pesFilterParams ) ) ;
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
}
close ( Handle - > handle ) ;
Handle - > handle = - 1 ;
}
}
return true ;
}
void cDvbSdFfDevice : : TurnOffLiveMode ( bool LiveView )
{
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 ) ) ;
}
// Turn off live PIDs:
DetachAll ( pidHandles [ ptAudio ] . pid ) ;
DetachAll ( pidHandles [ ptVideo ] . pid ) ;
DetachAll ( pidHandles [ ptPcr ] . pid ) ;
DetachAll ( pidHandles [ ptTeletext ] . pid ) ;
DelPid ( pidHandles [ ptAudio ] . pid ) ;
DelPid ( pidHandles [ ptVideo ] . pid ) ;
DelPid ( pidHandles [ ptPcr ] . pid , ptPcr ) ;
DelPid ( pidHandles [ ptTeletext ] . pid ) ;
DelPid ( pidHandles [ ptDolby ] . pid ) ;
}
bool cDvbSdFfDevice : : SetChannelDevice ( const cChannel * Channel , bool LiveView )
{
int apid = Channel - > Apid ( 0 ) ;
int vpid = Channel - > Vpid ( ) ;
int dpid = Channel - > Dpid ( 0 ) ;
bool DoTune = ! IsTunedToTransponder ( Channel ) ;
bool pidHandlesVideo = pidHandles [ ptVideo ] . pid = = vpid ;
bool pidHandlesAudio = pidHandles [ ptAudio ] . pid = = apid ;
bool TurnOffLivePIDs = DoTune
| | ! IsPrimaryDevice ( )
| | LiveView // for a new live view the old PIDs need to be turned off
| | pidHandlesVideo // for recording the PIDs must be shifted from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
;
bool StartTransferMode = IsPrimaryDevice ( ) & & ! DoTune
& & ( LiveView & & HasPid ( vpid ? vpid : apid ) & & ( ! pidHandlesVideo | | ( ! pidHandlesAudio & & ( dpid ? pidHandles [ ptAudio ] . pid ! = dpid : true ) ) ) // the PID is already set as DMX_PES_OTHER
| | ! LiveView & & ( pidHandlesVideo | | pidHandlesAudio ) // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
) ;
if ( CamSlot ( ) & & ! ChannelCamRelations . CamDecrypt ( Channel - > GetChannelID ( ) , CamSlot ( ) - > SlotNumber ( ) ) )
StartTransferMode | = LiveView & & IsPrimaryDevice ( ) & & Channel - > Ca ( ) > = CA_ENCRYPTED_MIN ;
bool TurnOnLivePIDs = ! StartTransferMode & & LiveView ;
// Turn off live PIDs if necessary:
if ( TurnOffLivePIDs )
TurnOffLiveMode ( LiveView ) ;
// Set the tuner:
if ( ! cDvbDevice : : SetChannelDevice ( Channel , LiveView ) )
return false ;
// 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):
if ( EITScanner . UsesDevice ( this ) ) //XXX
return true ;
// PID settings:
if ( TurnOnLivePIDs ) {
SetAudioBypass ( false ) ;
if ( ! ( AddPid ( Channel - > Ppid ( ) , ptPcr ) & & AddPid ( vpid , ptVideo ) & & AddPid ( apid , ptAudio ) ) ) {
esyslog ( " ERROR: failed to set PIDs for channel %d on device %d " , Channel - > Number ( ) , CardIndex ( ) + 1 ) ;
return false ;
}
if ( IsPrimaryDevice ( ) )
AddPid ( Channel - > Tpid ( ) , ptTeletext ) ;
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
CHECK ( ioctl ( fd_audio , AUDIO_SET_AV_SYNC , true ) ) ;
}
else if ( StartTransferMode )
2010-01-30 11:10:25 +01:00
cControl : : Launch ( new cTransferControl ( this , Channel ) ) ;
2009-12-31 15:38:18 +01:00
return true ;
}
int cDvbSdFfDevice : : GetAudioChannelDevice ( void )
{
audio_status_t as ;
CHECK ( ioctl ( fd_audio , AUDIO_GET_STATUS , & as ) ) ;
return as . channel_select ;
}
void cDvbSdFfDevice : : SetAudioChannelDevice ( int AudioChannel )
{
CHECK ( ioctl ( fd_audio , AUDIO_CHANNEL_SELECT , AudioChannel ) ) ;
}
void cDvbSdFfDevice : : SetVolumeDevice ( int Volume )
{
if ( digitalAudio )
Volume = 0 ;
audio_mixer_t am ;
// conversion for linear volume response:
am . volume_left = am . volume_right = 2 * Volume - Volume * Volume / 255 ;
CHECK ( ioctl ( fd_audio , AUDIO_SET_MIXER , & am ) ) ;
}
void cDvbSdFfDevice : : SetDigitalAudioDevice ( bool On )
{
if ( digitalAudio ! = On ) {
if ( digitalAudio )
cCondWait : : SleepMs ( 1000 ) ; // Wait until any leftover digital data has been flushed
digitalAudio = On ;
SetVolumeDevice ( On | | IsMute ( ) ? 0 : CurrentVolume ( ) ) ;
}
}
void cDvbSdFfDevice : : SetAudioTrackDevice ( eTrackType Type )
{
const tTrackId * TrackId = GetTrack ( Type ) ;
if ( TrackId & & TrackId - > id ) {
SetAudioBypass ( false ) ;
if ( IS_AUDIO_TRACK ( Type ) | | ( IS_DOLBY_TRACK ( Type ) & & SetAudioBypass ( true ) ) ) {
if ( pidHandles [ ptAudio ] . pid & & pidHandles [ ptAudio ] . pid ! = TrackId - > id ) {
DetachAll ( pidHandles [ ptAudio ] . pid ) ;
if ( CamSlot ( ) )
CamSlot ( ) - > SetPid ( pidHandles [ ptAudio ] . pid , false ) ;
pidHandles [ ptAudio ] . pid = TrackId - > id ;
SetPid ( & pidHandles [ ptAudio ] , ptAudio , true ) ;
if ( CamSlot ( ) ) {
CamSlot ( ) - > SetPid ( pidHandles [ ptAudio ] . pid , true ) ;
CamSlot ( ) - > StartDecrypting ( ) ;
}
}
}
else if ( IS_DOLBY_TRACK ( Type ) ) {
if ( setTransferModeForDolbyDigital = = 0 )
return ;
// Currently this works only in Transfer Mode
ForceTransferMode ( ) ;
}
}
}
bool cDvbSdFfDevice : : CanReplay ( void ) const
{
return cDevice : : CanReplay ( ) ;
}
bool cDvbSdFfDevice : : SetPlayMode ( ePlayMode PlayMode )
{
if ( PlayMode ! = pmExtern_THIS_SHOULD_BE_AVOIDED & & fd_video < 0 & & fd_audio < 0 ) {
// reopen the devices
2010-01-04 14:16:11 +01:00
fd_video = DvbOpen ( DEV_DVB_VIDEO , adapter , frontend , O_RDWR | O_NONBLOCK ) ;
fd_audio = DvbOpen ( DEV_DVB_AUDIO , adapter , frontend , O_RDWR | O_NONBLOCK ) ;
2009-12-31 15:38:18 +01:00
SetVideoFormat ( Setup . VideoFormat ) ;
}
switch ( PlayMode ) {
case pmNone :
// 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 ) ) ;
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 :
case pmAudioOnlyBlack :
if ( playMode = = pmNone )
TurnOffLiveMode ( true ) ;
CHECK ( ioctl ( fd_video , VIDEO_SET_BLANK , true ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_SELECT_SOURCE , AUDIO_SOURCE_MEMORY ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_SET_AV_SYNC , PlayMode = = pmAudioVideo ) ) ;
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 ;
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 ;
case pmExtern_THIS_SHOULD_BE_AVOIDED :
close ( fd_video ) ;
close ( fd_audio ) ;
fd_video = fd_audio = - 1 ;
break ;
default : esyslog ( " ERROR: unknown playmode %d " , PlayMode ) ;
}
playMode = PlayMode ;
return true ;
}
int64_t cDvbSdFfDevice : : 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 ;
}
void cDvbSdFfDevice : : TrickSpeed ( int Speed )
{
if ( fd_video > = 0 )
CHECK ( ioctl ( fd_video , VIDEO_SLOWMOTION , Speed ) ) ;
}
void cDvbSdFfDevice : : Clear ( void )
{
if ( fd_video > = 0 )
CHECK ( ioctl ( fd_video , VIDEO_CLEAR_BUFFER ) ) ;
if ( fd_audio > = 0 )
CHECK ( ioctl ( fd_audio , AUDIO_CLEAR_BUFFER ) ) ;
cDevice : : Clear ( ) ;
}
void cDvbSdFfDevice : : Play ( void )
{
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 ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_CONTINUE ) ) ;
}
if ( fd_video > = 0 )
CHECK ( ioctl ( fd_video , VIDEO_CONTINUE ) ) ;
}
cDevice : : Play ( ) ;
}
void cDvbSdFfDevice : : Freeze ( void )
{
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 ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_PAUSE ) ) ;
}
if ( fd_video > = 0 )
CHECK ( ioctl ( fd_video , VIDEO_FREEZE ) ) ;
}
cDevice : : Freeze ( ) ;
}
void cDvbSdFfDevice : : Mute ( void )
{
if ( fd_audio > = 0 ) {
CHECK ( ioctl ( fd_audio , AUDIO_SET_AV_SYNC , false ) ) ;
CHECK ( ioctl ( fd_audio , AUDIO_SET_MUTE , true ) ) ;
}
cDevice : : Mute ( ) ;
}
void cDvbSdFfDevice : : StillPicture ( const uchar * Data , int Length )
{
if ( ! Data | | Length < TS_SIZE )
return ;
if ( Data [ 0 ] = = 0x47 ) {
// TS data
cDevice : : StillPicture ( Data , Length ) ;
}
else 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 ;
while ( i < Length - 6 ) {
if ( Data [ i ] = = 0x00 & & Data [ i + 1 ] = = 0x00 & & Data [ i + 2 ] = = 0x01 ) {
int len = Data [ i + 4 ] * 256 + Data [ i + 5 ] ;
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
if ( Data [ i + 8 ] > = Length )
break ;
offs + = 3 ;
offs + = Data [ i + 8 ] ;
len - = 3 ;
len - = Data [ i + 8 ] ;
if ( len < 0 | | offs + len > Length )
break ;
}
else {
// MPEG-1 PES header
while ( offs < Length & & len > 0 & & Data [ offs ] = = 0xFF ) {
offs + + ;
len - - ;
}
if ( offs < = Length - 2 & & len > = 2 & & ( Data [ offs ] & 0xC0 ) = = 0x40 ) {
offs + = 2 ;
len - = 2 ;
}
if ( offs < = Length - 5 & & len > = 5 & & ( Data [ offs ] & 0xF0 ) = = 0x20 ) {
offs + = 5 ;
len - = 5 ;
}
else if ( offs < = Length - 10 & & len > = 10 & & ( Data [ offs ] & 0xF0 ) = = 0x30 ) {
offs + = 10 ;
len - = 10 ;
}
else if ( offs < Length & & len > 0 ) {
offs + + ;
len - - ;
}
}
if ( blen + len > Length ) // invalid PES length field
break ;
memcpy ( & buf [ blen ] , & Data [ offs ] , len ) ;
i = offs + len ;
blen + = len ;
}
else if ( Data [ i + 3 ] > = 0xBD & & Data [ i + 3 ] < = 0xDF ) // other PES packets
i + = len + 6 ;
else
i + + ;
}
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 ) ) ;
}
}
bool cDvbSdFfDevice : : Poll ( cPoller & Poller , int TimeoutMs )
{
Poller . Add ( ( playMode = = pmAudioOnly | | playMode = = pmAudioOnlyBlack ) ? fd_audio : fd_video , true ) ;
return Poller . Poll ( TimeoutMs ) ;
}
bool cDvbSdFfDevice : : Flush ( int TimeoutMs )
{
//TODO actually this function should wait until all buffered data has been processed by the card, but how?
return true ;
}
int cDvbSdFfDevice : : PlayVideo ( const uchar * Data , int Length )
{
return WriteAllOrNothing ( fd_video , Data , Length , 1000 , 10 ) ;
}
int cDvbSdFfDevice : : PlayAudio ( const uchar * Data , int Length , uchar Id )
{
return WriteAllOrNothing ( fd_audio , Data , Length , 1000 , 10 ) ;
}
int cDvbSdFfDevice : : PlayTsVideo ( const uchar * Data , int Length )
{
return WriteAllOrNothing ( fd_video , Data , Length , 1000 , 10 ) ;
}
int cDvbSdFfDevice : : PlayTsAudio ( const uchar * Data , int Length )
{
return WriteAllOrNothing ( fd_audio , Data , Length , 1000 , 10 ) ;
}
// --- cDvbSdFfDeviceProbe ---------------------------------------------------
2010-01-04 14:16:11 +01:00
bool cDvbSdFfDeviceProbe : : Probe ( int Adapter , int Frontend )
2009-12-31 15:38:18 +01:00
{
static uint32_t SubsystemIds [ ] = {
0x110A0000 , // Fujitsu Siemens DVB-C
0x13C20000 , // Technotrend/Hauppauge WinTV DVB-S rev1.X or Fujitsu Siemens DVB-C
0x13C20001 , // Technotrend/Hauppauge WinTV DVB-T rev1.X
0x13C20002 , // Technotrend/Hauppauge WinTV DVB-C rev2.X
0x13C20003 , // Technotrend/Hauppauge WinTV Nexus-S rev2.X
0x13C20004 , // Galaxis DVB-S rev1.3
0x13C20006 , // Fujitsu Siemens DVB-S rev1.6
0x13C20008 , // Technotrend/Hauppauge DVB-T
0x13C2000A , // Technotrend/Hauppauge WinTV Nexus-CA rev1.X
0x13C2000E , // Technotrend/Hauppauge WinTV Nexus-S rev2.3
0x13C21002 , // Technotrend/Hauppauge WinTV DVB-S rev1.3 SE
0x00000000
} ;
cString FileName ;
cReadLine ReadLine ;
FILE * f = NULL ;
uint32_t SubsystemId = 0 ;
2010-01-04 14:16:11 +01:00
FileName = cString : : sprintf ( " /sys/class/dvb/dvb%d.frontend%d/device/subsystem_vendor " , Adapter , Frontend ) ;
2009-12-31 15:38:18 +01:00
if ( ( f = fopen ( FileName , " r " ) ) ! = NULL ) {
if ( char * s = ReadLine . Read ( f ) )
SubsystemId = strtoul ( s , NULL , 0 ) < < 16 ;
fclose ( f ) ;
}
2010-01-04 14:16:11 +01:00
FileName = cString : : sprintf ( " /sys/class/dvb/dvb%d.frontend%d/device/subsystem_device " , Adapter , Frontend ) ;
2009-12-31 15:38:18 +01:00
if ( ( f = fopen ( FileName , " r " ) ) ! = NULL ) {
if ( char * s = ReadLine . Read ( f ) )
SubsystemId | = strtoul ( s , NULL , 0 ) ;
fclose ( f ) ;
}
for ( uint32_t * sid = SubsystemIds ; * sid ; sid + + ) {
if ( * sid = = SubsystemId ) {
dsyslog ( " creating cDvbSdFfDevice " ) ;
2010-01-04 14:16:11 +01:00
new cDvbSdFfDevice ( Adapter , Frontend ) ;
2009-12-31 15:38:18 +01:00
return true ;
}
}
return false ;
}