//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2011 by PDSoft (Attila Padar)                *
//*                http://mpxplay.sourceforge.net                          *
//*                  email: mpxplay@freemail.hu                            *
//**************************************************************************
//*  This program is distributed in the hope that it will be useful,       *
//*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *
//*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                  *
//*  Please contact with the author (with me) if you want to use           *
//*  or modify this source.                                                *
//**************************************************************************
//function: MKV/WEBM,MPG/VOB,TS/MTS file handling/demuxing
//using the part of FFMPEG library (ff_mkv,ff_mpg,ff_ts,ffutils)

//#define MPXPLAY_USE_DEBUGF 1
#define MPXPLAY_DEBUG_OUTPUT NULL //stdout

#include "mpxplay.h"

#ifdef MPXPLAY_LINK_INFILE_FFMPG

#include "ffutils.h"

#ifdef MPXPLAY_USE_DEBUGF
 //#define INFFMPG_DEBUG_HEADER 1
#endif

#define INFFMPG_PTS_CHECK_NUM_MAX 12

typedef struct ffmpg_demuxer_data_s{
 unsigned int audio_stream_index;
 unsigned int nb_audio_streams;
 AVStream *audio_stream_data;
 AVPacket pkt;
 AVFormatContext fctx;
}ffmpg_demuxer_data_s;

extern AVInputFormat mpxplay_ff_matroska_demuxer;
extern AVInputFormat mpxplay_ff_mpegps_demuxer;
extern AVInputFormat mpxplay_ff_mpegts_demuxer;
extern AVInputFormat mpxplay_ff_flv_demuxer;

static long inffmpg_get_pts_from_filepos(struct ffmpg_demuxer_data_s *ffmpi,int64_t filepos,int64_t *pts_vals,unsigned int index,unsigned int lastindex)
{
 AVFormatContext *ic=&ffmpi->fctx;
 AVPacket *pkt=&ffmpi->pkt;
 long res,retry,retryrp;
#ifdef INFFMPG_DEBUG_HEADER
 long counter=0;
#endif

 ic->fbfs->fseek(ic->fbds,filepos,SEEK_SET);
 if(index==lastindex)
  retry=10000;
 else
  retry=1000;
 retryrp=2;
 do{
  pkt->stream_index=100;
  pkt->size=0;
#ifdef INFFMPG_DEBUG_HEADER
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"%4d read_packet fp:%"PRIi64"",retry,(mpxp_int64_t)filepos);
#endif
  if((res=ic->iformat->read_packet(ic,pkt))<0)
   if((res==AVERROR(EIO)) || !(--retryrp))
    return res;
  if(((pkt->pts>0) || (pkt->stream_index==ffmpi->audio_stream_index)) && pkt->size){ // not exact, but faster if we use the pts of other stream
#ifdef INFFMPG_DEBUG_HEADER
   if(pkt->pts>=0)
    mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"%5.5d i:%d si:%d fsi:%d pts:%"PRIi64" dts:%"PRIi64" fp:%"PRIi64"",
     counter++,index,pkt->stream_index,ffmpi->audio_stream_index,pkt->pts,pkt->dts,(mpxp_int64_t)filepos);
#endif
   if(index==lastindex){
    if((pkt->pts>=0) && (pkt->pts>pts_vals[index]))
     pts_vals[index]=pkt->pts;
   }else{
    if(pkt->pts>=0){
     pts_vals[index]=pkt->pts;
     break;
    }
   }
  }
 }while(--retry);
 if(!retry)
  return -1;
 return 0;
}

//it reads from all streams, not only a specified audio stream! (this is faster, but not exact)
static int64_t inffmpg_get_stream_duration_pts(struct ffmpg_demuxer_data_s *ffmpi,struct mpxplay_infile_info_s *miis,unsigned int checknum)
{
 AVFormatContext *ic=&ffmpi->fctx;
 long i;
 mpxp_filesize_t oldfilepos=-1,filepos,lastblocksize=0;
 float diff1,diff2;
 int64_t duration=0,pts_vals[INFFMPG_PTS_CHECK_NUM_MAX];

 oldfilepos=ic->fbfs->ftell(ic->fbds);

 do{
  pds_memset(&pts_vals[0],0,sizeof(pts_vals));
  filepos=0;
  for(i=0;i<checknum;i++){
   if(inffmpg_get_pts_from_filepos(ffmpi,filepos,&pts_vals[0],i,(checknum-1))<0)
    break;
   if(!i){ // calculate last block checksize from the first blocksize
    lastblocksize=max(INFFMPG_INITIAL_PACKETBUF_SIZE,ic->fbfs->ftell(ic->fbds)*2);
    if(lastblocksize>(miis->filesize/2))
     lastblocksize=miis->filesize/2;
   }
   if(i==(checknum-2)) // last block follows
    filepos=miis->filesize-lastblocksize;
   else
    filepos+=miis->filesize/(checknum-1);
#ifdef INFFMPG_DEBUG_HEADER
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"c:%d i:%d pts:%"PRIi64" fp:%d",checknum,i,pts_vals[i],(long)filepos);
#endif
  }

  if(checknum<3){ // TS files
   if(pts_vals[1]>pts_vals[0]){ // usually have to be
    duration=pts_vals[1]-pts_vals[0];
    break;
   } // invalid pts vals, search like MPG
   checknum=3;
  }else{ // MPG/VOB files (search for two sequential block with similar length)
   diff1=0;
   for(i=0;i<(checknum-1);i++){
    if(pts_vals[i+1]<pts_vals[i])
     diff1=0;
    else if(diff1==0)
     diff1=(float)pts_vals[i+1]-(float)pts_vals[i];
    else{
     diff2=pts_vals[i+1]-pts_vals[i];
     if(((diff1*1.4)>diff2) && ((diff1*0.6)<diff2)){ // max 40% difference between the length of block1 and block2
      if(checknum==3){
       duration=pts_vals[2]-pts_vals[0];
      }else{
       if(diff2>diff1)
        diff1=diff2;
       duration=(diff1*(float)(checknum-1));
      }
      goto err_out_durpts;
     }else
      diff1=diff2;
    }
   }
  }

  if(checknum>(INFFMPG_PTS_CHECK_NUM_MAX/2)){ // last pass, block-search failed
   if(pts_vals[1]>pts_vals[0]){
    for(i=1;i<(checknum-1);i++){
     if(pts_vals[i+1]<pts_vals[i]){ // pts[i] is the last valid pts, we calculate the duration from this value
      duration=(((float)pts_vals[i]-(float)pts_vals[0])*(float)(checknum-1)/(float)i);
      goto err_out_durpts;
     }
    }
    if(i==(checknum-1))
     duration=pts_vals[checknum-1]-pts_vals[0];
   }
  }
  checknum<<=1; // 3,6,12
 }while(checknum<=INFFMPG_PTS_CHECK_NUM_MAX);

err_out_durpts:

 duration=(int64_t)((float)duration*(float)AV_TIME_BASE/90000.0); // !!! MPEG like to ns

 ic->fbfs->fseek(ic->fbds,oldfilepos,SEEK_SET);

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"sp0:%"PRIi64" sp1:%"PRIi64" ep:%"PRIi64" d:%"PRIi64"",pts_vals[0],pts_vals[1],pts_vals[checknum-1],duration);
 return duration;
}

static void inffmpg_sort_streams_by_stid(struct ffmpg_demuxer_data_s *ffmpi)
{
 AVFormatContext *ic=&ffmpi->fctx;
 unsigned int i,j;
 if(ic->nb_streams<=1)
  return;
 for(j=0;j<(ic->nb_streams-1);j++){
  for(i=0;i<(ic->nb_streams-1);i++){
   AVStream *st1=ic->streams[i];
   AVStream *st2=ic->streams[i+1];
   if(st1->id>st2->id){
    st2->index=i;
    st1->index=i+1;
    ic->streams[i]=st2;
    ic->streams[i+1]=st1;
   }
  }
 }
}

static void inffmpg_search_audio_stream(struct ffmpg_demuxer_data_s *ffmpi,struct mpxplay_streampacket_info_s *spi)
{
 AVFormatContext *ic=&ffmpi->fctx;
 unsigned int i;

 ffmpi->nb_audio_streams=0;

 for(i=0;i<ic->nb_streams;i++){
  AVStream *st=ic->streams[i];
  AVCodecContext *codec=st->codec;
  if(codec && (codec->codec_type==CODEC_TYPE_AUDIO)){
   if(ffmpi->nb_audio_streams<=spi->stream_select){
    ffmpi->audio_stream_index=i;
    ffmpi->audio_stream_data=st;
   }
   ffmpi->nb_audio_streams++;
  }
 }
#ifdef INFFMPG_DEBUG_HEADER
  for(i=0;i<ic->nb_streams;i++){
   AVStream *st=ic->streams[i];
   AVCodecContext *codec=st->codec;
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"s:%d id:%d c:%8.8X",i,st->id,((codec)? codec->codec_id:0));
  }
#endif
}

static void inffmpg_codecid_to_waveid(struct ffmpg_demuxer_data_s *ffmpi,struct mpxplay_infile_info_s *miis)
{
 struct mpxplay_streampacket_info_s *spi=miis->audio_stream;
 struct mpxplay_audio_decoder_info_s *adi=miis->audio_decoder_infos;
 AVStream *st=ffmpi->audio_stream_data;
 AVCodecContext *codec;

 if(!st)
  return;
 codec=st->codec;

 switch(codec->codec_id){
  case CODEC_ID_PCM_S8:spi->wave_id=MPXPLAY_WAVEID_PCM_SLE;adi->bits=8;break;
  case CODEC_ID_PCM_S16LE:spi->wave_id=MPXPLAY_WAVEID_PCM_SLE;adi->bits=16;break;
  case CODEC_ID_PCM_S24LE:spi->wave_id=MPXPLAY_WAVEID_PCM_SLE;adi->bits=24;break;
  case CODEC_ID_PCM_S32LE:spi->wave_id=MPXPLAY_WAVEID_PCM_SLE;adi->bits=32;break;
  case CODEC_ID_PCM_S16BE:spi->wave_id=MPXPLAY_WAVEID_PCM_SBE;adi->bits=16;break;
  case CODEC_ID_PCM_S24BE:spi->wave_id=MPXPLAY_WAVEID_PCM_SBE;adi->bits=24;break;
  case CODEC_ID_PCM_S32BE:spi->wave_id=MPXPLAY_WAVEID_PCM_SBE;adi->bits=32;break;
  case CODEC_ID_PCM_DVD:spi->wave_id=MPXPLAY_WAVEID_PCM_DVD;adi->bits=codec->bits_per_coded_sample;break;
  case CODEC_ID_PCM_F32LE:spi->wave_id=MPXPLAY_WAVEID_PCM_FLOAT;adi->bits=1;break;
  case CODEC_ID_AAC:spi->wave_id=MPXPLAY_WAVEID_AAC;break;
  case CODEC_ID_AAC_LATM:spi->wave_id=MPXPLAY_WAVEID_LATMAAC;break;
  case CODEC_ID_AC3:spi->wave_id=MPXPLAY_WAVEID_AC3;break;
  case CODEC_ID_DTS:spi->wave_id=MPXPLAY_WAVEID_DTS;break;
  case CODEC_ID_MP2:spi->wave_id=MPXPLAY_WAVEID_MP2;break;
  case CODEC_ID_MP3:spi->wave_id=MPXPLAY_WAVEID_MP3;break;
  case CODEC_ID_VORBIS:spi->wave_id=MPXPLAY_WAVEID_VORBIS;break;
  case CODEC_ID_WMAV1:spi->wave_id=MPXPLAY_WAVEID_WMAV1;break;
  case CODEC_ID_WMAV2:spi->wave_id=MPXPLAY_WAVEID_WMAV2;break;
  case CODEC_ID_FLAC:spi->wave_id=MPXPLAY_WAVEID_FLAC;break;
  case CODEC_ID_SPEEX:spi->wave_id=MPXPLAY_WAVEID_SPEEX;break;
  case CODEC_ID_OPUS:spi->wave_id=MPXPLAY_WAVEID_OPUS;break;
 }
 if(codec->sample_rate)
  adi->freq=codec->sample_rate;
 if(codec->channels)
  adi->filechannels=adi->outchannels=codec->channels;
 if(codec->bit_rate && (codec->bit_rate<9999000) && (codec->codec_id>=CODEC_ID_MP2))
  adi->bitrate=codec->bit_rate/1000;
 spi->extradata=codec->extradata;
 spi->extradata_size=codec->extradata_size;
}

