建筑新型组合塑料模板/湖南seo推广系统
文章目录
- 背景
- 解决过程
- 曲线修复方案
- 解决问题根源
背景
最近在测试视频录制功能时发现,AudioRecord + MediaCodec + MediaMuxer生成的MP4,PC浏览器无法播放 ,但是Android、Windows、Mac的播放器应用都能正常播放。虽然不禁想吐槽浏览器视频组件的容错性差,但我也意识生成的文件格式肯定也是有问题的。
然后尝试了合成MP4视频时,只保留视频通道,不要音频,发现拖到浏览器中可以正常播放。使用ffprobe
检查有问题的MP4文件,有如下错误输出:
[aac @ 0x7f95c9c0e7c0] Input buffer exhausted before END element found
至此,基本确定问题出现在生成的音频数据上。
解决过程
由于此前个人音视频开发经验不足,MediaCodec、MediaMuxer编码和合成视频的相关代码参考了一些开源项目及博客。
但由于开发周期紧急,没有足够的时间来仔细研究和排查,当时就采用了一种曲线救国的方案。
曲线修复方案
能想到这个方案也比较偶然。当时查阅了一些资料和博客,用到了ffmpeg和ffprobe工具对问题视频进行分析。
在尝试了使用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
这个错误输入;反之不写入就是正常的。
相关文章:

【音视频笔记】Mediacodec+Muxer生成mp4,浏览器无法播放问题处理
文章目录 背景解决过程曲线修复方案 解决问题根源 背景 最近在测试视频录制功能时发现,AudioRecord MediaCodec MediaMuxer生成的MP4,PC浏览器无法播放 ,但是Android、Windows、Mac的播放器应用都能正常播放。虽然不禁想吐槽浏览器视频组件…...

debug过程中,矩阵左乘右乘相关概念梳理
1. 变换点或者变换向量 1.1左乘 矩阵左乘通常是指对”目标点“进行左乘,即: A ′ R ∗ A AR*A A′R∗A 其中,A为原始3维点,表示一个3*1的列向量,R为33的旋转矩阵,A‘为变换后的点 B ′ T ∗ B BT*B B′T∗B 其中…...

Ubuntu 安装Kafka
在本指南中,我们将逐步演示如何在 Ubuntu 22.04 上安装 Apache Kafka。 在大数据中,数以百万计的数据源生成了大量的数据记录流,这些数据源包括社交媒体平台、企业系统、移动应用程序和物联网设备等。如此庞大的数据带来的主要挑战有两个方面…...

洗地机性价比高的是哪款?高性价比洗地机排名
洗地机已成为当下备受欢迎的智能家电之一,但在挑选合适的洗地机时,面对各种新词汇和功能选择,可能会让人感到困惑。因此,为了帮助大家在购买洗地机时不踩坑,我们基于市面上主流品牌的综合分析对比,总结出来…...

安装konga
创建konga数据库 docker run --rm pantsel/konga:latest -c prepare -a postgres -u postgresql://kong:kong{IP}:5432/konga这里要注意docker部署时IP不能直接访问localhost 安装konga docker run -p 1337:1337 \--network kong-net \--name konga \-e "NODE_ENVprodu…...

算法基础之高精度总结
目录 高精度算法分类 高精度加减乘除的异同点 加和乘 相同点 减和除 相同点 不同点 处理前导0的方式 高精度算法分类 分类:加、减、乘、除 其中加减乘都适用于两个数都是高精度,除法因为除数是高精度的话不好用整除的方法,所以除法时…...

oracle TNS Listener 远程投毒漏洞修复
有个客户在等保测评过程,测评公司扫出一个关于oracle的漏洞如下: 客户是RAC环境11.2.0.4,在生产修复漏洞前我做了如下测试验证: 测试环境准备: RAC一套11.2.0.4 实例名dbserver [oraclehisdb1 ~]$ cat /etc/hosts …...

