最近因为耳机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. MySQL版本是8.0.3,数据库连接字符串都检查无误,驱动类名加不加cj都试过了,URL是否加时区、是否加useSSL都试过了,都无法正常登录进去!

    我在运行library这个web程序登录时(我的MySQL版本是8.0.3,数据库连接字符串都检查无误,驱动类名加不加cj都试过了,URL是否加时区.是否加useSSL都试过了,都无法正常登录进去!) ...

  2. 开源即时通讯IM框架MobileIMSDK的H5端技术概览

    一.基本介绍 MobileIMSDK的H5端是一套纯JS编写的基于标准WebSocket的即时通讯库: 1)超轻量级.极少依赖: 2)纯JS编写.高度提炼,简单易用: 3)基于标准WebSocket协 ...

  3. 分析基于ASP.NET Core Kernel的gRPC服务在不同.NET版本的不同部署方式的不同线程池下的性能表现

    分析基于ASP.NET Core Kernel的gRPC服务在不同.NET版本的不同部署方式的不同线程池下的性能表现 使用默认的 gRPC 项目模板创建,垃圾回收器类型为 ServerGC(Serve ...

  4. Solution -「NOI 2017」「洛谷 P3826」蔬菜

    \(\mathscr{Description}\)   Link.   原题意比较简洁了.注意一下卖出的菜也会变质,且让它们代替未卖出的菜变质是更优的. \(\mathscr{Solution}\) ...

  5. Java方法引用、lambda如何序列化&方法引用与lambda底层原理

    系列文章目录和关于我 0.引入 最近笔者使用flink实现一些实时数据清洗(从kafka清洗数据写入到clickhouse)的功能,在编写flink作业后进行上传,发现运行的时候抛出:java.io. ...

  6. Java之线程本地变量ThreadLocal-copy

    基本概念和用法 线程本地变量是说,每个线程都有同一个变量的独有拷贝,这个概念听上去比较难以理解,我们先直接来看类TheadLocal的用法. ThreadLocal是一个泛型类,接受一个类型参数T,它 ...

  7. VueRouter案列

    案列内容,包含,模板,路由传参,路由重定向,路由嵌套,能够复习路由基本使用,成果如图: 完整代码: 1 <!DOCTYPE html> 2 <html lang="en&q ...

  8. Kotlin:【字符串操作】substring、split、replace、字符串比较==与===、foreach遍历字符

    字符串的内存区域问题: 都在常量池内,相同的字符串比较属于同一引用 在字符串常量池开辟了新的内存区域,一共有三个对象,所以引用比较不相等

  9. 如何安全发布 CompletableFuture ?Java9新增方法分析

    如何安全发布 CompletableFuture ?Java9新增方法分析 本文未经允许禁止转载. JDK9 中对于CompletableFuture做了新的增强,除了超时功能(orTimeout), ...

  10. Redis缓存异常及解决方案

    本文分享自天翼云开发者社区<Redis缓存异常及解决方案>,作者:l****n 本文向读者解释了Redis使用过程中,数据不一致.缓存雪崩.缓存击穿和缓存穿透等问题的定义,并给出对应的解决 ...