ffmpeg問題匯總及解決方案 <設置avformat_open_input 超時><轉>


1:
如果數據是rtp/rtsp傳輸的話,ffmpeg會每隔30s(哪里設置該值?)發送一個keepalive包,如果ipc支持GET_PARAMETER命令,就發該命令等ipc回復以確認ipc還活着。
某些ipc(IPCamera)不支持GET_PARAMETER的rtsp命令。則會通過OPTIONS *來是keepalive,不過這會導致連接斷掉
原代碼,及修改部分如下。先這樣處理,有時間再研究原代碼為什么不這么處理。

//ffmpeg/libavformat/rtspdec.c rtsp_read_packet()
if (!(rt->rtsp_flags & RTSP_FLAG_LISTEN)) {
        /* send dummy request to keep TCP connection alive */
        if ((av_gettime() - rt->last_cmd_time) / 1000000 >= rt->timeout / 2 ||
            rt->auth_state.stale) {
            if (rt->server_type == RTSP_SERVER_WMS ||
                (rt->server_type != RTSP_SERVER_REAL &&
                 rt->get_parameter_supported)) {
                ff_rtsp_send_cmd_async(s, "GET_PARAMETER", rt->control_uri, NULL);
            } else {
                // ff_rtsp_send_cmd_async(s, "OPTIONS", "*", NULL);                 //原代碼
                ff_rtsp_send_cmd_async(s, "OPTIONS", rt->control_uri, NULL);        //修改后的代碼,這樣就算ipc不支持GET_PARAMETER的命令可以keepalive了
            }
            /* The stale flag should be reset when creating the auth response in
             * ff_rtsp_send_cmd_async, but reset it here just in case we never
             * called the auth code (if we didn't have any credentials set). */
            rt->auth_state.stale = 0;
        }
    }

2:
如果數據是rtp/rtsp/tcp傳輸,如果沒有設置stimeout(socket timeout),那么server斷線后av_read_frame會阻塞

AVFormatContext *pFormatCtx = NULL;
    AVPacket packet;
    AVDictionary *optionsDict = NULL;
    char *streamurl1 = "rtsp://test:test@192.168.11.111:554/test.stream";
    
    // Register all formats and codecs
    av_register_all();
    avformat_network_init();

    pFormatCtx = avformat_alloc_context();
    av_dict_set(&optionsDict, "rtsp_transport", "tcp", 0);                //采用tcp傳輸
    av_dict_set(&optionsDict, "stimeout", "2000000", 0);                  //如果沒有設置stimeout,那么把ipc網線拔掉,av_read_frame會阻塞(時間單位是微妙)

    // Open video file
    if(avformat_open_input(&pFormatCtx, streamurl, NULL, &optionsDict)!=0){
        printf("linesize = %d\n", __LINE__);    
        return -1; // Couldn't open file
    }

    // Retrieve stream information
    if(avformat_find_stream_info(pFormatCtx, NULL)<0){
        printf("linesize = %d\n", __LINE__);    
        return -1; // Couldn't find stream information
    }

    // Find the first video stream
    int videoStream=-1;
    for(i=0; i<pFormatCtx->nb_streams; i++){
        if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
            videoStream=i;
            break;
        }
    }
    
    if(videoStream==-1){
        printf("linesize = %d\n", __LINE__);    
        return -1; // Didn't find a video stream
    }
    
    // Read frames and save first five frames to disk
    while(av_read_frame(pFormatCtx, &packet)>=0) {
        av_free_packet(&packet);
    }

如果是ffmpeg2.0.1版本的話,添加一句就ok了。可是舊版本ffmpeg1.2.*沒有stimeout這個字段可以設置
那么只好自己添加了。
在ffmpeg/libavformat/rtsp.h  struct RTSPState添加一個stimeout字段

typedef struct RTSPState {
    /**
     * timeout of socket i/o operations.(this version do not have this segment. added by yqing, refer to ffmpeg-2.0.1)
     */
    int stimeout;
} RTSPState;

在ffmpeg/libavformat/rtsp.c  ff_rtsp_options添加一個stimeout字段

const AVOption ff_rtsp_options[] = {
    { "stimeout", "timeout (in micro seconds) of socket i/o operations.", OFFSET(stimeout), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC },    //添加這行代碼
    RTSP_REORDERING_OPTS(),
    { NULL },
};

在ffmpeg/libavformat/rtsp.c  ff_rtsp_connect函數修改部分代碼如下

//ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL);                            //原代碼
  ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, "?timeout=%d", rt->stimeout);     //修改后的代碼

原貼地址:http://blog.chinaunix.net/uid-27091949-id-4186640.html

1:
如果數據是rtp/rtsp傳輸的話,ffmpeg會每隔30s(哪里設置該值?)發送一個keepalive包,如果ipc支持GET_PARAMETER命令,就發該命令等ipc回復以確認ipc還活着。
某些ipc(IPCamera)不支持GET_PARAMETER的rtsp命令。則會通過OPTIONS *來是keepalive,不過這會導致連接斷掉
原代碼,及修改部分如下。先這樣處理,有時間再研究原代碼為什么不這么處理。

點擊(此處)折疊或打開

  1. //ffmpeg/libavformat/rtspdec.c rtsp_read_packet()
  2. if (!(rt->rtsp_flags & RTSP_FLAG_LISTEN)) {
  3.         /* send dummy request to keep TCP connection alive */
  4.         if ((av_gettime() - rt->last_cmd_time) / 1000000 >= rt->timeout / 2 ||
  5.             rt->auth_state.stale) {
  6.             if (rt->server_type == RTSP_SERVER_WMS ||
  7.                 (rt->server_type != RTSP_SERVER_REAL &&
  8.                  rt->get_parameter_supported)) {
  9.                 ff_rtsp_send_cmd_async(s, "GET_PARAMETER", rt->control_uri, NULL);
  10.             } else {
  11.                 // ff_rtsp_send_cmd_async(s, "OPTIONS", "*", NULL);                 //原代碼
  12.                 ff_rtsp_send_cmd_async(s, "OPTIONS", rt->control_uri, NULL);        //修改后的代碼,這樣就算ipc不支持GET_PARAMETER的命令可以keepalive了
  13.             }
  14.             /* The stale flag should be reset when creating the auth response in
  15.              * ff_rtsp_send_cmd_async, but reset it here just in case we never
  16.              * called the auth code (if we didn't have any credentials set). */
  17.             rt->auth_state.stale = 0;
  18.         }
  19.     }


2:
如果數據是rtp/rtsp/tcp傳輸,如果沒有設置stimeout(socket timeout),那么server斷線后av_read_frame會阻塞

點擊(此處)折疊或打開

  1. AVFormatContext *pFormatCtx = NULL;
  2.     AVPacket packet;
  3.     AVDictionary *optionsDict = NULL;
  4.     char *streamurl1 = "rtsp://test:test@192.168.11.111:554/test.stream";
  5.     
  6.     // Register all formats and codecs
  7.     av_register_all();
  8.     avformat_network_init();
  9.     pFormatCtx = avformat_alloc_context();
  10.     av_dict_set(&optionsDict, "rtsp_transport", "tcp", 0);                //采用tcp傳輸
  11.     av_dict_set(&optionsDict, "stimeout", "2000000", 0);                  //如果沒有設置stimeout,那么把ipc網線拔掉,av_read_frame會阻塞(時間單位是微妙)
  12.     // Open video file
  13.     if(avformat_open_input(&pFormatCtx, streamurl, NULL, &optionsDict)!=0){
  14.         printf("linesize = %d\n", __LINE__);    
  15.         return -1; // Couldn't open file
  16.     }
  17.     // Retrieve stream information
  18.     if(avformat_find_stream_info(pFormatCtx, NULL)<0){
  19.         printf("linesize = %d\n", __LINE__);    
  20.         return -1; // Couldn't find stream information
  21.     }
  22.     // Find the first video stream
  23.     int videoStream=-1;
  24.     for(i=0; i<pFormatCtx->nb_streams; i++){
  25.         if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
  26.             videoStream=i;
  27.             break;
  28.         }
  29.     }
  30.     
  31.     if(videoStream==-1){
  32.         printf("linesize = %d\n", __LINE__);    
  33.         return -1; // Didn't find a video stream
  34.     }
  35.     
  36.     // Read frames and save first five frames to disk
  37.     while(av_read_frame(pFormatCtx, &packet)>=0) {
  38.         av_free_packet(&packet);
  39.     }

如果是ffmpeg2.0.1版本的話,添加一句就ok了。可是舊版本ffmpeg1.2.*沒有stimeout這個字段可以設置
那么只好自己添加了。
在ffmpeg/libavformat/rtsp.h  struct RTSPState添加一個stimeout字段

點擊(此處)折疊或打開

  1. typedef struct RTSPState {
  2.     /**
  3.      * timeout of socket i/o operations.(this version do not have this segment. added by yqing, refer to ffmpeg-2.0.1)
  4.      */
  5.     int stimeout;
  6. } RTSPState;

在ffmpeg/libavformat/rtsp.c  ff_rtsp_options添加一個stimeout字段

點擊(此處)折疊或打開

  1. const AVOption ff_rtsp_options[] = {
  2.     { "stimeout", "timeout (in micro seconds) of socket i/o operations.", OFFSET(stimeout), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC },    //添加這行代碼
  3.     RTSP_REORDERING_OPTS(),
  4.     { NULL },
  5. };

在ffmpeg/libavformat/rtsp.c  ff_rtsp_connect函數修改部分代碼如下

點擊(此處)折疊或打開

  1. //ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL);                            //原代碼
  2.   ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, "?timeout=%d", rt->stimeout);     //修改后的代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM