1 | /********** |
2 | This library is free software; you can redistribute it and/or modify it under |
3 | the terms of the GNU Lesser General Public License as published by the |
4 | Free Software Foundation; either version 3 of the License, or (at your |
5 | option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
6 | |
7 | This library is distributed in the hope that it will be useful, but WITHOUT |
8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
9 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
10 | more details. |
11 | |
12 | You should have received a copy of the GNU Lesser General Public License |
13 | along with this library; if not, write to the Free Software Foundation, Inc., |
14 | 51 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 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 | |
30 | class InputESSourceRecord { |
31 | public: |
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 | |
51 | private: |
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 | |
60 | private: |
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 | |
76 | unsigned MPEG2TransportStreamFromESSource::maxInputESFrameSize = 100000; // bytes |
77 | |
78 | MPEG2TransportStreamFromESSource* MPEG2TransportStreamFromESSource |
79 | ::createNew(UsageEnvironment& env) { |
80 | return new MPEG2TransportStreamFromESSource(env); |
81 | } |
82 | |
83 | void 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 | |
90 | void 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 | |
96 | MPEG2TransportStreamFromESSource |
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 | |
104 | MPEG2TransportStreamFromESSource::~MPEG2TransportStreamFromESSource() { |
105 | doStopGettingFrames(); |
106 | delete fInputSources; |
107 | } |
108 | |
109 | void 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 | |
117 | void 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 | |
148 | void 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 | |
159 | InputESSourceRecord |
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 | |
170 | InputESSourceRecord::~InputESSourceRecord() { |
171 | Medium::close(fInputSource); |
172 | delete[] fInputBuffer; |
173 | delete fNext; |
174 | } |
175 | |
176 | void 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 | |
200 | Boolean 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 | |
228 | void 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 | } |
236 | void 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 | |