 /***************************************************************************************
 *This program is free software; you can redistribute it and/or modify					*
 * it under the terms of the GNU General Public License as published by					*
 * the Free Software Foundation; either version 2 of the License, or					*
 * (at your option) any later version.													*
 *																						*
 * The GPL can be found at: http://www.gnu.org/copyleft/gpl.html						*
 *																						*
 *																						*	
 ****************************************************************************************
 * Authors:																			  *
 *          Marc Dukette                                                              *
 **************************************************************************************/



#include "OGMReader.h"
#include "OGGStream.h"
//#include "../ogglib/lib/vorbis_codec.h"
#include "../ogglib/lib/ogg.h"
#include "../ogglib/lib/ivorbiscodec.h"
#include "../ogglib/lib/misc.h"

int CacheSize;
videoinfo thisvid;
typedef int (CALLBACK* pInputMediaRead)(char *data, unsigned int size);
typedef int (CALLBACK* pInputMediaSeek)(int   size, unsigned int method);
typedef int (CALLBACK* pInputMediaOpen)(LPTSTR lpFilename, int mode, int type, int reserve, int maxsize);
typedef void (CALLBACK* pInputMediaClose)();

pInputMediaRead InputMediaRead;
pInputMediaSeek InputMediaSeek;
pInputMediaOpen InputMediaOpen;
pInputMediaClose InputMediaClose;
HMODULE hDLL;
	ogg_sync_state   oy; /* sync and verify incoming physical bitstream */
	ogg_page         og; /* one Ogg bitstream page.  Vorbis packets are inside */
	ogg_packet       op; /* one raw packet of data for decode */

	stream_state os[2]; 
	vorbis_info      vi; /* struct that stores all the static vorbis bitstream
		  settings */
	vorbis_comment   vc; /* struct that stores all the bitstream user comments */
	stream_header*	 sh[2];

	vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
	vorbis_block     vb; /* local working space for packet->PCM decode */

//	int  bytes;
	int gotaudio;
	int gotvideo;

	//int i;
	int stream_init=0;
	ogg_int32_t **pcm;
	int samples;
	int readpage=1;
	int readpagea=1;
	int fsize;
	__int64 lastpos;

	int leftover=0;
	//char* convbuffer[8000];
//	stream_header* hi;
	//unsigned char temp[32000];
	//ogg_int16_t convbuffer[4096]; /* take 8k out of the data segment, not the stack */
	int convsize=4096;
	vorbis_info      viv;
	vorbis_comment   vcv; 

/*
 * Reading Functions
 *
 */

/*
 * Tries to open an AVI
 * with and without an index
 */
