summaryrefslogtreecommitdiff
path: root/pes2ts.c
blob: f7d7f55a479bce26f3f383eb61c6729e1571983c (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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/* 
 * pes2ts.c: PES2TS remux
 *
 * See the README file for copyright information and how to reach the author.
 *
 */
#include <vdr/tools.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>

#include "tsworker.h"

#include "pes2ts.h"

cPES2TSRemux::cPES2TSRemux(int VPid, int APid):
    cThread("[ffnetdev] PES2TS remux"),
    m_OutputBuffer(new cRingBufferLinear(OUTPUTBUFSIZE, TS_SIZE * 2)),
    m_InputBuffer(new cRingBufferLinear(INPUTBUFSIZE, IPACKS)),
    m_Active(false),
    m_PlayModeChanged(false)
{
  vpid = VPid;
  apid = APid;
  m_InputBuffer->SetTimeouts(0, 1000);  // IMPORTANT to avoid busy wait in threads main loop and thus a high CPU load
  Start();
  OutputLocked = false;
}

cPES2TSRemux::~cPES2TSRemux()
{
  m_Active = false;
  delete m_InputBuffer;
  delete m_OutputBuffer;

}

void cPES2TSRemux::Action(void)
{
  unsigned int i;
  uchar acc=0;    // continutiy counter for audio packets
  uchar vcc=0;      // continutiy counter for video packets
  uchar *cc;        // either cc=&vcc; or cc=&acc;
  unsigned short pid=0;
  unsigned int packetlen;
  uchar ts[188];
  uchar pes[IPACKS];

  unsigned int minNeededPacketlen = 10; // needed for read packet len: 6 Should be enought ... but makes no sense

  
  m_Active = true;
  while (m_Active) {
    int count=0;
//    fprintf(stderr, "[ffnetdev] Remuxer: Inputbuffersize: %d, Outputbuffersize: %d\n", 
//	    m_InputBuffer->Available(), m_OutputBuffer->Available());

    if (m_PlayModeChanged)
    {
	cCondWait::SleepMs(1500);
	m_PlayModeChanged = false;
    }

    if (m_InputBuffer->Available() < (int)IPACKS*10) {	
	cCondWait::SleepMs(5);
	continue;
    }

    if (!cTSWorker::HaveStreamClient()) {
	ClearOutput();
	cCondWait::SleepMs(10);
	continue;
    }
    

    InputMutex.Lock();
    uchar *data = m_InputBuffer->Get(count);
    if (data==NULL) {
       InputMutex.Unlock();
       cCondWait::SleepMs(3);
       continue;
    }

//    fprintf(stderr, "[ffnetdev] count: %07d Free: %07d AvailO %07d AvailI: %07d\n", count, m_InputBuffer->Free(),
//	    m_OutputBuffer->Available(), m_InputBuffer->Available());

    if ( count < (int)minNeededPacketlen )	{
       fprintf(stderr, "[ffnetdev] Remuxer: not enought bytes for PacketLen-Analysis, have only: %d\n", count);
       InputMutex.Unlock();
       cCondWait::SleepMs(2);
      continue;
    }

    //DEBUG
    //fprintf(stderr, "Data ready to read: %d\n", count);
    //for (i=0; i<20; i++)
    //  fprintf(stderr, "%02X ", data[i]);
    // END DEBUG  
      

       // check for valid PES signature in PES header
    if ( (data[0]==0x00) && (data[1]==0x00) && (data[2]==0x01) ) {
          
          packetlen = ((data[4]<<8) | data[5]) + 6 ;
    
          if ( packetlen>IPACKS) {
              fprintf(stderr, "[ffnetdev] Remuxer: IPACKS changed? packet length was %d, maximum: %d\n"
                      "This should not happen! Please report!\n", packetlen, IPACKS);
          }

	  if ( count < (int)packetlen) {
	       fprintf(stderr, "[ffnetdev] Remuxer: not enought bytes for whole packet, have only: %d but LenShoud be %d\n", count, packetlen);
	       InputMutex.Unlock();
    	       cCondWait::SleepMs(1);
	       continue;
	  }


          // check for valid stream id type: is it video or audio or unknown?
          if ( (data[3]>=0xC0) && (data[3]<=0xDF) || data[3] == 0xBD ) {
              pid=apid;
              cc=&acc;
          } 
          else {
              if ( (data[3]>=0xE0) && (data[3]<=0xEF) ) {
                  pid=vpid;
                  cc=&vcc;
              } 
              else {
                  fprintf(stderr, "[ffnetdev] Remuxer: Unknown stream id: neither video nor audio type.\n");
                  // throw away whole PES packet
                  m_InputBuffer->Del(packetlen);
                  InputMutex.Unlock();
                  continue;
              }
          }
          
          memcpy( pes, data, packetlen);
          // we are now finished with the PES packet, delete it from ring buffer
          m_InputBuffer->Del(packetlen);    
	  
          InputMutex.Unlock();
    }
    else {
      // no valid PES signature was found, so delete this stuff from ring buffer
      // normally we should always receive a whole PES packet, since VDR always gives us a whole packet and not less or more
      // with each call in streamdevice.c (PlayVideo, PlayAudio)
      fprintf(stderr, "[ffnetdev] Remuxer: No valid PES signature found. This should not happen.\n");

      m_InputBuffer->Del(1); // Probably it is better to delete 1 byte only to get in sync again!?
      InputMutex.Unlock();
      continue;
    } 
    
    int tspacketlen = ((int)packetlen/184) * 188  + ((packetlen % 184 > 0) ? 188 : 0); 
    while (m_OutputBuffer->Free() < tspacketlen) {	
	if (!m_Active)
    	    continue;
	cCondWait::SleepMs(10);
	//fprintf(stderr, "[ffnetdev] Remuxer: sleep %d %d\n", m_OutputBuffer->Free(), tspacketlen);
    }

    LockOutput();
    bool first = true;    
  //--------------------------------------divide PES packet into small TS packets-----------------------    
    for (i=0; i< packetlen/184; i++) {
      ts[0] = 0x47; //SYNC Byte
      if (first) ts[1] = 0x40;        // Set PUSI or
      else       ts[1] = 0x00;        // clear PUSI,  TODO: PID (high) is missing
      ts[2] = pid & 0xFF;             // PID (low)  
      ts[3] = 0x10 | ((*cc)&0x0F);    // No adaptation field, payload only, continuity counter 
      memcpy(ts + 4, pes + i * 184, 184);
      ++(*cc);
      m_OutputBuffer->Put(ts, 188);
      first = false;
    }
    uchar rest = packetlen % 184;
    if (rest>0) {
      ts[0] = 0x47; //SYNC Byte
      if (first) ts[1] = 0x40;        // Set PUSI or
      else       ts[1] = 0x00;        // clear PUSI,  TODO: PID (high) is missing
      ts[2] = pid & 0xFF;             // PID (low)  
      ts[3] = 0x30 | ((*cc)&0x0F);    // adaptation field, payload, continuity counter 
      ++(*cc);
      ts[4] = 183-rest;
      if (ts[4]>0) {
        ts[5] = 0x00;
        memset(ts + 6, 0xFF, ts[4] - 1);
      }
      memcpy(ts + 5 + ts[4], pes + i * 184, rest);
      m_OutputBuffer->Put(ts, 188);
      first = false;
    }
    
    UnlockOutput();
	
  }
  m_Active = false;
}


int cPES2TSRemux::Put(const uchar *Data, int Count)
{
  InputMutex.Lock();
  int result = m_InputBuffer->Put(Data, Count);
  InputMutex.Unlock();
  return ( result);
}