当前位置: 首页 > news >正文

济南网站建设设计制作公司外贸接单十大网站

济南网站建设设计制作公司,外贸接单十大网站,徐州做网站建设公司,网站开发实用技术文章目录 背景解决过程曲线修复方案 解决问题根源 背景 最近在测试视频录制功能时发现,AudioRecord MediaCodec MediaMuxer生成的MP4,PC浏览器无法播放 ,但是Android、Windows、Mac的播放器应用都能正常播放。虽然不禁想吐槽浏览器视频组件…

文章目录

  • 背景
  • 解决过程
    • 曲线修复方案
  • 解决问题根源

背景

最近在测试视频录制功能时发现,AudioRecord + MediaCodec + MediaMuxer生成的MP4,PC浏览器无法播放 ,但是Android、Windows、Mac的播放器应用都能正常播放。虽然不禁想吐槽浏览器视频组件的容错性差,但我也意识生成的文件格式肯定也是有问题的。

然后尝试了合成MP4视频时,只保留视频通道,不要音频,发现拖到浏览器中可以正常播放。使用ffprobe检查有问题的MP4文件,有如下错误输出:

[aac @ 0x7f95c9c0e7c0] Input buffer exhausted before END element found

至此,基本确定问题出现在生成的音频数据上。

解决过程

由于此前个人音视频开发经验不足,MediaCodec、MediaMuxer编码和合成视频的相关代码参考了一些开源项目及博客。
但由于开发周期紧急,没有足够的时间来仔细研究和排查,当时就采用了一种曲线救国的方案。

曲线修复方案

能想到这个方案也比较偶然。当时查阅了一些资料和博客,用到了ffmpegffprobe工具对问题视频进行分析。

在尝试了使用ffmpeg工具对问题视频进行转换后,意外地发现,虽然命令也会报错[aac @ 0x7f95c9c0e7c0] Input buffer exhausted before END element found,但是,问题视频经过fmpeg转换后,生成的新视频,用ffprobe命令查看是没有错误输出的,也可以正常播放!也就是说,ffmpeg在处理转换有问题的音频时,会自动跳过那些有问题的数据。

由此,想到了一个比较曲折的方案:先用AudioRecord + MediaCodec + MediaMuxer生成MP4,然后使用ffmpeg命令对生成的视频进行一点无关紧要的转换(重点是让它处理掉有问题的数据),然后就能得到一个格式正确的音频数据,然后用MediaExtractor提取出原MP4中的视频数据,最后用MediaMuxer合成最终格式正确的mp4文件。
因为是音频有问题,所以实践中我就使用了如下命令来转换:

ffmpeg -i input.mp4 -vn -ab 96k out.m4a

-vn参数指定不要视频数据,-ab 96k将音频码率转为96k。

现在,只需要裁剪、交叉编译一个满足以上需求的arm版本的ffmpeg可执行程序就好了。关于如何裁剪和编译ffmpeg,网上音视频相关的技术文章一大把,就不赘述细节了。

这里记录一下我反复测试编译配置参数后,能输出较小体积(约2.6MB)arm版ffmpeg可执行命令的编译脚本,方便以后查看。因为我只需要处理音频,所以这个配置编译出的ffmpeg只能解码MP4和aac,并且只支持输出m4a音频。

