Android Open Source - AudioChoice Basic Media Extractor Media Player






From Project

Back to project page AudioChoice.

License

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.

Java Source Code

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) {
    }
}




Java Source Code List

com.hwrdprkns.audiochoice.BasicMediaExtractorMediaPlayer.java
com.hwrdprkns.audiochoice.BasicMediaPlayer.java
com.hwrdprkns.audiochoice.BasicProxyMediaPlayer.java
com.hwrdprkns.audiochoice.BasicStreamMediaPlayer.java
com.hwrdprkns.audiochoice.IAudioPlayer.java
com.hwrdprkns.audiochoice.IPlayerView.java
com.hwrdprkns.audiochoice.MainActivity.java
com.hwrdprkns.audiochoice.MainFragment.java
com.hwrdprkns.audiochoice.server.AudioChoiceLocalServer.java
com.hwrdprkns.audiochoice.server.NanoHTTPD.java