当前位置: 首页 > news >正文

RK3399平台ffmpeg-VPU硬编码录制USB摄像头视频、H264或MJPEG编码

文章目录

  • 1 前言
  • 2 项目内容详细说明
    • 2.0 功能
    • 2.1 工程文件夹说明
  • 3 代码
    • 3.1 CameraThread类
    • 3.1 CameraThreadImpl类
  • 4 资源下载


1 前言

  在某项目中需要在RK3399平台实现USB摄像头画面的实时预览、视频录制、拍照存储等功能。
  先来看需要实现的最终效果。
  

ffmpeg USB摄像头 H264编码录视频拍照


  本项目使用了搭载了RK3399芯片的嵌入式平台,操作系统Debain10,调用ffmpeg-rkmpp库进行H264硬编码。
  本项目需要外接一个支持 2592x1944,1080p,720p,480p的USB摄像头。

2 项目内容详细说明

2.0 功能

  (1)实时预览;
  (2)摄像头分辨率设置,可设置为480p,720p,1080p;
  (3)编码格式设置,可按照H264格式编码,或直接存MJPEG;
  (4)存储文件格式,可设置为.avi或者.mp4格式;
  (5)录制视频时,显示当前录制时间以及“REC”闪烁字样;
  (6)拍照功能,拍摄照片分辨率为2592x1944(500万像素);
  (7)通过F3、F4键移动光标,通过 ↑ 按键实现“开始、停止”录视频,通过 → 按键实现“确定”,通过 ← 按键实现“拍照”。

2.1 工程文件夹说明

  工程文件夹结构如下:
在这里插入图片描述
  在cameraModule文件夹内的是视频相关内容,可单独编译成库,在cameravideowidget.cpp中实现代码的调用。

3 代码

3.1 CameraThread类

  CameraThread类为视频录制、拍照实现的关键类,其接口如下表所示。

成员类型名称描述
构造函数CameraThread()初始化 CameraThread 对象
析构函数~CameraThread()释放 CameraThread 对象的资源。
函数int CheckUSBCamera()检查 USB 相机是否正常工作。返回值为 0 表示正常,其他值表示错误代码。
void setParamter(int, int, int)设置视频参数(分辨率、编码格式、封装格式)。必须在 OpenCamera() 之前调用。
int OpenCamera()开启相机。返回值为 0 表示成功,其他值表示错误代码。
int CloseCamera()关闭相机。返回值为 0 表示成功,其他值表示错误代码。
int TakePhoto(QString name)拍照接口,调用该函数使用相机拍摄一张照片并保存至指定路径。返回值为 0 表示成功,其他值表示错误代码。
int DeletPhoto()删除照片接口,调用该函数删除当前拍摄的照片。返回值为 0 表示成功,其他值表示错误代码。
int StartTakeVideo(QString name)开始视频录制并保存到指定路径。返回值为 0 表示成功,其他值表示错误代码。
int StopTakeVideo()停止当前视频录制操作,并完成所有未完成任务。返回值为 0 表示成功,其他值表示错误代码。
信号void sendPhoto(QImage img)当拍摄照片后发出此信号,传递一个 QImage 类型的图像用于UI绘制。
信号void sendRealTimePhoto(QImage img)发送实时图像帧信号,用于传递实时预览图像。
公有变量bool isOpen标志相机是否已打开。
公有变量bool isRecording标志是否正在录制视频。
公有变量std::unique_ptr impl使用 PIMPL 设计模式隐藏实现细节的实现类指针。

  camerathread.cpp实现如下所示。

