我的音视频技术路线

我的音视频技术路线

音视频基础技术栈

​ 抖音/快手等短视频APP的风靡,让音视频成为当下最火热的技术,越来越多的人想要进入到这个领域,我自己也是从图形方向刚刚踏入这领域不久,音视频方向所包含的技术栈非常复杂,我自己也在一点一点慢慢钻研,这里面每一个方向都值得深入研究,而且随着5G时代的到来,音视频方向的应用会更加广泛,所以希望自己能掌握更多的关于音视频方向的技能,未来可以探索更多的音视频玩法。然后这篇博客主要是想梳理一下我自己关于音视频这个方向的学习路线,分享出来的同时也能鼓励自己朝着这个方向继续深耕下去。

​ 关于音视频方向的基础技能分支,先来看一张图(图片来自网上)

basic.jpg

采集:音视频数据来源,比如Android Camera数据采集

渲染:将采集得到的数据展示到Surface上,并添加一些图形效果

处理:对源数据的加工,比如添加滤镜、特效,还有多视频剪辑、变速、转场等等

编解码:对音视频数据进行压缩封装,减少数据量,方便传输

传输:对采集加工完成的数据传输至客户端,比如直播推流、拉流

1. 关于音视频数据采集(Android)

​ 因为自己主要是对Android Camera比较熟悉,所以我主要梳理一下这方面的知识点,我会从最基础的Android Camera API的接口以及基本流程开始梳理,后面进阶部分主要是结合Camera HAL高通架构进一步详细梳理底层的Camera原理,最后是我对于Camera专业视频方向的一些探索。

1.1 Android Camera API

​ Android 5.0之后Camera接口升级成API2,因为Camera API 1接口过于简单,根本体现不出硬件能力,用户能控制的不多,比如拿不到RAW数据,控制不了相机参数的下发等等,如下代码看下他内部的基本接口

​ Camera API 1

try {
   
   
            mCamera = Camera.open(mCurrentCamera);
            Camera.Parameters params = mCamera.getParameters();
            List<Camera.Size> previewSizes = params.getSupportedPreviewSizes();

            Camera.Size preViewSize = previewSizes.get(previewSizes.size() > 4 ? previewSizes.size() - 4 : 0);
            params.setPreviewSize(preViewSize.width, preViewSize.height);
            mPreviewHeight = preViewSize.height;
            mPreviewWidth = preViewSize.width;
            Log.e(TAG, "preViewSize->width: " + preViewSize.width + ", preViewSize->height: " + preViewSize.height);

            params.setPictureFormat(ImageFormat.JPEG);
            params.setJpegQuality(100);
            //是否开启闪光
            List<String> flashModes = params.getSupportedFlashModes();
            if (flashModes != null && flashModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {
   
   
                params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
            }

            mCamera.setPreviewCallback(this);
            mCamera.setDisplayOrientation(PORTRAIT_MODE);
            mCamera.setParameters(params);
            if(!Consts.SHOW_CAMERA) {
   
   
                LogUtils.log_main_step("相机开始preview");
                mCamera.startPreview();
            }

            LogUtils.logd(TAG, "camera init finish.....");
        } catch (Exception e) {
   
   
            LogUtils.loge(TAG, e.getMessage());
            e.printStackTrace();
        }

然后在CameraPreview callback里面就可以处理相机数据了

@Override
    public void onPreviewFrame(byte[] data, Camera camera) {
   
   
        Camera.CameraInfo mCameraInfo = new Camera.CameraInfo();
        //如果使用前置摄像头,请注意显示的图像与帧图像左右对称,需处理坐标
        boolean frontCamera = (mCurrentCamera == Camera.CameraInfo.CAMERA_FACING_FRONT);

        //获取重力传感器返回的方向
        int dir = getDirection();

        int rotate = (dir ^ 1);
        //Log.e("stRotateCamera  ", (dir ^ 1) + " rotate result");

        //在使用后置摄像头,且传感器方向为0或2时,后置摄像头与前置orentation相反
        if (!frontCamera && dir == 0) {
   
   
            dir = 2;
        } else if (!frontCamera && dir == 2) {
   
   
            dir = 0;
        }
        dir = (dir ^ 2);
      	//.....
      	process(data)
    }

关于Camera2的介绍

Camera 2.0: New computing platforms for computational photography

​ Camera API2对比API 1改动非常大,主要配合HAL3进行使用,功能和接口都更加齐全,同时使用起来也会更加复杂,但如果熟悉之后,也能拍摄出更加丰富的效果。下图关于Camera API 2的几个使用场景

在这里插入图片描述

​ 然后结合具体代码讲几个Camera2的主要接口

  1. CameraManager

关于硬件能力的统一封装接口

CameraManager cameraManager = (CameraManager)mContext.getSystemService(Context.CAMERA_SERVICE);
//可以拿到所以的相机列表,比如Wide,Tele,Macro,Tele2x,Tele4x等等
String[] cameraIdList = cameraManager.getCameraIdList();
//根据Camera ID拿到对应设备支持的能力
CameraCharacteristics cameraCharacteristics =
  cameraManager.getCameraCharacteristics(cameraIdStr);
//比如用这个去判断支持的最小Focus distance
Float focusDistance = mCharacteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE);

