summaryrefslogtreecommitdiff
path: root/dvbdevice.c
diff options
context:
space:
mode:
Diffstat (limited to 'dvbdevice.c')
-rw-r--r--dvbdevice.c264
1 files changed, 145 insertions, 119 deletions
diff --git a/dvbdevice.c b/dvbdevice.c
index 30ce1e4..7fa39cc 100644
--- a/dvbdevice.c
+++ b/dvbdevice.c
@@ -4,18 +4,11 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbdevice.c 1.138 2005/11/26 13:23:11 kls Exp $
+ * $Id: dvbdevice.c 1.149 2006/01/07 15:15:01 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>
#include <linux/dvb/audio.h>
@@ -47,6 +40,13 @@ extern "C" {
#define DEV_DVB_AUDIO "audio"
#define DEV_DVB_CA "ca"
+#define DVBS_TUNE_TIMEOUT 2000 //ms
+#define DVBS_LOCK_TIMEOUT 2000 //ms
+#define DVBC_TUNE_TIMEOUT 5000 //ms
+#define DVBC_LOCK_TIMEOUT 2000 //ms
+#define DVBT_TUNE_TIMEOUT 9000 //ms
+#define DVBT_LOCK_TIMEOUT 2000 //ms
+
class cDvbName {
private:
char buffer[PATH_MAX];
@@ -73,6 +73,9 @@ private:
enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked };
int fd_frontend;
int cardIndex;
+ int tuneTimeout;
+ int lockTimeout;
+ time_t lastTimeoutReport;
fe_type_t frontendType;
cCiHandler *ciHandler;
cChannel channel;
@@ -81,7 +84,7 @@ private:
cMutex mutex;
cCondVar locked;
cCondVar newSet;
- bool GetFrontendEvent(dvb_frontend_event &Event, int TimeoutMs = 0);
+ bool GetFrontendStatus(fe_status_t &Status, int TimeoutMs = 0);
bool SetFrontend(void);
virtual void Action(void);
public:
@@ -98,6 +101,9 @@ cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCi
cardIndex = CardIndex;
frontendType = FrontendType;
ciHandler = CiHandler;
+ tuneTimeout = 0;
+ lockTimeout = 0;
+ lastTimeoutReport = 0;
diseqcCommands = NULL;
tunerStatus = tsIdle;
if (frontendType == FE_QPSK)
@@ -125,6 +131,7 @@ void cDvbTuner::Set(const cChannel *Channel, bool Tune)
if (Tune)
tunerStatus = tsSet;
channel = *Channel;
+ lastTimeoutReport = 0;
newSet.Broadcast();
}
@@ -140,26 +147,18 @@ bool cDvbTuner::Locked(int TimeoutMs)
return tunerStatus >= tsLocked;
}
-bool cDvbTuner::GetFrontendEvent(dvb_frontend_event &Event, int TimeoutMs)
+bool cDvbTuner::GetFrontendStatus(fe_status_t &Status, int TimeoutMs)
{
if (TimeoutMs) {
- struct pollfd pfd;
- pfd.fd = fd_frontend;
- pfd.events = POLLIN | POLLPRI;
- do {
- int stat = poll(&pfd, 1, TimeoutMs);
- if (stat == 1)
- break;
- if (stat < 0) {
- if (errno == EINTR)
- continue;
- esyslog("ERROR: frontend %d poll failed: %m", cardIndex);
- }
- return false;
- } while (0);
+ cPoller Poller(fd_frontend);
+ if (Poller.Poll(TimeoutMs)) {
+ dvb_frontend_event Event;
+ while (ioctl(fd_frontend, FE_GET_EVENT, &Event) == 0)
+ ; // just to clear the event queue - we'll read the actual status below
+ }
}
do {
- int stat = ioctl(fd_frontend, FE_GET_EVENT, &Event);
+ int stat = ioctl(fd_frontend, FE_READ_STATUS, &Status);
if (stat == 0)
return true;
if (stat < 0) {
@@ -245,6 +244,9 @@ bool cDvbTuner::SetFrontend(void)
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());
+
+ tuneTimeout = DVBS_TUNE_TIMEOUT;
+ lockTimeout = DVBS_LOCK_TIMEOUT;
}
break;
case FE_QAM: { // DVB-C
@@ -256,6 +258,9 @@ bool cDvbTuner::SetFrontend(void)
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());
+
+ tuneTimeout = DVBC_TUNE_TIMEOUT;
+ lockTimeout = DVBC_LOCK_TIMEOUT;
}
break;
case FE_OFDM: { // DVB-T
@@ -271,6 +276,9 @@ bool cDvbTuner::SetFrontend(void)
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());
+
+ tuneTimeout = DVBT_TUNE_TIMEOUT;
+ lockTimeout = DVBT_LOCK_TIMEOUT;
}
break;
default:
@@ -286,30 +294,54 @@ bool cDvbTuner::SetFrontend(void)
void cDvbTuner::Action(void)
{
- dvb_frontend_event event;
+ cTimeMs Timer;
+ bool LostLock = false;
+ fe_status_t Status = (fe_status_t)0;
while (Running()) {
- bool hasEvent = GetFrontendEvent(event, 1);
-
+ fe_status_t NewStatus;
+ if (GetFrontendStatus(NewStatus, 10))
+ Status = NewStatus;
cMutexLock MutexLock(&mutex);
switch (tunerStatus) {
case tsIdle:
break;
case tsSet:
- if (hasEvent)
- continue;
tunerStatus = SetFrontend() ? tsTuned : tsIdle;
+ Timer.Set(tuneTimeout);
continue;
case tsTuned:
- case tsLocked:
- if (hasEvent) {
- if (event.status & FE_REINIT) {
- tunerStatus = tsSet;
- esyslog("ERROR: frontend %d was reinitialized - re-tuning", cardIndex);
+ if (Timer.TimedOut()) {
+ tunerStatus = tsSet;
+ diseqcCommands = NULL;
+ if (time(NULL) - lastTimeoutReport > 60) { // let's not get too many of these
+ esyslog("ERROR: frontend %d timed out while tuning to channel %d, tp %d", cardIndex, channel.Number(), channel.Transponder());
+ lastTimeoutReport = time(NULL);
}
- if (event.status & FE_HAS_LOCK) {
- tunerStatus = tsLocked;
- locked.Broadcast();
+ continue;
+ }
+ case tsLocked:
+ if (Status & FE_REINIT) {
+ tunerStatus = tsSet;
+ diseqcCommands = NULL;
+ esyslog("ERROR: frontend %d was reinitialized", cardIndex);
+ lastTimeoutReport = 0;
+ continue;
+ }
+ else if (Status & FE_HAS_LOCK) {
+ if (LostLock) {
+ esyslog("frontend %d regained lock on channel %d, tp %d", cardIndex, channel.Number(), channel.Transponder());
+ LostLock = false;
}
+ tunerStatus = tsLocked;
+ locked.Broadcast();
+ lastTimeoutReport = 0;
+ }
+ else if (tunerStatus == tsLocked) {
+ LostLock = true;
+ esyslog("ERROR: frontend %d lost lock on channel %d, tp %d", cardIndex, channel.Number(), channel.Transponder());
+ tunerStatus = tsTuned;
+ Timer.Set(lockTimeout);
+ lastTimeoutReport = 0;
continue;
}
}
@@ -471,13 +503,21 @@ bool cDvbDevice::Ready(void)
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);
+ int NumCams = 0;
+ if (ciHandler) {
+ NumCams = ciHandler->NumCams();
+ if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
+ unsigned short ids[MAXCAIDS + 1];
+ for (int i = 0; i <= MAXCAIDS; i++) // '<=' copies the terminating 0!
+ ids[i] = Channel->Ca(i);
+ if (ciHandler->ProvidesCa(ids))
+ return NumCams + 1;
+ }
}
- return cDevice::ProvidesCa(Channel);
+ int result = cDevice::ProvidesCa(Channel);
+ if (result > 0)
+ result += NumCams;
+ return result;
}
cSpuDecoder *cDvbDevice::GetSpuDecoder(void)
@@ -487,103 +527,84 @@ cSpuDecoder *cDvbDevice::GetSpuDecoder(void)
return spuDecoder;
}
-bool cDvbDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY)
+uchar *cDvbDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY)
{
if (devVideoIndex < 0)
- return false;
+ return NULL;
char buffer[PATH_MAX];
snprintf(buffer, sizeof(buffer), "%s%d", DEV_VIDEO, devVideoIndex);
int videoDev = open(buffer, O_RDWR);
- if (videoDev < 0)
- LOG_ERROR_STR(buffer);
if (videoDev >= 0) {
- int result = 0;
+ uchar *result = NULL;
struct video_mbuf mbuf;
- result |= ioctl(videoDev, VIDIOCGMBUF, &mbuf);
- if (result == 0) {
+ if (ioctl(videoDev, VIDIOCGMBUF, &mbuf) == 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 = 100;
-
- 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);
+ if (ioctl(videoDev, VIDIOCGCAP, &vc) == 0) {
+ struct video_mmap vm;
+ vm.frame = 0;
+ if ((SizeX > 0) && (SizeX <= vc.maxwidth) &&
+ (SizeY > 0) && (SizeY <= vc.maxheight)) {
+ vm.width = SizeX;
+ vm.height = SizeY;
}
else {
- // 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) != 1) {
- LOG_ERROR_STR(FileName);
- result |= 1;
+ vm.width = vc.maxwidth;
+ vm.height = vc.maxheight;
+ }
+ vm.format = VIDEO_PALETTE_RGB24;
+ if (ioctl(videoDev, VIDIOCMCAPTURE, &vm) == 0 && ioctl(videoDev, VIDIOCSYNC, &vm.frame) == 0) {
+ // make RGB out of BGR:
+ int memsize = vm.width * vm.height;
+ unsigned char *mem1 = mem;
+ for (int i = 0; i < memsize; i++) {
+ unsigned char tmp = mem1[2];
+ mem1[2] = mem1[0];
+ mem1[0] = tmp;
+ mem1 += 3;
+ }
+
+ if (Quality < 0)
+ Quality = 100;
+
+ isyslog("grabbing to %s %d %d %d", Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height);
+ if (Jpeg) {
+ // convert to JPEG:
+ result = RgbToJpeg(mem, vm.width, vm.height, Size, Quality);
+ if (!result)
+ esyslog("ERROR: failed to convert image to JPEG");
+ }
+ else {
+ // convert to PNM:
+ char buf[32];
+ snprintf(buf, sizeof(buf), "P6\n%d\n%d\n255\n", vm.width, vm.height);
+ int l = strlen(buf);
+ int bytes = memsize * 3;
+ Size = l + bytes;
+ result = MALLOC(uchar, Size);
+ if (result) {
+ memcpy(result, buf, l);
+ memcpy(result + l, mem, bytes);
+ }
+ else
+ esyslog("ERROR: failed to convert image to PNM");
}
}
- fclose(f);
- }
- else {
- LOG_ERROR_STR(FileName);
- result |= 1;
}
munmap(mem, msize);
}
else
- result |= 1;
+ esyslog("ERROR: failed to memmap video device");
}
close(videoDev);
- return result == 0;
+ return result;
}
- return false;
+ else
+ LOG_ERROR_STR(buffer);
+ return NULL;
}
void cDvbDevice::SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat)
@@ -754,7 +775,7 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0))) {
#ifdef DO_MULTIPLE_RECORDINGS
#ifndef DO_MULTIPLE_CA_CHANNELS
- if (Ca() > CACONFBASE || Channel->Ca() > CACONFBASE)
+ if (Ca() >= CA_ENCRYPTED_MIN || Channel->Ca() >= CA_ENCRYPTED_MIN)
needsDetachReceivers = Ca() != Channel->Ca();
else
#endif
@@ -825,6 +846,11 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
esyslog("ERROR: failed to set PIDs for channel %d on device %d", Channel->Number(), CardIndex() + 1);
return false;
}
+ //XXX quick workaround for additional live audio PIDs:
+ if (ciHandler) {
+ ciHandler->SetPid(Channel->Apid(1), true);
+ ciHandler->SetPid(Channel->Dpid(0), true);
+ }
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üßler <marco@lordzodiac.de> this works