最近因为耳机3.5mm接口的一些干扰问题,舍弃了之前的接入方式,需要重新实现网络音频流的接入,在这个过程中遇到了一些问题,特来记录一下~

一、网络音频流的接入

这个音频流来源各不相同,我这里是udp广播到10001端口上的,直接监听本机10001端口就可以看到音频信息。

需要提前知道的是这个网络音频流原本的采样率和位深是多少,这里我已经知道了61000hz的采样率和16的位深

这时候我们借助ffmpeg下的ffplay程序可以直接播放这个音频,命令如下

.\ffplay.exe -f s16le -ar 61000 -ac 2 udp://0.0.0.0:10001

二、音频流和视频流合并

通过ffmpeg对网络音频流进行读取,读取之后和rtsp视频流进行合并,

这一步简单讲一讲:

首先跟rtsp读取流的方式类似,ffmpeg对音频流的读取方式也是支持url直接读取的

如下所示:

if ((ret = ffmpeg.avformat_open_input(&formatContext, _audioUrl, _inputAudioFormat, &options)) < 0)
{
ffmpeg.av_dict_free(&options);
ffmpeg.avformat_close_input(&formatContext);
throw new Exception($"Could not open input path: {_audioUrl} (error '{FFmpegHelper.av_err2str(ret)}')");
}

需要注意的是,因为是网络音频流,这个音频流是不带位深和采样率信息的,需要我们自己提前指定

AVCodecParameters* codecpar = formatContext->streams[_audioIndex]->codecpar;

if (SampleRate > 0)
{
codecpar->sample_rate = 61000;
codecpar->channels = ffmpeg.av_get_channel_layout_nb_channels((ulong)_channelLayout);
}
_inputAudioFormat = ffmpeg.av_find_input_format("s16le");

由上面初始化开启探针读取网络音频流和视频流之后,剩下的处理就是合并了。

合并这里本质上就是合并读取出来的audioPacket和videoPacket,然后将packet转为frame,这时候转换为要输出的格式,再构建到packet中,最终将packet交错写入到同一个_formatContext进行保存。(很大白话了23333)

ffmpeg.av_interleaved_write_frame(_formatContext, outputPacket);

三、音画不同步的问题

上面的功能实现之后又发现保存后的视频存在音画不同步的问题

现象是:

①实时播放过程中,耳机反馈实时播放声音,实际动作比声音略晚,视频略早于声音

②播放视频过程中,声音比动作先出现了,也就是音频早于视频。

这里①很正常,因为本来音频通过wave过去并播放,肯定是需要时间的

但是②就不太对了,这是摆明了就是视频音画不同步。

而音画不同步的原因有以下几种可能:

①网络延迟:音频和视频数据在传输过程中可能会受到网络延迟的影响,导致数据到达接收端的时间不同步。这可能是由于网络拥塞、传输路径不稳定等原因引起的。

②编解码延迟:音频和视频的编解码过程可能会引入一定的延迟,导致数据的播放时间不同步。不同的编解码算法和参数设置可能会对延迟产生影响。

③媒体同步机制:RTP流中的音频和视频数据通常是分开传输的,接收端需要根据时间戳等信息将它们进行同步播放。如果同步机制实现不正确或者缺失,就会导致音频和视频不同步。

其实原因看起来很多,但归根到底都还是同步问题。

这里上面的现象让我直接推断是视频进入帧的时候比较晚,于是直接就怀疑了是视频读包要晚于音频的情况,想到这我们直接验证:

只需要在写入第一个视频帧和写入第一个音频帧的地方打印一个距流开启的时间就能观察到问题,这里我使用了_stopwatch进行准确的打印。

结果确实也验证了上面的想法——音频的偏移大概是35ms左右,可以忽略不计,而视频相较于音频大概延迟400-500ms。

那么我们这时候就可以进行音画同步了。

四、音画同步

首先说明:这里的同步是针对于探针时间偏差和编解码延迟导致的音画不同步问题设计的。

原理很简单:通过音频和视频写入第一帧的时间和流开启的时间的偏差,移动视频和音频的pts和dts进行对齐。

首先从上面的例子中就可以获取_stopwatch.ElapsedMilliseconds,分别是音频写入第一帧的时间和视频写入第一帧的时间与流开启的时间的偏差,这里分别是音频35ms,视频400ms。这时候我们统一对音频和视频pts和dts进行左移,将二者的首端都移到视频起始的位置。

这里需要注意的是,我们需要在时基转换之后的outpacket上进行操作,不然无法生效。

ffmpeg.av_packet_rescale_ts(outputPacket, _audioCodecContext->time_base, _audioStream->time_base);

在上面的时基转换后,我们以音频举例,这时获取了_sampleStartPts(音频的偏移),因为ElapsedMilliseconds是毫秒,所以我们需要除1000换算成秒,然后乘以时基,这时候我们就得到了pts实际的需要偏移的量。

_sampleStartPts = _stopwatch.ElapsedMilliseconds * _audioStream->time_base.den / 1000;

(特别提醒:这里的时基我默认分子为1,所以我直接用的分母,如果分子不同需要加入进行换算)

得到了ptsOffset(_sampleStartPts)之后呢,我们直接对所有输出的packet进行pts、dts移动就可以啦

outputPacket->pts -= _sampleStartPts;
outputPacket->dts -= _sampleStartPts;

这里因为是要去首端对齐,所以是减去偏移量~

最后再把上面的操作对应到视频流中执行一遍,出来的视频音画就同步了~

ps 文章原创于idealy233,转载请私信哦~

