vitamio官方demo源码分析(1)——MediaPlayerDemo_Video.java分析


  332人阅读  评论(0)  收藏  举报
多媒体 android

最近在做一个视频监控项目的android客户端,要求用rtsp协议完成视频流的传输,但苦于找到不合适的库。之前考虑过用live555或ffmpeg,但涉及到jni调用,加之不熟悉函数调用顺序,开发难度和周期较长,遂作罢。于是乎,混迹于各大论坛寻找解决方案,经过一番苦苦寻觅,终于找到了一个比较满意的多媒体框架——vitamio。

vitamio作为一个国人开发的android多媒体开发框架,以支持格式多、操作简单、容易上手著称。vitamio也支持多种流媒体协议,如HTTP Streaming、rtsp、mms等等。国内也有很多播放器是基于该框架构建而成,用户基数较大。

在开始分析源码之前,我们先来分析一下mediaplayer的生命周期。

vitamio官方demo源码分析(1)——MediaPlayerDemo_Video.java分析_第1张图片

状态1:Idel(空闲)状态 当 mediaplayer创建或者执行reset()方法后处于这个状态。

      状态2:Initialized(已初始化)状态 当 调用mediaplayer的setDataResource()方法给mediaplayer设置播放的数据源后,mediaplayer会处于该状态。

      状态3:Prepared(准备就续)状态 设置完数据源后,调用mediaplayer的prepare()方法,让mediaplayer准备播放。值得一提的是,这里除了prepare()方法,还有prepareAsnyc()方法,此方法是异步方法,一般用于网络视频的缓冲。当缓冲完毕后,就会触发准备完毕的事件。我们要做的就是监听该事件(OnPreparedListener),当缓冲完成时,执行相应的操作。在此状态上,我们可以调用seekTo()方法定位视频,此方法不改变mediaplayer的状态;亦可调用stop()放弃视频播放,使mediaplayer处于Stopped状态。一般我们会在此状态上调用start()方法开始播放视频。

       状态4:Started(开始)状态 当处于Prepared状态、Paused状态和PlayebackCompeleted状态时,调用Started()方法即可进入该状态。在该状态中,mediaplayer开始播放视频,可以通过seekTo()方法和start()方法改变视频播放的进度,当Looping为真且播放完毕后,它会重新开始播放(即循环播放);否则播放完毕后,会触发事件并调用OnCompletionaListener.OnCompletion()方法,进行特定操作,并进入PlaybackCompleted状态。在此状态中,亦可调用pause()方法或者stop()方法让视频暂停或停止,此时mediaplayer分别处于Stopped和Paused状态。

      状态5:Stopped(停止)状态 当 mediaplayer处于Prepared 、Started、Paused、PlaybackCompleted状态时,调用stop()方法即可进入本状态。应特别注意的是,在本状态中,若想重新开始播放,不能直接调用start()方法,必须调用prepare()方法或prepareAsync()方法重新让mediaplayer处于Prepared状态方可调用start()方法播放视频。

      状态6:Paused(暂停)状态 当 mediaplayer处于Started状态是,调用pause()方法即可进入本状态。在本状态里,可直接调用start()方法使,mediaplayer回到Started状态,亦可调用stop()方法停止视频播放,让播放器处于停止态。

      状态7:PlaybackCompleted(播放完成)状态 当 mediaplayer播放完成且Looping为假时即可进入本状态。在本状态可调用start()方法使mediaplayer回到Started状态(注意此时是从头开始播放);亦可调用stop()方法使mediaplayer处于停止态,结束播放。

     状态8:Error(错误)状态 当 mediaplayer出现错误时处于此状态。

调用release()方法即可释放此mediaplayer对象。

至此,我们已经了解了mediaplayer的声明周期,下面开始分析代码:

1、布局文件

