AudioSource.cpp revision 4f998cdef97b9c027f145b2da5c48278e19c3d33
1e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber/* 2e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber * Copyright (C) 2010 The Android Open Source Project 3e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber * 4e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber * Licensed under the Apache License, Version 2.0 (the "License"); 5e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber * you may not use this file except in compliance with the License. 6e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber * You may obtain a copy of the License at 7e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber * 8e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber * http://www.apache.org/licenses/LICENSE-2.0 9e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber * 10e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber * Unless required by applicable law or agreed to in writing, software 11e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber * distributed under the License is distributed on an "AS IS" BASIS, 12e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber * See the License for the specific language governing permissions and 14e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber * limitations under the License. 15e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber */ 16e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 17a5750e0dad9e90f2195ce36f2c4457fa04b2b83eMark Salyzyn#include <inttypes.h> 18a5750e0dad9e90f2195ce36f2c4457fa04b2b83eMark Salyzyn#include <stdlib.h> 19a5750e0dad9e90f2195ce36f2c4457fa04b2b83eMark Salyzyn 20050b28a593350047845a45a14cc5026221ac1620James Dong//#define LOG_NDEBUG 0 21050b28a593350047845a45a14cc5026221ac1620James Dong#define LOG_TAG "AudioSource" 22050b28a593350047845a45a14cc5026221ac1620James Dong#include <utils/Log.h> 23050b28a593350047845a45a14cc5026221ac1620James Dong 24e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber#include <media/AudioRecord.h> 256b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong#include <media/stagefright/AudioSource.h> 266b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong#include <media/stagefright/MediaBuffer.h> 27e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber#include <media/stagefright/MediaDefs.h> 28e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber#include <media/stagefright/MetaData.h> 296b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong#include <media/stagefright/foundation/ADebug.h> 30082830f92373a1b9e512dbbfb940187ffa1c2c6fAndreas Huber#include <media/stagefright/foundation/ALooper.h> 31365a963142093a1cd8efdcea76b5f65096a5b115James Dong#include <cutils/properties.h> 32e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 33e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Hubernamespace android { 34e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 356b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dongstatic void AudioRecordCallbackFunction(int event, void *user, void *info) { 366b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong AudioSource *source = (AudioSource *) user; 376b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong switch (event) { 386b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong case AudioRecord::EVENT_MORE_DATA: { 39082830f92373a1b9e512dbbfb940187ffa1c2c6fAndreas Huber source->dataCallback(*((AudioRecord::Buffer *) info)); 406b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong break; 416b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong } 426b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong case AudioRecord::EVENT_OVERRUN: { 435ff1dd576bb93c45b44088a51544a18fc43ebf58Steve Block ALOGW("AudioRecord reported overrun!"); 446b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong break; 456b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong } 466b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong default: 476b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong // does nothing 486b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong break; 496b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong } 506b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong} 516b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong 52e7c9cb48fec02697227bd847cd2e69432659adfdAndreas HuberAudioSource::AudioSource( 5346d26dd29195450db15704e84d65740628a821fbChong Zhang audio_source_t inputSource, const String16 &opPackageName, 54b2379ba0a32638bae2ea0460644f68cf5a0967ceEric Laurent uint32_t sampleRate, uint32_t channelCount, uint32_t outSampleRate, 55b2379ba0a32638bae2ea0460644f68cf5a0967ceEric Laurent uid_t uid, pid_t pid) 56e2ffd5b583da9d30d96710b0e8879e90b2b51d30Glenn Kasten : mStarted(false), 576b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong mSampleRate(sampleRate), 5846d26dd29195450db15704e84d65740628a821fbChong Zhang mOutSampleRate(outSampleRate > 0 ? outSampleRate : sampleRate), 59f88d1d8e63442d09303ca1090e1ee12e22040500Marco Nelissen mTrackMaxAmplitude(false), 60f88d1d8e63442d09303ca1090e1ee12e22040500Marco Nelissen mStartTimeUs(0), 61f88d1d8e63442d09303ca1090e1ee12e22040500Marco Nelissen mMaxAmplitude(0), 6246292fb347d72a314d985e34e5e3743d846cb9b6James Dong mPrevSampleTimeUs(0), 63f88d1d8e63442d09303ca1090e1ee12e22040500Marco Nelissen mInitialReadTimeUs(0), 646b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong mNumFramesReceived(0), 654f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang mNumFramesSkipped(0), 664f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang mNumFramesLost(0), 67af5dd7753e62353411cf0daf3b513c38818e9662Andreas Huber mNumClientOwnedBuffers(0) { 6846d26dd29195450db15704e84d65740628a821fbChong Zhang ALOGV("sampleRate: %u, outSampleRate: %u, channelCount: %u", 6946d26dd29195450db15704e84d65740628a821fbChong Zhang sampleRate, outSampleRate, channelCount); 70ab334fd351ae5a0e18903da123d63e565b536874Glenn Kasten CHECK(channelCount == 1 || channelCount == 2); 7146d26dd29195450db15704e84d65740628a821fbChong Zhang CHECK(sampleRate > 0); 72be6ec71af2d12e2a55f2f0b1b77d3fa5d593a1c7James Dong 73e33054eb968cbf8ccaee1b0ff0301403902deed6Glenn Kasten size_t minFrameCount; 74e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent status_t status = AudioRecord::getMinFrameCount(&minFrameCount, 75e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent sampleRate, 76e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent AUDIO_FORMAT_PCM_16_BIT, 77dd8104cc5367262f0e5f13df4e79f131e8d560bbGlenn Kasten audio_channel_in_mask_from_count(channelCount)); 78e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent if (status == OK) { 79e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent // make sure that the AudioRecord callback never returns more than the maximum 80e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent // buffer size 81838b3d8bafa4a781e277870dee4e0390165cff52Glenn Kasten uint32_t frameCount = kMaxBufferSize / sizeof(int16_t) / channelCount; 82e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent 83e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent // make sure that the AudioRecord total buffer size is large enough 8484333e0475bc911adc16417f4ca327c975cf6c36Andreas Huber size_t bufCount = 2; 85e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent while ((bufCount * frameCount) < minFrameCount) { 86e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent bufCount++; 87e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent } 88e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent 89e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent mRecord = new AudioRecord( 90e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent inputSource, sampleRate, AUDIO_FORMAT_PCM_16_BIT, 91e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent audio_channel_in_mask_from_count(channelCount), 92be71aa29a3c86d2e01cd17839d2a72ab09a1bce5Svet Ganov opPackageName, 93bce50bfc3846ab008bafa75c5d3f29fd7b5395f7Glenn Kasten (size_t) (bufCount * frameCount), 94e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent AudioRecordCallbackFunction, 95e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent this, 96b2379ba0a32638bae2ea0460644f68cf5a0967ceEric Laurent frameCount /*notificationFrames*/, 97b2379ba0a32638bae2ea0460644f68cf5a0967ceEric Laurent AUDIO_SESSION_ALLOCATE, 98b2379ba0a32638bae2ea0460644f68cf5a0967ceEric Laurent AudioRecord::TRANSFER_DEFAULT, 99b2379ba0a32638bae2ea0460644f68cf5a0967ceEric Laurent AUDIO_INPUT_FLAG_NONE, 100b2379ba0a32638bae2ea0460644f68cf5a0967ceEric Laurent uid, 101b2379ba0a32638bae2ea0460644f68cf5a0967ceEric Laurent pid); 102e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent mInitCheck = mRecord->initCheck(); 1033e98ecd18c906dc3ac2ff1a890f0b3163447272dGlenn Kasten if (mInitCheck != OK) { 1043e98ecd18c906dc3ac2ff1a890f0b3163447272dGlenn Kasten mRecord.clear(); 1053e98ecd18c906dc3ac2ff1a890f0b3163447272dGlenn Kasten } 106e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent } else { 107e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent mInitCheck = status; 108e49f2b424318aa8e830e7a1338e5e32ab82992f9Eric Laurent } 109e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber} 110e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 111e7c9cb48fec02697227bd847cd2e69432659adfdAndreas HuberAudioSource::~AudioSource() { 112e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber if (mStarted) { 113b44c9d2bdc0d5b9cb03254022a58e017b516e9e6James Dong reset(); 114e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber } 115e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber} 116e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 117e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huberstatus_t AudioSource::initCheck() const { 118e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber return mInitCheck; 119e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber} 120e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 121e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huberstatus_t AudioSource::start(MetaData *params) { 1226b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong Mutex::Autolock autoLock(mLock); 123e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber if (mStarted) { 124e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber return UNKNOWN_ERROR; 125e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber } 126e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 1276e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong if (mInitCheck != OK) { 1286e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong return NO_INIT; 1296e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong } 1306e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong 131d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong mTrackMaxAmplitude = false; 132d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong mMaxAmplitude = 0; 133d707fcb3e29707ca4a5935c294ef0b38eb5aba5fJames Dong mInitialReadTimeUs = 0; 134f60cafe0e6aad8f9ce54660fa88b651ae4e749e6James Dong mStartTimeUs = 0; 135f60cafe0e6aad8f9ce54660fa88b651ae4e749e6James Dong int64_t startTimeUs; 136f60cafe0e6aad8f9ce54660fa88b651ae4e749e6James Dong if (params && params->findInt64(kKeyTime, &startTimeUs)) { 137f60cafe0e6aad8f9ce54660fa88b651ae4e749e6James Dong mStartTimeUs = startTimeUs; 138f60cafe0e6aad8f9ce54660fa88b651ae4e749e6James Dong } 139e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber status_t err = mRecord->start(); 140e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber if (err == OK) { 141e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber mStarted = true; 142eaae38445a340c4857c1c5569475879a728e63b7James Dong } else { 143e2ffd5b583da9d30d96710b0e8879e90b2b51d30Glenn Kasten mRecord.clear(); 144e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber } 145e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 146eaae38445a340c4857c1c5569475879a728e63b7James Dong 147e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber return err; 148e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber} 149e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 1506b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dongvoid AudioSource::releaseQueuedFrames_l() { 1513856b090cd04ba5dd4a59a12430ed724d5995909Steve Block ALOGV("releaseQueuedFrames_l"); 1526b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong List<MediaBuffer *>::iterator it; 1536b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong while (!mBuffersReceived.empty()) { 1546b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong it = mBuffersReceived.begin(); 1556b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong (*it)->release(); 1566b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong mBuffersReceived.erase(it); 1576b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong } 1586b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong} 1596b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong 1606b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dongvoid AudioSource::waitOutstandingEncodingFrames_l() { 161a5750e0dad9e90f2195ce36f2c4457fa04b2b83eMark Salyzyn ALOGV("waitOutstandingEncodingFrames_l: %" PRId64, mNumClientOwnedBuffers); 1626b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong while (mNumClientOwnedBuffers > 0) { 1636b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong mFrameEncodingCompletionCondition.wait(mLock); 1646b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong } 1656b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong} 1666b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong 167b44c9d2bdc0d5b9cb03254022a58e017b516e9e6James Dongstatus_t AudioSource::reset() { 1686b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong Mutex::Autolock autoLock(mLock); 169e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber if (!mStarted) { 170e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber return UNKNOWN_ERROR; 171e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber } 172e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 1736e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong if (mInitCheck != OK) { 1746e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong return NO_INIT; 1756e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong } 1766e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong 177e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber mStarted = false; 17816e79115e497386eaf010af388627f94314a55a3Chong Zhang mFrameAvailableCondition.signal(); 17916e79115e497386eaf010af388627f94314a55a3Chong Zhang 1806b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong mRecord->stop(); 1816b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong waitOutstandingEncodingFrames_l(); 1826b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong releaseQueuedFrames_l(); 183365a963142093a1cd8efdcea76b5f65096a5b115James Dong 184e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber return OK; 185e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber} 186e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 187e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Hubersp<MetaData> AudioSource::getFormat() { 1886b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong Mutex::Autolock autoLock(mLock); 1896e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong if (mInitCheck != OK) { 1906e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong return 0; 1916e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong } 1926e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong 193e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber sp<MetaData> meta = new MetaData; 194e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); 1956b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong meta->setInt32(kKeySampleRate, mSampleRate); 196e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber meta->setInt32(kKeyChannelCount, mRecord->channelCount()); 197e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber meta->setInt32(kKeyMaxInputSize, kMaxBufferSize); 19878bd91b15ee8ea5aa2ab5a8cad7e892cb2d01c1bLajos Molnar meta->setInt32(kKeyPcmEncoding, kAudioEncodingPcm16bit); 199e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 200e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber return meta; 201e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber} 202e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 203f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dongvoid AudioSource::rampVolume( 204f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong int32_t startFrame, int32_t rampDurationFrames, 205f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong uint8_t *data, size_t bytes) { 206f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong 207f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong const int32_t kShift = 14; 208f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong int32_t fixedMultiplier = (startFrame << kShift) / rampDurationFrames; 209f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong const int32_t nChannels = mRecord->channelCount(); 210f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong int32_t stopFrame = startFrame + bytes / sizeof(int16_t); 211f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong int16_t *frame = (int16_t *) data; 212f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong if (stopFrame > rampDurationFrames) { 213f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong stopFrame = rampDurationFrames; 214f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong } 215f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong 216f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong while (startFrame < stopFrame) { 217f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong if (nChannels == 1) { // mono 218f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong frame[0] = (frame[0] * fixedMultiplier) >> kShift; 219f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong ++frame; 220f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong ++startFrame; 221f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong } else { // stereo 222f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong frame[0] = (frame[0] * fixedMultiplier) >> kShift; 223f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong frame[1] = (frame[1] * fixedMultiplier) >> kShift; 224f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong frame += 2; 225f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong startFrame += 2; 226f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong } 227f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong 228f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong // Update the multiplier every 4 frames 229f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong if ((startFrame & 3) == 0) { 230f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong fixedMultiplier = (startFrame << kShift) / rampDurationFrames; 231f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong } 232f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong } 233f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong} 234f1ae1963f5028a670573b50a9c1cfb504fc426b4James Dong 235e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huberstatus_t AudioSource::read( 23684333e0475bc911adc16417f4ca327c975cf6c36Andreas Huber MediaBuffer **out, const ReadOptions * /* options */) { 2376b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong Mutex::Autolock autoLock(mLock); 2386b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong *out = NULL; 2396e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong 2406e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong if (mInitCheck != OK) { 2416e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong return NO_INIT; 2426e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong } 2436e20bdf799a6f4efa6c42121a958634ea32ed5ccJames Dong 2446b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong while (mStarted && mBuffersReceived.empty()) { 2456b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong mFrameAvailableCondition.wait(mLock); 2466b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong } 2476b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong if (!mStarted) { 2486b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong return OK; 2496b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong } 2506b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong MediaBuffer *buffer = *mBuffersReceived.begin(); 2516b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong mBuffersReceived.erase(mBuffersReceived.begin()); 2526b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong ++mNumClientOwnedBuffers; 2536b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong buffer->setObserver(this); 2546b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong buffer->add_ref(); 2556b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong 2566b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong // Mute/suppress the recording sound 2576b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong int64_t timeUs; 2586b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs)); 2596b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong int64_t elapsedTimeUs = timeUs - mStartTimeUs; 2606b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong if (elapsedTimeUs < kAutoRampStartUs) { 2616b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong memset((uint8_t *) buffer->data(), 0, buffer->range_length()); 2626b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong } else if (elapsedTimeUs < kAutoRampStartUs + kAutoRampDurationUs) { 2636b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong int32_t autoRampDurationFrames = 264b65473f4f881ee7c0a24217ceac69514f6c127d5Vineeta Srivastava ((int64_t)kAutoRampDurationUs * mSampleRate + 500000LL) / 1000000LL; //Need type casting 2656b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong 2666b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong int32_t autoRampStartFrames = 267b65473f4f881ee7c0a24217ceac69514f6c127d5Vineeta Srivastava ((int64_t)kAutoRampStartUs * mSampleRate + 500000LL) / 1000000LL; //Need type casting 2686b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong 2696b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong int32_t nFrames = mNumFramesReceived - autoRampStartFrames; 2706b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong rampVolume(nFrames, autoRampDurationFrames, 2716b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong (uint8_t *) buffer->data(), buffer->range_length()); 2726b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong } 273542db5d438988360d491a5add1040a2df9aa90c9James Dong 2746b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong // Track the max recording signal amplitude. 2756b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong if (mTrackMaxAmplitude) { 2766b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong trackMaxAmplitude( 2776b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong (int16_t *) buffer->data(), buffer->range_length() >> 1); 2786b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong } 279542db5d438988360d491a5add1040a2df9aa90c9James Dong 28046d26dd29195450db15704e84d65740628a821fbChong Zhang if (mSampleRate != mOutSampleRate) { 281bd83e4b3e77b31e089832bcfbebde086392216c9Hangyu Kuang timeUs *= (int64_t)mSampleRate / (int64_t)mOutSampleRate; 282bd83e4b3e77b31e089832bcfbebde086392216c9Hangyu Kuang buffer->meta_data()->setInt64(kKeyTime, timeUs); 28346d26dd29195450db15704e84d65740628a821fbChong Zhang } 28446d26dd29195450db15704e84d65740628a821fbChong Zhang 2856b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong *out = buffer; 2866b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong return OK; 2876b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong} 288542db5d438988360d491a5add1040a2df9aa90c9James Dong 2896b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dongvoid AudioSource::signalBufferReturned(MediaBuffer *buffer) { 2903856b090cd04ba5dd4a59a12430ed724d5995909Steve Block ALOGV("signalBufferReturned: %p", buffer->data()); 2916b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong Mutex::Autolock autoLock(mLock); 2926b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong --mNumClientOwnedBuffers; 2936b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong buffer->setObserver(0); 2946b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong buffer->release(); 2956b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong mFrameEncodingCompletionCondition.signal(); 2966b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong return; 2976b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong} 29846292fb347d72a314d985e34e5e3743d846cb9b6James Dong 299082830f92373a1b9e512dbbfb940187ffa1c2c6fAndreas Huberstatus_t AudioSource::dataCallback(const AudioRecord::Buffer& audioBuffer) { 3004f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang int64_t timeUs, position, timeNs; 3014f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang ExtendedTimestamp ts; 3024f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang ExtendedTimestamp::Location location; 3034f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang const int32_t usPerSec = 1000000; 3044f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang 3054f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang if (mRecord->getTimestamp(&ts) == OK && 3064f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang ts.getBestTimestamp(&position, &timeNs, ExtendedTimestamp::TIMEBASE_MONOTONIC, 3074f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang &location) == OK) { 3084f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang // Use audio timestamp. 3094f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang timeUs = timeNs / 1000 - 3104f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang (position - mNumFramesSkipped - 3114f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang mNumFramesReceived + mNumFramesLost) * usPerSec / mSampleRate; 3124f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang } else { 3134f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang // This should not happen in normal case. 3144f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang ALOGW("Failed to get audio timestamp, fallback to use systemclock"); 3154f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang timeUs = systemTime() / 1000ll; 3164f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang // Estimate the real sampling time of the 1st sample in this buffer 3174f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang // from AudioRecord's latency. (Apply this adjustment first so that 3184f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang // the start time logic is not affected.) 3194f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang timeUs -= mRecord->latency() * 1000LL; 3204f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang } 321082830f92373a1b9e512dbbfb940187ffa1c2c6fAndreas Huber 322a5750e0dad9e90f2195ce36f2c4457fa04b2b83eMark Salyzyn ALOGV("dataCallbackTimestamp: %" PRId64 " us", timeUs); 3236b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong Mutex::Autolock autoLock(mLock); 3246b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong if (!mStarted) { 3255ff1dd576bb93c45b44088a51544a18fc43ebf58Steve Block ALOGW("Spurious callback from AudioRecord. Drop the audio data."); 3266b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong return OK; 3276b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong } 32846292fb347d72a314d985e34e5e3743d846cb9b6James Dong 3294f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang const size_t bufferSize = audioBuffer.size; 3304f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang 331a472613aec322e25891abf5c77bf3f7e3c244920James Dong // Drop retrieved and previously lost audio data. 332a472613aec322e25891abf5c77bf3f7e3c244920James Dong if (mNumFramesReceived == 0 && timeUs < mStartTimeUs) { 3335f972c031d4061f4f037c9fda1ea4bd9b6a756cdGlenn Kasten (void) mRecord->getInputFramesLost(); 3344f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang int64_t receievedFrames = bufferSize / mRecord->frameSize(); 3354f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang ALOGV("Drop audio data(%" PRId64 " frames) at %" PRId64 "/%" PRId64 " us", 3364f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang receievedFrames, timeUs, mStartTimeUs); 3374f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang mNumFramesSkipped += receievedFrames; 338a472613aec322e25891abf5c77bf3f7e3c244920James Dong return OK; 339a472613aec322e25891abf5c77bf3f7e3c244920James Dong } 340a472613aec322e25891abf5c77bf3f7e3c244920James Dong 3416b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong if (mNumFramesReceived == 0 && mPrevSampleTimeUs == 0) { 3426b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong mInitialReadTimeUs = timeUs; 3436b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong // Initial delay 344af5dd7753e62353411cf0daf3b513c38818e9662Andreas Huber if (mStartTimeUs > 0) { 3456b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong mStartTimeUs = timeUs - mStartTimeUs; 346f60cafe0e6aad8f9ce54660fa88b651ae4e749e6James Dong } 3476b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong mPrevSampleTimeUs = mStartTimeUs; 3486b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong } 349e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 350a472613aec322e25891abf5c77bf3f7e3c244920James Dong size_t numLostBytes = 0; 351a472613aec322e25891abf5c77bf3f7e3c244920James Dong if (mNumFramesReceived > 0) { // Ignore earlier frame lost 352a472613aec322e25891abf5c77bf3f7e3c244920James Dong // getInputFramesLost() returns the number of lost frames. 353a472613aec322e25891abf5c77bf3f7e3c244920James Dong // Convert number of frames lost to number of bytes lost. 354a472613aec322e25891abf5c77bf3f7e3c244920James Dong numLostBytes = mRecord->getInputFramesLost() * mRecord->frameSize(); 355a472613aec322e25891abf5c77bf3f7e3c244920James Dong } 356a472613aec322e25891abf5c77bf3f7e3c244920James Dong 3576b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong CHECK_EQ(numLostBytes & 1, 0u); 3586b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong CHECK_EQ(audioBuffer.size & 1, 0u); 3596b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong if (numLostBytes > 0) { 360b575ddce78d266fa218006f90306158dda5c8f56James Dong // Loss of audio frames should happen rarely; thus the LOGW should 361b575ddce78d266fa218006f90306158dda5c8f56James Dong // not cause a logging spam 362377b2ec9a2885f9b6405b07ba900a9e3f4349c38Kévin PETIT ALOGW("Lost audio record data: %zu bytes", numLostBytes); 363b575ddce78d266fa218006f90306158dda5c8f56James Dong } 364b575ddce78d266fa218006f90306158dda5c8f56James Dong 365b575ddce78d266fa218006f90306158dda5c8f56James Dong while (numLostBytes > 0) { 366b575ddce78d266fa218006f90306158dda5c8f56James Dong size_t bufferSize = numLostBytes; 367b575ddce78d266fa218006f90306158dda5c8f56James Dong if (numLostBytes > kMaxBufferSize) { 368b575ddce78d266fa218006f90306158dda5c8f56James Dong numLostBytes -= kMaxBufferSize; 369b575ddce78d266fa218006f90306158dda5c8f56James Dong bufferSize = kMaxBufferSize; 370b575ddce78d266fa218006f90306158dda5c8f56James Dong } else { 371b575ddce78d266fa218006f90306158dda5c8f56James Dong numLostBytes = 0; 372d707fcb3e29707ca4a5935c294ef0b38eb5aba5fJames Dong } 373b575ddce78d266fa218006f90306158dda5c8f56James Dong MediaBuffer *lostAudioBuffer = new MediaBuffer(bufferSize); 374b575ddce78d266fa218006f90306158dda5c8f56James Dong memset(lostAudioBuffer->data(), 0, bufferSize); 375b575ddce78d266fa218006f90306158dda5c8f56James Dong lostAudioBuffer->set_range(0, bufferSize); 3764f998cdef97b9c027f145b2da5c48278e19c3d33Hangyu Kuang mNumFramesLost += bufferSize / mRecord->frameSize(); 377b575ddce78d266fa218006f90306158dda5c8f56James Dong queueInputBuffer_l(lostAudioBuffer, timeUs); 378b575ddce78d266fa218006f90306158dda5c8f56James Dong } 379b575ddce78d266fa218006f90306158dda5c8f56James Dong 380b575ddce78d266fa218006f90306158dda5c8f56James Dong if (audioBuffer.size == 0) { 381b575ddce78d266fa218006f90306158dda5c8f56James Dong ALOGW("Nothing is available from AudioRecord callback buffer"); 382b575ddce78d266fa218006f90306158dda5c8f56James Dong return OK; 3836b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong } 3843c3763d2ee1cd1fba7fe522fbaf0faca315d8c2aJames Dong 385b575ddce78d266fa218006f90306158dda5c8f56James Dong MediaBuffer *buffer = new MediaBuffer(bufferSize); 386b575ddce78d266fa218006f90306158dda5c8f56James Dong memcpy((uint8_t *) buffer->data(), 387b575ddce78d266fa218006f90306158dda5c8f56James Dong audioBuffer.i16, audioBuffer.size); 3886b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong buffer->set_range(0, bufferSize); 389b575ddce78d266fa218006f90306158dda5c8f56James Dong queueInputBuffer_l(buffer, timeUs); 390b575ddce78d266fa218006f90306158dda5c8f56James Dong return OK; 391b575ddce78d266fa218006f90306158dda5c8f56James Dong} 392b575ddce78d266fa218006f90306158dda5c8f56James Dong 393b575ddce78d266fa218006f90306158dda5c8f56James Dongvoid AudioSource::queueInputBuffer_l(MediaBuffer *buffer, int64_t timeUs) { 394b575ddce78d266fa218006f90306158dda5c8f56James Dong const size_t bufferSize = buffer->range_length(); 395b575ddce78d266fa218006f90306158dda5c8f56James Dong const size_t frameSize = mRecord->frameSize(); 396b575ddce78d266fa218006f90306158dda5c8f56James Dong const int64_t timestampUs = 397b575ddce78d266fa218006f90306158dda5c8f56James Dong mPrevSampleTimeUs + 398b575ddce78d266fa218006f90306158dda5c8f56James Dong ((1000000LL * (bufferSize / frameSize)) + 399b575ddce78d266fa218006f90306158dda5c8f56James Dong (mSampleRate >> 1)) / mSampleRate; 400e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 4016b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong if (mNumFramesReceived == 0) { 4026b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong buffer->meta_data()->setInt64(kKeyAnchorTime, mStartTimeUs); 403542db5d438988360d491a5add1040a2df9aa90c9James Dong } 404b575ddce78d266fa218006f90306158dda5c8f56James Dong 4056b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs); 4066b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong buffer->meta_data()->setInt64(kKeyDriftTime, timeUs - mInitialReadTimeUs); 4076b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong mPrevSampleTimeUs = timestampUs; 408b575ddce78d266fa218006f90306158dda5c8f56James Dong mNumFramesReceived += bufferSize / frameSize; 4096b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong mBuffersReceived.push_back(buffer); 4106b61f4355db1974cd0f0dfaa4effdd7117b9f09bJames Dong mFrameAvailableCondition.signal(); 411e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber} 412e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber 413d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dongvoid AudioSource::trackMaxAmplitude(int16_t *data, int nSamples) { 414d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong for (int i = nSamples; i > 0; --i) { 415d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong int16_t value = *data++; 416d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong if (value < 0) { 417d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong value = -value; 418d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong } 419d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong if (mMaxAmplitude < value) { 420d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong mMaxAmplitude = value; 421d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong } 422d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong } 423d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong} 424d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong 425d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dongint16_t AudioSource::getMaxAmplitude() { 426d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong // First call activates the tracking. 427d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong if (!mTrackMaxAmplitude) { 428d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong mTrackMaxAmplitude = true; 429d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong } 430d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong int16_t value = mMaxAmplitude; 431d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong mMaxAmplitude = 0; 4323856b090cd04ba5dd4a59a12430ed724d5995909Steve Block ALOGV("max amplitude since last call: %d", value); 433d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong return value; 434d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong} 435d3d4e5069e1af0437c4f5a7b4ba344bda5b937afJames Dong 436e7c9cb48fec02697227bd847cd2e69432659adfdAndreas Huber} // namespace android 437