RTMP потоковая передача с использованием преобразования FFMPEG и HLS в NGINX - PullRequest
0 голосов
/ 30 апреля 2019

У меня есть некоторый код ffmpeg на c ++, который генерирует поток RTMP из H264 NALU и аудиосэмплы, закодированные в AAC.Я использую NGINX для передачи потока RTMP и перенаправления клиентам, и он работает нормально.Моя проблема заключается в том, что, когда я использую NGINX для преобразования потока RTMP в HLS, не генерируется порция HLS и генерируется плейлист.Я использую ffmpeg для копирования потока RTMP и создания нового потока в NGINX, преобразование HLS работает.

Вот что я получаю, когда делаю копию потока, используя FFMPEG:

Input #0, flv, from 'rtmp://127.0.0.1/live/beam_0'
Metadata:
Server          : NGINX RTMP (github.com/sergey-dryabzhinsky/nginx-rtmp-module)
displayWidth    : 1920
displayHeight   : 1080
fps             : 30
profile         : 
level           : 
Duration: 00:00:00.00, start: 5.019000, bitrate: N/A
Stream #0:0: Audio: aac, 44100 Hz, mono, fltp, 128 kb/s
Stream #0:1: Video: h264 (High), 1 reference frame, yuv420p(progressive, left), 1920x1080 (1920x1088), 8000 kb/s, 30 fps, 30.30 tbr, 1k tbn, 60 tbc

Output #0, flv, to 'rtmp://localhost/live/copy_stream':
Metadata:
Server          : NGINX RTMP (github.com/sergey-dryabzhinsky/nginx-rtmp-module)
displayWidth    : 1920
displayHeight   : 1080
fps             : 30
profile         : 
level           : 
encoder         : Lavf57.83.100
Stream #0:0: Video: h264 (High), 1 reference frame ([7][0][0][0] / 0x0007), yuv420p(progressive, left), 1920x1080 (0x0), q=2-31, 8000 kb/s, 30 fps, 30.30 tbr, 1k tbn, 1k tbc
Stream #0:1: Audio: aac ([10][0][0][0] / 0x000A), 44100 Hz, mono, fltp, 128 kb/s

Между этими двумя потоками нет большой разницы, поэтому я не понимаю, что происходит неправильнои что я должен изменить в своем коде C ++.Одна очень странная проблема, которую я вижу, заключается в том, что мой аудиопоток составляет 48 кГц, когда я публикую его, но здесь он распознается как 44100 Гц:

Output #0, flv, to 'rtmp://127.0.0.1/live/beam_0':
Stream #0:0, 0, 1/1000: Video: h264 (libx264), 1 reference frame, yuv420p, 1920x1080, 0/1, q=-1--1, 8000 kb/s, 30 fps, 1k tbn, 1k tbc
Stream #0:1, 0, 1/1000: Audio: aac, 48000 Hz, 1 channels, fltp, 128 kb/s

ОБНОВЛЕНИЕ 1:

Контекст вывода создается с использованиемследующий код:

pOutputFormatContext->oformat = av_guess_format("flv", url.toStdString().c_str(), nullptr);
memcpy(pOutputFormatContext->filename, url.toStdString().c_str(), url.length());
avio_open(&pOutputFormatContext->pb,  url.toStdString().c_str(), AVIO_FLAG_WRITE));
pOutputFormatContext->oformat->video_codec = AV_CODEC_ID_H264 ;
pOutputFormatContext->oformat->audio_codec = AV_CODEC_ID_AAC ;

Аудиопоток создается с помощью:

AVDictionary *opts = nullptr;
//pAudioCodec = avcodec_find_encoder(AV_CODEC_ID_VORBIS);
pAudioCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);

pAudioCodecContext = avcodec_alloc_context3(pAudioCodec);

pAudioCodecContext->thread_count = 1;
pAudioFrame = av_frame_alloc();

av_dict_set(&opts, "strict", "experimental", 0);
pAudioCodecContext->bit_rate = 128000;
pAudioCodecContext->sample_fmt = AV_SAMPLE_FMT_FLTP;
pAudioCodecContext->sample_rate = static_cast<int>(sample_rate) ;
pAudioCodecContext->channels = nb_channels ;
pAudioCodecContext->time_base.num = 1;
pAudioCodecContext->time_base.den = 1000 ;

