0
点赞
收藏
分享

微信扫一扫

ShiftMediaProject项目介绍—H264 相关处理函数分析

海滨公园 06-16 21:00 阅读 8

h264_probe码流探测

函数调用
avformat_open_input
s->iformat->read_header(s)
函数说明
/**
Read the format header and initialize the AVFormatContext
structure. Return 0 if OK. 'avformat_new_stream' should be
called to create new streams.
/
int (read_header)(struct AVFormatContext *);
从read_header函数调用,去探测h264的码流格式


ff_h264_parse_sprop_parameter_sets

 RTSP协议中SDP记录了视音频媒体流的编码信息,其中针对H264码流数据的格式信息,也就是SPS/PPS,进行了Base64编码,保存在字段sprop-parameter-sets中

ff_h264_parse_sprop_parameter_sets函数就是用来解析出SPS/PPS(先进行Base64解码,然后添加起始码0x0001),保存在AVFormatContext->streams[videoIndex]->codecpar->extradata字段中。avio_open2函数将会在打开H264解码器的时候,会读取该字段信息,获取视频图像的详细信息,例如图像宽高等。

SDP信息

v=0
o=- 1626364324874146 1626364324874146 IN IP4 192.168.18.204
s=Media Presentation
e=NONE
b=AS:5050
t=0 0
a=control:rtsp://192.168.18.204:554/h264/ch1/main/av_stream/
m=video 0 RTP/AVP 96
b=AS:5000
a=control:rtsp://192.168.18.204:554/h264/ch1/main/av_stream/trackID=1
a=rtpmap:96 H264/90000
a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z0IAKpY1QPAET8s3AQEBAg==,aM48gA==
a=Media_header:MEDIAINFO=494D4B48010100000400000100000000000000000000000000000000000000000000000000000000;
a=appversion:1.0

int ff_h264_parse_sprop_parameter_sets(AVFormatContext *s,
                                       uint8_t **data_ptr, int *size_ptr,
                                       const char *value)
{//刚进入代码段,*value = "Z0IAKpY1QPAET8s3AQEBAg==,aM48gA=="   data_ptr = NULL;
    char base64packet[1024];
    uint8_t decoded_packet[1024];
    int packet_size;

    while (*value){
//第一次通过dst保存数据的头指针//第二次循环,因为这个时候*value = "aM48gA==",继续进行解码
        char *dst = base64packet;

//第一次将value字符串拷贝一份到dst指针,当遇到逗号,停止赋值
        while (*value && *value != ','
               && (dst - base64packet) < sizeof(base64packet) - 1)       {
            *dst++ = *value++;
        }        *dst++ = '\0';//添加结束符
//第一次当代码走到这里,*value=",aM48gA==",   base64packet[] = "Z0IAKpY1QPAET8s3AQEBAg=="
//第二次base64packet[] = "aM48gA=="
//第一次去掉value字符串中的逗号
        if (*value == ',')
            value++;
//base64解码结果,保存在decoded_packet
        packet_size = av_base64_decode(decoded_packet, base64packet,
                                       sizeof(decoded_packet));
        if (packet_size > 0) {
//对申请的内存进行对齐
            uint8_t *dest = av_realloc(*data_ptr,
                                       packet_size + sizeof(start_sequence) +
                                       *size_ptr +
                                      AV_INPUT_BUFFER_PADDING_SIZE);
            if (!dest) {
                av_log(s, AV_LOG_ERROR,
                       "Unable to allocate memory for extradata!\n");
                return AVERROR(ENOMEM);
            }
            *data_ptr = dest;
//static const uint8_t start_sequence[] = { 0, 0, 0, 1 };在数据开头添加起始码
            memcpy(dest + *size_ptr, start_sequence,
                   sizeof(start_sequence));
            memcpy(dest + *size_ptr + sizeof(start_sequence),
                   decoded_packet, packet_size);
            memset(dest + *size_ptr + sizeof(start_sequence) +
                   packet_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);

            *size_ptr += sizeof(start_sequence) + packet_size;
        }
    }

    return 0;
}

调用结果

par->extradata:00 00 00 01 67 42 00 2a 96 35 40 f0 04 4f cb 37 01 01 01 02 00 00 00 01 68 ce 3c 80
par->extradata_size:28

说明00 00 00 01 67开头是SPS,00 00 00 01 68是PPS

代码调用逻辑堆栈

>    ffplayd.exe!sdp_parse_fmtp_config_h264(AVFormatContext * s, AVStream * stream, PayloadContext * h264_data, const char * attr, const char * value) 行 177    C
     ffplayd.exe!ff_parse_fmtp(AVFormatContext * s, AVStream * stream, PayloadContext * data, const char * p, int(*)(AVFormatContext *, AVStream *, PayloadContext *, const char *, const char *) parse_fmtp) 行 917    C
     ffplayd.exe!parse_h264_sdp_line(AVFormatContext * s, int st_index, PayloadContext * h264_data, const char * line) 行 403    C
     ffplayd.exe!parse_fmtp(AVFormatContext * s, RTSPState * rt, int payload_type, const char * line) 行 388    C
     ffplayd.exe!sdp_parse_line(AVFormatContext * s, SDPParseState * s1, int letter, const char * buf) 行 587    C
     ffplayd.exe!ff_sdp_parse(AVFormatContext * s, const char * content) 行 721    C
     ffplayd.exe!ff_rtsp_setup_input_streams(AVFormatContext * s, RTSPMessageHeader * reply) 行 622    C
     ffplayd.exe!ff_rtsp_connect(AVFormatContext * s) 行 1897    C
     ffplayd.exe!rtsp_read_header(AVFormatContext * s) 行 726    C
     ffplayd.exe!avformat_open_input(AVFormatContext * * ps, const char * filename, AVInputFormat * fmt, AVDictionary * * options) 行 631    C
     ffplayd.exe!read_thread(void * arg) 行 2783    C
     ffplayd.exe!SDL_RunThread(void * data) 行 283    C
     ffplayd.exe!RunThread(void * data) 行 91    C
     ffplayd.exe!RunThreadViaBeginThreadEx(void * data) 行 106    C
     ucrtbased.dll!00007ffa01684fb8()    未知
     ucrtbased.dll!00007ffa01684bf1()    未知
     kernel32.dll!00007ffa572b7c24()    未知
     ntdll.dll!00007ffa578ed4d1()    未知


extradata赋值过程

经过avformat_open_input函数返回之后,SPS/PPS保存位置AVFormatContext->streams[videoIndex]->codecpar->extradata

2 avformat_find_stream_info函数内部对extradata的分析

AVCodecContext *avctx = st->internal->avctx;

其中st是媒体流

avcodec_parameters_to_context(avctx, st->codecpar);

经过这个操作,已经将streams[videoIndex]->codecpar->extradata拷贝到avctx->extradata

接着开始打开解码器

avcodec_open2(AVCodecContext * avctx, const AVCodec * codec, AVDictionary * * options)  

进行解码器的初始化

h264_decode_init(AVCodecContext * avctx)  

然后在函数体中,开始解析

   if (avctx->extradata_size > 0 && avctx->extradata) {

       ret = ff_h264_decode_extradata(avctx->extradata, avctx->extradata_size,

                                      &h->ps, &h->is_avc, &h->nal_length_size,

                                      avctx->err_recognition, avctx);

       if (ret < 0) {

           h264_decode_end(avctx);

           return ret;

       }

   }


Bitstream Filter

(1)主要目的是对数据进行格式转换,使它能够被解码器处理(比如HEVC QSV的解码器)。

(2)Bitstream Filter对已编码的码流进行操作,不涉及解码过程。

(3)使用ffmpeg -bsfs命令可以查看ffmpeg工具支持的Bitstream Filter类型。

(4)使用ff*工具的 -bsf 选项来指定对具体流的Bitstream Filter,使用逗号分割的多个filter,如果filter有参数, 参数名和参数值跟在filter名称后面。

bsf的使用方法:

(1)使用查询函数av_bsf_get_by_name 根据名称查找对应的AVBitstreamFilter。

(2)使用av_bsf_alloc为AVBSFContext和他的一些内部变量分配内存。

(3)设置AVBSFContext可能需要使用一些变解码参数和time_base[2].

(4)在设置好参数之后使用av_bsf_init来初始化bsf.

(5)使用av_bsf_send_packet函数输入数据,并使用av_bsf_receive_packet获得处理后的输出数据。

(6)处理结束后,av_bsf_free清理用于上下文和内部缓冲区的内存。


H264封装成MP4

FFmpeg 4.4版本

xtern "C"
{
#include "libavformat/avformat.h"
    //引入时间
#include "libavutil/time.h"
}//引入库
#pragma comment(lib,"avformat.lib")
//工具库,包括获取错误信息等
#pragma comment(lib,"avutil.lib")
//编解码的库
#pragma comment(lib,"avcodec.lib")int avError(int errNum)
{
    char buf[1024] = { 0 };
    av_strerror(errNum, buf, sizeof(buf));
    std::cout << "failed info:" << buf << std::endl;
    return -1;
}int TestH264ToMP4()
{
    int videoIndex = -1;    avformat_network_init();
    const char* pszFile = "D:/hls/mytest.h264";
    const char* pszRTMPURL = "F:/hls/home/test.mp4";    AVFormatContext* pInputAVFormatContext = NULL;
    AVOutputFormat* pAVOutputFormat = NULL;    int nRet = avformat_open_input(&pInputAVFormatContext, pszFile, 0, NULL);
    if (nRet < 0)
    {
        return avError(nRet);
    }    nRet = avformat_find_stream_info(pInputAVFormatContext, 0);
    if (nRet != 0)
    {
        return avError(nRet);
    }
    av_dump_format(pInputAVFormatContext, 0, pszFile, 0);    AVFormatContext* pOutputAVFormatContext = NULL;
    nRet = avformat_alloc_output_context2(&pOutputAVFormatContext, NULL, "mp4", pszRTMPURL);
    if (nRet < 0)
    {
        return avError(nRet);
    }    pAVOutputFormat = pOutputAVFormatContext->oformat;
    for (int i = 0; i < pInputAVFormatContext->nb_streams; i++)
    {
        AVStream* pInputAVStream = pInputAVFormatContext->streams[i];
        AVStream* pOutputAVStream = avformat_new_stream(pOutputAVFormatContext, 0);
        nRet = avcodec_parameters_copy(pOutputAVStream->codecpar, pInputAVStream->codecpar);
        if (nRet < 0)
        {
            return avError(nRet);
        }
        pOutputAVStream->codecpar->codec_tag = 0;
    }
    for (int i = 0; i < pInputAVFormatContext->nb_streams; i++)
    {
        if (pInputAVFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoIndex = i;
            break;
        }
    }    av_dump_format(pOutputAVFormatContext, 0, pszRTMPURL, 1);
    nRet = avio_open(&pOutputAVFormatContext->pb, pszRTMPURL, AVIO_FLAG_WRITE);
    if (nRet < 0)
    {
        return avError(nRet);
    }    nRet = avformat_write_header(pOutputAVFormatContext, NULL);
    if (nRet < 0)
    {
        return avError(nRet);
    }    AVPacket pkt;
    std::int64_t llStartTime = av_gettime();
    std::int64_t llFrameIndex = 0;
    while (true)
    {
        AVStream* pInputStream = NULL;
        AVStream* pOutputStream = NULL;
        nRet = av_read_frame(pInputAVFormatContext, &pkt);
        if (nRet < 0)
        {
            break;
        }        if (pkt.pts == AV_NOPTS_VALUE)
        {
            AVRational time_base1 = pInputAVFormatContext->streams[videoIndex]->time_base;
            std::int64_t llCalcDuration = (double)AV_TIME_BASE / av_q2d(pInputAVFormatContext->streams[videoIndex]->r_frame_rate);
            pkt.pts = (double)(llFrameIndex * llCalcDuration) / (double(av_q2d(time_base1)*AV_TIME_BASE));
            pkt.dts = pkt.pts;
            pkt.duration = (double)llCalcDuration / (double)(av_q2d(time_base1)*AV_TIME_BASE);
        }        if (pkt.stream_index == videoIndex)
        {
            AVRational time_base = pInputAVFormatContext->streams[videoIndex]->time_base;
            AVRational time_base_q = { 1, AV_TIME_BASE };
            std::int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q);
            std::int64_t now_time = av_gettime() - llStartTime;
        }        pInputStream = pInputAVFormatContext->streams[pkt.stream_index];
        pOutputStream = pOutputAVFormatContext->streams[pkt.stream_index];
        pkt.pts = av_rescale_q_rnd(pkt.pts, pInputStream->time_base, pOutputStream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        pkt.dts = av_rescale_q_rnd(pkt.dts, pInputStream->time_base, pOutputStream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        pkt.duration = (int)av_rescale_q(pkt.duration, pInputStream->time_base, pOutputStream->time_base);
        //字节流的位置,-1 表示不知道字节流位置
        pkt.pos = -1;        if (pkt.stream_index == videoIndex)
        {
            llFrameIndex++;
        }        nRet = av_interleaved_write_frame(pOutputAVFormatContext, &pkt);
        if (nRet < 0) {
            printf("发送数据包出错\n");
            break;
        }        av_packet_unref(&pkt);
    }
    av_write_trailer(pOutputAVFormatContext);
    if (!(pOutputAVFormatContext->oformat->flags & AVFMT_NOFILE))
    {
        avio_close(pOutputAVFormatContext->pb);
    }
    avformat_free_context(pOutputAVFormatContext);
    avformat_close_input(&pInputAVFormatContext);
    return 0;
}

RTSP h264编码修改为h265

场景

missing picture in access unit with size

data partitioning is not implemented. Update your FFmpeg version to the newest one from Git. If the problem still occurs, it means that your file has a feature which has not been implemented.
[h264 @ 00000216C008C5C0] If you want to help, upload a sample of this file to ftp://upload.ffmpeg.org/incoming/ and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)

检测到传输的编码格式不符合H264,因为传输过来的是H265

代码

static inline int parse_nal_units(AVCodecParserContext *s,
                                  AVCodecContext *avctx,
                                  const uint8_t * const buf, int buf_size)
      /* didn't find a picture! */
    av_log(avctx, AV_LOG_ERROR, "missing picture in access unit with size %d\n", buf_size);
