diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:43 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:43 -0800 |
commit | 7b5eb023f8d87cca6d830ae6c11c6aadbe02aca8 (patch) | |
tree | d9838a098f868ee2fcffc20627a8c9a33cb23377 | |
parent | 2729ea9262ca60d93047e984739887cfc89e82eb (diff) | |
download | frameworks_av-7b5eb023f8d87cca6d830ae6c11c6aadbe02aca8.zip frameworks_av-7b5eb023f8d87cca6d830ae6c11c6aadbe02aca8.tar.gz frameworks_av-7b5eb023f8d87cca6d830ae6c11c6aadbe02aca8.tar.bz2 |
Code drop from //branches/cupcake/...@124589
43 files changed, 4123 insertions, 809 deletions
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h index 008569f..71744be 100644 --- a/include/media/AudioRecord.h +++ b/include/media/AudioRecord.h @@ -37,16 +37,29 @@ namespace android { class AudioRecord { -public: - +public: + enum stream_type { DEFAULT_INPUT =-1, MIC_INPUT = 0, NUM_STREAM_TYPES }; - - static const int DEFAULT_SAMPLE_RATE = 8000; - + + static const int DEFAULT_SAMPLE_RATE = 8000; + + /* Events used by AudioRecord callback function (callback_t). + * + * to keep in sync with frameworks/base/media/java/android/media/AudioRecord.java + */ + enum event_type { + EVENT_MORE_DATA = 0, // Request to reqd more data from PCM buffer. + EVENT_OVERRUN = 1, // PCM buffer overrun occured. + EVENT_MARKER = 2, // Record head is at the specified marker position + // (See setMarkerPosition()). + EVENT_NEW_POS = 3, // Record head is at a new position + // (See setPositionUpdatePeriod()). + }; + /* Create Buffer on the stack and pass it to obtainBuffer() * and releaseBuffer(). */ @@ -75,71 +88,108 @@ public: // 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. + * invokes the callback when a new buffer becomes ready or an overrun condition occurs. + * Parameters: + * + * event: type of event notified (see enum AudioRecord::event_type). + * user: Pointer to context for use by the callback receiver. + * info: Pointer to optional parameter according to event type: + * - EVENT_MORE_DATA: pointer to AudioRecord::Buffer struct. The callback must not read + * more bytes than indicated by 'size' field and update 'size' if less bytes are + * read. + * - EVENT_OVERRUN: unused. + * - EVENT_MARKER: pointer to an uin32_t containing the marker position in frames. + * - EVENT_NEW_POS: pointer to an uin32_t containing the new position in frames. */ - typedef bool (*callback_t)(void* user, const Buffer& info); + + typedef void (*callback_t)(int event, void* user, void *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. + * + * Parameters: + * + * streamType: Select the audio input to record to (e.g. AudioRecord::MIC_INPUT). + * sampleRate: Track sampling rate in Hz. + * format: PCM sample format (e.g AudioSystem::PCM_16_BIT for signed + * 16 bits per sample). + * channelCount: Number of PCM channels (e.g 2 for stereo). + * frameCount: Total size of track PCM buffer in frames. This defines the + * latency of the track. + * flags: Reserved for future use. + * cbf: Callback function. If not null, this function is called periodically + * to provide new PCM data. + * notificationFrames: The callback function is called each time notificationFrames PCM + * frames are ready in record track output buffer. + * user Context for use by the callback receiver. */ - - AudioRecord(int streamType = 0, + + AudioRecord(int streamType, uint32_t sampleRate = 0, int format = 0, int channelCount = 0, - int bufferCount = 0, + int frameCount = 0, uint32_t flags = 0, - callback_t cbf = 0, void* user = 0); + callback_t cbf = 0, + void* user = 0, + int notificationFrames = 0); /* Terminates the AudioRecord and unregisters it from AudioFlinger. * Also destroys all resources assotiated with the AudioRecord. - */ + */ ~AudioRecord(); - /* Initialize an uninitialized AudioRecord. */ + /* Initialize an uninitialized AudioRecord. + * Returned status (from utils/Errors.h) can be: + * - NO_ERROR: successful intialization + * - INVALID_OPERATION: AudioRecord is already intitialized or record device is already in use + * - BAD_VALUE: invalid parameter (channelCount, format, sampleRate...) + * - NO_INIT: audio server or audio hardware not initialized + * - PERMISSION_DENIED: recording is not allowed for the requesting process + * */ status_t set(int streamType = 0, uint32_t sampleRate = 0, int format = 0, int channelCount = 0, - int bufferCount = 0, + int frameCount = 0, uint32_t flags = 0, - callback_t cbf = 0, void* user = 0); - + callback_t cbf = 0, + void* user = 0, + int notificationFrames = 0, + bool threadCanCallJava = false); + /* Result of constructing the AudioRecord. This must be checked * before using any AudioRecord API (except for set()), using - * an uninitialized AudioRecord prduces undefined results. + * an uninitialized AudioRecord produces undefined results. + * See set() method above for possible return codes. */ 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. + /* Returns this track's latency in milliseconds. + * This includes the latency due to AudioRecord buffer size + * and audio hardware driver. */ - nsecs_t latency() const; + uint32_t latency() const; + + /* getters, see constructor */ - /* getters, see constructor */ - uint32_t sampleRate() const; int format() const; int channelCount() const; - int bufferCount() const; + uint32_t frameCount() const; + int frameSize() const; /* After it's created the track is not active. Call start() to @@ -158,27 +208,79 @@ public: */ uint32_t getSampleRate(); + /* Sets marker position. When record reaches the number of frames specified, + * a callback with event type EVENT_MARKER is called. Calling setMarkerPosition + * with marker == 0 cancels marker notification callback. + * If the AudioRecord has been opened with no callback function associated, + * the operation will fail. + * + * Parameters: + * + * marker: marker position expressed in frames. + * + * Returned status (from utils/Errors.h) can be: + * - NO_ERROR: successful operation + * - INVALID_OPERATION: the AudioRecord has no callback installed. + */ + status_t setMarkerPosition(uint32_t marker); + status_t getMarkerPosition(uint32_t *marker); + + + /* Sets position update period. Every time the number of frames specified has been recorded, + * a callback with event type EVENT_NEW_POS is called. + * Calling setPositionUpdatePeriod with updatePeriod == 0 cancels new position notification + * callback. + * If the AudioRecord has been opened with no callback function associated, + * the operation will fail. + * + * Parameters: + * + * updatePeriod: position update notification period expressed in frames. + * + * Returned status (from utils/Errors.h) can be: + * - NO_ERROR: successful operation + * - INVALID_OPERATION: the AudioRecord has no callback installed. + */ + status_t setPositionUpdatePeriod(uint32_t updatePeriod); + status_t getPositionUpdatePeriod(uint32_t *updatePeriod); + + + /* Gets record head position. The position is the total number of frames + * recorded since record start. + * + * Parameters: + * + * position: Address where to return record head position within AudioRecord buffer. + * + * Returned status (from utils/Errors.h) can be: + * - NO_ERROR: successful operation + * - BAD_VALUE: position is NULL + */ + status_t getPosition(uint32_t *position); + + + /* 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 + * is exhausted, at which point obtainBuffer() will either block * or return WOULD_BLOCK depending on the value of the "blocking" - * parameter. + * 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. + * This is implemented on top of lockBuffer/unlockBuffer. */ ssize_t read(void* buffer, size_t size); @@ -191,15 +293,16 @@ private: class ClientRecordThread : public Thread { public: - ClientRecordThread(AudioRecord& receiver); + ClientRecordThread(AudioRecord& receiver, bool bCanCallJava = false); private: friend class AudioRecord; virtual bool threadLoop(); virtual status_t readyToRun() { return NO_ERROR; } virtual void onFirstRef() {} AudioRecord& mReceiver; + Mutex mLock; }; - + bool processAudioBuffer(const sp<ClientRecordThread>& thread); sp<IAudioFlinger> mAudioFlinger; @@ -207,27 +310,26 @@ private: sp<IMemory> mCblkMemory; sp<ClientRecordThread> mClientRecordThread; Mutex mRecordThreadLock; - + uint32_t mSampleRate; - size_t mFrameCount; + uint32_t mFrameCount; audio_track_cblk_t* mCblk; uint8_t mFormat; - uint8_t mBufferCount; - uint8_t mChannelCount : 4; - uint8_t mReserved : 3; + uint8_t mChannelCount; + uint8_t mReserved[2]; status_t mStatus; - nsecs_t mLatency; + uint32_t mLatency; volatile int32_t mActive; callback_t mCbf; void* mUserData; - - AudioRecord::Buffer mAudioBuffer; - size_t mPosition; - - uint32_t mReservedFBC[4]; + uint32_t mNotificationFrames; + uint32_t mRemainingFrames; + uint32_t mMarkerPosition; + uint32_t mNewPosition; + uint32_t mUpdatePeriod; }; }; // namespace android diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index 9fcbea5..77676bf 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -48,9 +48,10 @@ public: enum audio_routes { ROUTE_EARPIECE = (1 << 0), ROUTE_SPEAKER = (1 << 1), - ROUTE_BLUETOOTH = (1 << 2), + ROUTE_BLUETOOTH_SCO = (1 << 2), ROUTE_HEADSET = (1 << 3), - ROUTE_ALL = (ROUTE_EARPIECE | ROUTE_SPEAKER | ROUTE_BLUETOOTH | ROUTE_HEADSET) + ROUTE_BLUETOOTH_A2DP = (1 << 4), + ROUTE_ALL = 0xFFFFFFFF }; /* These are static methods to control the system-wide AudioFlinger @@ -95,6 +96,10 @@ public: static float linearToLog(int volume); static int logToLinear(float volume); + static status_t getOutputSamplingRate(int* samplingRate); + static status_t getOutputFrameCount(int* frameCount); + static status_t getOutputLatency(uint32_t* latency); + // ---------------------------------------------------------------------------- private: @@ -115,6 +120,9 @@ private: static Mutex gLock; static sp<IAudioFlinger> gAudioFlinger; static audio_error_callback gAudioErrorCallback; + static int gOutSamplingRate; + static int gOutFrameCount; + static uint32_t gOutLatency; }; }; // namespace android diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index a89d7ff..f382451 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -41,15 +41,16 @@ class audio_track_cblk_t; class AudioTrack { -public: +public: enum stream_type { - DEFAULT =-1, - VOICE_CALL = 0, - SYSTEM = 1, - RING = 2, - MUSIC = 3, - ALARM = 4, + DEFAULT =-1, + VOICE_CALL = 0, + SYSTEM = 1, + RING = 2, + MUSIC = 3, + ALARM = 4, + NOTIFICATION = 5, NUM_STREAM_TYPES }; @@ -59,6 +60,17 @@ public: RIGHT = 1 }; + /* Events used by AudioTrack callback function (audio_track_cblk_t). + */ + enum event_type { + EVENT_MORE_DATA = 0, // Request to write more data to PCM buffer. + EVENT_UNDERRUN = 1, // PCM buffer underrun occured. + EVENT_LOOP_END = 2, // Sample loop end was reached; playback restarted from loop start if loop count was not 0. + EVENT_MARKER = 3, // Playback head is at the specified marker position (See setMarkerPosition()). + EVENT_NEW_POS = 4, // Playback head is at a new position (See setPositionUpdatePeriod()). + EVENT_BUFFER_END = 5 // Playback head is at the end of the buffer. + }; + /* Create Buffer on the stack and pass it to obtainBuffer() * and releaseBuffer(). */ @@ -81,72 +93,132 @@ public: }; }; - /* 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. + * invokes the callback when a new buffer becomes availlable or an underrun condition occurs. + * Parameters: + * + * event: type of event notified (see enum AudioTrack::event_type). + * user: Pointer to context for use by the callback receiver. + * info: Pointer to optional parameter according to event type: + * - EVENT_MORE_DATA: pointer to AudioTrack::Buffer struct. The callback must not write + * more bytes than indicated by 'size' field and update 'size' if less bytes are + * written. + * - EVENT_UNDERRUN: unused. + * - EVENT_LOOP_END: pointer to an int indicating the number of loops remaining. + * - EVENT_MARKER: pointer to an uin32_t containing the marker position in frames. + * - EVENT_NEW_POS: pointer to an uin32_t containing the new position in frames. + * - EVENT_BUFFER_END: unused. */ - typedef void (*callback_t)(void* user, const Buffer& info); + + typedef void (*callback_t)(int event, void* user, void *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. + * + * Parameters: + * + * streamType: Select the type of audio stream this track is attached to + * (e.g. AudioTrack::MUSIC). + * sampleRate: Track sampling rate in Hz. + * format: PCM sample format (e.g AudioSystem::PCM_16_BIT for signed + * 16 bits per sample). + * channelCount: Number of PCM channels (e.g 2 for stereo). + * frameCount: Total size of track PCM buffer in frames. This defines the + * latency of the track. + * flags: Reserved for future use. + * cbf: Callback function. If not null, this function is called periodically + * to request new PCM data. + * notificationFrames: The callback function is called each time notificationFrames PCM + * frames have been comsumed from track input buffer. + * user Context for use by the callback receiver. */ - + + AudioTrack( int streamType, + uint32_t sampleRate = 0, + int format = 0, + int channelCount = 0, + int frameCount = 0, + uint32_t flags = 0, + callback_t cbf = 0, + void* user = 0, + int notificationFrames = 0); + + /* Creates an audio track and registers it with AudioFlinger. With this constructor, + * The PCM data to be rendered by AudioTrack is passed in a shared memory buffer + * identified by the argument sharedBuffer. This prototype is for static buffer playback. + * PCM data must be present into memory before the AudioTrack is started. + * The Write() and Flush() methods are not supported in this case. + * It is recommented to pass a callback function to be notified of playback end by an + * EVENT_UNDERRUN event. + */ + AudioTrack( int streamType, uint32_t sampleRate = 0, int format = 0, int channelCount = 0, - int bufferCount = 0, + const sp<IMemory>& sharedBuffer = 0, uint32_t flags = 0, - callback_t cbf = 0, void* user = 0); - + callback_t cbf = 0, + void* user = 0, + int notificationFrames = 0); /* Terminates the AudioTrack and unregisters it from AudioFlinger. * Also destroys all resources assotiated with the AudioTrack. - */ + */ ~AudioTrack(); - /* Initialize an uninitialized AudioTrack. */ + /* Initialize an uninitialized AudioTrack. + * Returned status (from utils/Errors.h) can be: + * - NO_ERROR: successful intialization + * - INVALID_OPERATION: AudioTrack is already intitialized + * - BAD_VALUE: invalid parameter (channelCount, format, sampleRate...) + * - NO_INIT: audio server or audio hardware not initialized + * */ status_t set(int streamType =-1, uint32_t sampleRate = 0, int format = 0, int channelCount = 0, - int bufferCount = 0, + int frameCount = 0, uint32_t flags = 0, - callback_t cbf = 0, void* user = 0); - + callback_t cbf = 0, + void* user = 0, + int notificationFrames = 0, + const sp<IMemory>& sharedBuffer = 0, + bool threadCanCallJava = false); + /* Result of constructing the AudioTrack. This must be checked * before using any AudioTrack API (except for set()), using - * an uninitialized AudoiTrack prduces undefined results. + * an uninitialized AudioTrack produces undefined results. + * See set() method above for possible return codes. */ 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. + /* Returns this track's latency in milliseconds. + * This includes the latency due to AudioTrack buffer size, AudioMixer (if any) + * and audio hardware driver. */ - nsecs_t latency() const; + uint32_t latency() const; + + /* getters, see constructor */ - /* getters, see constructor */ - int streamType() const; uint32_t sampleRate() const; int format() const; int channelCount() const; - int bufferCount() const; + uint32_t frameCount() const; + int frameSize() const; + sp<IMemory>& sharedBuffer(); /* After it's created the track is not active. Call start() to @@ -189,21 +261,103 @@ public: void setSampleRate(int sampleRate); uint32_t getSampleRate(); + /* Enables looping and sets the start and end points of looping. + * + * Parameters: + * + * loopStart: loop start expressed as the number of PCM frames played since AudioTrack start. + * loopEnd: loop end expressed as the number of PCM frames played since AudioTrack start. + * loopCount: number of loops to execute. Calling setLoop() with loopCount == 0 cancels any pending or + * active loop. loopCount = -1 means infinite looping. + * + * For proper operation the following condition must be respected: + * (loopEnd-loopStart) <= framecount() + */ + status_t setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount); + status_t getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount); + + + /* Sets marker position. When playback reaches the number of frames specified, a callback with event + * type EVENT_MARKER is called. Calling setMarkerPosition with marker == 0 cancels marker notification + * callback. + * If the AudioTrack has been opened with no callback function associated, the operation will fail. + * + * Parameters: + * + * marker: marker position expressed in frames. + * + * Returned status (from utils/Errors.h) can be: + * - NO_ERROR: successful operation + * - INVALID_OPERATION: the AudioTrack has no callback installed. + */ + status_t setMarkerPosition(uint32_t marker); + status_t getMarkerPosition(uint32_t *marker); + + + /* Sets position update period. Every time the number of frames specified has been played, + * a callback with event type EVENT_NEW_POS is called. + * Calling setPositionUpdatePeriod with updatePeriod == 0 cancels new position notification + * callback. + * If the AudioTrack has been opened with no callback function associated, the operation will fail. + * + * Parameters: + * + * updatePeriod: position update notification period expressed in frames. + * + * Returned status (from utils/Errors.h) can be: + * - NO_ERROR: successful operation + * - INVALID_OPERATION: the AudioTrack has no callback installed. + */ + status_t setPositionUpdatePeriod(uint32_t updatePeriod); + status_t getPositionUpdatePeriod(uint32_t *updatePeriod); + + + /* Sets playback head position within AudioTrack buffer. The new position is specified + * in number of frames. + * This method must be called with the AudioTrack in paused or stopped state. + * Note that the actual position set is <position> modulo the AudioTrack buffer size in frames. + * Therefore using this method makes sense only when playing a "static" audio buffer + * as opposed to streaming. + * The getPosition() method on the other hand returns the total number of frames played since + * playback start. + * + * Parameters: + * + * position: New playback head position within AudioTrack buffer. + * + * Returned status (from utils/Errors.h) can be: + * - NO_ERROR: successful operation + * - INVALID_OPERATION: the AudioTrack is not stopped. + * - BAD_VALUE: The specified position is beyond the number of frames present in AudioTrack buffer + */ + status_t setPosition(uint32_t position); + status_t getPosition(uint32_t *position); + + /* Forces AudioTrack buffer full condition. When playing a static buffer, this method avoids + * rewriting the buffer before restarting playback after a stop. + * This method must be called with the AudioTrack in paused or stopped state. + * + * Returned status (from utils/Errors.h) can be: + * - NO_ERROR: successful operation + * - INVALID_OPERATION: the AudioTrack is not stopped. + */ + status_t reload(); + /* 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 + * is exhausted, at which point obtainBuffer() will either block * or return WOULD_BLOCK depending on the value of the "blocking" - * parameter. + * parameter. */ - + enum { NO_MORE_BUFFERS = 0x80000001, STOPPED = 1 }; - + status_t obtainBuffer(Buffer* audioBuffer, bool blocking); void releaseBuffer(Buffer* audioBuffer); @@ -211,10 +365,10 @@ public: /* 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. */ @@ -229,7 +383,7 @@ private: class AudioTrackThread : public Thread { public: - AudioTrackThread(AudioTrack& receiver); + AudioTrackThread(AudioTrack& receiver, bool bCanCallJava = false); private: friend class AudioTrack; virtual bool threadLoop(); @@ -238,37 +392,37 @@ private: AudioTrack& mReceiver; Mutex mLock; }; - + bool processAudioBuffer(const sp<AudioTrackThread>& thread); sp<IAudioFlinger> mAudioFlinger; sp<IAudioTrack> mAudioTrack; sp<IMemory> mCblkMemory; sp<AudioTrackThread> mAudioTrackThread; - + float mVolume[2]; uint32_t mSampleRate; - size_t mFrameCount; + uint32_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; + uint8_t mChannelCount; + uint8_t mMuted; status_t mStatus; - nsecs_t mLatency; + uint32_t mLatency; volatile int32_t mActive; callback_t mCbf; void* mUserData; - - AudioTrack::Buffer mAudioBuffer; - size_t mPosition; - - uint32_t mReservedFBC[4]; + uint32_t mNotificationFrames; + sp<IMemory> mSharedBuffer; + int mLoopCount; + uint32_t mRemainingFrames; + uint32_t mMarkerPosition; + uint32_t mNewPosition; + uint32_t mUpdatePeriod; }; diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h index fa8e121..69703b2 100644 --- a/include/media/IAudioFlinger.h +++ b/include/media/IAudioFlinger.h @@ -46,8 +46,10 @@ public: uint32_t sampleRate, int format, int channelCount, - int bufferCount, - uint32_t flags) = 0; + int frameCount, + uint32_t flags, + const sp<IMemory>& sharedBuffer, + status_t *status) = 0; virtual sp<IAudioRecord> openRecord( pid_t pid, @@ -55,8 +57,9 @@ public: uint32_t sampleRate, int format, int channelCount, - int bufferCount, - uint32_t flags) = 0; + int frameCount, + uint32_t flags, + status_t *status) = 0; /* query the audio hardware state. This state never changes, * and therefore can be cached. @@ -65,6 +68,7 @@ public: virtual int channelCount() const = 0; virtual int format() const = 0; virtual size_t frameCount() const = 0; + virtual uint32_t latency() const = 0; /* set/get the audio hardware state. This will probably be used by * the preference panel, mostly. diff --git a/include/media/IMediaMetadataRetriever.h b/include/media/IMediaMetadataRetriever.h new file mode 100644 index 0000000..c677e83 --- /dev/null +++ b/include/media/IMediaMetadataRetriever.h @@ -0,0 +1,56 @@ +/* +** +** 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_IMEDIAMETADATARETRIEVER_H +#define ANDROID_IMEDIAMETADATARETRIEVER_H + +#include <utils/RefBase.h> +#include <utils/IInterface.h> +#include <utils/Parcel.h> +#include <utils/IMemory.h> + +namespace android { + +class IMediaMetadataRetriever: public IInterface +{ +public: + DECLARE_META_INTERFACE(MediaMetadataRetriever); + virtual void disconnect() = 0; + virtual status_t setDataSource(const char* srcUrl) = 0; + virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0; + virtual status_t setMode(int mode) = 0; + virtual status_t getMode(int* mode) const = 0; + virtual sp<IMemory> captureFrame() = 0; + virtual sp<IMemory> extractAlbumArt() = 0; + virtual const char* extractMetadata(int keyCode) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnMediaMetadataRetriever: public BnInterface<IMediaMetadataRetriever> +{ +public: + virtual status_t onTransact(uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif // ANDROID_IMEDIAMETADATARETRIEVER_H + diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h index 43abf77..a683e74 100644 --- a/include/media/IMediaPlayer.h +++ b/include/media/IMediaPlayer.h @@ -38,7 +38,6 @@ public: 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; diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h index 63c7a00..8125cc9 100644 --- a/include/media/IMediaPlayerService.h +++ b/include/media/IMediaPlayerService.h @@ -23,18 +23,24 @@ #include <media/IMediaPlayerClient.h> #include <media/IMediaPlayer.h> +#include <media/IMediaMetadataRetriever.h> namespace android { +class IMediaRecorder; + class IMediaPlayerService: public IInterface { public: DECLARE_META_INTERFACE(MediaPlayerService); + virtual sp<IMediaRecorder> createMediaRecorder(pid_t pid) = 0; + virtual sp<IMediaMetadataRetriever> createMetadataRetriever(pid_t pid) = 0; + virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url) = 0; virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length) = 0; - virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) = 0; - virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) = 0; + virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0; + virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h new file mode 100644 index 0000000..49e45d1 --- /dev/null +++ b/include/media/IMediaRecorder.h @@ -0,0 +1,67 @@ +/* + ** + ** Copyright 2008, HTC Inc. + ** + ** 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_IMEDIARECORDER_H +#define ANDROID_IMEDIARECORDER_H + +#include <utils/IInterface.h> + +namespace android { + +class ISurface; +class ICamera; + +class IMediaRecorder: public IInterface +{ +public: + DECLARE_META_INTERFACE(MediaRecorder); + + virtual status_t setCamera(const sp<ICamera>& camera) = 0; + virtual status_t setPreviewSurface(const sp<ISurface>& surface) = 0; + virtual status_t setVideoSource(int vs) = 0; + virtual status_t setAudioSource(int as) = 0; + virtual status_t setOutputFormat(int of) = 0; + virtual status_t setVideoEncoder(int ve) = 0; + virtual status_t setAudioEncoder(int ae) = 0; + virtual status_t setOutputFile(const char* path) = 0; + virtual status_t setVideoSize(int width, int height) = 0; + virtual status_t setVideoFrameRate(int frames_per_second) = 0; + virtual status_t prepare() = 0; + virtual status_t getMaxAmplitude(int* max) = 0; + virtual status_t start() = 0; + virtual status_t stop() = 0; + virtual status_t reset() = 0; + virtual status_t init() = 0; + virtual status_t close() = 0; + virtual status_t release() = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnMediaRecorder: public BnInterface<IMediaRecorder> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif // ANDROID_IMEDIARECORDER_H + diff --git a/include/media/MediaMetadataRetrieverInterface.h b/include/media/MediaMetadataRetrieverInterface.h new file mode 100644 index 0000000..b178836 --- /dev/null +++ b/include/media/MediaMetadataRetrieverInterface.h @@ -0,0 +1,52 @@ +/* +** +** 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_MEDIAMETADATARETRIEVERINTERFACE_H +#define ANDROID_MEDIAMETADATARETRIEVERINTERFACE_H + +#include <utils/RefBase.h> +#include <media/mediametadataretriever.h> +#include <private/media/VideoFrame.h> + +namespace android { + +// Abstract base class +class MediaMetadataRetrieverBase : public RefBase +{ +public: + MediaMetadataRetrieverBase() {} + virtual ~MediaMetadataRetrieverBase() {} + virtual status_t setDataSource(const char *url) = 0; + virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0; + virtual status_t setMode(int mode) = 0; + virtual status_t getMode(int* mode) const = 0; + virtual VideoFrame* captureFrame() = 0; + virtual MediaAlbumArt* extractAlbumArt() = 0; + virtual const char* extractMetadata(int keyCode) = 0; +}; + +// MediaMetadataRetrieverInterface +class MediaMetadataRetrieverInterface : public MediaMetadataRetrieverBase +{ +public: + virtual ~MediaMetadataRetrieverInterface() {} +}; + +}; // namespace android + +#endif // ANDROID_MEDIAMETADATARETRIEVERINTERFACE_H + diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h index 275e789..30e4578 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -26,6 +26,7 @@ #include <utils/RefBase.h> #include <media/mediaplayer.h> +#include <media/AudioSystem.h> namespace android { @@ -36,6 +37,9 @@ enum player_type { }; #define DEFAULT_AUDIOSINK_BUFFERCOUNT 4 +#define DEFAULT_AUDIOSINK_BUFFERSIZE 1200 +#define DEFAULT_AUDIOSINK_SAMPLERATE 44100 + // callback mechanism for passing messages to MediaPlayer object typedef void (*notify_callback_f)(void* cookie, int msg, int ext1, int ext2); @@ -57,7 +61,7 @@ public: 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 status_t open(uint32_t sampleRate, int channelCount, int format=AudioSystem::PCM_16_BIT, 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; @@ -80,8 +84,6 @@ public: 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; diff --git a/include/media/PVMediaRecorder.h b/include/media/PVMediaRecorder.h new file mode 100644 index 0000000..ec497ae --- /dev/null +++ b/include/media/PVMediaRecorder.h @@ -0,0 +1,60 @@ +/* + ** + ** Copyright 2008, HTC Inc. + ** + ** 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_PVMEDIARECORDER_H +#define ANDROID_PVMEDIARECORDER_H + +#include <media/mediarecorder.h> + +namespace android { + +class ISurface; +class ICamera; +class AuthorDriverWrapper; + +class PVMediaRecorder +{ +public: + PVMediaRecorder(); + ~PVMediaRecorder(); + + 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 setCamera(const sp<ICamera>& camera); + status_t setPreviewSurface(const sp<ISurface>& surface); + status_t setOutputFile(const char *path); + status_t prepare(); + status_t start(); + status_t stop(); + status_t close(); + status_t reset(); + status_t getMaxAmplitude(int *max); + +private: + AuthorDriverWrapper* mAuthorDriverWrapper; +}; + +}; // namespace android + +#endif // ANDROID_PVMEDIARECORDER_H + diff --git a/include/media/PVMetadataRetriever.h b/include/media/PVMetadataRetriever.h new file mode 100644 index 0000000..c202dfe --- /dev/null +++ b/include/media/PVMetadataRetriever.h @@ -0,0 +1,51 @@ +/* +** +** 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_PVMETADATARETRIEVER_H +#define ANDROID_PVMETADATARETRIEVER_H + +#include <utils/Errors.h> +#include <media/MediaMetadataRetrieverInterface.h> +#include <private/media/VideoFrame.h> + +namespace android { + +class MetadataDriver; + +class PVMetadataRetriever : public MediaMetadataRetrieverInterface +{ +public: + PVMetadataRetriever(); + virtual ~PVMetadataRetriever(); + + virtual status_t setDataSource(const char *url); + virtual status_t setDataSource(int fd, int64_t offset, int64_t length); + virtual status_t setMode(int mode); + virtual status_t getMode(int* mode) const; + virtual VideoFrame* captureFrame(); + virtual MediaAlbumArt* extractAlbumArt(); + virtual const char* extractMetadata(int keyCode); + +private: + mutable Mutex mLock; + MetadataDriver* mMetadataDriver; + char* mDataSourcePath; +}; + +}; // namespace android + +#endif // ANDROID_PVMETADATARETRIEVER_H diff --git a/include/media/PVPlayer.h b/include/media/PVPlayer.h index 8164d8c..5f302ed 100644 --- a/include/media/PVPlayer.h +++ b/include/media/PVPlayer.h @@ -41,8 +41,6 @@ public: 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); @@ -54,11 +52,11 @@ public: 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); + static void do_nothing(status_t s, void *cookie, bool cancelled) { } + static void run_init(status_t s, void *cookie, bool cancelled); + static void run_set_video_surface(status_t s, void *cookie, bool cancelled); + static void run_set_audio_output(status_t s, void *cookie, bool cancelled); + static void run_prepare(status_t s, void *cookie, bool cancelled); PlayerDriver* mPlayerDriver; char * mDataSourcePath; diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h index bc27d35..da1489f 100644 --- a/include/media/ToneGenerator.h +++ b/include/media/ToneGenerator.h @@ -85,7 +85,7 @@ private: TONE_RESTARTING // }; - static const unsigned int NUM_PCM_BUFFERS = 2; // number of pcm buffers of audio track + static const unsigned int NUM_PCM_BUFFERS = 2; // Number of AudioTrack pcm buffers static const unsigned int TONEGEN_MAX_WAVES = 3; static const unsigned int TONEGEN_MAX_SEGMENTS = 4; // Maximun number of elenemts in @@ -126,14 +126,17 @@ private: const ToneDescriptor *mpToneDesc; // pointer to active tone descriptor const ToneDescriptor *mpNewToneDesc; // pointer to next active tone descriptor - unsigned int mSamplingRate; // Sampling rate + int mSamplingRate; // AudioFlinger Sampling rate + int mBufferSize; // PCM buffer size in frames 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 + int mStreamType; // Audio stream used for output - static void audioCallback(void* user, const AudioTrack::Buffer& info); + bool initAudioTrack(); + static void audioCallback(int event, void* user, void *info); bool prepareWave(); unsigned int numWaves(); void clearWaveGens(); diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h index 586dda1c..05cba30 100644 --- a/include/media/mediametadataretriever.h +++ b/include/media/mediametadataretriever.h @@ -18,10 +18,16 @@ #ifndef MEDIAMETADATARETRIEVER_H #define MEDIAMETADATARETRIEVER_H -#include <graphics/SkBitmap.h> // for SkBitmap +#include <utils/Errors.h> // for status_t +#include <utils/threads.h> +#include <utils/IMemory.h> +#include <media/IMediaMetadataRetriever.h> namespace android { +class IMediaPlayerService; +class IMediaMetadataRetriever; + // Keep these in synch with the constants defined in MediaMetadataRetriever.java // class. enum { @@ -38,62 +44,45 @@ enum { METADATA_KEY_NUM_TRACKS = 10, METADATA_KEY_IS_DRM_CRIPPLED = 11, METADATA_KEY_CODEC = 12, + METADATA_KEY_RATING = 13, + METADATA_KEY_COMMENT = 14, + METADATA_KEY_COPYRIGHT = 15, // Add more here... }; -// A utility class that holds the size and actual data in album art. -class MediaAlbumArt { + +class MediaMetadataRetriever: public RefBase +{ 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(); } + MediaMetadataRetriever(); + ~MediaMetadataRetriever(); + void disconnect(); + status_t setDataSource(const char* dataSourceUrl); + status_t setDataSource(int fd, int64_t offset, int64_t length); + status_t setMode(int mode); + status_t getMode(int* mode); + sp<IMemory> captureFrame(); + sp<IMemory> extractAlbumArt(); + const char* extractMetadata(int keyCode); - 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. -}; + static const sp<IMediaPlayerService>& getService(); -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 DeathNotifier: public IBinder::DeathRecipient + { + public: + DeathNotifier() {} + virtual ~DeathNotifier(); + virtual void binderDied(const wp<IBinder>& who); + }; -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(); + static sp<DeathNotifier> sDeathNotifier; + static Mutex sServiceLock; + static sp<IMediaPlayerService> sService; + + Mutex mLock; + sp<IMediaMetadataRetriever> mRetriever; -private: - MediaMetadataRetriever() {} - static MediaMetadataRetrieverImpl *mRetriever; - static void *mLibHandler; }; }; // namespace android diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h index aadfc32..7288445 100644 --- a/include/media/mediaplayer.h +++ b/include/media/mediaplayer.h @@ -17,11 +17,12 @@ #ifndef ANDROID_MEDIAPLAYER_H #define ANDROID_MEDIAPLAYER_H +#include <utils/IMemory.h> #include <ui/Surface.h> -#include <media/AudioTrack.h> #include <media/IMediaPlayerClient.h> #include <media/IMediaPlayer.h> #include <media/IMediaPlayerService.h> +#include <utils/SortedVector.h> namespace android { @@ -31,6 +32,7 @@ enum media_event_type { MEDIA_PLAYBACK_COMPLETE = 2, MEDIA_BUFFERING_UPDATE = 3, MEDIA_SEEK_COMPLETE = 4, + MEDIA_SET_VIDEO_SIZE = 5, MEDIA_ERROR = 100, }; @@ -52,18 +54,18 @@ enum media_player_states { // ---------------------------------------------------------------------------- // ref-counted object for callbacks -class MediaPlayerListener: public RefBase +class MediaPlayerListener: virtual public RefBase { public: virtual void notify(int msg, int ext1, int ext2) = 0; }; -class MediaPlayer : public BnMediaPlayerClient, public IBinder::DeathRecipient +class MediaPlayer : public BnMediaPlayerClient { public: MediaPlayer(); ~MediaPlayer(); - + void onFirstRef(); void disconnect(); status_t setDataSource(const char *url); status_t setDataSource(int fd, int64_t offset, int64_t length); @@ -83,10 +85,11 @@ public: status_t reset(); status_t setAudioStreamType(int type); status_t setLooping(int loop); + bool isLooping(); status_t setVolume(float leftVolume, float rightVolume); void notify(int msg, int ext1, int ext2); - static sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels); - static sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels); + static sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); + static sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); private: void clear_l(); @@ -96,7 +99,8 @@ private: status_t setDataSource(const sp<IMediaPlayer>& player); static const sp<IMediaPlayerService>& getMediaPlayerService(); - virtual void binderDied(const wp<IBinder>& who); + static void addObitRecipient(const wp<MediaPlayer>& recipient); + static void removeObitRecipient(const wp<MediaPlayer>& recipient); class DeathNotifier: public IBinder::DeathRecipient { @@ -107,8 +111,6 @@ private: virtual void binderDied(const wp<IBinder>& who); }; - static sp<DeathNotifier> mDeathNotifier; - sp<IMediaPlayer> mPlayer; Mutex mLock; Mutex mNotifyLock; @@ -125,11 +127,15 @@ private: bool mLoop; float mLeftVolume; float mRightVolume; + int mVideoWidth; + int mVideoHeight; friend class DeathNotifier; - static Mutex mServiceLock; - static sp<IMediaPlayerService> mMediaPlayerService; + static Mutex sServiceLock; + static sp<IMediaPlayerService> sMediaPlayerService; + static sp<DeathNotifier> sDeathNotifier; + static SortedVector< wp<MediaPlayer> > sObitRecipients; }; }; // namespace android diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h index f247424..a901d32 100644 --- a/include/media/mediarecorder.h +++ b/include/media/mediarecorder.h @@ -1,28 +1,30 @@ /* - * 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. + ** 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 +#ifndef ANDROID_MEDIARECORDER_H +#define ANDROID_MEDIARECORDER_H #include <utils.h> -#include <ui/SurfaceComposerClient.h> namespace android { -class AuthorDriverWrapper; +class Surface; +class IMediaRecorder; +class ICamera; typedef void (*media_completion_f)(status_t status, void *cookie); @@ -31,7 +33,7 @@ typedef void (*media_completion_f)(status_t status, void *cookie); */ enum audio_source { AUDIO_SOURCE_DEFAULT = 0, - AUDIO_SOURCE_MIC = 1, + AUDIO_SOURCE_MIC = 1, }; enum video_source { @@ -39,10 +41,13 @@ enum video_source { VIDEO_SOURCE_CAMERA = 1, }; +//Please update java/android/android/media/MediaRecorder.java if the following is updated. enum output_format { OUTPUT_FORMAT_DEFAULT = 0, - OUTPUT_FORMAT_THREE_GPP = 1, - OUTPUT_FORMAT_MPEG_4 = 2, + OUTPUT_FORMAT_THREE_GPP, + OUTPUT_FORMAT_MPEG_4, + OUTPUT_FORMAT_RAW_AMR, + OUTPUT_FORMAT_LIST_END // must be last - used to validate format type }; enum audio_encoder { @@ -57,26 +62,29 @@ enum video_encoder { VIDEO_ENCODER_MPEG_4_SP = 3, }; +// Maximum frames per second is 24 +#define MEDIA_RECORDER_MAX_FRAME_RATE 24 + /* * 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_DATASOURCE_CONFIGURED * 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, + MEDIA_RECORDER_ERROR = 0, + MEDIA_RECORDER_IDLE = 1 << 0, + MEDIA_RECORDER_INITIALIZED = 1 << 1, + MEDIA_RECORDER_DATASOURCE_CONFIGURED = 1 << 2, + MEDIA_RECORDER_PREPARED = 1 << 3, + MEDIA_RECORDER_RECORDING = 1 << 4, }; class MediaRecorder @@ -85,36 +93,39 @@ 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>& 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); + status_t initCheck(); + status_t setCamera(const sp<ICamera>& camera); + status_t setPreviewSurface(const sp<Surface>& surface); + status_t setVideoSource(int vs); + status_t setAudioSource(int as); + status_t setOutputFormat(int of); + status_t setVideoEncoder(int ve); + status_t setAudioEncoder(int ae); + status_t setOutputFile(const char* path); + status_t setVideoSize(int width, int height); + status_t setVideoFrameRate(int frames_per_second); + status_t prepare(); + status_t getMaxAmplitude(int* max); + status_t start(); + status_t stop(); + status_t reset(); + status_t init(); + status_t close(); + status_t release(); private: - AuthorDriverWrapper *mAuthorDriverWrapper; - bool mOutputFormatSpecified; - media_recorder_states mCurrentState; - + void doCleanUp(); + status_t doReset(); + + sp<IMediaRecorder> mMediaRecorder; + media_recorder_states mCurrentState; + bool mIsAudioSourceSet; + bool mIsVideoSourceSet; + bool mIsAudioEncoderSet; + bool mIsVideoEncoderSet; + bool mIsOutputFileSet; }; -}; // namespace android - -#endif // MEDIAPLAYER_H +}; // namespace android +#endif // ANDROID_MEDIARECORDER_H diff --git a/include/media/mediascanner.h b/include/media/mediascanner.h index 5d0122d..fbef1db 100644 --- a/include/media/mediascanner.h +++ b/include/media/mediascanner.h @@ -23,6 +23,7 @@ namespace android { class MediaScannerClient; +class StringArray; class MediaScanner { @@ -35,6 +36,7 @@ public: 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); + void setLocale(const char* locale); // extracts album art as a block of data char* extractAlbumArt(int fd); @@ -45,16 +47,36 @@ private: status_t doProcessDirectory(char *path, int pathRemaining, const char* extensions, MediaScannerClient& client, ExceptionCheck exceptionCheck, void* exceptionEnv); void initializeForThread(); + + // current locale (like "ja_JP"), created/destroyed with strdup()/free() + char* mLocale; }; class MediaScannerClient { public: - virtual ~MediaScannerClient() {} + MediaScannerClient(); + virtual ~MediaScannerClient(); + void setLocale(const char* locale); + void beginFile(); + bool addStringTag(const char* name, const char* value); + void endFile(); + 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; + +protected: + void convertValues(uint32_t encoding); + +protected: + // cached name and value strings, for native encoding support. + StringArray* mNames; + StringArray* mValues; + + // default encoding based on MediaScanner::mLocale string + uint32_t mLocaleEncoding; }; }; // namespace android diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h index 1878f3c..72ed281 100644 --- a/include/private/media/AudioTrackShared.h +++ b/include/private/media/AudioTrackShared.h @@ -31,29 +31,41 @@ namespace android { struct audio_track_cblk_t { - enum { - SEQUENCE_MASK = 0xFFFFFF00, - BUFFER_MASK = 0x000000FF - }; + // The data members are grouped so that members accessed frequently and in the same context + // are in the same line of data cache. Mutex lock; Condition cv; volatile uint32_t user; volatile uint32_t server; + uint32_t userBase; + uint32_t serverBase; + void* buffers; + uint32_t frameCount; + // Cache line boundary + uint32_t loopStart; + uint32_t loopEnd; + int loopCount; volatile union { uint16_t volume[2]; uint32_t volumeLR; }; uint16_t sampleRate; - uint16_t reserved; - - void* buffers; - size_t size; - + uint16_t channels; + int16_t flowControlFlag; // underrun (out) or overrrun (in) indication + uint8_t out; // out equals 1 for AudioTrack and 0 for AudioRecord + uint8_t forceReady; + // Padding ensuring that data buffer starts on a cache line boundary (32 bytes). + // See AudioFlinger::TrackBase constructor + int32_t Padding[4]; + audio_track_cblk_t(); - uint32_t stepUser(int bufferCount); - bool stepServer(int bufferCount); - void* buffer(int id) const; + uint32_t stepUser(uint32_t frameCount); + bool stepServer(uint32_t frameCount); + void* buffer(uint32_t offset) const; + uint32_t framesAvailable(); + uint32_t framesAvailable_l(); + uint32_t framesReady(); }; diff --git a/include/private/media/VideoFrame.h b/include/private/media/VideoFrame.h new file mode 100644 index 0000000..9c35274 --- /dev/null +++ b/include/private/media/VideoFrame.h @@ -0,0 +1,127 @@ +/* +** +** 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_VIDEO_FRAME_H +#define ANDROID_VIDEO_FRAME_H + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <utils/Log.h> + +namespace android { + +// A simple buffer to hold binary data +class MediaAlbumArt +{ +public: + MediaAlbumArt(): mSize(0), mData(0) {} + + explicit MediaAlbumArt(const char* url) { + mSize = 0; + mData = NULL; + FILE *in = fopen(url, "r"); + if (!in) { + return; + } + fseek(in, 0, SEEK_END); + mSize = ftell(in); // Allocating buffer of size equals to the external file size. + if (mSize == 0 || (mData = new uint8_t[mSize]) == NULL) { + fclose(in); + if (mSize != 0) { + mSize = 0; + } + return; + } + rewind(in); + if (fread(mData, 1, mSize, in) != mSize) { // Read failed. + delete[] mData; + mData = NULL; + mSize = 0; + return; + } + fclose(in); + } + + MediaAlbumArt(const MediaAlbumArt& copy) { + mSize = copy.mSize; + mData = NULL; // initialize it first + if (mSize > 0 && copy.mData != NULL) { + mData = new uint8_t[copy.mSize]; + if (mData != NULL) { + memcpy(mData, copy.mData, mSize); + } else { + mSize = 0; + } + } + } + + ~MediaAlbumArt() { + if (mData != 0) { + delete[] mData; + } + } + + // Intentional public access modifier: + // We have to know the internal structure in order to share it between + // processes? + uint32_t mSize; // Number of bytes in mData + uint8_t* mData; // Actual binary data +}; + +// Represents a color converted (RGB-based) video frame +// with bitmap pixels stored in FrameBuffer +class VideoFrame +{ +public: + VideoFrame(): mWidth(0), mHeight(0), mDisplayWidth(0), mDisplayHeight(0), mSize(0), mData(0) {} + + VideoFrame(const VideoFrame& copy) { + mWidth = copy.mWidth; + mHeight = copy.mHeight; + mDisplayWidth = copy.mDisplayWidth; + mDisplayHeight = copy.mDisplayHeight; + mSize = copy.mSize; + mData = NULL; // initialize it first + if (mSize > 0 && copy.mData != NULL) { + mData = new uint8_t[mSize]; + if (mData != NULL) { + memcpy(mData, copy.mData, mSize); + } else { + mSize = 0; + } + } + } + + ~VideoFrame() { + if (mData != 0) { + delete[] mData; + } + } + + // Intentional public access modifier: + uint32_t mWidth; + uint32_t mHeight; + uint32_t mDisplayWidth; + uint32_t mDisplayHeight; + uint32_t mSize; // Number of bytes in mData + uint8_t* mData; // Actual binary data +}; + +}; // namespace android + +#endif // ANDROID_VIDEO_FRAME_H diff --git a/include/private/opengles/gl_context.h b/include/private/opengles/gl_context.h index 67d50fd..2aa78d8 100644 --- a/include/private/opengles/gl_context.h +++ b/include/private/opengles/gl_context.h @@ -532,6 +532,10 @@ struct compute_iterators_t vertex_t const* v1, vertex_t const* v2); + void initLine( + vertex_t const* v0, + vertex_t const* v1); + inline void initLerp(vertex_t const* v0, uint32_t enables); int iteratorsScale(int32_t it[3], @@ -543,6 +547,9 @@ struct compute_iterators_t void iterators0032(int32_t it[3], int32_t c0, int32_t c1, int32_t c2) const; + void iterators0032(int64_t it[3], + int32_t c0, int32_t c1, int32_t c2) const; + GGLcoord area() const { return m_area; } private: diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index cfa837a..650684a 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -12,6 +12,9 @@ LOCAL_SRC_FILES:= \ IMediaPlayerService.cpp \ IMediaPlayerClient.cpp \ IMediaPlayer.cpp \ + IMediaRecorder.cpp \ + mediarecorder.cpp \ + IMediaMetadataRetriever.cpp \ mediametadataretriever.cpp \ ToneGenerator.cpp diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index f3e4123..bbb9548 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -2,19 +2,20 @@ ** ** 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 +** 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 +** 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 +** 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 "AudioRecord" #include <stdint.h> @@ -53,24 +54,26 @@ AudioRecord::AudioRecord( uint32_t sampleRate, int format, int channelCount, - int bufferCount, + int frameCount, uint32_t flags, - callback_t cbf, void* user) + callback_t cbf, + void* user, + int notificationFrames) : mStatus(NO_INIT) { mStatus = set(streamType, sampleRate, format, channelCount, - bufferCount, flags, cbf, user); + frameCount, flags, cbf, user, notificationFrames); } AudioRecord::~AudioRecord() { if (mStatus == NO_ERROR) { - if (mPosition) { - releaseBuffer(&mAudioBuffer); - } - // obtainBuffer() will give up with an error - mAudioRecord->stop(); + // Make sure that callback function exits in the case where + // it is looping on buffer empty condition in obtainBuffer(). + // Otherwise the callback thread will never exit. + stop(); if (mClientRecordThread != 0) { + mCblk->cv.signal(); mClientRecordThread->requestExitAndWait(); mClientRecordThread.clear(); } @@ -84,11 +87,15 @@ status_t AudioRecord::set( uint32_t sampleRate, int format, int channelCount, - int bufferCount, + int frameCount, uint32_t flags, - callback_t cbf, void* user) + callback_t cbf, + void* user, + int notificationFrames, + bool threadCanCallJava) { + LOGV("set(): sampleRate %d, channelCount %d, frameCount %d",sampleRate, channelCount, frameCount); if (mAudioFlinger != 0) { return INVALID_OPERATION; } @@ -112,11 +119,6 @@ status_t AudioRecord::set( 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) { @@ -125,22 +127,34 @@ status_t AudioRecord::set( if (channelCount != 1 && channelCount != 2) { return BAD_VALUE; } - if (bufferCount < 2) { + + // TODO: Get input frame count from hardware. + int minFrameCount = 1024*2; + + if (frameCount == 0) { + frameCount = minFrameCount; + } else if (frameCount < minFrameCount) { return BAD_VALUE; } + if (notificationFrames == 0) { + notificationFrames = frameCount/2; + } + // open record channel + status_t status; sp<IAudioRecord> record = audioFlinger->openRecord(getpid(), streamType, - sampleRate, format, channelCount, bufferCount, flags); + sampleRate, format, channelCount, frameCount, flags, &status); if (record == 0) { - return NO_INIT; + LOGE("AudioFlinger could not create record track, status: %d", status); + return status; } sp<IMemory> cblk = record->getCblk(); if (cblk == 0) { return NO_INIT; } if (cbf != 0) { - mClientRecordThread = new ClientRecordThread(*this); + mClientRecordThread = new ClientRecordThread(*this, threadCanCallJava); if (mClientRecordThread == 0) { return NO_INIT; } @@ -153,16 +167,23 @@ status_t AudioRecord::set( mCblkMemory = cblk; mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer()); mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + mCblk->out = 0; mSampleRate = sampleRate; - mFrameCount = audioFlinger->frameCount(); mFormat = format; - mBufferCount = bufferCount; + // Update buffer size in case it has been limited by AudioFlinger during track creation + mFrameCount = mCblk->frameCount; mChannelCount = channelCount; mActive = 0; mCbf = cbf; + mNotificationFrames = notificationFrames; + mRemainingFrames = notificationFrames; mUserData = user; - mLatency = seconds(mFrameCount) / mSampleRate; - mPosition = 0; + // TODO: add audio hardware input latency here + mLatency = (1000*mFrameCount) / mSampleRate; + mMarkerPosition = 0; + mNewPosition = 0; + mUpdatePeriod = 0; + return NO_ERROR; } @@ -173,7 +194,7 @@ status_t AudioRecord::initCheck() const // ------------------------------------------------------------------------- -nsecs_t AudioRecord::latency() const +uint32_t AudioRecord::latency() const { return mLatency; } @@ -193,9 +214,14 @@ int AudioRecord::channelCount() const return mChannelCount; } -int AudioRecord::bufferCount() const +uint32_t AudioRecord::frameCount() const +{ + return mFrameCount; +} + +int AudioRecord::frameSize() const { - return mBufferCount; + return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t)); } // ------------------------------------------------------------------------- @@ -203,54 +229,60 @@ int AudioRecord::bufferCount() const 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(); - } + sp<ClientRecordThread> t = mClientRecordThread; - 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); + LOGV("start"); + + if (t != 0) { + if (t->exitPending()) { + if (t->requestExitAndWait() == WOULD_BLOCK) { + LOGE("AudioRecord::start called from thread"); + return WOULD_BLOCK; } } + t->mLock.lock(); + } + + if (android_atomic_or(1, &mActive) == 0) { + mNewPosition = mCblk->user + mUpdatePeriod; + if (t != 0) { + t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT); + } else { + setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT); + } + ret = mAudioRecord->start(); } - - if (mClientRecordThread != 0) { - mRecordThreadLock.unlock(); + + if (t != 0) { + t->mLock.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(); - } + sp<ClientRecordThread> t = mClientRecordThread; + + LOGV("stop"); + + if (t != 0) { + t->mLock.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 (t != 0) { + t->requestExit(); + } else { + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL); } } - - if (mClientRecordThread != 0) { - mRecordThreadLock.unlock(); + + if (t != 0) { + t->mLock.unlock(); } - + return NO_ERROR; } @@ -259,22 +291,74 @@ bool AudioRecord::stopped() const return !mActive; } +status_t AudioRecord::setMarkerPosition(uint32_t marker) +{ + if (mCbf == 0) return INVALID_OPERATION; + + mMarkerPosition = marker; + + return NO_ERROR; +} + +status_t AudioRecord::getMarkerPosition(uint32_t *marker) +{ + if (marker == 0) return BAD_VALUE; + + *marker = mMarkerPosition; + + return NO_ERROR; +} + +status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod) +{ + if (mCbf == 0) return INVALID_OPERATION; + + uint32_t curPosition; + getPosition(&curPosition); + mNewPosition = curPosition + updatePeriod; + mUpdatePeriod = updatePeriod; + + return NO_ERROR; +} + +status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod) +{ + if (updatePeriod == 0) return BAD_VALUE; + + *updatePeriod = mUpdatePeriod; + + return NO_ERROR; +} + +status_t AudioRecord::getPosition(uint32_t *position) +{ + if (position == 0) return BAD_VALUE; + + *position = mCblk->user; + + return NO_ERROR; +} + + // ------------------------------------------------------------------------- status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, bool blocking) { - int active = mActive; + int active; int timeout = 0; status_t result; audio_track_cblk_t* cblk = mCblk; + uint32_t framesReq = audioBuffer->frameCount; + + audioBuffer->frameCount = 0; + audioBuffer->size = 0; - const uint32_t u = cblk->user; - uint32_t s = cblk->server; + uint32_t framesReady = cblk->framesReady(); - if (u == s) { + if (framesReady == 0) { Mutex::Autolock _l(cblk->lock); goto start_loop_here; - while (u == s) { + while (framesReady == 0) { active = mActive; if (UNLIKELY(!active)) return NO_MORE_BUFFERS; @@ -284,40 +368,45 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, bool blocking) 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); + "user=%08x, server=%08x", cblk->user, cblk->server); timeout = 1; } // read the server count again start_loop_here: - s = cblk->server; + framesReady = cblk->framesReady(); } } 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); + "this shouldn't happen (user=%08x, server=%08x)", cblk->user, cblk->server); + + if (framesReq > framesReady) { + framesReq = framesReady; + } + + uint32_t u = cblk->user; + uint32_t bufferEnd = cblk->userBase + cblk->frameCount; + + if (u + framesReady > bufferEnd) { + framesReq = bufferEnd - u; + } 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); + audioBuffer->frameCount = framesReq; + audioBuffer->size = framesReq*mChannelCount*sizeof(int16_t); + audioBuffer->raw = (int8_t*)cblk->buffer(u); + active = mActive; 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); + cblk->stepUser(audioBuffer->frameCount); } // ------------------------------------------------------------------------- @@ -325,32 +414,38 @@ void AudioRecord::releaseBuffer(Buffer* audioBuffer) 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); - } - } + Buffer audioBuffer; + int8_t *dst = static_cast<int8_t*>(buffer); - size_t capacity = mAudioBuffer.size - mPosition; - size_t toRead = userSize < capacity ? userSize : capacity; + if (ssize_t(userSize) < 0) { + // sanity-check. user is most-likely passing an error code. + LOGE("AudioRecord::read(buffer=%p, size=%u (%d)", + buffer, userSize, userSize); + return BAD_VALUE; + } - memcpy(buffer, mAudioBuffer.i8 + mPosition, toRead); + LOGV("read size: %d", userSize); - buffer = static_cast<int8_t*>(buffer) + toRead; - mPosition += toRead; - userSize -= toRead; - capacity -= toRead; - read += toRead; + do { - if (capacity == 0) { - mPosition = 0; - releaseBuffer(&mAudioBuffer); + audioBuffer.frameCount = userSize/mChannelCount/sizeof(int16_t); + + status_t err = obtainBuffer(&audioBuffer, true); + if (err < 0) { + // out of buffers, return #bytes written + if (err == status_t(NO_MORE_BUFFERS)) + break; + return ssize_t(err); } + + size_t bytesRead = audioBuffer.size; + memcpy(dst, audioBuffer.i8, bytesRead); + + dst += bytesRead; + userSize -= bytesRead; + read += bytesRead; + + releaseBuffer(&audioBuffer); } while (userSize); return read; @@ -361,28 +456,83 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize) bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) { Buffer audioBuffer; - bool more; + uint32_t frames = mRemainingFrames; + size_t readSize = 0; + + // Manage marker callback + if (mMarkerPosition > 0) { + if (mCblk->user >= mMarkerPosition) { + mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition); + mMarkerPosition = 0; + } + } + + // Manage new position callback + if (mUpdatePeriod > 0) { + while (mCblk->user >= mNewPosition) { + mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition); + mNewPosition += mUpdatePeriod; + } + } do { - status_t err = obtainBuffer(&audioBuffer, true); + audioBuffer.frameCount = frames; + status_t err = obtainBuffer(&audioBuffer, false); if (err < NO_ERROR) { - LOGE("Error obtaining an audio buffer, giving up."); - return false; + if (err != WOULD_BLOCK) { + LOGE("Error obtaining an audio buffer, giving up."); + return false; + } } - more = mCbf(mUserData, audioBuffer); + if (err == status_t(STOPPED)) return false; + + if (audioBuffer.size == 0) break; + + size_t reqSize = audioBuffer.size; + mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); + readSize = audioBuffer.size; + + // Sanity check on returned size + if (ssize_t(readSize) <= 0) break; + if (readSize > reqSize) readSize = reqSize; + + audioBuffer.size = readSize; + audioBuffer.frameCount = readSize/mChannelCount/sizeof(int16_t); + frames -= audioBuffer.frameCount; + releaseBuffer(&audioBuffer); - } while (more && !thread->exitPending()); - // stop the track automatically - this->stop(); + } while (frames); + + + // Manage overrun callback + if (mActive && (mCblk->framesAvailable_l() == 0)) { + LOGV("Overrun user: %x, server: %x, flowControlFlag %d", mCblk->user, mCblk->server, mCblk->flowControlFlag); + if (mCblk->flowControlFlag == 0) { + mCbf(EVENT_OVERRUN, mUserData, 0); + mCblk->flowControlFlag = 1; + } + } + // If no data was read, it is likely that obtainBuffer() did + // not find available data in PCM buffer: we release the processor for + // a few millisecond before polling again for available data. + if (readSize == 0) { + usleep(5000); + } + + if (frames == 0) { + mRemainingFrames = mNotificationFrames; + } else { + mRemainingFrames = frames; + } return true; } // ========================================================================= -AudioRecord::ClientRecordThread::ClientRecordThread(AudioRecord& receiver) - : Thread(false), mReceiver(receiver) +AudioRecord::ClientRecordThread::ClientRecordThread(AudioRecord& receiver, bool bCanCallJava) + : Thread(bCanCallJava), mReceiver(receiver) { } diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 22de463..a375b55 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -28,6 +28,11 @@ Mutex AudioSystem::gLock; sp<IAudioFlinger> AudioSystem::gAudioFlinger; sp<AudioSystem::DeathNotifier> AudioSystem::gDeathNotifier; audio_error_callback AudioSystem::gAudioErrorCallback = NULL; +// Cached values +int AudioSystem::gOutSamplingRate = 0; +int AudioSystem::gOutFrameCount = 0; +uint32_t AudioSystem::gOutLatency = 0; + // establish binder interface to AudioFlinger service const sp<IAudioFlinger>& AudioSystem::get_audio_flinger() @@ -47,11 +52,15 @@ const sp<IAudioFlinger>& AudioSystem::get_audio_flinger() gDeathNotifier = new DeathNotifier(); } else { if (gAudioErrorCallback) { - gAudioErrorCallback(NO_ERROR); + gAudioErrorCallback(NO_ERROR); } } binder->linkToDeath(gDeathNotifier); gAudioFlinger = interface_cast<IAudioFlinger>(binder); + // Cache frequently accessed parameters + gOutFrameCount = (int)gAudioFlinger->frameCount(); + gOutSamplingRate = (int)gAudioFlinger->sampleRate(); + gOutLatency = gAudioFlinger->latency(); } LOGE_IF(gAudioFlinger==0, "no AudioFlinger!?"); return gAudioFlinger; @@ -71,7 +80,7 @@ status_t AudioSystem::isSpeakerphoneOn(bool* state) { } status_t AudioSystem::bluetoothSco(bool state) { - uint32_t mask = ROUTE_BLUETOOTH; + uint32_t mask = ROUTE_BLUETOOTH_SCO; uint32_t routes = state ? mask : ROUTE_EARPIECE; return setRouting(MODE_IN_CALL, routes, ROUTE_ALL); } @@ -79,7 +88,7 @@ status_t AudioSystem::bluetoothSco(bool state) { status_t AudioSystem::isBluetoothScoOn(bool* state) { uint32_t routes = 0; status_t s = getRouting(MODE_IN_CALL, &routes); - *state = !!(routes & ROUTE_BLUETOOTH); + *state = !!(routes & ROUTE_BLUETOOTH_SCO); return s; } @@ -235,11 +244,50 @@ int AudioSystem::logToLinear(float volume) return volume ? 100 - int(dBConvertInverse * log(volume) + 0.5) : 0; } +status_t AudioSystem::getOutputSamplingRate(int* samplingRate) +{ + if (gOutSamplingRate == 0) { + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + // gOutSamplingRate is updated by get_audio_flinger() + } + *samplingRate = gOutSamplingRate; + + return NO_ERROR; +} + +status_t AudioSystem::getOutputFrameCount(int* frameCount) +{ + if (gOutFrameCount == 0) { + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + // gOutSamplingRate is updated by get_audio_flinger() + } + *frameCount = gOutFrameCount; + return NO_ERROR; +} + +status_t AudioSystem::getOutputLatency(uint32_t* latency) +{ + if (gOutLatency == 0) { + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + // gOutLatency is updated by get_audio_flinger() + } + *latency = gOutLatency; + + return NO_ERROR; +} + // --------------------------------------------------------------------------- -void AudioSystem::DeathNotifier::binderDied(const wp<IBinder>& who) { +void AudioSystem::DeathNotifier::binderDied(const wp<IBinder>& who) { Mutex::Autolock _l(AudioSystem::gLock); AudioSystem::gAudioFlinger.clear(); + AudioSystem::gOutSamplingRate = 0; + AudioSystem::gOutFrameCount = 0; + AudioSystem::gOutLatency = 0; + if (gAudioErrorCallback) { gAudioErrorCallback(DEAD_OBJECT); } diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 298170a..d4f2e5a 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -21,6 +21,7 @@ #include <stdint.h> #include <sys/types.h> +#include <limits.h> #include <sched.h> #include <sys/resource.h> @@ -44,22 +45,25 @@ namespace android { // --------------------------------------------------------------------------- -static volatile size_t gFrameCount = 0; - -size_t AudioTrack::frameCount() +AudioTrack::AudioTrack() + : mStatus(NO_INIT) { - if (gFrameCount) return gFrameCount; - const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); - if (af == 0) return PERMISSION_DENIED; - gFrameCount = af->frameCount(); - return gFrameCount; } -// --------------------------------------------------------------------------- - -AudioTrack::AudioTrack() +AudioTrack::AudioTrack( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + callback_t cbf, + void* user, + int notificationFrames) : mStatus(NO_INIT) { + mStatus = set(streamType, sampleRate, format, channelCount, + frameCount, flags, cbf, user, notificationFrames, 0); } AudioTrack::AudioTrack( @@ -67,24 +71,28 @@ AudioTrack::AudioTrack( uint32_t sampleRate, int format, int channelCount, - int bufferCount, + const sp<IMemory>& sharedBuffer, uint32_t flags, - callback_t cbf, void* user) + callback_t cbf, + void* user, + int notificationFrames) : mStatus(NO_INIT) { mStatus = set(streamType, sampleRate, format, channelCount, - bufferCount, flags, cbf, user); + 0, flags, cbf, user, notificationFrames, sharedBuffer); } AudioTrack::~AudioTrack() { + LOGV_IF(mSharedBuffer != 0, "Destructor sharedBuffer: %p", mSharedBuffer->pointer()); + if (mStatus == NO_ERROR) { - if (mPosition) { - releaseBuffer(&mAudioBuffer); - } - // obtainBuffer() will give up with an error - mAudioTrack->stop(); + // Make sure that callback function exits in the case where + // it is looping on buffer full condition in obtainBuffer(). + // Otherwise the callback thread will never exit. + stop(); if (mAudioTrackThread != 0) { + mCblk->cv.signal(); mAudioTrackThread->requestExitAndWait(); mAudioTrackThread.clear(); } @@ -98,11 +106,17 @@ status_t AudioTrack::set( uint32_t sampleRate, int format, int channelCount, - int bufferCount, + int frameCount, uint32_t flags, - callback_t cbf, void* user) + callback_t cbf, + void* user, + int notificationFrames, + const sp<IMemory>& sharedBuffer, + bool threadCanCallJava) { + LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); + if (mAudioFlinger != 0) { LOGE("Track already in use"); return INVALID_OPERATION; @@ -113,13 +127,26 @@ status_t AudioTrack::set( LOGE("Could not get audioflinger"); return NO_INIT; } + int afSampleRate; + if (AudioSystem::getOutputSamplingRate(&afSampleRate) != NO_ERROR) { + return NO_INIT; + } + int afFrameCount; + if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { + return NO_INIT; + } + uint32_t afLatency; + if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) { + return NO_INIT; + } + // handle default values first. if (streamType == DEFAULT) { streamType = MUSIC; } if (sampleRate == 0) { - sampleRate = audioFlinger->sampleRate(); + sampleRate = afSampleRate; } // these below should probably come from the audioFlinger too... if (format == 0) { @@ -128,12 +155,10 @@ status_t AudioTrack::set( if (channelCount == 0) { channelCount = 2; } - if (bufferCount == 0) { - bufferCount = 2; - } // validate parameters - if (format != AudioSystem::PCM_16_BIT) { + if (((format != AudioSystem::PCM_8_BIT) || mSharedBuffer != 0) && + (format != AudioSystem::PCM_16_BIT)) { LOGE("Invalid format"); return BAD_VALUE; } @@ -141,17 +166,51 @@ status_t AudioTrack::set( LOGE("Invalid channel number"); return BAD_VALUE; } - if (bufferCount < 2) { - LOGE("Invalid buffer count"); - return BAD_VALUE; + + // Ensure that buffer depth covers at least audio hardware latency + uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate); + // When playing from shared buffer, playback will start even if last audioflinger + // block is partly filled. + if (sharedBuffer != 0 && minBufCount > 1) { + minBufCount--; + } + + int minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate; + + if (sharedBuffer == 0) { + if (frameCount == 0) { + frameCount = minFrameCount; + } + if (notificationFrames == 0) { + notificationFrames = frameCount/2; + } + // Make sure that application is notified with sufficient margin + // before underrun + if (notificationFrames > frameCount/2) { + notificationFrames = frameCount/2; + } + } else { + // Ensure that buffer alignment matches channelcount + if (((uint32_t)sharedBuffer->pointer() & (channelCount | 1)) != 0) { + LOGE("Invalid buffer alignement: address %p, channelCount %d", sharedBuffer->pointer(), channelCount); + return BAD_VALUE; + } + frameCount = sharedBuffer->size()/channelCount/sizeof(int16_t); + } + + if (frameCount < minFrameCount) { + LOGE("Invalid buffer size: minFrameCount %d, frameCount %d", minFrameCount, frameCount); + return BAD_VALUE; } // create the track + status_t status; sp<IAudioTrack> track = audioFlinger->createTrack(getpid(), - streamType, sampleRate, format, channelCount, bufferCount, flags); + streamType, sampleRate, format, channelCount, frameCount, flags, sharedBuffer, &status); + if (track == 0) { - LOGE("AudioFlinger could not create track"); - return NO_INIT; + LOGE("AudioFlinger could not create track, status: %d", status); + return status; } sp<IMemory> cblk = track->getCblk(); if (cblk == 0) { @@ -159,7 +218,7 @@ status_t AudioTrack::set( return NO_INIT; } if (cbf != 0) { - mAudioTrackThread = new AudioTrackThread(*this); + mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava); if (mAudioTrackThread == 0) { LOGE("Could not create callback thread"); return NO_INIT; @@ -172,23 +231,34 @@ status_t AudioTrack::set( mAudioTrack = track; mCblkMemory = cblk; mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer()); - mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + if (sharedBuffer == 0) { + mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + } else { + mCblk->buffers = sharedBuffer->pointer(); + } + mCblk->out = 1; 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; + // Update buffer size in case it has been limited by AudioFlinger during track creation + mFrameCount = mCblk->frameCount; mChannelCount = channelCount; + mSharedBuffer = sharedBuffer; mMuted = false; mActive = 0; - mReserved = 0; mCbf = cbf; + mNotificationFrames = notificationFrames; + mRemainingFrames = notificationFrames; mUserData = user; - mLatency = seconds(mFrameCount) / mSampleRate; - mPosition = 0; + mLatency = afLatency + (1000*mFrameCount) / mSampleRate; + mLoopCount = 0; + mMarkerPosition = 0; + mNewPosition = 0; + mUpdatePeriod = 0; + return NO_ERROR; } @@ -199,7 +269,7 @@ status_t AudioTrack::initCheck() const // ------------------------------------------------------------------------- -nsecs_t AudioTrack::latency() const +uint32_t AudioTrack::latency() const { return mLatency; } @@ -224,9 +294,19 @@ int AudioTrack::channelCount() const return mChannelCount; } -int AudioTrack::bufferCount() const +uint32_t AudioTrack::frameCount() const +{ + return mFrameCount; +} + +int AudioTrack::frameSize() const { - return mBufferCount; + return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t)); +} + +sp<IMemory>& AudioTrack::sharedBuffer() +{ + return mSharedBuffer; } // ------------------------------------------------------------------------- @@ -247,6 +327,12 @@ void AudioTrack::start() } if (android_atomic_or(1, &mActive) == 0) { + if (mSharedBuffer != 0) { + // Force buffer full condition as data is already present in shared memory + mCblk->user = mFrameCount; + mCblk->flowControlFlag = 0; + } + mNewPosition = mCblk->server + mUpdatePeriod; if (t != 0) { t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT); } else { @@ -270,15 +356,20 @@ void AudioTrack::stop() } 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); - } + // Cancel loops (If we are in the middle of a loop, playback + // would not stop until loopCount reaches 0). + setLoop(0, 0, 0); + // Force flush if a shared buffer is used otherwise audioflinger + // will not stop before end of buffer is reached. + if (mSharedBuffer != 0) { + flush(); + } + if (t != 0) { + t->requestExit(); + } else { + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL); + } } if (t != 0) { @@ -294,6 +385,7 @@ bool AudioTrack::stopped() const void AudioTrack::flush() { LOGV("flush"); + if (!mActive) { mCblk->lock.lock(); mAudioTrack->flush(); @@ -341,7 +433,16 @@ void AudioTrack::getVolume(float* left, float* right) void AudioTrack::setSampleRate(int rate) { + int afSamplingRate; + + if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) { + return; + } + // Resampler implementation limits input sampling rate to 2 x output sampling rate. + if (rate > afSamplingRate*2) rate = afSamplingRate*2; + if (rate > MAX_SAMPLE_RATE) rate = MAX_SAMPLE_RATE; + mCblk->sampleRate = rate; } @@ -350,6 +451,129 @@ uint32_t AudioTrack::getSampleRate() return uint32_t(mCblk->sampleRate); } +status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount) +{ + audio_track_cblk_t* cblk = mCblk; + + + Mutex::Autolock _l(cblk->lock); + + if (loopCount == 0) { + cblk->loopStart = UINT_MAX; + cblk->loopEnd = UINT_MAX; + cblk->loopCount = 0; + mLoopCount = 0; + return NO_ERROR; + } + + if (loopStart >= loopEnd || + loopStart < cblk->user || + loopEnd - loopStart > mFrameCount) { + LOGW("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, user %d", loopStart, loopEnd, loopCount, mFrameCount, cblk->user); + return BAD_VALUE; + } + // TODO handle shared buffer here: limit loop end to framecount + + cblk->loopStart = loopStart; + cblk->loopEnd = loopEnd; + cblk->loopCount = loopCount; + mLoopCount = loopCount; + + return NO_ERROR; +} + +status_t AudioTrack::getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount) +{ + if (loopStart != 0) { + *loopStart = mCblk->loopStart; + } + if (loopEnd != 0) { + *loopEnd = mCblk->loopEnd; + } + if (loopCount != 0) { + if (mCblk->loopCount < 0) { + *loopCount = -1; + } else { + *loopCount = mCblk->loopCount; + } + } + + return NO_ERROR; +} + +status_t AudioTrack::setMarkerPosition(uint32_t marker) +{ + if (mCbf == 0) return INVALID_OPERATION; + + mMarkerPosition = marker; + + return NO_ERROR; +} + +status_t AudioTrack::getMarkerPosition(uint32_t *marker) +{ + if (marker == 0) return BAD_VALUE; + + *marker = mMarkerPosition; + + return NO_ERROR; +} + +status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod) +{ + if (mCbf == 0) return INVALID_OPERATION; + + uint32_t curPosition; + getPosition(&curPosition); + mNewPosition = curPosition + updatePeriod; + mUpdatePeriod = updatePeriod; + + return NO_ERROR; +} + +status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) +{ + if (updatePeriod == 0) return BAD_VALUE; + + *updatePeriod = mUpdatePeriod; + + return NO_ERROR; +} + +status_t AudioTrack::setPosition(uint32_t position) +{ + Mutex::Autolock _l(mCblk->lock); + + if (!stopped()) return INVALID_OPERATION; + + if (position > mCblk->user) return BAD_VALUE; + + mCblk->server = position; + mCblk->forceReady = 1; + + return NO_ERROR; +} + +status_t AudioTrack::getPosition(uint32_t *position) +{ + if (position == 0) return BAD_VALUE; + + *position = mCblk->server; + + return NO_ERROR; +} + +status_t AudioTrack::reload() +{ + if (!stopped()) return INVALID_OPERATION; + + flush(); + + mCblk->stepUser(mFrameCount); + + return NO_ERROR; +} + // ------------------------------------------------------------------------- status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, bool blocking) @@ -358,21 +582,17 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, bool blocking) int timeout = 0; status_t result; audio_track_cblk_t* cblk = mCblk; + uint32_t framesReq = audioBuffer->frameCount; - 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; + audioBuffer->frameCount = 0; + audioBuffer->size = 0; - 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; + uint32_t framesAvail = cblk->framesAvailable(); - LOGW_IF(u_seq < s_seq, "user doesn't fill buffers fast enough"); - - if (u_seq > s_seq && u_buf == s_buf) { + if (framesAvail == 0) { Mutex::Autolock _l(cblk->lock); goto start_loop_here; - while (u_seq > s_seq && u_buf == s_buf) { + while (framesAvail == 0) { active = mActive; if (UNLIKELY(!active)) { LOGV("Not active and NO_MORE_BUFFERS"); @@ -384,89 +604,101 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, bool blocking) 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); + "user=%08x, server=%08x", cblk->user, cblk->server); 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; + framesAvail = cblk->framesAvailable_l(); } } + if (framesReq > framesAvail) { + framesReq = framesAvail; + } + + uint32_t u = cblk->user; + uint32_t bufferEnd = cblk->userBase + cblk->frameCount; + + if (u + framesReq > bufferEnd) { + framesReq = bufferEnd - u; + } + 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); + "this shouldn't happen (user=%08x, server=%08x)", cblk->user, cblk->server); 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); + audioBuffer->format = AudioSystem::PCM_16_BIT; + audioBuffer->frameCount = framesReq; + audioBuffer->size = framesReq*mChannelCount*sizeof(int16_t); + audioBuffer->raw = (int8_t *)cblk->buffer(u); 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); + cblk->stepUser(audioBuffer->frameCount); } // ------------------------------------------------------------------------- ssize_t AudioTrack::write(const void* buffer, size_t userSize) { + + if (mSharedBuffer != 0) return INVALID_OPERATION; + if (ssize_t(userSize) < 0) { // sanity-check. user is most-likely passing an error code. - LOGE("AudioTrack::write(buffer=%p, size=%u (%d)", + 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; + const int8_t *src = (const int8_t *)buffer; + Buffer audioBuffer; + 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); - } + audioBuffer.frameCount = userSize/mChannelCount; + if (mFormat == AudioSystem::PCM_16_BIT) { + audioBuffer.frameCount >>= 1; } - size_t capacity = mAudioBuffer.size - mPosition; - size_t toWrite = userSize < capacity ? userSize : capacity; + status_t err = obtainBuffer(&audioBuffer, true); + if (err < 0) { + // out of buffers, return #bytes written + if (err == status_t(NO_MORE_BUFFERS)) + break; + return ssize_t(err); + } - memcpy(mAudioBuffer.i8 + mPosition, buffer, toWrite); - buffer = static_cast<const int8_t*>(buffer) + toWrite; - mPosition += toWrite; + size_t toWrite; + if (mFormat == AudioSystem::PCM_8_BIT) { + // Divide capacity by 2 to take expansion into account + toWrite = audioBuffer.size>>1; + // 8 to 16 bit conversion + int count = toWrite; + int16_t *dst = (int16_t *)(audioBuffer.i8); + while(count--) { + *dst++ = (int16_t)(*src++^0x80) << 8; + } + }else { + toWrite = audioBuffer.size; + memcpy(audioBuffer.i8, src, toWrite); + src += toWrite; + } userSize -= toWrite; - capacity -= toWrite; written += toWrite; - if (capacity == 0) { - mPosition = 0; - releaseBuffer(&mAudioBuffer); - } + releaseBuffer(&audioBuffer); } while (userSize); return written; @@ -477,16 +709,115 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize) bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) { Buffer audioBuffer; + uint32_t frames; + size_t writtenSize = 0; + + // Manage underrun callback + if (mActive && (mCblk->framesReady() == 0)) { + LOGV("Underrun user: %x, server: %x, flowControlFlag %d", mCblk->user, mCblk->server, mCblk->flowControlFlag); + if (mCblk->flowControlFlag == 0) { + mCbf(EVENT_UNDERRUN, mUserData, 0); + if (mCblk->server == mCblk->frameCount) { + mCbf(EVENT_BUFFER_END, mUserData, 0); + } + mCblk->flowControlFlag = 1; + if (mSharedBuffer != 0) return false; + } + } + + // Manage loop end callback + while (mLoopCount > mCblk->loopCount) { + int loopCount = -1; + mLoopCount--; + if (mLoopCount >= 0) loopCount = mLoopCount; + + mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount); + } - status_t err = obtainBuffer(&audioBuffer, true); - if (err < NO_ERROR) { - LOGE("Error obtaining an audio buffer, giving up."); - return false; + // Manage marker callback + if(mMarkerPosition > 0) { + if (mCblk->server >= mMarkerPosition) { + mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition); + mMarkerPosition = 0; + } } - if (err == status_t(STOPPED)) return false; - mCbf(mUserData, audioBuffer); - releaseBuffer(&audioBuffer); + // Manage new position callback + if(mUpdatePeriod > 0) { + while (mCblk->server >= mNewPosition) { + mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition); + mNewPosition += mUpdatePeriod; + } + } + + // If Shared buffer is used, no data is requested from client. + if (mSharedBuffer != 0) { + frames = 0; + } else { + frames = mRemainingFrames; + } + + do { + + audioBuffer.frameCount = frames; + + status_t err = obtainBuffer(&audioBuffer, false); + if (err < NO_ERROR) { + if (err != WOULD_BLOCK) { + LOGE("Error obtaining an audio buffer, giving up."); + return false; + } + } + if (err == status_t(STOPPED)) return false; + + if (audioBuffer.size == 0) break; + + // Divide buffer size by 2 to take into account the expansion + // due to 8 to 16 bit conversion: the callback must fill only half + // of the destination buffer + if (mFormat == AudioSystem::PCM_8_BIT) { + audioBuffer.size >>= 1; + } + + size_t reqSize = audioBuffer.size; + mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); + writtenSize = audioBuffer.size; + + // Sanity check on returned size + if (ssize_t(writtenSize) <= 0) break; + if (writtenSize > reqSize) writtenSize = reqSize; + + if (mFormat == AudioSystem::PCM_8_BIT) { + // 8 to 16 bit conversion + const int8_t *src = audioBuffer.i8 + writtenSize-1; + int count = writtenSize; + int16_t *dst = audioBuffer.i16 + writtenSize-1; + while(count--) { + *dst-- = (int16_t)(*src--^0x80) << 8; + } + writtenSize <<= 1; + } + + audioBuffer.size = writtenSize; + audioBuffer.frameCount = writtenSize/mChannelCount/sizeof(int16_t); + frames -= audioBuffer.frameCount; + + releaseBuffer(&audioBuffer); + } + while (frames); + + // If no data was written, it is likely that obtainBuffer() did + // not find room in PCM buffer: we release the processor for + // a few millisecond before polling again for available room. + if (writtenSize == 0) { + usleep(5000); + } + + if (frames == 0) { + mRemainingFrames = mNotificationFrames; + } else { + mRemainingFrames = frames; + } return true; } @@ -500,11 +831,11 @@ status_t AudioTrack::dump(int fd, const Vector<String16>& args) const 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); + snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat, mChannelCount, mFrameCount); result.append(buffer); - snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d), reserved(%d)\n", mSampleRate, mStatus, mMuted, mReserved); + snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d)\n", mSampleRate, mStatus, mMuted); result.append(buffer); - snprintf(buffer, 255, " active(%d), latency (%lld), position(%d)\n", mActive, mLatency, mPosition); + snprintf(buffer, 255, " active(%d), latency (%d)\n", mActive, mLatency); result.append(buffer); ::write(fd, result.string(), result.size()); return NO_ERROR; @@ -512,8 +843,8 @@ status_t AudioTrack::dump(int fd, const Vector<String16>& args) const // ========================================================================= -AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver) - : Thread(false), mReceiver(receiver) +AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava) + : Thread(bCanCallJava), mReceiver(receiver) { } @@ -534,25 +865,35 @@ void AudioTrack::AudioTrackThread::onFirstRef() // ========================================================================= audio_track_cblk_t::audio_track_cblk_t() - : user(0), server(0), volumeLR(0), buffers(0), size(0) + : user(0), server(0), userBase(0), serverBase(0), buffers(0), frameCount(0), + loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0), flowControlFlag(1), forceReady(0) { } -uint32_t audio_track_cblk_t::stepUser(int bufferCount) +uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) { 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; + + u += frameCount; + // Ensure that user is never ahead of server for AudioRecord + if (!out && u > this->server) { + LOGW("stepServer occured after track reset"); + u = this->server; + } + + if (u >= userBase + this->frameCount) { + userBase += this->frameCount; + } + + this->user = u; + + // Clear flow control error condition as new data has been written/read to/from buffer. + flowControlFlag = 0; + return u; } -bool audio_track_cblk_t::stepServer(int bufferCount) +bool audio_track_cblk_t::stepServer(uint32_t frameCount) { // the code below simulates lock-with-timeout // we MUST do this to protect the AudioFlinger server @@ -570,24 +911,83 @@ bool audio_track_cblk_t::stepServer(int bufferCount) } 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 += frameCount; + // It is possible that we receive a flush() + // while the mixer is processing a block: in this case, + // stepServer() is called After the flush() has reset u & s and + // we have s > u + if (out && s > this->user) { + LOGW("stepServer occured after track reset"); + s = this->user; } - s = s_seq | s_buf; - this->server = s; + if (s >= loopEnd) { + LOGW_IF(s > loopEnd, "stepServer: s %u > loopEnd %u", s, loopEnd); + s = loopStart; + if (--loopCount == 0) { + loopEnd = UINT_MAX; + loopStart = UINT_MAX; + } + } + if (s >= serverBase + this->frameCount) { + serverBase += this->frameCount; + } + + this->server = s; + cv.signal(); lock.unlock(); return true; } -void* audio_track_cblk_t::buffer(int id) const +void* audio_track_cblk_t::buffer(uint32_t offset) const { - return (char*)this->buffers + id * this->size; + return (int16_t *)this->buffers + (offset-userBase)*this->channels; +} + +uint32_t audio_track_cblk_t::framesAvailable() +{ + Mutex::Autolock _l(lock); + return framesAvailable_l(); +} + +uint32_t audio_track_cblk_t::framesAvailable_l() +{ + uint32_t u = this->user; + uint32_t s = this->server; + + if (out) { + if (u < loopEnd) { + return s + frameCount - u; + } else { + uint32_t limit = (s < loopStart) ? s : loopStart; + return limit + frameCount - u; + } + } else { + return frameCount + u - s; + } +} + +uint32_t audio_track_cblk_t::framesReady() +{ + uint32_t u = this->user; + uint32_t s = this->server; + + if (out) { + if (u < loopEnd) { + return u - s; + } else { + Mutex::Autolock _l(lock); + if (loopCount >= 0) { + return (loopEnd - loopStart)*loopCount + u - s; + } else { + return UINT_MAX; + } + } + } else { + return s - u; + } } // ------------------------------------------------------------------------- diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 474381b..018ea6c 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -34,6 +34,7 @@ enum { CHANNEL_COUNT, FORMAT, FRAME_COUNT, + LATENCY, SET_MASTER_VOLUME, SET_MASTER_MUTE, MASTER_VOLUME, @@ -66,8 +67,10 @@ public: uint32_t sampleRate, int format, int channelCount, - int bufferCount, - uint32_t flags) + int frameCount, + uint32_t flags, + const sp<IMemory>& sharedBuffer, + status_t *status) { Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); @@ -76,13 +79,17 @@ public: data.writeInt32(sampleRate); data.writeInt32(format); data.writeInt32(channelCount); - data.writeInt32(bufferCount); + data.writeInt32(frameCount); data.writeInt32(flags); - status_t status = remote()->transact(CREATE_TRACK, data, &reply); - if ( status != NO_ERROR) { - LOGE("createTrack error: %s", strerror(-status)); + data.writeStrongBinder(sharedBuffer->asBinder()); + status_t lStatus = remote()->transact(CREATE_TRACK, data, &reply); + if (lStatus != NO_ERROR) { + LOGE("createTrack error: %s", strerror(-lStatus)); + } + lStatus = reply.readInt32(); + if (status) { + *status = lStatus; } - return interface_cast<IAudioTrack>(reply.readStrongBinder()); } @@ -92,8 +99,9 @@ public: uint32_t sampleRate, int format, int channelCount, - int bufferCount, - uint32_t flags) + int frameCount, + uint32_t flags, + status_t *status) { Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); @@ -102,9 +110,13 @@ public: data.writeInt32(sampleRate); data.writeInt32(format); data.writeInt32(channelCount); - data.writeInt32(bufferCount); + data.writeInt32(frameCount); data.writeInt32(flags); remote()->transact(OPEN_RECORD, data, &reply); + status_t lStatus = reply.readInt32(); + if (status) { + *status = lStatus; + } return interface_cast<IAudioRecord>(reply.readStrongBinder()); } @@ -140,6 +152,14 @@ public: return reply.readInt32(); } + virtual uint32_t latency() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(LATENCY, data, &reply); + return reply.readInt32(); + } + virtual status_t setMasterVolume(float value) { Parcel data, reply; @@ -308,9 +328,12 @@ status_t BnAudioFlinger::onTransact( int channelCount = data.readInt32(); size_t bufferCount = data.readInt32(); uint32_t flags = data.readInt32(); - sp<IAudioTrack> track = createTrack(pid, + sp<IMemory> buffer = interface_cast<IMemory>(data.readStrongBinder()); + status_t status; + sp<IAudioTrack> track = createTrack(pid, streamType, sampleRate, format, - channelCount, bufferCount, flags); + channelCount, bufferCount, flags, buffer, &status); + reply->writeInt32(status); reply->writeStrongBinder(track->asBinder()); return NO_ERROR; } break; @@ -323,8 +346,10 @@ status_t BnAudioFlinger::onTransact( int channelCount = data.readInt32(); size_t bufferCount = data.readInt32(); uint32_t flags = data.readInt32(); + status_t status; sp<IAudioRecord> record = openRecord(pid, streamType, - sampleRate, format, channelCount, bufferCount, flags); + sampleRate, format, channelCount, bufferCount, flags, &status); + reply->writeInt32(status); reply->writeStrongBinder(record->asBinder()); return NO_ERROR; } break; @@ -348,7 +373,12 @@ status_t BnAudioFlinger::onTransact( reply->writeInt32( frameCount() ); return NO_ERROR; } break; - case SET_MASTER_VOLUME: { + case LATENCY: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( latency() ); + return NO_ERROR; + } break; + case SET_MASTER_VOLUME: { CHECK_INTERFACE(IAudioFlinger, data, reply); reply->writeInt32( setMasterVolume(data.readFloat()) ); return NO_ERROR; diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp new file mode 100644 index 0000000..615ae37 --- /dev/null +++ b/media/libmedia/IMediaMetadataRetriever.cpp @@ -0,0 +1,218 @@ +/* +** +** Copyright (C) 2008 The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <stdint.h> +#include <sys/types.h> +#include <utils/Parcel.h> +#include <graphics/SkBitmap.h> +#include <media/IMediaMetadataRetriever.h> + +namespace android { + +enum { + DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, + SET_DATA_SOURCE_URL, + SET_DATA_SOURCE_FD, + SET_MODE, + GET_MODE, + CAPTURE_FRAME, + EXTARCT_ALBUM_ART, + EXTRACT_METADATA, +}; + +class BpMediaMetadataRetriever: public BpInterface<IMediaMetadataRetriever> +{ +public: + BpMediaMetadataRetriever(const sp<IBinder>& impl) + : BpInterface<IMediaMetadataRetriever>(impl) + { + } + + // disconnect from media metadata retriever service + void disconnect() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + remote()->transact(DISCONNECT, data, &reply); + } + + status_t setDataSource(const char* srcUrl) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + data.writeCString(srcUrl); + remote()->transact(SET_DATA_SOURCE_URL, data, &reply); + return reply.readInt32(); + } + + status_t setDataSource(int fd, int64_t offset, int64_t length) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + data.writeFileDescriptor(fd); + data.writeInt64(offset); + data.writeInt64(length); + remote()->transact(SET_DATA_SOURCE_FD, data, &reply); + return reply.readInt32(); + } + + status_t setMode(int mode) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + data.writeInt32(mode); + remote()->transact(SET_MODE, data, &reply); + return reply.readInt32(); + } + + status_t getMode(int* mode) const + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + remote()->transact(GET_MODE, data, &reply); + *mode = reply.readInt32(); + return reply.readInt32(); + } + + sp<IMemory> captureFrame() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + remote()->transact(CAPTURE_FRAME, data, &reply); + status_t ret = reply.readInt32(); + if (ret != NO_ERROR) { + return NULL; + } + return interface_cast<IMemory>(reply.readStrongBinder()); + } + + sp<IMemory> extractAlbumArt() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + remote()->transact(EXTARCT_ALBUM_ART, data, &reply); + status_t ret = reply.readInt32(); + if (ret != NO_ERROR) { + return NULL; + } + return interface_cast<IMemory>(reply.readStrongBinder()); + } + + const char* extractMetadata(int keyCode) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + data.writeInt32(keyCode); + remote()->transact(EXTRACT_METADATA, data, &reply); + status_t ret = reply.readInt32(); + if (ret != NO_ERROR) { + return NULL; + } + return reply.readCString(); + } +}; + +IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.hardware.IMediaMetadataRetriever"); + +// ---------------------------------------------------------------------- + +#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 BnMediaMetadataRetriever::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case DISCONNECT: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + disconnect(); + return NO_ERROR; + } break; + case SET_DATA_SOURCE_URL: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + const char* srcUrl = data.readCString(); + reply->writeInt32(setDataSource(srcUrl)); + return NO_ERROR; + } break; + case SET_DATA_SOURCE_FD: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + int fd = dup(data.readFileDescriptor()); + int64_t offset = data.readInt64(); + int64_t length = data.readInt64(); + reply->writeInt32(setDataSource(fd, offset, length)); + return NO_ERROR; + } break; + case SET_MODE: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + int mode = data.readInt32(); + reply->writeInt32(setMode(mode)); + return NO_ERROR; + } break; + case GET_MODE: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + int mode; + status_t status = getMode(&mode); + reply->writeInt32(mode); + reply->writeInt32(status); + return NO_ERROR; + } break; + case CAPTURE_FRAME: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + sp<IMemory> bitmap = captureFrame(); + if (bitmap != 0) { // Don't send NULL across the binder interface + reply->writeInt32(NO_ERROR); + reply->writeStrongBinder(bitmap->asBinder()); + } else { + reply->writeInt32(UNKNOWN_ERROR); + } + return NO_ERROR; + } break; + case EXTARCT_ALBUM_ART: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + sp<IMemory> albumArt = extractAlbumArt(); + if (albumArt != 0) { // Don't send NULL across the binder interface + reply->writeInt32(NO_ERROR); + reply->writeStrongBinder(albumArt->asBinder()); + } else { + reply->writeInt32(UNKNOWN_ERROR); + } + return NO_ERROR; + } break; + case EXTRACT_METADATA: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + int keyCode = data.readInt32(); + const char* value = extractMetadata(keyCode); + if (value != NULL) { // Don't send NULL across the binder interface + reply->writeInt32(NO_ERROR); + reply->writeCString(value); + } else { + reply->writeInt32(UNKNOWN_ERROR); + } + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index 8385114..f37519f 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -33,7 +33,6 @@ enum { STOP, IS_PLAYING, PAUSE, - GET_VIDEO_SIZE, SEEK_TO, GET_CURRENT_POSITION, GET_DURATION, @@ -109,16 +108,6 @@ public: 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; @@ -235,15 +224,6 @@ status_t BnMediaPlayer::onTransact( 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())); diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index b087100..370e3fb 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -21,6 +21,7 @@ #include <utils/IMemory.h> #include <media/IMediaPlayerService.h> +#include <media/IMediaRecorder.h> namespace android { @@ -29,6 +30,8 @@ enum { CREATE_FD, DECODE_URL, DECODE_FD, + CREATE_MEDIA_RECORDER, + CREATE_METADATA_RETRIEVER, }; class BpMediaPlayerService: public BpInterface<IMediaPlayerService> @@ -39,6 +42,15 @@ public: { } + virtual sp<IMediaMetadataRetriever> createMetadataRetriever(pid_t pid) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeInt32(pid); + remote()->transact(CREATE_METADATA_RETRIEVER, data, &reply); + return interface_cast<IMediaMetadataRetriever>(reply.readStrongBinder()); + } + virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url) { Parcel data, reply; @@ -50,6 +62,15 @@ public: return interface_cast<IMediaPlayer>(reply.readStrongBinder()); } + virtual sp<IMediaRecorder> createMediaRecorder(pid_t pid) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeInt32(pid); + remote()->transact(CREATE_MEDIA_RECORDER, data, &reply); + return interface_cast<IMediaRecorder>(reply.readStrongBinder()); + } + virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length) { Parcel data, reply; @@ -63,7 +84,7 @@ public: return interface_cast<IMediaPlayer>(reply.readStrongBinder()); } - virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) + virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); @@ -71,10 +92,11 @@ public: remote()->transact(DECODE_URL, data, &reply); *pSampleRate = uint32_t(reply.readInt32()); *pNumChannels = reply.readInt32(); + *pFormat = reply.readInt32(); return interface_cast<IMemory>(reply.readStrongBinder()); } - virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) + virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); @@ -84,6 +106,7 @@ public: remote()->transact(DECODE_FD, data, &reply); *pSampleRate = uint32_t(reply.readInt32()); *pNumChannels = reply.readInt32(); + *pFormat = reply.readInt32(); return interface_cast<IMemory>(reply.readStrongBinder()); } }; @@ -127,9 +150,11 @@ status_t BnMediaPlayerService::onTransact( const char* url = data.readCString(); uint32_t sampleRate; int numChannels; - sp<IMemory> player = decode(url, &sampleRate, &numChannels); + int format; + sp<IMemory> player = decode(url, &sampleRate, &numChannels, &format); reply->writeInt32(sampleRate); reply->writeInt32(numChannels); + reply->writeInt32(format); reply->writeStrongBinder(player->asBinder()); return NO_ERROR; } break; @@ -140,12 +165,28 @@ status_t BnMediaPlayerService::onTransact( int64_t length = data.readInt64(); uint32_t sampleRate; int numChannels; - sp<IMemory> player = decode(fd, offset, length, &sampleRate, &numChannels); + int format; + sp<IMemory> player = decode(fd, offset, length, &sampleRate, &numChannels, &format); reply->writeInt32(sampleRate); reply->writeInt32(numChannels); + reply->writeInt32(format); reply->writeStrongBinder(player->asBinder()); return NO_ERROR; } break; + case CREATE_MEDIA_RECORDER: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + pid_t pid = data.readInt32(); + sp<IMediaRecorder> recorder = createMediaRecorder(pid); + reply->writeStrongBinder(recorder->asBinder()); + return NO_ERROR; + } break; + case CREATE_METADATA_RETRIEVER: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + pid_t pid = data.readInt32(); + sp<IMediaMetadataRetriever> retriever = createMetadataRetriever(pid); + reply->writeStrongBinder(retriever->asBinder()); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp new file mode 100644 index 0000000..1f6d599 --- /dev/null +++ b/media/libmedia/IMediaRecorder.cpp @@ -0,0 +1,376 @@ +/* + ** + ** Copyright 2008, HTC Inc. + ** + ** 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 "IMediaRecorder" +#include <utils/Log.h> +#include <utils/Parcel.h> +#include <ui/ISurface.h> +#include <ui/ICamera.h> +#include <media/IMediaRecorder.h> + +namespace android { + +enum { + RELEASE = IBinder::FIRST_CALL_TRANSACTION, + INIT, + CLOSE, + RESET, + STOP, + START, + PREPARE, + GET_MAX_AMPLITUDE, + SET_VIDEO_SOURCE, + SET_AUDIO_SOURCE, + SET_OUTPUT_FORMAT, + SET_VIDEO_ENCODER, + SET_AUDIO_ENCODER, + SET_OUTPUT_FILE, + SET_VIDEO_SIZE, + SET_VIDEO_FRAMERATE, + SET_PREVIEW_SURFACE, + SET_CAMERA +}; + +class BpMediaRecorder: public BpInterface<IMediaRecorder> +{ +public: + BpMediaRecorder(const sp<IBinder>& impl) + : BpInterface<IMediaRecorder>(impl) + { + } + + status_t setCamera(const sp<ICamera>& camera) + { + LOGV("setCamera(%p)", camera.get()); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeStrongBinder(camera->asBinder()); + remote()->transact(SET_CAMERA, data, &reply); + return reply.readInt32(); + } + + status_t setPreviewSurface(const sp<ISurface>& surface) + { + LOGV("setPreviewSurface(%p)", surface.get()); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeStrongBinder(surface->asBinder()); + remote()->transact(SET_PREVIEW_SURFACE, data, &reply); + return reply.readInt32(); + } + + status_t init() + { + LOGV("init"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(INIT, data, &reply); + return reply.readInt32(); + } + + status_t setVideoSource(int vs) + { + LOGV("setVideoSource(%d)", vs); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(vs); + remote()->transact(SET_VIDEO_SOURCE, data, &reply); + return reply.readInt32(); + } + + status_t setAudioSource(int as) + { + LOGV("setAudioSource(%d)", as); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(as); + remote()->transact(SET_AUDIO_SOURCE, data, &reply); + return reply.readInt32(); + } + + status_t setOutputFormat(int of) + { + LOGV("setOutputFormat(%d)", of); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(of); + remote()->transact(SET_OUTPUT_FORMAT, data, &reply); + return reply.readInt32(); + } + + status_t setVideoEncoder(int ve) + { + LOGV("setVideoEncoder(%d)", ve); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(ve); + remote()->transact(SET_VIDEO_ENCODER, data, &reply); + return reply.readInt32(); + } + + status_t setAudioEncoder(int ae) + { + LOGV("setAudioEncoder(%d)", ae); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(ae); + remote()->transact(SET_AUDIO_ENCODER, data, &reply); + return reply.readInt32(); + } + + status_t setOutputFile(const char* path) + { + LOGV("setOutputFile(%s)", path); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeCString(path); + remote()->transact(SET_OUTPUT_FILE, data, &reply); + return reply.readInt32(); + } + + status_t setVideoSize(int width, int height) + { + LOGV("setVideoSize(%dx%d)", width, height); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(width); + data.writeInt32(height); + remote()->transact(SET_VIDEO_SIZE, data, &reply); + return reply.readInt32(); + } + + status_t setVideoFrameRate(int frames_per_second) + { + LOGV("setVideoFrameRate(%d)", frames_per_second); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(frames_per_second); + remote()->transact(SET_VIDEO_FRAMERATE, data, &reply); + return reply.readInt32(); + } + + status_t prepare() + { + LOGV("prepare"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(PREPARE, data, &reply); + return reply.readInt32(); + } + + status_t getMaxAmplitude(int* max) + { + LOGV("getMaxAmplitude"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(GET_MAX_AMPLITUDE, data, &reply); + *max = reply.readInt32(); + return reply.readInt32(); + } + + status_t start() + { + LOGV("start"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(START, data, &reply); + return reply.readInt32(); + } + + status_t stop() + { + LOGV("stop"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(STOP, data, &reply); + return reply.readInt32(); + } + + status_t reset() + { + LOGV("reset"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(RESET, data, &reply); + return reply.readInt32(); + } + + status_t close() + { + LOGV("close"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(CLOSE, data, &reply); + return reply.readInt32(); + } + + status_t release() + { + LOGV("release"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(RELEASE, data, &reply); + return reply.readInt32(); + } +}; + +IMPLEMENT_META_INTERFACE(MediaRecorder, "android.hardware.IMediaRecorder"); + +// ---------------------------------------------------------------------- + +#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 BnMediaRecorder::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case RELEASE: { + LOGV("RELEASE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(release()); + return NO_ERROR; + } break; + case INIT: { + LOGV("INIT"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(init()); + return NO_ERROR; + } break; + case CLOSE: { + LOGV("CLOSE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(close()); + return NO_ERROR; + } break; + case RESET: { + LOGV("RESET"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(reset()); + return NO_ERROR; + } break; + case STOP: { + LOGV("STOP"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(stop()); + return NO_ERROR; + } break; + case START: { + LOGV("START"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(start()); + return NO_ERROR; + } break; + case PREPARE: { + LOGV("PREPARE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(prepare()); + return NO_ERROR; + } break; + case GET_MAX_AMPLITUDE: { + LOGV("GET_MAX_AMPLITUDE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int max = 0; + status_t ret = getMaxAmplitude(&max); + reply->writeInt32(max); + reply->writeInt32(ret); + return NO_ERROR; + } break; + case SET_VIDEO_SOURCE: { + LOGV("SET_VIDEO_SOURCE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int vs = data.readInt32(); + reply->writeInt32(setVideoSource(vs)); + return NO_ERROR; + } break; + case SET_AUDIO_SOURCE: { + LOGV("SET_AUDIO_SOURCE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int as = data.readInt32(); + reply->writeInt32(setAudioSource(as)); + return NO_ERROR; + } break; + case SET_OUTPUT_FORMAT: { + LOGV("SET_OUTPUT_FORMAT"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int of = data.readInt32(); + reply->writeInt32(setOutputFormat(of)); + return NO_ERROR; + } break; + case SET_VIDEO_ENCODER: { + LOGV("SET_VIDEO_ENCODER"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int ve = data.readInt32(); + reply->writeInt32(setVideoEncoder(ve)); + return NO_ERROR; + } break; + case SET_AUDIO_ENCODER: { + LOGV("SET_AUDIO_ENCODER"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int ae = data.readInt32(); + reply->writeInt32(setAudioEncoder(ae)); + return NO_ERROR; + + } break; + case SET_OUTPUT_FILE: { + LOGV("SET_OUTPUT_FILE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + const char* path = data.readCString(); + reply->writeInt32(setOutputFile(path)); + return NO_ERROR; + } break; + case SET_VIDEO_SIZE: { + LOGV("SET_VIDEO_SIZE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int width = data.readInt32(); + int height = data.readInt32(); + reply->writeInt32(setVideoSize(width, height)); + return NO_ERROR; + } break; + case SET_VIDEO_FRAMERATE: { + LOGV("SET_VIDEO_FRAMERATE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int frames_per_second = data.readInt32(); + reply->writeInt32(setVideoFrameRate(frames_per_second)); + return NO_ERROR; + } break; + case SET_PREVIEW_SURFACE: { + LOGV("SET_PREVIEW_SURFACE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + sp<ISurface> surface = interface_cast<ISurface>(data.readStrongBinder()); + reply->writeInt32(setPreviewSurface(surface)); + return NO_ERROR; + } break; + case SET_CAMERA: { + LOGV("SET_CAMERA"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + sp<ICamera> camera = interface_cast<ICamera>(data.readStrongBinder()); + reply->writeInt32(setCamera(camera)); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 89ab2be..0dee1f6 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -88,56 +88,34 @@ const ToneGenerator::ToneDescriptor // //////////////////////////////////////////////////////////////////////////////// ToneGenerator::ToneGenerator(int streamType, float volume) { - const sp<IAudioFlinger>& 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) { + if (AudioSystem::getOutputSamplingRate(&mSamplingRate) != NO_ERROR) { LOGE("Unable to marshal AudioFlinger"); - goto ToneGenerator_exit; + return; } - - 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; + if (AudioSystem::getOutputFrameCount(&mBufferSize) != NO_ERROR) { + LOGE("Unable to marshal AudioFlinger"); + return; } - LOGV("Create Track: %p\n", mpAudioTrack); + mStreamType = streamType; + mVolume = volume; + mpAudioTrack = 0; + mpToneDesc = 0; + mpNewToneDesc = 0; - if (mpAudioTrack->initCheck() != NO_ERROR) { - LOGE("AudioTrack->initCheck failed"); - goto ToneGenerator_exit; + if (initAudioTrack()) { + LOGV("ToneGenerator INIT OK, time: %d\n", (unsigned int)(systemTime()/1000000)); + } else { + LOGV("!!!ToneGenerator INIT FAILED!!!\n"); } +} - 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"); -} //////////////////////////////////////////////////////////////////////////////// // @@ -179,9 +157,16 @@ ToneGenerator::~ToneGenerator() { bool ToneGenerator::startTone(int toneType) { bool lResult = false; - if (mState == TONE_IDLE || toneType >= NUM_TONES) + if (toneType >= NUM_TONES) return lResult; + if (mState == TONE_IDLE) { + LOGV("startTone: try to re-init AudioTrack"); + if (!initAudioTrack()) { + return lResult; + } + } + LOGV("startTone\n"); mLock.lock(); @@ -198,8 +183,10 @@ bool ToneGenerator::startTone(int toneType) { mpAudioTrack->start(); mLock.lock(); if (mState == TONE_STARTING) { - if (mWaitCbkCond.waitRelative(mLock, seconds(1)) != NO_ERROR) + if (mWaitCbkCond.waitRelative(mLock, seconds(1)) != NO_ERROR) { LOGE("--- timed out"); + mState = TONE_IDLE; + } } if (mState == TONE_PLAYING) @@ -216,6 +203,7 @@ bool ToneGenerator::startTone(int toneType) { LOGV("cond received"); } else { LOGE("--- timed out"); + mState = TONE_IDLE; } } mLock.unlock(); @@ -250,7 +238,8 @@ void ToneGenerator::stopTone() { LOGV("track stop complete, time %d", (unsigned int)(systemTime()/1000000)); } else { LOGE("--- timed out"); - mState = TONE_INIT; + mState = TONE_IDLE; + mpAudioTrack->stop(); } } @@ -262,6 +251,62 @@ void ToneGenerator::stopTone() { //---------------------------------- private methods --------------------------- + + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::initAudioTrack() +// +// Description: Allocates and configures AudioTrack used for PCM output. +// +// Input: +// none +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +bool ToneGenerator::initAudioTrack() { + + if (mpAudioTrack) { + delete mpAudioTrack; + mpAudioTrack = 0; + } + + // Open audio track in mono, PCM 16bit, default sampling rate, 2 buffers of + mpAudioTrack + = new AudioTrack(mStreamType, 0, AudioSystem::PCM_16_BIT, 1, NUM_PCM_BUFFERS*mBufferSize, 0, audioCallback, this, mBufferSize); + + if (mpAudioTrack == 0) { + LOGE("AudioTrack allocation failed"); + goto initAudioTrack_exit; + } + LOGV("Create Track: %p\n", mpAudioTrack); + + if (mpAudioTrack->initCheck() != NO_ERROR) { + LOGE("AudioTrack->initCheck failed"); + goto initAudioTrack_exit; + } + + mpAudioTrack->setVolume(mVolume, mVolume); + + mState = TONE_INIT; + + return true; + +initAudioTrack_exit: + + // Cleanup + if (mpAudioTrack) { + LOGV("Delete Track I: %p\n", mpAudioTrack); + delete mpAudioTrack; + mpAudioTrack = 0; + } + + return false; +} + + //////////////////////////////////////////////////////////////////////////////// // // Method: ToneGenerator::audioCallback() @@ -278,19 +323,24 @@ void ToneGenerator::stopTone() { // returned value: always true. // //////////////////////////////////////////////////////////////////////////////// -void ToneGenerator::audioCallback(void* user, const AudioTrack::Buffer& info) { +void ToneGenerator::audioCallback(int event, void* user, void *info) { + + if (event != AudioTrack::EVENT_MORE_DATA) return; + + const AudioTrack::Buffer *buffer = static_cast<const AudioTrack::Buffer *>(info); ToneGenerator *lpToneGen = static_cast<ToneGenerator *>(user); - short *lpOut = info.i16; - unsigned int lReqSmp = info.size/sizeof(short); + short *lpOut = buffer->i16; + unsigned int lReqSmp = buffer->size/sizeof(short); unsigned int lGenSmp; unsigned int lWaveCmd = WaveGenerator::WAVEGEN_CONT; bool lSignal = false; + if (buffer->size == 0) return; lpToneGen->mLock.lock(); // Clear output buffer: WaveGenerator accumulates into lpOut buffer - memset(lpOut, 0, info.size); + memset(lpOut, 0, buffer->size); // Update pcm frame count and end time (current time at the end of this process) lpToneGen->mTotalSmp += lReqSmp; @@ -317,8 +367,11 @@ void ToneGenerator::audioCallback(void* user, const AudioTrack::Buffer& info) { goto audioCallback_Exit; } - // Exit if to sequence is over + // Exit if tone sequence is over if (lpToneGen->mpToneDesc->segments[lpToneGen->mCurSegment] == 0) { + if (lpToneGen->mState == TONE_PLAYING) { + lpToneGen->mState = TONE_STOPPING; + } goto audioCallback_Exit; } @@ -327,7 +380,7 @@ void ToneGenerator::audioCallback(void* user, const AudioTrack::Buffer& info) { LOGV("End Segment, time: %d\n", (unsigned int)(systemTime()/1000000)); - lGenSmp = lReqSmp; + lGenSmp = lReqSmp; if (lpToneGen->mCurSegment & 0x0001) { // If odd segment, OFF -> ON transition : reset wave generator diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp index 9cbafbc..09afc6c 100644 --- a/media/libmedia/mediametadataretriever.cpp +++ b/media/libmedia/mediametadataretriever.cpp @@ -15,168 +15,174 @@ ** limitations under the License. */ -#include <media/mediametadataretriever.h> - -#ifdef LOG_TAG -#undef LOG_TAG +//#define LOG_NDEBUG 0 #define LOG_TAG "MediaMetadataRetriever" -#endif +#include <utils/IServiceManager.h> +#include <utils/IPCThreadState.h> +#include <media/mediametadataretriever.h> +#include <media/IMediaPlayerService.h> #include <utils/Log.h> #include <dlfcn.h> namespace android { -// Factory class function in shared libpvplayer.so -typedef MediaMetadataRetrieverImpl* (*createRetriever_f)(); +// client singleton for binder interface to service +Mutex MediaMetadataRetriever::sServiceLock; +sp<IMediaPlayerService> MediaMetadataRetriever::sService; +sp<MediaMetadataRetriever::DeathNotifier> MediaMetadataRetriever::sDeathNotifier; -MediaMetadataRetrieverImpl *MediaMetadataRetriever::mRetriever = NULL; -void *MediaMetadataRetriever::mLibHandler = NULL; - -void MediaMetadataRetriever::create() +const sp<IMediaPlayerService>& MediaMetadataRetriever::getService() { - // 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; + Mutex::Autolock lock(sServiceLock); + if (sService.get() == 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> 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 (sDeathNotifier == NULL) { + sDeathNotifier = new DeathNotifier(); } + binder->linkToDeath(sDeathNotifier); + sService = interface_cast<IMediaPlayerService>(binder); } - - // Each time create a new MediaMetadataRetrieverImpl object. - if (mRetriever) { - delete mRetriever; - } - createRetriever_f createRetriever = reinterpret_cast<createRetriever_f>(dlsym(mLibHandler, "createRetriever")); - if (!createRetriever) { - LOGE("setDataSource: dlsym failed on createRetriever in libpvplayer.so"); + LOGE_IF(sService == 0, "no MediaPlayerService!?"); + return sService; +} + +MediaMetadataRetriever::MediaMetadataRetriever() +{ + LOGV("constructor"); + const sp<IMediaPlayerService>& service(getService()); + if (service == 0) { + LOGE("failed to obtain MediaMetadataRetrieverService"); return; } - mRetriever = createRetriever(); - if (!mRetriever) { - LOGE("setDataSource: createRetriever failed in libpvplayer.so"); + sp<IMediaMetadataRetriever> retriever(service->createMetadataRetriever(getpid())); + if (retriever == 0) { + LOGE("failed to create IMediaMetadataRetriever object from server"); } + mRetriever = retriever; } -status_t MediaMetadataRetriever::setDataSource(const char* srcUrl) +MediaMetadataRetriever::~MediaMetadataRetriever() { - if (srcUrl == NULL) { - return UNKNOWN_ERROR; + LOGV("destructor"); + disconnect(); + IPCThreadState::self()->flushCommands(); +} + +void MediaMetadataRetriever::disconnect() +{ + LOGV("disconnect"); + sp<IMediaMetadataRetriever> retriever; + { + Mutex::Autolock _l(mLock); + retriever = mRetriever; + mRetriever.clear(); } - - if (mRetriever) { - return mRetriever->setDataSource(srcUrl); + if (retriever != 0) { + retriever->disconnect(); } - return UNKNOWN_ERROR; } -const char* MediaMetadataRetriever::extractMetadata(int keyCode) +status_t MediaMetadataRetriever::setDataSource(const char* srcUrl) { - if (mRetriever) { - return mRetriever->extractMetadata(keyCode); + LOGV("setDataSource"); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return INVALID_OPERATION; + } + if (srcUrl == NULL) { + LOGE("data source is a null pointer"); + return UNKNOWN_ERROR; } - return NULL; + LOGV("data source (%s)", srcUrl); + return mRetriever->setDataSource(srcUrl); } -MediaAlbumArt* MediaMetadataRetriever::extractAlbumArt() +status_t MediaMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length) { - if (mRetriever) { - return mRetriever->extractAlbumArt(); + LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return INVALID_OPERATION; } - return NULL; + if (fd < 0 || offset < 0 || length < 0) { + LOGE("Invalid negative argument"); + return UNKNOWN_ERROR; + } + return mRetriever->setDataSource(fd, offset, length); } -SkBitmap* MediaMetadataRetriever::captureFrame() +status_t MediaMetadataRetriever::setMode(int mode) { - if (mRetriever) { - return mRetriever->captureFrame(); + LOGV("setMode(%d)", mode); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return INVALID_OPERATION; } - return NULL; + return mRetriever->setMode(mode); } -void MediaMetadataRetriever::setMode(int mode) +status_t MediaMetadataRetriever::getMode(int* mode) { - if (mRetriever) { - mRetriever->setMode(mode); + LOGV("getMode"); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return INVALID_OPERATION; } + return mRetriever->getMode(mode); } -void MediaMetadataRetriever::release() +sp<IMemory> MediaMetadataRetriever::captureFrame() { - if (!mLibHandler) { - dlclose(mLibHandler); - mLibHandler = NULL; - } - if (!mRetriever) { - delete mRetriever; - mRetriever = NULL; + LOGV("captureFrame"); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return NULL; } + return mRetriever->captureFrame(); } -void MediaAlbumArt::clearData() { - if (data != NULL) { - delete []data; - data = NULL; +const char* MediaMetadataRetriever::extractMetadata(int keyCode) +{ + LOGV("extractMetadata(%d)", keyCode); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return NULL; } - length = 0; + return mRetriever->extractMetadata(keyCode); } - -MediaAlbumArt::MediaAlbumArt(const char* url) +sp<IMemory> MediaMetadataRetriever::extractAlbumArt() { - 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."); + LOGV("extractAlbumArt"); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return NULL; } - fclose(in); + return mRetriever->extractAlbumArt(); } -status_t MediaAlbumArt::setData(unsigned int len, const char* buf) { - clearData(); - length = len; - data = copyData(len, buf); - return (data != NULL)? OK: UNKNOWN_ERROR; +void MediaMetadataRetriever::DeathNotifier::binderDied(const wp<IBinder>& who) { + Mutex::Autolock lock(MediaMetadataRetriever::sServiceLock); + MediaMetadataRetriever::sService.clear(); + LOGW("MediaMetadataRetriever server died!"); } -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; +MediaMetadataRetriever::DeathNotifier::~DeathNotifier() +{ + Mutex::Autolock lock(sServiceLock); + if (sService != 0) { + sService->asBinder()->unlinkToDeath(this); } - memcpy(copy, buf, len); - return copy; } }; // namespace android diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 736d84a..ebdbda8 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -28,22 +28,23 @@ #include <utils/IPCThreadState.h> #include <media/mediaplayer.h> -#include <libsonivox/eas.h> +#include <media/AudioTrack.h> #include <utils/MemoryBase.h> namespace android { // client singleton for binder interface to service -Mutex MediaPlayer::mServiceLock; -sp<IMediaPlayerService> MediaPlayer::mMediaPlayerService; -sp<MediaPlayer::DeathNotifier> MediaPlayer::mDeathNotifier; +Mutex MediaPlayer::sServiceLock; +sp<IMediaPlayerService> MediaPlayer::sMediaPlayerService; +sp<MediaPlayer::DeathNotifier> MediaPlayer::sDeathNotifier; +SortedVector< wp<MediaPlayer> > MediaPlayer::sObitRecipients; // establish binder interface to service const sp<IMediaPlayerService>& MediaPlayer::getMediaPlayerService() { - Mutex::Autolock _l(mServiceLock); - if (mMediaPlayerService.get() == 0) { + Mutex::Autolock _l(sServiceLock); + if (sMediaPlayerService.get() == 0) { sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder; do { @@ -53,14 +54,26 @@ const sp<IMediaPlayerService>& MediaPlayer::getMediaPlayerService() LOGW("MediaPlayerService not published, waiting..."); usleep(500000); // 0.5 s } while(true); - if (mDeathNotifier == NULL) { - mDeathNotifier = new DeathNotifier(); + if (sDeathNotifier == NULL) { + sDeathNotifier = new DeathNotifier(); } - binder->linkToDeath(mDeathNotifier); - mMediaPlayerService = interface_cast<IMediaPlayerService>(binder); + binder->linkToDeath(sDeathNotifier); + sMediaPlayerService = interface_cast<IMediaPlayerService>(binder); } - LOGE_IF(mMediaPlayerService==0, "no MediaPlayerService!?"); - return mMediaPlayerService; + LOGE_IF(sMediaPlayerService==0, "no MediaPlayerService!?"); + return sMediaPlayerService; +} + +void MediaPlayer::addObitRecipient(const wp<MediaPlayer>& recipient) +{ + Mutex::Autolock _l(sServiceLock); + sObitRecipients.add(recipient); +} + +void MediaPlayer::removeObitRecipient(const wp<MediaPlayer>& recipient) +{ + Mutex::Autolock _l(sServiceLock); + sObitRecipients.remove(recipient); } MediaPlayer::MediaPlayer() @@ -77,11 +90,18 @@ MediaPlayer::MediaPlayer() mPrepareStatus = NO_ERROR; mLoop = false; mLeftVolume = mRightVolume = 1.0; + mVideoWidth = mVideoHeight = 0; +} + +void MediaPlayer::onFirstRef() +{ + addObitRecipient(this); } MediaPlayer::~MediaPlayer() { LOGV("destructor"); + removeObitRecipient(this); disconnect(); IPCThreadState::self()->flushCommands(); } @@ -98,7 +118,6 @@ void MediaPlayer::disconnect() if (p != 0) { p->disconnect(); - p->asBinder()->unlinkToDeath(this); } } @@ -108,6 +127,7 @@ void MediaPlayer::clear_l() mDuration = -1; mCurrentPosition = -1; mSeekPosition = -1; + mVideoWidth = mVideoHeight = 0; } status_t MediaPlayer::setListener(const sp<MediaPlayerListener>& listener) @@ -136,7 +156,6 @@ status_t MediaPlayer::setDataSource(const sp<IMediaPlayer>& player) mPlayer = player; if (player != 0) { mCurrentState = MEDIA_PLAYER_INITIALIZED; - player->asBinder()->linkToDeath(this); err = NO_ERROR; } else { LOGE("Unable to to create media player"); @@ -145,8 +164,8 @@ status_t MediaPlayer::setDataSource(const sp<IMediaPlayer>& player) if (p != 0) { p->disconnect(); - p->asBinder()->unlinkToDeath(this); } + return err; } @@ -307,22 +326,18 @@ 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; + if (mPlayer == 0) return INVALID_OPERATION; + *w = mVideoWidth; + return NO_ERROR; } 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; + if (mPlayer == 0) return INVALID_OPERATION; + *h = mVideoHeight; + return NO_ERROR; } status_t MediaPlayer::getCurrentPosition(int *msec) @@ -405,6 +420,7 @@ status_t MediaPlayer::reset() if (mPlayer != 0) { status_t ret = mPlayer->reset(); if (ret != NO_ERROR) { + LOGE("reset() failed with return code (%d)", ret); mCurrentState = MEDIA_PLAYER_STATE_ERROR; ret = UNKNOWN_ERROR; } else { @@ -443,6 +459,16 @@ status_t MediaPlayer::setLooping(int loop) return OK; } +bool MediaPlayer::isLooping() { + LOGV("isLooping"); + Mutex::Autolock _l(mLock); + if (mPlayer != 0) { + return mLoop; + } + LOGV("isLooping: no active player"); + return false; +} + status_t MediaPlayer::setVolume(float leftVolume, float rightVolume) { LOGV("MediaPlayer::setVolume(%f, %f)", leftVolume, rightVolume); @@ -466,6 +492,7 @@ void MediaPlayer::notify(int msg, int ext1, int ext2) mLock.lock(); if (mPlayer == 0) { LOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2); + mLock.unlock(); // release the lock when done. return; } @@ -489,7 +516,8 @@ void MediaPlayer::notify(int msg, int ext1, int ext2) } break; case MEDIA_ERROR: - LOGV("error (%d, %d)", ext1, ext2); + // Always log errors + LOGE("error (%d, %d)", ext1, ext2); mCurrentState = MEDIA_PLAYER_STATE_ERROR; if (mPrepareSync) { @@ -515,6 +543,11 @@ void MediaPlayer::notify(int msg, int ext1, int ext2) case MEDIA_BUFFERING_UPDATE: LOGV("buffering %d", ext1); break; + case MEDIA_SET_VIDEO_SIZE: + LOGV("New video size %d x %d", ext1, ext2); + mVideoWidth = ext1; + mVideoHeight = ext2; + break; default: LOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2); break; @@ -532,32 +565,45 @@ void MediaPlayer::notify(int msg, int ext1, int ext2) } } -void MediaPlayer::binderDied(const wp<IBinder>& who) { - LOGW("IMediaplayer died"); - notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0); -} - void MediaPlayer::DeathNotifier::binderDied(const wp<IBinder>& who) { - Mutex::Autolock _l(MediaPlayer::mServiceLock); - MediaPlayer::mMediaPlayerService.clear(); LOGW("MediaPlayer server died!"); + + // Need to do this with the lock held + SortedVector< wp<MediaPlayer> > list; + { + Mutex::Autolock _l(MediaPlayer::sServiceLock); + MediaPlayer::sMediaPlayerService.clear(); + list = sObitRecipients; + } + + // Notify application when media server dies. + // Don't hold the static lock during callback in case app + // makes a call that needs the lock. + size_t count = list.size(); + for (size_t iter = 0; iter < count; ++iter) { + sp<MediaPlayer> player = list[iter].promote(); + if ((player != 0) && (player->mPlayer != 0)) { + player->notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0); + } + } } MediaPlayer::DeathNotifier::~DeathNotifier() { - Mutex::Autolock _l(mServiceLock); - if (mMediaPlayerService != 0) { - mMediaPlayerService->asBinder()->unlinkToDeath(this); + Mutex::Autolock _l(sServiceLock); + sObitRecipients.clear(); + if (sMediaPlayerService != 0) { + sMediaPlayerService->asBinder()->unlinkToDeath(this); } } -/*static*/ sp<IMemory> MediaPlayer::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) +/*static*/ sp<IMemory> MediaPlayer::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) { LOGV("decode(%s)", url); sp<IMemory> p; const sp<IMediaPlayerService>& service = getMediaPlayerService(); if (service != 0) { - p = mMediaPlayerService->decode(url, pSampleRate, pNumChannels); + p = sMediaPlayerService->decode(url, pSampleRate, pNumChannels, pFormat); } else { LOGE("Unable to locate media service"); } @@ -565,13 +611,13 @@ MediaPlayer::DeathNotifier::~DeathNotifier() } -/*static*/ sp<IMemory> MediaPlayer::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) +/*static*/ sp<IMemory> MediaPlayer::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) { LOGV("decode(%d, %lld, %lld)", fd, offset, length); sp<IMemory> p; const sp<IMediaPlayerService>& service = getMediaPlayerService(); if (service != 0) { - p = mMediaPlayerService->decode(fd, offset, length, pSampleRate, pNumChannels); + p = sMediaPlayerService->decode(fd, offset, length, pSampleRate, pNumChannels, pFormat); } else { LOGE("Unable to locate media service"); } diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp new file mode 100644 index 0000000..825e145 --- /dev/null +++ b/media/libmedia/mediarecorder.cpp @@ -0,0 +1,518 @@ +/* + ** + ** 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 "MediaRecorder" +#include <utils/Log.h> +#include <ui/Surface.h> +#include <media/mediarecorder.h> +#include <utils/IServiceManager.h> +#include <media/IMediaPlayerService.h> +#include <media/IMediaRecorder.h> + +namespace android { + +status_t MediaRecorder::setCamera(const sp<ICamera>& camera) +{ + LOGV("setCamera(%p)", camera.get()); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_IDLE)) { + LOGE("setCamera called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setCamera(camera); + if (OK != ret) { + LOGV("setCamera failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + return ret; +} + +status_t MediaRecorder::setPreviewSurface(const sp<Surface>& surface) +{ + LOGV("setPreviewSurface(%p)", surface.get()); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setPreviewSurface called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setPreviewSurface(surface->getISurface()); + if (OK != ret) { + LOGV("setPreviewSurface failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + return ret; +} + +status_t MediaRecorder::init() +{ + LOGV("init"); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_IDLE)) { + LOGE("init called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->init(); + if (OK != ret) { + LOGV("init failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mCurrentState = MEDIA_RECORDER_INITIALIZED; + return ret; +} + +status_t MediaRecorder::setVideoSource(int vs) +{ + LOGV("setVideoSource(%d)", vs); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mIsVideoSourceSet) { + LOGE("video source has already been set"); + return INVALID_OPERATION; + } + if (mCurrentState & MEDIA_RECORDER_IDLE) { + LOGV("Call init() since the media recorder is not initialized yet"); + status_t ret = init(); + if (OK != ret) { + return ret; + } + } + if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED)) { + LOGE("setVideoSource called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setVideoSource(vs); + if (OK != ret) { + LOGV("setVideoSource failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsVideoSourceSet = true; + return ret; +} + +status_t MediaRecorder::setAudioSource(int as) +{ + LOGV("setAudioSource(%d)", as); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mCurrentState & MEDIA_RECORDER_IDLE) { + LOGV("Call init() since the media recorder is not initialized yet"); + status_t ret = init(); + if (OK != ret) { + return ret; + } + } + if (mIsAudioSourceSet) { + LOGE("audio source has already been set"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED)) { + LOGE("setAudioSource called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setAudioSource(as); + if (OK != ret) { + LOGV("setAudioSource failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsAudioSourceSet = true; + return ret; +} + +status_t MediaRecorder::setOutputFormat(int of) +{ + LOGV("setOutputFormat(%d)", of); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED)) { + LOGE("setOutputFormat called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setOutputFormat(of); + if (OK != ret) { + LOGE("setOutputFormat failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mCurrentState = MEDIA_RECORDER_DATASOURCE_CONFIGURED; + return ret; +} + +status_t MediaRecorder::setVideoEncoder(int ve) +{ + LOGV("setVideoEncoder(%d)", ve); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mIsVideoEncoderSet) { + LOGE("video encoder has already been set"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setVideoEncoder called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setVideoEncoder(ve); + if (OK != ret) { + LOGV("setVideoEncoder failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsVideoEncoderSet = true; + return ret; +} + +status_t MediaRecorder::setAudioEncoder(int ae) +{ + LOGV("setAudioEncoder(%d)", ae); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mIsAudioEncoderSet) { + LOGE("audio encoder has already been set"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setAudioEncoder called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setAudioEncoder(ae); + if (OK != ret) { + LOGV("setAudioEncoder failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsAudioEncoderSet = true; + return ret; +} + +status_t MediaRecorder::setOutputFile(const char* path) +{ + LOGV("setOutputFile(%s)", path); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mIsOutputFileSet) { + LOGE("output file has already been set"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setOutputFile called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setOutputFile(path); + if (OK != ret) { + LOGV("setAudioEncoder failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsOutputFileSet = true; + return ret; +} + +status_t MediaRecorder::setVideoSize(int width, int height) +{ + LOGV("setVideoSize(%d, %d)", width, height); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setVideoSize called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setVideoSize(width, height); + if (OK != ret) { + LOGE("setVideoSize failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + return ret; +} + +status_t MediaRecorder::setVideoFrameRate(int frames_per_second) +{ + LOGV("setVideoFrameRate(%d)", frames_per_second); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setVideoFrameRate called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setVideoFrameRate(frames_per_second); + if (OK != ret) { + LOGE("setVideoFrameRate failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + return ret; +} + +status_t MediaRecorder::prepare() +{ + LOGV("prepare"); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setVideoFrameRate called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->prepare(); + if (OK != ret) { + LOGE("prepare failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mCurrentState = MEDIA_RECORDER_PREPARED; + return ret; +} + +status_t MediaRecorder::getMaxAmplitude(int* max) +{ + LOGV("getMaxAmplitude"); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mCurrentState & MEDIA_RECORDER_ERROR) { + LOGE("setVideoFrameRate called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->getMaxAmplitude(max); + if (OK != ret) { + LOGE("getMaxAmplitude failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + return ret; +} + +status_t MediaRecorder::start() +{ + LOGV("start"); + if (mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_PREPARED)) { + LOGE("start called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->start(); + if (OK != ret) { + LOGE("start failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mCurrentState = MEDIA_RECORDER_RECORDING; + return ret; +} + +status_t MediaRecorder::stop() +{ + LOGV("stop"); + if (mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_RECORDING)) { + LOGE("stop called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->stop(); + if (OK != ret) { + LOGE("stop failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mCurrentState = MEDIA_RECORDER_IDLE; + return ret; +} + +// Reset should be OK in any state +status_t MediaRecorder::reset() +{ + LOGV("reset"); + if (mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + + doCleanUp(); + status_t ret = UNKNOWN_ERROR; + switch(mCurrentState) { + case MEDIA_RECORDER_IDLE: + ret = OK; + break; + + case MEDIA_RECORDER_RECORDING: + case MEDIA_RECORDER_DATASOURCE_CONFIGURED: + case MEDIA_RECORDER_PREPARED: + case MEDIA_RECORDER_ERROR: { + ret = doReset(); + if (OK != ret) { + return ret; // No need to continue + } + } // Intentional fall through + case MEDIA_RECORDER_INITIALIZED: + ret = close(); + break; + + default: { + LOGE("Unexpected non-existing state: %d", mCurrentState); + break; + } + } + return ret; +} + +status_t MediaRecorder::close() +{ + LOGV("close"); + if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED)) { + LOGE("close called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + status_t ret = mMediaRecorder->close(); + if (OK != ret) { + LOGE("close failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } else { + mCurrentState = MEDIA_RECORDER_IDLE; + } + return ret; +} + +status_t MediaRecorder::doReset() +{ + LOGV("doReset"); + status_t ret = mMediaRecorder->reset(); + if (OK != ret) { + LOGE("doReset failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } else { + mCurrentState = MEDIA_RECORDER_INITIALIZED; + } + return ret; +} + +void MediaRecorder::doCleanUp() +{ + LOGV("doCleanUp"); + mIsAudioSourceSet = false; + mIsVideoSourceSet = false; + mIsAudioEncoderSet = false; + mIsVideoEncoderSet = false; + mIsOutputFileSet = false; +} + +// Release should be OK in any state +status_t MediaRecorder::release() +{ + LOGV("release"); + if (mMediaRecorder != NULL) { + return mMediaRecorder->release(); + } + return INVALID_OPERATION; +} + +MediaRecorder::MediaRecorder() +{ + LOGV("constructor"); + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder; + + do { + binder = sm->getService(String16("media.player")); + if (binder != NULL) { + break; + } + LOGW("MediaPlayerService not published, waiting..."); + usleep(500000); // 0.5 s + } while(true); + + sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); + if (service != NULL) { + mMediaRecorder = service->createMediaRecorder(getpid()); + } + + mMediaRecorder = service->createMediaRecorder(getpid()); + if (mMediaRecorder != NULL) { + mCurrentState = MEDIA_RECORDER_IDLE; + } + doCleanUp(); +} + +status_t MediaRecorder::initCheck() +{ + return mMediaRecorder != 0 ? NO_ERROR : NO_INIT; +} + +MediaRecorder::~MediaRecorder() +{ + LOGV("destructor"); + if (mMediaRecorder != NULL) { + mMediaRecorder.clear(); + } +} + +}; // namespace android + diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index b3a5747..f710921 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -7,7 +7,9 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + MediaRecorderClient.cpp \ MediaPlayerService.cpp \ + MetadataRetrieverClient.cpp \ VorbisPlayer.cpp \ MidiFile.cpp @@ -21,6 +23,7 @@ LOCAL_SHARED_LIBRARIES := \ libvorbisidec \ libsonivox \ libopencoreplayer \ + libopencoreauthor \ libmedia \ libandroid_runtime diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index fd5f0ed..5383171 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -34,11 +34,17 @@ #include <utils/IServiceManager.h> #include <utils/MemoryHeapBase.h> #include <utils/MemoryBase.h> +#include <cutils/properties.h> #include <media/MediaPlayerInterface.h> +#include <media/mediarecorder.h> +#include <media/MediaMetadataRetrieverInterface.h> #include <media/AudioTrack.h> +#include "MediaRecorderClient.h" #include "MediaPlayerService.h" +#include "MetadataRetrieverClient.h" + #include "MidiFile.h" #include "VorbisPlayer.h" #include <media/PVPlayer.h> @@ -72,7 +78,7 @@ pid_t gettid() { return syscall(__NR_gettid);} restart continuously. */ #define USE_SIGBUS_HANDLER 0 - + // TODO: Temp hack until we can register players static const char* MIDI_FILE_EXTS[] = { @@ -87,8 +93,10 @@ static const char* MIDI_FILE_EXTS[] = namespace android { -// TODO: should come from audio driver -/* static */ const uint32_t MediaPlayerService::AudioOutput::kDriverLatencyInMsecs = 150; +// TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround +/* static */ const uint32_t MediaPlayerService::AudioOutput::kAudioVideoDelayMs = 96; +/* static */ int MediaPlayerService::AudioOutput::mMinBufferCount = 4; +/* static */ bool MediaPlayerService::AudioOutput::mIsOnEmulator = false; static struct sigaction oldact; static pthread_key_t sigbuskey; @@ -172,7 +180,7 @@ MediaPlayerService::MediaPlayerService() pthread_key_create(&sigbuskey, NULL); - + #if USE_SIGBUS_HANDLER struct sigaction act; memset(&act,0, sizeof act); @@ -191,6 +199,20 @@ MediaPlayerService::~MediaPlayerService() LOGV("MediaPlayerService destroyed"); } +sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(pid_t pid) +{ + sp<MediaRecorderClient> recorder = new MediaRecorderClient(pid); + LOGV("Create new media recorder client from pid %d", pid); + return recorder; +} + +sp<IMediaMetadataRetriever> MediaPlayerService::createMetadataRetriever(pid_t pid) +{ + sp<MetadataRetrieverClient> retriever = new MetadataRetrieverClient(pid); + LOGV("Create new media retriever from pid %d", pid); + return retriever; +} + sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url) { int32_t connId = android_atomic_inc(&mNextConnId); @@ -237,8 +259,8 @@ status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& ar 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); + snprintf(buffer, 255, " msec per frame(%f), channel count(%d), format(%d), frame count(%ld)\n", + mMsecsPerFrame, mChannelCount, mFormat, mFrameCount); result.append(buffer); snprintf(buffer, 255, " sample rate(%d), size(%d), error(%d), command complete(%s)\n", mSampleRate, mSize, mError, mCommandComplete?"true":"false"); @@ -257,8 +279,8 @@ status_t MediaPlayerService::AudioOutput::dump(int fd, const Vector<String16>& a 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); + snprintf(buffer, 255, " msec per frame(%f), latency (%d)\n", + mMsecsPerFrame, mLatency); result.append(buffer); ::write(fd, result.string(), result.size()); if (mTrack != 0) { @@ -315,7 +337,7 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) if (f) { while (!feof(f)) { fgets(buffer, SIZE, f); - if (strstr(buffer, " /sdcard/") || + if (strstr(buffer, " /sdcard/") || strstr(buffer, " /system/sounds/") || strstr(buffer, " /system/media/")) { result.append(" "); @@ -334,7 +356,7 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) if (d) { struct dirent *ent; while((ent = readdir(d)) != NULL) { - if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) { + 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) { @@ -350,7 +372,7 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) } else { linkto[len] = 0; } - if (strstr(linkto, "/sdcard/") == linkto || + if (strstr(linkto, "/sdcard/") == linkto || strstr(linkto, "/system/sounds/") == linkto || strstr(linkto, "/system/media/") == linkto) { result.append(" "); @@ -683,20 +705,6 @@ status_t MediaPlayerService::Client::isPlaying(bool* state) return NO_ERROR; } -status_t MediaPlayerService::Client::getVideoSize(int *w, int *h) -{ - sp<MediaPlayerBase> 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"); @@ -812,7 +820,7 @@ int Antagonizer::callbackThread(void* user) static size_t kDefaultHeapSize = 1024 * 1024; // 1MB -sp<IMemory> MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) +sp<IMemory> MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) { LOGV("decode(%s)", url); sp<MemoryBase> mem; @@ -856,14 +864,15 @@ sp<IMemory> MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, i 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); + *pFormat = cache->format(); + LOGV("return memory @ %p, sampleRate=%u, channelCount = %d, format = %d", mem->pointer(), *pSampleRate, *pNumChannels, *pFormat); Exit: if (player != 0) player->reset(); return mem; } -sp<IMemory> MediaPlayerService::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) +sp<IMemory> MediaPlayerService::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) { LOGV("decode(%d, %lld, %lld)", fd, offset, length); sp<MemoryBase> mem; @@ -898,7 +907,8 @@ sp<IMemory> MediaPlayerService::decode(int fd, int64_t offset, int64_t length, u 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); + *pFormat = cache->format(); + LOGV("return memory @ %p, sampleRate=%u, channelCount = %d, format = %d", mem->pointer(), *pSampleRate, *pNumChannels, *pFormat); Exit: if (player != 0) player->reset(); @@ -916,6 +926,7 @@ MediaPlayerService::AudioOutput::AudioOutput() mRightVolume = 1.0; mLatency = 0; mMsecsPerFrame = 0; + setMinBufferCount(); } MediaPlayerService::AudioOutput::~AudioOutput() @@ -923,10 +934,31 @@ MediaPlayerService::AudioOutput::~AudioOutput() close(); } +void MediaPlayerService::AudioOutput::setMinBufferCount() +{ + char value[PROPERTY_VALUE_MAX]; + if (property_get("ro.kernel.qemu", value, 0)) { + mIsOnEmulator = true; + mMinBufferCount = 12; // to prevent systematic buffer underrun for emulator + } +} + +bool MediaPlayerService::AudioOutput::isOnEmulator() +{ + setMinBufferCount(); + return mIsOnEmulator; +} + +int MediaPlayerService::AudioOutput::getMinBufferCount() +{ + setMinBufferCount(); + return mMinBufferCount; +} + ssize_t MediaPlayerService::AudioOutput::bufferSize() const { if (mTrack == 0) return NO_INIT; - return mTrack->frameCount() * mTrack->channelCount() * sizeof(int16_t); + return mTrack->frameCount() * frameSize(); } ssize_t MediaPlayerService::AudioOutput::frameCount() const @@ -944,7 +976,7 @@ ssize_t MediaPlayerService::AudioOutput::channelCount() const ssize_t MediaPlayerService::AudioOutput::frameSize() const { if (mTrack == 0) return NO_INIT; - return mTrack->channelCount() * sizeof(int16_t); + return mTrack->frameSize(); } uint32_t MediaPlayerService::AudioOutput::latency () const @@ -957,12 +989,29 @@ float MediaPlayerService::AudioOutput::msecsPerFrame() const return mMsecsPerFrame; } -status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelCount, int bufferCount) +status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelCount, int format, int bufferCount) { - LOGV("open(%u, %d, %d)", sampleRate, channelCount, bufferCount); + // Check argument "bufferCount" against the mininum buffer count + if (bufferCount < mMinBufferCount) { + LOGD("bufferCount (%d) is too small and increased to %d", bufferCount, mMinBufferCount); + bufferCount = mMinBufferCount; + + } + LOGV("open(%u, %d, %d, %d)", sampleRate, channelCount, format, bufferCount); if (mTrack) close(); + int afSampleRate; + int afFrameCount; + int frameCount; - AudioTrack *t = new AudioTrack(mStreamType, sampleRate, AudioSystem::PCM_16_BIT, channelCount, bufferCount); + if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { + return NO_INIT; + } + if (AudioSystem::getOutputSamplingRate(&afSampleRate) != NO_ERROR) { + return NO_INIT; + } + + frameCount = (sampleRate*afFrameCount)/afSampleRate; + AudioTrack *t = new AudioTrack(mStreamType, sampleRate, format, channelCount, frameCount*bufferCount); if ((t == 0) || (t->initCheck() != NO_ERROR)) { LOGE("Unable to create audio track"); delete t; @@ -972,7 +1021,7 @@ status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelC LOGV("setVolume"); t->setVolume(mLeftVolume, mRightVolume); mMsecsPerFrame = 1.e3 / (float) sampleRate; - mLatency = (mMsecsPerFrame * bufferCount * t->frameCount()) + kDriverLatencyInMsecs; + mLatency = t->latency() + kAudioVideoDelayMs; mTrack = t; return NO_ERROR; } @@ -1031,7 +1080,7 @@ void MediaPlayerService::AudioOutput::setVolume(float left, float right) #undef LOG_TAG #define LOG_TAG "AudioCache" MediaPlayerService::AudioCache::AudioCache(const char* name) : - mChannelCount(0), mFrameCount(0), mSampleRate(0), mSize(0), + mChannelCount(0), mFrameCount(1024), mSampleRate(0), mSize(0), mError(NO_ERROR), mCommandComplete(false) { // create ashmem heap @@ -1048,12 +1097,13 @@ float MediaPlayerService::AudioCache::msecsPerFrame() const return mMsecsPerFrame; } -status_t MediaPlayerService::AudioCache::open(uint32_t sampleRate, int channelCount, int bufferCount) +status_t MediaPlayerService::AudioCache::open(uint32_t sampleRate, int channelCount, int format, int bufferCount) { - LOGV("open(%u, %d, %d)", sampleRate, channelCount, bufferCount); - if (mHeap->getHeapID() < 0) return NO_INIT; - mSampleRate = sampleRate; - mChannelCount = channelCount; + LOGV("open(%u, %d, %d, %d)", sampleRate, channelCount, format, bufferCount); + if (mHeap->getHeapID() < 0) return NO_INIT; + mSampleRate = sampleRate; + mChannelCount = (uint16_t)channelCount; + mFormat = (uint16_t)format; mMsecsPerFrame = 1.e3 / (float) sampleRate; return NO_ERROR; } @@ -1067,6 +1117,10 @@ ssize_t MediaPlayerService::AudioCache::write(const void* buffer, size_t size) if (p == NULL) return NO_INIT; p += mSize; LOGV("memcpy(%p, %p, %u)", p, buffer, size); + if (mSize + size > mHeap->getSize()) { + LOGE("Heap size overflow! req size: %d, max size: %d", (mSize + size), mHeap->getSize()); + size = mHeap->getSize() - mSize; + } memcpy(p, buffer, size); mSize += size; return size; diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index c2007cb..f138886 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -25,10 +25,11 @@ #include <media/IMediaPlayerService.h> #include <media/MediaPlayerInterface.h> -class SkBitmap; - namespace android { +class IMediaRecorder; +class IMediaMetadataRetriever; + #define CALLBACK_ANTAGONIZER 0 #if CALLBACK_ANTAGONIZER class Antagonizer { @@ -68,7 +69,7 @@ class MediaPlayerService : public BnMediaPlayerService 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 status_t open(uint32_t sampleRate, int channelCount, int format, int bufferCount=4); virtual void start(); virtual ssize_t write(const void* buffer, size_t size); virtual void stop(); @@ -78,14 +79,24 @@ class MediaPlayerService : public BnMediaPlayerService void setAudioStreamType(int streamType) { mStreamType = streamType; } void setVolume(float left, float right); virtual status_t dump(int fd, const Vector<String16>& args) const; + + static bool isOnEmulator(); + static int getMinBufferCount(); private: + static void setMinBufferCount(); + AudioTrack* mTrack; int mStreamType; float mLeftVolume; float mRightVolume; float mMsecsPerFrame; uint32_t mLatency; - static const uint32_t kDriverLatencyInMsecs; + + // TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround + static const uint32_t kAudioVideoDelayMs; + static bool mIsOnEmulator; + static int mMinBufferCount; // 12 for emulator; otherwise 4 + }; class AudioCache : public MediaPlayerBase::AudioSink @@ -96,13 +107,13 @@ class MediaPlayerService : public BnMediaPlayerService 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 bufferSize() const { return frameSize() * mFrameCount; } 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 ssize_t channelCount() const { return (ssize_t)mChannelCount; } + virtual ssize_t frameSize() const { return ssize_t(mChannelCount * ((mFormat == AudioSystem::PCM_16_BIT)?sizeof(int16_t):sizeof(u_int8_t))); } virtual uint32_t latency() const; virtual float msecsPerFrame() const; - virtual status_t open(uint32_t sampleRate, int channelCount, int bufferCount=1); + virtual status_t open(uint32_t sampleRate, int channelCount, int format, int bufferCount=1); virtual void start() {} virtual ssize_t write(const void* buffer, size_t size); virtual void stop() {} @@ -112,6 +123,7 @@ class MediaPlayerService : public BnMediaPlayerService void setAudioStreamType(int streamType) {} void setVolume(float left, float right) {} uint32_t sampleRate() const { return mSampleRate; } + uint32_t format() const { return (uint32_t)mFormat; } size_t size() const { return mSize; } status_t wait(); @@ -127,7 +139,8 @@ class MediaPlayerService : public BnMediaPlayerService Condition mSignal; sp<MemoryHeapBase> mHeap; float mMsecsPerFrame; - ssize_t mChannelCount; + uint16_t mChannelCount; + uint16_t mFormat; ssize_t mFrameCount; uint32_t mSampleRate; uint32_t mSize; @@ -139,10 +152,14 @@ public: static void instantiate(); // IMediaPlayerService interface + virtual sp<IMediaRecorder> createMediaRecorder(pid_t pid); + virtual sp<IMediaMetadataRetriever> createMetadataRetriever(pid_t pid); + + // House keeping for media player clients virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url); virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length); - virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels); - virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels); + virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); + virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); virtual status_t dump(int fd, const Vector<String16>& args); @@ -160,7 +177,6 @@ private: 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); diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp new file mode 100644 index 0000000..f326a0e --- /dev/null +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -0,0 +1,251 @@ +/* + ** Copyright 2008, HTC Inc. + ** + ** 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 "MediaRecorderService" +#include <utils/Log.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <string.h> +#include <cutils/atomic.h> +#include <android_runtime/ActivityManager.h> +#include <utils/IPCThreadState.h> +#include <utils/IServiceManager.h> +#include <utils/MemoryHeapBase.h> +#include <utils/MemoryBase.h> +#include <media/PVMediaRecorder.h> + +#include "MediaRecorderClient.h" + +namespace android { + +status_t MediaRecorderClient::setCamera(const sp<ICamera>& camera) +{ + LOGV("setCamera"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setCamera(camera); +} + +status_t MediaRecorderClient::setPreviewSurface(const sp<ISurface>& surface) +{ + LOGV("setPreviewSurface"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setPreviewSurface(surface); +} + +status_t MediaRecorderClient::setVideoSource(int vs) +{ + LOGV("setVideoSource(%d)", vs); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + } + return mRecorder->setVideoSource((video_source)vs); +} + +status_t MediaRecorderClient::setAudioSource(int as) +{ + LOGV("setAudioSource(%d)", as); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + } + return mRecorder->setAudioSource((audio_source)as); +} + +status_t MediaRecorderClient::setOutputFormat(int of) +{ + LOGV("setOutputFormat(%d)", of); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setOutputFormat((output_format)of); +} + +status_t MediaRecorderClient::setVideoEncoder(int ve) +{ + LOGV("setVideoEncoder(%d)", ve); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setVideoEncoder((video_encoder)ve); +} + +status_t MediaRecorderClient::setAudioEncoder(int ae) +{ + LOGV("setAudioEncoder(%d)", ae); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setAudioEncoder((audio_encoder)ae); +} + +status_t MediaRecorderClient::setOutputFile(const char* path) +{ + LOGV("setOutputFile(%s)", path); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setOutputFile(path); +} + +status_t MediaRecorderClient::setVideoSize(int width, int height) +{ + LOGV("setVideoSize(%dx%d)", width, height); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setVideoSize(width, height); +} + +status_t MediaRecorderClient::setVideoFrameRate(int frames_per_second) +{ + LOGV("setVideoFrameRate(%d)", frames_per_second); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setVideoFrameRate(frames_per_second); +} + +status_t MediaRecorderClient::prepare() +{ + LOGV("prepare"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->prepare(); +} + + +status_t MediaRecorderClient::getMaxAmplitude(int* max) +{ + LOGV("getMaxAmplitude"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->getMaxAmplitude(max); +} + +status_t MediaRecorderClient::start() +{ + LOGV("start"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->start(); + +} + +status_t MediaRecorderClient::stop() +{ + LOGV("stop"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->stop(); +} + +status_t MediaRecorderClient::init() +{ + LOGV("init"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->init(); +} + +status_t MediaRecorderClient::close() +{ + LOGV("close"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->close(); +} + + +status_t MediaRecorderClient::reset() +{ + LOGV("reset"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->reset(); +} + +status_t MediaRecorderClient::release() +{ + LOGV("release"); + Mutex::Autolock lock(mLock); + if (mRecorder != NULL) { + delete mRecorder; + mRecorder = NULL; + } + return NO_ERROR; +} + +MediaRecorderClient::MediaRecorderClient(pid_t pid) +{ + LOGV("Client constructor"); + mPid = pid; + mRecorder = new PVMediaRecorder(); +} + +MediaRecorderClient::~MediaRecorderClient() +{ + LOGV("Client destructor"); + release(); +} + +}; // namespace android + diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h new file mode 100644 index 0000000..3158017 --- /dev/null +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -0,0 +1,64 @@ +/* + ** + ** Copyright 2008, HTC Inc. + ** + ** 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_MEDIARECORDERCLIENT_H +#define ANDROID_MEDIARECORDERCLIENT_H + +#include <media/IMediaRecorder.h> + +namespace android { + +class PVMediaRecorder; +class ISurface; + +class MediaRecorderClient : public BnMediaRecorder +{ +public: + virtual status_t setCamera(const sp<ICamera>& camera); + virtual status_t setPreviewSurface(const sp<ISurface>& surface); + virtual status_t setVideoSource(int vs); + virtual status_t setAudioSource(int as); + virtual status_t setOutputFormat(int of); + virtual status_t setVideoEncoder(int ve); + virtual status_t setAudioEncoder(int ae); + virtual status_t setOutputFile(const char* path); + virtual status_t setVideoSize(int width, int height); + virtual status_t setVideoFrameRate(int frames_per_second); + virtual status_t prepare(); + virtual status_t getMaxAmplitude(int* max); + virtual status_t start(); + virtual status_t stop(); + virtual status_t reset(); + virtual status_t init(); + virtual status_t close(); + virtual status_t release(); + +private: + friend class MediaPlayerService; // for accessing private constructor + + MediaRecorderClient(pid_t pid); + virtual ~MediaRecorderClient(); + + pid_t mPid; + Mutex mLock; + PVMediaRecorder *mRecorder; +}; + +}; // namespace android + +#endif // ANDROID_MEDIARECORDERCLIENT_H + diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp new file mode 100644 index 0000000..a320bd5 --- /dev/null +++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp @@ -0,0 +1,250 @@ +/* +** +** 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 "MetadataRetrieverClient" +#include <utils/Log.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> + +#include <string.h> +#include <cutils/atomic.h> +#include <utils/MemoryDealer.h> +#include <android_runtime/ActivityManager.h> +#include <utils/IPCThreadState.h> +#include <utils/IServiceManager.h> +#include <media/MediaMetadataRetrieverInterface.h> +#include <media/MediaPlayerInterface.h> +#include <media/PVMetadataRetriever.h> +#include <private/media/VideoFrame.h> + +#include "MetadataRetrieverClient.h" + + +namespace android { + +MetadataRetrieverClient::MetadataRetrieverClient(pid_t pid) +{ + LOGV("MetadataRetrieverClient constructor pid(%d)", pid); + mPid = pid; + mThumbnailDealer = NULL; + mAlbumArtDealer = NULL; + mThumbnail = NULL; + mAlbumArt = NULL; + + mRetriever = new PVMetadataRetriever(); + if (mRetriever == NULL) { + LOGE("failed to initialize the retriever"); + } +} + +MetadataRetrieverClient::~MetadataRetrieverClient() +{ + LOGV("MetadataRetrieverClient destructor"); + disconnect(); +} + +status_t MetadataRetrieverClient::dump(int fd, const Vector<String16>& args) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + result.append(" MetadataRetrieverClient\n"); + snprintf(buffer, 255, " pid(%d)\n", mPid); + result.append(buffer); + write(fd, result.string(), result.size()); + write(fd, "\n", 1); + return NO_ERROR; +} + +void MetadataRetrieverClient::disconnect() +{ + LOGV("disconnect from pid %d", mPid); + Mutex::Autolock lock(mLock); + mRetriever.clear(); + mThumbnailDealer.clear(); + mAlbumArtDealer.clear(); + mThumbnail.clear(); + mAlbumArt.clear(); + IPCThreadState::self()->flushCommands(); +} + +status_t MetadataRetrieverClient::setDataSource(const char *url) +{ + LOGV("setDataSource(%s)", url); + Mutex::Autolock lock(mLock); + if (url == NULL) { + return UNKNOWN_ERROR; + } + if (mRetriever == NULL) { + LOGE("retriever is not initialized"); + return NO_INIT; + } + return mRetriever->setDataSource(url); +} + +status_t MetadataRetrieverClient::setDataSource(int fd, int64_t offset, int64_t length) +{ + LOGV("setDataSource fd=%d, offset=%lld, length=%lld", fd, offset, length); + Mutex::Autolock lock(mLock); + if (mRetriever == NULL) { + LOGE("retriever is not initialized"); + ::close(fd); + return NO_INIT; + } + + 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 (%lld) bigger than file size (%llu)", offset, sb.st_size); + ::close(fd); + return UNKNOWN_ERROR; + } + if (offset + length > sb.st_size) { + length = sb.st_size - offset; + LOGE("calculated length = %lld", length); + } + status_t status = mRetriever->setDataSource(fd, offset, length); + ::close(fd); + return status; +} + +status_t MetadataRetrieverClient::setMode(int mode) +{ + LOGV("setMode"); + Mutex::Autolock lock(mLock); + if (mRetriever == NULL) { + LOGE("retriever is not initialized"); + return NO_INIT; + } + return mRetriever->setMode(mode); +} + +status_t MetadataRetrieverClient::getMode(int* mode) const +{ + LOGV("getMode"); + Mutex::Autolock lock(mLock); + if (mRetriever == NULL) { + LOGE("retriever is not initialized"); + return NO_INIT; + } + return mRetriever->getMode(mode); +} + +sp<IMemory> MetadataRetrieverClient::captureFrame() +{ + LOGV("captureFrame"); + Mutex::Autolock lock(mLock); + mThumbnail.clear(); + mThumbnailDealer.clear(); + if (mRetriever == NULL) { + LOGE("retriever is not initialized"); + return NULL; + } + VideoFrame *frame = mRetriever->captureFrame(); + if (frame == NULL) { + LOGE("failed to capture a video frame"); + return NULL; + } + size_t size = sizeof(VideoFrame) + frame->mSize; + mThumbnailDealer = new MemoryDealer(size); + if (mThumbnailDealer == NULL) { + LOGE("failed to create MemoryDealer"); + delete frame; + return NULL; + } + mThumbnail = mThumbnailDealer->allocate(size); + if (mThumbnail == NULL) { + LOGE("not enough memory for VideoFrame size=%u", size); + mThumbnailDealer.clear(); + delete frame; + return NULL; + } + VideoFrame *frameCopy = static_cast<VideoFrame *>(mThumbnail->pointer()); + frameCopy->mWidth = frame->mWidth; + frameCopy->mHeight = frame->mHeight; + frameCopy->mDisplayWidth = frame->mDisplayWidth; + frameCopy->mDisplayHeight = frame->mDisplayHeight; + frameCopy->mSize = frame->mSize; + frameCopy->mData = (uint8_t *)frameCopy + sizeof(VideoFrame); + memcpy(frameCopy->mData, frame->mData, frame->mSize); + delete frame; // Fix memory leakage + return mThumbnail; +} + +sp<IMemory> MetadataRetrieverClient::extractAlbumArt() +{ + LOGV("extractAlbumArt"); + Mutex::Autolock lock(mLock); + mAlbumArt.clear(); + mAlbumArtDealer.clear(); + if (mRetriever == NULL) { + LOGE("retriever is not initialized"); + return NULL; + } + MediaAlbumArt *albumArt = mRetriever->extractAlbumArt(); + if (albumArt == NULL) { + LOGE("failed to extract an album art"); + return NULL; + } + size_t size = sizeof(MediaAlbumArt) + albumArt->mSize; + mAlbumArtDealer = new MemoryDealer(size); + if (mAlbumArtDealer == NULL) { + LOGE("failed to create MemoryDealer object"); + delete albumArt; + return NULL; + } + mAlbumArt = mAlbumArtDealer->allocate(size); + if (mAlbumArt == NULL) { + LOGE("not enough memory for MediaAlbumArt size=%u", size); + mAlbumArtDealer.clear(); + delete albumArt; + return NULL; + } + MediaAlbumArt *albumArtCopy = static_cast<MediaAlbumArt *>(mAlbumArt->pointer()); + albumArtCopy->mSize = albumArt->mSize; + albumArtCopy->mData = (uint8_t *)albumArtCopy + sizeof(MediaAlbumArt); + memcpy(albumArtCopy->mData, albumArt->mData, albumArt->mSize); + delete albumArt; // Fix memory leakage + return mAlbumArt; +} + +const char* MetadataRetrieverClient::extractMetadata(int keyCode) +{ + LOGV("extractMetadata"); + Mutex::Autolock lock(mLock); + if (mRetriever == NULL) { + LOGE("retriever is not initialized"); + return NULL; + } + return mRetriever->extractMetadata(keyCode); +} + +}; // namespace android diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h new file mode 100644 index 0000000..ce29c98 --- /dev/null +++ b/media/libmediaplayerservice/MetadataRetrieverClient.h @@ -0,0 +1,71 @@ +/* +** +** 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_MEDIAMETADATARETRIEVERSERVICE_H +#define ANDROID_MEDIAMETADATARETRIEVERSERVICE_H + +#include <utils.h> +#include <utils/KeyedVector.h> +#include <utils/IMemory.h> + +#include <media/MediaMetadataRetrieverInterface.h> + + +namespace android { + +class IMediaPlayerService; +class MemoryDealer; + +class MetadataRetrieverClient : public BnMediaMetadataRetriever +{ +public: + MetadataRetrieverClient(const sp<IMediaPlayerService>& service, pid_t pid, int32_t connId); + + // Implements IMediaMetadataRetriever interface + // These methods are called in IMediaMetadataRetriever.cpp? + virtual void disconnect(); + virtual status_t setDataSource(const char *url); + virtual status_t setDataSource(int fd, int64_t offset, int64_t length); + virtual status_t setMode(int mode); + virtual status_t getMode(int* mode) const; + virtual sp<IMemory> captureFrame(); + virtual sp<IMemory> extractAlbumArt(); + virtual const char* extractMetadata(int keyCode); + + virtual status_t dump(int fd, const Vector<String16>& args) const; + +private: + friend class MediaPlayerService; + + explicit MetadataRetrieverClient(pid_t pid); + virtual ~MetadataRetrieverClient(); + + mutable Mutex mLock; + sp<MediaMetadataRetrieverBase> mRetriever; + pid_t mPid; + + // Keep the shared memory copy of album art and capture frame (for thumbnail) + sp<MemoryDealer> mAlbumArtDealer; + sp<MemoryDealer> mThumbnailDealer; + sp<IMemory> mAlbumArt; + sp<IMemory> mThumbnail; +}; + +}; // namespace android + +#endif // ANDROID_MEDIAMETADATARETRIEVERSERVICE_H + diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp index 538f7d4..cfad66c 100644 --- a/media/libmediaplayerservice/MidiFile.cpp +++ b/media/libmediaplayerservice/MidiFile.cpp @@ -431,7 +431,7 @@ status_t MidiFile::setLooping(int loop) } status_t MidiFile::createOutputTrack() { - if (mAudioSink->open(pLibConfig->sampleRate,pLibConfig->numChannels, 2) != NO_ERROR) { + if (mAudioSink->open(pLibConfig->sampleRate, pLibConfig->numChannels, AudioSystem::PCM_16_BIT, 2) != NO_ERROR) { LOGE("mAudioSink open failed"); return ERROR_OPEN_FAILED; } diff --git a/media/libmediaplayerservice/VorbisPlayer.cpp b/media/libmediaplayerservice/VorbisPlayer.cpp index a0e0f39..9a64403 100644 --- a/media/libmediaplayerservice/VorbisPlayer.cpp +++ b/media/libmediaplayerservice/VorbisPlayer.cpp @@ -385,7 +385,7 @@ status_t VorbisPlayer::createOutputTrack() { 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) { + if (mAudioSink->open(vi->rate, vi->channels, AudioSystem::PCM_16_BIT, DEFAULT_AUDIOSINK_BUFFERCOUNT) != NO_ERROR) { LOGE("mAudioSink open failed"); return ERROR_OPEN_FAILED; } |