mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
- Fixed a missing Flush() call in the remote control learning procedure (thanks to Oliver Endriss). - Modified channel handling to cover all parameters necessary for DVB-C and DVB-T (see man vdr(5) for the meaning of the additional parameters stored in the field previously named 'polarisation'). Thanks to Uwe Scheffler and Andy Carter for testing. If you have a system with different kinds of DVB cards, like DVB-T and DVB-C, for instance, there is no more need to distinguish the channels through the 'Ca' parameter in order to assign them to the various DVB cards. This is now taken care of by the "source" parameter. So a channel marked as "terrestrial", for example, will only be received on DVB-T cards. Note that the cChannel class has been moved into a separate file (channels.[ch]), and that all data members have been made private and are now only accessible through member functions. You may have to change any plugin code that accesses cChannel data accordingly. - The new configuration file 'sources.conf' contains the various signal sources (satellites, cable and terrestrial) which are used in 'channels.conf' and 'diseqc.conf' (thanks to Reinhard Walter Buchner for adding some satellites to 'sources.conf' and Oliver Endriss and Lauri Tischler for testing and debugging). - The 'diseqc' parameter in the channel definitions has been redefined to hold the "source" of the given channel (which can be either a satellite, cable or terrestrial). For compatibility with channels.conf files from older versions, numeric values in this parameter will be tolerated, but they have no meaning. If you want to use DiSEqC you will need to replace these old values with the proper source identifiers defined in the new configuration file 'sources.conf'. See how this is done in the 'channels.conf' file that comes with the VDR package. - The new configuration file 'diseqc.conf' can be used to set up the individual diseqc configuration (see man vdr(5) for a description of the file format). - The "Edit channel" menu has a new entry "Source:" in which the source of this channel can be selected (either a satellite, cable or terrestrial). The set of parameters at the end of this menu will change according to the type of source. - The "Use DiSEqC" parameter in the "Setup/LNB" menu has been moved to the beginning of the list and disables the rest of the parameters when set to "yes", since these are now only meaningful if DiSEqC is _not_ used. - Removed some unnecessary #includes from eit.c and changed cMenuRecordings::Del() to cMenuRecordings::Delete() to avoid warnings in gcc-3.2 (thanks to Andreas Schultz for pointing this out). - Improved skipping channels that are (currently) not available (thanks to Stefan Huelswitt). - Updated channels.conf.terr and channels.conf.cable (thanks to Uwe Scheffler). - Fixed a bug when pressing the "Blue" button in the main menu without having displayed it (thanks to Oliver Endriss for reporting this one).
930 lines
32 KiB
C
930 lines
32 KiB
C
/*
|
|
* dvbdevice.c: The DVB device interface
|
|
*
|
|
* See the main source file 'vdr.c' for copyright information and
|
|
* how to reach the author.
|
|
*
|
|
* $Id: dvbdevice.c 1.22 2002/10/06 09:07:45 kls Exp $
|
|
*/
|
|
|
|
#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>
|
|
#ifdef NEWSTRUCT
|
|
#include <linux/dvb/audio.h>
|
|
#include <linux/dvb/dmx.h>
|
|
#include <linux/dvb/frontend.h>
|
|
#include <linux/dvb/video.h>
|
|
#else
|
|
#include <ost/audio.h>
|
|
#include <ost/dmx.h>
|
|
#include <ost/sec.h>
|
|
#include <ost/video.h>
|
|
#endif
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include "channels.h"
|
|
#include "diseqc.h"
|
|
#include "dvbosd.h"
|
|
#include "player.h"
|
|
#include "receiver.h"
|
|
#include "status.h"
|
|
#include "transfer.h"
|
|
|
|
#define DEV_VIDEO "/dev/video"
|
|
#ifdef NEWSTRUCT
|
|
#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"
|
|
#else
|
|
#define DEV_DVB_OSD "/dev/ost/osd"
|
|
#define DEV_DVB_FRONTEND "/dev/ost/frontend"
|
|
#define DEV_DVB_SEC "/dev/ost/sec"
|
|
#define DEV_DVB_DVR "/dev/ost/dvr"
|
|
#define DEV_DVB_DEMUX "/dev/ost/demux"
|
|
#define DEV_DVB_VIDEO "/dev/ost/video"
|
|
#define DEV_DVB_AUDIO "/dev/ost/audio"
|
|
#endif
|
|
|
|
static const char *DvbName(const char *Name, int n)
|
|
{
|
|
static char buffer[PATH_MAX];
|
|
#ifdef NEWSTRUCT
|
|
snprintf(buffer, sizeof(buffer), "%s%d/%s%d", DEV_DVB_ADAPTER, n, Name, 0);
|
|
#else
|
|
snprintf(buffer, sizeof(buffer), "%s%d", Name, n);
|
|
#endif
|
|
return buffer;
|
|
}
|
|
|
|
static int DvbOpen(const char *Name, int n, int Mode, bool ReportError = false)
|
|
{
|
|
const char *FileName = DvbName(Name, n);
|
|
int fd = open(FileName, Mode);
|
|
if (fd < 0 && ReportError)
|
|
LOG_ERROR_STR(FileName);
|
|
return fd;
|
|
}
|
|
|
|
cDvbDevice::cDvbDevice(int n)
|
|
{
|
|
frontendType = FrontendType(-1); // don't know how else to initialize this - there is no FE_UNKNOWN
|
|
siProcessor = NULL;
|
|
spuDecoder = NULL;
|
|
playMode = pmNone;
|
|
|
|
// Devices that are present on all card types:
|
|
|
|
fd_frontend = DvbOpen(DEV_DVB_FRONTEND, n, O_RDWR | O_NONBLOCK);
|
|
|
|
// Devices that are only present on cards with decoders:
|
|
|
|
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);
|
|
|
|
#ifndef NEWSTRUCT
|
|
// Devices that are only present on DVB-S cards:
|
|
|
|
fd_sec = DvbOpen(DEV_DVB_SEC, n, O_RDWR);
|
|
#endif
|
|
|
|
// The DVR device (will be opened and closed as needed):
|
|
|
|
fd_dvr = -1;
|
|
|
|
// 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) {
|
|
#ifdef NEWSTRUCT
|
|
dvb_frontend_info feinfo;
|
|
#else
|
|
FrontendInfo feinfo;
|
|
#endif
|
|
siProcessor = new cSIProcessor(DvbName(DEV_DVB_DEMUX, n));
|
|
if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0)
|
|
frontendType = feinfo.type;
|
|
else
|
|
LOG_ERROR;
|
|
}
|
|
else
|
|
esyslog("ERROR: can't open DVB device %d", n);
|
|
|
|
source = -1;
|
|
frequency = -1;
|
|
diseqcCommands = NULL;
|
|
}
|
|
|
|
cDvbDevice::~cDvbDevice()
|
|
{
|
|
delete spuDecoder;
|
|
delete siProcessor;
|
|
// 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())) {
|
|
if (Probe(DvbName(DEV_DVB_FRONTEND, i))) {
|
|
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)
|
|
{
|
|
cDvbOsd::SetDvbDevice(On ? this : NULL);
|
|
}
|
|
|
|
bool cDvbDevice::HasDecoder(void) const
|
|
{
|
|
return fd_video >= 0 && fd_audio >= 0;
|
|
}
|
|
|
|
cOsdBase *cDvbDevice::NewOsd(int x, int y)
|
|
{
|
|
return new cDvbOsd(x, y);
|
|
}
|
|
|
|
cSpuDecoder *cDvbDevice::GetSpuDecoder(void)
|
|
{
|
|
if (!spuDecoder && IsPrimaryDevice())
|
|
spuDecoder = new cDvbSpuDecoder();
|
|
return spuDecoder;
|
|
}
|
|
|
|
bool cDvbDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY)
|
|
{
|
|
char buffer[PATH_MAX];
|
|
snprintf(buffer, sizeof(buffer), "%s%d", DEV_VIDEO, CardIndex());
|
|
int videoDev = open(buffer, O_RDWR);
|
|
if (videoDev < 0)
|
|
LOG_ERROR_STR(buffer);
|
|
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;
|
|
}
|
|
|
|
if (Quality < 0)
|
|
Quality = 255; //XXX is this 'best'???
|
|
|
|
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;
|
|
|
|
jpeg_set_defaults(&cinfo);
|
|
jpeg_set_quality(&cinfo, Quality, true);
|
|
jpeg_start_compress(&cinfo, true);
|
|
|
|
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\n255\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));
|
|
}
|
|
|
|
// ptAudio ptVideo ptTeletext ptDolby ptOther
|
|
dmxPesType_t PesTypes[] = { DMX_PES_AUDIO, DMX_PES_VIDEO, DMX_PES_TELETEXT, DMX_PES_OTHER, DMX_PES_OTHER };
|
|
|
|
bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
|
|
{
|
|
if (Handle->pid) {
|
|
dmxPesFilterParams pesFilterParams;
|
|
memset(&pesFilterParams, 0, sizeof(pesFilterParams));
|
|
if (On) {
|
|
if (Handle->handle < 0) {
|
|
Handle->handle = DvbOpen(DEV_DVB_DEMUX, CardIndex(), O_RDWR | O_NONBLOCK, true);
|
|
if (Handle->handle < 0)
|
|
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.pesType = 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.pesType = PesTypes[Type];
|
|
pesFilterParams.flags = DMX_IMMEDIATE_START;
|
|
CHECK(ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams));
|
|
close(Handle->handle);
|
|
Handle->handle = -1;
|
|
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
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool cDvbDevice::IsTunedTo(const cChannel *Channel) const
|
|
{
|
|
return source == Channel->Source() && frequency == Channel->Frequency();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
|
|
{
|
|
bool result = false;
|
|
bool hasPriority = Priority < 0 || Priority > this->Priority();
|
|
bool needsDetachReceivers = true;
|
|
|
|
if (ProvidesSource(Channel->Source()) && ProvidesCa(Channel->Ca())) {
|
|
if (Receiving()) {
|
|
if (IsTunedTo(Channel)) {
|
|
needsDetachReceivers = false;
|
|
if (!HasPid(Channel->Vpid())) {
|
|
if (Channel->Ca() > CACONFBASE) {
|
|
needsDetachReceivers = true;
|
|
result = hasPriority;
|
|
}
|
|
else if (!IsPrimaryDevice())
|
|
result = true;
|
|
else {
|
|
#define DVB_DRIVER_VERSION 2002090101 //XXX+
|
|
#define MIN_DVB_DRIVER_VERSION_FOR_TIMESHIFT 2002090101
|
|
#ifdef DVB_DRIVER_VERSION
|
|
#if (DVB_DRIVER_VERSION >= MIN_DVB_DRIVER_VERSION_FOR_TIMESHIFT)
|
|
result = !IsPrimaryDevice() || Priority >= Setup.PrimaryLimit;
|
|
#endif
|
|
#else
|
|
#warning "DVB_DRIVER_VERSION not defined - time shift with only one DVB device disabled!"
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
result = !IsPrimaryDevice() || Priority >= Setup.PrimaryLimit;
|
|
}
|
|
else
|
|
result = hasPriority;
|
|
}
|
|
else
|
|
result = hasPriority;
|
|
}
|
|
if (NeedsDetachReceivers)
|
|
*NeedsDetachReceivers = needsDetachReceivers;
|
|
return result;
|
|
}
|
|
|
|
bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
|
|
{
|
|
#if (DVB_DRIVER_VERSION < MIN_DVB_DRIVER_VERSION_FOR_TIMESHIFT)
|
|
if (HasDecoder())
|
|
LiveView = true;
|
|
#endif
|
|
|
|
bool DoTune = !IsTunedTo(Channel);
|
|
|
|
bool TurnOffLivePIDs = HasDecoder()
|
|
&& (DoTune
|
|
|| Channel->Ca() > CACONFBASE && pidHandles[ptVideo].pid != Channel->Vpid() // CA channels can only be decrypted in "live" mode
|
|
|| 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
|
|
)
|
|
);
|
|
|
|
bool StartTransferMode = IsPrimaryDevice() && !DoTune
|
|
&& (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
|
|
);
|
|
|
|
bool TurnOnLivePIDs = HasDecoder() && !StartTransferMode
|
|
&& (Channel->Ca() > CACONFBASE // CA channels can only be decrypted in "live" mode
|
|
|| LiveView
|
|
);
|
|
|
|
// Stop setting system time:
|
|
|
|
if (siProcessor)
|
|
siProcessor->SetCurrentTransponder(0);
|
|
|
|
// Turn off live PIDs if necessary:
|
|
|
|
if (TurnOffLivePIDs) {
|
|
|
|
// 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[ptTeletext].pid);
|
|
DelPid(pidHandles[ptDolby].pid);
|
|
}
|
|
|
|
if (DoTune) {
|
|
|
|
#ifdef NEWSTRUCT
|
|
dvb_frontend_parameters Frontend;
|
|
#else
|
|
FrontendParameters Frontend;
|
|
#endif
|
|
|
|
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)) {
|
|
#ifndef NEWSTRUCT
|
|
int SecTone = SEC_TONE_OFF;
|
|
int SecVolt = SEC_VOLTAGE_13;
|
|
#endif
|
|
cDiseqc::eDiseqcActions da;
|
|
for (bool Start = true; (da = diseqc->Execute(Start)) != cDiseqc::daNone; Start = false) {
|
|
switch (da) {
|
|
#ifdef NEWSTRUCT
|
|
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;
|
|
#else
|
|
// This may not work very good with the old driver.
|
|
// Let's try to emulate the NEWSTRUCT driver's behaviour as good as possible...
|
|
case cDiseqc::daNone: break;
|
|
case cDiseqc::daToneOff: CHECK(ioctl(fd_sec, SEC_SET_TONE, SecTone = SEC_TONE_OFF)); break;
|
|
case cDiseqc::daToneOn: CHECK(ioctl(fd_sec, SEC_SET_TONE, SecTone = SEC_TONE_ON)); break;
|
|
case cDiseqc::daVoltage13: CHECK(ioctl(fd_sec, SEC_SET_VOLTAGE, SecVolt = SEC_VOLTAGE_13)); break;
|
|
case cDiseqc::daVoltage18: CHECK(ioctl(fd_sec, SEC_SET_VOLTAGE, SecVolt = SEC_VOLTAGE_18)); break;
|
|
case cDiseqc::daMiniA:
|
|
case cDiseqc::daMiniB: {
|
|
secCmdSequence scmds;
|
|
memset(&scmds, 0, sizeof(scmds));
|
|
scmds.voltage = SecVolt;
|
|
scmds.miniCommand = (da == cDiseqc::daMiniA) ? SEC_MINI_A : SEC_MINI_B;
|
|
scmds.continuousTone = SecTone;
|
|
CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds));
|
|
}
|
|
break;
|
|
case cDiseqc::daCodes: {
|
|
int n = 0;
|
|
uchar *codes = diseqc->Codes(n);
|
|
if (codes && n >= 3 && codes[0] == 0xE0) {
|
|
secCommand scmd;
|
|
memset(&scmd, 0, sizeof(scmd));
|
|
scmd.type = SEC_CMDTYPE_DISEQC;
|
|
scmd.u.diseqc.addr = codes[1];
|
|
scmd.u.diseqc.cmd = codes[2];
|
|
scmd.u.diseqc.numParams = n - 3;
|
|
memcpy(scmd.u.diseqc.params, &codes[3], min(n - 3, int(sizeof(scmd.u.diseqc.params))));
|
|
|
|
secCmdSequence scmds;
|
|
memset(&scmds, 0, sizeof(scmds));
|
|
scmds.voltage = SecVolt;
|
|
scmds.miniCommand = SEC_MINI_NONE;
|
|
scmds.continuousTone = SecTone;
|
|
scmds.numCommands = 1;
|
|
scmds.commands = &scmd;
|
|
|
|
CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds));
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
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;
|
|
#ifdef NEWSTRUCT
|
|
CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt));
|
|
CHECK(ioctl(fd_frontend, FE_SET_TONE, tone));
|
|
#else
|
|
secCmdSequence scmds;
|
|
memset(&scmds, 0, sizeof(scmds));
|
|
scmds.voltage = volt;
|
|
scmds.miniCommand = SEC_MINI_NONE;
|
|
scmds.continuousTone = tone;
|
|
CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds));
|
|
#endif
|
|
}
|
|
|
|
#ifdef NEWSTRUCT
|
|
Frontend.frequency = frequency * 1000UL;
|
|
Frontend.inversion = SpectralInversion(Channel->Inversion());
|
|
Frontend.u.qpsk.symbol_rate = Channel->Srate() * 1000UL;
|
|
Frontend.u.qpsk.fec_inner = CodeRate(Channel->CoderateH());
|
|
#else
|
|
Frontend.Frequency = frequency * 1000UL;
|
|
Frontend.Inversion = SpectralInversion(Channel->Inversion());
|
|
Frontend.u.qpsk.SymbolRate = Channel->Srate() * 1000UL;
|
|
Frontend.u.qpsk.FEC_inner = CodeRate(Channel->CoderateH());
|
|
#endif
|
|
}
|
|
break;
|
|
case FE_QAM: { // DVB-C
|
|
|
|
// Frequency and symbol rate:
|
|
|
|
#ifdef NEWSTRUCT
|
|
Frontend.frequency = Channel->Frequency() * 1000000UL;
|
|
Frontend.inversion = SpectralInversion(Channel->Inversion());
|
|
Frontend.u.qam.symbol_rate = Channel->Srate() * 1000UL;
|
|
Frontend.u.qam.fec_inner = CodeRate(Channel->CoderateH());
|
|
Frontend.u.qam.modulation = Modulation(Channel->Modulation());
|
|
#else
|
|
Frontend.Frequency = Channel->Frequency() * 1000000UL;
|
|
Frontend.Inversion = SpectralInversion(Channel->Inversion());
|
|
Frontend.u.qam.SymbolRate = Channel->Srate() * 1000UL;
|
|
Frontend.u.qam.FEC_inner = CodeRate(Channel->CoderateH());
|
|
Frontend.u.qam.QAM = Modulation(Channel->Modulation());
|
|
#endif
|
|
}
|
|
break;
|
|
case FE_OFDM: { // DVB-T
|
|
|
|
// Frequency and OFDM paramaters:
|
|
|
|
#ifdef NEWSTRUCT
|
|
Frontend.frequency = Channel->Frequency() * 1000UL;
|
|
Frontend.inversion = SpectralInversion(Channel->Inversion());
|
|
Frontend.u.ofdm.bandwidth = BandWidth(Channel->Bandwidth());
|
|
Frontend.u.ofdm.code_rate_HP = CodeRate(Channel->CoderateH());
|
|
Frontend.u.ofdm.code_rate_LP = CodeRate(Channel->CoderateL());
|
|
Frontend.u.ofdm.constellation = Modulation(Channel->Modulation());
|
|
Frontend.u.ofdm.transmission_mode = TransmitMode(Channel->Transmission());
|
|
Frontend.u.ofdm.guard_interval = GuardInterval(Channel->Guard());
|
|
Frontend.u.ofdm.hierarchy_information = Hierarchy(Channel->Hierarchy());
|
|
#else
|
|
Frontend.Frequency = Channel->Frequency() * 1000UL;
|
|
Frontend.Inversion = SpectralInversion(Channel->Inversion());
|
|
Frontend.u.ofdm.bandWidth = BandWidth(Channel->Bandwidth());
|
|
Frontend.u.ofdm.HP_CodeRate = CodeRate(Channel->CoderateH());
|
|
Frontend.u.ofdm.LP_CodeRate = CodeRate(Channel->CoderateL());
|
|
Frontend.u.ofdm.Constellation = Modulation(Channel->Modulation());
|
|
Frontend.u.ofdm.TransmissionMode = TransmitMode(Channel->Transmission());
|
|
Frontend.u.ofdm.guardInterval = GuardInterval(Channel->Guard());
|
|
Frontend.u.ofdm.HierarchyInformation = Hierarchy(Channel->Hierarchy());
|
|
#endif
|
|
}
|
|
break;
|
|
default:
|
|
esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
|
|
return false;
|
|
}
|
|
|
|
#ifdef NEWSTRUCT
|
|
// Discard stale events:
|
|
|
|
for (;;) {
|
|
dvb_frontend_event event;
|
|
if (ioctl(fd_frontend, FE_GET_EVENT, &event) < 0)
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
// Tuning:
|
|
|
|
CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend));
|
|
|
|
// Wait for channel lock:
|
|
|
|
#ifdef NEWSTRUCT
|
|
FrontendStatus status = FrontendStatus(0);
|
|
for (int i = 0; i < 100; i++) {
|
|
CHECK(ioctl(fd_frontend, FE_READ_STATUS, &status));
|
|
if (status & FE_HAS_LOCK)
|
|
break;
|
|
usleep(10 * 1000);
|
|
}
|
|
if (!(status & FE_HAS_LOCK)) {
|
|
esyslog("ERROR: channel %d not locked on DVB card %d!", Channel->Number(), CardIndex() + 1);
|
|
if (LiveView && IsPrimaryDevice())
|
|
cThread::RaisePanic();
|
|
return false;
|
|
}
|
|
#else
|
|
if (cFile::FileReady(fd_frontend, 5000)) {
|
|
FrontendEvent event;
|
|
if (ioctl(fd_frontend, FE_GET_EVENT, &event) >= 0) {
|
|
if (event.type != FE_COMPLETION_EV) {
|
|
esyslog("ERROR: channel %d not sync'ed on DVB card %d!", Channel->Number(), CardIndex() + 1);
|
|
if (LiveView && IsPrimaryDevice())
|
|
cThread::RaisePanic();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
esyslog("ERROR in frontend get event (channel %d, card %d): %m", Channel->Number(), CardIndex() + 1);
|
|
}
|
|
else
|
|
esyslog("ERROR: timeout while tuning on DVB card %d", CardIndex() + 1);
|
|
#endif
|
|
|
|
source = Channel->Source();
|
|
frequency = Channel->Frequency();
|
|
|
|
}
|
|
|
|
// PID settings:
|
|
|
|
if (TurnOnLivePIDs) {
|
|
if (!(AddPid(Channel->Apid1(), ptAudio) && AddPid(Channel->Vpid(), ptVideo))) {//XXX+ dolby dpid1!!! (if audio plugins are attached)
|
|
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_AV_SYNC, true));
|
|
CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
|
|
CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false));
|
|
}
|
|
else if (StartTransferMode)
|
|
cControl::Launch(new cTransferControl(this, Channel->Vpid(), Channel->Apid1(), 0, 0, 0));
|
|
|
|
// Start setting system time:
|
|
|
|
if (siProcessor)
|
|
siProcessor->SetCurrentTransponder(Channel->Frequency());
|
|
|
|
return true;
|
|
}
|
|
|
|
void cDvbDevice::SetVolumeDevice(int Volume)
|
|
{
|
|
if (HasDecoder()) {
|
|
audioMixer_t am;
|
|
am.volume_left = am.volume_right = Volume;
|
|
CHECK(ioctl(fd_audio, AUDIO_SET_MIXER, &am));
|
|
}
|
|
}
|
|
|
|
bool cDvbDevice::SetPlayMode(ePlayMode PlayMode)
|
|
{
|
|
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);
|
|
}
|
|
|
|
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));
|
|
if (siProcessor)
|
|
siProcessor->SetStatus(true);
|
|
break;
|
|
case pmAudioVideo:
|
|
case pmAudioOnlyBlack:
|
|
if (siProcessor)
|
|
siProcessor->SetStatus(false);
|
|
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:
|
|
if (siProcessor)
|
|
siProcessor->SetStatus(false);
|
|
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 pmExtern_THIS_SHOULD_BE_AVOIDED:
|
|
if (siProcessor)
|
|
siProcessor->SetStatus(false);
|
|
close(fd_video);
|
|
close(fd_audio);
|
|
fd_video = fd_audio = -1;
|
|
break;
|
|
}
|
|
playMode = PlayMode;
|
|
return true;
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
void cDvbDevice::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));
|
|
if (fd_video >= 0)
|
|
CHECK(ioctl(fd_video, VIDEO_CONTINUE));
|
|
}
|
|
}
|
|
|
|
void cDvbDevice::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));
|
|
if (fd_video >= 0)
|
|
CHECK(ioctl(fd_video, VIDEO_FREEZE));
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
void cDvbDevice::StillPicture(const uchar *Data, int Length)
|
|
{
|
|
Mute();
|
|
/* 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
|
|
*/
|
|
//#define VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
|
|
#ifdef VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
|
|
videoDisplayStillPicture sp = { (char *)Data, Length };
|
|
CHECK(ioctl(fd_video, VIDEO_STILLPICTURE, &sp));
|
|
#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
|
|
}
|
|
|
|
bool cDvbDevice::Poll(cPoller &Poller, int TimeoutMs)
|
|
{
|
|
Poller.Add((playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) ? fd_audio : fd_video, true);
|
|
return Poller.Poll(TimeoutMs);
|
|
}
|
|
|
|
int cDvbDevice::PlayVideo(const uchar *Data, int Length)
|
|
{
|
|
int fd = (playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) ? fd_audio : fd_video;
|
|
if (fd >= 0)
|
|
return write(fd, Data, Length);
|
|
return -1;
|
|
}
|
|
|
|
int cDvbDevice::PlayAudio(const uchar *Data, int Length)
|
|
{
|
|
//XXX+
|
|
return -1;
|
|
}
|
|
|
|
bool cDvbDevice::OpenDvr(void)
|
|
{
|
|
CloseDvr();
|
|
fd_dvr = DvbOpen(DEV_DVB_DVR, CardIndex(), O_RDONLY | O_NONBLOCK, true);
|
|
if (fd_dvr >= 0)
|
|
tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(2), CardIndex() + 1);
|
|
return fd_dvr >= 0;
|
|
}
|
|
|
|
void cDvbDevice::CloseDvr(void)
|
|
{
|
|
if (fd_dvr >= 0) {
|
|
close(fd_dvr);
|
|
fd_dvr = -1;
|
|
delete tsBuffer;
|
|
tsBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
bool cDvbDevice::GetTSPacket(uchar *&Data)
|
|
{
|
|
if (tsBuffer) {
|
|
int r = tsBuffer->Read();
|
|
if (r >= 0) {
|
|
Data = tsBuffer->Get();
|
|
return true;
|
|
}
|
|
else if (FATALERRNO) {
|
|
if (errno == EBUFFEROVERFLOW) // this error code is not defined in the library
|
|
esyslog("ERROR: DVB driver buffer overflow on device %d", CardIndex() + 1);
|
|
else {
|
|
LOG_ERROR;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|