fail:
    av_freep(&rbsp.rbsp_buffer);
    return -1;

堆栈

ffplayd.exe!av_log(void * avcl, int level, const char * fmt, ...) 行 363	C
>	ffplayd.exe!parse_nal_units(AVCodecParserContext * s, AVCodecContext * avctx, const unsigned char * const buf, int buf_size) 行 568	C
 	ffplayd.exe!h264_parse(AVCodecParserContext * s, AVCodecContext * avctx, const unsigned char * * poutbuf, int * poutbuf_size, const unsigned char * buf, int buf_size) 行 609	C
 	ffplayd.exe!av_parser_parse2(AVCodecParserContext * s, AVCodecContext * avctx, unsigned char * * poutbuf, int * poutbuf_size, const unsigned char * buf, int buf_size, __int64 pts, __int64 dts, __int64 pos) 行 166	C
 	ffplayd.exe!parse_packet(AVFormatContext * s, AVPacket * pkt, int stream_index) 行 1461	C
 	ffplayd.exe!read_frame_internal(AVFormatContext * s, AVPacket * pkt) 行 1591	C
 	ffplayd.exe!av_read_frame(AVFormatContext * s, AVPacket * pkt) 行 1776	C
 	ffplayd.exe!read_thread(void * arg) 行 3008	C
 	ffplayd.exe!SDL_RunThread(void * data) 行 283	C
 	ffplayd.exe!RunThread(void * data) 行 91	C
 	ffplayd.exe!RunThreadViaBeginThreadEx(void * data) 行 106	C
 	ucrtbased.dll!00007ff8d8434fb8()	未知
 	ucrtbased.dll!00007ff8d8434bf1()	未知
 	kernel32.dll!00007ff922d17c24()	未知
 	ntdll.dll!00007ff922e6d721()	未知

举报

相关推荐

0 条评论