【问题标题】:How to write audio files on Android, preferably .3gp如何在Android上编写音频文件,最好是.3gp
【发布时间】:2012-10-27 00:32:39
【问题描述】:

我已经研究了很长一段时间,但仍然没有乐趣。

我知道 MediaRecorder 会读取音频源并以多种格式之一直接写入文件。而且我也知道 AudioRecord 会读取音频源并让您处理原始数据,但是有没有办法从您的应用中写入数据?

例如,我想读取音频,以某种简单的方式对其进行过滤,然后写入 .3gp 文件。有没有办法做到这一点?搜索示例,我只能找到一种通过手动编写标题来生成.wav文件的方法。

【问题讨论】:

    标签: android audio audio-recording


    【解决方案1】:

    编写您自己的编码器或使用现有的编码器,例如使用 JNI 的FFMpeg

    这个问题也很有用。
    FFmpeg on Android

    【讨论】:

    • 叹息。我希望有一种方法可以访问现有的 3gp/mp4/etc。 Android 中的编码器。
    • 糟糕,抱歉,我们还没有支持它们:(
    • 乐叹息。好的,赞成这个答案,但不要回答这个问题,希望有一天真正的答案会出现。
    【解决方案2】:

    【讨论】:

    • 嗯,我希望有一些我可以在 2.2 中使用的东西,而这是 API 16 中的新功能,但我认为这是我能得到的最佳答案。
    • 我最近将一个解复用器从 libstagefright(Android 的媒体库)从 C++ 翻译成 Java,但这可能是相当多的工作。
    【解决方案3】:

    .aac 会为您工作吗?如果是,那么检查一下: 使用管理器类通过新线程运行此可运行文件。在 startRecording 按钮上单击:

    1. 初始化新线程。
    2. 创建带有.aac 扩展名的文件。
    3. 创建文件的输出流。
    4. 设置输出
    5. SetListener 并执行线程。

    OnStopClick :

    1. 中断线程,音频将保存在文件中。

    Here is full gist of for reference :

    import android.media.AudioFormat;
    import android.media.AudioRecord;
    import android.media.MediaCodec;
    import android.media.MediaCodecInfo;
    import android.media.MediaFormat;
    import android.media.MediaRecorder;
    import android.os.Build;
    import android.util.Log;
    
    import java.io.IOException;
    import java.io.OutputStream;
    import java.nio.ByteBuffer;
    
    public class AudioRecordThread implements Runnable {
    
        private static final String TAG = AudioRecordThread.class.getSimpleName();
    
        private static final int SAMPLE_RATE = 44100;
        private static final int SAMPLE_RATE_INDEX = 4;
        private static final int CHANNELS = 1;
        private static final int BIT_RATE = 32000;
    
        private final int bufferSize;
        private final MediaCodec mediaCodec;
        private final AudioRecord audioRecord;
        private final OutputStream outputStream;
    
        private OnRecorderFailedListener onRecorderFailedListener;
    
    
        AudioRecordThread(OutputStream outputStream, OnRecorderFailedListener onRecorderFailedListener) throws IOException {
    
            this.bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
            this.audioRecord = createAudioRecord(this.bufferSize);
            this.mediaCodec = createMediaCodec(this.bufferSize);
            this.outputStream = outputStream;
            this.onRecorderFailedListener = onRecorderFailedListener;
    
            this.mediaCodec.start();
    
            try {
                audioRecord.startRecording();
            } catch (Exception e) {
                Log.w(TAG, e);
                mediaCodec.release();
                throw new IOException(e);
            }
        }
    
        @Override
        public void run() {
            if (onRecorderFailedListener != null) {
                Log.d(TAG, "onRecorderStarted");
                onRecorderFailedListener.onRecorderStarted();
            }
            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            ByteBuffer[] codecInputBuffers = mediaCodec.getInputBuffers();
            ByteBuffer[] codecOutputBuffers = mediaCodec.getOutputBuffers();
    
            try {
                while (!Thread.interrupted()) {
    
                    boolean success = handleCodecInput(audioRecord, mediaCodec, codecInputBuffers, Thread.currentThread().isAlive());
                    if (success)
                        handleCodecOutput(mediaCodec, codecOutputBuffers, bufferInfo, outputStream);
                }
            } catch (IOException e) {
                Log.w(TAG, e);
            } finally {
                mediaCodec.stop();
                audioRecord.stop();
    
                mediaCodec.release();
                audioRecord.release();
    
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    
        private boolean handleCodecInput(AudioRecord audioRecord,
                                         MediaCodec mediaCodec, ByteBuffer[] codecInputBuffers,
                                         boolean running) throws IOException {
            byte[] audioRecordData = new byte[bufferSize];
            int length = audioRecord.read(audioRecordData, 0, audioRecordData.length);
    
            if (length == AudioRecord.ERROR_BAD_VALUE ||
                    length == AudioRecord.ERROR_INVALID_OPERATION ||
                    length != bufferSize) {
    
                if (length != bufferSize) {
                    if (onRecorderFailedListener != null) {
                        Log.d(TAG, "length != BufferSize calling onRecordFailed");
                        onRecorderFailedListener.onRecorderFailed();
                    }
                    return false;
                }
            }
    
            int codecInputBufferIndex = mediaCodec.dequeueInputBuffer(10 * 1000);
    
            if (codecInputBufferIndex >= 0) {
                ByteBuffer codecBuffer = codecInputBuffers[codecInputBufferIndex];
                codecBuffer.clear();
                codecBuffer.put(audioRecordData);
                mediaCodec.queueInputBuffer(codecInputBufferIndex, 0, length, 0, running ? 0 : MediaCodec.BUFFER_FLAG_END_OF_STREAM);
            }
    
            return true;
        }
    
        private void handleCodecOutput(MediaCodec mediaCodec,
                                       ByteBuffer[] codecOutputBuffers,
                                       MediaCodec.BufferInfo bufferInfo,
                                       OutputStream outputStream)
                throws IOException {
            int codecOutputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
    
            while (codecOutputBufferIndex != MediaCodec.INFO_TRY_AGAIN_LATER) {
                if (codecOutputBufferIndex >= 0) {
                    ByteBuffer encoderOutputBuffer = codecOutputBuffers[codecOutputBufferIndex];
    
                    encoderOutputBuffer.position(bufferInfo.offset);
                    encoderOutputBuffer.limit(bufferInfo.offset + bufferInfo.size);
    
                    if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != MediaCodec.BUFFER_FLAG_CODEC_CONFIG) {
                        byte[] header = createAdtsHeader(bufferInfo.size - bufferInfo.offset);
    
    
                        outputStream.write(header);
    
                        byte[] data = new byte[encoderOutputBuffer.remaining()];
                        encoderOutputBuffer.get(data);
                        outputStream.write(data);
                    }
    
                    encoderOutputBuffer.clear();
    
                    mediaCodec.releaseOutputBuffer(codecOutputBufferIndex, false);
                } else if (codecOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                    codecOutputBuffers = mediaCodec.getOutputBuffers();
                }
    
                codecOutputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
            }
        }
    
    
        private byte[] createAdtsHeader(int length) {
            int frameLength = length + 7;
            byte[] adtsHeader = new byte[7];
    
            adtsHeader[0] = (byte) 0xFF; // Sync Word
            adtsHeader[1] = (byte) 0xF1; // MPEG-4, Layer (0), No CRC
            adtsHeader[2] = (byte) ((MediaCodecInfo.CodecProfileLevel.AACObjectLC - 1) << 6);
            adtsHeader[2] |= (((byte) SAMPLE_RATE_INDEX) << 2);
            adtsHeader[2] |= (((byte) CHANNELS) >> 2);
            adtsHeader[3] = (byte) (((CHANNELS & 3) << 6) | ((frameLength >> 11) & 0x03));
            adtsHeader[4] = (byte) ((frameLength >> 3) & 0xFF);
            adtsHeader[5] = (byte) (((frameLength & 0x07) << 5) | 0x1f);
            adtsHeader[6] = (byte) 0xFC;
    
            return adtsHeader;
        }
    
        private AudioRecord createAudioRecord(int bufferSize) {
            AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE,
                    AudioFormat.CHANNEL_IN_MONO,
                    AudioFormat.ENCODING_PCM_16BIT, bufferSize * 10);
    
            if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
                Log.d(TAG, "Unable to initialize AudioRecord");
                throw new RuntimeException("Unable to initialize AudioRecord");
            }
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                if (android.media.audiofx.NoiseSuppressor.isAvailable()) {
                    android.media.audiofx.NoiseSuppressor noiseSuppressor = android.media.audiofx.NoiseSuppressor
                            .create(audioRecord.getAudioSessionId());
                    if (noiseSuppressor != null) {
                        noiseSuppressor.setEnabled(true);
                    }
                }
            }
    
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                if (android.media.audiofx.AutomaticGainControl.isAvailable()) {
                    android.media.audiofx.AutomaticGainControl automaticGainControl = android.media.audiofx.AutomaticGainControl
                            .create(audioRecord.getAudioSessionId());
                    if (automaticGainControl != null) {
                        automaticGainControl.setEnabled(true);
                    }
                }
            }
    
    
            return audioRecord;
        }
    
        private MediaCodec createMediaCodec(int bufferSize) throws IOException {
            MediaCodec mediaCodec = MediaCodec.createEncoderByType("audio/mp4a-latm");
            MediaFormat mediaFormat = new MediaFormat();
    
            mediaFormat.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
            mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, SAMPLE_RATE);
            mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, CHANNELS);
            mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, bufferSize);
            mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
            mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
    
            try {
                mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            } catch (Exception e) {
                Log.w(TAG, e);
                mediaCodec.release();
                throw new IOException(e);
            }
    
            return mediaCodec;
        }
    
        interface OnRecorderFailedListener {
            void onRecorderFailed();
    
            void onRecorderStarted();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-11-01
      • 2011-12-16
      • 1970-01-01
      • 2021-02-11
      • 2015-06-26
      • 2011-06-08
      • 2013-04-26
      • 2020-09-08
      • 1970-01-01
      相关资源
      最近更新 更多