Back to project page AudioChoice.
The source code is released under:
Apache License
If you think the Android project AudioChoice listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.hwrdprkns.audiochoice; // w ww.j a v a 2 s. c o m import android.annotation.TargetApi; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; import android.media.MediaCodec; import android.media.MediaExtractor; import android.media.MediaFormat; import android.util.Log; import java.io.IOException; import java.nio.ByteBuffer; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.TimeUnit; @TargetApi(16) public class BasicMediaExtractorMediaPlayer implements IAudioPlayer, AudioTrack.OnPlaybackPositionUpdateListener { private static final String TAG = BasicMediaExtractorMediaPlayer.class.getSimpleName(); protected Context context; protected IPlayerView view_delegate; private AudioTrack current_track; AssetFileDescriptor music_file; MediaExtractor extractor; MediaFormat format; MediaCodec codec; ByteBuffer[] codecInputBuffers; ByteBuffer[] codecOutputBuffers; public BasicMediaExtractorMediaPlayer(Context context, IPlayerView viewDelegate) throws IOException { this.context = context; this.view_delegate = viewDelegate; music_file = context.getResources().openRawResourceFd(R.raw.wildfire); extractor = new MediaExtractor(); extractor.setDataSource(music_file.getFileDescriptor(), music_file.getStartOffset(), music_file.getLength()); format = extractor.getTrackFormat(0); codec = MediaCodec.createDecoderByType(format.getString(MediaFormat.KEY_MIME)); codec.configure(format, null, null, 0); codec.start(); codecInputBuffers = codec.getInputBuffers(); codecOutputBuffers = codec.getOutputBuffers(); extractor.selectTrack(0); } @Override public void start() { if (current_track == null) { int bufferSize = AudioTrack.getMinBufferSize(format.getInteger(MediaFormat.KEY_SAMPLE_RATE),AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT); current_track = new AudioTrack(AudioManager.STREAM_MUSIC, format.getInteger(MediaFormat.KEY_SAMPLE_RATE), AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM); current_track.setPlaybackPositionUpdateListener(this); } long hours = TimeUnit.MILLISECONDS.toHours(getDuration()); Date date = new Date(getDuration()); DateFormat formatter = new SimpleDateFormat(hours > 0 ? "HH:mm:ss" : "mm:ss"); view_delegate.setDuration(formatter.format(date)); pause_decoding = false; if (!isPlaying()) { new Thread(new Runnable() { @Override public void run() { try { startProducingAudio(); } catch (Exception e) { // Just in case we break something. } } }).start(); } } // This really helped me decode correctly // https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/DecoderTest.java private void startProducingAudio() { final long kTimeOutUs = 5000; MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); boolean sawInputEOS = false; boolean sawOutputEOS = false; long startNanoTime = System.nanoTime(); int loopCount = 0; while (!sawOutputEOS && !pause_decoding) { if (loopCount % 999 == 0) { long endNanoTime = System.nanoTime(); Log.d(TAG, "1000 decode loops in " + TimeUnit.NANOSECONDS.toSeconds(endNanoTime - startNanoTime) + " seconds."); startNanoTime = System.nanoTime(); loopCount = 0; } loopCount++; if (!sawInputEOS) { int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs); if (inputBufIndex >= 0) { ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; int sampleSize = extractor.readSampleData(dstBuf, 0); long presentationTimeUs = 0; if (sampleSize < 0) { sawInputEOS = true; sampleSize = 0; } else { presentationTimeUs = extractor.getSampleTime(); } codec.queueInputBuffer(inputBufIndex, 0, //offset sampleSize, presentationTimeUs, sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); if (!sawInputEOS) { extractor.advance(); } } } final int res = codec.dequeueOutputBuffer(info, kTimeOutUs); if (res >= 0) { int outputBufIndex = res; ByteBuffer buf = codecOutputBuffers[outputBufIndex]; final byte[] chunk = new byte[info.size]; buf.get(chunk); // Read the buffer all at once buf.clear(); // ** MUST DO!!! OTHERWISE THE NEXT TIME YOU GET THIS SAME BUFFER BAD THINGS WILL HAPPEN if (chunk.length > 0) { current_track.write(chunk, 0, chunk.length); // Check pause decoding so we don't get a crash when // switching players if (!isPlaying() && !pause_decoding) { current_track.play(); } } codec.releaseOutputBuffer(outputBufIndex, false /* render */); if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { sawOutputEOS = true; } } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { codecOutputBuffers = codec.getOutputBuffers(); } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { final MediaFormat oformat = codec.getOutputFormat(); Log.d(TAG, "Output format has changed to " + oformat); current_track.setPlaybackRate(oformat.getInteger(MediaFormat.KEY_SAMPLE_RATE)); } } } private boolean pause_decoding = false; @Override public void pause() { pause_decoding = true; current_track.pause(); } @Override public void seekTo(int percent) { extractor.seekTo((format.getLong(MediaFormat.KEY_DURATION) * percent) / 100l, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); } @Override public int getCurrentPosition() { return (current_track.getPlaybackHeadPosition() / current_track.getSampleRate()) * 1000; // frames * sec / frame = sec } @Override public int getDuration() { return (int) (format.getLong(MediaFormat.KEY_DURATION) / 1000); // us -> ms } @Override public boolean isPlaying() { return current_track.getPlayState() == AudioTrack.PLAYSTATE_PLAYING; } @Override public void stop() { pause(); current_track.stop(); } @Override public void release() { codec.stop(); codec.release(); current_track.release(); } @Override public void reset() { release(); } @Override public void onMarkerReached(AudioTrack track) { } @Override public void onPeriodicNotification(AudioTrack track) { } }