音频解码器是干什么用的 解码器的主要功能

1.MediaCodec的工作原理编解码器类Android提供了访问低级多媒体编解码器的接口,它是Android低级多媒体架构的一部分。通常与MediaExtractor、MediaMuxer、AudioTrack配合使用

本文最后更新时间:  2023-05-02 05:42:50

1.MediaCodec的工作原理

编解码器类Android提供了访问低级多媒体编解码器的接口,它是Android低级多媒体架构的一部分。通常与MediaExtractor、MediaMuxer、AudioTrack配合使用,可以对H.264、H.265、AAC、3gp等常见音视频格式进行编解码。

广义来说,MediaCodec的工作原理是处理输入数据生成输出数据。具体来说,MediaCodec在编解码过程中使用一组输入/输出缓冲区同步或异步处理数据:首先客户端将待编解码的数据写入获取的Codec的输入缓冲区并提交给codec,codec处理后传输到编码器的输出缓冲区,同时回收客户端对输入缓冲区的所有权;然后,客户端从获得编解码器输出缓冲区中读取编码后的数据进行处理,处理完成后,编解码器将客户端的所有权收回到输出缓冲区。重复整个过程,直到编码器停止工作或异常退出。

MEDIC的功能是处理输入数据,生成输出数据。首先,生成输入数据缓冲区,并将数据填充到缓冲区中并提供给编解码器。编解码器将异步处理输入数据,然后将填充的输出缓冲区提供给消费者,消费者在消费后将缓冲区返回给编解码器。

二、MediaCodec编码过程

在整个编解码过程中,MediaCodec的使用会经历配置、启动、数据处理、停止、释放等几个过程,对应的状态可以概括为停止、执行、释放三种状态。停止状态可以细分为未初始化、已配置和异常,执行状态也可以细分为读写数据(刷新)、运行和流结束。

media codec整体状态结构图如下:

从上图可以看出,MediaCodec创建时,会进入未初始化状态。设置好配置信息,调用start()启动后,MediaCodec将进入运行状态,可以读写数据。如果在此过程中出现错误,MediaCodec将进入停止状态。我们只需要使用reset方法重置编解码器,否则MediaCodec持有的资源最终会被释放。当然,如果正常使用MediaCodec,我们也可以向编解码器发送EOS指令,调用stop和release方法来终止编解码器的使用。

三。MediaCodec API描述

MediaCodec可以处理特定的视频流。主要有这些方法:

getInputBuffers:获取需要编码数据的输入流队列,返回的是一个ByteBuffer数组queueInputBuffer:输入流入队列dequeueInputBuffer:从输入流队列中取数据进行编码操作getOutputBuffers:获取编解码之后的数据输出流队列,返回的是一个ByteBuffer数组dequeueOutputBuffer:从输出队列中取出编码操作之后的数据releaseOutputBuffer:处理完成,释放ByteBuffer数据

第四,MediaCodec的基本使用

所有同步模式的MediaCodec API都遵循一种模式:

创建并配置MediaCodec对象
循环,直到完成:
如果输入缓冲区准备好了,读取一个输入块并将其复制到输入缓冲区
如果输出缓冲区准备好了,复制输出缓冲区的数据
并释放MediaCodec对象。

(1)创建编码器/解码器

MediaCodec主要提供了createEncoderByType(字符串类型)和createDecoderByType(字符串类型)两种方法来创建编解码器,这两种方法都需要MIME类型的多媒体格式。常见的MIME多媒体格式如下:
●" video/x-vnd . on2 . vp8 " –VP8视频(即视频输入。webm)
●" video/x-vnd . on2 . vp9 " –VP9视频(即视频输入。webm)
●“视频/AVC”–H.264/AVC视频
●“视频/mp4v-es”–MPEG4视频
●“视频/3gpp”–H.263视频
●“音频/3gpp”–AMR窄带音频
●" audio/AMR-WB " –AMR宽带音频
●“音频/mpeg”–MPEG1/2音频层III
●" audio/mp4a-latm " –AAC音频(注意,这是原始的AAC包,没有在LATM包装!)
●" audio/vorbis " –vorbis audio
●" audio/g711-alaw " –g . 711 alaw audio
●" audio/g711-mlaw " –G.711 ulaw audio
当然,MediaCodec还提供了createByCodecName(字符串名称)方法,支持使用特定的组件名称来创建编解码器。不过这种方法用起来有些麻烦,官方建议最好配合MediaCodecList使用,因为MediaCodecList记录了所有可用的编解码器。当然,我们也可以使用这个类来判断传入的minmeType参数,以匹配MediaCodec是否支持mineType编解码器。

以指定为“video/avc”的MIME类型为例,代码如下:

private static MediaCodecInfo selectCodec(String mimeType) { // 获取所有支持编解码器数量 int numCodecs = MediaCodecList.getCodecCount(); for (int i = 0; i < numCodecs; i++) { // 编解码器相关性信息存储在MediaCodecInfo中 MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); // 判断是否为编码器 if (!codecInfo.isEncoder()) { continue; } // 获取编码器支持的MIME类型,并进行匹配 String[] types = codecInfo.getSupportedTypes(); for (int j = 0; j < types.length; j++) { if (types[j].equalsIgnoreCase(mimeType)) { return codecInfo; } } } return null; }

(2)配置并启动编码器/解码器。

编解码器配置使用MediaCodec的configure方法,该方法首先提取以MediaFormat存储的数据映射,然后调用本地方法native-configure来配置编解码器。配置时,configure方法需要传入format、surface、crypto、flags等参数,其中format是MediaFormat的一个实例,以“key-value”键值对的形式存储多媒体数据格式信息;Surface用来表示解码器的数据源来自这个面;Crypto用于指定MediaCrypto对象以安全地解密媒体数据;Flags表示编码器(CONFIGURE_FLAG_ENCODE)已配置。

MediaFormat mFormat = MediaFormat.createVideoFormat("video/avc", 640 ,480); // 创建MediaFormatmFormat.setInteger(MediaFormat.KEY_BIT_RATE,600); // 指定比特率mFormat.setInteger(MediaFormat.KEY_FRAME_RATE,30); // 指定帧率mFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,mColorFormat); // 指定编码器颜色格式 mFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,10); // 指定关键帧时间间隔mVideoEncodec.configure(mFormat,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);

以上代码是H.264编码时的配置方法,CreateVideoFormat ("video/avc ",640,480)是“video/avc”类型(即H.264)编码器的MediaFormat对象,需要指定视频数据的宽度和高度。如果对音频数据进行编码和解码,则调用MediaFormat的createaudioformat (string mime,int sample rate,int channel count)方法。除了一些配置参数,例如视频帧速率、音频采样率等。,这里我们需要重点关注
media format . key _ color _ format配置属性,该属性用于指示视频编码器的颜色格式。所选择的特定颜色格式与输入视频数据源的颜色格式相关。比如我们都知道相机预览捕捉到的图像流通常是NV21或者YV12,所以编码器需要指定相应的颜色格式;否则,编码后的数据可能会出现花屏、重叠、颜色失真等现象。Mediacinfo。代码能力。存储编码器支持的所有颜色格式。常见的颜色格式映射如下:

原始数据 编码器 NV12(YUV420sp) ———> COLOR_FormatYUV420PackedSemiPlanar NV21 ———-> COLOR_FormatYUV420SemiPlanar YV12(I420) ———-> COLOR_FormatYUV420Planar

配置好编解码器后,可以调用MediaCodec的start()方法,该方法会调用低级native_start()方法启动编码器,调用低级ByteBuffer[] getBuffers(input)方法打开一系列输入输出缓冲区。

start()方法的源代码如下:

public final void start() { native_start(); synchronized(mBufferLock) { cacheBuffers(true /* input */); cacheBuffers(false /* input */); } }

(3)数据处理

MediaCodec支持两种模式的编解码器,即同步同步和异步。所谓同步模式是指编解码器数据的输入和输出是同步的,编解码器只有在处理完输出后才会再次接收输入的数据。但是,异步编解码器数据的输入和输出是异步的,编解码器不会等待输出数据被处理后再接收输入数据。这里,我们主要介绍同步编码和解码,因为我们大量使用这种方法。我们知道,当编解码器启动时,每个编解码器都会有一组输入输出缓冲区,但这些缓冲区暂时无法使用。只有通过MediaCodec的
dequeue input buffer/dequeue output buffer方法,才能对输入输出缓冲区进行授权,并且这些缓冲区可以通过返回的ID进行操作。让我们通过官方提供的代码进行扩展分析:

MediaCodec codec = MediaCodec.createByCodecName(name); codec.configure(format, …); MediaFormat outputFormat = codec.getOutputFormat(); // option B codec.start(); for (;;) { int inputBufferId = codec.dequeueInputBuffer(timeoutUs); if (inputBufferId >= 0) { ByteBuffer inputBuffer = codec.getInputBuffer(…); // fill inputBuffer with valid data … codec.queueInputBuffer(inputBufferId, …); } int outputBufferId = codec.dequeueOutputBuffer(…); if (outputBufferId >= 0) { ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId); MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A // bufferFormat is identical to outputFormat // outputBuffer is ready to be processed or rendered. … codec.releaseOutputBuffer(outputBufferId, …); } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { // Subsequent data will conform to new format. // Can ignore if using getOutputFormat(outputBufferId) outputFormat = codec.getOutputFormat(); // option B } } codec.stop(); codec.release();

从上面的代码来看,当编解码器启动时,它会输入一个for(;;)循环,这是一个无限循环,以便从编解码器的输入缓冲池中连续获得包含数据的缓冲区,然后从输出缓冲池中获得编解码器的输出数据。

温馨提示:内容均由网友自行发布提供,仅用于学习交流,如有版权问题,请联系我们。