【Android FFMPEG 开发】FFMPEG 音频重采样 ( 初始化音频重采样上下文 SwrContext | 计算音频延迟 | 计算输出样本个数 | 音频重采样 swr_convert )(一)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【Android FFMPEG 开发】FFMPEG 音频重采样 ( 初始化音频重采样上下文 SwrContext | 计算音频延迟 | 计算输出样本个数 | 音频重采样 swr_convert )(一)

I . FFMPEG 播放视频流程


FFMPEG 播放视频流程 : 视频中包含图像和音频 ;



① FFMPEG 初始化 : 参考博客 【Android FFMPEG 开发】FFMPEG 初始化 ( 网络初始化 | 打开音视频 | 查找音视频流 )


② FFMPEG 获取 AVStream 音视频流 : 参考博客 【Android FFMPEG 开发】FFMPEG 获取 AVStream 音视频流 ( AVFormatContext 结构体 | 获取音视频流信息 | 获取音视频流个数 | 获取音视频流 )


③ FFMPEG 获取 AVCodec 编解码器 : 参考博客 【Android FFMPEG 开发】FFMPEG 获取编解码器 ( 获取编解码参数 | 查找编解码器 | 获取编解码器上下文 | 设置上下文参数 | 打开编解码器 )


④ FFMPEG 读取音视频流中的数据到 AVPacket : 参考博客 【Android FFMPEG 开发】FFMPEG 读取音视频流中的数据到 AVPacket ( 初始化 AVPacket 数据 | 读取 AVPacket )


⑤ FFMPEG 解码 AVPacket 数据到 AVFrame ( 音频 / 视频数据解码 ) : 参考博客 【Android FFMPEG 开发】FFMPEG 解码 AVPacket 数据到 AVFrame ( AVPacket->解码器 | 初始化 AVFrame | 解码为 AVFrame 数据 )


⑥ FFMPEG AVFrame 图像格式转换 YUV -> RGBA : 参考博客 【Android FFMPEG 开发】FFMPEG AVFrame 图像格式转换 YUV -> RGBA ( 获取 SwsContext | 初始化图像数据存储内存 | 图像格式转换 )


⑦ FFMPEG ANativeWindow 原生绘制 准备 : 参考博客 【Android FFMPEG 开发】FFMPEG ANativeWindow 原生绘制 ( Java 层获取 Surface | 传递画布到本地 | 创建 ANativeWindow )




II . FFMPEG 音频重采样流程


FFMPEG 音频重采样流程 :



〇 视频播放操作 : FFMPEG 环境初始化 , 获取 AVStream 音视频流 , 获取 AVCodec 编解码器 , 读取音视频流中的数据到 AVPacket , 解码 AVPacket 数据到 AVFrame , AVFrame 图像格式转换 YUV -> RGBA , ANativeWindow 原生绘制 ;



〇 音频播放操作 : FFMPEG 环境初始化 , 获取 AVStream 音视频流 , 获取 AVCodec 编解码器 , 读取音视频流中的数据到 AVPacket , 解码 AVPacket 数据到 AVFrame , 然后进行下面的操作 , 音频重采样 ;



① 初始化音频重采样上下文 : struct SwrContext *swr_alloc_set_opts( … ) , int swr_init(struct SwrContext *s)


SwrContext *swrContext = swr_alloc_set_opts(
        0 ,                     //现在还没有 SwrContext 上下文 , 先传入 0
        //输出的音频参数
        AV_CH_LAYOUT_STEREO ,   //双声道立体声
        AV_SAMPLE_FMT_S16 ,     //采样位数 16 位
        44100 ,                 //输出的采样率
        //从编码器中获取输入音频格式
        avCodecContext->channel_layout, //输入的声道数
        avCodecContext->sample_fmt,     //输入的采样位数
        avCodecContext->sample_rate,    //输入的采样率
        0, 0    //日志参数 设置 0 即可
        );
swr_init(swrContext);



② 计算积压的延迟数据 : int64_t swr_get_delay(struct SwrContext *s, int64_t base)


int64_t delay = swr_get_delay(swrContext , avFrame->sample_rate);

