MediaPlayer 视频播放

来源:互联网 发布:广州淘宝培训 编辑:程序博客网 时间:2024/04/28 14:40

 我参考的是android 源码中播放资源的对象VideoView.java, VideoView对象中实现视频播放最重要的对象是MediaPlayer 对象。

VideoView 连接视频资源的时候, 首先需要初始化一个MediaPlayer对象。

 

    public void setVideoURI(Uri uri, Map<String, String> headers) {        mUri = uri;        mHeaders = headers;        mSeekWhenPrepared = 0;        openVideo();        requestLayout();        invalidate();    }


在openVideo 的处理中,初始化mediaplayer, 并播放视频。

mMediaPlayer = new MediaPlayer();......mMediaPlayer.setSubtitleAnchor(controller, this);......mMediaPlayer.setOnPreparedListener(mPreparedListener);mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);mMediaPlayer.setOnCompletionListener(mCompletionListener);mMediaPlayer.setOnErrorListener(mErrorListener);mMediaPlayer.setOnInfoListener(mInfoListener);mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);mCurrentBufferPercentage = 0;mMediaPlayer.setDataSource(mContext, mUri, mHeaders);mMediaPlayer.setDisplay(mSurfaceHolder);mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);mMediaPlayer.setScreenOnWhilePlaying(true);mMediaPlayer.prepareAsync();

 

一 MeidiaPlayer 初始化

MediaPlayer 对象以JNI 的方式与 库函数进行交互, MediaPlayer 对象的native 方法在android_media_MediaPlayer.cpp中实现。

以下是MediaPlayer.java 中初始化对象的部分。

    static {        System.loadLibrary("media_jni");        native_init();    }
    public MediaPlayer() {        super(new AudioAttributes.Builder().build());        Looper looper;        if ((looper = Looper.myLooper()) != null) {            mEventHandler = new EventHandler(this, looper);        } else if ((looper = Looper.getMainLooper()) != null) {            mEventHandler = new EventHandler(this, looper);        } else {            mEventHandler = null;        }        mTimeProvider = new TimeProvider(this);        mOpenSubtitleSources = new Vector<InputStream>();        /* Native setup requires a weak reference to our object.         * It's easier to create it here than in C++.         */        native_setup(new WeakReference<MediaPlayer>(this));    }
   1) 静态代码块加载media_jni 库, 它会直接调用android_media_MediaPlayer.cpp中init() 方法.

   2) MediaPlayer 构造函数 初始化一个Handler 接收底层库函数返回的消息。

      然后调用android_media_MediaPlayer.cpp的android_media_MediaPlayer_native_setup方法。


MediaPlayer 和android_media_MediaPlayer.cpp 函数的对应关系如下。

