summaryrefslogtreecommitdiff
path: root/remux.c
blob: e48296d8405ddf0747c7503751509483f502490c (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
/*
 * remux.c: A streaming MPEG2 remultiplexer
 *
 * See the main source file 'vdr.c' for copyright information and
 * how to reach the author.
 *
 * $Id: remux.c 1.1 2001/03/31 08:42:17 kls Exp $
 */

/* The calling interface of the 'cRemux::Process()' function is defined
   as follows:

   'Data' points to a chunk of data that consists of 'Count' bytes.
   The 'Process' function shall try to remultiplex as much of the
   data as possible and return a pointer to the resulting buffer.
   That buffer typically is different from the incoming 'Data',
   but in the simplest case (when 'Process' does nothing) might
   as well point to the original 'Data'. When returning, 'Count'
   shall be set to the number of bytes that have been processed
   (i.e. have been taken from 'Data'), while 'Result' indicates
   how many bytes the returned buffer contains. 'PictureType' shall
   be set to NO_PICTURE if the returned data does not start a new
   picture, or one of I_FRAME, P_FRAME or B_FRAME if a new picture
   starting point has been found. This also means that the returned
   data buffer may contain at most one entire video frame, because
   the next frame must be returned with its own value for 'PictureType'.

   'Process' shall do it's best to keep the latency time as short
   as possible in order to allow a quick start of VDR's "Transfer
   mode" (displaying the signal of one DVB card on another card).
   In order to do that, this function may decide to first pass
   through the incoming data (almost) unprocessed, and make
   actual processing kick in after a few seconds (if that is at
   all possible for the algorithm). This may result in a non-
   optimal stream at the beginning, which won't matter for normal
   recordings but may make switching through encrypted channels
   in "Transfer mode" faster.

   In the resulting data stream, a new packet shall always be started
   when a frame border is encountered. VDR needs this in order to
   be able to detect and store the frame indexes, and to easily
   display single frames in fast forward/back mode. The very first
   data block returned shall be the starting point of an I_FRAME.
   Everything before that shall be silently dropped.

   If the incoming data is not enough to do remultiplexing, a value
   of NULL shall be returned ('Result' has no meaning then). This
   will tell the caller to wait for more data to be presented in
   the next call. If NULL is returned and 'Count' is not 0, the
   caller shall remove 'Count' bytes from the beginning of 'Data'
   before the next call. This is the way 'Process' indicates that
   it must skip that data.

   Any data that is not used during this call will appear at the
   beginning of the incoming 'Data' buffer at the next call, plus
   any new data that has become available.

   It is guaranteed that the caller will completely process any
   returned data before the next call to 'Process'. That way, 'Process'
   can dynamically allocate its return buffer and be sure the caller
   doesn't keep any pointers into that buffer.
*/

#include "remux.h"
#include "tools.h"

#if defined(REMUX_NONE)

cRemux::cRemux(void)
{
  synced = false;
}

cRemux::~cRemux()
{
}

int cRemux::GetPacketLength(const uchar *Data, int Count, int Offset)
{
  // Returns the entire length of the packet starting at offset, or -1 in case of error.
  return (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1;
}

int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType)
{
  // Scans the video packet starting at Offset and returns its length.
  // If the return value is -1 the packet was not completely in the buffer.

  int Length = GetPacketLength(Data, Count, Offset);
  if (Length > 0 && Offset + Length <= Count) {
     int i = Offset + 8; // the minimum length of the video packet header
     i += Data[i] + 1;   // possible additional header bytes
     for (; i < Offset + Length; i++) {
         if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
            switch (Data[i + 3]) {
              case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
                               return Length;
              }
            }
         }
     PictureType = NO_PICTURE;
     return Length;
     }
  return -1;
}

const uchar *cRemux::Process(const uchar *Data, int &Count, int &Result, uchar &PictureType)
{
  int Skip = 0;

  PictureType = NO_PICTURE;

  if (Count >= MINVIDEODATA) {
     for (int i = 0; i < Count; i++) {
         if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
            switch (Data[i + 3]) {
              case SC_VIDEO:
                   {
                     uchar pt = NO_PICTURE;
                     int l = ScanVideoPacket(Data, Count, i, pt);
                     if (l < 0) {
                        if (Skip < Count)
                           Count = Skip;
                        return NULL; // no useful data found, wait for more
                        }
                     if (pt != NO_PICTURE) {
                        if (pt < I_FRAME || B_FRAME < pt) {
                           esyslog(LOG_ERR, "ERROR: unknown picture type '%d'", pt);
                           }
                        else if (PictureType == NO_PICTURE) {
                           if (!synced) {
                              if (pt == I_FRAME) {
                                 Skip = i;
                                 synced = true;
                                 }
                              else {
                                 i += l;
                                 Skip = i;
                                 break;
                                 }
                              }
                           if (synced)
                              PictureType = pt;
                           }
                        else {
                           Count = i;
                           Result = i - Skip;
                           return Data + Skip;
                           }
                        }
                     else if (!synced) {
                        i += l;
                        Skip = i;
                        break;
                        }
                     i += l - 1; // -1 to compensate for i++ in the loop!
                   }
                   break;
              case SC_AUDIO:
                   i += GetPacketLength(Data, Count, i) - 1; // -1 to compensate for i++ in the loop!
                   break;
              }
            }
         }
     }
  if (Skip < Count)
     Count = Skip;
  return NULL; // no useful data found, wait for more
}

#elif defined(REMUX_TEST)
#endif