int FillHeader(LPTSTR lpFilename,int CacheSize)
{
	int i=0;
	int stream=0;
	int bytes;
	char inbuff[4096];
	char *buffer;
	lastpos=0;
	gotvideo=0;
	char comment[25][200];
	ogg_sync_init(&oy); /* Now we can read pages */
	if (!wcsstr(lpFilename,_T("://")))
	{
		InputMediaSeek(fsize-100000,SEEK_SET);
		bytes=4096;
		InputMediaRead(inbuff, 4096);
		buffer=ogg_sync_buffer(&oy,bytes);
		memcpy(buffer,inbuff,bytes);
		ogg_sync_wrote(&oy,bytes);
		stream_state_init(&os[0],0);

		while(1)
		{

			int result=ogg_sync_pageout(&oy,&og);
			if(result<=0)
			{
				int bytes=InputMediaRead(inbuff, 4096);
				if (!bytes) break;
				buffer=ogg_sync_buffer(&oy,bytes);
				memcpy(buffer,inbuff,bytes);
				ogg_sync_wrote(&oy,bytes);
				continue;
			}
			/* Don't complain about missing or corrupt data yet.  We'll
			catch it at the packet output phase */
			if(result==1)
			{
				stream=ogg_page_serialno(&og);
				if (stream==0) ogg_stream_pagein(&os[stream].os,&og); 
				else continue;
			}
			while(1)
			{
				result=ogg_stream_packetout(&os[0].os,&op);
				if (result==0)
				{
					InputMediaRead(inbuff, 4096);
					buffer=ogg_sync_buffer(&oy,bytes);
					memcpy(buffer,inbuff,bytes);
					ogg_sync_wrote(&oy,bytes);
					break;
				}
				if (result<0)
				{ 
					/* no page? must not be vorbis */
					break;
				}
				if (op.granulepos!=-1)
				{
					thisvid.video_frames=op.granulepos+1;
				}
			}		
		}
		InputMediaClose();
		fsize=InputMediaOpen(lpFilename,0,0,4000000,CacheSize);

	//	InputMediaSeek(0,SEEK_SET);
		stream=0;
		ogg_stream_clear(&os[0].os);
	}
	else
	{
		thisvid.video_frames=250000;
	}
	if (thisvid.video_frames==0)
		thisvid.video_frames=250000;

	
	ogg_sync_init(&oy); /* Now we can read pages */
	gotaudio=false;
	gotvideo=false;
//	if(vidinfo.input != NULL) {
		bytes=4096;
		i=0;
		InputMediaRead(inbuff, 4096);
		buffer=ogg_sync_buffer(&oy,bytes);
		memcpy(buffer,inbuff,bytes);
		ogg_sync_wrote(&oy,bytes);
//		i=1;
	while(!gotaudio||(!gotvideo))
	{
			//convbuffer=(ogg_int16_t*) outmemory;
			/* submit a 4k block to libvorbis' Ogg layer */
			/* Get the first page. */
			int result=ogg_sync_pageout(&oy,&og);
			if (result==0)
			{
				InputMediaRead(inbuff, 4096);
				buffer=ogg_sync_buffer(&oy,bytes);
				memcpy(buffer,inbuff,bytes);
				ogg_sync_wrote(&oy,bytes);
				continue;
			}
			if (result<0)
			{
				return -1;
			}
			//ogg_stream_init(&os.os,ogg_page_serialno(&og));
			stream_state_init(&os[i],ogg_page_serialno(&og));
			stream=ogg_page_serialno(&og);

			if(ogg_stream_pagein(&os[stream].os,&og)<0)
			{ 
				/* error; stream version mismatch perhaps */
				return -1;
			}

			result=ogg_stream_packetout(&os[i].os,&op);
			if (result==0)
			{
				if (i==0)
					gotvideo=-1;
				if (i==1) 
					gotaudio=-1;
				InputMediaRead(inbuff, 4096);
				buffer=ogg_sync_buffer(&oy,bytes);
				memcpy(buffer,inbuff,bytes);
				ogg_sync_wrote(&oy,bytes);
				continue;
			}
			if (result<0)
			{ 
				/* no page? must not be vorbis */
				return -1;
			}


			/* extract the initial header from the first page and verify that the
			   Ogg bitstream is in fact Vorbis data */

			/* I handle the initial header first instead of just having the code
			   read all three Vorbis headers at once because reading the initial
			   header is an easy way to identify a Vorbis bitstream and it's
			   useful to see that functionality seperated out. */
			if (stream_header_in(&sh[i],&op)!=0)
			{
				vorbis_info_init(&vi);
				vorbis_comment_init(&vc);
				if(vorbis_synthesis_headerin(&vi,&vc,&op)<0)
				{ 
					/* error case; not a vorbis header */
					return -1;
				}
				gotaudio=1;
//				stream_state_init(&os[i],ogg_page_serialno(&og));
				//if(ogg_stream_pagein(&os[i].os,&og)<0)
				//{ 
				//	/* error; stream version mismatch perhaps */
				//	return -1;
				//}

			}
			else
			{
				if (strcmp(sh[i]->streamtype,MT_Video)) gotaudio=1;
				else gotvideo=1;
			}
//			else
			/* Get the serial number and set up the rest of decode. */
			/* serialno first; use it to set up a logical stream */

//			stream_init=1;
			i++;
	}

	if (gotvideo>0)
	{
		int i=0;

		while(!i)
		{
			while(!i)
			{
				int result=ogg_sync_pageout(&oy,&og);
				if(result==0)
				{
					InputMediaRead(inbuff, 4096);
					buffer=ogg_sync_buffer(&oy,bytes);
					memcpy(buffer,inbuff,bytes);
					ogg_sync_wrote(&oy,bytes);
					continue;
				}
				if(result==1)
				{
					stream=ogg_page_serialno(&og);
					ogg_stream_pagein(&os[stream].os,&og);
					while(!i)
					{
						result=ogg_stream_packetout(&os[0].os,&op);
						if(result==0)
						{
							InputMediaRead(inbuff, 4096);
							buffer=ogg_sync_buffer(&oy,bytes);
							memcpy(buffer,inbuff,bytes);
							ogg_sync_wrote(&oy,bytes);
							break;
						}
						if(result<0)
						{
							return -1;
						}
						vorbis_info_init(&viv);
						vorbis_comment_init(&vcv);
						viv.rate=1;
						if(vorbis_synthesis_headerin(&viv,&vcv,&op)<0)
						{ 
							return -1;
						}
						int c=0;
						int j=0;
#if (_WIN32_WCE >= 300)
						while ((c<vcv.comments)&&(_strnicmp(vcv.user_comments[c],"CHAPTER",7))) c++;
#else
						while ((c<vcv.comments)&&(strncmp(vcv.user_comments[c],"CHAPTER",7))) c++;
#endif
						c++;
#if (_WIN32_WCE >= 300)
						for (;c<vcv.comments&&(!_strnicmp(vcv.user_comments[c],"CHAPTER",7));c+=2)
#else
						for (;c<vcv.comments&&(!strncmp(vcv.user_comments[c],"CHAPTER",7));c+=2)
#endif
						{
							MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,vcv.user_comments[c]+14,-1,thisvid.ChapterDisplay[j],vcv.comment_lengths[c]-14);
							//MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,vcv.user_comments[(c-1)]+10,-1,thisvid.ChapterDisplay[(c-1)/2]+vcv.comment_lengths[c]-14,8);
							j++;
						}
						thisvid.ChapterCount=j;
						i++;
					}
				}
			}
		}	
	
	}

	if (gotaudio>0)
	{
		int i=0;

		while(i<2)
		{
			while(i<2)
			{
				int result=ogg_sync_pageout(&oy,&og);
				if(result==0)
				{
					InputMediaRead(inbuff, 4096);
					buffer=ogg_sync_buffer(&oy,bytes);
					memcpy(buffer,inbuff,bytes);
					ogg_sync_wrote(&oy,bytes);
					continue;
				}
				if(result==1)
				{
					stream=ogg_page_serialno(&og);
					ogg_stream_pagein(&os[stream].os,&og);
					while(i<2)
					{
						result=ogg_stream_packetout(&os[1].os,&op);
						if(result==0)
						{
							InputMediaRead(inbuff, 4096);
							buffer=ogg_sync_buffer(&oy,bytes);
							memcpy(buffer,inbuff,bytes);
							ogg_sync_wrote(&oy,bytes);
							break;
						}
						if(result<0)
						{
							return -1;
						}
						if (!sh[1])
						{
							if(vorbis_synthesis_headerin(&vi,&vc,&op)<0)
							{ 
								return -1;
							}
						}
						else
						{
							i++;
						}
						i++;
					}
				}
			}
		}	
	
	}
	
	
		stream_init=0;
		if (!sh[1]&&(gotaudio>0))
		{
			vorbis_synthesis_init(&vd,&vi); 
			vorbis_block_init(&vd,&vb);     
			convsize=4096/vi.channels;
		}
	return 1;
}

int AVIDecaps_Open(LPTSTR lpFilename, int Cache, int CacheSize,videoinfo** vidinfo)
{
	int ret=0;
	wcscpy(thisvid.m_lpFilename,lpFilename);
	thisvid.m_type=Cache;
	::CacheSize=CacheSize;

	if (wcsstr(lpFilename,_T("://")))
		hDLL=LoadLibrary(_T("InputMediaHTTP.dll"));
	else if (Cache)
		hDLL=LoadLibrary(_T("InputMediaCache.dll"));
	else
		hDLL=LoadLibrary(_T("InputMedia.dll"));

	if (!hDLL) return 0;
	leftover=0;
	InputMediaRead=(pInputMediaRead)GetProcAddress(hDLL,_T("InputMediaRead"));
	InputMediaSeek=(pInputMediaSeek)GetProcAddress(hDLL,_T("InputMediaSeek"));
	InputMediaOpen=(pInputMediaOpen)GetProcAddress(hDLL,_T("InputMediaOpen"));
	InputMediaClose=(pInputMediaClose)GetProcAddress(hDLL,_T("InputMediaClose"));
	if ((fsize=InputMediaOpen(lpFilename, 0, 0,-1,1000000))==0)
	{
		FreeLibrary(hDLL);
		hDLL=NULL;
		return 0;
	}
	thisvid.hIOMutex = CreateMutex (NULL, FALSE, NULL);

	thisvid.video_pos  = 0;
	thisvid.audio_posc = 0;
	thisvid.audio_posb = 0;

	thisvid.idx         = NULL;
	thisvid.video_index = NULL;
	thisvid.audio_index = NULL;

	
	FillHeader(lpFilename,CacheSize);
	thisvid.audio_bytes=0;
	//thisvid.video_frames=100000;
	//thisvid.audio_bytes=10584000;
	strncpy(thisvid.compressor,sh[0]->subtype,4);
	thisvid.width=sh[0]->video.width;
	thisvid.height=sh[0]->video.height;
	thisvid.fps=((double)10000000)/((double)sh[0]->time_unit);
	if (gotaudio>0)
	{
		if (!sh[1])
		{
			thisvid.a_fmt=0;
			thisvid.a_chans=vi.channels;
			thisvid.a_rate=vi.rate;
			thisvid.a_bits=vi.bitrate_nominal;
			thisvid.audio_bytes=((double)((double)thisvid.video_frames)/thisvid.fps)*(double)thisvid.a_rate*thisvid.a_chans*2;
		}
		else
		{
			thisvid.a_fmt=atoi(sh[1]->subtype);
			thisvid.a_chans=sh[1]->samples_per_unit;
			thisvid.a_rate=sh[1]->audio.channels;
			thisvid.audio_bytes=((double)((double)thisvid.video_frames)/thisvid.fps)*(double)sh[1]->audio.avgbytespersec;

		}
	}
	*vidinfo=&::thisvid;
	return 1;
}

bool didseek=false;
char oneframe[48000];
int oneframelen;
__int64  video_reftime;
__int64  audio_reftime;
/*
 * Reads the next video Frame into
 * buffer, return the actual size of
 * the frame.
 *
 */
#define VIDEO_STREAM 0
int AVIDecaps_NextVideoFrame(char *buffer, int drop)
{

	int stream=0;
	char inbuff[4096];
	int eos=0;
	int bytes;
	if (!didseek&&(oneframelen))
	{
		memcpy(buffer,oneframe,oneframelen);
		bytes=oneframelen;
		oneframelen=0;
		return bytes;
	}
	WaitForSingleObject(thisvid.hIOMutex, INFINITE);
	while(!eos)
	{
		if (readpage)
		{
			int result=ogg_sync_pageout(&oy,&og);
			readpage=0;
			if(result==0) /* missing or corrupt data at this page position */
			{
				bytes=4096;
				char* buff=ogg_sync_buffer(&oy,bytes);
				bytes=InputMediaRead(buff, 4096);
					if (bytes==0)
					{
					    ReleaseMutex(thisvid.hIOMutex);
						return -1;
					}
				if (ogg_sync_wrote(&oy,bytes)==-1)
				{
					int error=1;
				}
				readpage=1;
				continue;
			}
			else if (result<0)
			{
				readpage=1;
				continue;
			}
			stream=ogg_page_serialno(&og);
			ogg_stream_pagein(&os[stream].os,&og); 
		}							 
		if (stream==VIDEO_STREAM)
		{
			int result;
			unsigned char* temp;
			int bufferlen;
			bool isKey;
			result= stream_sampleout(&os[VIDEO_STREAM], sh[VIDEO_STREAM],
										   (bool*)&eos, &isKey,
										   &video_reftime,
										   &temp, &bufferlen);
			if(result==0||(result<0&&(result!=-2052)))
			{
				readpage=1;
			}
			else if (isKey||(!didseek))
			{
				ReleaseMutex(thisvid.hIOMutex);
				memcpy(buffer,temp,bufferlen);
				if (os[VIDEO_STREAM].lastpos>=0) thisvid.video_pos=os[VIDEO_STREAM].lastpos;
				else
					thisvid.video_pos++;
				return bufferlen;
			}
			else
			{
				if (os[VIDEO_STREAM].lastpos>0) 
					thisvid.video_pos=os[VIDEO_STREAM].lastpos;
				else
					thisvid.video_pos++;
			}
		}
		else
			readpage=1;
	}
	ReleaseMutex(thisvid.hIOMutex);

	return 0;
}

/*
 * Reads any amount of audio
 * data. FIXME : should return
 * the actual number read.
 */