static void inffmpg_calculate_length(struct ffmpg_demuxer_data_s *ffmpi,struct mpxplay_infile_info_s *miis)
{
 AVFormatContext *ic=&ffmpi->fctx;
 struct mpxplay_streampacket_info_s *spi=miis->audio_stream;
 struct mpxplay_audio_decoder_info_s *adi=miis->audio_decoder_infos;

#ifdef INFFMPG_DEBUG_HEADER
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"calculate length");
#endif

 if((ic->duration<=0) && (spi->stream_select<ffmpi->nb_audio_streams)) // !!! hack to execute or not get_stream_duration (to search for more streams)
  ic->duration=((float)miis->timemsec*AV_TIME_BASE/MPXPLAY_TIME_BASE);
 if(ic->duration<=0){
  if(ic->iformat==&mpxplay_ff_mpegts_demuxer)
   ic->duration=inffmpg_get_stream_duration_pts(ffmpi,miis,2);
  else if(ic->iformat==&mpxplay_ff_mpegps_demuxer)
   ic->duration=inffmpg_get_stream_duration_pts(ffmpi,miis,3);
 }
 if(ic->duration>0)
  miis->timemsec=ic->duration/(AV_TIME_BASE/MPXPLAY_TIME_BASE); // ns->ms
 else if(miis->audio_stream->wave_id && (!(ic->flags&ADFMT_FLAG_AUTODETECT) || (adi->freq && adi->filechannels))){
  ic->duration=240*AV_TIME_BASE; // !!! hack (4 minutes fix value)
  miis->timemsec=240*MPXPLAY_TIME_BASE;
 }
}

static int inffmpg_assign_values_audio(struct ffmpg_demuxer_data_s *ffmpi,struct mpxplay_infile_info_s *miis,mpxp_uint32_t openmode)
{
 struct mpxplay_streampacket_info_s *spi=miis->audio_stream;
 struct mpxplay_audio_decoder_info_s *adi=miis->audio_decoder_infos;
 AVFormatContext *ic=&ffmpi->fctx;

 if(ic->iformat==&mpxplay_ff_mpegps_demuxer) // we should sort TS too, but in some cases it's wrong (joined different streams, possible same program-id)
  inffmpg_sort_streams_by_stid(ffmpi);
 inffmpg_search_audio_stream(ffmpi,spi);
 inffmpg_codecid_to_waveid(ffmpi,miis);
 if(openmode&MPXPLAY_INFILE_OPENMODE_INFO_LENGTH)
  inffmpg_calculate_length(ffmpi,miis);

 if((ic->iformat==&mpxplay_ff_mpegps_demuxer) || (ic->iformat==&mpxplay_ff_mpegts_demuxer)){ // maybe we've found new streams in get_stream_duration_pts()
  if(ic->iformat==&mpxplay_ff_mpegps_demuxer)
   inffmpg_sort_streams_by_stid(ffmpi);
  inffmpg_search_audio_stream(ffmpi,spi);
  inffmpg_codecid_to_waveid(ffmpi,miis);
 }

 if(!miis->timemsec)
  return 0;

 spi->streamtype=MPXPLAY_SPI_STREAMTYPE_AUDIO;
 if(openmode&MPXPLAY_INFILE_OPENMODE_INFO_DECODER){
  funcbit_enable(spi->flags,(MPXPLAY_SPI_FLAG_NEED_DECODER|MPXPLAY_SPI_FLAG_NEED_PARSING|MPXPLAY_SPI_FLAG_CONTAINER));
  if(adi->infobits&ADI_CNTRLBIT_BITSTREAMOUT)
   funcbit_enable(adi->infobits,(ADI_FLAG_BITSTREAMOUT|ADI_FLAG_BITSTREAMNOFRH));
 }
#ifdef MPXPLAY_WIN32
 spi->bs_framesize=INFFMPG_INITIAL_PACKETBUF_SIZE;
#else
 spi->bs_framesize=65536; // !!! have to be INFFMPG_INITIAL_PACKETBUF_SIZE;
#endif

#ifdef INFFMPG_DEBUG_HEADER
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"ns:%d freq:%d chans:%d bits:%d duration:%d br:%d wid:%8.8X",
  ic->nb_streams,adi->freq,adi->outchannels,adi->bits,(long)miis->timemsec,adi->bitrate,spi->wave_id);
#endif

 return 1;
}

