| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964 | #include "main.h"#include "api__in_avi.h"#include "../nsavi/nsavi.h"#include "interfaces.h"#include "../nu/AudioOutput.h"#include "../Winamp/wa_ipc.h"#include <api/service/waservicefactory.h>#include "VideoThread.h"#include "win32_avi_reader.h"#include "http_avi_reader.h"#include "StreamSelector.h"#include <shlwapi.h>#include <strsafe.h>#include <map>nsavi::HeaderList header_list;int video_stream_num, audio_stream_num;ifc_avivideodecoder *video_decoder=0;IVideoOutput *video_output=0;HANDLE audio_break=0, audio_resume=0, audio_break_done=0;static Streams streams;static bool checked_in_dshow=false;extern int GetOutputTime();class StatsFOURCC{public:	uint32_t GetAudioStat()	{		uint32_t fourcc=0;		uint32_t max=0;		for (Stats::iterator itr = audio_types.begin();itr!=audio_types.end();itr++)		{			if (itr->second > max)			{				max = itr->second;				fourcc = itr->first;			}		}		return fourcc;	}	uint32_t GetVideoStat()	{		uint32_t fourcc=0;		uint32_t max=0;		for (Stats::iterator itr = video_fourccs.begin();itr!=video_fourccs.end();itr++)		{			if (itr->second > max)			{				max = itr->second;				fourcc = itr->first;			}		}		return fourcc;	}	typedef std::map<uint32_t, uint32_t> Stats;	Stats audio_types;	Stats video_fourccs;};static StatsFOURCC stats;class AVIWait{public:	int WaitOrAbort(int time_in_ms)	{		HANDLE events[] = {killswitch, seek_event};		int ret = WaitForMultipleObjects(2, events, FALSE, time_in_ms);		if (ret == WAIT_TIMEOUT)			return 0;		else if (ret == WAIT_OBJECT_0)			return 1;		else if (ret == WAIT_OBJECT_0+1)			return 2;		return -1;	}};static bool audio_opened=false;static ifc_aviaudiodecoder *audio_decoder=0;static char audio_output[65536];static nu::AudioOutput<AVIWait> out(&plugin);// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}static const GUID playbackConfigGroupGUID ={	0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf }};static int GetStreamNumber( uint32_t id ){	char *stream_data = (char *)( &id );	if ( !isxdigit( stream_data[ 0 ] ) || !isxdigit( stream_data[ 1 ] ) )		return -1;	stream_data[ 2 ] = 0;	int stream_number = strtoul( stream_data, 0, 16 );	return stream_number;}static ifc_aviaudiodecoder *FindAudioDecoder( const nsavi::AVIH *avi_header, const nsavi::STRL &stream ){	unsigned int bits_per_sample = (unsigned int)AGAVE_API_CONFIG->GetUnsigned( playbackConfigGroupGUID, L"bits", 16 );	if ( bits_per_sample >= 24 )	bits_per_sample = 24;	else	bits_per_sample = 16;	unsigned int max_channels;	// get max channels	if ( AGAVE_API_CONFIG->GetBool( playbackConfigGroupGUID, L"surround", true ) )		max_channels = 6;	else if ( AGAVE_API_CONFIG->GetBool( playbackConfigGroupGUID, L"mono", false ) )		max_channels = 1;	else		max_channels = 2;	size_t n = 0;	waServiceFactory *sf = 0;	while ( sf = plugin.service->service_enumService( WaSvc::AVIDECODER, n++ ) )	{		svc_avidecoder *dec = static_cast<svc_avidecoder *>( sf->getInterface() );		if ( dec )		{			ifc_aviaudiodecoder *decoder = 0;			if ( dec->CreateAudioDecoder( avi_header, stream.stream_header, stream.stream_format, stream.stream_data,				 bits_per_sample, max_channels, false,				 &decoder ) == svc_avidecoder::CREATEDECODER_SUCCESS )			{				sf->releaseInterface( dec );				return decoder;			}			sf->releaseInterface( dec );		}	}	return 0;}static ifc_avivideodecoder *FindVideoDecoder(const nsavi::AVIH *avi_header, const nsavi::STRL &stream){	size_t n = 0;	waServiceFactory *sf = 0;	while (sf = plugin.service->service_enumService(WaSvc::AVIDECODER, n++))	{		svc_avidecoder *dec = static_cast<svc_avidecoder *>(sf->getInterface());		if (dec)		{			ifc_avivideodecoder *decoder=0;			if (dec->CreateVideoDecoder(avi_header, stream.stream_header, stream.stream_format, stream.stream_data, &decoder) == svc_avidecoder::CREATEDECODER_SUCCESS)			{				sf->releaseInterface(dec);				return decoder;			}			sf->releaseInterface(dec);		}	}	return 0;}static bool OnAudio( uint16_t type, const void **input_buffer, uint32_t *input_buffer_bytes ){	uint32_t output_len = sizeof( audio_output );	int ret = audio_decoder->DecodeChunk( type, input_buffer, input_buffer_bytes, audio_output, &output_len );	//if (*input_buffer_bytes != 0)	//DebugBreak();	if ( ( ret == ifc_aviaudiodecoder::AVI_SUCCESS || ret == ifc_aviaudiodecoder::AVI_NEED_MORE_INPUT ) && output_len )	{		if ( !audio_opened )		{			unsigned int sample_rate, channels, bps;			bool is_float;			if ( audio_decoder->GetOutputProperties( &sample_rate, &channels, &bps, &is_float ) == ifc_aviaudiodecoder::AVI_SUCCESS )			{				audio_opened = out.Open( 0, channels, sample_rate, bps );				if ( !audio_opened )					return false;			}			else			{				// TODO: buffer audio.  can nu::AudioOutput handle this for us?			}		}		if ( audio_opened )			out.Write( audio_output, output_len );	}	return true;}static bool CheckDSHOW(){	if (!checked_in_dshow)	{		LPCWSTR pluginsDir = (LPCWSTR)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORYW);		wchar_t in_dshow_path[MAX_PATH] = {0};		PathCombine(in_dshow_path, pluginsDir, L"in_dshow.dll");		in_dshow = LoadLibrary(in_dshow_path);		checked_in_dshow = true;	}	return !!in_dshow;}static void CALLBACK DSHOWAPC( ULONG_PTR param ){	In_Module *dshow_mod_local = 0;	wchar_t *playFile = (wchar_t *)param;	if ( in_dshow )	{		typedef In_Module *( *MODULEGETTER )( );		MODULEGETTER moduleGetter = (MODULEGETTER)GetProcAddress( in_dshow, "winampGetInModule2" );		if ( moduleGetter )			dshow_mod_local = moduleGetter();	}	if ( dshow_mod_local )	{		dshow_mod_local->outMod = plugin.outMod;		if ( dshow_mod_local->Play( playFile ) )			dshow_mod_local = 0;	}	free( playFile );	if ( !dshow_mod_local )	{		if ( WaitForSingleObject( killswitch, 200 ) != WAIT_OBJECT_0 )			PostMessage( plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0 );	}	else		dshow_mod = dshow_mod_local;}/* --- Video Window text info --- */void SetVideoInfoText(){	wchar_t audio_name[128] = {0}, audio_properties[256] = {0};	wchar_t video_name[128] = {0}, video_properties[256] = {0};	wchar_t video_info[512] = {0};	if (audio_decoder && video_decoder)	{		GetAudioCodecName(audio_name, sizeof(audio_name)/sizeof(*audio_name), header_list.stream_list[audio_stream_num].stream_format);		GetAudioCodecDescription(audio_properties, sizeof(audio_properties)/sizeof(*audio_properties), header_list.stream_list[audio_stream_num].stream_format); 		GetVideoCodecName(video_name, sizeof(video_name)/sizeof(*video_name), header_list.stream_list[video_stream_num].stream_format); 		GetVideoCodecDescription(video_properties, sizeof(video_properties)/sizeof(*video_properties), header_list.stream_list[video_stream_num].stream_format); 		StringCbPrintf(video_info, sizeof(video_info), L"AVI: %s (%s), %s (%s)", audio_name, audio_properties, video_name, video_properties);		video_output->extended(VIDUSER_SET_INFOSTRINGW,(INT_PTR)video_info,0);	}	else if (audio_decoder)	{		GetAudioCodecName(audio_name, sizeof(audio_name)/sizeof(*audio_name), header_list.stream_list[audio_stream_num].stream_format);		GetAudioCodecDescription(audio_properties, sizeof(audio_properties)/sizeof(*audio_properties), header_list.stream_list[audio_stream_num].stream_format); 		StringCbPrintf(video_info, sizeof(video_info), L"AVI: %s (%s)", audio_name, audio_properties);		video_output->extended(VIDUSER_SET_INFOSTRINGW,(INT_PTR)video_info,0);	}	else if (video_decoder)	{		GetVideoCodecName(video_name, sizeof(video_name)/sizeof(*video_name), header_list.stream_list[video_stream_num].stream_format);		GetVideoCodecDescription(video_properties, sizeof(video_properties)/sizeof(*video_properties), header_list.stream_list[video_stream_num].stream_format); 		StringCbPrintf(video_info, sizeof(video_info), L"AVI: %s (%s)", video_name, video_properties);		video_output->extended(VIDUSER_SET_INFOSTRINGW,(INT_PTR)video_info,0);	}}void Streams::Reset(){	num_audio_streams    = 0;	num_video_streams    = 0;		current_audio_stream = 0;}void Streams::AddAudioStream(int stream_num){	audio_streams[num_audio_streams++]=stream_num;}void Streams::AddVideoStream(int stream_num){	video_streams[num_video_streams++]=stream_num;}void Streams::SetAudioStream(int stream_num){	for (int i=0;i<num_audio_streams;i++)	{		if (audio_streams[i] == stream_num)			current_audio_stream=i;	}}void Streams::SetVideoStream(int stream_num){	for (int i=0;i<num_video_streams;i++)	{		if (video_streams[i] == stream_num)			current_video_stream=i;	}}int Streams::getNumAudioTracks(){	return num_audio_streams;}void Streams::enumAudioTrackName(int n, char *buf, int size){	StringCchPrintfA(buf, size, "Audio Stream %d", n);}int Streams::getCurAudioTrack(){	return current_audio_stream;}int Streams::getNumVideoTracks(){	return num_video_streams;}void Streams::enumVideoTrackName(int n, char *buf, int size){	StringCchPrintfA(buf, size, "Video Stream %d", n);}int Streams::getCurVideoTrack(){	return current_video_stream;}void Streams::setAudioTrack(int n){	SetEvent(audio_break);	WaitForSingleObject(audio_break_done, INFINITE);	int i = audio_streams[n];	const nsavi::STRL &stream = header_list.stream_list[i];	if (audio_decoder)	{		audio_decoder->Close();		audio_decoder=0;	}	audio_decoder = FindAudioDecoder(header_list.avi_header, stream);	if (audio_decoder)	{		current_audio_stream = n;		audio_stream_num = i;		video_only=0;  // TODO! need to do more to get this to work if we are switching FROM video_only	}	else	{		video_only; // TODO! need to do more to get this to work here	}	SetEvent(audio_resume);	WaitForSingleObject(audio_break_done, INFINITE);	SetVideoInfoText();}void Streams::setVideoTrack(int n){	// TODO: need to VideoBreak, destroy decoder, create new one and update video_stream_num}bool SingleReaderLoop(nsavi::Demuxer &demuxer, nsavi::avi_reader *reader, nsavi::SeekTable *&audio_seek_table, nsavi::SeekTable *&video_seek_table){	const void *input_buffer = 0;	uint16_t type = 0;	uint32_t input_buffer_bytes = 0;	bool idx1_searched=false;	HANDLE events[] = { killswitch, seek_event, audio_break, audio_resume };	void *data;	uint32_t data_size;	uint32_t data_type;	int waitTime = 0;	for (;;)	{		int ret = WaitForMultipleObjects(4, events, FALSE, waitTime);		if (ret == WAIT_OBJECT_0)		{			break;		}		else if (ret == WAIT_OBJECT_0+1)		{			volatile LONG _this_seek_position;			do			{				InterlockedExchange(&_this_seek_position, seek_position);				if (_this_seek_position != -1)				{					int this_seek_position = _this_seek_position;					ResetEvent(seek_event); // reset this first so nothing aborts on it					if (!idx1_searched) 					{						nsavi::IDX1 *index;						ret = demuxer.GetSeekTable(&index);						if (ret == nsavi::READ_OK)						{							if (video_seek_table)								video_seek_table->AddIndex(index);							if (audio_seek_table)								audio_seek_table->AddIndex(index);						}						idx1_searched=true;												}					uint64_t index_position, start_time;					while (video_seek_table && video_seek_table->GetIndexLocation(this_seek_position, &index_position, &start_time))					{						nsavi::INDX *next_index=0;						if (demuxer.GetIndexChunk(&next_index, index_position) == 0)						{							video_seek_table->AddIndex(next_index, start_time); // seek table takes ownership							free(next_index);						}					}					while (audio_seek_table && audio_seek_table->GetIndexLocation(this_seek_position, &index_position, &start_time))					{						nsavi::INDX *next_index=0;						if (demuxer.GetIndexChunk(&next_index, index_position) == 0)						{							audio_seek_table->AddIndex(next_index, start_time); // seek table takes ownership							free(next_index);						}					}					if (video_seek_table)					{						int curr_time = GetOutputTime();						int direction = (curr_time < this_seek_position)?nsavi::SeekTable::SEEK_FORWARD:nsavi::SeekTable::SEEK_BACKWARD;						const nsavi::SeekEntry *video_seek_entry=video_seek_table->GetSeekPoint(this_seek_position, curr_time, direction);						if (video_seek_entry)						{							Video_Break();							if (video_only)							{								demuxer.Seek(video_seek_entry->file_position, video_seek_entry->absolute, reader);								video_clock.Seek(this_seek_position);							}							else if (audio_seek_table)							{								const nsavi::SeekEntry *audio_seek_entry=audio_seek_table->GetSeekPoint(this_seek_position);								if (audio_seek_entry)								{									if (audio_seek_entry->file_position < video_seek_entry->file_position)										demuxer.Seek(audio_seek_entry->file_position, audio_seek_entry->absolute, reader);									else										demuxer.Seek(video_seek_entry->file_position, video_seek_entry->absolute, reader);									audio_decoder->Flush();									out.Flush(this_seek_position);								}							}							video_total_time = video_seek_entry->stream_time;							Video_Flush();						}					}					else if (audio_seek_table)					{						int curr_time = GetOutputTime();						int direction = (curr_time < this_seek_position)?nsavi::SeekTable::SEEK_FORWARD:nsavi::SeekTable::SEEK_BACKWARD;						const nsavi::SeekEntry *audio_seek_entry=audio_seek_table->GetSeekPoint(this_seek_position, curr_time, direction);						if (audio_seek_entry)						{							demuxer.Seek(audio_seek_entry->file_position, audio_seek_entry->absolute, reader);							audio_decoder->Flush();							out.Flush(this_seek_position);						}					}				}			} while (InterlockedCompareExchange(&seek_position, -1, _this_seek_position) != _this_seek_position); // loop again if seek point changed		}		else if (ret == WAIT_OBJECT_0+2)		{ // audio break			ResetEvent(audio_break);			SetEvent(audio_break_done);			waitTime = INFINITE;			continue;		}		else if (ret == WAIT_OBJECT_0+3)		{ // audio resume			ResetEvent(audio_resume);			SetEvent(audio_break_done);			waitTime = 0;			continue;		}		else if (ret != WAIT_TIMEOUT)		{			break;		}		if (input_buffer_bytes) // TODO: read ahead in situation where there is one giant audio chunk for the entire movie		{			if (!OnAudio(type, &input_buffer, &input_buffer_bytes))			{				return false;			}			if (input_buffer_bytes == 0)			{				free(data);				data = NULL;			}		}		else		{			ret = demuxer.GetNextMovieChunk(reader, &data, &data_size, &data_type);			if (ret != nsavi::READ_OK)			{				break;			}			int stream_number = GetStreamNumber(data_type);			type = (data_type>>16);			if (stream_number == audio_stream_num)			{				input_buffer = (const void *)data;				input_buffer_bytes = data_size;				if (!OnAudio(type, &input_buffer, &input_buffer_bytes))				{					return false;				}				if (input_buffer_bytes == 0)				{					free(data);					data = NULL;				}			}			else if (stream_number == video_stream_num)			{				OnVideo(type, data, data_size);				data = NULL;			}			else			{				free(data);				data = NULL;			}		}	}	return true;}bool MultiReaderLoop(nsavi::Demuxer &demuxer, nsavi::avi_reader *reader, nsavi::avi_reader *video_reader, nsavi::SeekTable *&audio_seek_table, nsavi::SeekTable *&video_seek_table){	demuxer.SeekToMovieChunk(video_reader);	CreateVideoReaderThread(&demuxer, video_reader);	const void *input_buffer = 0;	uint16_t type = 0;	uint32_t input_buffer_bytes = 0;	bool idx1_searched=false;	HANDLE events[] = { killswitch, seek_event, audio_break, audio_resume};	void *data;	uint32_t data_size;	uint32_t data_type;	int waitTime=0;	for (;;)	{		int ret = WaitForMultipleObjects(4, events, FALSE, waitTime);		if (ret == WAIT_OBJECT_0)		{			break;		}		else if (ret == WAIT_OBJECT_0+1)		{			volatile LONG _this_seek_position;			do			{				InterlockedExchange(&_this_seek_position, seek_position);				if (_this_seek_position != -1)				{					int this_seek_position = _this_seek_position;					ResetEvent(seek_event); // reset this first so nothing aborts on it					if (!idx1_searched) 					{						nsavi::IDX1 *index;						ret = demuxer.GetSeekTable(&index);						if (ret == nsavi::READ_OK)						{							video_seek_table->AddIndex(index);							audio_seek_table->AddIndex(index);						}						idx1_searched=true;												}					uint64_t index_position, start_time;					while (video_seek_table->GetIndexLocation(this_seek_position, &index_position, &start_time))					{						nsavi::INDX *next_index=0;						if (demuxer.GetIndexChunk(&next_index, index_position) == 0)						{							video_seek_table->AddIndex(next_index, start_time); // seek table takes ownership							free(next_index);						}					}					while (audio_seek_table->GetIndexLocation(this_seek_position, &index_position, &start_time))					{						nsavi::INDX *next_index=0;						if (demuxer.GetIndexChunk(&next_index, index_position) == 0)						{							audio_seek_table->AddIndex(next_index, start_time); // seek table takes ownership							free(next_index);						}					}					int curr_time = GetOutputTime();					int direction = (curr_time < this_seek_position)?nsavi::SeekTable::SEEK_FORWARD:nsavi::SeekTable::SEEK_BACKWARD;					const nsavi::SeekEntry *video_seek_entry=video_seek_table->GetSeekPoint(this_seek_position, curr_time, direction);					if (video_seek_entry)					{						Video_Break();						demuxer.Seek(video_seek_entry->file_position, video_seek_entry->absolute, video_reader);						const nsavi::SeekEntry *audio_seek_entry=audio_seek_table->GetSeekPoint(this_seek_position);						if (audio_seek_entry)						{							demuxer.Seek(audio_seek_entry->file_position, audio_seek_entry->absolute, reader);							audio_decoder->Flush();							out.Flush(this_seek_position);						}						video_total_time = video_seek_entry->stream_time;						Video_Flush();					}				}			} while (InterlockedCompareExchange(&seek_position, -1, _this_seek_position) != _this_seek_position); // loop again if seek point changed		}		else if (ret == WAIT_OBJECT_0+2)		{ // audio break			ResetEvent(audio_break);			SetEvent(audio_break_done);			waitTime = INFINITE;			continue;		}		else if (ret == WAIT_OBJECT_0+3)		{ // audio resume			ResetEvent(audio_resume);			SetEvent(audio_break_done);			waitTime = 0;			continue;		}		else if (ret != WAIT_TIMEOUT)		{			break;		}		if (input_buffer_bytes) // TODO: read ahead in situation where there is one giant audio chunk for the entire movie		{			if (!OnAudio(type, &input_buffer, &input_buffer_bytes))			{				return false;			}			if (input_buffer_bytes == 0)			{				free(data);				data = NULL;			}		}		else		{			ret = demuxer.GetNextMovieChunk(reader, &data, &data_size, &data_type, audio_stream_num);			if (ret != nsavi::READ_OK)			{				break;			}			int stream_number = GetStreamNumber(data_type);			type = (data_type>>16);			if (stream_number == audio_stream_num && type != 0x7869) // ignore 'ix'			{				input_buffer = (const void *)data;				input_buffer_bytes = data_size;				if (!OnAudio(type, &input_buffer, &input_buffer_bytes))				{					return false;				}				if (input_buffer_bytes == 0)				{					free(data);					data = NULL;				}			}			else			{				free(data);				data = NULL;			}		}	}	return true;}void PlayLoop(nsavi::avi_reader *reader, bool multiple_readers){	AVIReaderWin32 video_reader;	uint32_t riff_type;	audio_decoder=0;	video_decoder=0;	nsavi::SeekTable *video_seek_table = 0, *audio_seek_table = 0;	nsavi::Demuxer demuxer(reader);	audio_opened=false;	int audio_bitrate=0;	streams.Reset();	out.Init(plugin.outMod);	if (!video_output)		video_output = (IVideoOutput *)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GET_IVIDEOOUTPUT);	audio_stream_num = 65536;	video_stream_num=65536; // purposefully too big value	Video_Init();	if (demuxer.GetRIFFType(&riff_type) == nsavi::READ_OK)	{		bool audio_no_decoder=false;		bool video_no_decoder=false;		if (demuxer.GetHeaderList(&header_list) == nsavi::READ_OK)		{			// find available codecs			for (uint32_t i=0;i!=header_list.stream_list_size;i++)			{				const nsavi::STRL &stream = header_list.stream_list[i];				if (stream.stream_header)				{					if (stream.stream_header->stream_type == nsavi::stream_type_audio)					{						nsavi::audio_format *f = (nsavi::audio_format *)stream.stream_format;						if (f)						{							stats.audio_types[f->format]++;							streams.AddAudioStream(i);							if (!audio_decoder)							{ // TODO: check priority								audio_decoder = FindAudioDecoder(header_list.avi_header, stream);								if (audio_decoder)								{									streams.SetAudioStream(i);									audio_stream_num = i;									video_only=0; 								}								else									audio_no_decoder = true;								if (stream.stream_header->length && !stream.stream_header->sample_size && stream.stream_header->rate)									g_duration = (uint64_t)stream.stream_header->length * (uint64_t)stream.stream_header->scale * 1000ULL / (uint64_t)stream.stream_header->rate;								audio_bitrate = MulDiv(f->average_bytes_per_second, 8, 1000);								plugin.SetInfo(audio_bitrate, -1, -1, -1);							}						}					}					else if (stream.stream_header->stream_type == nsavi::stream_type_video)					{						nsavi::video_format *f = (nsavi::video_format *)stream.stream_format;						if (f)						{							stats.video_fourccs[f->compression]++;							streams.AddVideoStream(i);							if (!video_decoder)							{ // TODO: check priority								video_decoder = FindVideoDecoder(header_list.avi_header, stream);								if (video_decoder)								{									video_stream_num = i;									streams.SetVideoStream(i);								}								else									video_no_decoder = true;								if (g_duration == -1 && stream.stream_header->rate)									g_duration = (uint64_t)stream.stream_header->length * (uint64_t)stream.stream_header->scale * 1000ULL / (uint64_t)stream.stream_header->rate;							}						}					}				}			}		}		if (AGAVE_API_STATS)		{			uint32_t audio_format = stats.GetAudioStat();			uint32_t video_format = stats.GetVideoStat();			AGAVE_API_STATS->SetStat(api_stats::AVI_AUDIO_FORMAT, audio_format);			AGAVE_API_STATS->SetStat(api_stats::AVI_VIDEO_FOURCC, video_format);		}		if ((audio_no_decoder || video_no_decoder) && CheckDSHOW())		{			// use in_dshow to play this one			HANDLE mainThread = WASABI_API_APP->main_getMainThreadHandle();			if (mainThread)			{				Video_Stop();				if (audio_decoder)				{					audio_decoder->Close();					audio_decoder=0;				}				Video_Close();				delete video_seek_table;				delete audio_seek_table;				wchar_t *fn = (wchar_t *)calloc(1024, sizeof(wchar_t *));				reader->GetFilename(fn, 1024);				QueueUserAPC(DSHOWAPC, mainThread, (ULONG_PTR)fn);				CloseHandle(mainThread);				return ;			}		}		if (!audio_decoder && !video_decoder)		{			goto btfo;		}		if (!audio_decoder)		{			video_only=1;			video_clock.Start();		}	}	else	{		goto btfo;	}	SetVideoInfoText();	video_output->extended(VIDUSER_SET_TRACKSELINTERFACE, (INT_PTR)&streams, 0);	if (video_stream_num != 65536)		video_seek_table = new nsavi::SeekTable(video_stream_num, !!video_decoder, &header_list);	if (audio_stream_num != 65536)		audio_seek_table = new nsavi::SeekTable(audio_stream_num, false, &header_list);	uint64_t content_length = reader->GetContentLength();	if (content_length && g_duration)	{		int total_bitrate = (int)(8ULL * content_length / (uint64_t)g_duration);		plugin.SetInfo(total_bitrate, -1, -1, -1);	}	else if (header_list.avi_header->max_bytes_per_second)	{		int total_bitrate = MulDiv(header_list.avi_header->max_bytes_per_second, 8, 1000);		plugin.SetInfo(total_bitrate, -1, -1, -1);	}	else	{		// use seek table for bitrate?	}	if (demuxer.FindMovieChunk() != nsavi::READ_OK)	{		goto btfo;	}	if (multiple_readers && video_decoder && !video_only)	{		wchar_t fn[MAX_PATH] = {0};		reader->GetFilename(fn, MAX_PATH);		if (video_reader.Open(fn) == nsavi::READ_OK)		{			MultiReaderLoop(demuxer, reader, &video_reader, audio_seek_table, video_seek_table);		}		else			SingleReaderLoop(demuxer, reader, audio_seek_table, video_seek_table);			}	else		SingleReaderLoop(demuxer, reader, audio_seek_table, video_seek_table);	if (audio_opened && WaitForSingleObject(killswitch, 0) == WAIT_TIMEOUT)	{		out.Write(0, 0);		out.WaitWhilePlaying();	}btfo:	if (WaitForSingleObject(killswitch, 0) == WAIT_TIMEOUT)		PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);	Video_Stop();	if (audio_decoder)	{		audio_decoder->Close();		audio_decoder=0;		if (audio_opened)			out.Close();	}	Video_Close();	video_reader.Close();	delete video_seek_table;	delete audio_seek_table;}DWORD CALLBACK AVIPlayThread(LPVOID param){	if (!audio_break)		audio_break = CreateEvent(0, TRUE, FALSE, 0);	if (!audio_resume)		audio_resume = CreateEvent(0, TRUE, FALSE, 0);	if (!audio_break_done)		audio_break_done = CreateEvent(0, FALSE, FALSE, 0);	wchar_t *filename = (wchar_t *)param;	if (PathIsURLW(filename))	{		AVIReaderHTTP reader(killswitch, seek_event);		if (reader.Open(filename) != nsavi::READ_OK || reader.Connect() != nsavi::READ_OK)		{			if (WaitForSingleObject(killswitch, 200) == WAIT_TIMEOUT)				PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);		}		else		{			PlayLoop(&reader, false);			reader.Close();		}	}	else	{		AVIReaderWin32 reader;		if (reader.Open(filename) != nsavi::READ_OK)		{			if (WaitForSingleObject(killswitch, 200) == WAIT_TIMEOUT)				PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);		}		else		{			wchar_t root[4] = {0};			StringCchCopy(root, 4, filename);			UINT drive_type = GetDriveType(root);			if (drive_type == DRIVE_CDROM)				PlayLoop(&reader, false);			else				PlayLoop(&reader, true);			reader.Close();		}	}	free(filename);	return 0;}
 |