「音视频直播技术」Android下H264解码

前言

上一篇文章中我介绍了如何使用MediaCodec编码,今天我们再来分析一下如何通过 MediaCodec 进行解码。

为了讲解的方便,我们引入了 MediaExtractor 类。它用于打开MP4等媒体文件,并从中抽取出音视频数据。

打开媒体文件

MediaExtractor,音视频数据分离器。每种媒体文件如MP4, FLV, MOOV等都是一种容器,里边存放了音频数据和视频数据。MediaExtractor的作用就是根据容器协议打开容器,并读取其中的音频或视频数据。

在容器文件(MP4)中,音频数据与视频数据是以轨道(track)的概念存放的。取的是两条轨道永远不相交的意思,也就指明音频数据与视频数据是分别存储的。

我们使用MediaExtractor类打开媒体文件,它的使用非常简单,步骤如下:

1. 创建一个MediaExtractor对象。
2. 将媒体文件设置给MediaExtractor对象。
3. 选定要处理的轨道。

我们先来看一下示例代码吧

......

MediaExtractor extractor = null;
try {
    extractor = new MediaExtractor();
    extractor.setDataSource(sourceFile.toString());
    int trackIndex = selectTrack(extractor); //根据关键字获取视频Track
    if (trackIndex < 0) {
        throw new RuntimeException("No video track found in " + mSourceFile);
    }
    extractor.selectTrack(trackIndex); //选定视频轨

    ......

} finally {
    if (extractor != null) {
        extractor.release();
    }
}

......

通过上面的步骤我们就选好了要处理的视频轨。下面我们来创建解码器。

创建解码器

在创建解码器之前,需要先通过 MediaExtractor 获取到要处理的视频轨的媒体格式(因为媒体格式中包括了 CSD-0/CSD-1 信息,这个信息对于解码非常重要)。然后通过媒体格式的 mime 信息创建解码器。

CSD-0/CSD-1 指的就是 H264中的 PPS 和 SPS。

另外,在配置解码器时,可以给它传入一个 Surface,这样解码器解码后,就可以直接将图像帧渲染到 Surface里了。代码如下:


......


MediaFormat format = extractor.getTrackFormat(trackIndex);

// Create a MediaCodec decoder, and configure it with the MediaFormat from the
// extractor.  It's very important to use the format from the extractor because
// it contains a copy of the CSD-0/CSD-1 codec-specific data chunks.
String mime = format.getString(MediaFormat.KEY_MIME);
decoder = MediaCodec.createDecoderByType(mime);
decoder.configure(format, mOutputSurface, null, 0);
decoder.start();

......

解码

解码按如下步骤进行:

1. 从InputBuffer队列中取出一个空闲的InputBuffer。
2. 通过 MediaExtractor 对象从视频轨道中取出H264数据存到InputBuffer中。
3. 将InputBuffer放到InputBuffer队列中。此时需要解码的数据已经送入了解码器。
4. 从OutputBuffer队列中取OutputBuffer,如果能取到说明已经有解码好的数据了。
5. 最后调用releaseOutputBuffer释放OutputBuffer。此时OutputBuffer中的数据将被转成纹理进行渲染。

示例代码如下:


......

while (!outputDone){

    ......

    // Feed more data to the decoder.
    if (!inputDone) {
        int inputBufIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC);
        if (inputBufIndex >= 0) {

            ByteBuffer inputBuf = decoderInputBuffers[inputBufIndex];
            int chunkSize = extractor.readSampleData(inputBuf, 0);

            ......

            long presentationTimeUs = extractor.getSampleTime();
            decoder.queueInputBuffer(inputBufIndex, 0, chunkSize,
                    presentationTimeUs, 0 /*flags*/);

            extractor.advance(); //处理下一帧

            ......

        } 
    }

    if (!outputDone) {
        int decoderStatus = decoder.dequeueOutputBuffer(
                        mBufferInfo, TIMEOUT_USEC);

        ......

        if(decoderStatus > 0) {
            // As soon as we call releaseOutputBuffer, the buffer will be forwarded
            // to SurfaceTexture to convert to a texture. 
            decoder.releaseOutputBuffer(decoderStatus, doRender); //解码数据
        }
        ......
    }
}
......

小结

通过上面的介绍我们知道通过MediaCodec进行解码也非常的简单,主要是三大步:
1. 创建视频解码器。
2. 获取数据。今天我们是通过 MediaExtrator从文件中获取的。如果是直播系统,则是直接从网上获取数据。
3. 在循环中不停的向解码器喂数据,并从解码器中取出解码后的数据。

参考

  1. H264编码
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值