android系统学习笔记六

时间:2022-12-25 17:01:17

 android 的多媒体系统

    

 多媒体系统的结构和业务

多媒体系统的宏鸡观结构

应用层,java框架层,c 语言层,硬件抽像层,其中输入输出由HAL,处理环节由packetViewOpenCore实现,

多媒体业备有以下几种:

musicPlayer(音频播放器)

viderPlayer(视频播放器)

Camera(照相机)

soundRecord(录音机)

videoCamera (摄像机)

Media metadata(媒体元信息)

   核心是媒体的播放和录制,分别由下层的OpenCorePVPlayerPVAuthor来实现

多媒体的java:

 \frameworks\base\media\java\android\media

Jni部分的代码路径:

 \frameworks\base\media\jni   最终编译生成libMedia_jni.so

多媒体的本地框架:

\frameworks\base\include\media

\frameworks\base\media\libmedia

最终被子编译成libmedia.so

多媒体的服务部分

库的代码路径:\frameworks\base\media\libmediaplayerservice

   最后编译生成:libmediaplayerservice.so

守护进程的代码路径:\frameworks\base\media\mediaserver

多媒体的实现部分:

      

多媒体的各种业务

多媒体从实现角度看分为两部分:

输入/输出环节 

中间处理环节(文件格式的处理和编解码环节)

例如一个mp3的播放

Mp3格式文件的解析,mp3编码流的解码,pcm输出的播放

媒体播放器涉及内容

   本地媒体揪放器部分;

   PVPlayer(实现的核心部分)   

   音频视频的编解码 

   音频输出环节

   视频输出环节  (surface或者是overlay)

   Android.media.mediaplayer

   Android.view.surface

   Andorid.widget.videoview

数据流在android 媒体播放器中的运行情况是:

  上层的java应用程序将媒体的URI设置到媒体播放器中.

   Java框架架----->JNI-------->本地框架------->PVPlayer,

   PVPlayer解析后,将媒体分为音频流和视频流

   经过编码器的处理和同步(AVSync),转换成原始数据(音频是PCM,视频一般是YUV或者是RGB)

照相机的统结构

录音机的系统结构

   本地媒体框架中的媒体记录器部分

   PVPlayer

 音频编码模块

 音频输入环节

 android.media.mediaRecorder

 soundRecorder

摄像机的系统结构

    本地框加的媒体记录器部分

    PVAuthor

    音频/视频编码模块

    音频输入环节

    Camera的本地接口

    视频输出环节

    android.media.MediaRecorder

    Android.view.surface

    Andoird.widget.videoview

    Music包和camera

多媒体系统的各个层次

libMedia的框架部分

 媒体播放器

    头文件的目录

\frameworks\base\include\media

主要的头文件有:

 Mediaplayer.h媒体播放器本地部分的上层接口 

       提供了对上层的调用,通过JNI将接口给java调用,其实都是调用下层的mediaplayer

继承自Bnmediaplayerclient.

    部分代码如下:

    class MediaPlayer : public BnMediaPlayerClient,

                    public virtual IMediaDeathNotifier

{

public:

    MediaPlayer();

    ~MediaPlayer();

            void            died();

            void            disconnect();

            status_t        setDataSource(

                    const char *url,//设置数据源url

                    const KeyedVector<String8, String8> *headers);

            status_t        setDataSource(int fd, int64_t offset, int64_t length);//设置数据源文件

            status_t        setVideoSurface(const sp<Surface>& surface);//设视频输出界面

            status_t        setListener(const sp<MediaPlayerListener>& listener);//设置临听

            status_t        prepare();//准备播放

            status_t        prepareAsync();//异部准备播放

            status_t        start();//开始

            status_t        stop();//停止

            status_t        pause();//暂停

            bool            isPlaying();//是否正在播放

            status_t        getVideoWidth(int *w);//获取视频播放的宽

            status_t        getVideoHeight(int *h);//获取视频播放的高 

            status_t        seekTo(int msec);//跳转到指定位置

            status_t        getCurrentPosition(int *msec);//取得当前的播放位置

            status_t        getDuration(int *msec);//播放的持续时间(总时长)

            status_t        reset();//复位

            status_t        setAudioStreamType(int type);//设置音频流的格式

            status_t        setLooping(int loop);// 设置循环

            bool            isLooping();//是否循环

            status_t        setVolume(float leftVolume, float rightVolume);//设置音量

            void            notify(int msg, int ext1, int ext2);//通知函数

    static  sp<IMemory>     decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);

    static  sp<IMemory>     decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);

            status_t        invoke(const Parcel& request, Parcel *reply);

            status_t        setMetadataFilter(const Parcel& filter);

            status_t        getMetadata(bool update_only, bool apply_filter, Parcel *metadata);

            status_t        suspend();

            status_t        resume();

            status_t        setAudioSessionId(int sessionId);

            int             getAudioSessionId();

            status_t        setAuxEffectSendLevel(float level);

            status_t        attachAuxEffect(int effectId);

