That fuck shit the fascists are using
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}