H.264添加 SEI 信息技术文档

  1. 概述
    本文档描述如何在 H.264 视频流中动态插入自定义 SEI信息。SEI 是 H.264/AVC 标准中定义的一种元数据格式,可用于携带时间戳、版权信息、场景标记等附加数据。本方案基于 FFmpeg 的 AVPacket 结构和标准 H.264 NAL 单元格式实现,支持与视频帧的精确同步。
  2. SEI 核心概念
  3. SEI 的作用
    1. 元数据承载:传递与视频帧相关的非压缩数据(如 GPS 信息、时间戳)。
    2. 解码辅助:提供解码器增强信息(如颜色空间描述)。
    3. 流分析:用于视频流完整性校验或内容标记。
  4. SEI 结构
    字段 长度 描述
    NAL Header 1 byte NAL 单元类型(0x06 表示 SEI)
    Payload Type 可变长度 SEI 数据类型(如用户自定义=5)
    Payload Size 可变长度 负载数据的字节长度
    Payload Data N bytes 实际负载内容
  5. 实现原理
  6. 整体流程
    A[输入H.264数据包] --> B[生成SEI NAL单元]
    B --> C[合并到原始数据前]
    C --> D[更新AVPacket]
    D --> E[写入封装层]
  7. 关键技术点
    1. NAL 单元封装:遵循 H.264 Annex B 格式,添加 0x00000001 起始码。
    2. 可变长度编码:对 payload_type 和 payload_size 进行多字节编码。
    3. 内存管理:使用 FFmpeg 的 AVBufferRef 安全管理数据内存。
  8. 代码实现详解
  9. 代码实现
    // 将字符串转换为二进制表示
    std::vector<uint8_t> string_to_binary(const std::string& str) {
    return std::vector<uint8_t>(str.begin(), str.end());
    }

// 编码payloadType和payloadSize
void encode_sei_header(std::vector<uint8_t>& nal_unit, uint32_t payload_type, size_t payload_size) {
while (payload_type > 0x7F) {
nal_unit.push_back((payload_type & 0x7F) | 0x80);
payload_type >>= 7;
}
nal_unit.push_back(payload_type);

    while (payload_size > 0x7F) {
            nal_unit.push_back((payload_size & 0x7F) | 0x80);
            payload_size >>= 7;
    }
    nal_unit.push_back(payload_size);

}

// H.264起始码
const uint8_t H264_START_CODE[] = {0x00, 0x00, 0x00, 0x01};

void append_custom_sei_to_packet(AVPacket *pkt, const std::string &sei_message)
{
// 创建SEI负载
std::vector<uint8_t> sei_payload = string_to_binary(sei_message);

// 构建SEI NAL单元
std::vector<uint8_t> sei_nal;
sei_nal.insert(sei_nal.end(), H264_START_CODE, H264_START_CODE + 4);  // 添加起始码
sei_nal.push_back(0x06);                                              // SEI NAL单元头

// 编码SEI消息头
encode_sei_header(sei_nal, 5, sei_payload.size());  // payloadType=5 表示用户数据未注册

// 合并NAL头和负载
sei_nal.insert(sei_nal.end(), sei_payload.begin(), sei_payload.end());

// 分配新内存并复制数据
size_t   new_size = pkt->size + sei_nal.size();
uint8_t *new_data = (uint8_t *)av_malloc(new_size);
if (!new_data)
{
    fprintf(stderr, "Memory allocation failed\n");
    return;
}
memcpy(new_data, sei_nal.data(), sei_nal.size());
memcpy(new_data + sei_nal.size(), pkt->data, pkt->size);

// 更新pkt的数据和大小
av_packet_unref(pkt);
pkt->data = new_data;
pkt->size = new_size;

// 创建AVBufferRef
AVBufferRef *buffer_ref = av_buffer_create(new_data, new_size, av_buffer_default_free, NULL, 0);
if (!buffer_ref)
{
    fprintf(stderr, "Failed to create AVBufferRef\n");
    av_free(new_data);
    return;
}
pkt->buf = buffer_ref;

}

void MP4Recorder::receiveH264Stream(const uint8_t *data, size_t size, const std::string &sei)
{
if (!m_bIsRecording || !ofmt_ctx) return;

    AVPacket pkt;
    init_packet(&pkt);

    pkt.data = const_cast<uint8_t*>(data);
    pkt.size = size;

    // 动态生成并附加SEI到当前帧
    append_custom_sei_to_packet(&pkt, sei);

    write_video_packet(pkt);

    av_packet_unref(&pkt);

}
2. 函数说明
1. string_to_binary:将字符串转换为二进制数据
2. encode_sei_header:编码 SEI 头部(Payload Type + Size),nal_unit: 输出的 NAL 单元容器,payload_type: SEI 类型(如 5=用户未注册数据),payload_size: 负载数据长度,每个字节最高位为延续标志(1 表示后续还有字节),其余7 位存储数据,按小端序排列
3. append_custom_sei_to_packet:将 SEI 添加到 AVPacket 开头,构建 SEI NAL 单元(起始码 + NAL头 + 负载),合并原始视频数据,更新 AVPacket 内存引用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值