//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2014 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: in-file handling - main/common routines

//#define MPXPLAY_USE_DEBUGF 1
#define MPXPLAY_DEBUG_WARNING NULL //stdout
#define MPXPLAY_DEBUG_OUTPUT NULL //stdout
#define MPXPLAY_DEBUGOUT_EXT NULL // stdout
#define MPXPLAY_DEBUGOUT_CB  NULL // stdout
#define MPXPLAY_DEBUGOUT_TIMESYNC stdout
#define MPXPLAY_DEBUGOUT_CLOSE NULL // stdout
//#define MPXPLAY_USE_DEBUGMSG 1

#include "mpxplay.h"
#include "mpxinbuf.h"
#include "au_cards\dmairq.h"
#include "au_mixer\au_mixer.h"
#include "au_mixer\mix_func.h"
#include "decoders\decoders.h"
#include "deparser\tagging.h"
#include "diskdriv\diskdriv.h"
#include "newfunc\dll_load.h"
#include "display\display.h"
#ifdef MPXPLAY_GUI_QT
#include "disp_qt/disp_qt.h"
#endif
#include <io.h>

extern struct mainvars mvps;
extern unsigned int refdisp, mpxplay_config_video_audiovisualization_type;
extern char *mpxplay_extrafiletype_extptrs[MPXINI_MAX_EXTRA_FILETYPES];
extern struct mpxplay_audioout_info_s au_infos;

#ifdef MPXPLAY_LINK_INFILE_FF_MPEG

//ffmpeg parsers
extern struct mpxplay_infile_func_s IN_FFMPEG_AUTODETECT;
extern struct mpxplay_infile_func_s IN_FFMPEG_AAC_funcs, IN_FFMPEG_APE_funcs;
extern struct mpxplay_infile_func_s IN_FFMPEG_ASF_funcs, IN_FFMPEG_AVI_funcs, IN_FFMPEG_MP3_funcs;
extern struct mpxplay_infile_func_s IN_FFMPEG_AC3_funcs, IN_FFMPEG_DTS_funcs, IN_FFMPEG_FLAC_funcs;
extern struct mpxplay_infile_func_s IN_FFMPEG_FLV_funcs, IN_FFMPEG_MKV_funcs, IN_FFMPEG_MOV_funcs;
extern struct mpxplay_infile_func_s IN_FFMPEG_MPG_funcs, IN_FFMPEG_OGG_funcs, IN_FFMPEG_TS_funcs;

#ifdef MPXPLAY_LINK_INFILE_MPX
extern struct mpxplay_infile_func_s IN_MP2_funcs,IN_MP3_funcs;
#endif

#else

//parsers
extern struct mpxplay_infile_func_s IN_AAC_funcs,IN_AC3_funcs,IN_APE_funcs;
extern struct mpxplay_infile_func_s IN_FLAC_funcs;
extern struct mpxplay_infile_func_s IN_MP2_funcs,IN_MP3_funcs,IN_MPC_funcs;
extern struct mpxplay_infile_func_s IN_OGG_funcs,IN_OGV_funcs,IN_WAVPACK_funcs;
//containers
extern struct mpxplay_infile_func_s IN_ASF_funcs,IN_WMA_funcs, IN_AVI_funcs;
extern struct mpxplay_infile_func_s IN_M4A_funcs,IN_MP4_funcs;
extern struct mpxplay_infile_func_s IN_WAV_funcs,IN_AIF_funcs,IN_FLV_funcs;
extern struct mpxplay_infile_func_s IN_MKA_funcs, IN_MKV_funcs,IN_MPG_funcs,IN_TS_funcs;

#endif

#ifdef MPXPLAY_LINK_INFILE_FF_MPEG
static struct mpxplay_infile_func_s *all_infile_funcs[]={
 &IN_FFMPEG_AAC_funcs,
 &IN_FFMPEG_APE_funcs,
 &IN_FFMPEG_ASF_funcs,
 &IN_FFMPEG_AVI_funcs,
 &IN_FFMPEG_AC3_funcs,
 &IN_FFMPEG_DTS_funcs,
 &IN_FFMPEG_FLAC_funcs,
 &IN_FFMPEG_FLV_funcs,
 &IN_FFMPEG_MKV_funcs,
 &IN_FFMPEG_MOV_funcs,
 &IN_FFMPEG_MP3_funcs,
 &IN_FFMPEG_OGG_funcs,
 &IN_FFMPEG_MPG_funcs,
 &IN_FFMPEG_TS_funcs,
#ifdef MPXPLAY_LINK_INFILE_MPX
 &IN_MP3_funcs,
 &IN_MP2_funcs,
#endif
 &IN_FFMPEG_AUTODETECT,
 NULL
};

#else

static struct mpxplay_infile_func_s *all_infile_funcs[]={
#ifdef MPXPLAY_LINK_INFILE_WAV // first (it's possible with other filename extensions (ie:MP3))
 &IN_WAV_funcs,
 &IN_AIF_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_ASF // the others are sorted by autodetection speed
 &IN_ASF_funcs,
 &IN_WMA_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_AVI
 &IN_AVI_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_MP4
 &IN_M4A_funcs,
 &IN_MP4_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_OGG
 &IN_OGG_funcs,
 &IN_OGV_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_FFMPG
 &IN_FLV_funcs,
 &IN_MKA_funcs,
 &IN_MKV_funcs,
#endif

#ifdef MPXPLAY_LINK_INFILE_APE
 &IN_APE_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_FLAC
 &IN_FLAC_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_MPC
 &IN_MPC_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_WAVPACK
 &IN_WAVPACK_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_FFMPG
 &IN_TS_funcs,
 &IN_MPG_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_AC3
 &IN_AC3_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_MPX
 &IN_MP3_funcs,
 &IN_MP2_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_AAC // last (must be if autodetection is enabled, currently it isn't)
 &IN_AAC_funcs,
#endif
 NULL
};

#endif

#define LAST_INFILE_FUNCNUM (sizeof(all_infile_funcs)/sizeof(struct mpxplay_infile_func_s *)-2)

#ifdef MPXPLAY_LINK_DLLLOAD

static struct infiledlltype_s{
 unsigned int dlltype;
 unsigned int modulever;
}infiledlltypes[]=
{
 {MPXPLAY_DLLMODULETYPE_FILEIN_PARSER,   MPXPLAY_DLLMODULEVER_FILEIN_PARSER},
 {MPXPLAY_DLLMODULETYPE_FILEIN_CONTAINER,MPXPLAY_DLLMODULEVER_FILEIN_CONTAINER},
 {0,0}
};

#endif

static int infile_subdecode(struct mpxpframe_s *frp);
static void infile_visualization_clear(struct mpxpframe_s *frp);

extern char *id3tagset[I3I_MAX+1];
extern unsigned int crossfadepart,displaymode,desktopmode,videocontrol;
extern unsigned int stream_select_audio,loadid3tag,playcontrol,playcountframe;
extern mpxp_uint32_t mpxplay_programcontrol;
extern enum mpxplay_video_player_types mpxplay_config_videoplayer_type;

static struct mpxplay_infile_info_s        infile_infos[3];
static struct mpxplay_streampacket_info_s  audiopacket_infos[3];
static struct mpxplay_audio_decoder_info_s audiodecoder_infos[3];
#ifdef MPXPLAY_LINK_INFILE_FF_MPEG
static struct mpxplay_streampacket_info_s  videopacket_infos[3];
#endif
#ifdef MPXPLAY_LINK_VIDEO
static struct mpxplay_video_decoder_info_s videodecoder_infos[3];
#endif

//char mpxplay_tag_year[8];

