使用ffmpeg rtmp进行流发布:未充分利用网络带宽

时间:2017-02-14 12:58:24

标签: ffmpeg ping rtmp

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

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

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

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

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

我计算了代码的各个部分并发现问题出现时,调用av_write_frame / av_interleaved_write_frame(我从不混合和匹配它们,我总是在任何特定版本中使用一个版本,它&# 39;只是我已经尝试了两者,看看是否有任何差异)有时需要很长时间 - 我已经看到这些电话有时需要500-1000毫秒,尽管平均值为#34 ;坏情况"在50-100ms范围内。并非所有对他们的通话都需要这么长时间,大部分都会立即返回,但这些通话所花费的平均时间比平均帧时长要大,所以我不再进行实时上传。

在我看来,主要的嫌疑人可能是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 个答案:

答案 0 :(得分:1)

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

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

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

3)传递" rtmp_buffer_size"参数为avio_open2,并将其值增加到令人满意的值

我无法全面逐步解释出现了什么问题,但这至少解决了导致我出现问题的症状。