为什么mp3解码的输出声音如此延迟?(使用ffmpeg mp3lame lib)

时间:2014-03-10 13:51:17

标签: c++ ffmpeg mp3 recording

我用ffmpeg lib录制声音和编码到mp3。然后立即解码mp3数据,播放解码数据,但它听起来如此延迟。 这是代码: 函数encode first参数接受原始pcm数据,len = 44100。

编码参数:

cntx_->channels = 1;
cntx_->sample_rate = 44100;
cntx_->sample_fmt = 6;
cntx_->channel_layout =  AV_CH_LAYOUT_MONO;
cntx_->bit_rate = 8000;
err_ = avcodec_open2(cntx_, codec_, NULL);

vector<unsigned char>       encode(unsigned char* encode_data, unsigned int len)
{
    vector<unsigned char> ret;
    AVPacket avpkt;
    av_init_packet(&avpkt);

    unsigned int len_encoded = 0; 
    int data_left = len / 2;
    int miss_c = 0, i = 0;
    while (data_left > 0)
    {
        int sz = data_left > cntx_->frame_size ? cntx_->frame_size : data_left;
        mp3_frame_->nb_samples = sz;
        mp3_frame_->format = cntx_->sample_fmt;
        mp3_frame_->channel_layout = cntx_->channel_layout;

        int needed_size = av_samples_get_buffer_size(NULL, 1,
            mp3_frame_->nb_samples, cntx_->sample_fmt, 1);

        int r = avcodec_fill_audio_frame(mp3_frame_, 1, cntx_->sample_fmt, encode_data + len_encoded, needed_size, 0);

        int gotted = -1;

        r = avcodec_encode_audio2(cntx_, &avpkt, mp3_frame_, &gotted);
        if (gotted){
            i++;
            ret.insert(ret.end(), avpkt.data, avpkt.data + avpkt.size);
        }
        else if (gotted == 0){
            miss_c++;
        }
        len_encoded += needed_size;
        data_left -= sz;
        av_free_packet(&avpkt);
    }
    return ret;
}

std::vector<unsigned char>  decode(unsigned char* data, unsigned int len)
{
    std::vector<unsigned char> ret;

    AVPacket avpkt;
    av_init_packet(&avpkt);
    avpkt.data = data;
    avpkt.size = len;

    AVFrame* pframe = av_frame_alloc();
    while (avpkt.size > 0){
        int goted = -1;av_frame_unref(pframe);
        int used = avcodec_decode_audio4(cntx_, pframe, &goted, &avpkt);
        if (goted){
            ret.insert(ret.end(), pframe->data[0], pframe->data[0] + pframe->linesize[0]);
            avpkt.data += used;
            avpkt.size -= used;
            avpkt.dts = avpkt.pts = AV_NOPTS_VALUE; 
        }
        else if (goted == 0){
            avpkt.data += used;
            avpkt.size -= used;
            avpkt.dts = avpkt.pts = AV_NOPTS_VALUE; 
        }
        else if(goted < 0){
            break;
        }
    }
    av_frame_free(&pframe);
    return ret;
}

假设它是第100次编码(data,len),这个“帧”将在解码调用中出现在150或更晚,延迟是不可接受的。似乎mp3lame编码器会保留样本数据供以后使用,但不是我的愿望。 我不知道出了什么问题。感谢您提供任何信息。

今天我再次调试代码并发布一些细节:

编码:每个pcm样本帧len = 23040,这是mp3帧大小的10倍,每次调用只编码输出9帧,此输出导致解码输出20736个样本,1帧(2304字节)丢失,并且声音很吵。

如果mp3或mp2编码不适合实时语音传输,我应该选择哪种编码器?

1 个答案:

答案 0 :(得分:0)

  

假设它是第100次编码(data,len),这个“帧”将在解码调用中出现在150或更晚,延迟是不可接受的。

了解编解码器的工作原理并相应地调整您的期望。

MP3是一种有损编解码器。它的工作原理是将您的时域PCM数据转换为频域。仅此转换需要时间(因为频率分量在任何时刻都不存在......它们只能在一段时间内存在)。在一个简单的层面上,它然后使用一些算法来确定要保留的光谱信息以及丢弃的内容。每个MP3帧的持续时间长达数百个样本。 (576是你通常可以去的那么低。两倍是一个典型的数字。)

既然您有最短的时间来创建帧,MP3也会使用所谓的位库。如果复杂段落需要更多带宽,则它会从相邻帧中借用未使用的带宽。为此,需要一个多帧的缓冲区。

除了所有编解码器工作之外,FFmpeg本身还有缓冲(用于检测输入和什么不是),并且管道中有缓冲区来往和来自FFmpeg。我认为编解码器本身也可以在输入和输出上使用通用缓冲。

最后,您正在解码流并将其播放,这意味着编码中使用的大多数相同类型的缓冲区现在都用于解码。而且,我们甚至没有谈到通过声卡获取音频数据并将模拟信号传输到扬声器所需的几百毫秒延迟。

你有一个不切实际的期望,虽然有可能调整一些东西以减少延迟(例如禁用位储存器),但它会导致质量不佳的流并且无论如何都不会有低延迟。