天天看點

android 播放視訊常見問題android 橫豎屏切換屬性和播放視訊全屏切換android 視訊播放器Vitamio踩坑之路

在android 開發中常見到視訊播放的問題,在正常的視訊中 有直接用videoView + MediaController 或者 mediaController + serfercie holder

常見的問題

1 在播放中如何處理播放器的橫屏切換 和 播放器上的文案顯示的布局變化

在activity 中 設定

  • 1

這樣在旋轉中,activity 就不會重建

重寫系統方法

public void onConfigurationChanged(Configuration newConfig) {
        // TODO Auto-generated method stub
        super.onConfigurationChanged(newConfig);
        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            initVideoLandLayout();
        } else {
            initVideoPortLayout();
        }
    }
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

通過 判斷 newConfig.orientation 來判斷 目前是橫屏還是豎屏, 這個是在看視訊的過程中去判斷是不是橫屏還是豎屏,在我門一開始播放視訊的時候,也可以去判斷,具體的處理方法是

private void initVideoPlayerLayout() {
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        width = dm.widthPixels;
        heigh = dm.heightPixels;
        if (width / heigh > ) { // 橫屏
            initVideoLandLayout();
            fullscreen = true;
        }
        if (width / heigh == ) { //豎屏
            initVideoPortLayout();
        }

    }
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在具體變化播放器上的布局的時候我們可以動态的去更換

private void initVideoPortLayout() {
        RelativeLayout.LayoutParams videoLp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
        videoLp.addRule(RelativeLayout.CENTER_IN_PARENT);
        videoView.setLayoutParams(videoLp);
        videoView.start();
        RelativeLayout.LayoutParams hotelInfoLp = new RelativeLayout.LayoutParams(DeviceInfoUtil.getPixelFromDip(), DeviceInfoUtil.getPixelFromDip());
        hotelInfoLp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        hotelInfoLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
        hotelInfoLp.rightMargin = DeviceInfoUtil.getPixelFromDip();
        hotelInfoLp.bottomMargin = DeviceInfoUtil.getPixelFromDip();
        mHotelInfoLayout.setLayoutParams(hotelInfoLp);
    }
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

這是豎屏處理

private void initVideoLandLayout() {
        RelativeLayout.LayoutParams layoutParams =
                new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT);
        layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
        layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
        layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
        videoView.setLayoutParams(layoutParams);
        RelativeLayout.LayoutParams hotelInfoLp = new RelativeLayout.LayoutParams(DeviceInfoUtil.getPixelFromDip(), DeviceInfoUtil.getPixelFromDip());
        hotelInfoLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
        hotelInfoLp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        hotelInfoLp.rightMargin = DeviceInfoUtil.getPixelFromDip();
        hotelInfoLp.bottomMargin = DeviceInfoUtil.getPixelFromDip();
        mHotelInfoLayout.setLayoutParams(hotelInfoLp);
    }
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

這是橫屏處理

我門知道 系統的videoView 控件 自帶有播放,暫停 等進度條這是MediaContronller 當我們手點選的時候 進度條會顯示,手離開界面的時候,會不顯示,這是videoView 中的方法

@Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (isInPlaybackState() && mMediaController != null) {
            toggleMediaControlsVisiblity();
        }
        return false;
    }
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

再朝下看

private void toggleMediaControlsVisiblity() {
        if (mMediaController.isShowing()) {
            mMediaController.hide();
        } else {
            mMediaController.show();
        }
    }
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

最終調的是 mMediaController.hide(); 和 mMediaController.show(),

是以我門在自己的播放器界面要是實作自己的布局和播放器進度條 重寫hide() 和 show() 方法即可

2 在視訊播放的過程中經常會出現播放異常的情況,比如彈出無法播放視訊的彈框,當然我門是不想讓它彈的,于是我門的處理方式為:

videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
            @Override
            public boolean onError(MediaPlayer mp, int what, int extra) {
                return true;
            }
        });
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