1


③ 计算本次重采样后的样本个数 : int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c,

enum AVRounding rnd) av_const
int64_t out_count = av_rescale_rnd(
        avFrame->nb_samples + delay, //本次要处理的数据个数
        44100,
        avFrame->sample_rate ,
        AV_ROUND_UP );



④ 音频重采样 : int swr_convert(struct SwrContext *s, uint8_t **out, int out_count, const uint8_t **in , int in_count)


int samples_per_channel_count = swr_convert(
        swrContext ,
        &data,
        out_count ,
        (const uint8_t **)avFrame->data, //普通指针转为 const 指针需要使用 const_cast 转换
        avFrame->nb_samples
        );



⑤ 计算音频重采样字节数 : 音频重采样 swr_convert ( ) 返回值

samples_per_channel_count 是 每个通道的样本数 ;
pcm_data_bit_size = samples_per_channel_count * 2 * 2;
1



III . FFMPEG 音频重采样


1 . 音频解码 : FFMPEG 从 AVStream 音频流中读取 AVPacket 压缩的编码数据包 , 然后进行解码 , 获得解码后的数据 , 封装在 AVFrame 中 ;



2 . 音频重采样 : 解码后的 AVFrame 的音频 采样率 , 采样位数 , 声道数 ( 左声道 / 右声道 / 立体声 ) 都是不确定的 , 但是在 Android 中的播放器 , 需要播放指定的 采样率 , 采样位数 , 声道数 参数的音频 , 因此需要将 AVFrame 中的音频数据 , 进行重采样 , 将其转换为我们创建的 Android 播放器可以播放的音频数据 ;



3 . 参考视频解码 : 视频播放的时候也是从 AVStream 中读取 AVPacket 数据 , 然后解码为 AVFrame 数据 , 但是其图像大部分是 YUV 像素格式的 , 需要转成 ARGB 像素格式才能再 Android 的 SurfaceView 中进行绘制 ;



4 . 重采样 与 像素格式转换 : 这个 音频重采样 与 图像的像素格式转换作用相同 , 都是将读取的不确定的音频图像格式 , 转成可以在 Android 中播放或显示的固定的音频图像格式 ;



5 . OpenSL ES 播放参数举例 : 我们设置的 OpenSLES 播放器设定播放的音频格式是 立体声 , 44100 Hz 采样 , 16位采样位数 , 要将 AVFrame 中的解码后的音频转为上面的格式要求 , 才能再 OpenSLES 播放器中播放 ;




IV . FFMPEG 初始化音频重采样上下文 SwrContext


1 . 初始化音频重采样上下文 : 音频重采样需要先初始化 音频重采样上下文 SwrContext , 首先要调用 swr_alloc_set_opts ( ) 初始化内存 并 设置 SwrContext 参数 , 再调用 swr_init(swrContext) 方法初始化 ;



2 . swr_alloc_set_opts ( ) 函数原型 : 为 SwrContext 音频重采样上下文 结构体分配内存 , 并设置相关参数 ;



① struct SwrContext *s 参数 : 音频重采样上下文 结构体指针 , 这里还没有 , 传入 0 即可 ;



输出相关参数 :


② int64_t out_ch_layout 参数 : 输出通道参数 , 左声道 / 右声道 / 立体声 ;


③ enum AVSampleFormat out_sample_fmt 参数 : 输出采样位数 , 每个样本的大小 , 8 位 或 16 位 ;


④ int out_sample_rate 参数 : 输出的采样率 , 单位 Hz , 如 44100 Hz , 代表一秒钟有 44100 个采样 ;



输入相关参数 :


⑤ int64_t in_ch_layout 参数 : 输入通道参数 , 左声道 / 右声道 / 立体声 ;


⑥ enum AVSampleFormat in_sample_fmt 参数 : 输入采样位数 , 每个样本的大小 , 8 位 或 16 位 ;


⑦ int in_sample_rate 参数 : 输入的采样率 , 单位 Hz , 如 44100 Hz , 代表一秒钟有 44100 个采样 ;



⑧ int log_offset 参数 : 日志相关参数 ; 0 即可 ;


