Android 音視頻學習:使用 MediaExtractor 和 MediaMuxer 解析和封裝 mp4 文件


這篇文章的目的主要是學習 Android 平臺的 MediaExtractor 和 MediaMuxer API,知道如何解析和封裝 mp4 文件。html

一個音視頻文件是包含音頻和視頻,Android 中能夠經過 MediaExtractor  API 把音頻或視頻給單獨抽取出來,經過 MediaMuxer 合成新的視頻。git

MediaExtractor

MediaExtractor 的做用就是將音頻和視頻分離。github

主要是如下幾個步驟:web

一、建立實例緩存

MediaExtractor mediaExtractor = new MediaExtractor();

二、設置數據源微信

mediaExtractor.setDataSource(path);

三、獲取數據源的軌道數,切換到想要的軌道編輯器

// 軌道索引
int videoIndex = -1;
// 視頻軌道格式信息
MediaFormat mediaFormat = null;
// 數據源的軌道數
int trackCount = mediaExtractor.getTrackCount();
for (int i = 0; i < trackCount; i++) {
    MediaFormat format = mediaExtractor.getTrackFormat(i);
    String mimeType = format.getString(MediaFormat.KEY_MIME);
    if (mimeType.startsWith("video/")) {
        videoIndex = i;
        mediaFormat = format;
        break;
    }
}
// 切換到想要的軌道
mediaExtractor.selectTrack(videoIndex);

四、對所需軌道數據循環讀取讀取每幀,進行處理ide

while (true) {
    // 將樣本數據存儲到字節緩存區
    int readSampleSize = mediaExtractor.readSampleData(byteBuffer, 0);
    // 若是沒有可獲取的樣本,退出循環
    if (readSampleSize < 0) {
        mediaExtractor.unselectTrack(videoIndex);
        break;
    }
    ...
    ...
    // 讀取下一幀數據
    mediaExtractor.advance();
}

五、完成後釋放資源學習

mediaExtractor.release();

MediaMuxer

MediaMuxer 的做用是生成音頻或視頻文件;還能夠把音頻與視頻混合成一個音視頻文件。flex

主要是如下幾個步驟:

一、建立實例

MediaMuxermediaMuxer = new MediaMuxer(path, format);

path: 輸出文件的名稱;format: 輸出文件的格式,當前只支持 MP4 格式。

二、將音頻軌或視頻軌添加到 MediaMuxer,返回新的軌道

int trackIndex = mediaMuxer.addTrack(videoFormat);

三、開始合成

mediaMuxer.start();

四、循環將音頻軌或視頻軌的數據寫到文件

 while (true) {
     // 將樣本數據存儲到字節緩存區
     int readSampleSize = mediaExtractor.readSampleData(byteBuffer, 0);
     // 若是沒有可獲取的樣本,退出循環
     if (readSampleSize < 0) {
         mediaExtractor.unselectTrack(videoIndex);
         break;
     }
     bufferInfo.size = readSampleSize;
     bufferInfo.flags = mediaExtractor.getSampleFlags();
     bufferInfo.offset = 0;
     bufferInfo.presentationTimeUs = mediaExtractor.getSampleTime();
     mediaMuxer.writeSampleData(trackIndex, byteBuffer, bufferInfo);
     // 讀取下一幀數據
     mediaExtractor.advance();
 }

五、完成後釋放資源

mediaMuxer.stop();
mediaMuxer.release();

實例

從 MP4 文件中分離出視頻生成無聲視頻文件。

/**
  * 分離視頻的視頻軌,輸入視頻 input.mp4,輸出 output_video.mp4
  */

private void extractVideo() {
    MediaExtractor mediaExtractor = new MediaExtractor();
    MediaMuxer mediaMuxer = null;
    File fileDir = FileUtil.getExternalAssetsDir(this);
    try {
        // 設置視頻源
        mediaExtractor.setDataSource(new File(fileDir, VIDEO_SOURCE).getAbsolutePath());
        // 軌道索引
        int videoIndex = -1;
        // 視頻軌道格式信息
        MediaFormat mediaFormat = null;
        // 數據源的軌道數
        int trackCount = mediaExtractor.getTrackCount();
        for (int i = 0; i < trackCount; i++) {
            MediaFormat format = mediaExtractor.getTrackFormat(i);
            String mimeType = format.getString(MediaFormat.KEY_MIME);
            if (mimeType.startsWith("video/")) {
                videoIndex = i;
                mediaFormat = format;
                break;
            }
        }
        // 切換到想要的軌道
        mediaExtractor.selectTrack(videoIndex);
        File outFile = new File(FileUtil.getMuxerAndExtractorDir(this), OUTPUT_VIDEO);
        if (outFile.exists()) {
            outFile.delete();
        }
        mediaMuxer = new MediaMuxer(outFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        // 將視頻軌添加到 MediaMuxer,返回新的軌道
        int trackIndex = mediaMuxer.addTrack(mediaFormat);
        ByteBuffer byteBuffer = ByteBuffer.allocate(mediaFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE));
        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.flags = mediaExtractor.getSampleFlags();
            bufferInfo.offset = 0;
            bufferInfo.presentationTimeUs = mediaExtractor.getSampleTime();
            mediaMuxer.writeSampleData(trackIndex, byteBuffer, bufferInfo);
            // 讀取下一幀數據
            mediaExtractor.advance();
        }
        Toast.makeText(this"分離視頻完成", Toast.LENGTH_SHORT).show();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (mediaMuxer != null) {
            mediaMuxer.stop();
            mediaMuxer.release();
        }
        mediaExtractor.release();
    }
}

分離音頻、合成音視頻的代碼相似,詳見 GitHub :AndroidMultiMediaLearning[1]

引用

一、Android 視頻分離和合成(MediaMuxer和MediaExtractor)[2]

二、Android 音視頻開發(五):使用 MediaExtractor 和 MediaMuxer API 解析和封裝 mp4 文件[3]

參考資料

[1]

AndroidMultiMediaLearning: https://github.com/zywudev/AndroidMultiMediaLearning

[2]

Android 視頻分離和合成(MediaMuxer和MediaExtractor): https://blog.csdn.net/zhi184816/article/details/52514138

[3]

Android 音視頻開發(五):使用 MediaExtractor 和 MediaMuxer API 解析和封裝 mp4 文件: https://www.cnblogs.com/renhui/p/7474096.html

本文分享自微信公衆號 - 賈小昆(zywudev)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索