winamp/Src/Plugins/Input/in_avi/PlayThread.cpp
2024-09-24 14:54:57 +02:00

964 lines
25 KiB
C++

#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;
}