#include "camerathread.h"
#include "camerathreadimpl.h"CameraThread::CameraThread() :impl(std::make_unique<CameraThreadImpl>())
{connect(impl.get(), &CameraThreadImpl::sendPhoto, this, &CameraThread::sendPhoto);connect(impl.get(), &CameraThreadImpl::sendRealTimePhoto, this, &CameraThread::sendRealTimePhoto);qDebug()<<"libcamerathread.so_version_20250731_";
}CameraThread::~CameraThread()
{}int CameraThread::CheckUSBCamera()
{int ret = 0;qDebug()<<"CheckUSBCamera!";if(isOpen==true){ret = 0;}else{ret = impl->CheckUSBCamera();}return ret;
}void CameraThread::setParamter(int resolution, int encodeformat, int containerformat)
{qDebug()<<"resolution: "<<resolution<<" encodeformat: "<<encodeformat<<"containerformat "<<containerformat;impl->setParamter(resolution, encodeformat, containerformat);
}int CameraThread::OpenCamera()
{int ret = 0;qDebug()<<"Camera Open!";ret=impl->OpenCamera();if(!ret){isOpen = true;}qDebug()<<"OpenCamera isOpen: "<<isOpen;return ret;
}int CameraThread::CloseCamera()
{int ret = 0;qDebug()<<"Camera Close!";qDebug()<<"CloseCamera isOpen: "<<isOpen;if(isOpen == true){ret = impl->CloseCamera();}if(!ret){isOpen = false;}ret = StopTakeVideo();return ret;
}int CameraThread::TakePhoto(QString name)
{int ret = 0;ret=impl->TakePhoto(name);return ret;
}int CameraThread::DeletPhoto()
{int ret = 0;qDebug()<<"Delete Photo";ret=impl->DeletPhoto();return ret;
}int CameraThread::StartTakeVideo(QString name)
{int ret = 0;qDebug()<<"StartTakeVideo: "<<name;ret=impl->StartTakeVideo(name);isRecording = true;return ret;
}int CameraThread::StopTakeVideo()
{int ret = 0;if(isRecording){ret=impl->StopTakeVideo();}isRecording = false;return ret;
}

3.1 CameraThreadImpl类

  CameraThread类中使用CameraThreadImpl类作为内部实现类,可使得CameraThread接口不暴露具体实现方法。

camerathreadimpl.cpp具体实现如下所示。

