最近在處理視頻錄製的一些東西,趁閒暇時間把瑣碎的東西整理下供後續用到查看
說到視頻錄製,現將整體流程圖獻上,讓我們對視頻錄製有個整體的瞭解。
在整個流程中最爲繁瑣的點要屬尺寸及方向,難點在預覽及編碼處理,本講主要解析尺寸和方向。
談到尺寸,腦袋裏面一定要以下幾個尺寸概念有所瞭解:
- 預覽幀尺寸
- 拍攝幀尺寸
- 視頻編碼尺寸
- 顯示預覽控制尺寸
預覽幀尺寸
每臺手機都支持很多預覽幀尺寸,且長寬比例不一,通常我們選採用getSupportedPreviewSize接口獲取系統支持的預覽幀尺寸集合,然後在選出符合要求長寬比的尺寸集,最後再根據顯示預覽控件尺寸進行比對,選擇一個剛好大於顯示預覽控件尺寸大小的預覽幀尺寸即可,建議方法如下:
private Size chooseOptimalSize(SortedSet<Size> sizes) {
if (!mPreview.isReady()) { // Not yet laid out
return sizes.first(); // Return the smallest size
}
int desiredWidth;
int desiredHeight;
final int surfaceWidth = mPreview.getWidth();
final int surfaceHeight = mPreview.getHeight();
if (isLandscape(mDisplayOrientation)) {
desiredWidth = surfaceHeight;
desiredHeight = surfaceWidth;
} else {
desiredWidth = surfaceWidth;
desiredHeight = surfaceHeight;
}
Size result = null;
for (Size size : sizes) { // Iterate from small to large
if (desiredWidth <= size.getWidth() && desiredHeight <= size.getHeight()) {
return size;
}
result = size;
}
return result;
}
拍攝幀尺寸
拍攝幀尺寸和預覽幀尺寸相似,也是採用getSupportedPictureSize接口獲取系統支持的拍攝幀尺寸集,然後和預覽幀尺寸選擇類似,僅在最後選擇時不是選擇剛好大於顯示預覽控件尺寸的size,而是選擇符合長寬比例最大的size,這個分辨率高點清晰。
視頻編碼尺寸
視頻編碼尺寸,也就是說你想讓編碼錄製好的視頻分辨率多少,通常這個尺寸需業務方按需設置不同檔位,然後根據不同檔位進行設置調整
顯示預覽控件尺寸
顯示預覽控件尺寸,只要在該控件進行layout行爲後,即可通過getWidth()/getHeight()接口獲取對應的尺寸。
對尺寸的選擇,最好各種size長寬比例一致,這樣可以避免很多因size不同導致的問題。
瞭解了尺寸問題後,對圖像出現拉伸及顯示模糊等問題,就能很快定位並將其解決掉。接下來,我們就重點突破方向問題
談到方向也是先系統羅列下幾個所需瞭解的方向概念:
- 手機自然方向
- 屏幕切換方向
- 傳感器取景方向
- 預覽幀方向
- 拍攝幀方向
接着來逐一講解各個方向,所謂手機自然方向,就是指:手機橫屏/豎屏狀態,以左上角爲原點,往右爲x軸方向,往左爲y軸方向
傳感器取景方向就是相機取景錄製圖像數據的方向,默認情況下,手機的傳感器取景方向一般都是手機的右上角,當手機橫屏時手機的傳感器取景方向就和手機自然方向相同。
說到這,恐怕有人感覺這些文件及圖片展示都還是比較抽象,不理解這些手機自然方向,手機傳感器取景方向,x方向,y方向到底有什麼作用,完全是一堆莫名的概念充斥着天靈。換種表達方式吧,我們拿了張白紙準備寫字,習慣的表達是不是從左上角開始逐行寫下來,直至寫完整張紙,其實手機屏幕顯示也是如此,它也是按自然方向從左往右(x方向)進行逐行(y方向)渲染,最終呈現手機上展示的內容。現在有個問題,相機傳感器取景方向在豎屏情況下,x方向是向下,y方向是從右往左,這樣不做任何修改的話,相機獲取到的圖片數據在手機屏幕顯示就是成90度差。這樣說,你可能覺得還是不直觀,讓我們看下下圖:
現在的問題來了傳感器取景方向和手機自然方向都瞭解了,但取到的圖像方向顯示到屏幕上,用戶到看不正常呀,那怎麼讓所見即手機所示呢?爲獲取正常的預覽顯示方向,讓我們先來了解下以下兩個概念:
屏幕切換方向
這個方向其實不理解也沒關係,可以通過系統接口獲取Activity.getWindowManager().getDefaultDisplay().getRotation(),這個
方法獲取到的值表示,當前屏幕以自然方向爲基準逆時針選擇了多少度,也就是說如果此時手機順時針旋轉對應的角度即可回到手機自然方向,如圖所示:
相機圖片方向
說白了這個方向其實就是傳感器取景方向,不過不了解也沒有關係,通過Camera.CameraInfo.orietation可以獲取到,這個角度表示如果圖片做順時針旋轉對應的角度即可回到手機自然方向,正常顯示在屏幕上,如圖:
圖中藍色點爲手機自然方向的座標原點,即渲染原點;紅色原點爲原始圖片座標原點,說白了,就是讓兩個點重合,x方向和y方向相同,即可讓圖片正常顯示到手機上,從圖中所示也非常清晰可以說明當旋轉90的時候,就可以正常顯示
說了這麼多,我們對屏幕選擇方向,相機圖片方向有了清晰的認知,接下來就可以進入正題,我們如何保證預覽看到的圖片是正常顯示的,而非90、180、270度的錯位顯示。官方推薦做法如下:
public static void setCameraDisplayOrientation (Activity activity, int cameraId, android.hardware.Camera camera) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo (cameraId , info);
int rotation = activity.getWindowManager ().getDefaultDisplay ().getRotation ();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else {
// back-facing
result = ( info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation (result);
}
其中degrees就是屏幕旋轉方向,info.orientation就是相機圖像方向,最終獲取的旋轉方向就是通過這個兩個方向獲取的,可能很多同學對這段優秀的代碼不瞭解,基本上都是copy使用,其實瞭解各個方向後,其實還是很簡單的,實在還不懂,沒關係,下面有圖詳解(該圖以後置攝像頭爲例,前置加個mirror就可以):
挑一個例子講解下,你就很容易明白這個圖了,那就以orientation=90 & degrees=270爲例吧,你可以按着前文說明理解下,從這兩個值可以獲取到這些信息:1)屏幕已經往逆時針旋轉了270度;2)相機圖片需往順時針轉了90度;對1)而言,原本要順時針旋轉270度才能回到自然方向,對2)而言需要旋轉90度才能回到自然方向也就是說此時相機圖片相對於自然方向逆時針旋轉了90度;當1)順時針旋轉了180時,相機圖片原點就和屏幕原點重合了,此時預覽圖片就可以正常展示了。至於後續是否要一起再旋轉90回到自然方向,就不是我們關心的事情了。我們僅關心圖片在手機中正常展示即可。
採用官方的方法計算出旋轉角度後,採用setDisplayOrientation設置後預覽即可顯示正常,但該方法僅僅是設置了預覽幀方向,也就是你看到手機裏的圖片顯示正常了,它不影響拍攝出來,錄製出來的圖像,那如何讓拍攝出來的圖片或視頻顯示也正常呢?
接下來就是拍攝幀方向和視頻展示方向,其實這兩個方向差不多,計算方式也一樣,唯一不同的時採用不同的方式把方向信息寫入圖像或視頻中,以供圖片加載器或視頻播放器獲知這個方向信息作出響應旋轉顯示正常。
拍攝幀方向
方向記錄採用Camera.Parameters.setRotation()
視頻展示方向
方向記錄採用MediaMuxer.setOrietationHint()
處理該方法其實官方也有推薦,也是結合屏幕切換方向以及相機圖片方向進行處理(這個屏幕切換方向最好監聽陀螺儀來獲取)
public void onOrientationChanged(int orientation) {
if (orientation == ORIENTATION_UNKNOWN) {
return;
}
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
orientation = (orientation + 45) / 90 * 90;
int rotation = 0;
if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
rotation = (info.orientation - orientation + 360) % 360;
} else { // back-facing camera
rotation = (info.orientation + orientation) % 360;
}
mParameters.setRotation(rotation);
}
到此方向信息基本講解完成,自行腦補橫屏/豎屏、前置攝像頭以及後置攝像頭場景區別。
最後說明,文中示例說明圖很多引用其他文章,但忘記原圖鏈接,若原作者看到請知會,一定附上參考鏈接,感謝!