添加SEI单元至编码帧后遇FFmpeg截断错误求助
H.264 SEI单元截断错误排查与修复
问题描述
编写编码器在每个编码帧后添加SEI单元,参考BSAnalyzer结构(SEI位于每一帧之后),但使用FFmpeg播放生成的文件时出现如下截断错误:
[NULL @ 000001cd901df1c0] SEI type 5 size 179843 truncated at 179776 [h264 @ 000001cd96eae780] SEI type 5 size 179843 truncated at 179774 [NULL @ 000001cd901df1c0] SEI type 5 size 179843 truncated at 179776 [h264 @ 000001cd970789c0] SEI type 5 size 179843 truncated at 179774 ...and more
错误原因分析
错误核心是SEI payload size的编码逻辑完全错误。H.264规定SEI的payload size采用EBSP可变长度整数编码:
- 用若干个
0xFF字节表示超出255的部分 - 最后一个字节为剩余值(小于
0xFF) - 所有字节的总和等于实际payload大小(UUID + 用户数据的总长度)
你的代码在计算payload size编码长度和写入时,错误地用0xFF - 剩余值作为最后一个字节,导致FFmpeg解析时计算出的payload size远大于实际数据长度,从而触发截断错误。
修复后的代码
修正后的calc_sei_size函数
static const uint8_t START_MARKER[4] = {0x0, 0x0, 0x0, 0x1}; static const uint8_t UUID[16] = { 0x81, 0x39, 0xe9, 0xbd, 0xa6, 0x09, 0x48, 0xb7, 0x76, 0x2c, 0xc8, 0x20, 0xd9, 0x68, 0xcc, 0xcf}; static const uint8_t SEI_TYPE[1] = {0x06}; static const uint8_t PAYLOAD_TYPE[1] = {0x05}; int calc_sei_size(size_t user_data_size) { int new_buffer_size = 0; new_buffer_size += sizeof(START_MARKER); // NAL起始标记 new_buffer_size += sizeof(SEI_TYPE); // NAL类型(0x06) new_buffer_size += sizeof(PAYLOAD_TYPE); // SEI payload type(0x05) int payload_size = user_data_size + sizeof(UUID); // 实际payload大小:UUID + 用户数据 int temp = payload_size; int size_bytes = 0; // 计算payload size的编码字节数 while (temp > 0) { size_bytes++; if (temp > 0xFF) { temp -= 0xFF; } else { temp = 0; } } new_buffer_size += size_bytes; // 加上payload size的编码字节数 new_buffer_size += sizeof(UUID); // UUID长度 new_buffer_size += user_data_size; // 用户数据长度 return new_buffer_size; }
修正后的sei_write函数
void sei_write(uint8_t *user_data, size_t user_data_size) { int newSize = calc_sei_size(user_data_size); uint8_t *buffer = (uint8_t *)calloc(sizeof(uint8_t), newSize); if (!buffer) return; // 增加内存分配失败判断 int offset = 0; memcpy(&buffer[offset], START_MARKER, sizeof(START_MARKER)); // 复制NAL起始标记 offset += sizeof(START_MARKER); memcpy(&buffer[offset], SEI_TYPE, sizeof(SEI_TYPE)); // 复制NAL类型(0x06) offset += sizeof(SEI_TYPE); memcpy(&buffer[offset], PAYLOAD_TYPE, sizeof(PAYLOAD_TYPE)); // 复制payload type(0x05) offset += sizeof(PAYLOAD_TYPE); // 正确编码payload size int payload_size = user_data_size + sizeof(UUID); int temp = payload_size; while (temp > 0xFF) { buffer[offset++] = 0xFF; temp -= 0xFF; } buffer[offset++] = temp; // 写入剩余值,而非0xFF - temp memcpy(&buffer[offset], UUID, sizeof(UUID)); // 复制UUID offset += sizeof(UUID); memcpy(&buffer[offset], user_data, user_data_size); // 复制用户数据 DoSomeWithBuffer(buffer, newSize); free(buffer); // 释放内存,避免泄漏 }
额外注意事项
- 内存泄漏修复:原代码未释放
calloc分配的内存,修复后添加free(buffer) - EBSP转换:如果用户数据中包含
0x000002或0x000003序列,需要插入0x03进行EBSP转换,否则可能导致NALU解析失败 - Payload Type编码:如果未来使用大于255的payload type,需要按照EBSP规则编码(当前type=5,单字节没问题)
内容的提问来源于stack exchange,提问作者zerpico
相关产品推荐
相关产品推荐

