Android硬編、硬解h 264

6305 ワード

プロジェクトエンジニアリングdemoアドレスhttps://github.com/liluojun/PlayVideo
demoは、h 264のハードコーディング、libyuvトリミング画像、openglesレンダリングyuvデータ、ffmpeg復号裸h 264データなどの機能を含むため、参考テストのみに供する.
ハードコーディングはまずエンコーダを設定します
MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);//    
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);//  
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, m_framerate);//  
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2);//I   
try {
    mediaCodec = MediaCodec.createEncoderByType("video/avc");
} catch (Exception e) {
    e.printStackTrace();
}
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mediaCodec.start();//     

これは基本的にテンプレートコードで、エンコーダでよく使われるパラメータはここにあります.
もしもし、データコード、データソース携帯電話カメラ
public void encoder(byte[] input) {
    if (isRuning) {
        try {
            if (input != null && input.length != 0) {
                if (colorFormat <= 20) {
                    JavaToNativeMethod.getInstence().nv21ToI420(input, yuv420sp, m_width, m_height, yByte, uByte, vByte);
                } else {
                    JavaToNativeMethod.getInstence().nv21ToNv12(input, yuv420sp, m_width, m_height, yByte, uByte, vByte);
                }
            }
            if (!encode) {
                return;
            }
            if (input != null && input.length != 0) {
                try {
                    ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
                    ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
                    int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
                    Log.e("index", "" + inputBufferIndex);
                    if (inputBufferIndex >= 0) {
                        pts = computePresentationTime(generateIndex);
                        ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                        inputBuffer.clear();
                        inputBuffer.put(input);
                        mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, pts, 0);
                        generateIndex += 1;
                    } else {
                        return;
                    }
                    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                    int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
                    while (outputBufferIndex >= 0) {
                        UiVideoData u = new UiVideoData();
                        ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
                        byte[] outData = new byte[bufferInfo.size];
                        outputBuffer.get(outData);
                    }
                    bufferInfo = null;
                } catch (Exception e) {
                    Log.e("Encoder", "    " + e.getMessage());
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

変換データフォーマットAndroid携帯電話のデフォルトnv 21フォーマットを使用していますが、携帯電話のyuvデータフォーマットの格納に制限されているため、変換フォーマットが必要です.フォーマット変換はlibyuvライブラリで、コードは以下の通りです.
if (colorFormat <= 20) {
    JavaToNativeMethod.getInstence().nv21ToI420(input, yuv420sp, m_width, m_height, yByte, uByte, vByte);
} else {
    JavaToNativeMethod.getInstence().nv21ToNv12(input, yuv420sp, m_width, m_height, yByte, uByte, vByte);
}

符号化は基本的にテンプレートコードがoutputBufferである.get(outData);ここでoutdataは符号化されたデータです.
spsとppsの取得について:
if (spsPpsBuffer.getInt() == 0x00000001) {
    byte[] mMediaHead = new byte[outData.length];
    System.arraycopy(outData, 0, mMediaHead, 0, outData.length);
    configbyte = outData;
    mMediaHead = null;
    outData = null;
    break;
}

エンコーダの起動後に出た最初のデータはspsとpps情報である.
Iフレームについての判断:
(outData[4] & 0x1f) == 5

ここで私が採用したのは上記の判定方式で、ネット上にはoutData[4]=65||outData[4]=25などの他の判定方式もあります.
強制Iフレームは、エンコーダのflush()メソッドを呼び出すだけで1フレームIフレームを強制する必要がある場合に遭遇することがあります.
ハードデコードもまずデコーダを設定します
mediaformat = MediaFormat.createVideoFormat("video/avc", w, h);
mediaformat.setByteBuffer("csd-0", ByteBuffer.wrap(sps));//  sps
mediaformat.setByteBuffer("csd-1", ByteBuffer.wrap(pps));//  pps
mediaformat.setInteger(MediaFormat.KEY_FRAME_RATE, m_framerate);//  
mediaformat.setInteger(MediaFormat.KEY_BIT_RATE, 1024 * 1000);//  
mediaformat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2);//I   
mCodec.configure(mediaformat, null, null, 0);//           surface,         ,            null
mCodec.start();//     

コーデックの設定差は多くなく、spsとppsのパラメータ設定が多くなっただけです.
フィードデータデコード
public void onFrame(UiVideoData uiVideoData) {
    byte[] buf = uiVideoData._data;
    ByteBuffer[] inputBuffers = mCodec.getInputBuffers();//MediaCodec  ByteBuffer[]       
    ByteBuffer[] outputBuffers = mCodec.getOutputBuffers(); //       
    int inputBufferIndex = mCodec.dequeueInputBuffer(100);//          
    if (inputBufferIndex >= 0) {
        long pts = computePresentationTime(generateIndex);
        ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
        inputBuffer.clear();
        inputBuffer.put(buf);//      ,    
        mCodec.queueInputBuffer(inputBufferIndex, 0, buf.length, pts, 0);//    ,            ,           ,       ,      
        generateIndex += 1;
    } else {
        return;
    }
    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();//         byte[]       
    int outputBufferIndex = mCodec.dequeueOutputBuffer(bufferInfo, 1000);//-1      ,0     
    Log.e(TAG, "outputBufferIndex=" + outputBufferIndex);
    while (outputBufferIndex >= 0) {//    0          
        ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
        byte[] outData = new byte[bufferInfo.size];
        outputBuffer.get(outData);// Buffer            
        mCodec.releaseOutputBuffer(outputBufferIndex, true);
        outputBufferIndex = -1;
    }
}
           outputBuffer.get(outData)                    。

注意:Androidハードコーディングは、すべてのデバイスが正常に動作することを保証するものではありません.