parent
083c306d2b
commit
d4b5956a81
Binary file not shown.
Binary file not shown.
@ -0,0 +1,224 @@ |
|||||||
|
|
||||||
|
#include "include/lame/lame.h" |
||||||
|
#include <jni.h> |
||||||
|
|
||||||
|
lame_global_flags *glf; |
||||||
|
|
||||||
|
lame_global_flags *initializeDefault(JNIEnv *env) { |
||||||
|
lame_global_flags *glf = lame_init(); |
||||||
|
lame_init_params(glf); |
||||||
|
return glf; |
||||||
|
} |
||||||
|
|
||||||
|
lame_global_flags *initialize( |
||||||
|
JNIEnv *env, |
||||||
|
jint inSamplerate, jint outChannel, |
||||||
|
jint outSamplerate, jint outBitrate, jfloat scaleInput, jint mode, jint vbrMode, |
||||||
|
jint quality, jint vbrQuality, jint abrMeanBitrate, jint lowpassFreq, jint highpassFreq, |
||||||
|
jstring id3tagTitle, jstring id3tagArtist, jstring id3tagAlbum, |
||||||
|
jstring id3tagYear, jstring id3tagComment) { |
||||||
|
|
||||||
|
lame_global_flags *glf = lame_init(); |
||||||
|
lame_set_in_samplerate(glf, inSamplerate); |
||||||
|
lame_set_num_channels(glf, outChannel); |
||||||
|
lame_set_out_samplerate(glf, outSamplerate); |
||||||
|
lame_set_brate(glf, outBitrate); |
||||||
|
lame_set_quality(glf, quality); |
||||||
|
lame_set_scale(glf, scaleInput); |
||||||
|
lame_set_VBR_q(glf, vbrQuality); |
||||||
|
lame_set_VBR_mean_bitrate_kbps(glf, abrMeanBitrate); |
||||||
|
lame_set_lowpassfreq(glf, lowpassFreq); |
||||||
|
lame_set_highpassfreq(glf, highpassFreq); |
||||||
|
|
||||||
|
switch (mode) { |
||||||
|
case 0: |
||||||
|
lame_set_mode(glf, STEREO); |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
lame_set_mode(glf, JOINT_STEREO); |
||||||
|
break; |
||||||
|
case 3: |
||||||
|
lame_set_mode(glf, MONO); |
||||||
|
break; |
||||||
|
case 4: |
||||||
|
lame_set_mode(glf, NOT_SET); |
||||||
|
break; |
||||||
|
default: |
||||||
|
lame_set_mode(glf, NOT_SET); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
switch (vbrMode) { |
||||||
|
case 0: |
||||||
|
lame_set_VBR(glf, vbr_off); |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
lame_set_VBR(glf, vbr_rh); |
||||||
|
break; |
||||||
|
case 3: |
||||||
|
lame_set_VBR(glf, vbr_abr); |
||||||
|
break; |
||||||
|
case 4: |
||||||
|
lame_set_VBR(glf, vbr_mtrh); |
||||||
|
break; |
||||||
|
case 6: |
||||||
|
lame_set_VBR(glf, vbr_default); |
||||||
|
break; |
||||||
|
default: |
||||||
|
lame_set_VBR(glf, vbr_off); |
||||||
|
break; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
const jchar *title = NULL; |
||||||
|
const jchar *artist = NULL; |
||||||
|
const jchar *album = NULL; |
||||||
|
const jchar *year = NULL; |
||||||
|
const jchar *comment = NULL; |
||||||
|
if (id3tagTitle) { |
||||||
|
title = (*env)->GetStringChars(env, id3tagTitle, NULL); |
||||||
|
} |
||||||
|
if (id3tagArtist) { |
||||||
|
artist = (*env)->GetStringChars(env, id3tagArtist, NULL); |
||||||
|
} |
||||||
|
if (id3tagAlbum) { |
||||||
|
album = (*env)->GetStringChars(env, id3tagAlbum, NULL); |
||||||
|
} |
||||||
|
if (id3tagYear) { |
||||||
|
year = (*env)->GetStringChars(env, id3tagYear, NULL); |
||||||
|
} |
||||||
|
if (id3tagComment) { |
||||||
|
comment = (*env)->GetStringChars(env, id3tagComment, NULL); |
||||||
|
} |
||||||
|
|
||||||
|
if (title || artist || album || year || comment) { |
||||||
|
id3tag_init(glf); |
||||||
|
|
||||||
|
if (title) { |
||||||
|
id3tag_set_title(glf, (const char *) title); |
||||||
|
(*env)->ReleaseStringChars(env, id3tagTitle, title); |
||||||
|
} |
||||||
|
if (artist) { |
||||||
|
id3tag_set_artist(glf, (const char *) artist); |
||||||
|
(*env)->ReleaseStringChars(env, id3tagArtist, artist); |
||||||
|
} |
||||||
|
if (album) { |
||||||
|
id3tag_set_album(glf, (const char *) album); |
||||||
|
(*env)->ReleaseStringChars(env, id3tagAlbum, album); |
||||||
|
} |
||||||
|
if (year) { |
||||||
|
id3tag_set_year(glf, (const char *) year); |
||||||
|
(*env)->ReleaseStringChars(env, id3tagYear, year); |
||||||
|
} |
||||||
|
if (comment) { |
||||||
|
id3tag_set_comment(glf, (const char *) comment); |
||||||
|
(*env)->ReleaseStringChars(env, id3tagComment, comment); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
lame_init_params(glf); |
||||||
|
|
||||||
|
|
||||||
|
return glf; |
||||||
|
} |
||||||
|
|
||||||
|
jint encode( |
||||||
|
JNIEnv *env, lame_global_flags *glf, |
||||||
|
jshortArray buffer_l, jshortArray buffer_r, |
||||||
|
jint samples, jbyteArray mp3buf) { |
||||||
|
jshort *j_buffer_l = (*env)->GetShortArrayElements(env, buffer_l, NULL); |
||||||
|
|
||||||
|
jshort *j_buffer_r = (*env)->GetShortArrayElements(env, buffer_r, NULL); |
||||||
|
|
||||||
|
const jsize mp3buf_size = (*env)->GetArrayLength(env, mp3buf); |
||||||
|
jbyte *j_mp3buf = (*env)->GetByteArrayElements(env, mp3buf, NULL); |
||||||
|
|
||||||
|
int result = lame_encode_buffer(glf, j_buffer_l, j_buffer_r, |
||||||
|
samples, j_mp3buf, mp3buf_size); |
||||||
|
|
||||||
|
(*env)->ReleaseShortArrayElements(env, buffer_l, j_buffer_l, 0); |
||||||
|
(*env)->ReleaseShortArrayElements(env, buffer_r, j_buffer_r, 0); |
||||||
|
(*env)->ReleaseByteArrayElements(env, mp3buf, j_mp3buf, 0); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
jint encodeBufferInterleaved( |
||||||
|
JNIEnv *env, lame_global_flags *glf, |
||||||
|
jshortArray pcm, jint samples, jbyteArray mp3buf) { |
||||||
|
jshort *j_pcm = (*env)->GetShortArrayElements(env, pcm, NULL); |
||||||
|
|
||||||
|
const jsize mp3buf_size = (*env)->GetArrayLength(env, mp3buf); |
||||||
|
jbyte *j_mp3buf = (*env)->GetByteArrayElements(env, mp3buf, NULL); |
||||||
|
|
||||||
|
int result = lame_encode_buffer_interleaved(glf, j_pcm, |
||||||
|
samples, j_mp3buf, mp3buf_size); |
||||||
|
|
||||||
|
(*env)->ReleaseShortArrayElements(env, pcm, j_pcm, 0); |
||||||
|
(*env)->ReleaseByteArrayElements(env, mp3buf, j_mp3buf, 0); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
jint flush( |
||||||
|
JNIEnv *env, lame_global_flags *glf, |
||||||
|
jbyteArray mp3buf) { |
||||||
|
const jsize mp3buf_size = (*env)->GetArrayLength(env, mp3buf); |
||||||
|
jbyte *j_mp3buf = (*env)->GetByteArrayElements(env, mp3buf, NULL); |
||||||
|
|
||||||
|
int result = lame_encode_flush(glf, j_mp3buf, mp3buf_size); |
||||||
|
|
||||||
|
(*env)->ReleaseByteArrayElements(env, mp3buf, j_mp3buf, 0); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
void close_lame(lame_global_flags *glf) { |
||||||
|
lame_close(glf); |
||||||
|
glf = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_com_frank_ffmpeg_AudioPlayer_lameInitDefault |
||||||
|
(JNIEnv *env, jclass jclazz) { |
||||||
|
|
||||||
|
glf = initializeDefault(env); |
||||||
|
} |
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_com_frank_ffmpeg_AudioPlayer_lameInit( |
||||||
|
JNIEnv *env, jclass cls, jint inSampleRate, jint outChannel, |
||||||
|
jint outSampleRate, jint outBitrate, jfloat scaleInput, jint mode, jint vbrMode, |
||||||
|
jint quality, jint vbrQuality, jint abrMeanBitrate, jint lowPassFreq, jint highPassFreq, |
||||||
|
jstring id3tagTitle, jstring id3tagArtist, jstring id3tagAlbum, |
||||||
|
jstring id3tagYear, jstring id3tagComment) { |
||||||
|
|
||||||
|
glf = initialize(env, inSampleRate, outChannel, outSampleRate, outBitrate, scaleInput, mode, |
||||||
|
vbrMode, |
||||||
|
quality, vbrQuality, abrMeanBitrate, lowPassFreq, highPassFreq, id3tagTitle, |
||||||
|
id3tagArtist, id3tagAlbum, |
||||||
|
id3tagYear, |
||||||
|
id3tagComment); |
||||||
|
} |
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL Java_com_frank_ffmpeg_AudioPlayer_lameEncode( |
||||||
|
JNIEnv *env, jclass cls, jshortArray buffer_l, |
||||||
|
jshortArray buffer_r, jint samples, jbyteArray mp3buf) { |
||||||
|
return encode(env, glf, buffer_l, buffer_r, samples, mp3buf); |
||||||
|
} |
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL Java_com_frank_ffmpeg_AudioPlayer_encodeBufferInterleaved( |
||||||
|
JNIEnv *env, jclass cls, jshortArray pcm, |
||||||
|
jint samples, jbyteArray mp3buf) { |
||||||
|
return encodeBufferInterleaved(env, glf, pcm, samples, mp3buf); |
||||||
|
} |
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL Java_com_frank_ffmpeg_AudioPlayer_lameFlush( |
||||||
|
JNIEnv *env, jclass cls, jbyteArray mp3buf) { |
||||||
|
return flush(env, glf, mp3buf); |
||||||
|
} |
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_com_frank_ffmpeg_AudioPlayer_lameClose( |
||||||
|
JNIEnv *env, jclass cls) { |
||||||
|
close_lame(glf); |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,340 @@ |
|||||||
|
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,71 @@ |
|||||||
|
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,147 @@ |
|||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue