That fuck shit the fascists are using
at master 222 lines 7.6 kB view raw
1package org.tm.archive.audio; 2 3import android.media.AudioFormat; 4import android.media.AudioRecord; 5import android.media.MediaCodec; 6import android.media.MediaCodecInfo; 7import android.media.MediaFormat; 8import android.media.MediaRecorder; 9import android.os.ParcelFileDescriptor; 10import android.os.Process; 11 12import org.signal.core.util.StreamUtil; 13import org.signal.core.util.logging.Log; 14import org.tm.archive.util.Util; 15 16import java.io.IOException; 17import java.io.OutputStream; 18import java.nio.ByteBuffer; 19 20public class AudioCodec implements Recorder { 21 22 private static final String TAG = Log.tag(AudioCodec.class); 23 24 private static final int SAMPLE_RATE = 44100; 25 private static final int SAMPLE_RATE_INDEX = 4; 26 private static final int CHANNELS = 1; 27 private static final int BIT_RATE = 32000; 28 29 private final int bufferSize; 30 private final MediaCodec mediaCodec; 31 private final AudioRecord audioRecord; 32 33 private boolean running = true; 34 private boolean failed = false; 35 private boolean finished = false; 36 37 public AudioCodec() throws IOException { 38 this.bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); 39 this.audioRecord = createAudioRecord(this.bufferSize); 40 this.mediaCodec = createMediaCodec(this.bufferSize); 41 42 this.mediaCodec.start(); 43 44 try { 45 audioRecord.startRecording(); 46 } catch (Exception e) { 47 Log.w(TAG, e); 48 mediaCodec.release(); 49 throw new IOException(e); 50 } 51 } 52 53 @Override 54 public void start(ParcelFileDescriptor fileDescriptor) { 55 Log.i(TAG, "Recording voice note using AudioCodec."); 56 start(new ParcelFileDescriptor.AutoCloseOutputStream(fileDescriptor)); 57 } 58 59 @Override 60 public synchronized void stop() { 61 running = false; 62 while (!finished) Util.wait(this, 0); 63 } 64 65 private void start(final OutputStream outputStream) { 66 new Thread(new Runnable() { 67 @Override 68 public void run() { 69 Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO); 70 MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); 71 byte[] audioRecordData = new byte[bufferSize]; 72 ByteBuffer[] codecInputBuffers = mediaCodec.getInputBuffers(); 73 ByteBuffer[] codecOutputBuffers = mediaCodec.getOutputBuffers(); 74 75 try { 76 while (true) { 77 boolean running = isRunning(); 78 79 handleCodecInput(audioRecord, audioRecordData, mediaCodec, codecInputBuffers, running); 80 handleCodecOutput(mediaCodec, codecOutputBuffers, bufferInfo, outputStream); 81 82 if (!running) break; 83 } 84 } catch (IOException e) { 85 Log.w(TAG, e); 86 } finally { 87 88 try { 89 mediaCodec.stop(); 90 } catch (IllegalStateException ise) { 91 Log.w(TAG, "mediaCodec stop failed.", ise); 92 } 93 94 try { 95 audioRecord.stop(); 96 } catch (IllegalStateException ise) { 97 Log.w(TAG, "audioRecord stop failed.", ise); 98 } 99 100 try { 101 mediaCodec.release(); 102 } catch (IllegalStateException ise) { 103 Log.w(TAG, "mediaCodec release failed. Probably already released.", ise); 104 } 105 106 audioRecord.release(); 107 108 StreamUtil.close(outputStream); 109 setFinished(); 110 } 111 } 112 }, "signal-AudioCodec").start(); 113 } 114 115 private synchronized boolean isRunning() { 116 return running; 117 } 118 119 private synchronized void setFinished() { 120 finished = true; 121 notifyAll(); 122 } 123 124 private void handleCodecInput(AudioRecord audioRecord, byte[] audioRecordData, 125 MediaCodec mediaCodec, ByteBuffer[] codecInputBuffers, 126 boolean running) 127 { 128 int length = audioRecord.read(audioRecordData, 0, audioRecordData.length); 129 int codecInputBufferIndex = mediaCodec.dequeueInputBuffer(10 * 1000); 130 131 if (codecInputBufferIndex >= 0) { 132 ByteBuffer codecBuffer = codecInputBuffers[codecInputBufferIndex]; 133 codecBuffer.clear(); 134 codecBuffer.put(audioRecordData); 135 mediaCodec.queueInputBuffer(codecInputBufferIndex, 0, length, 0, running ? 0 : MediaCodec.BUFFER_FLAG_END_OF_STREAM); 136 } 137 } 138 139 private void handleCodecOutput(MediaCodec mediaCodec, 140 ByteBuffer[] codecOutputBuffers, 141 MediaCodec.BufferInfo bufferInfo, 142 OutputStream outputStream) 143 throws IOException 144 { 145 int codecOutputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0); 146 147 while (codecOutputBufferIndex != MediaCodec.INFO_TRY_AGAIN_LATER) { 148 if (codecOutputBufferIndex >= 0) { 149 ByteBuffer encoderOutputBuffer = codecOutputBuffers[codecOutputBufferIndex]; 150 151 encoderOutputBuffer.position(bufferInfo.offset); 152 encoderOutputBuffer.limit(bufferInfo.offset + bufferInfo.size); 153 154 if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != MediaCodec.BUFFER_FLAG_CODEC_CONFIG) { 155 byte[] header = createAdtsHeader(bufferInfo.size - bufferInfo.offset); 156 157 158 outputStream.write(header); 159 160 byte[] data = new byte[encoderOutputBuffer.remaining()]; 161 encoderOutputBuffer.get(data); 162 outputStream.write(data); 163 } 164 165 encoderOutputBuffer.clear(); 166 167 mediaCodec.releaseOutputBuffer(codecOutputBufferIndex, false); 168 } else if (codecOutputBufferIndex== MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 169 codecOutputBuffers = mediaCodec.getOutputBuffers(); 170 } 171 172 codecOutputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0); 173 } 174 175 } 176 177 private byte[] createAdtsHeader(int length) { 178 int frameLength = length + 7; 179 byte[] adtsHeader = new byte[7]; 180 181 adtsHeader[0] = (byte) 0xFF; // Sync Word 182 adtsHeader[1] = (byte) 0xF1; // MPEG-4, Layer (0), No CRC 183 adtsHeader[2] = (byte) ((MediaCodecInfo.CodecProfileLevel.AACObjectLC - 1) << 6); 184 adtsHeader[2] |= (((byte) SAMPLE_RATE_INDEX) << 2); 185 adtsHeader[2] |= (((byte) CHANNELS) >> 2); 186 adtsHeader[3] = (byte) (((CHANNELS & 3) << 6) | ((frameLength >> 11) & 0x03)); 187 adtsHeader[4] = (byte) ((frameLength >> 3) & 0xFF); 188 adtsHeader[5] = (byte) (((frameLength & 0x07) << 5) | 0x1f); 189 adtsHeader[6] = (byte) 0xFC; 190 191 return adtsHeader; 192 } 193 194 private AudioRecord createAudioRecord(int bufferSize) { 195 return new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE, 196 AudioFormat.CHANNEL_IN_MONO, 197 AudioFormat.ENCODING_PCM_16BIT, bufferSize * 10); 198 } 199 200 private MediaCodec createMediaCodec(int bufferSize) throws IOException { 201 MediaCodec mediaCodec = MediaCodec.createEncoderByType("audio/mp4a-latm"); 202 MediaFormat mediaFormat = new MediaFormat(); 203 204 mediaFormat.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm"); 205 mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, SAMPLE_RATE); 206 mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, CHANNELS); 207 mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, bufferSize); 208 mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE); 209 mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); 210 211 try { 212 mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 213 } catch (Exception e) { 214 Log.w(TAG, e); 215 mediaCodec.release(); 216 throw new IOException(e); 217 } 218 219 return mediaCodec; 220 } 221 222}