private:

            void            clear_l();

            status_t        seekTo_l(int msec);

            status_t        prepareAsync_l();

            status_t        getDuration_l(int *msec);

            status_t        setDataSource(const sp<IMediaPlayer>& player);

    sp<IMediaPlayer>            mPlayer;

    thread_id_t                 mLockThreadId;

    Mutex                       mLock;

    Mutex                       mNotifyLock;

    Condition                   mSignal;

    sp<MediaPlayerListener>     mListener;

    void*                       mCookie;

    media_player_states         mCurrentState;

    int                         mDuration;

    int                         mCurrentPosition;

    int                         mSeekPosition;

    bool                        mPrepareSync;

    status_t                    mPrepareStatus;

    int                         mStreamType;

    bool                        mLoop;

    float                       mLeftVolume;

    float                       mRightVolume;

    int                         mVideoWidth;

    int                         mVideoHeight;

    int                         mAudioSessionId;

    float                       mSendLevel;

};

   

 IMeciaplayer.h 媒体播和器服务部分的接口(和上层的mediaplay 中的接口方法类似)

   被BnMedaplayer继承,提供Binder通信本地实现基础

    部分代码如下:

//继承mediaplayerBase,通过autoFlinger输出

    class MediaPlayerInterface : public MediaPlayerBase{

public:

    virtual             ~MediaPlayerInterface() { }

    virtual bool        hardwareOutput() { return false; }

    virtual void        setAudioSink(const sp<AudioSink>& audioSink) { mAudioSink = audioSink; }

protected:

    sp<AudioSink>       mAudioSink;//音频轮输出设备的抽像接口

};

// Implement this class for media players that output directo to hardware

//直接从硬功夫件进行音频输出

class MediaPlayerHWInterface : public MediaPlayerBase//继承mediaplayerBaser

{

public:

    virtual             ~MediaPlayerHWInterface() {}

    virtual bool        hardwareOutput() { return true; }

    virtual status_t    setVolume(float leftVolume, float rightVolume) = 0;

    virtual status_t    setAudioStreamType(int streamType) = 0;

};

 mediaplayerInterface.h

  PVPlayer.h是媒体播放器实现层的接口 (opencore媒体播放器实现的头文件)

   继承自MediaPlayerInterface

   部分代码如下:

  class PVPlayer : public MediaPlayerInterface

{

public:

                        PVPlayer();

    virtual             ~PVPlayer();

    virtual status_t    initCheck();

    virtual status_t    setDataSource(

            const char *url, const KeyedVector<String8, String8> *headers);

    virtual status_t    setDataSource(int fd, int64_t offset, int64_t length);

    virtual status_t    setVideoSurface(const sp<ISurface>& surface);

    virtual status_t    prepare();

    virtual status_t    prepareAsync();

    virtual status_t    start();

    virtual status_t    stop();

    virtual status_t    pause();

    virtual bool        isPlaying();

    virtual status_t    seekTo(int msec);

    virtual status_t    getCurrentPosition(int *msec);

    virtual status_t    getDuration(int *msec);

    virtual status_t    reset();

    virtual status_t    setLooping(int loop);

    virtual player_type playerType() { return PV_PLAYER; }

    virtual status_t    invoke(const Parcel& request, Parcel *reply);

    virtual status_t    getMetadata(

        const SortedVector<media::Metadata::Type>& ids,

        Parcel *records);

    // make available to PlayerDriver

    void        sendEvent(int msg, int ext1=0, int ext2=0) { MediaPlayerBase::sendEvent(msg, ext1, ext2); }

private:

    static void         do_nothing(status_t s, void *cookie, bool cancelled) { }

    static void         run_init(status_t s, void *cookie, bool cancelled);

    static void         run_set_video_surface(status_t s, void *cookie, bool cancelled);

    static void         run_set_audio_output(status_t s, void *cookie, bool cancelled);

    static void         run_prepare(status_t s, void *cookie, bool cancelled);

    static void         check_for_live_streaming(status_t s, void *cookie, bool cancelled);

    PlayerDriver*               mPlayerDriver;

    char *                      mDataSourcePath;

    bool                        mIsDataSourceSet;

    sp<ISurface>                mSurface;

    int                         mSharedFd;

    status_t                    mInit;

    int                         mDuration;

#ifdef MAX_OPENCORE_INSTANCES

