//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2018 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: input file buffering

//#define MPXPLAY_USE_DEBUGF 1
#define MPXPLAY_DEBUG_WARNING stderr
#define MPXPLAY_DEBUG_OUTPUT stdout
#define MPXPLAY_DEBUGOUT_ALLOC NULL // stdout
#define MPXPLAY_DEBUGOUT_MUTEX NULL // stdout
#define MPXPLAY_DEBUGOUT_FILLNESS NULL //stdout
#define MPXPLAY_DEBUGOUT_SEEKALL NULL //stdout
#define MPXPLAY_DEBUGOUT_SEEKLOW NULL //stdout
#define MPXPLAY_DEBUGOUT_SEEKBUF NULL // stdout
#define MPXPLAY_DEBUGOUT_FILL    NULL // stdout
#define MPXPLAY_DEBUGOUT_FREAD   stdout

#include "mpxplay.h"
#include "mpxinbuf.h"
#include "diskdriv\diskdriv.h"
#include "display\display.h"

static unsigned int mpxinbuf_alloc_fullbuffer(struct mainvars *mvp,struct mpxpframe_s *frp);
static unsigned int mpxinbuf_ringbuffer_fill(struct mpxpframe_s *);
static mpxp_filesize_t mpxplay_mpxinbuf_ringbuffer_advance(struct mpxpframe_s *frp,mpxp_filesize_t newpos_absolute);

static long mpxinbuf_lowlevel_file_read(struct mpxpframe_s *frp,char *ptr,unsigned int num);
static mpxp_filesize_t mpxinbuf_lowlevel_file_seek(struct mpxpframe_s *frp,mpxp_filesize_t newpos_absolute);
static unsigned int  mpxinbuf_lowlevel_file_eof(struct mpxpframe_s *frp);
static int           mpxinbuf_lowlevel_file_chsize(struct mpxpframe_s *frp,mpxp_filesize_t offset);

static void          mpxplay_mpxinbuf_fclose(void *fbds);
static unsigned long mpxplay_mpxinbuf_fread(void *fbds,void *ptr,unsigned long num);
static long          mpxplay_mpxinbuf_fwrite(void *fbds,void *ptr,unsigned long num);
static mpxp_filesize_t mpxplay_mpxinbuf_seek_unbuffered(struct mpxpframe_s *frp,mpxp_filesize_t newpos_absolute);
static mpxp_filesize_t mpxplay_mpxinbuf_fseek(void *fbds,mpxp_filesize_t offset,int whence);
static mpxp_filesize_t mpxplay_mpxinbuf_ftell(void *fbds);
static mpxp_filesize_t mpxplay_mpxinbuf_filelength(void *fbds);
static int           mpxplay_mpxinbuf_feof(void *fbds);
static int           mpxplay_mpxinbuf_chsize(void *fbds,mpxp_filesize_t offset);
static mpxp_int32_t  mpxplay_mpxinbuf_get_bufbytes(void *fbds,char *destbuf,mpxp_int32_t bytes);
static mpxp_uint32_t mpxplay_mpxinbuf_get_byte(void *fbds);
static mpxp_uint32_t mpxplay_mpxinbuf_get_le16(void *fbds);
static mpxp_uint32_t mpxplay_mpxinbuf_get_le24(void *fbds);
static mpxp_uint32_t mpxplay_mpxinbuf_get_le32(void *fbds);
static mpxp_uint64_t mpxplay_mpxinbuf_get_le64(void *fbds);
static mpxp_uint32_t mpxplay_mpxinbuf_get_be16(void *fbds);
static mpxp_uint32_t mpxplay_mpxinbuf_get_be24(void *fbds);
static mpxp_uint32_t mpxplay_mpxinbuf_get_be32(void *fbds);
static mpxp_uint64_t mpxplay_mpxinbuf_get_be64(void *fbds);
static long mpxplay_mpxinbuf_put_byte(void *fbds,unsigned int data);
static long mpxplay_mpxinbuf_put_le16(void *fbds,unsigned int data);
static long mpxplay_mpxinbuf_put_le24(void *fbds,mpxp_uint32_t data);
static long mpxplay_mpxinbuf_put_le32(void *fbds,mpxp_uint32_t data);
static long mpxplay_mpxinbuf_put_le64(void *fbds,mpxp_uint64_t data);
static long mpxplay_mpxinbuf_put_be16(void *fbds,unsigned int data);
static long mpxplay_mpxinbuf_put_be24(void *fbds,mpxp_uint32_t data);
static long mpxplay_mpxinbuf_put_be32(void *fbds,mpxp_uint32_t data);
static long mpxplay_mpxinbuf_put_be64(void *fbds,mpxp_uint64_t data);

static struct mpxplay_filehand_buffered_func_s mpxinbuf_functions={
 &mpxplay_mpxinbuf_fopen,
 &mpxplay_mpxinbuf_fclose,
 &mpxplay_mpxinbuf_fread,
 &mpxplay_mpxinbuf_fwrite,
 &mpxplay_mpxinbuf_fseek,
 &mpxplay_mpxinbuf_ftell,
 &mpxplay_mpxinbuf_filelength,
 &mpxplay_mpxinbuf_feof,
 &mpxplay_mpxinbuf_chsize,

 &mpxplay_mpxinbuf_get_bufbytes,
 &mpxplay_mpxinbuf_get_byte,
 &mpxplay_mpxinbuf_get_le16,
 &mpxplay_mpxinbuf_get_le24,
 &mpxplay_mpxinbuf_get_le32,
 &mpxplay_mpxinbuf_get_le64,
 &mpxplay_mpxinbuf_get_be16,
 &mpxplay_mpxinbuf_get_be24,
 &mpxplay_mpxinbuf_get_be32,
 &mpxplay_mpxinbuf_get_be64,

 &mpxplay_mpxinbuf_put_byte,
 &mpxplay_mpxinbuf_put_le16,
 &mpxplay_mpxinbuf_put_le24,
 &mpxplay_mpxinbuf_put_le32,
 &mpxplay_mpxinbuf_put_le64,
 &mpxplay_mpxinbuf_put_be16,
 &mpxplay_mpxinbuf_put_be24,
 &mpxplay_mpxinbuf_put_be32,
 &mpxplay_mpxinbuf_put_be64,
};

extern unsigned int prebuffertype,prebufferblocks;
extern unsigned int intsoundconfig,intsoundcontrol;
extern volatile unsigned long mpxplay_signal_events;
extern volatile unsigned int playcontrol;

void mpxplay_mpxinbuf_init(struct mainvars *mvp)
{
 struct mpxpframe_s *frp=mvp->fr_base;

 frp->filebuf_funcs=&mpxinbuf_functions;
 frp++;
 frp->filebuf_funcs=&mpxinbuf_functions;
 frp++;
 frp->filebuf_funcs=&mpxinbuf_functions;

 if(prebuffertype&PREBUFTYPE_MASK)
  mpxplay_mpxinbuf_alloc_ringbuffer(mvp,frp,PREBUFFERBLOCKS_SHORTRING);

 if(prebufferblocks>(0x7fffffff/PREBUFFERBLOCKSIZE_DECODE)) // int32 overflow in allocation
  prebufferblocks=0x7fffffff/PREBUFFERBLOCKSIZE_DECODE;
}

void mpxplay_mpxinbuf_assign_funcs(struct mpxpframe_s *frp)
{
 frp->filebuf_funcs=&mpxinbuf_functions;
}

void mpxplay_mpxinbuf_prealloc(struct mainvars *mvp)
{
 if(prebuffertype&PREBUFTYPE_RING){
  unsigned int i=2;
  struct mpxpframe_s *frp=mvp->fr_base;
  display_message(0,0,"Prebuffer memory allocation (a few seconds)");
  do{
   if(!mpxplay_mpxinbuf_alloc_ringbuffer(mvp,frp,prebufferblocks))
    mpxplay_close_program(MPXERROR_XMS_MEM);
#ifdef MPXINBUF_USE_MUTEX
   pds_threads_mutex_new(&frp->mutexhnd_frame);
#endif
#ifdef MPXINBUF_USE_FILLBUF_MUTEX
   pds_threads_mutex_new(&frp->mutexhnd_fillbuffer);
#endif
   frp++;
  }while(--i);
  clear_message();
 }
}

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

 do{
  if(frp->prebufferbegin){
   pds_free(frp->prebufferbegin);
   funcbit_smp_pointer_put(frp->prebufferbegin,NULL);
  }
#ifdef MPXINBUF_USE_MUTEX
  pds_threads_mutex_del(&frp->mutexhnd_frame);
#endif
#ifdef MPXINBUF_USE_FILLBUF_MUTEX
  pds_threads_mutex_del(&frp->mutexhnd_fillbuffer);
#endif
  frp++;
 }while(--i);
}

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

unsigned int mpxplay_mpxinbuf_alloc(struct mainvars *mvp,struct mpxpframe_s *frp)
{
 unsigned int ok=0;

 if((prebuffertype&PREBUFTYPE_FULL) && !(frp->filetype&HFT_STREAM))
  ok=mpxinbuf_alloc_fullbuffer(mvp,frp);
 if((prebuffertype&PREBUFTYPE_RING) || ((prebuffertype&PREBUFTYPE_FULL) && !ok))
  ok=mpxplay_mpxinbuf_alloc_ringbuffer(mvp,frp,prebufferblocks);
 if(!(prebuffertype&PREBUFTYPE_MASK)){
  funcbit_smp_enable(frp->buffertype,PREBUFTYPE_FILLED);
  ok=1;
 }
 funcbit_smp_filesize_put(frp->prebuffer_seek_retry,PREBUFFER_SEEKRETRY_INVALID);
 return ok;
}

