Android 音視頻開發 - 用MediaExtractor和MediaMuxer分離合成視頻

序言

一個音視頻文件是由音頻和視頻組成的,Android 提供了 MediaExtractor 和 MediaMuxer 類,用來把音頻或視頻單獨抽取出來,然後合成新的視頻。

我們分別看一下 API 的使用,實現分離 MP4 視頻文件,然後再合成分離的音視頻功能。

MediaExtractor 的使用主要有這麼幾步:

  1. 設置數據源
  2. 獲取通道數,找到想要的軌道和通道格式,並切換過去
  3. 循環讀取每幀的樣本數據
  4. 完成後釋放資源

MediaMuxer 的使用和 MediaExtractor 類似:

  1. 設置目標文件路徑和音視頻格式
  2. 添加感興趣的通道
  3. 開始合成,循環寫入樣本數據
  4. 完成後釋放資源

下面是個提取併合成視頻的示例,MediaExtractor 和 MediaMuxer 一起使用,相當於把原視頻的聲音通道去掉,生成一個無聲的視頻文件。

    // 分離視頻的純視頻 輸入視頻 input.mp4,分離保存的視頻 output_video.mp4
    private void extractVideo() {
        MediaExtractor mediaExtractor = new MediaExtractor();
        MediaMuxer mediaMuxer = null;
        try {
            File fileDir = FileUtils.getFileDir(this);
            // 設置視頻源
            mediaExtractor.setDataSource(new File(fileDir, VIDEO_SOURCE_PATH).getAbsolutePath());
            int videoIndex = -1;
            int maxInputSize = -1;
            int frameRate = -1;
            // 獲取數據源的軌道數
            int trackCount = mediaExtractor.getTrackCount();
            // 循環軌道數,找到我們想要的視頻軌
            for (int i = 0; i < trackCount; i++) {
                MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
                // 主要描述mime類型的媒體格式
                String mimeType = trackFormat.getString(MediaFormat.KEY_MIME);
                // //找到視頻軌
                if (mimeType.startsWith("video/")) {
                    videoIndex = i;
                    // 獲取視頻最大的輸入大小
                    maxInputSize = trackFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
                    // 獲取視頻的幀率
                    frameRate = trackFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
                }
            }
            logger.info("frameRate:{}, maxInputSize:{}", frameRate, maxInputSize);
            //切換視頻的信道
            mediaExtractor.selectTrack(videoIndex);
            MediaFormat trackFormat = mediaExtractor.getTrackFormat(videoIndex);
            mediaMuxer = new MediaMuxer(new File(fileDir, OUTPUT_VIDEO).getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
            //將視頻軌添加到 MediaMuxer,並返回新的軌道
            int trackIndex = mediaMuxer.addTrack(trackFormat);
            ByteBuffer byteBuffer = ByteBuffer.allocate(maxInputSize);
            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            // 開始合成
            mediaMuxer.start();
            while (true) {
                // 檢索當前編碼的樣本並將其存儲在字節緩衝區中
                int readSampleSize = mediaExtractor.readSampleData(byteBuffer, 0);
                //  如果沒有可獲取的樣本則退出循環
                if (readSampleSize < 0) {
                    mediaExtractor.unselectTrack(videoIndex);
                    break;
                }
                // 設置樣本編碼信息
                bufferInfo.size = readSampleSize;
                bufferInfo.offset = 0;
                bufferInfo.flags = mediaExtractor.getSampleFlags();
                bufferInfo.presentationTimeUs += 1000 * 1000 / frameRate;
                //寫入樣本數據
                mediaMuxer.writeSampleData(trackIndex, byteBuffer, bufferInfo);
                //推進到下一個樣本,類似快進
                mediaExtractor.advance();
            }

            logger.info("finish extract video");
        } catch (IOException e) {
            logger.error(e);
        } finally {
            if (mediaMuxer != null) {
                mediaMuxer.stop();
                mediaMuxer.release();
            }
            mediaExtractor.release();
        }
    }

同樣地,分離音頻和合成音視頻的過程和上面差不多,代碼在 GitHub 上,歡迎各位參閱。
【附錄】

需要資料的朋友可以加入Android架構交流QQ羣聊:513088520

點擊鏈接加入羣聊【Android移動架構總羣】:加入羣聊

獲取免費學習視頻,學習大綱另外還有像高級UI、性能優化、架構師課程、NDK、混合式開發(ReactNative+Weex)等Android高階開發資料免費分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章