summaryrefslogtreecommitdiff
path: root/transfer.c
blob: f34dea7d6347a35a2bc71c9c6c4bcf46e6f5b889 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
 * transfer.c: Transfer mode
 *
 * See the main source file 'vdr.c' for copyright information and
 * how to reach the author.
 *
 * $Id: transfer.c 1.30 2005/08/14 10:55:03 kls Exp $
 */

#include "transfer.h"

#define TRANSFERBUFSIZE  MEGABYTE(2)
#define POLLTIMEOUTS_BEFORE_DEVICECLEAR 6

// --- cTransfer -------------------------------------------------------------

cTransfer::cTransfer(int VPid, const int *APids, const int *DPids, const int *SPids)
:cReceiver(0, -1, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
,cThread("transfer")
{
  ringBuffer = new cRingBufferLinear(TRANSFERBUFSIZE, TS_SIZE * 2, true, "Transfer");
  remux = new cRemux(VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids);
  needsBufferReserve = Setup.UseDolbyDigital && VPid != 0 && DPids && DPids[0] != 0;
}

cTransfer::~cTransfer()
{
  cReceiver::Detach();
  cPlayer::Detach();
  delete remux;
  delete ringBuffer;
}

void cTransfer::Activate(bool On)
{
  if (On)
     Start();
  else
     Cancel(3);
}

void cTransfer::Receive(uchar *Data, int Length)
{
  if (IsAttached() && Running()) {
     int p = ringBuffer->Put(Data, Length);
     if (p != Length && Running())
        ringBuffer->ReportOverflow(Length - p);
     return;
     }
}

#define FW_NEEDS_BUFFER_RESERVE_FOR_AC3
#ifdef FW_NEEDS_BUFFER_RESERVE_FOR_AC3
//XXX This is a very ugly hack to allow cDvbOsd to reduce the buffer
//XXX requirements in cTransfer if it detects a 4MB full featured DVB card.
bool DvbCardWith4MBofSDRAM = false;
#endif

void cTransfer::Action(void)
{
  int PollTimeouts = 0;
  uchar *p = NULL;
  int Result = 0;
#ifdef FW_NEEDS_BUFFER_RESERVE_FOR_AC3
  bool GotBufferReserve = false;
  int RequiredBufferReserve = KILOBYTE(DvbCardWith4MBofSDRAM ? 288 : 576);
#endif
  while (Running()) {
#ifdef FW_NEEDS_BUFFER_RESERVE_FOR_AC3
        if (needsBufferReserve && !GotBufferReserve) {
           //XXX For dolby we've to fill the buffer because the firmware does
           //XXX not decode dolby but use a PCM stream for transport, therefore
           //XXX the firmware has not enough buffer for noiseless skipping early
           //XXX PCM samples (each dolby frame requires 6144 bytes in PCM and
           //XXX audio is mostly to early in comparison to video).
           //XXX To resolve this, the remuxer or PlayPes() should synchronize
           //XXX audio with the video frames. 2004/09/09 Werner
           if (ringBuffer->Available() < RequiredBufferReserve) { // used to be MAXFRAMESIZE, but the HDTV value of KILOBYTE(512) is way too much here
              cCondWait::SleepMs(20); // allow the buffer to collect some reserve
              continue;
              }
           else
              GotBufferReserve = true;
           }
#endif
        int Count;
        uchar *b = ringBuffer->Get(Count);
        if (b) {
           if (ringBuffer->Available() > TRANSFERBUFSIZE * 9 / 10) {
              // If the buffer runs full, we have no chance of ever catching up
              // since the data comes in at the same rate as it goes out (it's "live").
              // So let's clear the buffer instead of suffering from permanent
              // overflows.
              dsyslog("clearing transfer buffer to avoid overflows");
              DeviceClear();
              ringBuffer->Clear();
              remux->Clear();
              PlayPes(NULL, 0);
              p = NULL;
#ifdef FW_NEEDS_BUFFER_RESERVE_FOR_AC3
              GotBufferReserve = false;
#endif
              continue;
              }
           Count = remux->Put(b, Count);
           if (Count)
              ringBuffer->Del(Count);
           }
        if (!p)
           p = remux->Get(Result);
        if (p) {
           cPoller Poller;
           if (DevicePoll(Poller, 100)) {
              PollTimeouts = 0;
              int w = PlayPes(p, Result);
              if (w > 0) {
                 p += w;
                 Result -= w;
                 remux->Del(w);
                 if (Result <= 0)
                    p = NULL;
                 }
              else if (w < 0 && FATALERRNO)
                 LOG_ERROR;
              }
           else {
              PollTimeouts++;
              if (PollTimeouts == POLLTIMEOUTS_BEFORE_DEVICECLEAR) {
                 dsyslog("clearing device because of consecutive poll timeouts");
                 DeviceClear();
                 ringBuffer->Clear();
                 remux->Clear();
                 PlayPes(NULL, 0);
                 p = NULL;
#ifdef FW_NEEDS_BUFFER_RESERVE_FOR_AC3
                 GotBufferReserve = false;
#endif
                 }
              }
           }
        }
}

// --- cTransferControl ------------------------------------------------------

cDevice *cTransferControl::receiverDevice = NULL;

cTransferControl::cTransferControl(cDevice *ReceiverDevice, int VPid, const int *APids, const int *DPids, const int *SPids)
:cControl(transfer = new cTransfer(VPid, APids, DPids, SPids), true)
{
  ReceiverDevice->AttachReceiver(transfer);
  receiverDevice = ReceiverDevice;
}

cTransferControl::~cTransferControl()
{
  receiverDevice = NULL;
  delete transfer;
}