MediaPlayer

MediaPlayer类是Androd多媒体框架中的一个重要组件,通过该类,我们可以以最小的步骤来获取,解码和播放音视频。它支持三种不同的媒体来源:

  • 本地资源
  • 内部URI,比如你可以通过ContentResolver来获取
  • 外部URL(流)

对于Android支持的媒体格式列表,可见:Supported Media Formats文档

在播放网络上的视频流时,Android原生的MediaPlayer支持两种协议,HTTP和RTSP,这两种协议最大的不同是,RTSP协议支持实时流媒体的播放,而HTTP协议不支持。因为VideoView的底层实现是MediaPlayer,因此VideoView也支持以上两种协议。 
但是Android原生MediaPalyer支持的协议(不支持RTMP、MMS等)和封装格式实在太有限了,如果我们想播放那些它不支持的视频,这时候就需要第三方播放器了,很多第三方播放器的底层实现都是基于FFmpeg,后续我会专门来讲讲第三方播放器和FFmpeg的使用。

相关方法详解

(1)获得MediaPlayer实例: 
可以直接new或者调用create方法创建:

  1. MediaPlayer mp = new MediaPlayer();
  2. MediaPlayer mp = MediaPlayer.create(this, R.raw.test); //无需再调用setDataSource

另外create还有这样的形式:

  1. create(Context context, Uri uri, SurfaceHolder holder)

通过Uri和指定 SurfaceHolder 【抽象类】 创建一个多媒体播放器。

(2)设置播放文件:

  1. MediaPlayer.create(this, R.raw.test); //①raw下的资源
  2. mp.setDataSource("/sdcard/test.mp3"); //②本地文件路径
  3. mp.setDataSource("http://www.xxx.com/music/test.mp3");//③网络URL文件

另外setDataSource()方法有多个,里面有这样一个类型的参数:FileDescriptor 
在使用这个API的时候,需要把文件放到res文件夹平级的assets文件夹里,然后使用下述代码设置DataSource:

  1. AssetFileDescriptor fileDescriptor = getAssets().openFd("rain.mp3");
  2. mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(), fileDescriptor.getLength());

(3)其他方法

  1. getCurrentPosition( ):得到当前的播放位置
  2. getDuration() :得到文件的时间
  3. getVideoHeight() :得到视频高度
  4. getVideoWidth() :得到视频宽度
  5. isLooping():是否循环播放
  6. isPlaying():是否正在播放
  7. pause():暂停
  8. prepare():准备(同步)
  9. prepareAsync():准备(异步)
  10. release():释放MediaPlayer对象
  11. reset():重置MediaPlayer对象
  12. seekTo(int msec):指定播放的位置(以毫秒为单位的时间)
  13. setAudioStreamType(int streamtype):指定流媒体的类型
  14. setDisplay(SurfaceHolder sh):设置用SurfaceHolder来显示多媒体
  15. setLooping(boolean looping):设置是否循环播放
  16. setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener):网络流媒体的缓冲监听
  17. setOnCompletionListener(MediaPlayer.OnCompletionListener listener):网络流媒体播放结束监听
  18. setOnErrorListener(MediaPlayer.OnErrorListener listener):设置错误信息监听
  19. setOnVideoSizeChangedListener(MediaPlayer.OnVideoSizeChangedListener listener):视频尺寸监听
  20. setScreenOnWhilePlaying(boolean screenOn):设置是否使用SurfaceHolder显示
  21. setVolume(float leftVolume, float rightVolume):设置音量
  22. start():开始播放
  23. stop():停止播放

