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// Media Sinks
19// Implementation
20
21#include "MediaSink.hh"
22#include "GroupsockHelper.hh"
23#include <string.h>
24
25////////// MediaSink //////////
26
27MediaSink::MediaSink(UsageEnvironment& env)
28 : Medium(env), fSource(NULL) {
29}
30
31MediaSink::~MediaSink() {
32 stopPlaying();
33}
34
35Boolean MediaSink::isSink() const {
36 return True;
37}
38
39Boolean 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
55Boolean MediaSink::sourceIsCompatibleWithUs(MediaSource& source) {
56 // We currently support only framed sources.
57 return source.isFramedSource();
58}
59
60Boolean 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
81void 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
92void MediaSink::onSourceClosure(void* clientData) {
93 MediaSink* sink = (MediaSink*)clientData;
94 sink->onSourceClosure();
95}
96
97void 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
107Boolean MediaSink::isRTPSink() const {
108 return False; // default implementation
109}
110
111////////// OutPacketBuffer //////////
112
113unsigned OutPacketBuffer::maxSize = 60000; // by default
114
115OutPacketBuffer
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
128OutPacketBuffer::~OutPacketBuffer() {
129 delete[] fBuf;
130}
131
132void 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
144void OutPacketBuffer::enqueueWord(u_int32_t word) {
145 u_int32_t nWord = htonl(word);
146 enqueue((unsigned char*)&nWord, 4);
147}
148
149void 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
163void OutPacketBuffer::insertWord(u_int32_t word, unsigned toPosition) {
164 u_int32_t nWord = htonl(word);
165 insert((unsigned char*)&nWord, 4, toPosition);
166}
167
168void OutPacketBuffer::extract(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
179u_int32_t OutPacketBuffer::extractWord(unsigned fromPosition) {
180 u_int32_t nWord;
181 extract((unsigned char*)&nWord, 4, fromPosition);
182 return ntohl(nWord);
183}
184
185void OutPacketBuffer::skipBytes(unsigned numBytes) {
186 if (numBytes > totalBytesAvailable()) {
187 numBytes = totalBytesAvailable();
188 }
189
190 increment(numBytes);
191}
192
193void 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
204void OutPacketBuffer::useOverflowData() {
205 enqueue(&fBuf[fPacketStart + fOverflowDataOffset], fOverflowDataSize);
206 fCurOffset -= fOverflowDataSize; // undoes increment performed by "enqueue"
207 resetOverflowData();
208}
209
210void 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
220void OutPacketBuffer::resetPacketStart() {
221 if (fOverflowDataSize > 0) {
222 fOverflowDataOffset += fPacketStart;
223 }
224 fPacketStart = 0;
225}
226