1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2.1 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
**********/
// "liveMedia"
// Copyright (c) 1996-2005 Live Networks, Inc. All rights reserved.
// A source object for AMR audio files (as defined in RFC 3267, section 5)
// Implementation
#include "AMRAudioFileSource.hh"
#include "InputFile.hh"
#include "GroupsockHelper.hh"
////////// AMRAudioFileSource //////////
AMRAudioFileSource*
AMRAudioFileSource::createNew(UsageEnvironment& env, char const* fileName) {
FILE* fid = NULL;
Boolean magicNumberOK = True;
do {
fid = OpenInputFile(env, fileName);
if (fid == NULL) break;
// Now, having opened the input file, read the first few bytes, to
// check the required 'magic number':
magicNumberOK = False; // until we learn otherwise
Boolean isWideband = False; // by default
unsigned numChannels = 1; // by default
char buf[100];
// Start with the first 6 bytes (the first 5 of which must be "#!AMR"):
if (fread(buf, 1, 6, fid) < 6) break;
if (strncmp(buf, "#!AMR", 5) != 0) break; // bad magic #
unsigned bytesRead = 6;
// The next bytes must be "\n", "-WB\n", "_MC1.0\n", or "-WB_MC1.0\n"
if (buf[5] == '-') {
// The next bytes must be "WB\n" or "WB_MC1.0\n"
if (fread(&buf[bytesRead], 1, 3, fid) < 3) break;
if (strncmp(&buf[bytesRead], "WB", 2) != 0) break; // bad magic #
isWideband = True;
bytesRead += 3;
}
if (buf[bytesRead-1] == '_') {
// The next bytes must be "MC1.0\n"
if (fread(&buf[bytesRead], 1, 6, fid) < 6) break;
if (strncmp(&buf[bytesRead], "MC1.0\n", 6) != 0) break; // bad magic #
bytesRead += 6;
// The next 4 bytes contain the number of channels:
char channelDesc[4];
if (fread(channelDesc, 1, 4, fid) < 4) break;
numChannels = channelDesc[3]&0xF;
} else if (buf[bytesRead-1] != '\n') {
break; // bad magic #
}
// If we get here, the magic number was OK:
magicNumberOK = True;
#ifdef DEBUG
fprintf(stderr, "isWideband: %d, numChannels: %d\n",
isWideband, numChannels);
#endif
return new AMRAudioFileSource(env, fid, isWideband, numChannels);
} while (0);
// An error occurred:
CloseInputFile(fid);
if (!magicNumberOK) {
env.setResultMsg("Bad (or nonexistent) AMR file header");
}
return NULL;
}
AMRAudioFileSource
::AMRAudioFileSource(UsageEnvironment& env, FILE* fid,
Boolean isWideband, unsigned numChannels)
: AMRAudioSource(env, isWideband, numChannels),
fFid(fid) {
}
AMRAudioFileSource::~AMRAudioFileSource() {
CloseInputFile(fFid);
}
// The mapping from the "FT" field to frame size.
// Values of 65535 are invalid.
#define FT_INVALID 65535
static unsigned short frameSize[16] = {
12, 13, 15, 17,
19, 20, 26, 31,
5, FT_INVALID, FT_INVALID, FT_INVALID,
FT_INVALID, FT_INVALID, FT_INVALID, 0
};
static unsigned short frameSizeWideband[16] = {
17, 23, 32, 36,
40, 46, 50, 58,
60, 5, FT_INVALID, FT_INVALID,
FT_INVALID, FT_INVALID, 0, 0
};
void AMRAudioFileSource::doGetNextFrame() {
if (feof(fFid) || ferror(fFid)) {
handleClosure(this);
return;
}
// Begin by reading the 1-byte frame header (and checking it for validity)
while (1) {
if (fread(&fLastFrameHeader, 1, 1, fFid) < 1) {
handleClosure(this);
return;
}
if ((fLastFrameHeader&0x83) != 0) {
#ifdef DEBUG
fprintf(stderr, "Invalid frame header 0x%02x (padding bits (0x83) are not zero)\n", fLastFrameHeader);
#endif
} else {
unsigned char ft = (fLastFrameHeader&0x78)>>3;
fFrameSize = fIsWideband ? frameSizeWideband[ft] : frameSize[ft];
if (fFrameSize == FT_INVALID) {
#ifdef DEBUG
fprintf(stderr, "Invalid FT field %d (from frame header 0x%02x)\n",
ft, fLastFrameHeader);
#endif
} else {
// The frame header is OK
#ifdef DEBUG
fprintf(stderr, "Valid frame header 0x%02x -> ft %d -> frame size %d\n", fLastFrameHeader, ft, fFrameSize);
#endif
break;
}
}
}
// Next, read the frame-block into the buffer provided:
fFrameSize *= fNumChannels; // because multiple channels make up a frame-block
if (fFrameSize > fMaxSize) {
fNumTruncatedBytes = fFrameSize - fMaxSize;
fFrameSize = fMaxSize;
}
fFrameSize = fread(fTo, 1, fFrameSize, fFid);
// Set the 'presentation time':
if (fPresentationTime.tv_sec == 0 && fPresentationTime.tv_usec == 0) {
// This is the first frame, so use the current time:
gettimeofday(&fPresentationTime, NULL);
} else {
// Increment by the play time of the previous frame (20 ms)
unsigned uSeconds = fPresentationTime.tv_usec + 20000;
fPresentationTime.tv_sec += uSeconds/1000000;
fPresentationTime.tv_usec = uSeconds%1000000;
}
fDurationInMicroseconds = 20000; // each frame is 20 ms
// Switch to another task, and inform the reader that he has data:
nextTask() = envir().taskScheduler().scheduleDelayedTask(0,
(TaskFunc*)FramedSource::afterGetting, this);
}
|