AMRExtractor.cpp revision 48c948b1137e7bbdb161b51908657ab72ac5e2da
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 <media/stagefright/AMRExtractor.h>
22#include <media/stagefright/DataSource.h>
23#include <media/stagefright/MediaBufferGroup.h>
24#include <media/stagefright/MediaDebug.h>
25#include <media/stagefright/MediaDefs.h>
26#include <media/stagefright/MediaErrors.h>
27#include <media/stagefright/MediaSource.h>
28#include <media/stagefright/MetaData.h>
29#include <utils/String8.h>
30
31namespace android {
32
33class AMRSource : public MediaSource {
34public:
35    AMRSource(const sp<DataSource> &source, bool isWide);
36
37    virtual status_t start(MetaData *params = NULL);
38    virtual status_t stop();
39
40    virtual sp<MetaData> getFormat();
41
42    virtual status_t read(
43            MediaBuffer **buffer, const ReadOptions *options = NULL);
44
45protected:
46    virtual ~AMRSource();
47
48private:
49    sp<DataSource> mDataSource;
50    bool mIsWide;
51
52    off_t mOffset;
53    int64_t mCurrentTimeUs;
54    bool mStarted;
55    MediaBufferGroup *mGroup;
56
57    AMRSource(const AMRSource &);
58    AMRSource &operator=(const AMRSource &);
59};
60
61////////////////////////////////////////////////////////////////////////////////
62
63AMRExtractor::AMRExtractor(const sp<DataSource> &source)
64    : mDataSource(source),
65      mInitCheck(NO_INIT) {
66    String8 mimeType;
67    float confidence;
68    if (SniffAMR(mDataSource, &mimeType, &confidence)) {
69        mInitCheck = OK;
70        mIsWide = (mimeType == MEDIA_MIMETYPE_AUDIO_AMR_WB);
71    }
72}
73
74AMRExtractor::~AMRExtractor() {
75}
76
77size_t AMRExtractor::countTracks() {
78    return mInitCheck == OK ? 1 : 0;
79}
80
81sp<MediaSource> AMRExtractor::getTrack(size_t index) {
82    if (mInitCheck != OK || index != 0) {
83        return NULL;
84    }
85
86    return new AMRSource(mDataSource, mIsWide);
87}
88
89sp<MetaData> AMRExtractor::getTrackMetaData(size_t index) {
90    if (mInitCheck != OK || index != 0) {
91        return NULL;
92    }
93
94    return makeAMRFormat(mIsWide);
95}
96
97// static
98sp<MetaData> AMRExtractor::makeAMRFormat(bool isWide) {
99    sp<MetaData> meta = new MetaData;
100    meta->setCString(
101            kKeyMIMEType, isWide ? MEDIA_MIMETYPE_AUDIO_AMR_WB
102                                 : MEDIA_MIMETYPE_AUDIO_AMR_NB);
103
104    meta->setInt32(kKeyChannelCount, 1);
105    meta->setInt32(kKeySampleRate, isWide ? 16000 : 8000);
106
107    return meta;
108}
109
110////////////////////////////////////////////////////////////////////////////////
111
112AMRSource::AMRSource(const sp<DataSource> &source, bool isWide)
113    : mDataSource(source),
114      mIsWide(isWide),
115      mOffset(mIsWide ? 9 : 6),
116      mCurrentTimeUs(0),
117      mStarted(false),
118      mGroup(NULL) {
119}
120
121AMRSource::~AMRSource() {
122    if (mStarted) {
123        stop();
124    }
125}
126
127status_t AMRSource::start(MetaData *params) {
128    CHECK(!mStarted);
129
130    mOffset = mIsWide ? 9 : 6;
131    mCurrentTimeUs = 0;
132    mGroup = new MediaBufferGroup;
133    mGroup->add_buffer(new MediaBuffer(128));
134    mStarted = true;
135
136    return OK;
137}
138
139status_t AMRSource::stop() {
140    CHECK(mStarted);
141
142    delete mGroup;
143    mGroup = NULL;
144
145    mStarted = false;
146    return OK;
147}
148
149sp<MetaData> AMRSource::getFormat() {
150    return AMRExtractor::makeAMRFormat(mIsWide);
151}
152
153status_t AMRSource::read(
154        MediaBuffer **out, const ReadOptions *options) {
155    *out = NULL;
156
157    uint8_t header;
158    ssize_t n = mDataSource->read_at(mOffset, &header, 1);
159
160    if (n < 1) {
161        return ERROR_IO;
162    }
163
164    MediaBuffer *buffer;
165    status_t err = mGroup->acquire_buffer(&buffer);
166    if (err != OK) {
167        return err;
168    }
169
170    if (header & 0x83) {
171        // Padding bits must be 0.
172
173        return ERROR_MALFORMED;
174    }
175
176    unsigned FT = (header >> 3) & 0x0f;
177
178    if (FT > 8 || (!mIsWide && FT > 7)) {
179        return ERROR_MALFORMED;
180    }
181
182    static const size_t kFrameSizeNB[8] = {
183        95, 103, 118, 134, 148, 159, 204, 244
184    };
185    static const size_t kFrameSizeWB[9] = {
186        132, 177, 253, 285, 317, 365, 397, 461, 477
187    };
188
189    size_t frameSize = mIsWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
190
191    // Round up bits to bytes and add 1 for the header byte.
192    frameSize = (frameSize + 7) / 8 + 1;
193
194    n = mDataSource->read_at(mOffset, buffer->data(), frameSize);
195
196    if (n != (ssize_t)frameSize) {
197        buffer->release();
198        buffer = NULL;
199
200        return ERROR_IO;
201    }
202
203    buffer->set_range(0, frameSize);
204    buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs);
205
206    mOffset += frameSize;
207    mCurrentTimeUs += 20000;  // Each frame is 20ms
208
209    *out = buffer;
210
211    return OK;
212}
213
214////////////////////////////////////////////////////////////////////////////////
215
216bool SniffAMR(
217        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
218    char header[9];
219
220    if (source->read_at(0, header, sizeof(header)) != sizeof(header)) {
221        return false;
222    }
223
224    if (!memcmp(header, "#!AMR\n", 6)) {
225        *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_NB;
226        *confidence = 0.5;
227
228        return true;
229    } else if (!memcmp(header, "#!AMR-WB\n", 9)) {
230        *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_WB;
231        *confidence = 0.5;
232
233        return true;
234    }
235
236    return false;
237}
238
239}  // namespace android
240