欢迎访问 生活随笔!

凯发k8官方网

当前位置: 凯发k8官方网 > 运维知识 > android >内容正文

android

android硬编码封装mp4,【android 音视频开发打怪升级:音视频硬解码篇】四、音视频解封和封装:生成一个mp4... -凯发k8官方网

发布时间:2024/10/12 android 33 豆豆
凯发k8官方网 收集整理的这篇文章主要介绍了 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渲染篇系列文章,将进一步介绍音视频渲染、重编码、封装等内容,敬请关注。

总结

以上是凯发k8官方网为你收集整理的android硬编码封装mp4,【android 音视频开发打怪升级:音视频硬解码篇】四、音视频解封和封装:生成一个mp4...的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得凯发k8官方网网站内容还不错,欢迎将凯发k8官方网推荐给好友。

  • 上一篇:
  • 下一篇:
网站地图