好几天没写博客了,处理了一点个人私事加上平时加班,基本上时间不充裕,上篇文章讲了一下用Mediaplayer来播放音乐,这次就讲讲使用Mediaplayer来和SurfaceView配合播放一个视频流媒体。MediaPlayer不仅可以播放视频,还可以与SurfaceView的配合,SurfaceView主要用于显示MediaPlayer播放的视频流媒体的画面渲染,两者可以一起协同播放视频。

基础维护

先来看下要实现的界面:

如果你看过上篇文章,就发现其实很简单的就是多了一个进度条,还有一个就是SurfaceView,就是下面那块黑色区域;

布局文件代码:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.googlevideo.MainActivity" > <EditText
android:id="@+id/edit_musicPath"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="输入MV的路径" /> <SeekBar
android:id="@+id/seekBar_video"
android:layout_width="match_parent"
android:layout_height="wrap_content" /> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" > <Button
android:id="@+id/btn_Play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="playEvent"
android:text="播放" /> <Button
android:id="@+id/btn_Pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="pauseEvent"
android:text="暂停" /> <Button
android:id="@+id/btn_Stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="stopEvent"
android:text="停止" /> <Button
android:id="@+id/btn_Replay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="replayEvent"
android:text="重播" />
</LinearLayout> <SurfaceView
android:id="@+id/surface_video"
android:layout_width="match_parent"
android:layout_height="match_parent" /> </LinearLayout>

 Demo实现

实现Demo之前应该讲讲视频播放的原理,先确定视频的格式,这个和解码相关,不同的格式视频编码不同,然后通过编码格式进行解码,最后得到一帧一帧的图像,并把这些图像快速的显示在界面上,即为播放一段视频。SurfaceView在Android中就是完成这个功能的。SurfaceView是配合MediaPlayer使用的,MediaPlayer也提供了相应的方法设置SurfaceView显示图片,只需要为MediaPlayer指定SurfaceView显示图像即可。它的完整签名:void setDisplay(SurfaceHolder sh)。它需要传递一个SurfaceHolder对象,SurfaceHolder可以理解为SurfaceView装载需要显示的一帧帧图像的容器,它可以通过SurfaceHolder.getHolder()方法获得。使用MediaPlayer配合SurfaceView播放视频的步骤与播放使用MediaPlayer播放MP3大体一致,只需要额外设置显示的SurfaceView即可。

先准备一段能播放的视频:

播放视频效果图:

代码如下:

	editText = (EditText) findViewById(R.id.edit_musicPath);
pathString = editText.getText().toString().trim();
File file = new File(pathString);
if (file.exists()) {
try {
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(pathString);
//通过SurfaceView获取的Holder
mediaPlayer.setDisplay(holder);
mediaPlayer.prepare();
mediaPlayer.start();
btn_PlayButton.setEnabled(false);
//设置Bar的最大值
int max=mediaPlayer.getDuration();
seekBarVideo.setMax(max);
//定时器更新进度条
timer=new Timer();
timeTask=new TimerTask() { @Override
public void run() {
// TODO Auto-generated method stub
seekBarVideo.setProgress(mediaPlayer.getCurrentPosition());
}
};
timer.schedule(timeTask, 0, 500);
//视频播放完之后重新设置显示
mediaPlayer.setOnCompletionListener(new OnCompletionListener() { @Override
public void onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub
btn_PlayButton.setEnabled(true);
}
}); } catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
Toast.makeText(this, "Sorry,你输入的路径有问题,请仔细检查", Toast.LENGTH_SHORT)
.show();
}

SeekBar的使用: 

	//设置Bar的最大值
int max=mediaPlayer.getDuration();
seekBarVideo.setMax(max);
//定时器更新进度条
timer=new Timer();
timeTask=new TimerTask() { @Override
public void run() {
// TODO Auto-generated method stub
seekBarVideo.setProgress(mediaPlayer.getCurrentPosition());
}
};
timer.schedule(timeTask, 0, 500);

 其中holder是SurfaceHolder,在onCreate中获取:

	surfaceView = (SurfaceView) findViewById(R.id.surface_video);