​ 现如今机型的摄像头的数量越来越多,组合也越来越丰富,不同的Camera负责不同的能力,比如我们需要更大的拍摄范围会选择Ultra Wide,比如小米一亿像素的Wide,还有Macro等等。

  1. CaptureDevice

我们可以在OpenCamera Callback拿到CameraDevice

String cameraIdStr = String.valueOf(mCameraId);
            cameraManager.openCamera(cameraIdStr, mCameraStateCallback, mMainHandler);
//
private CameraDevice.StateCallback mCameraStateCallback = new CameraDevice.StateCallback() {
   
   

        @Override
        public void onOpened(@NonNull CameraDevice camera) {
   
   
            synchronized (SnapCamera.this) {
   
   
                mCameraDevice = camera;
            }
            if (mStatusListener != null) {
   
   
                mStatusListener.onCameraOpened();
            }
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
   
   
            Log.w(TAG, "onDisconnected");
            // fail-safe: make sure resources get released
            release();
        }

        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
   
   
            Log.e(TAG, "onError: " + error);
            // fail-safe: make sure resources get released
            release();
        }
    };
  1. CaptureRequest

​ 通过CameraDevice可以创建CaptureRequest,类型主要有以下几种,1-6分布用于预览、拍照、录制、录制中拍照、ZSL、手动。

public static final int TEMPLATE_MANUAL = 6;
public static final int TEMPLATE_PREVIEW = 1; 
public static final int TEMPLATE_RECORD = 3;
public static final int TEMPLATE_STILL_CAPTURE = 2;
public static final int TEMPLATE_VIDEO_SNAPSHOT = 4;
public static final int TEMPLATE_ZERO_SHUTTER_LAG = 5;
//创建的代码
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

//这里是更加专业一点的用法,可以不看,就是拿到RequestBuilder之后我们可以去下发各种TAG,如下设置AE/AWB锁
CaptureRequestBuilder.applyAELock(request, mConfigs.isAELocked());
CaptureRequestBuilder.applyAWBLock(request, mConfigs.isAWBLocked());
  1. Surface

Surface主要是用来接相机返回的数据,可以支持不同的数据类型和分辨率

//SurfaceTexture的方式,用于预览
mSurfaceTexture = new SurfaceTexture(false);
mSurfaceTexture.setDefaultBufferSize(optimalSize.width, optimalSize.height);
mPreviewSurface = new Surface(mSurfaceTexture);
//ImageReader 的方式, Format可以是YUV、RAW,DEPTH等
mPhotoImageReader = ImageReader.newInstance(size.getWidth(), size.getHeight(),
                ImageFormat.JPEG, /* maxImages */ 2);
