/*
**
** Copyright 2018, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#ifndef ANDROID_MEDIAPLAYER2AUDIOOUTPUT_H
#define ANDROID_MEDIAPLAYER2AUDIOOUTPUT_H
#include <mediaplayer2/MediaPlayer2Interface.h>
#include <mediaplayer2/JAudioTrack.h>
#include <mediaplayer2/JObjectHolder.h>
#include <utility>
#include <utils/String16.h>
#include <utils/Vector.h>
#include "jni.h"
namespace android {
class AudioTrack;
class MediaPlayer2AudioOutput : public MediaPlayer2Interface::AudioSink
{
class CallbackData;
public:
MediaPlayer2AudioOutput(int32_t sessionId,
uid_t uid,
int pid,
const jobject attributes);
virtual ~MediaPlayer2AudioOutput();
virtual bool ready() const {
return mJAudioTrack != nullptr;
}
virtual ssize_t bufferSize() const;
virtual ssize_t frameCount() const;
virtual ssize_t channelCount() const;
virtual ssize_t frameSize() const;
virtual uint32_t latency() const;
virtual float msecsPerFrame() const;
virtual status_t getPosition(uint32_t *position) const;
virtual status_t getTimestamp(AudioTimestamp &ts) const;
virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const;
virtual status_t getFramesWritten(uint32_t *frameswritten) const;
virtual int32_t getSessionId() const;
virtual void setSessionId(const int32_t id);
virtual uint32_t getSampleRate() const;
virtual int64_t getBufferDurationInUs() const;
virtual status_t open(
uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
audio_format_t format,
AudioCallback cb, void *cookie,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
const audio_offload_info_t *offloadInfo = NULL,
uint32_t suggestedFrameCount = 0);
virtual status_t start();
virtual ssize_t write(const void* buffer, size_t size, bool blocking = true);
virtual void stop();
virtual void flush();
virtual void pause();
virtual void close();
void setAudioAttributes(const jobject attributes);
virtual audio_stream_type_t getAudioStreamType() const;
void setVolume(float volume);
virtual status_t setPlaybackRate(const AudioPlaybackRate& rate);
virtual status_t getPlaybackRate(AudioPlaybackRate* rate /* nonnull */);
status_t setAuxEffectSendLevel(float level);
status_t attachAuxEffect(int effectId);
virtual status_t dump(int fd, const Vector<String16>& args) const;
static bool isOnEmulator();
static int getMinBufferCount();
virtual bool needsTrailingPadding() {
return true;
// TODO: return correct value.
//return mNextOutput == NULL;
}
// AudioRouting
virtual status_t setPreferredDevice(jobject device);
virtual jobject getRoutedDevice();
virtual status_t addAudioDeviceCallback(jobject routingDelegate);
virtual status_t removeAudioDeviceCallback(jobject listener);
private:
static void setMinBufferCount();
static void CallbackWrapper(int event, void *me, void *info);
void deleteRecycledTrack_l();
void close_l();
status_t updateTrack_l();
sp<JAudioTrack> mJAudioTrack;
AudioCallback mCallback;
void * mCallbackCookie;
CallbackData * mCallbackData;
sp<JObjectHolder> mAttributes;
float mVolume;
AudioPlaybackRate mPlaybackRate;
uint32_t mSampleRateHz; // sample rate of the content, as set in open()
float mMsecsPerFrame;
size_t mFrameSize;
int32_t mSessionId;
uid_t mUid;
int mPid;
float mSendLevel;
int mAuxEffectId;
audio_output_flags_t mFlags;
sp<JObjectHolder> mPreferredDevice;
mutable Mutex mLock;
// <listener, routingDelegate>
Vector<std::pair<sp<JObjectHolder>, sp<JObjectHolder>>> mRoutingDelegates;
// static variables below not protected by mutex
static bool mIsOnEmulator;
static int mMinBufferCount; // 12 for emulator; otherwise 4
// CallbackData is what is passed to the AudioTrack as the "user" data.
// We need to be able to target this to a different Output on the fly,
// so we can't use the Output itself for this.
class CallbackData {
friend MediaPlayer2AudioOutput;
public:
explicit CallbackData(MediaPlayer2AudioOutput *cookie) {
mData = cookie;
mSwitching = false;
}
MediaPlayer2AudioOutput *getOutput() const {
return mData;
}
void setOutput(MediaPlayer2AudioOutput* newcookie) {
mData = newcookie;
}
// lock/unlock are used by the callback before accessing the payload of this object
void lock() const {
mLock.lock();
}
void unlock() const {
mLock.unlock();
}
// tryBeginTrackSwitch/endTrackSwitch are used when the CallbackData is handed over
// to the next sink.
// tryBeginTrackSwitch() returns true only if it obtains the lock.
bool tryBeginTrackSwitch() {
LOG_ALWAYS_FATAL_IF(mSwitching, "tryBeginTrackSwitch() already called");
if (mLock.tryLock() != OK) {
return false;
}
mSwitching = true;
return true;
}
void endTrackSwitch() {
if (mSwitching) {
mLock.unlock();
}
mSwitching = false;
}
private:
MediaPlayer2AudioOutput *mData;
mutable Mutex mLock; // a recursive mutex might make this unnecessary.
bool mSwitching;
DISALLOW_EVIL_CONSTRUCTORS(CallbackData);
};
};
}; // namespace android
#endif // ANDROID_MEDIAPLAYER2AUDIOOUTPUT_H