抽取视频数据

本文介绍了如何利用ffmpeg抽取.mp4视频文件中的H264数据,包括找到视频流,添加sps/pps数据,以及读取sps和pps的详细步骤。关键在于解析extradata字段以获取sps和pps信息,并在写入H264文件时添加特征码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

抽取视频数据步骤

1、打开.mp4格式文件
2、创建并打开一个空文件存储 H264 数据
3、找到视频流,并循环读取流中AVPacket,并为每个流添加特征码或sps/pps等数据(只有关键帧前面要添加sps/pps数据,其他的只需要添加特征码)。
4、都处理完后将数据写入文件保存
流程图:
在这里插入图片描述

相关API

文件操作
相关API

读取sps和pps 数据

从AVCodecContext的extradata数据域中读取,如下所示,mp4文件的extradata的部分数据
包含sps及pps的extradata的部分数据
extradata 数据详细分析

  • 跳过前 4 个字节
  • 第5个字节 ff 的后2位用于指示表示编码数据长度所需字节数
  • 第6个字节 e1 后5位(结果是1)是表示接下来的sps或pps的个数为1
  • 第7、8两个字节00 18表示接下来的sps或pps数据的长度,结果是接下来sps或pps长度是24个字节。
  • 第9个字节是67表示这个是sps数据,也就是说从67到9a这24个字节是sps数据。因为sps只有一个,所以接下来是pps数据。
  • 第34个字节也就是9a后面的那个字节 01,表示 pps 的个数为 1 个
  • 第 35、36 两个字节 00 06 标识pps 数据长度是 6
  • 第 37 个字节 68 表明这确实是pps数据,包括68在内的6个字节是pps的内容
    提取到的每个sps/pps数据在写入h264文件时都要在其前面加上4个字节的特征码(0x00000001)。

源码:

/*
 *提取视频中的视频数据
 * */

#include <stdio.h>
#include <libavformat/avformat.h>
#include <libavutil/log.h>
#include <libavformat/avio.h>


/*
 在帧前面添加特征码(一般SPS/PPS的帧的特征码用4字节表示,为0X00000001,其他的帧特征码用3个字节表示,为0X000001。也有都用4字节表示的,我们这里采用前面的方式)
 out是要输出的AVPaket
 sps_pps是SPS和PPS数据的指针,对于非关键帧就传NULL
 sps_pps_size是SPS/PPS数据的大小,对于非关键帧传0
 in是指向当前要处理的帧的头信息的指针
 in_size是当前要处理的帧大小(nal_size)
*/
static int alloc_and_copy(AVPacket *out,const uint8_t *sps_pps, uint32_t sps_pps_size,const uint8_t *in, uint32_t in_size)
{
   
    uint32_t offset = out->size; // 偏移量,就是out已有数据的大小,后面再写入数据就要从偏移量处开始操作
    // 特征码的大小,SPS/PPS占4字节,其余占3字节
    uint8_t nal_header_size = sps_pps==NULL ? 3 : 4;
    int err;

    // 每次处理前都要对out进行扩容,扩容的大小就是此次要写入的内容的大小,也就是特征码大小加上sps/pps大小加上加上本帧数据大小
    if ((err = av_grow_packet(out, sps_pps_size + in_size + nal_header_size)) < 0)
        return err;

    // 1.如果有sps_pps则先将sps_pps拷贝进out(memcpy()函数用于内存拷贝,第一个参数为拷贝要存储的地方,第二个参数是要拷贝的内容,第三个参数是拷贝内容的大小)
    if (sps_pps)
    {
   
        memcpy(out->data + offset, sps_pps, sps_pps_size);
    }

    // 2.再设置特征码(sps/pps特征码4位0x00000001,其他的特征码3位0x000001)
    for (int i = 0; i < nal_header_size; i++)
    {
   
       (out->data+offset+sps_pps_size)[i] = i==nal_header_size-1 ? 1 : 0; 
    }
    
    // 3.最后再拷贝NALU数据(当前处理的帧数据)
    memcpy(out->data + sps_pps_size + nal_header_size + offset, in, in_size);

    return 0;
}

/*
读取并拷贝sps/pps数据
codec_extradata是codecpar的扩展数据,sps/pps数据就在这个扩展数据里面
codec_extradata_size是扩展数据大小
out_extradata是输出sps/pps数据的AVPacket包
padding:就是宏AV_INPUT_BUFFER_PADDING_SIZE的值(64),是用于解码的输入流的末尾必要的额外字节个数,需要它主要是因为一些优化的流读取器一次读取32或者64比特,可能会读取超过size大小内存的末尾。
*/
int h264_extradata_to_annexb(const uint8_t *codec_extradata, const int codec_extradata_size, AVPacket *out_extradata, int padding)
{
   
    uint16_t unit_size; // sps/pps数据长度
    uint64_t total_size = 0; // 所有sps/pps数据长度加上其特征码长度后的总长度

    for (int i = 0; i < codec_extradata_size; ++i)
    {
   
        printf("%02x ",*(codec_extradata+i));
    }
    
    /*
        out:是一个指向一段内存的指针,这段内存用于存放所有拷贝的sps/pps数据和其特征码数据
        unit_nb:sps/pps个数
        sps_done:sps数据是否已经处理完毕
        sps_seen:是否有sps数据
        pps_seen:是否有pps数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值