//**************************************************************************
//*                     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: ASF demuxing  (based on the FFMPEG lib)

#include "in_file.h"

#ifdef MPXPLAY_LINK_INFILE_ASF

#include "newfunc\newfunc.h"
#include <malloc.h>
#include <string.h>
#include <stdarg.h>

//#define DEBUG_FILEINFO 1
//#define DEBUG_HEADER 1
//#define DEBUG_DECODE 1

static void asf_debugf(const char *format, ...);

#define ASF_MAX_STREAMS 16

typedef mpxp_uint8_t GUID[16];

typedef struct {
 mpxp_uint64_t file_size;	// in bytes // invalid if broadcasting
 mpxp_uint64_t create_time;	// time of creation, in 100-nanosecond units since 1.1.1601 // invalid if broadcasting
 mpxp_uint64_t packets_count;   // how many packets are there in the file // invalid if broadcasting
 mpxp_uint64_t play_time;	// play time, in 100-nanosecond units // invalid if broadcasting
 mpxp_uint64_t send_time;	// time to send file, in 100-nanosecond units // invalid if broadcasting (could be ignored)
 mpxp_uint32_t preroll;	        // timestamp of the first packet, in milliseconds // if nonzero - substract from time
 mpxp_uint32_t ignore;          // preroll is 64bit - but let's just ignore it
 mpxp_uint32_t flags;	        // 0x01 - broadcast // 0x02 - seekable
                                // rest is reserved should be 0
 mpxp_uint32_t min_pktsize;	// size of a data packet // invalid if broadcasting
 mpxp_uint32_t max_pktsize;	// shall be the same as for min_pktsize // invalid if broadcasting
 mpxp_uint32_t max_bitrate;	// bandwith of stream in bps // should be the sum of bitrates of the individual media streams

 GUID guid;		        // generated by client computer
}ASFMainHeader;

typedef struct asf_stream_data_s{
 unsigned int streamtype;
 mpxp_uint32_t codec_tag;
 unsigned int need_parsing;

 unsigned int stream_index;

 int id;
 int num;
 unsigned char seq;

 int frag_offset;
 int timestamp;

 int ds_span;		// descrambling
 int ds_packet_size;
 int ds_chunk_size;
 int ds_data_size;
 int ds_silence_data;

 int packet_pos;

 long start_time;
 mpxp_int64_t duration;
 unsigned long bit_rate;

 mpxp_uint8_t *extradata;
 unsigned long extradata_size;

 // audio
 unsigned int channels;
 unsigned long sample_rate;
 unsigned int block_align;
 unsigned int bits_per_sample;

 //video
 unsigned long video_res_x;
 unsigned long video_res_y;
 unsigned int  video_bpp;

 unsigned long seek_entries;
 mpxp_uint32_t *seek_table;
 mpxp_uint8_t  *seek_flags;

}asf_stream_data_s;

typedef struct asf_demuxer_data_s{
 struct mpxplay_bitstreambuf_s *bs_packet;
 unsigned int nb_streams;
 unsigned int packet_size;

 mpxp_int64_t nb_packets;
 mpxp_int64_t duration; // in 100ns units

 mpxp_uint8_t *descrambling_buffer;

 mpxp_uint64_t data_offset; // begining of the first data packet

 int packet_size_left;
 int packet_flags;
 int packet_property;
 int packet_timestamp;
 int packet_segsizetype;
 int packet_segments;
 int packet_seq;
 int packet_replic_size;
 int packet_key_frame;
 int packet_padsize;
 int packet_frag_offset;
 int packet_frag_size;
 int packet_frag_timestamp;
 int packet_multi_size;
 int packet_obj_size;
 int packet_time_delta;
 int packet_time_start;
 int packet_pos;

 int stream_index;

 asf_stream_data_s *ast_audio;
 asf_stream_data_s *ast_video;
 asf_stream_data_s *ast_curr; // currently decoded stream

 ASFMainHeader hdr;
 int asfid2avid[128];           // conversion table from asf ID 2 AVStream ID
 asf_stream_data_s streams[ASF_MAX_STREAMS];

 char *tag_strs[I3I_MAX+1];
 unsigned int tag_lens[I3I_MAX+1];
 unsigned int tag_type_byte[I3I_MAX+1]; // else unicode (UTF16)
 unsigned int track;

}asf_demuxer_data_s;

static const GUID asf_header = {
 0x30,0x26,0xB2,0x75,0x8E,0x66,0xCF,0x11,0xA6,0xD9,0x00,0xAA,0x00,0x62,0xCE,0x6C
};
static const GUID file_header = {
 0xA1,0xDC,0xAB,0x8C,0x47,0xA9,0xCF,0x11,0x8E,0xE4,0x00,0xC0,0x0C,0x20,0x53,0x65
};
static const GUID stream_header = {
 0x91,0x07,0xDC,0xB7,0xB7,0xA9,0xCF,0x11,0x8E,0xE6,0x00,0xC0,0x0C,0x20,0x53,0x65
};
static const GUID audio_stream = {
 0x40,0x9E,0x69,0xF8,0x4D,0x5B,0xCF,0x11,0xA8,0xFD,0x00,0x80,0x5F,0x5C,0x44,0x2B
};
#ifdef MPXPLAY_LINK_VIDEO
static const GUID video_stream = {
 0xC0,0xEF,0x19,0xBC,0x4D,0x5B,0xCF,0x11,0xA8,0xFD,0x00,0x80,0x5F,0x5C,0x44,0x2B
};
#endif
static const GUID comment_header = {
 0x33,0x26,0xB2,0x75,0x8E,0x66,0xCF,0x11,0xA6,0xD9,0x00,0xAA,0x00,0x62,0xCE,0x6C
};
static const GUID data_header = {
 0x36,0x26,0xB2,0x75,0x8E,0x66,0xCF,0x11,0xA6,0xD9,0x00,0xAA,0x00,0x62,0xCE,0x6C
};
static const GUID extended_content_header = {
 0x40,0xA4,0xD0,0xD2,0x07,0xE3,0xD2,0x11,0x97,0xF0,0x00,0xA0,0xC9,0x5E,0xA8,0x50
};
/*static const GUID simple_index_header = {
 0x90,0x08,0x00,0x33,0xB1,0xE5,0xCF,0x11,0x89,0xF4,0x00,0xA0,0xC9,0x03,0x49,0xCB
};*/

static struct asf_stream_data_s *asf_get_stream(struct asf_demuxer_data_s *asf,unsigned int streamtype,struct mpxplay_streampacket_info_s *spi);
static void asf_assign_audio(struct asf_demuxer_data_s *asf,struct mpxplay_infile_info_s *miis,unsigned int full_load);

static unsigned int asf_read_header(struct asf_demuxer_data_s *asf,struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis);
static unsigned int asf_read_wavheader(asf_stream_data_s *ast, struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds, int size);
static void asf_read_str16_nolen(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds, int len, char *buf, int buf_size);
static unsigned int asf_alloc_and_read_tag(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds, int len, char **strptr,unsigned int type);
#define ASF_READ_GUID(fbfs,fbds,g) fbfs->fread(fbds,g,sizeof(GUID))

static struct asf_demuxer_data_s *asf_open_and_check(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,char *filename,struct mpxplay_infile_info_s *miis,unsigned int full_load)
{
 struct asf_demuxer_data_s *asfi=NULL;

 if(!fbfs->fopen_read(fbds,filename,2048))
  return NULL;

 miis->filesize=fbfs->filelength(fbds);
 if(miis->filesize<16)
  return NULL;

 asfi=(struct asf_demuxer_data_s *)calloc(1,sizeof(struct asf_demuxer_data_s));
 if(!asfi)
  return NULL;
 miis->private_data=asfi;

 if(!asf_read_header(asfi,fbfs,fbds,miis))
  return NULL;

 asfi->ast_audio=asf_get_stream(asfi,MPXPLAY_SPI_STREAMTYPE_AUDIO,miis->audio_stream);
#ifdef MPXPLAY_LINK_VIDEO
 asfi->ast_video=asf_get_stream(asfi,MPXPLAY_SPI_STREAMTYPE_VIDEO,miis->video_stream);
#endif

 if(!asfi->ast_audio && !asfi->ast_video)
  return NULL;

 asf_assign_audio(asfi,miis,full_load);

 return asfi;
}

static int ASF_infile_check(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,char *filename,struct mpxplay_infile_info_s *miis)
{
 struct asf_demuxer_data_s *asfi;

 asfi=asf_open_and_check(fbfs,fbds,filename,miis,0);
 if(!asfi)
  goto err_out_check;

 return MPXPLAY_ERROR_INFILE_OK;

err_out_check:
 return MPXPLAY_ERROR_INFILE_CANTOPEN;
}

static int ASF_infile_open(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,char *filename,struct mpxplay_infile_info_s *miis)
{
 struct asf_demuxer_data_s *asfi;

 asfi=asf_open_and_check(fbfs,fbds,filename,miis,1);
 if(!asfi)
  goto err_out_check;

 asfi->descrambling_buffer=(mpxp_uint8_t *)malloc(asfi->hdr.max_pktsize);
 if(!asfi->descrambling_buffer)
  goto err_out_check;
 asfi->bs_packet=mpxplay_bitstream_alloc(asfi->hdr.max_pktsize);
 if(!asfi->bs_packet)
  goto err_out_check;

 return MPXPLAY_ERROR_INFILE_OK;

err_out_check:
 return MPXPLAY_ERROR_INFILE_CANTOPEN;
}

static void ASF_infile_close(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis)
{
 struct asf_demuxer_data_s *asfi=(struct asf_demuxer_data_s *)miis->private_data;
 unsigned int i;
 if(asfi){
  for(i=0;i<asfi->nb_streams;i++){
   asf_stream_data_s *ast=&asfi->streams[i];
   if(ast->extradata)
    free(ast->extradata);
   if(ast->seek_table)
    free(ast->seek_table);
   if(ast->seek_flags)
    free(ast->seek_flags);
  }
  for(i=0;i<=I3I_MAX;i++)
   if(asfi->tag_strs[i])
    free(asfi->tag_strs[i]);
  if(asfi->descrambling_buffer)
   free(asfi->descrambling_buffer);
  mpxplay_bitstream_free(asfi->bs_packet);
  free(asfi);
 }
 fbfs->fclose(fbds);
}

static struct asf_stream_data_s *asf_get_stream(struct asf_demuxer_data_s *asf,unsigned int streamtype,struct mpxplay_streampacket_info_s *spi)
{
 unsigned int i,streamtype_count=0;
 struct asf_stream_data_s *ast=&asf->streams[0],*found_stream=NULL;

 for(i=0;i<asf->nb_streams;i++){
  if(ast->streamtype==streamtype){
   if(streamtype_count<=spi->stream_select)
    found_stream=ast;
   streamtype_count++;
  }
  ast++;
 }
 spi->nb_streams=streamtype_count;
 return found_stream;
}

