android硬编码封装mp4,【android 音视频开发打怪升级:音视频硬解码篇】四、音视频解封和封装:生成一个mp4... -凯发k8官方网
【声 明】
首先,这一系列文章均基于自己的理解和实践,可能有不对的地方,欢迎大家指正。
其次,这是一个入门系列,涉及的知识也仅限于够用,深入的知识网上也有许许多多的博文供大家学习了。
最后,写文章过程中,会借鉴参考其他人分享的文章,会在文章最后列出,感谢这些作者的分享。
码字不易,转载请注明出处!
目录
一、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渲染篇系列文章,将进一步介绍音视频渲染、重编码、封装等内容,敬请关注。
总结
以上是凯发k8官方网为你收集整理的android硬编码封装mp4,【android 音视频开发打怪升级:音视频硬解码篇】四、音视频解封和封装:生成一个mp4...的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇:
- 下一篇: