2012-04-13 15 views
10

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?

Respuesta

4

h264 es un estándar de codificación. Especifica cómo se comprimen y almacenan los datos de video en un formato que se puede descomprimir en una transmisión de video en un momento posterior.

RTP es un protocolo de transmisión. Especifica el formato y el orden de los paquetes que pueden transportar datos de audio y video codificados por un codificador arbitrario .

GStreamer espera recibir datos que se ajusten al protocolo RTP. ¿Su expectativa de que libaformat producirá los paquetes RTP inmediatamente legibles por GStreamer garantizados? ¿Tal vez GStreamers espera una descripción adicional de la secuencia que le permita aceptar y decodificar los paquetes transmitidos usando el decodificador apropiado? ¿Tal vez requiera un intercambio RTSP adicional o el archivo de descriptor de flujo SDP?

El mensaje de error indica bastante claramente que no se ha negociado un formato RTP. caps son short-hand para capacidades. El receptor necesita conocer las capacidades del transmisor para configurar correctamente la maquinaria receptora/decodificadora.

Sugiero al menos intentar al menos crear un archivo SDP para su secuencia RTP. libavformat should be able to do it for you.

+0

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. –

+0

Tienes que negociarlo de alguna manera. ¿Por qué no intentas crear un archivo SDP para tu transmisión? –

+0

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. –

Cuestiones relacionadas