#define FULLBUFFER_READ_BLOCKS 20

static unsigned int mpxinbuf_alloc_fullbuffer(struct mainvars *mvp,struct mpxpframe_s *frp)
{
 struct mpxpframe_s *frop;
 int blocksize,percent;
 char *pointer,sout[40];

 funcbit_smp_int32_put(frp->buffertype,PREBUFTYPE_NONE);
 if(playcontrol&PLAYC_RUNNING){
  AU_stop(mvp->aui);
  funcbit_enable(playcontrol,PLAYC_STARTNEXT);
 }
 frop=frp->fro;
 mpxplay_infile_close(frop);
 if(frop->prebufferbegin!=NULL){
  pds_free(frop->prebufferbegin);
  funcbit_smp_pointer_put(frop->prebufferbegin,NULL);
  funcbit_smp_int32_put(frop->buffertype,0);
 }
 if(frp->prebufferbegin!=NULL)
  pds_free(frp->prebufferbegin);
 funcbit_smp_pointer_put(frp->prebufferbegin,pds_malloc(frp->filesize));
 if(frp->prebufferbegin==NULL)
  return 0;
 blocksize=PREBUFFERBLOCKSIZE_DECODE;
 mpxplay_diskdrive_file_config(frp->filehand_datas,MPXPLAY_DISKFILE_CFGFUNCNUM_SET_FILEBLOCKSIZE,&blocksize,NULL);
 mpxinbuf_lowlevel_file_seek(frp,0);
 blocksize=frp->filesize/FULLBUFFER_READ_BLOCKS;
 if(blocksize){
  pointer=frp->prebufferbegin;
  for(percent=0;percent<100;percent+=(100/FULLBUFFER_READ_BLOCKS)){
   if(pds_look_extgetch()==KEY_ESC)
    break;
   sprintf(sout,"Loading song : %d%%",percent);
   display_message(0,0,sout);
   mpxinbuf_lowlevel_file_read(frp,pointer,blocksize);
   pointer+=blocksize;
  }
  if(pds_look_extgetch()==KEY_ESC){
   pds_extgetch();
   pds_free(frp->prebufferbegin);
   frp->prebufferbegin=NULL;
   return 0;
  }
 }
 mpxinbuf_lowlevel_file_read(frp,pointer,frp->filesize-(FULLBUFFER_READ_BLOCKS*blocksize));
 funcbit_smp_int32_put(frp->prebufferbytes_forward,frp->filesize);
 funcbit_smp_int32_put(frp->prebufferputp,frp->filesize);
 funcbit_smp_int32_put(frp->prebuffersize,frp->filesize);
 funcbit_smp_int32_put(frp->buffertype,PREBUFTYPE_FULL|PREBUFTYPE_FILLED);
 return 1;
}

unsigned int mpxplay_mpxinbuf_alloc_ringbuffer(struct mainvars *mvp,struct mpxpframe_s *frp,unsigned long blocks)
{
 if(blocks > PREBUFFERBLOCKS_SHORTRING){ // not called from in_file.c
  long drv_prebuffersize, drv_prereadbytes, drv_blocksize, drv_prebufferblocks;
  drv_prebuffersize = mpxplay_diskdrive_file_config(frp->filehand_datas, MPXPLAY_DISKDRIV_CFGFUNCNUM_GET_PREBUFFERSIZE, NULL, NULL);
  drv_prereadbytes = mpxplay_diskdrive_file_config(frp->filehand_datas, MPXPLAY_DISKDRIV_CFGFUNCNUM_GET_PREREADBUFBYTES, NULL, NULL);
  drv_blocksize = mpxplay_diskdrive_file_config(frp->filehand_datas, MPXPLAY_DISKDRIV_CFGFUNCNUM_GET_CHKBUFBLOCKBYTES, NULL, NULL);
  if(drv_blocksize > 0){
   frp->prebufferblocksize = drv_blocksize;
  }else{
   drv_blocksize = frp->prebufferblocksize;
   if(drv_blocksize <= 0)
    drv_blocksize = PREBUFFERBLOCKSIZE_DECODE;
  }
  drv_prebufferblocks = ((drv_prebuffersize > drv_prereadbytes)? drv_prebuffersize : (drv_prereadbytes * 2)) / drv_blocksize;
  if(blocks < drv_prebufferblocks)
   blocks = drv_prebufferblocks;
 }

 if(frp->prebufferblocksize < PREBUFFERBLOCKSIZE_DECODE)
  funcbit_smp_int32_put(frp->prebufferblocksize, PREBUFFERBLOCKSIZE_DECODE);
 if(frp->prebuffersize < (blocks * frp->prebufferblocksize))
 {
  funcbit_smp_int32_put(frp->prebuffersize, (blocks * frp->prebufferblocksize));
  if(frp->prebufferbegin){
   pds_free(frp->prebufferbegin);
   frp->prebufferbegin = NULL;
  }
 }

 if(frp->prebufferbegin==NULL){
  funcbit_smp_pointer_put(frp->prebufferbegin,pds_malloc(frp->prebuffersize+32));
  if(frp->prebufferbegin==NULL){
   struct mpxpframe_s *frop;
   if(mvp && (playcontrol&PLAYC_RUNNING)){
    AU_stop(mvp->aui);
    funcbit_enable(playcontrol,PLAYC_STARTNEXT);
   }
   frop=frp->fro;
   if(!frop || !frop->prebufferbegin)
    return 0;
   funcbit_smp_pointer_put(frp->prebufferbegin,frop->prebufferbegin);
   funcbit_smp_int32_put(frp->prebuffersize,frop->prebuffersize);  // ??? have to be the same
   funcbit_smp_pointer_put(frop->prebufferbegin,NULL);
   funcbit_smp_int32_put(frop->buffertype,0);
  }
 }
 funcbit_smp_disable(frp->buffertype,(~PREBUFTYPE_DONTCLEAR));
 if(blocks<=PREBUFFERBLOCKS_SHORTRING) // TODO: called from in_file.c (not correct decision)
  funcbit_smp_enable(frp->buffertype,PREBUFTYPE_RING);
 else
  funcbit_smp_enable(frp->buffertype,(PREBUFTYPE_RING|(prebuffertype&PREBUFTYPE_BACK)));
 mpxplay_diskdrive_file_config(frp->filehand_datas,MPXPLAY_DISKFILE_CFGFUNCNUM_SET_FILEBLOCKSIZE,&frp->prebufferblocksize,NULL);

 mpxplay_debugf(MPXPLAY_DEBUGOUT_ALLOC,"mpxplay_mpxinbuf_ALLOC_ringbuffer END fr:%8.8X bt:%8.8X bs:%d bls:%d bl:%d",
                (unsigned int)frp, frp->buffertype, frp->prebuffersize, frp->prebufferblocksize, blocks);

 return 1;
}

void mpxplay_mpxinbuf_set_intsound(struct mpxpframe_s *frp,unsigned int intcfg)
{
 if(intcfg&INTSOUND_DECODER)
  funcbit_smp_enable(frp->buffertype,PREBUFTYPE_INT);
}

unsigned int mpxplay_mpxinbuf_buffer_check(struct mpxpframe_s *frp, mpxp_bool_t wait_for_data) // returns 1 at eof/read-error
{
 unsigned int retcode = 1;
 unsigned long readwait_enable;
 mpxp_filesize_t seek_retry;

 readwait_enable = (wait_for_data)? 1 : 0;
 mpxplay_diskdrive_file_config(frp->filehand_datas, MPXPLAY_DISKFILE_CFGFUNCNUM_SET_READWAIT, &readwait_enable, NULL);

 funcbit_smp_enable(frp->buffertype,PREBUFTYPE_FILLED);
 seek_retry = frp->prebuffer_seek_retry;
 if(seek_retry >= ((mpxp_filesize_t)0)){ // post file seek (if it cannot be done on interrupt)
  funcbit_smp_filesize_put(frp->prebuffer_seek_retry,PREBUFFER_SEEKRETRY_INVALID);
  if((frp->buffertype&PREBUFTYPE_RING) && !(frp->buffertype&PREBUFTYPE_WRITEPROTECT)){
   if(mpxplay_mpxinbuf_ringbuffer_advance(frp,seek_retry)<0)
    goto err_out_bfc;
  }else{
   if(mpxplay_mpxinbuf_seek_unbuffered(frp,seek_retry)<0)
    goto err_out_bfc;
  }
 }

 if((frp->buffertype&PREBUFTYPE_RING) && !(frp->buffertype&PREBUFTYPE_WRITEPROTECT)){
  if( (((frp->prebuffersize-frp->prebufferbytes_forward)>frp->prebufferblocksize) && !(frp->buffertype&PREBUFTYPE_BACK))
   || ((frp->prebufferbytes_forward+frp->prebufferblocksize)<(frp->prebuffersize/100*PREBUFTYPE_GET_BACKBUF_PERCENT(prebuffertype)))
  ){
   if(mpxinbuf_ringbuffer_fill(frp) < frp->prebufferblocksize){ // fill buffer
    if(!(frp->filetype&HFT_STREAM) && !mpxinbuf_lowlevel_file_eof(frp))
     funcbit_smp_disable(frp->buffertype,PREBUFTYPE_FILLED);
    goto err_out_bfc;
   }
   funcbit_smp_disable(frp->buffertype,PREBUFTYPE_FILLED);
  }
  retcode=0;
 }

err_out_bfc:
 if(!wait_for_data){
  readwait_enable = 1;
  mpxplay_diskdrive_file_config(frp->filehand_datas, MPXPLAY_DISKFILE_CFGFUNCNUM_SET_READWAIT, &readwait_enable, NULL);
 }
 return retcode;
}

