Enregistrement de stream RTSP avec FFmpeg libavformat

J’essaie d’enregistrer un stream RTSP depuis une caméra Axis avec FFmpeg libavformat. Je peux récupérer des vidéos à partir de fichiers, puis les enregistrer dans un autre fichier, ce n’est pas grave. Mais la caméra envoie des données étranges, le FPS est 100 et la caméra envoie toutes les 4 images, donc le résultat est d’environ 25. Mais libavformat définit les paquets dts / pts pour 90000 fps (par défaut?) Et le nouveau stream de fichiers à 100 fps. Le résultat est une vidéo d’une heure avec seulement 100 images.

Voici mon code

#include  #include  #include  #include  #include  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;inb_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 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); } 

Le fichier de résultats est ici: http://dl.dropbox.com/u/1243577/test.avi

Merci pour tout conseil

Voici comment je le fais. Ce que j’ai trouvé, c’est que lors de la réception de H264, le framerate dans le stream n’est pas correct. Il envoie 1/90000 Base de temps. Je saute l’initialisation du nouveau stream à partir du stream entrant et ne fais que copier certains parameters. Le r_frame_rate entrant doit être précis si max_analyze_frames fonctionne correctement.

 #include  #include  #include  #include  #include  #include  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; } 

Je ne pense pas que vous soyez censé simplement augmenter la valeur PTS comme ça. Cela peut fonctionner dans de rares occasions où la base de temps est juste, mais dans le cas général cela ne fonctionnera pas.

Vous devriez changer ceci:

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

Pour ça:

 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); 

Cela permet de convertir les unités PTS / DTS du paquet utilisées dans le codec du stream d’entrée en unités du stream de sortie.

En outre, certains stream ont plusieurs ticks par image. Par conséquent, si la vidéo tourne à une vitesse double, vous devrez peut-être afficher ceci directement sous la ligne ci-dessus:

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

D’après mon expérience avec un encodeur H.264 moderne, je constate que la durée renvoyée par ffmpeg n’est qu’une “suggestion” et qu’il existe une certaine “instabilité” dans le PTS. Le seul moyen précis de déterminer la fréquence d’images ou la durée consiste à le mesurer vous-même à l’aide des valeurs PTS.

Pour un codeur H.264 fonctionnant à 30 images par seconde, la durée est toujours signalée par 3000/90000, alors que la durée mesurée est généralement de +/- 1, mais saute périodiquement, par exemple 3000 + 25 une image et 3000-25 la suivante. J’ajoute cela en notant les images adjacentes avec une déviation opposée et en ajustant le PTS de la 2ème image tout en préservant la durée totale.

Cela me donne un stream avec une durée occasionnelle (calculée) de 30001 ou 2999 reflétant la dérive de l’horloge.

Av_read_frame () renvoie toujours une durée de 3 000, alors que la durée nominale calculée est 3003 (correct pour 29,97) avec les mêmes sauts et dérives que ceux décrits ci-dessus.

Dans mon cas, je viens de construire une machine à états pour nettoyer le minutage. En espérant que cela aide quelqu’un.

Faisait récemment la même chose. J’avais FPS deux fois plus bas que la caméra envoyée. La raison en était dans le champ AVstream-> codec-> ticks_per_frame, défini sur 2. Mon source était progressive, et si votre fichier était entrelacé – cela pourrait être une raison d’un autre facteur de 2, donnant 4x FPS différents. 90000 Hz est la base de temps par défaut pour le stream vidéo envoyé via RTSP. La base de temps est différente de FPS en résolution. Par exemple, une image avec l’horodatage 30000 sera affichée à 1/3 seconde si la base de temps est 90000 Hz. La base de temps doit être insérée dans la structure AVstream lors de la sortie, mais AVFormatContext doit avoir une valeur FPS réelle.