使用MediaCodec创建AAC编码器时出现错误

4
问题很简单,但我没有任何线索来解决它:
我写了一行代码,在我的 Nexus 4(Android 4.4.2)上创建了一个 AAC 编码器。
MediaCodec codec = MediaCodec.createEncoderByType("audio/mp4a-latm");

在“codec”中保存的返回值不为空,但是我在Logcat中收到了一个红色的错误消息:
03-20 15:25:08.985: E/OMXMaster(24517): A component of name 'OMX.qcom.audio.decoder.aac' already exists, ignoring this one.

我还尝试了另一种方法:

MediaCodec codec = MediaCodec.createByCodecName("OMX.google.aac.encoder");

我得到了相同的错误结果。

在使用MediaCodec之前,我是否错过了任何初始化步骤?我在官方文档中没有找到任何相关信息。

有人遇到过这个问题吗?

实际上,我正在尝试将PCM编码为AAC文件。我已经阅读了@hubeir的帖子。看起来他已经解决了这个问题。我也做了同样的事情:(1)设置mediacodec并提供PCM数据以获得编码帧。为此,我阅读了来自CTS的代码。每个编码帧长度约为371-379。(2)向帧添加adts头,然后保存到文件。我逐位检查了头部,它是正确的。但是该文件仍然无法播放。因此,我认为错误日志可能是问题所在。

以下是我的整个代码,供参考:

MediaCodec codec = MediaCodec.createByCodecName("OMX.google.aac.encoder");      
MediaFormat format = new MediaFormat();
    format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
    format.setInteger(MediaFormat.KEY_AAC_PROFILE, 
                      MediaCodecInfo.CodecProfileLevel.AACObjectELD);
    format.setInteger(MediaFormat.KEY_SAMPLE_RATE, nSamplerate);
    format.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
    format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, nChannels);

    codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    codec.start();

    ByteBuffer[] inputBuffers = codec.getInputBuffers();
    ByteBuffer[] outputBuffers = codec.getOutputBuffers();

    boolean bEndInput = false;
    boolean bEndOutput = false;

while(true)
{
    if (!bEndInput)
    {
        int inputBufferIndex = codec.dequeueInputBuffer(0);
        if (inputBufferIndex >= 0)
        {
            int nLen = app.readPCM(nHandle,inputBuffers[inputBufferIndex]);//This line read PCM, return 0 if end of data.
            int nBufLen = inputBuffers[inputBufferIndex].capacity(); 
            if (nLen == nBufLen)
                codec.queueInputBuffer(inputBufferIndex, 0, nLen, 0,  MediaCodec.BUFFER_FLAG_SYNC_FRAME);
            else if (nLen < nBufLen)
            {
                codec.queueInputBuffer(inputBufferIndex, 0, nLen, 0,  MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                bEndInput = true;
                break;
            }   
        }
    }

    MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
    if (!bEndOutput)
    {
            int outputBufferIndex = codec.dequeueOutputBuffer(info, 0);
            if (outputBufferIndex  >= 0)
            {
                int outBitsSize   = info.size;
                Log.d("test", "Offset:"+info.offset);
                Log.d("test", "Size:"+info.size);
                Log.d("test", "Time:"+info.presentationTimeUs);
                Log.d("test", "Flags:"+info.flags);
                if (outBitsSize <= 10)
                {
                    codec.releaseOutputBuffer(outputBufferIndex, false /* render */);
                    continue;
                }

                int outPacketSize = outBitsSize + 7;    // 7 is ADTS size
                ByteBuffer outBuf = outputBuffers[outputBufferIndex];

                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);
                    outputStream.write(data, 0, outPacketSize);  //open FileOutputStream beforehand
                } catch (IOException e) {
                    Log.e("test", "failed writing bitstream data to file");
                    e.printStackTrace();
                }

                outBuf.clear();
                codec.releaseOutputBuffer(outputBufferIndex, false /* render */);
                Log.d("test", "  dequeued " + outBitsSize + " bytes of output data.");
                Log.d("test", "  wrote " + outPacketSize + " bytes into output file.");

                if (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM)
                {
                    bEndOutput = true;
                    //break;
                }   
            }
            else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) 
            {
                 outputBuffers = codec.getOutputBuffers();
            } 
            else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) 
            {

            }
    }

    if (bEndInput && bEndOutput)
        break;
}

有些驱动程序在“错误”级别记录所有内容,即使只是轻微的警告。在使用MediaCodec时,即使一切正常,也经常会看到大量的“错误”。你的应用程序工作有问题吗?还是只是想知道这些消息是关于什么的? - fadden
实际上我正在尝试将PCM编码为AAC文件。我已经阅读了这篇帖子。看起来他已经成功了。我也做了同样的事情:(1)设置mediacodec并提供PCM数据以获取编码帧。为此,我从cst中阅读了代码。每个编码帧的长度约为371-379。 - Matt
(2)将adts头添加到帧中,然后保存到文件。我已经逐位检查了添加的adts头,它是正确的。但是该文件仍无法播放。因此,我认为错误日志可能是问题所在。@hubeir - Matt
3个回答

6
我明白了。
(1) 媒体格式中的错误设置:
format.setInteger(MediaFormat.KEY_AAC_PROFILE, 
                      MediaCodecInfo.CodecProfileLevel.AACObjectELD);

应该是

format.setInteger(MediaFormat.KEY_AAC_PROFILE, 
                      MediaCodecInfo.CodecProfileLevel.AACObjectLC);

(2) 当 (info.flags==0) 时,编码的帧才能使用 adts 文件写入文件。
(3) 输出文件的文件名后缀应为 "aac"。"mp4" 或 "m4a" 可能对某些应用程序不起作用。
MediaCodec codec = MediaCodec.createByCodecName("OMX.google.aac.encoder");      
MediaFormat format = new MediaFormat();
    format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
    format.setInteger(MediaFormat.KEY_AAC_PROFILE, 
                      MediaCodecInfo.CodecProfileLevel.AACObjectLC); //fixed version
    format.setInteger(MediaFormat.KEY_SAMPLE_RATE, nSamplerate);
    format.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
    format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, nChannels);

    codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    codec.start();

    ByteBuffer[] inputBuffers = codec.getInputBuffers();
    ByteBuffer[] outputBuffers = codec.getOutputBuffers();

    boolean bEndInput = false;
    boolean bEndOutput = false;

while(true)
{
    if (!bEndInput)
    {
        int inputBufferIndex = codec.dequeueInputBuffer(0);
        if (inputBufferIndex >= 0)
        {
            int nLen = app.readPCM(nHandle,inputBuffers[inputBufferIndex]);//This line read PCM, return 0 if end of data.
            int nBufLen = inputBuffers[inputBufferIndex].capacity(); 
            if (nLen == nBufLen)
                codec.queueInputBuffer(inputBufferIndex, 0, nLen, 0,  MediaCodec.BUFFER_FLAG_SYNC_FRAME);
            else if (nLen < nBufLen)
            {
                codec.queueInputBuffer(inputBufferIndex, 0, nLen, 0,  MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                bEndInput = true;
                break;
            }   
        }
    }

    MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
    if (!bEndOutput)
    {
            int outputBufferIndex = codec.dequeueOutputBuffer(info, 0);
            if (outputBufferIndex  >= 0)
            {
                int outBitsSize   = info.size;
                Log.d("test", "Offset:"+info.offset);
                Log.d("test", "Size:"+info.size);
                Log.d("test", "Time:"+info.presentationTimeUs);
                Log.d("test", "Flags:"+info.flags);
                if (info.flags != 0) //fixed version
                {
                    codec.releaseOutputBuffer(outputBufferIndex, false /* render */);
                    continue;
                }

                int outPacketSize = outBitsSize + 7;    // 7 is ADTS size
                ByteBuffer outBuf = outputBuffers[outputBufferIndex];

                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);
                    outputStream.write(data, 0, outPacketSize);  //open FileOutputStream beforehand
                } catch (IOException e) {
                    Log.e("test", "failed writing bitstream data to file");
                    e.printStackTrace();
                }

                outBuf.clear();
                codec.releaseOutputBuffer(outputBufferIndex, false /* render */);
                Log.d("test", "  dequeued " + outBitsSize + " bytes of output data.");
                Log.d("test", "  wrote " + outPacketSize + " bytes into output file.");

                if (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM)
                {
                    bEndOutput = true;
                    //break;
                }   
            }
            else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) 
            {
                 outputBuffers = codec.getOutputBuffers();
            } 
            else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) 
            {

            }
    }

    if (bEndInput && bEndOutput)
        break;
}

0

请检查位于设备文件夹下的media_codecs_xxxx.xml文件(注意:xxxx通常取决于您的硬件平台)。

在这里,您可能会找到OMX.qcom.audio.decoder.aac和OMX.google.aac.encoder。请使用其中任何一个并注释掉另一个。

请参考https://source.android.com/devices/media.html(将编解码器公开给框架),这将对您有所帮助。


0

有趣的是,之前我在某个设备上遇到了类似的问题:如果使用MediaCodec.createEncoderByType(),则会创建AAC解码器而不是编码器。为了解决这个问题,我使用了以下方法:

  String codecName = selectEncoder(mime);
  mediaCodec = MediaCodec.createByCodecName(codecName);

并且

  private String selectEncoder(String mime) {
    for (int index = 0; index < MediaCodecList.getCodecCount(); index++) {
        MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(index);

        if (!codecInfo.isEncoder()) {
            continue;
        }

        for (String type : codecInfo.getSupportedTypes()) {
            if (type.equalsIgnoreCase(mime)) {
                return codecInfogetName();
            }
        }
    }
    return null;
 }

这样的东西


谢谢您的回复。我尝试了这段代码,但仍然出现错误。 - Matt

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