生命周期

  • 状态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. @Override
  2. public void onClick(View v) {
  3. switch (v.getId()){
  4. case R.id.btn_play:
  5. if(isRelease){
  6. mPlayer = MediaPlayer.create(this,R.raw.fly);
  7. isRelease = false;
  8. }
  9. mPlayer.start(); //开始播放
  10. break;
  11. case R.id.btn_pause:
  12. mPlayer.pause(); //停止播放
  13. break;
  14. case R.id.btn_stop:
  15. mPlayer.reset(); //重置MediaPlayer
  16. mPlayer.release(); //释放MediaPlayer
  17. isRelease = true;
  18. break;
  19. }

注意事项: 
播放的是res/raw目录下的音频文件,创建MediaPlayer调用的是create方法,第一次启动播放前不需要再调用prepare(),如果是使用构造方法构造的话,则需要调用一次prepare()方法! 
另外贴下官方文档中,从其他两种途径播放音频的示例代码: 
本地Uri:

  1. Uri myUri = ....; /**initialize Uri here*/
  2. MediaPlayer mediaPlayer = new MediaPlayer();
  3. mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
  4. mediaPlayer.setDataSource(getApplicationContext(), myUri);
  5. mediaPlayer.prepare();
  6. mediaPlayer.start();

外部URL:

  1. String url = "http://........"; // your URL here
  2. MediaPlayer mediaPlayer = new MediaPlayer();
  3. mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
  4. mediaPlayer.setDataSource(url);
  5. mediaPlayer.prepare(); // might take long! (for buffering, etc)
  6. mediaPlayer.start();

Note:假如你通过一个URL以流的形式播放在线音频文件,该文件必须可以进行渐进式下载。

使用MediaPlayer + SurfaceView播放视频

MediaPlayer主要用于播放音频,没有提供图像输出界面,所以我们需要借助其他的组件来显示MediaPlayer播放的图像输出,我们可以使用SurfaceView来显示。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:orientation="vertical" >
  6. <SurfaceView
  7. android:id="@+id/surfaceView"
  8. android:layout_width="fill_parent"
  9. android:layout_height="300dp" />
  10. <LinearLayout
  11. android:layout_width="fill_parent"
  12. android:layout_height="wrap_content"
  13. android:gravity="center_horizontal"
  14. android:orientation="horizontal" >
  15. <ImageButton
  16. android:id="@+id/btnplay"
  17. android:layout_width="60dp"
  18. android:layout_height="60dp"
  19. android:background="@mipmap/play"/>
  20. <ImageButton
  21. android:id="@+id/btnpause"
  22. android:layout_width="60dp"
  23. android:layout_height="60dp"
  24. android:background="@mipmap/pause"/>
  25. <ImageButton
  26. android:id="@+id/btnstop"
  27. android:layout_width="60dp"
  28. android:layout_height="60dp"
  29. android:background="@mipmap/stop"/>
  30. </LinearLayout>
  31. </LinearLayout>
  1. public class MediaPlayerActivity extends Activity implements View.OnClickListener {
  2. private ImageButton btnplay, btnstop, btnpause;
  3. private SurfaceView surfaceView;
  4. private MediaPlayer mediaPlayer;
  5. private int position;
  6. private String url1 = "http://flashmedia.eastday.com/newdate/news/2016-11/shznews1125-19.mp4";
  7. private String url2 = "rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov";
  8. private String url3 = "http://42.96.249.166/live/388.m3u8";
  9. private String url4 = "http://61.129.89.191/ThroughTrain/download.html?id=4035&flag=-org-"; //音频url
  10. public void onCreate(Bundle savedInstanceState) {
  11. super.onCreate(savedInstanceState);
  12. setContentView(R.layout.activity_mediaplayer);
  13. btnplay = (ImageButton) this.findViewById(R.id.btnplay);
  14. btnstop = (ImageButton) this.findViewById(R.id.btnstop);
  15. btnpause = (ImageButton) this.findViewById(R.id.btnpause);
  16. btnstop.setOnClickListener(this);
  17. btnplay.setOnClickListener(this);
  18. btnpause.setOnClickListener(this);
  19. mediaPlayer = new MediaPlayer();
  20. surfaceView = (SurfaceView) this.findViewById(R.id.surfaceView);
  21. // 设置SurfaceView自己不管理的缓冲区
  22. surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
  23. surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
  24. @Override
  25. public void surfaceDestroyed(SurfaceHolder holder) {
  26. }
  27. @Override
  28. public void surfaceCreated(SurfaceHolder holder) {
  29. if (position > 0) {
  30. try {
  31. // 开始播放
  32. play();
  33. // 并直接从指定位置开始播放
  34. mediaPlayer.seekTo(position);
  35. position = 0;
  36. } catch (Exception e) {
  37. }
  38. }
  39. }
  40. @Override
  41. public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
  42. }
  43. });
  44. }
  45. @Override
  46. public void onClick(View v) {
  47. switch (v.getId()) {
  48. case R.id.btnplay:
  49. play();
  50. break;
  51. case R.id.btnpause:
  52. if (mediaPlayer.isPlaying()) {
  53. mediaPlayer.pause();
  54. } else {
  55. mediaPlayer.start();
  56. }
  57. break;
  58. case R.id.btnstop:
  59. if (mediaPlayer.isPlaying()) {
  60. mediaPlayer.stop();
  61. }
  62. break;
  63. default:
  64. break;
  65. }
  66. }
  67. @Override
  68. protected void onPause() {
  69. // 先判断是否正在播放
  70. if (mediaPlayer.isPlaying()) {
  71. // 如果正在播放我们就先保存这个播放位置
  72. position = mediaPlayer.getCurrentPosition();
  73. mediaPlayer.stop();
  74. }
  75. super.onPause();
  76. }
  77. private void play() {
  78. try {
  79. mediaPlayer.reset();
  80. mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
  81. // 设置需要播放的视频
  82. Uri uri = Uri.parse(url1);
  83. mediaPlayer.setDataSource(getApplicationContext(), uri);
  84. // 把视频画面输出到SurfaceView
  85. mediaPlayer.setDisplay(surfaceView.getHolder());
  86. mediaPlayer.prepare();
  87. // 播放
  88. mediaPlayer.start();
  89. Toast.makeText(this, "开始播放!", Toast.LENGTH_LONG).show();
  90. } catch (Exception e) {
  91. }
  92. }
  93. }

