1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "AMRExtractor"
19 #include <utils/Log.h>
20 
21 #include "include/AMRExtractor.h"
22 
23 #include <media/stagefright/foundation/ADebug.h>
24 #include <media/stagefright/DataSource.h>
25 #include <media/stagefright/MediaBufferGroup.h>
26 #include <media/stagefright/MediaDefs.h>
27 #include <media/stagefright/MediaErrors.h>
28 #include <media/stagefright/MediaSource.h>
29 #include <media/stagefright/MetaData.h>
30 #include <utils/String8.h>
31 
32 namespace android {
33 
34 class AMRSource : public MediaSource {
35 public:
36     AMRSource(const sp<DataSource> &source,
37               const sp<MetaData> &meta,
38               bool isWide,
39               const off64_t *offset_table,
40               size_t offset_table_length);
41 
42     virtual status_t start(MetaData *params = NULL);
43     virtual status_t stop();
44 
45     virtual sp<MetaData> getFormat();
46 
47     virtual status_t read(
48             MediaBuffer **buffer, const ReadOptions *options = NULL);
49 
50 protected:
51     virtual ~AMRSource();
52 
53 private:
54     sp<DataSource> mDataSource;
55     sp<MetaData> mMeta;
56     bool mIsWide;
57 
58     off64_t mOffset;
59     int64_t mCurrentTimeUs;
60     bool mStarted;
61     MediaBufferGroup *mGroup;
62 
63     off64_t mOffsetTable[OFFSET_TABLE_LEN];
64     size_t mOffsetTableLength;
65 
66     AMRSource(const AMRSource &);
67     AMRSource &operator=(const AMRSource &);
68 };
69 
70 ////////////////////////////////////////////////////////////////////////////////
71 
getFrameSize(bool isWide,unsigned FT)72 static size_t getFrameSize(bool isWide, unsigned FT) {
73     static const size_t kFrameSizeNB[16] = {
74         95, 103, 118, 134, 148, 159, 204, 244,
75         39, 43, 38, 37, // SID
76         0, 0, 0, // future use
77         0 // no data
78     };
79     static const size_t kFrameSizeWB[16] = {
80         132, 177, 253, 285, 317, 365, 397, 461, 477,
81         40, // SID
82         0, 0, 0, 0, // future use
83         0, // speech lost
84         0 // no data
85     };
86 
87     if (FT > 15 || (isWide && FT > 9 && FT < 14) || (!isWide && FT > 11 && FT < 15)) {
88         ALOGE("illegal AMR frame type %d", FT);
89         return 0;
90     }
91 
92     size_t frameSize = isWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
93 
94     // Round up bits to bytes and add 1 for the header byte.
95     frameSize = (frameSize + 7) / 8 + 1;
96 
97     return frameSize;
98 }
99 
getFrameSizeByOffset(const sp<DataSource> & source,off64_t offset,bool isWide,size_t * frameSize)100 static status_t getFrameSizeByOffset(const sp<DataSource> &source,
101         off64_t offset, bool isWide, size_t *frameSize) {
102     uint8_t header;
103     if (source->readAt(offset, &header, 1) < 1) {
104         return ERROR_IO;
105     }
106 
107     unsigned FT = (header >> 3) & 0x0f;
108 
109     *frameSize = getFrameSize(isWide, FT);
110     if (*frameSize == 0) {
111         return ERROR_MALFORMED;
112     }
113     return OK;
114 }
115 
AMRExtractor(const sp<DataSource> & source)116 AMRExtractor::AMRExtractor(const sp<DataSource> &source)
117     : mDataSource(source),
118       mInitCheck(NO_INIT),
119       mOffsetTableLength(0) {
120     String8 mimeType;
121     float confidence;
122     if (!SniffAMR(mDataSource, &mimeType, &confidence, NULL)) {
123         return;
124     }
125 
126     mIsWide = (mimeType == MEDIA_MIMETYPE_AUDIO_AMR_WB);
127 
128     mMeta = new MetaData;
129     mMeta->setCString(
130             kKeyMIMEType, mIsWide ? MEDIA_MIMETYPE_AUDIO_AMR_WB
131                                   : MEDIA_MIMETYPE_AUDIO_AMR_NB);
132 
133     mMeta->setInt32(kKeyChannelCount, 1);
134     mMeta->setInt32(kKeySampleRate, mIsWide ? 16000 : 8000);
135 
136     off64_t offset = mIsWide ? 9 : 6;
137     off64_t streamSize;
138     size_t frameSize, numFrames = 0;
139     int64_t duration = 0;
140 
141     if (mDataSource->getSize(&streamSize) == OK) {
142          while (offset < streamSize) {
143             if (getFrameSizeByOffset(source, offset, mIsWide, &frameSize) != OK) {
144                 return;
145             }
146 
147             if ((numFrames % 50 == 0) && (numFrames / 50 < OFFSET_TABLE_LEN)) {
148                 CHECK_EQ(mOffsetTableLength, numFrames / 50);
149                 mOffsetTable[mOffsetTableLength] = offset - (mIsWide ? 9: 6);
150                 mOffsetTableLength ++;
151             }
152 
153             offset += frameSize;
154             duration += 20000;  // Each frame is 20ms
155             numFrames ++;
156         }
157 
158         mMeta->setInt64(kKeyDuration, duration);
159     }
160 
161     mInitCheck = OK;
162 }
163 
~AMRExtractor()164 AMRExtractor::~AMRExtractor() {
165 }
166 
getMetaData()167 sp<MetaData> AMRExtractor::getMetaData() {
168     sp<MetaData> meta = new MetaData;
169 
170     if (mInitCheck != OK) {
171         return meta;
172     }
173 
174     meta->setCString(kKeyMIMEType, mIsWide ? "audio/amr-wb" : "audio/amr");
175 
176     return meta;
177 }
178 
countTracks()179 size_t AMRExtractor::countTracks() {
180     return mInitCheck == OK ? 1 : 0;
181 }
182 
getTrack(size_t index)183 sp<MediaSource> AMRExtractor::getTrack(size_t index) {
184     if (mInitCheck != OK || index != 0) {
185         return NULL;
186     }
187 
188     return new AMRSource(mDataSource, mMeta, mIsWide,
189             mOffsetTable, mOffsetTableLength);
190 }
191 
getTrackMetaData(size_t index,uint32_t)192 sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t /* flags */) {
193     if (mInitCheck != OK || index != 0) {
194         return NULL;
195     }
196 
197     return mMeta;
198 }
199 
200 ////////////////////////////////////////////////////////////////////////////////
201 
AMRSource(const sp<DataSource> & source,const sp<MetaData> & meta,bool isWide,const off64_t * offset_table,size_t offset_table_length)202 AMRSource::AMRSource(
203         const sp<DataSource> &source, const sp<MetaData> &meta,
204         bool isWide, const off64_t *offset_table, size_t offset_table_length)
205     : mDataSource(source),
206       mMeta(meta),
207       mIsWide(isWide),
208       mOffset(mIsWide ? 9 : 6),
209       mCurrentTimeUs(0),
210       mStarted(false),
211       mGroup(NULL),
212       mOffsetTableLength(offset_table_length) {
213     if (mOffsetTableLength > 0 && mOffsetTableLength <= OFFSET_TABLE_LEN) {
214         memcpy ((char*)mOffsetTable, (char*)offset_table, sizeof(off64_t) * mOffsetTableLength);
215     }
216 }
217 
~AMRSource()218 AMRSource::~AMRSource() {
219     if (mStarted) {
220         stop();
221     }
222 }
223 
start(MetaData *)224 status_t AMRSource::start(MetaData * /* params */) {
225     CHECK(!mStarted);
226 
227     mOffset = mIsWide ? 9 : 6;
228     mCurrentTimeUs = 0;
229     mGroup = new MediaBufferGroup;
230     mGroup->add_buffer(new MediaBuffer(128));
231     mStarted = true;
232 
233     return OK;
234 }
235 
stop()236 status_t AMRSource::stop() {
237     CHECK(mStarted);
238 
239     delete mGroup;
240     mGroup = NULL;
241 
242     mStarted = false;
243     return OK;
244 }
245 
getFormat()246 sp<MetaData> AMRSource::getFormat() {
247     return mMeta;
248 }
249 
read(MediaBuffer ** out,const ReadOptions * options)250 status_t AMRSource::read(
251         MediaBuffer **out, const ReadOptions *options) {
252     *out = NULL;
253 
254     int64_t seekTimeUs;
255     ReadOptions::SeekMode mode;
256     if (options && options->getSeekTo(&seekTimeUs, &mode)) {
257         size_t size;
258         int64_t seekFrame = seekTimeUs / 20000ll;  // 20ms per frame.
259         mCurrentTimeUs = seekFrame * 20000ll;
260 
261         size_t index = seekFrame < 0 ? 0 : seekFrame / 50;
262         if (index >= mOffsetTableLength) {
263             index = mOffsetTableLength - 1;
264         }
265 
266         mOffset = mOffsetTable[index] + (mIsWide ? 9 : 6);
267 
268         for (size_t i = 0; i< seekFrame - index * 50; i++) {
269             status_t err;
270             if ((err = getFrameSizeByOffset(mDataSource, mOffset,
271                             mIsWide, &size)) != OK) {
272                 return err;
273             }
274             mOffset += size;
275         }
276     }
277 
278     uint8_t header;
279     ssize_t n = mDataSource->readAt(mOffset, &header, 1);
280 
281     if (n < 1) {
282         return ERROR_END_OF_STREAM;
283     }
284 
285     if (header & 0x83) {
286         // Padding bits must be 0.
287 
288         ALOGE("padding bits must be 0, header is 0x%02x", header);
289 
290         return ERROR_MALFORMED;
291     }
292 
293     unsigned FT = (header >> 3) & 0x0f;
294 
295     size_t frameSize = getFrameSize(mIsWide, FT);
296     if (frameSize == 0) {
297         return ERROR_MALFORMED;
298     }
299 
300     MediaBuffer *buffer;
301     status_t err = mGroup->acquire_buffer(&buffer);
302     if (err != OK) {
303         return err;
304     }
305 
306     n = mDataSource->readAt(mOffset, buffer->data(), frameSize);
307 
308     if (n != (ssize_t)frameSize) {
309         buffer->release();
310         buffer = NULL;
311 
312         return ERROR_IO;
313     }
314 
315     buffer->set_range(0, frameSize);
316     buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs);
317     buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
318 
319     mOffset += frameSize;
320     mCurrentTimeUs += 20000;  // Each frame is 20ms
321 
322     *out = buffer;
323 
324     return OK;
325 }
326 
327 ////////////////////////////////////////////////////////////////////////////////
328 
SniffAMR(const sp<DataSource> & source,String8 * mimeType,float * confidence,sp<AMessage> *)329 bool SniffAMR(
330         const sp<DataSource> &source, String8 *mimeType, float *confidence,
331         sp<AMessage> *) {
332     char header[9];
333 
334     if (source->readAt(0, header, sizeof(header)) != sizeof(header)) {
335         return false;
336     }
337 
338     if (!memcmp(header, "#!AMR\n", 6)) {
339         *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_NB;
340         *confidence = 0.5;
341 
342         return true;
343     } else if (!memcmp(header, "#!AMR-WB\n", 9)) {
344         *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_WB;
345         *confidence = 0.5;
346 
347         return true;
348     }
349 
350     return false;
351 }
352 
353 }  // namespace android
354