static int inffmpg_infile_check(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,char *filename,struct mpxplay_infile_info_s *miis,AVInputFormat *demuxer,mpxp_uint32_t openmode)
{
 struct ffmpg_demuxer_data_s *ffmpi;
 AVFormatContext *ic;

 if(!fbfs->fopen(fbds,filename,O_RDONLY|O_BINARY,8192))
  return MPXPLAY_ERROR_INFILE_FILEOPEN;

 miis->filesize=fbfs->filelength(fbds);
 if(miis->filesize<=44)
  goto err_out_chk;

 ffmpi=pds_calloc(1,sizeof(struct ffmpg_demuxer_data_s));
 if(!ffmpi)
  goto err_out_chk;
 miis->private_data=ffmpi;

 ffmpi->fctx.fbfs=fbfs;
 ffmpi->fctx.fbds=fbds;

 ic=&ffmpi->fctx;
 ic->iformat=demuxer;
 if(openmode&MPXPLAY_INFILE_OPENMODE_AUTODETECT)
  funcbit_enable(ic->flags,ADFMT_FLAG_AUTODETECT);
 if(!(openmode&MPXPLAY_INFILE_OPENMODE_LOAD_SEEKTAB))
  funcbit_enable(ic->flags,ADFMT_FLAG_LOADHEADONLY);

 if(ic->iformat->priv_data_size>0){
  ic->priv_data=pds_calloc(1,ic->iformat->priv_data_size);
  if(!ic->priv_data)
   goto err_out_chk;
 }

 funcbit_enable(ic->flags,ADFMT_FLAG_PARSESTATUS);

 if(mpxplay_av_new_packet(&ffmpi->pkt,INFFMPG_INITIAL_PACKETBUF_SIZE)!=0)
  goto err_out_chk;
 if(ic->iformat->read_header(ic,&ffmpi->pkt)<0)
  goto err_out_chk;
 if(!ic->nb_streams)
  goto err_out_chk;
 if(!inffmpg_assign_values_audio(ffmpi,miis,openmode))
  goto err_out_chk;

 funcbit_disable(ic->flags,ADFMT_FLAG_PARSESTATUS);

 return MPXPLAY_ERROR_INFILE_OK;

err_out_chk:
 return MPXPLAY_ERROR_INFILE_CANTOPEN;
}

static void FFMPG_infile_close(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis)
{
 struct ffmpg_demuxer_data_s *ffmpi=miis->private_data;
#ifdef INFFMPG_DEBUG_HEADER
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"ffmpg infile close %8.8X",(long)ffmpi);
#endif
 if(ffmpi){
  mpxplay_av_close_input_stream(&ffmpi->fctx);
  if(ffmpi->pkt.data)
   pds_free(ffmpi->pkt.data);
  pds_free(ffmpi);
 }
 fbfs->fclose(fbds);
#ifdef INFFMPG_DEBUG_HEADER
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"ffmpg infile close end");
#endif
}

static int FFMPG_infile_demux(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis)
{
 struct ffmpg_demuxer_data_s *ffmpi=miis->private_data;
 struct mpxplay_streampacket_info_s *spi=miis->audio_stream;
 AVFormatContext *ic=&ffmpi->fctx;
 unsigned int retry=100;
 int retcode;

 ic->fbfs=fbfs;
 ic->fbds=fbds;

 do{
  ffmpi->pkt.stream_index=100;
  ffmpi->pkt.size=0;
  retcode=ic->iformat->read_packet(ic,&ffmpi->pkt);
  if(retcode<0){
   //if((retcode!=-21) && (retcode!=-31) && (retcode!=-18)){
    //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"rpr: %d",retcode);
   //}
   if((retcode==AVERROR(EINVAL)) || (retcode==AVERROR(EAGAIN)))
    continue;
   if(fbfs->eof(fbds))
    retcode=MPXPLAY_ERROR_INFILE_EOF;
   else if(retcode==AVERROR(ESPIPE))
    retcode=MPXPLAY_ERROR_INFILE_SYNC_IN;
   else
    retcode=MPXPLAY_ERROR_INFILE_NODATA;
   goto err_out_demux;
  }
  //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"si:%d asi:%d pks:%d bs:%d",ffmpi->pkt.stream_index,ffmpi->audio_stream_index,ffmpi->pkt.size,spi->bs_bufsize);
  if(ffmpi->pkt.stream_index==ffmpi->audio_stream_index)
   break;
 }while(--retry);
 if(!retry){
  retcode=MPXPLAY_ERROR_INFILE_SYNC_IN;
  goto err_out_demux;
 }

 if(!ffmpi->pkt.size || !ffmpi->pkt.data || (ffmpi->pkt.size>spi->bs_bufsize)){
  retcode=MPXPLAY_ERROR_INFILE_NODATA;
  goto err_out_demux;
 }

 spi->bs_leftbytes=ffmpi->pkt.size;
 pds_memcpy(spi->bitstreambuf,ffmpi->pkt.data,spi->bs_leftbytes);
 retcode=MPXPLAY_ERROR_INFILE_OK;

err_out_demux:

 /*if((retcode<0) && (retcode!=-1) && (retcode!=-4)){
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"rtc: %d",retcode);
 }*/

 return retcode;
}

