diff options
Diffstat (limited to 'media/libmedia')
55 files changed, 2929 insertions, 1041 deletions
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 6c585fb..a3c3d3c 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -7,6 +7,9 @@ LOCAL_SRC_FILES:= \ LOCAL_MODULE:= libmedia_helper LOCAL_MODULE_TAGS := optional +LOCAL_C_FLAGS += -Werror -Wno-error=deprecated-declarations -Wall +LOCAL_CLANG := true + include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) @@ -19,6 +22,7 @@ LOCAL_SRC_FILES:= \ IAudioTrack.cpp \ IAudioRecord.cpp \ ICrypto.cpp \ + IDataSource.cpp \ IDrm.cpp \ IDrmClient.cpp \ IHDCP.cpp \ @@ -36,6 +40,8 @@ LOCAL_SRC_FILES:= \ IMediaRecorder.cpp \ IRemoteDisplay.cpp \ IRemoteDisplayClient.cpp \ + IResourceManagerClient.cpp \ + IResourceManagerService.cpp \ IStreamSource.cpp \ MediaCodecInfo.cpp \ Metadata.cpp \ @@ -53,6 +59,8 @@ LOCAL_SRC_FILES:= \ CharacterEncodingDetector.cpp \ IMediaDeathNotifier.cpp \ MediaProfiles.cpp \ + MediaResource.cpp \ + MediaResourcePolicy.cpp \ IEffect.cpp \ IEffectClient.cpp \ AudioEffect.cpp \ @@ -61,15 +69,11 @@ LOCAL_SRC_FILES:= \ StringArray.cpp \ AudioPolicy.cpp -LOCAL_SRC_FILES += ../libnbaio/roundup.c - LOCAL_SHARED_LIBRARIES := \ libui liblog libcutils libutils libbinder libsonivox libicuuc libicui18n libexpat \ libcamera_client libstagefright_foundation \ libgui libdl libaudioutils libnbaio -LOCAL_STATIC_LIBRARIES += libinstantssq - LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper LOCAL_MODULE:= libmedia @@ -83,14 +87,8 @@ LOCAL_C_INCLUDES := \ $(call include-path-for, audio-effects) \ $(call include-path-for, audio-utils) -include $(BUILD_SHARED_LIBRARY) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES += SingleStateQueue.cpp -LOCAL_CFLAGS += -DSINGLE_STATE_QUEUE_INSTANTIATIONS='"SingleStateQueueInstantiations.cpp"' +LOCAL_CFLAGS += -Werror -Wno-error=deprecated-declarations -Wall +LOCAL_CLANG := true -LOCAL_MODULE := libinstantssq -LOCAL_MODULE_TAGS := optional +include $(BUILD_SHARED_LIBRARY) -include $(BUILD_STATIC_LIBRARY) diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp index af103c1..ff82544 100644 --- a/media/libmedia/AudioEffect.cpp +++ b/media/libmedia/AudioEffect.cpp @@ -35,13 +35,14 @@ namespace android { // --------------------------------------------------------------------------- -AudioEffect::AudioEffect() - : mStatus(NO_INIT) +AudioEffect::AudioEffect(const String16& opPackageName) + : mStatus(NO_INIT), mOpPackageName(opPackageName) { } AudioEffect::AudioEffect(const effect_uuid_t *type, + const String16& opPackageName, const effect_uuid_t *uuid, int32_t priority, effect_callback_t cbf, @@ -49,12 +50,13 @@ AudioEffect::AudioEffect(const effect_uuid_t *type, int sessionId, audio_io_handle_t io ) - : mStatus(NO_INIT) + : mStatus(NO_INIT), mOpPackageName(opPackageName) { mStatus = set(type, uuid, priority, cbf, user, sessionId, io); } AudioEffect::AudioEffect(const char *typeStr, + const String16& opPackageName, const char *uuidStr, int32_t priority, effect_callback_t cbf, @@ -62,7 +64,7 @@ AudioEffect::AudioEffect(const char *typeStr, int sessionId, audio_io_handle_t io ) - : mStatus(NO_INIT) + : mStatus(NO_INIT), mOpPackageName(opPackageName) { effect_uuid_t type; effect_uuid_t *pType = NULL; @@ -128,16 +130,18 @@ status_t AudioEffect::set(const effect_uuid_t *type, mIEffectClient = new EffectClient(this); iEffect = audioFlinger->createEffect((effect_descriptor_t *)&mDescriptor, - mIEffectClient, priority, io, mSessionId, &mStatus, &mId, &enabled); + mIEffectClient, priority, io, mSessionId, mOpPackageName, &mStatus, &mId, &enabled); if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) { ALOGE("set(): AudioFlinger could not create effect, status: %d", mStatus); + if (iEffect == 0) { + mStatus = NO_INIT; + } return mStatus; } mEnabled = (volatile int32_t)enabled; - mIEffect = iEffect; cblk = iEffect->getCblk(); if (cblk == 0) { mStatus = NO_INIT; @@ -145,6 +149,7 @@ status_t AudioEffect::set(const effect_uuid_t *type, return mStatus; } + mIEffect = iEffect; mCblkMemory = cblk; mCblk = static_cast<effect_param_cblk_t*>(cblk->pointer()); int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int); @@ -175,11 +180,11 @@ AudioEffect::~AudioEffect() mIEffect->disconnect(); IInterface::asBinder(mIEffect)->unlinkToDeath(mIEffectClient); } + mIEffect.clear(); + mCblkMemory.clear(); + mIEffectClient.clear(); IPCThreadState::self()->flushCommands(); } - mIEffect.clear(); - mIEffectClient.clear(); - mCblkMemory.clear(); } @@ -486,4 +491,4 @@ status_t AudioEffect::guidToString(const effect_uuid_t *guid, char *str, size_t } -}; // namespace android +} // namespace android diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp index 33dbf0b..8c8cf45 100644 --- a/media/libmedia/AudioParameter.cpp +++ b/media/libmedia/AudioParameter.cpp @@ -180,4 +180,4 @@ status_t AudioParameter::getAt(size_t index, String8& key, String8& value) } } -}; // namespace android +} // namespace android diff --git a/media/libmedia/AudioPolicy.cpp b/media/libmedia/AudioPolicy.cpp index d2d0971..9d07011 100644 --- a/media/libmedia/AudioPolicy.cpp +++ b/media/libmedia/AudioPolicy.cpp @@ -68,6 +68,7 @@ status_t AudioMix::readFromParcel(Parcel *parcel) mFormat.format = (audio_format_t)parcel->readInt32(); mRouteFlags = parcel->readInt32(); mRegistrationId = parcel->readString8(); + mCbFlags = (uint32_t)parcel->readInt32(); size_t size = (size_t)parcel->readInt32(); if (size > MAX_CRITERIA_PER_MIX) { size = MAX_CRITERIA_PER_MIX; @@ -89,6 +90,7 @@ status_t AudioMix::writeToParcel(Parcel *parcel) const parcel->writeInt32(mFormat.format); parcel->writeInt32(mRouteFlags); parcel->writeString8(mRegistrationId); + parcel->writeInt32(mCbFlags); size_t size = mCriteria.size(); if (size > MAX_CRITERIA_PER_MIX) { size = MAX_CRITERIA_PER_MIX; @@ -112,4 +114,4 @@ status_t AudioMix::writeToParcel(Parcel *parcel) const return NO_ERROR; } -}; // namespace android +} // namespace android diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 07ca14f..011b31f 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -65,9 +65,10 @@ status_t AudioRecord::getMinFrameCount( // --------------------------------------------------------------------------- -AudioRecord::AudioRecord() - : mStatus(NO_INIT), mSessionId(AUDIO_SESSION_ALLOCATE), - mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT) +AudioRecord::AudioRecord(const String16 &opPackageName) + : mStatus(NO_INIT), mOpPackageName(opPackageName), mSessionId(AUDIO_SESSION_ALLOCATE), + mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), + mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE) { } @@ -76,6 +77,7 @@ AudioRecord::AudioRecord( uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, + const String16& opPackageName, size_t frameCount, callback_t cbf, void* user, @@ -83,15 +85,20 @@ AudioRecord::AudioRecord( int sessionId, transfer_type transferType, audio_input_flags_t flags, + int uid, + pid_t pid, const audio_attributes_t* pAttributes) - : mStatus(NO_INIT), mSessionId(AUDIO_SESSION_ALLOCATE), + : mStatus(NO_INIT), + mOpPackageName(opPackageName), + mSessionId(AUDIO_SESSION_ALLOCATE), mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), - mProxy(NULL) + mProxy(NULL), + mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE) { mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user, notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags, - pAttributes); + uid, pid, pAttributes); } AudioRecord::~AudioRecord() @@ -107,12 +114,18 @@ AudioRecord::~AudioRecord() mAudioRecordThread->requestExitAndWait(); mAudioRecordThread.clear(); } + // No lock here: worst case we remove a NULL callback which will be a nop + if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) { + AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput); + } IInterface::asBinder(mAudioRecord)->unlinkToDeath(mDeathNotifier, this); mAudioRecord.clear(); mCblkMemory.clear(); mBufferMemory.clear(); IPCThreadState::self()->flushCommands(); - AudioSystem::releaseAudioSessionId(mSessionId, -1); + ALOGV("~AudioRecord, releasing session id %d", + mSessionId); + AudioSystem::releaseAudioSessionId(mSessionId, -1 /*pid*/); } } @@ -129,12 +142,15 @@ status_t AudioRecord::set( int sessionId, transfer_type transferType, audio_input_flags_t flags, + int uid, + pid_t pid, const audio_attributes_t* pAttributes) { ALOGV("set(): inputSource %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, " - "notificationFrames %u, sessionId %d, transferType %d, flags %#x", + "notificationFrames %u, sessionId %d, transferType %d, flags %#x, opPackageName %s " + "uid %d, pid %d", inputSource, sampleRate, format, channelMask, frameCount, notificationFrames, - sessionId, transferType, flags); + sessionId, transferType, flags, String8(mOpPackageName).string(), uid, pid); switch (transferType) { case TRANSFER_DEFAULT: @@ -159,8 +175,6 @@ status_t AudioRecord::set( } mTransfer = transferType; - AutoMutex lock(mLock); - // invariant that mAudioRecord != 0 is true only after set() returns successfully if (mAudioRecord != 0) { ALOGE("Track already in use"); @@ -189,13 +203,9 @@ status_t AudioRecord::set( } // validate parameters - if (!audio_is_valid_format(format)) { - ALOGE("Invalid format %#x", format); - return BAD_VALUE; - } - // Temporary restriction: AudioFlinger currently supports 16-bit PCM only - if (format != AUDIO_FORMAT_PCM_16_BIT) { - ALOGE("Format %#x is not supported", format); + // AudioFlinger capture only supports linear PCM + if (!audio_is_valid_format(format) || !audio_is_linear_pcm(format)) { + ALOGE("Format %#x is not linear pcm", format); return BAD_VALUE; } mFormat = format; @@ -227,16 +237,30 @@ status_t AudioRecord::set( } ALOGV("set(): mSessionId %d", mSessionId); + int callingpid = IPCThreadState::self()->getCallingPid(); + int mypid = getpid(); + if (uid == -1 || (callingpid != mypid)) { + mClientUid = IPCThreadState::self()->getCallingUid(); + } else { + mClientUid = uid; + } + if (pid == -1 || (callingpid != mypid)) { + mClientPid = callingpid; + } else { + mClientPid = pid; + } + mFlags = flags; mCbf = cbf; if (cbf != NULL) { mAudioRecordThread = new AudioRecordThread(*this, threadCanCallJava); mAudioRecordThread->run("AudioRecord", ANDROID_PRIORITY_AUDIO); + // thread begins in paused state, and will not reference us until start() } // create the IAudioRecord - status_t status = openRecord_l(0 /*epoch*/); + status_t status = openRecord_l(0 /*epoch*/, mOpPackageName); if (status != NO_ERROR) { if (mAudioRecordThread != 0) { @@ -284,9 +308,10 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession) mNewPosition = mProxy->getPosition() + mUpdatePeriod; int32_t flags = android_atomic_acquire_load(&mCblk->mFlags); + mActive = true; + status_t status = NO_ERROR; if (!(flags & CBLK_INVALID)) { - ALOGV("mAudioRecord->start()"); status = mAudioRecord->start(event, triggerSession); if (status == DEAD_OBJECT) { flags |= CBLK_INVALID; @@ -297,9 +322,9 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession) } if (status != NO_ERROR) { + mActive = false; ALOGE("start() status %d", status); } else { - mActive = true; sp<AudioRecordThread> t = mAudioRecordThread; if (t != 0) { t->resume(); @@ -352,6 +377,10 @@ status_t AudioRecord::setMarkerPosition(uint32_t marker) mMarkerPosition = marker; mMarkerReached = false; + sp<AudioRecordThread> t = mAudioRecordThread; + if (t != 0) { + t->wake(); + } return NO_ERROR; } @@ -378,6 +407,10 @@ status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod) mNewPosition = mProxy->getPosition() + updatePeriod; mUpdatePeriod = updatePeriod; + sp<AudioRecordThread> t = mAudioRecordThread; + if (t != 0) { + t->wake(); + } return NO_ERROR; } @@ -408,15 +441,42 @@ status_t AudioRecord::getPosition(uint32_t *position) const uint32_t AudioRecord::getInputFramesLost() const { // no need to check mActive, because if inactive this will return 0, which is what we want - return AudioSystem::getInputFramesLost(getInput()); + return AudioSystem::getInputFramesLost(getInputPrivate()); +} + +// ---- Explicit Routing --------------------------------------------------- +status_t AudioRecord::setInputDevice(audio_port_handle_t deviceId) { + AutoMutex lock(mLock); + if (mSelectedDeviceId != deviceId) { + mSelectedDeviceId = deviceId; + // stop capture so that audio policy manager does not reject the new instance start request + // as only one capture can be active at a time. + if (mAudioRecord != 0 && mActive) { + mAudioRecord->stop(); + } + android_atomic_or(CBLK_INVALID, &mCblk->mFlags); + } + return NO_ERROR; +} + +audio_port_handle_t AudioRecord::getInputDevice() { + AutoMutex lock(mLock); + return mSelectedDeviceId; +} + +audio_port_handle_t AudioRecord::getRoutedDeviceId() { + AutoMutex lock(mLock); + if (mInput == AUDIO_IO_HANDLE_NONE) { + return AUDIO_PORT_HANDLE_NONE; + } + return AudioSystem::getDeviceIdForIo(mInput); } // ------------------------------------------------------------------------- // must be called with mLock held -status_t AudioRecord::openRecord_l(size_t epoch) +status_t AudioRecord::openRecord_l(size_t epoch, const String16& opPackageName) { - status_t status; const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger(); if (audioFlinger == 0) { ALOGE("Could not get audioflinger"); @@ -431,12 +491,16 @@ status_t AudioRecord::openRecord_l(size_t epoch) } // Client can only express a preference for FAST. Server will perform additional tests. - if ((mFlags & AUDIO_INPUT_FLAG_FAST) && !( - // use case: callback transfer mode - (mTransfer == TRANSFER_CALLBACK) && + if ((mFlags & AUDIO_INPUT_FLAG_FAST) && !(( + // either of these use cases: + // use case 1: callback transfer mode + (mTransfer == TRANSFER_CALLBACK) || + // use case 2: obtain/release mode + (mTransfer == TRANSFER_OBTAIN)) && // matching sample rate (mSampleRate == afSampleRate))) { - ALOGW("AUDIO_INPUT_FLAG_FAST denied by client"); + ALOGW("AUDIO_INPUT_FLAG_FAST denied by client; transfer %d, track %u Hz, primary %u Hz", + mTransfer, mSampleRate, afSampleRate); // once denied, do not request again if IAudioRecord is re-created mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST); } @@ -451,9 +515,16 @@ status_t AudioRecord::openRecord_l(size_t epoch) } } + if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) { + AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput); + } + audio_io_handle_t input; - status = AudioSystem::getInputForAttr(&mAttributes, &input, (audio_session_t)mSessionId, - mSampleRate, mFormat, mChannelMask, mFlags); + status_t status = AudioSystem::getInputForAttr(&mAttributes, &input, + (audio_session_t)mSessionId, + IPCThreadState::self()->getCallingUid(), + mSampleRate, mFormat, mChannelMask, + mFlags, mSelectedDeviceId); if (status != NO_ERROR) { ALOGE("Could not get audio input for record source %d, sample rate %u, format %#x, " @@ -476,11 +547,14 @@ status_t AudioRecord::openRecord_l(size_t epoch) sp<IMemory> iMem; // for cblk sp<IMemory> bufferMem; sp<IAudioRecord> record = audioFlinger->openRecord(input, - mSampleRate, mFormat, + mSampleRate, + mFormat, mChannelMask, + opPackageName, &temp, &trackFlags, tid, + mClientUid, &mSessionId, ¬ificationFrames, iMem, @@ -577,6 +651,10 @@ status_t AudioRecord::openRecord_l(size_t epoch) mDeathNotifier = new DeathNotifier(this); IInterface::asBinder(mAudioRecord)->linkToDeath(mDeathNotifier, this); + if (mDeviceCallback != 0) { + AudioSystem::addAudioDeviceCallback(mDeviceCallback, mInput); + } + return NO_ERROR; } @@ -588,15 +666,21 @@ release: return status; } -status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) +status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount, size_t *nonContig) { if (audioBuffer == NULL) { + if (nonContig != NULL) { + *nonContig = 0; + } return BAD_VALUE; } if (mTransfer != TRANSFER_OBTAIN) { audioBuffer->frameCount = 0; audioBuffer->size = 0; audioBuffer->raw = NULL; + if (nonContig != NULL) { + *nonContig = 0; + } return INVALID_OPERATION; } @@ -615,7 +699,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) ALOGE("%s invalid waitCount %d", __func__, waitCount); requested = NULL; } - return obtainBuffer(audioBuffer, requested); + return obtainBuffer(audioBuffer, requested, NULL /*elapsed*/, nonContig); } status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested, @@ -684,9 +768,9 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, const struct timespec *r return status; } -void AudioRecord::releaseBuffer(Buffer* audioBuffer) +void AudioRecord::releaseBuffer(const Buffer* audioBuffer) { - // all TRANSFER_* are valid + // FIXME add error checking on mode, by adding an internal version size_t stepCount = audioBuffer->size / mFrameSize; if (stepCount == 0) { @@ -704,7 +788,7 @@ void AudioRecord::releaseBuffer(Buffer* audioBuffer) // the server does not automatically disable recorder on overrun, so no need to restart } -audio_io_handle_t AudioRecord::getInput() const +audio_io_handle_t AudioRecord::getInputPrivate() const { AutoMutex lock(mLock); return mInput; @@ -712,7 +796,7 @@ audio_io_handle_t AudioRecord::getInput() const // ------------------------------------------------------------------------- -ssize_t AudioRecord::read(void* buffer, size_t userSize) +ssize_t AudioRecord::read(void* buffer, size_t userSize, bool blocking) { if (mTransfer != TRANSFER_SYNC) { return INVALID_OPERATION; @@ -731,7 +815,8 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize) while (userSize >= mFrameSize) { audioBuffer.frameCount = userSize / mFrameSize; - status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever); + status_t err = obtainBuffer(&audioBuffer, + blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking); if (err < 0) { if (read > 0) { break; @@ -863,8 +948,11 @@ nsecs_t AudioRecord::processAudioBuffer() if (!markerReached && position < markerPosition) { minFrames = markerPosition - position; } - if (updatePeriod > 0 && updatePeriod < minFrames) { - minFrames = updatePeriod; + if (updatePeriod > 0) { + uint32_t remaining = newPosition - position; + if (remaining < minFrames) { + minFrames = remaining; + } } // If > 0, poll periodically to recover from a stuck server. A good value is 2. @@ -990,14 +1078,13 @@ status_t AudioRecord::restoreRecord_l(const char *from) { ALOGW("dead IAudioRecord, creating a new one from %s()", from); ++mSequence; - status_t result; // if the new IAudioRecord is created, openRecord_l() will modify the // following member variables: mAudioRecord, mCblkMemory, mCblk, mBufferMemory. // It will also delete the strong references on previous IAudioRecord and IMemory size_t position = mProxy->getPosition(); mNewPosition = position + mUpdatePeriod; - result = openRecord_l(position); + status_t result = openRecord_l(position, mOpPackageName); if (result == NO_ERROR) { if (mActive) { // callback thread or sync event hasn't changed @@ -1013,6 +1100,48 @@ status_t AudioRecord::restoreRecord_l(const char *from) return result; } +status_t AudioRecord::addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback) +{ + if (callback == 0) { + ALOGW("%s adding NULL callback!", __FUNCTION__); + return BAD_VALUE; + } + AutoMutex lock(mLock); + if (mDeviceCallback == callback) { + ALOGW("%s adding same callback!", __FUNCTION__); + return INVALID_OPERATION; + } + status_t status = NO_ERROR; + if (mInput != AUDIO_IO_HANDLE_NONE) { + if (mDeviceCallback != 0) { + ALOGW("%s callback already present!", __FUNCTION__); + AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput); + } + status = AudioSystem::addAudioDeviceCallback(callback, mInput); + } + mDeviceCallback = callback; + return status; +} + +status_t AudioRecord::removeAudioDeviceCallback( + const sp<AudioSystem::AudioDeviceCallback>& callback) +{ + if (callback == 0) { + ALOGW("%s removing NULL callback!", __FUNCTION__); + return BAD_VALUE; + } + AutoMutex lock(mLock); + if (mDeviceCallback != callback) { + ALOGW("%s removing different callback!", __FUNCTION__); + return INVALID_OPERATION; + } + if (mInput != AUDIO_IO_HANDLE_NONE) { + AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput); + } + mDeviceCallback = 0; + return NO_ERROR; +} + // ========================================================================= void AudioRecord::DeathNotifier::binderDied(const wp<IBinder>& who __unused) @@ -1069,8 +1198,8 @@ bool AudioRecord::AudioRecordThread::threadLoop() case NS_NEVER: return false; case NS_WHENEVER: - // FIXME increase poll interval, or make event-driven - ns = 1000000000LL; + // Event driven: call wake() when callback notifications conditions change. + ns = INT64_MAX; // fall through default: LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %" PRId64, ns); @@ -1103,6 +1232,21 @@ void AudioRecord::AudioRecordThread::resume() } } +void AudioRecord::AudioRecordThread::wake() +{ + AutoMutex _l(mMyLock); + if (!mPaused) { + // wake() might be called while servicing a callback - ignore the next + // pause time and call processAudioBuffer. + mIgnoreNextPausedInt = true; + if (mPausedInt && mPausedNs > 0) { + // audio record is active and internally paused with timeout. + mPausedInt = false; + mMyCond.signal(); + } + } +} + void AudioRecord::AudioRecordThread::pauseInternal(nsecs_t ns) { AutoMutex _l(mMyLock); @@ -1112,4 +1256,4 @@ void AudioRecord::AudioRecordThread::pauseInternal(nsecs_t ns) // ------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 9cae21c..3bfb09a 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -32,23 +32,12 @@ namespace android { // client singleton for AudioFlinger binder interface Mutex AudioSystem::gLock; -Mutex AudioSystem::gLockCache; Mutex AudioSystem::gLockAPS; -Mutex AudioSystem::gLockAPC; sp<IAudioFlinger> AudioSystem::gAudioFlinger; sp<AudioSystem::AudioFlingerClient> AudioSystem::gAudioFlingerClient; audio_error_callback AudioSystem::gAudioErrorCallback = NULL; +dynamic_policy_callback AudioSystem::gDynPolicyCallback = NULL; -// Cached values for output handles -DefaultKeyedVector<audio_io_handle_t, AudioSystem::OutputDescriptor *> AudioSystem::gOutputs(NULL); - -// Cached values for recording queries, all protected by gLock -uint32_t AudioSystem::gPrevInSamplingRate; -audio_format_t AudioSystem::gPrevInFormat; -audio_channel_mask_t AudioSystem::gPrevInChannelMask; -size_t AudioSystem::gInBuffSize = 0; // zero indicates cache is invalid - -sp<AudioSystem::AudioPortCallback> AudioSystem::gAudioPortCallback; // establish binder interface to AudioFlinger service const sp<IAudioFlinger> AudioSystem::get_audio_flinger() @@ -87,6 +76,25 @@ const sp<IAudioFlinger> AudioSystem::get_audio_flinger() return af; } +const sp<AudioSystem::AudioFlingerClient> AudioSystem::getAudioFlingerClient() +{ + // calling get_audio_flinger() will initialize gAudioFlingerClient if needed + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) return 0; + Mutex::Autolock _l(gLock); + return gAudioFlingerClient; +} + +sp<AudioIoDescriptor> AudioSystem::getIoDescriptor(audio_io_handle_t ioHandle) +{ + sp<AudioIoDescriptor> desc; + const sp<AudioFlingerClient> afc = getAudioFlingerClient(); + if (afc != 0) { + desc = afc->getIoDescriptor(ioHandle); + } + return desc; +} + /* static */ status_t AudioSystem::checkAudioFlinger() { if (defaultServiceManager()->checkService(String16("media.audio_flinger")) != 0) { @@ -260,18 +268,13 @@ status_t AudioSystem::getSamplingRate(audio_io_handle_t output, { const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; - - Mutex::Autolock _l(gLockCache); - - OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output); - if (outputDesc == NULL) { + sp<AudioIoDescriptor> outputDesc = getIoDescriptor(output); + if (outputDesc == 0) { ALOGV("getOutputSamplingRate() no output descriptor for output %d in gOutputs", output); - gLockCache.unlock(); *samplingRate = af->sampleRate(output); - gLockCache.lock(); } else { ALOGV("getOutputSamplingRate() reading from output desc"); - *samplingRate = outputDesc->samplingRate; + *samplingRate = outputDesc->mSamplingRate; } if (*samplingRate == 0) { ALOGE("AudioSystem::getSamplingRate failed for output %d", output); @@ -304,16 +307,11 @@ status_t AudioSystem::getFrameCount(audio_io_handle_t output, { const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; - - Mutex::Autolock _l(gLockCache); - - OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output); - if (outputDesc == NULL) { - gLockCache.unlock(); + sp<AudioIoDescriptor> outputDesc = getIoDescriptor(output); + if (outputDesc == 0) { *frameCount = af->frameCount(output); - gLockCache.lock(); } else { - *frameCount = outputDesc->frameCount; + *frameCount = outputDesc->mFrameCount; } if (*frameCount == 0) { ALOGE("AudioSystem::getFrameCount failed for output %d", output); @@ -346,16 +344,11 @@ status_t AudioSystem::getLatency(audio_io_handle_t output, { const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; - - Mutex::Autolock _l(gLockCache); - - OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output); - if (outputDesc == NULL) { - gLockCache.unlock(); + sp<AudioIoDescriptor> outputDesc = getIoDescriptor(output); + if (outputDesc == 0) { *latency = af->latency(output); - gLockCache.lock(); } else { - *latency = outputDesc->latency; + *latency = outputDesc->mLatency; } ALOGV("getLatency() output %d, latency %d", output, *latency); @@ -366,34 +359,11 @@ status_t AudioSystem::getLatency(audio_io_handle_t output, status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, size_t* buffSize) { - const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); - if (af == 0) { - return PERMISSION_DENIED; - } - Mutex::Autolock _l(gLockCache); - // Do we have a stale gInBufferSize or are we requesting the input buffer size for new values - size_t inBuffSize = gInBuffSize; - if ((inBuffSize == 0) || (sampleRate != gPrevInSamplingRate) || (format != gPrevInFormat) - || (channelMask != gPrevInChannelMask)) { - gLockCache.unlock(); - inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask); - gLockCache.lock(); - if (inBuffSize == 0) { - ALOGE("AudioSystem::getInputBufferSize failed sampleRate %d format %#x channelMask %x", - sampleRate, format, channelMask); - return BAD_VALUE; - } - // A benign race is possible here: we could overwrite a fresher cache entry - // save the request params - gPrevInSamplingRate = sampleRate; - gPrevInFormat = format; - gPrevInChannelMask = channelMask; - - gInBuffSize = inBuffSize; + const sp<AudioFlingerClient> afc = getAudioFlingerClient(); + if (afc == 0) { + return NO_INIT; } - *buffSize = inBuffSize; - - return NO_ERROR; + return afc->getInputBufferSize(sampleRate, format, channelMask, buffSize); } status_t AudioSystem::setVoiceVolume(float value) @@ -453,8 +423,26 @@ audio_hw_sync_t AudioSystem::getAudioHwSyncForSession(audio_session_t sessionId) return af->getAudioHwSyncForSession(sessionId); } +status_t AudioSystem::systemReady() +{ + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) return NO_INIT; + return af->systemReady(); +} + // --------------------------------------------------------------------------- + +void AudioSystem::AudioFlingerClient::clearIoCache() +{ + Mutex::Autolock _l(mLock); + mIoDescriptors.clear(); + mInBuffSize = 0; + mInSamplingRate = 0; + mInFormat = AUDIO_FORMAT_DEFAULT; + mInChannelMask = AUDIO_CHANNEL_NONE; +} + void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who __unused) { audio_error_callback cb = NULL; @@ -464,11 +452,8 @@ void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who __unused cb = gAudioErrorCallback; } - { - // clear output handles and stream to output map caches - Mutex::Autolock _l(gLockCache); - AudioSystem::gOutputs.clear(); - } + // clear output handles and stream to output map caches + clearIoCache(); if (cb) { cb(DEAD_OBJECT); @@ -476,76 +461,191 @@ void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who __unused ALOGW("AudioFlinger server died!"); } -void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle_t ioHandle, - const void *param2) { +void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event event, + const sp<AudioIoDescriptor>& ioDesc) { ALOGV("ioConfigChanged() event %d", event); - const OutputDescriptor *desc; - audio_stream_type_t stream; - if (ioHandle == AUDIO_IO_HANDLE_NONE) return; + if (ioDesc == 0 || ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return; - Mutex::Autolock _l(AudioSystem::gLockCache); + audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE; + Vector < sp<AudioDeviceCallback> > callbacks; + + { + Mutex::Autolock _l(mLock); + + switch (event) { + case AUDIO_OUTPUT_OPENED: + case AUDIO_INPUT_OPENED: { + sp<AudioIoDescriptor> oldDesc = getIoDescriptor(ioDesc->mIoHandle); + if (oldDesc == 0) { + mIoDescriptors.add(ioDesc->mIoHandle, ioDesc); + } else { + deviceId = oldDesc->getDeviceId(); + mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc); + } + + if (ioDesc->getDeviceId() != AUDIO_PORT_HANDLE_NONE) { + deviceId = ioDesc->getDeviceId(); + ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle); + if (ioIndex >= 0) { + callbacks = mAudioDeviceCallbacks.valueAt(ioIndex); + } + } + ALOGV("ioConfigChanged() new %s opened %d samplingRate %u, format %#x channel mask %#x " + "frameCount %zu deviceId %d", event == AUDIO_OUTPUT_OPENED ? "output" : "input", + ioDesc->mIoHandle, ioDesc->mSamplingRate, ioDesc->mFormat, ioDesc->mChannelMask, + ioDesc->mFrameCount, ioDesc->getDeviceId()); + } break; + case AUDIO_OUTPUT_CLOSED: + case AUDIO_INPUT_CLOSED: { + if (getIoDescriptor(ioDesc->mIoHandle) == 0) { + ALOGW("ioConfigChanged() closing unknown %s %d", + event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle); + break; + } + ALOGV("ioConfigChanged() %s %d closed", + event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle); + + mIoDescriptors.removeItem(ioDesc->mIoHandle); + mAudioDeviceCallbacks.removeItem(ioDesc->mIoHandle); + } break; + + case AUDIO_OUTPUT_CONFIG_CHANGED: + case AUDIO_INPUT_CONFIG_CHANGED: { + sp<AudioIoDescriptor> oldDesc = getIoDescriptor(ioDesc->mIoHandle); + if (oldDesc == 0) { + ALOGW("ioConfigChanged() modifying unknown output! %d", ioDesc->mIoHandle); + break; + } + + deviceId = oldDesc->getDeviceId(); + mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc); + + if (deviceId != ioDesc->getDeviceId()) { + deviceId = ioDesc->getDeviceId(); + ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle); + if (ioIndex >= 0) { + callbacks = mAudioDeviceCallbacks.valueAt(ioIndex); + } + } + ALOGV("ioConfigChanged() new config for %s %d samplingRate %u, format %#x " + "channel mask %#x frameCount %zu deviceId %d", + event == AUDIO_OUTPUT_CONFIG_CHANGED ? "output" : "input", + ioDesc->mIoHandle, ioDesc->mSamplingRate, ioDesc->mFormat, + ioDesc->mChannelMask, ioDesc->mFrameCount, ioDesc->getDeviceId()); - switch (event) { - case STREAM_CONFIG_CHANGED: - break; - case OUTPUT_OPENED: { - if (gOutputs.indexOfKey(ioHandle) >= 0) { - ALOGV("ioConfigChanged() opening already existing output! %d", ioHandle); - break; - } - if (param2 == NULL) break; - desc = (const OutputDescriptor *)param2; - - OutputDescriptor *outputDesc = new OutputDescriptor(*desc); - gOutputs.add(ioHandle, outputDesc); - ALOGV("ioConfigChanged() new output samplingRate %u, format %#x channel mask %#x frameCount %zu " - "latency %d", - outputDesc->samplingRate, outputDesc->format, outputDesc->channelMask, - outputDesc->frameCount, outputDesc->latency); } break; - case OUTPUT_CLOSED: { - if (gOutputs.indexOfKey(ioHandle) < 0) { - ALOGW("ioConfigChanged() closing unknown output! %d", ioHandle); - break; } - ALOGV("ioConfigChanged() output %d closed", ioHandle); + } + // callbacks.size() != 0 => ioDesc->mIoHandle and deviceId are valid + for (size_t i = 0; i < callbacks.size(); i++) { + callbacks[i]->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId); + } +} - gOutputs.removeItem(ioHandle); - } break; +status_t AudioSystem::AudioFlingerClient::getInputBufferSize( + uint32_t sampleRate, audio_format_t format, + audio_channel_mask_t channelMask, size_t* buffSize) +{ + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + Mutex::Autolock _l(mLock); + // Do we have a stale mInBuffSize or are we requesting the input buffer size for new values + if ((mInBuffSize == 0) || (sampleRate != mInSamplingRate) || (format != mInFormat) + || (channelMask != mInChannelMask)) { + size_t inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask); + if (inBuffSize == 0) { + ALOGE("AudioSystem::getInputBufferSize failed sampleRate %d format %#x channelMask %x", + sampleRate, format, channelMask); + return BAD_VALUE; + } + // A benign race is possible here: we could overwrite a fresher cache entry + // save the request params + mInSamplingRate = sampleRate; + mInFormat = format; + mInChannelMask = channelMask; - case OUTPUT_CONFIG_CHANGED: { - int index = gOutputs.indexOfKey(ioHandle); - if (index < 0) { - ALOGW("ioConfigChanged() modifying unknown output! %d", ioHandle); - break; + mInBuffSize = inBuffSize; + } + + *buffSize = mInBuffSize; + + return NO_ERROR; +} + +sp<AudioIoDescriptor> AudioSystem::AudioFlingerClient::getIoDescriptor(audio_io_handle_t ioHandle) +{ + sp<AudioIoDescriptor> desc; + ssize_t index = mIoDescriptors.indexOfKey(ioHandle); + if (index >= 0) { + desc = mIoDescriptors.valueAt(index); + } + return desc; +} + +status_t AudioSystem::AudioFlingerClient::addAudioDeviceCallback( + const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo) +{ + Mutex::Autolock _l(mLock); + Vector < sp<AudioDeviceCallback> > callbacks; + ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo); + if (ioIndex >= 0) { + callbacks = mAudioDeviceCallbacks.valueAt(ioIndex); + } + + for (size_t cbIndex = 0; cbIndex < callbacks.size(); cbIndex++) { + if (callbacks[cbIndex] == callback) { + return INVALID_OPERATION; } - if (param2 == NULL) break; - desc = (const OutputDescriptor *)param2; + } + callbacks.add(callback); + + mAudioDeviceCallbacks.replaceValueFor(audioIo, callbacks); + return NO_ERROR; +} - ALOGV("ioConfigChanged() new config for output %d samplingRate %u, format %#x channel mask %#x " - "frameCount %zu latency %d", - ioHandle, desc->samplingRate, desc->format, - desc->channelMask, desc->frameCount, desc->latency); - OutputDescriptor *outputDesc = gOutputs.valueAt(index); - delete outputDesc; - outputDesc = new OutputDescriptor(*desc); - gOutputs.replaceValueFor(ioHandle, outputDesc); - } break; - case INPUT_OPENED: - case INPUT_CLOSED: - case INPUT_CONFIG_CHANGED: - break; +status_t AudioSystem::AudioFlingerClient::removeAudioDeviceCallback( + const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo) +{ + Mutex::Autolock _l(mLock); + ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo); + if (ioIndex < 0) { + return INVALID_OPERATION; + } + Vector < sp<AudioDeviceCallback> > callbacks = mAudioDeviceCallbacks.valueAt(ioIndex); + size_t cbIndex; + for (cbIndex = 0; cbIndex < callbacks.size(); cbIndex++) { + if (callbacks[cbIndex] == callback) { + break; + } + } + if (cbIndex == callbacks.size()) { + return INVALID_OPERATION; } + callbacks.removeAt(cbIndex); + if (callbacks.size() != 0) { + mAudioDeviceCallbacks.replaceValueFor(audioIo, callbacks); + } else { + mAudioDeviceCallbacks.removeItem(audioIo); + } + return NO_ERROR; } -void AudioSystem::setErrorCallback(audio_error_callback cb) +/* static */ void AudioSystem::setErrorCallback(audio_error_callback cb) { Mutex::Autolock _l(gLock); gAudioErrorCallback = cb; } +/*static*/ void AudioSystem::setDynPolicyCallback(dynamic_policy_callback cb) +{ + Mutex::Autolock _l(gLock); + gDynPolicyCallback = cb; +} + // client singleton for AudioPolicyService binder interface // protected by gLockAPS sp<IAudioPolicyService> AudioSystem::gAudioPolicyService; @@ -590,18 +690,22 @@ const sp<IAudioPolicyService> AudioSystem::get_audio_policy_service() status_t AudioSystem::setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address) + const char *device_address, + const char *device_name) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); const char *address = ""; + const char *name = ""; if (aps == 0) return PERMISSION_DENIED; if (device_address != NULL) { address = device_address; } - - return aps->setDeviceConnectionState(device, state, address); + if (device_name != NULL) { + name = device_name; + } + return aps->setDeviceConnectionState(device, state, address, name); } audio_policy_dev_state_t AudioSystem::getDeviceConnectionState(audio_devices_t device, @@ -653,17 +757,19 @@ status_t AudioSystem::getOutputForAttr(const audio_attributes_t *attr, audio_io_handle_t *output, audio_session_t session, audio_stream_type_t *stream, + uid_t uid, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, audio_output_flags_t flags, + audio_port_handle_t selectedDeviceId, const audio_offload_info_t *offloadInfo) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return NO_INIT; - return aps->getOutputForAttr(attr, output, session, stream, + return aps->getOutputForAttr(attr, output, session, stream, uid, samplingRate, format, channelMask, - flags, offloadInfo); + flags, selectedDeviceId, offloadInfo); } status_t AudioSystem::startOutput(audio_io_handle_t output, @@ -696,14 +802,17 @@ void AudioSystem::releaseOutput(audio_io_handle_t output, status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, audio_session_t session, + uid_t uid, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, - audio_input_flags_t flags) + audio_input_flags_t flags, + audio_port_handle_t selectedDeviceId) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return NO_INIT; - return aps->getInputForAttr(attr, input, session, samplingRate, format, channelMask, flags); + return aps->getInputForAttr( + attr, input, session, uid, samplingRate, format, channelMask, flags, selectedDeviceId); } status_t AudioSystem::startInput(audio_io_handle_t input, @@ -858,18 +967,16 @@ void AudioSystem::clearAudioConfigCache() // called by restoreTrack_l(), which needs new IAudioFlinger and IAudioPolicyService instances ALOGV("clearAudioConfigCache()"); { - Mutex::Autolock _l(gLockCache); - gOutputs.clear(); - } - { Mutex::Autolock _l(gLock); + if (gAudioFlingerClient != 0) { + gAudioFlingerClient->clearIoCache(); + } gAudioFlinger.clear(); } { Mutex::Autolock _l(gLockAPS); gAudioPolicyService.clear(); } - // Do not clear gAudioPortCallback } bool AudioSystem::isOffloadSupported(const audio_offload_info_t& info) @@ -929,10 +1036,75 @@ status_t AudioSystem::setAudioPortConfig(const struct audio_port_config *config) return aps->setAudioPortConfig(config); } -void AudioSystem::setAudioPortCallback(sp<AudioPortCallback> callBack) +status_t AudioSystem::addAudioPortCallback(const sp<AudioPortCallback>& callback) +{ + const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + + Mutex::Autolock _l(gLockAPS); + if (gAudioPolicyServiceClient == 0) { + return NO_INIT; + } + int ret = gAudioPolicyServiceClient->addAudioPortCallback(callback); + if (ret == 1) { + aps->setAudioPortCallbacksEnabled(true); + } + return (ret < 0) ? INVALID_OPERATION : NO_ERROR; +} + +/*static*/ +status_t AudioSystem::removeAudioPortCallback(const sp<AudioPortCallback>& callback) +{ + const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + + Mutex::Autolock _l(gLockAPS); + if (gAudioPolicyServiceClient == 0) { + return NO_INIT; + } + int ret = gAudioPolicyServiceClient->removeAudioPortCallback(callback); + if (ret == 0) { + aps->setAudioPortCallbacksEnabled(false); + } + return (ret < 0) ? INVALID_OPERATION : NO_ERROR; +} + +status_t AudioSystem::addAudioDeviceCallback( + const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo) { - Mutex::Autolock _l(gLockAPC); - gAudioPortCallback = callBack; + const sp<AudioFlingerClient> afc = getAudioFlingerClient(); + if (afc == 0) { + return NO_INIT; + } + status_t status = afc->addAudioDeviceCallback(callback, audioIo); + if (status == NO_ERROR) { + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af != 0) { + af->registerClient(afc); + } + } + return status; +} + +status_t AudioSystem::removeAudioDeviceCallback( + const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo) +{ + const sp<AudioFlingerClient> afc = getAudioFlingerClient(); + if (afc == 0) { + return NO_INIT; + } + return afc->removeAudioDeviceCallback(callback, audioIo); +} + +audio_port_handle_t AudioSystem::getDeviceIdForIo(audio_io_handle_t audioIo) +{ + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + const sp<AudioIoDescriptor> desc = getIoDescriptor(audioIo); + if (desc == 0) { + return AUDIO_PORT_HANDLE_NONE; + } + return desc->getDeviceId(); } status_t AudioSystem::acquireSoundTriggerSession(audio_session_t *session, @@ -965,38 +1137,100 @@ status_t AudioSystem::registerPolicyMixes(Vector<AudioMix> mixes, bool registrat return aps->registerPolicyMixes(mixes, registration); } +status_t AudioSystem::startAudioSource(const struct audio_port_config *source, + const audio_attributes_t *attributes, + audio_io_handle_t *handle) +{ + const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + return aps->startAudioSource(source, attributes, handle); +} + +status_t AudioSystem::stopAudioSource(audio_io_handle_t handle) +{ + const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + return aps->stopAudioSource(handle); +} + // --------------------------------------------------------------------------- -void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who __unused) +int AudioSystem::AudioPolicyServiceClient::addAudioPortCallback( + const sp<AudioPortCallback>& callback) { - { - Mutex::Autolock _l(gLockAPC); - if (gAudioPortCallback != 0) { - gAudioPortCallback->onServiceDied(); + Mutex::Autolock _l(mLock); + for (size_t i = 0; i < mAudioPortCallbacks.size(); i++) { + if (mAudioPortCallbacks[i] == callback) { + return -1; } } - { - Mutex::Autolock _l(gLockAPS); - AudioSystem::gAudioPolicyService.clear(); - } + mAudioPortCallbacks.add(callback); + return mAudioPortCallbacks.size(); +} - ALOGW("AudioPolicyService server died!"); +int AudioSystem::AudioPolicyServiceClient::removeAudioPortCallback( + const sp<AudioPortCallback>& callback) +{ + Mutex::Autolock _l(mLock); + size_t i; + for (i = 0; i < mAudioPortCallbacks.size(); i++) { + if (mAudioPortCallbacks[i] == callback) { + break; + } + } + if (i == mAudioPortCallbacks.size()) { + return -1; + } + mAudioPortCallbacks.removeAt(i); + return mAudioPortCallbacks.size(); } + void AudioSystem::AudioPolicyServiceClient::onAudioPortListUpdate() { - Mutex::Autolock _l(gLockAPC); - if (gAudioPortCallback != 0) { - gAudioPortCallback->onAudioPortListUpdate(); + Mutex::Autolock _l(mLock); + for (size_t i = 0; i < mAudioPortCallbacks.size(); i++) { + mAudioPortCallbacks[i]->onAudioPortListUpdate(); } } void AudioSystem::AudioPolicyServiceClient::onAudioPatchListUpdate() { - Mutex::Autolock _l(gLockAPC); - if (gAudioPortCallback != 0) { - gAudioPortCallback->onAudioPatchListUpdate(); + Mutex::Autolock _l(mLock); + for (size_t i = 0; i < mAudioPortCallbacks.size(); i++) { + mAudioPortCallbacks[i]->onAudioPatchListUpdate(); + } +} + +void AudioSystem::AudioPolicyServiceClient::onDynamicPolicyMixStateUpdate( + String8 regId, int32_t state) +{ + ALOGV("AudioPolicyServiceClient::onDynamicPolicyMixStateUpdate(%s, %d)", regId.string(), state); + dynamic_policy_callback cb = NULL; + { + Mutex::Autolock _l(AudioSystem::gLock); + cb = gDynPolicyCallback; + } + + if (cb != NULL) { + cb(DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE, regId, state); } } -}; // namespace android +void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who __unused) +{ + { + Mutex::Autolock _l(mLock); + for (size_t i = 0; i < mAudioPortCallbacks.size(); i++) { + mAudioPortCallbacks[i]->onServiceDied(); + } + } + { + Mutex::Autolock _l(gLockAPS); + AudioSystem::gAudioPolicyService.clear(); + } + + ALOGW("AudioPolicyService server died!"); +} + +} // namespace android diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 735db5c..444f4d8 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -33,11 +33,28 @@ #define WAIT_PERIOD_MS 10 #define WAIT_STREAM_END_TIMEOUT_SEC 120 - +static const int kMaxLoopCountNotifications = 32; namespace android { // --------------------------------------------------------------------------- +// TODO: Move to a separate .h + +template <typename T> +static inline const T &min(const T &x, const T &y) { + return x < y ? x : y; +} + +template <typename T> +static inline const T &max(const T &x, const T &y) { + return x > y ? x : y; +} + +static inline nsecs_t framesToNanoseconds(ssize_t frames, uint32_t sampleRate, float speed) +{ + return ((double)frames * 1000000000) / ((double)sampleRate * speed); +} + static int64_t convertTimespecToUs(const struct timespec &tv) { return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000; @@ -51,6 +68,42 @@ static int64_t getNowUs() return convertTimespecToUs(tv); } +// FIXME: we don't use the pitch setting in the time stretcher (not working); +// instead we emulate it using our sample rate converter. +static const bool kFixPitch = true; // enable pitch fix +static inline uint32_t adjustSampleRate(uint32_t sampleRate, float pitch) +{ + return kFixPitch ? (sampleRate * pitch + 0.5) : sampleRate; +} + +static inline float adjustSpeed(float speed, float pitch) +{ + return kFixPitch ? speed / max(pitch, AUDIO_TIMESTRETCH_PITCH_MIN_DELTA) : speed; +} + +static inline float adjustPitch(float pitch) +{ + return kFixPitch ? AUDIO_TIMESTRETCH_PITCH_NORMAL : pitch; +} + +// Must match similar computation in createTrack_l in Threads.cpp. +// TODO: Move to a common library +static size_t calculateMinFrameCount( + uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate, + uint32_t sampleRate, float speed) +{ + // Ensure that buffer depth covers at least audio hardware latency + uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate); + if (minBufCount < 2) { + minBufCount = 2; + } + ALOGV("calculateMinFrameCount afLatency %u afFrameCount %u afSampleRate %u " + "sampleRate %u speed %f minBufCount: %u", + afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount); + return minBufCount * sourceFramesNeededWithTimestretch( + sampleRate, afFrameCount, afSampleRate, speed); +} + // static status_t AudioTrack::getMinFrameCount( size_t* frameCount, @@ -61,12 +114,11 @@ status_t AudioTrack::getMinFrameCount( return BAD_VALUE; } - // FIXME merge with similar code in createTrack_l(), except we're missing - // some information here that is available in createTrack_l(): + // FIXME handle in server, like createTrack_l(), possible missing info: // audio_io_handle_t output // audio_format_t format // audio_channel_mask_t channelMask - // audio_output_flags_t flags + // audio_output_flags_t flags (FAST) uint32_t afSampleRate; status_t status; status = AudioSystem::getOutputSamplingRate(&afSampleRate, streamType); @@ -90,23 +142,20 @@ status_t AudioTrack::getMinFrameCount( return status; } - // Ensure that buffer depth covers at least audio hardware latency - uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate); - if (minBufCount < 2) { - minBufCount = 2; - } + // When called from createTrack, speed is 1.0f (normal speed). + // This is rechecked again on setting playback rate (TODO: on setting sample rate, too). + *frameCount = calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, 1.0f); - *frameCount = (sampleRate == 0) ? afFrameCount * minBufCount : - afFrameCount * minBufCount * uint64_t(sampleRate) / afSampleRate; - // The formula above should always produce a non-zero value, but return an error - // in the unlikely event that it does not, as that's part of the API contract. + // The formula above should always produce a non-zero value under normal circumstances: + // AudioTrack.SAMPLE_RATE_HZ_MIN <= sampleRate <= AudioTrack.SAMPLE_RATE_HZ_MAX. + // Return error in the unlikely event that it does not, as that's part of the API contract. if (*frameCount == 0) { - ALOGE("AudioTrack::getMinFrameCount failed for streamType %d, sampleRate %d", + ALOGE("AudioTrack::getMinFrameCount failed for streamType %d, sampleRate %u", streamType, sampleRate); return BAD_VALUE; } - ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, minBufCount=%d, afSampleRate=%d, afLatency=%d", - *frameCount, afFrameCount, minBufCount, afSampleRate, afLatency); + ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, afSampleRate=%u, afLatency=%u", + *frameCount, afFrameCount, afSampleRate, afLatency); return NO_ERROR; } @@ -117,7 +166,8 @@ AudioTrack::AudioTrack() mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), - mPausedPosition(0) + mPausedPosition(0), + mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE) { mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN; mAttributes.usage = AUDIO_USAGE_UNKNOWN; @@ -140,17 +190,19 @@ AudioTrack::AudioTrack( const audio_offload_info_t *offloadInfo, int uid, pid_t pid, - const audio_attributes_t* pAttributes) + const audio_attributes_t* pAttributes, + bool doNotReconnect) : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), - mPausedPosition(0) + mPausedPosition(0), + mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE) { mStatus = set(streamType, sampleRate, format, channelMask, frameCount, flags, cbf, user, notificationFrames, 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType, - offloadInfo, uid, pid, pAttributes); + offloadInfo, uid, pid, pAttributes, doNotReconnect); } AudioTrack::AudioTrack( @@ -168,17 +220,19 @@ AudioTrack::AudioTrack( const audio_offload_info_t *offloadInfo, int uid, pid_t pid, - const audio_attributes_t* pAttributes) + const audio_attributes_t* pAttributes, + bool doNotReconnect) : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), - mPausedPosition(0) + mPausedPosition(0), + mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE) { mStatus = set(streamType, sampleRate, format, channelMask, 0 /*frameCount*/, flags, cbf, user, notificationFrames, sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo, - uid, pid, pAttributes); + uid, pid, pAttributes, doNotReconnect); } AudioTrack::~AudioTrack() @@ -194,13 +248,17 @@ AudioTrack::~AudioTrack() mAudioTrackThread->requestExitAndWait(); mAudioTrackThread.clear(); } + // No lock here: worst case we remove a NULL callback which will be a nop + if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) { + AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput); + } IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this); mAudioTrack.clear(); mCblkMemory.clear(); mSharedBuffer.clear(); IPCThreadState::self()->flushCommands(); - ALOGV("~AudioTrack, releasing session id from %d on behalf of %d", - IPCThreadState::self()->getCallingPid(), mClientPid); + ALOGV("~AudioTrack, releasing session id %d from %d on behalf of %d", + mSessionId, IPCThreadState::self()->getCallingPid(), mClientPid); AudioSystem::releaseAudioSessionId(mSessionId, mClientPid); } } @@ -222,12 +280,13 @@ status_t AudioTrack::set( const audio_offload_info_t *offloadInfo, int uid, pid_t pid, - const audio_attributes_t* pAttributes) + const audio_attributes_t* pAttributes, + bool doNotReconnect) { ALOGV("set(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, " - "flags #%x, notificationFrames %u, sessionId %d, transferType %d", + "flags #%x, notificationFrames %u, sessionId %d, transferType %d, uid %d, pid %d", streamType, sampleRate, format, channelMask, frameCount, flags, notificationFrames, - sessionId, transferType); + sessionId, transferType, uid, pid); switch (transferType) { case TRANSFER_DEFAULT: @@ -264,14 +323,13 @@ status_t AudioTrack::set( } mSharedBuffer = sharedBuffer; mTransfer = transferType; + mDoNotReconnect = doNotReconnect; - ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), + ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %zu", sharedBuffer->pointer(), sharedBuffer->size()); ALOGV("set() streamType %d frameCount %zu flags %04x", streamType, frameCount, flags); - AutoMutex lock(mLock); - // invariant that mAudioTrack != 0 is true only after set() returns successfully if (mAudioTrack != 0) { ALOGE("Track already in use"); @@ -295,6 +353,9 @@ status_t AudioTrack::set( ALOGV("Building AudioTrack with attributes: usage=%d content=%d flags=0x%x tags=[%s]", mAttributes.usage, mAttributes.content_type, mAttributes.flags, mAttributes.tags); mStreamType = AUDIO_STREAM_DEFAULT; + if ((mAttributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) { + flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC); + } } // these below should probably come from the audioFlinger too... @@ -317,12 +378,6 @@ status_t AudioTrack::set( uint32_t channelCount = audio_channel_count_from_out_mask(channelMask); mChannelCount = channelCount; - // AudioFlinger does not currently support 8-bit data in shared memory - if (format == AUDIO_FORMAT_PCM_8_BIT && sharedBuffer != 0) { - ALOGE("8-bit data in shared memory is not supported"); - return BAD_VALUE; - } - // force direct flag if format is not linear PCM // or offload was requested if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) @@ -346,12 +401,9 @@ status_t AudioTrack::set( } else { mFrameSize = sizeof(uint8_t); } - mFrameSizeAF = mFrameSize; } else { ALOG_ASSERT(audio_is_linear_pcm(format)); mFrameSize = channelCount * audio_bytes_per_sample(format); - mFrameSizeAF = channelCount * audio_bytes_per_sample( - format == AUDIO_FORMAT_PCM_8_BIT ? AUDIO_FORMAT_PCM_16_BIT : format); // createTrack will return an error if PCM format is not supported by server, // so no need to check for specific PCM formats here } @@ -361,6 +413,8 @@ status_t AudioTrack::set( return BAD_VALUE; } mSampleRate = sampleRate; + mOriginalSampleRate = sampleRate; + mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT; // Make copy of input parameter offloadInfo so that in the future: // (a) createTrack_l doesn't need it as an input parameter @@ -403,6 +457,7 @@ status_t AudioTrack::set( if (cbf != NULL) { mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava); mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/); + // thread begins in paused state, and will not reference us until start() } // create the IAudioTrack @@ -420,12 +475,14 @@ status_t AudioTrack::set( mStatus = NO_ERROR; mState = STATE_STOPPED; mUserData = user; - mLoopPeriod = 0; + mLoopCount = 0; + mLoopStart = 0; + mLoopEnd = 0; + mLoopCountNotified = 0; mMarkerPosition = 0; mMarkerReached = false; mNewPosition = 0; mUpdatePeriod = 0; - mServer = 0; mPosition = 0; mReleased = 0; mStartUs = 0; @@ -433,6 +490,9 @@ status_t AudioTrack::set( mSequence = 1; mObservedSequence = mSequence; mInUnderrun = false; + mPreviousTimestampValid = false; + mTimestampStartupGlitchReported = false; + mRetrogradeMotionReported = false; return NO_ERROR; } @@ -459,6 +519,10 @@ status_t AudioTrack::start() if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) { // reset current position as seen by client to 0 mPosition = 0; + mPreviousTimestampValid = false; + mTimestampStartupGlitchReported = false; + mRetrogradeMotionReported = false; + // For offloaded tracks, we don't know if the hardware counters are really zero here, // since the flush is asynchronous and stop may not fully drain. // We save the time when the track is started to later verify whether @@ -531,14 +595,12 @@ void AudioTrack::stop() // the playback head position will reset to 0, so if a marker is set, we need // to activate it again mMarkerReached = false; -#if 0 - // Force flush if a shared buffer is used otherwise audioflinger - // will not stop before end of buffer is reached. - // It may be needed to make sure that we stop playback, likely in case looping is on. + if (mSharedBuffer != 0) { - flush_l(); + // clear buffer position and loop count. + mStaticProxy->setBufferPositionAndLoop(0 /* position */, + 0 /* loopStart */, 0 /* loopEnd */, 0 /* loopCount */); } -#endif sp<AudioTrackThread> t = mAudioTrackThread; if (t != 0) { @@ -669,24 +731,31 @@ void AudioTrack::getAuxEffectSendLevel(float* level) const status_t AudioTrack::setSampleRate(uint32_t rate) { - if (mIsTimed || isOffloadedOrDirect()) { + AutoMutex lock(mLock); + if (rate == mSampleRate) { + return NO_ERROR; + } + if (mIsTimed || isOffloadedOrDirect_l() || (mFlags & AUDIO_OUTPUT_FLAG_FAST)) { return INVALID_OPERATION; } - - AutoMutex lock(mLock); if (mOutput == AUDIO_IO_HANDLE_NONE) { return NO_INIT; } + // NOTE: it is theoretically possible, but highly unlikely, that a device change + // could mean a previously allowed sampling rate is no longer allowed. uint32_t afSamplingRate; if (AudioSystem::getSamplingRate(mOutput, &afSamplingRate) != NO_ERROR) { return NO_INIT; } - if (rate == 0 || rate > afSamplingRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) { + // pitch is emulated by adjusting speed and sampleRate + const uint32_t effectiveSampleRate = adjustSampleRate(rate, mPlaybackRate.mPitch); + if (rate == 0 || effectiveSampleRate > afSamplingRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) { return BAD_VALUE; } + // TODO: Should we also check if the buffer size is compatible? mSampleRate = rate; - mProxy->setSampleRate(rate); + mProxy->setSampleRate(effectiveSampleRate); return NO_ERROR; } @@ -714,6 +783,69 @@ uint32_t AudioTrack::getSampleRate() const return mSampleRate; } +uint32_t AudioTrack::getOriginalSampleRate() const +{ + if (mIsTimed) { + return 0; + } + + return mOriginalSampleRate; +} + +status_t AudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) +{ + AutoMutex lock(mLock); + if (isAudioPlaybackRateEqual(playbackRate, mPlaybackRate)) { + return NO_ERROR; + } + if (mIsTimed || isOffloadedOrDirect_l()) { + return INVALID_OPERATION; + } + if (mFlags & AUDIO_OUTPUT_FLAG_FAST) { + return INVALID_OPERATION; + } + // pitch is emulated by adjusting speed and sampleRate + const uint32_t effectiveRate = adjustSampleRate(mSampleRate, playbackRate.mPitch); + const float effectiveSpeed = adjustSpeed(playbackRate.mSpeed, playbackRate.mPitch); + const float effectivePitch = adjustPitch(playbackRate.mPitch); + AudioPlaybackRate playbackRateTemp = playbackRate; + playbackRateTemp.mSpeed = effectiveSpeed; + playbackRateTemp.mPitch = effectivePitch; + + if (!isAudioPlaybackRateValid(playbackRateTemp)) { + return BAD_VALUE; + } + // Check if the buffer size is compatible. + if (!isSampleRateSpeedAllowed_l(effectiveRate, effectiveSpeed)) { + ALOGV("setPlaybackRate(%f, %f) failed", playbackRate.mSpeed, playbackRate.mPitch); + return BAD_VALUE; + } + + // Check resampler ratios are within bounds + if (effectiveRate > mSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) { + ALOGV("setPlaybackRate(%f, %f) failed. Resample rate exceeds max accepted value", + playbackRate.mSpeed, playbackRate.mPitch); + return BAD_VALUE; + } + + if (effectiveRate * AUDIO_RESAMPLER_UP_RATIO_MAX < mSampleRate) { + ALOGV("setPlaybackRate(%f, %f) failed. Resample rate below min accepted value", + playbackRate.mSpeed, playbackRate.mPitch); + return BAD_VALUE; + } + mPlaybackRate = playbackRate; + //set effective rates + mProxy->setPlaybackRate(playbackRateTemp); + mProxy->setSampleRate(effectiveRate); // FIXME: not quite "atomic" with setPlaybackRate + return NO_ERROR; +} + +const AudioPlaybackRate& AudioTrack::getPlaybackRate() const +{ + AutoMutex lock(mLock); + return mPlaybackRate; +} + status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount) { if (mSharedBuffer == 0 || mIsTimed || isOffloadedOrDirect()) { @@ -740,10 +872,15 @@ status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount void AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount) { - // Setting the loop will reset next notification update period (like setPosition). - mNewPosition = updateAndGetPosition_l() + mUpdatePeriod; - mLoopPeriod = loopCount != 0 ? loopEnd - loopStart : 0; + // We do not update the periodic notification point. + // mNewPosition = updateAndGetPosition_l() + mUpdatePeriod; + mLoopCount = loopCount; + mLoopEnd = loopEnd; + mLoopStart = loopStart; + mLoopCountNotified = loopCount; mStaticProxy->setLoop(loopStart, loopEnd, loopCount); + + // Waking the AudioTrackThread is not needed as this cannot be called when active. } status_t AudioTrack::setMarkerPosition(uint32_t marker) @@ -757,6 +894,10 @@ status_t AudioTrack::setMarkerPosition(uint32_t marker) mMarkerPosition = marker; mMarkerReached = false; + sp<AudioTrackThread> t = mAudioTrackThread; + if (t != 0) { + t->wake(); + } return NO_ERROR; } @@ -786,6 +927,10 @@ status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod) mNewPosition = updateAndGetPosition_l() + updatePeriod; mUpdatePeriod = updatePeriod; + sp<AudioTrackThread> t = mAudioTrackThread; + if (t != 0) { + t->wake(); + } return NO_ERROR; } @@ -823,12 +968,11 @@ status_t AudioTrack::setPosition(uint32_t position) if (mState == STATE_ACTIVE) { return INVALID_OPERATION; } + // After setting the position, use full update period before notification. mNewPosition = updateAndGetPosition_l() + mUpdatePeriod; - mLoopPeriod = 0; - // FIXME Check whether loops and setting position are incompatible in old code. - // If we use setLoop for both purposes we lose the capability to set the position while looping. - mStaticProxy->setLoop(position, mFrameCount, 0); + mStaticProxy->setBufferPosition(position); + // Waking the AudioTrackThread is not needed as this cannot be called when active. return NO_ERROR; } @@ -849,15 +993,18 @@ status_t AudioTrack::getPosition(uint32_t *position) } if (mOutput != AUDIO_IO_HANDLE_NONE) { - uint32_t halFrames; - AudioSystem::getRenderPosition(mOutput, &halFrames, &dspFrames); + uint32_t halFrames; // actually unused + (void) AudioSystem::getRenderPosition(mOutput, &halFrames, &dspFrames); + // FIXME: on getRenderPosition() error, we return OK with frame position 0. } // FIXME: dspFrames may not be zero in (mState == STATE_STOPPED || mState == STATE_FLUSHED) // due to hardware latency. We leave this behavior for now. *position = dspFrames; } else { if (mCblk->mFlags & CBLK_INVALID) { - restoreTrack_l("getPosition"); + (void) restoreTrack_l("getPosition"); + // FIXME: for compatibility with the Java API we ignore the restoreTrack_l() + // error here (e.g. DEAD_OBJECT) and return OK with the last recorded server position. } // IAudioTrack::stop() isn't synchronous; we don't know when presentation completes @@ -893,10 +1040,19 @@ status_t AudioTrack::reload() return INVALID_OPERATION; } mNewPosition = mUpdatePeriod; - mLoopPeriod = 0; - // FIXME The new code cannot reload while keeping a loop specified. - // Need to check how the old code handled this, and whether it's a significant change. - mStaticProxy->setLoop(0, mFrameCount, 0); + (void) updateAndGetPosition_l(); + mPosition = 0; + mPreviousTimestampValid = false; +#if 0 + // The documentation is not clear on the behavior of reload() and the restoration + // of loop count. Historically we have not restored loop count, start, end, + // but it makes sense if one desires to repeat playing a particular sound. + if (mLoopCount != 0) { + mLoopCountNotified = mLoopCount; + mStaticProxy->setLoop(mLoopStart, mLoopEnd, mLoopCount); + } +#endif + mStaticProxy->setBufferPosition(0); return NO_ERROR; } @@ -906,6 +1062,28 @@ audio_io_handle_t AudioTrack::getOutput() const return mOutput; } +status_t AudioTrack::setOutputDevice(audio_port_handle_t deviceId) { + AutoMutex lock(mLock); + if (mSelectedDeviceId != deviceId) { + mSelectedDeviceId = deviceId; + android_atomic_or(CBLK_INVALID, &mCblk->mFlags); + } + return NO_ERROR; +} + +audio_port_handle_t AudioTrack::getOutputDevice() { + AutoMutex lock(mLock); + return mSelectedDeviceId; +} + +audio_port_handle_t AudioTrack::getRoutedDeviceId() { + AutoMutex lock(mLock); + if (mOutput == AUDIO_IO_HANDLE_NONE) { + return AUDIO_PORT_HANDLE_NONE; + } + return AudioSystem::getDeviceIdForIo(mOutput); +} + status_t AudioTrack::attachAuxEffect(int effectId) { AutoMutex lock(mLock); @@ -935,19 +1113,23 @@ status_t AudioTrack::createTrack_l() return NO_INIT; } + if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) { + AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput); + } audio_io_handle_t output; audio_stream_type_t streamType = mStreamType; audio_attributes_t *attr = (mStreamType == AUDIO_STREAM_DEFAULT) ? &mAttributes : NULL; - status_t status = AudioSystem::getOutputForAttr(attr, &output, - (audio_session_t)mSessionId, &streamType, - mSampleRate, mFormat, mChannelMask, - mFlags, mOffloadInfo); + status_t status; + status = AudioSystem::getOutputForAttr(attr, &output, + (audio_session_t)mSessionId, &streamType, mClientUid, + mSampleRate, mFormat, mChannelMask, + mFlags, mSelectedDeviceId, mOffloadInfo); if (status != NO_ERROR || output == AUDIO_IO_HANDLE_NONE) { - ALOGE("Could not get audio output for stream type %d, usage %d, sample rate %u, format %#x," + ALOGE("Could not get audio output for session %d, stream type %d, usage %d, sample rate %u, format %#x," " channel mask %#x, flags %#x", - streamType, mAttributes.usage, mSampleRate, mFormat, mChannelMask, mFlags); + mSessionId, streamType, mAttributes.usage, mSampleRate, mFormat, mChannelMask, mFlags); return BAD_VALUE; } { @@ -955,29 +1137,27 @@ status_t AudioTrack::createTrack_l() // we must release it ourselves if anything goes wrong. // Not all of these values are needed under all conditions, but it is easier to get them all - - uint32_t afLatency; - status = AudioSystem::getLatency(output, &afLatency); + status = AudioSystem::getLatency(output, &mAfLatency); if (status != NO_ERROR) { ALOGE("getLatency(%d) failed status %d", output, status); goto release; } + ALOGV("createTrack_l() output %d afLatency %u", output, mAfLatency); - size_t afFrameCount; - status = AudioSystem::getFrameCount(output, &afFrameCount); + status = AudioSystem::getFrameCount(output, &mAfFrameCount); if (status != NO_ERROR) { ALOGE("getFrameCount(output=%d) status %d", output, status); goto release; } - uint32_t afSampleRate; - status = AudioSystem::getSamplingRate(output, &afSampleRate); + status = AudioSystem::getSamplingRate(output, &mAfSampleRate); if (status != NO_ERROR) { ALOGE("getSamplingRate(output=%d) status %d", output, status); goto release; } if (mSampleRate == 0) { - mSampleRate = afSampleRate; + mSampleRate = mAfSampleRate; + mOriginalSampleRate = mAfSampleRate; } // Client decides whether the track is TIMED (see below), but can only express a preference // for FAST. Server will perform additional tests. @@ -986,23 +1166,23 @@ status_t AudioTrack::createTrack_l() // use case 1: shared buffer (mSharedBuffer != 0) || // use case 2: callback transfer mode - (mTransfer == TRANSFER_CALLBACK)) && + (mTransfer == TRANSFER_CALLBACK) || + // use case 3: obtain/release mode + (mTransfer == TRANSFER_OBTAIN)) && // matching sample rate - (mSampleRate == afSampleRate))) { - ALOGW("AUDIO_OUTPUT_FLAG_FAST denied by client"); + (mSampleRate == mAfSampleRate))) { + ALOGW("AUDIO_OUTPUT_FLAG_FAST denied by client; transfer %d, track %u Hz, output %u Hz", + mTransfer, mSampleRate, mAfSampleRate); // once denied, do not request again if IAudioTrack is re-created mFlags = (audio_output_flags_t) (mFlags & ~AUDIO_OUTPUT_FLAG_FAST); } - ALOGV("createTrack_l() output %d afLatency %d", output, afLatency); // The client's AudioTrack buffer is divided into n parts for purpose of wakeup by server, where // n = 1 fast track with single buffering; nBuffering is ignored // n = 2 fast track with double buffering - // n = 2 normal track, no sample rate conversion - // n = 3 normal track, with sample rate conversion - // (pessimistic; some non-1:1 conversion ratios don't actually need triple-buffering) - // n > 3 very high latency or very small notification interval; nBuffering is ignored - const uint32_t nBuffering = (mSampleRate == afSampleRate) ? 2 : 3; + // n = 2 normal track, (including those with sample rate conversion) + // n >= 3 very high latency or very small notification interval (unused). + const uint32_t nBuffering = 2; mNotificationFramesAct = mNotificationFramesReq; @@ -1013,18 +1193,18 @@ status_t AudioTrack::createTrack_l() // Same comment as below about ignoring frameCount parameter for set() frameCount = mSharedBuffer->size(); } else if (frameCount == 0) { - frameCount = afFrameCount; + frameCount = mAfFrameCount; } if (mNotificationFramesAct != frameCount) { mNotificationFramesAct = frameCount; } } else if (mSharedBuffer != 0) { - - // Ensure that buffer alignment matches channel count - // 8-bit data in shared memory is not currently supported by AudioFlinger - size_t alignment = audio_bytes_per_sample( - mFormat == AUDIO_FORMAT_PCM_8_BIT ? AUDIO_FORMAT_PCM_16_BIT : mFormat); + // FIXME: Ensure client side memory buffers need + // not have additional alignment beyond sample + // (e.g. 16 bit stereo accessed as 32 bit frame). + size_t alignment = audio_bytes_per_sample(mFormat); if (alignment & 1) { + // for AUDIO_FORMAT_PCM_24_BIT_PACKED (not exposed through Java). alignment = 1; } if (mChannelCount > 1) { @@ -1042,40 +1222,19 @@ status_t AudioTrack::createTrack_l() // there's no frameCount parameter. // But when initializing a shared buffer AudioTrack via set(), // there _is_ a frameCount parameter. We silently ignore it. - frameCount = mSharedBuffer->size() / mFrameSizeAF; - - } else if (!(mFlags & AUDIO_OUTPUT_FLAG_FAST)) { - - // FIXME move these calculations and associated checks to server - - // Ensure that buffer depth covers at least audio hardware latency - uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate); - ALOGV("afFrameCount=%zu, minBufCount=%d, afSampleRate=%u, afLatency=%d", - afFrameCount, minBufCount, afSampleRate, afLatency); - if (minBufCount <= nBuffering) { - minBufCount = nBuffering; - } - - size_t minFrameCount = afFrameCount * minBufCount * uint64_t(mSampleRate) / afSampleRate; - ALOGV("minFrameCount: %zu, afFrameCount=%zu, minBufCount=%d, sampleRate=%u, afSampleRate=%u" - ", afLatency=%d", - minFrameCount, afFrameCount, minBufCount, mSampleRate, afSampleRate, afLatency); - - if (frameCount == 0) { - frameCount = minFrameCount; - } else if (frameCount < minFrameCount) { - // not ALOGW because it happens all the time when playing key clicks over A2DP - ALOGV("Minimum buffer size corrected from %zu to %zu", - frameCount, minFrameCount); - frameCount = minFrameCount; - } - // Make sure that application is notified with sufficient margin before underrun - if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/nBuffering) { - mNotificationFramesAct = frameCount/nBuffering; - } - + frameCount = mSharedBuffer->size() / mFrameSize; } else { - // For fast tracks, the frame count calculations and checks are done by server + // For fast tracks the frame count calculations and checks are done by server + + if ((mFlags & AUDIO_OUTPUT_FLAG_FAST) == 0) { + // for normal tracks precompute the frame count based on speed. + const size_t minFrameCount = calculateMinFrameCount( + mAfLatency, mAfFrameCount, mAfSampleRate, mSampleRate, + mPlaybackRate.mSpeed); + if (frameCount < minFrameCount) { + frameCount = minFrameCount; + } + } } IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT; @@ -1101,12 +1260,10 @@ status_t AudioTrack::createTrack_l() size_t temp = frameCount; // temp may be replaced by a revised value of frameCount, // but we will still need the original value also + int originalSessionId = mSessionId; sp<IAudioTrack> track = audioFlinger->createTrack(streamType, mSampleRate, - // AudioFlinger only sees 16-bit PCM - mFormat == AUDIO_FORMAT_PCM_8_BIT && - !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT) ? - AUDIO_FORMAT_PCM_16_BIT : mFormat, + mFormat, mChannelMask, &temp, &trackFlags, @@ -1116,6 +1273,8 @@ status_t AudioTrack::createTrack_l() &mSessionId, mClientUid, &status); + ALOGE_IF(originalSessionId != AUDIO_SESSION_ALLOCATE && mSessionId != originalSessionId, + "session ID changed from %d to %d", originalSessionId, mSessionId); if (status != NO_ERROR) { ALOGE("AudioFlinger could not create track, status: %d", status); @@ -1161,23 +1320,10 @@ status_t AudioTrack::createTrack_l() if (trackFlags & IAudioFlinger::TRACK_FAST) { ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %zu", frameCount); mAwaitBoost = true; - if (mSharedBuffer == 0) { - // Theoretically double-buffering is not required for fast tracks, - // due to tighter scheduling. But in practice, to accommodate kernels with - // scheduling jitter, and apps with computation jitter, we use double-buffering. - if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/nBuffering) { - mNotificationFramesAct = frameCount/nBuffering; - } - } } else { ALOGV("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %zu", frameCount); // once denied, do not request again if IAudioTrack is re-created mFlags = (audio_output_flags_t) (mFlags & ~AUDIO_OUTPUT_FLAG_FAST); - if (mSharedBuffer == 0) { - if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/nBuffering) { - mNotificationFramesAct = frameCount/nBuffering; - } - } } } if (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) { @@ -1200,6 +1346,16 @@ status_t AudioTrack::createTrack_l() //return NO_INIT; } } + // Make sure that application is notified with sufficient margin before underrun + if (mSharedBuffer == 0 && audio_is_linear_pcm(mFormat)) { + // Theoretically double-buffering is not required for fast tracks, + // due to tighter scheduling. But in practice, to accommodate kernels with + // scheduling jitter, and apps with computation jitter, we use double-buffering + // for fast tracks just like normal streaming tracks. + if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount / nBuffering) { + mNotificationFramesAct = frameCount / nBuffering; + } + } // We retain a copy of the I/O handle, but don't own the reference mOutput = output; @@ -1211,14 +1367,19 @@ status_t AudioTrack::createTrack_l() // address space. AudioFlinger::TrackBase::mBuffer is for the server address space. void* buffers; if (mSharedBuffer == 0) { - buffers = (char*)cblk + sizeof(audio_track_cblk_t); + buffers = cblk + 1; } else { buffers = mSharedBuffer->pointer(); + if (buffers == NULL) { + ALOGE("Could not get buffer pointer"); + return NO_INIT; + } } mAudioTrack->attachAuxEffect(mAuxEffectId); + // FIXME doesn't take into account speed or future sample rate changes (until restoreTrack) // FIXME don't believe this lie - mLatency = afLatency + (1000*frameCount) / mSampleRate; + mLatency = mAfLatency + (1000*frameCount) / mSampleRate; mFrameCount = frameCount; // If IAudioTrack is re-created, don't let the requested frameCount @@ -1227,12 +1388,15 @@ status_t AudioTrack::createTrack_l() mReqFrameCount = frameCount; } + // reset server position to 0 as we have new cblk. + mServer = 0; + // update proxy if (mSharedBuffer == 0) { mStaticProxy.clear(); - mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF); + mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize); } else { - mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF); + mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize); mProxy = mStaticProxy; } @@ -1241,12 +1405,24 @@ status_t AudioTrack::createTrack_l() gain_from_float(mVolume[AUDIO_INTERLEAVE_RIGHT]))); mProxy->setSendLevel(mSendLevel); - mProxy->setSampleRate(mSampleRate); + const uint32_t effectiveSampleRate = adjustSampleRate(mSampleRate, mPlaybackRate.mPitch); + const float effectiveSpeed = adjustSpeed(mPlaybackRate.mSpeed, mPlaybackRate.mPitch); + const float effectivePitch = adjustPitch(mPlaybackRate.mPitch); + mProxy->setSampleRate(effectiveSampleRate); + + AudioPlaybackRate playbackRateTemp = mPlaybackRate; + playbackRateTemp.mSpeed = effectiveSpeed; + playbackRateTemp.mPitch = effectivePitch; + mProxy->setPlaybackRate(playbackRateTemp); mProxy->setMinimum(mNotificationFramesAct); mDeathNotifier = new DeathNotifier(this); IInterface::asBinder(mAudioTrack)->linkToDeath(mDeathNotifier, this); + if (mDeviceCallback != 0) { + AudioSystem::addAudioDeviceCallback(mDeviceCallback, mOutput); + } + return NO_ERROR; } @@ -1258,15 +1434,21 @@ release: return status; } -status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) +status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount, size_t *nonContig) { if (audioBuffer == NULL) { + if (nonContig != NULL) { + *nonContig = 0; + } return BAD_VALUE; } if (mTransfer != TRANSFER_OBTAIN) { audioBuffer->frameCount = 0; audioBuffer->size = 0; audioBuffer->raw = NULL; + if (nonContig != NULL) { + *nonContig = 0; + } return INVALID_OPERATION; } @@ -1285,7 +1467,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) ALOGE("%s invalid waitCount %d", __func__, waitCount); requested = NULL; } - return obtainBuffer(audioBuffer, requested); + return obtainBuffer(audioBuffer, requested, NULL /*elapsed*/, nonContig); } status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested, @@ -1352,7 +1534,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *re } while ((status == DEAD_OBJECT) && (tryCounter-- > 0)); audioBuffer->frameCount = buffer.mFrameCount; - audioBuffer->size = buffer.mFrameCount * mFrameSizeAF; + audioBuffer->size = buffer.mFrameCount * mFrameSize; audioBuffer->raw = buffer.mRaw; if (nonContig != NULL) { *nonContig = buffer.mNonContig; @@ -1360,13 +1542,14 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *re return status; } -void AudioTrack::releaseBuffer(Buffer* audioBuffer) +void AudioTrack::releaseBuffer(const Buffer* audioBuffer) { + // FIXME add error checking on mode, by adding an internal version if (mTransfer == TRANSFER_SHARED) { return; } - size_t stepCount = audioBuffer->size / mFrameSizeAF; + size_t stepCount = audioBuffer->size / mFrameSize; if (stepCount == 0) { return; } @@ -1431,15 +1614,8 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking) return ssize_t(err); } - size_t toWrite; - if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) { - // Divide capacity by 2 to take expansion into account - toWrite = audioBuffer.size >> 1; - memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) buffer, toWrite); - } else { - toWrite = audioBuffer.size; - memcpy(audioBuffer.i8, buffer, toWrite); - } + size_t toWrite = audioBuffer.size; + memcpy(audioBuffer.i8, buffer, toWrite); buffer = ((const char *) buffer) + toWrite; userSize -= toWrite; written += toWrite; @@ -1558,10 +1734,10 @@ nsecs_t AudioTrack::processAudioBuffer() // AudioSystem cache. We should not exit here but after calling the callback so // that the upper layers can recreate the track if (!isOffloadedOrDirect_l() || (mSequence == mObservedSequence)) { - status_t status = restoreTrack_l("processAudioBuffer"); - mLock.unlock(); - // Run again immediately, but with a new IAudioTrack - return 0; + status_t status __unused = restoreTrack_l("processAudioBuffer"); + // FIXME unused status + // after restoration, continue below to make sure that the loop and buffer events + // are notified because they have been cleared from mCblk->mFlags above. } } @@ -1610,9 +1786,9 @@ nsecs_t AudioTrack::processAudioBuffer() } // Cache other fields that will be needed soon - uint32_t loopPeriod = mLoopPeriod; uint32_t sampleRate = mSampleRate; - uint32_t notificationFrames = mNotificationFramesAct; + float speed = mPlaybackRate.mSpeed; + const uint32_t notificationFrames = mNotificationFramesAct; if (mRefreshRemaining) { mRefreshRemaining = false; mRemainingFrames = notificationFrames; @@ -1622,13 +1798,42 @@ nsecs_t AudioTrack::processAudioBuffer() uint32_t sequence = mSequence; sp<AudioTrackClientProxy> proxy = mProxy; + // Determine the number of new loop callback(s) that will be needed, while locked. + int loopCountNotifications = 0; + uint32_t loopPeriod = 0; // time in frames for next EVENT_LOOP_END or EVENT_BUFFER_END + + if (mLoopCount > 0) { + int loopCount; + size_t bufferPosition; + mStaticProxy->getBufferPositionAndLoopCount(&bufferPosition, &loopCount); + loopPeriod = ((loopCount > 0) ? mLoopEnd : mFrameCount) - bufferPosition; + loopCountNotifications = min(mLoopCountNotified - loopCount, kMaxLoopCountNotifications); + mLoopCountNotified = loopCount; // discard any excess notifications + } else if (mLoopCount < 0) { + // FIXME: We're not accurate with notification count and position with infinite looping + // since loopCount from server side will always return -1 (we could decrement it). + size_t bufferPosition = mStaticProxy->getBufferPosition(); + loopCountNotifications = int((flags & (CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL)) != 0); + loopPeriod = mLoopEnd - bufferPosition; + } else if (/* mLoopCount == 0 && */ mSharedBuffer != 0) { + size_t bufferPosition = mStaticProxy->getBufferPosition(); + loopPeriod = mFrameCount - bufferPosition; + } + // These fields don't need to be cached, because they are assigned only by set(): - // mTransfer, mCbf, mUserData, mFormat, mFrameSize, mFrameSizeAF, mFlags + // mTransfer, mCbf, mUserData, mFormat, mFrameSize, mFlags // mFlags is also assigned by createTrack_l(), but not the bit we care about. mLock.unlock(); + // get anchor time to account for callbacks. + const nsecs_t timeBeforeCallbacks = systemTime(); + if (waitStreamEnd) { + // FIXME: Instead of blocking in proxy->waitStreamEndDone(), Callback thread + // should wait on proxy futex and handle CBLK_STREAM_END_DONE within this function + // (and make sure we don't callback for more data while we're stopping). + // This helps with position, marker notifications, and track invalidation. struct timespec timeout; timeout.tv_sec = WAIT_STREAM_END_TIMEOUT_SEC; timeout.tv_nsec = 0; @@ -1662,10 +1867,9 @@ nsecs_t AudioTrack::processAudioBuffer() if (newUnderrun) { mCbf(EVENT_UNDERRUN, mUserData, NULL); } - // FIXME we will miss loops if loop cycle was signaled several times since last call - // to processAudioBuffer() - if (flags & (CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL)) { + while (loopCountNotifications > 0) { mCbf(EVENT_LOOP_END, mUserData, NULL); + --loopCountNotifications; } if (flags & CBLK_BUFFER_END) { mCbf(EVENT_BUFFER_END, mUserData, NULL); @@ -1701,10 +1905,11 @@ nsecs_t AudioTrack::processAudioBuffer() minFrames = markerPosition - position; } if (loopPeriod > 0 && loopPeriod < minFrames) { + // loopPeriod is already adjusted for actual position. minFrames = loopPeriod; } - if (updatePeriod > 0 && updatePeriod < minFrames) { - minFrames = updatePeriod; + if (updatePeriod > 0) { + minFrames = min(minFrames, uint32_t(newPosition - position)); } // If > 0, poll periodically to recover from a stuck server. A good value is 2. @@ -1713,12 +1918,17 @@ nsecs_t AudioTrack::processAudioBuffer() minFrames = kPoll * notificationFrames; } + // This "fudge factor" avoids soaking CPU, and compensates for late progress by server + static const nsecs_t kWaitPeriodNs = WAIT_PERIOD_MS * 1000000LL; + const nsecs_t timeAfterCallbacks = systemTime(); + // Convert frame units to time units nsecs_t ns = NS_WHENEVER; if (minFrames != (uint32_t) ~0) { - // This "fudge factor" avoids soaking CPU, and compensates for late progress by server - static const nsecs_t kFudgeNs = 10000000LL; // 10 ms - ns = ((minFrames * 1000000000LL) / sampleRate) + kFudgeNs; + ns = framesToNanoseconds(minFrames, sampleRate, speed) + kWaitPeriodNs; + ns -= (timeAfterCallbacks - timeBeforeCallbacks); // account for callback time + // TODO: Should we warn if the callback time is too long? + if (ns < 0) ns = 0; } // If not supplying data by EVENT_MORE_DATA, then we're done @@ -1726,6 +1936,13 @@ nsecs_t AudioTrack::processAudioBuffer() return ns; } + // EVENT_MORE_DATA callback handling. + // Timing for linear pcm audio data formats can be derived directly from the + // buffer fill level. + // Timing for compressed data is not directly available from the buffer fill level, + // rather indirectly from waiting for blocking mode callbacks or waiting for obtain() + // to return a certain fill level. + struct timespec timeout; const struct timespec *requested = &ClientProxy::kForever; if (ns != NS_WHENEVER) { @@ -1756,24 +1973,21 @@ nsecs_t AudioTrack::processAudioBuffer() return NS_NEVER; } - if (mRetryOnPartialBuffer && !isOffloaded()) { + if (mRetryOnPartialBuffer && audio_is_linear_pcm(mFormat)) { mRetryOnPartialBuffer = false; if (avail < mRemainingFrames) { - int64_t myns = ((mRemainingFrames - avail) * 1100000000LL) / sampleRate; - if (ns < 0 || myns < ns) { + if (ns > 0) { // account for obtain time + const nsecs_t timeNow = systemTime(); + ns = max((nsecs_t)0, ns - (timeNow - timeAfterCallbacks)); + } + nsecs_t myns = framesToNanoseconds(mRemainingFrames - avail, sampleRate, speed); + if (ns < 0 /* NS_WHENEVER */ || myns < ns) { ns = myns; } return ns; } } - // Divide buffer size by 2 to take into account the expansion - // due to 8 to 16 bit conversion: the callback must fill only half - // of the destination buffer - if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) { - audioBuffer.size >>= 1; - } - size_t reqSize = audioBuffer.size; mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); size_t writtenSize = audioBuffer.size; @@ -1790,16 +2004,45 @@ nsecs_t AudioTrack::processAudioBuffer() // Keep this thread going to handle timed events and // still try to get more data in intervals of WAIT_PERIOD_MS // but don't just loop and block the CPU, so wait - return WAIT_PERIOD_MS * 1000000LL; - } - if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) { - // 8 to 16 bit conversion, note that source and destination are the same address - memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) audioBuffer.i8, writtenSize); - audioBuffer.size <<= 1; + // mCbf(EVENT_MORE_DATA, ...) might either + // (1) Block until it can fill the buffer, returning 0 size on EOS. + // (2) Block until it can fill the buffer, returning 0 data (silence) on EOS. + // (3) Return 0 size when no data is available, does not wait for more data. + // + // (1) and (2) occurs with AudioPlayer/AwesomePlayer; (3) occurs with NuPlayer. + // We try to compute the wait time to avoid a tight sleep-wait cycle, + // especially for case (3). + // + // The decision to support (1) and (2) affect the sizing of mRemainingFrames + // and this loop; whereas for case (3) we could simply check once with the full + // buffer size and skip the loop entirely. + + nsecs_t myns; + if (audio_is_linear_pcm(mFormat)) { + // time to wait based on buffer occupancy + const nsecs_t datans = mRemainingFrames <= avail ? 0 : + framesToNanoseconds(mRemainingFrames - avail, sampleRate, speed); + // audio flinger thread buffer size (TODO: adjust for fast tracks) + const nsecs_t afns = framesToNanoseconds(mAfFrameCount, mAfSampleRate, speed); + // add a half the AudioFlinger buffer time to avoid soaking CPU if datans is 0. + myns = datans + (afns / 2); + } else { + // FIXME: This could ping quite a bit if the buffer isn't full. + // Note that when mState is stopping we waitStreamEnd, so it never gets here. + myns = kWaitPeriodNs; + } + if (ns > 0) { // account for obtain and callback time + const nsecs_t timeNow = systemTime(); + ns = max((nsecs_t)0, ns - (timeNow - timeAfterCallbacks)); + } + if (ns < 0 /* NS_WHENEVER */ || myns < ns) { + ns = myns; + } + return ns; } - size_t releasedFrames = audioBuffer.size / mFrameSizeAF; + size_t releasedFrames = writtenSize / mFrameSize; audioBuffer.frameCount = releasedFrames; mRemainingFrames -= releasedFrames; if (misalignment >= releasedFrames) { @@ -1827,7 +2070,7 @@ nsecs_t AudioTrack::processAudioBuffer() // that total to a sum == notificationFrames. if (0 < misalignment && misalignment <= mRemainingFrames) { mRemainingFrames = misalignment; - return (mRemainingFrames * 1100000000LL) / sampleRate; + return ((double)mRemainingFrames * 1100000000) / ((double)sampleRate * speed); } #endif @@ -1844,51 +2087,49 @@ status_t AudioTrack::restoreTrack_l(const char *from) ALOGW("dead IAudioTrack, %s, creating a new one from %s()", isOffloadedOrDirect_l() ? "Offloaded or Direct" : "PCM", from); ++mSequence; - status_t result; // refresh the audio configuration cache in this process to make sure we get new // output parameters and new IAudioFlinger in createTrack_l() AudioSystem::clearAudioConfigCache(); - if (isOffloadedOrDirect_l()) { - // FIXME re-creation of offloaded tracks is not yet implemented + if (isOffloadedOrDirect_l() || mDoNotReconnect) { + // FIXME re-creation of offloaded and direct tracks is not yet implemented; + // reconsider enabling for linear PCM encodings when position can be preserved. return DEAD_OBJECT; } // save the old static buffer position - size_t bufferPosition = mStaticProxy != NULL ? mStaticProxy->getBufferPosition() : 0; + size_t bufferPosition = 0; + int loopCount = 0; + if (mStaticProxy != 0) { + mStaticProxy->getBufferPositionAndLoopCount(&bufferPosition, &loopCount); + } // If a new IAudioTrack is successfully created, createTrack_l() will modify the // following member variables: mAudioTrack, mCblkMemory and mCblk. // It will also delete the strong references on previous IAudioTrack and IMemory. // If a new IAudioTrack cannot be created, the previous (dead) instance will be left intact. - result = createTrack_l(); - - // take the frames that will be lost by track recreation into account in saved position - (void) updateAndGetPosition_l(); - mPosition = mReleased; + status_t result = createTrack_l(); if (result == NO_ERROR) { - // continue playback from last known position, but - // don't attempt to restore loop after invalidation; it's difficult and not worthwhile - if (mStaticProxy != NULL) { - mLoopPeriod = 0; - mStaticProxy->setLoop(bufferPosition, mFrameCount, 0); - } - // FIXME How do we simulate the fact that all frames present in the buffer at the time of - // track destruction have been played? This is critical for SoundPool implementation - // This must be broken, and needs to be tested/debugged. -#if 0 - // restore write index and set other indexes to reflect empty buffer status - if (!strcmp(from, "start")) { - // Make sure that a client relying on callback events indicating underrun or - // the actual amount of audio frames played (e.g SoundPool) receives them. - if (mSharedBuffer == 0) { - // restart playback even if buffer is not completely filled. - android_atomic_or(CBLK_FORCEREADY, &mCblk->mFlags); + // take the frames that will be lost by track recreation into account in saved position + // For streaming tracks, this is the amount we obtained from the user/client + // (not the number actually consumed at the server - those are already lost). + if (mStaticProxy == 0) { + mPosition = mReleased; + } + // Continue playback from last known position and restore loop. + if (mStaticProxy != 0) { + if (loopCount != 0) { + mStaticProxy->setBufferPositionAndLoop(bufferPosition, + mLoopStart, mLoopEnd, loopCount); + } else { + mStaticProxy->setBufferPosition(bufferPosition); + if (bufferPosition == mFrameCount) { + ALOGD("restoring track at end of static buffer"); + } } } -#endif if (mState == STATE_ACTIVE) { result = mAudioTrack->start(); } @@ -1923,6 +2164,19 @@ uint32_t AudioTrack::updateAndGetPosition_l() return mPosition += (uint32_t) delta; } +bool AudioTrack::isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const +{ + // applicable for mixing tracks only (not offloaded or direct) + if (mStaticProxy != 0) { + return true; // static tracks do not have issues with buffer sizing. + } + const size_t minFrameCount = + calculateMinFrameCount(mAfLatency, mAfFrameCount, mAfSampleRate, sampleRate, speed); + ALOGV("isSampleRateSpeedAllowed_l mFrameCount %zu minFrameCount %zu", + mFrameCount, minFrameCount); + return mFrameCount >= minFrameCount; +} + status_t AudioTrack::setParameters(const String8& keyValuePairs) { AutoMutex lock(mLock); @@ -1932,6 +2186,11 @@ status_t AudioTrack::setParameters(const String8& keyValuePairs) status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) { AutoMutex lock(mLock); + + bool previousTimestampValid = mPreviousTimestampValid; + // Set false here to cover all the error return cases. + mPreviousTimestampValid = false; + // FIXME not implemented for fast tracks; should use proxy and SSQ if (mFlags & AUDIO_OUTPUT_FLAG_FAST) { return INVALID_OPERATION; @@ -1956,7 +2215,12 @@ status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) } if (mCblk->mFlags & CBLK_INVALID) { - restoreTrack_l("getTimestamp"); + const status_t status = restoreTrack_l("getTimestamp"); + if (status != OK) { + // per getTimestamp() API doc in header, we return DEAD_OBJECT here, + // recommending that the track be recreated. + return DEAD_OBJECT; + } } // The presented frame count must always lag behind the consumed frame count. @@ -1975,7 +2239,12 @@ status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) } // Check whether a pending flush or stop has completed, as those commands may - // be asynchronous or return near finish. + // be asynchronous or return near finish or exhibit glitchy behavior. + // + // Originally this showed up as the first timestamp being a continuation of + // the previous song under gapless playback. + // However, we sometimes see zero timestamps, then a glitch of + // the previous song's position, and then correct timestamps afterwards. if (mStartUs != 0 && mSampleRate != 0) { static const int kTimeJitterUs = 100000; // 100 ms static const int k1SecUs = 1000000; @@ -1988,20 +2257,34 @@ status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) return WOULD_BLOCK; // stale timestamp time, occurs before start. } const int64_t deltaTimeUs = timestampTimeUs - mStartUs; - const int64_t deltaPositionByUs = timestamp.mPosition * 1000000LL / mSampleRate; + const int64_t deltaPositionByUs = (double)timestamp.mPosition * 1000000 + / ((double)mSampleRate * mPlaybackRate.mSpeed); if (deltaPositionByUs > deltaTimeUs + kTimeJitterUs) { // Verify that the counter can't count faster than the sample rate - // since the start time. If greater, then that means we have failed + // since the start time. If greater, then that means we may have failed // to completely flush or stop the previous playing track. - ALOGW("incomplete flush or stop:" + ALOGW_IF(!mTimestampStartupGlitchReported, + "getTimestamp startup glitch detected" " deltaTimeUs(%lld) deltaPositionUs(%lld) tsmPosition(%u)", (long long)deltaTimeUs, (long long)deltaPositionByUs, timestamp.mPosition); + mTimestampStartupGlitchReported = true; + if (previousTimestampValid + && mPreviousTimestamp.mPosition == 0 /* should be true if valid */) { + timestamp = mPreviousTimestamp; + mPreviousTimestampValid = true; + return NO_ERROR; + } return WOULD_BLOCK; } + if (deltaPositionByUs != 0) { + mStartUs = 0; // don't check again, we got valid nonzero position. + } + } else { + mStartUs = 0; // don't check again, start time expired. } - mStartUs = 0; // no need to check again, start timestamp has either expired or unneeded. + mTimestampStartupGlitchReported = false; } } else { // Update the mapping between local consumed (mPosition) and server consumed (mServer) @@ -2029,6 +2312,46 @@ status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) // IAudioTrack. And timestamp.mPosition is initially in server's // point of view, so we need to apply the same fudge factor to it. } + + // Prevent retrograde motion in timestamp. + // This is sometimes caused by erratic reports of the available space in the ALSA drivers. + if (status == NO_ERROR) { + if (previousTimestampValid) { +#define TIME_TO_NANOS(time) ((uint64_t)time.tv_sec * 1000000000 + time.tv_nsec) + const uint64_t previousTimeNanos = TIME_TO_NANOS(mPreviousTimestamp.mTime); + const uint64_t currentTimeNanos = TIME_TO_NANOS(timestamp.mTime); +#undef TIME_TO_NANOS + if (currentTimeNanos < previousTimeNanos) { + ALOGW("retrograde timestamp time"); + // FIXME Consider blocking this from propagating upwards. + } + + // Looking at signed delta will work even when the timestamps + // are wrapping around. + int32_t deltaPosition = static_cast<int32_t>(timestamp.mPosition + - mPreviousTimestamp.mPosition); + // position can bobble slightly as an artifact; this hides the bobble + static const int32_t MINIMUM_POSITION_DELTA = 8; + if (deltaPosition < 0) { + // Only report once per position instead of spamming the log. + if (!mRetrogradeMotionReported) { + ALOGW("retrograde timestamp position corrected, %d = %u - %u", + deltaPosition, + timestamp.mPosition, + mPreviousTimestamp.mPosition); + mRetrogradeMotionReported = true; + } + } else { + mRetrogradeMotionReported = false; + } + if (deltaPosition < MINIMUM_POSITION_DELTA) { + timestamp = mPreviousTimestamp; // Use last valid timestamp. + } + } + mPreviousTimestamp = timestamp; + mPreviousTimestampValid = true; + } + return status; } @@ -2075,7 +2398,8 @@ status_t AudioTrack::dump(int fd, const Vector<String16>& args __unused) const snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%zu)\n", mFormat, mChannelCount, mFrameCount); result.append(buffer); - snprintf(buffer, 255, " sample rate(%u), status(%d)\n", mSampleRate, mStatus); + snprintf(buffer, 255, " sample rate(%u), speed(%f), status(%d)\n", + mSampleRate, mPlaybackRate.mSpeed, mStatus); result.append(buffer); snprintf(buffer, 255, " state(%d), latency (%d)\n", mState, mLatency); result.append(buffer); @@ -2089,6 +2413,48 @@ uint32_t AudioTrack::getUnderrunFrames() const return mProxy->getUnderrunFrames(); } +status_t AudioTrack::addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback) +{ + if (callback == 0) { + ALOGW("%s adding NULL callback!", __FUNCTION__); + return BAD_VALUE; + } + AutoMutex lock(mLock); + if (mDeviceCallback == callback) { + ALOGW("%s adding same callback!", __FUNCTION__); + return INVALID_OPERATION; + } + status_t status = NO_ERROR; + if (mOutput != AUDIO_IO_HANDLE_NONE) { + if (mDeviceCallback != 0) { + ALOGW("%s callback already present!", __FUNCTION__); + AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput); + } + status = AudioSystem::addAudioDeviceCallback(callback, mOutput); + } + mDeviceCallback = callback; + return status; +} + +status_t AudioTrack::removeAudioDeviceCallback( + const sp<AudioSystem::AudioDeviceCallback>& callback) +{ + if (callback == 0) { + ALOGW("%s removing NULL callback!", __FUNCTION__); + return BAD_VALUE; + } + AutoMutex lock(mLock); + if (mDeviceCallback != callback) { + ALOGW("%s removing different callback!", __FUNCTION__); + return INVALID_OPERATION; + } + if (mOutput != AUDIO_IO_HANDLE_NONE) { + AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput); + } + mDeviceCallback = 0; + return NO_ERROR; +} + // ========================================================================= void AudioTrack::DeathNotifier::binderDied(const wp<IBinder>& who __unused) @@ -2148,8 +2514,8 @@ bool AudioTrack::AudioTrackThread::threadLoop() case NS_NEVER: return false; case NS_WHENEVER: - // FIXME increase poll interval, or make event-driven - ns = 1000000000LL; + // Event driven: call wake() when callback notifications conditions change. + ns = INT64_MAX; // fall through default: LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %" PRId64, ns); @@ -2182,6 +2548,21 @@ void AudioTrack::AudioTrackThread::resume() } } +void AudioTrack::AudioTrackThread::wake() +{ + AutoMutex _l(mMyLock); + if (!mPaused) { + // wake() might be called while servicing a callback - ignore the next + // pause time and call processAudioBuffer. + mIgnoreNextPausedInt = true; + if (mPausedInt && mPausedNs > 0) { + // audio track is active and internally paused with timeout. + mPausedInt = false; + mMyCond.signal(); + } + } +} + void AudioTrack::AudioTrackThread::pauseInternal(nsecs_t ns) { AutoMutex _l(mMyLock); @@ -2189,4 +2570,4 @@ void AudioTrack::AudioTrackThread::pauseInternal(nsecs_t ns) mPausedNs = ns; } -}; // namespace android +} // namespace android diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index ff24475..6a51a76 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -28,7 +28,21 @@ namespace android { // used to clamp a value to size_t. TODO: move to another file. template <typename T> size_t clampToSize(T x) { - return x > SIZE_MAX ? SIZE_MAX : x < 0 ? 0 : (size_t) x; + return sizeof(T) > sizeof(size_t) && x > (T) SIZE_MAX ? SIZE_MAX : x < 0 ? 0 : (size_t) x; +} + +// incrementSequence is used to determine the next sequence value +// for the loop and position sequence counters. It should return +// a value between "other" + 1 and "other" + INT32_MAX, the choice of +// which needs to be the "least recently used" sequence value for "self". +// In general, this means (new_self) returned is max(self, other) + 1. + +static uint32_t incrementSequence(uint32_t self, uint32_t other) { + int32_t diff = self - other; + if (diff >= 0 && diff < INT32_MAX) { + return self + 1; // we're already ahead of other. + } + return other + 1; // we're behind, so move just ahead of other. } audio_track_cblk_t::audio_track_cblk_t() @@ -360,6 +374,9 @@ void AudioTrackClientProxy::flush() size_t increment = mFrameCountP2 << 1; size_t mask = increment - 1; audio_track_cblk_t* cblk = mCblk; + // mFlush is 32 bits concatenated as [ flush_counter ] [ newfront_offset ] + // Should newFlush = cblk->u.mStreaming.mRear? Only problem is + // if you want to flush twice to the same rear location after a 32 bit wrap. int32_t newFlush = (cblk->u.mStreaming.mRear & mask) | ((cblk->u.mStreaming.mFlush & ~mask) + increment); android_atomic_release_store(newFlush, &cblk->u.mStreaming.mFlush); @@ -409,7 +426,6 @@ status_t AudioTrackClientProxy::waitStreamEndDone(const struct timespec *request goto end; } // check for obtainBuffer interrupted by client - // check for obtainBuffer interrupted by client if (flags & CBLK_INTERRUPT) { ALOGV("waitStreamEndDone() interrupted by client"); status = -EINTR; @@ -485,8 +501,11 @@ end: StaticAudioTrackClientProxy::StaticAudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize) : AudioTrackClientProxy(cblk, buffers, frameCount, frameSize), - mMutator(&cblk->u.mStatic.mSingleStateQueue), mBufferPosition(0) + mMutator(&cblk->u.mStatic.mSingleStateQueue), + mPosLoopObserver(&cblk->u.mStatic.mPosLoopQueue) { + memset(&mState, 0, sizeof(mState)); + memset(&mPosLoop, 0, sizeof(mPosLoop)); } void StaticAudioTrackClientProxy::flush() @@ -501,30 +520,72 @@ void StaticAudioTrackClientProxy::setLoop(size_t loopStart, size_t loopEnd, int // FIXME Should return an error status return; } - StaticAudioTrackState newState; - newState.mLoopStart = (uint32_t) loopStart; - newState.mLoopEnd = (uint32_t) loopEnd; - newState.mLoopCount = loopCount; - size_t bufferPosition; - if (loopCount == 0 || (bufferPosition = getBufferPosition()) >= loopEnd) { - bufferPosition = loopStart; + mState.mLoopStart = (uint32_t) loopStart; + mState.mLoopEnd = (uint32_t) loopEnd; + mState.mLoopCount = loopCount; + mState.mLoopSequence = incrementSequence(mState.mLoopSequence, mState.mPositionSequence); + // set patch-up variables until the mState is acknowledged by the ServerProxy. + // observed buffer position and loop count will freeze until then to give the + // illusion of a synchronous change. + getBufferPositionAndLoopCount(NULL, NULL); + // preserve behavior to restart at mState.mLoopStart if position exceeds mState.mLoopEnd. + if (mState.mLoopCount != 0 && mPosLoop.mBufferPosition >= mState.mLoopEnd) { + mPosLoop.mBufferPosition = mState.mLoopStart; } - mBufferPosition = bufferPosition; // snapshot buffer position until loop is acknowledged. - (void) mMutator.push(newState); + mPosLoop.mLoopCount = mState.mLoopCount; + (void) mMutator.push(mState); +} + +void StaticAudioTrackClientProxy::setBufferPosition(size_t position) +{ + // This can only happen on a 64-bit client + if (position > UINT32_MAX) { + // FIXME Should return an error status + return; + } + mState.mPosition = (uint32_t) position; + mState.mPositionSequence = incrementSequence(mState.mPositionSequence, mState.mLoopSequence); + // set patch-up variables until the mState is acknowledged by the ServerProxy. + // observed buffer position and loop count will freeze until then to give the + // illusion of a synchronous change. + if (mState.mLoopCount > 0) { // only check if loop count is changing + getBufferPositionAndLoopCount(NULL, NULL); // get last position + } + mPosLoop.mBufferPosition = position; + if (position >= mState.mLoopEnd) { + // no ongoing loop is possible if position is greater than loopEnd. + mPosLoop.mLoopCount = 0; + } + (void) mMutator.push(mState); +} + +void StaticAudioTrackClientProxy::setBufferPositionAndLoop(size_t position, size_t loopStart, + size_t loopEnd, int loopCount) +{ + setLoop(loopStart, loopEnd, loopCount); + setBufferPosition(position); } size_t StaticAudioTrackClientProxy::getBufferPosition() { - size_t bufferPosition; - if (mMutator.ack()) { - bufferPosition = (size_t) mCblk->u.mStatic.mBufferPosition; - if (bufferPosition > mFrameCount) { - bufferPosition = mFrameCount; - } - } else { - bufferPosition = mBufferPosition; + getBufferPositionAndLoopCount(NULL, NULL); + return mPosLoop.mBufferPosition; +} + +void StaticAudioTrackClientProxy::getBufferPositionAndLoopCount( + size_t *position, int *loopCount) +{ + if (mMutator.ack() == StaticAudioTrackSingleStateQueue::SSQ_DONE) { + if (mPosLoopObserver.poll(mPosLoop)) { + ; // a valid mPosLoop should be available if ackDone is true. + } + } + if (position != NULL) { + *position = mPosLoop.mBufferPosition; + } + if (loopCount != NULL) { + *loopCount = mPosLoop.mLoopCount; } - return bufferPosition; } // --------------------------------------------------------------------------- @@ -555,13 +616,24 @@ status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush) front = cblk->u.mStreaming.mFront; if (flush != mFlush) { // effectively obtain then release whatever is in the buffer - size_t mask = (mFrameCountP2 << 1) - 1; + const size_t overflowBit = mFrameCountP2 << 1; + const size_t mask = overflowBit - 1; int32_t newFront = (front & ~mask) | (flush & mask); ssize_t filled = rear - newFront; + if (filled >= (ssize_t)overflowBit) { + // front and rear offsets span the overflow bit of the p2 mask + // so rebasing newFront on the front offset is off by the overflow bit. + // adjust newFront to match rear offset. + ALOGV("flush wrap: filled %zx >= overflowBit %zx", filled, overflowBit); + newFront += overflowBit; + filled -= overflowBit; + } // Rather than shutting down on a corrupt flush, just treat it as a full flush if (!(0 <= filled && (size_t) filled <= mFrameCount)) { - ALOGE("mFlush %#x -> %#x, front %#x, rear %#x, mask %#x, newFront %#x, filled %d=%#x", - mFlush, flush, front, rear, mask, newFront, filled, filled); + ALOGE("mFlush %#x -> %#x, front %#x, rear %#x, mask %#x, newFront %#x, " + "filled %zd=%#x", + mFlush, flush, front, rear, + (unsigned)mask, newFront, filled, (unsigned)filled); newFront = rear; } mFlush = flush; @@ -734,18 +806,23 @@ void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount) (void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags); } +AudioPlaybackRate AudioTrackServerProxy::getPlaybackRate() +{ // do not call from multiple threads without holding lock + mPlaybackRateObserver.poll(mPlaybackRate); + return mPlaybackRate; +} + // --------------------------------------------------------------------------- StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize) : AudioTrackServerProxy(cblk, buffers, frameCount, frameSize), - mObserver(&cblk->u.mStatic.mSingleStateQueue), mPosition(0), + mObserver(&cblk->u.mStatic.mSingleStateQueue), + mPosLoopMutator(&cblk->u.mStatic.mPosLoopQueue), mFramesReadySafe(frameCount), mFramesReady(frameCount), mFramesReadyIsCalledByMultipleThreads(false) { - mState.mLoopStart = 0; - mState.mLoopEnd = 0; - mState.mLoopCount = 0; + memset(&mState, 0, sizeof(mState)); } void StaticAudioTrackServerProxy::framesReadyIsCalledByMultipleThreads() @@ -762,55 +839,97 @@ size_t StaticAudioTrackServerProxy::framesReady() return mFramesReadySafe; } -ssize_t StaticAudioTrackServerProxy::pollPosition() +status_t StaticAudioTrackServerProxy::updateStateWithLoop( + StaticAudioTrackState *localState, const StaticAudioTrackState &update) const { - size_t position = mPosition; - StaticAudioTrackState state; - if (mObserver.poll(state)) { + if (localState->mLoopSequence != update.mLoopSequence) { bool valid = false; - size_t loopStart = state.mLoopStart; - size_t loopEnd = state.mLoopEnd; - if (state.mLoopCount == 0) { - if (loopStart > mFrameCount) { - loopStart = mFrameCount; - } - // ignore loopEnd - mPosition = position = loopStart; - mFramesReady = mFrameCount - mPosition; - mState.mLoopCount = 0; + const size_t loopStart = update.mLoopStart; + const size_t loopEnd = update.mLoopEnd; + size_t position = localState->mPosition; + if (update.mLoopCount == 0) { valid = true; - } else if (state.mLoopCount >= -1) { + } else if (update.mLoopCount >= -1) { if (loopStart < loopEnd && loopEnd <= mFrameCount && loopEnd - loopStart >= MIN_LOOP) { // If the current position is greater than the end of the loop // we "wrap" to the loop start. This might cause an audible pop. if (position >= loopEnd) { - mPosition = position = loopStart; - } - if (state.mLoopCount == -1) { - mFramesReady = INT64_MAX; - } else { - // mFramesReady is 64 bits to handle the effective number of frames - // that the static audio track contains, including loops. - // TODO: Later consider fixing overflow, but does not seem needed now - // as will not overflow if loopStart and loopEnd are Java "ints". - mFramesReady = int64_t(state.mLoopCount) * (loopEnd - loopStart) - + mFrameCount - mPosition; + position = loopStart; } - mState = state; valid = true; } } - if (!valid || mPosition > mFrameCount) { + if (!valid || position > mFrameCount) { + return NO_INIT; + } + localState->mPosition = position; + localState->mLoopCount = update.mLoopCount; + localState->mLoopEnd = loopEnd; + localState->mLoopStart = loopStart; + localState->mLoopSequence = update.mLoopSequence; + } + return OK; +} + +status_t StaticAudioTrackServerProxy::updateStateWithPosition( + StaticAudioTrackState *localState, const StaticAudioTrackState &update) const +{ + if (localState->mPositionSequence != update.mPositionSequence) { + if (update.mPosition > mFrameCount) { + return NO_INIT; + } else if (localState->mLoopCount != 0 && update.mPosition >= localState->mLoopEnd) { + localState->mLoopCount = 0; // disable loop count if position is beyond loop end. + } + localState->mPosition = update.mPosition; + localState->mPositionSequence = update.mPositionSequence; + } + return OK; +} + +ssize_t StaticAudioTrackServerProxy::pollPosition() +{ + StaticAudioTrackState state; + if (mObserver.poll(state)) { + StaticAudioTrackState trystate = mState; + bool result; + const int32_t diffSeq = state.mLoopSequence - state.mPositionSequence; + + if (diffSeq < 0) { + result = updateStateWithLoop(&trystate, state) == OK && + updateStateWithPosition(&trystate, state) == OK; + } else { + result = updateStateWithPosition(&trystate, state) == OK && + updateStateWithLoop(&trystate, state) == OK; + } + if (!result) { + mObserver.done(); + // caution: no update occurs so server state will be inconsistent with client state. ALOGE("%s client pushed an invalid state, shutting down", __func__); mIsShutdown = true; return (ssize_t) NO_INIT; } + mState = trystate; + if (mState.mLoopCount == -1) { + mFramesReady = INT64_MAX; + } else if (mState.mLoopCount == 0) { + mFramesReady = mFrameCount - mState.mPosition; + } else if (mState.mLoopCount > 0) { + // TODO: Later consider fixing overflow, but does not seem needed now + // as will not overflow if loopStart and loopEnd are Java "ints". + mFramesReady = int64_t(mState.mLoopCount) * (mState.mLoopEnd - mState.mLoopStart) + + mFrameCount - mState.mPosition; + } mFramesReadySafe = clampToSize(mFramesReady); // This may overflow, but client is not supposed to rely on it - mCblk->u.mStatic.mBufferPosition = (uint32_t) position; + StaticAudioTrackPosLoop posLoop; + + posLoop.mLoopCount = (int32_t) mState.mLoopCount; + posLoop.mBufferPosition = (uint32_t) mState.mPosition; + mPosLoopMutator.push(posLoop); + mObserver.done(); // safe to read mStatic variables. } - return (ssize_t) position; + return (ssize_t) mState.mPosition; } status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush __unused) @@ -849,7 +968,7 @@ status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush } // As mFramesReady is the total remaining frames in the static audio track, // it is always larger or equal to avail. - LOG_ALWAYS_FATAL_IF(mFramesReady < avail); + LOG_ALWAYS_FATAL_IF(mFramesReady < (int64_t) avail); buffer->mNonContig = mFramesReady == INT64_MAX ? SIZE_MAX : clampToSize(mFramesReady - avail); mUnreleased = avail; return NO_ERROR; @@ -858,7 +977,7 @@ status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) { size_t stepCount = buffer->mFrameCount; - LOG_ALWAYS_FATAL_IF(!(stepCount <= mFramesReady)); + LOG_ALWAYS_FATAL_IF(!((int64_t) stepCount <= mFramesReady)); LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased)); if (stepCount == 0) { // prevent accidental re-use of buffer @@ -868,11 +987,12 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) } mUnreleased -= stepCount; audio_track_cblk_t* cblk = mCblk; - size_t position = mPosition; + size_t position = mState.mPosition; size_t newPosition = position + stepCount; int32_t setFlags = 0; if (!(position <= newPosition && newPosition <= mFrameCount)) { - ALOGW("%s newPosition %zu outside [%zu, %zu]", __func__, newPosition, position, mFrameCount); + ALOGW("%s newPosition %zu outside [%zu, %zu]", __func__, newPosition, position, + mFrameCount); newPosition = mFrameCount; } else if (mState.mLoopCount != 0 && newPosition == mState.mLoopEnd) { newPosition = mState.mLoopStart; @@ -885,7 +1005,7 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) if (newPosition == mFrameCount) { setFlags |= CBLK_BUFFER_END; } - mPosition = newPosition; + mState.mPosition = newPosition; if (mFramesReady != INT64_MAX) { mFramesReady -= stepCount; } @@ -893,7 +1013,10 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) cblk->mServer += stepCount; // This may overflow, but client is not supposed to rely on it - cblk->u.mStatic.mBufferPosition = (uint32_t) newPosition; + StaticAudioTrackPosLoop posLoop; + posLoop.mBufferPosition = mState.mPosition; + posLoop.mLoopCount = mState.mLoopCount; + mPosLoopMutator.push(posLoop); if (setFlags != 0) { (void) android_atomic_or(setFlags, &cblk->mFlags); // this would be a good place to wake a futex diff --git a/media/libmedia/CharacterEncodingDetector.cpp b/media/libmedia/CharacterEncodingDetector.cpp index 41994dc..3020136 100644 --- a/media/libmedia/CharacterEncodingDetector.cpp +++ b/media/libmedia/CharacterEncodingDetector.cpp @@ -89,7 +89,6 @@ void CharacterEncodingDetector::detectAndConvert() { // try combined detection of artist/album/title etc. char buf[1024]; buf[0] = 0; - int idx; bool allprintable = true; for (int i = 0; i < size; i++) { const char *name = mNames.getEntry(i); @@ -169,7 +168,6 @@ void CharacterEncodingDetector::detectAndConvert() { const char *name = mNames.getEntry(i); uint8_t* src = (uint8_t *)mValues.getEntry(i); int len = strlen((char *)src); - uint8_t* dest = src; ALOGV("@@@ checking %s", name); const char *s = mValues.getEntry(i); diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index a94f9ce..0bf503a 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -80,7 +80,8 @@ enum { RELEASE_AUDIO_PATCH, LIST_AUDIO_PATCHES, SET_AUDIO_PORT_CONFIG, - GET_AUDIO_HW_SYNC + GET_AUDIO_HW_SYNC, + SYSTEM_READY }; #define MAX_ITEMS_PER_LIST 1024 @@ -174,9 +175,11 @@ public: uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, + const String16& opPackageName, size_t *pFrameCount, track_flags_t *flags, pid_t tid, + int clientUid, int *sessionId, size_t *notificationFrames, sp<IMemory>& cblk, @@ -190,11 +193,13 @@ public: data.writeInt32(sampleRate); data.writeInt32(format); data.writeInt32(channelMask); + data.writeString16(opPackageName); size_t frameCount = pFrameCount != NULL ? *pFrameCount : 0; data.writeInt64(frameCount); track_flags_t lFlags = flags != NULL ? *flags : (track_flags_t) TRACK_DEFAULT; data.writeInt32(lFlags); data.writeInt32((int32_t) tid); + data.writeInt32((int32_t) clientUid); int lSessionId = AUDIO_SESSION_ALLOCATE; if (sessionId != NULL) { lSessionId = *sessionId; @@ -702,6 +707,7 @@ public: int32_t priority, audio_io_handle_t output, int sessionId, + const String16& opPackageName, status_t *status, int *id, int *enabled) @@ -722,6 +728,7 @@ public: data.writeInt32(priority); data.writeInt32((int32_t) output); data.writeInt32(sessionId); + data.writeString16(opPackageName); status_t lStatus = remote()->transact(CREATE_EFFECT, data, &reply); if (lStatus != NO_ERROR) { @@ -897,6 +904,12 @@ public: } return (audio_hw_sync_t)reply.readInt32(); } + virtual status_t systemReady() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + return remote()->transact(SYSTEM_READY, data, &reply, IBinder::FLAG_ONEWAY); + } }; IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger"); @@ -950,18 +963,19 @@ status_t BnAudioFlinger::onTransact( uint32_t sampleRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); audio_channel_mask_t channelMask = data.readInt32(); + const String16& opPackageName = data.readString16(); size_t frameCount = data.readInt64(); track_flags_t flags = (track_flags_t) data.readInt32(); pid_t tid = (pid_t) data.readInt32(); + int clientUid = data.readInt32(); int sessionId = data.readInt32(); size_t notificationFrames = data.readInt64(); sp<IMemory> cblk; sp<IMemory> buffers; status_t status = NO_ERROR; sp<IAudioRecord> record = openRecord(input, - sampleRate, format, channelMask, &frameCount, &flags, tid, &sessionId, - ¬ificationFrames, - cblk, buffers, &status); + sampleRate, format, channelMask, opPackageName, &frameCount, &flags, tid, + clientUid, &sessionId, ¬ificationFrames, cblk, buffers, &status); LOG_ALWAYS_FATAL_IF((record != 0) != (status == NO_ERROR)); reply->writeInt64(frameCount); reply->writeInt32(flags); @@ -1253,12 +1267,13 @@ status_t BnAudioFlinger::onTransact( int32_t priority = data.readInt32(); audio_io_handle_t output = (audio_io_handle_t) data.readInt32(); int sessionId = data.readInt32(); + const String16 opPackageName = data.readString16(); status_t status = NO_ERROR; int id = 0; int enabled = 0; sp<IEffect> effect = createEffect(&desc, client, priority, output, sessionId, - &status, &id, &enabled); + opPackageName, &status, &id, &enabled); reply->writeInt32(status); reply->writeInt32(id); reply->writeInt32(enabled); @@ -1398,6 +1413,11 @@ status_t BnAudioFlinger::onTransact( reply->writeInt32(getAudioHwSyncForSession((audio_session_t)data.readInt32())); return NO_ERROR; } break; + case SYSTEM_READY: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + systemReady(); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } @@ -1405,4 +1425,4 @@ status_t BnAudioFlinger::onTransact( // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp index 1c299f7..3429d36 100644 --- a/media/libmedia/IAudioFlingerClient.cpp +++ b/media/libmedia/IAudioFlingerClient.cpp @@ -39,25 +39,18 @@ public: { } - void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2) + void ioConfigChanged(audio_io_config_event event, const sp<AudioIoDescriptor>& ioDesc) { Parcel data, reply; data.writeInterfaceToken(IAudioFlingerClient::getInterfaceDescriptor()); data.writeInt32(event); - data.writeInt32((int32_t) ioHandle); - if (event == AudioSystem::STREAM_CONFIG_CHANGED) { - uint32_t stream = *(const uint32_t *)param2; - ALOGV("ioConfigChanged stream %d", stream); - data.writeInt32(stream); - } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) { - const AudioSystem::OutputDescriptor *desc = - (const AudioSystem::OutputDescriptor *)param2; - data.writeInt32(desc->samplingRate); - data.writeInt32(desc->format); - data.writeInt32(desc->channelMask); - data.writeInt64(desc->frameCount); - data.writeInt32(desc->latency); - } + data.writeInt32((int32_t)ioDesc->mIoHandle); + data.write(&ioDesc->mPatch, sizeof(struct audio_patch)); + data.writeInt32(ioDesc->mSamplingRate); + data.writeInt32(ioDesc->mFormat); + data.writeInt32(ioDesc->mChannelMask); + data.writeInt64(ioDesc->mFrameCount); + data.writeInt32(ioDesc->mLatency); remote()->transact(IO_CONFIG_CHANGED, data, &reply, IBinder::FLAG_ONEWAY); } }; @@ -72,24 +65,16 @@ status_t BnAudioFlingerClient::onTransact( switch (code) { case IO_CONFIG_CHANGED: { CHECK_INTERFACE(IAudioFlingerClient, data, reply); - int event = data.readInt32(); - audio_io_handle_t ioHandle = (audio_io_handle_t) data.readInt32(); - const void *param2 = NULL; - AudioSystem::OutputDescriptor desc; - uint32_t stream; - if (event == AudioSystem::STREAM_CONFIG_CHANGED) { - stream = data.readInt32(); - param2 = &stream; - ALOGV("STREAM_CONFIG_CHANGED stream %d", stream); - } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) { - desc.samplingRate = data.readInt32(); - desc.format = (audio_format_t) data.readInt32(); - desc.channelMask = (audio_channel_mask_t) data.readInt32(); - desc.frameCount = data.readInt64(); - desc.latency = data.readInt32(); - param2 = &desc; - } - ioConfigChanged(event, ioHandle, param2); + audio_io_config_event event = (audio_io_config_event)data.readInt32(); + sp<AudioIoDescriptor> ioDesc = new AudioIoDescriptor(); + ioDesc->mIoHandle = (audio_io_handle_t) data.readInt32(); + data.read(&ioDesc->mPatch, sizeof(struct audio_patch)); + ioDesc->mSamplingRate = data.readInt32(); + ioDesc->mFormat = (audio_format_t) data.readInt32(); + ioDesc->mChannelMask = (audio_channel_mask_t) data.readInt32(); + ioDesc->mFrameCount = data.readInt64(); + ioDesc->mLatency = data.readInt32(); + ioConfigChanged(event, ioDesc); return NO_ERROR; } break; default: @@ -99,4 +84,4 @@ status_t BnAudioFlingerClient::onTransact( // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp index 1b8ca2d..76b5924 100644 --- a/media/libmedia/IAudioPolicyService.cpp +++ b/media/libmedia/IAudioPolicyService.cpp @@ -71,6 +71,9 @@ enum { RELEASE_SOUNDTRIGGER_SESSION, GET_PHONE_STATE, REGISTER_POLICY_MIXES, + START_AUDIO_SOURCE, + STOP_AUDIO_SOURCE, + SET_AUDIO_PORT_CALLBACK_ENABLED, }; #define MAX_ITEMS_PER_LIST 1024 @@ -86,13 +89,15 @@ public: virtual status_t setDeviceConnectionState( audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address) + const char *device_address, + const char *device_name) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); data.writeInt32(static_cast <uint32_t>(device)); data.writeInt32(static_cast <uint32_t>(state)); data.writeCString(device_address); + data.writeCString(device_name); remote()->transact(SET_DEVICE_CONNECTION_STATE, data, &reply); return static_cast <status_t> (reply.readInt32()); } @@ -167,10 +172,12 @@ public: audio_io_handle_t *output, audio_session_t session, audio_stream_type_t *stream, + uid_t uid, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, audio_output_flags_t flags, + audio_port_handle_t selectedDeviceId, const audio_offload_info_t *offloadInfo) { Parcel data, reply; @@ -202,10 +209,12 @@ public: data.writeInt32(1); data.writeInt32(*stream); } + data.writeInt32(uid); data.writeInt32(samplingRate); data.writeInt32(static_cast <uint32_t>(format)); data.writeInt32(channelMask); data.writeInt32(static_cast <uint32_t>(flags)); + data.writeInt32(selectedDeviceId); // hasOffloadInfo if (offloadInfo == NULL) { data.writeInt32(0); @@ -269,10 +278,12 @@ public: virtual status_t getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, audio_session_t session, + uid_t uid, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, - audio_input_flags_t flags) + audio_input_flags_t flags, + audio_port_handle_t selectedDeviceId) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); @@ -286,10 +297,12 @@ public: } data.write(attr, sizeof(audio_attributes_t)); data.writeInt32(session); + data.writeInt32(uid); data.writeInt32(samplingRate); data.writeInt32(static_cast <uint32_t>(format)); data.writeInt32(channelMask); data.writeInt32(flags); + data.writeInt32(selectedDeviceId); status_t status = remote()->transact(GET_INPUT_FOR_ATTR, data, &reply); if (status != NO_ERROR) { return status; @@ -634,6 +647,14 @@ public: remote()->transact(REGISTER_CLIENT, data, &reply); } + virtual void setAudioPortCallbacksEnabled(bool enabled) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.writeInt32(enabled ? 1 : 0); + remote()->transact(SET_AUDIO_PORT_CALLBACK_ENABLED, data, &reply); + } + virtual status_t acquireSoundTriggerSession(audio_session_t *session, audio_io_handle_t *ioHandle, audio_devices_t *device) @@ -710,6 +731,42 @@ public: } return status; } + + virtual status_t startAudioSource(const struct audio_port_config *source, + const audio_attributes_t *attributes, + audio_io_handle_t *handle) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + if (source == NULL || attributes == NULL || handle == NULL) { + return BAD_VALUE; + } + data.write(source, sizeof(struct audio_port_config)); + data.write(attributes, sizeof(audio_attributes_t)); + status_t status = remote()->transact(START_AUDIO_SOURCE, data, &reply); + if (status != NO_ERROR) { + return status; + } + status = (status_t)reply.readInt32(); + if (status != NO_ERROR) { + return status; + } + *handle = (audio_io_handle_t)reply.readInt32(); + return status; + } + + virtual status_t stopAudioSource(audio_io_handle_t handle) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.writeInt32(handle); + status_t status = remote()->transact(STOP_AUDIO_SOURCE, data, &reply); + if (status != NO_ERROR) { + return status; + } + status = (status_t)reply.readInt32(); + return status; + } }; IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService"); @@ -728,9 +785,11 @@ status_t BnAudioPolicyService::onTransact( audio_policy_dev_state_t state = static_cast <audio_policy_dev_state_t>(data.readInt32()); const char *device_address = data.readCString(); + const char *device_name = data.readCString(); reply->writeInt32(static_cast<uint32_t> (setDeviceConnectionState(device, state, - device_address))); + device_address, + device_name))); return NO_ERROR; } break; @@ -806,11 +865,13 @@ status_t BnAudioPolicyService::onTransact( if (hasStream) { stream = (audio_stream_type_t)data.readInt32(); } + uid_t uid = (uid_t)data.readInt32(); uint32_t samplingRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); audio_channel_mask_t channelMask = data.readInt32(); audio_output_flags_t flags = static_cast <audio_output_flags_t>(data.readInt32()); + audio_port_handle_t selectedDeviceId = data.readInt32(); bool hasOffloadInfo = data.readInt32() != 0; audio_offload_info_t offloadInfo; if (hasOffloadInfo) { @@ -818,9 +879,9 @@ status_t BnAudioPolicyService::onTransact( } audio_io_handle_t output = 0; status_t status = getOutputForAttr(hasAttributes ? &attr : NULL, - &output, session, &stream, + &output, session, &stream, uid, samplingRate, format, channelMask, - flags, hasOffloadInfo ? &offloadInfo : NULL); + flags, selectedDeviceId, hasOffloadInfo ? &offloadInfo : NULL); reply->writeInt32(status); reply->writeInt32(output); reply->writeInt32(stream); @@ -865,14 +926,16 @@ status_t BnAudioPolicyService::onTransact( audio_attributes_t attr; data.read(&attr, sizeof(audio_attributes_t)); audio_session_t session = (audio_session_t)data.readInt32(); + uid_t uid = (uid_t)data.readInt32(); uint32_t samplingRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); audio_channel_mask_t channelMask = data.readInt32(); audio_input_flags_t flags = (audio_input_flags_t) data.readInt32(); - audio_io_handle_t input; - status_t status = getInputForAttr(&attr, &input, session, + audio_port_handle_t selectedDeviceId = (audio_port_handle_t) data.readInt32(); + audio_io_handle_t input = {}; + status_t status = getInputForAttr(&attr, &input, session, uid, samplingRate, format, channelMask, - flags); + flags, selectedDeviceId); reply->writeInt32(status); if (status == NO_ERROR) { reply->writeInt32(input); @@ -1169,6 +1232,12 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } break; + case SET_AUDIO_PORT_CALLBACK_ENABLED: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + setAudioPortCallbacksEnabled(data.readInt32() == 1); + return NO_ERROR; + } break; + case ACQUIRE_SOUNDTRIGGER_SESSION: { CHECK_INTERFACE(IAudioPolicyService, data, reply); sp<IAudioPolicyServiceClient> client = interface_cast<IAudioPolicyServiceClient>( @@ -1221,6 +1290,27 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } break; + case START_AUDIO_SOURCE: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + struct audio_port_config source; + data.read(&source, sizeof(struct audio_port_config)); + audio_attributes_t attributes; + data.read(&attributes, sizeof(audio_attributes_t)); + audio_io_handle_t handle = {}; + status_t status = startAudioSource(&source, &attributes, &handle); + reply->writeInt32(status); + reply->writeInt32(handle); + return NO_ERROR; + } break; + + case STOP_AUDIO_SOURCE: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + audio_io_handle_t handle = (audio_io_handle_t)data.readInt32(); + status_t status = stopAudioSource(handle); + reply->writeInt32(status); + return NO_ERROR; + } break; + default: return BBinder::onTransact(code, data, reply, flags); } @@ -1228,4 +1318,4 @@ status_t BnAudioPolicyService::onTransact( // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/media/libmedia/IAudioPolicyServiceClient.cpp b/media/libmedia/IAudioPolicyServiceClient.cpp index e802277..65cc7d6 100644 --- a/media/libmedia/IAudioPolicyServiceClient.cpp +++ b/media/libmedia/IAudioPolicyServiceClient.cpp @@ -29,7 +29,8 @@ namespace android { enum { PORT_LIST_UPDATE = IBinder::FIRST_CALL_TRANSACTION, - PATCH_LIST_UPDATE + PATCH_LIST_UPDATE, + MIX_STATE_UPDATE }; class BpAudioPolicyServiceClient : public BpInterface<IAudioPolicyServiceClient> @@ -53,6 +54,15 @@ public: data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor()); remote()->transact(PATCH_LIST_UPDATE, data, &reply, IBinder::FLAG_ONEWAY); } + + void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor()); + data.writeString8(regId); + data.writeInt32(state); + remote()->transact(MIX_STATE_UPDATE, data, &reply, IBinder::FLAG_ONEWAY); + } }; IMPLEMENT_META_INTERFACE(AudioPolicyServiceClient, "android.media.IAudioPolicyServiceClient"); @@ -73,6 +83,13 @@ status_t BnAudioPolicyServiceClient::onTransact( onAudioPatchListUpdate(); return NO_ERROR; } break; + case MIX_STATE_UPDATE: { + CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply); + String8 regId = data.readString8(); + int32_t state = data.readInt32(); + onDynamicPolicyMixStateUpdate(regId, state); + return NO_ERROR; + } default: return BBinder::onTransact(code, data, reply, flags); } @@ -80,4 +97,4 @@ status_t BnAudioPolicyServiceClient::onTransact( // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp index 8a4a383..9d80753 100644 --- a/media/libmedia/IAudioRecord.cpp +++ b/media/libmedia/IAudioRecord.cpp @@ -91,4 +91,4 @@ status_t BnAudioRecord::onTransact( } } -}; // namespace android +} // namespace android diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp index df209fd..651cb61 100644 --- a/media/libmedia/IAudioTrack.cpp +++ b/media/libmedia/IAudioTrack.cpp @@ -292,4 +292,4 @@ status_t BnAudioTrack::onTransact( } } -}; // namespace android +} // namespace android diff --git a/media/libmedia/ICrypto.cpp b/media/libmedia/ICrypto.cpp index f51aaa2..a398ff7 100644 --- a/media/libmedia/ICrypto.cpp +++ b/media/libmedia/ICrypto.cpp @@ -19,6 +19,7 @@ #include <utils/Log.h> #include <binder/Parcel.h> +#include <binder/IMemory.h> #include <media/ICrypto.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/foundation/ADebug.h> @@ -34,6 +35,7 @@ enum { REQUIRES_SECURE_COMPONENT, DECRYPT, NOTIFY_RESOLUTION, + SET_MEDIADRM_SESSION, }; struct BpCrypto : public BpInterface<ICrypto> { @@ -97,7 +99,7 @@ struct BpCrypto : public BpInterface<ICrypto> { const uint8_t key[16], const uint8_t iv[16], CryptoPlugin::Mode mode, - const void *srcPtr, + const sp<IMemory> &sharedBuffer, size_t offset, const CryptoPlugin::SubSample *subSamples, size_t numSubSamples, void *dstPtr, AString *errorDetailMsg) { @@ -126,7 +128,8 @@ struct BpCrypto : public BpInterface<ICrypto> { } data.writeInt32(totalSize); - data.write(srcPtr, totalSize); + data.writeStrongBinder(IInterface::asBinder(sharedBuffer)); + data.writeInt32(offset); data.writeInt32(numSubSamples); data.write(subSamples, sizeof(CryptoPlugin::SubSample) * numSubSamples); @@ -139,7 +142,7 @@ struct BpCrypto : public BpInterface<ICrypto> { ssize_t result = reply.readInt32(); - if (result >= ERROR_DRM_VENDOR_MIN && result <= ERROR_DRM_VENDOR_MAX) { + if (isCryptoError(result)) { errorDetailMsg->setTo(reply.readCString()); } @@ -159,7 +162,28 @@ struct BpCrypto : public BpInterface<ICrypto> { remote()->transact(NOTIFY_RESOLUTION, data, &reply); } + virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) { + Parcel data, reply; + data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); + + writeVector(data, sessionId); + remote()->transact(SET_MEDIADRM_SESSION, data, &reply); + + return reply.readInt32(); + } + private: + void readVector(Parcel &reply, Vector<uint8_t> &vector) const { + uint32_t size = reply.readInt32(); + vector.insertAt((size_t)0, size); + reply.read(vector.editArray(), size); + } + + void writeVector(Parcel &data, Vector<uint8_t> const &vector) const { + data.writeInt32(vector.size()); + data.write(vector.array(), vector.size()); + } + DISALLOW_EVIL_CONSTRUCTORS(BpCrypto); }; @@ -167,6 +191,17 @@ IMPLEMENT_META_INTERFACE(Crypto, "android.hardware.ICrypto"); //////////////////////////////////////////////////////////////////////////////// +void BnCrypto::readVector(const Parcel &data, Vector<uint8_t> &vector) const { + uint32_t size = data.readInt32(); + vector.insertAt((size_t)0, size); + data.read(vector.editArray(), size); +} + +void BnCrypto::writeVector(Parcel *reply, Vector<uint8_t> const &vector) const { + reply->writeInt32(vector.size()); + reply->write(vector.array(), vector.size()); +} + status_t BnCrypto::onTransact( uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { switch (code) { @@ -245,9 +280,9 @@ status_t BnCrypto::onTransact( data.read(iv, sizeof(iv)); size_t totalSize = data.readInt32(); - void *srcData = malloc(totalSize); - memset(srcData, 0, totalSize); - data.read(srcData, totalSize); + sp<IMemory> sharedBuffer = + interface_cast<IMemory>(data.readStrongBinder()); + int32_t offset = data.readInt32(); int32_t numSubSamples = data.readInt32(); @@ -262,8 +297,7 @@ status_t BnCrypto::onTransact( if (secure) { secureBufferId = reinterpret_cast<void *>(static_cast<uintptr_t>(data.readInt64())); } else { - dstPtr = malloc(totalSize); - memset(dstPtr, 0, totalSize); + dstPtr = calloc(1, totalSize); } AString errorDetailMsg; @@ -287,13 +321,15 @@ status_t BnCrypto::onTransact( if (overflow || sumSubsampleSizes != totalSize) { result = -EINVAL; + } else if (offset + totalSize > sharedBuffer->size()) { + result = -EINVAL; } else { result = decrypt( secure, key, iv, mode, - srcData, + sharedBuffer, offset, subSamples, numSubSamples, secure ? secureBufferId : dstPtr, &errorDetailMsg); @@ -301,8 +337,7 @@ status_t BnCrypto::onTransact( reply->writeInt32(result); - if (result >= ERROR_DRM_VENDOR_MIN - && result <= ERROR_DRM_VENDOR_MAX) { + if (isCryptoError(result)) { reply->writeCString(errorDetailMsg.c_str()); } @@ -318,9 +353,6 @@ status_t BnCrypto::onTransact( delete[] subSamples; subSamples = NULL; - free(srcData); - srcData = NULL; - return OK; } @@ -335,6 +367,15 @@ status_t BnCrypto::onTransact( return OK; } + case SET_MEDIADRM_SESSION: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector<uint8_t> sessionId; + readVector(data, sessionId); + reply->writeInt32(setMediaDrmSession(sessionId)); + return OK; + } + default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmedia/IDataSource.cpp b/media/libmedia/IDataSource.cpp new file mode 100644 index 0000000..76d1d68 --- /dev/null +++ b/media/libmedia/IDataSource.cpp @@ -0,0 +1,108 @@ +/* + * Copyright 2015 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 "IDataSource" +#include <utils/Log.h> +#include <utils/Timers.h> + +#include <media/IDataSource.h> + +#include <binder/IMemory.h> +#include <binder/Parcel.h> +#include <media/stagefright/foundation/ADebug.h> + +namespace android { + +enum { + GET_IMEMORY = IBinder::FIRST_CALL_TRANSACTION, + READ_AT, + GET_SIZE, + CLOSE, +}; + +struct BpDataSource : public BpInterface<IDataSource> { + BpDataSource(const sp<IBinder>& impl) : BpInterface<IDataSource>(impl) {} + + virtual sp<IMemory> getIMemory() { + Parcel data, reply; + data.writeInterfaceToken(IDataSource::getInterfaceDescriptor()); + remote()->transact(GET_IMEMORY, data, &reply); + sp<IBinder> binder = reply.readStrongBinder(); + return interface_cast<IMemory>(binder); + } + + virtual ssize_t readAt(off64_t offset, size_t size) { + Parcel data, reply; + data.writeInterfaceToken(IDataSource::getInterfaceDescriptor()); + data.writeInt64(offset); + data.writeInt64(size); + remote()->transact(READ_AT, data, &reply); + return reply.readInt64(); + } + + virtual status_t getSize(off64_t* size) { + Parcel data, reply; + data.writeInterfaceToken(IDataSource::getInterfaceDescriptor()); + remote()->transact(GET_SIZE, data, &reply); + status_t err = reply.readInt32(); + *size = reply.readInt64(); + return err; + } + + virtual void close() { + Parcel data, reply; + data.writeInterfaceToken(IDataSource::getInterfaceDescriptor()); + remote()->transact(CLOSE, data, &reply); + } +}; + +IMPLEMENT_META_INTERFACE(DataSource, "android.media.IDataSource"); + +status_t BnDataSource::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + switch (code) { + case GET_IMEMORY: { + CHECK_INTERFACE(IDataSource, data, reply); + reply->writeStrongBinder(IInterface::asBinder(getIMemory())); + return NO_ERROR; + } break; + case READ_AT: { + CHECK_INTERFACE(IDataSource, data, reply); + off64_t offset = (off64_t) data.readInt64(); + size_t size = (size_t) data.readInt64(); + reply->writeInt64(readAt(offset, size)); + return NO_ERROR; + } break; + case GET_SIZE: { + CHECK_INTERFACE(IDataSource, data, reply); + off64_t size; + status_t err = getSize(&size); + reply->writeInt32(err); + reply->writeInt64(size); + return NO_ERROR; + } break; + case CLOSE: { + CHECK_INTERFACE(IDataSource, data, reply); + close(); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +} // namespace android diff --git a/media/libmedia/IDrm.cpp b/media/libmedia/IDrm.cpp index b08fa82..b1ad0c5 100644 --- a/media/libmedia/IDrm.cpp +++ b/media/libmedia/IDrm.cpp @@ -67,7 +67,10 @@ struct BpDrm : public BpInterface<IDrm> { virtual status_t initCheck() const { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - remote()->transact(INIT_CHECK, data, &reply); + status_t status = remote()->transact(INIT_CHECK, data, &reply); + if (status != OK) { + return status; + } return reply.readInt32(); } @@ -77,7 +80,11 @@ struct BpDrm : public BpInterface<IDrm> { data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); data.write(uuid, 16); data.writeString8(mimeType); - remote()->transact(IS_CRYPTO_SUPPORTED, data, &reply); + status_t status = remote()->transact(IS_CRYPTO_SUPPORTED, data, &reply); + if (status != OK) { + ALOGE("isCryptoSchemeSupported: binder call failed: %d", status); + return false; + } return reply.readInt32() != 0; } @@ -87,7 +94,10 @@ struct BpDrm : public BpInterface<IDrm> { data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); data.write(uuid, 16); - remote()->transact(CREATE_PLUGIN, data, &reply); + status_t status = remote()->transact(CREATE_PLUGIN, data, &reply); + if (status != OK) { + return status; + } return reply.readInt32(); } @@ -95,7 +105,10 @@ struct BpDrm : public BpInterface<IDrm> { virtual status_t destroyPlugin() { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - remote()->transact(DESTROY_PLUGIN, data, &reply); + status_t status = remote()->transact(DESTROY_PLUGIN, data, &reply); + if (status != OK) { + return status; + } return reply.readInt32(); } @@ -104,7 +117,10 @@ struct BpDrm : public BpInterface<IDrm> { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - remote()->transact(OPEN_SESSION, data, &reply); + status_t status = remote()->transact(OPEN_SESSION, data, &reply); + if (status != OK) { + return status; + } readVector(reply, sessionId); return reply.readInt32(); @@ -115,7 +131,10 @@ struct BpDrm : public BpInterface<IDrm> { data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); writeVector(data, sessionId); - remote()->transact(CLOSE_SESSION, data, &reply); + status_t status = remote()->transact(CLOSE_SESSION, data, &reply); + if (status != OK) { + return status; + } return reply.readInt32(); } @@ -125,7 +144,8 @@ struct BpDrm : public BpInterface<IDrm> { Vector<uint8_t> const &initData, String8 const &mimeType, DrmPlugin::KeyType keyType, KeyedVector<String8, String8> const &optionalParameters, - Vector<uint8_t> &request, String8 &defaultUrl) { + Vector<uint8_t> &request, String8 &defaultUrl, + DrmPlugin::KeyRequestType *keyRequestType) { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); @@ -139,10 +159,15 @@ struct BpDrm : public BpInterface<IDrm> { data.writeString8(optionalParameters.keyAt(i)); data.writeString8(optionalParameters.valueAt(i)); } - remote()->transact(GET_KEY_REQUEST, data, &reply); + + status_t status = remote()->transact(GET_KEY_REQUEST, data, &reply); + if (status != OK) { + return status; + } readVector(reply, request); defaultUrl = reply.readString8(); + *keyRequestType = static_cast<DrmPlugin::KeyRequestType>(reply.readInt32()); return reply.readInt32(); } @@ -154,7 +179,12 @@ struct BpDrm : public BpInterface<IDrm> { data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); writeVector(data, sessionId); writeVector(data, response); - remote()->transact(PROVIDE_KEY_RESPONSE, data, &reply); + + status_t status = remote()->transact(PROVIDE_KEY_RESPONSE, data, &reply); + if (status != OK) { + return status; + } + readVector(reply, keySetId); return reply.readInt32(); @@ -165,7 +195,10 @@ struct BpDrm : public BpInterface<IDrm> { data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); writeVector(data, keySetId); - remote()->transact(REMOVE_KEYS, data, &reply); + status_t status = remote()->transact(REMOVE_KEYS, data, &reply); + if (status != OK) { + return status; + } return reply.readInt32(); } @@ -177,7 +210,10 @@ struct BpDrm : public BpInterface<IDrm> { writeVector(data, sessionId); writeVector(data, keySetId); - remote()->transact(RESTORE_KEYS, data, &reply); + status_t status = remote()->transact(RESTORE_KEYS, data, &reply); + if (status != OK) { + return status; + } return reply.readInt32(); } @@ -188,7 +224,10 @@ struct BpDrm : public BpInterface<IDrm> { data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); writeVector(data, sessionId); - remote()->transact(QUERY_KEY_STATUS, data, &reply); + status_t status = remote()->transact(QUERY_KEY_STATUS, data, &reply); + if (status != OK) { + return status; + } infoMap.clear(); size_t count = reply.readInt32(); @@ -209,7 +248,10 @@ struct BpDrm : public BpInterface<IDrm> { data.writeString8(certType); data.writeString8(certAuthority); - remote()->transact(GET_PROVISION_REQUEST, data, &reply); + status_t status = remote()->transact(GET_PROVISION_REQUEST, data, &reply); + if (status != OK) { + return status; + } readVector(reply, request); defaultUrl = reply.readString8(); @@ -224,7 +266,10 @@ struct BpDrm : public BpInterface<IDrm> { data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); writeVector(data, response); - remote()->transact(PROVIDE_PROVISION_RESPONSE, data, &reply); + status_t status = remote()->transact(PROVIDE_PROVISION_RESPONSE, data, &reply); + if (status != OK) { + return status; + } readVector(reply, certificate); readVector(reply, wrappedKey); @@ -236,7 +281,10 @@ struct BpDrm : public BpInterface<IDrm> { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - remote()->transact(UNPROVISION_DEVICE, data, &reply); + status_t status = remote()->transact(UNPROVISION_DEVICE, data, &reply); + if (status != OK) { + return status; + } return reply.readInt32(); } @@ -245,7 +293,10 @@ struct BpDrm : public BpInterface<IDrm> { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - remote()->transact(GET_SECURE_STOPS, data, &reply); + status_t status = remote()->transact(GET_SECURE_STOPS, data, &reply); + if (status != OK) { + return status; + } secureStops.clear(); uint32_t count = reply.readInt32(); @@ -262,7 +313,10 @@ struct BpDrm : public BpInterface<IDrm> { data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); writeVector(data, ssid); - remote()->transact(GET_SECURE_STOP, data, &reply); + status_t status = remote()->transact(GET_SECURE_STOP, data, &reply); + if (status != OK) { + return status; + } readVector(reply, secureStop); return reply.readInt32(); @@ -273,7 +327,10 @@ struct BpDrm : public BpInterface<IDrm> { data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); writeVector(data, ssRelease); - remote()->transact(RELEASE_SECURE_STOPS, data, &reply); + status_t status = remote()->transact(RELEASE_SECURE_STOPS, data, &reply); + if (status != OK) { + return status; + } return reply.readInt32(); } @@ -282,7 +339,10 @@ struct BpDrm : public BpInterface<IDrm> { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - remote()->transact(RELEASE_ALL_SECURE_STOPS, data, &reply); + status_t status = remote()->transact(RELEASE_ALL_SECURE_STOPS, data, &reply); + if (status != OK) { + return status; + } return reply.readInt32(); } @@ -292,7 +352,10 @@ struct BpDrm : public BpInterface<IDrm> { data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); data.writeString8(name); - remote()->transact(GET_PROPERTY_STRING, data, &reply); + status_t status = remote()->transact(GET_PROPERTY_STRING, data, &reply); + if (status != OK) { + return status; + } value = reply.readString8(); return reply.readInt32(); @@ -303,7 +366,10 @@ struct BpDrm : public BpInterface<IDrm> { data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); data.writeString8(name); - remote()->transact(GET_PROPERTY_BYTE_ARRAY, data, &reply); + status_t status = remote()->transact(GET_PROPERTY_BYTE_ARRAY, data, &reply); + if (status != OK) { + return status; + } readVector(reply, value); return reply.readInt32(); @@ -315,7 +381,10 @@ struct BpDrm : public BpInterface<IDrm> { data.writeString8(name); data.writeString8(value); - remote()->transact(SET_PROPERTY_STRING, data, &reply); + status_t status = remote()->transact(SET_PROPERTY_STRING, data, &reply); + if (status != OK) { + return status; + } return reply.readInt32(); } @@ -327,7 +396,10 @@ struct BpDrm : public BpInterface<IDrm> { data.writeString8(name); writeVector(data, value); - remote()->transact(SET_PROPERTY_BYTE_ARRAY, data, &reply); + status_t status = remote()->transact(SET_PROPERTY_BYTE_ARRAY, data, &reply); + if (status != OK) { + return status; + } return reply.readInt32(); } @@ -340,7 +412,10 @@ struct BpDrm : public BpInterface<IDrm> { writeVector(data, sessionId); data.writeString8(algorithm); - remote()->transact(SET_CIPHER_ALGORITHM, data, &reply); + status_t status = remote()->transact(SET_CIPHER_ALGORITHM, data, &reply); + if (status != OK) { + return status; + } return reply.readInt32(); } @@ -351,7 +426,10 @@ struct BpDrm : public BpInterface<IDrm> { writeVector(data, sessionId); data.writeString8(algorithm); - remote()->transact(SET_MAC_ALGORITHM, data, &reply); + status_t status = remote()->transact(SET_MAC_ALGORITHM, data, &reply); + if (status != OK) { + return status; + } return reply.readInt32(); } @@ -368,7 +446,10 @@ struct BpDrm : public BpInterface<IDrm> { writeVector(data, input); writeVector(data, iv); - remote()->transact(ENCRYPT, data, &reply); + status_t status = remote()->transact(ENCRYPT, data, &reply); + if (status != OK) { + return status; + } readVector(reply, output); return reply.readInt32(); @@ -387,7 +468,10 @@ struct BpDrm : public BpInterface<IDrm> { writeVector(data, input); writeVector(data, iv); - remote()->transact(DECRYPT, data, &reply); + status_t status = remote()->transact(DECRYPT, data, &reply); + if (status != OK) { + return status; + } readVector(reply, output); return reply.readInt32(); @@ -404,7 +488,10 @@ struct BpDrm : public BpInterface<IDrm> { writeVector(data, keyId); writeVector(data, message); - remote()->transact(SIGN, data, &reply); + status_t status = remote()->transact(SIGN, data, &reply); + if (status != OK) { + return status; + } readVector(reply, signature); return reply.readInt32(); @@ -423,7 +510,10 @@ struct BpDrm : public BpInterface<IDrm> { writeVector(data, message); writeVector(data, signature); - remote()->transact(VERIFY, data, &reply); + status_t status = remote()->transact(VERIFY, data, &reply); + if (status != OK) { + return status; + } match = (bool)reply.readInt32(); return reply.readInt32(); } @@ -441,7 +531,10 @@ struct BpDrm : public BpInterface<IDrm> { writeVector(data, message); writeVector(data, wrappedKey); - remote()->transact(SIGN_RSA, data, &reply); + status_t status = remote()->transact(SIGN_RSA, data, &reply); + if (status != OK) { + return status; + } readVector(reply, signature); return reply.readInt32(); @@ -451,7 +544,10 @@ struct BpDrm : public BpInterface<IDrm> { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); data.writeStrongBinder(IInterface::asBinder(listener)); - remote()->transact(SET_LISTENER, data, &reply); + status_t status = remote()->transact(SET_LISTENER, data, &reply); + if (status != OK) { + return status; + } return reply.readInt32(); } @@ -562,13 +658,15 @@ status_t BnDrm::onTransact( Vector<uint8_t> request; String8 defaultUrl; + DrmPlugin::KeyRequestType keyRequestType; + + status_t result = getKeyRequest(sessionId, initData, mimeType, + keyType, optionalParameters, request, defaultUrl, + &keyRequestType); - status_t result = getKeyRequest(sessionId, initData, - mimeType, keyType, - optionalParameters, - request, defaultUrl); writeVector(reply, request); reply->writeString8(defaultUrl); + reply->writeInt32(static_cast<int32_t>(keyRequestType)); reply->writeInt32(result); return OK; } diff --git a/media/libmedia/IDrmClient.cpp b/media/libmedia/IDrmClient.cpp index f50715e..490c6ed 100644 --- a/media/libmedia/IDrmClient.cpp +++ b/media/libmedia/IDrmClient.cpp @@ -78,4 +78,4 @@ status_t BnDrmClient::onTransact( } } -}; // namespace android +} // namespace android diff --git a/media/libmedia/IEffect.cpp b/media/libmedia/IEffect.cpp index 2f6a91e..faf5795 100644 --- a/media/libmedia/IEffect.cpp +++ b/media/libmedia/IEffect.cpp @@ -214,4 +214,4 @@ status_t BnEffect::onTransact( // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/media/libmedia/IEffectClient.cpp b/media/libmedia/IEffectClient.cpp index aef4371..1322e72 100644 --- a/media/libmedia/IEffectClient.cpp +++ b/media/libmedia/IEffectClient.cpp @@ -141,4 +141,4 @@ status_t BnEffectClient::onTransact( // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/media/libmedia/IHDCP.cpp b/media/libmedia/IHDCP.cpp index 79944ee..f3a8902 100644 --- a/media/libmedia/IHDCP.cpp +++ b/media/libmedia/IHDCP.cpp @@ -284,11 +284,17 @@ status_t BnHDCP::onTransact( size_t offset = data.readInt32(); size_t size = data.readInt32(); uint32_t streamCTR = data.readInt32(); - void *outData = malloc(size); + void *outData = NULL; uint64_t inputCTR; - status_t err = encryptNative(graphicBuffer, offset, size, - streamCTR, &inputCTR, outData); + status_t err = ERROR_OUT_OF_RANGE; + + outData = malloc(size); + + if (outData != NULL) { + err = encryptNative(graphicBuffer, offset, size, + streamCTR, &inputCTR, outData); + } reply->writeInt32(err); diff --git a/media/libmedia/IMediaCodecList.cpp b/media/libmedia/IMediaCodecList.cpp index bf7c5ca..e2df104 100644 --- a/media/libmedia/IMediaCodecList.cpp +++ b/media/libmedia/IMediaCodecList.cpp @@ -30,6 +30,7 @@ enum { CREATE = IBinder::FIRST_CALL_TRANSACTION, COUNT_CODECS, GET_CODEC_INFO, + GET_GLOBAL_SETTINGS, FIND_CODEC_BY_TYPE, FIND_CODEC_BY_NAME, }; @@ -64,6 +65,19 @@ public: } } + virtual const sp<AMessage> getGlobalSettings() const + { + Parcel data, reply; + data.writeInterfaceToken(IMediaCodecList::getInterfaceDescriptor()); + remote()->transact(GET_GLOBAL_SETTINGS, data, &reply); + status_t err = reply.readInt32(); + if (err == OK) { + return AMessage::FromParcel(reply); + } else { + return NULL; + } + } + virtual ssize_t findCodecByType( const char *type, bool encoder, size_t startIndex = 0) const { @@ -125,6 +139,20 @@ status_t BnMediaCodecList::onTransact( } break; + case GET_GLOBAL_SETTINGS: + { + CHECK_INTERFACE(IMediaCodecList, data, reply); + const sp<AMessage> info = getGlobalSettings(); + if (info != NULL) { + reply->writeInt32(OK); + info->writeToParcel(reply); + } else { + reply->writeInt32(-ERANGE); + } + return NO_ERROR; + } + break; + case FIND_CODEC_BY_TYPE: { CHECK_INTERFACE(IMediaCodecList, data, reply); @@ -160,4 +188,4 @@ status_t BnMediaCodecList::onTransact( // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/media/libmedia/IMediaDeathNotifier.cpp b/media/libmedia/IMediaDeathNotifier.cpp index 38e9ca0..d4360ea 100644 --- a/media/libmedia/IMediaDeathNotifier.cpp +++ b/media/libmedia/IMediaDeathNotifier.cpp @@ -108,4 +108,4 @@ IMediaDeathNotifier::DeathNotifier::~DeathNotifier() } } -}; // namespace android +} // namespace android diff --git a/media/libmedia/IMediaHTTPConnection.cpp b/media/libmedia/IMediaHTTPConnection.cpp index a5a3714..0dda0be 100644 --- a/media/libmedia/IMediaHTTPConnection.cpp +++ b/media/libmedia/IMediaHTTPConnection.cpp @@ -70,7 +70,7 @@ struct BpMediaHTTPConnection : public BpInterface<IMediaHTTPConnection> { int32_t exceptionCode = reply.readExceptionCode(); if (exceptionCode) { - return UNKNOWN_ERROR; + return false; } sp<IBinder> binder = reply.readStrongBinder(); @@ -107,7 +107,14 @@ struct BpMediaHTTPConnection : public BpInterface<IMediaHTTPConnection> { return UNKNOWN_ERROR; } - size_t len = reply.readInt32(); + int32_t lenOrErrorCode = reply.readInt32(); + + // Negative values are error codes + if (lenOrErrorCode < 0) { + return lenOrErrorCode; + } + + size_t len = lenOrErrorCode; if (len > size) { ALOGE("requested %zu, got %zu", size, len); @@ -186,5 +193,4 @@ private: IMPLEMENT_META_INTERFACE( MediaHTTPConnection, "android.media.IMediaHTTPConnection"); -} // namespace android - +} // namespace android diff --git a/media/libmedia/IMediaHTTPService.cpp b/media/libmedia/IMediaHTTPService.cpp index 1260582..0c16a2b 100644 --- a/media/libmedia/IMediaHTTPService.cpp +++ b/media/libmedia/IMediaHTTPService.cpp @@ -44,6 +44,7 @@ struct BpMediaHTTPService : public BpInterface<IMediaHTTPService> { status_t err = reply.readInt32(); if (err != OK) { + ALOGE("Unable to make HTTP connection (err = %d)", err); return NULL; } @@ -54,5 +55,4 @@ struct BpMediaHTTPService : public BpInterface<IMediaHTTPService> { IMPLEMENT_META_INTERFACE( MediaHTTPService, "android.media.IMediaHTTPService"); -} // namespace android - +} // namespace android diff --git a/media/libmedia/IMediaLogService.cpp b/media/libmedia/IMediaLogService.cpp index a4af7b7..1536337 100644 --- a/media/libmedia/IMediaLogService.cpp +++ b/media/libmedia/IMediaLogService.cpp @@ -45,7 +45,7 @@ public: data.writeStrongBinder(IInterface::asBinder(shared)); data.writeInt64((int64_t) size); data.writeCString(name); - status_t status = remote()->transact(REGISTER_WRITER, data, &reply); + status_t status __unused = remote()->transact(REGISTER_WRITER, data, &reply); // FIXME ignores status } @@ -53,7 +53,7 @@ public: Parcel data, reply; data.writeInterfaceToken(IMediaLogService::getInterfaceDescriptor()); data.writeStrongBinder(IInterface::asBinder(shared)); - status_t status = remote()->transact(UNREGISTER_WRITER, data, &reply); + status_t status __unused = remote()->transact(UNREGISTER_WRITER, data, &reply); // FIXME ignores status } @@ -91,4 +91,4 @@ status_t BnMediaLogService::onTransact( // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp index aa2665a..9765f0d 100644 --- a/media/libmedia/IMediaMetadataRetriever.cpp +++ b/media/libmedia/IMediaMetadataRetriever.cpp @@ -20,6 +20,7 @@ #include <sys/types.h> #include <binder/Parcel.h> +#include <media/IDataSource.h> #include <media/IMediaHTTPService.h> #include <media/IMediaMetadataRetriever.h> #include <utils/String8.h> @@ -65,6 +66,7 @@ enum { DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, SET_DATA_SOURCE_URL, SET_DATA_SOURCE_FD, + SET_DATA_SOURCE_CALLBACK, GET_FRAME_AT_TIME, EXTRACT_ALBUM_ART, EXTRACT_METADATA, @@ -125,6 +127,15 @@ public: return reply.readInt32(); } + status_t setDataSource(const sp<IDataSource>& source) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + data.writeStrongBinder(IInterface::asBinder(source)); + remote()->transact(SET_DATA_SOURCE_CALLBACK, data, &reply); + return reply.readInt32(); + } + sp<IMemory> getFrameAtTime(int64_t timeUs, int option) { ALOGV("getTimeAtTime: time(%" PRId64 " us) and option(%d)", timeUs, option); @@ -235,6 +246,13 @@ status_t BnMediaMetadataRetriever::onTransact( reply->writeInt32(setDataSource(fd, offset, length)); return NO_ERROR; } break; + case SET_DATA_SOURCE_CALLBACK: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + sp<IDataSource> source = + interface_cast<IDataSource>(data.readStrongBinder()); + reply->writeInt32(setDataSource(source)); + return NO_ERROR; + } break; case GET_FRAME_AT_TIME: { CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); int64_t timeUs = data.readInt64(); @@ -297,4 +315,4 @@ status_t BnMediaMetadataRetriever::onTransact( // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index c1b9025..942aec3 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -21,6 +21,10 @@ #include <binder/Parcel.h> +#include <media/AudioResamplerPublic.h> +#include <media/AVSyncSettings.h> + +#include <media/IDataSource.h> #include <media/IMediaHTTPService.h> #include <media/IMediaPlayer.h> #include <media/IStreamSource.h> @@ -35,10 +39,15 @@ enum { SET_DATA_SOURCE_URL, SET_DATA_SOURCE_FD, SET_DATA_SOURCE_STREAM, + SET_DATA_SOURCE_CALLBACK, PREPARE_ASYNC, START, STOP, IS_PLAYING, + SET_PLAYBACK_SETTINGS, + GET_PLAYBACK_SETTINGS, + SET_SYNC_SETTINGS, + GET_SYNC_SETTINGS, PAUSE, SEEK_TO, GET_CURRENT_POSITION, @@ -120,6 +129,14 @@ public: return reply.readInt32(); } + status_t setDataSource(const sp<IDataSource> &source) { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + data.writeStrongBinder(IInterface::asBinder(source)); + remote()->transact(SET_DATA_SOURCE_CALLBACK, data, &reply); + return reply.readInt32(); + } + // pass the buffered IGraphicBufferProducer to the media player service status_t setVideoSurfaceTexture(const sp<IGraphicBufferProducer>& bufferProducer) { @@ -164,6 +181,63 @@ public: return reply.readInt32(); } + status_t setPlaybackSettings(const AudioPlaybackRate& rate) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + data.writeFloat(rate.mSpeed); + data.writeFloat(rate.mPitch); + data.writeInt32((int32_t)rate.mFallbackMode); + data.writeInt32((int32_t)rate.mStretchMode); + remote()->transact(SET_PLAYBACK_SETTINGS, data, &reply); + return reply.readInt32(); + } + + status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(GET_PLAYBACK_SETTINGS, data, &reply); + status_t err = reply.readInt32(); + if (err == OK) { + *rate = AUDIO_PLAYBACK_RATE_DEFAULT; + rate->mSpeed = reply.readFloat(); + rate->mPitch = reply.readFloat(); + rate->mFallbackMode = (AudioTimestretchFallbackMode)reply.readInt32(); + rate->mStretchMode = (AudioTimestretchStretchMode)reply.readInt32(); + } + return err; + } + + status_t setSyncSettings(const AVSyncSettings& sync, float videoFpsHint) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + data.writeInt32((int32_t)sync.mSource); + data.writeInt32((int32_t)sync.mAudioAdjustMode); + data.writeFloat(sync.mTolerance); + data.writeFloat(videoFpsHint); + remote()->transact(SET_SYNC_SETTINGS, data, &reply); + return reply.readInt32(); + } + + status_t getSyncSettings(AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(GET_SYNC_SETTINGS, data, &reply); + status_t err = reply.readInt32(); + if (err == OK) { + AVSyncSettings settings; + settings.mSource = (AVSyncSource)reply.readInt32(); + settings.mAudioAdjustMode = (AVSyncAudioAdjustMode)reply.readInt32(); + settings.mTolerance = reply.readFloat(); + *sync = settings; + *videoFps = reply.readFloat(); + } + return err; + } + status_t pause() { Parcel data, reply; @@ -396,6 +470,13 @@ status_t BnMediaPlayer::onTransact( reply->writeInt32(setDataSource(source)); return NO_ERROR; } + case SET_DATA_SOURCE_CALLBACK: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + sp<IDataSource> source = + interface_cast<IDataSource>(data.readStrongBinder()); + reply->writeInt32(setDataSource(source)); + return NO_ERROR; + } case SET_VIDEO_SURFACETEXTURE: { CHECK_INTERFACE(IMediaPlayer, data, reply); sp<IGraphicBufferProducer> bufferProducer = @@ -426,6 +507,53 @@ status_t BnMediaPlayer::onTransact( reply->writeInt32(ret); return NO_ERROR; } break; + case SET_PLAYBACK_SETTINGS: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + AudioPlaybackRate rate = AUDIO_PLAYBACK_RATE_DEFAULT; + rate.mSpeed = data.readFloat(); + rate.mPitch = data.readFloat(); + rate.mFallbackMode = (AudioTimestretchFallbackMode)data.readInt32(); + rate.mStretchMode = (AudioTimestretchStretchMode)data.readInt32(); + reply->writeInt32(setPlaybackSettings(rate)); + return NO_ERROR; + } break; + case GET_PLAYBACK_SETTINGS: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + AudioPlaybackRate rate = AUDIO_PLAYBACK_RATE_DEFAULT; + status_t err = getPlaybackSettings(&rate); + reply->writeInt32(err); + if (err == OK) { + reply->writeFloat(rate.mSpeed); + reply->writeFloat(rate.mPitch); + reply->writeInt32((int32_t)rate.mFallbackMode); + reply->writeInt32((int32_t)rate.mStretchMode); + } + return NO_ERROR; + } break; + case SET_SYNC_SETTINGS: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + AVSyncSettings sync; + sync.mSource = (AVSyncSource)data.readInt32(); + sync.mAudioAdjustMode = (AVSyncAudioAdjustMode)data.readInt32(); + sync.mTolerance = data.readFloat(); + float videoFpsHint = data.readFloat(); + reply->writeInt32(setSyncSettings(sync, videoFpsHint)); + return NO_ERROR; + } break; + case GET_SYNC_SETTINGS: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + AVSyncSettings sync; + float videoFps; + status_t err = getSyncSettings(&sync, &videoFps); + reply->writeInt32(err); + if (err == OK) { + reply->writeInt32((int32_t)sync.mSource); + reply->writeInt32((int32_t)sync.mAudioAdjustMode); + reply->writeFloat(sync.mTolerance); + reply->writeFloat(videoFps); + } + return NO_ERROR; + } break; case PAUSE: { CHECK_INTERFACE(IMediaPlayer, data, reply); reply->writeInt32(pause()); @@ -561,4 +689,4 @@ status_t BnMediaPlayer::onTransact( // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/media/libmedia/IMediaPlayerClient.cpp b/media/libmedia/IMediaPlayerClient.cpp index a670c96..d608386 100644 --- a/media/libmedia/IMediaPlayerClient.cpp +++ b/media/libmedia/IMediaPlayerClient.cpp @@ -75,4 +75,4 @@ status_t BnMediaPlayerClient::onTransact( } } -}; // namespace android +} // namespace android diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index feea267..05f8670 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -78,10 +78,11 @@ public: return interface_cast<IMediaPlayer>(reply.readStrongBinder()); } - virtual sp<IMediaRecorder> createMediaRecorder() + virtual sp<IMediaRecorder> createMediaRecorder(const String16 &opPackageName) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeString16(opPackageName); remote()->transact(CREATE_MEDIA_RECORDER, data, &reply); return interface_cast<IMediaRecorder>(reply.readStrongBinder()); } @@ -128,11 +129,12 @@ public: return remote()->transact(PULL_BATTERY_DATA, data, reply); } - virtual sp<IRemoteDisplay> listenForRemoteDisplay(const sp<IRemoteDisplayClient>& client, - const String8& iface) + virtual sp<IRemoteDisplay> listenForRemoteDisplay(const String16 &opPackageName, + const sp<IRemoteDisplayClient>& client, const String8& iface) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeString16(opPackageName); data.writeStrongBinder(IInterface::asBinder(client)); data.writeString8(iface); remote()->transact(LISTEN_FOR_REMOTE_DISPLAY, data, &reply); @@ -166,7 +168,8 @@ status_t BnMediaPlayerService::onTransact( } break; case CREATE_MEDIA_RECORDER: { CHECK_INTERFACE(IMediaPlayerService, data, reply); - sp<IMediaRecorder> recorder = createMediaRecorder(); + const String16 opPackageName = data.readString16(); + sp<IMediaRecorder> recorder = createMediaRecorder(opPackageName); reply->writeStrongBinder(IInterface::asBinder(recorder)); return NO_ERROR; } break; @@ -214,10 +217,11 @@ status_t BnMediaPlayerService::onTransact( } break; case LISTEN_FOR_REMOTE_DISPLAY: { CHECK_INTERFACE(IMediaPlayerService, data, reply); + const String16 opPackageName = data.readString16(); sp<IRemoteDisplayClient> client( interface_cast<IRemoteDisplayClient>(data.readStrongBinder())); String8 iface(data.readString8()); - sp<IRemoteDisplay> display(listenForRemoteDisplay(client, iface)); + sp<IRemoteDisplay> display(listenForRemoteDisplay(opPackageName, client, iface)); reply->writeStrongBinder(IInterface::asBinder(display)); return NO_ERROR; } break; @@ -234,4 +238,4 @@ status_t BnMediaPlayerService::onTransact( // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp index a733b68..ee3b584 100644 --- a/media/libmedia/IMediaRecorder.cpp +++ b/media/libmedia/IMediaRecorder.cpp @@ -35,6 +35,7 @@ enum { RELEASE = IBinder::FIRST_CALL_TRANSACTION, INIT, CLOSE, + SET_INPUT_SURFACE, QUERY_SURFACE_MEDIASOURCE, RESET, STOP, @@ -46,7 +47,6 @@ enum { SET_OUTPUT_FORMAT, SET_VIDEO_ENCODER, SET_AUDIO_ENCODER, - SET_OUTPUT_FILE_PATH, SET_OUTPUT_FILE_FD, SET_VIDEO_SIZE, SET_VIDEO_FRAMERATE, @@ -76,6 +76,16 @@ public: return reply.readInt32(); } + status_t setInputSurface(const sp<IGraphicBufferConsumer>& surface) + { + ALOGV("setInputSurface(%p)", surface.get()); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeStrongBinder(IInterface::asBinder(surface)); + remote()->transact(SET_INPUT_SURFACE, data, &reply); + return reply.readInt32(); + } + sp<IGraphicBufferProducer> querySurfaceMediaSource() { ALOGV("Query SurfaceMediaSource"); @@ -158,16 +168,6 @@ public: return reply.readInt32(); } - status_t setOutputFile(const char* path) - { - ALOGV("setOutputFile(%s)", path); - Parcel data, reply; - data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); - data.writeCString(path); - remote()->transact(SET_OUTPUT_FILE_PATH, data, &reply); - return reply.readInt32(); - } - status_t setOutputFile(int fd, int64_t offset, int64_t length) { ALOGV("setOutputFile(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length); Parcel data, reply; @@ -300,7 +300,8 @@ IMPLEMENT_META_INTERFACE(MediaRecorder, "android.media.IMediaRecorder"); // ---------------------------------------------------------------------- status_t BnMediaRecorder::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) + uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { switch (code) { case RELEASE: { @@ -390,13 +391,6 @@ status_t BnMediaRecorder::onTransact( return NO_ERROR; } break; - case SET_OUTPUT_FILE_PATH: { - ALOGV("SET_OUTPUT_FILE_PATH"); - CHECK_INTERFACE(IMediaRecorder, data, reply); - const char* path = data.readCString(); - reply->writeInt32(setOutputFile(path)); - return NO_ERROR; - } break; case SET_OUTPUT_FILE_FD: { ALOGV("SET_OUTPUT_FILE_FD"); CHECK_INTERFACE(IMediaRecorder, data, reply); @@ -445,7 +439,8 @@ status_t BnMediaRecorder::onTransact( case SET_PREVIEW_SURFACE: { ALOGV("SET_PREVIEW_SURFACE"); CHECK_INTERFACE(IMediaRecorder, data, reply); - sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(data.readStrongBinder()); + sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>( + data.readStrongBinder()); reply->writeInt32(setPreviewSurface(surface)); return NO_ERROR; } break; @@ -458,6 +453,14 @@ status_t BnMediaRecorder::onTransact( reply->writeInt32(setCamera(camera, proxy)); return NO_ERROR; } break; + case SET_INPUT_SURFACE: { + ALOGV("SET_INPUT_SURFACE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + sp<IGraphicBufferConsumer> surface = interface_cast<IGraphicBufferConsumer>( + data.readStrongBinder()); + reply->writeInt32(setInputSurface(surface)); + return NO_ERROR; + } break; case QUERY_SURFACE_MEDIASOURCE: { ALOGV("QUERY_SURFACE_MEDIASOURCE"); CHECK_INTERFACE(IMediaRecorder, data, reply); @@ -479,4 +482,4 @@ status_t BnMediaRecorder::onTransact( // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/media/libmedia/IMediaRecorderClient.cpp b/media/libmedia/IMediaRecorderClient.cpp index e7907e3..6795d23 100644 --- a/media/libmedia/IMediaRecorderClient.cpp +++ b/media/libmedia/IMediaRecorderClient.cpp @@ -67,4 +67,4 @@ status_t BnMediaRecorderClient::onTransact( } } -}; // namespace android +} // namespace android diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index f239bc7..5423c2a 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -41,6 +41,8 @@ enum { USE_BUFFER, USE_GRAPHIC_BUFFER, CREATE_INPUT_SURFACE, + CREATE_PERSISTENT_INPUT_SURFACE, + SET_INPUT_SURFACE, SIGNAL_END_OF_INPUT_STREAM, STORE_META_DATA_IN_BUFFERS, PREPARE_FOR_ADAPTIVE_PLAYBACK, @@ -243,12 +245,13 @@ public: virtual status_t useBuffer( node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms, - buffer_id *buffer) { + buffer_id *buffer, OMX_U32 allottedSize) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeInt32((int32_t)node); data.writeInt32(port_index); data.writeStrongBinder(IInterface::asBinder(params)); + data.writeInt32(allottedSize); remote()->transact(USE_BUFFER, data, &reply); status_t err = reply.readInt32(); @@ -303,7 +306,7 @@ public: virtual status_t createInputSurface( node_id node, OMX_U32 port_index, - sp<IGraphicBufferProducer> *bufferProducer) { + sp<IGraphicBufferProducer> *bufferProducer, MetadataBufferType *type) { Parcel data, reply; status_t err; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); @@ -315,6 +318,12 @@ public: return err; } + // read type even if createInputSurface failed + int negotiatedType = reply.readInt32(); + if (type != NULL) { + *type = (MetadataBufferType)negotiatedType; + } + err = reply.readInt32(); if (err != OK) { return err; @@ -326,6 +335,57 @@ public: return err; } + virtual status_t createPersistentInputSurface( + sp<IGraphicBufferProducer> *bufferProducer, + sp<IGraphicBufferConsumer> *bufferConsumer) { + Parcel data, reply; + status_t err; + data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); + err = remote()->transact(CREATE_PERSISTENT_INPUT_SURFACE, data, &reply); + if (err != OK) { + ALOGW("binder transaction failed: %d", err); + return err; + } + + err = reply.readInt32(); + if (err != OK) { + return err; + } + + *bufferProducer = IGraphicBufferProducer::asInterface( + reply.readStrongBinder()); + *bufferConsumer = IGraphicBufferConsumer::asInterface( + reply.readStrongBinder()); + + return err; + } + + virtual status_t setInputSurface( + node_id node, OMX_U32 port_index, + const sp<IGraphicBufferConsumer> &bufferConsumer, MetadataBufferType *type) { + Parcel data, reply; + data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); + status_t err; + data.writeInt32((int32_t)node); + data.writeInt32(port_index); + data.writeStrongBinder(IInterface::asBinder(bufferConsumer)); + + err = remote()->transact(SET_INPUT_SURFACE, data, &reply); + + if (err != OK) { + ALOGW("binder transaction failed: %d", err); + return err; + } + + // read type even if setInputSurface failed + int negotiatedType = reply.readInt32(); + if (type != NULL) { + *type = (MetadataBufferType)negotiatedType; + } + + return reply.readInt32(); + } + virtual status_t signalEndOfInputStream(node_id node) { Parcel data, reply; status_t err; @@ -341,7 +401,7 @@ public: } virtual status_t storeMetaDataInBuffers( - node_id node, OMX_U32 port_index, OMX_BOOL enable) { + node_id node, OMX_U32 port_index, OMX_BOOL enable, MetadataBufferType *type) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeInt32((int32_t)node); @@ -349,8 +409,13 @@ public: data.writeInt32((uint32_t)enable); remote()->transact(STORE_META_DATA_IN_BUFFERS, data, &reply); - status_t err = reply.readInt32(); - return err; + // read type even storeMetaDataInBuffers failed + int negotiatedType = reply.readInt32(); + if (type != NULL) { + *type = (MetadataBufferType)negotiatedType; + } + + return reply.readInt32(); } virtual status_t prepareForAdaptivePlayback( @@ -413,12 +478,13 @@ public: virtual status_t allocateBufferWithBackup( node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms, - buffer_id *buffer) { + buffer_id *buffer, OMX_U32 allottedSize) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeInt32((int32_t)node); data.writeInt32(port_index); data.writeStrongBinder(IInterface::asBinder(params)); + data.writeInt32(allottedSize); remote()->transact(ALLOC_BUFFER_WITH_BACKUP, data, &reply); status_t err = reply.readInt32(); @@ -445,11 +511,15 @@ public: return reply.readInt32(); } - virtual status_t fillBuffer(node_id node, buffer_id buffer) { + virtual status_t fillBuffer(node_id node, buffer_id buffer, int fenceFd) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeInt32((int32_t)node); data.writeInt32((int32_t)buffer); + data.writeInt32(fenceFd >= 0); + if (fenceFd >= 0) { + data.writeFileDescriptor(fenceFd, true /* takeOwnership */); + } remote()->transact(FILL_BUFFER, data, &reply); return reply.readInt32(); @@ -459,7 +529,7 @@ public: node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, - OMX_U32 flags, OMX_TICKS timestamp) { + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeInt32((int32_t)node); @@ -468,6 +538,10 @@ public: data.writeInt32(range_length); data.writeInt32(flags); data.writeInt64(timestamp); + data.writeInt32(fenceFd >= 0); + if (fenceFd >= 0) { + data.writeFileDescriptor(fenceFd, true /* takeOwnership */); + } remote()->transact(EMPTY_BUFFER, data, &reply); return reply.readInt32(); @@ -711,9 +785,10 @@ status_t BnOMX::onTransact( OMX_U32 port_index = data.readInt32(); sp<IMemory> params = interface_cast<IMemory>(data.readStrongBinder()); + OMX_U32 allottedSize = data.readInt32(); buffer_id buffer; - status_t err = useBuffer(node, port_index, params, &buffer); + status_t err = useBuffer(node, port_index, params, &buffer, allottedSize); reply->writeInt32(err); if (err == OK) { @@ -769,18 +844,56 @@ status_t BnOMX::onTransact( OMX_U32 port_index = data.readInt32(); sp<IGraphicBufferProducer> bufferProducer; - status_t err = createInputSurface(node, port_index, - &bufferProducer); + MetadataBufferType type; + status_t err = createInputSurface(node, port_index, &bufferProducer, &type); + + reply->writeInt32(type); + reply->writeInt32(err); + + if (err == OK) { + reply->writeStrongBinder(IInterface::asBinder(bufferProducer)); + } + + return NO_ERROR; + } + + case CREATE_PERSISTENT_INPUT_SURFACE: + { + CHECK_OMX_INTERFACE(IOMX, data, reply); + + sp<IGraphicBufferProducer> bufferProducer; + sp<IGraphicBufferConsumer> bufferConsumer; + status_t err = createPersistentInputSurface( + &bufferProducer, &bufferConsumer); reply->writeInt32(err); if (err == OK) { reply->writeStrongBinder(IInterface::asBinder(bufferProducer)); + reply->writeStrongBinder(IInterface::asBinder(bufferConsumer)); } return NO_ERROR; } + case SET_INPUT_SURFACE: + { + CHECK_OMX_INTERFACE(IOMX, data, reply); + + node_id node = (node_id)data.readInt32(); + OMX_U32 port_index = data.readInt32(); + + sp<IGraphicBufferConsumer> bufferConsumer = + interface_cast<IGraphicBufferConsumer>(data.readStrongBinder()); + + MetadataBufferType type; + status_t err = setInputSurface(node, port_index, bufferConsumer, &type); + + reply->writeInt32(type); + reply->writeInt32(err); + return NO_ERROR; + } + case SIGNAL_END_OF_INPUT_STREAM: { CHECK_OMX_INTERFACE(IOMX, data, reply); @@ -801,7 +914,9 @@ status_t BnOMX::onTransact( OMX_U32 port_index = data.readInt32(); OMX_BOOL enable = (OMX_BOOL)data.readInt32(); - status_t err = storeMetaDataInBuffers(node, port_index, enable); + MetadataBufferType type; + status_t err = storeMetaDataInBuffers(node, port_index, enable, &type); + reply->writeInt32(type); reply->writeInt32(err); return NO_ERROR; @@ -878,10 +993,11 @@ status_t BnOMX::onTransact( OMX_U32 port_index = data.readInt32(); sp<IMemory> params = interface_cast<IMemory>(data.readStrongBinder()); + OMX_U32 allottedSize = data.readInt32(); buffer_id buffer; status_t err = allocateBufferWithBackup( - node, port_index, params, &buffer); + node, port_index, params, &buffer, allottedSize); reply->writeInt32(err); @@ -910,7 +1026,9 @@ status_t BnOMX::onTransact( node_id node = (node_id)data.readInt32(); buffer_id buffer = (buffer_id)data.readInt32(); - reply->writeInt32(fillBuffer(node, buffer)); + bool haveFence = data.readInt32(); + int fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1; + reply->writeInt32(fillBuffer(node, buffer, fenceFd)); return NO_ERROR; } @@ -925,11 +1043,10 @@ status_t BnOMX::onTransact( OMX_U32 range_length = data.readInt32(); OMX_U32 flags = data.readInt32(); OMX_TICKS timestamp = data.readInt64(); - - reply->writeInt32( - emptyBuffer( - node, buffer, range_offset, range_length, - flags, timestamp)); + bool haveFence = data.readInt32(); + int fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1; + reply->writeInt32(emptyBuffer( + node, buffer, range_offset, range_length, flags, timestamp, fenceFd)); return NO_ERROR; } @@ -966,14 +1083,29 @@ public: : BpInterface<IOMXObserver>(impl) { } - virtual void onMessage(const omx_message &msg) { + virtual void onMessages(const std::list<omx_message> &messages) { Parcel data, reply; - data.writeInterfaceToken(IOMXObserver::getInterfaceDescriptor()); - data.write(&msg, sizeof(msg)); - - ALOGV("onMessage writing message %d, size %zu", msg.type, sizeof(msg)); - - remote()->transact(OBSERVER_ON_MSG, data, &reply, IBinder::FLAG_ONEWAY); + std::list<omx_message>::const_iterator it = messages.cbegin(); + bool first = true; + while (it != messages.cend()) { + const omx_message &msg = *it++; + if (first) { + data.writeInterfaceToken(IOMXObserver::getInterfaceDescriptor()); + data.writeInt32(msg.node); + first = false; + } + data.writeInt32(msg.fenceFd >= 0); + if (msg.fenceFd >= 0) { + data.writeFileDescriptor(msg.fenceFd, true /* takeOwnership */); + } + data.writeInt32(msg.type); + data.write(&msg.u, sizeof(msg.u)); + ALOGV("onMessage writing message %d, size %zu", msg.type, sizeof(msg)); + } + if (!first) { + data.writeInt32(-1); // mark end + remote()->transact(OBSERVER_ON_MSG, data, &reply, IBinder::FLAG_ONEWAY); + } } }; @@ -985,16 +1117,28 @@ status_t BnOMXObserver::onTransact( case OBSERVER_ON_MSG: { CHECK_OMX_INTERFACE(IOMXObserver, data, reply); + IOMX::node_id node = data.readInt32(); + std::list<omx_message> messages; + status_t err = FAILED_TRANSACTION; // must receive at least one message + do { + int haveFence = data.readInt32(); + if (haveFence < 0) { // we use -1 to mark end of messages + break; + } + omx_message msg; + msg.node = node; + msg.fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1; + msg.type = (typeof(msg.type))data.readInt32(); + err = data.read(&msg.u, sizeof(msg.u)); + ALOGV("onTransact reading message %d, size %zu", msg.type, sizeof(msg)); + messages.push_back(msg); + } while (err == OK); - omx_message msg; - data.read(&msg, sizeof(msg)); - - ALOGV("onTransact reading message %d, size %zu", msg.type, sizeof(msg)); - - // XXX Could use readInplace maybe? - onMessage(msg); + if (err == OK) { + onMessages(messages); + } - return NO_ERROR; + return err; } default: diff --git a/media/libmedia/IRemoteDisplay.cpp b/media/libmedia/IRemoteDisplay.cpp index 1e15434..869d11a 100644 --- a/media/libmedia/IRemoteDisplay.cpp +++ b/media/libmedia/IRemoteDisplay.cpp @@ -91,4 +91,4 @@ status_t BnRemoteDisplay::onTransact( } } -}; // namespace android +} // namespace android diff --git a/media/libmedia/IRemoteDisplayClient.cpp b/media/libmedia/IRemoteDisplayClient.cpp index 9d63bc9..bedeb6c 100644 --- a/media/libmedia/IRemoteDisplayClient.cpp +++ b/media/libmedia/IRemoteDisplayClient.cpp @@ -101,4 +101,4 @@ status_t BnRemoteDisplayClient::onTransact( } } -}; // namespace android +} // namespace android diff --git a/media/libmedia/IResourceManagerClient.cpp b/media/libmedia/IResourceManagerClient.cpp new file mode 100644 index 0000000..b3f56e8 --- /dev/null +++ b/media/libmedia/IResourceManagerClient.cpp @@ -0,0 +1,90 @@ +/* +** +** Copyright 2015, 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. +*/ + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +#include <media/IResourceManagerClient.h> + +namespace android { + +enum { + RECLAIM_RESOURCE = IBinder::FIRST_CALL_TRANSACTION, + GET_NAME, +}; + +class BpResourceManagerClient: public BpInterface<IResourceManagerClient> +{ +public: + BpResourceManagerClient(const sp<IBinder> &impl) + : BpInterface<IResourceManagerClient>(impl) + { + } + + virtual bool reclaimResource() { + Parcel data, reply; + data.writeInterfaceToken(IResourceManagerClient::getInterfaceDescriptor()); + + bool ret = false; + status_t status = remote()->transact(RECLAIM_RESOURCE, data, &reply); + if (status == NO_ERROR) { + ret = (bool)reply.readInt32(); + } + return ret; + } + + virtual String8 getName() { + Parcel data, reply; + data.writeInterfaceToken(IResourceManagerClient::getInterfaceDescriptor()); + + String8 ret; + status_t status = remote()->transact(GET_NAME, data, &reply); + if (status == NO_ERROR) { + ret = reply.readString8(); + } + return ret; + } + +}; + +IMPLEMENT_META_INTERFACE(ResourceManagerClient, "android.media.IResourceManagerClient"); + +// ---------------------------------------------------------------------- + +status_t BnResourceManagerClient::onTransact( + uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) +{ + switch (code) { + case RECLAIM_RESOURCE: { + CHECK_INTERFACE(IResourceManagerClient, data, reply); + bool ret = reclaimResource(); + reply->writeInt32(ret); + return NO_ERROR; + } break; + case GET_NAME: { + CHECK_INTERFACE(IResourceManagerClient, data, reply); + String8 ret = getName(); + reply->writeString8(ret); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android diff --git a/media/libmedia/IResourceManagerService.cpp b/media/libmedia/IResourceManagerService.cpp new file mode 100644 index 0000000..4598686 --- /dev/null +++ b/media/libmedia/IResourceManagerService.cpp @@ -0,0 +1,166 @@ +/* +** +** Copyright 2015, 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 "IResourceManagerService" +#include <utils/Log.h> + +#include "media/IResourceManagerService.h" + +#include <binder/Parcel.h> + +#include <stdint.h> +#include <sys/types.h> + +namespace android { + +enum { + CONFIG = IBinder::FIRST_CALL_TRANSACTION, + ADD_RESOURCE, + REMOVE_RESOURCE, + RECLAIM_RESOURCE, +}; + +template <typename T> +static void writeToParcel(Parcel *data, const Vector<T> &items) { + size_t size = items.size(); + // truncates size, but should be okay for this usecase + data->writeUint32(static_cast<uint32_t>(size)); + for (size_t i = 0; i < size; i++) { + items[i].writeToParcel(data); + } +} + +template <typename T> +static void readFromParcel(const Parcel &data, Vector<T> *items) { + size_t size = (size_t)data.readUint32(); + for (size_t i = 0; i < size && data.dataAvail() > 0; i++) { + T item; + item.readFromParcel(data); + items->add(item); + } +} + +class BpResourceManagerService : public BpInterface<IResourceManagerService> +{ +public: + BpResourceManagerService(const sp<IBinder> &impl) + : BpInterface<IResourceManagerService>(impl) + { + } + + virtual void config(const Vector<MediaResourcePolicy> &policies) { + Parcel data, reply; + data.writeInterfaceToken(IResourceManagerService::getInterfaceDescriptor()); + writeToParcel(&data, policies); + remote()->transact(CONFIG, data, &reply); + } + + virtual void addResource( + int pid, + int64_t clientId, + const sp<IResourceManagerClient> client, + const Vector<MediaResource> &resources) { + Parcel data, reply; + data.writeInterfaceToken(IResourceManagerService::getInterfaceDescriptor()); + data.writeInt32(pid); + data.writeInt64(clientId); + data.writeStrongBinder(IInterface::asBinder(client)); + writeToParcel(&data, resources); + + remote()->transact(ADD_RESOURCE, data, &reply); + } + + virtual void removeResource(int pid, int64_t clientId) { + Parcel data, reply; + data.writeInterfaceToken(IResourceManagerService::getInterfaceDescriptor()); + data.writeInt32(pid); + data.writeInt64(clientId); + + remote()->transact(REMOVE_RESOURCE, data, &reply); + } + + virtual bool reclaimResource(int callingPid, const Vector<MediaResource> &resources) { + Parcel data, reply; + data.writeInterfaceToken(IResourceManagerService::getInterfaceDescriptor()); + data.writeInt32(callingPid); + writeToParcel(&data, resources); + + bool ret = false; + status_t status = remote()->transact(RECLAIM_RESOURCE, data, &reply); + if (status == NO_ERROR) { + ret = (bool)reply.readInt32(); + } + return ret; + } +}; + +IMPLEMENT_META_INTERFACE(ResourceManagerService, "android.media.IResourceManagerService"); + +// ---------------------------------------------------------------------- + + +status_t BnResourceManagerService::onTransact( + uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) +{ + switch (code) { + case CONFIG: { + CHECK_INTERFACE(IResourceManagerService, data, reply); + Vector<MediaResourcePolicy> policies; + readFromParcel(data, &policies); + config(policies); + return NO_ERROR; + } break; + + case ADD_RESOURCE: { + CHECK_INTERFACE(IResourceManagerService, data, reply); + int pid = data.readInt32(); + int64_t clientId = data.readInt64(); + sp<IResourceManagerClient> client( + interface_cast<IResourceManagerClient>(data.readStrongBinder())); + Vector<MediaResource> resources; + readFromParcel(data, &resources); + addResource(pid, clientId, client, resources); + return NO_ERROR; + } break; + + case REMOVE_RESOURCE: { + CHECK_INTERFACE(IResourceManagerService, data, reply); + int pid = data.readInt32(); + int64_t clientId = data.readInt64(); + removeResource(pid, clientId); + return NO_ERROR; + } break; + + case RECLAIM_RESOURCE: { + CHECK_INTERFACE(IResourceManagerService, data, reply); + int callingPid = data.readInt32(); + Vector<MediaResource> resources; + readFromParcel(data, &resources); + bool ret = reclaimResource(callingPid, resources); + reply->writeInt32(ret); + return NO_ERROR; + } break; + + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/media/libmedia/IStreamSource.cpp b/media/libmedia/IStreamSource.cpp index d480aef..840e453 100644 --- a/media/libmedia/IStreamSource.cpp +++ b/media/libmedia/IStreamSource.cpp @@ -35,6 +35,9 @@ const char *const IStreamListener::kKeyDiscontinuityMask = "discontinuity-mask"; // static const char *const IStreamListener::kKeyMediaTimeUs = "media-time-us"; +// static +const char *const IStreamListener::kKeyRecentMediaTimeUs = "recent-media-time-us"; + enum { // IStreamSource SET_LISTENER = IBinder::FIRST_CALL_TRANSACTION, diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp index 721d8d7..34deb59 100644 --- a/media/libmedia/JetPlayer.cpp +++ b/media/libmedia/JetPlayer.cpp @@ -85,12 +85,18 @@ int JetPlayer::init() // create the output AudioTrack mAudioTrack = new AudioTrack(); - mAudioTrack->set(AUDIO_STREAM_MUSIC, //TODO parameterize this + status_t status = mAudioTrack->set(AUDIO_STREAM_MUSIC, //TODO parameterize this pLibConfig->sampleRate, AUDIO_FORMAT_PCM_16_BIT, audio_channel_out_mask_from_count(pLibConfig->numChannels), (size_t) mTrackBufferSize, AUDIO_OUTPUT_FLAG_NONE); + if (status != OK) { + ALOGE("JetPlayer::init(): Error initializing JET library; AudioTrack error %d", status); + mAudioTrack.clear(); + mState = EAS_STATE_ERROR; + return EAS_FAILURE; + } // create render and playback thread { @@ -408,7 +414,8 @@ int JetPlayer::queueSegment(int segmentNum, int libNum, int repeatCount, int tra ALOGV("JetPlayer::queueSegment segmentNum=%d, libNum=%d, repeatCount=%d, transpose=%d", segmentNum, libNum, repeatCount, transpose); Mutex::Autolock lock(mMutex); - return JET_QueueSegment(mEasData, segmentNum, libNum, repeatCount, transpose, muteFlags, userID); + return JET_QueueSegment(mEasData, segmentNum, libNum, repeatCount, transpose, muteFlags, + userID); } //------------------------------------------------------------------------------------------------- @@ -449,7 +456,8 @@ void JetPlayer::dump() void JetPlayer::dumpJetStatus(S_JET_STATUS* pJetStatus) { if (pJetStatus!=NULL) - ALOGV(">> current JET player status: userID=%d segmentRepeatCount=%d numQueuedSegments=%d paused=%d", + ALOGV(">> current JET player status: userID=%d segmentRepeatCount=%d numQueuedSegments=%d " + "paused=%d", pJetStatus->currentUserID, pJetStatus->segmentRepeatCount, pJetStatus->numQueuedSegments, pJetStatus->paused); else diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp index 7b4c4e2..8d3fa7b 100644 --- a/media/libmedia/MediaCodecInfo.cpp +++ b/media/libmedia/MediaCodecInfo.cpp @@ -206,6 +206,17 @@ status_t MediaCodecInfo::addMime(const char *mime) { return OK; } +status_t MediaCodecInfo::updateMime(const char *mime) { + ssize_t ix = getCapabilityIndex(mime); + if (ix < 0) { + ALOGE("updateMime mime not found %s", mime); + return -EINVAL; + } + + mCurrentCaps = mCaps.valueAt(ix); + return OK; +} + void MediaCodecInfo::removeMime(const char *mime) { ssize_t ix = getCapabilityIndex(mime); if (ix >= 0) { diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp index e2e6042..c5790fb 100644 --- a/media/libmedia/MediaProfiles.cpp +++ b/media/libmedia/MediaProfiles.cpp @@ -152,18 +152,9 @@ MediaProfiles::logAudioDecoderCap(const MediaProfiles::AudioDecoderCap& cap UNUS ALOGV("codec = %d", cap.mCodec); } -/*static*/ void -MediaProfiles::logVideoEditorCap(const MediaProfiles::VideoEditorCap& cap UNUSED) -{ - ALOGV("videoeditor cap:"); - ALOGV("mMaxInputFrameWidth = %d", cap.mMaxInputFrameWidth); - ALOGV("mMaxInputFrameHeight = %d", cap.mMaxInputFrameHeight); - ALOGV("mMaxOutputFrameWidth = %d", cap.mMaxOutputFrameWidth); - ALOGV("mMaxOutputFrameHeight = %d", cap.mMaxOutputFrameHeight); -} - /*static*/ int -MediaProfiles::findTagForName(const MediaProfiles::NameToTagMap *map, size_t nMappings, const char *name) +MediaProfiles::findTagForName(const MediaProfiles::NameToTagMap *map, size_t nMappings, + const char *name) { int tag = -1; for (size_t i = 0; i < nMappings; ++i) { @@ -295,9 +286,8 @@ MediaProfiles::createAudioEncoderCap(const char **atts) CHECK(codec != -1); MediaProfiles::AudioEncoderCap *cap = - new MediaProfiles::AudioEncoderCap(static_cast<audio_encoder>(codec), atoi(atts[5]), atoi(atts[7]), - atoi(atts[9]), atoi(atts[11]), atoi(atts[13]), - atoi(atts[15])); + new MediaProfiles::AudioEncoderCap(static_cast<audio_encoder>(codec), atoi(atts[5]), + atoi(atts[7]), atoi(atts[9]), atoi(atts[11]), atoi(atts[13]), atoi(atts[15])); logAudioEncoderCap(*cap); return cap; } @@ -330,7 +320,8 @@ MediaProfiles::createCamcorderProfile(int cameraId, const char **atts, Vector<in !strcmp("fileFormat", atts[2]) && !strcmp("duration", atts[4])); - const size_t nProfileMappings = sizeof(sCamcorderQualityNameMap)/sizeof(sCamcorderQualityNameMap[0]); + const size_t nProfileMappings = sizeof(sCamcorderQualityNameMap)/ + sizeof(sCamcorderQualityNameMap[0]); const int quality = findTagForName(sCamcorderQualityNameMap, nProfileMappings, atts[1]); CHECK(quality != -1); @@ -397,42 +388,6 @@ void MediaProfiles::addStartTimeOffset(int cameraId, const char** atts) ALOGV("%s: cameraId=%d, offset=%d ms", __func__, cameraId, offsetTimeMs); mStartTimeOffsets.replaceValueFor(cameraId, offsetTimeMs); } -/*static*/ MediaProfiles::ExportVideoProfile* -MediaProfiles::createExportVideoProfile(const char **atts) -{ - CHECK(!strcmp("name", atts[0]) && - !strcmp("profile", atts[2]) && - !strcmp("level", atts[4])); - - const size_t nMappings = - sizeof(sVideoEncoderNameMap)/sizeof(sVideoEncoderNameMap[0]); - const int codec = findTagForName(sVideoEncoderNameMap, nMappings, atts[1]); - CHECK(codec != -1); - - MediaProfiles::ExportVideoProfile *profile = - new MediaProfiles::ExportVideoProfile( - codec, atoi(atts[3]), atoi(atts[5])); - - return profile; -} -/*static*/ MediaProfiles::VideoEditorCap* -MediaProfiles::createVideoEditorCap(const char **atts, MediaProfiles *profiles) -{ - CHECK(!strcmp("maxInputFrameWidth", atts[0]) && - !strcmp("maxInputFrameHeight", atts[2]) && - !strcmp("maxOutputFrameWidth", atts[4]) && - !strcmp("maxOutputFrameHeight", atts[6]) && - !strcmp("maxPrefetchYUVFrames", atts[8])); - - MediaProfiles::VideoEditorCap *pVideoEditorCap = - new MediaProfiles::VideoEditorCap(atoi(atts[1]), atoi(atts[3]), - atoi(atts[5]), atoi(atts[7]), atoi(atts[9])); - - logVideoEditorCap(*pVideoEditorCap); - profiles->mVideoEditorCap = pVideoEditorCap; - - return pVideoEditorCap; -} /*static*/ void MediaProfiles::startElementHandler(void *userData, const char *name, const char **atts) @@ -464,10 +419,6 @@ MediaProfiles::startElementHandler(void *userData, const char *name, const char createCamcorderProfile(profiles->mCurrentCameraId, atts, profiles->mCameraIds)); } else if (strcmp("ImageEncoding", name) == 0) { profiles->addImageEncodingQualityLevel(profiles->mCurrentCameraId, atts); - } else if (strcmp("VideoEditorCap", name) == 0) { - createVideoEditorCap(atts, profiles); - } else if (strcmp("ExportVideoProfile", name) == 0) { - profiles->mVideoEditorExportProfiles.add(createExportVideoProfile(atts)); } } @@ -531,7 +482,6 @@ void MediaProfiles::checkAndAddRequiredProfilesIfNecessary() { CHECK(refIndex != -1); RequiredProfileRefInfo *info; camcorder_quality refQuality; - VideoCodec *codec = NULL; // Check high and low from either camcorder profile, timelapse profile // or high speed profile, but not all of them. Default, check camcorder profile @@ -722,16 +672,20 @@ MediaProfiles::createDefaultCamcorderTimeLapse480pProfile(camcorder_quality qual MediaProfiles::createDefaultCamcorderTimeLapseLowProfiles( MediaProfiles::CamcorderProfile **lowTimeLapseProfile, MediaProfiles::CamcorderProfile **lowSpecificTimeLapseProfile) { - *lowTimeLapseProfile = createDefaultCamcorderTimeLapseQcifProfile(CAMCORDER_QUALITY_TIME_LAPSE_LOW); - *lowSpecificTimeLapseProfile = createDefaultCamcorderTimeLapseQcifProfile(CAMCORDER_QUALITY_TIME_LAPSE_QCIF); + *lowTimeLapseProfile = createDefaultCamcorderTimeLapseQcifProfile( + CAMCORDER_QUALITY_TIME_LAPSE_LOW); + *lowSpecificTimeLapseProfile = createDefaultCamcorderTimeLapseQcifProfile( + CAMCORDER_QUALITY_TIME_LAPSE_QCIF); } /*static*/ void MediaProfiles::createDefaultCamcorderTimeLapseHighProfiles( MediaProfiles::CamcorderProfile **highTimeLapseProfile, MediaProfiles::CamcorderProfile **highSpecificTimeLapseProfile) { - *highTimeLapseProfile = createDefaultCamcorderTimeLapse480pProfile(CAMCORDER_QUALITY_TIME_LAPSE_HIGH); - *highSpecificTimeLapseProfile = createDefaultCamcorderTimeLapse480pProfile(CAMCORDER_QUALITY_TIME_LAPSE_480P); + *highTimeLapseProfile = createDefaultCamcorderTimeLapse480pProfile( + CAMCORDER_QUALITY_TIME_LAPSE_HIGH); + *highSpecificTimeLapseProfile = createDefaultCamcorderTimeLapse480pProfile( + CAMCORDER_QUALITY_TIME_LAPSE_480P); } /*static*/ MediaProfiles::CamcorderProfile* @@ -809,7 +763,8 @@ MediaProfiles::createDefaultCamcorderProfiles(MediaProfiles *profiles) // high camcorder time lapse profiles. MediaProfiles::CamcorderProfile *highTimeLapseProfile, *highSpecificTimeLapseProfile; - createDefaultCamcorderTimeLapseHighProfiles(&highTimeLapseProfile, &highSpecificTimeLapseProfile); + createDefaultCamcorderTimeLapseHighProfiles(&highTimeLapseProfile, + &highSpecificTimeLapseProfile); profiles->mCamcorderProfiles.add(highTimeLapseProfile); profiles->mCamcorderProfiles.add(highSpecificTimeLapseProfile); @@ -868,32 +823,6 @@ MediaProfiles::createDefaultImageEncodingQualityLevels(MediaProfiles *profiles) profiles->mImageEncodingQualityLevels.add(levels); } -/*static*/ void -MediaProfiles::createDefaultVideoEditorCap(MediaProfiles *profiles) -{ - profiles->mVideoEditorCap = - new MediaProfiles::VideoEditorCap( - VIDEOEDITOR_DEFAULT_MAX_INPUT_FRAME_WIDTH, - VIDEOEDITOR_DEFUALT_MAX_INPUT_FRAME_HEIGHT, - VIDEOEDITOR_DEFAULT_MAX_OUTPUT_FRAME_WIDTH, - VIDEOEDITOR_DEFUALT_MAX_OUTPUT_FRAME_HEIGHT, - VIDEOEDITOR_DEFAULT_MAX_PREFETCH_YUV_FRAMES); -} -/*static*/ void -MediaProfiles::createDefaultExportVideoProfiles(MediaProfiles *profiles) -{ - // Create default video export profiles - profiles->mVideoEditorExportProfiles.add( - new ExportVideoProfile(VIDEO_ENCODER_H263, - OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level10)); - profiles->mVideoEditorExportProfiles.add( - new ExportVideoProfile(VIDEO_ENCODER_MPEG_4_SP, - OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level1)); - profiles->mVideoEditorExportProfiles.add( - new ExportVideoProfile(VIDEO_ENCODER_H264, - OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel13)); -} - /*static*/ MediaProfiles* MediaProfiles::createDefaultInstance() { @@ -905,8 +834,6 @@ MediaProfiles::createDefaultInstance() createDefaultAudioDecoders(profiles); createDefaultEncoderOutputFileFormats(profiles); createDefaultImageEncodingQualityLevels(profiles); - createDefaultVideoEditorCap(profiles); - createDefaultExportVideoProfiles(profiles); return profiles; } @@ -1004,54 +931,6 @@ int MediaProfiles::getVideoEncoderParamByName(const char *name, video_encoder co ALOGE("The given video encoder param name %s is not found", name); return -1; } -int MediaProfiles::getVideoEditorExportParamByName( - const char *name, int codec) const -{ - ALOGV("getVideoEditorExportParamByName: name %s codec %d", name, codec); - ExportVideoProfile *exportProfile = NULL; - int index = -1; - for (size_t i =0; i < mVideoEditorExportProfiles.size(); i++) { - exportProfile = mVideoEditorExportProfiles[i]; - if (exportProfile->mCodec == codec) { - index = i; - break; - } - } - if (index == -1) { - ALOGE("The given video decoder %d is not found", codec); - return -1; - } - if (!strcmp("videoeditor.export.profile", name)) - return exportProfile->mProfile; - if (!strcmp("videoeditor.export.level", name)) - return exportProfile->mLevel; - - ALOGE("The given video editor export param name %s is not found", name); - return -1; -} -int MediaProfiles::getVideoEditorCapParamByName(const char *name) const -{ - ALOGV("getVideoEditorCapParamByName: %s", name); - - if (mVideoEditorCap == NULL) { - ALOGE("The mVideoEditorCap is not created, then create default cap."); - createDefaultVideoEditorCap(sInstance); - } - - if (!strcmp("videoeditor.input.width.max", name)) - return mVideoEditorCap->mMaxInputFrameWidth; - if (!strcmp("videoeditor.input.height.max", name)) - return mVideoEditorCap->mMaxInputFrameHeight; - if (!strcmp("videoeditor.output.width.max", name)) - return mVideoEditorCap->mMaxOutputFrameWidth; - if (!strcmp("videoeditor.output.height.max", name)) - return mVideoEditorCap->mMaxOutputFrameHeight; - if (!strcmp("maxPrefetchYUVFrames", name)) - return mVideoEditorCap->mMaxPrefetchYUVFrames; - - ALOGE("The given video editor param name %s is not found", name); - return -1; -} Vector<audio_encoder> MediaProfiles::getAudioEncoders() const { diff --git a/media/libmedia/MediaResource.cpp b/media/libmedia/MediaResource.cpp new file mode 100644 index 0000000..40ec0cb --- /dev/null +++ b/media/libmedia/MediaResource.cpp @@ -0,0 +1,67 @@ +/* + * Copyright 2015 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 "MediaResource" +#include <utils/Log.h> +#include <media/MediaResource.h> + +namespace android { + +const char kResourceSecureCodec[] = "secure-codec"; +const char kResourceNonSecureCodec[] = "non-secure-codec"; +const char kResourceAudioCodec[] = "audio-codec"; +const char kResourceVideoCodec[] = "video-codec"; +const char kResourceGraphicMemory[] = "graphic-memory"; + +MediaResource::MediaResource() : mValue(0) {} + +MediaResource::MediaResource(String8 type, uint64_t value) + : mType(type), + mValue(value) {} + +MediaResource::MediaResource(String8 type, String8 subType, uint64_t value) + : mType(type), + mSubType(subType), + mValue(value) {} + +void MediaResource::readFromParcel(const Parcel &parcel) { + mType = parcel.readString8(); + mSubType = parcel.readString8(); + mValue = parcel.readUint64(); +} + +void MediaResource::writeToParcel(Parcel *parcel) const { + parcel->writeString8(mType); + parcel->writeString8(mSubType); + parcel->writeUint64(mValue); +} + +String8 MediaResource::toString() const { + String8 str; + str.appendFormat("%s/%s:%llu", mType.string(), mSubType.string(), (unsigned long long)mValue); + return str; +} + +bool MediaResource::operator==(const MediaResource &other) const { + return (other.mType == mType) && (other.mSubType == mSubType) && (other.mValue == mValue); +} + +bool MediaResource::operator!=(const MediaResource &other) const { + return !(*this == other); +} + +}; // namespace android diff --git a/media/libmedia/MediaResourcePolicy.cpp b/media/libmedia/MediaResourcePolicy.cpp new file mode 100644 index 0000000..5210825 --- /dev/null +++ b/media/libmedia/MediaResourcePolicy.cpp @@ -0,0 +1,49 @@ +/* + * Copyright 2015 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 "MediaResourcePolicy" +#include <utils/Log.h> +#include <media/MediaResourcePolicy.h> + +namespace android { + +const char kPolicySupportsMultipleSecureCodecs[] = "supports-multiple-secure-codecs"; +const char kPolicySupportsSecureWithNonSecureCodec[] = "supports-secure-with-non-secure-codec"; + +MediaResourcePolicy::MediaResourcePolicy() {} + +MediaResourcePolicy::MediaResourcePolicy(String8 type, String8 value) + : mType(type), + mValue(value) {} + +void MediaResourcePolicy::readFromParcel(const Parcel &parcel) { + mType = parcel.readString8(); + mValue = parcel.readString8(); +} + +void MediaResourcePolicy::writeToParcel(Parcel *parcel) const { + parcel->writeString8(mType); + parcel->writeString8(mValue); +} + +String8 MediaResourcePolicy::toString() const { + String8 str; + str.appendFormat("%s:%s", mType.string(), mValue.string()); + return str; +} + +}; // namespace android diff --git a/media/libmedia/MemoryLeakTrackUtil.cpp b/media/libmedia/MemoryLeakTrackUtil.cpp index d31f721..554dbae 100644 --- a/media/libmedia/MemoryLeakTrackUtil.cpp +++ b/media/libmedia/MemoryLeakTrackUtil.cpp @@ -173,7 +173,7 @@ void dumpMemoryAddresses(int fd) #else // Does nothing -void dumpMemoryAddresses(int fd) {} +void dumpMemoryAddresses(int fd __unused) {} #endif } // namespace android diff --git a/media/libmedia/SingleStateQueue.cpp b/media/libmedia/SingleStateQueue.cpp deleted file mode 100644 index c241184..0000000 --- a/media/libmedia/SingleStateQueue.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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. - */ - -#include <new> -#include <cutils/atomic.h> -#include <media/SingleStateQueue.h> - -namespace android { - -template<typename T> SingleStateQueue<T>::Mutator::Mutator(Shared *shared) - : mSequence(0), mShared((Shared *) shared) -{ - // exactly one of Mutator and Observer must initialize, currently it is Observer - //shared->init(); -} - -template<typename T> int32_t SingleStateQueue<T>::Mutator::push(const T& value) -{ - Shared *shared = mShared; - int32_t sequence = mSequence; - sequence++; - android_atomic_acquire_store(sequence, &shared->mSequence); - shared->mValue = value; - sequence++; - android_atomic_release_store(sequence, &shared->mSequence); - mSequence = sequence; - // consider signalling a futex here, if we know that observer is waiting - return sequence; -} - -template<typename T> bool SingleStateQueue<T>::Mutator::ack() -{ - return mShared->mAck - mSequence == 0; -} - -template<typename T> bool SingleStateQueue<T>::Mutator::ack(int32_t sequence) -{ - // this relies on 2's complement rollover to detect an ancient sequence number - return mShared->mAck - sequence >= 0; -} - -template<typename T> SingleStateQueue<T>::Observer::Observer(Shared *shared) - : mSequence(0), mSeed(1), mShared((Shared *) shared) -{ - // exactly one of Mutator and Observer must initialize, currently it is Observer - shared->init(); -} - -template<typename T> bool SingleStateQueue<T>::Observer::poll(T& value) -{ - Shared *shared = mShared; - int32_t before = shared->mSequence; - if (before == mSequence) { - return false; - } - for (int tries = 0; ; ) { - const int MAX_TRIES = 5; - if (before & 1) { - if (++tries >= MAX_TRIES) { - return false; - } - before = shared->mSequence; - } else { - android_memory_barrier(); - T temp = shared->mValue; - int32_t after = android_atomic_release_load(&shared->mSequence); - if (after == before) { - value = temp; - shared->mAck = before; - mSequence = before; - return true; - } - if (++tries >= MAX_TRIES) { - return false; - } - before = after; - } - } -} - -#if 0 -template<typename T> SingleStateQueue<T>::SingleStateQueue(void /*Shared*/ *shared) -{ - ((Shared *) shared)->init(); -} -#endif - -} // namespace android - -// hack for gcc -#ifdef SINGLE_STATE_QUEUE_INSTANTIATIONS -#include SINGLE_STATE_QUEUE_INSTANTIATIONS -#endif diff --git a/media/libmedia/SingleStateQueueInstantiations.cpp b/media/libmedia/SingleStateQueueInstantiations.cpp deleted file mode 100644 index 0265c8c..0000000 --- a/media/libmedia/SingleStateQueueInstantiations.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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. - */ - -#include <media/SingleStateQueue.h> -#include <private/media/StaticAudioTrackState.h> -#include <media/AudioTimestamp.h> - -// FIXME hack for gcc - -namespace android { - -template class SingleStateQueue<StaticAudioTrackState>; // typedef StaticAudioTrackSingleStateQueue -template class SingleStateQueue<AudioTimestamp>; // typedef AudioTimestampSingleStateQueue - -} diff --git a/media/libmedia/StringArray.cpp b/media/libmedia/StringArray.cpp index 5f5b57a..b2e5907 100644 --- a/media/libmedia/StringArray.cpp +++ b/media/libmedia/StringArray.cpp @@ -16,7 +16,7 @@ // // Sortable array of strings. STL-ish, but STL-free. -// +// #include <stdlib.h> #include <string.h> @@ -110,4 +110,4 @@ void StringArray::setEntry(int idx, const char* str) { } -}; // namespace android +} // namespace android diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 2cc4685..6da5348 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -984,7 +984,6 @@ void ToneGenerator::stopTone() { if ((mStartTime.tv_sec != 0) && (clock_gettime(CLOCK_MONOTONIC, &stopTime) == 0)) { time_t sec = stopTime.tv_sec - mStartTime.tv_sec; long nsec = stopTime.tv_nsec - mStartTime.tv_nsec; - long durationMs; if (nsec < 0) { --sec; nsec += 1000000000; diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp index f91e3e4..f5c1b1f 100644 --- a/media/libmedia/Visualizer.cpp +++ b/media/libmedia/Visualizer.cpp @@ -34,11 +34,12 @@ namespace android { // --------------------------------------------------------------------------- -Visualizer::Visualizer (int32_t priority, +Visualizer::Visualizer (const String16& opPackageName, + int32_t priority, effect_callback_t cbf, void* user, int sessionId) - : AudioEffect(SL_IID_VISUALIZATION, NULL, priority, cbf, user, sessionId), + : AudioEffect(SL_IID_VISUALIZATION, opPackageName, NULL, priority, cbf, user, sessionId), mCaptureRate(CAPTURE_RATE_DEF), mCaptureSize(CAPTURE_SIZE_DEF), mSampleRate(44100000), @@ -53,12 +54,8 @@ Visualizer::Visualizer (int32_t priority, Visualizer::~Visualizer() { ALOGV("Visualizer::~Visualizer()"); - if (mCaptureThread != NULL) { - mCaptureThread->requestExitAndWait(); - mCaptureThread.clear(); - } - mCaptureCallBack = NULL; - mCaptureFlags = 0; + setEnabled(false); + setCaptureCallBack(NULL, NULL, 0, 0, true); } status_t Visualizer::setEnabled(bool enabled) @@ -98,14 +95,14 @@ status_t Visualizer::setEnabled(bool enabled) } status_t Visualizer::setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags, - uint32_t rate) + uint32_t rate, bool force) { if (rate > CAPTURE_RATE_MAX) { return BAD_VALUE; } Mutex::Autolock _l(mCaptureLock); - if (mEnabled) { + if (force || mEnabled) { return INVALID_OPERATION; } @@ -429,4 +426,4 @@ bool Visualizer::CaptureThread::threadLoop() return false; } -}; // namespace android +} // namespace android diff --git a/media/libmedia/docs/Makefile b/media/libmedia/docs/Makefile new file mode 100644 index 0000000..bddbc9b --- /dev/null +++ b/media/libmedia/docs/Makefile @@ -0,0 +1,2 @@ +paused.png : paused.dot + dot -Tpng < $< > $@ diff --git a/media/libmedia/docs/paused.dot b/media/libmedia/docs/paused.dot new file mode 100644 index 0000000..11e1777 --- /dev/null +++ b/media/libmedia/docs/paused.dot @@ -0,0 +1,85 @@ +digraph paused { +initial [label="INITIAL\n\ +mIgnoreNextPausedInt = false\n\ +mPaused = false\n\ +mPausedInt = false"]; + +resume_body [label="mIgnoreNextPausedInt = true\nif (mPaused || mPausedInt)"]; +resume_paused [label="mPaused = false\nmPausedInt = false\nsignal()"]; +resume_paused -> resume_merged; +resume_merged [label="return"]; + +Application -> ATstop; +ATstop [label="AudioTrack::stop()"]; +ATstop -> pause; +Application -> ATpause; +ATpause [label="AudioTrack::pause()"]; +ATpause -> pause; +ATstart -> resume; +ATstart [label="AudioTrack::start()"]; +destructor [label="~AudioTrack()"]; +destructor -> requestExit; +requestExit [label="AudioTrackThread::requestExit()"]; +requestExit -> resume; +Application -> ATsetMarkerPosition +ATsetMarkerPosition [label="AudioTrack::setMarkerPosition()\n[sets marker variables]"]; +ATsetMarkerPosition -> ATTwake +Application -> ATsetPositionUpdatePeriod +ATsetPositionUpdatePeriod [label="AudioTrack::setPositionUpdatePeriod()\n[sets update period variables]"]; +ATsetPositionUpdatePeriod -> ATTwake +Application -> ATstart; + +resume [label="AudioTrackThread::resume()"]; +resume -> resume_body; + +resume_body -> resume_paused [label="true"]; +resume_body -> resume_merged [label="false"]; + +ATTwake [label="AudioTrackThread::wake()\nif (!mPaused && mPausedInt && mPausedNs > 0)"]; +ATTwake-> ATTWake_wakeable [label="true"]; +ATTWake_wakeable [label="mIgnoreNextPausedInt = true\nmPausedInt = false\nsignal()"]; +ATTwake-> ATTWake_cannotwake [label="false"] +ATTWake_cannotwake [label="ignore"]; + +pause [label="mPaused = true"]; +pause -> return; + +threadLoop [label="AudioTrackThread::threadLoop()\nENTRY"]; +threadLoop -> threadLoop_1; +threadLoop_1 [label="if (mPaused)"]; +threadLoop_1 -> threadLoop_1_true [label="true"]; +threadLoop_1 -> threadLoop_2 [label="false"]; +threadLoop_1_true [label="wait()\nreturn true"]; +threadLoop_2 [label="if (mIgnoreNextPausedInt)"]; +threadLoop_2 -> threadLoop_2_true [label="true"]; +threadLoop_2 -> threadLoop_3 [label="false"]; +threadLoop_2_true [label="mIgnoreNextPausedInt = false\nmPausedInt = false"]; +threadLoop_2_true -> threadLoop_3; +threadLoop_3 [label="if (mPausedInt)"]; +threadLoop_3 -> threadLoop_3_true [label="true"]; +threadLoop_3 -> threadLoop_4 [label="false"]; +threadLoop_3_true [label="wait()\nmPausedInt = false\nreturn true"]; +threadLoop_4 [label="if (exitPending)"]; +threadLoop_4 -> threadLoop_4_true [label="true"]; +threadLoop_4 -> threadLoop_5 [label="false"]; +threadLoop_4_true [label="return false"]; +threadLoop_5 [label="ns = processAudioBuffer()"]; +threadLoop_5 -> threadLoop_6; +threadLoop_6 [label="case ns"]; +threadLoop_6 -> threadLoop_6_0 [label="0"]; +threadLoop_6 -> threadLoop_6_NS_INACTIVE [label="NS_INACTIVE"]; +threadLoop_6 -> threadLoop_6_NS_NEVER [label="NS_NEVER"]; +threadLoop_6 -> threadLoop_6_NS_WHENEVER [label="NS_WHENEVER"]; +threadLoop_6 -> threadLoop_6_default [label="default"]; +threadLoop_6_default [label="if (ns < 0)"]; +threadLoop_6_default -> threadLoop_6_default_true [label="true"]; +threadLoop_6_default -> threadLoop_6_default_false [label="false"]; +threadLoop_6_default_true [label="FATAL"]; +threadLoop_6_default_false [label="pauseInternal(ns) [wake()-able]\nmPausedInternal = true\nmPausedNs = ns\nreturn true"]; +threadLoop_6_0 [label="return true"]; +threadLoop_6_NS_INACTIVE [label="pauseInternal()\nmPausedInternal = true\nmPausedNs = 0\nreturn true"]; +threadLoop_6_NS_NEVER [label="return false"]; +threadLoop_6_NS_WHENEVER [label="ns = 1s"]; +threadLoop_6_NS_WHENEVER -> threadLoop_6_default_false; + +} diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp index 8e8a1ed..9a76f58 100644 --- a/media/libmedia/mediametadataretriever.cpp +++ b/media/libmedia/mediametadataretriever.cpp @@ -129,6 +129,18 @@ status_t MediaMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t l return mRetriever->setDataSource(fd, offset, length); } +status_t MediaMetadataRetriever::setDataSource( + const sp<IDataSource>& dataSource) +{ + ALOGV("setDataSource(IDataSource)"); + Mutex::Autolock _l(mLock); + if (mRetriever == 0) { + ALOGE("retriever is not initialized"); + return INVALID_OPERATION; + } + return mRetriever->setDataSource(dataSource); +} + sp<IMemory> MediaMetadataRetriever::getFrameAtTime(int64_t timeUs, int option) { ALOGV("getFrameAtTime: time(%" PRId64 " us) option(%d)", timeUs, option); @@ -176,4 +188,4 @@ MediaMetadataRetriever::DeathNotifier::~DeathNotifier() } } -}; // namespace android +} // namespace android diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 05c89ed..502ab2d 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -32,7 +32,10 @@ #include <gui/Surface.h> #include <media/mediaplayer.h> +#include <media/AudioResamplerPublic.h> #include <media/AudioSystem.h> +#include <media/AVSyncSettings.h> +#include <media/IDataSource.h> #include <binder/MemoryBase.h> @@ -194,6 +197,22 @@ status_t MediaPlayer::setDataSource(const sp<IStreamSource> &source) return err; } +status_t MediaPlayer::setDataSource(const sp<IDataSource> &source) +{ + ALOGV("setDataSource(IDataSource)"); + status_t err = UNKNOWN_ERROR; + const sp<IMediaPlayerService>& service(getMediaPlayerService()); + if (service != 0) { + sp<IMediaPlayer> player(service->create(this, mAudioSessionId)); + if ((NO_ERROR != doSetRetransmitEndpoint(player)) || + (NO_ERROR != player->setDataSource(source))) { + player.clear(); + } + err = attachNewPlayer(player); + } + return err; +} + status_t MediaPlayer::invoke(const Parcel& request, Parcel *reply) { Mutex::Autolock _l(mLock); @@ -240,10 +259,11 @@ status_t MediaPlayer::setVideoSurfaceTexture( // must call with lock held status_t MediaPlayer::prepareAsync_l() { - if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) { - mPlayer->setAudioStreamType(mStreamType); + if ( (mPlayer != 0) && ( mCurrentState & (MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) { if (mAudioAttributesParcel != NULL) { mPlayer->setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, *mAudioAttributesParcel); + } else { + mPlayer->setAudioStreamType(mStreamType); } mCurrentState = MEDIA_PLAYER_PREPARING; return mPlayer->prepareAsync(); @@ -371,6 +391,9 @@ bool MediaPlayer::isPlaying() if ((mCurrentState & MEDIA_PLAYER_STARTED) && ! temp) { ALOGE("internal/external state mismatch corrected"); mCurrentState = MEDIA_PLAYER_PAUSED; + } else if ((mCurrentState & MEDIA_PLAYER_PAUSED) && temp) { + ALOGE("internal/external state mismatch corrected"); + mCurrentState = MEDIA_PLAYER_STARTED; } return temp; } @@ -378,6 +401,52 @@ bool MediaPlayer::isPlaying() return false; } +status_t MediaPlayer::setPlaybackSettings(const AudioPlaybackRate& rate) +{ + ALOGV("setPlaybackSettings: %f %f %d %d", + rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode); + // Negative speed and pitch does not make sense. Further validation will + // be done by the respective mediaplayers. + if (rate.mSpeed < 0.f || rate.mPitch < 0.f) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + if (mPlayer == 0) return INVALID_OPERATION; + status_t err = mPlayer->setPlaybackSettings(rate); + if (err == OK) { + if (rate.mSpeed == 0.f && mCurrentState == MEDIA_PLAYER_STARTED) { + mCurrentState = MEDIA_PLAYER_PAUSED; + } else if (rate.mSpeed != 0.f && mCurrentState == MEDIA_PLAYER_PAUSED) { + mCurrentState = MEDIA_PLAYER_STARTED; + } + } + return err; +} + +status_t MediaPlayer::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) +{ + Mutex::Autolock _l(mLock); + if (mPlayer == 0) return INVALID_OPERATION; + return mPlayer->getPlaybackSettings(rate); +} + +status_t MediaPlayer::setSyncSettings(const AVSyncSettings& sync, float videoFpsHint) +{ + ALOGV("setSyncSettings: %u %u %f %f", + sync.mSource, sync.mAudioAdjustMode, sync.mTolerance, videoFpsHint); + Mutex::Autolock _l(mLock); + if (mPlayer == 0) return INVALID_OPERATION; + return mPlayer->setSyncSettings(sync, videoFpsHint); +} + +status_t MediaPlayer::getSyncSettings( + AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */) +{ + Mutex::Autolock _l(mLock); + if (mPlayer == 0) return INVALID_OPERATION; + return mPlayer->getSyncSettings(sync, videoFps); +} + status_t MediaPlayer::getVideoWidth(int *w) { ALOGV("getVideoWidth"); @@ -414,7 +483,8 @@ status_t MediaPlayer::getCurrentPosition(int *msec) status_t MediaPlayer::getDuration_l(int *msec) { ALOGV("getDuration_l"); - bool isValidState = (mCurrentState & (MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_STOPPED | MEDIA_PLAYER_PLAYBACK_COMPLETE)); + bool isValidState = (mCurrentState & (MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | + MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_STOPPED | MEDIA_PLAYER_PLAYBACK_COMPLETE)); if (mPlayer != 0 && isValidState) { int durationMs; status_t ret = mPlayer->getDuration(&durationMs); @@ -443,7 +513,8 @@ status_t MediaPlayer::getDuration(int *msec) status_t MediaPlayer::seekTo_l(int msec) { ALOGV("seekTo %d", msec); - if ((mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE) ) ) { + if ((mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED | + MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE) ) ) { if ( msec < 0 ) { ALOGW("Attempt to seek to invalid position: %d", msec); msec = 0; @@ -477,7 +548,8 @@ status_t MediaPlayer::seekTo_l(int msec) return NO_ERROR; } } - ALOGE("Attempt to perform seekTo in wrong state: mPlayer=%p, mCurrentState=%u", mPlayer.get(), mCurrentState); + ALOGE("Attempt to perform seekTo in wrong state: mPlayer=%p, mCurrentState=%u", mPlayer.get(), + mCurrentState); return INVALID_OPERATION; } @@ -502,6 +574,7 @@ status_t MediaPlayer::reset_l() ALOGE("reset() failed with return code (%d)", ret); mCurrentState = MEDIA_PLAYER_STATE_ERROR; } else { + mPlayer->disconnect(); mCurrentState = MEDIA_PLAYER_IDLE; } // setDataSource has to be called again to create a @@ -663,24 +736,28 @@ status_t MediaPlayer::checkStateForKeySet_l(int key) status_t MediaPlayer::setParameter(int key, const Parcel& request) { ALOGV("MediaPlayer::setParameter(%d)", key); + status_t status = INVALID_OPERATION; Mutex::Autolock _l(mLock); if (checkStateForKeySet_l(key) != OK) { - return INVALID_OPERATION; - } - if (mPlayer != NULL) { - return mPlayer->setParameter(key, request); + return status; } switch (key) { case KEY_PARAMETER_AUDIO_ATTRIBUTES: - // no player, save the marshalled audio attributes + // save the marshalled audio attributes if (mAudioAttributesParcel != NULL) { delete mAudioAttributesParcel; }; mAudioAttributesParcel = new Parcel(); mAudioAttributesParcel->appendFrom(&request, 0, request.dataSize()); - return OK; + status = OK; + break; default: - ALOGV("setParameter: no active player"); - return INVALID_OPERATION; + ALOGV_IF(mPlayer == NULL, "setParameter: no active player"); + break; + } + + if (mPlayer != NULL) { + status = mPlayer->setParameter(key, request); } + return status; } status_t MediaPlayer::getParameter(int key, Parcel *reply) @@ -818,6 +895,9 @@ void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj) case MEDIA_SUBTITLE_DATA: ALOGV("Received subtitle data message"); break; + case MEDIA_META_DATA: + ALOGV("Received timed metadata message"); + break; default: ALOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2); break; @@ -855,4 +935,4 @@ status_t MediaPlayer::setNextMediaPlayer(const sp<MediaPlayer>& next) { return mPlayer->setNextPlayer(next == NULL ? NULL : next->mPlayer); } -}; // namespace android +} // namespace android diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index 1952b86..8bbd8f1 100644 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -27,6 +27,7 @@ #include <media/IMediaPlayerService.h> #include <media/IMediaRecorder.h> #include <media/mediaplayer.h> // for MEDIA_ERROR_SERVER_DIED +#include <media/stagefright/PersistentSurface.h> #include <gui/IGraphicBufferProducer.h> namespace android { @@ -264,32 +265,6 @@ status_t MediaRecorder::setAudioEncoder(int ae) return ret; } -status_t MediaRecorder::setOutputFile(const char* path) -{ - ALOGV("setOutputFile(%s)", path); - if (mMediaRecorder == NULL) { - ALOGE("media recorder is not initialized yet"); - return INVALID_OPERATION; - } - if (mIsOutputFileSet) { - ALOGE("output file has already been set"); - return INVALID_OPERATION; - } - if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { - ALOGE("setOutputFile called in an invalid state(%d)", mCurrentState); - return INVALID_OPERATION; - } - - status_t ret = mMediaRecorder->setOutputFile(path); - if (OK != ret) { - ALOGV("setOutputFile failed: %d", ret); - mCurrentState = MEDIA_RECORDER_ERROR; - return ret; - } - mIsOutputFileSet = true; - return ret; -} - status_t MediaRecorder::setOutputFile(int fd, int64_t offset, int64_t length) { ALOGV("setOutputFile(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length); @@ -370,6 +345,24 @@ sp<IGraphicBufferProducer> MediaRecorder:: +status_t MediaRecorder::setInputSurface(const sp<PersistentSurface>& surface) +{ + ALOGV("setInputSurface"); + if (mMediaRecorder == NULL) { + ALOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + bool isInvalidState = (mCurrentState & + (MEDIA_RECORDER_PREPARED | + MEDIA_RECORDER_RECORDING)); + if (isInvalidState) { + ALOGE("setInputSurface is called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + return mMediaRecorder->setInputSurface(surface->getBufferConsumer()); +} + status_t MediaRecorder::setVideoFrameRate(int frames_per_second) { ALOGV("setVideoFrameRate(%d)", frames_per_second); @@ -620,13 +613,13 @@ status_t MediaRecorder::release() return INVALID_OPERATION; } -MediaRecorder::MediaRecorder() : mSurfaceMediaSource(NULL) +MediaRecorder::MediaRecorder(const String16& opPackageName) : mSurfaceMediaSource(NULL) { ALOGV("constructor"); const sp<IMediaPlayerService>& service(getMediaPlayerService()); if (service != NULL) { - mMediaRecorder = service->createMediaRecorder(); + mMediaRecorder = service->createMediaRecorder(opPackageName); } if (mMediaRecorder != NULL) { mCurrentState = MEDIA_RECORDER_IDLE; @@ -706,4 +699,4 @@ void MediaRecorder::died() notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_ERROR_SERVER_DIED, 0); } -}; // namespace android +} // namespace android |