webrtc-发送端-Android编码器创建.drawio.pnggithub:https://github.com/bigonelby/webrtcUml/tree/master/latest
-
这张图介绍了Android编码器的创建过程
-
前面已经介绍过iOS编码器的创建过程,Android编码器的创建过程比iOS编码器创建过程类似,也是从native层,转到java层,再从java层回到native层。但是Android上有两点比iOS要复杂些:其一就是Android上支持软编码fallback;其二就是Android编码器支持纯java层和java native wrapper两种
-
首先还是从应用设置给底层的factory说起。通过PeerConnectionFactory传入这些factory,当然,传入的都是java层的。即Java层的VideoEncoderFactory。具体的实现类是DefaultVideoEncoderFactory。这个稍后再说。不过对于底层而言,最终还需要一个native层的VideoEncoderFactory,这样,当然就需要一个转换类了,这个转换类本身是native层的,并且继承了VideoEncoderFactory,但真正的创建工作,是通过jni调用java层的VideoEncoderFactory完成的。完成这个工作的转换类就是VideoEncoderFactoryWrapper
-
目前,我们知道了,上层传入的是java层的VideoEncoderFactory,webrtc进行了内部处理,即创建了native层的VideoEncoderFactoryWrapper,当需要创建编码器时,通过java层的VideoEncoderFactory创建
-
下面看看java层的编码器。本身VideoEncoderFactory工厂类创建编码器VideoEncoder是很正常的。不过Android的编码器是支持fallback的,这就涉及到了三个编码器,其一是支持fallback的编码器;其二是硬编码器;其三是软编码器。对应的java层的类分别为:VideoEncoderFallback,HardwareVideoEncoder和H264Encoder。他们的关系是VideoEncoderFallback持有两个编码器,primary和fallback。优先采用primary编码器进行编码,即HardwareVideoEncoder。当编码出错超过一定阈值时,启动fallback编码器并通过fallback编码器进行编码,这里的fallback编码器就是H264Encoder。java层既然有三个编码器,当然也就对应了三个编码器的工厂类,分别为:DefaultVideoEncoderFactory,将会创建VideoEncoderFallback;HardwareVideoEncoderFactory,将会创建HardwareVideoEncoder;以及SoftwareVideoEncoderFactory,将会创建H264Encoder。不难猜测,DefaultVideoEncoderFactory持有了HardwareVideoEncoderFactory和SoftwareVideoEncoderFactory这两个工厂。当通过DefaultVideoEncoderFactory创建编码器时,首先通过HardwareVideoEncoderFactory创建出HardwareVideoEncoder;接着通过SoftwareVideoEncoderFactory创建H264Encoder;最后创建出VideoEncoderFallback并将创建好的软硬编码器作为参数传入构造函数。
-
接着说说编码器,Android中的编码器分为硬编码器,比如HardwareVideoEncoder,和WrappedNativeVideoEncoder两种。怎么理解呢?这里有个非常重要的技巧就是createNativeVideoEncoder这个方法是如何实现的。对于HardwareVideoEncoder而言,这个是硬编码器,根正苗红,直接和MediaCodec打交道,并不需要native层的配合,这点事他自己干就完全可以了。另一种,WrappedNativeVideoEncoder则是一个名副其实的傀儡了,他自己本身完不成任何编码的能力。这个类实现了VideoEncoder接口的若干方法,但是实现的方式都是抛出异常,也就是说,别人可别调用我的这些方法,这些方法我都搞不定。那么真正干活的是谁呢?是native层的encoder,每个WrappedNativeVideoEncoder都可以创建出自己的nativeEncoder,通过接口createNativeVideoEncoder来完成。创建出的encoder,继承了底层的VideoEncoder,并且可以独立完成所有的编码工作,换言之,不需要通过jni接口反调用java层的接口,不需要java层的任何辅助。对于HardwareVideoEncoder这种不属于WrappedNativeVideoEncoder而言,虽然可以独立完成编码工作,但毕竟所有pipeline都是底层驱动的,因此还需要一个native层的转换类VideoEncoderWrapper来完成这个适配的过程,VideoEncoderWrapper本身继承自VideoEncoder,并且所有的接口实现,都是通过jni反调上层java层的HardwareVideoEncoder完成。小结一下,Android的编码器分为纯java编码器(硬编码器)和native wrapped编码器,对于硬编码器而言,native层有转换类VideoEncoderWrapper继承自VideoEncoder并通过jni回调到java层,核心工作在java层完成;对于native wrapped编码器,则java层不做任何工作,创建出的native encoder继承了VideoEncoder,并进行相应的工作
-
明白了编码器的这两种分类,java层的编码器可以转换为native层的编码器了,这是我们想要的。对于硬编码器,其对应的native层的编码器就是VideoEncoderWrapper;对于软编码器这种native wrapped编码器,通过createNativeVideoEncoder方法创建native层所对应的编码器,这个编码器就是我们需要的native的编码器。如何判断一个java层的编码器是纯java层的,还是native wrapped的呢?就是通过接口createNativeVideoEncoder方法的返回值!如果返回值为0,则代表不需要native encoder,则为纯java层encoder;如果返回值非0,则为native wrapper encoder
-
饶了一大圈,我们梳理一下整个pipeline,上层传下来的VideoEncoderFactory,将被底层包装为native层的VideoEncoderFactoryWrapper。底层创建编码器时,将用这个factory的CreateVideoEncoder方法创建native层的encoder。首先通过保存的java层的factory,即DefaultVideoEncoderFactory,创建出java层的encoder,即VideoEncoderFallback,再将java层的encoder转换为native层的encoder,具体的方法是,由于VideoEncoderFallback为WrappedNativeVideoEncoder,因此直接通过其createNativeVideoEncoder方法,直接创建出native的encoder即可
-
最后再简单的说一下fallback。java层的VideoEncoderFallback所创建出的native层的编码器为VideoEncoderSoftwareFallbackWrapper,这个编码器的特殊之处在于他同时拥有硬编码器和软编码器两个编码器,这两个编码器都是通过java层的编码器转换而来,具体的转换方法参见上述的7。首先通过硬编码器进行编码,如果在编码过程中出错超过一定阈值,则会初始化软编码器,交由软编码器编码,这就实现了fallback的过程
网友评论