aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_avi/PlayThread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Input/in_avi/PlayThread.cpp')
-rw-r--r--Src/Plugins/Input/in_avi/PlayThread.cpp964
1 files changed, 964 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_avi/PlayThread.cpp b/Src/Plugins/Input/in_avi/PlayThread.cpp
new file mode 100644
index 00000000..8ed3b658
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/PlayThread.cpp
@@ -0,0 +1,964 @@
+#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;
+}