FFMPEG+Qt 实时显示本机USB摄像头1080p画面以及同步录制mp4视频

FFMPEG+Qt 实时显示本机USB摄像头1080p画面以及同步录制mp4视频

文章目录

  • FFMPEG+Qt 实时显示本机USB摄像头1080p画面以及同步录制mp4视频
  • 1、前言
    • 1.1 目标
    • 1.2 一些说明
  • 2、效果
  • 3、代码
    • 3.1 思路
    • 3.2 工程目录
    • 3.3 核心代码
  • 4、全部代码获取


1、前言

  本文通过FFMPEG(7.0.2)与Qt(5.13.2)实现在windows10系统下,实时预览以及录制1080p视频。
  本文程序只对视频数据进行处理,不考虑音频数据。

1.1 目标

  在win10平台,用FFMPEG和Qt实现实时显示USB摄像头画面以及同步录制mp4视频。

1.2 一些说明

  本程序实现USB摄像头数据视频流获取及显示,录制、显示的视频质量与USB相机有关,本人摄像头为1920*1080@30Hz。
  USB相机默认视频流格式会有差别,如果需要特定格式及分辨率的时候,需要手动设置。
  大部分USB摄像头有MJPG和YUV两种格式,为了追求高分辨率,可以将摄像头参数设置为MJPG输入。需要通过以下代码实现。即配置AVDictionary。需要同时配置相机的帧率、分辨率和格式,不能只设置输入格式!要不然不成功。

AVDictionary* options = NULL;
av_dict_set(&options, "input_format", "mjpeg", 0);
av_dict_set(&options, "framerate", "30", 0);
av_dict_set(&options, "video_size", "1920x1080", 0);avformat_open_input(&pFormatCtx_, in_file.c_str(), ifmt, &options);

2、效果

  先看演示效果视频:

FFMPEG+Qt win10 实时预览录制1080p视频

3、代码

  本项目全部代码请到此处获取:https://download.csdn.net/download/wang_chao118/89921908

3.1 思路

  通过Qt进行画面可视化,FFMPEG对USB视频流进行解码、编码以及存储。将FFMPEG的循环操作放到一个子线程中,与现实线程隔离。
  通过Qt的信号槽机制,将FFMPEG循环操作过程中解码出的单帧图像转化成QImage*通过信号传递至主线程进行图像绘制。

thread_ = new std::thread(&CameraThread::Run, this);
QObject::connect(camera_thread_, &CameraThread::frameReady, &w, &Widget::updatePic);

3.2 工程目录

  本项目中将FFMPEG相关上下文的初始化、解码、编码、记录循环功能集成在一个CameraThread类中。
  CameraThread类继承Thread类。Thread类中实现子线程的初始化、开始、停止、运行等基础功能。
在这里插入图片描述

3.3 核心代码

  CameraThread::Start()函数用于初始化FFMPEG的各类上下文,设置解码器、编码器参数等,在Qt界面中只要点击“开始录制”按钮就会调用该函数,点击“停止录制”按钮调用CameraThread::Stop()函数,对上下文进行清理。

int CameraThread::Start()
{start_pts = 0;int ret = 0;/******************************************打开摄像头设备********************************************/const AVInputFormat* m_inputFormat = av_find_input_format("dshow");inputContext = avformat_alloc_context();AVDictionary *options = nullptr;ret = avformat_open_input(&inputContext, url_.c_str(), m_inputFormat, &options);if (ret < 0){qDebug()<<"avformat_open_input failed, ret: "<<ret;return -1;}// 获取摄像头流信息ret = avformat_find_stream_info(inputContext, nullptr);if (ret < 0){qDebug()<<"Could not retrieve input stream information";return -1;}int videoStreamId = -1;videoStreamId = av_find_best_stream(inputContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, NULL);inputStream = inputContext->streams[videoStreamId];codecParameters = inputStream->codecpar;// 获取摄像头的实际分辨率和帧率int actual_width = codecParameters->width;int actual_height = codecParameters->height;AVRational actual_frame_rate = inputStream->r_frame_rate;qDebug()<< "Camera Resolution: " << actual_width << "x" << actual_height;qDebug()<< "Camera Frame Rate: " << actual_frame_rate.num << "/" << actual_frame_rate.den << " fps";/******************************************创建解码器********************************************/// 查找解码器auto codec_id = inputStream->codecpar->codec_id;const AVCodec* decoder = avcodec_find_decoder(codec_id);if (!decoder) {qDebug()<<"Unsupported codec !";return -1;}// 创建解码器上下文decoderContext = avcodec_alloc_context3(decoder);ret = avcodec_parameters_to_context(decoderContext, codecParameters);if (ret<0) {qDebug()<<"Failed to copy codec parameters to decoder context.";return -1;}qDebug() << " output pix_fmt=" << av_get_pix_fmt_name((AVPixelFormat)codecParameters->format) <<" "<< codecParameters->format;ret = avcodec_open2(decoderContext, decoder, nullptr);if (ret<0) {qDebug()<<"Failed to open decoder.";return -1;}/******************************************创建编码器********************************************/// 查找 H.264 编码器const AVCodec* pEncoderH264 = avcodec_find_encoder(AV_CODEC_ID_H264);if (pEncoderH264 == NULL) {qDebug() << "Unsupported encodec.";return -1;}//视频编码器上下文encoderContext = avcodec_alloc_context3(pEncoderH264);encoderContext->time_base.num = inputStream->time_base.num;encoderContext->time_base.den = inputStream->time_base.den;encoderContext->has_b_frames = 0;
//    encoderContext->gop_size = 50;encoderContext->codec_id = pEncoderH264->id;encoderContext->pix_fmt = (AVPixelFormat)inputStream->codecpar->format;qDebug()<<"111111111111111111111111111111: "<<encoderContext->pix_fmt;encoderContext->width = inputStream->codecpar->width;encoderContext->height = inputStream->codecpar->height;
//    encoderContext->bit_rate = 0;encoderContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;encoderContext->framerate = inputStream->avg_frame_rate;//编码帧率按照采集帧率来
//    encoderContext->bit_rate = 5000000;               //数值越小文件越小//编码器不用等待缓冲区填满,接收到数据即开始编码av_opt_set(encoderContext->priv_data, "tune", "zerolatency", 0);
//    av_opt_set(encoderContext->priv_data, "crf", "23", 0);               //数值越大越模糊,存储文件越小qDebug() << "encoder h246 information:";qDebug() << " encode fps=" << (encoderContext->framerate.num / encoderContext->framerate.den) ;qDebug() << " w=" << encoderContext->width << ", h=" << encoderContext->height;qDebug() << " input pix format=" << av_get_pix_fmt_name(encoderContext->pix_fmt) <<" "<< encoderContext->pix_fmt ;ret = avcodec_open2(encoderContext, pEncoderH264, NULL);if (ret < 0) {qDebug() <<"avcodec_open2";return -1;}/******************************************创建视频输出文件********************************************/const AVOutputFormat* outputFormat = av_guess_format("mp4", NULL, NULL);outputContext = avformat_alloc_context();outputContext->oformat = const_cast<AVOutputFormat*>(outputFormat);// 创建输出流outputStream = avformat_new_stream(outputContext, encoderContext->codec);if (!outputStream){qDebug() <<"Failed to create output stream";return -1;}avcodec_parameters_from_context(outputStream->codecpar, encoderContext);outputStream->time_base = av_inv_q(inputStream->r_frame_rate);// 创建输出 MP4 文件mp4_count++;m_mp4_file_name = m_mp4_file_name_header + std::to_string(mp4_count) + ".mp4";ret = avio_open(&outputContext->pb, m_mp4_file_name.c_str(), AVIO_FLAG_WRITE);if(ret <0){qDebug() <<"Failed to create output file.";return -1;}ret = avformat_write_header(outputContext, NULL);if(ret <0){qDebug() <<"Failed to write header";return -1;}/******************************************开线程****************************************************/thread_ = new std::thread(&CameraThread::Run, this);if (!thread_){qDebug()<<"new std::thread(&CameraThread::Run, this) failed";return -1;}return 0;
}

  CameraThread::Run()函数是循环录制过程的执行函数,主要是从FFMPEG上下文中取出视频数据包AVPacket,解码至视频帧AVFrame,再按相应格式(H264)编码成AVPacket,然后再保存至文件中。

