ffmpeg学习 编码为h264裸流添加sps、pps

本文介绍了在使用ffmpeg进行h264编码时,如何处理SPS和PPS数据。在实时流中,通常需要在每个I帧前添加SPS和PPS,以确保解码正确。通过示例代码展示了手动添加SPS/PPS到I帧前的过程,并解释了编码器中的flag设置对这一过程的影响。

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

在h264流中,有两种NALU极其的重要,序列参数集(Sequence Paramater Set,SPS)和图像参数集(Picture ParamaterSet,PPS)。

SPS中的信息至关重要,记录了编码的prfile、level、图像宽高等,如果其中的数据丢失或出现错误,那么解码过程很可能会失败。每一帧编码后数据所依赖的参数保存于PPS中。

解决问题:一般情况SPS和PPS的NAL Unit通常位于整个码流的起始位置。封装文件一般进保存一次,位于文件头部,sps/sps再整个解码过程中复用,不发生变化。然而对于实时流,通常是从流中间开始解码,因此需要在每个I帧前添加SPS和PPS;如果编码器在编码过程中改变了码流参数(如分辨率),需要重新调整SPS和PPS数据

ffmpeg中SPS、PPS数据

从输入流中解析时,位于AVFormatContext->streams[video_index]->codecpar->extradata中。
编码时,sps/pps数据存放于编码器上下文AVCodecContext->extradata中,但是该对象通常是空指针,还需要进行额外设置。

通常我们编码保存裸流时,仅第一个I帧前有SPS/PPS数据(见后续代码示例分析)。但是,如果需要做实时流传输,必须要在每一个I帧前添加SPS和PPS

示例代码

这里先以AVDevice打开摄像头,将原始的rawvideo编码的yuv数据解析后,进行h264编码直接保存,查看其sps和pps数据保存情况。

#include <stdio.h>

#ifdef __cplusplus  
extern "C" {
   
   
#endif  

#include "libavformat/avformat.h"

#include "libavdevice/avdevice.h"

#ifdef __cplusplus  
}
#endif 

#include <signal.h>
bool bRuning = true;
void sig_handle(int sig_num)
{
   
   
    bRuning = false;
}

int main()
{
   
     
    signal(SIGINT, sig_handle);

    int ret;

    avdevice_register_all(); // 必须执行,否则av_find_input_format失败

    const char* input_file = "/dev/video0";

    AVDictionary *options = NULL;  
    av_dict_set(&options, "f", "v4l2", 0);                  // valid, for replacing AVInputFormat argument
    
    //av_dict_set(&options, "input_format", "h264", 0);    // valid
    av_dict_set(&options, "pixel_format", "yuv420p", 0);    // valid
    //av_dict_set(&options, "input_format", "yuv420p", 0);    // valid , 同上

    av_dict_set(&options, "video_size", "1280x720", 0);     // valid
    av_dict_set(&options, "framerate", "25", 0);            // valid


    AVFormatContext* input_fmt_ctx = NULL; // 必须设置NULL
    if((ret = avformat_open_input(&input_fmt_ctx, input_file, NULL, &options)) < 0) {
   
   
        av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");
        return ret;
    }

    // 分析流信息
    if((ret = avformat_find_stream_info(input_fmt_ctx, NULL)) < 0) {
   
   
        av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");
        return ret;
    }

    // 打印信息
    av_dump_format(input_fmt_ctx, 0, input_file, 0);

    //---------------------- 解码部分 ----------------------// 
    int video_stream_index = -1;
    AVCodec *video_codec;
    AVCodecContext *video_decoder_ctx;

    // 查找视频流 
    //if((ret = av_find_best_stream(input_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &video_codec, -1)) < 0) {
   
   
    if((ret = av_find_best_stream(input_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, -1)) < 0) {
   
   
        av_log(NULL, AV_LOG_ERROR, "Cannot find an video stream in the input file\n");
        avformat_close_input(&input_fmt_ctx);
        return ret;
    }
    video_stream_index = ret;

    // // 解码器初始化
    AVCodecParameters *codecpar = input_fmt_ctx->streams[video_stream_index]->codecpar;
    video_codec = avcodec_find_decoder(codecpar->codec_id);
    if(!video_codec) {
   
   
        av_log(NULL, AV_LOG_ERROR, "Can't find decoder\n");
        return -1;
    }

    video_decoder_ctx = avcodec_alloc_context3(video_codec);
    if(!video_decoder_ctx) {
   
   
        av_log(NULL, AV_LOG_ERROR, "C
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aworkholic

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值