diff options
49 files changed, 7072 insertions, 175 deletions
diff --git a/include/media/AudioParameter.h b/include/media/AudioParameter.h index 891bc4b..d29c699 100644 --- a/include/media/AudioParameter.h +++ b/include/media/AudioParameter.h @@ -1,5 +1,6 @@  /*   * Copyright (C) 2008-2011 The Android Open Source Project + * Copyright (c) 2012, The Linux Foundation. All rights reserved.   *   * Licensed under the Apache License, Version 2.0 (the "License");   * you may not use this file except in compliance with the License. @@ -48,6 +49,13 @@ public:      static const char * const keyFrameCount;      static const char * const keyInputSource;      static const char * const keyScreenState; +#ifdef QCOM_HARDWARE +    static const char * const keyHandleFm; +    static const char * const keyVoipCheck; +    static const char * const keyFluenceType; +    static const char * const keySSR; +    static const char * const keyHandleA2dpDevice; +#endif      String8 toString(); diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index 49e1afc..e66fc3a 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -1,4 +1,8 @@  /* + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only. + *   * Copyright (C) 2008 The Android Open Source Project   *   * Licensed under the Apache License, Version 2.0 (the "License"); @@ -144,6 +148,9 @@ public:          INPUT_CLOSED,          INPUT_CONFIG_CHANGED,          STREAM_CONFIG_CHANGED, +#ifdef QCOM_HARDWARE +        EFFECT_CONFIG_CHANGED, +#endif          NUM_CONFIG_EVENTS      }; diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index 34108b3..77a0b26 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -31,6 +31,10 @@  #include <cutils/sched_policy.h>  #include <utils/threads.h> +#ifdef QCOM_HARDWARE +#include <media/IDirectTrackClient.h> +#endif +  namespace android {  // ---------------------------------------------------------------------------- @@ -39,7 +43,11 @@ class audio_track_cblk_t;  // ---------------------------------------------------------------------------- -class AudioTrack : virtual public RefBase +class AudioTrack : +#ifdef QCOM_HARDWARE +                   public BnDirectTrackClient, +#endif +                   virtual public RefBase  {  public:      enum channel_index { @@ -451,6 +459,11 @@ public:       */              status_t dump(int fd, const Vector<String16>& args) const; +#ifdef QCOM_HARDWARE +            virtual void notify(int msg); +            virtual status_t getTimeStamp(uint64_t *tstamp); +#endif +  protected:      /* copying audio tracks is not allowed */                          AudioTrack(const AudioTrack& other); @@ -496,6 +509,9 @@ protected:              status_t restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart);              bool stopped_l() const { return !mActive; } +#ifdef QCOM_HARDWARE +    sp<IDirectTrack>        mDirectTrack; +#endif      sp<IAudioTrack>         mAudioTrack;      sp<IMemory>             mCblkMemory;      sp<AudioTrackThread>    mAudioTrackThread; @@ -529,10 +545,17 @@ protected:      uint32_t                mUpdatePeriod;      bool                    mFlushed; // FIXME will be made obsolete by making flush() synchronous      audio_output_flags_t    mFlags; +#ifdef QCOM_HARDWARE +    sp<IAudioFlinger>       mAudioFlinger; +    audio_io_handle_t       mAudioDirectOutput; +#endif      int                     mSessionId;      int                     mAuxEffectId;      mutable Mutex           mLock;      status_t                mRestoreStatus; +#ifdef QCOM_HARDWARE +    void*                   mObserver; +#endif      bool                    mIsTimed;      int                     mPreviousPriority;          // before start()      SchedPolicy             mPreviousSchedulingGroup; diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h index 5170a87..c895c13 100644 --- a/include/media/IAudioFlinger.h +++ b/include/media/IAudioFlinger.h @@ -1,4 +1,8 @@  /* + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only. + *   * Copyright (C) 2007 The Android Open Source Project   *   * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,6 +28,10 @@  #include <utils/RefBase.h>  #include <utils/Errors.h>  #include <binder/IInterface.h> +#ifdef QCOM_HARDWARE +#include <media/IDirectTrack.h> +#include <media/IDirectTrackClient.h> +#endif  #include <media/IAudioTrack.h>  #include <media/IAudioRecord.h>  #include <media/IAudioFlingerClient.h> @@ -69,6 +77,21 @@ public:                                  int *sessionId,                                  status_t *status) = 0; +#ifdef QCOM_HARDWARE +    /* create a direct audio track and registers it with AudioFlinger. +     * return null if the track cannot be created. +     */ +    virtual sp<IDirectTrack> createDirectTrack( +                                pid_t pid, +                                uint32_t sampleRate, +                                audio_channel_mask_t channelMask, +                                audio_io_handle_t output, +                                int *sessionId, +                                IDirectTrackClient* client, +                                audio_stream_type_t streamType, +                                status_t *status) = 0; +#endif +      virtual sp<IAudioRecord> openRecord(                                  pid_t pid,                                  audio_io_handle_t input, diff --git a/include/media/IDirectTrack.h b/include/media/IDirectTrack.h new file mode 100644 index 0000000..c1f4f09 --- /dev/null +++ b/include/media/IDirectTrack.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only. + * + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_IDIRECTTRACK_H +#define ANDROID_IDIRECTTRACK_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/RefBase.h> +#include <utils/Errors.h> +#include <binder/IInterface.h> +#include <binder/IMemory.h> + + +namespace android { + +// ---------------------------------------------------------------------------- + +class IDirectTrack : public IInterface +{ +public: +    DECLARE_META_INTERFACE(DirectTrack); + +    /* After it's created the track is not active. Call start() to +     * make it active. If set, the callback will start being called. +     */ +    virtual status_t    start() = 0; + +    /* Stop a track. If set, the callback will cease being called and +     * obtainBuffer will return an error. Buffers that are already released +     * will be processed, unless flush() is called. +     */ +    virtual void        stop() = 0; + +    /* flush a stopped track. All pending buffers are discarded. +     * This function has no effect if the track is not stoped. +     */ +    virtual void        flush() = 0; + +    /* mute or unmutes this track. +     * While mutted, the callback, if set, is still called. +     */ +    virtual void        mute(bool) = 0; + +    /* Pause a track. If set, the callback will cease being called and +     * obtainBuffer will return an error. Buffers that are already released +     * will be processed, unless flush() is called. +     */ +    virtual void        pause() = 0; + +    /* set volume for both left and right channels. +     */ +    virtual void        setVolume(float l, float r) = 0; + +    virtual ssize_t     write(const void*, size_t) =  0; + +    virtual int64_t     getTimeStamp() =  0; +}; + +// ---------------------------------------------------------------------------- + +class BnDirectTrack : public BnInterface<IDirectTrack> +{ +public: +    virtual status_t    onTransact( uint32_t code, +                                    const Parcel& data, +                                    Parcel* reply, +                                    uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IAUDIOTRACK_H diff --git a/include/media/IDirectTrackClient.h b/include/media/IDirectTrackClient.h new file mode 100644 index 0000000..9383690 --- /dev/null +++ b/include/media/IDirectTrackClient.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only. + * + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_IDIRECTTRACKCLIENT_H +#define ANDROID_IDIRECTTRACKCLIENT_H + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +namespace android { + +class IDirectTrackClient: public IInterface +{ +public: +    DECLARE_META_INTERFACE(DirectTrackClient); + +    virtual void notify(int msg) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnDirectTrackClient: public BnInterface<IDirectTrackClient> +{ +public: +    virtual status_t    onTransact( uint32_t code, +                                    const Parcel& data, +                                    Parcel* reply, +                                    uint32_t flags = 0); +}; + +}; // namespace android + +#endif // ANDROID_IDIRECTTRACKCLIENT_H diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h index b7bee3f..a7570d6 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -110,6 +110,10 @@ public:          virtual status_t    setPlaybackRatePermille(int32_t rate) { return INVALID_OPERATION; }          virtual bool        needsTrailingPadding() { return true; } +#ifdef QCOM_HARDWARE +        virtual ssize_t     sampleRate() const {return 0;}; +        virtual status_t    getTimeStamp(uint64_t *tstamp) {return 0;}; +#endif      };                          MediaPlayerBase() : mCookie(0), mNotify(0) {} diff --git a/include/media/MediaProfiles.h b/include/media/MediaProfiles.h index 60d6bef..0df9fd4 100644 --- a/include/media/MediaProfiles.h +++ b/include/media/MediaProfiles.h @@ -1,6 +1,7 @@  /*   **   ** Copyright 2010, The Android Open Source Project. + ** Copyright (c) 2010 - 2012, The Linux Foundation. All rights reserved.   **   ** Licensed under the Apache License, Version 2.0 (the "License");   ** you may not use this file except in compliance with the License. @@ -461,6 +462,10 @@ private:      static VideoEncoderCap* createDefaultH263VideoEncoderCap();      static VideoEncoderCap* createDefaultM4vVideoEncoderCap();      static AudioEncoderCap* createDefaultAmrNBEncoderCap(); +#ifdef QCOM_HARDWARE +    static AudioEncoderCap* createDefaultAacEncoderCap(); +    static AudioEncoderCap* createDefaultLpcmEncoderCap(); +#endif      static int findTagForName(const NameToTagMap *map, size_t nMappings, const char *name); diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h index 3c2e700..6dfa5d9 100644 --- a/include/media/mediarecorder.h +++ b/include/media/mediarecorder.h @@ -1,5 +1,6 @@  /*   ** Copyright (C) 2008 The Android Open Source Project + ** Copyright (c) 2010 - 2012, The Linux Foundation. All rights reserved.   **   ** Licensed under the Apache License, Version 2.0 (the "License");   ** you may not use this file except in compliance with the License. @@ -67,6 +68,12 @@ enum output_format {      /* H.264/AAC data encapsulated in MPEG2/TS */      OUTPUT_FORMAT_MPEG2TS = 8, +#ifdef QCOM_HARDWARE +    OUTPUT_FORMAT_QCP = 9, // QCP file format +    OUTPUT_FORMAT_THREE_GPP2 = 10, /*3GPP2*/ +    OUTPUT_FORMAT_WAVE = 11, /*WAVE*/ +#endif +      OUTPUT_FORMAT_LIST_END // must be last - used to validate format type  }; @@ -77,6 +84,11 @@ enum audio_encoder {      AUDIO_ENCODER_AAC = 3,      AUDIO_ENCODER_HE_AAC = 4,      AUDIO_ENCODER_AAC_ELD = 5, +#ifdef QCOM_HARDWARE +    AUDIO_ENCODER_EVRC = 6, +    AUDIO_ENCODER_QCELP = 7, +    AUDIO_ENCODER_LPCM = 8, +#endif      AUDIO_ENCODER_LIST_END // must be the last - used to validate the audio encoder type  }; diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h index 1dc408f..624fe3e 100644 --- a/include/media/stagefright/AudioPlayer.h +++ b/include/media/stagefright/AudioPlayer.h @@ -43,27 +43,27 @@ public:      virtual ~AudioPlayer();      // Caller retains ownership of "source". -    void setSource(const sp<MediaSource> &source); +    virtual void setSource(const sp<MediaSource> &source);      // Return time in us.      virtual int64_t getRealTimeUs(); -    status_t start(bool sourceAlreadyStarted = false); +    virtual status_t start(bool sourceAlreadyStarted = false); -    void pause(bool playPendingSamples = false); -    void resume(); +    virtual void pause(bool playPendingSamples = false); +    virtual void resume();      // Returns the timestamp of the last buffer played (in us). -    int64_t getMediaTimeUs(); +    virtual int64_t getMediaTimeUs();      // Returns true iff a mapping is established, i.e. the AudioPlayer      // has played at least one frame of audio. -    bool getMediaTimeMapping(int64_t *realtime_us, int64_t *mediatime_us); +    virtual bool getMediaTimeMapping(int64_t *realtime_us, int64_t *mediatime_us); -    status_t seekTo(int64_t time_us); +    virtual status_t seekTo(int64_t time_us); -    bool isSeeking(); -    bool reachedEOS(status_t *finalStatus); +    virtual bool isSeeking(); +    virtual bool reachedEOS(status_t *finalStatus);      status_t setPlaybackRatePermille(int32_t ratePermille); @@ -91,6 +91,9 @@ private:      int64_t mSeekTimeUs;      bool mStarted; +#ifdef QCOM_HARDWARE +    bool mSourcePaused; +#endif      bool mIsFirstBuffer;      status_t mFirstBufferResult; diff --git a/include/media/stagefright/ExtendedWriter.h b/include/media/stagefright/ExtendedWriter.h new file mode 100644 index 0000000..23944b0 --- /dev/null +++ b/include/media/stagefright/ExtendedWriter.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only. + * + * 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 EXTENDED_WRITER_H_ + +#define EXTENDED_WRITER_H_ + +#include <stdio.h> + +#include <media/stagefright/MediaWriter.h> +#include <utils/threads.h> +#include <cutils/log.h> + +#define LITERAL_TO_STRING_INTERNAL(x)    #x +#define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x) + +#define CHECK_EQ(x,y)                                                   \ +    LOG_ALWAYS_FATAL_IF(                                                \ +            (x) != (y),                                                 \ +            __FILE__ ":" LITERAL_TO_STRING(__LINE__) " " #x " != " #y) + +#define CHECK(x)                                                        \ +    LOG_ALWAYS_FATAL_IF(                                                \ +            !(x),                                                       \ +            __FILE__ ":" LITERAL_TO_STRING(__LINE__) " " #x) + +namespace android { + +struct MediaSource; +struct MetaData; + +struct ExtendedWriter : public MediaWriter { +    ExtendedWriter(const char *filename); +    ExtendedWriter(int fd); + +    status_t initCheck() const; + +    virtual status_t addSource(const sp<MediaSource> &source); +    virtual bool reachedEOS(); +    virtual status_t start(MetaData *params = NULL); +    virtual status_t stop(); +    virtual status_t pause(); + +protected: +    virtual ~ExtendedWriter(); + +private: +    FILE *mFile; +    status_t mInitCheck; +    sp<MediaSource> mSource; +    bool mStarted; +    volatile bool mPaused; +    volatile bool mResumed; +    volatile bool mDone; +    volatile bool mReachedEOS; +    pthread_t mThread; +    int64_t mEstimatedSizeBytes; +    int64_t mEstimatedDurationUs; + +    int32_t mFormat; + +    //QCP/EVRC header +    struct QCPEVRCHeader +    { +        /* RIFF Section */ +        char riff[4]; +        unsigned int s_riff; +        char qlcm[4]; + +        /* Format chunk */ +        char fmt[4]; +        unsigned int s_fmt; +        char mjr; +        char mnr; +        unsigned int data1; + +        /* UNIQUE ID of the codec */ +        unsigned short data2; +        unsigned short data3; +        char data4[8]; +        unsigned short ver; + +        /* Codec Info */ +        char name[80]; +        unsigned short abps; + +        /* average bits per sec of the codec */ +        unsigned short bytes_per_pkt; +        unsigned short samp_per_block; +        unsigned short samp_per_sec; +        unsigned short bits_per_samp; +        unsigned char vr_num_of_rates; + +        /* Rate Header fmt info */ +        unsigned char rvd1[3]; +        unsigned short vr_bytes_per_pkt[8]; +        unsigned int rvd2[5]; + +        /* Vrat chunk */ +        unsigned char vrat[4]; +        unsigned int s_vrat; +        unsigned int v_rate; +        unsigned int size_in_pkts; + +        /* Data chunk */ +        unsigned char data[4]; +        unsigned int s_data; +    } __attribute__ ((packed)); + +    struct QCPEVRCHeader mHeader; +    off_t mOffset; //note off_t + +    static void *ThreadWrapper(void *); +    status_t threadFunc(); +    bool exceedsFileSizeLimit(); +    bool exceedsFileDurationLimit(); + +    ExtendedWriter(const ExtendedWriter &); +    ExtendedWriter &operator=(const ExtendedWriter &); + +    status_t writeQCPHeader( ); +    status_t writeEVRCHeader( ); +}; + +}  // namespace android + +#endif  // AMR_WRITER_H_ diff --git a/include/media/stagefright/LPAPlayer.h b/include/media/stagefright/LPAPlayer.h new file mode 100644 index 0000000..c351211 --- /dev/null +++ b/include/media/stagefright/LPAPlayer.h @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved. + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only. + * + * 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 LPA_PLAYER_H_ + +#define LPA_PLAYER_H_ + +#include "AudioPlayer.h" +#include <media/IAudioFlinger.h> +#include <utils/threads.h> +#include <utils/List.h> +#include <utils/Vector.h> +#include <fcntl.h> +#include <pthread.h> +#include <binder/IServiceManager.h> +#include <linux/unistd.h> +#include <include/TimedEventQueue.h> +#include <binder/BinderService.h> +#include <binder/MemoryDealer.h> +#include <powermanager/IPowerManager.h> + +// Pause timeout = 3sec +#define LPA_PAUSE_TIMEOUT_USEC 3000000 + +namespace android { + +class LPAPlayer : public AudioPlayer  { +public: +    enum { +        REACHED_EOS, +        SEEK_COMPLETE +    }; + +    enum { +        TRACK_DIRECT, +        TRACK_REGULAR, +        TRACK_NONE +    }; + +    LPAPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink, bool &initCheck, +                AwesomePlayer *audioObserver = NULL); + +    virtual ~LPAPlayer(); + +    // Caller retains ownership of "source". +    virtual void setSource(const sp<MediaSource> &source); + +    // Return time in us. +    virtual int64_t getRealTimeUs(); + +    virtual status_t start(bool sourceAlreadyStarted = false); + +    virtual void pause(bool playPendingSamples = false); +    virtual void resume(); + +    // Returns the timestamp of the last buffer played (in us). +    virtual int64_t getMediaTimeUs(); + +    // Returns true iff a mapping is established, i.e. the LPAPlayer +    // has played at least one frame of audio. +    virtual bool getMediaTimeMapping(int64_t *realtime_us, int64_t *mediatime_us); + +    virtual status_t seekTo(int64_t time_us); + +    virtual bool isSeeking(); +    virtual bool reachedEOS(status_t *finalStatus); + +    static int objectsAlive; +private: +    int64_t mPositionTimeMediaUs; +    int64_t mPositionTimeRealUs; +    bool mInternalSeeking; +    bool mIsAudioRouted; +    bool mStarted; +    bool mPaused; +    bool mA2DPEnabled; +    int32_t mChannelMask; +    int32_t numChannels; +    int32_t mSampleRate; +    int64_t mLatencyUs; +    size_t mFrameSize; +    int64_t mTimeStarted; +    int64_t mTimePlayed; +    int64_t mNumFramesPlayed; +    int64_t mNumFramesPlayedSysTimeUs; +    int64_t mNumA2DPBytesPlayed; + +    void clearPowerManager(); + +    class PMDeathRecipient : public IBinder::DeathRecipient { +        public: +                        PMDeathRecipient(void *obj){parentClass = (LPAPlayer *)obj;} +            virtual     ~PMDeathRecipient() {} + +            // IBinder::DeathRecipient +            virtual     void        binderDied(const wp<IBinder>& who); + +        private: +                        LPAPlayer *parentClass; +                        PMDeathRecipient(const PMDeathRecipient&); +                        PMDeathRecipient& operator = (const PMDeathRecipient&); + +        friend class LPAPlayer; +    }; + +    friend class PMDeathRecipient; + +    void        acquireWakeLock(); +    void        releaseWakeLock(); + +    sp<IPowerManager>       mPowerManager; +    sp<IBinder>             mWakeLockToken; +    sp<PMDeathRecipient>    mDeathRecipient; + +    pthread_t decoderThread; + +    pthread_t A2DPNotificationThread; + +    //Kill Thread boolean +    bool killDecoderThread; + + + +    bool killA2DPNotificationThread; + +    //Thread alive boolean +    bool decoderThreadAlive; + + +    bool a2dpNotificationThreadAlive; + +    //Declare the condition Variables and Mutex + +    pthread_mutex_t decoder_mutex; + +    pthread_mutex_t audio_sink_setup_mutex; + +    pthread_mutex_t a2dp_notification_mutex; + + + +    pthread_cond_t decoder_cv; + + +    pthread_cond_t a2dp_notification_cv; + + +    // make sure Decoder thread has exited +    void requestAndWaitForDecoderThreadExit(); + + +    // make sure the Effects thread also exited +    void requestAndWaitForA2DPNotificationThreadExit(); + +    static void *decoderThreadWrapper(void *me); +    void decoderThreadEntry(); +    static void *A2DPNotificationThreadWrapper(void *me); +    void A2DPNotificationThreadEntry(); + +    void createThreads(); + +    volatile bool mIsA2DPEnabled; + +    //Structure to recieve the BT notification from the flinger. +    class AudioFlingerLPAdecodeClient: public IBinder::DeathRecipient, public BnAudioFlingerClient { +    public: +        AudioFlingerLPAdecodeClient(void *obj); + +        LPAPlayer *pBaseClass; +        // DeathRecipient +        virtual void binderDied(const wp<IBinder>& who); + +        // IAudioFlingerClient + +        // indicate a change in the configuration of an output or input: keeps the cached +        // values for output/input parameters upto date in client process +        virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2); + +        friend class LPAPlayer; +    }; + +    sp<IAudioFlinger> mAudioFlinger; + +    // helper function to obtain AudioFlinger service handle +    void getAudioFlinger(); + +    void handleA2DPSwitch(); +    void onPauseTimeOut(); + +    sp<AudioFlingerLPAdecodeClient> AudioFlingerClient; +    friend class AudioFlingerLPAdecodeClient; +    Mutex AudioFlingerLock; +    sp<MediaSource> mSource; + +    MediaBuffer *mInputBuffer; + +    Mutex mLock; +    Mutex mResumeLock; + +    bool mSeeking; +    bool mReachedEOS; +    bool mReachedOutputEOS; +    status_t mFinalStatus; +    int64_t mSeekTimeUs; +    int64_t mPauseTime; + + +    bool mIsFirstBuffer; +    status_t mFirstBufferResult; +    MediaBuffer *mFirstBuffer; +    TimedEventQueue mQueue; +    bool            mQueueStarted; +    sp<TimedEventQueue::Event>  mPauseEvent; +    bool                        mPauseEventPending; + +    sp<MediaPlayerBase::AudioSink> mAudioSink; +    AwesomePlayer *mObserver; +    int mTrackType; + +    static size_t AudioSinkCallback( +        MediaPlayerBase::AudioSink *audioSink, +        void *data, size_t size, void *me); + +    enum A2DPState { +        A2DP_ENABLED, +        A2DP_DISABLED, +        A2DP_CONNECT, +        A2DP_DISCONNECT +    }; + +    int64_t getTimeStamp(A2DPState state); + +    size_t fillBuffer(void *data, size_t size); + +    int64_t getRealTimeUsLocked(); + +    void reset(); + +    status_t setupAudioSink(); +    static size_t AudioCallback( +        MediaPlayerBase::AudioSink *audioSink, +        void *buffer, size_t size, void *cookie); +    size_t AudioCallback(void *cookie, void *data, size_t size); + +    LPAPlayer(const LPAPlayer &); +    LPAPlayer &operator=(const LPAPlayer &); +}; + +struct TimedEvent : public TimedEventQueue::Event { +    TimedEvent(LPAPlayer *player, +               void (LPAPlayer::*method)()) +        : mPlayer(player), +          mMethod(method) { +    } + +protected: +    virtual ~TimedEvent() {} + +    virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) { +        (mPlayer->*mMethod)(); +    } + +private: +    LPAPlayer *mPlayer; +    void (LPAPlayer::*mMethod)(); + +    TimedEvent(const TimedEvent &); +    TimedEvent &operator=(const TimedEvent &); +}; + +}  // namespace android + +#endif  // LPA_PLAYER_H_ + diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index 2439be6..aad8844 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -1,5 +1,6 @@  /*   * Copyright (C) 2009 The Android Open Source Project + * Copyright (c) 2010 - 2012, The Linux Foundation. All rights reserved.   *   * Licensed under the Apache License, Version 2.0 (the "License");   * you may not use this file except in compliance with the License. @@ -22,6 +23,9 @@  #include <media/IOMX.h>  #include <media/stagefright/MediaBuffer.h>  #include <media/stagefright/MediaSource.h> +#ifdef QCOM_HARDWARE +#include <media/stagefright/QCOMXCodec.h> +#endif  #include <utils/threads.h>  #include <OMX_Audio.h> @@ -100,6 +104,10 @@ struct OMXCodec : public MediaSource,          kSupportsMultipleFramesPerInputBuffer = 1024,          kRequiresLargerEncoderOutputBuffer    = 2048,          kOutputBuffersAreUnreadable           = 4096, +#ifdef QCOM_HARDWARE +        kRequiresGlobalFlush                  = 0x20000000, // 2^29 +        kRequiresWMAProComponent              = 0x40000000, //2^30 +#endif      };      struct CodecNameAndQuirks { @@ -127,6 +135,11 @@ private:      // Make sure mLock is accessible to OMXCodecObserver      friend class OMXCodecObserver; +#ifdef QCOM_HARDWARE +    // QCOMXCodec can access variables of OMXCodec +    friend class QCOMXCodec; +#endif +      // Call this with mLock hold      void on_message(const omx_message &msg); @@ -143,6 +156,9 @@ private:      };      enum { +#ifdef QCOM_HARDWARE +        kPortIndexBoth   = -1, +#endif          kPortIndexInput  = 0,          kPortIndexOutput = 1      }; @@ -250,6 +266,11 @@ private:      void setG711Format(int32_t numChannels); +#ifdef QCOM_HARDWARE +    void setEVRCFormat( int32_t sampleRate, int32_t numChannels, int32_t bitRate); +    void setQCELPFormat( int32_t sampleRate, int32_t numChannels, int32_t bitRate); +#endif +      status_t setVideoPortFormatType(              OMX_U32 portIndex,              OMX_VIDEO_CODINGTYPE compressionFormat, @@ -361,6 +382,11 @@ private:      OMXCodec(const OMXCodec &);      OMXCodec &operator=(const OMXCodec &); + +#ifdef QCOM_HARDWARE +    status_t setWMAFormat(const sp<MetaData> &inputFormat); +    void setAC3Format(int32_t numChannels, int32_t sampleRate); +#endif  };  struct CodecCapabilities { diff --git a/include/media/stagefright/QCOMXCodec.h b/include/media/stagefright/QCOMXCodec.h new file mode 100644 index 0000000..7259af9 --- /dev/null +++ b/include/media/stagefright/QCOMXCodec.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + *     * Redistributions of source code must retain the above copyright + *      notice, this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above + *       copyright notice, this list of conditions and the following + *       disclaimer in the documentation and/or other materials provided + *      with the distribution. + *     * Neither the name of The Linux Foundation nor the names of its + *      contributors may be used to endorse or promote products derived + *       from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef QC_OMX_CODEC_H_ + +#define QC_OMX_CODEC_H_ + +#include <android/native_window.h> +#include <media/IOMX.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/foundation/AString.h> +#include <utils/threads.h> + +#include <OMX_Audio.h> + +namespace android { + +struct MediaCodecList; +struct OMXCodec; + +enum{ +    kRequiresWMAProComponent = 0x40000000, +}; + + +struct QCOMXCodec { + +    static uint32_t getQCComponentQuirks(const MediaCodecList *list, size_t index); + +    static status_t configureDIVXCodec(const sp<MetaData> &meta, char* mime, +                          sp<IOMX> OMXhandle,IOMX::node_id nodeID, int port_index); + +    static status_t setQCFormat(const sp<MetaData> &meta, char* mime, +                                sp<IOMX> OMXhandle,IOMX::node_id nodeID, +                                       OMXCodec *handle, bool isEncoder); + +    static status_t setWMAFormat(const sp<MetaData> &meta, sp<IOMX> OMXhandle, +                                        IOMX::node_id nodeID, bool isEncoder ); + +    static status_t setQCVideoInputFormat(const char *mime, +                                          OMX_VIDEO_CODINGTYPE *compressionFormat); + +    static status_t setQCVideoOutputFormat(const char *mime, +                                           OMX_VIDEO_CODINGTYPE *compressionFormat); + +    static status_t checkQCFormats(int format, AString* meta); + +    static void     setASFQuirks(uint32_t quirks, const sp<MetaData> &meta, +                                                 const char* componentName); + +    static void     checkAndAddRawFormat(OMXCodec *handle, const sp<MetaData> &meta); + +    static void     setEVRCFormat(int32_t numChannels, int32_t sampleRate, +                                  sp<IOMX> OMXhandle, IOMX::node_id nodeID, +                                        OMXCodec *handle,  bool isEncoder ); + +    static void     setQCELPFormat(int32_t numChannels, int32_t sampleRate, +                                   sp<IOMX> OMXhandle, IOMX::node_id nodeID, +                                         OMXCodec *handle,  bool isEncoder ); + +    static void     setAC3Format(int32_t numChannels, int32_t sampleRate, +                                 sp<IOMX> OMXhandle, IOMX::node_id nodeID); + +    static void     checkQCRole(const sp<IOMX> &omx, IOMX::node_id node, +                                        bool isEncoder,const char *mime); + +}; + +} +#endif /*QC_OMX_CODEC_H_ */ + diff --git a/include/media/stagefright/TunnelPlayer.h b/include/media/stagefright/TunnelPlayer.h new file mode 100644 index 0000000..71c4f10 --- /dev/null +++ b/include/media/stagefright/TunnelPlayer.h @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved. + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only. + * + * + * 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 TUNNEL_PLAYER_H_ + +#define TUNNEL_PLAYER_H_ + +#include "AudioPlayer.h" +#include <media/IAudioFlinger.h> +#include <utils/threads.h> +#include <utils/List.h> +#include <utils/Vector.h> +#include <fcntl.h> +#include <pthread.h> +#include <binder/IServiceManager.h> +#include <linux/unistd.h> +#include <include/TimedEventQueue.h> +#include <binder/BinderService.h> +#include <binder/MemoryDealer.h> +#include <powermanager/IPowerManager.h> + +// Pause timeout = 3sec +#define TUNNEL_PAUSE_TIMEOUT_USEC 3000000 +namespace android { + +class TunnelPlayer : public AudioPlayer  { +public: +    enum { +        REACHED_EOS, +        SEEK_COMPLETE +    }; + +    TunnelPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink, bool &initCheck, +                AwesomePlayer *audioObserver = NULL, bool hasVideo = false); + +    virtual ~TunnelPlayer(); + +    // Caller retains ownership of "source". +    virtual void setSource(const sp<MediaSource> &source); + +    // Return time in us. +    virtual int64_t getRealTimeUs(); + +    virtual status_t start(bool sourceAlreadyStarted = false); + +    virtual void pause(bool playPendingSamples = false); +    virtual void resume(); + +    // Returns the timestamp of the last buffer played (in us). +    virtual int64_t getMediaTimeUs(); + +    // Returns true iff a mapping is established, i.e. the TunnelPlayer +    // has played at least one frame of audio. +    virtual bool getMediaTimeMapping(int64_t *realtime_us, int64_t *mediatime_us); + +    virtual status_t seekTo(int64_t time_us); + +    virtual bool isSeeking(); +    virtual bool reachedEOS(status_t *finalStatus); + + +    static int mTunnelObjectsAlive; +private: +    int64_t mPositionTimeMediaUs; +    int64_t mPositionTimeRealUs; +    bool mInternalSeeking; +    bool mIsAudioRouted; +    bool mStarted; +    bool mPaused; +    bool mA2DPEnabled; +    int32_t mChannelMask; +    int32_t numChannels; +    int32_t mSampleRate; +    int64_t mLatencyUs; +    size_t mFrameSize; +    int64_t mNumFramesPlayed; +    int64_t mNumFramesPlayedSysTimeUs; +    audio_format_t mFormat; +    bool mHasVideo; +    void clearPowerManager(); + +    class PMDeathRecipient : public IBinder::DeathRecipient { +        public: +                        PMDeathRecipient(void *obj){parentClass = (TunnelPlayer *)obj;} +            virtual     ~PMDeathRecipient() {} + +            // IBinder::DeathRecipient +            virtual     void        binderDied(const wp<IBinder>& who); + +        private: +                        TunnelPlayer *parentClass; +                        PMDeathRecipient(const PMDeathRecipient&); +                        PMDeathRecipient& operator = (const PMDeathRecipient&); + +        friend class TunnelPlayer; +    }; + +    friend class PMDeathRecipient; + +    void        acquireWakeLock(); +    void        releaseWakeLock(); + +    sp<IPowerManager>       mPowerManager; +    sp<IBinder>             mWakeLockToken; +    sp<PMDeathRecipient>    mDeathRecipient; + +    pthread_t extractorThread; + +    //Kill Thread boolean +    bool killExtractorThread; + +    //Thread alive boolean +    bool extractorThreadAlive; + + +    //Declare the condition Variables and Mutex + +    pthread_mutex_t extractor_mutex; +    pthread_cond_t extractor_cv; + + +    // make sure Decoder thread has exited +    void requestAndWaitForExtractorThreadExit(); + + +    static void *extractorThreadWrapper(void *me); +    void extractorThreadEntry(); + +    void createThreads(); + +    volatile bool mIsA2DPEnabled; + +    //Structure to recieve the BT notification from the flinger. +    class AudioFlingerTunneldecodeClient: public IBinder::DeathRecipient, public BnAudioFlingerClient { +    public: +        AudioFlingerTunneldecodeClient(void *obj); + +        TunnelPlayer *pBaseClass; +        // DeathRecipient +        virtual void binderDied(const wp<IBinder>& who); + +        // IAudioFlingerClient + +        // indicate a change in the configuration of an output or input: keeps the cached +        // values for output/input parameters upto date in client process +        virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2); + +        friend class TunnelPlayer; +    }; + +    sp<IAudioFlinger> mAudioFlinger; + +    // helper function to obtain AudioFlinger service handle +    void getAudioFlinger(); +    void onPauseTimeOut(); + +    sp<AudioFlingerTunneldecodeClient> mAudioFlingerClient; +    friend class AudioFlingerTunneldecodeClient; +    Mutex mAudioFlingerLock; +    sp<MediaSource> mSource; + +    MediaBuffer *mInputBuffer; + +    Mutex pmLock; +    Mutex mLock; + +    bool mSeeking; +    bool mReachedEOS; +    bool mReachedOutputEOS; +    status_t mFinalStatus; +    int64_t mSeekTimeUs; +    int64_t mPauseTime; + + +    bool mIsFirstBuffer; +    status_t mFirstBufferResult; +    MediaBuffer *mFirstBuffer; +    TimedEventQueue mQueue; +    bool            mQueueStarted; +    sp<TimedEventQueue::Event>  mPauseEvent; +    bool                        mPauseEventPending; + +    sp<MediaPlayerBase::AudioSink> mAudioSink; +    AwesomePlayer *mObserver; + +    static size_t AudioSinkCallback( +        MediaPlayerBase::AudioSink *audioSink, +        void *data, size_t size, void *me); + +    enum A2DPState { +        A2DP_ENABLED, +        A2DP_DISABLED, +        A2DP_CONNECT, +        A2DP_DISCONNECT +    }; + +    int64_t getTimeStamp(A2DPState state); + +    size_t fillBuffer(void *data, size_t size); + +    int64_t getRealTimeUsLocked(); + +    void reset(); + +    TunnelPlayer(const TunnelPlayer &); +    TunnelPlayer &operator=(const TunnelPlayer &); +}; + +struct TunnelEvent : public TimedEventQueue::Event { +    TunnelEvent(TunnelPlayer *player, +               void (TunnelPlayer::*method)()) +        : mPlayer(player), +          mMethod(method) { +    } + +protected: +    virtual ~TunnelEvent() {} + +    virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) { +        (mPlayer->*mMethod)(); +    } + +private: +    TunnelPlayer *mPlayer; +    void (TunnelPlayer::*mMethod)(); + +    TunnelEvent(const TunnelEvent &); +    TunnelEvent &operator=(const TunnelEvent &); +}; + +}  // namespace android + +#endif  // LPA_PLAYER_H_ diff --git a/include/media/stagefright/WAVEWriter.h b/include/media/stagefright/WAVEWriter.h new file mode 100644 index 0000000..766d8f4 --- /dev/null +++ b/include/media/stagefright/WAVEWriter.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + *     * Redistributions of source code must retain the above copyright + *       notice, this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above + *       copyright notice, this list of conditions and the following + *       disclaimer in the documentation and/or other materials provided + *       with the distribution. + *     * Neither the name of The Linux Foundation nor the names of its + *       contributors may be used to endorse or promote products derived + *       from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WAVE_WRITER_H_ + +#define WAVE_WRITER_H_ + +#include <stdio.h> + +#include <media/stagefright/MediaWriter.h> +#include <utils/threads.h> + +namespace android { + + +#define ID_RIFF 0x46464952 +#define ID_WAVE 0x45564157 +#define ID_FMT  0x20746d66 +#define ID_DATA 0x61746164 +#define FORMAT_PCM 1 + + +struct MediaSource; +struct MetaData; + +struct wav_header { +    uint32_t riff_id; +    uint32_t riff_sz; +    uint32_t riff_fmt; +    uint32_t fmt_id; +    uint32_t fmt_sz; +    uint16_t audio_format; +    uint16_t num_channels; +    uint32_t sample_rate; +    uint32_t byte_rate;       /* sample_rate * num_channels * bps / 8 */ +    uint16_t block_align;     /* num_channels * bps / 8 */ +    uint16_t bits_per_sample; +    uint32_t data_id; +    uint32_t data_sz; +}; + + +struct WAVEWriter : public MediaWriter { +    WAVEWriter(const char *filename); +    WAVEWriter(int fd); + +    status_t initCheck() const; + +    virtual status_t addSource(const sp<MediaSource> &source); +    virtual bool reachedEOS(); +    virtual status_t start(MetaData *params = NULL); +    virtual status_t stop(); +    virtual status_t pause(); + +protected: +    virtual ~WAVEWriter(); + +private: +    int   mFd; +    status_t mInitCheck; +    sp<MediaSource> mSource; +    bool mStarted; +    volatile bool mPaused; +    volatile bool mResumed; +    volatile bool mDone; +    volatile bool mReachedEOS; +    pthread_t mThread; +    int64_t mEstimatedSizeBytes; +    int64_t mEstimatedDurationUs; + +    static void *ThreadWrapper(void *); +    status_t threadFunc(); +    bool exceedsFileSizeLimit(); +    bool exceedsFileDurationLimit(); + +    WAVEWriter(const WAVEWriter &); +    WAVEWriter &operator=(const WAVEWriter &); +}; + +}  // namespace android + +#endif  // WAVE_WRITER_H_ diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 54666fb..69361fa 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -11,6 +11,17 @@ include $(BUILD_STATIC_LIBRARY)  include $(CLEAR_VARS) +ifeq ($(BOARD_USES_QCOM_HARDWARE),true) +LOCAL_SRC_FILES:= AudioParameter.cpp +LOCAL_MODULE:= libaudioparameter +LOCAL_MODULE_TAGS := optional +LOCAL_SHARED_LIBRARIES := libutils + +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +endif +  LOCAL_SRC_FILES:= \      AudioTrack.cpp \      IAudioFlinger.cpp \ @@ -51,6 +62,16 @@ LOCAL_SRC_FILES:= \      SoundPool.cpp \      SoundPoolThread.cpp +ifeq ($(BOARD_USES_QCOM_HARDWARE),true) +LOCAL_SRC_FILES += \ +    IDirectTrack.cpp \ +    IDirectTrackClient.cpp + +ifeq ($(TARGET_QCOM_AUDIO_VARIANT),caf) +LOCAL_CFLAGS += -DQCOM_ENHANCED_AUDIO +endif +endif +  LOCAL_SHARED_LIBRARIES := \  	libui libcutils libutils libbinder libsonivox libicuuc libexpat \          libcamera_client libstagefright_foundation \ diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp index e3fea77..fbb34f4 100644 --- a/media/libmedia/AudioParameter.cpp +++ b/media/libmedia/AudioParameter.cpp @@ -1,5 +1,6 @@  /*   * Copyright (C) 2006-2011 The Android Open Source Project + * Copyright (c) 2012, The Linux Foundation. All rights reserved.   *   * Licensed under the Apache License, Version 2.0 (the "License");   * you may not use this file except in compliance with the License. @@ -32,6 +33,13 @@ const char * const AudioParameter::keyChannels = AUDIO_PARAMETER_STREAM_CHANNELS  const char * const AudioParameter::keyFrameCount = AUDIO_PARAMETER_STREAM_FRAME_COUNT;  const char * const AudioParameter::keyInputSource = AUDIO_PARAMETER_STREAM_INPUT_SOURCE;  const char * const AudioParameter::keyScreenState = AUDIO_PARAMETER_KEY_SCREEN_STATE; +#ifdef QCOM_HARDWARE +const char * const AudioParameter::keyHandleFm = AUDIO_PARAMETER_KEY_HANDLE_FM; +const char * const AudioParameter::keyVoipCheck = AUDIO_PARAMETER_KEY_VOIP_CHECK; +const char * const AudioParameter::keyFluenceType = AUDIO_PARAMETER_KEY_FLUENCE_TYPE; +const char * const AudioParameter::keySSR = AUDIO_PARAMETER_KEY_SSR; +const char * const AudioParameter::keyHandleA2dpDevice = AUDIO_PARAMETER_KEY_HANDLE_A2DP_DEVICE; +#endif  AudioParameter::AudioParameter(const String8& keyValuePairs)  { diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 8ea6306..ce03754 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -1,6 +1,7 @@  /*  **  ** Copyright 2008, The Android Open Source Project +** Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.  **  ** Licensed under the Apache License, Version 2.0 (the "License");  ** you may not use this file except in compliance with the License. @@ -156,7 +157,11 @@ status_t AudioRecord::set(          return BAD_VALUE;      } +#ifdef QCOM_HARDWARE +    int channelCount = popcount((channelMask) & (AUDIO_CHANNEL_IN_STEREO | AUDIO_CHANNEL_IN_MONO | AUDIO_CHANNEL_IN_5POINT1)); +#else      int channelCount = popcount(channelMask); +#endif      if (sessionId == 0 ) {          mSessionId = AudioSystem::newAudioSessionId(); diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index fa52fa9..a2172c9 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -2,6 +2,10 @@  **  ** Copyright 2007, The Android Open Source Project  ** +** Copyright (c) 2012, The Linux Foundation. All rights reserved. +** Not a Contribution, Apache license notifications and license are retained +** for attribution purposes only. +  ** 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 @@ -96,6 +100,10 @@ AudioTrack::AudioTrack()        mIsTimed(false),        mPreviousPriority(ANDROID_PRIORITY_NORMAL),        mPreviousSchedulingGroup(SP_DEFAULT) +#ifdef QCOM_HARDWARE +      ,mAudioFlinger(NULL), +      mObserver(NULL) +#endif  {  } @@ -114,6 +122,10 @@ AudioTrack::AudioTrack(        mIsTimed(false),        mPreviousPriority(ANDROID_PRIORITY_NORMAL),        mPreviousSchedulingGroup(SP_DEFAULT) +#ifdef QCOM_HARDWARE +      ,mAudioFlinger(NULL), +      mObserver(NULL) +#endif  {      mStatus = set(streamType, sampleRate, format, channelMask,              frameCount, flags, cbf, user, notificationFrames, @@ -135,6 +147,10 @@ AudioTrack::AudioTrack(      : mStatus(NO_INIT),        mIsTimed(false),        mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT) +#ifdef QCOM_HARDWARE +      ,mAudioFlinger(NULL), +      mObserver(NULL) +#endif  {      mStatus = set((audio_stream_type_t)streamType, sampleRate, (audio_format_t)format,              (audio_channel_mask_t) channelMask, @@ -157,6 +173,10 @@ AudioTrack::AudioTrack(        mIsTimed(false),        mPreviousPriority(ANDROID_PRIORITY_NORMAL),        mPreviousSchedulingGroup(SP_DEFAULT) +#ifdef QCOM_HARDWARE +      ,mAudioFlinger(NULL), +      mObserver(NULL) +#endif  {      mStatus = set(streamType, sampleRate, format, channelMask,              0 /*frameCount*/, flags, cbf, user, notificationFrames, @@ -177,9 +197,22 @@ AudioTrack::~AudioTrack()              mAudioTrackThread->requestExitAndWait();              mAudioTrackThread.clear();          } +#ifdef QCOM_HARDWARE +        if (mAudioTrack != 0) { +            mAudioTrack.clear(); +            AudioSystem::releaseAudioSessionId(mSessionId); +        } + +        if (mDirectTrack != 0) { +            mDirectTrack.clear(); +        } +#else          mAudioTrack.clear(); +#endif          IPCThreadState::self()->flushCommands(); +#ifndef QCOM_HARDWARE          AudioSystem::releaseAudioSessionId(mSessionId); +#endif      }  } @@ -252,12 +285,24 @@ status_t AudioTrack::set(          flags = (audio_output_flags_t)(flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER);      } +#ifdef QCOM_ENHANCED_AUDIO +    if ((streamType == AUDIO_STREAM_VOICE_CALL) +         && (channelMask == AUDIO_CHANNEL_OUT_MONO) +         && ((sampleRate == 8000 || sampleRate == 16000))) +    { +        ALOGD("Turn on Direct Output for VOIP RX"); +        flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_VOIP_RX|AUDIO_OUTPUT_FLAG_DIRECT); +    } +#endif +      if (!audio_is_output_channel(channelMask)) {          ALOGE("Invalid channel mask %#x", channelMask);          return BAD_VALUE;      }      uint32_t channelCount = popcount(channelMask); +    ALOGV("AudioTrack getOutput streamType %d, sampleRate %d, format %d, channelMask %d, flags %x", +           streamType, sampleRate, format, channelMask, flags);      audio_io_handle_t output = AudioSystem::getOutput(                                      streamType,                                      sampleRate, format, channelMask, @@ -278,46 +323,86 @@ status_t AudioTrack::set(      mFlags = flags;      mCbf = cbf; -    if (cbf != NULL) { -        mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava); -        mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/); -    } - -    // create the IAudioTrack -    status_t status = createTrack_l(streamType, -                                  sampleRate, -                                  format, -                                  channelMask, -                                  frameCount, -                                  flags, -                                  sharedBuffer, -                                  output); +#ifdef QCOM_HARDWARE +    if (flags & AUDIO_OUTPUT_FLAG_LPA || flags & AUDIO_OUTPUT_FLAG_TUNNEL) { +        ALOGV("Creating Direct Track"); +        const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger(); +        if (audioFlinger == 0) { +            ALOGE("Could not get audioflinger"); +            return NO_INIT; +        } +        mAudioFlinger = audioFlinger; +        status_t status = NO_ERROR; +        mAudioDirectOutput = output; +        mDirectTrack = audioFlinger->createDirectTrack( getpid(), +                                                        sampleRate, +                                                        channelMask, +                                                        mAudioDirectOutput, +                                                        &mSessionId, +                                                        this, +                                                        streamType, +                                                        &status); +        if(status != NO_ERROR) { +            ALOGE("createDirectTrack returned with status %d", status); +            return status; +        } +        mAudioTrack = NULL; +        mSharedBuffer = NULL; +    } +    else { +#endif +        if (cbf != NULL) { +            mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava); +            mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/); +        } +        // create the IAudioTrack +        status_t status = createTrack_l(streamType, +                                      sampleRate, +                                      format, +                                      channelMask, +                                      frameCount, +                                      flags, +                                      sharedBuffer, +                                      output); -    if (status != NO_ERROR) { -        if (mAudioTrackThread != 0) { -            mAudioTrackThread->requestExit(); -            mAudioTrackThread.clear(); +        if (status != NO_ERROR) { +            if (mAudioTrackThread != 0) { +                mAudioTrackThread->requestExit(); +                mAudioTrackThread.clear(); +            } +            return status;          } -        return status; +#ifdef QCOM_HARDWARE +        AudioSystem::acquireAudioSessionId(mSessionId); +        mAudioDirectOutput = -1; +        mDirectTrack = NULL; +        mSharedBuffer = sharedBuffer;      } - +    mUserData = user; +#endif      mStatus = NO_ERROR;      mStreamType = streamType;      mFormat = format;      mChannelMask = channelMask;      mChannelCount = channelCount; -    mSharedBuffer = sharedBuffer; +      mMuted = false;      mActive = false; -    mUserData = user; +      mLoopCount = 0;      mMarkerPosition = 0;      mMarkerReached = false;      mNewPosition = 0;      mUpdatePeriod = 0;      mFlushed = false; + +#ifndef QCOM_HARDWARE +    mSharedBuffer = sharedBuffer; +    mUserData = user;      AudioSystem::acquireAudioSessionId(mSessionId); +#endif +      mRestoreStatus = NO_ERROR;      return NO_ERROR;  } @@ -331,6 +416,11 @@ status_t AudioTrack::initCheck() const  uint32_t AudioTrack::latency() const  { +#ifdef QCOM_HARDWARE +    if(mAudioDirectOutput != -1) { +        return mAudioFlinger->latency(mAudioDirectOutput); +    } +#endif      return mLatency;  } @@ -351,6 +441,11 @@ int AudioTrack::channelCount() const  uint32_t AudioTrack::frameCount() const  { +#ifdef QCOM_HARDWARE +    if(mAudioDirectOutput != -1) { +        return mAudioFlinger->frameCount(mAudioDirectOutput); +    } +#endif      return mCblk->frameCount;  } @@ -372,6 +467,16 @@ sp<IMemory>& AudioTrack::sharedBuffer()  void AudioTrack::start()  { +#ifdef QCOM_HARDWARE +    if (mDirectTrack != NULL) { +        if(mActive == 0) { +            mActive = 1; +            mDirectTrack->start(); +        } +        return; +    } +#endif +      sp<AudioTrackThread> t = mAudioTrackThread;      ALOGV("start %p", this); @@ -436,26 +541,35 @@ void AudioTrack::stop()      AutoMutex lock(mLock);      if (mActive) { -        mActive = false; -        mCblk->cv.signal(); -        mAudioTrack->stop(); -        // Cancel loops (If we are in the middle of a loop, playback -        // would not stop until loopCount reaches 0). -        setLoop_l(0, 0, 0); -        // the playback head position will reset to 0, so if a marker is set, we need -        // to activate it again -        mMarkerReached = false; -        // Force flush if a shared buffer is used otherwise audioflinger -        // will not stop before end of buffer is reached. -        if (mSharedBuffer != 0) { -            flush_l(); -        } -        if (t != 0) { -            t->pause(); -        } else { -            setpriority(PRIO_PROCESS, 0, mPreviousPriority); -            set_sched_policy(0, mPreviousSchedulingGroup); +#ifdef QCOM_HARDWARE +        if(mDirectTrack != NULL) { +            mActive = false; +            mDirectTrack->stop(); +        } else if (mAudioTrack != NULL) { +#endif +            mActive = false; +            mCblk->cv.signal(); +            mAudioTrack->stop(); +            // Cancel loops (If we are in the middle of a loop, playback +            // would not stop until loopCount reaches 0). +            setLoop_l(0, 0, 0); +            // the playback head position will reset to 0, so if a marker is set, we need +            // to activate it again +            mMarkerReached = false; +            // Force flush if a shared buffer is used otherwise audioflinger +            // will not stop before end of buffer is reached. +            if (mSharedBuffer != 0) { +                flush_l(); +            } +            if (t != 0) { +                t->pause(); +            } else { +                setpriority(PRIO_PROCESS, 0, mPreviousPriority); +                set_sched_policy(0, mPreviousSchedulingGroup); +            } +#ifdef QCOM_HARDWARE          } +#endif      }  } @@ -469,7 +583,12 @@ bool AudioTrack::stopped() const  void AudioTrack::flush()  {      AutoMutex lock(mLock); -    flush_l(); +#ifdef QCOM_HARDWARE +    if(mDirectTrack != NULL) { +        mDirectTrack->flush(); +    } else +#endif +        flush_l();  }  // must be called with mLock held @@ -497,14 +616,28 @@ void AudioTrack::pause()      AutoMutex lock(mLock);      if (mActive) {          mActive = false; -        mCblk->cv.signal(); -        mAudioTrack->pause(); +#ifdef QCOM_HARDWARE +        if(mDirectTrack != NULL) { +            ALOGV("mDirectTrack pause"); +            mDirectTrack->pause(); +        } else { +#endif +            mCblk->cv.signal(); +            mAudioTrack->pause(); +#ifdef QCOM_HARDWARE +        } +#endif      }  }  void AudioTrack::mute(bool e)  { -    mAudioTrack->mute(e); +#ifdef QCOM_HARDWARE +    if(mDirectTrack != NULL) { +        mDirectTrack->mute(e); +    } else +#endif +        mAudioTrack->mute(e);      mMuted = e;  } @@ -522,8 +655,13 @@ status_t AudioTrack::setVolume(float left, float right)      AutoMutex lock(mLock);      mVolume[LEFT] = left;      mVolume[RIGHT] = right; - -    mCblk->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000)); +#ifdef QCOM_HARDWARE +    if(mDirectTrack != NULL) { +        ALOGV("mDirectTrack->setVolume(left = %f , right = %f)", left,right); +        mDirectTrack->setVolume(left, right); +    } else +#endif +        mCblk->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000));      return NO_ERROR;  } @@ -540,6 +678,11 @@ void AudioTrack::getVolume(float* left, float* right) const  status_t AudioTrack::setAuxEffectSendLevel(float level)  { +#ifdef QCOM_HARDWARE +    if (mDirectTrack != NULL) { +        return NO_ERROR; +    } +#endif      ALOGV("setAuxEffectSendLevel(%f)", level);      if (level < 0.0f || level > 1.0f) {          return BAD_VALUE; @@ -586,6 +729,11 @@ uint32_t AudioTrack::getSampleRate() const      }      AutoMutex lock(mLock); +#ifdef QCOM_HARDWARE +    if(mAudioDirectOutput != -1) { +        return mAudioFlinger->sampleRate(mAudioDirectOutput); +    } +#endif      return mCblk->sampleRate;  } @@ -1076,7 +1224,12 @@ void AudioTrack::releaseBuffer(Buffer* audioBuffer)  ssize_t AudioTrack::write(const void* buffer, size_t userSize)  { - +#ifdef QCOM_HARDWARE +    if (mDirectTrack != NULL) { +        mDirectTrack->write(buffer,userSize); +        return userSize; +    } +#endif      if (mSharedBuffer != 0) return INVALID_OPERATION;      if (mIsTimed) return INVALID_OPERATION; @@ -1456,6 +1609,23 @@ status_t AudioTrack::dump(int fd, const Vector<String16>& args) const      return NO_ERROR;  } +#ifdef QCOM_HARDWARE +void AudioTrack::notify(int msg) { +    if (msg == EVENT_UNDERRUN) { +        ALOGV("Posting event underrun to Audio Sink."); +        mCbf(EVENT_UNDERRUN, mUserData, 0); +    } +} + +status_t AudioTrack::getTimeStamp(uint64_t *tstamp) { +    if (mDirectTrack != NULL) { +        *tstamp = mDirectTrack->getTimeStamp(); +        ALOGV("Timestamp %lld ", *tstamp); +    } +    return NO_ERROR; +} +#endif +  // =========================================================================  AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava) diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index ce8ffc4..cc6a75c 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -1,6 +1,9 @@  /*  **  ** Copyright 2007, The Android Open Source Project +** Copyright (c) 2012, The Linux Foundation. All rights reserved. +** Not a Contribution, Apache license notifications and license are retained +** for attribution purposes only.  **  ** Licensed under the Apache License, Version 2.0 (the "License");  ** you may not use this file except in compliance with the License. @@ -73,6 +76,9 @@ enum {      LOAD_HW_MODULE,      GET_PRIMARY_OUTPUT_SAMPLING_RATE,      GET_PRIMARY_OUTPUT_FRAME_COUNT, +#ifdef QCOM_HARDWARE +    CREATE_DIRECT_TRACK +#endif  };  class BpAudioFlinger : public BpInterface<IAudioFlinger> @@ -132,6 +138,49 @@ public:          return track;      } +#ifdef QCOM_HARDWARE +    virtual sp<IDirectTrack> createDirectTrack( +                                pid_t pid, +                                uint32_t sampleRate, +                                audio_channel_mask_t channelMask, +                                audio_io_handle_t output, +                                int *sessionId, +                                IDirectTrackClient* client, +                                audio_stream_type_t streamType, +                                status_t *status) +    { +        Parcel data, reply; +        sp<IDirectTrack> track; +        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); +        data.writeInt32(pid); +        data.writeInt32(sampleRate); +        data.writeInt32(channelMask); +        data.writeInt32((int32_t)output); +        int lSessionId = 0; +        if (sessionId != NULL) { +            lSessionId = *sessionId; +        } +        data.writeInt32(lSessionId); +        data.write(client, sizeof(IDirectTrackClient)); +        data.writeInt32((int32_t) streamType); +        status_t lStatus = remote()->transact(CREATE_DIRECT_TRACK, data, &reply); +        if (lStatus != NO_ERROR) { +            ALOGE("createDirectTrack error: %s", strerror(-lStatus)); +        } else { +            lSessionId = reply.readInt32(); +            if (sessionId != NULL) { +                *sessionId = lSessionId; +            } +            lStatus = reply.readInt32(); +            track = interface_cast<IDirectTrack>(reply.readStrongBinder()); +        } +        if (status) { +            *status = lStatus; +        } +        return track; +    } +#endif +      virtual sp<IAudioRecord> openRecord(                                  pid_t pid,                                  audio_io_handle_t input, @@ -738,6 +787,26 @@ status_t BnAudioFlinger::onTransact(              reply->writeStrongBinder(track->asBinder());              return NO_ERROR;          } break; +#ifdef QCOM_HARDWARE +        case CREATE_DIRECT_TRACK: { +            CHECK_INTERFACE(IAudioFlinger, data, reply); +            pid_t pid = data.readInt32(); +            uint32_t sampleRate = data.readInt32(); +            audio_channel_mask_t channelMask = data.readInt32(); +            audio_io_handle_t output = (audio_io_handle_t) data.readInt32(); +            int sessionId = data.readInt32(); +            IDirectTrackClient* client; +            data.read(client,sizeof(IDirectTrackClient)); +            int streamType = data.readInt32(); +            status_t status; +            sp<IDirectTrack> track = createDirectTrack(pid, +                    sampleRate, channelMask, output, &sessionId, client,(audio_stream_type_t) streamType, &status); +            reply->writeInt32(sessionId); +            reply->writeInt32(status); +            reply->writeStrongBinder(track->asBinder()); +            return NO_ERROR; +        } break; +#endif          case OPEN_RECORD: {              CHECK_INTERFACE(IAudioFlinger, data, reply);              pid_t pid = data.readInt32(); diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp index 4178b29..e289703 100644 --- a/media/libmedia/IAudioFlingerClient.cpp +++ b/media/libmedia/IAudioFlingerClient.cpp @@ -1,4 +1,8 @@  /* + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only. + *   * Copyright (C) 2009 The Android Open Source Project   *   * Licensed under the Apache License, Version 2.0 (the "License"); @@ -49,7 +53,11 @@ public:              uint32_t stream = *(const uint32_t *)param2;              ALOGV("ioConfigChanged stream %d", stream);              data.writeInt32(stream); -        } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) { +        } else if (event != AudioSystem::OUTPUT_CLOSED && +#ifdef QCOM_HARDWARE +                        event != AudioSystem::EFFECT_CONFIG_CHANGED && +#endif +                        event != AudioSystem::INPUT_CLOSED) {              const AudioSystem::OutputDescriptor *desc = (const AudioSystem::OutputDescriptor *)param2;              data.writeInt32(desc->samplingRate);              data.writeInt32(desc->format); diff --git a/media/libmedia/IDirectTrack.cpp b/media/libmedia/IDirectTrack.cpp new file mode 100644 index 0000000..480761f --- /dev/null +++ b/media/libmedia/IDirectTrack.cpp @@ -0,0 +1,178 @@ +/* +** Copyright (c) 2012, The Linux Foundation. All rights reserved. +** Not a Contribution, Apache license notifications and license are retained +** for attribution purposes only. +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +**     http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "IDirectTrack" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> + +#include <stdint.h> +#include <sys/types.h> + +#include <binder/Parcel.h> + +#include <media/IDirectTrack.h> + +namespace android { + +enum { +    START = IBinder::FIRST_CALL_TRANSACTION, +    STOP, +    FLUSH, +    MUTE, +    PAUSE, +    SET_VOLUME, +    WRITE, +    GET_TIMESTAMP +}; + +class BpDirectTrack : public BpInterface<IDirectTrack> +{ +public: +    BpDirectTrack(const sp<IBinder>& impl) +        : BpInterface<IDirectTrack>(impl) +    { +    } + +    virtual status_t start() +    { +        Parcel data, reply; +        data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor()); +        status_t status = remote()->transact(START, data, &reply); +        if (status == NO_ERROR) { +            status = reply.readInt32(); +        } else { +            ALOGW("start() error: %s", strerror(-status)); +        } +        return status; +    } + +    virtual void stop() +    { +        Parcel data, reply; +        data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor()); +        remote()->transact(STOP, data, &reply); +    } + +    virtual void flush() +    { +        Parcel data, reply; +        data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor()); +        remote()->transact(FLUSH, data, &reply); +    } + +    virtual void mute(bool e) +    { +        Parcel data, reply; +        data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor()); +        data.writeInt32(e); +        remote()->transact(MUTE, data, &reply); +    } + +    virtual void pause() +    { +        Parcel data, reply; +        data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor()); +        remote()->transact(PAUSE, data, &reply); +    } + +    virtual void setVolume(float left, float right) +    { +        Parcel data, reply; +        data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor()); +        remote()->transact(SET_VOLUME, data, &reply); +    } + +    virtual ssize_t write(const void* buffer, size_t bytes) +    { +        Parcel data, reply; +        data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor()); +        ssize_t bytesWritten = remote()->transact(WRITE, data, &reply); +        return bytesWritten; +    } + +    virtual int64_t getTimeStamp() { +        Parcel data, reply; +        data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor()); +        int64_t tstamp = remote()->transact(GET_TIMESTAMP, data, &reply); +        return tstamp; +    } +}; + +IMPLEMENT_META_INTERFACE(DirectTrack, "android.media.IDirectTrack"); + +// ---------------------------------------------------------------------- + +status_t BnDirectTrack::onTransact( +    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ +    switch(code) { +        case START: { +            CHECK_INTERFACE(IDirectTrack, data, reply); +            reply->writeInt32(start()); +            return NO_ERROR; +        } break; +        case STOP: { +            CHECK_INTERFACE(IDirectTrack, data, reply); +            stop(); +            return NO_ERROR; +        } break; +        case FLUSH: { +            CHECK_INTERFACE(IDirectTrack, data, reply); +            flush(); +            return NO_ERROR; +        } break; +        case MUTE: { +            CHECK_INTERFACE(IDirectTrack, data, reply); +            mute( data.readInt32() ); +            return NO_ERROR; +        } break; +        case PAUSE: { +            CHECK_INTERFACE(IDirectTrack, data, reply); +            pause(); +            return NO_ERROR; +        } +        case SET_VOLUME: { +            CHECK_INTERFACE(IDirectTrack, data, reply); +            float left = 1.0; +            float right = 1.0; +            setVolume(left, right); +            return NO_ERROR; +        } +        case WRITE: { +            CHECK_INTERFACE(IDirectTrack, data, reply); +            const void *buffer = (void *)data.readInt32(); +            size_t bytes = data.readInt32(); +            ssize_t bytesWritten = write(buffer, bytes); +            reply->writeInt32(bytesWritten); +            return NO_ERROR; +        } +        case GET_TIMESTAMP: { +            CHECK_INTERFACE(IDirectTrack, data, reply); +            int64_t time = getTimeStamp(); +            reply->writeInt32(time); +            return NO_ERROR; +        } +        default: +            return BBinder::onTransact(code, data, reply, flags); +    } +} + +}; // namespace android + diff --git a/media/libmedia/IDirectTrackClient.cpp b/media/libmedia/IDirectTrackClient.cpp new file mode 100644 index 0000000..86a47ec --- /dev/null +++ b/media/libmedia/IDirectTrackClient.cpp @@ -0,0 +1,69 @@ +/* +** Copyright (c) 2012, The Linux Foundation. All rights reserved. +** Not a Contribution, Apache license notifications and license are retained +** for attribution purposes only. +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +**     http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +#include <media/IDirectTrackClient.h> + +namespace android { + +enum { +    NOTIFY = IBinder::FIRST_CALL_TRANSACTION, +}; + +class BpDirectTrackClient: public BpInterface<IDirectTrackClient> +{ +public: +    BpDirectTrackClient(const sp<IBinder>& impl) +        : BpInterface<IDirectTrackClient>(impl) +    { +    } + +    virtual void notify(int msg) +    { +        Parcel data, reply; +        data.writeInterfaceToken(IDirectTrackClient::getInterfaceDescriptor()); +        data.writeInt32(msg); +        remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY); +    } +}; + +IMPLEMENT_META_INTERFACE(DirectTrackClient, "android.media.IDirectTrackClient"); + +// ---------------------------------------------------------------------- + +status_t BnDirectTrackClient::onTransact( +    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ +    switch (code) { +        case NOTIFY: { +            CHECK_INTERFACE(IDirectTrackClient, data, reply); +            int msg = data.readInt32(); +            notify(msg); +            return NO_ERROR; +        } break; +        default: +            return BBinder::onTransact(code, data, reply, flags); +    } +} + +}; // namespace android diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp index f69dbea..fa536a6 100644 --- a/media/libmedia/MediaProfiles.cpp +++ b/media/libmedia/MediaProfiles.cpp @@ -1,6 +1,7 @@  /*  **  ** Copyright 2010, The Android Open Source Project +** Copyright (c) 2010 - 2012, The Linux Foundation. All rights reserved.  **  ** Licensed under the Apache License, Version 2.0 (the "License");  ** you may not use this file except in compliance with the License. @@ -45,7 +46,10 @@ const MediaProfiles::NameToTagMap MediaProfiles::sAudioEncoderNameMap[] = {      {"amrwb",  AUDIO_ENCODER_AMR_WB},      {"aac",    AUDIO_ENCODER_AAC},      {"heaac",  AUDIO_ENCODER_HE_AAC}, -    {"aaceld", AUDIO_ENCODER_AAC_ELD} +    {"aaceld", AUDIO_ENCODER_AAC_ELD}, +#ifdef QCOM_HARDWARE +    {"lpcm",  AUDIO_ENCODER_LPCM}, +#endif  };  const MediaProfiles::NameToTagMap MediaProfiles::sFileFormatMap[] = { @@ -804,6 +808,10 @@ MediaProfiles::createDefaultCamcorderProfiles(MediaProfiles *profiles)  MediaProfiles::createDefaultAudioEncoders(MediaProfiles *profiles)  {      profiles->mAudioEncoders.add(createDefaultAmrNBEncoderCap()); +#ifdef QCOM_HARDWARE +    profiles->mAudioEncoders.add(createDefaultAacEncoderCap()); +    profiles->mAudioEncoders.add(createDefaultLpcmEncoderCap()); +#endif  }  /*static*/ void @@ -838,6 +846,22 @@ MediaProfiles::createDefaultAmrNBEncoderCap()          AUDIO_ENCODER_AMR_NB, 5525, 12200, 8000, 8000, 1, 1);  } +#ifdef QCOM_HARDWARE +/*static*/ MediaProfiles::AudioEncoderCap* +MediaProfiles::createDefaultAacEncoderCap() +{ +    return new MediaProfiles::AudioEncoderCap( +        AUDIO_ENCODER_AAC, 64000, 156000, 8000, 48000, 1, 2); +} + +/*static*/ MediaProfiles::AudioEncoderCap* +MediaProfiles::createDefaultLpcmEncoderCap() +{ +    return new MediaProfiles::AudioEncoderCap( +        AUDIO_ENCODER_LPCM, 768000, 4608000, 48000, 48000, 1, 6); +} +#endif +  /*static*/ void  MediaProfiles::createDefaultImageEncodingQualityLevels(MediaProfiles *profiles)  { diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index 5b5ed71..a583d48 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -47,7 +47,12 @@ LOCAL_C_INCLUDES :=                                                 \      $(TOP)/frameworks/av/media/libstagefright/rtsp                  \      $(TOP)/frameworks/av/media/libstagefright/wifi-display          \      $(TOP)/frameworks/native/include/media/openmax                  \ -    $(TOP)/external/tremolo/Tremolo                                 \ +    $(TOP)/external/tremolo/Tremolo + +ifeq ($(BOARD_USES_QCOM_HARDWARE),true) +LOCAL_C_INCLUDES += \ +    $(TOP)/hardware/qcom/media/mm-core/inc +endif  LOCAL_MODULE:= libmediaplayerservice diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 9bedff1..414c262 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -1,5 +1,9 @@  /*  ** +** Copyright (c) 2012, The Linux Foundation. All rights reserved. +** Not a Contribution, Apache license notifications and license are retained +** for attribution purposes only. +**  ** Copyright 2008, The Android Open Source Project  **  ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -74,6 +78,7 @@  #include "Crypto.h"  #include "HDCP.h"  #include "RemoteDisplay.h" +#define DEFAULT_SAMPLE_RATE 44100  namespace {  using android::media::Metadata; @@ -1363,6 +1368,22 @@ status_t MediaPlayerService::AudioOutput::getPosition(uint32_t *position) const      return mTrack->getPosition(position);  } +#ifdef QCOM_HARDWARE +ssize_t MediaPlayerService::AudioOutput::sampleRate() const +{ +    if (mTrack == 0) return NO_INIT; +    return DEFAULT_SAMPLE_RATE; +} + +status_t MediaPlayerService::AudioOutput::getTimeStamp(uint64_t *tstamp) +{ +    if (tstamp == 0) return BAD_VALUE; +    if (mTrack == 0) return NO_INIT; +    mTrack->getTimeStamp(tstamp); +    return NO_ERROR; +} +#endif +  status_t MediaPlayerService::AudioOutput::getFramesWritten(uint32_t *frameswritten) const  {      if (mTrack == 0) return NO_INIT; @@ -1379,6 +1400,65 @@ status_t MediaPlayerService::AudioOutput::open(      mCallback = cb;      mCallbackCookie = cookie; +#ifdef QCOM_HARDWARE +    if (flags & AUDIO_OUTPUT_FLAG_LPA || flags & AUDIO_OUTPUT_FLAG_TUNNEL) { +        ALOGV("AudioOutput open: with flags %x",flags); +        channelMask = audio_channel_out_mask_from_count(channelCount); +        if (0 == channelMask) { +            ALOGE("open() error, can't derive mask for %d audio channels", channelCount); +            return NO_INIT; +        } +        AudioTrack *audioTrack = NULL; +        CallbackData *newcbd = NULL; +        if (mCallback != NULL) { +            newcbd = new CallbackData(this); +            audioTrack = new AudioTrack( +                             mStreamType, +                             sampleRate, +                             format, +                             channelMask, +                             0, +                             flags, +                             CallbackWrapper, +                             newcbd, +                             0, +                             mSessionId); +            if ((audioTrack == 0) || (audioTrack->initCheck() != NO_ERROR)) { +                ALOGE("Unable to create audio track"); +                delete audioTrack; +                delete newcbd; +                return NO_INIT; +            } +        } else { +            ALOGE("no callback supplied"); +            return NO_INIT; +        } + +        if (mRecycledTrack) { +            //usleep(500000); +            // if we're not going to reuse the track, unblock and flush it +            if (mCallbackData != NULL) { +                mCallbackData->setOutput(NULL); +                mCallbackData->endTrackSwitch(); +            } +            mRecycledTrack->flush(); +            delete mRecycledTrack; +            mRecycledTrack = NULL; +            delete mCallbackData; +            mCallbackData = NULL; +            close(); +        } + +        ALOGV("setVolume"); +        mCallbackData = newcbd; +        audioTrack->setVolume(mLeftVolume, mRightVolume); +        mSampleRateHz = sampleRate; +        mFlags = flags; +        mTrack = audioTrack; +        return NO_ERROR; +    } +#endif +      // Check argument "bufferCount" against the mininum buffer count      if (bufferCount < mMinBufferCount) {          ALOGD("bufferCount (%d) is too small and increased to %d", bufferCount, mMinBufferCount); @@ -1551,7 +1631,7 @@ void MediaPlayerService::AudioOutput::switchToNextOutput() {  ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)  { -    LOG_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback."); +    //LOG_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");      //ALOGV("write(%p, %u)", buffer, size);      if (mTrack) { @@ -1637,35 +1717,56 @@ status_t MediaPlayerService::AudioOutput::attachAuxEffect(int effectId)  void MediaPlayerService::AudioOutput::CallbackWrapper(          int event, void *cookie, void *info) {      //ALOGV("callbackwrapper"); -    if (event != AudioTrack::EVENT_MORE_DATA) { -        return; -    } - -    CallbackData *data = (CallbackData*)cookie; -    data->lock(); -    AudioOutput *me = data->getOutput(); -    AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info; -    if (me == NULL) { -        // no output set, likely because the track was scheduled to be reused -        // by another player, but the format turned out to be incompatible. +#ifdef QCOM_HARDWARE +    if (event == AudioTrack::EVENT_UNDERRUN) { +        ALOGW("Event underrun"); +        CallbackData *data = (CallbackData*)cookie; +        data->lock(); +        AudioOutput *me = data->getOutput(); +        if (me == NULL) { +            // no output set, likely because the track was scheduled to be reused +            // by another player, but the format turned out to be incompatible. +            data->unlock(); +            return; +        } +        ALOGD("Callback!!!"); +        (*me->mCallback)( +            me, NULL, (size_t)AudioTrack::EVENT_UNDERRUN, me->mCallbackCookie);          data->unlock(); -        buffer->size = 0;          return;      } +#endif +    if (event == AudioTrack::EVENT_MORE_DATA) { +        CallbackData *data = (CallbackData*)cookie; +        data->lock(); +        AudioOutput *me = data->getOutput(); +        AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info; +        if (me == NULL) { +            // no output set, likely because the track was scheduled to be reused +            // by another player, but the format turned out to be incompatible. +            data->unlock(); +            buffer->size = 0; +            return; +        } + +        size_t actualSize = (*me->mCallback)( +                me, buffer->raw, buffer->size, me->mCallbackCookie); -    size_t actualSize = (*me->mCallback)( -            me, buffer->raw, buffer->size, me->mCallbackCookie); +        if (actualSize == 0 && buffer->size > 0 && me->mNextOutput == NULL) { +            // We've reached EOS but the audio track is not stopped yet, +            // keep playing silence. -    if (actualSize == 0 && buffer->size > 0 && me->mNextOutput == NULL) { -        // We've reached EOS but the audio track is not stopped yet, -        // keep playing silence. +            memset(buffer->raw, 0, buffer->size); +            actualSize = buffer->size; +        } -        memset(buffer->raw, 0, buffer->size); -        actualSize = buffer->size; +        buffer->size = actualSize; +        data->unlock();      } -    buffer->size = actualSize; -    data->unlock(); +    return; + +  }  int MediaPlayerService::AudioOutput::getSessionId() const @@ -1700,6 +1801,13 @@ status_t MediaPlayerService::AudioCache::getPosition(uint32_t *position) const      return NO_ERROR;  } +#ifdef QCOM_HARDWARE +ssize_t MediaPlayerService::AudioCache::sampleRate() const +{ +    return mSampleRate; +} +#endif +  status_t MediaPlayerService::AudioCache::getFramesWritten(uint32_t *written) const  {      if (written == 0) return BAD_VALUE; diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index fd648df..54df9d2 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -114,6 +114,10 @@ class MediaPlayerService : public BnMediaPlayerService                  void            setNextOutput(const sp<AudioOutput>& nextOutput);                  void            switchToNextOutput();          virtual bool            needsTrailingPadding() { return mNextOutput == NULL; } +#ifdef QCOM_HARDWARE +        virtual ssize_t         sampleRate() const; +        virtual status_t        getTimeStamp(uint64_t *tstamp); +#endif      private:          static void             setMinBufferCount(); @@ -205,8 +209,10 @@ class MediaPlayerService : public BnMediaPlayerService          virtual void            close() {}                  void            setAudioStreamType(audio_stream_type_t streamType) {}                  void            setVolume(float left, float right) {} -        virtual status_t        setPlaybackRatePermille(int32_t ratePermille) { return INVALID_OPERATION; } +#ifndef QCOM_HARDWARE                  uint32_t        sampleRate() const { return mSampleRate; } +#endif +        virtual status_t        setPlaybackRatePermille(int32_t ratePermille) { return INVALID_OPERATION; }                  audio_format_t  format() const { return mFormat; }                  size_t          size() const { return mSize; }                  status_t        wait(); @@ -216,6 +222,9 @@ class MediaPlayerService : public BnMediaPlayerService          static  void            notify(void* cookie, int msg,                                         int ext1, int ext2, const Parcel *obj);          virtual status_t        dump(int fd, const Vector<String16>& args) const; +#ifdef QCOM_HARDWARE +        virtual ssize_t         sampleRate() const; +#endif      private:                                  AudioCache(); diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 57b0ec2..aa25eff 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -1,5 +1,6 @@  /*   * Copyright (C) 2009 The Android Open Source Project + * Copyright (c) 2010 - 2012, The Linux Foundation. All rights reserved.   *   * Licensed under the Apache License, Version 2.0 (the "License");   * you may not use this file except in compliance with the License. @@ -29,6 +30,10 @@  #include <media/stagefright/AudioSource.h>  #include <media/stagefright/AMRWriter.h>  #include <media/stagefright/AACWriter.h> +#ifdef QCOM_HARDWARE +#include <media/stagefright/ExtendedWriter.h> +#include <media/stagefright/WAVEWriter.h> +#endif  #include <media/stagefright/CameraSource.h>  #include <media/stagefright/CameraSourceTimeLapse.h>  #include <media/stagefright/MPEG2TSWriter.h> @@ -49,6 +54,9 @@  #include <unistd.h>  #include <system/audio.h> +#ifdef QCOM_HARDWARE +#include <QCMediaDefs.h> +#endif  #include "ARTPWriter.h" @@ -159,6 +167,26 @@ status_t StagefrightRecorder::setAudioEncoder(audio_encoder ae) {          mAudioEncoder = ae;      } +#ifdef QCOM_HARDWARE +    // Use default values if appropriate setparam's weren't called. +    if(mAudioEncoder == AUDIO_ENCODER_AAC) { +        mSampleRate = mSampleRate ? mSampleRate : 48000; +        mAudioChannels = mAudioChannels ? mAudioChannels : 2; +        mAudioBitRate = mAudioBitRate ? mAudioBitRate : 156000; +    } else if(mAudioEncoder == AUDIO_ENCODER_LPCM) { +        mSampleRate = mSampleRate ? mSampleRate : 48000; +        mAudioChannels = mAudioChannels ? mAudioChannels : 2; +        mAudioBitRate = mAudioBitRate ? mAudioBitRate : 4608000; +    } else if(mAudioEncoder == AUDIO_ENCODER_AMR_WB) { +        mSampleRate = 16000; +        mAudioChannels = 1; +        mAudioBitRate = 23850; +    } else { +        mSampleRate = mSampleRate ? mSampleRate : 8000; +        mAudioChannels = mAudioChannels ? mAudioChannels : 1; +        mAudioBitRate = mAudioBitRate ? mAudioBitRate : 12200; +    } +#endif      return OK;  } @@ -768,7 +796,15 @@ status_t StagefrightRecorder::start() {          case OUTPUT_FORMAT_MPEG2TS:              status = startMPEG2TSRecording();              break; +#ifdef QCOM_HARDWARE +        case OUTPUT_FORMAT_QCP: +            status = startExtendedRecording( ); +            break; +        case OUTPUT_FORMAT_WAVE: +            status = startWAVERecording( ); +            break; +#endif          default:              ALOGE("Unsupported output file format: %d", mOutputFormat);              status = UNKNOWN_ERROR; @@ -809,6 +845,11 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() {      sp<MetaData> encMeta = new MetaData;      const char *mime;      switch (mAudioEncoder) { +#ifdef QCOM_HARDWARE +        case AUDIO_ENCODER_LPCM: +            mime = MEDIA_MIMETYPE_AUDIO_RAW; +            break; +#endif          case AUDIO_ENCODER_AMR_NB:          case AUDIO_ENCODER_DEFAULT:              mime = MEDIA_MIMETYPE_AUDIO_AMR_NB; @@ -828,6 +869,14 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() {              mime = MEDIA_MIMETYPE_AUDIO_AAC;              encMeta->setInt32(kKeyAACProfile, OMX_AUDIO_AACObjectELD);              break; +#ifdef QCOM_HARDWARE +        case AUDIO_ENCODER_EVRC: +            mime = MEDIA_MIMETYPE_AUDIO_EVRC; +            break; +        case AUDIO_ENCODER_QCELP: +            mime = MEDIA_MIMETYPE_AUDIO_QCELP; +            break; +#endif          default:              ALOGE("Unknown audio encoder: %d", mAudioEncoder); @@ -852,6 +901,17 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() {      sp<MediaSource> audioEncoder =          OMXCodec::Create(client.interface(), encMeta,                           true /* createEncoder */, audioSource); +#ifdef QCOM_HARDWARE +    // If encoder could not be created (as in LPCM), then +    // use the AudioSource directly as the MediaSource. +    if (audioEncoder == NULL && AUDIO_ENCODER_LPCM) { +        ALOGD("No encoder is needed, use the AudioSource directly as the MediaSource for LPCM format"); +        audioEncoder = audioSource; +    } +    if (mAudioSourceNode != NULL) { +        mAudioSourceNode.clear(); +    } +#endif      mAudioSourceNode = audioSource;      return audioEncoder; @@ -888,13 +948,35 @@ status_t StagefrightRecorder::startAMRRecording() {                      mAudioEncoder);              return BAD_VALUE;          } +#ifdef QCOM_HARDWARE +        if (mSampleRate != 8000) { +            ALOGE("Invalid sampling rate %d used for AMRNB recording", +                    mSampleRate); +            return BAD_VALUE; +        } +#endif      } else {  // mOutputFormat must be OUTPUT_FORMAT_AMR_WB          if (mAudioEncoder != AUDIO_ENCODER_AMR_WB) {              ALOGE("Invlaid encoder %d used for AMRWB recording",                      mAudioEncoder);              return BAD_VALUE;          } +#ifdef QCOM_HARDWARE +        if (mSampleRate != 16000) { +            ALOGE("Invalid sample rate %d used for AMRWB recording", +                    mSampleRate); +            return BAD_VALUE; +        } +#endif +    } + +#ifdef QCOM_HARDWARE +    if (mAudioChannels != 1) { +        ALOGE("Invalid number of audio channels %d used for amr recording", +                mAudioChannels); +        return BAD_VALUE;      } +#endif      mWriter = new AMRWriter(mOutputFd);      status_t status = startRawAudioRecording(); @@ -905,6 +987,24 @@ status_t StagefrightRecorder::startAMRRecording() {      return status;  } +#ifdef QCOM_HARDWARE +status_t StagefrightRecorder::startWAVERecording() { +    CHECK(mOutputFormat == OUTPUT_FORMAT_WAVE); + +    CHECK(mAudioEncoder == AUDIO_ENCODER_LPCM); +    CHECK(mAudioSource != AUDIO_SOURCE_CNT); + +    mWriter = new WAVEWriter(mOutputFd); +    status_t status = startRawAudioRecording(); +    if (status != OK) { +        mWriter.clear(); +        mWriter = NULL; +    } + +    return status; +} +#endif +  status_t StagefrightRecorder::startRawAudioRecording() {      if (mAudioSource >= AUDIO_SOURCE_CNT) {          ALOGE("Invalid audio source: %d", mAudioSource); @@ -1450,6 +1550,9 @@ status_t StagefrightRecorder::setupAudioEncoder(const sp<MediaWriter>& writer) {          case AUDIO_ENCODER_AAC:          case AUDIO_ENCODER_HE_AAC:          case AUDIO_ENCODER_AAC_ELD: +#ifdef QCOM_HARDWARE +        case AUDIO_ENCODER_LPCM: +#endif              break;          default: @@ -1611,7 +1714,12 @@ status_t StagefrightRecorder::stop() {          ::close(mOutputFd);          mOutputFd = -1;      } - +#ifdef QCOM_HARDWARE +    if (mAudioSourceNode != NULL) { +        mAudioSourceNode.clear(); +        mAudioSourceNode = NULL; +    } +#endif      if (mStarted) {          mStarted = false; @@ -1653,9 +1761,15 @@ status_t StagefrightRecorder::reset() {      mVideoHeight   = 144;      mFrameRate     = -1;      mVideoBitRate  = 192000; +#ifdef QCOM_HARDWARE +    mSampleRate    = 0; +    mAudioChannels = 0; +    mAudioBitRate  = 0; +#else      mSampleRate    = 8000;      mAudioChannels = 1;      mAudioBitRate  = 12200; +#endif      mInterleaveDurationUs = 0;      mIFramesIntervalSec = 1;      mAudioSourceNode = 0; @@ -1767,4 +1881,48 @@ status_t StagefrightRecorder::dump(      ::write(fd, result.string(), result.size());      return OK;  } + +#ifdef QCOM_HARDWARE +status_t StagefrightRecorder::startExtendedRecording() { +    CHECK(mOutputFormat == OUTPUT_FORMAT_QCP); + +    if (mSampleRate != 8000) { +        ALOGE("Invalid sampling rate %d used for recording", +             mSampleRate); +        return BAD_VALUE; +    } +    if (mAudioChannels != 1) { +        ALOGE("Invalid number of audio channels %d used for recording", +                mAudioChannels); +        return BAD_VALUE; +    } + +    if (mAudioSource >= AUDIO_SOURCE_CNT) { +        ALOGE("Invalid audio source: %d", mAudioSource); +        return BAD_VALUE; +    } + +    sp<MediaSource> audioEncoder = createAudioSource(); + +    if (audioEncoder == NULL) { +        ALOGE("AudioEncoder NULL"); +        return UNKNOWN_ERROR; +    } + +    mWriter = new ExtendedWriter(dup(mOutputFd)); +    mWriter->addSource(audioEncoder); + +    if (mMaxFileDurationUs != 0) { +        mWriter->setMaxFileDuration(mMaxFileDurationUs); +    } +    if (mMaxFileSizeBytes != 0) { +        mWriter->setMaxFileSize(mMaxFileSizeBytes); +    } +    mWriter->setListener(mListener); +    mWriter->start(); + +    return OK; +} +#endif +  }  // namespace android diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index ec5ce7e..3f0b821 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -1,5 +1,6 @@  /*   * Copyright (C) 2009 The Android Open Source Project + * Copyright (c) 2012, The Linux Foundation. All rights reserved.   *   * Licensed under the Apache License, Version 2.0 (the "License");   * you may not use this file except in compliance with the License. @@ -132,6 +133,9 @@ private:      status_t startMPEG4Recording();      status_t startAMRRecording();      status_t startAACRecording(); +#ifdef QCOM_HARDWARE +    status_t startWAVERecording(); +#endif      status_t startRawAudioRecording();      status_t startRTPRecording();      status_t startMPEG2TSRecording(); @@ -187,6 +191,11 @@ private:      StagefrightRecorder(const StagefrightRecorder &);      StagefrightRecorder &operator=(const StagefrightRecorder &); + +#ifdef QCOM_HARDWARE +    /* extension */ +    status_t startExtendedRecording(); +#endif  };  }  // namespace android diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 7302692..35a5d05 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -1,6 +1,12 @@  LOCAL_PATH:= $(call my-dir)  include $(CLEAR_VARS) +ifeq ($(BOARD_USES_ALSA_AUDIO),true) +    ifeq ($(call is-chipset-in-board-platform,msm8960),true) +        LOCAL_CFLAGS += -DUSE_TUNNEL_MODE +    endif +endif +  include frameworks/av/media/libstagefright/codecs/common/Config.mk  LOCAL_SRC_FILES:=                         \ @@ -63,7 +69,27 @@ LOCAL_C_INCLUDES:= \          $(TOP)/frameworks/native/include/media/openmax \          $(TOP)/external/flac/include \          $(TOP)/external/tremolo \ -        $(TOP)/external/openssl/include \ +        $(TOP)/external/openssl/include + +ifeq ($(BOARD_USES_QCOM_HARDWARE),true) +LOCAL_SRC_FILES += \ +        ExtendedWriter.cpp                \ +        QCMediaDefs.cpp                   \ +        QCOMXCodec.cpp                    \ +        WAVEWriter.cpp                    \ +        ExtendedExtractor.cpp + +LOCAL_C_INCLUDES += \ +        $(TOP)/hardware/qcom/media/mm-core/inc + +ifeq ($(TARGET_QCOM_AUDIO_VARIANT),caf) +LOCAL_CFLAGS += -DQCOM_ENHANCED_AUDIO +LOCAL_SRC_FILES += \ +        LPAPlayerALSA.cpp                 \ +        TunnelPlayer.cpp +endif +endif +  LOCAL_SHARED_LIBRARIES := \          libbinder \ diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index 4208019..deb6b70 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -310,6 +310,13 @@ size_t AudioPlayer::AudioSinkCallback(          void *buffer, size_t size, void *cookie) {      AudioPlayer *me = (AudioPlayer *)cookie; +#ifdef QCOM_ENHANCED_AUDIO +    if (buffer == NULL) { +        //Not applicable for AudioPlayer +        ALOGE("This indicates the event underrun case for LPA/Tunnel"); +        return 0; +    } +#endif      return me->fillBuffer(buffer, size);  } diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 48b6371..5cffad8 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -2,6 +2,10 @@   * Copyright (C) 2009 The Android Open Source Project   * Copyright (c) 2012, The Linux Foundation. All rights reserved.   * + * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only. +   * 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 @@ -40,6 +44,12 @@  #include <media/stagefright/foundation/ADebug.h>  #include <media/stagefright/timedtext/TimedTextDriver.h>  #include <media/stagefright/AudioPlayer.h> +#ifdef QCOM_ENHANCED_AUDIO +#include <media/stagefright/LPAPlayer.h> +#ifdef USE_TUNNEL_MODE +#include <media/stagefright/TunnelPlayer.h> +#endif +#endif  #include <media/stagefright/DataSource.h>  #include <media/stagefright/FileSource.h>  #include <media/stagefright/MediaBuffer.h> @@ -65,6 +75,9 @@ static int64_t kLowWaterMarkUs = 2000000ll;  // 2secs  static int64_t kHighWaterMarkUs = 5000000ll;  // 5secs  static const size_t kLowWaterMarkBytes = 40000;  static const size_t kHighWaterMarkBytes = 200000; +#ifdef QCOM_ENHANCED_AUDIO +int AwesomePlayer::mTunnelAliveAP = 0; +#endif  struct AwesomeEvent : public TimedEventQueue::Event {      AwesomeEvent( @@ -215,6 +228,9 @@ AwesomePlayer::AwesomePlayer()      mAudioStatusEventPending = false;      reset(); +#ifdef QCOM_ENHANCED_AUDIO +    mIsTunnelAudio = false; +#endif  }  AwesomePlayer::~AwesomePlayer() { @@ -224,6 +240,17 @@ AwesomePlayer::~AwesomePlayer() {      reset(); +#ifdef QCOM_ENHANCED_AUDIO +    // Disable Tunnel Mode Audio +    if (mIsTunnelAudio) { +        if(mTunnelAliveAP > 0) { +            mTunnelAliveAP--; +            ALOGV("mTunnelAliveAP = %d", mTunnelAliveAP); +        } +    } +    mIsTunnelAudio = false; +#endif +      mClient.disconnect();  } @@ -858,6 +885,9 @@ status_t AwesomePlayer::play() {  }  status_t AwesomePlayer::play_l() { +#ifdef QCOM_ENHANCED_AUDIO +    int tunnelObjectsAlive = 0; +#endif      modifyFlags(SEEK_PREVIEW, CLEAR);      if (mFlags & PLAYING) { @@ -885,6 +915,13 @@ status_t AwesomePlayer::play_l() {      if (mAudioSource != NULL) {          if (mAudioPlayer == NULL) {              if (mAudioSink != NULL) { +#ifdef QCOM_ENHANCED_AUDIO +                sp<MetaData> format = mAudioTrack->getFormat(); +                const char *mime; +                bool success = format->findCString(kKeyMIMEType, &mime); +                CHECK(success); +#endif +                  bool allowDeepBuffering;                  int64_t cachedDurationUs;                  bool eos; @@ -896,8 +933,64 @@ status_t AwesomePlayer::play_l() {                  } else {                      allowDeepBuffering = false;                  } - -                mAudioPlayer = new AudioPlayer(mAudioSink, allowDeepBuffering, this); +#ifdef QCOM_ENHANCED_AUDIO +#ifdef USE_TUNNEL_MODE +                // Create tunnel player if tunnel mode is enabled +                ALOGW("Trying to create tunnel player mIsTunnelAudio %d, \ +                        LPAPlayer::objectsAlive %d, \ +                        TunnelPlayer::mTunnelObjectsAlive = %d,\ +                        (mAudioPlayer == NULL) %d", +                        mIsTunnelAudio, TunnelPlayer::mTunnelObjectsAlive, +                        LPAPlayer::objectsAlive,(mAudioPlayer == NULL)); + +                if(mIsTunnelAudio && (mAudioPlayer == NULL) && +                        (LPAPlayer::objectsAlive == 0) && +                        (TunnelPlayer::mTunnelObjectsAlive == 0)) { +                    ALOGD("Tunnel player created for  mime %s duration %lld\n",\ +                        mime, mDurationUs); +                    bool initCheck =  false; +                    if(mVideoSource != NULL) { +                        // The parameter true is to inform tunnel player that +                        // clip is audio video +                        mAudioPlayer = new TunnelPlayer(mAudioSink, initCheck, +                                this, true); +                    } +                    else { +                        mAudioPlayer = new TunnelPlayer(mAudioSink, initCheck, +                                this); +                    } +                    if(!initCheck) { +                        ALOGE("deleting Tunnel Player - initCheck failed"); +                        delete mAudioPlayer; +                        mAudioPlayer = NULL; +                    } +                } +                tunnelObjectsAlive = (TunnelPlayer::mTunnelObjectsAlive); +#endif +                char lpaDecode[128]; +                property_get("lpa.decode",lpaDecode,"0"); +                if((strcmp("true",lpaDecode) == 0) && (mAudioPlayer == NULL) && tunnelObjectsAlive==0 ) +                { +                    ALOGV("LPAPlayer::getObjectsAlive() %d",LPAPlayer::objectsAlive); +                    if ( mDurationUs > 60000000 +                         && (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG) || !strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_AAC)) +                         && LPAPlayer::objectsAlive == 0 && mVideoSource == NULL) { +                        ALOGD("LPAPlayer created, LPA MODE detected mime %s duration %lld", mime, mDurationUs); +                        bool initCheck =  false; +                        mAudioPlayer = new LPAPlayer(mAudioSink, initCheck, this); +                        if(!initCheck) { +                             delete mAudioPlayer; +                             mAudioPlayer = NULL; +                        } +                    } +                } +                if(mAudioPlayer == NULL) { +                    ALOGV("AudioPlayer created, Non-LPA mode mime %s duration %lld\n", mime, mDurationUs); +#endif +                    mAudioPlayer = new AudioPlayer(mAudioSink, allowDeepBuffering, this); +#ifdef QCOM_ENHANCED_AUDIO +                } +#endif                  mAudioPlayer->setSource(mAudioSource);                  mTimeSource = mAudioPlayer; @@ -915,9 +1008,14 @@ status_t AwesomePlayer::play_l() {          if (mVideoSource == NULL) {              // We don't want to post an error notification at this point,              // the error returned from MediaPlayer::start() will suffice. - -            status_t err = startAudioPlayer_l( -                    false /* sendErrorNotification */); +            bool sendErrorNotification = false; +#ifdef QCOM_ENHANCED_AUDIO +            if(mIsTunnelAudio) { +                // For tunnel Audio error has to be posted to the client +                sendErrorNotification = true; +            } +#endif +            status_t err = startAudioPlayer_l(sendErrorNotification);              if (err != OK) {                  delete mAudioPlayer; @@ -1387,14 +1485,92 @@ status_t AwesomePlayer::initAudioDecoder() {      const char *mime;      CHECK(meta->findCString(kKeyMIMEType, &mime)); - +#ifdef QCOM_ENHANCED_AUDIO +#ifdef USE_TUNNEL_MODE +    char value[PROPERTY_VALUE_MAX]; +    char tunnelDecode[128]; +    property_get("tunnel.decode",tunnelDecode,"0"); +    // Enable tunnel mode for mp3 and aac and if the clip is not aac adif +    // and if no other tunnel mode instances aare running. +    ALOGD("Tunnel Mime Type: %s, object alive = %d, mTunnelAliveAP = %d",\ +            mime, (TunnelPlayer::mTunnelObjectsAlive), mTunnelAliveAP); +    if(((strcmp("true",tunnelDecode) == 0)||(atoi(tunnelDecode))) && +            (TunnelPlayer::mTunnelObjectsAlive == 0) && +            mTunnelAliveAP == 0 && +            ((!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) || +            (!strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_AAC)))) { + +        if(mVideoSource != NULL) { +           char tunnelAVDecode[128]; +           property_get("tunnel.audiovideo.decode",tunnelAVDecode,"0"); +           if(((strncmp("true", tunnelAVDecode, 4) == 0)||(atoi(tunnelAVDecode)))) { +               ALOGD("Enable Tunnel Mode for A-V playback"); +               mIsTunnelAudio = true; +           } +        } +        else { +            ALOGI("Tunnel Mode Audio Enabled"); +            mIsTunnelAudio = true; +        } +    } +    else +       ALOGD("Normal Audio Playback"); +#endif +    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW) || +             (mIsTunnelAudio && (mTunnelAliveAP == 0))) { +        ALOGD("Set Audio Track as Audio Source"); +        if(mIsTunnelAudio) { +            mTunnelAliveAP++; +        } +#else      if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) { +#endif          mAudioSource = mAudioTrack;      } else { +#ifdef QCOM_ENHANCED_AUDIO +        // For LPA Playback use the decoder without OMX layer +        char *matchComponentName = NULL; +        int64_t durationUs; +        uint32_t flags = 0; +        char lpaDecode[128]; +        property_get("lpa.decode",lpaDecode,"0"); +        if (mAudioTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) { +            Mutex::Autolock autoLock(mMiscStateLock); +            if (mDurationUs < 0 || durationUs > mDurationUs) { +                mDurationUs = durationUs; +            } +        } +        if ( mDurationUs > 60000000 +             && (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG) || !strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_AAC)) +             && LPAPlayer::objectsAlive == 0 && mVideoSource == NULL && (strcmp("true",lpaDecode) == 0)) { +            char nonOMXDecoder[128]; +            if(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) { +                ALOGD("matchComponentName is set to MP3Decoder %lld, mime %s",mDurationUs,mime); +                property_get("use.non-omx.mp3.decoder",nonOMXDecoder,"0"); +                if((strcmp("true",nonOMXDecoder) == 0)) { +                    matchComponentName = (char *) "MP3Decoder"; +                } +            } else if((!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC))) { +                ALOGD("matchComponentName is set to AACDecoder %lld, mime %s",mDurationUs,mime); +                property_get("use.non-omx.aac.decoder",nonOMXDecoder,"0"); +                if((strcmp("true",nonOMXDecoder) == 0)) { +                    matchComponentName = (char *) "AACDecoder"; +                } else { +                    matchComponentName = (char *) "OMX.google.aac.decoder"; +                } +            } +            flags |= OMXCodec::kSoftwareCodecsOnly; +        } +        mAudioSource = OMXCodec::Create( +                mClient.interface(), mAudioTrack->getFormat(), +                false, // createEncoder +                mAudioTrack, matchComponentName, flags,NULL); +#else          mAudioSource = OMXCodec::Create(                  mClient.interface(), mAudioTrack->getFormat(),                  false, // createEncoder                  mAudioTrack); +#endif      }      if (mAudioSource != NULL) { diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index 9d0eea2..4ab602f 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -33,6 +33,9 @@  #include "include/OggExtractor.h"  #include "include/WAVExtractor.h"  #include "include/WVMExtractor.h" +#ifdef QCOM_HARDWARE +#include "include/ExtendedExtractor.h" +#endif  #include "matroska/MatroskaExtractor.h" @@ -122,6 +125,9 @@ void DataSource::RegisterDefaultSniffers() {      RegisterSniffer(SniffAAC);      RegisterSniffer(SniffMPEG2PS);      RegisterSniffer(SniffWVM); +#ifdef QCOM_HARDWARE +    RegisterSniffer(SniffExtendedExtractor); +#endif      char value[PROPERTY_VALUE_MAX];      if (property_get("drm.service.enabled", value, NULL) diff --git a/media/libstagefright/ExtendedExtractor.cpp b/media/libstagefright/ExtendedExtractor.cpp new file mode 100644 index 0000000..8e0d5d7 --- /dev/null +++ b/media/libstagefright/ExtendedExtractor.cpp @@ -0,0 +1,110 @@ +/*Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + *     * Redistributions of source code must retain the above copyright + *      notice, this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above + *       copyright notice, this list of conditions and the following + *       disclaimer in the documentation and/or other materials provided + *      with the distribution. + *     * Neither the name of The Linux Foundation nor the names of its + *      contributors may be used to endorse or promote products derived + *       from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ExtendedExtractor" +#include <utils/Log.h> +#include <dlfcn.h>  // for dlopen/dlclose + +#include "include/ExtendedExtractor.h" + +static const char* EXTENDED_PARSER_LIB = "libExtendedExtractor.so"; + +namespace android { + +void* ExtendedParserLib() { +    static void* extendedParserLib = NULL; +    static bool alreadyTriedToOpenParsers = false; + +    if(!alreadyTriedToOpenParsers) { +        alreadyTriedToOpenParsers = true; + +        extendedParserLib = ::dlopen(EXTENDED_PARSER_LIB, RTLD_LAZY); + +        if(extendedParserLib == NULL) { +            ALOGV("Failed to open EXTENDED_PARSER_LIB, dlerror = %s \n", dlerror()); +        } +    } + +    return extendedParserLib; +} + +MediaExtractor* ExtendedExtractor::CreateExtractor(const sp<DataSource> &source, const char *mime) { +    static MediaExtractorFactory mediaFactoryFunction = NULL; +    static bool alreadyTriedToFindFactoryFunction = false; + +    MediaExtractor* extractor = NULL; + +    if(!alreadyTriedToFindFactoryFunction) { + +        void *extendedParserLib = ExtendedParserLib(); +        if (extendedParserLib != NULL) { + +            mediaFactoryFunction = (MediaExtractorFactory) dlsym(extendedParserLib, MEDIA_CREATE_EXTRACTOR); +            alreadyTriedToFindFactoryFunction = true; +        } +    } + +    if(mediaFactoryFunction==NULL) { +        ALOGE(" dlsym for ExtendedExtractor factory function failed, dlerror = %s \n", dlerror()); +        return NULL; +    } + +    extractor = mediaFactoryFunction(source, mime); +    if(extractor==NULL) { +        ALOGE(" ExtendedExtractor failed to instantiate extractor \n"); +    } + +    return extractor; +} + +bool SniffExtendedExtractor(const sp<DataSource> &source, String8 *mimeType, +                            float *confidence,sp<AMessage> *meta) { +    void *extendedParserLib = ExtendedParserLib(); +    bool retVal = false; +    if (extendedParserLib != NULL) { +       ExtendedExtractorSniffers extendedExtractorSniffers= +           (ExtendedExtractorSniffers) dlsym(extendedParserLib, EXTENDED_EXTRACTOR_SNIFFERS); + +       if(extendedExtractorSniffers == NULL) { +           ALOGE(" dlsym for extendedExtractorSniffers function failed, dlerror = %s \n", dlerror()); +           return retVal; +       } + +       retVal = extendedExtractorSniffers(source, mimeType, confidence, meta); + +       if(!retVal) { +           ALOGV("ExtendedExtractor:: ExtendedExtractorSniffers  Failed"); +       } +    } +    return retVal; +} + +}  // namespace android + + diff --git a/media/libstagefright/ExtendedWriter.cpp b/media/libstagefright/ExtendedWriter.cpp new file mode 100644 index 0000000..7c8b08e --- /dev/null +++ b/media/libstagefright/ExtendedWriter.cpp @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only. + * 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 <media/stagefright/ExtendedWriter.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> +#include <media/mediarecorder.h> +#include <system/audio.h> + +#include <sys/prctl.h> +#include <sys/resource.h> + +#include <arpa/inet.h> +#include <QCMediaDefs.h> + +#undef LOG_TAG +#define LOG_TAG "ExtendedWriter" + +namespace android { + +ExtendedWriter::ExtendedWriter(const char *filename) +    : mFile(fopen(filename, "wb")), +      mInitCheck(mFile != NULL ? OK : NO_INIT), +      mStarted(false), +      mPaused(false), +      mResumed(false), +      mOffset(0) { +} + +ExtendedWriter::ExtendedWriter(int fd) +    : mFile(fdopen(fd, "wb")), +      mInitCheck(mFile != NULL ? OK : NO_INIT), +      mStarted(false), +      mPaused(false), +      mResumed(false), +      mOffset(0) { +} + +ExtendedWriter::~ExtendedWriter() { +    if (mStarted) { +        stop(); +    } + +    if (mFile != NULL) { +        fclose(mFile); +        mFile = NULL; +    } +} + +status_t ExtendedWriter::initCheck() const { +    return mInitCheck; +} + +status_t ExtendedWriter::addSource(const sp<MediaSource> &source) { +    if (mInitCheck != OK) { +        ALOGE("Init Check not OK, return"); +        return mInitCheck; +    } + +    if (mSource != NULL) { +        ALOGE("A source already exists, return"); +        return UNKNOWN_ERROR; +    } + +    sp<MetaData> meta = source->getFormat(); + +    const char *mime; +    CHECK(meta->findCString(kKeyMIMEType, &mime)); + +    if ( !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_QCELP)) { +        mFormat = AUDIO_FORMAT_QCELP; +    } else if ( !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_EVRC)) { +        mFormat = AUDIO_FORMAT_EVRC; +    } +    else { +        return UNKNOWN_ERROR; +    } + +    int32_t channelCount; +    int32_t sampleRate; +    CHECK(meta->findInt32(kKeyChannelCount, &channelCount)); +    CHECK_EQ(channelCount, 1); +    CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); +    CHECK_EQ(sampleRate, 8000); + +    mSource = source; + +    return OK; +} + +status_t ExtendedWriter::start(MetaData *params) { +    if (mInitCheck != OK) { +        ALOGE("Init Check not OK, return"); +        return mInitCheck; +    } + +    if (mSource == NULL) { +        ALOGE("NULL Source"); +        return UNKNOWN_ERROR; +    } + +    if (mStarted && mPaused) { +        mPaused = false; +        mResumed = true; +        return OK; +    } else if (mStarted) { +        ALOGE("Already startd, return"); +        return OK; +    } + +    //space for header; +    size_t headerSize = sizeof( struct QCPEVRCHeader ); +    uint8_t * header = (uint8_t *)malloc(headerSize); +    memset( header, '?', headerSize); +    fwrite( header, 1, headerSize, mFile ); +    mOffset += headerSize; +    delete header; + +    status_t err = mSource->start(); + +    if (err != OK) { +        return err; +    } + +    pthread_attr_t attr; +    pthread_attr_init(&attr); +    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + +    mReachedEOS = false; +    mDone = false; + +    pthread_create(&mThread, &attr, ThreadWrapper, this); +    pthread_attr_destroy(&attr); + +    mStarted = true; + +    return OK; +} + +status_t ExtendedWriter::pause() { +    if (!mStarted) { +        return OK; +    } +    mPaused = true; +    return OK; +} + +status_t ExtendedWriter::stop() { +    if (!mStarted) { +        return OK; +    } + +    mDone = true; + +    void *dummy; +    pthread_join(mThread, &dummy); + +    status_t err = (status_t) dummy; +    { +        status_t status = mSource->stop(); +        if (err == OK && +            (status != OK && status != ERROR_END_OF_STREAM)) { +            err = status; +        } +    } + +    mStarted = false; +    return err; +} + +bool ExtendedWriter::exceedsFileSizeLimit() { +    if (mMaxFileSizeLimitBytes == 0) { +        return false; +    } +    return mEstimatedSizeBytes >= mMaxFileSizeLimitBytes; +} + +bool ExtendedWriter::exceedsFileDurationLimit() { +    if (mMaxFileDurationLimitUs == 0) { +        return false; +    } +    return mEstimatedDurationUs >= mMaxFileDurationLimitUs; +} + +// static +void *ExtendedWriter::ThreadWrapper(void *me) { +    return (void *) static_cast<ExtendedWriter *>(me)->threadFunc(); +} + +status_t ExtendedWriter::threadFunc() { +    mEstimatedDurationUs = 0; +    mEstimatedSizeBytes = 0; +    bool stoppedPrematurely = true; +    int64_t previousPausedDurationUs = 0; +    int64_t maxTimestampUs = 0; +    status_t err = OK; + +    pid_t tid  = gettid(); +    androidSetThreadPriority(tid, ANDROID_PRIORITY_AUDIO); +    prctl(PR_SET_NAME, (unsigned long)"ExtendedWriter", 0, 0, 0); +    while (!mDone) { +        MediaBuffer *buffer; +        err = mSource->read(&buffer); + +        if (err != OK) { +            break; +        } + +        if (mPaused) { +            buffer->release(); +            buffer = NULL; +            continue; +        } + +        mEstimatedSizeBytes += buffer->range_length(); +        if (exceedsFileSizeLimit()) { +            buffer->release(); +            buffer = NULL; +            notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0); +            break; +        } + +        int64_t timestampUs; +        CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs)); +        if (timestampUs > mEstimatedDurationUs) { +            mEstimatedDurationUs = timestampUs; +        } +        if (mResumed) { +            previousPausedDurationUs += (timestampUs - maxTimestampUs - 20000); +            mResumed = false; +        } +        timestampUs -= previousPausedDurationUs; +        ALOGV("time stamp: %lld, previous paused duration: %lld", +                timestampUs, previousPausedDurationUs); +        if (timestampUs > maxTimestampUs) { +            maxTimestampUs = timestampUs; +        } + +        if (exceedsFileDurationLimit()) { +            buffer->release(); +            buffer = NULL; +            notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0); +            break; +        } +        ssize_t n = fwrite( +                (const uint8_t *)buffer->data() + buffer->range_offset(), +                1, +                buffer->range_length(), +                mFile); +        mOffset += n; + +        if (n < (ssize_t)buffer->range_length()) { +            buffer->release(); +            buffer = NULL; + +            break; +        } + +        // XXX: How to tell it is stopped prematurely? +        if (stoppedPrematurely) { +            stoppedPrematurely = false; +        } + +        buffer->release(); +        buffer = NULL; +    } + +    if (stoppedPrematurely) { +        notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS, UNKNOWN_ERROR); +    } + +    if ( mFormat == AUDIO_FORMAT_QCELP ) { +        writeQCPHeader( ); +    } +    else if ( mFormat == AUDIO_FORMAT_EVRC ) { +        writeEVRCHeader( ); +    } + +    fflush(mFile); +    fclose(mFile); +    mFile = NULL; +    mReachedEOS = true; +    if (err == ERROR_END_OF_STREAM || (err == -ETIMEDOUT)) { +        return OK; +    } +    return err; +} + +bool ExtendedWriter::reachedEOS() { +    return mReachedEOS; +} + +status_t ExtendedWriter::writeQCPHeader() { +    /* Common part */ +    struct QCPEVRCHeader header = { +        {'R', 'I', 'F', 'F'}, 0, {'Q', 'L', 'C', 'M'}, /* Riff */ +        {'f', 'm', 't', ' '}, 150, 1, 0, 0, 0, 0,{0}, 0, {0},0,0,160,8000,16,0,{0},{0},{0}, /* Fmt */ +        {'v','r','a','t'}, 0, 0, 0, /* Vrat */ +        {'d','a','t','a'},0 /* Data */ +    }; + +    fseeko(mFile, 0, SEEK_SET); +    header.s_riff = (mOffset - 8); +    header.data1 = (0x5E7F6D41); +    header.data2 = (0xB115); +    header.data3 = (0x11D0); +    header.data4[0] = 0xBA; +    header.data4[1] = 0x91; +    header.data4[2] = 0x00; +    header.data4[3] = 0x80; +    header.data4[4] = 0x5F; +    header.data4[5] = 0xB4; +    header.data4[6] = 0xB9; +    header.data4[7] = 0x7E; +    header.ver = (0x0002); +    memcpy(header.name, "Qcelp 13K", 9); +    header.abps = (13000); +    header.bytes_per_pkt = (35); +    header.vr_num_of_rates = 5; +    header.vr_bytes_per_pkt[0] = (0x0422); +    header.vr_bytes_per_pkt[1] = (0x0310); +    header.vr_bytes_per_pkt[2] = (0x0207); +    header.vr_bytes_per_pkt[3] = (0x0103); +    header.s_vrat = (0x00000008); +    header.v_rate = (0x00000001); +    header.size_in_pkts = (mOffset - sizeof( struct QCPEVRCHeader ))/ header.bytes_per_pkt; +    header.s_data = mOffset - sizeof( struct QCPEVRCHeader ); +    fwrite( &header, 1, sizeof( struct QCPEVRCHeader ), mFile ); +    return OK; +} + +status_t ExtendedWriter::writeEVRCHeader() { +    /* Common part */ +    struct QCPEVRCHeader header = { +        {'R', 'I', 'F', 'F'}, 0, {'Q', 'L', 'C', 'M'}, /* Riff */ +        {'f', 'm', 't', ' '}, 150, 1, 0, 0, 0, 0,{0}, 0, {0},0,0,160,8000,16,0,{0},{0},{0}, /* Fmt */ +        {'v','r','a','t'}, 0, 0, 0, /* Vrat */ +        {'d','a','t','a'},0 /* Data */ +    }; + +    fseeko(mFile, 0, SEEK_SET); +    header.s_riff = (mOffset - 8); +    header.data1 = (0xe689d48d); +    header.data2 = (0x9076); +    header.data3 = (0x46b5); +    header.data4[0] = 0x91; +    header.data4[1] = 0xef; +    header.data4[2] = 0x73; +    header.data4[3] = 0x6a; +    header.data4[4] = 0x51; +    header.data4[5] = 0x00; +    header.data4[6] = 0xce; +    header.data4[7] = 0xb4; +    header.ver = (0x0001); +    memcpy(header.name, "TIA IS-127 Enhanced Variable Rate Codec, Speech Service Option 3", 64); +    header.abps = (9600); +    header.bytes_per_pkt = (23); +    header.vr_num_of_rates = 4; +    header.vr_bytes_per_pkt[0] = (0x0416); +    header.vr_bytes_per_pkt[1] = (0x030a); +    header.vr_bytes_per_pkt[2] = (0x0200); +    header.vr_bytes_per_pkt[3] = (0x0102); +    header.s_vrat = (0x00000008); +    header.v_rate = (0x00000001); +    header.size_in_pkts = (mOffset - sizeof( struct QCPEVRCHeader )) / header.bytes_per_pkt; +    header.s_data = mOffset - sizeof( struct QCPEVRCHeader ); +    fwrite( &header, 1, sizeof( struct QCPEVRCHeader ), mFile ); +    return OK; +} + + +}  // namespace android diff --git a/media/libstagefright/LPAPlayerALSA.cpp b/media/libstagefright/LPAPlayerALSA.cpp new file mode 100644 index 0000000..db3a022 --- /dev/null +++ b/media/libstagefright/LPAPlayerALSA.cpp @@ -0,0 +1,791 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved. + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only. + * + * + * 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_NDDEBUG 0 +#define LOG_NDEBUG 0 +#define LOG_TAG "LPAPlayerALSA" + +#include <utils/Log.h> +#include <utils/threads.h> + +#include <signal.h> +#include <sys/prctl.h> +#include <sys/resource.h> +#include <sys/poll.h> +#include <sys/eventfd.h> +#include <binder/IPCThreadState.h> +#include <media/AudioTrack.h> + +#include <media/stagefright/LPAPlayer.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/ALooper.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/MediaErrors.h> + +#include <hardware_legacy/power.h> + +#include <linux/unistd.h> + +#include "include/AwesomePlayer.h" +#include <powermanager/PowerManager.h> + +static const char   mName[] = "LPAPlayer"; + +#define MEM_BUFFER_SIZE 262144 +#define MEM_BUFFER_COUNT 4 + +#define PCM_FORMAT 2 +#define NUM_FDS 2 +namespace android { +int LPAPlayer::objectsAlive = 0; + +LPAPlayer::LPAPlayer( +                    const sp<MediaPlayerBase::AudioSink> &audioSink, bool &initCheck, +                    AwesomePlayer *observer) +:AudioPlayer(audioSink,observer), +mPositionTimeMediaUs(-1), +mPositionTimeRealUs(-1), +mInternalSeeking(false), +mStarted(false), +mA2DPEnabled(false), +mSampleRate(0), +mLatencyUs(0), +mFrameSize(0), +mNumFramesPlayed(0), +mNumFramesPlayedSysTimeUs(0), +mInputBuffer(NULL), +mSeeking(false), +mReachedEOS(false), +mReachedOutputEOS(false), +mFinalStatus(OK), +mSeekTimeUs(0), +mPauseTime(0), +mIsFirstBuffer(false), +mFirstBufferResult(OK), +mFirstBuffer(NULL), +mAudioSink(audioSink), +mObserver(observer) { +    ALOGV("LPAPlayer::LPAPlayer() ctor"); +    objectsAlive++; +    numChannels =0; +    mPaused = false; +    mIsA2DPEnabled = false; +    mAudioFlinger = NULL; +    AudioFlingerClient = NULL; +    /* Initialize Suspend/Resume related variables */ +    mQueue.start(); +    mQueueStarted      = true; +    mPauseEvent        = new TimedEvent(this, &LPAPlayer::onPauseTimeOut); +    mPauseEventPending = false; +    getAudioFlinger(); +    ALOGV("Registering client with AudioFlinger"); +    //mAudioFlinger->registerClient(AudioFlingerClient); + +    mIsAudioRouted = false; + +    initCheck = true; + +} + +LPAPlayer::~LPAPlayer() { +    ALOGV("LPAPlayer::~LPAPlayer()"); +    if (mQueueStarted) { +        mQueue.stop(); +    } + +    reset(); + +    //mAudioFlinger->deregisterClient(AudioFlingerClient); +    objectsAlive--; +} + +void LPAPlayer::getAudioFlinger() { +    Mutex::Autolock _l(AudioFlingerLock); + +    if ( mAudioFlinger.get() == 0 ) { +        sp<IServiceManager> sm = defaultServiceManager(); +        sp<IBinder> binder; +        do { +            binder = sm->getService(String16("media.audio_flinger")); +            if ( binder != 0 ) +                break; +            ALOGW("AudioFlinger not published, waiting..."); +            usleep(500000); // 0.5 s +        } while ( true ); +        if ( AudioFlingerClient == NULL ) { +            AudioFlingerClient = new AudioFlingerLPAdecodeClient(this); +        } + +        binder->linkToDeath(AudioFlingerClient); +        mAudioFlinger = interface_cast<IAudioFlinger>(binder); +    } +    ALOGE_IF(mAudioFlinger==0, "no AudioFlinger!?"); +} + +LPAPlayer::AudioFlingerLPAdecodeClient::AudioFlingerLPAdecodeClient(void *obj) +{ +    ALOGV("LPAPlayer::AudioFlingerLPAdecodeClient::AudioFlingerLPAdecodeClient"); +    pBaseClass = (LPAPlayer*)obj; +} + +void LPAPlayer::AudioFlingerLPAdecodeClient::binderDied(const wp<IBinder>& who) { +    Mutex::Autolock _l(pBaseClass->AudioFlingerLock); + +    pBaseClass->mAudioFlinger.clear(); +    ALOGW("AudioFlinger server died!"); +} + +void LPAPlayer::AudioFlingerLPAdecodeClient::ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2) { +    ALOGV("ioConfigChanged() event %d", event); +    /* +    if ( event != AudioSystem::A2DP_OUTPUT_STATE && +         event != AudioSystem::EFFECT_CONFIG_CHANGED) { +        return; +    } + +    switch ( event ) { +    case AudioSystem::A2DP_OUTPUT_STATE: +        { +            ALOGV("ioConfigChanged() A2DP_OUTPUT_STATE iohandle is %d with A2DPEnabled in %d", ioHandle, pBaseClass->mIsA2DPEnabled); +            if ( -1 == ioHandle ) { +                if ( pBaseClass->mIsA2DPEnabled ) { +                    pBaseClass->mIsA2DPEnabled = false; +                    if (pBaseClass->mStarted) { +                        pBaseClass->handleA2DPSwitch(); +                    } +                    ALOGV("ioConfigChanged:: A2DP Disabled"); +                } +            } else { +                if ( !pBaseClass->mIsA2DPEnabled ) { + +                    pBaseClass->mIsA2DPEnabled = true; +                    if (pBaseClass->mStarted) { +                        pBaseClass->handleA2DPSwitch(); +                    } + +                    ALOGV("ioConfigChanged:: A2DP Enabled"); +                } +            } +        } +        break; +    } +    ALOGV("ioConfigChanged Out"); +    */ +} + +void LPAPlayer::handleA2DPSwitch() { +    //TODO: Implement +} + +void LPAPlayer::setSource(const sp<MediaSource> &source) { +    CHECK(mSource == NULL); +    ALOGV("Setting source from LPA Player"); +    mSource = source; +} + +status_t LPAPlayer::start(bool sourceAlreadyStarted) { +    CHECK(!mStarted); +    CHECK(mSource != NULL); + +    ALOGV("start: sourceAlreadyStarted %d", sourceAlreadyStarted); +    //Check if the source is started, start it +    status_t err; +    if (!sourceAlreadyStarted) { +        err = mSource->start(); + +        if (err != OK) { +            return err; +        } +    } + +    //Create decoder and a2dp notification thread and initialize all the +    //mutexes and coditional variables +    createThreads(); +    ALOGV("All Threads Created."); + +    // We allow an optional INFO_FORMAT_CHANGED at the very beginning +    // of playback, if there is one, getFormat below will retrieve the +    // updated format, if there isn't, we'll stash away the valid buffer +    // of data to be used on the first audio callback. + +    CHECK(mFirstBuffer == NULL); + +    MediaSource::ReadOptions options; +    if (mSeeking) { +        options.setSeekTo(mSeekTimeUs); +        mSeeking = false; +    } + +    mFirstBufferResult = mSource->read(&mFirstBuffer, &options); +    if (mFirstBufferResult == INFO_FORMAT_CHANGED) { +        ALOGV("INFO_FORMAT_CHANGED!!!"); +        CHECK(mFirstBuffer == NULL); +        mFirstBufferResult = OK; +        mIsFirstBuffer = false; +    } else { +        mIsFirstBuffer = true; +    } + +    sp<MetaData> format = mSource->getFormat(); +    const char *mime; + +    bool success = format->findCString(kKeyMIMEType, &mime); +    CHECK(success); +    CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)); + +    success = format->findInt32(kKeySampleRate, &mSampleRate); +    CHECK(success); + +    success = format->findInt32(kKeyChannelCount, &numChannels); +    CHECK(success); + +    if(!format->findInt32(kKeyChannelMask, &mChannelMask)) { +        // log only when there's a risk of ambiguity of channel mask selection +        ALOGI_IF(numChannels > 2, +                "source format didn't specify channel mask, using (%d) channel order", numChannels); +        mChannelMask = CHANNEL_MASK_USE_CHANNEL_ORDER; +    } +    audio_output_flags_t flags = (audio_output_flags_t) (AUDIO_OUTPUT_FLAG_LPA | +                                                         AUDIO_OUTPUT_FLAG_DIRECT); +    ALOGV("mAudiosink->open() mSampleRate %d, numChannels %d, mChannelMask %d, flags %d",mSampleRate, numChannels, mChannelMask, flags); +    err = mAudioSink->open( +        mSampleRate, numChannels, mChannelMask, AUDIO_FORMAT_PCM_16_BIT, +        DEFAULT_AUDIOSINK_BUFFERCOUNT, +        &LPAPlayer::AudioSinkCallback, +        this, +        (mA2DPEnabled ? AUDIO_OUTPUT_FLAG_NONE : flags)); + +    if (err != OK) { +        if (mFirstBuffer != NULL) { +            mFirstBuffer->release(); +            mFirstBuffer = NULL; +        } + +        if (!sourceAlreadyStarted) { +            mSource->stop(); +        } + +        ALOGE("Opening a routing session failed"); +        return err; +    } + +    mIsAudioRouted = true; +    mStarted = true; +    mAudioSink->start(); +    ALOGV("Waking up decoder thread"); +    pthread_cond_signal(&decoder_cv); + +    return OK; +} + +status_t LPAPlayer::seekTo(int64_t time_us) { +    Mutex::Autolock autoLock(mLock); +    ALOGV("seekTo: time_us %lld", time_us); +    if ( mReachedEOS ) { +        mReachedEOS = false; +        mReachedOutputEOS = false; +    } +    mSeeking = true; +    mSeekTimeUs = time_us; +    mPauseTime = mSeekTimeUs; +    ALOGV("In seekTo(), mSeekTimeUs %lld",mSeekTimeUs); +    mAudioSink->flush(); +    pthread_cond_signal(&decoder_cv); +    return OK; +} + +void LPAPlayer::pause(bool playPendingSamples) { +    CHECK(mStarted); +    if (mPaused) { +        return; +    } +    ALOGV("pause: playPendingSamples %d", playPendingSamples); +    mPaused = true; +    A2DPState state; +    if (playPendingSamples) { +        if (!mIsA2DPEnabled) { +            if (!mPauseEventPending) { +                ALOGV("Posting an event for Pause timeout"); +                mQueue.postEventWithDelay(mPauseEvent, LPA_PAUSE_TIMEOUT_USEC); +                mPauseEventPending = true; +            } +            mPauseTime = mSeekTimeUs + getTimeStamp(A2DP_DISABLED); +        } +        else { +            mPauseTime = mSeekTimeUs + getTimeStamp(A2DP_ENABLED); +        } +        if (mAudioSink.get() != NULL) +            mAudioSink->pause(); +    } else { +        if (!mIsA2DPEnabled) { +            if(!mPauseEventPending) { +                ALOGV("Posting an event for Pause timeout"); +                mQueue.postEventWithDelay(mPauseEvent, LPA_PAUSE_TIMEOUT_USEC); +                mPauseEventPending = true; +            } +            mPauseTime = mSeekTimeUs + getTimeStamp(A2DP_DISABLED); +        } else { +            mPauseTime = mSeekTimeUs + getTimeStamp(A2DP_ENABLED); +        } +        if (mAudioSink.get() != NULL) { +            ALOGV("AudioSink pause"); +            mAudioSink->pause(); +        } +    } +} + +void LPAPlayer::resume() { +    ALOGV("resume: mPaused %d",mPaused); +    Mutex::Autolock autoLock(mResumeLock); +    if ( mPaused) { +        CHECK(mStarted); +        if (!mIsA2DPEnabled) { +            if(mPauseEventPending) { +                ALOGV("Resume(): Cancelling the puaseTimeout event"); +                mPauseEventPending = false; +                mQueue.cancelEvent(mPauseEvent->eventID()); +            } + +        } + +        if (!mIsAudioRouted) { +            audio_output_flags_t flags = (audio_output_flags_t) (AUDIO_OUTPUT_FLAG_LPA | +                                                                AUDIO_OUTPUT_FLAG_DIRECT); +            status_t err = mAudioSink->open( +                mSampleRate, numChannels, mChannelMask, AUDIO_FORMAT_PCM_16_BIT, +                DEFAULT_AUDIOSINK_BUFFERCOUNT, +                &LPAPlayer::AudioSinkCallback, +                this, +                (mA2DPEnabled ?  AUDIO_OUTPUT_FLAG_NONE : flags )); +            if (err != NO_ERROR) { +                ALOGE("Audio sink open failed."); +            } +            mIsAudioRouted = true; +        } +        mPaused = false; +        mAudioSink->start(); +        pthread_cond_signal(&decoder_cv); +    } +} + +//static +size_t LPAPlayer::AudioSinkCallback( +        MediaPlayerBase::AudioSink *audioSink, +        void *buffer, size_t size, void *cookie) { +    if (buffer == NULL && size == AudioTrack::EVENT_UNDERRUN) { +        LPAPlayer *me = (LPAPlayer *)cookie; +        me->mReachedEOS = true; +        me->mReachedOutputEOS = true; +        ALOGV("postAudioEOS"); +        me->mObserver->postAudioEOS(0); +    } +    return 1; +} + +void LPAPlayer::reset() { + +    ALOGV("Reset"); +    // Close the audiosink after all the threads exited to make sure +    mReachedEOS = true; + +    // make sure Decoder thread has exited +    ALOGV("Closing all the threads"); +    requestAndWaitForDecoderThreadExit(); +    requestAndWaitForA2DPNotificationThreadExit(); + +    ALOGV("Close the Sink"); +    if (mIsAudioRouted) { +	    mAudioSink->stop(); +        mAudioSink->close(); +        mAudioSink.clear(); +    } +    // Make sure to release any buffer we hold onto so that the +    // source is able to stop(). +    if (mFirstBuffer != NULL) { +        mFirstBuffer->release(); +        mFirstBuffer = NULL; +    } + +    if (mInputBuffer != NULL) { +        ALOGV("AudioPlayer releasing input buffer."); +        mInputBuffer->release(); +        mInputBuffer = NULL; +    } + +    mSource->stop(); + +    // The following hack is necessary to ensure that the OMX +    // component is completely released by the time we may try +    // to instantiate it again. +    wp<MediaSource> tmp = mSource; +    mSource.clear(); +    while (tmp.promote() != NULL) { +        usleep(1000); +    } + +    mPositionTimeMediaUs = -1; +    mPositionTimeRealUs = -1; +    mSeeking = false; +    mReachedEOS = false; +    mReachedOutputEOS = false; +    mFinalStatus = OK; +    mStarted = false; +} + + +bool LPAPlayer::isSeeking() { +    Mutex::Autolock autoLock(mLock); +    return mSeeking; +} + +bool LPAPlayer::reachedEOS(status_t *finalStatus) { +    *finalStatus = OK; +    Mutex::Autolock autoLock(mLock); +    *finalStatus = mFinalStatus; +    return mReachedOutputEOS; +} + + +void *LPAPlayer::decoderThreadWrapper(void *me) { +    static_cast<LPAPlayer *>(me)->decoderThreadEntry(); +    return NULL; +} + + +void LPAPlayer::decoderThreadEntry() { + +    pthread_mutex_lock(&decoder_mutex); + +    pid_t tid  = gettid(); +    androidSetThreadPriority(tid, ANDROID_PRIORITY_AUDIO); +    prctl(PR_SET_NAME, (unsigned long)"LPA DecodeThread", 0, 0, 0); + +    ALOGV("decoderThreadEntry wait for signal \n"); +    if (!mStarted) { +        pthread_cond_wait(&decoder_cv, &decoder_mutex); +    } +    ALOGV("decoderThreadEntry ready to work \n"); +    pthread_mutex_unlock(&decoder_mutex); +    if (killDecoderThread) { +        return; +    } +    void* local_buf = malloc(MEM_BUFFER_SIZE); +    int bytesWritten = 0; +    while (!killDecoderThread) { + +        if (mReachedEOS || mPaused || !mIsAudioRouted) { +            pthread_mutex_lock(&decoder_mutex); +            pthread_cond_wait(&decoder_cv, &decoder_mutex); +            pthread_mutex_unlock(&decoder_mutex); +            continue; +        } + +        if (!mIsA2DPEnabled) { +            ALOGV("FillBuffer: MemBuffer size %d", MEM_BUFFER_SIZE); +            ALOGV("Fillbuffer started"); +            //TODO: Add memset +            bytesWritten = fillBuffer(local_buf, MEM_BUFFER_SIZE); +            ALOGV("FillBuffer completed bytesToWrite %d", bytesWritten); + +            if(!killDecoderThread) { +                mAudioSink->write(local_buf, bytesWritten); +            } +        } +    } + +    free(local_buf); + +    //TODO: Call fillbuffer with different size and write to mAudioSink() +} + +void *LPAPlayer::A2DPNotificationThreadWrapper(void *me) { +    static_cast<LPAPlayer *>(me)->A2DPNotificationThreadEntry(); +    return NULL; +} + +void LPAPlayer::A2DPNotificationThreadEntry() { +    while (1) { +        pthread_mutex_lock(&a2dp_notification_mutex); +        pthread_cond_wait(&a2dp_notification_cv, &a2dp_notification_mutex); +        pthread_mutex_unlock(&a2dp_notification_mutex); +        if (killA2DPNotificationThread) { +            break; +        } + +        ALOGV("A2DP notification has come mIsA2DPEnabled: %d", mIsA2DPEnabled); + +        if (mIsA2DPEnabled) { +            //TODO: +        } +        else { +        //TODO +        } +    } +    a2dpNotificationThreadAlive = false; +    ALOGV("A2DPNotificationThread is dying"); + +} + +void LPAPlayer::createThreads() { + +    //Initialize all the Mutexes and Condition Variables +    pthread_mutex_init(&decoder_mutex, NULL); +    pthread_mutex_init(&a2dp_notification_mutex, NULL); +    pthread_cond_init (&decoder_cv, NULL); +    pthread_cond_init (&a2dp_notification_cv, NULL); + +    // Create 4 threads Effect, decoder, event and A2dp +    pthread_attr_t attr; +    pthread_attr_init(&attr); +    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + +    killDecoderThread = false; +    killA2DPNotificationThread = false; + +    decoderThreadAlive = true; +    a2dpNotificationThreadAlive = true; + +    ALOGV("Creating decoder Thread"); +    pthread_create(&decoderThread, &attr, decoderThreadWrapper, this); + +    ALOGV("Creating A2DP Notification Thread"); +    pthread_create(&A2DPNotificationThread, &attr, A2DPNotificationThreadWrapper, this); + +    pthread_attr_destroy(&attr); +} + +size_t LPAPlayer::fillBuffer(void *data, size_t size) { + +    if (mReachedEOS) { +        return 0; +    } + +    bool postSeekComplete = false; + +    size_t size_done = 0; +    size_t size_remaining = size; +    while (size_remaining > 0) { +        MediaSource::ReadOptions options; + +        { +            Mutex::Autolock autoLock(mLock); + +            if (mSeeking) { +                mInternalSeeking = false; +            } +            if (mSeeking || mInternalSeeking) { +                if (mIsFirstBuffer) { +                    if (mFirstBuffer != NULL) { +                        mFirstBuffer->release(); +                        mFirstBuffer = NULL; +                    } +                    mIsFirstBuffer = false; +                } + +                options.setSeekTo(mSeekTimeUs); + +                if (mInputBuffer != NULL) { +                    mInputBuffer->release(); +                    mInputBuffer = NULL; +                } + +                size_remaining = size; +                size_done = 0; + +                mSeeking = false; +                if (mObserver && !mInternalSeeking) { +                    ALOGV("fillBuffer: Posting audio seek complete event"); +                    postSeekComplete = true; +                } +                mInternalSeeking = false; +            } +        } + +        if (mInputBuffer == NULL) { +            status_t err; + +            if (mIsFirstBuffer) { +                mInputBuffer = mFirstBuffer; +                mFirstBuffer = NULL; +                err = mFirstBufferResult; + +                mIsFirstBuffer = false; +            } else { +                err = mSource->read(&mInputBuffer, &options); +            } + +            CHECK((err == OK && mInputBuffer != NULL) +                   || (err != OK && mInputBuffer == NULL)); + +            Mutex::Autolock autoLock(mLock); + +            if (err != OK) { +                mReachedEOS = true; +                mFinalStatus = err; +                break; +            } + +            CHECK(mInputBuffer->meta_data()->findInt64( +                        kKeyTime, &mPositionTimeMediaUs)); +            mFrameSize = mAudioSink->frameSize(); +            mPositionTimeRealUs = +                ((mNumFramesPlayed + size_done / mFrameSize) * 1000000) +                    / mSampleRate; +        } +        if (mInputBuffer->range_length() == 0) { +            mInputBuffer->release(); +            mInputBuffer = NULL; + +            continue; +        } + +        size_t copy = size_remaining; +        if (copy > mInputBuffer->range_length()) { +            copy = mInputBuffer->range_length(); +        } + +        memcpy((char *)data + size_done, +               (const char *)mInputBuffer->data() + mInputBuffer->range_offset(), +               copy); + +        mInputBuffer->set_range(mInputBuffer->range_offset() + copy, +                                mInputBuffer->range_length() - copy); + +        size_done += copy; +        size_remaining -= copy; +    } + +    if (postSeekComplete) { +        mObserver->postAudioSeekComplete(); +    } + +    return size_done; +} + +int64_t LPAPlayer::getRealTimeUs() { +    Mutex::Autolock autoLock(mLock); +    return getRealTimeUsLocked(); +} + + +int64_t LPAPlayer::getRealTimeUsLocked(){ +    //Used for AV sync: irrelevant API for LPA. +    return 0; +} + +int64_t LPAPlayer::getTimeStamp(A2DPState state) { +    uint64_t timestamp = 0; +    switch (state) { +    case A2DP_ENABLED: +    case A2DP_DISCONNECT: +        ALOGV("Get timestamp for A2DP"); +        break; +    case A2DP_DISABLED: +    case A2DP_CONNECT: { +        mAudioSink->getTimeStamp(×tamp); +        break; +    } +    default: +        break; +    } +    ALOGV("timestamp %lld ", timestamp); +    return timestamp; +} + +int64_t LPAPlayer::getMediaTimeUs() { +    Mutex::Autolock autoLock(mLock); +    ALOGV("getMediaTimeUs() mPaused %d mSeekTimeUs %lld mPauseTime %lld", mPaused, mSeekTimeUs, mPauseTime); +    if (mPaused) { +        return mPauseTime; +    } else { +        A2DPState state = mIsA2DPEnabled ? A2DP_ENABLED : A2DP_DISABLED; +        return (mSeekTimeUs + getTimeStamp(state)); +    } +} + +bool LPAPlayer::getMediaTimeMapping( +                                   int64_t *realtime_us, int64_t *mediatime_us) { +    Mutex::Autolock autoLock(mLock); + +    *realtime_us = mPositionTimeRealUs; +    *mediatime_us = mPositionTimeMediaUs; + +    return mPositionTimeRealUs != -1 && mPositionTimeMediaUs != -1; +} + +void LPAPlayer::requestAndWaitForDecoderThreadExit() { + +    if (!decoderThreadAlive) +        return; +    killDecoderThread = true; + +    /* Flush the audio sink to unblock the decoder thread +       if any write to audio HAL is blocked */ +    if (!mReachedOutputEOS && mIsAudioRouted) +        mAudioSink->flush(); + +    pthread_cond_signal(&decoder_cv); +    pthread_join(decoderThread,NULL); +    ALOGV("decoder thread killed"); + +} + +void LPAPlayer::requestAndWaitForA2DPNotificationThreadExit() { +    if (!a2dpNotificationThreadAlive) +        return; +    killA2DPNotificationThread = true; +    pthread_cond_signal(&a2dp_notification_cv); +    pthread_join(A2DPNotificationThread,NULL); +    ALOGV("a2dp notification thread killed"); +} + +void LPAPlayer::onPauseTimeOut() { +    ALOGV("onPauseTimeOut"); +    Mutex::Autolock autoLock(mResumeLock); +    if (!mPauseEventPending) { +        return; +    } +    mPauseEventPending = false; +    if(!mIsA2DPEnabled) { +        // 1.) Set seek flags +        mReachedEOS = false; +        mReachedOutputEOS = false; +        if(mSeeking == false) { +            mSeekTimeUs += getTimeStamp(A2DP_DISABLED); +            mInternalSeeking = true; +        } else { +            //do not update seek time if user has already seeked +            // to a new position +            // also seek has to be posted back to player, +            // so do not set mInternalSeeking flag +            ALOGV("do not update seek time %lld ", mSeekTimeUs); +        } +        ALOGV("newseek time = %lld ", mSeekTimeUs); +        // 2.) Close routing Session +        mAudioSink->close(); +        mIsAudioRouted = false; +    } + +} + +} //namespace android diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index dc8e4a3..62ba826 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -40,6 +40,9 @@  #include <media/stagefright/MetaData.h>  #include <media/stagefright/Utils.h>  #include <utils/String8.h> +#ifdef QCOM_HARDWARE +#include <QCMediaDefs.h> +#endif  namespace android { @@ -257,6 +260,28 @@ static const char *FourCC2MIME(uint32_t fourcc) {          case FOURCC('a', 'v', 'c', '1'):              return MEDIA_MIMETYPE_VIDEO_AVC; +#ifdef QCOM_HARDWARE +        case FOURCC('s', 'q', 'c', 'p'): +            return MEDIA_MIMETYPE_AUDIO_QCELP; + +        case FOURCC('s', 'e', 'v', 'c'): +            return MEDIA_MIMETYPE_AUDIO_EVRC; + +        case FOURCC('d', 't', 's', 'c'): +        case FOURCC('d', 't', 's', 'h'): +        case FOURCC('d', 't', 's', 'l'): +            return MEDIA_MIMETYPE_AUDIO_DTS; + +        case FOURCC('d', 't', 's', 'e'): +            return MEDIA_MIMETYPE_AUDIO_DTS_LBR; + +        case FOURCC('a', 'c', '-', '3'): +            return MEDIA_MIMETYPE_AUDIO_AC3; + +        case FOURCC('e', 'c', '-', '3'): +            return MEDIA_MIMETYPE_AUDIO_EAC3; +#endif +          default:              CHECK(!"should not be here.");              return NULL; @@ -921,6 +946,17 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {          case FOURCC('m', 'p', '4', 'a'):          case FOURCC('s', 'a', 'm', 'r'):          case FOURCC('s', 'a', 'w', 'b'): +#ifdef QCOM_HARDWARE +        case FOURCC('.', 'm', 'p', '3'): +        case FOURCC('s', 'e', 'v', 'c'): +        case FOURCC('s', 'q', 'c', 'p'): +        case FOURCC('d', 't', 's', 'c'): +        case FOURCC('d', 't', 's', 'h'): +        case FOURCC('d', 't', 's', 'l'): +        case FOURCC('d', 't', 's', 'e'): +        case FOURCC('a', 'c', '-', '3'): +        case FOURCC('e', 'c', '-', '3'): +#endif          {              uint8_t buffer[8 + 20];              if (chunk_data_size < (ssize_t)sizeof(buffer)) { @@ -961,7 +997,16 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {              mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);              off64_t stop_offset = *offset + chunk_size; -            *offset = data_offset + sizeof(buffer); +#ifdef QCOM_HARDWARE +            if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_MPEG, FourCC2MIME(chunk_type)) || +                !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, FourCC2MIME(chunk_type))) { +                // ESD is not required in mp3 +                // amr wb with damr atom corrupted can cause the clip to not play +               *offset = stop_offset; +            } else +#endif +               *offset = data_offset + sizeof(buffer); +              while (*offset < stop_offset) {                  status_t err = parseChunk(offset, depth + 1);                  if (err != OK) { @@ -1219,6 +1264,18 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {              break;          } +#ifdef QCOM_HARDWARE +        case FOURCC('d', 'd', 't', 's'): +        case FOURCC('d', 'a', 'c', '3'): +        case FOURCC('d', 'e', 'c', '3'): +        { +            //no information need to be passed here, just log and end +            ALOGV("ddts/dac3/dec3 pass from mpeg4 extractor"); +            *offset += chunk_size; +            break; +        } +#endif +          case FOURCC('a', 'v', 'c', 'C'):          {              char buffer[256]; @@ -1799,6 +1856,14 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio(          return ERROR_MALFORMED;      } +#ifdef QCOM_HARDWARE +    if (objectTypeIndication == 0xA0) { +        // This isn't MPEG4 audio at all, it's EVRC +       mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_EVRC); +       return OK; +    } +#endif +      if (objectTypeIndication == 0xe1) {          // This isn't MPEG4 audio at all, it's QCELP 14k...          mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP); @@ -2313,6 +2378,9 @@ static bool LegacySniffMPEG4(      }      if (!memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8) +#ifdef QCOM_HARDWARE +        || !memcmp(header, "ftyp3g2a", 8) || !memcmp(header, "ftyp3g2b", 8) || !memcmp(header, "ftyp3g2c", 8) +#endif          || !memcmp(header, "ftyp3gr6", 8) || !memcmp(header, "ftyp3gs6", 8)          || !memcmp(header, "ftyp3ge6", 8) || !memcmp(header, "ftyp3gg6", 8)          || !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8) diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp index b18c916..cca49fe 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -30,6 +30,9 @@  #include "include/WVMExtractor.h"  #include "include/FLACExtractor.h"  #include "include/AACExtractor.h" +#ifdef QCOM_HARDWARE +#include "include/ExtendedExtractor.h" +#endif  #include "matroska/MatroskaExtractor.h" @@ -132,6 +135,14 @@ sp<MediaExtractor> MediaExtractor::Create(         }      } +#ifdef QCOM_HARDWARE +    if(ret == NULL) { +        //Create Extended Extractor only if default extractor are not selected +        ALOGV("Using ExtendedExtractor\n"); +        ret =  ExtendedExtractor::CreateExtractor(source, mime); +    } +#endif +      return ret;  } diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 560c1bb..4e6c395 100755..100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1,5 +1,9 @@  /*   * Copyright (C) 2009 The Android Open Source Project + * Copyright (c) 2010 - 2012, The Linux Foundation. All rights reserved. + * + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only.   *   * Licensed under the Apache License, Version 2.0 (the "License");   * you may not use this file except in compliance with the License. @@ -42,6 +46,12 @@  #include <OMX_Audio.h>  #include <OMX_Component.h> +#ifdef QCOM_HARDWARE +#include <QCMediaDefs.h> +#include <QCMetaData.h> +#include <QOMX_AudioExtensions.h> +#include <OMX_QCOMExtns.h> +#endif  #include "include/avc_utils.h"  #ifdef USE_SAMSUNG_COLORFORMAT @@ -271,6 +281,26 @@ uint32_t OMXCodec::getComponentQuirks(                  index, "output-buffers-are-unreadable")) {          quirks |= kOutputBuffersAreUnreadable;      } +#ifdef QCOM_HARDWARE +    if (list->codecHasQuirk( +                index, "requies-loaded-to-idle-after-allocation")) { +      quirks |= kRequiresLoadedToIdleAfterAllocation; +    } +    if (list->codecHasQuirk( +                index, "requires-global-flush")) { +        quirks |= kRequiresGlobalFlush; +    } +    if (list->codecHasQuirk( +                index, "requires-wma-pro-component")) { +        quirks |= kRequiresWMAProComponent; +    } +    if (list->codecHasQuirk( +                index, "defers-output-buffer-allocation")) { +      quirks |= kDefersOutputBufferAllocation; +    } + +    quirks |= QCOMXCodec::getQCComponentQuirks(list,index); +#endif      return quirks;  } @@ -352,7 +382,28 @@ sp<MediaSource> OMXCodec::Create(                  return softwareCodec;              }          } - +#ifdef QCOM_HARDWARE +        //quirks = getComponentQuirks(componentNameBase, createEncoder); +        if(quirks & kRequiresWMAProComponent) +        { +           int32_t version; +           CHECK(meta->findInt32(kKeyWMAVersion, &version)); +           if(version==kTypeWMA) +           { +              componentName = "OMX.qcom.audio.decoder.wma"; +           } +           else if(version==kTypeWMAPro) +           { +              componentName= "OMX.qcom.audio.decoder.wma10Pro"; +           } +           else if(version==kTypeWMALossLess) +           { +              componentName= "OMX.qcom.audio.decoder.wmaLossLess"; +           } +        } + +        QCOMXCodec::setASFQuirks(quirks, meta, componentName); +#endif          ALOGV("Attempting to allocate OMX node '%s'", componentName);          if (!createEncoder @@ -515,9 +566,25 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {              CHECK(meta->findData(kKeyVorbisBooks, &type, &data, &size));              addCodecSpecificData(data, size); +#ifdef QCOM_HARDWARE +        } else if (meta->findData(kKeyRawCodecSpecificData, &type, &data, &size)) { +            ALOGV("OMXCodec::configureCodec found kKeyRawCodecSpecificData of size %d\n", size); +            addCodecSpecificData(data, size); +        } else { +            QCOMXCodec::checkAndAddRawFormat(this,meta); +#endif          }      } +#ifdef QCOM_HARDWARE +    status_t errRetVal = QCOMXCodec::configureDIVXCodec( meta, mMIME, mOMX, mNode, +                                                         (OMXCodec::mIsEncoder ? +                                                         kPortIndexOutput : kPortIndexInput)); +    if(OK != errRetVal) { +        return errRetVal; +    } +#endif +      int32_t bitRate = 0;      if (mIsEncoder) {          CHECK(meta->findInt32(kKeyBitRate, &bitRate)); @@ -545,6 +612,42 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {              CODEC_LOGE("setAACFormat() failed (err = %d)", err);              return err;          } + +#ifdef QCOM_HARDWARE +        uint32_t type; +        const void *data; +        size_t size; + +        if (meta->findData(kKeyAacCodecSpecificData, &type, &data, &size)) { +            ALOGV("OMXCodec:: configureCodec found kKeyAacCodecSpecificData of size %d\n", size); +            addCodecSpecificData(data, size); +        } +    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AC3, mMIME)) { +        int32_t numChannels, sampleRate; +        CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); +        CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); +        setAC3Format(numChannels, sampleRate); +    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_EAC3, mMIME)) { +        int32_t numChannels, sampleRate; +        CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); +        CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); +        setAC3Format(numChannels, sampleRate); //since AC3 and EAC3 use same format at present +    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_EVRC, mMIME)) { +        int32_t numChannels, sampleRate; +        CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); +        CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); +        setEVRCFormat(numChannels, sampleRate, bitRate); +    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_QCELP, mMIME)) { +        int32_t numChannels, sampleRate; +        CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); +        CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); +        setQCELPFormat(numChannels, sampleRate, bitRate); +    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_WMA, mMIME))  { +        status_t err = setWMAFormat(meta); +        if(err!=OK){ +           return err; +        } +#endif      } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_ALAW, mMIME)              || !strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_MLAW, mMIME)) {          // These are PCM-like formats with a fixed sample rate but @@ -562,10 +665,37 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {          CHECK(meta->findInt32(kKeySampleRate, &sampleRate));          setRawAudioFormat(kPortIndexInput, sampleRate, numChannels); +#ifdef QCOM_HARDWARE +    } else { +        status_t err = QCOMXCodec::setQCFormat(meta, mMIME, mOMX, mNode, this, mIsEncoder); + +        if(OK != err) { +            return err; +        } +#endif      }      if (!strncasecmp(mMIME, "video/", 6)) { +#ifdef QCOM_HARDWARE +        if ((mFlags & kClientNeedsFramebuffer) && !strncmp(mComponentName, "OMX.qcom.", 9)) { +            ALOGV("Enabling thumbnail mode."); +            QOMX_ENABLETYPE enableType; +            OMX_INDEXTYPE indexType; + +            status_t err = mOMX->getExtensionIndex( +                mNode, OMX_QCOM_INDEX_PARAM_VIDEO_SYNCFRAMEDECODINGMODE, &indexType); + +            CHECK_EQ(err, (status_t)OK); + +            enableType.bEnable = OMX_TRUE; +            err = mOMX->setParameter( +                    mNode, indexType, &enableType, sizeof(enableType)); +            CHECK_EQ(err, (status_t)OK); + +            ALOGV("Thumbnail mode enabled."); +        } +#endif          if (mIsEncoder) {              setVideoInputFormat(mMIME, meta);          } else { @@ -833,8 +963,15 @@ void OMXCodec::setVideoInputFormat(      } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {          compressionFormat = OMX_VIDEO_CodingH263;      } else { -        ALOGE("Not a supported video mime type: %s", mime); -        CHECK(!"Should not be here. Not a supported video mime type."); +#ifdef QCOM_HARDWARE +        status_t err = QCOMXCodec::setQCVideoInputFormat(mime, &compressionFormat); +        if(err != OK) { +#endif +            ALOGE("Not a supported video mime type: %s", mime); +            CHECK(!"Should not be here. Not a supported video mime type."); +#ifdef QCOM_HARDWARE +        } +#endif      }      OMX_COLOR_FORMATTYPE colorFormat; @@ -1230,8 +1367,16 @@ status_t OMXCodec::setVideoOutputFormat(      } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) {          compressionFormat = OMX_VIDEO_CodingMPEG2;      } else { -        ALOGE("Not a supported video mime type: %s", mime); -        CHECK(!"Should not be here. Not a supported video mime type."); +#ifdef QCOM_HARDWARE +        status_t err = QCOMXCodec::setQCVideoOutputFormat(mime,&compressionFormat); + +        if(err != OK) { +#endif +            ALOGE("Not a supported video mime type: %s", mime); +            CHECK(!"Should not be here. Not a supported video mime type."); +#ifdef QCOM_HARDWARE +        } +#endif      }      status_t err = setVideoPortFormatType( @@ -1389,6 +1534,9 @@ OMXCodec::OMXCodec(        mState(LOADED),        mInitialBufferSubmit(true),        mSignalledEOS(false), +#ifdef QCOM_HARDWARE +      mFinalStatus(OK), +#endif        mNoMoreOutputData(false),        mOutputPortSettingsHaveChanged(false),        mSeekTimeUs(-1), @@ -1437,6 +1585,12 @@ void OMXCodec::setComponentRole(              "audio_decoder.g711mlaw", "audio_encoder.g711mlaw" },          { MEDIA_MIMETYPE_AUDIO_G711_ALAW,              "audio_decoder.g711alaw", "audio_encoder.g711alaw" }, +#ifdef QCOM_HARDWARE +        { MEDIA_MIMETYPE_AUDIO_EVRC, +            "audio_decoder.evrchw", "audio_encoder.evrc" }, +        { MEDIA_MIMETYPE_AUDIO_QCELP, +            "audio_decoder,qcelp13Hw", "audio_encoder.qcelp13" }, +#endif          { MEDIA_MIMETYPE_VIDEO_AVC,              "video_decoder.avc", "video_encoder.avc" },          { MEDIA_MIMETYPE_VIDEO_MPEG4, @@ -1449,6 +1603,16 @@ void OMXCodec::setComponentRole(              "audio_decoder.raw", "audio_encoder.raw" },          { MEDIA_MIMETYPE_AUDIO_FLAC,              "audio_decoder.flac", "audio_encoder.flac" }, +#ifdef QCOM_HARDWARE +        { MEDIA_MIMETYPE_VIDEO_DIVX, +            "video_decoder.divx", NULL }, +        { MEDIA_MIMETYPE_AUDIO_AC3, +            "audio_decoder.ac3", NULL }, +        { MEDIA_MIMETYPE_AUDIO_EAC3, +            "audio_decoder.eac3", NULL }, +        { MEDIA_MIMETYPE_VIDEO_DIVX311, +            "video_decoder.divx", NULL }, +#endif      };      static const size_t kNumMimeToRole = @@ -1462,6 +1626,9 @@ void OMXCodec::setComponentRole(      }      if (i == kNumMimeToRole) { +#ifdef QCOM_HARDWARE +        QCOMXCodec::checkQCRole(omx, node, isEncoder, mime); +#endif          return;      } @@ -2627,11 +2794,22 @@ void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) {              CODEC_LOGV("FLUSH_DONE(%ld)", portIndex); -            CHECK_EQ((int)mPortStatus[portIndex], (int)SHUTTING_DOWN); -            mPortStatus[portIndex] = ENABLED; +#ifdef QCOM_HARDWARE +            if (portIndex == (OMX_U32) -1) { +                CHECK_EQ((int)mPortStatus[kPortIndexInput], (int)SHUTTING_DOWN); +                mPortStatus[kPortIndexInput] = ENABLED; +                CHECK_EQ((int)mPortStatus[kPortIndexOutput], (int)SHUTTING_DOWN); +                mPortStatus[kPortIndexOutput] = ENABLED; +            } else { +#endif +                CHECK_EQ((int)mPortStatus[portIndex], (int)SHUTTING_DOWN); +                mPortStatus[portIndex] = ENABLED; -            CHECK_EQ(countBuffersWeOwn(mPortBuffers[portIndex]), -                     mPortBuffers[portIndex].size()); +                CHECK_EQ(countBuffersWeOwn(mPortBuffers[portIndex]), +                         mPortBuffers[portIndex].size()); +#ifdef QCOM_HARDWARE +            } +#endif              if (mSkipCutBuffer != NULL && mPortStatus[kPortIndexOutput] == ENABLED) {                  mSkipCutBuffer->clear(); @@ -2896,21 +3074,30 @@ bool OMXCodec::flushPortAsync(OMX_U32 portIndex) {      CHECK(mState == EXECUTING || mState == RECONFIGURING              || mState == EXECUTING_TO_IDLE); -    CODEC_LOGV("flushPortAsync(%ld): we own %d out of %d buffers already.", -         portIndex, countBuffersWeOwn(mPortBuffers[portIndex]), -         mPortBuffers[portIndex].size()); +#ifdef QCOM_HARDWARE +    if (portIndex == (OMX_U32) -1 ) { +        mPortStatus[kPortIndexInput] = SHUTTING_DOWN; +        mPortStatus[kPortIndexOutput] = SHUTTING_DOWN; +    } else { +#endif +        CODEC_LOGV("flushPortAsync(%ld): we own %d out of %d buffers already.", +             portIndex, countBuffersWeOwn(mPortBuffers[portIndex]), +             mPortBuffers[portIndex].size()); -    CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLED); -    mPortStatus[portIndex] = SHUTTING_DOWN; +        CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLED); +        mPortStatus[portIndex] = SHUTTING_DOWN; -    if ((mQuirks & kRequiresFlushCompleteEmulation) -        && countBuffersWeOwn(mPortBuffers[portIndex]) -                == mPortBuffers[portIndex].size()) { -        // No flush is necessary and this component fails to send a -        // flush-complete event in this case. +        if ((mQuirks & kRequiresFlushCompleteEmulation) +            && countBuffersWeOwn(mPortBuffers[portIndex]) +                    == mPortBuffers[portIndex].size()) { +            // No flush is necessary and this component fails to send a +            // flush-complete event in this case. -        return false; +           return false; +        } +#ifdef QCOM_HARDWARE      } +#endif      status_t err =          mOMX->sendCommand(mNode, OMX_CommandFlush, portIndex); @@ -2950,16 +3137,27 @@ void OMXCodec::fillOutputBuffers() {      // end-of-output-stream. If we own all input buffers and also own      // all output buffers and we already signalled end-of-input-stream,      // the end-of-output-stream is implied. -    if (mSignalledEOS + +#ifdef QCOM_HARDWARE +    // NOTE: Thumbnail mode needs a call to fillOutputBuffer in order +    // to get the decoded frame from the component. Currently, +    // thumbnail mode calls emptyBuffer with an EOS flag on its first +    // frame and sets mSignalledEOS to true, so without the check for +    // !mThumbnailMode, fillOutputBuffer will never be called. +    if (!((mFlags & kClientNeedsFramebuffer) && !strncmp(mComponentName, "OMX.qcom.", 9))){ +#endif +        if (mSignalledEOS              && countBuffersWeOwn(mPortBuffers[kPortIndexInput])                  == mPortBuffers[kPortIndexInput].size()              && countBuffersWeOwn(mPortBuffers[kPortIndexOutput])                  == mPortBuffers[kPortIndexOutput].size()) { -        mNoMoreOutputData = true; -        mBufferFilled.signal(); - -        return; +            mNoMoreOutputData = true; +            mBufferFilled.signal(); +            return; +        } +#ifdef QCOM_HARDWARE      } +#endif      Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];      for (size_t i = 0; i < buffers->size(); ++i) { @@ -3283,6 +3481,20 @@ bool OMXCodec::drainInputBuffer(BufferInfo *info) {      if (signalEOS) {          flags |= OMX_BUFFERFLAG_EOS; +#ifdef QCOM_HARDWARE +    } else if ((mFlags & kClientNeedsFramebuffer) && !strncmp(mComponentName, "OMX.qcom.", 9)) { +        // Because we don't get an EOS after getting the first frame, we +        // need to notify the component with OMX_BUFFERFLAG_EOS, set +        // mNoMoreOutputData to false so fillOutputBuffer gets called on +        // the first output buffer (see comment in fillOutputBuffer), and +        // mSignalledEOS must be true so drainInputBuffer is not executed +        // on extra frames. Setting mFinalStatus to ERROR_END_OF_STREAM as +        // we dont want to return OK and NULL buffer in read. +        flags |= OMX_BUFFERFLAG_EOS; +        mNoMoreOutputData = false; +        mSignalledEOS = true; +        mFinalStatus = ERROR_END_OF_STREAM; +#endif      } else {          mNoMoreOutputData = false;      } @@ -3381,6 +3593,14 @@ status_t OMXCodec::waitForBufferFilled_l() {          return mBufferFilled.wait(mLock);      }      status_t err = mBufferFilled.waitRelative(mLock, kBufferFilledEventTimeOutNs); +#ifdef QCOM_HARDWARE +    if ((err == -ETIMEDOUT) && (mPaused == true)){ +        // When the audio playback is paused, the fill buffer maybe timed out +        // if input data is not available to decode. Hence, considering the +        // timed out as a valid case. +        err = OK; +    } +#endif      if (err != OK) {          CODEC_LOGE("Timed out waiting for output buffers: %d/%d",              countBuffersWeOwn(mPortBuffers[kPortIndexInput]), @@ -3605,6 +3825,182 @@ status_t OMXCodec::setAACFormat(      return OK;  } +#ifdef QCOM_HARDWARE +void OMXCodec::setAC3Format(int32_t numChannels, int32_t sampleRate) { + +    QOMX_AUDIO_PARAM_AC3TYPE profileAC3; +    QOMX_AUDIO_PARAM_AC3PP profileAC3PP; +    OMX_INDEXTYPE indexTypeAC3; +    OMX_INDEXTYPE indexTypeAC3PP; +    OMX_PARAM_PORTDEFINITIONTYPE portParam; + +    //configure input port +    CODEC_LOGV("setAC3Format samplerate %d, numChannels %d", sampleRate, numChannels); +    InitOMXParams(&portParam); +    portParam.nPortIndex = 0; +    status_t err = mOMX->getParameter( +       mNode, OMX_IndexParamPortDefinition, &portParam, sizeof(portParam)); +    CHECK_EQ(err, (status_t)OK); +    err = mOMX->setParameter( +       mNode, OMX_IndexParamPortDefinition, &portParam, sizeof(portParam)); +    CHECK_EQ(err, (status_t)OK); + +    //configure output port +    portParam.nPortIndex = 1; +    err = mOMX->getParameter( +       mNode, OMX_IndexParamPortDefinition, &portParam, sizeof(portParam)); +    CHECK_EQ(err, (status_t)OK); +    err = mOMX->setParameter( +       mNode, OMX_IndexParamPortDefinition, &portParam, sizeof(portParam)); +    CHECK_EQ(err, (status_t)OK); + +    err = mOMX->getExtensionIndex(mNode, OMX_QCOM_INDEX_PARAM_AC3TYPE, &indexTypeAC3); + +    InitOMXParams(&profileAC3); +    profileAC3.nPortIndex = kPortIndexInput; +    err = mOMX->getParameter(mNode, indexTypeAC3, &profileAC3, sizeof(profileAC3)); +    CHECK_EQ(err,(status_t)OK); + +    profileAC3.nSamplingRate  =  sampleRate; +    profileAC3.nChannels      =  2; +    profileAC3.eChannelConfig =  OMX_AUDIO_AC3_CHANNEL_CONFIG_2_0; + +    CODEC_LOGE("numChannels = %d, profileAC3.nChannels = %d", numChannels, profileAC3.nChannels); + +    err = mOMX->setParameter(mNode, indexTypeAC3, &profileAC3, sizeof(profileAC3)); +    CHECK_EQ(err,(status_t)OK); + +    //for output port +    OMX_AUDIO_PARAM_PCMMODETYPE profilePcm; +    InitOMXParams(&profilePcm); +    profilePcm.nPortIndex = kPortIndexOutput; +    err = mOMX->getParameter(mNode, OMX_IndexParamAudioPcm, &profilePcm, sizeof(profilePcm)); +    CHECK_EQ(err, (status_t)OK); + +    profilePcm.nSamplingRate  =  sampleRate; +    err = mOMX->setParameter(mNode, OMX_IndexParamAudioPcm, &profilePcm, sizeof(profilePcm)); +    CHECK_EQ(err, (status_t)OK); +    mOMX->getExtensionIndex(mNode, OMX_QCOM_INDEX_PARAM_AC3PP, &indexTypeAC3PP); + +    InitOMXParams(&profileAC3PP); +    profileAC3PP.nPortIndex = kPortIndexInput; +    err = mOMX->getParameter(mNode, indexTypeAC3PP, &profileAC3PP, sizeof(profileAC3PP)); +    CHECK_EQ(err, (status_t)OK); + +    int i; +    int channel_routing[6]; + +    for (i=0; i<6; i++) { +        channel_routing[i] = -1; +    } +    for (i=0; i<6; i++) { +        profileAC3PP.eChannelRouting[i] =  (OMX_AUDIO_AC3_CHANNEL_ROUTING)channel_routing[i]; +    } + +    profileAC3PP.eChannelRouting[0] =  OMX_AUDIO_AC3_CHANNEL_LEFT; +    profileAC3PP.eChannelRouting[1] =  OMX_AUDIO_AC3_CHANNEL_RIGHT; +    err = mOMX->setParameter(mNode, indexTypeAC3PP, &profileAC3PP, sizeof(profileAC3PP)); +    CHECK_EQ(err, (status_t)OK); + +} + + +status_t OMXCodec::setWMAFormat(const sp<MetaData> &meta) +{ +   if (mIsEncoder) { +        CODEC_LOGE("WMA encoding not supported"); +        return OK; +    } else { +        int32_t version; +        OMX_AUDIO_PARAM_WMATYPE paramWMA; +        QOMX_AUDIO_PARAM_WMA10PROTYPE paramWMA10; +        CHECK(meta->findInt32(kKeyWMAVersion, &version)); +        int32_t numChannels; +        int32_t bitRate; +        int32_t sampleRate; +        int32_t encodeOptions; +        int32_t blockAlign; +        int32_t bitspersample; +        int32_t formattag; +        int32_t advencopt1; +        int32_t advencopt2; +        int32_t VirtualPktSize; +        if(version==kTypeWMAPro || version==kTypeWMALossLess) { +            CHECK(meta->findInt32(kKeyWMABitspersample, &bitspersample)); +            CHECK(meta->findInt32(kKeyWMAFormatTag, &formattag)); +            CHECK(meta->findInt32(kKeyWMAAdvEncOpt1,&advencopt1)); +            CHECK(meta->findInt32(kKeyWMAAdvEncOpt2,&advencopt2)); +            CHECK(meta->findInt32(kKeyWMAVirPktSize,&VirtualPktSize)); +        } +        if(version==kTypeWMA) { +            InitOMXParams(¶mWMA); +            paramWMA.nPortIndex = kPortIndexInput; +        } else if(version==kTypeWMAPro || version==kTypeWMALossLess) { +            InitOMXParams(¶mWMA10); +            paramWMA10.nPortIndex = kPortIndexInput; +        } +        CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); +        CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); +        CHECK(meta->findInt32(kKeyBitRate, &bitRate)); +        CHECK(meta->findInt32(kKeyWMAEncodeOpt, &encodeOptions)); +        CHECK(meta->findInt32(kKeyWMABlockAlign, &blockAlign)); +        CODEC_LOGV("Channels: %d, SampleRate: %d, BitRate; %d" +                   "EncodeOptions: %d, blockAlign: %d", numChannels, +                   sampleRate, bitRate, encodeOptions, blockAlign); +        if(sampleRate>48000 || numChannels>2) +        { +            ALOGE("Unsupported samplerate/channels"); +            return ERROR_UNSUPPORTED; +        } +        if(version==kTypeWMAPro || version==kTypeWMALossLess) +        { +            CODEC_LOGV("Bitspersample: %d, wmaformattag: %d," +                       "advencopt1: %d, advencopt2: %d VirtualPktSize %d", bitspersample, +                       formattag, advencopt1, advencopt2, VirtualPktSize); +        } +        status_t err = OK; +        OMX_INDEXTYPE index; +        if(version==kTypeWMA) { +            err = mOMX->getParameter( +                   mNode, OMX_IndexParamAudioWma, ¶mWMA, sizeof(paramWMA)); +        } else if(version==kTypeWMAPro || version==kTypeWMALossLess) { +            mOMX->getExtensionIndex(mNode,"OMX.Qualcomm.index.audio.wma10Pro",&index); +            err = mOMX->getParameter( +                   mNode, index, ¶mWMA10, sizeof(paramWMA10)); +        } +        CHECK_EQ(err, (status_t)OK); +        if(version==kTypeWMA) { +            paramWMA.nChannels = numChannels; +            paramWMA.nSamplingRate = sampleRate; +            paramWMA.nEncodeOptions = encodeOptions; +            paramWMA.nBitRate = bitRate; +            paramWMA.nBlockAlign = blockAlign; +        } else if(version==kTypeWMAPro || version==kTypeWMALossLess) { +            paramWMA10.nChannels = numChannels; +            paramWMA10.nSamplingRate = sampleRate; +            paramWMA10.nEncodeOptions = encodeOptions; +            paramWMA10.nBitRate = bitRate; +            paramWMA10.nBlockAlign = blockAlign; +        } +        if(version==kTypeWMAPro || version==kTypeWMALossLess) { +            paramWMA10.advancedEncodeOpt = advencopt1; +            paramWMA10.advancedEncodeOpt2 = advencopt2; +            paramWMA10.formatTag = formattag; +            paramWMA10.validBitsPerSample = bitspersample; +            paramWMA10.nVirtualPktSize = VirtualPktSize; +        } +        if(version==kTypeWMA) { +            err = mOMX->setParameter( +                  mNode, OMX_IndexParamAudioWma, ¶mWMA, sizeof(paramWMA)); +        } else if(version==kTypeWMAPro || version==kTypeWMALossLess) { +           err = mOMX->setParameter( +                 mNode, index, ¶mWMA10, sizeof(paramWMA10)); +        } +        return err; +    } +} +#endif +  void OMXCodec::setG711Format(int32_t numChannels) {      CHECK(!mIsEncoder);      setRawAudioFormat(kPortIndexInput, 8000, numChannels); @@ -3846,19 +4242,32 @@ status_t OMXCodec::stopOmxComponent_l() {                  CODEC_LOGV("This component requires a flush before transitioning "                       "from EXECUTING to IDLE..."); -                bool emulateInputFlushCompletion = -                    !flushPortAsync(kPortIndexInput); +#ifdef QCOM_HARDWARE +                //DSP supports flushing of ports simultaneously. +                //Flushing individual port is not supported. +                if(mQuirks & kRequiresGlobalFlush) { +                    bool emulateFlushCompletion = !flushPortAsync(kPortIndexBoth); +                    if (emulateFlushCompletion) { +                        onCmdComplete(OMX_CommandFlush, kPortIndexBoth); +                    } +                } else { +#endif +                    bool emulateInputFlushCompletion = +                        !flushPortAsync(kPortIndexInput); -                bool emulateOutputFlushCompletion = -                    !flushPortAsync(kPortIndexOutput); +                    bool emulateOutputFlushCompletion = +                        !flushPortAsync(kPortIndexOutput); -                if (emulateInputFlushCompletion) { -                    onCmdComplete(OMX_CommandFlush, kPortIndexInput); -                } +                    if (emulateInputFlushCompletion) { +                        onCmdComplete(OMX_CommandFlush, kPortIndexInput); +                    } -                if (emulateOutputFlushCompletion) { -                    onCmdComplete(OMX_CommandFlush, kPortIndexOutput); +                    if (emulateOutputFlushCompletion) { +                        onCmdComplete(OMX_CommandFlush, kPortIndexOutput); +                    } +#ifdef QCOM_HARDWARE                  } +#endif              } else {                  mPortStatus[kPortIndexInput] = SHUTTING_DOWN;                  mPortStatus[kPortIndexOutput] = SHUTTING_DOWN; @@ -3966,16 +4375,39 @@ status_t OMXCodec::read(          CHECK_EQ((int)mState, (int)EXECUTING); -        bool emulateInputFlushCompletion = !flushPortAsync(kPortIndexInput); -        bool emulateOutputFlushCompletion = !flushPortAsync(kPortIndexOutput); +#ifdef QCOM_HARDWARE +        //DSP supports flushing of ports simultaneously. Flushing individual port is not supported. -        if (emulateInputFlushCompletion) { -            onCmdComplete(OMX_CommandFlush, kPortIndexInput); -        } +        if(mQuirks & kRequiresGlobalFlush) { +          bool emulateFlushCompletion = !flushPortAsync(kPortIndexBoth); +          if (emulateFlushCompletion) { +              onCmdComplete(OMX_CommandFlush, kPortIndexBoth); +          } +        } else { + +        //DSP supports flushing of ports simultaneously. +        //Flushing individual port is not supported. +        if(mQuirks & kRequiresGlobalFlush) { +            bool emulateFlushCompletion = !flushPortAsync(kPortIndexBoth); +            if (emulateFlushCompletion) { +                onCmdComplete(OMX_CommandFlush, kPortIndexBoth); +            } +        } else { +#endif +            bool emulateInputFlushCompletion = !flushPortAsync(kPortIndexInput); +            bool emulateOutputFlushCompletion = !flushPortAsync(kPortIndexOutput); -        if (emulateOutputFlushCompletion) { -            onCmdComplete(OMX_CommandFlush, kPortIndexOutput); +            if (emulateInputFlushCompletion) { +                onCmdComplete(OMX_CommandFlush, kPortIndexInput); +            } + +            if (emulateOutputFlushCompletion) { +                onCmdComplete(OMX_CommandFlush, kPortIndexOutput); +            } +#ifdef QCOM_HARDWARE +        }          } +#endif          while (mSeekTimeUs >= 0) {              if ((err = waitForBufferFilled_l()) != OK) { @@ -4553,9 +4985,46 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) {                  mOutputFormat->setInt32(kKeyChannelCount, numChannels);                  mOutputFormat->setInt32(kKeySampleRate, sampleRate);                  mOutputFormat->setInt32(kKeyBitRate, bitRate); +#ifdef QCOM_HARDWARE +            } else if (audio_def->eEncoding == OMX_AUDIO_CodingQCELP13 ) { +                mOutputFormat->setCString( +                        kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP); +                int32_t numChannels, sampleRate, bitRate; +                inputFormat->findInt32(kKeyChannelCount, &numChannels); +                inputFormat->findInt32(kKeySampleRate, &sampleRate); +                inputFormat->findInt32(kKeyBitRate, &bitRate); +                mOutputFormat->setInt32(kKeyChannelCount, numChannels); +                mOutputFormat->setInt32(kKeySampleRate, sampleRate); +                mOutputFormat->setInt32(kKeyBitRate, bitRate); +            } else if (audio_def->eEncoding == OMX_AUDIO_CodingEVRC ) { +                mOutputFormat->setCString( +                        kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_EVRC); +                int32_t numChannels, sampleRate, bitRate; +                inputFormat->findInt32(kKeyChannelCount, &numChannels); +                inputFormat->findInt32(kKeySampleRate, &sampleRate); +                inputFormat->findInt32(kKeyBitRate, &bitRate); +                mOutputFormat->setInt32(kKeyChannelCount, numChannels); +                mOutputFormat->setInt32(kKeySampleRate, sampleRate); +                mOutputFormat->setInt32(kKeyBitRate, bitRate);              } else { -                CHECK(!"Should not be here. Unknown audio encoding."); +                AString mimeType; +                if(OK == QCOMXCodec::checkQCFormats(audio_def->eEncoding, &mimeType)) { +                    mOutputFormat->setCString( +                            kKeyMIMEType, mimeType.c_str()); +                    int32_t numChannels, sampleRate, bitRate; +                    inputFormat->findInt32(kKeyChannelCount, &numChannels); +                    inputFormat->findInt32(kKeySampleRate, &sampleRate); +                    inputFormat->findInt32(kKeyBitRate, &bitRate); +                    mOutputFormat->setInt32(kKeyChannelCount, numChannels); +                    mOutputFormat->setInt32(kKeySampleRate, sampleRate); +                    mOutputFormat->setInt32(kKeyBitRate, bitRate); +#endif +                } else { +                    CHECK(!"Should not be here. Unknown audio encoding."); +                } +#ifdef QCOM_HARDWARE              } +#endif              break;          } @@ -4756,6 +5225,108 @@ status_t QueryCodecs(      return QueryCodecs(omx, mimeType, queryDecoders, false /*hwCodecOnly*/, results);  } +#ifdef QCOM_HARDWARE +void OMXCodec::setEVRCFormat(int32_t numChannels, int32_t sampleRate, int32_t bitRate) { +    if (mIsEncoder) { +      CHECK(numChannels == 1); +        //////////////// input port //////////////////// +        setRawAudioFormat(kPortIndexInput, sampleRate, numChannels); +        //////////////// output port //////////////////// +        // format +        OMX_AUDIO_PARAM_PORTFORMATTYPE format; +        format.nPortIndex = kPortIndexOutput; +        format.nIndex = 0; +        status_t err = OMX_ErrorNone; +        while (OMX_ErrorNone == err) { +            CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioPortFormat, +                    &format, sizeof(format)), (status_t)OK); +            if (format.eEncoding == OMX_AUDIO_CodingEVRC) { +                break; +            } +            format.nIndex++; +        } +        CHECK_EQ((status_t)OK, err); +        CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioPortFormat, +                &format, sizeof(format)), (status_t)OK); + +        // port definition +        OMX_PARAM_PORTDEFINITIONTYPE def; +        InitOMXParams(&def); +        def.nPortIndex = kPortIndexOutput; +        def.format.audio.cMIMEType = NULL; +        CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, +                &def, sizeof(def)), (status_t)OK); +        def.format.audio.bFlagErrorConcealment = OMX_TRUE; +        def.format.audio.eEncoding = OMX_AUDIO_CodingEVRC; +        CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition, +                &def, sizeof(def)), (status_t)OK); + +        // profile +        OMX_AUDIO_PARAM_EVRCTYPE profile; +        InitOMXParams(&profile); +        profile.nPortIndex = kPortIndexOutput; +        CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioEvrc, +                &profile, sizeof(profile)), (status_t)OK); +        profile.nChannels = numChannels; +        CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioEvrc, +                &profile, sizeof(profile)), (status_t)OK); +    } +    else{ +      ALOGI("EVRC decoder \n"); +    } +} + +void OMXCodec::setQCELPFormat(int32_t numChannels, int32_t sampleRate, int32_t bitRate) { +    if (mIsEncoder) { +        CHECK(numChannels == 1); +        //////////////// input port //////////////////// +        setRawAudioFormat(kPortIndexInput, sampleRate, numChannels); +        //////////////// output port //////////////////// +        // format +        OMX_AUDIO_PARAM_PORTFORMATTYPE format; +        format.nPortIndex = kPortIndexOutput; +        format.nIndex = 0; +        status_t err = OMX_ErrorNone; +        while (OMX_ErrorNone == err) { +            CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioPortFormat, +                    &format, sizeof(format)), (status_t)OK); +            if (format.eEncoding == OMX_AUDIO_CodingQCELP13) { +                break; +            } +            format.nIndex++; +        } +        CHECK_EQ((status_t)OK, err); +        CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioPortFormat, +                &format, sizeof(format)), (status_t)OK); + +        // port definition +        OMX_PARAM_PORTDEFINITIONTYPE def; +        InitOMXParams(&def); +        def.nPortIndex = kPortIndexOutput; +        def.format.audio.cMIMEType = NULL; +        CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, +                &def, sizeof(def)), (status_t)OK); +        def.format.audio.bFlagErrorConcealment = OMX_TRUE; +        def.format.audio.eEncoding = OMX_AUDIO_CodingQCELP13; +        CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition, +                &def, sizeof(def)), (status_t)OK); + +        // profile +        OMX_AUDIO_PARAM_QCELP13TYPE profile; +        InitOMXParams(&profile); +        profile.nPortIndex = kPortIndexOutput; +        CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioQcelp13, +                &profile, sizeof(profile)), (status_t)OK); +        profile.nChannels = numChannels; +        CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioQcelp13, +                &profile, sizeof(profile)), (status_t)OK); +    } +    else{ +      ALOGI("QCELP decoder \n"); +    } +} +#endif +  // These are supposed be equivalent to the logic in  // "audio_channel_out_mask_from_count".  status_t getOMXChannelMapping(size_t numChannels, OMX_AUDIO_CHANNELTYPE map[]) { diff --git a/media/libstagefright/QCMediaDefs.cpp b/media/libstagefright/QCMediaDefs.cpp new file mode 100644 index 0000000..ec2d04e --- /dev/null +++ b/media/libstagefright/QCMediaDefs.cpp @@ -0,0 +1,55 @@ +/*Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + *     * Redistributions of source code must retain the above copyright + *      notice, this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above + *       copyright notice, this list of conditions and the following + *       disclaimer in the documentation and/or other materials provided + *      with the distribution. + *     * Neither the name of The Linux Foundation nor the names of its + *      contributors may be used to endorse or promote products derived + *       from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <QCMediaDefs.h> + +namespace android { + +const char *MEDIA_MIMETYPE_AUDIO_EVRC = "audio/evrc"; + +const char *MEDIA_MIMETYPE_VIDEO_WMV = "video/x-ms-wmv"; +const char *MEDIA_MIMETYPE_AUDIO_WMA = "audio/x-ms-wma"; +const char *MEDIA_MIMETYPE_CONTAINER_ASF = "video/x-ms-asf"; +const char *MEDIA_MIMETYPE_VIDEO_DIVX = "video/divx"; +const char *MEDIA_MIMETYPE_AUDIO_AC3 = "audio/ac3"; +const char *MEDIA_MIMETYPE_CONTAINER_AAC = "audio/aac"; +const char *MEDIA_MIMETYPE_CONTAINER_QCP = "audio/vnd.qcelp"; +const char *MEDIA_MIMETYPE_VIDEO_DIVX311 = "video/divx311"; +const char *MEDIA_MIMETYPE_VIDEO_DIVX4 = "video/divx4"; + +const char *MEDIA_MIMETYPE_CONTAINER_MPEG2 = "video/mp2"; + +const char *MEDIA_MIMETYPE_CONTAINER_3G2 = "video/3g2"; +const char *MEDIA_MIMETYPE_AUDIO_DTS = "audio/dts"; + +const char *MEDIA_MIMETYPE_AUDIO_DTS_LBR = "audio/dts-lbr"; +const char *MEDIA_MIMETYPE_AUDIO_EAC3 = "audio/eac3"; +const char *MEDIA_MIMETYPE_AUDIO_AMR_WB_PLUS = "audio/amr-wb-plus"; + +}  // namespace android + diff --git a/media/libstagefright/QCOMXCodec.cpp b/media/libstagefright/QCOMXCodec.cpp new file mode 100644 index 0000000..1b24c8b --- /dev/null +++ b/media/libstagefright/QCOMXCodec.cpp @@ -0,0 +1,548 @@ +/* + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + *     * Redistributions of source code must retain the above copyright + *      notice, this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above + *       copyright notice, this list of conditions and the following + *       disclaimer in the documentation and/or other materials provided + *      with the distribution. + *     * Neither the name of The Linux Foundation nor the names of its + *      contributors may be used to endorse or promote products derived + *       from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "QCOMXCodec" +#include <utils/Log.h> + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaCodecList.h> + +#include <media/stagefright/MetaData.h> +#include <media/stagefright/QCOMXCodec.h> +#include <media/stagefright/OMXCodec.h> +#include <QCMetaData.h> +#include <QCMediaDefs.h> +#include <OMX_QCOMExtns.h> + +#include <OMX_Component.h> +#include <QOMX_AudioExtensions.h> + + +namespace android { + +uint32_t QCOMXCodec::getQCComponentQuirks(const MediaCodecList *list, size_t index) { +    uint32_t quirks = 0; + +    if (list->codecHasQuirk( +                index, "requires-wma-pro-component")) { +        quirks |= kRequiresWMAProComponent; +    } +    return quirks; +} + +void QCOMXCodec::setASFQuirks(uint32_t quirks, const sp<MetaData> &meta, const char* componentName) { +    if(quirks & kRequiresWMAProComponent) +    { +       int32_t version; +       CHECK(meta->findInt32(kKeyWMAVersion, &version)); +       if(version==kTypeWMA) { +          componentName = "OMX.qcom.audio.decoder.wma"; +       } else if(version==kTypeWMAPro) { +          componentName= "OMX.qcom.audio.decoder.wma10Pro"; +       } else if(version==kTypeWMALossLess) { +          componentName= "OMX.qcom.audio.decoder.wmaLossLess"; +       } +    } +} + +template<class T> +static void InitOMXParams(T *params) { +    params->nSize = sizeof(T); +    params->nVersion.s.nVersionMajor = 1; +    params->nVersion.s.nVersionMinor = 0; +    params->nVersion.s.nRevision = 0; +    params->nVersion.s.nStep = 0; +} + + +status_t QCOMXCodec::configureDIVXCodec(const sp<MetaData> &meta, char* mime, sp<IOMX> OMXhandle, IOMX::node_id nodeID, int port_index) { +    status_t err = OK; +    if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX, mime) || +        !strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX4, mime) || +        !strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX311, mime)) { +        ALOGV("Setting the QOMX_VIDEO_PARAM_DIVXTYPE params "); +        QOMX_VIDEO_PARAM_DIVXTYPE paramDivX; +        InitOMXParams(¶mDivX); +        paramDivX.nPortIndex = port_index; +        int32_t DivxVersion = 0; +        CHECK(meta->findInt32(kKeyDivXVersion,&DivxVersion)); +        ALOGV("Divx Version Type %d\n",DivxVersion); + +        if(DivxVersion == kTypeDivXVer_4) { +            paramDivX.eFormat = QOMX_VIDEO_DIVXFormat4; +        } else if(DivxVersion == kTypeDivXVer_5) { +            paramDivX.eFormat = QOMX_VIDEO_DIVXFormat5; +        } else if(DivxVersion == kTypeDivXVer_6) { +            paramDivX.eFormat = QOMX_VIDEO_DIVXFormat6; +        } else if(DivxVersion == kTypeDivXVer_3_11 ) { +            paramDivX.eFormat = QOMX_VIDEO_DIVXFormat311; +        } else { +            paramDivX.eFormat = QOMX_VIDEO_DIVXFormatUnused; +        } +        paramDivX.eProfile = (QOMX_VIDEO_DIVXPROFILETYPE)0;    //Not used for now. + +        err =  OMXhandle->setParameter(nodeID, +                         (OMX_INDEXTYPE)OMX_QcomIndexParamVideoDivx, +                         ¶mDivX, sizeof(paramDivX)); +    } + +    return err; +} + +void QCOMXCodec::checkAndAddRawFormat(OMXCodec *handle, const sp<MetaData> &meta){ +    uint32_t type; +    const void *data; +    size_t size; + +    if (meta->findData(kKeyRawCodecSpecificData, &type, &data, &size)) { +        ALOGV("OMXCodec::configureCodec found kKeyRawCodecSpecificData of size %d\n", size); +        handle->addCodecSpecificData(data, size); +    } + +} + +status_t QCOMXCodec::setQCFormat(const sp<MetaData> &meta, char* mime, sp<IOMX> OMXhandle, +                                     IOMX::node_id nodeID, OMXCodec *handle, bool isEncoder ) { +    ALOGV("setQCFormat -- called "); +    status_t err = OK; +    if ((!strcasecmp(MEDIA_MIMETYPE_AUDIO_AC3, mime)) || +        (!strcasecmp(MEDIA_MIMETYPE_AUDIO_EAC3, mime))){ +        int32_t numChannels, sampleRate; +        CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); +        CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); +        setAC3Format(numChannels, sampleRate, OMXhandle, nodeID); +    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_EVRC, mime)) { +        int32_t numChannels, sampleRate; +        CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); +        CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); +        setEVRCFormat(numChannels, sampleRate, OMXhandle, nodeID, handle,isEncoder ); +    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_QCELP, mime)) { +        int32_t numChannels, sampleRate; +        CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); +        CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); +        setQCELPFormat(numChannels, sampleRate, OMXhandle, nodeID, handle,isEncoder); +    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_WMA, mime))  { +        err = setWMAFormat(meta, OMXhandle, nodeID, isEncoder); +    } +    return err; +} + + +void QCOMXCodec::setEVRCFormat(int32_t numChannels, int32_t sampleRate, sp<IOMX> OMXhandle, +                                    IOMX::node_id nodeID, OMXCodec *handle,  bool isEncoder ) { +    ALOGV("setEVRCFormat -- called "); +    if (isEncoder) { +        CHECK(numChannels == 1); +        //////////////// input port //////////////////// +        handle->setRawAudioFormat(OMXCodec::kPortIndexInput, sampleRate, numChannels); +        //////////////// output port //////////////////// +        // format +        OMX_AUDIO_PARAM_PORTFORMATTYPE format; +        format.nPortIndex = OMXCodec::kPortIndexOutput; +        format.nIndex = 0; +        status_t err = OMX_ErrorNone; +        while (OMX_ErrorNone == err) { +            CHECK_EQ(OMXhandle->getParameter(nodeID, OMX_IndexParamAudioPortFormat, +                    &format, sizeof(format)), (status_t)OK); +            if (format.eEncoding == OMX_AUDIO_CodingEVRC) { +                break; +            } +            format.nIndex++; +        } +        CHECK_EQ((status_t)OK, err); +        CHECK_EQ(OMXhandle->setParameter(nodeID, OMX_IndexParamAudioPortFormat, +                &format, sizeof(format)), (status_t)OK); + +        // port definition +        OMX_PARAM_PORTDEFINITIONTYPE def; +        InitOMXParams(&def); +        def.nPortIndex = OMXCodec::kPortIndexOutput; +        def.format.audio.cMIMEType = NULL; +        CHECK_EQ(OMXhandle->getParameter(nodeID, OMX_IndexParamPortDefinition, +                &def, sizeof(def)), (status_t)OK); +        def.format.audio.bFlagErrorConcealment = OMX_TRUE; +        def.format.audio.eEncoding = OMX_AUDIO_CodingEVRC; +        CHECK_EQ(OMXhandle->setParameter(nodeID, OMX_IndexParamPortDefinition, +                &def, sizeof(def)), (status_t)OK); + +        // profile +        OMX_AUDIO_PARAM_EVRCTYPE profile; +        InitOMXParams(&profile); +        profile.nPortIndex = OMXCodec::kPortIndexOutput; +        CHECK_EQ(OMXhandle->getParameter(nodeID, OMX_IndexParamAudioEvrc, +                &profile, sizeof(profile)), (status_t)OK); +        profile.nChannels = numChannels; +        CHECK_EQ(OMXhandle->setParameter(nodeID, OMX_IndexParamAudioEvrc, +                &profile, sizeof(profile)), (status_t)OK); +    } +    else{ +      ALOGI("EVRC decoder \n"); +    } +} + + +void QCOMXCodec::setQCELPFormat(int32_t numChannels, int32_t sampleRate, sp<IOMX> OMXhandle, +                                     IOMX::node_id nodeID, OMXCodec *handle, bool isEncoder ) { +    if (isEncoder) { +        CHECK(numChannels == 1); +        //////////////// input port //////////////////// +        handle->setRawAudioFormat(OMXCodec::kPortIndexInput, sampleRate, numChannels); +        //////////////// output port //////////////////// +        // format +        OMX_AUDIO_PARAM_PORTFORMATTYPE format; +        format.nPortIndex = OMXCodec::kPortIndexOutput; +        format.nIndex = 0; +        status_t err = OMX_ErrorNone; +        while (OMX_ErrorNone == err) { +            CHECK_EQ(OMXhandle->getParameter(nodeID, OMX_IndexParamAudioPortFormat, +                    &format, sizeof(format)), (status_t)OK); +            if (format.eEncoding == OMX_AUDIO_CodingQCELP13) { +                break; +            } +            format.nIndex++; +        } +        CHECK_EQ((status_t)OK, err); +        CHECK_EQ(OMXhandle->setParameter(nodeID, OMX_IndexParamAudioPortFormat, +                &format, sizeof(format)), (status_t)OK); + +        // port definition +        OMX_PARAM_PORTDEFINITIONTYPE def; +        InitOMXParams(&def); +        def.nPortIndex = OMXCodec::kPortIndexOutput; +        def.format.audio.cMIMEType = NULL; +        CHECK_EQ(OMXhandle->getParameter(nodeID, OMX_IndexParamPortDefinition, +                &def, sizeof(def)), (status_t)OK); +        def.format.audio.bFlagErrorConcealment = OMX_TRUE; +        def.format.audio.eEncoding = OMX_AUDIO_CodingQCELP13; +        CHECK_EQ(OMXhandle->setParameter(nodeID, OMX_IndexParamPortDefinition, +                &def, sizeof(def)), (status_t)OK); + +        // profile +        OMX_AUDIO_PARAM_QCELP13TYPE profile; +        InitOMXParams(&profile); +        profile.nPortIndex = OMXCodec::kPortIndexOutput; +        CHECK_EQ(OMXhandle->getParameter(nodeID, OMX_IndexParamAudioQcelp13, +                &profile, sizeof(profile)), (status_t)OK); +        profile.nChannels = numChannels; +        CHECK_EQ(OMXhandle->setParameter(nodeID, OMX_IndexParamAudioQcelp13, +                &profile, sizeof(profile)), (status_t)OK); +    } +    else { +        ALOGI("QCELP decoder \n"); +    } +} + +status_t QCOMXCodec::setWMAFormat(const sp<MetaData> &meta, sp<IOMX> OMXhandle, +                                       IOMX::node_id nodeID, bool isEncoder ) { +    ALOGV("setWMAFormat Called"); +    if (isEncoder) { +        ALOGE("WMA encoding not supported"); +        return OK; +    } else { +        int32_t version; +        OMX_AUDIO_PARAM_WMATYPE paramWMA; +        QOMX_AUDIO_PARAM_WMA10PROTYPE paramWMA10; +        CHECK(meta->findInt32(kKeyWMAVersion, &version)); +        int32_t numChannels; +        int32_t bitRate; +        int32_t sampleRate; +        int32_t encodeOptions; +        int32_t blockAlign; +        int32_t bitspersample; +        int32_t formattag; +        int32_t advencopt1; +        int32_t advencopt2; +        int32_t VirtualPktSize; +        if(version==kTypeWMAPro || version==kTypeWMALossLess) { +            CHECK(meta->findInt32(kKeyWMABitspersample, &bitspersample)); +            CHECK(meta->findInt32(kKeyWMAFormatTag, &formattag)); +            CHECK(meta->findInt32(kKeyWMAAdvEncOpt1,&advencopt1)); +            CHECK(meta->findInt32(kKeyWMAAdvEncOpt2,&advencopt2)); +            CHECK(meta->findInt32(kKeyWMAVirPktSize,&VirtualPktSize)); +        } +        if(version==kTypeWMA) { +            InitOMXParams(¶mWMA); +            paramWMA.nPortIndex = OMXCodec::kPortIndexInput; +        } else if(version==kTypeWMAPro || version==kTypeWMALossLess) { +            InitOMXParams(¶mWMA10); +            paramWMA10.nPortIndex = OMXCodec::kPortIndexInput; +        } +        CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); +        CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); +        CHECK(meta->findInt32(kKeyBitRate, &bitRate)); +        CHECK(meta->findInt32(kKeyWMAEncodeOpt, &encodeOptions)); +        CHECK(meta->findInt32(kKeyWMABlockAlign, &blockAlign)); +        ALOGV("Channels: %d, SampleRate: %d, BitRate; %d" +                   "EncodeOptions: %d, blockAlign: %d", numChannels, +                   sampleRate, bitRate, encodeOptions, blockAlign); +        if(sampleRate>48000 || numChannels>2) +        { +            ALOGE("Unsupported samplerate/channels"); +            return ERROR_UNSUPPORTED; +        } +        if(version==kTypeWMAPro || version==kTypeWMALossLess) +        { +            ALOGV("Bitspersample: %d, wmaformattag: %d," +                       "advencopt1: %d, advencopt2: %d VirtualPktSize %d", bitspersample, +                       formattag, advencopt1, advencopt2, VirtualPktSize); +        } +        status_t err = OK; +        OMX_INDEXTYPE index; +        if(version==kTypeWMA) { +            err = OMXhandle->getParameter( +                   nodeID, OMX_IndexParamAudioWma, ¶mWMA, sizeof(paramWMA)); +        } else if(version==kTypeWMAPro || version==kTypeWMALossLess) { +            OMXhandle->getExtensionIndex(nodeID,"OMX.Qualcomm.index.audio.wma10Pro",&index); +            err = OMXhandle->getParameter( +                   nodeID, index, ¶mWMA10, sizeof(paramWMA10)); +        } +        CHECK_EQ(err, (status_t)OK); +        if(version==kTypeWMA) { +            paramWMA.nChannels = numChannels; +            paramWMA.nSamplingRate = sampleRate; +            paramWMA.nEncodeOptions = encodeOptions; +            paramWMA.nBitRate = bitRate; +            paramWMA.nBlockAlign = blockAlign; +        } else if(version==kTypeWMAPro || version==kTypeWMALossLess) { +            paramWMA10.nChannels = numChannels; +            paramWMA10.nSamplingRate = sampleRate; +            paramWMA10.nEncodeOptions = encodeOptions; +            paramWMA10.nBitRate = bitRate; +            paramWMA10.nBlockAlign = blockAlign; +        } +        if(version==kTypeWMAPro || version==kTypeWMALossLess) { +            paramWMA10.advancedEncodeOpt = advencopt1; +            paramWMA10.advancedEncodeOpt2 = advencopt2; +            paramWMA10.formatTag = formattag; +            paramWMA10.validBitsPerSample = bitspersample; +            paramWMA10.nVirtualPktSize = VirtualPktSize; +        } +        if(version==kTypeWMA) { +            err = OMXhandle->setParameter( +                  nodeID, OMX_IndexParamAudioWma, ¶mWMA, sizeof(paramWMA)); +        } else if(version==kTypeWMAPro || version==kTypeWMALossLess) { +           err = OMXhandle->setParameter( +                 nodeID, index, ¶mWMA10, sizeof(paramWMA10)); +        } +        return err; +    } +    return OK; +} + + +void QCOMXCodec::setAC3Format(int32_t numChannels, int32_t sampleRate, sp<IOMX> OMXhandle, +                                  IOMX::node_id nodeID) { +    QOMX_AUDIO_PARAM_AC3TYPE profileAC3; +    QOMX_AUDIO_PARAM_AC3PP profileAC3PP; +    OMX_INDEXTYPE indexTypeAC3; +    OMX_INDEXTYPE indexTypeAC3PP; +    OMX_PARAM_PORTDEFINITIONTYPE portParam; + +    //configure input port +    ALOGV("setAC3Format samplerate %d, numChannels %d", sampleRate, numChannels); +    InitOMXParams(&portParam); +    portParam.nPortIndex = 0; +    status_t err = OMXhandle->getParameter( +       nodeID, OMX_IndexParamPortDefinition, &portParam, sizeof(portParam)); +    CHECK_EQ(err, (status_t)OK); +    err = OMXhandle->setParameter( +       nodeID, OMX_IndexParamPortDefinition, &portParam, sizeof(portParam)); +    CHECK_EQ(err, (status_t)OK); + +    //configure output port +    portParam.nPortIndex = 1; +    err = OMXhandle->getParameter( +       nodeID, OMX_IndexParamPortDefinition, &portParam, sizeof(portParam)); +    CHECK_EQ(err, (status_t)OK); +    err = OMXhandle->setParameter( +       nodeID, OMX_IndexParamPortDefinition, &portParam, sizeof(portParam)); +    CHECK_EQ(err, (status_t)OK); + +    err = OMXhandle->getExtensionIndex(nodeID, OMX_QCOM_INDEX_PARAM_AC3TYPE, &indexTypeAC3); + +    InitOMXParams(&profileAC3); +    profileAC3.nPortIndex = OMXCodec::kPortIndexInput; +    err = OMXhandle->getParameter(nodeID, indexTypeAC3, &profileAC3, sizeof(profileAC3)); +    CHECK_EQ(err,(status_t)OK); + +    profileAC3.nSamplingRate  =  sampleRate; +    profileAC3.nChannels      =  2; +    profileAC3.eChannelConfig =  OMX_AUDIO_AC3_CHANNEL_CONFIG_2_0; + +    ALOGV("numChannels = %d, profileAC3.nChannels = %d", numChannels, profileAC3.nChannels); + +    err = OMXhandle->setParameter(nodeID, indexTypeAC3, &profileAC3, sizeof(profileAC3)); +    CHECK_EQ(err,(status_t)OK); + +    //for output port +    OMX_AUDIO_PARAM_PCMMODETYPE profilePcm; +    InitOMXParams(&profilePcm); +    profilePcm.nPortIndex = OMXCodec::kPortIndexOutput; +    err = OMXhandle->getParameter(nodeID, OMX_IndexParamAudioPcm, &profilePcm, sizeof(profilePcm)); +    CHECK_EQ(err, (status_t)OK); + +    profilePcm.nSamplingRate  =  sampleRate; +    err = OMXhandle->setParameter(nodeID, OMX_IndexParamAudioPcm, &profilePcm, sizeof(profilePcm)); +    CHECK_EQ(err, (status_t)OK); +    OMXhandle->getExtensionIndex(nodeID, OMX_QCOM_INDEX_PARAM_AC3PP, &indexTypeAC3PP); + +    InitOMXParams(&profileAC3PP); +    profileAC3PP.nPortIndex = OMXCodec::kPortIndexInput; +    err = OMXhandle->getParameter(nodeID, indexTypeAC3PP, &profileAC3PP, sizeof(profileAC3PP)); +    CHECK_EQ(err, (status_t)OK); + +    int i; +    int channel_routing[6]; + +    for (i=0; i<6; i++) { +        channel_routing[i] = -1; +    } +    for (i=0; i<6; i++) { +        profileAC3PP.eChannelRouting[i] =  (OMX_AUDIO_AC3_CHANNEL_ROUTING)channel_routing[i]; +    } + +    profileAC3PP.eChannelRouting[0] =  OMX_AUDIO_AC3_CHANNEL_LEFT; +    profileAC3PP.eChannelRouting[1] =  OMX_AUDIO_AC3_CHANNEL_RIGHT; +    err = OMXhandle->setParameter(nodeID, indexTypeAC3PP, &profileAC3PP, sizeof(profileAC3PP)); +    CHECK_EQ(err, (status_t)OK); +} + + +status_t QCOMXCodec::setQCVideoInputFormat(const char *mime, OMX_VIDEO_CODINGTYPE *compressionFormat) { +    status_t retVal = OK; +    if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX, mime)){ +        *compressionFormat= (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingDivx; +    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX4, mime)){ +        *compressionFormat= (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingDivx; +    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX311, mime)){ +        *compressionFormat= (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingDivx; +    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_WMV, mime)){ +        *compressionFormat = OMX_VIDEO_CodingWMV; +    } else if (!strcasecmp(MEDIA_MIMETYPE_CONTAINER_MPEG2, mime)){ +        *compressionFormat = OMX_VIDEO_CodingMPEG2; +    } else { +        retVal = BAD_VALUE; +    } + +    return retVal; +} + +status_t QCOMXCodec::setQCVideoOutputFormat(const char *mime, OMX_VIDEO_CODINGTYPE *compressionFormat) { +    status_t retVal = OK; +    if(!strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX, mime)) { +        *compressionFormat = (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingDivx; +    } else if(!strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX311, mime)) { +        *compressionFormat = (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingDivx; +    } else if(!strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX4, mime)) { +        *compressionFormat = (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingDivx; +    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_WMV, mime)){ +        *compressionFormat = OMX_VIDEO_CodingWMV; +    } else { +        retVal = BAD_VALUE; +    } +    return retVal; +} + + +void QCOMXCodec::checkQCRole( const sp<IOMX> &omx, IOMX::node_id node, +                                  bool isEncoder, const char *mime){ +    ALOGV("checkQCRole Called"); +    struct MimeToRole { +        const char *mime; +        const char *decoderRole; +        const char *encoderRole; +    }; + +    static const MimeToRole kQCMimeToRole[] = { +        { MEDIA_MIMETYPE_AUDIO_EVRC, +          "audio_decoder.evrchw", "audio_encoder.evrc" }, +        { MEDIA_MIMETYPE_AUDIO_QCELP, +          "audio_decoder,qcelp13Hw", "audio_encoder.qcelp13" }, +        { MEDIA_MIMETYPE_VIDEO_DIVX, +          "video_decoder.divx", NULL }, +        { MEDIA_MIMETYPE_AUDIO_AC3, +          "audio_decoder.ac3", NULL }, +        { MEDIA_MIMETYPE_VIDEO_DIVX311, +          "video_decoder.divx", NULL }, +        }; + +    static const size_t kNumMimeToRole = +                     sizeof(kQCMimeToRole) / sizeof(kQCMimeToRole[0]); + +    size_t i; +    for (i = 0; i < kNumMimeToRole; ++i) { +        if (!strcasecmp(mime, kQCMimeToRole[i].mime)) { +            break; +        } +    } + +    if (i == kNumMimeToRole) { +        return; +    } + +    const char *role = +        isEncoder ? kQCMimeToRole[i].encoderRole +                  : kQCMimeToRole[i].decoderRole; + +    if (role != NULL) { +        OMX_PARAM_COMPONENTROLETYPE roleParams; +        InitOMXParams(&roleParams); + +        strncpy((char *)roleParams.cRole, +                role, OMX_MAX_STRINGNAME_SIZE - 1); + +        roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0'; + +        status_t err = omx->setParameter( +                node, OMX_IndexParamStandardComponentRole, +                &roleParams, sizeof(roleParams)); + +        if (err != OK) { +            ALOGW("Failed to set standard component role '%s'.", role); +        } +    } + +} + +status_t QCOMXCodec::checkQCFormats(int format, AString* meta){ +    ALOGV("checkQCFormats called"); +    status_t retVal = OK; +    if (format == OMX_AUDIO_CodingQCELP13 ) { +        *meta = MEDIA_MIMETYPE_AUDIO_QCELP; +    } else if(format == OMX_AUDIO_CodingEVRC ) { +        *meta = MEDIA_MIMETYPE_AUDIO_EVRC; +    } else { +        retVal = BAD_VALUE; +    } +    return retVal; +} + +} diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index b7cf96e..510252a 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -1,5 +1,6 @@  /*   * Copyright (C) 2009 The Android Open Source Project + * Copyright (c) 2012, The Linux Foundation. All rights reserved.   *   * Licensed under the Apache License, Version 2.0 (the "License");   * you may not use this file except in compliance with the License. @@ -42,7 +43,12 @@ static bool FileHasAcceptableExtension(const char *extension) {          ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",          ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",          ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf", -        ".avi", ".mpeg", ".mpg" +        ".avi", ".mpg", +#ifndef QCOM_HARDWARE +       ".mpeg" +#else +        ".qcp", ".awb", ".ac3", ".dts", ".wmv" +#endif      };      static const size_t kNumValidExtensions =          sizeof(kValidExtensions) / sizeof(kValidExtensions[0]); diff --git a/media/libstagefright/TunnelPlayer.cpp b/media/libstagefright/TunnelPlayer.cpp new file mode 100644 index 0000000..34c260f --- /dev/null +++ b/media/libstagefright/TunnelPlayer.cpp @@ -0,0 +1,782 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved. + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only. + * + * + * 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_NDDEBUG 0 +#define LOG_NDEBUG 0 +#define LOG_TAG "TunnelPlayer" +#include <utils/Log.h> +#include <utils/threads.h> + +#include <signal.h> +#include <sys/prctl.h> +#include <sys/resource.h> +#include <sys/poll.h> +#include <sys/eventfd.h> +#include <binder/IPCThreadState.h> +#include <media/AudioTrack.h> + +#include <media/stagefright/TunnelPlayer.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/ALooper.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/MediaErrors.h> + +#include <hardware_legacy/power.h> + +#include <linux/unistd.h> + +#include "include/AwesomePlayer.h" +#include <powermanager/PowerManager.h> + +static const char   mName[] = "TunnelPlayer"; +#define MEM_METADATA_SIZE 64 +#define MEM_BUFFER_SIZE (600*1024 - MEM_METADATA_SIZE) +#define MEM_BUFFER_COUNT 4 + +namespace android { +int TunnelPlayer::mTunnelObjectsAlive = 0; + +TunnelPlayer::TunnelPlayer( +                    const sp<MediaPlayerBase::AudioSink> &audioSink, bool &initCheck, +                    AwesomePlayer *observer, bool hasVideo) +:AudioPlayer(audioSink,observer), +mPositionTimeMediaUs(-1), +mPositionTimeRealUs(-1), +mInternalSeeking(false), +mStarted(false), +mA2DPEnabled(false), +mSampleRate(0), +mLatencyUs(0), +mFrameSize(0), +mNumFramesPlayed(0), +mNumFramesPlayedSysTimeUs(0), +mInputBuffer(NULL), +mSeeking(false), +mReachedEOS(false), +mReachedOutputEOS(false), +mFinalStatus(OK), +mSeekTimeUs(0), +mPauseTime(0), +mIsFirstBuffer(false), +mFirstBufferResult(OK), +mFirstBuffer(NULL), +mAudioSink(audioSink), +mObserver(observer) { +    ALOGD("TunnelPlayer::TunnelPlayer()"); +    mTunnelObjectsAlive++; +    numChannels = 0; +    mPaused = false; +    mIsA2DPEnabled = false; +    mAudioFlinger = NULL; +    mAudioFlingerClient = NULL; +    mFormat = AUDIO_FORMAT_MP3; +    mQueue.start(); +    mQueueStarted      = true; +    mPauseEvent        = new TunnelEvent(this, &TunnelPlayer::onPauseTimeOut); +    mPauseEventPending = false; + +    //getAudioFlinger(); +    //ALOGD("Registering client with AudioFlinger"); +    //mAudioFlinger->registerClient(mAudioFlingerClient); + +    mSeekTimeUs = 0; + +    mHasVideo = hasVideo; +    initCheck = true; + +    //mDeathRecipient = new PMDeathRecipient(this); +} +void TunnelPlayer::acquireWakeLock() +{ +    /*Mutex::Autolock _l(pmLock); + +    if (mPowerManager == 0) { +        // use checkService() to avoid blocking if power service is not up yet +        sp<IBinder> binder = +            defaultServiceManager()->checkService(String16("power")); +        if (binder == 0) { +            ALOGW("Thread %s cannot connect to the power manager service", mName); +        } else { +            mPowerManager = interface_cast<IPowerManager>(binder); +            binder->linkToDeath(mDeathRecipient); +        } +    } +    if (mPowerManager != 0 && mWakeLockToken == 0) { +        sp<IBinder> binder = new BBinder(); +        status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK, +                                                         binder, +                                                         String16(mName)); +        if (status == NO_ERROR) { +            mWakeLockToken = binder; +        } +        ALOGV("acquireWakeLock() %s status %d", mName, status); +    }*/ +} + +void TunnelPlayer::releaseWakeLock() +{ +    /*Mutex::Autolock _l(pmLock); + +    if (mWakeLockToken != 0) { +        ALOGV("releaseWakeLock() %s", mName); +        if (mPowerManager != 0) { +            mPowerManager->releaseWakeLock(mWakeLockToken, 0); +        } +        mWakeLockToken.clear(); +    }*/ +} + +void TunnelPlayer::clearPowerManager() +{ +    Mutex::Autolock _l(pmLock); +    releaseWakeLock(); +    mPowerManager.clear(); +} + +void TunnelPlayer::PMDeathRecipient::binderDied(const wp<IBinder>& who) +{ +    parentClass->clearPowerManager(); +    ALOGW("power manager service died !!!"); +} + +TunnelPlayer::~TunnelPlayer() { +    ALOGD("TunnelPlayer::~TunnelPlayer()"); +    if (mQueueStarted) { +        mQueue.stop(); +    } + +    reset(); +    //mAudioFlinger->deregisterClient(mAudioFlingerClient); +    mTunnelObjectsAlive--; + +    releaseWakeLock(); +    if (mPowerManager != 0) { +        sp<IBinder> binder = mPowerManager->asBinder(); +        binder->unlinkToDeath(mDeathRecipient); +    } + + +} + +void TunnelPlayer::getAudioFlinger() { +/*    Mutex::Autolock _l(mAudioFlingerLock); + +    if ( mAudioFlinger.get() == 0 ) { +        sp<IServiceManager> sm = defaultServiceManager(); +        sp<IBinder> binder; +        do { +            binder = sm->getService(String16("media.audio_flinger")); +            if ( binder != 0 ) +                break; +            ALOGW("AudioFlinger not published, waiting..."); +            usleep(500000); // 0.5 s +        } while ( true ); +        if ( mAudioFlingerClient == NULL ) { +            mAudioFlingerClient = new AudioFlingerTunnelDecodeClient(this); +        } + +        binder->linkToDeath(mAudioFlingerClient); +        mAudioFlinger = interface_cast<IAudioFlinger>(binder); +    } +    ALOGE_IF(mAudioFlinger==0, "no AudioFlinger!?");*/ +} + +/*TunnelPlayer::AudioFlingerTunnelDecodeClient::AudioFlingerTunnelDecodeClient(void *obj) +{ +    ALOGD("TunnelPlayer::AudioFlingerTunnelDecodeClient - Constructor"); +    pBaseClass = (TunnelPlayer*)obj; +} + +void TunnelPlayer::AudioFlingerTunnelDecodeClient::binderDied(const wp<IBinder>& who) { +    Mutex::Autolock _l(pBaseClass->mAudioFlingerLock); + +    pBaseClass->mAudioFlinger.clear(); +    ALOGW("AudioFlinger server died!"); +}*/ + +/*void TunnelPlayer::AudioFlingerTunnelDecodeClient::ioConfigChanged(int event, int ioHandle, void *param2) { +    ALOGV("ioConfigChanged() event %d", event); + + +    if (event != AudioSystem::A2DP_OUTPUT_STATE) { +        return; +    } + +    switch ( event ) { +    case AudioSystem::A2DP_OUTPUT_STATE: +        { +            if ( -1 == ioHandle ) { +                if ( pBaseClass->mIsA2DPEnabled ) { +                    pBaseClass->mIsA2DPEnabled = false; +                    if (pBaseClass->mStarted) { +                        pBaseClass->handleA2DPSwitch(); +                    } +                    ALOGV("ioConfigChanged:: A2DP Disabled"); +                } +            } else { +                if ( !pBaseClass->mIsA2DPEnabled ) { +                    pBaseClass->mIsA2DPEnabled = true; +                    if (pBaseClass->mStarted) { +                        pBaseClass->handleA2DPSwitch(); +                    } +                    ALOGV("ioConfigChanged:: A2DP Enabled"); +                } +            } +        } +        break; +    default: +        break; +    } +    ALOGV("ioConfigChanged Out"); +}*/ + +/*void TunnelPlayer::handleA2DPSwitch() { +    //TODO: Implement +} +*/ + +void TunnelPlayer::setSource(const sp<MediaSource> &source) { +    CHECK(mSource == NULL); +    ALOGD("Setting source from Tunnel Player"); +    mSource = source; +} + +status_t TunnelPlayer::start(bool sourceAlreadyStarted) { +    CHECK(!mStarted); +    CHECK(mSource != NULL); + +    ALOGD("start: sourceAlreadyStarted %d", sourceAlreadyStarted); +    //Check if the source is started, start it +    status_t err; +    if (!sourceAlreadyStarted) { +        err = mSource->start(); +        if (err != OK) { +            return err; +        } +    } + +    //Create decoder and a2dp notification thread and initialize all the +    //mutexes and coditional variables +    createThreads(); +    ALOGV("All Threads Created."); + +    // We allow an optional INFO_FORMAT_CHANGED at the very beginning +    // of playback, if there is one, getFormat below will retrieve the +    // updated format, if there isn't, we'll stash away the valid buffer +    // of data to be used on the first audio callback. + +    CHECK(mFirstBuffer == NULL); + +    MediaSource::ReadOptions options; +    if (mSeeking) { +        options.setSeekTo(mSeekTimeUs); +        mSeeking = false; +    } + +    mFirstBufferResult = mSource->read(&mFirstBuffer, &options); +    if (mFirstBufferResult == INFO_FORMAT_CHANGED) { +        ALOGV("INFO_FORMAT_CHANGED!!!"); +        CHECK(mFirstBuffer == NULL); +        mFirstBufferResult = OK; +        mIsFirstBuffer = false; +    } else { +        mIsFirstBuffer = true; +    } + +    sp<MetaData> format = mSource->getFormat(); +    const char *mime; +    bool success = format->findCString(kKeyMIMEType, &mime); +    if (!strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_AAC)) { +        mFormat = AUDIO_FORMAT_AAC; +    } +    if (!strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_AMR_WB)) { +        mFormat = AUDIO_FORMAT_AMR_WB; +        ALOGV("TunnelPlayer::start AUDIO_FORMAT_AMR_WB"); +    } +//    if (!strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_AMR_WB_PLUS)) { +//        mFormat = AUDIO_FORMAT_AMR_WB_PLUS; +//        ALOGV("TunnelPlayer::start AUDIO_FORMAT_AMR_WB_PLUS"); +//    } + +    CHECK(success); + +    success = format->findInt32(kKeySampleRate, &mSampleRate); +    CHECK(success); + +    success = format->findInt32(kKeyChannelCount, &numChannels); +    CHECK(success); + +    if(!format->findInt32(kKeyChannelMask, &mChannelMask)) { +        // log only when there's a risk of ambiguity of channel mask selection +        ALOGI_IF(numChannels > 2, +                "source format didn't specify channel mask, using (%d) channel order", numChannels); +        mChannelMask = CHANNEL_MASK_USE_CHANNEL_ORDER; +    } +    audio_output_flags_t flags = (audio_output_flags_t) (AUDIO_OUTPUT_FLAG_TUNNEL | +                                                         AUDIO_OUTPUT_FLAG_DIRECT); +    ALOGV("mAudiosink->open() mSampleRate %d, numChannels %d, mChannelMask %d, flags %d",mSampleRate, numChannels, mChannelMask, flags); +    err = mAudioSink->open( +        mSampleRate, numChannels, mChannelMask, mFormat, +        DEFAULT_AUDIOSINK_BUFFERCOUNT, +        &TunnelPlayer::AudioSinkCallback, +        this, +        (mA2DPEnabled ? AUDIO_OUTPUT_FLAG_NONE : flags)); + +    if (err != OK) { +        if (mFirstBuffer != NULL) { +            mFirstBuffer->release(); +            mFirstBuffer = NULL; +        } + +        if (!sourceAlreadyStarted) { +            mSource->stop(); +        } + +        ALOGE("Opening a routing session failed"); +        return err; +    } + +    if (!mIsA2DPEnabled) { +        acquireWakeLock(); +    } + +    mIsAudioRouted = true; +    mStarted = true; +    mAudioSink->start(); +    ALOGV("Waking up decoder thread"); +    pthread_cond_signal(&extractor_cv); + +    return OK; +} + +status_t TunnelPlayer::seekTo(int64_t time_us) { + +    ALOGV("seekTo: time_us %lld", time_us); +    if ( mReachedEOS ) { +        mReachedEOS = false; +        mReachedOutputEOS = false; +    } +    mSeeking = true; +    mSeekTimeUs = time_us; +    ALOGV("In seekTo(), mSeekTimeUs %lld",mSeekTimeUs); +    mAudioSink->flush(); +    pthread_cond_signal(&extractor_cv); +    //TODO: Update the mPauseTime +    return OK; +} +void TunnelPlayer::pause(bool playPendingSamples) { +    CHECK(mStarted); +    if (mPaused) { +        return; +    } +    ALOGV("pause: playPendingSamples %d", playPendingSamples); +    mPaused = true; +    A2DPState state; +    if(!mPauseEventPending) { +        ALOGV("Posting an event for Pause timeout"); +        mQueue.postEventWithDelay(mPauseEvent, TUNNEL_PAUSE_TIMEOUT_USEC); +        mPauseEventPending = true; +    } +    mPauseTime = mSeekTimeUs + getTimeStamp(A2DP_DISABLED); +    if (mAudioSink.get() != NULL) { +        ALOGV("AudioSink pause"); +        mAudioSink->pause(); +    } +} + +void TunnelPlayer::resume() { +    ALOGV("resume: mPaused %d",mPaused); +    if ( mPaused) { +        CHECK(mStarted); +        if (!mIsA2DPEnabled) { +            if(mPauseEventPending) { +                ALOGV("Resume(): Cancelling the puaseTimeout event"); +                mPauseEventPending = false; +                mQueue.cancelEvent(mPauseEvent->eventID()); +            } + +        } +        audio_format_t format; + +        if (!mIsAudioRouted) { +            audio_output_flags_t flags = (audio_output_flags_t) (AUDIO_OUTPUT_FLAG_TUNNEL | +                                                                AUDIO_OUTPUT_FLAG_DIRECT); +            status_t err = mAudioSink->open( +                mSampleRate, numChannels, mChannelMask, mFormat, +                DEFAULT_AUDIOSINK_BUFFERCOUNT, +                &TunnelPlayer::AudioSinkCallback, +                this, +                (mA2DPEnabled ?  AUDIO_OUTPUT_FLAG_NONE : flags )); +            if (err != NO_ERROR) { +                ALOGE("Audio sink open failed."); +            } +            mIsAudioRouted = true; +        } +        mPaused = false; +        mAudioSink->start(); +        pthread_cond_signal(&extractor_cv); +    } +} + +//static +size_t TunnelPlayer::AudioSinkCallback( +        MediaPlayerBase::AudioSink *audioSink, +        void *buffer, size_t size, void *cookie) { +    if (buffer == NULL && size == AudioTrack::EVENT_UNDERRUN) { +        TunnelPlayer *me = (TunnelPlayer *)cookie; +        me->mReachedEOS = true; +        me->mReachedOutputEOS = true; +        ALOGV("postAudioEOS"); +        me->mObserver->postAudioEOS(0); +    } +    return 1; +} + +void TunnelPlayer::reset() { + +    mReachedEOS = true; + +    // make sure Decoder thread has exited +    requestAndWaitForExtractorThreadExit(); + +    // Close the audiosink after all the threads exited to make sure +    mAudioSink->stop(); +    mAudioSink->close(); +    //TODO: Release Wake lock + +    // Make sure to release any buffer we hold onto so that the +    // source is able to stop(). +    if (mFirstBuffer != NULL) { +        mFirstBuffer->release(); +        mFirstBuffer = NULL; +    } + +    if (mInputBuffer != NULL) { +        ALOGV("AudioPlayer releasing input buffer."); +        mInputBuffer->release(); +        mInputBuffer = NULL; +    } + +    mSource->stop(); + +    // The following hack is necessary to ensure that the OMX +    // component is completely released by the time we may try +    // to instantiate it again. +    wp<MediaSource> tmp = mSource; +    mSource.clear(); +    while (tmp.promote() != NULL) { +        usleep(1000); +    } + +    mPositionTimeMediaUs = -1; +    mPositionTimeRealUs = -1; +    mSeeking = false; +    mReachedEOS = false; +    mReachedOutputEOS = false; +    mFinalStatus = OK; +    mStarted = false; +} + + +bool TunnelPlayer::isSeeking() { +    Mutex::Autolock autoLock(mLock); +    return mSeeking; +} + +bool TunnelPlayer::reachedEOS(status_t *finalStatus) { +    *finalStatus = OK; +    Mutex::Autolock autoLock(mLock); +    *finalStatus = mFinalStatus; +    return mReachedOutputEOS; +} + + +void *TunnelPlayer::extractorThreadWrapper(void *me) { +    static_cast<TunnelPlayer *>(me)->extractorThreadEntry(); +    return NULL; +} + + +void TunnelPlayer::extractorThreadEntry() { + +    pthread_mutex_lock(&extractor_mutex); +    uint32_t BufferSizeToUse = MEM_BUFFER_SIZE; + +    pid_t tid  = gettid(); +    androidSetThreadPriority(tid, ANDROID_PRIORITY_AUDIO); +    prctl(PR_SET_NAME, (unsigned long)"Tunnel DecodeThread", 0, 0, 0); + +    ALOGV("extractorThreadEntry wait for signal \n"); +    if (!mStarted) { +        pthread_cond_wait(&extractor_cv, &extractor_mutex); +    } +    ALOGV("extractorThreadEntry ready to work \n"); +    pthread_mutex_unlock(&extractor_mutex); +    if (killExtractorThread) { +        return; +    } +    if(mSource != NULL) { +        sp<MetaData> format = mSource->getFormat(); +        const char *mime; +        bool success = format->findCString(kKeyMIMEType, &mime); +    } +    void* local_buf = malloc(BufferSizeToUse); +    int bytesWritten = 0; +    while (!killExtractorThread) { + +        if (mReachedEOS || mPaused || !mIsAudioRouted) { +            pthread_mutex_lock(&extractor_mutex); +            pthread_cond_wait(&extractor_cv, &extractor_mutex); +            pthread_mutex_unlock(&extractor_mutex); +            continue; +        } + +        if (!mIsA2DPEnabled) { +            ALOGW("FillBuffer: MemBuffer size %d", BufferSizeToUse); +            ALOGV("Fillbuffer started"); +            bytesWritten = fillBuffer(local_buf, BufferSizeToUse); +            ALOGV("FillBuffer completed bytesToWrite %d", bytesWritten); +            if(!killExtractorThread) { +                mAudioSink->write(local_buf, bytesWritten); +                if(mReachedEOS && bytesWritten) +                    mAudioSink->write(local_buf, 0); +            } +        } +    } + +    free(local_buf); + +    //TODO: Call fillbuffer with different size and write to mAudioSink() +} +void TunnelPlayer::createThreads() { + +    //Initialize all the Mutexes and Condition Variables +    pthread_mutex_init(&extractor_mutex, NULL); +    pthread_cond_init (&extractor_cv, NULL); + +    // Create 4 threads Effect, decoder, event and A2dp +    pthread_attr_t attr; +    pthread_attr_init(&attr); +    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + +    killExtractorThread = false; + +    extractorThreadAlive = true; + +    ALOGV("Creating decoder Thread"); +    pthread_create(&extractorThread, &attr, extractorThreadWrapper, this); + +    pthread_attr_destroy(&attr); +} +size_t TunnelPlayer::fillBuffer(void *data, size_t size) { + +    if (mReachedEOS) { +        return 0; +    } + +    bool postSeekComplete = false; + +    size_t size_done = 0; +    size_t size_remaining = size; + +    while (size_remaining > 0) { +        MediaSource::ReadOptions options; +        { +            Mutex::Autolock autoLock(mLock); +            if(mSeeking) { +                mInternalSeeking = false; +            } + +            if (mSeeking || mInternalSeeking) { +                if (mIsFirstBuffer) { +                    if (mFirstBuffer != NULL) { +                        mFirstBuffer->release(); +                        mFirstBuffer = NULL; +                    } +                    mIsFirstBuffer = false; +                } + +                MediaSource::ReadOptions::SeekMode seekMode; +                seekMode = MediaSource::ReadOptions::SEEK_CLOSEST_SYNC; +                options.setSeekTo(mSeekTimeUs, seekMode ); +                if (mInputBuffer != NULL) { +                    mInputBuffer->release(); +                    mInputBuffer = NULL; +                } + +                // This is to ignore the data already filled in the output buffer +                size_done = 0; +                size_remaining = size; + +                mSeeking = false; +                if (mObserver && !mInternalSeeking) { +                    ALOGD("fillBuffer: Posting audio seek complete event"); +                    postSeekComplete = true; +                } +                mInternalSeeking = false; +            } +        } +        if (mInputBuffer == NULL) { +            status_t err; + +            if (mIsFirstBuffer) { +                mInputBuffer = mFirstBuffer; +                mFirstBuffer = NULL; +                err = mFirstBufferResult; + +                mIsFirstBuffer = false; +            } else { +                err = mSource->read(&mInputBuffer, &options); +            } + +            CHECK((err == OK && mInputBuffer != NULL) +                  || (err != OK && mInputBuffer == NULL)); +            { +                Mutex::Autolock autoLock(mLock); + +                if (err != OK) { +                    ALOGD("fill buffer - reached eos true"); +                    mReachedEOS = true; +                    mFinalStatus = err; +                    break; +                } +            } + +        } +        if (mInputBuffer->range_length() == 0) { +            mInputBuffer->release(); +            mInputBuffer = NULL; +            continue; +        } + +        size_t copy = size_remaining; +        if (copy > mInputBuffer->range_length()) { +            copy = mInputBuffer->range_length(); +        } +        memcpy((char *)data + size_done, +               (const char *)mInputBuffer->data() + mInputBuffer->range_offset(), +               copy); + +        mInputBuffer->set_range(mInputBuffer->range_offset() + copy, +                                mInputBuffer->range_length() - copy); + +        size_done += copy; +        size_remaining -= copy; +    } +    if(mReachedEOS) +        memset((char *)data + size_done, 0x0, size_remaining); +    ALOGV("fill buffer size_done = %d",size_done); + +    if (postSeekComplete) { +        mObserver->postAudioSeekComplete(); +    } + +    return size_done; +} + +int64_t TunnelPlayer::getRealTimeUs() { +    Mutex::Autolock autoLock(mLock); +    return getRealTimeUsLocked(); +} + + +int64_t TunnelPlayer::getRealTimeUsLocked(){ +    //Used for AV sync: irrelevant API for Tunnel. +    return 0; +} + +int64_t TunnelPlayer::getTimeStamp(A2DPState state) { +    uint64_t timestamp = 0; +    switch (state) { +    case A2DP_ENABLED: +    case A2DP_DISCONNECT: +        ALOGV("Get timestamp for A2DP"); +        break; +    case A2DP_DISABLED: +    case A2DP_CONNECT: { +        mAudioSink->getTimeStamp(×tamp); +        break; +    } +    default: +        break; +    } +    ALOGV("timestamp %lld ", timestamp); +    return timestamp; +} + +int64_t TunnelPlayer::getMediaTimeUs() { +    Mutex::Autolock autoLock(mLock); +    ALOGV("getMediaTimeUs() mPaused %d mSeekTimeUs %lld mPauseTime %lld", mPaused, mSeekTimeUs, mPauseTime); +    if (mPaused) { +        return mPauseTime; +    } else { +        A2DPState state = mIsA2DPEnabled ? A2DP_ENABLED : A2DP_DISABLED; +        return (mSeekTimeUs + getTimeStamp(state)); +    } +} + +bool TunnelPlayer::getMediaTimeMapping( +                                   int64_t *realtime_us, int64_t *mediatime_us) { +    Mutex::Autolock autoLock(mLock); + +    *realtime_us = mPositionTimeRealUs; +    *mediatime_us = mPositionTimeMediaUs; + +    return mPositionTimeRealUs != -1 && mPositionTimeMediaUs != -1; +} + +void TunnelPlayer::requestAndWaitForExtractorThreadExit() { + +    if (!extractorThreadAlive) +        return; +    mAudioSink->flush(); +    killExtractorThread = true; +    pthread_cond_signal(&extractor_cv); +    pthread_join(extractorThread,NULL); +    ALOGD("Extractor thread killed"); +} + +void TunnelPlayer::onPauseTimeOut() { +    ALOGV("onPauseTimeOut"); +    if (!mPauseEventPending) { +        return; +    } +    mPauseEventPending = false; +    if(!mIsA2DPEnabled) { +        // 1.) Set seek flags +        mReachedEOS = false; +        mReachedOutputEOS = false; +        mSeekTimeUs += getTimeStamp(A2DP_DISABLED); +        mInternalSeeking = true; + +        // 2.) Close routing Session +        mAudioSink->close(); +        mIsAudioRouted = false; + +        // 3.) Release Wake Lock +        releaseWakeLock(); +    } + +} + +} //namespace android diff --git a/media/libstagefright/WAVEWriter.cpp b/media/libstagefright/WAVEWriter.cpp new file mode 100644 index 0000000..9700fa7 --- /dev/null +++ b/media/libstagefright/WAVEWriter.cpp @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + *     * Redistributions of source code must retain the above copyright + *       notice, this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above + *       copyright notice, this list of conditions and the following + *       disclaimer in the documentation and/or other materials provided + *       with the distribution. + *     * Neither the name of The Linux Foundation nor the names of its + *       contributors may be used to endorse or promote products derived + *       from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "WAVEWriter" +#include <utils/Log.h> + +#include <media/stagefright/WAVEWriter.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> +#include <media/mediarecorder.h> +#include <sys/prctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +namespace android { + +static struct wav_header hdr; + + +WAVEWriter::WAVEWriter(const char *filename) +    : mFd(-1), +      mInitCheck(NO_INIT), +      mStarted(false), +      mPaused(false), +      mResumed(false) { + +    mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); +    if (mFd >= 0) { +        mInitCheck = OK; +    } +} + +WAVEWriter::WAVEWriter(int fd) +    : mFd(dup(fd)), +      mInitCheck(mFd < 0? NO_INIT: OK), +      mStarted(false), +      mPaused(false), +      mResumed(false) { +} + +WAVEWriter::~WAVEWriter() { +    if (mStarted) { +        stop(); +    } + +    if (mFd != -1) { +        close(mFd); +        mFd = -1; +    } +} + +status_t WAVEWriter::initCheck() const { +    return mInitCheck; +} + +status_t WAVEWriter::addSource(const sp<MediaSource> &source) { +    uint32_t count; +    if (mInitCheck != OK) { +        ALOGE("Init Check not OK, return"); +        return mInitCheck; +    } + +    if (mSource != NULL) { +        ALOGE("A source already exists, return"); +        return UNKNOWN_ERROR; +    } + +    sp<MetaData> meta = source->getFormat(); + +    const char *mime; +    CHECK(meta->findCString(kKeyMIMEType, &mime)); + +    int32_t channelCount; +    int32_t sampleRate; +    CHECK(meta->findInt32(kKeyChannelCount, &channelCount)); +    CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); + +    memset(&hdr, 0, sizeof(struct wav_header)); +    hdr.riff_id = ID_RIFF; +    hdr.riff_fmt = ID_WAVE; +    hdr.fmt_id = ID_FMT; +    hdr.fmt_sz = 16; +    hdr.audio_format = FORMAT_PCM; +    hdr.num_channels = channelCount; +    hdr.sample_rate = sampleRate; +    hdr.bits_per_sample = 16; +    hdr.byte_rate = (sampleRate * channelCount * hdr.bits_per_sample) / 8; +    hdr.block_align = ( hdr.bits_per_sample * channelCount ) / 8; +    hdr.data_id = ID_DATA; +    hdr.data_sz = 0; +    hdr.riff_sz = hdr.data_sz + 44 - 8; + +    if (write(mFd, &hdr, sizeof(hdr)) != sizeof(hdr)) { +        ALOGE("Write header error, return ERROR_IO"); +        return -ERROR_IO; +    } + +    mSource = source; + +    return OK; +} + +status_t WAVEWriter::start(MetaData *params) { +    if (mInitCheck != OK) { +        ALOGE("Init Check not OK, return"); +        return mInitCheck; +    } + +    if (mSource == NULL) { +        ALOGE("NULL Source"); +        return UNKNOWN_ERROR; +    } + +    if (mStarted && mPaused) { +        mPaused = false; +        mResumed = true; +        return OK; +    } else if (mStarted) { +        ALOGE("Already startd, return"); +        return OK; +    } + +    status_t err = mSource->start(); + +    if (err != OK) { +        return err; +    } + +    pthread_attr_t attr; +    pthread_attr_init(&attr); +    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + +    mReachedEOS = false; +    mDone = false; + +    pthread_create(&mThread, &attr, ThreadWrapper, this); +    pthread_attr_destroy(&attr); + +    mStarted = true; + +    return OK; +} + +status_t WAVEWriter::pause() { +    if (!mStarted) { +        return OK; +    } +    mPaused = true; +    return OK; +} + +status_t WAVEWriter::stop() { +    if (!mStarted) { +        return OK; +    } + +    mDone = true; + +    void *dummy; +    pthread_join(mThread, &dummy); + +    status_t err = (status_t) dummy; +    { +        status_t status = mSource->stop(); +        if (err == OK && +            (status != OK && status != ERROR_END_OF_STREAM)) { +            err = status; +        } +    } + +    mStarted = false; +    return err; +} + +bool WAVEWriter::exceedsFileSizeLimit() { +    if (mMaxFileSizeLimitBytes == 0) { +        return false; +    } +    return mEstimatedSizeBytes >= mMaxFileSizeLimitBytes; +} + +bool WAVEWriter::exceedsFileDurationLimit() { +    if (mMaxFileDurationLimitUs == 0) { +        return false; +    } +    return mEstimatedDurationUs >= mMaxFileDurationLimitUs; +} + +// static +void *WAVEWriter::ThreadWrapper(void *me) { +    return (void *) static_cast<WAVEWriter *>(me)->threadFunc(); +} + +status_t WAVEWriter::threadFunc() { +    mEstimatedDurationUs = 0; +    mEstimatedSizeBytes = 0; +    bool stoppedPrematurely = true; +    int64_t previousPausedDurationUs = 0; +    int64_t maxTimestampUs = 0; +    status_t err = OK; + +    prctl(PR_SET_NAME, (unsigned long)"WAVEWriter", 0, 0, 0); +    hdr.data_sz = 0; +    while (!mDone) { +        MediaBuffer *buffer; +        err = mSource->read(&buffer); + +        if (err != OK) { +            break; +        } + +        if (mPaused) { +            buffer->release(); +            buffer = NULL; +            continue; +        } + +        mEstimatedSizeBytes += buffer->range_length(); +        if (exceedsFileSizeLimit()) { +            buffer->release(); +            buffer = NULL; +            notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0); +            break; +        } + +        int64_t timestampUs; +        CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs)); +        if (timestampUs > mEstimatedDurationUs) { +            mEstimatedDurationUs = timestampUs; +        } +        if (mResumed) { +            previousPausedDurationUs += (timestampUs - maxTimestampUs - 20000); +            mResumed = false; +        } +        timestampUs -= previousPausedDurationUs; +        ALOGV("time stamp: %lld, previous paused duration: %lld", +                timestampUs, previousPausedDurationUs); +        if (timestampUs > maxTimestampUs) { +            maxTimestampUs = timestampUs; +        } + +        if (exceedsFileDurationLimit()) { +            buffer->release(); +            buffer = NULL; +            notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0); +            break; +        } +        ssize_t n = write(mFd, +                        (const uint8_t *)buffer->data() + buffer->range_offset(), +                        buffer->range_length()); + +        hdr.data_sz += (ssize_t)buffer->range_length(); +        hdr.riff_sz = hdr.data_sz + 44 - 8; + +        if (n < (ssize_t)buffer->range_length()) { +            buffer->release(); +            buffer = NULL; + +            break; +        } + +        if (stoppedPrematurely) { +            stoppedPrematurely = false; +        } + +        buffer->release(); +        buffer = NULL; +    } + +    if (stoppedPrematurely) { +        notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS, UNKNOWN_ERROR); +    } + +    lseek(mFd, 0, SEEK_SET); +    write(mFd, &hdr, sizeof(hdr)); +    lseek(mFd, 0, SEEK_END); + +    close(mFd); +    mFd = -1; +    mReachedEOS = true; +    if (err == ERROR_END_OF_STREAM) { +        return OK; +    } +    return err; +} + +bool WAVEWriter::reachedEOS() { +    return mReachedEOS; +} + +}  // namespace android diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 1422687..107c5da 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -200,6 +200,9 @@ private:      bool mWatchForAudioSeekComplete;      bool mWatchForAudioEOS; +#ifdef QCOM_ENHANCED_AUDIO +    static int mTunnelAliveAP; +#endif      sp<TimedEventQueue::Event> mVideoEvent;      bool mVideoEventPending; @@ -339,6 +342,11 @@ private:      size_t countTracks() const; +#ifdef QCOM_ENHANCED_AUDIO +    //Flag to check if tunnel mode audio is enabled +    bool mIsTunnelAudio; +#endif +      AwesomePlayer(const AwesomePlayer &);      AwesomePlayer &operator=(const AwesomePlayer &);  }; diff --git a/media/libstagefright/include/ExtendedExtractor.h b/media/libstagefright/include/ExtendedExtractor.h new file mode 100644 index 0000000..e7d8704 --- /dev/null +++ b/media/libstagefright/include/ExtendedExtractor.h @@ -0,0 +1,58 @@ +/*Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + *     * Redistributions of source code must retain the above copyright + *      notice, this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above + *       copyright notice, this list of conditions and the following + *       disclaimer in the documentation and/or other materials provided + *      with the distribution. + *     * Neither the name of The Linux Foundation nor the names of its + *      contributors may be used to endorse or promote products derived + *       from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EXTENDED_EXTRACTOR_ +#define EXTENDED_EXTRACTOR_ + +#include <media/stagefright/DataSource.h> + +namespace android { + +class MediaExtractor; + +typedef MediaExtractor* (*MediaExtractorFactory)(const sp<DataSource> &source, const char* mime); + +static const char* MEDIA_CREATE_EXTRACTOR = "CreateExtractor"; + +typedef bool (*ExtendedExtractorSniffers)(const sp<DataSource> &source, String8 *mimeType, +                                          float *confidence,sp<AMessage> *meta); + +static const char* EXTENDED_EXTRACTOR_SNIFFERS = "SniffExtendedExtractor"; + +class ExtendedExtractor +{ +public: +    static MediaExtractor* CreateExtractor(const sp<DataSource> &source, const char *mime); +}; + +bool SniffExtendedExtractor(const sp<DataSource> &source, String8 *mimeType, +                               float *confidence,sp<AMessage> *meta); + +}  // namespace android + +#endif //EXTENDED_EXTRACTOR_ diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 1ce47a3..c7fbb09 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1,6 +1,11 @@  /*  **  ** Copyright 2007, The Android Open Source Project +** Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. +** +** Copyright (c) 2012, The Linux Foundation. All rights reserved. +** Not a Contribution, Apache license notifications and license are retained +** for attribution purposes only.  **  ** Licensed under the Apache License, Version 2.0 (the "License");  ** you may not use this file except in compliance with the License. @@ -100,6 +105,11 @@  #define ALOGVV(a...) do { } while(0)  #endif +#ifdef QCOM_HARDWARE +#define DIRECT_TRACK_EOS 1 +static const char lockName[] = "DirectTrack"; +#endif +  namespace android {  static const char kDeadlockedString[] = "AudioFlinger may be deadlocked\n"; @@ -220,6 +230,14 @@ out:      return rc;  } +static uint32_t getInputChannelCount(uint32_t channels) { +#ifdef QCOM_HARDWARE +   // only mono or stereo and 5.1 are supported for input sources +   return popcount((channels) & (AUDIO_CHANNEL_IN_STEREO | AUDIO_CHANNEL_IN_MONO | AUDIO_CHANNEL_IN_5POINT1)); +#else +   return popcount(channels); +#endif +}  // ----------------------------------------------------------------------------  AudioFlinger::AudioFlinger() @@ -241,6 +259,11 @@ void AudioFlinger::onFirstRef()      Mutex::Autolock _l(mLock);      /* TODO: move all this work into an Init() function */ +#ifdef QCOM_HARDWARE +    mLPASessionId = -2; // -2 is invalid session ID +    mIsEffectConfigChanged = false; +    mLPAEffectChain = NULL; +#endif      char val_str[PROPERTY_VALUE_MAX] = { 0 };      if (property_get("ro.audio.flinger_standbytime_ms", val_str, NULL) >= 0) {          uint32_t int_val; @@ -546,9 +569,218 @@ Exit:      return trackHandle;  } +#ifdef QCOM_HARDWARE +sp<IDirectTrack> AudioFlinger::createDirectTrack( +        pid_t pid, +        uint32_t sampleRate, +        audio_channel_mask_t channelMask, +        audio_io_handle_t output, +        int *sessionId, +        IDirectTrackClient *client, +        audio_stream_type_t streamType, +        status_t *status) +{ +    *status = NO_ERROR; +    status_t lStatus = NO_ERROR; +    sp<IDirectTrack> track = NULL; +    DirectAudioTrack* directTrack = NULL; +    Mutex::Autolock _l(mLock); + +    ALOGV("createDirectTrack() sessionId: %d sampleRate %d channelMask %d", +         *sessionId, sampleRate, channelMask); +    AudioSessionDescriptor *desc = mDirectAudioTracks.valueFor(output); +    if(desc == NULL) { +        ALOGE("Error: Invalid output (%d) to create direct audio track", output); +        lStatus = BAD_VALUE; +        goto Exit; +    } +    desc->mStreamType = streamType; +    if (desc->flag & AUDIO_OUTPUT_FLAG_LPA) { +        if (sessionId != NULL && *sessionId != AUDIO_SESSION_OUTPUT_MIX) { +            for (size_t i = 0; i < mPlaybackThreads.size(); i++) { +                sp<PlaybackThread> t = mPlaybackThreads.valueAt(i); +                // Check if the session ID is already associated with a track +                uint32_t sessions = t->hasAudioSession(*sessionId); + +                // check if an effect with same session ID is waiting for a ssession to be created +                ALOGV("check if an effect with same session ID is waiting for a ssession to be created"); +                if ((mLPAEffectChain == NULL) && (sessions & PlaybackThread::EFFECT_SESSION)) { +                    // Clear reference to previous effect chain if any +                    t->mLock.lock(); +                    ALOGV("getting the LPA effect chain and setting LPA flag to true."); +                    mLPAEffectChain = t->getEffectChain_l(*sessionId); +                    t->mLock.unlock(); +                } +            } +            mLPASessionId = *sessionId; +            if (mLPAEffectChain != NULL) { +                mLPAEffectChain->setLPAFlag(true); +                // For LPA, the volume will be applied in DSP. No need for volume +                // control in the Effect chain, so setting it to unity. +                uint32_t volume = 0x1000000; // Equals to 1.0 in 8.24 format +                mLPAEffectChain->setVolume_l(&volume,&volume); +            } else { +                ALOGW("There was no effectChain created for the sessionId(%d)", mLPASessionId); +            } +            mLPASampleRate  = sampleRate; +            mLPANumChannels = popcount(channelMask); +        } else { +            if(sessionId != NULL) { +                ALOGE("Error: Invalid sessionID (%d) for direct audio track", *sessionId); +            } +        } +    } +    mLock.unlock(); +    directTrack = new DirectAudioTrack(this, output, desc, client, desc->flag); +    desc->trackRefPtr = dynamic_cast<void *>(directTrack); +    mLock.lock(); +    if (directTrack != 0) { +        track = dynamic_cast<IDirectTrack *>(directTrack); +        AudioEventObserver* obv = dynamic_cast<AudioEventObserver *>(directTrack); +        ALOGE("setting observer mOutputDesc track %p, obv %p", track.get(), obv); +        desc->stream->set_observer(desc->stream, reinterpret_cast<void *>(obv)); +    } else { +        lStatus = BAD_VALUE; +    } +Exit: +    if(lStatus) { +        if (track != NULL) { +            track.clear(); +        } +        *status = lStatus; +    } +    return track; +} + +void AudioFlinger::deleteEffectSession() +{ +    Mutex::Autolock _l(mLock); +    ALOGV("deleteSession"); +    // -2 is invalid session ID +    mLPASessionId = -2; +    if (mLPAEffectChain != NULL) { +        mLPAEffectChain->setLPAFlag(false); +        size_t i, numEffects = mLPAEffectChain->getNumEffects(); +        for(i = 0; i < numEffects; i++) { +            sp<EffectModule> effect = mLPAEffectChain->getEffectFromIndex_l(i); +            effect->setInBuffer(mLPAEffectChain->inBuffer()); +            if (i == numEffects-1) { +                effect->setOutBuffer(mLPAEffectChain->outBuffer()); +            } else { +                effect->setOutBuffer(mLPAEffectChain->inBuffer()); +            } +            effect->configure(); +        } +        mLPAEffectChain.clear(); +        mLPAEffectChain = NULL; +    } +} + +// ToDo: Should we go ahead with this frameCount? +#define DEAFULT_FRAME_COUNT 1200 +void AudioFlinger::applyEffectsOn(void *token, int16_t *inBuffer, int16_t *outBuffer, int size) +{ +    ALOGV("applyEffectsOn: inBuf %p outBuf %p size %d token %p", inBuffer, outBuffer, size, token); +    // This might be the first buffer to apply effects after effect config change +    // should not skip effects processing +    mIsEffectConfigChanged = false; + +    volatile size_t numEffects = 0; +    if(mLPAEffectChain != NULL) { +        numEffects = mLPAEffectChain->getNumEffects(); +    } + +    if( numEffects > 0) { +        size_t  i     = 0; +        int16_t *pIn  = inBuffer; +        int16_t *pOut = outBuffer; + +        int frameCount = size / (sizeof(int16_t) * mLPANumChannels); + +        while(frameCount > 0) { +            if(mLPAEffectChain == NULL) { +                ALOGV("LPA Effect Chain is removed - No effects processing !!"); +                numEffects = 0; +                break; +            } +            mLPAEffectChain->lock(); + +            numEffects = mLPAEffectChain->getNumEffects(); +            if(!numEffects) { +                ALOGV("applyEffectsOn: All the effects are removed - nothing to process"); +                mLPAEffectChain->unlock(); +                break; +            } + +            int outFrameCount = (frameCount > DEAFULT_FRAME_COUNT ? DEAFULT_FRAME_COUNT: frameCount); +            bool isEffectEnabled = false; +            for(i = 0; i < numEffects; i++) { +                // If effect configuration is changed while applying effects do not process further +                if(mIsEffectConfigChanged) { +                    mLPAEffectChain->unlock(); +                    ALOGV("applyEffectsOn: mIsEffectConfigChanged is set - no further processing"); +                    return; +                } +                sp<EffectModule> effect = mLPAEffectChain->getEffectFromIndex_l(i); +                if(effect == NULL) { +                    ALOGE("getEffectFromIndex_l(%d) returned NULL ptr", i); +                    mLPAEffectChain->unlock(); +                    return; +                } +                if(i == 0) { +                    // For the first set input and output buffers different +                    isEffectEnabled = effect->isProcessEnabled(); +                    effect->setInBuffer(pIn); +                    effect->setOutBuffer(pOut); +                } else { +                    // For the remaining use previous effect's output buffer as input buffer +                    effect->setInBuffer(pOut); +                    effect->setOutBuffer(pOut); +                } +                // true indicates that it is being applied on LPA output +                effect->configure(true, mLPASampleRate, mLPANumChannels, outFrameCount); +            } + +            if(isEffectEnabled) { +                // Clear the output buffer +                memset(pOut, 0, (outFrameCount * mLPANumChannels * sizeof(int16_t))); +            } else { +                // Copy input buffer content to the output buffer +                memcpy(pOut, pIn, (outFrameCount * mLPANumChannels * sizeof(int16_t))); +            } + +            mLPAEffectChain->process_l(); + +            mLPAEffectChain->unlock(); + +            // Update input and output buffer pointers +            pIn        += (outFrameCount * mLPANumChannels); +            pOut       += (outFrameCount * mLPANumChannels); +            frameCount -= outFrameCount; +        } +    } + +    if (!numEffects) { +        ALOGV("applyEffectsOn: There are no effects to be applied"); +        if(inBuffer != outBuffer) { +            // No effect applied so just copy input buffer to output buffer +            memcpy(outBuffer, inBuffer, size); +        } +    } +} +#endif +  uint32_t AudioFlinger::sampleRate(audio_io_handle_t output) const  {      Mutex::Autolock _l(mLock); +#ifdef QCOM_HARDWARE +    if (!mDirectAudioTracks.isEmpty()) { +        AudioSessionDescriptor *desc = mDirectAudioTracks.valueFor(output); +        if(desc != NULL) { +            return desc->stream->common.get_sample_rate(&desc->stream->common); +        } +    } +#endif      PlaybackThread *thread = checkPlaybackThread_l(output);      if (thread == NULL) {          ALOGW("sampleRate() unknown thread %d", output); @@ -560,6 +792,12 @@ uint32_t AudioFlinger::sampleRate(audio_io_handle_t output) const  int AudioFlinger::channelCount(audio_io_handle_t output) const  {      Mutex::Autolock _l(mLock); +#ifdef QCOM_HARDWARE +    AudioSessionDescriptor *desc = mDirectAudioTracks.valueFor(output); +    if(desc != NULL) { +        return desc->stream->common.get_channels(&desc->stream->common); +    } +#endif      PlaybackThread *thread = checkPlaybackThread_l(output);      if (thread == NULL) {          ALOGW("channelCount() unknown thread %d", output); @@ -582,6 +820,12 @@ audio_format_t AudioFlinger::format(audio_io_handle_t output) const  size_t AudioFlinger::frameCount(audio_io_handle_t output) const  {      Mutex::Autolock _l(mLock); +#ifdef QCOM_HARDWARE +    AudioSessionDescriptor *desc = mDirectAudioTracks.valueFor(output); +    if(desc != NULL) { +        return desc->stream->common.get_buffer_size(&desc->stream->common); +    } +#endif      PlaybackThread *thread = checkPlaybackThread_l(output);      if (thread == NULL) {          ALOGW("frameCount() unknown thread %d", output); @@ -785,12 +1029,35 @@ status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,      }      AutoMutex lock(mLock); +#ifdef QCOM_HARDWARE +    ALOGV("setStreamVolume stream %d, output %d, value %f",stream, output, value); +    AudioSessionDescriptor *desc = NULL; +    if (!mDirectAudioTracks.isEmpty()) { +        desc = mDirectAudioTracks.valueFor(output); +        if (desc != NULL) { +            ALOGV("setStreamVolume for mAudioTracks size %d desc %p",mDirectAudioTracks.size(),desc); +            if (desc->mStreamType == stream) { +                mStreamTypes[stream].volume = value; +                desc->stream->set_volume(desc->stream, +                                         desc->mVolumeLeft * mStreamTypes[stream].volume, +                                         desc->mVolumeRight* mStreamTypes[stream].volume); +                return NO_ERROR; +            } +        } +    } +#endif      PlaybackThread *thread = NULL;      if (output) {          thread = checkPlaybackThread_l(output);          if (thread == NULL) { +#ifdef QCOM_HARDWARE +            if (desc != NULL) { +                return NO_ERROR; +            } +#endif              return BAD_VALUE;          } +      }      mStreamTypes[stream].volume = value; @@ -917,6 +1184,18 @@ status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8&          return final_result;      } +#ifdef QCOM_HARDWARE +    AudioSessionDescriptor *desc = NULL; +    if (!mDirectAudioTracks.isEmpty()) { +        desc = mDirectAudioTracks.valueFor(ioHandle); +        if (desc != NULL) { +            ALOGV("setParameters for mAudioTracks size %d desc %p",mDirectAudioTracks.size(),desc); +            desc->stream->common.set_parameters(&desc->stream->common, keyValuePairs.string()); +            return NO_ERROR; +        } +    } +#endif +      // hold a strong ref on thread in case closeOutput() or closeInput() is called      // and the thread is exited once the lock is released      sp<ThreadBase> thread; @@ -929,13 +1208,17 @@ status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8&              // indicate output device change to all input threads for pre processing              AudioParameter param = AudioParameter(keyValuePairs);              int value; +            DefaultKeyedVector< int, sp<RecordThread> >    recordThreads = mRecordThreads; +            mLock.unlock();              if ((param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) &&                      (value != 0)) { -                for (size_t i = 0; i < mRecordThreads.size(); i++) { -                    mRecordThreads.valueAt(i)->setParameters(keyValuePairs); +                for (size_t i = 0; i < recordThreads.size(); i++) { +                    recordThreads.valueAt(i)->setParameters(keyValuePairs);                  }              } +            mLock.lock();          } +        mLock.unlock();      }      if (thread != 0) {          return thread->setParameters(keyValuePairs); @@ -1056,14 +1339,14 @@ void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client)      Mutex::Autolock _l(mLock); -    pid_t pid = IPCThreadState::self()->getCallingPid(); -    if (mNotificationClients.indexOfKey(pid) < 0) { +    sp<IBinder> binder = client->asBinder(); +    if (mNotificationClients.indexOfKey(binder) < 0) {          sp<NotificationClient> notificationClient = new NotificationClient(this,                                                                              client, -                                                                            pid); -        ALOGV("registerClient() client %p, pid %d", notificationClient.get(), pid); +                                                                            binder); +        ALOGV("registerClient() client %p, binder %d", notificationClient.get(), binder.get); -        mNotificationClients.add(pid, notificationClient); +        mNotificationClients.add(binder, notificationClient);          sp<IBinder> binder = client->asBinder();          binder->linkToDeath(notificationClient); @@ -1080,12 +1363,30 @@ void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client)      }  } -void AudioFlinger::removeNotificationClient(pid_t pid) +#ifdef QCOM_HARDWARE +status_t AudioFlinger::deregisterClient(const sp<IAudioFlingerClient>& client)  { +    ALOGV("deregisterClient() %p, tid %d, calling tid %d", client.get(), gettid(), IPCThreadState::self()->getCallingPid());      Mutex::Autolock _l(mLock); -    mNotificationClients.removeItem(pid); +    sp<IBinder> binder = client->asBinder(); +    int index = mNotificationClients.indexOfKey(binder); +    if (index >= 0) { +        mNotificationClients.removeItemsAt(index); +        return true; +    } +    return false; +} +#endif + +void AudioFlinger::removeNotificationClient(sp<IBinder> binder) +{ +    Mutex::Autolock _l(mLock); + +    mNotificationClients.removeItem(binder); + +    int pid = IPCThreadState::self()->getCallingPid();      ALOGV("%d died, releasing its sessions", pid);      size_t num = mAudioSessionRefs.size();      bool removed = false; @@ -1110,6 +1411,12 @@ void AudioFlinger::removeNotificationClient(pid_t pid)  // audioConfigChanged_l() must be called with AudioFlinger::mLock held  void AudioFlinger::audioConfigChanged_l(int event, audio_io_handle_t ioHandle, const void *param2)  { +#ifdef QCOM_HARDWARE +    ALOGV("AudioFlinger::audioConfigChanged_l: event %d", event); +    if (event == AudioSystem::EFFECT_CONFIG_CHANGED) { +        mIsEffectConfigChanged = true; +    } +#endif      size_t size = mNotificationClients.size();      for (size_t i = 0; i < size; i++) {          mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioHandle, @@ -1212,6 +1519,15 @@ status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs)      return status;  } +#ifdef QCOM_HARDWARE +void AudioFlinger::ThreadBase::effectConfigChanged() { +    mAudioFlinger->mLock.lock(); +    ALOGV("New effect is being added to LPA chain, Notifying LPA Direct Track"); +    mAudioFlinger->audioConfigChanged_l(AudioSystem::EFFECT_CONFIG_CHANGED, 0, NULL); +    mAudioFlinger->mLock.unlock(); +} +#endif +  void AudioFlinger::ThreadBase::sendIoConfigEvent(int event, int param)  {      Mutex::Autolock _l(mLock); @@ -2660,7 +2976,10 @@ bool AudioFlinger::PlaybackThread::threadLoop()          // only process effects if we're going to write          if (sleepTime == 0) {              for (size_t i = 0; i < effectChains.size(); i ++) { -                effectChains[i]->process_l(); +#ifdef QCOM_HARDWARE +                if (effectChains[i] != mAudioFlinger->mLPAEffectChain) +#endif +                    effectChains[i]->process_l();              }          } @@ -5785,8 +6104,8 @@ void AudioFlinger::Client::releaseTimedTrack()  AudioFlinger::NotificationClient::NotificationClient(const sp<AudioFlinger>& audioFlinger,                                                       const sp<IAudioFlingerClient>& client, -                                                     pid_t pid) -    : mAudioFlinger(audioFlinger), mPid(pid), mAudioFlingerClient(client) +                                                     sp<IBinder> binder) +    : mAudioFlinger(audioFlinger), mBinder(binder), mAudioFlingerClient(client)  {  } @@ -5797,9 +6116,309 @@ AudioFlinger::NotificationClient::~NotificationClient()  void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who)  {      sp<NotificationClient> keep(this); -    mAudioFlinger->removeNotificationClient(mPid); +    mAudioFlinger->removeNotificationClient(mBinder); +} + +// ---------------------------------------------------------------------------- +#ifdef QCOM_HARDWARE +AudioFlinger::DirectAudioTrack::DirectAudioTrack(const sp<AudioFlinger>& audioFlinger, +                                                 int output, AudioSessionDescriptor *outputDesc, +                                                 IDirectTrackClient* client, audio_output_flags_t outflag) +    : BnDirectTrack(), mIsPaused(false), mAudioFlinger(audioFlinger), mOutput(output), mOutputDesc(outputDesc), +      mClient(client), mEffectConfigChanged(false), mKillEffectsThread(false), mFlag(outflag) +{ +    if (mFlag & AUDIO_OUTPUT_FLAG_LPA) { +        createEffectThread(); + +        mAudioFlingerClient = new AudioFlingerDirectTrackClient(this); +        mAudioFlinger->registerClient(mAudioFlingerClient); + +        allocateBufPool(); +    } +    mDeathRecipient = new PMDeathRecipient(this); +    acquireWakeLock(); +} + +AudioFlinger::DirectAudioTrack::~DirectAudioTrack() { +    if (mFlag & AUDIO_OUTPUT_FLAG_LPA) { +        requestAndWaitForEffectsThreadExit(); +        mAudioFlinger->deregisterClient(mAudioFlingerClient); +        mAudioFlinger->deleteEffectSession(); +        deallocateBufPool(); +    } +    releaseWakeLock(); +    if (mPowerManager != 0) { +        sp<IBinder> binder = mPowerManager->asBinder(); +        binder->unlinkToDeath(mDeathRecipient); +    } +    AudioSystem::releaseOutput(mOutput); +} + +status_t AudioFlinger::DirectAudioTrack::start() { +    if(mIsPaused) { +        mIsPaused = false; +        mOutputDesc->stream->start(mOutputDesc->stream); +    } +    mOutputDesc->mActive = true; +    AudioSystem::startOutput(mOutput, (audio_stream_type_t)mOutputDesc->mStreamType); +    return NO_ERROR; +} + +void AudioFlinger::DirectAudioTrack::stop() { +    mOutputDesc->mActive = false; +    mOutputDesc->stream->stop(mOutputDesc->stream); +    AudioSystem::stopOutput(mOutput, (audio_stream_type_t)mOutputDesc->mStreamType); +} + +void AudioFlinger::DirectAudioTrack::pause() { +    if(!mIsPaused) { +        mIsPaused = true; +        mOutputDesc->stream->pause(mOutputDesc->stream); +        mOutputDesc->mActive = false; +        AudioSystem::stopOutput(mOutput, (audio_stream_type_t)mOutputDesc->mStreamType); +    } +} + +ssize_t AudioFlinger::DirectAudioTrack::write(const void *buffer, size_t size) { +    ALOGV("Writing to AudioSessionOut"); +    int isAvail = 0; +    mOutputDesc->stream->is_buffer_available(mOutputDesc->stream, &isAvail); +    if (!isAvail) { +        return 0; +    } + +    if (mFlag & AUDIO_OUTPUT_FLAG_LPA) { +        mEffectLock.lock(); +        List<BufferInfo>::iterator it = mEffectsPool.begin(); +        BufferInfo buf = *it; +        mEffectsPool.erase(it); +        memcpy((char *) buf.localBuf, (char *)buffer, size); +        buf.bytesToWrite = size; +        mEffectsPool.push_back(buf); +        mAudioFlinger->applyEffectsOn(static_cast<void *>(this), (int16_t*)buf.localBuf,(int16_t*)buffer,(int)size); +        mEffectLock.unlock(); +    } +    return mOutputDesc->stream->write(mOutputDesc->stream, buffer, size); +} + +void AudioFlinger::DirectAudioTrack::flush() { +    if (mFlag & AUDIO_OUTPUT_FLAG_LPA) { +        mEffectsPool.clear(); +        mEffectsPool = mBufPool; +    } +    mOutputDesc->stream->flush(mOutputDesc->stream); +} + +void AudioFlinger::DirectAudioTrack::mute(bool muted) { +} + +void AudioFlinger::DirectAudioTrack::setVolume(float left, float right) { +    mOutputDesc->mVolumeLeft = 1.0; +    mOutputDesc->mVolumeRight = 1.0; +} + +int64_t AudioFlinger::DirectAudioTrack::getTimeStamp() { +    int64_t time; +    mOutputDesc->stream->get_next_write_timestamp(mOutputDesc->stream, &time); +    ALOGV("Timestamp %lld",time); +    return time; +} + +void AudioFlinger::DirectAudioTrack::postEOS(int64_t delayUs) { +    ALOGV("Notify Audio Track of EOS event"); +    mClient->notify(DIRECT_TRACK_EOS); +} + +void AudioFlinger::DirectAudioTrack::allocateBufPool() { +    void *dsp_buf = NULL; +    void *local_buf = NULL; + +    //1. get the ion buffer information +    struct buf_info* buf = NULL; +    mOutputDesc->stream->get_buffer_info(mOutputDesc->stream, &buf); +    ALOGV("get buffer info %p",buf); +    if (!buf) { +        ALOGV("buffer is NULL"); +        return; +    } +    int nSize = buf->bufsize; +    int bufferCount = buf->nBufs; + +    //2. allocate the buffer pool, allocate local buffers +    for (int i = 0; i < bufferCount; i++) { +        dsp_buf = (void *)buf->buffers[i]; +        local_buf = malloc(nSize); +        memset(local_buf, 0, nSize); +        // Store this information for internal mapping / maintanence +        BufferInfo buf(local_buf, dsp_buf, nSize); +        buf.bytesToWrite = 0; +        mBufPool.push_back(buf); +        mEffectsPool.push_back(buf); + +        ALOGV("The MEM that is allocated buffer is %x, size %d",(unsigned int)dsp_buf,nSize); +    } +    free(buf); +} + +void AudioFlinger::DirectAudioTrack::deallocateBufPool() { + +    //1. Deallocate the local memory +    //2. Remove all the buffers from bufpool +    while (!mBufPool.empty())  { +        List<BufferInfo>::iterator it = mBufPool.begin(); +        BufferInfo &memBuffer = *it; +        // free the local buffer corresponding to mem buffer +        if (memBuffer.localBuf) { +            free(memBuffer.localBuf); +            memBuffer.localBuf = NULL; +        } +        ALOGV("Removing from bufpool"); +        mBufPool.erase(it); +    } +} + +status_t AudioFlinger::DirectAudioTrack::onTransact( +    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ +    return BnDirectTrack::onTransact(code, data, reply, flags); +} + +void *AudioFlinger::DirectAudioTrack::EffectsThreadWrapper(void *me) { +    static_cast<DirectAudioTrack *>(me)->EffectsThreadEntry(); +    return NULL; +} + +void AudioFlinger::DirectAudioTrack::EffectsThreadEntry() { +    while(1) { +        mEffectLock.lock(); +        if (!mEffectConfigChanged && !mKillEffectsThread) { +            mEffectCv.wait(mEffectLock); +        } + +        if(mKillEffectsThread) { +            mEffectLock.unlock(); +            break; +        } + +        if (mEffectConfigChanged) { +            mEffectConfigChanged = false; +            for ( List<BufferInfo>::iterator it = mEffectsPool.begin(); +                  it != mEffectsPool.end(); it++) { +                ALOGV("Apply effects on the buffer dspbuf %p, mEffectsPool.size() %d",it->dspBuf,mEffectsPool.size()); +                mAudioFlinger->applyEffectsOn(static_cast<void *>(this), +                                              (int16_t *)it->localBuf, +                                              (int16_t *)it->dspBuf, +                                              it->bytesToWrite); +                if (mEffectConfigChanged) { +                    break; +                } +            } + +        } +        mEffectLock.unlock(); +    } +    ALOGV("Effects thread is dead"); +    mEffectsThreadAlive = false; +} + +void AudioFlinger::DirectAudioTrack::requestAndWaitForEffectsThreadExit() { +    if (!mEffectsThreadAlive) +        return; +    mKillEffectsThread = true; +    mEffectCv.signal(); +    pthread_join(mEffectsThread,NULL); +    ALOGV("effects thread killed"); +} + +void AudioFlinger::DirectAudioTrack::createEffectThread() { +    //Create the effects thread +    pthread_attr_t attr; +    pthread_attr_init(&attr); +    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); +    mEffectsThreadAlive = true; +    ALOGV("Creating Effects Thread"); +    pthread_create(&mEffectsThread, &attr, EffectsThreadWrapper, this); +} +AudioFlinger::DirectAudioTrack::AudioFlingerDirectTrackClient::AudioFlingerDirectTrackClient(void *obj) +{ +    ALOGV("AudioFlinger::DirectAudioTrack::AudioFlingerDirectTrackClient"); +    pBaseClass = (DirectAudioTrack*)obj; +} + +void AudioFlinger::DirectAudioTrack::AudioFlingerDirectTrackClient::binderDied(const wp<IBinder>& who) { +    pBaseClass->mAudioFlinger.clear(); +    ALOGW("AudioFlinger server died!"); +} + +void AudioFlinger::DirectAudioTrack::AudioFlingerDirectTrackClient +     ::ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2) { +    ALOGV("ioConfigChanged() event %d", event); +    if (event == AudioSystem::EFFECT_CONFIG_CHANGED) { +        ALOGV("Received notification for change in effect module"); +        // Seek to current media time - flush the decoded buffers with the driver +        pBaseClass->mEffectConfigChanged = true; +        // Signal effects thread to re-apply effects +        ALOGV("Signalling Effects Thread"); +        pBaseClass->mEffectCv.signal(); + +    } +    ALOGV("ioConfigChanged Out"); +} + +void AudioFlinger::DirectAudioTrack::acquireWakeLock() +{ +    Mutex::Autolock _l(pmLock); + +    if (mPowerManager == 0) { +        // use checkService() to avoid blocking if power service is not up yet +        sp<IBinder> binder = +            defaultServiceManager()->checkService(String16("power")); +        if (binder == 0) { +            ALOGW("Thread %s cannot connect to the power manager service", lockName); +        } else { +            mPowerManager = interface_cast<IPowerManager>(binder); +            binder->linkToDeath(mDeathRecipient); +        } +    } +    if (mPowerManager != 0 && mWakeLockToken == 0) { +        sp<IBinder> binder = new BBinder(); +        status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK, +                                                         binder, +                                                         String16(lockName)); +        if (status == NO_ERROR) { +            mWakeLockToken = binder; +        } +        ALOGV("acquireWakeLock() status %d", status); +    }  } +void AudioFlinger::DirectAudioTrack::releaseWakeLock() +{ +   Mutex::Autolock _l(pmLock); + +    if (mWakeLockToken != 0) { +        ALOGV("releaseWakeLock()"); +        if (mPowerManager != 0) { +            mPowerManager->releaseWakeLock(mWakeLockToken, 0); +        } +        mWakeLockToken.clear(); +    } +} + +void AudioFlinger::DirectAudioTrack::clearPowerManager() +{ +    Mutex::Autolock _l(pmLock); +    releaseWakeLock(); +    mPowerManager.clear(); +} + +void AudioFlinger::DirectAudioTrack::PMDeathRecipient::binderDied(const wp<IBinder>& who) +{ +    parentClass->clearPowerManager(); +    ALOGW("power manager service died !!!"); +} +#endif +  // ----------------------------------------------------------------------------  AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::PlaybackThread::Track>& track) @@ -6002,7 +6621,7 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,      ThreadBase(audioFlinger, id, AUDIO_DEVICE_NONE, device, RECORD),      mInput(input), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),      // mRsmpInIndex and mInputBytes set by readInputParameters() -    mReqChannelCount(popcount(channelMask)), +    mReqChannelCount(getInputChannelCount(channelMask)),      mReqSampleRate(sampleRate)      // mBytesRead is only meaningful while active, and so is cleared in start()      // (but might be better to also clear here for dump?) @@ -6617,7 +7236,7 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()              reconfig = true;          }          if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) { -            reqChannelCount = popcount(value); +            reqChannelCount = getInputChannelCount(value);              reconfig = true;          }          if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { @@ -6678,7 +7297,7 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()                      reqFormat == mInput->stream->common.get_format(&mInput->stream->common) &&                      reqFormat == AUDIO_FORMAT_PCM_16_BIT &&                      ((int)mInput->stream->common.get_sample_rate(&mInput->stream->common) <= (2 * reqSamplingRate)) && -                    popcount(mInput->stream->common.get_channels(&mInput->stream->common)) <= FCC_2 && +                    getInputChannelCount(mInput->stream->common.get_channels(&mInput->stream->common)) <= FCC_2 &&                      (reqChannelCount <= FCC_2)) {                      status = NO_ERROR;                  } @@ -6749,7 +7368,7 @@ void AudioFlinger::RecordThread::readInputParameters()      mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);      mChannelMask = mInput->stream->common.get_channels(&mInput->stream->common); -    mChannelCount = (uint16_t)popcount(mChannelMask); +    mChannelCount = (uint16_t)getInputChannelCount(mChannelMask);      mFormat = mInput->stream->common.get_format(&mInput->stream->common);      mFrameSize = audio_stream_frame_size(&mInput->stream->common);      mInputBytes = mInput->stream->common.get_buffer_size(&mInput->stream->common); @@ -7023,7 +7642,18 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,      if (status == NO_ERROR && outStream != NULL) {          AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream); - +#ifdef QCOM_HARDWARE +        if (flags & AUDIO_OUTPUT_FLAG_LPA || flags & AUDIO_OUTPUT_FLAG_TUNNEL ) { +            AudioSessionDescriptor *desc = new AudioSessionDescriptor(hwDevHal, outStream, flags); +            desc->mActive = true; +            //TODO: no stream type +            //desc->mStreamType = streamType; +            desc->mVolumeLeft = 1.0; +            desc->mVolumeRight = 1.0; +            desc->device = *pDevices; +            mDirectAudioTracks.add(id, desc); +        } else +#endif          if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||              (config.format != AUDIO_FORMAT_PCM_16_BIT) ||              (config.channel_mask != AUDIO_CHANNEL_OUT_STEREO)) { @@ -7033,16 +7663,30 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,              thread = new MixerThread(this, output, id, *pDevices);              ALOGV("openOutput() created mixer output: ID %d thread %p", id, thread);          } -        mPlaybackThreads.add(id, thread); +#ifdef QCOM_HARDWARE +        if (thread != NULL) +#endif +            mPlaybackThreads.add(id, thread);          if (pSamplingRate != NULL) *pSamplingRate = config.sample_rate;          if (pFormat != NULL) *pFormat = config.format;          if (pChannelMask != NULL) *pChannelMask = config.channel_mask; -        if (pLatencyMs != NULL) *pLatencyMs = thread->latency(); - -        // notify client processes of the new output creation -        thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED); - +#ifdef QCOM_HARDWARE +        if (thread != NULL) { +#endif +            if (pLatencyMs != NULL) *pLatencyMs = thread->latency(); +            // notify client processes of the new output creation +            thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED); +#ifdef QCOM_HARDWARE +        } +        else { +            *pLatencyMs = 0; +            if ((flags & AUDIO_OUTPUT_FLAG_LPA) || (flags & AUDIO_OUTPUT_FLAG_TUNNEL)) { +                AudioSessionDescriptor *desc = mDirectAudioTracks.valueFor(id); +                *pLatencyMs = desc->stream->get_latency(desc->stream); +            } +        } +#endif          // the first primary output opened designates the primary hw device          if ((mPrimaryHardwareDev == NULL) && (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) {              ALOGI("Using module %d has the primary audio interface", module); @@ -7089,6 +7733,21 @@ status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output)  {      // keep strong reference on the playback thread so that      // it is not destroyed while exit() is executed +#ifdef QCOM_HARDWARE +    AudioSessionDescriptor *desc = mDirectAudioTracks.valueFor(output); +    if (desc) { +        ALOGV("Closing DirectTrack output %d", output); +        desc->mActive = false; +        desc->stream->common.standby(&desc->stream->common); +        desc->hwDev->close_output_stream(desc->hwDev, desc->stream); +        desc->trackRefPtr = NULL; +        mDirectAudioTracks.removeItem(output); +        audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL); +        delete desc; +        return NO_ERROR; +    } +#endif +      sp<PlaybackThread> thread;      {          Mutex::Autolock _l(mLock); @@ -7210,7 +7869,7 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,      if (status == BAD_VALUE &&          reqFormat == config.format && config.format == AUDIO_FORMAT_PCM_16_BIT &&          (config.sample_rate <= 2 * reqSamplingRate) && -        (popcount(config.channel_mask) <= FCC_2) && (popcount(reqChannels) <= FCC_2)) { +        (getInputChannelCount(config.channel_mask) <= FCC_2) && (getInputChannelCount(reqChannels) <= FCC_2)) {          ALOGV("openInput() reopening with proposed sampling rate and channel mask");          inStream = NULL;  #ifndef ICS_AUDIO_BLOB @@ -7833,6 +8492,21 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(              addEffectChain_l(chain);              chain->setStrategy(getStrategyForSession_l(sessionId));              chainCreated = true; +#ifdef QCOM_HARDWARE +            if(sessionId == mAudioFlinger->mLPASessionId) { +                // Clear reference to previous effect chain if any +                if(mAudioFlinger->mLPAEffectChain.get()) { +                    mAudioFlinger->mLPAEffectChain.clear(); +                } +                ALOGV("New EffectChain is created for LPA session ID %d", sessionId); +                mAudioFlinger->mLPAEffectChain = chain; +                chain->setLPAFlag(true); +                // For LPA, the volume will be applied in DSP. No need for volume +                // control in the Effect chain, so setting it to unity. +                uint32_t volume = 0x1000000; // Equals to 1.0 in 8.24 format +                chain->setVolume_l(&volume,&volume); +            } +#endif          } else {              effect = chain->getEffectFromDesc_l(desc);          } @@ -7863,6 +8537,11 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(              effect->setDevice(mInDevice);              effect->setMode(mAudioFlinger->getMode());              effect->setAudioSource(mAudioSource); +#ifdef QCOM_HARDWARE +            if(chain == mAudioFlinger->mLPAEffectChain) { +                effect->setLPAFlag(true); +            } +#endif          }          // create effect handle and connect it to effect module          handle = new EffectHandle(effect, client, effectClient, priority); @@ -7969,7 +8648,10 @@ void AudioFlinger::ThreadBase::lockEffectChains_l(  {      effectChains = mEffectChains;      for (size_t i = 0; i < mEffectChains.size(); i++) { -        mEffectChains[i]->lock(); +#ifdef QCOM_HARDWARE +        if (mEffectChains[i] != mAudioFlinger->mLPAEffectChain) +#endif +            mEffectChains[i]->lock();      }  } @@ -7977,7 +8659,10 @@ void AudioFlinger::ThreadBase::unlockEffectChains(          const Vector< sp<AudioFlinger::EffectChain> >& effectChains)  {      for (size_t i = 0; i < effectChains.size(); i++) { -        effectChains[i]->unlock(); +#ifdef QCOM_HARDWARE +        if (mEffectChains[i] != mAudioFlinger->mLPAEffectChain) +#endif +            effectChains[i]->unlock();      }  } @@ -8209,6 +8894,9 @@ AudioFlinger::EffectModule::EffectModule(ThreadBase *thread,        // mMaxDisableWaitCnt is set by configure() and not used before then        // mDisableWaitCnt is set by process() and updateState() and not used before then        mSuspended(false) +#ifdef QCOM_HARDWARE +      ,mIsForLPA(false) +#endif  {      ALOGV("Constructor %p", this);      int lStatus; @@ -8334,6 +9022,9 @@ AudioFlinger::EffectHandle *AudioFlinger::EffectModule::controlHandle_l()  size_t AudioFlinger::EffectModule::disconnect(EffectHandle *handle, bool unpinIfLast)  { +#ifdef QCOM_HARDWARE +    setEnabled(false); +#endif      ALOGV("disconnect() %p handle %p", this, handle);      // keep a strong reference on this EffectModule to avoid calling the      // destructor before we exit @@ -8440,8 +9131,19 @@ void AudioFlinger::EffectModule::reset_l()      (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_RESET, 0, NULL, 0, NULL);  } +#ifndef QCOM_HARDWARE  status_t AudioFlinger::EffectModule::configure()  { +#else +status_t AudioFlinger::EffectModule::configure(bool isForLPA, int sampleRate, int channelCount, int frameCount) +{ +    uint32_t channels; + +    // Acquire lock here to make sure that any other thread does not delete +    // the effect handle and release the effect module. +    Mutex::Autolock _l(mLock); +#endif +      if (mEffectInterface == NULL) {          return NO_INIT;      } @@ -8453,6 +9155,23 @@ status_t AudioFlinger::EffectModule::configure()      // TODO: handle configuration of effects replacing track process      audio_channel_mask_t channelMask = thread->channelMask(); +#ifdef QCOM_HARDWARE +    mIsForLPA = isForLPA; +    if(isForLPA) { +        if (channelCount == 1) { +            channels = AUDIO_CHANNEL_OUT_MONO; +        } else { +            channels = AUDIO_CHANNEL_OUT_STEREO; +        } +        ALOGV("%s: LPA ON - channels %d", __func__, channels); +    } else { +        if (thread->channelCount() == 1) { +            channels = AUDIO_CHANNEL_OUT_MONO; +        } else { +            channels = AUDIO_CHANNEL_OUT_STEREO; +        } +    } +#endif      if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {          mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_MONO; @@ -8462,7 +9181,13 @@ status_t AudioFlinger::EffectModule::configure()      mConfig.outputCfg.channels = channelMask;      mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;      mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; -    mConfig.inputCfg.samplingRate = thread->sampleRate(); +#ifdef QCOM_HARDWARE +    if(isForLPA){ +        mConfig.inputCfg.samplingRate = sampleRate; +        ALOGV("%s: LPA ON - sampleRate %d", __func__, sampleRate); +    } else +#endif +        mConfig.inputCfg.samplingRate = thread->sampleRate();      mConfig.outputCfg.samplingRate = mConfig.inputCfg.samplingRate;      mConfig.inputCfg.bufferProvider.cookie = NULL;      mConfig.inputCfg.bufferProvider.getBuffer = NULL; @@ -8487,7 +9212,13 @@ status_t AudioFlinger::EffectModule::configure()      }      mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;      mConfig.outputCfg.mask = EFFECT_CONFIG_ALL; -    mConfig.inputCfg.buffer.frameCount = thread->frameCount(); +#ifdef QCOM_HARDWARE +    if(isForLPA) { +        mConfig.inputCfg.buffer.frameCount = frameCount; +        ALOGV("%s: LPA ON - frameCount %d", __func__, frameCount); +    } else +#endif +        mConfig.inputCfg.buffer.frameCount = thread->frameCount();      mConfig.outputCfg.buffer.frameCount = mConfig.inputCfg.buffer.frameCount;      ALOGV("configure() %p thread %p buffer %p framecount %d", @@ -8667,10 +9398,15 @@ status_t AudioFlinger::EffectModule::setEnabled(bool enabled)  // must be called with EffectModule::mLock held  status_t AudioFlinger::EffectModule::setEnabled_l(bool enabled)  { - +#ifdef QCOM_HARDWARE +    bool effectStateChanged = false; +#endif      ALOGV("setEnabled %p enabled %d", this, enabled);      if (enabled != isEnabled()) { +#ifdef QCOM_HARDWARE +        effectStateChanged = true; +#endif          status_t status = AudioSystem::setEffectEnabled(mId, enabled);          if (enabled && status != NO_ERROR) {              return status; @@ -8708,6 +9444,16 @@ status_t AudioFlinger::EffectModule::setEnabled_l(bool enabled)              }          }      } +#ifdef QCOM_HARDWARE +    /* +       Send notification event to LPA Player when an effect for +       LPA output is enabled or disabled. +    */ +    if (effectStateChanged && mIsForLPA) { +        sp<ThreadBase> thread = mThread.promote(); +        thread->effectConfigChanged(); +    } +#endif      return NO_ERROR;  } @@ -9157,6 +9903,18 @@ status_t AudioFlinger::EffectHandle::command(uint32_t cmdCode,          return disable();      } +#ifdef QCOM_HARDWARE +    ALOGV("EffectHandle::command: isOnLPA %d", mEffect->isOnLPA()); +    if(mEffect->isOnLPA() && +       ((cmdCode == EFFECT_CMD_SET_PARAM) || (cmdCode == EFFECT_CMD_SET_PARAM_DEFERRED) || +        (cmdCode == EFFECT_CMD_SET_PARAM_COMMIT) || (cmdCode == EFFECT_CMD_SET_DEVICE) || +        (cmdCode == EFFECT_CMD_SET_VOLUME) || (cmdCode == EFFECT_CMD_SET_AUDIO_MODE)) ) { +        // Notify Direct track for the change in Effect module +        // TODO: check if it is required to send mLPAHandle +        ALOGV("Notifying Direct Track for the change in effect config"); +        mClient->audioFlinger()->audioConfigChanged_l(AudioSystem::EFFECT_CONFIG_CHANGED, 0, NULL); +    } +#endif      return mEffect->command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);  } @@ -9225,6 +9983,9 @@ AudioFlinger::EffectChain::EffectChain(ThreadBase *thread,      : mThread(thread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),        mOwnInBuffer(false), mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),        mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX) +#ifdef QCOM_HARDWARE +      ,mIsForLPATrack(false) +#endif  {      mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);      if (thread == NULL) { @@ -9269,6 +10030,20 @@ sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromId_l(int      return 0;  } +#ifdef QCOM_HARDWARE +sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromIndex_l(int idx) +{ +    sp<EffectModule> effect = NULL; +    if(idx < 0 || idx >= mEffects.size()) { +        ALOGE("EffectChain::getEffectFromIndex_l: invalid index %d", idx); +    } +    if(mEffects.size() > 0){ +        effect = mEffects[idx]; +    } +    return effect; +} +#endif +  // getEffectFromType_l() must be called with ThreadBase::mLock held  sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromType_l(          const effect_uuid_t *type) @@ -9335,7 +10110,11 @@ void AudioFlinger::EffectChain::process_l()      }      size_t size = mEffects.size(); +#ifdef QCOM_HARDWARE +    if (doProcess || isForLPATrack()) { +#else      if (doProcess) { +#endif          for (size_t i = 0; i < size; i++) {              mEffects[i]->process();          } diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 49e2b2c..d2daae7 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -1,6 +1,9 @@  /*  **  ** Copyright 2007, The Android Open Source Project +** Copyright (c) 2012, The Linux Foundation. All rights reserved. +** Not a Contribution, Apache license notifications and license are retained +** for attribution purposes only.  **  ** Licensed under the Apache License, Version 2.0 (the "License");  ** you may not use this file except in compliance with the License. @@ -26,6 +29,10 @@  #include <media/IAudioFlinger.h>  #include <media/IAudioFlingerClient.h> +#ifdef QCOM_HARDWARE +#include <media/IDirectTrack.h> +#include <media/IDirectTrackClient.h> +#endif  #include <media/IAudioTrack.h>  #include <media/IAudioRecord.h>  #include <media/AudioSystem.h> @@ -52,6 +59,7 @@  #include "AudioWatchdog.h"  #include <powermanager/IPowerManager.h> +#include <utils/List.h>  namespace android { @@ -99,6 +107,19 @@ public:                                  pid_t tid,                                  int *sessionId,                                  status_t *status); +#ifdef QCOM_HARDWARE +    virtual sp<IDirectTrack> createDirectTrack( +                                pid_t pid, +                                uint32_t sampleRate, +                                audio_channel_mask_t channelMask, +                                audio_io_handle_t output, +                                int *sessionId, +                                IDirectTrackClient* client, +                                audio_stream_type_t streamType, +                                status_t *status); + +    virtual void deleteEffectSession(); +#endif      virtual sp<IAudioRecord> openRecord(                                  pid_t pid, @@ -141,7 +162,9 @@ public:      virtual     String8     getParameters(audio_io_handle_t ioHandle, const String8& keys) const;      virtual     void        registerClient(const sp<IAudioFlingerClient>& client); - +#ifdef QCOM_HARDWARE +    virtual status_t deregisterClient(const sp<IAudioFlingerClient>& client); +#endif      virtual     size_t      getInputBufferSize(uint32_t sampleRate, audio_format_t format,                                                 audio_channel_mask_t channelMask) const; @@ -216,6 +239,13 @@ public:                                  Parcel* reply,                                  uint32_t flags); +#ifdef QCOM_HARDWARE +    void applyEffectsOn(void *token, +                        int16_t *buffer1, +                        int16_t *buffer2, +                        int size); +#endif +      // end of IAudioFlinger interface      class SyncEvent; @@ -314,7 +344,7 @@ private:      public:                              NotificationClient(const sp<AudioFlinger>& audioFlinger,                                                  const sp<IAudioFlingerClient>& client, -                                                pid_t pid); +                                                sp<IBinder> binder);          virtual             ~NotificationClient();                  sp<IAudioFlingerClient> audioFlingerClient() const { return mAudioFlingerClient; } @@ -327,7 +357,7 @@ private:                              NotificationClient& operator = (const NotificationClient&);          const sp<AudioFlinger>  mAudioFlinger; -        const pid_t             mPid; +        sp<IBinder>             mBinder;          const sp<IAudioFlingerClient> mAudioFlingerClient;      }; @@ -343,6 +373,9 @@ private:      class EffectModule;      class EffectHandle;      class EffectChain; +#ifdef QCOM_HARDWARE +    struct AudioSessionDescriptor; +#endif      struct AudioStreamOut;      struct AudioStreamIn; @@ -570,6 +603,9 @@ private:          virtual     status_t    setParameters(const String8& keyValuePairs);          virtual     String8     getParameters(const String8& keys) = 0;          virtual     void        audioConfigChanged_l(int event, int param = 0) = 0; +#ifdef QCOM_HARDWARE +                    void        effectConfigChanged(); +#endif                      void        sendIoConfigEvent(int event, int param = 0);                      void        sendIoConfigEvent_l(int event, int param = 0);                      void        sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio); @@ -1400,6 +1436,115 @@ private:                sp<PlaybackThread> getEffectThread_l(int sessionId, int EffectId);      // server side of the client's IAudioTrack +#ifdef QCOM_HARDWARE +    class DirectAudioTrack : public android::BnDirectTrack, +                             public AudioEventObserver +    { +    public: +                            DirectAudioTrack(const sp<AudioFlinger>& audioFlinger, +                                             int output, AudioSessionDescriptor *outputDesc, +                                             IDirectTrackClient* client, audio_output_flags_t outflag); +        virtual             ~DirectAudioTrack(); +        virtual status_t    start(); +        virtual void        stop(); +        virtual void        flush(); +        virtual void        mute(bool); +        virtual void        pause(); +        virtual ssize_t     write(const void *buffer, size_t bytes); +        virtual void        setVolume(float left, float right); +        virtual int64_t     getTimeStamp(); +        virtual void        postEOS(int64_t delayUs); + +        virtual status_t    onTransact( +            uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); +    private: + +        IDirectTrackClient* mClient; +        AudioSessionDescriptor *mOutputDesc; +        int  mOutput; +        bool mIsPaused; +        audio_output_flags_t mFlag; + +        class BufferInfo { +        public: +            BufferInfo(void *buf1, void *buf2, int32_t nSize) : +            localBuf(buf1), dspBuf(buf2), memBufsize(nSize) +            {} + +            void *localBuf; +            void *dspBuf; +            uint32_t memBufsize; +            uint32_t bytesToWrite; +        }; +        List<BufferInfo> mBufPool; +        List<BufferInfo> mEffectsPool; + +        void allocateBufPool(); +        void deallocateBufPool(); + +        //******Effects************* +        static void *EffectsThreadWrapper(void *me); +        void EffectsThreadEntry(); +        // make sure the Effects thread also exited +        void requestAndWaitForEffectsThreadExit(); +        void createEffectThread(); +        Condition mEffectCv; +        Mutex mEffectLock; +        pthread_t mEffectsThread; +        bool mKillEffectsThread; +        bool mEffectsThreadAlive; +        bool mEffectConfigChanged; + +        //Structure to recieve the Effect notification from the flinger. +        class AudioFlingerDirectTrackClient: public IBinder::DeathRecipient, public BnAudioFlingerClient { +        public: +            AudioFlingerDirectTrackClient(void *obj); + +            DirectAudioTrack *pBaseClass; +            // DeathRecipient +            virtual void binderDied(const wp<IBinder>& who); + +            // IAudioFlingerClient + +            // indicate a change in the configuration of an output or input: keeps the cached +            // values for output/input parameters upto date in client process +            virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2); + +            friend class DirectAudioTrack; +        }; +        // helper function to obtain AudioFlinger service handle +        sp<AudioFlinger> mAudioFlinger; +        sp<AudioFlingerDirectTrackClient> mAudioFlingerClient; + +	void clearPowerManager(); +        class PMDeathRecipient : public IBinder::DeathRecipient { +            public: +                            PMDeathRecipient(void *obj){parentClass = (DirectAudioTrack *)obj;} +                virtual     ~PMDeathRecipient() {} + +                // IBinder::DeathRecipient +                virtual     void        binderDied(const wp<IBinder>& who); + +            private: +                            DirectAudioTrack *parentClass; +                            PMDeathRecipient(const PMDeathRecipient&); +                            PMDeathRecipient& operator = (const PMDeathRecipient&); + +            friend class DirectAudioTrack; +        }; + +        friend class PMDeathRecipient; + +        Mutex pmLock; +        void        acquireWakeLock(); +        void        releaseWakeLock(); + +        sp<IPowerManager>       mPowerManager; +        sp<IBinder>             mWakeLockToken; +        sp<PMDeathRecipient>    mDeathRecipient; +    }; +#endif +      class TrackHandle : public android::BnAudioTrack {      public:                              TrackHandle(const sp<PlaybackThread::Track>& track); @@ -1424,7 +1569,7 @@ private:      };                  void        removeClient_l(pid_t pid); -                void        removeNotificationClient(pid_t pid); +                void        removeNotificationClient(sp<IBinder> binder);      // record thread @@ -1636,7 +1781,14 @@ private:                           void *pReplyData);          void reset_l(); +#ifdef QCOM_HARDWARE +        status_t configure(bool isForLPA = false, +                           int sampleRate = 0, +                           int channelCount = 0, +                           int frameCount = 0); +#else          status_t configure(); +#endif          status_t init();          effect_state state() const {              return mState; @@ -1683,7 +1835,10 @@ private:          bool             purgeHandles();          void             lock() { mLock.lock(); }          void             unlock() { mLock.unlock(); } - +#ifdef QCOM_HARDWARE +        bool             isOnLPA() { return mIsForLPA;} +        void             setLPAFlag(bool isForLPA) {mIsForLPA = isForLPA; } +#endif          void             dump(int fd, const Vector<String16>& args);      protected: @@ -1715,6 +1870,9 @@ mutable Mutex               mLock;      // mutex for process, commands and handl                                          // sending disable command.          uint32_t mDisableWaitCnt;       // current process() calls count during disable period.          bool     mSuspended;            // effect is suspended: temporarily disabled by framework +#ifdef QCOM_HARDWARE +        bool     mIsForLPA; +#endif      };      // The EffectHandle class implements the IEffect interface. It provides resources @@ -1823,12 +1981,18 @@ mutable Mutex               mLock;      // mutex for process, commands and handl          status_t addEffect_l(const sp<EffectModule>& handle);          size_t removeEffect_l(const sp<EffectModule>& handle); +#ifdef QCOM_HARDWARE +        size_t getNumEffects() { return mEffects.size(); } +#endif          int sessionId() const { return mSessionId; }          void setSessionId(int sessionId) { mSessionId = sessionId; }          sp<EffectModule> getEffectFromDesc_l(effect_descriptor_t *descriptor);          sp<EffectModule> getEffectFromId_l(int id); +#ifdef QCOM_HARDWARE +        sp<EffectModule> getEffectFromIndex_l(int idx); +#endif          sp<EffectModule> getEffectFromType_l(const effect_uuid_t *type);          bool setVolume_l(uint32_t *left, uint32_t *right);          void setDevice_l(audio_devices_t device); @@ -1874,6 +2038,10 @@ mutable Mutex               mLock;      // mutex for process, commands and handl          void clearInputBuffer();          void dump(int fd, const Vector<String16>& args); +#ifdef QCOM_HARDWARE +        bool isForLPATrack() {return mIsForLPATrack; } +        void setLPAFlag(bool flag) {mIsForLPATrack = flag;} +#endif      protected:          friend class AudioFlinger;  // for mThread, mEffects @@ -1922,6 +2090,9 @@ mutable Mutex               mLock;      // mutex for process, commands and handl          uint32_t mNewLeftVolume;       // new volume on left channel          uint32_t mNewRightVolume;      // new volume on right channel          uint32_t mStrategy; // strategy for this effect chain +#ifdef QCOM_HARDWARE +        bool     mIsForLPATrack; +#endif          // mSuspendedEffects lists all effects currently suspended in the chain.          // Use effect type UUID timelow field as key. There is no real risk of identical          // timeLow fields among effect type UUIDs. @@ -1983,7 +2154,21 @@ mutable Mutex               mLock;      // mutex for process, commands and handl          AudioStreamIn(AudioHwDevice *dev, audio_stream_in_t *in) :              audioHwDev(dev), stream(in) {}      }; - +#ifdef QCOM_HARDWARE +    struct AudioSessionDescriptor { +        bool    mActive; +        int     mStreamType; +        float   mVolumeLeft; +        float   mVolumeRight; +        audio_hw_device_t   *hwDev; +        audio_stream_out_t  *stream; +        audio_output_flags_t flag; +        void *trackRefPtr; +        audio_devices_t device; +        AudioSessionDescriptor(audio_hw_device_t *dev, audio_stream_out_t *out, audio_output_flags_t outflag) : +            hwDev(dev), stream(out), flag(outflag)  {} +    }; +#endif      // for mAudioSessionRefs only      struct AudioSessionRef {          AudioSessionRef(int sessionid, pid_t pid) : @@ -2043,14 +2228,25 @@ mutable Mutex               mLock;      // mutex for process, commands and handl                  DefaultKeyedVector< audio_io_handle_t, sp<RecordThread> >    mRecordThreads; -                DefaultKeyedVector< pid_t, sp<NotificationClient> >    mNotificationClients; +                DefaultKeyedVector< sp<IBinder>, sp<NotificationClient> >    mNotificationClients;                  volatile int32_t                    mNextUniqueId;  // updated by android_atomic_inc                  audio_mode_t                        mMode;                  bool                                mBtNrecIsOff; - +#ifdef QCOM_HARDWARE +                DefaultKeyedVector<audio_io_handle_t, AudioSessionDescriptor *> mDirectAudioTracks; +#endif                  // protected by mLock +#ifdef QCOM_HARDWARE +                volatile bool                       mIsEffectConfigChanged; +#endif                  Vector<AudioSessionRef*> mAudioSessionRefs; - +#ifdef QCOM_HARDWARE +                sp<EffectChain> mLPAEffectChain; +                int         mLPASessionId; +                int                                 mLPASampleRate; +                int                                 mLPANumChannels; +                volatile bool                       mAllChainsLocked; +#endif                  float       masterVolume_l() const;                  bool        masterMute_l() const;                  audio_module_handle_t loadHwModule_l(const char *name);  | 
