Create AudioNoise

pull/530/head
Mattia Iavarone 6 years ago
parent e6562daa78
commit 11088d7864
  1. 28
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/AudioMediaEncoder.java
  2. 59
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/AudioNoise.java
  3. 1
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/MediaEncoder.java

@ -33,12 +33,6 @@ public class AudioMediaEncoder extends MediaEncoder {
private static final boolean PERFORMANCE_FILL_GAPS = true; private static final boolean PERFORMANCE_FILL_GAPS = true;
private static final int PERFORMANCE_MAX_GAPS = 8; private static final int PERFORMANCE_MAX_GAPS = 8;
private final static Random NOISE = new Random();
private static short noise() {
return (short) NOISE.nextInt(50);
}
private boolean mRequestStop = false; private boolean mRequestStop = false;
private AudioEncodingThread mEncoder; private AudioEncodingThread mEncoder;
private AudioRecordingThread mRecorder; private AudioRecordingThread mRecorder;
@ -47,7 +41,7 @@ public class AudioMediaEncoder extends MediaEncoder {
private AudioConfig mConfig; private AudioConfig mConfig;
private InputBufferPool mInputBufferPool = new InputBufferPool(); private InputBufferPool mInputBufferPool = new InputBufferPool();
private final LinkedBlockingQueue<InputBuffer> mInputBufferQueue = new LinkedBlockingQueue<>(); private final LinkedBlockingQueue<InputBuffer> mInputBufferQueue = new LinkedBlockingQueue<>();
private ByteBuffer mNoiseBuffer; private AudioNoise mAudioNoise;
// Just to debug performance. // Just to debug performance.
private int mDebugSendCount = 0; private int mDebugSendCount = 0;
@ -84,6 +78,7 @@ public class AudioMediaEncoder extends MediaEncoder {
mMediaCodec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); mMediaCodec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mMediaCodec.start(); mMediaCodec.start();
mByteBufferPool = new ByteBufferPool(mConfig.frameSize(), mConfig.bufferPoolMaxSize()); mByteBufferPool = new ByteBufferPool(mConfig.frameSize(), mConfig.bufferPoolMaxSize());
mAudioNoise = new AudioNoise(mConfig);
} }
@EncoderThread @EncoderThread
@ -304,31 +299,14 @@ public class AudioMediaEncoder extends MediaEncoder {
long gapStart = mTimestamp.getGapStartUs(mLastTimeUs); long gapStart = mTimestamp.getGapStartUs(mLastTimeUs);
long frameUs = AudioTimestamp.bytesToUs(mConfig.frameSize(), mConfig.byteRate()); long frameUs = AudioTimestamp.bytesToUs(mConfig.frameSize(), mConfig.byteRate());
LOG.w("read thread - GAPS: trying to add", gaps, "noise buffers. PERFORMANCE_MAX_GAPS:", PERFORMANCE_MAX_GAPS); LOG.w("read thread - GAPS: trying to add", gaps, "noise buffers. PERFORMANCE_MAX_GAPS:", PERFORMANCE_MAX_GAPS);
// Prepare the noise buffer.
if (mNoiseBuffer == null) {
LOG.w("read thread - GAPS: creating noise buffer.");
mNoiseBuffer = ByteBuffer.allocateDirect(mConfig.frameSize()).order(ByteOrder.nativeOrder());
while (mNoiseBuffer.hasRemaining()) {
// Assume remaining() is not an odd number!
// Also assuming byte order is little endian in Android.
short noise = noise();
mNoiseBuffer.put((byte) noise);
mNoiseBuffer.put((byte) (noise >> 8));
}
}
// Fill all gaps.
// Well, at most PERFORMANCE_MAX_GAPS.
for (int i = 0; i < Math.min(gaps, PERFORMANCE_MAX_GAPS); i++) { for (int i = 0; i < Math.min(gaps, PERFORMANCE_MAX_GAPS); i++) {
ByteBuffer noiseBuffer = mByteBufferPool.get(); ByteBuffer noiseBuffer = mByteBufferPool.get();
if (noiseBuffer == null) { if (noiseBuffer == null) {
LOG.e("read thread - GAPS: aborting because we have no free buffer."); LOG.e("read thread - GAPS: aborting because we have no free buffer.");
break; break;
} }
mNoiseBuffer.clear();
noiseBuffer.clear(); noiseBuffer.clear();
noiseBuffer.put(mNoiseBuffer); mAudioNoise.fill(noiseBuffer);
noiseBuffer.rewind(); noiseBuffer.rewind();
enqueue(noiseBuffer, gapStart, false); enqueue(noiseBuffer, gapStart, false);
gapStart += frameUs; gapStart += frameUs;

@ -0,0 +1,59 @@
package com.otaliastudios.cameraview.video.encoding;
import androidx.annotation.NonNull;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.Random;
/**
* An AudioNoise instance offers buffers of noise that we can use when recording
* some samples failed for some reason.
*
* Since we can't create noise anytime it's needed - that would be expensive and
* slow down the recording thread - we create a big noise buffer at start time.
*
* We'd like to work with {@link ShortBuffer}s, but this requires converting the
* input buffer to ShortBuffer each time, and this can be expensive.
*/
class AudioNoise {
private final static int FRAMES = 1; // After testing, it looks like this is the best setup
private final static Random RANDOM = new Random();
private final ByteBuffer mNoiseBuffer;
AudioNoise(@NonNull AudioConfig config) {
//noinspection ConstantConditions
if (config.sampleSizePerChannel != 2) {
throw new IllegalArgumentException("AudioNoise expects 2bytes-1short samples.");
}
mNoiseBuffer = ByteBuffer
.allocateDirect(config.frameSize() * FRAMES)
.order(ByteOrder.nativeOrder());
double i = 0;
double frequency = config.frameSize() / 2D; // each X samples, the signal repeats
double step = Math.PI / frequency; // the increase in radians
double max = 10; // might choose this from 0 to Short.MAX_VALUE
while (mNoiseBuffer.hasRemaining()) {
short noise = (short) (Math.sin(++i * step) * max);
mNoiseBuffer.put((byte) noise);
mNoiseBuffer.put((byte) (noise >> 8));
}
mNoiseBuffer.rewind();
}
void fill(@NonNull ByteBuffer outBuffer) {
mNoiseBuffer.clear();
if (mNoiseBuffer.capacity() == outBuffer.remaining()) {
mNoiseBuffer.position(0); // Happens if FRAMES = 1.
} else {
mNoiseBuffer.position(RANDOM.nextInt(mNoiseBuffer.capacity()
- outBuffer.remaining()));
}
mNoiseBuffer.limit(mNoiseBuffer.position() + outBuffer.remaining());
outBuffer.put(mNoiseBuffer);
}
}

@ -325,6 +325,7 @@ public abstract class MediaEncoder {
mOutputBufferPool = null; mOutputBufferPool = null;
mBuffers = null; mBuffers = null;
setState(STATE_STOPPED); setState(STATE_STOPPED);
mWorker.destroy();
} }
/** /**

Loading…
Cancel
Save