[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <io.vov.vitamio.widget.CenterLayout  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="match_parent"  
  10.         android:orientation="vertical" >  
  11.   
  12.         <SurfaceView  
  13.             android:id="@+id/surface"  
  14.             android:layout_width="320dp"  
  15.             android:layout_height="480dp"  
  16.             android:layout_gravity="center" >  
  17.         </SurfaceView>  
  18.     </io.vov.vitamio.widget.CenterLayout>  
  19.   
  20. </LinearLayout>  

这个布局文件非常简单,就是一个SurfaceView。其中它的宽和高是我手动指定的,官方给的demo中原本全都是wrap_content的,不过经过测试,当播放流媒体时,如果不手动指定的话,图像会很小。

2、代码

[java]  view plain copy
  1. /* 
  2.  * Copyright (C) 2013 yixia.com 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package io.vov.vitamio.demo;  
  18.   
  19. import io.vov.vitamio.LibsChecker;  
  20. import io.vov.vitamio.MediaPlayer;  
  21. import io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener;  
  22. import io.vov.vitamio.MediaPlayer.OnCompletionListener;  
  23. import io.vov.vitamio.MediaPlayer.OnPreparedListener;  
  24. import io.vov.vitamio.MediaPlayer.OnVideoSizeChangedListener;  
  25. import android.app.Activity;  
  26. import android.graphics.PixelFormat;  
  27. import android.media.AudioManager;  
  28. import android.os.Bundle;  
  29. import android.util.Log;  
  30. import android.view.SurfaceHolder;  
  31. import android.view.SurfaceView;  
  32. import android.widget.Toast;  
[java]  view plain copy
  1. /*  
[java]  view plain copy
  1. 注意这个activity的写法。它实现了很多接口,这样的话,我们在绑定事件监听时,就不用编写很多个内部监听类了,直接用this即可。  
[java]  view plain copy
  1. */  
  2.   
  3. public class MediaPlayerDemo_Video extends Activity implements OnBufferingUpdateListener, OnCompletionListener, OnPreparedListener, OnVideoSizeChangedListener, SurfaceHolder.Callback {  
  4.   
  5.     private static final String TAG = "MediaPlayerDemo";  
  6.     private int mVideoWidth;  
  7.     private int mVideoHeight;  
  8.     private MediaPlayer mMediaPlayer;  
  9.     private SurfaceView mPreview;  
  10.     private SurfaceHolder holder;  
  11.     private String path="rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov";//视频资源地址,是rtsp协议的流媒体  
  12.     private Bundle extras;  
  13.     private static final String MEDIA = "media";  
  14.     private static final int LOCAL_AUDIO = 1;  
  15.     private static final int STREAM_AUDIO = 2;  
  16.     private static final int RESOURCES_AUDIO = 3;  
  17.     private static final int LOCAL_VIDEO = 4;  
  18.     private static final int STREAM_VIDEO = 5;  
  19.     private boolean mIsVideoSizeKnown = false;  
  20.     private boolean mIsVideoReadyToBePlayed = false;  
  21.   
  22.     /** 
  23.      *  
  24.      * Called when the activity is first created. 
  25.      */  
  26.     @Override  
  27.     public void onCreate(Bundle icicle) {  
  28.         super.onCreate(icicle);  
  29.         if (!LibsChecker.checkVitamioLibs(this))//初始化库。若少了会报错!!  
  30.             return;  
  31.         setContentView(R.layout.mediaplayer_2);//加载布局文件  
  32.         mPreview = (SurfaceView) findViewById(R.id.surface);//查找组件  
[java]  view plain copy
  1.         holder = mPreview.getHolder();//获取此surfaceView的holder对象。此holder对象即为mediaplayer显示的地方。  
  2.         holder.addCallback(this);//设置回调。这里主要是<span style="font-family: Arial, Helvetica, sans-serif;">surfaceChanged、</span><span style="font-family: Arial, Helvetica, sans-serif;">surfaceDestroyed、</span><span style="font-family: Arial, Helvetica, sans-serif;">surfaceCreated三个方法。</span><span style="font-family: Arial, Helvetica, sans-serif;">  
  3. </span>     holder.setFormat(PixelFormat.RGBA_8888);   
  4.         extras = getIntent().getExtras();//获取数据  
  5.   
  6.     }  
  7.   
  8.     private void playVideo(Integer Media) {  
  9.         doCleanUp();  
  10.         try {  
  11.   
  12.             switch (Media) {  
  13.             case LOCAL_VIDEO:  
  14.                 /* 
  15.                  * TODO: Set the path variable to a local media file path. 
  16.                  */  
  17.                 path = "/storage/sdcard0/1.avi";  
  18.                 if (path == "") {  
  19.                     // Tell the user to provide a media file URL.  
  20.                     Toast.makeText(MediaPlayerDemo_Video.this"Please edit MediaPlayerDemo_Video Activity, " + "and set the path variable to your media file path." + " Your media file must be stored on sdcard.", Toast.LENGTH_LONG).show();  
  21.                     return;  
  22.                 }  
  23.                 break;  
  24.             case STREAM_VIDEO:  
  25.                 /* 
  26.                  * TODO: Set path variable to progressive streamable mp4 or 
  27.                  * 3gpp format URL. Http protocol should be used. 
  28.                  * Mediaplayer can only play "progressive streamable 
  29.                  * contents" which basically means: 1. the movie atom has to 
  30.                  * precede all the media data atoms. 2. The clip has to be 
  31.                  * reasonably interleaved. 
  32.                  *  
  33.                  */  
  34.                 //path = "";  
  35.                 if (path == "") {  
  36.                     // Tell the user to provide a media file URL.  
  37.                     Toast.makeText(MediaPlayerDemo_Video.this"Please edit MediaPlayerDemo_Video Activity," + " and set the path variable to your media file URL.", Toast.LENGTH_LONG).show();  
  38.                     return;  
  39.                 }  
  40.   
  41.                 break;  
  42.   
  43.             }  
  44.   
  45.             // Create a new media player and set the listeners  
  46.             mMediaPlayer = new MediaPlayer(this);//初始化mediaplayer。  
  47.             mMediaPlayer.setDataSource("rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov");//设置数据源  
  48.             mMediaPlayer.setDisplay(holder);//设置显示  
  49.             mMediaPlayer.prepare();//准备(这里用prepareAsync()应该会更好。。)  
  50.             mMediaPlayer.setOnBufferingUpdateListener(this);//设置缓冲监听  
  51.             mMediaPlayer.setOnCompletionListener(this);//设置播放完毕监听  
  52.             mMediaPlayer.setOnPreparedListener(this);//设置准备完毕监听  
  53.             mMediaPlayer.setOnVideoSizeChangedListener(this);//设置显示大小改变监听  
  54.             //mMediaPlayer.getMetadata();//在播放网络流媒体时。<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">若加上此句,会产生I/O Error,不清楚为什么......另外个人认为此句没有意义。</span>  

[java]  view plain copy
  1. <span style="font-size:14px;">            
  2.             setVolumeControlStream(AudioManager.STREAM_MUSIC);  
  3.   
  4.         } catch (Exception e) {  
  5.             Log.e(TAG, "error: " + e.getMessage(), e);  
  6.         }  
  7.     }  
  8.   
  9.     public void onBufferingUpdate(MediaPlayer arg0, int percent) {//缓冲监听的实现  
  10.         Log.d(TAG, "onBufferingUpdate percent:" + percent);  
  11.   
  12.     }  
  13.   
  14.     public void onCompletion(MediaPlayer arg0) {//播放完毕监听的实现  
  15.         Log.d(TAG, "onCompletion called");  
  16.     }  
  17.   
  18.     public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {//</span><span style="font-family: Arial, Helvetica, sans-serif;"><span style="font-size:12px;">显示大小改变监听的实现</span></span><span style="font-size:14px;">  
  19.         Log.v(TAG, "onVideoSizeChanged called");  
  20.         if (width == 0 || height == 0) {  
  21.             Log.e(TAG, "invalid video width(" + width + ") or height(" + height + ")");  
  22.             return;  
  23.         }  
  24.         mIsVideoSizeKnown = true;  
  25.         mVideoWidth = width;  
  26.         mVideoHeight = height;  
  27.         if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {  
  28.             startVideoPlayback();  
  29.         }  
  30.     }  
  31.   
  32.     public void onPrepared(MediaPlayer mediaplayer) {//准备完毕监听的实现  
  33.         Log.d(TAG, "onPrepared called");  
  34.         mIsVideoReadyToBePlayed = true;  
  35.         if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {  
  36.             startVideoPlayback();  
  37.         }  
  38.     }  
  39.   
  40.     public void surfaceChanged(SurfaceHolder surfaceholder, int i, int j, int k) {  
  41.         Log.d(TAG, "surfaceChanged called");  
  42.   
  43.     }  
  44.   
  45.     public void surfaceDestroyed(SurfaceHolder surfaceholder) {  
  46.         Log.d(TAG, "surfaceDestroyed called");  
  47.     }  
  48.   
  49.     public void surfaceCreated(SurfaceHolder holder) {  
  50.         Log.d(TAG, "surfaceCreated called");  
  51.         playVideo(extras.getInt(MEDIA));  
  52.   
  53.     }  
  54.   
  55.     @Override  
  56.     protected void onPause() {//当此activity处于pause状态时,停止播放,销毁mediaplayer  
  57.         super.onPause();  
  58.         releaseMediaPlayer();  
  59.         doCleanUp();  
  60.     }  
  61.   
  62.     @Override  
  63.     protected void onDestroy() {//当此activity即将销毁时,销毁mediaplayer  
  64.         super.onDestroy();  
  65.         releaseMediaPlayer();  
  66.         doCleanUp();  
  67.     }  
  68.   
  69.     private void releaseMediaPlayer() {  
  70.         if (mMediaPlayer != null) {  
  71.             mMediaPlayer.release();  
  72.             mMediaPlayer = null;  
  73.         }  
  74.     }  
  75.   
  76.     private void doCleanUp() {  
  77.         mVideoWidth = 0;  
  78.         mVideoHeight = 0;  
  79.         mIsVideoReadyToBePlayed = false;  
  80.         mIsVideoSizeKnown = false;  
  81.     }  
  82.   
  83.     private void startVideoPlayback() {  
  84.         Log.v(TAG, "startVideoPlayback");  
  85.         holder.setFixedSize(mVideoWidth, mVideoHeight);  
  86.         mMediaPlayer.start();  
  87.     }  
  88. }  
  89. </span>  
 现在初步实验成果,网络流媒体的播放也基本达到音画同步的要求,下一步就是用vitamio打造属于自己的播放器。

你可能感兴趣的:(vitamio官方demo源码分析(1)——MediaPlayerDemo_Video.java分析)