static void asf_assign_audio(struct asf_demuxer_data_s *asf,struct mpxplay_infile_info_s *miis,unsigned int full_load)
{
 struct mpxplay_audio_decoder_info_s *adi=miis->audio_decoder_infos;
 struct mpxplay_streampacket_info_s  *spi=miis->audio_stream;
 struct asf_stream_data_s *ast=asf->ast_audio;

 if(!ast)
  return;

 spi->streamtype  =ast->streamtype;
 spi->wave_id     =ast->codec_tag;
 spi->block_align =ast->block_align;
 spi->bs_framesize=asf->hdr.max_pktsize; // ???
 if(full_load){
  funcbit_enable(spi->flags,MPXPLAY_SPI_FLAG_NEED_DECODER);
  if(ast->need_parsing)
   funcbit_enable(spi->flags,MPXPLAY_SPI_FLAG_NEED_PARSING);
 }
 funcbit_enable(spi->flags,MPXPLAY_SPI_FLAG_CONTAINER);
 spi->extradata=ast->extradata;
 spi->extradata_size=ast->extradata_size;

 adi->filechannels=adi->outchannels=ast->channels;
 adi->freq=ast->sample_rate;
 adi->bits=ast->bits_per_sample;
 if((spi->wave_id!=MPXPLAY_WAVEID_PCM_SLE) && (spi->wave_id!=MPXPLAY_WAVEID_PCM_FLOAT))
  adi->bitrate=(ast->bit_rate+500)/1000;

 miis->timemsec=ast->duration;

#ifdef DEBUG_FILEINFO
 asf_debugf("st:%d wd:%4.4X fs:%d c:%d f:%d b:%d br:%d ba:%d d:%d mps:%d\n",
  spi->streamtype,spi->wave_id,spi->bs_framesize,adi->filechannels,adi->freq,
  adi->bits,adi->bitrate,spi->block_align,(long)ast->duration,(long)asf->hdr.max_pktsize);
 {
  unsigned int i;
  char sout[128];
  sprintf(sout,"%8.8X %d ",spi->extradata,spi->extradata_size);
  for(i=0;i<spi->extradata_size;i++)
   sprintf(sout,"%s%2.2X",sout,spi->extradata[i]);
  asf_debugf(sout);
 }
#endif
}

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

#define FRAME_HEADER_SIZE 17 // Fix Me! FRAME_HEADER_SIZE may be different.

static unsigned int asf_read_header(struct asf_demuxer_data_s *asf,struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis)
{
 asf_stream_data_s *ast;
#ifdef MPXPLAY_LINK_VIDEO
 long size;
#endif
 long filepos;
 mpxp_uint64_t gsize;
 GUID g;
 char name[64];

 ASF_READ_GUID(fbfs,fbds, &g);
 if(memcmp(&g, &asf_header, sizeof(GUID)))
  return 0;
 fbfs->get_le64(fbds);
 fbfs->get_le32(fbds);
 fbfs->get_byte(fbds);
 fbfs->get_byte(fbds);
 memset(&asf->asfid2avid, -1, sizeof(asf->asfid2avid));
 for(;;) {
  ASF_READ_GUID(fbfs,fbds, &g);
  gsize = fbfs->get_le64(fbds);
#ifdef DEBUG_HEADER
  asf_debugf(" pos:%d size=%d %8.8X ", fbfs->ftell(fbds) - 24, gsize, PDS_GETB_LE32(&g));
#endif
  if(gsize < 24)
   return 0;
  filepos=fbfs->ftell(fbds);
  if(memcmp(&g, &file_header, sizeof(GUID))==0) {
   ASF_READ_GUID(fbfs,fbds, &asf->hdr.guid);
   asf->hdr.file_size	 = fbfs->get_le64(fbds);
   asf->hdr.create_time	 = fbfs->get_le64(fbds);
   asf->hdr.packets_count= fbfs->get_le64(fbds);
   asf->hdr.play_time	 = fbfs->get_le64(fbds);
   asf->hdr.send_time	 = fbfs->get_le64(fbds);
   asf->hdr.preroll	 = fbfs->get_le32(fbds);
   asf->hdr.ignore	 = fbfs->get_le32(fbds);
   asf->hdr.flags	 = fbfs->get_le32(fbds);
   asf->hdr.min_pktsize	 = fbfs->get_le32(fbds);
   asf->hdr.max_pktsize	 = fbfs->get_le32(fbds);
   asf->hdr.max_bitrate	 = fbfs->get_le32(fbds);
   asf->packet_size      = asf->hdr.max_pktsize;
   asf->nb_packets       = asf->hdr.packets_count;

  }else if(memcmp(&g, &stream_header, sizeof(GUID))==0) {
   int type, total_size, type_specific_size;
#ifdef MPXPLAY_LINK_VIDEO
   int sizeX;
   unsigned int tag1;
#endif
   long pos1, pos2;

   if(asf->nb_streams>=ASF_MAX_STREAMS)
    goto skip_header;

   pos1 = fbfs->ftell(fbds);

   ast = &asf->streams[asf->nb_streams];
   ast->start_time = asf->hdr.preroll;
   ast->duration = asf->hdr.send_time / (10000000 / 1000) - ast->start_time;
   ASF_READ_GUID(fbfs,fbds, &g);
   if(!memcmp(&g, &audio_stream, sizeof(GUID))) {
    type = MPXPLAY_SPI_STREAMTYPE_AUDIO;
   }else
#ifdef MPXPLAY_LINK_VIDEO
    if(!memcmp(&g, &video_stream, sizeof(GUID))) {
     type = MPXPLAY_SPI_STREAMTYPE_VIDEO;
    }else
#endif
    {
#ifdef DEBUG_HEADER
     asf_debugf("bad streamtype\n");
#endif
     goto skip_header;
    }

   ASF_READ_GUID(fbfs,fbds, &g);
   total_size = fbfs->get_le64(fbds);
   type_specific_size = fbfs->get_le32(fbds);
   fbfs->get_le32(fbds);
   ast->id = fbfs->get_le16(fbds) & 0x7f; // stream id
   asf->asfid2avid[ast->id] = asf->nb_streams; // mapping of asf ID to AV stream ID
#ifdef DEBUG_HEADER
   asf_debugf("ast_id:%d \n",ast->id);
#endif

   fbfs->get_le32(fbds);
   ast->streamtype = type;
   if(type == MPXPLAY_SPI_STREAMTYPE_AUDIO) {
    if(!asf_read_wavheader(ast,fbfs,fbds,type_specific_size))
     return 0;
    ast->need_parsing = 1;
    pos2 = fbfs->ftell(fbds);
    if (gsize > (pos2 + 8 - pos1 + 24)) {  // descrambling
     ast->ds_span = fbfs->get_byte(fbds);
     ast->ds_packet_size = fbfs->get_le16(fbds);
     ast->ds_chunk_size = fbfs->get_le16(fbds);
     ast->ds_data_size = fbfs->get_le16(fbds);
     ast->ds_silence_data = fbfs->get_byte(fbds);
    }
    if (ast->ds_span > 1) {
     if (!ast->ds_chunk_size || (ast->ds_packet_size/ast->ds_chunk_size <= 1))
      ast->ds_span = 0; // disable descrambling
    }
   }else{
#ifdef MPXPLAY_LINK_VIDEO
    fbfs->get_le32(fbds);
    fbfs->get_le32(fbds);
    fbfs->get_byte(fbds);
    size = fbfs->get_le16(fbds); /* size */
    sizeX= fbfs->get_le32(fbds); /* size */
    ast->video_res_x = fbfs->get_le32(fbds);
    ast->video_res_y = fbfs->get_le32(fbds);
    // not available for asf
    fbfs->get_le16(fbds); // panes
    ast->video_bpp = fbfs->get_le16(fbds); // depth
    tag1 = fbfs->get_le32(fbds);
    fbfs->fseek(fbds,20,SEEK_CUR);
    size= sizeX;
    if (size > 40) {
     ast->extradata_size = size - 40;
     ast->extradata = malloc(ast->extradata_size + MPXPLAY_SPI_EXTRADATA_PADDING);
     if(!ast->extradata)
      return 0;
     if(fbfs->fread(fbds, ast->extradata, ast->extradata_size)!=ast->extradata_size)
      return 0;
     pds_memset(ast->extradata+ast->extradata_size,0,MPXPLAY_SPI_EXTRADATA_PADDING);
    }

    ast->codec_tag = tag1;
    if(tag1 == PDS_GET4C_LE32('D','V','R',' '))
     ast->need_parsing = 1;
#endif
   }
   pos2 = fbfs->ftell(fbds);
   fbfs->fseek(fbds,(gsize - (pos2 - pos1 + 24)),SEEK_CUR);
   asf->nb_streams++;
  }else if (!memcmp(&g, &data_header, sizeof(GUID))) {
   break;
  }else if (!memcmp(&g, &comment_header, sizeof(GUID))) {
   int len1, len2, len3, len4, len5;

   len1 = fbfs->get_le16(fbds);
   len2 = fbfs->get_le16(fbds);
   len3 = fbfs->get_le16(fbds);
   len4 = fbfs->get_le16(fbds);
   len5 = fbfs->get_le16(fbds);

   asf->tag_lens[I3I_TITLE]=asf_alloc_and_read_tag(fbfs,fbds,len1,&asf->tag_strs[I3I_TITLE],0);
   asf->tag_lens[I3I_ARTIST]=asf_alloc_and_read_tag(fbfs,fbds,len2,&asf->tag_strs[I3I_ARTIST],0);
   fbfs->fseek(fbds,len3,SEEK_CUR); // copyright string
   asf->tag_lens[I3I_COMMENT]=asf_alloc_and_read_tag(fbfs,fbds,len4,&asf->tag_strs[I3I_COMMENT],0);
   fbfs->fseek(fbds,len5,SEEK_CUR);

  }else if (!memcmp(&g, &extended_content_header, sizeof(GUID))) {
   int desc_count, i;

   desc_count = fbfs->get_le16(fbds);

   for(i=0;i<desc_count;i++){
    int name_len,value_type,value_len;
    long desc_fp;

    name_len = fbfs->get_le16(fbds);
    asf_read_str16_nolen(fbfs,fbds, name_len, name, sizeof(name));
    value_type = fbfs->get_le16(fbds);
    value_len  = fbfs->get_le16(fbds);

    desc_fp=fbfs->ftell(fbds);

    if((value_type==0) || (value_type==1)){ // unicode or byte
     if(strcmp(name,"WM/AlbumTitle")==0){
      asf->tag_lens[I3I_ALBUM]=asf_alloc_and_read_tag(fbfs,fbds,value_len,&asf->tag_strs[I3I_ALBUM],value_type);
      asf->tag_type_byte[I3I_ALBUM]=value_type;
     }else if(strcmp(name,"WM/Year")==0){
      asf->tag_lens[I3I_YEAR]=asf_alloc_and_read_tag(fbfs,fbds,value_len,&asf->tag_strs[I3I_YEAR],value_type);
      asf->tag_type_byte[I3I_YEAR]=value_type;
     }else if(strcmp(name,"WM/Genre")==0){
      asf->tag_lens[I3I_GENRE]=asf_alloc_and_read_tag(fbfs,fbds,value_len,&asf->tag_strs[I3I_GENRE],value_type);
      asf->tag_type_byte[I3I_GENRE]=value_type;
     }
    }else{
     if((value_type >= 2) && (value_type <= 5)){ // boolean or DWORD or QWORD or WORD
      int value_num;
      if (value_type==2) value_num = fbfs->get_le32(fbds);
      if (value_type==3) value_num = fbfs->get_le32(fbds);
      if (value_type==4) value_num = fbfs->get_le64(fbds);
      if (value_type==5) value_num = fbfs->get_le16(fbds);
      if (strcmp(name,"WM/Track")==0) asf->track = value_num + 1;
      if (strcmp(name,"WM/TrackNumber")==0) asf->track = value_num;
     }
    }
    if(fbfs->fseek(fbds,desc_fp+value_len,SEEK_SET)<0)
     break;
   }
   goto skip_header;
  }else if (fbfs->eof(fbds)) {
   return 0;
  }else{
skip_header:
   if(fbfs->fseek(fbds, filepos+gsize - 24, SEEK_SET)<0)
    return 0;
  }
 }

 ASF_READ_GUID(fbfs,fbds, &g);
 fbfs->get_le64(fbds);
 fbfs->get_byte(fbds);
 fbfs->get_byte(fbds);
 if(fbfs->eof(fbds))
  return 0;

 asf->data_offset = fbfs->ftell(fbds);
 asf->packet_size_left = 0;

#ifdef DEBUG_HEADER
 asf_debugf("read head ok\n");
#endif

 return 1;
}

static unsigned int asf_read_wavheader(asf_stream_data_s *ast, struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds, int size)
{
 ast->streamtype = MPXPLAY_SPI_STREAMTYPE_AUDIO;
 ast->codec_tag  = fbfs->get_le16(fbds);
 ast->channels   = fbfs->get_le16(fbds);
 ast->sample_rate= fbfs->get_le32(fbds);
 ast->bit_rate   = fbfs->get_le32(fbds) * 8;
 ast->block_align= fbfs->get_le16(fbds);
 if(size == 14)  // plain vanilla WAVEFORMAT
  ast->bits_per_sample = 8;
 else
  ast->bits_per_sample = fbfs->get_le16(fbds);

 if(size > 16){  // WAVEFORMATEX
  ast->extradata_size = fbfs->get_le16(fbds);
  if(ast->extradata_size){
   if(ast->extradata_size > (size - 18))
    ast->extradata_size = size - 18;
   ast->extradata = malloc(ast->extradata_size + MPXPLAY_SPI_EXTRADATA_PADDING);
   if(!ast->extradata)
    return 0;
   if(fbfs->fread(fbds, ast->extradata, ast->extradata_size)!=ast->extradata_size)
    return 0;
   pds_memset(ast->extradata+ast->extradata_size,0,MPXPLAY_SPI_EXTRADATA_PADDING);
#ifdef DEBUG_HEADER
   asf_debugf("wav: %8.8X %d %2.2X%2.2X%2.2X%2.2X%2.2X%2.2X \n",ast->extradata,ast->extradata_size,
    ast->extradata[0],ast->extradata[1],ast->extradata[2],ast->extradata[3],ast->extradata[4],ast->extradata[5]);
#endif
  }
  if((size - ast->extradata_size - 18) > 0) // skip garbage at the end
   fbfs->fseek(fbds, (size - ast->extradata_size - 18), SEEK_CUR);
 }
 return 1;
}

static void asf_read_str16_nolen(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds, int len, char *buf, int buf_size)
{
 buf_size--;

 while((len>0) && (buf_size>0)){
  *buf++ = (char)fbfs->get_le16(fbds);
  buf_size--;
  len-=2;
 }
 *buf = 0;
 if(len>0)
  fbfs->fseek(fbds,len,SEEK_CUR);
}

static unsigned int asf_alloc_and_read_tag(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds, int len, char **strptr,unsigned int type)
{
 char *ptr;

 if((type==0) && (len<4)) // min len (unicode)
  goto skip;
 if((type==1) && (len<2)) // min len (byte)
  goto skip;
 ptr=(char *)malloc(len+2);
 if(!ptr)
  goto skip;
 fbfs->fread(fbds,ptr,len);
 ptr[len]=ptr[len+1]=0;
 *strptr=ptr;
 return len;
skip:
 fbfs->fseek(fbds,len,SEEK_CUR);
 return 0;
}

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

#define DO_2BITS(bits, var, defval) \
 switch ((bits) & 3){ \
  case 3: var = mpxplay_bitstream_get_le32(asf->bs_packet); rsize += 4; break; \
  case 2: var = mpxplay_bitstream_get_le16(asf->bs_packet); rsize += 2; break; \
  case 1: var = mpxplay_bitstream_get_byte(asf->bs_packet); rsize++; break; \
  default: var = defval; break; \
 }