holder = surfaceView.getHolder();
//兼容4.0以后的手机版本,本身是不维护的
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

 如果正在播放视频,最小化的时候是会有声音的,需要在回调函数中处理一下:

		holder.addCallback(new Callback() {

			@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
Log.i(tag, "销毁了Holder");
if (mediaPlayer!=null&&mediaPlayer.isPlaying()) {
currentPosition=mediaPlayer.getCurrentPosition();
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer=null;
timer.cancel();
timeTask.cancel();
timer=null;
timeTask=null;
}
} @Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
Log.i(tag,"创建了Holder");
if (currentPosition>0) {
try {
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(pathString);
//通过SurfaceView获取的Holder
mediaPlayer.setDisplay(holder);
mediaPlayer.prepare();
mediaPlayer.start();
mediaPlayer.seekTo(currentPosition);
btn_PlayButton.setEnabled(false);
//视频播放完之后重新设置显示
mediaPlayer.setOnCompletionListener(new OnCompletionListener() { @Override
public void onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub
btn_PlayButton.setEnabled(true);
}
}); int max=mediaPlayer.getDuration();
seekBarVideo.setMax(max);
//定时器更新进度条
timer=new Timer();
timeTask=new TimerTask() { @Override
public void run() {
// TODO Auto-generated method stub
seekBarVideo.setProgress(mediaPlayer.getCurrentPosition());
}
};
timer.schedule(timeTask, 0, 500);
} catch (Exception e) {
// TODO: handle exception
}
}
} @Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
Log.i(tag,"改变了Holder");
}
});

  进度条是SeekBar,如果需要快进或者后退的时候是需要将焦点赋值给mediaPlayer的:

	seekBarVideo=(SeekBar) findViewById(R.id.seekBar_video);
seekBarVideo.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
int position=seekBar.getProgress();
if (mediaPlayer!=null&&mediaPlayer.isPlaying()) {
mediaPlayer.seekTo(position);
}
} @Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub } @Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub }
});

  暂停事件:

	public void pauseEvent(View view) {
if (btn_PauseButton.getText().equals("继续")) {
mediaPlayer.start();
btn_PauseButton.setText("暂停");
return;
}
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
btn_PauseButton.setText("继续");
}
}

 停止事件:

	public void stopEvent(View view) {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
btn_PlayButton.setEnabled(true);
mediaPlayer.stop();
// 释放mediaplayer否则的话会占用内存
mediaPlayer.release();
mediaPlayer = null;
}
btn_PauseButton.setText("暂停");
btn_PlayButton.setEnabled(true);
}

  重播事件:

	public void replayEvent(View view) {
surfaceView.setVisibility(View.VISIBLE);
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.seekTo(0);
} else {
playEvent(view);
}
// 重播的时候应该设置播放的状态
btn_PlayButton.setEnabled(true);
}

