c ffmpeg libavformat rtsp-client

Graba el flujo RTSP con FFmpeg libavformat



ffmpeg rtsp (4)

Estoy tratando de grabar la transmisión RTSP de la cámara Axis con FFmpeg libavformat. Puedo capturar el video de los archivos y luego guardarlo en otro archivo, está bien. Pero la cámara envía datos extraños, el FPS es 100 y la cámara envía cada 4to fotograma, por lo que el resultado es FPS aproximadamente 25. Pero el formato de libavatat establece paquetes dts / pts para 90000 fps (¿predeterminado?) Y la nueva secuencia de archivos tiene 100 fps. El resultado es un video de una hora con solo 100 cuadros.

Aqui esta mi codigo

#include <stdio.h> #include <stdlib.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavformat/avio.h> int main(int argc, char** argv) { AVFormatContext* context = avformat_alloc_context(); int video_stream_index; av_register_all(); avcodec_register_all(); avformat_network_init(); //open rtsp if(avformat_open_input(&context, "rtsp://195.200.199.8/mpeg4/media.amp",NULL,NULL) != 0){ return EXIT_FAILURE; } if(avformat_find_stream_info(context,NULL) < 0){ return EXIT_FAILURE; } //search video stream for(int i =0;i<context->nb_streams;i++){ if(context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) video_stream_index = i; } AVPacket packet; av_init_packet(&packet); //open output file AVOutputFormat* fmt = av_guess_format(NULL,"test2.avi",NULL); AVFormatContext* oc = avformat_alloc_context(); oc->oformat = fmt; avio_open2(&oc->pb, "test.avi", AVIO_FLAG_WRITE,NULL,NULL); AVStream* stream=NULL; int cnt = 0; //start reading packets from stream and write them to file av_read_play(context);//play RTSP while(av_read_frame(context,&packet)>=0 && cnt <100){//read 100 frames if(packet.stream_index == video_stream_index){//packet is video if(stream == NULL){//create stream in file stream = avformat_new_stream(oc,context->streams[video_stream_index]->codec->codec); avcodec_copy_context(stream->codec,context->streams[video_stream_index]->codec); stream->sample_aspect_ratio = context->streams[video_stream_index]->codec->sample_aspect_ratio; avformat_write_header(oc,NULL); } packet.stream_index = stream->id; av_write_frame(oc,&packet); cnt++; } av_free_packet(&packet); av_init_packet(&packet); } av_read_pause(context); av_write_trailer(oc); avio_close(oc->pb); avformat_free_context(oc); return (EXIT_SUCCESS); }

El archivo de resultados está aquí: http://dl.dropbox.com/u/1243577/test.avi

Gracias por cualquier consejo


Así es como lo hago. Lo que encontré fue que cuando recibía H264 la tasa de cuadros en el flujo no es correcta. Envía 1/90000 Base de tiempo. Omito la inicialización del nuevo flujo desde el flujo entrante y solo copio ciertos parámetros. La r_frame_rate entrante debe ser precisa si max_analyze_frames funciona correctamente.

