使用libav将FLOAT PCM编码为OGG

时间:2014-07-30 21:33:38

标签: audio encoding ogg libavcodec libav

我目前正在尝试将原始PCM Float缓冲区转换为OGG编码文件。我尝试了几个库来完成编码过程,最后我选择了libavcodec。

我真正想做的是获取我的音频库提供的浮动缓冲区([-1; 1])并将其转换为编码ogg数据的字符缓冲区。

我设法用这个(概念证明)代码将浮动缓冲区编码到编码MP2的缓冲区:

static AVCodec *codec;
static AVCodecContext *c;
static AVPacket pkt;
static uint16_t* samples;
static AVFrame* frame;
static int frameEncoded;

FILE *file;

int main(int argc, char *argv[])
{
    file = fopen("file.ogg", "w+");

    long ret;

    avcodec_register_all();

    codec = avcodec_find_encoder(AV_CODEC_ID_MP2);
    if (!codec) {
        fprintf(stderr, "codec not found\n");
        exit(1);
    }

    c = avcodec_alloc_context3(NULL);

    c->bit_rate = 256000;
    c->sample_rate = 44100;
    c->channels = 2;
    c->sample_fmt = AV_SAMPLE_FMT_S16;
    c->channel_layout = AV_CH_LAYOUT_STEREO;

    /* open it */
    if (avcodec_open2(c, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }


    /* frame containing input raw audio */
    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate audio frame\n");
        exit(1);
    }

    frame->nb_samples     = c->frame_size;
    frame->format         = c->sample_fmt;
    frame->channel_layout = c->channel_layout;

    /* the codec gives us the frame size, in samples,
     * we calculate the size of the samples buffer in bytes */
    int buffer_size = av_samples_get_buffer_size(NULL, c->channels, c->frame_size,
                                                 c->sample_fmt, 0);
    if (buffer_size < 0) {
        fprintf(stderr, "Could not get sample buffer size\n");
        exit(1);
    }
    samples = av_malloc(buffer_size);
    if (!samples) {
        fprintf(stderr, "Could not allocate %d bytes for samples buffer\n",
                buffer_size);
        exit(1);
    }
    /* setup the data pointers in the AVFrame */
    ret = avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt,
                                   (const uint8_t*)samples, buffer_size, 0);
    if (ret < 0) {
        fprintf(stderr, "Could not setup audio frame\n");
        exit(1);
    }
}

void  myLibraryCallback(float *inbuffer, unsigned int length)
{
    for(int j = 0; j < (2 * length); j++) {
        if(frameEncoded >= (c->frame_size *2)) {
            int avret, got_output;

            av_init_packet(&pkt);
            pkt.data = NULL; // packet data will be allocated by the encoder
            pkt.size = 0;

            avret = avcodec_encode_audio2(c, &pkt, frame, &got_output);
            if (avret < 0) {
                fprintf(stderr, "Error encoding audio frame\n");
                exit(1);
            }
            if (got_output) {
                fwrite(pkt.data, 1, pkt.size, file);
                av_free_packet(&pkt);
            }

            frameEncoded = 0;
        }

        samples[frameEncoded] = inbuffer[j] * SHRT_MAX;
        frameEncoded++;
    }
}

代码非常简单,我通常的方式初始化libavencode,然后我的音频库发送处理的PCM FLOAT [-1; 1]以44.1Khz交错并且每个通道的inbuffer中的浮点数(通常为1024) (2代表立体声)。通常,inbuffer包含2048个浮点数。

这很简单,因为我只需要将我的PCM转换为16P,两者都是交错的。此外,可以在单个字符上编码16P样本。

现在我想将此应用于需要AV_SAMPLE_FMT_FLTP样本格式的OGG。 由于我的原生格式是AV_SAMPLE_FMT_FLT,因此它应该只是一些去交错。这很容易做到。

我没有得到的要点是:

  1. 如何在char缓冲区上发送浮点缓冲区?我们按原样处理它们(float * floatSamples =(float *)样本)?如果是这样,avcodec的样本号是什么意思?它是花车或字符的数量吗?
  2. 当avcodec_fill_audio_frame只接受(uint8_t *)参数而不是(uint8_t **)多个通道时,如何在两个缓冲区(一个用于左侧,一个用于右侧)上发送数据?它是否完全改变了以前的示例代码?
  3. 我自己试图找到一些答案,到目前为止我做了很多实验,但我没有对这些问题表示失败。由于缺乏关于这些的文档,如果你有答案,我将非常感激。

    谢谢!

0 个答案:

没有答案