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