From 2729ea9262ca60d93047e984739887cfc89e82eb Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 21 Oct 2008 07:00:00 -0700 Subject: Initial Contribution --- include/media/AudioRecord.h | 235 +++++ include/media/AudioSystem.h | 122 +++ include/media/AudioTrack.h | 277 +++++ include/media/IAudioFlinger.h | 124 +++ include/media/IAudioRecord.h | 68 ++ include/media/IAudioTrack.h | 84 ++ include/media/IMediaPlayer.h | 65 ++ include/media/IMediaPlayerClient.h | 48 + include/media/IMediaPlayerService.h | 54 + include/media/MediaPlayerInterface.h | 156 +++ include/media/PVPlayer.h | 75 ++ include/media/ToneGenerator.h | 175 +++ include/media/mediametadataretriever.h | 101 ++ include/media/mediaplayer.h | 138 +++ include/media/mediarecorder.h | 120 +++ include/media/mediascanner.h | 63 ++ include/media/thread_init.h | 25 + include/private/media/AudioTrackShared.h | 64 ++ include/private/opengles/gl_context.h | 624 +++++++++++ include/private/ui/LayerState.h | 75 ++ include/private/ui/SharedState.h | 168 +++ include/private/ui/SurfaceFlingerSynchro.h | 76 ++ include/private/utils/Static.h | 58 + include/private/utils/binder_module.h | 148 +++ include/private/utils/futex_synchro.h | 60 ++ media/libmedia/Android.mk | 34 + media/libmedia/AudioRecord.cpp | 397 +++++++ media/libmedia/AudioSystem.cpp | 255 +++++ media/libmedia/AudioTrack.cpp | 596 +++++++++++ media/libmedia/IAudioFlinger.cpp | 450 ++++++++ media/libmedia/IAudioRecord.cpp | 100 ++ media/libmedia/IAudioTrack.cpp | 140 +++ media/libmedia/IMediaPlayer.cpp | 295 ++++++ media/libmedia/IMediaPlayerClient.cpp | 77 ++ media/libmedia/IMediaPlayerService.cpp | 157 +++ media/libmedia/MODULE_LICENSE_APACHE2 | 0 media/libmedia/NOTICE | 190 ++++ media/libmedia/ToneGenerator.cpp | 662 ++++++++++++ media/libmedia/mediametadataretriever.cpp | 182 ++++ media/libmedia/mediaplayer.cpp | 582 ++++++++++ media/libmediaplayerservice/Android.mk | 33 + media/libmediaplayerservice/MediaPlayerService.cpp | 1112 ++++++++++++++++++++ media/libmediaplayerservice/MediaPlayerService.h | 222 ++++ media/libmediaplayerservice/MidiFile.cpp | 572 ++++++++++ media/libmediaplayerservice/MidiFile.h | 79 ++ media/libmediaplayerservice/VorbisPlayer.cpp | 527 ++++++++++ media/libmediaplayerservice/VorbisPlayer.h | 91 ++ media/mediaserver/Android.mk | 22 + media/mediaserver/main_mediaserver.cpp | 45 + 49 files changed, 10023 insertions(+) create mode 100644 include/media/AudioRecord.h create mode 100644 include/media/AudioSystem.h create mode 100644 include/media/AudioTrack.h create mode 100644 include/media/IAudioFlinger.h create mode 100644 include/media/IAudioRecord.h create mode 100644 include/media/IAudioTrack.h create mode 100644 include/media/IMediaPlayer.h create mode 100644 include/media/IMediaPlayerClient.h create mode 100644 include/media/IMediaPlayerService.h create mode 100644 include/media/MediaPlayerInterface.h create mode 100644 include/media/PVPlayer.h create mode 100644 include/media/ToneGenerator.h create mode 100644 include/media/mediametadataretriever.h create mode 100644 include/media/mediaplayer.h create mode 100644 include/media/mediarecorder.h create mode 100644 include/media/mediascanner.h create mode 100644 include/media/thread_init.h create mode 100644 include/private/media/AudioTrackShared.h create mode 100644 include/private/opengles/gl_context.h create mode 100644 include/private/ui/LayerState.h create mode 100644 include/private/ui/SharedState.h create mode 100644 include/private/ui/SurfaceFlingerSynchro.h create mode 100644 include/private/utils/Static.h create mode 100644 include/private/utils/binder_module.h create mode 100644 include/private/utils/futex_synchro.h create mode 100644 media/libmedia/Android.mk create mode 100644 media/libmedia/AudioRecord.cpp create mode 100644 media/libmedia/AudioSystem.cpp create mode 100644 media/libmedia/AudioTrack.cpp create mode 100644 media/libmedia/IAudioFlinger.cpp create mode 100644 media/libmedia/IAudioRecord.cpp create mode 100644 media/libmedia/IAudioTrack.cpp create mode 100644 media/libmedia/IMediaPlayer.cpp create mode 100644 media/libmedia/IMediaPlayerClient.cpp create mode 100644 media/libmedia/IMediaPlayerService.cpp create mode 100644 media/libmedia/MODULE_LICENSE_APACHE2 create mode 100644 media/libmedia/NOTICE create mode 100644 media/libmedia/ToneGenerator.cpp create mode 100644 media/libmedia/mediametadataretriever.cpp create mode 100644 media/libmedia/mediaplayer.cpp create mode 100644 media/libmediaplayerservice/Android.mk create mode 100644 media/libmediaplayerservice/MediaPlayerService.cpp create mode 100644 media/libmediaplayerservice/MediaPlayerService.h create mode 100644 media/libmediaplayerservice/MidiFile.cpp create mode 100644 media/libmediaplayerservice/MidiFile.h create mode 100644 media/libmediaplayerservice/VorbisPlayer.cpp create mode 100644 media/libmediaplayerservice/VorbisPlayer.h create mode 100644 media/mediaserver/Android.mk create mode 100644 media/mediaserver/main_mediaserver.cpp diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h new file mode 100644 index 0000000..008569f --- /dev/null +++ b/include/media/AudioRecord.h @@ -0,0 +1,235 @@ +/* + * 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. + */ + +#ifndef AUDIORECORD_H_ +#define AUDIORECORD_H_ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + + +namespace android { + +// ---------------------------------------------------------------------------- + +class AudioRecord +{ +public: + + enum stream_type { + DEFAULT_INPUT =-1, + MIC_INPUT = 0, + NUM_STREAM_TYPES + }; + + static const int DEFAULT_SAMPLE_RATE = 8000; + + /* Create Buffer on the stack and pass it to obtainBuffer() + * and releaseBuffer(). + */ + + class Buffer + { + public: + enum { + MUTE = 0x00000001 + }; + uint32_t flags; + int channelCount; + int format; + size_t frameCount; + size_t size; + union { + void* raw; + short* i16; + int8_t* i8; + }; + }; + + /* These are static methods to control the system-wide AudioFlinger + * only privileged processes can have access to them + */ + +// static status_t setMasterMute(bool mute); + + /* Returns AudioFlinger's frame count. AudioRecord's buffers will + * be created with this size. + */ + static size_t frameCount(); + + /* As a convenience, if a callback is supplied, a handler thread + * is automatically created with the appropriate priority. This thread + * invokes the callback when a new buffer becomes availlable. + */ + typedef bool (*callback_t)(void* user, const Buffer& info); + + /* Constructs an uninitialized AudioRecord. No connection with + * AudioFlinger takes place. + */ + AudioRecord(); + + /* Creates an AudioRecord track and registers it with AudioFlinger. + * Once created, the track needs to be started before it can be used. + * Unspecified values are set to the audio hardware's current + * values. + */ + + AudioRecord(int streamType = 0, + uint32_t sampleRate = 0, + int format = 0, + int channelCount = 0, + int bufferCount = 0, + uint32_t flags = 0, + callback_t cbf = 0, void* user = 0); + + + /* Terminates the AudioRecord and unregisters it from AudioFlinger. + * Also destroys all resources assotiated with the AudioRecord. + */ + ~AudioRecord(); + + + /* Initialize an uninitialized AudioRecord. */ + status_t set(int streamType = 0, + uint32_t sampleRate = 0, + int format = 0, + int channelCount = 0, + int bufferCount = 0, + uint32_t flags = 0, + callback_t cbf = 0, void* user = 0); + + + /* Result of constructing the AudioRecord. This must be checked + * before using any AudioRecord API (except for set()), using + * an uninitialized AudioRecord prduces undefined results. + */ + status_t initCheck() const; + + /* Returns this track's latency in nanoseconds or framecount. + * This only includes the latency due to the fill buffer size. + * In particular, the hardware or driver latencies are not accounted. + */ + nsecs_t latency() const; + + /* getters, see constructor */ + + uint32_t sampleRate() const; + int format() const; + int channelCount() const; + int bufferCount() const; + + + /* After it's created the track is not active. Call start() to + * make it active. If set, the callback will start being called. + */ + status_t start(); + + /* Stop a track. If set, the callback will cease being called and + * obtainBuffer returns STOPPED. Note that obtainBuffer() still works + * and will fill up buffers until the pool is exhausted. + */ + status_t stop(); + bool stopped() const; + + /* get sample rate for this track + */ + uint32_t getSampleRate(); + + /* obtains a buffer of "frameCount" frames. The buffer must be + * filled entirely. If the track is stopped, obtainBuffer() returns + * STOPPED instead of NO_ERROR as long as there are buffers availlable, + * at which point NO_MORE_BUFFERS is returned. + * Buffers will be returned until the pool (buffercount()) + * is exhausted, at which point obtainBuffer() will either block + * or return WOULD_BLOCK depending on the value of the "blocking" + * parameter. + */ + + enum { + NO_MORE_BUFFERS = 0x80000001, + STOPPED = 1 + }; + + status_t obtainBuffer(Buffer* audioBuffer, bool blocking); + void releaseBuffer(Buffer* audioBuffer); + + + /* As a convenience we provide a read() interface to the audio buffer. + * This is implemented on top of lockBuffer/unlockBuffer. + */ + ssize_t read(void* buffer, size_t size); + +private: + /* copying audio tracks is not allowed */ + AudioRecord(const AudioRecord& other); + AudioRecord& operator = (const AudioRecord& other); + + /* a small internal class to handle the callback */ + class ClientRecordThread : public Thread + { + public: + ClientRecordThread(AudioRecord& receiver); + private: + friend class AudioRecord; + virtual bool threadLoop(); + virtual status_t readyToRun() { return NO_ERROR; } + virtual void onFirstRef() {} + AudioRecord& mReceiver; + }; + + bool processAudioBuffer(const sp& thread); + + sp mAudioFlinger; + sp mAudioRecord; + sp mCblkMemory; + sp mClientRecordThread; + Mutex mRecordThreadLock; + + uint32_t mSampleRate; + size_t mFrameCount; + + audio_track_cblk_t* mCblk; + uint8_t mFormat; + uint8_t mBufferCount; + uint8_t mChannelCount : 4; + uint8_t mReserved : 3; + status_t mStatus; + nsecs_t mLatency; + + volatile int32_t mActive; + + callback_t mCbf; + void* mUserData; + + AudioRecord::Buffer mAudioBuffer; + size_t mPosition; + + uint32_t mReservedFBC[4]; +}; + +}; // namespace android + +#endif /*AUDIORECORD_H_*/ diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h new file mode 100644 index 0000000..9fcbea5 --- /dev/null +++ b/include/media/AudioSystem.h @@ -0,0 +1,122 @@ +/* + * 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. + */ + +#ifndef ANDROID_AUDIOSYSTEM_H_ +#define ANDROID_AUDIOSYSTEM_H_ + +#include +#include +#include + +namespace android { + +typedef void (*audio_error_callback)(status_t err); + +class AudioSystem +{ +public: + + enum audio_format { + DEFAULT = 0, + PCM_16_BIT, + PCM_8_BIT, + INVALID_FORMAT + }; + + enum audio_mode { + MODE_INVALID = -2, + MODE_CURRENT = -1, + MODE_NORMAL = 0, + MODE_RINGTONE, + MODE_IN_CALL, + NUM_MODES // not a valid entry, denotes end-of-list + }; + + enum audio_routes { + ROUTE_EARPIECE = (1 << 0), + ROUTE_SPEAKER = (1 << 1), + ROUTE_BLUETOOTH = (1 << 2), + ROUTE_HEADSET = (1 << 3), + ROUTE_ALL = (ROUTE_EARPIECE | ROUTE_SPEAKER | ROUTE_BLUETOOTH | ROUTE_HEADSET) + }; + + /* These are static methods to control the system-wide AudioFlinger + * only privileged processes can have access to them + */ + + // routing helper functions + static status_t speakerphone(bool state); + static status_t isSpeakerphoneOn(bool* state); + static status_t bluetoothSco(bool state); + static status_t isBluetoothScoOn(bool* state); + static status_t muteMicrophone(bool state); + static status_t isMicrophoneMuted(bool *state); + + static status_t setMasterVolume(float value); + static status_t setMasterMute(bool mute); + static status_t getMasterVolume(float* volume); + static status_t getMasterMute(bool* mute); + + static status_t setStreamVolume(int stream, float value); + static status_t setStreamMute(int stream, bool mute); + static status_t getStreamVolume(int stream, float* volume); + static status_t getStreamMute(int stream, bool* mute); + + static status_t setMode(int mode); + static status_t getMode(int* mode); + + static status_t setRouting(int mode, uint32_t routes, uint32_t mask); + static status_t getRouting(int mode, uint32_t* routes); + + static status_t isMusicActive(bool *state); + + // Temporary interface, do not use + // TODO: Replace with a more generic key:value get/set mechanism + static status_t setParameter(const char* key, const char* value); + + static void setErrorCallback(audio_error_callback cb); + + // helper function to obtain AudioFlinger service handle + static const sp& get_audio_flinger(); + + static float linearToLog(int volume); + static int logToLinear(float volume); + + // ---------------------------------------------------------------------------- + +private: + + class DeathNotifier: public IBinder::DeathRecipient + { + public: + DeathNotifier() { + } + + virtual void binderDied(const wp& who); + }; + + static sp gDeathNotifier; + + friend class DeathNotifier; + + static Mutex gLock; + static sp gAudioFlinger; + static audio_error_callback gAudioErrorCallback; +}; + +}; // namespace android + +#endif /*ANDROID_AUDIOSYSTEM_H_*/ diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h new file mode 100644 index 0000000..a89d7ff --- /dev/null +++ b/include/media/AudioTrack.h @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2007 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_AUDIOTRACK_H +#define ANDROID_AUDIOTRACK_H + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + + +namespace android { + +// ---------------------------------------------------------------------------- + +class audio_track_cblk_t; + +// ---------------------------------------------------------------------------- + +class AudioTrack +{ +public: + + enum stream_type { + DEFAULT =-1, + VOICE_CALL = 0, + SYSTEM = 1, + RING = 2, + MUSIC = 3, + ALARM = 4, + NUM_STREAM_TYPES + }; + + enum channel_index { + MONO = 0, + LEFT = 0, + RIGHT = 1 + }; + + /* Create Buffer on the stack and pass it to obtainBuffer() + * and releaseBuffer(). + */ + + class Buffer + { + public: + enum { + MUTE = 0x00000001 + }; + uint32_t flags; + int channelCount; + int format; + size_t frameCount; + size_t size; + union { + void* raw; + short* i16; + int8_t* i8; + }; + }; + + /* Returns AudioFlinger's frame count. AudioTrack's buffers will + * be created with this size. + */ + static size_t frameCount(); + + /* As a convenience, if a callback is supplied, a handler thread + * is automatically created with the appropriate priority. This thread + * invokes the callback when a new buffer becomes availlable. + */ + typedef void (*callback_t)(void* user, const Buffer& info); + + /* Constructs an uninitialized AudioTrack. No connection with + * AudioFlinger takes place. + */ + AudioTrack(); + + /* Creates an audio track and registers it with AudioFlinger. + * Once created, the track needs to be started before it can be used. + * Unspecified values are set to the audio hardware's current + * values. + */ + + AudioTrack( int streamType, + uint32_t sampleRate = 0, + int format = 0, + int channelCount = 0, + int bufferCount = 0, + uint32_t flags = 0, + callback_t cbf = 0, void* user = 0); + + + /* Terminates the AudioTrack and unregisters it from AudioFlinger. + * Also destroys all resources assotiated with the AudioTrack. + */ + ~AudioTrack(); + + + /* Initialize an uninitialized AudioTrack. */ + status_t set(int streamType =-1, + uint32_t sampleRate = 0, + int format = 0, + int channelCount = 0, + int bufferCount = 0, + uint32_t flags = 0, + callback_t cbf = 0, void* user = 0); + + + /* Result of constructing the AudioTrack. This must be checked + * before using any AudioTrack API (except for set()), using + * an uninitialized AudoiTrack prduces undefined results. + */ + status_t initCheck() const; + + /* Returns this track's latency in nanoseconds or framecount. + * This only includes the latency due to the fill buffer size. + * In particular, the hardware or driver latencies are not accounted. + */ + nsecs_t latency() const; + + /* getters, see constructor */ + + int streamType() const; + uint32_t sampleRate() const; + int format() const; + int channelCount() const; + int bufferCount() const; + + + /* After it's created the track is not active. Call start() to + * make it active. If set, the callback will start being called. + */ + void start(); + + /* Stop a track. If set, the callback will cease being called and + * obtainBuffer returns STOPPED. Note that obtainBuffer() still works + * and will fill up buffers until the pool is exhausted. + */ + void stop(); + bool stopped() const; + + /* flush a stopped track. All pending buffers are discarded. + * This function has no effect if the track is not stoped. + */ + void flush(); + + /* Pause a track. If set, the callback will cease being called and + * obtainBuffer returns STOPPED. Note that obtainBuffer() still works + * and will fill up buffers until the pool is exhausted. + */ + void pause(); + + /* mute or unmutes this track. + * While mutted, the callback, if set, is still called. + */ + void mute(bool); + bool muted() const; + + + /* set volume for this track, mostly used for games' sound effects + */ + void setVolume(float left, float right); + void getVolume(float* left, float* right); + + /* set sample rate for this track, mostly used for games' sound effects + */ + void setSampleRate(int sampleRate); + uint32_t getSampleRate(); + + /* obtains a buffer of "frameCount" frames. The buffer must be + * filled entirely. If the track is stopped, obtainBuffer() returns + * STOPPED instead of NO_ERROR as long as there are buffers availlable, + * at which point NO_MORE_BUFFERS is returned. + * Buffers will be returned until the pool (buffercount()) + * is exhausted, at which point obtainBuffer() will either block + * or return WOULD_BLOCK depending on the value of the "blocking" + * parameter. + */ + + enum { + NO_MORE_BUFFERS = 0x80000001, + STOPPED = 1 + }; + + status_t obtainBuffer(Buffer* audioBuffer, bool blocking); + void releaseBuffer(Buffer* audioBuffer); + + + /* As a convenience we provide a write() interface to the audio buffer. + * This is implemented on top of lockBuffer/unlockBuffer. For best + * performance + * + */ + ssize_t write(const void* buffer, size_t size); + + /* + * Dumps the state of an audio track. + */ + status_t dump(int fd, const Vector& args) const; + +private: + /* copying audio tracks is not allowed */ + AudioTrack(const AudioTrack& other); + AudioTrack& operator = (const AudioTrack& other); + + /* a small internal class to handle the callback */ + class AudioTrackThread : public Thread + { + public: + AudioTrackThread(AudioTrack& receiver); + private: + friend class AudioTrack; + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + AudioTrack& mReceiver; + Mutex mLock; + }; + + bool processAudioBuffer(const sp& thread); + + sp mAudioFlinger; + sp mAudioTrack; + sp mCblkMemory; + sp mAudioTrackThread; + + float mVolume[2]; + uint32_t mSampleRate; + size_t mFrameCount; + + audio_track_cblk_t* mCblk; + uint8_t mStreamType; + uint8_t mFormat; + uint8_t mBufferCount; + uint8_t mChannelCount : 4; + uint8_t mMuted : 1; + uint8_t mReserved : 2; + status_t mStatus; + nsecs_t mLatency; + + volatile int32_t mActive; + + callback_t mCbf; + void* mUserData; + + AudioTrack::Buffer mAudioBuffer; + size_t mPosition; + + uint32_t mReservedFBC[4]; +}; + + +}; // namespace android + +#endif // ANDROID_AUDIOTRACK_H diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h new file mode 100644 index 0000000..fa8e121 --- /dev/null +++ b/include/media/IAudioFlinger.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2007 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_IAUDIOFLINGER_H +#define ANDROID_IAUDIOFLINGER_H + +#include +#include +#include + +#include +#include +#include +#include +#include + + +namespace android { + +// ---------------------------------------------------------------------------- + +class IAudioFlinger : public IInterface +{ +public: + DECLARE_META_INTERFACE(AudioFlinger); + + /* create an audio track and registers it with AudioFlinger. + * return null if the track cannot be created. + */ + virtual sp createTrack( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags) = 0; + + virtual sp openRecord( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags) = 0; + + /* query the audio hardware state. This state never changes, + * and therefore can be cached. + */ + virtual uint32_t sampleRate() const = 0; + virtual int channelCount() const = 0; + virtual int format() const = 0; + virtual size_t frameCount() const = 0; + + /* set/get the audio hardware state. This will probably be used by + * the preference panel, mostly. + */ + virtual status_t setMasterVolume(float value) = 0; + virtual status_t setMasterMute(bool muted) = 0; + + virtual float masterVolume() const = 0; + virtual bool masterMute() const = 0; + + /* set/get stream type state. This will probably be used by + * the preference panel, mostly. + */ + virtual status_t setStreamVolume(int stream, float value) = 0; + virtual status_t setStreamMute(int stream, bool muted) = 0; + + virtual float streamVolume(int stream) const = 0; + virtual bool streamMute(int stream) const = 0; + + // set/get audio routing + virtual status_t setRouting(int mode, uint32_t routes, uint32_t mask) = 0; + virtual uint32_t getRouting(int mode) const = 0; + + // set/get audio mode + virtual status_t setMode(int mode) = 0; + virtual int getMode() const = 0; + + // mic mute/state + virtual status_t setMicMute(bool state) = 0; + virtual bool getMicMute() const = 0; + + // is a music stream active? + virtual bool isMusicActive() const = 0; + + // pass a generic configuration parameter to libaudio + // Temporary interface, do not use + // TODO: Replace with a more generic key:value get/set mechanism + virtual status_t setParameter(const char* key, const char* value) = 0; +}; + + +// ---------------------------------------------------------------------------- + +class BnAudioFlinger : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IAUDIOFLINGER_H diff --git a/include/media/IAudioRecord.h b/include/media/IAudioRecord.h new file mode 100644 index 0000000..9d45d2d --- /dev/null +++ b/include/media/IAudioRecord.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2007 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 IAUDIORECORD_H_ +#define IAUDIORECORD_H_ + +#include +#include + +#include +#include +#include +#include + + +namespace android { + +// ---------------------------------------------------------------------------- + +class IAudioRecord : public IInterface +{ +public: + DECLARE_META_INTERFACE(AudioRecord); + + /* After it's created the track is not active. Call start() to + * make it active. If set, the callback will start being called. + */ + virtual status_t start() = 0; + + /* Stop a track. If set, the callback will cease being called and + * obtainBuffer will return an error. Buffers that are already released + * will be processed, unless flush() is called. + */ + virtual void stop() = 0; + + /* get this tracks control block */ + virtual sp getCblk() const = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnAudioRecord : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif /*IAUDIORECORD_H_*/ diff --git a/include/media/IAudioTrack.h b/include/media/IAudioTrack.h new file mode 100644 index 0000000..12f2111 --- /dev/null +++ b/include/media/IAudioTrack.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2007 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_IAUDIOTRACK_H +#define ANDROID_IAUDIOTRACK_H + +#include +#include + +#include +#include +#include +#include + + +namespace android { + +// ---------------------------------------------------------------------------- + +class IAudioTrack : public IInterface +{ +public: + DECLARE_META_INTERFACE(AudioTrack); + + /* After it's created the track is not active. Call start() to + * make it active. If set, the callback will start being called. + */ + virtual status_t start() = 0; + + /* Stop a track. If set, the callback will cease being called and + * obtainBuffer will return an error. Buffers that are already released + * will be processed, unless flush() is called. + */ + virtual void stop() = 0; + + /* flush a stopped track. All pending buffers are discarded. + * This function has no effect if the track is not stoped. + */ + virtual void flush() = 0; + + /* mute or unmutes this track. + * While mutted, the callback, if set, is still called. + */ + virtual void mute(bool) = 0; + + /* Pause a track. If set, the callback will cease being called and + * obtainBuffer will return an error. Buffers that are already released + * will be processed, unless flush() is called. + */ + virtual void pause() = 0; + + /* get this tracks control block */ + virtual sp getCblk() const = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnAudioTrack : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IAUDIOTRACK_H diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h new file mode 100644 index 0000000..43abf77 --- /dev/null +++ b/include/media/IMediaPlayer.h @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#ifndef ANDROID_IMEDIAPLAYER_H +#define ANDROID_IMEDIAPLAYER_H + +#include +#include +#include + +namespace android { + +class ISurface; + +class IMediaPlayer: public IInterface +{ +public: + DECLARE_META_INTERFACE(MediaPlayer); + + virtual void disconnect() = 0; + + virtual status_t setVideoSurface(const sp& surface) = 0; + virtual status_t prepareAsync() = 0; + virtual status_t start() = 0; + virtual status_t stop() = 0; + virtual status_t pause() = 0; + virtual status_t isPlaying(bool* state) = 0; + virtual status_t getVideoSize(int* w, int* h) = 0; + virtual status_t seekTo(int msec) = 0; + virtual status_t getCurrentPosition(int* msec) = 0; + virtual status_t getDuration(int* msec) = 0; + virtual status_t reset() = 0; + virtual status_t setAudioStreamType(int type) = 0; + virtual status_t setLooping(int loop) = 0; + virtual status_t setVolume(float leftVolume, float rightVolume) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnMediaPlayer: public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif // ANDROID_IMEDIAPLAYER_H + diff --git a/include/media/IMediaPlayerClient.h b/include/media/IMediaPlayerClient.h new file mode 100644 index 0000000..5d32811 --- /dev/null +++ b/include/media/IMediaPlayerClient.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef ANDROID_IMEDIAPLAYERCLIENT_H +#define ANDROID_IMEDIAPLAYERCLIENT_H + +#include +#include +#include + +namespace android { + +class IMediaPlayerClient: public IInterface +{ +public: + DECLARE_META_INTERFACE(MediaPlayerClient); + + virtual void notify(int msg, int ext1, int ext2) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnMediaPlayerClient: public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif // ANDROID_IMEDIAPLAYERCLIENT_H + diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h new file mode 100644 index 0000000..63c7a00 --- /dev/null +++ b/include/media/IMediaPlayerService.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#ifndef ANDROID_IMEDIAPLAYERSERVICE_H +#define ANDROID_IMEDIAPLAYERSERVICE_H + +#include +#include +#include + +#include +#include + +namespace android { + +class IMediaPlayerService: public IInterface +{ +public: + DECLARE_META_INTERFACE(MediaPlayerService); + + virtual sp create(pid_t pid, const sp& client, const char* url) = 0; + virtual sp create(pid_t pid, const sp& client, int fd, int64_t offset, int64_t length) = 0; + virtual sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) = 0; + virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnMediaPlayerService: public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif // ANDROID_IMEDIAPLAYERSERVICE_H + diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h new file mode 100644 index 0000000..275e789 --- /dev/null +++ b/include/media/MediaPlayerInterface.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2007 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_MEDIAPLAYERINTERFACE_H +#define ANDROID_MEDIAPLAYERINTERFACE_H + +#include +#include + +#ifdef __cplusplus + +#include +#include + +#include + +namespace android { + +enum player_type { + PV_PLAYER = 1, + SONIVOX_PLAYER = 2, + VORBIS_PLAYER = 3 +}; + +#define DEFAULT_AUDIOSINK_BUFFERCOUNT 4 + +// callback mechanism for passing messages to MediaPlayer object +typedef void (*notify_callback_f)(void* cookie, int msg, int ext1, int ext2); + +// abstract base class - use MediaPlayerInterface +class MediaPlayerBase : public RefBase +{ +public: + + // AudioSink: abstraction layer for audio output + class AudioSink : public RefBase { + public: + virtual ~AudioSink() {} + virtual bool ready() const = 0; // audio output is open and ready + virtual bool realtime() const = 0; // audio output is real-time output + virtual ssize_t bufferSize() const = 0; + virtual ssize_t frameCount() const = 0; + virtual ssize_t channelCount() const = 0; + virtual ssize_t frameSize() const = 0; + virtual uint32_t latency() const = 0; + virtual float msecsPerFrame() const = 0; + virtual status_t open(uint32_t sampleRate, int channelCount, int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT) = 0; + virtual void start() = 0; + virtual ssize_t write(const void* buffer, size_t size) = 0; + virtual void stop() = 0; + virtual void flush() = 0; + virtual void pause() = 0; + virtual void close() = 0; + }; + + MediaPlayerBase() : mCookie(0), mNotify(0) {} + virtual ~MediaPlayerBase() {} + virtual status_t initCheck() = 0; + virtual bool hardwareOutput() = 0; + virtual status_t setSigBusHandlerStructTLSKey(pthread_key_t key) { return 0; } + virtual status_t setDataSource(const char *url) = 0; + virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0; + virtual status_t setVideoSurface(const sp& surface) = 0; + virtual status_t prepare() = 0; + virtual status_t prepareAsync() = 0; + virtual status_t start() = 0; + virtual status_t stop() = 0; + virtual status_t pause() = 0; + virtual bool isPlaying() = 0; + virtual status_t getVideoWidth(int *w) {return 0;} + virtual status_t getVideoHeight(int *h) {return 0;} + virtual status_t seekTo(int msec) = 0; + virtual status_t getCurrentPosition(int *msec) = 0; + virtual status_t getDuration(int *msec) = 0; + virtual status_t reset() = 0; + virtual status_t setLooping(int loop) = 0; + virtual player_type playerType() = 0; + virtual void setNotifyCallback(void* cookie, notify_callback_f notifyFunc) { + mCookie = cookie; mNotify = notifyFunc; } + +protected: + virtual void sendEvent(int msg, int ext1=0, int ext2=0) { if (mNotify) mNotify(mCookie, msg, ext1, ext2); } + + void* mCookie; + notify_callback_f mNotify; +}; + +// Implement this class for media players that use the AudioFlinger software mixer +class MediaPlayerInterface : public MediaPlayerBase +{ +public: + virtual ~MediaPlayerInterface() { } + virtual bool hardwareOutput() { return false; } + virtual void setAudioSink(const sp& audioSink) { mAudioSink = audioSink; } +protected: + sp mAudioSink; +}; + +// Implement this class for media players that output directo to hardware +class MediaPlayerHWInterface : public MediaPlayerBase +{ +public: + virtual ~MediaPlayerHWInterface() {} + virtual bool hardwareOutput() { return true; } + virtual status_t setVolume(float leftVolume, float rightVolume) = 0; + virtual status_t setAudioStreamType(int streamType) = 0; +}; + +}; // namespace android + +#endif // __cplusplus + +// A thread can set the thread local variable identified by the pthread_key_t +// that was passed to the player using the setSigBusHandlerStructTLSKey() +// method to the address of the following structure. +// If 'handlesigbus' is non-NULL, the function it points to will be called, +// and if it returns 0, the signal will be assumed to have been handled, +// and no other action will be taken. If it returns non-zero, the old SIGBUS +// handler will be called. +// If 'handlesigbus is NULL, then sigbusvar must be non NULL. The system's +// SIGBUS handler will map an accessible page filled with zeroes at the +// location that caused the original fault, set the variable pointed to by +// sigbusvar to a non-zero value, and exit (which causes the operation to +// be retried, which should now succeed). +// If base and len are non zero, which is strongly recommended, they will +// be used as additional constraints on the signal handler. That is, when +// specified, the fault address must be in the range specified by base and +// len in order for handlesigbus() to be called or sigbusvar to be set. +// If the fault address is outside of the range, the old SIGBUS handler +// will be called. +struct mediasigbushandler { + int (*handlesigbus)(siginfo_t *, struct mediasigbushandler *); + int *sigbusvar; + char *base; + int len; + // these next two are free for application use + struct mediasigbushandler *next; + void *data; +}; + + +#endif // ANDROID_MEDIAPLAYERINTERFACE_H + diff --git a/include/media/PVPlayer.h b/include/media/PVPlayer.h new file mode 100644 index 0000000..8164d8c --- /dev/null +++ b/include/media/PVPlayer.h @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#ifndef ANDROID_PVPLAYER_H +#define ANDROID_PVPLAYER_H + +#include +#include + +class PlayerDriver; + +namespace android { + +class PVPlayer : public MediaPlayerInterface +{ +public: + PVPlayer(); + virtual ~PVPlayer(); + + virtual status_t initCheck(); + virtual status_t setSigBusHandlerStructTLSKey(pthread_key_t key); + virtual status_t setDataSource(const char *url); + virtual status_t setDataSource(int fd, int64_t offset, int64_t length); + virtual status_t setVideoSurface(const sp& surface); + virtual status_t prepare(); + virtual status_t prepareAsync(); + virtual status_t start(); + virtual status_t stop(); + virtual status_t pause(); + virtual bool isPlaying(); + virtual status_t getVideoWidth(int *w); + virtual status_t getVideoHeight(int *h); + virtual status_t seekTo(int msec); + virtual status_t getCurrentPosition(int *msec); + virtual status_t getDuration(int *msec); + virtual status_t reset(); + virtual status_t setLooping(int loop); + virtual player_type playerType() { return PV_PLAYER; } + + // make available to PlayerDriver + void sendEvent(int msg, int ext1=0, int ext2=0) { MediaPlayerBase::sendEvent(msg, ext1, ext2); } + +private: + static void do_nothing(status_t s, void *cookie) { } + static void run_init(status_t s, void *cookie); + static void run_set_video_surface(status_t s, void *cookie); + static void run_set_audio_output(status_t s, void *cookie); + static void run_prepare(status_t s, void *cookie); + + PlayerDriver* mPlayerDriver; + char * mDataSourcePath; + bool mIsDataSourceSet; + sp mSurface; + void * mMemBase; + off_t mMemSize; + status_t mInit; + int mDuration; +}; + +}; // namespace android + +#endif // ANDROID_PVPLAYER_H diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h new file mode 100644 index 0000000..bc27d35 --- /dev/null +++ b/include/media/ToneGenerator.h @@ -0,0 +1,175 @@ +/* + * 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. + */ + +#ifndef ANDROID_TONEGENERATOR_H_ +#define ANDROID_TONEGENERATOR_H_ + +#include +#include +#include +#include +#include + +namespace android { + +class ToneGenerator { +public: + + // List of all available tones + // This enum must be kept consistant with constants in ToneGenerator JAVA class + enum tone_type { + // DTMF tones ITU-T Recommendation Q.23 + TONE_DTMF_0 = 0, // 0 key: 1336Hz, 941Hz + TONE_DTMF_1, // 1 key: 1209Hz, 697Hz + TONE_DTMF_2, // 2 key: 1336Hz, 697Hz + TONE_DTMF_3, // 3 key: 1477Hz, 697Hz + TONE_DTMF_4, // 4 key: 1209Hz, 770Hz + TONE_DTMF_5, // 5 key: 1336Hz, 770Hz + TONE_DTMF_6, // 6 key: 1477Hz, 770Hz + TONE_DTMF_7, // 7 key: 1209Hz, 852Hz + TONE_DTMF_8, // 8 key: 1336Hz, 852Hz + TONE_DTMF_9, // 9 key: 1477Hz, 852Hz + TONE_DTMF_S, // * key: 1209Hz, 941Hz + TONE_DTMF_P, // # key: 1477Hz, 941Hz + TONE_DTMF_A, // A key: 1633Hz, 697Hz + TONE_DTMF_B, // B key: 1633Hz, 770Hz + TONE_DTMF_C, // C key: 1633Hz, 852Hz + TONE_DTMF_D, // D key: 1633Hz, 941Hz + // Call supervisory tones: 3GPP TS 22.001 (CEPT) + TONE_SUP_DIAL, // Dial tone: 425Hz, continuous + TONE_SUP_BUSY, // Busy tone: 425Hz, 500ms ON, 500ms OFF... + TONE_SUP_CONGESTION, // Congestion tone: 425Hz, 200ms ON, 200ms OFF... + TONE_SUP_RADIO_ACK, // Radio path acknowlegment: 425Hz, 200ms ON + TONE_SUP_RADIO_NOTAVAIL, // Radio path not available: 425Hz, 200ms ON, 200 OFF 3 bursts + TONE_SUP_ERROR, // Error/Special info: 950Hz+1400Hz+1800Hz, 330ms ON, 1s OFF... + TONE_SUP_CALL_WAITING, // Call Waiting: 425Hz, 200ms ON, 600ms OFF, 200ms ON, 3s OFF... + TONE_SUP_RINGTONE, // Ring Tone: 425Hz, 1s ON, 4s OFF... + // Proprietary tones: 3GPP TS 31.111 + TONE_PROP_BEEP, // General beep: 400Hz+1200Hz, 35ms ON + TONE_PROP_ACK, // Positive Acknowlgement: 1200Hz, 100ms ON, 100ms OFF 2 bursts + TONE_PROP_NACK, // Negative Acknowlgement: 300Hz+400Hz+500Hz, 400ms ON + TONE_PROP_PROMPT, // Prompt tone: 400Hz+1200Hz, 200ms ON + TONE_PROP_BEEP2, // General double beep: 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms on + NUM_TONES + }; + + ToneGenerator(int streamType, float volume); + ~ToneGenerator(); + + bool startTone(int toneType); + void stopTone(); + + bool isInited() { return (mState == TONE_IDLE)?false:true;} + +private: + + enum tone_state { + TONE_IDLE, // ToneGenerator is being initialized or initialization failed + TONE_INIT, // ToneGenerator has been successfully initialized and is not playing + TONE_STARTING, // ToneGenerator is starting playing + TONE_PLAYING, // ToneGenerator is playing + TONE_STOPPING, // ToneGenerator is stoping + TONE_RESTARTING // + }; + + static const unsigned int NUM_PCM_BUFFERS = 2; // number of pcm buffers of audio track + + static const unsigned int TONEGEN_MAX_WAVES = 3; + static const unsigned int TONEGEN_MAX_SEGMENTS = 4; // Maximun number of elenemts in + static const unsigned int TONEGEN_INF = 0xFFFFFFFF; // Represents infinite time duration + static const float TONEGEN_GAIN = 0.9; // Default gain passed to WaveGenerator(). + + // ToneDescriptor class contains all parameters needed to generate a tone: + // - The array waveFreq[] contains the frequencies of all individual waves making the multi-tone. + // The number of sine waves varies from 1 to TONEGEN_MAX_WAVES. + // The first null value indicates that no more waves are needed. + // - The array segments[] is used to generate the tone pulses. A segment is a period of time + // during which the tone is ON or OFF. Segments with even index (starting from 0) + // correspond to tone ON state and segments with odd index to OFF state. + // The data stored in segments[] is the duration of the corresponding period in ms. + // The first segment encountered with a 0 duration indicates that no more segment follows. + // - repeatCnt indicates the number of times the sequence described by segments[] array must be repeated. + // When the tone generator encounters the first 0 duration segment, it will compare repeatCnt to mCurCount. + // If mCurCount > repeatCnt, the tone is stopped automatically. + + class ToneDescriptor { + public: + unsigned short waveFreq[TONEGEN_MAX_WAVES+1]; + unsigned long segments[TONEGEN_MAX_SEGMENTS+1]; + unsigned long repeatCnt; + }; + + static const ToneDescriptor toneDescriptors[NUM_TONES]; + + unsigned int mTotalSmp; // Total number of audio samples played (gives current time) + unsigned int mNextSegSmp; // Position of next segment transition expressed in samples + // NOTE: because mTotalSmp, mNextSegSmp are stored on 32 bit, current design will operate properly + // only if tone duration is less than about 27 Hours(@44100Hz sampling rate). If this time is exceeded, + // no crash will occur but tone sequence will show a glitch. + + unsigned short mCurSegment; // Current segment index in ToneDescriptor segments[] + unsigned short mCurCount; // Current sequence repeat count + volatile unsigned short mState; // ToneGenerator state (tone_state) + const ToneDescriptor *mpToneDesc; // pointer to active tone descriptor + const ToneDescriptor *mpNewToneDesc; // pointer to next active tone descriptor + + unsigned int mSamplingRate; // Sampling rate + AudioTrack *mpAudioTrack; // Pointer to audio track used for playback + Mutex mLock; // Mutex to control concurent access to ToneGenerator object from audio callback and application API + Mutex mCbkCondLock; // Mutex associated to mWaitCbkCond + Condition mWaitCbkCond; // condition enabling interface to wait for audio callback completion after a change is requested + float mVolume; // Volume applied to audio track + + static void audioCallback(void* user, const AudioTrack::Buffer& info); + bool prepareWave(); + unsigned int numWaves(); + void clearWaveGens(); + + // WaveGenerator generates a single sine wave + class WaveGenerator { + public: + enum gen_command { + WAVEGEN_START, // Start/restart wave from phase 0 + WAVEGEN_CONT, // Continue wave from current phase + WAVEGEN_STOP // Stop wave on zero crossing + }; + + WaveGenerator(unsigned short samplingRate, unsigned short frequency, + float volume); + ~WaveGenerator(); + + void getSamples(short *outBuffer, unsigned int count, + unsigned int command); + + private: + static const short GEN_AMP = 32000; // amplitude of generator + static const short S_Q14 = 14; // shift for Q14 + static const short S_Q15 = 15; // shift for Q15 + + short mA1_Q14; // Q14 coefficient + // delay line of full amplitude generator + short mS1, mS2; // delay line S2 oldest + short mS2_0; // saved value for reinitialisation + short mAmplitude_Q15; // Q15 amplitude + }; + + Vector mWaveGens; // list of active wave generators. +}; + +} +; // namespace android + +#endif /*ANDROID_TONEGENERATOR_H_*/ diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h new file mode 100644 index 0000000..586dda1c --- /dev/null +++ b/include/media/mediametadataretriever.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + + +#ifndef MEDIAMETADATARETRIEVER_H +#define MEDIAMETADATARETRIEVER_H + +#include // for SkBitmap + +namespace android { + +// Keep these in synch with the constants defined in MediaMetadataRetriever.java +// class. +enum { + METADATA_KEY_CD_TRACK_NUMBER = 0, + METADATA_KEY_ALBUM = 1, + METADATA_KEY_ARTIST = 2, + METADATA_KEY_AUTHOR = 3, + METADATA_KEY_COMPOSER = 4, + METADATA_KEY_DATE = 5, + METADATA_KEY_GENRE = 6, + METADATA_KEY_TITLE = 7, + METADATA_KEY_YEAR = 8, + METADATA_KEY_DURATION = 9, + METADATA_KEY_NUM_TRACKS = 10, + METADATA_KEY_IS_DRM_CRIPPLED = 11, + METADATA_KEY_CODEC = 12, + // Add more here... +}; + +// A utility class that holds the size and actual data in album art. +class MediaAlbumArt { +public: + MediaAlbumArt(): length(0), data(NULL) {} + MediaAlbumArt(const MediaAlbumArt& copy) { + // Don't be caught by uninitialized variables!! + length = 0; + data = NULL; + setData(copy.length, copy.data); + } + MediaAlbumArt(const char* url); + ~MediaAlbumArt() { clearData(); } + + void clearData(); + status_t setData(unsigned int len, const char* buf); + char *getData() const { return copyData(length, data); } + unsigned int getLength() const { return length; } + +private: + // Disable copy assignment operator! + MediaAlbumArt& operator=(const MediaAlbumArt& rhs); + static char* copyData(unsigned int len, const char* buf); + + unsigned int length; // Number of bytes in data. + char *data; // Actual binary data. +}; + +class MediaMetadataRetrieverImpl +{ +public: + virtual ~MediaMetadataRetrieverImpl() {}; + virtual status_t setDataSource(const char* dataSourceUrl) = 0; + virtual SkBitmap *captureFrame() = 0; + virtual const char* extractMetadata(int keyCode) = 0; + virtual MediaAlbumArt* extractAlbumArt() = 0; + virtual void setMode(int mode) = 0; +}; + +class MediaMetadataRetriever +{ +public: + static status_t setDataSource(const char* dataSourceUrl); + static SkBitmap *captureFrame(); + static const char* extractMetadata(int keyCode); + static MediaAlbumArt* extractAlbumArt(); + static void setMode(int mode); + static void release(); + static void create(); + +private: + MediaMetadataRetriever() {} + static MediaMetadataRetrieverImpl *mRetriever; + static void *mLibHandler; +}; + +}; // namespace android + +#endif // MEDIAMETADATARETRIEVER_H diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h new file mode 100644 index 0000000..aadfc32 --- /dev/null +++ b/include/media/mediaplayer.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2007 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_MEDIAPLAYER_H +#define ANDROID_MEDIAPLAYER_H + +#include +#include +#include +#include +#include + +namespace android { + +enum media_event_type { + MEDIA_NOP = 0, // interface test message + MEDIA_PREPARED = 1, + MEDIA_PLAYBACK_COMPLETE = 2, + MEDIA_BUFFERING_UPDATE = 3, + MEDIA_SEEK_COMPLETE = 4, + MEDIA_ERROR = 100, +}; + +typedef int media_error_type; +const media_error_type MEDIA_ERROR_UNKNOWN = 1; +const media_error_type MEDIA_ERROR_SERVER_DIED = 100; + +enum media_player_states { + MEDIA_PLAYER_STATE_ERROR = 0, + MEDIA_PLAYER_IDLE = 1 << 0, + MEDIA_PLAYER_INITIALIZED = 1 << 1, + MEDIA_PLAYER_PREPARING = 1 << 2, + MEDIA_PLAYER_PREPARED = 1 << 3, + MEDIA_PLAYER_STARTED = 1 << 4, + MEDIA_PLAYER_PAUSED = 1 << 5, + MEDIA_PLAYER_STOPPED = 1 << 6, + MEDIA_PLAYER_PLAYBACK_COMPLETE = 1 << 7 +}; + +// ---------------------------------------------------------------------------- +// ref-counted object for callbacks +class MediaPlayerListener: public RefBase +{ +public: + virtual void notify(int msg, int ext1, int ext2) = 0; +}; + +class MediaPlayer : public BnMediaPlayerClient, public IBinder::DeathRecipient +{ +public: + MediaPlayer(); + ~MediaPlayer(); + + void disconnect(); + status_t setDataSource(const char *url); + status_t setDataSource(int fd, int64_t offset, int64_t length); + status_t setVideoSurface(const sp& surface); + status_t setListener(const sp& listener); + status_t prepare(); + status_t prepareAsync(); + status_t start(); + status_t stop(); + status_t pause(); + bool isPlaying(); + status_t getVideoWidth(int *w); + status_t getVideoHeight(int *h); + status_t seekTo(int msec); + status_t getCurrentPosition(int *msec); + status_t getDuration(int *msec); + status_t reset(); + status_t setAudioStreamType(int type); + status_t setLooping(int loop); + status_t setVolume(float leftVolume, float rightVolume); + void notify(int msg, int ext1, int ext2); + static sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels); + static sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels); + +private: + void clear_l(); + status_t seekTo_l(int msec); + status_t prepareAsync_l(); + status_t getDuration_l(int *msec); + status_t setDataSource(const sp& player); + + static const sp& getMediaPlayerService(); + virtual void binderDied(const wp& who); + + class DeathNotifier: public IBinder::DeathRecipient + { + public: + DeathNotifier() {} + virtual ~DeathNotifier(); + + virtual void binderDied(const wp& who); + }; + + static sp mDeathNotifier; + + sp mPlayer; + Mutex mLock; + Mutex mNotifyLock; + Condition mSignal; + sp mListener; + void* mCookie; + media_player_states mCurrentState; + int mDuration; + int mCurrentPosition; + int mSeekPosition; + bool mPrepareSync; + status_t mPrepareStatus; + int mStreamType; + bool mLoop; + float mLeftVolume; + float mRightVolume; + + friend class DeathNotifier; + + static Mutex mServiceLock; + static sp mMediaPlayerService; +}; + +}; // namespace android + +#endif // ANDROID_MEDIAPLAYER_H + diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h new file mode 100644 index 0000000..f247424 --- /dev/null +++ b/include/media/mediarecorder.h @@ -0,0 +1,120 @@ +/* + * 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. + */ + +#ifndef MEDIARECORDER_H +#define MEDIARECORDER_H + +#include +#include + +namespace android { + +class AuthorDriverWrapper; + +typedef void (*media_completion_f)(status_t status, void *cookie); + +/* Do not change these values without updating their counterparts + * in java/android/android/media/MediaRecorder.java! + */ +enum audio_source { + AUDIO_SOURCE_DEFAULT = 0, + AUDIO_SOURCE_MIC = 1, +}; + +enum video_source { + VIDEO_SOURCE_DEFAULT = 0, + VIDEO_SOURCE_CAMERA = 1, +}; + +enum output_format { + OUTPUT_FORMAT_DEFAULT = 0, + OUTPUT_FORMAT_THREE_GPP = 1, + OUTPUT_FORMAT_MPEG_4 = 2, +}; + +enum audio_encoder { + AUDIO_ENCODER_DEFAULT = 0, + AUDIO_ENCODER_AMR_NB = 1, +}; + +enum video_encoder { + VIDEO_ENCODER_DEFAULT = 0, + VIDEO_ENCODER_H263 = 1, + VIDEO_ENCODER_H264 = 2, + VIDEO_ENCODER_MPEG_4_SP = 3, +}; + +/* + * The state machine of the media_recorder uses a set of different state names. + * The mapping between the media_recorder and the pvauthorengine is shown below: + * + * mediarecorder pvauthorengine + * ---------------------------------------------------------------- + * MEDIA_RECORDER_ERROR ERROR + * MEDIA_RECORDER_IDLE IDLE + * MEDIA_RECORDER_INITIALIZED OPENED + * MEDIA_RECORDER_PREPARING + * MEDIA_RECORDER_PREPARED INITIALIZED + * MEDIA_RECORDER_RECORDING RECORDING + */ +enum media_recorder_states { + MEDIA_RECORDER_ERROR = 0, + MEDIA_RECORDER_IDLE = 1 << 0, + MEDIA_RECORDER_INITIALIZED = 1 << 1, + MEDIA_RECORDER_PREPARING = 1 << 2, + MEDIA_RECORDER_PREPARED = 1 << 3, + MEDIA_RECORDER_RECORDING = 1 << 4, +}; + +class MediaRecorder +{ +public: + MediaRecorder(); + ~MediaRecorder(); + + status_t init(); + + status_t setAudioSource(audio_source as); + status_t setVideoSource(video_source vs); + status_t setOutputFormat(output_format of); + status_t setAudioEncoder(audio_encoder ae); + status_t setVideoEncoder(video_encoder ve); + status_t setVideoSize(int width, int height); + status_t setVideoFrameRate(int frames_per_second); + status_t setPreviewSurface(const sp& surface); + + status_t setOutputFile(const char *path); + // XXX metadata setup + + status_t prepare(); + status_t start(); + status_t stop(); + status_t reset(); + status_t getIfOutputFormatSpecified(); + + status_t getMaxAmplitude(int *max); + +private: + AuthorDriverWrapper *mAuthorDriverWrapper; + bool mOutputFormatSpecified; + media_recorder_states mCurrentState; + +}; + +}; // namespace android + +#endif // MEDIAPLAYER_H + diff --git a/include/media/mediascanner.h b/include/media/mediascanner.h new file mode 100644 index 0000000..5d0122d --- /dev/null +++ b/include/media/mediascanner.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#ifndef MEDIASCANNER_H +#define MEDIASCANNER_H + +#include +#include + +namespace android { + +class MediaScannerClient; + +class MediaScanner +{ +public: + MediaScanner(); + ~MediaScanner(); + + typedef bool (*ExceptionCheck)(void* env); + + status_t processFile(const char *path, const char *mimeType, MediaScannerClient& client); + status_t processDirectory(const char *path, const char* extensions, + MediaScannerClient& client, ExceptionCheck exceptionCheck, void* exceptionEnv); + + // extracts album art as a block of data + char* extractAlbumArt(int fd); + + static void uninitializeForThread(); + +private: + status_t doProcessDirectory(char *path, int pathRemaining, const char* extensions, + MediaScannerClient& client, ExceptionCheck exceptionCheck, void* exceptionEnv); + void initializeForThread(); +}; + + +class MediaScannerClient +{ +public: + virtual ~MediaScannerClient() {} + virtual bool scanFile(const char* path, long long lastModified, long long fileSize) = 0; + virtual bool handleStringTag(const char* name, const char* value) = 0; + virtual bool setMimeType(const char* mimeType) = 0; +}; + +}; // namespace android + +#endif // MEDIASCANNER_H + diff --git a/include/media/thread_init.h b/include/media/thread_init.h new file mode 100644 index 0000000..2c0c1f1 --- /dev/null +++ b/include/media/thread_init.h @@ -0,0 +1,25 @@ +/* + * 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. + */ + +#ifndef THREAD_INIT_H +#define THREAD_INIT_H + +bool InitializeForThread(); +void UninitializeForThread(); +void keydestructor(void*); + +#endif /* THREAD_INIT_H*/ + diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h new file mode 100644 index 0000000..1878f3c --- /dev/null +++ b/include/private/media/AudioTrackShared.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2007 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_AUDIO_TRACK_SHARED_H +#define ANDROID_AUDIO_TRACK_SHARED_H + +#include +#include + +#include + +namespace android { + +// ---------------------------------------------------------------------------- + +#define MAX_SAMPLE_RATE 65535 +#define THREAD_PRIORITY_AUDIO_CLIENT (ANDROID_PRIORITY_AUDIO) + +struct audio_track_cblk_t +{ + enum { + SEQUENCE_MASK = 0xFFFFFF00, + BUFFER_MASK = 0x000000FF + }; + + Mutex lock; + Condition cv; + volatile uint32_t user; + volatile uint32_t server; + volatile union { + uint16_t volume[2]; + uint32_t volumeLR; + }; + uint16_t sampleRate; + uint16_t reserved; + + void* buffers; + size_t size; + + audio_track_cblk_t(); + uint32_t stepUser(int bufferCount); + bool stepServer(int bufferCount); + void* buffer(int id) const; +}; + + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_AUDIO_TRACK_SHARED_H diff --git a/include/private/opengles/gl_context.h b/include/private/opengles/gl_context.h new file mode 100644 index 0000000..67d50fd --- /dev/null +++ b/include/private/opengles/gl_context.h @@ -0,0 +1,624 @@ +/* + * Copyright (C) 2006 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_OPENGLES_CONTEXT_H +#define ANDROID_OPENGLES_CONTEXT_H + +#include +#include +#include +#include +#ifdef HAVE_ANDROID_OS +#include +#endif + +#include + +#include + +namespace android { + +const unsigned int OGLES_NUM_COMPRESSED_TEXTURE_FORMATS = 10; + +class EGLTextureObject; +class EGLSurfaceManager; +class EGLBufferObjectManager; + +namespace gl { + +struct ogles_context_t; +struct matrixx_t; +struct transform_t; +struct buffer_t; + +ogles_context_t* getGlContext(); + +template +static inline void swap(T& a, T& b) { + T t(a); a = b; b = t; +} +template +inline T max(T a, T b) { + return a +inline T max(T a, T b, T c) { + return max(a, max(b, c)); +} +template +inline T min(T a, T b) { + return a +inline T min(T a, T b, T c) { + return min(a, min(b, c)); +} +template +inline T min(T a, T b, T c, T d) { + return min(min(a,b), min(c,d)); +} + +// ---------------------------------------------------------------------------- +// vertices +// ---------------------------------------------------------------------------- + +struct vec3_t { + union { + struct { GLfixed x, y, z; }; + struct { GLfixed r, g, b; }; + struct { GLfixed S, T, R; }; + GLfixed v[3]; + }; +}; + +struct vec4_t { + union { + struct { GLfixed x, y, z, w; }; + struct { GLfixed r, g, b, a; }; + struct { GLfixed S, T, R, Q; }; + GLfixed v[4]; + }; +}; + +struct vertex_t { + enum { + // these constant matter for our clipping + CLIP_L = 0x0001, // clipping flags + CLIP_R = 0x0002, + CLIP_B = 0x0004, + CLIP_T = 0x0008, + CLIP_N = 0x0010, + CLIP_F = 0x0020, + + EYE = 0x0040, + RESERVED = 0x0080, + + USER_CLIP_0 = 0x0100, // user clipping flags + USER_CLIP_1 = 0x0200, + USER_CLIP_2 = 0x0400, + USER_CLIP_3 = 0x0800, + USER_CLIP_4 = 0x1000, + USER_CLIP_5 = 0x2000, + + LIT = 0x4000, // lighting has been applied + TT = 0x8000, // texture coords transformed + + FRUSTUM_CLIP_ALL= 0x003F, + USER_CLIP_ALL = 0x3F00, + CLIP_ALL = 0x3F3F, + }; + + // the fields below are arranged to minimize d-cache usage + // we group together, by cache-line, the fields most likely to be used + + union { + vec4_t obj; + vec4_t eye; + }; + vec4_t clip; + + uint32_t flags; + size_t index; // cache tag, and vertex index + GLfixed fog; + uint8_t locked; + uint8_t mru; + uint8_t reserved[2]; + vec4_t window; + + vec4_t color; + vec4_t texture[GGL_TEXTURE_UNIT_COUNT]; + uint32_t reserved1[4]; + + inline void clear() { + flags = index = locked = mru = 0; + } +}; + +struct point_size_t { + GGLcoord size; + GLboolean smooth; +}; + +struct line_width_t { + GGLcoord width; + GLboolean smooth; +}; + +struct polygon_offset_t { + GLfixed factor; + GLfixed units; + GLboolean enable; +}; + +// ---------------------------------------------------------------------------- +// arrays +// ---------------------------------------------------------------------------- + +struct array_t { + typedef void (*fetcher_t)(ogles_context_t*, GLfixed*, const GLvoid*); + fetcher_t fetch; + GLvoid const* physical_pointer; + GLint size; + GLsizei stride; + GLvoid const* pointer; + buffer_t const* bo; + uint16_t type; + GLboolean enable; + GLboolean pad; + GLsizei bounds; + void init(GLint, GLenum, GLsizei, const GLvoid *, const buffer_t*, GLsizei); + inline void resolve(); + inline const GLubyte* element(GLint i) const { + return (const GLubyte*)physical_pointer + i * stride; + } +}; + +struct array_machine_t { + array_t vertex; + array_t normal; + array_t color; + array_t texture[GGL_TEXTURE_UNIT_COUNT]; + uint8_t activeTexture; + uint8_t tmu; + uint16_t cull; + uint32_t flags; + GLenum indicesType; + buffer_t const* array_buffer; + buffer_t const* element_array_buffer; + + void (*compileElements)(ogles_context_t*, vertex_t*, GLint, GLsizei); + void (*compileElement)(ogles_context_t*, vertex_t*, GLint); + + void (*mvp_transform)(transform_t const*, vec4_t*, vec4_t const*); + void (*mv_transform)(transform_t const*, vec4_t*, vec4_t const*); + void (*tex_transform[2])(transform_t const*, vec4_t*, vec4_t const*); + void (*perspective)(ogles_context_t*c, vertex_t* v); + void (*clipVertex)(ogles_context_t* c, vertex_t* nv, + GGLfixed t, const vertex_t* s, const vertex_t* p); + void (*clipEye)(ogles_context_t* c, vertex_t* nv, + GGLfixed t, const vertex_t* s, const vertex_t* p); +}; + +struct vertex_cache_t { + enum { + // must be at least 4 + // 3 vertice for triangles + // or 2 + 2 for indexed triangles w/ cache contention + VERTEX_BUFFER_SIZE = 8, + // must be a power of two and at least 3 + VERTEX_CACHE_SIZE = 64, // 8 KB + + INDEX_BITS = 16, + INDEX_MASK = ((1LU<(pthread_getspecific(gGLKey)); + } +#endif + + +struct prims_t { + typedef ogles_context_t* GL; + void (*renderPoint)(GL, vertex_t*); + void (*renderLine)(GL, vertex_t*, vertex_t*); + void (*renderTriangle)(GL, vertex_t*, vertex_t*, vertex_t*); +}; + +struct ogles_context_t { + context_t rasterizer; + array_machine_t arrays __attribute__((aligned(32))); + texture_state_t textures; + transform_state_t transforms; + vertex_cache_t vc; + prims_t prims; + culling_t cull; + lighting_t lighting; + user_clip_planes_t clipPlanes; + compute_iterators_t lerp; __attribute__((aligned(32))); + vertex_t current; + vec4_t currentColorClamped; + vec3_t currentNormal; + viewport_t viewport; + point_size_t point; + line_width_t line; + polygon_offset_t polygonOffset; + fog_t fog; + uint32_t perspective : 1; + uint32_t transformTextures : 1; + EGLSurfaceManager* surfaceManager; + EGLBufferObjectManager* bufferObjectManager; + GLenum error; + + static inline ogles_context_t* get() { + return getGlThreadSpecific(); + } + +}; + +}; // namespace gl +}; // namespace android + +#endif // ANDROID_OPENGLES_CONTEXT_H + diff --git a/include/private/ui/LayerState.h b/include/private/ui/LayerState.h new file mode 100644 index 0000000..b6fcd80 --- /dev/null +++ b/include/private/ui/LayerState.h @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#ifndef ANDROID_COMPOSER_LAYER_STATE_H +#define ANDROID_COMPOSER_LAYER_STATE_H + +#include +#include + +#include + +#include +#include + +#include + +namespace android { + +class Parcel; + +struct layer_state_t { + + layer_state_t() + : surface(0), what(0), + x(0), y(0), z(0), w(0), h(0), + alpha(0), tint(0), flags(0), mask(0), + reserved(0) + { + matrix.dsdx = matrix.dtdy = 1.0f; + matrix.dsdy = matrix.dtdx = 0.0f; + } + + status_t write(Parcel& output) const; + status_t read(const Parcel& input); + + struct matrix22_t { + float dsdx; + float dtdx; + float dsdy; + float dtdy; + }; + SurfaceID surface; + uint32_t what; + int32_t x; + int32_t y; + uint32_t z; + uint32_t w; + uint32_t h; + float alpha; + uint32_t tint; + uint8_t flags; + uint8_t mask; + uint8_t reserved; + matrix22_t matrix; + // non POD must be last. see write/read + Region transparentRegion; +}; + +}; // namespace android + +#endif // ANDROID_COMPOSER_LAYER_STATE_H + diff --git a/include/private/ui/SharedState.h b/include/private/ui/SharedState.h new file mode 100644 index 0000000..546d0ad --- /dev/null +++ b/include/private/ui/SharedState.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2007 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_UI_SHARED_STATE_H +#define ANDROID_UI_SHARED_STATE_H + +#include +#include + +#include + +namespace android { + +/* + * These structures are shared between the composer process and its clients + */ + +// --------------------------------------------------------------------------- + +struct surface_info_t { // 4 longs, 16 bytes + enum { + eBufferDirty = 0x01 + }; + uint16_t w; + uint16_t h; + uint16_t stride; + uint16_t bpr; + uint16_t reserved; + uint8_t format; + uint8_t flags; + ssize_t bits_offset; +}; + +// --------------------------------------------------------------------------- + +const uint32_t NUM_LAYERS_MAX = 31; + +enum { // layer_cblk_t swapState + eIndex = 0x00000001, + eFlipRequested = 0x00000002, + + eResizeBuffer0 = 0x00000004, + eResizeBuffer1 = 0x00000008, + eResizeRequested = eResizeBuffer0 | eResizeBuffer1, + + eBusy = 0x00000010, + eLocked = 0x00000020, + eNextFlipPending = 0x00000040, + eInvalidSurface = 0x00000080 +}; + +enum { // layer_cblk_t flags + eLayerNotPosted = 0x00000001, + eNoCopyBack = 0x00000002, + eReserved = 0x0000007C, + eBufferIndexShift = 7, + eBufferIndex = 1<>1)); + } + static inline int frontBuffer(uint32_t state) { + return 1 - backBuffer(state); + } +}; + +// --------------------------------------------------------------------------- + +struct per_client_cblk_t // 4KB max +{ + Mutex lock; + Condition cv; + layer_cblk_t layers[NUM_LAYERS_MAX] __attribute__((aligned(32))); + + enum { + BLOCKING = 0x00000001, + INSPECT = 0x00000002 + }; + + per_client_cblk_t(); + + // these functions are used by the clients + status_t validate(size_t i) const; + int32_t lock_layer(size_t i, uint32_t flags); + uint32_t unlock_layer_and_post(size_t i); + void unlock_layer(size_t i); +}; +// --------------------------------------------------------------------------- + +const uint32_t NUM_DISPLAY_MAX = 4; + +struct display_cblk_t +{ + uint16_t w; + uint16_t h; + uint8_t format; + uint8_t orientation; + uint8_t reserved[2]; + float fps; + float density; + float xdpi; + float ydpi; + uint32_t pad[2]; +}; + +struct surface_flinger_cblk_t // 4KB max +{ + surface_flinger_cblk_t(); + + uint8_t connected; + uint8_t reserved[3]; + uint32_t pad[7]; + + display_cblk_t displays[NUM_DISPLAY_MAX]; +}; + +// --------------------------------------------------------------------------- + +template struct CTA; +template<> struct CTA { }; + +// compile-time assertions. just to avoid catastrophes. +inline void compile_time_asserts() { + CTA sizeof__layer_cblk_t__eq_128; + (void)sizeof__layer_cblk_t__eq_128; // we don't want a warning + CTA sizeof__per_client_cblk_t__le_4096; + (void)sizeof__per_client_cblk_t__le_4096; // we don't want a warning + CTA sizeof__surface_flinger_cblk_t__le_4096; + (void)sizeof__surface_flinger_cblk_t__le_4096; // we don't want a warning +} + +}; // namespace android + +#endif // ANDROID_UI_SHARED_STATE_H + diff --git a/include/private/ui/SurfaceFlingerSynchro.h b/include/private/ui/SurfaceFlingerSynchro.h new file mode 100644 index 0000000..ff91b61 --- /dev/null +++ b/include/private/ui/SurfaceFlingerSynchro.h @@ -0,0 +1,76 @@ +/* + * 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. + */ + + +#ifndef ANDROID_SURFACE_FLINGER_SYNCHRO_H +#define ANDROID_SURFACE_FLINGER_SYNCHRO_H + +#include +#include +#include +#include +#include + +namespace android { + +class SurfaceFlinger; + +class SurfaceFlingerSynchro +{ +public: + + // client constructor + SurfaceFlingerSynchro(const sp& flinger); + ~SurfaceFlingerSynchro(); + + // signal surfaceflinger for some work + status_t signal(); + +private: + class Barrier { + public: + Barrier(); + ~Barrier(); + void open(); + void close(); + void waitAndClose(); + status_t waitAndClose(nsecs_t timeout); + private: + enum { OPENED, CLOSED }; + mutable Mutex lock; + mutable Condition cv; + volatile int state; + }; + + friend class SurfaceFlinger; + + // server constructor + SurfaceFlingerSynchro(); + + void open(); + + // wait until there is some work to do + status_t wait(); + status_t wait(nsecs_t timeout); + + sp mSurfaceComposer; + Barrier mBarrier; +}; + +}; // namespace android + +#endif // ANDROID_SURFACE_FLINGER_SYNCHRO_H + diff --git a/include/private/utils/Static.h b/include/private/utils/Static.h new file mode 100644 index 0000000..f1439b7 --- /dev/null +++ b/include/private/utils/Static.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +// All static variables go here, to control initialization and +// destruction order in the library. + +#include +#include + +#ifndef LIBUTILS_NATIVE +#include +#include +#include +#include +#include +#endif + +namespace android { +// For TextStream.cpp +extern Vector gTextBuffers; + +// For String8.cpp +extern void initialize_string8(); +extern void terminate_string8(); + +// For String16.cpp +extern void initialize_string16(); +extern void terminate_string16(); + + + +#ifndef LIBUTILS_NATIVE + +// For ProcessState.cpp +extern Mutex gProcessMutex; +extern sp gProcess; + +// For ServiceManager.cpp +extern Mutex gDefaultServiceManagerLock; +extern sp gDefaultServiceManager; +extern sp gPermissionController; + +#endif + +} // namespace android diff --git a/include/private/utils/binder_module.h b/include/private/utils/binder_module.h new file mode 100644 index 0000000..fdf327e --- /dev/null +++ b/include/private/utils/binder_module.h @@ -0,0 +1,148 @@ +/* + * 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. + */ + +#ifndef _BINDER_MODULE_H_ +#define _BINDER_MODULE_H_ + +#ifdef __cplusplus +namespace android { +#endif + +#if defined(HAVE_ANDROID_OS) + +/* obtain structures and constants from the kernel header */ + +#include +#include + +#else + +/* Some parts of the simulator need fake versions of this + * stuff in order to compile. Really this should go away + * entirely... + */ + +#define BINDER_CURRENT_PROTOCOL_VERSION 7 + +#define BINDER_TYPE_BINDER 1 +#define BINDER_TYPE_WEAK_BINDER 2 +#define BINDER_TYPE_HANDLE 3 +#define BINDER_TYPE_WEAK_HANDLE 4 +#define BINDER_TYPE_FD 5 + +struct flat_binder_object { + unsigned long type; + unsigned long flags; + union { + void *binder; + signed long handle; + }; + void *cookie; +}; + +struct binder_write_read { + signed long write_size; + signed long write_consumed; + unsigned long write_buffer; + signed long read_size; + signed long read_consumed; + unsigned long read_buffer; +}; + +struct binder_transaction_data { + union { + size_t handle; + void *ptr; + } target; + void *cookie; + unsigned int code; + + unsigned int flags; + pid_t sender_pid; + uid_t sender_euid; + size_t data_size; + size_t offsets_size; + + union { + struct { + const void *buffer; + const void *offsets; + } ptr; + uint8_t buf[8]; + } data; +}; + +enum transaction_flags { + TF_ONE_WAY = 0x01, + TF_ROOT_OBJECT = 0x04, + TF_STATUS_CODE = 0x08, + TF_ACCEPT_FDS = 0x10, +}; + + +enum { + FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff, + FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100, +}; + +enum BinderDriverReturnProtocol { + BR_ERROR, + BR_OK, + BR_TRANSACTION, + BR_REPLY, + BR_ACQUIRE_RESULT, + BR_DEAD_REPLY, + BR_TRANSACTION_COMPLETE, + BR_INCREFS, + BR_ACQUIRE, + BR_RELEASE, + BR_DECREFS, + BR_ATTEMPT_ACQUIRE, + BR_NOOP, + BR_SPAWN_LOOPER, + BR_FINISHED, + BR_DEAD_BINDER, + BR_CLEAR_DEATH_NOTIFICATION_DONE, + BR_FAILED_REPLY, +}; + +enum BinderDriverCommandProtocol { + BC_TRANSACTION, + BC_REPLY, + BC_ACQUIRE_RESULT, + BC_FREE_BUFFER, + BC_INCREFS, + BC_ACQUIRE, + BC_RELEASE, + BC_DECREFS, + BC_INCREFS_DONE, + BC_ACQUIRE_DONE, + BC_ATTEMPT_ACQUIRE, + BC_REGISTER_LOOPER, + BC_ENTER_LOOPER, + BC_EXIT_LOOPER, + BC_REQUEST_DEATH_NOTIFICATION, + BC_CLEAR_DEATH_NOTIFICATION, + BC_DEAD_BINDER_DONE, +}; + +#endif + +#ifdef __cplusplus +} // namespace android +#endif + +#endif // _BINDER_MODULE_H_ diff --git a/include/private/utils/futex_synchro.h b/include/private/utils/futex_synchro.h new file mode 100644 index 0000000..ac2ab19 --- /dev/null +++ b/include/private/utils/futex_synchro.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef _FUTEX_SYNCHRO_H +#define _FUTEX_SYNCHRO_H + +#ifndef HAVE_FUTEX +#error "HAVE_FUTEX not defined" +#endif + +#define FUTEX_WAIT_INFINITE (0) + +typedef struct futex_mutex_t futex_mutex_t; + +struct futex_mutex_t +{ + volatile int value; +}; + +typedef struct futex_cond_t futex_cond_t; + +struct futex_cond_t +{ + volatile int value; +}; + + +#if __cplusplus +extern "C" { +#endif + +void futex_mutex_init(futex_mutex_t *m); +int futex_mutex_lock(futex_mutex_t *m, unsigned msec); +void futex_mutex_unlock(futex_mutex_t *m); +int futex_mutex_trylock(futex_mutex_t *m); + +void futex_cond_init(futex_cond_t *c); +int futex_cond_wait(futex_cond_t *c, futex_mutex_t *m, unsigned msec); +void futex_cond_signal(futex_cond_t *c); +void futex_cond_broadcast(futex_cond_t *c); + +#if __cplusplus +} // extern "C" +#endif + +#endif // _FUTEX_SYNCHRO_H + diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk new file mode 100644 index 0000000..cfa837a --- /dev/null +++ b/media/libmedia/Android.mk @@ -0,0 +1,34 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioTrack.cpp \ + IAudioFlinger.cpp \ + IAudioTrack.cpp \ + IAudioRecord.cpp \ + AudioRecord.cpp \ + AudioSystem.cpp \ + mediaplayer.cpp \ + IMediaPlayerService.cpp \ + IMediaPlayerClient.cpp \ + IMediaPlayer.cpp \ + mediametadataretriever.cpp \ + ToneGenerator.cpp + +LOCAL_SHARED_LIBRARIES := \ + libui libcutils libutils + +LOCAL_MODULE:= libmedia + +ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) +LOCAL_LDLIBS += -ldl +endif + +ifneq ($(TARGET_SIMULATOR),true) +LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_C_INCLUDES := \ + $(call include-path-for, graphics corecg) + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp new file mode 100644 index 0000000..f3e4123 --- /dev/null +++ b/media/libmedia/AudioRecord.cpp @@ -0,0 +1,397 @@ +/* +** +** Copyright 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. +*/ + +#define LOG_TAG "AudioRecord" + +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +namespace android { + +// --------------------------------------------------------------------------- + +AudioRecord::AudioRecord() + : mStatus(NO_INIT) +{ +} + +AudioRecord::AudioRecord( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags, + callback_t cbf, void* user) + : mStatus(NO_INIT) +{ + mStatus = set(streamType, sampleRate, format, channelCount, + bufferCount, flags, cbf, user); +} + +AudioRecord::~AudioRecord() +{ + if (mStatus == NO_ERROR) { + if (mPosition) { + releaseBuffer(&mAudioBuffer); + } + // obtainBuffer() will give up with an error + mAudioRecord->stop(); + if (mClientRecordThread != 0) { + mClientRecordThread->requestExitAndWait(); + mClientRecordThread.clear(); + } + mAudioRecord.clear(); + IPCThreadState::self()->flushCommands(); + } +} + +status_t AudioRecord::set( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags, + callback_t cbf, void* user) +{ + + if (mAudioFlinger != 0) { + return INVALID_OPERATION; + } + + const sp& audioFlinger = AudioSystem::get_audio_flinger(); + if (audioFlinger == 0) { + return NO_INIT; + } + + if (streamType == DEFAULT_INPUT) { + streamType = MIC_INPUT; + } + + if (sampleRate == 0) { + sampleRate = DEFAULT_SAMPLE_RATE; + } + // these below should probably come from the audioFlinger too... + if (format == 0) { + format = AudioSystem::PCM_16_BIT; + } + if (channelCount == 0) { + channelCount = 1; + } + if (bufferCount == 0) { + bufferCount = 2; + } else if (bufferCount < 2) { + return BAD_VALUE; + } + + // validate parameters + if (format != AudioSystem::PCM_16_BIT) { + return BAD_VALUE; + } + if (channelCount != 1 && channelCount != 2) { + return BAD_VALUE; + } + if (bufferCount < 2) { + return BAD_VALUE; + } + + // open record channel + sp record = audioFlinger->openRecord(getpid(), streamType, + sampleRate, format, channelCount, bufferCount, flags); + if (record == 0) { + return NO_INIT; + } + sp cblk = record->getCblk(); + if (cblk == 0) { + return NO_INIT; + } + if (cbf != 0) { + mClientRecordThread = new ClientRecordThread(*this); + if (mClientRecordThread == 0) { + return NO_INIT; + } + } + + mStatus = NO_ERROR; + + mAudioFlinger = audioFlinger; + mAudioRecord = record; + mCblkMemory = cblk; + mCblk = static_cast(cblk->pointer()); + mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + mSampleRate = sampleRate; + mFrameCount = audioFlinger->frameCount(); + mFormat = format; + mBufferCount = bufferCount; + mChannelCount = channelCount; + mActive = 0; + mCbf = cbf; + mUserData = user; + mLatency = seconds(mFrameCount) / mSampleRate; + mPosition = 0; + return NO_ERROR; +} + +status_t AudioRecord::initCheck() const +{ + return mStatus; +} + +// ------------------------------------------------------------------------- + +nsecs_t AudioRecord::latency() const +{ + return mLatency; +} + +uint32_t AudioRecord::sampleRate() const +{ + return mSampleRate; +} + +int AudioRecord::format() const +{ + return mFormat; +} + +int AudioRecord::channelCount() const +{ + return mChannelCount; +} + +int AudioRecord::bufferCount() const +{ + return mBufferCount; +} + +// ------------------------------------------------------------------------- + +status_t AudioRecord::start() +{ + status_t ret = NO_ERROR; + + // If using record thread, protect start sequence to make sure that + // no stop command is processed before the thread is started + if (mClientRecordThread != 0) { + mRecordThreadLock.lock(); + } + + if (android_atomic_or(1, &mActive) == 0) { + setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT); + ret = mAudioRecord->start(); + if (ret == NO_ERROR) { + if (mClientRecordThread != 0) { + mClientRecordThread->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT); + } + } + } + + if (mClientRecordThread != 0) { + mRecordThreadLock.unlock(); + } + + return ret; +} + +status_t AudioRecord::stop() +{ + // If using record thread, protect stop sequence to make sure that + // no start command is processed before requestExit() is called + if (mClientRecordThread != 0) { + mRecordThreadLock.lock(); + } + + if (android_atomic_and(~1, &mActive) == 1) { + if (mPosition) { + mPosition = 0; + releaseBuffer(&mAudioBuffer); + } + mAudioRecord->stop(); + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL); + if (mClientRecordThread != 0) { + mClientRecordThread->requestExit(); + } + } + + if (mClientRecordThread != 0) { + mRecordThreadLock.unlock(); + } + + return NO_ERROR; +} + +bool AudioRecord::stopped() const +{ + return !mActive; +} + +// ------------------------------------------------------------------------- + +status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, bool blocking) +{ + int active = mActive; + int timeout = 0; + status_t result; + audio_track_cblk_t* cblk = mCblk; + + const uint32_t u = cblk->user; + uint32_t s = cblk->server; + + if (u == s) { + Mutex::Autolock _l(cblk->lock); + goto start_loop_here; + while (u == s) { + active = mActive; + if (UNLIKELY(!active)) + return NO_MORE_BUFFERS; + if (UNLIKELY(!blocking)) + return WOULD_BLOCK; + timeout = 0; + result = cblk->cv.waitRelative(cblk->lock, seconds(1)); + if (__builtin_expect(result!=NO_ERROR, false)) { + LOGW( "obtainBuffer timed out (is the CPU pegged?) " + "user=%08x, server=%08x", u, s); + timeout = 1; + } + // read the server count again + start_loop_here: + s = cblk->server; + } + } + + LOGW_IF(timeout, + "*** SERIOUS WARNING *** obtainBuffer() timed out " + "but didn't need to be locked. We recovered, but " + "this shouldn't happen (user=%08x, server=%08x)", u, s); + + audioBuffer->flags = 0; + audioBuffer->channelCount= mChannelCount; + audioBuffer->format = mFormat; + audioBuffer->frameCount = mFrameCount; + audioBuffer->size = cblk->size; + audioBuffer->raw = (int8_t*) + cblk->buffer(cblk->user & audio_track_cblk_t::BUFFER_MASK); + return active ? status_t(NO_ERROR) : status_t(STOPPED); +} + +void AudioRecord::releaseBuffer(Buffer* audioBuffer) +{ + // next buffer... + if (UNLIKELY(mPosition)) { + // clean the remaining part of the buffer + size_t capacity = mAudioBuffer.size - mPosition; + memset(mAudioBuffer.i8 + mPosition, 0, capacity); + } + audio_track_cblk_t* cblk = mCblk; + cblk->stepUser(mBufferCount); +} + +// ------------------------------------------------------------------------- + +ssize_t AudioRecord::read(void* buffer, size_t userSize) +{ + ssize_t read = 0; + do { + if (mPosition == 0) { + status_t err = obtainBuffer(&mAudioBuffer, true); + if (err < 0) { + // out of buffers, return #bytes written + if (err == status_t(NO_MORE_BUFFERS)) + break; + return ssize_t(err); + } + } + + size_t capacity = mAudioBuffer.size - mPosition; + size_t toRead = userSize < capacity ? userSize : capacity; + + memcpy(buffer, mAudioBuffer.i8 + mPosition, toRead); + + buffer = static_cast(buffer) + toRead; + mPosition += toRead; + userSize -= toRead; + capacity -= toRead; + read += toRead; + + if (capacity == 0) { + mPosition = 0; + releaseBuffer(&mAudioBuffer); + } + } while (userSize); + + return read; +} + +// ------------------------------------------------------------------------- + +bool AudioRecord::processAudioBuffer(const sp& thread) +{ + Buffer audioBuffer; + bool more; + + do { + status_t err = obtainBuffer(&audioBuffer, true); + if (err < NO_ERROR) { + LOGE("Error obtaining an audio buffer, giving up."); + return false; + } + more = mCbf(mUserData, audioBuffer); + releaseBuffer(&audioBuffer); + } while (more && !thread->exitPending()); + + // stop the track automatically + this->stop(); + + return true; +} + +// ========================================================================= + +AudioRecord::ClientRecordThread::ClientRecordThread(AudioRecord& receiver) + : Thread(false), mReceiver(receiver) +{ +} + +bool AudioRecord::ClientRecordThread::threadLoop() +{ + return mReceiver.processAudioBuffer(this); +} + +// ------------------------------------------------------------------------- + +}; // namespace android + diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp new file mode 100644 index 0000000..22de463 --- /dev/null +++ b/media/libmedia/AudioSystem.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2006-2007 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_TAG "AudioSystem" +#include +#include +#include +#include +#include + +namespace android { + +// client singleton for AudioFlinger binder interface +Mutex AudioSystem::gLock; +sp AudioSystem::gAudioFlinger; +sp AudioSystem::gDeathNotifier; +audio_error_callback AudioSystem::gAudioErrorCallback = NULL; + +// establish binder interface to AudioFlinger service +const sp& AudioSystem::get_audio_flinger() +{ + Mutex::Autolock _l(gLock); + if (gAudioFlinger.get() == 0) { + sp sm = defaultServiceManager(); + sp binder; + do { + binder = sm->getService(String16("media.audio_flinger")); + if (binder != 0) + break; + LOGW("AudioFlinger not published, waiting..."); + usleep(500000); // 0.5 s + } while(true); + if (gDeathNotifier == NULL) { + gDeathNotifier = new DeathNotifier(); + } else { + if (gAudioErrorCallback) { + gAudioErrorCallback(NO_ERROR); + } + } + binder->linkToDeath(gDeathNotifier); + gAudioFlinger = interface_cast(binder); + } + LOGE_IF(gAudioFlinger==0, "no AudioFlinger!?"); + return gAudioFlinger; +} + +// routing helper functions +status_t AudioSystem::speakerphone(bool state) { + uint32_t routes = state ? ROUTE_SPEAKER : ROUTE_EARPIECE; + return setRouting(MODE_IN_CALL, routes, ROUTE_ALL); +} + +status_t AudioSystem::isSpeakerphoneOn(bool* state) { + uint32_t routes = 0; + status_t s = getRouting(MODE_IN_CALL, &routes); + *state = !!(routes & ROUTE_SPEAKER); + return s; +} + +status_t AudioSystem::bluetoothSco(bool state) { + uint32_t mask = ROUTE_BLUETOOTH; + uint32_t routes = state ? mask : ROUTE_EARPIECE; + return setRouting(MODE_IN_CALL, routes, ROUTE_ALL); +} + +status_t AudioSystem::isBluetoothScoOn(bool* state) { + uint32_t routes = 0; + status_t s = getRouting(MODE_IN_CALL, &routes); + *state = !!(routes & ROUTE_BLUETOOTH); + return s; +} + +status_t AudioSystem::muteMicrophone(bool state) { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->setMicMute(state); +} + +status_t AudioSystem::isMicrophoneMuted(bool* state) { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + *state = af->getMicMute(); + return NO_ERROR; +} + +status_t AudioSystem::setMasterVolume(float value) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + af->setMasterVolume(value); + return NO_ERROR; +} + +status_t AudioSystem::setMasterMute(bool mute) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + af->setMasterMute(mute); + return NO_ERROR; +} + +status_t AudioSystem::getMasterVolume(float* volume) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + *volume = af->masterVolume(); + return NO_ERROR; +} + +status_t AudioSystem::getMasterMute(bool* mute) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + *mute = af->masterMute(); + return NO_ERROR; +} + +status_t AudioSystem::setStreamVolume(int stream, float value) +{ + if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) return BAD_VALUE; + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + af->setStreamVolume(stream, value); + return NO_ERROR; +} + +status_t AudioSystem::setStreamMute(int stream, bool mute) +{ + if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) return BAD_VALUE; + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + af->setStreamMute(stream, mute); + return NO_ERROR; +} + +status_t AudioSystem::getStreamVolume(int stream, float* volume) +{ + if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) return BAD_VALUE; + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + *volume = af->streamVolume(stream); + return NO_ERROR; +} + +status_t AudioSystem::getStreamMute(int stream, bool* mute) +{ + if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) return BAD_VALUE; + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + *mute = af->streamMute(stream); + return NO_ERROR; +} + +status_t AudioSystem::setMode(int mode) +{ + if (mode >= NUM_MODES) return BAD_VALUE; + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->setMode(mode); +} + +status_t AudioSystem::getMode(int* mode) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + *mode = af->getMode(); + return NO_ERROR; +} + +status_t AudioSystem::setRouting(int mode, uint32_t routes, uint32_t mask) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->setRouting(mode, routes, mask); +} + +status_t AudioSystem::getRouting(int mode, uint32_t* routes) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + uint32_t r = af->getRouting(mode); + *routes = r; + return NO_ERROR; +} + +status_t AudioSystem::isMusicActive(bool* state) { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + *state = af->isMusicActive(); + return NO_ERROR; +} + +// Temporary interface, do not use +// TODO: Replace with a more generic key:value get/set mechanism +status_t AudioSystem::setParameter(const char* key, const char* value) { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->setParameter(key, value); +} + +// convert volume steps to natural log scale + +// change this value to change volume scaling +static const float dBPerStep = 0.5f; +// shouldn't need to touch these +static const float dBConvert = -dBPerStep * 2.302585093f / 20.0f; +static const float dBConvertInverse = 1.0f / dBConvert; + +float AudioSystem::linearToLog(int volume) +{ + // float v = volume ? exp(float(100 - volume) * dBConvert) : 0; + // LOGD("linearToLog(%d)=%f", volume, v); + // return v; + return volume ? exp(float(100 - volume) * dBConvert) : 0; +} + +int AudioSystem::logToLinear(float volume) +{ + // int v = volume ? 100 - int(dBConvertInverse * log(volume) + 0.5) : 0; + // LOGD("logTolinear(%d)=%f", v, volume); + // return v; + return volume ? 100 - int(dBConvertInverse * log(volume) + 0.5) : 0; +} + +// --------------------------------------------------------------------------- + +void AudioSystem::DeathNotifier::binderDied(const wp& who) { + Mutex::Autolock _l(AudioSystem::gLock); + AudioSystem::gAudioFlinger.clear(); + if (gAudioErrorCallback) { + gAudioErrorCallback(DEAD_OBJECT); + } + LOGW("AudioFlinger server died!"); +} + +void AudioSystem::setErrorCallback(audio_error_callback cb) { + Mutex::Autolock _l(AudioSystem::gLock); + gAudioErrorCallback = cb; +} + +}; // namespace android + diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp new file mode 100644 index 0000000..298170a --- /dev/null +++ b/media/libmedia/AudioTrack.cpp @@ -0,0 +1,596 @@ +/* //device/extlibs/pv/android/AudioTrack.cpp +** +** Copyright 2007, 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 "AudioTrack" + +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +namespace android { + +// --------------------------------------------------------------------------- + +static volatile size_t gFrameCount = 0; + +size_t AudioTrack::frameCount() +{ + if (gFrameCount) return gFrameCount; + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + gFrameCount = af->frameCount(); + return gFrameCount; +} + +// --------------------------------------------------------------------------- + +AudioTrack::AudioTrack() + : mStatus(NO_INIT) +{ +} + +AudioTrack::AudioTrack( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags, + callback_t cbf, void* user) + : mStatus(NO_INIT) +{ + mStatus = set(streamType, sampleRate, format, channelCount, + bufferCount, flags, cbf, user); +} + +AudioTrack::~AudioTrack() +{ + if (mStatus == NO_ERROR) { + if (mPosition) { + releaseBuffer(&mAudioBuffer); + } + // obtainBuffer() will give up with an error + mAudioTrack->stop(); + if (mAudioTrackThread != 0) { + mAudioTrackThread->requestExitAndWait(); + mAudioTrackThread.clear(); + } + mAudioTrack.clear(); + IPCThreadState::self()->flushCommands(); + } +} + +status_t AudioTrack::set( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags, + callback_t cbf, void* user) +{ + + if (mAudioFlinger != 0) { + LOGE("Track already in use"); + return INVALID_OPERATION; + } + + const sp& audioFlinger = AudioSystem::get_audio_flinger(); + if (audioFlinger == 0) { + LOGE("Could not get audioflinger"); + return NO_INIT; + } + + // handle default values first. + if (streamType == DEFAULT) { + streamType = MUSIC; + } + if (sampleRate == 0) { + sampleRate = audioFlinger->sampleRate(); + } + // these below should probably come from the audioFlinger too... + if (format == 0) { + format = AudioSystem::PCM_16_BIT; + } + if (channelCount == 0) { + channelCount = 2; + } + if (bufferCount == 0) { + bufferCount = 2; + } + + // validate parameters + if (format != AudioSystem::PCM_16_BIT) { + LOGE("Invalid format"); + return BAD_VALUE; + } + if (channelCount != 1 && channelCount != 2) { + LOGE("Invalid channel number"); + return BAD_VALUE; + } + if (bufferCount < 2) { + LOGE("Invalid buffer count"); + return BAD_VALUE; + } + + // create the track + sp track = audioFlinger->createTrack(getpid(), + streamType, sampleRate, format, channelCount, bufferCount, flags); + if (track == 0) { + LOGE("AudioFlinger could not create track"); + return NO_INIT; + } + sp cblk = track->getCblk(); + if (cblk == 0) { + LOGE("Could not get control block"); + return NO_INIT; + } + if (cbf != 0) { + mAudioTrackThread = new AudioTrackThread(*this); + if (mAudioTrackThread == 0) { + LOGE("Could not create callback thread"); + return NO_INIT; + } + } + + mStatus = NO_ERROR; + + mAudioFlinger = audioFlinger; + mAudioTrack = track; + mCblkMemory = cblk; + mCblk = static_cast(cblk->pointer()); + mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + mCblk->volume[0] = mCblk->volume[1] = 0x1000; + mVolume[LEFT] = 1.0f; + mVolume[RIGHT] = 1.0f; + mSampleRate = sampleRate; + mFrameCount = audioFlinger->frameCount(); + mStreamType = streamType; + mFormat = format; + mBufferCount = bufferCount; + mChannelCount = channelCount; + mMuted = false; + mActive = 0; + mReserved = 0; + mCbf = cbf; + mUserData = user; + mLatency = seconds(mFrameCount) / mSampleRate; + mPosition = 0; + return NO_ERROR; +} + +status_t AudioTrack::initCheck() const +{ + return mStatus; +} + +// ------------------------------------------------------------------------- + +nsecs_t AudioTrack::latency() const +{ + return mLatency; +} + +int AudioTrack::streamType() const +{ + return mStreamType; +} + +uint32_t AudioTrack::sampleRate() const +{ + return mSampleRate; +} + +int AudioTrack::format() const +{ + return mFormat; +} + +int AudioTrack::channelCount() const +{ + return mChannelCount; +} + +int AudioTrack::bufferCount() const +{ + return mBufferCount; +} + +// ------------------------------------------------------------------------- + +void AudioTrack::start() +{ + sp t = mAudioTrackThread; + + LOGV("start"); + if (t != 0) { + if (t->exitPending()) { + if (t->requestExitAndWait() == WOULD_BLOCK) { + LOGE("AudioTrack::start called from thread"); + return; + } + } + t->mLock.lock(); + } + + if (android_atomic_or(1, &mActive) == 0) { + if (t != 0) { + t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT); + } else { + setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT); + } + mAudioTrack->start(); + } + + if (t != 0) { + t->mLock.unlock(); + } +} + +void AudioTrack::stop() +{ + sp t = mAudioTrackThread; + + LOGV("stop"); + if (t != 0) { + t->mLock.lock(); + } + + if (android_atomic_and(~1, &mActive) == 1) { + if (mPosition) { + releaseBuffer(&mAudioBuffer); + } + mAudioTrack->stop(); + if (t != 0) { + t->requestExit(); + } else { + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL); + } + } + + if (t != 0) { + t->mLock.unlock(); + } +} + +bool AudioTrack::stopped() const +{ + return !mActive; +} + +void AudioTrack::flush() +{ + LOGV("flush"); + if (!mActive) { + mCblk->lock.lock(); + mAudioTrack->flush(); + // Release AudioTrack callback thread in case it was waiting for new buffers + // in AudioTrack::obtainBuffer() + mCblk->cv.signal(); + mCblk->lock.unlock(); + } +} + +void AudioTrack::pause() +{ + LOGV("pause"); + if (android_atomic_and(~1, &mActive) == 1) { + mActive = 0; + mAudioTrack->pause(); + } +} + +void AudioTrack::mute(bool e) +{ + mAudioTrack->mute(e); + mMuted = e; +} + +bool AudioTrack::muted() const +{ + return mMuted; +} + +void AudioTrack::setVolume(float left, float right) +{ + mVolume[LEFT] = left; + mVolume[RIGHT] = right; + + // write must be atomic + mCblk->volumeLR = (int32_t(int16_t(left * 0x1000)) << 16) | int16_t(right * 0x1000); +} + +void AudioTrack::getVolume(float* left, float* right) +{ + *left = mVolume[LEFT]; + *right = mVolume[RIGHT]; +} + +void AudioTrack::setSampleRate(int rate) +{ + if (rate > MAX_SAMPLE_RATE) rate = MAX_SAMPLE_RATE; + mCblk->sampleRate = rate; +} + +uint32_t AudioTrack::getSampleRate() +{ + return uint32_t(mCblk->sampleRate); +} + +// ------------------------------------------------------------------------- + +status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, bool blocking) +{ + int active; + int timeout = 0; + status_t result; + audio_track_cblk_t* cblk = mCblk; + + uint32_t u = cblk->user; + uint32_t u_seq = u & audio_track_cblk_t::SEQUENCE_MASK; + uint32_t u_buf = u & audio_track_cblk_t::BUFFER_MASK; + + uint32_t s = cblk->server; + uint32_t s_seq = s & audio_track_cblk_t::SEQUENCE_MASK; + uint32_t s_buf = s & audio_track_cblk_t::BUFFER_MASK; + + LOGW_IF(u_seq < s_seq, "user doesn't fill buffers fast enough"); + + if (u_seq > s_seq && u_buf == s_buf) { + Mutex::Autolock _l(cblk->lock); + goto start_loop_here; + while (u_seq > s_seq && u_buf == s_buf) { + active = mActive; + if (UNLIKELY(!active)) { + LOGV("Not active and NO_MORE_BUFFERS"); + return NO_MORE_BUFFERS; + } + if (UNLIKELY(!blocking)) + return WOULD_BLOCK; + timeout = 0; + result = cblk->cv.waitRelative(cblk->lock, seconds(1)); + if (__builtin_expect(result!=NO_ERROR, false)) { + LOGW( "obtainBuffer timed out (is the CPU pegged?) " + "user=%08x, server=%08x", u, s); + mAudioTrack->start(); // FIXME: Wake up audioflinger + timeout = 1; + } + // Read user count in case a flush has reset while we where waiting on cv. + u = cblk->user; + u_seq = u & audio_track_cblk_t::SEQUENCE_MASK; + u_buf = u & audio_track_cblk_t::BUFFER_MASK; + + // read the server count again + start_loop_here: + s = cblk->server; + s_seq = s & audio_track_cblk_t::SEQUENCE_MASK; + s_buf = s & audio_track_cblk_t::BUFFER_MASK; + } + } + + LOGW_IF(timeout, + "*** SERIOUS WARNING *** obtainBuffer() timed out " + "but didn't need to be locked. We recovered, but " + "this shouldn't happen (user=%08x, server=%08x)", u, s); + + audioBuffer->flags = mMuted ? Buffer::MUTE : 0; + audioBuffer->channelCount= mChannelCount; + audioBuffer->format = mFormat; + audioBuffer->frameCount = mFrameCount; + audioBuffer->size = cblk->size; + audioBuffer->raw = (int8_t *)cblk->buffer(u_buf); + active = mActive; + return active ? status_t(NO_ERROR) : status_t(STOPPED); +} + +void AudioTrack::releaseBuffer(Buffer* audioBuffer) +{ + // next buffer... + if (UNLIKELY(mPosition)) { + // clean the remaining part of the buffer + size_t capacity = mAudioBuffer.size - mPosition; + memset(mAudioBuffer.i8 + mPosition, 0, capacity); + mPosition = 0; + } + audio_track_cblk_t* cblk = mCblk; + cblk->stepUser(mBufferCount); +} + +// ------------------------------------------------------------------------- + +ssize_t AudioTrack::write(const void* buffer, size_t userSize) +{ + if (ssize_t(userSize) < 0) { + // sanity-check. user is most-likely passing an error code. + LOGE("AudioTrack::write(buffer=%p, size=%u (%d)", + buffer, userSize, userSize); + return BAD_VALUE; + } + + LOGV("write %d bytes, mActive=%d", userSize, mActive); + ssize_t written = 0; + do { + if (mPosition == 0) { + status_t err = obtainBuffer(&mAudioBuffer, true); + if (err < 0) { + // out of buffers, return #bytes written + if (err == status_t(NO_MORE_BUFFERS)) + break; + return ssize_t(err); + } + } + + size_t capacity = mAudioBuffer.size - mPosition; + size_t toWrite = userSize < capacity ? userSize : capacity; + + memcpy(mAudioBuffer.i8 + mPosition, buffer, toWrite); + buffer = static_cast(buffer) + toWrite; + mPosition += toWrite; + userSize -= toWrite; + capacity -= toWrite; + written += toWrite; + + if (capacity == 0) { + mPosition = 0; + releaseBuffer(&mAudioBuffer); + } + } while (userSize); + + return written; +} + +// ------------------------------------------------------------------------- + +bool AudioTrack::processAudioBuffer(const sp& thread) +{ + Buffer audioBuffer; + + status_t err = obtainBuffer(&audioBuffer, true); + if (err < NO_ERROR) { + LOGE("Error obtaining an audio buffer, giving up."); + return false; + } + if (err == status_t(STOPPED)) return false; + mCbf(mUserData, audioBuffer); + releaseBuffer(&audioBuffer); + + return true; +} + +status_t AudioTrack::dump(int fd, const Vector& args) const +{ + + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + result.append(" AudioTrack::dump\n"); + snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n", mStreamType, mVolume[0], mVolume[1]); + result.append(buffer); + snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d), buffer count(%d)\n", mFormat, mChannelCount, mFrameCount, mBufferCount); + result.append(buffer); + snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d), reserved(%d)\n", mSampleRate, mStatus, mMuted, mReserved); + result.append(buffer); + snprintf(buffer, 255, " active(%d), latency (%lld), position(%d)\n", mActive, mLatency, mPosition); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// ========================================================================= + +AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver) + : Thread(false), mReceiver(receiver) +{ +} + +bool AudioTrack::AudioTrackThread::threadLoop() +{ + return mReceiver.processAudioBuffer(this); +} + +status_t AudioTrack::AudioTrackThread::readyToRun() +{ + return NO_ERROR; +} + +void AudioTrack::AudioTrackThread::onFirstRef() +{ +} + +// ========================================================================= + +audio_track_cblk_t::audio_track_cblk_t() + : user(0), server(0), volumeLR(0), buffers(0), size(0) +{ +} + +uint32_t audio_track_cblk_t::stepUser(int bufferCount) +{ + uint32_t u = this->user; + uint32_t u_seq = u & audio_track_cblk_t::SEQUENCE_MASK; + uint32_t u_buf = u & audio_track_cblk_t::BUFFER_MASK; + if (++u_buf >= uint32_t(bufferCount)) { + u_seq += 0x100; + u_buf = 0; + } + u = u_seq | u_buf; + this->user = u; + return u; +} + +bool audio_track_cblk_t::stepServer(int bufferCount) +{ + // the code below simulates lock-with-timeout + // we MUST do this to protect the AudioFlinger server + // as this lock is shared with the client. + status_t err; + + err = lock.tryLock(); + if (err == -EBUSY) { // just wait a bit + usleep(1000); + err = lock.tryLock(); + } + if (err != NO_ERROR) { + // probably, the client just died. + return false; + } + + uint32_t s = this->server; + uint32_t s_seq = s & audio_track_cblk_t::SEQUENCE_MASK; + uint32_t s_buf = s & audio_track_cblk_t::BUFFER_MASK; + s_buf++; + if (s_buf >= uint32_t(bufferCount)) { + s_seq += 0x100; + s_buf = 0; + } + s = s_seq | s_buf; + + this->server = s; + cv.signal(); + lock.unlock(); + return true; +} + +void* audio_track_cblk_t::buffer(int id) const +{ + return (char*)this->buffers + id * this->size; +} + +// ------------------------------------------------------------------------- + +}; // namespace android + diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp new file mode 100644 index 0000000..474381b --- /dev/null +++ b/media/libmedia/IAudioFlinger.cpp @@ -0,0 +1,450 @@ +/* //device/extlibs/pv/android/IAudioflinger.cpp +** +** Copyright 2007, 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_TAG "IAudioFlinger" +#include + +#include +#include + +#include + +#include + +namespace android { + +enum { + CREATE_TRACK = IBinder::FIRST_CALL_TRANSACTION, + OPEN_RECORD, + SAMPLE_RATE, + CHANNEL_COUNT, + FORMAT, + FRAME_COUNT, + SET_MASTER_VOLUME, + SET_MASTER_MUTE, + MASTER_VOLUME, + MASTER_MUTE, + SET_STREAM_VOLUME, + SET_STREAM_MUTE, + STREAM_VOLUME, + STREAM_MUTE, + SET_MODE, + GET_MODE, + SET_ROUTING, + GET_ROUTING, + SET_MIC_MUTE, + GET_MIC_MUTE, + IS_MUSIC_ACTIVE, + SET_PARAMETER, +}; + +class BpAudioFlinger : public BpInterface +{ +public: + BpAudioFlinger(const sp& impl) + : BpInterface(impl) + { + } + + virtual sp createTrack( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(pid); + data.writeInt32(streamType); + data.writeInt32(sampleRate); + data.writeInt32(format); + data.writeInt32(channelCount); + data.writeInt32(bufferCount); + data.writeInt32(flags); + status_t status = remote()->transact(CREATE_TRACK, data, &reply); + if ( status != NO_ERROR) { + LOGE("createTrack error: %s", strerror(-status)); + } + + return interface_cast(reply.readStrongBinder()); + } + + virtual sp openRecord( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(pid); + data.writeInt32(streamType); + data.writeInt32(sampleRate); + data.writeInt32(format); + data.writeInt32(channelCount); + data.writeInt32(bufferCount); + data.writeInt32(flags); + remote()->transact(OPEN_RECORD, data, &reply); + return interface_cast(reply.readStrongBinder()); + } + + virtual uint32_t sampleRate() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(SAMPLE_RATE, data, &reply); + return reply.readInt32(); + } + + virtual int channelCount() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(CHANNEL_COUNT, data, &reply); + return reply.readInt32(); + } + + virtual int format() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(FORMAT, data, &reply); + return reply.readInt32(); + } + + virtual size_t frameCount() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(FRAME_COUNT, data, &reply); + return reply.readInt32(); + } + + virtual status_t setMasterVolume(float value) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeFloat(value); + remote()->transact(SET_MASTER_VOLUME, data, &reply); + return reply.readInt32(); + } + + virtual status_t setMasterMute(bool muted) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(muted); + remote()->transact(SET_MASTER_MUTE, data, &reply); + return reply.readInt32(); + } + + virtual float masterVolume() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(MASTER_VOLUME, data, &reply); + return reply.readFloat(); + } + + virtual bool masterMute() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(MASTER_MUTE, data, &reply); + return reply.readInt32(); + } + + virtual status_t setStreamVolume(int stream, float value) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(stream); + data.writeFloat(value); + remote()->transact(SET_STREAM_VOLUME, data, &reply); + return reply.readInt32(); + } + + virtual status_t setStreamMute(int stream, bool muted) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(stream); + data.writeInt32(muted); + remote()->transact(SET_STREAM_MUTE, data, &reply); + return reply.readInt32(); + } + + virtual float streamVolume(int stream) const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(stream); + remote()->transact(STREAM_VOLUME, data, &reply); + return reply.readFloat(); + } + + virtual bool streamMute(int stream) const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(stream); + remote()->transact(STREAM_MUTE, data, &reply); + return reply.readInt32(); + } + + virtual status_t setRouting(int mode, uint32_t routes, uint32_t mask) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(mode); + data.writeInt32(routes); + data.writeInt32(mask); + remote()->transact(SET_ROUTING, data, &reply); + return reply.readInt32(); + } + + virtual uint32_t getRouting(int mode) const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(mode); + remote()->transact(GET_ROUTING, data, &reply); + return reply.readInt32(); + } + + virtual status_t setMode(int mode) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(mode); + remote()->transact(SET_MODE, data, &reply); + return reply.readInt32(); + } + + virtual int getMode() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(GET_MODE, data, &reply); + return reply.readInt32(); + } + + virtual status_t setMicMute(bool state) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(state); + remote()->transact(SET_MIC_MUTE, data, &reply); + return reply.readInt32(); + } + + virtual bool getMicMute() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(GET_MIC_MUTE, data, &reply); + return reply.readInt32(); + } + + virtual bool isMusicActive() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(IS_MUSIC_ACTIVE, data, &reply); + return reply.readInt32(); + } + + virtual status_t setParameter(const char* key, const char* value) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeCString(key); + data.writeCString(value); + remote()->transact(SET_PARAMETER, data, &reply); + return reply.readInt32(); + } +}; + +IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnAudioFlinger::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case CREATE_TRACK: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + pid_t pid = data.readInt32(); + int streamType = data.readInt32(); + uint32_t sampleRate = data.readInt32(); + int format = data.readInt32(); + int channelCount = data.readInt32(); + size_t bufferCount = data.readInt32(); + uint32_t flags = data.readInt32(); + sp track = createTrack(pid, + streamType, sampleRate, format, + channelCount, bufferCount, flags); + reply->writeStrongBinder(track->asBinder()); + return NO_ERROR; + } break; + case OPEN_RECORD: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + pid_t pid = data.readInt32(); + int streamType = data.readInt32(); + uint32_t sampleRate = data.readInt32(); + int format = data.readInt32(); + int channelCount = data.readInt32(); + size_t bufferCount = data.readInt32(); + uint32_t flags = data.readInt32(); + sp record = openRecord(pid, streamType, + sampleRate, format, channelCount, bufferCount, flags); + reply->writeStrongBinder(record->asBinder()); + return NO_ERROR; + } break; + case SAMPLE_RATE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( sampleRate() ); + return NO_ERROR; + } break; + case CHANNEL_COUNT: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( channelCount() ); + return NO_ERROR; + } break; + case FORMAT: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( format() ); + return NO_ERROR; + } break; + case FRAME_COUNT: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( frameCount() ); + return NO_ERROR; + } break; + case SET_MASTER_VOLUME: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( setMasterVolume(data.readFloat()) ); + return NO_ERROR; + } break; + case SET_MASTER_MUTE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( setMasterMute(data.readInt32()) ); + return NO_ERROR; + } break; + case MASTER_VOLUME: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeFloat( masterVolume() ); + return NO_ERROR; + } break; + case MASTER_MUTE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( masterMute() ); + return NO_ERROR; + } break; + case SET_STREAM_VOLUME: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int stream = data.readInt32(); + reply->writeInt32( setStreamVolume(stream, data.readFloat()) ); + return NO_ERROR; + } break; + case SET_STREAM_MUTE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int stream = data.readInt32(); + reply->writeInt32( setStreamMute(stream, data.readInt32()) ); + return NO_ERROR; + } break; + case STREAM_VOLUME: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int stream = data.readInt32(); + reply->writeFloat( streamVolume(stream) ); + return NO_ERROR; + } break; + case STREAM_MUTE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int stream = data.readInt32(); + reply->writeInt32( streamMute(stream) ); + return NO_ERROR; + } break; + case SET_ROUTING: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int mode = data.readInt32(); + uint32_t routes = data.readInt32(); + uint32_t mask = data.readInt32(); + reply->writeInt32( setRouting(mode, routes, mask) ); + return NO_ERROR; + } break; + case GET_ROUTING: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int mode = data.readInt32(); + reply->writeInt32( getRouting(mode) ); + return NO_ERROR; + } break; + case SET_MODE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int mode = data.readInt32(); + reply->writeInt32( setMode(mode) ); + return NO_ERROR; + } break; + case GET_MODE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( getMode() ); + return NO_ERROR; + } break; + case SET_MIC_MUTE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int state = data.readInt32(); + reply->writeInt32( setMicMute(state) ); + return NO_ERROR; + } break; + case GET_MIC_MUTE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( getMicMute() ); + return NO_ERROR; + } break; + case IS_MUSIC_ACTIVE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( isMusicActive() ); + return NO_ERROR; + } break; + case SET_PARAMETER: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + const char *key = data.readCString(); + const char *value = data.readCString(); + reply->writeInt32( setParameter(key, value) ); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp new file mode 100644 index 0000000..6e42dac --- /dev/null +++ b/media/libmedia/IAudioRecord.cpp @@ -0,0 +1,100 @@ +/* +** +** Copyright 2007, 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 +#include + +#include + +#include + +namespace android { + +enum { + GET_CBLK = IBinder::FIRST_CALL_TRANSACTION, + START, + STOP +}; + +class BpAudioRecord : public BpInterface +{ +public: + BpAudioRecord(const sp& impl) + : BpInterface(impl) + { + } + + virtual status_t start() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor()); + remote()->transact(START, data, &reply); + return reply.readInt32(); + } + + virtual void stop() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor()); + remote()->transact(STOP, data, &reply); + } + + virtual sp getCblk() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor()); + remote()->transact(GET_CBLK, data, &reply); + return interface_cast(reply.readStrongBinder()); + } +}; + +IMPLEMENT_META_INTERFACE(AudioRecord, "android.media.IAudioRecord"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnAudioRecord::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case GET_CBLK: { + CHECK_INTERFACE(IAudioRecord, data, reply); + reply->writeStrongBinder(getCblk()->asBinder()); + return NO_ERROR; + } break; + case START: { + CHECK_INTERFACE(IAudioRecord, data, reply); + reply->writeInt32(start()); + return NO_ERROR; + } break; + case STOP: { + CHECK_INTERFACE(IAudioRecord, data, reply); + stop(); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android + diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp new file mode 100644 index 0000000..abc202d --- /dev/null +++ b/media/libmedia/IAudioTrack.cpp @@ -0,0 +1,140 @@ +/* //device/extlibs/pv/android/IAudioTrack.cpp +** +** Copyright 2007, 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 +#include + +#include + +#include + +namespace android { + +enum { + GET_CBLK = IBinder::FIRST_CALL_TRANSACTION, + START, + STOP, + FLUSH, + MUTE, + PAUSE +}; + +class BpAudioTrack : public BpInterface +{ +public: + BpAudioTrack(const sp& impl) + : BpInterface(impl) + { + } + + virtual status_t start() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + remote()->transact(START, data, &reply); + return reply.readInt32(); + } + + virtual void stop() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + remote()->transact(STOP, data, &reply); + } + + virtual void flush() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + remote()->transact(FLUSH, data, &reply); + } + + virtual void mute(bool e) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + data.writeInt32(e); + remote()->transact(MUTE, data, &reply); + } + + virtual void pause() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + remote()->transact(PAUSE, data, &reply); + } + + virtual sp getCblk() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + remote()->transact(GET_CBLK, data, &reply); + return interface_cast(reply.readStrongBinder()); + } +}; + +IMPLEMENT_META_INTERFACE(AudioTrack, "android.media.IAudioTrack"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnAudioTrack::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case GET_CBLK: { + CHECK_INTERFACE(IAudioTrack, data, reply); + reply->writeStrongBinder(getCblk()->asBinder()); + return NO_ERROR; + } break; + case START: { + CHECK_INTERFACE(IAudioTrack, data, reply); + reply->writeInt32(start()); + return NO_ERROR; + } break; + case STOP: { + CHECK_INTERFACE(IAudioTrack, data, reply); + stop(); + return NO_ERROR; + } break; + case FLUSH: { + CHECK_INTERFACE(IAudioTrack, data, reply); + flush(); + return NO_ERROR; + } break; + case MUTE: { + CHECK_INTERFACE(IAudioTrack, data, reply); + mute( data.readInt32() ); + return NO_ERROR; + } break; + case PAUSE: { + CHECK_INTERFACE(IAudioTrack, data, reply); + pause(); + return NO_ERROR; + } + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android + diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp new file mode 100644 index 0000000..8385114 --- /dev/null +++ b/media/libmedia/IMediaPlayer.cpp @@ -0,0 +1,295 @@ +/* +** +** Copyright 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 +#include + +#include + +#include +#include + +namespace android { + +enum { + DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, + SET_VIDEO_SURFACE, + PREPARE_ASYNC, + START, + STOP, + IS_PLAYING, + PAUSE, + GET_VIDEO_SIZE, + SEEK_TO, + GET_CURRENT_POSITION, + GET_DURATION, + RESET, + SET_AUDIO_STREAM_TYPE, + SET_LOOPING, + SET_VOLUME +}; + +class BpMediaPlayer: public BpInterface +{ +public: + BpMediaPlayer(const sp& impl) + : BpInterface(impl) + { + } + + // disconnect from media player service + void disconnect() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(DISCONNECT, data, &reply); + } + + status_t setVideoSurface(const sp& surface) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + data.writeStrongBinder(surface->asBinder()); + remote()->transact(SET_VIDEO_SURFACE, data, &reply); + return reply.readInt32(); + } + + status_t prepareAsync() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(PREPARE_ASYNC, data, &reply); + return reply.readInt32(); + } + + status_t start() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(START, data, &reply); + return reply.readInt32(); + } + + status_t stop() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(STOP, data, &reply); + return reply.readInt32(); + } + + status_t isPlaying(bool* state) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(IS_PLAYING, data, &reply); + *state = reply.readInt32(); + return reply.readInt32(); + } + + status_t pause() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(PAUSE, data, &reply); + return reply.readInt32(); + } + + status_t getVideoSize(int* w, int* h) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(GET_VIDEO_SIZE, data, &reply); + *w = reply.readInt32(); + *h = reply.readInt32(); + return reply.readInt32(); + } + + status_t seekTo(int msec) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + data.writeInt32(msec); + remote()->transact(SEEK_TO, data, &reply); + return reply.readInt32(); + } + + status_t getCurrentPosition(int* msec) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(GET_CURRENT_POSITION, data, &reply); + *msec = reply.readInt32(); + return reply.readInt32(); + } + + status_t getDuration(int* msec) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(GET_DURATION, data, &reply); + *msec = reply.readInt32(); + return reply.readInt32(); + } + + status_t reset() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(RESET, data, &reply); + return reply.readInt32(); + } + + status_t setAudioStreamType(int type) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + data.writeInt32(type); + remote()->transact(SET_AUDIO_STREAM_TYPE, data, &reply); + return reply.readInt32(); + } + + status_t setLooping(int loop) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + data.writeInt32(loop); + remote()->transact(SET_LOOPING, data, &reply); + return reply.readInt32(); + } + + status_t setVolume(float leftVolume, float rightVolume) + { + Parcel data, reply; + data.writeFloat(leftVolume); + data.writeFloat(rightVolume); + remote()->transact(SET_VOLUME, data, &reply); + return reply.readInt32(); + } +}; + +IMPLEMENT_META_INTERFACE(MediaPlayer, "android.hardware.IMediaPlayer"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnMediaPlayer::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case DISCONNECT: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + disconnect(); + return NO_ERROR; + } break; + case SET_VIDEO_SURFACE: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + sp surface = interface_cast(data.readStrongBinder()); + reply->writeInt32(setVideoSurface(surface)); + return NO_ERROR; + } break; + case PREPARE_ASYNC: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(prepareAsync()); + return NO_ERROR; + } break; + case START: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(start()); + return NO_ERROR; + } break; + case STOP: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(stop()); + return NO_ERROR; + } break; + case IS_PLAYING: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + bool state; + status_t ret = isPlaying(&state); + reply->writeInt32(state); + reply->writeInt32(ret); + return NO_ERROR; + } break; + case PAUSE: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(pause()); + return NO_ERROR; + } break; + case GET_VIDEO_SIZE: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + int w, h; + status_t ret = getVideoSize(&w, &h); + reply->writeInt32(w); + reply->writeInt32(h); + reply->writeInt32(ret); + return NO_ERROR; + } break; + case SEEK_TO: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(seekTo(data.readInt32())); + return NO_ERROR; + } break; + case GET_CURRENT_POSITION: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + int msec; + status_t ret = getCurrentPosition(&msec); + reply->writeInt32(msec); + reply->writeInt32(ret); + return NO_ERROR; + } break; + case GET_DURATION: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + int msec; + status_t ret = getDuration(&msec); + reply->writeInt32(msec); + reply->writeInt32(ret); + return NO_ERROR; + } break; + case RESET: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(reset()); + return NO_ERROR; + } break; + case SET_AUDIO_STREAM_TYPE: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(setAudioStreamType(data.readInt32())); + return NO_ERROR; + } break; + case SET_LOOPING: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(setLooping(data.readInt32())); + return NO_ERROR; + } break; + case SET_VOLUME: { + reply->writeInt32(setVolume(data.readFloat(), data.readFloat())); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/media/libmedia/IMediaPlayerClient.cpp b/media/libmedia/IMediaPlayerClient.cpp new file mode 100644 index 0000000..65022cd --- /dev/null +++ b/media/libmedia/IMediaPlayerClient.cpp @@ -0,0 +1,77 @@ +/* +** +** Copyright 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 +#include +#include + +#include + +namespace android { + +enum { + NOTIFY = IBinder::FIRST_CALL_TRANSACTION, +}; + +class BpMediaPlayerClient: public BpInterface +{ +public: + BpMediaPlayerClient(const sp& impl) + : BpInterface(impl) + { + } + + virtual void notify(int msg, int ext1, int ext2) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerClient::getInterfaceDescriptor()); + data.writeInt32(msg); + data.writeInt32(ext1); + data.writeInt32(ext2); + remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(MediaPlayerClient, "android.hardware.IMediaPlayerClient"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnMediaPlayerClient::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case NOTIFY: { + CHECK_INTERFACE(IMediaPlayerClient, data, reply); + int msg = data.readInt32(); + int ext1 = data.readInt32(); + int ext2 = data.readInt32(); + notify(msg, ext1, ext2); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android + diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp new file mode 100644 index 0000000..b087100 --- /dev/null +++ b/media/libmedia/IMediaPlayerService.cpp @@ -0,0 +1,157 @@ +/* +** +** Copyright 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 +#include +#include + +#include +#include + +namespace android { + +enum { + CREATE_URL = IBinder::FIRST_CALL_TRANSACTION, + CREATE_FD, + DECODE_URL, + DECODE_FD, +}; + +class BpMediaPlayerService: public BpInterface +{ +public: + BpMediaPlayerService(const sp& impl) + : BpInterface(impl) + { + } + + virtual sp create(pid_t pid, const sp& client, const char* url) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeInt32(pid); + data.writeStrongBinder(client->asBinder()); + data.writeCString(url); + remote()->transact(CREATE_URL, data, &reply); + return interface_cast(reply.readStrongBinder()); + } + + virtual sp create(pid_t pid, const sp& client, int fd, int64_t offset, int64_t length) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeInt32(pid); + data.writeStrongBinder(client->asBinder()); + data.writeFileDescriptor(fd); + data.writeInt64(offset); + data.writeInt64(length); + remote()->transact(CREATE_FD, data, &reply); + return interface_cast(reply.readStrongBinder()); + } + + virtual sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeCString(url); + remote()->transact(DECODE_URL, data, &reply); + *pSampleRate = uint32_t(reply.readInt32()); + *pNumChannels = reply.readInt32(); + return interface_cast(reply.readStrongBinder()); + } + + virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeFileDescriptor(fd); + data.writeInt64(offset); + data.writeInt64(length); + remote()->transact(DECODE_FD, data, &reply); + *pSampleRate = uint32_t(reply.readInt32()); + *pNumChannels = reply.readInt32(); + return interface_cast(reply.readStrongBinder()); + } +}; + +IMPLEMENT_META_INTERFACE(MediaPlayerService, "android.hardware.IMediaPlayerService"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnMediaPlayerService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case CREATE_URL: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + pid_t pid = data.readInt32(); + sp client = interface_cast(data.readStrongBinder()); + const char* url = data.readCString(); + sp player = create(pid, client, url); + reply->writeStrongBinder(player->asBinder()); + return NO_ERROR; + } break; + case CREATE_FD: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + pid_t pid = data.readInt32(); + sp client = interface_cast(data.readStrongBinder()); + int fd = dup(data.readFileDescriptor()); + int64_t offset = data.readInt64(); + int64_t length = data.readInt64(); + sp player = create(pid, client, fd, offset, length); + reply->writeStrongBinder(player->asBinder()); + return NO_ERROR; + } break; + case DECODE_URL: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + const char* url = data.readCString(); + uint32_t sampleRate; + int numChannels; + sp player = decode(url, &sampleRate, &numChannels); + reply->writeInt32(sampleRate); + reply->writeInt32(numChannels); + reply->writeStrongBinder(player->asBinder()); + return NO_ERROR; + } break; + case DECODE_FD: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + int fd = dup(data.readFileDescriptor()); + int64_t offset = data.readInt64(); + int64_t length = data.readInt64(); + uint32_t sampleRate; + int numChannels; + sp player = decode(fd, offset, length, &sampleRate, &numChannels); + reply->writeInt32(sampleRate); + reply->writeInt32(numChannels); + reply->writeStrongBinder(player->asBinder()); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/media/libmedia/MODULE_LICENSE_APACHE2 b/media/libmedia/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libmedia/NOTICE b/media/libmedia/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/media/libmedia/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp new file mode 100644 index 0000000..89ab2be --- /dev/null +++ b/media/libmedia/ToneGenerator.cpp @@ -0,0 +1,662 @@ +/* + * 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ToneGenerator" +#include + +#include +#include +#include +#include +#include +#include +#include "media/ToneGenerator.h" + +namespace android { + +// Descriptors for all available tones (See ToneGenerator::ToneDescriptor class declaration for details) +const ToneGenerator::ToneDescriptor + ToneGenerator::toneDescriptors[NUM_TONES] = { + // waveFreq[] segments[] repeatCnt + { { 1336, 941, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_0 + { { 1209, 697, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_1 + { { 1336, 697, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_2 + { { 1477, 697, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_3 + { { 1209, 770, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_4 + { { 1336, 770, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_5 + { { 1477, 770, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_6 + { { 1209, 852, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_7 + { { 1336, 852, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_8 + { { 1477, 852, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_9 + { { 1209, 941, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_S + { { 1477, 941, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_P + { { 1633, 697, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_A + { { 1633, 770, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_B + { { 1633, 852, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_C + { { 1633, 941, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_D + { { 425, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_DIAL + { { 425, 0 }, { 500, 500, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_BUSY + { { 425, 0 }, { 200, 200, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_CONGESTION + { { 425, 0 }, { 200, 0 }, 0 }, // TONE_SUP_RADIO_ACK + { { 425, 0 }, { 200, 200, 0 }, 2 }, // TONE_SUP_RADIO_NOTAVAIL + { { 950, 1400, 1800, 0 }, { 330, 1000, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_ERROR + { { 425, 0 }, { 200, 600, 200, 3000, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_CALL_WAITING + { { 425, 0 }, { 1000, 4000, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_RINGTONE + { { 400, 1200, 0 }, { 35, 0 }, 0 }, // TONE_PROP_BEEP + { { 1200, 0 }, { 100, 100, 0 }, 1 }, // TONE_PROP_ACK + { { 300, 400, 500, 0 }, { 400, 0 }, 0 }, // TONE_PROP_NACK + { { 400, 1200, 0 }, { 200, 0 }, 0 }, // TONE_PROP_PROMPT + { { 400, 1200, 0 }, { 35, 200, 35, 0 }, 0 } // TONE_PROP_BEEP2 + }; + +//////////////////////////////////////////////////////////////////////////////// +// ToneGenerator class Implementation +//////////////////////////////////////////////////////////////////////////////// + + +//---------------------------------- public methods ---------------------------- + + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::ToneGenerator() +// +// Description: Constructor. Initializes the tone sequencer, intantiates required sine wave +// generators, instantiates output audio track. +// +// Input: +// toneType: Type of tone generated (values in enum tone_type) +// streamType: Type of stream used for tone playback (enum AudioTrack::stream_type) +// volume: volume applied to tone (0.0 to 1.0) +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +ToneGenerator::ToneGenerator(int streamType, float volume) { + const sp& lpAudioFlinger = AudioSystem::get_audio_flinger(); + + LOGV("ToneGenerator constructor: streamType=%d, volume=%f\n", streamType, volume); + + mState = TONE_IDLE; + mpAudioTrack = 0; + mpToneDesc = 0; + mpNewToneDesc = 0; + + if (lpAudioFlinger == 0) { + LOGE("Unable to marshal AudioFlinger"); + goto ToneGenerator_exit; + } + + mSamplingRate = lpAudioFlinger->sampleRate(); + + mVolume = volume; + // Open audio track in mono, PCM 16bit, default sampling rate, 2 buffers + mpAudioTrack + = new AudioTrack(streamType, 0, AudioSystem::PCM_16_BIT, 1, NUM_PCM_BUFFERS, 0, audioCallback, this); + + if (mpAudioTrack == 0) { + LOGE("AudioTrack allocation failed"); + goto ToneGenerator_exit; + } + LOGV("Create Track: %p\n", mpAudioTrack); + + if (mpAudioTrack->initCheck() != NO_ERROR) { + LOGE("AudioTrack->initCheck failed"); + goto ToneGenerator_exit; + } + + mpAudioTrack->setVolume(volume, volume); + + LOGV("ToneGenerator INIT OK, time: %d\n", (unsigned int)(systemTime()/1000000)); + + mState = TONE_INIT; + + return; + +ToneGenerator_exit: + + // Cleanup + if (mpAudioTrack) { + LOGV("Delete Track I: %p\n", mpAudioTrack); + delete mpAudioTrack; + } + + LOGV("!!!ToneGenerator INIT FAILED!!!\n"); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::~ToneGenerator() +// +// Description: Destructor. Stop sound playback and delete audio track if +// needed and delete sine wave generators. +// +// Input: +// none +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +ToneGenerator::~ToneGenerator() { + LOGV("ToneGenerator destructor\n"); + + if (mpAudioTrack) { + stopTone(); + LOGV("Delete Track: %p\n", mpAudioTrack); + delete mpAudioTrack; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::startTone() +// +// Description: Starts tone playback. +// +// Input: +// none +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +bool ToneGenerator::startTone(int toneType) { + bool lResult = false; + + if (mState == TONE_IDLE || toneType >= NUM_TONES) + return lResult; + + LOGV("startTone\n"); + + mLock.lock(); + + // Get descriptor for requested tone + mpNewToneDesc = &toneDescriptors[toneType]; + + if (mState == TONE_INIT) { + if (prepareWave()) { + LOGV("Immediate start, time %d\n", (unsigned int)(systemTime()/1000000)); + + mState = TONE_STARTING; + mLock.unlock(); + mpAudioTrack->start(); + mLock.lock(); + if (mState == TONE_STARTING) { + if (mWaitCbkCond.waitRelative(mLock, seconds(1)) != NO_ERROR) + LOGE("--- timed out"); + } + + if (mState == TONE_PLAYING) + lResult = true; + } + } else { + LOGV("Delayed start\n"); + + mState = TONE_RESTARTING; + if (mWaitCbkCond.waitRelative(mLock, seconds(1)) == NO_ERROR) { + if (mState != TONE_INIT) { + lResult = true; + } + LOGV("cond received"); + } else { + LOGE("--- timed out"); + } + } + mLock.unlock(); + + LOGV("Tone started, time %d\n", (unsigned int)(systemTime()/1000000)); + + return lResult; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::stopTone() +// +// Description: Stops tone playback. +// +// Input: +// none +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +void ToneGenerator::stopTone() { + LOGV("stopTone"); + + mLock.lock(); + if (mState == TONE_PLAYING || mState == TONE_STARTING || mState == TONE_RESTARTING) { + mState = TONE_STOPPING; + LOGV("waiting cond"); + status_t lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1)); + if (lStatus == NO_ERROR) { + LOGV("track stop complete, time %d", (unsigned int)(systemTime()/1000000)); + } else { + LOGE("--- timed out"); + mState = TONE_INIT; + } + } + + clearWaveGens(); + + mLock.unlock(); +} + +//---------------------------------- private methods --------------------------- + + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::audioCallback() +// +// Description: AudioTrack callback implementation. Generates a block of +// PCM samples +// and manages tone generator sequencer: tones pulses, tone duration... +// +// Input: +// user reference (pointer to our ToneGenerator) +// info audio buffer descriptor +// +// Output: +// returned value: always true. +// +//////////////////////////////////////////////////////////////////////////////// +void ToneGenerator::audioCallback(void* user, const AudioTrack::Buffer& info) { + ToneGenerator *lpToneGen = static_cast(user); + short *lpOut = info.i16; + unsigned int lReqSmp = info.size/sizeof(short); + unsigned int lGenSmp; + unsigned int lWaveCmd = WaveGenerator::WAVEGEN_CONT; + bool lSignal = false; + + + lpToneGen->mLock.lock(); + + // Clear output buffer: WaveGenerator accumulates into lpOut buffer + memset(lpOut, 0, info.size); + + // Update pcm frame count and end time (current time at the end of this process) + lpToneGen->mTotalSmp += lReqSmp; + + // Update tone gen state machine and select wave gen command + switch (lpToneGen->mState) { + case TONE_PLAYING: + lWaveCmd = WaveGenerator::WAVEGEN_CONT; + break; + case TONE_STARTING: + LOGV("Starting Cbk"); + + lWaveCmd = WaveGenerator::WAVEGEN_START; + break; + case TONE_STOPPING: + case TONE_RESTARTING: + LOGV("Stop/restart Cbk"); + + lWaveCmd = WaveGenerator::WAVEGEN_STOP; + lpToneGen->mNextSegSmp = TONEGEN_INF; // forced to skip state machine management below + break; + default: + LOGV("Extra Cbk"); + goto audioCallback_Exit; + } + + // Exit if to sequence is over + if (lpToneGen->mpToneDesc->segments[lpToneGen->mCurSegment] == 0) { + goto audioCallback_Exit; + } + + if (lpToneGen->mTotalSmp > lpToneGen->mNextSegSmp) { + // Time to go to next sequence segment + + LOGV("End Segment, time: %d\n", (unsigned int)(systemTime()/1000000)); + + lGenSmp = lReqSmp; + + if (lpToneGen->mCurSegment & 0x0001) { + // If odd segment, OFF -> ON transition : reset wave generator + lWaveCmd = WaveGenerator::WAVEGEN_START; + + LOGV("OFF->ON, lGenSmp: %d, lReqSmp: %d\n", lGenSmp, lReqSmp); + } else { + // If even segment, ON -> OFF transition : ramp volume down + lWaveCmd = WaveGenerator::WAVEGEN_STOP; + + LOGV("ON->OFF, lGenSmp: %d, lReqSmp: %d\n", lGenSmp, lReqSmp); + } + + // Pre increment segment index and handle loop if last segment reached + if (lpToneGen->mpToneDesc->segments[++lpToneGen->mCurSegment] == 0) { + LOGV("Last Seg: %d\n", lpToneGen->mCurSegment); + + // Pre increment loop count and restart if total count not reached. Stop sequence otherwise + if (++lpToneGen->mCurCount <= lpToneGen->mpToneDesc->repeatCnt) { + LOGV("Repeating Count: %d\n", lpToneGen->mCurCount); + + lpToneGen->mCurSegment = 0; + + LOGV("New segment %d, Next Time: %d\n", lpToneGen->mCurSegment, + (lpToneGen->mNextSegSmp*1000)/lpToneGen->mSamplingRate); + + } else { + LOGV("End repeat, time: %d\n", (unsigned int)(systemTime()/1000000)); + + // Cancel OFF->ON transition in case previous segment tone state was OFF + if (!(lpToneGen->mCurSegment & 0x0001)) { + lGenSmp = 0; + } + } + } else { + LOGV("New segment %d, Next Time: %d\n", lpToneGen->mCurSegment, + (lpToneGen->mNextSegSmp*1000)/lpToneGen->mSamplingRate); + } + + // Update next segment transition position. No harm to do it also for last segment as lpToneGen->mNextSegSmp won't be used any more + lpToneGen->mNextSegSmp + += (lpToneGen->mpToneDesc->segments[lpToneGen->mCurSegment] * lpToneGen->mSamplingRate) / 1000; + + } else { + // Inside a segment keep tone ON or OFF + if (lpToneGen->mCurSegment & 0x0001) { + lGenSmp = 0; // If odd segment, tone is currently OFF + } else { + lGenSmp = lReqSmp; // If event segment, tone is currently ON + } + } + + if (lGenSmp) { + // If samples must be generated, call all active wave generators and acumulate waves in lpOut + unsigned int lWaveIdx; + + for (lWaveIdx = 0; lWaveIdx < (unsigned int)lpToneGen->mWaveGens.size(); lWaveIdx++) { + WaveGenerator *lpWaveGen = lpToneGen->mWaveGens[lWaveIdx]; + lpWaveGen->getSamples(lpOut, lGenSmp, lWaveCmd); + } + } + +audioCallback_Exit: + + switch (lpToneGen->mState) { + case TONE_RESTARTING: + LOGV("Cbk restarting track\n"); + if (lpToneGen->prepareWave()) { + lpToneGen->mState = TONE_STARTING; + } else { + lpToneGen->mState = TONE_INIT; + lpToneGen->mpAudioTrack->stop(); + } + lSignal = true; + break; + case TONE_STOPPING: + lpToneGen->mState = TONE_INIT; + LOGV("Cbk Stopping track\n"); + lSignal = true; + lpToneGen->mpAudioTrack->stop(); + break; + case TONE_STARTING: + LOGV("Cbk starting track\n"); + lpToneGen->mState = TONE_PLAYING; + lSignal = true; + break; + default: + break; + } + + if (lSignal) + lpToneGen->mWaitCbkCond.signal(); + lpToneGen->mLock.unlock(); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::prepareWave() +// +// Description: Prepare wave generators and reset tone sequencer state machine. +// mpNewToneDesc must have been initialized befoire calling this function. +// Input: +// none +// +// Output: +// returned value: true if wave generators have been created, false otherwise +// +//////////////////////////////////////////////////////////////////////////////// +bool ToneGenerator::prepareWave() { + unsigned int lCnt = 0; + unsigned int lNumWaves; + + if (!mpNewToneDesc) { + return false; + } + // Remove existing wave generators if any + clearWaveGens(); + + mpToneDesc = mpNewToneDesc; + + // Get total number of sine waves: needed to adapt sine wave gain. + lNumWaves = numWaves(); + + // Instantiate as many wave generators as listed in descriptor + while (lCnt < lNumWaves) { + ToneGenerator::WaveGenerator *lpWaveGen = + new ToneGenerator::WaveGenerator((unsigned short)mSamplingRate, + mpToneDesc->waveFreq[lCnt], + TONEGEN_GAIN/lNumWaves); + if (lpWaveGen == 0) { + goto prepareWave_exit; + } + + mWaveGens.push(lpWaveGen); + LOGV("Create sine: %d\n", mpToneDesc->waveFreq[lCnt]); + lCnt++; + } + + // Initialize tone sequencer + mTotalSmp = 0; + mCurSegment = 0; + mCurCount = 0; + mNextSegSmp = (mpToneDesc->segments[0] * mSamplingRate) / 1000; + + return true; + +prepareWave_exit: + + clearWaveGens(); + + return false; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::numWaves() +// +// Description: Count number of sine waves needed to generate tone (e.g 2 for DTMF). +// +// Input: +// none +// +// Output: +// returned value: nummber of sine waves +// +//////////////////////////////////////////////////////////////////////////////// +unsigned int ToneGenerator::numWaves() { + unsigned int lCnt = 0; + + while (mpToneDesc->waveFreq[lCnt]) { + lCnt++; + } + + return lCnt; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::clearWaveGens() +// +// Description: Removes all wave generators. +// +// Input: +// none +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +void ToneGenerator::clearWaveGens() { + LOGV("Clearing mWaveGens:"); + + while (!mWaveGens.isEmpty()) { + delete mWaveGens.top(); + mWaveGens.pop(); + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// WaveGenerator::WaveGenerator class Implementation +//////////////////////////////////////////////////////////////////////////////// + +//---------------------------------- public methods ---------------------------- + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: WaveGenerator::WaveGenerator() +// +// Description: Constructor. +// +// Input: +// samplingRate: Output sampling rate in Hz +// frequency: Frequency of the sine wave to generate in Hz +// volume: volume (0.0 to 1.0) +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +ToneGenerator::WaveGenerator::WaveGenerator(unsigned short samplingRate, + unsigned short frequency, float volume) { + double d0; + double F_div_Fs; // frequency / samplingRate + + F_div_Fs = frequency / (double)samplingRate; + d0 = - (float)GEN_AMP * sin(2 * M_PI * F_div_Fs); + mS2_0 = (short)d0; + mS1 = 0; + mS2 = mS2_0; + + mAmplitude_Q15 = (short)(32767. * 32767. * volume / GEN_AMP); + // take some margin for amplitude fluctuation + if (mAmplitude_Q15 > 32500) + mAmplitude_Q15 = 32500; + + d0 = 32768.0 * cos(2 * M_PI * F_div_Fs); // Q14*2*cos() + if (d0 > 32767) + d0 = 32767; + mA1_Q14 = (short) d0; + + LOGV("WaveGenerator init, mA1_Q14: %d, mS2_0: %d, mAmplitude_Q15: %d\n", + mA1_Q14, mS2_0, mAmplitude_Q15); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: WaveGenerator::~WaveGenerator() +// +// Description: Destructor. +// +// Input: +// none +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +ToneGenerator::WaveGenerator::~WaveGenerator() { +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: WaveGenerator::getSamples() +// +// Description: Generates count samples of a sine wave and accumulates +// result in outBuffer. +// +// Input: +// outBuffer: Output buffer where to accumulate samples. +// count: number of samples to produce. +// command: special action requested (see enum gen_command). +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +void ToneGenerator::WaveGenerator::getSamples(short *outBuffer, + unsigned int count, unsigned int command) { + long lS1, lS2; + long lA1, lAmplitude; + long Sample; // current sample + + // init local + if (command == WAVEGEN_START) { + lS1 = (long)0; + lS2 = (long)mS2_0; + } else { + lS1 = (long)mS1; + lS2 = (long)mS2; + } + lA1 = (long)mA1_Q14; + lAmplitude = (long)mAmplitude_Q15; + + if (command == WAVEGEN_STOP) { + lAmplitude <<= 16; + if (count == 0) { + return; + } + long dec = lAmplitude/count; + // loop generation + while (count--) { + Sample = ((lA1 * lS1) >> S_Q14) - lS2; + // shift delay + lS2 = lS1; + lS1 = Sample; + Sample = ((lAmplitude>>16) * Sample) >> S_Q15; + *(outBuffer++) += (short)Sample; // put result in buffer + lAmplitude -= dec; + } + } else { + // loop generation + while (count--) { + Sample = ((lA1 * lS1) >> S_Q14) - lS2; + // shift delay + lS2 = lS1; + lS1 = Sample; + Sample = (lAmplitude * Sample) >> S_Q15; + *(outBuffer++) += (short)Sample; // put result in buffer + } + } + + // save status + mS1 = (short)lS1; + mS2 = (short)lS2; +} + +} // end namespace android + diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp new file mode 100644 index 0000000..9cbafbc --- /dev/null +++ b/media/libmedia/mediametadataretriever.cpp @@ -0,0 +1,182 @@ +/* +** +** Copyright 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 + +#ifdef LOG_TAG +#undef LOG_TAG +#define LOG_TAG "MediaMetadataRetriever" +#endif + +#include +#include + +namespace android { + +// Factory class function in shared libpvplayer.so +typedef MediaMetadataRetrieverImpl* (*createRetriever_f)(); + +MediaMetadataRetrieverImpl *MediaMetadataRetriever::mRetriever = NULL; +void *MediaMetadataRetriever::mLibHandler = NULL; + +void MediaMetadataRetriever::create() +{ + // Load libpvplayer library once and only once. + if (!mLibHandler) { + mLibHandler = dlopen("libopencoreplayer.so", RTLD_NOW); + if (!mLibHandler) { + LOGE("setDataSource: dlopen failed on libopencoreplayer.so"); + return; + } + } + + // Each time create a new MediaMetadataRetrieverImpl object. + if (mRetriever) { + delete mRetriever; + } + createRetriever_f createRetriever = reinterpret_cast(dlsym(mLibHandler, "createRetriever")); + if (!createRetriever) { + LOGE("setDataSource: dlsym failed on createRetriever in libpvplayer.so"); + return; + } + mRetriever = createRetriever(); + if (!mRetriever) { + LOGE("setDataSource: createRetriever failed in libpvplayer.so"); + } +} + +status_t MediaMetadataRetriever::setDataSource(const char* srcUrl) +{ + if (srcUrl == NULL) { + return UNKNOWN_ERROR; + } + + if (mRetriever) { + return mRetriever->setDataSource(srcUrl); + } + return UNKNOWN_ERROR; +} + +const char* MediaMetadataRetriever::extractMetadata(int keyCode) +{ + if (mRetriever) { + return mRetriever->extractMetadata(keyCode); + } + return NULL; +} + +MediaAlbumArt* MediaMetadataRetriever::extractAlbumArt() +{ + if (mRetriever) { + return mRetriever->extractAlbumArt(); + } + return NULL; +} + +SkBitmap* MediaMetadataRetriever::captureFrame() +{ + if (mRetriever) { + return mRetriever->captureFrame(); + } + return NULL; +} + +void MediaMetadataRetriever::setMode(int mode) +{ + if (mRetriever) { + mRetriever->setMode(mode); + } +} + +void MediaMetadataRetriever::release() +{ + if (!mLibHandler) { + dlclose(mLibHandler); + mLibHandler = NULL; + } + if (!mRetriever) { + delete mRetriever; + mRetriever = NULL; + } +} + +void MediaAlbumArt::clearData() { + if (data != NULL) { + delete []data; + data = NULL; + } + length = 0; +} + + +MediaAlbumArt::MediaAlbumArt(const char* url) +{ + length = 0; + data = NULL; + FILE *in = fopen(url, "r"); + if (!in) { + LOGE("extractExternalAlbumArt: Failed to open external album art url: %s.", url); + return; + } + fseek(in, 0, SEEK_END); + length = ftell(in); // Allocating buffer of size equals to the external file size. + if (length == 0 || (data = new char[length]) == NULL) { + if (length == 0) { + LOGE("extractExternalAlbumArt: External album art url: %s has a size of 0.", url); + } else if (data == NULL) { + LOGE("extractExternalAlbumArt: No enough memory for storing the retrieved album art."); + length = 0; + } + fclose(in); + return; + } + rewind(in); + if (fread(data, 1, length, in) != length) { // Read failed. + length = 0; + delete []data; + data = NULL; + LOGE("extractExternalAlbumArt: Failed to retrieve the contents of an external album art."); + } + fclose(in); +} + +status_t MediaAlbumArt::setData(unsigned int len, const char* buf) { + clearData(); + length = len; + data = copyData(len, buf); + return (data != NULL)? OK: UNKNOWN_ERROR; +} + +char* MediaAlbumArt::copyData(unsigned int len, const char* buf) { + if (len == 0 || !buf) { + if (len == 0) { + LOGE("copyData: Length is 0."); + } else if (!buf) { + LOGE("copyData: buf is NULL pointer"); + } + return NULL; + } + char* copy = new char[len]; + if (!copy) { + LOGE("copyData: No enough memory to copy out the data."); + return NULL; + } + memcpy(copy, buf, len); + return copy; +} + +}; // namespace android diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp new file mode 100644 index 0000000..736d84a --- /dev/null +++ b/media/libmedia/mediaplayer.cpp @@ -0,0 +1,582 @@ +/* mediaplayer.cpp +** +** Copyright 2006, 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 "MediaPlayer" +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +namespace android { + +// client singleton for binder interface to service +Mutex MediaPlayer::mServiceLock; +sp MediaPlayer::mMediaPlayerService; +sp MediaPlayer::mDeathNotifier; + +// establish binder interface to service +const sp& MediaPlayer::getMediaPlayerService() +{ + Mutex::Autolock _l(mServiceLock); + if (mMediaPlayerService.get() == 0) { + sp sm = defaultServiceManager(); + sp binder; + do { + binder = sm->getService(String16("media.player")); + if (binder != 0) + break; + LOGW("MediaPlayerService not published, waiting..."); + usleep(500000); // 0.5 s + } while(true); + if (mDeathNotifier == NULL) { + mDeathNotifier = new DeathNotifier(); + } + binder->linkToDeath(mDeathNotifier); + mMediaPlayerService = interface_cast(binder); + } + LOGE_IF(mMediaPlayerService==0, "no MediaPlayerService!?"); + return mMediaPlayerService; +} + +MediaPlayer::MediaPlayer() +{ + LOGV("constructor"); + mListener = NULL; + mCookie = NULL; + mDuration = -1; + mStreamType = AudioTrack::MUSIC; + mCurrentPosition = -1; + mSeekPosition = -1; + mCurrentState = MEDIA_PLAYER_IDLE; + mPrepareSync = false; + mPrepareStatus = NO_ERROR; + mLoop = false; + mLeftVolume = mRightVolume = 1.0; +} + +MediaPlayer::~MediaPlayer() +{ + LOGV("destructor"); + disconnect(); + IPCThreadState::self()->flushCommands(); +} + +void MediaPlayer::disconnect() +{ + LOGV("disconnect"); + sp p; + { + Mutex::Autolock _l(mLock); + p = mPlayer; + mPlayer.clear(); + } + + if (p != 0) { + p->disconnect(); + p->asBinder()->unlinkToDeath(this); + } +} + +// always call with lock held +void MediaPlayer::clear_l() +{ + mDuration = -1; + mCurrentPosition = -1; + mSeekPosition = -1; +} + +status_t MediaPlayer::setListener(const sp& listener) +{ + LOGV("setListener"); + Mutex::Autolock _l(mLock); + mListener = listener; + return NO_ERROR; +} + + +status_t MediaPlayer::setDataSource(const sp& player) +{ + status_t err = UNKNOWN_ERROR; + sp p; + { // scope for the lock + Mutex::Autolock _l(mLock); + + if ( !( mCurrentState & ( MEDIA_PLAYER_IDLE | MEDIA_PLAYER_STATE_ERROR ) ) ) { + LOGE("setDataSource called in state %d", mCurrentState); + return INVALID_OPERATION; + } + + clear_l(); + p = mPlayer; + mPlayer = player; + if (player != 0) { + mCurrentState = MEDIA_PLAYER_INITIALIZED; + player->asBinder()->linkToDeath(this); + err = NO_ERROR; + } else { + LOGE("Unable to to create media player"); + } + } + + if (p != 0) { + p->disconnect(); + p->asBinder()->unlinkToDeath(this); + } + return err; +} + +status_t MediaPlayer::setDataSource(const char *url) +{ + LOGV("setDataSource(%s)", url); + status_t err = UNKNOWN_ERROR; + if (url != NULL) { + const sp& service(getMediaPlayerService()); + if (service != 0) { + sp player(service->create(getpid(), this, url)); + err = setDataSource(player); + } + } + return err; +} + +status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length) +{ + LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length); + status_t err = UNKNOWN_ERROR; + const sp& service(getMediaPlayerService()); + if (service != 0) { + sp player(service->create(getpid(), this, fd, offset, length)); + err = setDataSource(player); + } + return err; +} + +status_t MediaPlayer::setVideoSurface(const sp& surface) +{ + LOGV("setVideoSurface"); + Mutex::Autolock _l(mLock); + if (mPlayer == 0) return UNKNOWN_ERROR; + return mPlayer->setVideoSurface(surface->getISurface()); +} + +// must call with lock held +status_t MediaPlayer::prepareAsync_l() +{ + if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) { + mPlayer->setAudioStreamType(mStreamType); + mCurrentState = MEDIA_PLAYER_PREPARING; + return mPlayer->prepareAsync(); + } + LOGE("prepareAsync called in state %d", mCurrentState); + return INVALID_OPERATION; +} + +status_t MediaPlayer::prepare() +{ + LOGV("prepare"); + Mutex::Autolock _l(mLock); + if (mPrepareSync) return UNKNOWN_ERROR; + mPrepareSync = true; + status_t ret = prepareAsync_l(); + if (ret != NO_ERROR) return ret; + + if (mPrepareSync) { + mSignal.wait(mLock); // wait for prepare done + mPrepareSync = false; + } + LOGV("prepare complete - status=%d", mPrepareStatus); + return mPrepareStatus; +} + +status_t MediaPlayer::prepareAsync() +{ + LOGV("prepareAsync"); + Mutex::Autolock _l(mLock); + return prepareAsync_l(); +} + +status_t MediaPlayer::start() +{ + LOGV("start"); + Mutex::Autolock _l(mLock); + if (mCurrentState & MEDIA_PLAYER_STARTED) + return NO_ERROR; + if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_PREPARED | + MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_PAUSED ) ) ) { + mPlayer->setLooping(mLoop); + mPlayer->setVolume(mLeftVolume, mRightVolume); + mCurrentState = MEDIA_PLAYER_STARTED; + status_t ret = mPlayer->start(); + if (ret != NO_ERROR) { + mCurrentState = MEDIA_PLAYER_STATE_ERROR; + ret = UNKNOWN_ERROR; + } else { + if (mCurrentState == MEDIA_PLAYER_PLAYBACK_COMPLETE) { + LOGV("playback completed immediately following start()"); + } + } + return ret; + } + LOGE("start called in state %d", mCurrentState); + return INVALID_OPERATION; +} + +status_t MediaPlayer::stop() +{ + LOGV("stop"); + Mutex::Autolock _l(mLock); + if (mCurrentState & MEDIA_PLAYER_STOPPED) return NO_ERROR; + if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED | + MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE ) ) ) { + status_t ret = mPlayer->stop(); + if (ret != NO_ERROR) { + mCurrentState = MEDIA_PLAYER_STATE_ERROR; + ret = UNKNOWN_ERROR; + } else { + mCurrentState = MEDIA_PLAYER_STOPPED; + } + return ret; + } + LOGE("stop called in state %d", mCurrentState); + return INVALID_OPERATION; +} + +status_t MediaPlayer::pause() +{ + LOGV("pause"); + Mutex::Autolock _l(mLock); + if (mCurrentState & MEDIA_PLAYER_PAUSED) + return NO_ERROR; + if ((mPlayer != 0) && (mCurrentState & MEDIA_PLAYER_STARTED)) { + status_t ret = mPlayer->pause(); + if (ret != NO_ERROR) { + mCurrentState = MEDIA_PLAYER_STATE_ERROR; + ret = UNKNOWN_ERROR; + } else { + mCurrentState = MEDIA_PLAYER_PAUSED; + } + return ret; + } + LOGE("pause called in state %d", mCurrentState); + return INVALID_OPERATION; +} + +bool MediaPlayer::isPlaying() +{ + Mutex::Autolock _l(mLock); + if (mPlayer != 0) { + bool temp = false; + mPlayer->isPlaying(&temp); + LOGV("isPlaying: %d", temp); + if ((mCurrentState & MEDIA_PLAYER_STARTED) && ! temp) { + LOGE("internal/external state mismatch corrected"); + mCurrentState = MEDIA_PLAYER_PAUSED; + } + return temp; + } + LOGV("isPlaying: no active player"); + return false; +} + +status_t MediaPlayer::getVideoWidth(int *w) +{ + LOGV("getVideoWidth"); + Mutex::Autolock _l(mLock); + if (mPlayer != 0) { + int h; + return mPlayer->getVideoSize(w, &h); + } + return INVALID_OPERATION; +} + +status_t MediaPlayer::getVideoHeight(int *h) +{ + LOGV("getVideoHeight"); + Mutex::Autolock _l(mLock); + if (mPlayer != 0) { + int w; + return mPlayer->getVideoSize(&w, h); + } + return INVALID_OPERATION; +} + +status_t MediaPlayer::getCurrentPosition(int *msec) +{ + LOGV("getCurrentPosition"); + Mutex::Autolock _l(mLock); + if (mPlayer != 0) { + if (mCurrentPosition >= 0) { + LOGV("Using cached seek position: %d", mCurrentPosition); + *msec = mCurrentPosition; + return NO_ERROR; + } + return mPlayer->getCurrentPosition(msec); + } + return INVALID_OPERATION; +} + +status_t MediaPlayer::getDuration_l(int *msec) +{ + LOGV("getDuration"); + bool isValidState = (mCurrentState & (MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_STOPPED | MEDIA_PLAYER_PLAYBACK_COMPLETE)); + if (mPlayer != 0 && isValidState) { + status_t ret = NO_ERROR; + if (mDuration <= 0) + ret = mPlayer->getDuration(&mDuration); + if (msec) + *msec = mDuration; + return ret; + } + LOGE("Attempt to call getDuration without a valid mediaplayer"); + return INVALID_OPERATION; +} + +status_t MediaPlayer::getDuration(int *msec) +{ + Mutex::Autolock _l(mLock); + return getDuration_l(msec); +} + +status_t MediaPlayer::seekTo_l(int msec) +{ + LOGV("seekTo %d", msec); + if ((mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE) ) ) { + if ( msec < 0 ) { + LOGW("Attempt to seek to invalid position: %d", msec); + msec = 0; + } else if ((mDuration > 0) && (msec > mDuration)) { + LOGW("Attempt to seek to past end of file: request = %d, EOF = %d", msec, mDuration); + msec = mDuration; + } + // cache duration + mCurrentPosition = msec; + if (mSeekPosition < 0) { + getDuration_l(NULL); + mSeekPosition = msec; + return mPlayer->seekTo(msec); + } + else { + LOGV("Seek in progress - queue up seekTo[%d]", msec); + return NO_ERROR; + } + } + LOGE("Attempt to perform seekTo in wrong state: mPlayer=%p, mCurrentState=%u", mPlayer.get(), mCurrentState); + return INVALID_OPERATION; +} + +status_t MediaPlayer::seekTo(int msec) +{ + Mutex::Autolock _l(mLock); + return seekTo_l(msec); +} + +status_t MediaPlayer::reset() +{ + LOGV("reset"); + Mutex::Autolock _l(mLock); + mLoop = false; + if (mCurrentState == MEDIA_PLAYER_IDLE) return NO_ERROR; + mPrepareSync = false; + if (mPlayer != 0) { + status_t ret = mPlayer->reset(); + if (ret != NO_ERROR) { + mCurrentState = MEDIA_PLAYER_STATE_ERROR; + ret = UNKNOWN_ERROR; + } else { + mCurrentState = MEDIA_PLAYER_IDLE; + } + return ret; + } + clear_l(); + return NO_ERROR; +} + +status_t MediaPlayer::setAudioStreamType(int type) +{ + LOGV("MediaPlayer::setAudioStreamType"); + Mutex::Autolock _l(mLock); + if (mStreamType == type) return NO_ERROR; + if (mCurrentState & ( MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | + MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE ) ) { + // Can't change the stream type after prepare + LOGE("setAudioStream called in state %d", mCurrentState); + return INVALID_OPERATION; + } + // cache + mStreamType = type; + return OK; +} + +status_t MediaPlayer::setLooping(int loop) +{ + LOGV("MediaPlayer::setLooping"); + Mutex::Autolock _l(mLock); + mLoop = (loop != 0); + if (mPlayer != 0) { + return mPlayer->setLooping(loop); + } + return OK; +} + +status_t MediaPlayer::setVolume(float leftVolume, float rightVolume) +{ + LOGV("MediaPlayer::setVolume(%f, %f)", leftVolume, rightVolume); + Mutex::Autolock _l(mLock); + mLeftVolume = leftVolume; + mRightVolume = rightVolume; + if (mPlayer != 0) { + return mPlayer->setVolume(leftVolume, rightVolume); + } + return OK; +} + +void MediaPlayer::notify(int msg, int ext1, int ext2) +{ + LOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2); + bool send = true; + + // TODO: In the future, we might be on the same thread if the app is + // running in the same process as the media server. In that case, + // this will deadlock. + mLock.lock(); + if (mPlayer == 0) { + LOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2); + return; + } + + switch (msg) { + case MEDIA_NOP: // interface test message + break; + case MEDIA_PREPARED: + LOGV("prepared"); + mCurrentState = MEDIA_PLAYER_PREPARED; + if (mPrepareSync) { + LOGV("signal application thread"); + mPrepareSync = false; + mPrepareStatus = NO_ERROR; + mSignal.signal(); + } + break; + case MEDIA_PLAYBACK_COMPLETE: + LOGV("playback complete"); + if (!mLoop) { + mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE; + } + break; + case MEDIA_ERROR: + LOGV("error (%d, %d)", ext1, ext2); + mCurrentState = MEDIA_PLAYER_STATE_ERROR; + if (mPrepareSync) + { + LOGV("signal application thread"); + mPrepareSync = false; + mPrepareStatus = ext1; + mSignal.signal(); + send = false; + } + break; + case MEDIA_SEEK_COMPLETE: + LOGV("Received seek complete"); + if (mSeekPosition != mCurrentPosition) { + LOGV("Executing queued seekTo(%d)", mSeekPosition); + mSeekPosition = -1; + seekTo_l(mCurrentPosition); + } + else { + LOGV("All seeks complete - return to regularly scheduled program"); + mCurrentPosition = mSeekPosition = -1; + } + break; + case MEDIA_BUFFERING_UPDATE: + LOGV("buffering %d", ext1); + break; + default: + LOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2); + break; + } + + sp listener = mListener; + mLock.unlock(); + + // this prevents re-entrant calls into client code + if ((listener != 0) && send) { + Mutex::Autolock _l(mNotifyLock); + LOGV("callback application"); + listener->notify(msg, ext1, ext2); + LOGV("back from callback"); + } +} + +void MediaPlayer::binderDied(const wp& who) { + LOGW("IMediaplayer died"); + notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0); +} + +void MediaPlayer::DeathNotifier::binderDied(const wp& who) { + Mutex::Autolock _l(MediaPlayer::mServiceLock); + MediaPlayer::mMediaPlayerService.clear(); + LOGW("MediaPlayer server died!"); +} + +MediaPlayer::DeathNotifier::~DeathNotifier() +{ + Mutex::Autolock _l(mServiceLock); + if (mMediaPlayerService != 0) { + mMediaPlayerService->asBinder()->unlinkToDeath(this); + } +} + +/*static*/ sp MediaPlayer::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) +{ + LOGV("decode(%s)", url); + sp p; + const sp& service = getMediaPlayerService(); + if (service != 0) { + p = mMediaPlayerService->decode(url, pSampleRate, pNumChannels); + } else { + LOGE("Unable to locate media service"); + } + return p; + +} + +/*static*/ sp MediaPlayer::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) +{ + LOGV("decode(%d, %lld, %lld)", fd, offset, length); + sp p; + const sp& service = getMediaPlayerService(); + if (service != 0) { + p = mMediaPlayerService->decode(fd, offset, length, pSampleRate, pNumChannels); + } else { + LOGE("Unable to locate media service"); + } + return p; + +} + +}; // namespace android diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk new file mode 100644 index 0000000..b3a5747 --- /dev/null +++ b/media/libmediaplayerservice/Android.mk @@ -0,0 +1,33 @@ +LOCAL_PATH:= $(call my-dir) + +# +# libmediaplayerservice +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + MediaPlayerService.cpp \ + VorbisPlayer.cpp \ + MidiFile.cpp + +ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) +LOCAL_LDLIBS += -ldl -lpthread +endif + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libvorbisidec \ + libsonivox \ + libopencoreplayer \ + libmedia \ + libandroid_runtime + +LOCAL_C_INCLUDES := external/tremor/Tremor \ + $(call include-path-for, graphics corecg) + +LOCAL_MODULE:= libmediaplayerservice + +include $(BUILD_SHARED_LIBRARY) + diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp new file mode 100644 index 0000000..fd5f0ed --- /dev/null +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -0,0 +1,1112 @@ +/* +** +** Copyright 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. +*/ + +// Proxy for media player implementations + +//#define LOG_NDEBUG 0 +#define LOG_TAG "MediaPlayerService" +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "MediaPlayerService.h" +#include "MidiFile.h" +#include "VorbisPlayer.h" +#include + +/* desktop Linux needs a little help with gettid() */ +#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS) +#define __KERNEL__ +# include +#ifdef _syscall0 +_syscall0(pid_t,gettid) +#else +pid_t gettid() { return syscall(__NR_gettid);} +#endif +#undef __KERNEL__ +#endif + +/* + When USE_SIGBUS_HANDLER is set to 1, a handler for SIGBUS will be + installed, which allows us to recover when there is a read error + when accessing an mmap'ed file. However, since the kernel folks + don't seem to like it when non kernel folks install signal handlers + in their own process, this is currently disabled. + Without the handler, the process hosting this service will die and + then be restarted. This is mostly OK right now because the process is + not being shared with any other services, and clients of the service + will be notified of its death in their MediaPlayer.onErrorListener + callback, assuming they have installed one, and can then attempt to + do their own recovery. + It does open us up to a DOS attack against the media server, where + a malicious application can trivially force the media server to + restart continuously. +*/ +#define USE_SIGBUS_HANDLER 0 + +// TODO: Temp hack until we can register players +static const char* MIDI_FILE_EXTS[] = +{ + ".mid", + ".smf", + ".xmf", + ".imy", + ".rtttl", + ".rtx", + ".ota" +}; + +namespace android { + +// TODO: should come from audio driver +/* static */ const uint32_t MediaPlayerService::AudioOutput::kDriverLatencyInMsecs = 150; + +static struct sigaction oldact; +static pthread_key_t sigbuskey; + +static void sigbushandler(int signal, siginfo_t *info, void *context) +{ + char *faultaddr = (char*) info->si_addr; + LOGE("SIGBUS at %p\n", faultaddr); + + struct mediasigbushandler* h = (struct mediasigbushandler*) pthread_getspecific(sigbuskey); + + if (h) { + if (h->len) { + if (faultaddr < h->base || faultaddr >= h->base + h->len) { + // outside specified range, call old handler + if (oldact.sa_flags & SA_SIGINFO) { + oldact.sa_sigaction(signal, info, context); + } else { + oldact.sa_handler(signal); + } + return; + } + } + + // no range specified or address was in range + + if (h->handlesigbus) { + if (h->handlesigbus(info, h)) { + // thread's handler didn't handle the signal + if (oldact.sa_flags & SA_SIGINFO) { + oldact.sa_sigaction(signal, info, context); + } else { + oldact.sa_handler(signal); + } + } + return; + } + + if (h->sigbusvar) { + // map in a zeroed out page so the operation can succeed + long pagesize = sysconf(_SC_PAGE_SIZE); + long pagemask = ~(pagesize - 1); + void * pageaddr = (void*) (((long)(faultaddr)) & pagemask); + + void * bar = mmap( pageaddr, pagesize, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0); + if (bar == MAP_FAILED) { + LOGE("couldn't map zero page at %p: %s", pageaddr, strerror(errno)); + if (oldact.sa_flags & SA_SIGINFO) { + oldact.sa_sigaction(signal, info, context); + } else { + oldact.sa_handler(signal); + } + return; + } + + LOGE("setting sigbusvar at %p", h->sigbusvar); + *(h->sigbusvar) = 1; + return; + } + } + + LOGE("SIGBUS: no handler, or improperly configured handler (%p)", h); + + if (oldact.sa_flags & SA_SIGINFO) { + oldact.sa_sigaction(signal, info, context); + } else { + oldact.sa_handler(signal); + } + return; +} + +void MediaPlayerService::instantiate() { + defaultServiceManager()->addService( + String16("media.player"), new MediaPlayerService()); +} + +MediaPlayerService::MediaPlayerService() +{ + LOGV("MediaPlayerService created"); + mNextConnId = 1; + + pthread_key_create(&sigbuskey, NULL); + + +#if USE_SIGBUS_HANDLER + struct sigaction act; + memset(&act,0, sizeof act); + act.sa_sigaction = sigbushandler; + act.sa_flags = SA_SIGINFO; + sigaction(SIGBUS, &act, &oldact); +#endif +} + +MediaPlayerService::~MediaPlayerService() +{ +#if USE_SIGBUS_HANDLER + sigaction(SIGBUS, &oldact, NULL); +#endif + pthread_key_delete(sigbuskey); + LOGV("MediaPlayerService destroyed"); +} + +sp MediaPlayerService::create(pid_t pid, const sp& client, const char* url) +{ + int32_t connId = android_atomic_inc(&mNextConnId); + sp c = new Client(this, pid, connId, client); + LOGV("Create new client(%d) from pid %d, url=%s, connId=%d", connId, pid, url, connId); + if (NO_ERROR != c->setDataSource(url)) + { + c.clear(); + return c; + } + wp w = c; + Mutex::Autolock lock(mLock); + mClients.add(w); + return c; +} + +sp MediaPlayerService::create(pid_t pid, const sp& client, + int fd, int64_t offset, int64_t length) +{ + int32_t connId = android_atomic_inc(&mNextConnId); + sp c = new Client(this, pid, connId, client); + LOGV("Create new client(%d) from pid %d, fd=%d, offset=%lld, length=%lld", + connId, pid, fd, offset, length); + if (NO_ERROR != c->setDataSource(fd, offset, length)) { + c.clear(); + } else { + wp w = c; + Mutex::Autolock lock(mLock); + mClients.add(w); + } + ::close(fd); + return c; +} + +status_t MediaPlayerService::AudioCache::dump(int fd, const Vector& args) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + result.append(" AudioCache\n"); + if (mHeap != 0) { + snprintf(buffer, 255, " heap base(%p), size(%d), flags(%d), device(%s)\n", + mHeap->getBase(), mHeap->getSize(), mHeap->getFlags(), mHeap->getDevice()); + result.append(buffer); + } + snprintf(buffer, 255, " msec per frame(%f), channel count(%ld), frame count(%ld)\n", + mMsecsPerFrame, mChannelCount, mFrameCount); + result.append(buffer); + snprintf(buffer, 255, " sample rate(%d), size(%d), error(%d), command complete(%s)\n", + mSampleRate, mSize, mError, mCommandComplete?"true":"false"); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t MediaPlayerService::AudioOutput::dump(int fd, const Vector& args) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + result.append(" AudioOutput\n"); + snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n", + mStreamType, mLeftVolume, mRightVolume); + result.append(buffer); + snprintf(buffer, 255, " msec per frame(%f), latency (%d), driver latency(%d)\n", + mMsecsPerFrame, mLatency, kDriverLatencyInMsecs); + result.append(buffer); + ::write(fd, result.string(), result.size()); + if (mTrack != 0) { + mTrack->dump(fd, args); + } + return NO_ERROR; +} + +status_t MediaPlayerService::Client::dump(int fd, const Vector& args) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + result.append(" Client\n"); + snprintf(buffer, 255, " pid(%d), connId(%d), status(%d), looping(%s)\n", + mPid, mConnId, mStatus, mLoop?"true": "false"); + result.append(buffer); + write(fd, result.string(), result.size()); + if (mAudioOutput != 0) { + mAudioOutput->dump(fd, args); + } + write(fd, "\n", 1); + return NO_ERROR; +} + +static int myTid() { +#ifdef HAVE_GETTID + return gettid(); +#else + return getpid(); +#endif +} + +status_t MediaPlayerService::dump(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump MediaPlayerService from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + result.append(buffer); + } else { + Mutex::Autolock lock(mLock); + for (int i = 0, n = mClients.size(); i < n; ++i) { + sp c = mClients[i].promote(); + if (c != 0) c->dump(fd, args); + } + result.append(" Files opened and/or mapped:\n"); + snprintf(buffer, SIZE, "/proc/%d/maps", myTid()); + FILE *f = fopen(buffer, "r"); + if (f) { + while (!feof(f)) { + fgets(buffer, SIZE, f); + if (strstr(buffer, " /sdcard/") || + strstr(buffer, " /system/sounds/") || + strstr(buffer, " /system/media/")) { + result.append(" "); + result.append(buffer); + } + } + fclose(f); + } else { + result.append("couldn't open "); + result.append(buffer); + result.append("\n"); + } + + snprintf(buffer, SIZE, "/proc/%d/fd", myTid()); + DIR *d = opendir(buffer); + if (d) { + struct dirent *ent; + while((ent = readdir(d)) != NULL) { + if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) { + snprintf(buffer, SIZE, "/proc/%d/fd/%s", myTid(), ent->d_name); + struct stat s; + if (lstat(buffer, &s) == 0) { + if ((s.st_mode & S_IFMT) == S_IFLNK) { + char linkto[256]; + int len = readlink(buffer, linkto, sizeof(linkto)); + if(len > 0) { + if(len > 255) { + linkto[252] = '.'; + linkto[253] = '.'; + linkto[254] = '.'; + linkto[255] = 0; + } else { + linkto[len] = 0; + } + if (strstr(linkto, "/sdcard/") == linkto || + strstr(linkto, "/system/sounds/") == linkto || + strstr(linkto, "/system/media/") == linkto) { + result.append(" "); + result.append(buffer); + result.append(" -> "); + result.append(linkto); + result.append("\n"); + } + } + } else { + result.append(" unexpected type for "); + result.append(buffer); + result.append("\n"); + } + } + } + } + closedir(d); + } else { + result.append("couldn't open "); + result.append(buffer); + result.append("\n"); + } + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +void MediaPlayerService::removeClient(wp client) +{ + Mutex::Autolock lock(mLock); + mClients.remove(client); +} + +MediaPlayerService::Client::Client(const sp& service, pid_t pid, + int32_t connId, const sp& client) +{ + LOGV("Client(%d) constructor", connId); + mPid = pid; + mConnId = connId; + mService = service; + mClient = client; + mLoop = false; + mStatus = NO_INIT; +#if CALLBACK_ANTAGONIZER + LOGD("create Antagonizer"); + mAntagonizer = new Antagonizer(notify, this); +#endif +} + +MediaPlayerService::Client::~Client() +{ + LOGV("Client(%d) destructor pid = %d", mConnId, mPid); + mAudioOutput.clear(); + wp client(this); + disconnect(); + mService->removeClient(client); +} + +void MediaPlayerService::Client::disconnect() +{ + LOGV("disconnect(%d) from pid %d", mConnId, mPid); + // grab local reference and clear main reference to prevent future + // access to object + sp p; + { + Mutex::Autolock l(mLock); + p = mPlayer; + } + mPlayer.clear(); + + // clear the notification to prevent callbacks to dead client + // and reset the player. We assume the player will serialize + // access to itself if necessary. + if (p != 0) { + p->setNotifyCallback(0, 0); +#if CALLBACK_ANTAGONIZER + LOGD("kill Antagonizer"); + mAntagonizer->kill(); +#endif + p->reset(); + } + + IPCThreadState::self()->flushCommands(); +} + +static player_type getPlayerType(int fd, int64_t offset, int64_t length) +{ + char buf[20]; + lseek(fd, offset, SEEK_SET); + read(fd, buf, sizeof(buf)); + lseek(fd, offset, SEEK_SET); + + long ident = *((long*)buf); + + // Ogg vorbis? + if (ident == 0x5367674f) // 'OggS' + return VORBIS_PLAYER; + + // Some kind of MIDI? + EAS_DATA_HANDLE easdata; + if (EAS_Init(&easdata) == EAS_SUCCESS) { + EAS_FILE locator; + locator.path = NULL; + locator.fd = fd; + locator.offset = offset; + locator.length = length; + EAS_HANDLE eashandle; + if (EAS_OpenFile(easdata, &locator, &eashandle, NULL) == EAS_SUCCESS) { + EAS_CloseFile(easdata, eashandle); + EAS_Shutdown(easdata); + return SONIVOX_PLAYER; + } + EAS_Shutdown(easdata); + } + + // Fall through to PV + return PV_PLAYER; +} + +static player_type getPlayerType(const char* url) +{ + + // use MidiFile for MIDI extensions + int lenURL = strlen(url); + for (int i = 0; i < NELEM(MIDI_FILE_EXTS); ++i) { + int len = strlen(MIDI_FILE_EXTS[i]); + int start = lenURL - len; + if (start > 0) { + if (!strncmp(url + start, MIDI_FILE_EXTS[i], len)) { + LOGV("Type is MIDI"); + return SONIVOX_PLAYER; + } + } + } + + if (strcmp(url + strlen(url) - 4, ".ogg") == 0) { + LOGV("Type is Vorbis"); + return VORBIS_PLAYER; + } + + // Fall through to PV + return PV_PLAYER; +} + +static sp createPlayer(player_type playerType, void* cookie, + notify_callback_f notifyFunc) +{ + sp p; + switch (playerType) { + case PV_PLAYER: + LOGV(" create PVPlayer"); + p = new PVPlayer(); + break; + case SONIVOX_PLAYER: + LOGV(" create MidiFile"); + p = new MidiFile(); + break; + case VORBIS_PLAYER: + LOGV(" create VorbisPlayer"); + p = new VorbisPlayer(); + break; + } + if (p != NULL) { + if (p->initCheck() == NO_ERROR) { + p->setNotifyCallback(cookie, notifyFunc); + p->setSigBusHandlerStructTLSKey(sigbuskey); + } else { + p.clear(); + } + } + if (p == NULL) { + LOGE("Failed to create player object"); + } + return p; +} + +sp MediaPlayerService::Client::createPlayer(player_type playerType) +{ + // determine if we have the right player type + sp p = mPlayer; + if ((p != NULL) && (p->playerType() != playerType)) { + LOGV("delete player"); + p.clear(); + } + if (p == NULL) { + p = android::createPlayer(playerType, this, notify); + } + return p; +} + +status_t MediaPlayerService::Client::setDataSource(const char *url) +{ + LOGV("setDataSource(%s)", url); + if (url == NULL) + return UNKNOWN_ERROR; + + if (strncmp(url, "content://", 10) == 0) { + // get a filedescriptor for the content Uri and + // pass it to the setDataSource(fd) method + + String16 url16(url); + int fd = android::openContentProviderFile(url16); + if (fd < 0) + { + LOGE("Couldn't open fd for %s", url); + return UNKNOWN_ERROR; + } + setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatus + close(fd); + return mStatus; + } else { + player_type playerType = getPlayerType(url); + LOGV("player type = %d", playerType); + + // create the right type of player + sp p = createPlayer(playerType); + if (p == NULL) return NO_INIT; + + if (!p->hardwareOutput()) { + mAudioOutput = new AudioOutput(); + static_cast(p.get())->setAudioSink(mAudioOutput); + } + + // now set data source + LOGV(" setDataSource"); + mStatus = p->setDataSource(url); + if (mStatus == NO_ERROR) mPlayer = p; + return mStatus; + } +} + +status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length) +{ + LOGV("setDataSource fd=%d, offset=%lld, length=%lld", fd, offset, length); + struct stat sb; + int ret = fstat(fd, &sb); + if (ret != 0) { + LOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno)); + return UNKNOWN_ERROR; + } + + LOGV("st_dev = %llu", sb.st_dev); + LOGV("st_mode = %u", sb.st_mode); + LOGV("st_uid = %lu", sb.st_uid); + LOGV("st_gid = %lu", sb.st_gid); + LOGV("st_size = %llu", sb.st_size); + + if (offset >= sb.st_size) { + LOGE("offset error"); + ::close(fd); + return UNKNOWN_ERROR; + } + if (offset + length > sb.st_size) { + length = sb.st_size - offset; + LOGV("calculated length = %lld", length); + } + + player_type playerType = getPlayerType(fd, offset, length); + LOGV("player type = %d", playerType); + + // create the right type of player + sp p = createPlayer(playerType); + if (p == NULL) return NO_INIT; + + if (!p->hardwareOutput()) { + mAudioOutput = new AudioOutput(); + static_cast(p.get())->setAudioSink(mAudioOutput); + } + + // now set data source + mStatus = p->setDataSource(fd, offset, length); + if (mStatus == NO_ERROR) mPlayer = p; + return mStatus; +} + +status_t MediaPlayerService::Client::setVideoSurface(const sp& surface) +{ + LOGV("[%d] setVideoSurface(%p)", mConnId, surface.get()); + sp p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + return p->setVideoSurface(surface); +} + +status_t MediaPlayerService::Client::prepareAsync() +{ + LOGV("[%d] prepareAsync", mConnId); + sp p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + status_t ret = p->prepareAsync(); +#if CALLBACK_ANTAGONIZER + LOGD("start Antagonizer"); + if (ret == NO_ERROR) mAntagonizer->start(); +#endif + return ret; +} + +status_t MediaPlayerService::Client::start() +{ + LOGV("[%d] start", mConnId); + sp p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + p->setLooping(mLoop); + return p->start(); +} + +status_t MediaPlayerService::Client::stop() +{ + LOGV("[%d] stop", mConnId); + sp p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + return p->stop(); +} + +status_t MediaPlayerService::Client::pause() +{ + LOGV("[%d] pause", mConnId); + sp p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + return p->pause(); +} + +status_t MediaPlayerService::Client::isPlaying(bool* state) +{ + *state = false; + sp p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + *state = p->isPlaying(); + LOGV("[%d] isPlaying: %d", mConnId, *state); + return NO_ERROR; +} + +status_t MediaPlayerService::Client::getVideoSize(int *w, int *h) +{ + sp p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + status_t ret = p->getVideoWidth(w); + if (ret == NO_ERROR) ret = p->getVideoHeight(h); + if (ret == NO_ERROR) { + LOGV("[%d] getVideoWidth = (%d, %d)", mConnId, *w, *h); + } else { + LOGE("getVideoSize returned %d", ret); + } + return ret; +} + +status_t MediaPlayerService::Client::getCurrentPosition(int *msec) +{ + LOGV("getCurrentPosition"); + sp p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + status_t ret = p->getCurrentPosition(msec); + if (ret == NO_ERROR) { + LOGV("[%d] getCurrentPosition = %d", mConnId, *msec); + } else { + LOGE("getCurrentPosition returned %d", ret); + } + return ret; +} + +status_t MediaPlayerService::Client::getDuration(int *msec) +{ + LOGV("getDuration"); + sp p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + status_t ret = p->getDuration(msec); + if (ret == NO_ERROR) { + LOGV("[%d] getDuration = %d", mConnId, *msec); + } else { + LOGE("getDuration returned %d", ret); + } + return ret; +} + +status_t MediaPlayerService::Client::seekTo(int msec) +{ + LOGV("[%d] seekTo(%d)", mConnId, msec); + sp p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + return p->seekTo(msec); +} + +status_t MediaPlayerService::Client::reset() +{ + LOGV("[%d] reset", mConnId); + sp p = getPlayer(); + if (p == 0) return UNKNOWN_ERROR; + return p->reset(); +} + +status_t MediaPlayerService::Client::setAudioStreamType(int type) +{ + LOGV("[%d] setAudioStreamType(%d)", mConnId, type); + // TODO: for hardware output, call player instead + Mutex::Autolock l(mLock); + if (mAudioOutput != 0) mAudioOutput->setAudioStreamType(type); + return NO_ERROR; +} + +status_t MediaPlayerService::Client::setLooping(int loop) +{ + LOGV("[%d] setLooping(%d)", mConnId, loop); + mLoop = loop; + sp p = getPlayer(); + if (p != 0) return p->setLooping(loop); + return NO_ERROR; +} + +status_t MediaPlayerService::Client::setVolume(float leftVolume, float rightVolume) +{ + LOGV("[%d] setVolume(%f, %f)", mConnId, leftVolume, rightVolume); + // TODO: for hardware output, call player instead + Mutex::Autolock l(mLock); + if (mAudioOutput != 0) mAudioOutput->setVolume(leftVolume, rightVolume); + return NO_ERROR; +} + +void MediaPlayerService::Client::notify(void* cookie, int msg, int ext1, int ext2) +{ + Client* client = static_cast(cookie); + LOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, cookie, msg, ext1, ext2); + client->mClient->notify(msg, ext1, ext2); +} + +#if CALLBACK_ANTAGONIZER +const int Antagonizer::interval = 10000; // 10 msecs + +Antagonizer::Antagonizer(notify_callback_f cb, void* client) : + mExit(false), mActive(false), mClient(client), mCb(cb) +{ + createThread(callbackThread, this); +} + +void Antagonizer::kill() +{ + Mutex::Autolock _l(mLock); + mActive = false; + mExit = true; + mCondition.wait(mLock); +} + +int Antagonizer::callbackThread(void* user) +{ + LOGD("Antagonizer started"); + Antagonizer* p = reinterpret_cast(user); + while (!p->mExit) { + if (p->mActive) { + LOGV("send event"); + p->mCb(p->mClient, 0, 0, 0); + } + usleep(interval); + } + Mutex::Autolock _l(p->mLock); + p->mCondition.signal(); + LOGD("Antagonizer stopped"); + return 0; +} +#endif + +static size_t kDefaultHeapSize = 1024 * 1024; // 1MB + +sp MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) +{ + LOGV("decode(%s)", url); + sp mem; + sp player; + + // Protect our precious, precious DRMd ringtones by only allowing + // decoding of http, but not filesystem paths or content Uris. + // If the application wants to decode those, it should open a + // filedescriptor for them and use that. + if (url != NULL && strncmp(url, "http://", 7) != 0) { + LOGD("Can't decode %s by path, use filedescriptor instead", url); + return mem; + } + + player_type playerType = getPlayerType(url); + LOGV("player type = %d", playerType); + + // create the right type of player + sp cache = new AudioCache(url); + player = android::createPlayer(playerType, cache.get(), cache->notify); + if (player == NULL) goto Exit; + if (player->hardwareOutput()) goto Exit; + + static_cast(player.get())->setAudioSink(cache); + + // set data source + if (player->setDataSource(url) != NO_ERROR) goto Exit; + + LOGV("prepare"); + player->prepareAsync(); + + LOGV("wait for prepare"); + if (cache->wait() != NO_ERROR) goto Exit; + + LOGV("start"); + player->start(); + + LOGV("wait for playback complete"); + if (cache->wait() != NO_ERROR) goto Exit; + + mem = new MemoryBase(cache->getHeap(), 0, cache->size()); + *pSampleRate = cache->sampleRate(); + *pNumChannels = cache->channelCount(); + LOGV("return memory @ %p, sampleRate=%u, channelCount = %d", mem->pointer(), *pSampleRate, *pNumChannels); + +Exit: + if (player != 0) player->reset(); + return mem; +} + +sp MediaPlayerService::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) +{ + LOGV("decode(%d, %lld, %lld)", fd, offset, length); + sp mem; + sp player; + + player_type playerType = getPlayerType(fd, offset, length); + LOGV("player type = %d", playerType); + + // create the right type of player + sp cache = new AudioCache("decode_fd"); + player = android::createPlayer(playerType, cache.get(), cache->notify); + if (player == NULL) goto Exit; + if (player->hardwareOutput()) goto Exit; + + static_cast(player.get())->setAudioSink(cache); + + // set data source + if (player->setDataSource(fd, offset, length) != NO_ERROR) goto Exit; + + LOGV("prepare"); + player->prepareAsync(); + + LOGV("wait for prepare"); + if (cache->wait() != NO_ERROR) goto Exit; + + LOGV("start"); + player->start(); + + LOGV("wait for playback complete"); + if (cache->wait() != NO_ERROR) goto Exit; + + mem = new MemoryBase(cache->getHeap(), 0, cache->size()); + *pSampleRate = cache->sampleRate(); + *pNumChannels = cache->channelCount(); + LOGV("return memory @ %p, sampleRate=%u, channelCount = %d", mem->pointer(), *pSampleRate, *pNumChannels); + +Exit: + if (player != 0) player->reset(); + ::close(fd); + return mem; +} + +#undef LOG_TAG +#define LOG_TAG "AudioSink" +MediaPlayerService::AudioOutput::AudioOutput() +{ + mTrack = 0; + mStreamType = AudioTrack::MUSIC; + mLeftVolume = 1.0; + mRightVolume = 1.0; + mLatency = 0; + mMsecsPerFrame = 0; +} + +MediaPlayerService::AudioOutput::~AudioOutput() +{ + close(); +} + +ssize_t MediaPlayerService::AudioOutput::bufferSize() const +{ + if (mTrack == 0) return NO_INIT; + return mTrack->frameCount() * mTrack->channelCount() * sizeof(int16_t); +} + +ssize_t MediaPlayerService::AudioOutput::frameCount() const +{ + if (mTrack == 0) return NO_INIT; + return mTrack->frameCount(); +} + +ssize_t MediaPlayerService::AudioOutput::channelCount() const +{ + if (mTrack == 0) return NO_INIT; + return mTrack->channelCount(); +} + +ssize_t MediaPlayerService::AudioOutput::frameSize() const +{ + if (mTrack == 0) return NO_INIT; + return mTrack->channelCount() * sizeof(int16_t); +} + +uint32_t MediaPlayerService::AudioOutput::latency () const +{ + return mLatency; +} + +float MediaPlayerService::AudioOutput::msecsPerFrame() const +{ + return mMsecsPerFrame; +} + +status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelCount, int bufferCount) +{ + LOGV("open(%u, %d, %d)", sampleRate, channelCount, bufferCount); + if (mTrack) close(); + + AudioTrack *t = new AudioTrack(mStreamType, sampleRate, AudioSystem::PCM_16_BIT, channelCount, bufferCount); + if ((t == 0) || (t->initCheck() != NO_ERROR)) { + LOGE("Unable to create audio track"); + delete t; + return NO_INIT; + } + + LOGV("setVolume"); + t->setVolume(mLeftVolume, mRightVolume); + mMsecsPerFrame = 1.e3 / (float) sampleRate; + mLatency = (mMsecsPerFrame * bufferCount * t->frameCount()) + kDriverLatencyInMsecs; + mTrack = t; + return NO_ERROR; +} + +void MediaPlayerService::AudioOutput::start() +{ + LOGV("start"); + if (mTrack) { + mTrack->setVolume(mLeftVolume, mRightVolume); + mTrack->start(); + } +} + +ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size) +{ + //LOGV("write(%p, %u)", buffer, size); + if (mTrack) return mTrack->write(buffer, size); + return NO_INIT; +} + +void MediaPlayerService::AudioOutput::stop() +{ + LOGV("stop"); + if (mTrack) mTrack->stop(); +} + +void MediaPlayerService::AudioOutput::flush() +{ + LOGV("flush"); + if (mTrack) mTrack->flush(); +} + +void MediaPlayerService::AudioOutput::pause() +{ + LOGV("pause"); + if (mTrack) mTrack->pause(); +} + +void MediaPlayerService::AudioOutput::close() +{ + LOGV("close"); + delete mTrack; + mTrack = 0; +} + +void MediaPlayerService::AudioOutput::setVolume(float left, float right) +{ + LOGV("setVolume(%f, %f)", left, right); + mLeftVolume = left; + mRightVolume = right; + if (mTrack) { + mTrack->setVolume(left, right); + } +} + +#undef LOG_TAG +#define LOG_TAG "AudioCache" +MediaPlayerService::AudioCache::AudioCache(const char* name) : + mChannelCount(0), mFrameCount(0), mSampleRate(0), mSize(0), + mError(NO_ERROR), mCommandComplete(false) +{ + // create ashmem heap + mHeap = new MemoryHeapBase(kDefaultHeapSize, 0, name); +} + +uint32_t MediaPlayerService::AudioCache::latency () const +{ + return 0; +} + +float MediaPlayerService::AudioCache::msecsPerFrame() const +{ + return mMsecsPerFrame; +} + +status_t MediaPlayerService::AudioCache::open(uint32_t sampleRate, int channelCount, int bufferCount) +{ + LOGV("open(%u, %d, %d)", sampleRate, channelCount, bufferCount); + if (mHeap->getHeapID() < 0) return NO_INIT; + mSampleRate = sampleRate; + mChannelCount = channelCount; + mMsecsPerFrame = 1.e3 / (float) sampleRate; + return NO_ERROR; +} + +ssize_t MediaPlayerService::AudioCache::write(const void* buffer, size_t size) +{ + LOGV("write(%p, %u)", buffer, size); + if ((buffer == 0) || (size == 0)) return size; + + uint8_t* p = static_cast(mHeap->getBase()); + if (p == NULL) return NO_INIT; + p += mSize; + LOGV("memcpy(%p, %p, %u)", p, buffer, size); + memcpy(p, buffer, size); + mSize += size; + return size; +} + +// call with lock held +status_t MediaPlayerService::AudioCache::wait() +{ + Mutex::Autolock lock(mLock); + if (!mCommandComplete) { + mSignal.wait(mLock); + } + mCommandComplete = false; + + if (mError == NO_ERROR) { + LOGV("wait - success"); + } else { + LOGV("wait - error"); + } + return mError; +} + +void MediaPlayerService::AudioCache::notify(void* cookie, int msg, int ext1, int ext2) +{ + LOGV("notify(%p, %d, %d, %d)", cookie, msg, ext1, ext2); + AudioCache* p = static_cast(cookie); + + // ignore buffering messages + if (msg == MEDIA_BUFFERING_UPDATE) return; + + // set error condition + if (msg == MEDIA_ERROR) { + LOGE("Error %d, %d occurred", ext1, ext2); + p->mError = ext1; + } + + // wake up thread + LOGV("wakeup thread"); + p->mCommandComplete = true; + p->mSignal.signal(); +} + +}; // namespace android diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h new file mode 100644 index 0000000..c2007cb --- /dev/null +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -0,0 +1,222 @@ +/* +** +** Copyright 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. +*/ + +#ifndef ANDROID_MEDIAPLAYERSERVICE_H +#define ANDROID_MEDIAPLAYERSERVICE_H + +#include +#include +#include + +#include +#include + +class SkBitmap; + +namespace android { + +#define CALLBACK_ANTAGONIZER 0 +#if CALLBACK_ANTAGONIZER +class Antagonizer { +public: + Antagonizer(notify_callback_f cb, void* client); + void start() { mActive = true; } + void stop() { mActive = false; } + void kill(); +private: + static const int interval; + Antagonizer(); + static int callbackThread(void* cookie); + Mutex mLock; + Condition mCondition; + bool mExit; + bool mActive; + void* mClient; + notify_callback_f mCb; +}; +#endif + +class MediaPlayerService : public BnMediaPlayerService +{ + class Client; + + class AudioOutput : public MediaPlayerBase::AudioSink + { + public: + AudioOutput(); + virtual ~AudioOutput(); + + virtual bool ready() const { return mTrack != NULL; } + virtual bool realtime() const { return true; } + 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 open(uint32_t sampleRate, int channelCount, int bufferCount=4); + virtual void start(); + virtual ssize_t write(const void* buffer, size_t size); + virtual void stop(); + virtual void flush(); + virtual void pause(); + virtual void close(); + void setAudioStreamType(int streamType) { mStreamType = streamType; } + void setVolume(float left, float right); + virtual status_t dump(int fd, const Vector& args) const; + private: + AudioTrack* mTrack; + int mStreamType; + float mLeftVolume; + float mRightVolume; + float mMsecsPerFrame; + uint32_t mLatency; + static const uint32_t kDriverLatencyInMsecs; + }; + + class AudioCache : public MediaPlayerBase::AudioSink + { + public: + AudioCache(const char* name); + virtual ~AudioCache() {} + + virtual bool ready() const { return (mChannelCount > 0) && (mHeap->getHeapID() > 0); } + virtual bool realtime() const { return false; } + virtual ssize_t bufferSize() const { return 4096; } + virtual ssize_t frameCount() const { return mFrameCount; } + virtual ssize_t channelCount() const { return mChannelCount; } + virtual ssize_t frameSize() const { return ssize_t(mChannelCount * sizeof(int16_t)); } + virtual uint32_t latency() const; + virtual float msecsPerFrame() const; + virtual status_t open(uint32_t sampleRate, int channelCount, int bufferCount=1); + virtual void start() {} + virtual ssize_t write(const void* buffer, size_t size); + virtual void stop() {} + virtual void flush() {} + virtual void pause() {} + virtual void close() {} + void setAudioStreamType(int streamType) {} + void setVolume(float left, float right) {} + uint32_t sampleRate() const { return mSampleRate; } + size_t size() const { return mSize; } + status_t wait(); + + sp getHeap() const { return mHeap; } + + static void notify(void* cookie, int msg, int ext1, int ext2); + virtual status_t dump(int fd, const Vector& args) const; + + private: + AudioCache(); + + Mutex mLock; + Condition mSignal; + sp mHeap; + float mMsecsPerFrame; + ssize_t mChannelCount; + ssize_t mFrameCount; + uint32_t mSampleRate; + uint32_t mSize; + int mError; + bool mCommandComplete; + }; + +public: + static void instantiate(); + + // IMediaPlayerService interface + virtual sp create(pid_t pid, const sp& client, const char* url); + virtual sp create(pid_t pid, const sp& client, int fd, int64_t offset, int64_t length); + virtual sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels); + virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels); + + virtual status_t dump(int fd, const Vector& args); + + void removeClient(wp client); + +private: + + class Client : public BnMediaPlayer { + + // IMediaPlayer interface + virtual void disconnect(); + virtual status_t setVideoSurface(const sp& surface); + virtual status_t prepareAsync(); + virtual status_t start(); + virtual status_t stop(); + virtual status_t pause(); + virtual status_t isPlaying(bool* state); + virtual status_t getVideoSize(int* w, int* h); + virtual status_t seekTo(int msec); + virtual status_t getCurrentPosition(int* msec); + virtual status_t getDuration(int* msec); + virtual status_t reset(); + virtual status_t setAudioStreamType(int type); + virtual status_t setLooping(int loop); + virtual status_t setVolume(float leftVolume, float rightVolume); + + sp createPlayer(player_type playerType); + status_t setDataSource(const char *url); + status_t setDataSource(int fd, int64_t offset, int64_t length); + static void notify(void* cookie, int msg, int ext1, int ext2); + + pid_t pid() const { return mPid; } + virtual status_t dump(int fd, const Vector& args) const; + + private: + friend class MediaPlayerService; + Client( const sp& service, + pid_t pid, + int32_t connId, + const sp& client); + Client(); + virtual ~Client(); + + void deletePlayer(); + + sp getPlayer() const { Mutex::Autolock lock(mLock); return mPlayer; } + + mutable Mutex mLock; + sp mPlayer; + sp mService; + sp mClient; + sp mAudioOutput; + pid_t mPid; + status_t mStatus; + bool mLoop; + int32_t mConnId; +#if CALLBACK_ANTAGONIZER + Antagonizer* mAntagonizer; +#endif + }; + +// ---------------------------------------------------------------------------- + + MediaPlayerService(); + virtual ~MediaPlayerService(); + + mutable Mutex mLock; + SortedVector< wp > mClients; + int32_t mNextConnId; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_MEDIAPLAYERSERVICE_H + diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp new file mode 100644 index 0000000..538f7d4 --- /dev/null +++ b/media/libmediaplayerservice/MidiFile.cpp @@ -0,0 +1,572 @@ +/* MidiFile.cpp +** +** Copyright 2007, 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 "MidiFile" +#include "utils/Log.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MidiFile.h" + +#ifdef HAVE_GETTID +static pid_t myTid() { return gettid(); } +#else +static pid_t myTid() { return getpid(); } +#endif + +// ---------------------------------------------------------------------------- + +extern pthread_key_t EAS_sigbuskey; + +namespace android { + +// ---------------------------------------------------------------------------- + +// The midi engine buffers are a bit small (128 frames), so we batch them up +static const int NUM_BUFFERS = 4; + +// TODO: Determine appropriate return codes +static status_t ERROR_NOT_OPEN = -1; +static status_t ERROR_OPEN_FAILED = -2; +static status_t ERROR_EAS_FAILURE = -3; +static status_t ERROR_ALLOCATE_FAILED = -4; + +static const S_EAS_LIB_CONFIG* pLibConfig = NULL; + +MidiFile::MidiFile() : + mEasData(NULL), mEasHandle(NULL), mAudioBuffer(NULL), + mPlayTime(-1), mDuration(-1), mState(EAS_STATE_ERROR), + mStreamType(AudioTrack::MUSIC), mLoop(false), mExit(false), + mPaused(false), mRender(false), mTid(-1) +{ + LOGV("constructor"); + + mFileLocator.path = NULL; + mFileLocator.fd = -1; + mFileLocator.offset = 0; + mFileLocator.length = 0; + + // get the library configuration and do sanity check + if (pLibConfig == NULL) + pLibConfig = EAS_Config(); + if ((pLibConfig == NULL) || (LIB_VERSION != pLibConfig->libVersion)) { + LOGE("EAS library/header mismatch"); + goto Failed; + } + + // initialize EAS library + if (EAS_Init(&mEasData) != EAS_SUCCESS) { + LOGE("EAS_Init failed"); + goto Failed; + } + + // select reverb preset and enable + EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_PRESET, EAS_PARAM_REVERB_CHAMBER); + EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_BYPASS, EAS_FALSE); + + // create playback thread + { + Mutex::Autolock l(mMutex); + createThreadEtc(renderThread, this, "midithread"); + mCondition.wait(mMutex); + LOGV("thread started"); + } + + // indicate success + if (mTid > 0) { + LOGV(" render thread(%d) started", mTid); + mState = EAS_STATE_READY; + } + +Failed: + return; +} + +status_t MidiFile::initCheck() +{ + if (mState == EAS_STATE_ERROR) return ERROR_EAS_FAILURE; + return NO_ERROR; +} + +MidiFile::~MidiFile() { + LOGV("MidiFile destructor"); + release(); +} + +status_t MidiFile::setDataSource(const char* path) +{ + LOGV("MidiFile::setDataSource url=%s", path); + Mutex::Autolock lock(mMutex); + + // file still open? + if (mEasHandle) { + reset_nosync(); + } + + // open file and set paused state + mFileLocator.path = strdup(path); + mFileLocator.fd = -1; + mFileLocator.offset = 0; + mFileLocator.length = 0; + EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle, &mMemFailedVar); + if (result == EAS_SUCCESS) { + updateState(); + } + + if (result != EAS_SUCCESS) { + LOGE("EAS_OpenFile failed: [%d]", (int)result); + mState = EAS_STATE_ERROR; + return ERROR_OPEN_FAILED; + } + + mState = EAS_STATE_OPEN; + mPlayTime = 0; + return NO_ERROR; +} + +status_t MidiFile::setSigBusHandlerStructTLSKey(pthread_key_t key) +{ + EAS_sigbuskey = key; + return 0; +} + +status_t MidiFile::setDataSource(int fd, int64_t offset, int64_t length) +{ + LOGV("MidiFile::setDataSource fd=%d", fd); + Mutex::Autolock lock(mMutex); + + // file still open? + if (mEasHandle) { + reset_nosync(); + } + + // open file and set paused state + mFileLocator.fd = dup(fd); + mFileLocator.offset = offset; + mFileLocator.length = length; + EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle, &mMemFailedVar); + updateState(); + + if (result != EAS_SUCCESS) { + LOGE("EAS_OpenFile failed: [%d]", (int)result); + mState = EAS_STATE_ERROR; + return ERROR_OPEN_FAILED; + } + + mState = EAS_STATE_OPEN; + mPlayTime = 0; + return NO_ERROR; +} + +status_t MidiFile::prepare() +{ + LOGV("MidiFile::prepare"); + Mutex::Autolock lock(mMutex); + if (!mEasHandle) { + return ERROR_NOT_OPEN; + } + EAS_RESULT result; + if ((result = EAS_Prepare(mEasData, mEasHandle)) != EAS_SUCCESS) { + LOGE("EAS_Prepare failed: [%ld]", result); + return ERROR_EAS_FAILURE; + } + updateState(); + return NO_ERROR; +} + +status_t MidiFile::prepareAsync() +{ + LOGV("MidiFile::prepareAsync"); + status_t ret = prepare(); + + // don't hold lock during callback + if (ret == NO_ERROR) { + sendEvent(MEDIA_PREPARED); + } else { + sendEvent(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ret); + } + return ret; +} + +status_t MidiFile::start() +{ + LOGV("MidiFile::start"); + Mutex::Autolock lock(mMutex); + if (!mEasHandle) { + return ERROR_NOT_OPEN; + } + + // resuming after pause? + if (mPaused) { + if (EAS_Resume(mEasData, mEasHandle) != EAS_SUCCESS) { + return ERROR_EAS_FAILURE; + } + mPaused = false; + updateState(); + } + + mRender = true; + + // wake up render thread + LOGV(" wakeup render thread"); + mCondition.signal(); + return NO_ERROR; +} + +status_t MidiFile::stop() +{ + LOGV("MidiFile::stop"); + Mutex::Autolock lock(mMutex); + if (!mEasHandle) { + return ERROR_NOT_OPEN; + } + if (!mPaused && (mState != EAS_STATE_STOPPED)) { + EAS_RESULT result = EAS_Pause(mEasData, mEasHandle); + if (result != EAS_SUCCESS) { + LOGE("EAS_Pause returned error %ld", result); + return ERROR_EAS_FAILURE; + } + } + mPaused = false; + return NO_ERROR; +} + +status_t MidiFile::seekTo(int position) +{ + LOGV("MidiFile::seekTo %d", position); + // hold lock during EAS calls + { + Mutex::Autolock lock(mMutex); + if (!mEasHandle) { + return ERROR_NOT_OPEN; + } + EAS_RESULT result; + if ((result = EAS_Locate(mEasData, mEasHandle, position, false)) + != EAS_SUCCESS) + { + LOGE("EAS_Locate returned %ld", result); + return ERROR_EAS_FAILURE; + } + EAS_GetLocation(mEasData, mEasHandle, &mPlayTime); + } + sendEvent(MEDIA_SEEK_COMPLETE); + return NO_ERROR; +} + +status_t MidiFile::pause() +{ + LOGV("MidiFile::pause"); + Mutex::Autolock lock(mMutex); + if (!mEasHandle) { + return ERROR_NOT_OPEN; + } + if ((mState == EAS_STATE_PAUSING) || (mState == EAS_STATE_PAUSED)) return NO_ERROR; + if (EAS_Pause(mEasData, mEasHandle) != EAS_SUCCESS) { + return ERROR_EAS_FAILURE; + } + mPaused = true; + return NO_ERROR; +} + +bool MidiFile::isPlaying() +{ + LOGV("MidiFile::isPlaying, mState=%d", int(mState)); + if (!mEasHandle || mPaused) return false; + return (mState == EAS_STATE_PLAY); +} + +status_t MidiFile::getCurrentPosition(int* position) +{ + LOGV("MidiFile::getCurrentPosition"); + if (!mEasHandle) { + LOGE("getCurrentPosition(): file not open"); + return ERROR_NOT_OPEN; + } + if (mPlayTime < 0) { + LOGE("getCurrentPosition(): mPlayTime = %ld", mPlayTime); + return ERROR_EAS_FAILURE; + } + *position = mPlayTime; + return NO_ERROR; +} + +status_t MidiFile::getDuration(int* duration) +{ + + LOGV("MidiFile::getDuration"); + { + Mutex::Autolock lock(mMutex); + if (!mEasHandle) return ERROR_NOT_OPEN; + *duration = mDuration; + } + + // if no duration cached, get the duration + // don't need a lock here because we spin up a new engine + if (*duration < 0) { + EAS_I32 temp; + EAS_DATA_HANDLE easData = NULL; + EAS_HANDLE easHandle = NULL; + EAS_RESULT result = EAS_Init(&easData); + if (result == EAS_SUCCESS) { + result = EAS_OpenFile(easData, &mFileLocator, &easHandle, NULL); + } + if (result == EAS_SUCCESS) { + result = EAS_Prepare(easData, easHandle); + } + if (result == EAS_SUCCESS) { + result = EAS_ParseMetaData(easData, easHandle, &temp); + } + if (easHandle) { + EAS_CloseFile(easData, easHandle); + } + if (easData) { + EAS_Shutdown(easData); + } + + if (result != EAS_SUCCESS) { + return ERROR_EAS_FAILURE; + } + + // cache successful result + mDuration = *duration = int(temp); + } + + return NO_ERROR; +} + +status_t MidiFile::release() +{ + LOGV("MidiFile::release"); + Mutex::Autolock l(mMutex); + reset_nosync(); + + // wait for render thread to exit + mExit = true; + mCondition.signal(); + + // wait for thread to exit + if (mAudioBuffer) { + mCondition.wait(mMutex); + } + + // release resources + if (mEasData) { + EAS_Shutdown(mEasData); + mEasData = NULL; + } + return NO_ERROR; +} + +status_t MidiFile::reset() +{ + LOGV("MidiFile::reset"); + Mutex::Autolock lock(mMutex); + return reset_nosync(); +} + +// call only with mutex held +status_t MidiFile::reset_nosync() +{ + LOGV("MidiFile::reset_nosync"); + // close file + if (mEasHandle) { + EAS_CloseFile(mEasData, mEasHandle); + mEasHandle = NULL; + } + if (mFileLocator.path) { + free((void*)mFileLocator.path); + mFileLocator.path = NULL; + } + if (mFileLocator.fd >= 0) { + close(mFileLocator.fd); + } + mFileLocator.fd = -1; + mFileLocator.offset = 0; + mFileLocator.length = 0; + + mPlayTime = -1; + mDuration = -1; + mLoop = false; + mPaused = false; + mRender = false; + return NO_ERROR; +} + +status_t MidiFile::setLooping(int loop) +{ + LOGV("MidiFile::setLooping"); + Mutex::Autolock lock(mMutex); + if (!mEasHandle) { + return ERROR_NOT_OPEN; + } + loop = loop ? -1 : 0; + if (EAS_SetRepeat(mEasData, mEasHandle, loop) != EAS_SUCCESS) { + return ERROR_EAS_FAILURE; + } + return NO_ERROR; +} + +status_t MidiFile::createOutputTrack() { + if (mAudioSink->open(pLibConfig->sampleRate,pLibConfig->numChannels, 2) != NO_ERROR) { + LOGE("mAudioSink open failed"); + return ERROR_OPEN_FAILED; + } + return NO_ERROR; +} + +int MidiFile::renderThread(void* p) { + + return ((MidiFile*)p)->render(); +} + +int MidiFile::render() { + EAS_RESULT result = EAS_FAILURE; + EAS_I32 count; + int temp; + bool audioStarted = false; + + LOGV("MidiFile::render"); + + struct mediasigbushandler sigbushandler; + + // allocate render buffer + mAudioBuffer = new EAS_PCM[pLibConfig->mixBufferSize * pLibConfig->numChannels * NUM_BUFFERS]; + if (!mAudioBuffer) { + LOGE("mAudioBuffer allocate failed"); + goto threadExit; + } + + // signal main thread that we started + { + Mutex::Autolock l(mMutex); + mTid = myTid(); + LOGV("render thread(%d) signal", mTid); + mCondition.signal(); + } + + sigbushandler.handlesigbus = NULL; + sigbushandler.sigbusvar = mMemFailedVar; + pthread_setspecific(EAS_sigbuskey, &sigbushandler); + + while (1) { + mMutex.lock(); + + // nothing to render, wait for client thread to wake us up + while (!mRender && !mExit) + { + LOGV("MidiFile::render - signal wait"); + mCondition.wait(mMutex); + LOGV("MidiFile::render - signal rx'd"); + } + if (mExit) { + mMutex.unlock(); + break; + } + + // render midi data into the input buffer + //LOGV("MidiFile::render - rendering audio"); + int num_output = 0; + EAS_PCM* p = mAudioBuffer; + for (int i = 0; i < NUM_BUFFERS; i++) { + result = EAS_Render(mEasData, p, pLibConfig->mixBufferSize, &count); + if (result != EAS_SUCCESS) { + LOGE("EAS_Render returned %ld", result); + } + p += count * pLibConfig->numChannels; + num_output += count * pLibConfig->numChannels * sizeof(EAS_PCM); + } + + // update playback state and position + // LOGV("MidiFile::render - updating state"); + EAS_GetLocation(mEasData, mEasHandle, &mPlayTime); + EAS_State(mEasData, mEasHandle, &mState); + mMutex.unlock(); + + // create audio output track if necessary + if (!mAudioSink->ready()) { + LOGV("MidiFile::render - create output track"); + if (createOutputTrack() != NO_ERROR) + goto threadExit; + } + + // Write data to the audio hardware + // LOGV("MidiFile::render - writing to audio output"); + if ((temp = mAudioSink->write(mAudioBuffer, num_output)) < 0) { + LOGE("Error in writing:%d",temp); + return temp; + } + + // start audio output if necessary + if (!audioStarted) { + //LOGV("MidiFile::render - starting audio"); + mAudioSink->start(); + audioStarted = true; + } + + // still playing? + if ((mState == EAS_STATE_STOPPED) || (mState == EAS_STATE_ERROR) || + (mState == EAS_STATE_PAUSED)) + { + switch(mState) { + case EAS_STATE_STOPPED: + { + LOGV("MidiFile::render - stopped"); + sendEvent(MEDIA_PLAYBACK_COMPLETE); + break; + } + case EAS_STATE_ERROR: + { + LOGE("MidiFile::render - error"); + sendEvent(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN); + break; + } + case EAS_STATE_PAUSED: + LOGV("MidiFile::render - paused"); + break; + default: + break; + } + mAudioSink->stop(); + audioStarted = false; + mRender = false; + } + } + +threadExit: + mAudioSink.clear(); + if (mAudioBuffer) { + delete [] mAudioBuffer; + mAudioBuffer = NULL; + } + mMutex.lock(); + mTid = -1; + mCondition.signal(); + mMutex.unlock(); + return result; +} + +} // end namespace android diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h new file mode 100644 index 0000000..9d2dfdd --- /dev/null +++ b/media/libmediaplayerservice/MidiFile.h @@ -0,0 +1,79 @@ +/* +** +** Copyright 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. +*/ + +#ifndef ANDROID_MIDIFILE_H +#define ANDROID_MIDIFILE_H + +#include +#include +#include + +namespace android { + +class MidiFile : public MediaPlayerInterface { +public: + MidiFile(); + ~MidiFile(); + + virtual status_t initCheck(); + virtual status_t setSigBusHandlerStructTLSKey(pthread_key_t key); + virtual status_t setDataSource(const char* path); + virtual status_t setDataSource(int fd, int64_t offset, int64_t length); + virtual status_t setVideoSurface(const sp& surface) { return UNKNOWN_ERROR; } + virtual status_t prepare(); + virtual status_t prepareAsync(); + virtual status_t start(); + virtual status_t stop(); + virtual status_t seekTo(int msec); + virtual status_t pause(); + virtual bool isPlaying(); + virtual status_t getCurrentPosition(int* msec); + virtual status_t getDuration(int* msec); + virtual status_t release(); + virtual status_t reset(); + virtual status_t setLooping(int loop); + virtual player_type playerType() { return SONIVOX_PLAYER; } + +private: + status_t createOutputTrack(); + status_t reset_nosync(); + static int renderThread(void*); + int render(); + void updateState(){ EAS_State(mEasData, mEasHandle, &mState); } + + Mutex mMutex; + Condition mCondition; + int* mMemFailedVar; + EAS_DATA_HANDLE mEasData; + EAS_HANDLE mEasHandle; + EAS_PCM* mAudioBuffer; + EAS_I32 mPlayTime; + EAS_I32 mDuration; + EAS_STATE mState; + EAS_FILE mFileLocator; + int mStreamType; + bool mLoop; + volatile bool mExit; + bool mPaused; + volatile bool mRender; + pid_t mTid; +}; + +}; // namespace android + +#endif // ANDROID_MIDIFILE_H + diff --git a/media/libmediaplayerservice/VorbisPlayer.cpp b/media/libmediaplayerservice/VorbisPlayer.cpp new file mode 100644 index 0000000..a0e0f39 --- /dev/null +++ b/media/libmediaplayerservice/VorbisPlayer.cpp @@ -0,0 +1,527 @@ +/* +** Copyright 2007, 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 "VorbisPlayer" +#include "utils/Log.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "VorbisPlayer.h" + +#ifdef HAVE_GETTID +static pid_t myTid() { return gettid(); } +#else +static pid_t myTid() { return getpid(); } +#endif + +// ---------------------------------------------------------------------------- + +namespace android { + +// ---------------------------------------------------------------------------- + +// TODO: Determine appropriate return codes +static status_t ERROR_NOT_OPEN = -1; +static status_t ERROR_OPEN_FAILED = -2; +static status_t ERROR_ALLOCATE_FAILED = -4; +static status_t ERROR_NOT_SUPPORTED = -8; +static status_t ERROR_NOT_READY = -16; +static status_t STATE_INIT = 0; +static status_t STATE_ERROR = 1; +static status_t STATE_OPEN = 2; + + +VorbisPlayer::VorbisPlayer() : + mAudioBuffer(NULL), mPlayTime(-1), mDuration(-1), mState(STATE_ERROR), + mStreamType(AudioTrack::MUSIC), mLoop(false), mAndroidLoop(false), + mExit(false), mPaused(false), mRender(false), mRenderTid(-1) +{ + LOGV("constructor\n"); + memset(&mVorbisFile, 0, sizeof mVorbisFile); +} + +void VorbisPlayer::onFirstRef() +{ + LOGV("onFirstRef"); + // create playback thread + Mutex::Autolock l(mMutex); + createThreadEtc(renderThread, this, "vorbis decoder"); + mCondition.wait(mMutex); + if (mRenderTid > 0) { + LOGV("render thread(%d) started", mRenderTid); + mState = STATE_INIT; + } +} + +status_t VorbisPlayer::initCheck() +{ + if (mState != STATE_ERROR) return NO_ERROR; + return ERROR_NOT_READY; +} + +VorbisPlayer::~VorbisPlayer() { + LOGV("VorbisPlayer destructor\n"); + release(); +} + +status_t VorbisPlayer::setDataSource(const char* path) +{ + return setdatasource(path, -1, 0, 0x7ffffffffffffffLL); // intentionally less than LONG_MAX +} + +status_t VorbisPlayer::setDataSource(int fd, int64_t offset, int64_t length) +{ + return setdatasource(NULL, fd, offset, length); +} + +size_t VorbisPlayer::vp_fread(void *buf, size_t size, size_t nmemb, void *me) { + VorbisPlayer *self = (VorbisPlayer*) me; + + long curpos = vp_ftell(me); + while (nmemb != 0 && (curpos + size * nmemb) > self->mLength) { + nmemb--; + } + return fread(buf, size, nmemb, self->mFile); +} + +int VorbisPlayer::vp_fseek(void *me, ogg_int64_t off, int whence) { + VorbisPlayer *self = (VorbisPlayer*) me; + if (whence == SEEK_SET) + return fseek(self->mFile, off + self->mOffset, whence); + else if (whence == SEEK_CUR) + return fseek(self->mFile, off, whence); + else if (whence == SEEK_END) + return fseek(self->mFile, self->mOffset + self->mLength + off, SEEK_SET); + return -1; +} + +int VorbisPlayer::vp_fclose(void *me) { + LOGV("vp_fclose"); + VorbisPlayer *self = (VorbisPlayer*) me; + int ret = fclose (self->mFile); + self->mFile = NULL; + return ret; +} + +long VorbisPlayer::vp_ftell(void *me) { + VorbisPlayer *self = (VorbisPlayer*) me; + return ftell(self->mFile) - self->mOffset; +} + +status_t VorbisPlayer::setdatasource(const char *path, int fd, int64_t offset, int64_t length) +{ + LOGV("setDataSource url=%s, fd=%d\n", path, fd); + + // file still open? + Mutex::Autolock l(mMutex); + if (mState == STATE_OPEN) { + reset_nosync(); + } + + // open file and set paused state + if (path) { + mFile = fopen(path, "r"); + } else { + mFile = fdopen(dup(fd), "r"); + } + if (mFile == NULL) { + return ERROR_OPEN_FAILED; + } + + struct stat sb; + int ret; + if (path) { + ret = stat(path, &sb); + } else { + ret = fstat(fd, &sb); + } + if (ret != 0) { + mState = STATE_ERROR; + fclose(mFile); + return ERROR_OPEN_FAILED; + } + if (sb.st_size > (length + offset)) { + mLength = length; + } else { + mLength = sb.st_size - offset; + } + + ov_callbacks callbacks = { + (size_t (*)(void *, size_t, size_t, void *)) vp_fread, + (int (*)(void *, ogg_int64_t, int)) vp_fseek, + (int (*)(void *)) vp_fclose, + (long (*)(void *)) vp_ftell + }; + + mOffset = offset; + fseek(mFile, offset, SEEK_SET); + + int result = ov_open_callbacks(this, &mVorbisFile, NULL, 0, callbacks); + if (result < 0) { + LOGE("ov_open() failed: [%d]\n", (int)result); + mState = STATE_ERROR; + fclose(mFile); + return ERROR_OPEN_FAILED; + } + + // look for the android loop tag (for ringtones) + char **ptr = ov_comment(&mVorbisFile,-1)->user_comments; + while(*ptr) { + // does the comment start with ANDROID_LOOP_TAG + if(strncmp(*ptr, ANDROID_LOOP_TAG, strlen(ANDROID_LOOP_TAG)) == 0) { + // read the value of the tag + char *val = *ptr + strlen(ANDROID_LOOP_TAG) + 1; + mAndroidLoop = (strncmp(val, "true", 4) == 0); + } + // we keep parsing even after finding one occurence of ANDROID_LOOP_TAG, + // as we could find another one (the tag might have been appended more than once). + ++ptr; + } + LOGV_IF(mAndroidLoop, "looped sound"); + + mState = STATE_OPEN; + return NO_ERROR; +} + +status_t VorbisPlayer::prepare() +{ + LOGV("prepare\n"); + if (mState != STATE_OPEN ) { + return ERROR_NOT_OPEN; + } + return NO_ERROR; +} + +status_t VorbisPlayer::prepareAsync() { + LOGV("prepareAsync\n"); + // can't hold the lock here because of the callback + // it's safe because we don't change state + if (mState != STATE_OPEN ) { + sendEvent(MEDIA_ERROR); + return NO_ERROR; + } + sendEvent(MEDIA_PREPARED); + return NO_ERROR; +} + +status_t VorbisPlayer::start() +{ + LOGV("start\n"); + Mutex::Autolock l(mMutex); + if (mState != STATE_OPEN) { + return ERROR_NOT_OPEN; + } + + mPaused = false; + mRender = true; + + // wake up render thread + LOGV(" wakeup render thread\n"); + mCondition.signal(); + return NO_ERROR; +} + +status_t VorbisPlayer::stop() +{ + LOGV("stop\n"); + Mutex::Autolock l(mMutex); + if (mState != STATE_OPEN) { + return ERROR_NOT_OPEN; + } + mPaused = true; + mRender = false; + return NO_ERROR; +} + +status_t VorbisPlayer::seekTo(int position) +{ + LOGV("seekTo %d\n", position); + Mutex::Autolock l(mMutex); + if (mState != STATE_OPEN) { + return ERROR_NOT_OPEN; + } + + int result = ov_time_seek(&mVorbisFile, position); + if (result != 0) { + LOGE("ov_time_seek() returned %d\n", result); + return result; + } + sendEvent(MEDIA_SEEK_COMPLETE); + return NO_ERROR; +} + +status_t VorbisPlayer::pause() +{ + LOGV("pause\n"); + Mutex::Autolock l(mMutex); + if (mState != STATE_OPEN) { + return ERROR_NOT_OPEN; + } + mPaused = true; + return NO_ERROR; +} + +bool VorbisPlayer::isPlaying() +{ + LOGV("isPlaying\n"); + if (mState == STATE_OPEN) { + return mRender; + } + return false; +} + +status_t VorbisPlayer::getCurrentPosition(int* position) +{ + LOGV("getCurrentPosition\n"); + Mutex::Autolock l(mMutex); + if (mState != STATE_OPEN) { + LOGE("getCurrentPosition(): file not open"); + return ERROR_NOT_OPEN; + } + *position = ov_time_tell(&mVorbisFile); + if (*position < 0) { + LOGE("getCurrentPosition(): ov_time_tell returned %d", *position); + return *position; + } + return NO_ERROR; +} + +status_t VorbisPlayer::getDuration(int* duration) +{ + LOGV("getDuration\n"); + Mutex::Autolock l(mMutex); + if (mState != STATE_OPEN) { + return ERROR_NOT_OPEN; + } + + int ret = ov_time_total(&mVorbisFile, -1); + if (ret == OV_EINVAL) { + return -1; + } + + *duration = ret; + return NO_ERROR; +} + +status_t VorbisPlayer::release() +{ + LOGV("release\n"); + Mutex::Autolock l(mMutex); + reset_nosync(); + + // TODO: timeout when thread won't exit + // wait for render thread to exit + if (mRenderTid > 0) { + mExit = true; + mCondition.signal(); + mCondition.wait(mMutex); + } + return NO_ERROR; +} + +status_t VorbisPlayer::reset() +{ + LOGV("reset\n"); + Mutex::Autolock l(mMutex); + if (mState != STATE_OPEN) { + return NO_ERROR; + } + return reset_nosync(); +} + +// always call with lock held +status_t VorbisPlayer::reset_nosync() +{ + // close file + ov_clear(&mVorbisFile); // this also closes the FILE + if (mFile != NULL) { + LOGV("OOPS! Vorbis didn't close the file"); + fclose(mFile); + } + mState = STATE_ERROR; + + mPlayTime = -1; + mDuration = -1; + mLoop = false; + mAndroidLoop = false; + mPaused = false; + mRender = false; + return NO_ERROR; +} + +status_t VorbisPlayer::setLooping(int loop) +{ + LOGV("setLooping\n"); + Mutex::Autolock l(mMutex); + mLoop = (loop != 0); + return NO_ERROR; +} + +status_t VorbisPlayer::createOutputTrack() { + // open audio track + vorbis_info *vi = ov_info(&mVorbisFile, -1); + + LOGV("Create AudioTrack object: rate=%ld, channels=%d\n", + vi->rate, vi->channels); + if (mAudioSink->open(vi->rate, vi->channels, DEFAULT_AUDIOSINK_BUFFERCOUNT) != NO_ERROR) { + LOGE("mAudioSink open failed"); + return ERROR_OPEN_FAILED; + } + return NO_ERROR; +} + +int VorbisPlayer::renderThread(void* p) { + return ((VorbisPlayer*)p)->render(); +} + +#define AUDIOBUFFER_SIZE 4096 + +int VorbisPlayer::render() { + int result = -1; + int temp; + int current_section = 0; + bool audioStarted = false; + + LOGV("render\n"); + + // allocate render buffer + mAudioBuffer = new char[AUDIOBUFFER_SIZE]; + if (!mAudioBuffer) { + LOGE("mAudioBuffer allocate failed\n"); + goto threadExit; + } + + // let main thread know we're ready + { + Mutex::Autolock l(mMutex); + mRenderTid = myTid(); + mCondition.signal(); + } + + while (1) { + long numread = 0; + { + Mutex::Autolock l(mMutex); + + // pausing? + if (mPaused) { + if (mAudioSink->ready()) mAudioSink->pause(); + mRender = false; + audioStarted = false; + } + + // nothing to render, wait for client thread to wake us up + if (!mExit && !mRender) { + LOGV("render - signal wait\n"); + mCondition.wait(mMutex); + LOGV("render - signal rx'd\n"); + } + if (mExit) break; + + // We could end up here if start() is called, and before we get a + // chance to run, the app calls stop() or reset(). Re-check render + // flag so we don't try to render in stop or reset state. + if (!mRender) continue; + + // render vorbis data into the input buffer + numread = ov_read(&mVorbisFile, mAudioBuffer, AUDIOBUFFER_SIZE, ¤t_section); + if (numread == 0) { + // end of file, do we need to loop? + // ... + if (mLoop || mAndroidLoop) { + ov_time_seek(&mVorbisFile, 0); + current_section = 0; + numread = ov_read(&mVorbisFile, mAudioBuffer, AUDIOBUFFER_SIZE, ¤t_section); + } else { + sendEvent(MEDIA_PLAYBACK_COMPLETE); + mAudioSink->stop(); + audioStarted = false; + mRender = false; + mPaused = true; + int endpos = ov_time_tell(&mVorbisFile); + + // wait until we're started again + LOGV("playback complete - wait for signal"); + mCondition.wait(mMutex); + LOGV("playback complete - signal rx'd"); + if (mExit) break; + + // if we're still at the end, restart from the beginning + if (mState == STATE_OPEN) { + int curpos = ov_time_tell(&mVorbisFile); + if (curpos == endpos) { + ov_time_seek(&mVorbisFile, 0); + } + current_section = 0; + numread = ov_read(&mVorbisFile, mAudioBuffer, AUDIOBUFFER_SIZE, ¤t_section); + } + } + } + } + + // codec returns negative number on error + if (numread < 0) { + LOGE("Error in Vorbis decoder"); + sendEvent(MEDIA_ERROR); + break; + } + + // create audio output track if necessary + if (!mAudioSink->ready()) { + LOGV("render - create output track\n"); + if (createOutputTrack() != NO_ERROR) + break; + } + + // Write data to the audio hardware + if ((temp = mAudioSink->write(mAudioBuffer, numread)) < 0) { + LOGE("Error in writing:%d",temp); + result = temp; + break; + } + + // start audio output if necessary + if (!audioStarted && !mPaused && !mExit) { + LOGV("render - starting audio\n"); + mAudioSink->start(); + audioStarted = true; + } + } + +threadExit: + mAudioSink.clear(); + if (mAudioBuffer) { + delete [] mAudioBuffer; + mAudioBuffer = NULL; + } + + // tell main thread goodbye + Mutex::Autolock l(mMutex); + mRenderTid = -1; + mCondition.signal(); + return result; +} + +} // end namespace android diff --git a/media/libmediaplayerservice/VorbisPlayer.h b/media/libmediaplayerservice/VorbisPlayer.h new file mode 100644 index 0000000..c30dc1b --- /dev/null +++ b/media/libmediaplayerservice/VorbisPlayer.h @@ -0,0 +1,91 @@ +/* +** +** Copyright 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. +*/ + +#ifndef ANDROID_VORBISPLAYER_H +#define ANDROID_VORBISPLAYER_H + +#include + +#include +#include + +#include "ivorbiscodec.h" +#include "ivorbisfile.h" + +#define ANDROID_LOOP_TAG "ANDROID_LOOP" + +namespace android { + +class VorbisPlayer : public MediaPlayerInterface { +public: + VorbisPlayer(); + ~VorbisPlayer(); + + virtual void onFirstRef(); + virtual status_t initCheck(); + virtual status_t setDataSource(const char* path); + virtual status_t setDataSource(int fd, int64_t offset, int64_t length); + virtual status_t setVideoSurface(const sp& surface) { return UNKNOWN_ERROR; } + virtual status_t prepare(); + virtual status_t prepareAsync(); + virtual status_t start(); + virtual status_t stop(); + virtual status_t seekTo(int msec); + virtual status_t pause(); + virtual bool isPlaying(); + virtual status_t getCurrentPosition(int* msec); + virtual status_t getDuration(int* msec); + virtual status_t release(); + virtual status_t reset(); + virtual status_t setLooping(int loop); + virtual player_type playerType() { return VORBIS_PLAYER; } + +private: + status_t setdatasource(const char *path, int fd, int64_t offset, int64_t length); + status_t reset_nosync(); + status_t createOutputTrack(); + static int renderThread(void*); + int render(); + + static size_t vp_fread(void *, size_t, size_t, void *); + static int vp_fseek(void *, ogg_int64_t, int); + static int vp_fclose(void *); + static long vp_ftell(void *); + + Mutex mMutex; + Condition mCondition; + FILE* mFile; + int64_t mOffset; + int64_t mLength; + OggVorbis_File mVorbisFile; + char* mAudioBuffer; + int mPlayTime; + int mDuration; + status_t mState; + int mStreamType; + bool mLoop; + bool mAndroidLoop; + volatile bool mExit; + bool mPaused; + volatile bool mRender; + pid_t mRenderTid; +}; + +}; // namespace android + +#endif // ANDROID_VORBISPLAYER_H + diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk new file mode 100644 index 0000000..c681698 --- /dev/null +++ b/media/mediaserver/Android.mk @@ -0,0 +1,22 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + main_mediaserver.cpp + +LOCAL_SHARED_LIBRARIES := \ + libaudioflinger \ + libcameraservice \ + libmediaplayerservice \ + libutils + +base := $(LOCAL_PATH)/../.. + +LOCAL_C_INCLUDES := \ + $(base)/libs/audioflinger \ + $(base)/camera/libcameraservice \ + $(base)/media/libmediaplayerservice + +LOCAL_MODULE:= mediaserver + +include $(BUILD_EXECUTABLE) diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp new file mode 100644 index 0000000..6954b63 --- /dev/null +++ b/media/mediaserver/main_mediaserver.cpp @@ -0,0 +1,45 @@ +/* +** +** Copyright 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. +*/ + +// System headers required for setgroups, etc. +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace android; + +int main(int argc, char** argv) +{ + sp proc(ProcessState::self()); + sp sm = defaultServiceManager(); + LOGI("ServiceManager: %p", sm.get()); + AudioFlinger::instantiate(); + MediaPlayerService::instantiate(); + CameraService::instantiate(); + ProcessState::self()->startThreadPool(); + IPCThreadState::self()->joinThreadPool(); +} -- cgit v1.1