218 lines
6.7 KiB
Java
218 lines
6.7 KiB
Java
/*
|
|
* Copyright (C) 2021 pedroSG94.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.pedro.encoder.input.decoder;
|
|
|
|
import android.media.MediaCodec;
|
|
import android.media.MediaExtractor;
|
|
import android.media.MediaFormat;
|
|
import android.util.Log;
|
|
import com.pedro.encoder.Frame;
|
|
import com.pedro.encoder.input.audio.GetMicrophoneData;
|
|
import com.pedro.encoder.utils.CodecUtil;
|
|
import com.pedro.encoder.utils.PCMUtil;
|
|
import java.nio.ByteBuffer;
|
|
|
|
/**
|
|
* Created by pedro on 20/06/17.
|
|
*/
|
|
public class AudioDecoder extends BaseDecoder {
|
|
|
|
private AudioDecoderInterface audioDecoderInterface;
|
|
private GetMicrophoneData getMicrophoneData;
|
|
private int sampleRate;
|
|
private boolean isStereo;
|
|
private int channels = 1;
|
|
private int size = 2048;
|
|
private byte[] pcmBuffer = new byte[size];
|
|
private byte[] pcmBufferMuted = new byte[11];
|
|
private boolean muted = false;
|
|
|
|
public AudioDecoder(GetMicrophoneData getMicrophoneData,
|
|
AudioDecoderInterface audioDecoderInterface, LoopFileInterface loopFileInterface) {
|
|
super(loopFileInterface);
|
|
TAG = "AudioDecoder";
|
|
this.getMicrophoneData = getMicrophoneData;
|
|
this.audioDecoderInterface = audioDecoderInterface;
|
|
}
|
|
|
|
@Override
|
|
protected boolean extract(MediaExtractor audioExtractor) {
|
|
size = 2048;
|
|
running = false;
|
|
for (int i = 0; i < audioExtractor.getTrackCount() && !mime.startsWith("audio/"); i++) {
|
|
mediaFormat = audioExtractor.getTrackFormat(i);
|
|
mime = mediaFormat.getString(MediaFormat.KEY_MIME);
|
|
if (mime.startsWith("audio/")) {
|
|
audioExtractor.selectTrack(i);
|
|
} else {
|
|
mediaFormat = null;
|
|
}
|
|
}
|
|
if (mediaFormat != null) {
|
|
channels = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
|
|
isStereo = channels >= 2;
|
|
sampleRate = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
|
|
duration = mediaFormat.getLong(MediaFormat.KEY_DURATION);
|
|
fixBuffer();
|
|
return true;
|
|
//audio decoder not supported
|
|
} else {
|
|
mime = "";
|
|
mediaFormat = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void fixBuffer() {
|
|
if (channels >= 2) {
|
|
size *= channels;
|
|
}
|
|
pcmBuffer = new byte[size];
|
|
}
|
|
|
|
public boolean prepareAudio() {
|
|
return prepare(null);
|
|
}
|
|
|
|
public void reset() {
|
|
resetCodec(null);
|
|
}
|
|
|
|
@Override
|
|
protected void decode() {
|
|
ByteBuffer[] inputBuffers = codec.getInputBuffers();
|
|
ByteBuffer[] outputBuffers = codec.getOutputBuffers();
|
|
startMs = System.currentTimeMillis();
|
|
while (running) {
|
|
int inIndex = codec.dequeueInputBuffer(10000);
|
|
if (inIndex >= 0) {
|
|
ByteBuffer buffer = inputBuffers[inIndex];
|
|
int sampleSize = extractor.readSampleData(buffer, 0);
|
|
if (sampleSize < 0) {
|
|
codec.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
|
} else {
|
|
codec.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
|
|
extractor.advance();
|
|
}
|
|
|
|
int outIndex = codec.dequeueOutputBuffer(bufferInfo, 10000);
|
|
switch (outIndex) {
|
|
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
|
|
outputBuffers = codec.getOutputBuffers();
|
|
break;
|
|
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
|
|
case MediaCodec.INFO_TRY_AGAIN_LATER:
|
|
break;
|
|
default:
|
|
long extractorTs = extractor.getSampleTime() / 1000;
|
|
long currentTs = System.currentTimeMillis() - startMs + seekTime;
|
|
if (extractorTs > currentTs) {
|
|
try {
|
|
long sleepTime = extractorTs - currentTs;
|
|
Thread.sleep(sleepTime);
|
|
} catch (InterruptedException e) {
|
|
Thread.currentThread().interrupt();
|
|
return;
|
|
}
|
|
}
|
|
ByteBuffer outBuffer = outputBuffers[outIndex];
|
|
//This buffer is PCM data
|
|
if (muted) {
|
|
outBuffer.get(pcmBufferMuted, 0,
|
|
Math.min(outBuffer.remaining(), pcmBufferMuted.length));
|
|
getMicrophoneData.inputPCMData(new Frame(pcmBufferMuted, 0, pcmBufferMuted.length));
|
|
} else {
|
|
if (pcmBuffer.length < outBuffer.remaining()) {
|
|
pcmBuffer = new byte[outBuffer.remaining()];
|
|
}
|
|
outBuffer.get(pcmBuffer, 0, Math.min(outBuffer.remaining(), pcmBuffer.length));
|
|
if (channels > 2) { //downgrade to stereo
|
|
byte[] bufferStereo = PCMUtil.pcmToStereo(pcmBuffer, channels);
|
|
getMicrophoneData.inputPCMData(new Frame(bufferStereo, 0, bufferStereo.length));
|
|
} else {
|
|
getMicrophoneData.inputPCMData(new Frame(pcmBuffer, 0, pcmBuffer.length));
|
|
}
|
|
}
|
|
codec.releaseOutputBuffer(outIndex, false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// All decoded frames have been rendered, we can stop playing now
|
|
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
|
|
seekTime = 0;
|
|
Log.i(TAG, "end of file out");
|
|
running = false;
|
|
if (loopMode) {
|
|
loopFileInterface.onReset(false);
|
|
} else {
|
|
audioDecoderInterface.onAudioDecoderFinished();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method should be called after prepare.
|
|
* Get max output size to set max input size in encoder.
|
|
*/
|
|
public int getOutsize() {
|
|
if (!(mime.equals(CodecUtil.AAC_MIME) || mime.equals(CodecUtil.OPUS_MIME) || mime.equals(
|
|
CodecUtil.VORBIS_MIME))) {
|
|
Log.i(TAG, "fixing input size");
|
|
try {
|
|
if (running) {
|
|
return codec.getOutputBuffers()[0].remaining();
|
|
} else {
|
|
if (codec != null) {
|
|
codec.start();
|
|
int outSize = codec.getOutputBuffers()[0].remaining();
|
|
stopDecoder();
|
|
if (prepare(null)) return outSize;
|
|
}
|
|
return 0;
|
|
}
|
|
} catch (Exception e) {
|
|
return 0;
|
|
}
|
|
} else {
|
|
Log.i(TAG, "default input size");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public void mute() {
|
|
muted = true;
|
|
}
|
|
|
|
public void unMute() {
|
|
muted = false;
|
|
}
|
|
|
|
public boolean isMuted() {
|
|
return muted;
|
|
}
|
|
|
|
public int getSampleRate() {
|
|
return sampleRate;
|
|
}
|
|
|
|
public boolean isStereo() {
|
|
return isStereo;
|
|
}
|
|
}
|