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 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
19// on demand, from a MP3 audio file.
20// (Actually, any MPEG-1 or MPEG-2 audio file should work.)
21// Implementation
22
23#include "MP3AudioFileServerMediaSubsession.hh"
24#include "MPEG1or2AudioRTPSink.hh"
25#include "MP3ADURTPSink.hh"
26#include "MP3FileSource.hh"
27#include "MP3ADU.hh"
28
29MP3AudioFileServerMediaSubsession* MP3AudioFileServerMediaSubsession
30::createNew(UsageEnvironment& env, char const* fileName, Boolean reuseFirstSource,
31 Boolean generateADUs, Interleaving* interleaving) {
32 return new MP3AudioFileServerMediaSubsession(env, fileName, reuseFirstSource,
33 generateADUs, interleaving);
34}
35
36MP3AudioFileServerMediaSubsession
37::MP3AudioFileServerMediaSubsession(UsageEnvironment& env,
38 char const* fileName, Boolean reuseFirstSource,
39 Boolean generateADUs,
40 Interleaving* interleaving)
41 : FileServerMediaSubsession(env, fileName, reuseFirstSource),
42 fGenerateADUs(generateADUs), fInterleaving(interleaving), fFileDuration(0.0) {
43}
44
45MP3AudioFileServerMediaSubsession
46::~MP3AudioFileServerMediaSubsession() {
47 delete fInterleaving;
48}
49
50FramedSource* MP3AudioFileServerMediaSubsession
51::createNewStreamSourceCommon(FramedSource* baseMP3Source, unsigned mp3NumBytes, unsigned& estBitrate) {
52 FramedSource* streamSource;
53 do {
54 streamSource = baseMP3Source; // by default
55 if (streamSource == NULL) break;
56
57 // Use the MP3 file size, plus the duration, to estimate the stream's bitrate:
58 if (mp3NumBytes > 0 && fFileDuration > 0.0) {
59 estBitrate = (unsigned)(mp3NumBytes/(125*fFileDuration) + 0.5); // kbps, rounded
60 } else {
61 estBitrate = 128; // kbps, estimate
62 }
63
64 if (fGenerateADUs) {
65 // Add a filter that converts the source MP3s to ADUs:
66 streamSource = ADUFromMP3Source::createNew(envir(), streamSource);
67 if (streamSource == NULL) break;
68
69 if (fInterleaving != NULL) {
70 // Add another filter that interleaves the ADUs before packetizing:
71 streamSource = MP3ADUinterleaver::createNew(envir(), *fInterleaving,
72 streamSource);
73 if (streamSource == NULL) break;
74 }
75 } else if (fFileDuration > 0.0) {
76 // Because this is a seekable file, insert a pair of filters: one that
77 // converts the input MP3 stream to ADUs; another that converts these
78 // ADUs back to MP3. This allows us to seek within the input stream without
79 // tripping over the MP3 'bit reservoir':
80 streamSource = ADUFromMP3Source::createNew(envir(), streamSource);
81 if (streamSource == NULL) break;
82
83 streamSource = MP3FromADUSource::createNew(envir(), streamSource);
84 if (streamSource == NULL) break;
85 }
86 } while (0);
87
88 return streamSource;
89}
90
91void MP3AudioFileServerMediaSubsession::getBaseStreams(FramedSource* frontStream,
92 FramedSource*& sourceMP3Stream, ADUFromMP3Source*& aduStream/*if any*/) {
93 if (fGenerateADUs) {
94 // There's an ADU stream.
95 if (fInterleaving != NULL) {
96 // There's an interleaving filter in front of the ADU stream. So go back one, to reach the ADU stream:
97 aduStream = (ADUFromMP3Source*)(((FramedFilter*)frontStream)->inputSource());
98 } else {
99 aduStream = (ADUFromMP3Source*)frontStream;
100 }
101
102 // Then, go back one more, to reach the MP3 source:
103 sourceMP3Stream = (MP3FileSource*)(aduStream->inputSource());
104 } else if (fFileDuration > 0.0) {
105 // There are a pair of filters - MP3->ADU and ADU->MP3 - in front of the
106 // original MP3 source. So, go back one, to reach the ADU source:
107 aduStream = (ADUFromMP3Source*)(((FramedFilter*)frontStream)->inputSource());
108
109 // Then, go back one more, to reach the MP3 source:
110 sourceMP3Stream = (MP3FileSource*)(aduStream->inputSource());
111 } else {
112 // There's no filter in front of the source MP3 stream (and there's no ADU stream):
113 aduStream = NULL;
114 sourceMP3Stream = frontStream;
115 }
116}
117
118
119void MP3AudioFileServerMediaSubsession
120::seekStreamSource(FramedSource* inputSource, double& seekNPT, double streamDuration, u_int64_t& /*numBytes*/) {
121 FramedSource* sourceMP3Stream;
122 ADUFromMP3Source* aduStream;
123 getBaseStreams(inputSource, sourceMP3Stream, aduStream);
124
125 if (aduStream != NULL) aduStream->resetInput(); // because we're about to seek within its source
126 ((MP3FileSource*)sourceMP3Stream)->seekWithinFile(seekNPT, streamDuration);
127}
128
129void MP3AudioFileServerMediaSubsession
130::setStreamSourceScale(FramedSource* inputSource, float scale) {
131
132 FramedSource* sourceMP3Stream;
133 ADUFromMP3Source* aduStream;
134 getBaseStreams(inputSource, sourceMP3Stream, aduStream);
135
136 if (aduStream == NULL) return; // because, in this case, the stream's not scalable
137
138 int iScale = (int)scale;
139 aduStream->setScaleFactor(iScale);
140 ((MP3FileSource*)sourceMP3Stream)->setPresentationTimeScale(iScale);
141}
142
143FramedSource* MP3AudioFileServerMediaSubsession
144::createNewStreamSource(unsigned /*clientSessionId*/, unsigned& estBitrate) {
145 MP3FileSource* mp3Source = MP3FileSource::createNew(envir(), fFileName);
146 if (mp3Source == NULL) return NULL;
147 fFileDuration = mp3Source->filePlayTime();
148
149 return createNewStreamSourceCommon(mp3Source, mp3Source->fileSize(), estBitrate);
150}
151
152RTPSink* MP3AudioFileServerMediaSubsession
153::createNewRTPSink(Groupsock* rtpGroupsock,
154 unsigned char rtpPayloadTypeIfDynamic,
155 FramedSource* /*inputSource*/) {
156 if (fGenerateADUs) {
157 return MP3ADURTPSink::createNew(envir(), rtpGroupsock,
158 rtpPayloadTypeIfDynamic);
159 } else {
160 return MPEG1or2AudioRTPSink::createNew(envir(), rtpGroupsock);
161 }
162}
163
164void MP3AudioFileServerMediaSubsession::testScaleFactor(float& scale) {
165 if (fFileDuration <= 0.0) {
166 // The file is non-seekable, so is probably a live input source.
167 // We don't support scale factors other than 1
168 scale = 1;
169 } else {
170 // We support any integral scale >= 1
171 int iScale = (int)(scale + 0.5); // round
172 if (iScale < 1) iScale = 1;
173 scale = (float)iScale;
174 }
175}
176
177float MP3AudioFileServerMediaSubsession::duration() const {
178 return fFileDuration;
179}
180