int mpxplay_infile_decode(struct mpxplay_audioout_info_s *aui)
{
 long retcode_demuxer,retcode_decoder;
 unsigned int fileselect,retry_count_decoder;
 struct mainvars *mvp = aui->mvp;
 struct mpxpframe_s *frp_primary = mvp->fr_primary;
 struct mpxp_aumixer_main_info_s *mmi = &aui->mvp->mmi;

 if(PDS_THREADS_MUTEX_LOCK(&frp_primary->mutexhnd_frame, 1) != MPXPLAY_ERROR_OK)
  return MPXPLAY_ERROR_INFILE_SYNC_IN;

 if(crossfadepart == CROSS_FADE)
  funcbit_enable(mmi->mixer_infobits, AUINFOS_MIXERINFOBIT_CROSSFADE);
 else
  funcbit_disable(mmi->mixer_infobits, AUINFOS_MIXERINFOBIT_CROSSFADE);

 for(fileselect=0;fileselect<=((mmi->mixer_infobits & AUINFOS_MIXERINFOBIT_CROSSFADE)? 1:0);fileselect++){
  struct mpxpframe_s *frp = (fileselect)? frp_primary->fro : frp_primary;
  struct mpxplay_audio_decoder_info_s *adi;
  if(fileselect)
   PDS_THREADS_MUTEX_LOCK(&frp->mutexhnd_frame, -1);
  adi=frp->infile_infos->audio_decoder_infos;
#ifdef MPXPLAY_LINK_VIDEO
  struct mpxplay_video_decoder_info_s *vdi=frp->infile_infos->video_decoder_infos;

  if((displaymode&DISP_GRAPHICAL) && (vdi->flags&VDI_CNTRLBIT_DECODEVIDEO))
   funcbit_enable(vdi->flags,VDI_CNTRLBIT_SHOWVIDEO);
  else
   funcbit_disable(vdi->flags,VDI_CNTRLBIT_SHOWVIDEO);
#endif

  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"infile_decode begin");

  if(frp->infile_infos->seektype){
   infile_visualization_clear(frp);
   if(frp->infile_funcs && frp->infile_funcs->seek_postprocess)
    frp->infile_funcs->seek_postprocess(frp->filebuf_funcs,frp,frp->infile_infos,frp->infile_infos->seektype);
   mpxplay_decoders_clearbuf(frp->infile_infos,frp,frp->infile_infos->seektype);
   funcbit_disable(frp->infile_infos->seektype,MPX_SEEKTYPES_CLEARBUF);
  }

  retcode_demuxer = MPXPLAY_ERROR_INFILE_OK;
  retry_count_decoder = 32; // if decoder doesn't give back correct retcode (if it always give back OK)
  do{
   if(frp->pcmdec_leftsamples && (frp->pcmdec_storedsamples >= frp->pcmdec_leftsamples)){
    unsigned long used_samples = AUMIXER_collect_pcmout_data(frp,(PCM_CV_TYPE_S *)(frp->pcmdec_buffer+(frp->pcmdec_storedsamples-frp->pcmdec_leftsamples)*adi->bytespersample),frp->pcmdec_leftsamples);
    frp->pcmdec_leftsamples -= used_samples;
    if(frp->pcmdec_leftsamples || (aui->card_infobits&AUINFOS_CARDINFOBIT_BITSTREAMOUT))
     break;
   }
   adi->pcm_bufptr = frp->pcmdec_buffer;
   adi->pcm_samplenum = 0;
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"infile_decode demux n:%d",frp->frameNum);

   if(retcode_demuxer == MPXPLAY_ERROR_INFILE_OK){
    if(frp->prebuffer_seek_retry != PREBUFFER_SEEKRETRY_INVALID) {
     retcode_demuxer = MPXPLAY_ERROR_INFILE_SYNC_IN;
     break;
    }
    retcode_demuxer = infile_subdecode(frp);
    if(retcode_demuxer == MPXPLAY_ERROR_INFILE_RESYNC)
     break;
   }

   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"infile_decode decode lb:%d wi:%4.4X",
     frp->infile_infos->audio_stream->bs_leftbytes,frp->infile_infos->audio_stream->wave_id);

   retcode_decoder=mpxplay_decoders_decode(frp->infile_infos);
   frp->pcmdec_storedsamples = frp->pcmdec_leftsamples = adi->pcm_samplenum;

   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"decode ss:%4d lb:%d ub:%4d",frp->pcmdec_storedsamples,
     frp->infile_infos->audio_stream->bs_leftbytes,frp->infile_infos->audio_stream->bs_usedbytes);

  }while((--retry_count_decoder) && ((retcode_demuxer==MPXPLAY_ERROR_INFILE_OK) || (retcode_decoder==MPXPLAY_ERROR_INFILE_OK)));

  if(MIXER_conversion(&aui->mvp->mmi,aui,frp)){
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"get    ss:%4d bsc:%4d %8.8X ", frp->pcmout_storedsamples,frp->pcmout_blocksize,frp->pcmout_buffer);
   frp->frameNum++;
   frp->framecounter++;
  }else if(fileselect && ((retcode_demuxer==MPXPLAY_ERROR_INFILE_EOF) || (retcode_demuxer==MPXPLAY_ERROR_INFILE_NODATA) || (retcode_demuxer==MPXPLAY_ERROR_INFILE_SYNCLOST))){ // if crossfade
   mpxplay_decoders_reset(frp->infile_infos,frp);
   crossfade_part_step(aui->mvp);
  }else if((retcode_demuxer==MPXPLAY_ERROR_INFILE_EOF) || (retcode_demuxer==MPXPLAY_ERROR_INFILE_NODATA) || (retcode_demuxer==MPXPLAY_ERROR_INFILE_SYNC_IN) || (retcode_demuxer==MPXPLAY_ERROR_INFILE_RESYNC) || (retcode_demuxer==MPXPLAY_ERROR_INFILE_SYNCLOST)){
   funcbit_disable(aui->card_controlbits,AUINFOS_CARDCNTRLBIT_DMADONTWAIT);
   if(fileselect)
    PDS_THREADS_MUTEX_UNLOCK(&frp->mutexhnd_frame);
   PDS_THREADS_MUTEX_UNLOCK(&frp_primary->mutexhnd_frame);
   return retcode_demuxer;
  }
  if(fileselect)
   PDS_THREADS_MUTEX_UNLOCK(&frp->mutexhnd_frame);
 }

 MIXER_main(&aui->mvp->mmi,aui,frp_primary);
 AU_writedata(aui);
 MIXER_main_postprocess(&aui->mvp->mmi,frp_primary);

 PDS_THREADS_MUTEX_UNLOCK(&frp_primary->mutexhnd_frame);

 return 0;
}

//--------------------------------------------------------------------------
unsigned int mpxplay_infile_get_samplenum_per_frame(unsigned int freq)
{
 struct mpxplay_audioout_info_s *aui=mvps.aui;
 unsigned int samplenum;

 if(freq>=32000 && freq<=48000)  // match with MPEG 1.0 and AC3 freqs
  samplenum=PCM_OUTSAMPLES;
 else if(freq>=8000 && freq<32000) // match with MPEG 2.x freqs
  samplenum=PCM_OUTSAMPLES/2;
 else
  samplenum=((PCM_OUTSAMPLES*freq)+22050)/44100;

 if(samplenum>PCM_MAX_SAMPLES)
  samplenum=PCM_MAX_SAMPLES;

 return samplenum;
}

void miis_to_frp(struct mpxplay_infile_info_s *miis,struct mpxpframe_s *frp)
{
 struct mpxplay_audio_decoder_info_s *adi=miis->audio_decoder_infos;
 long samplenum_frame;

 funcbit_disable(frp->filetype,HFT_FILE_EXT);
 funcbit_enable(frp->filetype,HFT_FILE_INT);
 frp->filesize=miis->filesize;

 // calculate samplenum
 if((adi->infobits&ADI_FLAG_BITSTREAMOUT) && adi->pcm_framelen)
  samplenum_frame=adi->pcm_framelen;
 else{
  samplenum_frame=mpxplay_infile_get_samplenum_per_frame(adi->freq);
  if(!adi->pcm_framelen) // ie: pcm decoder don't give back a specified length, we configure it
   adi->pcm_framelen=samplenum_frame;
 }

 // calculate allframes (if parser/demuxer didn't set)
 if(miis->allframes<=1){
  if(miis->timemsec) // containers (AVI,ASF,MP4,OGG,WAV)
   miis->allframes=(long)((float)miis->timemsec/1000.0*(float)adi->freq/(float)samplenum_frame);
  else
   if(adi->bitrate)  // pure bitstreams (AC3,DTS,MP3,etc.)
    miis->allframes=(long)((float)frp->filesize/(float)adi->bitrate*(float)adi->freq/(1000.0/8.0)/(float)samplenum_frame);
  if(miis->allframes<1)
   miis->allframes=1;
 }
 frp->allframes=miis->allframes;

 // calculate timesec (if parser/demuxer didn't set)
 if(!miis->timemsec){
  if(miis->allframes) // MP3-VBR,MPC (from header); AC3,DTS (calculated from bitrate)
   miis->timemsec=1000.0*(float)miis->allframes*(float)samplenum_frame/(float)adi->freq;
  else // ???
   if(adi->bitrate)
    miis->timemsec=(float)frp->filesize*8.0/(float)adi->bitrate;
 }

 if(adi->infobits&ADI_FLAG_FLOATOUT)
  adi->bytespersample=sizeof(PCM_CV_TYPE_F);
 else
  if(!adi->bytespersample)
   adi->bytespersample=(adi->bits+7) >> 3;
}

#ifdef MPXPLAY_LINK_VIDEO
void mpxplay_infile_video_config_open(struct mpxplay_videoout_info_s *voi,struct mpxplay_video_decoder_info_s *vdi)
{
 if(videocontrol&MPXPLAY_VIDEOCONTROL_DECODEVIDEO)
  funcbit_enable(vdi->flags,VDI_CNTRLBIT_DECODEVIDEO);
 vdi->picture=voi->screen_linear_ptr; // !!!
 vdi->screen_res_x=voi->screen_res_x;
 vdi->screen_res_y=voi->screen_res_y;
}
#endif

//-------------------------------------------------------------------------------
static unsigned long infile_get_header_autodetect(struct mpxpframe_s *frp,struct mpxplay_diskdrive_data_s *mdds,char *filename,int extfound,mpxp_uint32_t openmode)
{
 int i,retcode;
 for(i=0;i<=LAST_INFILE_FUNCNUM;i++){
  if((i!=extfound) && all_infile_funcs[i]->open && !(all_infile_funcs[i]->flags&MPXPLAY_INFILEFUNC_FLAG_NOADETECT)){
   mpxplay_infile_close(frp);
   frp->mdds=mdds;
   frp->infile_funcs=all_infile_funcs[i];
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"chkaut: %4.4s ",frp->infile_funcs->file_extensions[0]);
   if(frp->infile_funcs->own_filehand_funcs)
    frp->filehand_funcs=frp->infile_funcs->own_filehand_funcs;
   retcode=frp->infile_funcs->open(frp->filebuf_funcs,frp,filename,frp->infile_infos,(openmode|MPXPLAY_INFILE_OPENMODE_AUTODETECT));
   switch(retcode){
    case MPXPLAY_ERROR_INFILE_FILEOPEN:goto err_out_autodetect;
    case MPXPLAY_ERROR_INFILE_OK:
     mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"infile: %4.4s ",frp->infile_funcs->file_extensions[0]);
     if(mpxplay_decoders_open(frp->infile_infos,frp)==MPXPLAY_ERROR_INFILE_OK){
      mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"decopen: %4.4s ",frp->infile_funcs->file_extensions[0]);
      miis_to_frp(frp->infile_infos,frp); // !!!
      return 1;
     }
   }
  }
 }
err_out_autodetect:
 mpxplay_infile_close(frp);
 return 0;
}

static unsigned int infile_check_infilefuncs_by_ext(struct mpxpframe_s *frp,struct mpxplay_infile_func_s *infilefuncs,char *extension,mpxp_uint32_t openmode)
{
 unsigned int j=0;
 if(!infilefuncs)
  return 0;
// if((infilefuncs->flags&MPXPLAY_INFILEFUNC_FLAG_ONLYVIDEO) && (mpxplay_config_videoplayer_type == MPXPLAY_VIDEOPLAYERTYPE_NONE))
//  return 0;
// if((infilefuncs->flags&MPXPLAY_INFILEFUNC_FLAG_NOCHECK) && !(openmode&MPXPLAY_INFILE_OPENMODE_LOAD_PLAY))
//  return 0;
 while(infilefuncs->file_extensions[j]){
  if(pds_stricmp(infilefuncs->file_extensions[j],extension)==0){
   if(frp)
    frp->infile_funcs=infilefuncs;
   return 1;
  }
  j++;
 }
 return 0;
}

