vdr/transfer.c
Klaus Schmidinger 48fd2b04e9 Version 1.1.20
- Now checking if there is a connection to the keyboard (thanks to Jaakko Hyvätti)
  and only creating the KBD remote control if VDR is running in the foreground.
- Fixed taking an active SVDRP connection into account when doing shutdown (thanks
  to Gregoire Favre for reporting this one).
- Changed setting of CXX and CXXFLAGS variables in Makefile, so that an externally
  defined value will be taken if present (suggested by Robert Schiele).
  Plugin authors should please change the lines

  CXX      = g++
  CXXFLAGS = -O2 -Wall -Woverloaded-virtual

  in their Makefile to

  CXX      ?= g++
  CXXFLAGS ?= -O2 -Wall -Woverloaded-virtual

- Fixed recording overlapping timers on the same channel in case
  DO_REC_AND_PLAY_ON_PRIMARY_DEVICE and/or DO_MULTIPLE_RECORDINGS is not defined
  (thanks to Jaakko Hyvätti).
- No longer stopping/restarting the DMX when switching audio channels (thanks to
  Sven Goethel).
- Fixed high CPU load in 'Transfer Mode' (thanks to Oliver Endriss).
- If a PC keyboard is used as remote control, the string entry fields in the
  menus now accept character input directly (however, this works only for keys that
  are not otherwise defined as remote control keys). Also, plugins can switch the
  cKbdRemote class into "raw mode", where all keyboard input will be made available
  through the new 'kKbd' key code and none of it will be processed as normal remote
  control functions (thanks to Jan Rieger for suggestions and testing).
- Fixed deleting characters in string entry fields in 'insert' mode.
- Now using "Doxygen" to generate source documentation (thanks to Walter Stroebel
  for providing an initial 'Doxyfile' configuration and adjusting some comments).
  See INSTALL for information how to do this. Some function descriptions have
  already been adapted to Doxygen, more will follow.
2002-12-15 18:00:00 +01:00

192 lines
5.1 KiB
C

/*
* transfer.c: Transfer mode
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: transfer.c 1.8 2002/12/14 13:14:53 kls Exp $
*/
#include "transfer.h"
//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)
// --- cTransfer -------------------------------------------------------------
cTransfer::cTransfer(int VPid, int APid1, int APid2, int DPid1, int DPid2)
:cReceiver(0, -1, 5, VPid, APid1, APid2, DPid1, DPid2)
{
ringBuffer = new cRingBufferLinear(VIDEOBUFSIZE, true);
remux = new cRemux(VPid, APid1, APid2, DPid1, DPid2);
canToggleAudioTrack = false;
audioTrack = 0xC0;
gotBufferReserve = false;
active = false;
}
cTransfer::~cTransfer()
{
cReceiver::Detach();
cPlayer::Detach();
delete remux;
delete ringBuffer;
}
void cTransfer::Activate(bool On)
{
if (On) {
if (!active)
Start();
}
else if (active) {
active = false;
Cancel(3);
}
}
void cTransfer::Receive(uchar *Data, int Length)
{
if (IsAttached()) {
int p = ringBuffer->Put(Data, Length);
if (p != Length && active)
esyslog("ERROR: ring buffer overflow (%d bytes dropped)", Length - p);
}
}
void cTransfer::Action(void)
{
dsyslog("transfer thread started (pid=%d)", getpid());
uchar b[MINVIDEODATA];
int r = 0;
active = true;
while (active) {
//XXX+ Maybe we need this to avoid "buffer empty" log messages from the driver.
//XXX+ But then again, it appears to play just fine without this...
/*
if (!gotBufferReserve) {
if (ringBuffer->Available() < 4 * MAXFRAMESIZE) {
usleep(100000); // allow the buffer to collect some reserve
continue;
}
else
gotBufferReserve = true;
}
*/
// Get data from the buffer:
int g = ringBuffer->Get(b + r, sizeof(b) - r);
if (g > 0)
r += g;
// Play the data:
if (r > 0) {
int Count = r, Result;
uchar *p = remux->Process(b, Count, Result);
if (p) {
StripAudioPackets(p, Result, audioTrack);
while (Result > 0 && active) {
int w = PlayVideo(p, Result);
if (w > 0) {
p += w;
Result -= w;
}
else if (w < 0 && FATALERRNO) {
LOG_ERROR;
break;
}
}
}
if (Count > 0) {
r -= Count;
if (r > 0)
memmove(b, b + Count, r);
}
}
else
usleep(1); // this keeps the CPU load low
}
dsyslog("transfer thread ended (pid=%d)", getpid());
}
void cTransfer::StripAudioPackets(uchar *b, int Length, uchar Except)
{
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)
PlayAudio(&b[i], l);
// continue with deleting the data - otherwise it disturbs DVB replay
case 0xC0 ... 0xC1: // audio
if (c == 0xC1)
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*/
}
}
int cTransfer::NumAudioTracks(void) const
{
return canToggleAudioTrack ? 2 : 1;
}
const char **cTransfer::GetAudioTracks(int *CurrentTrack) const
{
if (NumAudioTracks()) {
if (CurrentTrack)
*CurrentTrack = (audioTrack == 0xC0) ? 0 : 1;
static const char *audioTracks1[] = { "Audio 1", NULL };
static const char *audioTracks2[] = { "Audio 1", "Audio 2", NULL };
return NumAudioTracks() > 1 ? audioTracks2 : audioTracks1;
}
return NULL;
}
void cTransfer::SetAudioTrack(int Index)
{
if ((audioTrack == 0xC0) != (Index == 0)) {
audioTrack = (Index == 1) ? 0xC1 : 0xC0;
DeviceClear();
}
}
// --- cTransferControl ------------------------------------------------------
cTransferControl::cTransferControl(cDevice *ReceiverDevice, int VPid, int APid1, int APid2, int DPid1, int DPid2)
:cControl(transfer = new cTransfer(VPid, APid1, APid2, DPid1, DPid2), true)
{
ReceiverDevice->AttachReceiver(transfer);
}
cTransferControl::~cTransferControl()
{
delete transfer;
}