【问题标题】:RTMP live stream directly from NVENC encoder直接来自 NVENC 编码器的 RTMP 直播流
【发布时间】:2019-04-15 14:49:36
【问题描述】:

我正在尝试创建一个实时 RTMP 流,其中包含使用 NVIDIA OptiX 生成的动画。该流将由 nginx + rtmp 模块接收并以 MPEG-DASH 格式广播。如果视频首先保存到 .flv 文件,然后我使用 ffmpeg 发送它而不使用命令重新格式化,则到 dash.js 播放器的完整链正在工作:

ffmpeg -re -i my_video.flv -c:v copy -f flv rtmp://x.x.x.x:1935/dash/test

但我想直接从代码中流式传输。有了这个我失败了...... Nginx 记录一个错误“破折号:收到无效的 avcc(2:没有这样的文件或目录)”。然后它似乎正确地接收了流(片段正在滚动,破折号清单在那里),但是无法在浏览器中播放流。

我只能在清单中看到直接流和来自文件的流之间的一个区别。缺少直接流中表示的编解码器属性:codecs="avcc1.000000" 而不是从文件流式传输时得到的“avc1.640028”。

我的代码打开了流:

av_register_all();
AVOutputFormat* fmt = av_guess_format("flv",
file_name, nullptr);
fmt->video_codec = AV_CODEC_ID_H264;

AVFormatContext* _oc;
avformat_alloc_output_context2(&_oc, fmt, nullptr, "rtmp://x.x.x.x:1935/dash/test");

AVStream* _vs = avformat_new_stream(_oc, nullptr);
_vs->id = 0;
_vs->time_base = AVRational { 1, 25 };
_vs->avg_frame_rate = AVRational{ 25, 1 };

AVCodecParameters *vpar = _vs->codecpar;
vpar->codec_id = fmt->video_codec;
vpar->codec_type = AVMEDIA_TYPE_VIDEO;
vpar->format = AV_PIX_FMT_YUV420P;
vpar->profile = FF_PROFILE_H264_HIGH;
vpar->level = _level;
vpar->width = _width;
vpar->height = _height;
vpar->bit_rate = _avg_bitrate;

avio_open(&_oc->pb, _oc->filename, AVIO_FLAG_WRITE);
avformat_write_header(_oc, nullptr);

我从 NVENC 编码器设置中获得的宽度、高度、比特率、电平和配置文件。我也做了错误检查,这里省略了。然后我有一个循环写入每个编码的数据包,IDR 帧等都使用 NVENC 动态准备。循环体是:

auto & pkt_data = _packets[i];
AVPacket pkt = { 0 };
av_init_packet(&pkt);
pkt.pts = av_rescale_q(_n_frames++, AVRational{ 1, 25 }, _vs->time_base);
pkt.duration = av_rescale_q(1, AVRational{ 1, 25 }, _vs->time_base);
pkt.dts = pkt.pts;
pkt.stream_index = _vs->index;
pkt.data = pkt_data.data();
pkt.size = (int)pkt_data.size();

if (!memcmp(pkt_data.data(), "\x00\x00\x00\x01\x67", 5))
{
    pkt.flags |= AV_PKT_FLAG_KEY;
}

av_write_frame(_oc, &pkt);

显然 ffmpeg 正在某处编写 avcc 代码...我不知道在哪里添加此代码以便 RTMP 服务器可以识别它。还是我错过了其他东西?

任何提示都非常感谢,伙计们!

【问题讨论】:

  • FLV 输出需要启用 AVCodecContext AV_CODEC_FLAG_GLOBAL_HEADER 标志,然后由编码器实现。请参阅 nvenc 的 ffmpeg 包装器,以了解如何在 OptiX 编码器中处理和实现这一点:github.com/FFmpeg/FFmpeg/blob/master/libavcodec/nvenc.c
  • 谢谢!我真的被这个问题困扰了很多天。使用您的链接代码,我终于可以将各个部分连接起来 - 现在一切正常!
  • 您能告诉我们解决方案吗? :)

标签: ffmpeg mpeg-dash libav libx264


【解决方案1】:

感谢 Gyan 的评论,我能够解决这个问题。在wrapper 中的AV_CODEC_FLAG_GLOBAL_HEADER 标志之后,可以看到如何添加全局标头,这在我的情况下是缺失的。可以直接使用NVENC API函数nvEncGetSequenceParams,不过反正我是用SDK的,还是比较干净的。

所以我不得不将标题附加到AVCodecParameters::extradata

std::vector<uint8_t> payload;
_encoder->GetSequenceParams(payload);
vpar->extradata_size = payload.size();
vpar->extradata = (uint8_t*)av_mallocz(payload.size() + AV_INPUT_BUFFER_PADDING_SIZE);
memcpy(vpar->extradata, payload.data(), payload.size());

_encoder 是我来自 SDK 的 NvEncoder 实例。

wrapper 也在做同样的事情,但是使用了已弃用的结构 AVCodecContext

【讨论】:

    猜你喜欢
    • 2017-09-02
    • 2011-07-26
    • 2019-06-09
    • 2018-08-08
    • 1970-01-01
    • 2019-11-08
    • 2016-12-10
    • 1970-01-01
    • 2015-10-26
    相关资源
    最近更新 更多