Android視訊播放實現的三種辦法
今天來說一下Android中怎麼實現視訊播放,我主要說三種:
1.MediaPlayer+SurfaceView;
2.VideoView;
3.Vitamio框架。
1.MediaPlayer+SurfaceView這種方法是基礎,後面的兩種方法其實就是把這種方法封裝了一下,使用起來更方便些。
我認為大家基本都會用MediaPlayer這個API,我們都知道MediaPlayer是媒體播放器,可以播放音訊,視訊其實就是給音訊配上影像,而SurfaceView就是給音訊配上影像的工具,我們只需要把SurfaceView與MediaPlayer關聯起來就行了。
主要程式碼:
SurfaceHolderholder = SurfaceView.getHolder();
MediaPlayer.setDisplay(holder);//將影像播放控制元件與媒體播放控制元件關聯起來我認為之前不知道的程式碼可能只有這兩句。
佈局程式碼:
效果圖
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main"android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.video.MainActivity" android:layout_margin="10dp" android:orientation="vertical"> <EditText android:id="@+id/et" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="請輸入檔名稱例如:aa.mp4,務必確保檔案放在sdcard目錄下"/> <SurfaceView android:id="@+id/sfv" android:layout_width="match_parent" android:layout_marginTop="10dp" android:layout_height="200dp" /> <SeekBar android:id="@+id/sb" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:orientation="horizontal"> <Button android:id="@+id/play" android:layout_width="0dp" android:layout_height="60dp" android:layout_weight="1" android:onClick="play" android:text="播放"/> <Button android:id="@+id/pause" android:layout_width="0dp" android:layout_height="60dp" android:layout_marginLeft="10dp" android:layout_weight="1" android:onClick="pause" android:text="暫停"/> <Button android:layout_width="0dp" android:layout_height="60dp" android:layout_marginLeft="10dp" android:layout_weight="1" android:onClick="stop" android:text="停止"/> <Button android:layout_width="0dp" android:layout_height="60dp" android:layout_marginLeft="10dp" android:layout_weight="1" android:onClick="replay" android:text="重播"/> </LinearLayout> </LinearLayout>
完整程式碼+註釋:
package com.example.video; import android.app.Activity; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Environment; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.SeekBar; import android.widget.Toast; import java.io.File; import java.io.IOException; import java.util.Timer; import java.util.TimerTask; /** * 需要注意的是我實驗了.mp4,3gp和avi3種格式的視訊,在5.1的真機和模擬器上avi格式都是隻有聲音沒有影像,其他兩種格式 * 播放正常。 */ public class MainActivity extends Activity { private SurfaceView sfv;//能夠播放影象的控制元件 private SeekBar sb;//進度條 private String path ;//本地檔案路徑 private SurfaceHolder holder; private MediaPlayer player;//媒體播放器 private Button Play;//播放按鈕 private Timer timer;//定時器 private TimerTask task;//定時器任務 private int position = 0; private EditText et; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } //初始化控制元件,並且為進度條和影象控制元件新增監聽 private void initView() { sfv = (SurfaceView) findViewById(R.id.sfv); sb = (SeekBar) findViewById(R.id.sb); Play = (Button) findViewById(R.id.play); et = (EditText) findViewById(R.id.et); Play.setEnabled(false); holder = sfv.getHolder(); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { //當進度條停止拖動的時候,把媒體播放器的進度跳轉到進度條對應的進度 if (player != null) { player.seekTo(seekBar.getProgress()); } } }); holder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { //為了避免影象控制元件還沒有建立成功,使用者就開始播放視訊,造成程式異常,所以在建立成功後才使播放按鈕可點選 Log.d("zhangdi","surfaceCreated"); Play.setEnabled(true); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.d("zhangdi","surfaceChanged"); } @Override public void surfaceDestroyed(SurfaceHolder holder) { //當程式沒有退出,但不在前臺執行時,因為surfaceview很耗費空間,所以會自動銷燬, // 這樣就會出現當你再次點選程序序的時候點選播放按鈕,聲音繼續播放,卻沒有影象 //為了避免這種不友好的問題,簡單的解決方式就是隻要surfaceview銷燬,我就把媒體播放器等 //都銷燬掉,這樣每次進來都會重新播放,當然更好的做法是在這裡再記錄一下當前的播放位置, //每次點選進來的時候把位置賦給媒體播放器,很簡單加個全域性變數就行了。 Log.d("zhangdi","surfaceDestroyed"); if (player != null) { position = player.getCurrentPosition(); stop(); } } }); } private void play() { Play.setEnabled(false);//在播放時不允許再點選播放按鈕 if (isPause) {//如果是暫停狀態下播放,直接start isPause = false; player.start(); return; } path = Environment.getExternalStorageDirectory().getPath()+"/"; path = path + et.getText().toString();//sdcard的路徑加上檔名稱是檔案全路徑 File file = new File(path); if (!file.exists()) {//判斷需要播放的檔案路徑是否存在,不存在退出播放流程 Toast.makeText(this,"檔案路徑不存在",Toast.LENGTH_LONG).show(); return; } try { player = new MediaPlayer(); player.setDataSource(path); player.setDisplay(holder);//將影像播放控制元件與媒體播放控制元件關聯起來 player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) {//視訊播放完成後,釋放資源 Play.setEnabled(true); stop(); } }); player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { //媒體播放器就緒後,設定進度條總長度,開啟計時器不斷更新進度條,播放視訊 Log.d("zhangdi","onPrepared"); sb.setMax(player.getDuration()); timer = new Timer(); task = new TimerTask() { @Override public void run() { if (player != null) { int time = player.getCurrentPosition(); sb.setProgress(time); } } }; timer.schedule(task,0,500); sb.setProgress(position); player.seekTo(position); player.start(); } }); player.prepareAsync(); } catch (IOException e) { e.printStackTrace(); } } public void play(View v) { play(); Log.d("zhangdi",path); } private boolean isPause; private void pause() { if (player != null && player.isPlaying()) { player.pause(); isPause = true; Play.setEnabled(true); } } public void pause(View v) { pause(); } private void replay() { isPause = false; if (player != null) { stop(); play(); } } public void replay(View v) { replay(); } private void stop(){ isPause = false; if (player != null) { sb.setProgress(0); player.stop(); player.release(); player = null; if (timer != null) { timer.cancel(); } Play.setEnabled(true); } } public void stop(View v) { stop(); } @Override protected void onDestroy() { super.onDestroy(); stop(); } }
2.VideoView 系統自帶的視訊播放控制元件,自帶進度條、暫停、播放等功能,使用起來十分簡單,只需要為控制元件設定好播放路徑,監聽是否準備就緒,就緒後直接播放就可以了。
效果圖
佈局程式碼:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main2" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.video.Main2Activity" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:id="@+id/et1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="3" android:hint="請輸入檔名"/> <Button android:id="@+id/btn" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="確定"/> </LinearLayout> <VideoView android:id="@+id/video" android:layout_width="match_parent" android:layout_height="300dp" android:layout_marginTop="10dp"/> </LinearLayout>
完整程式碼:
package com.example.video; import android.app.Activity; import android.media.MediaPlayer; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.MediaController; import android.widget.VideoView; import java.net.URI; public class Main2Activity extends Activity { private VideoView video; private EditText et; private Button btn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); initView(); } private void initView() { et = (EditText) findViewById(R.id.et1); video = (VideoView) findViewById(R.id.video); btn = (Button) findViewById(R.id.btn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String path = Environment.getExternalStorageDirectory().getPath()+"/"+et.getText().toString();//獲取視訊路徑 Uri uri = Uri.parse(path);//將路徑轉換成uri video.setVideoURI(uri);//為視訊播放器設定視訊路徑 video.setMediaController(new MediaController(Main2Activity.this));//顯示控制欄 video.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { video.start();//開始播放視訊 } }); } }); } }
3.Vitamio 框架 使用起來和VideoView基本一樣,區別在於,我們自己寫的和系統自帶的控制元件無法做到所有格式的視訊都可以播放,但是這個框架基本可以實現,我用真機測試了mp4,flv,mkv,rmvb,3gp,avi這5中格式的視訊,其中只有avi的只有聲音沒有影像,其他都可以正常播放,不過avi格式無法播放也可能是視訊本身有點問題,不過我也沒有其他視訊了,無法繼續測試,如果大家看完文章自己測試的時候可以用,也可以在回覆的地方告訴我下,謝了。
vitamio的library下載地址是:https://www.vitamio.org/Download/
效果圖與2一樣
佈局程式碼:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main2" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.video.Main2Activity" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:id="@+id/et2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="3" android:hint="請輸入檔名"/> <Button android:id="@+id/btn1" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="確定"/> </LinearLayout> <io.vov.vitamio.widget.VideoView android:id="@+id/video1" android:layout_width="match_parent" android:layout_height="300dp" android:layout_marginTop="10dp"/> </LinearLayout>
完整程式碼與2也一樣,需要注意的是裡面的VideoView是Vitamio中的,不是android.widget中的:
package com.example.video; import android.app.Activity; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.view.View; import android.widget.Button; import android.widget.EditText; import io.vov.vitamio.MediaPlayer; import io.vov.vitamio.widget.MediaController; import io.vov.vitamio.widget.VideoView; public class Main3Activity extends Activity { private VideoView video; private Button btn; private EditText et; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main3); initView(); } private void initView() { video = (VideoView) findViewById(R.id.video1); btn = (Button) findViewById(R.id.btn1); et = (EditText) findViewById(R.id.et2); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String path = Environment.getExternalStorageDirectory().getPath()+"/"+et.getText().toString(); Uri uri = Uri.parse(path); video.setVideoURI(uri); video.setMediaController(new MediaController(Main3Activity.this)); video.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { video.start(); } }); } }); } }
以上三種方法完整案例demo下載地址:下載地址