如何使用Android MediaCodec生成AAC ADTS基本流 [英] How to generate the AAC ADTS elementary stream with Android MediaCodec

查看:1548
本文介绍了如何使用Android MediaCodec生成AAC ADTS基本流的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要做的是:使用Android的MediaCodec将原始PCM音频样本编码为原始AAC文件。

What I am trying to do: use Android's MediaCodec to encode raw PCM audio samples into a raw AAC file.

当我使用FFMPEG将生成的原始AAC文件打包成M4A容器时,FFMPEG会抱怨文件中缺少编解码器参数。

The problem I have: when I use FFMPEG to pack the generated raw AAC file into an M4A container, FFMPEG complains about missing codec parameters in the file.

详细信息:

由于我找不到生成输出AAC文件的音频编码器的任何MediaCodec示例代码,我尝试修改视频编码器转换成音频编码器。原始代码在这里: source_code

Since I can't find any MediaCodec sample code for the audio encoder that generates an output AAC file, I tried to modify the video encoder into an audio encoder. The original code is here: source_code

我配置了这样的音频编码器:

I configured the audio encoder like this:

    mEncoderFormat = MediaFormat.createAudioFormat("audio/mp4a-latm", (int)mAudioSampleRate, 2);

    // redundant?
    mEncoderFormat.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
    mEncoderFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, 
                      MediaCodecInfo.CodecProfileLevel.AACObjectELD);
    mEncoderFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, kSampleRates);
    mEncoderFormat.setInteger(MediaFormat.KEY_BIT_RATE, kBitRates);
    mEncoderFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2);
    testEncoderWithFormat("audio/mp4a-latm", mEncoderFormat);

    try {
        codec.configure(
                mEncoderFormat,
                null /* surface */,
                null /* crypto */,
                MediaCodec.CONFIGURE_FLAG_ENCODE);
    } catch (IllegalStateException e) {
        Log.e(TAG, "codec '" + componentName + "' failed configuration.");
        return;
    }
    Log.d(TAG, "  testEncoder configured with format = " + format);

然后,我为每个帧馈送10ms的PCM样本的编码器。编码器采取每一帧,产生比特流帧,并将比特流写入FileOutputStream。循环继续,直到输入文件的结尾。

Then I feed the encoder with 10ms worth of PCM samples per frame. The encoder takes each frame, generates a frame of bitstream, and I write the bitstream into an FileOutputStream. The loop continues until the end of the input file.

代码运行到完成。我做adb拉从生成的AAC文件从设备到我的电脑,并使用FFMPEG来阅读它。以下是FFMPEG命令和错误消息:

The code runs to the finish. I do 'adb pull' to get the generated AAC file from the device to my PC, and use FFMPEG to read it. Below is the command and the error FFMPEG spits out:

$ ffmpeg -f aac -i BlessedNoColor_nexus7_api18.aac
ffmpeg version N-45739-g04bf2e7 Copyright (c) 2000-2012 the FFmpeg developers
  built on Oct 20 2012 00:20:36 with gcc 4.7.2 (GCC)
  configuration: --enable-gpl --enable-version3 --disable-pthreads --enable-runt
ime-cpudetect --enable-avisynth --enable-bzlib --enable-frei0r --enable-libass -
-enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libfreetype --enab
le-libgsm --enable-libmp3lame --enable-libnut --enable-libopenjpeg --enable-libo
pus --enable-librtmp --enable-libschroedinger --enable-libspeex --enable-libtheo
ra --enable-libutvideo --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-li
bvorbis --enable-libvpx --enable-libx264 --enable-libxavs --enable-libxvid --ena
ble-zlib
  libavutil      51. 76.100 / 51. 76.100
  libavcodec     54. 67.100 / 54. 67.100
  libavformat    54. 33.100 / 54. 33.100
  libavdevice    54.  3.100 / 54.  3.100
  libavfilter     3. 19.103 /  3. 19.103
  libswscale      2.  1.101 /  2.  1.101
  libswresample   0. 16.100 /  0. 16.100
  libpostproc    52.  1.100 / 52.  1.100
[aac @ 00000000002efae0] channel element 2.0 is not allocated
[aac @ 00000000003cf520] decoding for stream 0 failed
[aac @ 00000000003cf520] Could not find codec parameters for stream 0 (Audio: aac, 0 channels, s16): unspecified sample rate
Consider increasing the value for the 'analyzeduration' and 'probesize' options
[aac @ 00000000003cf520] Estimating duration from bitrate, this may be inaccurate

BlessedNoColor_nexus7_api18.aac: could not find codec parameters

我的问题:


  1. 我在拨打编解码器之前配置了编码器。开始()。为什么生成的AAC文件缺少编解码器参数?

  2. 在原始视频编解码器示例中,参数csd-0从编码器传递到解码器,但不会写入比特流文件明确。我需要将它们明确写入AAC文件吗?

  3. 我将输入的PCM样本分成10ms,每帧不一定会产生完整的输出数据包。对于每个输入帧,我只需将编码器输出的任何内容写入文件。这是一个令人担忧的原因吗?

