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

news/2025/2/9 5:41:44 标签: c++, 开发语言, V4L2, RKMPP, ffmpeg

在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; // 码率4Mbps
    mpi->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) {
    // 停止V4L2
    enum 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.niftyadmin.cn/n/5845597.html

相关文章

【RocketMQ 存储】- 同步刷盘和异步刷盘

文章目录 1. 前言2. 概述3. submitFlushRequest 提交刷盘请求4. FlushDiskWatcher 同步刷盘监视器5. 同步刷盘但是不需要等待刷盘结果6. 小结 本文章基于 RocketMQ 4.9.3 1. 前言 RocketMQ 存储部分系列文章&#xff1a; 【RocketMQ 存储】- RocketMQ存储类 MappedFile【Rock…

React 生命周期函数详解

React 组件在其生命周期中有多个阶段&#xff0c;每个阶段都有特定的生命周期函数&#xff08;Lifecycle Methods&#xff09;。这些函数允许你在组件的不同阶段执行特定的操作。以下是 React 组件生命周期的主要阶段及其对应的生命周期函数&#xff0c;并结合了 React 16.3 的…

【ROS2】【2025】Simulate a 6DoF Robotic Arm in Gazebo and ROS2

在本教程中&#xff0c;将学习如何从头开始模拟机械臂。我们将使用 Doosan Robotics 的 6DoF 机械臂。Gazebo 和 ROS2 是执行此模拟的软件。所有代码、URDF 和配置文件都可以在我的 gitee 存储库中找到和下载。 https://gitee.com/kong-yue1/robotic_arm_environment.githttps…

FreeRTOS的事件组

1 创建事件组 xEventGroupCreate EventGroupHandle_t xEventGroupCreate( void ) { EventGroup_t *pxEventBits;/* 分配事件组内存。*/pxEventBits ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) );if( pxEventBits ! NULL ){pxEventBits->uxEventBits 0; …

uniapp中使用uCharts折线图X轴数据间隔显示

1、先看官网 https://www.ucharts.cn/ 2、设置代码 "xAxisDemo3":function(val, index, opts){if(index % 2 0){return val}else {return }}, 再在数据中引入设置好样式

Django开发入门 – 1.搭建基于Python Web框架Django的IDE开发环境

Django开发入门 – 1.搭建基于Python Web框架Django的IDE开发环境 Build A Integrated Development Environment(IDE) for Python Web Framework - django By JacksonML 1. 获取及安装最新版Python 打开Chrome浏览器&#xff0c;访问Python官网链接&#xff1a;https://www…

Windows下AMD显卡在本地运行大语言模型(deepseek-r1)

Windows下AMD显卡在本地运行大语言模型 本人电脑配置第一步先在官网确认自己的 AMD 显卡是否支持 ROCm下载Ollama安装程序模型下载位置更改下载 ROCmLibs先确认自己显卡的gfx型号下载解压 替换替换rocblas.dll替换library文件夹下的所有 重启Ollama下载模型运行效果 本人电脑配…

《Java核心技术 卷II》本地化的数字格式

数字格式 数字和货币的格式高度依赖locale。 格式化对象的集合&#xff0c;可以对java.text包中的数字进行格式化和解析。 格式化数字值 对特定locale的数字进行格式化的步骤&#xff1a; 得到Locale对象使用工厂方法得到一个格式器对象。使用这个格式器对象来完成格式化解析工…