代码很简单,布局有个SurfaceView,然后调用getHolder获得一个SurfaceHolder对象,在这里完成SurfaceView相关的设置,设置了类型以及一个Callback接口,重写了SurfaceView创建时,发生变化时,以及销毁时的三个方法!然后按钮控制播放、暂停以及停止而已。 

注:这里视频宽高总是等于定义的SurfaceView布局宽高,所以视频可能会被拉伸变形。那么如何解决呢?

  1. ...
  2. mMediaPlayer.setOnVideoSizeChangedListener(new OnVideoSizeChangedListener(){
  3. @Override
  4. public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
  5. if (width == 0 || height == 0) {
  6. Log.e(TAG, "invalid video width(" + width + ") or height(" + height + ")");
  7. return;
  8. }
  9. mIsVideoSizeKnown = true;
  10. mVideoWidth = width;
  11. mVideoHeight = height;
  12. if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
  13. startVideoPlayback();
  14. }
  15. }
  16. });
  17. mMediaPlayer.setOnPreparedListener(new OnPreparedListener(){
  18. @Override
  19. public void onPrepared(MediaPlayer mp) {
  20. mIsVideoReadyToBePlayed = true;
  21. if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
  22. startVideoPlayback();
  23. }
  24. }
  25. });
  26. private void startVideoPlayback() {
  27. holder.setFixedSize(mVideoWidth, mVideoHeight);
  28. mMediaPlayer.start();
  29. }

VideoView

