private void initStream() throws IOException, IllegalArgumentException {
    L.v(TAG, "initStream called in state=" + state);
    lock.lock();
    try {
      extractor = new MediaExtractor();
      if (path != null) {
        extractor.setDataSource(path);
      } else {
        error("initStream");
        throw new IOException();
      }
      int trackNum = 0;
      final MediaFormat oFormat = extractor.getTrackFormat(trackNum);

      if (!oFormat.containsKey(MediaFormat.KEY_SAMPLE_RATE)) {
        error("initStream");
        throw new IOException("No KEY_SAMPLE_RATE");
      }
      int sampleRate = oFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);

      if (!oFormat.containsKey(MediaFormat.KEY_CHANNEL_COUNT)) {
        error("initStream");
        throw new IOException("No KEY_CHANNEL_COUNT");
      }
      int channelCount = oFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);

      if (!oFormat.containsKey(MediaFormat.KEY_MIME)) {
        error("initStream");
        throw new IOException("No KEY_MIME");
      }
      final String mime = oFormat.getString(MediaFormat.KEY_MIME);

      if (!oFormat.containsKey(MediaFormat.KEY_DURATION)) {
        error("initStream");
        throw new IOException("No KEY_DURATION");
      }
      duration = oFormat.getLong(MediaFormat.KEY_DURATION);

      L.v(TAG, "Sample rate: " + sampleRate);
      L.v(TAG, "Mime type: " + mime);
      initDevice(sampleRate, channelCount);
      extractor.selectTrack(trackNum);
      codec = MediaCodec.createDecoderByType(mime);
      codec.configure(oFormat, null, null, 0);
    } finally {
      lock.unlock();
    }
  }
 @Override
 public void prepare() throws IOException {
   L.v(TAG, "prepare called in state: " + state);
   switch (state) {
     case INITIALIZED:
     case STOPPED:
       initStream();
       state = State.PREPARED;
       L.d(TAG, "State changed to: " + state);
       break;
     default:
       error("prepare");
   }
 }
 @Override
 public void pause() {
   L.v(TAG, "pause called");
   switch (state) {
     case PLAYBACK_COMPLETED:
       state = State.PAUSED;
       L.d(TAG, "State changed to: " + state);
       stayAwake(false);
       break;
     case STARTED:
     case PAUSED:
       track.pause();
       state = State.PAUSED;
       L.d(TAG, "State changed to: " + state);
       stayAwake(false);
       break;
     default:
       error("pause");
   }
 }
 @Override
 public void reset() {
   L.v(TAG, "reset called in state: " + state);
   stayAwake(false);
   lock.lock();
   try {
     continuing = false;
     try {
       if (state != State.PLAYBACK_COMPLETED) {
         while (isDecoding) {
           synchronized (decoderLock) {
             decoderLock.notify();
             decoderLock.wait();
           }
         }
       }
     } catch (InterruptedException e) {
       L.e(TAG, "Interrupted in reset while waiting for decoder thread to stop.", e);
     }
     if (codec != null) {
       codec.release();
       L.d(TAG, "releasing codec");
       codec = null;
     }
     if (extractor != null) {
       extractor.release();
       extractor = null;
     }
     if (track != null) {
       track.release();
       track = null;
     }
     state = State.IDLE;
     L.d(TAG, "State changed to: " + state);
   } finally {
     lock.unlock();
   }
 }
 @Override
 public void start() {
   L.v(TAG, "start called in state:" + state);
   switch (state) {
     case PLAYBACK_COMPLETED:
       try {
         initStream();
       } catch (IOException e) {
         e.printStackTrace();
         error("start");
         break;
       }
     case PREPARED:
       state = State.STARTED;
       L.d(TAG, "State changed to: " + state);
       continuing = true;
       track.play();
       decode();
       stayAwake(true);
       break;
     case STARTED:
       break;
     case PAUSED:
       state = State.STARTED;
       L.d(TAG, "State changed to: " + state + " with path=" + path);
       synchronized (decoderLock) {
         decoderLock.notify();
       }
       track.play();
       stayAwake(true);
       break;
     default:
       error("start");
       break;
   }
 }