第二章:最新版零基础学习 PYTHON 教程(第一节 - Python 输入/输出–在 Python 中获取输入)
开发人员经常需要与用户交互,以获取数据或提供某种结果。如今,大多数程序都使用对话框来要求用户提供某种类型的输入。而Python为我们提供了两个内置函数来读取键盘输入。 目录 输入(提示) raw_input(提示) 输入函数在 Python 中的工作原理:...

react create-react-app v5 从零搭建项目
前言: 好久没用 create-react-app做项目了,这次为了个h5项目,就几个页面,决定自己搭建一个(ps:mmp 好久没用,搭建的时候遇到一堆问题)。 我之前都是使用 umi 。后台管理系统的项目 使用 antd-…...

2023软件测试八股文,涵盖所有面试题
Part1 1、你的测试职业发展是什么? 测试经验越多,测试能力越高。所以我的职业发展是需要时间积累的,一步步向着高级测试工程师奔去。而且我也有初步的职业规划,前3年积累测试经验,按如何做好测试工程师的要点去要求自…...

性能压力测试的定义及步骤是什么
在今天的数字化时代,软件系统的性能和稳定性对于企业的成功至关重要。为了确保软件在高负载和压力情况下的正常运行,性能压力测试成为了不可或缺的环节。本文将介绍性能压力测试的定义、步骤。 一、性能压力测试的定义和目标 性能压力测试是通过模拟实际…...

Selenium自动化中处理鼠标悬停并操作的方法
因为测试中遇到要鼠标悬停显示Tooltip,并操作tip上的内容,现记录如下。 方法一:通过鼠标链式操作 from selenium.webdriver.common.action_chains import ActionChains as ACac AC(self.driver)lst self.driver.find_element_by_xpath(//…...

python socket 编程实现猜数字
项目地址 https://gitee.com/lmk73444/learn_spring/tree/master/doc/1_x%E5%AD%A6%E4%B9%A0/002_py_socket python socket试验 mkdir /root/git_proj cd /root/git_proj# 首次 clone 项目 git clone https://gitee.com/lmk73444/learn_spring.git# 非首次 # 更新项目 cd /ro…...

20个提升效率的JS简写技巧,告别屎山!
JavaScript 中有很多简写技巧,可以缩短代码长度、减少冗余,并且提高代码的可读性和可维护性。本文将介绍 20 个提升效率的 JS 简写技巧,助你告别屎山,轻松编写优雅的代码! 移除数组假值 可以使用 filter() 结合 Bool…...

Pikachu靶场——SSRF 服务端请求伪造
文章目录 1 SSRF 服务端请求伪造1.1 SSRF(curl)1.1.1 漏洞防御 1.2 SSRF(file_get_content)1.2.1 漏洞防御1.2.3 SSRF 防御 1 SSRF 服务端请求伪造 SSRF(Server-Side Request Forgery:服务器端请求伪造) 其形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能&a…...

Android file
写文件——FileOutputStream openFileOutput 读文件——FileInputStream openFileInput openFileOutput写文件时当文件不存在,Android自动创建。 通过BufferedWriter直接写入字符串 public void writeFile(String inputText) {FileOutputStream outputStream nul…...

【计算机网络】计网常见面试题总结
目录 一、谈一谈对OSI七层模型和TCP/IP四层模型的理解? 二、谈一谈TCP协议的三次握手过程? 三、TCP协议为什么要三次握手?两次、四次不行吗? 四、谈一谈TCP协议的四次挥手过程? 五、什么是流量控制? …...

SpringMVC 学习(七)JSON
9. JSON 9.1 简介 JSON(JavaScript Object Notation,JS 对象标记)是一种轻量级数据交换格式,采用独立于编程语言的文本格式储存和表示数据,易于机器解析和生成,提升网络传输效率。 任何 JavaScript 支持…...

重学C++ | std::set 的原理
std::set 是C标准库中的容器之一,它基于红黑树实现。std::set 利用红黑树的特性来实现有序的插入、查找和删除操作,并且具有较好的平均和最坏情况下的时间复杂度。 当向 std::set 插入元素时,它会按照特定的比较函数(bool less<…...

AnV-X6使用及总结
目录 1 简介2 安装3 基础概念3.1 画布Graph3.2 基类Cell3.3 节点Node3.4 边Edge 4 使用4.1 创建节点4.2 节点连线4.3 事件系统 5 总结 1 简介 AntV是一个数据可视化(https://x6.antv.antgroup.com/)的工具(https://antv.vision/zh/ …...

Go 围炉札记
文章目录 一、安装二、文档三、使用 一、安装 VSCode 和 CLion 为 Go 开发配置Visual Studio Code | Microsoft Learn VScode下配置Go语言开发环境【2023最新】 基础篇:新手使用vs code新建go项目 vscode里安装Go插件和配置Go环境 GO 笔记 Golang 配置代理 golang…...

数据分析回头看2——重复值检查/元素替换/异常值筛选
0、前言: 这部分内容是对Pandas的回顾,同时也是对Pandas处理异常数据的一些技巧的总结,不一定全面,只是自己在数据处理当中遇到的问题进行的总结。 1、当数据中有重复行的时候需要检测重复行: 方法:使用p…...

什么是OSPF?为什么需要OSPF
【微|信|公|众|号:厦门微思网络】 【微思网络www.xmws.cn,成立于2002年,专业培训21年,思科、华为、红帽、ORACLE、VMware等厂商认证及考试,以及其他认证PMP、CISP、ITIL等】 什么是OSPF? 开放式最短路径优…...

轻量级的日志采集组件 Filebeat 讲解与实战操作
文章目录 一、概述二、Kafka 安装三、Filebeat 安装1)下载 Filebeat2)Filebeat 配置参数讲解3)filebeat.prospectors 推送kafka完整配置1、filebeat.prospectors2、processors3、output.kafka 4)filebeat.inputs 与 filebeat.pros…...

C# 委托和事件
C# 委托和事件 委托匿名方法事件 委托 当要把方法传送给其他方法时,需要使用委托。首先定义要使用的委托,对于委托,定义它就是告诉编译器这种类型的委托代表了哪种类型的方法,然后创建该委托的一个或多个实例。编译器在后台将创建…...

数据结构与算法之字典: Leetcode 349. 两个数组的交集 (Typescript版)
两个数组的交集 https://leetcode.cn/problems/intersection-of-two-arrays/description/ 题目和解题参考 https://blog.csdn.net/Tyro_java/article/details/133279737 使用字典来解题的算法实现 字典:顾名思义,像新华字典一样可查找,基…...

day-56 代码随想录算法训练营(19)动态规划 part 16
538.两个字符串的删除操作 思路一: 1.dp存储:以word1[i-1]结尾,word2[j-1]结尾,最少进行dp[i][j]次操作2.动态转移方程: if(word1[i-1]word2[i-1]) dp[i][j]dp[i-1][j-1]; else dp[i][j]min(dp[i-1][…...

蓝桥等考Python组别四级005
第一部分:选择题 1、Python L4 (15分) 字符“0”的ASCII码值为48,则字符“5”的ASCII码值为( )。 3953120240正确答案:B 2、Python L4 (15分) 下面哪个是Python中正确的变量名?( ) ABC#sup01Trueif正确答案:B...

【Linux】diff 命令
【Linux】diff 命令——并排格式输出 功能 diff 以逐行的方式,比较文本文件的异同处。 如果指定要比较目录,则 diff 会比较目录中相同文件名的文件,但不会比较其中子目录 diff [参数] [文件A] [文件B]diff [参数] [目录A] [目录B]【参数】…...

【51单片机】9-定时器和计数器
1.定时器的介绍 1.什么是定时器 (1)SoC的一种内部的外设【在单片机里面,但是在CPU外面】 (2)定时器就是CPU的”闹钟“ 2.什么是计数器 (1)定时器就是用计数的原始实现的 (2…...