除了使用MediaPlayer + SurfaceView播放视频的方式,我们还可以使用VideoView来直接播放视频。SurfaceView播放视频时,如果不进行设置,视频宽高总是等于定义的SurfaceView布局宽高,所以视频可能会被拉伸变形。而使用VideoView时,视频宽度等于VideoView布局宽,但是高是自适应的,自动调整宽高比到视频原始比例,所以不会有拉伸。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. android:id="@+id/activity_main"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. android:paddingBottom="@dimen/activity_vertical_margin"
  8. android:paddingLeft="@dimen/activity_horizontal_margin"
  9. android:paddingRight="@dimen/activity_horizontal_margin"
  10. android:paddingTop="@dimen/activity_vertical_margin"
  11. tools:context="com.hx.mediaplay.MainActivity"
  12. android:orientation="vertical">
  13. <Button
  14. android:id="@+id/go"
  15. android:layout_width="match_parent"
  16. android:layout_height="wrap_content"
  17. android:text="mediaPlayer+sufaceview" />
  18. <EditText
  19. android:id="@+id/url"
  20. android:layout_width="match_parent"
  21. android:layout_height="wrap_content"
  22. android:hint="url" />
  23. <Button
  24. android:id="@+id/play"
  25. android:layout_width="match_parent"
  26. android:layout_height="wrap_content"
  27. android:text="Play" />
  28. <VideoView
  29. android:id="@+id/video"
  30. android:layout_width="match_parent"
  31. android:layout_height="match_parent"
  32. android:layout_centerInParent="true" />
  33. </LinearLayout>
  1. public class MainActivity extends AppCompatActivity {
  2. private Button go;
  3. private EditText url;
  4. private Button button;
  5. private VideoView videoview;
  6. private MediaController mMediaController;
  7. private String url1 = "http://flashmedia.eastday.com/newdate/news/2016-11/shznews1125-19.mp4";
  8. private String url2 = "rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov";
  9. private String url3 = "http://42.96.249.166/live/388.m3u8";
  10. private String url4 = "http://61.129.89.191/ThroughTrain/download.html?id=4035&flag=-org-"; //音频url
  11. @Override
  12. protected void onCreate(Bundle savedInstanceState) {
  13. super.onCreate(savedInstanceState);
  14. setContentView(R.layout.activity_main);
  15. go = (Button) findViewById(R.id.go);
  16. url = (EditText) findViewById(R.id.url);
  17. button = (Button) findViewById(R.id.play);
  18. videoview = (VideoView) findViewById(R.id.video);
  19. mMediaController = new MediaController(this);
  20. videoview.setMediaController(mMediaController);
  21. url.setText(url1);
  22. button.setOnClickListener(new View.OnClickListener(){
  23. @Override
  24. public void onClick(View v) {
  25. loadView(url.getText().toString());
  26. }
  27. });
  28. go.setOnClickListener(new View.OnClickListener(){
  29. @Override
  30. public void onClick(View v) {
  31. Intent intent = new Intent(MainActivity.this, MediaPlayerActivity.class);
  32. startActivity(intent);
  33. }
  34. });
  35. }
  36. public void loadView(String path) {
  37. Uri uri = Uri.parse(path);
  38. videoview.setVideoURI(uri);
  39. videoview.start();
  40. videoview.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
  41. @Override
  42. public void onPrepared(MediaPlayer mp) {
  43. // mp.setLooping(true);
  44. mp.start();// 播放
  45. Toast.makeText(MainActivity.this, "开始播放!", Toast.LENGTH_LONG).show();
  46. }
  47. });
  48. videoview.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
  49. @Override
  50. public void onCompletion(MediaPlayer mp) {
  51. Toast.makeText(MainActivity.this, "播放完毕", Toast.LENGTH_SHORT).show();
  52. }
  53. });
  54. }
  55. }

Demo下载地址

