2013-02-20 37 views
7

Tôi giải mã aac thành pcm bằng ffmpeg với avcodec_decode_audio3. Tuy nhiên nó giải mã thành định dạng mẫu AV_SAMPLE_FMT_FLTP (PCM 32bit Float Planar) và tôi cần AV_SAMPLE_FMT_S16 (PCM 16 bit đã ký - S16LE).Cách chuyển đổi tỷ lệ mẫu từ AV_SAMPLE_FMT_FLTP thành AV_SAMPLE_FMT_S16?

Tôi biết rằng ffmpeg có thể thực hiện việc này dễ dàng với -sample_fmt. Tôi muốn làm điều tương tự với mã nhưng tôi vẫn không thể hiểu được.

audio_resample không hoạt động cho: không thành công với thông báo lỗi: .... chuyển đổi không thành công.

+0

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 –

Trả lời

35

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.

+0

Tôi có một vấn đề liên quan, thời gian này, tôi cần phải chuyển đổi S16 sang S16P. Vì ffmpeg mới nhất cần S16P để mã hóa libmp3lame. Tôi sẽ rất vui nếu bạn xem xét: http://stackoverflow.com/questions/18131389/how-to-convert-av-sample-fmt-s16-to-av-sample-fmt-s16p – frankish

+0

Reuben, bạn sẽ có mã này? Tôi đang cố gắng để chuyển đổi này hoạt động nhưng tôi đang gặp một số vấn đề. Tôi muốn xem giải pháp làm việc hoàn chỉnh nếu bạn có thể đăng liên kết. Cảm ơn trước. –

+0

Tôi không còn có mã cho tùy chọn 2 ... bằng cách sử dụng libswresample là cách duy nhất để giải quyết vấn đề này. Bạn có vấn đề gì? –

2

Cảm ơn Reuben về giải pháp này. Tôi đã tìm thấy rằng một số giá trị mẫu đã được giảm nhẹ khi so sánh với một ffmpeg -i file.wav thẳng. Dường như trong chuyển đổi, họ sử dụng một vòng() trên giá trị.

Để thực hiện chuyển đổi, tôi đã làm những gì bạn đã làm với một nỗ lực sửa đổi để làm việc cho bất kỳ số lượng các kênh truyền hình:

if (audioCodecContext->sample_fmt == AV_SAMPLE_FMT_FLTP) 
{ 
    int nb_samples = decoded_frame->nb_samples; 
    int channels = decoded_frame->channels; 
    int outputBufferLen = nb_samples & channels * 2; 
    short* outputBuffer = new short[outputBufferLen/2]; 

    for (int i = 0; i < nb_samples; i++) 
    { 
     for (int c = 0; c < channels; c++) 
     { 
      float* extended_data = (float*)decoded_frame->extended_data[c]; 
      float sample = extended_data[i]; 
      if (sample < -1.0f) sample = -1.0f; 
      else if (sample > 1.0f) sample = 1.0f; 
      outputBuffer[i * channels + c] = (short)round(sample * 32767.0f); 
     } 
    } 

    // Do what you want with the data etc. 

} 

tôi đã đi từ ffmpeg 0.11.1 -> 1.1.3 và thấy sự thay đổi của định dạng mẫu gây phiền nhiễu. Tôi đã xem xét việc đặt request_sample_fmt thành AV_SAMPLE_FMT_S16 nhưng có vẻ như bộ giải mã aac không hỗ trợ bất kỳ điều gì khác ngoài AV_SAMPLE_FMT_FLTP.

+0

Ngoài ra tuyệt vời, cảm ơn bạn – frankish

+0

Tôi đã cập nhật câu trả lời của mình bằng cách sử dụng libswresample tốt hơn. Thật đáng ngạc nhiên là dễ làm. –

+0

@BradMitchell Làm thế nào chúng ta có thể làm ngược lại điều này? Bạn có nhớ xem http://stackoverflow.com/questions/18131389/how-to-convert-av-sample-fmt-s16-to-av-sample-fmt-s16p không? – frankish

5

Tôi tìm thấy 2 hàm mẫu từ FFMPEG. Hiệu suất có thể tốt hơn.

  1. avresample_convert() http://libav.org/doxygen/master/group__lavr.html
  2. swr_convert() http://spirton.com/svn/MPlayer-SB/ffmpeg/libswresample/swresample_test.c
+0

Bạn chắc chắn đã đúng dòng ở đây Albert ... Tôi đã có khiếu nại về hiệu suất sớm hơn hôm nay vì vậy phải xem xét việc tìm kiếm một phương pháp tối ưu để thực hiện chuyển đổi này và libswresample là người bạn thân nhất của tôi. Câu trả lời của tôi ở trên đã được cập nhật với mã cần thiết. –

Các vấn đề liên quan