当前位置:   article > 正文

Android 音视频编解码(二) -- MediaCodec 解码(同步和异步)

android 音视频编解码

在上一个的文章Android 音视频编解码(一) – MediaCodec 初探 中,已经对 MediaCodec 有个感性的认知,这一章,来学习 MediaCodec 的解码功能。

本章效果如下:

在这里插入图片描述

1、同步解码

为了更好的理解 MediaCodec 的工作原理和工作步骤,首先先使用同步解码的方式去解码本地视频。

1.1 拿到视频参数

首先,我们先要准备一个视频,比如 MP4 格式的,它其实已经被编码过的,我们可以通过 MediaCodec 来获取该视频的 MediaFormat 信息,如果你对 MediaExtractor 不熟悉,可以通过 Android 音视频开发(五) – 使用 MediaExtractor 分离音视频,并使用 MediaMuxer合成新视频(音视频同步) 来学习;

定义一个 MyExtractor ,里面实现 MediaExtractor 用来专门解析视频,拿到视频数据:

  1. public MyExtractor(String path) {
  2.       try {
  3.           mediaExtractor = new MediaExtractor();
  4.           // 设置数据源
  5.           mediaExtractor.setDataSource(path);
  6.       } catch (IOException e) {
  7.           e.printStackTrace();
  8.       }
  9.       //拿到所有的轨道
  10.       int count = mediaExtractor.getTrackCount();
  11.       for (int i = 0; i < count; i++) {
  12.           //根据下标拿到 MediaFormat
  13.           MediaFormat format = mediaExtractor.getTrackFormat(i);
  14.           //拿到 mime 类型
  15.           String mime = format.getString(MediaFormat.KEY_MIME);
  16.           //拿到视频轨
  17.           if (mime.startsWith("video")) {
  18.               videoTrackId = i;
  19.               videoFormat = format;
  20.           } else if (mime.startsWith("audio")) {
  21.               //拿到音频轨
  22.               audioTrackId = i;
  23.               audioFormat = format;
  24.           }
  25.       }
  26.   }
  27.   public void selectTrack(int trackId){
  28.       mediaExtractor.selectTrack(trackId);
  29.   }
  30.   /**
  31.     * 读取一帧的数据
  32.     *
  33.     * @param buffer
  34.     * @return
  35.     */
  36.   public int readBuffer(ByteBuffer buffer) {
  37.       //先清空数据
  38.       buffer.clear();
  39.       //选择要解析的轨道
  40.     // mediaExtractor.selectTrack(video ? videoTrackId : audioTrackId);
  41.       //读取当前帧的数据
  42.       int buffercount = mediaExtractor.readSampleData(buffer, 0);
  43.       if (buffercount < 0) {
  44.           return -1;
  45.       }
  46.       //记录当前时间戳
  47.       curSampleTime = mediaExtractor.getSampleTime();
  48.       //记录当前帧的标志位
  49.       curSampleFlags = mediaExtractor.getSampleFlags();
  50.       //进入下一帧
  51.       mediaExtractor.advance();
  52.       return buffercount;
  53.   }

首先,先通过 selectTrack 来指定是解析视频还是音频,接着使用 readBuffer 这个方法,该方法使用 mediaExtractor.readSampleData(buffer, 0); 去拿到当前视频的 buffer,并通过 mediaExtractor.advance() 拿到下一帧的数据。

1.2 解码流程

上一章说到 MediaCodec 的解码是基于以下两张图的:

MediaCodec 工作图 MediaCodec工作原理图

MediaCodec状态图

MediaCodec状态图

如果你对这两张图的流程不熟悉,请阅读 Android 音视频编解码(一) – MediaCodec 初探

为了方便后续的音频解码,这里我们定义一个基类,用来解析视频和音频,由于是同步解析视频,我们应该在线程中去解析,所以继承 Runnable:

  1.   /**
  2.     * 解码基类,用于解码音视频
  3.     */
  4.   abstract class BaseDecode implements Runnable {
  5.       final static int VIDEO = 1;
  6.       final static int AUDIO = 2;
  7.       //等待时间
  8.       final static int TIME_US = 1000;
  9.       MediaFormat mediaFormat;
  10.       MediaCodec mediaCodec;
  11.       MyExtractor extractor;
  12.       private boolean isDone;
  13.       public BaseDecode() {
  14.           try {
  15.               //获取 MediaExtractor
  16.               extractor = new MyExtractor(Constants.VIDEO_PATH);
  17.               //判断是音频还是视频
  18.               int type = decodeType();
  19.               //拿到音频或视频的 MediaFormat
  20.               mediaFormat = (type == VIDEO ? extractor.getVideoFormat() : extractor.getAudioFormat());
  21.               String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
  22.               //选择要解析的轨道
  23.               extractor.selectTrack(type == VIDEO ? extractor.getVideoTrackId() : extractor.getAudioTr
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号