音视频之视频录制(十)

MAC端录制视频

命令行录制

avfoundation支持的设备

ffmpeg -f avfoundation -list_devices true -i ' '

输出结果大致如下所示:

[AVFoundation indev @ 0x7fd2d1804400] AVFoundation video devices:
[AVFoundation indev @ 0x7fd2d1804400] [0] FaceTime HD Camera
[AVFoundation indev @ 0x7fd2d1804400] [1] Capture screen 0
[AVFoundation indev @ 0x7fd2d1804400] AVFoundation audio devices:
[AVFoundation indev @ 0x7fd2d1804400] [0] Built-in Microphone
[AVFoundation indev @ 0x7fd2d1804400] [1] 江波的AirPods


0号设备就是Mac自带的摄像头

avfoundation支持的参数

ffmpeg -h demuxer=avfoundation

输出结果大致如下所示:

AVFoundation indev AVOptions:
  -list_devices      <boolean>    .D......... list available devices (default false)
  -video_device_index <int>        .D......... select video device by index for devices with same name (starts at 0) (from -1 to INT_MAX) (default -1)
  -audio_device_index <int>        .D......... select audio device by index for devices with same name (starts at 0) (from -1 to INT_MAX) (default -1)
  -pixel_format      <pix_fmt>    .D......... set pixel format (default yuv420p)
  -framerate         <video_rate> .D......... set frame rate (default "ntsc")
  -video_size        <image_size> .D......... set video size
  -capture_cursor    <boolean>    .D......... capture the screen cursor (default false)
  -capture_mouse_clicks <boolean>    .D......... capture the screen mouse clicks (default false)
  -capture_raw_data  <boolean>    .D......... capture the raw data from device connection (default false)
  -drop_late_frames  <boolean>    .D......... drop frames that are available later than expected (default true)
  • -video_size: 分辨率
  • -pixel_format: 像素格式
    • 默认是yuv420p
  • -framerate: 帧率(每秒菜鸡多少帧画面)
    • 默认是ntsc,也就是30000/1001,约等于29.970030
  • -list_device: true表示列出avfoundation支持的所有设备

录制

使用0号视频设备录制

ffmpeg -f avfoundation -i 0 out.yuv

 

然后你可能会遇到一个错误: 这个设备(摄像头)不支持29.970030的帧率

Selected framerate (29.970030) is not supported by the device

 

重新设置30的帧率试试 

ffmpeg -f avfoundation -framerate 30 -i 0 out.yuv
  • 这个设备(摄像头)不支持yuv420p
  • 只支持uyvy422、yuyv422、nv12、0rgb、bgr0
  • 并且自动选择使用uyvy422替代yuv420p  
Selected pixel format (yuv420p) is not supported by the input device.
Supported pixel formats:
  uyvy422
  yuyv422
  nv12
  0rgb
  bgr0
Overriding selected pixel format to use uyvy422 instead.

与此同时,也成功开始采集摄像头的视频数据了。

  • 像素格式: uyvy422
  • 分辨率: 1280x720
  • 帧率: 30    
Input #0, avfoundation, from '0':
    Stream #0:0: Video: rawvideo, uyvy422, 1280x720

Output #0, rawvideo, to 'out.yuv':
    Stream #0:0: Video: rawvideo, uyvy422, 1280x720, 30 fps

播放录制好的YUV

ffplay -video_size 1280x720 -pixel_format uyvy422 -framerate 30 out.yuv

编程录制视频

依赖库

需要依赖5个库

extern "C" {
// 设备相关API
#include <libavdevice/avdevice.h>
// 格式相关API
#include <libavformat/avformat.h>
// 工具(比如错误处理)
#include <libavutil/avutil.h>
// 编码相关的API
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
}

宏定义

#ifdef Q_OS_WIN
    // 格式名称
    #define FMT_NAME "dshow"
    // 设备名称
    #define DEVICE_NAME ""
    // PCM文件名
    #define FILEPATH "F:/"
#else
    #define FMT_NAME "avfoundation"
    #define DEVICE_NAME ":0"
    #define FILEPATH "/Users/muzi/Desktop/out.yuv"
#endif

权限申请

在Mac平台,有2个注意点:

<?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>
        <key>NSMicrophoneUsageDescription</key>
        <string>申请使用您的麦克风权限</string>
</dict>
</plist>
  • 需要在Info.plist中添加摄像头、麦克风的使用说明,申请摄像头、麦克风的使用权限
  • 使用Debug模式运行程序
  • 不然会出现闪退的情况

注册设备

在整个程序的运行过程中,只需要执行1次注册设备的代码。

 // 注册设备
 avdevice_register_all();

获取输入格式对象

// 获取输入格式对象
    AVInputFormat *fmt = av_find_input_format(FMT_NAME);
    if (!fmt) {
        qDebug() << "获取输入格式对象失败" << 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) {
        char errbuf[1024];
        av_strerror(ret, errbuf, sizeof (errbuf));
        qDebug() << "打开设备失败" << errbuf;
        return;
    }

打开输出文件

// 文件名
    QFile file(FILEPATH);

    // 打开文件
    // WriteOnly:只写模式。如果文件不存在,就创建文件;如果文件存在,就会清空文件内容
    if (!file.open(QFile::WriteOnly)) {
        qDebug() << "文件打开失败" ;

        // 关闭设备
        avformat_close_input(&ctx);
        return;
    }

采集视频数据

// 计算一帧的大小
    AVCodecParameters *params = ctx->streams[0]->codecpar;
    AVPixelFormat pixFmt = (AVPixelFormat) params->format;

    int imageSize = av_image_get_buffer_size(pixFmt,
                                             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);

            // 释放资源
            av_packet_unref(pkt);
        } else if (ret == AVERROR(EAGAIN)) { // 资源临时不可用
            continue;
        } else { // 其他错误
            char errbuf[1024];
            av_strerror(ret, errbuf, sizeof (errbuf));
            qDebug() << "av_read_frame error" << errbuf << ret;
            break;
        }

        // 必须要加,释放pkt内部的资源
        av_packet_unref(pkt);
    }

释放资源

// 释放资源
    // 关闭文件
    file.close();

    // 释放资源
    av_packet_free(&pkt);

    // 关闭设备
    avformat_close_input(&ctx);

    qDebug() << this << "正常结束----------";
posted @ 2021-11-11 20:39  木子沉雨  阅读(206)  评论(0编辑  收藏  举报