3.4.3 JDK API 说明(含 Python 封装)
目录
数据类型定义(data_type)
定义 API 中使用的基本枚举和结构体
枚举类型: media_type
设备媒体类型枚举:
值 | 含义 |
---|---|
MEDIA_TYPE_CANT_STAT | 无法获取设备状态 |
MEDIA_TYPE_UNKNOWN | 未知 |
MEDIA_TYPE_VIDEO | 视频 |
MEDIA_TYPE_VBI | VBI(垂直消隐) |
MEDIA_TYPE_RADIO | 广播 |
MEDIA_TYPE_SDR | SDR(软件定义无线电) |
MEDIA_TYPE_TOUCH | 触摸输入 |
MEDIA_TYPE_SUBDEV | 子设备 |
MEDIA_TYPE_DVB_FRONTEND | 数字电视前端 |
MEDIA_TYPE_DVB_DEMUX | 数字电视解复用 |
MEDIA_TYPE_DVB_DVR | 数字电视录像 |
MEDIA_TYPE_DVB_NET | 数字电视网络 |
MEDIA_TYPE_DTV_CA | 数字电视条件访问 |
MEDIA_TYPE_MEDIA | 媒体设备 |
枚举类型: codec_type
表示当前设备或上下文是否为编码或解码:
值 | 含义 |
---|---|
NOT_CODEC | 非编解码 |
CODEC_DEC | 解码 |
CODEC_ENC | 编码 |
结构体:v4l2_ctx
V4L2 捕获及编码上下文结构体定义:
struct v4l2_ctx {
int fd; // 设备文件句柄
unsigned int width; // 视频宽度
unsigned int height; // 视频高度
unsigned int pixelformat; // 输入像素格式
unsigned int out_pixelformat; // 输出像素格式
int nplanes; // 输入平面数
int out_nplanes; // 输出平面数
struct buffer* cap_buffers; // 捕获缓冲区数组
struct buffer* out_buffers; // 输出缓冲区数组
__u32 bytesperline[VIDEO_MAX_PLANES]; // 各输入平面行字节数
__u32 out_bytesperline[VIDEO_MAX_PLANES]; // 各输出平面行字节数
FILE* file[2]; // 输入/输出文件指针
int verbose; // 日志详细等级
enum codec_type ctype; // 编码/解码类型
};
核心接口(C++)
处理多媒体的主要类:包括帧、摄像头、解码/编码器、视频输出和图像处理。
JdkFrame
: 图像帧封装类
class JdkFrame {
public:
JdkFrame(int dma_fd_, size_t size_, int w, int h);
~JdkFrame();
// 将 DMA 缓冲区映射到 CPU 内存并返回指针
unsigned char* toHost() const;
// 克隆返回数据副本
std::vector<unsigned char> Clone() const;
// 保存为 NV12 格式 .yuv 文件
bool saveToFile(const std::string& filename) const;
// 从文件加载数据(与 saveToFile 配对使用)
bool loadFromFile(const std::string& filename, size_t expected_size);
// 获取底层 DMA FD
int getDMAFd() const;
// 获取缓冲区大小
size_t getSize() const { return size_; }
// 获取分辨率
int getWidth() const { return width_; }
int getHeight() const { return height_; }
// 将原始 NALU 数据拷贝到内部 buffer(如编码后写入)
// offset:目标缓冲区偏移
int MemCopy(const uint8_t* nalu, int nalu_size, int offset = 0);
private:
size_t size_; // 缓冲区总大小
int width_;
int height_;
JdkDma dma_; // DMA 同步辅助
std::shared_ptr<JdkDmaBuffer> data; // 底层 DMA buffer
};
using JdkFramePtr = std::shared_ptr<JdkFrame>;
JdkDma
与 JdkDmaBuffer
class JdkDmaBuffer {
public:
// 构造并分配 DMA 缓冲区
explicit JdkDmaBuffer(size_t size);
~JdkDmaBuffer();
// 返回映射后的用户空间地址
void* data() const;
// 整块填充值
void fill(uint8_t val);
// 获取物理地址(需先调用 map_phys_addr)
void map_phys_addr();
// 公开字段(只读)
size_t m_size;
uint64_t m_phys;
};
class JdkDma {
public:
// 通过 DMA 引擎异步复制数据
int Asyn(const JdkDmaBuffer& dst, const JdkDmaBuffer& src, size_t size);
// FD 间的 DMA 复制
int Asyn(const int& dst_fd, const int& src_fd, size_t size);
};
JdkCamera
class JdkCamera {
public:
/**
* 创建并打开 V4L2 设备
* @param device 设备路径 (e.g. "/dev/video0")
* @param width 期望采集宽度
* @param height 期望采集高度
* @param pixfmt V4L2 像素格式 (e.g. V4L2_PIX_FMT_NV12)
* @param req_count 请求的缓冲区数量 (默认 4)
* @return 成功返回 JdkCameraPtr,否则返回 nullptr
*/
static std::shared_ptr<JdkCamera> create(const std::string& device,
int width,
int height,
__u32 pixfmt,
int req_count = 4);
/** 获取一帧图像(阻塞) */
JdkFramePtr getFrame();
~JdkCamera();
private:
explicit JdkCamera(const std::string& device);
class Impl;
std::unique_ptr<Impl> impl_;
};
using JdkCameraPtr = std::shared_ptr<JdkCamera>;
JdkDecoder
class JdkDecoder {
public:
/**
* 初始化硬件解码器
* @param width 输出分辨率宽度
* @param height 输出分辨率高度
* @param payload 输入码流类型 (见 MppCodingType)
* @param Format 输出像素格式 (默认 NV12)
*/
JdkDecoder(int width, int height,
MppCodingType payload,
MppPixelFormat Format = PIXEL_FORMAT_NV12);
~JdkDecoder();
/** 解码,从已封装帧中解码 */
std::shared_ptr<JdkFrame> Decode(std::shared_ptr<JdkFrame> frame);
/** 解码,从裸 NALU 数据解码 */
std::shared_ptr<JdkFrame> Decode(const uint8_t* nalu, int nalu_size);
private:
int width_;
int height_;
MppCodingType payload_;
int format_;
int channel_id_;
MppVdecCtx* pVdecCtx = nullptr;
};
JdkEncoder
class JdkEncoder {
public:
/**
* 初始化硬件编码器
* @param width 输入分辨率宽度
* @param height 输入分辨率高度
* @param payload 输出码流类型 (见 MppCodingType)
* @param Format 输入像素格式 (默认 NV12)
*/
JdkEncoder(int width, int height,
MppCodingType payload,
MppPixelFormat Format = PIXEL_FORMAT_NV12);
~JdkEncoder();
/** 编码,将原始帧编码为压缩码流 */
std::shared_ptr<JdkFrame> Encode(std::shared_ptr<JdkFrame> frame);
private:
int width_;
int height_;
MppCodingType payload_;
int format_;
int encoder_id_ = 0;
MppVencCtx* pVencCtx = nullptr;
};
JdkDrm
/** 支持的像素格式 */
enum class PixelFmt : uint32_t {
NV12 = DRM_FORMAT_NV12
};
class JdkDrm {
public:
/**
* 打开 DRM 设备并初始化
* @param width 显示宽度
* @param height 显示高度
* @param stride 行跨度 (bytes)
* @param fmt 像素格式
* @param device DRM 设备路径 (默认 "/dev/dri/card0")
*/
JdkDrm(int width, int height, int stride,
PixelFmt fmt = PixelFmt::NV12,
const char* device = "/dev/dri/card0");
~JdkDrm();
/** 发送一帧到 DRM 屏幕 */
int sendFrame(std::shared_ptr<JdkFrame> frame);
/** 销毁指定的 framebuffer */
void destroyFb(uint32_t fb, uint32_t handle);
/** 打开 DRM 设备 */
int openCard(const char* dev);
/** 自动选取合适的 connector/crtc/plane */
int pickConnectorCrtcPlane();
/** 导入 DMA FD 为 DRM framebuffer */
int importFb(int dma_fd, uint32_t& fb_id, uint32_t& handle);
private:
struct LastFB {
uint32_t fb_id;
uint32_t handle;
int dma_fd;
} last_;
};
JdkV2D
/** 支持的目标像素格式(枚举值请参考完整头文件) */
enum V2DFormat {
// 例如: V2D_NV12, V2D_RGB888, ……
};
/** 矩形区域 */
struct V2DRect {
int x, y, width, height;
};
class JdkV2D {
public:
JdkV2D() = default;
~JdkV2D() = default;
/** 格式转换 */
JdkFramePtr convert_format(const JdkFramePtr& input,
V2DFormat out_format);
/** 缩放 */
JdkFramePtr resize(const JdkFramePtr& input,
int out_width, int out_height);
/** 同时缩放并格式转换 */
JdkFramePtr resize_and_convert(const JdkFramePtr& input,
int out_width, int out_height,
V2DFormat out_format);
/** 填充矩形区域 */
bool fill_rect(const JdkFramePtr& image,
const V2DRect& rect,
uint32_t rgba_color);
/** 绘制矩形边框 */
bool draw_rect(const JdkFramePtr& image,
const V2DRect& rect,
uint32_t rgba_color,
int thickness = 2);
/** 绘制多矩形 */
bool draw_rects(const JdkFramePtr& image,
const std::vector<V2DRect>& rects,
uint32_t rgba_color,
int thickness = 2);
/** 图像融合(bottom 上叠加 top) */
JdkFramePtr blend(const JdkFramePtr& bottom,
const JdkFramePtr& top);
};
JdkVo
class JdkVo {
public:
/**
* 初始化 Vo 输出
* @param width 输出宽度
* @param height 输出高度
* @param Format 像素格式 (默认 NV12)
*/
JdkVo(int width, int height,
MppPixelFormat Format = PIXEL_FORMAT_NV12);
~JdkVo();
/** 发送一帧到 Vo 硬件输出 */
int sendFrame(std::shared_ptr<JdkFrame> frame);
private:
int width_;
int height_;
MppPixelFormat format_;
int channel_id_;
MppVoCtx* pVoCtx = nullptr;
};
Python 绑定(pyjdk)
模块导入
#下载安装
wget https://gitlab.dc.com:8443/bianbu/bianbu-linux/jdk/-/blob/main/pyjdk/pyjdk-0.1.0-cp312-cp312-linux_riscv64.whl
pip install pyjdk-0.1.0-cp312-cp312-linux_riscv64.whl
#导入
import pyjdk as jdk
枚举(Enums)
jdk.PixelFormat
:NV12
,MJPEG
,JPEG
(对应 V4L2 FourCC)jdk.CodingType
:H264
,H265
,JPEG
,MJPEG
jdk.MppPixelFormat
:NV12
,NV21
jdk.V2DFormat
: 常用如RGB888
(其余以实际编译产物为准)jdk.DrmPixelFormat
:NV12
jdk.V2DRect
r = jdk.V2DRect(x, y, w, h)
# 也支持 (x,y,w,h) tuple / list / dict 传入到绘制接口
jdk.Dma
dma = jdk.Dma()
dma.asyn(dst_fd: int, src_fd: int, size: int) -> int # 异步 DMA 拷贝(包装 JdkDma::Asyn)
jdk.Frame
(原 JdkFrame)
f = jdk.Frame(dma_fd: int, size: int, width: int, height: int)
# 只读属性
f.dma_fd: int
f.size: int
f.width: int
f.height: int
# I/O 与视图
f.save(path: str) -> bool # 保存底层缓冲(NV12/raw/比特流)
f.load_from_file(path: str, expected_size: int) -> bool
f.to_numpy_nv12(copy: bool = False) -> (y, uv) # 零拷贝/深拷贝到 numpy(NV12 两平面)
f.to_bytes() -> bytes # 直接导出底层缓冲
f.mem_copy(src: bytes|bytearray|memoryview, offset: int = 0) -> int
# 资源管理
f.release() # 立即归还底层缓冲(QBUF)
# 支持 with 语法,退出时自动 release()
with f:
y, uv = f.to_numpy_nv12()
说明:
to_numpy_nv12(copy=False)
返回的y/uv
是对底层缓冲的零拷贝视图,生命周期与 numpy 对象绑定;若需独立副本,请设copy=True
。
采集相机(MIPI / USB)
jdk.MipiCam
cam = jdk.MipiCam.create(device: str, width: int, height: int,
fourcc: jdk.PixelFormat = jdk.PixelFormat.NV12,
req_count: int = 4) -> jdk.MipiCam
frame = cam.get_frame() # 阻塞取帧,返回 jdk.Frame
# 也支持 for f in cam: 迭代获取帧;支持 with cam: 进入/退出管理
jdk.UsbCam
uc = jdk.UsbCam.create("/dev/video20", 1280, 720, jdk.PixelFormat.MJPEG)
f = uc.get_frame() # 返回 MJPEG 比特流帧(可配合 Decoder 解码)
对应示例:
mipi_cam.py
、usb_cam.py
。
编码器 jdk.Encoder
enc = jdk.Encoder(width: int, height: int,
coding: jdk.CodingType = jdk.CodingType.H264,
pixfmt: jdk.MppPixelFormat = jdk.MppPixelFormat.NV12)
pkt = enc.encode(frame: jdk.Frame) -> jdk.Frame # 返回“码流帧”(bitstream in Frame)
注意:源码仅导出了
encode(...)
方法;若你本地脚本里使用了encode_frame(...)
,请改为encode(...)
(encode_h264.py
示例里出现的encode_frame
为旧名,建议更新)。
解码器 jdk.Decoder
dec = jdk.Decoder(width: int, height: int,
coding: jdk.CodingType = jdk.CodingType.JPEG,
pixfmt: jdk.MppPixelFormat = jdk.MppPixelFormat.NV12)
# 1) 从“码流帧”(Frame)解码
yuv = dec.decode(bitstream_frame: jdk.Frame) -> jdk.Frame
# 2) 直接从 bytes-like(bytes/bytearray/memoryview)解码
yuv = dec.decode(bitstream: bytes|bytearray|memoryview) -> jdk.Frame
对应示例:
decode_jpeg.py
、encode_decode.py
。