獲取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
?