2013-06-03 55 views
6

Tôi đang làm việc để ghi và phát trực tuyến âm thanh đến máy chủ RTMP tại một thời điểm. Tôi làm việc dưới MacOS (trong Xcode), vì vậy để chụp bộ đệm mẫu âm thanh, tôi sử dụng khung công tác AVFoundation. Nhưng để mã hóa và phát trực tuyến, tôi cần sử dụng bộ mã hóa ffmpeg-API và libfaac. Vì vậy, định dạng đầu ra phải là AAC (để hỗ trợ phát lại luồng trên các thiết bị iOS).Cách mã hóa PCM-âm thanh đã được lấy lại thành AAC bằng cách sử dụng ffmpeg-API khi số lượng mẫu pcm đầu vào không bằng 1024

Và tôi phải đối mặt với vấn đề như vậy: thiết bị ghi âm (trong trường hợp máy ảnh logitech) cung cấp cho tôi bộ đệm mẫu với 512 mẫu LPCM và tôi có thể chọn tốc độ mẫu đầu vào từ 16000, 24000, 36000 hoặc 48000 Hz. Khi tôi đưa các mẫu 512 này tới bộ mã hóa AAC (được định cấu hình cho tỷ lệ mẫu thích hợp), tôi nghe thấy âm thanh chậm và giật chậm và giật (có vẻ giống như pice im lặng sau mỗi khung hình).

Tôi đã tìm ra (có thể tôi sai), bộ mã hóa libfaac đó chỉ chấp nhận khung âm thanh với 1024 mẫu. Khi tôi đặt lấy mẫu đầu vào thành 24000 và lấy mẫu lại bộ đệm mẫu đầu vào thành 48000 trước khi mã hóa, tôi nhận được 1024 mẫu được lấy mẫu lại. Sau khi mã hóa 1024 tấm đệm này thành AAC, tôi nghe thấy âm thanh phù hợp trên đầu ra. Nhưng web-cam của tôi tạo ra 512 mẫu trong bộ đệm cho bất kỳ lấy mẫu đầu vào nào, khi tỷ lệ mẫu đầu ra phải là 48000 Hz. Vì vậy, tôi cần phải làm resampling trong mọi trường hợp, và tôi sẽ không có được chính xác 1024 mẫu trong bộ đệm sau khi resampling.

Có cách nào giải quyết vấn đề này trong chức năng ffmpeg-API không?

Tôi rất biết ơn sự giúp đỡ của bạn.

PS: Tôi đoán rằng tôi có thể tích lũy bộ đệm được lấy mẫu lại cho đến khi đếm mẫu trở thành 1024 và sau đó mã hóa nó, nhưng đây là luồng nên sẽ gặp sự cố với dấu thời gian và các thiết bị đầu vào khác. thích hợp.

Vấn đề hiện tại ra khỏi vấn đề mô tả trong [câu hỏi]: How to fill audio AVFrame (ffmpeg) with the data obtained from CMSampleBufferRef (AVFoundation)?

Đây là một mã với configs nghe codec (cũng là dòng video nhưng đoạn video làm việc tốt):

/*global variables*/ 
    static AVFrame *aframe; 
    static AVFrame *frame; 
    AVOutputFormat *fmt; 
    AVFormatContext *oc; 
    AVStream *audio_st, *video_st; 
Init() 
{ 
    AVCodec *audio_codec, *video_codec; 
    int ret; 

    avcodec_register_all(); 
    av_register_all(); 
    avformat_network_init(); 
    avformat_alloc_output_context2(&oc, NULL, "flv", filename); 
    fmt = oc->oformat; 
    oc->oformat->video_codec = AV_CODEC_ID_H264; 
    oc->oformat->audio_codec = AV_CODEC_ID_AAC; 
    video_st = NULL; 
    audio_st = NULL; 
    if (fmt->video_codec != AV_CODEC_ID_NONE) 
     { //… /*init video codec*/} 
    if (fmt->audio_codec != AV_CODEC_ID_NONE) { 
    audio_codec= avcodec_find_encoder(fmt->audio_codec); 

    if (!(audio_codec)) { 
     fprintf(stderr, "Could not find encoder for '%s'\n", 
       avcodec_get_name(fmt->audio_codec)); 
     exit(1); 
    } 
    audio_st= avformat_new_stream(oc, audio_codec); 
    if (!audio_st) { 
     fprintf(stderr, "Could not allocate stream\n"); 
     exit(1); 
    } 
    audio_st->id = oc->nb_streams-1; 

    //AAC: 
    audio_st->codec->sample_fmt = AV_SAMPLE_FMT_S16; 
    audio_st->codec->bit_rate = 32000; 
    audio_st->codec->sample_rate = 48000; 
    audio_st->codec->profile=FF_PROFILE_AAC_LOW; 
    audio_st->time_base = (AVRational){1, audio_st->codec->sample_rate }; 
    audio_st->codec->channels = 1; 
    audio_st->codec->channel_layout = AV_CH_LAYOUT_MONO;  


    if (oc->oformat->flags & AVFMT_GLOBALHEADER) 
     audio_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; 
    } 

    if (video_st) 
    { 
    // … 
    /*prepare video*/ 
    } 
    if (audio_st) 
    { 
    aframe = avcodec_alloc_frame(); 
    if (!aframe) { 
     fprintf(stderr, "Could not allocate audio frame\n"); 
     exit(1); 
    } 
    AVCodecContext *c; 
    int ret; 

    c = audio_st->codec; 


    ret = avcodec_open2(c, audio_codec, 0); 
    if (ret < 0) { 
     fprintf(stderr, "Could not open audio codec: %s\n", av_err2str(ret)); 
     exit(1); 
    } 

    //… 
} 

và resampling và mã hóa âm thanh:

if (mType == kCMMediaType_Audio) 
{ 
    CMSampleTimingInfo timing_info; 
    CMSampleBufferGetSampleTimingInfo(sampleBuffer, 0, &timing_info); 
    double pts=0; 
    double dts=0; 
    AVCodecContext *c; 
    AVPacket pkt = { 0 }; // data and size must be 0; 
    int got_packet, ret; 
    av_init_packet(&pkt); 
    c = audio_st->codec; 
     CMItemCount numSamples = CMSampleBufferGetNumSamples(sampleBuffer); 

    NSUInteger channelIndex = 0; 

    CMBlockBufferRef audioBlockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer); 
    size_t audioBlockBufferOffset = (channelIndex * numSamples * sizeof(SInt16)); 
    size_t lengthAtOffset = 0; 
    size_t totalLength = 0; 
    SInt16 *samples = NULL; 
    CMBlockBufferGetDataPointer(audioBlockBuffer, audioBlockBufferOffset, &lengthAtOffset, &totalLength, (char **)(&samples)); 

    const AudioStreamBasicDescription *audioDescription = CMAudioFormatDescriptionGetStreamBasicDescription(CMSampleBufferGetFormatDescription(sampleBuffer)); 

    SwrContext *swr = swr_alloc(); 

    int in_smprt = (int)audioDescription->mSampleRate; 
    av_opt_set_int(swr, "in_channel_layout", AV_CH_LAYOUT_MONO, 0); 

    av_opt_set_int(swr, "out_channel_layout", audio_st->codec->channel_layout, 0); 

    av_opt_set_int(swr, "in_channel_count", audioDescription->mChannelsPerFrame, 0); 
    av_opt_set_int(swr, "out_channel_count", audio_st->codec->channels, 0); 

    av_opt_set_int(swr, "out_channel_layout", audio_st->codec->channel_layout, 0); 
    av_opt_set_int(swr, "in_sample_rate",  audioDescription->mSampleRate,0); 

    av_opt_set_int(swr, "out_sample_rate", audio_st->codec->sample_rate,0); 

    av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0); 

    av_opt_set_sample_fmt(swr, "out_sample_fmt", audio_st->codec->sample_fmt, 0); 

    swr_init(swr); 
    uint8_t **input = NULL; 
    int src_linesize; 
    int in_samples = (int)numSamples; 
    ret = av_samples_alloc_array_and_samples(&input, &src_linesize, audioDescription->mChannelsPerFrame, 
              in_samples, AV_SAMPLE_FMT_S16P, 0); 


    *input=(uint8_t*)samples; 
    uint8_t *output=NULL; 


    int out_samples = av_rescale_rnd(swr_get_delay(swr, in_smprt) +in_samples, (int)audio_st->codec->sample_rate, in_smprt, AV_ROUND_UP); 

    av_samples_alloc(&output, NULL, audio_st->codec->channels, out_samples, audio_st->codec->sample_fmt, 0); 
    in_samples = (int)numSamples; 
    out_samples = swr_convert(swr, &output, out_samples, (const uint8_t **)input, in_samples); 


    aframe->nb_samples =(int) out_samples; 


    ret = avcodec_fill_audio_frame(aframe, audio_st->codec->channels, audio_st->codec->sample_fmt, 
          (uint8_t *)output, 
          (int) out_samples * 
          av_get_bytes_per_sample(audio_st->codec->sample_fmt) * 
          audio_st->codec->channels, 1); 

    aframe->channel_layout = audio_st->codec->channel_layout; 
    aframe->channels=audio_st->codec->channels; 
    aframe->sample_rate= audio_st->codec->sample_rate; 

    if (timing_info.presentationTimeStamp.timescale!=0) 
     pts=(double) timing_info.presentationTimeStamp.value/timing_info.presentationTimeStamp.timescale; 

    aframe->pts=pts*audio_st->time_base.den; 
    aframe->pts = av_rescale_q(aframe->pts, audio_st->time_base, audio_st->codec->time_base); 

    ret = avcodec_encode_audio2(c, &pkt, aframe, &got_packet); 

    if (ret < 0) { 
     fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret)); 
     exit(1); 
    } 
    swr_free(&swr); 
    if (got_packet) 
    { 
     pkt.stream_index = audio_st->index; 

     pkt.pts = av_rescale_q(pkt.pts, audio_st->codec->time_base, audio_st->time_base); 
     pkt.dts = av_rescale_q(pkt.dts, audio_st->codec->time_base, audio_st->time_base); 

     // Write the compressed frame to the media file. 
     ret = av_interleaved_write_frame(oc, &pkt); 
     if (ret != 0) { 
      fprintf(stderr, "Error while writing audio frame: %s\n", 
        av_err2str(ret)); 
      exit(1); 
     } 

} 
+0

