前言
本篇文章的需求是将相机获取到的图片进行编码,编码成一个视频,耗费了大约一个星期的时间在解决各种问题。这里阐述一下这篇文章所要解决的几个问题:
1、如何将多张图片编码成视频。
2、如何进行定时录制视频。
3、同时开启多线程进行视频录制。
4、对录制文件目录进行管理:每次都检测录制目录大小是否超过指定大小,如果超过,则删除指定大小的时间最早的一些文件。
正文
一、准备工作
1、下载FFmpeg的开发版
1、下载链接: https://ffmpeg.org/download.html
4、由于我是在Win10下,所以选择:
2、使用环境
Win10 + Qt8.0.2(MSVC2019) + FFmpeg 4.4
二、整体流程解析
上面的流程图,基本上就是这次这个项目的整体流程了,上面的 2 3 4点都是在单例层完成的。只有第一点是在FFmpegRecord那一层完成的。
三、单例模式——多线程视频录制
首先,确定目标,我是要“同时录制多个视频”,所以,必须得开多个线程,并且,在这一层,就必须要完成:
1、给上层应用的调用提供一个接口。
2、进行定时关闭录制的操作。
3、对底层FFmpegRecord对象进行管理。
4、对录制文件进行管理——在开启录制的时候,检测当前目录文件的大小。
先给出开录制与关录制中整体的代码,然后再慢慢进行解释吧.
//开启录制的接口
QString CRecordMgr::StartRecord(eRecordType eType, QString sFileName, int iRecordSTime)
{
QMutexLocker oLocker(&m_mutex);
QString sPath = "/ics/recordfile";
quint64 iCountByte = _DetectDiskInfo(sPath);
if (iCountByte > DetectMinMB*1024*1024)
{
_RemoveRecordFile(sPath);
QThread::msleep(1000);
}
if (sFileName.isEmpty())
{
qint64 timestamp = QDateTime::currentDateTime().toSecsSinceEpoch();
sFileName = QString("%1.mp4").arg(timestamp);
}
_PreActionStart();
QString sMissionId = CCommonFunc::CreateUUID();
CFFmpegRecord *pFFmpegRecord = new CFFmpegRecord();
if (pFFmpegRecord)
{
pFFmpegRecord->setObjectName(sMissionId);
m_mapFFmpegRecord.insert(sMissionId, pFFmpegRecord);
m_mapRecordFileName.insert(sMissionId, sFileName);
m_mapRecordType.insert(sMissionId, eType);
pFFmpegRecord->SetMissionId(sMissionId);
pFFmpegRecord->SetRecordType((CFFmpegRecord::eRecordType)eType);
pFFmpegRecord->SetRecordFileName(sFileName);
pFFmpegRecord->Init();
int iTimeId = startTimer((iRecordSTime+1)*1000);//多录制1s,防止最后一帧未录制完整
m_mapTimerId.insert(sMissionId, iTimeId);
m_oConnect = connect(gVideoMgr::instance(), &CVideoMgr::SIGNAL_CommonCameraImage, pFFmpegRecord, &CFFmpegRecord::SLOT_FFmpegImage, Qt::QueuedConnection);
m_mapConnect.insert(sMissionId, m_oConnect);
}
return sMissionId;
}
//使用任务Id对录制任务进行关闭
bool CRecordMgr::StopRecord(CRecordMgr::eRecordType eType)
{
m_setDeleteId.clear();
QMap<QString, eRecordType>::iterator it;
for (it = m_mapRecordType.begin(); it != m_mapRecordType.end(); ++it)
{
QString sId = it.key();
CRecordMgr::eRecordType eRecordType = it.value();
if (eType == eRecordType)
{
CFFmpegRecord *pFFmpegRecord = m_mapFFmpegRecord.value(sId);
if (pFFmpegRecord)
{
pFFmpegRecord->StopRecord();
QEventLoop eventloop;
QTimer::singleShot(500, &eventloop, SLOT(quit()));
eventloop.exec();
QMetaObject::Connection oConnect = m_mapConnect.value(sId);
disconnect(oConnect);
CRecordMgr::eRecordType eRecordType = m_mapRecordType.value(sId);
QString sFileName = m_mapRecordFileName.value(sId);
int iTimerId = m_mapTimerId.value(sId);
killTimer(iTimerId);
if (m_mapFFmpegRecord.size() == 0)
{
_PreActionEnd();
}
pFFmpegRecord->deleteLater();
pFFmpegRecord = nullptr;
emit SIGNAL_RecordTask_Finished(eRecordType, sId, sFileName);
m_mapFFmpegRecord.remove(sId);
m_setDeleteId.insert(sId);