unsigned int mpxplay_mpxinbuf_buffer_protected_check(struct mpxpframe_s *frp) // returns 1 at eof/read-error
{
 unsigned int retcode = 0;

 if(frp->buffertype&PREBUFTYPE_BUFFFILL_NOCHK)
  return retcode;

#ifdef MPXINBUF_USE_FILLBUF_MUTEX
 if(PDS_THREADS_MUTEX_LOCK(&frp->mutexhnd_fillbuffer, MPXINBUF_FILLBUF_MUTEX_TIMEOUT) != MPXPLAY_ERROR_OK)
  return retcode;
#endif

 retcode = mpxplay_mpxinbuf_buffer_check(frp, FALSE);

#ifdef MPXINBUF_USE_FILLBUF_MUTEX
 PDS_THREADS_MUTEX_UNLOCK(&frp->mutexhnd_fillbuffer);
#endif

 return retcode;
}

unsigned int mpxplay_mpxinbuf_buffer_fillness_check(struct mainvars *mvp,struct mpxpframe_s *frp)
{
 unsigned int retcode = 0;

 if(frp->buffertype&PREBUFTYPE_BUFFFILL_NOCHK)
  return retcode;

#ifdef MPXINBUF_USE_FILLBUF_MUTEX
 mpxplay_debugf(MPXPLAY_DEBUGOUT_MUTEX,"mpxplay_mpxinbuf_buffer_fillness_check START frp:%8.8X mtx:%8.8X pbr:%7d pbf:%7d fill:%d",
   (unsigned int)frp, frp->mutexhnd_fillbuffer, frp->prebufferbytes_rewind, frp->prebufferbytes_forward,
   ((frp->buffertype&PREBUFTYPE_FILLED)? 1 : 0));
 if(PDS_THREADS_MUTEX_LOCK(&frp->mutexhnd_fillbuffer, MPXINBUF_FILLBUF_MUTEX_TIMEOUT) != MPXPLAY_ERROR_OK)
 {
  mpxplay_debugf(MPXPLAY_DEBUGOUT_MUTEX,"mpxplay_mpxinbuf_buffer_fillness_check LOCK failed %8.8X!", (unsigned int)frp);
  return retcode;
 }
 mpxplay_debugf(MPXPLAY_DEBUGOUT_MUTEX,"mpxplay_mpxinbuf_buffer_fillness_check LOCK end %8.8X", (unsigned int)frp);
#endif

 retcode = mpxplay_mpxinbuf_buffer_check(frp, FALSE);

 if(frp->filetype&HFT_STREAM){ // stream buffer fill check
  long prereadbytes=mpxplay_diskdrive_file_config(frp->filehand_datas,MPXPLAY_DISKDRIV_CFGFUNCNUM_GET_PREREADBUFBYTES,NULL,NULL);
//   mpxplay_debugf(MPXPLAY_DEBUGOUT_FILLNESS,"mpxplay_mpxinbuf_buffer_fillness_check BEGIN bf:%d r:%d p:%d pr:%d t:%8.8X frp:%8.8X",
//   frp->prebufferbytes_forward, retcode, (playcontrol & PLAYC_RUNNING), prereadbytes, frp->buffertype, (unsigned int)frp);
  char sout[100];
  if((prereadbytes>0) && (playcontrol&PLAYC_RUNNING) && (frp->prebufferbytes_forward<(prereadbytes/2))){ // buffer is low, stop playing
   AU_stop(mvp->aui);
   funcbit_enable(frp->buffertype,PREBUFTYPE_BUFFFILL_PAUSE);
   mpxplay_debugf(MPXPLAY_DEBUGOUT_FILLNESS,"mpxplay_mpxinbuf_buffer_fillness_check STOP bf:%d br:%d pr:%d r:%d t:%8.8X", frp->prebufferbytes_forward,
                  frp->prebufferbytes_rewind, prereadbytes, retcode, frp->buffertype);
  }else if(frp->buffertype&PREBUFTYPE_BUFFFILL_PAUSE){
   if(frp->prebufferbytes_forward>(prereadbytes*2)){ // buffer is filled, start playing
    AU_start(mvp->aui);
    funcbit_disable(frp->buffertype,PREBUFTYPE_BUFFFILL_PAUSE);
    clear_message();
    mpxplay_debugf(MPXPLAY_DEBUGOUT_FILLNESS,"mpxplay_mpxinbuf_buffer_fillness_check START bf:%d pr:%d r:%d", frp->prebufferbytes_forward, prereadbytes, retcode);
   }else if(!(playcontrol&PLAYC_RUNNING)){ // playing is automatically stopped
    if(!retcode){ // got data
     snprintf(sout,sizeof(sout),"Buffer fill: %d%%",(50*frp->prebufferbytes_forward/prereadbytes));
#ifdef MPXPLAY_GUI_CONSOLE
     display_message(1,0,sout);
#else
     display_message(0,0,sout);
#endif
    }else if(mpxinbuf_lowlevel_file_eof(frp)){ // can't read stream, skip to next entry
     funcbit_enable(playcontrol,PLAYC_STARTNEXT);
     funcbit_disable(frp->buffertype,PREBUFTYPE_BUFFFILL_PAUSE);
     clear_message();
     mvp->adone = ADONE_EOF;
    }
    //mpxplay_debugf(MPXPLAY_DEBUGOUT_FILLNESS,"mpxplay_mpxinbuf_buffer_fillness_check FILL bf:%d pr:%d r:%d", frp->prebufferbytes_forward, prereadbytes, retcode);
   }else{ // playing is manually started
    funcbit_disable(frp->buffertype,PREBUFTYPE_BUFFFILL_PAUSE);
    clear_message();
    //mpxplay_debugf(MPXPLAY_DEBUGOUT_FILLNESS,"mpxplay_mpxinbuf_buffer_fillness_check MANUAL bf:%d pr:%d r:%d", frp->prebufferbytes_forward, prereadbytes, retcode);
   }
  }
 }
#ifdef MPXINBUF_USE_FILLBUF_MUTEX
 mpxplay_debugf(MPXPLAY_DEBUGOUT_MUTEX,"mpxplay_mpxinbuf_buffer_fillness_check UNLOCK end %8.8X disk:%d", (unsigned int)frp,
    ((mpxplay_signal_events & MPXPLAY_SIGNALTYPE_DISKACCESS)? 1 : 0));
 PDS_THREADS_MUTEX_UNLOCK(&frp->mutexhnd_fillbuffer);
#endif

 return retcode;
}

//------------------------------------------------------------------------
//fill prebuffer
static void check_buffer_overflow(struct mpxpframe_s *frp)
{
 if(frp->prebufferputp<0)
  funcbit_smp_int32_put(frp->prebufferputp,(frp->prebufferputp+frp->prebuffersize));
 if(frp->prebufferputp<0)
  funcbit_smp_int32_put(frp->prebufferputp,0);
 if(frp->prebufferputp>=frp->prebuffersize)
  funcbit_smp_int32_put(frp->prebufferputp,(frp->prebufferputp-frp->prebuffersize));
 if(frp->prebufferputp>=frp->prebuffersize)
  funcbit_smp_int32_put(frp->prebufferputp,0);
 if(frp->prebufferbytes_forward>frp->prebuffersize)
  funcbit_smp_int32_put(frp->prebufferbytes_forward,frp->prebuffersize);
 if(frp->prebufferbytes_rewind>(frp->prebuffersize-frp->prebufferbytes_forward)) // ???
  funcbit_smp_int32_put(frp->prebufferbytes_rewind,(frp->prebuffersize-frp->prebufferbytes_forward));
}

