一、前言
前面我们已经讲解了视频的编码、解码、网络传输的相关基础知识,相信认真阅读多的朋友,应该熟悉了,有人会问,这些知识能够帮我们做什么呢?本篇文章就来说说具体能做那些项目。由于时间和篇幅的关系,先来说说音视频的采集、编码、推流(网络传输),这种应用场景大多在直播,拍摄视频上传服务器等场景。比如通过手机摄像头拍摄了一段很精彩的视频,发送到朋友圈,这个过程就是本文所要详细描述的音视频采集、编码、推流。这里我们就以Android和ios平台讲解,硬件平台就以各类主流手机。
二、视频采集
2.1.Android视频采集
首先打开手机摄像头需要获取系统权限,目前主要是用Cmaera和Camera2这两个类来实现视频的采集,需要打开如下的权限。在Android7.1平台上还需要动态获取权限,所以需要有用户去确认,是否给定权限。动态获取权限的核心代码如下所示。
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
指明一个需要获取权限的数组,比如需要获取打开摄像头权限、网络权限、存储卡的读写权限、窗口权限等。
private static String[] REQUEST_PERMISSIONS_ARRAY = {
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE",
"android.permission.INTERNET",
"android.permission.ACCESS_NETWORK_STATE",
"android.permission.CAMERA",
"android.hardware.camera",
"android.hardware.camera.autofocus",
"android.permission.SYSTEM_ALERT_WINDOW"};
private static final int REQUEST_PERMISSIONS = 1;
再完成检查权限是否拥有和申请权限
public static void verifyPermissions(Activity activity)
{
try {
//检测是否有写的权限
int permission = ActivityCompat.checkSelfPermission(activity, "android.permission.CAMERA");
if (permission != PackageManager.PERMISSION_GRANTED)
{
// 没有写的权限,去申请写的权限,会弹出对话框
ActivityCompat.requestPermissions(activity,REQUEST_PERMISSIONS_ARRAY,REQUEST_PERMISSIONS);
}
} catch (Exception e) {
e.printStackTrace();
}
}
最后用onCreate方法去实现调用,如下图所示:
@Overrideprotected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
verifyPermissions(MainActivity.this);
}
2.2Android摄像头的打开、预览、预览缓冲设置
通过设置ID获取摄像头属性,并打开摄像头。这里的摄像头ID包括前置和后置摄像头,前置摄像ID是Camera.CameraInfo.CAMERA_FACING_FRONT,后置摄像头ID是Camera.CameraInfo.CAMERA_FACING_BACK。VParam是自己封装的视频相关参数设置。这里的尺寸暂时就设定为1920X1080,你也可以设置其它预览大小。
打开摄像头后,就可以获取摄像头相关参数,并设置参数,如预览像素格式,预览图片大小,预览缓冲区的大小等。注意这里一定要设置预览大小,否则图片会被拉伸,图片会被放大,这里是一个坑。
//获取摄像头
IDmcamera = Camera.open(VParam.getCameraId());
//获取参数
Camera.Parameters Cpara = mcamera.getParameters();
//这里是设置预览像素格式,如果预览格式与编码的像素格式不一致,就要 //像素格式转换
//关于像素格式的文章,后面会有文章详细介绍 Cpara.setPreviewFormat(ImageFormat.NV21);
//获取系统支持的预览图片大小,后面会有方法SelectOptionSize介绍怎样匹配最合适的图片大小
List<Camera.Size> SupportPreSize = parameters.getSupportedPreviewSizes(); Cpara.setPictureSize(SelectOptionSize(SupportPreSize,VParam.getWidth(),VParam.getHeight()).width,SelectOptionSize(SupportPreSize,VParam.getWidth(),VParam.getHeight()).height);
//注意这里一定要设置预览大小,否则图片会被拉伸,图片会被放大,这里/是一个**坑** Cpara.setPreviewSize(SelectOptionSize(SupportPreSize,VParam.getWidth(),VParam.getHeight()).width,SelectOptionSize(SupportPreSize,VParam.getWidth(),VParam.getHeight()).height);
//给camera类配置参数
mcamera.setParameters(Cpara);
//这里是设置横竖屏,暂停设定为横屏
//竖屏是90°
mcamera.setDisplayOrientation(0);
//预览的图片或视频,需要在surfaceview上显示,这个surface就是由外面创建,然后传递过来 mcamera.setPreviewDisplay(surfaceHolder);
//分配回调摄像头空间大小,这个空间就是用来缓存预览视频大小 //这个大小根据视频的长、宽和像素格式来计算
prevBuffer = new byte[videoParam.getWidth() * videoParam.getHeight()*3];
//把prevBuffer 设置为CallbackBuffer mcamera.addCallbackBuffer(prevBuffer );
//这个方法表示与上面配套使用,表示当前对象使用这个buffer mcamera.setPreviewCallbackWithBuffer(this);
//开启预览
mcamera.startPreview();
接下来就说说方法SelectOptionSize,如何去匹配合适的预览图片大小。
private static Camera.Size SelectOptionSize( List<Camera.Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
//这里适合横屏,竖屏预览,这里可能需要修改
double targetRatio = (double) w / h;
Camera.Size OptionSize = null;
double minDiff = Double.MAX_VALUE;