2012-02-12 120 views
15

我试图用FFmpeg libavformat从Axis相机记录RTSP流。 我可以从文件抓取视频,然后将其保存到另一个文件,这是好的。但相机发送奇怪的数据,FPS为100,相机每隔4帧发送一次,因此结果FPS约为25.但libavformat为90000帧/秒(默认值?)设置数据包的DTS/PTS,新文件流的帧数为100fps。结果是只有100帧的一小时视频。使用FFmpeg libavformat记录RTSP流

这里是我的代码

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

结果文件是在这里:http://dl.dropbox.com/u/1243577/test.avi

感谢您的任何意见

+0

在你的代码中,你只记录前100帧,这是打算? – ciphor 2012-02-13 03:56:43

+0

相机的格式是什么?是h.264吗? – ransh 2015-06-14 16:53:32

+0

另请参阅https://stackoverflow.com/questions/10715170/receiving-rtsp-stream-using-ffmpeg-library – rogerdpack 2017-08-17 22:43:17

回答

0

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

+0

亲爱的托沙,这是解决方案?更改ticks_per_frame?改变fps?改变时基? – 2012-10-30 16:57:01

6

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

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

av_dump_format(ofcx,0,ofcx-> filename,1);给我一个错误。 – patrick 2015-06-12 11:32:18

+0

我得到同样的错误..你有没有得到任何解决方案,然后请帮助我们解决......预先感谢 – Anny 2016-04-09 06:40:28

3

我不认为你应该像这样增加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输出流。

此外,一些流有多个蜱每帧,所以如果视频以两倍速度运行时,您可能需要将上面的线下这一权利:

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

即使这适用于上面的代码,我得到一个mp4文件,显然不正确的时间。 mp4文件中约10秒钟录制的视频在播放时即刻完成。我可以擦洗所有的画面,但不会播放。 [mp4 @ 0x1040e9000]应用程序提供的持续时间:1967652701196434754/timestamp:749796超出mov/mp4格式的范围 [mp4 @ 0x1040e9000] pts没有值 – stevex 2016-05-20 11:23:36

1

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

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

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

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

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