зеркало из https://github.com/mozilla/gecko-dev.git
237 строки
5.9 KiB
C++
237 строки
5.9 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
/*
|
|
* Copyright (c) 2014 The Linux Foundation. All rights reserved.
|
|
* Copyright (C) 2008 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 <stagefright/foundation/ADebug.h>
|
|
#include "AudioOutput.h"
|
|
|
|
namespace mozilla {
|
|
|
|
extern PRLogModuleInfo* gAudioOffloadPlayerLog;
|
|
#define AUDIO_OFFLOAD_LOG(type, msg) \
|
|
PR_LOG(gAudioOffloadPlayerLog, type, msg)
|
|
|
|
using namespace android;
|
|
|
|
AudioOutput::AudioOutput(int aSessionId, int aUid) :
|
|
mCallbackCookie(nullptr),
|
|
mCallback(nullptr),
|
|
mCallbackData(nullptr),
|
|
mUid(aUid),
|
|
mSessionId(aSessionId)
|
|
{
|
|
if (!gAudioOffloadPlayerLog) {
|
|
gAudioOffloadPlayerLog = PR_NewLogModule("AudioOffloadPlayer");
|
|
}
|
|
}
|
|
|
|
AudioOutput::~AudioOutput()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
ssize_t AudioOutput::FrameSize() const
|
|
{
|
|
if (!mTrack.get()) {
|
|
return NO_INIT;
|
|
}
|
|
return mTrack->frameSize();
|
|
}
|
|
|
|
status_t AudioOutput::GetPosition(uint32_t *aPosition) const
|
|
{
|
|
if (!mTrack.get()) {
|
|
return NO_INIT;
|
|
}
|
|
return mTrack->getPosition(aPosition);
|
|
}
|
|
|
|
status_t AudioOutput::SetVolume(float aVolume) const
|
|
{
|
|
if (!mTrack.get()) {
|
|
return NO_INIT;
|
|
}
|
|
return mTrack->setVolume(aVolume);
|
|
}
|
|
|
|
status_t AudioOutput::SetParameters(const String8& aKeyValuePairs)
|
|
{
|
|
if (!mTrack.get()) {
|
|
return NO_INIT;
|
|
}
|
|
return mTrack->setParameters(aKeyValuePairs);
|
|
}
|
|
|
|
status_t AudioOutput::Open(uint32_t aSampleRate,
|
|
int aChannelCount,
|
|
audio_channel_mask_t aChannelMask,
|
|
audio_format_t aFormat,
|
|
AudioCallback aCb,
|
|
void* aCookie,
|
|
audio_output_flags_t aFlags,
|
|
const audio_offload_info_t *aOffloadInfo)
|
|
{
|
|
mCallback = aCb;
|
|
mCallbackCookie = aCookie;
|
|
|
|
if (((aFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) || !aCb ||
|
|
!aOffloadInfo) {
|
|
return BAD_VALUE;
|
|
}
|
|
|
|
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("open(%u, %d, 0x%x, 0x%x, %d 0x%x)",
|
|
aSampleRate, aChannelCount, aChannelMask, aFormat, mSessionId, aFlags));
|
|
|
|
if (aChannelMask == CHANNEL_MASK_USE_CHANNEL_ORDER) {
|
|
aChannelMask = audio_channel_out_mask_from_count(aChannelCount);
|
|
if (0 == aChannelMask) {
|
|
AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("open() error, can\'t derive mask for"
|
|
" %d audio channels", aChannelCount));
|
|
return NO_INIT;
|
|
}
|
|
}
|
|
|
|
sp<AudioTrack> t;
|
|
CallbackData* newcbd = new CallbackData(this);
|
|
|
|
t = new AudioTrack(
|
|
AUDIO_STREAM_MUSIC,
|
|
aSampleRate,
|
|
aFormat,
|
|
aChannelMask,
|
|
0, // Offloaded tracks will get frame count from AudioFlinger
|
|
aFlags,
|
|
CallbackWrapper,
|
|
newcbd,
|
|
0, // notification frames
|
|
mSessionId,
|
|
AudioTrack::TRANSFER_CALLBACK,
|
|
aOffloadInfo,
|
|
mUid);
|
|
|
|
if ((!t.get()) || (t->initCheck() != NO_ERROR)) {
|
|
AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Unable to create audio track"));
|
|
delete newcbd;
|
|
return NO_INIT;
|
|
}
|
|
|
|
mCallbackData = newcbd;
|
|
t->setVolume(1.0);
|
|
|
|
mTrack = t;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t AudioOutput::Start()
|
|
{
|
|
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__));
|
|
if (!mTrack.get()) {
|
|
return NO_INIT;
|
|
}
|
|
mTrack->setVolume(1.0);
|
|
return mTrack->start();
|
|
}
|
|
|
|
void AudioOutput::Stop()
|
|
{
|
|
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__));
|
|
if (mTrack.get()) {
|
|
mTrack->stop();
|
|
}
|
|
}
|
|
|
|
void AudioOutput::Flush()
|
|
{
|
|
if (mTrack.get()) {
|
|
mTrack->flush();
|
|
}
|
|
}
|
|
|
|
void AudioOutput::Pause()
|
|
{
|
|
if (mTrack.get()) {
|
|
mTrack->pause();
|
|
}
|
|
}
|
|
|
|
void AudioOutput::Close()
|
|
{
|
|
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__));
|
|
mTrack.clear();
|
|
|
|
delete mCallbackData;
|
|
mCallbackData = nullptr;
|
|
}
|
|
|
|
// static
|
|
void AudioOutput::CallbackWrapper(int aEvent, void* aCookie, void* aInfo)
|
|
{
|
|
CallbackData* data = (CallbackData*) aCookie;
|
|
data->Lock();
|
|
AudioOutput* me = data->GetOutput();
|
|
AudioTrack::Buffer* buffer = (AudioTrack::Buffer*) aInfo;
|
|
if (!me) {
|
|
// no output set, likely because the track was scheduled to be reused
|
|
// by another player, but the format turned out to be incompatible.
|
|
data->Unlock();
|
|
if (buffer) {
|
|
buffer->size = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch(aEvent) {
|
|
|
|
case AudioTrack::EVENT_MORE_DATA: {
|
|
|
|
size_t actualSize = (*me->mCallback)(me, buffer->raw, buffer->size,
|
|
me->mCallbackCookie, CB_EVENT_FILL_BUFFER);
|
|
|
|
if (actualSize == 0 && buffer->size > 0) {
|
|
// We've reached EOS but the audio track is not stopped yet,
|
|
// keep playing silence.
|
|
memset(buffer->raw, 0, buffer->size);
|
|
actualSize = buffer->size;
|
|
}
|
|
|
|
buffer->size = actualSize;
|
|
} break;
|
|
|
|
case AudioTrack::EVENT_STREAM_END:
|
|
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Callback wrapper: EVENT_STREAM_END"));
|
|
(*me->mCallback)(me, nullptr /* buffer */, 0 /* size */,
|
|
me->mCallbackCookie, CB_EVENT_STREAM_END);
|
|
break;
|
|
|
|
case AudioTrack::EVENT_NEW_IAUDIOTRACK :
|
|
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Callback wrapper: EVENT_TEAR_DOWN"));
|
|
(*me->mCallback)(me, nullptr /* buffer */, 0 /* size */,
|
|
me->mCallbackCookie, CB_EVENT_TEAR_DOWN);
|
|
break;
|
|
|
|
default:
|
|
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("received unknown event type: %d in"
|
|
" Callback wrapper!", aEvent));
|
|
break;
|
|
}
|
|
|
|
data->Unlock();
|
|
}
|
|
|
|
} // namespace mozilla
|