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

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

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

状态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、布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><io.vov.vitamio.widget.CenterLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><SurfaceViewandroid:id="@+id/surface"android:layout_width="320dp"android:layout_height="480dp"android:layout_gravity="center" ></SurfaceView></io.vov.vitamio.widget.CenterLayout></LinearLayout>

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

2、代码

/** Copyright (C) 2013 yixia.com** 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.*/package io.vov.vitamio.demo;import io.vov.vitamio.LibsChecker;
import io.vov.vitamio.MediaPlayer;
import io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener;
import io.vov.vitamio.MediaPlayer.OnCompletionListener;
import io.vov.vitamio.MediaPlayer.OnPreparedListener;
import io.vov.vitamio.MediaPlayer.OnVideoSizeChangedListener;
import android.app.Activity;
import android.graphics.PixelFormat;
import android.media.AudioManager;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;
/*
注意这个activity的写法。它实现了很多接口,这样的话,我们在绑定事件监听时,就不用编写很多个内部监听类了,直接用this即可。
*/public class MediaPlayerDemo_Video extends Activity implements OnBufferingUpdateListener, OnCompletionListener, OnPreparedListener, OnVideoSizeChangedListener, SurfaceHolder.Callback {private static final String TAG = "MediaPlayerDemo";private int mVideoWidth;private int mVideoHeight;private MediaPlayer mMediaPlayer;private SurfaceView mPreview;private SurfaceHolder holder;private String path="rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov";//视频资源地址,是rtsp协议的流媒体private Bundle extras;private static final String MEDIA = "media";private static final int LOCAL_AUDIO = 1;private static final int STREAM_AUDIO = 2;private static final int RESOURCES_AUDIO = 3;private static final int LOCAL_VIDEO = 4;private static final int STREAM_VIDEO = 5;private boolean mIsVideoSizeKnown = false;private boolean mIsVideoReadyToBePlayed = false;/*** * Called when the activity is first created.*/@Overridepublic void onCreate(Bundle icicle) {super.onCreate(icicle);if (!LibsChecker.checkVitamioLibs(this))//初始化库。若少了会报错!!return;setContentView(R.layout.mediaplayer_2);//加载布局文件mPreview = (SurfaceView) findViewById(R.id.surface);//查找组件
holder = mPreview.getHolder();//获取此surfaceView的holder对象。此holder对象即为mediaplayer显示的地方。holder.addCallback(this);//设置回调。这里主要是surfaceChanged、surfaceDestroyed、surfaceCreated三个方法。
        holder.setFormat(PixelFormat.RGBA_8888); extras = getIntent().getExtras();//获取数据}private void playVideo(Integer Media) {doCleanUp();try {switch (Media) {case LOCAL_VIDEO:/** TODO: Set the path variable to a local media file path.*/path = "/storage/sdcard0/1.avi";if (path == "") {// Tell the user to provide a media file URL.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();return;}break;case STREAM_VIDEO:/** TODO: Set path variable to progressive streamable mp4 or* 3gpp format URL. Http protocol should be used.* Mediaplayer can only play "progressive streamable* contents" which basically means: 1. the movie atom has to* precede all the media data atoms. 2. The clip has to be* reasonably interleaved.* *///path = "";if (path == "") {// Tell the user to provide a media file URL.Toast.makeText(MediaPlayerDemo_Video.this, "Please edit MediaPlayerDemo_Video Activity," + " and set the path variable to your media file URL.", Toast.LENGTH_LONG).show();return;}break;}// Create a new media player and set the listenersmMediaPlayer = new MediaPlayer(this);//初始化mediaplayer。mMediaPlayer.setDataSource("rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov");//设置数据源mMediaPlayer.setDisplay(holder);//设置显示mMediaPlayer.prepare();//准备(这里用prepareAsync()应该会更好。。)mMediaPlayer.setOnBufferingUpdateListener(this);//设置缓冲监听mMediaPlayer.setOnCompletionListener(this);//设置播放完毕监听mMediaPlayer.setOnPreparedListener(this);//设置准备完毕监听mMediaPlayer.setOnVideoSizeChangedListener(this);//设置显示大小改变监听//mMediaPlayer.getMetadata();//在播放网络流媒体时。若加上此句,会产生I/O Error,不清楚为什么......另外个人认为此句没有意义。
         setVolumeControlStream(AudioManager.STREAM_MUSIC);} catch (Exception e) {Log.e(TAG, "error: " + e.getMessage(), e);}}public void onBufferingUpdate(MediaPlayer arg0, int percent) {//缓冲监听的实现Log.d(TAG, "onBufferingUpdate percent:" + percent);}public void onCompletion(MediaPlayer arg0) {//播放完毕监听的实现Log.d(TAG, "onCompletion called");}public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {//显示大小改变监听的实现Log.v(TAG, "onVideoSizeChanged called");if (width == 0 || height == 0) {Log.e(TAG, "invalid video width(" + width + ") or height(" + height + ")");return;}mIsVideoSizeKnown = true;mVideoWidth = width;mVideoHeight = height;if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {startVideoPlayback();}}public void onPrepared(MediaPlayer mediaplayer) {//准备完毕监听的实现Log.d(TAG, "onPrepared called");mIsVideoReadyToBePlayed = true;if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {startVideoPlayback();}}public void surfaceChanged(SurfaceHolder surfaceholder, int i, int j, int k) {Log.d(TAG, "surfaceChanged called");}public void surfaceDestroyed(SurfaceHolder surfaceholder) {Log.d(TAG, "surfaceDestroyed called");}public void surfaceCreated(SurfaceHolder holder) {Log.d(TAG, "surfaceCreated called");playVideo(extras.getInt(MEDIA));}@Overrideprotected void onPause() {//当此activity处于pause状态时,停止播放,销毁mediaplayersuper.onPause();releaseMediaPlayer();doCleanUp();}@Overrideprotected void onDestroy() {//当此activity即将销毁时,销毁mediaplayersuper.onDestroy();releaseMediaPlayer();doCleanUp();}private void releaseMediaPlayer() {if (mMediaPlayer != null) {mMediaPlayer.release();mMediaPlayer = null;}}private void doCleanUp() {mVideoWidth = 0;mVideoHeight = 0;mIsVideoReadyToBePlayed = false;mIsVideoSizeKnown = false;}private void startVideoPlayback() {Log.v(TAG, "startVideoPlayback");holder.setFixedSize(mVideoWidth, mVideoHeight);mMediaPlayer.start();}
}

