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 source object for AAC audio files in ADTS format
19// Implementation
20
21#include "ADTSAudioFileSource.hh"
22#include "InputFile.hh"
23#include <GroupsockHelper.hh>
24
25////////// ADTSAudioFileSource //////////
26
27static unsigned const samplingFrequencyTable[16] = {
28 96000, 88200, 64000, 48000,
29 44100, 32000, 24000, 22050,
30 16000, 12000, 11025, 8000,
31 7350, 0, 0, 0
32};
33
34ADTSAudioFileSource*
35ADTSAudioFileSource::createNew(UsageEnvironment& env, char const* fileName) {
36 FILE* fid = NULL;
37 do {
38 fid = OpenInputFile(env, fileName);
39 if (fid == NULL) break;
40
41 // Now, having opened the input file, read the fixed header of the first frame,
42 // to get the audio stream's parameters:
43 unsigned char fixedHeader[4]; // it's actually 3.5 bytes long
44 if (fread(fixedHeader, 1, sizeof fixedHeader, fid) < sizeof fixedHeader) break;
45
46 // Check the 'syncword':
47 if (!(fixedHeader[0] == 0xFF && (fixedHeader[1]&0xF0) == 0xF0)) {
48 env.setResultMsg("Bad 'syncword' at start of ADTS file");
49 break;
50 }
51
52 // Get and check the 'profile':
53 u_int8_t profile = (fixedHeader[2]&0xC0)>>6; // 2 bits
54 if (profile == 3) {
55 env.setResultMsg("Bad (reserved) 'profile': 3 in first frame of ADTS file");
56 break;
57 }
58
59 // Get and check the 'sampling_frequency_index':
60 u_int8_t sampling_frequency_index = (fixedHeader[2]&0x3C)>>2; // 4 bits
61 if (samplingFrequencyTable[sampling_frequency_index] == 0) {
62 env.setResultMsg("Bad 'sampling_frequency_index' in first frame of ADTS file");
63 break;
64 }
65
66 // Get and check the 'channel_configuration':
67 u_int8_t channel_configuration
68 = ((fixedHeader[2]&0x01)<<2)|((fixedHeader[3]&0xC0)>>6); // 3 bits
69
70 // If we get here, the frame header was OK.
71 // Reset the fid to the beginning of the file:
72#ifndef _WIN32_WCE
73 rewind(fid);
74#else
75 SeekFile64(fid, SEEK_SET,0);
76#endif
77#ifdef DEBUG
78 fprintf(stderr, "Read first frame: profile %d, "
79 "sampling_frequency_index %d => samplingFrequency %d, "
80 "channel_configuration %d\n",
81 profile,
82 sampling_frequency_index, samplingFrequencyTable[sampling_frequency_index],
83 channel_configuration);
84#endif
85 return new ADTSAudioFileSource(env, fid, profile,
86 sampling_frequency_index, channel_configuration);
87 } while (0);
88
89 // An error occurred:
90 CloseInputFile(fid);
91 return NULL;
92}
93
94ADTSAudioFileSource
95::ADTSAudioFileSource(UsageEnvironment& env, FILE* fid, u_int8_t profile,
96 u_int8_t samplingFrequencyIndex, u_int8_t channelConfiguration)
97 : FramedFileSource(env, fid) {
98 fSamplingFrequency = samplingFrequencyTable[samplingFrequencyIndex];
99 fNumChannels = channelConfiguration == 0 ? 2 : channelConfiguration;
100 fuSecsPerFrame
101 = (1024/*samples-per-frame*/*1000000) / fSamplingFrequency/*samples-per-second*/;
102
103 // Construct the 'AudioSpecificConfig', and from it, the corresponding ASCII string:
104 unsigned char audioSpecificConfig[2];
105 u_int8_t const audioObjectType = profile + 1;
106 audioSpecificConfig[0] = (audioObjectType<<3) | (samplingFrequencyIndex>>1);
107 audioSpecificConfig[1] = (samplingFrequencyIndex<<7) | (channelConfiguration<<3);
108 sprintf(fConfigStr, "%02X%02x", audioSpecificConfig[0], audioSpecificConfig[1]);
109}
110
111ADTSAudioFileSource::~ADTSAudioFileSource() {
112 CloseInputFile(fFid);
113}
114
115// Note: We should change the following to use asynchronous file reading, #####
116// as we now do with ByteStreamFileSource. #####
117void ADTSAudioFileSource::doGetNextFrame() {
118 // Begin by reading the 7-byte fixed_variable headers:
119 unsigned char headers[7];
120 if (fread(headers, 1, sizeof headers, fFid) < sizeof headers
121 || feof(fFid) || ferror(fFid)) {
122 // The input source has ended:
123 handleClosure();
124 return;
125 }
126
127 // Extract important fields from the headers:
128 Boolean protection_absent = headers[1]&0x01;
129 u_int16_t frame_length
130 = ((headers[3]&0x03)<<11) | (headers[4]<<3) | ((headers[5]&0xE0)>>5);
131#ifdef DEBUG
132 u_int16_t syncword = (headers[0]<<4) | (headers[1]>>4);
133 fprintf(stderr, "Read frame: syncword 0x%x, protection_absent %d, frame_length %d\n", syncword, protection_absent, frame_length);
134 if (syncword != 0xFFF) fprintf(stderr, "WARNING: Bad syncword!\n");
135#endif
136 unsigned numBytesToRead
137 = frame_length > sizeof headers ? frame_length - sizeof headers : 0;
138
139 // If there's a 'crc_check' field, skip it:
140 if (!protection_absent) {
141 SeekFile64(fFid, 2, SEEK_CUR);
142 numBytesToRead = numBytesToRead > 2 ? numBytesToRead - 2 : 0;
143 }
144
145 // Next, read the raw frame data into the buffer provided:
146 if (numBytesToRead > fMaxSize) {
147 fNumTruncatedBytes = numBytesToRead - fMaxSize;
148 numBytesToRead = fMaxSize;
149 }
150 int numBytesRead = fread(fTo, 1, numBytesToRead, fFid);
151 if (numBytesRead < 0) numBytesRead = 0;
152 fFrameSize = numBytesRead;
153 fNumTruncatedBytes += numBytesToRead - numBytesRead;
154
155 // Set the 'presentation time':
156 if (fPresentationTime.tv_sec == 0 && fPresentationTime.tv_usec == 0) {
157 // This is the first frame, so use the current time:
158 gettimeofday(&fPresentationTime, NULL);
159 } else {
160 // Increment by the play time of the previous frame:
161 unsigned uSeconds = fPresentationTime.tv_usec + fuSecsPerFrame;
162 fPresentationTime.tv_sec += uSeconds/1000000;
163 fPresentationTime.tv_usec = uSeconds%1000000;
164 }
165
166 fDurationInMicroseconds = fuSecsPerFrame;
167
168 // Switch to another task, and inform the reader that he has data:
169 nextTask() = envir().taskScheduler().scheduleDelayedTask(0,
170 (TaskFunc*)FramedSource::afterGetting, this);
171}
172