michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* michael@0: * Copyright (c) 2014 The Linux Foundation. All rights reserved. michael@0: * Copyright (C) 2008 The Android Open Source Project michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: #include michael@0: #include "AudioOutput.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: #ifdef PR_LOGGING michael@0: extern PRLogModuleInfo* gAudioOffloadPlayerLog; michael@0: #define AUDIO_OFFLOAD_LOG(type, msg) \ michael@0: PR_LOG(gAudioOffloadPlayerLog, type, msg) michael@0: #else michael@0: #define AUDIO_OFFLOAD_LOG(type, msg) michael@0: #endif michael@0: michael@0: using namespace android; michael@0: michael@0: AudioOutput::AudioOutput(int aSessionId, int aUid) : michael@0: mCallback(nullptr), michael@0: mCallbackCookie(nullptr), michael@0: mCallbackData(nullptr), michael@0: mSessionId(aSessionId), michael@0: mUid(aUid) michael@0: { michael@0: #ifdef PR_LOGGING michael@0: if (!gAudioOffloadPlayerLog) { michael@0: gAudioOffloadPlayerLog = PR_NewLogModule("AudioOffloadPlayer"); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: AudioOutput::~AudioOutput() michael@0: { michael@0: Close(); michael@0: } michael@0: michael@0: ssize_t AudioOutput::FrameSize() const michael@0: { michael@0: if (!mTrack.get()) { michael@0: return NO_INIT; michael@0: } michael@0: return mTrack->frameSize(); michael@0: } michael@0: michael@0: status_t AudioOutput::GetPosition(uint32_t *aPosition) const michael@0: { michael@0: if (!mTrack.get()) { michael@0: return NO_INIT; michael@0: } michael@0: return mTrack->getPosition(aPosition); michael@0: } michael@0: michael@0: status_t AudioOutput::SetVolume(float aVolume) const michael@0: { michael@0: if (!mTrack.get()) { michael@0: return NO_INIT; michael@0: } michael@0: return mTrack->setVolume(aVolume); michael@0: } michael@0: michael@0: status_t AudioOutput::SetParameters(const String8& aKeyValuePairs) michael@0: { michael@0: if (!mTrack.get()) { michael@0: return NO_INIT; michael@0: } michael@0: return mTrack->setParameters(aKeyValuePairs); michael@0: } michael@0: michael@0: status_t AudioOutput::Open(uint32_t aSampleRate, michael@0: int aChannelCount, michael@0: audio_channel_mask_t aChannelMask, michael@0: audio_format_t aFormat, michael@0: AudioCallback aCb, michael@0: void* aCookie, michael@0: audio_output_flags_t aFlags, michael@0: const audio_offload_info_t *aOffloadInfo) michael@0: { michael@0: mCallback = aCb; michael@0: mCallbackCookie = aCookie; michael@0: michael@0: if (((aFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) || !aCb || michael@0: !aOffloadInfo) { michael@0: return BAD_VALUE; michael@0: } michael@0: michael@0: AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("open(%u, %d, 0x%x, 0x%x, %d 0x%x)", michael@0: aSampleRate, aChannelCount, aChannelMask, aFormat, mSessionId, aFlags)); michael@0: michael@0: if (aChannelMask == CHANNEL_MASK_USE_CHANNEL_ORDER) { michael@0: aChannelMask = audio_channel_out_mask_from_count(aChannelCount); michael@0: if (0 == aChannelMask) { michael@0: AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("open() error, can\'t derive mask for" michael@0: " %d audio channels", aChannelCount)); michael@0: return NO_INIT; michael@0: } michael@0: } michael@0: michael@0: sp t; michael@0: CallbackData* newcbd = new CallbackData(this); michael@0: michael@0: t = new AudioTrack( michael@0: AUDIO_STREAM_MUSIC, michael@0: aSampleRate, michael@0: aFormat, michael@0: aChannelMask, michael@0: 0, // Offloaded tracks will get frame count from AudioFlinger michael@0: aFlags, michael@0: CallbackWrapper, michael@0: newcbd, michael@0: 0, // notification frames michael@0: mSessionId, michael@0: AudioTrack::TRANSFER_CALLBACK, michael@0: aOffloadInfo, michael@0: mUid); michael@0: michael@0: if ((!t.get()) || (t->initCheck() != NO_ERROR)) { michael@0: AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Unable to create audio track")); michael@0: delete newcbd; michael@0: return NO_INIT; michael@0: } michael@0: michael@0: mCallbackData = newcbd; michael@0: t->setVolume(1.0); michael@0: michael@0: mTrack = t; michael@0: return NO_ERROR; michael@0: } michael@0: michael@0: status_t AudioOutput::Start() michael@0: { michael@0: AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__)); michael@0: if (!mTrack.get()) { michael@0: return NO_INIT; michael@0: } michael@0: mTrack->setVolume(1.0); michael@0: return mTrack->start(); michael@0: } michael@0: michael@0: void AudioOutput::Stop() michael@0: { michael@0: AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__)); michael@0: if (mTrack.get()) { michael@0: mTrack->stop(); michael@0: } michael@0: } michael@0: michael@0: void AudioOutput::Flush() michael@0: { michael@0: if (mTrack.get()) { michael@0: mTrack->flush(); michael@0: } michael@0: } michael@0: michael@0: void AudioOutput::Pause() michael@0: { michael@0: if (mTrack.get()) { michael@0: mTrack->pause(); michael@0: } michael@0: } michael@0: michael@0: void AudioOutput::Close() michael@0: { michael@0: AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__)); michael@0: mTrack.clear(); michael@0: michael@0: delete mCallbackData; michael@0: mCallbackData = nullptr; michael@0: } michael@0: michael@0: // static michael@0: void AudioOutput::CallbackWrapper(int aEvent, void* aCookie, void* aInfo) michael@0: { michael@0: CallbackData* data = (CallbackData*) aCookie; michael@0: data->Lock(); michael@0: AudioOutput* me = data->GetOutput(); michael@0: AudioTrack::Buffer* buffer = (AudioTrack::Buffer*) aInfo; michael@0: if (!me) { michael@0: // no output set, likely because the track was scheduled to be reused michael@0: // by another player, but the format turned out to be incompatible. michael@0: data->Unlock(); michael@0: if (buffer) { michael@0: buffer->size = 0; michael@0: } michael@0: return; michael@0: } michael@0: michael@0: switch(aEvent) { michael@0: michael@0: case AudioTrack::EVENT_MORE_DATA: { michael@0: michael@0: size_t actualSize = (*me->mCallback)(me, buffer->raw, buffer->size, michael@0: me->mCallbackCookie, CB_EVENT_FILL_BUFFER); michael@0: michael@0: if (actualSize == 0 && buffer->size > 0) { michael@0: // We've reached EOS but the audio track is not stopped yet, michael@0: // keep playing silence. michael@0: memset(buffer->raw, 0, buffer->size); michael@0: actualSize = buffer->size; michael@0: } michael@0: michael@0: buffer->size = actualSize; michael@0: } break; michael@0: michael@0: case AudioTrack::EVENT_STREAM_END: michael@0: AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Callback wrapper: EVENT_STREAM_END")); michael@0: (*me->mCallback)(me, nullptr /* buffer */, 0 /* size */, michael@0: me->mCallbackCookie, CB_EVENT_STREAM_END); michael@0: break; michael@0: michael@0: case AudioTrack::EVENT_NEW_IAUDIOTRACK : michael@0: AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Callback wrapper: EVENT_TEAR_DOWN")); michael@0: (*me->mCallback)(me, nullptr /* buffer */, 0 /* size */, michael@0: me->mCallbackCookie, CB_EVENT_TEAR_DOWN); michael@0: break; michael@0: michael@0: default: michael@0: AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("received unknown event type: %d in" michael@0: " Callback wrapper!", aEvent)); michael@0: break; michael@0: } michael@0: michael@0: data->Unlock(); michael@0: } michael@0: michael@0: } // namespace mozilla