Python通过ctypes传字节流至C转uint8_t数组结果不符问题
问题排查:Python WebSocket视频字节流转C uint8_t数组不一致问题
问题背景
- Python通过WebSocket获取视频原始二进制字节流,执行两个操作:
- 截取前14字节后写入
video.h264文件 - 通过ctypes将字节流传递给C库
decode_video.so的conversion_test函数,目标是转为uint8_t数组供FFmpeg解码
- 截取前14字节后写入
- 当前C函数使用
strtol将每两个字节转为十六进制值存入数组,但转换后的vals数组与C直接读取文件得到的inbuf数组内容完全不符
相关代码示例
Python代码
import ctypes import websocket # 加载C动态库 lib = ctypes.CDLL('./decode_video.so') # 声明C函数参数与返回值类型 lib.conversion_test.argtypes = [ctypes.c_char_p, ctypes.c_int] lib.conversion_test.restype = None def on_message(ws, message): # 跳过前14字节的头数据 data = message[14:] # 追加写入本地文件 with open('video.h264', 'ab') as f: f.write(data) # 将字节流传递给C函数处理 lib.conversion_test(data, len(data)) # WebSocket连接逻辑(省略初始化代码) ws = websocket.WebSocketApp("ws://your_ws_endpoint", on_message=on_message) ws.run_forever()
C代码(decode_video.c)
#include <stdio.h> #include <stdlib.h> #include <stdint.h> void conversion_test(char *data, int len) { // 错误:按每两个字节转一个元素分配空间 uint8_t *vals = malloc(len / 2 * sizeof(uint8_t)); int idx = 0; for (int i = 0; i < len; i += 2) { // 错误:将二进制字节当作十六进制字符串解析 char hex[3] = {data[i], data[i+1], '\0'}; vals[idx++] = (uint8_t)strtol(hex, NULL, 16); } // 打印转换结果前10个元素 printf("转换后的vals数组前10个值:"); for (int i = 0; i < 10; i++) { printf("%02x ", vals[i]); } printf("\n"); // 直接读取本地文件对比 FILE *fp = fopen("video.h264", "rb"); uint8_t inbuf[10]; fread(inbuf, 1, 10, fp); printf("直接读文件的inbuf数组前10个值:"); for (int i = 0; i < 10; i++) { printf("%02x ", inbuf[i]); } printf("\n"); free(vals); fclose(fp); }
输出对比示例
转换后的vals数组前10个值:41 e3 18 05 00 00 00 01 67 42
直接读文件的inbuf数组前10个值:00 00 00 01 67 42 c0 1e 9a 60
问题分析与修正
核心错误
你混淆了二进制字节流和十六进制字符串的概念:
- Python传递给C的
data是原始二进制字节,比如文件中的0x00字节,在C的char*中就是值为0的字节,而非字符串"00" - 用
strtol解析两个二进制字节为十六进制值的逻辑完全错误,相当于把随机的二进制值当作ASCII字符解析,自然和文件内容不符
正确实现
直接将char*中的字节逐位复制到uint8_t数组即可,因为char本质就是单字节类型,强转uint8_t可避免符号位问题:
修改后的C函数:
void conversion_test(char *data, int len) { // 正确:按字节数分配空间 uint8_t *vals = malloc(len * sizeof(uint8_t)); // 直接逐字节复制,无需转换 for (int i = 0; i < len; i++) { vals[i] = (uint8_t)data[i]; } // 打印对比 printf("转换后的vals数组前10个值:"); for (int i = 0; i < 10; i++) { printf("%02x ", vals[i]); } printf("\n"); FILE *fp = fopen("video.h264", "rb"); uint8_t inbuf[10]; fread(inbuf, 1, 10, fp); printf("直接读文件的inbuf数组前10个值:"); for (int i = 0; i < 10; i++) { printf("%02x ", inbuf[i]); } printf("\n"); free(vals); fclose(fp); }
额外说明
- 若Python传递的是十六进制字符串(如将二进制转为"0000000167..."格式),原
strtol逻辑才适用,但此处是原始二进制流,无需额外转换 - 强转
uint8_t是为了避免char作为有符号类型时的符号扩展问题,确保字节值正确 - 内存分配需对应实际字节数,原代码的
len/2会导致内存不足或数据截断
内容的提问来源于stack exchange,提问作者Arbaaz
相关产品推荐
相关产品推荐

