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 "AMRExtractor.h"
22
23#include <media/DataSourceBase.h>
24#include <media/MediaTrack.h>
25#include <media/stagefright/foundation/ADebug.h>
26#include <media/stagefright/MediaBufferGroup.h>
27#include <media/stagefright/MediaDefs.h>
28#include <media/stagefright/MediaErrors.h>
29#include <media/stagefright/MetaData.h>
30#include <utils/String8.h>
31
32namespace android {
33
34class AMRSource : public MediaTrack {
35public:
36    AMRSource(
37            DataSourceBase *source,
38            MetaDataBase &meta,
39            bool isWide,
40            const off64_t *offset_table,
41            size_t offset_table_length);
42
43    virtual status_t start(MetaDataBase *params = NULL);
44    virtual status_t stop();
45
46    virtual status_t getFormat(MetaDataBase &);
47
48    virtual status_t read(
49            MediaBufferBase **buffer, const ReadOptions *options = NULL);
50
51protected:
52    virtual ~AMRSource();
53
54private:
55    DataSourceBase *mDataSource;
56    MetaDataBase mMeta;
57    bool mIsWide;
58
59    off64_t mOffset;
60    int64_t mCurrentTimeUs;
61    bool mStarted;
62    MediaBufferGroup *mGroup;
63
64    off64_t mOffsetTable[OFFSET_TABLE_LEN];
65    size_t mOffsetTableLength;
66
67    AMRSource(const AMRSource &);
68    AMRSource &operator=(const AMRSource &);
69};
70
71////////////////////////////////////////////////////////////////////////////////
72
73static size_t getFrameSize(bool isWide, unsigned FT) {
74    static const size_t kFrameSizeNB[16] = {
75        95, 103, 118, 134, 148, 159, 204, 244,
76        39, 43, 38, 37, // SID
77        0, 0, 0, // future use
78        0 // no data
79    };
80    static const size_t kFrameSizeWB[16] = {
81        132, 177, 253, 285, 317, 365, 397, 461, 477,
82        40, // SID
83        0, 0, 0, 0, // future use
84        0, // speech lost
85        0 // no data
86    };
87
88    if (FT > 15 || (isWide && FT > 9 && FT < 14) || (!isWide && FT > 11 && FT < 15)) {
89        ALOGE("illegal AMR frame type %d", FT);
90        return 0;
91    }
92
93    size_t frameSize = isWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
94
95    // Round up bits to bytes and add 1 for the header byte.
96    frameSize = (frameSize + 7) / 8 + 1;
97
98    return frameSize;
99}
100
101static status_t getFrameSizeByOffset(DataSourceBase *source,
102        off64_t offset, bool isWide, size_t *frameSize) {
103    uint8_t header;
104    ssize_t count = source->readAt(offset, &header, 1);
105    if (count == 0) {
106        return ERROR_END_OF_STREAM;
107    } else if (count < 0) {
108        return ERROR_IO;
109    }
110
111    unsigned FT = (header >> 3) & 0x0f;
112
113    *frameSize = getFrameSize(isWide, FT);
114    if (*frameSize == 0) {
115        return ERROR_MALFORMED;
116    }
117    return OK;
118}
119
120static bool SniffAMR(
121        DataSourceBase *source, bool *isWide, float *confidence) {
122    char header[9];
123
124    if (source->readAt(0, header, sizeof(header)) != sizeof(header)) {
125        return false;
126    }
127
128    if (!memcmp(header, "#!AMR\n", 6)) {
129        if (isWide != nullptr) {
130            *isWide = false;
131        }
132        *confidence = 0.5;
133
134        return true;
135    } else if (!memcmp(header, "#!AMR-WB\n", 9)) {
136        if (isWide != nullptr) {
137            *isWide = true;
138        }
139        *confidence = 0.5;
140
141        return true;
142    }
143
144    return false;
145}
146
147AMRExtractor::AMRExtractor(DataSourceBase *source)
148    : mDataSource(source),
149      mInitCheck(NO_INIT),
150      mOffsetTableLength(0) {
151    float confidence;
152    if (!SniffAMR(mDataSource, &mIsWide, &confidence)) {
153        return;
154    }
155
156    mMeta.setCString(
157            kKeyMIMEType, mIsWide ? MEDIA_MIMETYPE_AUDIO_AMR_WB
158                                  : MEDIA_MIMETYPE_AUDIO_AMR_NB);
159
160    mMeta.setInt32(kKeyChannelCount, 1);
161    mMeta.setInt32(kKeySampleRate, mIsWide ? 16000 : 8000);
162
163    off64_t offset = mIsWide ? 9 : 6;
164    off64_t streamSize;
165    size_t frameSize, numFrames = 0;
166    int64_t duration = 0;
167
168    if (mDataSource->getSize(&streamSize) == OK) {
169         while (offset < streamSize) {
170             status_t status = getFrameSizeByOffset(source, offset, mIsWide, &frameSize);
171             if (status == ERROR_END_OF_STREAM) {
172                 break;
173             } else if (status != OK) {
174                return;
175            }
176
177            if ((numFrames % 50 == 0) && (numFrames / 50 < OFFSET_TABLE_LEN)) {
178                CHECK_EQ(mOffsetTableLength, numFrames / 50);
179                mOffsetTable[mOffsetTableLength] = offset - (mIsWide ? 9: 6);
180                mOffsetTableLength ++;
181            }
182
183            offset += frameSize;
184            duration += 20000;  // Each frame is 20ms
185            numFrames ++;
186        }
187
188        mMeta.setInt64(kKeyDuration, duration);
189    }
190
191    mInitCheck = OK;
192}
193
194AMRExtractor::~AMRExtractor() {
195}
196
197status_t AMRExtractor::getMetaData(MetaDataBase &meta) {
198    meta.clear();
199
200    if (mInitCheck == OK) {
201        meta.setCString(kKeyMIMEType, mIsWide ? "audio/amr-wb" : "audio/amr");
202    }
203
204    return OK;
205}
206
207size_t AMRExtractor::countTracks() {
208    return mInitCheck == OK ? 1 : 0;
209}
210
211MediaTrack *AMRExtractor::getTrack(size_t index) {
212    if (mInitCheck != OK || index != 0) {
213        return NULL;
214    }
215
216    return new AMRSource(mDataSource, mMeta, mIsWide,
217            mOffsetTable, mOffsetTableLength);
218}
219
220status_t AMRExtractor::getTrackMetaData(MetaDataBase &meta, size_t index, uint32_t /* flags */) {
221    if (mInitCheck != OK || index != 0) {
222        return UNKNOWN_ERROR;
223    }
224
225    meta = mMeta;
226    return OK;
227}
228
229////////////////////////////////////////////////////////////////////////////////
230
231AMRSource::AMRSource(
232        DataSourceBase *source, MetaDataBase &meta,
233        bool isWide, const off64_t *offset_table, size_t offset_table_length)
234    : mDataSource(source),
235      mMeta(meta),
236      mIsWide(isWide),
237      mOffset(mIsWide ? 9 : 6),
238      mCurrentTimeUs(0),
239      mStarted(false),
240      mGroup(NULL),
241      mOffsetTableLength(offset_table_length) {
242    if (mOffsetTableLength > 0 && mOffsetTableLength <= OFFSET_TABLE_LEN) {
243        memcpy ((char*)mOffsetTable, (char*)offset_table, sizeof(off64_t) * mOffsetTableLength);
244    }
245}
246
247AMRSource::~AMRSource() {
248    if (mStarted) {
249        stop();
250    }
251}
252
253status_t AMRSource::start(MetaDataBase * /* params */) {
254    CHECK(!mStarted);
255
256    mOffset = mIsWide ? 9 : 6;
257    mCurrentTimeUs = 0;
258    mGroup = new MediaBufferGroup;
259    mGroup->add_buffer(MediaBufferBase::Create(128));
260    mStarted = true;
261
262    return OK;
263}
264
265status_t AMRSource::stop() {
266    CHECK(mStarted);
267
268    delete mGroup;
269    mGroup = NULL;
270
271    mStarted = false;
272    return OK;
273}
274
275status_t AMRSource::getFormat(MetaDataBase &meta) {
276    meta = mMeta;
277    return OK;
278}
279
280status_t AMRSource::read(
281        MediaBufferBase **out, const ReadOptions *options) {
282    *out = NULL;
283
284    int64_t seekTimeUs;
285    ReadOptions::SeekMode mode;
286    if (mOffsetTableLength > 0 && options && options->getSeekTo(&seekTimeUs, &mode)) {
287        size_t size;
288        int64_t seekFrame = seekTimeUs / 20000ll;  // 20ms per frame.
289        mCurrentTimeUs = seekFrame * 20000ll;
290
291        size_t index = seekFrame < 0 ? 0 : seekFrame / 50;
292        if (index >= mOffsetTableLength) {
293            index = mOffsetTableLength - 1;
294        }
295
296        mOffset = mOffsetTable[index] + (mIsWide ? 9 : 6);
297
298        for (size_t i = 0; i< seekFrame - index * 50; i++) {
299            status_t err;
300            if ((err = getFrameSizeByOffset(mDataSource, mOffset,
301                            mIsWide, &size)) != OK) {
302                return err;
303            }
304            mOffset += size;
305        }
306    }
307
308    uint8_t header;
309    ssize_t n = mDataSource->readAt(mOffset, &header, 1);
310
311    if (n < 1) {
312        return ERROR_END_OF_STREAM;
313    }
314
315    if (header & 0x83) {
316        // Padding bits must be 0.
317
318        ALOGE("padding bits must be 0, header is 0x%02x", header);
319
320        return ERROR_MALFORMED;
321    }
322
323    unsigned FT = (header >> 3) & 0x0f;
324
325    size_t frameSize = getFrameSize(mIsWide, FT);
326    if (frameSize == 0) {
327        return ERROR_MALFORMED;
328    }
329
330    MediaBufferBase *buffer;
331    status_t err = mGroup->acquire_buffer(&buffer);
332    if (err != OK) {
333        return err;
334    }
335
336    n = mDataSource->readAt(mOffset, buffer->data(), frameSize);
337
338    if (n != (ssize_t)frameSize) {
339        buffer->release();
340        buffer = NULL;
341
342        if (n < 0) {
343            return ERROR_IO;
344        } else {
345            // only partial frame is available, treat it as EOS.
346            mOffset += n;
347            return ERROR_END_OF_STREAM;
348        }
349    }
350
351    buffer->set_range(0, frameSize);
352    buffer->meta_data().setInt64(kKeyTime, mCurrentTimeUs);
353    buffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
354
355    mOffset += frameSize;
356    mCurrentTimeUs += 20000;  // Each frame is 20ms
357
358    *out = buffer;
359
360    return OK;
361}
362
363////////////////////////////////////////////////////////////////////////////////
364
365extern "C" {
366// This is the only symbol that needs to be exported
367__attribute__ ((visibility ("default")))
368MediaExtractor::ExtractorDef GETEXTRACTORDEF() {
369    return {
370        MediaExtractor::EXTRACTORDEF_VERSION,
371        UUID("c86639c9-2f31-40ac-a715-fa01b4493aaf"),
372        1,
373        "AMR Extractor",
374        [](
375                DataSourceBase *source,
376                float *confidence,
377                void **,
378                MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc {
379            if (SniffAMR(source, nullptr, confidence)) {
380                return [](
381                        DataSourceBase *source,
382                        void *) -> MediaExtractor* {
383                    return new AMRExtractor(source);};
384            }
385            return NULL;
386        }
387    };
388}
389
390} // extern "C"
391
392}  // namespace android
393