给 Android WebRTC 增加美颜滤镜功能

字节流动

共 5312字,需浏览 11分钟

 ·

2021-11-08 13:40

  • 视频采集渲染流程分析


在增加滤镜功能之前,需要对 WebRTC 视频采集的流程有一定了解。


WebRTC 中定义了 VideoCapture 接口类,其中定义了相机的初始化,预览,停止预览销毁等操作。


实现类是 CameraCapture,并且封装了Camera1Capture、Camera2Capture 两个子类,甚至还有屏幕共享。


WebRTC 中开始视频采集非常的简单:


val videoCapture = createVideoCapture()
videoSource = videoCapture.isScreencast.let { factory.createVideoSource(it) }
videoCapture.initialize(surfaceTextureHelper,applicationContext,videoSource?.capturerObserver)
videoCapture.startCapture(48064030)


这里主要看一下 VideoSource类和capturerObserver


VideoSource 中有以下方法


@Override
    public void onFrameCaptured(VideoFrame frame) {
      final VideoProcessor.FrameAdaptationParameters parameters =
          nativeAndroidVideoTrackSource.adaptFrame(frame);
      synchronized (videoProcessorLock) {
        if (videoProcessor != null) {
          videoProcessor.onFrameCaptured(frame, parameters);
          return;
        }
      }
      VideoFrame adaptedFrame = VideoProcessor.applyFrameAdaptationParameters(frame, parameters);
      if (adaptedFrame != null) {
        nativeAndroidVideoTrackSource.onFrameCaptured(adaptedFrame);
        adaptedFrame.release();
      }
    }


采集到的视频帧数据会回调给 onFrameCaptured,在这里会做一下对视频的裁切缩放处理,并通过 nativeAndroidVideoTrackSource 传递给 Native层。


重点是 VideoProcessor 对象,据查是在2019年2月新增的。VideoSource里面有 setVideoProcessor 方法用于设置VideoProcessor,在上面方法中可知,如果设置了VideoProcessor,视频帧则走VideoProcessor的onFrameCaptured,否则的话直接传入 Native。


用 VideoProcessor 来实现处理发送前的视频帧非常方便,我们先来看下VideoProcessor类。


public interface VideoProcessor extends CapturerObserver {
  public static class FrameAdaptationParameters {
   ...

    public FrameAdaptationParameters(int cropX, int cropY, int cropWidth, int cropHeight,
        int scaleWidth, int scaleHeight, long timestampNs, boolean drop)
 
{
      ...
    }
  }

  default void onFrameCaptured(VideoFrame frame, FrameAdaptationParameters parameters) {
    VideoFrame adaptedFrame = applyFrameAdaptationParameters(frame, parameters);
    if (adaptedFrame != null) {
      onFrameCaptured(adaptedFrame);
      adaptedFrame.release();
    }
  }
....
 }


VideoSource中调用的 

onFrameCaptured(frame, parameters) 

并非CapturerObserver的onFrameCaptured,也就是暂时不会传入Native增,它在这个方法中也做了对ViewFrame的裁切缩放,之后再传入底层。


所以我们可以在这里实现对视频帧的美颜滤镜处理。


 class FilterProcessor : VideoProcessor{

               private var videoSink:VideoSink

        override fun onCapturerStarted(success: Boolean) {
        }

        override fun onCapturerStopped() {
        }

        override fun onFrameCaptured(frame: VideoFrame?) { 
          val newFrame = // TODO: 在这对VideoFrame进行视频滤镜美颜处理 
          sink.onFrame(newFrame)
        }

        override fun setSink(sink: VideoSink?) {
            //设置视频接收器 用来渲染并将frame传入Native
          videoSink = sink
        }
    }

val videoCapture = createVideoCapture()
videoSource = videoCapture.isScreencast.let { factory.createVideoSource(it) }
videoSource.setVideoProcessor(FilterProcessor())//设置处理器
videoCapture.initialize(surfaceTextureHelper,applicationContext,videoSource?.capturerObserver)
videoCapture.startCapture(48064030)


美颜的话可以用 GPUImage,也可以用商用SDK。


以上是在应用层的实现,利用 WebRTC自带的类就行。如果是NDK开发,道理也是一样的。


创建一个代理类 CapturerObserverProxy 实现 CapturerObserver,并将真正的 nativeCapturerObserver传进来,Native会回调视频帧数据给 CapturerObserverProxy的 onFrameCaptured。


然后在 onFrameCaptured 中对视频进行美颜滤镜处理,再将处理好的 VideoFrame 用 nativeCapturerObserver 传给底层编码传输。


public class CapturerObserverProxy implements CapturerObserver {
    public static final String TAG = CapturerObserverProxy.class.getSimpleName();

    private CapturerObserver originalObserver;
    private RTCVideoEffector videoEffector;

    public CapturerObserverProxy(final SurfaceTextureHelper surfaceTextureHelper,
                                 CapturerObserver observer,
                                 RTCVideoEffector effector) {

        this.originalObserver = observer;
        this.videoEffector = effector;

        final Handler handler = surfaceTextureHelper.getHandler();
        ThreadUtils.invokeAtFrontUninterruptibly(handler, () ->
                videoEffector.init(surfaceTextureHelper)
        );
    }

    @Override
    public void onCapturerStarted(boolean success) {
        this.originalObserver.onCapturerStarted(success);
    }

    @Override
    public void onCapturerStopped() {
        this.originalObserver.onCapturerStopped();
    }

    @Override
    public void onFrameCaptured(VideoFrame frame) {
        if (this.videoEffector.needToProcessFrame()) {
            VideoFrame.I420Buffer originalI420Buffer = frame.getBuffer().toI420();
            VideoFrame.I420Buffer effectedI420Buffer =
                    this.videoEffector.processByteBufferFrame(
                            originalI420Buffer, frame.getRotation(), frame.getTimestampNs());

            VideoFrame effectedVideoFrame = new VideoFrame(
                    effectedI420Buffer, frame.getRotation(), frame.getTimestampNs());
            originalI420Buffer.release();
            this.originalObserver.onFrameCaptured(effectedVideoFrame);
        } else {
            this.originalObserver.onFrameCaptured(frame);
        }
    }
}

 videoCapturer.initialize(videoCapturerSurfaceTextureHelper, context, observerProxy);


以上就是给 WebRTC 增加美颜功能的实现~


作者:anyRTC
链接:https://juejin.cn/post/7020641037592821790

推荐阅读:

Android FFmpeg 实现带滤镜的微信小视频录制功能

全网最全的 Android 音视频和 OpenGL ES 干货,都在这了

抖音传送带特效是怎么实现的?

所有你想要的图片转场效果,都在这了

浏览 80
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报