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;
}
相关推荐
夜雪一千10 小时前
MySQL 全局锁是什么?原理、风险、备份踩坑完整实战
android·mysql·adb
又见情义16 小时前
RK3568 Android 13 驱动适配实战:从零开始让SDK在自己的板子上跑起来
android·arm开发·驱动开发
霸道流氓气质18 小时前
MySQL 大表 DDL 变更 — 原理、方案与实践
android·数据库·mysql
明雨-开发19 小时前
Android打包时候Execution failed for task ‘:launcher:lintVitalAnalyzeRelease‘.
android
游戏开发爱好者819 小时前
开心上架是什么,一站式 Apple 开发者工作台总览
android·小程序·https·uni-app·iphone·webview
Android-Flutter19 小时前
android kotlin launch 源码分析
android·kotlin
qingmiaozhuan19 小时前
电脑录屏黑屏?只有声音没画面?从原理到排查一篇搞定
ffmpeg·电脑·音视频开发·录屏技术·电脑录屏
Android-Flutter19 小时前
android kotlin 状态机 Continuation 详解
android·kotlin
Zha0Zhun21 小时前
Jetpack Compose 实现 iOS 风格 3D WheelPicker
android
深念Y1 天前
RIO-UL00(EMUI 4.1 / Android 6.0.1 / arm64)开机自启动 sshd
android·linux·华为·安卓·chroot·sshd·emui