static int asf_get_packet(asf_demuxer_data_s *asf,struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds)
{
 mpxp_uint32_t packet_length, padsize;
 long datapos;
 int rsize = 9;
 int c;

resync:
#if DEBUG_DECODE
 asf_debugf("get_packet: resync");
 if(kbhit()){
  getch();
  return MPXPLAY_ERROR_INFILE_EOF;
 }
#endif

 datapos=fbfs->ftell(fbds) - asf->data_offset;

 if(datapos%asf->packet_size){
#ifdef DEBUG_DECODE
  asf_debugf("get_packet: bad align %d\n",datapos);
#endif
  datapos+=asf->packet_size;
  datapos-=datapos%asf->packet_size;
  if((datapos+asf->data_offset)>=fbfs->filelength(fbds))
   return MPXPLAY_ERROR_INFILE_EOF;
  if(fbfs->fseek(fbds,(datapos+asf->data_offset),SEEK_SET)<0){
   #ifdef DEBUG_DECODE
    asf_debugf("get_packet: seek failed %d\n",datapos+asf->data_offset);
   #endif
   return MPXPLAY_ERROR_INFILE_NODATA;
  }
 }

 asf->packet_pos=datapos+asf->data_offset;

 mpxplay_bitstream_reset(asf->bs_packet);
 if(mpxplay_bitstream_fill(asf->bs_packet,fbfs,fbds,asf->packet_size)<0){
  fbfs->fseek(fbds,(datapos+asf->data_offset),SEEK_SET);
  return MPXPLAY_ERROR_INFILE_NODATA;
 }

 c = mpxplay_bitstream_get_byte(asf->bs_packet);

#ifdef DEBUG_DECODE
 asf_debugf("get_packet: data read ok %2.2X %2.2X %5d %d",c,PDS_GETB_LE32(mpxplay_bitstream_getbufpos(asf->bs_packet)-1),mpxplay_bitstream_leftbytes(asf->bs_packet),datapos);
#endif

 if(c!=0x82)
  goto resync;

 if(mpxplay_bitstream_get_le16(asf->bs_packet)!=0)
  goto resync;

 rsize+=2;

 asf->packet_flags = mpxplay_bitstream_get_byte(asf->bs_packet);
 asf->packet_property = mpxplay_bitstream_get_byte(asf->bs_packet);

 DO_2BITS(asf->packet_flags >> 5, packet_length, asf->packet_size);
 DO_2BITS(asf->packet_flags >> 1, padsize, 0); // sequence ignored
 DO_2BITS(asf->packet_flags >> 3, padsize, 0); // padding length

 asf->packet_timestamp = mpxplay_bitstream_get_le32(asf->bs_packet);
 mpxplay_bitstream_get_le16(asf->bs_packet); // duration
 // rsize has at least 11 bytes which have to be present

 if(asf->packet_flags & 0x01) {
  asf->packet_segsizetype = mpxplay_bitstream_get_byte(asf->bs_packet); rsize++;
  asf->packet_segments = asf->packet_segsizetype & 0x3f;
 }else{
  asf->packet_segments = 1;
  asf->packet_segsizetype = 0x80;
 }

 asf->packet_size_left = packet_length - padsize - rsize;
 if(packet_length < asf->hdr.min_pktsize)
  padsize += asf->hdr.min_pktsize - packet_length;
 asf->packet_padsize = padsize;


#ifdef DEBUG_DECODE
 asf_debugf("packet: size=%d padsize=%d  left=%d\n", asf->packet_size, asf->packet_padsize, asf->packet_size_left);
#endif
 return MPXPLAY_ERROR_INFILE_OK;
}

static int ASF_infile_decode(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis)
{
 struct asf_demuxer_data_s *asf=(struct asf_demuxer_data_s *)miis->private_data;
 struct mpxplay_streampacket_info_s *spi=NULL;
 asf_stream_data_s *ast = 0;
#ifdef DEBUG_DECODE
 static unsigned int pc=0;
#endif

 for(;;){
  int rsize = 0;
  if((asf->packet_size_left<FRAME_HEADER_SIZE) || (asf->packet_segments<1)){
   int ret;
#ifdef DEBUG_DECODE
   asf_debugf("PacketLeftSize:%d  Pad:%d Pos:%d\n", (long)asf->packet_size_left, (long)asf->packet_padsize, (long)fbfs->ftell(fbds));
#endif
   ret = asf_get_packet(asf,fbfs,fbds);
#ifdef DEBUG_DECODE
   asf_debugf("READ ASF PACKET  %d   r:%d   c:%d\n", ret, asf->packet_size_left, pc++);
#endif
   if(ret!=MPXPLAY_ERROR_INFILE_OK)
    return ret;
   asf->packet_time_start = 0;
   continue;
  }
  if(asf->packet_time_start == 0) {
   // read frame header
   int num = mpxplay_bitstream_get_byte(asf->bs_packet);
   asf->packet_segments--;
   rsize++;
   asf->packet_key_frame = (num & 0x80) >> 7;
   asf->stream_index = asf->asfid2avid[num & 0x7f];
   // sequence should be ignored!
   DO_2BITS(asf->packet_property >> 4, asf->packet_seq, 0);
   DO_2BITS(asf->packet_property >> 2, asf->packet_frag_offset, 0);
   DO_2BITS(asf->packet_property     , asf->packet_replic_size, 0);
#ifdef DEBUG_DECODE
   asf_debugf("key:%d num:%d stream:%d seq:%d offset:%d replic_size:%d\n",
    asf->packet_key_frame, num, asf->stream_index, asf->packet_seq, asf->packet_frag_offset, asf->packet_replic_size);
#endif
   if(asf->packet_replic_size > 1){
    if(asf->packet_replic_size < 8)
     goto fail;
    // it should be always at least 8 bytes - FIXME validate
    asf->packet_obj_size = mpxplay_bitstream_get_le32(asf->bs_packet);
    asf->packet_frag_timestamp = mpxplay_bitstream_get_le32(asf->bs_packet); // timestamp
    if(asf->packet_replic_size > 8)
     mpxplay_bitstream_skipbytes(asf->bs_packet,(asf->packet_replic_size-8));
    rsize += asf->packet_replic_size; // FIXME - check validity
   }else
    if(asf->packet_replic_size==1){
     // multipacket - frag_offset is begining timestamp
     asf->packet_time_start = asf->packet_frag_offset;
     asf->packet_frag_offset = 0;
     asf->packet_frag_timestamp = asf->packet_timestamp;
     asf->packet_time_delta = mpxplay_bitstream_get_byte(asf->bs_packet);
     rsize++;
    }else{ // asf->packet_replic_size==0
     goto fail;
    }
   if(asf->packet_flags & 0x01) {
    DO_2BITS(asf->packet_segsizetype >> 6, asf->packet_frag_size, 0); // 0 is illegal
    //printf("Fragsize %d\n", asf->packet_frag_size);
   }else{
    asf->packet_frag_size = asf->packet_size_left - rsize;
#ifdef DEBUG_DECODE
    asf_debugf("Using rest  %d %d %d\n", asf->packet_frag_size, asf->packet_size_left, rsize);
#endif
   }
   if(asf->packet_replic_size == 1) {
    asf->packet_multi_size = asf->packet_frag_size;
    if (asf->packet_multi_size > asf->packet_size_left)
     goto fail;
   }
   asf->packet_size_left -= rsize;
#ifdef DEBUG_DECODE
   asf_debugf("___objsize____  %d   %d    rs:%d\n", asf->packet_obj_size, asf->packet_frag_offset, rsize);
#endif

   if(asf->stream_index < 0
    //|| asf->streams[asf->stream_index]->discard >= AVDISCARD_ALL
    //|| (!asf->packet_key_frame && s->streams[asf->stream_index]->discard >= AVDISCARD_NONKEY)
   ){
    asf->packet_time_start = 0;
    // unhandled packet (should not happen)
    mpxplay_bitstream_skipbytes(asf->bs_packet,asf->packet_frag_size);
    asf->packet_size_left -= asf->packet_frag_size;
    continue;
   }
   asf->ast_curr = &asf->streams[asf->stream_index];
  }

  ast = asf->ast_curr;

  if((asf->packet_frag_offset!=ast->frag_offset || (asf->packet_frag_offset && asf->packet_seq!=ast->seq))){ // seq should be ignored
   // cannot continue current packet
   if(spi)
    spi->bs_leftbytes= 0;
   ast->frag_offset = 0;
   if(asf->packet_frag_offset != 0) {
    mpxplay_bitstream_skipbytes(asf->bs_packet,asf->packet_frag_size);
    asf->packet_size_left -= asf->packet_frag_size;
    continue;
   }
  }

  if(asf->packet_replic_size == 1) {
   // frag_offset is here used as the begining timestamp
   asf->packet_frag_timestamp = asf->packet_time_start;
   asf->packet_time_start += asf->packet_time_delta;
   asf->packet_obj_size = asf->packet_frag_size = mpxplay_bitstream_get_byte(asf->bs_packet);
   asf->packet_size_left--;
   asf->packet_multi_size--;
   if(asf->packet_multi_size < asf->packet_obj_size){
    asf->packet_time_start = 0;
    mpxplay_bitstream_skipbytes(asf->bs_packet,asf->packet_multi_size);
    asf->packet_size_left -= asf->packet_multi_size;
    continue;
   }
   asf->packet_multi_size -= asf->packet_obj_size;
   //printf("COMPRESS size  %d  %d  %d   ms:%d\n", asf->packet_obj_size, asf->packet_frag_timestamp, asf->packet_size_left, asf->packet_multi_size);
  }

  switch(asf->streams[asf->stream_index].streamtype){
   case MPXPLAY_SPI_STREAMTYPE_AUDIO:spi=miis->audio_stream;break;
#ifdef MPXPLAY_LINK_VIDEO
   case MPXPLAY_SPI_STREAMTYPE_VIDEO:spi=miis->video_stream;break;
#endif
   default:asf->packet_time_start = 0;                              // ???
           mpxplay_bitstream_skipbytes(asf->bs_packet,asf->packet_frag_size);
           asf->packet_size_left -= asf->packet_frag_size;          //
           continue;                                                //
  }

  if(ast->frag_offset == 0) { // new packet
   ast->seq = asf->packet_seq;
   //ast->pkt.pts = asf->packet_frag_timestamp;
   ast->stream_index = asf->stream_index;
   ast->packet_pos= asf->packet_pos;
   if(asf->streams[asf->stream_index].streamtype == MPXPLAY_SPI_STREAMTYPE_AUDIO)
    asf->packet_key_frame = 1;
  }

  asf->packet_size_left -= asf->packet_frag_size;
  if(asf->packet_size_left < 0)
   continue;
#ifdef DEBUG_DECODE
  asf_debugf("read data: lb:%d fs:%d",mpxplay_bitstream_leftbytes(asf->bs_packet),asf->packet_frag_size);
#endif

  if(mpxplay_bitstream_readbytes(asf->bs_packet,spi->bitstreambuf+asf->packet_frag_offset,asf->packet_frag_size)!=asf->packet_frag_size)
   goto fail;
  ast->frag_offset += asf->packet_frag_size;
  spi->bs_leftbytes+= asf->packet_frag_size;

  if(ast->frag_offset >= spi->bs_leftbytes) {
   ast->frag_offset = 0;
   if(ast->ds_span > 1){ // packet descrambling
    int offset;
#ifdef DEBUG_DECODE
    asf_debugf("descrambling: bsb:%d dsp:%d ",spi->bs_leftbytes,ast->ds_span);
#endif
    if(spi->bs_leftbytes>=asf->hdr.max_pktsize){ // paket size is greater than allocated descrambling buffer
     spi->bs_leftbytes=0;
     continue;
    }
    offset=0;
    while(offset < spi->bs_leftbytes){
     int off = offset / ast->ds_chunk_size;
     int row = off / ast->ds_span;
     int col = off % ast->ds_span;
     int idx = row + col * ast->ds_packet_size / ast->ds_chunk_size;
     //asf_debugf("off:%d  row:%d  col:%d  idx:%d\n", off, row, col, idx);
     memcpy(asf->descrambling_buffer + offset,spi->bitstreambuf + idx * ast->ds_chunk_size,ast->ds_chunk_size);
     offset += ast->ds_chunk_size;
    }
    memcpy(spi->bitstreambuf,asf->descrambling_buffer,spi->bs_leftbytes);
   }
#ifdef DEBUG_DECODE
   asf_debugf("packet %d %d\n", spi->bs_leftbytes, asf->packet_frag_size);
#endif
   break; // packet completed
  }
  continue;
fail:
  asf->packet_segments=0;
  asf->packet_size_left=0;
  asf->packet_time_start=0;
 }
 return MPXPLAY_ERROR_INFILE_OK;
}