int mpxplay_infile_get_infilefuncs_by_ext(struct mpxpframe_s *frp,struct mpxplay_diskdrive_data_s *mdds,char *filename,mpxp_uint32_t openmode) // returns infile handler index, else -1 on error
{
 int i, retry = 2, retcode;
 char *extension = pds_filename_get_extension(filename);

 retcode = (extension)? MPXPLAY_ERROR_INFILE_CANTOPEN : MPXPLAY_ERROR_INFILE_FILETYPE;

 do{
  if(!extension && frp){
   void *mdfd = (frp->filehand_datas)? frp->filehand_datas : mpxplay_diskdrive_file_open(mdds,filename,openmode,&frp->last_errorcode); // open low level file if it's not open yet (and try to get extension, even if this fails)
   mpxplay_diskdrive_file_config(mdfd,MPXPLAY_DISKFILE_CFGFUNCNUM_GET_CONTENTTYPEEXT,&extension,NULL); // try to get file extension (content-type) from low level driver (ie: some http streams have no filename extension, just "stream" as filename)
   frp->filehand_datas = mdfd;
   retry = 1; // don't retry diskdrive again
  }
  if(!extension || !extension[0])
   break;

#ifdef MPXPLAY_LINK_DLLLOAD // !!! dll can override internal file parser
  // check DLLs (first)
  i=0;
  do{
   mpxplay_module_entry_s *dll_infile=NULL;
   do{
    dll_infile=newfunc_dllload_getmodule(infiledlltypes[i].dlltype,0,NULL,dll_infile);
    if(!dll_infile)
     break;
    if(dll_infile->module_structure_version==infiledlltypes[i].modulever){ // !!!
     if(infile_check_infilefuncs_by_ext(frp,(struct mpxplay_infile_func_s *)dll_infile->module_callpoint,extension,openmode)){
      if(frp)
       funcbit_enable(frp->filetype,HFT_FILE_DLL);
      return 0;  // FIXME: not valid for DLL file handlers
     }
    }
   }while(1);
   i++;
  }while(infiledlltypes[i].dlltype);
 #endif
  //check builtin infiles (last)
  for(i=0;i<=LAST_INFILE_FUNCNUM;i++)
   if(infile_check_infilefuncs_by_ext(frp,all_infile_funcs[i],extension,openmode))
    return i;
  extension=NULL;
 }while(--retry);

 if(frp && (frp->last_errorcode != MPXPLAY_ERROR_OK))
  retcode = frp->last_errorcode;

 return retcode;
}

unsigned int mpxplay_infile_check_extension(char *filename,struct mpxplay_diskdrive_data_s *mdds)
{
 if(mpxplay_infile_get_infilefuncs_by_ext(NULL,mdds,filename,0) >= 0)
  return 1;
 if(mpxplay_extrafiletype_extptrs[0]){
  char *extension = pds_filename_get_extension(filename), **extptrs = &mpxplay_extrafiletype_extptrs[0];
  do{
   if(pds_utf8_filename_wildcard_cmp((mpxp_uint8_t *)extension, (mpxp_uint8_t *)*extptrs))
    return 1;
  }while(*(++extptrs));
 }
 return 0;
}

int mpxplay_infile_get_header_by_ext(struct mpxpframe_s *frp,struct mpxplay_diskdrive_data_s *mdds,char *filename,mpxp_uint32_t openmode)
{
 int ext_infile_funcnum, retcode = MPXPLAY_ERROR_INFILE_FILETYPE;

 mpxplay_infile_close(frp);

 ext_infile_funcnum = mpxplay_infile_get_infilefuncs_by_ext(frp,mdds,filename,openmode);
 if(ext_infile_funcnum >= 0){
  struct mpxplay_infile_func_s *infilefuncs=frp->infile_funcs;
  frp->mdds=mdds;
  frp->infile_funcs=infilefuncs;
  if(infilefuncs->own_filehand_funcs)
   frp->filehand_funcs=infilefuncs->own_filehand_funcs;
  if(infilefuncs->open(frp->filebuf_funcs,frp,filename,frp->infile_infos,openmode)==MPXPLAY_ERROR_INFILE_OK){
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"in oke: %4.4s ",frp->infile_funcs->file_extensions[ext_infile_funcnum]);
   if(mpxplay_decoders_open(frp->infile_infos,frp)==MPXPLAY_ERROR_INFILE_OK){
    miis_to_frp(frp->infile_infos,frp); // !!!
    mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"decoder oke");
    return MPXPLAY_ERROR_OK;
   }
  }
  retcode = frp->last_errorcode;
  mpxplay_infile_close(frp);
 }else
  retcode = ext_infile_funcnum;

 if(MPXPLAY_ERROR_IS_DISKDRIV(retcode)) // if the file opening is failed on low level, we don't try to autodetect it
  return retcode;

 if(((ext_infile_funcnum < 0) && !funcbit_test(desktopmode,DTM_EDIT_ALLFILES)) || (mpxplay_programcontrol&MPXPLAY_PROGRAMC_AUTODETECTALWAYS))  // unknown or missing file extension
  if(infile_get_header_autodetect(frp,mdds,filename,ext_infile_funcnum,openmode))
   return MPXPLAY_ERROR_OK;

 return MPXPLAY_ERROR_INFILE_FILETYPE;
}

static void infile_assign_funcs(struct mpxpframe_s *frp)
{
 if(frp->infile_funcs){
  /*if(frp->filetype&HFT_FILE_DLL){
   mpxplay_module_entry_s *dll_infile=(mpxplay_module_entry_s *)frp->infile_funcs;
   if(newfunc_dllload_reloadmodule(dll_infile))
    frp->infile_funcs=(struct mpxplay_infile_func_s *)dll_infile->module_callpoint;
   else
    frp->infile_funcs=NULL;
  }
  if(frp->infile_funcs)*/
   if(frp->infile_funcs->own_filehand_funcs)
    frp->filehand_funcs=frp->infile_funcs->own_filehand_funcs;
 }
}

int mpxplay_infile_get_id3tag(struct mpxpframe_s *frp)
{
 struct mpxplay_filehand_buffered_func_s *fbfs=frp->filebuf_funcs;
 struct mpxplay_infile_info_s *miis=frp->infile_infos;
 int retcode=MPXPLAY_ERROR_OK;

 infile_assign_funcs(frp);
 if(frp->infile_funcs){
  miis->standard_id3tag_support=frp->infile_funcs->flags&MPXPLAY_TAGTYPE_FUNCMASK;
  if(!(frp->filetype&(HFT_DFT|HFT_STREAM)) && MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)){ // common/standard id3tag formats
   if(MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)&MPXPLAY_TAGTYPE_ID3V2){
    if(mpxplay_tagging_id3v2_check(fbfs,frp)){
     retcode=mpxplay_tagging_id3v2_get(fbfs,frp,miis);
     MPXPLAY_TAGTYPE_SET_FOUND(miis->standard_id3tag_support,MPXPLAY_TAGTYPE_ID3V2);
    }
   }
   if(MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)&MPXPLAY_TAGTYPE_ID3V1){
    if(mpxplay_tagging_id3v1_check(fbfs,frp)){
     retcode=mpxplay_tagging_id3v1_get(fbfs,frp,miis);
     MPXPLAY_TAGTYPE_SET_FOUND(miis->standard_id3tag_support,MPXPLAY_TAGTYPE_ID3V1);
    }
   }
   if((MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)&MPXPLAY_TAGTYPE_APETAG) && !(MPXPLAY_TAGTYPE_GET_FOUND(miis->standard_id3tag_support)&MPXPLAY_TAGTYPE_ID3V1)){
    if(mpxplay_tagging_apetag_check(fbfs,frp)){
     retcode=mpxplay_tagging_apetag_get(fbfs,frp,miis);
     MPXPLAY_TAGTYPE_SET_FOUND(miis->standard_id3tag_support,MPXPLAY_TAGTYPE_APETAG);
    }
   }
  }else{
   if(frp->infile_funcs->get_id3tag)
    frp->infile_funcs->get_id3tag(frp->filebuf_funcs,frp,frp->infile_infos);
  }
 }
 return retcode;
}

int mpxplay_infile_write_id3tag(struct mpxpframe_s *frp,char *filename,char **id3ip,unsigned long control)
{
 struct mpxplay_filehand_buffered_func_s *fbfs=frp->filebuf_funcs;
 struct mpxplay_infile_info_s *miis=frp->infile_infos;
 struct playlist_entry_info *pei=frp->pei;
 int error=MPXPLAY_ERROR_INFILE_CANTOPEN;
 unsigned int i,is_id3v1,is_id3v2,is_apetag;
 int id3v1_retcode,id3v2_retcode,apetag_retcode;

 if(frp->filetype&HFT_DFT)
  return MPXPLAY_ERROR_INFILE_WRITETAG_FILETYPE;
 if(frp->filetype&HFT_STREAM)
  return MPXPLAY_ERROR_INFILE_READONLYFS;

 infile_assign_funcs(frp);

 if(frp->infile_funcs){
  miis->standard_id3tag_support=frp->infile_funcs->flags&MPXPLAY_TAGTYPE_FUNCMASK;

  if(frp->infile_funcs->write_id3tag || MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)){

   pds_fileattrib_reset(filename,_A_RDONLY);

   for(i=0;i<=I3I_MAX;i++)
    if(id3tagset[i])
     playlist_editlist_add_id3_one(NULL,pei,i,id3tagset[i],-1);
     //id3ip[i]=id3tagset[i];

   //if(!id3ip[I3I_YEAR])
   // playlist_editlist_add_id3_one(NULL,pei,I3I_YEAR,mpxplay_tag_year,-1);
   // id3ip[I3I_YEAR]=&mpxplay_tag_year[0];

   if(MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)){ // common/standard id3tag write

    if(!fbfs->fopen(frp,filename,O_RDWR|O_BINARY,0))
     return error;

    is_id3v1=0;is_id3v2=0;is_apetag=0;id3v1_retcode=0;id3v2_retcode=0;apetag_retcode=0;

    if(MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)&MPXPLAY_TAGTYPE_ID3V2){
     if(mpxplay_tagging_id3v2_check(fbfs,frp)){
      id3v2_retcode=mpxplay_tagging_id3v2_put(fbfs,frp,miis,id3ip,control);
      if(id3v2_retcode==MPXPLAY_ERROR_INFILE_OK) // !!!
       is_id3v2=1;                               // hack to put id3v1 if v2 failed
     }
    }
    if(MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)&MPXPLAY_TAGTYPE_ID3V1){
     if(mpxplay_tagging_id3v1_check(fbfs,frp)){
      id3v1_retcode=mpxplay_tagging_id3v1_put(fbfs,frp,miis);
      is_id3v1=1;
     }
    }
    if(MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)&MPXPLAY_TAGTYPE_APETAG){
     if(mpxplay_tagging_apetag_check(fbfs,frp)){
      apetag_retcode=mpxplay_tagging_apetag_put(fbfs,frp,miis);
      is_apetag=1;
     }
    }
    if(!is_id3v1 && !is_id3v2 && !is_apetag){ // create new tag
     switch(MPXPLAY_TAGTYPE_GET_PRIMARY(miis->standard_id3tag_support)){
      case MPXPLAY_TAGTYPE_ID3V1 :id3v1_retcode=mpxplay_tagging_id3v1_put(fbfs,frp,miis);break;
      case MPXPLAY_TAGTYPE_ID3V2 :id3v2_retcode=mpxplay_tagging_id3v2_put(fbfs,frp,miis,id3ip,control);break;
      case MPXPLAY_TAGTYPE_APETAG:apetag_retcode=mpxplay_tagging_apetag_put(fbfs,frp,miis);break;
      default:error=MPXPLAY_ERROR_INFILE_WRITETAG_FILETYPE;
     }
    }

    error=min(id3v1_retcode,id3v2_retcode);
    error=min(error,apetag_retcode);

    fbfs->fclose(frp);
   }else{
    error=frp->infile_funcs->write_id3tag(fbfs,frp,frp->infile_infos,filename,control);
   }

  }else{
   error=MPXPLAY_ERROR_INFILE_WRITETAG_FILETYPE;
  }
 }

 return error;
}

