日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > 綜合技術 > Android - SurfaceView + MediaPlayer實現分段視頻無縫播放

Android - SurfaceView + MediaPlayer實現分段視頻無縫播放

來源:程序員人生   發布時間:2015-04-24 07:49:00 閱讀次數:5824次

Android當中實現視頻播放的方式有兩種,即:通過VideoView實現或通過SurfaceView + MediaPlayer實現。


由淺至深,首先來看下想要在Android上播放1段視頻,我們應當怎樣做。

前面我們已提到了兩種方式,這里我們來看1下具有更好的拓展性的第2種方式,也就是通過SurfaceView + MediaPlayer進行實現。


首先,我們來定義1個布局文件以下,為了方便起見,我們僅僅只在該布局中定義了1個SurfaceView:

<?xml version="1.0" encoding="utf⑻"?> <FrameLayout 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" android:id="@+id/videoLayout" > <SurfaceView android:id="@+id/surface" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_gravity="center"> </SurfaceView> </FrameLayout>


接著就是Activity類文件的定義:

package com.example.videodemo; import android.app.Activity; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Bundle; import android.view.SurfaceHolder; import android.view.SurfaceView; public class VideoPlayActivity extends Activity implements SurfaceHolder.Callback { /** Called when the activity is first created. */ MediaPlayer player; SurfaceView surface; SurfaceHolder surfaceHolder; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_play); initView(); } private void initView() { surface = (SurfaceView) findViewById(R.id.surface); surfaceHolder = surface.getHolder(); // SurfaceHolder是SurfaceView的控制接口 surfaceHolder.addCallback(this); // 由于這個類實現了SurfaceHolder.Callback接口,所以回調參數直接this } @Override public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { } @Override public void surfaceCreated(SurfaceHolder arg0) { // 必須在surface創建后才能初始化MediaPlayer,否則不會顯示圖象 player = new MediaPlayer(); player.setAudioStreamType(AudioManager.STREAM_MUSIC); player.setDisplay(surfaceHolder); // 設置顯示視頻顯示在SurfaceView上 try { player.setDataSource("你要播放的視頻的url"); player.prepare(); player.start(); } catch (Exception e) { e.printStackTrace(); } } @Override public void surfaceDestroyed(SurfaceHolder arg0) { // TODO Auto-generated method stub } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); if (player.isPlaying()) { player.stop(); } player.release(); // Activity燒毀時停止播放,釋放資源。不做這個操作,即便退出還是能聽到視頻播放的聲音 } }

由此你可以看到,這類實現方式有幾點值得注意的地方是:

1、你需要1個媒體播放器對象"MediaPlayer",該對象會負責播放你指定的視頻。

2、如果說MediaPlayer負責播放視頻,那末我們剛剛定義的SurfaceView則用于在屏幕中顯示播放視頻。

  (所以又可以理解為,如果MediaPlayer是1副畫,而SurfaceView則是讓這幅畫顯現在人們眼前的畫紙)

3、MediaPlayer類的成員方法設置用于顯示媒體視頻的SurfaceHolder,正如上面所說,就猶如你擇不同的畫紙來顯現你的畫。

4、MediaPlayer類的成員方法setDataSource用于指定你要播放的視頻數據源。

5、僅僅是設置完數據源是不足夠的,設置完數據源和顯示的Surface后,你需要調用prepare()或prepareAsync()來讓你的視頻數據源stand by..

6、所以你也可能已發現,對1段視頻的播放,MediaPlayer是關鍵,關于該類的更多使用,這篇博客里有更詳細的說明:Android - MediaPlayer類的使用說明


由此我們已基本掌握了,在android端簡單的播放視頻的方法。1切看上去10分美好。

但做開發就是有這么蛋疼,maybe有很多時候為了加快video與server端之間上傳于下載的速率,有時候會對視頻做分段處理。

正猶如做web開發時,上傳和下載文件時,如果文件過大,很多時候我們會選擇對文件做“切割處理1樣”。


那末這個時候,就出現了1種情況,就是可能你要播放的1段視頻,

事實上是由幾小段視頻組合而成的。所以就觸及到了連續播放。


可能當面對到這樣的需求時,我們首先最容易想到的就是:

對每段視頻進行監聽,當監聽到它播放結束時,立刻做Refresh切換到下1段視頻分段的播放。

而MediaPlayer的確也提供了這樣的監聽事件,正是:MediaPlayer.OnCompletionListener()。

我在網上查閱相干實現的功能時,也只看到類似的說法,也就是說在該監聽內做實現:

當1段數據源播放終了后,履行player.reset()釋放數據源,然后再設置新的資源進行播放。

但這樣做有很大的1個弊端就是,reset掉舊的數據源以后,新的數據源會有1段“加載時間”。

也就是說,在這段時間內,用戶看到的播放界面就處于1個停頓狀態。


那末,為了最大化的避免這個所謂的“停頓時間”,又應當怎樣去做呢?

首先斟酌到的便是,在1段視頻開始播放的同時,便開始做第2段視頻播放的“準備工作”。

但是通過前面的例子我們之前看到了,基于MediaPlayer本身的特性和限制。

如果我們想要實現這樣的方式,那末單1的MediaPlayer是滿足不了我們的需求的。


所以我們要做的工作便是:當我們進入視頻播放界面,第1段視頻準備終了,開始播放后,

便開始著手初始化另外一個新的MediaPlayer,這個新的MediaPlayer的數據源固然是接下來要播放的下1段視頻的url!

當這個MediaPlayer對象的準備工作都弄定后,剩下的工作就是:

我們需要“1顆釘子”,來將兩個分段的視頻段連接起來。

而這個釘子就是Android r16后添加的1個方法:setNextMediaPlayer()方法。


關于這個方法的使用,我找了又找,終究在1篇文章里,看到了1個這樣簡短的說明:

在第1個MediaPlayer類履行結束前的任什么時候間調用setNextMediaPlayer(MediaPlayernext)這個方法,

該方法的參數是第2個文件創建的MediaPlayer實例。然后Android系統將會在您第1個停止的時候緊接著播放第2個文件。


但我認為,在這個說明里,你應當注意到的關鍵點是:第1個MediaPlayer類履行結束前的任什么時候間調用這個方法

也就是說,你必須在前1個MediaPlayer對象播放終了之前使用該方法。

例如我后來發現,如果理想的在我們前面提到的OnCompletionListener監聽中使用該方法,是無效的。


并且,似乎其實不如該說明而言的“Android系統將會在您第1個停止的時候緊接著播放第2個文件”。

也就是說,這個切換播放的動作不是自動的,還需要我們手動的做1個小的控制,馬上接下來就會說到。


到了這里,我們要實現的思路已很明確了:在1段視頻播放的同時,做下1段視頻的player的初始化準備工作。

而此時另外一個格外需要記住的就是:不要再在UI線程去開啟新的MediaPlayer的賦值工作.

原理很簡單,其實也是Android開發所必須記住的,即是永久不要在UI線程里去做耗時的操作。

這樣做的后果基本有幾種,1種是報告“在主線程做了太多操作”的異常,而另外也可能出現,屏幕響應緩慢,

也就是說,例如你的視頻播放界面可能還存在1些按鈕和響應事件之類,這個響應會出現延遲。最后,固然也極可能出現ANR。

所以,我們還需要做的工作就是,將其它負責后續播放的MediaPlayer對象的初始化與賦值工作放在新的線程里去履行。


而最后我們需要做的,則是在OnCompletionListener里進行監聽,當1段視頻播放終了后,

馬上履行mp.setDisplay(null),然后調用負責下1個視頻分段播放的MediaPlayer履行setDisplay(surfaceHolder)。


說了這么多,還是通過代碼說話吧:

@SuppressLint("NewApi") public class MainActivity extends Activity implements SurfaceHolder.Callback { //用于播放視頻的mediaPlayer對象 private MediaPlayer firstPlayer, //負責播放進入視頻播放界面后的第1段視頻 nextMediaPlayer, //負責1段視頻播放結束后,播放下1段視頻 cachePlayer, //負責setNextMediaPlayer的player緩存對象 currentPlayer; //負責當前播放視頻段落的player對象 //負責配合mediaPlayer顯示視頻圖象播放的surfaceView private SurfaceView surface; private SurfaceHolder surfaceHolder; //底部聊天欄 private LinearLayout bottom_bar_layout; private FrameLayout video_layout; //================================================================ //寄存所有視頻真個url private ArrayList<String> VideoListQueue = new ArrayList<String>(); //所有player對象的緩存 private HashMap<String, MediaPlayer> playersCache = new HashMap<String, MediaPlayer>(); //當前播放到的視頻段落數 private int currentVideoIndex; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //橫屏顯示 this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); //初始化界面控件 initView(); } /* * 負責界面燒毀時,release各個mediaplayer * @see android.app.Activity#onDestroy() */ @Override protected void onDestroy() { super.onDestroy(); if (firstPlayer != null) { if (firstPlayer.isPlaying()) { firstPlayer.stop(); } firstPlayer.release(); } if (nextMediaPlayer != null) { if (nextMediaPlayer.isPlaying()) { nextMediaPlayer.stop(); } nextMediaPlayer.release(); } if (currentPlayer != null) { if (currentPlayer.isPlaying()) { currentPlayer.stop(); } currentPlayer.release(); } currentPlayer = null; } /* * 界面控件的初始化 */ private void initView() { surface = (SurfaceView) findViewById(R.id.surface); surfaceHolder = surface.getHolder();// SurfaceHolder是SurfaceView的控制接口 surfaceHolder.addCallback(this); // 由于這個類實現了SurfaceHolder.Callback接口,所以回調參數直接this bottom_bar_layout = (LinearLayout) findViewById(R.id.live_buttom_bar); //點擊屏幕任何地點,控制底部聊天欄的隱藏或顯示 video_layout = (FrameLayout) findViewById(R.id.videoLayout); video_layout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { if (bottom_bar_layout.getVisibility() == View.VISIBLE) { bottom_bar_layout.setVisibility(View.GONE); } else { bottom_bar_layout.setVisibility(View.VISIBLE); } } }); } @Override public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { // TODO 自動生成的方法存根 } @Override public void surfaceCreated(SurfaceHolder arg0) { //surfaceView創建終了后,首先獲得該直播間所有視頻分段的url getVideoUrls(); //然后初始化播放手段視頻的player對象 initFirstPlayer(); } @Override public void surfaceDestroyed(SurfaceHolder arg0) { // TODO 自動生成的方法存根 } /* * 初始化播放首段視頻的player */ private void initFirstPlayer() { firstPlayer = new MediaPlayer(); firstPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); firstPlayer.setDisplay(surfaceHolder); firstPlayer .setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { onVideoPlayCompleted(mp); } }); //設置cachePlayer為該player對象 cachePlayer = firstPlayer; initNexttPlayer(); //player對象初始化完成后,開啟播放 startPlayFirstVideo(); } private void startPlayFirstVideo() { try { firstPlayer.setDataSource(VideoListQueue.get(currentVideoIndex)); firstPlayer.prepare(); firstPlayer.start(); } catch (IOException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } } /* * 新開線程負責初始化負責播放剩余視頻分段的player對象,避免UI線程做過量耗時操作 */ private void initNexttPlayer() { new Thread(new Runnable() { @Override public void run() { for (int i = 1; i < VideoListQueue.size(); i++) { nextMediaPlayer = new MediaPlayer(); nextMediaPlayer .setAudioStreamType(AudioManager.STREAM_MUSIC); nextMediaPlayer .setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { onVideoPlayCompleted(mp); } }); try { nextMediaPlayer.setDataSource(VideoListQueue.get(i)); nextMediaPlayer.prepare(); } catch (IOException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } //set next mediaplayer cachePlayer.setNextMediaPlayer(nextMediaPlayer); //set new cachePlayer cachePlayer = nextMediaPlayer; //put nextMediaPlayer in cache playersCache.put(String.valueOf(i), nextMediaPlayer); } } }).start(); } /* * 負責處理1段視頻播放過后,切換player播放下1段視頻 */ private void onVideoPlayCompleted(MediaPlayer mp) { mp.setDisplay(null); //get next player currentPlayer = playersCache.get(String.valueOf(++currentVideoIndex)); if (currentPlayer != null) { currentPlayer.setDisplay(surfaceHolder); } else { Toast.makeText(MainActivity.this, "視頻播放終了..", Toast.LENGTH_SHORT) .show(); } } private void getVideoUrls() { for (int i = 0; i < 5; i++) { String url = getURI(i); VideoListQueue.add(url); } } private String getURI(int index) { return "要播放的第"+index+"段視頻的URI"; } }

而最后額外說明的就是,在上面的代碼中,我選擇新開線程直接根據總的視頻段數,循環完成所有視頻段的MediaPlayer對象的初始化與賦值工作。

其實本來另外1種實現方式似乎也很不錯,即是在前1個MediaPlayer對象的OnInfoListener中進行下1個視頻段MediaPlayer的初始化工作。

也就是說,當前1段視頻開始或結束緩沖時,才開啟它以后的1段視頻段的初始化工作。但屢次測試后,發現:

這類實現方式,如果你此次的播放中,視頻分段的數量較多時,總會出現1些稀里糊涂的異常,也沒能太弄清楚是甚么緣由釀成的。

所以總的來講,還是可以根據實際情況來選擇更適合的方式。



生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 亚洲欧美国产一区二区三区 | 亚洲国产精品自拍 | 国产精品一区在线播放 | 国产日韩av在线播放 | 欧美第二页 | 一区二区电影网 | 欧美在线亚洲 | 99导航 | 久久久久国产精品视频 | 国产精品不卡在线 | 久久成人免费 | 国产成人精品久久二区二区91 | 国产综合视频 | 99久久久久久 | 69xx视频 | 国内精品久久久久久 | 国产一区二区三区四区大秀 | 国产成人精品一区二区 | 国产精品久久久久久久久久久不卡 | 久久www免费人成看片小草 | 91麻豆精品国产91久久久久久 | 久久精品亚洲精品国产欧美 | 日韩欧美中文在线 | 一区二区三区在线视频播放 | 曰批视频在线观看 | 日韩亚洲一区二区 | 久久69精品久久久久久久电影好 | 日本精a在线观看 | 国产精品一区二区三 | 日韩三级中文字幕 | 久久久噜噜噜久久中文字幕色伊伊 | 特黄一级大片 | www.日韩精品 | 日韩中文一区二区三区 | 日韩精品网站 | 欧美日韩亚洲二区 | 日本一区二区三区在线观看视频 | 在线观看黄a | 国产在线观看一区二区 | 国产精品久久久久国产a级 亚洲天堂偷拍 | a毛片免费 |