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;
}
相关推荐
aningxiaoxixi17 分钟前
Android 之 audiotrack
android
枷锁—sha19 分钟前
【DVWA系列】——CSRF——Medium详细教程
android·服务器·前端·web安全·网络安全·csrf
Cao_Shixin攻城狮4 小时前
Flutter运行Android项目时显示java版本不兼容(Unsupported class file major version 65)的处理
android·java·flutter
呼啦啦呼啦啦啦啦啦啦7 小时前
利用pdfjs实现的pdf预览简单demo(包含翻页功能)
android·javascript·pdf
idjl8 小时前
Mysql测试题
android·adb
游戏开发爱好者811 小时前
iOS App 电池消耗管理与优化 提升用户体验的完整指南
android·ios·小程序·https·uni-app·iphone·webview
人生游戏牛马NPC1号11 小时前
学习 Flutter (四):玩安卓项目实战 - 中
android·学习·flutter
星辰也为你祝福h13 小时前
Android原生Dialog
android
梁同学与Android14 小时前
Android ---【CPU优化】需要优化的原因及优化的地方
android
Misha韩14 小时前
React Native 基础tabBar和自定义tabBar - bottom-tabs
android·react native