diff options
Diffstat (limited to 'media')
37 files changed, 2123 insertions, 93 deletions
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp index 51c8b68..bdb1a1c 100644 --- a/media/libeffects/visualizer/EffectVisualizer.cpp +++ b/media/libeffects/visualizer/EffectVisualizer.cpp @@ -21,6 +21,7 @@ #include <stdlib.h> #include <string.h> #include <new> +#include <time.h> #include <audio_effects/effect_visualizer.h> @@ -47,9 +48,9 @@ enum visualizer_state_e { VISUALIZER_STATE_ACTIVE, }; -// maximum number of reads from same buffer before resetting capture buffer. This means +// maximum time since last capture buffer update before resetting capture buffer. This means // that the framework has stopped playing audio and we must start returning silence -#define MAX_STALL_COUNT 10 +#define MAX_STALL_TIME_MS 1000 struct VisualizerContext { const struct effect_interface_s *mItfe; @@ -59,7 +60,7 @@ struct VisualizerContext { uint8_t mState; uint8_t mCurrentBuf; uint8_t mLastBuf; - uint8_t mStallCount; + struct timespec mBufferUpdateTime; uint8_t mCaptureBuf[2][VISUALIZER_CAPTURE_SIZE_MAX]; }; @@ -72,7 +73,7 @@ void Visualizer_reset(VisualizerContext *pContext) pContext->mCaptureIdx = 0; pContext->mCurrentBuf = 0; pContext->mLastBuf = 1; - pContext->mStallCount = 0; + pContext->mBufferUpdateTime.tv_sec = 0; memset(pContext->mCaptureBuf[0], 0x80, VISUALIZER_CAPTURE_SIZE_MAX); memset(pContext->mCaptureBuf[1], 0x80, VISUALIZER_CAPTURE_SIZE_MAX); } @@ -321,6 +322,11 @@ int Visualizer_process( if (pContext->mCaptureIdx == pContext->mCaptureSize) { pContext->mCurrentBuf ^= 1; pContext->mCaptureIdx = 0; + + // update last buffer update time stamp + if (clock_gettime(CLOCK_MONOTONIC, &pContext->mBufferUpdateTime) < 0) { + pContext->mBufferUpdateTime.tv_sec = 0; + } } if (inBuffer->raw != outBuffer->raw) { @@ -453,16 +459,25 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, pContext->mCaptureSize); // if audio framework has stopped playing audio although the effect is still // active we must clear the capture buffer to return silence - if (pContext->mLastBuf == pContext->mCurrentBuf) { - if (pContext->mStallCount < MAX_STALL_COUNT) { - if (++pContext->mStallCount == MAX_STALL_COUNT) { + if ((pContext->mLastBuf == pContext->mCurrentBuf) && + (pContext->mBufferUpdateTime.tv_sec != 0)) { + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + time_t secs = ts.tv_sec - pContext->mBufferUpdateTime.tv_sec; + long nsec = ts.tv_nsec - pContext->mBufferUpdateTime.tv_nsec; + if (nsec < 0) { + --secs; + nsec += 1000000000; + } + uint32_t deltaMs = secs * 1000 + nsec / 1000000; + if (deltaMs > MAX_STALL_TIME_MS) { + ALOGV("capture going to idle"); + pContext->mBufferUpdateTime.tv_sec = 0; memset(pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1], 0x80, pContext->mCaptureSize); } } - } else { - pContext->mStallCount = 0; } pContext->mLastBuf = pContext->mCurrentBuf; } else { diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 21e8f29..c8e1dc7 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -17,6 +17,7 @@ LOCAL_SRC_FILES:= \ IAudioFlingerClient.cpp \ IAudioTrack.cpp \ IAudioRecord.cpp \ + ICrypto.cpp \ AudioRecord.cpp \ AudioSystem.cpp \ mediaplayer.cpp \ @@ -43,7 +44,9 @@ LOCAL_SRC_FILES:= \ IEffectClient.cpp \ AudioEffect.cpp \ Visualizer.cpp \ - MemoryLeakTrackUtil.cpp + MemoryLeakTrackUtil.cpp \ + SoundPool.cpp \ + SoundPoolThread.cpp LOCAL_SHARED_LIBRARIES := \ libui libcutils libutils libbinder libsonivox libicuuc libexpat \ @@ -55,7 +58,6 @@ LOCAL_WHOLE_STATIC_LIBRARY := libmedia_helper LOCAL_MODULE:= libmedia LOCAL_C_INCLUDES := \ - $(JNI_H_INCLUDE) \ $(call include-path-for, graphics corecg) \ $(TOP)/frameworks/native/include/media/openmax \ external/icu4c/common \ diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 05ade75..70ec593 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -201,7 +201,7 @@ status_t AudioRecord::set( // create the IAudioRecord status = openRecord_l(sampleRate, format, channelMask, - frameCount, flags, input); + frameCount, input); if (status != NO_ERROR) { return status; } @@ -458,7 +458,6 @@ status_t AudioRecord::openRecord_l( audio_format_t format, uint32_t channelMask, int frameCount, - uint32_t flags, audio_io_handle_t input) { status_t status; @@ -471,7 +470,7 @@ status_t AudioRecord::openRecord_l( sampleRate, format, channelMask, frameCount, - ((uint16_t)flags) << 16, + IAudioFlinger::TRACK_DEFAULT, &mSessionId, &status); @@ -778,7 +777,7 @@ status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& cblk) // following member variables: mAudioRecord, mCblkMemory and mCblk. // It will also delete the strong references on previous IAudioRecord and IMemory result = openRecord_l(cblk->sampleRate, mFormat, mChannelMask, - mFrameCount, mFlags, getInput_l()); + mFrameCount, getInput_l()); if (result == NO_ERROR) { result = mAudioRecord->start(0); // callback thread hasn't changed } diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 50fbf36..bafde3a 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -803,16 +803,19 @@ status_t AudioTrack::createTrack_l( } } + IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT; + if (mIsTimed) { + trackFlags |= IAudioFlinger::TRACK_TIMED; + } sp<IAudioTrack> track = audioFlinger->createTrack(getpid(), streamType, sampleRate, format, channelMask, frameCount, - ((uint16_t)flags) << 16, + trackFlags, sharedBuffer, output, - mIsTimed, &mSessionId, &status); diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 07b12e4..ce10c8e 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -87,10 +87,9 @@ public: audio_format_t format, uint32_t channelMask, int frameCount, - uint32_t flags, + track_flags_t flags, const sp<IMemory>& sharedBuffer, audio_io_handle_t output, - bool isTimed, int *sessionId, status_t *status) { @@ -103,10 +102,9 @@ public: data.writeInt32(format); data.writeInt32(channelMask); data.writeInt32(frameCount); - data.writeInt32(flags); + data.writeInt32((int32_t) flags); data.writeStrongBinder(sharedBuffer->asBinder()); data.writeInt32((int32_t) output); - data.writeInt32(isTimed); int lSessionId = 0; if (sessionId != NULL) { lSessionId = *sessionId; @@ -136,7 +134,7 @@ public: audio_format_t format, uint32_t channelMask, int frameCount, - uint32_t flags, + track_flags_t flags, int *sessionId, status_t *status) { @@ -688,15 +686,14 @@ status_t BnAudioFlinger::onTransact( audio_format_t format = (audio_format_t) data.readInt32(); int channelCount = data.readInt32(); size_t bufferCount = data.readInt32(); - uint32_t flags = data.readInt32(); + track_flags_t flags = (track_flags_t) data.readInt32(); sp<IMemory> buffer = interface_cast<IMemory>(data.readStrongBinder()); audio_io_handle_t output = (audio_io_handle_t) data.readInt32(); - bool isTimed = data.readInt32(); int sessionId = data.readInt32(); status_t status; sp<IAudioTrack> track = createTrack(pid, (audio_stream_type_t) streamType, sampleRate, format, - channelCount, bufferCount, flags, buffer, output, isTimed, &sessionId, &status); + channelCount, bufferCount, flags, buffer, output, &sessionId, &status); reply->writeInt32(sessionId); reply->writeInt32(status); reply->writeStrongBinder(track->asBinder()); @@ -710,7 +707,7 @@ status_t BnAudioFlinger::onTransact( audio_format_t format = (audio_format_t) data.readInt32(); int channelCount = data.readInt32(); size_t bufferCount = data.readInt32(); - uint32_t flags = data.readInt32(); + track_flags_t flags = (track_flags_t) data.readInt32(); int sessionId = data.readInt32(); status_t status; sp<IAudioRecord> record = openRecord(pid, input, diff --git a/media/libmedia/ICrypto.cpp b/media/libmedia/ICrypto.cpp new file mode 100644 index 0000000..827d7af --- /dev/null +++ b/media/libmedia/ICrypto.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ICrypto" +#include <utils/Log.h> + +#include <binder/Parcel.h> +#include <media/ICrypto.h> +#include <media/stagefright/foundation/ADebug.h> + +namespace android { + +enum { + INITIALIZE = IBinder::FIRST_CALL_TRANSACTION, + TERMINATE, + SET_ENTITLEMENT_KEY, + SET_ECM, + DECRYPT_VIDEO, + DECRYPT_AUDIO, +}; + +struct BpCrypto : public BpInterface<ICrypto> { + BpCrypto(const sp<IBinder> &impl) + : BpInterface<ICrypto>(impl) { + } + + virtual status_t initialize() { + Parcel data, reply; + data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); + remote()->transact(INITIALIZE, data, &reply); + + return reply.readInt32(); + } + + virtual status_t terminate() { + Parcel data, reply; + data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); + remote()->transact(TERMINATE, data, &reply); + + return reply.readInt32(); + } + + virtual status_t setEntitlementKey( + const void *key, size_t keyLength) { + Parcel data, reply; + data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); + data.writeInt32(keyLength); + data.write(key, keyLength); + remote()->transact(SET_ENTITLEMENT_KEY, data, &reply); + + return reply.readInt32(); + } + + virtual status_t setEntitlementControlMessage( + const void *msg, size_t msgLength) { + Parcel data, reply; + data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); + data.writeInt32(msgLength); + data.write(msg, msgLength); + remote()->transact(SET_ECM, data, &reply); + + return reply.readInt32(); + } + + virtual ssize_t decryptVideo( + const void *iv, size_t ivLength, + const void *srcData, size_t srcDataSize, + void *dstData, size_t dstDataOffset) { + Parcel data, reply; + data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); + if (iv == NULL) { + if (ivLength > 0) { + return -EINVAL; + } + + data.writeInt32(-1); + } else { + data.writeInt32(ivLength); + data.write(iv, ivLength); + } + + data.writeInt32(srcDataSize); + data.write(srcData, srcDataSize); + + data.writeIntPtr((intptr_t)dstData); + data.writeInt32(dstDataOffset); + + remote()->transact(DECRYPT_VIDEO, data, &reply); + + return reply.readInt32(); + } + + virtual ssize_t decryptAudio( + const void *iv, size_t ivLength, + const void *srcData, size_t srcDataSize, + void *dstData, size_t dstDataSize) { + Parcel data, reply; + data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); + if (iv == NULL) { + if (ivLength > 0) { + return -EINVAL; + } + + data.writeInt32(-1); + } else { + data.writeInt32(ivLength); + data.write(iv, ivLength); + } + + data.writeInt32(srcDataSize); + data.write(srcData, srcDataSize); + data.writeInt32(dstDataSize); + + remote()->transact(DECRYPT_AUDIO, data, &reply); + + ssize_t res = reply.readInt32(); + + if (res <= 0) { + return res; + } + + reply.read(dstData, res); + + return res; + } + +private: + DISALLOW_EVIL_CONSTRUCTORS(BpCrypto); +}; + +IMPLEMENT_META_INTERFACE(Crypto, "android.hardware.ICrypto"); + +//////////////////////////////////////////////////////////////////////////////// + +status_t BnCrypto::onTransact( + uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { + switch (code) { + case INITIALIZE: + { + CHECK_INTERFACE(ICrypto, data, reply); + reply->writeInt32(initialize()); + + return OK; + } + + case TERMINATE: + { + CHECK_INTERFACE(ICrypto, data, reply); + reply->writeInt32(terminate()); + + return OK; + } + + case SET_ENTITLEMENT_KEY: + { + CHECK_INTERFACE(ICrypto, data, reply); + + size_t keyLength = data.readInt32(); + void *key = malloc(keyLength); + data.read(key, keyLength); + + reply->writeInt32(setEntitlementKey(key, keyLength)); + + free(key); + key = NULL; + + return OK; + } + + case SET_ECM: + { + CHECK_INTERFACE(ICrypto, data, reply); + + size_t msgLength = data.readInt32(); + void *msg = malloc(msgLength); + data.read(msg, msgLength); + + reply->writeInt32(setEntitlementControlMessage(msg, msgLength)); + + free(msg); + msg = NULL; + + return OK; + } + + case DECRYPT_VIDEO: + { + CHECK_INTERFACE(ICrypto, data, reply); + + void *iv = NULL; + + int32_t ivLength = data.readInt32(); + if (ivLength >= 0) { + iv = malloc(ivLength); + data.read(iv, ivLength); + } + + size_t srcDataSize = data.readInt32(); + void *srcData = malloc(srcDataSize); + data.read(srcData, srcDataSize); + + void *dstData = (void *)data.readIntPtr(); + size_t dstDataOffset = data.readInt32(); + + reply->writeInt32( + decryptVideo( + iv, + ivLength < 0 ? 0 : ivLength, + srcData, + srcDataSize, + dstData, + dstDataOffset)); + + free(srcData); + srcData = NULL; + + if (iv != NULL) { + free(iv); + iv = NULL; + } + + return OK; + } + + case DECRYPT_AUDIO: + { + CHECK_INTERFACE(ICrypto, data, reply); + + void *iv = NULL; + + int32_t ivLength = data.readInt32(); + if (ivLength >= 0) { + iv = malloc(ivLength); + data.read(iv, ivLength); + } + + size_t srcDataSize = data.readInt32(); + void *srcData = malloc(srcDataSize); + data.read(srcData, srcDataSize); + + size_t dstDataSize = data.readInt32(); + void *dstData = malloc(dstDataSize); + + ssize_t res = + decryptAudio( + iv, + ivLength < 0 ? 0 : ivLength, + srcData, + srcDataSize, + dstData, + dstDataSize); + + reply->writeInt32(res); + + if (res > 0) { + reply->write(dstData, res); + } + + free(dstData); + dstData = NULL; + + free(srcData); + srcData = NULL; + + if (iv != NULL) { + free(iv); + iv = NULL; + } + + return OK; + } + + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +} // namespace android + diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp index 9b8d7c3..7e6d54b 100644 --- a/media/libmedia/IMediaMetadataRetriever.cpp +++ b/media/libmedia/IMediaMetadataRetriever.cpp @@ -18,7 +18,6 @@ #include <stdint.h> #include <sys/types.h> #include <binder/Parcel.h> -#include <SkBitmap.h> #include <media/IMediaMetadataRetriever.h> #include <utils/String8.h> diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index f5fccef..9120617 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -20,6 +20,7 @@ #include <binder/Parcel.h> #include <binder/IMemory.h> +#include <media/ICrypto.h> #include <media/IMediaPlayerService.h> #include <media/IMediaRecorder.h> #include <media/IOMX.h> @@ -36,6 +37,7 @@ enum { CREATE_MEDIA_RECORDER, CREATE_METADATA_RETRIEVER, GET_OMX, + MAKE_CRYPTO, ADD_BATTERY_DATA, PULL_BATTERY_DATA }; @@ -111,6 +113,13 @@ public: return interface_cast<IOMX>(reply.readStrongBinder()); } + virtual sp<ICrypto> makeCrypto() { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + remote()->transact(MAKE_CRYPTO, data, &reply); + return interface_cast<ICrypto>(reply.readStrongBinder()); + } + virtual void addBatteryData(uint32_t params) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); @@ -191,6 +200,12 @@ status_t BnMediaPlayerService::onTransact( reply->writeStrongBinder(omx->asBinder()); return NO_ERROR; } break; + case MAKE_CRYPTO: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + sp<ICrypto> crypto = makeCrypto(); + reply->writeStrongBinder(crypto->asBinder()); + return NO_ERROR; + } break; case ADD_BATTERY_DATA: { CHECK_INTERFACE(IMediaPlayerService, data, reply); uint32_t params = data.readInt32(); diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp index 7fa6bb7..52aee49 100644 --- a/media/libmedia/JetPlayer.cpp +++ b/media/libmedia/JetPlayer.cpp @@ -30,7 +30,7 @@ static const int MIX_NUM_BUFFERS = 4; static const S_EAS_LIB_CONFIG* pLibConfig = NULL; //------------------------------------------------------------------------------------------------- -JetPlayer::JetPlayer(jobject javaJetPlayer, int maxTracks, int trackBufferSize) : +JetPlayer::JetPlayer(void *javaJetPlayer, int maxTracks, int trackBufferSize) : mEventCallback(NULL), mJavaJetPlayerRef(javaJetPlayer), mTid(-1), diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp new file mode 100644 index 0000000..306c57d --- /dev/null +++ b/media/libmedia/SoundPool.cpp @@ -0,0 +1,908 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoundPool" +#include <utils/Log.h> + +//#define USE_SHARED_MEM_BUFFER + +// XXX needed for timing latency +#include <utils/Timers.h> + +#include <media/AudioTrack.h> +#include <media/mediaplayer.h> + +#include <system/audio.h> + +#include "SoundPool.h" +#include "SoundPoolThread.h" + +namespace android +{ + +int kDefaultBufferCount = 4; +uint32_t kMaxSampleRate = 48000; +uint32_t kDefaultSampleRate = 44100; +uint32_t kDefaultFrameCount = 1200; + +SoundPool::SoundPool(int maxChannels, audio_stream_type_t streamType, int srcQuality) +{ + ALOGV("SoundPool constructor: maxChannels=%d, streamType=%d, srcQuality=%d", + maxChannels, streamType, srcQuality); + + // check limits + mMaxChannels = maxChannels; + if (mMaxChannels < 1) { + mMaxChannels = 1; + } + else if (mMaxChannels > 32) { + mMaxChannels = 32; + } + ALOGW_IF(maxChannels != mMaxChannels, "App requested %d channels", maxChannels); + + mQuit = false; + mDecodeThread = 0; + mStreamType = streamType; + mSrcQuality = srcQuality; + mAllocated = 0; + mNextSampleID = 0; + mNextChannelID = 0; + + mCallback = 0; + mUserData = 0; + + mChannelPool = new SoundChannel[mMaxChannels]; + for (int i = 0; i < mMaxChannels; ++i) { + mChannelPool[i].init(this); + mChannels.push_back(&mChannelPool[i]); + } + + // start decode thread + startThreads(); +} + +SoundPool::~SoundPool() +{ + ALOGV("SoundPool destructor"); + mDecodeThread->quit(); + quit(); + + Mutex::Autolock lock(&mLock); + + mChannels.clear(); + if (mChannelPool) + delete [] mChannelPool; + // clean up samples + ALOGV("clear samples"); + mSamples.clear(); + + if (mDecodeThread) + delete mDecodeThread; +} + +void SoundPool::addToRestartList(SoundChannel* channel) +{ + Mutex::Autolock lock(&mRestartLock); + if (!mQuit) { + mRestart.push_back(channel); + mCondition.signal(); + } +} + +void SoundPool::addToStopList(SoundChannel* channel) +{ + Mutex::Autolock lock(&mRestartLock); + if (!mQuit) { + mStop.push_back(channel); + mCondition.signal(); + } +} + +int SoundPool::beginThread(void* arg) +{ + SoundPool* p = (SoundPool*)arg; + return p->run(); +} + +int SoundPool::run() +{ + mRestartLock.lock(); + while (!mQuit) { + mCondition.wait(mRestartLock); + ALOGV("awake"); + if (mQuit) break; + + while (!mStop.empty()) { + SoundChannel* channel; + ALOGV("Getting channel from stop list"); + List<SoundChannel* >::iterator iter = mStop.begin(); + channel = *iter; + mStop.erase(iter); + mRestartLock.unlock(); + if (channel != 0) { + Mutex::Autolock lock(&mLock); + channel->stop(); + } + mRestartLock.lock(); + if (mQuit) break; + } + + while (!mRestart.empty()) { + SoundChannel* channel; + ALOGV("Getting channel from list"); + List<SoundChannel*>::iterator iter = mRestart.begin(); + channel = *iter; + mRestart.erase(iter); + mRestartLock.unlock(); + if (channel != 0) { + Mutex::Autolock lock(&mLock); + channel->nextEvent(); + } + mRestartLock.lock(); + if (mQuit) break; + } + } + + mStop.clear(); + mRestart.clear(); + mCondition.signal(); + mRestartLock.unlock(); + ALOGV("goodbye"); + return 0; +} + +void SoundPool::quit() +{ + mRestartLock.lock(); + mQuit = true; + mCondition.signal(); + mCondition.wait(mRestartLock); + ALOGV("return from quit"); + mRestartLock.unlock(); +} + +bool SoundPool::startThreads() +{ + createThreadEtc(beginThread, this, "SoundPool"); + if (mDecodeThread == NULL) + mDecodeThread = new SoundPoolThread(this); + return mDecodeThread != NULL; +} + +SoundChannel* SoundPool::findChannel(int channelID) +{ + for (int i = 0; i < mMaxChannels; ++i) { + if (mChannelPool[i].channelID() == channelID) { + return &mChannelPool[i]; + } + } + return NULL; +} + +SoundChannel* SoundPool::findNextChannel(int channelID) +{ + for (int i = 0; i < mMaxChannels; ++i) { + if (mChannelPool[i].nextChannelID() == channelID) { + return &mChannelPool[i]; + } + } + return NULL; +} + +int SoundPool::load(const char* path, int priority) +{ + ALOGV("load: path=%s, priority=%d", path, priority); + Mutex::Autolock lock(&mLock); + sp<Sample> sample = new Sample(++mNextSampleID, path); + mSamples.add(sample->sampleID(), sample); + doLoad(sample); + return sample->sampleID(); +} + +int SoundPool::load(int fd, int64_t offset, int64_t length, int priority) +{ + ALOGV("load: fd=%d, offset=%lld, length=%lld, priority=%d", + fd, offset, length, priority); + Mutex::Autolock lock(&mLock); + sp<Sample> sample = new Sample(++mNextSampleID, fd, offset, length); + mSamples.add(sample->sampleID(), sample); + doLoad(sample); + return sample->sampleID(); +} + +void SoundPool::doLoad(sp<Sample>& sample) +{ + ALOGV("doLoad: loading sample sampleID=%d", sample->sampleID()); + sample->startLoad(); + mDecodeThread->loadSample(sample->sampleID()); +} + +bool SoundPool::unload(int sampleID) +{ + ALOGV("unload: sampleID=%d", sampleID); + Mutex::Autolock lock(&mLock); + return mSamples.removeItem(sampleID); +} + +int SoundPool::play(int sampleID, float leftVolume, float rightVolume, + int priority, int loop, float rate) +{ + ALOGV("play sampleID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f", + sampleID, leftVolume, rightVolume, priority, loop, rate); + sp<Sample> sample; + SoundChannel* channel; + int channelID; + + Mutex::Autolock lock(&mLock); + + if (mQuit) { + return 0; + } + // is sample ready? + sample = findSample(sampleID); + if ((sample == 0) || (sample->state() != Sample::READY)) { + ALOGW(" sample %d not READY", sampleID); + return 0; + } + + dump(); + + // allocate a channel + channel = allocateChannel_l(priority); + + // no channel allocated - return 0 + if (!channel) { + ALOGV("No channel allocated"); + return 0; + } + + channelID = ++mNextChannelID; + + ALOGV("play channel %p state = %d", channel, channel->state()); + channel->play(sample, channelID, leftVolume, rightVolume, priority, loop, rate); + return channelID; +} + +SoundChannel* SoundPool::allocateChannel_l(int priority) +{ + List<SoundChannel*>::iterator iter; + SoundChannel* channel = NULL; + + // allocate a channel + if (!mChannels.empty()) { + iter = mChannels.begin(); + if (priority >= (*iter)->priority()) { + channel = *iter; + mChannels.erase(iter); + ALOGV("Allocated active channel"); + } + } + + // update priority and put it back in the list + if (channel) { + channel->setPriority(priority); + for (iter = mChannels.begin(); iter != mChannels.end(); ++iter) { + if (priority < (*iter)->priority()) { + break; + } + } + mChannels.insert(iter, channel); + } + return channel; +} + +// move a channel from its current position to the front of the list +void SoundPool::moveToFront_l(SoundChannel* channel) +{ + for (List<SoundChannel*>::iterator iter = mChannels.begin(); iter != mChannels.end(); ++iter) { + if (*iter == channel) { + mChannels.erase(iter); + mChannels.push_front(channel); + break; + } + } +} + +void SoundPool::pause(int channelID) +{ + ALOGV("pause(%d)", channelID); + Mutex::Autolock lock(&mLock); + SoundChannel* channel = findChannel(channelID); + if (channel) { + channel->pause(); + } +} + +void SoundPool::autoPause() +{ + ALOGV("autoPause()"); + Mutex::Autolock lock(&mLock); + for (int i = 0; i < mMaxChannels; ++i) { + SoundChannel* channel = &mChannelPool[i]; + channel->autoPause(); + } +} + +void SoundPool::resume(int channelID) +{ + ALOGV("resume(%d)", channelID); + Mutex::Autolock lock(&mLock); + SoundChannel* channel = findChannel(channelID); + if (channel) { + channel->resume(); + } +} + +void SoundPool::autoResume() +{ + ALOGV("autoResume()"); + Mutex::Autolock lock(&mLock); + for (int i = 0; i < mMaxChannels; ++i) { + SoundChannel* channel = &mChannelPool[i]; + channel->autoResume(); + } +} + +void SoundPool::stop(int channelID) +{ + ALOGV("stop(%d)", channelID); + Mutex::Autolock lock(&mLock); + SoundChannel* channel = findChannel(channelID); + if (channel) { + channel->stop(); + } else { + channel = findNextChannel(channelID); + if (channel) + channel->clearNextEvent(); + } +} + +void SoundPool::setVolume(int channelID, float leftVolume, float rightVolume) +{ + Mutex::Autolock lock(&mLock); + SoundChannel* channel = findChannel(channelID); + if (channel) { + channel->setVolume(leftVolume, rightVolume); + } +} + +void SoundPool::setPriority(int channelID, int priority) +{ + ALOGV("setPriority(%d, %d)", channelID, priority); + Mutex::Autolock lock(&mLock); + SoundChannel* channel = findChannel(channelID); + if (channel) { + channel->setPriority(priority); + } +} + +void SoundPool::setLoop(int channelID, int loop) +{ + ALOGV("setLoop(%d, %d)", channelID, loop); + Mutex::Autolock lock(&mLock); + SoundChannel* channel = findChannel(channelID); + if (channel) { + channel->setLoop(loop); + } +} + +void SoundPool::setRate(int channelID, float rate) +{ + ALOGV("setRate(%d, %f)", channelID, rate); + Mutex::Autolock lock(&mLock); + SoundChannel* channel = findChannel(channelID); + if (channel) { + channel->setRate(rate); + } +} + +// call with lock held +void SoundPool::done_l(SoundChannel* channel) +{ + ALOGV("done_l(%d)", channel->channelID()); + // if "stolen", play next event + if (channel->nextChannelID() != 0) { + ALOGV("add to restart list"); + addToRestartList(channel); + } + + // return to idle state + else { + ALOGV("move to front"); + moveToFront_l(channel); + } +} + +void SoundPool::setCallback(SoundPoolCallback* callback, void* user) +{ + Mutex::Autolock lock(&mCallbackLock); + mCallback = callback; + mUserData = user; +} + +void SoundPool::notify(SoundPoolEvent event) +{ + Mutex::Autolock lock(&mCallbackLock); + if (mCallback != NULL) { + mCallback(event, this, mUserData); + } +} + +void SoundPool::dump() +{ + for (int i = 0; i < mMaxChannels; ++i) { + mChannelPool[i].dump(); + } +} + + +Sample::Sample(int sampleID, const char* url) +{ + init(); + mSampleID = sampleID; + mUrl = strdup(url); + ALOGV("create sampleID=%d, url=%s", mSampleID, mUrl); +} + +Sample::Sample(int sampleID, int fd, int64_t offset, int64_t length) +{ + init(); + mSampleID = sampleID; + mFd = dup(fd); + mOffset = offset; + mLength = length; + ALOGV("create sampleID=%d, fd=%d, offset=%lld, length=%lld", mSampleID, mFd, mLength, mOffset); +} + +void Sample::init() +{ + mData = 0; + mSize = 0; + mRefCount = 0; + mSampleID = 0; + mState = UNLOADED; + mFd = -1; + mOffset = 0; + mLength = 0; + mUrl = 0; +} + +Sample::~Sample() +{ + ALOGV("Sample::destructor sampleID=%d, fd=%d", mSampleID, mFd); + if (mFd > 0) { + ALOGV("close(%d)", mFd); + ::close(mFd); + } + mData.clear(); + delete mUrl; +} + +status_t Sample::doLoad() +{ + uint32_t sampleRate; + int numChannels; + audio_format_t format; + sp<IMemory> p; + ALOGV("Start decode"); + if (mUrl) { + p = MediaPlayer::decode(mUrl, &sampleRate, &numChannels, &format); + } else { + p = MediaPlayer::decode(mFd, mOffset, mLength, &sampleRate, &numChannels, &format); + ALOGV("close(%d)", mFd); + ::close(mFd); + mFd = -1; + } + if (p == 0) { + ALOGE("Unable to load sample: %s", mUrl); + return -1; + } + ALOGV("pointer = %p, size = %u, sampleRate = %u, numChannels = %d", + p->pointer(), p->size(), sampleRate, numChannels); + + if (sampleRate > kMaxSampleRate) { + ALOGE("Sample rate (%u) out of range", sampleRate); + return - 1; + } + + if ((numChannels < 1) || (numChannels > 2)) { + ALOGE("Sample channel count (%d) out of range", numChannels); + return - 1; + } + + //_dumpBuffer(p->pointer(), p->size()); + uint8_t* q = static_cast<uint8_t*>(p->pointer()) + p->size() - 10; + //_dumpBuffer(q, 10, 10, false); + + mData = p; + mSize = p->size(); + mSampleRate = sampleRate; + mNumChannels = numChannels; + mFormat = format; + mState = READY; + return 0; +} + + +void SoundChannel::init(SoundPool* soundPool) +{ + mSoundPool = soundPool; +} + +// call with sound pool lock held +void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftVolume, + float rightVolume, int priority, int loop, float rate) +{ + AudioTrack* oldTrack; + AudioTrack* newTrack; + status_t status; + + { // scope for the lock + Mutex::Autolock lock(&mLock); + + ALOGV("SoundChannel::play %p: sampleID=%d, channelID=%d, leftVolume=%f, rightVolume=%f," + " priority=%d, loop=%d, rate=%f", + this, sample->sampleID(), nextChannelID, leftVolume, rightVolume, + priority, loop, rate); + + // if not idle, this voice is being stolen + if (mState != IDLE) { + ALOGV("channel %d stolen - event queued for channel %d", channelID(), nextChannelID); + mNextEvent.set(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate); + stop_l(); + return; + } + + // initialize track + int afFrameCount; + int afSampleRate; + audio_stream_type_t streamType = mSoundPool->streamType(); + if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { + afFrameCount = kDefaultFrameCount; + } + if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { + afSampleRate = kDefaultSampleRate; + } + int numChannels = sample->numChannels(); + uint32_t sampleRate = uint32_t(float(sample->sampleRate()) * rate + 0.5); + uint32_t totalFrames = (kDefaultBufferCount * afFrameCount * sampleRate) / afSampleRate; + uint32_t bufferFrames = (totalFrames + (kDefaultBufferCount - 1)) / kDefaultBufferCount; + uint32_t frameCount = 0; + + if (loop) { + frameCount = sample->size()/numChannels/ + ((sample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t)); + } + +#ifndef USE_SHARED_MEM_BUFFER + // Ensure minimum audio buffer size in case of short looped sample + if(frameCount < totalFrames) { + frameCount = totalFrames; + } +#endif + + // mToggle toggles each time a track is started on a given channel. + // The toggle is concatenated with the SoundChannel address and passed to AudioTrack + // as callback user data. This enables the detection of callbacks received from the old + // audio track while the new one is being started and avoids processing them with + // wrong audio audio buffer size (mAudioBufferSize) + unsigned long toggle = mToggle ^ 1; + void *userData = (void *)((unsigned long)this | toggle); + uint32_t channels = (numChannels == 2) ? + AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO; + + // do not create a new audio track if current track is compatible with sample parameters +#ifdef USE_SHARED_MEM_BUFFER + newTrack = new AudioTrack(streamType, sampleRate, sample->format(), + channels, sample->getIMemory(), AUDIO_POLICY_OUTPUT_FLAG_NONE, callback, userData); +#else + newTrack = new AudioTrack(streamType, sampleRate, sample->format(), + channels, frameCount, AUDIO_POLICY_OUTPUT_FLAG_NONE, callback, userData, + bufferFrames); +#endif + oldTrack = mAudioTrack; + status = newTrack->initCheck(); + if (status != NO_ERROR) { + ALOGE("Error creating AudioTrack"); + goto exit; + } + ALOGV("setVolume %p", newTrack); + newTrack->setVolume(leftVolume, rightVolume); + newTrack->setLoop(0, frameCount, loop); + + // From now on, AudioTrack callbacks received with previous toggle value will be ignored. + mToggle = toggle; + mAudioTrack = newTrack; + mPos = 0; + mSample = sample; + mChannelID = nextChannelID; + mPriority = priority; + mLoop = loop; + mLeftVolume = leftVolume; + mRightVolume = rightVolume; + mNumChannels = numChannels; + mRate = rate; + clearNextEvent(); + mState = PLAYING; + mAudioTrack->start(); + mAudioBufferSize = newTrack->frameCount()*newTrack->frameSize(); + } + +exit: + ALOGV("delete oldTrack %p", oldTrack); + delete oldTrack; + if (status != NO_ERROR) { + delete newTrack; + mAudioTrack = NULL; + } +} + +void SoundChannel::nextEvent() +{ + sp<Sample> sample; + int nextChannelID; + float leftVolume; + float rightVolume; + int priority; + int loop; + float rate; + + // check for valid event + { + Mutex::Autolock lock(&mLock); + nextChannelID = mNextEvent.channelID(); + if (nextChannelID == 0) { + ALOGV("stolen channel has no event"); + return; + } + + sample = mNextEvent.sample(); + leftVolume = mNextEvent.leftVolume(); + rightVolume = mNextEvent.rightVolume(); + priority = mNextEvent.priority(); + loop = mNextEvent.loop(); + rate = mNextEvent.rate(); + } + + ALOGV("Starting stolen channel %d -> %d", channelID(), nextChannelID); + play(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate); +} + +void SoundChannel::callback(int event, void* user, void *info) +{ + SoundChannel* channel = static_cast<SoundChannel*>((void *)((unsigned long)user & ~1)); + + channel->process(event, info, (unsigned long)user & 1); +} + +void SoundChannel::process(int event, void *info, unsigned long toggle) +{ + //ALOGV("process(%d)", mChannelID); + + Mutex::Autolock lock(&mLock); + + AudioTrack::Buffer* b = NULL; + if (event == AudioTrack::EVENT_MORE_DATA) { + b = static_cast<AudioTrack::Buffer *>(info); + } + + if (mToggle != toggle) { + ALOGV("process wrong toggle %p channel %d", this, mChannelID); + if (b != NULL) { + b->size = 0; + } + return; + } + + sp<Sample> sample = mSample; + +// ALOGV("SoundChannel::process event %d", event); + + if (event == AudioTrack::EVENT_MORE_DATA) { + + // check for stop state + if (b->size == 0) return; + + if (mState == IDLE) { + b->size = 0; + return; + } + + if (sample != 0) { + // fill buffer + uint8_t* q = (uint8_t*) b->i8; + size_t count = 0; + + if (mPos < (int)sample->size()) { + uint8_t* p = sample->data() + mPos; + count = sample->size() - mPos; + if (count > b->size) { + count = b->size; + } + memcpy(q, p, count); +// ALOGV("fill: q=%p, p=%p, mPos=%u, b->size=%u, count=%d", q, p, mPos, b->size, count); + } else if (mPos < mAudioBufferSize) { + count = mAudioBufferSize - mPos; + if (count > b->size) { + count = b->size; + } + memset(q, 0, count); +// ALOGV("fill extra: q=%p, mPos=%u, b->size=%u, count=%d", q, mPos, b->size, count); + } + + mPos += count; + b->size = count; + //ALOGV("buffer=%p, [0]=%d", b->i16, b->i16[0]); + } + } else if (event == AudioTrack::EVENT_UNDERRUN) { + ALOGV("process %p channel %d EVENT_UNDERRUN", this, mChannelID); + mSoundPool->addToStopList(this); + } else if (event == AudioTrack::EVENT_LOOP_END) { + ALOGV("End loop %p channel %d count %d", this, mChannelID, *(int *)info); + } +} + + +// call with lock held +bool SoundChannel::doStop_l() +{ + if (mState != IDLE) { + setVolume_l(0, 0); + ALOGV("stop"); + mAudioTrack->stop(); + mSample.clear(); + mState = IDLE; + mPriority = IDLE_PRIORITY; + return true; + } + return false; +} + +// call with lock held and sound pool lock held +void SoundChannel::stop_l() +{ + if (doStop_l()) { + mSoundPool->done_l(this); + } +} + +// call with sound pool lock held +void SoundChannel::stop() +{ + bool stopped; + { + Mutex::Autolock lock(&mLock); + stopped = doStop_l(); + } + + if (stopped) { + mSoundPool->done_l(this); + } +} + +//FIXME: Pause is a little broken right now +void SoundChannel::pause() +{ + Mutex::Autolock lock(&mLock); + if (mState == PLAYING) { + ALOGV("pause track"); + mState = PAUSED; + mAudioTrack->pause(); + } +} + +void SoundChannel::autoPause() +{ + Mutex::Autolock lock(&mLock); + if (mState == PLAYING) { + ALOGV("pause track"); + mState = PAUSED; + mAutoPaused = true; + mAudioTrack->pause(); + } +} + +void SoundChannel::resume() +{ + Mutex::Autolock lock(&mLock); + if (mState == PAUSED) { + ALOGV("resume track"); + mState = PLAYING; + mAutoPaused = false; + mAudioTrack->start(); + } +} + +void SoundChannel::autoResume() +{ + Mutex::Autolock lock(&mLock); + if (mAutoPaused && (mState == PAUSED)) { + ALOGV("resume track"); + mState = PLAYING; + mAutoPaused = false; + mAudioTrack->start(); + } +} + +void SoundChannel::setRate(float rate) +{ + Mutex::Autolock lock(&mLock); + if (mAudioTrack != NULL && mSample != 0) { + uint32_t sampleRate = uint32_t(float(mSample->sampleRate()) * rate + 0.5); + mAudioTrack->setSampleRate(sampleRate); + mRate = rate; + } +} + +// call with lock held +void SoundChannel::setVolume_l(float leftVolume, float rightVolume) +{ + mLeftVolume = leftVolume; + mRightVolume = rightVolume; + if (mAudioTrack != NULL) + mAudioTrack->setVolume(leftVolume, rightVolume); +} + +void SoundChannel::setVolume(float leftVolume, float rightVolume) +{ + Mutex::Autolock lock(&mLock); + setVolume_l(leftVolume, rightVolume); +} + +void SoundChannel::setLoop(int loop) +{ + Mutex::Autolock lock(&mLock); + if (mAudioTrack != NULL && mSample != 0) { + uint32_t loopEnd = mSample->size()/mNumChannels/ + ((mSample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t)); + mAudioTrack->setLoop(0, loopEnd, loop); + mLoop = loop; + } +} + +SoundChannel::~SoundChannel() +{ + ALOGV("SoundChannel destructor %p", this); + { + Mutex::Autolock lock(&mLock); + clearNextEvent(); + doStop_l(); + } + // do not call AudioTrack destructor with mLock held as it will wait for the AudioTrack + // callback thread to exit which may need to execute process() and acquire the mLock. + delete mAudioTrack; +} + +void SoundChannel::dump() +{ + ALOGV("mState = %d mChannelID=%d, mNumChannels=%d, mPos = %d, mPriority=%d, mLoop=%d", + mState, mChannelID, mNumChannels, mPos, mPriority, mLoop); +} + +void SoundEvent::set(const sp<Sample>& sample, int channelID, float leftVolume, + float rightVolume, int priority, int loop, float rate) +{ + mSample = sample; + mChannelID = channelID; + mLeftVolume = leftVolume; + mRightVolume = rightVolume; + mPriority = priority; + mLoop = loop; + mRate =rate; +} + +} // end namespace android diff --git a/media/libmedia/SoundPool.h b/media/libmedia/SoundPool.h new file mode 100644 index 0000000..002b045 --- /dev/null +++ b/media/libmedia/SoundPool.h @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOUNDPOOL_H_ +#define SOUNDPOOL_H_ + +#include <utils/threads.h> +#include <utils/List.h> +#include <utils/Vector.h> +#include <utils/KeyedVector.h> +#include <media/AudioTrack.h> + +namespace android { + +static const int IDLE_PRIORITY = -1; + +// forward declarations +class SoundEvent; +class SoundPoolThread; +class SoundPool; + +// for queued events +class SoundPoolEvent { +public: + SoundPoolEvent(int msg, int arg1=0, int arg2=0) : + mMsg(msg), mArg1(arg1), mArg2(arg2) {} + int mMsg; + int mArg1; + int mArg2; + enum MessageType { INVALID, SAMPLE_LOADED }; +}; + +// callback function prototype +typedef void SoundPoolCallback(SoundPoolEvent event, SoundPool* soundPool, void* user); + +// tracks samples used by application +class Sample : public RefBase { +public: + enum sample_state { UNLOADED, LOADING, READY, UNLOADING }; + Sample(int sampleID, const char* url); + Sample(int sampleID, int fd, int64_t offset, int64_t length); + ~Sample(); + int sampleID() { return mSampleID; } + int numChannels() { return mNumChannels; } + int sampleRate() { return mSampleRate; } + audio_format_t format() { return mFormat; } + size_t size() { return mSize; } + int state() { return mState; } + uint8_t* data() { return static_cast<uint8_t*>(mData->pointer()); } + status_t doLoad(); + void startLoad() { mState = LOADING; } + sp<IMemory> getIMemory() { return mData; } + + // hack + void init(int numChannels, int sampleRate, audio_format_t format, size_t size, sp<IMemory> data ) { + mNumChannels = numChannels; mSampleRate = sampleRate; mFormat = format; mSize = size; mData = data; } + +private: + void init(); + + size_t mSize; + volatile int32_t mRefCount; + uint16_t mSampleID; + uint16_t mSampleRate; + uint8_t mState : 3; + uint8_t mNumChannels : 2; + audio_format_t mFormat; + int mFd; + int64_t mOffset; + int64_t mLength; + char* mUrl; + sp<IMemory> mData; +}; + +// stores pending events for stolen channels +class SoundEvent +{ +public: + SoundEvent() : mChannelID(0), mLeftVolume(0), mRightVolume(0), + mPriority(IDLE_PRIORITY), mLoop(0), mRate(0) {} + void set(const sp<Sample>& sample, int channelID, float leftVolume, + float rightVolume, int priority, int loop, float rate); + sp<Sample> sample() { return mSample; } + int channelID() { return mChannelID; } + float leftVolume() { return mLeftVolume; } + float rightVolume() { return mRightVolume; } + int priority() { return mPriority; } + int loop() { return mLoop; } + float rate() { return mRate; } + void clear() { mChannelID = 0; mSample.clear(); } + +protected: + sp<Sample> mSample; + int mChannelID; + float mLeftVolume; + float mRightVolume; + int mPriority; + int mLoop; + float mRate; +}; + +// for channels aka AudioTracks +class SoundChannel : public SoundEvent { +public: + enum state { IDLE, RESUMING, STOPPING, PAUSED, PLAYING }; + SoundChannel() : mAudioTrack(NULL), mState(IDLE), mNumChannels(1), + mPos(0), mToggle(0), mAutoPaused(false) {} + ~SoundChannel(); + void init(SoundPool* soundPool); + void play(const sp<Sample>& sample, int channelID, float leftVolume, float rightVolume, + int priority, int loop, float rate); + void setVolume_l(float leftVolume, float rightVolume); + void setVolume(float leftVolume, float rightVolume); + void stop_l(); + void stop(); + void pause(); + void autoPause(); + void resume(); + void autoResume(); + void setRate(float rate); + int state() { return mState; } + void setPriority(int priority) { mPriority = priority; } + void setLoop(int loop); + int numChannels() { return mNumChannels; } + void clearNextEvent() { mNextEvent.clear(); } + void nextEvent(); + int nextChannelID() { return mNextEvent.channelID(); } + void dump(); + +private: + static void callback(int event, void* user, void *info); + void process(int event, void *info, unsigned long toggle); + bool doStop_l(); + + SoundPool* mSoundPool; + AudioTrack* mAudioTrack; + SoundEvent mNextEvent; + Mutex mLock; + int mState; + int mNumChannels; + int mPos; + int mAudioBufferSize; + unsigned long mToggle; + bool mAutoPaused; +}; + +// application object for managing a pool of sounds +class SoundPool { + friend class SoundPoolThread; + friend class SoundChannel; +public: + SoundPool(int maxChannels, audio_stream_type_t streamType, int srcQuality); + ~SoundPool(); + int load(const char* url, int priority); + int load(int fd, int64_t offset, int64_t length, int priority); + bool unload(int sampleID); + int play(int sampleID, float leftVolume, float rightVolume, int priority, + int loop, float rate); + void pause(int channelID); + void autoPause(); + void resume(int channelID); + void autoResume(); + void stop(int channelID); + void setVolume(int channelID, float leftVolume, float rightVolume); + void setPriority(int channelID, int priority); + void setLoop(int channelID, int loop); + void setRate(int channelID, float rate); + audio_stream_type_t streamType() const { return mStreamType; } + int srcQuality() const { return mSrcQuality; } + + // called from SoundPoolThread + void sampleLoaded(int sampleID); + + // called from AudioTrack thread + void done_l(SoundChannel* channel); + + // callback function + void setCallback(SoundPoolCallback* callback, void* user); + void* getUserData() { return mUserData; } + +private: + SoundPool() {} // no default constructor + bool startThreads(); + void doLoad(sp<Sample>& sample); + sp<Sample> findSample(int sampleID) { return mSamples.valueFor(sampleID); } + SoundChannel* findChannel (int channelID); + SoundChannel* findNextChannel (int channelID); + SoundChannel* allocateChannel_l(int priority); + void moveToFront_l(SoundChannel* channel); + void notify(SoundPoolEvent event); + void dump(); + + // restart thread + void addToRestartList(SoundChannel* channel); + void addToStopList(SoundChannel* channel); + static int beginThread(void* arg); + int run(); + void quit(); + + Mutex mLock; + Mutex mRestartLock; + Condition mCondition; + SoundPoolThread* mDecodeThread; + SoundChannel* mChannelPool; + List<SoundChannel*> mChannels; + List<SoundChannel*> mRestart; + List<SoundChannel*> mStop; + DefaultKeyedVector< int, sp<Sample> > mSamples; + int mMaxChannels; + audio_stream_type_t mStreamType; + int mSrcQuality; + int mAllocated; + int mNextSampleID; + int mNextChannelID; + bool mQuit; + + // callback + Mutex mCallbackLock; + SoundPoolCallback* mCallback; + void* mUserData; +}; + +} // end namespace android + +#endif /*SOUNDPOOL_H_*/ diff --git a/media/libmedia/SoundPoolThread.cpp b/media/libmedia/SoundPoolThread.cpp new file mode 100644 index 0000000..ba3b482 --- /dev/null +++ b/media/libmedia/SoundPoolThread.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoundPoolThread" +#include "utils/Log.h" + +#include "SoundPoolThread.h" + +namespace android { + +void SoundPoolThread::write(SoundPoolMsg msg) { + Mutex::Autolock lock(&mLock); + while (mMsgQueue.size() >= maxMessages) { + mCondition.wait(mLock); + } + + // if thread is quitting, don't add to queue + if (mRunning) { + mMsgQueue.push(msg); + mCondition.signal(); + } +} + +const SoundPoolMsg SoundPoolThread::read() { + Mutex::Autolock lock(&mLock); + while (mMsgQueue.size() == 0) { + mCondition.wait(mLock); + } + SoundPoolMsg msg = mMsgQueue[0]; + mMsgQueue.removeAt(0); + mCondition.signal(); + return msg; +} + +void SoundPoolThread::quit() { + Mutex::Autolock lock(&mLock); + if (mRunning) { + mRunning = false; + mMsgQueue.clear(); + mMsgQueue.push(SoundPoolMsg(SoundPoolMsg::KILL, 0)); + mCondition.signal(); + mCondition.wait(mLock); + } + ALOGV("return from quit"); +} + +SoundPoolThread::SoundPoolThread(SoundPool* soundPool) : + mSoundPool(soundPool) +{ + mMsgQueue.setCapacity(maxMessages); + if (createThreadEtc(beginThread, this, "SoundPoolThread")) { + mRunning = true; + } +} + +SoundPoolThread::~SoundPoolThread() +{ + quit(); +} + +int SoundPoolThread::beginThread(void* arg) { + ALOGV("beginThread"); + SoundPoolThread* soundPoolThread = (SoundPoolThread*)arg; + return soundPoolThread->run(); +} + +int SoundPoolThread::run() { + ALOGV("run"); + for (;;) { + SoundPoolMsg msg = read(); + ALOGV("Got message m=%d, mData=%d", msg.mMessageType, msg.mData); + switch (msg.mMessageType) { + case SoundPoolMsg::KILL: + ALOGV("goodbye"); + return NO_ERROR; + case SoundPoolMsg::LOAD_SAMPLE: + doLoadSample(msg.mData); + break; + default: + ALOGW("run: Unrecognized message %d\n", + msg.mMessageType); + break; + } + } +} + +void SoundPoolThread::loadSample(int sampleID) { + write(SoundPoolMsg(SoundPoolMsg::LOAD_SAMPLE, sampleID)); +} + +void SoundPoolThread::doLoadSample(int sampleID) { + sp <Sample> sample = mSoundPool->findSample(sampleID); + status_t status = -1; + if (sample != 0) { + status = sample->doLoad(); + } + mSoundPool->notify(SoundPoolEvent(SoundPoolEvent::SAMPLE_LOADED, sampleID, status)); +} + +} // end namespace android diff --git a/media/libmedia/SoundPoolThread.h b/media/libmedia/SoundPoolThread.h new file mode 100644 index 0000000..d388388 --- /dev/null +++ b/media/libmedia/SoundPoolThread.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOUNDPOOLTHREAD_H_ +#define SOUNDPOOLTHREAD_H_ + +#include <utils/threads.h> +#include <utils/Vector.h> +#include <media/AudioTrack.h> + +#include "SoundPool.h" + +namespace android { + +class SoundPoolMsg { +public: + enum MessageType { INVALID, KILL, LOAD_SAMPLE }; + SoundPoolMsg() : mMessageType(INVALID), mData(0) {} + SoundPoolMsg(MessageType MessageType, int data) : + mMessageType(MessageType), mData(data) {} + uint16_t mMessageType; + uint16_t mData; +}; + +/* + * This class handles background requests from the SoundPool + */ +class SoundPoolThread { +public: + SoundPoolThread(SoundPool* SoundPool); + ~SoundPoolThread(); + void loadSample(int sampleID); + void quit(); + void write(SoundPoolMsg msg); + +private: + static const size_t maxMessages = 5; + + static int beginThread(void* arg); + int run(); + void doLoadSample(int sampleID); + const SoundPoolMsg read(); + + Mutex mLock; + Condition mCondition; + Vector<SoundPoolMsg> mMsgQueue; + SoundPool* mSoundPool; + bool mRunning; +}; + +} // end namespace android + +#endif /*SOUNDPOOLTHREAD_H_*/ diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index ba5c776..675c563 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -7,6 +7,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + Crypto.cpp \ MediaRecorderClient.cpp \ MediaPlayerService.cpp \ MetadataRetrieverClient.cpp \ @@ -38,12 +39,11 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_rtsp \ LOCAL_C_INCLUDES := \ - $(JNI_H_INCLUDE) \ $(call include-path-for, graphics corecg) \ $(TOP)/frameworks/base/media/libstagefright/include \ $(TOP)/frameworks/base/media/libstagefright/rtsp \ $(TOP)/frameworks/native/include/media/openmax \ - $(TOP)/external/tremolo/Tremolo + $(TOP)/external/tremolo/Tremolo \ LOCAL_MODULE:= libmediaplayerservice diff --git a/media/libmediaplayerservice/Crypto.cpp b/media/libmediaplayerservice/Crypto.cpp new file mode 100644 index 0000000..e02035f --- /dev/null +++ b/media/libmediaplayerservice/Crypto.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Crypto" +#include <utils/Log.h> + +#include "Crypto.h" + +#include <media/stagefright/MediaErrors.h> + +namespace android { + +Crypto::Crypto() { +} + +Crypto::~Crypto() { +} + +status_t Crypto::initialize() { + return ERROR_UNSUPPORTED; +} + +status_t Crypto::terminate() { + return ERROR_UNSUPPORTED; +} + +status_t Crypto::setEntitlementKey( + const void *key, size_t keyLength) { + return ERROR_UNSUPPORTED; +} + +status_t Crypto::setEntitlementControlMessage( + const void *msg, size_t msgLength) { + return ERROR_UNSUPPORTED; +} + +ssize_t Crypto::decryptVideo( + const void *iv, size_t ivLength, + const void *srcData, size_t srcDataSize, + void *dstData, size_t dstDataOffset) { + return ERROR_UNSUPPORTED; +} + +ssize_t Crypto::decryptAudio( + const void *iv, size_t ivLength, + const void *srcData, size_t srcDataSize, + void *dstData, size_t dstDataSize) { + return ERROR_UNSUPPORTED; +} + +} // namespace android diff --git a/media/libmediaplayerservice/Crypto.h b/media/libmediaplayerservice/Crypto.h new file mode 100644 index 0000000..9855496 --- /dev/null +++ b/media/libmediaplayerservice/Crypto.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CRYPTO_H_ + +#define CRYPTO_H_ + +#include <media/ICrypto.h> +#include <utils/threads.h> + +namespace android { + +struct Crypto : public BnCrypto { + Crypto(); + + virtual status_t initialize(); + virtual status_t terminate(); + + virtual status_t setEntitlementKey( + const void *key, size_t keyLength); + + virtual status_t setEntitlementControlMessage( + const void *msg, size_t msgLength); + + virtual ssize_t decryptVideo( + const void *iv, size_t ivLength, + const void *srcData, size_t srcDataSize, + void *dstData, size_t dstDataOffset); + + virtual ssize_t decryptAudio( + const void *iv, size_t ivLength, + const void *srcData, size_t srcDataSize, + void *dstData, size_t dstDataSize); + +protected: + virtual ~Crypto(); + +private: + DISALLOW_EVIL_CONSTRUCTORS(Crypto); +}; + +} // namespace android + +#endif // CRYPTO_H_ diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 840e475..123d07f 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -70,6 +70,8 @@ #include <OMX.h> +#include "Crypto.h" + namespace android { sp<MediaPlayerBase> createAAH_TXPlayer(); sp<MediaPlayerBase> createAAH_RXPlayer(); @@ -292,6 +294,16 @@ sp<IOMX> MediaPlayerService::getOMX() { return mOMX; } +sp<ICrypto> MediaPlayerService::makeCrypto() { + Mutex::Autolock autoLock(mLock); + + if (mCrypto == NULL) { + mCrypto = new Crypto; + } + + return mCrypto; +} + status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& args) const { const size_t SIZE = 256; diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index d4e0eb1..b08dd6c 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -240,6 +240,7 @@ public: virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat); virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat); virtual sp<IOMX> getOMX(); + virtual sp<ICrypto> makeCrypto(); virtual status_t dump(int fd, const Vector<String16>& args); @@ -419,6 +420,7 @@ private: SortedVector< wp<MediaRecorderClient> > mMediaRecorderClients; int32_t mNextConnId; sp<IOMX> mOMX; + sp<ICrypto> mCrypto; }; // ---------------------------------------------------------------------------- diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 1600141..5733229 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -125,6 +125,11 @@ sp<AMessage> NuPlayer::Decoder::makeFormat(const sp<MetaData> &meta) { msg->setInt32("channel-count", numChannels); msg->setInt32("sample-rate", sampleRate); + + int32_t isADTS; + if (meta->findInt32(kKeyIsADTS, &isADTS) && isADTS != 0) { + msg->setInt32("is-adts", true); + } } int32_t maxInputSize; diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index e5ad4b7..db2beda 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -427,24 +427,34 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) { sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize); CHECK(mem.get() != NULL); - IOMX::buffer_id buffer; + BufferInfo info; + info.mStatus = BufferInfo::OWNED_BY_US; uint32_t requiresAllocateBufferBit = (portIndex == kPortIndexInput) ? OMXCodec::kRequiresAllocateBufferOnInputPorts : OMXCodec::kRequiresAllocateBufferOnOutputPorts; - if (mQuirks & requiresAllocateBufferBit) { + if (portIndex == kPortIndexInput && (mFlags & kFlagIsSecure)) { + mem.clear(); + + void *ptr; + err = mOMX->allocateBuffer( + mNode, portIndex, def.nBufferSize, &info.mBufferID, + &ptr); + + info.mData = new ABuffer(ptr, def.nBufferSize); + } else if (mQuirks & requiresAllocateBufferBit) { err = mOMX->allocateBufferWithBackup( - mNode, portIndex, mem, &buffer); + mNode, portIndex, mem, &info.mBufferID); } else { - err = mOMX->useBuffer(mNode, portIndex, mem, &buffer); + err = mOMX->useBuffer(mNode, portIndex, mem, &info.mBufferID); + } + + if (mem != NULL) { + info.mData = new ABuffer(mem->pointer(), def.nBufferSize); } - BufferInfo info; - info.mBufferID = buffer; - info.mStatus = BufferInfo::OWNED_BY_US; - info.mData = new ABuffer(mem->pointer(), def.nBufferSize); mBuffers[portIndex].push(info); } } @@ -840,7 +850,13 @@ status_t ACodec::configureCodec( || !msg->findInt32("sample-rate", &sampleRate)) { err = INVALID_OPERATION; } else { - err = setupAACCodec(encoder, numChannels, sampleRate, bitRate); + int32_t isADTS; + if (!msg->findInt32("is-adts", &isADTS)) { + isADTS = 0; + } + + err = setupAACCodec( + encoder, numChannels, sampleRate, bitRate, isADTS != 0); } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) { err = setupAMRCodec(encoder, false /* isWAMR */, bitRate); @@ -934,7 +950,11 @@ status_t ACodec::selectAudioPortFormat( status_t ACodec::setupAACCodec( bool encoder, - int32_t numChannels, int32_t sampleRate, int32_t bitRate) { + int32_t numChannels, int32_t sampleRate, int32_t bitRate, bool isADTS) { + if (encoder && isADTS) { + return -EINVAL; + } + status_t err = setupRawAudioFormat( encoder ? kPortIndexInput : kPortIndexOutput, sampleRate, @@ -1021,7 +1041,11 @@ status_t ACodec::setupAACCodec( profile.nChannels = numChannels; profile.nSampleRate = sampleRate; - profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS; + + profile.eAACStreamFormat = + isADTS + ? OMX_AUDIO_AACStreamFormatMP4ADTS + : OMX_AUDIO_AACStreamFormatMP4FF; return mOMX->setParameter( mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); @@ -2653,6 +2677,12 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) { observer->setNotificationMessage(notify); mCodec->mComponentName = componentName; + mCodec->mFlags = 0; + + if (componentName.endsWith(".secure")) { + mCodec->mFlags |= kFlagIsSecure; + } + mCodec->mQuirks = quirks; mCodec->mOMX = omx; mCodec->mNode = node; @@ -2701,6 +2731,7 @@ void ACodec::LoadedState::onShutdown(bool keepComponentAllocated) { mCodec->mNode = NULL; mCodec->mOMX.clear(); mCodec->mQuirks = 0; + mCodec->mFlags = 0; mCodec->mComponentName.clear(); mCodec->changeState(mCodec->mUninitializedState); diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 7d7bd7d..8948abb 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -57,7 +57,6 @@ LOCAL_SRC_FILES:= \ avc_utils.cpp \ LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ $(TOP)/frameworks/base/include/media/stagefright/timedtext \ $(TOP)/frameworks/native/include/media/hardware \ $(TOP)/frameworks/native/include/media/openmax \ @@ -69,7 +68,6 @@ LOCAL_C_INCLUDES:= \ LOCAL_SHARED_LIBRARIES := \ libbinder \ libcamera_client \ - libchromium_net \ libcrypto \ libcutils \ libdl \ @@ -102,14 +100,18 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_httplive \ libstagefright_id3 \ libFLAC \ - libstagefright_chromium_http \ + +ifneq ($(TARGET_BUILD_PDK), true) +LOCAL_STATIC_LIBRARIES += \ + libstagefright_chromium_http +LOCAL_SHARED_LIBRARIES += \ + libchromium_net +LOCAL_CPPFLAGS += -DCHROMIUM_AVAILABLE=1 +endif LOCAL_SHARED_LIBRARIES += libstlport include external/stlport/libstlport.mk -# TODO: Chromium is always available, so this flag can be removed. -LOCAL_CPPFLAGS += -DCHROMIUM_AVAILABLE=1 - LOCAL_SHARED_LIBRARIES += \ libstagefright_enc_common \ libstagefright_avc_common \ diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index a9e7f36..42b5c7e 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -22,10 +22,14 @@ #include "include/SoftwareRenderer.h" +#include <binder/IServiceManager.h> #include <gui/SurfaceTextureClient.h> +#include <media/ICrypto.h> +#include <media/IMediaPlayerService.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/ACodec.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> @@ -528,6 +532,12 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { info.mOwnedByClient = false; CHECK(msg->findBuffer(name.c_str(), &info.mData)); + if (portIndex == kPortIndexInput + && (mFlags & kFlagIsSecure)) { + info.mEncryptedData = + new ABuffer(info.mData->capacity()); + } + buffers->push_back(info); } @@ -742,6 +752,59 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { format->setInt32("encoder", true); } + if (flags & CONFIGURE_FLAG_SECURE) { + mFlags |= kFlagIsSecure; + + sp<IServiceManager> sm = defaultServiceManager(); + + sp<IBinder> binder = + sm->getService(String16("media.player")); + + sp<IMediaPlayerService> service = + interface_cast<IMediaPlayerService>(binder); + + CHECK(service != NULL); + + mCrypto = service->makeCrypto(); + + status_t err = mCrypto->initialize(); + + if (err == OK) { + sp<ABuffer> emm; + if (format->findBuffer("emm", &emm)) { + err = mCrypto->setEntitlementKey( + emm->data(), emm->size()); + } + } + + if (err == OK) { + sp<ABuffer> ecm; + if (format->findBuffer("ecm", &ecm)) { + CHECK_EQ(ecm->size(), 80u); + + // bytes 16..47 of the original ecm stream data. + err = mCrypto->setEntitlementControlMessage( + ecm->data() + 16, 32); + } + } + + if (err != OK) { + ALOGE("failed to instantiate crypto service."); + + mCrypto.clear(); + + setState(INITIALIZED); + + sp<AMessage> response = new AMessage; + response->setInt32("err", UNKNOWN_ERROR); + + response->postReply(mReplyID); + break; + } + } else { + mFlags &= ~kFlagIsSecure; + } + mCodec->initiateConfigureComponent(format); break; } @@ -983,7 +1046,10 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { for (size_t i = 0; i < srcBuffers.size(); ++i) { const BufferInfo &info = srcBuffers.itemAt(i); - dstBuffers->push_back(info.mData); + dstBuffers->push_back( + (portIndex == kPortIndexInput + && (mFlags & kFlagIsSecure)) + ? info.mEncryptedData : info.mData); } (new AMessage)->postReply(replyID); @@ -1037,10 +1103,15 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { } void MediaCodec::setState(State newState) { - if (newState == UNINITIALIZED) { + if (newState == INITIALIZED) { delete mSoftRenderer; mSoftRenderer = NULL; + if (mCrypto != NULL) { + mCrypto->terminate(); + mCrypto.clear(); + } + mNativeWindow.clear(); mOutputFormat.clear(); @@ -1150,6 +1221,43 @@ status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) { info->mData->meta()->setInt32("csd", true); } + if (mFlags & kFlagIsSecure) { + uint8_t iv[16]; + memset(iv, 0, sizeof(iv)); + + ssize_t outLength; + + if (mFlags & kFlagIsSoftwareCodec) { + outLength = mCrypto->decryptAudio( + (flags & BUFFER_FLAG_ENCRYPTED) ? iv : NULL, + (flags & BUFFER_FLAG_ENCRYPTED) ? sizeof(iv) : 0, + info->mEncryptedData->base() + offset, + size, + info->mData->base(), + info->mData->capacity()); + } else { + outLength = mCrypto->decryptVideo( + (flags & BUFFER_FLAG_ENCRYPTED) ? iv : NULL, + (flags & BUFFER_FLAG_ENCRYPTED) ? sizeof(iv) : 0, + info->mEncryptedData->base() + offset, + size, + info->mData->base(), + 0 /* offset */); + } + + if (outLength < 0) { + return outLength; + } + + if ((size_t)outLength > info->mEncryptedData->capacity()) { + return -ERANGE; + } + + info->mData->setRange(0, outLength); + } else if (flags & BUFFER_FLAG_ENCRYPTED) { + return -EINVAL; + } + reply->setBuffer("buffer", info->mData); reply->post(); diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp index afd4763..224ec33 100644 --- a/media/libstagefright/NuMediaExtractor.cpp +++ b/media/libstagefright/NuMediaExtractor.cpp @@ -107,6 +107,11 @@ status_t NuMediaExtractor::getTrackFormat( msg->setInt32("channel-count", numChannels); msg->setInt32("sample-rate", sampleRate); + + int32_t isADTS; + if (meta->findInt32(kKeyIsADTS, &isADTS)) { + msg->setInt32("is-adts", true); + } } int32_t maxInputSize; @@ -232,6 +237,20 @@ status_t NuMediaExtractor::getTrackFormat( msg->setBuffer("csd-1", buffer); } + if (meta->findData(kKeyEMM, &type, &data, &size)) { + sp<ABuffer> emm = new ABuffer(size); + memcpy(emm->data(), data, size); + + msg->setBuffer("emm", emm); + } + + if (meta->findData(kKeyECM, &type, &data, &size)) { + sp<ABuffer> ecm = new ABuffer(size); + memcpy(ecm->data(), data, size); + + msg->setBuffer("ecm", ecm); + } + *format = msg; return OK; @@ -267,13 +286,14 @@ status_t NuMediaExtractor::selectTrack(size_t index) { info->mFinalResult = OK; info->mSample = NULL; info->mSampleTimeUs = -1ll; - info->mFlags = 0; + info->mSampleFlags = 0; + info->mTrackFlags = 0; const char *mime; CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime)); if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) { - info->mFlags |= kIsVorbis; + info->mTrackFlags |= kIsVorbis; } return OK; @@ -288,6 +308,7 @@ void NuMediaExtractor::releaseTrackSamples() { info->mSample = NULL; info->mSampleTimeUs = -1ll; + info->mSampleFlags = 0; } } } @@ -306,6 +327,7 @@ ssize_t NuMediaExtractor::fetchTrackSamples(int64_t seekTimeUs) { info->mSample->release(); info->mSample = NULL; info->mSampleTimeUs = -1ll; + info->mSampleFlags = 0; } } else if (info->mFinalResult != OK) { continue; @@ -323,11 +345,25 @@ ssize_t NuMediaExtractor::fetchTrackSamples(int64_t seekTimeUs) { info->mFinalResult = err; info->mSampleTimeUs = -1ll; + info->mSampleFlags = 0; continue; } else { CHECK(info->mSample != NULL); CHECK(info->mSample->meta_data()->findInt64( kKeyTime, &info->mSampleTimeUs)); + + info->mSampleFlags = 0; + + int32_t val; + if (info->mSample->meta_data()->findInt32( + kKeyIsSyncFrame, &val) && val != 0) { + info->mSampleFlags |= SAMPLE_FLAG_SYNC; + } + + if (info->mSample->meta_data()->findInt32( + kKeyScrambling, &val) && val != 0) { + info->mSampleFlags |= SAMPLE_FLAG_ENCRYPTED; + } } } @@ -371,7 +407,7 @@ status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) { size_t sampleSize = info->mSample->range_length(); - if (info->mFlags & kIsVorbis) { + if (info->mTrackFlags & kIsVorbis) { // Each sample's data is suffixed by the number of page samples // or -1 if not available. sampleSize += sizeof(int32_t); @@ -387,7 +423,7 @@ status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) { memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length()); - if (info->mFlags & kIsVorbis) { + if (info->mTrackFlags & kIsVorbis) { int32_t numPageSamples; if (!info->mSample->meta_data()->findInt32( kKeyValidSamples, &numPageSamples)) { @@ -430,4 +466,17 @@ status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) { return OK; } +status_t NuMediaExtractor::getSampleFlags(uint32_t *sampleFlags) { + ssize_t minIndex = fetchTrackSamples(); + + if (minIndex < 0) { + return ERROR_END_OF_STREAM; + } + + TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); + *sampleFlags = info->mSampleFlags; + + return OK; +} + } // namespace android diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 8b6e9d5..9769f21 100755 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -515,7 +515,12 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) { CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); - status_t err = setAACFormat(numChannels, sampleRate, bitRate); + int32_t isADTS; + if (!meta->findInt32(kKeyIsADTS, &isADTS)) { + isADTS = false; + } + + status_t err = setAACFormat(numChannels, sampleRate, bitRate, isADTS); if (err != OK) { CODEC_LOGE("setAACFormat() failed (err = %d)", err); return err; @@ -3386,11 +3391,17 @@ void OMXCodec::setAMRFormat(bool isWAMR, int32_t bitRate) { } } -status_t OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t bitRate) { - if (numChannels > 2) +status_t OMXCodec::setAACFormat( + int32_t numChannels, int32_t sampleRate, int32_t bitRate, bool isADTS) { + if (numChannels > 2) { ALOGW("Number of channels: (%d) \n", numChannels); + } if (mIsEncoder) { + if (isADTS) { + return -EINVAL; + } + //////////////// input port //////////////////// setRawAudioFormat(kPortIndexInput, sampleRate, numChannels); @@ -3445,7 +3456,9 @@ status_t OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t &profile, sizeof(profile)); if (err != OK) { - CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed (err = %d)", err); + CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed " + "(err = %d)", + err); return err; } } else { @@ -3459,13 +3472,19 @@ status_t OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t profile.nChannels = numChannels; profile.nSampleRate = sampleRate; - profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS; + + profile.eAACStreamFormat = + isADTS + ? OMX_AUDIO_AACStreamFormatMP4ADTS + : OMX_AUDIO_AACStreamFormatMP4FF; err = mOMX->setParameter( mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); if (err != OK) { - CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed (err = %d)", err); + CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed " + "(err = %d)", + err); return err; } } diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp index 6d345bb..9df15eb 100644 --- a/media/libstagefright/TimedEventQueue.cpp +++ b/media/libstagefright/TimedEventQueue.cpp @@ -31,10 +31,6 @@ #include <media/stagefright/foundation/ADebug.h> -#ifdef ANDROID_SIMULATOR -#include <jni.h> -#endif - namespace android { TimedEventQueue::TimedEventQueue() @@ -193,27 +189,10 @@ int64_t TimedEventQueue::getRealTimeUs() { // static void *TimedEventQueue::ThreadWrapper(void *me) { -#ifdef ANDROID_SIMULATOR - // The simulator runs everything as one process, so any - // Binder calls happen on this thread instead of a thread - // in another process. We therefore need to make sure that - // this thread can do calls into interpreted code. - // On the device this is not an issue because the remote - // thread will already be set up correctly for this. - JavaVM *vm; - int numvms; - JNI_GetCreatedJavaVMs(&vm, 1, &numvms); - JNIEnv *env; - vm->AttachCurrentThread(&env, NULL); -#endif - androidSetThreadPriority(0, ANDROID_PRIORITY_FOREGROUND); static_cast<TimedEventQueue *>(me)->threadEntry(); -#ifdef ANDROID_SIMULATOR - vm->DetachCurrentThread(); -#endif return NULL; } diff --git a/media/libstagefright/chromium_http/Android.mk b/media/libstagefright/chromium_http/Android.mk index e37b4a8..d595686 100644 --- a/media/libstagefright/chromium_http/Android.mk +++ b/media/libstagefright/chromium_http/Android.mk @@ -1,5 +1,6 @@ LOCAL_PATH:= $(call my-dir) +ifneq ($(TARGET_BUILD_PDK), true) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ @@ -8,7 +9,6 @@ LOCAL_SRC_FILES:= \ support.cpp LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax \ external/chromium \ @@ -22,3 +22,4 @@ include external/stlport/libstlport.mk LOCAL_MODULE:= libstagefright_chromium_http include $(BUILD_STATIC_LIBRARY) +endif diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.cpp b/media/libstagefright/codecs/aacdec/SoftAAC.cpp index ea6c360..90f96c6 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC.cpp +++ b/media/libstagefright/codecs/aacdec/SoftAAC.cpp @@ -23,6 +23,7 @@ #include "pvmp4audiodecoder_api.h" #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/hexdump.h> namespace android { @@ -42,6 +43,7 @@ SoftAAC::SoftAAC( OMX_COMPONENTTYPE **component) : SimpleSoftOMXComponent(name, callbacks, appData, component), mConfig(new tPVMP4AudioDecoderExternal), + mIsADTS(false), mDecoderBuf(NULL), mInputBufferCount(0), mUpsamplingFactor(2), @@ -140,7 +142,12 @@ OMX_ERRORTYPE SoftAAC::internalGetParameter( aacParams->nAACtools = 0; aacParams->nAACERtools = 0; aacParams->eAACProfile = OMX_AUDIO_AACObjectMain; - aacParams->eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF; + + aacParams->eAACStreamFormat = + mIsADTS + ? OMX_AUDIO_AACStreamFormatMP4ADTS + : OMX_AUDIO_AACStreamFormatMP4FF; + aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo; if (!isConfigured()) { @@ -215,6 +222,15 @@ OMX_ERRORTYPE SoftAAC::internalSetParameter( return OMX_ErrorUndefined; } + if (aacParams->eAACStreamFormat == OMX_AUDIO_AACStreamFormatMP4FF) { + mIsADTS = false; + } else if (aacParams->eAACStreamFormat + == OMX_AUDIO_AACStreamFormatMP4ADTS) { + mIsADTS = true; + } else { + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; } @@ -299,8 +315,35 @@ void SoftAAC::onQueueFilled(OMX_U32 portIndex) { mNumSamplesOutput = 0; } - mConfig->pInputBuffer = inHeader->pBuffer + inHeader->nOffset; - mConfig->inputBufferCurrentLength = inHeader->nFilledLen; + if (mIsADTS) { + // skip 30 bits, aac_frame_length follows. + // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll????? + + const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset; + + CHECK_GE(inHeader->nFilledLen, 7); + + bool protectionAbsent = (adtsHeader[1] & 1); + + unsigned aac_frame_length = + ((adtsHeader[3] & 3) << 11) + | (adtsHeader[4] << 3) + | (adtsHeader[5] >> 5); + + CHECK_GE(inHeader->nFilledLen, aac_frame_length); + + size_t headerSize = (protectionAbsent ? 7 : 9); + + mConfig->pInputBuffer = (UChar *)adtsHeader + headerSize; + mConfig->inputBufferCurrentLength = aac_frame_length - headerSize; + + inHeader->nOffset += headerSize; + inHeader->nFilledLen -= headerSize; + } else { + mConfig->pInputBuffer = inHeader->pBuffer + inHeader->nOffset; + mConfig->inputBufferCurrentLength = inHeader->nFilledLen; + } + mConfig->inputBufferMaxLength = 0; mConfig->inputBufferUsedLength = 0; mConfig->remainderBits = 0; diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.h b/media/libstagefright/codecs/aacdec/SoftAAC.h index 963fd27..da0b8ed 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC.h +++ b/media/libstagefright/codecs/aacdec/SoftAAC.h @@ -49,6 +49,7 @@ private: }; tPVMP4AudioDecoderExternal *mConfig; + bool mIsADTS; void *mDecoderBuf; size_t mInputBufferCount; diff --git a/media/libstagefright/foundation/AString.cpp b/media/libstagefright/foundation/AString.cpp index 61b76cf..dee786d 100644 --- a/media/libstagefright/foundation/AString.cpp +++ b/media/libstagefright/foundation/AString.cpp @@ -310,6 +310,16 @@ bool AString::startsWith(const char *prefix) const { return !strncmp(mData, prefix, strlen(prefix)); } +bool AString::endsWith(const char *suffix) const { + size_t suffixLen = strlen(suffix); + + if (mSize < suffixLen) { + return false; + } + + return !strcmp(mData + mSize - suffixLen, suffix); +} + AString StringPrintf(const char *format, ...) { va_list ap; va_start(ap, format); diff --git a/media/libstagefright/httplive/Android.mk b/media/libstagefright/httplive/Android.mk index a5990c3..90cb448 100644 --- a/media/libstagefright/httplive/Android.mk +++ b/media/libstagefright/httplive/Android.mk @@ -8,7 +8,6 @@ LOCAL_SRC_FILES:= \ M3UParser.cpp \ LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ $(TOP)/frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax \ $(TOP)/external/openssl/include diff --git a/media/libstagefright/matroska/Android.mk b/media/libstagefright/matroska/Android.mk index e67da4c..2cccb4f 100644 --- a/media/libstagefright/matroska/Android.mk +++ b/media/libstagefright/matroska/Android.mk @@ -5,7 +5,6 @@ LOCAL_SRC_FILES:= \ MatroskaExtractor.cpp LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ $(TOP)/external/libvpx/mkvparser \ $(TOP)/frameworks/native/include/media/openmax \ diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk index ac4c2a1..eaa139d 100644 --- a/media/libstagefright/mpeg2ts/Android.mk +++ b/media/libstagefright/mpeg2ts/Android.mk @@ -10,7 +10,6 @@ LOCAL_SRC_FILES:= \ MPEG2TSExtractor.cpp \ LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ $(TOP)/frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index d708ba6..e1ac53c 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -117,6 +117,12 @@ status_t AnotherPacketSource::read( mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs); + int32_t scrambling; + if (buffer->meta()->findInt32("scrambling", &scrambling) + && scrambling != 0) { + mediaBuffer->meta_data()->setInt32(kKeyScrambling, scrambling); + } + *out = mediaBuffer; return OK; } diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk index 083c7ef..d20ecb6 100644 --- a/media/libstagefright/omx/Android.mk +++ b/media/libstagefright/omx/Android.mk @@ -1,8 +1,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_C_INCLUDES += $(JNI_H_INCLUDE) - LOCAL_SRC_FILES:= \ OMX.cpp \ OMXMaster.cpp \ diff --git a/media/libstagefright/omx/tests/Android.mk b/media/libstagefright/omx/tests/Android.mk index 07d47a8..6aa7470 100644 --- a/media/libstagefright/omx/tests/Android.mk +++ b/media/libstagefright/omx/tests/Android.mk @@ -8,7 +8,6 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright libbinder libmedia libutils libstagefright_foundation LOCAL_C_INCLUDES := \ - $(JNI_H_INCLUDE) \ frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk index b3bc37c..e0fe59b 100644 --- a/media/libstagefright/rtsp/Android.mk +++ b/media/libstagefright/rtsp/Android.mk @@ -18,7 +18,6 @@ LOCAL_SRC_FILES:= \ ASessionDescription.cpp \ LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ $(TOP)/frameworks/base/media/libstagefright/include \ $(TOP)/frameworks/native/include/media/openmax \ $(TOP)/external/openssl/include @@ -45,7 +44,6 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_rtsp LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk index d2d5f7b..58ef9e3 100644 --- a/media/libstagefright/timedtext/Android.mk +++ b/media/libstagefright/timedtext/Android.mk @@ -11,7 +11,6 @@ LOCAL_SRC_FILES:= \ LOCAL_CFLAGS += -Wno-multichar LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ $(TOP)/frameworks/base/include/media/stagefright/timedtext \ $(TOP)/frameworks/base/media/libstagefright |