Android视频播放-SurfaceView和Mediaplayer的更多相关文章

  1. Android音视频之MediaPlayer音视频播放

    前言: 昨天总结了视频录制,今天来学习一下视频的播放,Android的视频播放主要采用MediaPlayer类. MediaPlayer介绍 MediaPlayer类可用于控制音频/视频文件或流的播放 ...

  2. 【Android 多媒体开发】 MediaPlayer 网络视频播放器

    作者 : 万境绝尘 (octopus_truth@163.com) 转载请著名出处 : http://blog.csdn.net/shulianghan/article/details/3889514 ...

  3. Android 视频播放器 (二):使用MediaPlayer播放视频

    在 Android 视频播放器 (一):使用VideoView播放视频 我们讲了一下如何使用VideoView播放视频,了解了基本的播放器的一些知识和内容.也知道VideoView内部封装的就是Med ...

  4. android中使用surfaceview+MediaPlayer播放视频

    Android中播放视频主要有两种方式: 使用其自带的播放器.指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型 使用android自带的VideoView,这种方法 ...

  5. android视频播放心得体会

    android视频播放主要是两种方式1.系统封装好的videoplayer,有前进.后退.暂停/播放.拉动最基本的功能,够一般使用,操作办法也很简单,如果需要自定义程度高就需要用到第二种方法:Surf ...

  6. android视频播放

    视频播放我们用到的是MediaPlayer,显示控件使用的surfaceView 我们向SD卡中先添加个视频文件,我的是xajh.3gp,不要用mp4,MP4会出现 should have subti ...

  7. Android有关surfaceView又一次创建的问题。

    近期在做一个Android视频播放器的项目.遇到一个问题,就是锁屏之后.surfaceview就会被销毁掉,然后就会出现各种错误.到csdn论坛去发帖提问,各种所谓的大神都说,解锁屏在又一次创建一个, ...

  8. Android 视频播放器 (四):使用ExoPlayer播放视频

    一.简介 ExoPlayer是一个Android应用层的媒体播放器,它提供了一套可替换Android MediaPlayer的API,可以播放本地或者是线上的音视频资源.ExoPlayer支持一些An ...

  9. Android 视频播放器切换到下个视频时残留上个视频画面的解决办法

    最近在做一个Android视频播放器,遇到一个问题:切换到下一个视频时,中间会停留上一个视频的残存画面.   这是怎么回事?   我在网上找了很多资料,终于找到了原因:我是用自定义一个surfacev ...

随机推荐

  1. IAR搭建unity框架

    1. 新建工程 2. 增加组 unit,并加入相应源码 3. 增加需要测试的API源文件 4. 编写相应的测试用例 编译,download and debug view->TerminalIO ...

  2. 海康威视 - 萤石云开放平台 js 版

    开放平台 https://open.ys7.com/mobile/download.html API http://open.ys7.com/doc/zh/uikit/uikit_javascript ...

  3. python抓包模块

    pcapy模块 安装 yum install -y epel-release yum install -y pip gcc    gcc-c++   libpcap-devel python-deve ...

  4. 【构造】Codeforces Round #480 (Div. 2) B. Marlin

    题意:给你一个4*n的网格,保证n为奇数,让你在其中放k个障碍物,不能放在边界的格子上,使得从左上角走到右下角的最短路的方案数,恰好等于从左下角走到右上角的最短路的方案数. k为偶数时,以纵向为对称轴 ...

  5. 小程序在wxml页面中取整

    小程序无法像html中,在页面中直接parseInt() index.wxml {{price | Int}} 小程序还有另一种处理方法 wxs 是一种类似于js脚本的东西 filters.wxs v ...

  6. LNMP 1.2升级Nginx、MySQL/MariaDB、PHP教程

    一般情况下不建议对生产环境进行升级,升级开始后会停止LNMP相关服务.本文仅适用于LNMP1.2及以后版本! 在LNMP目前LNMP v1.2中已经包含了Nginx.MySQL/MariaDB.PHP ...

  7. 《TD式创新”祸国殃民》

    TD式创新”祸国殃民> 作者:北京邮电大学 阚凯力 (2014年12月16日) 电信业内早就众所周知的TD-SCDMA真相,终于公之于天下.铁的事实是,它从来就不是什么“自主知识产权”,而是西门 ...

  8. C#中,为什么在值类型后面加问号

    在C#中,声明一个值类型或引用类型的变量,无论是否给这个变量赋初值,该变量都有默认值: 比如声明引用类型变量: string a,其等效于string a = null,string的默认值为null ...

  9. java classpath作用

    环境变量的配置: 1):永久配置方式:JAVA_HOME=%安装路径%\Java\jdk path=%JAVA_HOME%\bin 2):临时配置方式:set path=%path%;C:\Progr ...

  10. .NET:通过 CAS 来理解数据库乐观并发控制,顺便给出无锁的 RingBuffer。

    背景 大多数企业开发人员都理解数据库乐观并发控制,不过很少有人听说过 CAS(我去年才听说这个概念),CAS 是多线程乐观并发控制策略的一种,一些无锁的支持并发的数据结构都会使用到 CAS,本文对比 ...