diff options
author | Marco Nelissen <marcone@google.com> | 2019-10-18 11:12:30 -0700 |
---|---|---|
committer | Marco Nelissen <marcone@google.com> | 2019-10-18 11:27:49 -0700 |
commit | 1edb68c38408d06bf8f97d6884663732b7dbcce2 (patch) | |
tree | 87159f2617c58f9cb971ab48c8512d5ad14da2df /cmds | |
parent | 03379fb2cc34c93214082a0ceaf038626117554d (diff) |
Move AudioPlayer to test commands
AudioPlayer was only used by the commandline utilities, so move it
out of libstagefright.
Test: build, run
Change-Id: I561cccd323206de7415bd235b72711194080aaea
Diffstat (limited to 'cmds')
-rw-r--r-- | cmds/stagefright/Android.mk | 13 | ||||
-rw-r--r-- | cmds/stagefright/AudioPlayer.cpp | 689 | ||||
-rw-r--r-- | cmds/stagefright/AudioPlayer.h | 125 | ||||
-rw-r--r-- | cmds/stagefright/audioloop.cpp | 2 | ||||
-rw-r--r-- | cmds/stagefright/record.cpp | 3 | ||||
-rw-r--r-- | cmds/stagefright/recordvideo.cpp | 4 | ||||
-rw-r--r-- | cmds/stagefright/stagefright.cpp | 3 |
7 files changed, 828 insertions, 11 deletions
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk index cc43b61612..defc94f8fd 100644 --- a/cmds/stagefright/Android.mk +++ b/cmds/stagefright/Android.mk @@ -3,6 +3,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + AudioPlayer.cpp \ stagefright.cpp \ jpeg.cpp \ SineSource.cpp @@ -10,7 +11,7 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libstagefright libmedia libmedia_codeclist libutils libbinder \ libstagefright_foundation libjpeg libui libgui libcutils liblog \ - libhidlbase libdatasource \ + libhidlbase libdatasource libaudioclient \ android.hardware.media.omx@1.0 \ LOCAL_C_INCLUDES:= \ @@ -31,12 +32,13 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + AudioPlayer.cpp \ SineSource.cpp \ record.cpp LOCAL_SHARED_LIBRARIES := \ libstagefright libmedia liblog libutils libbinder \ - libstagefright_foundation libdatasource + libstagefright_foundation libdatasource libaudioclient LOCAL_C_INCLUDES:= \ frameworks/av/camera/include \ @@ -57,12 +59,12 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - SineSource.cpp \ + AudioPlayer.cpp \ recordvideo.cpp LOCAL_SHARED_LIBRARIES := \ libstagefright libmedia liblog libutils libbinder \ - libstagefright_foundation + libstagefright_foundation libaudioclient LOCAL_C_INCLUDES:= \ frameworks/av/media/libstagefright \ @@ -83,12 +85,13 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + AudioPlayer.cpp \ SineSource.cpp \ audioloop.cpp LOCAL_SHARED_LIBRARIES := \ libstagefright libmedia liblog libutils libbinder \ - libstagefright_foundation + libstagefright_foundation libaudioclient LOCAL_C_INCLUDES:= \ frameworks/av/media/libstagefright \ diff --git a/cmds/stagefright/AudioPlayer.cpp b/cmds/stagefright/AudioPlayer.cpp new file mode 100644 index 0000000000..208713d950 --- /dev/null +++ b/cmds/stagefright/AudioPlayer.cpp @@ -0,0 +1,689 @@ +/* + * Copyright (C) 2009 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 <inttypes.h> + +//#define LOG_NDEBUG 0 +#define LOG_TAG "AudioPlayer" +#include <utils/Log.h> +#include <cutils/compiler.h> + +#include <binder/IPCThreadState.h> +#include <media/AudioTrack.h> +#include <media/MediaSource.h> +#include <media/openmax/OMX_Audio.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/ALookup.h> +#include <media/stagefright/foundation/ALooper.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/Utils.h> + +#include "AudioPlayer.h" + +namespace android { + +AudioPlayer::AudioPlayer( + const sp<MediaPlayerBase::AudioSink> &audioSink, + uint32_t flags) + : mInputBuffer(NULL), + mSampleRate(0), + mLatencyUs(0), + mFrameSize(0), + mNumFramesPlayed(0), + mNumFramesPlayedSysTimeUs(ALooper::GetNowUs()), + mPositionTimeMediaUs(-1), + mPositionTimeRealUs(-1), + mSeeking(false), + mReachedEOS(false), + mFinalStatus(OK), + mSeekTimeUs(0), + mStarted(false), + mIsFirstBuffer(false), + mFirstBufferResult(OK), + mFirstBuffer(NULL), + mAudioSink(audioSink), + mPlaying(false), + mStartPosUs(0), + mCreateFlags(flags) { +} + +AudioPlayer::~AudioPlayer() { + if (mStarted) { + reset(); + } +} + +void AudioPlayer::setSource(const sp<MediaSource> &source) { + CHECK(mSource == NULL); + mSource = source; +} + +ALookup<audio_format_t, int32_t> sAudioFormatToPcmEncoding { + { + { AUDIO_FORMAT_PCM_16_BIT, kAudioEncodingPcm16bit }, + { AUDIO_FORMAT_PCM_8_BIT, kAudioEncodingPcm8bit }, + { AUDIO_FORMAT_PCM_FLOAT, kAudioEncodingPcmFloat }, + } +}; + +status_t AudioPlayer::start(bool sourceAlreadyStarted) { + CHECK(!mStarted); + CHECK(mSource != NULL); + + status_t err; + if (!sourceAlreadyStarted) { + err = mSource->start(); + + if (err != OK) { + return err; + } + } + + // We allow an optional INFO_FORMAT_CHANGED at the very beginning + // of playback, if there is one, getFormat below will retrieve the + // updated format, if there isn't, we'll stash away the valid buffer + // of data to be used on the first audio callback. + + CHECK(mFirstBuffer == NULL); + + MediaSource::ReadOptions options; + if (mSeeking) { + options.setSeekTo(mSeekTimeUs); + mSeeking = false; + } + + mFirstBufferResult = mSource->read(&mFirstBuffer, &options); + if (mFirstBufferResult == INFO_FORMAT_CHANGED) { + ALOGV("INFO_FORMAT_CHANGED!!!"); + + CHECK(mFirstBuffer == NULL); + mFirstBufferResult = OK; + mIsFirstBuffer = false; + } else { + mIsFirstBuffer = true; + } + + sp<MetaData> format = mSource->getFormat(); + + if (format == NULL) { + ALOGE("No metadata b/118620871"); + android_errorWriteLog(0x534e4554, "118620871"); + return BAD_VALUE; + } + + const char *mime; + bool success = format->findCString(kKeyMIMEType, &mime); + CHECK(success); + CHECK(useOffload() || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)); + + success = format->findInt32(kKeySampleRate, &mSampleRate); + CHECK(success); + + int32_t numChannels, channelMask; + success = format->findInt32(kKeyChannelCount, &numChannels); + CHECK(success); + + if(!format->findInt32(kKeyChannelMask, &channelMask)) { + // log only when there's a risk of ambiguity of channel mask selection + ALOGI_IF(numChannels > 2, + "source format didn't specify channel mask, using (%d) channel order", numChannels); + channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER; + } + + audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT; + int32_t pcmEncoding; + if (format->findInt32(kKeyPcmEncoding, &pcmEncoding)) { + sAudioFormatToPcmEncoding.map(pcmEncoding, &audioFormat); + } + + if (useOffload()) { + if (mapMimeToAudioFormat(audioFormat, mime) != OK) { + ALOGE("Couldn't map mime type \"%s\" to a valid AudioSystem::audio_format", mime); + audioFormat = AUDIO_FORMAT_INVALID; + } else { + ALOGV("Mime type \"%s\" mapped to audio_format 0x%x", mime, audioFormat); + } + + int32_t aacaot = -1; + if ((audioFormat == AUDIO_FORMAT_AAC) && format->findInt32(kKeyAACAOT, &aacaot)) { + // Redefine AAC format corrosponding to aac profile + mapAACProfileToAudioFormat(audioFormat,(OMX_AUDIO_AACPROFILETYPE) aacaot); + } + } + + int avgBitRate = -1; + format->findInt32(kKeyBitRate, &avgBitRate); + + if (mAudioSink.get() != NULL) { + + uint32_t flags = AUDIO_OUTPUT_FLAG_NONE; + audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER; + + if (allowDeepBuffering()) { + flags |= AUDIO_OUTPUT_FLAG_DEEP_BUFFER; + } + if (useOffload()) { + flags |= AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; + + int64_t durationUs; + if (format->findInt64(kKeyDuration, &durationUs)) { + offloadInfo.duration_us = durationUs; + } else { + offloadInfo.duration_us = -1; + } + + offloadInfo.sample_rate = mSampleRate; + offloadInfo.channel_mask = channelMask; + offloadInfo.format = audioFormat; + offloadInfo.stream_type = AUDIO_STREAM_MUSIC; + offloadInfo.bit_rate = avgBitRate; + offloadInfo.has_video = ((mCreateFlags & HAS_VIDEO) != 0); + offloadInfo.is_streaming = ((mCreateFlags & IS_STREAMING) != 0); + } + + status_t err = mAudioSink->open( + mSampleRate, numChannels, channelMask, audioFormat, + DEFAULT_AUDIOSINK_BUFFERCOUNT, + &AudioPlayer::AudioSinkCallback, + this, + (audio_output_flags_t)flags, + useOffload() ? &offloadInfo : NULL); + + if (err == OK) { + mLatencyUs = (int64_t)mAudioSink->latency() * 1000; + mFrameSize = mAudioSink->frameSize(); + + if (useOffload()) { + // If the playback is offloaded to h/w we pass the + // HAL some metadata information + // We don't want to do this for PCM because it will be going + // through the AudioFlinger mixer before reaching the hardware + sendMetaDataToHal(mAudioSink, format); + } + + err = mAudioSink->start(); + // do not alter behavior for non offloaded tracks: ignore start status. + if (!useOffload()) { + err = OK; + } + } + + if (err != OK) { + if (mFirstBuffer != NULL) { + mFirstBuffer->release(); + mFirstBuffer = NULL; + } + + if (!sourceAlreadyStarted) { + mSource->stop(); + } + + return err; + } + + } else { + // playing to an AudioTrack, set up mask if necessary + audio_channel_mask_t audioMask = channelMask == CHANNEL_MASK_USE_CHANNEL_ORDER ? + audio_channel_out_mask_from_count(numChannels) : channelMask; + if (0 == audioMask) { + return BAD_VALUE; + } + + mAudioTrack = new AudioTrack( + AUDIO_STREAM_MUSIC, mSampleRate, AUDIO_FORMAT_PCM_16_BIT, audioMask, + 0 /*frameCount*/, AUDIO_OUTPUT_FLAG_NONE, &AudioCallback, this, + 0 /*notificationFrames*/); + + if ((err = mAudioTrack->initCheck()) != OK) { + mAudioTrack.clear(); + + if (mFirstBuffer != NULL) { + mFirstBuffer->release(); + mFirstBuffer = NULL; + } + + if (!sourceAlreadyStarted) { + mSource->stop(); + } + + return err; + } + + mLatencyUs = (int64_t)mAudioTrack->latency() * 1000; + mFrameSize = mAudioTrack->frameSize(); + + mAudioTrack->start(); + } + + mStarted = true; + mPlaying = true; + + return OK; +} + +void AudioPlayer::pause(bool playPendingSamples) { + CHECK(mStarted); + + if (playPendingSamples) { + if (mAudioSink.get() != NULL) { + mAudioSink->stop(); + } else { + mAudioTrack->stop(); + } + + mNumFramesPlayed = 0; + mNumFramesPlayedSysTimeUs = ALooper::GetNowUs(); + } else { + if (mAudioSink.get() != NULL) { + mAudioSink->pause(); + } else { + mAudioTrack->pause(); + } + } + + mPlaying = false; +} + +status_t AudioPlayer::resume() { + CHECK(mStarted); + status_t err; + + if (mAudioSink.get() != NULL) { + err = mAudioSink->start(); + } else { + err = mAudioTrack->start(); + } + + if (err == OK) { + mPlaying = true; + } + + return err; +} + +void AudioPlayer::reset() { + CHECK(mStarted); + + ALOGV("reset: mPlaying=%d mReachedEOS=%d useOffload=%d", + mPlaying, mReachedEOS, useOffload() ); + + if (mAudioSink.get() != NULL) { + mAudioSink->stop(); + // If we're closing and have reached EOS, we don't want to flush + // the track because if it is offloaded there could be a small + // amount of residual data in the hardware buffer which we must + // play to give gapless playback. + // But if we're resetting when paused or before we've reached EOS + // we can't be doing a gapless playback and there could be a large + // amount of data queued in the hardware if the track is offloaded, + // so we must flush to prevent a track switch being delayed playing + // the buffered data that we don't want now + if (!mPlaying || !mReachedEOS) { + mAudioSink->flush(); + } + + mAudioSink->close(); + } else { + mAudioTrack->stop(); + + if (!mPlaying || !mReachedEOS) { + mAudioTrack->flush(); + } + + mAudioTrack.clear(); + } + + // Make sure to release any buffer we hold onto so that the + // source is able to stop(). + + if (mFirstBuffer != NULL) { + mFirstBuffer->release(); + mFirstBuffer = NULL; + } + + if (mInputBuffer != NULL) { + ALOGV("AudioPlayer releasing input buffer."); + + mInputBuffer->release(); + mInputBuffer = NULL; + } + + mSource->stop(); + + // The following hack is necessary to ensure that the OMX + // component is completely released by the time we may try + // to instantiate it again. + // When offloading, the OMX component is not used so this hack + // is not needed + if (!useOffload()) { + wp<MediaSource> tmp = mSource; + mSource.clear(); + while (tmp.promote() != NULL) { + usleep(1000); + } + } else { + mSource.clear(); + } + IPCThreadState::self()->flushCommands(); + + mNumFramesPlayed = 0; + mNumFramesPlayedSysTimeUs = ALooper::GetNowUs(); + mPositionTimeMediaUs = -1; + mPositionTimeRealUs = -1; + mSeeking = false; + mSeekTimeUs = 0; + mReachedEOS = false; + mFinalStatus = OK; + mStarted = false; + mPlaying = false; + mStartPosUs = 0; +} + +// static +void AudioPlayer::AudioCallback(int event, void *user, void *info) { + static_cast<AudioPlayer *>(user)->AudioCallback(event, info); +} + +bool AudioPlayer::reachedEOS(status_t *finalStatus) { + *finalStatus = OK; + + Mutex::Autolock autoLock(mLock); + *finalStatus = mFinalStatus; + return mReachedEOS; +} + +status_t AudioPlayer::setPlaybackRate(const AudioPlaybackRate &rate) { + if (mAudioSink.get() != NULL) { + return mAudioSink->setPlaybackRate(rate); + } else if (mAudioTrack != 0){ + return mAudioTrack->setPlaybackRate(rate); + } else { + return NO_INIT; + } +} + +status_t AudioPlayer::getPlaybackRate(AudioPlaybackRate *rate /* nonnull */) { + if (mAudioSink.get() != NULL) { + return mAudioSink->getPlaybackRate(rate); + } else if (mAudioTrack != 0) { + *rate = mAudioTrack->getPlaybackRate(); + return OK; + } else { + return NO_INIT; + } +} + +// static +size_t AudioPlayer::AudioSinkCallback( + MediaPlayerBase::AudioSink * /* audioSink */, + void *buffer, size_t size, void *cookie, + MediaPlayerBase::AudioSink::cb_event_t event) { + AudioPlayer *me = (AudioPlayer *)cookie; + + switch(event) { + case MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER: + return me->fillBuffer(buffer, size); + + case MediaPlayerBase::AudioSink::CB_EVENT_STREAM_END: + ALOGV("AudioSinkCallback: stream end"); + me->mReachedEOS = true; + break; + + case MediaPlayerBase::AudioSink::CB_EVENT_TEAR_DOWN: + ALOGV("AudioSinkCallback: Tear down event"); + break; + } + + return 0; +} + +void AudioPlayer::AudioCallback(int event, void *info) { + switch (event) { + case AudioTrack::EVENT_MORE_DATA: + { + AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info; + size_t numBytesWritten = fillBuffer(buffer->raw, buffer->size); + buffer->size = numBytesWritten; + } + break; + + case AudioTrack::EVENT_STREAM_END: + mReachedEOS = true; + break; + } +} + +size_t AudioPlayer::fillBuffer(void *data, size_t size) { + if (mNumFramesPlayed == 0) { + ALOGV("AudioCallback"); + } + + if (mReachedEOS) { + return 0; + } + + size_t size_done = 0; + size_t size_remaining = size; + while (size_remaining > 0) { + MediaSource::ReadOptions options; + bool refreshSeekTime = false; + + { + Mutex::Autolock autoLock(mLock); + + if (mSeeking) { + if (mIsFirstBuffer) { + if (mFirstBuffer != NULL) { + mFirstBuffer->release(); + mFirstBuffer = NULL; + } + mIsFirstBuffer = false; + } + + options.setSeekTo(mSeekTimeUs); + refreshSeekTime = true; + + if (mInputBuffer != NULL) { + mInputBuffer->release(); + mInputBuffer = NULL; + } + + mSeeking = false; + } + } + + if (mInputBuffer == NULL) { + status_t err; + + if (mIsFirstBuffer) { + mInputBuffer = mFirstBuffer; + mFirstBuffer = NULL; + err = mFirstBufferResult; + + mIsFirstBuffer = false; + } else { + err = mSource->read(&mInputBuffer, &options); + } + + CHECK((err == OK && mInputBuffer != NULL) + || (err != OK && mInputBuffer == NULL)); + + Mutex::Autolock autoLock(mLock); + + if (err != OK) { + if (!mReachedEOS) { + if (useOffload()) { + // no more buffers to push - stop() and wait for STREAM_END + // don't set mReachedEOS until stream end received + if (mAudioSink != NULL) { + mAudioSink->stop(); + } else { + mAudioTrack->stop(); + } + } else { + mReachedEOS = true; + } + } + + mFinalStatus = err; + break; + } + + if (mAudioSink != NULL) { + mLatencyUs = (int64_t)mAudioSink->latency() * 1000; + } else { + mLatencyUs = (int64_t)mAudioTrack->latency() * 1000; + } + + if(mInputBuffer->range_length() != 0) { + CHECK(mInputBuffer->meta_data().findInt64( + kKeyTime, &mPositionTimeMediaUs)); + } + + // need to adjust the mStartPosUs for offload decoding since parser + // might not be able to get the exact seek time requested. + if (refreshSeekTime) { + if (useOffload()) { + mStartPosUs = mPositionTimeMediaUs; + ALOGV("adjust seek time to: %.2f", mStartPosUs/ 1E6); + } + // clear seek time with mLock locked and once we have valid mPositionTimeMediaUs + // and mPositionTimeRealUs + // before clearing mSeekTimeUs check if a new seek request has been received while + // we were reading from the source with mLock released. + if (!mSeeking) { + mSeekTimeUs = 0; + } + } + + if (!useOffload()) { + mPositionTimeRealUs = + ((mNumFramesPlayed + size_done / mFrameSize) * 1000000) + / mSampleRate; + ALOGV("buffer->size() = %zu, " + "mPositionTimeMediaUs=%.2f mPositionTimeRealUs=%.2f", + mInputBuffer->range_length(), + mPositionTimeMediaUs / 1E6, mPositionTimeRealUs / 1E6); + } + + } + + if (mInputBuffer->range_length() == 0) { + mInputBuffer->release(); + mInputBuffer = NULL; + + continue; + } + + size_t copy = size_remaining; + if (copy > mInputBuffer->range_length()) { + copy = mInputBuffer->range_length(); + } + + memcpy((char *)data + size_done, + (const char *)mInputBuffer->data() + mInputBuffer->range_offset(), + copy); + + mInputBuffer->set_range(mInputBuffer->range_offset() + copy, + mInputBuffer->range_length() - copy); + + size_done += copy; + size_remaining -= copy; + } + + if (useOffload()) { + // We must ask the hardware what it has played + mPositionTimeRealUs = getOutputPlayPositionUs_l(); + ALOGV("mPositionTimeMediaUs=%.2f mPositionTimeRealUs=%.2f", + mPositionTimeMediaUs / 1E6, mPositionTimeRealUs / 1E6); + } + + { + Mutex::Autolock autoLock(mLock); + mNumFramesPlayed += size_done / mFrameSize; + mNumFramesPlayedSysTimeUs = ALooper::GetNowUs(); + } + + return size_done; +} + +int64_t AudioPlayer::getOutputPlayPositionUs_l() +{ + uint32_t playedSamples = 0; + uint32_t sampleRate; + if (mAudioSink != NULL) { + mAudioSink->getPosition(&playedSamples); + sampleRate = mAudioSink->getSampleRate(); + } else { + mAudioTrack->getPosition(&playedSamples); + sampleRate = mAudioTrack->getSampleRate(); + } + if (sampleRate != 0) { + mSampleRate = sampleRate; + } + + int64_t playedUs; + if (mSampleRate != 0) { + playedUs = (static_cast<int64_t>(playedSamples) * 1000000 ) / mSampleRate; + } else { + playedUs = 0; + } + + // HAL position is relative to the first buffer we sent at mStartPosUs + const int64_t renderedDuration = mStartPosUs + playedUs; + ALOGV("getOutputPlayPositionUs_l %" PRId64, renderedDuration); + return renderedDuration; +} + +status_t AudioPlayer::seekTo(int64_t time_us) { + Mutex::Autolock autoLock(mLock); + + ALOGV("seekTo( %" PRId64 " )", time_us); + + mSeeking = true; + mPositionTimeRealUs = mPositionTimeMediaUs = -1; + mReachedEOS = false; + mSeekTimeUs = time_us; + mStartPosUs = time_us; + + // Flush resets the number of played frames + mNumFramesPlayed = 0; + mNumFramesPlayedSysTimeUs = ALooper::GetNowUs(); + + if (mAudioSink != NULL) { + if (mPlaying) { + mAudioSink->pause(); + } + mAudioSink->flush(); + if (mPlaying) { + mAudioSink->start(); + } + } else { + if (mPlaying) { + mAudioTrack->pause(); + } + mAudioTrack->flush(); + if (mPlaying) { + mAudioTrack->start(); + } + } + + return OK; +} + +} diff --git a/cmds/stagefright/AudioPlayer.h b/cmds/stagefright/AudioPlayer.h new file mode 100644 index 0000000000..7c2c36fc0e --- /dev/null +++ b/cmds/stagefright/AudioPlayer.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2009 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 AUDIO_PLAYER_H_ + +#define AUDIO_PLAYER_H_ + +#include <media/MediaSource.h> +#include <media/MediaPlayerInterface.h> +#include <media/stagefright/MediaBuffer.h> +#include <utils/threads.h> + +namespace android { + +struct AudioPlaybackRate; +class AudioTrack; +struct AwesomePlayer; + +class AudioPlayer { +public: + enum { + REACHED_EOS, + SEEK_COMPLETE + }; + + enum { + ALLOW_DEEP_BUFFERING = 0x01, + USE_OFFLOAD = 0x02, + HAS_VIDEO = 0x1000, + IS_STREAMING = 0x2000 + + }; + + AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink, + uint32_t flags = 0); + + virtual ~AudioPlayer(); + + // Caller retains ownership of "source". + void setSource(const sp<MediaSource> &source); + + status_t start(bool sourceAlreadyStarted = false); + + void pause(bool playPendingSamples = false); + status_t resume(); + + status_t seekTo(int64_t time_us); + + bool isSeeking(); + bool reachedEOS(status_t *finalStatus); + + status_t setPlaybackRate(const AudioPlaybackRate &rate); + status_t getPlaybackRate(AudioPlaybackRate *rate /* nonnull */); + +private: + sp<MediaSource> mSource; + sp<AudioTrack> mAudioTrack; + + MediaBufferBase *mInputBuffer; + + int mSampleRate; + int64_t mLatencyUs; + size_t mFrameSize; + + Mutex mLock; + int64_t mNumFramesPlayed; + int64_t mNumFramesPlayedSysTimeUs; + + int64_t mPositionTimeMediaUs; + int64_t mPositionTimeRealUs; + + bool mSeeking; + bool mReachedEOS; + status_t mFinalStatus; + int64_t mSeekTimeUs; + + bool mStarted; + + bool mIsFirstBuffer; + status_t mFirstBufferResult; + MediaBufferBase *mFirstBuffer; + + sp<MediaPlayerBase::AudioSink> mAudioSink; + + bool mPlaying; + int64_t mStartPosUs; + const uint32_t mCreateFlags; + + static void AudioCallback(int event, void *user, void *info); + void AudioCallback(int event, void *info); + + static size_t AudioSinkCallback( + MediaPlayerBase::AudioSink *audioSink, + void *data, size_t size, void *me, + MediaPlayerBase::AudioSink::cb_event_t event); + + size_t fillBuffer(void *data, size_t size); + + void reset(); + + int64_t getOutputPlayPositionUs_l(); + + bool allowDeepBuffering() const { return (mCreateFlags & ALLOW_DEEP_BUFFERING) != 0; } + bool useOffload() const { return (mCreateFlags & USE_OFFLOAD) != 0; } + + AudioPlayer(const AudioPlayer &); + AudioPlayer &operator=(const AudioPlayer &); +}; + +} // namespace android + +#endif // AUDIO_PLAYER_H_ diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp index d4f2e8d8d4..bd274d8f67 100644 --- a/cmds/stagefright/audioloop.cpp +++ b/cmds/stagefright/audioloop.cpp @@ -29,11 +29,11 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/AMRWriter.h> -#include <media/stagefright/AudioPlayer.h> #include <media/stagefright/AudioSource.h> #include <media/stagefright/MediaCodecSource.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/SimpleDecodingSource.h> +#include "AudioPlayer.h" #include "SineSource.h" using namespace android; diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp index 498237de8d..37091c43e1 100644 --- a/cmds/stagefright/record.cpp +++ b/cmds/stagefright/record.cpp @@ -21,7 +21,6 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/ALooper.h> #include <media/stagefright/foundation/AMessage.h> -#include <media/stagefright/AudioPlayer.h> #include <media/stagefright/CameraSource.h> #include <media/stagefright/MediaBufferGroup.h> #include <media/stagefright/MediaDefs.h> @@ -33,6 +32,8 @@ #include <media/stagefright/SimpleDecodingSource.h> #include <media/MediaPlayerInterface.h> +#include "AudioPlayer.h" + using namespace android; static const int32_t kAudioBitRate = 12200; diff --git a/cmds/stagefright/recordvideo.cpp b/cmds/stagefright/recordvideo.cpp index a63b9b9984..01a178e6a0 100644 --- a/cmds/stagefright/recordvideo.cpp +++ b/cmds/stagefright/recordvideo.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "SineSource.h" - #include <inttypes.h> #include <sys/types.h> #include <sys/stat.h> @@ -25,8 +23,8 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/ALooper.h> #include <media/stagefright/foundation/AMessage.h> -#include <media/stagefright/AudioPlayer.h> #include <media/stagefright/MediaBufferGroup.h> +#include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaCodecSource.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MetaData.h> diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index d52541d2df..a1ee90483f 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -40,7 +40,6 @@ #include <media/stagefright/foundation/ALooper.h> #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/foundation/AUtils.h> -#include <media/stagefright/AudioPlayer.h> #include <media/stagefright/JPEGSource.h> #include <media/stagefright/InterfaceUtils.h> #include <media/stagefright/MediaCodec.h> @@ -67,6 +66,8 @@ #include <android/hardware/media/omx/1.0/IOmx.h> +#include "AudioPlayer.h" + using namespace android; static long gNumRepetitions; |