使用ffmpeg(c++)编码AAC

7
我正在处理视频编码,该编码将用于Unity插件中。我已成功实现了图像编码,但现在遇到了音频问题。所以,我只尝试使用AAC编码的音频输入到mp4文件中。然而,我卡住了,生成的文件没有任何内容。另外,根据我所了解的,ffmpeg中的AAC仅支持AV_SAMPLE_FMT_FLTP格式,这就是为什么我使用它的原因。以下是我的代码:

设置:

int initialize_encoding_audio(const char *filename)
{
    int ret;
    AVCodecID aud_codec_id = AV_CODEC_ID_AAC;
    AVSampleFormat sample_fmt = AV_SAMPLE_FMT_FLTP;

    avcodec_register_all();
    av_register_all();

    aud_codec = avcodec_find_encoder(aud_codec_id);
    avcodec_register(aud_codec);

    if (!aud_codec)
        return COULD_NOT_FIND_AUD_CODEC;

    aud_codec_context = avcodec_alloc_context3(aud_codec);
    if (!aud_codec_context)
        return CONTEXT_CREATION_ERROR;

    aud_codec_context->bit_rate = 192000;
    aud_codec_context->sample_rate = select_sample_rate(aud_codec);
    aud_codec_context->sample_fmt = sample_fmt;
    aud_codec_context->channel_layout = AV_CH_LAYOUT_STEREO;
    aud_codec_context->channels = av_get_channel_layout_nb_channels(aud_codec_context->channel_layout);

    aud_codec_context->codec = aud_codec;
    aud_codec_context->codec_id = aud_codec_id;

    ret = avcodec_open2(aud_codec_context, aud_codec, NULL);

    if (ret < 0)
        return COULD_NOT_OPEN_AUD_CODEC;

    outctx = avformat_alloc_context();
    ret = avformat_alloc_output_context2(&outctx, NULL, "mp4", filename);

    outctx->audio_codec = aud_codec;
    outctx->audio_codec_id = aud_codec_id;

    audio_st = avformat_new_stream(outctx, aud_codec);

    audio_st->codecpar->bit_rate = aud_codec_context->bit_rate;
    audio_st->codecpar->sample_rate = aud_codec_context->sample_rate;
    audio_st->codecpar->channels = aud_codec_context->channels;
    audio_st->codecpar->channel_layout = aud_codec_context->channel_layout;
    audio_st->codecpar->codec_id = aud_codec_id;
    audio_st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
    audio_st->codecpar->format = sample_fmt;
    audio_st->codecpar->frame_size = aud_codec_context->frame_size;
    audio_st->codecpar->block_align = aud_codec_context->block_align;
    audio_st->codecpar->initial_padding = aud_codec_context->initial_padding;

    outctx->streams = new AVStream*[1];
    outctx->streams[0] = audio_st;

    av_dump_format(outctx, 0, filename, 1);

    if (!(outctx->oformat->flags & AVFMT_NOFILE))
    {
        if (avio_open(&outctx->pb, filename, AVIO_FLAG_WRITE) < 0)
            return COULD_NOT_OPEN_FILE;
    }

    ret = avformat_write_header(outctx, NULL);

    aud_frame = av_frame_alloc();
    aud_frame->nb_samples = aud_codec_context->frame_size;
    aud_frame->format = aud_codec_context->sample_fmt;
    aud_frame->channel_layout = aud_codec_context->channel_layout;

    int buffer_size = av_samples_get_buffer_size(NULL, aud_codec_context->channels, aud_codec_context->frame_size,
        aud_codec_context->sample_fmt, 0);

    av_frame_get_buffer(aud_frame, buffer_size / aud_codec_context->channels);

    if (!aud_frame)
        return COULD_NOT_ALLOCATE_FRAME;

    aud_frame_counter = 0;

    return 0;
}

编码:

