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;
}
相关推荐
shandianchengzi1 天前
【科普】安卓|安卓手机上如何简便实现Ctrl+Z(需要键盘或一台Windows电脑)
android·windows·智能手机·计算机外设·安卓·科普·记录
赏金术士1 天前
Compose 教学项目
android·kotlin·compose
晓梦林1 天前
ximai靶场学习笔记
android·笔记·学习
十六年开源服务商1 天前
2026服务器配置优化与WordPress运维实战指南
android·运维·服务器
音视频牛哥2 天前
大牛直播SDK(SmartMediaKit)Android平台Unity3D RTSP/RTMP播放器集成实践
android·unity3d·rtsp播放器·rtmp播放器·unity3d rtmp播放器·安卓unity rtsp播放器·安卓unity rtmp播放器
w1wi2 天前
安卓抓包完全指南(一):从入门到 SSL Pinning 绕过
android·网络协议·ssl
aqi002 天前
一文理清 HarmonyOS 6.0.2 涵盖的十个升级点
android·华为·harmonyos·鸿蒙·harmony
赏金术士2 天前
Jetpack Compose 状态提升(State Hoisting)完全指南
android·kotlin·compose
BoomHe2 天前
git Rebase 为任意一笔提交补上 Change-Id
android·git·android studio