android視訊適配與裁剪
首先說下基本背景, 當我們使用android系統原生的VideoView播放視訊時, 在XML中給它設定的一個尺寸, 但最終視訊開始播放後, VideoView實際的尺寸可能並不是這個尺寸設定的大小. VideoView在測量自身的尺寸時會依據視訊的真實尺寸來調整自己的大小, 遵循以下規則:
1. 實際視訊在VideoView上播放時所有部分都是可見的,或縮小或放大, 總之一定要全部顯示出來,不會裁剪實際視訊.
2. 儘量保持實際視訊的長寬比例, 具體是首先以我們使用者定義的長度為標準, 等比例縮放視訊大小, 直到長度達到我們定義的長度, 然後寬度(等比例縮放後的寬度)與我們定義的寬比較, 大於則以我們定義的寬度為準, 這樣視訊會在豎直方向上壓縮, 最終播放時也就不會成比例了; 小於則它以視訊縮放後的寬度為準, 這樣它會比我們定義的高度小,最終播放的效果是等比例的.
最近開發有如下需求:
視訊等比例放大,直至一邊鋪滿VideoView(或螢幕)的某一邊,另一邊超出View的另一邊,再移動到View的正中央,這樣長邊兩邊會被裁剪掉同樣大小的區域,視訊看起來不會變形,也即是:先把視訊區(實際的大小顯示區)與View(定義的大小)區的兩個中心點重合, 然後等比例放大或縮小視訊區,直至一條邊與View的一條邊相等,另一條邊超過View的另一條邊,這時再裁剪掉超出的邊, 使視訊區與View區大小一樣. 這樣在不同尺寸的手機上,視訊看起來不會變形,只是水平或豎直方向的兩端被裁剪了一些.
以上需求用系統的VideoView是無法達到的,所以要自定義VideoView.
最終我們擴充套件TextureView來自定義一個TextureVideoView, 並應用Matrix進行縮放, 也即是要求出變換的Matrix, 主要按以下步驟來即可求出變換矩陣, 這裡直接貼出程式碼:
//需求:視訊等比例放大,直至一邊鋪滿View的某一邊,另一邊超出View的另一邊,再移動到View的正中央,這樣長邊兩邊會被裁剪掉同樣大小的區域,視訊看起來不會變形
//也即是:先把視訊區(實際的大小顯示區)與View(定義的大小)區的兩個中心點重合, 然後等比例放大或縮小視訊區,直至一條邊與View的一條邊相等,另一條邊超過
//View的另一條邊,這時再裁剪掉超出的邊, 使視訊區與View區大小一樣. 這樣在不同尺寸的手機上,視訊看起來不會變形,只是水平或豎直方向的兩端被裁剪了一些.
private void transformVideo(int videoWidth, int videoHeight) {
if (getHeight() == 0 || getWidth() == 0) {
Log.d(TAG, "transformVideo, getHeight=" + getHeight() + "," + "getWidth=" + getWidth());
return;
}
float sx = (float) getWidth() / (float) videoWidth;
float sy = (float) getHeight() / (float) videoHeight;
Log.d(TAG, "transformVideo, sx=" + sx);
Log.d(TAG, "transformVideo, sy=" + sy);
float maxScale = Math.max(sx, sy);
if (this.matrix == null) {
matrix = new Matrix();
} else {
matrix.reset();
}
//第2步:把視訊區移動到View區,使兩者中心點重合.
matrix.preTranslate((getWidth() - videoWidth) / 2, (getHeight() - videoHeight) / 2);
//第1步:因為預設視訊是fitXY的形式顯示的,所以首先要縮放還原回來.
matrix.preScale(videoWidth / (float) getWidth(), videoHeight / (float) getHeight());
//第3步,等比例放大或縮小,直到視訊區的一邊超過View一邊, 另一邊與View的另一邊相等. 因為超過的部分超出了View的範圍,所以是不會顯示的,相當於裁剪了.
matrix.postScale(maxScale, maxScale, getWidth() / 2, getResizedHeight() / 2);//後兩個引數座標是以整個View的座標系以參考的
Log.d(TAG, "transformVideo, maxScale=" + maxScale);
setTransform(matrix);
postInvalidate();
Log.d(TAG, "transformVideo, videoWidth=" + videoWidth + "," + "videoHeight=" + videoHeight);
}
然後在OnVideoSizeChangedListener裡面監聽獲取到了視訊的實際尺寸後呼叫以上方法即可.