//--------------------------------------------------------------------------
static void clear_infile_infos(struct mpxplay_infile_info_s *miis,struct mpxpframe_s *frp)
{
 if(miis){ // it can be NULL at 'soundcard init failed'
  miis->filesize=0;
  miis->timemsec=0;
  miis->allframes=1;
  miis->private_data=NULL;
  miis->longname=NULL;
  miis->seektype=0;
  miis->standard_id3tag_support=0;

  miis->audio_decoder_funcs=NULL;
  miis->video_decoder_funcs=NULL;

  mpxplay_decoders_clear(miis,frp);
 }
}

static void infile_visualization_clear(struct mpxpframe_s *frp)
{
#if defined(MPXPLAY_LINK_INFILE_FF_MPEG) && defined(MPXPLAY_GUI_QT)
 mpxplay_ffmpgdec_packetqueue_clear(&frp->audio_packet_queue, 0);
 frp->avsync_clocktime = -1;
 frp->avsync_timestamp = -1;
#endif
}

static void clear_frame(struct mpxpframe_s *frp)
{
 frp->filetype=0;
 frp->filesize=0;
 frp->filepos=0;
 frp->frameNum=0;
 frp->framecounter=0;
 frp->index_start=0;
 frp->index_end=0;
 frp->index_len=1;      //
 frp->allframes=1;      // division by zero bugfix
 frp->timesec=0;

 frp->buffertype=0;
 frp->prebuffergetp=0;
 frp->prebufferputp=0;
 frp->prebufferbytes_rewind=0;
 frp->prebufferbytes_forward=0;
 frp->prebuffer_seek_retry=PREBUFFER_SEEKRETRY_INVALID;

 frp->mdds=NULL;
 frp->filehand_funcs=NULL;
 frp->filehand_datas=NULL;

 frp->infile_funcs=NULL;

 frp->pcmdec_storedsamples=0;
 frp->pcmout_storedsamples=0;
 frp->last_errorcode = MPXPLAY_ERROR_OK;
 frp->flags = 0;
 infile_visualization_clear(frp);
}

static void mpxplay_infile_clear_infos_all(struct mpxplay_infile_info_s *miis,struct mpxpframe_s *frp)
{
 mpxplay_infile_reset(frp);
 clear_infile_infos(miis, frp);
 clear_frame(frp);
}

//------------------------------------------------------------------------------------------------
#ifdef MPXPLAY_GUI_QT

#define MPXPLAY_INFILE_EXTFILE_REFRESH_FPS 4 // per sec -> gives 250ms seeking precision
#define MPXPLAY_INFILE_EXTFILE_FPS        38 // a fake value (near to MP3 fps)
#define MPXPLAY_INFILE_EXTFILE_FAKE_DUR_MS 300000 // 5 mins

void mpxplay_infile_duration_set(struct mpxpframe_s *frp, mpxp_int64_t duration_ms)
{
 struct mpxplay_audio_decoder_info_s *adi;
 struct mpxplay_audioout_info_s *aui;
 if(duration_ms < 0)
  return;
 if(!duration_ms){
  duration_ms = MPXPLAY_INFILE_EXTFILE_FAKE_DUR_MS;   // !!! fake duration if no other
  mpxplay_debugf(MPXPLAY_DEBUGOUT_EXT, "INFILE duration FAKE");
 }
 frp->infile_infos->timemsec = (long)duration_ms;
 frp->allframes = frp->infile_infos->allframes = (long)((float)duration_ms * (float)MPXPLAY_INFILE_EXTFILE_FPS / 1000.0); // with frame size of mp3
 adi = frp->infile_infos->audio_decoder_infos;
 aui = frp->mvp->aui;
 adi->freq = (aui->freq_card)? aui->freq_card : 44100;	// FIXME: ???
 adi->outchannels = adi->filechannels = (aui->chan_card)? aui->chan_card : PCM_CHANNELS_DEFAULT;
 adi->bits = (aui->bits_card)? aui->bits_card : 16;
 adi->bytespersample = (aui->bytespersample_card)? aui->bytespersample_card : 4;
 //adi->chanmatrix = NULL;
}

static void mpxplay_infile_duration_refresh(struct mpxpframe_s *frp)
{
 if(!frp->infile_infos->timemsec || (frp->infile_infos->timemsec == MPXPLAY_INFILE_EXTFILE_FAKE_DUR_MS)){
  mpxp_int64_t duration_ms = mpxplay_dispqt_video_duration_ms();
  mpxplay_infile_duration_set(frp, duration_ms);
  //if(frp->pei)  // not good after tab/psi del
  // frp->pei->timemsec = (unsigned long)duration_ms;
 }
}

void mpxplay_infile_framenum_refresh(struct mpxpframe_s *frp)
{
 if((playcontrol&PLAYC_RUNNING) || frp->infile_infos->seektype){
  mpxp_int64_t currpos_ms;
  long currframenum;

  mpxplay_infile_duration_refresh(frp);

  currpos_ms = mpxplay_dispqt_video_tell_ms();
  if(currpos_ms < 0)
   return;
  currframenum = (long)((float)currpos_ms * (float)frp->allframes / (float)frp->infile_infos->timemsec);
  frp->frameNum = currframenum;

  if(playcontrol & PLAYC_RUNNING){
   frp->framecounter += MPXPLAY_INFILE_EXTFILE_FPS / MPXPLAY_INFILE_EXTFILE_REFRESH_FPS;
   if(playcountframe && (frp->framecounter >= playcountframe))
    frp->mvp->adone=ADONE_EOF;
  }
  //mpxplay_debugf(MPXPLAY_DEBUGOUT_EXT, "REF ms:%5d fn:%d af:%d ts:%d fc:%3d  tst:%d tc:%d 8c:%d", (long)currpos_ms, frp->frameNum, frp->allframes, frp->timesec,
  //        frp->framecounter, mpxplay_timer_secs_to_counternum(1), (mpxplay_timer_secs_to_counternum(1) / MPXPLAY_INFILE_EXTFILE_REFRESH_FPS), pds_threads_timer_tick_get());
 }
 frp->infile_infos->seektype = 0;
}
#endif // MPXPLAY_GUI_QT

#if defined(MPXPLAY_LINK_INFILE_FF_MPEG) && defined(MPXPLAY_GUI_QT)

static struct mpxplay_streaminfo_t *mpxpinfile_streaminfolist_fill(struct mpxpframe_s *frp)
{
	struct mpxplay_infile_info_s *miis = frp->infile_infos;
	struct mpxplay_audio_decoder_info_s *adi;
	struct mpxplay_streaminfo_t *stream_list = NULL;
	char *chantext;
	if(!miis)
		return stream_list;
	adi = miis->audio_decoder_infos;
	if(!adi)
		return stream_list;

	stream_list = pds_calloc(1, sizeof(*stream_list));
	if(!stream_list)
		return stream_list;

	stream_list->nb_entries[MPXPLAY_STREAMTYPEINDEX_AUDIO] = 1;
	stream_list->stream_names[MPXPLAY_STREAMTYPEINDEX_AUDIO] = pds_calloc(1, sizeof(stream_list->stream_names[MPXPLAY_STREAMTYPEINDEX_AUDIO]));
	stream_list->stream_idx[MPXPLAY_STREAMTYPEINDEX_AUDIO] = pds_calloc(1, sizeof(stream_list->stream_idx[MPXPLAY_STREAMTYPEINDEX_AUDIO]));
	if(!stream_list->stream_names[MPXPLAY_STREAMTYPEINDEX_AUDIO] || !stream_list->stream_idx[MPXPLAY_STREAMTYPEINDEX_AUDIO])
		return stream_list;
	stream_list->stream_names[MPXPLAY_STREAMTYPEINDEX_AUDIO][0] = pds_malloc(128);
	if(!stream_list->stream_names[MPXPLAY_STREAMTYPEINDEX_AUDIO][0])
		return stream_list;
	chantext = (adi->channeltext)? adi->channeltext : ((adi->filechannels == 1)? "Mono" : "Stereo"); // currently there's no other cases
	snprintf(stream_list->stream_names[MPXPLAY_STREAMTYPEINDEX_AUDIO][0], 128, "%s, %d Hz, %s, %d kb/s", adi->shortname, adi->freq, chantext, adi->bitrate);

	return stream_list;
}

