交叉编译源码的方式移植ffmpeg-rockchip

发布于:2025-05-16 ⋅ 阅读:(16) ⋅ 点赞:(0)

获取ffmpeg源码

git submodule add -f https://github.com/FFmpeg/FFmpeg.git thirdparty/FFmpeg

瑞芯微ffmpeg-rk

git clone https://github.com/jjm2473/ffmpeg-rk/tree/enc#

 参考的一位博主的说法

使用 ffmpeg-rochip 的好处

传统的使用硬件编解码的开发思路是:使用 ffmpeg 获取视频流,然后用 MPP 库进行硬件编解码,最后再传给 ffmpeg 进行复用,生成容器文件或推流。这样做的缺点是整个开发成本较高,需要学习 ffmpeg,还要学习 MPP库。

而现在有了 ffmpeg-rochip 之后,我们可以省略去学习使用 MPP 库的步骤,因为这个库已经帮我们封装好了 MPP 的功能,我们只需要像之前那样使用 ffmpeg 即可,只需在使用编解码器时换成 xxx_rkmpp,比如 h264_rkmpp。这样做的好处就是大大降低我们的开发学习成本。

ffmpeg-rockchip源码编译流程

首先git clone,设置好交叉编译环境写PKG环境变量,这一点很重要

export PKG_CONFIG_SYSROOT_DIR=/home/zhangyu/RK3588/rk3588-buildroot-2021.11-sdk-v1.0/buildroot/output/rockchip_rk3588/host/aarch64-buildroot-linux-gnu/sysroot
export RKMPP_PC_PATH=/home/zhangyu/RK3588/rk3588-buildroot-2021.11-sdk-v1.0/buildroot/output/rockchip_rk3588/host/aarch64-buildroot-linux-gnu/sysroot/usr/lib/pkgconfig
export PKG_CONFIG_LIBDIR=${RKMPP_PC_PATH}:${PKG_CONFIG_SYSROOT_DIR}/usr/lib/pkgconfig:${PKG_CONFIG_SYSROOT_DIR}/usr/share/pkgconfig

export PKG_CONFIG_LIBDIR=${RKMPP_PC_PATH}:${PKG_CONFIG_SYSROOT_DIR}/usr/lib/:${PKG_CONFIG_SYSROOT_DIR}/usr/share/pkgconfig
export LD_LIBRARY_PATH="/home/zhangyu/RK3588/rk3588-buildroot-2021.11-sdk-v1.0/buildroot/output/rockchip_rk3588/host/lib:$LD_LIBRARY_PATH"

文件路径根据自己的buildroot文件更改,目的是让pkg-config能找到对应的包,

pkg-config libdrm -exists
pkg-config rk_mpp -exists    

echo $

输出为0,则说明能找到

进入ffmpeg-rockchip根目录下,

./configure --enable-version3 \
            --enable-nonfree \
            --enable-gpl \
	   --sysroot=$HOME/RK3588/rk3588-buildroot-2021.11-sdk-v1.0/buildroot/output/rockchip_rk3588/host/aarch64-buildroot-linux-gnu/sysroot \
            --prefix=/home/zhangyu/ffmpeg-rk-test/build\
            --target-os=linux \
            --arch=aarch64 \
            --cc=aarch64-buildroot-linux-gnu-gcc \
            --enable-cross-compile \
            --disable-x86asm \
            --enable-shared \
            --disable-static\
            --enable-libdrm \
            --enable-rkmpp\
            --enable-rkrga\
	    --disable-doc\
	    --disable-debug\
	    --disable-hardcoded-tables\
	    --disable-mipsdsp\
	    --disable-mipsfpu\
	    --disable-hardcoded-tables \
	    --disable-stripping\
  --extra-cflags="-fno-builtin-lrintf -fno-builtin-lrint" \
  --extra-ldflags="-lm"\
  --enable-protocols\
  --enable-network
make
make install

根据情况做出更改

ffmpeg rtsp流推流验证,

#include <cstdint>
#include <string>
#include <vector>
#include <iostream>
#include <thread>
#include <chrono>

extern "C" {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavutil/imgutils.h>
    #include <libavutil/time.h>
}

struct Frame {
void* data;
size_t size;
int width;
int height;
int pitch;
std::string cameraId;
std::string cameraName;
uint64_t timestamp;
};

void log_error(int ret, const std::string& msg) {
    char buf[256];
    av_strerror(ret, buf, sizeof(buf));
    std::cerr << msg << " Error: " << buf << std::endl;
}