现在初步实验成果,网络流媒体的播放也基本达到音画同步的要求,下一步就是用vitamio打造属于自己的播放器。

欢迎转载,转载时请保留出处和原文链接,尊重知识产权,从小事做起

转载于:https://my.oschina.net/u/1774064/blog/282841

vitamio官方demo源码分析(1)——MediaPlayerDemo_Video.java分析相关推荐

  1. Webrtc从理论到实践七: 官方demo源码走读(peerconnection_server)

    系列文章目录 Webrtc从理论到实践一:初识 Webrtc从理论到实践二: 架构 Webrtc从理论到实践三: 角色 Webrtc从理论到实践四: 通信 Webrtc从理论到实践五: 编译webrt ...

  2. 老李推荐:第5章5节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 获取系统服务引用 1...

    老李推荐:第5章5节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 获取系统服务引用 上一节我们描述了monkey的命令处理入口函数run是如何调用optionPro ...

  3. winserver的consul部署实践与.net core客户端使用(附demo源码)

    前言 随着微服务兴起,服务的管理显得极其重要.都知道微服务就是"拆",把臃肿的单块应用,拆分成多个轻量级的服务,每个服务可以在短周期内重构.迭代.交付.随着微服务的数量增多,因量变 ...

  4. vscode插件开发实践与demo源码

    vscode插件开发实践与demo源码 写在前面 工欲善其事必先利其器.vscode作为优秀的开发工具,给我的日常开发工作提供了极大的便利.其拓展机制更是如此. 但是,最近在做年度专业线任务时,有需要 ...

  5. 老李推荐:第6章1节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览 1...

    老李推荐:第6章1节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览 在上一章中我们有简要的介绍了事件源是怎么一回事,但是并没有进行详细的描述.那么往下的这几个 ...

  6. Api demo源码学习(8)--App/Activity/QuickContactsDemo --获取系统联系人信息

    本节通过Content Provider机制获取系统中的联系人信息,注意这个Anctivity直接继承的是ListActivity,所以不再需要setContentView函数来加载布局文件了(我自己 ...

  7. 老李推荐:第6章6节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令队列...

    老李推荐:第6章6节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-命令队列 事件源在获得字串命令并把它翻译成对应的MonkeyEvent事件后,会把这些事件 ...

  8. Api demo源码学习(4)--App/Activity/Dialog --Activity以Dialog形式呈现

    这一节实际上比 Api demo源码学习(2)--App/Activity/Custom Dialog 自定义Activity样式  还要简单一些,在源码学习(2)里,也是让Activity以Dial ...

  9. C# Windows系统音量调节Demo源码

    基于.NET Framework 4.0开发,包含滑块设置音量,键盘F1.F2调节音量. 通过滑块调节系统音量核心代码 /// <summary>/// 滑块设置音量/// </su ...

最新文章

  1. 最常用的决策树算法!Random Forest、Adaboost、GBDT 算法
  2. mysql常见面试题
  3. Redis实现分布式锁原理SetNx命令
  4. 内存映射文件 写入 卡住_在Java中使用内存映射文件时检测(写入)失败
  5. 谷歌入职邮件_为什么我全职学习了8个月以接受Google采访
  6. 15日直播预告丨SQL条件等价改写秘笈(主讲人:怀晓明)
  7. python常见的控制流结构有_【Python】控制流语句、函数、模块、数据结构
  8. 20200814:力扣201周周赛题解记录上
  9. java对象中方法的存储原理_垃圾收集机制的基本原理及方法
  10. kaldi运行thchs30例子
  11. Node——request使用代理
  12. 华为手机字体改简体_华为字体买了之后改不了 华为手机字体改简体
  13. 基于SSM框架开发的社区疫情管理系统 附带详细运行指导视频
  14. ae合成设置快捷键_解决AE提示错误预览需要2个或多个帧才能播放问题
  15. 云服务器ubuntu18安装界面,鲲鹏云服务器 ubuntu18 桌面安装
  16. 百瓦快充伤电池?1天两充用了574天后 我发现真相
  17. ASP是什么?ASP初识
  18. JavaWeb框架(一):Web入门,Http的请求和响应,https介绍,Web实战自定义服务器
  19. 大公司的高薪 OR 创业公司的期权,更加看中大公司的高薪?
  20. 使用imagine/imagine实现制作一个图片

热门文章

  1. Win32编程 使用自定义的ICON资源
  2. 什么是pickling和unpickling?
  3. 深度学习之强调一下数据的重要性
  4. 明星软件工程师的10种特质
  5. 布兰德 • 斯奈德节拍表
  6. ehcache 异常net.sf.ehcache.CacheException: When configured copyOnRead or copyOnWrite, a Store will onl
  7. 解构华为2014轮值CEO徐直军战略发言
  8. index.php 301,默认首页index/default做301重定向的思考
  9. jQuery实现拼图板效果
  10. 处理PDF文档比较强大的库