ffmpeg源码修改以适应海康摄像头rtsp流的aac音频

最近在测试通过rtsp采集海康的摄像头,以前海康的摄像头采用g711音频,没出现什么问题,这次的设备升级成了aac音频,结果使用ffmpeg出现音频格式不能识别的问题,报什么Audio object type 0的错误,查看发现是解码器的objecttype成了0造成的,于是查看源码。

跟踪发现打开rtsp时发来的sdp信息中音频的信息如下:

a=rtpmap:104 mpeg4-generic/16000/1
a=fmtp:104 profile-level-id=15; streamtype=5; mode=AAC-hbr; config=0400;SizeLength=13; IndexLength=3; IndexDeltaLength=3; Profile=1;
a=Media_header:MEDIAINFO=494D4B48010100000400010001200110803E000000FA", '0' <repeats 36 times>, ";
a=appversion:1.0

其中音频的Profile与objecttype是对应的,而音频采样率和声道数也已经给出了,但在程序中调试时该值却始终不对。

到网上查资料得知,config参数实际上包含了objecttype和采样率及声道数,于是采用网上给出的公式

  audioSpecificConfig[0] = (audioObjectType<<3) | (samplerateindex>>1);
  audioSpecificConfig[1] = (samplerateindex<<7) | (channels<<3);

算了一下,发现海康摄像头给出的sdp中的config和profile、samplerateindex、channels对不上,于是对ffmpeg的源码做出了修改:

要修改的文件是rtpdec_mpeg4.c

首先需要定义的取值,于是定义了

const int avpriv_aac_sample_rates[16] = {
    96000, 88200, 64000, 48000, 44100, 32000,
    24000, 22050, 16000, 12000, 11025, 8000, 7350
};

然后profile在PayloadContext中没有定义,ffmpeg源码没有对此值进行分析,于是在PayloadContext的定义中添加

    char* profile;

此处添加该值为指针,并修改

static PayloadContext *new_context(void)
{
    PayloadContext * newval = av_mallocz(sizeof(PayloadContext));
    newval->profile = NULL;
    return newval;
}

方便在后面sdp有没有出现过profile这个项。

接下来在static const AttrNameMap attr_names[]的定义中添加

    { "Profile",       ATTR_NAME_TYPE_STR,
      offsetof(PayloadContext, profile) },

以方便在后面分析sdp取得profile值。

修改

static int parse_fmtp(AVFormatContext *s,
                      AVStream *stream, PayloadContext *data,
                      char *attr, char *value)
{
    AVCodecContext *codec = stream->codec;
    int res, i;


    if (!strcmp(attr, "config")) {
        if (data->profile == NULL)
        {
            res = parse_fmtp_config(codec, value);
            if (res < 0)
                return res;
        }

    }


    if (codec->codec_id == AV_CODEC_ID_AAC) {
        /* Looking for a known attribute */
        for (i = 0; attr_names[i].str; ++i) {
            if (!av_strcasecmp(attr, attr_names[i].str)) {
                if (attr_names[i].type == ATTR_NAME_TYPE_INT) {
                    *(int *)((char *)data+
                        attr_names[i].offset) = atoi(value);
                } else if (attr_names[i].type == ATTR_NAME_TYPE_STR)
                    *(char **)((char *)data+
                        attr_names[i].offset) = av_strdup(value);
            }
            if ((!strcmp(attr, "Profile"))&&(data->profile != NULL))
            {
                PutBitContext pb;
                av_free(codec->extradata);
                if (ff_alloc_extradata(codec, 2))
                    return AVERROR(ENOMEM);
                init_put_bits(&pb, codec->extradata, codec->extradata_size*8);
                codec->profile = atoi(data->profile);
                put_bits(&pb, 5, codec->profile + 1);
                for (int i = 0; i < 16; i++)
                    if (codec->sample_rate == avpriv_aac_sample_rates[i])
                    {
                        put_bits(&pb, 4, i);
                        break;
                    }
                put_bits(&pb, 4, codec->channels);
                flush_put_bits(&pb);
            }

        }


    }
    return 0;
}

修改的逻辑是,当sdp中没有profile时,将config写入到codec的extradata中;如果sdp中有profile,那么config将不会写入,已写入的也会被profile、samplerateindex、channels的值给替换掉。这样就保证了在没有profile时采用sdp的config,有则采用profile。


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值