I currently have two separate media extractors and codecs that I am using to break down each individual sample into ByteBuffer
s. Then I store each sample into two short
arrays. Then I call my mix function which combines the two samples into one short[]
and if I play this short[]
with AudioTrack
it plays the result great with both sounds playing at the same time as desired.
However, what I really want to do is use MediaMuxer
to convert my new short[]
back to an mp4 audio file and store it to the device for later playback. Can someone please help me figure out what Im doing wrong?
Here is some of the code I have... As you can see, I commented out the AudioTrack
and that is where Im trying to turn the short[]
back into the ByteBuffer
so I can use the media muxer to create the audio file. The result this gives me is a new audio file that is the correct name and overall length stored on my device... but when I try to play it... it jumps from begining to end in a fraction of a second and doesnt actually play and audio.
songOutPath = Environment.getExternalStorageDirectory()+"/My Folder/song.raw";
muxer = new MediaMuxer(songOutPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
mTrackIndex = muxer.addTrack(format);
muxer.start();
mMuxerStarted = true;
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
final long timeoutUs = 5000;
boolean sawInputEOS = false;
boolean sawOutputEOS = false;
int noOutputCounter = 0;
MediaCodec.BufferInfo info2 = new MediaCodec.BufferInfo();
final long timeoutUs2 = 5000;
boolean sawInputEOS2 = false;
boolean sawOutputEOS2 = false;
int noOutputCounter2 = 0;
while (!sawOutputEOS && !sawOutputEOS2 && noOutputCounter < 50 && noOutputCounter2 < 50) {
noOutputCounter++;
if (!sawInputEOS) {
int inputBufferIndex = codec.dequeueInputBuffer(timeoutUs);
if (inputBufferIndex >= 0) {
ByteBuffer buffer = codecInputBuffers[inputBufferIndex];
int sampleSize = extractor.readSampleData(buffer, 0);
long presentationTimeUs = 0;
if (sampleSize < 0) {
sawInputEOS = true;
sampleSize = 0;
} else {
presentationTimeUs = extractor.getSampleTime();
}
codec.queueInputBuffer(inputBufferIndex, 0, sampleSize,
presentationTimeUs,
sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
if (!sawInputEOS) {
extractor.advance();
}
}
}
noOutputCounter2++;
if (!sawInputEOS2) {
int inputBufferIndex2 = codec2.dequeueInputBuffer(timeoutUs2);
if (inputBufferIndex2 >= 0) {
ByteBuffer buffer2 = codecInputBuffers2[inputBufferIndex2];
int sampleSize2 = extractor2.readSampleData(buffer2, 0);
long presentationTimeUs2 = 0;
if (sampleSize2 < 0) {
sawInputEOS2 = true;
sampleSize2 = 0;
} else {
presentationTimeUs2 = extractor2.getSampleTime();
}
codec2.queueInputBuffer(inputBufferIndex2, 0, sampleSize2,
presentationTimeUs2,
sawInputEOS2 ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
if (!sawInputEOS2) {
extractor2.advance();
}
}
}
int outputBufferIndex = codec.dequeueOutputBuffer(info, timeoutUs);
int outputBufferIndex2 = codec2.dequeueOutputBuffer(info2, timeoutUs2);
if (outputBufferIndex >= 0) {
if (info.size > 0) {
noOutputCounter = 0;
}
if (info2.size > 0) {
noOutputCounter2 = 0;
}
ByteBuffer buffer = codecOutputBuffers[outputBufferIndex];
ByteBuffer buffer2 = codecOutputBuffers2[outputBufferIndex2];
shortArrayOne = new short[info.size/2];
shortArrayTwo = new short[info2.size/2];
buffer.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shortArrayOne);
buffer.clear();
buffer2.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shortArrayTwo);
buffer2.clear();
shortArrayThree = mix(shortArrayOne, shortArrayTwo);
if(shortArrayThree.length > 0){
//audioTrack.write(shortArrayThree,0,shortArrayThree.length);
ByteBuffer byteBuf = ByteBuffer.allocate(2*shortArrayThree.length);
int index;
for(index = 0; index != shortArrayThree.length; index++)
{
byteBuf.putShort(shortArrayThree[index]);
}
muxer.writeSampleData(mTrackIndex, byteBuf, info);
}
codec.releaseOutputBuffer(outputBufferIndex, false);
codec2.releaseOutputBuffer(outputBufferIndex2, false);
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
sawOutputEOS = true;
}
if ((info2.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
sawOutputEOS2 = true;
}
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
codecOutputBuffers = codec.getOutputBuffers();
if (outputBufferIndex2 == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
codecOutputBuffers2 = codec2.getOutputBuffers();
}
}
}
Update
I added a method to try and encode the pcm file to an audio file for playback. This creates a new file but now when I play it it is twice the length as it is supposed to be and it just plays static the whole time. I think there is either something wrong with the way im creating the song.raw file or some thing wrong with the encoding of that file to a mp4. Here is a look at the rest of my code to encode the pcm file to mp4. Can someone tell me where I may be going wrong? Thank You.
public void encodeToAAC(){
final String LOGTAG = "Encode";
final String COMPRESSED_AUDIO_FILE_MIME_TYPE = "audio/mp4a-latm";
final int KEY_CHANNEL_COUNT = 2;
final int COMPRESSED_AUDIO_FILE_BIT_RATE = 96000; // 96kbps
final int SAMPLING_RATE = 44100;
final int CODEC_TIMEOUT_IN_MS = 5000;
final int BUFFER_SIZE = 88200;
Boolean mStop = false;
MediaCodec codec;
try {
String filePath = Environment.getExternalStorageDirectory()+"/My Folder/song.raw";
File inputFile = new File(filePath);
FileInputStream fis = new FileInputStream(inputFile);
File outputFile = new File(Environment.getExternalStorageDirectory()+"/My Folder/song.mp4");
if (outputFile.exists()) outputFile.delete();
MediaMuxer mux = new MediaMuxer(outputFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
MediaFormat outputFormat = MediaFormat.createAudioFormat(COMPRESSED_AUDIO_FILE_MIME_TYPE, SAMPLING_RATE, KEY_CHANNEL_COUNT);
outputFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, COMPRESSED_AUDIO_FILE_BIT_RATE);
codec = MediaCodec.createEncoderByType(COMPRESSED_AUDIO_FILE_MIME_TYPE);
codec.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
codec.start();
ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
MediaCodec.BufferInfo outBuffInfo = new MediaCodec.BufferInfo();
byte[] tempBuffer = new byte[BUFFER_SIZE];
boolean hasMoreData = true;
double presentationTimeUs = 0;
int audioTrackIdx = 0;
int totalBytesRead = 0;
int percentComplete;
do {
int inputBufIndex = 0;
while (inputBufIndex != -1 && hasMoreData) {
inputBufIndex = codec.dequeueInputBuffer(CODEC_TIMEOUT_IN_MS);
if (inputBufIndex >= 0) {
ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
dstBuf.clear();
int bytesRead = fis.read(tempBuffer, 0, dstBuf.limit());
if (bytesRead == -1) { // -1 implies EOS
hasMoreData = false;
codec.queueInputBuffer(inputBufIndex, 0, 0, (long) presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
} else {
totalBytesRead += bytesRead;
dstBuf.put(tempBuffer, 0, bytesRead);
codec.queueInputBuffer(inputBufIndex, 0, bytesRead, (long) presentationTimeUs, 0);
presentationTimeUs = 1000000l * (totalBytesRead / 2) / SAMPLING_RATE;
}
}
}
// Drain audio
int outputBufIndex = 0;
while (outputBufIndex != MediaCodec.INFO_TRY_AGAIN_LATER) {
outputBufIndex = codec.dequeueOutputBuffer(outBuffInfo, CODEC_TIMEOUT_IN_MS);
if (outputBufIndex >= 0) {
ByteBuffer encodedData = codecOutputBuffers[outputBufIndex];
encodedData.position(outBuffInfo.offset);
encodedData.limit(outBuffInfo.offset + outBuffInfo.size);
if ((outBuffInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0 && outBuffInfo.size != 0) {
codec.releaseOutputBuffer(outputBufIndex, false);
} else {
mux.writeSampleData(audioTrackIdx, codecOutputBuffers[outputBufIndex], outBuffInfo);
codec.releaseOutputBuffer(outputBufIndex, false);
}
} else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
outputFormat = codec.getOutputFormat();
Log.v(LOGTAG, "Output format changed - " + outputFormat);
audioTrackIdx = mux.addTrack(outputFormat);
mux.start();
} else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
Log.e(LOGTAG, "Output buffers changed during encode!");
} else if (outputBufIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
// NO OP
} else {
Log.e(LOGTAG, "Unknown return code from dequeueOutputBuffer - " + outputBufIndex);
}
}
percentComplete = (int) Math.round(((float) totalBytesRead / (float) inputFile.length()) * 100.0);
Log.v(LOGTAG, "Conversion % - " + percentComplete);
} while (outBuffInfo.flags != MediaCodec.BUFFER_FLAG_END_OF_STREAM && !mStop);
fis.close();
mux.stop();
mux.release();
Log.v(LOGTAG, "Compression done ...");
} catch (FileNotFoundException e) {
Log.e(LOGTAG, "File not found!", e);
} catch (IOException e) {
Log.e(LOGTAG, "IO exception!", e);
}
mStop = false;
// Notify UI thread...
}