QT视频采集之编码Enc和录像Rec

前面介绍了HDMI视频采集预览和OSD功能,相对来说编码和录像的细节就要多一些了,主角依旧还是某宝入手的那块pipivision的USB3.0的HDMI采集卡,使用前我们需要先了解一下相关资料,以下是示例源码下载链接
https://github.com/messicn/UVSMedia

网盘分享提取码n6sh

查看示例源码,这次我们依然使用DevManager.h来实现相应的功能,把源文件DevManager.cpp添加到工程并包含该头文件即可,内部的层次结构如图所示。

 首先我们先打开采集设备,简单用两句代码就能搞定,具体请参考之前的文章

QT叠加HDMI采集视频OSD

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;
}

编译运行画面(画圈位置为实时编码状态信息)

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是基于OpenCV和MPP进行视频采集和硬编码保存的示例代码,采用QT C++编写: ```c++ #include <opencv2/opencv.hpp> #include <rockchip/rk_mpi.h> #include <rockchip/mpp_buffer.h> using namespace cv; // 定义视频输入参数 #define VIDEO_WIDTH 1280 #define VIDEO_HEIGHT 720 #define VIDEO_FPS 30 #define VIDEO_BPS 8000000 #define VIDEO_GOP 30 // 定义编码器参数 #define ENCODER_NAME "h264enc" #define ENCODER_WIDTH VIDEO_WIDTH #define ENCODER_HEIGHT VIDEO_HEIGHT #define ENCODER_FPS VIDEO_FPS #define ENCODER_GOP VIDEO_GOP #define ENCODER_BITRATE VIDEO_BPS class VideoRecorder : public QObject { Q_OBJECT public: VideoRecorder(QObject* parent = nullptr); ~VideoRecorder(); void start(); void stop(); signals: void finished(); private: void encodeFrame(const Mat& frame); private: MPP_ENC_CONFIG_S enc_cfg_; MPP_ENC_HANDLE hEnc_; VideoCapture cap_; VideoWriter writer_; bool is_running_; }; VideoRecorder::VideoRecorder(QObject* parent) : QObject(parent), is_running_(false) { // 初始化MPP环境 RK_MPI_SYS_Init(); // 创建编码器 memset(&enc_cfg_, 0, sizeof(enc_cfg_)); enc_cfg_.eType = MPP_VIDEO_CodingAVC; enc_cfg_.iUsage = MPP_ENC_NORMAL; enc_cfg_.iPicWidth = ENCODER_WIDTH; enc_cfg_.iPicHeight = ENCODER_HEIGHT; enc_cfg_.iKeyInterval = ENCODER_GOP; enc_cfg_.fFrameRate = ENCODER_FPS; enc_cfg_.iBitRate = ENCODER_BITRATE; RK_MPI_VENC_Create(&hEnc_, &enc_cfg_); // 创建输入视频流 cap_.set(CAP_PROP_FRAME_WIDTH, VIDEO_WIDTH); cap_.set(CAP_PROP_FRAME_HEIGHT, VIDEO_HEIGHT); cap_.set(CAP_PROP_FPS, VIDEO_FPS); // 创建输出文件 writer_.open("output.mp4", VideoWriter::fourcc('a', 'v', 'c', '1'), VIDEO_FPS, Size(VIDEO_WIDTH, VIDEO_HEIGHT), true); } VideoRecorder::~VideoRecorder() { // 释放资源 writer_.release(); RK_MPI_VENC_Destroy(hEnc_); RK_MPI_SYS_Exit(); } void VideoRecorder::start() { is_running_ = true; // 循环采集视频帧并编码保存 while (is_running_) { Mat frame; cap_.read(frame); encodeFrame(frame); } emit finished(); } void VideoRecorder::stop() { is_running_ = false; } void VideoRecorder::encodeFrame(const Mat& frame) { MPP_BUFFER buffer; RK_MPI_MBPOOL pool = NULL; RK_MPI_MBPOOL_CREATE(&pool, MPP_BUFFER_TYPE_ION, 1, ENCODER_WIDTH*ENCODER_HEIGHT*3/2); RK_MPI_MBPOOL_GET_BUFFER(pool, &buffer, RK_TRUE); RK_MPI_MB_BLK blk = RK_MPI_MB_GetPtr(buffer); memcpy(blk->pVirAddr, frame.data, VIDEO_WIDTH*VIDEO_HEIGHT*3/2); blk->u32Size = VIDEO_WIDTH*VIDEO_HEIGHT*3/2; MPP_PACKET packet; memset(&packet, 0, sizeof(packet)); RK_MPI_VENC_SendFrame(hEnc_, buffer, &packet); if (packet != NULL) { writer_.write(packet->pData, packet->iDataSize); RK_MPI_SYS_FreePacket(packet); } RK_MPI_MBPOOL_PUT_BUFFER(pool, buffer); } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); VideoRecorder recorder; recorder.start(); QObject::connect(&recorder, &VideoRecorder::finished, &a, &QCoreApplication::quit); return a.exec(); } ``` 以上代码仅作为示例,具体实现过程需要根据您的具体需求进行调整和优化。另外,需要注意的是,由于硬编码需要使用MPP库,因此需要将相关头文件和库文件链接到项目中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值