获取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