Android Camera系列(六):MediaCodec视频编码上-编码YUV

己所不欲勿施于人

本系列主要讲述Android开发中Camera的相关操作、预览方式、视频录制等。项目结构简单、代码耦合性低,旨在帮助大家能从中有所收获(方便copy :) ),对于个人来说也是一个总结的好机会。

Alt

引言

前面我们已经花费了大量的章节来讲解Camera配合各种View用来预览、拍照等,接下来我们来讲解如何进行视频录制。Android中要进行录制视频有很多种方式,如:使用系统相机录制、MediaRecorder+Camera自定义录制等。如果上面的方式能够满足你的需求,那本章你可以划走了。

假如有这么一种需求,我们要获取Camera的帧数据流YUV进行实时检测识别,同时我又要将检测到的数据进行保存,那么MediaCodec就很合适,MediaCodec(硬编解码器)主要对帧数据进行编解码处理。实际的例子就好比抖音拍摄视频,人实时美颜或贴图后将视频保存。

除了可以使用MediaCodec对数据流进行编解码,还有ffmpeg同样也可以。

  • MediaCodec属于硬解码,处理数据使用GPU效率高,功能受硬件限制兼容性略低。
  • ‌FFmpeg属于软解码,处理数据使用CPU效率低,功能强大兼容性好。当然该方式不在我们本章讨论范围

MediaCodec介绍

MediaCodec是Android平台上的一个多媒体编解码器,它可以用于对音频和视频进行编解码。通过MediaCodec,开发者可以直接访问底层的编解码器,实现更高效的音视频处理。同时,MediaCodec也支持硬件加速,可以利用设备的硬件资源来提高编解码的性能。MediaCodec主要应用于以下几个方面:

  1. 音视频编解码:MediaCodec可以对音频和视频进行硬件加速的编解码处理,可以实现高效的音视频处理和播放。
  2. 多媒体格式支持:支持常见的音视频格式,包括H.264、AAC、MP3等,可以进行解码和编码操作。
  3. 硬件加速:利用设备的硬件加速功能,可以提高音视频处理的效率和性能。
  4. 实时处理:支持实时的音视频处理,适用于实时通信、直播等场景。
  5. 自定义处理:可以通过MediaCodec进行自定义的音视频处理,如滤镜、特效等操作。

MediaCodec在Android平台上提供了强大的音视频编解码功能,可以用于多媒体应用的开发和优化。

硬编码流程

  1. 使用MediaCodec,将yuv数据编码获得h264数据
  2. MediaMuxer用来封装h264获得一个mp4文件

MediaCodec使用

1. 创建MediaCodec

通过静态方法创建:createDecoderByTypecreateEncoderByTypecreateByCodecName

使用 createDecoderByTypecreateEncoderByType两个,创建用于解码和编码的实体,传入参数为编解码器的mime type名称。createByCodecName需要传入具体编解码器的名称。

官方建议:最好使用MediaCodecList.findEncoderForFormat和createByCodecName来确保生成的编解码器可以处理给定的格式。

mime可用的类型:

*   "video/x-vnd.on2.vp8" - VP8 video (i.e. video in .webm)
*   "video/x-vnd.on2.vp9" - VP9 video (i.e. video in .webm)
*   "video/avc" - H.264/AVC video
*   "video/hevc" - H.265/HEVC video
*   "video/mp4v-es" - MPEG4 video
*   "video/3gpp" - H.263 video
*   "audio/3gpp" - AMR narrowband audio
*   "audio/amr-wb" - AMR wideband audio
*   "audio/mpeg" - MPEG1/2 audio layer III
*   "audio/mp4a-latm" - AAC audio (note, this is raw AAC packets, not packaged in LATM!)
*   "audio/vorbis" - vorbis audio
*   "audio/g711-alaw" - G.711 alaw audio
*   "audio/g711-mlaw" - G.711 ulaw audio

最常用的是 : “video/avc” - H.264/AVC video,现在网络上流传的短视频的视轨格式基本上都是H264。

private static final String MIME_TYPE = "video/avc";

// 创建MediaCodec
MediaCodec mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);

2. 配置MediaCodec

实例化MediaCodec后,我们需要配置一些参数如:视频录制的宽高、码率、帧率等信息。配置使用的类是MediaFormat

调用MediaCodecconfigure方法:

public void configure(
            MediaFormat format,
            Surface surface,
            MediaCrypto crypto,
            int flags
);
  • MediaFormat format:输入数据的格式(解码器)或输出数据的所需格式(编码器)。
  • Surface surface:指定surface,一般用于解码器,设置后解码的内容会被渲染到所指定的surface上。无需要则传null
  • MediaCrypto crypto:指定一个crypto对象,用于对媒体数据进行安全解密。对于非安全的编解码器,传null。
  • int flags:当组件是编码器时,flags指定为常量CONFIGURE_FLAG_ENCODE。

后面三个参数基本是固定的,基本不需要变化。

