音视频入门基础:RTP专题(19)——FFmpeg源码中,获取RTP的音频信息的实现(下)

embedded/2025/3/17 3:09:40/

本文接着《音视频入门基础:RTP专题(18)——FFmpeg源码中,获取RTP的音频信息的实现(上)》,继续讲解FFmpeg获取SDP描述的RTP流的音频信息到底是从哪个地方获取的。本文的一级标题从“四”开始。

四、音频采样率

SDP协议中,a=rtpmap属性和a=fmtp属性中的config参数都会包含音频采样率信息。FFmpeg源码中首先会判断config参数中是否存在音频信息,如果存在,那就会从config参数(config是《ISO/IEC 14496-3》中定义的音频对象类型特定解码器配置数据 AudioSpecificConfig)中获取音频采样率;如果不存在,则会从a=rtpmap属性中获取音频采样率:

由《音视频入门基础:AAC专题(11)——AudioSpecificConfig简介》可以知道,AudioSpecificConfig中存在一个占4位的samplingFrequencyIndex属性,表示音频的采样频率:

FFmpeg源码的aac_decode_init函数中,会判断avctx->extradata_size是否大于0(avctx->extradata_size为SDP协议中config参数携带的内容的长度),大于0才表示config参数携带信息,才会执行decode_audio_specific_config函数:

static av_cold int aac_decode_init(AVCodecContext *avctx)
{
//...if (avctx->extradata_size > 0) {if ((ret = decode_audio_specific_config(ac, ac->avctx, &ac->oc[1].m4ac,avctx->extradata,avctx->extradata_size * 8LL,1)) < 0)return ret;} 
//...
}

decode_audio_specific_config函数内部会调用decode_audio_specific_config_gb函数:

static int decode_audio_specific_config(AACDecContext *ac,AVCodecContext *avctx,MPEG4AudioConfig *m4ac,const uint8_t *data, int64_t bit_size,int sync_extension)
{
//...return decode_audio_specific_config_gb(ac, avctx, m4ac, &gb, 0,sync_extension);
}

由《音视频入门基础:AAC专题(12)——FFmpeg源码中,解码AudioSpecificConfig的实现》可以知道,

ff_mpeg4audio_get_config_gb函数中,通过语句:c->sample_rate = get_sample_rate(gb, &c->sampling_index)获取AudioSpecificConfig的samplingFrequencyIndex属性。执行decode_audio_specific_config_gb函数后,m4ac指向的变量会得到从AudioSpecificConfig中解码出来的属性:

static inline int get_sample_rate(GetBitContext *gb, int *index)
{*index = get_bits(gb, 4);return *index == 0x0f ? get_bits(gb, 24) :ff_mpeg4audio_sample_rates[*index];
}

然后在decode_audio_specific_config_gb函数外部,通过aac_decode_frame_int函数将上一步得到的samplingFrequencyIndex属性赋值给AVCodecContext的sample_rate:

static int aac_decode_frame_int(AVCodecContext *avctx, AVFrame *frame,int *got_frame_ptr, GetBitContext *gb,const AVPacket *avpkt)
{
//...if (ac->oc[1].status && audio_found) {avctx->sample_rate = ac->oc[1].m4ac.sample_rate << multiplier;avctx->frame_size = samples;ac->oc[1].status = OC_LOCKED;}
//...
}

然后在dump_stream_format函数中,通过avcodec_string函数中的语句:av_bprintf(&bprint, "%d Hz, ", enc->sample_rate)拿到上一步中得到的AVCodecContext的sample_rate。最后再在dump_stream_format函数中将profile打印出来:

void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode)
{
//...switch (enc->codec_type) {case AVMEDIA_TYPE_AUDIO:av_bprintf(&bprint, "%s", separator);if (enc->sample_rate) {av_bprintf(&bprint, "%d Hz, ", enc->sample_rate);}
//...}
//...
}

五、音频声道数

 FFmpeg获取SDP描述的RTP流的音频声道数,是从SDP的a=rtpmap属性获取的。比如SDP中某一行的内容为:

a=rtpmap:97 MPEG4-GENERIC/48000/2

该例子中,该行的“48000”后面的那个“2”就是音频声道数,表示是双声道(立体声)。

当识别到上述“a=rtpmap”这个<type>后,sdp_parse_line函数中会调用sdp_parse_rtpmap函数:

else if (av_strstart(p, "rtpmap:", &p) && s->nb_streams > 0) {/* NOTE: rtpmap is only supported AFTER the 'm=' tag */get_word(buf1, sizeof(buf1), &p);payload_type = atoi(buf1);rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1];if (rtsp_st->stream_index >= 0) {st = s->streams[rtsp_st->stream_index];sdp_parse_rtpmap(s, st, rtsp_st, payload_type, p);}s1->seen_rtpmap = 1;if (s1->seen_fmtp) {parse_fmtp(s, rt, payload_type, s1->delayed_fmtp);}} 

sdp_parse_rtpmap函数中会把a=rtpmap属性中的音频通道数提取出来,并通过语句:av_channel_layout_default(&par->ch_layout, i)把音频声道数赋值给par->ch_layout。par->ch_layout为指向一个AVCodecParameters类型变量的指针:

/* parse the rtpmap description: <codec_name>/<clock_rate>[/<other params>] */
static int sdp_parse_rtpmap(AVFormatContext *s,AVStream *st, RTSPStream *rtsp_st,int payload_type, const char *p)
{
//...switch (par->codec_type) {case AVMEDIA_TYPE_AUDIO://...par->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;if (i > 0) {par->sample_rate = i;avpriv_set_pts_info(st, 32, 1, par->sample_rate);get_word_sep(buf, sizeof(buf), "/", &p);i = atoi(buf);if (i > 0)av_channel_layout_default(&par->ch_layout, i);}}
//...
}

