一.在Android中,我們有三種方式來實現(xiàn)視頻的播放:
1丙曙、使用其自帶的播放器爸业。指定Action為ACTION_VIEW,Data為Uri,Type為其MIME類型亏镰。
2沃呢、使用VideoView來播放。在布局文件中使用VideoView結(jié)合MediaController來實現(xiàn)對其控制拆挥。
3薄霜、使用MediaPlayer類和SurfaceView來實現(xiàn),這種方式很靈活纸兔。
本地資源
內(nèi)部URI惰瓜,比如你可以通過ContentResolver來獲取
外部URL(流)
二.第一種方式--使用系統(tǒng)自帶的播放器
private String videoPath1 = "/storage/emulated/0/v1.mp4";
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse(videoPath1);
intent.setDataAndType(uri, "video/mp4");
startActivity(intent);
三.第二種方式--使用VideoView結(jié)合MediaController來播放視頻
1.布局中添加VideoView
<VideoView
android:id="@+id/video"
android:layout_width="match_parent"
android:layout_height="match_parent" />
2.具體代碼
private String[] videos = {"ace.mp4","v1.mp4","v2.mp4"};
private int mPosition = 0;
private void play2() {
MediaController mediaController = new MediaController(this);
mVideo.setMediaController(mediaController);
mVideo.setVideoURI(Uri.parse("/storage/emulated/0/"+videos[mPosition]));
mVideo.start();
mediaController.setPrevNextListeners(new View.OnClickListener() {
@Override
public void onClick(View v) {
//下一首
mPosition++;
mPosition = mPosition%videos.length;//對視頻數(shù)量去余數(shù),以免越界汉矿,同時到達最后一首從第一首開始播
mVideo.setVideoURI(Uri.parse("/storage/emulated/0/"+videos[mPosition]));
mVideo.start();
}
}, new View.OnClickListener() {
@Override
public void onClick(View v) {
//上一首
mPosition--;
mPosition = mPosition<0?videos.length-1:mPosition; //如果index小于0崎坊,設(shè)置為最后一首,不然就還是自己洲拇,沒有越界
mVideo.setVideoURI(Uri.parse("/storage/emulated/0/"+videos[mPosition]));
mVideo.start();
}
});
//設(shè)置獲取焦點
mVideo.requestFocus();
}
四.第三種方式--使用MediaPlayer類和SurfaceView來實現(xiàn)
MediaPlayer主要用于播放音頻奈揍,沒有提供圖像輸出界面,所以我們需要借助其他的組件來顯示MediaPlayer播放的圖像輸出赋续,我們可以使用SurfaceView來顯示
a.調(diào)用player.setDataSource()方法設(shè)置要播放的資源男翰,可以是文件、文件路徑纽乱、或者URL蛾绎。
b.調(diào)用MediaPlayer.setDisplay(holder)設(shè)置surfaceHolder,surfaceHolder可以通過surfaceview的getHolder()方法獲得鸦列。
c.調(diào)用MediaPlayer.prepare()來準備租冠。
d.調(diào)用MediaPlayer.start()來播放視頻。
1.布局中添加SurfaceView
<SurfaceView
android:layout_gravity="center_vertical"
android:id="@+id/surface"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
2.具體代碼
public class SurfaceActivity extends AppCompatActivity {
private SurfaceView mSurface;
private ArrayList<VideoBean> mData;
private int mPosition;
private int mWidth;
private int mHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_surface);
getScreen();
//獲取傳遞過來的數(shù)據(jù)
Intent intent = getIntent();
Bundle extras = intent.getExtras();
mData = (ArrayList<VideoBean>) extras.getSerializable("data");
mPosition = extras.getInt("position");
initView();
}
//獲取屏幕寬高
private void getScreen() {
WindowManager systemService = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display defaultDisplay = systemService.getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
defaultDisplay.getMetrics(metrics);
mWidth = metrics.widthPixels;
mHeight = metrics.heightPixels;
}
private void initView() {
mSurface = (SurfaceView) findViewById(R.id.surface);
final MediaPlayer mediaPlayer = new MediaPlayer();
try {
//設(shè)置播放的視頻路徑
mediaPlayer.setDataSource(mData.get(mPosition).getData());
//異步加載流媒體
mediaPlayer.prepareAsync();
//獲取SurfaceHolder
SurfaceHolder holder = mSurface.getHolder();
//確保surfaceHolder已經(jīng)準備好了薯嗤。因此需要給surfaceHolder設(shè)置一個callback顽爹,
//調(diào)用addCallback()方法。Callback 有三個回調(diào)函數(shù)
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
//SurfaceHolder被創(chuàng)建的時候回調(diào)
mediaPlayer.setDisplay(holder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
//SurfaceHolder被銷毀的時候回調(diào)骆姐,在這里可以做一些釋放資源的操作镜粤,防止內(nèi)存泄漏
}
});
//MediaPlayer加載流媒體完畢的監(jiān)聽
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
//獲取視頻的寬高
int videoHeight = mediaPlayer.getVideoHeight();
int videoWidth = mediaPlayer.getVideoWidth();
if (videoHeight > mHeight || videoWidth> mWidth){
//如果視頻的寬或者高超出屏幕,要縮放
float widthRatio = (float)videoWidth/(float)mWidth;
float heightRatio = (float)videoHeight/(float)mHeight;
//選擇大的進行縮放
float max = Math.max(widthRatio,heightRatio);
videoWidth = (int) Math.ceil(videoWidth/max);
videoHeight = (int) Math.ceil(videoHeight/max);
//設(shè)置surfaceview的布局參數(shù)
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(videoWidth, videoHeight);
//設(shè)置垂直居中
layoutParams.gravity = Gravity.CENTER_VERTICAL;
mSurface.setLayoutParams(layoutParams);
}
mediaPlayer.start();
//mediaPlayer.setLooping(true);
}
});
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
//播放完成監(jiān)聽
finish();
}
});
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
//發(fā)生錯誤監(jiān)聽
return false;
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
五.三種播放視頻方式的分析對比
1. android SDK自帶的 MediaPlayer+SurfaceView或者videoview (簡單快速,格式支持少)
2. MediaPlayer類是Androd多媒體框架中的一個重要組件诲锹,通過該類繁仁,我們可以以最小的步驟來獲取,解碼和播放音視頻归园。它支持三種不同的媒體來源:
在播放網(wǎng)絡(luò)上的視頻流時黄虱,Android原生的MediaPlayer支持兩種協(xié)議,HTTP和RTSP庸诱,這兩種協(xié)議最大的不同是捻浦,RTSP協(xié)議支持實時流媒體的播放晤揣,而HTTP協(xié)議不支持。因為VideoView的底層實現(xiàn)是MediaPlayer朱灿,因此VideoView也支持以上兩種協(xié)議昧识。 ?但是Android原生MediaPalyer支持的協(xié)議(不支持RTMP、MMS等)和封裝格式實在太有限了盗扒,如果我們想播放那些它不支持的視頻跪楞,這時候就需要第三方播放器了,很多第三方播放器的底層實現(xiàn)都是基于FFmpeg侣灶,后續(xù)我會專門來講講第三方播放器和FFmpeg的使用甸祭。
3. MediaPlayer主要用于播放音頻,沒有提供圖像輸出界面褥影,所以我們需要借助其他的組件來顯示MediaPlayer播放的圖像輸出池户,我們可以使用SurfaceView來顯示。
4. 除了使用MediaPlayer + SurfaceView播放視頻的方式凡怎,我們還可以使用VideoView來直接播放視頻校焦。
5. SurfaceView播放視頻時,如果不進行設(shè)置统倒,視頻寬高總是等于定義的SurfaceView布局寬高寨典,所以視頻可能會被拉伸變形。而使用VideoView時檐薯,視頻寬度等于VideoView布局寬凝赛,但是高是自適應(yīng)的注暗,自動調(diào)整寬高比到視頻原始比例坛缕,所以不會有拉伸。
6. 本地資源,內(nèi)部URI捆昏,比如你可以通過ContentResolver來獲取,外部URL(流)
六.播放視頻資源的格式限制
Android官方公布的文檔顯示MediaPlayer支持如下視頻格式:
H.263 X X 3GPP (.3gp)
MPEG-4 (.mp4)?
H.264 AVC X 3GPP (.3gp)
MPEG-4 (.mp4)?
MPEG-4 SP X
3GPP (.3gp)
這些格式的視頻赚楚,基本上屬于手機支持的視頻格式。如果想觀看其他類型格式的視頻骗卜,比如flv等宠页,需要下載暴風(fēng)、迅雷等播放器寇仓。
七.三方視頻庫
1.Vitamio
Vitamio 是一款 Android與 iOS平臺上的全能多媒體開發(fā)框架举户,全面支持硬件解碼與 GPU渲染。Vitamio憑借其簡潔易用的 API接口贏得了全球眾多開發(fā)者的青睞遍烦。到目前俭嘁,全球已經(jīng)有超過 1800種應(yīng)用在使用 Vitamio,覆蓋用戶超過 2億服猪。
Vitamio 能夠流暢播放720P甚至1080P高清MKV供填,F(xiàn)LV拐云,MP4,MOV近她,TS叉瘩,RMVB等常見格式的視頻,還可以在 Android 與iOS上跨平臺支持 MMS, RTSP,RTMP, HLS(m3u8)等常見的多種視頻流媒體協(xié)議粘捎,包括點播與直播薇缅。
目前Vitamio的項目托管在Github上面:https://github.com/yixia
2.JiaoZiVideoPlayer(以前叫節(jié)操播放器)
Github:https://github.com/lipangit/JiaoZiVideoPlayer
3.ijkplayer
項目地址:https://github.com/Bilibili/ijkplayer
介紹:Ijkplayer 是Bilibili發(fā)布的基于 FFplay 的輕量級 Android/iOS 視頻播放器。實現(xiàn)了跨平臺功能攒磨,API 易于集成捅暴;編譯配置可裁剪,方便控制安裝包大羞志馈蓬痒;支持硬件加速解碼杨刨,更加省電圃阳;提供 Android 平臺下應(yīng)用彈幕集成的解決方案券时。
這里還有很多:http://www.sohu.com/a/149331808_733133
八.獲取視頻的第一幀
添加一個按鈕,點擊的時候切換橫豎屏
private void switchScreen() {
//獲取當(dāng)前屏幕的方向
int requestedOrientation = getRequestedOrientation();
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){
//設(shè)置屏幕方向,豎屏,切換為橫屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
對應(yīng)Activity在清單文件中配置,因為屏幕切換的時候Activity會重建,添加配置后Activity就不會重建了
<activity android:name=".VideoViewActivity"
//screensize,屏幕大小發(fā)生改變;
//orientation屏幕方向發(fā)生改變
//keyboardHidden鍵盤隱藏
android:configChanges="screenSize|orientation|keyboardHidden"
/>