Android音视频开发:MediaCodec解码视频,得到YUV值,一帧一帧加载到SD卡中保存

一、MediaCodec

​ MediaCodec类可用于访问低级媒体编解码器,即编码器/解码器组件。它是Android低级多媒体支持基础设施的一部分(通常与MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface,以及AudioTrack.)。

1.1数据类型

编解码器处理三种数据:压缩数据、原始音频数据和原始视频数据。​ 所有这三种数据都可以使用ByteBuffers,但您应该使用Surface用于原始视频数据以提高编解码器性能。Surface使用原生视频缓冲区,而不将其映射或复制到ByteBuffers因此,它的效率更高。

使用Surface时,通常无法访问原始视频数据,但可以使用ImageReader类来访问不安全的解码(原始)视频帧。这可能仍然比使用字节缓冲区更有效,因为一些本机缓冲区可以映射到直接的字节缓冲区。使用ByteBuffer模式时,可以使用Image类别和getInput/OutputImage(int)访问原始视频帧。

(1)压缩缓冲区

压缩数据可以作为解码器的输入、编码器的输出,需要指定数据的格式,这样codec才知道如何处理这些压缩数据

MediaFormat.KEY_MIME格式类型。

对于视频类型,通常是一个单独的压缩视频帧。

对于音频数据,通常是一个单独的访问单元(一个编码的音频段通常包含由格式类型决定的几毫秒的音频),但是这个要求稍微宽松一些,因为一个buffer可能包含多个编码的音频访问单元。

在这两种情况下,buffer都不会在任意字节边界上开始或结束,而是在帧/访问单元边界上开始或结束,除非它们被BUFFER_FLAG_PARTIAL_FRAME标记。

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

(2)原始音频缓冲区

原始音频数据 — PCM音频数据帧,这是每个通道按通道顺序的一个样本。

(3)原始视频缓冲区

在ByteBuffer模式下,视频buffer根据它们的MediaFormat.KEY_COLOR_FORMAT进行布局。可以从getCodecInfo(). MediaCodecInfo.getCapabilitiesForType.CodecCapability.colorFormats获取支持的颜色格式。视频编解码器可以支持三种颜色格式:

native raw video format: CodecCapabilities.COLOR_FormatSurface,可以与输入/输出的Surface一起使用。

flexible YUV buffers 例如CodecCapabilities.COLOR_FormatYUV420Flexible, 可以使用getInput/OutputImage(int)与输入/输出Surface一起使用,也可以在ByteBuffer模式下使用。

other, specific formats: 通常只支持ByteBuffer模式。有些颜色格式是厂商特有的,其他定义在CodecCapabilities。对于等价于flexible格式的颜色格式,可以使用getInput/OutputImage(int)。

从Build.VERSION_CODES.LOLLIPOP_MR1.开始,所有视频编解码器都支持flexible的YUV 4:2:0 buffer。

1.2 MediaCodec API

(1)MediaCodec创建:

createDecoderByType/createEncoderByType:根据特定MIME类型(如"video/avc")创建codec。然而,这不能用于注入特征,并且可能创建不能处理特定期望媒体格式的编解码器。

createByCodecName:知道组件的确切名称(如OMX.google.mp3.decoder)的时候,根据组件名创建codec。使用MediaCodecList可以获取组件的名称。

(2)MediaCodec配置与启动:

configure:配置解码器或者编码器。

四个参数:

MediaFormat format:输入数据的格式(解码器)或输出数据的所需格式(编码器)。传null等同于传递MediaFormat.MediaFormat作为空的MediaFormat。

Surface surface:指定Surface,用于解码器输出的渲染。如果编解码器不生成原始视频输出(例如,不是视频解码器)和/或想配置解码器输出ByteBuffer,则传null。

MediaCrypto crypto:指定一个crypto对象,用于对媒体数据进行安全解密。对于非安全的编解码器,传null。

int flags:当组件是编码器时,flags指定为常量CONFIGURE_FLAG_ENCODE。

  • start:成功配置组件后调用start。

(3)buffer处理的接口:

MediaCodec可以处理具体的视频流,主要有这几个方法:

getInputBuffers:获取需要编码数据的输入流队列,返回的是一个ByteBuffer数组

getInputBuffer(index) : 获取InputBuffers数组index下标的ByteBuffer

