【秒懂音視頻開發】20_視頻錄制02_編程


本文的主要內容:演示如何通過編程采集攝像頭的視頻數據。
整體的流程跟《音頻錄制02_編程》類似。

依賴庫

需要依賴4個庫。

extern "C" {
#include <libavdevice/avdevice.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libavcodec/avcodec.h>
}

宏定義

#ifdef Q_OS_WIN
    // 格式名稱
    #define FMT_NAME "dshow"
    // 設備名稱
    #define DEVICE_NAME "video=Integrated Camera"
    // YUV文件名
    #define FILENAME "F:/out.yuv"
#else
    #define FMT_NAME "avfoundation"
    #define DEVICE_NAME "0"
    #define FILENAME "/Users/mj/Desktop/out.yuv"
#endif

#define ERROR_BUF(ret) \
    char errbuf[1024]; \
    av_strerror(ret, errbuf, sizeof (errbuf));

權限申請

在Mac平台,有2個注意點:

  • 需要在Info.plist中添加攝像頭的使用說明,申請攝像頭的使用權限
  • 使用Debug模式運行程序
  • 不然會出現閃退的情況
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>NSCameraUsageDescription</key>
        <string>使用攝像頭采集您的靚照</string>
</dict>
</plist>

注冊設備

在整個程序的運行過程中,只需要執行1次注冊設備的代碼。

// 初始化libavdevice並注冊所有輸入和輸出設備
avdevice_register_all();

獲取輸入格式對象

// 獲取輸入格式對象
AVInputFormat *fmt = av_find_input_format(FMT_NAME);
if (!fmt) {
    qDebug() << "av_find_input_format error" << FMT_NAME;
    return;
}

打開輸入設備

// 格式上下文
AVFormatContext *ctx = nullptr;

// 傳遞給輸入設備的參數
AVDictionary *options = nullptr;
av_dict_set(&options, "video_size", "640x480", 0);
av_dict_set(&options, "pixel_format", "yuyv422", 0);
av_dict_set(&options, "framerate", "30", 0);

// 打開輸入設備
int ret = avformat_open_input(&ctx, DEVICE_NAME, fmt, &options);
if (ret < 0) {
    ERROR_BUF(ret);
    qDebug() << "avformat_open_input error" << errbuf;
    return;
}

打開輸出文件

// 打開文件
QFile file(FILENAME);
if (!file.open(QFile::WriteOnly)) {
    qDebug() << "file open error" << FILENAME;

    // 關閉輸入設備
    avformat_close_input(&ctx);
    return;
}

采集視頻數據

// 計算每一幀的大小
AVCodecParameters *params = ctx->streams[0]->codecpar;
int imageSize = av_image_get_buffer_size(
                    (AVPixelFormat) params->format,
                    params->width, params->height,
                    1);

// 數據包
AVPacket *pkt = av_packet_alloc();
while (!isInterruptionRequested()) {
    // 不斷采集數據
    ret = av_read_frame(ctx, pkt);

    if (ret == 0) { // 讀取成功
        // 將數據寫入文件
        file.write((const char *) pkt->data, imageSize);
        /*
         這里要使用imageSize,而不是pkt->size。
         pkt->size有可能比imageSize大(比如在Mac平台),
         使用pkt->size會導致寫入一些多余數據到YUV文件中,
         進而導致YUV內容無法正常播放
        */

        // 釋放資源
        av_packet_unref(pkt);
    } else if (ret == AVERROR(EAGAIN)) { // 資源臨時不可用
        continue;
    } else { // 其他錯誤
        ERROR_BUF(ret);
        qDebug() << "av_read_frame error" << errbuf;
        break;
    }
}

釋放資源

// 釋放資源
av_packet_free(&pkt);

// 關閉文件
file.close();

// 關閉設備
avformat_close_input(&ctx);


免責聲明!

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



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