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:
parent
b005b8fc4a
commit
a4bfddd2f9
21
HISTORY
21
HISTORY
@ -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.
|
||||
|
9
Makefile
9
Makefile
@ -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
|
||||
|
@ -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
11
audio.c
Normal 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
13
audio.h
Normal 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
|
29
config.c
29
config.c
@ -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) {
|
||||
|
10
config.h
10
config.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: config.h 1.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__;
|
||||
|
199
device.h
Normal file
199
device.h
Normal 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
733
dvbplayer.c
Normal 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
60
dvbplayer.h
Normal 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
3
eit.c
@ -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>
|
||||
|
20
eitscan.c
20
eitscan.c
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
186
menu.c
186
menu.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menu.c 1.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,23 +2411,24 @@ 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;
|
||||
@ -2434,14 +2441,15 @@ cRecordControl::cRecordControl(cDvbApi *DvbApi, cTimer *Timer)
|
||||
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)) {
|
||||
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(dvbApi, fileName);
|
||||
}
|
||||
Interface->DisplayRecording(dvbApi->CardIndex(), true);
|
||||
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
21
menu.h
@ -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
6
osd.c
@ -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
55
player.c
Normal 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
51
player.h
Normal 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
55
receiver.c
Normal 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
48
receiver.h
Normal 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
149
recorder.c
Normal 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
43
recorder.h
Normal 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
|
350
recording.c
350
recording.c
@ -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;
|
||||
}
|
||||
|
61
recording.h
61
recording.h
@ -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
|
||||
|
93
ringbuffer.c
93
ringbuffer.c
@ -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;
|
||||
}
|
||||
|
||||
|
42
ringbuffer.h
42
ringbuffer.h
@ -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
|
||||
|
14
status.c
14
status.c
@ -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)
|
||||
|
23
status.h
23
status.h
@ -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
26
svdrp.c
@ -10,7 +10,7 @@
|
||||
* and interact with the Video Disk Recorder - or write a full featured
|
||||
* graphical interface that sits on top of an SVDRP connection.
|
||||
*
|
||||
* $Id: svdrp.c 1.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)
|
||||
|
3
thread.c
3
thread.c
@ -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())
|
||||
|
4
thread.h
4
thread.h
@ -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);
|
||||
|
9
tools.h
9
tools.h
@ -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
51
vdr.c
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user