【秒懂音视频开发】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);