static unsigned int mpxinbuf_ringbuffer_fill(struct mpxpframe_s *frp)
{
 long outbytes = 0;
//  mpxplay_debugf(MPXPLAY_DEBUGOUT_FILL,"mpxinbuf_ringbuffer_fill START frp:%8.8X fhfd:%8.8X fp:%lld fs:%lld eof:%d int08:%d", (unsigned int)frp,  (unsigned int)frp->filehand_datas,
//                 (mpxp_int64_t)frp->filepos, (mpxp_int64_t)frp->filesize, mpxinbuf_lowlevel_file_eof(frp), newfunc_newhandler08_is_current_thread());
 if(frp->filehand_datas &&
#ifdef MPXPLAY_FSIZE64
  ((frp->filepos<frp->filesize) || !mpxinbuf_lowlevel_file_eof(frp))// !!! dirty hack for Watcom/Win32 bug
#else
  !mpxinbuf_lowlevel_file_eof(frp)
#endif
 ){
  long i,j;

#ifdef MPXINBUF_USE_MUTEX
  if(frp->buffertype & PREBUFTYPE_INT)
   PDS_THREADS_MUTEX_LOCK(&frp->mutexhnd_frame, -1);
#endif
  check_buffer_overflow(frp);
  i=frp->prebuffersize-frp->prebufferputp;

  if(i<frp->prebufferblocksize){
   mpxplay_debugf(MPXPLAY_DEBUGOUT_FILL,"mpxinbuf_ringbuffer_fill LFR i:%5d putp:%d",i,frp->prebufferputp);
#ifdef MPXINBUF_USE_MUTEX
   if(frp->buffertype & PREBUFTYPE_INT)
    PDS_THREADS_MUTEX_UNLOCK(&frp->mutexhnd_frame);
#endif
   j=mpxinbuf_lowlevel_file_read(frp,&frp->prebufferbegin[frp->prebufferputp],i);
#ifdef MPXINBUF_USE_MUTEX
   if(frp->buffertype & PREBUFTYPE_INT)
    PDS_THREADS_MUTEX_LOCK(&frp->mutexhnd_frame, -1);
#endif
   //mpxplay_debugf(MPXPLAY_DEBUGOUT_FILL,"mpxinbuf_ringbuffer_fill RBF memr j:%5d putp:%d",j,frp->prebufferputp);
   pds_smp_memrefresh(&frp->prebufferbegin[frp->prebufferputp],j);
   if(j<i){
    funcbit_smp_int32_put(frp->prebufferputp,frp->prebufferputp+j);
    i=0;
   }else{
    funcbit_smp_int32_put(frp->prebufferputp,0);
    i=frp->prebufferblocksize-i;
   }
   outbytes=j;
  }else{
   i=frp->prebufferblocksize;
   outbytes=0;
  }
  if(i){
   //mpxplay_debugf(MPXPLAY_DEBUGOUT_FILL,"mpxinbuf_ringbuffer_fill rbflfr2 i:%5d p:%d g:%d f:%d r:%d",i,frp->prebufferputp,frp->prebuffergetp,frp->prebufferbytes_forward,frp->prebufferbytes_rewind);
#ifdef MPXINBUF_USE_MUTEX
   if(frp->buffertype & PREBUFTYPE_INT)
    PDS_THREADS_MUTEX_UNLOCK(&frp->mutexhnd_frame);
#endif
   j=mpxinbuf_lowlevel_file_read(frp,&frp->prebufferbegin[frp->prebufferputp],i);
#ifdef MPXINBUF_USE_MUTEX
   if(frp->buffertype & PREBUFTYPE_INT)
    PDS_THREADS_MUTEX_LOCK(&frp->mutexhnd_frame, -1);
#endif
   //mpxplay_debugf(MPXPLAY_DEBUGOUT_FILL,"mpxinbuf_ringbuffer_fill memr2 j:%5d putp:%d", j, frp->prebufferputp);
   pds_smp_memrefresh(&frp->prebufferbegin[frp->prebufferputp],j);
   funcbit_smp_int32_put(frp->prebufferputp,frp->prebufferputp+j);
   outbytes+=j;
  }
  funcbit_smp_int32_put(frp->prebufferbytes_forward,frp->prebufferbytes_forward+outbytes);

  check_buffer_overflow(frp);

#ifdef MPXINBUF_USE_MUTEX
  if(frp->buffertype & PREBUFTYPE_INT)
   PDS_THREADS_MUTEX_UNLOCK(&frp->mutexhnd_frame);
#endif
 }
 mpxplay_debugf(MPXPLAY_DEBUGOUT_FILL,"mpxinbuf_ringbuffer_fill END read:%d pbr:%5d pbf:%5d putp:%d getp:%d",outbytes,frp->prebufferbytes_rewind,frp->prebufferbytes_forward,frp->prebufferputp,frp->prebuffergetp);
 return outbytes;
}

// fills the buffer with the given data, returns the number of buffered bytes, or negative error code
int mpxplay_mpxinbuf_buffer_fill(struct mpxpframe_s *frp, mpxp_uint8_t *dataptr, unsigned int datalen)
{
 int requested_buffer_size;
 if(!frp || !dataptr || !datalen)
  return MPXPLAY_ERROR_MPXINBUF_ARGUMENT;
 requested_buffer_size = max((PREBUFFERBLOCKS_SHORTRING * PREBUFFERBLOCKSIZE_DECODE), datalen);
 if(!mpxplay_mpxinbuf_alloc_ringbuffer(frp->mvp, frp, requested_buffer_size / PREBUFFERBLOCKSIZE_DECODE))
  return MPXPLAY_ERROR_FILEHAND_MEMORY;
 if((frp->prebufferbytes_forward + datalen) > frp->prebuffersize)
  datalen = frp->prebuffersize - frp->prebufferbytes_forward;
 if(!datalen)
  return MPXPLAY_ERROR_FILEHAND_MEMORY;
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"mpxplay_mpxinbuf_buffer_fill COPY pbp:%d pbf:%d len:%d b:%8.8X bs:%d",
		 frp->prebufferputp, frp->prebufferbytes_forward, datalen, (unsigned int)frp->prebufferbegin, frp->prebuffersize);
 pds_memcpy(&frp->prebufferbegin[frp->prebufferputp], dataptr, datalen); // TODO:
 frp->prebufferputp += datalen;
 frp->prebufferbytes_forward += datalen;
 frp->filepos += datalen;
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"mpxplay_mpxinbuf_buffer_fill END");
 return datalen;
}

//-------------------------------------------------------------------------
// low level
static void mpxinbuf_lowlevel_file_open(struct mpxpframe_s *frp, char *filename, mpxp_uint32_t openmode)
{
 if(!frp->filehand_datas){
  struct mpxplay_filehand_low_func_s *lowfuncs = frp->filehand_funcs;
  if(lowfuncs){
   if(openmode&(O_WRONLY|O_RDWR|O_CREAT)){
    if(lowfuncs->open_write)
     frp->filehand_datas = lowfuncs->open_write(filename);
   }else if(openmode&O_RDONLY){
    if(lowfuncs->open_read)
     frp->filehand_datas = lowfuncs->open_read(filename);
   }
  }else
   frp->filehand_datas = mpxplay_diskdrive_file_open(frp->mdds, filename, openmode, &frp->last_errorcode);
 }else{ // else use an already open low level file in frp struct
  funcbit_enable(frp->buffertype, PREBUFTYPE_IND_LOWFILE_OPEN);
 }
}

static void mpxinbuf_lowlevel_file_close(struct mpxpframe_s *frp)
{
 if(!funcbit_test(frp->buffertype, PREBUFTYPE_IND_LOWFILE_OPEN)){
  struct mpxplay_filehand_low_func_s *lowfuncs = frp->filehand_funcs;
  if(lowfuncs){
   if(lowfuncs->close)
    lowfuncs->close(frp->filehand_datas);
  }else
   mpxplay_diskdrive_file_close(frp->filehand_datas);
 }
 frp->filehand_datas = NULL; // we should not re-use this at IND_LOWFILE neither
}

static long mpxinbuf_lowlevel_file_read(struct mpxpframe_s *frp,char *ptr,unsigned int num)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;
 long bytes=0;

 if(lowfuncs){
  if(lowfuncs->read)
   bytes=lowfuncs->read(frp->filehand_datas,ptr,num);
 }else
  bytes=mpxplay_diskdrive_file_read(frp->filehand_datas,ptr,num);
 if(bytes > 0){
  frp->last_errorcode = MPXPLAY_ERROR_OK;
  if(!(frp->filetype&HFT_STREAM)) // FIXME: this is a dirty hack
   funcbit_smp_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_DISKACCESS);
 }else if(bytes <= 0){
  frp->last_errorcode = bytes;
  bytes = 0;
 }
 funcbit_smp_int32_put(frp->filepos,frp->filepos+bytes);
 return bytes;
}

static long mpxinbuf_lowlevel_file_write(struct mpxpframe_s *frp,char *ptr,unsigned int num)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;
 long bytes=0;

 if(lowfuncs){
  if(lowfuncs->write)
   bytes=lowfuncs->write(frp->filehand_datas,ptr,num);
 }else
  bytes=mpxplay_diskdrive_file_write(frp->filehand_datas,ptr,num);
 if(bytes<0)
  bytes=0;
 else
  funcbit_smp_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_DISKACCESS);
 funcbit_smp_int32_put(frp->filepos,frp->filepos+bytes);
 if(frp->filepos>=frp->filesize)
  funcbit_smp_int32_put(frp->filesize,frp->filepos+1);
 return bytes;
}

static mpxp_filesize_t mpxinbuf_lowlevel_file_length(struct mpxpframe_s *frp)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;
 mpxp_filesize_t filelen=0;

 if(lowfuncs){
  if(lowfuncs->filelength)
   filelen=lowfuncs->filelength(frp->filehand_datas);
 }else
  filelen=mpxplay_diskdrive_file_length(frp->filehand_datas);
 return filelen;
}

