private long fastSeek(long targetTime, MediaExtractor extractor, MediaCodec codec) throws IOException {
        codec.flush();
        extractor.seekTo(targetTime, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);

        if(extractor.getSampleTime() == targetTime) {
            Log.d(TAG, "skip fastseek, already there");
            return targetTime;
        }

        // 1. Queue first sample which should be the sync/I frame
        skipToNextSample();
        queueSampleToCodec(false);

        // 2. Then, fast forward to target frame
        /* 2.1 Search for the best candidate frame, which is the one whose
         *     right/positive/future distance is minimized
         */
        extractor.seekTo(targetTime, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);

        /* Specifies how many frames we continue to check after the first candidate,
         * to account for DTS picture reordering (this value is arbitrarily chosen) */
        int maxFrameLookahead = 20;

        long candidatePTS = 0;
        long candidateDistance = Long.MAX_VALUE;
        int lookaheadCount = 0;

        while (extractor.advance() && lookaheadCount < maxFrameLookahead) {
            long distance = targetTime - extractor.getSampleTime();
            if (distance >= 0 && distance < candidateDistance) {
                candidateDistance = distance;
                candidatePTS = extractor.getSampleTime();
                //Log.d(TAG, "candidate " + candidatePTS + " d=" + candidateDistance);
            }
            if (distance < 0) {
                lookaheadCount++;
            }
        }
        targetTime = candidatePTS; // set best candidate frame as exact seek target

        // 2.2 Fast forward to chosen candidate frame
        extractor.seekTo(targetTime, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
        while (extractor.getSampleTime() != targetTime) {
            extractor.advance();
        }
        Log.d(TAG, "exact fastseek match:       " + extractor.getSampleTime());

        return targetTime;
    }
In contrast to the old rendering method through {@link MediaCodec#releaseOutputBuffer(int, boolean)}
this method does not need a timing/throttling mechanism (e.g. {@link Thread#sleep(long)})
and returns instantly, but still defers rendering internally until the given
timestamp. It does not release the buffer until the actual rendering though,
and thus times/throttles the decoding loop by keeping the buffers and not returning
them until the picture is rendered, which means that {@link MediaCodec#dequeueOutputBuffer(MediaCodec.BufferInfo, long)}
fails until a frame is rendered and the associated buffer returned to the codec
for a new frame output.
MediaPlayer/src/main/java/net/protyposis/android/mediaplayer/MediaCodecVideoDecoder.java