#define AUDIO_STREAM 1
int AVIDecaps_ReadAudio(char *audbuf, int bytes)
{

	
	static char left[4096];
	int eos=0;
	int stream=AUDIO_STREAM;
	int todo=bytes;
	int got=0;
	//ogg_int16_t* convbuffer=(ogg_int16_t*) audbuf;
	while(!eos)
	{
	    WaitForSingleObject(thisvid.hIOMutex, INFINITE);
		if (readpagea)
		{
			do
			{
				int result=ogg_sync_pageout(&oy,&og);
				readpagea=0;
				if(result==0) /* missing or corrupt data at this page position */
				{
					int readbytes=4096;
					char* buff=ogg_sync_buffer(&oy,4096);
					readbytes=InputMediaRead(buff, 4096);
					if (readbytes==0)
					{
					    ReleaseMutex(thisvid.hIOMutex);
						return 0;
					}
					if (ogg_sync_wrote(&oy,readbytes)==-1)
					{
						int error=1;
					}
					readpagea=1;
					break;
				}
				else if (result<0)
				{
					readpagea=1;
					break;
				}
				stream=ogg_page_serialno(&og);
				ogg_stream_pagein(&os[stream].os,&og); 
			}while(stream!=AUDIO_STREAM);
		}		
		ReleaseMutex(thisvid.hIOMutex);
		if (readpagea) continue;
		int result=1;
		unsigned char* temp;
		int bufferlen;
		bool key;
		if (!sh[1])
		{
			result=ogg_stream_packetout(&os[AUDIO_STREAM].os,&op);
			if(result==0)
			{
				readpagea=1;
				continue; //return NEED_MORE; /* need more data */
			}
			if(result>=0)
			{
				/* we have a packet.  Decode it */
				if(vorbis_synthesis(&vb,&op)==0) /* test for success! */
					vorbis_synthesis_blockin(&vd,&vb);
				/* 

				**pcm is a multichannel float vector.  In stereo, for
				example, pcm[0] is left, and pcm[1] is right.  samples is
				the size of each channel.  Convert the float values
				(-1.<=range<=1.) to whatever PCM format and write it out */
				while((samples=vorbis_synthesis_pcmout(&vd,&pcm))>0)
				{
					int j;
					int i;

					for(i=0;i<vi.channels;i++) 
					{
						ogg_int32_t *src=pcm[i];
						short *dest=((short *)audbuf)+i;
						for(j=0;j<samples;j++) 
						{
							*dest=CLIP_TO_15(src[j]>>9);
							dest+=vi.channels;
						}
					}
					vorbis_synthesis_read(&vd,samples); 
					got+=2*vi.channels*samples;
					audbuf+=2*vi.channels*samples;
					if (got>=2000)
					{
						if (op.granulepos!=-1)
						{
							audio_reftime=mediatime_to_reference_time(SEC_IN_REFTIME, vi.rate, op.granulepos);
							lastpos=op.granulepos;
							if (lastpos==0) lastpos=1;
						}
						else if (lastpos)
						{
							lastpos+=samples;
							audio_reftime=mediatime_to_reference_time(SEC_IN_REFTIME, vi.rate, lastpos);
						}
						return got;
					}

				}	    
			}
			if(result==0)
			{
				readpagea=1;
			}
		}
		else
		{
			while(todo)
			{
				if (leftover)
				{
					if (leftover<=todo)
					{
						memcpy(audbuf,left,leftover);
						audbuf+=leftover;
						todo-=leftover;
						leftover=0;
					}
					else
					{
						memcpy(audbuf,left,todo);
						leftover-=todo;
						memmove(left,left+todo,leftover);
						return bytes;
					}
				}
				result= stream_sampleout(&os[AUDIO_STREAM], sh[AUDIO_STREAM],
										   (bool*)&eos, &key,
										   &audio_reftime, 
										   &temp, &bufferlen);
				if(result==0)
				{
					readpagea=1;
					break;
				}
				else 
				{
					if (bufferlen<todo)
					{
						memcpy(audbuf,temp,bufferlen);
						audbuf+=bufferlen;
						todo-=bufferlen;
					}
					else
					{
						memcpy(left,temp+todo,bufferlen-todo);
						leftover=bufferlen-todo;
						memcpy(audbuf,temp,todo);
						todo=0;
						return bytes;
					}
				}
			}
		}

	}	
  ReleaseMutex(thisvid.hIOMutex);
	
  return 0;
}


double AVIDecaps_FrameRate()
{

	return (double) thisvid.fps;
}


int AVIDecaps_AudioSeek(long bytes)
{
   return 0;
}

BOOL AVIDecaps_isKeyframe(long frame)
{
  return true;
}



char onebuff[8192];
int AVIDecaps_Seek(int percent, int isFrame)
{
	__int64 audio_bytes,bytes;
	  WaitForSingleObject(thisvid.hIOMutex, INFINITE);
begin:
	  if (!isFrame)
	  {
			bytes  = (__int64) (((__int64)percent) * fsize)/100;
	  }
	  else
	  {
			double fperc=((double)percent)/((double)thisvid.video_frames);
			bytes  = (__int64) (fperc * ((double)fsize));
	  }
	  	InputMediaSeek(bytes,SEEK_SET);
		ogg_sync_clear(&oy);
		stream_state_reset(&os[0]);
		stream_state_reset(&os[1]);
		readpage=readpagea=1;
		lastpos=0;
		if (percent==0)
		{
			video_reftime=0;
			audio_reftime=0;
			ReleaseMutex(thisvid.hIOMutex);
			thisvid.video_pos=0;
			didseek=0;	
			oneframelen=0;
			return thisvid.audio_bytes;
		}
		video_reftime=-1;
		audio_reftime=-1;
		didseek=1;
		oneframelen=0;
		while(video_reftime<0)
		{
			oneframelen=AVIDecaps_NextVideoFrame(oneframe,0);
			if (oneframelen<0) 
			{
				percent=0;
				goto begin;
			}
		}
		if (gotaudio>0)
		{
			while(audio_reftime<0)
			{
				if (!AVIDecaps_ReadAudio(onebuff,4096))
				{
					percent=0;
					goto begin;
				}			
			}
			while (audio_reftime>	video_reftime)
			{
				oneframelen=AVIDecaps_NextVideoFrame(oneframe,0);
				if (oneframelen<0) 
				{
					percent=0;
					goto begin;
				}
			}
			while (audio_reftime<	video_reftime)
			{
				if (!AVIDecaps_ReadAudio(onebuff,1024))
				{
					percent=0;
					goto begin;
				}			
			}
		}
		didseek=0;	
      ReleaseMutex(thisvid.hIOMutex);
	if (!sh[1])
	{
		audio_bytes=((double)((double)(thisvid.video_frames-thisvid.video_pos))/thisvid.fps)*(double)thisvid.a_rate*thisvid.a_chans*2;
	}
	else
	{
		audio_bytes=((double)((double)(thisvid.video_frames-thisvid.video_pos))/thisvid.fps)*(double)sh[1]->audio.avgbytespersec;
	}

      return audio_bytes;
}