JNI 的介绍请参考我的博文“JNI 与JNIEvn”(http://blog.csdn.net/shizhonghuo19870328/article/details/52402526)

static const JNINativeMethod gMethods[] = {    {        "nativeSetDataSource",        "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"        "[Ljava/lang/String;)V",        (void *)android_media_MediaPlayer_setDataSourceAndHeaders    },    {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},    {"_setDataSource",      "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },    {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},    {"_prepare",            "()V",                              (void *)android_media_MediaPlayer_prepare},    {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},    {"_start",              "()V",                              (void *)android_media_MediaPlayer_start},    {"_stop",               "()V",                              (void *)android_media_MediaPlayer_stop},    {"getVideoWidth",       "()I",                              (void *)android_media_MediaPlayer_getVideoWidth},    {"getVideoHeight",      "()I",                              (void *)android_media_MediaPlayer_getVideoHeight},    {"setPlaybackParams", "(Landroid/media/PlaybackParams;)V", (void *)android_media_MediaPlayer_setPlaybackParams},    {"getPlaybackParams", "()Landroid/media/PlaybackParams;", (void *)android_media_MediaPlayer_getPlaybackParams},    {"setSyncParams",     "(Landroid/media/SyncParams;)V",  (void *)android_media_MediaPlayer_setSyncParams},    {"getSyncParams",     "()Landroid/media/SyncParams;",   (void *)android_media_MediaPlayer_getSyncParams},    {"seekTo",              "(I)V",                             (void *)android_media_MediaPlayer_seekTo},    {"_pause",              "()V",                              (void *)android_media_MediaPlayer_pause},    {"isPlaying",           "()Z",                              (void *)android_media_MediaPlayer_isPlaying},    {"getCurrentPosition",  "()I",                              (void *)android_media_MediaPlayer_getCurrentPosition},    {"getDuration",         "()I",                              (void *)android_media_MediaPlayer_getDuration},    {"_release",            "()V",                              (void *)android_media_MediaPlayer_release},    {"_reset",              "()V",                              (void *)android_media_MediaPlayer_reset},    {"_setAudioStreamType", "(I)V",                             (void *)android_media_MediaPlayer_setAudioStreamType},    {"_getAudioStreamType", "()I",                              (void *)android_media_MediaPlayer_getAudioStreamType},    {"setParameter",        "(ILandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_setParameter},    {"setLooping",          "(Z)V",                             (void *)android_media_MediaPlayer_setLooping},    {"isLooping",           "()Z",                              (void *)android_media_MediaPlayer_isLooping},    {"_setVolume",          "(FF)V",                            (void *)android_media_MediaPlayer_setVolume},    {"native_invoke",       "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},    {"native_setMetadataFilter", "(Landroid/os/Parcel;)I",      (void *)android_media_MediaPlayer_setMetadataFilter},    {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_getMetadata},    {"native_init",         "()V",                              (void *)android_media_MediaPlayer_native_init},    {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer_native_setup},    {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer_native_finalize},    {"getAudioSessionId",   "()I",                              (void *)android_media_MediaPlayer_get_audio_session_id},    {"setAudioSessionId",   "(I)V",                             (void *)android_media_MediaPlayer_set_audio_session_id},    {"_setAuxEffectSendLevel", "(F)V",                          (void *)android_media_MediaPlayer_setAuxEffectSendLevel},    {"attachAuxEffect",     "(I)V",                             (void *)android_media_MediaPlayer_attachAuxEffect},    {"native_pullBatteryData", "(Landroid/os/Parcel;)I",        (void *)android_media_MediaPlayer_pullBatteryData},    {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I",  (void *)android_media_MediaPlayer_setRetransmitEndpoint},    {"setNextMediaPlayer",  "(Landroid/media/MediaPlayer;)V",   (void *)android_media_MediaPlayer_setNextMediaPlayer},};

   1.  android_media_MediaPlayer_native_init(JNIEnv *env)

    此方法主要是获得一些C库中需要用到的java 层的参数和方法句柄。

     如:    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");

   2.  android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)

     此方法功能是工厂模式建立指针sp<MediaPlayer>指向MediaPlayer.cpp,android_media_MediaPlayer.cpp的多媒体控制方法都是调用sp<MediaPlayer>实现。


二  获得服务,设置播放资源。

    以 android_media_MediaPlayer_setDataSourceFD 为例。 此方法也是调用MediaPlayer.cpp 的setDataSource 方法,如下所以:

status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length){    ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);    status_t err = UNKNOWN_ERROR;    const sp<IMediaPlayerService> service(getMediaPlayerService());    if (service != 0) {        sp<IMediaPlayer> player(service->create(this, mAudioSessionId));        if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||            (NO_ERROR != player->setDataSource(fd, offset, length))) {            player.clear();        }        err = attachNewPlayer(player);    }    return err;}
IMediaDeathNotifier::getMediaPlayerService(){    ALOGV("getMediaPlayerService");    Mutex::Autolock _l(sServiceLock);    if (sMediaPlayerService == 0) {        sp<IServiceManager> sm = defaultServiceManager();        sp<IBinder> binder;        do {            binder = sm->getService(String16("media.player"));            if (binder != 0) {                break;            }            ALOGW("Media player service not published, waiting...");            usleep(500000); // 0.5 s        } while (true);        if (sDeathNotifier == NULL) {            sDeathNotifier = new DeathNotifier();        }        binder->linkToDeath(sDeathNotifier);        sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);    }    ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");    return sMediaPlayerService;}

  1. 用Bind 方法获得进程的MediaPlayerService 服务(MediaPlayerService.cpp)

      请参考我的文章“Bind实现进程间通信”(http://blog.csdn.net/shizhonghuo19870328/article/details/53128133)。

       首先获得 用以下语句从BinderDriver获得IBinder 对象,

       binder = sm->getService(String16("media.player"));

       然后将IBinder  转化为MediaPlayerService。

template<typename INTERFACE>inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj){    return INTERFACE::asInterface(obj);}

以下代码向BinderDriver 注册 MediaPlayerService 服务 的过程。

void MediaPlayerService::instantiate() {    defaultServiceManager()->addService(            String16("media.player"), new MediaPlayerService());}

以上代码是向BinderDriver 注册 MediaPlayerService 服务 的过程。


2. 建立本地播放器

   调用MediaPlayerService 去建立本地播放器。 attachNewPlayer(player)方法将建立的本地播放器保存。

  

sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,        audio_session_t audioSessionId){    pid_t pid = IPCThreadState::self()->getCallingPid();    int32_t connId = android_atomic_inc(&mNextConnId);    sp<Client> c = new Client(            this, pid, connId, client, audioSessionId,            IPCThreadState::self()->getCallingUid());    ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,         IPCThreadState::self()->getCallingUid());    wp<Client> w = c;    {        Mutex::Autolock lock(mLock);        mClients.add(w);    }    return c;}
看一下client 的声明,

    class Client : public BnMediaPlayer {        // IMediaPlayer interface        virtual void            disconnect();        virtual status_t        setVideoSurfaceTexture(                                        const sp<IGraphicBufferProducer>& bufferProducer);        virtual status_t        prepareAsync();        virtual status_t        start();        virtual status_t        stop();        virtual status_t        pause();        virtual status_t        isPlaying(bool* state);        virtual status_t        setPlaybackSettings(const AudioPlaybackRate& rate);        virtual status_t        getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */);        virtual status_t        setSyncSettings(const AVSyncSettings& rate, float videoFpsHint);        virtual status_t        getSyncSettings(AVSyncSettings* rate /* nonnull */,                                                float* videoFps /* nonnull */);        virtual status_t        seekTo(int msec);        virtual status_t        getCurrentPosition(int* msec);        virtual status_t        getDuration(int* msec);        virtual status_t        reset();        virtual status_t        setAudioStreamType(audio_stream_type_t type);        virtual status_t        setLooping(int loop);        virtual status_t        setVolume(float leftVolume, float rightVolume);        virtual status_t        invoke(const Parcel& request, Parcel *reply);        virtual status_t        setMetadataFilter(const Parcel& filter);        virtual status_t        getMetadata(bool update_only,                                            bool apply_filter,                                            Parcel *reply);        virtual status_t        setAuxEffectSendLevel(float level);        virtual status_t        attachAuxEffect(int effectId);        virtual status_t        setParameter(int key, const Parcel &request);        virtual status_t        getParameter(int key, Parcel *reply);        virtual status_t        setRetransmitEndpoint(const struct sockaddr_in* endpoint);        virtual status_t        getRetransmitEndpoint(struct sockaddr_in* endpoint);        virtual status_t        setNextPlayer(const sp<IMediaPlayer>& player);        sp<MediaPlayerBase>     createPlayer(player_type playerType);        virtual status_t        setDataSource(                        const sp<IMediaHTTPService> &httpService,                        const char *url,                        const KeyedVector<String8, String8> *headers);        virtual status_t        setDataSource(int fd, int64_t offset, int64_t length);        virtual status_t        setDataSource(const sp<IStreamSource> &source);        virtual status_t        setDataSource(const sp<IDataSource> &source);        sp<MediaPlayerBase>     setDataSource_pre(player_type playerType);        void                    setDataSource_post(const sp<MediaPlayerBase>& p,                                                   status_t status);        static  void            notify(void* cookie, int msg,                                       int ext1, int ext2, const Parcel *obj);                pid_t           pid() const { return mPid; }        virtual status_t        dump(int fd, const Vector<String16>& args);                audio_session_t getAudioSessionId() { return mAudioSessionId; }
Client 继承了BnMediaPlayer,也就是本地播放器。同时Client 声明了所有的播放处理方法。

mediaplayer.cpp 中的播放处理方法调用的都是Client 中对应的方法。


三 设置播放资源

status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length){    ALOGV("setDataSource fd=%d, offset=%" PRId64 ", length=%" PRId64 "", fd, offset, length);    struct stat sb;    int ret = fstat(fd, &sb);    if (ret != 0) {        ALOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno));        return UNKNOWN_ERROR;    }    ALOGV("st_dev  = %" PRIu64 "", static_cast<uint64_t>(sb.st_dev));    ALOGV("st_mode = %u", sb.st_mode);    ALOGV("st_uid  = %lu", static_cast<unsigned long>(sb.st_uid));    ALOGV("st_gid  = %lu", static_cast<unsigned long>(sb.st_gid));    ALOGV("st_size = %" PRId64 "", sb.st_size);    if (offset >= sb.st_size) {        ALOGE("offset error");        return UNKNOWN_ERROR;    }    if (offset + length > sb.st_size) {        length = sb.st_size - offset;        ALOGV("calculated length = %" PRId64 "\n", length);    }    player_type playerType = MediaPlayerFactory::getPlayerType(this,                                                               fd,                                                               offset,                                                               length);    sp<MediaPlayerBase> p = setDataSource_pre(playerType);    if (p == NULL) {        return NO_INIT;    }    // now set data source    setDataSource_post(p, p->setDataSource(fd, offset, length));    return mStatus;}
其主体部分其实就是setDataSource_pre 和 setDataSource_post(p, p->setDataSource(fd, offset, length))。