int main() {
    av_log_set_level(AV_LOG_ERROR); // 只显示错误信息,避免日志过多
    avformat_network_init();

    // 初始化白帧和黑帧(Y分量)
    uint8_t buffW[1920 * 1080];
    uint8_t buffB[1920 * 1080];
    memset(buffW, 255, sizeof(buffW));
    memset(buffB, 0, sizeof(buffB));

    Frame frameWhite = { .data = buffW, .size = 1920 * 1080, .width = 1920, .height = 1080, .pitch = 1920 };
    Frame frameBlack = { .data = buffB, .size = 1920 * 1080, .width = 1920, .height = 1080, .pitch = 1920 };
    Frame* doubleBuffer[2] = { &frameWhite, &frameBlack };

    const char* outUrl = "rtsp://10.168.1.103:8554/live/stream1";

    // 查找硬件编码器(Rockchip)
    const AVCodec* codec = avcodec_find_encoder_by_name("h264_rkmpp");
    if (!codec) {
        std::cerr << "Encoder h264_rkmpp not found\n";
        return -1;
    }

    AVCodecContext* codecCtx = avcodec_alloc_context3(codec);
    codecCtx->width = 1920;
    codecCtx->height = 1080;
    codecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    codecCtx->time_base = AVRational{1, 25}; // 设置帧率为25fps
    codecCtx->framerate = AVRational{25, 1};

    if (avcodec_open2(codecCtx, codec, nullptr) < 0) {
        std::cerr << "Failed to open codec\n";
        return -1;
    }

    // 创建输出格式上下文(RTSP)
    AVFormatContext* fmtCtx = nullptr;
    avformat_alloc_output_context2(&fmtCtx, nullptr, "rtsp", outUrl);
    if (!fmtCtx) {
        std::cerr << "Could not allocate output context\n";
        return -1;
    }

    // 添加视频流并设置其参数
    AVStream* videoStream = avformat_new_stream(fmtCtx, codec);
    videoStream->time_base = codecCtx->time_base;
    avcodec_parameters_from_context(videoStream->codecpar, codecCtx);

    // 设置RTSP推流选项
    AVDictionary* opts = nullptr;
    av_dict_set(&opts, "rtsp_transport", "tcp", 0); // 使用TCP传输
    av_dict_set(&opts, "stimeout", "5000000", 0);   // 设置超时5秒

    // ⚠️ 不要调用 avio_open2 —— RTSP 使用的是 URL 打开方式,直接调用 avformat_write_header 即可。
    // 正确方式:建立连接并写入头部信息
    if (avformat_write_header(fmtCtx, &opts) < 0) {
        std::cerr << "Failed to write header to output\n";
        return -1;
    }

    // 创建并初始化帧
    AVFrame* frame = av_frame_alloc();
    frame->format = codecCtx->pix_fmt;
    frame->width = codecCtx->width;
    frame->height = codecCtx->height;
    av_frame_get_buffer(frame, 32);

    // 创建编码后的数据包
    AVPacket* pkt = av_packet_alloc();

    int64_t pts = 0;
    int index = 0;

    while (true) {
        Frame* src = doubleBuffer[index];
        index = !index;

        // 将灰度图像写入Y分量
        memcpy(frame->data[0], src->data, 1920 * 1080);
        // UV分量设置为中性值(灰色)
        memset(frame->data[1], 128, 1920 * 1080 / 4);
        memset(frame->data[2], 128, 1920 * 1080 / 4);

        frame->pts = pts++;

        int ret = avcodec_send_frame(codecCtx, frame);
        if (ret < 0) {
            log_error(ret, "send_frame");
            continue;
        }

        while (ret >= 0) {
            ret = avcodec_receive_packet(codecCtx, pkt);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break;
            if (ret < 0) {
                log_error(ret, "receive_packet");
                break;
            }

            pkt->stream_index = videoStream->index;
            pkt->pts = av_rescale_q(pkt->pts, codecCtx->time_base, videoStream->time_base);
            pkt->dts = av_rescale_q(pkt->dts, codecCtx->time_base, videoStream->time_base);
            pkt->duration = av_rescale_q(pkt->duration, codecCtx->time_base, videoStream->time_base);

            av_interleaved_write_frame(fmtCtx, pkt);
            av_packet_unref(pkt);
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(40)); // 控制帧率25fps
    }

    // 清理资源
    av_write_trailer(fmtCtx);
    avcodec_free_context(&codecCtx);
    avformat_free_context(fmtCtx);
    av_frame_free(&frame);
    av_packet_free(&pkt);
    return 0;
}

FFmpeg初始化验证

#include <iostream>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
}

int main() {
    // // 初始化 FFmpeg 网络模块(可选)
    // avformat_network_init();
    // 打印 FFmpeg 版本信息
    std::cout << "=== FFmpeg 库测试 ===" << std::endl;
    std::cout << "avcodec 版本: " << avcodec_version() << std::endl;
    std::cout << "avformat 版本: " << avformat_version() << std::endl;
    std::cout << "avutil 版本: " << avutil_version() << std::endl;

    return 0;
}

查找编码器

ffmpeg -encoders | grep h264

 


网站公告

今日签到

点亮在社区的每一天
去签到