    static volatile int32_t     sNumInstances;

#endif

};

 ImeciaplayerClient.h 多媒体的客户端(定义了媒体的客户端)  主要用作通知函数

    Mediaplayer继承ImediaplayerClient 所以可以得到下层传弟的信息

    部分代码如下:

class IMediaPlayerClient: public IInterface

{

public:

    DECLARE_META_INTERFACE(MediaPlayerClient);

    virtual void notify(int msg, int ext1, int ext2) = 0;//通知的信息是个消息

};

 Imediaplayerservice.h 多媒体的服务(定义了多媒体服务的接口,由下层服务去实现)

部分代码如下:

  class IMediaPlayerService: public IInterface

{

public:

    DECLARE_META_INTERFACE(MediaPlayerService);

/

    virtual sp<IMediaRecorder>  createMediaRecorder(pid_t pid) = 0;

    virtual sp<IMediaMetadataRetriever> createMetadataRetriever(pid_t pid) = 0;

/创建IMediaPlayer

    virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client,

            const char* url, const KeyedVector<String8, String8> *headers = NULL,

            int audioSessionId = 0) = 0;

    virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client,

            int fd, int64_t offset, int64_t length, int audioSessionId) = 0;

//用于直接解码

    virtual sp<IMemory>         decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;

    virtual sp<IMemory>         decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;

    virtual sp<IOMX>            getOMX() = 0;

};

// ----------------------------------------------------------------------------

class BnMediaPlayerService: public BnInterface<IMediaPlayerService>

{

public:

    virtual status_t    onTransact( uint32_t code,

                                    const Parcel& data,

                                    Parcel* reply,

                                    uint32_t flags = 0);

};

源文件的目录

\frameworks\base\media\libmedia

媒体记录器

头文件和实现文件的代码路径:

\frameworks\base\include\media

主要的头文件有:

 MediaRecorder.h 媒体记录器的上层拉接口,每个函数都调用IMediaRecord来实现他也继承了BnMediaPlayerClient用于接收下层返回的通知

    部分代码如下:

    class MediaRecorder : public BnMediaRecorderClient,

                      public virtual IMediaDeathNotifier

{

public:

    MediaRecorder();

    ~MediaRecorder();

    void        died();

    status_t    initCheck();

    status_t    setCamera(const sp<ICamera>& camera);  //设置camera作为输入设备

    status_t    setPreviewSurface(const sp<Surface>& surface); //设置视频预览界面

    status_t    setVideoSource(int vs);  //视频数据源枚举值)

    status_t    setAudioSource(int as);  //音频数据源 (同上)

    status_t    setOutputFormat(int of);  //设置输出格式

    status_t    setVideoEncoder(int ve);  //设置视频编码格式

    status_t    setAudioEncoder(int ae);   //设置音频编码格式

    status_t    setOutputFile(const char* path);  //设置输出文件路径

    status_t    setOutputFile(int fd, int64_t offset, int64_t length); //设置输出文件的文件描述符

    status_t    setVideoSize(int width, int height);  //设置视频尺寸

    status_t    setVideoFrameRate(int frames_per_second);  //设置视频帧率

    status_t    setParameters(const String8& params);  //设置其他参数

    status_t    setListener(const sp<MediaRecorderListener>& listener);  //设置临听

    status_t    prepare();  //准备录制

    status_t    getMaxAmplitude(int* max);  //获得最大增益

     status_t    start();   //开始

    status_t    stop();   //停止

    status_t    reset();  //复位

    status_t    init();  //初始化记录器

    status_t    close(); //关闭记录器

    status_t    release();  //释放资源

    void        notify(int msg, int ext1, int ext2);

private:

    void                    doCleanUp();

    status_t                doReset();

    sp<IMediaRecorder>          mMediaRecorder;

    sp<MediaRecorderListener>   mListener;

    media_recorder_states       mCurrentState;

    bool                        mIsAudioSourceSet;

    bool                        mIsVideoSourceSet;

    bool                        mIsAudioEncoderSet;

    bool                        mIsVideoEncoderSet;

    bool                        mIsOutputFileSet;

    Mutex                       mLock;

    Mutex                       mNotifyLock;

};

}; 

 IMediaRecorder.h 媒体记录器的部分实现接口 

   部分代码如下:

   class IMediaRecorder: public IInterface

{

public:

    DECLARE_META_INTERFACE(MediaRecorder);

    virtual status_t setCamera(const sp<ICamera>& camera) = 0;

    virtual status_t setPreviewSurface(const sp<ISurface>& surface) = 0;

    virtual status_t setVideoSource(int vs) = 0;

    virtual status_t setAudioSource(int as) = 0;

    virtual status_t setOutputFormat(int of) = 0;

    virtual status_t setVideoEncoder(int ve) = 0;

    virtual status_t setAudioEncoder(int ae) = 0;

