Android MediaCodec 硬编码器封装

以下为代码
注意:Android硬编码对手机平台有要求,不同平台支持的视频质量以及是否支持硬编码都不相同。
需要关注inputBufferIndex值 以及outputBufferIndex 是否正常。

import java.nio.ByteBuffer;
import java.util.Arrays;

import android.annotation.SuppressLint;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.util.Log;

public class AVCEncoder {

    private MediaCodec mediaCodec;
    int m_width;
    int m_height;
    byte[] m_info = null;

    private byte[] yuv420 = null;

    int m_SupportColorFormat = -1;

    @SuppressLint("NewApi")
    public AVCEncoder(int width, int height, int framerate, int bitrate, int iFrameInterval) {

        m_width = width;
        m_height = height;
        yuv420 = new byte[width * height * 3 / 2];

        m_SupportColorFormat = getSupportColorFormat();

        if (m_SupportColorFormat != -1) {
            mediaCodec = MediaCodec.createEncoderByType("video/avc");
            MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);
            mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
            mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate);
            mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, m_SupportColorFormat);
            mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); // 关键帧间隔时间//
            mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            mediaCodec.start();

        } else {
            Log.e("AvcEncoder", "mediacodec doesn't work");
            // 硬编码不成功
        }

    }

    @SuppressLint("NewApi")
    private int getSupportColorFormat() {
        int numCodecs = MediaCodecList.getCodecCount();
        MediaCodecInfo codecInfo = null;
        for (int i = 0; i < numCodecs && codecInfo == null; i++) {
            MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
            if (!info.isEncoder()) {
                continue;
            }
            String[] types = info.getSupportedTypes();
            boolean found = false;
            for (int j = 0; j < types.length && !found; j++) {
                if (types[j].equals("video/avc")) {
                    System.out.println("found");
                    found = true;
                }
            }
            if (!found)
                continue;
            codecInfo = info;
        }

        Log.e("AvcEncoder", "Found " + codecInfo.getName() + " supporting " + "video/avc");

        // Find a color profile that the codec supports
        MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType("video/avc");
        Log.e("AvcEncoder",
                "length-" + capabilities.colorFormats.length + "==" + Arrays.toString(capabilities.colorFormats));

        for (int i = 0; i < capabilities.colorFormats.length; i++) {

            switch (capabilities.colorFormats[i]) {
            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
            case MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar:
            case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:

                Log.e("AvcEncoder", "supported color format::" + capabilities.colorFormats[i]);
                return capabilities.colorFormats[i];
            default:
                Log.e("AvcEncoder", "unsupported color format " + capabilities.colorFormats[i]);
                break;
            }
        }

        return -1;
    }

    @SuppressLint("NewApi")
    public void close() {
        try {
            mediaCodec.stop();
            mediaCodec.release();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    class EncodeResult {
        private static final int FRAME_IVALID = -1;
        private static final int FRAME_IFRAME = 1;
        private static final int FRAME_PFRAME = 2;
        private static final int FRAME_ENCODE_LEN_INVALID = -1;

        private int frameFlag;
        private int encodeLen;
        private int frameIndex;

        public EncodeResult() {
            super();
            // TODO Auto-generated constructor stub
        }

        public EncodeResult(int frameFlag, int encodeLen) {
            super();
            this.frameFlag = frameFlag;
            this.encodeLen = encodeLen;
        }

        public int getFrameIndex() {
            return frameIndex;
        }

        public void setFrameIndex(int frameIndex) {
            this.frameIndex = frameIndex;
        }

        public int getFrameFlag() {
            return frameFlag;
        }

        public void setFrameFlag(int frameFlag) {
            this.frameFlag = frameFlag;
        }

        public int getEncodeLen() {
            return encodeLen;
        }

        public void setEncodeLen(int encodeLen) {
            this.encodeLen = encodeLen;
        }

    }

    private int frameIndex = 0;

    @SuppressLint("NewApi")
    public EncodeResult offerEncoder(byte[] input, byte[] output) {

        EncodeResult result = new EncodeResult();
        int pos = 0;

        switch (m_SupportColorFormat) {
        case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
            YV12toYUV420Planar(input, yuv420, m_width, m_height);
            break;
        case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
            // todo
            break;
        case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
            YV12toYUV420PackedSemiPlanar(input, yuv420, m_width, m_height);
            break;
        case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
            // todo
            break;
        case MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar:
            // todo
            break;
        case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
            YV12toYUV420SemiPlanar(input, yuv420, m_width, m_height);
            break;
        default:
            break;
        }

        try {
            ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
            ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
            int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
            //Log.i("lee", "inputBufferIndex:" + inputBufferIndex);
            if (inputBufferIndex >= 0) {
                ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                inputBuffer.clear();
                inputBuffer.put(yuv420);
                mediaCodec.queueInputBuffer(inputBufferIndex, 0, yuv420.length, 0, 0);
            }

            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
            //Log.i("lee", "outputBufferIndex:" + outputBufferIndex);
            while (outputBufferIndex >= 0) {
                ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
                byte[] outData = new byte[bufferInfo.size];
                outputBuffer.get(outData);

                if (m_info != null) {
                    System.arraycopy(outData, 0, output, pos, outData.length);
                    pos += outData.length;
                } else {
                    // 保存pps sps 只有开始时 第一个帧里有, 保存起来后面用
                    ByteBuffer spsPpsBuffer = ByteBuffer.wrap(outData);
                    if (spsPpsBuffer.getInt() == 0x00000001) {
                        m_info = new byte[outData.length];
                        System.arraycopy(outData, 0, m_info, 0, outData.length);
                    } else {
                        return new EncodeResult(EncodeResult.FRAME_IVALID, EncodeResult.FRAME_ENCODE_LEN_INVALID);
                    }
                }

                mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
            }
            // key frame 编码器生成关键帧时只有 00 00 00 01 65
            // 没有pps
            // sps, 要加上
            if (output[4] == 0x65) {
                System.arraycopy(output, 0, yuv420, 0, pos);
                System.arraycopy(m_info, 0, output, 0, m_info.length);
                System.arraycopy(yuv420, 0, output, m_info.length, pos);
                pos += m_info.length;
                frameIndex = 0;
                result.setFrameFlag(EncodeResult.FRAME_IFRAME);

            } else {
                frameIndex++;
                result.setFrameFlag(EncodeResult.FRAME_PFRAME);
            }

        } catch (Throwable t) {
            t.printStackTrace();
        }

        result.setEncodeLen(pos);
        result.setFrameIndex(frameIndex);
        return result;
    }

    public static void YV12toYUV420PackedSemiPlanar(final byte[] input, final byte[] output, final int width,
            final int height) {
        /*
         * COLOR_TI_FormatYUV420PackedSemiPlanar is NV12 We convert by putting
         * the corresponding U and V bytes together (interleaved).
         */
        final int frameSize = width * height;
        final int qFrameSize = frameSize / 4;

        System.arraycopy(input, 0, output, 0, frameSize); // Y

        for (int i = 0; i < qFrameSize; i++) {
            output[frameSize + i * 2] = input[frameSize + i + qFrameSize]; // Cb
                                                                            // (U)
            output[frameSize + i * 2 + 1] = input[frameSize + i]; // Cr (V)
        }
    }

    public static void YV12toYUV420Planar(byte[] input, byte[] output, int width, int height) {
        /*
         * COLOR_FormatYUV420Planar is I420 which is like YV12, but with U and V
         * reversed. So we just have to reverse U and V.
         */
        final int frameSize = width * height;
        final int qFrameSize = frameSize / 4;

        System.arraycopy(input, 0, output, 0, frameSize); // Y
        System.arraycopy(input, frameSize, output, frameSize + qFrameSize, qFrameSize); // Cr
                                                                                        // (V)
        System.arraycopy(input, frameSize + qFrameSize, output, frameSize, qFrameSize); // Cb
                                                                                        // (U)
    }

    public static void YV12toYUV420SemiPlanar(final byte[] input, final byte[] output, final int width,
            final int height) {
        final int frameSize = width * height;
        final int qFrameSize = frameSize / 4;

        int padding = 1024;
        if ((width == 640 && height == 480) || width == 1280 && height == 720)
            padding = 0;

        System.arraycopy(input, 0, output, 0, frameSize);
        for (int i = 0; i < (qFrameSize); i++) {
            output[frameSize + i * 2 + padding] = (input[frameSize + qFrameSize + i]);
            output[frameSize + i * 2 + 1 + padding] = (input[frameSize + i]);
        }
    }

}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
//------------------------------------------------------------------------ // Readme.txt //------------------------------------------------------------------------ This project stationery is designed to get you up and running quickly with CodeWarrior for MC9S12XS128. It is set up for the selected CPU and target connection, but can be easily modified. Sample code for the following language(s) is at your disposal: The wizard has prepared CodeWarrior target(s) with the connection methods of your choice: Additional connections can be chosen in the simulator/debugger, use the menu Component > Set Target. //------------------------------------------------------------------------ // Getting Started //------------------------------------------------------------------------ To build/debug your project, use the menu Project > Debug or press F5. This will open the simulator/debugger. Press again F5 in the debugger (or menu Run > Start/Continue) to start the application. The menu Run > Halt or F6 stops the application. In the debugger menu Component > Open you can load additional components. //------------------------------------------------------------------------ // Project structure //------------------------------------------------------------------------ The project generated contains various files/folders: - readme.txt: this file - Sources: folder with the application source code - Prm: - burner.bbl file to generate S-Records - Libraries: needed library files (ANSI, derivative header/implementation files) - Debugger Project File: contains a .ini file for the debugger for each connection - Debugger Cmd Files: contains sub-folders for each connection with command files //------------------------------------------------------------------------ // Adding your own code //------------------------------------------------------------------------ Once everything is working as expected, you can begin adding your own code to the project. Keep in mind that we provide this as an example of how to get up and running quickly with CodeWarrior. There are certainly other ways to handle interrupts and set up your linker command file. Feel free to modify any of the source files provided. // Simulator/Debugger: Additional components //------------------------------------------------------------------------ In the simulator/debugger, you can load additional components. Try the menu Component > Open. //------------------------------------------------------------------------ // Additional documentation //------------------------------------------------------------------------ Read the online documentation provided. Use in CodeWarrior IDE the menu Help > CodeWarrior Help. //------------------------------------------------------------------------ // Contacting Metrowerks //------------------------------------------------------------------------ For bug reports, technical questions, and suggestions, please use the forms installed in the Release_Notes folder and send them to: [email protected]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值