在Android中堤如,我們有三種方式來實(shí)現(xiàn)視頻的播放:
1、使用其自帶的播放器窒朋。指定Action為ACTION_VIEW,Data為Uri搀罢,Type為其MIME類型。
2侥猩、使用VideoView來播放榔至。在布局文件中使用VideoView結(jié)合MediaController來實(shí)現(xiàn)對(duì)其控制。
3欺劳、使用MediaPlayer類和SurfaceView來實(shí)現(xiàn)唧取,這種方式很靈活。
1划提、調(diào)用其自帶的播放器:
Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.m4v");
//調(diào)用系統(tǒng)自帶的播放器
Intent intent = new Intent(Intent.ACTION_VIEW);
Log.v("URI:::::::::", uri.toString());
intent.setDataAndType(uri, "video/mp4");
startActivity(intent); ```
2枫弟、使用VideoView來實(shí)現(xiàn):
Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.m4v");
VideoView videoView = (VideoView)this.findViewById(R.id.video_view);
videoView.setMediaController(new MediaController(this));
videoView.setVideoURI(uri);
videoView.start();
videoView.requestFocus(); ```
我們可以試想ImageView能顯示圖片,而VideoView就是用來顯示視頻的
使用VideoView播放視頻的步驟如下
【1】在界面布局中定義VideoView
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<VideoView
android:id="@+id/videoview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<Button
android:id="@+id/button"
android:text="播放"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>```
【2】調(diào)用如下兩個(gè)方法加載指定視頻
setVideoPath(String Path)鹏往;加載路徑下的視頻
setVideoURL(URL url);加載url所對(duì)應(yīng)的視頻淡诗。
mVideoView.setVideoPath(Environment.getExternalStorageDirectory()+"/aa.mp4");```
【3】權(quán)限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>```
【4】調(diào)用
start()、stop()伊履、pause()控制播放
【5】實(shí)際中常常結(jié)合MediaController類韩容,它提供一個(gè)友好的圖像控制界面控制視頻播放;
mVideoView.setMediaController(new MediaController(MainActivity.this));```
完整程序代碼如下
public class MainActivity extends Activity {
private VideoView mVideoView;
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mVideoView= (VideoView) findViewById(R.id.videoview);
mButton= (Button) findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//得到sdcard下面aa.mp4的視頻文件
//兩種調(diào)用方式
// File videofile =new File("/mut/extSdCard/DCIM/Camera/20150915_160202.mp4");
// mVideoView.setVideoPath(videofile.getAbsolutePath());
mVideoView.setVideoPath(Environment.getExternalStorageDirectory()+"/20150915_160202.mp4");
mVideoView.setMediaController(new MediaController(MainActivity.this));
mVideoView.start();
}
});
}
}```
這里可以參考:[[Android基礎(chǔ)] VideoView](http://www.reibang.com/p/2d3b221a2ee7)
3唐瀑、使用MediaPlayer:
package demo.camera;
import java.io.IOException;
import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaPlayer.OnSeekCompleteListener;
import android.media.MediaPlayer.OnVideoSizeChangedListener;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Display;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.LinearLayout;
/**
- 該實(shí)例中使用MediaPlayer完成播放群凶,同時(shí)界面使用SurfaceView來實(shí)現(xiàn)
- 這里我們實(shí)現(xiàn)MediaPlayer中很多狀態(tài)變化時(shí)的監(jiān)聽器
- 使用Mediaplayer時(shí),也可以使用MediaController類哄辣,但是需要實(shí)現(xiàn)MediaController.mediaController接口
- 實(shí)現(xiàn)一些控制方法座掘。
- 然后递惋,設(shè)置controller.setMediaPlayer(),setAnchorView(),setEnabled(),show()就可以了,這里不再實(shí)現(xiàn)
- @author Administrator
*/
public class VideoSurfaceDemo extends Activity implements OnCompletionListener,OnErrorListener,OnInfoListener,
OnPreparedListener, OnSeekCompleteListener,OnVideoSizeChangedListener,SurfaceHolder.Callback{
private Display currDisplay;
private SurfaceView surfaceView;
private SurfaceHolder holder;
private MediaPlayer player;
private int vWidth,vHeight;
//private boolean readyToPlay = false;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
this.setContentView(R.layout.video_surface);
surfaceView = (SurfaceView)this.findViewById(R.id.video_surface);
//給SurfaceView添加CallBack監(jiān)聽
holder = surfaceView.getHolder();
holder.addCallback(this);
//為了可以播放視頻或者使用Camera預(yù)覽溢陪,我們需要指定其Buffer類型
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
//下面開始實(shí)例化MediaPlayer對(duì)象
player = new MediaPlayer();
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
player.setOnInfoListener(this);
player.setOnPreparedListener(this);
player.setOnSeekCompleteListener(this);
player.setOnVideoSizeChangedListener(this);
Log.v("Begin:::", "surfaceDestroyed called");
//然后指定需要播放文件的路徑萍虽,初始化MediaPlayer
String dataPath = Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.m4v";
try {
player.setDataSource(dataPath);
Log.v("Next:::", "surfaceDestroyed called");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//然后,我們?nèi)〉卯?dāng)前Display對(duì)象
currDisplay = this.getWindowManager().getDefaultDisplay();
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// 當(dāng)Surface尺寸等參數(shù)改變時(shí)觸發(fā)
Log.v("Surface Change:::", "surfaceChanged called");
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// 當(dāng)SurfaceView中的Surface被創(chuàng)建的時(shí)候被調(diào)用
//在這里我們指定MediaPlayer在當(dāng)前的Surface中進(jìn)行播放
player.setDisplay(holder);
//在指定了MediaPlayer播放的容器后形真,我們就可以使用prepare或者prepareAsync來準(zhǔn)備播放了
player.prepareAsync();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.v("Surface Destory:::", "surfaceDestroyed called");
}
@Override
public void onVideoSizeChanged(MediaPlayer arg0, int arg1, int arg2) {
// 當(dāng)video大小改變時(shí)觸發(fā)
//這個(gè)方法在設(shè)置player的source后至少觸發(fā)一次
Log.v("Video Size Change", "onVideoSizeChanged called");
}
@Override
public void onSeekComplete(MediaPlayer arg0) {
// seek操作完成時(shí)觸發(fā)
Log.v("Seek Completion", "onSeekComplete called");
}
@Override
public void onPrepared(MediaPlayer player) {
// 當(dāng)prepare完成后杉编,該方法觸發(fā),在這里我們播放視頻
//首先取得video的寬和高
vWidth = player.getVideoWidth();
vHeight = player.getVideoHeight();
if(vWidth > currDisplay.getWidth() || vHeight > currDisplay.getHeight()){
//如果video的寬或者高超出了當(dāng)前屏幕的大小咆霜,則要進(jìn)行縮放
float wRatio = (float)vWidth/(float)currDisplay.getWidth();
float hRatio = (float)vHeight/(float)currDisplay.getHeight();
//選擇大的一個(gè)進(jìn)行縮放
float ratio = Math.max(wRatio, hRatio);
vWidth = (int)Math.ceil((float)vWidth/ratio);
vHeight = (int)Math.ceil((float)vHeight/ratio);
//設(shè)置surfaceView的布局參數(shù)
surfaceView.setLayoutParams(new LinearLayout.LayoutParams(vWidth, vHeight));
//然后開始播放視頻
player.start();
}
}
@Override
public boolean onInfo(MediaPlayer player, int whatInfo, int extra) {
// 當(dāng)一些特定信息出現(xiàn)或者警告時(shí)觸發(fā)
switch(whatInfo){
case MediaPlayer.MEDIA_INFO_BAD_INTERLEAVING:
break;
case MediaPlayer.MEDIA_INFO_METADATA_UPDATE:
break;
case MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING:
break;
case MediaPlayer.MEDIA_INFO_NOT_SEEKABLE:
break;
}
return false;
}
@Override
public boolean onError(MediaPlayer player, int whatError, int extra) {
Log.v("Play Error:::", "onError called");
switch (whatError) {
case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
Log.v("Play Error:::", "MEDIA_ERROR_SERVER_DIED");
break;
case MediaPlayer.MEDIA_ERROR_UNKNOWN:
Log.v("Play Error:::", "MEDIA_ERROR_UNKNOWN");
break;
default:
break;
}
return false;
}
@Override
public void onCompletion(MediaPlayer player) {
// 當(dāng)MediaPlayer播放完成后觸發(fā)
Log.v("Play Over:::", "onComletion called");
this.finish();
}
} ```
1)獲得MediaPlayer實(shí)例:
可以使用直接new的方式:
MediaPlayer mp = new MediaPlayer();
也可以使用create的方式邓馒,如:
MediaPlayer mp = MediaPlayer.create(this, R.raw.test);//這時(shí)就不用調(diào)用setDataSource了
2) 設(shè)置要播放的文件:
MediaPlayer要播放的文件主要包括3個(gè)來源:
a. 用戶在應(yīng)用中事先自帶的resource資源
例如:MediaPlayer.create(this, R.raw.test);
b. 存儲(chǔ)在SD卡或其他文件路徑下的媒體文件
例如:mp.setDataSource("/sdcard/test.mp3");
c. 網(wǎng)絡(luò)上的媒體文件
例如:mp.setDataSource("http://www.citynorth.cn/music/confucius.mp3");
MediaPlayer的setDataSource一共四個(gè)方法:
setDataSource (String path)
setDataSource (FileDescriptor fd)
setDataSource (Context context, Uri uri)
setDataSource (FileDescriptor fd, long offset, long length)
其中使用FileDescriptor時(shí),需要將文件放到與res文件夾平級(jí)的assets文件夾里蛾坯,然后使用:
AssetFileDescriptor fileDescriptor = getAssets().openFd("rain.mp3");
m_mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(), fileDescriptor.getLength());
來設(shè)置datasource
3)對(duì)播放器的主要控制方法:
Android通過控制播放器的狀態(tài)的方式來控制媒體文件的播放光酣,其中:
prepare()和prepareAsync() 提供了同步和異步兩種方式設(shè)置播放器進(jìn)入prepare狀態(tài),需要注意的是脉课,如果MediaPlayer實(shí)例是由create方法創(chuàng)建的救军,那么第一次啟動(dòng)播放前不需要再調(diào)用prepare()了,因?yàn)閏reate方法里已經(jīng)調(diào)用過了倘零。
start()是真正啟動(dòng)文件播放的方法唱遭,
pause()和stop()比較簡(jiǎn)單,起到暫停和停止播放的作用呈驶,
seekTo()是定位方法拷泽,可以讓播放器從指定的位置開始播放,需要注意的是該方法是個(gè)異步方法袖瞻,也就是說該方法返回時(shí)并不意味著定位完成司致,尤其是播放的網(wǎng)絡(luò)文件,真正定位完成時(shí)會(huì)觸發(fā)OnSeekComplete.onSeekComplete()聋迎,如果需要是可以調(diào)用setOnSeekCompleteListener(OnSeekCompleteListener)設(shè)置監(jiān)聽器來處理的蚌吸。
release()可以釋放播放器占用的資源,一旦確定不再使用播放器時(shí)應(yīng)當(dāng)盡早調(diào)用它釋放資源砌庄。
reset()可以使播放器從Error狀態(tài)中恢復(fù)過來羹唠,重新會(huì)到Idle狀態(tài)。
4)設(shè)置播放器的監(jiān)聽器:
MediaPlayer提供了一些設(shè)置不同監(jiān)聽器的方法來更好地對(duì)播放器的工作狀態(tài)進(jìn)行監(jiān)聽娄昆,以期及時(shí)處理各種情況佩微,
如: setOnCompletionListener(MediaPlayer.OnCompletionListener listener)、
setOnErrorListener(MediaPlayer.OnErrorListener listener)等,設(shè)置播放器時(shí)需要考慮到播放器可能出現(xiàn)的情況設(shè)置好監(jiān)聽和處理邏輯萌焰,以保持播放器的健壯性哺眯。