android - 我在使用 android mediacodec 编码和解码 h264 时得到了错误的颜色

标签 android android-mediacodec

最近在学习如何使用mediacodec,好像有点问题。我写了一个demo,从camera preview中获取数据,编码成h264,然后解码成surfaceview,但是corlor好像不对,就像这样:wrong corlor

我已经尝试发送我通过 rtp 获得的 h264 并通过 ffplay 播放,corlor 也是错误的。我对此感到困惑。有人知道如何解决它吗?因为我是新来的,英语不好,请多指教(中文)。这是我的代码`package com.example.androidcodec;

import java.nio.ByteBuffer;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.util.Log;
import android.view.Surface;

public class Encoder {
    private byte[] sps;
    private byte[] pps;
    private MediaCodec mEncoder;
    private int width, height;
    private byte[] yuv420;
    private byte[] mInfo = null;
    private MediaCodec mDecoder;
    private Surface surface;
    private int framerate;
    private int bitrate;

    public Encoder(int width, int height, int framerate, int bitrate,Surface surface) {
        this.surface = surface;
        this.width = width;
        this.height = height;
        this.framerate = framerate;
        this.bitrate = bitrate;
        yuv420 = new byte[width * height * 3 / 2];
        mEncoder = 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, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
        mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
        mEncoder.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        mEncoder.start();
    }

    public void close() {
        try {
            mEncoder.stop();
            mEncoder.release();
            mEncoder = null;
            mDecoder.stop();
            mDecoder.release();
            mDecoder = null;
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public int offerEncoder(byte[] input, byte[] output) {
        int pos = 0;
        swapYV12toI420(input, yuv420, width, height);
        ByteBuffer[] inputBuffers = mEncoder.getInputBuffers();
        ByteBuffer[] outputBuffers = mEncoder.getOutputBuffers();
        int inputBufferIndex = mEncoder.dequeueInputBuffer(-1);
        if (inputBufferIndex >= 0) {
            ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
            inputBuffer.clear();
            inputBuffer.put(yuv420);
            mEncoder.queueInputBuffer(inputBufferIndex, 0, yuv420.length, 0, 0);
        }
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        int outputBufferIndex = mEncoder.dequeueOutputBuffer(bufferInfo, 0);
        while (outputBufferIndex >= 0) {
            ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
            byte[] outData = new byte[bufferInfo.size];
            outputBuffer.get(outData);
            if (mInfo != null) {
                System.arraycopy(outData, 0, output, pos, outData.length);
                pos += outData.length;
            } else {
                ByteBuffer spsPpsBuffer = ByteBuffer.wrap(outData);
                if (spsPpsBuffer.getInt() == 0x00000001) {
                    mInfo = new byte[outData.length];
                    System.arraycopy(outData, 0, mInfo, 0, outData.length);
                    findSpsAndPps(mInfo);
                    initDecoder();
                }else {
                    return -1;
                }
            }
            mEncoder.releaseOutputBuffer(outputBufferIndex, false);  
            outputBufferIndex = mEncoder.dequeueOutputBuffer(bufferInfo, 0);
        }

        return pos;
    }

    private void initDecoder() {
        mDecoder = MediaCodec.createDecoderByType("video/avc");
        MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);
        mediaFormat.setByteBuffer("csd-0"  , ByteBuffer.wrap(sps));
        mediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(pps));
        mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, height*width);  
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate);
        mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
        mDecoder.configure(mediaFormat, surface, null, 0);
        mDecoder.start();

    }

    public void onFrame(byte[] buf, int offset, int length) {

        ByteBuffer[] inputBuffers = mDecoder.getInputBuffers();
        int inputBufferIndex = mDecoder.dequeueInputBuffer(-1);
        if (inputBufferIndex >= 0) {
            ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
            inputBuffer.clear();
            inputBuffer.put(buf, offset, length);
            int capacity = inputBuffer.capacity();
            Log.e("tag", "capacity < length?"+":"+(capacity < length)+"......................");
            mDecoder.queueInputBuffer(inputBufferIndex, 0, length, System.currentTimeMillis(), 0);
        }

        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        int outputBufferIndex = mDecoder.dequeueOutputBuffer(bufferInfo, 0);
        Log.e("tag", outputBufferIndex+"onFrame.........................................................");
        while (outputBufferIndex >= 0) {
            mDecoder.releaseOutputBuffer(outputBufferIndex, true);
            outputBufferIndex = mDecoder.dequeueOutputBuffer(bufferInfo, 0);
        }
    }


    // yv12 转 yuv420p yvu -> yuv
    private void swapYV12toI420(byte[] yv12bytes, byte[] i420bytes, int width, int height) {
        System.arraycopy(yv12bytes, 0, i420bytes, 0, width * height);
        System.arraycopy(yv12bytes, width * height + width * height / 4, i420bytes, width * height, width * height / 4);
        System.arraycopy(yv12bytes, width * height, i420bytes, width * height + width * height / 4, width * height / 4);
    }

    private void findSpsAndPps(byte[] config) {
        Log.e("tag", printBuffer(config,0,config.length-1));
        int spsEnd = 1;
        for(int i = 4; i < config.length-4; i ++){
            if(config[i]==0X00 && config[i+1]==0X00 &&config[i+2]==0X00 &&config[i+3]==0X01){
                spsEnd = i-1;
                break;
            }
        }
        Log.e("tag", spsEnd-3+"...............................");
        sps = new byte[spsEnd-3];
        pps = new byte[config.length-spsEnd-5];
        System.arraycopy(config,4,sps,0,spsEnd-3);
        System.arraycopy(config,spsEnd+5,pps,0,config.length-spsEnd-5);
    }

    public byte[] getSps() {
        return sps;
    }

    public byte[] getPps() {
        return pps;
    }

    protected static String printBuffer(byte[] buffer, int start,int end) {
        String str = "";
        for (int i=start;i<end;i++) str+=","+Integer.toHexString(buffer[i]&0xFF);
        return str+"\r\n";
    }
}

`

最佳答案

我自己解决了这个问题,我在细节上犯了一个错误。在预览开始之前应该调用parameters.setPreviewFormat而不是parameters.setPictureFormat。

关于android - 我在使用 android mediacodec 编码和解码 h264 时得到了错误的颜色,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40336303/

相关文章:

android - 如何在 MediaCodec 中为 h264 创建编解码器特定数据( csd )?

java - 通过 android 将网络凭据传递给 Web 服务

iphone - 访问 Android 和 iOS 平台的数据/语音流

java - 在 recyclerView 中显示元素列表失败

android - 使用 MediaCodec 解码 h.264 流,dequeueOutputBuffer 总是返回 -1

Android MediaCodec 更改分辨率

android - Adobe AIR 20+ 不能在使用 Nvidia GPU 的 Android 上以 GPU 渲染模式运行?

android - 使用操作菜单打开带有 "Powered By Chrome"的自定义 WebView

c++ - 如何从 MediaCodec 输出缓冲区获取 pcm 数据缓冲区?

Android MediaCodec Format/Resolution Change 中流