queueInputBuffer:输入流入队列

dequeueInputBuffer:从输入流队列中取数据进行编码操作

getOutputBuffers:获取编解码之后的数据输出流队列,返回的是一个ByteBuffer数组

getOutputBuffer(index) : 获取OutputBuffers数组index下标的ByteBuffer

dequeueOutputBuffer:从输出队列中取出编码操作之后的数据

releaseOutputBuffer:处理完成,释放ByteBuffer数据

(4)处理完之后的操作:

flush:清空的输入和输出端口。

stop:终止decode/encode会话

release:释放编解码器实例使用的资源。

二、MediaFormat 一些属性理解

MediaFormat的格式被指定为键/值对。keys是字符串。values可以是整数、长整型、浮点型、字符串或ByteBuffer。(要素元数据被指定为 字符串/布尔 对。)

2.1 所有音频/视频格式通用的键,所有未标记为可选的键都是强制性的:

Android音视频开发:MediaCodec解码视频,得到YUV值,一帧一帧加载到SD卡中保存_第1张图片

2.2 视频格式有以下键:

Android音视频开发:MediaCodec解码视频,得到YUV值,一帧一帧加载到SD卡中保存_第2张图片

2.3 音频格式有以下键: 

 Android音视频开发:MediaCodec解码视频,得到YUV值,一帧一帧加载到SD卡中保存_第3张图片

2.4 字幕格式有以下关键字:

线格式的类型。KEY_LANGUAGE线内容的语言。KEY_CAPTION_SERVICE_NUMBER(同Internationalorganizations)国际组织可选,隐藏字幕服务或频道号。

Android音视频开发:MediaCodec解码视频,得到YUV值,一帧一帧加载到SD卡中保存_第4张图片

2.5 图像格式有以下键: Android音视频开发:MediaCodec解码视频,得到YUV值,一帧一帧加载到SD卡中保存_第5张图片

三、Image

与媒体源一起使用的单个完整图像缓冲区,例如 MediaCodec或 CameraDevice 。

该类允许通过一个或多个ByteBuffers高效地直接应用程序访问图像的像素数据。 每个缓冲区都封装在描述该平面中像素数据布局的Image.Plane中。 由于这种直接访问方式,与Bitmap类不同,图像不能直接用作UI资源。

由于图像通常由硬件组件直接生成或使用,因此它们是整个系统共享的有限资源,应在不再需要时立即关闭。

例如,当使用ImageReader类从各种媒体源读出图像时,一旦达到the maximum outstanding image count ,不关闭旧的图像对象将阻止新图像的可用性。 发生这种情况时,获取新图像的函数通常会抛出IllegalStateException 。

3.1 getPlanes() 方法

获取该图像的像素平面阵列,平面的数量由图像的格式决定。这个需要了解ImageFormat的格式了,这个以后再了解。

3.2 ImageFormat.getBitsPerPixel()方法

使用此函数可检索ImageFormat的每个像素的位数。

这个在此就点一下算了,之后在单独开一节来具体讲解。

四、demo实现

//解码操作,返回YUV加载的bitmap图片
public class ImageShowActivity extends Activity {
    private TextView tv_yun;
    //图片的个数
    private int imageNum = 0;
    private final String mVideoPath = Environment.getExternalStorageDirectory() + "/Pictures/music.mp4";
    private MediaExtractor extractor;//用于解封装
    private MediaFormat videoFormat;//保存视频轨道的媒体格式
    private MediaCodec mediaCodec;//解码视频轨道资源
    private int rotation;
    private long duration;
 
    //用于代表YUV的种类
    public static final int YUV420P = 0;
    public static final int YUV420SP = 1;
    public static final int NV21 = 2;
 
    //保存YUV数据的byte[]
    private byte[] bytes;
 
    private static int width;
    private static int height;
 
    //1.创建MediaExtractor和MediaCodec :  MediaExtractor负责解封装,MediaCodec负责解码视频轨道资源
    //2.解码获取图片,并进行转换:YUV_420_888-->NV21
    //3.YuvImage 加载nv21,转化成Bitmap用于显示。
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_show);
 
        tv_yun = findViewById(R.id.tv_image_yuv);
 
        GetVideo();
    }
 
}

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

你可能感兴趣的:(音视频开发进阶,音视频,android,视频编解码,webrtc,实时音视频)