⑨ void *log_ctx 参数 : 日志相关参数 ; 0 即可 ;


/**
 * Allocate SwrContext if needed and set/reset common parameters.
 *
 * This function does not require s to be allocated with swr_alloc(). On the
 * other hand, swr_alloc() can use swr_alloc_set_opts() to set the parameters
 * on the allocated context.
 *
 * @param s               existing Swr context if available, or NULL if not
 * @param out_ch_layout   output channel layout (AV_CH_LAYOUT_*)
 * @param out_sample_fmt  output sample format (AV_SAMPLE_FMT_*).
 * @param out_sample_rate output sample rate (frequency in Hz)
 * @param in_ch_layout    input channel layout (AV_CH_LAYOUT_*)
 * @param in_sample_fmt   input sample format (AV_SAMPLE_FMT_*).
 * @param in_sample_rate  input sample rate (frequency in Hz)
 * @param log_offset      logging level offset
 * @param log_ctx         parent logging context, can be NULL
 *
 * @see swr_init(), swr_free()
 * @return NULL on error, allocated context otherwise
 */
struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,
                                      int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
                                      int64_t  in_ch_layout, enum AVSampleFormat  in_sample_fmt, int  in_sample_rate,
                                      int log_offset, void *log_ctx);


3 . swr_init ( ) 函数原型 : 在用户设置完音频重采样上下文参数后 , 调用该方法可以初始化该上下文 ;



① int 返回值 : 如果初始化失败 , 会返回 AVERROR 错误码 ;


/**
 * Initialize context after user parameters have been set.
 * @note The context must be configured using the AVOption API.
 *
 * @see av_opt_set_int()
 * @see av_opt_set_dict()
 *
 * @param[in,out]   s Swr context to initialize
 * @return AVERROR error code in case of failure.
 */
int swr_init(struct SwrContext *s);



4 . FFMPEG 初始化音频重采样上下文 SwrContext 代码示例 :


/*
 设置音频重采样的上下文参数
 struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,
    int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
    int64_t  in_ch_layout, enum AVSampleFormat  in_sample_fmt, int  in_sample_rate,
    int log_offset, void *log_ctx);
 */
swrContext = swr_alloc_set_opts(
        0 ,                     //现在还没有 SwrContext 上下文 , 先传入 0
        //输出的音频参数
        AV_CH_LAYOUT_STEREO ,   //双声道立体声
        AV_SAMPLE_FMT_S16 ,     //采样位数 16 位
        44100 ,                 //输出的采样率
        //从编码器中获取输入音频格式
        avCodecContext->channel_layout, //输入的声道数
        avCodecContext->sample_fmt,     //输入的采样位数
        avCodecContext->sample_rate,    //输入的采样率
        0, 0    //日志参数 设置 0 即可
        );
//注意创建完之后初始化
swr_init(swrContext);



V . FFMPEG 计算音频延迟样本数


1 . 音频延迟情况 : FFMPEG 转码的过程中 , 可能没有一次性将一帧数据处理完毕 , 如输入了 20 个数据 , 一般情况下 20 个数据都能处理完毕 , 有时还会出现只处理了 19 个 , 剩余的 1 个数据就积压在了缓冲区中的情况 , 如果这种积压在缓冲区中的数据过大 , 会造成很大的音频延迟 , 甚至内存崩溃 ;



2 . 延迟数据处理方案 : 每次音频处理时 , 都尝试将上一次积压的音频采样数据加入到本次处理的数据中 , 防止出现音频延迟的情况 ;



3 . 获取音频数据积压个数 : 调用 swr_get_delay ( ) 方法 , 可以获取当前积压的音频采样数 , 或播放延迟时间 ;



4 . 对延迟的理解 : swr_get_delay ( ) 获取的是下一次的样本数据 A 输入 经过多长时间延迟后 , 才能将样本 A 播放出来 , 这个延迟就是积压的数据的播放时间 , 因此每次处理时将少部分积压数据进行处理 , 可以有效降低音频延迟 ;



