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 encapsulating the state of a MP3 stream
19// Implementation
20
21#include "MP3StreamState.hh"
22#include "InputFile.hh"
23#include "GroupsockHelper.hh"
24
25#if defined(__WIN32__) || defined(_WIN32)
26#define snprintf _snprintf
27#if _MSC_VER >= 1400 // 1400 == vs2005
28#define fileno _fileno
29#endif
30#endif
31
32#define MILLION 1000000
33
34MP3StreamState::MP3StreamState(UsageEnvironment& env)
35 : fEnv(env), fFid(NULL), fPresentationTimeScale(1) {
36}
37
38MP3StreamState::~MP3StreamState() {
39 // Close our open file or socket:
40 if (fFid != NULL && fFid != stdin) {
41 if (fFidIsReallyASocket) {
42 intptr_t fid_long = (intptr_t)fFid;
43 closeSocket((int)fid_long);
44 } else {
45 CloseInputFile(fFid);
46 }
47 }
48}
49
50void MP3StreamState::assignStream(FILE* fid, unsigned fileSize) {
51 fFid = fid;
52
53 if (fileSize == (unsigned)(-1)) { /*HACK#####*/
54 fFidIsReallyASocket = 1;
55 fFileSize = 0;
56 } else {
57 fFidIsReallyASocket = 0;
58 fFileSize = fileSize;
59 }
60 fNumFramesInFile = 0; // until we know otherwise
61 fIsVBR = fHasXingTOC = False; // ditto
62
63 // Set the first frame's 'presentation time' to the current wall time:
64 gettimeofday(&fNextFramePresentationTime, NULL);
65}
66
67struct timeval MP3StreamState::currentFramePlayTime() const {
68 unsigned const numSamples = 1152;
69 unsigned const freq = fr().samplingFreq*(1 + fr().isMPEG2);
70
71 // result is numSamples/freq
72 unsigned const uSeconds
73 = ((numSamples*2*MILLION)/freq + 1)/2; // rounds to nearest integer
74
75 struct timeval result;
76 result.tv_sec = uSeconds/MILLION;
77 result.tv_usec = uSeconds%MILLION;
78 return result;
79}
80
81float MP3StreamState::filePlayTime() const {
82 unsigned numFramesInFile = fNumFramesInFile;
83 if (numFramesInFile == 0) {
84 // Estimate the number of frames from the file size, and the
85 // size of the current frame:
86 numFramesInFile = fFileSize/(4 + fCurrentFrame.frameSize);
87 }
88
89 struct timeval const pt = currentFramePlayTime();
90 return numFramesInFile*(pt.tv_sec + pt.tv_usec/(float)MILLION);
91}
92
93unsigned MP3StreamState::getByteNumberFromPositionFraction(float fraction) {
94 if (fHasXingTOC) {
95 // The file is VBR, with a Xing TOC; use it to determine which byte to seek to:
96 float percent = fraction*100.0f;
97 unsigned a = (unsigned)percent;
98 if (a > 99) a = 99;
99
100 unsigned fa = fXingTOC[a];
101 unsigned fb;
102 if (a < 99) {
103 fb = fXingTOC[a+1];
104 } else {
105 fb = 256;
106 }
107 fraction = (fa + (fb-fa)*(percent-a))/256.0f;
108 }
109
110 return (unsigned)(fraction*fFileSize);
111}
112
113void MP3StreamState::seekWithinFile(unsigned seekByteNumber) {
114 if (fFidIsReallyASocket) return; // it's not seekable
115
116 SeekFile64(fFid, seekByteNumber, SEEK_SET);
117}
118
119unsigned MP3StreamState::findNextHeader(struct timeval& presentationTime) {
120 presentationTime = fNextFramePresentationTime;
121
122 if (!findNextFrame()) return 0;
123
124 // From this frame, figure out the *next* frame's presentation time:
125 struct timeval framePlayTime = currentFramePlayTime();
126 if (fPresentationTimeScale > 1) {
127 // Scale this value
128 unsigned secondsRem = framePlayTime.tv_sec % fPresentationTimeScale;
129 framePlayTime.tv_sec -= secondsRem;
130 framePlayTime.tv_usec += secondsRem*MILLION;
131 framePlayTime.tv_sec /= fPresentationTimeScale;
132 framePlayTime.tv_usec /= fPresentationTimeScale;
133 }
134 fNextFramePresentationTime.tv_usec += framePlayTime.tv_usec;
135 fNextFramePresentationTime.tv_sec
136 += framePlayTime.tv_sec + fNextFramePresentationTime.tv_usec/MILLION;
137 fNextFramePresentationTime.tv_usec %= MILLION;
138
139 return fr().hdr;
140}
141
142Boolean MP3StreamState::readFrame(unsigned char* outBuf, unsigned outBufSize,
143 unsigned& resultFrameSize,
144 unsigned& resultDurationInMicroseconds) {
145 /* We assume that "mp3FindNextHeader()" has already been called */
146
147 resultFrameSize = 4 + fr().frameSize;
148
149 if (outBufSize < resultFrameSize) {
150#ifdef DEBUG_ERRORS
151 fprintf(stderr, "Insufficient buffer size for reading input frame (%d, need %d)\n",
152 outBufSize, resultFrameSize);
153#endif
154 if (outBufSize < 4) outBufSize = 0;
155 resultFrameSize = outBufSize;
156
157 return False;
158 }
159
160 if (resultFrameSize >= 4) {
161 unsigned& hdr = fr().hdr;
162 *outBuf++ = (unsigned char)(hdr>>24);
163 *outBuf++ = (unsigned char)(hdr>>16);
164 *outBuf++ = (unsigned char)(hdr>>8);
165 *outBuf++ = (unsigned char)(hdr);
166
167 memmove(outBuf, fr().frameBytes, resultFrameSize-4);
168 }
169
170 struct timeval const pt = currentFramePlayTime();
171 resultDurationInMicroseconds = pt.tv_sec*MILLION + pt.tv_usec;
172
173 return True;
174}
175
176void MP3StreamState::getAttributes(char* buffer, unsigned bufferSize) const {
177 char const* formatStr
178 = "bandwidth %d MPEGnumber %d MPEGlayer %d samplingFrequency %d isStereo %d playTime %d isVBR %d";
179 unsigned fpt = (unsigned)(filePlayTime() + 0.5); // rounds to nearest integer
180#if defined(IRIX) || defined(ALPHA) || defined(_QNX4) || defined(IMN_PIM) || defined(CRIS)
181 /* snprintf() isn't defined, so just use sprintf() - ugh! */
182 sprintf(buffer, formatStr,
183 fr().bitrate, fr().isMPEG2 ? 2 : 1, fr().layer, fr().samplingFreq, fr().isStereo,
184 fpt, fIsVBR);
185#else
186 snprintf(buffer, bufferSize, formatStr,
187 fr().bitrate, fr().isMPEG2 ? 2 : 1, fr().layer, fr().samplingFreq, fr().isStereo,
188 fpt, fIsVBR);
189#endif
190}
191
192// This is crufty old code that needs to be cleaned up #####
193#define HDRCMPMASK 0xfffffd00
194
195Boolean MP3StreamState::findNextFrame() {
196 unsigned char hbuf[8];
197 unsigned l; int i;
198 int attempt = 0;
199
200 read_again:
201 if (readFromStream(hbuf, 4) != 4) return False;
202
203 fr().hdr = ((unsigned long) hbuf[0] << 24)
204 | ((unsigned long) hbuf[1] << 16)
205 | ((unsigned long) hbuf[2] << 8)
206 | (unsigned long) hbuf[3];
207
208#ifdef DEBUG_PARSE
209 fprintf(stderr, "fr().hdr: 0x%08x\n", fr().hdr);
210#endif
211 if (fr().oldHdr != fr().hdr || !fr().oldHdr) {
212 i = 0;
213 init_resync:
214#ifdef DEBUG_PARSE
215 fprintf(stderr, "init_resync: fr().hdr: 0x%08x\n", fr().hdr);
216#endif
217 if ( (fr().hdr & 0xffe00000) != 0xffe00000
218 || (fr().hdr & 0x00060000) == 0 // undefined 'layer' field
219 || (fr().hdr & 0x0000F000) == 0 // 'free format' bitrate index
220 || (fr().hdr & 0x0000F000) == 0x0000F000 // undefined bitrate index
221 || (fr().hdr & 0x00000C00) == 0x00000C00 // undefined frequency index
222 || (fr().hdr & 0x00000003) != 0x00000000 // 'emphasis' field unexpectedly set
223 ) {
224 /* RSF: Do the following test even if we're not at the
225 start of the file, in case we have two or more
226 separate MP3 files cat'ed together:
227 */
228 /* Check for RIFF hdr */
229 if (fr().hdr == ('R'<<24)+('I'<<16)+('F'<<8)+'F') {
230 unsigned char buf[70 /*was: 40*/];
231#ifdef DEBUG_ERRORS
232 fprintf(stderr,"Skipped RIFF header\n");
233#endif
234 readFromStream(buf, 66); /* already read 4 */
235 goto read_again;
236 }
237 /* Check for ID3 hdr */
238 if ((fr().hdr&0xFFFFFF00) == ('I'<<24)+('D'<<16)+('3'<<8)) {
239 unsigned tagSize, bytesToSkip;
240 unsigned char buf[1000];
241 readFromStream(buf, 6); /* already read 4 */
242 tagSize = ((buf[2]&0x7F)<<21) + ((buf[3]&0x7F)<<14) + ((buf[4]&0x7F)<<7) + (buf[5]&0x7F);
243 bytesToSkip = tagSize;
244 while (bytesToSkip > 0) {
245 unsigned bytesToRead = sizeof buf;
246 if (bytesToRead > bytesToSkip) {
247 bytesToRead = bytesToSkip;
248 }
249 readFromStream(buf, bytesToRead);
250 bytesToSkip -= bytesToRead;
251 }
252#ifdef DEBUG_ERRORS
253 fprintf(stderr,"Skipped %d-byte ID3 header\n", tagSize);
254#endif
255 goto read_again;
256 }
257 /* give up after 20,000 bytes */
258 if (i++ < 20000/*4096*//*1024*/) {
259 memmove (&hbuf[0], &hbuf[1], 3);
260 if (readFromStream(hbuf+3,1) != 1) {
261 return False;
262 }
263 fr().hdr <<= 8;
264 fr().hdr |= hbuf[3];
265 fr().hdr &= 0xffffffff;
266#ifdef DEBUG_PARSE
267 fprintf(stderr, "calling init_resync %d\n", i);
268#endif
269 goto init_resync;
270 }
271#ifdef DEBUG_ERRORS
272 fprintf(stderr,"Giving up searching valid MPEG header\n");
273#endif
274 return False;
275
276#ifdef DEBUG_ERRORS
277 fprintf(stderr,"Illegal Audio-MPEG-Header 0x%08lx at offset 0x%lx.\n",
278 fr().hdr,tell_stream(str)-4);
279#endif
280 /* Read more bytes until we find something that looks
281 reasonably like a valid header. This is not a
282 perfect strategy, but it should get us back on the
283 track within a short time (and hopefully without
284 too much distortion in the audio output). */
285 do {
286 attempt++;
287 memmove (&hbuf[0], &hbuf[1], 7);
288 if (readFromStream(&hbuf[3],1) != 1) {
289 return False;
290 }
291
292 /* This is faster than combining fr().hdr from scratch */
293 fr().hdr = ((fr().hdr << 8) | hbuf[3]) & 0xffffffff;
294
295 if (!fr().oldHdr)
296 goto init_resync; /* "considered harmful", eh? */
297
298 } while ((fr().hdr & HDRCMPMASK) != (fr().oldHdr & HDRCMPMASK)
299 && (fr().hdr & HDRCMPMASK) != (fr().firstHdr & HDRCMPMASK));
300#ifdef DEBUG_ERRORS
301 fprintf (stderr, "Skipped %d bytes in input.\n", attempt);
302#endif
303 }
304 if (!fr().firstHdr) {
305 fr().firstHdr = fr().hdr;
306 }
307
308 fr().setParamsFromHeader();
309 fr().setBytePointer(fr().frameBytes, fr().frameSize);
310
311 fr().oldHdr = fr().hdr;
312
313 if (fr().isFreeFormat) {
314#ifdef DEBUG_ERRORS
315 fprintf(stderr,"Free format not supported.\n");
316#endif
317 return False;
318 }
319
320#ifdef MP3_ONLY
321 if (fr().layer != 3) {
322#ifdef DEBUG_ERRORS
323 fprintf(stderr, "MPEG layer %d is not supported!\n", fr().layer);
324#endif
325 return False;
326 }
327#endif
328 }
329
330 if ((l = readFromStream(fr().frameBytes, fr().frameSize))
331 != fr().frameSize) {
332 if (l == 0) return False;
333 memset(fr().frameBytes+1, 0, fr().frameSize-1);
334 }
335
336 return True;
337}
338
339static Boolean socketIsReadable(int socket) {
340 const unsigned numFds = socket+1;
341 fd_set rd_set;
342 FD_ZERO(&rd_set);
343 FD_SET((unsigned)socket, &rd_set);
344 struct timeval timeout;
345 timeout.tv_sec = timeout.tv_usec = 0;
346
347 int result = select(numFds, &rd_set, NULL, NULL, &timeout);
348 return result != 0; // not > 0, because windows can return -1 for file sockets
349}
350
351static char watchVariable;
352
353static void checkFunc(void* /*clientData*/) {
354 watchVariable = ~0;
355}
356
357static void waitUntilSocketIsReadable(UsageEnvironment& env, int socket) {
358 while (!socketIsReadable(socket)) {
359 // Delay a short period of time before checking again.
360 unsigned usecsToDelay = 1000; // 1 ms
361 env.taskScheduler().scheduleDelayedTask(usecsToDelay,
362 (TaskFunc*)checkFunc, (void*)NULL);
363 watchVariable = 0;
364 env.taskScheduler().doEventLoop(&watchVariable);
365 // This allows other tasks to run while we're waiting:
366 }
367}
368
369unsigned MP3StreamState::readFromStream(unsigned char* buf,
370 unsigned numChars) {
371 // Hack for doing socket I/O instead of file I/O (e.g., on Windows)
372 if (fFidIsReallyASocket) {
373 intptr_t fid_long = (intptr_t)fFid;
374 int sock = (int)fid_long;
375 unsigned totBytesRead = 0;
376 do {
377 waitUntilSocketIsReadable(fEnv, sock);
378 int bytesRead
379 = recv(sock, &((char*)buf)[totBytesRead], numChars-totBytesRead, 0);
380 if (bytesRead < 0) return 0;
381
382 totBytesRead += (unsigned)bytesRead;
383 } while (totBytesRead < numChars);
384
385 return totBytesRead;
386 } else {
387#ifndef _WIN32_WCE
388 waitUntilSocketIsReadable(fEnv, (int)fileno(fFid));
389#endif
390 return fread(buf, 1, numChars, fFid);
391 }
392}
393
394#define XING_FRAMES_FLAG 0x0001
395#define XING_BYTES_FLAG 0x0002
396#define XING_TOC_FLAG 0x0004
397#define XING_VBR_SCALE_FLAG 0x0008
398
399void MP3StreamState::checkForXingHeader() {
400 // Look for 'Xing' in the first 4 bytes after the 'side info':
401 if (fr().frameSize < fr().sideInfoSize) return;
402 unsigned bytesAvailable = fr().frameSize - fr().sideInfoSize;
403 unsigned char* p = &(fr().frameBytes[fr().sideInfoSize]);
404
405 if (bytesAvailable < 8) return;
406 if (p[0] != 'X' || p[1] != 'i' || p[2] != 'n' || p[3] != 'g') return;
407
408 // We found it.
409 fIsVBR = True;
410
411 u_int32_t flags = (p[4]<<24) | (p[5]<<16) | (p[6]<<8) | p[7];
412 unsigned i = 8;
413 bytesAvailable -= 8;
414
415 if (flags&XING_FRAMES_FLAG) {
416 // The next 4 bytes are the number of frames:
417 if (bytesAvailable < 4) return;
418 fNumFramesInFile = (p[i]<<24)|(p[i+1]<<16)|(p[i+2]<<8)|(p[i+3]);
419 i += 4; bytesAvailable -= 4;
420 }
421
422 if (flags&XING_BYTES_FLAG) {
423 // The next 4 bytes is the file size:
424 if (bytesAvailable < 4) return;
425 fFileSize = (p[i]<<24)|(p[i+1]<<16)|(p[i+2]<<8)|(p[i+3]);
426 i += 4; bytesAvailable -= 4;
427 }
428
429 if (flags&XING_TOC_FLAG) {
430 // Fill in the Xing 'table of contents':
431 if (bytesAvailable < XING_TOC_LENGTH) return;
432 fHasXingTOC = True;
433 for (unsigned j = 0; j < XING_TOC_LENGTH; ++j) {
434 fXingTOC[j] = p[i+j];
435 }
436 i += XING_TOC_FLAG; bytesAvailable -= XING_TOC_FLAG;
437 }
438}
439