1 | /********** |
2 | This library is free software; you can redistribute it and/or modify it under |
3 | the terms of the GNU Lesser General Public License as published by the |
4 | Free Software Foundation; either version 3 of the License, or (at your |
5 | option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
6 | |
7 | This library is distributed in the hope that it will be useful, but WITHOUT |
8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
9 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
10 | more details. |
11 | |
12 | You should have received a copy of the GNU Lesser General Public License |
13 | along with this library; if not, write to the Free Software Foundation, Inc., |
14 | 51 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 | |
27 | static 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 | |
34 | ADTSAudioFileSource* |
35 | ADTSAudioFileSource::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 [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 | |
94 | ADTSAudioFileSource |
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 | |
111 | ADTSAudioFileSource::~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. ##### |
117 | void ADTSAudioFileSource::doGetNextFrame() { |
118 | // Begin by reading the 7-byte fixed_variable headers: |
119 | unsigned char [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 | |