#include "camerathreadimpl.h"CameraThreadImpl::CameraThreadImpl()
{initParam();/***************************分配缓冲队列**************************************/video_packet_queue = new AVPacketQueue();video_packet_queue_output = new AVPacketQueue();/***************************分配缓冲队列**************************************/video_frame_queue = new AVFrameQueue();fmtConvert_queue_output = new AVFrameQueue();}CameraThreadImpl::~CameraThreadImpl()
{delete demux_thread;delete decode_thread;delete save_thread;delete video_packet_queue_output;delete video_packet_queue;}void CameraThreadImpl::initParam()
{// 读取INI配置文件QString configFilePath = QCoreApplication::applicationDirPath() + "/config.ini";QSettings settings(configFilePath, QSettings::IniFormat);/************************确认配置文件路径以及是否存在配置文件***************/
//    qDebug()<<"configFilePath: "<<configFilePath;
//    qDebug()<<"File Exists: " << QFile::exists(configFilePath);                         // 确保文件存在
//    qDebug()<<"QSettings file path: " << settings.fileName();/*********************************************************************/// 设置默认值QString defaultCameraUrl;QString defaultSavePath;
#ifdef Q_OS_WIN
//    defaultCameraUrl = "video=Rmoncam FHD 1080P";       // Windows 平台默认相机名称defaultCameraUrl = "video=USB Video Device";       // Windows 平台默认相机名称defaultSavePath = "C:\\Users\\chao8\\Desktop\\";    // Windows 默认保存路径
#else
//    defaultCameraUrl = "/dev/video10";                  // Forlinx 平台默认相机名称
//    defaultSavePath = "/home/forlinx/Desktop/";         // Forlinx 默认保存路径defaultCameraUrl = "/dev/video0";                  // Debain11平台默认相机名称defaultSavePath = "/mnt/ums/data/";                     // Debain11 默认保存路径
#endifdouble defaultRemainingSpace = 0.8;int defaultRecordIntervalMinutes = 1000;QString defaultResolution = "1920x1080";QString defaultBitrate = "40M";// 从INI文件读取配置值,如果没有配置则使用默认值savePath_ = settings.value("Parameters/savePath", defaultSavePath).toString().toStdString();url_ = settings.value("Parameters/url", defaultCameraUrl).toString().toStdString();remainingSpace = settings.value("Parameters/remainingStorageSpace", defaultRemainingSpace).toDouble();recordIntervalMinutes = settings.value("Parameters/recordIntervalMinutes", defaultRecordIntervalMinutes).toInt();Resolution = settings.value("Parameters/resolution", defaultResolution).toString();Bitrate = settings.value("Parameters/bitrate", defaultBitrate).toString();// 设置定时器时间,定时处理视频信息,每隔一段时间自动记录视频timer = new QTimer(this);timer->setInterval(recordIntervalMinutes * 60 * 1000);connect(timer, &QTimer::timeout, this, &CameraThreadImpl::handleVideoData);/*****************************打印各参数配置数值*****************************/
//    qDebug()<<"savePath_: "<<QString::fromStdString(savePath_);
//    qDebug()<<"url_: "<<QString::fromStdString(url_);
//    qDebug()<<"remainingSpace: "<<remainingSpace;
//    qDebug()<<"Resolution: "<<Resolution;
//    qDebug()<<"Bitrate: "<<Bitrate;
//    qDebug()<<"recordIntervalMinutes: "<<recordIntervalMinutes;/**************************************************************************/}int CameraThreadImpl::CheckUSBCamera()
{avdevice_register_all();std::string url_str = url_;AVFormatContext *ifmt_ctx = avformat_alloc_context();char err2str[256] = {0};AVDictionary *options = nullptr;av_dict_set(&options, "fflags", "nobuffer", 0);av_dict_set(&options, "probesize", "4096", 0);av_dict_set(&options, "framerate", "30", 0);av_dict_set(&options, "video_size", "1920x1080", 0);av_dict_set(&options, "input_format", "mjpeg", 0);#ifdef Q_OS_WINconst AVInputFormat *m_inputFormat = av_find_input_format("dshow");
#elseconst AVInputFormat *m_inputFormat = av_find_input_format("v4l2");
#endifint ret = avformat_open_input(&ifmt_ctx, url_str.c_str(), m_inputFormat, &options);//    qDebug()<<"ret: "<<ret;if (ret < 0) {av_strerror(ret, err2str, sizeof(err2str));qDebug("avformat_open_input failed, ret:%d, err2str:%s", ret, err2str);return ret;}ret = avformat_find_stream_info(ifmt_ctx, nullptr);if (ret < 0) {av_strerror(ret, err2str, sizeof(err2str));qDebug("avformat_find_stream_info failed, ret:%d, err2str:%s", ret, err2str);avformat_close_input(&ifmt_ctx);return ret;}avformat_close_input(&ifmt_ctx);qDebug() << "USB camera check OK!";return ret;
}void CameraThreadImpl::ReleaseQueue()
{video_packet_queue_output->release();video_packet_queue->release();video_frame_queue->release();fmtConvert_queue_output->release();
}void CameraThreadImpl::setParamter(int resolution, int encodeformat, int containerformat)
{if(resolution==0){Resolution = "1920x1080";}else if(resolution==1){Resolution = "1280x720";}else if(resolution==2){Resolution = "640x480";}if(encodeformat==0){EncodeFormat = "MJPEG";}else if(encodeformat==1){EncodeFormat = "H264";}if(containerformat==0){VideoContainerFormat = "avi";}else if(containerformat==1){VideoContainerFormat = "mp4";}
}void CameraThreadImpl::handleVideoData()
{qDebug()<<"handleVideoData! ";demux_thread->start_pts = 0;save_thread->Stop();ReleaseQueue();save_thread->Init();save_thread->Start();}int CameraThreadImpl::OpenCamera()
{int ret = 0;/*************************************************************************************/if(EncodeFormat == "MJPEG"){demux_thread = new DemuxThread(video_packet_queue, &url_);demux_thread->setResolution(Resolution);ret = demux_thread->Start();if(ret<0){isCameraValid = false;}else{isCameraValid = true;}/**************************************************************************************/decode_thread = new DecodeThread(video_packet_queue, video_packet_queue_output, &savePath_);decode_thread->setRemainingSpace(remainingSpace);ret = decode_thread->Init(demux_thread->VideoCodecParameters());ret = decode_thread->Start();}if(EncodeFormat == "H264"){demux_thread = new DemuxThread(video_packet_queue, &url_);demux_thread->setResolution(Resolution);ret = demux_thread->Start();if(ret<0){isCameraValid = false;}else{isCameraValid = true;}/**************************************************************************************/decode_thread = new DecodeThread(video_packet_queue, video_frame_queue, &savePath_);decode_thread->setRemainingSpace(remainingSpace);ret = decode_thread->Init(demux_thread->VideoCodecParameters());ret = decode_thread->Start();}/**************************************************************************************/connect(decode_thread, &DecodeThread::frameReady, this, &CameraThreadImpl::sendPhoto);connect(decode_thread, &DecodeThread::realTimeframeReady, this, &CameraThreadImpl::sendRealTimePhoto);connect(decode_thread, &DecodeThread::frameReady, this, &CameraThreadImpl::CloseCamera);connect(decode_thread, &DecodeThread::frameReady, this, &CameraThreadImpl::setisTakePhoto);return ret;
}int CameraThreadImpl::StartTakeVideo(QString name)
{int ret = 0;std::string savefilename = name.toStdString();ReleaseQueue();CloseCamera();OpenCamera();/**************************************************************************************/if(isCameraValid){if(EncodeFormat == "MJPEG"){save_thread = new SaveThread(video_packet_queue_output, demux_thread->inputStream, &savefilename);save_thread->setRemainingSpace(remainingSpace);save_thread->setVideoContainerFormat(VideoContainerFormat);ret = save_thread->Init();ReleaseQueue();demux_thread->start_pts = 0;decode_thread->isSaveFile = true;ret = save_thread->Start();}if(EncodeFormat == "H264"){/************************************************************************/fmtConvert_thread = new FormatConvertThread(video_frame_queue, fmtConvert_queue_output);/*********************************************************************/encode_thread = new EncodeThread(fmtConvert_queue_output, video_packet_queue_output, demux_thread->inputStream);encode_thread->setBitrate(Bitrate);encode_thread->isFIlter = isFilterOn;encode_thread->EncodeFormat = "H264";ret = encode_thread->Init();ret = fmtConvert_thread->Start();ret = encode_thread->Start();save_thread = new SaveThread(video_packet_queue_output, demux_thread->inputStream, &savefilename, encode_thread->encoderContext);save_thread->setRemainingSpace(remainingSpace);save_thread->setVideoContainerFormat(VideoContainerFormat);ret = save_thread->Init();ReleaseQueue();demux_thread->start_pts = 0;decode_thread->isSaveFile = true;ret = save_thread->Start();}timer->start();}else{qDebug()<<"Camera is not Valid! Cannot video!";}return ret;
}int CameraThreadImpl::StopTakeVideo()
{if(isCameraValid){if(save_thread!=nullptr){save_thread->Stop();save_thread = nullptr;decode_thread->isSaveFile = false;if (fmtConvert_thread) fmtConvert_thread->Stop();if (encode_thread) encode_thread->Stop();ReleaseQueue();timer->stop();qDebug()<<"StopTakeVideo!";}else{qDebug()<<"No save_thread!";}}else{qDebug()<<"Camera is not Valid! Cannot video!";}return 0;
}int CameraThreadImpl::CloseCamera()
{StopTakeVideo();if(isCameraValid){decode_thread->Stop();demux_thread->Stop();isCameraValid = false;}ReleaseQueue();      //清空缓冲队列里的东西,每次关闭相机的时候,必须要有!return 0;
}void CameraThreadImpl::setisTakePhoto()
{isTakePhoto = false;
}int CameraThreadImpl::TakePhoto(QString name)
{qDebug()<<"isTakePhoto: "<<isTakePhoto;if(!isTakePhoto){qDebug()<<"In TakePhoto!!!!!! ";isTakePhoto = true;CloseCamera();int ret = 0;demux_thread = new DemuxThread(video_packet_queue, &url_);demux_thread->setResolution("2592x1944");ret = demux_thread->Start();if(ret<0){isCameraValid = false;}else{isCameraValid = true;}/**************************************************************************************/decode_thread = new DecodeThread(video_packet_queue, video_packet_queue_output, &savePath_);decode_thread->setRemainingSpace(remainingSpace);ret = decode_thread->Init(demux_thread->VideoCodecParameters());ret = decode_thread->Start();connect(decode_thread, &DecodeThread::frameReady, this, &CameraThreadImpl::sendPhoto);connect(decode_thread, &DecodeThread::frameReady, this, &CameraThreadImpl::CloseCamera);connect(decode_thread, &DecodeThread::frameReady, this, &CameraThreadImpl::setisTakePhoto);if (decode_thread){decode_thread->takePhoto(name);}else{return -1;}return ret;}else{return 0;}}int CameraThreadImpl::DeletPhoto()
{decode_thread->DeletPhoto();return 0;
}

4 资源下载

本案例中涉及到的所有代码请到此处下载 https://download.csdn.net/download/wang_chao118/91923444。


文章转载自:

http://io4Xcgt5.qftzk.cn
http://2xRtvES4.qftzk.cn
http://3S7CSIWj.qftzk.cn
http://BJmDd3kd.qftzk.cn
http://p7BrWlTt.qftzk.cn
http://yd9xlmQ9.qftzk.cn
http://QVrStDkZ.qftzk.cn
http://VGDnsJUD.qftzk.cn
http://QgRcOVgv.qftzk.cn
http://wP6qZyBG.qftzk.cn
http://6oeHvOL9.qftzk.cn
http://qUGqEPhb.qftzk.cn
http://GJwVAN4i.qftzk.cn
http://kZAulxtq.qftzk.cn
http://liBByrLc.qftzk.cn
http://QCAVz6kT.qftzk.cn
http://rXff03k2.qftzk.cn
http://pATuj34o.qftzk.cn
http://vIZv51vW.qftzk.cn
http://ijRkBB4y.qftzk.cn
http://pPRBMvtx.qftzk.cn
http://jwK36RDu.qftzk.cn
http://tImWOHmw.qftzk.cn
http://xFjfrfDY.qftzk.cn
http://1rrnApMb.qftzk.cn
http://idRPDj3N.qftzk.cn
http://AsNCwZyI.qftzk.cn
http://hCC8WNjS.qftzk.cn
http://C1cOt88I.qftzk.cn
http://v5KR8Wzx.qftzk.cn
http://www.dtcms.com/a/377941.html

相关文章:

  • Android 编译 ffmpeg7.1.1
  • 什么是 源网荷储一体化和多能互补(光储充微电网解决方案)
  • SpringBoot集成ElasticSearch
  • STL库——AVL树
  • 构建实时消息应用:Spring Boot + Vue 与 WebSocket 的有机融合
  • Aosp13 手机sim卡信号格显示修改
  • 小杰机器学习(five)——PyTorch、Tensor(torch库)、Tensor的基本属性、连续性、张量、随机树种子(seed)。
  • ARM 架构的异常模型(Exception Model)
  • 深度学习——基于 PyTorch 的 CBOW 模型实现自然语言处理
  • Spring Cloud Alibaba快速入门03-OpenFeign进阶用法
  • 【PyTorch】多对象分割
  • npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚
  • NodeJS 8 ,从 0 到 1:npm 包发布与更新全流程指南( 含多场景适配与踩坑总结 )
  • Debian 系统上安装与配置 MediaMTX
  • 【PyTorch训练】准确率计算(代码片段拆解)
  • 【Linux】线程池——详细讲解
  • Linux epoll 机制的核心控制函数——`epoll_ctl`
  • 粒子群优化(PSO)算法详解:从鸟群行为到强大优化工具
  • 从两分钟到毫秒级:一次真实看板接口性能优化实战(已上线)
  • Java入门级教程17——利用Java SPI机制制作验证码、利用Java RMI机制实现分布式登录验证系统
  • 【Redis】常用数据结构之List篇:从常用命令到典型使用场景
  • 掌握单元测试的利器:JUnit 注解从入门到精通
  • 【Vue2手录05】响应式原理与双向绑定 v-model
  • spring项目部署后为什么会生成 logback-spring.xml文件
  • Java 日期字符串万能解析工具类(支持多种日期格式智能转换)
  • 在VS2022的WPF仿真,为什么在XAML实时预览点击 ce.xaml页面控件,却不会自动跳转到具体代码,这样不方便我修改代码,
  • 【数组】区间和
  • Qt 基础编程核心知识点全解析:含 Hello World 实现、对象树、坐标系及开发工具使用
  • 解决推理能力瓶颈,用因果推理提升LLM智能决策
  • 【大前端】常用 Android 工具类整理