今天解决录像相关事宜。
需求
首先来说一下使用需求,在使用过程中,有以下几种状态:
- 视频预览状态:使用前,用户可以进行录像工作、播放视频文件工作
- 正常录像状态:录像过程中,用户可以暂停录像、停止录像
- 暂停录像状态:在暂停状态下,用户可以继续录像、停止录像
其次,状态切换操作:
起始状态 | 用户操作 | 结束状态 |
视频预览状态 | 录像操作 | 正常录像状态 |
正常录像状态 | 暂停操作 | 暂停录像状态 |
停止操作 | 视频预览状态 | |
暂停录像状态 | 继续操作 | 正常录像状态 |
停止操作 | 视频预览状态 |
设计
状态处理
先设计状态类型:
enum TRecordingStatus { // 视频录制状态
rsNormal, // 视频预览状态
rsRecord, //正常录像状态
rsPause //暂停录像状态
};
TRecordingStatus FRecordingStatus;
__property TRecordingStatus RecordingStatus = {
read = FRecordingStatus, write = SetRecordingStatus
};
void __fastcall SetRecordingStatus(TRecordingStatus value);
void __fastcall TMainForm::SetRecordingStatus(TRecordingStatus value) {
FRecordingStatus = value;
if (FRecordingStatus == rsRecord) { // 录制状态
Button_Record->LargeImageIndex = 72;
Button_Record->ImageIndex = 84;
Button_Record->Caption = "暂停";
Button_Play->LargeImageIndex = 73;
Button_Play->ImageIndex = 85;
Button_Play->Caption = "停止";
}
else if (FRecordingStatus == rsPause) {
Button_Record->LargeImageIndex = 71;
Button_Record->ImageIndex = 78;
Button_Record->Caption = "继续";
Button_Play->LargeImageIndex = 73;
Button_Play->ImageIndex = 85;
Button_Play->Caption = "停止";
}
else if (FRecordingStatus == rsNormal) {
Button_Record->LargeImageIndex = 70;
Button_Record->ImageIndex = 77;
Button_Record->Caption = "录像";
Button_Play->LargeImageIndex = 71;
Button_Play->ImageIndex = 78;
Button_Play->Caption = "播放";
}
}
在实现过程中,发现图标下标确实太乱,大图标与小图标不对应,还需要逐个去找,悲催。有时间整理一下。
大图标:
小图标:
大小图标主要是用于窗口缩放导致主工具栏宽度不一样时,Ribbon会自动缩放按钮进行匹配。
则Button_RecordClick单击按钮事件处理:
if (FRecordingStatus == rsNormal || FRecordingStatus == rsPause)
RecordingStatus = rsRecord;
else if (FRecordingStatus == rsRecord)
RecordingStatus = rsPause;
Button_PlayClick按钮事件处理:
if (FRecordingStatus != rsNormal) // 非正常状态,即为录像相关状态,停止
VideoGrabber->Stop();
else // 播放视频文件
…
录像
录像首先确定目标文件名
UnicodeString path = OptionForm->Edit_SavePath->Text;
if (!THelper::IsEndWith(path, "\\"))
path += "\\";
TFrameCaptureDest destFormat = OptionForm->ComboBox_Format->ItemIndex == 0 ? fc_BmpFile : fc_JpegFile;
UnicodeString destFileName = "";
int index = 0;
do {
++index;
destFileName = THelper::FormatString("%svideo%d.asf", path.w_str(), index);
}
while (FileExists(destFileName));
发现该代码段用过多次,干脆设计为一个函数:
UnicodeString __fastcall THelper::NextExpectedFileName(UnicodeString path, UnicodeString baseFileName, UnicodeString extName) {
// 得到指定路径下的期望文件名称
if (!THelper::IsEndWith(path, "\\"))
path += "\\";
if(!THelper::IsStartWith(extName, "."))
extName = "." + extName;
UnicodeString destFileName = "";
int index = 0;
do {
++index;
destFileName = THelper::FormatString("%s%s%d%s", path.w_str(), baseFileName.w_str(), index, extName.w_str());
}
while (FileExists(destFileName));
return destFileName;
}
这样,只需要调用一个函数即可:
UnicodeString destFileName = THelper::NextExpectedFileName(OptionForm->Edit_SavePath->Text, L"video", L"asf");
录像的代码很简单,暂停、停止的功能也很好实现
缩放
上次在一所学校演示时,那时的老师提出了一个需求:能否在录像的同时,可以拖动、缩放。
这话说起来很简单,得实现哈。
花了整整一上午实现了这个功能的一半,汗!
翻来覆去地组合控件的各个属性:AdjustPixelAspectRatio、Display_AutoSize、Display_AspectRatio、Cropping_Zoom、Cropping_X、Cropping_Y、ZoomCoeff。最后靠把Inspector放在程序里进行手动调整达到预期效果。
设计使用方法:
之前,鼠标左键按下可拖动
而滚轮可缩放:
也就是说,缩放与拖动是整体式的。
现在需要外围不动,而内部内容变化。从操作角度而言,可以让用户按下Ctrl键再进行鼠标操作,比如原始视频:
Ctrl+滚轮进行内部缩放:
Ctrl+鼠标左键拖动内部: