日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

android硬编码封装mp4,【Android 音视频开发打怪升级:音视频硬解码篇】四、音视频解封和封装:生成一个MP4...

發布時間:2024/10/12 Android 161 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android硬编码封装mp4,【Android 音视频开发打怪升级:音视频硬解码篇】四、音视频解封和封装:生成一个MP4... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【聲 明】

首先,這一系列文章均基于自己的理解和實踐,可能有不對的地方,歡迎大家指正。

其次,這是一個入門系列,涉及的知識也僅限于夠用,深入的知識網上也有許許多多的博文供大家學習了。

最后,寫文章過程中,會借鑒參考其他人分享的文章,會在文章最后列出,感謝這些作者的分享。

碼字不易,轉載請注明出處!

目錄

一、Android音視頻硬解碼篇:

二、使用OpenGL渲染視頻畫面篇

三、Android FFmpeg音視頻解碼篇

1,FFmpeg so庫編譯

2,Android 引入FFmpeg

3,Android FFmpeg視頻解碼播放

4,Android FFmpeg+OpenSL ES音頻解碼播放

5,Android FFmpeg+OpenGL ES播放視頻

6,Android FFmpeg簡單合成MP4:視屏解封與重新封裝

7,Android FFmpeg視頻編碼

本文你可以了解到

本文主要講解音視頻的解封和封裝過程,但不涉及音視頻的編解碼,涉及到音視頻編解碼的完整流程,將在下一篇章講解完OpenGL后。主要是對音視頻的重新封裝有個基本了解。

一、音視頻解封

在本篇章的第二篇文章【音視頻硬解碼流程】,已經講過,Android使用的是MediaExtractor對音視頻數據流進行解封。這里,我們簡單再過一遍。

第一步,初始化MediaExtractor

init {

mExtractor = MediaExtractor()

mExtractor?.setDataSource(path)

}

第二步,獲取音頻或視頻的格式

/**

* 獲取視頻格式參數

*/

fun getVideoFormat(): MediaFormat? {

for (i in 0 until mExtractor!!.trackCount) {

val mediaFormat = mExtractor!!.getTrackFormat(i)

val mime = mediaFormat.getString(MediaFormat.KEY_MIME)

if (mime.startsWith("video/")) {

mVideoTrack = i

break

}

}

return if (mVideoTrack >= 0)

mExtractor!!.getTrackFormat(mVideoTrack)

else null

}

/**

* 獲取音頻格式參數

*/

fun getAudioFormat(): MediaFormat? {

for (i in 0 until mExtractor!!.trackCount) {

val mediaFormat = mExtractor!!.getTrackFormat(i)

val mime = mediaFormat.getString(MediaFormat.KEY_MIME)

if (mime.startsWith("audio/")) {

mAudioTrack = i

break

}

}

return if (mAudioTrack >= 0) {

mExtractor!!.getTrackFormat(mAudioTrack)

} else null

}

第三步,讀取(分離)音視頻數據

/**

* 讀取音視頻數據

*/

fun readBuffer(byteBuffer: ByteBuffer): Int {

byteBuffer.clear()

selectSourceTrack()

var readSampleCount = mExtractor!!.readSampleData(byteBuffer, 0)

if (readSampleCount < 0) {

return -1

}

//記錄當前幀的時間戳

mCurSampleTime = mExtractor!!.sampleTime

//進入下一幀

mExtractor!!.advance()

return readSampleCount

}

/**

* 選擇通道

*/

private fun selectSourceTrack() {

if (mVideoTrack >= 0) {

mExtractor!!.selectTrack(mVideoTrack)

} else if (mAudioTrack >= 0) {

mExtractor!!.selectTrack(mAudioTrack)

}

}

二、音視頻封裝

Android原生提供了一個封裝器MediaMuxer,用于將已經編碼好的音視頻流數據封裝到指定格式的文件中,MediaMuxer支持MP4、Webm、3GP三種封裝格式。一般使用MP4格式。

