• 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 所有音频/视频格式通用的键,所有未标记为可选的键都是强制性的:

    2.2 视频格式有以下键:

    2.3 音频格式有以下键: 

     

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

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

    2.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实现

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

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

  • 相关阅读:
    hive hive窗口打印库和表头、复杂类型ARRAY、MAP 和 STRUCT的定义和使用
    面试题之数组的去重方式
    超前进位加法器
    C++11的简单介绍(下)
    SSM+Vue+Element-UI实现智能新冠疫苗接种助手管理系统
    C++内存管理及内存问题的分析
    【树的存储结构,孩子链表】
    论文复现《SplaTAM: Splat, Track & Map 3D Gaussians for Dense RGB-D SLAM》
    spacedesk-笔记本、平板、扩展屏幕-解决平板字体太小的问题
    GEE错误——XXX is not a function,如何解决这个问题?
  • 原文地址:https://blog.csdn.net/m0_60259116/article/details/126559530