2016-07-18 7 views
1

Я пытаюсь реализовать точный поиск видео, используя MediaCodec и MediaExtractor. Следуя Grafika's MoviePlayer, мне удалось реализовать поиск вперед. Однако у меня все еще проблема с обратным поиском. Соответствующий бит кода здесь:Android MediaCodec назад ищет

public void seekBackward(long position){ 
    final int TIMEOUT_USEC = 10000; 
    int inputChunk = 0; 
    long firstInputTimeNsec = -1; 

    boolean outputDone = false; 
    boolean inputDone = false; 

    mExtractor.seekTo(position, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); 
    Log.d("TEST_MEDIA", "sampleTime: " + mExtractor.getSampleTime()/1000 + " -- position: " + position/1000 + " ----- BACKWARD"); 

    while (mExtractor.getSampleTime() < position && position >= 0) { 

     if (VERBOSE) Log.d(TAG, "loop"); 
     if (mIsStopRequested) { 
      Log.d(TAG, "Stop requested"); 
      return; 
     } 

     // Feed more data to the decoder. 
     if (!inputDone) { 
      int inputBufIndex = mDecoder.dequeueInputBuffer(TIMEOUT_USEC); 
      if (inputBufIndex >= 0) { 
       if (firstInputTimeNsec == -1) { 
        firstInputTimeNsec = System.nanoTime(); 
       } 
       ByteBuffer inputBuf = mDecoderInputBuffers[inputBufIndex]; 
       // Read the sample data into the ByteBuffer. This neither respects nor 
       // updates inputBuf's position, limit, etc. 
       int chunkSize = mExtractor.readSampleData(inputBuf, 0); 
       if (chunkSize < 0) { 
        // End of stream -- send empty frame with EOS flag set. 
        mDecoder.queueInputBuffer(inputBufIndex, 0, 0, 0L, 
          MediaCodec.BUFFER_FLAG_END_OF_STREAM); 
        inputDone = true; 
        if (VERBOSE) Log.d(TAG, "sent input EOS"); 
       } else { 
        if (mExtractor.getSampleTrackIndex() != mTrackIndex) { 
         Log.w(TAG, "WEIRD: got sample from track " + 
           mExtractor.getSampleTrackIndex() + ", expected " + mTrackIndex); 
        } 
        long presentationTimeUs = mExtractor.getSampleTime(); 
        mDecoder.queueInputBuffer(inputBufIndex, 0, chunkSize, 
          presentationTimeUs, 0 /*flags*/); 
        if (VERBOSE) { 
         Log.d(TAG, "submitted frame " + inputChunk + " to dec, size=" + chunkSize); 
        } 
        inputChunk++; 
        mExtractor.advance(); 
       } 
      } else { 
       if (VERBOSE) Log.d(TAG, "input buffer not available"); 
      } 
     } 

     if (!outputDone) { 
      int decoderStatus = mDecoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC); 
      if (decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { 
       // no output available yet 
       if (VERBOSE) Log.d(TAG, "no output from decoder available"); 
      } else if (decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 
       // not important for us, since we're using Surface 
       if (VERBOSE) Log.d(TAG, "decoder output buffers changed"); 
      } else if (decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 
       MediaFormat newFormat = mDecoder.getOutputFormat(); 
       if (VERBOSE) Log.d(TAG, "decoder output format changed: " + newFormat); 
      } else if (decoderStatus < 0) { 
       throw new RuntimeException(
         "unexpected result from decoder.dequeueOutputBuffer: " + 
           decoderStatus); 
      } else { // decoderStatus >= 0 
       if (firstInputTimeNsec != 0) { 
        // Log the delay from the first buffer of input to the first buffer 
        // of output. 
        long nowNsec = System.nanoTime(); 
        Log.d(TAG, "startup lag " + ((nowNsec-firstInputTimeNsec)/1000000.0) + " ms"); 
        firstInputTimeNsec = 0; 
       } 
       boolean doLoop = false; 
       if (VERBOSE) Log.d(TAG, "surface decoder given buffer " + decoderStatus + 
         " (size=" + mBufferInfo.size + ")"); 
       if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 
        if (VERBOSE) Log.d(TAG, "output EOS"); 
        if (mLoop) { 
         doLoop = true; 
        } else { 
         outputDone = true; 
        } 
       } 

       boolean doRender = (mBufferInfo.size != 0); 

       // As soon as we call releaseOutputBuffer, the buffer will be forwarded 
       // to SurfaceTexture to convert to a texture. We can't control when it 
       // appears on-screen, but we can manage the pace at which we release 
       // the buffers. 
       if (doRender && mFrameCallback != null) { 
        mFrameCallback.preRender(mBufferInfo.presentationTimeUs); 
       } 
       mDecoder.releaseOutputBuffer(decoderStatus, doRender); 
       doRender = false; 
       if (doRender && mFrameCallback != null) { 
        mFrameCallback.postRender(); 
       } 

       if (doLoop) { 
        Log.d(TAG, "Reached EOS, looping"); 
        mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 
        inputDone = false; 
        mDecoder.flush(); // reset decoder state 
        mFrameCallback.loopReset(); 
       } 
      } 
     } 
    } 
} 

В принципе, это то же самое, как doExtract метод MoviePlayer в. Я просто добавлю небольшую модификацию, чтобы вернуться к предыдущему ключевому кадру, чем декодировать вперед в позицию, которую я хочу. Я также следую fadden suggestion here с небольшим успехом.

Еще один вопрос, на мой взгляд, ExoPlayer построен на MediaCodec, а затем, как он может воспроизводить видео, записанные iOS, только отлично, в то время как чистая реализация MoviePlayer MediaCodec не может?

ответ

2

Хорошо, вот так я решаю свою проблему, в основном я неправильно понял комментарий Fadden на фразу render. Проблема заключается не в декодировании, а в том, чтобы отображать только последний буфер, который ближе всего к позиции поиска. Вот как я это делаю:

if (Math.abs(position - mExtractor.getSampleTime()) < 10000) { 
    mDecoder.releaseOutputBuffer(decoderStatus, true); 
} else { 
    mDecoder.releaseOutputBuffer(decoderStatus, false); 
} 

Это довольно хакерский способ сделать это. Элегантный способ должен сохранять последний выходной буфер и отображать его вне цикла while, но я действительно не знаю, как получить доступ к выходному буферу, чтобы я мог сохранить его во временный.

EDIT:

Это немного меньше хака способа сделать это. В принципе, нам нужно всего лишь вычислить полные кадры между ключевым кадром и поиском, а затем нам просто нужно отобразить 1 или 2 кадра, наиболее близких к поисковой позиции. Что-то вроде этого:

mExtractor.seekTo(position, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); 
    int stopPosition = getStopPosition(mExtractor.getSampleTime(), position); 
    int count = 0; 

    while (mExtractor.getSampleTime() < position && mExtractor.getSampleTime() != -1 && position >= 0) { 
    .... 

     if(stopPosition - count < 2) { //just to make sure we will get something (1 frame sooner), see getStopPosition comment 
      mDecoder.releaseOutputBuffer(decoderStatus, true); 
     }else{ 
      mDecoder.releaseOutputBuffer(decoderStatus, false); 
     } 
     count++; 
    ... 
    } 

/** 
* Calculate how many frame in between the key frame and the seeking position 
* so that we can determine how many while loop will be execute, then we can just 
* need to stop the loop 2 or 3 frames sooner to ensure we can get something. 
* */ 
private int getStopPosition(long start, long end){ 
    long delta = end - start; 
    float framePerMicroSecond = mFPS/1000000; 

    return (int)(delta * framePerMicroSecond); 
}