RTMP прямой эфир прямо из кодера NVENC - PullRequest
0 голосов
/ 12 ноября 2018

Я пытаюсь создать живой RTMP-поток, содержащий анимацию, созданную с помощью NVIDIA OptiX.Поток принимается модулем nginx + rtmp и транслируется в формате MPEG-DASH.Полная цепочка вплоть до проигрывателя dash.js работает, если видео сначала сохраняется в файл .flv, а затем я отправляю его с помощью ffmpeg без переформатирования, используя команду:

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 мог его распознать.Или я что-то упускаю?

Любой намек, который очень ценят, ребята!

1 Ответ

0 голосов
/ 16 ноября 2018

Благодаря комментарию Гаяна я смог решить проблему.После флага AV_CODEC_FLAG_GLOBAL_HEADER в оболочке можно увидеть, как добавляется глобальный заголовок, чего в моем случае не было.Вы можете напрямую использовать функцию API-интерфейса NVENC 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 - мой экземпляр NvEncoder из SDK.

Оболочка делает то же самое, но использует устаревшую структуру AVCodecContext.

...