Android 硬件编码 MediaCodec

MediaCodec 的整体流程如上图所示,从input 输入数据,从output 输出数据,编码的时候输入的是原始数据,输出的是编码后的数据。

初始化

configure 编码器的时候要将flag 设置为encode。

java 复制代码
public void startEncode(MediaFormat format) {
    mFormat = format;
    final String mimeType = format.getString(MediaFormat.KEY_MIME);

    // Check to see if this is actually a video mime type. If it is, then create
    // a codec that can decode this mime type.
    try {
        mCodec = MediaCodec.createEncoderByType(mimeType);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    mCodec.configure(format,null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    mCodec.start();
}

写数据

将原始数据送给 MediaCodec。

java 复制代码
public void sendFrame(byte[] input, long pts, int flags) {
    int size = input.length;
    int inputBufferId = mCodec.dequeueInputBuffer(timeOut);
    if (inputBufferId >= 0) {
        // fill inputBuffers[inputBufferId] with valid data
        ByteBuffer buffer = mCodec.getInputBuffer(inputBufferId);
        buffer.put(input);
        mCodec.queueInputBuffer(inputBufferId, 0, size, pts, flags);
    }
}

取数据

从 MediaCodec 中取出编码后的数据,这里要注意返回的outputBufferId,会有不同的处理。

java 复制代码
public VEAVPacket receivePacket() {
    VEAVPacket packet = new VEAVPacket();
    int outputBufferId = mCodec.dequeueOutputBuffer(mBufferInfo, timeOut);
    if (outputBufferId == MediaCodec.INFO_TRY_AGAIN_LATER) {
        Log.d(TAG, "INFO_TRY_AGAIN_LATER");
        return packet;
    } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
        // Subsequent data will conform to new format.
        packet.format = mCodec.getOutputFormat();
        return packet;
    } else if (outputBufferId < 0) {
        Log.d(TAG, "receivePacket:" + outputBufferId);
        return packet;
    }
    // outputBuffers[outputBufferId] is ready to be processed or rendered.
    ByteBuffer outputBuffer = mCodec.getOutputBuffer(outputBufferId);
    if (outputBuffer == null) {
        return packet;
    }
    byte[] outData = new byte[mBufferInfo.size];
    outputBuffer.get(outData);
    packet.buffer = ByteBuffer.wrap(outData);
    packet.bufferInfo = mBufferInfo;
    mCodec.releaseOutputBuffer(outputBufferId, timeOut);

    return packet;
}

结束

java 复制代码
public void stopEncode() {
  mCodec.stop();
  mCodec.release();
}

注意事项

封装的时候可能提前需要获取 sps 和 pps,然而编码器接收到一定数据才会吐出这些信息,因此,我们可以先送入一些空数据,将这个信息给挤出来。

java 复制代码
public MediaFormat getOutputFormat() {
    if (mOutFormat != null) {
        return mOutFormat;
    }
    byte [] data = new byte[1];

    long time = System.currentTimeMillis();
    sendFrame(data, time, 0);

    VEAVPacket videoPacket = receivePacket();
    if (videoPacket.format != null) {
        mOutFormat = videoPacket.format;
    }
    return mOutFormat;
}
相关推荐
换元不配限12 分钟前
Android 接入 protobuf 实战:从 Gradle 配置到避开 R8 混淆坑
android·grpc·序列化·protobuf·protocolbuffers·二进制序列化·.proto
Marco_Marco1351 小时前
定位SDK接入实战:Android/iOS/鸿蒙三端怎么选、怎么接
android·ios·harmonyos
芝麻别开门12 小时前
gst-shark安卓编译使用方法
android·gstreamer·gst-shark
2501_9159214316 小时前
iOS应用耗电测试:使用Energy Log、Keymob和Instruments进行性能监控与能源消耗分析
android·ios·小程序·https·uni-app·iphone·webview
蒲公英内测分发16 小时前
热成像仪交付客户后,Android 配套 App 不上架,怎么持续下载安装和更新
android·机器人·项目管理·智能家居·智能硬件
脚踏实地,坚持不懈!16 小时前
Android 常见性能问题总结
android
高旭的旭17 小时前
图解 Android 系统架构:从 App、Framework、HAL 到 Linux 驱动
android·linux·系统架构
hunterandroid18 小时前
Navigation Component 多模块导航与深度链接实战
android
快乐非自愿18 小时前
低代码落地费控系统:从报销流程到业财闭环的实操方案
android·低代码·rxjava·ai低代码
摇滚侠18 小时前
《Java并发编程之美》第 2 章 并发编程的其他基础知识 阅读笔记 3
android·java·笔记