android硬编解码MediaCodec

一 mediacodec简介

       MediaCodec 类可以用来访问底层媒体编解码器,即编码器/解码器的组件。 它是 Android 底层多媒体支持架构的一部分(通常与 MediaExtractor,MediaSync,MediaMuxer,MediaCrypto,MediaDrm,Image,Surface 和 AudioTrack 一起使用)。

       编解码器可以处理三类数据:压缩数据、原始音频数据、原始视频数据。

a Compressed Buffers 压缩缓冲区

输入和输出缓冲区包含了对应类型的压缩数据;对于视频类型通常是简单的压缩视频帧;音频数据通常是一个单入单元,(一种编码格式典型的包含了许多 ms 的音频类型),但当一个缓冲区包含了多种编码音频进入单元,可以不需要。另一方面,缓冲区不能在任意字节边界开始或停止,但当标记了 BUFFERFLAGPARTIAL_FRAME 标记时,可以访问帧或进入单元边界。

b Raw Audio Buffers 原始音频缓冲区

原始音频缓冲区包含完整的 PCM 格式的帧数据,一种通道对应一个采样率。每一种采样率是一个 16 位有符号整型在规定参数里面;

c Raw Video Buffers 原始视频缓冲区

在 ByteBuffer 模式,视频缓冲区根据颜色格式;可以通过 getCodecInfo().getCapabilitiesForType(…).colorFormats 获取支持的颜色格式,视频编码支持三种类型的颜色格式:

native raw video format: 标记 COLOR_FormatSurface,可以配合输入输出 surface 使用

flexible YUV buffers:COLOR_FormatYUV420Flexible,可以配合输入输出 surface、在 ByteBuffer 模式,可以通过 getInput/OutputImage(int)访问

other, specific formats:这些格式只在 ByteBuffer 模式支持。一些格式是厂商特有的,其他的定义在 MediaCodecInfo.CodecCapabilities;

自从 5.1.1 之后,所有的编解码器支持 YUV 4:2:0 buffers。

d Accessing Raw Video ByteBuffers on Older Devices 在老的设备上面访问原始视频缓冲区

状态

        编解码器理论上存在三种状态:停止、执行、释放;停止状态也包含三种子状态:未初始化的、已配置的、错误;执行状态也包含三种子状态:已刷新、正在运行、流结束;

0) 工厂方法创建一个编解码器,处于未初始化状态。

1) configure(…)方法进入已配置状态。

2) start()方法进入执行状态。此时,才可以通过上面缓冲队列来处理数据。

3)  start()之后,编解码出于已刷新子状态,此时持有所有的缓冲区;当第一个输入缓冲块被出队时,编解码器会耗费许多时间进入运行状态。当一个输入缓冲块被入队时(被标记流结束标记),编解码器进入流结束状态;此时,编解码器不在接收输入缓冲块,但是可以产生输出缓冲块,直到流结束块被出队。

4) 可以在任意时刻,通过调用 flush(),进入已刷新状态。

5)  stop()让其进入未初始化状态,如果需要使用,需要再配置一次。

6) 当你已经用完编解码器,你需要 release();

某些情况下,编解码器会遭遇错误进入错误状态;可以根据不合法返回值或者异常来判断;调用 reset()可以复位编码器,让其可以重新使用,并进入未初始化状态。调用 releases()进入最终释放状态。

1 creating创建

可以根据指定的 MediaFormat 通过 MediaCodecList 创建一个编解码器;

可以根据 MediaExtractor.getTrackFormat 来创建一个可以用于解码文件和流的编解码器;

在引入其他格式之前,当你想 MediaFormat.setFeatureEnabled,需要通过 MediaCodecList.findDecoderForFormat 获得与名字对应的特殊的媒体格式的编解码器;

最后通过 createByCodecName(String)创建;

也可以通过 MIME 类型使用 createDecoder/EncoderByType(String)来创建。

2 Initialization初始化

      creating之后,可以设置回调 setCallback 来异步处理数据;然后 configure 配置指定的媒体格式。你可以为视频生成指定一个输出 surface;也可以设置安全编码,参考 MediaCrypto;最后编解码器运行在多个模式下,需要特殊指定在编码或解码状态;

