代码改变世界

Android MediaPlayer stream实现

2013-06-28 09:45  白居不易  阅读(2524)  评论(0编辑  收藏  举报

对于Ait类的Camera,使用已编码数据(H264)进行Preview.若使用原始的SurfaceTexture实现,需要扩展ANativeWindow支持的视频格式,也会涉及到OpenGL相关的内容,工作量巨大。

另一种方式是使用MediaPlayer实现,MediaPlayer支持三种源,我们应该要使用Stream方式,但也不确定,看一下service的底层实现,也就是与Hardcodec的对接。另外关注一下Buffer的传递过程。

Java Framework

  frameworks/base/media/java/android/media/MediaPlayer.java

JNI

  frameworks/base/media/jni/android_media_MediaPlayer.cpp

cpp Framework(libmedia.so):client

  frameworks/av/media/libmedia

include 

  frameworks/av/include/media

Service

  frameworks/av/media/libmediaplayerservice

1、APP@packages/apps/Launchertv/src/com/xxxx/Launcher/VideoMediaPlayer.java

对于一个简单的APP,例如Jervis的Launcher 播放窗口,app需要做的较为简单

mMediaPlayer = new MediaPlayer();
mMediaPlayer.reset();
mMediaPlayer.setDataSource(VIDEO_PATH + mFileName.get(mCurrentItem));// 针对与本地视频文件播放
mMediaPlayer.prepare(); 
mMediaPlayer.start();

 

2、实现

源码中有一个stream的参考,framework/av/cmds/stagefright/stream.cpp,但不可用,看了以下代码,没有mMediaPlayer.prepare的调用,且原代码中并非对框架中MediaPlayer的调用,而是自己申请service。

因此新建类MyClient

struct MyClient : RefBase {
    MyClient()
        : mEOS(false) {
        mp = new MediaPlayer();
        mp->setListener(new MyListener(this));
    }

    class MyListener: public MediaPlayerListener {
    public:
        MyListener(sp<MyClient> mc) {
        mc4listener = mc;
        }

        ~MyListener() {
        }


            virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) {
                Mutex::Autolock autoLock(mc4listener->mLock);
            
        
        fprintf(stderr, "leon msg=%d\n", msg);
                if (msg == MEDIA_ERROR || msg == MEDIA_PLAYBACK_COMPLETE) {
                    mc4listener->mEOS = true;
                    mc4listener->mCondition.signal();
                }
            }
        sp<MyClient> mc4listener;
    };

    void waitForEOS() {
        Mutex::Autolock autoLock(mLock);
        while (!mEOS) {
            mCondition.wait(mLock);
        }
    }

    sp<MediaPlayer> mp;
protected:
    virtual ~MyClient() {
    }

private:
    Mutex mLock;
    Condition mCondition;

    bool mEOS;

    DISALLOW_EVIL_CONSTRUCTORS(MyClient);
};

在构造函数中new 一个MediaPlayer类,并设置Listener,此Listener是Service想MediaPlayer的通知。

另外新建streamSource类,在本例中,是通过打开一个Ts文件,读取数据并通知service进行播放,他继承了BnStreamSource类,并实现了三个重要的虚函数

struct MyStreamSource : public BnStreamSource {
    // Object assumes ownership of fd.
    MyStreamSource(int fd);

    virtual void setListener(const sp<IStreamListener> &listener);
    virtual void setBuffers(const Vector<sp<IMemory> > &buffers);

    virtual void onBufferAvailable(size_t index);

protected:
    virtual ~MyStreamSource();

private:
    int mFd;
    off64_t mFileSize;
    uint64_t mNumPacketsSent;

    sp<IStreamListener> mListener;
    Vector<sp<IMemory> > mBuffers;

    DISALLOW_EVIL_CONSTRUCTORS(MyStreamSource);
};

 setListener:设置mListener,通过此listener可调用service的一些函数实现。例如下面的两个函数

void MyStreamSource::setListener(const sp<IStreamListener> &listener) {
    mListener = listener;
}

 

setBuffers:将server中申请的IMemory容器传递给StreamSource类,用于向其中写入ts数据。

void MyStreamSource::setBuffers(const Vector<sp<IMemory> > &buffers) {
    mBuffers = buffers;
}

 

onBufferAvailable(size_t index):向可用的内存块中写入数据,完成后通过IStreamListener通知server

void MyStreamSource::onBufferAvailable(size_t index) {
    //CHECK_LT(index, mBuffers.size());
    if (index<0 || index >=8 )
    {
        fprintf(stderr, "size=%d,\tindex=%u\n", mBuffers.size(), index);
        return;
    }
    sp<IMemory> mem = mBuffers.itemAt(index);

    ssize_t n = read(mFd, mem->pointer(), mem->size());
    if (n <= 0) {
        mListener->issueCommand(IStreamListener::EOS, false /* synchronous */);
    } else {
        mListener->queueBuffer(index, n);

        mNumPacketsSent += n / 188;
    }
}

 

3、CameraHD应用中的考虑

在Camera Service中将MyClient中的实现移植到startPreview中,获取到H264数据后,打包成TS流,通过MyStreamSource将数据写入MediaPlayerService的buffer中。

http://pan.baidu.com/share/link?shareid=3224742726&uk=2852507874

这是将H264数据打包为TS流的工程,备份