• ffplay源码分析:音频重采样


    1.音频重采样

    FFmpeg解码得到的音频帧的格式未必能被SDL支持,在这种情况下,需要进行音频重采样,即将音频帧格式转换为SDL支持的音频格式,否则是无法正常播放的。 音频重采样涉及两个步骤:

    1. 打开音频设备时进行的准备工作:确定SDL支持的音频格式,作为后期音频重采样的目标格式

    2. 音频播放线程中,取出音频帧后,若有需要(音频帧格式与SDL支持音频格式不匹配)则进行重采样,否则直接输出

    1.1 打开音频设备

    音频设备的打开实际是在解复用线程中实现的。解复用线程中先打开音频设备(设定音频回调函数供SDL音频播放线程回调),然后再创建音频解码线程。调用链如下:

    1. main() -->
    2. stream_open() -->
    3. read_thread() -->
    4. stream_component_open() -->
    5. audio_open(is, channel_layout, nb_channels, sample_rate, &is->audio_tgt);
    6. decoder_start(&is->auddec, audio_thread, is);

    audio_open()函数填入期望的音频参数,打开音频设备后,将实际的音频参数存入输出参数is->audio_tgt中,后面音频播放线程用会用到此参数,使用此参数将原始音频数据重采样,转换为音频设备支持的格式。

    1. static int audio_open(void *opaque, int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate, struct AudioParams *audio_hw_params)
    2. {
    3. SDL_AudioSpec wanted_spec, spec;
    4. const char *env;
    5. static const int next_nb_channels[] = {0, 0, 1, 6, 2, 6, 4, 6};
    6. static const int next_sample_rates[] = {0, 44100, 48000, 96000, 192000};
    7. int next_sample_rate_idx = FF_ARRAY_ELEMS(next_sample_rates) - 1;
    8. env = SDL_getenv("SDL_AUDIO_CHANNELS");
    9. if (env) { // 若环境变量有设置,优先从环境变量取得声道数和声道布局
    10. wanted_nb_channels = atoi(env);
    11. wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
    12. }
    13. if (!wanted_channel_layout || wanted_nb_channels != av_get_channel_layout_nb_channels(wanted_channel_layout)) {
    14. wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
    15. wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
    16. }
    17. // 根据channel_layout获取nb_channels,当传入参数wanted_nb_channels不匹配时,此处会作修正
    18. wanted_nb_channels = av_get_channel_layout_nb_channels(wanted_channel_layout);
    19. wanted_spec.channels = wanted_nb_channels; // 声道数
    20. wanted_spec.freq = wanted_sample_rate; // 采样率
    21. if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0) {
    22. av_log(NULL, AV_LOG_ERROR, "Invalid sample rate or channel count!\n");
    23. return -1;
    24. }
    25. while (next_sample_rate_idx && next_sample_rates[next_sample_rate_idx] >= wanted_spec.freq)
    26. next_sample_rate_idx--; // 从采样率数组中找到第一个不大于传入参数wanted_sample_rate的值
    27. // 音频采样格式有两大类型:planar和packed,假设一个双声道音频文件,一个左声道采样点记作L,一个右声道采样点记作R,则:
    28. // planar存储格式:(plane1)LLLLLLLL...LLLL (plane2)RRRRRRRR...RRRR
    29. // packed存储格式:(plane1)LRLRLRLR...........................LRLR
    30. // 在这两种采样类型下,又细分多种采样格式,如AV_SAMPLE_FMT_S16、AV_SAMPLE_FMT_S16P等,注意SDL2.0目前不支持planar格式
    31. // channel_layout是int64_t类型,表示音频声道布局,每bit代表一个特定的声道,参考channel_layout.h中的定义,一目了然
    32. // 数据量(bits/秒) = 采样率(Hz) * 采样深度(bit) * 声道数
    33. wanted_spec.format = AUDIO_S16SYS; // 采样格式:S表带符号,16是采样深度(位深),SYS表采用系统字节序,这个宏在SDL中定义
    34. wanted_spec.silence = 0; // 静音值
    35. wanted_spec.samples = FFMAX(SDL_AUDIO_MIN_BUFFER_SIZE, 2 << av_log2(wanted_spec.freq / SDL_AUDIO_MAX_CALLBACKS_PER_SEC)); // SDL声音缓冲区尺寸,单位是单声道采样点尺寸x声道数
    36. wanted_spec.callback = sdl_audio_callback; // 回调函数,若为NULL,则应使用SDL_QueueAudio()机制
    37. wanted_spec.userdata = opaque; // 提供给回调函数的参数
    38. // 打开音频设备并创建音频处理线程。期望的参数是wanted_spec,实际得到的硬件参数是spec
    39. // 1) SDL提供两种使音频设备取得音频数据方法:
    40. // a. push,SDL以特定的频率调用回调函数,在回调函数中取得音频数据
    41. // b. pull,用户程序以特定的频率调用SDL_QueueAudio(),向音频设备提供数据。此种情况wanted_spec.callback=NULL
    42. // 2) 音频设备打开后播放静音,不启动回调,调用SDL_PauseAudio(0)后启动回调,开始正常播放音频
    43. // SDL_OpenAudioDevice()第一个参数为NULL时,等价于SDL_OpenAudio()
    44. while (!(audio_dev = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE))) {
    45. av_log(NULL, AV_LOG_WARNING, "SDL_OpenAudio (%d channels, %d Hz): %s\n",
    46. wanted_spec.channels, wanted_spec.freq, SDL_GetError());
    47. // 如果打开音频设备失败,则尝试用不同的声道数或采样率再试打开音频设备,这里有些奇怪,暂不深究
    48. wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)];
    49. if (!wanted_spec.channels) {
    50. wanted_spec.freq = next_sample_rates[next_sample_rate_idx--];
    51. wanted_spec.channels = wanted_nb_channels;
    52. if (!wanted_spec.freq) {
    53. av_log(NULL, AV_LOG_ERROR,
    54. "No more combinations to try, audio open failed\n");
    55. return -1;
    56. }
    57. }
    58. wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels);
    59. }
    60. // 检查打开音频设备的实际参数:采样格式
    61. if (spec.format != AUDIO_S16SYS) {
    62. av_log(NULL, AV_LOG_ERROR,
    63. "SDL advised audio format %d is not supported!\n", spec.format);
    64. return -1;
    65. }
    66. // 检查打开音频设备的实际参数:声道数
    67. if (spec.channels != wanted_spec.channels) {
    68. wanted_channel_layout = av_get_default_channel_layout(spec.channels);
    69. if (!wanted_channel_layout) {
    70. av_log(NULL, AV_LOG_ERROR,
    71. "SDL advised channel count %d is not supported!\n", spec.channels);
    72. return -1;
    73. }
    74. }
    75. // wanted_spec是期望的参数,spec是实际的参数,wanted_spec和spec都是SDL中的结构。
    76. // 此处audio_hw_params是FFmpeg中的参数,输出参数供上级函数使用
    77. audio_hw_params->fmt = AV_SAMPLE_FMT_S16;
    78. audio_hw_params->freq = spec.freq;
    79. audio_hw_params->channel_layout = wanted_channel_layout;
    80. audio_hw_params->channels = spec.channels;
    81. audio_hw_params->frame_size = av_samples_get_buffer_size(NULL, audio_hw_params->channels, 1, audio_hw_params->fmt, 1);
    82. audio_hw_params->bytes_per_sec = av_samples_get_buffer_size(NULL, audio_hw_params->channels, audio_hw_params->freq, audio_hw_params->fmt, 1);
    83. if (audio_hw_params->bytes_per_sec <= 0 || audio_hw_params->frame_size <= 0) {
    84. av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size failed\n");
    85. return -1;
    86. }
    87. return spec.size;
    88. }

    打开音频设备,涉及到FFmpeg中音频存储的基础概念,为稍显清晰,将相关注释摘抄如下:

    本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击领取↓↓

     

    1.1.1 音频格式相关

    1. **planar&packed**
    2. 音频采样格式有两大类型:planar和packed,假设一个双声道音频文件,一个左声道采样点记作L,一个右声道采样点记作R,则:
    3. planar存储格式:(plane1)LLLLLLLL...LLLL (plane2)RRRRRRRR...RRRR
    4. packed存储格式:(plane1)LRLRLRLR...........................LRLR
    5. 在这两种采样类型下,又细分多种采样格式,如AV_SAMPLE_FMT_S16AV_SAMPLE_FMT_S16P等,注意SDL2.0目前不支持planar格式
    6. SDL中定义音频参数数据结构定义如下:
    7. /**
    8. * The calculated values in this structure are calculated by SDL_OpenAudio().
    9. *
    10. * For multi-channel audio, the default SDL channel mapping is:
    11. * 2: FL FR (stereo)
    12. * 3: FL FR LFE (2.1 surround)
    13. * 4: FL FR BL BR (quad)
    14. * 5: FL FR FC BL BR (quad + center)
    15. * 6: FL FR FC LFE SL SR (5.1 surround - last two can also be BL BR)
    16. * 7: FL FR FC LFE BC SL SR (6.1 surround)
    17. * 8: FL FR FC LFE BL BR SL SR (7.1 surround)
    18. */
    19. typedef struct SDL_AudioSpec
    20. {
    21. int freq; /**< DSP frequency -- samples per second */
    22. SDL_AudioFormat format; /**< Audio data format */
    23. Uint8 channels; /**< Number of channels: 1 mono, 2 stereo */
    24. Uint8 silence; /**< Audio buffer silence value (calculated) */
    25. Uint16 samples; /**< Audio buffer size in sample FRAMES (total samples divided by channel count) */
    26. Uint16 padding; /**< Necessary for some compile environments */
    27. Uint32 size; /**< Audio buffer size in bytes (calculated) */
    28. SDL_AudioCallback callback; /**< Callback that feeds the audio device (NULL to use SDL_QueueAudio()). */
    29. void *userdata; /**< Userdata passed to callback (ignored for NULL callbacks). */
    30. } SDL_AudioSpec;

     SDL音频格式定义如下:

    1. /**
    2. * \brief Audio format flags.
    3. *
    4. * These are what the 16 bits in SDL_AudioFormat currently mean...
    5. * (Unspecified bits are always zero).
    6. *
    7. * \verbatim
    8. ++-----------------------sample is signed if set
    9. ||
    10. || ++-----------sample is bigendian if set
    11. || ||
    12. || || ++---sample is float if set
    13. || || ||
    14. || || || +---sample bit size---+
    15. || || || | |
    16. 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
    17. \endverbatim
    18. *
    19. * There are macros in SDL 2.0 and later to query these bits.
    20. */
    21. typedef Uint16 SDL_AudioFormat;
    22. /**
    23. * \name Audio format flags
    24. *
    25. * Defaults to LSB byte order.
    26. */
    27. /* @{ */
    28. #define AUDIO_U8 0x0008 /**< Unsigned 8-bit samples */
    29. #define AUDIO_S8 0x8008 /**< Signed 8-bit samples */
    30. #define AUDIO_U16LSB 0x0010 /**< Unsigned 16-bit samples */
    31. #define AUDIO_S16LSB 0x8010 /**< Signed 16-bit samples */
    32. #define AUDIO_U16MSB 0x1010 /**< As above, but big-endian byte order */
    33. #define AUDIO_S16MSB 0x9010 /**< As above, but big-endian byte order */
    34. #define AUDIO_U16 AUDIO_U16LSB
    35. #define AUDIO_S16 AUDIO_S16LSB
    36. /* @} */

    FFmpeg中定义音频参数的相关数据结构为:

    1. // 这个结构是在ffplay.c中定义的:
    2. typedef struct AudioParams {
    3. int freq;
    4. int channels;
    5. int64_t channel_layout;
    6. enum AVSampleFormat fmt;
    7. int frame_size;
    8. int bytes_per_sec;
    9. } AudioParams;
    10. /**
    11. * Audio sample formats
    12. *
    13. * - The data described by the sample format is always in native-endian order.
    14. * Sample values can be expressed by native C types, hence the lack of a signed
    15. * 24-bit sample format even though it is a common raw audio data format.
    16. *
    17. * - The floating-point formats are based on full volume being in the range
    18. * [-1.0, 1.0]. Any values outside this range are beyond full volume level.
    19. *
    20. * - The data layout as used in av_samples_fill_arrays() and elsewhere in FFmpeg
    21. * (such as AVFrame in libavcodec) is as follows:
    22. *
    23. * @par
    24. * For planar sample formats, each audio channel is in a separate data plane,
    25. * and linesize is the buffer size, in bytes, for a single plane. All data
    26. * planes must be the same size. For packed sample formats, only the first data
    27. * plane is used, and samples for each channel are interleaved. In this case,
    28. * linesize is the buffer size, in bytes, for the 1 plane.
    29. *
    30. */
    31. enum AVSampleFormat {
    32. AV_SAMPLE_FMT_NONE = -1,
    33. AV_SAMPLE_FMT_U8, ///< unsigned 8 bits
    34. AV_SAMPLE_FMT_S16, ///< signed 16 bits
    35. AV_SAMPLE_FMT_S32, ///< signed 32 bits
    36. AV_SAMPLE_FMT_FLT, ///< float
    37. AV_SAMPLE_FMT_DBL, ///< double
    38. AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar
    39. AV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar
    40. AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar
    41. AV_SAMPLE_FMT_FLTP, ///< float, planar
    42. AV_SAMPLE_FMT_DBLP, ///< double, planar
    43. AV_SAMPLE_FMT_S64, ///< signed 64 bits
    44. AV_SAMPLE_FMT_S64P, ///< signed 64 bits, planar
    45. AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically
    46. };
    1. **channel_layout**
    2. channel_layout是int64_t类型,表示音频声道布局,每bit代表一个特定的声道,参考channel_layout.h中的定义:
    1. /**
    2. * @defgroup channel_masks Audio channel masks
    3. *
    4. * A channel layout is a 64-bits integer with a bit set for every channel.
    5. * The number of bits set must be equal to the number of channels.
    6. * The value 0 means that the channel layout is not known.
    7. * @note this data structure is not powerful enough to handle channels
    8. * combinations that have the same channel multiple times, such as
    9. * dual-mono.
    10. *
    11. * @{
    12. */
    13. #define AV_CH_FRONT_LEFT 0x00000001
    14. #define AV_CH_FRONT_RIGHT 0x00000002
    15. #define AV_CH_FRONT_CENTER 0x00000004
    16. #define AV_CH_LOW_FREQUENCY 0x00000008
    17. #define AV_CH_BACK_LEFT 0x00000010
    18. #define AV_CH_BACK_RIGHT 0x00000020
    19. #define AV_CH_FRONT_LEFT_OF_CENTER 0x00000040
    20. #define AV_CH_FRONT_RIGHT_OF_CENTER 0x00000080
    21. #define AV_CH_BACK_CENTER 0x00000100
    22. #define AV_CH_SIDE_LEFT 0x00000200
    23. #define AV_CH_SIDE_RIGHT 0x00000400
    24. #define AV_CH_TOP_CENTER 0x00000800
    25. #define AV_CH_TOP_FRONT_LEFT 0x00001000
    26. #define AV_CH_TOP_FRONT_CENTER 0x00002000
    27. #define AV_CH_TOP_FRONT_RIGHT 0x00004000
    28. #define AV_CH_TOP_BACK_LEFT 0x00008000
    29. #define AV_CH_TOP_BACK_CENTER 0x00010000
    30. #define AV_CH_TOP_BACK_RIGHT 0x00020000
    31. #define AV_CH_STEREO_LEFT 0x20000000 ///< Stereo downmix.
    32. #define AV_CH_STEREO_RIGHT 0x40000000 ///< See AV_CH_STEREO_LEFT.
    33. #define AV_CH_WIDE_LEFT 0x0000000080000000ULL
    34. #define AV_CH_WIDE_RIGHT 0x0000000100000000ULL
    35. #define AV_CH_SURROUND_DIRECT_LEFT 0x0000000200000000ULL
    36. #define AV_CH_SURROUND_DIRECT_RIGHT 0x0000000400000000ULL
    37. #define AV_CH_LOW_FREQUENCY_2 0x0000000800000000ULL
    38. /** Channel mask value used for AVCodecContext.request_channel_layout
    39. to indicate that the user requests the channel order of the decoder output
    40. to be the native codec channel order. */
    41. #define AV_CH_LAYOUT_NATIVE 0x8000000000000000ULL
    42. /**
    43. * @}
    44. * @defgroup channel_mask_c Audio channel layouts
    45. * @{
    46. * */
    47. #define AV_CH_LAYOUT_MONO (AV_CH_FRONT_CENTER)
    48. #define AV_CH_LAYOUT_STEREO (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT)
    49. #define AV_CH_LAYOUT_2POINT1 (AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY)
    50. #define AV_CH_LAYOUT_2_1 (AV_CH_LAYOUT_STEREO|AV_CH_BACK_CENTER)
    51. #define AV_CH_LAYOUT_SURROUND (AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER)
    52. #define AV_CH_LAYOUT_3POINT1 (AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY)
    53. #define AV_CH_LAYOUT_4POINT0 (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_CENTER)
    54. #define AV_CH_LAYOUT_4POINT1 (AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY)
    55. #define AV_CH_LAYOUT_2_2 (AV_CH_LAYOUT_STEREO|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
    56. #define AV_CH_LAYOUT_QUAD (AV_CH_LAYOUT_STEREO|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
    57. #define AV_CH_LAYOUT_5POINT0 (AV_CH_LAYOUT_SURROUND|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
    58. #define AV_CH_LAYOUT_5POINT1 (AV_CH_LAYOUT_5POINT0|AV_CH_LOW_FREQUENCY)
    59. #define AV_CH_LAYOUT_5POINT0_BACK (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
    60. #define AV_CH_LAYOUT_5POINT1_BACK (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_LOW_FREQUENCY)
    61. #define AV_CH_LAYOUT_6POINT0 (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_CENTER)
    62. #define AV_CH_LAYOUT_6POINT0_FRONT (AV_CH_LAYOUT_2_2|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
    63. #define AV_CH_LAYOUT_HEXAGONAL (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_BACK_CENTER)
    64. #define AV_CH_LAYOUT_6POINT1 (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_CENTER)
    65. #define AV_CH_LAYOUT_6POINT1_BACK (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_BACK_CENTER)
    66. #define AV_CH_LAYOUT_6POINT1_FRONT (AV_CH_LAYOUT_6POINT0_FRONT|AV_CH_LOW_FREQUENCY)
    67. #define AV_CH_LAYOUT_7POINT0 (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
    68. #define AV_CH_LAYOUT_7POINT0_FRONT (AV_CH_LAYOUT_5POINT0|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
    69. #define AV_CH_LAYOUT_7POINT1 (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
    70. #define AV_CH_LAYOUT_7POINT1_WIDE (AV_CH_LAYOUT_5POINT1|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
    71. #define AV_CH_LAYOUT_7POINT1_WIDE_BACK (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
    72. #define AV_CH_LAYOUT_OCTAGONAL (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_CENTER|AV_CH_BACK_RIGHT)
    73. #define AV_CH_LAYOUT_HEXADECAGONAL (AV_CH_LAYOUT_OCTAGONAL|AV_CH_WIDE_LEFT|AV_CH_WIDE_RIGHT|AV_CH_TOP_BACK_LEFT|AV_CH_TOP_BACK_RIGHT|AV_CH_TOP_BACK_CENTER|AV_CH_TOP_FRONT_CENTER|AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_FRONT_RIGHT)
    74. #define AV_CH_LAYOUT_STEREO_DOWNMIX (AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT)

    1.1.2 打开音频设备

    1. 打开音频设备并创建音频处理线程,通过调用SDL_OpenAudio()或SDL_OpenAudioDevice()实现。输入参数是预期的参数,输出参数是实际参数
    2. 1) SDL提供两种使音频设备取得音频数据方法:
    3. a. push,SDL以特定的频率调用回调函数,在回调函数中取得音频数据
    4. b. pull,用户程序以特定的频率调用SDL_QueueAudio(),向音频设备提供数据。此种情况wanted_spec.callback=NULL
    5. 2) 音频设备打开后播放静音,不启动回调,调用SDL_PauseAudio(0)后启动回调,开始正常播放音频
    6. SDL_OpenAudioDevice()第一个参数为NULL时,等价于SDL_OpenAudio()

    1.2 音频重采样

    音频重采样在audio_decode_frame()中实现,audio_decode_frame()就是从音频frame队列中取出一个frame,按指定格式经过重采样后输出。 audio_decode_frame()函数名起得不太好,它只是进行重采样,并不进行解码,叫audio_resample_frame()可能更贴切。 重采样的细节很琐碎,直接看注释:

    1. /**
    2. * Decode one audio frame and return its uncompressed size.
    3. *
    4. * The processed audio frame is decoded, converted if required, and
    5. * stored in is->audio_buf, with size in bytes given by the return
    6. * value.
    7. */
    8. static int audio_decode_frame(VideoState *is)
    9. {
    10. int data_size, resampled_data_size;
    11. int64_t dec_channel_layout;
    12. av_unused double audio_clock0;
    13. int wanted_nb_samples;
    14. Frame *af;
    15. if (is->paused)
    16. return -1;
    17. do {
    18. #if defined(_WIN32)
    19. while (frame_queue_nb_remaining(&is->sampq) == 0) {
    20. if ((av_gettime_relative() - audio_callback_time) > 1000000LL * is->audio_hw_buf_size / is->audio_tgt.bytes_per_sec / 2)
    21. return -1;
    22. av_usleep (1000);
    23. }
    24. #endif
    25. // 若队列头部可读,则由af指向可读帧
    26. if (!(af = frame_queue_peek_readable(&is->sampq)))
    27. return -1;
    28. frame_queue_next(&is->sampq);
    29. } while (af->serial != is->audioq.serial);
    30. // 根据frame中指定的音频参数获取缓冲区的大小
    31. data_size = av_samples_get_buffer_size(NULL, af->frame->channels, // 本行两参数:linesize,声道数
    32. af->frame->nb_samples, // 本行一参数:本帧中包含的单个声道中的样本数
    33. af->frame->format, 1); // 本行两参数:采样格式,不对齐
    34. // 获取声道布局
    35. dec_channel_layout =
    36. (af->frame->channel_layout && af->frame->channels == av_get_channel_layout_nb_channels(af->frame->channel_layout)) ?
    37. af->frame->channel_layout : av_get_default_channel_layout(af->frame->channels);
    38. // 获取样本数校正值:若同步时钟是音频,则不调整样本数;否则根据同步需要调整样本数
    39. wanted_nb_samples = synchronize_audio(is, af->frame->nb_samples);
    40. // is->audio_tgt是SDL可接受的音频帧数,是audio_open()中取得的参数
    41. // 在audio_open()函数中又有“is->audio_src = is->audio_tgt”
    42. // 此处表示:如果frame中的音频参数 == is->audio_src == is->audio_tgt,那音频重采样的过程就免了(因此时is->swr_ctr是NULL)
    43. //      否则使用frame(源)和is->audio_tgt(目标)中的音频参数来设置is->swr_ctx,并使用frame中的音频参数来赋值is->audio_src
    44. if (af->frame->format != is->audio_src.fmt ||
    45. dec_channel_layout != is->audio_src.channel_layout ||
    46. af->frame->sample_rate != is->audio_src.freq ||
    47. (wanted_nb_samples != af->frame->nb_samples && !is->swr_ctx)) {
    48. swr_free(&is->swr_ctx);
    49. // 使用frame(源)和is->audio_tgt(目标)中的音频参数来设置is->swr_ctx
    50. is->swr_ctx = swr_alloc_set_opts(NULL,
    51. is->audio_tgt.channel_layout, is->audio_tgt.fmt, is->audio_tgt.freq,
    52. dec_channel_layout, af->frame->format, af->frame->sample_rate,
    53. 0, NULL);
    54. if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) {
    55. av_log(NULL, AV_LOG_ERROR,
    56. "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n",
    57. af->frame->sample_rate, av_get_sample_fmt_name(af->frame->format), af->frame->channels,
    58. is->audio_tgt.freq, av_get_sample_fmt_name(is->audio_tgt.fmt), is->audio_tgt.channels);
    59. swr_free(&is->swr_ctx);
    60. return -1;
    61. }
    62. // 使用frame中的参数更新is->audio_src,第一次更新后后面基本不用执行此if分支了,因为一个音频流中各frame通用参数一样
    63. is->audio_src.channel_layout = dec_channel_layout;
    64. is->audio_src.channels = af->frame->channels;
    65. is->audio_src.freq = af->frame->sample_rate;
    66. is->audio_src.fmt = af->frame->format;
    67. }
    68. if (is->swr_ctx) {
    69. // 重采样输入参数1:输入音频样本数是af->frame->nb_samples
    70. // 重采样输入参数2:输入音频缓冲区
    71. const uint8_t **in = (const uint8_t **)af->frame->extended_data;
    72. // 重采样输出参数1:输出音频缓冲区尺寸
    73. // 重采样输出参数2:输出音频缓冲区
    74. uint8_t **out = &is->audio_buf1;
    75. // 重采样输出参数:输出音频样本数(多加了256个样本)
    76. int out_count = (int64_t)wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate + 256;
    77. // 重采样输出参数:输出音频缓冲区尺寸(以字节为单位)
    78. int out_size = av_samples_get_buffer_size(NULL, is->audio_tgt.channels, out_count, is->audio_tgt.fmt, 0);
    79. int len2;
    80. if (out_size < 0) {
    81. av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size() failed\n");
    82. return -1;
    83. }
    84. // 如果frame中的样本数经过校正,则条件成立
    85. if (wanted_nb_samples != af->frame->nb_samples) {
    86. // 重采样补偿:不清楚参数怎么算的
    87. if (swr_set_compensation(is->swr_ctx, (wanted_nb_samples - af->frame->nb_samples) * is->audio_tgt.freq / af->frame->sample_rate,
    88. wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate) < 0) {
    89. av_log(NULL, AV_LOG_ERROR, "swr_set_compensation() failed\n");
    90. return -1;
    91. }
    92. }
    93. av_fast_malloc(&is->audio_buf1, &is->audio_buf1_size, out_size);
    94. if (!is->audio_buf1)
    95. return AVERROR(ENOMEM);
    96. // 音频重采样:返回值是重采样后得到的音频数据中单个声道的样本数
    97. len2 = swr_convert(is->swr_ctx, out, out_count, in, af->frame->nb_samples);
    98. if (len2 < 0) {
    99. av_log(NULL, AV_LOG_ERROR, "swr_convert() failed\n");
    100. return -1;
    101. }
    102. if (len2 == out_count) {
    103. av_log(NULL, AV_LOG_WARNING, "audio buffer is probably too small\n");
    104. if (swr_init(is->swr_ctx) < 0)
    105. swr_free(&is->swr_ctx);
    106. }
    107. is->audio_buf = is->audio_buf1;
    108. // 重采样返回的一帧音频数据大小(以字节为单位)
    109. resampled_data_size = len2 * is->audio_tgt.channels * av_get_bytes_per_sample(is->audio_tgt.fmt);
    110. } else {
    111. // 未经重采样,则将指针指向frame中的音频数据
    112. is->audio_buf = af->frame->data[0];
    113. resampled_data_size = data_size;
    114. }
    115. audio_clock0 = is->audio_clock;
    116. /* update the audio clock with the pts */
    117. if (!isnan(af->pts))
    118. is->audio_clock = af->pts + (double) af->frame->nb_samples / af->frame->sample_rate;
    119. else
    120. is->audio_clock = NAN;
    121. is->audio_clock_serial = af->serial;
    122. #ifdef DEBUG
    123. {
    124. static double last_clock;
    125. printf("audio: delay=%0.3f clock=%0.3f clock0=%0.3f\n",
    126. is->audio_clock - last_clock,
    127. is->audio_clock, audio_clock0);
    128. last_clock = is->audio_clock;
    129. }
    130. #endif
    131. return resampled_data_size;
    132. }

    本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击领取↓↓ 

  • 相关阅读:
    史上最全的Selenium三大等待介绍
    Laravel 多语言项目开发
    【jetpack】Navigation
    用了PDCA工作法模板,被领导夸被同事学
    2、shell文本处理工具
    JMeter接口测试工具基础— 取样器sampler(二)
    你真的懂Java中的equals和==吗?看完这篇文章你就知道了
    R语言时间序列数据提取:使用xts包的first函数提取时间序列中最前面两周的数据(first 2 week)
    最全前端面试总结(2)map/reduce
    kubernetes 集群安装-准备篇
  • 原文地址:https://blog.csdn.net/m0_60259116/article/details/126159788