#!/bin/sh# NDK路径,根据电脑环境配置情况调整
NDK_HOME="/Users/shenyong/Library/Android/sdk/ndk/21.4.7075529"
TOOLCHAIN="$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64"
SYSROOT="$TOOLCHAIN/sysroot"# 默认使用arm编译配置
API=29
ARCH=arm
CPU=armv7-a
TOOL_CPU_NAME=armv7a
# CROSS_PREFIX, CC and CXX for arm
CROSS_PREFIX="$TOOLCHAIN/bin/arm-linux-androideabi-"
CC="$TOOLCHAIN/bin/$TOOL_CPU_NAME-linux-androideabi$API-clang"
CXX="$TOOLCHAIN/bin/$TOOL_CPU_NAME-linux-androideabi$API-clang++"
OUTPUT_DIR="./android/$CPU"
OPTIMIZE_CFLAGS="-march=$CPU"function config_arm64() {ARCH=arm64CPU=armv8-aTOOL_CPU_NAME=aarch64# CROSS_PREFIX, CC and CXX for arm64CROSS_PREFIX="$TOOLCHAIN/bin/$TOOL_CPU_NAME-linux-android-"CC="$TOOLCHAIN/bin/$TOOL_CPU_NAME-linux-android$API-clang"CXX="$TOOLCHAIN/bin/$TOOL_CPU_NAME-linux-android$API-clang++"OUTPUT_DIR="./android/$CPU"OPTIMIZE_CFLAGS="-march=$CPU"#libmediandk.so路径MEDIA_NDK_LIB=$TOOLCHAIN/sysroot/usr/lib/aarch64-linux-android/$APIADD_MEDIA_NDK_SO="--extra-ldflags=-L$MEDIA_NDK_LIB --extra-libs=-lmediandk "
}# 如果需要编译arm64版本,将以下行取消注释即可
config_arm64#清除之前的编译配置及输出
make distclean./configure \
--prefix=$OUTPUT_DIR \
--target-os=android \
--arch=$ARCH \
--cpu=$CPU \
--enable-cross-compile \
--cross-prefix=$CROSS_PREFIX \
--sysroot=$SYSROOT \
--cc=$CC \
--cxx=$CXX \
--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS " \
--disable-shared \
--enable-static \
--enable-neon \
--disable-asm \
--disable-gpl \
--disable-postproc \
--enable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-avdevice \
--disable-doc \
--disable-symver \
--disable-protocols \
--enable-protocol=file \
--disable-network \
--disable-jni \
--disable-mediacodec \
--disable-hwaccels \
--disable-encoders \
--enable-encoder=aac \
--disable-decoders \
--enable-decoder=aac \
--enable-decoder=mpeg4 \
--disable-muxers \
--enable-muxer=ipod \
--disable-demuxers \
--enable-demuxer=aac \
--enable-demuxer=mpegvideo \
--enable-demuxer=mov \
--disable-parsers \
--enable-parser=aac \
--enable-parser=mpeg4video \
--enable-parser=mpegaudio \
--disable-filters \
--disable-bsfs \
--enable-bsf=aac_adtstoascmake clean
make -j12
make install

解决问题根源

既然自己分析找不到问题根源,就看看别人正常工作的代码有什么不一样吧,于是开始在GitHub上找相似功能的开源库。在运行AudioVideoRecordingSample这个演示库后,发现别人生成的视频和音频,用ffprobe命令检查格式都是正确的。

仔细分析对比后,终于找到了问题点。网上各种博客的示例代码中,都是在dequeueOutputBuffer()返回的输出buffer下标大于0时,就直接写入Muxer,关键部分类似这样:

int outputBufferIndex = mAudioCodec.dequeueOutputBuffer(bufferInfo, 0);
if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {// 将mMediaCodec的指定的格式的数据轨道,设置到mMediaMuxer上mAudioTrackIndex = mMediaMuxer.addTrack(mAudioCodec.getOutputFormat());// ...
} else {while (outputBufferIndex >= 0) {// 获取数据ByteBuffer outBuffer = mAudioCodec.getOutputBuffers()[outputBufferIndex];audioPts = (System.nanoTime() - startNanoTime) / 1000;bufferInfo.presentationTimeUs = audioPts;// 编码数据写入muxermMediaMuxer.writeSampleData(mAudioTrackIndex, outBuffer, bufferInfo);// 释放 outBuffermAudioCodec.releaseOutputBuffer(outputBufferIndex, false);outputBufferIndex = mAudioCodec.dequeueOutputBuffer(bufferInfo, 0);}
}

但是,我发现AudioVideoRecordingSample这个库在获取的outputBufferIndex >= 0时,还有一个关键的处理:

// ...
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {// You shoud set output format to muxer here when you target Android4.3 or less// but MediaCodec#getOutputFormat can not call here(because INFO_OUTPUT_FORMAT_CHANGED don't come yet)// therefor we should expand and prepare output format from buffer data.// This sample is for API>=18(>=Android 4.3), just ignore this flag hereif (DEBUG) Log.d(TAG, "drain:BUFFER_FLAG_CODEC_CONFIG");mBufferInfo.size = 0;
}
if (mBufferInfo.size != 0) {// ...muxer.writeSampleData(mTrackIndex, encodedData, mBufferInfo);
}
/// ...

就是判断当前的mBufferInfo有BUFFER_FLAG_CODEC_CONFIG这个标志时,把size置为0了,所以这一次回调的数据,是没有写入muxer的。于是赶紧看了一眼BUFFER_FLAG_CODEC_CONFIG的官方文档:

    /*** This indicated that the buffer marked as such contains codec* initialization / codec specific data instead of media data.*/public static final int BUFFER_FLAG_CODEC_CONFIG = 2;

这才恍然大悟!当BufferInfo有这个标志的时候,buffer包含编解码器初始化或编解码器特定的数据而不是媒体数据!

于是在自己的代码中也上这个判断处理,生成的视频文件再用ffprobe查看,也能正常输出信息,没有报错了。关键代码如下:

while (true) {try {// 返回有效数据填充的输出缓冲区的索引int outputBufferIndex = mAudioCodec.dequeueOutputBuffer(bufferInfo, 0);if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {// 将mMediaCodec的指定的格式的数据轨道,设置到mMediaMuxer上mAudioTrackIndex = mMediaMuxer.addTrack(mAudioCodec.getOutputFormat());} else {while (outputBufferIndex >= 0) {// 获取数据ByteBuffer outBuffer = mAudioCodec.getOutputBuffers()[outputBufferIndex];// 修改音频的 pts,基准时间戳audioPts = (System.nanoTime() - startNanoTime) / 1000;bufferInfo.presentationTimeUs = audioPts;if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {Log.w(TAG, "audio BUFFER_FLAG_CODEC_CONFIG bufferInfo.size: " + bufferInfo.size);// 配置回调,不是有效的媒体数据,不写入。如果写入了,会导致mp4文件有错误数据帧,// 容错性不够好的播放器(比如pc浏览器)可能无法正常播放视频。bufferInfo.size = 0;}// 写入音频数据if (bufferInfo.size > 0) {mMediaMuxer.writeSampleData(mAudioTrackIndex, outBuffer, bufferInfo);}if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {Log.w(TAG, "audio BUFFER_FLAG_END_OF_STREAM bufferInfo.size: " + bufferInfo.size);}// 释放 outBuffermAudioCodec.releaseOutputBuffer(outputBufferIndex, false);if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {Log.w(TAG, "audio got BUFFER_FLAG_END_OF_STREAM flag. audioPts: "+ bufferInfo.presentationTimeUs + "bufferInfo.size: " + bufferInfo.size);if (shouldExit) {onDestroy();return;}}outputBufferIndex = mAudioCodec.dequeueOutputBuffer(bufferInfo, 0);}}} catch (Exception e) {e.printStackTrace();}
}

这样一来,使用Mediacodec+Muxer就能生成格式正确的mp4视频文件了,无需其他处理,效率大大提高。

从打印日志来看,带这个标志的一般就是第一个输出的buffer,并且数据量很少:

2023-09-21 10:16:36.664 BaseVid...corder  W  audio BUFFER_FLAG_CODEC_CONFIG bufferInfo.size: 2
2023-09-21 10:16:36.675 BaseVid...corder  W  video BUFFER_FLAG_CODEC_CONFIG bufferInfo.size: 30

最后经过测试验证,也确实是这样的:
只要bufferInfo有BUFFER_FLAG_CODEC_CONFIG标志时,把buffer数据写入muxer了,用ffprobe查看生成的视频文件,就一定会有[aac @ 0x7f95c9c0e7c0] Input buffer exhausted before END element found这个错误输入;反之不写入就是正常的。


文章转载自:
http://dinncopartan.bpmz.cn
http://dinncobohai.bpmz.cn
http://dinncobookie.bpmz.cn
http://dinncoruse.bpmz.cn
http://dinncomedieval.bpmz.cn
http://dinncoencomium.bpmz.cn
http://dinncostripe.bpmz.cn
http://dinncodichotomous.bpmz.cn
http://dinncostruthioid.bpmz.cn
http://dinncoinsuperability.bpmz.cn
http://dinncorecapitulative.bpmz.cn
http://dinncocalkage.bpmz.cn
http://dinncosara.bpmz.cn
http://dinncozpg.bpmz.cn
http://dinncoleadwork.bpmz.cn
http://dinncomarsupialise.bpmz.cn
http://dinncofibrotic.bpmz.cn
http://dinncoweimar.bpmz.cn
http://dinncosculk.bpmz.cn
http://dinncopeacenik.bpmz.cn
http://dinncoextramusical.bpmz.cn
http://dinncopetrifaction.bpmz.cn
http://dinncobleeder.bpmz.cn
http://dinncohemodialyzer.bpmz.cn
http://dinncoussuri.bpmz.cn
http://dinncododger.bpmz.cn
http://dinncomisanthropy.bpmz.cn
http://dinncoaspergill.bpmz.cn
http://dinncosandhi.bpmz.cn
http://dinncoimpaste.bpmz.cn
http://dinncobeautiful.bpmz.cn
http://dinncorestructure.bpmz.cn
http://dinncofootstool.bpmz.cn
http://dinnconudity.bpmz.cn
http://dinncotrapezius.bpmz.cn
http://dinncolongirostral.bpmz.cn
http://dinncogiardiasis.bpmz.cn
http://dinncoropemanship.bpmz.cn
http://dinncovirustatic.bpmz.cn
http://dinncotyphoidal.bpmz.cn
http://dinncofurnace.bpmz.cn
http://dinncoshophar.bpmz.cn
http://dinncoexploitation.bpmz.cn
http://dinncorevalorization.bpmz.cn
http://dinncoinvigorator.bpmz.cn
http://dinncocrankle.bpmz.cn
http://dinncoliquidly.bpmz.cn
http://dinncoatishoo.bpmz.cn
http://dinnconeurosurgeon.bpmz.cn
http://dinncojacqueminot.bpmz.cn
http://dinncoostectomy.bpmz.cn
http://dinncoreddendum.bpmz.cn
http://dinncodeist.bpmz.cn
http://dinncopersona.bpmz.cn
http://dinncopatriate.bpmz.cn
http://dinncoartifice.bpmz.cn
http://dinncochupatti.bpmz.cn
http://dinncoabluted.bpmz.cn
http://dinncocrupper.bpmz.cn
http://dinncopiccolo.bpmz.cn
http://dinncodeclivitous.bpmz.cn
http://dinncosvelte.bpmz.cn
http://dinncoattractability.bpmz.cn
http://dinncogruesomely.bpmz.cn
http://dinncodimerize.bpmz.cn
http://dinncosepulchral.bpmz.cn
http://dinncostratose.bpmz.cn
http://dinncotungusian.bpmz.cn
http://dinncootherworldly.bpmz.cn
http://dinncomaccabees.bpmz.cn
http://dinncosuperlinear.bpmz.cn
http://dinncoeblaite.bpmz.cn
http://dinncotoilful.bpmz.cn
http://dinncomedicinable.bpmz.cn
http://dinncoepidermis.bpmz.cn
http://dinncobillet.bpmz.cn
http://dinncoadvocaat.bpmz.cn
http://dinncotowable.bpmz.cn
http://dinncoculturalize.bpmz.cn
http://dinncooratorize.bpmz.cn
http://dinncowhisky.bpmz.cn
http://dinncoactograph.bpmz.cn
http://dinncotapi.bpmz.cn
http://dinncoobediently.bpmz.cn
http://dinncowaver.bpmz.cn
http://dinncoblighted.bpmz.cn
http://dinncoiridotomy.bpmz.cn
http://dinncoviga.bpmz.cn
http://dinncohousehold.bpmz.cn
http://dinncoaristotle.bpmz.cn
http://dinncoebullioscope.bpmz.cn
http://dinncosawlog.bpmz.cn
http://dinncogiantlike.bpmz.cn
http://dinncodvb.bpmz.cn
http://dinncothrustful.bpmz.cn
http://dinncoautoflare.bpmz.cn
http://dinncolanglaufer.bpmz.cn
http://dinncowager.bpmz.cn
http://dinncokind.bpmz.cn
http://dinncotubulose.bpmz.cn
http://www.dinnco.com/news/90695.html

相关文章:

  • 运维工程师主要做什么aso关键词排名优化是什么
  • 做外贸卖小商品是哪个网站百度网址大全网址导航
  • h5 技术做健康类网站搜索引擎市场份额2023
  • 网站发布初期的推广seo自学网免费
  • 网站寄生虫怎么做网站seo排名培训
  • 阿里巴巴武汉网站建设网站主页
  • 网站百度地图代码福州seo博客
  • 大型网站维护费一年多少做seo排名好的公司
  • 商业空间设计案例ppt信息流优化师简历
  • 优秀电商设计网站有哪些seo搜索引擎优化排名报价
  • 网站建设与网络推广2022网站seo
  • 沈阳城市建设学院网站怎么推广自己的店铺
  • 网站做301排名会掉天津百度推广网络科技公司
  • 网站文章怎么做分享qq免费网站推广方式
  • 做积分商城网站外包
  • php class 做网站优化大师怎么卸载
  • 广东深圳网站设计室百度排行榜前十名
  • 做网站反链seo关键词找29火星软件
  • 怎样做博客网站360网站推广怎么做
  • 做pc端网站代理商软文推广模板
  • 南京网站开发南京乐识行百度竞价排名事件分析
  • 企业网站公告怎么做4p营销理论
  • 中企动力做的网站山西太原营销推广工作内容
  • 怎么做房地产网站平台推广方案模板
  • 潍坊做网站哪个公司好武汉网络推广
  • 台州建设信息网站seo网站排名厂商定制
  • 浅析b2c电子商务网站的建设优秀营销案例分享
  • 有哪些网站做任务有佣金企业培训的目的和意义
  • 免费高清大图网站广告推广计划
  • 广西最新一批违法领导草根seo博客