第一个参数MediaFormat是需要我们深入去了解下,解码的情况下,该参数大多通过MediaExtractor从视频中获取,这里先不讨论。编码的情况实体需要由我们来进行创建,常用的方法是它的静态方法:createVideoFormatcreateAudioFormat
以视频为例:

MediaFormat format = MediaFormat.createVideoFormat(MIME,width, height);
  • MIME:第一步中的mine_type
  • width,height:视频的分辨率/高宽

MediaFormat 必须设置的参数:YUV编码格式、比特率、帧率、关键帧间隔,不设置会报异常。

// 设置编码yuv格式
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
// 设置比特率
format.setInteger(MediaFormat.KEY_BIT_RATE, 1 * 1024 * 1024);
// 设置帧率
format.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
// 设置关键帧间隔,单位:秒
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);

需要特別说明的是MediaFormat.KEY_COLOR_FORMAT :该属性用于指明video编码器的颜色格式,具体选择哪种颜色格式与输入的视频数据源颜色格式有关。
比如:Camera预览采集的图像流通常为NV21或YV12,那么编码器需要指定相应的颜色格式,否则编码得到的数据可能会出现花屏、叠影、颜色失真等现象。

MediaCodecInfo.CodecCapabilities.存储了编码器所有支持的颜色格式,常见颜色格式映射如下:

// 最常用的
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar, // 19, I420,测试通过
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar, // 21, NV12,测试通过
// 其他不常用
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar, // 20, I420,测试通过
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar, // 39, NV12, 测试通过

YUV各种格式还不熟悉的,请看下面的说明:

  • YUV420p: I420、YV12
  • YUV420sp: NV12、NV21

同样,对于一个6*4的图像,这四种像素格式的存储方式如下:

	Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
	Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
	Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
	Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
	U U U U U U      V V V V V V      U V U V U V      V U V U V U
	V V V V V V      U U U U U U      U V U V U V      V U V U V U
     - I420 -          - YV12 -         - NV12 -         - NV21 -

虽然MediaCodec支持了多种YUV编码格式,而且代码中也有对应的值,但是正如我们引言中说的MediaCodec兼容性不好。支持哪种硬编码格式需要根据硬件来定,这也是大多数初入MediaCodec的人来说最头疼的地方。

3. 选择合适的编码器

MediaCodec都知道是硬编解码器,其实不然。他也是支持软编解码的,软编解码由Google实现,硬编解码由厂商自定义。这也就是兼容性的根源,因为我们不知道厂商是否真的支持硬编解码。我们只能自己查找合适的编解码器。而每种编解码器支持编码的YUV格式又不尽相同,我们是优先选择编码器还是优先选择YUV格式这是我们需要抉择的。

看过大部分的博文讲解都是优先选择编码器,而一旦我们选择的编码器后,就只能使用编码器中支持的YUV格式进行编码,如果发现YUV格式不是我们想要的,那么就会编码失败。而我们这里优先根据YUV格式来选择编码器,这么做的理由是有更好的兼容性,但是可能会牺牲一部分性能。

public class MediaVideoBufferEncoder extends MediaEncoder implements IVideoEncoder {
   
   

    private static final String MIME_TYPE = "video/avc";

    protected int mColorFormat;

	...
    
    /**
     * select the first codec that match a specific MIME type
     *
     * @param mimeType
     * @return null if no codec matched
     */
    @SuppressWarnings("deprecation")
    protected final MediaCodecInfo selectVideoCodec(final String mimeType) {
   
   
        if (DEBUG) Log.v(TAG, "selectVideoCodec:");

        // get the list of available codecs
        final int numCodecs = MediaCodecList.getCodecCount();
        // 硬编码器列表
        List<MediaCodecInfo> hardwareCodecInfoList = new ArrayList<>();
        // 软编码器列表
        List<MediaCodecInfo> softwareCodecInfoList = new ArrayList<>();

        for (int i = 0; i < numCodecs; i++) {
   
   
            final MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);

            if (!codecInfo.isEncoder()) {
   
     // skipp decoder
                continue;
            }

            /**
             * 硬件编解码器
             * OMX.qcom.video.encoder.heic
             * OMX.qcom.video.decoder.avc
             * OMX.qcom.video.decoder.avc.secure
             * OMX.qcom.video.decoder.mpeg2
             * OMX.google.gsm.decoder
             * OMX.qti.video.decoder.h263sw
             * c2.qti.avc.decoder
             *
             * 软件编解码器,通常以OMX.google或者c2.android开头
             * OMX.google.h264.encoder
             * c2.android.aac.decoder
             * c2.android.aac.decoder
             * c2.android.aac.encoder
             * c2.android.aac.encoder
             * c2.android.amrnb.decoder
             * c2.android.amrnb.decoder
             */
            // select first codec that match a specific MIME type and color format
            final String[] types = codecInfo.getSupportedTypes();
            for (int j = 0; j < types.length; j++) {
   
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值