//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2006 by PDSoft (Attila Padar)                *
//*                    http://mpxplay.cjb.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: FFMPG lib handling (not used)
//requires ffmpegaf\avformat.lib and avformat.h, avcodec.h files

#include "in_file.h"

#ifdef MPXPLAY_LINK_INFILE_FFMPG

#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include "newfunc\newfunc.h"
#include "in_file.h"
#include "display\display.h"
#include "ffmpegaf\avformat.h"
#include "ffmpegac\avcodec.h"
#undef malloc
#undef free
#include <mem.h>

//#define INFFMPG_DEBUG 1

#define INFFMPG_PACKETS_MIN    2
#define INFFMPG_PACKETBUF_SIZE 128000 // for every streamtypes (*2)

typedef struct ffmpg_codectypeinfo_s{
 unsigned int codectype;
 unsigned int *stream_selector;
 int  (*assign_values)(struct ffmpg_decoder_data_s *ffmpi,struct ffmpg_streamtype_s *st,struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis);
 int  (*open)(struct ffmpg_streamtype_s *st,struct mpxplay_infile_info_s *miis);
 void (*close)(struct ffmpg_streamtype_s *st,struct mpxplay_infile_info_s *miis);
 int  (*decoder)(struct ffmpg_streamtype_s *st,unsigned char *pkt_data,unsigned int pkt_size,struct mpxplay_infile_info_s *miis);
}ffmpg_codectypeinfo_s;

typedef struct ffmpg_stream_s{
 int index;            // n. stream in the container
 AVStream *stream;
 AVCodecContext *enc;
 AVCodec *codec;
}ffmpg_stream_s;

typedef struct ffmpg_streamtype_s{
 ffmpg_codectypeinfo_s *ctinfo;  // codectype info (ffmpg_codec_type_infos[])
 unsigned int nb_streams;        // number of streams in this codec type
 ffmpg_stream_s *ct_streams;     // all streams in this codec type
 ffmpg_stream_s *primary_stream; // primary stream in this codec (ie: audio stream selection)
 unsigned char *pkt_dataptr;
 void *private_data;             // of audio,video,etc.
}ffmpg_streamtype_s;

typedef struct inffmpg_file_private_data_s{
 struct mpxplay_filehand_buffered_func_s *fbfs;
 void *fbds;
}inffmpg_file_private_data_s;

typedef struct ffmpg_decoder_data_s{
 AVFormatContext *fctx;
 ffmpg_streamtype_s *streamtypes;
 inffmpg_file_private_data_s file_priv_datas;
 AVPacket pkt;
}ffmpg_decoder_data_s;

//---------------------------------------------------------------------
//audio codec
typedef struct ffmpg_audio_private_data_s{
 unsigned int  bytes_per_sample;
 unsigned char *pcmoutsave_buffer;
 unsigned int pcmoutsave_samplenum;
}ffmpg_audio_private_data_s;

unsigned int inffmpg_stream_selector_audio;
static int  inffmpg_assign_values_audio(struct ffmpg_decoder_data_s *ffmpi,struct ffmpg_streamtype_s *st,struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis);
static int  inffmpg_open_audio(struct ffmpg_streamtype_s *st,struct mpxplay_infile_info_s *miis);
static void inffmpg_close_audio(struct ffmpg_streamtype_s *st,struct mpxplay_infile_info_s *miis);
static int  inffmpg_decode_audio(struct ffmpg_streamtype_s *st,unsigned char *pkt_data,unsigned int pkt_size,struct mpxplay_infile_info_s *miis);

//---------------------------------------------------------------------
//video codec
typedef struct ffmpg_video_private_data_s{
 AVFrame *frame;
}ffmpg_video_private_data_s;

static int  inffmpg_assign_values_video(struct ffmpg_decoder_data_s *ffmpi,struct ffmpg_streamtype_s *st,struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis);
static int  inffmpg_open_video(struct ffmpg_streamtype_s *st,struct mpxplay_infile_info_s *miis);
static void inffmpg_close_video(struct ffmpg_streamtype_s *st,struct mpxplay_infile_info_s *miis);
static int  inffmpg_decode_video(struct ffmpg_streamtype_s *st,unsigned char *pkt_data,unsigned int pkt_size,struct mpxplay_infile_info_s *miis);

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

static ffmpg_codectypeinfo_s ffmpg_codec_type_infos[]={
 {CODEC_TYPE_AUDIO,&inffmpg_stream_selector_audio,inffmpg_assign_values_audio,inffmpg_open_audio,inffmpg_close_audio,inffmpg_decode_audio},
 {CODEC_TYPE_VIDEO,NULL                          ,inffmpg_assign_values_video,inffmpg_open_video,inffmpg_close_video,inffmpg_decode_video},
 {0,NULL}
};

#define FFMPG_CODECTYPEINFOS (sizeof(ffmpg_codec_type_infos)/sizeof(ffmpg_codectypeinfo_s)-1)

static URLProtocol inffmpg_file_protocol;

#ifdef INFFMPG_DEBUG
void inffmpg_debugf(const char *format, ...)
{
 va_list ap;
 char sout[500];

 va_start(ap,format);
 vsprintf(sout, format, ap );
 va_end(ap);

 pds_textdisplay_printf(sout);
 //fprintf(stdout,"%s\n",sout);
}
#endif

static void INFFMPG_preinit(void)
{
 av_register_all();
 register_protocol(&inffmpg_file_protocol);
 av_log_set_level(AV_LOG_QUIET);
 //av_log_set_level(AV_LOG_DEBUG);
 //av_log_set_level(AV_LOG_ERROR);
}

//-------------------------------------------------------------------
static int inffmpg_assign_values_audio(struct ffmpg_decoder_data_s *ffmpi,struct ffmpg_streamtype_s *st,struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis)
{
 mpxplay_audio_decoder_info_s *adi=miis->audio_decoder_infos;
 struct ffmpg_stream_s *primary_stream=st->primary_stream;
 AVCodecContext *enc=primary_stream->enc;
 mpxp_int64_t filesize=fbfs->filelength(fbds);
 int bytes_per_sample;
 float pcmdatalen,compr_ratio;

 adi->freq=enc->sample_rate;
 adi->filechannels=enc->channels;
 if(enc->channels>PCM_MAX_CHANNELS)
  enc->channels=PCM_MAX_CHANNELS; // !!!
 adi->outchannels=enc->channels;
 adi->bitrate=enc->bit_rate/1000;

 switch(enc->sample_fmt){
  case SAMPLE_FMT_S16:adi->bits=16;bytes_per_sample=2;break;
  case SAMPLE_FMT_S24:adi->bits=24;bytes_per_sample=3;break;
  case SAMPLE_FMT_S32:adi->bits=32;bytes_per_sample=4;break;
  case SAMPLE_FMT_FLT:adi->bits=1 ;bytes_per_sample=4;adi->infobits|=ADI_FLAG_FLOATOUT;break; // ???
  default:return 0;
 }

 miis->timemsec=ffmpi->fctx->duration/1000; // /1000=*1000/AV_TIME_BASE
 pcmdatalen=(float)ffmpi->fctx->duration/(float)AV_TIME_BASE*(float)adi->freq;

 if((adi->outchannels<PCM_MIN_CHANNELS) || (adi->outchannels>PCM_MAX_CHANNELS))
  return 0;
 if((adi->bits<PCM_MIN_BITS) || (adi->bits>PCM_MAX_BITS))
  return 0;

#ifdef INFFMPG_DEBUG
 inffmpg_debugf("freq:%d chans:%d bits:%d duration:%d",adi->freq,adi->outchannels,adi->bits,miis->timemsec);
#endif

 miis->longname=malloc(MPXPLAY_ADITEXTSIZE_LONGNAME+8);
 if(!miis->longname)
  return 0;
 snprintf(miis->longname,MPXPLAY_ADITEXTSIZE_LONGNAME,"%3.3s->%3.3s",(char *)ffmpi->fctx->iformat->name,(char *)enc->codec->name);
 miis->longname[MPXPLAY_ADITEXTSIZE_LONGNAME]=0;

 if(!adi->bitrate){ // it's not a lossy format
  compr_ratio=100.0*(float)filesize/pcmdatalen/(float)bytes_per_sample/(float)adi->filechannels;
  if(compr_ratio<=99.0){ // we assume it's a compressed lossless format
   adi->bitratetext=malloc(MPXPLAY_ADITEXTSIZE_BITRATE+8);
   if(!adi->bitratetext)
    return 0;
   snprintf(adi->bitratetext,MPXPLAY_ADITEXTSIZE_BITRATE,"%2d/%2.1f%%",adi->bits,compr_ratio);
   adi->bitratetext[MPXPLAY_ADITEXTSIZE_BITRATE]=0;
  }
 }


 return 1;
}

static int inffmpg_open_audio(struct ffmpg_streamtype_s *st,struct mpxplay_infile_info_s *miis)
{
 struct ffmpg_audio_private_data_s *apd;
 AVCodecContext *enc=st->primary_stream->enc;
#ifdef INFFMPG_DEBUG
 char sout[100];
#endif

 apd=(struct ffmpg_audio_private_data_s *)calloc(1,sizeof(struct ffmpg_audio_private_data_s));
 if(!apd)
  return 0;

 st->private_data=apd;

 apd->pcmoutsave_buffer=(unsigned char *)malloc(2*AVCODEC_MAX_AUDIO_FRAME_SIZE);
 if(!apd->pcmoutsave_buffer)
  return 0;

 switch(enc->sample_fmt){
  case SAMPLE_FMT_S16:apd->bytes_per_sample=2;break;
  case SAMPLE_FMT_S24:apd->bytes_per_sample=3;break;
  case SAMPLE_FMT_S32:apd->bytes_per_sample=4;break;
  case SAMPLE_FMT_FLT:apd->bytes_per_sample=4;break; // ???
  default:return 0;
 }

#ifdef INFFMPG_DEBUG
 avcodec_string(&sout,99,enc,0);
 inffmpg_debugf("%s",sout);
#endif

 return 1;
}

static void inffmpg_close_audio(struct ffmpg_streamtype_s *st,struct mpxplay_infile_info_s *miis)
{
 struct ffmpg_audio_private_data_s *apd=(struct ffmpg_audio_private_data_s *)st->private_data;
 if(apd){
  if(apd->pcmoutsave_buffer)
   free(apd->pcmoutsave_buffer);
  free(apd);
  st->private_data=NULL;
 }
}

static int inffmpg_decode_audio(struct ffmpg_streamtype_s *st,unsigned char *pkt_data,unsigned int pkt_size,struct mpxplay_infile_info_s *miis)
{
 mpxplay_audio_decoder_info_s *adi=miis->audio_decoder_infos;
 struct ffmpg_audio_private_data_s *apd=(struct ffmpg_audio_private_data_s *)st->private_data;
 int bytes_consumed,pcmdata_size=0;
 bytes_consumed=avcodec_decode_audio(
                 &st->primary_stream->stream->codec,
                 (short *)adi->pcm_putptr,
                 &pcmdata_size,
                 pkt_data, pkt_size);
 adi->pcm_storedsamples+=pcmdata_size/apd->bytes_per_sample;
//#ifdef INFFMPG_DEBUG
// inffmpg_debugf("decode audio: samplenum:%4d pkt_size:%4d bytes_consumed:%4d",adi->pcm_storedsamples,pkt_size,bytes_consumed);
//#endif
 return bytes_consumed;
}

//--------------------------------------------------------------------------
static int inffmpg_assign_values_video(struct ffmpg_decoder_data_s *ffmpi,struct ffmpg_streamtype_s *st,struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis)
{
 mpxplay_video_decoder_info_s *vdi=miis->video_decoder_infos;
 AVCodecContext *enc=st->primary_stream->enc;

 vdi->video_res_x=enc->width;
 vdi->video_res_y=enc->height;

 return 1;
}

static int inffmpg_open_video(struct ffmpg_streamtype_s *st,struct mpxplay_infile_info_s *miis)
{
 struct ffmpg_video_private_data_s *vpd;
#ifdef INFFMPG_DEBUG
 char sout[100];
#endif

 vpd=(struct ffmpg_video_private_data_s *)calloc(1,sizeof(struct ffmpg_video_private_data_s));
 if(!vpd)
  return 0;

 st->private_data=vpd;

 vpd->frame=avcodec_alloc_frame();
 if(!vpd->frame)
  return 0;
#ifdef INFFMPG_DEBUG
 avcodec_string(&sout,99,st->primary_stream->enc,0);
 inffmpg_debugf("%s",sout);
#endif

 return 1;
}

static void inffmpg_close_video(struct ffmpg_streamtype_s *st,struct mpxplay_infile_info_s *miis)
{
 mpxplay_video_decoder_info_s *vdi=miis->video_decoder_infos;
 struct ffmpg_video_private_data_s *vpd=(struct ffmpg_video_private_data_s *)st->private_data;
 if(vpd){
  if(vpd->frame)
   free(vpd->frame);
  free(vpd);
  st->private_data=NULL;
 }
 vdi->picture=NULL;
}

/*static unsigned int count_zero_dest(uint8_t *dest,int line_size)
{
 unsigned long count=0;
 while(line_size){
  if(!*dest++)
   count++;
  line_size--;
 }
 return count;
}*/

//static char pic_s[4];

static int inffmpg_decode_video(struct ffmpg_streamtype_s *st,unsigned char *pkt_data,unsigned int pkt_size,struct mpxplay_infile_info_s *miis)
{
 mpxplay_video_decoder_info_s *vdi=miis->video_decoder_infos;
 struct ffmpg_video_private_data_s *vpd=(struct ffmpg_video_private_data_s *)st->private_data;
 int got_picture=0,bytes_consumed;
 //int dst_pix_fmt=PIX_FMT_RGBA32;
 AVPicture pict;

 if(!(vdi->flags&VDI_CNTRLBIT_DECODEVIDEO))
  return pkt_size;

 bytes_consumed=avcodec_decode_video(
                &st->primary_stream->stream->codec,
                vpd->frame,
                &got_picture,
                pkt_data, pkt_size);

 if(got_picture){
  vdi->video_res_x=st->primary_stream->stream->codec.width;
  vdi->video_res_y=st->primary_stream->stream->codec.height;
  //if(vdi->flags&VDI_CNTRLBIT_SHOWVIDEO)
  {
   unsigned int i;
   unsigned char *datay=vpd->frame->data[0];
   unsigned char *picture=vdi->picture+(1024*768*4+1024);
   //unsigned char *picture=(unsigned char *)0xa0000;
   i=st->primary_stream->stream->codec.width*st->primary_stream->stream->codec.height;
   /*do{
    picture[0]=picture[1]=picture[2]=picture[3]=*datay++;
    //picture+=4;
   }while(--i);*/
   pds_memcpy(picture,datay,i);
   pds_memcpy(picture,datay,i);
   //pds_memcpy(picture,datay,i);
   //pds_memcpy(picture,datay,i);
   /*pds_memcpy(picture,datay,65535);
   pds_memcpy(picture,datay,65535);
   pds_memcpy(picture,datay,65535);
   pds_memcpy(picture,datay,65535);
   pds_memcpy(picture,datay,65535);
   pds_memcpy(picture,datay,65535);
   pds_memcpy(picture,datay,65535);
   pds_memcpy(picture,datay,65535);*/

   //dst_pix_fmt=st->primary_stream->stream->codec.pix_fmt;
   pict.data[0] = vdi->picture;

   pict.linesize[0] = vdi->screen_res_x;
   vdi->output_res_x=vdi->screen_res_x;
   vdi->output_res_y=vdi->video_res_y;

//#ifdef INFFMPG_DEBUG
//   inffmpg_debugf("datap:%8.8X picp:%8.8X\n",(unsigned long)datay,(unsigned long)picture);
//#endif

   /*if(img_convert(&pict,
              dst_pix_fmt,
              (AVPicture *)vpd->frame,
              st->primary_stream->stream->codec.pix_fmt,
              st->primary_stream->stream->codec.width,
              st->primary_stream->stream->codec.height)<0){
//#ifdef INFFMPG_DEBUG
//    inffmpg_debugf("img convert failed!");
//#endif
   }*/
  }
/*#ifdef INFFMPG_DEBUG
  inffmpg_debugf("picture: fmt:%2d res:%dx%d pkt_size:%5d bc:%5d datac:%d/%d",
   st->primary_stream->stream->codec.pix_fmt,st->primary_stream->stream->codec.width,st->primary_stream->stream->codec.height,
   pkt_size,bytes_consumed,count_zero_dest(vpd->frame->data[0],st->primary_stream->stream->codec.width*st->primary_stream->stream->codec.height),
   st->primary_stream->stream->codec.width*st->primary_stream->stream->codec.height);
#endif*/
 }

#ifdef INFFMPG_DEBUG
 else{
  inffmpg_debugf("no picture !!! pkt_size:%d",pkt_size);
 }
#endif

 return bytes_consumed;
}

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

static void *inffmpg_check_header(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,char *filename,struct mpxplay_infile_info_s *miis)
{
 struct ffmpg_decoder_data_s *ffmpi;
 int i,j,ctstcount[FFMPG_CODECTYPEINFOS];

#ifdef INFFMPG_DEBUG
 inffmpg_debugf("check_header begin");
#endif

 ffmpi=calloc(1,sizeof(struct ffmpg_decoder_data_s));
 if(!ffmpi)
  return ffmpi;
 miis->private_data=ffmpi;

 ffmpi->streamtypes=(struct ffmpg_streamtype_s *)calloc(FFMPG_CODECTYPEINFOS,sizeof(struct ffmpg_streamtype_s));
 if(!ffmpi->streamtypes)
  goto err_out_chk;

 for(j=0;j<FFMPG_CODECTYPEINFOS;j++){
  ffmpi->streamtypes[j].ctinfo=&ffmpg_codec_type_infos[j];
  ctstcount[j]=0;
 }

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

 if(av_open_input_file_pd(&ffmpi->fctx, filename, NULL, 0, NULL, ((void *)&ffmpi->file_priv_datas))<0)
  goto err_out_chk;

 miis->filesize=fbfs->filelength(fbds);

#ifdef INFFMPG_DEBUG
 inffmpg_debugf("av_open ok");
#endif

 if(av_find_stream_info(ffmpi->fctx)<0)
  goto err_out_chk;

#ifdef INFFMPG_DEBUG
 inffmpg_debugf("av_find_stream_info ok");
#endif

 // count streams for different codec types (audio,video,etc.)
 for(i=0; i<ffmpi->fctx->nb_streams; i++){
  AVCodecContext *enc = &ffmpi->fctx->streams[i]->codec;
  for(j=0; j<FFMPG_CODECTYPEINFOS; j++){
   ffmpg_streamtype_s *st=&ffmpi->streamtypes[j];
   if(st->ctinfo->codectype==enc->codec_type)
    st->nb_streams++;
  }
 }

 // check number of supported/found codectypes and allocate stream info
 i=0;
 for(j=0; j<FFMPG_CODECTYPEINFOS; j++){
  ffmpg_streamtype_s *st=&ffmpi->streamtypes[j];
  if(st->nb_streams){
   st->ct_streams=(ffmpg_stream_s *)calloc(st->nb_streams,sizeof(struct ffmpg_stream_s));
   if(st->ct_streams){
    i++;
#ifdef INFFMPG_DEBUG
    inffmpg_debugf("ct:%d streams:%d ",st->ctinfo->codectype,st->nb_streams);
#endif
   }
  }
 }

 if(!i) // supported stream (codec) type not found or couldn't allocated
  goto err_out_chk;

 // detect decoder(s) for stream(s)
 for(i=0; i<ffmpi->fctx->nb_streams; i++){
  AVCodecContext *enc = &ffmpi->fctx->streams[i]->codec;
  for(j=0; j<FFMPG_CODECTYPEINFOS; j++){
   ffmpg_streamtype_s *st=&ffmpi->streamtypes[j];
   if(st->ctinfo->codectype==enc->codec_type){
    AVCodec *codec=avcodec_find_decoder(enc->codec_id);
#ifdef INFFMPG_DEBUG
    inffmpg_debugf("id:%6.6X codec:%8.8X ",enc->codec_id,codec);
#endif
    if(codec){
     ffmpg_stream_s *s=&st->ct_streams[ctstcount[j]];
     s->index=i;
     s->stream=ffmpi->fctx->streams[i];
     s->enc=enc;
     s->codec=codec;
     ctstcount[j]++;
    }
    break;
   }
  }
 }

 // set primary stream, open codec, initialize/set stream infos for Mpxplay
 i=0;
 for(j=0; j<FFMPG_CODECTYPEINFOS; j++){
  if(ctstcount[j]){
   ffmpg_streamtype_s *st=&ffmpi->streamtypes[j];
   unsigned int select=0;
   if(st->ctinfo->stream_selector){
    select=*(st->ctinfo->stream_selector);
    if(select>=st->nb_streams)
     select=st->nb_streams-1;
   }
   st->primary_stream=&st->ct_streams[select];
   if(avcodec_open(st->primary_stream->enc,st->primary_stream->codec)==0){
    st->ctinfo->assign_values(ffmpi,st,fbfs,fbds,miis);
    i++;
   }
  }
 }
 if(!i)
  goto err_out_chk;

 return ffmpi;

err_out_chk:
 return NULL;
}

static int INFFMPG_infile_check(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,char *filename,struct mpxplay_infile_info_s *miis)
{
 if(inffmpg_check_header(fbfs,fbds,filename,miis)!=0)
  return MPXPLAY_ERROR_INFILE_OK;
 return MPXPLAY_ERROR_INFILE_CANTOPEN;
}

static int INFFMPG_infile_open(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,char *filename,struct mpxplay_infile_info_s *miis)
{
 struct ffmpg_decoder_data_s *ffmpi;
 unsigned int j;

 ffmpi=inffmpg_check_header(fbfs,fbds,filename,miis);
 if(!ffmpi)
  return MPXPLAY_ERROR_INFILE_CANTOPEN;

 for(j=0; j<FFMPG_CODECTYPEINFOS; j++){
  ffmpg_streamtype_s *st=&ffmpi->streamtypes[j];
  if(st->ct_streams){
   if(st->ctinfo->open)
    if(!st->ctinfo->open(st,miis))
     goto err_out_opn;
  }
 }

 av_init_packet(&ffmpi->pkt);
 ffmpi->pkt.size=0;

 return MPXPLAY_ERROR_INFILE_OK;

err_out_opn:
 return MPXPLAY_ERROR_INFILE_CANTOPEN;
}

static void INFFMPG_infile_close(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis)
{
 struct ffmpg_decoder_data_s *ffmpi=(struct ffmpg_decoder_data_s *)miis->private_data;
 mpxplay_audio_decoder_info_s *adi=miis->audio_decoder_infos;
 int i,j;

 ffmpi->file_priv_datas.fbds=fbds;

 if(ffmpi){
  if(ffmpi->fctx)
   av_close_input_file(ffmpi->fctx);
  if(ffmpi->streamtypes){
   for(j=0; j<FFMPG_CODECTYPEINFOS; j++){
    ffmpg_streamtype_s *st=&ffmpi->streamtypes[j];
    if(st->ct_streams){
     for(i=0; i<st->nb_streams; i++)
      if(st->ct_streams[i].enc)
       avcodec_close(st->ct_streams[i].enc);
     free(st->ct_streams);
     if(st->ctinfo->close)
      st->ctinfo->close(st,miis);
    }
   }
   free(ffmpi->streamtypes);
  }
  if(miis->longname)
   free(miis->longname);
  if(adi->bitratetext)
   free(adi->bitratetext);
  free(ffmpi);
 }
}

//-----------------------------------------------------------------------
//decoding

static int INFFMPG_infile_decode(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis)
{
 struct ffmpg_decoder_data_s *ffmpi=(struct ffmpg_decoder_data_s *)miis->private_data;
 unsigned int j,found_streamtype_for_packet;

 if(!ffmpi->pkt.size){
  if(ffmpi->pkt.data)
   av_free_packet(&ffmpi->pkt);
  if(av_read_frame(ffmpi->fctx,&ffmpi->pkt)<0){
#ifdef INFFMPG_DEBUG
   inffmpg_debugf("bad : index:%d data:%8.8X size:%d ",ffmpi->pkt.stream_index,ffmpi->pkt.data,ffmpi->pkt.size);
#endif
   av_free_packet(&ffmpi->pkt);
   return MPXPLAY_ERROR_INFILE_NODATA;
  }
//#ifdef INFFMPG_DEBUG
//  inffmpg_debugf("read_frame: index:%d data:%8.8X size:%d ",ffmpi->pkt.stream_index,ffmpi->pkt.data,ffmpi->pkt.size);
//#endif
  if((!ffmpi->pkt.data) || (ffmpi->pkt.size<=0)){
   av_free_packet(&ffmpi->pkt);
   return MPXPLAY_ERROR_INFILE_NODATA;
  }
 }

 found_streamtype_for_packet=0;
 for(j=0; j<FFMPG_CODECTYPEINFOS; j++){
  ffmpg_streamtype_s *st=&ffmpi->streamtypes[j];
  if(ffmpi->pkt.stream_index==st->primary_stream->index){
   int bytes_consumed;
   if(!st->pkt_dataptr)
    st->pkt_dataptr=ffmpi->pkt.data;
   bytes_consumed=st->ctinfo->decoder(st,st->pkt_dataptr,ffmpi->pkt.size,miis);
   if(bytes_consumed<0){
#ifdef INFFMPG_DEBUG
    inffmpg_debugf("decode failed: bytes_consumed:%4d pkt_size:%d ",bytes_consumed,ffmpi->pkt.size);
#endif
    av_free_packet(&ffmpi->pkt);
    st->pkt_dataptr=NULL;
    return MPXPLAY_ERROR_INFILE_EOF;
   }
   if(ffmpi->pkt.size>bytes_consumed){
    ffmpi->pkt.size-=bytes_consumed;
    st->pkt_dataptr+=bytes_consumed;
   }else{
    ffmpi->pkt.size=0;
    st->pkt_dataptr=NULL;
   }
   found_streamtype_for_packet=1;
   break;
  }
 }
 if(!found_streamtype_for_packet){
#ifdef INFFMPG_DEBUG
  inffmpg_debugf("non useful stream: size:%d",ffmpi->pkt.size);
#endif
  av_free_packet(&ffmpi->pkt);
  ffmpi->pkt.size=0;
 }

 return MPXPLAY_ERROR_INFILE_OK;
}

//-------------------------------------------------------------------------
// seeking

static void INFFMPG_clearbuffs(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis,unsigned int seektype)
{
 struct ffmpg_decoder_data_s *ffmpi=(struct ffmpg_decoder_data_s *)miis->private_data;
 int j;

 ffmpi->file_priv_datas.fbds=fbds;
 ffmpi->pkt.size=0;

 for(j=0; j<FFMPG_CODECTYPEINFOS; j++){
  ffmpg_streamtype_s *st=&ffmpi->streamtypes[j];
  if(st->primary_stream && st->primary_stream->enc){
   avcodec_flush_buffers(st->primary_stream->enc);
   st->pkt_dataptr=NULL;
  }
 }
 if(ffmpi->pkt.data)
  av_free_packet(&ffmpi->pkt);
 ffmpi->pkt.size=0;
}

static long INFFMPG_fseek(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis,long newmpxframenum)
{
 struct ffmpg_decoder_data_s *ffmpi=(struct ffmpg_decoder_data_s *)miis->private_data;
 mpxp_int64_t ff_newtimepos;

 ffmpi->file_priv_datas.fbds=fbds;

 ff_newtimepos=(mpxp_int64_t)((float)AV_TIME_BASE*(float)miis->timemsec/1000.0*(float)newmpxframenum/(float)miis->allframes);
 if(av_seek_frame(ffmpi->fctx,-1,ff_newtimepos,0)<0){
#ifdef INFFMPG_DEBUG
  inffmpg_debugf("seek_failed : timepos: %d ",ff_newtimepos);
#endif
  return MPXPLAY_ERROR_INFILE_EOF;
 }
 return newmpxframenum;
}

//--------------------------------------------------------------------------
// read tag infos from AVFormatContext (need to call check_header before)

static char *inffmpg_tag_get_str(char *str,char **id3ip,char *id3p,struct mpxplay_textconv_func_s *mtfs)
{
 unsigned int len;

 if(str[0]){
  len=pds_strncpy(id3p,str,510);
  if(len){
   id3p[len]=0;
   if((*(mtfs->control))&ID3TEXTCONV_UTF_AUTO) // !!! FLAC only
    len=mtfs->utf8_to_char(id3p,len);
   len=mtfs->all_to_char(id3p,len,ID3TEXTCONV_UTF8);
   if(len){
    *id3ip=id3p;
    id3p+=len+1;
   }
  }
 }
 return id3p;
}

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

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

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

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

 return id3p;
}

//--------------------------------------------------------------------------
// file protocol (connected to mpxinbuf)

static int inffmpg_file_open(URLContext *h, const char *filename, int flags)
{
 inffmpg_file_private_data_s *pd=(inffmpg_file_private_data_s *)h->priv_data;
 if((flags&URL_RDWR) || (flags&URL_WRONLY)){
  if(!pd->fbfs->fopen_write(pd->fbds,(char *)filename))
   return -ENOENT;
 }else{
  if(!pd->fbfs->fopen_read(pd->fbds,(char *)filename,0))
   return -ENOENT;
 }
 return 0;
}

static int inffmpg_file_read(URLContext *h, unsigned char *buf, int size)
{
 inffmpg_file_private_data_s *pd=(inffmpg_file_private_data_s *)h->priv_data;
 return pd->fbfs->fread(pd->fbds,buf,size);
}

static int inffmpg_file_write(URLContext *h, unsigned char *buf, int size)
{
 inffmpg_file_private_data_s *pd=(inffmpg_file_private_data_s *)h->priv_data;
 return pd->fbfs->fwrite(pd->fbds,buf,size);
}

static offset_t inffmpg_file_seek(URLContext *h, offset_t pos, int whence)
{
 inffmpg_file_private_data_s *pd=(inffmpg_file_private_data_s *)h->priv_data;
 return pd->fbfs->fseek(pd->fbds,pos,whence);
}

static int inffmpg_file_close(URLContext *h)
{
 inffmpg_file_private_data_s *pd=(inffmpg_file_private_data_s *)h->priv_data;
 pd->fbfs->fclose(pd->fbds);
 return 0;
}

static offset_t inffmpg_file_filesize(URLContext *h)
{
 inffmpg_file_private_data_s *pd=(inffmpg_file_private_data_s *)h->priv_data;
 return pd->fbfs->filelength(pd->fbds);
}

static URLProtocol inffmpg_file_protocol={
 "file",
 inffmpg_file_open,
 inffmpg_file_read,
 inffmpg_file_write,
 inffmpg_file_seek,
 inffmpg_file_close,
 inffmpg_file_filesize,
};

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

struct mpxplay_infile_func_s IN_FFMPG_funcs={
 0,
 &INFFMPG_preinit,
 NULL,
 &INFFMPG_infile_check,
 &INFFMPG_infile_check,
 &INFFMPG_infile_open,
 &INFFMPG_infile_close,
 &INFFMPG_infile_decode,
 &INFFMPG_fseek,
 &INFFMPG_clearbuffs,
 &INFFMPG_tag_get,
 NULL,
 NULL,
 { // (usually) no such information in the FFMPG lib, we must to set this here manually
 //"FLAC","FLA","FLC", // currently by IN_FFLAC
 "WMA",
 "AVI","VOB",
 NULL}
};

#endif // MPXPLAY_LINK_INFILE_FFMPG
