最近在处理视频录制的一些东西,趁闲暇时间把琐碎的东西整理下供后续用到查看
说到视频录制,现将整体流程图献上,让我们对视频录制有个整体的了解。
在整个流程中最为繁琐的点要属尺寸及方向,难点在预览及编码处理,本讲主要解析尺寸和方向。
谈到尺寸,脑袋里面一定要以下几个尺寸概念有所了解:
预览帧尺寸
每台手机都支持很多预览帧尺寸,且长宽比例不一,通常我们选采用getSupportedPreviewSize接口获取系统支持的预览帧尺寸集合,然后在选出符合要求长宽比的尺寸集,最后再根据显示预览控件尺寸进行比对,选择一个刚好大于显示预览控件尺寸大小的预览帧尺寸即可,建议方法如下:
private Size chooseOptimalSize(SortedSet 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);
}
到此方向信息基本讲解完成,自行脑补横屏/竖屏、前置摄像头以及后置摄像头场景区别。
最后说明,文中示例说明图很多引用其他文章,但忘记原图链接,若原作者看到请知会,一定附上参考链接,感谢!