私はrtmpの「取り込み」URLにメディアストリームを公開する必要があるアプリケーションを開発しています(YouTubeライブやWowza Streaming Engineなど)、私はrtmp層を処理するためにffmpegライブラリ(プログラムでは、C/C++から、コマンドラインツールではない)を使用しています。私は動作可能なバージョンを準備しましたが、pingが悪いほど高い帯域幅のストリームをサーバにストリーミングするときにいくつかの問題が発生しています。問題はffmpeg "native"/builtin rtmp実装とlibrtmpの実装の両方を使用している場合に存在します。ffmpegを使用したストリーム公開rtmp:ネットワーク帯域幅が十分に活用されていません
良いネットワーク(具体的にはローカルWowzaサーバ)を通じてpingが低いローカルターゲットサーバにストリーミングするとき、私のコードではこれまでスローしたすべてのストリームを処理しており、これはライブストリーム専用のため重要です。
しかし、pingが悪いリモートサーバーにストリーミングすると(たとえば、50Mmsのpingを持つa.rtmp.youtube.comのYouTubeの取り込みURL)、帯域幅の小さいストリームは正常に動作しますが、高いネットワークが十分に活用されていない - 例えば400kB/sのストリームの場合、私はネットワークを扱うために使用している戦略に応じて、多くのフレームが遅延/プッシュバック。
ffmpegコマンドラインツールを使用して同じターゲットサーバにストリームをアップロードしたり、ストリームにストリームをアップロードしたりすることができるため、ターゲットサーバとのネットワーク接続に問題はありません。ストリームをYouTubeの取り込みポイントに転送するローカルのWowzaサーバーに転送します。
したがって、ネットワーク接続は問題ではなく、問題は自分のコードにあるようです。
問題が発生したときに、av_write_frame/av_interleaved_write_frame(私は&を使用しません)を呼び出すと、特定のビルドで常に一貫して1つのバージョンを使用していることがわかりました。何か違いがあるかどうかを調べるために両方を試しました)時には本当に長い時間がかかることがあります - 平均的な "悪いケース"は50-100msの範囲ですが、これらの呼び出しが500-1000msまでかかることがあります。これらのコールはすべてこの時間がかかっているわけではありません。しかし、これらのコールで費やされた平均時間は平均フレーム時間よりも長くなりますので、もうリアルタイムでアップロードすることはできません。
主な疑いがあるのは、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.
オーディオストリームのセットアップは:
AVDictionary * pOptions = nullptr;
const int nWriteHeaderError = avformat_write_header(m_pAVFormatContext, &pOptions);
ビデオフレームを送信する:ストリームの開始
const int nAVioOpenError = avio_open2(&m_pAVFormatContext->pb, m_sOutputUrl.c_str(), AVIO_FLAG_WRITE);
:
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);
は、接続を開く
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);
アイデアをお持ちですか?謝辞窓について考えるのは正しいですか?私は何か完全に間違っているのですか?