ffmpeg合并时音画不同步问题及音频软编码实现记录的更多相关文章

  1. (大概是最全的解决方法)使用bandicam录制视频导入pr后音画不同步问题

    遇到这个问题大部分都是使用了VBR来录制视频导致的, 搜集了各种能够找到的方法,并没有每个尝试过 一 Handbrake转码 Audio out of sync AFTER importing 解决方 ...

  2. 在FPS游戏中,玩家对音画同步感知的量化与评估

    前言 在游戏测试中,音画同步测试是个难点(所谓游戏音画同步:游戏中,音效与画面的同步程度),现在一般采用人工主观判断的方式测试,但这会带来2个问题: 无法准确量化,针对同一场景的多次测试结果可能会相反 ...

  3. QQ视频直播架构及原理 流畅与低延迟之间做平衡 音画如何做同步?

    QQ视频直播架构及原理 - tianyu的专栏 - CSDN博客 https://blog.csdn.net/wishfly/article/details/53035342 作者:王宇(腾讯音视频高 ...

  4. ffmpeg protocol concat 进行ts流合并视频的时间戳计算及其音画同步方式一点浅析

    ffmpeg protocol concat 进行ts流合并视频的时间戳计算及音画同步方式一点浅析 目录 ffmpeg protocol concat 进行ts流合并视频的时间戳计算及音画同步方式一点 ...

  5. 【FFMPEG】各种音视频编解码学习详解 h264 ,mpeg4 ,aac 等所有音视频格式

    目录(?)[-] 编解码学习笔记二codec类型 编解码学习笔记三Mpeg系列Mpeg 1和Mpeg 2 编解码学习笔记四Mpeg系列Mpeg 4 编解码学习笔记五Mpeg系列AAC音频 编解码学习笔 ...

  6. 音视频编解码流程与如何使用 FFMPEG 命令进行音视频处理

    一.前言 FFMPEG 是特别强大的专门用于处理音视频的开源库.你既可以使用它的 API 对音视频进行处理,也可以使用它提供的工具,如 ffmpeg, ffplay, ffprobe,来编辑你的音视频 ...

  7. FastAdmin 开发时如何与官方同步升级

    FastAdmin 开发时如何与官方同步升级 使用 FastAdmin 开发时为了与官方同步升级,推荐使用 git 管理代码. 官网上提供的完整包是为了方便第一次使用的人快速测试. 我一般是给官方的 ...

  8. rsync+inotify 实现资源服务器的同步目录下的文件变化时,备份服务器的同步目录更新,以资源服务器为准,去同步其他客户端

    测试环境: 资源服务器(主服务器):192.168.200.95 备份服务器(客户端):192.168.200.89 同步目录:/etc/test 同步时使用的用户名hadoop密码12345 实验目 ...

  9. 关于 Javascript 严格模式下多文件合并时注意

    Javascript 在第一行使用 "use strict" 声明严格模式. 但是在多个 js 文件合并时就需要注意了,可能你的是严格模式,但别的文件不是,就会造成错误. 为什么使 ...

  10. 使用ffmpeg合并视频文件的三种方法

    ffmpeg合并视频的方法有三种.国内大多数仅介绍了其中之一.于是觉得有必要翻译一下.其实在ffmpeg的 FAQ文档中有比较详细的说明. 使用concat协议进行视频文件的合并 这种方式的适用场景是 ...

随机推荐

  1. [转]springboot 监控 Actuator和Admin

    参考链接: 1.springboot 监控 Actuator和Admin 2.SpringBoot:Actuator监控中心+AdminUI界面管理

  2. Slate文档编辑器-Decorator装饰器渲染调度

    Slate文档编辑器-Decorator装饰器渲染调度 在之前我们聊到了基于文档编辑器的数据结构设计,聊了聊基于slate实现的文档编辑器类型系统,那么当前我们来研究一下slate编辑器中的装饰器实现 ...

  3. w3cschool-Linux 命令大全

    Linux关机命令和重启命令说明 在Linux中,常用的关机命令shutdown.halt.poweroff.init:重启命令有:reboot.本文将主要为大家带来一些常用的关机命令以及各种关机命令 ...

  4. jdk并发包源码解析

    一.总括 java底层并发包,笔者将该包大致分成3个层次. 1.基础依赖: 共享变量volatile:有利于线程可见性.Unsafe类:CAS(Compare and Swap)比较并交换,用于并发下 ...

  5. 在JDBC中使用带参数的SQL语句

    ADO.Net中,支持带参数的SQL语句,例如:Select * from Tables where column1=@column1,其中@column1为SQL参数,使用起来非常方便,而JDBC中 ...

  6. linux:rpm

    rpm 对软件包进行管理:查询.卸载.安装 查询 rpm -qa | grep 关键字   :查询指定的包 卸载 rpm -e  关键字  :卸载指定的包 若存在依赖关系: rpm -e  关键字  ...

  7. Fail pg walkthrough Intermediate

    nmap ┌──(root㉿kali)-[/home/ftpuserr] └─# nmap -p- -A 192.168.159.126 Starting Nmap 7.94SVN ( https:/ ...

  8. web95 比较麻烦的interval绕过

    审计 include("flag.php"); highlight_file(__FILE__); if(isset($_GET['num'])){ $num = $_GET['n ...

  9. 为什么UNIX使用init进程启动其他进程?

    为什么UNIX使用init进程启动其他进程? 在UNIX系统中,当系统启动时,内核完成初始化后会启动第一个用户空间进程,通常是init进程.init进程负责启动和管理其他用户空间进程,而内核本身并不直 ...

  10. Kali 安装并配置 Nessus

    Kali 安装并配置 Nessus 安装 Nessus 创建nessus文件夹 sudo mkdir /opt/nessus 下载 Nessus ( https://www.tenable.com/d ...