天天看点

Android--视频播放器

surfaceview

既然surfaceview是配合mediaplayer使用的,mediaplayer也提供了相应的方法设置surfaceview显示图片,只需要为mediaplayer指定surfaceview显示图像即可。它的完整签名如下:

void setdisplay(surfaceholder sh)

它需要传递一个surfaceholder对象,surfaceholder可以理解为surfaceview装载需要显示的一帧帧图像的容器,它可以通过surfaceholder.getholder()方法获得。

使用mediaplayer配合surfaceview播放视频的步骤与播放使用mediaplayer播放mp3大体一致,只需要额外设置显示的surfaceview即可。

surfaceview双缓冲

上面有提到,surfaceview和大部分视频应用一样,把视频流解析成一帧帧的图像进行 显示,但是如果把这个解析的过程放到一个线程中完成,可能在上一帧图像已经显示过后,下一帧图像还没有来得及解析,这样会导致画面的不流畅或者声音和视频 不同步的问题。所以surfaceview和大部分视频应用一样,通过双缓冲的机制来显示帧图像。那么什么是双缓冲呢?双缓冲可以理解为有两个线程轮番去 解析视频流的帧图像,当一个线程解析完帧图像后,把图像渲染到界面中,同时另一线程开始解析下一帧图像,使得两个线程轮番配合去解析视频流,以达到流畅播 放的效果。

surfaceholder

surfaceview内部实现了双缓冲的机制,但是实现这个功能是非常消耗系统内存的。因为移动设备的局限性,android在设计的时候规 定,surfaceview如果为用户可见的时候,创建surfaceview的surfaceholder用于显示视频流解析的帧图片,如果发现 surfaceview变为用户不可见的时候,则立即销毁surfaceview的surfaceholder,以达到节约系统资源的目的。

如果开发人员不对surfaceholder进行维护,会出现最小化程序后,再打开应用的时候,视频的声音在继续播放,但是不显示画面了的情况,这 就是因为当surfaceview不被用户可见的时候,之前的surfaceholder已经被销毁了,再次进入的时候,界面上的 surfaceholder已经是新的surfaceholder了。所以surfaceholder需要我们开发人员去编码维护,维护 surfaceholder需要用到它的一个回调,surfaceholder.callback(),它需要实现三个如下三个方法:

void surfacedestroyed(surfaceholder holder):当surfaceholder被销毁的时候回调。

void surfacecreated(surfaceholder holder):当surfaceholder被创建的时候回调。

void surfacechange(surfaceholder holder):当surfaceholder的尺寸发生变化的时候被回调。

以下是这三个方法的调用的过程,在应用中分别为surfaceholder实现了这三个方法,先进入应用,surfaceholder被创建,创建 好之后会改变surfaceholder的大小,然后按home键回退到桌面销毁surfaceholder,最后再进入应用,重新 surfaceholder并改变其大小。

surfaceview的demo示例

上面讲了那么多关于surfaceview的内容,下面通过一个demo简单演示一下 surfaceview如何播放视频,加了一个滚动条,用于显示进度,还可以拖动滚动条选择播放位置,demo的注释比较完整.

Android--视频播放器

import java.io.file;   

import android.media.audiomanager;   

import android.media.mediaplayer;   

import android.media.mediaplayer.oncompletionlistener;   

import android.media.mediaplayer.onerrorlistener;   

import android.media.mediaplayer.onpreparedlistener;   

import android.os.bundle;   

import android.app.activity;   

import android.util.log;   

import android.view.surfaceholder;   

import android.view.surfaceholder.callback;   

import android.view.surfaceview;   

import android.view.view;   

import android.widget.button;   

import android.widget.edittext;   

import android.widget.seekbar;   

import android.widget.seekbar.onseekbarchangelistener;   

import android.widget.toast;   