static long ASF_infile_fseek(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis,long newmpxframenum)
{
 asf_demuxer_data_s *asf = miis->private_data;
 long packetnum,newfilepos;

 packetnum=(long)((float)asf->nb_packets*(float)newmpxframenum/(float)miis->allframes);
 newfilepos=packetnum*asf->packet_size+asf->data_offset;

 if(fbfs->fseek(fbds,newfilepos,SEEK_SET)<0)
  return MPXPLAY_ERROR_INFILE_EOF;

 return newmpxframenum;
}

static void ASF_infile_clearbuff(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis,unsigned int mpx_seektype)
{
 asf_demuxer_data_s *asf = miis->private_data;
 asf_stream_data_s *ast;
 int i;

 asf->packet_size_left = 0;
 asf->packet_segments = 0;
 asf->packet_flags = 0;
 asf->packet_property = 0;
 asf->packet_timestamp = 0;
 asf->packet_segsizetype = 0;
 asf->packet_segments = 0;
 asf->packet_seq = 0;
 asf->packet_replic_size = 0;
 asf->packet_key_frame = 0;
 asf->packet_padsize = 0;
 asf->packet_frag_offset = 0;
 asf->packet_frag_size = 0;
 asf->packet_frag_timestamp = 0;
 asf->packet_multi_size = 0;
 asf->packet_obj_size = 0;
 asf->packet_time_delta = 0;
 asf->packet_time_start = 0;

 for(i=0; i<asf->nb_streams; i++){
  ast= &asf->streams[i];
  ast->frag_offset=0;
  ast->seq=0;
 }
}

static char *asf_tag_get_str(char *str,unsigned int len,unsigned int type_byte,char **id3ip,char *id3p,struct mpxplay_textconv_func_s *mtfs)
{
 if(len){
  unsigned int convdone=0;
  memcpy(id3p,str,len);
  if(!type_byte && ((*(mtfs->control))&ID3TEXTCONV_UTF_AUTO)){
   len=mtfs->utf16LE_to_char(id3p,len);
   convdone=ID3TEXTCONV_UTF16;
  }
  len=mtfs->all_to_char(id3p,len,convdone);
  if(len){
   *id3ip=id3p;
   id3p+=len+1;
  }
 }
 return id3p;
}

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

static char *ASF_infile_tag_get(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis,char **id3ip,char *id3p,struct mpxplay_textconv_func_s *mtfs)
{
 asf_demuxer_data_s *asf = (asf_demuxer_data_s *)miis->private_data;
 unsigned int i;

 if(!asf)
  return id3p;

 for(i=0;i<=I3I_MAX;i++)
  id3p=asf_tag_get_str(asf->tag_strs[i],asf->tag_lens[i],asf->tag_type_byte[i],&id3ip[i],id3p,mtfs);

 id3p=asf_tag_get_num(asf->track  ,&id3ip[I3I_TRACKNUM],id3p);

 return id3p;
}

#if defined(DEBUG_FILEINFO) || defined(DEBUG_HEADER) || defined(DEBUG_DECODE)
static void asf_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

struct mpxplay_infile_func_s IN_ASF_funcs={
 0,
 NULL,
 NULL,
 &ASF_infile_check,
 &ASF_infile_check,
 &ASF_infile_open,
 &ASF_infile_close,
 &ASF_infile_decode,
 &ASF_infile_fseek,
 &ASF_infile_clearbuff,
 &ASF_infile_tag_get,
 NULL,
 NULL,
 {"ASF","WMA","WMV","ASX",NULL}
};

#endif // MPXPLAY_LINK_INFILE_ASF