static int mpxplay_infile_callback_func(void *demuxer_data, unsigned int control, void *arg1, int *arg2, int timeout_ms)
{
	struct mpxpframe_s *frp = (struct mpxpframe_s *)demuxer_data;
	mpxplay_packetlist_t *pktlist_elem;
	mpxp_uint32_t arg1_num, arg2_num;
	int retcode = MPXPLAY_ERROR_INFILE_NODATA, i, j, retval;

	if(!frp)
		return retcode;
	if(!arg1)
		return retcode;

	if(timeout_ms != MPXPLAY_MUTEXTIME_NOLOCK)
	{
		retval = PDS_THREADS_MUTEX_LOCK(&frp->mutexhnd_frame, timeout_ms);
		if((retval != MPXPLAY_ERROR_OK) && (timeout_ms > 10)){
			mpxplay_debugf(MPXPLAY_DEBUG_WARNING, "mpxplay_infile_callback_func pds_threads_mutex_lock FAILED cmd:%8.8X pd:%8.8X ret:%d tms:%d", control, (int)demuxer_data, retval, timeout_ms);
			return retcode;
		}
	}

	switch(control)
	{
		case MPXPLAY_INFILE_CBKCTRL_GET_STREAMLIST:
			*((struct mpxplay_streaminfo_t **)arg1) = mpxpinfile_streaminfolist_fill(frp);
			retcode = MPXPLAY_ERROR_OK;
			break;
		case MPXPLAY_INFILE_CBKCTRL_GET_TIMESYNC:
			if(frp->avsync_clocktime <= 0)
			{
				frp->avsync_clocktime = pds_gettimeu();
			}
			if(frp->avsync_timestamp >= 0)
			{
				*((mpxp_int64_t *)arg1) = pds_gettimeu() - frp->avsync_clocktime + frp->avsync_timestamp;
				retcode = MPXPLAY_ERROR_OK;
			}
			break;
		case MPXPLAY_INFILE_CBKCTRL_GET_PACKETLIST_ELEM:
			if(!arg2)
				return retcode;
			arg2_num = *arg2;
			if(arg2_num == MPXPLAY_STREAMTYPEINDEX_VIDEO) // FIXME: hack
			{
				pktlist_elem = NULL;
				retcode = mpxplay_ffmpgdec_packetqueue_get((void *)frp, &frp->audio_packet_queue, &pktlist_elem, 0);
				*((mpxplay_packetlist_t **)arg1) = pktlist_elem;
				if(pktlist_elem)
				{
					if(pktlist_elem->flags & MPXPLAY_PACKETLISTFLAG_CLEARBUF)
					{
						frp->avsync_clocktime = -1;
					}
					mpxplay_debugf(MPXPLAY_DEBUGOUT_CB, "mpxplay_infile_callback_func GETPACK pe:%8.8X avf:%8.8X pts:%lld diff:%lld",
							(int)pktlist_elem, (int)pktlist_elem->frame_pkt, ((AVFrame *)pktlist_elem->frame_pkt)->pts,
							(frp->avsync_timestamp + pds_gettimeu() - frp->avsync_clocktime - ((AVFrame *)pktlist_elem->frame_pkt)->pts));
				}
			}
			break;
		case MPXPLAY_INFILE_CBKCTRL_SET_PACKETQUEUE_CLEAR:
			infile_visualization_clear(frp);
			retcode = MPXPLAY_ERROR_OK;
			break;
	}

err_out_cbk:
	if(timeout_ms != MPXPLAY_MUTEXTIME_NOLOCK)
	{
		PDS_THREADS_MUTEX_UNLOCK(&frp->mutexhnd_frame);
	}

	return retcode;
}

mpxp_int64_t mpxplay_infile_callback_check_audio_timesync(void *fr_data, mpxp_int64_t avsync_clocktime, mpxp_int64_t *avsync_timestamp, mpxp_int64_t curr_pts, int bs_leftbytes, int sample_rate)
{
//	struct mpxpframe_s *frp = (struct mpxpframe_s *)fr_data;
	const int dma_bufsize = (au_infos.card_controlbits & AUINFOS_CARDCNTRLBIT_DOUBLEDMA)? (AUCARDS_DMABUFSIZE_NORMAL * 2) : AUCARDS_DMABUFSIZE_NORMAL;
	mpxp_int64_t audio_delay = (mpxp_int64_t)AV_TIME_BASE * (mpxp_int64_t)(max(dma_bufsize,bs_leftbytes)) / (mpxp_int64_t)sample_rate / 2 / 2; // FIXME: get correct audio output delay
	mpxp_int64_t timestamp_new = curr_pts - audio_delay;
	if(timestamp_new < 0)
	{
		timestamp_new = 0;
		*avsync_timestamp = -1LL;
	}
	else if(*avsync_timestamp > 0)
	{
		mpxp_int64_t avsync_curr = pds_gettimeu() - avsync_clocktime + *avsync_timestamp;
		mpxp_int64_t timestamp_diff = timestamp_new - avsync_curr;
		mpxp_int64_t small_correction = audio_delay / 4;
		if(timestamp_diff < 0){
			timestamp_diff = -timestamp_diff;
			small_correction = -small_correction;
		}
		if(timestamp_diff > (audio_delay * 2))
			*avsync_timestamp = -1LL;
		else if(timestamp_diff > (audio_delay / 2))
			*avsync_timestamp += small_correction;
		mpxplay_debugf(MPXPLAY_DEBUGOUT_TIMESYNC, "mpxplay_infile_callback_check_audio_timesync ast:%lld asc:%lld cpts:%lld tsn:%lld diff:%lld adl:%lld",
						*avsync_timestamp, avsync_curr, curr_pts, timestamp_new, (timestamp_new - avsync_curr), audio_delay);
	}
	return timestamp_new;
}

void mpxplay_infile_callback_push_audio_packet(void *fr_data, int sample_rate, int nb_bits, int nb_channels, int nb_samples_all_channels, unsigned char *data)
{
	struct mpxpframe_s *frp = (struct mpxpframe_s *)fr_data;
	unsigned int packet_flags = 0;
	mpxp_int64_t new_timestamp;
	AVFrame *audio_frame;

	if(!frp || !funcbit_test(frp->flags, MPXPLAY_MPXPFRAME_FLAG_SRVFFMV_CALLBACK) || !mpxplay_config_video_audiovisualization_type)
		return;

	audio_frame = av_frame_alloc();
	if(!audio_frame)
		return;
	switch(nb_bits)
	{
		case  1: audio_frame->format = AV_SAMPLE_FMT_FLT; nb_bits = 32; break; // !!! 1 bit means 32-bit float type
		case  8: audio_frame->format = AV_SAMPLE_FMT_U8;  break;
		case 16: audio_frame->format = AV_SAMPLE_FMT_S16; break;
		case 32: audio_frame->format = AV_SAMPLE_FMT_S32; break;
		default: goto err_out_push;
	}
	audio_frame->sample_rate = sample_rate;
	audio_frame->channels = nb_channels;
	audio_frame->channel_layout = av_get_default_channel_layout(nb_channels);
	audio_frame->nb_samples = nb_samples_all_channels / nb_channels;
	audio_frame->pts = (int64_t)frp->frameNum * (int64_t)audio_frame->nb_samples * 1000000LL / sample_rate;
	if(frp->avsync_timestamp <= 0)
	{
		funcbit_enable(packet_flags, MPXPLAY_PACKETLISTFLAG_CLEARBUF);
	}
	new_timestamp = mpxplay_infile_callback_check_audio_timesync(fr_data, frp->avsync_clocktime, &frp->avsync_timestamp, audio_frame->pts, 0, sample_rate);
	if(frp->avsync_timestamp <= 0)
	{
		frp->avsync_timestamp = new_timestamp;
		frp->avsync_clocktime = pds_gettimeu();
		mpxplay_debugf(MPXPLAY_DEBUGOUT_CB, "mpxplay_infile_callback_push_audio_packet TIMESTAMP CORR %lld -> %lld diff:%lld", audio_frame->pts, new_timestamp, (audio_frame->pts - new_timestamp));
	}
	if(av_frame_get_buffer(audio_frame, 1) < 0)
		goto err_out_push;
	pds_memcpy(audio_frame->data[0], data, (nb_bits / 8 * nb_samples_all_channels));
	//audio_frame->pts = audio_frame->pts - AV_TIME_BASE / 5;  // FIXME: doesn't work, causes choppy video (else delay in the visual output)
	if(mpxplay_ffmpgdec_packetqueue_put(&frp->audio_packet_queue, MPXPLAY_PACKETTYPE_AVFRAME_AUDIO, packet_flags, NULL, audio_frame) < 0)
		goto err_out_push;
	mpxplay_debugf(MPXPLAY_DEBUGOUT_CB, "mpxplay_infile_callback_push_audio_packet PUT n:%d avts:%lld nts:%lld pts:%lld diff:%lld",
			frp->audio_packet_queue.nb_packets, frp->avsync_timestamp, new_timestamp, audio_frame->pts,
			(frp->avsync_timestamp + pds_gettimeu() - frp->avsync_clocktime - audio_frame->pts));

	return;

err_out_push:
	av_frame_free(&audio_frame);
}

static void mpxplay_infile_callback_init(struct mpxpframe_s *frp)
{
	if(!funcbit_test(frp->flags, MPXPLAY_MPXPFRAME_FLAG_OWN_SRVFFMV_CALLBACK))
		if(mpxplay_dispqt_ffmpegvideo_config_callback(MPXPLAY_INFILE_CBKCFG_SRVFFMV_OPEN_CALLBACK, mpxplay_infile_callback_func, (void *)frp) == 0)
			funcbit_enable(frp->flags, MPXPLAY_MPXPFRAME_FLAG_SRVFFMV_CALLBACK);
}

static void mpxplay_infile_callback_close(struct mpxpframe_s *frp)
{
	if(funcbit_test(frp->flags, MPXPLAY_MPXPFRAME_FLAG_SRVFFMV_CALLBACK))
	{
		mpxplay_dispqt_ffmpegvideo_config_callback(MPXPLAY_INFILE_CBKCFG_SRVFFMV_CLOSE_CALLBACK, mpxplay_infile_callback_func, (void *)frp);
		mpxplay_ffmpgdec_packetqueue_clear(&frp->audio_packet_queue, 0);
		funcbit_disable(frp->flags, MPXPLAY_MPXPFRAME_FLAG_SRVFFMV_CALLBACK);
	}
}
#endif // defined(MPXPLAY_LINK_INFILE_FF_MPEG) && defined(MPXPLAY_GUI_QT)

//------------------------------------------------------------------------

int mpxplay_infile_open(struct mpxpframe_s *frp,char *filename,mpxp_uint32_t openmode)
{
 int retcode = MPXPLAY_ERROR_INFILE_FILETYPE;
 infile_assign_funcs(frp);
#ifdef MPXPLAY_GUI_QT
 if(openmode & MPXPLAY_INFILE_OPENMODE_LOAD_PLAY){
  if( (!frp->infile_funcs && funcbit_test(mpxplay_programcontrol, MPXPLAY_PROGRAMC_AUTODETECTALWAYS))
   || (frp->infile_funcs && (mpxplay_config_videoplayer_type >= MPXPLAY_VIDEOPLAYERTYPE_EXTERNAL) && (frp->infile_funcs->flags & MPXPLAY_INFILEFUNC_FLAG_VIDEO))){
   if(mpxplay_dispqt_video_open((void *)frp->infile_infos, filename)){
    mpxplay_debugf(MPXPLAY_DEBUGOUT_EXT, "INFILE EXT OPEN OK");
    funcbit_disable(frp->filetype,(HFT_FILE_INT|HFT_FILE_DLL));
    funcbit_enable(frp->filetype, HFT_FILE_EXT);
    mpxplay_infile_duration_refresh(frp);
    mpxplay_timer_addfunc(mpxplay_infile_framenum_refresh, frp, MPXPLAY_TIMERTYPE_REPEAT, (mpxplay_timer_secs_to_counternum(1) / MPXPLAY_INFILE_EXTFILE_REFRESH_FPS));
    return MPXPLAY_ERROR_OK;
   }
  }
  mpxplay_timer_deletefunc(mpxplay_infile_framenum_refresh, NULL);
 }
#endif

 if(frp->infile_funcs && frp->infile_funcs->open && filename && frp->filebuf_funcs && frp->infile_infos){
  if(frp->infile_funcs->open(frp->filebuf_funcs,frp,filename,frp->infile_infos,openmode)==MPXPLAY_ERROR_INFILE_OK){
   if(mpxplay_decoders_open(frp->infile_infos,frp)==MPXPLAY_ERROR_INFILE_OK){
    miis_to_frp(frp->infile_infos,frp); // !!!
#if defined(MPXPLAY_LINK_INFILE_FF_MPEG) && defined(MPXPLAY_GUI_QT)
    if(openmode & MPXPLAY_INFILE_OPENMODE_LOAD_PLAY)
     mpxplay_infile_callback_init(frp);
#endif
    return MPXPLAY_ERROR_OK;
   }
  }
  if(frp->last_errorcode != MPXPLAY_ERROR_OK)
   retcode = frp->last_errorcode; // TODO: decoder errors
  mpxplay_infile_close(frp);
 }
 return retcode;
}