讓onError 直接傳回為true 這樣能阻止異常框的彈出 同時什麼都不做,原理是

Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
                boolean error_was_handled = false;
                if (mOnErrorListener != null) {
                    error_was_handled = mOnErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2);
                }
                if (mOnCompletionListener != null && ! error_was_handled) {
                    mOnCompletionListener.onCompletion(mMediaPlayer);
                }
                stayAwake(false);
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

這裡error_was_handled 為true 這樣就不走進mOnCompletionListener.onCompletion(mMediaPlayer); 

了。

3 視訊上線發現 在部分小米機型和魅族等機器上橫屏 視訊沒有辦法橫屏。解決方案為

在布局中

<RelativeLayout
        android:id="@+id/videoView_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <VideoView
            android:id="@+id/video"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone" />
    </RelativeLayout>
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

嵌套RelativeLayout;

在代碼中處理

RelativeLayout.LayoutParams layoutParams =
                new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
        layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
        layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
        layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
        layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        mVideoViewLayout.setLayoutParams(layoutParams);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4 在視訊開發中,回發現window Leacked 問題,看了下 

MediaController 的show 方法

if (!mShowing && mAnchor != null) {
            setProgress();
            if (mPauseButton != null) {
                mPauseButton.requestFocus();
            }
            disableUnsupportedButtons();
            updateFloatingWindowLayout();
            mWindowManager.addView(mDecor, mDecorLayoutParams); 
            mShowing = true;
        }
        updatePausePlay();
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在不停的向view 中加布局,在MediaContrller 中方法了 中hide

/**
     * Remove the controller from the screen.
     */
    public void hide() {
        if (mAnchor == null)
            return;

        if (mShowing) {
            try {
                mHandler.removeMessages(SHOW_PROGRESS);
                mWindowManager.removeView(mDecor);
            } catch (IllegalArgumentException ex) {
                Log.w("MediaController", "already removed");
            }
            mShowing = false;
        }
    }
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

講view remove 是以 我門在activity 的industry 的生命周期的回調中 執行 hide() 方法即可

android 橫豎屏切換屬性和播放視訊全屏切換

通常我們的應用隻會設計成橫屏或者豎屏,鎖定橫屏或豎屏的方法是在manifest.xml檔案中設定屬性android:screenOrientation為”landscape”或”portrait”:

<activity
    android:name="com.jooylife.jimei_tablet.base.Main"
    android:label="@string/app_name" 
    android:screenOrientation="landscape">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

其實screenOrientation還可以設定成很多值:

android:screenOrientation= 

[“unspecified” | “behind” |”landscape” | “portrait”|”reverseLandscape”|”reversePortrait” |”sensorLandscape” | “sensorPortrait” |”userLandscape” | “userPortrait” |”sensor” | “fullSensor” | “nosensor” |”user” | “fullUser” | “locked”] 

其中sensorLandscape就是橫屏根據重力切換,sensorPortrait豎屏根據重力切換。

播放視訊全屏切換: 

1.Demo:

@Override 

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState); 

setContentView(R.layout.activity_main); 

if(this.getResources().getConfiguration().orientation==Configuration.ORIENTATION_LANDSCAPE) { 

getWindow().getDecorView().setSystemUiVisibility(View.INVISIBLE); 

}else if (this.getResources().getConfiguration().orientation==Configuration.ORIENTATION_PORTRAIT) { 

// this.requestWindowFeature(Window.f);// 去掉标題欄 

// this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 

// WindowManager.LayoutParams.FLAG_FULLSCREEN);// 去掉資訊欄 

Log.i(“info”, “portrait”); // 豎屏

}

View類提供了setSystemUiVisibility和getSystemUiVisibility方法,這兩個方法實作對狀态欄的動态顯示或隐藏的操作,以及擷取狀态欄目前可見性。

setSystemUiVisibility(int visibility)方法可傳入的實參為:

  1. View.SYSTEM_UI_FLAG_VISIBLE:顯示狀态欄,Activity不全屏顯示(恢複到有狀态的正常情況)。
  2. View.INVISIBLE:隐藏狀态欄,同時Activity會伸展全屏顯示。
  3. View.SYSTEM_UI_FLAG_FULLSCREEN:Activity全屏顯示,且狀态欄被隐藏覆寫掉。
  4. View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN:Activity全屏顯示,但狀态欄不會被隐藏覆寫,狀态欄依然可見,Activity頂端布局部分會被狀态遮住。
  5. View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION:效果同View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
  6. View.SYSTEM_UI_LAYOUT_FLAGS:效果同View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
  7. View.SYSTEM_UI_FLAG_HIDE_NAVIGATION:隐藏虛拟按鍵(導航欄)。有些手機會用虛拟按鍵來代替實體按鍵。
  8. View.SYSTEM_UI_FLAG_LOW_PROFILE:狀态欄顯示處于低能顯示狀态(low profile模式),狀态欄上一些圖示顯示會被隐藏。 

    2.

if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { 

WindowManager.LayoutParams attrs = getWindow().getAttributes(); 

attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; 

getWindow().setAttributes(attrs); 

getWindow().addFlags( 

WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); 

} else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { 

WindowManager.LayoutParams attrs = getWindow().getAttributes(); 

attrs.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN); 

getWindow().setAttributes(attrs); 

getWindow().clearFlags( 

WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); 

}

android 視訊播放器Vitamio踩坑之路

一、Vitamio底層音視訊解碼原理基于FFmpeg開發,vitamio的優點:

(1)Vitamio 能夠流暢播放720P甚至1080P高清MKV,FLV,MP4,MOV,TS,RMVB等常見格式的視訊,

(2)可以在 Android 上支援 MMS, RTSP, RTMP, HLS(m3u8) 等常見的多種視訊流媒體協定,包括點播與直播。

支援 ARMv6 和 ARMv7 兩種 ARM CPU,同時對 VFP, VFPv3, NEON 等指令集都做相應優化。

二、使用:

(1)內建:

1.github下載下傳vitamio資源 https://github.com/yixia/VitamioBundle

2.解壓檔案,将其中的vitamio導入到as中 ,打開AS,File -> New -> Import Moudle,選擇剛才解壓檔案夾下的 vitamio 檔案. 并且在app的gradle中添加該包的依賴

3.按照app目錄下的build.gradle配置vitamio目錄下的build.gradle(注意不是vitamio檔案夾下app下的)

(2)使用

1.mainfest檔案

<uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
           
在自己的AndroidManifest.xm 中添加
           

 <activity android:name="io.vov.vitamio.activity.InitActivity" android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation" android:launchMode="singleTop" android:theme="@android:style/Theme.NoTitleBar" android:windowSoftInputMode="stateAlwaysHidden" />

2.使用Vitamio庫

在視訊播放的Activity onCreate中 setContentView()之前添加解碼監聽判斷,

if (!io.vov.vitamio.LibsChecker.checkVitamioLibs(this))
    return;      

1.布局中

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.gbf.livingdemo.VitamioActivity"
    android:orientation="vertical">
<io.vov.vitamio.widget.VideoView
    android:id="@+id/vitamio_videoview"
    android:layout_width="match_parent"
    android:layout_height="200dp"/>
</LinearLayout>
      

2.activity