int encode_audio_samples(uint8_t **aud_samples)
{
    int ret;

    int buffer_size = av_samples_get_buffer_size(NULL, aud_codec_context->channels, aud_codec_context->frame_size,
        aud_codec_context->sample_fmt, 0);

    for (size_t i = 0; i < buffer_size / aud_codec_context->channels; i++)
    {
        aud_frame->data[0][i] = aud_samples[0][i];
        aud_frame->data[1][i] = aud_samples[1][i];
    }

    aud_frame->pts = aud_frame_counter++;

    ret = avcodec_send_frame(aud_codec_context, aud_frame);
    if (ret < 0)
        return ERROR_ENCODING_SAMPLES_SEND;

    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;

    fflush(stdout);

    while (true)
    {
        ret = avcodec_receive_packet(aud_codec_context, &pkt);
        if (!ret)
        {
            av_packet_rescale_ts(&pkt, aud_codec_context->time_base, audio_st->time_base);

            pkt.stream_index = audio_st->index;
            av_write_frame(outctx, &pkt);
            av_packet_unref(&pkt);
        }
        if (ret == AVERROR(EAGAIN))
            break;
        else if (ret < 0)
            return ERROR_ENCODING_SAMPLES_RECEIVE;
        else
            break;
    }

    return 0;
}

完成编码:

int finish_audio_encoding()
{
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;

    fflush(stdout);

    int ret = avcodec_send_frame(aud_codec_context, NULL);
    if (ret < 0)
        return ERROR_ENCODING_FRAME_SEND;

    while (true)
    {
        ret = avcodec_receive_packet(aud_codec_context, &pkt);
        if (!ret)
        {
            if (pkt.pts != AV_NOPTS_VALUE)
                pkt.pts = av_rescale_q(pkt.pts, aud_codec_context->time_base, audio_st->time_base);
            if (pkt.dts != AV_NOPTS_VALUE)
                pkt.dts = av_rescale_q(pkt.dts, aud_codec_context->time_base, audio_st->time_base);

            av_write_frame(outctx, &pkt);
            av_packet_unref(&pkt);
        }
        if (ret == -AVERROR(AVERROR_EOF))
            break;
        else if (ret < 0)
            return ERROR_ENCODING_FRAME_RECEIVE;
    }

    av_write_trailer(outctx);
}

主要内容:

void get_audio_frame(float_t *left_samples, float_t *right_samples, int frame_size, float* t, float* tincr, float* tincr2)
{
    int j, i;
    float v;
    for (j = 0; j < frame_size; j++)
    {
        v = sin(*t);
        *left_samples = v;
        *right_samples = v;

        left_samples++;
        right_samples++;

        *t += *tincr;
        *tincr += *tincr2;
    }
}

int main()
{
    int frame_rate = 30;  // this should be like 96000 / 1024 or somthing i guess?
    float t, tincr, tincr2;

    initialize_encoding_audio("audio.mp4");

    int sec = 50;

    float_t** aud_samples;
    int src_samples_linesize;
    int src_nb_samples = 1024;
    int src_channels = 2;

    int ret = av_samples_alloc_array_and_samples((uint8_t***)&aud_samples, &src_samples_linesize, src_channels,
        src_nb_samples, AV_SAMPLE_FMT_FLTP, 0);


    t = 0;
    tincr = 0;
    tincr2 = 0;

    for (size_t i = 0; i < frame_rate * sec; i++)
    {
        get_audio_frame(aud_samples[0], aud_samples[1], src_nb_samples, &t, &tincr, &tincr2);

        encode_audio_samples((uint8_t **)aud_samples);

    }

    finish_audio_encoding();
    //cleanup();

    return 0;
}

我想首先确保我正确了解合成声音的生成方式以及如何将其转移到AVFrame。我的转换是否正确?但是请随时指出任何可能存在的错误。
提前感谢!
编辑:整个源代码:http://pastebin.com/jYtmkhek 编辑2:添加tincr和tincr2的初始化。