使用也比較簡單,同樣分為幾個步驟:

第一步,初始化

class MMuxer {

private val TAG = "MMuxer"

private var mPath: String

private var mMediaMuxer: MediaMuxer? = null

private var mVideoTrackIndex = -1

private var mAudioTrackIndex = -1

private var mIsAudioTrackAdd = false

private var mIsVideoTrackAdd = false

private var mIsStart = false

init {

val fileName = "LVideo_" + SimpleDateFormat("yyyyMM_dd-HHmmss").format(Date()) + ".mp4"

val filePath = Environment.getExternalStorageDirectory().absolutePath.toString() + "/"

mPath = filePath + fileName

mMediaMuxer = MediaMuxer(mPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)

}

//......

}

這里指定了視頻并保存路徑和保存的格式。

第二步,添加音視頻軌道,設置音視頻數據流格式,并啟動封裝器

class MMuxer {

//......

fun addVideoTrack(mediaFormat: MediaFormat) {

if (mMediaMuxer != null) {

mVideoTrackIndex = try {

mMediaMuxer!!.addTrack(mediaFormat)

} catch (e: Exception) {

e.printStackTrace()

return

}

mIsVideoTrackAdd = true

startMuxer()

}

}

fun addAudioTrack(mediaFormat: MediaFormat) {

if (mMediaMuxer != null) {

mAudioTrackIndex = try {

mMediaMuxer!!.addTrack(mediaFormat)

} catch (e: Exception) {

e.printStackTrace()

return

}

mIsAudioTrackAdd = true

startMuxer()

}

}

/**

*忽略音頻軌道

*/

fun setNoAudio() {

if (mIsAudioTrackAdd) return

mIsAudioTrackAdd = true

startMuxer()

}

/**

*忽略視頻軌道

*/

fun setNoVideo() {

if (mIsVideoTrackAdd) return

mIsVideoTrackAdd = true

startMuxer()

}

private fun startMuxer() {

if (mIsAudioTrackAdd && mIsVideoTrackAdd) {

mMediaMuxer?.start()

mIsStart = true

Log.i(TAG, "啟動混合器,等待數據輸入...")

}

}

//......

}

在開啟封裝器前,首先需要設置音視頻對應的數據格式,這個格式來源于音視頻解封獲取的那個MediaFormat,即

MMExtractor#getVideoFormat()

MMExtractor#getAudioFormat()

通過mMediaMuxer!!.addTrack(mediaFormat)后,會返回音視頻數據對應的軌道索引,用于封裝數據時,將數據寫到正確的數據軌道中。

最后,判斷音視頻軌道是否都已經配置完畢,啟動封裝器。

第三步,寫入數據,也很簡單,將解封得到的數據寫入即可。

class MMuexer {

//......

fun writeVideoData(byteBuffer: ByteBuffer, bufferInfo: MediaCodec.BufferInfo) {

if (mIsStart) {

mMediaMuxer?.writeSampleData(mVideoTrackIndex, byteBuffer, bufferInfo)

}

}

fun writeAudioData(byteBuffer: ByteBuffer, bufferInfo: MediaCodec.BufferInfo) {

if (mIsStart) {

mMediaMuxer?.writeSampleData(mAudioTrackIndex, byteBuffer, bufferInfo)

}

}

//......

}

第四步,釋放封裝器,完成封裝過程

==這一步非常重要,必須要釋放之后,才能生成可用的完整的MP4文件==

class MMuxer {

//......

fun release() {

mIsAudioTrackAdd = false

mIsVideoTrackAdd = false

try {

mMediaMuxer?.stop()

mMediaMuxer?.release()

mMediaMuxer = null

Log.i(TAG, "混合器退出...")

} catch (e: Exception) {

e.printStackTrace()

}

}

//......

}

三、整合解封和封裝流程

