【Jetson AGX Xavier-FFmpeg SEI Unregistered信息提取】

文章讨论了在使用FFmpeg4.2+x264和4.4+nvmpi解码IP相机的RTSP流时,H264编码中的自定义SEI时间戳问题。FFmpeg4.4软解码能解析SEI并匹配OSD时间,而4.4+nvmpi硬解码下未发现相应数据,推测与解码器实现有关,需进一步研究。

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

前言

IP相机RTSP取H264码流,其定义了自定义SEI信息,具体信息为时间戳,但在调试时发现使用FFmpeg4.2+x264软解码或FFmpeg4.2+nvmpi硬解码时,解析出的时间戳信息与相机叠加在图像上的OSD时间不对应。通过分析源码等手段进行问题分析,解码过程中AVPacket和AVFrame存在队列处理?(待进一步研究),并通过请教其他人,发现FFmpeg4.4及以上版本中,AVFrame中AVFrameSideData附带对应解码出此Frame的AVPacket中的Unregistered SEI信息,因此在Jetson AGX Xavier(Jetpack4.4)搭建FFmpeg4.4+x264+nvmpi测试环境(环境搭建已另外一个帖子中记录),测试AVFrame-AVFrameSideData中Unregistered 信息是否与AVPacket中SEI自定义时间戳信息是否一致。

测试环境:FFmpeg4.4+x264+nvmpi测试环境(环境搭建见:FFmpeg4.4+x264+nvmpi测试环境搭建

测试代码

#include <string>
#include <iostream>
#include <opencv2/opencv.hpp>

extern "C"
{
#include "libavutil/imgutils.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
}

int main()
{
    av_register_all();
    avcodec_register_all();
    avformat_network_init();
    av_log_set_level(AV_LOG_QUIET);

    AVFormatContext *fmt_ctx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVStream *st;
    AVPacket *pkt;
    AVFrame *pFrame;

    char *url = "xxx"; //rtsp取流地址

    AVDictionary *opts = NULL;
    av_dict_set(&opts, "rtsp_transport", "udp", 0);
    av_dict_set(&opts, "stimeout", "1500000", 0);
    av_dict_set(&opts, "max_delay", "300000", 0);
    av_dict_set(&opts, "buffer_size", "2048000", 0);

    fmt_ctx = NULL;

    // 获取上下文
    if (avformat_open_input(&fmt_ctx, url, NULL, &opts) != 0)
    {
        printf("Couldn't open input stream.\n");
        return -1;
    }

    if (avformat_find_stream_info(fmt_ctx, NULL) < 0)
    {
        printf("Couldn't find stream information.\n");
        return -1;
    }

    int videoindex = -1;
    for (int i = 0; i < fmt_ctx->nb_streams; i++) // find video stream index
    {
        st = fmt_ctx->streams[i];
        if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoindex = i;
        }
    }

    if (-1 == videoindex)
    {
        printf("No H.264 video stream in the input file\n");
        return -1;
    }

    // 获取打开解码器
    pCodecCtx = avcodec_alloc_context3(NULL);
    if (avcodec_parameters_to_context(pCodecCtx, fmt_ctx->streams[videoindex]->codecpar) < 0)
    {
        printf("avcodec_parameters_to_context failed\n");
        return -1;
    }
    if (pCodecCtx->codec_id == AV_CODEC_ID_H264)
    {
        // pCodec = avcodec_find_decoder_by_name("h264_nvmpi");
        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    }
    
    if (pCodec == NULL)
    {
        printf("Codec not found.\n");
        return -1;
    }
    pCodecCtx->flags |= AV_CODEC_FLAG_LOW_DELAY; //加上这行好像对解码器时间延迟没什么用???
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
        printf("open codec failed!\n");
        return -1;
    }

    // 取流/解码/叠图/显示
    pFrame = av_frame_alloc();
    pkt = (AVPacket *)av_malloc(sizeof(AVPacket));
    while (av_read_frame(fmt_ctx, pkt) >= 0)
    {
        if (pkt->stream_index == videoindex)
        {
            int ret = 0;
            int got_picture = 0;

            ret = avcodec_send_packet(pCodecCtx, pkt);
            if (ret < 0)
            {
                std::cout << "Error submitting a packet for decoding." << std::endl;
                continue;
            }
                
            while (1)
            {                
                ret = avcodec_receive_frame(pCodecCtx, pFrame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                    break;
                else if (ret < 0)
                {
                    std::cout << "Error during decoding." << std::endl;
                    return -1;
                }

                if (pFrame->nb_side_data > 0)
                {
                    const AVFrameSideData *sei_data = av_frame_get_side_data(pFrame, AV_FRAME_DATA_SEI_UNREGISTERED);
                    unsigned long long tmp_timestamp = 0;
                    // SEI中自定义信息结构为UUID+Data,其中UUID占16字节,Data占8字节
                    memcpy(&tmp_timestamp, sei_data->data + 16, sizeof(tmp_timestamp));
                }

            }
            // 省略时间戳叠图代码和图像显示
        }
        av_free_packet(pkt);
    }

    av_dict_free(&opts);
    avformat_close_input(&fmt_ctx);
    avcodec_close(pCodecCtx);
    av_frame_free(&pFrame);
    
    return 0;
}

结果

  1. FFmpeg4.4+x264软解码
    AVFrame中AVFrameSideData中可以解析出AV_FRAME_DATA_SEI_UNREGISTERED类型SEI信息,通过AVFrame时间戳与OSD时间比较,两者可以对应上

  2. FFmpeg4.4+nvmpi硬解码

    AVFrame中AVFrameSideData中无数据,应该与解码器实现有关,待进一步研究

如有不对之处,请大家指出。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值