1. 程式人生 > 其它 >音視訊系列--MediaProjection錄屏生成H264和H265檔案

音視訊系列--MediaProjection錄屏生成H264和H265檔案

技術標籤:音視訊

一、前言


想要分析H264或者H265檔案,就需要生成對應檔案來分析,當然可以通過FFmpeg來操作,操作命令可以參考這裡,這裡打算使用MediaProjection錄頻來生成下H264或H265檔案。

二、MediaProjection


2.1、獲取MediaProjectionManager

使用之前別忘了申請許可權

 public boolean checkPermission() {
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission
( Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }, 1); } return false
; }

獲取MediaProjectionManager

this.mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);

2.2、申請錄頻

Intent captureIntent = mediaProjectionManager.createScreenCaptureIntent();
startActivityForResult(captureIntent, 100);

成功之後會在onActivityResult中回撥

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 100 && resultCode == Activity.RESULT_OK) { mediaProjection = mediaProjectionManager.getMediaProjection (resultCode, data); //同意了回撥這裡 initMediaCodec(); } }

同意之後獲取MediaProjection

2.3、使用MediaCodec獲取編碼h264資料

MediaProjection錄頻好的資料已經是編碼好的,設定好和MediaCodec提供的Surface,就會把資料通過MediaCodec傳給DSP晶片,我們就只需要通過MediaCodecDSP中獲取編碼好的資料。

private void initMediaCodec() {
    try {
        //此處是編碼
        mediaCodec = MediaCodec.createEncoderByType("video/avc");
        MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC,
                540, 960);

        format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
        //幀率
        format.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
        //位元速率
        format.setInteger(MediaFormat.KEY_BIT_RATE, 1200_000);
        //2秒一個I幀
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2);//2s一個I幀
        //不需要傳surface
        mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        //這是MediaCodec 提供的surface
        final Surface surface = mediaCodec.createInputSurface();
        new Thread() {
            @Override
            public void run() {
                //開始編碼
                mediaCodec.start();
                //提供的surface  與MediaProjection關聯
                mediaProjection.createVirtualDisplay("screen-codec",
                        540, 960, 1,
                        DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
                        surface, null, null);
                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                while (isStart) {
//                      錄頻資料直接會傳給dsp編碼,源源不斷的插敘編碼好的資料
                    int index = mediaCodec.dequeueOutputBuffer(bufferInfo, 100000);

                    Log.e(TAG, "run: " + index);
                    if (index >= 0) {
//                            dsp晶片提供的ByteBuffer,不能夠直接使用操作
                        ByteBuffer buffer = mediaCodec.getOutputBuffer(index);

                        byte[] outData = new byte[bufferInfo.size];
                        buffer.get(outData);

                        //以字串的方式寫入
                        writeContent(outData);
                        //寫成 檔案  我們就能夠播放起來
                        writeBytes(outData);
                        mediaCodec.releaseOutputBuffer(index, false);
                    }

                }

                mediaCodec.stop();
                mediaCodec.release();

                mediaProjection.stop();

            }
        }.start();

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

}

此處將獲取到的編碼資料寫到了txt檔案和h264檔案,前者是為了方便後續學習分析,寫到txt檔案需要將位元組進一步轉化為byte字串

public String writeContent(byte[] array) {
    char[] HEX_CHAR_TABLE = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
    };
    StringBuilder sb = new StringBuilder();
    for (byte b : array) {
        sb.append(HEX_CHAR_TABLE[(b & 0xf0) >> 4]);
        sb.append(HEX_CHAR_TABLE[b & 0x0f]);
    }
    Log.i(TAG, "writeContent: "+sb.toString());
    FileWriter writer = null;
    try {
        // 開啟一個寫檔案器,建構函式中的第二個引數true表示以追加形式寫檔案
        writer = new FileWriter(Environment.getExternalStorageDirectory()+"/codec.txt", true);
        writer.write(sb.toString());
        writer.write("\n");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if(writer != null){
                writer.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return sb.toString();
}

此時執行成功就可以在SD卡中生成對應檔案

在這裡插入圖片描述

1.NAL層和VCL層是同步進行的
2.第一幀永遠會將sps和pps當成一幀編碼出來

三、獲取H265檔案


只需要改幾處地方就可以了

1 .設定編碼的type MediaCodec.createEncoderByType("video/hevc");

2 .設定編碼器MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_HEVC, 540, 960)

3 . 設定輸出的檔案格式為.h265檔案即可

程式碼位置