本文标签: Android MediaPlayer , Android提高
上次讲解了《MediaPlayer播放网络音频》,介绍了MediaPlayer关于网络音频的缓冲和进度条控制的方法,这次再讲解MediaPlayer播放网络视频。播放网络视频比播放网络音频多需要一个SurfaceView而已,已经熟悉MediaPlayer播放网络音频之后,相信大家对播放网络视频也能很快地掌握。先来看看本文程序运行截图:
本文程序的视频来自http://daily3gp.com,大家可以替换程序中的视频链接,试试其他影片。
main.xml的源码如下:
Player.java是本文的核心,Player.java实现了“进度条更新”、“数据缓冲”、“SurfaceHolder生命周期”等功能,其中“SurfaceHolder生命周期”是视频与音频播放的最大区别,通过surfaceCreated()、surfaceDestroyed()、surfaceChanged()可以创建/释放某些资源。下面这个地方需要注意一下:
有些视频是android播放器不能播放的,不能播放时videoHeight=0,videoWidth=0,以此来判断是否播放视频。
Player.java源码如下:
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.SeekBar;
public class Player implements OnBufferingUpdateListener,
OnCompletionListener, MediaPlayer.OnPreparedListener,
SurfaceHolder.Callback {
private int videoWidth;
private int videoHeight;
public MediaPlayer mediaPlayer;
private SurfaceHolder surfaceHolder;
private SeekBar skbProgress;
private Timer mTimer=new Timer();
public Player(SurfaceView surfaceView,SeekBar skbProgress)
{
this.skbProgress=skbProgress;
surfaceHolder=surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mTimer.schedule(mTimerTask, 0, 1000);
}
/*******************************************************
* 通过定时器和Handler来更新进度条
******************************************************/
TimerTask mTimerTask = new TimerTask() {
@Override
public void run() {
if(mediaPlayer==null)
return;
if (mediaPlayer.isPlaying() && skbProgress.isPressed() == false) {
handleProgress.sendEmptyMessage(0);
}
}
};
Handler handleProgress = new Handler() {
public void handleMessage(Message msg) {
int position = mediaPlayer.getCurrentPosition();
int duration = mediaPlayer.getDuration();
if (duration > 0) {
long pos = skbProgress.getMax() * position / duration;
skbProgress.setProgress((int) pos);
}
};
};
//*****************************************************
public void play()
{
mediaPlayer.start();
}
public void playUrl(String videoUrl)
{
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(videoUrl);
mediaPlayer.prepare();//prepare之后自动播放
//mediaPlayer.start();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void pause()
{
mediaPlayer.pause();
}
public void stop()
{
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
Log.e("mediaPlayer", "surface changed");
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
try {
mediaPlayer = new MediaPlayer();
mediaPlayer.setDisplay(surfaceHolder);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnPreparedListener(this);
} catch (Exception e) {
Log.e("mediaPlayer", "error", e);
}
Log.e("mediaPlayer", "surface created");
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
Log.e("mediaPlayer", "surface destroyed");
}
@Override
/**
* 通过onPrepared播放
*/
public void onPrepared(MediaPlayer arg0) {
videoWidth = mediaPlayer.getVideoWidth();
videoHeight = mediaPlayer.getVideoHeight();
if (videoHeight != 0 && videoWidth != 0) {
arg0.start();
}
Log.e("mediaPlayer", "onPrepared");
}
@Override
public void onCompletion(MediaPlayer arg0) {
// TODO Auto-generated method stub
}
@Override
public void onBufferingUpdate(MediaPlayer arg0, int bufferingProgress) {
skbProgress.setSecondaryProgress(bufferingProgress);
int currentProgress=skbProgress.getMax()*mediaPlayer.getCurrentPosition()/mediaPlayer.getDuration();
Log.e(currentProgress+"% play", bufferingProgress + "% buffer");
}
}
test_videoplayer.java是主程序,负责调用Player类,其中关键部分是SeekBarChangeEvent这个SeekBar拖动的事件:SeekBar的Progress是0~SeekBar.getMax()之内的数,而MediaPlayer.seekTo()的参数是0~MediaPlayer.getDuration()之内数,所以MediaPlayer.seekTo()的参数是(progress/seekBar.getMax())*MediaPlayer.getDuration()。
test_videoplayer.java源码如下:
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
public class test_videoplayer extends Activity {
private SurfaceView surfaceView;
private Button btnPause, btnPlayUrl, btnStop;
private SeekBar skbProgress;
private Player player;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
surfaceView = (SurfaceView) this.findViewById(R.id.surfaceView1);
btnPlayUrl = (Button) this.findViewById(R.id.btnPlayUrl);
btnPlayUrl.setOnClickListener(new ClickEvent());
btnPause = (Button) this.findViewById(R.id.btnPause);
btnPause.setOnClickListener(new ClickEvent());
btnStop = (Button) this.findViewById(R.id.btnStop);
btnStop.setOnClickListener(new ClickEvent());
skbProgress = (SeekBar) this.findViewById(R.id.skbProgress);
skbProgress.setOnSeekBarChangeListener(new SeekBarChangeEvent());
player = new Player(surfaceView, skbProgress);
}
class ClickEvent implements OnClickListener {
@Override
public void onClick(View arg0) {
if (arg0 == btnPause) {
player.pause();
} else if (arg0 == btnPlayUrl) {
String url="http://daily3gp.com/vids/family_guy_penis_car.3gp";
player.playUrl(url);
} else if (arg0 == btnStop) {
player.stop();
}
}
}
class SeekBarChangeEvent implements SeekBar.OnSeekBarChangeListener {
int progress;
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// 原本是(progress/seekBar.getMax())*player.mediaPlayer.getDuration()
this.progress = progress * player.mediaPlayer.getDuration()
/ seekBar.getMax();
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// seekTo()的参数是相对与影片时间的数字,而不是与seekBar.getMax()相对的数字
player.mediaPlayer.seekTo(progress);
}
}
}
声明: 本文由( 张飞不张,文采横飞 )原创编译,转载请保留链接: Android提高第二十一篇之MediaPlayer播放网络视频
Linux系统与内核学习群:194051772
WP建站技术学习交流群:194062106
[e01]顶起
2011-05-18 14:25[e01],正准备学android,目前只能膜拜了。
2011-05-18 14:26[e01]相当不错!!
2011-05-18 14:26顶起
2011-05-18 14:27[e02]
2011-05-18 14:35不会吧!!???
2011-05-19 20:24为什么我的程序在执行到 test_vedioplayer.java player.playUrl(url)会抛出一个IOException?sunhh@bupt.edu.cn
2011-05-19 21:03回复 turbosun:
2011-05-19 22:19先确定一下你的模拟器是否可以上网
博主,你好,我想问下,像你的这些课程一课一课的,在哪看的啊?
2011-05-19 22:52回复 kay226:
2011-05-20 08:50呃。。。这不是课,这是我随意写得技术分享文章而已
回复 hellogv:你好,博主,谢谢你的回答。我感觉你的这个分享文章写的非常系统,有入门,有提高,博主看的是书,还是视频什么的才有这样的文章可写呢,能不能推荐一下,谢谢了。
2011-05-20 09:24请问博主:rtsp格式流媒体文件可以播放吗?模拟器2.2的。opencore中貌似支持,2.2源码中的说明也是可以播放http/rtsp的,但是具体测试了下不可以
2011-05-20 09:50学习中
2011-05-20 10:12回复 kay226:
2011-05-20 10:18我看SDK。。。
回复 hellogv:牛!
2011-05-20 10:27回复 hellogv:你好!我的模拟器可以上网,DDMS返回的错误是Command PLAYER_INIT complete with an error or info UNKNOWN PVMFStatus。我用的是android 2.1的模拟器。期待你的回复,谢谢!
2011-05-20 10:34学习ing 很炫[e01]
2011-05-20 11:30[e01][e03]
2011-05-20 13:32还是多分析android应用程序构架!
2011-05-20 22:01要不然写一大堆程序还是不知所以然!
看来好多人学
2011-05-21 00:07回复 wangchengcai2010:未请教
2011-05-21 10:41回复 zj_1395201:
2011-05-21 10:44RTSP只是一个传输通道,具体还是要看视频的编解码的。
回复 hellogv:H.264的,哦还有想请教下大牛,MediaRecorder在录制音频文件时,以G.711格式编码,在android里面是哪个参数?AAC、AMR_NB、AMR_WB、DEFAULT?API里面貌似说的不清楚,请大牛帮忙[e10]
2011-05-21 10:58[e01]
2011-05-21 13:21http://www.woir-cn.info
有电脑的同学可以去看看 赚点外快 很划算的
2011-05-21 14:26还是多分析android应用程序构架!要不然写一大堆程序还是不知所以然![e10]
2011-05-21 15:07[e01]还是多分析android应用程序构架!要不然写一大堆程序还是不知所以然!
2011-05-21 15:07很有用、
2011-05-21 16:03但是看不懂!!!
[e01]
2011-05-21 23:34[e01]
2011-05-22 10:15[e03]本想用一下了,可看不懂。
2011-05-22 14:41对,学者学者总是感觉比较乱,好像脑海里面总是零碎的一样,没有系统整理。
2011-05-22 19:54用Handler即可实现进度条的实时更新,为什么还加个定时器啊!用TimerTask不增加了程序负载,降低了效率不是?赐教
2011-05-23 09:44哦,标记一下
2011-05-23 12:49很好啊
2011-05-23 23:30厉害、厉害 佩服、佩服
2011-05-24 17:32能支持RTSP over http吗?
2011-05-25 09:52回复 zyy198723:
2011-05-25 13:26这个没试过。。。。
[e10]
2011-05-25 16:49使用更方便的VideoView吧,虽然VideoView extends SurfaceView implements MediaController.MediaPlayerControl
2011-06-09 17:43博主,我现在要播放网上以MMS:开头的视频流,您那有什么方法吗?
2011-06-10 17:49回复 guispor7:
2011-06-11 09:34抱歉,我没做MMS。。。
博主,你这个代码可能存在潜在问题!
2011-06-15 17:21由于player是在surfaceCreated回调中new出来的,而在播放playUrl()方法调用时,player可能还没有new出来(如果点击播放按钮够快的话),这时候就会出现空指针异常,我已经试过了!呵呵
回复 zhangbin2757:
2011-06-16 13:39在运行的时候加个null判断就好了
回复 hellogv:我运行也会出一个IOException,msg:repare failed.: status=0xC8,请指教。
2011-06-24 09:17回复 hellogv:我在运行也会出一个IOException,msg:repare failed.: status=0xC8,请指教。
2011-06-24 09:18回复 ycdx2001:
2011-06-24 10:11我也在模拟器上运行,没错误,请检测你播放的视频格式,以及ADB程序有没有错异常
回复 hellogv:我用手机来调试,不行,会出IO异常。
2011-06-28 08:54[e04][e03][e03][e03]
2011-06-28 17:18代码可以用
2011-07-13 14:49谢了,都是很好的文章。。。
2011-07-22 16:49膜拜
2011-08-12 17:26很好,很强大!
2011-08-12 17:26能不能用来播放swf格式的视频呢?比如我想播放优酷等网站的视频,虽然可以用WebView实现,但感觉效果不好。是不是优酷这些网站,带.swf格式其实是已经套了一个flash播放器了?突然自己觉得好像不能了……
2011-08-13 20:37[reply]ly_sr[/reply]
2011-08-14 09:46这个例子也是用系统默认播放器,如果不可以,那也不行了
你知道“快手视频”系列吗?能估计出来他那个大概是怎么实现的吗?是自己抓的地址做适配,还是可能人家直接和来源网站合作,网站直接供源?
2011-08-14 10:30您好,hellogv,麻烦问一下,用mediaplayer播放网络的实时视频流怎么才能降低延时呢?
2011-08-16 17:03[reply]chaijinjian[/reply]
2011-08-16 22:27降低延时的意思是?
请问下博主,在开发中,遇到了问题,并且不知道怎么解决,这时怎么去思路?
2011-08-22 17:15[reply]gg465164252[/reply]
2011-08-24 18:41关键是,先分析是什么问题,知道问题在哪才能去解决,绝不能碰运气去浪费时间
请问下前辈,这个要在真实手机上才能播放出视频吗?
2011-08-25 16:24我在模拟器运行的,用的本地网络,但是运行到prepare()就不动了
Log.e("mediaPlayer", "准备播放");
mediaPlayer.prepare();//prepare之后自动播放
Log.e("mediaPlayer", "开始播放");
日志有准备播放,没有开始播放
播放网络视频不需要mediaPlayer.start();这段吧?
[reply]scswlike1[/reply]
2011-08-25 22:10start()是必须的
为什么按照你的运行 播放不了视频 还出现错误呢
2011-08-26 14:07LZ,可否发一份源码呢?谢谢
2011-09-06 17:39wanjianqiao@126.com
[reply]hdf337[/reply]
2011-09-10 11:45什么错误?
LZ啊,你这个只能播放小文件啊,播放稍微大点的(譬如上10M的视频文件)就没反应了啊。。。。。。求解,希望LZ帮帮忙啊
2011-09-16 17:29[reply]z303729470[/reply]
2011-09-17 10:54兄弟,这是在线播放。。。你不是把视频放在资源里面了吧。。。
[reply]hellogv[/reply]
2011-09-17 18:21我的也是在线播放啊,我试过了我在服务器上放了比较小的3GP格式的视频,能放出来,一点没问题,一旦我改成播放一个稍微大点的3GP视频,等半天都没有反应啊,设置的资源路径也是mediaPlayer.setDataSource(“http://XXXXXX.3gp”); 不知道为什么会这样
[reply]z303729470[/reply]
2011-09-17 22:11那是因为服务器不支持流式播放,所以mediaplay帮你下载之后再播放
[reply]hellogv[/reply]
2011-09-18 12:26先谢谢LZ的热情回复,看了LZ的回复,我在网上搜索了一下,说是播放在线媒体文件,文件应该要能支持pragressive 下载,要支持pragressive 下载就要给这个视频文件打上一个hint,不知道LZ是不是也是这么做的呢?
[reply]z303729470[/reply]
2011-09-18 22:42服务器不支持流式播放,客户端怎么做都是下载后再播的。。。
你好 楼主
2011-10-17 18:02我使用了你的Demo,但是有bug的,报:: java.io.IOException: Prepare failed.: status=0xC8
我只是改了一下界面而已,视频是在你上面说的那个网站找的,但是还是不能播放视频。
thanks
kakarote
[reply]monlikey[/reply]
2011-10-17 23:13换个视频链接试试看,我这个连接是在网上随便找的
楼主为什么照着这个例子自己弄了个demo,什么也不能播放呢???
2011-10-18 17:02楼主用你的demo啥也没播放啊,只是肿么回事儿,请楼主指教!
2011-10-18 17:18[reply]ziyouzhifeng007[/reply]
2011-10-18 21:51也许是这个视频链接无效了
楼主什么也没播放,该怎么办呢?求助。
2011-10-19 09:24在模拟器上不能流畅的播放吗?
2012-01-12 17:03为什么在模拟器上可以播放,反而在真机上播放不了呢?
2012-01-12 18:01发生了这个异常java.io.IOException: Prepare failed.: status=0xC8
2012-02-09 16:22请教楼主,如何才能得到支持“progressive download”的视频播放格式的链接?我在优酷上找到视频链接(http://xxxx.xx.xxxx.html)是没有用的,可能是因为优酷上的视屏是通过flash播放的……
2012-02-10 10:00还有就是在Android设备浏览器上输入楼主的链接"http://daily3gp.com/vids/family_guy_penis_car.3gp",显示的是"该视频是无效的流媒体,无法分流到此设备"
2012-02-10 10:24[reply]chillser[/reply]
2012-02-10 14:08也许这个网络视频已经被删除了
[reply]Android_Robot[/reply]
2012-02-10 14:10为什么要用浏览器打开,而不用mediaplayer打开?webkit跟mediaplayer的http是独立分开的
把你的源代码压缩一下贴出了看看!
2012-03-23 10:04怎样获取缓存保存起来
2012-03-29 21:20[reply]white__cat[/reply]
2012-03-30 22:47mediaplayer没有提供获取缓存的api,只有自己实现边看边存
楼主,我也想要一份源码。正在学习写流媒体播放器,希望参考学习。我的QQ邮箱是378532514@qq.com。谢谢了!
2012-04-08 21:29有重大BUG 你播放是在UI线程执行的 可能会ANR
2012-05-09 09:04最好放在异步线程操作
1343066628@qq.com
2012-05-25 00:20前辈。
我正在学习安卓开发,还能否给我发一份您这个的源码,感谢
[reply]wxc_1990[/reply]
2012-05-27 10:00哥,代码都在这里了,你就抽空复制黏贴吧
楼至,有没有试过,videoview在2.1上播放媒体的时候,,播发完毕后,左边的时间还在走,,不会去触发完成事件onCompletion()
2012-06-09 11:15[reply]ZZZ123098[/reply]
2012-06-11 12:58我一般使用定时器,当currentposition跟duration差不多的时候,就回调自己实现的oncompletion,自己实现结束,这样靠谱多了
[reply]hellogv[/reply]
2012-06-12 15:10我可以按照你说的方法尝试一下。但是我不明白VideoView不就是使用MediaPlayer来实现的吗??当我使用单独的MediaPlayer()去播放视频的时候,是可以触发的onCompletion()
,但是我要是在添加上surfaceview去显示出来的时候。就不在调用OnCompetion()方法了。为什么呢??楼主有时间能发研究一下呢,讲解一下呢??谢谢。。
[reply]ZZZ123098[/reply]
2012-06-12 19:45MediaPlayer回调OnCompetion的情况很玄,这跟硬件厂家有关
[reply]hellogv[/reply]
2012-06-14 13:00哦,,谢谢。。那你有没有在4.0上面播放过视频呢??
[reply]ZZZ123098[/reply]
2012-06-17 18:59在i9100 android 4.0上播放过
test_videoplayer 类的oncreate方法中调用
String url="http://daily3gp.com/vids/family_guy_penis_car.3gp";
player.playUrl(url);
发生空对象错误,我想一启动这个 test_videoplayer 时播放视频
2012-06-26 17:46[reply]cas1991321[/reply]
2012-06-30 08:11或许视频链接无效了
android的这个类在播放的时候太脆弱了,很多东西都是提示无法播放的。
2012-07-21 16:51lz大哥,为什么我是按照你的步骤来的,怎么还有空指针异常啊???
2012-07-24 14:36E/AndroidRuntime(1097): at E/AndroidRuntime(1097): FATAL EXCEPTION: main
2012-07-24 15:18E/AndroidRuntime(1097): java.lang.NullPointerException
E/AndroidRuntime(1097): at com.android.androidvideo.MainActivity$ClickEvent.onClick(MainActivity.java:51)
E/AndroidRuntime(1097): at android.view.View.performClick(View.java:3480)
E/AndroidRuntime(1097): at android.view.View$PerformClick.run(View.java:13983)
E/AndroidRuntime(1097): at android.os.Handler.handleCallback(Handler.java:605)
E/AndroidRuntime(1097): at android.os.Handler.dispatchMessage(Handler.java:92)
E/AndroidRuntime(1097): at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4340)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
E/AndroidRuntime(1097): 这是我报的错
你好,我用mediaplayer播放rtsp网络文件,结果android 4.0的可以放,android 2.3的返回的width和heigth都为0,这个什么原因啊。
2012-07-30 14:02请问为什么我用mediaplayer播放rtsp时用4.0的机子没问题,用2.3的机子就播放不了啊。。
2012-07-31 09:54