    virtual status_t setOutputFile(const char* path) = 0;

    virtual status_t setOutputFile(int fd, int64_t offset, int64_t length) = 0;

    virtual status_t setVideoSize(int width, int height) = 0;

    virtual status_t setVideoFrameRate(int frames_per_second) = 0;

    virtual     status_t                setParameters(const String8& params) = 0;

    virtual     status_t                setListener(const sp<IMediaRecorderClient>& listener) = 0;

    virtual status_t prepare() = 0;

    virtual status_t getMaxAmplitude(int* max) = 0;

    virtual status_t start() = 0;

    virtual status_t stop() = 0;

    virtual status_t reset() = 0;

    virtual status_t        init() = 0;

    virtual status_t        close() = 0;

    virtual status_t release() = 0;

};

// ----------------------------------------------------------------------------

class BnMediaRecorder: public BnInterface<IMediaRecorder>

{

public:

    virtual status_t    onTransact( uint32_t code,

                                    const Parcel& data,

                                    Parcel* reply,

                                    uint32_t flags = 0);

};

};

 PVMediaRecorder.h 下层接口,openCore实现

 媒体元信息和扫描器

主要的头文件有:

MediaMetadataRetriever.h

  部分代码如下;

 class MediaMetadataRetriever: public RefBase

{

public:

    MediaMetadataRetriever();

    ~MediaMetadataRetriever();

    void disconnect();

    status_t setDataSource(const char* dataSourceUrl);  //设置数据源(url)

    status_t setDataSource(int fd, int64_t offset, int64_t length);  //设置数据源(文件描述符)

    sp<IMemory> getFrameAtTime(int64_t timeUs, int option); //捕获帧

    sp<IMemory> extractAlbumArt();  // 抽取

    const char* extractMetadata(int keyCode);  //抽取元信息

IMediaMetadataRetriever

MediaMetadataRetrieverInterface.h 实现的接口文件

PVMetadataRetriever.h     下层实现的接口

class MediaMetadataRetrieverBase : public RefBase{}

class MediaMetadataRetrieverInterface : public MediaMetadataRetrieverBase{}

class PVMetadataRetriever : public MediaMetadataRetrieverInterface{}

媒体扫描器的头文件

MediaScanner .h  scanner的接口扫描一个文件或者一个文件夹取得文件格式,会调用MediaScannerClient

 部分代码如下:

 struct MediaScanner {

    MediaScanner();

    virtual ~MediaScanner();

    virtual status_t processFile(

            const char *path, const char *mimeType,

            MediaScannerClient &client) = 0;

    typedef bool (*ExceptionCheck)(void* env);

    virtual status_t processDirectory(

            const char *path, const char *extensions,

            MediaScannerClient &client,

            ExceptionCheck exceptionCheck, void *exceptionEnv);

    void setLocale(const char *locale);

    // extracts album art as a block of data

    virtual char *extractAlbumArt(int fd) = 0;

}

class MediaScannerClient

{

public:

    MediaScannerClient();

    virtual ~MediaScannerClient();

    void setLocale(const char* locale);

    void beginFile();

    bool addStringTag(const char* name, const char* value);

    void endFile();

    virtual bool scanFile(const char* path, long long lastModified, long long fileSize) = 0;

    virtual bool handleStringTag(const char* name, const char* value) = 0;

    virtual bool setMimeType(const char* mimeType) = 0;

    virtual bool addNoMediaFolder(const char* path) = 0;

}

多媒体服务

    他包含媒体揪放器媒体记录器,媒体元信息管理 他和他的调用者是在两个不同的进程中,使用binder进行IPC通信

多媒体服务的守护进程main_mediaserver.cpp

 代码中和路径: \frameworks\base\media\mediaserver

  部分代码如下:

   int main(int argc, char** argv)

{

    sp<ProcessState> proc(ProcessState::self());

    sp<IServiceManager> sm = defaultServiceManager();

    LOGI("ServiceManager: %p", sm.get());

    AudioFlinger::instantiate();     //用于声音的混合

    MediaPlayerService::instantiate();  //用于音频播放

    CameraService::instantiate();     //摄像头相关服务

    AudioPolicyService::instantiate();

    ProcessState::self()->startThreadPool();

    IPCThreadState::self()->joinThreadPool();

}

audioFlinger 是通过defaultServiceMannager获取IServiceMamager接口 通过addService方法注册为

 Media.audido_flinger

Mediaserver作为一个守护进程,在androidinit.rc中具有如下定义

 Service media /system/bin/mediaserver

 User media

 Group system audio camera graphics inet net_bt net_bt_admin

由于没有定义oneshot,所以这个进程一直存在,如果被杀死,init会将其重新启动

多媒体服务的实现

