RK3568上使用C++结合V4L2拉流,并RKMPP硬件编解码,并保存为MP4文件

devtools/2025/2/11 19:51:38/

在RK3568平台上使用C++结合V4L2捕获视频流,并通过RKMPP进行硬件编码后保存为MP4文件,可以按照以下步骤实现:


1. 环境准备

  • 硬件:RK3568开发板、摄像头模块。
  • 软件依赖
    • Linux内核支持V4L2
    • Rockchip MPP库(RKMPP)。
    • FFmpeg库(用于MP4封装,可选)。

V4L2_13">2. V4L2视频捕获

初始化V4L2设备并配置视频捕获参数:

#include <linux/videodev2.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>int init_v4l2(const char* device, int width, int height) {int fd = open(device, O_RDWR);if (fd < 0) return -1;// 设置视频格式struct v4l2_format fmt = {0};fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt.fmt.pix.width = width;fmt.fmt.pix.height = height;fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // 根据摄像头支持调整fmt.fmt.pix.field = V4L2_FIELD_NONE;if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) {close(fd);return -1;}// 申请缓冲区struct v4l2_requestbuffers req = {0};req.count = 4;req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;req.memory = V4L2_MEMORY_MMAP;if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) {close(fd);return -1;}// 映射内存并入队for (int i = 0; i < req.count; ++i) {struct v4l2_buffer buf = {0};buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = i;if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) {close(fd);return -1;}void* ptr = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);// 保存缓冲区指针...ioctl(fd, VIDIOC_QBUF, &buf);}// 启动视频流enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ioctl(fd, VIDIOC_STREAMON, &type);return fd;
}

RKMPP_72">3. RKMPP编码器初始化

配置RKMPP的H.264编码器:

#include <rockchip/rk_mpi.h>MppCtx init_rkmpp_encoder(int width, int height) {MppCtx ctx = NULL;MppApi *mpi = NULL;MppParam param = NULL;mpp_create(&ctx, &mpi);mpi->control(ctx, MPP_SET_OUTPUT_FORMAT, MPP_FMT_YUV420SP); // 输入格式需与V4L2一致// 配置编码参数MppEncCodecCfg codec_cfg = {0};codec_cfg.coding = MPP_VIDEO_CodingAVC;mpi->control(ctx, MPP_ENC_SET_CODEC_CFG, &codec_cfg);MppEncPrepCfg prep_cfg = {0};prep_cfg.width = width;prep_cfg.height = height;prep_cfg.format = MPP_FMT_YUV420SP;mpi->control(ctx, MPP_ENC_SET_PREP_CFG, &prep_cfg);MppEncRcCfg rc_cfg = {0};rc_cfg.rc_mode = MPP_ENC_RC_MODE_CBR;rc_cfg.bps_target = 4000000; // 码率4Mbpsmpi->control(ctx, MPP_ENC_SET_RC_CFG, &rc_cfg);return ctx;
}

4. FFmpeg封装MP4文件

初始化FFmpeg用于写入MP4容器:

extern "C" {
#include <libavformat/avformat.h>
}AVFormatContext* init_mp4_writer(const char* filename, int width, int height) {avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, filename);AVStream* stream = avformat_new_stream(fmt_ctx, NULL);stream->codecpar->codec_id = AV_CODEC_ID_H264;stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;stream->codecpar->width = width;stream->codecpar->height = height;stream->time_base = (AVRational){1, 30};avio_open(&fmt_ctx->pb, filename, AVIO_FLAG_WRITE);avformat_write_header(fmt_ctx, NULL);return fmt_ctx;
}

5. 主循环处理

捕获、编码、写入循环:

void capture_encode_save(int v4l2_fd, MppCtx encoder, AVFormatContext* mp4_ctx) {AVStream* stream = mp4_ctx->streams[0];int64_t pts = 0;while (true) {// 从V4L2捕获一帧struct v4l2_buffer v4l2_buf = {0};v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;v4l2_buf.memory = V4L2_MEMORY_MMAP;ioctl(v4l2_fd, VIDIOC_DQBUF, &v4l2_buf);void* frame_data = ...; // 获取映射的缓冲区地址// 编码(伪代码,需适配RKMPP API)MppBuffer mpp_buf;mpp_buffer_put(mpp_buf, frame_data, v4l2_buf.length);mpi->encode_put_frame(encoder, mpp_buf);MppPacket packet;while (mpi->encode_get_packet(encoder, &packet) == MPP_OK) {AVPacket av_pkt = {0};av_pkt.data = mpp_packet_get_data(packet);av_pkt.size = mpp_packet_get_length(packet);av_pkt.pts = pts++;av_pkt.stream_index = stream->index;av_write_frame(mp4_ctx, &av_pkt);}ioctl(v4l2_fd, VIDIOC_QBUF, &v4l2_buf);}
}

6. 资源清理

退出时释放资源:

void cleanup(int v4l2_fd, MppCtx encoder, AVFormatContext* mp4_ctx) {// 停止V4L2enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ioctl(v4l2_fd, VIDIOC_STREAMOFF, &type);close(v4l2_fd);// 销毁RKMPP编码器mpp_destroy(encoder);// 结束MP4写入av_write_trailer(mp4_ctx);avio_closep(&mp4_ctx->pb);avformat_free_context(mp4_ctx);
}

注意事项

  1. 格式转换:若V4L2输出格式与编码器输入格式不匹配(如YUYV转YUV420),需使用libyuv或手动转换。
  2. 时间戳管理:根据实际帧率生成正确的PTS/DTS。
  3. 关键帧标记:在AVPacket中设置AV_PKT_FLAG_KEY以标记关键帧。
  4. 错误处理:所有IOCTL和API调用需检查返回值。

通过上述步骤,可以实现RK3568上的视频捕获、硬件编码及MP4封装。实际开发中需参考RKMPP和FFmpeg的具体API文档调整代码。


http://www.ppmy.cn/devtools/157690.html

相关文章

接入 deepseek 实现AI智能问诊

1. 准备工作 注册 DeepSeek 账号 前往 DeepSeek 官网 注册账号并获取 API Key。 创建 UniApp 项目 使用 HBuilderX 创建一个新的 UniApp 项目&#xff08;选择 Vue3 或 Vue2 模板&#xff09;。 安装依赖 如果需要在 UniApp 中使用 HTTP 请求&#xff0c;推荐使用 uni.requ…

Linux系统 环境变量

环境变量 写在前面概念查看环境变量main函数的参数argc & argvenv bash环境变量 写在前面 对于环境变量&#xff0c;本篇主要介绍基本概念及三四个环境变量 —— PATH、HOME、PWD。其中 PATH 作为 “ 敲门砖 ”&#xff0c;我们会更详细讲解&#xff1b;理解环境变量的全局…

MybatisPlus较全常用复杂查询引例(limit、orderby、groupby、having、like...)

MyBatis-Plus 是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。以下是 MyBatis-Plus 中常用复杂查询&#xff08;如 LIMIT、ORDER BY、GROUP BY、HAVING、LIKE 等&#xff09;的引例&#xff1a; 1. 环境准备…

【Java基础篇】——第2篇:Java语法基础

第2篇&#xff1a;Java语法基础 2.1 引言 在上一篇中&#xff0c;我们介绍了Java的基本概念、应用场景以及如何搭建开发环境。本篇将深入探讨Java的语法基础&#xff0c;涵盖变量与数据类型、运算符、控制结构、数组、方法、面向对象编程的进一步内容、异常处理以及常用的编程…

2025年前端面试,性能相关的面试题汇总

以下是一些与前端性能相关的面试题汇总,适合准备 2025 年的前端面试: 1. 性能优化基础 什么是页面加载性能? 解释页面加载性能的概念,包括首屏加载时间、内容交互时间等。 如何评估网页性能? 介绍使用 Chrome DevTools、Lighthouse、WebPageTest 等工具进行性能评估的方法…

[笔记] 汇编杂记(持续更新)

文章目录 前言举例解释函数的序言函数的调用栈数据的传递 总结 前言 举例解释 // Type your code here, or load an example. int square(int num) {return num * num; }int sub(int num1, int num2) {return num1 - num2; }int add(int num1, int num2) {return num1 num2;…

位图的深入解析:从数据结构到图像处理与C++实现

在学习优选算法课程的时候&#xff0c;博主学习位运算了解到位运算的这个概念&#xff0c;之前没有接触过&#xff0c;就查找了相关的资料&#xff0c;丰富一下自身&#xff0c;当作课外知识来了解一下。 位图&#xff08;Bitmap&#xff09;是一种用于表示图像的数据结构&…

selenium4.0 入门案例

from selenium import webdriver import time #创建webdriver对象&#xff0c;把驱动放置到了系统环境变量中&#xff0c;可不带参数创建 # driver webdriver.Firefox() driver webdriver.Chrome() #使用浏览器打开指定页面 driver.get(http://www.baidu.com)time.sleep(5) #回…