static mpxp_filesize_t mpxinbuf_lowlevel_file_seek(struct mpxpframe_s *frp,mpxp_filesize_t newpos_absolute)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;
 mpxp_filesize_t filepos=0;

 if(lowfuncs){
  if(lowfuncs->seek)
   filepos=lowfuncs->seek(frp->filehand_datas,newpos_absolute,SEEK_SET);
 }else
  filepos=mpxplay_diskdrive_file_seek(frp->filehand_datas,newpos_absolute,SEEK_SET);

 if(filepos>=0){
  frp->filepos=filepos;
  funcbit_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_DISKACCESS);
 }

 mpxplay_debugf(MPXPLAY_DEBUGOUT_SEEKLOW,"mpxinbuf_lowlevel_file_seek: %lld",(mpxp_int64_t)filepos);

 if(filepos<0)
  return MPXPLAY_ERROR_MPXINBUF_SEEK_LOW;

 return filepos;
}

static unsigned int mpxinbuf_lowlevel_file_eof(struct mpxpframe_s *frp)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;
 int eof_flag=-1;

 if(!frp || !frp->filehand_datas)
  return 1;

 if(lowfuncs){
  if(lowfuncs->eof)
   eof_flag=lowfuncs->eof(frp->filehand_datas);
 }else
  eof_flag=mpxplay_diskdrive_file_eof(frp->filehand_datas);

// mpxplay_debugf(MPXPLAY_DEBUGOUT_SEEKLOW,"mpxinbuf_lowlevel_file_eof: %d fp:%lld fs:%lld",eof_flag,(mpxp_int64_t)frp->filepos,(mpxp_int64_t)frp->filesize);

 if(eof_flag<0)
  eof_flag=(frp->filepos>=frp->filesize)? 1:0;

 return eof_flag;
}

static int mpxinbuf_lowlevel_file_chsize(struct mpxpframe_s *frp,mpxp_filesize_t offset)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;
 int success=0;

 if(lowfuncs){
  if(lowfuncs->chsize)
   success=lowfuncs->chsize(frp->filehand_datas,offset);
 }else
  success=mpxplay_diskdrive_file_chsize(frp->filehand_datas,offset);

 return success;
}

//-------------------------------------------------------------------------
// buffered open & close
void mpxplay_mpxinbuf_buffer_reset(struct mpxpframe_s *frp)
{
 funcbit_smp_int32_put(frp->filepos,0);
 funcbit_smp_int32_put(frp->buffertype,0);
 funcbit_smp_int32_put(frp->prebuffergetp,0);
 funcbit_smp_int32_put(frp->prebufferputp,0);
 funcbit_smp_int32_put(frp->prebufferbytes_forward,0);
 funcbit_smp_int32_put(frp->prebufferbytes_rewind,0);
}

unsigned int mpxplay_mpxinbuf_fopen(void *fbds,char *filename,unsigned long openmode,unsigned long file_blocksize)
{
 struct mpxpframe_s *frp = fbds;
 struct mpxplay_diskdrive_data_s *mdds;
 long drive_blocksize, readwait;

 if(!frp)
  return 0;
 frp->last_errorcode = MPXPLAY_ERROR_OK;

 if(!frp->filebuf_funcs)
  mpxplay_mpxinbuf_assign_funcs(frp);

 if(!frp->mdds)
  frp->mdds = playlist_loaddir_filename_to_drivehandler(filename);

 mpxinbuf_lowlevel_file_open(frp, filename, openmode);
 if(!frp->filehand_datas)
  goto err_out_open;

 if(!funcbit_test(frp->buffertype, PREBUFTYPE_IND_LOWFILE_OPEN))
  mpxplay_mpxinbuf_buffer_reset(frp);

 mdds=mpxplay_diskdrive_file_get_mdds(frp->filehand_datas); // don't put this mdds into frp->mdds
 if(!mdds)
  mdds=frp->mdds;  // FIXME: clarify frp->mdds

 funcbit_smp_int32_put(frp->filesize,mpxinbuf_lowlevel_file_length(frp));

 if(!(openmode&(O_WRONLY|O_RDWR|O_CREAT|O_APPEND|O_TRUNC))){
  if(mpxplay_diskdrive_file_config(frp->filehand_datas,MPXPLAY_DISKFILE_CFGFUNCNUM_GET_ISLIVESTREAM,NULL,NULL) == 1)
   funcbit_smp_enable(frp->filetype, HFT_STREAM);

  if(openmode&MPXPLAY_INFILE_OPENMODE_LOAD_PLAY){
   if(!mpxplay_mpxinbuf_alloc(frp->mvp,frp))
    goto err_out_open;
  }else if(frp->prebufferbegin){
   funcbit_smp_disable(frp->buffertype,(~PREBUFTYPE_DONTCLEAR));
   funcbit_smp_enable(frp->buffertype,PREBUFTYPE_RING); // FIXME: hack, wrong place
  }

  if(funcbit_test(frp->filetype,HFT_STREAM) || (mpxplay_diskdrive_file_config(frp->filehand_datas,MPXPLAY_DISKFILE_CFGFUNCNUM_GET_ISNONSEEKABLE,NULL,NULL) == 1))
   funcbit_smp_enable(frp->buffertype, PREBUFTYPE_NON_SEEKABLE);

  drive_blocksize = mpxplay_diskdrive_drive_config(mdds,MPXPLAY_DISKDRIV_CFGFUNCNUM_GET_CHKBUFBLOCKBYTES,NULL,NULL);
  if(drive_blocksize <= 0)
   drive_blocksize = PREBUFFERBLOCKSIZE_CHECK;
  if(drive_blocksize < file_blocksize)
   drive_blocksize = file_blocksize;

  if(frp->prebufferbegin){
   if(drive_blocksize > (frp->prebuffersize / 2))
    drive_blocksize = frp->prebuffersize / 2;
   funcbit_smp_int32_put(frp->prebufferblocksize, drive_blocksize);
  }

  mpxplay_diskdrive_file_config(frp->filehand_datas, MPXPLAY_DISKFILE_CFGFUNCNUM_SET_FILEBLOCKSIZE, &drive_blocksize, NULL);
  if(!funcbit_test(frp->buffertype, PREBUFTYPE_WRITEPROTECT)){
   readwait = 1;
   mpxplay_diskdrive_file_config(frp->filehand_datas, MPXPLAY_DISKFILE_CFGFUNCNUM_SET_READWAIT, &readwait, NULL); // enabled is the default
  }
 }

 return 1;

err_out_open:
 mpxplay_mpxinbuf_fclose(frp);
 return 0;
}

static void mpxplay_mpxinbuf_fclose(void *fbds)
{
 struct mpxpframe_s *frp=fbds;
 if(!frp)
  return;
 mpxinbuf_lowlevel_file_close(frp);
 mpxplay_mpxinbuf_buffer_reset(frp); // !!! we clear buffer properties even at IND_LOWFILE
}

//-----------------------------------------------------------------------
//read & write
static long check_buffer_underrun(struct mpxpframe_s *frp,long num)
{
 if(frp->prebuffergetp<0)
  funcbit_smp_int32_put(frp->prebuffergetp,frp->prebuffergetp+frp->prebuffersize);
 if(frp->prebuffergetp<0)
  funcbit_smp_int32_put(frp->prebuffergetp,0);
 if(frp->prebuffergetp>=frp->prebuffersize)
  funcbit_smp_int32_put(frp->prebuffergetp,frp->prebuffergetp-frp->prebuffersize);
 if(frp->prebuffergetp>=frp->prebuffersize)
  funcbit_smp_int32_put(frp->prebuffergetp,0);

 if(frp->prebufferbytes_forward<0)
  funcbit_smp_int32_put(frp->prebufferbytes_forward,0);
 if(frp->prebufferbytes_forward>frp->prebuffersize)
  funcbit_smp_int32_put(frp->prebufferbytes_forward,frp->prebuffersize);

 if(frp->prebufferbytes_rewind>(frp->prebuffersize-frp->prebufferbytes_forward)) // ???
  funcbit_smp_int32_put(frp->prebufferbytes_rewind,frp->prebuffersize-frp->prebufferbytes_forward);

 if(num>frp->prebufferbytes_forward) // required!
  num=frp->prebufferbytes_forward;

 return num;
}

static inline unsigned int mpxinbuf_buffer_readcheck_block(struct mpxpframe_s *frp,unsigned int bytes)
{
 if((frp->prebufferbytes_forward>=bytes) && ((frp->prebuffergetp+bytes)<=frp->prebuffersize))
  return 1;
 return 0;
}

static unsigned long mpxinbuf_buffer_read(struct mpxpframe_s *frp,char *ptr,unsigned int num)
{
 long i=check_buffer_underrun(frp,num);
 if(i){
  if((frp->prebuffergetp+i)>=frp->prebuffersize){
   long j=frp->prebuffersize-frp->prebuffergetp;
   pds_memcpy(ptr,&frp->prebufferbegin[frp->prebuffergetp],j);
   pds_memcpy(ptr+j,&frp->prebufferbegin[0],i-j);
   funcbit_smp_int32_put(frp->prebuffergetp,i-j);
  }else{
   pds_memcpy(ptr,&frp->prebufferbegin[frp->prebuffergetp],i);
   funcbit_smp_int32_put(frp->prebuffergetp,frp->prebuffergetp+i);
  }
  funcbit_smp_int32_put(frp->prebufferbytes_forward,frp->prebufferbytes_forward-i);
  funcbit_smp_int32_put(frp->prebufferbytes_rewind,frp->prebufferbytes_rewind+i);
 }
 if(i<num)
  pds_memset(ptr+i,0,num-i);
 return i;
}

