summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS3
-rw-r--r--HISTORY29
-rw-r--r--Makefile6
-rw-r--r--PLUGINS.html52
-rw-r--r--PLUGINS/SRC/status/Makefile4
-rw-r--r--channels.conf20
-rw-r--r--config.c32
-rw-r--r--config.h9
-rw-r--r--device.c270
-rw-r--r--device.h121
-rw-r--r--dvbdevice.c460
-rw-r--r--dvbdevice.h17
-rw-r--r--dvbspu.c505
-rw-r--r--dvbspu.h204
-rw-r--r--eitscan.c19
-rw-r--r--menu.c22
-rw-r--r--menuitems.c9
-rwxr-xr-xnewplugin6
-rw-r--r--osdbase.h6
-rw-r--r--recorder.c4
-rw-r--r--spu.c23
-rw-r--r--spu.h38
-rw-r--r--svdrp.c35
-rw-r--r--tools.h3
-rw-r--r--vdr.c10
-rw-r--r--xd1
26 files changed, 1480 insertions, 428 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index ca9a31a..1431bf7 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -145,6 +145,8 @@ Stefan Huelswitt <huels@iname.com>
option can't be accessed
for implementing several replay modes to allow players that play only audio
for improving cCondVar::Wait() and implementing cCondVar::TimedWait()
+ for reporting a bug when entering an integer value outside the limit
+ for adding play mode pmAudioOnlyBlack
Ulrich Röder <roeder@efr-net.de>
for pointing out that there are channels that have a symbol rate higher than
@@ -166,6 +168,7 @@ Andreas Schultz <aschultz@warp10.net>
for making the use of malloc/free and new/delete consistent
for adding cDevice::NewOsd() to allow a derived cDevice class to implement its own
OSD capabilities
+ for implementing an SPU decoder
Aaron Holtzman
for writing 'ac3dec'
diff --git a/HISTORY b/HISTORY
index fd17576..bfecec8 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1430,3 +1430,32 @@ Video Disk Recorder Revision History
- Fixed handling one-shot timers that were already recording and had their start
time changed into the future (thanks to Matthias Schniedermeyer for reporting
this one).
+
+2002-09-08: Version 1.1.9
+
+- Fixed the 'newplugin' script to make it name the target for creating the
+ distribution package 'dist', as stated in the PLUGINS.html documentation.
+ If you have already created a plugin source directory and Makefile you may
+ want to check it and replace the 'package' target with 'dist' if necessary.
+- Changed device handling for being able to do simultaneous recording and
+ replay on the same device (Time Shifting). In order for this to work you need
+ to use a driver with a firmware version that has this feature implemented.
+- cDevice::ProvidesCa() is no longer virtual. The new function
+ cDevice::ProvidesChannel() is now used to determine whether a device can
+ receive a given channel, and by default this function returns false. So a
+ device that is a pure replaying device doesn't need to do anything here.
+- Increased the recorder buffer size to 5MB in order to be able to better handle
+ multiple recordings.
+- Implemented cTSBuffer for better handling TS packet buffering in derived
+ cDevice classes.
+- Changed the interface if cDevice::GetTSPacket() to avoid unnecessary copying
+ of data.
+- Removed cDevice::Channel(), since this makes no more sense with devices
+ receiving multiple channels.
+- Switching through channels with the 'Up' and 'Down' keys now skips channels
+ that are currently not available (for instance because all devices are
+ recording and these channels are on different transponders).
+- Implemented an SPU decoder (thanks to Andreas Schultz).
+- Fixed a crash when entering an integer value outside the limits (thanks to
+ Stefan Huelswitt for reporting this one).
+- Added play mode pmAudioOnlyBlack (thanks to Stefan Huelswitt).
diff --git a/Makefile b/Makefile
index ddacbd4..7686b91 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
-# $Id: Makefile 1.45 2002/08/09 16:02:02 kls Exp $
+# $Id: Makefile 1.46 2002/09/08 14:00:48 kls Exp $
.DELETE_ON_ERROR:
@@ -32,9 +32,9 @@ endif
DTVLIB = $(DTVDIR)/libdtv.a
-OBJS = audio.o config.o cutter.o device.o dvbdevice.o dvbosd.o dvbplayer.o eit.o eitscan.o font.o i18n.o\
+OBJS = audio.o config.o cutter.o device.o dvbdevice.o dvbosd.o dvbplayer.o dvbspu.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\
+ recorder.o recording.o remote.o remux.o ringbuffer.o spu.o status.o svdrp.o thread.o\
tools.o transfer.o vdr.o videodir.o
OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
diff --git a/PLUGINS.html b/PLUGINS.html
index 186316d..02fd59a 100644
--- a/PLUGINS.html
+++ b/PLUGINS.html
@@ -21,18 +21,18 @@ VDR program and present itself to the user.
The <i>inside</i> interface provides the plugin code access to VDR's internal data
structures and allows it to hook itself into specific areas to perform special actions.
<p>
-<!--X1.1.5--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
-Important modifications introduced in version 1.1.5 are marked like this.
-<!--X1.1.5--></td></tr></table>
-<!--X1.1.6--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
+<!--X1.1.6--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.6 are marked like this.
<!--X1.1.6--></td></tr></table>
-<!--X1.1.7--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
+<!--X1.1.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.7 are marked like this.
<!--X1.1.7--></td></tr></table>
-<!--X1.1.8--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+<!--X1.1.8--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.8 are marked like this.
<!--X1.1.8--></td></tr></table>
+<!--X1.1.9--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+Important modifications introduced in version 1.1.9 are marked like this.
+<!--X1.1.9--></td></tr></table>
<a name="Part I - The Outside Interface"><hr><center><h1>Part I - The Outside Interface</h1></center>
@@ -352,20 +352,20 @@ these values inside the plugin. Here's an example:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
bool cPluginHello::ProcessArgs(int argc, char *argv[])
-{
+{
// Implement command line argument processing here if applicable.
static struct option long_options[] = {
{ "aaa", required_argument, NULL, 'a' },
{ "bbb", no_argument, NULL, 'b' },
{ NULL }
};
-
+
int c;
while ((c = getopt_long(argc, argv, "a:b", long_options, NULL)) != -1) {
switch (c) {
case 'a': option_a = optarg;
break;
- case 'b': option_b = true;
+ case 'b': option_b = true;
break;
default: return false;
}
@@ -609,7 +609,7 @@ public:
};
cMenuSetupHello::cMenuSetupHello(void)
-{
+{
newGreetingTime = GreetingTime;
newUseAlternateGreeting = UseAlternateGreeting;
Add(new cMenuEditIntItem( tr("Greeting time (s)"), &amp;newGreetingTime));
@@ -617,7 +617,7 @@ cMenuSetupHello::cMenuSetupHello(void)
}
void cMenuSetupHello::Store(void)
-{
+{
SetupStore("GreetingTime", GreetingTime = newGreetingTime);
SetupStore("UseAlternateGreeting", UseAlternateGreeting = newUseAlternateGreeting);
}
@@ -957,7 +957,7 @@ stream. There are no prerequisites regarding the length or alignment of an
individual block of data. The sum of all blocks must simply result in the
desired video data stream, and it must be delivered fast enough so that the
DVB device doesn't run out of data.
-<!--X1.1.7--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
+<!--X1.1.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
To avoid busy loops the player should call its member function
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
@@ -1034,7 +1034,7 @@ void DeviceStillPicture(const uchar *Data, int Length);
which can be called to display a still picture. VDR uses this function when handling
its editing marks. A special case of a "player" might use this function to implement
-a "picture viewer".
+a "picture viewer".
<p>
For detailed information on how to implement your own player, please take a look
at VDR's <tt>cDvbPlayer</tt> and <tt>cDvbPlayerControl</tt> classes.
@@ -1065,7 +1065,7 @@ enjoy additional players, since they will be able to control them with actions
that they already know. If you absolutely want to do things differently, just go
ahead - it's your show...
-<!--X1.1.6--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
+<!--X1.1.6--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<hr><h2>Receivers</h2>
<center><i><b>Tapping into the stream...</b></i></center><p>
@@ -1121,7 +1121,6 @@ If the <tt>cReceiver</tt> isn't needed any more, it may simply be <i>deleted</i>
and will automatically detach itself from the <tt>cDevice</tt>.
<!--X1.1.6--></td></tr></table>
-<!--X1.1.5--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<hr><h2>The On Screen Display</h2>
<center><i><b>Express yourself</b></i></center><p>
@@ -1151,9 +1150,8 @@ MyOsd-&gt;Create(...);
to define an actual OSD drawing area (see VDR/osdbase.h for the declarations
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=#00AA00>&nbsp;</td><td width=100%>
+<!--X1.1.6--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<hr><h2>Devices</h2>
<center><i><b>Expanding the possibilities</b></i></center><p>
@@ -1189,12 +1187,16 @@ the <tt>cDvbDevice</tt>, which is used to access the DVB PCI cards.
If the new device can receive, it most likely needs to provide a way of
selecting which channel it shall tune to:
+<!--X1.1.9--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
-virtual bool SetChannelDevice(const cChannel *Channel);
+virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
+virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
</pre></td></tr></table><p>
-This function will be called with the desired channel and shall return whether
-tuning to it was successful.
+These functions will be called with the desired channel and shall return whether
+this device can provide the requested channel and whether tuning to it was successful,
+repectively.
+<!--X1.1.9--></td></tr></table>
<p>
<b>Recording</b>
<p>
@@ -1204,10 +1206,12 @@ A device that can be used for recording must implement the functions
virtual bool SetPid(cPidHandle *Handle, int Type, bool On);
virtual bool OpenDvr(void);
virtual void CloseDvr(void);
-virtual int GetTSPacket(uchar *Data);
+<!--X1.1.9--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+virtual bool GetTSPacket(uchar *&amp;Data);
+<!--X1.1.9--></td></tr></table>
</pre></td></tr></table><p>
-which allow VDR to set the PIDs that shall be recorded, set up the device fro
+which allow VDR to set the PIDs that shall be recorded, set up the device for
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>
@@ -1226,7 +1230,7 @@ to indicate this to VDR.
<p>
The functions to implement replaying capabilites are
-<!--X1.1.7--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
+<!--X1.1.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual bool HasDecoder(void) const;
virtual bool SetPlayMode(ePlayMode PlayMode);
@@ -1250,7 +1254,7 @@ virtual void SetVideoFormat(bool VideoFormat16_9);
virtual void SetVolumeDevice(int Volume);
</pre></td></tr></table><p>
-<!--X1.1.8--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+<!--X1.1.8--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<p>
<b>On Screen Display</b>
<p>
diff --git a/PLUGINS/SRC/status/Makefile b/PLUGINS/SRC/status/Makefile
index 2d5a0e7..8cdb9fc 100644
--- a/PLUGINS/SRC/status/Makefile
+++ b/PLUGINS/SRC/status/Makefile
@@ -1,7 +1,7 @@
#
# Makefile for a Video Disk Recorder plugin
#
-# $Id: Makefile 1.1 2002/06/10 16:24:09 kls Exp $
+# $Id: Makefile 1.2 2002/08/28 19:30:35 kls Exp $
# The official name of this plugin.
# This name will be used in the '-P...' option of VDR to load the plugin.
@@ -67,7 +67,7 @@ libvdr-$(PLUGIN).so: $(OBJS)
$(CXX) $(CXXFLAGS) -shared $(OBJS) -o $@
@cp $@ $(LIBDIR)/$@.$(VDRVERSION)
-package: clean
+dist: clean
@-rm -rf $(TMPDIR)/$(ARCHIVE)
@mkdir $(TMPDIR)/$(ARCHIVE)
@cp -a * $(TMPDIR)/$(ARCHIVE)
diff --git a/channels.conf b/channels.conf
index ba58bc6..09cf433 100644
--- a/channels.conf
+++ b/channels.conf
@@ -7,7 +7,7 @@ BR3:11837:h:0:27500:201:202:204:0:28107
Hessen-3:11837:h:0:27500:301:302:304:0:28108
N3:12110:h:0:27500:2401:2402:2404:0:28224
SR3:11837:h:0:27500:501:502:504:0:28110
-WDR:11837:h:0:27500:601:602:604:0:28111
+WDR:11837:h:0:27500:601:602:0:0:28111
BR-alpha:11837:h:0:27500:701:702:704:0:28112
SWR BW:11837:h:0:27500:801:802:804:0:28113
Phoenix:11837:h:0:27500:901:902:904:0:28114
@@ -24,7 +24,7 @@ Super RTL:12188:h:0:27500:165:120:65:0:12040
VOX:12188:h:0:27500:167:136:0:0:12060
DW TV:10788:v:0:22000:305:306:0:0:8905
Kabel 1:12480:v:0:27500:511:512:33:0:899
-Neun Live:12480:v:0:27500:767:768:35:0:897
+Neun Live:12480:v:0:27500:767:768:0:0:897
DSF:12480:v:0:27500:1023:1024:0:0:900
HOT:12480:v:0:27500:1279:1280:0:0:40
Bloomberg TV Germany:12551:v:0:22000:162:99:0:0:12160
@@ -46,7 +46,7 @@ MDR:12110:h:0:27500:401:402:404:0:28204
NICK-PARAMOUNT:12246:v:0:27500:167:108:0:0:29312
ORB:12110:h:0:27500:501:502:504:0:28205
B1:12110:h:0:27500:601:602:604:0:28206
-ARD Online-Kanal:12722:h:0:22000:0:701:0:0:0
+ARD Online-Kanal:12722:h:0:22000:700:701:0:0:0
:Premiere World
Premiere Start:11797:h:0:27500:255:256:0:101:8
Premiere 1:11797:h:0:27500:511:512,513;515:0:101:10
@@ -78,7 +78,7 @@ Premiere Direkt 2B:11719:h:0:27500:767:768;769:0:101:181
Premiere Direkt 3A:11719:h:0:27500:511:512;515:0:101:180
Premiere Direkt 3B:11719:h:0:27500:1279:1280;1283:0:101:183
Premiere Direkt 4A:12031:h:0:27500:2815:2816:0:101:18
-:#Premiere Direkt 4B:12070:h:0:27500:1535:1536:0:101:216
+Premiere Direkt 4B:12070:h:0:27500:1535:1536:0:101:216
:PW Erotic
Beate-Uhse.TV:11758:h:0:27500:1023:1024:0:101:21
Premiere Erotik 1:12031:h:0:27500:1279:1280:0:101:513
@@ -89,11 +89,11 @@ Premiere Erotik 4:11719:h:0:27500:3583:3584:0:101:780
Premiere Sport 1:11720:h:0:27500:255:256,257:0:101:17
Premiere Sport 2:12031:h:0:27500:3839:3840:0:101:27
:Formel 1
-:#Supersignal:12070:h:0:27500:255:256:0:101:211
-:#Cockpitkanal:12070:h:0:27500:511:512:0:101:212
-:#Boxengasse:12070:h:0:27500:767:768:0:101:213
-:#Verfolgerfeld:12070:h:0:27500:1023:1024:0:101:214
-:#Infokanal:12070:h:0:27500:1279:1280:0:101:215
+Supersignal:12070:h:0:27500:255:256:0:101:211
+Cockpitkanal:12070:h:0:27500:511:512:0:101:212
+Boxengasse:12070:h:0:27500:767:768:0:101:213
+Verfolgerfeld:12070:h:0:27500:1023:1024:0:101:214
+Infokanal:12070:h:0:27500:1279:1280:0:101:215
Multikanal:11720:h:0:27500:255:256:0:101:17
:Beta Digital
N24:12480:v:0:27500:2047:2048:0:0:47
@@ -111,7 +111,7 @@ BuLi 7:11719:h:0:27500:3327:3328,3329:0:101:245
BuLi 8:12031:h:0:27500:3071:3072,3073:0:101:208
BuLi 9:12031:h:0:27500:3327:3328,3329:0:101:209
:
-:#TV Niepokalanow:11876:h:0:27500:305:321:0:0:20601
+TV Niepokalanow:11876:h:0:27500:305:321:0:0:20601
Mosaico:11934:v:0:27500:165:100:0:0:29010
Andalucia TV:11934:v:0:27500:166:104:0:0:29011
TVC Internacional:11934:v:0:27500:167:108:0:0:0
diff --git a/config.c b/config.c
index 5929b50..15d645d 100644
--- a/config.c
+++ b/config.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.c 1.104 2002/08/11 11:35:18 kls Exp $
+ * $Id: config.c 1.105 2002/09/04 13:45:56 kls Exp $
*/
#include "config.h"
@@ -293,30 +293,6 @@ bool cChannel::Save(FILE *f)
return fprintf(f, ToText()) > 0;
}
-bool cChannel::Switch(cDevice *Device, bool Log)
-{
- if (!Device)
- Device = cDevice::PrimaryDevice();
- if (!(Device->IsPrimaryDevice() && Device->Receiving()) && !groupSep) {
- if (Log)
- isyslog("switching to channel %d", number);
- for (int i = 3; i--;) {
- switch (Device->SetChannel(this)) {
- case scrOk: return true;
- case scrNoTransfer: if (Interface)
- Interface->Error(tr("Can't start Transfer Mode!"));
- return false;
- case scrFailed: break; // loop will retry
- }
- esyslog("retrying");
- }
- return false;
- }
- if (Device->IsPrimaryDevice() && Device->Receiving())
- Interface->Error(tr("Channel locked (recording)!"));
- return false;
-}
-
// -- cTimer -----------------------------------------------------------------
char *cTimer::buffer = NULL;
@@ -836,10 +812,10 @@ cChannel *cChannels::GetByServiceID(unsigned short ServiceId)
return NULL;
}
-bool cChannels::SwitchTo(int Number, cDevice *Device)
+bool cChannels::SwitchTo(int Number)
{
cChannel *channel = GetByNumber(Number);
- return channel && channel->Switch(Device);
+ return channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true);
}
const char *cChannels::GetChannelNameByNumber(int Number)
@@ -944,7 +920,7 @@ bool cSetupLine::operator< (const cListObject &ListObject)
{
const cSetupLine *sl = (cSetupLine *)&ListObject;
if (!plugin && !sl->plugin)
- return strcasecmp(name, sl->name) < 0;
+ return strcasecmp(name, sl->name) < 0;
if (!plugin)
return true;
if (!sl->plugin)
diff --git a/config.h b/config.h
index fe8a115..bbc006b 100644
--- a/config.h
+++ b/config.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.h 1.124 2002/08/24 10:23:48 kls Exp $
+ * $Id: config.h 1.126 2002/09/04 11:04:55 kls Exp $
*/
#ifndef __CONFIG_H
@@ -20,7 +20,7 @@
#include "eit.h"
#include "tools.h"
-#define VDRVERSION "1.1.8"
+#define VDRVERSION "1.1.9"
#define MAXPRIORITY 99
#define MAXLIFETIME 99
@@ -119,7 +119,6 @@ public:
const char *ToText(void);
bool Parse(const char *s);
bool Save(FILE *f);
- bool Switch(cDevice *Device = NULL, bool Log = true);
};
enum eTimerActive { taInactive = 0,
@@ -198,6 +197,8 @@ public:
bool Accepts(in_addr_t Address);
};
+#define CACONFBASE 100
+
class cCaDefinition : public cListObject {
private:
int number;
@@ -296,7 +297,7 @@ public:
cChannel *GetByNumber(int Number);
cChannel *GetByServiceID(unsigned short ServiceId);
const char *GetChannelNameByNumber(int Number);
- bool SwitchTo(int Number, cDevice *Device = NULL);
+ bool SwitchTo(int Number);
int MaxNumber(void) { return maxNumber; }
};
diff --git a/device.c b/device.c
index 48f5d40..72f1fc6 100644
--- a/device.c
+++ b/device.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: device.c 1.13 2002/08/25 09:16:51 kls Exp $
+ * $Id: device.c 1.19 2002/09/08 14:03:43 kls Exp $
*/
#include "device.h"
@@ -12,11 +12,14 @@
#include <sys/ioctl.h>
#include <sys/mman.h>
#include "eit.h"
+#include "i18n.h"
#include "player.h"
#include "receiver.h"
#include "status.h"
#include "transfer.h"
+// --- cDevice ---------------------------------------------------------------
+
// The default priority for non-primary devices:
#define DEFAULTPRIORITY -2
@@ -27,6 +30,7 @@
int cDevice::numDevices = 0;
int cDevice::useDevice = 0;
int cDevice::nextCardIndex = 0;
+int cDevice::currentChannel = 0;
cDevice *cDevice::device[MAXDEVICES] = { NULL };
cDevice *cDevice::primaryDevice = NULL;
@@ -38,8 +42,6 @@ cDevice::cDevice(void)
active = false;
- currentChannel = 0;
-
mute = false;
volume = Setup.CurrentVolume;
@@ -101,46 +103,44 @@ bool cDevice::SetPrimaryDevice(int n)
return false;
}
-bool cDevice::CanBeReUsed(int Frequency, int Vpid)
+bool cDevice::HasDecoder(void) const
{
return false;
}
-bool cDevice::HasDecoder(void) const
+cOsdBase *cDevice::NewOsd(int x, int y)
{
- return false;
+ return NULL;
}
-cOsdBase *cDevice::NewOsd(int x, int y)
+cSpuDecoder *cDevice::GetSpuDecoder(void)
{
return NULL;
}
-cDevice *cDevice::GetDevice(int Ca, int Priority, int Frequency, int Vpid, bool *ReUse)
+cDevice *cDevice::GetDevice(int Index)
+{
+ return (0 <= Index && Index < numDevices) ? device[Index] : NULL;
+}
+
+cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers)
{
- if (ReUse)
- *ReUse = false;
cDevice *d = NULL;
- int Provides[MAXDEVICES];
- // Check which devices provide Ca:
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]->CanBeReUsed(Frequency, Vpid)) {
- d = device[i];
- if (ReUse)
- *ReUse = true;
- break;
- }
- if (Priority > device[i]->Priority() // Priority is high enough to use this device
- && (!d // we don't have a device yet, or...
- || device[i]->Priority() < d->Priority() // ...this one has an even lower Priority
- || (device[i]->Priority() == d->Priority() // ...same Priority...
- && Provides[i] < Provides[d->CardIndex()] // ...but this one provides fewer Ca values
+ bool ndr;
+ if (device[i]->ProvidesChannel(Channel, Priority, &ndr) // this device is basicly able to do the job
+ && (!d // we don't have a device yet, or...
+ || (device[i]->Receiving() && !ndr) // ...this one is already receiving and allows additional receivers, or...
+ || !d->Receiving() // ...the one we have is not receiving...
+ && (device[i]->Priority() < d->Priority() // ...this one has an even lower Priority, or...
+ || device[i]->Priority() == d->Priority() // ...same Priority...
+ && device[i]->ProvidesCa(Channel->ca) < d->ProvidesCa(Channel->ca) // ...but this one provides fewer Ca values
)
- )
)
- d = device[i];
+ ) {
+ d = device[i];
+ if (NeedsDetachReceivers)
+ *NeedsDetachReceivers = ndr;
}
}
/*XXX+ too complex with multiple recordings per device
@@ -189,9 +189,18 @@ void cDevice::SetVideoFormat(bool VideoFormat16_9)
{
}
-//#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) { 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); }
#define PRINTPIDS(s)
+bool cDevice::HasPid(int Pid)
+{
+ for (int i = 0; i < MAXPIDHANDLES; i++) {
+ if (pidHandles[i].pid == Pid)
+ return true;
+ }
+ return false;
+}
+
bool cDevice::AddPid(int Pid, ePidType PidType)
{
if (Pid) {
@@ -207,10 +216,10 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
// The Pid is already in use
if (++pidHandles[n].used == 2 && n <= ptTeletext) {
// It's a special PID that may have to be switched into "tap" mode
- PRINTPIDS("A");//XXX+
+ PRINTPIDS("A");
return SetPid(&pidHandles[n], n, true);
}
- PRINTPIDS("a");//XXX+
+ PRINTPIDS("a");
return true;
}
else if (PidType < ptOther) {
@@ -226,7 +235,7 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
if (n >= 0) {
pidHandles[n].pid = Pid;
pidHandles[n].used = 1;
- PRINTPIDS("C");//XXX+
+ PRINTPIDS("C");
return SetPid(&pidHandles[n], n, true);
}
}
@@ -238,14 +247,15 @@ void cDevice::DelPid(int Pid)
if (Pid) {
for (int i = 0; i < MAXPIDHANDLES; i++) {
if (pidHandles[i].pid == Pid) {
+ PRINTPIDS("D");
if (--pidHandles[i].used < 2) {
SetPid(&pidHandles[i], i, false);
if (pidHandles[i].used == 0) {
- pidHandles[i].handle = -1;
- pidHandles[i].pid = 0;
- }
+ pidHandles[i].handle = -1;
+ pidHandles[i].pid = 0;
+ }
}
- PRINTPIDS("D");//XXX+
+ PRINTPIDS("E");
}
}
}
@@ -256,20 +266,66 @@ bool cDevice::SetPid(cPidHandle *Handle, int Type, bool On)
return false;
}
-eSetChannelResult cDevice::SetChannel(const cChannel *Channel)
+bool cDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers)
{
- cStatus::MsgChannelSwitch(this, 0);
+ return false;
+}
- StopReplay();
+bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView)
+{
+ if (LiveView)
+ isyslog("switching to channel %d", Channel->number);
+ for (int i = 3; i--;) {
+ switch (SetChannel(Channel, LiveView)) {
+ case scrOk: return true;
+ case scrNotAvailable: return false;
+ case scrNoTransfer: if (Interface)
+ Interface->Error(tr("Can't start Transfer Mode!"));
+ return false;
+ case scrFailed: break; // loop will retry
+ }
+ esyslog("retrying");
+ }
+ return false;
+}
- // Must set this anyway to avoid getting stuck when switching through
- // channels with 'Up' and 'Down' keys:
- currentChannel = Channel->number;
+bool cDevice::SwitchChannel(int Direction)
+{
+ bool result = false;
+ Direction = sgn(Direction);
+ if (Direction) {
+ int n = CurrentChannel() + Direction;
+ int first = n;
+ for (;;) {
+ cChannel *channel = Channels.GetByNumber(n);
+ if (!channel)
+ break;
+ if (PrimaryDevice()->SwitchChannel(channel, true)) {
+ result = true;
+ break;
+ }
+ n += Direction;
+ }
+ int d = n - first;
+ if (abs(d) == 1)
+ dsyslog("skipped channel %d", first);
+ else if (d)
+ dsyslog("skipped channels %d..%d", first, n - sgn(d));
+ }
+ return result;
+}
+
+eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+{
+ cStatus::MsgChannelSwitch(this, 0);
+
+ if (LiveView)
+ StopReplay();
// 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(Channel->ca));
+ bool NeedsTransferMode = (LiveView && IsPrimaryDevice() && !ProvidesChannel(Channel, Setup.PrimaryLimit));
eSetChannelResult Result = scrOk;
@@ -277,24 +333,30 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel)
// use the card that actually can receive it and transfer data from there:
if (NeedsTransferMode) {
- 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+
+ cDevice *CaDevice = GetDevice(Channel, 0);
+ if (CaDevice) {
+ if (CaDevice->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
+ cControl::Launch(new cTransferControl(CaDevice, Channel->vpid, Channel->apid1, 0, 0, 0));//XXX+
+ else
+ Result = scrNoTransfer;
+ }
else
- Result = scrNoTransfer;
+ Result = scrNotAvailable;
}
- else if (!SetChannelDevice(Channel))
+ else if (!SetChannelDevice(Channel, LiveView))
Result = scrFailed;
- if (IsPrimaryDevice())
+ if (Result == scrOk && LiveView && IsPrimaryDevice()) {
cSIProcessor::SetCurrentServiceID(Channel->pnr);
+ currentChannel = Channel->number;
+ }
cStatus::MsgChannelSwitch(this, Channel->number);
return Result;
}
-bool cDevice::SetChannelDevice(const cChannel *Channel)
+bool cDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
{
return false;
}
@@ -357,10 +419,6 @@ bool cDevice::Replaying(void)
bool cDevice::AttachPlayer(cPlayer *Player)
{
- if (Receiving()) {
- esyslog("ERROR: attempt to attach a cPlayer while receiving on device %d - ignored", CardIndex() + 1);
- return false;
- }
if (HasDecoder()) {
if (player)
Detach(player);
@@ -419,9 +477,7 @@ int cDevice::PlayAudio(const uchar *Data, int Length)
int cDevice::Priority(void)
{
- if (IsPrimaryDevice() && !Receiving())
- return Setup.PrimaryLimit - 1;
- int priority = DEFAULTPRIORITY;
+ int priority = IsPrimaryDevice() ? Setup.PrimaryLimit - 1 : DEFAULTPRIORITY;
for (int i = 0; i < MAXRECEIVERS; i++) {
if (receiver[i])
priority = max(receiver[i]->priority, priority);
@@ -497,15 +553,13 @@ void cDevice::Action(void)
dsyslog("receiver thread started on device %d (pid=%d)", CardIndex() + 1, getpid());
if (OpenDvr()) {
- uchar b[TS_SIZE];
time_t t = time(NULL);
active = true;
for (; active;) {
// Read data from the DVR device:
- int r = GetTSPacket(b);
- if (r == TS_SIZE) {
- if (*b == TS_SYNC_BYTE) {
- // We're locked on to a TS packet
+ uchar *b = NULL;
+ if (GetTSPacket(b)) {
+ if (b) {
int Pid = (((uint16_t)b[1] & PID_MASK_HI) << 8) | b[2];
// Distribute the packet to all attached receivers:
Lock();
@@ -514,12 +568,10 @@ void cDevice::Action(void)
receiver[i]->Receive(b, TS_SIZE);
}
Unlock();
+ t = time(NULL);
}
- t = time(NULL);
}
- 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)
+ else
break;
//XXX+ put this into the recorder??? or give the receiver a flag whether it wants this?
@@ -544,19 +596,17 @@ void cDevice::CloseDvr(void)
{
}
-int cDevice::GetTSPacket(uchar *Data)
+bool cDevice::GetTSPacket(uchar *&Data)
{
- return -1;
+ return false;
}
bool cDevice::AttachReceiver(cReceiver *Receiver)
{
- //XXX+ check for same transponder???
if (!Receiver)
return false;
if (Receiver->device == this)
return true;
- StopReplay();
for (int i = 0; i < MAXRECEIVERS; i++) {
if (!receiver[i]) {
for (int n = 0; n < MAXRECEIVEPIDS; n++)
@@ -597,3 +647,85 @@ void cDevice::Detach(cReceiver *Receiver)
Cancel(3);
}
}
+
+// --- cTSBuffer -------------------------------------------------------------
+
+cTSBuffer::cTSBuffer(int File, int Size, int CardIndex)
+{
+ f = File;
+ size = Size / TS_SIZE * TS_SIZE;
+ cardIndex = CardIndex;
+ tsRead = tsWrite = 0;
+ buf = (f >= 0 && size >= TS_SIZE) ? MALLOC(uchar, size + TS_SIZE) : NULL;
+ // the '+ TS_SIZE' allocates some extra space for handling packets that got split by a buffer roll-over
+ firstRead = true;
+}
+
+cTSBuffer::~cTSBuffer()
+{
+ free(buf);
+}
+
+int cTSBuffer::Read(void)
+{
+ if (buf) {
+ cPoller Poller(f, false);
+ bool repeat;
+ int total = 0;
+ do {
+ repeat = false;
+ if (firstRead || Used() > TS_SIZE || Poller.Poll(100)) { // only wait if there's not enough data in the buffer
+ firstRead = false;
+ if (tsRead == tsWrite)
+ tsRead = tsWrite = 0; // keep the maximum buffer space available
+ if (tsWrite >= size && tsRead > 0)
+ tsWrite = 0;
+ int free = tsRead <= tsWrite ? size - tsWrite : tsRead - tsWrite - 1;
+ if (free > 0) {
+ int r = read(f, buf + tsWrite, free);
+ if (r > 0) {
+ total += r;
+ tsWrite += r;
+ if (tsWrite >= size && tsRead > 0) {
+ tsWrite = 0;
+ repeat = true; // read again after a boundary roll-over
+ }
+ }
+ }
+ }
+ } while (repeat);
+ return total;
+ }
+ return -1;
+}
+
+uchar *cTSBuffer::Get(void)
+{
+ if (Used() >= TS_SIZE) {
+ uchar *p = buf + tsRead;
+ if (*p != TS_SYNC_BYTE) {
+ esyslog("ERROR: not sync'ed to TS packet on device %d", cardIndex);
+ int tsMax = tsRead < tsWrite ? tsWrite : size;
+ for (int i = tsRead; i < tsMax; i++) {
+ if (buf[i] == TS_SYNC_BYTE) {
+ esyslog("ERROR: skipped %d bytes to sync on TS packet on device %d", i - tsRead, cardIndex);
+ tsRead = i;
+ return NULL;
+ }
+ }
+ if ((tsRead = tsMax) >= size)
+ tsRead = 0;
+ return NULL;
+ }
+ if (tsRead + TS_SIZE > size) {
+ // the packet rolled over the buffer boundary, so let's fetch the rest from the beginning (which MUST be there, since Used() >= TS_SIZE)
+ int rest = TS_SIZE - (size - tsRead);
+ memcpy(buf + size, buf, rest);
+ tsRead = rest;
+ }
+ else if ((tsRead += TS_SIZE) >= size)
+ tsRead = 0;
+ return p;
+ }
+ return NULL;
+}
diff --git a/device.h b/device.h
index 5f62417..0d10793 100644
--- a/device.h
+++ b/device.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: device.h 1.10 2002/08/25 09:16:34 kls Exp $
+ * $Id: device.h 1.16 2002/09/08 14:56:21 kls Exp $
*/
#ifndef __DEVICE_H
@@ -24,11 +24,12 @@
#define TS_SYNC_BYTE 0x47
#define PID_MASK_HI 0x1F
-enum eSetChannelResult { scrOk, scrNoTransfer, scrFailed };
+enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed };
-enum ePlayMode { pmNone, // audio/video from decoder
- pmAudioVideo, // audio/video from player
- pmAudioOnly, // audio only from player, video from decoder
+enum ePlayMode { pmNone, // audio/video from decoder
+ pmAudioVideo, // audio/video from player
+ pmAudioOnly, // audio only from player, video from decoder
+ pmAudioOnlyBlack, // audio only from player, no video (black screen)
pmExtern_THIS_SHOULD_BE_AVOIDED
// external player (e.g. MPlayer), release the device
// WARNING: USE THIS MODE ONLY AS A LAST RESORT, IF YOU
@@ -47,6 +48,7 @@ class cOsdBase;
class cChannel;
class cPlayer;
class cReceiver;
+class cSpuDecoder;
class cDevice : cThread {
private:
@@ -69,19 +71,13 @@ public:
// 1...numDevices) and returns true if this was possible.
static cDevice *PrimaryDevice(void) { return primaryDevice; }
// Returns the primary device.
- static cDevice *GetDevice(int Ca, int Priority, int Frequency = 0, int Vpid = 0, bool *ReUse = NULL);
- // 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 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 cDevice *GetDevice(int Index);
+ // Returns the device with the Index (if Index is in the range
+ // 0..numDevices-1, NULL otherwise).
+ static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
+ // Returns a device that is able to receive the given Channel at the
+ // given Priority (see ProvidesChannel() for more information on how
+ // priorities are handled, and the meaning of NeedsDetachReceivers).
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.
@@ -115,18 +111,13 @@ public:
bool IsPrimaryDevice(void) const { return this == primaryDevice; }
int CardIndex(void) const { return cardIndex; }
// Returns the card index of this device (0 ... MAXDEVICES - 1).
- virtual int ProvidesCa(int Ca);
- //XXX TODO temporarily made this function virtual - until a general
- //XXX mechanism has been implemented
+ int ProvidesCa(int Ca);
// 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 device,
// 1 is returned. If it is 0 (FTA), 1 plus the number of other values
// in caCaps is returned.
- 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.
@@ -139,20 +130,46 @@ public:
// of the OSD at the given coordinates. If a derived cDevice doesn't
// implement this function, NULL will be returned by default (which
// means the device has no OSD capabilities).
+ virtual cSpuDecoder *GetSpuDecoder(void);
+ // Returns a pointer to the device's SPU decoder (or NULL, if this
+ // device doesn't have an SPU decoder).
// Channel facilities
protected:
- int currentChannel;
+ static int currentChannel;
public:
- eSetChannelResult SetChannel(const cChannel *Channel);
+ virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
+ // Returns true if this device can provide the given channel.
+ // In case the device has cReceivers attached to it or it is the primary
+ // device, Priority is used to decide whether the caller's request can
+ // be honored.
+ // The special Priority value -1 will tell the caller whether this device
+ // is principally able to provide the given Channel, regardless of any
+ // attached cReceivers.
+ // If NeedsDetachReceivers is given, the resulting value in it will tell the
+ // caller whether or not it will have to detach any currently attached
+ // receivers from this device before calling SwitchChannel. Note
+ // that the return value in NeedsDetachReceivers is only meaningful if the
+ // function itself actually returns true.
+ // The default implementation always returns false, so a derived cDevice
+ // class that can provide channels must implement this function.
+ bool SwitchChannel(const cChannel *Channel, bool LiveView);
+ // Switches the device to the given Channel, initiating transfer mode
+ // if necessary.
+ static bool SwitchChannel(int Direction);
+ // Switches the primary device to the next available channel in the given
+ // Direction (only the sign of Direction is evaluated, positive values
+ // switch to higher channel numbers).
+private:
+ eSetChannelResult SetChannel(const cChannel *Channel, bool LiveView);
// Sets the device to the given channel (general setup).
- virtual bool SetChannelDevice(const cChannel *Channel);
+protected:
+ virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
// Sets the device to the given channel (actual physical setup).
- static int CurrentChannel(void) { return primaryDevice ? primaryDevice->currentChannel : 0; }
+public:
+ static int CurrentChannel(void) { return 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
@@ -169,6 +186,8 @@ protected:
cPidHandle(void) { pid = used = 0; handle = -1; }
};
cPidHandle pidHandles[MAXPIDHANDLES];
+ bool HasPid(int Pid);
+ // Returns true if this device is currently receiving the given PID.
bool AddPid(int Pid, ePidType PidType = ptOther);
// Adds a PID to the set of PIDs this device shall receive.
void DelPid(int Pid);
@@ -263,23 +282,24 @@ public:
private:
cReceiver *receiver[MAXRECEIVERS];
int ca;
+ int CanShift(int Ca, int Priority, int UsedCards = 0);
+protected:
int Priority(void);
// Returns the priority of the current receiving session (0..MAXPRIORITY),
// 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.
+ virtual bool GetTSPacket(uchar *&Data);
+ // Gets exactly one TS packet from the DVR of this device and returns
+ // a pointer to it in Data. Only the first 188 bytes (TS_SIZE) Data
+ // points to are valid and may be accessed. If there is currently no
+ // new data available, Data will be set to NULL. The function returns
+ // false in case of a non recoverable error, otherwise it returns true,
+ // even if Data is NULL.
public:
int Ca(void) { return ca; }
// Returns the ca of the current receiving session.
@@ -291,4 +311,29 @@ public:
// Detaches the given receiver from this device.
};
+// Derived cDevice classes that can receive channels will have to provide
+// Transport Stream (TS) packets one at a time. cTSBuffer implements a
+// simple buffer that allows the device to read a larger amount of data
+// from the driver with each call to Read(), thus avoiding the overhead
+// of getting each TS packet separately from the driver. It also makes
+// sure the returned data points to a TS packet and automatically
+// re-synchronizes after broken packet.
+
+class cTSBuffer {
+private:
+ int f;
+ int size;
+ int cardIndex;
+ int tsRead;
+ int tsWrite;
+ uchar *buf;
+ bool firstRead;
+ int Used(void) { return tsRead <= tsWrite ? tsWrite - tsRead : size - tsRead + tsWrite; }
+public:
+ cTSBuffer(int File, int Size, int CardIndex);
+ ~cTSBuffer();
+ int Read(void);
+ uchar *Get(void);
+ };
+
#endif //__DEVICE_H
diff --git a/dvbdevice.c b/dvbdevice.c
index 52c7c3b..a130112 100644
--- a/dvbdevice.c
+++ b/dvbdevice.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbdevice.c 1.8 2002/08/25 09:20:53 kls Exp $
+ * $Id: dvbdevice.c 1.13 2002/09/08 15:00:46 kls Exp $
*/
#include "dvbdevice.h"
@@ -20,10 +20,12 @@ extern "C" {
#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
@@ -35,8 +37,6 @@ extern "C" {
#include "status.h"
#include "transfer.h"
-#define MAXDVBDEVICES 4
-
#define DEV_VIDEO "/dev/video"
#ifdef NEWSTRUCT
#define DEV_DVB_ADAPTER "/dev/dvb/adapter"
@@ -80,6 +80,7 @@ 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:
@@ -91,10 +92,10 @@ cDvbDevice::cDvbDevice(int n)
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
@@ -128,6 +129,7 @@ cDvbDevice::cDvbDevice(int n)
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...
@@ -179,15 +181,6 @@ 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;
@@ -198,6 +191,13 @@ 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)
{
int videoDev = DvbOpen(DEV_VIDEO, CardIndex(), O_RDWR, true);
@@ -235,10 +235,10 @@ bool cDvbDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int Siz
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) {
@@ -253,11 +253,11 @@ bool cDvbDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int Siz
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++)
@@ -313,6 +313,14 @@ bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
else {
CHECK(ioctl(Handle->handle, DMX_STOP));
if (Handle->used == 0) {
+ dmxPesFilterParams pesFilterParams;
+ memset(&pesFilterParams, 0, sizeof(pesFilterParams));
+ pesFilterParams.pid = 0x1FFF;
+ pesFilterParams.input = DMX_IN_FRONTEND;
+ pesFilterParams.output = DMX_OUT_DECODER;
+ pesFilterParams.pesType = PesTypes[Type < ptOther ? Type : ptOther];
+ pesFilterParams.flags = DMX_IMMEDIATE_START;
+ CHECK(ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams));
close(Handle->handle);
Handle->handle = -1;
return true;
@@ -339,8 +347,56 @@ bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
return true;
}
-bool cDvbDevice::SetChannelDevice(const cChannel *Channel)
+bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers)
{
+ bool result = false;
+ bool hasPriority = Priority < 0 || Priority > this->Priority();
+ bool needsDetachReceivers = true;
+
+ if (ProvidesCa(Channel->ca)) {
+ if (Receiving()) {
+ if (frequency == Channel->frequency) {
+ needsDetachReceivers = false;
+ if (!HasPid(Channel->vpid)) {
+ if (Channel->ca > CACONFBASE) {
+ needsDetachReceivers = true;
+ result = hasPriority;
+ }
+ else if (!HasDecoder())
+ result = true; // if it has no decoder it can't be the primary device
+ 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
+
// Avoid noise while switching:
if (HasDecoder()) {
@@ -357,204 +413,209 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel)
// Turn off current PIDs:
- if (HasDecoder()) {
+ if (HasDecoder() && (LiveView || pidHandles[ptVideo].pid == Channel->vpid)) {
DelPid(pidHandles[ptVideo].pid);
DelPid(pidHandles[ptAudio].pid);
DelPid(pidHandles[ptTeletext].pid);
DelPid(pidHandles[ptDolby].pid);
}
+ if (frequency != Channel->frequency || Channel->ca > CACONFBASE) { // CA channels can only be decrypted in "live" mode
+
#ifdef NEWSTRUCT
- dvb_frontend_parameters Frontend;
+ dvb_frontend_parameters Frontend;
#else
- FrontendParameters Frontend;
+ FrontendParameters Frontend;
#endif
- memset(&Frontend, 0, sizeof(Frontend));
+ memset(&Frontend, 0, sizeof(Frontend));
- switch (frontendType) {
- case FE_QPSK: { // DVB-S
+ switch (frontendType) {
+ case FE_QPSK: { // DVB-S
- // Frequency offsets:
+ // Frequency offsets:
- unsigned int freq = Channel->frequency;
- int tone = SEC_TONE_OFF;
+ 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;
- }
+ if (freq < (unsigned int)Setup.LnbSLOF) {
+ freq -= Setup.LnbFrequLo;
+ tone = SEC_TONE_OFF;
+ }
+ else {
+ freq -= Setup.LnbFrequHi;
+ tone = SEC_TONE_ON;
+ }
#ifdef NEWSTRUCT
- Frontend.frequency = freq * 1000UL;
- Frontend.inversion = INVERSION_AUTO;
- Frontend.u.qpsk.symbol_rate = Channel->srate * 1000UL;
- Frontend.u.qpsk.fec_inner = FEC_AUTO;
+ Frontend.frequency = freq * 1000UL;
+ Frontend.inversion = INVERSION_AUTO;
+ Frontend.u.qpsk.symbol_rate = Channel->srate * 1000UL;
+ Frontend.u.qpsk.fec_inner = FEC_AUTO;
#else
- Frontend.Frequency = freq * 1000UL;
- Frontend.Inversion = INVERSION_AUTO;
- Frontend.u.qpsk.SymbolRate = Channel->srate * 1000UL;
- Frontend.u.qpsk.FEC_inner = FEC_AUTO;
+ Frontend.Frequency = freq * 1000UL;
+ Frontend.Inversion = INVERSION_AUTO;
+ Frontend.u.qpsk.SymbolRate = Channel->srate * 1000UL;
+ Frontend.u.qpsk.FEC_inner = FEC_AUTO;
#endif
- int volt = (Channel->polarization == 'v' || Channel->polarization == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
+ int volt = (Channel->polarization == 'v' || Channel->polarization == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
- // DiSEqC:
+ // DiSEqC:
#ifdef NEWSTRUCT
- struct dvb_diseqc_master_cmd cmd = { {0xE0, 0x10, 0x38, 0xF0, 0x00, 0x00}, 4};
- cmd.msg[3] = 0xF0 | (((Channel->diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0));
-
- if (Setup.DiSEqC)
- CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF));
- CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt));
- if (Setup.DiSEqC) {
- usleep(15 * 1000);
- CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd));
- usleep(15 * 1000);
- CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, (Channel->diseqc / 4) % 2 ? SEC_MINI_B : SEC_MINI_A));
- usleep(15 * 1000);
- }
- CHECK(ioctl(fd_frontend, FE_SET_TONE, tone));
+ struct dvb_diseqc_master_cmd cmd = { {0xE0, 0x10, 0x38, 0xF0, 0x00, 0x00}, 4};
+ cmd.msg[3] = 0xF0 | (((Channel->diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0));
+
+ if (Setup.DiSEqC)
+ CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF));
+ CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt));
+ if (Setup.DiSEqC) {
+ usleep(15 * 1000);
+ CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd));
+ usleep(15 * 1000);
+ CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, (Channel->diseqc / 4) % 2 ? SEC_MINI_B : SEC_MINI_A));
+ usleep(15 * 1000);
+ }
+ CHECK(ioctl(fd_frontend, FE_SET_TONE, tone));
#else
- 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));
+ 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));
#endif
- }
- break;
- case FE_QAM: { // DVB-C
+ }
+ break;
+ case FE_QAM: { // DVB-C
- // Frequency and symbol rate:
+ // Frequency and symbol rate:
#ifdef NEWSTRUCT
- Frontend.frequency = Channel->frequency * 1000000UL;
- Frontend.inversion = INVERSION_AUTO;
- Frontend.u.qam.symbol_rate = Channel->srate * 1000UL;
- Frontend.u.qam.fec_inner = FEC_AUTO;
- Frontend.u.qam.modulation = QAM_64;
+ Frontend.frequency = Channel->frequency * 1000000UL;
+ Frontend.inversion = INVERSION_AUTO;
+ Frontend.u.qam.symbol_rate = Channel->srate * 1000UL;
+ Frontend.u.qam.fec_inner = FEC_AUTO;
+ Frontend.u.qam.modulation = QAM_64;
#else
- 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;
+ 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;
#endif
- }
- break;
- case FE_OFDM: { // DVB-T
+ }
+ break;
+ case FE_OFDM: { // DVB-T
- // Frequency and OFDM paramaters:
+ // Frequency and OFDM paramaters:
#ifdef NEWSTRUCT
- Frontend.frequency = Channel->frequency * 1000UL;
- Frontend.inversion = INVERSION_AUTO;
- Frontend.u.ofdm.bandwidth=BANDWIDTH_8_MHZ;
- Frontend.u.ofdm.code_rate_HP = FEC_2_3;
- Frontend.u.ofdm.code_rate_LP = FEC_1_2;
- Frontend.u.ofdm.constellation = QAM_64;
- Frontend.u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
- Frontend.u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
- Frontend.u.ofdm.hierarchy_information = HIERARCHY_NONE;
+ Frontend.frequency = Channel->frequency * 1000UL;
+ Frontend.inversion = INVERSION_AUTO;
+ Frontend.u.ofdm.bandwidth=BANDWIDTH_8_MHZ;
+ Frontend.u.ofdm.code_rate_HP = FEC_2_3;
+ Frontend.u.ofdm.code_rate_LP = FEC_1_2;
+ Frontend.u.ofdm.constellation = QAM_64;
+ Frontend.u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
+ Frontend.u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
+ Frontend.u.ofdm.hierarchy_information = HIERARCHY_NONE;
#else
- 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;
+ 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;
#endif
- }
- break;
- default:
- esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
- return false;
- }
+ }
+ break;
+ default:
+ esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
+ return false;
+ }
#ifdef NEWSTRUCT
- // Discard stale events:
+ // Discard stale events:
- for (;;) {
- dvb_frontend_event event;
- if (ioctl(fd_frontend, FE_GET_EVENT, &event) < 0)
- break;
- }
+ for (;;) {
+ dvb_frontend_event event;
+ if (ioctl(fd_frontend, FE_GET_EVENT, &event) < 0)
+ break;
+ }
#endif
- // Tuning:
+ // Tuning:
- CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend));
+ CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend));
- // Wait for channel lock:
+ // 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 (IsPrimaryDevice())
- cThread::RaisePanic();
- return false;
- }
+ 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 (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 (IsPrimaryDevice())
- cThread::RaisePanic();
- return false;
+ 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 (IsPrimaryDevice())
+ cThread::RaisePanic();
+ return false;
+ }
}
+ else
+ esyslog("ERROR in frontend get event (channel %d, card %d): %m", Channel->number, CardIndex() + 1);
}
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);
+ esyslog("ERROR: timeout while tuning on DVB card %d", CardIndex() + 1);
#endif
- frequency = Channel->frequency;
+ 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 (HasDecoder() && (LiveView || Channel->ca > CACONFBASE)) { // CA channels can only be decrypted in "live" mode
+ if (!HasPid(Channel->vpid)) {
+ 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));
+ CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
+ CHECK(ioctl(fd_video, VIDEO_SET_BLANK, 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));
+ else
+ cControl::Launch(new cTransferControl(this, Channel->vpid, Channel->apid1, 0, 0, 0));
}
// Start setting system time:
@@ -602,11 +663,12 @@ bool cDvbDevice::SetPlayMode(ePlayMode PlayMode)
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, true));
+ 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));
@@ -650,18 +712,30 @@ void cDvbDevice::Clear(void)
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));
+ 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 (fd_audio >= 0)
- CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
- if (fd_video >= 0)
- CHECK(ioctl(fd_video, VIDEO_FREEZE));
+ 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)
@@ -699,13 +773,13 @@ void cDvbDevice::StillPicture(const uchar *Data, int Length)
bool cDvbDevice::Poll(cPoller &Poller, int TimeoutMs)
{
- Poller.Add(playMode == pmAudioOnly ? fd_audio : fd_video, true);
+ 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 ? fd_audio : fd_video;
+ int fd = (playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) ? fd_audio : fd_video;
if (fd >= 0)
return write(fd, Data, Length);
return -1;
@@ -721,6 +795,8 @@ 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;
}
@@ -729,28 +805,28 @@ void cDvbDevice::CloseDvr(void)
if (fd_dvr >= 0) {
close(fd_dvr);
fd_dvr = -1;
+ delete tsBuffer;
+ tsBuffer = NULL;
}
}
-int cDvbDevice::GetTSPacket(uchar *Data)
+bool cDvbDevice::GetTSPacket(uchar *&Data)
{
- if (fd_dvr >= 0) {
- cPoller Poller(fd_dvr, false);
- if (Poller.Poll(100)) {
- 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;
- }
+ 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 0;
+ return true;
}
- else
- return -1;
+ return false;
}
diff --git a/dvbdevice.h b/dvbdevice.h
index a338399..b135ff6 100644
--- a/dvbdevice.h
+++ b/dvbdevice.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbdevice.h 1.6 2002/08/25 09:19:34 kls Exp $
+ * $Id: dvbdevice.h 1.10 2002/09/08 14:05:29 kls Exp $
*/
#ifndef __DVBDEVICE_H
@@ -21,8 +21,11 @@
#include <ost/frontend.h>
#endif
#include "device.h"
+#include "dvbspu.h"
#include "eit.h"
+#define MAXDVBDEVICES 4
+
class cDvbDevice : public cDevice {
friend class cDvbOsd;
private:
@@ -45,20 +48,24 @@ protected:
public:
cDvbDevice(int n);
virtual ~cDvbDevice();
- virtual bool CanBeReUsed(int Frequency, int Vpid);
virtual bool HasDecoder(void) const;
// OSD facilities
+private:
+ cDvbSpuDecoder *spuDecoder;
public:
cOsdBase *NewOsd(int x, int y);
+ virtual cSpuDecoder *GetSpuDecoder(void);
// Channel facilities
private:
int frequency;
public:
- virtual bool SetChannelDevice(const cChannel *Channel);
+ virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
+protected:
+ virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
// PID handle facilities
@@ -103,10 +110,12 @@ public:
// Receiver facilities
+private:
+ cTSBuffer *tsBuffer;
protected:
virtual bool OpenDvr(void);
virtual void CloseDvr(void);
- virtual int GetTSPacket(uchar *Data);
+ virtual bool GetTSPacket(uchar *&Data);
};
#endif //__DVBDEVICE_H
diff --git a/dvbspu.c b/dvbspu.c
new file mode 100644
index 0000000..1e29859
--- /dev/null
+++ b/dvbspu.c
@@ -0,0 +1,505 @@
+/*
+ * SPU decoder for DVB devices
+ *
+ * Copyright (C) 2001.2002 Andreas Schultz <aschultz@warp10.net>
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ *
+ * parts of this file are derived from the OMS program.
+ *
+ * $Id: dvbspu.c 1.1 2002/09/08 14:17:35 kls Exp $
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <inttypes.h>
+#include <math.h>
+
+#include "osd.h"
+#include "osdbase.h"
+#include "device.h"
+#include "dvbspu.h"
+
+/*
+ * cDvbSpubitmap:
+ *
+ * this is a bitmap of the full screen and two palettes
+ * the normal palette for the background and the highlight palette
+ *
+ * Inputs:
+ * - a SPU rle encoded image on creation, which will be decoded into
+ * the full screen indexed bitmap
+ *
+ * Output:
+ * - a minimal sized cDvbSpuBitmap a given palette, the indexed bitmap
+ * will be scanned to get the smallest possible resulting bitmap considering
+ * transparencies
+ */
+
+// #define SPUDEBUG
+
+#ifdef SPUDEBUG
+#define DEBUG(format, args...) printf (format, ## args)
+#else
+#define DEBUG(format, args...)
+#endif
+
+// --- cDvbSpuPalette----------------------------------
+
+void cDvbSpuPalette::setPalette(const uint32_t * pal)
+{
+ for (int i = 0; i < 16; i++)
+ palette[i] = yuv2rgb(pal[i]);
+}
+
+// --- cDvbSpuBitmap --------------------------------------------
+
+#define setMin(a, b) if (a > b) a = b
+#define setMax(a, b) if (a < b) a = b
+
+#define spuXres 720
+#define spuYres 576
+
+#define revRect(r1, r2) { r1.x1 = r2.x2; r1.y1 = r2.y2; r1.x2 = r2.x1; r1.y2 = r2.y1; }
+
+cDvbSpuBitmap::cDvbSpuBitmap(sDvbSpuRect size,
+ uint8_t * fodd, uint8_t * eodd,
+ uint8_t * feven, uint8_t * eeven)
+{
+ if (size.x1 < 0 || size.y1 < 0 || size.x2 >= spuXres
+ || size.y2 >= spuYres)
+ throw;
+
+ bmpsize = size;
+ revRect(minsize[0], size);
+ revRect(minsize[1], size);
+ revRect(minsize[2], size);
+ revRect(minsize[3], size);
+
+ if (!(bmp = new uint8_t[spuXres * spuYres * sizeof(uint8_t)]))
+ throw;
+
+ memset(bmp, 0, spuXres * spuYres * sizeof(uint8_t));
+ putFieldData(0, fodd, eodd);
+ putFieldData(1, feven, eeven);
+}
+
+cDvbSpuBitmap::~cDvbSpuBitmap()
+{
+ delete[]bmp;
+}
+
+cBitmap *cDvbSpuBitmap::getBitmap(const aDvbSpuPalDescr paldescr,
+ const cDvbSpuPalette & pal,
+ sDvbSpuRect & size) const
+{
+ int h = size.height();
+ int w = size.width();
+
+ if (size.y1 + h >= spuYres)
+ h = spuYres - size.y1 - 1;
+ if (size.x1 + w >= spuXres)
+ w = spuXres - size.x1 - 1;
+
+ if (w & 0x03)
+ w += 4 - (w & 0x03);
+
+ cBitmap *ret = new cBitmap(w, h, 2, true);
+
+ // set the palette
+ for (int i = 0; i < 4; i++) {
+ uint32_t color =
+ pal.getColor(paldescr[i].index, paldescr[i].trans);
+ ret->SetColor(i, (eDvbColor) color);
+ }
+
+ // set the content
+ for (int yp = 0; yp < h; yp++) {
+ for (int xp = 0; xp < w; xp++) {
+ uint8_t idx = bmp[(size.y1 + yp) * spuXres + size.x1 + xp];
+ ret->SetIndex(xp, yp, idx);
+ }
+ }
+ return ret;
+}
+
+// find the minimum non-transparent area
+bool cDvbSpuBitmap::getMinSize(const aDvbSpuPalDescr paldescr,
+ sDvbSpuRect & size) const
+{
+ bool ret = false;
+ for (int i = 0; i < 4; i++) {
+ if (paldescr[i].trans != 0) {
+ if (!ret)
+ size = minsize[i];
+ else {
+ setMin(size.x1, minsize[i].x1);
+ setMin(size.y1, minsize[i].y1);
+ setMax(size.x2, minsize[i].x2);
+ setMax(size.y2, minsize[i].y2);
+ ret = true;
+ }
+ }
+ }
+ if (ret)
+ DEBUG("MinSize: (%d, %d) x (%d, %d)\n",
+ size.x1, size.y1, size.x2, size.y2);
+
+ return ret;
+}
+
+void cDvbSpuBitmap::putPixel(int xp, int yp, int len, uint8_t colorid)
+{
+ memset(bmp + spuXres * yp + xp, colorid, len);
+ setMin(minsize[colorid].x1, xp);
+ setMin(minsize[colorid].y1, yp);
+ setMax(minsize[colorid].x2, xp + len - 1);
+ setMax(minsize[colorid].y2, yp + len - 1);
+}
+
+static uint8_t getBits(uint8_t * &data, uint8_t & bitf)
+{
+ uint8_t ret = *data;
+ if (bitf)
+ ret >>= 4;
+ else
+ data++;
+ bitf ^= 1;
+
+ return (ret & 0xf);
+}
+
+void cDvbSpuBitmap::putFieldData(int field, uint8_t * data, uint8_t * endp)
+{
+ int xp = bmpsize.x1;
+ int yp = bmpsize.y1 + field;
+ uint8_t bitf = 1;
+
+ while (data < endp) {
+ uint16_t vlc = getBits(data, bitf);
+ if (vlc < 0x0004) {
+ vlc = (vlc << 4) | getBits(data, bitf);
+ if (vlc < 0x0010) {
+ vlc = (vlc << 4) | getBits(data, bitf);
+ if (vlc < 0x0040) {
+ vlc = (vlc << 4) | getBits(data, bitf);
+ }
+ }
+ }
+
+ uint8_t color = vlc & 0x03;
+ int len = vlc >> 2;
+
+ // if len == 0 -> end sequence - fill to end of line
+ len = len ? : bmpsize.x2 - xp + 1;
+ putPixel(xp, yp, len, color);
+ xp += len;
+
+ if (xp > bmpsize.x2) {
+ // nextLine
+ if (!bitf)
+ data++;
+ bitf = 1;
+ xp = bmpsize.x1;
+ yp += 2;
+ if (yp > bmpsize.y2)
+ return;
+ }
+ }
+}
+
+// --- cDvbSpuDecoder-----------------------------
+
+#define CMD_SPU_MENU 0x00
+#define CMD_SPU_SHOW 0x01
+#define CMD_SPU_HIDE 0x02
+#define CMD_SPU_SET_PALETTE 0x03
+#define CMD_SPU_SET_ALPHA 0x04
+#define CMD_SPU_SET_SIZE 0x05
+#define CMD_SPU_SET_PXD_OFFSET 0x06
+#define CMD_SPU_EOF 0xff
+
+#define spuU32(i) ((spu[i] << 8) + spu[i+1])
+
+cDvbSpuDecoder::cDvbSpuDecoder()
+{
+ clean = true;
+ scaleMode = eSpuNormal;
+ spu = NULL;
+ osd = NULL;
+ spubmp = NULL;
+}
+
+cDvbSpuDecoder::~cDvbSpuDecoder()
+{
+ delete spubmp;
+ delete spu;
+ delete osd;
+}
+
+void cDvbSpuDecoder::processSPU(uint32_t pts, uint8_t * buf)
+{
+ setTime(pts);
+
+ DEBUG("SPU pushData: pts: %d\n", pts);
+
+ delete spubmp;
+ spubmp = NULL;
+ delete[]spu;
+ spu = buf;
+
+ DCSQ_offset = cmdOffs();
+ prev_DCSQ_offset = 0;
+
+ clean = true;
+}
+
+void cDvbSpuDecoder::setScaleMode(cSpuDecoder::eScaleMode ScaleMode)
+{
+ scaleMode = ScaleMode;
+}
+
+void cDvbSpuDecoder::setPalette(uint32_t * pal)
+{
+ palette.setPalette(pal);
+}
+
+void cDvbSpuDecoder::setHighlight(uint16_t sx, uint16_t sy,
+ uint16_t ex, uint16_t ey,
+ uint32_t palette)
+{
+ aDvbSpuPalDescr pld;
+ for (int i = 0; i < 4; i++) {
+ pld[i].index = 0xf & (palette >> (16 + 4 * i));
+ pld[i].trans = 0xf & (palette >> (4 * i));
+ }
+
+ bool ne = hlpsize.x1 != sx || hlpsize.y1 != sy ||
+ hlpsize.x2 != ex || hlpsize.y2 != ey ||
+ pld[0] != hlpDescr[0] || pld[1] != hlpDescr[1] ||
+ pld[2] != hlpDescr[2] || pld[3] != hlpDescr[3];
+
+ if (ne) {
+ DEBUG("setHighlight: %d,%d x %d,%d\n", sx, sy, ex, ey);
+ hlpsize.x1 = sx;
+ hlpsize.y1 = sy;
+ hlpsize.x2 = ex;
+ hlpsize.y2 = ey;
+ memcpy(hlpDescr, pld, sizeof(aDvbSpuPalDescr));
+ highlight = true;
+ clean = false;
+ }
+}
+
+void cDvbSpuDecoder::clearHighlight(void)
+{
+ clean &= !highlight;
+ highlight = false;
+}
+
+int cDvbSpuDecoder::ScaleYcoord(int value)
+{
+ if (scaleMode == eSpuLetterBox)
+ return lround((value * 3.0) / 4.0 + 72.0);
+ else
+ return value;
+}
+
+int cDvbSpuDecoder::ScaleYres(int value)
+{
+ if (scaleMode == eSpuLetterBox)
+ return lround((value * 3.0) / 4.0);
+ else
+ return value;
+}
+
+void cDvbSpuDecoder::DrawBmp(sDvbSpuRect & size, cBitmap * bmp)
+{
+ osd->Create(size.x1, size.y1, size.width(), size.height(), 2, false);
+ osd->SetBitmap(size.x1, size.y1, *bmp);
+ delete bmp;
+}
+
+void cDvbSpuDecoder::Draw(void)
+{
+ Hide();
+
+ if (!spubmp)
+ return;
+
+ cBitmap *fg = NULL;
+ cBitmap *bg = NULL;
+ sDvbSpuRect bgsize;
+ sDvbSpuRect hlsize;
+
+ hlsize.x1 = hlpsize.x1;
+ hlsize.y1 = ScaleYcoord(hlpsize.y1);
+ hlsize.x2 = hlpsize.x2;
+ hlsize.y2 = ScaleYcoord(hlpsize.y2);
+
+ if (highlight)
+ fg = spubmp->getBitmap(hlpDescr, palette, hlsize);
+
+ if (spubmp->getMinSize(palDescr, bgsize)) {
+ bg = spubmp->getBitmap(palDescr, palette, bgsize);
+ if (scaleMode == eSpuLetterBox) {
+ // the coordinates have to be modified for letterbox
+ int y1 = ScaleYres(bgsize.y1) + bgsize.height();
+ bgsize.y2 = y1 + bgsize.height();
+ bgsize.y1 = y1;
+ }
+ }
+
+ if (bg || fg) {
+ if (osd == NULL)
+ if ((osd = cOsd::OpenRaw(0, 0)) == NULL) {
+ dsyslog("OpenRaw failed\n");
+ return;
+ }
+
+ if (fg)
+ DrawBmp(hlsize, fg);
+
+ if (bg)
+ DrawBmp(bgsize, bg);
+
+ osd->Flush();
+ }
+
+ clean = true;
+}
+
+void cDvbSpuDecoder::Hide(void)
+{
+ delete osd;
+ osd = NULL;
+}
+
+void cDvbSpuDecoder::Empty(void)
+{
+ Hide();
+
+ delete spubmp;
+ spubmp = NULL;
+
+ delete[]spu;
+ spu = NULL;
+
+ clearHighlight();
+ clean = true;
+}
+
+int cDvbSpuDecoder::setTime(uint32_t pts)
+{
+ if (!spu)
+ return 0;
+
+ if (spu && !clean)
+ Draw();
+
+ while (DCSQ_offset != prev_DCSQ_offset) { /* Display Control Sequences */
+ int i = DCSQ_offset;
+ state = spNONE;
+
+ uint32_t exec_time = pts + spuU32(i) * 1024;
+ if ((pts != 0) && (exec_time > pts))
+ return 0;
+ DEBUG("offs = %d, rel = %d, time = %d, pts = %d, diff = %d\n",
+ i, spuU32(i) * 1024, exec_time, pts, exec_time - pts);
+
+ if (pts != 0) {
+ uint16_t feven = 0;
+ uint16_t fodd = 0;
+
+ i += 2;
+
+ prev_DCSQ_offset = DCSQ_offset;
+ DCSQ_offset = spuU32(i);
+ DEBUG("offs = %d, DCSQ = %d\n", i, DCSQ_offset);
+ i += 2;
+
+ while (spu[i] != CMD_SPU_EOF) { // Command Sequence
+ switch (spu[i]) {
+ case CMD_SPU_SHOW: // show subpicture
+ DEBUG("\tshow subpicture\n");
+ state = spSHOW;
+ i++;
+ break;
+
+ case CMD_SPU_HIDE: // hide subpicture
+ DEBUG("\thide subpicture\n");
+ state = spHIDE;
+ i++;
+ break;
+
+ case CMD_SPU_SET_PALETTE: // CLUT
+ palDescr[0].index = spu[i + 2] & 0xf;
+ palDescr[1].index = spu[i + 2] >> 4;
+ palDescr[2].index = spu[i + 1] & 0xf;
+ palDescr[3].index = spu[i + 1] >> 4;
+ i += 3;
+ break;
+
+ case CMD_SPU_SET_ALPHA: // transparency palette
+ palDescr[0].trans = spu[i + 2] & 0xf;
+ palDescr[1].trans = spu[i + 2] >> 4;
+ palDescr[2].trans = spu[i + 1] & 0xf;
+ palDescr[3].trans = spu[i + 1] >> 4;
+ i += 3;
+ break;
+
+ case CMD_SPU_SET_SIZE: // image coordinates
+ size.x1 = (spu[i + 1] << 4) | (spu[i + 2] >> 4);
+ size.x2 = ((spu[i + 2] & 0x0f) << 8) | spu[i + 3];
+
+ size.y1 = (spu[i + 4] << 4) | (spu[i + 5] >> 4);
+ size.y2 = ((spu[i + 5] & 0x0f) << 8) | spu[i + 6];
+
+ DEBUG("\t(%d, %d) x (%d, %d)\n",
+ size.x1, size.y1, size.x2, size.y2);
+ i += 7;
+ break;
+
+ case CMD_SPU_SET_PXD_OFFSET: // image 1 / image 2 offsets
+ fodd = spuU32(i + 1);
+ feven = spuU32(i + 3);
+ DEBUG("\todd = %d even = %d\n", fodd, feven);
+ i += 5;
+ break;
+
+ case CMD_SPU_MENU:
+ DEBUG("\tspu menu\n");
+ state = spMENU;
+
+ i++;
+ break;
+
+ default:
+ esyslog("invalid sequence in control header (%.2x)\n",
+ spu[i]);
+ assert(0);
+ i++;
+ break;
+ }
+ }
+ if (fodd != 0 && feven != 0) {
+ delete spubmp;
+ spubmp = new cDvbSpuBitmap(size, spu + fodd, spu + feven,
+ spu + feven, spu + cmdOffs());
+ }
+ } else if (!clean)
+ state = spSHOW;
+
+ if (state == spSHOW || state == spMENU)
+ Draw();
+
+ if (state == spHIDE)
+ Hide();
+
+ if (pts == 0)
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/dvbspu.h b/dvbspu.h
new file mode 100644
index 0000000..5ac2d0a
--- /dev/null
+++ b/dvbspu.h
@@ -0,0 +1,204 @@
+/*
+ * SPU decoder for DVB devices
+ *
+ * Copyright (C) 2001.2002 Andreas Schultz <aschultz@warp10.net>
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ *
+ * parts of this file are derived from the OMS program.
+ *
+ * $Id: dvbspu.h 1.1 2002/09/08 14:17:38 kls Exp $
+ */
+
+#ifndef __DVBSPU_H
+#define __DVBSPU_H
+
+#include <inttypes.h>
+
+#include "osdbase.h"
+#include "spu.h"
+
+typedef struct sDvbSpuPalDescr {
+ uint8_t index;
+ uint8_t trans;
+
+ bool operator != (const sDvbSpuPalDescr pd) const {
+ return index != pd.index && trans != pd.trans;
+ };
+} aDvbSpuPalDescr[4];
+
+typedef struct sDvbSpuRect {
+ int x1, y1;
+ int x2, y2;
+
+ int width() {
+ return x2 - x1 + 1;
+ };
+ int height() {
+ return y2 - y1 + 1;
+ };
+
+ bool operator != (const sDvbSpuRect r) const {
+ return r.x1 != x1 || r.y1 != y1 || r.x2 != x2 || r.y2 != y2;
+ };
+}
+
+sDvbSpuRect;
+
+// --- cDvbSpuPalette----------------------------------
+
+class cDvbSpuPalette {
+ private:
+ uint32_t palette[16];
+
+ private:
+ uint32_t yuv2rgb(uint32_t yuv_color);
+
+ public:
+ void setPalette(const uint32_t * pal);
+ uint32_t getColor(uint8_t idx, uint8_t trans) const;
+};
+
+// --- cDvbSpuBitmap----------------------------------
+
+class cDvbSpuBitmap {
+
+ public:
+ private:
+ sDvbSpuRect bmpsize;
+ sDvbSpuRect minsize[4];
+ uint8_t *bmp;
+
+ private:
+ void putPixel(int xp, int yp, int len, uint8_t colorid);
+ void putFieldData(int field, uint8_t * data, uint8_t * endp);
+
+ public:
+ cDvbSpuBitmap(sDvbSpuRect size,
+ uint8_t * fodd, uint8_t * eodd,
+ uint8_t * feven, uint8_t * eeven);
+ ~cDvbSpuBitmap();
+
+ bool getMinSize(const aDvbSpuPalDescr paldescr,
+ sDvbSpuRect & size) const;
+ cBitmap *getBitmap(const aDvbSpuPalDescr paldescr,
+ const cDvbSpuPalette & pal,
+ sDvbSpuRect & size) const;
+};
+
+// --- cDvbSpuDecoder------------------------------------
+
+class cDvbSpuDecoder:public cSpuDecoder {
+ private:
+ cOsdBase * osd;
+
+ // processing state
+ uint8_t *spu;
+ bool clean;
+ bool ready;
+
+ enum spFlag { spNONE, spHIDE, spSHOW, spMENU };
+ spFlag state;
+
+ cSpuDecoder::eScaleMode scaleMode;
+
+ //highligh area
+ bool highlight;
+ sDvbSpuRect hlpsize;
+ aDvbSpuPalDescr hlpDescr;
+
+ //palette
+ cDvbSpuPalette palette;
+
+ // spu info's
+ sDvbSpuRect size;
+ aDvbSpuPalDescr palDescr;
+
+ uint16_t DCSQ_offset;
+ uint16_t prev_DCSQ_offset;
+
+ cDvbSpuBitmap *spubmp;
+ private:
+ int cmdOffs(void) {
+ return ((spu[2] << 8) | spu[3]);
+ };
+ int spuSize(void) {
+ return ((spu[0] << 8) | spu[1]);
+ };
+
+ int ScaleYcoord(int value);
+ int ScaleYres(int value);
+ void DrawBmp(sDvbSpuRect & size, cBitmap * bmp);
+
+ void Draw();
+ void Hide();
+
+ public:
+ cDvbSpuDecoder();
+ ~cDvbSpuDecoder();
+
+ int setTime(uint32_t pts);
+
+ void setScaleMode(cSpuDecoder::eScaleMode ScaleMode);
+ void setPalette(uint32_t * pal);
+ void setHighlight(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey,
+ uint32_t palette);
+ void clearHighlight(void);
+ void Empty(void);
+ void processSPU(uint32_t pts, uint8_t * buf);
+};
+
+// --- cDvbSpuPalette -------------------------------------------
+
+inline uint32_t cDvbSpuPalette::yuv2rgb(uint32_t yuv_color)
+{
+ int Y, Cb, Cr;
+ int Ey, Epb, Epr;
+ int Eg, Eb, Er;
+ uint32_t result;
+
+ Y = (yuv_color >> 16) & 0xff;
+ Cb = (yuv_color) & 0xff;
+ Cr = (yuv_color >> 8) & 0xff;
+
+ Ey = (Y - 16);
+ Epb = (Cb - 128);
+ Epr = (Cr - 128);
+ /* ITU-R 709
+ Eg = (298*Ey - 55*Epb - 137*Epr)/256;
+ Eb = (298*Ey + 543*Epb)/256;
+ Er = (298*Ey + 460*Epr)/256;
+ */
+ /* FCC ~= mediaLib */
+ Eg = (298 * Ey - 100 * Epb - 208 * Epr) / 256;
+ Eb = (298 * Ey + 516 * Epb) / 256;
+ Er = (298 * Ey + 408 * Epr) / 256;
+
+ if (Eg > 255)
+ Eg = 255;
+ if (Eg < 0)
+ Eg = 0;
+
+ if (Eb > 255)
+ Eb = 255;
+ if (Eb < 0)
+ Eb = 0;
+
+ if (Er > 255)
+ Er = 255;
+ if (Er < 0)
+ Er = 0;
+
+ result = (Eb << 16) | (Eg << 8) | Er;
+
+ return result;
+}
+
+inline uint32_t cDvbSpuPalette::getColor(uint8_t idx, uint8_t trans) const
+{
+ uint8_t t = trans == 0x0f ? 0xff : trans << 4;
+ return palette[idx] | (t << 24);
+}
+
+#endif // __DVBSPU_H
diff --git a/eitscan.c b/eitscan.c
index 7a2000d..069a133 100644
--- a/eitscan.c
+++ b/eitscan.c
@@ -4,11 +4,12 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: eitscan.c 1.5 2002/08/11 11:11:39 kls Exp $
+ * $Id: eitscan.c 1.7 2002/09/08 11:08:52 kls Exp $
*/
#include "eitscan.h"
#include <stdlib.h>
+#include "dvbdevice.h"
cEITScanner::cEITScanner(void)
{
@@ -49,9 +50,9 @@ void cEITScanner::Process(void)
if (Setup.EPGScanTimeout && Channels.MaxNumber() > 1) {
time_t now = time(NULL);
if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) {
- for (int i = 0; i < MAXDEVICES; i++) {
- cDevice *Device = cDevice::GetDevice(i + 1, MAXPRIORITY + 1);
- if (Device) {
+ for (int i = 0; i < cDevice::NumDevices(); i++) {
+ cDevice *Device = cDevice::GetDevice(i);
+ if (Device && Device->CardIndex() < MAXDVBDEVICES) {
if (Device != cDevice::PrimaryDevice() || (cDevice::NumDevices() == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) {
if (!(Device->Receiving() || Device->Replaying())) {
int oldCh = lastChannel;
@@ -63,12 +64,12 @@ void cEITScanner::Process(void)
}
cChannel *Channel = Channels.GetByNumber(ch);
if (Channel) {
- if (Channel->ca <= MAXDEVICES && !Device->ProvidesCa(Channel->ca))
- break; // the channel says it explicitly needs a different card
+ if (!Device->ProvidesChannel(Channel))
+ break;
if (Channel->pnr && !TransponderScanned(Channel)) {
if (Device == cDevice::PrimaryDevice() && !currentChannel)
- currentChannel = Device->Channel();
- Channel->Switch(Device, false);
+ currentChannel = Device->CurrentChannel();
+ Device->SwitchChannel(Channel, false);
lastChannel = ch;
break;
}
@@ -78,6 +79,8 @@ void cEITScanner::Process(void)
}
}
}
+ else
+ lastChannel++; // avoid hangup in case the last channel in the list is not provided by a DVB card
}
lastScan = time(NULL);
}
diff --git a/menu.c b/menu.c
index ea10a36..7d13055 100644
--- a/menu.c
+++ b/menu.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.c 1.206 2002/08/25 10:56:09 kls Exp $
+ * $Id: menu.c 1.208 2002/09/06 14:07:58 kls Exp $
*/
#include "menu.h"
@@ -508,7 +508,7 @@ eOSState cMenuChannels::Switch(void)
{
cChannel *ch = Channels.Get(Current());
if (ch)
- ch->Switch();
+ cDevice::PrimaryDevice()->SwitchChannel(ch, true);
return osEnd;
}
@@ -1054,7 +1054,7 @@ eOSState cMenuWhatsOn::Switch(void)
cMenuWhatsOnItem *item = (cMenuWhatsOnItem *)Get(Current());
if (item) {
cChannel *channel = Channels.GetByServiceID(item->eventInfo->GetServiceID());
- if (channel && channel->Switch())
+ if (channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true))
return osEnd;
}
Interface->Error(tr("Can't switch channel!"));
@@ -2519,15 +2519,14 @@ bool cRecordControls::Start(cTimer *Timer)
cChannel *channel = Channels.GetByNumber(ch);
if (channel) {
- bool ReUse = false;
- cDevice *device = cDevice::GetDevice(channel->ca, Timer ? Timer->priority : Setup.DefaultPriority, channel->frequency, channel->vpid, &ReUse);
+ bool NeedsDetachReceivers = false;
+ cDevice *device = cDevice::GetDevice(channel, Timer ? Timer->priority : Setup.DefaultPriority, &NeedsDetachReceivers);
if (device) {
- if (!ReUse) {
+ if (NeedsDetachReceivers)
Stop(device);
- if (!channel->Switch(device)) {
- cThread::EmergencyExit(true);
- return false;
- }
+ if (!device->SwitchChannel(channel, false)) {
+ cThread::EmergencyExit(true);
+ return false;
}
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (!RecordControls[i]) {
@@ -2570,7 +2569,8 @@ void cRecordControls::Stop(cDevice *Device)
bool cRecordControls::StopPrimary(bool DoIt)
{
if (cDevice::PrimaryDevice()->Receiving()) {
- cDevice *device = cDevice::GetDevice(cDevice::PrimaryDevice()->Ca(), 0);
+ //XXX+ disabled for the moment - might become obsolete with DVB_DRIVER_VERSION >= 2002090101
+ cDevice *device = NULL;//XXX cDevice::GetDevice(cDevice::PrimaryDevice()->Ca(), 0);
if (device) {
if (DoIt)
Stop(cDevice::PrimaryDevice());
diff --git a/menuitems.c b/menuitems.c
index 8c7848f..97d81ad 100644
--- a/menuitems.c
+++ b/menuitems.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menuitems.c 1.8 2002/08/15 11:27:57 kls Exp $
+ * $Id: menuitems.c 1.9 2002/09/08 14:51:28 kls Exp $
*/
#include "menuitems.h"
@@ -68,7 +68,7 @@ eOSState cMenuEditIntItem::ProcessKey(eKeys Key)
*value = 0;
fresh = false;
}
- newValue = *value * 10 + (Key - k0);
+ newValue = *value * 10 + (Key - k0);
}
else if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
newValue = *value - 1;
@@ -78,8 +78,11 @@ eOSState cMenuEditIntItem::ProcessKey(eKeys Key)
newValue = *value + 1;
fresh = true;
}
- else
+ else {
+ if (*value < min) { *value = min; Set(); }
+ if (*value > max) { *value = max; Set(); }
return state;
+ }
if ((!fresh || min <= newValue) && newValue <= max) {
*value = newValue;
Set();
diff --git a/newplugin b/newplugin
index 2612c85..55d425f 100755
--- a/newplugin
+++ b/newplugin
@@ -12,7 +12,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
-# $Id: newplugin 1.6 2002/06/10 16:24:34 kls Exp $
+# $Id: newplugin 1.7 2002/08/28 19:26:27 kls Exp $
$PLUGIN_NAME = $ARGV[0] || die "Usage: newplugin <name>\n";
@@ -122,7 +122,7 @@ libvdr-\$(PLUGIN).so: \$(OBJS)
\$(CXX) \$(CXXFLAGS) -shared \$(OBJS) -o \$\@
\@cp \$\@ \$(LIBDIR)/\$\@.\$(VDRVERSION)
-package: clean
+dist: clean
\@-rm -rf \$(TMPDIR)/\$(ARCHIVE)
\@mkdir \$(TMPDIR)/\$(ARCHIVE)
\@cp -a * \$(TMPDIR)/\$(ARCHIVE)
@@ -251,7 +251,7 @@ The next steps you should perform now are:
sub CreateFile
{
my ($Name, $Content) = @_;
- open(FILE, ">$PLUGINDIR/$Name") || die "$Name: V113 $!\n";
+ open(FILE, ">$PLUGINDIR/$Name") || die "$Name: $!\n";
print FILE $Content;
close(FILE);
}
diff --git a/osdbase.h b/osdbase.h
index 3922126..605bd8e 100644
--- a/osdbase.h
+++ b/osdbase.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osdbase.h 1.5 2002/08/25 10:01:00 kls Exp $
+ * $Id: osdbase.h 1.6 2002/09/08 14:12:41 kls Exp $
*/
#ifndef __OSDBASE_H
@@ -50,12 +50,12 @@ private:
bool full;
protected:
typedef unsigned char tIndexes[MAXNUMCOLORS];
- void SetColor(int Index, eDvbColor Color);
- eDvbColor GetColor(int Index) { return color[Index]; }
public:
cPalette(int Bpp);
int Index(eDvbColor Color);
void Reset(void);
+ void SetColor(int Index, eDvbColor Color);
+ eDvbColor GetColor(int Index) { return Index < maxColors ? color[Index] : clrBlack; }
const eDvbColor *NewColors(int &FirstColor, int &LastColor);
// With every call this function returns a consecutive range of
// color entries that have been added since the last call. The
diff --git a/recorder.c b/recorder.c
index 3a0941b..ba7dc06 100644
--- a/recorder.c
+++ b/recorder.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: recorder.c 1.1 2002/06/16 10:03:25 kls Exp $
+ * $Id: recorder.c 1.2 2002/09/07 13:40:23 kls Exp $
*/
#include <stdarg.h>
@@ -14,7 +14,7 @@
// The size of the array used to buffer video data:
// (must be larger than MINVIDEODATA - see remux.h)
-#define VIDEOBUFSIZE MEGABYTE(1)
+#define VIDEOBUFSIZE MEGABYTE(5)
#define MINFREEDISKSPACE (512) // MB
#define DISKCHECKINTERVAL 100 // seconds
diff --git a/spu.c b/spu.c
new file mode 100644
index 0000000..ccdcc39
--- /dev/null
+++ b/spu.c
@@ -0,0 +1,23 @@
+/*
+ * SPU Decoder Prototype
+ *
+ * Copyright (C) 2001.2002 Andreas Schultz <aschultz@warp10.net>
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ *
+ * $Id: spu.c 1.1 2002/09/08 14:17:41 kls Exp $
+ */
+
+#include <inttypes.h>
+#include "spu.h"
+
+// -- cSpuDecoder ----------------
+/*
+cSpuDecoder::cSpuDecoder()
+{};
+*/
+
+cSpuDecoder::~cSpuDecoder()
+{
+};
diff --git a/spu.h b/spu.h
new file mode 100644
index 0000000..1ccc74b
--- /dev/null
+++ b/spu.h
@@ -0,0 +1,38 @@
+/*
+ * SPU Decoder Prototype
+ *
+ * Copyright (C) 2001.2002 Andreas Schultz <aschultz@warp10.net>
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ *
+ * $Id: spu.h 1.1 2002/09/08 14:17:51 kls Exp $
+ */
+
+#ifndef __SPU_VDR_H
+#define __SPU_VDR_H
+
+#include <inttypes.h>
+
+// --- cSpuDecoder -------------------------------------------
+
+class cSpuDecoder {
+ public:
+ typedef enum { eSpuNormal, eSpuLetterBox, eSpuPanAndScan } eScaleMode;
+ public:
+ // cSpuDecoder();
+ virtual ~ cSpuDecoder();
+
+ virtual int setTime(uint32_t pts) = 0;
+
+ virtual void setScaleMode(cSpuDecoder::eScaleMode ScaleMode) = 0;
+ virtual void setPalette(uint32_t * pal) = 0;
+ virtual void setHighlight(uint16_t sx, uint16_t sy,
+ uint16_t ex, uint16_t ey,
+ uint32_t palette) = 0;
+ virtual void clearHighlight(void) = 0;
+ virtual void Empty(void) = 0;
+ virtual void processSPU(uint32_t pts, uint8_t * buf) = 0;
+};
+
+#endif // __SPU_VDR_H
diff --git a/svdrp.c b/svdrp.c
index 92365e7..3115349 100644
--- a/svdrp.c
+++ b/svdrp.c
@@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection.
*
- * $Id: svdrp.c 1.40 2002/08/25 10:40:46 kls Exp $
+ * $Id: svdrp.c 1.42 2002/09/08 11:22:57 kls Exp $
*/
#include "svdrp.h"
@@ -387,6 +387,7 @@ void cSVDRP::CmdCHAN(const char *Option)
{
if (*Option) {
int n = -1;
+ int d = 0;
if (isnumber(Option)) {
int o = strtol(Option, NULL, 10);
if (o >= 1 && o <= Channels.MaxNumber())
@@ -394,13 +395,17 @@ void cSVDRP::CmdCHAN(const char *Option)
}
else if (strcmp(Option, "-") == 0) {
n = cDevice::CurrentChannel();
- if (n > 1)
+ if (n > 1) {
n--;
+ d = -1;
+ }
}
else if (strcmp(Option, "+") == 0) {
n = cDevice::CurrentChannel();
- if (n < Channels.MaxNumber())
+ if (n < Channels.MaxNumber()) {
n++;
+ d = 1;
+ }
}
else {
int i = 1;
@@ -417,21 +422,21 @@ void cSVDRP::CmdCHAN(const char *Option)
Reply(501, "Undefined channel \"%s\"", Option);
return;
}
- if (Interface->Recording()) {
- Reply(550, "Can't switch channel, interface is recording");
- return;
- }
- cChannel *channel = Channels.GetByNumber(n);
- if (channel) {
- if (!channel->Switch()) {
- Reply(554, "Error switching to channel \"%d\"", channel->number);
+ if (!d) {
+ cChannel *channel = Channels.GetByNumber(n);
+ if (channel) {
+ if (!cDevice::PrimaryDevice()->SwitchChannel(channel, true)) {
+ Reply(554, "Error switching to channel \"%d\"", channel->number);
+ return;
+ }
+ }
+ else {
+ Reply(550, "Unable to find channel \"%s\"", Option);
return;
}
}
- else {
- Reply(550, "Unable to find channel \"%s\"", Option);
- return;
- }
+ else
+ cDevice::SwitchChannel(d);
}
cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
if (channel)
diff --git a/tools.h b/tools.h
index ebabb47..f8bf43f 100644
--- a/tools.h
+++ b/tools.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: tools.h 1.49 2002/08/16 08:52:01 kls Exp $
+ * $Id: tools.h 1.50 2002/09/08 10:22:29 kls Exp $
*/
#ifndef __TOOLS_H
@@ -46,6 +46,7 @@ extern int SysLogLevel;
template<class T> inline T min(T a, T b) { return a <= b ? a : b; }
template<class T> inline T max(T a, T b) { return a >= b ? a : b; }
+template<class T> inline int sgn(T a) { return a < 0 ? -1 : a > 0 ? 1 : 0; }
template<class T> inline void swap(T &a, T &b) { T t = a; a = b; b = t; }
ssize_t safe_read(int filedes, void *buffer, size_t size);
diff --git a/vdr.c b/vdr.c
index 9da5b5e..355a515 100644
--- a/vdr.c
+++ b/vdr.c
@@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/people/kls/vdr
*
- * $Id: vdr.c 1.120 2002/08/16 09:54:03 kls Exp $
+ * $Id: vdr.c 1.122 2002/09/08 11:19:01 kls Exp $
*/
#include <getopt.h>
@@ -544,13 +544,9 @@ int main(int argc, char *argv[])
case kUp|k_Repeat:
case kUp:
case kDown|k_Repeat:
- case kDown: {
- int n = cDevice::CurrentChannel() + (NORMALKEY(key) == kUp ? 1 : -1);
- cChannel *channel = Channels.GetByNumber(n);
- if (channel)
- channel->Switch();
+ case kDown:
+ cDevice::SwitchChannel(NORMALKEY(key) == kUp ? 1 : -1);
break;
- }
// Viewing Control:
case kOk: LastChannel = -1; break; // forces channel display
default: break;
diff --git a/xd b/xd
deleted file mode 100644
index a2cff6f..0000000
--- a/xd
+++ /dev/null
@@ -1 +0,0 @@
-rm x.diff