=====================================================
=====================================================
FFmpeg解码得到的⾳频帧的格式未必能被SDL⽀持,在这种情况下,需要进⾏⾳频重采样,即将⾳频帧
格式转换为SDL⽀持的⾳频格式,否则是⽆法正常播放的。
这里重采样主要在回调读取里做的
/** * @brief sdl_audio_callback * @param opaque 指向user的数据 * @param stream 拷贝PCM的地址 * @param len 需要拷贝的长度 */ static void sdl_audio_callback(void *opaque, Uint8 *stream, int len) { VideoState *is = opaque; int audio_size, len1; audio_callback_time = av_gettime_relative(); while (len > 0) { // 循环读取,直到读取到足够的数据 /* (1)如果is->audio_buf_index < is->audio_buf_size则说明上次拷贝还剩余一些数据, * 先拷贝到stream再调用audio_decode_frame * (2)如果audio_buf消耗完了,则调用audio_decode_frame重新填充audio_buf */ if (is->audio_buf_index >= is->audio_buf_size) { audio_size = audio_decode_frame(is); if (audio_size < 0) { /* if error, just output silence */ is->audio_buf = NULL; is->audio_buf_size = SDL_AUDIO_MIN_BUFFER_SIZE / is->audio_tgt.frame_size * is->audio_tgt.frame_size; } else { if (is->show_mode != SHOW_MODE_VIDEO) update_sample_display(is, (int16_t *)is->audio_buf, audio_size); is->audio_buf_size = audio_size; // 讲字节 多少字节 } is->audio_buf_index = 0; } //根据缓冲区剩余大小量力而行 len1 = is->audio_buf_size - is->audio_buf_index; if (len1 > len) // len = 3000 < len1 4096 len1 = len; //根据audio_volume决定如何输出audio_buf /* 判断是否为静音,以及当前音量的大小,如果音量为最大则直接拷贝数据 */ if (!is->muted && is->audio_buf && is->audio_volume == SDL_MIX_MAXVOLUME) memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1); else { memset(stream, 0, len1); // 3.调整音量 /* 如果处于mute状态则直接使用stream填0数据, 暂停时is->audio_buf = NULL */ if (!is->muted && is->audio_buf) SDL_MixAudioFormat(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, AUDIO_S16SYS, len1, is->audio_volume); } len -= len1; stream += len1; /* 更新is->audio_buf_index,指向audio_buf中未被拷贝到stream的数据(剩余数据)的起始位置 */ is->audio_buf_index += len1; } is->audio_write_buf_size = is->audio_buf_size - is->audio_buf_index; /* Let's assume the audio driver that is used by SDL has two periods. */ if (!isnan(is->audio_clock)) { set_clock_at(&is->audclk, is->audio_clock - (double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / is->audio_tgt.bytes_per_sec, is->audio_clock_serial, audio_callback_time / 1000000.0); sync_clock_to_slave(&is->extclk, &is->audclk); } }
/** * Decode one audio frame and return its uncompressed size. * * The processed audio frame is decoded, converted if required, and * stored in is->audio_buf, with size in bytes given by the return * value. */ static int audio_decode_frame(VideoState *is) { int data_size, resampled_data_size; int64_t dec_channel_layout; av_unused double audio_clock0; int wanted_nb_samples; Frame *af; if (is->paused) return -1; do { #if defined(_WIN32) while (frame_queue_nb_remaining(&is->sampq) == 0) { if ((av_gettime_relative() - audio_callback_time) > 1000000LL * is->audio_hw_buf_size / is->audio_tgt.bytes_per_sec / 2) return -1; av_usleep (1000); } #endif // 若队列头部可读,则由af指向可读帧 if (!(af = frame_queue_peek_readable(&is->sampq))) return -1; frame_queue_next(&is->sampq); } while (af->serial != is->audioq.serial); // 根据frame中指定的音频参数获取缓冲区的大小 af->frame->channels * af->frame->nb_samples * 2 data_size = av_samples_get_buffer_size(NULL, af->frame->channels, af->frame->nb_samples, af->frame->format, 1); // 获取声道布局 dec_channel_layout = (af->frame->channel_layout && af->frame->channels == av_get_channel_layout_nb_channels(af->frame->channel_layout)) ? af->frame->channel_layout : av_get_default_channel_layout(af->frame->channels); // 获取样本数校正值:若同步时钟是音频,则不调整样本数;否则根据同步需要调整样本数 wanted_nb_samples = synchronize_audio(is, af->frame->nb_samples); // is->audio_tgt是SDL可接受的音频帧数,是audio_open()中取得的参数 // 在audio_open()函数中又有"is->audio_src = is->audio_tgt"" // 此处表示:如果frame中的音频参数 == is->audio_src == is->audio_tgt, // 那音频重采样的过程就免了(因此时is->swr_ctr是NULL) // 否则使用frame(源)和is->audio_tgt(目标)中的音频参数来设置is->swr_ctx, // 并使用frame中的音频参数来赋值is->audio_src if (af->frame->format != is->audio_src.fmt || // 采样格式 dec_channel_layout != is->audio_src.channel_layout || // 通道布局 af->frame->sample_rate != is->audio_src.freq || // 采样率 // 第4个条件, 要改变样本数量, 那就是需要初始化重采样 (wanted_nb_samples != af->frame->nb_samples && !is->swr_ctx) // samples不同且swr_ctx没有初始化 ) { swr_free(&is->swr_ctx); is->swr_ctx = swr_alloc_set_opts(NULL, is->audio_tgt.channel_layout, // 目标输出 is->audio_tgt.fmt, is->audio_tgt.freq, dec_channel_layout, // 数据源 af->frame->format, af->frame->sample_rate, 0, NULL); if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels! ", af->frame->sample_rate, av_get_sample_fmt_name(af->frame->format), af->frame->channels, is->audio_tgt.freq, av_get_sample_fmt_name(is->audio_tgt.fmt), is->audio_tgt.channels); swr_free(&is->swr_ctx); return -1; } is->audio_src.channel_layout = dec_channel_layout; is->audio_src.channels = af->frame->channels; is->audio_src.freq = af->frame->sample_rate; is->audio_src.fmt = af->frame->format; } if (is->swr_ctx) { // 重采样输入参数1:输入音频样本数是af->frame->nb_samples // 重采样输入参数2:输入音频缓冲区 const uint8_t **in = (const uint8_t **)af->frame->extended_data; // data[0] data[1] // 重采样输出参数1:输出音频缓冲区尺寸 uint8_t **out = &is->audio_buf1; //真正分配缓存audio_buf1,指向是用audio_buf // 重采样输出参数2:输出音频缓冲区 int out_count = (int64_t)wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate + 256; int out_size = av_samples_get_buffer_size(NULL, is->audio_tgt.channels, out_count, is->audio_tgt.fmt, 0); int len2; if (out_size < 0) { av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size() failed "); return -1; } // 如果frame中的样本数经过校正,则条件成立 if (wanted_nb_samples != af->frame->nb_samples) { if (swr_set_compensation(is->swr_ctx, (wanted_nb_samples - af->frame->nb_samples) * is->audio_tgt.freq / af->frame->sample_rate, wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate) < 0) { av_log(NULL, AV_LOG_ERROR, "swr_set_compensation() failed "); return -1; } } av_fast_malloc(&is->audio_buf1, &is->audio_buf1_size, out_size); if (!is->audio_buf1) return AVERROR(ENOMEM); // 音频重采样:返回值是重采样后得到的音频数据中单个声道的样本数 len2 = swr_convert(is->swr_ctx, out, out_count, in, af->frame->nb_samples); if (len2 < 0) { av_log(NULL, AV_LOG_ERROR, "swr_convert() failed "); return -1; } if (len2 == out_count) { av_log(NULL, AV_LOG_WARNING, "audio buffer is probably too small "); if (swr_init(is->swr_ctx) < 0) swr_free(&is->swr_ctx); } // 重采样返回的一帧音频数据大小(以字节为单位) is->audio_buf = is->audio_buf1; resampled_data_size = len2 * is->audio_tgt.channels * av_get_bytes_per_sample(is->audio_tgt.fmt); } else { // 未经重采样,则将指针指向frame中的音频数据 is->audio_buf = af->frame->data[0]; // s16交错模式data[0], fltp data[0] data[1] resampled_data_size = data_size; } audio_clock0 = is->audio_clock; /* update the audio clock with the pts */ if (!isnan(af->pts)) is->audio_clock = af->pts + (double) af->frame->nb_samples / af->frame->sample_rate; else is->audio_clock = NAN; is->audio_clock_serial = af->serial; #ifdef DEBUG { static double last_clock; printf("audio: delay=%0.3f clock=%0.3f clock0=%0.3f ", is->audio_clock - last_clock, is->audio_clock, audio_clock0); last_clock = is->audio_clock; } #endif return resampled_data_size; }