title: How to properly pass an asset FileDescriptor to FFmpeg using JNI in Android
body: <p>I'm trying to retrieve metadata in Android using FFmpeg, JNI and a Java <a href="http://developer.android.com/reference/java/io/FileDescriptor.html" rel="noreferrer">FileDescriptor</a> and it isn't' working. I know FFmpeg supports the <a href="https://www.ffmpeg.org/ffmpeg-protocols.html#pipe" rel="noreferrer">pipe protocol</a> so I'm trying to emmulate: "<code>cat test.mp3 | ffmpeg i pipe:0</code>" programmatically. I use the following code to get a FileDescriptor from an asset bundled with the Android application:</p>
  
  <pre><code>FileDescriptor fd = getContext().getAssets().openFd("test.mp3").getFileDescriptor();
  setDataSource(fd, 0, 0x7ffffffffffffffL); // native function, shown below
  </code></pre>
  
  <p>Then, in my native (In C++) code I get the FileDescriptor by calling:</p>
  
  <pre><code>static void wseemann_media_FFmpegMediaMetadataRetriever_setDataSource(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
  {
      //...
  
      int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); // function contents show below
  
      //...
  }
  
  // function contents
  static int jniGetFDFromFileDescriptor(JNIEnv * env, jobject fileDescriptor) {
      jint fd = -1;
      jclass fdClass = env-&gt;FindClass("java/io/FileDescriptor");
  
      if (fdClass != NULL) {
          jfieldID fdClassDescriptorFieldID = env-&gt;GetFieldID(fdClass, "descriptor", "I");
          if (fdClassDescriptorFieldID != NULL &amp;&amp; fileDescriptor != NULL) {
              fd = env-&gt;GetIntField(fileDescriptor, fdClassDescriptorFieldID);
          }
      }
  
      return fd;
  }
  </code></pre>
  
  <p>I then pass the file descriptor pipe # (In C) to FFmpeg:</p>
  
  <pre><code>char path[256] = "";
  
  FILE *file = fdopen(fd, "rb");
  
  if (file &amp;&amp; (fseek(file, offset, SEEK_SET) == 0)) {
      char str[20];
      sprintf(str, "pipe:%d", fd);
      strcat(path, str);
  }
  
  State *state = av_mallocz(sizeof(State));
  state-&gt;pFormatCtx = NULL;
  
  if (avformat_open_input(&amp;state-&gt;pFormatCtx, path, NULL, &amp;options) != 0) { // Note: path is in the format "pipe:&lt;the FD #&gt;"
      printf("Metadata could not be retrieved\n");
      *ps = NULL;
      return FAILURE;
  }
  
  if (avformat_find_stream_info(state-&gt;pFormatCtx, NULL) &lt; 0) {
      printf("Metadata could not be retrieved\n");
      avformat_close_input(&amp;state-&gt;pFormatCtx);
      *ps = NULL;
      return FAILURE;
  }
  
  // Find the first audio and video stream
  for (i = 0; i &lt; state-&gt;pFormatCtx-&gt;nb_streams; i++) {
      if (state-&gt;pFormatCtx-&gt;streams[i]-&gt;codec-&gt;codec_type == AVMEDIA_TYPE_VIDEO &amp;&amp; video_index &lt; 0) {
          video_index = i;
      }
  
      if (state-&gt;pFormatCtx-&gt;streams[i]-&gt;codec-&gt;codec_type == AVMEDIA_TYPE_AUDIO &amp;&amp; audio_index &lt; 0) {
          audio_index = i;
      }
  
      set_codec(state-&gt;pFormatCtx, i);
  }
  
  if (audio_index &gt;= 0) {
      stream_component_open(state, audio_index);
  }
  
  if (video_index &gt;= 0) {
      stream_component_open(state, video_index);
  }
  
  printf("Found metadata\n");
  AVDictionaryEntry *tag = NULL;
  while ((tag = av_dict_get(state-&gt;pFormatCtx-&gt;metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
      printf("Key %s: \n", tag-&gt;key);
      printf("Value %s: \n", tag-&gt;value);
  }
  
  *ps = state;
  return SUCCESS;
  </code></pre>
  
  <p>My issue is <code>avformat_open_input</code> doesn't fail but it also doesn't let me retrieve any metadata or frames, The same code works if I use a regular file URI (e.g file://sdcard/test.mp3) as the path. What am I doing wrong? Thanks in advance.</p>
  
  <p><strong>Note</strong>: if you would like to look at all of the code I'm trying to solve the issue in order to provide this functionality for my library: <a href="https://github.com/wseemann/FFmpegMediaMetadataRetriever" rel="noreferrer">FFmpegMediaMetadataRetriever</a>.</p>