int AVIDecaps_SeekChapter(int Chapter)
{
	double h,m,s;
	int shouldbeframe;
	char t[4];
	__int64 audio_bytes,bytes;
	double percent;

	  WaitForSingleObject(thisvid.hIOMutex, INFINITE);
begin:
	  int start=0;
#if (_WIN32_WCE >= 300)
	  while (_strnicmp(vcv.user_comments[start],"CHAPTER",7)) start++;
#else
	  while (strncmp(vcv.user_comments[start],"CHAPTER",7)) start++;
#endif
	  strncpy(t,vcv.user_comments[Chapter*2+start]+10,2);
	  t[2]=0;
	  h=atoi(t);
	  strncpy(t,vcv.user_comments[Chapter*2+start]+13,2);
	  t[2]=0;
	  m=atoi(t);
	  strncpy(t,vcv.user_comments[Chapter*2+start]+16,2);
	  t[2]=0;
	  s=atoi(t);
		s=(h*3600)+(m*60)+s;
		percent=(double)(s*thisvid.fps)/(double)(thisvid.video_frames);
		shouldbeframe=(s*thisvid.fps);
repeat:
        bytes  = (__int64) (percent * (double)fsize);
	  	InputMediaSeek(bytes,SEEK_SET);
		ogg_sync_clear(&oy);
		stream_state_reset(&os[0]);
		stream_state_reset(&os[1]);
		readpage=readpagea=1;
		lastpos=0;
		if (percent==0)
		{
			video_reftime=0;
			audio_reftime=0;
			ReleaseMutex(thisvid.hIOMutex);
			thisvid.video_pos=0;
			didseek=0;	
			oneframelen=0;
			return thisvid.audio_bytes;
		}
		video_reftime=-1;
		audio_reftime=-1;
		didseek=1;
		oneframelen=0;
		do
		{
			oneframelen=AVIDecaps_NextVideoFrame(oneframe,0);
			if (oneframelen<0) 
			{
				percent=0;
				goto begin;
			}
		}while(video_reftime<0||(thisvid.video_pos<shouldbeframe));

		if (thisvid.video_pos>(shouldbeframe+thisvid.fps))
		{
			percent-=.01;
			goto repeat;
		}
		if (gotaudio>0)
		{
			while(audio_reftime<0)
			{
				if (!AVIDecaps_ReadAudio(onebuff,4096))
				{
					percent=0;
					goto begin;
				}			
			}
			while (audio_reftime>	video_reftime)
			{
				oneframelen=AVIDecaps_NextVideoFrame(oneframe,0);
				if (oneframelen<0) 
				{
					percent=0;
					goto begin;
				}
			}
			while (audio_reftime<	video_reftime)
			{
				if (!AVIDecaps_ReadAudio(onebuff,1024))
				{
					percent=0;
					goto begin;
				}			
			}
		}
		didseek=0;	
      ReleaseMutex(thisvid.hIOMutex);
	if (!sh[1])
	{
		audio_bytes=((double)((double)(thisvid.video_frames-thisvid.video_pos))/thisvid.fps)*(double)thisvid.a_rate*thisvid.a_chans*2;
	}
	else
	{
		audio_bytes=((double)((double)(thisvid.video_frames-thisvid.video_pos))/thisvid.fps)*(double)sh[1]->audio.avgbytespersec;

	}

      return audio_bytes;
}

int AVIDecaps_ReSeekAudio()
{
	__int64 audio_bytes; 
	  WaitForSingleObject(thisvid.hIOMutex, INFINITE);
		while(audio_reftime<0)
		{
			if (!AVIDecaps_ReadAudio(onebuff,4096))
			{
			      ReleaseMutex(thisvid.hIOMutex);
				return 0;
			}
		}
		while (audio_reftime<	video_reftime)
		{
			if (!AVIDecaps_ReadAudio(onebuff,1024))
			{
			      ReleaseMutex(thisvid.hIOMutex);
				return 0;
			}			
		}
      ReleaseMutex(thisvid.hIOMutex);

	if (!sh[1])
	{
		audio_bytes=((double)((double)(thisvid.video_frames-thisvid.video_pos))/thisvid.fps)*(double)thisvid.a_rate*thisvid.a_chans*2;
	}
	else
	{
		audio_bytes=((double)((double)(thisvid.video_frames-thisvid.video_pos))/thisvid.fps)*(double)sh[1]->audio.avgbytespersec;

	}

    return audio_bytes;
}

double AVIDecaps_GetProgress()
{
	return (double) ((double)(thisvid.video_pos))*100.0/((double)thisvid.video_frames);
}


int AVIDecaps_Close()
{
	ogg_stream_clear(&os[0].os);
	ogg_stream_clear(&os[1].os);

	/* ogg_page and ogg_packet structs always point to storage in
	   libvorbis.  They're never freed or manipulated directly */

	vorbis_block_clear(&vb);
	vorbis_dsp_clear(&vd);
	vorbis_comment_clear(&vc);
	vorbis_info_clear(&vi);  /* must be called last */

	ogg_sync_clear(&oy);
	if (hDLL)
	{
		InputMediaClose();
		FreeLibrary(hDLL);
		hDLL=NULL;
	}
	return 1;
}


void InitializeReader(reader* rd)
{
	rd->READER_Open=AVIDecaps_Open;
	rd->READER_ReadAudio=AVIDecaps_ReadAudio;
	rd->READER_NextVideoFrame=AVIDecaps_NextVideoFrame;
	rd->READER_Seek=AVIDecaps_Seek;
	rd->READER_ReSeekAudio=AVIDecaps_ReSeekAudio;
	rd->READER_GetProgress=AVIDecaps_GetProgress;
	rd->READER_Close=AVIDecaps_Close;
	rd->READER_isKeyFrame=AVIDecaps_isKeyframe;
	rd->READER_SeekChapter=AVIDecaps_SeekChapter;
}