Android 直播系統開發的音視頻開發: 音視頻錄製流程總結

在前面咱們學習和使用了AudioRecord、AudioTrack、Camera、 MediaExtractor、MediaMuxer API、MediaCodec。 學習和使用了上述的API以後,相信對Android系統的音視頻處理有必定的經驗和心得了。本文及後面的幾篇文章作的事情就是將這些知識串聯起來,作一些稍微複雜的事情。git

1、流程分析
1.1 需求說明
咱們須要作的事情就是:串聯整個音視頻錄製流程,完成音視頻的採集、編碼、封包成 mp4 輸出。github

1.2 實現方式
Android音視頻採集的方法:預覽用SurfaceView,視頻採集用Camera類,音頻採集用AudioRecord。網絡

1.3 數據處理思路
使用MediaCodec 類進行編碼壓縮,視頻壓縮爲H.264,音頻壓縮爲aac,使用MediaMuxer 將音視頻合成爲MP4。ide

2、 實現過程
2.1 收集Camera數據,並轉碼爲H264存儲到文件
在收集數據以前,對Camera設置一些參數,方便收集後進行數據處理:學習

Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewFormat(ImageFormat.NV21);
parameters.setPreviewSize(1280, 720);
而後設置PreviewCallback:ui

camera.setPreviewCallback(this);
就能夠獲取到Camera的原始NV21數據:this

onPreviewFrame(byte[] bytes, Camera camera)
在建立一個H264Encoder類,在裏面進行編碼操做,並將編碼後的數據存儲到文件:編碼

複製代碼
new Thread(new Runnable() {code

@Override
public void run() {
    isRuning = true;
    byte[] input = null;
    long pts = 0;
    long generateIndex = 0;

    while (isRuning) {
        if (yuv420Queue.size() > 0) {
            input = yuv420Queue.poll();
            byte[] yuv420sp = new byte[width * height * 3 / 2];
            // 必需要轉格式,不然錄製的內容播放出來爲綠屏
            NV21ToNV12(input, yuv420sp, width, height);
            input = yuv420sp;
        }
        if (input != null) {
            try {
                ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
                ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
                int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
                if (inputBufferIndex >= 0) {
                  pts = computePresentationTime(generateIndex);
                  ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                  inputBuffer.clear();
                  inputBuffer.put(input);
                  mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, System.currentTimeMillis(), 0);
                  generateIndex += 1;
                }

                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
                while (outputBufferIndex >= 0) {
                    ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
                    byte[] outData = new byte[bufferInfo.size];
                    outputBuffer.get(outData);
                    if (bufferInfo.flags == MediaCodec.BUFFER_FLAG_CODEC_CONFIG) {
                        configbyte = new byte[bufferInfo.size];
                        configbyte = outData;
                    } else if (bufferInfo.flags == MediaCodec.BUFFER_FLAG_SYNC_FRAME) {
                        byte[] keyframe = new byte[bufferInfo.size + configbyte.length];
                        System.arraycopy(configbyte, 0, keyframe, 0, configbyte.length);
                        System.arraycopy(outData, 0, keyframe, configbyte.length, outData.length);
                        outputStream.write(keyframe, 0, keyframe.length);
                    } else {
                        outputStream.write(outData, 0, outData.length);
                    }

                    mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                    outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
                }

            } catch (Throwable t) {
                t.printStackTrace();
            }
        } else {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // 中止編解碼器並釋放資源
    try {
        mediaCodec.stop();
        mediaCodec.release();
    } catch (Exception e) {
        e.printStackTrace();
    }

    // 關閉數據流
    try {
        outputStream.flush();
        outputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

}).start();
複製代碼
當結束編碼的時候,須要將相關的資源釋放掉:orm

複製代碼
// 中止編解碼器並釋放資源
try {
mediaCodec.stop();
mediaCodec.release();
} catch (Exception e) {
e.printStackTrace();
}

// 關閉數據流
try {
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
複製代碼
此時,咱們作到了將視頻內容採集-->編碼-->存儲文件。但這個僅僅是對Android 音視頻開發(四):使用 Camera API 採集視頻數據的延伸,可是頗有必要。由於在前面學習瞭如何採集音頻,如何使用MediaCodec去處理音視頻,如何使用MediaMuxer去混合音視頻。

示例代碼:https://github.com/renhui/AndroidRecorder/releases/tag/only_h264_video

下面咱們在當前的的基礎上繼續完善,即將音視頻採集並混合爲音視頻。

2.2 音視頻採集+混合,存儲到文件
基本完成思路已經在2.1的結尾處坐了說明,下面貼一下demo的連接:

示例代碼:https://github.com/renhui/AndroidRecorder/releases/tag/h264_video_audio

本文轉載自網絡,感謝原做者的分享,轉載僅爲分享乾貨知識,若有侵權歡迎聯繫做者進行刪除處理。

相關文章
相關標籤/搜索