diff options
author | Klaus Schmidinger <vdr@tvdr.de> | 2002-08-04 14:57:29 +0200 |
---|---|---|
committer | Klaus Schmidinger <vdr@tvdr.de> | 2002-08-04 14:57:29 +0200 |
commit | 15cc1733e0e7bf005362fad61a3ccbbefe1cbceb (patch) | |
tree | 7bab620fbfb760f31daa464445f791f3fc0cf322 | |
parent | 61ccfd5fab3e009bb6feda0647cd958107258819 (diff) | |
download | vdr-15cc1733e0e7bf005362fad61a3ccbbefe1cbceb.tar.gz vdr-15cc1733e0e7bf005362fad61a3ccbbefe1cbceb.tar.bz2 |
Changed the cDevice class to allow plugins to implement their own devices
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | PLUGINS.html | 117 | ||||
-rw-r--r-- | config.c | 6 | ||||
-rw-r--r-- | device.c | 673 | ||||
-rw-r--r-- | device.h | 205 | ||||
-rw-r--r-- | dvbdevice.c | 615 | ||||
-rw-r--r-- | dvbdevice.h | 97 | ||||
-rw-r--r-- | dvbosd.c | 13 | ||||
-rw-r--r-- | dvbosd.h | 7 | ||||
-rw-r--r-- | eit.h | 4 | ||||
-rw-r--r-- | eitscan.c | 3 | ||||
-rw-r--r-- | menu.c | 4 | ||||
-rw-r--r-- | osd.c | 4 | ||||
-rw-r--r-- | receiver.c | 3 | ||||
-rw-r--r-- | remux.h | 4 | ||||
-rw-r--r-- | ringbuffer.h | 5 | ||||
-rw-r--r-- | tools.c | 4 | ||||
-rw-r--r-- | vdr.c | 11 |
18 files changed, 1135 insertions, 644 deletions
@@ -4,7 +4,7 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: Makefile 1.43 2002/07/28 13:24:58 kls Exp $ +# $Id: Makefile 1.44 2002/07/28 15:20:47 kls Exp $ .DELETE_ON_ERROR: @@ -27,7 +27,7 @@ INCLUDES = -I$(DVBDIR)/ost/include DTVLIB = $(DTVDIR)/libdtv.a -OBJS = audio.o config.o cutter.o device.o dvbplayer.o dvbosd.o eit.o eitscan.o font.o i18n.o\ +OBJS = audio.o config.o cutter.o device.o dvbdevice.o dvbosd.o dvbplayer.o eit.o eitscan.o font.o i18n.o\ interface.o menu.o menuitems.o osdbase.o osd.o player.o plugin.o receiver.o\ recorder.o recording.o remote.o remux.o ringbuffer.o status.o svdrp.o thread.o\ tools.o transfer.o vdr.o videodir.o diff --git a/PLUGINS.html b/PLUGINS.html index 2a66e88d..c9d6c542 100644 --- a/PLUGINS.html +++ b/PLUGINS.html @@ -1152,5 +1152,122 @@ of these functions, and VDR/osd.c to see how VDR opens the OSD and sets up its windows and color depths). <!--X1.1.5--></td></tr></table> +<!--X1.1.6--><table width=100%><tr><td bgcolor=#FF0000> </td><td width=100%> +<hr><h2>Devices</h2> + +<center><i><b>Expanding the possibilities</b></i></center><p> + +By default VDR is based on using DVB PCI cards that are supported by the +LinuxDVB driver. However, a plugin can implement additional devices that +can be used as sources of MPEG data for viewing or recording, and also +as output devices for replaying. Such a device can be a physical card +that is installed in the PC (like, for instance, an MPEG encoder card that +allows the analog signal of a proprietary set-top box to be integrated +into a VDR system; or an analog TV receiver card, which does the MPEG encoding +"on the fly" - assuming your machine is fast enough), or just a software program that takes an MPEG data +stream and displays it, for instance, on an existing graphics adapter. +<p> +To implement an additional device, a plugin must derive a class from cDevice: + +<p><table><tr><td bgcolor=#F0F0F0><pre><br> +#include <vdr/device.h> + +class cMyDevice : public cDevice { + ... + }; +</pre></td></tr></table><p> + +The derived class must implement several virtual functions, according to +the abilities this new class of devices can provide. See the comments in the +file <tt>VDR/device.h</tt> for more information on the various functions, +and also <tt>VDR/dvbdevice.[hc]</tt> for details on the implementation of +the <tt>cDvbDevice</tt>, which is used to access the DVB PCI cards. +<p> +<b>Channel selection</b> +<p> +If the new device can receive, it most likely needs to provide a way of +selecting which channel it shall tune to: + +<p><table><tr><td bgcolor=#F0F0F0><pre><br> +virtual bool SetChannelDevice(const cChannel *Channel); +</pre></td></tr></table><p> + +This function will be called with the desired channel and shall return whether +tuning to it was successful. +<p> +<b>Recording</b> +<p> +A device that can be used for recording must implement the functions + +<p><table><tr><td bgcolor=#F0F0F0><pre><br> +virtual bool SetPid(cPidHandle *Handle, int Type, bool On); +virtual bool OpenDvr(void); +virtual void CloseDvr(void); +virtual int GetTSPacket(uchar *Data); +</pre></td></tr></table><p> + +which allow VDR to set the PIDs that shall be recorded, set up the device fro +recording (and shut it down again), and receive the MPEG data stream. The data +must be delivered in the form of a Transport Stream (TS), which consists of +packets that are all 188 bytes in size. Each call to <tt>GetTSPacket()</tt> +must deliver exactly one such packet (if one is currently available). +<p> +If this device allows receiving several different data streams, it can +implement + +<p><table><tr><td bgcolor=#F0F0F0><pre><br> +virtual bool CanBeReUsed(int Frequency, int Vpid); +</pre></td></tr></table><p> + +to indicate this to VDR. +<p> +<b>Replaying</b> +<p> +The functions to implement replaying capabilites are + +<p><table><tr><td bgcolor=#F0F0F0><pre><br> +virtual bool HasDecoder(void) const; +virtual int SetPlayMode(bool On); +virtual void TrickSpeed(int Speed); +virtual void Clear(void); +virtual void Play(void); +virtual void Freeze(void); +virtual void Mute(void); +virtual void StillPicture(const uchar *Data, int Length); +virtual int PlayVideo(const uchar *Data, int Length); +</pre></td></tr></table><p> + +In addition, the following functions may be implemented to provide further +functionality: + +<p><table><tr><td bgcolor=#F0F0F0><pre><br> +virtual bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int Si +virtual void SetVideoFormat(bool VideoFormat16_9); +virtual void SetVolumeDevice(int Volume); +</pre></td></tr></table><p> + +<b>Initializing new devices</b> +<p> +A derived cDevice class shall implement a static function + +<p><table><tr><td bgcolor=#F0F0F0><pre><br> +static bool Initialize(void); +</pre></td></tr></table><p> + +in which it determines whether the necessary hardware to run this sort of +device is actually present in this machine (or whatever other prerequisites +might be important), and then creates as many device objects as necessary. +See <tt>VDR/dvbdevice.c</tt> for the implementation of the <tt>cDvbDevice</tt> +initialize function. +<p> +A plugin that adds devices to a VDR instance shall call this initializing +function from its <a href="#Getting started"><tt>Start()</tt></a> function. +<p> +Nothing needs to be done to shut down the devices. VDR will automatically +shut down (delete) all devices when the program terminates. It is therefore +important that the devices are created on the heap, using the <tt>new</tt> +operator! +<!--X1.1.6--></td></tr></table> + </body> </html> @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.c 1.102 2002/06/16 12:57:31 kls Exp $ + * $Id: config.c 1.103 2002/08/04 12:03:11 kls Exp $ */ #include "config.h" @@ -301,7 +301,7 @@ bool cChannel::Switch(cDevice *Device, bool Log) if (Log) isyslog("switching to channel %d", number); for (int i = 3; i--;) { - switch (Device->SetChannel(number, frequency, polarization, diseqc, srate, vpid, apid1, tpid, ca, pnr)) { + switch (Device->SetChannel(this)) { case scrOk: return true; case scrNoTransfer: if (Interface) Interface->Error(tr("Can't start Transfer Mode!")); @@ -1018,7 +1018,7 @@ cSetup::cSetup(void) DefaultLifetime = 50; UseSubtitle = 1; RecordingDirs = 1; - VideoFormat = VIDEO_FORMAT_4_3; + VideoFormat = 0; RecordDolbyDigital = 1; ChannelInfoPos = 0; OSDwidth = 52; @@ -4,108 +4,42 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.c 1.6 2002/07/28 11:03:53 kls Exp $ + * $Id: device.c 1.7 2002/08/04 12:32:49 kls Exp $ */ #include "device.h" #include <errno.h> -extern "C" { -#define HAVE_BOOLEAN -#include <jpeglib.h> -} -#include <linux/videodev.h> -#include <ost/sec.h> #include <poll.h> #include <sys/ioctl.h> #include <sys/mman.h> +#include "eit.h" #include "player.h" #include "receiver.h" #include "status.h" #include "transfer.h" -#define DEV_VIDEO "/dev/video" -#define DEV_OST_OSD "/dev/ost/osd" -#define DEV_OST_FRONTEND "/dev/ost/frontend" -#define DEV_OST_SEC "/dev/ost/sec" -#define DEV_OST_DVR "/dev/ost/dvr" -#define DEV_OST_DEMUX "/dev/ost/demux" -#define DEV_OST_VIDEO "/dev/ost/video" -#define DEV_OST_AUDIO "/dev/ost/audio" - -// The default priority for non-primary DVB cards: +// The default priority for non-primary devices: #define DEFAULTPRIORITY -2 -#define TS_SIZE 188 -#define TS_SYNC_BYTE 0x47 -#define PID_MASK_HI 0x1F - // The maximum time we wait before assuming that a recorded video data stream // is broken: #define MAXBROKENTIMEOUT 30 // seconds -static const char *OstName(const char *Name, int n) -{ - static char buffer[_POSIX_PATH_MAX]; - snprintf(buffer, sizeof(buffer), "%s%d", Name, n); - return buffer; -} - -static int OstOpen(const char *Name, int n, int Mode, bool ReportError = false) -{ - const char *FileName = OstName(Name, n); - int fd = open(FileName, Mode); - if (fd < 0 && ReportError) - LOG_ERROR_STR(FileName); - return fd; -} - int cDevice::numDevices = 0; int cDevice::useDevice = 0; +int cDevice::nextCardIndex = 0; cDevice *cDevice::device[MAXDEVICES] = { NULL }; cDevice *cDevice::primaryDevice = NULL; -cDevice::cDevice(int n) +cDevice::cDevice(void) { - frontendType = FrontendType(-1); // don't know how else to initialize this - there is no FE_UNKNOWN - siProcessor = NULL; - cardIndex = n; - - // Devices that are present on all card types: + cardIndex = nextCardIndex++; - fd_frontend = OstOpen(DEV_OST_FRONTEND, n, O_RDWR); - - // Devices that are only present on DVB-S cards: - - fd_sec = OstOpen(DEV_OST_SEC, n, O_RDWR); - - // Devices that are only present on cards with decoders: - - fd_osd = OstOpen(DEV_OST_OSD, n, O_RDWR); - fd_video = OstOpen(DEV_OST_VIDEO, n, O_RDWR | O_NONBLOCK); - fd_audio = OstOpen(DEV_OST_AUDIO, n, O_RDWR | O_NONBLOCK); - - // 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) { - siProcessor = new cSIProcessor(OstName(DEV_OST_DEMUX, n)); - FrontendInfo feinfo; - if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0) - frontendType = feinfo.type; - else - LOG_ERROR; - } - else - esyslog("ERROR: can't open video device %d", n); + SetVideoFormat(Setup.VideoFormat); - dvrFileName = strdup(OstName(DEV_OST_DVR, CardIndex())); active = false; currentChannel = 0; - frequency = 0; mute = false; volume = Setup.CurrentVolume; @@ -115,17 +49,20 @@ cDevice::cDevice(int n) for (int i = 0; i < MAXRECEIVERS; i++) receiver[i] = NULL; ca = -1; + + if (numDevices < MAXDEVICES) { + device[numDevices++] = this; + SetCaCaps(cardIndex); + } + else + esyslog("ERROR: too many devices!"); } cDevice::~cDevice() { - delete dvrFileName; - delete siProcessor; Detach(player); for (int i = 0; i < MAXRECEIVERS; i++) Detach(receiver[i]); - // We're not explicitly closing any device files here, since this sometimes - // caused segfaults. Besides, the program is about to terminate anyway... } void cDevice::SetUseDevice(int n) @@ -134,15 +71,44 @@ void cDevice::SetUseDevice(int n) useDevice |= (1 << n); } +int cDevice::NextCardIndex(int n) +{ + if (n > 0) { + nextCardIndex += n; + if (nextCardIndex >= MAXDEVICES) + esyslog("ERROR: nextCardIndex too big (%d)", nextCardIndex); + } + else if (n < 0) + esyslog("ERROR: illegal value in IncCardIndex(%d)", n); + return nextCardIndex; +} + +void cDevice::MakePrimaryDevice(bool On) +{ +} + bool cDevice::SetPrimaryDevice(int n) { n--; if (0 <= n && n < numDevices && device[n]) { isyslog("setting primary device to %d", n + 1); + if (primaryDevice) + primaryDevice->MakePrimaryDevice(false); primaryDevice = device[n]; + primaryDevice->MakePrimaryDevice(true); return true; } - esyslog("invalid devive number: %d", n + 1); + esyslog("invalid device number: %d", n + 1); + return false; +} + +bool cDevice::CanBeReUsed(int Frequency, int Vpid) +{ + return false; +} + +bool cDevice::HasDecoder(void) const +{ return false; } @@ -156,20 +122,7 @@ cDevice *cDevice::GetDevice(int Ca, int Priority, int Frequency, int Vpid, bool for (int i = 0; i < numDevices; i++) { if ((Provides[i] = device[i]->ProvidesCa(Ca)) != 0) { // this device is basicly able to do the job //XXX+ dsyslog("GetDevice: %d %d %d %5d %5d", i, device[i]->HasDecoder(), device[i]->Receiving(), Frequency, device[i]->frequency);//XXX - if ( (!device[i]->HasDecoder() // it's a "budget card" which can receive multiple channels... - && device[i]->frequency == Frequency // ...and it is tuned to the requested frequency... - && device[i]->Receiving() // ...and is already receiving - // need not check priority - if a budget card is already receiving on the requested - // frequency, we can attach another receiver regardless of priority - ) - || (device[i]->HasDecoder() // it's a "full featured card" which can receive only one channel... - && device[i]->frequency == Frequency // ...and it is tuned to the requested frequency... - && device[i]->pidHandles[ptVideo].pid == Vpid // ...and the requested video PID... - && device[i]->Receiving() // ...and is already receiving - // need not check priority - if a full featured card is already receiving the requested - // frequency and video PID, we can attach another receiver regardless of priority - ) - ) { + if (device[i]->CanBeReUsed(Frequency, Vpid)) { d = device[i]; if (ReUse) *ReUse = true; @@ -204,50 +157,14 @@ cDevice *cDevice::GetDevice(int Ca, int Priority, int Frequency, int Vpid, bool return d; } -void cDevice::SetCaCaps(void) +void cDevice::SetCaCaps(int Index) { for (int d = 0; d < numDevices; d++) { - for (int i = 0; i < MAXCACAPS; i++) - device[d]->caCaps[i] = Setup.CaCaps[device[d]->CardIndex()][i]; - } -} - -bool cDevice::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 cDevice::Initialize(void) -{ - numDevices = 0; - for (int i = 0; i < MAXDEVICES; i++) { - if (useDevice == 0 || (useDevice & (1 << i)) != 0) { - if (Probe(OstName(DEV_OST_FRONTEND, i))) - device[numDevices++] = new cDevice(i); - else - break; + if (Index < 0 || Index == device[d]->CardIndex()) { + for (int i = 0; i < MAXCACAPS; i++) + device[d]->caCaps[i] = Setup.CaCaps[device[d]->CardIndex()][i]; } } - primaryDevice = device[0]; - if (numDevices > 0) { - isyslog("found %d video device%s", numDevices, numDevices > 1 ? "s" : ""); - SetCaCaps(); - } - else - esyslog("ERROR: no video device found, giving up!"); - return numDevices > 0; } void cDevice::Shutdown(void) @@ -261,106 +178,13 @@ void cDevice::Shutdown(void) bool cDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY) { - int videoDev = OstOpen(DEV_VIDEO, CardIndex(), O_RDWR, true); - 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 cDevice::SetVideoFormat(videoFormat_t Format) +void cDevice::SetVideoFormat(bool VideoFormat16_9) { - if (HasDecoder()) - CHECK(ioctl(fd_video, VIDEO_SET_FORMAT, Format)); } -// ptVideo ptAudio ptTeletext ptDolby ptOther -dmxPesType_t PesTypes[] = { DMX_PES_VIDEO, DMX_PES_AUDIO, DMX_PES_TELETEXT, DMX_PES_OTHER, DMX_PES_OTHER }; - //#define PRINTPIDS(s) { char b[500]; char *q = b; q += sprintf(q, "%d %s ", CardIndex(), s); for (int i = 0; i < MAXPIDHANDLES; i++) q += sprintf(q, " %s%4d %d", i == ptOther ? "* " : "", pidHandles[i].pid, pidHandles[i].used); dsyslog(b); } //XXX+ #define PRINTPIDS(s) @@ -375,13 +199,12 @@ bool cDevice::AddPid(int Pid, ePidType PidType) else if (a < 0 && i >= ptOther && !pidHandles[i].used) a = i; } - dmxPesType_t PesType = PesTypes[ptOther]; if (n >= 0) { // The Pid is already in use if (++pidHandles[n].used == 2 && n <= ptTeletext) { - // It's a special PID that has to be switched into "tap" mode + // It's a special PID that may have to be switched into "tap" mode PRINTPIDS("A");//XXX+ - return SetPid(pidHandles[n].fd, PesTypes[n], Pid, DMX_OUT_TS_TAP); + return SetPid(&pidHandles[n], n, true); } PRINTPIDS("a");//XXX+ return true; @@ -389,8 +212,6 @@ bool cDevice::AddPid(int Pid, ePidType PidType) else if (PidType < ptOther) { // The Pid is not yet in use and it is a special one n = PidType; - PesType = PesTypes[PidType]; - PRINTPIDS("B");//XXX+ } else if (a >= 0) { // The Pid is not yet in use and we have a free slot @@ -400,217 +221,51 @@ bool cDevice::AddPid(int Pid, ePidType PidType) esyslog("ERROR: no free slot for PID %d", Pid); if (n >= 0) { pidHandles[n].pid = Pid; - pidHandles[n].fd = OstOpen(DEV_OST_DEMUX, CardIndex(), O_RDWR | O_NONBLOCK, true); pidHandles[n].used = 1; PRINTPIDS("C");//XXX+ - return SetPid(pidHandles[n].fd, PesType, Pid, PidType <= ptTeletext ? DMX_OUT_DECODER : DMX_OUT_TS_TAP); + return SetPid(&pidHandles[n], n, true); } } return true; } -bool cDevice::DelPid(int Pid) +void cDevice::DelPid(int Pid) { if (Pid) { for (int i = 0; i < MAXPIDHANDLES; i++) { if (pidHandles[i].pid == Pid) { - switch (--pidHandles[i].used) { - case 0: CHECK(ioctl(pidHandles[i].fd, DMX_STOP));//XXX+ is this necessary??? - close(pidHandles[i].fd); - pidHandles[i].fd = -1; - pidHandles[i].pid = 0; - break; - case 1: if (i <= ptTeletext) - SetPid(pidHandles[i].fd, PesTypes[i], Pid, DMX_OUT_DECODER); - break; - } + if (--pidHandles[i].used < 2) { + SetPid(&pidHandles[i], i, false); + if (pidHandles[i].used == 0) { + pidHandles[i].handle = -1; + pidHandles[i].pid = 0; + } + } PRINTPIDS("D");//XXX+ - return pidHandles[i].used; } } } - return false; } -bool cDevice::SetPid(int fd, dmxPesType_t PesType, int Pid, dmxOutput_t Output) +bool cDevice::SetPid(cPidHandle *Handle, int Type, bool On) { - if (Pid) { - CHECK(ioctl(fd, DMX_STOP)); - if (Pid != 0x1FFF) { - dmxPesFilterParams pesFilterParams; - pesFilterParams.pid = Pid; - pesFilterParams.input = DMX_IN_FRONTEND; - pesFilterParams.output = Output; - pesFilterParams.pesType = PesType; - pesFilterParams.flags = DMX_IMMEDIATE_START; - //XXX+ pesFilterParams.flags = DMX_CHECK_CRC;//XXX - if (ioctl(fd, DMX_SET_PES_FILTER, &pesFilterParams) < 0) { - LOG_ERROR; - return false; - } - //XXX+ CHECK(ioctl(fd, DMX_SET_BUFFER_SIZE, KILOBYTE(32)));//XXX - //XXX+ CHECK(ioctl(fd, DMX_START));//XXX - } - } - return true; + return false; } -eSetChannelResult cDevice::SetChannel(int ChannelNumber, int Frequency, char Polarization, int Diseqc, int Srate, int Vpid, int Apid, int Tpid, int Ca, int Pnr) +eSetChannelResult cDevice::SetChannel(const cChannel *Channel) { - StopReplay(); - cStatus::MsgChannelSwitch(this, 0); + StopReplay(); + // Must set this anyway to avoid getting stuck when switching through // channels with 'Up' and 'Down' keys: - currentChannel = ChannelNumber; - - // Avoid noise while switching: - - if (HasDecoder()) { - 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)); - } - - // Stop setting system time: - - if (siProcessor) - siProcessor->SetCurrentTransponder(0); + currentChannel = Channel->number; // If this card can't receive this channel, we must not actually switch // the channel here, because that would irritate the driver when we // start replaying in Transfer Mode immediately after switching the channel: - bool NeedsTransferMode = (IsPrimaryDevice() && !ProvidesCa(Ca)); - - if (!NeedsTransferMode) { - - // Turn off current PIDs: - - if (HasDecoder()) { - DelPid(pidHandles[ptVideo].pid); - DelPid(pidHandles[ptAudio].pid); - DelPid(pidHandles[ptTeletext].pid); - DelPid(pidHandles[ptDolby].pid); - } - - FrontendParameters Frontend; - - switch (frontendType) { - case FE_QPSK: { // DVB-S - - // Frequency offsets: - - unsigned int freq = Frequency; - int tone = SEC_TONE_OFF; - - if (freq < (unsigned int)Setup.LnbSLOF) { - freq -= Setup.LnbFrequLo; - tone = SEC_TONE_OFF; - } - else { - freq -= Setup.LnbFrequHi; - tone = SEC_TONE_ON; - } - - Frontend.Frequency = freq * 1000UL; - Frontend.Inversion = INVERSION_AUTO; - Frontend.u.qpsk.SymbolRate = Srate * 1000UL; - Frontend.u.qpsk.FEC_inner = FEC_AUTO; - - int volt = (Polarization == 'v' || Polarization == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18; - - // DiseqC: - - secCommand scmd; - scmd.type = 0; - scmd.u.diseqc.addr = 0x10; - scmd.u.diseqc.cmd = 0x38; - scmd.u.diseqc.numParams = 1; - scmd.u.diseqc.params[0] = 0xF0 | ((Diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0); - - secCmdSequence scmds; - scmds.voltage = volt; - scmds.miniCommand = SEC_MINI_NONE; - scmds.continuousTone = tone; - scmds.numCommands = Setup.DiSEqC ? 1 : 0; - scmds.commands = &scmd; - - CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds)); - } - break; - case FE_QAM: { // DVB-C - - // Frequency and symbol rate: - - Frontend.Frequency = Frequency * 1000000UL; - Frontend.Inversion = INVERSION_AUTO; - Frontend.u.qam.SymbolRate = Srate * 1000UL; - Frontend.u.qam.FEC_inner = FEC_AUTO; - Frontend.u.qam.QAM = QAM_64; - } - break; - case FE_OFDM: { // DVB-T - - // Frequency and OFDM paramaters: - - Frontend.Frequency = Frequency * 1000UL; - Frontend.Inversion = INVERSION_AUTO; - Frontend.u.ofdm.bandWidth=BANDWIDTH_8_MHZ; - Frontend.u.ofdm.HP_CodeRate=FEC_2_3; - Frontend.u.ofdm.LP_CodeRate=FEC_1_2; - Frontend.u.ofdm.Constellation=QAM_64; - Frontend.u.ofdm.TransmissionMode=TRANSMISSION_MODE_2K; - Frontend.u.ofdm.guardInterval=GUARD_INTERVAL_1_32; - Frontend.u.ofdm.HierarchyInformation=HIERARCHY_NONE; - } - break; - default: - esyslog("ERROR: attempt to set channel with unknown DVB frontend type"); - return scrFailed; - } - - // Tuning: - - CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend)); - - // Wait for channel sync: - - if (cFile::FileReady(fd_frontend, 5000)) { - FrontendEvent event; - int res = ioctl(fd_frontend, FE_GET_EVENT, &event); - if (res >= 0) { - if (event.type != FE_COMPLETION_EV) { - esyslog("ERROR: channel %d not sync'ed on DVB card %d!", ChannelNumber, CardIndex() + 1); - if (IsPrimaryDevice()) - cThread::RaisePanic(); - return scrFailed; - } - } - else - esyslog("ERROR %d in frontend get event (channel %d, card %d)", res, ChannelNumber, CardIndex() + 1); - } - else - esyslog("ERROR: timeout while tuning"); - - frequency = Frequency; - - // PID settings: - - if (HasDecoder()) { - if (!(AddPid(Vpid, ptVideo) && AddPid(Apid, ptAudio))) {//XXX+ dolby Dpid1!!! (if audio plugins are attached) - esyslog("ERROR: failed to set PIDs for channel %d", ChannelNumber); - return scrFailed; - } - if (IsPrimaryDevice()) - AddPid(Tpid, ptTeletext); - CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true)); - } - } - - if (IsPrimaryDevice() && siProcessor) - siProcessor->SetCurrentServiceID(Pnr); + bool NeedsTransferMode = (IsPrimaryDevice() && !ProvidesCa(Channel->ca)); eSetChannelResult Result = scrOk; @@ -618,28 +273,30 @@ eSetChannelResult cDevice::SetChannel(int ChannelNumber, int Frequency, char Pol // use the card that actually can receive it and transfer data from there: if (NeedsTransferMode) { - cDevice *CaDevice = GetDevice(Ca, 0); - if (CaDevice && !CaDevice->Receiving()) { - if ((Result = CaDevice->SetChannel(ChannelNumber, Frequency, Polarization, Diseqc, Srate, Vpid, Apid, Tpid, Ca, Pnr)) == scrOk) - cControl::Launch(new cTransferControl(CaDevice, Vpid, Apid, 0, 0, 0));//XXX+ - } + cDevice *CaDevice = GetDevice(Channel->ca, 0); + if (CaDevice && !CaDevice->Receiving() && CaDevice->SetChannel(Channel) == scrOk) + cControl::Launch(new cTransferControl(CaDevice, Channel->vpid, Channel->apid1, 0, 0, 0));//XXX+ else Result = scrNoTransfer; } + else if (!SetChannelDevice(Channel)) + Result = scrFailed; - if (HasDecoder()) { - CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false)); - CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false)); - } + if (IsPrimaryDevice()) + cSIProcessor::SetCurrentServiceID(Channel->pnr); - // Start setting system time: + cStatus::MsgChannelSwitch(this, Channel->number); - if (Result == scrOk && siProcessor) - siProcessor->SetCurrentTransponder(Frequency); + return Result; +} - cStatus::MsgChannelSwitch(this, ChannelNumber); +bool cDevice::SetChannelDevice(const cChannel *Channel) +{ + return false; +} - return Result; +void cDevice::SetVolumeDevice(int Volume) +{ } bool cDevice::ToggleMute(void) @@ -653,78 +310,40 @@ bool cDevice::ToggleMute(void) void cDevice::SetVolume(int Volume, bool Absolute) { - if (HasDecoder()) { - volume = min(max(Absolute ? Volume : volume + Volume, 0), MAXVOLUME); - audioMixer_t am; - am.volume_left = am.volume_right = volume; - CHECK(ioctl(fd_audio, AUDIO_SET_MIXER, &am)); - cStatus::MsgSetVolume(volume, Absolute); - if (volume > 0) - mute = false; - } + volume = min(max(Absolute ? Volume : volume + Volume, 0), MAXVOLUME); + SetVolumeDevice(volume); + cStatus::MsgSetVolume(volume, Absolute); + if (volume > 0) + mute = false; +} + +int cDevice::SetPlayMode(bool On) +{ + return -1; } void cDevice::TrickSpeed(int Speed) { - if (fd_video >= 0) - CHECK(ioctl(fd_video, VIDEO_SLOWMOTION, Speed)); } void cDevice::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 cDevice::Play(void) { - if (fd_audio >= 0) - CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true)); - if (fd_video >= 0) - CHECK(ioctl(fd_video, VIDEO_CONTINUE)); } void cDevice::Freeze(void) { - if (fd_audio >= 0) - CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false)); - if (fd_video >= 0) - CHECK(ioctl(fd_video, VIDEO_FREEZE)); } void cDevice::Mute(void) { - if (fd_audio >= 0) { - CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false)); - CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true)); - } } void cDevice::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 cDevice::Replaying(void) @@ -741,19 +360,9 @@ bool cDevice::AttachPlayer(cPlayer *Player) if (HasDecoder()) { if (player) Detach(player); - - 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, true)); - CHECK(ioctl(fd_audio, AUDIO_PLAY)); - CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY)); - CHECK(ioctl(fd_video, VIDEO_PLAY)); - player = Player; player->device = this; - player->deviceFileHandle = fd_video; + player->deviceFileHandle = SetPlayMode(true); player->Activate(true); return true; } @@ -767,17 +376,7 @@ void cDevice::Detach(cPlayer *Player) player->deviceFileHandle = -1; player->device = NULL; player = NULL; - - 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); + SetPlayMode(false); } } @@ -802,14 +401,11 @@ void cDevice::StopReplay(void) int cDevice::PlayVideo(const uchar *Data, int Length) { - if (fd_video >= 0) - return write(fd_video, Data, Length); return -1; } int cDevice::PlayAudio(const uchar *Data, int Length) { - //XXX+ return -1; } @@ -828,7 +424,7 @@ int cDevice::Priority(void) int cDevice::CanShift(int Ca, int Priority, int UsedCards) { return -1;//XXX+ too complex with multiple recordings per device - // Test whether a receiving on this DVB device can be shifted to another one + // Test whether a receiver on this device can be shifted to another one // in order to perform a new receiving with the given Ca and Priority on this device: int ShiftLevel = -1; // default means this device can't be shifted if (UsedCards & (1 << CardIndex()) != 0) @@ -892,51 +488,31 @@ void cDevice::Action(void) { dsyslog("receiver thread started on device %d (pid=%d)", CardIndex() + 1, getpid()); - int fd_dvr = open(dvrFileName, O_RDONLY | O_NONBLOCK); - if (fd_dvr >= 0) { - pollfd pfd; - pfd.fd = fd_dvr; - pfd.events = pfd.revents = POLLIN; + if (OpenDvr()) { uchar b[TS_SIZE]; time_t t = time(NULL); active = true; for (; active;) { - // Read data from the DVR device: - - if (pfd.revents & POLLIN != 0) { - int r = read(fd_dvr, b, sizeof(b)); - if (r == TS_SIZE) { - if (*b == TS_SYNC_BYTE) { - // We're locked on to a TS packet - int Pid = (((uint16_t)b[1] & PID_MASK_HI) << 8) | b[2]; - // Distribute the packet to all attached receivers: - Lock(); - for (int i = 0; i < MAXRECEIVERS; i++) { - if (receiver[i] && receiver[i]->WantsPid(Pid)) - receiver[i]->Receive(b, TS_SIZE); - } - Unlock(); - } - t = time(NULL); - } - else if (r > 0) - esyslog("ERROR: got incomplete TS packet (%d bytes)", r);//XXX+ TODO do we have to read the rest??? - else if (r < 0) { - 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; - break; - } - } + int r = GetTSPacket(b); + if (r == TS_SIZE) { + if (*b == TS_SYNC_BYTE) { + // We're locked on to a TS packet + int Pid = (((uint16_t)b[1] & PID_MASK_HI) << 8) | b[2]; + // Distribute the packet to all attached receivers: + Lock(); + for (int i = 0; i < MAXRECEIVERS; i++) { + if (receiver[i] && receiver[i]->WantsPid(Pid)) + receiver[i]->Receive(b, TS_SIZE); + } + Unlock(); } + t = time(NULL); } - - // Wait for more data to become available: - - poll(&pfd, 1, 100); + else if (r > 0) + esyslog("ERROR: got incomplete TS packet (%d bytes) on device %d", r, CardIndex() + 1);//XXX+ TODO do we have to read the rest??? + else if (r < 0) + break; //XXX+ put this into the recorder??? or give the receiver a flag whether it wants this? if (time(NULL) - t > MAXBROKENTIMEOUT) { @@ -945,14 +521,26 @@ void cDevice::Action(void) t = time(NULL); } } - close(fd_dvr); + CloseDvr(); } - else - LOG_ERROR_STR(dvrFileName); dsyslog("receiver thread ended on device %d (pid=%d)", CardIndex() + 1, getpid()); } +bool cDevice::OpenDvr(void) +{ + return false; +} + +void cDevice::CloseDvr(void) +{ +} + +int cDevice::GetTSPacket(uchar *Data) +{ + return -1; +} + bool cDevice::AttachReceiver(cReceiver *Receiver) { //XXX+ check for same transponder??? @@ -963,7 +551,6 @@ bool cDevice::AttachReceiver(cReceiver *Receiver) StopReplay(); for (int i = 0; i < MAXRECEIVERS; i++) { if (!receiver[i]) { - //siProcessor->SetStatus(false);//XXX+ for (int n = 0; n < MAXRECEIVEPIDS; n++) AddPid(Receiver->pids[n]);//XXX+ retval! Receiver->Activate(true); @@ -4,38 +4,33 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.h 1.4 2002/07/28 10:48:12 kls Exp $ + * $Id: device.h 1.5 2002/08/04 14:02:19 kls Exp $ */ #ifndef __DEVICE_H #define __DEVICE_H -#include <stdlib.h> // FIXME: this is apparently necessary for the ost/... header files - // FIXME: shouldn't every header file include ALL the other header - // FIXME: files it depends on? The sequence in which header files - // FIXME: are included here should not matter - and it should NOT - // FIXME: be necessary to include <stdlib.h> here! -#include <ost/dmx.h> -#include <ost/frontend.h> -#include <ost/audio.h> -#include <ost/video.h> -#include "eit.h" #include "thread.h" +#include "tools.h" -enum eSetChannelResult { scrOk, scrNoTransfer, scrFailed }; - -#define MAXDEVICES 4 // the maximum number of devices in the system -#define MAXCACAPS 16 // the maximum number of different CA values per DVB device -#define MAXPIDHANDLES 16 // the maximum number of different PIDs per DVB device -#define MAXRECEIVERS 16 // the maximum number of receivers per DVB device +#define MAXDEVICES 16 // the maximum number of devices in the system +#define MAXCACAPS 16 // the maximum number of different CA values per device +#define MAXPIDHANDLES 16 // the maximum number of different PIDs per device +#define MAXRECEIVERS 16 // the maximum number of receivers per device #define MAXVOLUME 255 #define VOLUMEDELTA 5 // used to increase/decrease the volume +#define TS_SIZE 188 +#define TS_SYNC_BYTE 0x47 +#define PID_MASK_HI 0x1F + +enum eSetChannelResult { scrOk, scrNoTransfer, scrFailed }; + +class cChannel; class cPlayer; class cReceiver; class cDevice : cThread { - friend class cOsd;//XXX private: static int numDevices; static int useDevice; @@ -43,106 +38,142 @@ private: static cDevice *primaryDevice; public: static int NumDevices(void) { return numDevices; } - // Returns the total number of DVB devices. + // Returns the total number of devices. static void SetUseDevice(int n); - // Sets the 'useDevice' flag of the given DVB device. - // If this function is not called before Initialize(), all DVB devices + // Sets the 'useDevice' flag of the given device. + // If this function is not called before initializing, all devices // will be used. + static bool UseDevice(int n) { return useDevice == 0 || (useDevice & (1 << n)) != 0; } + // Tells whether the device with the given card index shall be used in + // this instance of VDR. static bool SetPrimaryDevice(int n); - // Sets the primary DVB device to 'n' (which must be in the range + // Sets the primary device to 'n' (which must be in the range // 1...numDevices) and returns true if this was possible. static cDevice *PrimaryDevice(void) { return primaryDevice; } - // Returns the primary DVB device. + // Returns the primary device. static cDevice *GetDevice(int Ca, int Priority, int Frequency = 0, int Vpid = 0, bool *ReUse = NULL); - // Selects a free DVB device, avoiding the primaryDevice if possible. + // Selects a free device, avoiding the primaryDevice if possible. // If Ca is not 0, the device with the given number will be returned // in case Ca is <= MAXDEVICES, or the device that provides the given // value in its caCaps. - // If there is a device that is already tuned to the given Frequency, - // and that device is able to receive multiple channels ("budget" cards), - // that device will be returned. Else if a ("full featured") device is - // tuned to Frequency and Vpid, that one will be returned. - // If all DVB devices are currently receiving, the one receiving the - // lowest priority timer (if any) that is lower than the given Priority + // If there is a device that is already receiving and can be re-used to + // receive another data stream, that device will be returned. + // If all devices are currently receiving, the one receiving with the + // lowest priority (if any) that is lower than the given Priority // will be returned. // If ReUse is given, the caller will be informed whether the device can be re-used // for a new recording. If ReUse returns 'true', the caller must NOT switch the channel // (the device is already properly tuned). Otherwise the caller MUST switch the channel. - static void SetCaCaps(void); - // Sets the CaCaps of all DVB devices according to the Setup data. - static bool Probe(const char *FileName); - // Probes for existing DVB devices. - static bool Initialize(void); - // Initializes the DVB devices. - // Must be called before accessing any DVB functions. + static void SetCaCaps(int Index = -1); + // Sets the CaCaps of the given device according to the Setup data. + // By default the CaCaps of all devices are set. static void Shutdown(void); - // Closes down all DVB devices. + // Closes down all devices. // Must be called at the end of the program. private: + static int nextCardIndex; int cardIndex; int caCaps[MAXCACAPS]; - FrontendType frontendType; - char *dvrFileName; - bool active; - int fd_osd, fd_frontend, fd_sec, fd_audio, fd_video; - int OsdDeviceHandle(void) { return fd_osd; } -public: - cDevice(int n); +protected: + cDevice(void); virtual ~cDevice(); + static int NextCardIndex(int n = 0); + // Each device in a given machine must have a unique card index, which + // will be used to identify the device for assigning Ca parameters and + // deciding whether to actually use that device in this particular + // instance of VDR. Every time a new cDevice is created, it will be + // given the current nextCardIndex, and then nextCardIndex will be + // automatically incremented by 1. A derived class can determine whether + // a given device shall be used by checking UseDevice(NextCardIndex()). + // If a device is skipped, or if there are possible device indexes left + // after a derived class has set up all its devices, NextCardIndex(n) + // must be called, where n is the number of card indexes to skip. + virtual void MakePrimaryDevice(bool On); + // Informs a device that it will be the primary device. If there is + // anything the device needs to set up when it becomes the primary + // device (On = true) or to shut down when it no longer is the primary + // device (On = false), it should do so in this function. +public: bool IsPrimaryDevice(void) const { return this == primaryDevice; } int CardIndex(void) const { return cardIndex; } // Returns the card index of this device (0 ... MAXDEVICES - 1). int ProvidesCa(int Ca); - // Checks whether this DVB device provides the given value in its + // Checks whether this device provides the given value in its // caCaps. Returns 0 if the value is not provided, 1 if only this // value is provided, and > 1 if this and other values are provided. - // If the given value is equal to the number of this DVB device, + // If the given value is equal to the number of this device, // 1 is returned. If it is 0 (FTA), 1 plus the number of other values // in caCaps is returned. - bool HasDecoder(void) const { return fd_video >= 0 && fd_audio >= 0; } + virtual bool CanBeReUsed(int Frequency, int Vpid);//XXX TODO make it more abstract + // Tells whether this device is already receiving and allows another + // receiver with the given settings to be attached to it. + virtual bool HasDecoder(void) const; + // Tells whether this device has an MPEG decoder. // Channel facilities -private: +protected: int currentChannel; - int frequency; public: - eSetChannelResult SetChannel(int ChannelNumber, int Frequency, char Polarization, int Diseqc, int Srate, int Vpid, int Apid, int Tpid, int Ca, int Pnr); + eSetChannelResult SetChannel(const cChannel *Channel); + // Sets the device to the given channel (general setup). + virtual bool SetChannelDevice(const cChannel *Channel); + // Sets the device to the given channel (actual physical setup). static int CurrentChannel(void) { return primaryDevice ? primaryDevice->currentChannel : 0; } + // Returns the number of the current channel on the primary device. int Channel(void) { return currentChannel; } + // Returns the number of the current channel on this device. // PID handle facilities private: + bool active; + virtual void Action(void); +protected: enum ePidType { ptVideo, ptAudio, ptTeletext, ptDolby, ptOther }; class cPidHandle { public: int pid; - int fd; + int handle; int used; - cPidHandle(void) { pid = used = 0; fd = -1; } + cPidHandle(void) { pid = used = 0; handle = -1; } }; cPidHandle pidHandles[MAXPIDHANDLES]; bool AddPid(int Pid, ePidType PidType = ptOther); - bool DelPid(int Pid); - bool SetPid(int fd, dmxPesType_t PesType, int Pid, dmxOutput_t Output); - virtual void Action(void); + // Adds a PID to the set of PIDs this device shall receive. + void DelPid(int Pid); + // Deletes a PID from the set of PIDs this device shall receive. + virtual bool SetPid(cPidHandle *Handle, int Type, bool On); + // Does the actual PID setting on this device. + // On indicates whether the PID shall be added or deleted. + // Handle->handle can be used by the device to store information it + // needs to receive this PID (for instance a file handle). + // Handle->used indicated how many receivers are using this PID. + // Type indicates some special types of PIDs, which the device may + // need to set in a specific way. // Image Grab facilities public: - bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); + virtual bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); + // Grabs the currently visible screen image into the given file, with the + // given parameters. // Video format facilities public: - virtual void SetVideoFormat(videoFormat_t Format); + virtual void SetVideoFormat(bool VideoFormat16_9); + // Sets the output video format to either 16:9 or 4:3 (only useful + // if this device has an MPEG decoder). // Volume facilities private: bool mute; int volume; +protected: + virtual void SetVolumeDevice(int Volume); + // Sets the audio volume on this device (Volume = 0...255). public: bool IsMute(void) { return mute; } bool ToggleMute(void); @@ -152,30 +183,46 @@ public: // the current volume. static int CurrentVolume(void) { return primaryDevice ? primaryDevice->volume : 0; }//XXX??? - // EIT facilities - -private: - cSIProcessor *siProcessor; - // Player facilities private: cPlayer *player; +protected: + virtual int SetPlayMode(bool On); + // Sets the device into play mode (On = true) or normal + // viewing mode (On = false). If On is true, it may return a file + // handle that a player can use to poll this device when replaying. + //XXX TODO should be implemented differently public: - void TrickSpeed(int Speed); - void Clear(void); - void Play(void); - void Freeze(void); - void Mute(void); - void StillPicture(const uchar *Data, int Length); + virtual void TrickSpeed(int Speed); + // Sets the device into a mode where replay is done slower. + // Every single frame shall then be displayed the given number of + // times. + virtual void Clear(void); + // Clears all video and audio data from the device. + virtual void Play(void); + // Sets the device into play mode (after a previous trick + // mode). + virtual void Freeze(void); + // Puts the device into "freeze frame" mode. + virtual void Mute(void); + // Turns off audio while replaying. + virtual void StillPicture(const uchar *Data, int Length); + // Displays the given I-frame as a still picture. + virtual int PlayVideo(const uchar *Data, int Length); + // Actually plays the given data block as video. The data must be + // part of a PES (Packetized Elementary Stream) which can contain + // one video and one audio strem. + virtual int PlayAudio(const uchar *Data, int Length); + // Plays additional audio streams, like Dolby Digital. bool Replaying(void); // Returns true if we are currently replaying. void StopReplay(void); // Stops the current replay session (if any). bool AttachPlayer(cPlayer *Player); + // Attaches the given player to this device. void Detach(cPlayer *Player); - virtual int PlayVideo(const uchar *Data, int Length); - virtual int PlayAudio(const uchar *Data, int Length); + // Detaches the given player from this device. // Receiver facilities @@ -184,16 +231,30 @@ private: int ca; int Priority(void); // Returns the priority of the current receiving session (0..MAXPRIORITY), - // or -1 if no receiver is currently active. The primary DVB device will + // or -1 if no receiver is currently active. The primary device will // always return at least Setup.PrimaryLimit-1. int CanShift(int Ca, int Priority, int UsedCards = 0); +protected: + virtual bool OpenDvr(void); + // Opens the DVR of this device and prepares it to deliver a Transport + // Stream for use in a cReceiver. + virtual void CloseDvr(void); + // Shuts down the DVR. + virtual int GetTSPacket(uchar *Data); + // Gets exactly one TS packet from the DVR of this device and copies it + // into the given memory area (which is exactly 188 bytes in size). + // Returns the number of bytes copied into Data (which must be 188). + // If there is currently no TS packet available, 0 should be returned. + // In case of a non recoverable error, returns -1. public: int Ca(void) { return ca; } // Returns the ca of the current receiving session. bool Receiving(void); // Returns true if we are currently receiving. bool AttachReceiver(cReceiver *Receiver); + // Attaches the given receiver to this device. void Detach(cReceiver *Receiver); + // Detaches the given receiver from this device. }; #endif //__DEVICE_H diff --git a/dvbdevice.c b/dvbdevice.c new file mode 100644 index 00000000..be765005 --- /dev/null +++ b/dvbdevice.c @@ -0,0 +1,615 @@ +/* + * 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.1 2002/08/04 12:24:25 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 <ost/audio.h> +#include <ost/sec.h> +#include <ost/video.h> +#include <poll.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include "dvbosd.h" +#include "player.h" +#include "receiver.h" +#include "status.h" +#include "transfer.h" + +#define MAXDVBDEVICES 4 + +#define DEV_VIDEO "/dev/video" +#define DEV_OST_OSD "/dev/ost/osd" +#define DEV_OST_FRONTEND "/dev/ost/frontend" +#define DEV_OST_SEC "/dev/ost/sec" +#define DEV_OST_DVR "/dev/ost/dvr" +#define DEV_OST_DEMUX "/dev/ost/demux" +#define DEV_OST_VIDEO "/dev/ost/video" +#define DEV_OST_AUDIO "/dev/ost/audio" + +static const char *OstName(const char *Name, int n) +{ + static char buffer[PATH_MAX]; + snprintf(buffer, sizeof(buffer), "%s%d", Name, n); + return buffer; +} + +static int OstOpen(const char *Name, int n, int Mode, bool ReportError = false) +{ + const char *FileName = OstName(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; + + // Devices that are present on all card types: + + fd_frontend = OstOpen(DEV_OST_FRONTEND, n, O_RDWR); + + // Devices that are only present on DVB-S cards: + + fd_sec = OstOpen(DEV_OST_SEC, n, O_RDWR); + + // Devices that are only present on cards with decoders: + + fd_osd = OstOpen(DEV_OST_OSD, n, O_RDWR); + fd_video = OstOpen(DEV_OST_VIDEO, n, O_RDWR | O_NONBLOCK); + fd_audio = OstOpen(DEV_OST_AUDIO, n, O_RDWR | O_NONBLOCK); + + // 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) { + siProcessor = new cSIProcessor(OstName(DEV_OST_DEMUX, n)); + FrontendInfo feinfo; + 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); + + frequency = 0; +} + +cDvbDevice::~cDvbDevice() +{ + 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(OstName(DEV_OST_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::CanBeReUsed(int Frequency, int Vpid) +{ + return Receiving() // to be reused the DVB device must already be receiving... + && frequency == Frequency // ...and tuned to the requested frequency... + && (!HasDecoder() // ...and either be a "budget card" which can receive multiple channels... + || pidHandles[ptVideo].pid == Vpid // ...or be a "full featured card" that's already tuned to the requested video PID + ); +} + +bool cDvbDevice::HasDecoder(void) const +{ + return fd_video >= 0 && fd_audio >= 0; +} + +bool cDvbDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY) +{ + int videoDev = OstOpen(DEV_VIDEO, CardIndex(), O_RDWR, true); + 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)); +} + +// ptVideo ptAudio ptTeletext ptDolby ptOther +dmxPesType_t PesTypes[] = { DMX_PES_VIDEO, DMX_PES_AUDIO, DMX_PES_TELETEXT, DMX_PES_OTHER, DMX_PES_OTHER }; + +bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On) +{ + if (Handle->pid) { + if (On) { + if (Handle->handle < 0) { + Handle->handle = OstOpen(DEV_OST_DEMUX, CardIndex(), O_RDWR | O_NONBLOCK, true); + if (Handle->handle < 0) + return false; + } + } + else { + CHECK(ioctl(Handle->handle, DMX_STOP)); + if (Handle->used == 0) { + close(Handle->handle); + Handle->handle = -1; + return true; + } + } + + if (Handle->pid != 0x1FFF) { + dmxPesFilterParams pesFilterParams; + 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; + //XXX+ pesFilterParams.flags = DMX_CHECK_CRC;//XXX + if (ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams) < 0) { + LOG_ERROR; + return false; + } + //XXX+ CHECK(ioctl(Handle->handle, DMX_SET_BUFFER_SIZE, KILOBYTE(32)));//XXX + //XXX+ CHECK(ioctl(Handle->handle, DMX_START));//XXX + } + } + return true; +} + +bool cDvbDevice::SetChannelDevice(const cChannel *Channel) +{ + // Avoid noise while switching: + + if (HasDecoder()) { + 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)); + } + + // Stop setting system time: + + if (siProcessor) + siProcessor->SetCurrentTransponder(0); + + // Turn off current PIDs: + + if (HasDecoder()) { + DelPid(pidHandles[ptVideo].pid); + DelPid(pidHandles[ptAudio].pid); + DelPid(pidHandles[ptTeletext].pid); + DelPid(pidHandles[ptDolby].pid); + } + + FrontendParameters Frontend; + + switch (frontendType) { + case FE_QPSK: { // DVB-S + + // Frequency offsets: + + unsigned int freq = Channel->frequency; + int tone = SEC_TONE_OFF; + + if (freq < (unsigned int)Setup.LnbSLOF) { + freq -= Setup.LnbFrequLo; + tone = SEC_TONE_OFF; + } + else { + freq -= Setup.LnbFrequHi; + tone = SEC_TONE_ON; + } + + Frontend.Frequency = freq * 1000UL; + Frontend.Inversion = INVERSION_AUTO; + Frontend.u.qpsk.SymbolRate = Channel->srate * 1000UL; + Frontend.u.qpsk.FEC_inner = FEC_AUTO; + + int volt = (Channel->polarization == 'v' || Channel->polarization == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18; + + // DiseqC: + + secCommand scmd; + scmd.type = 0; + scmd.u.diseqc.addr = 0x10; + scmd.u.diseqc.cmd = 0x38; + scmd.u.diseqc.numParams = 1; + scmd.u.diseqc.params[0] = 0xF0 | ((Channel->diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0); + + secCmdSequence scmds; + scmds.voltage = volt; + scmds.miniCommand = SEC_MINI_NONE; + scmds.continuousTone = tone; + scmds.numCommands = Setup.DiSEqC ? 1 : 0; + scmds.commands = &scmd; + + CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds)); + } + break; + case FE_QAM: { // DVB-C + + // Frequency and symbol rate: + + Frontend.Frequency = Channel->frequency * 1000000UL; + Frontend.Inversion = INVERSION_AUTO; + Frontend.u.qam.SymbolRate = Channel->srate * 1000UL; + Frontend.u.qam.FEC_inner = FEC_AUTO; + Frontend.u.qam.QAM = QAM_64; + } + break; + case FE_OFDM: { // DVB-T + + // Frequency and OFDM paramaters: + + Frontend.Frequency = Channel->frequency * 1000UL; + Frontend.Inversion = INVERSION_AUTO; + Frontend.u.ofdm.bandWidth=BANDWIDTH_8_MHZ; + Frontend.u.ofdm.HP_CodeRate=FEC_2_3; + Frontend.u.ofdm.LP_CodeRate=FEC_1_2; + Frontend.u.ofdm.Constellation=QAM_64; + Frontend.u.ofdm.TransmissionMode=TRANSMISSION_MODE_2K; + Frontend.u.ofdm.guardInterval=GUARD_INTERVAL_1_32; + Frontend.u.ofdm.HierarchyInformation=HIERARCHY_NONE; + } + break; + default: + esyslog("ERROR: attempt to set channel with unknown DVB frontend type"); + return false; + } + + // Tuning: + + CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend)); + + // Wait for channel sync: + + if (cFile::FileReady(fd_frontend, 5000)) { + FrontendEvent event; + int res = ioctl(fd_frontend, FE_GET_EVENT, &event); + if (res >= 0) { + if (event.type != FE_COMPLETION_EV) { + esyslog("ERROR: channel %d not sync'ed on DVB card %d!", Channel->number, CardIndex() + 1); + if (IsPrimaryDevice()) + cThread::RaisePanic(); + return false; + } + } + else + esyslog("ERROR %d in frontend get event (channel %d, card %d)", res, Channel->number, CardIndex() + 1); + } + else + esyslog("ERROR: timeout while tuning"); + + frequency = Channel->frequency; + + // PID settings: + + if (HasDecoder()) { + if (!(AddPid(Channel->vpid, ptVideo) && AddPid(Channel->apid1, ptAudio))) {//XXX+ dolby dpid1!!! (if audio plugins are attached) + esyslog("ERROR: failed to set PIDs for channel %d", Channel->number); + return false; + } + if (IsPrimaryDevice()) + AddPid(Channel->tpid, ptTeletext); + CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true)); + } + + if (HasDecoder()) { + CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false)); + CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false)); + } + + // 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)); + } +} + +int cDvbDevice::SetPlayMode(bool On) +{ + if (On) { + 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, true)); + CHECK(ioctl(fd_audio, AUDIO_PLAY)); + CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY)); + CHECK(ioctl(fd_video, VIDEO_PLAY)); + return fd_video; + } + else { + 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); + return -1; + } +} + +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 (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 (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 +} + +int cDvbDevice::PlayVideo(const uchar *Data, int Length) +{ + if (fd_video >= 0) + return write(fd_video, Data, Length); + return -1; +} + +int cDvbDevice::PlayAudio(const uchar *Data, int Length) +{ + //XXX+ + return -1; +} + +bool cDvbDevice::OpenDvr(void) +{ + CloseDvr(); + fd_dvr = OstOpen(DEV_OST_DVR, CardIndex(), O_RDONLY | O_NONBLOCK, true); + return fd_dvr >= 0; +} + +void cDvbDevice::CloseDvr(void) +{ + if (fd_dvr >= 0) { + close(fd_dvr); + fd_dvr = -1; + } +} + +int cDvbDevice::GetTSPacket(uchar *Data) +{ + if (fd_dvr >= 0) { + pollfd pfd; + pfd.fd = fd_dvr; + pfd.events = POLLIN; + + poll(&pfd, 1, 100); + + if (pfd.revents & POLLIN != 0) { + int r = read(fd_dvr, Data, TS_SIZE); + if (r >= 0) + return r; + 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 -1; + } + } + } + return 0; + } + else + return -1; +} diff --git a/dvbdevice.h b/dvbdevice.h new file mode 100644 index 00000000..25777f06 --- /dev/null +++ b/dvbdevice.h @@ -0,0 +1,97 @@ +/* + * dvbdevice.h: The DVB device interface + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: dvbdevice.h 1.1 2002/08/04 12:19:10 kls Exp $ + */ + +#ifndef __DVBDEVICE_H +#define __DVBDEVICE_H + +#include <stdlib.h> // FIXME: this is apparently necessary for the ost/... header files + // FIXME: shouldn't every header file include ALL the other header + // FIXME: files it depends on? The sequence in which header files + // FIXME: are included here should not matter - and it should NOT + // FIXME: be necessary to include <stdlib.h> here! +#include <ost/frontend.h> +#include "device.h" +#include "eit.h" + +class cDvbDevice : public cDevice { + friend class cDvbOsd; +private: + static bool Probe(const char *FileName); + // Probes for existing DVB devices. +public: + static bool Initialize(void); + // Initializes the DVB devices. + // Must be called before accessing any DVB functions. +private: + FrontendType frontendType; + int fd_osd, fd_frontend, fd_sec, fd_audio, fd_video, fd_dvr; + int OsdDeviceHandle(void) const { return fd_osd; } +protected: + virtual void MakePrimaryDevice(bool On); +public: + cDvbDevice(int n); + virtual ~cDvbDevice(); + virtual bool CanBeReUsed(int Frequency, int Vpid); + virtual bool HasDecoder(void) const; + +// Channel facilities + +private: + int frequency; +public: + virtual bool SetChannelDevice(const cChannel *Channel); + +// PID handle facilities + +protected: + virtual bool SetPid(cPidHandle *Handle, int Type, bool On); + +// Image Grab facilities + +public: + virtual bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); + +// Video format facilities + +public: + virtual void SetVideoFormat(bool VideoFormat16_9); + +// Volume facilities + +protected: + virtual void SetVolumeDevice(int Volume); + +// EIT facilities + +private: + cSIProcessor *siProcessor; + +// Player facilities + +protected: + virtual int SetPlayMode(bool On); +public: + virtual void TrickSpeed(int Speed); + virtual void Clear(void); + virtual void Play(void); + virtual void Freeze(void); + virtual void Mute(void); + virtual void StillPicture(const uchar *Data, int Length); + virtual int PlayVideo(const uchar *Data, int Length); + virtual int PlayAudio(const uchar *Data, int Length); + +// Receiver facilities + +protected: + virtual bool OpenDvr(void); + virtual void CloseDvr(void); + virtual int GetTSPacket(uchar *Data); + }; + +#endif //__DVBDEVICE_H @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbosd.c 1.17 2002/05/18 13:39:02 kls Exp $ + * $Id: dvbosd.c 1.18 2002/08/04 10:13:21 kls Exp $ */ #include "dvbosd.h" @@ -13,10 +13,12 @@ #include <sys/unistd.h> #include "tools.h" -cDvbOsd::cDvbOsd(int OsdDev, int x, int y) +const cDvbDevice *cDvbOsd::dvbDevice = NULL; + +cDvbOsd::cDvbOsd(int x, int y) :cOsdBase(x, y) { - osdDev = OsdDev; + osdDev = dvbDevice ? dvbDevice->OsdDeviceHandle() : -1; if (osdDev < 0) esyslog("ERROR: illegal OSD device handle (%d)!", osdDev); } @@ -27,6 +29,11 @@ cDvbOsd::~cDvbOsd() CloseWindow(GetWindowNr(i)); } +void cDvbOsd::SetDvbDevice(const cDvbDevice *DvbDevice) +{ + dvbDevice = DvbDevice; +} + bool cDvbOsd::SetWindow(cWindow *Window) { if (Window) { @@ -4,17 +4,19 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbosd.h 1.13 2002/05/18 13:38:09 kls Exp $ + * $Id: dvbosd.h 1.14 2002/08/04 10:12:14 kls Exp $ */ #ifndef __DVBOSD_H #define __DVBOSD_H #include <ost/osd.h> +#include "dvbdevice.h" #include "osdbase.h" class cDvbOsd : public cOsdBase { private: + static const cDvbDevice *dvbDevice; int osdDev; bool SetWindow(cWindow *Window); void Cmd(OSD_Command cmd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, const void *data = NULL); @@ -26,8 +28,9 @@ protected: virtual void MoveWindow(cWindow *Window, int x, int y); virtual void CloseWindow(cWindow *Window); public: - cDvbOsd(int OsdDev, int x, int y); + cDvbOsd(int x, int y); virtual ~cDvbOsd(); + static void SetDvbDevice(const cDvbDevice *DvbDevice); }; #endif //__DVBOSD_H @@ -16,7 +16,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: eit.h 1.16 2002/03/10 10:56:57 kls Exp $ + * $Id: eit.h 1.17 2002/08/04 11:30:24 kls Exp $ ***************************************************************************/ #ifndef __EIT_H @@ -158,7 +158,7 @@ public: static bool Read(FILE *f = NULL); void SetStatus(bool On); void SetCurrentTransponder(int CurrentTransponder); - bool SetCurrentServiceID(unsigned short servid); + static bool SetCurrentServiceID(unsigned short servid); }; #endif @@ -4,10 +4,11 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: eitscan.c 1.3 2002/06/22 13:02:40 kls Exp $ + * $Id: eitscan.c 1.4 2002/07/28 15:10:23 kls Exp $ */ #include "eitscan.h" +#include <stdlib.h> cEITScanner::cEITScanner(void) { @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 1.202 2002/07/14 10:55:37 kls Exp $ + * $Id: menu.c 1.203 2002/08/03 09:55:44 kls Exp $ */ #include "menu.h" @@ -1628,7 +1628,7 @@ eOSState cMenuSetupDVB::ProcessKey(eKeys Key) if (state == osBack && Key == kOk) { if (Setup.PrimaryDVB != oldPrimaryDVB) { state = osSwitchDvb; - cDevice::PrimaryDevice()->SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3); + cDevice::PrimaryDevice()->SetVideoFormat(Setup.VideoFormat); } } return state; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.c 1.31 2002/07/14 11:05:30 kls Exp $ + * $Id: osd.c 1.32 2002/08/04 10:11:26 kls Exp $ */ #include "osd.h" @@ -73,7 +73,7 @@ cOsdBase *cOsd::OpenRaw(int x, int y) #ifdef DEBUG_OSD return NULL; #else - return osd ? NULL : new cDvbOsd(cDevice::PrimaryDevice()->OsdDeviceHandle(), x, y); + return osd ? NULL : new cDvbOsd(x, y); #endif } @@ -4,12 +4,13 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: receiver.c 1.2 2002/07/28 10:48:42 kls Exp $ + * $Id: receiver.c 1.3 2002/07/28 15:14:49 kls Exp $ */ #include <stdarg.h> #include <stdio.h> #include "receiver.h" +#include "tools.h" cReceiver::cReceiver(int Ca, int Priority, int NumPids, ...) { @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remux.h 1.5 2001/06/23 14:06:59 kls Exp $ + * $Id: remux.h 1.6 2002/08/04 10:27:07 kls Exp $ */ #ifndef __REMUX_H @@ -12,6 +12,7 @@ #include <time.h> //XXX FIXME: DVB/ost/include/ost/dmx.h should include <time.h> itself!!! #include <ost/dmx.h> +#include "tools.h" // Picture types: #define NO_PICTURE 0 @@ -24,7 +25,6 @@ #define RESULTBUFFERSIZE (MINVIDEODATA * 4) -typedef unsigned char uchar; class cTS2PES; class cRemux { diff --git a/ringbuffer.h b/ringbuffer.h index 43176bde..660a08b0 100644 --- a/ringbuffer.h +++ b/ringbuffer.h @@ -4,15 +4,14 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: ringbuffer.h 1.6 2002/06/16 11:30:07 kls Exp $ + * $Id: ringbuffer.h 1.7 2002/08/04 10:27:30 kls Exp $ */ #ifndef __RINGBUFFER_H #define __RINGBUFFER_H #include "thread.h" - -typedef unsigned char uchar;//XXX+ +#include "tools.h" class cRingBuffer { private: @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.c 1.67 2002/05/18 15:10:45 kls Exp $ + * $Id: tools.c 1.68 2002/08/03 15:44:53 kls Exp $ */ #include "tools.h" @@ -407,7 +407,7 @@ bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis) char *ReadLink(const char *FileName) { - char RealName[_POSIX_PATH_MAX]; + char RealName[PATH_MAX]; const char *TargetName = NULL; int n = readlink(FileName, RealName, sizeof(RealName) - 1); if (n < 0) { @@ -22,7 +22,7 @@ * * The project's page is at http://www.cadsoft.de/people/kls/vdr * - * $Id: vdr.c 1.117 2002/06/23 11:23:34 kls Exp $ + * $Id: vdr.c 1.118 2002/08/04 09:56:30 kls Exp $ */ #include <getopt.h> @@ -33,6 +33,7 @@ #include "config.h" #include "cutter.h" #include "device.h" +#include "dvbdevice.h" #include "eitscan.h" #include "i18n.h" #include "interface.h" @@ -326,11 +327,9 @@ int main(int argc, char *argv[]) // DVB interfaces: - if (!cDevice::Initialize()) + if (!cDvbDevice::Initialize()) return 2; - cDevice::SetPrimaryDevice(Setup.PrimaryDVB); - cSIProcessor::Read(); // Start plugins: @@ -338,6 +337,10 @@ int main(int argc, char *argv[]) if (!PluginManager.StartPlugins()) return 2; + // Primary device: + + cDevice::SetPrimaryDevice(Setup.PrimaryDVB); + // OSD: cOsd::Initialize(); |