parent
1a73f8e027
commit
c668a359a5
@ -1,340 +0,0 @@ |
||||
package com.frank.ffmpeg.mp3; |
||||
|
||||
import android.annotation.TargetApi; |
||||
import android.media.MediaCodec; |
||||
import android.media.MediaCodec.BufferInfo; |
||||
import android.media.MediaExtractor; |
||||
import android.media.MediaFormat; |
||||
import android.os.Build; |
||||
import android.util.Log; |
||||
|
||||
import java.io.BufferedOutputStream; |
||||
import java.io.File; |
||||
import java.io.FileOutputStream; |
||||
import java.io.IOException; |
||||
import java.nio.ByteBuffer; |
||||
import java.nio.ByteOrder; |
||||
import java.nio.ShortBuffer; |
||||
import java.util.concurrent.BlockingDeque; |
||||
import java.util.concurrent.LinkedBlockingDeque; |
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN) |
||||
public class Mp3Converter { |
||||
|
||||
private final static String TAG = Mp3Converter.class.getSimpleName(); |
||||
|
||||
private MediaCodec mMediaCodec; |
||||
private MediaExtractor mediaExtractor; |
||||
private BufferInfo bufferInfo; |
||||
private ByteBuffer[] rawInputBuffers; |
||||
private ByteBuffer[] encodedOutputBuffers; |
||||
private int inSampleRate; |
||||
private int channels; |
||||
|
||||
private final static int DEFAULT_QUEUE_SIZE = 512; |
||||
private BlockingDeque<BufferEncoded> writeQueue = new LinkedBlockingDeque<>(DEFAULT_QUEUE_SIZE); |
||||
private BlockingDeque<BufferDecoded> encodeQueue = new LinkedBlockingDeque<>(DEFAULT_QUEUE_SIZE); |
||||
private byte[] mp3buf; |
||||
|
||||
private boolean decodeFinished; |
||||
private boolean encodeFinished; |
||||
|
||||
private long readSize; |
||||
private long decodeSize; |
||||
private long encodeSize; |
||||
|
||||
private Mp3Lame mp3Lame; |
||||
private WriteThread writeThread; |
||||
private EncodeThread encodeThread; |
||||
private long lastPts; |
||||
|
||||
private class BufferDecoded { |
||||
int channels; |
||||
short[] leftBuffer; |
||||
short[] rightBuffer; |
||||
short[] pcm; |
||||
long pts; |
||||
} |
||||
|
||||
private class BufferEncoded { |
||||
byte[] buffer; |
||||
int length; |
||||
} |
||||
|
||||
private class WriteThread extends Thread { |
||||
|
||||
private String mp3Path; |
||||
private FileOutputStream fos; |
||||
private BufferedOutputStream bos; |
||||
|
||||
WriteThread(String path) { |
||||
super(); |
||||
mp3Path = path; |
||||
} |
||||
|
||||
@Override |
||||
public void run() { |
||||
try { |
||||
Log.i(TAG, "WriteThread start"); |
||||
|
||||
fos = new FileOutputStream(new File(mp3Path)); |
||||
bos = new BufferedOutputStream(fos, 200 * 1024); |
||||
|
||||
while (true) { |
||||
if (encodeFinished && writeQueue.size() == 0) { |
||||
break; |
||||
} |
||||
|
||||
BufferEncoded buffer = null; |
||||
try { |
||||
buffer = writeQueue.take(); |
||||
} catch (InterruptedException e) { |
||||
Log.e(TAG, "WriteThread InterruptedException=" + e.toString()); |
||||
} |
||||
|
||||
if(buffer != null) { |
||||
bos.write(buffer.buffer, 0, buffer.length); |
||||
} |
||||
} |
||||
bos.flush(); |
||||
bos.close(); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} finally { |
||||
try { |
||||
bos.flush(); |
||||
bos.close(); |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
Log.i(TAG, "WriteThread end"); |
||||
} |
||||
} |
||||
|
||||
private class EncodeThread extends Thread { |
||||
@Override |
||||
public void run() { |
||||
Log.i(TAG, "EncodeThread start"); |
||||
|
||||
while (true) { |
||||
if (decodeFinished && encodeQueue.size() == 0) { |
||||
break; |
||||
} |
||||
BufferDecoded buffer = null; |
||||
try { |
||||
buffer = encodeQueue.take(); |
||||
} catch (InterruptedException e) { |
||||
Log.e(TAG, "EncodeThread InterruptedException=" + e.toString()); |
||||
} |
||||
if (buffer != null) { |
||||
encodeToMp3(buffer); |
||||
} |
||||
} |
||||
encodeFinished = true; |
||||
|
||||
writeThread.interrupt(); |
||||
|
||||
Log.i(TAG, "EncodeThread end"); |
||||
} |
||||
} |
||||
|
||||
private class DecodeThread extends Thread { |
||||
@Override |
||||
public void run() { |
||||
long startTime = System.currentTimeMillis(); |
||||
try { |
||||
Log.i(TAG, "DecodeThread start"); |
||||
|
||||
while (true) { |
||||
int outputBufIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, -1); |
||||
if (outputBufIndex >= 0) { |
||||
ByteBuffer buffer = encodedOutputBuffers[outputBufIndex]; |
||||
decodeSize += bufferInfo.size; |
||||
ShortBuffer shortBuffer = buffer.order(ByteOrder.nativeOrder()).asShortBuffer(); |
||||
|
||||
short[] leftBuffer = null; |
||||
short[] rightBuffer = null; |
||||
short[] pcm = null; |
||||
|
||||
if (channels == 2) { |
||||
pcm = new short[shortBuffer.remaining()]; |
||||
shortBuffer.get(pcm); |
||||
} else { |
||||
leftBuffer = new short[shortBuffer.remaining()]; |
||||
rightBuffer = leftBuffer; |
||||
shortBuffer.get(leftBuffer); |
||||
Log.e(TAG, "single channel leftBuffer.length = " + leftBuffer.length); |
||||
} |
||||
|
||||
buffer.clear(); |
||||
|
||||
BufferDecoded bufferDecoded = new BufferDecoded(); |
||||
bufferDecoded.leftBuffer = leftBuffer; |
||||
bufferDecoded.rightBuffer = rightBuffer; |
||||
bufferDecoded.pcm = pcm; |
||||
bufferDecoded.channels = channels; |
||||
bufferDecoded.pts = bufferInfo.presentationTimeUs; |
||||
encodeQueue.put(bufferDecoded); |
||||
|
||||
mMediaCodec.releaseOutputBuffer(outputBufIndex, false); |
||||
|
||||
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { |
||||
Log.e(TAG, "DecodeThread get BUFFER_FLAG_END_OF_STREAM"); |
||||
decodeFinished = true; |
||||
break; |
||||
} |
||||
} else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { |
||||
encodedOutputBuffers = mMediaCodec.getOutputBuffers(); |
||||
} else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { |
||||
final MediaFormat oformat = mMediaCodec.getOutputFormat(); |
||||
Log.d(TAG, "Output format has changed to " + oformat); |
||||
} |
||||
} |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
|
||||
encodeThread.interrupt(); |
||||
long endTime = System.currentTimeMillis(); |
||||
Log.i(TAG, "DecodeThread finished time=" + (endTime - startTime) / 1000); |
||||
} |
||||
} |
||||
|
||||
public void convertToMp3(String srcPath, String mp3Path) { |
||||
|
||||
long startTime = System.currentTimeMillis(); |
||||
long endTime; |
||||
|
||||
encodeThread = new EncodeThread(); |
||||
writeThread = new WriteThread(mp3Path); |
||||
DecodeThread decodeThread = new DecodeThread(); |
||||
|
||||
encodeThread.start(); |
||||
writeThread.start(); |
||||
prepareDecode(srcPath); |
||||
decodeThread.start(); |
||||
readSampleData(); |
||||
|
||||
try { |
||||
writeThread.join(); |
||||
} catch (InterruptedException e) { |
||||
Log.e(TAG, "convertToMp3 InterruptedException=" + e.toString()); |
||||
} |
||||
|
||||
double mReadSize = readSize / 1024.0 / 1024.0; |
||||
double mDecodeSize = decodeSize / 1024.0 / 1024.0; |
||||
double mEncodeSize = encodeSize / 1024.0 / 1024.0; |
||||
Log.i(TAG, "readSize=" + mReadSize + ", decodeSize=" + mDecodeSize + ",encodeSize=" + mEncodeSize); |
||||
|
||||
endTime = System.currentTimeMillis(); |
||||
Log.i(TAG, "convertToMp3 finish time=" + (endTime - startTime) / 1000); |
||||
} |
||||
|
||||
private void prepareDecode(String path) { |
||||
try { |
||||
mediaExtractor = new MediaExtractor(); |
||||
mediaExtractor.setDataSource(path); |
||||
for (int i = 0; i < mediaExtractor.getTrackCount(); i++) { |
||||
MediaFormat mMediaFormat = mediaExtractor.getTrackFormat(i); |
||||
Log.i(TAG, "prepareDecode get mMediaFormat=" + mMediaFormat); |
||||
|
||||
String mime = mMediaFormat.getString(MediaFormat.KEY_MIME); |
||||
if (mime.startsWith("audio")) { |
||||
mMediaCodec = MediaCodec.createDecoderByType(mime); |
||||
mMediaCodec.configure(mMediaFormat, null, null, 0); |
||||
mediaExtractor.selectTrack(i); |
||||
inSampleRate = mMediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE); |
||||
channels = mMediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT); |
||||
break; |
||||
} |
||||
} |
||||
mMediaCodec.start(); |
||||
|
||||
bufferInfo = new BufferInfo(); |
||||
rawInputBuffers = mMediaCodec.getInputBuffers(); |
||||
encodedOutputBuffers = mMediaCodec.getOutputBuffers(); |
||||
Log.i(TAG, "--channel=" + channels + "--sampleRate=" + inSampleRate); |
||||
|
||||
mp3Lame = new Mp3LameBuilder() |
||||
.setInSampleRate(inSampleRate) |
||||
.setOutChannels(channels) |
||||
.setOutBitrate(128) |
||||
.setOutSampleRate(inSampleRate) |
||||
.setQuality(9) |
||||
.setVbrMode(Mp3LameBuilder.VbrMode.VBR_MTRH) |
||||
.build(); |
||||
|
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
private void readSampleData() { |
||||
boolean rawInputEOS = false; |
||||
|
||||
while (!rawInputEOS) { |
||||
for (int i = 0; i < rawInputBuffers.length; i++) { |
||||
int inputBufIndex = mMediaCodec.dequeueInputBuffer(-1); |
||||
if (inputBufIndex >= 0) { |
||||
ByteBuffer buffer = rawInputBuffers[inputBufIndex]; |
||||
int sampleSize = mediaExtractor.readSampleData(buffer, 0); |
||||
long presentationTimeUs = 0; |
||||
if (sampleSize < 0) { |
||||
rawInputEOS = true; |
||||
sampleSize = 0; |
||||
} else { |
||||
readSize += sampleSize; |
||||
presentationTimeUs = mediaExtractor.getSampleTime(); |
||||
} |
||||
mMediaCodec.queueInputBuffer(inputBufIndex, 0, |
||||
sampleSize, presentationTimeUs, |
||||
rawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); |
||||
if (!rawInputEOS) { |
||||
mediaExtractor.advance(); |
||||
} else { |
||||
break; |
||||
} |
||||
} else { |
||||
Log.e(TAG, "wrong inputBufIndex=" + inputBufIndex); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void encodeToMp3(BufferDecoded buffer) { |
||||
|
||||
if (buffer == null || buffer.pts == lastPts) { |
||||
return; |
||||
} |
||||
lastPts = buffer.pts; |
||||
|
||||
int bufferLength = buffer.pcm.length / 2; |
||||
if (mp3buf == null) { |
||||
mp3buf = new byte[(int) (bufferLength * 1.25 + 7200)]; |
||||
} |
||||
if (bufferLength > 0) { |
||||
int bytesEncoded; |
||||
if (channels == 2) { |
||||
bytesEncoded = mp3Lame.encodeBufferInterLeaved(buffer.pcm, bufferLength, mp3buf); |
||||
}else { |
||||
bytesEncoded = mp3Lame.encode(buffer.leftBuffer, buffer.leftBuffer, bufferLength, mp3buf); |
||||
} |
||||
Log.d(TAG, "mp3Lame encodeSize=" + bytesEncoded); |
||||
|
||||
if (bytesEncoded > 0) { |
||||
BufferEncoded be = new BufferEncoded(); |
||||
be.buffer = mp3buf; |
||||
be.length = bytesEncoded; |
||||
try { |
||||
writeQueue.put(be); |
||||
} catch (InterruptedException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
encodeSize += bytesEncoded; |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,335 @@ |
||||
package com.frank.ffmpeg.mp3 |
||||
|
||||
import android.annotation.TargetApi |
||||
import android.media.MediaCodec |
||||
import android.media.MediaCodec.BufferInfo |
||||
import android.media.MediaExtractor |
||||
import android.media.MediaFormat |
||||
import android.os.Build |
||||
import android.util.Log |
||||
|
||||
import java.io.BufferedOutputStream |
||||
import java.io.File |
||||
import java.io.FileOutputStream |
||||
import java.io.IOException |
||||
import java.nio.ByteBuffer |
||||
import java.nio.ByteOrder |
||||
import java.util.concurrent.LinkedBlockingDeque |
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN) |
||||
class Mp3Converter { |
||||
|
||||
private var mMediaCodec: MediaCodec? = null |
||||
private var mediaExtractor: MediaExtractor? = null |
||||
private var bufferInfo: BufferInfo? = null |
||||
private var rawInputBuffers: Array<ByteBuffer>? = null |
||||
private var encodedOutputBuffers: Array<ByteBuffer>? = null |
||||
private var inSampleRate: Int = 0 |
||||
private var channels: Int = 0 |
||||
private val writeQueue = LinkedBlockingDeque<BufferEncoded>(DEFAULT_QUEUE_SIZE) |
||||
private val encodeQueue = LinkedBlockingDeque<BufferDecoded>(DEFAULT_QUEUE_SIZE) |
||||
private var mp3buf: ByteArray? = null |
||||
|
||||
private var decodeFinished: Boolean = false |
||||
private var encodeFinished: Boolean = false |
||||
|
||||
private var readSize: Long = 0 |
||||
private var decodeSize: Long = 0 |
||||
private var encodeSize: Long = 0 |
||||
|
||||
private var mp3Lame: Mp3Lame? = null |
||||
private var writeThread: WriteThread? = null |
||||
private var encodeThread: EncodeThread? = null |
||||
private var lastPts: Long = 0 |
||||
|
||||
private inner class BufferDecoded { |
||||
internal var channels: Int = 0 |
||||
internal var leftBuffer: ShortArray? = null |
||||
internal var rightBuffer: ShortArray? = null |
||||
internal var pcm: ShortArray? = null |
||||
internal var pts: Long = 0 |
||||
} |
||||
|
||||
private inner class BufferEncoded { |
||||
internal var buffer: ByteArray? = null |
||||
internal var length: Int = 0 |
||||
} |
||||
|
||||
private inner class WriteThread internal constructor(private val mp3Path: String) : Thread() { |
||||
private var fos: FileOutputStream? = null |
||||
private var bos: BufferedOutputStream? = null |
||||
|
||||
override fun run() { |
||||
try { |
||||
Log.i(TAG, "WriteThread start") |
||||
|
||||
fos = FileOutputStream(File(mp3Path)) |
||||
bos = BufferedOutputStream(fos, 200 * 1024) |
||||
|
||||
while (true) { |
||||
if (encodeFinished && writeQueue.size == 0) { |
||||
break |
||||
} |
||||
|
||||
var buffer: BufferEncoded? = null |
||||
try { |
||||
buffer = writeQueue.take() |
||||
} catch (e: InterruptedException) { |
||||
Log.e(TAG, "WriteThread InterruptedException=$e") |
||||
} |
||||
|
||||
if (buffer != null) { |
||||
bos!!.write(buffer.buffer, 0, buffer.length) |
||||
} |
||||
} |
||||
bos!!.flush() |
||||
bos!!.close() |
||||
} catch (e: Exception) { |
||||
e.printStackTrace() |
||||
} finally { |
||||
try { |
||||
bos!!.flush() |
||||
bos!!.close() |
||||
} catch (e: IOException) { |
||||
e.printStackTrace() |
||||
} |
||||
|
||||
} |
||||
|
||||
Log.i(TAG, "WriteThread end") |
||||
} |
||||
} |
||||
|
||||
private inner class EncodeThread : Thread() { |
||||
override fun run() { |
||||
Log.i(TAG, "EncodeThread start") |
||||
|
||||
while (true) { |
||||
if (decodeFinished && encodeQueue.size == 0) { |
||||
break |
||||
} |
||||
var buffer: BufferDecoded? = null |
||||
try { |
||||
buffer = encodeQueue.take() |
||||
} catch (e: InterruptedException) { |
||||
Log.e(TAG, "EncodeThread InterruptedException=$e") |
||||
} |
||||
|
||||
if (buffer != null) { |
||||
encodeToMp3(buffer) |
||||
} |
||||
} |
||||
encodeFinished = true |
||||
|
||||
writeThread!!.interrupt() |
||||
|
||||
Log.i(TAG, "EncodeThread end") |
||||
} |
||||
} |
||||
|
||||
private inner class DecodeThread : Thread() { |
||||
override fun run() { |
||||
val startTime = System.currentTimeMillis() |
||||
try { |
||||
Log.i(TAG, "DecodeThread start") |
||||
|
||||
while (true) { |
||||
val outputBufIndex = mMediaCodec!!.dequeueOutputBuffer(bufferInfo!!, -1) |
||||
if (outputBufIndex >= 0) { |
||||
val buffer = encodedOutputBuffers!![outputBufIndex] |
||||
decodeSize += bufferInfo!!.size.toLong() |
||||
val shortBuffer = buffer.order(ByteOrder.nativeOrder()).asShortBuffer() |
||||
|
||||
var leftBuffer: ShortArray? = null |
||||
var rightBuffer: ShortArray? = null |
||||
var pcm: ShortArray? = null |
||||
|
||||
if (channels == 2) { |
||||
pcm = ShortArray(shortBuffer.remaining()) |
||||
shortBuffer.get(pcm) |
||||
} else { |
||||
leftBuffer = ShortArray(shortBuffer.remaining()) |
||||
rightBuffer = leftBuffer |
||||
shortBuffer.get(leftBuffer) |
||||
Log.e(TAG, "single channel leftBuffer.length = " + leftBuffer.size) |
||||
} |
||||
|
||||
buffer.clear() |
||||
|
||||
val bufferDecoded = BufferDecoded() |
||||
bufferDecoded.leftBuffer = leftBuffer |
||||
bufferDecoded.rightBuffer = rightBuffer |
||||
bufferDecoded.pcm = pcm |
||||
bufferDecoded.channels = channels |
||||
bufferDecoded.pts = bufferInfo!!.presentationTimeUs |
||||
encodeQueue.put(bufferDecoded) |
||||
|
||||
mMediaCodec!!.releaseOutputBuffer(outputBufIndex, false) |
||||
|
||||
if (bufferInfo!!.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM != 0) { |
||||
Log.e(TAG, "DecodeThread get BUFFER_FLAG_END_OF_STREAM") |
||||
decodeFinished = true |
||||
break |
||||
} |
||||
} else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { |
||||
encodedOutputBuffers = mMediaCodec!!.outputBuffers |
||||
} else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { |
||||
val oformat = mMediaCodec!!.outputFormat |
||||
Log.d(TAG, "Output format has changed to $oformat") |
||||
} |
||||
} |
||||
} catch (e: Exception) { |
||||
e.printStackTrace() |
||||
} |
||||
|
||||
encodeThread!!.interrupt() |
||||
val endTime = System.currentTimeMillis() |
||||
Log.i(TAG, "DecodeThread finished time=" + (endTime - startTime) / 1000) |
||||
} |
||||
} |
||||
|
||||
fun convertToMp3(srcPath: String, mp3Path: String) { |
||||
|
||||
val startTime = System.currentTimeMillis() |
||||
val endTime: Long |
||||
|
||||
encodeThread = EncodeThread() |
||||
writeThread = WriteThread(mp3Path) |
||||
val decodeThread = DecodeThread() |
||||
|
||||
encodeThread!!.start() |
||||
writeThread!!.start() |
||||
prepareDecode(srcPath) |
||||
decodeThread.start() |
||||
readSampleData() |
||||
|
||||
try { |
||||
writeThread!!.join() |
||||
} catch (e: InterruptedException) { |
||||
Log.e(TAG, "convertToMp3 InterruptedException=$e") |
||||
} |
||||
|
||||
val mReadSize = readSize.toDouble() / 1024.0 / 1024.0 |
||||
val mDecodeSize = decodeSize.toDouble() / 1024.0 / 1024.0 |
||||
val mEncodeSize = encodeSize.toDouble() / 1024.0 / 1024.0 |
||||
Log.i(TAG, "readSize=$mReadSize, decodeSize=$mDecodeSize,encodeSize=$mEncodeSize") |
||||
|
||||
endTime = System.currentTimeMillis() |
||||
Log.i(TAG, "convertToMp3 finish time=" + (endTime - startTime) / 1000) |
||||
} |
||||
|
||||
private fun prepareDecode(path: String) { |
||||
try { |
||||
mediaExtractor = MediaExtractor() |
||||
mediaExtractor!!.setDataSource(path) |
||||
for (i in 0 until mediaExtractor!!.trackCount) { |
||||
val mMediaFormat = mediaExtractor!!.getTrackFormat(i) |
||||
Log.i(TAG, "prepareDecode get mMediaFormat=$mMediaFormat") |
||||
|
||||
val mime = mMediaFormat.getString(MediaFormat.KEY_MIME) |
||||
if (mime.startsWith("audio")) { |
||||
mMediaCodec = MediaCodec.createDecoderByType(mime) |
||||
mMediaCodec!!.configure(mMediaFormat, null, null, 0) |
||||
mediaExtractor!!.selectTrack(i) |
||||
inSampleRate = mMediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE) |
||||
channels = mMediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) |
||||
break |
||||
} |
||||
} |
||||
mMediaCodec!!.start() |
||||
|
||||
bufferInfo = BufferInfo() |
||||
rawInputBuffers = mMediaCodec!!.inputBuffers |
||||
encodedOutputBuffers = mMediaCodec!!.outputBuffers |
||||
Log.i(TAG, "--channel=$channels--sampleRate=$inSampleRate") |
||||
|
||||
mp3Lame = Mp3LameBuilder() |
||||
.setInSampleRate(inSampleRate) |
||||
.setOutChannels(channels) |
||||
.setOutBitrate(128) |
||||
.setOutSampleRate(inSampleRate) |
||||
.setQuality(9) |
||||
.setVbrMode(Mp3LameBuilder.VbrMode.VBR_MTRH) |
||||
.build() |
||||
|
||||
} catch (e: IOException) { |
||||
e.printStackTrace() |
||||
} |
||||
|
||||
} |
||||
|
||||
private fun readSampleData() { |
||||
var rawInputEOS = false |
||||
|
||||
while (!rawInputEOS) { |
||||
for (i in rawInputBuffers!!.indices) { |
||||
val inputBufIndex = mMediaCodec!!.dequeueInputBuffer(-1) |
||||
if (inputBufIndex >= 0) { |
||||
val buffer = rawInputBuffers!![inputBufIndex] |
||||
var sampleSize = mediaExtractor!!.readSampleData(buffer, 0) |
||||
var presentationTimeUs: Long = 0 |
||||
if (sampleSize < 0) { |
||||
rawInputEOS = true |
||||
sampleSize = 0 |
||||
} else { |
||||
readSize += sampleSize.toLong() |
||||
presentationTimeUs = mediaExtractor!!.sampleTime |
||||
} |
||||
mMediaCodec!!.queueInputBuffer(inputBufIndex, 0, |
||||
sampleSize, presentationTimeUs, |
||||
if (rawInputEOS) MediaCodec.BUFFER_FLAG_END_OF_STREAM else 0) |
||||
if (!rawInputEOS) { |
||||
mediaExtractor!!.advance() |
||||
} else { |
||||
break |
||||
} |
||||
} else { |
||||
Log.e(TAG, "wrong inputBufIndex=$inputBufIndex") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private fun encodeToMp3(buffer: BufferDecoded?) { |
||||
|
||||
if (buffer == null || buffer.pts == lastPts) { |
||||
return |
||||
} |
||||
lastPts = buffer.pts |
||||
|
||||
val bufferLength = buffer.pcm!!.size / 2 |
||||
if (mp3buf == null) { |
||||
mp3buf = ByteArray((bufferLength * 1.25 + 7200).toInt()) |
||||
} |
||||
if (bufferLength > 0) { |
||||
val bytesEncoded: Int |
||||
if (channels == 2) { |
||||
bytesEncoded = mp3Lame!!.encodeBufferInterLeaved(buffer.pcm!!, bufferLength, mp3buf!!) |
||||
} else { |
||||
bytesEncoded = mp3Lame!!.encode(buffer.leftBuffer!!, buffer.leftBuffer!!, bufferLength, mp3buf!!) |
||||
} |
||||
Log.d(TAG, "mp3Lame encodeSize=$bytesEncoded") |
||||
|
||||
if (bytesEncoded > 0) { |
||||
val be = BufferEncoded() |
||||
be.buffer = mp3buf |
||||
be.length = bytesEncoded |
||||
try { |
||||
writeQueue.put(be) |
||||
} catch (e: InterruptedException) { |
||||
e.printStackTrace() |
||||
} |
||||
|
||||
encodeSize += bytesEncoded.toLong() |
||||
} |
||||
} |
||||
} |
||||
|
||||
companion object { |
||||
|
||||
private val TAG = Mp3Converter::class.java.simpleName |
||||
|
||||
private const val DEFAULT_QUEUE_SIZE = 512 |
||||
} |
||||
|
||||
} |
@ -1,71 +0,0 @@ |
||||
package com.frank.ffmpeg.mp3; |
||||
|
||||
import com.frank.ffmpeg.AudioPlayer; |
||||
|
||||
public class Mp3Lame { |
||||
|
||||
public Mp3Lame() { |
||||
AudioPlayer.lameInitDefault(); |
||||
} |
||||
|
||||
Mp3Lame(Mp3LameBuilder builder) { |
||||
initialize(builder); |
||||
} |
||||
|
||||
private void initialize(Mp3LameBuilder builder) { |
||||
AudioPlayer.lameInit(builder.inSampleRate, builder.outChannel, builder.outSampleRate, |
||||
builder.outBitrate, builder.scaleInput, getIntForMode(builder.mode), getIntForVbrMode(builder.vbrMode), builder.quality, builder.vbrQuality, builder.abrMeanBitrate, |
||||
builder.lowPassFreq, builder.highPassFreq, builder.id3tagTitle, builder.id3tagArtist, |
||||
builder.id3tagAlbum, builder.id3tagYear, builder.id3tagComment); |
||||
} |
||||
|
||||
public int encode(short[] buffer_l, short[] buffer_r, |
||||
int samples, byte[] mp3buf) { |
||||
|
||||
return AudioPlayer.lameEncode(buffer_l, buffer_r, samples, mp3buf); |
||||
} |
||||
|
||||
int encodeBufferInterLeaved(short[] pcm, int samples, |
||||
byte[] mp3buf) { |
||||
return AudioPlayer.encodeBufferInterleaved(pcm, samples, mp3buf); |
||||
} |
||||
|
||||
public int flush(byte[] mp3buf) { |
||||
return AudioPlayer.lameFlush(mp3buf); |
||||
} |
||||
|
||||
public void close() { |
||||
AudioPlayer.lameClose(); |
||||
} |
||||
|
||||
private static int getIntForMode(Mp3LameBuilder.Mode mode) { |
||||
switch (mode) { |
||||
case STEREO: |
||||
return 0; |
||||
case JSTEREO: |
||||
return 1; |
||||
case MONO: |
||||
return 3; |
||||
case DEFAULT: |
||||
return 4; |
||||
} |
||||
return -1; |
||||
} |
||||
|
||||
private static int getIntForVbrMode(Mp3LameBuilder.VbrMode mode) { |
||||
switch (mode) { |
||||
case VBR_OFF: |
||||
return 0; |
||||
case VBR_RH: |
||||
return 2; |
||||
case VBR_ABR: |
||||
return 3; |
||||
case VBR_MTRH: |
||||
return 4; |
||||
case VBR_DEFAUT: |
||||
return 6; |
||||
} |
||||
return -1; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,60 @@ |
||||
package com.frank.ffmpeg.mp3 |
||||
|
||||
import com.frank.ffmpeg.AudioPlayer |
||||
|
||||
class Mp3Lame { |
||||
|
||||
constructor() { |
||||
AudioPlayer.lameInitDefault() |
||||
} |
||||
|
||||
internal constructor(builder: Mp3LameBuilder) { |
||||
initialize(builder) |
||||
} |
||||
|
||||
private fun initialize(builder: Mp3LameBuilder) { |
||||
AudioPlayer.lameInit(builder.inSampleRate, builder.outChannel, builder.outSampleRate, |
||||
builder.outBitrate, builder.scaleInput, getIntForMode(builder.mode), getIntForVbrMode(builder.vbrMode), builder.quality, builder.vbrQuality, builder.abrMeanBitrate, |
||||
builder.lowPassFreq, builder.highPassFreq, builder.id3tagTitle, builder.id3tagArtist, |
||||
builder.id3tagAlbum, builder.id3tagYear, builder.id3tagComment) |
||||
} |
||||
|
||||
fun encode(buffer_l: ShortArray, buffer_r: ShortArray, |
||||
samples: Int, mp3buf: ByteArray): Int { |
||||
|
||||
return AudioPlayer.lameEncode(buffer_l, buffer_r, samples, mp3buf) |
||||
} |
||||
|
||||
internal fun encodeBufferInterLeaved(pcm: ShortArray, samples: Int, |
||||
mp3buf: ByteArray): Int { |
||||
return AudioPlayer.encodeBufferInterleaved(pcm, samples, mp3buf) |
||||
} |
||||
|
||||
fun flush(mp3buf: ByteArray): Int { |
||||
return AudioPlayer.lameFlush(mp3buf) |
||||
} |
||||
|
||||
fun close() { |
||||
AudioPlayer.lameClose() |
||||
} |
||||
|
||||
private fun getIntForMode(mode: Mp3LameBuilder.Mode): Int { |
||||
return when (mode) { |
||||
Mp3LameBuilder.Mode.STEREO -> 0 |
||||
Mp3LameBuilder.Mode.JSTEREO -> 1 |
||||
Mp3LameBuilder.Mode.MONO -> 3 |
||||
Mp3LameBuilder.Mode.DEFAULT -> 4 |
||||
} |
||||
} |
||||
|
||||
private fun getIntForVbrMode(mode: Mp3LameBuilder.VbrMode): Int { |
||||
return when (mode) { |
||||
Mp3LameBuilder.VbrMode.VBR_OFF -> 0 |
||||
Mp3LameBuilder.VbrMode.VBR_RH -> 2 |
||||
Mp3LameBuilder.VbrMode.VBR_ABR -> 3 |
||||
Mp3LameBuilder.VbrMode.VBR_MTRH -> 4 |
||||
Mp3LameBuilder.VbrMode.VBR_DEFAUT -> 6 |
||||
} |
||||
} |
||||
|
||||
} |
@ -1,147 +0,0 @@ |
||||
package com.frank.ffmpeg.mp3; |
||||
|
||||
public class Mp3LameBuilder { |
||||
|
||||
|
||||
public enum Mode { |
||||
STEREO, JSTEREO, MONO, DEFAULT |
||||
} |
||||
|
||||
public enum VbrMode { |
||||
VBR_OFF, VBR_RH, VBR_MTRH, VBR_ABR, VBR_DEFAUT |
||||
} |
||||
|
||||
|
||||
int inSampleRate; |
||||
int outSampleRate; |
||||
int outBitrate; |
||||
int outChannel; |
||||
public int quality; |
||||
int vbrQuality; |
||||
int abrMeanBitrate; |
||||
int lowPassFreq; |
||||
int highPassFreq; |
||||
float scaleInput; |
||||
Mode mode; |
||||
VbrMode vbrMode; |
||||
|
||||
String id3tagTitle; |
||||
String id3tagArtist; |
||||
String id3tagAlbum; |
||||
String id3tagComment; |
||||
String id3tagYear; |
||||
|
||||
Mp3LameBuilder() { |
||||
|
||||
this.id3tagTitle = null; |
||||
this.id3tagAlbum = null; |
||||
this.id3tagArtist = null; |
||||
this.id3tagComment = null; |
||||
this.id3tagYear = null; |
||||
|
||||
this.inSampleRate = 44100; |
||||
this.outSampleRate = 0; |
||||
this.outChannel = 2; |
||||
this.outBitrate = 128; |
||||
this.scaleInput = 1; |
||||
|
||||
this.quality = 5; |
||||
this.mode = Mode.DEFAULT; |
||||
this.vbrMode = VbrMode.VBR_OFF; |
||||
this.vbrQuality = 5; |
||||
this.abrMeanBitrate = 128; |
||||
|
||||
this.lowPassFreq = 0; |
||||
this.highPassFreq = 0; |
||||
} |
||||
|
||||
Mp3LameBuilder setQuality(int quality) { |
||||
this.quality = quality; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setInSampleRate(int inSampleRate) { |
||||
this.inSampleRate = inSampleRate; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setOutSampleRate(int outSampleRate) { |
||||
this.outSampleRate = outSampleRate; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setOutBitrate(int bitrate) { |
||||
this.outBitrate = bitrate; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setOutChannels(int channels) { |
||||
this.outChannel = channels; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setId3tagTitle(String title) { |
||||
this.id3tagTitle = title; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setId3tagArtist(String artist) { |
||||
this.id3tagArtist = artist; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setId3tagAlbum(String album) { |
||||
this.id3tagAlbum = album; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setId3tagComment(String comment) { |
||||
this.id3tagComment = comment; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setId3tagYear(String year) { |
||||
this.id3tagYear = year; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setScaleInput(float scaleAmount) { |
||||
this.scaleInput = scaleAmount; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setMode(Mode mode) { |
||||
this.mode = mode; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setVbrMode(VbrMode mode) { |
||||
this.vbrMode = mode; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setVbrQuality(int quality) { |
||||
this.vbrQuality = quality; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setAbrMeanBitrate(int bitrate) { |
||||
this.abrMeanBitrate = bitrate; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setLowpassFreqency(int freq) { |
||||
this.lowPassFreq = freq; |
||||
return this; |
||||
} |
||||
|
||||
Mp3LameBuilder setHighpassFreqency(int freq) { |
||||
this.highPassFreq = freq; |
||||
return this; |
||||
} |
||||
|
||||
Mp3Lame build() { |
||||
return new Mp3Lame(this); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,147 @@ |
||||
package com.frank.ffmpeg.mp3 |
||||
|
||||
class Mp3LameBuilder internal constructor() { |
||||
|
||||
|
||||
internal var inSampleRate: Int = 0 |
||||
internal var outSampleRate: Int = 0 |
||||
internal var outBitrate: Int = 0 |
||||
internal var outChannel: Int = 0 |
||||
var quality: Int = 0 |
||||
internal var vbrQuality: Int = 0 |
||||
internal var abrMeanBitrate: Int = 0 |
||||
internal var lowPassFreq: Int = 0 |
||||
internal var highPassFreq: Int = 0 |
||||
internal var scaleInput: Float = 0.toFloat() |
||||
internal var mode: Mode |
||||
internal var vbrMode: VbrMode |
||||
|
||||
internal var id3tagTitle: String? = null |
||||
internal var id3tagArtist: String? = null |
||||
internal var id3tagAlbum: String? = null |
||||
internal var id3tagComment: String? = null |
||||
internal var id3tagYear: String? = null |
||||
|
||||
|
||||
enum class Mode { |
||||
STEREO, JSTEREO, MONO, DEFAULT |
||||
} |
||||
|
||||
enum class VbrMode { |
||||
VBR_OFF, VBR_RH, VBR_MTRH, VBR_ABR, VBR_DEFAUT |
||||
} |
||||
|
||||
init { |
||||
|
||||
this.id3tagTitle = null |
||||
this.id3tagAlbum = null |
||||
this.id3tagArtist = null |
||||
this.id3tagComment = null |
||||
this.id3tagYear = null |
||||
|
||||
this.inSampleRate = 44100 |
||||
this.outSampleRate = 0 |
||||
this.outChannel = 2 |
||||
this.outBitrate = 128 |
||||
this.scaleInput = 1f |
||||
|
||||
this.quality = 5 |
||||
this.mode = Mode.DEFAULT |
||||
this.vbrMode = VbrMode.VBR_OFF |
||||
this.vbrQuality = 5 |
||||
this.abrMeanBitrate = 128 |
||||
|
||||
this.lowPassFreq = 0 |
||||
this.highPassFreq = 0 |
||||
} |
||||
|
||||
internal fun setQuality(quality: Int): Mp3LameBuilder { |
||||
this.quality = quality |
||||
return this |
||||
} |
||||
|
||||
internal fun setInSampleRate(inSampleRate: Int): Mp3LameBuilder { |
||||
this.inSampleRate = inSampleRate |
||||
return this |
||||
} |
||||
|
||||
internal fun setOutSampleRate(outSampleRate: Int): Mp3LameBuilder { |
||||
this.outSampleRate = outSampleRate |
||||
return this |
||||
} |
||||
|
||||
internal fun setOutBitrate(bitrate: Int): Mp3LameBuilder { |
||||
this.outBitrate = bitrate |
||||
return this |
||||
} |
||||
|
||||
internal fun setOutChannels(channels: Int): Mp3LameBuilder { |
||||
this.outChannel = channels |
||||
return this |
||||
} |
||||
|
||||
internal fun setId3tagTitle(title: String): Mp3LameBuilder { |
||||
this.id3tagTitle = title |
||||
return this |
||||
} |
||||
|
||||
internal fun setId3tagArtist(artist: String): Mp3LameBuilder { |
||||
this.id3tagArtist = artist |
||||
return this |
||||
} |
||||
|
||||
internal fun setId3tagAlbum(album: String): Mp3LameBuilder { |
||||
this.id3tagAlbum = album |
||||
return this |
||||
} |
||||
|
||||
internal fun setId3tagComment(comment: String): Mp3LameBuilder { |
||||
this.id3tagComment = comment |
||||
return this |
||||
} |
||||
|
||||
internal fun setId3tagYear(year: String): Mp3LameBuilder { |
||||
this.id3tagYear = year |
||||
return this |
||||
} |
||||
|
||||
internal fun setScaleInput(scaleAmount: Float): Mp3LameBuilder { |
||||
this.scaleInput = scaleAmount |
||||
return this |
||||
} |
||||
|
||||
internal fun setMode(mode: Mode): Mp3LameBuilder { |
||||
this.mode = mode |
||||
return this |
||||
} |
||||
|
||||
internal fun setVbrMode(mode: VbrMode): Mp3LameBuilder { |
||||
this.vbrMode = mode |
||||
return this |
||||
} |
||||
|
||||
internal fun setVbrQuality(quality: Int): Mp3LameBuilder { |
||||
this.vbrQuality = quality |
||||
return this |
||||
} |
||||
|
||||
internal fun setAbrMeanBitrate(bitrate: Int): Mp3LameBuilder { |
||||
this.abrMeanBitrate = bitrate |
||||
return this |
||||
} |
||||
|
||||
internal fun setLowpassFreqency(freq: Int): Mp3LameBuilder { |
||||
this.lowPassFreq = freq |
||||
return this |
||||
} |
||||
|
||||
internal fun setHighpassFreqency(freq: Int): Mp3LameBuilder { |
||||
this.highPassFreq = freq |
||||
return this |
||||
} |
||||
|
||||
internal fun build(): Mp3Lame { |
||||
return Mp3Lame(this) |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue