1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Totally rearranged device/player/recorder structures

This commit is contained in:
Klaus Schmidinger 2002-06-16 12:57:31 +02:00
parent b005b8fc4a
commit a4bfddd2f9
34 changed files with 3144 additions and 311 deletions

21
HISTORY
View File

@ -1301,7 +1301,7 @@ Video Disk Recorder Revision History
- Removed compiler option '-m486' to make it work on non-Intel platforms (thanks
to Alastair McKinstry for pointing this out).
2002-05-26: Version 1.1.3
2002-06-16: Version 1.1.3
- Improved the VDR Makefile to avoid a warning if the '.dependencies' file does
not exist, and also using $(MAKE) to call recursive makes.
@ -1328,3 +1328,22 @@ Video Disk Recorder Revision History
- Removed compiler option '-m486' to make it work on non-Intel platforms. If you
have already started a plugin project, you may want to make sure you remove this
option from your existing Makefile.
- Completely rearranged the recording and replay functions to make them available
to plugins.
- Replay is now done in a single thread (no more syncing between input and output
thread necessary).
- It is now possible to record several channels on the same transponder with "budget
cards". VDR automatically attaches a recording timer to a card that already
records on the appropriate transponder. How many parallel recordings can actually
be done depends on the computer's performance. Currently any number of recordings
gets attached to a card, so you should carefully plan your timers to not exceed
the limit. On a K6-II/450 it was possible to record three channels from transponder
12480 with a single WinTV NOVA-S.
- Timers that record two successive shows on the same channel may now overlap and
will use the same DVB card. During the time where both timers record the data
is simply saved to both files.
- The following limitations apply to this version:
+ Transfer mode doesn't work yet.
+ The '-a' option (for Dolby Digital audio) doesn't work yet.
+ Switching between different language tracks doesn't work yet.
+ Cutting doesn't work yet.

View File

@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
# $Id: Makefile 1.39 2002/06/10 16:26:51 kls Exp $
# $Id: Makefile 1.40 2002/06/10 16:31:34 kls Exp $
.DELETE_ON_ERROR:
@ -21,9 +21,10 @@ INCLUDES = -I$(DVBDIR)/ost/include
DTVLIB = $(DTVDIR)/libdtv.a
OBJS = config.o dvbapi.o dvbosd.o eit.o eitscan.o font.o i18n.o interface.o menu.o\
menuitems.o osdbase.o osd.o plugin.o recording.o remote.o remux.o ringbuffer.o\
status.o svdrp.o thread.o tools.o vdr.o videodir.o
OBJS = audio.o config.o device.o dvbplayer.o dvbosd.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 vdr.o videodir.o
OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1

View File

@ -864,15 +864,15 @@ If a plugin wants to get informed on various events in VDR, it can derive a clas
class cMyStatusMonitor : public cStatusMonitor {
protected:
virtual void ChannelSwitch(const cDvbApi *DvbApi, int ChannelNumber);
virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber);
};
void cMyStatusMonitor::ChannelSwitch(const cDvbApi *DvbApi, int ChannelNumber)
void cMyStatusMonitor::ChannelSwitch(const cDevice *Device, int ChannelNumber)
{
if (ChannelNumber)
dsyslog("channel switched to %d on DVB %d", ChannelNumber, DvbApi->CardIndex());
dsyslog("channel switched to %d on DVB %d", ChannelNumber, Device->CardIndex());
else
dsyslog("about to switch channel on DVB %d", DvbApi->CardIndex());
dsyslog("about to switch channel on DVB %d", Device->CardIndex());
}
</pre></td></tr></table><p>

11
audio.c Normal file
View File

@ -0,0 +1,11 @@
/*
* audio.c: The basic audio interface
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: audio.c 1.1 2002/06/10 16:30:00 kls Exp $
*/
#include "audio.h"

13
audio.h Normal file
View File

@ -0,0 +1,13 @@
/*
* audio.h: The basic audio interface
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: audio.h 1.1 2002/06/10 16:30:00 kls Exp $
*/
#ifndef __AUDIO_H
#define __AUDIO_H
#endif //__AUDIO_H

View File

@ -4,16 +4,16 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.c 1.101 2002/05/13 16:28:12 kls Exp $
* $Id: config.c 1.102 2002/06/16 12:57:31 kls Exp $
*/
#include "config.h"
#include <ctype.h>
#include <stdlib.h>
#include "dvbapi.h"
#include "i18n.h"
#include "interface.h"
#include "plugin.h"
#include "recording.h"
// IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
// format characters in order to allow any number of blanks after a numeric
@ -293,15 +293,15 @@ bool cChannel::Save(FILE *f)
return fprintf(f, ToText()) > 0;
}
bool cChannel::Switch(cDvbApi *DvbApi, bool Log)
bool cChannel::Switch(cDevice *Device, bool Log)
{
if (!DvbApi)
DvbApi = cDvbApi::PrimaryDvbApi;
if (!DvbApi->Recording() && !groupSep) {
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 (DvbApi->SetChannel(number, frequency, polarization, diseqc, srate, vpid, apid1, apid2, dpid1, dpid2, tpid, ca, pnr)) {
switch (Device->SetChannel(number, frequency, polarization, diseqc, srate, vpid, apid1, tpid, ca, pnr)) {
case scrOk: return true;
case scrNoTransfer: if (Interface)
Interface->Error(tr("Can't start Transfer Mode!"));
@ -312,7 +312,7 @@ bool cChannel::Switch(cDvbApi *DvbApi, bool Log)
}
return false;
}
if (DvbApi->Recording())
if (Device->IsPrimaryDevice() && Device->Receiving())
Interface->Error(tr("Channel locked (recording)!"));
return false;
}
@ -326,7 +326,7 @@ cTimer::cTimer(bool Instant)
startTime = stopTime = 0;
recording = pending = false;
active = Instant ? taActInst : taInactive;
cChannel *ch = Channels.GetByNumber(cDvbApi::CurrentChannel());
cChannel *ch = Channels.GetByNumber(cDevice::CurrentChannel());
channel = ch ? ch->number : 0;
time_t t = time(NULL);
struct tm tm_r;
@ -836,10 +836,10 @@ cChannel *cChannels::GetByServiceID(unsigned short ServiceId)
return NULL;
}
bool cChannels::SwitchTo(int Number, cDvbApi *DvbApi)
bool cChannels::SwitchTo(int Number, cDevice *Device)
{
cChannel *channel = GetByNumber(Number);
return channel && channel->Switch(DvbApi);
return channel && channel->Switch(Device);
}
const char *cChannels::GetChannelNameByNumber(int Number)
@ -957,6 +957,7 @@ bool cSetupLine::operator< (const cListObject &ListObject)
bool cSetupLine::Parse(char *s)
{
//dsyslog("cSetupLine::Parse '%s'", s);//XXX-
char *p = strchr(s, '=');
if (p) {
*p = 0;
@ -974,6 +975,7 @@ bool cSetupLine::Parse(char *s)
}
name = strdup(Name);
value = strdup(Value);
//dsyslog("cSetupLine::Parse '%s' = '%s'", name, value);//XXX-
return true;
}
}
@ -982,6 +984,7 @@ bool cSetupLine::Parse(char *s)
bool cSetupLine::Save(FILE *f)
{
//dsyslog("cSetupLine::Save '%s' = '%s'", name, value);//XXX-
return fprintf(f, "%s%s%s = %s\n", plugin ? plugin : "", plugin ? "." : "", name, value) > 0;
}
@ -1095,7 +1098,7 @@ bool cSetup::Load(const char *FileName)
void cSetup::StoreCaCaps(const char *Name)
{
for (int d = 0; d < MAXDVBAPI; d++) {
for (int d = 0; d < MAXDEVICES; d++) {
char buffer[MAXPARSEBUFFER];
char *q = buffer;
*buffer = 0;
@ -1115,7 +1118,7 @@ bool cSetup::ParseCaCaps(const char *Value)
{
char *p;
int d = strtol(Value, &p, 10);
if (d > 0 && d <= MAXDVBAPI) {
if (d > 0 && d <= MAXDEVICES) {
d--;
int i = 0;
while (p != Value && p && *p) {

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.h 1.117 2002/05/14 21:21:53 kls Exp $
* $Id: config.h 1.118 2002/06/10 16:30:00 kls Exp $
*/
#ifndef __CONFIG_H
@ -15,7 +15,7 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "dvbapi.h"
#include "device.h"
#include "eit.h"
#include "tools.h"
@ -118,7 +118,7 @@ public:
const char *ToText(void);
bool Parse(const char *s);
bool Save(FILE *f);
bool Switch(cDvbApi *DvbApi = NULL, bool Log = true);
bool Switch(cDevice *Device = NULL, bool Log = true);
};
enum eTimerActive { taInactive = 0,
@ -294,7 +294,7 @@ public:
cChannel *GetByNumber(int Number);
cChannel *GetByServiceID(unsigned short ServiceId);
const char *GetChannelNameByNumber(int Number);
bool SwitchTo(int Number, cDvbApi *DvbApi = NULL);
bool SwitchTo(int Number, cDevice *Device = NULL);
int MaxNumber(void) { return maxNumber; }
};
@ -385,7 +385,7 @@ public:
int MinEventTimeout, MinUserInactivity;
int MultiSpeedMode;
int ShowReplayMode;
int CaCaps[MAXDVBAPI][MAXCACAPS];
int CaCaps[MAXDEVICES][MAXCACAPS];
int CurrentChannel;
int CurrentVolume;
int __EndData__;

1017
device.c Normal file

File diff suppressed because it is too large Load Diff

199
device.h Normal file
View File

@ -0,0 +1,199 @@
/*
* device.h: The basic device interface
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: device.h 1.1 2002/06/10 16:30:00 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"
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 MAXVOLUME 255
#define VOLUMEDELTA 5 // used to increase/decrease the volume
class cPlayer;
class cReceiver;
class cDevice : cThread {
friend class cOsd;//XXX
private:
static int numDevices;
static int useDevice;
static cDevice *device[MAXDEVICES];
static cDevice *primaryDevice;
public:
static int NumDevices(void) { return numDevices; }
// Returns the total number of DVB 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
// will be used.
static bool SetPrimaryDevice(int n);
// Sets the primary DVB 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.
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.
// 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
// 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 Shutdown(void);
// Closes down all DVB devices.
// Must be called at the end of the program.
private:
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);
virtual ~cDevice();
bool IsPrimaryDevice(void) { 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
// 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,
// 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; }
// Channel facilities
private:
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);
static int CurrentChannel(void) { return primaryDevice ? primaryDevice->currentChannel : 0; }
int Channel(void) { return currentChannel; }
// PID handle facilities
private:
enum ePidType { ptVideo, ptAudio, ptTeletext, ptDolby, ptOther };
class cPidHandle {
public:
int pid;
int fd;
int used;
cPidHandle(void) { pid = used = 0; fd = -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);
// Image Grab facilities
public:
bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1);
// Video format facilities
public:
virtual void SetVideoFormat(videoFormat_t Format);
// Volume facilities
private:
bool mute;
int volume;
public:
bool IsMute(void) { return mute; }
bool ToggleMute(void);
// Turns the volume off or on and returns the new mute state.
void SetVolume(int Volume, bool Absolute = false);
// Sets the volume to the given value, either absolutely or relative to
// the current volume.
static int CurrentVolume(void) { return primaryDevice ? primaryDevice->volume : 0; }//XXX???
// EIT facilities
private:
cSIProcessor *siProcessor;
// Player facilities
private:
cPlayer *player;
public:
void TrickSpeed(int Speed);
void Clear(void);
void Play(void);
void Freeze(void);
void Mute(void);
void StillPicture(const uchar *Data, int Length);
bool Replaying(void);
// Returns true if we are currently replaying.
void StopReplay(void);
// Stops the current replay session (if any).
bool Attach(cPlayer *Player);
void Detach(cPlayer *Player);
virtual int PlayVideo(const uchar *Data, int Length);
virtual int PlayAudio(const uchar *Data, int Length);
// Receiver facilities
private:
cReceiver *receiver[MAXRECEIVERS];
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
// always return at least Setup.PrimaryLimit-1.
int CanShift(int Ca, int Priority, int UsedCards = 0);
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 Attach(cReceiver *Receiver);
void Detach(cReceiver *Receiver);
};
#endif //__DEVICE_H

733
dvbplayer.c Normal file
View File

@ -0,0 +1,733 @@
/*
* dvbplayer.c: The DVB player
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbplayer.c 1.1 2002/06/16 10:59:45 kls Exp $
*/
#include "dvbplayer.h"
#include <poll.h>
#include "recording.h"
#include "ringbuffer.h"
#include "thread.h"
// --- ReadFrame -------------------------------------------------------------
int ReadFrame(int f, uchar *b, int Length, int Max)
{
if (Length == -1)
Length = Max; // this means we read up to EOF (see cIndex)
else if (Length > Max) {
esyslog("ERROR: frame larger than buffer (%d > %d)", Length, Max);
Length = Max;
}
int r = safe_read(f, b, Length);
if (r < 0)
LOG_ERROR;
return r;
}
// --- cBackTrace ----------------------------------------------------------
#define AVG_FRAME_SIZE 15000 // an assumption about the average frame size
#define DVB_BUF_SIZE (256 * 1024) // an assumption about the dvb firmware buffer size
#define BACKTRACE_ENTRIES (DVB_BUF_SIZE / AVG_FRAME_SIZE + 20) // how many entries are needed to backtrace buffer contents
class cBackTrace {
private:
int index[BACKTRACE_ENTRIES];
int length[BACKTRACE_ENTRIES];
int pos, num;
public:
cBackTrace(void);
void Clear(void);
void Add(int Index, int Length);
int Get(bool Forward);
};
cBackTrace::cBackTrace(void)
{
Clear();
}
void cBackTrace::Clear(void)
{
pos = num = 0;
}
void cBackTrace::Add(int Index, int Length)
{
index[pos] = Index;
length[pos] = Length;
if (++pos >= BACKTRACE_ENTRIES)
pos = 0;
if (num < BACKTRACE_ENTRIES)
num++;
}
int cBackTrace::Get(bool Forward)
{
int p = pos;
int n = num;
int l = DVB_BUF_SIZE + (Forward ? 0 : 256 * 1024); //XXX (256 * 1024) == DVB_BUF_SIZE ???
int i = -1;
while (n && l > 0) {
if (--p < 0)
p = BACKTRACE_ENTRIES - 1;
i = index[p] - 1;
l -= length[p];
n--;
}
return i;
}
// --- cDvbPlayer ------------------------------------------------------------
//XXX+ also used in recorder.c - find a better place???
// The size of the array used to buffer video data:
// (must be larger than MINVIDEODATA - see remux.h)
#define VIDEOBUFSIZE MEGABYTE(1)
// The maximum size of a single frame:
#define MAXFRAMESIZE KILOBYTE(192)
// The number of frames to back up when resuming an interrupted replay session:
#define RESUMEBACKUP (10 * FRAMESPERSEC)
class cDvbPlayer : public cPlayer, cThread {
private:
enum ePlayModes { pmPlay, pmPause, pmSlow, pmFast, pmStill };
enum ePlayDirs { pdForward, pdBackward };
static int Speeds[];
cRingBufferFrame *ringBuffer;
cBackTrace *backTrace;
cFileName *fileName;
cIndexFile *index;
int replayFile;
bool eof;
bool active;
ePlayModes playMode;
ePlayDirs playDir;
int trickSpeed;
int readIndex, writeIndex;
cFrame *readFrame;
const cFrame *playFrame;
void TrickSpeed(int Increment);
void Empty(void);
void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
int Resume(void);
bool Save(void);
protected:
virtual void Activate(bool On);
virtual void Action(void);
public:
cDvbPlayer(const char *FileName);
virtual ~cDvbPlayer();
bool Active(void) { return active; }
void Pause(void);
void Play(void);
void Forward(void);
void Backward(void);
int SkipFrames(int Frames);
void SkipSeconds(int Seconds);
void Goto(int Position, bool Still = false);
void GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
};
#define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
#define NORMAL_SPEED 4 // the index of the '1' entry in the following array
#define MAX_SPEEDS 3 // the offset of the maximum speed from normal speed in either direction
#define SPEED_MULT 12 // the speed multiplier
int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
cDvbPlayer::cDvbPlayer(const char *FileName)
{
ringBuffer = NULL;
backTrace = NULL;
index = NULL;
eof = false;
active = false;
playMode = pmPlay;
playDir = pdForward;
trickSpeed = NORMAL_SPEED;
readIndex = writeIndex = -1;
readFrame = NULL;
playFrame = NULL;
isyslog("replay %s", FileName);
fileName = new cFileName(FileName, false);
replayFile = fileName->Open();
if (replayFile < 0)
return;
ringBuffer = new cRingBufferFrame(VIDEOBUFSIZE);
// Create the index file:
index = new cIndexFile(FileName, false);
if (!index)
esyslog("ERROR: can't allocate index");
else if (!index->Ok()) {
delete index;
index = NULL;
}
backTrace = new cBackTrace;
}
cDvbPlayer::~cDvbPlayer()
{
Detach();
Save();
delete index;
delete fileName;
delete backTrace;
delete ringBuffer;
}
void cDvbPlayer::TrickSpeed(int Increment)
{
int nts = trickSpeed + Increment;
if (Speeds[nts] == 1) {
trickSpeed = nts;
if (playMode == pmFast)
Play();
else
Pause();
}
else if (Speeds[nts]) {
trickSpeed = nts;
int Mult = (playMode == pmSlow && playDir == pdForward) ? 1 : SPEED_MULT;
int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult;
if (sp > MAX_VIDEO_SLOWMOTION)
sp = MAX_VIDEO_SLOWMOTION;
DeviceTrickSpeed(sp);
}
}
void cDvbPlayer::Empty(void)
{
Lock();
if ((readIndex = backTrace->Get(playDir == pdForward)) < 0)
readIndex = writeIndex;
readFrame = NULL;
playFrame = NULL;
ringBuffer->Clear();
backTrace->Clear();
DeviceClear();
Unlock();
}
void cDvbPlayer::StripAudioPackets(uchar *b, int Length, uchar Except)
{
if (index) {
for (int i = 0; i < Length - 6; i++) {
if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
uchar c = b[i + 3];
int l = b[i + 4] * 256 + b[i + 5] + 6;
switch (c) {
case 0xBD: // dolby
if (Except)
;//XXX+ PlayExternalDolby(&b[i], Length - i);
// continue with deleting the data - otherwise it disturbs DVB replay
case 0xC0 ... 0xC1: // audio
if (c == 0xC1)
;//XXX+ canToggleAudioTrack = true;
if (!Except || c != Except) {
int n = l;
for (int j = i; j < Length && n--; j++)
b[j] = 0x00;
}
break;
case 0xE0 ... 0xEF: // video
break;
default:
//esyslog("ERROR: unexpected packet id %02X", c);
l = 0;
}
if (l)
i += l - 1; // the loop increments, too!
}
/*XXX
else
esyslog("ERROR: broken packet header");
XXX*/
}
}
}
bool cDvbPlayer::NextFile(uchar FileNumber, int FileOffset)
{
if (FileNumber > 0)
replayFile = fileName->SetOffset(FileNumber, FileOffset);
else if (replayFile >= 0 && eof)
replayFile = fileName->NextFile();
eof = false;
return replayFile >= 0;
}
int cDvbPlayer::Resume(void)
{
if (index) {
int Index = index->GetResume();
if (Index >= 0) {
uchar FileNumber;
int FileOffset;
if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset))
return Index;
}
}
return -1;
}
bool cDvbPlayer::Save(void)
{
if (index) {
int Index = writeIndex;
if (Index >= 0) {
Index -= RESUMEBACKUP;
if (Index > 0)
Index = index->GetNextIFrame(Index, false);
else
Index = 0;
if (Index >= 0)
return index->StoreResume(Index);
}
}
return false;
}
void cDvbPlayer::Activate(bool On)
{
if (On) {
if (replayFile >= 0)
Start();
}
else if (active) {
active = false;
Cancel(3);
}
}
void cDvbPlayer::Action(void)
{
active = true;
dsyslog("dvbplayer thread started (pid=%d)", getpid());
uchar b[MAXFRAMESIZE];
const uchar *p = NULL;
int pc = 0;
pollfd pfd[2];
pfd[0].fd = DeviceFileHandle();
pfd[0].events = pfd[0].revents = POLLOUT;
pfd[1].fd = replayFile;
pfd[1].events = pfd[1].revents = POLLIN;
readIndex = Resume();
if (readIndex >= 0)
isyslog("resuming replay at index %d (%s)", readIndex, IndexToHMSF(readIndex, true));
while (active && NextFile()) {
{
LOCK_THREAD;
// Read the next frame from the file:
if (!readFrame && (pfd[1].revents & POLLIN)) {
if (playMode != pmStill) {
int r = 0;
if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
uchar FileNumber;
int FileOffset, Length;
int Index = index->GetNextIFrame(readIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, true);
if (Index >= 0) {
if (!NextFile(FileNumber, FileOffset))
break;
}
else {
// can't call Play() here, because those functions may only be
// called from the foreground thread - and we also don't need
// to empty the buffer here
DevicePlay();
playMode = pmPlay;
playDir = pdForward;
continue;
}
readIndex = Index;
r = ReadFrame(replayFile, b, Length, sizeof(b));
// must call StripAudioPackets() here because the buffer is not emptied
// when falling back from "fast forward" to "play" (see above)
StripAudioPackets(b, r);
}
else if (index) {
uchar FileNumber;
int FileOffset, Length;
readIndex++;
if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset)))
break;
r = ReadFrame(replayFile, b, Length, sizeof(b));
}
else // allows replay even if the index file is missing
r = read(replayFile, b, sizeof(b));
if (r > 0)
readFrame = new cFrame(b, r, ftUnknown, readIndex);
else if (r == 0)
eof = true;
else if (r < 0 && FATALERRNO) {
LOG_ERROR;
break;
}
}
else//XXX
usleep(1); // this keeps the CPU load low
}
// Store the frame in the buffer:
if (readFrame) {
if (ringBuffer->Put(readFrame))
readFrame = NULL;
}
// Get the next frame from the buffer:
if (!playFrame) {
playFrame = ringBuffer->Get();
p = NULL;
pc = 0;
}
// Play the frame:
if (playFrame && (pfd[0].revents & POLLOUT)) {
if (!p) {
p = playFrame->Data();
pc = playFrame->Count();
}
if (p) {
int w = PlayVideo(p, pc);
if (w > 0) {
p += w;
pc -= w;
}
else if (w < 0 && FATALERRNO) {
LOG_ERROR;
break;
}
}
if (pc == 0) {
writeIndex = playFrame->Index();
backTrace->Add(playFrame->Index(), playFrame->Count());
ringBuffer->Drop(playFrame);
playFrame = NULL;
p = 0;
}
}
}
// Wait for input or output to become ready:
if (poll(pfd, readFrame ? 1 : 2, 10) < 0 && FATALERRNO) {
LOG_ERROR;
break;
}
}
dsyslog("dvbplayer thread ended (pid=%d)", getpid());
active = false;
}
void cDvbPlayer::Pause(void)
{
if (playMode == pmPause || playMode == pmStill)
Play();
else {
LOCK_THREAD;
if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))
Empty();
DeviceFreeze();
playMode = pmPause;
}
}
void cDvbPlayer::Play(void)
{
if (playMode != pmPlay) {
LOCK_THREAD;
if (playMode == pmStill || playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))
Empty();
DevicePlay();
playMode = pmPlay;
playDir = pdForward;
}
}
void cDvbPlayer::Forward(void)
{
if (index) {
switch (playMode) {
case pmFast:
if (Setup.MultiSpeedMode) {
TrickSpeed(playDir == pdForward ? 1 : -1);
break;
}
else if (playDir == pdForward) {
Play();
break;
}
// run into pmPlay
case pmPlay: {
LOCK_THREAD;
Empty();
DeviceMute();
playMode = pmFast;
playDir = pdForward;
trickSpeed = NORMAL_SPEED;
TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
}
break;
case pmSlow:
if (Setup.MultiSpeedMode) {
TrickSpeed(playDir == pdForward ? -1 : 1);
break;
}
else if (playDir == pdForward) {
Pause();
break;
}
// run into pmPause
case pmStill:
case pmPause:
DeviceMute();
playMode = pmSlow;
playDir = pdForward;
trickSpeed = NORMAL_SPEED;
TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
break;
}
}
}
void cDvbPlayer::Backward(void)
{
if (index) {
switch (playMode) {
case pmFast:
if (Setup.MultiSpeedMode) {
TrickSpeed(playDir == pdBackward ? 1 : -1);
break;
}
else if (playDir == pdBackward) {
Play();
break;
}
// run into pmPlay
case pmPlay: {
LOCK_THREAD;
Empty();
DeviceMute();
playMode = pmFast;
playDir = pdBackward;
trickSpeed = NORMAL_SPEED;
TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
}
break;
case pmSlow:
if (Setup.MultiSpeedMode) {
TrickSpeed(playDir == pdBackward ? -1 : 1);
break;
}
else if (playDir == pdBackward) {
Pause();
break;
}
// run into pmPause
case pmStill:
case pmPause: {
LOCK_THREAD;
Empty();
DeviceMute();
playMode = pmSlow;
playDir = pdBackward;
trickSpeed = NORMAL_SPEED;
TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
}
break;
}
}
}
int cDvbPlayer::SkipFrames(int Frames)
{
if (index && Frames) {
int Current, Total;
GetIndex(Current, Total, true);
int OldCurrent = Current;
Current = index->GetNextIFrame(Current + Frames, Frames > 0);
return Current >= 0 ? Current : OldCurrent;
}
return -1;
}
void cDvbPlayer::SkipSeconds(int Seconds)
{
if (index && Seconds) {
LOCK_THREAD;
Empty();
int Index = writeIndex;
if (Index >= 0) {
Index = max(Index + Seconds * FRAMESPERSEC, 0);
if (Index > 0)
Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL, true);
if (Index >= 0)
readIndex = writeIndex = Index - 1; // Input() will first increment it!
}
Play();
}
}
void cDvbPlayer::Goto(int Index, bool Still)
{
if (index) {
LOCK_THREAD;
Empty();
if (++Index <= 0)
Index = 1; // not '0', to allow GetNextIFrame() below to work!
uchar FileNumber;
int FileOffset, Length;
Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
uchar b[MAXFRAMESIZE];
int r = ReadFrame(replayFile, b, Length, sizeof(b));
if (r > 0) {
if (playMode == pmPause)
DevicePlay();
StripAudioPackets(b, r);
DeviceStillPicture(b, r);
}
playMode = pmStill;
}
readIndex = writeIndex = Index;
}
}
void cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
{
if (index) {
if (playMode == pmStill)
Current = max(readIndex, 0);
else {
Current = max(writeIndex, 0);
if (SnapToIFrame) {
int i1 = index->GetNextIFrame(Current + 1, false);
int i2 = index->GetNextIFrame(Current, true);
Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2;
}
}
Total = index->Last();
}
else
Current = Total = -1;
}
bool cDvbPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
{
Play = (playMode == pmPlay || playMode == pmFast);
Forward = (playDir == pdForward);
if (playMode == pmFast || playMode == pmSlow)
Speed = Setup.MultiSpeedMode ? abs(trickSpeed - NORMAL_SPEED) : 0;
else
Speed = -1;
return true;
}
// --- cDvbPlayerControl -----------------------------------------------------
cDvbPlayerControl::cDvbPlayerControl(void)
{
player = NULL;
}
cDvbPlayerControl::~cDvbPlayerControl()
{
Stop();
}
bool cDvbPlayerControl::Active(void)
{
return player && player->Active();
}
bool cDvbPlayerControl::Start(const char *FileName)
{
delete player;
player = new cDvbPlayer(FileName);
if (cDevice::PrimaryDevice()->Attach(player))
return true;
Stop();
return false;
}
void cDvbPlayerControl::Stop(void)
{
delete player;
player = NULL;
}
void cDvbPlayerControl::Pause(void)
{
if (player)
player->Pause();
}
void cDvbPlayerControl::Play(void)
{
if (player)
player->Play();
}
void cDvbPlayerControl::Forward(void)
{
if (player)
player->Forward();
}
void cDvbPlayerControl::Backward(void)
{
if (player)
player->Backward();
}
void cDvbPlayerControl::SkipSeconds(int Seconds)
{
if (player)
player->SkipSeconds(Seconds);
}
int cDvbPlayerControl::SkipFrames(int Frames)
{
if (player)
return player->SkipFrames(Frames);
return -1;
}
bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame)
{
if (player) {
player->GetIndex(Current, Total, SnapToIFrame);
return true;
}
return false;
}
bool cDvbPlayerControl::GetReplayMode(bool &Play, bool &Forward, int &Speed)
{
return player && player->GetReplayMode(Play, Forward, Speed);
}
void cDvbPlayerControl::Goto(int Position, bool Still)
{
if (player)
player->Goto(Position, Still);
}

60
dvbplayer.h Normal file
View File

@ -0,0 +1,60 @@
/*
* dvbplayer.h: The DVB player
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbplayer.h 1.1 2002/06/16 10:59:14 kls Exp $
*/
#ifndef __DVBPLAYER_H
#define __DVBPLAYER_H
#include "player.h"
#include "thread.h"
class cDvbPlayer;
class cDvbPlayerControl : public cControl {
private:
cDvbPlayer *player;
public:
cDvbPlayerControl(void);
virtual ~cDvbPlayerControl();
bool Active(void);
bool Start(const char *FileName);
// Starts replaying the given file.
void Stop(void);
// Stops the current replay session (if any).
void Pause(void);
// Pauses the current replay session, or resumes a paused session.
void Play(void);
// Resumes normal replay mode.
void Forward(void);
// Runs the current replay session forward at a higher speed.
void Backward(void);
// Runs the current replay session backwards at a higher speed.
int SkipFrames(int Frames);
// Returns the new index into the current replay session after skipping
// the given number of frames (no actual repositioning is done!).
// The sign of 'Frames' determines the direction in which to skip.
void SkipSeconds(int Seconds);
// Skips the given number of seconds in the current replay session.
// The sign of 'Seconds' determines the direction in which to skip.
// Use a very large negative value to go all the way back to the
// beginning of the recording.
bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
// Returns the current and total frame index, optionally snapped to the
// nearest I-frame.
bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
// Returns the current replay mode (if applicable).
// 'Play' tells whether we are playing or pausing, 'Forward' tells whether
// we are going forward or backward and 'Speed' is -1 if this is normal
// play/pause mode, 0 if it is single speed fast/slow forward/back mode
// and >0 if this is multi speed mode.
void Goto(int Index, bool Still = false);
// Positions to the given index and displays that frame as a still picture
// if Still is true.
};
#endif //__DVBPLAYER_H

3
eit.c
View File

@ -16,7 +16,7 @@
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* $Id: eit.c 1.45 2002/05/13 16:35:49 kls Exp $
* $Id: eit.c 1.46 2002/06/10 16:30:00 kls Exp $
***************************************************************************/
#include "eit.h"
@ -26,6 +26,7 @@
#include <iomanip.h>
#include <iostream.h>
#include <limits.h>
#include <ost/dmx.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: eitscan.c 1.1 2002/05/20 10:58:10 kls Exp $
* $Id: eitscan.c 1.2 2002/06/10 16:30:00 kls Exp $
*/
#include "eitscan.h"
@ -48,11 +48,11 @@ 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 < MAXDVBAPI; i++) {
cDvbApi *DvbApi = cDvbApi::GetDvbApi(i + 1, MAXPRIORITY + 1);
if (DvbApi) {
if (DvbApi != cDvbApi::PrimaryDvbApi || (cDvbApi::NumDvbApis == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) {
if (!(DvbApi->Recording() || DvbApi->Replaying() || DvbApi->Transferring())) {
for (int i = 0; i < MAXDEVICES; i++) {
cDevice *Device = cDevice::GetDevice(i + 1, MAXPRIORITY + 1);
if (Device) {
if (Device != cDevice::PrimaryDevice() || (cDevice::NumDevices() == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) {
if (!(Device->Receiving() || Device->Replaying()/*XXX+ || Device->Transferring()XXX*/)) {
int oldCh = lastChannel;
int ch = oldCh + 1;
while (ch != oldCh) {
@ -62,12 +62,12 @@ void cEITScanner::Process(void)
}
cChannel *Channel = Channels.GetByNumber(ch);
if (Channel) {
if (Channel->ca <= MAXDVBAPI && !DvbApi->ProvidesCa(Channel->ca))
if (Channel->ca <= MAXDEVICES && !Device->ProvidesCa(Channel->ca))
break; // the channel says it explicitly needs a different card
if (Channel->pnr && !TransponderScanned(Channel)) {
if (DvbApi == cDvbApi::PrimaryDvbApi && !currentChannel)
currentChannel = DvbApi->Channel();
Channel->Switch(DvbApi, false);
if (Device == cDevice::PrimaryDevice() && !currentChannel)
currentChannel = Device->Channel();
Channel->Switch(Device, false);
lastChannel = ch;
break;
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: interface.c 1.50 2002/05/19 12:02:57 kls Exp $
* $Id: interface.c 1.51 2002/06/10 16:30:00 kls Exp $
*/
#include "interface.h"
@ -484,6 +484,6 @@ void cInterface::DisplayRecording(int Index, bool On)
bool cInterface::Recording(void)
{
// This is located here because the Interface has to do with the "PrimaryDvbApi" anyway
return cDvbApi::PrimaryDvbApi->Recording();
// This is located here because the Interface has to do with the "PrimaryDevice" anyway
return cDevice::PrimaryDevice()->Receiving();
}

212
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menu.c 1.195 2002/05/19 14:55:22 kls Exp $
* $Id: menu.c 1.196 2002/06/16 12:11:02 kls Exp $
*/
#include "menu.h"
@ -18,6 +18,7 @@
#include "i18n.h"
#include "menuitems.h"
#include "plugin.h"
#include "recording.h"
#include "status.h"
#include "videodir.h"
@ -25,6 +26,7 @@
#define MAXWAIT4EPGINFO 10 // seconds
#define MODETIMEOUT 3 // seconds
#define MAXRECORDCONTROLS (MAXDEVICES * MAXRECEIVERS)
#define MAXINSTANTRECTIME (24 * 60 - 1) // 23:59 hours
#define CHNUMWIDTH (Channels.Count() > 999 ? 5 : 4) // there are people with more than 999 channels...
@ -376,7 +378,7 @@ eOSState cMenuEditCaItem::ProcessKey(eKeys Key)
}
}
else if (NORMALKEY(Key) == kRight) {
if (ca && ca->Next() && (allowCardNr || ((cCaDefinition *)ca->Next())->Number() > MAXDVBAPI)) {
if (ca && ca->Next() && (allowCardNr || ((cCaDefinition *)ca->Next())->Number() > MAXDEVICES)) {
ca = (cCaDefinition *)ca->Next();
*value = ca->Number();
}
@ -494,7 +496,7 @@ cMenuChannels::cMenuChannels(void)
//TODO
int i = 0;
cChannel *channel;
int curr = ((channel = Channels.GetByNumber(cDvbApi::CurrentChannel())) != NULL) ? channel->Index() : -1;
int curr = ((channel = Channels.GetByNumber(cDevice::CurrentChannel())) != NULL) ? channel->Index() : -1;
while ((channel = Channels.Get(i)) != NULL) {
Add(new cMenuChannelItem(i, channel), i == curr);
@ -1145,7 +1147,7 @@ cMenuSchedule::cMenuSchedule(void)
{
now = next = false;
otherChannel = 0;
cChannel *channel = Channels.GetByNumber(cDvbApi::CurrentChannel());
cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
if (channel) {
cMenuWhatsOn::SetCurrentChannel(channel->number);
schedules = cSIProcessor::Schedules(mutexLock);
@ -1264,7 +1266,7 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key)
cChannel *channel = Channels.GetByServiceID(ei->GetServiceID());
if (channel) {
PrepareSchedule(channel);
if (channel->number != cDvbApi::CurrentChannel()) {
if (channel->number != cDevice::CurrentChannel()) {
otherChannel = channel->number;
SetHelp(tr("Record"), tr("Now"), tr("Next"), tr("Switch"));
}
@ -1441,7 +1443,7 @@ eOSState cMenuRecordings::Rewind(void)
{
cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
if (ri && !ri->IsDirectory()) {
cDvbApi::PrimaryDvbApi->StopReplay(); // must do this first to be able to rewind the currently replayed recording
cDevice::PrimaryDevice()->StopReplay(); // must do this first to be able to rewind the currently replayed recording
cResumeFile ResumeFile(ri->FileName());
ResumeFile.Delete();
return Play();
@ -1614,7 +1616,7 @@ public:
cMenuSetupDVB::cMenuSetupDVB(void)
{
SetSection(tr("DVB"));
Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDvbApi::NumDvbApis));
Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDevice::NumDevices()));
Add(new cMenuEditBoolItem(tr("Setup.DVB$Video format"), &data.VideoFormat, "4:3", "16:9"));
}
@ -1626,7 +1628,7 @@ eOSState cMenuSetupDVB::ProcessKey(eKeys Key)
if (state == osBack && Key == kOk) {
if (Setup.PrimaryDVB != oldPrimaryDVB) {
state = osSwitchDvb;
cDvbApi::PrimaryDvbApi->SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3);
cDevice::PrimaryDevice()->SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3);
}
}
return state;
@ -1659,7 +1661,7 @@ public:
cMenuSetupCICAM::cMenuSetupCICAM(void)
{
SetSection(tr("CICAM"));
for (int d = 0; d < cDvbApi::NumDvbApis; d++) {
for (int d = 0; d < cDevice::NumDevices(); d++) {
for (int i = 0; i < 2; i++) {
char buffer[32];
snprintf(buffer, sizeof(buffer), "%s%d %d", tr("Setup.CICAM$CICAM DVB"), d + 1, i + 1);
@ -1673,7 +1675,7 @@ eOSState cMenuSetupCICAM::ProcessKey(eKeys Key)
eOSState state = cMenuSetupBase::ProcessKey(Key);
if (state == osBack && Key == kOk)
cDvbApi::SetCaCaps();
cDevice::SetCaCaps();
return state;
}
@ -2024,12 +2026,14 @@ void cMenuMain::Set(void)
// Editing control:
/*XXX+
if (cVideoCutter::Active())
Add(new cOsdItem(tr(" Cancel editing"), osCancelEdit));
XXX*/
// Color buttons:
SetHelp(tr("Record"), cDvbApi::PrimaryDvbApi->CanToggleAudioTrack() ? tr("Language") : NULL, NULL, replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Resume") : NULL);
SetHelp(tr("Record"), /*XXX+ cDevice::PrimaryDevice()->CanToggleAudioTrack() ? tr("Language") :XXX*/ NULL, NULL, replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Resume") : NULL);
Display();
lastActivity = time(NULL);
}
@ -2059,7 +2063,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
}
break;
case osCancelEdit: if (Interface->Confirm(tr("Cancel editing?"))) {
cVideoCutter::Stop();
//XXX+cVideoCutter::Stop();
return osEnd;
}
break;
@ -2082,11 +2086,13 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
state = osRecord;
break;
case kGreen: if (!HasSubMenu()) {
if (cDvbApi::PrimaryDvbApi->CanToggleAudioTrack()) {
/*XXX+
if (cDevice::PrimaryDevice()->CanToggleAudioTrack()) {
Interface->Clear();
cDvbApi::PrimaryDvbApi->ToggleAudioTrack();
cDevice::PrimaryDevice()->ToggleAudioTrack();
state = osEnd;
}
XXX*/
}
break;
case kBlue: if (!HasSubMenu())
@ -2134,7 +2140,7 @@ cDisplayChannel::cDisplayChannel(eKeys FirstKey)
:cOsdObject(true)
{
group = -1;
oldNumber = cDvbApi::CurrentChannel();
oldNumber = cDevice::CurrentChannel();
number = 0;
lastTime = time_ms();
int EpgLines = Setup.ShowInfoOnChSwitch ? 5 : 1;
@ -2253,7 +2259,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
case kRight:
withInfo = false;
if (group < 0) {
cChannel *channel = Channels.GetByNumber(cDvbApi::CurrentChannel());
cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
if (channel)
group = channel->Index();
}
@ -2328,7 +2334,7 @@ cDisplayVolume::cDisplayVolume(void)
:cOsdObject(true)
{
displayVolume = this;
timeout = time_ms() + (cDvbApi::PrimaryDvbApi->IsMute() ? MUTETIMEOUT : VOLUMETIMEOUT);
timeout = time_ms() + (cDevice::PrimaryDevice()->IsMute() ? MUTETIMEOUT : VOLUMETIMEOUT);
Interface->Open(Setup.OSDwidth, -1);
Show();
}
@ -2341,13 +2347,13 @@ cDisplayVolume::~cDisplayVolume()
void cDisplayVolume::Show(void)
{
cDvbApi *dvbApi = cDvbApi::PrimaryDvbApi;
if (dvbApi->IsMute()) {
cDevice *device = cDevice::PrimaryDevice();
if (device->IsMute()) {
Interface->Fill(0, 0, Width(), 1, clrTransparent);
Interface->Write(0, 0, tr("Mute"), clrGreen);
}
else {
int Current = cDvbApi::CurrentVolume();
int Current = cDevice::CurrentVolume();
int Total = MAXVOLUME;
const char *Prompt = tr("Volume ");
#ifdef DEBUG_OSD
@ -2387,7 +2393,7 @@ eOSState cDisplayVolume::ProcessKey(eKeys Key)
timeout = time_ms() + VOLUMETIMEOUT;
break;
case kMute:
if (cDvbApi::PrimaryDvbApi->IsMute()) {
if (cDevice::PrimaryDevice()->IsMute()) {
Show();
timeout = time_ms() + MUTETIMEOUT;
}
@ -2405,43 +2411,45 @@ eOSState cDisplayVolume::ProcessKey(eKeys Key)
// --- cRecordControl --------------------------------------------------------
cRecordControl::cRecordControl(cDvbApi *DvbApi, cTimer *Timer)
cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer)
{
eventInfo = NULL;
instantId = NULL;
fileName = NULL;
dvbApi = DvbApi;
if (!dvbApi) dvbApi = cDvbApi::PrimaryDvbApi;//XXX
recorder = NULL;
device = Device;
if (!device) device = cDevice::PrimaryDevice();//XXX
timer = Timer;
if (!timer) {
timer = new cTimer(true);
Timers.Add(timer);
Timers.Save();
asprintf(&instantId, cDvbApi::NumDvbApis > 1 ? "%s - %d" : "%s", Channels.GetChannelNameByNumber(timer->channel), dvbApi->CardIndex() + 1);
asprintf(&instantId, cDevice::NumDevices() > 1 ? "%s - %d" : "%s", Channels.GetChannelNameByNumber(timer->channel), device->CardIndex() + 1);
}
timer->SetPending(true);
timer->SetRecording(true);
if (Channels.SwitchTo(timer->channel, dvbApi)) {
const char *Title = NULL;
const char *Subtitle = NULL;
const char *Summary = NULL;
if (GetEventInfo()) {
Title = eventInfo->GetTitle();
Subtitle = eventInfo->GetSubtitle();
Summary = eventInfo->GetExtendedDescription();
dsyslog("Title: '%s' Subtitle: '%s'", Title, Subtitle);
}
cRecording Recording(timer, Title, Subtitle, Summary);
fileName = strdup(Recording.FileName());
cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName);
if (dvbApi->StartRecord(fileName, Channels.GetByNumber(timer->channel)->ca, timer->priority)) {
Recording.WriteSummary();
cStatusMonitor::MsgRecording(dvbApi, fileName);
}
Interface->DisplayRecording(dvbApi->CardIndex(), true);
const char *Title = NULL;
const char *Subtitle = NULL;
const char *Summary = NULL;
if (GetEventInfo()) {
Title = eventInfo->GetTitle();
Subtitle = eventInfo->GetSubtitle();
Summary = eventInfo->GetExtendedDescription();
dsyslog("Title: '%s' Subtitle: '%s'", Title, Subtitle);
}
cRecording Recording(timer, Title, Subtitle, Summary);
fileName = strdup(Recording.FileName());
cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName);
cChannel *ch = Channels.GetByNumber(timer->channel);
recorder = new cRecorder(fileName, ch->ca, timer->priority, ch->vpid, ch->apid1, ch->apid2, ch->dpid1, ch->dpid2);
if (device->Attach(recorder)) {
Recording.WriteSummary();
cStatusMonitor::MsgRecording(device, fileName);
Interface->DisplayRecording(device->CardIndex(), true);
}
else
cThread::EmergencyExit(true);
DELETENULL(recorder);
}
cRecordControl::~cRecordControl()
@ -2484,8 +2492,8 @@ bool cRecordControl::GetEventInfo(void)
void cRecordControl::Stop(bool KeepInstant)
{
if (timer) {
cStatusMonitor::MsgRecording(dvbApi, NULL);
dvbApi->StopRecord();
cStatusMonitor::MsgRecording(device, NULL);
DELETENULL(recorder);
timer->SetRecording(false);
if ((IsInstant() && !KeepInstant) || (timer->IsSingleEvent() && !timer->Matches())) {
// checking timer->Matches() to make sure we don't delete the timer
@ -2495,14 +2503,14 @@ void cRecordControl::Stop(bool KeepInstant)
Timers.Save();
}
timer = NULL;
Interface->DisplayRecording(dvbApi->CardIndex(), false);
Interface->DisplayRecording(device->CardIndex(), false);
cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName);
}
}
bool cRecordControl::Process(time_t t)
{
if (!timer || !timer->Matches(t))
if (!recorder || !timer || !timer->Matches(t))
return false;
AssertFreeDiskSpace(timer->priority);
return true;
@ -2510,20 +2518,27 @@ bool cRecordControl::Process(time_t t)
// --- cRecordControls -------------------------------------------------------
cRecordControl *cRecordControls::RecordControls[MAXDVBAPI] = { NULL };
cRecordControl *cRecordControls::RecordControls[MAXRECORDCONTROLS] = { NULL };
bool cRecordControls::Start(cTimer *Timer)
{
int ch = Timer ? Timer->channel : cDvbApi::CurrentChannel();
int ch = Timer ? Timer->channel : cDevice::CurrentChannel();
cChannel *channel = Channels.GetByNumber(ch);
if (channel) {
cDvbApi *dvbApi = cDvbApi::GetDvbApi(channel->ca, Timer ? Timer->priority : Setup.DefaultPriority);
if (dvbApi) {
Stop(dvbApi);
for (int i = 0; i < MAXDVBAPI; i++) {
bool ReUse = false;
cDevice *device = cDevice::GetDevice(channel->ca, Timer ? Timer->priority : Setup.DefaultPriority, channel->frequency, channel->vpid, &ReUse);
if (device) {
if (!ReUse) {
Stop(device);
if (!channel->Switch(device)) {
cThread::EmergencyExit(true);
return false;
}
}
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (!RecordControls[i]) {
RecordControls[i] = new cRecordControl(dvbApi, Timer);
RecordControls[i] = new cRecordControl(device, Timer);
return true;
}
}
@ -2538,7 +2553,7 @@ bool cRecordControls::Start(cTimer *Timer)
void cRecordControls::Stop(const char *InstantId)
{
for (int i = 0; i < MAXDVBAPI; i++) {
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i]) {
const char *id = RecordControls[i]->InstantId();
if (id && strcmp(id, InstantId) == 0)
@ -2547,12 +2562,12 @@ void cRecordControls::Stop(const char *InstantId)
}
}
void cRecordControls::Stop(cDvbApi *DvbApi)
void cRecordControls::Stop(cDevice *Device)
{
for (int i = 0; i < MAXDVBAPI; i++) {
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i]) {
if (RecordControls[i]->Uses(DvbApi)) {
isyslog("stopping recording on DVB device %d due to higher priority", DvbApi->CardIndex() + 1);
if (RecordControls[i]->Uses(Device)) {
isyslog("stopping recording on DVB device %d due to higher priority", Device->CardIndex() + 1);
RecordControls[i]->Stop(true);
}
}
@ -2561,11 +2576,11 @@ void cRecordControls::Stop(cDvbApi *DvbApi)
bool cRecordControls::StopPrimary(bool DoIt)
{
if (cDvbApi::PrimaryDvbApi->Recording()) {
cDvbApi *dvbApi = cDvbApi::GetDvbApi(cDvbApi::PrimaryDvbApi->Ca(), 0);
if (dvbApi) {
if (cDevice::PrimaryDevice()->Receiving()) {
cDevice *device = cDevice::GetDevice(cDevice::PrimaryDevice()->Ca(), 0);
if (device) {
if (DoIt)
Stop(cDvbApi::PrimaryDvbApi);
Stop(cDevice::PrimaryDevice());
return true;
}
}
@ -2574,7 +2589,7 @@ bool cRecordControls::StopPrimary(bool DoIt)
const char *cRecordControls::GetInstantId(const char *LastInstantId)
{
for (int i = 0; i < MAXDVBAPI; i++) {
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i]) {
if (!LastInstantId && RecordControls[i]->InstantId())
return RecordControls[i]->InstantId();
@ -2587,7 +2602,7 @@ const char *cRecordControls::GetInstantId(const char *LastInstantId)
cRecordControl *cRecordControls::GetRecordControl(const char *FileName)
{
for (int i = 0; i < MAXDVBAPI; i++) {
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i] && strcmp(RecordControls[i]->FileName(), FileName) == 0)
return RecordControls[i];
}
@ -2596,7 +2611,7 @@ cRecordControl *cRecordControls::GetRecordControl(const char *FileName)
void cRecordControls::Process(time_t t)
{
for (int i = 0; i < MAXDVBAPI; i++) {
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i]) {
if (!RecordControls[i]->Process(t))
DELETENULL(RecordControls[i]);
@ -2606,13 +2621,19 @@ void cRecordControls::Process(time_t t)
bool cRecordControls::Active(void)
{
for (int i = 0; i < MAXDVBAPI; i++) {
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i])
return true;
}
return false;
}
void cRecordControls::Shutdown(void)
{
for (int i = 0; i < MAXRECORDCONTROLS; i++)
DELETENULL(RecordControls[i]);
}
// --- cProgressBar ----------------------------------------------------------
class cProgressBar : public cBitmap {
@ -2664,25 +2685,24 @@ char *cReplayControl::title = NULL;
cReplayControl::cReplayControl(void)
{
dvbApi = cDvbApi::PrimaryDvbApi;
visible = modeOnly = shown = displayFrames = false;
lastCurrent = lastTotal = -1;
timeoutShow = 0;
timeSearchActive = false;
if (fileName) {
marks.Load(fileName);
if (!dvbApi->StartReplay(fileName))
Interface->Error(tr("Channel locked (recording)!"));
if (!Start(fileName))
Interface->Error(tr("Channel locked (recording)!"));//XXX+
else
cStatusMonitor::MsgReplaying(dvbApi, fileName);
cStatusMonitor::MsgReplaying(this, fileName);
}
}
cReplayControl::~cReplayControl()
{
Hide();
cStatusMonitor::MsgReplaying(dvbApi, NULL);
dvbApi->StopReplay();
cStatusMonitor::MsgReplaying(this, NULL);
Stop();
}
void cReplayControl::SetRecording(const char *FileName, const char *Title)
@ -2744,7 +2764,7 @@ void cReplayControl::ShowMode(void)
if (Setup.ShowReplayMode && !timeSearchActive) {
bool Play, Forward;
int Speed;
if (dvbApi->GetReplayMode(Play, Forward, Speed)) {
if (GetReplayMode(Play, Forward, Speed)) {
bool NormalPlay = (Play && Speed == -1);
if (!visible) {
@ -2782,7 +2802,7 @@ bool cReplayControl::ShowProgress(bool Initial)
{
int Current, Total;
if (dvbApi->GetIndex(Current, Total) && Total > 0) {
if (GetIndex(Current, Total) && Total > 0) {
if (!visible) {
Interface->Open(Setup.OSDwidth, -3);
needsFastResponse = visible = true;
@ -2857,14 +2877,14 @@ void cReplayControl::TimeSearchProcess(eKeys Key)
int dir = (Key == kRight ? 1 : -1);
if (dir > 0)
Seconds = min(Total - Current - STAY_SECONDS_OFF_END, Seconds);
dvbApi->SkipSeconds(Seconds * dir);
SkipSeconds(Seconds * dir);
timeSearchActive = false;
}
break;
case kUp:
case kDown:
Seconds = min(Total - STAY_SECONDS_OFF_END, Seconds);
dvbApi->Goto(Seconds * FRAMESPERSEC, Key == kDown);
Goto(Seconds * FRAMESPERSEC, Key == kDown);
timeSearchActive = false;
break;
default:
@ -2902,7 +2922,7 @@ void cReplayControl::TimeSearch(void)
void cReplayControl::MarkToggle(void)
{
int Current, Total;
if (dvbApi->GetIndex(Current, Total, true)) {
if (GetIndex(Current, Total, true)) {
cMark *m = marks.Get(Current);
lastCurrent = -1; // triggers redisplay
if (m)
@ -2919,10 +2939,10 @@ void cReplayControl::MarkJump(bool Forward)
{
if (marks.Count()) {
int Current, Total;
if (dvbApi->GetIndex(Current, Total)) {
if (GetIndex(Current, Total)) {
cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current);
if (m)
dvbApi->Goto(m->position, true);
Goto(m->position, true);
}
displayFrames = true;
}
@ -2931,11 +2951,11 @@ void cReplayControl::MarkJump(bool Forward)
void cReplayControl::MarkMove(bool Forward)
{
int Current, Total;
if (dvbApi->GetIndex(Current, Total)) {
if (GetIndex(Current, Total)) {
cMark *m = marks.Get(Current);
if (m) {
displayFrames = true;
int p = dvbApi->SkipFrames(Forward ? 1 : -1);
int p = SkipFrames(Forward ? 1 : -1);
cMark *m2;
if (Forward) {
if ((m2 = marks.Next(m)) != NULL && m2->position <= p)
@ -2945,7 +2965,7 @@ void cReplayControl::MarkMove(bool Forward)
if ((m2 = marks.Prev(m)) != NULL && m2->position >= p)
return;
}
dvbApi->Goto(m->position = p, true);
Goto(m->position = p, true);
marks.Save();
}
}
@ -2953,6 +2973,7 @@ void cReplayControl::MarkMove(bool Forward)
void cReplayControl::EditCut(void)
{
/*XXX+
if (fileName) {
Hide();
if (!cVideoCutter::Active()) {
@ -2965,12 +2986,13 @@ void cReplayControl::EditCut(void)
Interface->Error(tr("Editing process already active!"));
ShowMode();
}
XXX*/
}
void cReplayControl::EditTest(void)
{
int Current, Total;
if (dvbApi->GetIndex(Current, Total)) {
if (GetIndex(Current, Total)) {
cMark *m = marks.Get(Current);
if (!m)
m = marks.GetNext(Current);
@ -2978,8 +3000,8 @@ void cReplayControl::EditTest(void)
if ((m->Index() & 0x01) != 0)
m = marks.Next(m);
if (m) {
dvbApi->Goto(m->position - dvbApi->SecondsToFrames(3));
dvbApi->Play();
Goto(m->position - SecondsToFrames(3));
Play();
}
}
}
@ -2987,7 +3009,7 @@ void cReplayControl::EditTest(void)
eOSState cReplayControl::ProcessKey(eKeys Key)
{
if (!dvbApi->Replaying())
if (!Active())
return osEnd;
if (visible) {
if (timeoutShow && time(NULL) > timeoutShow) {
@ -3009,21 +3031,21 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
bool DoShowMode = true;
switch (Key) {
// Positioning:
case kUp: dvbApi->Play(); break;
case kDown: dvbApi->Pause(); break;
case kUp: Play(); break;
case kDown: Pause(); break;
case kLeft|k_Release:
if (Setup.MultiSpeedMode) break;
case kLeft: dvbApi->Backward(); break;
case kLeft: Backward(); break;
case kRight|k_Release:
if (Setup.MultiSpeedMode) break;
case kRight: dvbApi->Forward(); break;
case kRight: Forward(); break;
case kRed: TimeSearch(); break;
case kGreen|k_Repeat:
case kGreen: dvbApi->SkipSeconds(-60); break;
case kGreen: SkipSeconds(-60); break;
case kYellow|k_Repeat:
case kYellow: dvbApi->SkipSeconds( 60); break;
case kYellow: SkipSeconds( 60); break;
case kBlue: Hide();
dvbApi->StopReplay();
Stop();
return osEnd;
default: {
DoShowMode = false;

21
menu.h
View File

@ -4,14 +4,16 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menu.h 1.43 2002/05/18 12:36:06 kls Exp $
* $Id: menu.h 1.44 2002/06/14 12:33:35 kls Exp $
*/
#ifndef __MENU_H
#define __MENU_H
#include "dvbapi.h"
#include "device.h"
#include "osd.h"
#include "dvbplayer.h"
#include "recorder.h"
#include "recording.h"
class cMenuMain : public cOsdMenu {
@ -76,17 +78,18 @@ public:
class cRecordControl {
private:
cDvbApi *dvbApi;
cDevice *device;
cTimer *timer;
cRecorder *recorder;
const cEventInfo *eventInfo;
char *instantId;
char *fileName;
bool GetEventInfo(void);
public:
cRecordControl(cDvbApi *DvbApi, cTimer *Timer = NULL);
cRecordControl(cDevice *Device, cTimer *Timer = NULL);
virtual ~cRecordControl();
bool Process(time_t t);
bool Uses(cDvbApi *DvbApi) { return DvbApi == dvbApi; }
bool Uses(cDevice *Device) { return Device == device; }
void Stop(bool KeepInstant = false);
bool IsInstant(void) { return instantId; }
const char *InstantId(void) { return instantId; }
@ -96,21 +99,21 @@ public:
class cRecordControls {
private:
static cRecordControl *RecordControls[MAXDVBAPI];
static cRecordControl *RecordControls[];
public:
static bool Start(cTimer *Timer = NULL);
static void Stop(const char *InstantId);
static void Stop(cDvbApi *DvbApi);
static void Stop(cDevice *Device);
static bool StopPrimary(bool DoIt = false);
static const char *GetInstantId(const char *LastInstantId);
static cRecordControl *GetRecordControl(const char *FileName);
static void Process(time_t t);
static bool Active(void);
static void Shutdown(void);
};
class cReplayControl : public cOsdObject {
class cReplayControl : public cDvbPlayerControl {
private:
cDvbApi *dvbApi;
cMarks marks;
bool visible, modeOnly, shown, displayFrames;
int lastCurrent, lastTotal;

6
osd.c
View File

@ -4,12 +4,12 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: osd.c 1.27 2002/05/19 12:56:57 kls Exp $
* $Id: osd.c 1.28 2002/06/10 16:30:00 kls Exp $
*/
#include "osd.h"
#include <string.h>
#include "dvbapi.h"
#include "device.h"
#include "i18n.h"
#include "status.h"
@ -95,7 +95,7 @@ void cOsd::Open(int w, int h)
int x = (720 - w + charWidth) / 2; //TODO PAL vs. NTSC???
int y = (576 - Setup.OSDheight * lineHeight) / 2 + d;
//XXX
osd = new cDvbOsd(cDvbApi::PrimaryDvbApi->OsdDeviceHandle(), x, y);
osd = new cDvbOsd(cDevice::PrimaryDevice()->OsdDeviceHandle(), x, y);
//XXX TODO this should be transferred to the places where the individual windows are requested (there's too much detailed knowledge here!)
if (h / lineHeight == 5) { //XXX channel display
osd->Create(0, 0, w, h, 4);

55
player.c Normal file
View File

@ -0,0 +1,55 @@
/*
* player.c: The basic player interface
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: player.c 1.1 2002/06/16 10:34:50 kls Exp $
*/
#include "player.h"
// --- cPlayer ---------------------------------------------------------------
cPlayer::cPlayer(void)
{
device = NULL;
deviceFileHandle = -1;
}
cPlayer::~cPlayer()
{
Detach();
}
int cPlayer::PlayVideo(const uchar *Data, int Length)
{
if (device)
return device->PlayVideo(Data, Length);
esyslog("ERROR: attempt to use cPlayer::PlayVideo() without attaching to a cDevice!");
return -1;
}
int cPlayer::PlayAudio(const uchar *Data, int Length)
{
if (device)
return device->PlayAudio(Data, Length);
esyslog("ERROR: attempt to use cPlayer::PlayAudio() without attaching to a cDevice!");
return -1;
}
void cPlayer::Detach(void)
{
if (device)
device->Detach(this);
}
// --- cControl --------------------------------------------------------------
cControl::cControl(void)
{
}
cControl::~cControl()
{
}

51
player.h Normal file
View File

@ -0,0 +1,51 @@
/*
* player.h: The basic player interface
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: player.h 1.1 2002/06/16 11:52:45 kls Exp $
*/
#ifndef __PLAYER_H
#define __PLAYER_H
#include "device.h"
#include "osd.h"
class cPlayer {
friend class cDevice;
private:
cDevice *device;
int deviceFileHandle;
protected:
int DeviceFileHandle(void) { return deviceFileHandle; } //XXX+ needed for polling
void DeviceTrickSpeed(int Speed) { if (device) device->TrickSpeed(Speed); }
void DeviceClear(void) { if (device) device->Clear(); }
void DevicePlay(void) { if (device) device->Play(); }
void DeviceFreeze(void) { if (device) device->Freeze(); }
void DeviceMute(void) { if (device) device->Mute(); }
void DeviceStillPicture(const uchar *Data, int Length) { if (device) device->StillPicture(Data, Length); }
void Detach(void);
virtual void Activate(bool On) {}
// This function is called right after the cPlayer has been attached to
// (On == true) or before it gets detached from (On == false) a cDevice.
// It can be used to do things like starting/stopping a thread.
int PlayVideo(const uchar *Data, int Length);
// Sends the given Data to the video device and returns the number of
// bytes that have actually been accepted by the video device (or a
// negative value in case of an error).
int PlayAudio(const uchar *Data, int Length);
// XXX+ TODO
public:
cPlayer(void);
virtual ~cPlayer();
};
class cControl : public cOsdObject {
public:
cControl(void);
virtual ~cControl();
};
#endif //__PLAYER_H

55
receiver.c Normal file
View File

@ -0,0 +1,55 @@
/*
* receiver.c: The basic receiver interface
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: receiver.c 1.1 2002/06/10 16:30:00 kls Exp $
*/
#include <stdarg.h>
#include <stdio.h>
#include "receiver.h"
cReceiver::cReceiver(int Ca, int Priority, int NumPids, ...)
{
device = NULL;
ca = Ca;
priority = Priority;
if (NumPids) {
va_list ap;
va_start(ap, NumPids);
int n = 0;
while (n < MAXRECEIVEPIDS && NumPids--) {
if ((pids[n] = va_arg(ap, int)) != 0)
n++;
}
va_end(ap);
}
else
esyslog("ERROR: cReceiver called without a PID!");
}
cReceiver::~cReceiver()
{
Detach();
}
bool cReceiver::WantsPid(int Pid)
{
if (Pid) {
for (int i = 0; i < MAXRECEIVEPIDS; i++) {
if (pids[i] == Pid)
return true;
if (!pids[i])
break;
}
}
return false;
}
void cReceiver::Detach(void)
{
if (device)
device->Detach(this);
}

48
receiver.h Normal file
View File

@ -0,0 +1,48 @@
/*
* receiver.h: The basic receiver interface
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: receiver.h 1.1 2002/06/10 16:30:00 kls Exp $
*/
#ifndef __RECEIVER_H
#define __RECEIVER_H
#include "device.h"
#define MAXRECEIVEPIDS 16 // the maximum number of PIDs per receiver
class cReceiver {
friend class cDevice;
private:
cDevice *device;
int ca;
int priority;
int pids[MAXRECEIVEPIDS];
bool WantsPid(int Pid);
protected:
void Detach(void);
virtual void Activate(bool On) {}
// This function is called just before the cReceiver gets attached to
// (On == true) or detached from (On == false) a cDevice. It can be used
// to do things like starting/stopping a thread.
// It is guaranteed that Receive() will not be called before Activate(true).
virtual void Receive(uchar *Data, int Length) = 0;
// This function is called from the cDevice we are attached to, and
// delivers one TS packet from the set of PIDs the cReceiver has requested.
// The data packet must be accepted immediately, and the call must return
// as soon as possible, without any unnecessary delay. Each TS packet
// will be delivered only ONCE, so the cReceiver must make sure that
// it will be able to buffer the data if necessary.
public:
cReceiver(int Ca, int Priority, int NumPids, ...);
// Creates a new receiver that requires conditional access Ca and has
// the given Priority. NumPids defines the number of PIDs that follow
// this parameter. If any of these PIDs are 0, they will be silently ignored.
// The total number of non-zero PIDs must not exceed MAXRECEIVEPIDS.
virtual ~cReceiver();
};
#endif //__RECEIVER_H

149
recorder.c Normal file
View File

@ -0,0 +1,149 @@
/*
* recorder.h: The actual DVB recorder
*
* 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 $
*/
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include "recorder.h"
// The size of the array used to buffer video data:
// (must be larger than MINVIDEODATA - see remux.h)
#define VIDEOBUFSIZE MEGABYTE(1)
#define MINFREEDISKSPACE (512) // MB
#define DISKCHECKINTERVAL 100 // seconds
cRecorder::cRecorder(const char *FileName, int Ca, int Priority, int VPid, int APid1, int APid2, int DPid1, int DPid2)
:cReceiver(Ca, Priority, 5, VPid, APid1, APid2, DPid1, DPid2)
{
ringBuffer = NULL;
remux = NULL;
fileName = NULL;
index = NULL;
pictureType = NO_PICTURE;
fileSize = 0;
active = false;
lastDiskSpaceCheck = time(NULL);
isyslog("record %s", FileName);
// Create directories if necessary:
if (!MakeDirs(FileName, true))
return;
// Make sure the disk is up and running:
SpinUpDisk(FileName);
ringBuffer = new cRingBufferLinear(VIDEOBUFSIZE, true);
remux = new cRemux(VPid, APid1, APid2, DPid1, DPid2, true);
fileName = new cFileName(FileName, true);
recordFile = fileName->Open();
if (recordFile < 0)
return;
// Create the index file:
index = new cIndexFile(FileName, true);
if (!index)
esyslog("ERROR: can't allocate index");
// let's continue without index, so we'll at least have the recording
}
cRecorder::~cRecorder()
{
Detach();
delete index;
delete fileName;
delete remux;
delete ringBuffer;
}
void cRecorder::Activate(bool On)
{
if (On) {
if (recordFile >= 0)
Start();
}
else if (active) {
active = false;
Cancel(3);
}
}
bool cRecorder::RunningLowOnDiskSpace(void)
{
if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
int Free = FreeDiskSpaceMB(fileName->Name());
lastDiskSpaceCheck = time(NULL);
if (Free < MINFREEDISKSPACE) {
dsyslog("low disk space (%d MB, limit is %d MB)", Free, MINFREEDISKSPACE);
return true;
}
}
return false;
}
bool cRecorder::NextFile(void)
{
if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
if (fileSize > MEGABYTE(Setup.MaxVideoFileSize) || RunningLowOnDiskSpace()) {
recordFile = fileName->NextFile();
fileSize = 0;
}
}
return recordFile >= 0;
}
void cRecorder::Receive(uchar *Data, int Length)
{
int p = ringBuffer->Put(Data, Length);
if (p != Length && active)
esyslog("ERROR: ring buffer overflow (%d bytes dropped)", Length - p);
}
void cRecorder::Action(void)
{
dsyslog("recording thread started (pid=%d)", getpid());
uchar b[MINVIDEODATA];
int r = 0;
active = true;
while (active) {
int g = ringBuffer->Get(b + r, sizeof(b) - r);
if (g > 0)
r += g;
if (r > 0) {
int Count = r, Result;
const uchar *p = remux->Process(b, Count, Result, &pictureType);
if (p) {
//XXX+ active??? see old version (Busy)
if (!active && pictureType == I_FRAME) // finish the recording before the next 'I' frame
break;
if (NextFile()) {
if (index && pictureType != NO_PICTURE)
index->Write(pictureType, fileName->Number(), fileSize);
if (safe_write(recordFile, p, Result) < 0) {
LOG_ERROR_STR(fileName->Name());
break;
}
fileSize += Result;
}
else
break;
}
if (Count > 0) {
r -= Count;
memmove(b, b + Count, r);
}
}
else
usleep(1); // this keeps the CPU load low
}
dsyslog("recording thread ended (pid=%d)", getpid());
}

43
recorder.h Normal file
View File

@ -0,0 +1,43 @@
/*
* recorder.h: The actual DVB recorder
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: recorder.h 1.1 2002/06/10 16:30:00 kls Exp $
*/
#ifndef __RECORDER_H
#define __RECORDER_H
#include "receiver.h"
#include "recording.h"
#include "remux.h"
#include "ringbuffer.h"
#include "thread.h"
class cRecorder : public cReceiver, cThread {
private:
cRingBufferLinear *ringBuffer;
cRemux *remux;
cFileName *fileName;
cIndexFile *index;
uchar pictureType;
int fileSize;
int recordFile;
bool active;
time_t lastDiskSpaceCheck;
bool RunningLowOnDiskSpace(void);
bool NextFile(void);
protected:
virtual void Activate(bool On);
virtual void Receive(uchar *Data, int Length);
virtual void Action(void);
public:
cRecorder(const char *FileName, int Ca, int Priority, int VPid, int APid1, int APid2, int DPid1, int DPid2);
// Creates a new recorder that requires conditional access Ca, has
// the given Priority and will record the given PIDs into the file FileName.
virtual ~cRecorder();
};
#endif //__RECORDER_H

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: recording.c 1.62 2002/05/13 16:31:21 kls Exp $
* $Id: recording.c 1.63 2002/06/16 11:29:27 kls Exp $
*/
#include "recording.h"
@ -16,6 +16,7 @@
#include <unistd.h>
#include "i18n.h"
#include "interface.h"
#include "remux.h" //XXX+ I_FRAME
#include "tools.h"
#include "videodir.h"
@ -732,3 +733,350 @@ void cRecordingUserCommand::InvokeCommand(const char *State, const char *Recordi
delete cmd;
}
}
// --- XXX+
//XXX+ somewhere else???
// --- cIndexFile ------------------------------------------------------------
#define INDEXFILESUFFIX "/index.vdr"
// The maximum time to wait before giving up while catching up on an index file:
#define MAXINDEXCATCHUP 2 // seconds
cIndexFile::cIndexFile(const char *FileName, bool Record)
:resumeFile(FileName)
{
f = -1;
fileName = NULL;
size = 0;
last = -1;
index = NULL;
if (FileName) {
fileName = new char[strlen(FileName) + strlen(INDEXFILESUFFIX) + 1];
if (fileName) {
strcpy(fileName, FileName);
char *pFileExt = fileName + strlen(fileName);
strcpy(pFileExt, INDEXFILESUFFIX);
int delta = 0;
if (access(fileName, R_OK) == 0) {
struct stat buf;
if (stat(fileName, &buf) == 0) {
delta = buf.st_size % sizeof(tIndex);
if (delta) {
delta = sizeof(tIndex) - delta;
esyslog("ERROR: invalid file size (%ld) in '%s'", buf.st_size, fileName);
}
last = (buf.st_size + delta) / sizeof(tIndex) - 1;
if (!Record && last >= 0) {
size = last + 1;
index = new tIndex[size];
if (index) {
f = open(fileName, O_RDONLY);
if (f >= 0) {
if ((int)safe_read(f, index, buf.st_size) != buf.st_size) {
esyslog("ERROR: can't read from file '%s'", fileName);
delete index;
index = NULL;
close(f);
f = -1;
}
// we don't close f here, see CatchUp()!
}
else
LOG_ERROR_STR(fileName);
}
else
esyslog("ERROR: can't allocate %d bytes for index '%s'", size * sizeof(tIndex), fileName);
}
}
else
LOG_ERROR;
}
else if (!Record)
isyslog("missing index file %s", fileName);
if (Record) {
if ((f = open(fileName, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) >= 0) {
if (delta) {
esyslog("ERROR: padding index file with %d '0' bytes", delta);
while (delta--)
writechar(f, 0);
}
}
else
LOG_ERROR_STR(fileName);
}
}
else
esyslog("ERROR: can't copy file name '%s'", FileName);
}
}
cIndexFile::~cIndexFile()
{
if (f >= 0)
close(f);
delete fileName;
delete index;
}
bool cIndexFile::CatchUp(int Index)
{
if (index && f >= 0) {
for (int i = 0; i <= MAXINDEXCATCHUP && (Index < 0 || Index >= last); i++) {
struct stat buf;
if (fstat(f, &buf) == 0) {
int newLast = buf.st_size / sizeof(tIndex) - 1;
if (newLast > last) {
if (size <= newLast) {
size *= 2;
if (size <= newLast)
size = newLast + 1;
}
index = (tIndex *)realloc(index, size * sizeof(tIndex));
if (index) {
int offset = (last + 1) * sizeof(tIndex);
int delta = (newLast - last) * sizeof(tIndex);
if (lseek(f, offset, SEEK_SET) == offset) {
if (safe_read(f, &index[last + 1], delta) != delta) {
esyslog("ERROR: can't read from index");
delete index;
index = NULL;
close(f);
f = -1;
break;
}
last = newLast;
}
else
LOG_ERROR_STR(fileName);
}
else
esyslog("ERROR: can't realloc() index");
}
}
else
LOG_ERROR_STR(fileName);
if (Index >= last)
sleep(1);
else
return true;
}
}
return false;
}
bool cIndexFile::Write(uchar PictureType, uchar FileNumber, int FileOffset)
{
if (f >= 0) {
tIndex i = { FileOffset, PictureType, FileNumber, 0 };
if (safe_write(f, &i, sizeof(i)) < 0) {
LOG_ERROR_STR(fileName);
close(f);
f = -1;
return false;
}
last++;
}
return f >= 0;
}
bool cIndexFile::Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType, int *Length)
{
if (index) {
CatchUp(Index);
if (Index >= 0 && Index <= last) {
*FileNumber = index[Index].number;
*FileOffset = index[Index].offset;
if (PictureType)
*PictureType = index[Index].type;
if (Length) {
int fn = index[Index + 1].number;
int fo = index[Index + 1].offset;
if (fn == *FileNumber)
*Length = fo - *FileOffset;
else
*Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly)
}
return true;
}
}
return false;
}
int cIndexFile::GetNextIFrame(int Index, bool Forward, uchar *FileNumber, int *FileOffset, int *Length, bool StayOffEnd)
{
if (index) {
CatchUp();
int d = Forward ? 1 : -1;
for (;;) {
Index += d;
if (Index >= 0 && Index < last - ((Forward && StayOffEnd) ? 100 : 0)) {
if (index[Index].type == I_FRAME) {
if (FileNumber)
*FileNumber = index[Index].number;
else
FileNumber = &index[Index].number;
if (FileOffset)
*FileOffset = index[Index].offset;
else
FileOffset = &index[Index].offset;
if (Length) {
// all recordings end with a non-I_FRAME, so the following should be safe:
int fn = index[Index + 1].number;
int fo = index[Index + 1].offset;
if (fn == *FileNumber)
*Length = fo - *FileOffset;
else {
esyslog("ERROR: 'I' frame at end of file #%d", *FileNumber);
*Length = -1;
}
}
return Index;
}
}
else
break;
}
}
return -1;
}
int cIndexFile::Get(uchar FileNumber, int FileOffset)
{
if (index) {
CatchUp();
//TODO implement binary search!
int i;
for (i = 0; i < last; i++) {
if (index[i].number > FileNumber || (index[i].number == FileNumber) && index[i].offset >= FileOffset)
break;
}
return i;
}
return -1;
}
// --- cFileName -------------------------------------------------------------
#include <errno.h>
#include <unistd.h>
#include "videodir.h"
#define MAXFILESPERRECORDING 255
#define RECORDFILESUFFIX "/%03d.vdr"
#define RECORDFILESUFFIXLEN 20 // some additional bytes for safety...
cFileName::cFileName(const char *FileName, bool Record, bool Blocking)
{
file = -1;
fileNumber = 0;
record = Record;
blocking = Blocking;
// Prepare the file name:
fileName = new char[strlen(FileName) + RECORDFILESUFFIXLEN];
if (!fileName) {
esyslog("ERROR: can't copy file name '%s'", fileName);
return;
}
strcpy(fileName, FileName);
pFileNumber = fileName + strlen(fileName);
SetOffset(1);
}
cFileName::~cFileName()
{
Close();
delete fileName;
}
int cFileName::Open(void)
{
if (file < 0) {
int BlockingFlag = blocking ? 0 : O_NONBLOCK;
if (record) {
dsyslog("recording to '%s'", fileName);
file = OpenVideoFile(fileName, O_RDWR | O_CREAT | BlockingFlag);
if (file < 0)
LOG_ERROR_STR(fileName);
}
else {
if (access(fileName, R_OK) == 0) {
dsyslog("playing '%s'", fileName);
file = open(fileName, O_RDONLY | BlockingFlag);
if (file < 0)
LOG_ERROR_STR(fileName);
}
else if (errno != ENOENT)
LOG_ERROR_STR(fileName);
}
}
return file;
}
void cFileName::Close(void)
{
if (file >= 0) {
if ((record && CloseVideoFile(file) < 0) || (!record && close(file) < 0))
LOG_ERROR_STR(fileName);
file = -1;
}
}
int cFileName::SetOffset(int Number, int Offset)
{
if (fileNumber != Number)
Close();
if (0 < Number && Number <= MAXFILESPERRECORDING) {
fileNumber = Number;
sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
if (record) {
if (access(fileName, F_OK) == 0) // file exists, let's try next suffix
return SetOffset(Number + 1);
else if (errno != ENOENT) { // something serious has happened
LOG_ERROR_STR(fileName);
return -1;
}
// found a non existing file suffix
}
if (Open() >= 0) {
if (!record && Offset >= 0 && lseek(file, Offset, SEEK_SET) != Offset) {
LOG_ERROR_STR(fileName);
return -1;
}
}
return file;
}
esyslog("ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
return -1;
}
int cFileName::NextFile(void)
{
return SetOffset(fileNumber + 1);
}
const char *IndexToHMSF(int Index, bool WithFrame)
{
static char buffer[16];
int f = (Index % FRAMESPERSEC) + 1;
int s = (Index / FRAMESPERSEC);
int m = s / 60 % 60;
int h = s / 3600;
s %= 60;
snprintf(buffer, sizeof(buffer), WithFrame ? "%d:%02d:%02d.%02d" : "%d:%02d:%02d", h, m, s, f);
return buffer;
}
int HMSFToIndex(const char *HMSF)
{
int h, m, s, f = 0;
if (3 <= sscanf(HMSF, "%d:%d:%d.%d", &h, &m, &s, &f))
return (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
return 0;
}
int SecondsToFrames(int Seconds)
{
return Seconds * FRAMESPERSEC;
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: recording.h 1.22 2002/02/03 11:59:49 kls Exp $
* $Id: recording.h 1.23 2002/06/16 11:29:47 kls Exp $
*/
#ifndef __RECORDING_H
@ -104,4 +104,63 @@ public:
static void InvokeCommand(const char *State, const char *RecordingFileName);
};
//XXX+
#define FRAMESPERSEC 25
// The maximum file size is limited by the range that can be covered
// with 'int'. 4GB might be possible (if the range is considered
// 'unsigned'), 2GB should be possible (even if the range is considered
// 'signed'), so let's use 2000MB for absolute safety (the actual file size
// may be slightly higher because we stop recording only before the next
// 'I' frame, to have a complete Group Of Pictures):
#define MAXVIDEOFILESIZE 2000 // MB
#define MINVIDEOFILESIZE 100 // MB
class cIndexFile {
private:
struct tIndex { int offset; uchar type; uchar number; short reserved; };
int f;
char *fileName;
int size, last;
tIndex *index;
cResumeFile resumeFile;
bool CatchUp(int Index = -1);
public:
cIndexFile(const char *FileName, bool Record);
~cIndexFile();
bool Ok(void) { return index != NULL; }
bool Write(uchar PictureType, uchar FileNumber, int FileOffset);
bool Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType = NULL, int *Length = NULL);
int GetNextIFrame(int Index, bool Forward, uchar *FileNumber = NULL, int *FileOffset = NULL, int *Length = NULL, bool StayOffEnd = false);
int Get(uchar FileNumber, int FileOffset);
int Last(void) { CatchUp(); return last; }
int GetResume(void) { return resumeFile.Read(); }
bool StoreResume(int Index) { return resumeFile.Save(Index); }
};
class cFileName {
private:
int file;
int fileNumber;
char *fileName, *pFileNumber;
bool record;
bool blocking;
public:
cFileName(const char *FileName, bool Record, bool Blocking = false);
~cFileName();
const char *Name(void) { return fileName; }
int Number(void) { return fileNumber; }
int Open(void);
void Close(void);
int SetOffset(int Number, int Offset = 0);
int NextFile(void);
};
const char *IndexToHMSF(int Index, bool WithFrame = false);
// Converts the given index to a string, optionally containing the frame number.
int HMSFToIndex(const char *HMSF);
// Converts the given string (format: "hh:mm:ss.ff") to an index.
int SecondsToFrames(int Seconds); //XXX+ ->player???
// Returns the number of frames corresponding to the given number of seconds.
#endif //__RECORDING_H

View File

@ -1,5 +1,5 @@
/*
* ringbuffer.c: A threaded ring buffer
* ringbuffer.c: A ring buffer
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
@ -7,50 +7,25 @@
* Parts of this file were inspired by the 'ringbuffy.c' from the
* LinuxDVB driver (see linuxtv.org).
*
* $Id: ringbuffer.c 1.8 2002/05/18 08:54:52 kls Exp $
* $Id: ringbuffer.c 1.9 2002/06/16 11:24:40 kls Exp $
*/
#include "ringbuffer.h"
#include <unistd.h>
#include "tools.h"
// --- cRingBufferInputThread -------------------------------------------------
class cRingBufferInputThread : public cThread {
private:
cRingBuffer *ringBuffer;
protected:
virtual void Action(void) { ringBuffer->Input(); }
public:
cRingBufferInputThread(cRingBuffer *RingBuffer) { ringBuffer = RingBuffer; }
};
// --- cRingBufferOutputThread ------------------------------------------------
class cRingBufferOutputThread : public cThread {
private:
cRingBuffer *ringBuffer;
protected:
virtual void Action(void) { ringBuffer->Output(); }
public:
cRingBufferOutputThread(cRingBuffer *RingBuffer) { ringBuffer = RingBuffer; }
};
// --- cRingBuffer ------------------------------------------------------------
// --- cRingBuffer -----------------------------------------------------------
cRingBuffer::cRingBuffer(int Size, bool Statistics)
{
size = Size;
statistics = Statistics;
inputThread = NULL;
outputThread = NULL;
busy = false;
maxFill = 0;
lastPercent = 0;
}
cRingBuffer::~cRingBuffer()
{
delete inputThread;
delete outputThread;
if (statistics)
dsyslog("buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1));
}
@ -79,45 +54,13 @@ void cRingBuffer::EnableGet(void)
readyForGet.Broadcast();
}
bool cRingBuffer::Start(void)
{
if (!busy) {
busy = true;
outputThread = new cRingBufferOutputThread(this);
if (!outputThread->Start())
DELETENULL(outputThread);
inputThread = new cRingBufferInputThread(this);
if (!inputThread->Start()) {
DELETENULL(inputThread);
DELETENULL(outputThread);
}
busy = outputThread && inputThread;
}
return busy;
}
bool cRingBuffer::Active(void)
{
return outputThread && outputThread->Active() && inputThread && inputThread->Active();
}
void cRingBuffer::Stop(void)
{
busy = false;
for (time_t t0 = time(NULL) + 3; time(NULL) < t0; ) {
if (!((outputThread && outputThread->Active()) || (inputThread && inputThread->Active())))
break;
}
DELETENULL(inputThread);
DELETENULL(outputThread);
}
// --- cRingBufferLinear ----------------------------------------------------
// --- cRingBufferLinear -----------------------------------------------------
cRingBufferLinear::cRingBufferLinear(int Size, bool Statistics)
:cRingBuffer(Size, Statistics)
{
buffer = NULL;
getThreadPid = -1;
if (Size > 1) { // 'Size - 1' must not be 0!
buffer = new uchar[Size];
if (!buffer)
@ -146,6 +89,8 @@ void cRingBufferLinear::Clear(void)
Lock();
head = tail = 0;
Unlock();
EnablePut();
EnableGet();
}
int cRingBufferLinear::Put(const uchar *Data, int Count)
@ -159,11 +104,13 @@ int cRingBufferLinear::Put(const uchar *Data, int Count)
int fill = Size() - free - 1 + Count;
if (fill >= Size())
fill = Size() - 1;
if (fill > maxFill) {
if (fill > maxFill)
maxFill = fill;
int percent = maxFill * 100 / (Size() - 1);
int percent = maxFill * 100 / (Size() - 1) / 5 * 5;
if (abs(lastPercent - percent) >= 5) {
if (percent > 75)
dsyslog("buffer usage: %d%%", percent);
dsyslog("buffer usage: %d%% (pid=%d)", percent, getThreadPid);
lastPercent = percent;
}
}
if (free > 0) {
@ -185,6 +132,7 @@ int cRingBufferLinear::Put(const uchar *Data, int Count)
else
Count = 0;
Unlock();
EnableGet();
}
return Count;
}
@ -193,6 +141,8 @@ int cRingBufferLinear::Get(uchar *Data, int Count)
{
if (Count > 0) {
Lock();
if (getThreadPid < 0)
getThreadPid = getpid();
int rest = Size() - tail;
int diff = head - tail;
int cont = (diff >= 0) ? diff : Size() + diff;
@ -213,6 +163,8 @@ int cRingBufferLinear::Get(uchar *Data, int Count)
else
Count = 0;
Unlock();
if (Count == 0)
WaitForGet();
}
return Count;
}
@ -255,7 +207,7 @@ void cRingBufferFrame::Clear(void)
{
Lock();
const cFrame *p;
while ((p = Get(false)) != NULL)
while ((p = Get()) != NULL)
Drop(p);
Unlock();
EnablePut();
@ -279,17 +231,14 @@ bool cRingBufferFrame::Put(cFrame *Frame)
EnableGet();
return true;
}
WaitForPut();
return false;
}
const cFrame *cRingBufferFrame::Get(bool Wait)
const cFrame *cRingBufferFrame::Get(void)
{
Lock();
cFrame *p = head ? head->next : NULL;
Unlock();
if (!p && Wait)
WaitForGet();
return p;
}

View File

@ -1,10 +1,10 @@
/*
* ringbuffer.h: A threaded ring buffer
* ringbuffer.h: A ring buffer
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: ringbuffer.h 1.5 2001/11/03 10:41:33 kls Exp $
* $Id: ringbuffer.h 1.6 2002/06/16 11:30:07 kls Exp $
*/
#ifndef __RINGBUFFER_H
@ -12,24 +12,17 @@
#include "thread.h"
typedef unsigned char uchar;
class cRingBufferInputThread;
class cRingBufferOutputThread;
typedef unsigned char uchar;//XXX+
class cRingBuffer {
friend class cRingBufferInputThread;
friend class cRingBufferOutputThread;
private:
cRingBufferInputThread *inputThread;
cRingBufferOutputThread *outputThread;
cMutex mutex;
cCondVar readyForPut, readyForGet;
cMutex putMutex, getMutex;
int size;
bool busy;
protected:
int maxFill;//XXX
int lastPercent;
bool statistics;//XXX
void WaitForPut(void);
void WaitForGet(void);
@ -41,26 +34,19 @@ protected:
void Lock(void) { mutex.Lock(); }
void Unlock(void) { mutex.Unlock(); }
int Size(void) { return size; }
bool Busy(void) { return busy; }
virtual void Input(void) = 0;
// Runs as a separate thread and shall continuously read data from
// a source and call Put() to store the data in the ring buffer.
virtual void Output(void) = 0;
// Runs as a separate thread and shall continuously call Get() to
// retrieve data from the ring buffer and write it to a destination.
public:
cRingBuffer(int Size, bool Statistics = false);
virtual ~cRingBuffer();
bool Start(void);
bool Active(void);
void Stop(void);
};
class cRingBufferLinear : public cRingBuffer {
private:
int head, tail;
uchar *buffer;
protected:
pid_t getThreadPid;
public:
cRingBufferLinear(int Size, bool Statistics = false);
virtual ~cRingBufferLinear();
virtual int Available(void);
virtual void Clear(void);
// Immediately clears the ring buffer.
@ -70,9 +56,6 @@ protected:
int Get(uchar *Data, int Count);
// Gets at most Count bytes of Data from the ring buffer.
// Returns the number of bytes actually retrieved.
public:
cRingBufferLinear(int Size, bool Statistics = false);
virtual ~cRingBufferLinear();
};
enum eFrameType { ftUnknown, ftVideo, ftAudio, ftDolby };
@ -99,21 +82,20 @@ private:
cFrame *head;
int currentFill;
void Delete(const cFrame *Frame);
protected:
public:
cRingBufferFrame(int Size, bool Statistics = false);
virtual ~cRingBufferFrame();
virtual int Available(void);
virtual void Clear(void);
// Immediately clears the ring buffer.
bool Put(cFrame *Frame);
// Puts the Frame into the ring buffer.
// Returns true if this was possible.
const cFrame *Get(bool Wait = true);
const cFrame *Get(void);
// Gets the next frame from the ring buffer.
// The actual data still remains in the buffer until Drop() is called.
void Drop(const cFrame *Frame);
// Drops the Frame that has just been fetched with Get().
public:
cRingBufferFrame(int Size, bool Statistics = false);
virtual ~cRingBufferFrame();
};
#endif // __RINGBUFFER_H

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: status.c 1.1 2002/05/19 14:54:30 kls Exp $
* $Id: status.c 1.2 2002/06/16 12:10:44 kls Exp $
*/
#include "status.h"
@ -23,22 +23,22 @@ cStatusMonitor::~cStatusMonitor()
statusMonitors.Del(this, false);
}
void cStatusMonitor::MsgChannelSwitch(const cDvbApi *DvbApi, int ChannelNumber)
void cStatusMonitor::MsgChannelSwitch(const cDevice *Device, int ChannelNumber)
{
for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->ChannelSwitch(DvbApi, ChannelNumber);
sm->ChannelSwitch(Device, ChannelNumber);
}
void cStatusMonitor::MsgRecording(const cDvbApi *DvbApi, const char *Name)
void cStatusMonitor::MsgRecording(const cDevice *Device, const char *Name)
{
for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->Recording(DvbApi, Name);
sm->Recording(Device, Name);
}
void cStatusMonitor::MsgReplaying(const cDvbApi *DvbApi, const char *Name)
void cStatusMonitor::MsgReplaying(const cDvbPlayerControl *DvbPlayerControl, const char *Name)
{
for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->Replaying(DvbApi, Name);
sm->Replaying(DvbPlayerControl, Name);
}
void cStatusMonitor::MsgSetVolume(int Volume, bool Absolute)

View File

@ -4,14 +4,15 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: status.h 1.1 2002/05/19 14:54:15 kls Exp $
* $Id: status.h 1.2 2002/06/16 12:09:55 kls Exp $
*/
#ifndef __STATUS_H
#define __STATUS_H
#include "config.h"
#include "dvbapi.h"
#include "device.h"
#include "dvbplayer.h"
#include "tools.h"
class cStatusMonitor : public cListObject {
@ -19,15 +20,15 @@ private:
static cList<cStatusMonitor> statusMonitors;
protected:
// These functions can be implemented by derived classes to receive status information:
virtual void ChannelSwitch(const cDvbApi *DvbApi, int ChannelNumber) {}
// Indicates a channel switch on DVB device DvbApi.
virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber) {}
// Indicates a channel switch on the given DVB device.
// If ChannelNumber is 0, this is before the channel is being switched,
// otherwise ChannelNumber is the number of the channel that has been switched to.
virtual void Recording(const cDvbApi *DvbApi, const char *Name) {}
// DVB device DvbApi has started recording Name. Name is the full directory
virtual void Recording(const cDevice *Device, const char *Name) {}
// The given DVB device has started recording Name. Name is the full directory
// name of the recording. If Name is NULL, the recording has ended.
virtual void Replaying(const cDvbApi *DvbApi, const char *Name) {}
// DVB device DvbApi has started replaying Name. Name is the full directory
virtual void Replaying(const cDvbPlayerControl *DvbPlayerControl, const char *Name) {}
// The given player control has started replaying Name. Name is the full directory
// name of the recording. If Name is NULL, the replay has ended.
virtual void SetVolume(int Volume, bool Absolute) {}
// The volume has been set to the given value, either
@ -57,9 +58,9 @@ public:
cStatusMonitor(void);
virtual ~cStatusMonitor();
// These functions are called whenever the related status information changes:
static void MsgChannelSwitch(const cDvbApi *DvbApi, int ChannelNumber);
static void MsgRecording(const cDvbApi *DvbApi, const char *Name);
static void MsgReplaying(const cDvbApi *DvbApi, const char *Name);
static void MsgChannelSwitch(const cDevice *Device, int ChannelNumber);
static void MsgRecording(const cDevice *Device, const char *Name);
static void MsgReplaying(const cDvbPlayerControl *DvbPlayerControl, const char *Name);
static void MsgSetVolume(int Volume, bool Absolute);
static void MsgOsdClear(void);
static void MsgOsdTitle(const char *Title);

26
svdrp.c
View File

@ -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.37 2002/05/13 16:32:05 kls Exp $
* $Id: svdrp.c 1.38 2002/06/10 16:30:00 kls Exp $
*/
#include "svdrp.h"
@ -27,7 +27,7 @@
#include <sys/time.h>
#include <unistd.h>
#include "config.h"
#include "dvbapi.h"
#include "device.h"
#include "interface.h"
#include "tools.h"
@ -390,12 +390,12 @@ void cSVDRP::CmdCHAN(const char *Option)
n = o;
}
else if (strcmp(Option, "-") == 0) {
n = cDvbApi::CurrentChannel();
n = cDevice::CurrentChannel();
if (n > 1)
n--;
}
else if (strcmp(Option, "+") == 0) {
n = cDvbApi::CurrentChannel();
n = cDevice::CurrentChannel();
if (n < Channels.MaxNumber())
n++;
}
@ -430,11 +430,11 @@ void cSVDRP::CmdCHAN(const char *Option)
return;
}
}
cChannel *channel = Channels.GetByNumber(cDvbApi::CurrentChannel());
cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
if (channel)
Reply(250, "%d %s", channel->number, channel->name);
else
Reply(550, "Unable to find channel \"%d\"", cDvbApi::CurrentChannel());
Reply(550, "Unable to find channel \"%d\"", cDevice::CurrentChannel());
}
void cSVDRP::CmdDELC(const char *Option)
@ -541,7 +541,7 @@ void cSVDRP::CmdGRAB(const char *Option)
Reply(501, "Unexpected parameter \"%s\"", p);
return;
}
if (cDvbApi::PrimaryDvbApi->GrabImage(FileName, Jpeg, Quality, SizeX, SizeY))
if (cDevice::PrimaryDevice()->GrabImage(FileName, Jpeg, Quality, SizeX, SizeY))
Reply(250, "Grabbed image %s", Option);
else
Reply(451, "Grab image failed");
@ -926,22 +926,22 @@ void cSVDRP::CmdVOLU(const char *Option)
{
if (*Option) {
if (isnumber(Option))
cDvbApi::PrimaryDvbApi->SetVolume(strtol(Option, NULL, 10), true);
cDevice::PrimaryDevice()->SetVolume(strtol(Option, NULL, 10), true);
else if (strcmp(Option, "+") == 0)
cDvbApi::PrimaryDvbApi->SetVolume(VOLUMEDELTA);
cDevice::PrimaryDevice()->SetVolume(VOLUMEDELTA);
else if (strcmp(Option, "-") == 0)
cDvbApi::PrimaryDvbApi->SetVolume(-VOLUMEDELTA);
cDevice::PrimaryDevice()->SetVolume(-VOLUMEDELTA);
else if (strcasecmp(Option, "MUTE") == 0)
cDvbApi::PrimaryDvbApi->ToggleMute();
cDevice::PrimaryDevice()->ToggleMute();
else {
Reply(501, "Unknown option: \"%s\"", Option);
return;
}
}
if (cDvbApi::PrimaryDvbApi->IsMute())
if (cDevice::PrimaryDevice()->IsMute())
Reply(250, "Audio is mute");
else
Reply(250, "Audio volume is %d", cDvbApi::CurrentVolume());
Reply(250, "Audio volume is %d", cDevice::CurrentVolume());
}
#define CMD(c) (strcasecmp(Cmd, c) == 0)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: thread.c 1.20 2002/05/13 16:32:09 kls Exp $
* $Id: thread.c 1.21 2002/06/10 16:30:00 kls Exp $
*/
#include "thread.h"
@ -147,6 +147,7 @@ bool cThread::Active(void)
void cThread::Cancel(int WaitSeconds)
{
running = false;
if (WaitSeconds > 0) {
for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) {
if (!Active())

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: thread.h 1.12 2002/02/23 13:53:38 kls Exp $
* $Id: thread.h 1.13 2002/06/10 16:30:00 kls Exp $
*/
#ifndef __THREAD_H
@ -54,9 +54,9 @@ private:
static bool signalHandlerInstalled;
static void SignalHandler(int signum);
static void *StartThread(cThread *Thread);
protected:
void Lock(void) { mutex.Lock(); }
void Unlock(void) { mutex.Unlock(); }
protected:
void WakeUp(void);
virtual void Action(void) = 0;
void Cancel(int WaitSeconds = 0);

View File

@ -4,13 +4,13 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: tools.h 1.46 2002/05/18 15:10:10 kls Exp $
* $Id: tools.h 1.47 2002/06/10 16:30:00 kls Exp $
*/
#ifndef __TOOLS_H
#define __TOOLS_H
//#include <errno.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
@ -18,6 +18,8 @@
#include <sys/stat.h>
#include <sys/types.h>
typedef unsigned char uchar;
extern int SysLogLevel;
#define esyslog(a...) void( (SysLogLevel > 0) ? syslog(LOG_ERR, a) : void() )
@ -36,6 +38,9 @@ extern int SysLogLevel;
#define DELETENULL(p) (delete (p), p = NULL)
#define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls
#define FATALERRNO (errno != EAGAIN && errno != EINTR)
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 void swap(T &a, T &b) { T t = a; a = b; b = t; }

51
vdr.c
View File

@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/people/kls/vdr
*
* $Id: vdr.c 1.113 2002/05/20 11:02:10 kls Exp $
* $Id: vdr.c 1.114 2002/06/16 11:30:28 kls Exp $
*/
#include <getopt.h>
@ -31,7 +31,7 @@
#include <stdlib.h>
#include <unistd.h>
#include "config.h"
#include "dvbapi.h"
#include "device.h"
#include "eitscan.h"
#include "i18n.h"
#include "interface.h"
@ -118,15 +118,17 @@ int main(int argc, char *argv[])
int c;
while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:L:mp:P:r:s:t:v:Vw:", long_options, NULL)) != -1) {
switch (c) {
case 'a': cDvbApi::SetAudioCommand(optarg);
/*XXX+
case 'a': cDevice::SetAudioCommand(optarg);
break;
XXX*/
case 'c': ConfigDirectory = optarg;
break;
case 'd': DaemonMode = true; break;
case 'D': if (isnumber(optarg)) {
int n = atoi(optarg);
if (0 <= n && n < MAXDVBAPI) {
cDvbApi::SetUseDvbApi(n);
if (0 <= n && n < MAXDEVICES) {
cDevice::SetUseDevice(n);
break;
}
}
@ -323,10 +325,10 @@ int main(int argc, char *argv[])
// DVB interfaces:
if (!cDvbApi::Initialize())
if (!cDevice::Initialize())
return 2;
cDvbApi::SetPrimaryDvbApi(Setup.PrimaryDVB);
cDevice::SetPrimaryDevice(Setup.PrimaryDVB);
cSIProcessor::Read();
@ -343,9 +345,9 @@ int main(int argc, char *argv[])
Channels.SwitchTo(Setup.CurrentChannel);
if (MuteAudio)
cDvbApi::PrimaryDvbApi->ToggleMute();
cDevice::PrimaryDevice()->ToggleMute();
else
cDvbApi::PrimaryDvbApi->SetVolume(Setup.CurrentVolume, true);
cDevice::PrimaryDevice()->SetVolume(Setup.CurrentVolume, true);
cEITScanner EITScanner;
@ -371,7 +373,7 @@ int main(int argc, char *argv[])
cOsdObject *Menu = NULL;
cReplayControl *ReplayControl = NULL;
int LastChannel = -1;
int PreviousChannel = cDvbApi::CurrentChannel();
int PreviousChannel = cDevice::CurrentChannel();
time_t LastActivity = 0;
int MaxLatencyTime = 0;
bool ForceShutdown = false;
@ -396,12 +398,12 @@ int main(int argc, char *argv[])
}
}
// Channel display:
if (!EITScanner.Active() && cDvbApi::CurrentChannel() != LastChannel) {
if (!EITScanner.Active() && cDevice::CurrentChannel() != LastChannel) {
if (!Menu)
Menu = new cDisplayChannel(cDvbApi::CurrentChannel(), LastChannel > 0);
Menu = new cDisplayChannel(cDevice::CurrentChannel(), LastChannel > 0);
if (LastChannel > 0)
PreviousChannel = LastChannel;
LastChannel = cDvbApi::CurrentChannel();
LastChannel = cDevice::CurrentChannel();
}
// Timers and Recordings:
if (!Menu) {
@ -429,11 +431,11 @@ int main(int argc, char *argv[])
case kVolDn:
case kMute:
if (key == kMute) {
if (!cDvbApi::PrimaryDvbApi->ToggleMute() && !Menu)
if (!cDevice::PrimaryDevice()->ToggleMute() && !Menu)
break; // no need to display "mute off"
}
else
cDvbApi::PrimaryDvbApi->SetVolume(NORMALKEY(key) == kVolDn ? -VOLUMEDELTA : VOLUMEDELTA);
cDevice::PrimaryDevice()->SetVolume(NORMALKEY(key) == kVolDn ? -VOLUMEDELTA : VOLUMEDELTA);
if (!Menu && (!ReplayControl || !ReplayControl->Visible()))
Menu = cDisplayVolume::Create();
cDisplayVolume::Process(key);
@ -477,7 +479,7 @@ int main(int argc, char *argv[])
case osSwitchDvb:
DELETENULL(*Interact);
Interface->Info(tr("Switching primary DVB..."));
cDvbApi::SetPrimaryDvbApi(Setup.PrimaryDVB);
cDevice::SetPrimaryDevice(Setup.PrimaryDVB);
break;
case osBack:
case osEnd: DELETENULL(*Interact);
@ -490,7 +492,7 @@ int main(int argc, char *argv[])
switch (key) {
// Toggle channels:
case k0: {
int CurrentChannel = cDvbApi::CurrentChannel();
int CurrentChannel = cDevice::CurrentChannel();
Channels.SwitchTo(PreviousChannel);
PreviousChannel = CurrentChannel;
break;
@ -511,7 +513,7 @@ int main(int argc, char *argv[])
case kUp:
case kDown|k_Repeat:
case kDown: {
int n = cDvbApi::CurrentChannel() + (NORMALKEY(key) == kUp ? 1 : -1);
int n = cDevice::CurrentChannel() + (NORMALKEY(key) == kUp ? 1 : -1);
cChannel *channel = Channels.GetByNumber(n);
if (channel)
channel->Switch();
@ -527,14 +529,16 @@ int main(int argc, char *argv[])
}
if (!Menu) {
EITScanner.Process();
/*XXX+
if (!cVideoCutter::Active() && cVideoCutter::Ended()) {
if (cVideoCutter::Error())
Interface->Error(tr("Editing process failed!"));
else
Interface->Info(tr("Editing process finished"));
}
XXX*/
}
if (!*Interact && ((!cRecordControls::Active() && !cVideoCutter::Active()) || ForceShutdown)) {
if (!*Interact && ((!cRecordControls::Active() /*XXX+&& !cVideoCutter::Active()XXX*/) || ForceShutdown)) {
time_t Now = time(NULL);
if (Now - LastActivity > ACTIVITYTIMEOUT) {
// Shutdown:
@ -593,16 +597,17 @@ int main(int argc, char *argv[])
}
if (Interrupted)
isyslog("caught signal %d", Interrupted);
cVideoCutter::Stop();
cRecordControls::Shutdown();
//XXX+cVideoCutter::Stop();
delete Menu;
delete ReplayControl;
delete Interface;
cOsd::Shutdown();
PluginManager.Shutdown(true);
Setup.CurrentChannel = cDvbApi::CurrentChannel();
Setup.CurrentVolume = cDvbApi::CurrentVolume();
Setup.CurrentChannel = cDevice::CurrentChannel();
Setup.CurrentVolume = cDevice::CurrentVolume();
Setup.Save();
cDvbApi::Shutdown();
cDevice::Shutdown();
if (WatchdogTimeout > 0)
dsyslog("max. latency time %d seconds", MaxLatencyTime);
isyslog("exiting");