橘子味的心
标题:6.5 Android视频

首先我们来看一下 Android N 支持的视频文件有哪些。Android N 支持的视频格式下表所示。

Android N 支持的视频文件
格式/编码 支持的文件类型
H.263 3GPP(.3gp);MpEG-4(.mp4)
H.264 AVC 3GPP(.3gp);MpEG-4(.mp4)
MPEG-TS(.ts,AAC audio only,not seekable,Android 3.0+)
MPEG-4 SP 3GPP(.3gp)
VP8 WebM(.webm);Matroska mkv

视频播放器

与音频播放相比,视频播放需要使用视觉组件将影像显示出来。

在 Android SDK 中提供了多种播放视频文件的方法。例如,可以用 VideoView 或 SurfaceView 来播放视频,其中使用 VideoView 组件播放视频最为方便。

实例 VideoPlayDemo 演示了使用 android.widget.VideoView 组件进行视频播放的方法,运行效果如图 1 所示。

VideoPlayDemo运行结果
图 1  VideoPlayDemo 的运行效果

实例 VideoPlayDemo 中含有两个 Activity,其中 PlayVideo 含有 VideoView 组件对象,用于播放视频。视频文件存放在 SD 卡中,路径为“Movies/movie.3gp”。而 VideoPlayAcitvity 为主 Activity,用于启动 PlayVideo。

实例 VideoPlayDemo 中 MainActivity.java 的代码如下:
  1. package introduction.android.videoplaydemo;
  2.  
  3.  
  4. import android.app.Activity;
  5. import android.content.Intent;
  6.  
  7. import android.os.Bundle;
  8. import android.view.View;
  9.  
  10. import android.view.View.OnClickListener;
  11. import android.widget.Button;
  12.  
  13. public class MainAcitvity extends Activity {
  14.  
  15. /**
  16. * Called when the activity is first created.
  17. */
  18. private Button buttonOl;
  19.  
  20. @Override
  21.  
  22. public void onCreate(Bundle savedInstanceState) {
  23. super.onCreate(savedInstanceState);
  24. setContentView(R.layout.main);
  25. button01 = (Button) findViewById(R.id.buttonOl);
  26. button0l.setOnClickListener(new buttonListener());
  27. }
  28.  
  29. class buttonListener implements OnClickListener {
  30. @Override
  31. public void onClick(View v) {
  32. // TODO Auto-generated method stub
  33. Intent intent = new Intent(MainActivity.this, PlayVideo.class);
  34. MainAcitvity.this.startActivity(intent);
  35.  
  36. }
  37.  
  38. }
  39. }
实例 VideoPlayDemo 中 PlayVideo.java 的代码如下:
  1. package introduction.android.videoplaydemo;
  2.  
  3.  
  4. import android.app.Activity;
  5. import android.media.MediaPlayer;
  6. import android.net.Uri;
  7. import android.os.Bundle;
  8. import android.widget.MediaController;
  9. import android.widget.Toast;
  10. import android.widget.VideoView;
  11.  
  12. public class PlayVideo extends Activity {
  13. private VideoView videoView;
  14. private MediaController mc;
  15. private String path;
  16.  
  17. @Override
  18.  
  19. public void onCreate(Bundle savedInstanceState) {
  20. super.onCreate(savedInstanceState);
  21. setContentView(R.layout.other);
  22.  
  23. videoView = (VideoView) this.findViewById(R.id.videoView);
  24. path = "sdcard/Movies/movie.3gp";
  25. mc = new MediaController(this);
  26. videoView.setMediaController(mc);
  27. videoView.setVideoPath(path);
  28. videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
  29. @Override
  30. public void onCompletion(MediaPlayer argO) {
  31. // TODO Auto-generated method stub
  32. finish();
  33. }
  34. });
  35. videoView.requestFocus();
  36. videoView.start();
  37. }
  38. }
其中,MediaController 类为 Android SDK 提供的视频控制器,用于显示播放时间,对播放视频进行控制。

通过 VideoView 类的 setMediaController() 方法可以将视频控制器和 VideoView 类结合在一起,对 VideoView 中播放的视频进行控制,大大降低了编码强度。

