diff --git a/library-push/src/main/java/com/yinuo/library/vlc/encoder/AudioEncoderCore.java b/library-push/src/main/java/com/yinuo/library/vlc/encoder/AudioEncoderCore.java index 889444c..d34a82c 100644 --- a/library-push/src/main/java/com/yinuo/library/vlc/encoder/AudioEncoderCore.java +++ b/library-push/src/main/java/com/yinuo/library/vlc/encoder/AudioEncoderCore.java @@ -12,9 +12,6 @@ import android.view.Surface; import com.common.commonlib.utils.LogUtils; -import org.easydarwin.PushHelper; -import org.easydarwin.easypusher.BuildConfig; - import java.io.IOException; import java.nio.ByteBuffer; @@ -34,13 +31,15 @@ public class AudioEncoderCore extends MediaEncoderCore implements Runnable { protected static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT; - private int mSampleRate = 8000; + private int mSampleRate = 44100; private int mChannelCount = 1; - private int mBitRate = 16000; + private int mBitRate = 128000; + +// private int mMaxInputSize = 16384; - private int mMaxInputSize = 1920; + int AUDIO_BUFFER_SIZE; private AudioRecord mAudioRecord; @@ -63,14 +62,14 @@ public class AudioEncoderCore extends MediaEncoderCore implements Runnable { format.setInteger(MediaFormat.KEY_SAMPLE_RATE, mSampleRate); format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, mChannelCount); format.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate); - format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, mMaxInputSize); +// format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, mMaxInputSize); // Create a MediaCodec encoder, and configure it with our format. Get a Surface // we can use for input and wrap it with a class that handles the EGL work. try { mEncoder = MediaCodec.createEncoderByType(MIME_TYPE); } catch (IOException e) { - LogUtils.e(e); + e.printStackTrace(); } mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); @@ -79,6 +78,8 @@ public class AudioEncoderCore extends MediaEncoderCore implements Runnable { LogUtils.v(String.format("%s prepareEncoder, mEncoder = %s", getClass().getSimpleName(), mEncoder)); } + int minBufferSize; + @SuppressLint("MissingPermission") private void prepareRecorder() { switch (mChannelCount) { @@ -92,24 +93,25 @@ public class AudioEncoderCore extends MediaEncoderCore implements Runnable { throw new IllegalArgumentException(); } - int minBufferSize = AudioRecord.getMinBufferSize(mSampleRate, + minBufferSize = AudioRecord.getMinBufferSize(mSampleRate, mChannelConfig, AUDIO_FORMAT); - + AUDIO_BUFFER_SIZE = 2 * minBufferSize; mAudioRecord = new AudioRecord( - MediaRecorder.AudioSource.MIC, // source + MediaRecorder.AudioSource.CAMCORDER, // source mSampleRate, // sample rate, hz mChannelConfig, // channels AUDIO_FORMAT, // audio format - minBufferSize); // buffer size (bytes) + AUDIO_BUFFER_SIZE); // buffer size (bytes) } @Override public void start() { if (!mRecording) { -// mRecording = true; -// mAudioRecord.startRecording(); -// -// TaskUtils.execute(this); + mRecording = true; + mAudioRecord.startRecording(); + + new Thread(this).start(); + } } @@ -123,6 +125,87 @@ public class AudioEncoderCore extends MediaEncoderCore implements Runnable { return null; } + @Override + public void drainEncoder(boolean endOfStream) { +// LogUtils.v(String.format("%s drainEncoder: end = %b", getClass().getSimpleName(), endOfStream)); + + final int TIMEOUT_USEC = 10000; + if (VERBOSE) Log.d(TAG, "drainEncoder(" + endOfStream + ")"); + + if (endOfStream && isSurfaceInput()) { + if (VERBOSE) Log.d(TAG, "sending EOS to encoder"); + mEncoder.signalEndOfInputStream(); + } + + ByteBuffer[] encoderOutputBuffers = mEncoder.getOutputBuffers(); + while (true) { + int encoderStatus = mEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC); + + if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { + // no output available yet + if (!endOfStream) { + break; // out of while + } else { + if (VERBOSE) Log.d(TAG, "no output available, spinning to await EOS"); + } + } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { + // not expected for an encoder + encoderOutputBuffers = mEncoder.getOutputBuffers(); + } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { + // should happen before receiving buffers, and should only happen once + MediaFormat newFormat = mEncoder.getOutputFormat(); + Log.d(TAG, "encoder output format changed: " + newFormat); + + // now that we have the Magic Goodies, start the muxer + mTrackIndex = mMuxer.addTrack(newFormat); + } else if (encoderStatus < 0) { + Log.w(TAG, "unexpected result from encoder.dequeueOutputBuffer: " + + encoderStatus); + // let's ignore it + } else { + ByteBuffer encodedData = encoderOutputBuffers[encoderStatus]; + if (encodedData == null) { + throw new RuntimeException("encoderOutputBuffer " + encoderStatus + + " was null"); + } + + if (!mMuxer.isStarted()) { + mBufferInfo.size = 0; + } + + if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { + // The codec config data was pulled out and fed to the muxer when we got + // the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it. + if (VERBOSE) Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG"); + mBufferInfo.size = 0; + } + + if (mBufferInfo.size != 0) { + // adjust the ByteBuffer values to match BufferInfo (not needed?) + encodedData.position(mBufferInfo.offset); + encodedData.limit(mBufferInfo.offset + mBufferInfo.size); + + mMuxer.writeSampleData(mTrackIndex, encodedData, mBufferInfo); + if (VERBOSE) { + Log.d(TAG, "sent " + mBufferInfo.size + " bytes to muxer, ts=" + + mBufferInfo.presentationTimeUs); + } + } + + mEncoder.releaseOutputBuffer(encoderStatus, false); + + if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { + if (!endOfStream) { + Log.w(TAG, "reached end of stream unexpectedly"); + } else { + if (VERBOSE) Log.d(TAG, "end of stream reached"); + } + break; // out of while + } + } + } + } + @Override protected boolean isSurfaceInput() { return false; @@ -131,14 +214,14 @@ public class AudioEncoderCore extends MediaEncoderCore implements Runnable { @Override public void run() { while (mRecording) { -// drainEncoder(false); -// drainAudio(false); + drainAudio(false); + drainEncoder(false); } -// drainAudio(true); + drainAudio(true); mAudioRecord.stop(); -// drainEncoder(true); + drainEncoder(true); release(); } @@ -146,104 +229,32 @@ public class AudioEncoderCore extends MediaEncoderCore implements Runnable { private void drainAudio(boolean endOfStream) { // LogUtils.v(String.format("drainAudio %b", endOfStream)); - ByteBuffer[] inputBuffers = mEncoder.getInputBuffers(); int bufferIndex = mEncoder.dequeueInputBuffer(-1); // wait indefinitely if (bufferIndex >= 0) { - ByteBuffer inputBuffer = inputBuffers[bufferIndex]; + ByteBuffer inputBuffer = mEncoder.getInputBuffer(bufferIndex); inputBuffer.clear(); - - int len = mAudioRecord.read(inputBuffer, SAMPLES_PER_FRAME * 2); // read blocking - long ptsUs = System.nanoTime() / 1000; - +// byte[] buffer = new byte[minBufferSize]; +// int len = mAudioRecord.read(buffer, 0, minBufferSize); // read blocking +// long ptsUs = System.nanoTime() / 1000; +// if (len > 0) { +// inputBuffer.put(buffer); +// if (endOfStream) { +// mEncoder.queueInputBuffer(bufferIndex, 0, buffer.length, ptsUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM); +// } else { +// mEncoder.queueInputBuffer(bufferIndex, 0, buffer.length, ptsUs, 0); +// } +// } + + int bytesRead = mAudioRecord.read(inputBuffer, AUDIO_BUFFER_SIZE); + long presentationTimeUs = System.nanoTime() / 1000; if (endOfStream) { - mEncoder.queueInputBuffer(bufferIndex, 0, len, ptsUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM); - } else { - mEncoder.queueInputBuffer(bufferIndex, 0, len, ptsUs, 0); - } - } - } - - @Override - public void drainEncoder(boolean endOfStream) { - final int TIMEOUT_USEC = 10000; - if (VERBOSE) Log.d(TAG, "drainEncoder(" + endOfStream + ")"); - - if (endOfStream && isSurfaceInput()) { - if (VERBOSE) Log.d(TAG, "sending EOS to encoder"); - mEncoder.signalEndOfInputStream(); - } - ByteBuffer mBuffer = ByteBuffer.allocate(10240); - ByteBuffer[] encoderOutputBuffers = mEncoder.getOutputBuffers(); - int encoderStatus = mEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC); - - if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { - // no output available yet - if (!endOfStream) { + mEncoder.queueInputBuffer(bufferIndex, 0, bytesRead, presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM); } else { - if (VERBOSE) Log.d(TAG, "no output available, spinning to await EOS"); - } - } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { - // not expected for an encoder - encoderOutputBuffers = mEncoder.getOutputBuffers(); - } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { - // should happen before receiving buffers, and should only happen once - MediaFormat newFormat = mEncoder.getOutputFormat(); - Log.d(TAG, "encoder output format changed: " + newFormat); - - // now that we have the Magic Goodies, start the muxer -// mTrackIndex = mMuxer.addTrack(newFormat); - } else if (encoderStatus < 0) { - Log.w(TAG, "unexpected result from encoder.dequeueOutputBuffer: " + - encoderStatus); - // let's ignore it - } else { - ByteBuffer encodedData = encoderOutputBuffers[encoderStatus]; - if (encodedData == null) { - throw new RuntimeException("encoderOutputBuffer " + encoderStatus + - " was null"); - } - - if (!mMuxer.isStarted()) { - mBufferInfo.size = 0; - } - - if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { - // The codec config data was pulled out and fed to the muxer when we got - // the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it. - if (VERBOSE) Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG"); - mBufferInfo.size = 0; + mEncoder.queueInputBuffer(bufferIndex, 0, bytesRead, + presentationTimeUs, 0); } - if (mBufferInfo.size != 0) { - encodedData.get(mBuffer.array(), 7, mBufferInfo.size); - encodedData.clear(); - mBuffer.position(7 + mBufferInfo.size); - addADTStoPacket(mBuffer.array(), mBufferInfo.size + 7); - mBuffer.flip(); - PushHelper.INSTANCE.pushData(mBuffer.array(), mBufferInfo.size + 7, mBufferInfo.presentationTimeUs / 1000); - if (BuildConfig.DEBUG) - Log.i(TAG, String.format("push audio stamp:%d", mBufferInfo.presentationTimeUs / 1000)); - } - - mEncoder.releaseOutputBuffer(encoderStatus, false); - - if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { - if (!endOfStream) { - Log.w(TAG, "reached end of stream unexpectedly"); - } else { - if (VERBOSE) Log.d(TAG, "end of stream reached"); - } - } } - } - private void addADTStoPacket(byte[] packet, int packetLen) { - packet[0] = (byte) 0xFF; - packet[1] = (byte) 0xF1; - packet[2] = (byte) (((2 - 1) << 6) + (11 << 2) + (1 >> 2)); - packet[3] = (byte) (((1 & 3) << 6) + (packetLen >> 11)); - packet[4] = (byte) ((packetLen & 0x7FF) >> 3); - packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F); - packet[6] = (byte) 0xFC; } }