1
有具体的问题吗?你似乎在问“我的代码是否正确”,对此的常见回答是“它能正常工作吗?” - Ronald S. Bultje
好的,正如我所说,“生成的文件中没有包含任何内容。”。因此,文件中没有声音。 - Mockarutan
@Mockarutan v = sin(*t) 你确定这在可听范围内吗?因为它对我来说听起来并不像是肯定的答复 :) 请链接完整的源文件。 - aergistal
文档中写道:“* - 浮点格式基于完整音量在[-1.0,1.0]范围内。任何超出此范围的值都超出了完整音量级别。”所以这应该是正确的吧?这是完整的源代码。但我也可以链接到Pastebin,一旦可用就会这样做。 - Mockarutan
是的,编译/调试会更容易。 - aergistal
显示剩余3条评论
1个回答

2
除非我在pastebin中漏掉了什么,否则您忘记初始化一些变量。您正在使用垃圾生成样本。
float t, tincr, tincr2;
[...]
get_audio_frame(aud_samples[0], aud_samples[1], src_nb_samples, &t, &tincr, &tincr2);

你可能想从t=0开始,按照2 * PI * frequency / sample rate的步长增加来生成正弦波。
此外,avformat_new_stream()会为你创建流,不要用new来创建。 更新: 我删除了所有的 c++ 代码来测试这段话。以下是可行的代码: pastebin 并且这是生成的文件:audio.mp4
ffmpeg -i audio.mp4 -filter_complex "showwaves=s=640x120:mode=line:colors=white" -frames:v 1 wave.jpg

这里输入图片描述

差异:

1,6d0
< #include "encoder.h"
< #include <algorithm>
< #include <iterator>
< 
< extern "C"
< {
14a9
> #include <math.h>
40,41c35,36
<   SwsContext *sws_ctx;
<   SwrContext *swr_ctx = NULL;
---
> struct SwsContext *sws_ctx;
> struct SwrContext *swr_ctx = NULL;
76,77c71,72
<       AVCodecID aud_codec_id = AV_CODEC_ID_AAC;
<       AVSampleFormat sample_fmt = AV_SAMPLE_FMT_FLTP;
---
>   enum AVCodecID aud_codec_id = AV_CODEC_ID_AAC;
>   enum AVSampleFormat sample_fmt = AV_SAMPLE_FMT_FLTP;
125,126c120,121
<       outctx->streams = new AVStream*[1];
<       outctx->streams[0] = audio_st;
---
>   //outctx->streams = new AVStream*[1];
>   //outctx->streams[0] = audio_st;
182c177
<       while (true)
---
>   while (1)
216c211
<       while (true)
---
>   while (1)
291c286
<       float t, tincr, tincr2;
---
>   float t = 0, tincr = 2 * M_PI * 440.0 / 96000, tincr2 = 0;
317d311
<   }

你说得对,我漏掉了那个。然而,文件仍然大部分为空。似乎声音实际上变长了,从100毫秒到500毫秒。文件描述仍然显示00:00长度。所以你发现了问题,但并没有解决它。(我已经用新代码编辑了帖子) - Mockarutan
@Mockarutan添加了代码、差异和示例音频。它适用于大多数播放器,但VLC中的FAAD解码器不喜欢比特流。无论如何,现在文件中有音频,您应该能够自行解决其余的代码问题。 - aergistal
添加了生成音频流的波形表示。 - aergistal
哇,这可真出乎意料!我原本还在为我的代码发疯,而事实上它可能一直在生成某种有效的声音,但我从未意识到 VLC(我曾认为它可以播放任何东西)可能会对采样率等进行挑剔。我尝试切换到48khz(因为我只试过44.1k和96k),现在甚至可以在VLC中播放了!谢谢! - Mockarutan
使用此代码创建的audio.aac无法播放。在avformat_alloc_output_context2(&outctx, NULL, NULL, "audio.aac")中传递了format_name = NULL。 - Arunraj Shanmugam
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接