由于要播放的视频为放置在 SD 卡中的“Movies/movie.3gp”文件,VideoView 组件使用 setVideoPath() 方法即可指定该文件,并通过 start() 方法进行播放。
  1. videoView.setOnCompletionListener(new OnCompletionListener(){
  2. @Override
  3. public void onCompletion(MediaPlayer argO) {
  4. // TODO Auto-generated method stub
  5. finish();
  6. }
  7. })
这行代码指定了 videoView 组件的视频播放完成事件的触发器,当视频播放完成后,关闭当前 Activity。

PlayVideo 使用的布局为 R.layout.other,该布局中含有 VideoView 组件,其所对应的 XML 文件 other.xml 的代码如下:
  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. <VideoView
  7. android:id="@+id/videoView"
  8. android:layout_width="320px"
  9. android:layout_height="240px" />
  10. </LinearLayout>
实例 VideoPlayDemo 中 AndroidManifest.xml 文件的代码如下:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="introduction.android.videoplaydemo">
  4.  
  5. <application
  6. android:allowBackup="true"
  7. android:icon="@mipmap/ic_launcher"
  8. android:label="@string/app_name"
  9. android:roundIcon="@mipmap/ic_launcher_round"
  10. android:supportsRtl="true"
  11. android:theme="@style/AppTheme">
  12. <activity android:name=".MainActivity">
  13. <intent-filter>
  14. <action android:name="android.intent.action.MAIN" />
  15. <category android:name="android.intent.category.LAUNCHER" />
  16. </intent-filter>
  17. </activity>
  18. <activity android:name="introduction.android.playvideo.PlayVideo" />
  19. </application>
  20.  
  21. </manifest>
此外,VideoView 也支持网络流媒体的播放,只需将 VideoView 的 setVideoPath() 方法替换为 setViewURI(),并指定对应的 URI 即可。

需要注意的是,并不是所有的 MP4 和 3GP 文件都可以被 VideoView 组件播放,只有使用 progressive streamable 模式转化的影片才可以被播放。

播放网络流媒体文件时,需要在 AndroidManifest.xml 文件中添加相应权限:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>

  • android.permission.INTERNET 权限使当前应用程序可以访问网络资源;
  • android. permission.WAKE_LOCK 权限使当前应用程序运行时,手机不会进入休眠状态,以便于视频播放。

使用 SurfaceView 组件播放视频的方法也不复杂,而且更加灵活。

实例 MediaPlayerVideoDemo 演示了使用 SurfaceView 和 MediaPlayer 组件播放视频的方法,运行效果如图 2 所示。

MediaPlayerVideoDemo运行结果
图 2  MediaPlayerVideoDemo的运行效果
对应的布局文件 main.xml 的内容如下:
  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.  
  7. <SurfaceView
  8. android:id="@+id/surfaceView1"
  9. android:layout_width="fill_parent"
  10. android:layout_height="wrap_content"
  11. android:layout_weight="1.01" />
  12.  
  13. <LinearLayout
  14. android:id="@+id/linearLayout1"
  15. android:layout_width="match_parent"
  16. android:layout_height="wrap_content"
  17. android:gravity="center">
  18.  
  19. <Button
  20. android:id="@+id/button1"
  21. android:layout_width="wrap_content"
  22. android:layout_height="wrap_content"
  23. android:text="播放" />
  24.  
  25. <Button
  26. android:id="@+id/button2"
  27. android:layout_width="wrap_content"
  28. android:layout_height="wrap_content"
  29. android:text="暂停" />
  30.  
  31. <Button
  32. android:id="@+id/button3"
  33. android:layout_width="wrap_content"
  34. android:layout_height="wrap_content"
  35. android:text="重置" />
  36.  
  37. <Button
  38. android:id="@+id/button4"
  39. android:layout_width="wrap_content"
  40. android:layout_height="wrap_content"
  41. android:text="停止" />
  42. </LinearLayout>
  43. </LinearLayout>