static unsigned long mpxplay_mpxinbuf_read_unbuffered(struct mpxpframe_s *frp,char *ptr,unsigned int num)
{
 unsigned long got_bytes = mpxinbuf_lowlevel_file_read(frp,ptr,num);
 if(got_bytes){
  funcbit_smp_int32_put(frp->prebuffergetp,0);
  funcbit_smp_int32_put(frp->prebufferputp,0);
  funcbit_smp_int32_put(frp->prebufferbytes_rewind,0);
  funcbit_smp_int32_put(frp->prebufferbytes_forward,0);
 }
 return got_bytes;
}

static unsigned long mpxplay_mpxinbuf_read_buffered(struct mpxpframe_s *frp, char *ptr, long num)
{
 unsigned long got_bytes = 0;
 while(num > 0){
  long curr;
  if((frp->prebufferbytes_forward <= (frp->prebufferblocksize * 2)) || (num > frp->prebufferbytes_forward)) // *2 to avoid auto stop at low buffer
   mpxplay_mpxinbuf_buffer_check(frp, TRUE);
  curr = mpxinbuf_buffer_read(frp,ptr + got_bytes,num);
  if(!curr){
   got_bytes += mpxplay_mpxinbuf_read_unbuffered(frp, ptr + got_bytes, num); // FIXME: clears buffer data
   mpxplay_debugf(MPXPLAY_DEBUG_WARNING,"mpxplay_mpxinbuf_read_buffered UNBUF frp:%8.8X pp:%5d gp:%5d r:%5d f:%5d n:%d bg:%d", (unsigned int)frp,
    frp->prebufferputp,frp->prebuffergetp,frp->prebufferbytes_rewind,frp->prebufferbytes_forward,num,got_bytes);
   break;
  }
  num -= curr;
  got_bytes += curr;
 }
 return got_bytes;
}

static unsigned long mpxplay_mpxinbuf_fread(void *fbds,void *ptr,unsigned long num)
{
 struct mpxpframe_s *frp=fbds;
 unsigned long bytes=0;
 if(!frp || !ptr || !num)
  return MPXPLAY_ERROR_MPXINBUF_ARGUMENT;
 if(num>4){
  //mpxplay_debugf(MPXPLAY_DEBUGOUT_FREAD,"mpxplay_mpxinbuf_fread BEGIN pp:%5d gp:%5d r:%5d f:%5d n:%d fp:%lld",frp->prebufferputp,frp->prebuffergetp,frp->prebufferbytes_rewind,frp->prebufferbytes_forward,num,(mpxp_int64_t)mpxplay_mpxinbuf_ftell(frp));
 }
 switch(frp->buffertype&PREBUFTYPE_MASK){
  case PREBUFTYPE_NONE:bytes=mpxplay_mpxinbuf_read_unbuffered(frp,(char *)ptr,num);break;
  case PREBUFTYPE_RING:
                       if(frp->buffertype&PREBUFTYPE_INT)
                        bytes=mpxinbuf_buffer_read(frp,(char *)ptr,num);
                       else
                        bytes=mpxplay_mpxinbuf_read_buffered(frp,(char *)ptr,num);
                       break;
  case PREBUFTYPE_FULL:bytes=mpxinbuf_buffer_read(frp,(char *)ptr,num);break;
 }
 if(num>=frp->prebuffersize){
  mpxplay_debugf(MPXPLAY_DEBUGOUT_FREAD,"mpxplay_mpxinbuf_fread END frp:%8.8X pp:%5d gp:%5d r:%5d f:%5d n:%d", (unsigned int)fbds,
   frp->prebufferputp,frp->prebuffergetp,frp->prebufferbytes_rewind,frp->prebufferbytes_forward,num);
 }
 return bytes;
}

static long mpxplay_mpxinbuf_fwrite(void *fbds,void *ptr,unsigned long num)
{
 struct mpxpframe_s *frp=fbds;
 if(!frp || !ptr || !num)
  return MPXPLAY_ERROR_MPXINBUF_ARGUMENT;
 //if(frp->prebufferbytes_forward) // ???
 // mpxplay_mpxinbuf_seek_unbuffered(frp,mpxplay_mpxinbuf_ftell(fbds)); // !!!
 return mpxinbuf_lowlevel_file_write(frp,(char *)ptr,num);
}

//-------------------------------------------------------------------------
// seek
static mpxp_filesize_t mpxplay_mpxinbuf_seek_unbuffered(struct mpxpframe_s *frp,mpxp_filesize_t newpos_absolute)
{
 mpxplay_debugf(MPXPLAY_DEBUGOUT_SEEKLOW,"mpxplay_mpxinbuf_seek_unbuffered na:%lld",(mpxp_int64_t)newpos_absolute);
 funcbit_smp_int32_put(frp->prebuffergetp,0);
 funcbit_smp_int32_put(frp->prebufferputp,0);
 funcbit_smp_int32_put(frp->prebufferbytes_rewind,0);
 funcbit_smp_int32_put(frp->prebufferbytes_forward,0);
 return mpxinbuf_lowlevel_file_seek(frp,newpos_absolute);
}

static inline void mpxplay_mpxinbuf_buffer_readskip_nochk(struct mpxpframe_s *frp,unsigned long bytes)
{
 funcbit_smp_int32_put(frp->prebufferbytes_forward,(frp->prebufferbytes_forward-bytes));
 funcbit_smp_int32_put(frp->prebufferbytes_rewind,(frp->prebufferbytes_rewind+bytes));
 funcbit_smp_int32_put(frp->prebuffergetp,(frp->prebuffergetp+bytes));
}

static mpxp_filesize_t mpxplay_mpxinbuf_ringbuffer_seek(struct mpxpframe_s *frp,mpxp_filesize_t newpos_absolute,mpxp_filesize_t newpos_relative)
{
 long pbr=frp->prebufferbytes_rewind,pbf=frp->prebufferbytes_forward,pbg;
 mpxp_filesize_t retcode;
#if 1
 if(frp->filetype&HFT_STREAM){ // TODO: check
  if((newpos_relative<0) && (newpos_relative<(-pbr))){
   newpos_absolute-=newpos_relative;
   newpos_relative=-pbr;
   newpos_absolute+=newpos_relative;
  }
 }
#endif
 if( ((newpos_relative>0) && (newpos_relative<pbf))
  || ((newpos_relative<0) && ((-newpos_relative)<=pbr))){
  pbr+=newpos_relative;
  pbf-=newpos_relative;
  pbg=frp->prebuffergetp+newpos_relative;
  if(pbg>=frp->prebuffersize) // at forward
   pbg-=frp->prebuffersize;
  if(pbg<0)                   // at rewind
   pbg+=frp->prebuffersize;
  funcbit_smp_int32_put(frp->prebuffergetp,pbg);
  retcode=newpos_absolute;
 }else{
  pbf=pbr=0;
  retcode=MPXPLAY_ERROR_MPXINBUF_SEEK_BUF;
 }
 if(frp->prebuffersize > 1500000){
  mpxplay_debugf(MPXPLAY_DEBUGOUT_SEEKBUF,"mpxplay_mpxinbuf_ringbuffer_seek END na:%8d nr:%8d ro:%6d fo:%6d rn:%6d fn:%6d bs:%d",
    (long)newpos_absolute, (long)newpos_relative, frp->prebufferbytes_rewind, frp->prebufferbytes_forward, pbr, pbf, frp->prebuffersize);
 }
 funcbit_smp_int32_put(frp->prebufferbytes_rewind,pbr);
 funcbit_smp_int32_put(frp->prebufferbytes_forward,pbf);
 return retcode;
}

static mpxp_filesize_t mpxplay_mpxinbuf_fullbuffer_seek(struct mpxpframe_s *frp,mpxp_filesize_t newpos_absolute)
{
 if(newpos_absolute<frp->prebuffersize){
  funcbit_smp_int32_put(frp->prebuffergetp,newpos_absolute);
  funcbit_smp_int32_put(frp->prebufferbytes_rewind,newpos_absolute);
  funcbit_smp_int32_put(frp->prebufferbytes_forward,(frp->prebuffersize-newpos_absolute));
  return newpos_absolute;
 }
 return MPXPLAY_ERROR_MPXINBUF_SEEK_EOF;
}