任何帮助将深受赞赏。如果有一个示例项目,我会尝试在这里做的很棒。如果我的源代码可以帮助您帮助我,我会发布。我需要做一些清理。谢谢!

Any helps will be deeply appreciated. It'd be great if there is a sample project that does what I'm trying to do here. If my source code can help you help me, I'll post it. I need to do some cleanup. Thanks!

编辑:将标题从MediaCodec缺少编解码器参数生成的基本AAC文件更改为如何生成AAC ADTS小组流媒体与Android MediaCodec

Edit: Changed the title from "Elementary AAC file generated by MediaCodec missing codec parameters" to "How to generate the AAC ADTS elementary stream with Android MediaCodec"

推荐答案

我终于生成可在Android设备和Windows主机上播放的AAC文件。我在这里发布我的解决方案,希望它可以帮助别人。

I finally generated AAC files that are playable on both the Android device and the Windows host computer. I am posting my solution here, hoping it could help others.

首先,我之前假设Android MediaCodec编码器生成基本的AAC流是不准确的。 MediaCodec编码器生成原始AAC流。这就是为什么文件无法播放的原因。原始AAC流需要转换为可播放格式,例如 ADTS 另一篇文章,提出了类似的问题,并已一个很好的答案。然而,新手可能不一定了解那里的简要描述。我没有得到它第一次阅读这篇文章。

First, my previous assumption that the Android MediaCodec encoder generates the elementary AAC stream was not accurate. The MediaCodec encoder generates the raw AAC stream. That's why the files could not be played. The raw AAC stream needs to be converted into a playable format, such as the ADTS stream. I have changed the title of this post to reflect my new understanding. There was another post that asked a similar question, and had an excellent answer. However, a novice may not necessarily understand the brief descriptions there. I didn't quite get it the 1st time I read that post.

所以,为了生成可以由媒体播放器播放的AAC比特流,我开始从fadden在他的第一个评论中给出的EncoderTest示例,但修改了原始代码以添加每个输出帧(访问单元)的ADTS标题,并将生成的流写入一个文件(替换原始代码的第248到267行,以下代码片段):

So, in order to generate an AAC bitstream that can be played by a media player, I started from the EncoderTest example given by fadden in his 1st comment, but modified the original code to add the ADTS header per output frame (access unit), and to write the resulting stream into a file (replaced lines 248 through 267 of the original code with the following code snippet):

if (index >= 0) {
    int outBitsSize   = info.size;
    int outPacketSize = outBitsSize + 7;    // 7 is ADTS size
    ByteBuffer outBuf = codecOutputBuffers[index];

    outBuf.position(info.offset);
    outBuf.limit(info.offset + outBitsSize);
    try {
        byte[] data = new byte[outPacketSize];  //space for ADTS header included
        addADTStoPacket(data, outPacketSize);
        outBuf.get(data, 7, outBitsSize);
        outBuf.position(info.offset);
        mFileStream.write(data, 0, outPacketSize);  //open FileOutputStream beforehand
    } catch (IOException e) {
        Log.e(TAG, "failed writing bitstream data to file");
        e.printStackTrace();
    }

    numBytesDequeued += info.size;

    outBuf.clear();
    codec.releaseOutputBuffer(index, false /* render */);
    Log.d(TAG, "  dequeued " + outBitsSize + " bytes of output data.");
    Log.d(TAG, "  wrote " + outPacketSize + " bytes into output file.");
}
else if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
}
else if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
    codecOutputBuffers = codec.getOutputBuffers();
}

在循环之外,我定义了这样的功能addADTStoPacket:

Outside the loop, I defined the function addADTStoPacket like this:

/**
 *  Add ADTS header at the beginning of each and every AAC packet.
 *  This is needed as MediaCodec encoder generates a packet of raw
 *  AAC data.
 *
 *  Note the packetLen must count in the ADTS header itself.
 **/
private void addADTStoPacket(byte[] packet, int packetLen) {
    int profile = 2;  //AAC LC
                      //39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
    int freqIdx = 4;  //44.1KHz
    int chanCfg = 2;  //CPE

    // fill in ADTS data
    packet[0] = (byte)0xFF;
    packet[1] = (byte)0xF9;
    packet[2] = (byte)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
    packet[3] = (byte)(((chanCfg&3)<<6) + (packetLen>>11));
    packet[4] = (byte)((packetLen&0x7FF) >> 3);
    packet[5] = (byte)(((packetLen&7)<<5) + 0x1F);
    packet[6] = (byte)0xFC;
}

我还添加了代码来控制如​​何停止生成AAC ADTS流,但是这是具体的应用程序,所以我不会在这里详细介绍。通过所有这些更改,生成的AAC文件可以在Android设备上,在我的Windows PC上播放,ffmpeg对它们很满意。

I also added code to control how to stop generating the AAC ADTS stream, but that's application specific, so I won't detail here. With all these changes, the generated AAC files can be played on the Android device, on my Windows PC, and ffmpeg is happy with them.

这篇关于如何使用Android MediaCodec生成AAC ADTS基本流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