  多媒体服务的路径:\frameworks\base\media\libmediaplayerservice

mediaPlayerService.h头文件中定义了 ,IMediaplayer的实现

 class MediaPlayerService : public BnMediaPlayerService{

     class AudioOutput : public MediaPlayerBase::AudioSink {}

     class AudioCache : public MediaPlayerBase::AudioSink{}

     class Client : public BnMediaPlayer {}

}

IMeciaRecorder的实现

class MediaRecorderClient : public BnMediaRecorder{}

IMediadataRetriever的实现

class MetadataRetrieverClient : public BnMediaMetadataRetriever{}

MediaPlayerService.cpp中定义了取得媒体记录器(IMediaRecorder>)的接口

sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(pid_t pid)

{

    sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid);

    wp<MediaRecorderClient> w = recorder;

    Mutex::Autolock lock(mLock);

    mMediaRecorderClients.add(w);

    LOGV("Create new media recorder client from pid %d", pid);

    return recorder;

}

取得媒体播放器的媒体元信息

sp<IMediaMetadataRetriever> MediaPlayerService::createMetadataRetriever(pid_t pid)

{

    sp<MetadataRetrieverClient> retriever = new MetadataRetrieverClient(pid);

    LOGV("Create new media retriever from pid %d", pid);

    return retriever;

}

  

MediaPlayService 类中创建媒体播放器的过程:

 

1

 sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client,

        int fd, int64_t offset, int64_t length, int audioSessionId)

{

    int32_t connId = android_atomic_inc(&mNextConnId);

    //创建mediaPlayerService::Client

    sp<Client> c = new Client(this, pid, connId, client, audioSessionId);

    LOGV("Create new client(%d) from pid %d, fd=%d, offset=%lld, length=%lld, audioSessionId=%d",

            connId, pid, fd, offset, length, audioSessionId);

//设置源的url

    if (NO_ERROR != c->setDataSource(fd, offset, length)) {//根据setDataSource()时根据输入的类型创建不同的mediaPlayBase, 接着调用下面的createPlayer方法创建不同的player

        c.clear();

    } else {

        wp<Client> w = c;

        Mutex::Autolock lock(mLock);

        mClients.add(w);

    }

    ::close(fd);

    return c;

}

static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,

        notify_callback_f notifyFunc)

{

    sp<MediaPlayerBase> p;

    switch (playerType) {  //根据playerType的类型建立不同的播放器

#ifndef NO_OPENCORE

        case PV_PLAYER:

            LOGV(" create PVPlayer");

            p = new PVPlayer();

            break;

#endif

        case SONIVOX_PLAYER:

            LOGV(" create MidiFile");

            p = new MidiFile();

            break;

        case STAGEFRIGHT_PLAYER:

            LOGV(" create StagefrightPlayer");

            p = new StagefrightPlayer;

            break;

        case TEST_PLAYER:

            LOGV("Create Test Player stub");

            p = new TestPlayerStub();

            break;

    }

    if (p != NULL) {

        if (p->initCheck() == NO_ERROR) {

            p->setNotifyCallback(cookie, notifyFunc);

        } else {

            p.clear();

        }

    }

    if (p == NULL) {

        LOGE("Failed to create player object");

    }

    return p;

}

 PVPlayer  MidiFileVorbisPlayer三个都继承MediaPlayInterface得到的,MediaPlayerInterface是继承MediaPlayerBase得到,  三者具有相同的接口类型,三者在建立之后通过MediaPlayerBase接口来控制他们

媒体播放器的实现结构如下图所示

MediaPlayerService::AudioOutput实现audio输出环节的封装,由Audio系统来实现,主要是调用AudioTrakc的接口

status_t MediaPlayerService::AudioOutput::open(

        uint32_t sampleRate, int channelCount, int format, int bufferCount,

        AudioCallback cb, void *cookie)