int mpxplay_infile_avfile_open(struct mpxpframe_s *frp,char *filename,mpxp_uint32_t openmode,unsigned long framenum)
{
 if(mpxplay_infile_open(frp,filename,openmode) == MPXPLAY_ERROR_OK){
  mpxplay_debugf(MPXPLAY_DEBUGOUT_EXT, "INFILE AVOPEN %d %d", framenum, frp->frameNum);
  if(framenum){
   if(mpxplay_infile_fseek(frp,framenum)<0)
    mpxplay_infile_fseek(frp,0);
   else
    frp->infile_infos->seektype=MPX_SEEKTYPE_NORM;
  }
  if(funcbit_test(frp->filetype, HFT_FILE_EXT))
   return 1;
  AU_setrate(mvps.aui,frp->infile_infos->audio_decoder_infos);
  if((mpxplay_decoders_alloc(frp,1)==MPXPLAY_ERROR_INFILE_OK) && MIXER_configure(&mvps.mmi,mvps.aui,frp)){
   //MIXER_allfuncinit_reinit();
   return 1;
  }
 }
 return 0;
}

void mpxplay_infile_reset(struct mpxpframe_s *frp)
{
 if(frp->infile_infos)
  funcbit_smp_int32_put(frp->infile_infos->seektype, 0);
 funcbit_smp_int32_put(frp->prebufferbytes_forward, 0);
 funcbit_smp_int32_put(frp->prebufferbytes_rewind, 0);
}

void mpxplay_infile_suspend_close(struct mpxpframe_s *frp)
{
 mpxplay_debugf(MPXPLAY_DEBUGOUT_CLOSE,"mpxplay_infile_suspend_close BEGIN err:%d", frp->last_errorcode);
 mpxplay_timer_deletefunc(mpxplay_infile_close,frp);
 mpxplay_infile_reset(frp);
#ifdef MPXPLAY_GUI_QT
 if(funcbit_test(frp->filetype, HFT_FILE_EXT)) {
  mpxplay_timer_deletefunc(mpxplay_infile_framenum_refresh, frp);
  mpxplay_dispqt_video_close((void *)frp->infile_infos); // !!! to not close not-proper video
 }else
#endif
 {
#if defined(MPXPLAY_GUI_QT) && defined(MPXPLAY_LINK_INFILE_FF_MPEG)
  mpxplay_infile_callback_close(frp);
#endif
  if(frp->infile_funcs && frp->infile_funcs->close){
   frp->infile_funcs->close(frp->filebuf_funcs,frp,frp->infile_infos);
   mpxplay_debugf(MPXPLAY_DEBUGOUT_CLOSE,"mpxplay_infile_suspend_close frp->infile_funcs->close DONE");
  }
  mpxplay_decoders_close(frp->infile_infos,frp);
 }
 clear_infile_infos(frp->infile_infos,frp);
}

void mpxplay_infile_close(struct mpxpframe_s *frp)
{
 mpxplay_infile_suspend_close(frp);
 clear_frame(frp);
}

static int infile_subdecode(struct mpxpframe_s *frp) // demux (AVI,ASF) or direct decoding (APE,WAV)
{
 int retcode=MPXPLAY_ERROR_INFILE_EOF;

 if(frp->infile_funcs && frp->infile_funcs->decode){
  if(frp->infile_infos->audio_stream->bs_leftbytes)
   retcode=MPXPLAY_ERROR_INFILE_OK;
  else
   retcode=frp->infile_funcs->decode(frp->filebuf_funcs,frp,frp->infile_infos);
 }

 return retcode;
}

long mpxplay_infile_fseek(struct mpxpframe_s *frp, long framenum_set)
{
#if defined(MPXPLAY_GUI_QT)
 if(funcbit_test(frp->filetype, HFT_FILE_EXT)) {
  mpxp_int64_t newpos_ms = (mpxp_int64_t)((float)framenum_set * (float)frp->infile_infos->timemsec / (float)frp->allframes);
  mpxplay_dispqt_video_seek_ms(newpos_ms);
  mpxplay_debugf(MPXPLAY_DEBUGOUT_EXT, "ISEEK ms:%d fr:%d sr:%d", (long)newpos_ms, frp->frameNum, framenum_set);
  return framenum_set;
 }
#endif
 if(frp->infile_funcs && frp->infile_funcs->fseek){
  if((framenum_set<=frp->frameNum) || (framenum_set==frp->index_start))
   funcbit_enable(frp->infile_infos->seektype,MPX_SEEKTYPE_BACKWARD);
  framenum_set=frp->infile_funcs->fseek(frp->filebuf_funcs,frp,frp->infile_infos,framenum_set);
 }
 funcbit_disable(frp->infile_infos->seektype,MPX_SEEKTYPES_FSEEK);

 //mpxplay_decoders_setpos(frp->infile_infos,framenum_set);

 return framenum_set;
}

