title: Unable to do low-level decoding of video on Android 4.2 without using media extractor
body: <p>I wanted to decode video frames without using an extractor. So I just tried a small sample, where I use media extractor but I don't do <code>extractor.readsample()</code> to copy the bitstream data into the input buffer instead I use FFmpeg parser, inside JNI, where I memcopy the video frames into the input byte buffers and then queued the input buffer.</p>
  <p>But when I call <code>decoder.dequeueOutputBuffer(info, 10000)</code>:</p>
  <ul>
  <li>It returns <code>MediaCodec.INFO_TRY_AGAIN_LATER</code></li>
  <li>While it works fine if I use <code>extractor.readsample()</code></li>
  </ul>
  <p><strong>Java Side:</strong></p>
  <pre><code>import java.io.File;
  import java.io.FileNotFoundException;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.nio.ByteBuffer;
  
  import android.app.Activity;
  import android.media.MediaCodec;
  import android.media.MediaCodec.BufferInfo;
  import android.media.MediaExtractor;
  import android.media.MediaFormat;
  import android.os.Bundle;
  import android.os.Environment;
  import android.util.Log;
  import android.view.Surface;
  import android.view.SurfaceHolder;
  import android.view.SurfaceView;
  
  public class VideoBrowser extends Activity implements SurfaceHolder.Callback {
      private static final String SAMPLE = Environment.getExternalStorageDirectory() + &quot;/obama.mp4&quot;;
      private PlayerThread mPlayer = null;
      private static native &lt; jintArray &gt; int AVinitializecntxt(String strl, jintArray arr);
      private native int AVREADVIDEO(byte[] array);
      public int FLAG = 0;
      public int jk = 0;
      File f1;
      FileOutputStream f;
  
      static {
          Log.i(&quot;ABCD&quot;, &quot;BEFORE&quot;);
          System.loadLibrary(&quot;ffmpeg&quot;);
          System.loadLibrary(&quot;ffmpeg-test-jni&quot;);
          Log.i(&quot;ABCD&quot;, &quot;Success&quot;);
      }
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          SurfaceView sv = new SurfaceView(this);
          sv.getHolder().addCallback(this);
          setContentView(sv);
          int val;
          int[] array = new int[6];
          int END_OF_FILE = 0;
          int aud_stream = 0;
          int vid_stream = 0;
          String urlString = &quot;/mnt/sdcard/obama.mp4&quot;;
          f1 = new File(&quot;/mnt/sdcard/t.h264&quot;);
          try {
              f = new FileOutputStream(f1);
          } catch (FileNotFoundException e) {
              e.printStackTrace();
          }
          // This is where I call the function to initialize the ffmpeg inside JNI
          val = AVinitializecntxt(urlString, array);
          FLAG = val;
          Log.i(&quot;ABCD&quot;, &quot;FLAG : &quot; + FLAG + val);
      }
  
      protected void onDestroy() {
          super.onDestroy();
      }
  
      @Override
      public void surfaceCreated(SurfaceHolder holder) {}
  
      @Override
      public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
          if (mPlayer == null) {
              mPlayer = new PlayerThread(holder.getSurface());
              mPlayer.start();
          }
      }
  
      @Override
      public void surfaceDestroyed(SurfaceHolder holder) {
          if (mPlayer != null) {
              mPlayer.interrupt();
          }
      }
  
      private class PlayerThread extends Thread {
          private MediaExtractor extractor;
          private MediaCodec decoder;
          private Surface surface;
          // private VideoPlayer VideoPlayerAPIInterfaceClass = new VideoPlayer();
  
          public PlayerThread(Surface surface) {
              this.surface = surface;
          }
  
          @Override
          public void run() {
              if (FLAG == 1) {
                  extractor = new MediaExtractor();
                  extractor.setDataSource(SAMPLE);
                  for (int i = 0; i &lt; extractor.getTrackCount(); i++) {
                      MediaFormat format = extractor.getTrackFormat(i);
                      String mime = format.getString(MediaFormat.KEY_MIME);
                      if (mime.startsWith(&quot;video/&quot;)) {
                          extractor.selectTrack(i);
                          decoder = MediaCodec.createDecoderByType(&quot;video/avc&quot;);
                          // Log.i(&quot;ABCD&quot;, &quot;MIME : &quot; + mime);
                          decoder.configure(format, surface, null, 0);
                          break;
                      }
                  }
  
                  if (decoder == null) {
                      Log.e(&quot;DecodeActivity&quot;, &quot;Can't find video info!&quot;);
                      return;
                  }
  
                  decoder.start();
  
                  ByteBuffer[] inputBuffers = decoder.getInputBuffers();
                  ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
                  BufferInfo info = new BufferInfo();
                  boolean isEOS = false;
                  long startMs = System.currentTimeMillis();
                  int outIndex1 = -1;
  
                  while (outIndex1 &lt; 0) {
                      outIndex1 = decoder.dequeueOutputBuffer(info, 10000);
                      Log.i(&quot;ABCD&quot;, &quot;etgeuieoy&quot;);
                  }
  
                  while (!Thread.interrupted()) {
                      if (!isEOS) {
                          int inIndex = decoder.dequeueInputBuffer(10000);
                          if (inIndex &gt;= 0) {
                              ByteBuffer buffer = inputBuffers[inIndex];
                              // int sampleSize = extractor.readSampleData(buffer, 0);
                              byte[] bytes = new byte[buffer.capacity()];
                              // This is where we call JNI function to memcopy the encoded bitstream into the input buffer
                              int sampleSize = [b] AVREADVIDEO[/b](bytes);
                              buffer.clear(); buffer.put(bytes, 0, sampleSize);
                              if (sampleSize &lt; 0) {
                              // We shouldn't stop the playback at this point, just pass the EOS
                              // flag to decoder, we will get it again from the
                              // dequeueOutputBuffer
                              // Log.d(&quot;DecodeActivity&quot;, &quot;InputBuffer BUFFER_FLAG_END_OF_STREAM&quot;);
                                  decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                                  isEOS = true;
                              } else {
                                  decoder.queueInputBuffer(inIndex, 0, sampleSize, 0, 0);
                                  extractor.advance();
                              }
                           }
                       }
                       int outIndex = decoder.dequeueOutputBuffer(info, 10000);
                       switch (outIndex) {
                          case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                              Log.d(&quot;DecodeActivity&quot;, &quot;INFO_OUTPUT_BUFFERS_CHANGED&quot;);
                              outputBuffers = decoder.getOutputBuffers();
                              break;
                          case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                               Log.d(&quot;DecodeActivity&quot;, &quot;New format &quot; + decoder.getOutputFormat());
                               break;
                          case MediaCodec.INFO_TRY_AGAIN_LATER:
                               Log.d(&quot;DecodeActivity&quot;, &quot;dequeueOutputBuffer timed out!&quot;);
                               break;
                         default:
                              ByteBuffer buffer = outputBuffers[outIndex];
                              Log.v(&quot;DecodeActivity&quot;, &quot;We can't use this buffer but render it due to the API limit, &quot; + buffer);
                              // We use a very simple clock to keep the video FPS, or the video
                              // playback will be too fast
                              while (info.presentationTimeUs / 1000 &gt; System.currentTimeMillis() - startMs) {
                                  try {
                                      sleep(10);
                                  } catch (InterruptedException e) {
                                      e.printStackTrace();
                                      break;
                                  }
                              }
                              //  Log.i(&quot;ABCD&quot;, &quot;RELEASING OUTPUT BUFFER&quot;);
                              decoder.releaseOutputBuffer(outIndex, true);
                              //decoder.releaseOutputBuffer(outIndex, false);
                              break;
                          }
  
                          // All decoded frames have been rendered, we can stop playing now
                          if ((info.flags &amp; MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                              Log.d(&quot;DecodeActivity&quot;, &quot;OutputBuffer BUFFER_FLAG_END_OF_STREAM&quot;);
                              break;
                          }
                      }
  
                      decoder.stop();
                      decoder.release();
                      extractor.release();
                  }
              }
          }
      }
  }
  </code></pre>
  <p>JNI Side:</p>
  <pre><code>JNIEXPORT jint JNICALL
  Java_com_alldigital_videoplayer_VideoBrowser_AVREADVIDEO(JNIEnv * pEnv, 
      jobject pObj, jbyteArray array) {
  
      AV_ctxt * avctxt = &amp; aud_vid_ctxt;
      jbyte * buf = ( * pEnv) - &gt; GetByteArrayElements(pEnv, array, NULL);
      if (buf == NULL) {
          LOGERR(10, &quot;AVVIDEOREAD&quot;, &quot;Bytes null&quot;);
      }
  
      AVPacket * packet;
      packet = av_malloc(sizeof(AVPacket));
      av_init_packet(packet);
      int avread_res = av_read_frame(avctxt - &gt; gFormatCtx, packet);
      int size = packet - &gt; size;
      if (avread_res &gt;= 0) {
          if (packet - &gt; stream_index == avctxt - &gt; gVideoStreamIndex) {
              // packet-&gt;size,packet-&gt;
              if (NULL == memcpy(buf,(char * ) packet - &gt; data, packet - &gt; size))
                  LOGERR(10, &quot;AV_AUDIO_DECODE&quot;, &quot;memcpy for audio buffer failed&quot;);
              }
          }
          ( * pEnv) - &gt; ReleaseByteArrayElements(pEnv, array, buf, 0);
          av_free_packet(packet);
          packet = NULL;
          return size;
      }
  }
  </code></pre>
  <p>Even though I am copying the encoded data of each frame through FFmpeg without calling extractor, I am getting this outputbuffer timeout issue - why?</p>