{

    mCallback = cb;

    mCallbackCookie = cookie;

    // Check argument "bufferCount" against the mininum buffer count

    if (bufferCount < mMinBufferCount) {

        LOGD("bufferCount (%d) is too small and increased to %d", bufferCount, mMinBufferCount);

        bufferCount = mMinBufferCount;

    }

    LOGV("open(%u, %d, %d, %d, %d)", sampleRate, channelCount, format, bufferCount,mSessionId);

    if (mTrack) close();

    int afSampleRate;

    int afFrameCount;

    int frameCount;

    if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {

        return NO_INIT;

    }

    if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {

        return NO_INIT;

    }

    //获得帧数和采样率

    frameCount = (sampleRate*afFrameCount*bufferCount)/afSampleRate;

    AudioTrack *t;

    if (mCallback != NULL) {

        t = new AudioTrack(

                mStreamType,

                sampleRate,

                format,

                (channelCount == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,

                frameCount,

                0 /* flags */,

                CallbackWrapper,

                this,

                0,

                mSessionId);

    } else {

        t = new AudioTrack(

                mStreamType,

                sampleRate,

                format,

                (channelCount == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,

                frameCount,

                0,

                NULL,

                NULL,

                0,

                mSessionId);

    }

    if ((t == 0) || (t->initCheck() != NO_ERROR)) {

        LOGE("Unable to create audio track");

        delete t;

        return NO_INIT;

    }

    LOGV("setVolume");

    t->setVolume(mLeftVolume, mRightVolume);

    mMsecsPerFrame = 1.e3 / (float) sampleRate;

    mLatency = t->latency();

    mTrack = t;

    t->setAuxEffectSendLevel(mSendLevel);

    return t->attachAuxEffect(mAuxEffectId);;

}

 

音频输出的接口

ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)

{

    LOG_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");

    //LOGV("write(%p, %u)", buffer, size);

    if (mTrack) {

        ssize_t ret = mTrack->write(buffer, size);

        return ret;

    }

    return NO_INIT;

}

多媒体的JNI部分

   本地调用部分的代码路径为:

     Frameworks/base/media/jni

   主要文件有:

Android 23后改用stagefright

\frameworks\base\media\libstagefright

两者的处理机制不同

openCore 的处理流程如下: 

Stagefright部分的处理流程如下:

从上面可以看出

 1 OpenCore的 parser和 dec是分离的,各行其职,stagefright则是邦在一起作为一个独立的原子操作

 2 stagefright通过callback videoevent 来驱动数据输出, openCore是通过sink-node节点控制输出

 3 Opencoreparser/dec/sink是并行处理的 stagefright 是串行处理android_media_MediaPlayer.cpp//媒体播放器

android_media_MediaRecorder.cpp//媒体记录器

android_media_MediaMetadataRetriever.cpp//媒体元信息工具

android_media_MediaScanner.cpp//媒体扫描器

   这部分内容最终编译成libmedia_jni.so,

设置surface作为视频输出和取景器预览的接口没有对java提供,而是在preapare()函数中直接从环境中得到并设置了。

 static void

android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)

{

    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);

    if (mp == NULL ) {

        jniThrowException(env, "java/lang/IllegalStateException", NULL);

        return;

    }

    setVideoSurface(mp, env, thiz);//调用mediaplayer函数作视频输出设置

    process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );

}

static void

android_media_MediaRecorder_prepare(JNIEnv *env, jobject thiz)

{

    LOGV("prepare");

    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);

    jobject surface = env->GetObjectField(thiz, fields.surface);

    if (surface != NULL) {

        const sp<Surface> native_surface = get_surface(env, surface);

        // The application may misbehave and

        // the preview surface becomes unavailable

        if (native_surface.get() == 0) {

            LOGE("Application lost the surface");

            jniThrowException(env, "java/io/IOException", "invalid preview surface");

            return;

        }

        LOGI("prepare: surface=%p (identity=%d)", native_surface.get(), native_surface->getIdentity());

       //调用mediaplayer函数作视频输出设置

        if (process_media_recorder_call(env, mr->setPreviewSurface(native_surface), "java/lang/RuntimeException", "setPreviewSurface failed.")) {

            return;

        }

    }

    process_media_recorder_call(env, mr->prepare(), "java/io/IOException", "prepare failed.");

}

  多媒体部分的java部分代码

  Java框架类的路径为:

  frameworks\base\media\java\android\media

  主要文介绍;: 

  MediaFile.java 文件提供了媒体文件的文件类型,

  MediaPlayer  MediaRecorder  MediaMetadataRecorder 等类基本上和JNI层的内容一一对应

  MediaScanner在这里实现有客户端内容比较多

   

  其中MedisPlayer   中对视频输出和取景器预览的接口

    public void setDisplay(SurfaceHolder sh) {

        mSurfaceHolder = sh;

        if (sh != null) {

            mSurface = sh.getSurface();

        } else {

            mSurface = null;

        }

        _setVideoSurface();

        updateSurfaceScreenOn();

    }

   MediaRecorder中对视频输出和取景器预览的接口

  public void setPreviewDisplay(Surface sv) {

        mSurface = sv;

    }

 

Java框架层没有直接使用传递参数的方式,而是使用了保存在环境中再传递的方式

Android.widgetVideoView是一个UI元素

代码路径为:frameworks\base\core\java\android\widget

   使用该类,可以不用再调用MediaPlayer,节省了一些中间环节

public class VideoView extends SurfaceView implements MediaPlayerControl {

 public void setVideoPath(String path) { }//设置源文件路径

