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