PCM - > AAC(恩codeR) - >的PCM(德codeR)在实时与正确的优化实时、正确、GT、PCM

2023-09-12 07:03:09 作者:大吼一声继续走

我试图实施

AudioRecord (MIC) ->

PCM -> AAC Encoder
AAC -> PCM Decode

-> AudioTrack??  (SPEAKER)

媒体codeC 在Android 4.1+(API16)。

with MediaCodec on Android 4.1+ (API16).

首先,我成功(但不知道正确的优化)实施 PCM - > AAC恩codeR 媒体codeC 如预期如下

Firstly, I successfully (but not sure correctly optimized) implemented PCM -> AAC Encoder by MediaCodec as intended as below

private boolean setEncoder(int rate)
{
    encoder = MediaCodec.createEncoderByType("audio/mp4a-latm");
    MediaFormat format = new MediaFormat();
    format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
    format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
    format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
    format.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024);//AAC-HE 64kbps
    format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectHE);
    encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    return true;
}

输入:PCM比特率= ​​44100(赫兹)×16(位)×1(MONORAL)= 705600比特/秒

输出:AAC-HE比特率= ​​64×1024(位)= 65536比特/秒

因此,数据量约为COM pressed X11 ,然后我通过观察日志证实了这一工作。

So, the data size is approximately compressed x11 ,and I confirmed this working by observing a log

AudioRe codeR:4096字节读 AudioEn codeR:369字节连接codeD

数据大小约为COM pressed X11 ,到目前为止好。

the data size is approximately compressed x11, so far so good.

现在,我有一个UDP服务器接收EN codeD数据,然后去code吧。

Now, I have a UDP server to receive the encoded data, then decode it.

本德codeR配置文件设置如下:

The decoder profile is set as follows:

private boolean setDecoder(int rate)
{
    decoder = MediaCodec.createDecoderByType("audio/mp4a-latm");
    MediaFormat format = new MediaFormat();
    format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
    format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
    format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
    format.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024);//AAC-HE 64kbps
    format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectHE);
    decoder.configure(format, null, null, 0);

    return true;
}

由于UDPserver数据包缓冲区大小为 1024

UDPserver:收到1024字节

和,因为这是COM pressed AAC数据,我希望解码规模将

and since this is the compressed AAC data, I would expect the decoding size will be

大约是1024 X11 ,但实际的结果是

approximately 1024 x11, however the actual result is

AudioDe codeR:8192字节德codeD

这是约 X8 ,我觉得有些不妥。

It's approximately x8, and I feel something wrong.

本德codeR code是如下:

The decoder code is as follows:

    IOudpPlayer = new Thread(new Runnable()
    {
        public void run()
        {
            SocketAddress sockAddress;
            String address;

            int len = 1024;
            byte[] buffer2 = new byte[len];
            DatagramPacket packet;

            byte[] data;

            ByteBuffer[] inputBuffers;
            ByteBuffer[] outputBuffers;

            ByteBuffer inputBuffer;
            ByteBuffer outputBuffer;

            MediaCodec.BufferInfo bufferInfo;
            int inputBufferIndex;
            int outputBufferIndex;
            byte[] outData;
            try
            {
                decoder.start();
                isPlaying = true;
                while (isPlaying)
                {
                    try
                    {
                        packet = new DatagramPacket(buffer2, len);
                        ds.receive(packet);

                        sockAddress = packet.getSocketAddress();
                        address = sockAddress.toString();

                        Log.d("UDP Receiver"," received !!! from " + address);

                        data = new byte[packet.getLength()];
                        System.arraycopy(packet.getData(), packet.getOffset(), data, 0, packet.getLength());

                        Log.d("UDP Receiver",  data.length + " bytes received");

                        //===========
                        inputBuffers = decoder.getInputBuffers();
                        outputBuffers = decoder.getOutputBuffers();
                        inputBufferIndex = decoder.dequeueInputBuffer(-1);
                        if (inputBufferIndex >= 0)
                        {
                            inputBuffer = inputBuffers[inputBufferIndex];
                            inputBuffer.clear();

                            inputBuffer.put(data);

                            decoder.queueInputBuffer(inputBufferIndex, 0, data.length, 0, 0);
                        }

                        bufferInfo = new MediaCodec.BufferInfo();
                        outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);

                        while (outputBufferIndex >= 0)
                        {
                            outputBuffer = outputBuffers[outputBufferIndex];

                            outputBuffer.position(bufferInfo.offset);
                            outputBuffer.limit(bufferInfo.offset + bufferInfo.size);

                            outData = new byte[bufferInfo.size];
                            outputBuffer.get(outData);

                            Log.d("AudioDecoder", outData.length + " bytes decoded");

                            decoder.releaseOutputBuffer(outputBufferIndex, false);
                            outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);

                        }



                        //===========

                    }
                    catch (IOException e)
                    {
                    }
                }

                decoder.stop();

            }
            catch (Exception e)
            {
            }
        }
    });