void CameraThread::Run()
{qDebug() << "Run into CameraThread::Run()!";int ret = 0;SwsContext *sws_ctx = nullptr;while (!abort_){// 读取视频帧ret = av_read_frame(inputContext, captured_packet);if (ret < 0){qDebug() << "av_read_frame error";continue; // 如果读取失败,跳过到下一帧}// start capture pts from 0// 初始化 start_ptsif (start_pts == 0){start_pts = captured_packet->pts; // 仅在第一次读取时设置}captured_packet->pts -= start_pts;qDebug()<<"captured_packet->pts: "<<captured_packet->pts;av_log(nullptr, AV_LOG_INFO, "packet size is %d\n", captured_packet->size);// 解码 MJPEG 数据ret = avcodec_send_packet(decoderContext, captured_packet);if (ret < 0){qDebug() << "Error sending packet for decoding.";av_packet_unref(captured_packet); // 释放 packet 内存continue; // 继续读取下一个包}auto decoded_frame = av_frame_alloc();ret = avcodec_receive_frame(decoderContext, decoded_frame); // decoded_frame 自带引用计数if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){av_frame_free(&decoded_frame); // 确保在这些情况下也释放内存av_packet_unref(captured_packet); // 释放 packet 内存continue;}else if (ret < 0){qDebug() << "avcodec_receive_frame failed!";av_frame_free(&decoded_frame);av_packet_unref(captured_packet); // 释放 packet 内存break;}// 编码 H264 数据ret = avcodec_send_frame(encoderContext, decoded_frame);if (ret < 0){qDebug() << "Error sending frame for encoding.";av_frame_free(&decoded_frame);av_packet_unref(captured_packet); // 释放 packet 内存continue;}/******************************************发送QImage***********************************************/// 转换解码后的帧为 QImageif (!sws_ctx){sws_ctx = sws_getContext(decoded_frame->width, decoded_frame->height,(AVPixelFormat)decoded_frame->format,decoded_frame->width, decoded_frame->height,AV_PIX_FMT_RGB32, SWS_BILINEAR, nullptr, nullptr, nullptr);}QImage image(decoded_frame->width, decoded_frame->height, QImage::Format_RGB32);uint8_t *dst[4] = { image.bits(), nullptr, nullptr, nullptr };int dstStride[4] = { image.bytesPerLine(), 0, 0, 0 };sws_scale(sws_ctx, decoded_frame->data, decoded_frame->linesize, 0,decoded_frame->height, dst, dstStride);// 发射信号emit frameReady(image);/******************************************存视频****************************************************/ret = avcodec_receive_packet(encoderContext, h264_pkt);if (ret < 0){qDebug() << "avcodec_receive_packet error.";av_frame_free(&decoded_frame);av_packet_unref(captured_packet); // 释放 packet 内存continue;}ret = av_write_frame(outputContext, h264_pkt);if (ret < 0){qDebug() << "write error.";}// 释放 decoded_frame 和 packet 内存av_frame_free(&decoded_frame);av_packet_unref(captured_packet); // 释放 packet 内存}
}

4、全部代码获取

  本项目全部代码请到此处获取:https://download.csdn.net/download/wang_chao118/89921908

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/456851.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

YOLO系列入门:1、YOLO V11环境搭建

YOLO了解 yolo检测原理 yolo是目标检测模型&#xff0c;目标检测包含物体分类、位置预测两个内容。目前yolo的开发公司官网为&#xff1a;https://docs.ultralytics.com/zh截止到目前2024年10月&#xff0c;最新的是yolo11。关于YOLO的介绍可以参考这篇文章&#xff1a;https…

Python+Django+VUE 搭建深度学习训练界面 (持续ing)

PythonDjangoVUE 搭建深度学习训练界面 &#xff08;持续ing&#xff09; 环境说明 Pycharm 专业版2024.1.4&#xff0c;社区版不支持网页开发 下载链接&#xff1a;https://www.jetbrains.com/pycharm/download/other.html 参考链接&#xff1a;https://www.quanxiaoha.co…

es实现桶聚合

目录 聚合 聚合的分类 DSL实现桶聚合 dsl语句 结果 聚合结果排序 限定聚合范围 总结 聚合必须的三要素&#xff1a; 聚合可配置的属性 DSL实现metric聚合 例如&#xff1a;我们需要获取每个品牌的用户评分的min,max,avg等值 只求socre的max 利用RestHighLevelClien…

BIO,NIO,直接内存,零拷贝

前置知识 什么是Socket&#xff1f; Socket是应用层与TCP/IP协议族通信的中间软件抽象层&#xff0c;它是一组接口&#xff0c;一般由操作系统提供。在设计模式中&#xff0c;Socket其实就是一个门面模式&#xff0c;它把复杂的TCP/IP协议处理和通信缓存管理等等都隐藏在Sock…

vue3使用i18n做国际化多语言,实现常量跟随语言切换翻译

因为我有一个常量的配置文件在项目中&#xff0c;而且有中文内容&#xff0c;我想在切换语言的时候&#xff0c;跟着这个翻译也实时切换&#xff0c;就可以使用computed计算属性实现。 把name改成下面的样子&#xff1a; name: computed(() > t(pad.regularMode)), 就可以…

分享一款录屏、直播软件

光音录屏 光音录屏 是新一代的录屏工具&#xff0c;跟传统录屏工具相比&#xff0c;它不仅可以录制屏幕&#xff0c;还可以同时录制「人像 屏幕」&#xff0c;此外它还提供了美颜、虚拟背景、绿幕抠像、图片、文本编辑、字幕、白板等更多高级功能。你可以将录制好的视频&…

ue5实现数字滚动增长

方法1 https://www.bilibili.com/video/BV1h14y197D1/?spm_id_from333.999.0.0 b站教程 重写loop节点 方法二 写在eventtick里

ffmpeg视频滤镜: 色温- colortemperature

滤镜简述 colortemperature 官网链接 》 FFmpeg Filters Documentation 这个滤镜可以调节图片的色温&#xff0c;色温值越大显得越冷&#xff0c;可以参考一下下图&#xff1a; 咱们装修的时候可能会用到&#xff0c;比如选择灯还有地板的颜色的时候&#xff0c;选暖色调还是…

多厂商的实现不同vlan间通信

Cisco单臂路由 Cisco路由器配置 -交换机配置 -pc配置 华三的单臂路由 -路由器配置 -华三的接口默认是打开的 -pc配置及ping的结果 -注意不要忘记配置默认网关 Cisco-SVI -交换机的配置 -创建vlan -> 设置物理接口对应的Acess或Trunk -> 进入vlan接口&#xff0c;打开接…

【纯血鸿蒙】HarmonyOS和OpenHarmony 的区别

一、开源鸿蒙&#xff08;Open Harmony&#xff09; 鸿蒙系统愿来的设计初衷&#xff0c;就是让所有设备都可以运行一个系统&#xff0c;但是每个设备的运算能力和功能都不同&#xff0c;所以内核的设计上&#xff0c;采用了微内核的设计&#xff0c;除了最基础的功能放在内核…

mfc之tab标签控件的使用--附TabSheet源码

TabSheet源码 TabSheet.h #if !defined(AFX_TABSHEET_H__42EE262D_D15F_46D5_8F26_28FD049E99F4__INCLUDED_) #define AFX_TABSHEET_H__42EE262D_D15F_46D5_8F26_28FD049E99F4__INCLUDED_#if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // TabSheet.h : …

C++面向对象编程学习

C面向对象编程学习 前言一、C面向对象编程二、知识点学习1. 定义一个类1.1 使用struct定义1.2 使用class定义1.3 struct和class的区别 2. 类的定义方式2.1 单文件定义&#xff08;Inline Definition&#xff09;2.2 分离定义&#xff08;Separate Definition&#xff09;2.3 头…

[bug] vllm 0.6.1 RuntimeError: operator torchvision::nms does not exist

[bug] vllm 0.6.1 RuntimeError: operator torchvision::nms does not exist 环境 python 3.10 torch 2.4.0cu118 torchvision 0.19.0cu118 vllm 0.6.1.post2cu118问题详情 if torch._C._d…

【华为路由】OSPF多区域配置

网络拓扑 设备接口地址 设备 端口 IP地址 RTA Loopback 0 1.1.1.1/32 G0/0/0 10.1.1.1/24 RTB Loopback 0 2.2.2.2/32 G0/0/0 10.1.1.2/24 G0/0/1 10.1.2.1/24 RTC Loopback 0 3.3.3.3/32 G0/0/0 10.1.2.2/24 G0/0/1 10.1.3.1/24 RTD Loopback 0 4.4.4…

【Jenkins】解决在Jenkins Agent节点容器内无法访问物理机的docker和docker compose的问题

解决在Jenkins Agent节点容器内无法访问物理机的docker和docker compose的问题 1. 确定物理机docker和docker compose已经安装2. 编写Jenkins Agent结点docker-compose.yaml配置文件3. 修改docker运行时文件权限4. 启动容器并验证 最近接触到一个发布产物是一个 docker镜像的项…

【K8s】Kubernetes 证书管理工具 Cert-Manager

本文内容均来自个人笔记并重新梳理&#xff0c;如有错误欢迎指正&#xff01; 如果对您有帮助&#xff0c;烦请点赞、关注、转发、订阅专栏&#xff01; 专栏订阅入口 | 精选文章 | Kubernetes | Docker | Linux | 羊毛资源 | 工具推荐 | 往期精彩文章 【Docker】&#xff08;全…

Github优质项目推荐(第八期)

文章目录 Github优质项目推荐 - 第八期一、【manim】&#xff0c;66.5k stars - 创建数学动画的 Python 框架二、【siyuan】&#xff0c;19.5k stars - 个人知识管理软件三、 【GetQzonehistory】&#xff0c;1.3k stars - 获取QQ空间发布的历史说说四、【SecLists】&#xff0…

【Linux系统编程】冯诺依曼体系结构与操作系统

目录 1、冯诺依曼体系结构 1.1 冯诺依曼体系结构的组成 1.2 程序运行时必须要加载到内存 1.3 数据通信 1.4 为什么要有内存 2、操作系统 2.1 概念 2.2 设计OS的目的 2.3 如何理解管理 2.4 系统调用和库函数的概念 1、冯诺依曼体系结构 我们常见的计算机&#xff0c;如…

transforms的使用

示例代码 from PIL import Image from torch.utils.tensorboard import SummaryWriter from torchvision import transforms#打开该图片 img_path"hymenoptera_data/val/bees/10870992_eebeeb3a12.jpg" imgImage.open(img_path) writerSummaryWriter("logs&quo…

CSS行块标签的显示方式

块级元素 标签&#xff1a;h1-h6&#xff0c;p,div,ul,ol,li,dd,dt 特点&#xff1a; &#xff08;1&#xff09;如果块级元素不设置默认宽度&#xff0c;那么该元素的宽度等于其父元素的宽度。 &#xff08;2&#xff09;所有的块级元素独占一行显示. &#xff08;3&#xff…