交叉编译源码的方式移植ffmpeg-rockchip
获取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/pkgconfigexport 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}; // 设置帧率为25fpscodecCtx->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