Ubuntu下使用ffmpeg调用设备摄像头采集视频,保存yuv文件并播放
打开视频设备
AVFormatContext* open_dev()
{
// 获取采集格式
AVInputFormat *inputFmt = av_find_input_format("video4linux2"); // 视频
int ret = 0;
AVFormatContext *fmt_ctx = NULL;
char *deviceName = "/dev/video0"; // 视频设备
AVDictionary *options = NULL;
av_dict_set(&options, "video_size", "640x480", 0); // 为打开视频设备设置参数
// av_dict_set(&options, "pixel_format", "yuyv422",0);
// 打开设备
ret = avformat_open_input(&fmt_ctx, deviceName, inputFmt, &options);
char errors[1024] = {0};
if (ret < 0)
{
av_strerror(ret, errors, 1024);
printf("Failed to open audio device, [%d]%s\n", ret, errors);
return NULL;
}
return fmt_ctx;
}
录制视频并保存文件
void MainWindow::rec_video()
{
AVFormatContext *fmt_ctx = NULL;
AVPacket packet;
av_init_packet(&packet);
int ret = 0;
// 设置日志级别
av_log_set_level(AV_LOG_DEBUG);
// 注册设备
avdevice_register_all();
// 创建文件
char *outPath = "/opt/document/vedio.yuv"; // 保存视频文件
FILE *outFile = fopen(outPath, "wb+");
if (!outFile)
{
goto __ERROR;
}
// 打开设备
fmt_ctx = open_dev();
if (!fmt_ctx)
{
goto __ERROR;
}
while ((ret = av_read_frame(fmt_ctx, &packet) == 0) && m_status)
{
av_log(NULL, AV_LOG_INFO, "Packet size: %d(%p)\n",
packet.size, packet.data);
// 写入文件
fwrite(packet.data, 1, packet.size, outFile);
fflush(outFile);
// 释放packet空间
av_packet_unref(&packet);
}
__ERROR:
// 关闭文件
if (outFile)
{
fclose(outFile);
}
// 关闭设备,释放上下文空间
if (fmt_ctx)
{
avformat_close_input(&fmt_ctx);
}
av_log(NULL, AV_LOG_DEBUG, "Finish!\n");
}
下图为获取到的视频数据包大小:
av_read_frame阻塞问题:
采集视频时遇到了一个问题,就是程序执行到**while ((ret = av_read_frame(fmt_ctx, &packet) == 0) && m_status)**语句时会阻塞,程序一直卡在这个地方,没有返回,输出区没有packet大小,保存的文件大小为0字节。
因为我在虚拟机中连接了主机的摄像头,打开设备时摄像头也点亮了,所以我一直以为是代码有问题,尝试设置了回调、延时等等方法,都没有效果。最后我使用cheese调用摄像头,发现摄像头采集不到画面,我才发现是虚拟机设置的问题。
可以根据下拉列表具体内容选择,我这里改成了USB3.1。
然后再执行代码,很流畅的采集到了视频。
使用ffplay播放采集到的视频也正常。
ffplay -pix_fmt yuyv422 -s 640x480 vedio.yuv
播放参数设置可以根据实际情况修改。