使用FFmpeg libavformat记录RTSP流

我正试图用FFmpeg libavformat记录来自Axis相机的RTSP流。 我可以从文件中获取video,然后将其保存到另一个文件,这没关系。 但是摄像机发送奇怪的数据,FPS为100,摄像机每隔4帧发送一次,因此结果FPS约为25.但是libavformat设置数据包dts / pts为90000 fps(默认值?),新文件流有100fps。 结果是一小时video,只有100帧。

这是我的代码

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

结果文件位于: http : //dl.dropbox.com/u/1243577/test.avi

谢谢你的建议

我就是这样做的。 我发现当接收H264时,流中的帧速率不正确。 它发送1/90000时基。 我跳过从传入流初始化新流并只复制某些参数。 如果max_analyze_frames正常工作,则传入的r_frame_rate应该是准确的。

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

我不认为你应该像这样增加PTS值。 它可能适用于时间基础恰到好处的罕见情况,但对于一般情况它不起作用。

你应该改变这个:

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

对此:

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

这样做是将数据包的PTS / DTS从输入流的编解码器中使用的单位转换为输出流的单位。

此外,某些流有每帧多个刻度,因此如果video以双倍速度运行,您可能需要在上面的行下方:

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

根据我使用现代H.264编码器的经验,我发现ffmpeg返回的持续时间只是一个“建议”,并且PTS中存在一些“抖动”。 确定帧速率或持续时间的唯一准确方法是使用PTS值自行测量。

对于以30fps运行的H.264编码器,持续时间总是报告为3000/90000,而测量持续时间通常为+/- 1,但是周期性跳跃表示一帧3000 + 25,下一帧3000-25。 我通过注意任何具有相反偏差的相邻帧并在保持总持续时间的同时调整第二帧的PTS来平滑这个以进行记录。

这给了我一个偶尔(计算)持续时间为30001或2999的流,反映了时钟漂移。

当记录29.97fps流时,av_read_frame()始终返回持续时间3000,而标称计算持续时间为3003(正确为29.97),具有与上述相同的抖动和漂移。

在我的情况下,我只是建立了一个状态机来清理时间。 希望这有助于某人。

最近也在做同样的事情。 我的FPS比发送的相机低两倍。 原因是在AVstream-> codec-> ticks_per_frame字段中,设置为2.我的源是渐进的,如果你的是交错的 – 那么这可能是另一个因素为2的原因,给出4倍不同的FPS。 90000 Hz是通过RTSP发送的video流的默认时基。 时基与FPS的分辨率不同。 例如,如果时基是90000Hz,则具有时间戳30000的帧将以1/3秒显示。 时基应该在输出期间放入AVstream结构中,但AVFormatContext应该具有真实的FPS值。