2012-02-12 223 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),具有與上述相同的抖動和漂移。

在我的情況下,我只是建立了一個狀態機來清理時間。希望這可以幫助某人。