static void INFFMPG_clearbuffs(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis,unsigned int seektype)
{
 struct ffmpg_demuxer_data_s *ffmpi=(struct ffmpg_demuxer_data_s *)miis->private_data;
 ffmpi->fctx.fbds=fbds;
}

static long FFMPG_infile_fseek(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis,long newmpxframenum)
{
 struct ffmpg_demuxer_data_s *ffmpi=miis->private_data;
 AVFormatContext *ic=&ffmpi->fctx;
 mpxp_filesize_t newfilepos;
 mpxp_int64_t newtimepos;

 ic->fbfs=fbfs;
 ic->fbds=fbds;

 if(ic->iformat->read_seek){
  int flags=0;
  if(miis->seektype&MPX_SEEKTYPE_BACKWARD)
   funcbit_enable(flags,AVSEEK_FLAG_BACKWARD);
  if(miis->seektype&MPX_SEEKTYPE_RELATIVE)
   funcbit_enable(flags,AVSEEK_FLAG_RELATIVE);
  newtimepos=(int64_t)((float)miis->timemsec*(float)newmpxframenum/(float)miis->allframes*(AV_TIME_BASE/MPXPLAY_TIME_BASE));
  newtimepos=ic->iformat->read_seek(ic,ffmpi->audio_stream_index,newtimepos,flags);
  if(newtimepos<0)
   return MPXPLAY_ERROR_INFILE_EOF;
  newmpxframenum=(long)((float)newtimepos*MPXPLAY_TIME_BASE/AV_TIME_BASE*(float)miis->allframes/(float)miis->timemsec);
 }else{
  newtimepos=(int64_t)((float)miis->timemsec*(float)newmpxframenum/(float)miis->allframes);
  if(miis->seektype&MPX_SEEKTYPE_RELATIVE){
   if(miis->seektype&MPX_SEEKTYPE_BACKWARD){
    newtimepos-=1*MPXPLAY_TIME_BASE;
    if(newtimepos<0)
     newtimepos=0;
   }else
    newtimepos+=1*MPXPLAY_TIME_BASE;
  }
  newfilepos=(mpxp_filesize_t)((float)newtimepos*(float)miis->filesize/(float)miis->timemsec);
  newfilepos=fbfs->fseek(fbds,newfilepos,SEEK_SET);
  if(newfilepos<0)
   return MPXPLAY_ERROR_INFILE_EOF;
  newmpxframenum=(long)((float)newfilepos*(float)miis->allframes/(float)miis->filesize);
 }

 return newmpxframenum;
}

//-------------------------------------------------------------------
/*static char *inffmpg_tag_get_str(struct mpxplay_infile_info_s *miis,char *str,char **id3ip,char *id3p)
{
 unsigned int len;
 if(str[0]){
  len=miis->textconv_func(MPXPLAY_TEXTCONV_TYPES_PUT(MPXPLAY_TEXTCONV_TYPE_UTF8,MPXPLAY_TEXTCONV_TYPE_MPXPNATIVE),
           str,510,id3p,MPXPLAY_TEXTCONV_MAX_ID3LEN/2);
  if(len){
   *id3ip=id3p;
   id3p+=len+1;
  }
 }
 return id3p;
}

static char *inffmpg_tag_get_num(unsigned int num,char **id3ip,char *id3p)
{
 unsigned int len;
 if(num){
  len=snprintf(id3p,16,"%d",num);
  id3p[len]=0;
  *id3ip=id3p;
  id3p+=len+1;
 }
 return id3p;
}

static char *INFFMPG_tag_get(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis,char **id3ip,char *id3p)
{
 struct ffmpg_demuxer_data_s *ffmpi=(struct ffmpg_demuxer_data_s *)miis->private_data;
 AVFormatContext *fctx=&ffmpi->fctx;

 id3p=inffmpg_tag_get_str(miis,fctx->title  ,&id3ip[I3I_TITLE],id3p);
 id3p=inffmpg_tag_get_str(miis,fctx->author ,&id3ip[I3I_ARTIST],id3p);
 id3p=inffmpg_tag_get_str(miis,fctx->comment,&id3ip[I3I_COMMENT],id3p);
 id3p=inffmpg_tag_get_str(miis,fctx->album  ,&id3ip[I3I_ALBUM],id3p);
 id3p=inffmpg_tag_get_str(miis,fctx->genre  ,&id3ip[I3I_GENRE],id3p);

 id3p=inffmpg_tag_get_num(fctx->year   ,&id3ip[I3I_YEAR],id3p);
 id3p=inffmpg_tag_get_num(fctx->track  ,&id3ip[I3I_TRACKNUM],id3p);

 return id3p;
}*/