//-------------------------------------------------------------------------
long mpxplay_infile_control_cb(void *cb_data,unsigned long funcnum,void *argp1,void *argp2)
{
 long retcode=MPXPLAY_ERROR_OK,i;
 char *p;
 struct mpxpframe_s *frp=(struct mpxpframe_s *)cb_data,*frp_new;
 unsigned int texttype,i3index,len;
 struct playlist_side_info *psi;
 struct playlist_entry_info *pei;
 struct mpxplay_diskdrive_data_s *mdds;
 char original_filename[MAX_PATHNAMELEN], tmpfile_filename[MAX_PATHNAMELEN], sout[128];
#ifdef MPXPLAY_USE_DEBUGF
 static int counter = 0;
#endif

 if(!frp)
  return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
 psi=frp->psi;
 pei=frp->pei;

 //mpxplay_debugf(MPXPLAY_DEBUGOUT_CB,"icb %5d %8.8X", ++counter, funcnum);

 if((funcnum&MPXPLAY_CFGFUNCNUM_INFILE_FILEOPEN_FUNCMASK)==MPXPLAY_CFGFUNCNUM_INFILE_FILE_OPEN){
  if(!argp1 || !argp2)
   return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
  frp_new=(struct mpxpframe_s *)pds_calloc(1,sizeof(struct mpxpframe_s)); // !!!
  if(!frp_new)
   return MPXPLAY_ERROR_FILEHAND_MEMORY;
  frp_new->mdds=frp->mdds;
  frp_new->psi=frp->psi;
  if(!(funcnum&(O_WRONLY|O_RDWR|O_CREAT|O_APPEND|O_TRUNC)))
   mpxplay_mpxinbuf_alloc_ringbuffer(NULL,frp_new,PREBUFFERBLOCKS_SHORTRING);
  if(mpxplay_mpxinbuf_fopen(frp_new,(char *)argp2,(funcnum&(~MPXPLAY_CFGFUNCNUM_INFILE_FILEOPEN_FUNCMASK)),0)){
   *((void **)argp1)=(void *)frp_new; // !!!
   frp_new->pei=(struct playlist_entry_info *)pds_calloc(1,sizeof(struct playlist_entry_info));
   if(frp_new->pei){
    frp_new->pei->filename=pds_malloc(MAX_PATHNAMELEN);
    pds_strcpy(frp_new->pei->filename,(char *)argp2);
   }
   if(!(funcnum&(O_WRONLY|O_RDWR|O_CREAT|O_APPEND|O_TRUNC)))
    mpxplay_mpxinbuf_alloc_ringbuffer(NULL,frp_new,PREBUFFERBLOCKS_SHORTRING);
  }else{
   if(frp_new->prebufferbegin)
    pds_free(frp_new->prebufferbegin);
   pds_free(frp_new);
   retcode=MPXPLAY_ERROR_FILEHAND_CANTOPEN;
  }
 }else if((funcnum&MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_FUNCMASK)==MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_GET){
  if(!argp1 || !argp2)
   return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
  if(!pei || !psi)
   return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
  texttype=(funcnum>>4)&0x0f;
  i3index=funcnum&0x0f;
  if(i3index>I3I_MAX)
   return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
  if(!pei->id3info[i3index])
   return MPXPLAY_ERROR_OK;
  len=*((unsigned long *)argp2);
  if(len)
   p=*((char **)argp1);
  else{
   len=MAX_STRLEN;
   p=pds_malloc(MAX_STRLEN+8);
   if(!p)
    return MPXPLAY_ERROR_INFILE_MEMORY;
  }
  i=mpxplay_playlist_textconv_by_texttypes(MPXPLAY_TEXTCONV_TYPES_PUT(MPXPLAY_TEXTCONV_TYPE_MPXPLAY,texttype),
    pei->id3info[i3index],-1,p,len);
  if(i<=0){
   if(!(*((unsigned long *)argp2)))
    pds_free(p);
   p=NULL;
   i=0;
  }else{
   if(!(*((unsigned long *)argp2)) && (i<len)){
    char *np=pds_malloc(i+2);
    if(np){
     pds_memcpy(np,p,i+2);
     pds_free(p);
     p=np;
    }
   }
  }
  *((char **)argp1)=p;
  *((long *)argp2)=i;
 }else if(((funcnum&MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_FUNCMASK)==MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_PUT)
       || ((funcnum&MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_FUNCMASK)==MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_UPDATE)
 ){
  unsigned int funccmd = funcnum & MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_FUNCMASK;
  if((funccmd==MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_UPDATE) && !(loadid3tag & ID3LOADMODE_FILE))
   return MPXPLAY_ERROR_OK; // TODO: check
  retcode = mpxplay_infile_controlcb_modify_pei_by_filename(funcnum, psi, pei, mpxplay_diskdrive_file_get_filename(frp->filehand_datas), (char*)argp1,*((long *)argp2));
 }else{
  switch(funcnum){
   case MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAGS_CLEAR:
    if(!pei || !psi)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    playlist_editlist_del_id3_all(psi,pei);
    break;
   /*case MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAGS_RELOAD:
    if(!pei || !psi)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    playlist_chkentry_load_id3tag_from_file(psi,pei,frp,loadid3tag,0);
    //playlist_pei0_set(psi->mvp,pei,EDITLIST_MODE_ID3);
    break;*/
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_CLOSE:
    if(!argp1)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    frp=(struct mpxpframe_s *)*((void **)argp1);   // !!!
    if(frp){
     if(frp->filebuf_funcs)
      frp->filebuf_funcs->fclose(frp);
     if(frp->prebufferbegin)
      pds_free(frp->prebufferbegin);
     if(frp->pei){
      if(frp->pei->filename)
       pds_free(frp->pei->filename);
      pds_free(frp->pei);
     }
     pds_free(frp); // !!!
     *((void **)argp1)=NULL;
    }
    break;
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_SEEK:
    if(!argp1 || !argp2)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    return frp->filebuf_funcs->fseek(frp,*((mpxp_int64_t *)argp1),*((int *)argp2));
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_TELL:
    if(!argp1)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    *((mpxp_int64_t *)argp1)=frp->filebuf_funcs->ftell(frp);
    break;
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_LENGTH:
    if(!argp1)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    *((mpxp_int64_t *)argp1)=frp->filebuf_funcs->filelength(frp);
    break;
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_EOF:
    if(!frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    return frp->filebuf_funcs->eof(frp);
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_CHSIZE:
    if(!argp1)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    return frp->filebuf_funcs->chsize(frp,*((mpxp_int64_t *)argp1));
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_READ_BYTES:
    if(!argp1 || !argp2)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    return frp->filebuf_funcs->fread(frp,argp1,*(unsigned long *)argp2);
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_WRITE_BYTES:
    if(!argp1 || !argp2)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    return frp->filebuf_funcs->fwrite(frp,argp1,*(unsigned long *)argp2);
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_RENAME:
    if(!argp1 || !argp2)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->mdds)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    retcode=mpxplay_diskdrive_rename(frp->mdds,(char *)argp1,(char *)argp2);
    break;
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_UNLINK:
    if(!argp1)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->mdds)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    retcode=mpxplay_diskdrive_unlink(frp->mdds,(char *)argp1);
    break;
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_COPY:
   {
    char *copybuf;
    struct mpxpframe_s *frp_dest;
    struct mpxplay_filehand_buffered_func_s *fbfs;
    mpxp_int32_t last_percent;
    mpxp_filesize_t beginpos,copylen,filesize_src,copypos;
    if(!argp1 || !argp2)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    fbfs=frp->filebuf_funcs;
    if(!fbfs){
     retcode=MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
     break;
    }
    frp_dest=(struct mpxpframe_s *)argp1;
    beginpos=fbfs->ftell(frp);
    copylen=*((mpxp_int64_t *)argp2);
    filesize_src=fbfs->filelength(frp);
    if((copylen<0) || (copylen>(filesize_src-beginpos)))
     copylen=filesize_src-beginpos;
    if(!copylen){
     retcode=MPXPLAY_ERROR_INFILE_EOF;
     break;
    }
    copybuf=pds_malloc(65536+32);
    if(!copybuf){
     retcode=MPXPLAY_ERROR_INFILE_MEMORY;
     break;
    }
    copypos=beginpos;
    last_percent=-1;
    if(frp->pei)
     display_message(1,0,pds_getfilename_any_from_fullname(frp->pei->filename));
    do{
     long blocksize,i,percent;
     percent=(long)(100.0*(float)copypos/(float)filesize_src);
     if(percent!=last_percent){
      sprintf(sout,"Duplicating file (%2d%%) ... (ESC to abort)",percent);
      display_message(0,0,sout);
      last_percent=percent;
      if(pds_look_extgetch()==KEY_ESC){
       pds_extgetch();
       retcode=MPXPLAY_ERROR_FILEHAND_USERABORT;
       break;
      }
     }
     if(copylen>65536)
      blocksize=65536;
     else
      blocksize=copylen;
     i=fbfs->fread(frp,copybuf,blocksize);
     //if(i!=blocksize){
     if((i<=0) || (i>blocksize)){
      if(i<0)
       retcode=i;
      else
       retcode=MPXPLAY_ERROR_INFILE_EOF;
      break;
     }
     blocksize=i;
     i=fbfs->fwrite(frp_dest,copybuf,blocksize);
     if(i!=blocksize){
      if(i<0)
       retcode=i;
      else
       retcode=MPXPLAY_ERROR_FILEHAND_CANTWRITE;
      break;
     }
     copylen-=blocksize;
     copypos+=blocksize;
    }while(copylen);
    clear_message();
    pds_free(copybuf);
    break;
   }
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_TMPOPEN:
    if(!argp1)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->mdds || !frp->pei || !frp->pei->filename)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    pds_getpath_from_fullname(tmpfile_filename,frp->pei->filename);
    i=pds_strlen(tmpfile_filename);
    if(tmpfile_filename[i-1]!=PDS_DIRECTORY_SEPARATOR_CHAR)
     i+=pds_strcpy(&tmpfile_filename[i],PDS_DIRECTORY_SEPARATOR_STR);
    pds_strcpy(&tmpfile_filename[i],"MPXPLAY.TMP");
    mpxplay_diskdrive_unlink(frp->mdds,tmpfile_filename);
    retcode=mpxplay_infile_control_cb(cb_data,MPXPLAY_CFGFUNCNUM_INFILE_FILE_OPEN|O_CREAT|O_TRUNC|O_RDWR|O_BINARY,argp1,tmpfile_filename);
    break;
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_TMPXCHCLOSE:
    {
    struct mpxpframe_s *frp_primary=NULL;
    //unsigned int playc_save=playcontrol;
    if(!argp1)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->mdds || !frp->pei || !frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    pds_strcpy(original_filename,frp->pei->filename);
    frp_new=(struct mpxpframe_s *)*((void **)argp1);
    if(!frp_new->pei)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    pds_strcpy(tmpfile_filename,frp_new->pei->filename);
    mpxplay_infile_control_cb(frp,MPXPLAY_CFGFUNCNUM_INFILE_FILE_CLOSE,argp1,NULL);
    frp->filebuf_funcs->fclose(frp);
    if(mvps.fr_primary->filehand_datas && (pds_utf8_stricmp(mvps.pei0->filename,original_filename)==0)){
     frp_primary=mvps.fr_primary;
     if(frp_primary->infile_funcs && frp_primary->filebuf_funcs && frp_primary->infile_infos){
      AU_suspend_decoding(mvps.aui);
      //AU_stop(mvps.aui);
      //mpxplay_infile_suspend_close(frp0);
      frp_primary->infile_funcs->close(frp_primary->filebuf_funcs,frp_primary,frp_primary->infile_infos);
      mpxplay_infile_reset(frp_primary);
      frp_primary->filepos=0;
     }else
      frp_primary=NULL;
    }
    retcode=mpxplay_diskdrive_unlink(frp->mdds,original_filename);
    if(retcode==MPXPLAY_ERROR_OK)
     retcode=mpxplay_diskdrive_rename(frp->mdds,tmpfile_filename,original_filename);
    else
     mpxplay_diskdrive_unlink(frp->mdds,tmpfile_filename);
    if(frp_primary){
     /*if(mpxplay_infile_avfile_open(frp0,original_filename,MPXPLAY_INFILE_OPENMODE_PLAY,frp0->frameNum))
      if(playc_save&PLAYC_RUNNING)
       AU_prestart(mvps.aui);*/
     if(frp_primary->infile_funcs->open(frp_primary->filebuf_funcs,frp_primary,original_filename,frp_primary->infile_infos,MPXPLAY_INFILE_OPENMODE_PLAY)==MPXPLAY_ERROR_INFILE_OK){
      mpxplay_infile_fseek(frp_primary,frp_primary->frameNum);
      mpxplay_mpxinbuf_buffer_protected_check(frp_primary);
      AU_resume_decoding(mvps.aui);
      mvps.adone=mvps.fdone=0;
     }
    }
    break;
    }
   case MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_FINAME_GET:
    if(!argp1 || !argp2)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->pei)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    i=*((unsigned long *)argp2);
    if(!i)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    p=(char *)argp1;
    pds_strncpy(p,frp->pei->filename,i-1);
    p[i-1]=0;
    break;
   case MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_RBLOCKSIZE:
    mdds = mpxplay_diskdrive_file_get_mdds(frp->filehand_datas); // FIXME: clarify frp->mdds
    if(!mdds)
     mdds = frp->mdds;
    return mpxplay_diskdrive_drive_config(mdds, MPXPLAY_DISKDRIV_CFGFUNCNUM_GET_CHKBUFBLOCKBYTES, NULL, NULL);
   case MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_ISSEEKABLE:
    if((frp->filetype&HFT_STREAM) || (frp->buffertype&PREBUFTYPE_NON_SEEKABLE))
     return 0;
    return 1;
   case MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_ISSTREAM:
    if(frp->filetype&HFT_STREAM)
     return 1;
    return 0;
#ifdef MPXPLAY_GUI_QT
   case MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_PLAYSPEED_SET: // argp1:unsigned long *speed (0 - 1000 - 10000)
    mpxplay_debugf(MPXPLAY_DEBUGOUT_CB, "CB SPD frp:%8.8X t:%8.8X", (unsigned long)frp, frp->filetype);
    if(!(frp->filetype&HFT_FILE_EXT))
     break;
    i=*((long*)argp1);
    mpxplay_dispqt_video_set_speed(i);
    break;
   case MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_PLAYVOLUME_SET: // argp1:unsigned long *volume (0 - 700)
	mpxplay_debugf(MPXPLAY_DEBUGOUT_CB, "CB VOL frp:%8.8X t:%8.8X", (unsigned long)frp, frp->filetype);
    if(!(frp->filetype&HFT_FILE_EXT))
     break;
    i=*((long*)argp1);
    mpxplay_dispqt_video_set_volume(i);
    break;