然后在sdp_parse_rtpmap函数外部,通过avcodec_parameters_to_context函数将AVCodecParameters的ch_layout赋值给AVCodecContext的ch_layout:

int avcodec_parameters_to_context(AVCodecContext *codec,const AVCodecParameters *par)
{
//...switch (par->codec_type) {case AVMEDIA_TYPE_AUDIO:ret = av_channel_layout_copy(&codec->ch_layout, &par->ch_layout);//....break;}
//...
}

然后在dump_stream_format函数中,通过avcodec_string函数中的语句:av_channel_layout_describe_bprint(&enc->ch_layout, &bprint)拿到AVCodecContext的ch_layout对应的音频声道数目。最后再在dump_stream_format函数中将音频声道数目打印出来:

void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode)
{
//...switch (enc->codec_type) {case AVMEDIA_TYPE_AUDIO:av_channel_layout_describe_bprint(&enc->ch_layout, &bprint);//...break;}
//...
}

所以FFmpeg获取SDP描述的RTP流的音频声道数,是从SDP的a=rtpmap属性获取的:

六、Bit depth

如果SDP描述的RTP流的音频压缩编码格式为AAC,FFmpeg会强制把Bit depth设置为fltp。这是因为对于有损压缩编解码器(如MP3和AAC),Bit depth是在编码期间计算的,并且可以因采样而异,Bit depth只对PCM数字信号有意义。具体可以参考:《音视频入门基础:AAC专题(3)——AAC的ADTS格式简介》。

可以看到在aac_decode_init函数中(该函数定义在libavcodec/aacdec_template.c),强制把音频采样格式设置成了AV_SAMPLE_FMT_FLTP:

static av_cold int aac_decode_init(AVCodecContext *avctx)
{
//...avctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
//...
}

所以如果SDP描述的RTP流的音频压缩编码格式为AAC,通过“ffmpeg -protocol_whitelist "file,rtp,udp" -i XXX.sdp命令”获取到的音频采样格式固定为fltp,该值没有意义:


http://www.ppmy.cn/embedded/173220.html

相关文章

通义万相2.1 图生视频:为AI绘梦插上翅膀,开启ALGC算力领域新纪元

通义万相2.1图生视频大模型 通义万相2.1图生视频技术架构万相2.1的功能特点性能优势与其他工具的集成方案 蓝耘平台部署万相2.1核心目标典型应用场景未来发展方向 通义万相2.1ALGC实战应用操作说明功能测试 为什么选择蓝耘智算蓝耘智算平台的优势如何通过API调用万相2.1 写在最…

C++使用ZeroMQ和MessagePack实现简单又轻量级的RPC框架

在现代的分布式系统中&#xff0c;远程过程调用&#xff08;RPC&#xff09;是一个非常重要的机制&#xff0c;它允许不同的服务或组件之间的通信&#xff0c;就像调用本地函数一样。本文将介绍如何使用ZeroMQ和MessagePack来构建一个轻量级的RPC框架&#xff0c;并提供一个简单…

Qt程序基于共享内存读写CodeSys的变量

文章目录 1.背景2.结构体从CodeSys导出后导入到C2.1.将结构体从CodeSys中导出2.2.将结构体从m4文件提取翻译成c格式 3.添加RTTR注册信息4.读取PLC变量值5.更改PLC变量值 1.背景 在文章【基于RTTR在C中实现结构体数据的多层级动态读写】中&#xff0c;我们实现了通过字符串读写…

Vue3 + Vite + Yarn + Fabricjs构建的开源演示系统

Next-Slides 本项目灵感来源于 Prezi&#xff0c;旨在提供一个现代化的在线演示工具&#xff0c;可以作为传统PPT的替代方案。项目采用 TypeScript Vue3 Vite Yarn 技术栈构建&#xff0c;专注于在线教育和会议演示场景&#xff0c;提供交互式课件和智能课件功能。 主仓库…

嵌入式学习L6网络编程D5UDP编程

网络编程 UDPclient端 /*udp demo */ /* usage:* ./client serv_ip serv_port */ #include "net.h" void usage(char *s) {printf("\nThis is udp demo!\n");printf("\nUsage:\n\t %s serv_ip serv_port",s);printf("\n\t serv_ip: udp …

周志华机器学习西瓜书 第九章 聚类-学习笔记

一、聚类任务 聚类是无监督学习中非常典型的任务&#xff0c;聚类的目的是将数据样本划分为若干个通常不相交的子集&#xff0c;每一个子集成为"簇-cluster"&#xff0c;其即可以作为一个单独过程&#xff0c;用于找寻数据内在的分布结构&#xff0c;也可作为分类等其…

【数据分析大屏】基于Django+Vue汽车销售数据分析可视化大屏(完整系统源码+数据库+开发笔记+详细部署教程+虚拟机分布式启动教程)✅

目录 一、项目背景 二、项目创新点 三、项目功能 四、开发技术介绍 五、项目功能展示 六、权威视频链接 一、项目背景 汽车行业数字化转型加速&#xff0c;销售数据多维分析需求激增。本项目针对传统报表系统交互性弱、实时性差等痛点&#xff0c;基于DjangoVue架构构建…

鸿蒙系统liteos_m开发环境配置

在工作中开发基于HC32F4A0的鸿蒙liteos_m的操作系统移植时&#xff0c;开发环境选的命令行模式&#xff0c;官方的参考请看链接《快速入门概述》 在ubuntu18.04环境中安装时&#xff0c;安装库和工具集时官方提供的安装库的指令无法进行安装&#xff0c;部分库应该是有安装顺序…