EDIT 9 tháng 4 năm 2013: Đã tìm hiểu cách sử dụng libswresample để thực hiện việc này ... nhanh hơn nhiều!
Tại một số thời điểm trong 2-3 năm qua, định dạng đầu ra AAC decoder của FFmpeg đã thay đổi từ AV_SAMPLE_FMT_S16 thành AV_SAMPLE_FMT_FLTP. Điều này có nghĩa là mỗi kênh âm thanh có bộ đệm riêng, và mỗi giá trị mẫu là một giá trị dấu phẩy động 32 bit được chia tỷ lệ từ -1.0 đến +1.0.
Trong khi đó với AV_SAMPLE_FMT_S16 dữ liệu nằm trong một bộ đệm duy nhất, với các mẫu xen kẽ và mỗi mẫu là số nguyên đã ký từ -32767 đến +32767.
Và nếu bạn thực sự cần âm thanh của mình dưới dạng AV_SAMPLE_FMT_S16, thì bạn phải tự thực hiện chuyển đổi. Tôi đã tìm ra hai cách để làm điều đó:
1. Sử dụng libswresample (đề nghị)
#include "libswresample/swresample.h"
...
SwrContext *swr;
...
// Set up SWR context once you've got codec information
swr = swr_alloc();
av_opt_set_int(swr, "in_channel_layout", audioCodec->channel_layout, 0);
av_opt_set_int(swr, "out_channel_layout", audioCodec->channel_layout, 0);
av_opt_set_int(swr, "in_sample_rate", audioCodec->sample_rate, 0);
av_opt_set_int(swr, "out_sample_rate", audioCodec->sample_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
swr_init(swr);
...
// In your decoder loop, after decoding an audio frame:
AVFrame *audioFrame = ...;
int16_t* outputBuffer = ...;
swr_convert(&outputBuffer, audioFrame->nb_samples, audioFrame->extended_data, audioFrame->nb_samples);
Và đó là tất cả các bạn phải làm!
2. Làm điều đó bằng tay trong C (câu trả lời ban đầu, không khuyến khích)
Vì vậy, trong vòng lặp giải mã của bạn, khi bạn đã có một gói âm thanh bạn giải mã nó như thế này:
AVCodecContext *audioCodec; // init'd elsewhere
AVFrame *audioFrame; // init'd elsewhere
AVPacket packet; // init'd elsewhere
int16_t* outputBuffer; // init'd elsewhere
int out_size = 0;
...
int len = avcodec_decode_audio4(audioCodec, audioFrame, &out_size, &packet);
Và sau đó, nếu bạn đã có một khung hình đầy đủ âm thanh, bạn có thể chuyển đổi nó khá dễ dàng:
// Convert from AV_SAMPLE_FMT_FLTP to AV_SAMPLE_FMT_S16
int in_samples = audioFrame->nb_samples;
int in_linesize = audioFrame->linesize[0];
int i=0;
float* inputChannel0 = (float*)audioFrame->extended_data[0];
// Mono
if (audioFrame->channels==1) {
for (i=0 ; i<in_samples ; i++) {
float sample = *inputChannel0++;
if (sample<-1.0f) sample=-1.0f; else if (sample>1.0f) sample=1.0f;
outputBuffer[i] = (int16_t) (sample * 32767.0f);
}
}
// Stereo
else {
float* inputChannel1 = (float*)audioFrame->extended_data[1];
for (i=0 ; i<in_samples ; i++) {
outputBuffer[i*2] = (int16_t) ((*inputChannel0++) * 32767.0f);
outputBuffer[i*2+1] = (int16_t) ((*inputChannel1++) * 32767.0f);
}
}
// outputBuffer now contains 16-bit PCM!
tôi đã để lại một vài điều ra cho rõ ràng ... thứ e kẹp trong đường dẫn mono lý tưởng nên được nhân đôi trong đường dẫn âm thanh stereo. Và mã có thể dễ dàng được tối ưu hóa.
Bạn đã bao giờ làm việc ra các câu trả lời cho điều này? Đang đối mặt với cùng một vấn đề chính xác –