static mpxp_filesize_t mpxplay_mpxinbuf_ringbuffer_advance(struct mpxpframe_s *frp,mpxp_filesize_t newpos_absolute)
{
 mpxp_filesize_t filepos = mpxplay_mpxinbuf_ftell(frp);
 mpxp_filesize_t advance = newpos_absolute - filepos;

 mpxplay_debugf(MPXPLAY_DEBUGOUT_SEEKALL,"mpxplay_mpxinbuf_ringbuffer_advance BEGIN fp:%lld ad:%lld",(mpxp_int64_t)filepos,(mpxp_int64_t)advance);

 if((frp->buffertype&PREBUFTYPE_NON_SEEKABLE) && (newpos_absolute >= filepos) && (advance < frp->prebuffersize)){ // ???
  do{
   long skip;
   mpxinbuf_ringbuffer_fill(frp);
   skip = (frp->prebufferbytes_forward<advance)? frp->prebufferbytes_forward:advance;
   if(skip<=0)
    continue;
   filepos += skip;
   advance -= skip;
   if(mpxplay_mpxinbuf_ringbuffer_seek(frp, filepos, skip) >= newpos_absolute)
    break;
  }while(advance>0);
 }else if((newpos_absolute > filepos) && (newpos_absolute < (filepos + frp->prebufferblocksize))){
  long newgp = frp->prebufferputp,pbr;
  pbr = frp->prebufferbytes_rewind + frp->prebufferbytes_forward;
  if(pbr > (frp->prebuffersize - frp->prebufferblocksize))
   pbr = frp->prebuffersize - frp->prebufferblocksize;
  funcbit_smp_int32_put(frp->prebufferbytes_rewind, pbr);
  funcbit_smp_int32_put(frp->prebufferbytes_forward, 0);
  mpxplay_debugf(MPXPLAY_DEBUGOUT_SEEKALL,"mpxplay_mpxinbuf_ringbuffer_advance RBF pbr:%5d ad:%lld",pbr,(mpxp_int64_t)advance);
  mpxinbuf_ringbuffer_fill(frp);
  if(frp->prebufferbytes_forward <= advance)
   return MPXPLAY_ERROR_MPXINBUF_SEEK_LOW;
  funcbit_smp_int32_put(frp->prebuffergetp, (newgp+advance));
  funcbit_smp_int32_put(frp->prebufferbytes_forward, (frp->prebufferbytes_forward-advance));
  funcbit_smp_int32_put(frp->prebufferbytes_rewind, (frp->prebufferbytes_rewind+advance));
 }else{
  mpxplay_debugf(MPXPLAY_DEBUGOUT_SEEKALL,"mpxplay_mpxinbuf_ringbuffer_advance SU1 na:%lld",(mpxp_int64_t)newpos_absolute);
  if(mpxplay_mpxinbuf_seek_unbuffered(frp, newpos_absolute) < 0)
   return MPXPLAY_ERROR_MPXINBUF_SEEK_LOW;
  mpxinbuf_ringbuffer_fill(frp);
  if(!frp->prebufferbytes_forward)
   return MPXPLAY_ERROR_MPXINBUF_SEEK_LOW;
  mpxplay_debugf(MPXPLAY_DEBUGOUT_SEEKALL,"mpxplay_mpxinbuf_ringbuffer_advance SU2 na:%lld",(mpxp_int64_t)newpos_absolute);
 }
 return newpos_absolute;
}

static mpxp_filesize_t mpxplay_mpxinbuf_fseek(void *fbds,mpxp_filesize_t offset,int whence)
{
 mpxp_filesize_t newpos_absolute,newpos_relative,np,fp;
 struct mpxpframe_s *frp=fbds;

 if(!frp)
  return MPXPLAY_ERROR_MPXINBUF_ARGUMENT;

 fp = mpxplay_mpxinbuf_ftell(frp);

 switch(whence){
  case SEEK_SET:newpos_absolute=offset;newpos_relative=offset-fp;break;
  case SEEK_CUR:newpos_absolute=fp+offset;newpos_relative=offset;break;
  case SEEK_END:newpos_absolute=mpxplay_mpxinbuf_filelength(frp)+offset;newpos_relative=newpos_absolute-fp;break;
  default:return MPXPLAY_ERROR_MPXINBUF_SEEK_EOF; // program should never reach this point
 }

 //if(frp->prebuffersize > 1500000)
 {
  mpxplay_debugf(MPXPLAY_DEBUGOUT_SEEKBUF,"mpxplay_mpxinbuf_fseek BEGIN na:%5lld fp:%5lld ft:%5lld nr:%5lld o:%5lld w:%d pp:%5d gp:%5d r:%5d f:%5d",
   (mpxp_int64_t)newpos_absolute,(mpxp_int64_t)frp->filepos,(mpxp_int64_t)fp,(mpxp_int64_t)newpos_relative,
   (mpxp_int64_t)offset,whence,frp->prebufferputp,frp->prebuffergetp,frp->prebufferbytes_rewind,frp->prebufferbytes_forward);
 }

 if(!newpos_relative && !(frp->buffertype&PREBUFTYPE_FULL))
  goto err_out_fseek;

 if((newpos_absolute<0) || (!(frp->filetype&HFT_STREAM) && (newpos_absolute>mpxplay_mpxinbuf_filelength(frp)))){
  newpos_absolute=MPXPLAY_ERROR_MPXINBUF_SEEK_EOF;
  goto err_out_fseek;
 }

 switch(frp->buffertype&PREBUFTYPE_MASK){
  case PREBUFTYPE_NONE:
     return mpxplay_mpxinbuf_seek_unbuffered(frp,newpos_absolute);
  case PREBUFTYPE_FULL:
     return mpxplay_mpxinbuf_fullbuffer_seek(frp,newpos_absolute);
  case PREBUFTYPE_RING:
     np=mpxplay_mpxinbuf_ringbuffer_seek(frp,newpos_absolute,newpos_relative);
     if(np>=0){
      newpos_absolute=np;
      goto err_out_fseek;
     }
     if(frp->buffertype&PREBUFTYPE_INT){
      funcbit_smp_filesize_put(frp->prebuffer_seek_retry,newpos_absolute);
      return np;
     }
     if(frp->buffertype&PREBUFTYPE_WRITEPROTECT)
      return mpxplay_mpxinbuf_seek_unbuffered(frp,newpos_absolute);
     newpos_absolute=mpxplay_mpxinbuf_ringbuffer_advance(frp,newpos_absolute);
     mpxplay_debugf(MPXPLAY_DEBUGOUT_SEEKALL,"mpxplay_mpxinbuf_fseek RING na:%lld",(mpxp_int64_t)newpos_absolute);
     return newpos_absolute;
 }

err_out_fseek:
 mpxplay_debugf(MPXPLAY_DEBUGOUT_SEEKALL,"mpxplay_mpxinbuf_fseek ERR na:%5lld nr:%5lld pp:%5d gp:%5d r:%5d f:%5d fp:%lld",(mpxp_int64_t)newpos_absolute,(mpxp_int64_t)newpos_relative,
     frp->prebufferputp,frp->prebuffergetp,frp->prebufferbytes_rewind,frp->prebufferbytes_forward,(mpxp_int64_t)mpxplay_mpxinbuf_ftell(frp));
 return newpos_absolute;
}

mpxp_filesize_t mpxplay_mpxinbuf_ftell(void *fbds)
{
 struct mpxpframe_s *frp=fbds;
 if(!frp)
  return MPXPLAY_ERROR_MPXINBUF_ARGUMENT;
 return (frp->filepos-frp->prebufferbytes_forward);
}

static mpxp_filesize_t mpxplay_mpxinbuf_filelength(void *fbds)
{
 struct mpxpframe_s *frp=fbds;
 if(!frp)
  return MPXPLAY_ERROR_MPXINBUF_ARGUMENT;
 return (frp->filesize);
}

static int mpxplay_mpxinbuf_feof(void *fbds)
{
 struct mpxpframe_s *frp=fbds;
 if(!frp)
  return MPXPLAY_ERROR_MPXINBUF_ARGUMENT;
 return (((!frp->filehand_datas || (frp->filepos>=frp->filesize)) && !frp->prebufferbytes_forward)? 1:0);
}

static int mpxplay_mpxinbuf_chsize(void *fbds,mpxp_filesize_t offset)
{
 struct mpxpframe_s *frp=fbds;
 if(!frp)
  return MPXPLAY_ERROR_MPXINBUF_ARGUMENT;
 return mpxinbuf_lowlevel_file_chsize(frp,offset);
}

//-----------------------------------------------------------------------
static mpxp_int32_t mpxplay_mpxinbuf_get_bufbytes(void *fbds,char *destbuf,mpxp_int32_t bytes)
{
 struct mpxpframe_s *frp=fbds;
 if(mpxinbuf_buffer_readcheck_block(frp,bytes)){
  pds_memcpy(destbuf,&frp->prebufferbegin[frp->prebuffergetp],bytes);
  mpxplay_mpxinbuf_buffer_readskip_nochk(frp,bytes);
 }else{
  bytes=mpxplay_mpxinbuf_fread(frp,destbuf,bytes);
 }
 return bytes;
}

static mpxp_uint32_t mpxplay_mpxinbuf_get_byte(void *fbds)
{
 struct mpxpframe_s *frp=fbds;
 mpxp_uint8_t tmp;
 if(mpxinbuf_buffer_readcheck_block(frp,1)){
  tmp=PDS_GETB_8U(&frp->prebufferbegin[frp->prebuffergetp]);
  mpxplay_mpxinbuf_buffer_readskip_nochk(frp,1);
 }else{
  if(mpxplay_mpxinbuf_fread(frp,&tmp,1)!=1)
   tmp=0;
 }
 return ((mpxp_uint32_t)tmp);
}

static mpxp_uint32_t mpxplay_mpxinbuf_get_le16(void *fbds)
{
 struct mpxpframe_s *frp=fbds;
 mpxp_uint16_t tmp;
 if(mpxinbuf_buffer_readcheck_block(frp,2)){
  tmp=PDS_GETB_LEU16(&frp->prebufferbegin[frp->prebuffergetp]);
  mpxplay_mpxinbuf_buffer_readskip_nochk(frp,2);
 }else{
  if(mpxplay_mpxinbuf_fread(frp,&tmp,2)!=2)
   tmp=0;
 }
 return ((mpxp_uint32_t)tmp);
}