public class mainactivity extends activity {   

private final string tag = "main";   

private edittext et_path;   

private surfaceview sv;   

private button btn_play, btn_pause, btn_replay, btn_stop;   

private mediaplayer mediaplayer;   

private seekbar seekbar;   

private int currentposition = 0;   

private boolean isplaying;   

@override   

protected void oncreate(bundle savedinstancestate) {   

super.oncreate(savedinstancestate);   

setcontentview(r.layout.activity_main);   

seekbar = (seekbar) findviewbyid(r.id.seekbar);   

sv = (surfaceview) findviewbyid(r.id.sv);   

et_path = (edittext) findviewbyid(r.id.et_path);   

btn_play = (button) findviewbyid(r.id.btn_play);   

btn_pause = (button) findviewbyid(r.id.btn_pause);   

btn_replay = (button) findviewbyid(r.id.btn_replay);   

btn_stop = (button) findviewbyid(r.id.btn_stop);   

btn_play.setonclicklistener(click);   

btn_pause.setonclicklistener(click);   

btn_replay.setonclicklistener(click);   

btn_stop.setonclicklistener(click);   

// 为surfaceholder添加回调   

sv.getholder().addcallback(callback);   

// 4.0版本之下需要设置的属性   

// 设置surface不维护自己的缓冲区,而是等待屏幕的渲染引擎将内容推送到界面   

// sv.getholder().settype(surfaceholder.surface_type_push_buffers);   

// 为进度条添加进度更改事件   

seekbar.setonseekbarchangelistener(change);   

}   

private callback callback = new callback() {   

// surfaceholder被修改的时候回调   

public void surfacedestroyed(surfaceholder holder) {   

log.i(tag, "surfaceholder 被销毁");   

// 销毁surfaceholder的时候记录当前的播放位置并停止播放   

if (mediaplayer != null && mediaplayer.isplaying()) {   

currentposition = mediaplayer.getcurrentposition();   

mediaplayer.stop();   

public void surfacecreated(surfaceholder holder) {   

log.i(tag, "surfaceholder 被创建");   

if (currentposition > 0) {   

// 创建surfaceholder的时候,如果存在上次播放的位置,则按照上次播放位置进行播放   

play(currentposition);   

currentposition = 0;   

public void surfacechanged(surfaceholder holder, int format, int width,   

int height) {   

log.i(tag, "surfaceholder 大小被改变");   

};   

private onseekbarchangelistener change = new onseekbarchangelistener() {   

public void onstoptrackingtouch(seekbar seekbar) {   

// 当进度条停止修改的时候触发   

// 取得当前进度条的刻度   

int progress = seekbar.getprogress();   

// 设置当前播放的位置   

mediaplayer.seekto(progress);   

public void onstarttrackingtouch(seekbar seekbar) {   

public void onprogresschanged(seekbar seekbar, int progress,   

boolean fromuser) {   

private view.onclicklistener click = new view.onclicklistener() {   

public void onclick(view v) {   

switch (v.getid()) {   

case r.id.btn_play:   

play(0);   

break;   

case r.id.btn_pause:   

pause();   

case r.id.btn_replay:   

replay();   

case r.id.btn_stop:   

stop();   

default:   

/*  

* 停止播放  

*/   

protected void stop() {   

mediaplayer.release();   

mediaplayer = null;   

btn_play.setenabled(true);   

isplaying = false;   

/**  

* 开始播放  

*  

* @param msec 播放初始位置  

protected void play(final int msec) {   

// 获取视频文件地址   

string path = et_path.gettext().tostring().trim();   

file file = new file(path);   

if (!file.exists()) {   

toast.maketext(this, "视频文件路径错误", 0).show();   

return;   

try {   

mediaplayer = new mediaplayer();   

mediaplayer.setaudiostreamtype(audiomanager.stream_music);   

// 设置播放的视频源   

mediaplayer.setdatasource(file.getabsolutepath());   

// 设置显示视频的surfaceholder   

mediaplayer.setdisplay(sv.getholder());   

log.i(tag, "开始装载");   

mediaplayer.prepareasync();   

mediaplayer.setonpreparedlistener(new onpreparedlistener() {   

public void onprepared(mediaplayer mp) {   

log.i(tag, "装载完成");   

mediaplayer.start();   

// 按照初始位置播放   

mediaplayer.seekto(msec);   

// 设置进度条的最大进度为视频流的最大播放时长   

seekbar.setmax(mediaplayer.getduration());   

// 开始线程,更新进度条的刻度   

new thread() {   

public void run() {   

isplaying = true;   

while (isplaying) {   

int current = mediaplayer   

.getcurrentposition();   

seekbar.setprogress(current);   

sleep(500);   

} catch (exception e) {   

e.printstacktrace();   

}.start();   

btn_play.setenabled(false);   

});   

mediaplayer.setoncompletionlistener(new oncompletionlistener() {   

public void oncompletion(mediaplayer mp) {   

// 在播放完毕被回调   

mediaplayer.setonerrorlistener(new onerrorlistener() {   

public boolean onerror(mediaplayer mp, int what, int extra) {   

// 发生错误重新播放   

return false;   

* 重新开始播放  

protected void replay() {   

mediaplayer.seekto(0);   

toast.maketext(this, "重新播放", 0).show();   

btn_pause.settext("暂停");   

* 暂停或继续  

protected void pause() {   

if (btn_pause.gettext().tostring().trim().equals("继续")) {   

toast.maketext(this, "继续播放", 0).show();   

mediaplayer.pause();   

btn_pause.settext("继续");   

toast.maketext(this, "暂停播放", 0).show();   

}   

转载:http://blog.csdn.net/chaoyu168/article/details/51179636

继续阅读