实例 MediaPlayerVideoDemo 的配置文件 AndroidManifest.xml 的内容如下:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="introduction.android.videoPlayDemo"
  4. android:versionCode="1"
  5. android:versionName="1.0">
  6.  
  7. <uses-sdk android:minSdkVersion="14" />
  8. <application
  9. android:icon="@drawable/ic_launcher"
  10. android:label="@string/app_name">
  11. <activity
  12. android:name=".VideoPlayDemoActivity"
  13. android:label="@string/app_name">
  14. <intnt-filter>
  15. <action android:name="android.intent.action.MAIN" />
  16. <category android:name="android.intent.category.LAUNCHER" />
  17. </intnt-filter>
  18. </activity>
  19. </application>
  20. </manifest>
实例 MediaPlayerVideoDemo 中的主 Activity 文件MainActivity.Java 的代码如下:
  1. package introduction.android.videoplaydemo;
  2.  
  3. import java.io.IOException;
  4.  
  5. import android.app.Activity;
  6. import android.media.AudioManager;
  7. import android.media.MediaPlayer;
  8. import android.os.Bundle;
  9. import android.util.Log;
  10. import android.view.SurfaceHolder;
  11. import android.view.SurfaceView;
  12. import android.view.View;
  13.  
  14. import android.view.View.OnClickListener;
  15. import android.widget.Button;
  16.  
  17. public class MainActivity extends Activity {
  18.  
  19. /**
  20. * Called when the activity is first created.
  21. */
  22. private Button playbtn;
  23. private Button pausebtn;
  24. private Button replaybtn;
  25. private Button stopbtn;
  26. private SurfaceView surview;
  27. private SurfaceHolder surHolder;
  28. private MediaPlayer mp;
  29.  
  30. private String path = "sdcard/movies/movie.3gp";
  31. protected boolean pause = false;
  32.  
  33. @Override
  34. public void onCreate(Bundle savedInstanceState) {
  35. super.onCreate(savedInstanceState);
  36. setContentView(R.layout.main);
  37.  
  38. surview = (SurfaceView) this.findViewById(R.id.surfaceView1);
  39. surHolder = surview.getHolder();
  40. surHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
  41. mp = new MediaPlayer();
  42. mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
  43. @Override
  44. public void onCompletion(MediaPlayer mp) {
  45. // TODO Auto-generated method stub
  46. Log.i("mediaplayer", "播放完成");
  47. }
  48. });
  49. playbtn = (Button) this.findViewById(R.id.buttonl);
  50. playbtn.setOnClickListener(new OnClickListener() {
  51. @Override
  52. public void onClick(View argO) {
  53. // TODO Auto-generated method stub
  54. if (!pause) {
  55. //开始播放
  56. mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
  57. mp.setDisplay(surHolder);
  58. try {
  59. mp.setDataSource(path);
  60. mp.prepare();
  61. mp.start();
  62. } catch (IOException e) {
  63. // TODO Auto-generated catch block
  64. e.printStackTrace();
  65. }
  66. } else {
  67. //暂停播放
  68. mp.start();
  69. pause = false;
  70. }
  71. }
  72. });
  73. pausebtn = (Button) this.findViewById(R.id.button2);
  74. pausebtn.setOnClickListener(new OnClickListener() {
  75. //暂停播放
  76. @Override
  77. public void onClick(View argO) {
  78. // TODO Auto-generated method stub
  79. if (mp != null) {
  80. pause = true;
  81. mp.pause();
  82. }
  83. }
  84. });
  85. replaybtn = (Button) this.findViewById(R.id.button3);
  86. replaybtn.setOnClickListener(new OnClickListener() {
  87. //重新播放
  88. @Override
  89. public void onClick(View argO) {
  90. // TODO Auto-generated method stub
  91. if (mp != null) {
  92. mp.seekTo(0);
  93. }
  94. }
  95. });
  96. stopbtn = (Button) this.findViewById(R.id.button4);
  97. stopbtn.setOnClickListener(new OnClickListener() {
  98. @Override
  99. public void onClick(View argO) {
  100. // TODO Auto-generated method stub
  101. if (mp != null) {
  102. mp.stop();
  103. mp.release();
  104. }
  105. }
  106. });
  107. }
  108. }