【AVD】FFmpeg 音频编码时 SampleFormat 的选择,报错 “Specified sample format s16 is invalid or not supported.“ 的解决

21 篇文章 0 订阅

最近业务需要一个转码接口,这个转码接口将用于各平台(Linux、Android、iOS 等),在开发过程中发现,整个接口在 Linux 上运行良好,但在 Android 端却无法正常转码。

根据自己添加的 Log 发现,在初始化音频编码器时发生了问题。经调试发现,起初是因为直接将 codecId == AAC 的编码器用 avcodec_find_encoder_by_name("libfdk_aac) 写死成用 libfdk_aac 库了,而 Android 端因为缺少 libfdk_aac 这个库而无法正常编码音频。
由于 Linux 上直接 apt-get install 就能安装相关库,但是 Android 端却需要自己在 Linux 环境下载源码,再交叉编译、设置 Android 工具链等一堆复杂操作。同时,由于 Android 端只是一个预览的作用,因此,没必要再去折腾 libfdk_aac 在 Android 平台的库的编译了。直接使用安卓默认自带的 aac 库编码就可以了。
但是后来仔细看了下,代码中还有一句 codec = codec ? codec : avcodec_find_encoder(codecId),也就是说,如果通过 name 找不到 libfdk_aac 的话,它也是会通过 codecId 找到 aac 这个编码器的。
我在 Linux 上把 avcodec_find_encoder_by_name("libfdk_aac) 这一句注释掉之后,发现在 Linux 上也无法正常编码音频了,而且报了个来自 FFmpeg 的 bug:Specified sample format s16 is invalid or not supported.
这句话,曾经也遇到过,但一直没仔细看。于是,开始查源码,看这一句是从哪里报出来的。
很清楚地,全 FFmpeg 源码只有一处 “Specified sample format”,通过查看这一句所在的上下文可以知道,FFmpeg 先遍历了 avctx->codec->sample_fmts[i],也就是编码器所支持的那些 sample_fmts,如果 avctx->sample_fmt == avctx->codec->sample_fmts[i],也就是说,如果用户设置的输入采样点采样格式 avctx->sample_fmt 与当前遍历的编码器所支持的采样点格式 avctx->codec->sample_fmts[i] 匹配,就 break,进入下一个 if,而这下一个 if 就只判断,当前的 i 所指向的 avctx->codec->sample_fmt[i] 是不是 AV_SAMPLE_FMT_NONE,如果是,就说明把编码器所支持的所有采样格式都遍历完成了,都没有跟用户设置的输入采样点的采样格式匹配起来。这就报出了 Specified sample format s16 is invalid or not supported. 这个错误。

// libavcodec/utils.c/791line
if (avctx->codec->sample_fmts) {
    for (i = 0; avctx->codec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; i++) {
        if (avctx->sample_fmt == avctx->codec->sample_fmts[i])
            break;
        if (avctx->channels == 1 &&
            av_get_planar_sample_fmt(avctx->sample_fmt) ==
            av_get_planar_sample_fmt(avctx->codec->sample_fmts[i])) {
            avctx->sample_fmt = avctx->codec->sample_fmts[i];
            break;
        }
    }
    if (avctx->codec->sample_fmts[i] == AV_SAMPLE_FMT_NONE) {
        char buf[128];
        snprintf(buf, sizeof(buf), "%d", avctx->sample_fmt);
        av_log(avctx, AV_LOG_ERROR, "Specified sample format %s is invalid or not supported\n",
               (char *)av_x_if_null(av_get_sample_fmt_name(avctx->sample_fmt), buf));
        ret = AVERROR(EINVAL);
        goto free_and_end;
    }
}

因此,设计编码接口时,不能直接拿用户给的采样格式输入到上下文环境(AVCodecContext)。而要判断一下,用户选择的 codecID 在系统上都有哪些支持的编码器,这些支持的编码器都支持哪些采样格式。如果用户给的输入采样格式与编码器支持的采样格式不匹配,则需要报个警告,或者添加个重采样器转一下采样格式。
因此,我对代码进行了些优化,先让采样格式等于编码器支持的第一个格式,然后开始轮询编码器支持的所有格式与用户给的格式进行匹配判断,如有匹配成功,则让采样格式等于用户给的格式,否则,就等于编码器支持的第一个格式。

// pAudioCodec: AVCodec*,由 avcodec_find_encoder(codec_id) 返回。
// pDestAudioInfo:自定义格式,用户输入的音频信息。
AVSampleFormat sf = pAudioCodec->sample_fmts[0];
for (int i = 0; pAudioCodec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; i++) {
  if (pAudioCodec->sample_fmts[i] == pDestAudioInfo->sample_format) {
    sf = pAudioCodec->sample_fmts[i];
    break;
  }
}
codec_context_->sample_fmt = sf;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

深海Enoch

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

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

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

打赏作者

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

抵扣说明:

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

余额充值