5 . swr_get_delay ( ) 函数原型 : 获取下一次输入的样本 , 到对应的样本输出时 , 需要经历的延迟 , 即获取延迟的数据播放时长或样本个数 ( 二选一 ) ;



① struct SwrContext *s 参数 : 音频重采样上下文 结构体指针 ;


② int64_t base 参数 : 设置成 1 / 1000 获取延迟的时间 秒 / 毫秒 , 设置采样率 获取延迟的样本个数 ;


/**
 * Gets the delay the next input sample will experience relative to the next output sample.
 *
 * Swresample can buffer data if more input has been provided than available
 * output space, also converting between sample rates needs a delay.
 * This function returns the sum of all such delays.
 * The exact delay is not necessarily an integer value in either input or
 * output sample rate. Especially when downsampling by a large value, the
 * output sample rate may be a poor choice to represent the delay, similarly
 * for upsampling and the input sample rate.
 *
 * @param s     swr context
 * @param base  timebase in which the returned delay will be:
 *              @li if it's set to 1 the returned delay is in seconds
 *              @li if it's set to 1000 the returned delay is in milliseconds
 *              @li if it's set to the input sample rate then the returned
 *                  delay is in input samples
 *              @li if it's set to the output sample rate then the returned
 *                  delay is in output samples
 *              @li if it's the least common multiple of in_sample_rate and
 *                  out_sample_rate then an exact rounding-free delay will be
 *                  returned
 * @returns     the delay in 1 / @c base units.
 */
int64_t swr_get_delay(struct SwrContext *s, int64_t base);



6 . FFMPEG 计算音频延迟样本数 swr_get_delay ( ) 函数使用示例 : 这里传入样本采样率 , 获取的是样本个数 ;


//OpenSLES 播放器设定播放的音频格式是 立体声 , 44100 Hz 采样 , 16位采样位数
//  解码出来的 AVFrame 中的数据格式不确定 , 需要进行重采样
/*
    int64_t swr_get_delay(
    struct SwrContext *s,
    int64_t base
    );
    转码的过程中 , 输入 10 个数据 , 并不一定都能处理完毕并输出 10 个数据 , 可能处理输出了 8 个数据
    还剩余 2 个数据没有处理
    那么在下一次处理的时候 , 需要将上次没有处理完的两个数据处理了 ;
    如果不处理上次的2个数据 , 那么数据会一直积压 , 如果积压数据过多 , 最终造成很大的延迟 , 甚至崩溃
    因此每次处理的时候 , 都要尝试将上次剩余没有处理的数据加入到本次处理的数据中
    如果计算出的 delay 一直等于 0 , 说明没有积压数据
 */
int64_t delay = swr_get_delay(swrContext , avFrame->sample_rate);



目录
相关文章
|
4天前
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
23 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
|
13天前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
37 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
16天前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
118 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
29天前
|
缓存 前端开发 Android开发
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
79 12
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
|
1月前
|
Dart 前端开发 Android开发
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
36 1
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
Android开发
【Android 高性能音频】AAudio 音频流 样本缓冲 相关配置 ( 通道数 | 样本格式 | 帧缓冲 | 采样率 | 每帧样本数 == 通道数 )(二)
【Android 高性能音频】AAudio 音频流 样本缓冲 相关配置 ( 通道数 | 样本格式 | 帧缓冲 | 采样率 | 每帧样本数 == 通道数 )(二)
572 0
|
Android开发 数据格式
【Android 高性能音频】AAudio 音频流 样本缓冲 相关配置 ( 通道数 | 样本格式 | 帧缓冲 | 采样率 | 每帧样本数 == 通道数 )(一)
【Android 高性能音频】AAudio 音频流 样本缓冲 相关配置 ( 通道数 | 样本格式 | 帧缓冲 | 采样率 | 每帧样本数 == 通道数 )(一)
242 0
|
2月前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
173 3
|
2月前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
77 19
|
2月前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
91 14

热门文章

最新文章

  • 1
    如何修复 Android 和 Windows 不支持视频编解码器的问题?
  • 2
    Android历史版本与APK文件结构
  • 3
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 4
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 5
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
  • 6
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
  • 7
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 8
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
  • 9
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 10
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