1/**********
2This library is free software; you can redistribute it and/or modify it under
3the terms of the GNU Lesser General Public License as published by the
4Free Software Foundation; either version 3 of the License, or (at your
5option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7This library is distributed in the hope that it will be useful, but WITHOUT
8ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10more details.
11
12You should have received a copy of the GNU Lesser General Public License
13along with this library; if not, write to the Free Software Foundation, Inc.,
1451 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15**********/
16// "liveMedia"
17// Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved.
18// A filter for converting one or more MPEG Elementary Streams
19// to a MPEG-2 Transport Stream
20// Implementation
21
22#include "MPEG2TransportStreamFromESSource.hh"
23
24#define SIMPLE_PES_HEADER_SIZE 14
25#define INPUT_BUFFER_SIZE (SIMPLE_PES_HEADER_SIZE + 2*MPEG2TransportStreamFromESSource::maxInputESFrameSize)
26#define LOW_WATER_MARK 1000 // <= MPEG2TransportStreamFromESSource::maxInputESFrameSize
27
28////////// InputESSourceRecord definition //////////
29
30class InputESSourceRecord {
31public:
32 InputESSourceRecord(MPEG2TransportStreamFromESSource& parent,
33 FramedSource* inputSource,
34 u_int8_t streamId, int mpegVersion,
35 InputESSourceRecord* next, int16_t PID = -1);
36 virtual ~InputESSourceRecord();
37
38 InputESSourceRecord* next() const { return fNext; }
39 FramedSource* inputSource() const { return fInputSource; }
40
41 void askForNewData();
42 Boolean deliverBufferToClient();
43
44 unsigned char* buffer() const { return fInputBuffer; }
45 void reset() {
46 // Reset the buffer for future use:
47 fInputBufferBytesAvailable = 0;
48 fInputBufferInUse = False;
49 }
50
51private:
52 static void afterGettingFrame(void* clientData, unsigned frameSize,
53 unsigned numTruncatedBytes,
54 struct timeval presentationTime,
55 unsigned durationInMicroseconds);
56 void afterGettingFrame1(unsigned frameSize,
57 unsigned numTruncatedBytes,
58 struct timeval presentationTime);
59
60private:
61 InputESSourceRecord* fNext;
62 MPEG2TransportStreamFromESSource& fParent;
63 FramedSource* fInputSource;
64 u_int8_t fStreamId;
65 int fMPEGVersion;
66 unsigned char* fInputBuffer;
67 unsigned fInputBufferBytesAvailable;
68 Boolean fInputBufferInUse;
69 MPEG1or2Demux::SCR fSCR;
70 int16_t fPID;
71};
72
73
74////////// MPEG2TransportStreamFromESSource implementation //////////
75
76unsigned MPEG2TransportStreamFromESSource::maxInputESFrameSize = 100000; // bytes
77
78MPEG2TransportStreamFromESSource* MPEG2TransportStreamFromESSource
79::createNew(UsageEnvironment& env) {
80 return new MPEG2TransportStreamFromESSource(env);
81}
82
83void MPEG2TransportStreamFromESSource
84::addNewVideoSource(FramedSource* inputSource, int mpegVersion, int16_t PID) {
85 u_int8_t streamId = 0xE0 | (fVideoSourceCounter++&0x0F);
86 addNewInputSource(inputSource, streamId, mpegVersion, PID);
87 fHaveVideoStreams = True;
88}
89
90void MPEG2TransportStreamFromESSource
91::addNewAudioSource(FramedSource* inputSource, int mpegVersion, int16_t PID) {
92 u_int8_t streamId = 0xC0 | (fAudioSourceCounter++&0x0F);
93 addNewInputSource(inputSource, streamId, mpegVersion, PID);
94}
95
96MPEG2TransportStreamFromESSource
97::MPEG2TransportStreamFromESSource(UsageEnvironment& env)
98 : MPEG2TransportStreamMultiplexor(env),
99 fInputSources(NULL), fVideoSourceCounter(0), fAudioSourceCounter(0),
100 fAwaitingBackgroundDelivery(False) {
101 fHaveVideoStreams = False; // unless we add a video source
102}
103
104MPEG2TransportStreamFromESSource::~MPEG2TransportStreamFromESSource() {
105 doStopGettingFrames();
106 delete fInputSources;
107}
108
109void MPEG2TransportStreamFromESSource::doStopGettingFrames() {
110 // Stop each input source:
111 for (InputESSourceRecord* sourceRec = fInputSources; sourceRec != NULL;
112 sourceRec = sourceRec->next()) {
113 sourceRec->inputSource()->stopGettingFrames();
114 }
115}
116
117void MPEG2TransportStreamFromESSource
118::awaitNewBuffer(unsigned char* oldBuffer) {
119 InputESSourceRecord* sourceRec;
120 // Begin by resetting the old buffer:
121 if (oldBuffer != NULL) {
122 for (sourceRec = fInputSources; sourceRec != NULL;
123 sourceRec = sourceRec->next()) {
124 if (sourceRec->buffer() == oldBuffer) {
125 sourceRec->reset();
126 break;
127 }
128 }
129 fAwaitingBackgroundDelivery = False;
130 }
131
132 if (isCurrentlyAwaitingData()) {
133 // Try to deliver one filled-in buffer to the client:
134 for (sourceRec = fInputSources; sourceRec != NULL;
135 sourceRec = sourceRec->next()) {
136 if (sourceRec->deliverBufferToClient()) return;
137 }
138 fAwaitingBackgroundDelivery = True;
139 }
140
141 // No filled-in buffers are available. Ask each of our inputs for data:
142 for (sourceRec = fInputSources; sourceRec != NULL;
143 sourceRec = sourceRec->next()) {
144 sourceRec->askForNewData();
145 }
146}
147
148void MPEG2TransportStreamFromESSource
149::addNewInputSource(FramedSource* inputSource,
150 u_int8_t streamId, int mpegVersion, int16_t PID) {
151 if (inputSource == NULL) return;
152 fInputSources = new InputESSourceRecord(*this, inputSource, streamId,
153 mpegVersion, fInputSources, PID);
154}
155
156
157////////// InputESSourceRecord implementation //////////
158
159InputESSourceRecord
160::InputESSourceRecord(MPEG2TransportStreamFromESSource& parent,
161 FramedSource* inputSource,
162 u_int8_t streamId, int mpegVersion,
163 InputESSourceRecord* next, int16_t PID)
164 : fNext(next), fParent(parent), fInputSource(inputSource),
165 fStreamId(streamId), fMPEGVersion(mpegVersion), fPID(PID) {
166 fInputBuffer = new unsigned char[INPUT_BUFFER_SIZE];
167 reset();
168}
169
170InputESSourceRecord::~InputESSourceRecord() {
171 Medium::close(fInputSource);
172 delete[] fInputBuffer;
173 delete fNext;
174}
175
176void InputESSourceRecord::askForNewData() {
177 if (fInputBufferInUse) return;
178
179 if (fInputBufferBytesAvailable == 0) {
180 // Reset our buffer, by adding a simple PES header at the start:
181 fInputBuffer[0] = 0; fInputBuffer[1] = 0; fInputBuffer[2] = 1;
182 fInputBuffer[3] = fStreamId;
183 fInputBuffer[4] = 0; fInputBuffer[5] = 0; // fill in later with the length
184 fInputBuffer[6] = 0x80;
185 fInputBuffer[7] = 0x80; // include a PTS
186 fInputBuffer[8] = 5; // PES_header_data_length (enough for a PTS)
187 // fInputBuffer[9..13] will be the PTS; fill this in later
188 fInputBufferBytesAvailable = SIMPLE_PES_HEADER_SIZE;
189 }
190 if (fInputBufferBytesAvailable < LOW_WATER_MARK &&
191 !fInputSource->isCurrentlyAwaitingData()) {
192 // We don't yet have enough data in our buffer. Arrange to read more:
193 fInputSource->getNextFrame(&fInputBuffer[fInputBufferBytesAvailable],
194 INPUT_BUFFER_SIZE-fInputBufferBytesAvailable,
195 afterGettingFrame, this,
196 FramedSource::handleClosure, &fParent);
197 }
198}
199
200Boolean InputESSourceRecord::deliverBufferToClient() {
201 if (fInputBufferInUse || fInputBufferBytesAvailable < LOW_WATER_MARK) return False;
202
203 // Fill in the PES_packet_length field that we left unset before:
204 unsigned PES_packet_length = fInputBufferBytesAvailable - 6;
205 if (PES_packet_length > 0xFFFF) {
206 // Set the PES_packet_length field to 0. This indicates an unbounded length (see ISO 13818-1, 2.4.3.7)
207 PES_packet_length = 0;
208 }
209 fInputBuffer[4] = PES_packet_length>>8;
210 fInputBuffer[5] = PES_packet_length;
211
212 // Fill in the PES PTS (from our SCR):
213 fInputBuffer[9] = 0x20|(fSCR.highBit<<3)|(fSCR.remainingBits>>29)|0x01;
214 fInputBuffer[10] = fSCR.remainingBits>>22;
215 fInputBuffer[11] = (fSCR.remainingBits>>14)|0x01;
216 fInputBuffer[12] = fSCR.remainingBits>>7;
217 fInputBuffer[13] = (fSCR.remainingBits<<1)|0x01;
218
219 fInputBufferInUse = True;
220
221 // Do the delivery:
222 fParent.handleNewBuffer(fInputBuffer, fInputBufferBytesAvailable,
223 fMPEGVersion, fSCR, fPID);
224
225 return True;
226}
227
228void InputESSourceRecord
229::afterGettingFrame(void* clientData, unsigned frameSize,
230 unsigned numTruncatedBytes,
231 struct timeval presentationTime,
232 unsigned /*durationInMicroseconds*/) {
233 InputESSourceRecord* source = (InputESSourceRecord*)clientData;
234 source->afterGettingFrame1(frameSize, numTruncatedBytes, presentationTime);
235}
236void InputESSourceRecord
237::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
238 struct timeval presentationTime) {
239 if (numTruncatedBytes > 0) {
240 fParent.envir() << "MPEG2TransportStreamFromESSource: input buffer too small; increase \"MPEG2TransportStreamFromESSource::maxInputESFrameSize\" by at least "
241 << numTruncatedBytes << " bytes!\n";
242 }
243
244 if (fInputBufferBytesAvailable == SIMPLE_PES_HEADER_SIZE) {
245 // Use this presentationTime for our SCR:
246 fSCR.highBit
247 = ((presentationTime.tv_sec*45000 + (presentationTime.tv_usec*9)/200)&
248 0x80000000) != 0;
249 fSCR.remainingBits
250 = presentationTime.tv_sec*90000 + (presentationTime.tv_usec*9)/100;
251 fSCR.extension = (presentationTime.tv_usec*9)%100;
252#ifdef DEBUG_SCR
253 fprintf(stderr, "PES header: stream_id 0x%02x, pts: %u.%06u => SCR 0x%x%08x:%03x\n", fStreamId, (unsigned)presentationTime.tv_sec, (unsigned)presentationTime.tv_usec, fSCR.highBit, fSCR.remainingBits, fSCR.extension);
254#endif
255 }
256
257 fInputBufferBytesAvailable += frameSize;
258
259 fParent.fPresentationTime = presentationTime;
260
261 // Now that we have new input data, check if we can deliver to the client:
262 if (fParent.fAwaitingBackgroundDelivery) {
263 fParent.fAwaitingBackgroundDelivery = False;
264 fParent.awaitNewBuffer(NULL);
265 }
266}
267