Android播放视频(一)

简介: package cn.c; import java.io.File; import android.app.Activity; import android.
package cn.c;
import java.io.File;
import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaPlayer.OnSeekCompleteListener;
import android.media.MediaPlayer.OnVideoSizeChangedListener;
import android.os.Bundle;
import android.os.Environment;
import android.view.Display;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.LinearLayout;
import android.widget.MediaController;
import android.widget.MediaController.MediaPlayerControl;
/**
 * 参考资料:http://www.embedu.org/Column/Column503.htm
 * 利用MidiaPlayer播放视频大致的流程:
 * 创建播放器对象-->设置需要播放的视频的来源-->
 * 对视频进行解码等准备工作-->开始播放-->(暂停/重启)停止播放
 * 对应这些流程,在MediaPlayer中都定义了相关方法;
 * 而且,在调用这些方法后,MediaPlayer可以进入不同的状态.
 * 这些方法和状态都很好理解,读者可以参照上图来了解.
 * 另外,因为在设置播放的视频的视频数据源之后,它需要对其进行解码(调用prepare()方法)
 * 这是一个比较耗时的操作,为了避免应用程序阻止,
 * 可以使用它的prepareAsync()方法配合OnPreparedListener监听器来让其异步操作.
 * 注意,只有MediaPlayer的准备工作做完后,才能开始播放

 * MediaPlayer本身不能显示视频内容,它只能控制视频的播放
 * 如果要使用MediaPlayer来播放视频,还需要配合SurfaceView来显示视频
 * 且将MediaPlayer中解码的视频数据在SurfaceView中播放,需要调用MediaPlayer中的setDisplay()
 * 它接受一个SurfaceHolder对象,可将我们用于
 * 播放视频的SurfaceView对应的SurfaceHolder对象作为参数
 * 这样,MediaPlayer和SurfaceView就建立起了关联.
 * 
 * MediaPlayer播放视频步骤
 * 1 设置MediaPlayer将要播放的视频
 *   mMediaPlayer.setDataSource(path);
 * 2 设置MediaPlayer的视频输出界面
 *   mMediaPlayer.setDisplay(mSurfaceHolder);
 * 3 MediaPlayer解码
 *   mMediaPlayer.prepareAsync()且结合
 *   public void onPrepared(MediaPlayer mediaPlayer)
 * 4 MediaPlayer开始播放
 *   mediaPlayer.start();
 * 
 * 
 */

public class MainActivity extends Activity implements 
OnCompletionListener,OnErrorListener,OnInfoListener,OnPreparedListener,
OnSeekCompleteListener,OnVideoSizeChangedListener,SurfaceHolder.Callback,MediaPlayerControl{
    private SurfaceView mSurfaceView;
    private SurfaceHolder mSurfaceHolder;
    private MediaController mMediaController;
    private Display mDisplay;
    private MediaPlayer mMediaPlayer;
    int videoWidth=0;
    int videoHeight=0;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mDisplay=getWindowManager().getDefaultDisplay();
        mSurfaceView=(SurfaceView) findViewById(R.id.surfaceView);
        mSurfaceHolder=mSurfaceView.getHolder();
        //SurfaceHolder是用于管理SurfaceView对象
        //那它是怎么做到管理的呢?即用mSurfaceHolder
        //添加回调即addCallback.
        //该类实现了SurfaceHolder.Callback接口,所以addCallback(this)
        //在SurfaceHolder.Callback接口中一共有三个方法来实现对于
        //SurfaceView对象的管理,即:
        //surfaceCreated()
        //surfaceChanged()
        //surfaceDestroyed()
        //概况:SurfaceHolder主要作用是监控底层的情况
        mSurfaceHolder.addCallback(this);
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        
        mMediaPlayer=new MediaPlayer();
        mMediaPlayer.setOnCompletionListener(this);
        mMediaPlayer.setOnErrorListener(this);
        mMediaPlayer.setOnInfoListener(this);
        mMediaPlayer.setOnPreparedListener(this);
        mMediaPlayer.setOnSeekCompleteListener(this);
        mMediaPlayer.setOnVideoSizeChangedListener(this);
        String path=Environment.getExternalStorageDirectory().getPath()
        +File.separator+"Test_Movie.m4v";
        try {
        	//设置MediaPlayer将要播放的视频
			mMediaPlayer.setDataSource(path);
		} catch (Exception e) {
			e.printStackTrace();
			finish();
		}
        mMediaController=new MediaController(MainActivity.this);
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
    	if (mMediaController.isShowing()) {
			mMediaController.hide();
		} else {
           mMediaController.show();
		}
    	return false;
    }
    
    //来自于SurfaceHolder.Callback接口
    //创建SurfaceView完成时,会调用该方法
	public void surfaceCreated(SurfaceHolder holder) {
		//设置MediaPlayer的视频输出界面
		//只显示音频而不显示视频时可不调用这个方法
		mMediaPlayer.setDisplay(mSurfaceHolder);
		try {
			//准备播放,调用mMediaPlayer.prepareAsync();
			//方法后会执行覆写后的
			//public void onPrepared(MediaPlayer mp)
			//注意:
			//也可调用prepare()解码,但为一个同步的操作
			mMediaPlayer.prepareAsync();
		} catch (Exception e) {
			e.printStackTrace();
			finish();
		}
	}
    //来自于SurfaceHolder.Callback接口
	//当SurfaceView的宽度,高度或其他参数发生变化时,会调用该方法
	public void surfaceChanged(SurfaceHolder holder, 
			int format, int width,int height) {
		
	}
    //来自于SurfaceHolder.Callback接口
	//当SurfaceView摧毁时,会调用该方法
	public void surfaceDestroyed(SurfaceHolder holder) {
		
	}
	//来自于MediaPlayer.OnVideoSizeChangedListener接口
	//当视频的宽度或高度发生变化时调用该方法
	public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
		
	}
	//来自于MediaPlayer.OnSeekCompleteListener接口
	public void onSeekComplete(MediaPlayer mp) {
		
	}
	//来自于MediaPlayer.OnPreparedListener接口
	//一旦调用该方法,MediaPlayer就进入了"准备就绪"
	//状态,准备开始播放.
	//此处可用于动态设置SurfaceView的宽和高!!!
	public void onPrepared(MediaPlayer mediaPlayer) {
		videoWidth=mediaPlayer.getVideoWidth();
		videoHeight=mediaPlayer.getVideoHeight();
		if (videoWidth > mDisplay.getWidth()|| videoHeight > mDisplay.getHeight()) {
			float heightRatio = (float) videoHeight/ (float) mDisplay.getHeight();
			float widthRatio = (float) videoWidth/ (float) mDisplay.getWidth();
			if (heightRatio > 1 || widthRatio > 1) {
				if (heightRatio > widthRatio) {
					videoHeight = (int) Math.ceil((float) videoHeight/ (float) heightRatio);
					videoWidth = (int) Math.ceil((float) videoWidth/ (float) heightRatio);
				} else {
					videoHeight = (int) Math.ceil((float) videoHeight/ (float) widthRatio);
					videoWidth = (int) Math.ceil((float) videoWidth/ (float) widthRatio);
				}
			}
		}
		//设置SurfaceView的宽和高
		mSurfaceView.setLayoutParams
		(new LinearLayout.LayoutParams(videoWidth,videoHeight));
		//只有在MediaPlayer准备好之后才可以对
		//MediaController进行设置和使用
		mMediaController.setMediaPlayer(this);
		mMediaController.setAnchorView(findViewById(R.id.mainView));
		mMediaController.setEnabled(true);
		mMediaController.show();
		//MediaPlayer开始播放
		mediaPlayer.start();
	}
	//来自于MediaPlayer.OnInfoListener接口
	//当出现关于播放媒体的特定信息或者需要发出警告的时候
	//将调用该方法
	//比如开始缓冲、缓冲结束、下载速度变化(该行待验证)
	//小结:这些Info都是以MediaPlayer.MEDIA_INFO_开头的
	public boolean onInfo(MediaPlayer mp, int what, int extra) {
		if (what==MediaPlayer.MEDIA_INFO_BAD_INTERLEAVING) {
			//音频和视频数据不正确地交错时将出现该提示信息.在一个
			//正确交错的媒体文件中,音频和视频样本将依序排列,从而
			//使得播放可以有效和平稳地进行
		}
		if (what==MediaPlayer.MEDIA_INFO_NOT_SEEKABLE) {
			//当媒体不能正确定位时将出现该提示信息.
			//此时意味着它可能是一个在线流
		}
		if (what==MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING) {
			//当设备无法播放视频时将出现该提示信息
			//比如视频太复杂或者码率过高
		}
		if (what==MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
			//当新的元数据可用时将出现该提示信息
		}
		if (what==MediaPlayer.MEDIA_INFO_UNKNOWN) {
			//其余不可知提示信息
		}
		return false;
	}
	//来自于MediaPlayer.OnErrorListener接口
	//MediaPlayer发生错误时会调用该方法
	//只有如下这三种错误.
	//小结:这些错误都是以MediaPlayer.MEDIA_ERROR.开头的
	public boolean onError(MediaPlayer mp, int what, int extra) {
		if (what==MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
			System.out.println("第一种错误");
		}
		if (what==MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
			System.out.println("第二种错误");
		}
		if (what==MediaPlayer.MEDIA_ERROR_UNKNOWN) {
			System.out.println("第三种错误");
		}
		return false;
	}
	//来自于MediaPlayer.OnCompletionListener接口
	//当MediaPlayer播放完文件时,会调用该方法.
	//此时可以进行一些其他的操作比如:播放下一个视频
	public void onCompletion(MediaPlayer mp) {
           finish();		
	}
	
	//以下方法均来自MediaPlayerControl接口
	public void start() {
		mMediaPlayer.start();
	}
	public void pause() {
		if (mMediaPlayer.isPlaying()) {
			mMediaPlayer.pause();
		}
	}
	public int getDuration() {
		return mMediaPlayer.getDuration();
	}
	public int getCurrentPosition() {
		return mMediaPlayer.getCurrentPosition();
	}
	public void seekTo(int pos) {
		mMediaPlayer.seekTo(pos);
	}
	public boolean isPlaying() {
		return mMediaPlayer.isPlaying();
	}
	public int getBufferPercentage() {
		// TODO Auto-generated method stub
		return 0;
	}
	public boolean canPause() {
		return true;
	}
	public boolean canSeekBackward() {
		return true;
	}
	public boolean canSeekForward() {
		return true;
	}

	
}

相关文章
|
6月前
|
Android开发 容器
35. 【Android教程】视频页面:ViewPager
35. 【Android教程】视频页面:ViewPager
64 3
|
4月前
|
编解码 监控 API
惊艳登场!揭秘如何在Android平台上轻松玩转GB28181标准,实现多视频通道接入的超实用指南!
【8月更文挑战第14天】GB28181是公共安全视频监控联网的技术标准。本文介绍如何在Android平台上实现该标准下的多视频通道接入。首先准备开发环境,接着引入GB28181 SDK依赖并初始化SDK。实现设备注册与登录后,通过指定不同通道号请求多路视频流。最后,处理接收到的数据并显示给用户。此过程涉及视频解码,需确保应用稳定及良好的用户体验。
105 0
|
6月前
|
监控 Android开发 数据安全/隐私保护
安卓kotlin JetPack Compose 实现摄像头监控画面变化并录制视频
在这个示例中,开发者正在使用Kotlin和Jetpack Compose构建一个Android应用程序,该程序 能够通过手机后置主摄像头录制视频、检测画面差异、实时预览并将视频上传至FTP服务器的Android应用
|
6月前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的安卓的微博客系统附带文章和源代码部署视频讲解等
基于ssm+vue.js+uniapp小程序的安卓的微博客系统附带文章和源代码部署视频讲解等
51 2
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的高校后勤网上报修系统安卓app附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的高校后勤网上报修系统安卓app附带文章源码部署视频讲解等
65 0
|
7月前
|
XML Android开发 数据格式
Fragment的使用,零基础入门android逆向视频课程
Fragment的使用,零基础入门android逆向视频课程
|
7月前
|
JSON 网络协议 Android开发
【Android App】实战项目之仿微信的私信和群聊App(附源码和演示视频 超详细必看)
【Android App】实战项目之仿微信的私信和群聊App(附源码和演示视频 超详细必看)
398 4
|
7月前
|
Web App开发 Android开发 ice
【Android App】给App集成WebRTC实现视频发送和接受实战(附源码和演示 超详细)
【Android App】给App集成WebRTC实现视频发送和接受实战(附源码和演示 超详细)
769 1
|
7月前
|
Java 定位技术 Android开发
【Android App】利用腾讯地图获取地点信息和规划导航线路讲解及实战(附源码和演示视频 超详细必看)
【Android App】利用腾讯地图获取地点信息和规划导航线路讲解及实战(附源码和演示视频 超详细必看)
445 1
|
7月前
|
JSON Android开发 数据格式
【Android App】实战项目之仿抖音的短视频分享App(附源码和演示视频 超详细必看)
【Android App】实战项目之仿抖音的短视频分享App(附源码和演示视频 超详细必看)
406 1