static int INFLV_infile_open(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,char *filename,struct mpxplay_infile_info_s *miis,mpxp_uint32_t openmode)
{
 return inffmpg_infile_check(fbfs,fbds,filename,miis,&mpxplay_ff_flv_demuxer,openmode);
}

static int INMKV_infile_open(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,char *filename,struct mpxplay_infile_info_s *miis,mpxp_uint32_t openmode)
{
 return inffmpg_infile_check(fbfs,fbds,filename,miis,&mpxplay_ff_matroska_demuxer,openmode);
}

static int INMPG_infile_open(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,char *filename,struct mpxplay_infile_info_s *miis,mpxp_uint32_t openmode)
{
 return inffmpg_infile_check(fbfs,fbds,filename,miis,&mpxplay_ff_mpegps_demuxer,openmode);
}

static int INTS_infile_open(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,char *filename,struct mpxplay_infile_info_s *miis,mpxp_uint32_t openmode)
{
 return inffmpg_infile_check(fbfs,fbds,filename,miis,&mpxplay_ff_mpegts_demuxer,openmode);
}

struct mpxplay_infile_func_s IN_FLV_funcs={
 MPXPLAY_INFILEFUNC_FLAG_VIDEO,
 NULL,
 NULL,
 &INFLV_infile_open,
 &FFMPG_infile_close,
 &FFMPG_infile_demux,
 &FFMPG_infile_fseek,
 &INFFMPG_clearbuffs,
 NULL,//&INFFMPG_tag_get,
 NULL,
 NULL,
 {"FLV",NULL}
};

struct mpxplay_infile_func_s IN_MKA_funcs={
 MPXPLAY_INFILEFUNC_FLAG_NOADETECT,
 NULL,
 NULL,
 &INMKV_infile_open,
 &FFMPG_infile_close,
 &FFMPG_infile_demux,
 &FFMPG_infile_fseek,
 &INFFMPG_clearbuffs,
 NULL,//&INFFMPG_tag_get,
 NULL,
 NULL,
 {"MKA",NULL}
};

struct mpxplay_infile_func_s IN_MKV_funcs={
 MPXPLAY_INFILEFUNC_FLAG_VIDEO,
 NULL,
 NULL,
 &INMKV_infile_open,
 &FFMPG_infile_close,
 &FFMPG_infile_demux,
 &FFMPG_infile_fseek,
 &INFFMPG_clearbuffs,
 NULL,//&INFFMPG_tag_get,
 NULL,
 NULL,
 {"MKV","WEBM",
#ifdef __DOS__
 "WEB",
#endif
 NULL}
};

struct mpxplay_infile_func_s IN_MPG_funcs={
 MPXPLAY_INFILEFUNC_FLAG_VIDEO,
 NULL,
 NULL,
 &INMPG_infile_open,
 &FFMPG_infile_close,
 &FFMPG_infile_demux,
 &FFMPG_infile_fseek,
 &INFFMPG_clearbuffs,
 NULL,
 NULL,
 NULL,
 {"MPG","MPEG","VOB","M2V",
#ifdef __DOS__
 "MPE",
#endif
 NULL}
};

struct mpxplay_infile_func_s IN_TS_funcs={
 MPXPLAY_INFILEFUNC_FLAG_VIDEO,
 NULL,
 NULL,
 &INTS_infile_open,
 &FFMPG_infile_close,
 &FFMPG_infile_demux,
 &FFMPG_infile_fseek,
 &INFFMPG_clearbuffs,
 NULL,
 NULL,
 NULL,
 {"TS","MTS","M2TS",
#ifdef __DOS__
 "M2T",
#endif
 NULL}
};

#endif //MPXPLAY_LINK_INFILE_FFMPG