//pAudioCodecContext->time_base.den = static_cast<int>(sample_rate) ;

pAudioCodecContext->codec_type = AVMEDIA_TYPE_AUDIO;
avcodec_open2(pAudioCodecContext, pAudioCodec, &opts);


pAudioFrame->nb_samples     = pAudioCodecContext->frame_size;
pAudioFrame->format         = pAudioCodecContext->sample_fmt;
pAudioFrame->channel_layout = pAudioCodecContext->channel_layout;
mAudioSamplesBufferSize = av_samples_get_buffer_size(nullptr, pAudioCodecContext->channels, pAudioCodecContext->frame_size, pAudioCodecContext->sample_fmt, 0);

avcodec_fill_audio_frame(pAudioFrame, pAudioCodecContext->channels, pAudioCodecContext->sample_fmt, (const uint8_t*) pAudioSamples, mAudioSamplesBufferSize, 0);

if( pOutputFormatContext->oformat->flags & AVFMT_GLOBALHEADER )
    pAudioCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

pAudioStream = avformat_new_stream(pOutputFormatContext, 0);

pAudioStream->codec = pAudioCodecContext ;
pAudioStream->id =  pOutputFormatContext->nb_streams-1;;
pAudioStream->time_base.den = pAudioStream->pts.den =  pAudioCodecContext->time_base.den;
pAudioStream->time_base.num = pAudioStream->pts.num =  pAudioCodecContext->time_base.num;

mAudioPacketTs = 0 ;

Видеопоток создается с помощью:

pVideoCodec = avcodec_find_encoder(AV_CODEC_ID_H264);

pVideoCodecContext = avcodec_alloc_context3(pVideoCodec);

pVideoCodecContext->codec_type = AVMEDIA_TYPE_VIDEO ;
pVideoCodecContext->thread_count = 1 ;
pVideoCodecContext->width = width;
pVideoCodecContext->height = height;
pVideoCodecContext->bit_rate = 8000000 ;
pVideoCodecContext->time_base.den = 1000 ;
pVideoCodecContext->time_base.num = 1 ;
pVideoCodecContext->gop_size = 10;
pVideoCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;
pVideoCodecContext->flags = 0x0007 ;

pVideoCodecContext->extradata_size = sizeof(extra_data_buffer);
pVideoCodecContext->extradata = (uint8_t *) av_malloc ( sizeof(extra_data_buffer) );
memcpy ( pVideoCodecContext->extradata, extra_data_buffer, sizeof(extra_data_buffer));

avcodec_open2(pVideoCodecContext,pVideoCodec,0);

pVideoFrame = av_frame_alloc();

AVDictionary *opts = nullptr;
av_dict_set(&opts, "strict", "experimental", 0);
memcpy(pOutputFormatContext->filename, this->mStreamUrl.toStdString().c_str(), this->mStreamUrl.length());
pOutputFormatContext->oformat->video_codec = AV_CODEC_ID_H264 ;

if( pOutputFormatContext->oformat->flags & AVFMT_GLOBALHEADER )
    pVideoCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

pVideoStream = avformat_new_stream(pOutputFormatContext, pVideoCodec);


//This following section is because AVFormat complains about parameters being passed throught the context and not CodecPar
pVideoStream->codec = pVideoCodecContext ;
pVideoStream->id = pOutputFormatContext->nb_streams-1;
pVideoStream->time_base.den = pVideoStream->pts.den =  pVideoCodecContext->time_base.den;
pVideoStream->time_base.num = pVideoStream->pts.num =  pVideoCodecContext->time_base.num;
pVideoStream->avg_frame_rate.num = fps ;
pVideoStream->avg_frame_rate.den = 1 ;
pVideoStream->codec->gop_size = 10 ;

mVideoPacketTs = 0 ;

Затем каждый видеопакет иаудиопакет передается с правильным масштабом pts / dts.Я исправил проблему 48 кГц.Это было потому, что я настраивал поток через контекст кодека и через параметры кодека (из-за предупреждения во время выполнения).

Этот поток RTMP все еще не работает для преобразования HLS NGINX, но если я просто используюFFMPEG для получения RTMP-потока из NGINX и повторной публикации с копированием кодека, после чего он работает.

...