#endif
   default:retcode = MPXPLAY_ERROR_CFGFUNC_UNSUPPFUNC; break;
  }
 }

 mpxplay_debugf(MPXPLAY_DEBUGOUT_CB,"icb end %d %d", counter, retcode);
 return retcode;
}

int mpxplay_infile_controlcb_modify_pei_by_filename(unsigned int cb_cmd, struct playlist_side_info *psi, struct playlist_entry_info *pei, char *filename, char *str_data, int str_len)
{
 mpxp_uint32_t texttype, i3index;
 int len;
 char cnvtmp[MPXPLAY_TEXTCONV_MAX_ID3LEN];

 if(!pei || !psi)
  return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
 if(!filename || !str_data)
  return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
 texttype = MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_UNPACKFUNC_TEXTTYPE(cb_cmd);
 i3index = MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_UNPACKFUNC_I3I(cb_cmd);
 if(i3index > I3I_MAX)
  return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
 cb_cmd &= MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_FUNCMASK;
 len = mpxplay_playlist_textconv_by_texttypes(MPXPLAY_TEXTCONV_TYPES_PUT(texttype,MPXPLAY_TEXTCONV_TYPE_MPXPLAY), str_data, str_len, cnvtmp, sizeof(cnvtmp)-4);
 if(len > 0)
  len = pds_str_clean(cnvtmp);

 // update playlist entry
 if((pds_strcmp(filename, pei->filename) != 0) && (psi->mvp->aktfilenum >= psi->firstsong))
  pei = psi->mvp->aktfilenum; // TODO: !!! (for all caller?)
 if(pei && (pds_strcmp(filename, pei->filename) == 0) && (pds_strcmp(cnvtmp, pei->id3info[i3index]) != 0)){
  if(pei->id3info[i3index] && (cb_cmd == MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_UPDATE)){
   playlist_editlist_del_id3_one(psi,pei,i3index);
   funcbit_enable(psi->editloadtype, PLL_CHG_ID3);
  }
  if(!pei->id3info[i3index]){ // TAG_PUT or TAG_UPDATE
   playlist_editlist_add_id3_one(psi, pei, i3index, cnvtmp, len);
   funcbit_enable(psi->editloadtype, PLL_CHG_ID3);
  }
  display_draw_editor_oneline(psi, pei, -1);
 }
 // update currently played
 if(cb_cmd == MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_UPDATE){
  pei = psi->mvp->pei0;
  if((pds_strcmp(filename, pei->filename) == 0) && (pds_strcmp(cnvtmp, pei->id3info[i3index]) != 0)){
   if(pei->infobits & PEIF_ALLOCATED){ // !!! only at dynamic playlist allocation
    playlist_editlist_add_id3_one(psi, pei, i3index, cnvtmp, len);  // !!! hack (without playlist_pei0_set)
    refdisp|=RDT_ID3INFO|RDT_HEADER;    // FIXME: RDT_BROWSER?
   }
  }
 }
 return MPXPLAY_ERROR_OK;
}

//-------------------------------------------------------------------------
static unsigned int infile_initialized;

void mpxplay_infile_init(struct mainvars *mvp)
{
 struct mpxpframe_s *frp=mvp->fr_base;
 unsigned int i=0;

 do{
  struct mpxplay_infile_info_s *miis=&infile_infos[i];
  frp->mvp=mvp;
  frp->infile_infos=miis;
  miis->control_cb=&mpxplay_infile_control_cb;
  miis->ccb_data=frp;
  miis->textconv_func=&mpxplay_playlist_textconv_by_texttypes;
  miis->audio_stream=&audiopacket_infos[i];
  miis->audio_stream->stream_select=stream_select_audio;
  miis->audio_decoder_infos=&audiodecoder_infos[i];
#ifdef MPXPLAY_LINK_INFILE_FF_MPEG
  miis->video_stream=&videopacket_infos[i];
#endif
#ifdef MPXPLAY_LINK_VIDEO
  miis->video_decoder_infos=&videodecoder_infos[i];
#endif
  mpxplay_decoders_alloc(frp,((i<2)? 1:0));
  mpxplay_infile_clear_infos_all(miis, frp);
  frp++;
  i++;
 }while(i<3);

 for(i=0;i<=LAST_INFILE_FUNCNUM;i++){
  if(all_infile_funcs[i]->preinit)
   all_infile_funcs[i]->preinit();
 }

 mpxplay_decoders_preinit();

 //i=pds_getdate();
 //sprintf(&mpxplay_tag_year[0],"%4d/%2.2d",i>>16,(i>>8)&0xff);
 infile_initialized=1;
}

void mpxplay_infile_deinit(struct mainvars *mvp)
{
 struct mpxpframe_s *frp = mvp->fr_base;
 unsigned int i;

 if(!infile_initialized || !frp)
  return;

 for(i=0;i<2;i++,frp++){
  mpxplay_infile_close(frp);
  mpxplay_decoders_free(frp->infile_infos,frp);
 }

 for(i=0;i<=LAST_INFILE_FUNCNUM;i++){
  if(all_infile_funcs[i]->deinit)
   all_infile_funcs[i]->deinit();
 }

 mpxplay_decoders_deinit();
}

// allocates a non-static frame (file/decoder) info structure (!!! clear the temporary (stack) frp field before calling)
struct mpxpframe_s *mpxplay_infile_frame_alloc(struct mpxpframe_s *frp)
{
 struct mpxplay_infile_info_s *miis;

 frp->infile_funcs = NULL;

 mpxplay_mpxinbuf_assign_funcs(frp);
 if(!mpxplay_mpxinbuf_alloc_ringbuffer(NULL,frp,PREBUFFERBLOCKS_SHORTRING))
  goto err_out_fa;

 if(!frp->infile_infos){
  miis=(struct mpxplay_infile_info_s *)pds_calloc(1,sizeof(*frp->infile_infos));
  if(!miis)
   goto err_out_fa;
  frp->infile_infos=miis;
 }else
  miis=frp->infile_infos;

 miis->control_cb=&mpxplay_infile_control_cb;
 miis->ccb_data=frp;
 miis->textconv_func=&mpxplay_playlist_textconv_by_texttypes;
 if(!miis->audio_stream){
  miis->audio_stream=(struct mpxplay_streampacket_info_s *)pds_calloc(1,sizeof(*miis->audio_stream));
  if(!miis->audio_stream)
   goto err_out_fa;
 }

 miis->audio_stream->stream_select=stream_select_audio;
 if(!miis->audio_decoder_infos){
  miis->audio_decoder_infos=(struct mpxplay_audio_decoder_info_s *)pds_calloc(1,sizeof(*miis->audio_decoder_infos));
  if(!miis->audio_decoder_infos)
   goto err_out_fa;
 }

#ifdef MPXPLAY_LINK_INFILE_FF_MPEG
 if(!miis->video_stream){
  miis->video_stream=(struct mpxplay_streampacket_info_s *)pds_calloc(1,sizeof(*miis->video_stream));
  if(!miis->video_stream)
   goto err_out_fa;
 }
#endif
#ifdef MPXPLAY_LINK_VIDEO
 if(!miis->video_decoder_infos){
  miis->video_decoder_infos=(struct mpxplay_video_decoder_info_s *)pds_calloc(1,sizeof(*miis->video_decoder_infos));
  if(!miis->video_decoder_infos)
   goto err_out_fa;
 }
#endif
 mpxplay_decoders_alloc(frp,1);

 clear_infile_infos(miis,frp);

 return frp;

err_out_fa:
 mpxplay_infile_frame_free(frp);
 return NULL;
}

/*struct mpxpframe_s *mpxplay_infile_frame_alloc(struct mpxpframe_s *frp)
{
 struct mpxplay_infile_info_s *miis;

 pds_memset(frp,0,sizeof(struct mpxpframe_s));
 mpxplay_mpxinbuf_assign_funcs(frp);
 if(!mpxplay_mpxinbuf_alloc_ringbuffer(NULL,frp,PREBUFFERBLOCKS_SHORTRING))
  goto err_out_fa;
 miis=(struct mpxplay_infile_info_s *)pds_calloc(1,sizeof(*frp->infile_infos));
 if(!miis)
  goto err_out_fa;
 frp->infile_infos=miis;
 miis->control_cb=&mpxplay_infile_control_cb;
 miis->ccb_data=frp;
 miis->textconv_func=&mpxplay_playlist_textconv_by_texttypes;
 miis->audio_stream=(struct mpxplay_streampacket_info_s *)pds_calloc(1,sizeof(*miis->audio_stream));
 if(!miis->audio_stream)
  goto err_out_fa;
 miis->audio_stream->stream_select=stream_select_audio;
 miis->audio_decoder_infos=(struct mpxplay_audio_decoder_info_s *)pds_calloc(1,sizeof(*miis->audio_decoder_infos));
 if(!miis->audio_decoder_infos)
  goto err_out_fa;
#ifdef MPXPLAY_LINK_VIDEO
 miis->video_stream=(struct mpxplay_streampacket_info_s *)pds_calloc(1,sizeof(*miis->video_stream));
 if(!miis->video_stream)
  goto err_out_fa;
 miis->video_decoder_infos=(struct mpxplay_video_decoder_info_s *)pds_calloc(1,sizeof(*miis->video_decoder_infos));
 if(!miis->video_decoder_infos)
  goto err_out_fa;
#endif
 mpxplay_decoders_alloc(frp,1);

 clear_infile_infos(miis,frp);

 return frp;

err_out_fa:
 mpxplay_infile_frame_free(frp);
 return NULL;
}*/

void mpxplay_infile_frame_free(struct mpxpframe_s *frp)
{
 struct mpxplay_infile_info_s *miis;
 if(frp){
  miis=frp->infile_infos;
  if(miis){
   mpxplay_infile_close(frp); // ???
   mpxplay_decoders_free(miis,frp);
   if(miis->audio_stream)
    pds_free(miis->audio_stream);
   if(miis->audio_decoder_infos)
    pds_free(miis->audio_decoder_infos);
#ifdef MPXPLAY_LINK_INFILE_FF_MPEG
   if(miis->video_stream)
    pds_free(miis->video_stream);
#endif
#ifdef MPXPLAY_LINK_VIDEO
   if(miis->video_decoder_infos)
    pds_free(miis->video_decoder_infos);
#endif
   pds_free(miis);
   frp->infile_infos=NULL;
  }
  if(frp->prebufferbegin)
   pds_free(frp->prebufferbegin);
  if(frp->pcmdec_buffer)
   pds_free(frp->pcmdec_buffer);
  if(frp->pcmout_buffer)
   pds_free(frp->pcmout_buffer);
  pds_memset(frp,0,sizeof(struct mpxpframe_s));
 }
}
