ffmpeg - 使用 ffmpeg rtmp : network bandwidth not fully utilized 进行流式发布

标签 ffmpeg ping rtmp

我正在开发一个需要将媒体流发布到 rtmp “摄取” url 的应用程序(如 YouTube Live 中使用的,或作为 Wowza 流引擎的输入等),我正在使用 ffmpeg 库(以编程方式,来自C/C++,不是命令行工具)来处理 rtmp 层。我已经准备好了一个工作版本,但是在将更高带宽的流传输到 ping 更差的服务器时遇到了一些问题。使用 ffmpeg "native"/builtin rtmp 实现和 librtmp 实现时都存在问题。

当通过良好的网络(特别是本地 Wowza 服务器)以低 ping 流式传输到本地目标服务器时,到目前为止,我的代码已经处理了我向它抛出的每个流并设法实时上传所有内容 - 这很重要,因为这专门用于直播。

但是,当流式传输到具有更差 ping 的远程服务器时(例如,a.rtmp.youtube.com 上的 youtube 摄取 url,对我来说有 50+ms 的 ping),较低带宽的流工作正常,但使用较高的带宽流网络未得到充分利用 - 例如,对于 400kB/s 的流,我只看到 ~140kB/s 的网络使用情况,有很多帧被延迟/丢弃,这取决于我用来处理网络推送的策略。

现在,我知道到目标服务器的网络连接不是问题,因为我可以在使用 ffmpeg 命令行工具到同一目标服务器或使用我的代码流式传输到本地 Wowza 时成功实时上传流然后将流转发到 youtube 摄取点的服务器。

所以网络连接不是问题,问题似乎在于我的代码。

我已经对我的代码的各个部分进行了计时,发现当问题出现时,调用 av_write_frame/av_interleaved_write_frame(我从不混合和匹配它们,我总是在任何特定构建中始终使用一个版本,只是我已经尝试过两者都看是否有任何区别)有时需要很长时间 - 我看到这些调用有时需要 500-1000 毫秒,尽管平均“坏情况”在 50-100 毫秒范围内。并非所有对他们的调用都需要这么长时间,大多数会立即返回,但是这些调用所花费的平均时间比平均帧持续时间要长,所以我不再获得实时上传。

在我看来,主要的嫌疑人可能是 rtmp 确认窗口机制,其中数据发送者在发送每 N 个字节后等待确认接收,然后再发送更多数据 - 这可以解释可用网络带宽不完全使用,因为客户端只是坐在那里等待响应(由于 ping 较低,这需要更长的时间),而不是使用可用带宽。虽然我还没有查看 ffmpeg 的 rtmp/librtmp 代码来查看它是否真的实现了这种节流,但它可能完全是另外一回事。

该应用程序的完整代码太多,无法在此处发布,但这里有一些重要的片段:

格式上下文创建:

const int nAVFormatContextCreateError = avformat_alloc_output_context2(&m_pAVFormatContext, nullptr, "flv", m_sOutputUrl.c_str());

流创建:
m_pVideoAVStream = avformat_new_stream(m_pAVFormatContext, nullptr);
m_pVideoAVStream->id = m_pAVFormatContext->nb_streams - 1;

m_pAudioAVStream = avformat_new_stream(m_pAVFormatContext, nullptr);
m_pAudioAVStream->id = m_pAVFormatContext->nb_streams - 1;

视频流设置:
m_pVideoAVStream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
m_pVideoAVStream->codecpar->codec_id = AV_CODEC_ID_H264;
m_pVideoAVStream->codecpar->width = nWidth;
m_pVideoAVStream->codecpar->height = nHeight;
m_pVideoAVStream->codecpar->format = AV_PIX_FMT_YUV420P;
m_pVideoAVStream->codecpar->bit_rate = 10 * 1000 * 1000;
m_pVideoAVStream->time_base = AVRational { 1, 1000 };

m_pVideoAVStream->codecpar->extradata_size = int(nTotalSizeRequired);
m_pVideoAVStream->codecpar->extradata = (uint8_t*)av_malloc(m_pVideoAVStream->codecpar->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
// Fill in the extradata here - I'm sure I'm doing that correctly.

音频流设置:
m_pAudioAVStream->time_base = AVRational { 1, 1000 };
// Let's leave creation of m_pAudioCodecContext out of the scope of this question, I'm quite sure everything is done right there.
const int nAudioCodecCopyParamsError = avcodec_parameters_from_context(m_pAudioAVStream->codecpar, m_pAudioCodecContext);

打开连接:
const int nAVioOpenError = avio_open2(&m_pAVFormatContext->pb, m_sOutputUrl.c_str(), AVIO_FLAG_WRITE);

启动流:
AVDictionary * pOptions = nullptr;
const int nWriteHeaderError = avformat_write_header(m_pAVFormatContext, &pOptions);

发送视频帧:
AVPacket pkt = { 0 };
av_init_packet(&pkt);
pkt.dts = nTimestamp;
pkt.pts = nTimestamp;
pkt.duration = nDuration; // I know what I have the wrong duration sometimes, but I don't think that's the issue.
pkt.data = pFrameData;
pkt.size = pFrameDataSize;
pkt.flags = bKeyframe ? AV_PKT_FLAG_KEY : 0;
pkt.stream_index = m_pVideoAVStream->index;
const int nWriteFrameError = av_write_frame(m_pAVFormatContext, &pkt); // This is where too much time is spent.

发送音频帧:
AVPacket pkt = { 0 };
av_init_packet(&pkt);
pkt.pts = m_nTimestampMs;
pkt.dts = m_nTimestampMs;
pkt.duration = m_nDurationMs;
pkt.stream_index = m_pAudioAVStream->index;
const int nWriteFrameError = av_write_frame(m_pAVFormatContext, &pkt);

有任何想法吗?我在考虑确认窗口方面是否走在正确的轨道上?我在做其他完全错误的事情吗?

最佳答案

我认为这并不能解释一切,但是,以防万一,对于处于类似情况的人,我发现的修复/解决方法是:

1)用rtmp协议(protocol)的librtmp实现构建ffmpeg

2) 使用 --enable-network 构建 ffmpeg,它为 librtmp 协议(protocol)添加了一些功能

3)将“rtmp_buffer_size”参数传递给avio_open2,并将其值增加到满意的值

我不能给你一个完整的逐步解释出了什么问题,但这至少解决了导致我问题的症状。

关于ffmpeg - 使用 ffmpeg rtmp : network bandwidth not fully utilized 进行流式发布,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42226828/

相关文章:

android - 在 Android 上将 jpeg 编码为 H264 流 (ffmpeg)

c# - 在 C# 中获得更快的 Ping 结果

c# - PING 的回复是否包含原始数据?

ffmpeg - RTMP 上的 ffplay/ffmpeg,采用 H264 编码

ffmpeg rtsp_transport 到 rtmp 问题

linux - ffmpeg 错误 : cur_dts is invalid

batch-file - 拖放ffmpeg批处理文件

ffmpeg - DirectX 与 FFmpeg

java - 从 PING 扫描中检索 MAC 地址

java - rtmp 和 rtsp 协议(protocol)有什么区别?