iPhone hỗ trợ hơn aac audio, chỉ tò mò tại sao bạn chỉ hỗ trợ AAC, không phải máy ảnh logitech hỗ trợ bất kỳ âm thanh nào sau đây, g711 (ulaw), apcm, mpeg2 vv. Hầu hết máy ảnh chúng tôi biết hỗ trợ ít nhất là g711, về mặt kỹ thuật với một số ami bổ sung cũng có thể. –

+0

Codec AAC là một trong các codec bắt buộc trong nhiệm vụ của tôi và với các codec khác, tôi không gặp phải vấn đề gì. – Aleksei2414904

+0

hi Aleksei2414904 i am mã hóa mẫu PCM thành android aac và phải đối mặt với cùng một vấn đề xin vui lòng giúp tôi nếu bạn tìm thấy bất kỳ giải pháp. –

Trả lời

0

Tôi gặp vấn đề tương tự. Tôi đã mã hóa các gói PCM tới AAC trong khi chiều dài PCM các gói đôi khi nhỏ hơn .

Nếu tôi mã hóa gói nhỏ hơn 1024, âm thanh sẽ là chậm. Mặt khác, nếu tôi vứt nó đi, âm thanh sẽ nhận được nhanh hơn. Chức năng swr_convert không có bất kỳ đệm tự động nào từ quan sát của tôi.

tôi đã kết thúc với một chương trình đệm mà các gói tin đã được điền vào một 1024 đệm và bộ đệm được mã hóasạch mọi lúc nó là đầy đủ.

Chức năng để điền vào đệm dưới đây:

// put frame data into buffer of fixed size 
bool ffmpegHelper::putAudioBuffer(const AVFrame *pAvFrameIn, AVFrame **pAvFrameBuffer, AVCodecContext *dec_ctx, int frame_size, int &k0) { 
    // prepare pFrameAudio 
    if (!(*pAvFrameBuffer)) { 
    if (!(*pAvFrameBuffer = av_frame_alloc())) { 
     av_log(NULL, AV_LOG_ERROR, "Alloc frame failed\n"); 
     return false; 
    } else { 
     (*pAvFrameBuffer)->format = dec_ctx->sample_fmt; 
     (*pAvFrameBuffer)->channels = dec_ctx->channels; 
     (*pAvFrameBuffer)->sample_rate = dec_ctx->sample_rate; 
     (*pAvFrameBuffer)->nb_samples = frame_size; 
     int ret = av_frame_get_buffer(*pAvFrameBuffer, 0); 
     if (ret < 0) { 
     char err[500]; 
     av_log(NULL, AV_LOG_ERROR, "get audio buffer failed: %s\n", 
      av_make_error_string(err, AV_ERROR_MAX_STRING_SIZE, ret)); 
     return false; 
     } 
     (*pAvFrameBuffer)->nb_samples = 0; 
     (*pAvFrameBuffer)->pts = pAvFrameIn->pts; 
    } 
    } 

    // copy input data to buffer 
    int n_channels = pAvFrameIn->channels; 
    int new_samples = min(pAvFrameIn->nb_samples - k0, frame_size - (*pAvFrameBuffer)->nb_samples); 
    int k1 = (*pAvFrameBuffer)->nb_samples; 

    if (pAvFrameIn->format == AV_SAMPLE_FMT_S16) { 
    int16_t *d_in = (int16_t *)pAvFrameIn->data[0]; 
    d_in += n_channels * k0; 
    int16_t *d_out = (int16_t *)(*pAvFrameBuffer)->data[0]; 
    d_out += n_channels * k1; 

    for (int i = 0; i < new_samples; ++i) { 
     for (int j = 0; j < pAvFrameIn->channels; ++j) { 
     *d_out++ = *d_in++; 
     } 
    } 
    } else { 
    printf("not handled format for audio buffer\n"); 
    return false; 
    } 

    (*pAvFrameBuffer)->nb_samples += new_samples; 
    k0 += new_samples; 

    return true; 
} 

Và vòng lặp cho đệm điền và mã hóa dưới:

// transcoding needed 
int got_frame; 
AVMediaType stream_type; 
// decode the packet (do it your self) 
decodePacket(packet, dec_ctx, &pAvFrame_, got_frame); 

if (enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { 
    ret = 0; 
    // break audio packet down to buffer 
    if (enc_ctx->frame_size > 0) { 
     int k = 0; 
     while (k < pAvFrame_->nb_samples) { 
      if (!putAudioBuffer(pAvFrame_, &pFrameAudio_, dec_ctx, enc_ctx->frame_size, k)) 
       return false; 
      if (pFrameAudio_->nb_samples == enc_ctx->frame_size) { 
       // the buffer is full, encode it (do it yourself) 
       ret = encodeFrame(pFrameAudio_, stream_index, got_frame, false); 
       if (ret < 0) 
        return false; 
       pFrameAudio_->pts += enc_ctx->frame_size; 
       pFrameAudio_->nb_samples = 0; 
      } 
     } 
    } else { 
     ret = encodeFrame(pAvFrame_, stream_index, got_frame, false); 
    } 
} else { 
    // encode packet directly 
    ret = encodeFrame(pAvFrame_, stream_index, got_frame, false); 
} 
0

Bạn cần phải phá vỡ đệm mẫu thành những phần kích thước 1024, tôi đã làm cho ghi âm mp3 trong android để biết thêm fo llow các liên kết này link1, links2

0

Nếu có ai kết thúc ở đây, tôi gặp vấn đề tương tự và chỉ cần @Mohit chỉ ra cho AAC mỗi khung âm thanh phải được chia nhỏ thành 1024 byte khối.

dụ:

uint8_t *buffer = (uint8_t*) malloc(1024); 
AVFrame *frame = av_frame_alloc(); 
while((fread(buffer, 1024, 1, fp)) == 1) { 
    frame->data[0] = buffer; 
} 
1

Tôi cũng đã kết thúc ở đây sau khi gặp vấn đề tương tự. Tôi đang đọc âm thanh và video từ một thẻ SDI Blackmagic Decklink ở độ phân giải 720p50 có nghĩa là tôi có 960 mẫu trên mỗi videoframe (48k/50fps) Tôi muốn mã hóa cùng với video. Có âm thanh thực sự kỳ lạ khi chỉ gửi 960 mẫu đến aacenc và nó cũng không thực sự phàn nàn về thực tế này.

Bắt đầu sử dụng AVAudioFifo (xem ffmpeg/doc/examples/transcode_aac.c) và tiếp tục thêm khung cho đến khi tôi có đủ khung để thỏa mãn aacenc. Điều này có nghĩa là tôi có mẫu chơi quá muộn, tôi đoán, vì pts sẽ được đặt trên 1024 mẫu khi 960 đầu tiên thực sự có giá trị khác.Nhưng, nó không thực sự đáng chú ý đến mức tôi có thể nghe/thấy.