满code:

the full code:

https://gist.github.com/kenokabe/9029256

也需要权限:

 <uses-permission android:name="android.permission.INTERNET"></uses-permission>
 <uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission>

谁的作品为谷歌的成员法登告诉我

A member fadden who works for Google told me

貌似我没有设定位置和放大器;限制在输出缓冲器。

Looks like I'm not setting position & limit on the output buffer.

我已阅读 VP8编码的Nexus 5返回空/ 0帧的,但不知道如何正确地执行。

I have read VP8 Encoding Nexus 5 returns empty/0-Frames , but not sure how to implement correctly.

更新:我有点明白在哪里修改

貌似我没有设定位置和放大器;限制在输出缓冲器

Looks like I'm not setting position & limit on the output buffer.

,所以恩codeR和德codeR while循环中添加两行如下:

 outputBuffer.position(bufferInfo.offset);
 outputBuffer.limit(bufferInfo.offset + bufferInfo.size);

https://gist.github.com/kenokabe/9029256/revisions

然而,结果是一样的。

现在,我认为,这些错误:   W / SoftAAC2:AAC德codeR返回错误16388,代沉默表示该日codeR从第一个完全失败。这是一次中的数据是不可搜索的问题。 寻求在Android上AAC流中这是非常令人失望的,如果AAC德codeR不能以这种方式,但只增加了一些头处理流数据。

and now, I think, the errors: W/SoftAAC2﹕ AAC decoder returned error 16388, substituting silence. indicates this decoder fails completely from the first. It's again the data is not seekable issue. Seeking in AAC streams on Android It's very disappointing if the AAC decoder cannot handle the streaming data in this way but only with adding some header.

UPDATE2:UDP接收器做错了,所以修改

https://gist.github.com/kenokabe/9029256

现在,误差

W / SoftAAC2:AAC德codeR返回错误16388,代沉默 消失!

W/SoftAAC2﹕ AAC decoder returned error 16388, substituting silence. disappeared!!

所以,这表明去codeR工作没有错误,至少,

So, it indicates the decoder works without an error, at least,

然而,这是1个周期的日志:

however, this is the log of 1 cycle:

D/AudioRecoder﹕ 4096 bytes read
D/AudioEncoder﹕ 360 bytes encoded
D/UDP Receiver﹕ received !!! from /127.0.0.1:39000
D/UDP Receiver﹕ 360 bytes received
D/AudioDecoder﹕ 8192 bytes decoded

PCM(4096) - > AACen codeD(360) - > UDP-AAC(360) - >(应该是)PCM(8192)

PCM(4096)->AACencoded(360)->UDP-AAC(360)->(supposed to be )PCM(8192)

最终的结果是原来的PCM约2倍大小的东西仍然是错误的。

The final result is about 2x size of the original PCM, something is still wrong.

所以在这里我的问题是

你能正确地优化我的例子code才能正常工作? FFmpeg PCM 编码 AAC

Can you properly optimize my sample code to work correctly?

这是个正确的方式来使用 AudioTrack API来打去codeD PCM上飞的原始数据,而且你能告诉我正确的办法做到这一点?一个例子code是AP preciated。

Is it a right way to use AudioTrack API to play the decoded PCM raw data on the fly, and can you show me the proper way to do that? A example code is appreciated.

感谢你。

PS。在Android4.1 +(API16),我读过的东西都在API18(Andeoid 4.3+)比较容易,但很明显兼容性的原因,很遗憾,我不得不跳过MediaMuxer等我在这里的项目目标...

PS. My project targets on Android4.1+(API16), I've read things are easier on API18(Andeoid 4.3+), but for obvious compatibility reasons, unfortunately, I have to skip MediaMuxer etc. here...

推荐答案

测试这之后就是我想出了从修改您的code:

After testing this is what I came up with from modifying your code:

 package com.example.app;

    import android.app.Activity;

    import android.media.AudioManager;
    import android.media.MediaCodecInfo;
    import android.media.MediaFormat;
    import android.os.Bundle;

    import android.media.AudioFormat;
    import android.media.AudioRecord;
    import android.media.AudioTrack;
    import android.media.MediaCodec;

    import android.media.MediaRecorder.AudioSource;

    import android.util.Log;

    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    import java.net.SocketAddress;
    import java.net.SocketException;
    import java.nio.ByteBuffer;

    public class MainActivity extends Activity
    {
        private AudioRecord recorder;
        private AudioTrack player;

        private MediaCodec encoder;
        private MediaCodec decoder;

        private short audioFormat = AudioFormat.ENCODING_PCM_16BIT;
        private short channelConfig = AudioFormat.CHANNEL_IN_MONO;

        private int bufferSize;
        private boolean isRecording;
        private boolean isPlaying;

        private Thread IOrecorder;

        private Thread IOudpPlayer;


        private DatagramSocket ds;
        private final int localPort = 39000;

        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            IOrecorder = new Thread(new Runnable()
            {
                public void run()
                {
                    int read;
                    byte[] buffer1 = new byte[bufferSize];

                    ByteBuffer[] inputBuffers;
                    ByteBuffer[] outputBuffers;

                    ByteBuffer inputBuffer;
                    ByteBuffer outputBuffer;

                    MediaCodec.BufferInfo bufferInfo;
                    int inputBufferIndex;
                    int outputBufferIndex;

                    byte[] outData;

                    DatagramPacket packet;
                    try
                    {
                        encoder.start();
                        recorder.startRecording();
                        isRecording = true;
                        while (isRecording)
                        {
                            read = recorder.read(buffer1, 0, bufferSize);
                           // Log.d("AudioRecoder", read + " bytes read");
                            //------------------------

                            inputBuffers = encoder.getInputBuffers();
                            outputBuffers = encoder.getOutputBuffers();
                            inputBufferIndex = encoder.dequeueInputBuffer(-1);
                            if (inputBufferIndex >= 0)
                            {
                                inputBuffer = inputBuffers[inputBufferIndex];
                                inputBuffer.clear();

                                inputBuffer.put(buffer1);

                                encoder.queueInputBuffer(inputBufferIndex, 0, buffer1.length, 0, 0);
                            }

                            bufferInfo = new MediaCodec.BufferInfo();
                            outputBufferIndex = encoder.dequeueOutputBuffer(bufferInfo, 0);



                            while (outputBufferIndex >= 0)
                            {
                                outputBuffer = outputBuffers[outputBufferIndex];

                                outputBuffer.position(bufferInfo.offset);
                                outputBuffer.limit(bufferInfo.offset + bufferInfo.size);

                                outData = new byte[bufferInfo.size];
                                outputBuffer.get(outData);


                               // Log.d("AudioEncoder ", outData.length + " bytes encoded");
                                //-------------
                                packet = new DatagramPacket(outData, outData.length,
                                        InetAddress.getByName("127.0.0.1"), localPort);
                                ds.send(packet);
                                //------------

                                encoder.releaseOutputBuffer(outputBufferIndex, false);
                                outputBufferIndex = encoder.dequeueOutputBuffer(bufferInfo, 0);

                            }
                            // ----------------------;

                        }
                        encoder.stop();
                        recorder.stop();
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                }
            });



            IOudpPlayer = new Thread(new Runnable()
            {
                public void run()
                {
                    SocketAddress sockAddress;
                    String address;

                    int len = 2048
                    byte[] buffer2 = new byte[len];
                    DatagramPacket packet;

                    byte[] data;

                    ByteBuffer[] inputBuffers;
                    ByteBuffer[] outputBuffers;

                    ByteBuffer inputBuffer;
                    ByteBuffer outputBuffer;

                    MediaCodec.BufferInfo bufferInfo;
                    int inputBufferIndex;
                    int outputBufferIndex;
                    byte[] outData;
                    try
                    {
                        player.play();
                        decoder.start();
                        isPlaying = true;
                        while (isPlaying)
                        {
                            try
                            {
                                packet = new DatagramPacket(buffer2, len);
                                ds.receive(packet);

                                sockAddress = packet.getSocketAddress();
                                address = sockAddress.toString();

                             //   Log.d("UDP Receiver"," received !!! from " + address);

                                data = new byte[packet.getLength()];
                                System.arraycopy(packet.getData(), packet.getOffset(), data, 0, packet.getLength());

                               // Log.d("UDP Receiver",  data.length + " bytes received");

                                //===========
                                inputBuffers = decoder.getInputBuffers();
                                outputBuffers = decoder.getOutputBuffers();
                                inputBufferIndex = decoder.dequeueInputBuffer(-1);
                                if (inputBufferIndex >= 0)
                                {
                                    inputBuffer = inputBuffers[inputBufferIndex];
                                    inputBuffer.clear();

                                    inputBuffer.put(data);

                                    decoder.queueInputBuffer(inputBufferIndex, 0, data.length, 0, 0);
                                }

                                bufferInfo = new MediaCodec.BufferInfo();
                                outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);

                                while (outputBufferIndex >= 0)
                                {
                                    outputBuffer = outputBuffers[outputBufferIndex];

                                    outputBuffer.position(bufferInfo.offset);
                                    outputBuffer.limit(bufferInfo.offset + bufferInfo.size);

                                    outData = new byte[bufferInfo.size];
                                    outputBuffer.get(outData);

                                  //  Log.d("AudioDecoder", outData.length + " bytes decoded");

                                    player.write(outData, 0, outData.length);

                                    decoder.releaseOutputBuffer(outputBufferIndex, false);
                                    outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0..