bonus-edge-proxy/src/rknn/ffmpeg_rga_decoder.cpp

355 lines
10 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ffmpeg_rga_decoder.h"
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include "spdlog/spdlog.h"
// 结构体定义
struct RgaSrcInfo {
int fd;
int wstride;
int hstride;
void* vaddr;
int format;
};
FFmpegRGADecoder::FFmpegRGADecoder() {
pkt_ = av_packet_alloc();
frame_ = av_frame_alloc();
}
FFmpegRGADecoder::~FFmpegRGADecoder() {
release();
if (pkt_)
av_packet_free(&pkt_);
if (frame_)
av_frame_free(&frame_);
}
void FFmpegRGADecoder::cleanUp() {
if (dec_ctx_) {
avcodec_free_context(&dec_ctx_);
dec_ctx_ = nullptr;
}
if (fmt_ctx_) {
avformat_close_input(&fmt_ctx_);
fmt_ctx_ = nullptr;
}
is_opened_ = false;
}
void FFmpegRGADecoder::release() {
cleanUp();
output_dma_buf_.reset();
}
bool FFmpegRGADecoder::isOpened() const {
return is_opened_;
}
double FFmpegRGADecoder::get(int propId) {
if (propId == cv::CAP_PROP_FRAME_WIDTH)
return (double)width_;
if (propId == cv::CAP_PROP_FRAME_HEIGHT)
return (double)height_;
if (propId == cv::CAP_PROP_FPS)
return fps_;
return 0.0;
}
bool FFmpegRGADecoder::open(const std::string& url) {
cleanUp();
int ret;
AVDictionary* options = nullptr;
// RTSP 优化参数
av_dict_set(&options, "rtsp_transport", "tcp", 0);
av_dict_set(&options, "buffer_size", "1024000", 0);
av_dict_set(&options, "stimeout", "5000000", 0);
av_dict_set(&options, "flags", "low_delay", 0);
spdlog::info("FFmpeg: Opening stream: {}", url);
if ((ret = avformat_open_input(&fmt_ctx_, url.c_str(), nullptr, &options)) < 0) {
spdlog::error("FFmpeg: Could not open input URL: {}", url);
return false;
}
if ((ret = avformat_find_stream_info(fmt_ctx_, nullptr)) < 0)
return false;
video_stream_idx_ = av_find_best_stream(fmt_ctx_, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
if (video_stream_idx_ < 0)
return false;
AVStream* st = fmt_ctx_->streams[video_stream_idx_];
AVCodecParameters* codecpar = st->codecpar;
const char* decoder_name = nullptr;
if (codecpar->codec_id == AV_CODEC_ID_HEVC)
decoder_name = "hevc_rkmpp";
else if (codecpar->codec_id == AV_CODEC_ID_H264)
decoder_name = "h264_rkmpp";
if (decoder_name) {
decoder_ = avcodec_find_decoder_by_name(decoder_name);
if (decoder_)
spdlog::info("FFmpeg: Using Hardware Decoder: {}", decoder_name);
}
if (!decoder_) {
spdlog::warn("FFmpeg: Hardware decoder not found, falling back.");
decoder_ = avcodec_find_decoder(codecpar->codec_id);
}
if (!decoder_)
return false;
dec_ctx_ = avcodec_alloc_context3(decoder_);
avcodec_parameters_to_context(dec_ctx_, codecpar);
if ((ret = avcodec_open2(dec_ctx_, decoder_, nullptr)) < 0) {
spdlog::error("FFmpeg: Failed to open codec");
return false;
}
width_ = codecpar->width;
height_ = codecpar->height;
fps_ = (st->avg_frame_rate.den > 0) ? av_q2d(st->avg_frame_rate) : 25.0;
is_opened_ = true;
spdlog::info("FFmpeg: Decoder Ready. {}x{} @ {:.2f} fps", width_, height_, fps_);
return true;
}
bool FFmpegRGADecoder::ensure_output_buffer(int width, int height) {
size_t img_size = width * height * 3;
if (!output_dma_buf_ || output_dma_buf_->size < img_size || width_ != width ||
height_ != height) {
width_ = width;
height_ = height;
output_dma_buf_ = std::make_unique<DmaBuffer>(img_size, "/dev/dma_heap/system-uncached");
if (output_dma_buf_->fd < 0) {
spdlog::error("FFmpeg: Failed to allocate output DMA buffer!");
return false;
}
spdlog::info("FFmpeg: Allocated Output DMA Buffer (FD: {})", output_dma_buf_->fd);
}
return true;
}
// 获取源信息
bool get_src_info(const AVFrame* frame, RgaSrcInfo& info) {
info.fd = 0;
info.vaddr = nullptr;
info.wstride = frame->width;
info.hstride = frame->height;
info.format = RK_FORMAT_YCbCr_420_SP; // Default NV12
if (frame->format == AV_PIX_FMT_DRM_PRIME && frame->data[0]) {
AVDRMFrameDescriptor* desc = (AVDRMFrameDescriptor*)frame->data[0];
info.fd = desc->objects[0].fd;
// 1. 获取 Pitch (wstride)
info.wstride = desc->layers[0].planes[0].pitch;
// 2. 计算 hstride
if (desc->layers[0].nb_planes >= 2) {
int y_offset = desc->layers[0].planes[0].offset;
int uv_offset = desc->layers[0].planes[1].offset;
info.hstride = (uv_offset - y_offset) / info.wstride;
} else {
// [修改] 如果没有多平面信息,默认假设紧凑排列 (Compact)
// 之前强制对齐到 16 可能导致读取位置错误(跳过了真正的 UV
// 如果画面变灰,通常是因为 hstride 算大了。
info.hstride = frame->height;
}
return true;
} else if (frame->format == AV_PIX_FMT_NV12) {
info.wstride = frame->linesize[0];
info.vaddr = (void*)frame->data[0];
info.hstride = frame->height;
return true;
}
// [新增] 检测 10bit 格式 (H.265 常见)
else if (frame->format == AV_PIX_FMT_P010) {
info.wstride = frame->linesize[0] / 2; // P010 也是 2 bytes per pixel? 不stride 是 bytes
// RGA 对 P010 的处理需要特殊 flag这里暂时回退到 NV12 处理尝试
// 或者直接报错让软件处理
info.wstride = frame->linesize[0];
info.vaddr = (void*)frame->data[0];
info.hstride = frame->height;
info.format = RK_FORMAT_YCbCr_420_SP_10B; // RGA 支持的 10bit 格式
return true;
}
return false;
}
bool FFmpegRGADecoder::read(cv::Mat& output_mat) {
if (!is_opened_)
return false;
static int frame_count = 0;
frame_count++;
int ret;
while (true) {
ret = av_read_frame(fmt_ctx_, pkt_);
if (ret < 0)
return false;
if (pkt_->stream_index == video_stream_idx_) {
ret = avcodec_send_packet(dec_ctx_, pkt_);
if (ret < 0) {
av_packet_unref(pkt_);
continue;
}
ret = avcodec_receive_frame(dec_ctx_, frame_);
if (ret == 0) {
if (!ensure_output_buffer(frame_->width, frame_->height)) {
av_packet_unref(pkt_);
return false;
}
RgaSrcInfo src_info;
bool has_info = get_src_info(frame_, src_info);
// 调试日志:每 100 帧打印一次 Stride 信息,帮你确认计算是否正确
if (frame_count % 100 == 0) {
spdlog::info("Debug Frame: FD={} W={} H={} WStride={} HStride={} Fmt={}",
src_info.fd, frame_->width, frame_->height, src_info.wstride,
src_info.hstride, frame_->format);
}
if (has_info && src_info.fd > 0) {
// --- 硬件路径 ---
rga_buffer_t src_img, dst_img;
rga_buffer_handle_t src_handle, dst_handle;
// Import
size_t src_size = src_info.wstride * src_info.hstride * 3 / 2;
// 如果是 10bit大小要翻倍? 不P010 是 2 bytes per pixel
if (src_info.format == RK_FORMAT_YCbCr_420_SP_10B)
src_size *= 2;
src_handle = importbuffer_fd(src_info.fd, src_size);
dst_handle =
importbuffer_fd(output_dma_buf_->fd, output_dma_buf_->actual_alloc_size);
if (src_handle && dst_handle) {
src_img = wrapbuffer_handle(src_handle, frame_->width, frame_->height,
src_info.format);
dst_img = wrapbuffer_handle(dst_handle, width_, height_, RK_FORMAT_BGR_888);
// 设置 Stride
src_img.wstride = src_info.wstride;
src_img.hstride = src_info.hstride;
dst_img.wstride = width_;
dst_img.hstride = height_;
imsetColorSpace(&src_img, IM_YUV_BT709_LIMIT_RANGE);
imsetColorSpace(&dst_img, IM_RGB_FULL);
IM_STATUS status =
imcvtcolor(src_img, dst_img, src_info.format, RK_FORMAT_BGR_888);
releasebuffer_handle(src_handle);
releasebuffer_handle(dst_handle);
if (status == IM_STATUS_SUCCESS) {
output_mat = cv::Mat(height_, width_, CV_8UC3, output_dma_buf_->vaddr);
av_packet_unref(pkt_);
return true;
} else {
spdlog::error("RGA Fail: {}", imStrError(status));
}
}
}
// --- 软件回退 ---
// 如果走到这里,说明硬件路径失败或无 FD
// 简单的软件转换,确保颜色正常
if (frame_->format == AV_PIX_FMT_NV12) {
// 1. 将有效数据拷贝到连续内存 (去除 stride 影响)
cv::Mat mYUV_Continous(frame_->height * 3 / 2, frame_->width, CV_8UC1);
// 拷贝 Y
uint8_t* src_y = frame_->data[0];
uint8_t* dst_y = mYUV_Continous.data;
for (int i = 0; i < frame_->height; i++) {
memcpy(dst_y + i * frame_->width, src_y + i * frame_->linesize[0],
frame_->width);
}
// 拷贝 UV
uint8_t* src_uv = frame_->data[1];
// 如果 data[1] 为空,尝试根据 height 计算偏移
if (!src_uv)
src_uv = src_y + src_info.hstride * src_info.wstride; // 尝试用 hstride
uint8_t* dst_uv = dst_y + frame_->width * frame_->height;
for (int i = 0; i < frame_->height / 2; i++) {
memcpy(dst_uv + i * frame_->width, src_uv + i * frame_->linesize[0],
frame_->width);
}
cv::Mat mBGR(height_, width_, CV_8UC3, output_dma_buf_->vaddr);
cv::cvtColor(mYUV_Continous, mBGR, cv::COLOR_YUV2BGR_NV12);
if (width_ != frame_->width) {
cv::resize(mBGR, mBGR, cv::Size(width_, height_));
}
output_mat = mBGR;
av_packet_unref(pkt_);
return true;
}
}
}
av_packet_unref(pkt_);
}
}
bool FFmpegRGADecoder::read_raw(RgaFrameInfo& output_info) {
if (!is_opened_)
return false;
int ret;
while (true) {
ret = av_read_frame(fmt_ctx_, pkt_);
if (ret < 0)
return false;
if (pkt_->stream_index == video_stream_idx_) {
ret = avcodec_send_packet(dec_ctx_, pkt_);
if (ret < 0) {
av_packet_unref(pkt_);
continue;
}
ret = avcodec_receive_frame(dec_ctx_, frame_);
if (ret == 0) {
// 利用现有的逻辑获取信息
RgaSrcInfo info;
get_src_info(frame_, info);
output_info.fd = info.fd;
output_info.vaddr = info.vaddr;
output_info.width = frame_->width;
output_info.height = frame_->height;
output_info.wstride = info.wstride;
output_info.hstride = info.hstride;
output_info.format = info.format;
// 注意:这里我们返回了 info但没有释放 AVPacket 或 AVFrame
// 在单线程模型中,下一帧读取会覆盖 frame_。
// 如果是 Zero-Copy必须确保在 NPU 推理完成前frame_ 内存不被释放/覆盖。
// 但 rknn_run 是阻塞的,所以这里直接返回是安全的(前提是 infer 结束后才读下一帧)。
av_packet_unref(pkt_);
return true;
}
}
av_packet_unref(pkt_);
}
}