#include <stdio.h> #include <stdlib.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavformat/avio.h> #include <sys/time.h> time_t get_time() { struct timeval tv; gettimeofday( &tv, NULL ); return tv.tv_sec; } int main( int argc, char* argv[] ) { AVFormatContext *ifcx = NULL; AVInputFormat *ifmt; AVCodecContext *iccx; AVCodec *icodec; AVStream *ist; int i_index; time_t timenow, timestart; int got_key_frame = 0; AVFormatContext *ofcx; AVOutputFormat *ofmt; AVCodecContext *occx; AVCodec *ocodec; AVStream *ost; int o_index; AVPacket pkt; int ix; const char *sProg = argv[ 0 ]; const char *sFileInput; const char *sFileOutput; int bRunTime; if ( argc != 4 ) { printf( "Usage: %s url outfile runtime/n", sProg ); return EXIT_FAILURE; } sFileInput = argv[ 1 ]; sFileOutput = argv[ 2 ]; bRunTime = atoi( argv[ 3 ] ); // Initialize library av_log_set_level( AV_LOG_DEBUG ); av_register_all(); avcodec_register_all(); avformat_network_init(); // // Input // //open rtsp if ( avformat_open_input( &ifcx, sFileInput, NULL, NULL) != 0 ) { printf( "ERROR: Cannot open input file/n" ); return EXIT_FAILURE; } if ( avformat_find_stream_info( ifcx, NULL ) < 0 ) { printf( "ERROR: Cannot find stream info/n" ); avformat_close_input( &ifcx ); return EXIT_FAILURE; } snprintf( ifcx->filename, sizeof( ifcx->filename ), "%s", sFileInput ); //search video stream i_index = -1; for ( ix = 0; ix < ifcx->nb_streams; ix++ ) { iccx = ifcx->streams[ ix ]->codec; if ( iccx->codec_type == AVMEDIA_TYPE_VIDEO ) { ist = ifcx->streams[ ix ]; i_index = ix; break; } } if ( i_index < 0 ) { printf( "ERROR: Cannot find input video stream/n" ); avformat_close_input( &ifcx ); return EXIT_FAILURE; } // // Output // //open output file ofmt = av_guess_format( NULL, sFileOutput, NULL ); ofcx = avformat_alloc_context(); ofcx->oformat = ofmt; avio_open2( &ofcx->pb, sFileOutput, AVIO_FLAG_WRITE, NULL, NULL ); // Create output stream //ost = avformat_new_stream( ofcx, (AVCodec *) iccx->codec ); ost = avformat_new_stream( ofcx, NULL ); avcodec_copy_context( ost->codec, iccx ); ost->sample_aspect_ratio.num = iccx->sample_aspect_ratio.num; ost->sample_aspect_ratio.den = iccx->sample_aspect_ratio.den; // Assume r_frame_rate is accurate ost->r_frame_rate = ist->r_frame_rate; ost->avg_frame_rate = ost->r_frame_rate; ost->time_base = av_inv_q( ost->r_frame_rate ); ost->codec->time_base = ost->time_base; avformat_write_header( ofcx, NULL ); snprintf( ofcx->filename, sizeof( ofcx->filename ), "%s", sFileOutput ); //start reading packets from stream and write them to file av_dump_format( ifcx, 0, ifcx->filename, 0 ); av_dump_format( ofcx, 0, ofcx->filename, 1 ); timestart = timenow = get_time(); ix = 0; //av_read_play(context);//play RTSP (Shouldn''t need this since it defaults to playing on connect) av_init_packet( &pkt ); while ( av_read_frame( ifcx, &pkt ) >= 0 && timenow - timestart <= bRunTime ) { if ( pkt.stream_index == i_index ) { //packet is video // Make sure we start on a key frame if ( timestart == timenow && ! ( pkt.flags & AV_PKT_FLAG_KEY ) ) { timestart = timenow = get_time(); continue; } got_key_frame = 1; pkt.stream_index = ost->id; pkt.pts = ix++; pkt.dts = pkt.pts; av_interleaved_write_frame( ofcx, &pkt ); } av_free_packet( &pkt ); av_init_packet( &pkt ); timenow = get_time(); } av_read_pause( ifcx ); av_write_trailer( ofcx ); avio_close( ofcx->pb ); avformat_free_context( ofcx ); avformat_network_deinit(); return EXIT_SUCCESS; }


En mi experiencia con un moderno codificador H.264, estoy descubriendo que la duración devuelta por ffmpeg es solo una "sugerencia" y que hay algo de "jitter" en el PTS. La única manera precisa de determinar la velocidad de cuadros o la duración es medirla usted mismo utilizando los valores PTS.

Para un codificador H.264 que se ejecuta a 30 fps, la duración siempre se informa como 3000/90000, mientras que la duración medida generalmente es de +/- 1, pero periódicamente salta, por ejemplo, 3000 + 25 un fotograma y 3000-25 el siguiente. Lo estoy suavizando para grabar, observando cualquier cuadro adyacente con una desviación opuesta y ajustando el PTS del segundo cuadro manteniendo la duración total.

Esto me da un flujo con una duración ocasional (calculada) de 30001 o 2999, reflejando la deriva del reloj.

Cuando se graba un flujo de 29.97fps, av_read_frame () siempre devuelve una duración de 3000, mientras que la duración calculada nominal es de 3003 (correcta para 29.97) con el mismo jitter y deriva como se describió anteriormente.

En mi caso, acabo de construir una máquina de estado para limpiar el tiempo. Espero que esto ayude a alguien.


No creo que debas incrementar el valor de PTS de esa manera. Puede funcionar en raras ocasiones en que la base de tiempo es la correcta, pero en el caso general no funcionará.

Deberías cambiar esto:

pkt.pts = ix++; pkt.dts = pkt.pts;

A esto:

pkt.pts = av_rescale_q(pkt.pts, ifcx->streams[0]->codec->time_base, ofcx->streams[0]->time_base); pkt.dts = av_rescale_q(pkt.dts, ifcx->streams[0]->codec->time_base, ofcx->streams[0]->time_base);

Lo que eso hace es convertir el PTS / DTS del paquete de las unidades utilizadas en el códec del flujo de entrada a las unidades del flujo de salida.

Además, algunas transmisiones tienen múltiples tics por fotograma, por lo que si el video se ejecuta a doble velocidad, es posible que tenga que hacerlo justo debajo de la línea anterior:

pkt.pts *= ifcx->streams[0]->codec->ticks_per_frame; pkt.dts *= ifcx->streams[0]->codec->ticks_per_frame;


Recientemente estaba haciendo lo mismo. Tuve FPS dos veces más bajo que la cámara enviada. La razón estaba en el campo AVstream-> codec-> ticks_per_frame, establecido en 2. Mi fuente era progresiva, y en caso de que la tuya estuviera intercalada, entonces esa podría ser una razón de otro factor de 2, lo que da 4 veces más FPS. 90000 Hz es la base de tiempo predeterminada para la transmisión de video enviada a través de RTSP. La base de tiempo es diferente de FPS en resolución. Por ejemplo, un cuadro con la marca de tiempo 30000 se mostrará a 1/3 de segundo si la base de tiempo es 90000 Hz. La base de tiempo se debe colocar en la estructura AVstream durante la salida, pero AVFormatContext debe tener un valor real de FPS.