前面介绍了HDMI视频采集预览和OSD功能,相对来说编码和录像的细节就要多一些了,主角依旧还是某宝入手的那块pipivision的USB3.0的HDMI采集卡,使用前我们需要先了解一下相关资料,以下是示例源码下载链接
https://github.com/messicn/UVSMedia
查看示例源码,这次我们依然使用DevManager.h来实现相应的功能,把源文件DevManager.cpp添加到工程并包含该头文件即可,内部的层次结构如图所示。
首先我们先打开采集设备,简单用两句代码就能搞定,具体请参考之前的文章
HDMI采集既有音频又有视频,因此编码时我们得根据需要考虑以下几个问题
1 需要编码音频,则必须在调用DeviceStart前先指定音频捕获设备;
2 音视频编码器和参数设置;
3 视频编码是否需要支持多路不同分辨率码流,通过索引号进行区别,设备多路编码模式如下图
音频编码设置相对比较简单,只需要设置编码算法,例如设置AAC编码
dev.SetAudioProperty(uvs_audio_codec_AAC);
对于视频来说我们要先确定编码器,在有硬件支持的情况下我们当然是期望开启硬件编码加速大大降低CPU的占用,例如Intel集显支持H.264/265编码,或者是Nvidia显卡支持的H.264/265编码,或者是使用CPU的OpenH264软编码器。存在多路编码的情况下,建议高分辨率图像选则硬件加速,低分辨率图像根据情况选择硬件加速或软编码。需要注意的是,在Debian/Linux下必须安装i965驱动并设置以下环境变量才能启用Intel硬件编码加速
export LD_LIBRARY_PATH=.:/usr/local/lib:$LD_LIBRARY_PATH
export LIBVA_DRIVER_NAME=i965
通过以下接口可以查询对应的编码器是否可以使用,例如检查Intel编码器是否支持(成功返回UVS_OK)
dev.QueryVideoEncoder(uvs_video_codec_intel_h264);
选择好视频编码器之后,我们需要配置对应的视频编码参数,参数说明如下:
typedef struct {
uvs_target_usage_e targetUsage; // 编码模式,偏重速度还是画质
uvs_codec_profile_e codecProfile; // profile,0表示默认
uvs_avc_entropy_e entropyCoding; // AVC(h264) 熵编码算法,0表示默认
uvs_scale_usage_e scaleUsage; // 图像缩放模式,偏重速度还是图像质量,图像大小不变就不用管
int scaleWidth; // 图像缩放宽度,0表示不变
int scaleHeight; // 图像缩放高度,0表示不变
int cropLeft; // 图像裁剪X坐标
int cropTop; // 图像裁剪Y坐标
int cropWidth; // 图像裁剪宽度,0表示不裁剪
int cropHeight; // 图像裁剪高度,0表示不裁剪
float frameRate; // 编码帧率,0表示帧率不变
uvs_video_rcmode_e rcMode; // 编码码率控制,CBR固定码率,VBR可变码率,CQP固定画质,AVBR平均可变码率,一般选CBR或VBR即可
int encBitRate; // 编码码率(kbit/sec),一般选1000到10000之间,数值越大清晰度越高
int maxBitRate; // 编码最大码率(kbit/sec),一般选3000到15000之间,数值越大清晰度越高
int encQuality; // 编码质量,一般选20到30之间,注意数值越小清晰度越高,当然产生的码率也越大,一般可设置27左右
int GOPLength; // 关键帧间隔,0表示默认
} uvs_encode_config_t;
可以看到,除了编码码率和质量参数以外,其他参数一般使用默认值0就可以了,当然也可以进行缩放和裁剪设置,比如当前图像分辨率是1080P(1920 x 1080),可以按照720P(1280 x 720)的分辨率进行编码。通过以下接口配置视频编码,第一个参数为编码流索引号0(任意非负整数):
dev.SetVideoEncodeParam(0, uvs_video_codec_intel_h264, encconfig);
如果要启动多路编码,使用不同的索引号1和参数调用该接口即可
dev.SetVideoEncodeParam(1, uvs_video_codec_sw_h264, encconfig);
在此之前,我们要确定启动编码的目的是录像还是推流,如果是录像,我们先进行相应的设置,包括目录,录像封包大小时长等
dev.RecordStart(0, _string(str), Q_NULLPTR, true, flags, sz, dur);
这个接口的参数依次为
编码流索引号0;
str为录像目录和格式;
flags指定是否支持录像中断修复;
sz表示限定的录像文件大小,0表示不限制;
dur表示限定的录像时长,0表示不限制;
当录像达到限定的大小或时长,会自动开始新的录像,未指定限制条件时需要调用对应的接口停止录像。
然后我们就可以启动索引对应的编码流了,UVS_STREAM_ALL表示启动所有编码流。
dev.EncodeStart(UVS_STREAM_ALL);
编码流启动后,我们还可以实时获取编码的信息,例如编码帧数和帧率
dev.GetVideoEncodeStatus(0, encstatus);
完整代码如下:
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::Dialog)
, dev(0) // open device 0
{
ui->setupUi(this);
if (dev)
{
dev.DeviceStart();
}
}
void Dialog::showEvent(QShowEvent *event)
{
if (dev)
{
dev.PreviewStart(reinterpret_cast< HWND >(ui->video->winId()));
}
}
void Dialog::on_rec_clicked()
{
if (dev)
{
uvs_encode_config_t config;
memset(&config, 0, sizeof(config));
config.rcMode = uvs_video_rcmode_VBR;
config.encBitRate = 2000;
config.maxBitRate = 5000;
config.encQuality = 27;
r = dev.SetVideoEncodeParam(0, uvs_video_codec_intel_h264, config);
if (r != UVS_OK)
{
r = CDevSDK::QueryVideoEncoder(uvs_video_codec_intel_h264);
if (r != UVS_OK)
{
QMessageBox::information(this, "Record", "HW not support or codec library not found!");
}
return;
}
/* %M month, %D month of day, %H 24 hours, %h 12 hours, %m minute, %s second, %Y year,
* %t AM/PM, %N month name, %n month short name, %W week name, %w week short name, %i stream index, %% % */
str = QDir::cleanPath("." + QDir::separator() + "%Y%M%D_%H%m%s_%i.mp4");
r = dev.RecordStart(0, _string(str), Q_NULLPTR, true, false, 0, 300000);
if (r != UVS_OK) return;
// audio stream config
dev.SetAudioProperty(uvs_audio_codec_AAC);
r = dev.EncodeStart(0);
if (r != UVS_OK)
{
QMessageBox::information(this, "Record", "Encode failed!");
return;
}
}
}
Dialog::~Dialog()
{
dev.DeviceClose();
delete ui;
}
编译运行画面(画圈位置为实时编码状态信息)