您好!针对您遇到的 ret=-268435456 (0xFFFF0000, 通常表示 HB_ERR_INVALID_STATE 或硬件忙) 错误,您的分析非常准确:
核心原因:libsrcampy.Encoder 对象不是线程安全的。
在 RDK X5 的底层实现中,encode_file() (Input) 和 get_img() (Output) 操作的是同一个硬件编码通道上下文。如果两个线程同时调用这两个 API,会导致:
- 状态机冲突:硬件状态寄存器被并发修改,导致状态错乱。
- 缓冲区竞争:输入缓冲区和输出缓冲区指针被同时读写,引发崩溃。
解决方案:串行化访问(加锁)
既然硬件通道同一时刻只能处理“输入”或“输出”中的一个动作,必须在 Python 层加锁,强制串行化访问编码器对象。
修正后的双线程代码(加锁版)
import threading
import queue
from hobot_vio import libsrcampy
import time
# 1. 全局锁:保护 Encoder 对象
enc_lock = threading.Lock()
# 帧队列
frame_queue = queue.Queue(maxsize=3)
stop_flag = threading.Event()
# 编码器初始化 (必须在主线程或单一线程中初始化)
enc = libsrcampy.Encoder()
enc.encode(0, 1, 1920, 1080) # channel=0, H.264
def input_thread():
"""输入线程:只负责采集和放入队列,不直接操作 Encoder"""
while not stop_flag.is_set():
nv12_frame = get_nv12_frame() # 你的采集逻辑
# 队列管理(丢旧帧)
if frame_queue.full():
try:
frame_queue.get_nowait()
except queue.Empty:
pass
frame_queue.put_nowait(nv12_frame)
time.sleep(0.01) # 避免空转占用 CPU
def output_thread():
"""输出线程:统一负责 Input 和 Output 操作"""
while not stop_flag.is_set():
try:
# 从队列取帧
nv12_frame = frame_queue.get(timeout=0.1)
except queue.Empty:
continue
# 【关键】加锁:确保 encode_file 和 get_img 原子执行
with enc_lock:
try:
# 1. 输入数据
enc.encode_file(nv12_frame.tobytes())
# 2. 获取输出数据 (必须紧跟在 input 之后)
h264_data = enc.get_img()
# 3. 输出数据
if h264_data:
write_h264(h264_data)
except Exception as e:
print(f"Encoding error: {e}")
# 遇到错误可考虑重置编码器或跳过当前帧
# 启动线程
t1 = threading.Thread(target=input_thread, daemon=True)
t2 = threading.Thread(target=output_thread, daemon=True)
t1.start()
t2.start()
# 等待退出
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
stop_flag.set()
t1.join()
t2.join()
enc.close()
错误码解析
您遇到的 ret=-268435443 (十六进制 0xFFFF0000 附近) 通常对应:
HB_ERR_INVALID_STATE:编码器处于错误状态(如未初始化、已关闭、或状态机混乱)。HB_ERR_BUSY:硬件忙,但通常不会导致崩溃,只会返回错误。
并发导致的典型流程:
- 线程 A 调用
encode_file()→ 硬件开始编码。 - 线程 B 同时调用
get_img()→ 此时编码未完成,或上下文被线程 A 的另一次调用覆盖。 - 底层驱动检测到状态不一致 → 返回错误码 → Python 层未捕获异常 → 崩溃。
更好的架构建议
如果加锁后性能仍不满足(因为锁会阻塞),或者您需要更稳定的生产级方案:
方案 A:单线程编码模型(推荐)
将采集、编码、推流全部放在同一个线程中,利用 queue 仅做数据传递,不做并发处理。
- 优点:彻底避免锁竞争,逻辑简单,稳定性最高。
- 缺点:如果采集耗时过长,会阻塞编码(但通常采集是 DMA,耗时极短)。
def single_thread_worker():
enc = libsrcampy.Encoder()
enc.encode(0, 1, 1920, 1080)
while not stop_flag.is_set():
# 1. 采集
frame = get_nv12_frame()
# 2. 编码 (Input + Output 原子执行)
enc.encode_file(frame.tobytes())
h264 = enc.get_img()
# 3. 输出
if h264:
write_h264(h264)
方案 B:C++ + 回调机制(高性能)
使用 C++ 调用 HB_VENC 底层接口,注册回调函数获取编码结果,完全异步。
- 输入:主线程调用
HB_VENC_SendFrame。 - 输出:硬件编码完成后,自动触发回调函数,在回调中获取码流。
- 优势:无需手动管理 Input/Output 同步,由驱动层保证线程安全。
总结
- 根本原因:
libsrcampy.Encoder不支持并发调用,Input 和 Output 必须串行。 - 快速修复:使用
threading.Lock()包裹encode_file()和get_img()。 - 最佳实践:
- 低延迟场景:使用单线程循环(采集→编码→输出)。
- 高吞吐场景:使用 C++ 回调 或 TROS 多媒体节点。
请尝试加上锁后再次测试,应该能解决崩溃问题!如有 C++ 回调示例需求,可随时告知。![]()