mPhotoImageReader.setOnImageAvailableListener(mPhotoAvailableListener, mCameraHandler);
//创建session
List<Surface> surfaces = Arrays.asList(mPreviewSurface, mPhotoImageReader.getSurface());
mCameraDevice.createCaptureSession(surfaces, mSessionCallback, mCameraHandler);     
  1. CaptureSession

通过上面的CameraDevice创建Session,在Callback里面拿到Session

mCameraDevice.createCaptureSession(surfaces, mSessionCallback, mCameraHandler);
private CameraCaptureSession.StateCallback
            mSessionCallback = new CameraCaptureSession.StateCallback() {
   
   

        @Override
        public void onConfigured(@NonNull CameraCaptureSession session) {
   
   
            synchronized (SnapCamera.this) {
   
   
                if (mCameraDevice == null) {
   
   
                    Log.e(TAG, "onConfigured: CameraDevice was already closed.");
                    session.close();
                    return;
                }
                mCaptureSession = session;
            }
            startPreview();
            capture();
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
   
   
            Log.e(TAG, "sessionCb: onConfigureFailed");
        }
    };

​ 后面所有的采集工作都是基于Session,关于Session机制最早是诺基亚提出来的,后面苹果的IOS也采取Session作为相机拍照,录制的会话单元。

/*发起请求,后续相机开始往surface输出数据,这个可以在onConfigured里面调用,如上面的代码       		    startPreview();
capture();*/
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
                    mCaptureCallback, mCameraHandler);
mCaptureSession.capture(requestBuilder.build(), mCaptureCallback, mCameraHandler);

// 这个是针对120/240/960Fps recording
mCaptureSession.setRepeatingBurst(requestList, mCaptureCallback, mCameraHandler);
mCaptureSession.captureBurst(requestList, listener, handler);

然后还可以通过Session控制结束动作

mCaptureSession.abortCaptures();
mCaptureSession.stopRepeating();

​ 对象更加专业一点的系统相机而言,在setRepeatingRequest之前可以还需要做很多工作,比如拍照要等Focus finish,还需要Apply一些列参数,比如FocusMode, Zoom, AE/F Lock, Video FPS…

总结:上面只是一个大概流程,至于每个流程你需要去控制什么可能就会更加复杂,但其实对于第三方APP,需要控制的不多,基本就是OpenCamera -> 配置Surface -> 创建Session -> 设置参数 -> RepeatingRequest ->ImageReader或者GLSurafce回调接数据做后续处理。可能有些更加专业一点的相机应用,可能会涉及的一些专业参数的下发,比如IOS, FocusDistance, Shutter Time, EV…

1.2 Camera HAL

Android Camera硬件抽象层(HAL,Hardware Abstraction Layer)主要用于把底层camera drive与硬件和位于android.hardware中的framework APIs连接起来。Camera子系统主要包含了camera pipeline components 的各种实现,而camera HAL提供了这些组件的使用接口

官网的系统架构图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4kKP7Iai-1585555092589)(https://i.loli.net/2020/03/30/xBLf8olEGuO3FAq.png)]

​ 做相机开发除了第一部分讲到的APP层面的相机控制,然后就是HAL层的开发了,HAL层由芯片厂商定制(比如高通等),手机厂商可以再其上增加一些内容。Camera HAL 经历1-3的版本迭代,不同的硬件支持的Camera2程度不一样,主要有以下等级

//每一个等级啥意思自己搜吧
INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
INFO_SUPPORTED_HARDWARE_LEVEL_FULL
INFO_SUPPORTED_HARDWARE_LEVEL_3
INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL

想了一下这一块比较复杂,我自己也不是非常的清楚,而且好像第三方APP一般不会涉及,后面有机会单独整理吧(挖坑1)

1.3 CameraX

​ 如上看到的Camera API2非常复杂,为了简化流程,Google推出CameraX,借助 CameraX,开发者只需两行代码就能利用与预安装的相机应用相同的相机体验和功能。 CameraX Extensions 是可选插件,通过该插件,您可以在支持的设备上向自己的应用中添加人像、HD

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值