Android MediaPlayer和VideoView的使用的更多相关文章

  1. Android:视频(VideoView/MediaPlayer)

    Android之视频播放 VideoView if(android.os.Environment.getExternalStorageState().equals(android.os.Environ ...

  2. Android 视频播放器 VideoView 的使用,播放本地视频 和 网络 视频

    1.布局文件 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:and ...

  3. Android MediaPlayer架构 -- MediaPlayer的创建过程

    本文系作者自己学习之所用,文章内容仅出自作者拙劣之思考,问题之处烦请不吝指教. MediaPlayer 能被用来控制音/视频文件或流媒体的回放.Android中以MediaPlayer类作为音视频播放 ...

  4. Android视频播放之VideoView

    Android视频播放之VideoView 1.VideoView类介绍 Android的VideoView组件可以从不同的来源(例如资源文件或内容提供器)读取图像,计算和维护视频的画面尺寸以使其适用 ...

  5. 第一部分 Android MediaPlayer 概述

    [IT168 技术文档]本文主要介绍的是Android中很重要也最为复杂的媒体播放器(MediaPlayer)部分的架构.对于Android这样一个完整又相对复杂的系统,一个MediaPlayer功能 ...

  6. Android MediaPlayer Error/Info Code

    1. 常见错误 error(-38, 0) 我觉得-38表示在当前的MediaPlayer状态下,不能运行你的操作. 详细怎样做请參考:Android MediaPlayer 另外我在其它资料中.发现 ...

  7. Android(java)学习笔记180:Android MediaPlayer 播放prepareAsync called in state 8解决办法

    使用android MediaPlayer播放音频文件时,有时会出现prepareasync called in state 8错误. 以下方法可以避免这个异常出现.  第一种方法: private ...

  8. Android MediaPlayer接口及状态迁移

    [时间:2016-09] [状态:Open] [关键词:android,mediaplayer,播放接口,播放状态图] 引言 本文内容相对简单,作为后续处理的起点,简要整理了Android Media ...

  9. Android MediaPlayer 常用方法介绍

    Android MediaPlayer 常用方法介绍 方法:create(Context context, Uri uri) 解释:静态方法,通过Uri创建一个多媒体播放器. 方法:create(Co ...

随机推荐

  1. iOS的settings bundle中开关button(Toggle Switch)取不到值的问题

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 假设认为写的不好请多提意见,假设认为不错请多多支持点赞.谢谢! hopy ;) 在Xcode7.2中设置App的settings bundle ...

  2. Office办公 WPS如何设置页边距

    打开页眉页脚,在选项里面可以设置顶部的一行文字距离边界的距离   此外在页面布局,页边距也可以查看和修改                        

  3. javascript获取和设置URL中的参数

    勘误版 function getQuery(key, url) { url = url || window.location.href; if (url.indexOf('#') !== -1) ur ...

  4. LintCode: Identical Binary Tree

    C++ /** * Definition of TreeNode: * class TreeNode { * public: * int val; * TreeNode *left, *right; ...

  5. 如何随机获取数据库不连续ID的数据?

    这个问题的来由是我朋友要为一网站实现一个标签云功能,和我交流后我给出了一个方案,在此略作记录,亦求拍砖. 大概需求这是样的: 在数据库有一张表A如下图: 其中id字段的值未必是连续的,现在我朋友要做的 ...

  6. intel vt

    EPT和VPID技术是内存虚拟化技术, 是页表扩充技术Extended Page Table (EPT) 的缩写, 是VT-x技术的一部分. 内存虚拟化的主要任务是实现地址空间的虚拟化,内存虚拟化是通 ...

  7. HDS推出HUS中端阵列 文件、块和对象统一存储

    http://storage.chinabyte.com/86/12320086.shtml http://storage.chinabyte.com/134/12324134.shtml 日立数据系 ...

  8. Hive表的建立和导入导出数据

    Hive是Hadoop的常用工具之一,Hive查询语言(HiveQL)的语法和SQL类似,基本实现了SQL-92标准. 1. 表的建立 编写以下的文件: USE test; DROP TABLE IF ...

  9. 算法笔记_225:数字密码发生器(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 在对银行账户等重要权限设置密码的时候,我们常常遇到这样的烦恼:如果为了好记用生日吧,容易被破解,不安全:如果设置不好记的密码,又担心自己也会忘记:如 ...

  10. python之tcp自动重连

    操作系统: CentOS 6.9_x64 python语言版本: 2.7.13 问题描述 现有一个tcp客户端程序,需定期从服务器取数据,但由于种种原因(网络不稳定等)需要自动重连. 测试服务器示例代 ...