He intentado durante la última semana implementar la transmisión H.264 a través de RTP, usando x264 como codificador y libavformat para empacar y enviar la transmisión. El problema es que, por lo que puedo ver, no está funcionando correctamente.transmitiendo H.264 a través de RTP con libavformat
Ahora mismo estoy codificando datos aleatorios (x264_picture_alloc) y extrayendo marcos NAL de libx264. Esto es bastante simple:
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
se utiliza para pasar de manera segura a través de marcos a una clase Streamer que a su vez envía los marcos cabo. En este momento no está enhebrado, ya que solo estoy probando para tratar de hacer que esto funcione. Antes de codificar cuadros individuales, me he asegurado de inicializar el codificador.
Pero no creo que x264 sea el problema, ya que puedo ver datos de marcos en los NALs. Streaming los datos se lleva a cabo con libavformat, que es inicializado por primera vez en una clase Streamer:
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);
}
Aquí es donde las cosas parecen ir mal. av_write_header
arriba parece no hacer absolutamente nada; He usado wireshark para verificar esto. Como referencia, utilizo Streamer streamer(&enc, "10.89.6.3", 49990, 800, 600, 30, 40000);
para inicializar la instancia de Streamer, siendo enc
una referencia a un objeto Encoder
utilizado para manejar x264 anteriormente.
Ahora cuando quiero transmitir un NAL, yo uso esto:
// 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);
En este punto, puedo ver los datos RTP que aparece en la red, y parece que los marcos que he estado enviando , incluso incluye una pequeña mancha de copyright de x264. Pero, ningún jugador que he usado ha sido capaz de dar sentido a los datos. VLC deja de querer una descripción de SDP, que apparently isn't required.
Luego trató de jugar a través de gst-launch
:
gst-launch udpsrc port=49990 ! rtph264depay ! decodebin ! xvimagesink
Esto se sentará a la espera de los datos UDP, pero cuando se recibe, me sale:
ERROR: element /GstPipeline:pipeline0/GstRtpH264Depay:rtph264depay0: No RTP format was negotiated. Additional debug info: gstbasertpdepayload.c(372): gst_base_rtp_depayload_chain(): /GstPipeline:pipeline0/GstRtpH264Depay:rtph264depay0: Input buffers need to have RTP caps set on them. This is usually achieved by setting the 'caps' property of the upstream source element (often udpsrc or appsrc), or by putting a capsfilter element before the depayloader and setting the 'caps' property on that. Also see http://cgit.freedesktop.org/gstreamer/gst-plugins-good/tree/gst/rtp/README
Como estoy no estoy usando GStreamer para transmitir, no estoy muy seguro de lo que significa con las tapas RTP. Pero me hace preguntarme si no estoy enviando suficiente información sobre RTP para describir la transmisión. Soy bastante nuevo en el video y siento que hay algo clave que me falta aquí. ¿Algún consejo?
Eso es lo que pasa, no sé, y tengo problemas para encontrar la información que necesito. Por lo que puedo decir, libavformat empacará cosas en una transmisión RTP por ti (y no enviará paquetes inválidos, lo he intentado). No hace ninguna negociación RTSP; con el tiempo esto se señalará en Feng o en alguna otra aplicación externa para manejar la transmisión RTSP a los clientes. Sin embargo, eso no explica por qué nada puede hacer que cara o cruz del flujo RTP genere libavformat. –
Tienes que negociarlo de alguna manera. ¿Por qué no intentas crear un archivo SDP para tu transmisión? –
Lo probé y puedo hacer que VLC muestre una pantalla verde, ya sea que sea correcta o no, no lo sé, pero es un comienzo. Estaremos trabajando en ello hoy, así que veremos si este fue realmente el problema. –