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// A class that encapsulates an Ogg file.
19// Implementation
20
21#include "OggFileParser.hh"
22#include "OggDemuxedTrack.hh"
23#include "ByteStreamFileSource.hh"
24#include "VorbisAudioRTPSink.hh"
25#include "SimpleRTPSink.hh"
26#include "TheoraVideoRTPSink.hh"
27
28////////// OggTrackTable definition /////////
29
30// For looking up and iterating over the file's tracks:
31
32class OggTrackTable {
33public:
34 OggTrackTable();
35 virtual ~OggTrackTable();
36
37 void add(OggTrack* newTrack);
38 OggTrack* lookup(u_int32_t trackNumber);
39
40 unsigned numTracks() const;
41
42private:
43 friend class OggTrackTableIterator;
44 HashTable* fTable;
45};
46
47
48////////// OggFile implementation //////////
49
50void OggFile::createNew(UsageEnvironment& env, char const* fileName,
51 onCreationFunc* onCreation, void* onCreationClientData) {
52 new OggFile(env, fileName, onCreation, onCreationClientData);
53}
54
55OggTrack* OggFile::lookup(u_int32_t trackNumber) {
56 return fTrackTable->lookup(trackNumber);
57}
58
59OggDemux* OggFile::newDemux() {
60 OggDemux* demux = new OggDemux(*this);
61 fDemuxesTable->Add((char const*)demux, demux);
62
63 return demux;
64}
65
66unsigned OggFile::numTracks() const {
67 return fTrackTable->numTracks();
68}
69
70FramedSource* OggFile
71::createSourceForStreaming(FramedSource* baseSource, u_int32_t trackNumber,
72 unsigned& estBitrate, unsigned& numFiltersInFrontOfTrack) {
73 if (baseSource == NULL) return NULL;
74
75 FramedSource* result = baseSource; // by default
76 numFiltersInFrontOfTrack = 0; // by default
77
78 // Look at the track's MIME type to set its estimated bitrate (for use by RTCP).
79 // (Later, try to be smarter about figuring out the bitrate.) #####
80 // Some MIME types also require adding a special 'framer' in front of the source.
81 OggTrack* track = lookup(trackNumber);
82 if (track != NULL) { // should always be true
83 estBitrate = track->estBitrate;
84 }
85
86 return result;
87}
88
89RTPSink* OggFile
90::createRTPSinkForTrackNumber(u_int32_t trackNumber, Groupsock* rtpGroupsock,
91 unsigned char rtpPayloadTypeIfDynamic) {
92 OggTrack* track = lookup(trackNumber);
93 if (track == NULL || track->mimeType == NULL) return NULL;
94
95 RTPSink* result = NULL; // default value for unknown media types
96
97 if (strcmp(track->mimeType, "audio/VORBIS") == 0) {
98 // For Vorbis audio, we use the special "identification", "comment", and "setup" headers
99 // that we read when we initially read the headers at the start of the file:
100 result = VorbisAudioRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
101 track->samplingFrequency, track->numChannels,
102 track->vtoHdrs.header[0], track->vtoHdrs.headerSize[0],
103 track->vtoHdrs.header[1], track->vtoHdrs.headerSize[1],
104 track->vtoHdrs.header[2], track->vtoHdrs.headerSize[2]);
105 } else if (strcmp(track->mimeType, "audio/OPUS") == 0) {
106 result = SimpleRTPSink
107 ::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
108 48000, "audio", "OPUS", 2, False/*only 1 Opus 'packet' in each RTP packet*/);
109 } else if (strcmp(track->mimeType, "video/THEORA") == 0) {
110 // For Theora video, we use the special "identification", "comment", and "setup" headers
111 // that we read when we initially read the headers at the start of the file:
112 result = TheoraVideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
113 track->vtoHdrs.header[0], track->vtoHdrs.headerSize[0],
114 track->vtoHdrs.header[1], track->vtoHdrs.headerSize[1],
115 track->vtoHdrs.header[2], track->vtoHdrs.headerSize[2]);
116 }
117
118 return result;
119}
120
121
122OggFile::OggFile(UsageEnvironment& env, char const* fileName,
123 onCreationFunc* onCreation, void* onCreationClientData)
124 : Medium(env),
125 fFileName(strDup(fileName)),
126 fOnCreation(onCreation), fOnCreationClientData(onCreationClientData) {
127 fTrackTable = new OggTrackTable;
128 fDemuxesTable = HashTable::create(ONE_WORD_HASH_KEYS);
129
130 FramedSource* inputSource = ByteStreamFileSource::createNew(envir(), fileName);
131 if (inputSource == NULL) {
132 // The specified input file does not exist!
133 fParserForInitialization = NULL;
134 handleEndOfBosPageParsing(); // we have no file, and thus no tracks, but we still need to signal this
135 } else {
136 // Initialize ourselves by parsing the file's headers:
137 fParserForInitialization
138 = new OggFileParser(*this, inputSource, handleEndOfBosPageParsing, this);
139 }
140}
141
142OggFile::~OggFile() {
143 delete fParserForInitialization;
144
145 // Delete any outstanding "OggDemux"s, and the table for them:
146 OggDemux* demux;
147 while ((demux = (OggDemux*)fDemuxesTable->RemoveNext()) != NULL) {
148 delete demux;
149 }
150 delete fDemuxesTable;
151 delete fTrackTable;
152
153 delete[] (char*)fFileName;
154}
155
156void OggFile::handleEndOfBosPageParsing(void* clientData) {
157 ((OggFile*)clientData)->handleEndOfBosPageParsing();
158}
159
160void OggFile::handleEndOfBosPageParsing() {
161 // Delete our parser, because it's done its job now:
162 delete fParserForInitialization; fParserForInitialization = NULL;
163
164 // Finally, signal our caller that we've been created and initialized:
165 if (fOnCreation != NULL) (*fOnCreation)(this, fOnCreationClientData);
166}
167
168void OggFile::addTrack(OggTrack* newTrack) {
169 fTrackTable->add(newTrack);
170}
171
172void OggFile::removeDemux(OggDemux* demux) {
173 fDemuxesTable->Remove((char const*)demux);
174}
175
176
177////////// OggTrackTable implementation /////////
178
179OggTrackTable::OggTrackTable()
180 : fTable(HashTable::create(ONE_WORD_HASH_KEYS)) {
181}
182
183OggTrackTable::~OggTrackTable() {
184 // Remove and delete all of our "OggTrack" descriptors, and the hash table itself:
185 OggTrack* track;
186 while ((track = (OggTrack*)fTable->RemoveNext()) != NULL) {
187 delete track;
188 }
189 delete fTable;
190}
191
192void OggTrackTable::add(OggTrack* newTrack) {
193 OggTrack* existingTrack
194 = (OggTrack*)fTable->Add((char const*)newTrack->trackNumber, newTrack);
195 delete existingTrack; // if any
196}
197
198OggTrack* OggTrackTable::lookup(u_int32_t trackNumber) {
199 return (OggTrack*)fTable->Lookup((char const*)trackNumber);
200}
201
202unsigned OggTrackTable::numTracks() const { return fTable->numEntries(); }
203
204OggTrackTableIterator::OggTrackTableIterator(OggTrackTable& ourTable) {
205 fIter = HashTable::Iterator::create(*(ourTable.fTable));
206}
207
208OggTrackTableIterator::~OggTrackTableIterator() {
209 delete fIter;
210}
211
212OggTrack* OggTrackTableIterator::next() {
213 char const* key;
214 return (OggTrack*)fIter->next(key);
215}
216
217
218////////// OggTrack implementation //////////
219
220OggTrack::OggTrack()
221 : trackNumber(0), mimeType(NULL),
222 samplingFrequency(48000), numChannels(2), estBitrate(100) { // default settings
223 vtoHdrs.header[0] = vtoHdrs.header[1] = vtoHdrs.header[2] = NULL;
224 vtoHdrs.headerSize[0] = vtoHdrs.headerSize[1] = vtoHdrs.headerSize[2] = 0;
225
226 vtoHdrs.vorbis_mode_count = 0;
227 vtoHdrs.vorbis_mode_blockflag = NULL;
228}
229
230OggTrack::~OggTrack() {
231 delete[] vtoHdrs.header[0]; delete[] vtoHdrs.header[1]; delete[] vtoHdrs.header[2];
232 delete[] vtoHdrs.vorbis_mode_blockflag;
233}
234
235
236///////// OggDemux implementation /////////
237
238FramedSource* OggDemux::newDemuxedTrack(u_int32_t& resultTrackNumber) {
239 OggTrack* nextTrack;
240 do {
241 nextTrack = fIter->next();
242 } while (nextTrack != NULL && nextTrack->mimeType == NULL);
243
244 if (nextTrack == NULL) { // no more tracks
245 resultTrackNumber = 0;
246 return NULL;
247 }
248
249 resultTrackNumber = nextTrack->trackNumber;
250 FramedSource* trackSource = new OggDemuxedTrack(envir(), resultTrackNumber, *this);
251 fDemuxedTracksTable->Add((char const*)resultTrackNumber, trackSource);
252 return trackSource;
253}
254
255FramedSource* OggDemux::newDemuxedTrackByTrackNumber(unsigned trackNumber) {
256 if (trackNumber == 0) return NULL;
257
258 FramedSource* trackSource = new OggDemuxedTrack(envir(), trackNumber, *this);
259 fDemuxedTracksTable->Add((char const*)trackNumber, trackSource);
260 return trackSource;
261}
262
263OggDemuxedTrack* OggDemux::lookupDemuxedTrack(u_int32_t trackNumber) {
264 return (OggDemuxedTrack*)fDemuxedTracksTable->Lookup((char const*)trackNumber);
265}
266
267OggDemux::OggDemux(OggFile& ourFile)
268 : Medium(ourFile.envir()),
269 fOurFile(ourFile), fDemuxedTracksTable(HashTable::create(ONE_WORD_HASH_KEYS)),
270 fIter(new OggTrackTableIterator(*fOurFile.fTrackTable)) {
271 FramedSource* fileSource = ByteStreamFileSource::createNew(envir(), ourFile.fileName());
272 fOurParser = new OggFileParser(ourFile, fileSource, handleEndOfFile, this, this);
273}
274
275OggDemux::~OggDemux() {
276 // Begin by acting as if we've reached the end of the source file.
277 // This should cause all of our demuxed tracks to get closed.
278 handleEndOfFile();
279
280 // Then delete our table of "OggDemuxedTrack"s
281 // - but not the "OggDemuxedTrack"s themselves; that should have already happened:
282 delete fDemuxedTracksTable;
283
284 delete fIter;
285 delete fOurParser;
286 fOurFile.removeDemux(this);
287}
288
289void OggDemux::removeTrack(u_int32_t trackNumber) {
290 fDemuxedTracksTable->Remove((char const*)trackNumber);
291 if (fDemuxedTracksTable->numEntries() == 0) {
292 // We no longer have any demuxed tracks, so delete ourselves now:
293 delete this;
294 }
295}
296
297void OggDemux::continueReading() {
298 fOurParser->continueParsing();
299}
300
301void OggDemux::handleEndOfFile(void* clientData) {
302 ((OggDemux*)clientData)->handleEndOfFile();
303}
304
305void OggDemux::handleEndOfFile() {
306 // Iterate through all of our 'demuxed tracks', handling 'end of input' on each one.
307 // Hack: Because this can cause the hash table to get modified underneath us,
308 // we don't call the handlers until after we've first iterated through all of the tracks.
309 unsigned numTracks = fDemuxedTracksTable->numEntries();
310 if (numTracks == 0) return;
311 OggDemuxedTrack** tracks = new OggDemuxedTrack*[numTracks];
312
313 HashTable::Iterator* iter = HashTable::Iterator::create(*fDemuxedTracksTable);
314 unsigned i;
315 char const* trackNumber;
316
317 for (i = 0; i < numTracks; ++i) {
318 tracks[i] = (OggDemuxedTrack*)iter->next(trackNumber);
319 }
320 delete iter;
321
322 for (i = 0; i < numTracks; ++i) {
323 if (tracks[i] == NULL) continue; // sanity check; shouldn't happen
324 tracks[i]->handleClosure();
325 }
326
327 delete[] tracks;
328}
329