 public void setVideoURI(Uri uri) {}//设置视频的URL

 public void start() {}//开始播放

 public void stopPlayback() {}//停止播放

public void pause() { }//暂停播放

 public void seekTo(int msec) {}//更改播放位置

多媒体实现的核心部分  OpenCore

多媒体系统框架PacketVideo的开源版本OpenCoreandroid 多媒体本地实现在的核心

它为android提供的引警如下

 PVPlayer 媒体播放器的功能  音频和视频的回放功能
      PVAuthor 媒体记录器功能   音频和视频的录制

OpenCore的层次结构

  自上而下分为

OSCL  (operation system compatibility  library ,操作系统兼容库类似一个基础的c++

PVMF   (packetVideo  Multimedia  Framework  多媒体框架)  packetVideo 的基本框架例如nodea基类,输入输出的抽象类

文件格式处理,   文件解析(parser)和组成(composer)两个部分

各种Node,   是packetVideo 中的基本功能模块

播放器(Player Engine) 播放器引擎

记录器 (author Engine) 媒体记录器引擎

注: 在openCore2.X之后, 开始提供了2-way engine 两路引擎  用于构建视频电话

在使用OpenCoreSDK时,需要在应用层实现一个适配器

 PVPlaytrPVAuthor就是基于OpenCore的下层功能和接口构建军的应用层的库

android 系统中  OpenCore的代码路径为:externam/opencore/

 Stagefright整体框图:

Android froyo版本对多媒体引擎作了变动.新添加了stagefright框架,但并没有完全抛弃opencore 

主要是作了一个omx ,仅是对opencoreomx-component部分作了引用,,它在android 系统中作为

共享库(libstagefright.so)存在,  其中的module--awesomePlayer用来播放video/audio

 Awesomeplayer 提供的API可以供上次的应用(java/JNI)来调用

 StageFrigtht数据流封装

 1 MediaExtractor.cpp根据数据源DataSource生成MediaExtractor 

    具体实现是通过调用(代码路径为;frameworks\base\media\libstagefright)

    sp<MediaExtractor> MediaExtractor::Create( const sp<DataSource> &source, const char *mime) {}

    通过DateSourcesource->sniff(&tmp, &confidence, &meta)来探测数据类型

 2  AwesomePlayer.cpp把音视频轨道分离,生成mVideoTrack MediaSource 

    部分代码如下:

 if (!haveVideo && !strncasecmp(mime, "video/", 6)) {

            setVideoSource(extractor->getTrack(i));

            haveVideo = true;

        } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {

            setAudioSource(extractor->getTrack(i));

            haveAudio = true;

            if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {

                // Only do this for vorbis audio, none of the other audio

                // formats even support this ringtone specific hack and

                // retrieving the metadata on some extractors may turn out

                // to be very expensive.

                sp<MetaData> fileMeta = extractor->getMetaData();

                int32_t loop;

                if (fileMeta != NULL

                        && fileMeta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {

                    mFlags |= AUTO_LOOPING;

                }

            }

        }

  3 得到的两个mediaSource  只具有parser功能,,没有decode功能还需要对两个MediaSource做进一步的包装

 mAudioSource = OMXCodec::Create(

                mClient.interface(), mAudioTrack->getFormat(),

                false, // createEncoder

                mAudioTrack);

 mVideoSource = OMXCodec::Create(

            mClient.interface(), mVideoTrack->getFormat(),

            false, // createEncoder

            mVideoTrack,

            NULL, flags);

当调用mediaSource.start() 方法后就会开始从数据源获取数据并解析,等到缓冲区满后就停止

awesomePlayer就可以调用mediaSourceread方法读取解码后的数据

对于mVideoSource来说,   读取数据mVideoource->read(&mVideoBuffer,&options) 交给显示模块进行渲染,  mVideoRenderer->render(mVideoBufer) 

 4 stageFrightdecode

经过流的封装得到两个MediaSource ,其实是两个OMXCodec,

AwesomePlayermAudioPlayer都是从mediaSource中得到数据进行播放最终需要渲染的原始视频数据,也就是说OMXCodec中得到的是原始数据

部分代码如下:

sp<MediaSource> OMXCodec::Create(

        const sp<IOMX> &omx, //OMXNodeInstance对象的实例

        const sp<MetaData> &meta, bool createEncoder, //MediaSource.getFormat获取得到,

                                    //他的对象成员是一个keyedVector<uint32_t,typed_data>

                                    //里面存放的是代表mediaSource格式信息的键值对

        const sp<MediaSource> &source,  //mediaExtractor

        const char *matchComponentName, //指定一种codec用于生成omxcodec

        uint32_t flags) {

              //首先调用findMatchingCodecs()方法,找到对应的Codec, 

              findMatchingCodecs(

            mime, createEncoder, matchComponentName, flags, &matchingCodecs);

            //找到以后为当前的IOMX分配并注册监听事件,

             status_t err = omx->allocateNode(componentName, observer, &node);

            //这样就得到了OMXCodec

             sp<OMXCodec> codec = new OMXCodec(

                    omx, node, quirks,

                    createEncoder, mime, componentName,

                    source);

         }

 

    在AwesomePlayer中得到这个OMXCodec后,首先调用mVideoSource->start()进行初始化,主要有两件事

1  openMAX发送命令   

 err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);

2 err = allocateBuffers(); 分配两个缓冲区    ,freeBuffersOnPort()   分别用于输入和输出

awesomePlayer开始播放以后,通过mVideoSource->read(&mVideoBuffer,&options) 读取数据

 OMXCodec.read分两部来实现数据读取,

  1 通过调用draininputBuffers()mPortBuffers[kPortindexOutput]进行填充,这一步完成parse

     由OpenMAX从数据源把demux后的数据读取到输入缓,作为OpenMAX的输入

  2 通过fillOutputBuffers()mPortBuffers[kPortIndexInput]进行填充这一步完成decode,

     由OpenMAX对输入缓冲区的数据进行解码

  3 AwesomePlayer通过mVideoRenderer->reder()对经过parsedecode处理的数据进行渲染

 mVideoRenderer = new AwesomeLocalRenderer(

            false,  // previewOnly

            component,

            (OMX_COLOR_FORMATTYPE)format,

            mISurface,

            mVideoWidth, mVideoHeight,

            decodedWidth, decodedHeight, rotationDegrees);

StageFright的处理流程

AudioplayerawesomePlayer的成员,audioplayer通过callback来驱动数据的获取,

 Awesomeplayer则是通过videoevent来驱动,数据获取由mSource->Read()来完成,

 Read内部将parsetdecod 在一起

  两者进行同步部分  audio完全是callback驱动数据流,

                    Video部分在onvideoEvent会读取audio的时间戳,是传统的AV时间戳同步

AwesomePlayerVideo主要有以下几个成员

 mVideoSource(解码视频)

 mVideoTeack(从媒体文件中读取视频数据)

 mVideoRenderer(对解码好的视频进行格式转换,android 使用的格式为 RGB565) 

 mlSurface(重绘图层)

 mQueue(event事件对列)

 

 Audio部分的抽像流程如下:

    设置mUrl路径 

    启动mQueue,创建一个线程threadEntry(timedEventQueue,这个线程就是event调度器)

    打开mUrl指定文件头部,根据不同类型选择不同的分离器(例如:MPEG4Extractor

    使用分离器(MPEG4ExtractorMP4进行音视频轨道的分离,返回MPEG4Source类型的视频轨道给mVideoTrack

    根据mVideoTrack 中的编码类型来选择解码器, avc的编码类型会选择AVCDecoder,并返回给mVideoSource并设置mVideoSource中的mSourcemVideoTrack

   插入到onVideoEventqueue,开始解码播放

   通过mVideoSource对象来读取解析好的视频buffer,如果解析好的buffer还没到AV时间戳同步的时刻,则推迟到下一轮操作,

 mVideoRenderer为空,则进行初始化(如果不使用,OMX会将mVideoRenderer设置为AwesomeLocalRenderer)

 通过mVideoRenderer对象将解析好的视频buffer转换成RGB565格式,并发给display模块进行图像绘制

onVideoEvent重新插入event调度器来循环   

数据源到最终解码后的流程如下

   URI,FD------->DataSource---------->MediaExtractor------------>mVideoTrack mAudioTrack(音视频数据流)--------------->mVideoSource   mAudioSource(音视频解码器)

   注: URI可以为;http://    rtsp://   

      FD是本地文件描述符

打开log日志

代码标记Log

     依据第4》项StageFright描述的Vide视频播放流程,作Log标记跟踪视频DATA获取、CODEC过程。从AwesomePlayer.cpp中方法着手,步骤如下:

 n    在修改的/mydroid/frameworks/base/media/libstagefrigh/下,用mm编译,并调试直到生成相应的.so文件。注:允许单模块编译时,需事先在/mydroid下允许. ./build/envsetup.sh文件。

 n    在/mydroid/目录下make进行整体编译,生成system.img文件。说明:先单模块编译,后再整体编译的好处是,可以缩短调试编译的时间。

 n    将system.img文件copy/android-sdk-linux/platforms/android-8/下。注意:事先备份原有的system.img

 n    带sdcard启动模拟器,在/android-sdk-linux/tools/下运行./adb shell文件,再运行logcat

 n    打开Gallery选择视频文件运行,并同步查看log