mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
- Completed the Finnish OSD texts (thanks to Rolf Ahrenberg). - Fixed some descriptor handling in 'libsi' (thanks to Stéphane Esté-Gracias). - Fixed handling the current menu item (thanks to Marc Hoppe). - Fixed assigning events to timers (they no longer get "stuck"). - Added log entries whenever the running status of an event changes (currently only logging the first 30 channels). - Fixed handling timers in VPS margin if the EPG scan is turned on (the EPG scan switched the device away from the channel, so it wouldn't see the change of the running status). - Fixed handling "itemized" texts in EPG data (thanks to Stéphane Esté-Gracias for pointing out this problem, and Marcel Wiesweg for improving 'libsi'). - Fixed handling VPS times at year boundaries. - Avoiding too many consecutive "ring buffer overflow" messages (which only slowed down performance even more). - Taking the Sid into account when detecting version changes in processing the PMT (thanks to Stéphane Esté-Gracias for pointing out this problem). - Completed the Russian OSD texts (thanks to Vyacheslav Dikonov). - Any newline characters in the 'description' of EPG events are now preserved to allow texts to be displayed the way the tv stations have formatted them. This was also necessary to better display itemized texts. - Fixed detecting the running status in case an empty EPG event is broadcast (thanks to Michael Pennewiß for pointing this out). - Improved performance when paging through very long menu lists. - Removed cSchedule::GetEventNumber() and cSchedule::NumEvents(). There is now cSchedule::Events() that returns the list of events directly. - Avoiding occasional bad responsiveness to user interaction caused by assigning events to timers. - Now explicitly turning on the LNB power at startup, because newer drivers don't do this any more (thanks to Oliver Endriss for pointing this out).
203 lines
5.5 KiB
C
203 lines
5.5 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.16 2004/03/07 14:40:15 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)
|
|
#define POLLTIMEOUTS_BEFORE_DEVICECLEAR 3
|
|
|
|
// --- cTransfer -------------------------------------------------------------
|
|
|
|
cTransfer::cTransfer(int VPid, int APid1, int APid2, int DPid1, int DPid2)
|
|
:cReceiver(0, -1, 5, VPid, APid1, APid2, DPid1, DPid2)
|
|
,cThread("transfer")
|
|
{
|
|
ringBuffer = new cRingBufferLinear(VIDEOBUFSIZE, TS_SIZE * 2, 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 i = 0;
|
|
while (active && Length > 0) {
|
|
if (i++ > 10) {
|
|
ringBuffer->ReportOverflow(Length);
|
|
break;
|
|
}
|
|
int p = ringBuffer->Put(Data, Length);
|
|
Length -= p;
|
|
Data += p;
|
|
}
|
|
}
|
|
}
|
|
|
|
void cTransfer::Action(void)
|
|
{
|
|
int PollTimeouts = 0;
|
|
active = true;
|
|
while (active) {
|
|
|
|
//XXX+ Maybe we need this to avoid buffer underruns in 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 r;
|
|
const uchar *b = ringBuffer->Get(r);
|
|
|
|
// Play the data:
|
|
|
|
if (b) {
|
|
int Count = r, Result;
|
|
uchar *p = remux->Process(b, Count, Result);
|
|
ringBuffer->Del(Count);
|
|
if (p) {
|
|
StripAudioPackets(p, Result, audioTrack);
|
|
while (Result > 0 && active) {
|
|
cPoller Poller;
|
|
if (DevicePoll(Poller, 100)) {
|
|
PollTimeouts = 0;
|
|
int w = PlayVideo(p, Result);
|
|
if (w > 0) {
|
|
p += w;
|
|
Result -= w;
|
|
}
|
|
else if (w < 0 && FATALERRNO) {
|
|
LOG_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
PollTimeouts++;
|
|
if (PollTimeouts == POLLTIMEOUTS_BEFORE_DEVICECLEAR) {
|
|
dsyslog("clearing device because of consecutive poll timeouts");
|
|
DeviceClear();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
usleep(1); // this keeps the CPU load low
|
|
}
|
|
}
|
|
|
|
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)
|
|
memset(&b[i], 0x00, min(l, Length-i));
|
|
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 ------------------------------------------------------
|
|
|
|
cDevice *cTransferControl::receiverDevice = NULL;
|
|
|
|
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);
|
|
receiverDevice = ReceiverDevice;
|
|
}
|
|
|
|
cTransferControl::~cTransferControl()
|
|
{
|
|
receiverDevice = NULL;
|
|
delete transfer;
|
|
}
|