| 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 | // Media Sinks |
| 19 | // Implementation |
| 20 | |
| 21 | #include "MediaSink.hh" |
| 22 | #include "GroupsockHelper.hh" |
| 23 | #include <string.h> |
| 24 | |
| 25 | ////////// MediaSink ////////// |
| 26 | |
| 27 | MediaSink::MediaSink(UsageEnvironment& env) |
| 28 | : Medium(env), fSource(NULL) { |
| 29 | } |
| 30 | |
| 31 | MediaSink::~MediaSink() { |
| 32 | stopPlaying(); |
| 33 | } |
| 34 | |
| 35 | Boolean MediaSink::isSink() const { |
| 36 | return True; |
| 37 | } |
| 38 | |
| 39 | Boolean MediaSink::lookupByName(UsageEnvironment& env, char const* sinkName, |
| 40 | MediaSink*& resultSink) { |
| 41 | resultSink = NULL; // unless we succeed |
| 42 | |
| 43 | Medium* medium; |
| 44 | if (!Medium::lookupByName(env, sinkName, medium)) return False; |
| 45 | |
| 46 | if (!medium->isSink()) { |
| 47 | env.setResultMsg(sinkName, " is not a media sink" ); |
| 48 | return False; |
| 49 | } |
| 50 | |
| 51 | resultSink = (MediaSink*)medium; |
| 52 | return True; |
| 53 | } |
| 54 | |
| 55 | Boolean MediaSink::sourceIsCompatibleWithUs(MediaSource& source) { |
| 56 | // We currently support only framed sources. |
| 57 | return source.isFramedSource(); |
| 58 | } |
| 59 | |
| 60 | Boolean MediaSink::startPlaying(MediaSource& source, |
| 61 | afterPlayingFunc* afterFunc, |
| 62 | void* afterClientData) { |
| 63 | // Make sure we're not already being played: |
| 64 | if (fSource != NULL) { |
| 65 | envir().setResultMsg("This sink is already being played" ); |
| 66 | return False; |
| 67 | } |
| 68 | |
| 69 | // Make sure our source is compatible: |
| 70 | if (!sourceIsCompatibleWithUs(source)) { |
| 71 | envir().setResultMsg("MediaSink::startPlaying(): source is not compatible!" ); |
| 72 | return False; |
| 73 | } |
| 74 | fSource = (FramedSource*)&source; |
| 75 | |
| 76 | fAfterFunc = afterFunc; |
| 77 | fAfterClientData = afterClientData; |
| 78 | return continuePlaying(); |
| 79 | } |
| 80 | |
| 81 | void MediaSink::stopPlaying() { |
| 82 | // First, tell the source that we're no longer interested: |
| 83 | if (fSource != NULL) fSource->stopGettingFrames(); |
| 84 | |
| 85 | // Cancel any pending tasks: |
| 86 | envir().taskScheduler().unscheduleDelayedTask(nextTask()); |
| 87 | |
| 88 | fSource = NULL; // indicates that we can be played again |
| 89 | fAfterFunc = NULL; |
| 90 | } |
| 91 | |
| 92 | void MediaSink::onSourceClosure(void* clientData) { |
| 93 | MediaSink* sink = (MediaSink*)clientData; |
| 94 | sink->onSourceClosure(); |
| 95 | } |
| 96 | |
| 97 | void MediaSink::onSourceClosure() { |
| 98 | // Cancel any pending tasks: |
| 99 | envir().taskScheduler().unscheduleDelayedTask(nextTask()); |
| 100 | |
| 101 | fSource = NULL; // indicates that we can be played again |
| 102 | if (fAfterFunc != NULL) { |
| 103 | (*fAfterFunc)(fAfterClientData); |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | Boolean MediaSink::isRTPSink() const { |
| 108 | return False; // default implementation |
| 109 | } |
| 110 | |
| 111 | ////////// OutPacketBuffer ////////// |
| 112 | |
| 113 | unsigned OutPacketBuffer::maxSize = 60000; // by default |
| 114 | |
| 115 | OutPacketBuffer |
| 116 | ::OutPacketBuffer(unsigned preferredPacketSize, unsigned maxPacketSize, unsigned maxBufferSize) |
| 117 | : fPreferred(preferredPacketSize), fMax(maxPacketSize), |
| 118 | fOverflowDataSize(0) { |
| 119 | if (maxBufferSize == 0) maxBufferSize = maxSize; |
| 120 | unsigned maxNumPackets = (maxBufferSize + (maxPacketSize-1))/maxPacketSize; |
| 121 | fLimit = maxNumPackets*maxPacketSize; |
| 122 | fBuf = new unsigned char[fLimit]; |
| 123 | resetPacketStart(); |
| 124 | resetOffset(); |
| 125 | resetOverflowData(); |
| 126 | } |
| 127 | |
| 128 | OutPacketBuffer::~OutPacketBuffer() { |
| 129 | delete[] fBuf; |
| 130 | } |
| 131 | |
| 132 | void OutPacketBuffer::enqueue(unsigned char const* from, unsigned numBytes) { |
| 133 | if (numBytes > totalBytesAvailable()) { |
| 134 | #ifdef DEBUG |
| 135 | fprintf(stderr, "OutPacketBuffer::enqueue() warning: %d > %d\n" , numBytes, totalBytesAvailable()); |
| 136 | #endif |
| 137 | numBytes = totalBytesAvailable(); |
| 138 | } |
| 139 | |
| 140 | if (curPtr() != from) memmove(curPtr(), from, numBytes); |
| 141 | increment(numBytes); |
| 142 | } |
| 143 | |
| 144 | void OutPacketBuffer::enqueueWord(u_int32_t word) { |
| 145 | u_int32_t nWord = htonl(word); |
| 146 | enqueue((unsigned char*)&nWord, 4); |
| 147 | } |
| 148 | |
| 149 | void OutPacketBuffer::insert(unsigned char const* from, unsigned numBytes, |
| 150 | unsigned toPosition) { |
| 151 | unsigned realToPosition = fPacketStart + toPosition; |
| 152 | if (realToPosition + numBytes > fLimit) { |
| 153 | if (realToPosition > fLimit) return; // we can't do this |
| 154 | numBytes = fLimit - realToPosition; |
| 155 | } |
| 156 | |
| 157 | memmove(&fBuf[realToPosition], from, numBytes); |
| 158 | if (toPosition + numBytes > fCurOffset) { |
| 159 | fCurOffset = toPosition + numBytes; |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | void OutPacketBuffer::insertWord(u_int32_t word, unsigned toPosition) { |
| 164 | u_int32_t nWord = htonl(word); |
| 165 | insert((unsigned char*)&nWord, 4, toPosition); |
| 166 | } |
| 167 | |
| 168 | void OutPacketBuffer::(unsigned char* to, unsigned numBytes, |
| 169 | unsigned fromPosition) { |
| 170 | unsigned realFromPosition = fPacketStart + fromPosition; |
| 171 | if (realFromPosition + numBytes > fLimit) { // sanity check |
| 172 | if (realFromPosition > fLimit) return; // we can't do this |
| 173 | numBytes = fLimit - realFromPosition; |
| 174 | } |
| 175 | |
| 176 | memmove(to, &fBuf[realFromPosition], numBytes); |
| 177 | } |
| 178 | |
| 179 | u_int32_t OutPacketBuffer::(unsigned fromPosition) { |
| 180 | u_int32_t nWord; |
| 181 | extract((unsigned char*)&nWord, 4, fromPosition); |
| 182 | return ntohl(nWord); |
| 183 | } |
| 184 | |
| 185 | void OutPacketBuffer::skipBytes(unsigned numBytes) { |
| 186 | if (numBytes > totalBytesAvailable()) { |
| 187 | numBytes = totalBytesAvailable(); |
| 188 | } |
| 189 | |
| 190 | increment(numBytes); |
| 191 | } |
| 192 | |
| 193 | void OutPacketBuffer |
| 194 | ::setOverflowData(unsigned overflowDataOffset, |
| 195 | unsigned overflowDataSize, |
| 196 | struct timeval const& presentationTime, |
| 197 | unsigned durationInMicroseconds) { |
| 198 | fOverflowDataOffset = overflowDataOffset; |
| 199 | fOverflowDataSize = overflowDataSize; |
| 200 | fOverflowPresentationTime = presentationTime; |
| 201 | fOverflowDurationInMicroseconds = durationInMicroseconds; |
| 202 | } |
| 203 | |
| 204 | void OutPacketBuffer::useOverflowData() { |
| 205 | enqueue(&fBuf[fPacketStart + fOverflowDataOffset], fOverflowDataSize); |
| 206 | fCurOffset -= fOverflowDataSize; // undoes increment performed by "enqueue" |
| 207 | resetOverflowData(); |
| 208 | } |
| 209 | |
| 210 | void OutPacketBuffer::adjustPacketStart(unsigned numBytes) { |
| 211 | fPacketStart += numBytes; |
| 212 | if (fOverflowDataOffset >= numBytes) { |
| 213 | fOverflowDataOffset -= numBytes; |
| 214 | } else { |
| 215 | fOverflowDataOffset = 0; |
| 216 | fOverflowDataSize = 0; // an error otherwise |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | void OutPacketBuffer::resetPacketStart() { |
| 221 | if (fOverflowDataSize > 0) { |
| 222 | fOverflowDataOffset += fPacketStart; |
| 223 | } |
| 224 | fPacketStart = 0; |
| 225 | } |
| 226 | |