2017-02-14 6 views
0

Я разрабатываю приложение, которое должно публиковать медиапоток на URL-адрес «проглатывания» rtmp (используемый в YouTube Live или в качестве входа в Wowza Streaming Engine и т. Д.).), и я использую библиотеку ffmpeg (программно, из C/C++, а не инструмент командной строки) для обработки уровня rtmp. У меня рабочая версия готова, но я вижу некоторые проблемы при потоковой передаче потоков с большей пропускной способностью на серверы с худшим пингом. Проблема существует как при использовании встроенной rtmp-реализации ffmpeg, так и реализации librtmp.Поток публикации с использованием ffmpeg rtmp: пропускная способность сети не полностью используется

При переходе на локальный целевой сервер с низким уровнем пинга через хорошую сеть (в частности, локальный сервер Wowza) мой код до сих пор обрабатывал каждый поток, который я выбрал для него, и смог загрузить все в режиме реального времени - что важно, поскольку это подразумевается исключительно для живых потоков.

Однако при потоковой передаче на удаленный сервер с худшим пингом (например, URL-адрес запроса на YouTube на a.rtmp.youtube.com, который для меня имеет 50 + ms пинг), потоки с низкой пропускной способностью работают нормально, но с более высоким потоки полосы пропускания, сеть недоиспользуется - например, для потока 400 кБ/с, я вижу только использование сети ~ 140 кБ/с, при этом много кадров получает задержку/отброс, в зависимости от стратегии, которую я использую для работы с сетью отталкивать.

Теперь я знаю, что это не проблема с сетевым подключением к целевому серверу, потому что я могу успешно загрузить поток в реальном времени при использовании инструмента командной строки ffmpeg на один и тот же целевой сервер или используя мой код для потока к локальному серверу Wowza, который затем перенаправляет поток в точку приема в YouTube.

Таким образом, сетевое соединение не является проблемой, и проблема, похоже, связана с моим кодом.

Я приурочена различные части моего кода и обнаружил, что, когда появляется проблема, вызовов av_write_frame/av_interleaved_write_frame (я никогда не смешивайте & соответствовать им, я всегда использовать одну версию последовательно в какой-либо конкретной сборке, это просто, что я я экспериментировал с обоими, чтобы увидеть, есть ли какая-то разница), иногда занимает очень много времени - я видел, что эти звонки иногда занимают до 500-1000 мс, хотя средний «плохой случай» находится в диапазоне 50-100 мс. Не все призывы к ним берут это долгое время, большинство возвращаются мгновенно, но среднее время, затрачиваемое на эти вызовы, растет больше, чем средняя продолжительность кадра, поэтому я больше не получаю загрузку в режиме реального времени.

Основным подозреваемым, как мне кажется, может быть механизм окна подтверждения подтверждения rtmp, когда отправитель данных ожидает подтверждения получения после отправки каждого N байта, прежде чем отправлять какие-либо данные - это объясняет доступную сеть пропускная способность не полностью используется, поскольку клиент просто сидел бы там и ждал ответа (который занимает больше времени из-за более низкого пинга) вместо использования доступной полосы пропускания. Хотя я не смотрел код ftmpeg 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. 

Sen ding a audio frame:

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); 

Любые идеи? Я на правильном пути, думая о Окне подтверждения? Я что-то другое делаю неправильно?

ответ

0

Я не думаю, что это все объясняет, но, на всякий случай, для кого-то в подобной ситуации, исправление/обходной путь я нашел:

1) построить FFmpeg с реализацией librtmp протокола RTMP

2) построить FFmpeg с --enable-сети, он добавляет несколько функций к протоколу librtmp

3) передать «rtmp_buffer_size» параметр avio_open2, и увеличить это значение до удовлетворительной

Я не могу gi у вас полное пошаговое объяснение того, что происходит не так, но это фиксировало хотя бы симптом, который вызывал у меня проблемы.

Смежные вопросы