public class VitamioActivity extends AppCompatActivity implements MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener{
    private VideoView mVideoView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //檢查vitamio架構是否可用
        if (!io.vov.vitamio.LibsChecker.checkVitamioLibs(this))
            return;
        setContentView(R.layout.activity_vitamio);
        //一定要初始化
        Vitamio.initialize(this);
        initView();
        initData();
    }

    private void initData() {
        mVideoView.setVideoURI(Uri.parse("http://qiubai-video.qiushibaike.com/91B2TEYP9D300XXH_3g.mp4"));
//        mVideoView.setVideoURI(Uri.parse("http://alcdn.hls.xiaoka.tv/2017427/14b/7b3/Jzq08Sl8BbyELNTo/index.m3u8"));
        mVideoView.setMediaController(new MediaController(this));

        //設定監聽
        mVideoView.setOnPreparedListener(this);
        mVideoView.setOnErrorListener(this);
        mVideoView.setOnCompletionListener(this);
    }

    private void initView() {
        mVideoView=findViewById(R.id.vitamio_videoview);
    }



    @Override
    public void onPrepared(MediaPlayer mp) {
        Toast.makeText(this,"準備好了", Toast.LENGTH_LONG).show();
        mVideoView.start();
    }

    @Override
    public void onCompletion(MediaPlayer mp) {
        Toast.makeText(this,"播放完成", Toast.LENGTH_LONG).show();
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        Toast.makeText(this,"Error", Toast.LENGTH_LONG).show();
//          傳回 true
        return true;
    }
}      

别忘記

@Override
protected void onDestroy() {
    mVideoView.stopPlayback();
    super.onDestroy();
}      

效果如下:

豎屏:

android 播放視訊常見問題android 橫豎屏切換屬性和播放視訊全屏切換android 視訊播放器Vitamio踩坑之路

正常情況下來,進度條應該是在視訊的底部,而不是在手機螢幕的底部

修改如下:(1)xml布局,在videoview外面添加一層布局如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.gbf.livingdemo.VitamioActivity"
    android:orientation="vertical">
    <RelativeLayout
        android:id="@+id/rela"
        android:layout_width="match_parent"
        android:layout_height="260dp">
<io.vov.vitamio.widget.VideoView
    android:id="@+id/vitamio_videoview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
    </RelativeLayout>
</LinearLayout>
      

(2)MediaController添加構造函數

public MediaController(Context context, boolean isFromXml, View container) {
  super(context);
  initController(context);
  mFromXml = isFromXml;
  mRoot = makeControllerView();
  if(container instanceof RelativeLayout)
  {
    RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT,
            LayoutParams.WRAP_CONTENT);
    p.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
    mRoot.setLayoutParams(p);
    ((RelativeLayout)container).addView(mRoot);
  }
}  public MediaController(Context context) {
  super(context);
  if (!mFromXml && initController(context))
    initFloatingWindow();
}      

(3)activity中執行個體化MediaController

//        mVideoView.setMediaController(new MediaController(this));
        mVideoView.setMediaController(new MediaController(this,true,mRelatviLayout));      

效果如下:

android 播放視訊常見問題android 橫豎屏切換屬性和播放視訊全屏切換android 視訊播放器Vitamio踩坑之路

豎屏顯示正常~~

橫螢幕:

android 播放視訊常見問題android 橫豎屏切換屬性和播放視訊全屏切換android 視訊播放器Vitamio踩坑之路

解決辦法:

(1)清單檔案中目前activity

設定如下:

android:configChanges="orientation|keyboardHidden|screenLayout|screenSize"      

(2)判斷螢幕是否是橫屏或者豎屏

@Override
public void onConfigurationChanged(Configuration newConfig) {
    //螢幕切換時,設定全屏
    if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
    {
        Log.d("haha","切換橫屏");
        setFullScreen();
    }
    else
    {
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                600);
        mRelatviLayout.setLayoutParams(layoutParams);
        RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(
                RelativeLayout.LayoutParams.MATCH_PARENT,
                RelativeLayout.LayoutParams.MATCH_PARENT);
        mVideoView.setLayoutParams(layoutParams1);
    }
    super.onConfigurationChanged(newConfig);
}

private void setFullScreen() {
    Log.d("haha","調用設定全屏");

    LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.MATCH_PARENT);
    mRelatviLayout.setLayoutParams(layoutParams);
    RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(
            RelativeLayout.LayoutParams.MATCH_PARENT,
            RelativeLayout.LayoutParams.MATCH_PARENT);
    mVideoView.setLayoutParams(layoutParams1);
}      

效果:

android 播放視訊常見問題android 橫豎屏切換屬性和播放視訊全屏切換android 視訊播放器Vitamio踩坑之路

像一個正常的播放器了~~