如果你想处理原始输入视频缓冲区,可以在配置后通过 createInputSurface()创建一个指定的 Surface。也可以通过 setInputSurface(Surface)设置编解码器使用指定的 Surface。

       AAC audio and MPEG4, H.264 and H.265 video 格式要求预置启动参数或者编解码特殊数据。当处理一些压缩格式时,这些数据必须在任意帧数据之前和 start()之后提交到编解码器。这些数据在调用 queueInputBuffer 时需要被标记 BUFFERFLAGCODEC_CONFIG。

这些数据也可以通过 configure 来配置,可以从 MediaExtractor 获取并放在 MediaFromat 里面。这些数据会在 start()时提交到比爱你解码器里面。

编码器会在任何可用数据之前创建和返回特定标记了 codec-config 标记的编码参数,缓冲区包含了没有时间戳的 codec-specific-data。

3 Data processing执行编解码

        同步模式下,获取一个输入缓冲区之后,填充数据,并通过 queueInputBuffer 提交到 codec,不要提交多个同样时间戳一样的输入数据到 codec。codec 处理完后,会返回一个只读输出缓冲区数据。

        异步模式可以通过 onOutputBufferAvailable 读取,同步模式通过 dequeuOutputBuffer 读取;最后需要调用 releaseOutputBuffer 返回缓冲区到 codec。

二  用法示例

//播放mp4文件
package com.cclin.jubaohe.activity.Media;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Bundle;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import com.cclin.jubaohe.R;
import com.cclin.jubaohe.base.BaseActivity;
import com.cclin.jubaohe.util.CameraUtil;
import com.cclin.jubaohe.util.LogUtil;
import com.cclin.jubaohe.util.SDPathConfig;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* Created by LinChengChun on 2018/4/14.
*/
public class MediaTestActivity extends BaseActivity implements SurfaceHolder.Callback, View.OnClickListener {
    private final static String MEDIA_FILE_PATH = SDPathConfig.LIVE_MOVIE_PATH+"/18-04-12-10:47:06-0.mp4";
    private Surface mSurface;
    private SurfaceView mSvRenderFromCamera;
    private SurfaceView mSvRenderFromFile;
    Button mBtnCameraPreview;
    Button mBtnPlayMediaFile;
    private MediaStream mMediaStream;
    private Thread mVideoDecoderThread;
    private AudioTrack mAudioTrack;
    private Thread mAudioDecoderThread;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBtnCameraPreview = retrieveView(R.id.btn_camera_preview);
        mBtnPlayMediaFile = retrieveView(R.id.btn_play_media_file);
        mBtnCameraPreview.setOnClickListener(this);
        mBtnPlayMediaFile.setOnClickListener(this);
        mSvRenderFromCamera = retrieveView(R.id.sv_render);
        mSvRenderFromCamera.setOnClickListener(this);
        mSvRenderFromCamera.getHolder().addCallback(this);
        mSvRenderFromFile = retrieveView(R.id.sv_display);
        mSvRenderFromFile.setOnClickListener(this);
        mSvRenderFromFile.getHolder().addCallback(this);
        init();
    }
    @Override
    protected int initLayout() {
        return R.layout.activity_media_test;
    }
    private void init(){
        File file = new File(MEDIA_FILE_PATH);
        if (!file.exists()){
            LogUtil.e("文件不存在!!");
            return;
        }
        LogUtil.e("目标文件存在!!");
    }
    private void startVideoDecoder(){
        // fill inputBuffer with valid data
        mVideoDecoderThread = new Thread("mVideoDecoderThread"){
            @Override
            public void run() {
                super.run();
                MediaFormat mMfVideo = null, mMfAudio = null;
                String value = null;
                String strVideoMime = null;
                String strAudioMime = null;
                try {
                    MediaExtractor mediaExtractor = new MediaExtractor(); // 提取器用来从文件中读取音视频
                    mediaExtractor.setDataSource(MEDIA_FILE_PATH);
                    int numTracks = mediaExtractor.getTrackCount(); // 轨道数,一般为2
                    LogUtil.e("获取track数"+numTracks);
                    for (int i=0; i< numTrac
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

步基

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值