android - ffmpeg 编码音频中的持续时间和比特率不正确

标签 android audio encoding ffmpeg aac

我正在使用 ffmpeg 库对 Android 上的原始数据进行编码。 native 代码从外部设备读取音频数据并将其编码为 mp4 容器中的 AAC 格式。我发现音频数据已成功编码(我可以使用我的默认 Windows 音频播放器 Groove Music 播放它)。但是,根据 ffprobe 的报告,元数据的持续时间不正确,为 0.05 秒 - 实际上是几秒长。即使我指定了 192kbps,比特率也被错误地报告为大约 65kbps。

我尝试过各种持续时间的录音,但结果总是相似的——(非常小的)持续时间和比特率。我尝试过其他各种音频播放器,例如 Quicktime,但它们只播放音频的前 0.05 秒左右。

我已从以下内容中删除了错误检查。实际代码会检查每个调用,并且不会报告任何问题。

初始化:

void AudioWriter::initialise( const char *filePath )
{
    AVCodecID avCodecID = AVCodecID::AV_CODEC_ID_AAC;
    int bitRate = 192000;
    char *containerFormat = "mp4";
    int sampleRate = 48000;
    int nChannels = 2;

    mAvCodec = avcodec_find_encoder(avCodecID);
    mAvCodecContext = avcodec_alloc_context3(mAvCodec);
    mAvCodecContext->codec_id = avCodecID;
    mAvCodecContext->codec_type = AVMEDIA_TYPE_AUDIO;
    mAvCodecContext->sample_fmt = AV_SAMPLE_FMT_FLTP;
    mAvCodecContext->bit_rate = bitRate;
    mAvCodecContext->sample_rate = sampleRate;
    mAvCodecContext->channels = nChannels; 
    mAvCodecContext->channel_layout = AV_CH_LAYOUT_STEREO;

    avcodec_open2( mAvCodecContext, mAvCodec, nullptr );

    mAvFormatContext = avformat_alloc_context();

    avformat_alloc_output_context2(&mAvFormatContext, nullptr, containerFormat, nullptr);
    mAvFormatContext->audio_codec = mAvCodec;
    mAvFormatContext->audio_codec_id = avCodecID;
    mAvOutputStream = avformat_new_stream(mAvFormatContext, mAvCodec);
    avcodec_parameters_from_context(mAvOutputStream->codecpar, mAvCodecContext);
    if (!(mAvFormatContext->oformat->flags & AVFMT_NOFILE))
    {
        avio_open(&mAvFormatContext->pb, filePath, AVIO_FLAG_WRITE);
    }

    if ( mAvFormatContext->oformat->flags & AVFMT_GLOBALHEADER )
    {
        mAvCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }

    avformat_write_header(mAvFormatContext, NULL);

    mAvAudioFrame = av_frame_alloc();
    mAvAudioFrame->nb_samples = mAvCodecContext->frame_size;
    mAvAudioFrame->format = mAvCodecContext->sample_fmt;
    mAvAudioFrame->channel_layout = mAvCodecContext->channel_layout;

    av_samples_get_buffer_size(NULL, mAvCodecContext->channels, mAvCodecContext->frame_size,
                                                 mAvCodecContext->sample_fmt, 0);
    av_frame_get_buffer(mAvAudioFrame, 0);
    av_frame_make_writable(mAvAudioFrame);
    mAvPacket = av_packet_alloc();
  }

编码:
// SoundRecording is a custom class with the raw samples to be encoded
bool AudioWriter::encodeToContainer( SoundRecording *soundRecording )
{
    int ret;
    int frameCount = mAvCodecContext->frame_size;
    int nChannels = mAvCodecContext->channels;
    float *buf = new float[frameCount*nChannels];

    while ( soundRecording->hasReadableData() )
    {
        //Populate the frame
        int samplesRead = soundRecording->read( buf, frameCount*nChannels );
        // Planar data
        int nFrames = samplesRead/nChannels;
        for ( int i = 0; i < nFrames; ++i )
        {
            for (int c = 0; c < nChannels; ++c )
            {
                samples[c][i] = buf[nChannels*i +c];
            }
        }
        // Fill a gap at the end with silence
        if ( samplesRead < frameCount*nChannels )
        {
            for ( int i = samplesRead; i < frameCount*nChannels; ++i )
            {
                for (int c = 0; c < nChannels; ++c )
                {
                    samples[c][i] = 0.0;
                }
            }
        }

    encodeFrame( mAvAudioFrame ) )
    }

    finish();
 }

bool AudioWriter::encodeFrame( AVFrame *frame )
{
    //send the frame for encoding
    int ret;

    if ( frame != nullptr )
    {
        frame->pts = mAudFrameCounter++;
    }
    avcodec_send_frame(mAvCodecContext, frame );

    while (ret >= 0)
    {
        ret = avcodec_receive_packet(mAvCodecContext, mAvPacket);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF )
        {
            break;
        }
        else
            if (ret < 0) {
             return false;
        }
        av_packet_rescale_ts(mAvPacket, mAvCodecContext->time_base, mAvOutputStream->time_base);
        mAvPacket->stream_index = mAvOutputStream->index;

        av_interleaved_write_frame(mAvFormatContext, mAvPacket);
         av_packet_unref(mAvPacket);
    }

    return true;
}

void AudioWriter::finish()
{
    // Flush by sending a null frame
    encodeFrame( nullptr );

    av_write_trailer(mAvFormatContext);
}


由于生成的文件包含录制的音乐,因此操作音频数据的代码似乎是正确的(除非我以某种方式覆盖其他内存)。

不准确的持续时间和比特率表明有关时间的信息没有得到适当的管理。我使用一个简单的递增整数来设置帧的 pts。我不清楚设置时间戳和流索引的代码实现了什么——以及它是否有必要:我从所谓的工作代码中复制了它,但我见过没有它的其他代码。

谁能看到我做错了什么?

最佳答案

时间戳必须正确。将 time_base 设置为 1/sample_rate 并将时间戳每帧增加 1024。注意:1024 是 aac 特定的。如果更改编解码器,则需要更改帧大小。

关于android - ffmpeg 编码音频中的持续时间和比特率不正确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56380233/

相关文章:

java - 无法打开安卓工作室。 (JDK相关问题)

android - 命令 APP_CMD_SAVE_STATE 的目的是什么?

math - 了解 fft 信号分解

language-agnostic - 我可以通过立体声信号在频域中获得更高的分辨率吗?

PHP,将 UTF-8 转换为 ASCII 8 位

javascript - 我无法在安装了 phonegap 插件的 eclipse 中使用 css、javascript 文件

android - 如何在Android中使用Robotium编写测试用例方法

android - 混合音频文件

Python:通过 utf-8 字符串进行迭代 -> 迭代器的数据类型/编码是什么?

Java 字符串 HTTP 编码