You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python从H264 WebSocket流提取单帧图像问题求助

问题:WebSocket传输H264流提取单帧失败

我正在从厂商系统通过WebSocket传输的原始H264视频流中提取单帧图像,最终目标是将该帧用于OpenCV目标检测。尝试参考相关方案后仍未解决问题:

  • 使用decode_image函数时返回None值;
  • 剥离冗余信息后的数据虽有0x000001起始码,但单条消息数据量远小于864x480帧对应的414720字节。

相关代码如下:

import requests
import cv2
import sys
import av
import asyncio
import websockets

global stream
stream = []

def decode_image(raw_bytes: bytes):
    code_ctx = av.CodecContext.create("h264", "r")
    packets = code_ctx.parse(raw_bytes)
    for i, packet in enumerate(packets):
        frames = code_ctx.decode(packet)
        if frames:
            return frames[0].to_image()

async def websocket_connect(cookies):
    async with websockets.connect('wss://****:7001/api/ws', extra_headers={"Cookie":cookies}) as ws:
        print('### Waiting for send Command ###')
        await ws.send('&site=***&camera=2&raw=true')
        print('### Send command sent, set the site location and camera number ###')

        async for message in ws:
            #Need to filter out the data to just what I want
            global stream
            SOCKET_MESSAGE = get_socket_message(message)
            if SOCKET_MESSAGE['msg_type'] == 0:
                VIDEO_DATA = get_video_data(message)
                stream = VIDEO_DATA['data']
        await ws.close()

async def videodata_print():
    global stream
    while True:
        print('### RUNNING INSIDE VIDEODATA_PRINT ###')
        #Check that VIDEO_DATA has values
        print(f'\nSTREAM: {stream}\n')
        if len(stream) != 0:
            tmp = stream
            frame = decode_image(tmp)
            print(frame)
            cv2.imshow('stream',frame)
            #If frame works here, then add rest of code to get the info I need out of that frame
        await asyncio.sleep(5)

async def run_livestream(cookies):
    await asyncio.gather(websocket_connect(cookies), videodata_print())


#Grab authentication token of server, need to pass cookie with websocket to connect
r = check_Authentication(CREDENTIAL)
cookies = f"token={r.cookies['token']}"
print(cookies)

asyncio.run(run_livestream(cookies))

核心问题分析与修复方案

1. H264帧分片传输导致单条消息不完整

厂商WebSocket发送的H264数据通常是分片NALU,单条消息只是完整帧的一部分,因此数据量远小于一帧的大小,解码器无法解析出完整帧。

  • 修复:缓存收到的NALU数据,直到凑齐一完整帧(通过判断NALU类型,比如I帧的起始和结束),再传给解码器。
  • 代码调整:将全局stream改为累加缓存,而非每次覆盖:
    global stream
    stream.extend(VIDEO_DATA['data'])  # 替换原stream = VIDEO_DATA['data']
    
    同时需判断完整帧边界:比如检测到0x00000167(I帧起始码)后,将下一个起始码到来前的内容作为一帧处理。

2. 解码器上下文重复创建导致状态丢失

decode_image函数每次调用都重新创建CodecContext,但H264解码器需要保留SPS/PPS等关键参数才能正确解码后续帧,重复创建会丢失这些状态,导致解码失败返回None。

  • 修复:将解码器上下文设为全局变量,复用同一个上下文:
    # 全局初始化解码器
    code_ctx = av.CodecContext.create("h264", "r")
    
    def decode_image(raw_bytes: bytes):
        global code_ctx
        packets = code_ctx.parse(raw_bytes)
        for packet in packets:
            frames = code_ctx.decode(packet)
            if frames:
                # 转换为OpenCV兼容的BGR格式
                return frames[0].to_ndarray(format='bgr24')
    

3. OpenCV显示的线程与刷新问题

cv2.imshow需要配合cv2.waitKey()才能正常刷新窗口,且当前协程环境下直接调用可能存在线程兼容性问题。

  • 修复:添加cv2.waitKey(1)确保窗口刷新,同时判断帧不为空再显示:
    if len(stream) != 0:
        tmp = stream
        frame = decode_image(tmp)
        if frame is not None:
            cv2.imshow('stream', frame)
            cv2.waitKey(1)  # 必须调用,否则窗口无法刷新
    

4. 缓存清理避免重复解码

每次解码成功后,需清理已处理的缓存数据,避免重复解析旧数据。

  • 修复:解码到帧后截取未处理的缓存部分(需根据NALU边界调整逻辑):
    if frames:
        global stream
        # 示例:假设当前packet对应的数据已处理,清理缓存
        stream = stream[len(raw_bytes):]
        return frames[0].to_ndarray(format='bgr24')
    

5. 验证H264数据格式

确认剥离后的VIDEO_DATA['data']是标准Annexb格式(带0x000001起始码),如果是AVCC格式(带4字节长度前缀),需先转换为Annexb格式才能被av库解析:

# AVCC转Annexb示例(需根据实际格式调整)
def avcc_to_annexb(data):
    offset = 0
    annexb_data = b''
    while offset < len(data):
        length = int.from_bytes(data[offset:offset+4], byteorder='big')
        offset +=4
        annexb_data += b'\x00\x00\x01' + data[offset:offset+length]
        offset += length
    return annexb_data

内容的提问来源于stack exchange,提问作者fysloc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 05:47:45