1.  建立具体播放器

      设置播放资源之前需要根据资源类别建立具体的播放器。

sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(        player_type playerType){    ALOGV("player type = %d", playerType);    // create the right type of player    sp<MediaPlayerBase> p = createPlayer(playerType);    if (p == NULL) {        return p;    }    sp<IServiceManager> sm = defaultServiceManager();    sp<IBinder> binder = sm->getService(String16("media.extractor"));    mExtractorDeathListener = new ServiceDeathNotifier(binder, p, MEDIAEXTRACTOR_PROCESS_DEATH);    binder->linkToDeath(mExtractorDeathListener);    binder = sm->getService(String16("media.codec"));    mCodecDeathListener = new ServiceDeathNotifier(binder, p, MEDIACODEC_PROCESS_DEATH);    binder->linkToDeath(mCodecDeathListener);    if (!p->hardwareOutput()) {        Mutex::Autolock l(mLock);        mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),                mPid, mAudioAttributes);        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);    }    return p;}

sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType){    // determine if we have the right player type    sp<MediaPlayerBase> p = mPlayer;    if ((p != NULL) && (p->playerType() != playerType)) {        ALOGV("delete player");        p.clear();    }    if (p == NULL) {        p = MediaPlayerFactory::createPlayer(playerType, this, notify, mPid);    }    if (p != NULL) {        p->setUID(mUID);    }    return p;}

利用工厂模式建立具体播放器。 每一种playerType 对应一种MediaplayerFactory, 每一种MediaplayerFactory 建立一种MediaPlayer. 

class StagefrightPlayerFactory :    public MediaPlayerFactory::IFactory {  public:......    virtual sp<MediaPlayerBase> createPlayer() {        ALOGV(" create StagefrightPlayer");        return new StagefrightPlayer();    }......

这里我们用StagefrightPlayer为例。

2.  调用具体mediaPlayer  设置播放资源

    setDataSource_post(p, p->setDataSource(fd, offset, length));

其实从程序中可以看到,Client 是本地播放器的一个总的接口,所有播放处理方法都需要调用具体的播放器去完成。

所以client::setDataSource() 最后会调用StagefrightPlayer::setDataSource() 


从StagefrightPlayer 的构造器可以看到,StagefrightPlayer 中包含一个私有对象AwesomePlayer  *mplayer。 AwesomePlayer  是Stagefright 的核心。 大部分Stagefright Player的播放处理方法都要调用AwesomePlayer去实现。


StagefrightPlayer::StagefrightPlayer()    : mPlayer(new AwesomePlayer) {    ALOGV("StagefrightPlayer");    mPlayer->setListener(this);}
status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {    ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);    return mPlayer->setDataSource(dup(fd), offset, length);}
                                             
0 0