10

先週、RTP 経由で H.264 ストリーミングを実装しようと試みました。x264 をエンコーダとして使用し、libavformat を使用してストリームをパックして送信しました。問題は、私が知る限り、正しく機能していないことです。

現在、ランダム データ (x264_picture_alloc) をエンコードし、libx264 から NAL フレームを抽出しています。これはかなり単純です。

x264_picture_t pic_out;
x264_nal_t* nals;
int num_nals;
int frame_size = x264_encoder_encode(this->encoder, &nals, &num_nals, this->pic_in, &pic_out);

if (frame_size <= 0)
{
    return frame_size;
}

// push NALs into the queue
for (int i = 0; i < num_nals; i++)
{
    // create a NAL storage unit
    NAL nal;
    nal.size = nals[i].i_payload;
    nal.payload = new uint8_t[nal.size];
    memcpy(nal.payload, nals[i].p_payload, nal.size);

    // push the storage into the NAL queue
    {
        // lock and push the NAL to the queue
        boost::mutex::scoped_lock lock(this->nal_lock);
        this->nal_queue.push(nal);
    }
}

nal_queueフレームを安全に Streamer クラスに渡し、フレームを送信するために使用されます。これを機能させるためにテストしているだけなので、現在はスレッド化されていません。個々のフレームをエンコードする前に、エンコーダーを確実に初期化しました。

しかし、x264 が問題であるとは思いません。返される NAL でフレーム データを確認できるからです。データのストリーミングは、Streamer クラスで最初に初期化される libavformat で実現されます。

Streamer::Streamer(Encoder* encoder, string rtp_address, int rtp_port, int width, int height, int fps, int bitrate)
{
    this->encoder = encoder;

    // initalize the AV context
    this->ctx = avformat_alloc_context();
    if (!this->ctx)
    {
        throw runtime_error("Couldn't initalize AVFormat output context");
    }

    // get the output format
    this->fmt = av_guess_format("rtp", NULL, NULL);
    if (!this->fmt)
    {
        throw runtime_error("Unsuitable output format");
    }
    this->ctx->oformat = this->fmt;

    // try to open the RTP stream
    snprintf(this->ctx->filename, sizeof(this->ctx->filename), "rtp://%s:%d", rtp_address.c_str(), rtp_port);
    if (url_fopen(&(this->ctx->pb), this->ctx->filename, URL_WRONLY) < 0)
    {
        throw runtime_error("Couldn't open RTP output stream");
    }

    // add an H.264 stream
    this->stream = av_new_stream(this->ctx, 1);
    if (!this->stream)
    {
        throw runtime_error("Couldn't allocate H.264 stream");
    }

    // initalize codec
    AVCodecContext* c = this->stream->codec;
    c->codec_id = CODEC_ID_H264;
    c->codec_type = AVMEDIA_TYPE_VIDEO;
    c->bit_rate = bitrate;
    c->width = width;
    c->height = height;
    c->time_base.den = fps;
    c->time_base.num = 1;

    // write the header
    av_write_header(this->ctx);
}

これは、物事がうまくいかないように見えるところです。av_write_header上記はまったく何もしないようです。これを確認するためにwiresharkを使用しました。参考までに、以前に x264 を処理するために使用されたオブジェクトへの参照を使用Streamer streamer(&enc, "10.89.6.3", 49990, 800, 600, 30, 40000);して、Streamer インスタンスを初期化します。encEncoder

NALをストリーミングしたいときは、これを使用します:

// grab a NAL
NAL nal = this->encoder->nal_pop();
cout << "NAL popped with size " << nal.size << endl;

// initalize a packet
AVPacket p;
av_init_packet(&p);
p.data = nal.payload;
p.size = nal.size;
p.stream_index = this->stream->index;

// send it out
av_write_frame(this->ctx, &p);

この時点で、RTP データがネットワーク上に表示されていることがわかります。これまで送信してきたフレームのように見えます。x264 からの小さな著作権の塊も含まれています。しかし、私が使用したプレーヤーは、データの意味を理解できませんでした. VLC は SDP の説明を必要としなくなりますが、これは明らかに必須ではありません

私はそれからそれを再生しようとしましたgst-launch:

gst-launch udpsrc port=49990 ! rtph264depay ! decodebin ! xvimagesink

これはUDPデータを待っていますが、受信すると次のようになります:

エラー: 要素 /GstPipeline:pipeline0/GstRtpH264Depay:rtph264depay0: RTP 形式がネゴシエートされませんでした。追加のデバッグ情報: gstbasertpdepayload.c(372): gst_base_rtp_depayload_chain (): /GstPipeline:pipeline0/GstRtpH264Depay:rtph264depay0: 入力バッファーには RTP キャップを設定する必要があります。これは通常、上流のソース要素 (多くの場合、udpsrc または appsrc) の「caps」プロパティを設定するか、depayloader の前に capsfilter 要素を配置して「caps」プロパティを設定することによって実現されます。http://cgit.freedesktop.org/gstreamer/gst-plugins-good/tree/gst/rtp/READMEも参照して ください。

私は GStreamer を使用してストリーミングしていないので、RTP キャップでそれが何を意味するのかよくわかりません。しかし、RTP でストリームを説明するのに十分な情報を送信していないのではないかと思います。私はビデオにかなり慣れていないので、ここで欠けている重要なものがあるように感じます. ヒントはありますか?

4

1 に答える 1

4

h264 はエンコード標準です。ビデオ データを圧縮し、後でビデオ ストリームに解凍できる形式で保存する方法を指定します。

RTP は伝送プロトコルです。これは、任意のエンコーダーによってエンコードされたオーディオ ビデオ データを運ぶことができるパケットの形式と順序を指定します。

GStreamer は、RTP プロトコルに準拠したデータを受信することを想定しています。libaformat が GStreamer によってすぐに読み取り可能な RTP パケットを生成するという期待は保証されますか? おそらく、GStreamer は、適切なデコーダーを使用してストリーミングされたパケットを受け入れてデコードできるようにする追加のストリーム記述を期待していますか? おそらく、追加の RTSP 交換または SDP ストリーム記述子ファイルが必要ですか?

エラー メッセージは、RTP 形式がネゴシエートされていないことを明確に示しています。caps機能の省略形です。レシーバーは、レシーバー/デコード機構を正しくセットアップするために、トランスミッターの機能を知る必要があります。

少なくとも RTP ストリーム用の SDP ファイルを作成することを強くお勧めします。libavformatがそれを実行できるはずです

于 2012-04-13T16:40:47.627 に答える