通過上面兩個步驟,就已經完成了最基本的工具封裝,接下來只需要將它們整合起來就可以了。

新建一個重打包類MP4Repack

class MP4Repack(path: String) {

private val TAG = "MP4Repack"

//初始化音視頻分離器

private val mAExtractor: AudioExtractor = AudioExtractor(path)

private val mVExtractor: VideoExtractor = VideoExtractor(path)

//初始化封裝器

private val mMuxer: MMuxer = MMuxer()

/**

*啟動重封裝

*/

fun start() {

val audioFormat = mAExtractor.getFormat()

val videoFormat = mVExtractor.getFormat()

//判斷是否有音頻數據,沒有音頻數據則告訴封裝器,忽略音頻軌道

if (audioFormat != null) {

mMuxer.addAudioTrack(audioFormat)

} else {

mMuxer.setNoAudio()

}

//判斷是否有視頻數據,沒有音頻數據則告訴封裝器,忽略視頻軌道

if (videoFormat != null) {

mMuxer.addVideoTrack(videoFormat)

} else {

mMuxer.setNoVideo()

}

//啟動線程

Thread {

val buffer = ByteBuffer.allocate(500 * 1024)

val bufferInfo = MediaCodec.BufferInfo()

//音頻數據分離和寫入

if (audioFormat != null) {

var size = mAExtractor.readBuffer(buffer)

while (size > 0) {

bufferInfo.set(0, size, mAExtractor.getCurrentTimestamp(),

mAExtractor.getSampleFlag())

mMuxer.writeAudioData(buffer, bufferInfo)

size = mAExtractor.readBuffer(buffer)

}

}

//視頻數據分離和寫入

if (videoFormat != null) {

var size = mVExtractor.readBuffer(buffer)

while (size > 0) {

bufferInfo.set(0, size, mVExtractor.getCurrentTimestamp(),

mVExtractor.getSampleFlag())

mMuxer.writeVideoData(buffer, bufferInfo)

size = mVExtractor.readBuffer(buffer)

}

}

mAExtractor.stop()

mVExtractor.stop()

mMuxer.release()

Log.i(TAG, "MP4 重打包完成")

}.start()

}

}

首先,定義了音頻和視頻分離器,以及封裝器;

接著,判斷要重封裝的視頻是否包含有音視頻數據,沒有則忽略相應的軌道;

最后,啟動線程,開始解封和封裝,分為兩部分:

音頻數據分離和寫入

視頻數據分離和寫入

其中有一個要注意的就是BufferInfo的參數

val bufferInfo = MediaCodec.BufferInfo()

bufferInfo.set(0, size, mVExtractor.getCurrentTimestamp(),

mVExtractor.getSampleFlag())

第一個為offset,一般為0

第二個為數據大小,就是Extractor提取的當前幀的數據大小

第三個為當前幀對應的時間戳,這個時間戳非常重要,影響到視頻能不能正常播放,通過Extractor獲取

第四個為當前幀類型,如視頻I/P/B幀,也可通過Extractor獲取

四、調用MediaRepack重封裝工具實現重封裝

調用就非常簡單了,如下:

private fun repack() {

val path = Environment.getExternalStorageDirectory().absolutePath + "/mvtest_2.mp4"

val repack = MP4Repack(path)

repack.start()

}

到這里,本篇章【音視頻硬解碼篇】系列文章就結束了,本系列共四篇文章,從【音視頻基礎知識介紹】->【Android音解碼流程】->【音視頻播放與同步】->【視頻解封與封裝】,比較全面的介紹了Android應用系統提供的硬解碼能力,實現音視頻的解碼。

接下來,將進入OpenGL渲染篇系列文章,將進一步介紹音視頻渲染、重編碼、封裝等內容,敬請關注。

總結

以上是生活随笔為你收集整理的android硬编码封装mp4,【Android 音视频开发打怪升级:音视频硬解码篇】四、音视频解封和封装:生成一个MP4...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。