| 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 | |