static mpxp_uint32_t mpxplay_mpxinbuf_get_le24(void *fbds)
{
 struct mpxpframe_s *frp=fbds;
 mpxp_uint32_t tmp;
 if(mpxinbuf_buffer_readcheck_block(frp,4)){
  tmp=PDS_GETB_LE24(&frp->prebufferbegin[frp->prebuffergetp]);
  mpxplay_mpxinbuf_buffer_readskip_nochk(frp,3);
 }else{
  if(mpxplay_mpxinbuf_fread(frp,&tmp,3)==3)
   tmp=PDS_GETB_LE24(&tmp);
  else
   tmp=0;
 }
 return tmp;
}

static mpxp_uint32_t mpxplay_mpxinbuf_get_le32(void *fbds)
{
 struct mpxpframe_s *frp=fbds;
 mpxp_uint32_t tmp;
 if(mpxinbuf_buffer_readcheck_block(frp,4)){
  tmp=PDS_GETB_LEU32(&frp->prebufferbegin[frp->prebuffergetp]);
  mpxplay_mpxinbuf_buffer_readskip_nochk(frp,4);
 }else{
  if(mpxplay_mpxinbuf_fread(frp,&tmp,4)!=4)
   tmp=0;
 }
 return tmp;
}

static mpxp_uint64_t mpxplay_mpxinbuf_get_le64(void *fbds)
{
 struct mpxpframe_s *frp=fbds;
 mpxp_uint64_t tmp;
 if(mpxinbuf_buffer_readcheck_block(frp,8)){
  tmp=PDS_GETB_LEU64(&frp->prebufferbegin[frp->prebuffergetp]);
  mpxplay_mpxinbuf_buffer_readskip_nochk(frp,8);
 }else{
  if(mpxplay_mpxinbuf_fread(frp,&tmp,8)!=8)
   tmp=0;
 }
 return tmp;
}

static mpxp_uint32_t mpxplay_mpxinbuf_get_be16(void *fbds)
{
 struct mpxpframe_s *frp=fbds;
 mpxp_uint16_t tmp;
 if(mpxinbuf_buffer_readcheck_block(frp,2)){
  tmp=PDS_GETB_LEU16(&frp->prebufferbegin[frp->prebuffergetp]);
  mpxplay_mpxinbuf_buffer_readskip_nochk(frp,2);
 }else{
  if(mpxplay_mpxinbuf_fread(frp,&tmp,2)!=2)
   tmp=0;
 }
 return (pds_bswap16(tmp));
}

static mpxp_uint32_t mpxplay_mpxinbuf_get_be24(void *fbds)
{
 struct mpxpframe_s *frp=fbds;
 mpxp_uint32_t tmp;
 if(mpxinbuf_buffer_readcheck_block(frp,4)){
  tmp=PDS_GETB_BE24(&frp->prebufferbegin[frp->prebuffergetp]);
  mpxplay_mpxinbuf_buffer_readskip_nochk(frp,3);
 }else{
  if(mpxplay_mpxinbuf_fread(frp,&tmp,3)==3)
   tmp=PDS_GETB_BE24(&tmp);
  else
   tmp=0;
 }
 return tmp;
}

static mpxp_uint32_t mpxplay_mpxinbuf_get_be32(void *fbds)
{
 struct mpxpframe_s *frp=fbds;
 mpxp_uint32_t tmp;
 if(mpxinbuf_buffer_readcheck_block(frp,4)){
  tmp=PDS_GETB_LEU32(&frp->prebufferbegin[frp->prebuffergetp]);
  mpxplay_mpxinbuf_buffer_readskip_nochk(frp,4);
 }else{
  if(mpxplay_mpxinbuf_fread(frp,&tmp,4)!=4)
   tmp=0;
 }
 return (pds_bswap32(tmp));
}

static mpxp_uint64_t mpxplay_mpxinbuf_get_be64(void *fbds)
{
 struct mpxpframe_s *frp=fbds;
 mpxp_uint64_t tmp;
 if(mpxinbuf_buffer_readcheck_block(frp,8)){
  tmp=PDS_GETB_BEU64(&frp->prebufferbegin[frp->prebuffergetp]);
  mpxplay_mpxinbuf_buffer_readskip_nochk(frp,8);
 }else{
  if(mpxplay_mpxinbuf_fread(frp,&tmp,8)==8)
   tmp=PDS_GETB_BEU64(&tmp);
  else
   tmp=0;
 }
 return tmp;
}

static long mpxplay_mpxinbuf_put_byte(void *fbds,unsigned int data)
{
 mpxp_uint8_t tmp=data;
 return mpxplay_mpxinbuf_fwrite(fbds,&tmp,1);
}

static long mpxplay_mpxinbuf_put_le16(void *fbds,unsigned int data)
{
 mpxp_uint16_t tmp=data;
 return mpxplay_mpxinbuf_fwrite(fbds,&tmp,2);
}

static long mpxplay_mpxinbuf_put_le24(void *fbds,mpxp_uint32_t data)
{
 mpxp_uint32_t tmp=PDS_GETB_LE24(&data);
 return mpxplay_mpxinbuf_fwrite(fbds,&tmp,3);
}

static long mpxplay_mpxinbuf_put_le32(void *fbds,mpxp_uint32_t data)
{
 mpxp_uint32_t tmp=data;
 return mpxplay_mpxinbuf_fwrite(fbds,&tmp,4);
}

static long mpxplay_mpxinbuf_put_le64(void *fbds,mpxp_uint64_t data)
{
 mpxp_uint64_t tmp=data;
 return mpxplay_mpxinbuf_fwrite(fbds,&tmp,8);
}

static long mpxplay_mpxinbuf_put_be16(void *fbds,unsigned int data)
{
 mpxp_uint16_t tmp;
 PDS_PUTB_BEU16(&tmp,data);
 return mpxplay_mpxinbuf_fwrite(fbds,&tmp,2);
}

static long mpxplay_mpxinbuf_put_be24(void *fbds,mpxp_uint32_t data)
{
 mpxp_uint32_t tmp=PDS_GETB_BE24(&data);
 return mpxplay_mpxinbuf_fwrite(fbds,&tmp,3);
}

static long mpxplay_mpxinbuf_put_be32(void *fbds,mpxp_uint32_t data)
{
 mpxp_uint32_t tmp;
 PDS_PUTB_BEU32(&tmp,data);
 return mpxplay_mpxinbuf_fwrite(fbds,&tmp,4);
}

static long mpxplay_mpxinbuf_put_be64(void *fbds,mpxp_uint64_t data)
{
 mpxp_uint64_t tmp;
 PDS_PUTB_BEU64(&tmp,data);
 return mpxplay_mpxinbuf_fwrite(fbds,&tmp,8);
}

//------------------------------------------------------------------------
static struct mpxpframe_s fr_seek_helper;

struct mpxpframe_s *mpxplay_mpxinbuf_seekhelper_init(struct mpxpframe_s *frp)
{
 unsigned int intsoundcntrl_save;
 MPXPLAY_INTSOUNDDECODER_DISALLOW;
#ifdef MPXINBUF_USE_MUTEX
 if(frp->buffertype & PREBUFTYPE_INT)
  PDS_THREADS_MUTEX_LOCK(&frp->mutexhnd_frame, -1);
#endif
 pds_memcpy((void *)&fr_seek_helper,(void *)frp,sizeof(struct mpxpframe_s));
 MPXPLAY_INTSOUNDDECODER_ALLOW;
#ifdef MPXINBUF_USE_MUTEX
 if(frp->buffertype & PREBUFTYPE_INT)
  PDS_THREADS_MUTEX_UNLOCK(&frp->mutexhnd_frame);
#endif
 funcbit_disable(fr_seek_helper.buffertype,PREBUFTYPE_INT);
 funcbit_enable(fr_seek_helper.buffertype,PREBUFTYPE_WRITEPROTECT);
 return (&fr_seek_helper);
}

void mpxplay_mpxinbuf_seekhelper_close(struct mpxpframe_s *frp)
{
 unsigned int intsoundcntrl_save;
 MPXPLAY_INTSOUNDDECODER_DISALLOW;
#ifdef MPXINBUF_USE_MUTEX
 if(frp->buffertype & PREBUFTYPE_INT)
  PDS_THREADS_MUTEX_LOCK(&frp->mutexhnd_frame, -1);
#endif
 funcbit_smp_int32_put(frp->prebuffergetp         ,fr_seek_helper.prebuffergetp);
 funcbit_smp_int32_put(frp->prebufferputp         ,fr_seek_helper.prebufferputp);
 funcbit_smp_int32_put(frp->prebufferbytes_rewind ,fr_seek_helper.prebufferbytes_rewind);
 funcbit_smp_int32_put(frp->prebufferbytes_forward,fr_seek_helper.prebufferbytes_forward);
 funcbit_smp_int32_put(frp->filepos               ,fr_seek_helper.filepos);
 MPXPLAY_INTSOUNDDECODER_ALLOW;
#ifdef MPXINBUF_USE_MUTEX
 if(frp->buffertype & PREBUFTYPE_INT)
  PDS_THREADS_MUTEX_UNLOCK(&frp->mutexhnd_frame);
#endif
}
