| 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 |  | 
|---|