func: static int read_thread(void *arg)
  
  {
  
      VideoState *is = arg;
  
      AVFormatContext *ic = NULL;
  
      int err, i, ret;
  
      int st_index[AVMEDIA_TYPE_NB];
  
      AVPacket pkt1, *pkt = &pkt1;
  
      int eof = 0;
  
      int pkt_in_play_range = 0;
  
      AVDictionaryEntry *t;
  
      AVDictionary **opts;
  
      int orig_nb_streams;
  
      SDL_mutex *wait_mutex = SDL_CreateMutex();
  
  
  
      memset(st_index, -1, sizeof(st_index));
  
      is->last_video_stream = is->video_stream = -1;
  
      is->last_audio_stream = is->audio_stream = -1;
  
      is->last_subtitle_stream = is->subtitle_stream = -1;
  
  
  
      ic = avformat_alloc_context();
  
      ic->interrupt_callback.callback = decode_interrupt_cb;
  
      ic->interrupt_callback.opaque = is;
  
      err = avformat_open_input(&ic, is->filename, is->iformat, &format_opts);
  
      if (err < 0) {
  
          print_error(is->filename, err);
  
          ret = -1;
  
          goto fail;
  
      }
  
      if ((t = av_dict_get(format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
  
          av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
  
          ret = AVERROR_OPTION_NOT_FOUND;
  
          goto fail;
  
      }
  
      is->ic = ic;
  
  
  
      if (genpts)
  
          ic->flags |= AVFMT_FLAG_GENPTS;
  
  
  
      opts = setup_find_stream_info_opts(ic, codec_opts);
  
      orig_nb_streams = ic->nb_streams;
  
  
  
      err = avformat_find_stream_info(ic, opts);
  
      if (err < 0) {
  
          fprintf(stderr, "%s: could not find codec parameters\n", is->filename);
  
          ret = -1;
  
          goto fail;
  
      }
  
      for (i = 0; i < orig_nb_streams; i++)
  
          av_dict_free(&opts[i]);
  
      av_freep(&opts);
  
  
  
      if (ic->pb)
  
          ic->pb->eof_reached = 0; // FIXME hack, ffplay maybe should not use url_feof() to test for the end
  
  
  
      if (seek_by_bytes < 0)
  
          seek_by_bytes = !!(ic->iformat->flags & AVFMT_TS_DISCONT);
  
  
  
      is->max_frame_duration = (ic->iformat->flags & AVFMT_TS_DISCONT) ? 10.0 : 3600.0;
  
  
  
      /* if seeking requested, we execute it */
  
      if (start_time != AV_NOPTS_VALUE) {
  
          int64_t timestamp;
  
  
  
          timestamp = start_time;
  
          /* add the stream start time */
  
          if (ic->start_time != AV_NOPTS_VALUE)
  
              timestamp += ic->start_time;
  
          ret = avformat_seek_file(ic, -1, INT64_MIN, timestamp, INT64_MAX, 0);
  
          if (ret < 0) {
  
              fprintf(stderr, "%s: could not seek to position %0.3f\n",
  
                      is->filename, (double)timestamp / AV_TIME_BASE);
  
          }
  
      }
  
  
  
      is->realtime = is_realtime(ic);
  
  
  
      for (i = 0; i < ic->nb_streams; i++)
  
          ic->streams[i]->discard = AVDISCARD_ALL;
  
      if (!video_disable)
  
          st_index[AVMEDIA_TYPE_VIDEO] =
  
              av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO,
  
                                  wanted_stream[AVMEDIA_TYPE_VIDEO], -1, NULL, 0);
  
      if (!audio_disable)
  
          st_index[AVMEDIA_TYPE_AUDIO] =
  
              av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO,
  
                                  wanted_stream[AVMEDIA_TYPE_AUDIO],
  
                                  st_index[AVMEDIA_TYPE_VIDEO],
  
                                  NULL, 0);
  
      if (!video_disable)
  
          st_index[AVMEDIA_TYPE_SUBTITLE] =
  
              av_find_best_stream(ic, AVMEDIA_TYPE_SUBTITLE,
  
                                  wanted_stream[AVMEDIA_TYPE_SUBTITLE],
  
                                  (st_index[AVMEDIA_TYPE_AUDIO] >= 0 ?
  
                                   st_index[AVMEDIA_TYPE_AUDIO] :
  
                                   st_index[AVMEDIA_TYPE_VIDEO]),
  
                                  NULL, 0);
  
      if (show_status) {
  
          av_dump_format(ic, 0, is->filename, 0);
  
      }
  
  
  
      is->show_mode = show_mode;
  
  
  
      /* open the streams */
  
      if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) {
  
          stream_component_open(is, st_index[AVMEDIA_TYPE_AUDIO]);
  
      }
  
  
  
      ret = -1;
  
      if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
  
          ret = stream_component_open(is, st_index[AVMEDIA_TYPE_VIDEO]);
  
      }
  
      if (is->show_mode == SHOW_MODE_NONE)
  
          is->show_mode = ret >= 0 ? SHOW_MODE_VIDEO : SHOW_MODE_RDFT;
  
  
  
      is->refresh_tid = SDL_CreateThread(refresh_thread, is);
  
  
  
      if (st_index[AVMEDIA_TYPE_SUBTITLE] >= 0) {
  
          stream_component_open(is, st_index[AVMEDIA_TYPE_SUBTITLE]);
  
      }
  
  
  
      if (is->video_stream < 0 && is->audio_stream < 0) {
  
          fprintf(stderr, "%s: could not open codecs\n", is->filename);
  
          ret = -1;
  
          goto fail;
  
      }
  
  
  
      if (infinite_buffer < 0 && is->realtime)
  
          infinite_buffer = 1;
  
  
  
      for (;;) {
  
          if (is->abort_request)
  
              break;
  
          if (is->paused != is->last_paused) {
  
              is->last_paused = is->paused;
  
              if (is->paused)
  
                  is->read_pause_return = av_read_pause(ic);
  
              else
  
                  av_read_play(ic);
  
          }
  
  #if CONFIG_RTSP_DEMUXER || CONFIG_MMSH_PROTOCOL
  
          if (is->paused &&
  
                  (!strcmp(ic->iformat->name, "rtsp") ||
  
                   (ic->pb && !strncmp(input_filename, "mmsh:", 5)))) {
  
              /* wait 10 ms to avoid trying to get another packet */
  
              /* XXX: horrible */
  
              SDL_Delay(10);
  
              continue;
  
          }
  
  #endif
  
          if (is->seek_req) {
  
              int64_t seek_target = is->seek_pos;
  
              int64_t seek_min    = is->seek_rel > 0 ? seek_target - is->seek_rel + 2: INT64_MIN;
  
              int64_t seek_max    = is->seek_rel < 0 ? seek_target - is->seek_rel - 2: INT64_MAX;
  
  // FIXME the +-2 is due to rounding being not done in the correct direction in generation
  
  //      of the seek_pos/seek_rel variables
  
  
  
              ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags);
  
              if (ret < 0) {
  
                  fprintf(stderr, "%s: error while seeking\n", is->ic->filename);
  
              } else {
  
                  if (is->audio_stream >= 0) {
  
                      packet_queue_flush(&is->audioq);
  
                      packet_queue_put(&is->audioq, &flush_pkt);
  
                  }
  
                  if (is->subtitle_stream >= 0) {
  
                      packet_queue_flush(&is->subtitleq);
  
                      packet_queue_put(&is->subtitleq, &flush_pkt);
  
                  }
  
                  if (is->video_stream >= 0) {
  
                      packet_queue_flush(&is->videoq);
  
                      packet_queue_put(&is->videoq, &flush_pkt);
  
                  }
  
                  if (is->seek_flags & AVSEEK_FLAG_BYTE) {
  
                     //FIXME: use a cleaner way to signal obsolete external clock...
  
                     update_external_clock_pts(is, (double)AV_NOPTS_VALUE);
  
                  } else {
  
                     update_external_clock_pts(is, seek_target / (double)AV_TIME_BASE);
  
                  }
  
              }
  
              is->seek_req = 0;
  
              eof = 0;
  
              if (is->paused)
  
                  step_to_next_frame(is);
  
          }
  
          if (is->queue_attachments_req) {
  
              avformat_queue_attached_pictures(ic);
  
              is->queue_attachments_req = 0;
  
          }
  
  
  
          /* if the queue are full, no need to read more */
  
          if (infinite_buffer<1 &&
  
                (is->audioq.size + is->videoq.size + is->subtitleq.size > MAX_QUEUE_SIZE
  
              || (   (is->audioq   .nb_packets > MIN_FRAMES || is->audio_stream < 0 || is->audioq.abort_request)
  
                  && (is->videoq   .nb_packets > MIN_FRAMES || is->video_stream < 0 || is->videoq.abort_request)
  
                  && (is->subtitleq.nb_packets > MIN_FRAMES || is->subtitle_stream < 0 || is->subtitleq.abort_request)))) {
  
              /* wait 10 ms */
  
              SDL_LockMutex(wait_mutex);
  
              SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);
  
              SDL_UnlockMutex(wait_mutex);
  
              continue;
  
          }
  
          if (eof) {
  
              if (is->video_stream >= 0) {
  
                  av_init_packet(pkt);
  
                  pkt->data = NULL;
  
                  pkt->size = 0;
  
                  pkt->stream_index = is->video_stream;
  
                  packet_queue_put(&is->videoq, pkt);
  
              }
  
              if (is->audio_stream >= 0 &&
  
                  is->audio_st->codec->codec->capabilities & CODEC_CAP_DELAY) {
  
                  av_init_packet(pkt);
  
                  pkt->data = NULL;
  
                  pkt->size = 0;
  
                  pkt->stream_index = is->audio_stream;
  
                  packet_queue_put(&is->audioq, pkt);
  
              }
  
              SDL_Delay(10);
  
              if (is->audioq.size + is->videoq.size + is->subtitleq.size == 0) {
  
                  if (loop != 1 && (!loop || --loop)) {
  
                      stream_seek(is, start_time != AV_NOPTS_VALUE ? start_time : 0, 0, 0);
  
                  } else if (autoexit) {
  
                      ret = AVERROR_EOF;
  
                      goto fail;
  
                  }
  
              }
  
              eof=0;
  
              continue;
  
          }
  
          ret = av_read_frame(ic, pkt);
  
          if (ret < 0) {
  
              if (ret == AVERROR_EOF || url_feof(ic->pb))
  
                  eof = 1;
  
              if (ic->pb && ic->pb->error)
  
                  break;
  
              SDL_LockMutex(wait_mutex);
  
              SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);
  
              SDL_UnlockMutex(wait_mutex);
  
              continue;
  
          }
  
          /* check if packet is in play range specified by user, then queue, otherwise discard */
  
          pkt_in_play_range = duration == AV_NOPTS_VALUE ||
  
                  (pkt->pts - ic->streams[pkt->stream_index]->start_time) *
  
                  av_q2d(ic->streams[pkt->stream_index]->time_base) -
  
                  (double)(start_time != AV_NOPTS_VALUE ? start_time : 0) / 1000000
  
                  <= ((double)duration / 1000000);
  
          if (pkt->stream_index == is->audio_stream && pkt_in_play_range) {
  
              packet_queue_put(&is->audioq, pkt);
  
          } else if (pkt->stream_index == is->video_stream && pkt_in_play_range) {
  
              packet_queue_put(&is->videoq, pkt);
  
          } else if (pkt->stream_index == is->subtitle_stream && pkt_in_play_range) {
  
              packet_queue_put(&is->subtitleq, pkt);
  
          } else {
  
              av_free_packet(pkt);
  
          }
  
      }
  
      /* wait until the end */
  
      while (!is->abort_request) {
  
          SDL_Delay(100);
  
      }
  
  
  
      ret = 0;
  
   fail:
  
      /* close each stream */
  
      if (is->audio_stream >= 0)
  
          stream_component_close(is, is->audio_stream);
  
      if (is->video_stream >= 0)
  
          stream_component_close(is, is->video_stream);
  
      if (is->subtitle_stream >= 0)
  
          stream_component_close(is, is->subtitle_stream);
  
      if (is->ic) {
  
          avformat_close_input(&is->ic);
  
      }
  
  
  
      if (ret != 0) {
  
          SDL_Event event;
  
  
  
          event.type = FF_QUIT_EVENT;
  
          event.user.data1 = is;
  
          SDL_PushEvent(&event);
  
      }
  
      SDL_DestroyMutex(wait_mutex);
  
      return 0;
  
  }
  