1. 程式人生 > 實用技巧 >Qt音視訊開發37-USB攝像頭解碼ffmpeg方案

Qt音視訊開發37-USB攝像頭解碼ffmpeg方案

一、前言

用ffmpeg來處理USB攝像頭,是前段時間研究視訊監控ffmpeg核心的時候搞定的,既然ffmpeg這麼牛逼的庫可以解析各種音視訊,我想處理個本地USB攝像頭應該也不是什麼難事,果真搜尋也是一大堆,當然主要也是因為有個專案的應用需要用到ffmpeg來處理本地USB攝像頭,需要拿到每張圖片做智慧分析,用Qt自帶的camera類不大好處理,剛好將ffmpeg的處理流程都搞清楚了,索性直接用ffmpeg來直接處理好了,用上這麼強大的解碼庫,理論上支援各種USB攝像頭。本地USB攝像機不需要硬解碼,視訊流編碼型別為 AV_CODEC_ID_RAWVIDEO 畫素格式為 AV_PIX_FMT_YUYV422 不經過解碼操作直接就可顯示。

ffmpeg方案處理流程:

  1. 引入avdevice.h標頭檔案,呼叫avdevice_register_all();註冊本地裝置處理。
  2. 呼叫av_dict_set設定解析度(video_size)、幀率(framerate)等引數。
  3. 呼叫av_find_input_format設定輸入格式。
  4. 呼叫avformat_open_input開啟檔案。
  5. 呼叫av_find_best_stream找到視訊流地址。
  6. 呼叫avcodec_find_decoder設定視訊解碼器。
  7. 呼叫av_read_frame迴圈解碼讀取幀資料。
  8. 呼叫avcodec_send_packet avcodec_receive_frame解碼資料。
  9. 處理完以後呼叫av_frame_free avcodec_close等釋放資源。

二、功能特點

  1. 同時支援windows、linux、嵌入式linux上的USB攝像頭實時採集。
  2. 支援多路USB攝像頭多執行緒實時採集。
  3. 在嵌入式linux裝置上,自動查詢USB裝置檔案並載入。
  4. 可手動設定裝置檔名稱,手動設定後按照手動設定的裝置檔案載入。
  5. 在嵌入式linux裝置上支援人臉識別介面,實時繪製人臉框。
  6. 具有開啟、暫停、繼續、關閉、截圖等常規功能。
  7. 可設定兩路OSD標籤,分別設定文字、顏色、字號、位置等。
  8. 可作為視訊監控系統使用。

三、效果圖

四、相關站點

  1. 國內站點:https://gitee.com/feiyangqingyun/QWidgetDemo
  2. 國際站點:https://github.com/feiyangqingyun/QWidgetDemo
  3. 個人主頁:https://blog.csdn.net/feiyangqingyun
  4. 知乎主頁:https://www.zhihu.com/people/feiyangqingyun/
  5. 體驗地址:https://blog.csdn.net/feiyangqingyun/article/details/97565652

五、核心程式碼

void FFmpegThread::initOption()
{
    //在開啟碼流前指定各種引數比如:探測時間/超時時間/最大延時等
    //設定快取大小,1080p可將值調大
    av_dict_set(&options, "buffer_size", "8192000", 0);
    //以tcp方式開啟,如果以udp方式開啟將tcp替換為udp
    av_dict_set(&options, "rtsp_transport", transport.toUtf8().constData(), 0);
    //設定超時斷開連線時間,單位微秒,3000000表示3秒
    av_dict_set(&options, "stimeout", "3000000", 0);
    //設定最大時延,單位微秒,1000000表示1秒
    av_dict_set(&options, "max_delay", "1000000", 0);
    //自動開啟執行緒數
    av_dict_set(&options, "threads", "auto", 0);

    //單獨對USB攝像機設定引數
    if (isUsbCamera) {
        //設定輸入格式
        //av_dict_set(&options, "input_format", "mjpeg", 0);
        //設定解析度
        QString size = QString("%1x%2").arg(videoWidth).arg(videoHeight);
        av_dict_set(&options, "video_size", size.toUtf8().constData(), 0);
        //設定幀率
        av_dict_set(&options, "framerate", "25", 0);
    }

    //本地USB攝像機不需要硬解碼,強制改成回撥執行和無硬解碼
    //視訊流編碼型別為 AV_CODEC_ID_RAWVIDEO 畫素格式為 AV_PIX_FMT_YUYV422 不經過解碼操作直接就可顯示
    if (isUsbCamera) {
        callback = true;
        hardware = "none";
    }

    //沒有啟用opengl則強制改為回撥
#ifndef opengl
    callback = true;
#endif

    //rtmp視訊流強制改成儲存成h264裸流,目前儲存成mp4還有問題
    if (url.startsWith("rtmp", Qt::CaseInsensitive)) {
        saveMp4 = false;
    }
}

bool FFmpegThread::initInput()
{
    //例項化格式處理上下文
    formatCtx = avformat_alloc_context();
    //設定超時回撥,有些不存在的地址或者網路不好的情況下要卡很久
    formatCtx->interrupt_callback.callback = AVInterruptCallBackFun;
    formatCtx->interrupt_callback.opaque = this;

    //必須要有tryOpen標誌位來控制超時回撥,由他來控制是否繼續阻塞等待開啟
    tryOpen = false;

    //先判斷是否是本地裝置(video=裝置名字串),開啟的方式不一樣
    QByteArray urlData = url.toUtf8();
    AVInputFormat *ifmt = NULL;
    if (isUsbCamera) {
#if defined(Q_OS_WIN)
        ifmt = av_find_input_format("dshow");
#elif defined(Q_OS_LINUX)
        //ifmt = av_find_input_format("v4l2");
        ifmt = av_find_input_format("video4linux2");
#elif defined(Q_OS_MAC)
        ifmt = av_find_input_format("avfoundation");
#endif
    }

    int result = avformat_open_input(&formatCtx, urlData.data(), ifmt, &options);
    tryOpen = true;
    if (result < 0) {
        qDebug() << TIMEMS << "open input error" << url;
        return false;
    }

    //釋放設定引數
    if (options != NULL) {
        av_dict_free(&options);
    }

    //獲取流資訊
    result = avformat_find_stream_info(formatCtx, NULL);
    if (result < 0) {
        qDebug() << TIMEMS << "find stream info error";
        return false;
    }

    return true;
}