音視頻:使用 MediaExtractor 和 MediaMuxer 解析和封裝 MP4

今天來學習一下如何解析和封裝 MP4。此次咱們使用的 API 是 MediaExtractor 和 MediaMuxer。java

一個用來解析,一個用來封裝。git

API 簡介

MediaExtractor 是什麼?

顧名思義,MediaExtractor 能夠從數據源中提取通過編碼的媒體數據。MediaExtractor 不只能夠解析本地媒體文件,還能夠解析網絡媒體資源。github

MediaMuxer 是什麼?

一樣,名字已經說明了一切。MediaMuxer 能夠將多個流混合封裝起來,支持 MP四、Webm 和 3GP 文件做爲輸出,並且從 Android N 開始,已經支持在 MP4 中混合 B 幀了。微信

此次的任務是什麼?

此次的任務是從一個 MP4 文件中只提取視頻數據,並封裝爲一個新的 MP4 文件。外在表現就是將一個有聲視頻,轉換爲一個無聲視頻markdown

如何實現?

實現部分主要靠 MediaExtractor 提取軌道信息,而後用 MediaMuxer 將數據封裝。步驟以下:網絡

  1. MediaExtractor 設置數據源,就是咱們將要解析的文件的路徑地址
  2. 使用 MediaExtractor 獲取到咱們的目標軌道。好比此次咱們就須要的是視頻軌道。

這裏給出一個軌道的樣例信息:ide

{track-id=1, level=16, mime=video/avc, frame-count=374, profile=65536, language=und, color-standard=4, display-width=320, csd-1=java.nio.HeapByteBuffer[pos=0 lim=8 cap=8], color-transfer=3, durationUs=12612000, display-height=240, width=320, color-range=2, max-input-size=4676, frame-rate=30, height=240, csd-0=java.nio.HeapByteBuffer[pos=0 lim=29 cap=29]}複製代碼

  1. 拿到軌道後,咱們一幀一幀的讀取數據,同時將數據一幀一幀地寫到 MediaMuxer 中進行封裝。

代碼部分

val mediaExtractor = MediaExtractor()
mediaExtractor.setDataSource(oriVideoPath)
var videoOnlyTackIndex = -1
var frameRate = 0
val mediaMuxer =
    MediaMuxer(outputVideoOnlyPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
for (trackIndex in 0 until mediaExtractor.trackCount) {
    val trackFormat = mediaExtractor.getTrackFormat(trackIndex)
    val mime = trackFormat.getString(MediaFormat.KEY_MIME)
    if (mime == null || !mime.startsWith("video/")) {
        // 若是不是視頻軌道,則跳過
        continue
    }
    frameRate = trackFormat.getInteger(MediaFormat.KEY_FRAME_RATE)
    mediaExtractor.selectTrack(trackIndex)
    videoOnlyTackIndex = mediaMuxer.addTrack(trackFormat)
    mediaMuxer.start()
}
if (videoOnlyTackIndex == -1) {
    return
}
val bufferInfo = MediaCodec.BufferInfo()
bufferInfo.presentationTimeUs = 0
var sampleSize = 0
val buffer = ByteBuffer.allocate(500 * 1024)
val sampleTime = mediaExtractor.cachedDuration
while (mediaExtractor.readSampleData(buffer, 0).also {
        sampleSize = it
    } > 0) {
    bufferInfo.size = sampleSize
    bufferInfo.flags = MediaCodec.BUFFER_FLAG_KEY_FRAME
    bufferInfo.presentationTimeUs += 1000 * 1000 / frameRate
    mediaMuxer.writeSampleData(videoOnlyTackIndex, buffer, bufferInfo)
    mediaExtractor.advance()
}
mediaExtractor.release()
mediaMuxer.stop()
mediaMuxer.release()複製代碼

代碼大概如上所示,可是還有幾個問題沒有搞清楚。學習

遺留問題

  1. 咱們讀取每一幀數據的時候,須要一個 buffer,那麼這個 buffer 是怎麼來的呢?
  2. 實際解析過程當中,咱們能夠看到:每一幀的大小實際上是不同的,那這又是爲何呢?

在這裏簡單解釋一下第一個問題(可能不太準確):編碼

這個 buffer 的大小實際上是能夠根據每一幀的最大大小來設定。而每一幀的最大大小,取決於視頻的分辨率編碼格式,因此就有了下邊這個公式:spa

每一幀最大大小 = H x W x 編碼格式係數

第二個問題後面再聊~

最後

上代碼!

https://github.com/T-Oner/MediaPractice

最新更新請關注微信公衆號

相關文章
相關標籤/搜索