aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_avi
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Input/in_avi')
-rw-r--r--Src/Plugins/Input/in_avi/ExtendedFileInfo.cpp138
-rw-r--r--Src/Plugins/Input/in_avi/InfoDialog.cpp390
-rw-r--r--Src/Plugins/Input/in_avi/PlayThread.cpp964
-rw-r--r--Src/Plugins/Input/in_avi/StreamSelector.h32
-rw-r--r--Src/Plugins/Input/in_avi/VideoThread.cpp428
-rw-r--r--Src/Plugins/Input/in_avi/VideoThread.h12
-rw-r--r--Src/Plugins/Input/in_avi/api.cpp50
-rw-r--r--Src/Plugins/Input/in_avi/api__in_avi.h15
-rw-r--r--Src/Plugins/Input/in_avi/http_avi_reader.cpp255
-rw-r--r--Src/Plugins/Input/in_avi/http_avi_reader.h31
-rw-r--r--Src/Plugins/Input/in_avi/ifc_aviaudiodecoder.h67
-rw-r--r--Src/Plugins/Input/in_avi/ifc_avivideodecoder.h77
-rw-r--r--Src/Plugins/Input/in_avi/in_avi.rc159
-rw-r--r--Src/Plugins/Input/in_avi/in_avi.sln31
-rw-r--r--Src/Plugins/Input/in_avi/in_avi.vcxproj284
-rw-r--r--Src/Plugins/Input/in_avi/in_avi.vcxproj.filters158
-rw-r--r--Src/Plugins/Input/in_avi/interfaces.h4
-rw-r--r--Src/Plugins/Input/in_avi/main.cpp330
-rw-r--r--Src/Plugins/Input/in_avi/main.h19
-rw-r--r--Src/Plugins/Input/in_avi/player.h3
-rw-r--r--Src/Plugins/Input/in_avi/resource.h50
-rw-r--r--Src/Plugins/Input/in_avi/svc_avidecoder.h74
-rw-r--r--Src/Plugins/Input/in_avi/version.rc239
-rw-r--r--Src/Plugins/Input/in_avi/win32_avi_reader.cpp174
-rw-r--r--Src/Plugins/Input/in_avi/win32_avi_reader.h36
25 files changed, 3820 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_avi/ExtendedFileInfo.cpp b/Src/Plugins/Input/in_avi/ExtendedFileInfo.cpp
new file mode 100644
index 00000000..23262e66
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/ExtendedFileInfo.cpp
@@ -0,0 +1,138 @@
+#include <bfc/platform/types.h>
+#include <windows.h>
+#include "api__in_avi.h"
+#include "win32_avi_reader.h"
+#include "../nsavi/metadata.h"
+#include "../nu/ns_wc.h"
+#include <strsafe.h>
+#include "resource.h"
+
+static void ReadMetadata(nsavi::Metadata &metadata, uint32_t id, wchar_t *dest, size_t destlen)
+{
+ nsavi::Info *info=0;
+ const char *str = 0;
+ if (metadata.GetInfo(&info) == nsavi::READ_OK && (str = info->GetMetadata(id)))
+ {
+ MultiByteToWideCharSZ(CP_ACP/*UTF8*/, 0, str, -1, dest, (int)destlen);
+ }
+ else
+ dest[0]=0;
+}
+
+extern "C" __declspec(dllexport)
+int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, size_t destlen)
+{
+ if (!_stricmp(data, "type"))
+ {
+ dest[0]='1';
+ dest[1]=0;
+ return 1;
+ }
+ else if (!_stricmp(data, "family"))
+ {
+ int len;
+ const wchar_t *p;
+ if (!fn || !fn[0]) return 0;
+ len = lstrlenW(fn);
+ if (len < 4 || L'.' != fn[len - 4]) return 0;
+ p = &fn[len - 3];
+ if (!_wcsicmp(p, L"AVI") && S_OK == StringCchCopyW(dest, destlen, WASABI_API_LNGSTRINGW(IDS_FAMILY_STRING))) return 1;
+ return 0;
+ }
+ else
+ {
+ AVIReaderWin32 reader;
+ if (reader.Open(fn) == nsavi::READ_OK)
+ {
+ nsavi::Metadata metadata(&reader);
+ uint32_t riff_type;
+ metadata.GetRIFFType(&riff_type); // need to call this to get the party started
+ // TODO: cache metadata object
+
+ if (!_stricmp(data, "length"))
+ {
+ int time_ms;
+ if (metadata.GetDuration(&time_ms) == nsavi::READ_OK)
+ StringCchPrintf(dest, destlen, L"%d", time_ms);
+ else
+ dest[0]=0;
+ }
+ else if (!_stricmp(data, "height"))
+ {
+ nsavi::HeaderList header_list;
+ if (metadata.GetHeaderList(&header_list) == nsavi::READ_OK && header_list.avi_header && header_list.avi_header->height)
+ StringCchPrintf(dest, destlen, L"%d", header_list.avi_header->height);
+ else
+ dest[0]=0;
+ }
+ else if (!_stricmp(data, "width"))
+ {
+ nsavi::HeaderList header_list;
+ if (metadata.GetHeaderList(&header_list) == nsavi::READ_OK && header_list.avi_header && header_list.avi_header->width)
+ StringCchPrintf(dest, destlen, L"%d", header_list.avi_header->width);
+ else
+ dest[0]=0;
+ }
+ else if (!_stricmp(data, "bitrate"))
+ {
+ int time_ms = 0;
+ uint64_t file_length = 0;
+ if (metadata.GetDuration(&time_ms) == nsavi::READ_OK
+ && (file_length = reader.GetContentLength())
+ && time_ms > 0 && file_length > 0)
+ {
+ uint64_t bitrate = 8ULL * file_length / (uint64_t)time_ms;
+ StringCchPrintf(dest, destlen, L"%I64u", bitrate);
+ }
+ else
+ dest[0]=0;
+ }
+ else if (!_stricmp(data, "artist"))
+ {
+ ReadMetadata(metadata, nsaviFOURCC('I','A','R','T'), dest, destlen);
+ }
+ else if (!_stricmp(data, "publisher"))
+ {
+ ReadMetadata(metadata, nsaviFOURCC('I','P','U','B'), dest, destlen);
+ }
+ else if (!_stricmp(data, "album"))
+ {
+ ReadMetadata(metadata, nsaviFOURCC('I','A','L','B'), dest, destlen);
+ }
+ else if (!_stricmp(data, "composer"))
+ {
+ ReadMetadata(metadata, nsaviFOURCC('I','C','O','M'), dest, destlen);
+ }
+ else if (!_stricmp(data, "genre"))
+ {
+ ReadMetadata(metadata, nsaviFOURCC('I','G','N','R'), dest, destlen);
+ }
+ else if (!_stricmp(data, "comment"))
+ {
+ ReadMetadata(metadata, nsaviFOURCC('I','C','M','T'), dest, destlen);
+ }
+ else if (!_stricmp(data, "title"))
+ {
+ ReadMetadata(metadata, nsaviFOURCC('I','N','A','M'), dest, destlen);
+ }
+ else if (!_stricmp(data, "tool"))
+ {
+ ReadMetadata(metadata, nsaviFOURCC('I','S','F','T'), dest, destlen);
+ }
+ else if (!_stricmp(data, "copyright"))
+ {
+ ReadMetadata(metadata, nsaviFOURCC('I','C','O','P'), dest, destlen);
+ }
+ else
+ {
+ reader.Close();
+ return 0;
+ }
+
+ reader.Close();
+ return 1;
+ }
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_avi/InfoDialog.cpp b/Src/Plugins/Input/in_avi/InfoDialog.cpp
new file mode 100644
index 00000000..ea31e660
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/InfoDialog.cpp
@@ -0,0 +1,390 @@
+#include "../nsavi/nsavi.h"
+#include "api__in_avi.h"
+#include "../nu/ListView.h"
+#include "../nu/AutoWide.h"
+#include "resource.h"
+#include "main.h"
+#include <strsafe.h>
+
+struct KnownField
+{
+ uint32_t field;
+ wchar_t name[256]; // TODO: change to resource ID
+};
+
+static KnownField known_fields[] =
+{
+ {nsaviFOURCC('I','S','F','T'), L"Tool"}, // IDS_FIELD_TOOL
+ {nsaviFOURCC('I','A','R','T'), L"Artist"}, // IDS_FIELD_ARTIST
+ {nsaviFOURCC('I','P','U','B'), L"Publisher"}, // IDS_FIELD_PUBLISHER
+ {nsaviFOURCC('I','A','L','B'), L"Album"}, // IDS_FIELD_ALBUM
+ {nsaviFOURCC('I','C','O','M'), L"Composer"}, // IDS_FIELD_COMPOSER
+ {nsaviFOURCC('I','G','N','R'), L"Genre"}, // IDS_FIELD_GENRE
+ {nsaviFOURCC('I','C','M','T'), L"Comment"}, // IDS_FIELD_COMMENT
+ {nsaviFOURCC('I','N','A','M'), L"Title"}, // IDS_FIELD_TITLE
+ {nsaviFOURCC('I','C','O','P'), L"Copyright"}, // IDS_FIELD_COPYRIGHT
+};
+
+static KnownField known_video_codecs[] =
+{
+ {nsaviFOURCC('V','P','6','0'), L"On2 VP6"},
+ {nsaviFOURCC('V','P','6','1'), L"On2 VP6"},
+ {nsaviFOURCC('V','P','6','2'), L"On2 VP6"},
+
+ {nsaviFOURCC('X','V','I','D'), L"MPEG-4 Part 2"},
+ {nsaviFOURCC('x','v','i','d'), L"MPEG-4 Part 2"},
+
+ {nsaviFOURCC('d','i','v','x'), L"MPEG-4 Part 2"},
+ {nsaviFOURCC('D','I','V','X'), L"MPEG-4 Part 2"},
+ {nsaviFOURCC('D','X','5','0'), L"MPEG-4 Part 2"},
+
+ {nsaviFOURCC('m','p','4','v'), L"MPEG-4 Part 2"},
+
+ {nsaviFOURCC('S','E','D','G'), L"MPEG-4 Part 2"},
+
+ {nsaviFOURCC('H','2','6','4'), L"H.264"},
+
+ {nsaviFOURCC('M','J','P','G'), L"Motion JPEG"},
+
+ {nsaviFOURCC('t','s','c','c'), L"TechSmith"},
+
+ {nsaviFOURCC('c','v','i','d'), L"Cinepack"},
+
+ {nsaviFOURCC('M','P','G','4'), L"MS-MPEG-4 v1"},
+ {nsaviFOURCC('M','P','4','1'), L"MS-MPEG-4 v1"},
+ {nsaviFOURCC('M','P','4','2'), L"MS-MPEG-4 v2"},
+ {nsaviFOURCC('M','P','4','3'), L"MS-MPEG-4 v3"},
+
+ {nsavi::video_format_rgb, L"RGB"},
+ {nsavi::video_format_rle8, L"8bpp RLE"},
+ {nsavi::video_format_rle4, L"4bpp RLE"},
+};
+
+static KnownField known_audio_codecs[] =
+{
+ {nsavi::audio_format_pcm, L"Wave"},
+ {nsavi::audio_format_ms_adpcm, L"Microsoft ADPCM"},
+ {nsavi::audio_format_alaw, L"A-law"},
+ {nsavi::audio_format_ulaw, L"μ-law"},
+ {nsavi::audio_format_ima_adpcm, L"IMA ADPCM"},
+ {nsavi::audio_format_truespeech, L"DSP Truespeech"},
+ {nsavi::audio_format_mp2, L"MPEG Layer 2"},
+ {nsavi::audio_format_mp3, L"MPEG Layer 3"},
+ {nsavi::audio_format_a52, L"ATSC A/52 (AC3)"},
+ {nsavi::audio_format_aac, L"AAC"},
+ {nsavi::audio_format_vorbis, L"Vorbis"},
+ {nsavi::audio_format_speex, L"Speex"},
+ {nsavi::audio_format_extensible, L"Extensible Wave"},
+ {nsavi::audio_format_dts, L"DTS"},
+
+};
+
+enum
+{
+ COLUMN_TRACK_TYPE = 0,
+ COLUMN_CODEC_NAME = 1,
+ COLUMN_CODEC_ID = 2,
+ COLUMN_DESCRIPTION = 3,
+ COLUMN_STREAM_NAME = 4,
+};
+
+static const wchar_t *FindKnownName(const KnownField *fields, size_t num_fields, uint32_t value)
+{
+ for (size_t i=0;i!=num_fields;i++)
+ {
+ if (fields[i].field == value)
+ {
+ return fields[i].name;
+ }
+ }
+ return 0;
+}
+
+static void MakeStringFromFOURCC(wchar_t *str, uint32_t fourcc)
+{
+ const uint8_t *characters = (const uint8_t *)&(fourcc);
+ if (fourcc < 65536)
+ {
+ StringCchPrintfW(str, 5, L"%X", fourcc);
+ }
+
+ else
+ {
+ str[0] = characters[0];
+ str[1] = characters[1];
+ str[2] = characters[2];
+ str[3] = characters[3];
+ str[4] = 0;
+ }
+}
+
+
+static INT_PTR CALLBACK InfoDialog_Metadata(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ nsavi::Metadata *metadata = (nsavi::Metadata *)lParam;
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam);
+
+ W_ListView list_view(hwndDlg, IDC_TRACKLIST);
+
+ list_view.AddCol(WASABI_API_LNGSTRINGW(IDS_COLUMN_FIELD), 100);
+ list_view.AddCol(WASABI_API_LNGSTRINGW(IDS_COLUMN_FOURCC), 75);
+ list_view.AddCol(WASABI_API_LNGSTRINGW(IDS_COLUMN_VALUE), 250);
+
+ nsavi::Info *info;
+ if (metadata->GetInfo(&info) == nsavi::READ_OK)
+ {
+ int n=0;
+ for (nsavi::Info::const_iterator itr = info->begin();itr!=info->end();itr++)
+ {
+ const wchar_t *field_name = FindKnownName(known_fields, sizeof(known_fields)/sizeof(known_fields[0]), itr->first);
+
+ wchar_t fourcc[5] = {0};
+ MakeStringFromFOURCC(fourcc, itr->first);
+
+ if (field_name)
+ n= list_view.AppendItem(field_name, 0);
+ else
+ n= list_view.AppendItem(fourcc, 0);
+
+ list_view.SetItemText(n, 1, fourcc);
+ list_view.SetItemText(n, 2, AutoWide(itr->second, CP_ACP/*UTF8*/));
+ }
+ }
+ }
+ return 1;
+ case WM_SIZE:
+ {
+ RECT r;
+ GetClientRect(hwndDlg, &r);
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_TRACKLIST), HWND_TOP, r.left, r.top, r.right, r.bottom, SWP_NOACTIVATE);
+ }
+ break;
+ }
+ return 0;
+}
+
+void GetVideoCodecName(wchar_t *str, size_t str_cch, nsavi::STRF *stream_format)
+{
+ nsavi::video_format *format = (nsavi::video_format *)stream_format;
+ const wchar_t *codec_name = FindKnownName(known_video_codecs, sizeof(known_video_codecs)/sizeof(known_video_codecs[0]), format->compression);
+ if (codec_name)
+ StringCchCopy(str, str_cch, codec_name);
+ else
+ MakeStringFromFOURCC(str, format->compression);
+}
+
+void GetVideoCodecDescription(wchar_t *str, size_t str_cch, nsavi::STRF *stream_format)
+{
+ nsavi::video_format *format = (nsavi::video_format *)stream_format;
+ StringCchPrintf(str, str_cch, L"%ux%u", format->width, format->height);
+}
+
+void GetAudioCodecName(wchar_t *str, size_t str_cch, nsavi::STRF *stream_format)
+{
+ nsavi::audio_format *format = (nsavi::audio_format *)stream_format;
+ const wchar_t *codec_name = FindKnownName(known_audio_codecs, sizeof(known_audio_codecs)/sizeof(known_audio_codecs[0]), format->format);
+ if (codec_name)
+ StringCchCopy(str, str_cch, codec_name);
+ else
+ MakeStringFromFOURCC(str, format->format);
+}
+
+void GetAudioCodecDescription(wchar_t *str, size_t str_cch, nsavi::STRF *stream_format)
+{
+ nsavi::audio_format *format = (nsavi::audio_format *)stream_format;
+ if (format->average_bytes_per_second)
+ {
+ StringCchPrintf(str, str_cch, L"%u %s", format->average_bytes_per_second / 125UL, WASABI_API_LNGSTRINGW(IDS_KBPS));
+ }
+ else
+ str[0]=0;
+}
+
+static INT_PTR CALLBACK InfoDialog_Tracks(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ nsavi::Metadata *metadata = (nsavi::Metadata *)lParam;
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam);
+
+ W_ListView list_view(hwndDlg, IDC_TRACKLIST);
+
+ list_view.AddCol(WASABI_API_LNGSTRINGW(IDS_COLUMN_TRACK_TYPE), 100);
+ list_view.AddCol(WASABI_API_LNGSTRINGW(IDS_COLUMN_CODEC_NAME), 100);
+ list_view.AddCol(WASABI_API_LNGSTRINGW(IDS_COLUMN_CODEC_ID), 75);
+ list_view.AddCol(WASABI_API_LNGSTRINGW(IDS_COLUMN_DESCRIPTION), 100);
+ list_view.AddCol(WASABI_API_LNGSTRINGW(IDS_COLUMN_STREAM_NAME), 100);
+
+ nsavi::HeaderList header_list;
+ if (metadata->GetHeaderList(&header_list) == nsavi::READ_OK)
+ {
+ for (size_t i=0;i!=header_list.stream_list_size;i++)
+ {
+ int n;
+ const nsavi::STRL &stream = header_list.stream_list[i];
+ switch(stream.stream_header->stream_type)
+ {
+ case nsavi::stream_type_audio:
+ {
+ n = list_view.AppendItem(WASABI_API_LNGSTRINGW(IDS_TYPE_AUDIO), 0);
+ nsavi::audio_format *format = (nsavi::audio_format *)stream.stream_format;
+ wchar_t codec_id[5] = {0};
+ MakeStringFromFOURCC(codec_id, format->format);
+ list_view.SetItemText(n, COLUMN_CODEC_ID, codec_id);
+ const wchar_t *codec_name = FindKnownName(known_audio_codecs, sizeof(known_audio_codecs)/sizeof(known_audio_codecs[0]), format->format);
+ if (codec_name)
+ list_view.SetItemText(n, COLUMN_CODEC_NAME, codec_name);
+ else
+ list_view.SetItemText(n, COLUMN_CODEC_NAME, codec_id);
+
+ wchar_t description[256] = {0};
+ GetAudioCodecDescription(description, 256, stream.stream_format);
+ list_view.SetItemText(n, COLUMN_DESCRIPTION, description);
+ }
+ break;
+ case nsavi::stream_type_video:
+ {
+ n = list_view.AppendItem(WASABI_API_LNGSTRINGW(IDS_TYPE_VIDEO), 0);
+ nsavi::video_format *format = (nsavi::video_format *)stream.stream_format;
+ wchar_t fourcc[5] = {0};
+ MakeStringFromFOURCC(fourcc, format->compression);
+ list_view.SetItemText(n, COLUMN_CODEC_ID, fourcc);
+ const wchar_t *codec_name = FindKnownName(known_video_codecs, sizeof(known_video_codecs)/sizeof(known_video_codecs[0]), format->compression);
+ if (codec_name)
+ list_view.SetItemText(n, COLUMN_CODEC_NAME, codec_name);
+ else
+ list_view.SetItemText(n, COLUMN_CODEC_NAME, fourcc);
+ wchar_t description[256] = {0};
+ GetVideoCodecDescription(description, 256, stream.stream_format);
+ list_view.SetItemText(n, COLUMN_DESCRIPTION, description);
+ }
+ break;
+ default:
+ {
+ wchar_t fourcc[5] = {0};
+ MakeStringFromFOURCC(fourcc, stream.stream_header->stream_type);
+ n = list_view.AppendItem(fourcc, 0);
+ }
+ break;
+ }
+ if (stream.stream_name)
+ {
+ //const char *name = (const char *) (((const uint8_t *)stream.stream_name) + 4);
+ // TODO: need AutoWideN before this is safe
+ // list_view.SetItemText(n, COLUMN_STREAM_NAME, AutoWide(name, CP_UTF8));
+ }
+ }
+ }
+ }
+ return 1;
+ case WM_SIZE:
+ {
+ RECT r;
+ GetClientRect(hwndDlg, &r);
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_TRACKLIST), HWND_TOP, r.left, r.top, r.right, r.bottom, SWP_NOACTIVATE);
+ }
+ break;
+ }
+ return 0;
+}
+
+
+struct InfoDialogContext
+{
+ nsavi::Metadata *metadata;
+ HWND active_tab;
+};
+
+static VOID WINAPI OnSelChanged(HWND hwndDlg, HWND hwndTab, InfoDialogContext *context)
+{
+ if (context->active_tab)
+ {
+ DestroyWindow(context->active_tab);
+ }
+ int selection = TabCtrl_GetCurSel(hwndTab);
+ switch(selection)
+ {
+ case 0:
+ context->active_tab = WASABI_API_CREATEDIALOGPARAMW(IDD_TRACKS, hwndDlg, InfoDialog_Metadata, (LPARAM)context->metadata);
+ break;
+ case 1:
+ context->active_tab = WASABI_API_CREATEDIALOGPARAMW(IDD_TRACKS, hwndDlg, InfoDialog_Tracks, (LPARAM)context->metadata);
+ break;
+ }
+
+ RECT r;
+ GetWindowRect(hwndTab,&r);
+ TabCtrl_AdjustRect(hwndTab,FALSE,&r);
+ MapWindowPoints(NULL,hwndDlg,(LPPOINT)&r,2);
+
+ SetWindowPos(context->active_tab,HWND_TOP,r.left,r.top,r.right-r.left,r.bottom-r.top,SWP_NOACTIVATE);
+ ShowWindow(context->active_tab, SW_SHOWNA);
+
+ if (GetFocus() != hwndTab)
+ {
+ SetFocus(context->active_tab);
+ }
+}
+
+INT_PTR CALLBACK InfoDialog(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ HWND hwndTab = GetDlgItem(hwndDlg,IDC_TAB1);
+ InfoDialogContext *context = (InfoDialogContext *)calloc(1, sizeof(InfoDialogContext));
+ context->metadata = (nsavi::Metadata *)lParam;
+ context->active_tab = 0;
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA, (LPARAM)context);
+ TCITEMW tie = {0};
+ tie.mask = TCIF_TEXT;
+ tie.pszText = WASABI_API_LNGSTRINGW(IDS_TAB_METADATA);
+ SendMessageW(hwndTab, TCM_INSERTITEMW, 0, (LPARAM)&tie);
+ tie.pszText = WASABI_API_LNGSTRINGW(IDS_TAB_TRACKS);
+ SendMessageW(hwndTab, TCM_INSERTITEMW, 1, (LPARAM)&tie);
+ OnSelChanged(hwndDlg, hwndTab, context);
+ }
+ return 1;
+
+ case WM_DESTROY:
+ {
+ InfoDialogContext *context = (InfoDialogContext *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ free(context);
+ }
+ break;
+ case WM_NOTIFY:
+ {
+ LPNMHDR lpn = (LPNMHDR) lParam;
+ if (lpn && lpn->code==TCN_SELCHANGE)
+ {
+ InfoDialogContext *context = (InfoDialogContext *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ OnSelChanged(hwndDlg,GetDlgItem(hwndDlg,IDC_TAB1),context);
+ }
+ }
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ EndDialog(hwndDlg,0);
+ }
+ break;
+ case IDCANCEL:
+ {
+ EndDialog(hwndDlg,1);
+ }
+ break;
+ }
+ break;
+ }
+
+ return 0;
+} \ No newline at end of file
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;
+}
diff --git a/Src/Plugins/Input/in_avi/StreamSelector.h b/Src/Plugins/Input/in_avi/StreamSelector.h
new file mode 100644
index 00000000..a81743d2
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/StreamSelector.h
@@ -0,0 +1,32 @@
+#pragma once
+#include "../Winamp/wa_ipc.h"
+#include <bfc/platform/types.h>
+class Streams : public ITrackSelector
+{
+public:
+ void Reset();
+ void AddAudioStream(int stream_num);
+ void AddVideoStream(int stream_num);
+
+ void SetAudioStream(int stream_num);
+ void SetVideoStream(int stream_num);
+
+ uint16_t audio_streams[256];
+ int num_audio_streams;
+ int current_audio_stream;
+ uint16_t video_streams[256];
+ int num_video_streams;
+ int current_video_stream;
+
+ /* ITrackSelector interface */
+ int getNumAudioTracks();
+ void enumAudioTrackName(int n, char *buf, int size);
+ int getCurAudioTrack();
+
+ int getNumVideoTracks();
+ void enumVideoTrackName(int n, char *buf, int size);
+ int getCurVideoTrack();
+
+ void setAudioTrack(int n);
+ void setVideoTrack(int n);
+};
diff --git a/Src/Plugins/Input/in_avi/VideoThread.cpp b/Src/Plugins/Input/in_avi/VideoThread.cpp
new file mode 100644
index 00000000..4dcdea3c
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/VideoThread.cpp
@@ -0,0 +1,428 @@
+#include "main.h"
+#include "api__in_avi.h"
+#include "../nu/AutoLock.h"
+#include "../nu/SampleQueue.h"
+#include "../Winamp/wa_ipc.h"
+#include "interfaces.h"
+#include "../nsavi/nsavi.h"
+#include "../nu/ThreadName.h"
+#include "player.h"
+
+// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
+static const GUID playbackConfigGroupGUID =
+{ 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf } };
+
+extern nsavi::HeaderList header_list;
+extern int video_stream_num;
+
+int width, height;
+extern IVideoOutput *video_output;
+static HANDLE video_thread=0;
+extern ifc_avivideodecoder *video_decoder;
+bool video_opened=false;
+static HANDLE coded_frames_event=0;
+static Nullsoft::Utility::LockGuard coded_frames_guard;
+uint64_t video_total_time=0;
+HANDLE video_break=0, video_flush=0, video_flush_done=0, video_resume=0, video_ready=0;
+
+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;
+}
+
+void Video_Init()
+{
+ video_opened=false;
+ video_decoder=0;
+ video_thread=0;
+ width=0;
+ height=0;
+ video_total_time=0;
+
+ if (coded_frames_event == 0)
+ coded_frames_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ /* video events */
+ if (!video_break)
+ video_break = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (!video_flush_done)
+ video_flush_done = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ if (!video_flush)
+ video_flush = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (!video_resume)
+ video_resume = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (!video_ready)
+ video_ready = CreateEvent(NULL, TRUE, FALSE, NULL);
+}
+
+struct FRAMEDATA
+{
+ FRAMEDATA()
+ {
+ type = 0;
+ data=0;
+ length=0;
+ }
+
+ ~FRAMEDATA()
+ {
+ free(data);
+ }
+ void Reset()
+ {
+ free(data);
+ data=0;
+ length=0;
+ type = 0;
+ }
+ void Set(uint16_t _type, void *_data, size_t _length)
+ {
+ type = _type;
+ data = _data;
+ length = _length;
+ }
+ void *data;
+ size_t length;
+ uint16_t type;
+};
+
+static SampleQueue<FRAMEDATA> coded_frames;
+
+extern int GetOutputTime();
+struct VideoContext
+{
+ nsavi::avi_reader *reader;
+ nsavi::Demuxer *demuxer;
+ int video_stream_num;
+};
+
+static void DecodeVideo(FRAMEDATA *frame_data)
+{
+ HANDLE handles[] = {killswitch, video_break};
+ if (WaitForMultipleObjects(2, handles, FALSE, 0) == WAIT_TIMEOUT)
+ {
+ int decodeResult = video_decoder->DecodeChunk(frame_data->type, frame_data->data, frame_data->length);
+
+ if (decodeResult == ifc_avivideodecoder::AVI_SUCCESS)
+ {
+ void *data, *decoder_data;
+ while (video_decoder->GetPicture(&data, &decoder_data) == ifc_avivideodecoder::AVI_SUCCESS)
+ {
+ if (!video_opened)
+ {
+ int color_format;
+ double aspect_ratio=1.0;
+ int flip=0;
+ if (video_decoder->GetOutputProperties(&width, &height, &color_format, &aspect_ratio, &flip) == ifc_avivideodecoder::AVI_SUCCESS)
+ {
+ nsavi::VPRP *vprp = header_list.stream_list[video_stream_num].video_properties;
+ if (vprp)
+ {
+ uint32_t asp = vprp->aspect_ratio;
+ uint32_t aspect_x = HIWORD(asp);
+ uint32_t aspect_y = LOWORD(asp);
+
+ aspect_ratio = (double)vprp->frame_width / (double)vprp->frame_height / ((double)aspect_x / (double)aspect_y);
+ }
+ else
+ aspect_ratio = 1.0/aspect_ratio;
+
+ video_output->extended(VIDUSER_SET_THREAD_SAFE, 1, 0);
+ video_output->open(width, height, flip, aspect_ratio, color_format);
+ video_opened=true;
+ }
+ }
+ if (video_opened)
+ {
+ int timestamp=(int)(video_total_time*1000ULL/(uint64_t) header_list.stream_list[video_stream_num].stream_header->rate);
+again:
+ int real_time =(int)GetOutputTime();
+ if (timestamp > (real_time+5))
+ {
+ HANDLE handles[] = {killswitch, video_break};
+ int ret=WaitForMultipleObjects(2, handles, FALSE, timestamp-real_time);
+ if (ret != WAIT_TIMEOUT)
+ {
+ video_decoder->FreePicture(data, decoder_data);
+ frame_data->Reset();
+ return ;
+ }
+ goto again; // TODO: handle paused state a little better than this
+ }
+ RGB32 *palette=0;
+ if (video_decoder->GetPalette(&palette) == ifc_avivideodecoder::AVI_SUCCESS)
+ {
+ video_output->extended(VIDUSER_SET_PALETTE, (INT_PTR)palette, 0);
+ }
+ video_output->draw(data);
+ }
+ video_total_time += header_list.stream_list[video_stream_num].stream_header->scale;
+ video_decoder->FreePicture(data, decoder_data);
+ }
+ }
+
+ }
+
+ // frame_data->Reset();
+}
+
+static DWORD CALLBACK VideoProcedure(LPVOID param)
+{
+ SetThreadName(-1,"AVI_VideoProcedure");
+ HANDLE wait_handles[] = { killswitch, video_break, video_flush, video_resume, coded_frames_event };
+ while (1)
+ {
+ int ret = WaitForMultipleObjects(5, wait_handles, FALSE, INFINITE);
+
+ if (ret == WAIT_OBJECT_0)
+ {
+ break;
+ }
+ if (ret == WAIT_OBJECT_0 + 1) // video break
+ {
+ ResetEvent(coded_frames_event);
+ ResetEvent(video_break);
+ SetEvent(video_flush_done);
+ }
+ else if (ret == WAIT_OBJECT_0 + 2) // video flush
+ {
+ if (video_decoder)
+ video_decoder->Flush();
+ ResetEvent(video_flush);
+ coded_frames.Trim();
+ SetEvent(video_flush_done);
+ }
+ else if (ret == WAIT_OBJECT_0 + 3) // video resume
+ {
+ ResetEvent(video_resume);
+ SetEvent(coded_frames_event);
+ SetEvent(video_flush_done);
+ }
+ else if (ret == WAIT_OBJECT_0 + 4) // data from demuxer
+ {
+ FRAMEDATA *frame_data = 0;
+ while (frame_data = coded_frames.PopProcessed())
+ {
+ DecodeVideo(frame_data);
+ frame_data->Reset();
+ coded_frames.PushFree(frame_data);
+ }
+ }
+ };
+
+ if (video_opened && video_output)
+ video_output->close();
+ video_opened=false;
+
+ return 0;
+}
+
+static DWORD CALLBACK VideoReaderProcedure(LPVOID param)
+{
+ VideoContext *ctx = (VideoContext *)param;
+ nsavi::avi_reader *reader = ctx->reader;
+ nsavi::Demuxer *demuxer = ctx->demuxer;
+ SetThreadName(-1,"AVI_VideoProcedure");
+ HANDLE wait_handles[] = { killswitch, video_break, video_flush, video_resume };
+ int waitTime = 0;
+ while (1)
+ {
+ int ret = WaitForMultipleObjects(4, wait_handles, FALSE, waitTime);
+
+ if (ret == WAIT_OBJECT_0)
+ {
+ break;
+ }
+ if (ret == WAIT_OBJECT_0 + 1) // video break
+ {
+ ResetEvent(coded_frames_event);
+ ResetEvent(video_break);
+ SetEvent(video_flush_done);
+ waitTime = INFINITE;
+ }
+ else if (ret == WAIT_OBJECT_0 + 2) // video flush
+ {
+ if (video_decoder)
+ video_decoder->Flush();
+ ResetEvent(video_flush);
+ coded_frames.Trim();
+ SetEvent(video_flush_done);
+ waitTime = 0;
+ }
+ else if (ret == WAIT_OBJECT_0 + 3) // video resume
+ {
+ ResetEvent(video_resume);
+ SetEvent(coded_frames_event);
+ SetEvent(video_flush_done);
+ waitTime = 0;
+ }
+ else if (ret == WAIT_TIMEOUT)
+ {
+ void *data=0;
+ uint32_t data_size = 0;
+ uint32_t data_type = 0;
+ int ret = demuxer->GetNextMovieChunk(reader, &data, &data_size, &data_type, video_stream_num);
+ if (ret != nsavi::READ_OK)
+ {
+ break;
+ }
+ int stream_number = GetStreamNumber(data_type);
+ uint16_t type = (data_type>>16);
+
+ if (stream_number == video_stream_num)
+ {
+ FRAMEDATA frame_data;
+ frame_data.data = data;
+ frame_data.length = data_size;
+ frame_data.type = type;
+ DecodeVideo(&frame_data);
+ frame_data.Reset();
+ }
+ else
+ free(data);
+ }
+ };
+
+ if (video_opened && video_output)
+ video_output->close();
+ video_opened=false;
+ free(ctx);
+ return 0;
+}
+
+bool CreateVideoReaderThread(nsavi::Demuxer *demuxer, nsavi::avi_reader *video_reader)
+{
+ if (!video_decoder || video_only)
+ return false;
+
+ VideoContext *context = (VideoContext *)calloc(1, sizeof(VideoContext));
+
+ context->reader = video_reader;
+ context->demuxer = demuxer;
+ DWORD threadId = 0;
+ video_thread = CreateThread(0, 0, VideoReaderProcedure, context, 0, &threadId);
+ SetThreadPriority(video_thread, (INT)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
+ return true;
+}
+
+bool OnVideo(uint16_t type, void *data, size_t length)
+{
+ if (!video_decoder)
+ return false;
+
+ if (!video_only && !video_thread)
+ {
+ DWORD threadId;
+ video_thread = CreateThread(0, 0, VideoProcedure, 0, 0, &threadId);
+ SetThreadPriority(video_thread, (INT)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
+ }
+
+ if (video_thread)
+ {
+ FRAMEDATA *new_frame = coded_frames.PopFree();
+ if (new_frame)
+ {
+ new_frame->Set(type, data, length);
+ coded_frames.PushProcessed(new_frame);
+ SetEvent(coded_frames_event);
+ }
+ return true;
+ }
+ else if (video_only)
+ {
+ FRAMEDATA *new_frame = coded_frames.PopFree();
+ if (new_frame)
+ {
+ new_frame->Set(type, data, length);
+ DecodeVideo(new_frame);
+ new_frame->Reset();
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void Video_Stop()
+{
+ SetEvent(killswitch);
+ if (video_only)
+ {
+ video_thread=0;
+
+ ResetEvent(coded_frames_event);
+ Nullsoft::Utility::AutoLock l(coded_frames_guard);
+ coded_frames.Trim();
+ if (video_opened && video_output)
+ video_output->close();
+ video_opened=false;
+ }
+ else
+ {
+ if (video_thread)
+ WaitForSingleObject(video_thread, INFINITE);
+ video_thread=0;
+
+ ResetEvent(coded_frames_event);
+ Nullsoft::Utility::AutoLock l(coded_frames_guard);
+ coded_frames.Trim();
+ }
+}
+
+void Video_Close()
+{
+ video_opened=false;
+
+ if (video_decoder)
+ {
+ video_decoder->Close();
+ video_decoder=0;
+ }
+}
+
+void Video_Break()
+{
+ if (!video_only && video_decoder)
+ {
+ SetEvent(video_break);
+ HANDLE events[2] = {video_flush_done, video_thread};
+ WaitForMultipleObjects(2, events, FALSE, INFINITE);
+ }
+}
+
+void Video_Flush()
+{
+ if (video_decoder)
+ {
+ if (video_only)
+ {
+ video_decoder->Flush();
+ }
+ else
+ {
+ SetEvent(video_flush);
+ HANDLE events[2] = {video_flush_done, video_thread};
+ WaitForMultipleObjects(2, events, FALSE, INFINITE);
+ }
+ }
+}
+
+/*
+bool Video_DecoderReady()
+{
+if (!video_decoder)
+return true;
+
+return !!video_decoder->Ready();
+}
+*/
diff --git a/Src/Plugins/Input/in_avi/VideoThread.h b/Src/Plugins/Input/in_avi/VideoThread.h
new file mode 100644
index 00000000..c8a134f1
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/VideoThread.h
@@ -0,0 +1,12 @@
+#pragma once
+#include <bfc/platform/types.h>
+#include "../nsavi/demuxer.h"
+extern uint64_t video_total_time;
+
+bool OnVideo(uint16_t type, void *data, size_t length);
+void Video_Stop();
+void Video_Close();
+void Video_Flush();
+void Video_Init();
+void Video_Break();
+bool CreateVideoReaderThread(nsavi::Demuxer *demuxer, nsavi::avi_reader *video_reader); \ No newline at end of file
diff --git a/Src/Plugins/Input/in_avi/api.cpp b/Src/Plugins/Input/in_avi/api.cpp
new file mode 100644
index 00000000..545c00ac
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/api.cpp
@@ -0,0 +1,50 @@
+#include "api__in_avi.h"
+#include "main.h"
+#include <api/service/waservicefactory.h>
+
+api_config *AGAVE_API_CONFIG = 0;
+api_application *WASABI_API_APP = 0;
+api_stats *AGAVE_API_STATS = 0;
+api_language *WASABI_API_LNG = 0;
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+
+template <class api_T>
+void ServiceBuild(api_T *&api_t, GUID factoryGUID_t)
+{
+ if (plugin.service)
+ {
+ waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
+ if (factory)
+ api_t = reinterpret_cast<api_T *>( factory->getInterface() );
+ }
+}
+
+template <class api_T>
+void ServiceRelease(api_T *api_t, GUID factoryGUID_t)
+{
+ if (plugin.service && api_t)
+ {
+ waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
+ if (factory)
+ factory->releaseInterface(api_t);
+ }
+ api_t = NULL;
+}
+
+void WasabiInit()
+{
+ ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID);
+ ServiceBuild(WASABI_API_APP, applicationApiServiceGuid);
+ ServiceBuild(AGAVE_API_STATS, AnonymousStatsGUID);
+ ServiceBuild(WASABI_API_LNG, languageApiGUID);
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(plugin.hDllInstance,InAviLangGUID);
+}
+
+void WasabiQuit()
+{
+ ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID);
+ ServiceRelease(WASABI_API_APP, applicationApiServiceGuid);
+ ServiceRelease(AGAVE_API_STATS, AnonymousStatsGUID);
+ ServiceRelease(WASABI_API_LNG, languageApiGUID);
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_avi/api__in_avi.h b/Src/Plugins/Input/in_avi/api__in_avi.h
new file mode 100644
index 00000000..06d7dd13
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/api__in_avi.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "../Agave/Config/api_config.h"
+#include "../Agave/Language/api_language.h"
+
+#include <api/application/api_application.h>
+extern api_application *applicationApi;
+#define WASABI_API_APP applicationApi
+
+#include "../Winamp/api_stats.h"
+extern api_stats *statsApi;
+#define AGAVE_API_STATS statsApi
+
+void WasabiInit();
+void WasabiQuit();
diff --git a/Src/Plugins/Input/in_avi/http_avi_reader.cpp b/Src/Plugins/Input/in_avi/http_avi_reader.cpp
new file mode 100644
index 00000000..abe9cf53
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/http_avi_reader.cpp
@@ -0,0 +1,255 @@
+#include "main.h"
+#include "http_avi_reader.h"
+#include "../nu/AutoChar.h"
+#include "api__in_avi.h"
+#include <api/service/waservicefactory.h>
+#include "../nu/ns_wc.h"
+#include <strsafe.h>
+
+static const int http_buffer_size=128*1024;
+// {C0A565DC-0CFE-405a-A27C-468B0C8A3A5C}
+static const GUID internetConfigGroupGUID =
+{
+ 0xc0a565dc, 0xcfe, 0x405a, { 0xa2, 0x7c, 0x46, 0x8b, 0xc, 0x8a, 0x3a, 0x5c }
+};
+
+AVIReaderHTTP::AVIReaderHTTP(HANDLE killswitch, HANDLE seek_event)
+{
+ seekable = 0;
+ http = 0;
+ position = 0;
+ handles[0]=killswitch;
+ handles[1]=seek_event;
+}
+
+static void SetUserAgent(api_httpreceiver *http)
+{
+ char agent[256] = {0};
+ StringCchPrintfA(agent, 256, "User-Agent: %S/%S", WASABI_API_APP->main_getAppName(), WASABI_API_APP->main_getVersionNumString());
+ http->addheader(agent);
+}
+
+void AVIReaderHTTP::GetFilename(wchar_t *fn, size_t len)
+{
+ const char *url = http->get_url();
+ MultiByteToWideCharSZ(CP_ACP, 0, url, -1, fn, (int)len);
+}
+
+int AVIReaderHTTP::Open(const wchar_t *url)
+{
+ int use_proxy = 1;
+
+ const wchar_t *proxy = AGAVE_API_CONFIG->GetString(internetConfigGroupGUID, L"proxy", 0);
+ bool proxy80 = AGAVE_API_CONFIG->GetBool(internetConfigGroupGUID, L"proxy80", false);
+ if (proxy80 && wcsstr(url, L":") && (!wcsstr(url, L":80/") && wcsstr(url, L":80") != (url + wcslen(url) - 3)))
+ use_proxy = 0;
+
+ waServiceFactory *sf = plugin.service->service_getServiceByGuid(httpreceiverGUID);
+ if (sf) http = (api_httpreceiver *)sf->getInterface();
+
+ http->open(API_DNS_AUTODNS, http_buffer_size, (use_proxy && proxy && proxy[0]) ? (char *)AutoChar(proxy) : NULL);
+ http->addheader("Accept:*/*");
+ SetUserAgent(http);
+
+ http->connect(AutoChar(url, CP_UTF8), 0);
+
+ return nsavi::READ_OK;
+}
+
+
+int AVIReaderHTTP::Open(const char *url, uint64_t start_offset)
+{
+ int use_proxy = 1;
+
+ const wchar_t *proxy = AGAVE_API_CONFIG->GetString(internetConfigGroupGUID, L"proxy", 0);
+ bool proxy80 = AGAVE_API_CONFIG->GetBool(internetConfigGroupGUID, L"proxy80", false);
+ if (proxy80 && strstr(url, ":") && (!strstr(url, ":80/") && strstr(url, ":80") != (url + strlen(url) - 3)))
+ use_proxy = 0;
+
+ waServiceFactory *sf = plugin.service->service_getServiceByGuid(httpreceiverGUID);
+ if (sf) http = (api_httpreceiver *)sf->getInterface();
+
+ http->open(API_DNS_AUTODNS, http_buffer_size, (use_proxy && proxy && proxy[0]) ? (char *)AutoChar(proxy) : NULL);
+ http->addheader("Accept:*/*");
+ SetUserAgent(http);
+
+ if (start_offset)
+ {
+ char temp[128] = {0};
+ StringCchPrintfA(temp, 128, "Range: bytes=%I64u-", start_offset);
+ http->addheader(temp);
+ position = start_offset;
+ }
+
+ http->connect(url, !!start_offset);
+
+ return nsavi::READ_OK;
+}
+
+void AVIReaderHTTP::Close()
+{
+ if (http)
+ {
+ waServiceFactory *sf = plugin.service->service_getServiceByGuid(httpreceiverGUID);
+ if (sf) sf->releaseInterface(http);
+ }
+ http = 0;
+}
+
+
+int AVIReaderHTTP::Connect()
+{
+ http->run();
+ int status = http->get_status();
+ while (status == HTTPRECEIVER_STATUS_CONNECTING || status == HTTPRECEIVER_STATUS_READING_HEADERS)
+ {
+ int ret = WaitForMultipleObjects(2, handles, FALSE, 50);
+ if (ret == WAIT_OBJECT_0+1)
+ return -2;
+ else if (ret != WAIT_TIMEOUT)
+ return -1;
+ http->run();
+ status = http->get_status();
+ }
+
+ if (status == HTTPRECEIVER_STATUS_ERROR)
+ {
+ return nsavi::READ_FAILED;
+ }
+ const char *headers = http->getallheaders();
+ const char *ranges = http->getheader("accept-ranges");
+ seekable = (ranges && _stricmp(ranges, "bytes"));
+
+ return nsavi::READ_OK;
+}
+
+int AVIReaderHTTP::Buffer()
+{
+ while (http->bytes_available() < http_buffer_size && http->run() == HTTPRECEIVER_RUN_OK)
+ {
+ int ret = WaitForMultipleObjects(2, handles, FALSE, 50);
+ if (ret == WAIT_OBJECT_0+1)
+ return -2;
+ else if (ret != WAIT_TIMEOUT)
+ return -1;
+ }
+
+ return nsavi::READ_OK;
+}
+
+uint64_t AVIReaderHTTP::GetContentLength()
+{
+ const char *content_length = http->getheader("content-length");
+ if (content_length)
+ return _atoi64(content_length);
+ else
+ return 0;
+}
+
+int AVIReaderHTTP::Read(void *buffer, uint32_t read_length, uint32_t *bytes_read)
+{
+ uint32_t total_bytes_read=0;
+ for(;;)
+ {
+ int ret = http->run();
+ int http_bytes_read = http->get_bytes(buffer, read_length);
+
+ read_length -= http_bytes_read;
+ buffer = (uint8_t *)buffer + http_bytes_read;
+ total_bytes_read+=http_bytes_read;
+ position += http_bytes_read;
+
+ if (!read_length)
+ {
+ *bytes_read = total_bytes_read;
+ return nsavi::READ_OK;
+ }
+
+ if (http->bytes_available() == 0)
+ {
+ if (ret == HTTPRECEIVER_RUN_CONNECTION_CLOSED)
+ {
+ if (position == http->content_length())
+ return nsavi::READ_EOF;
+ else
+ return nsavi::READ_DISCONNECT;
+ }
+ else if (ret == HTTPRECEIVER_RUN_ERROR)
+ {
+ return nsavi::READ_DISCONNECT;
+ }
+ }
+
+ ret = WaitForMultipleObjects(2, handles, FALSE, 50);
+ if (ret == WAIT_OBJECT_0+1)
+ return -2;
+ else if (ret != WAIT_TIMEOUT)
+ return -1;
+ }
+
+ return nsavi::READ_OK;
+}
+
+int AVIReaderHTTP::Peek(void *buffer, uint32_t read_length, uint32_t *bytes_read)
+{
+ uint32_t total_bytes_read=0;
+
+ while (http->bytes_available() < (int)read_length && http->run() == HTTPRECEIVER_RUN_OK)
+ {
+ int ret = WaitForMultipleObjects(2, handles, FALSE, 50);
+ if (ret == WAIT_OBJECT_0+1)
+ return -2;
+ else if (ret != WAIT_TIMEOUT)
+ return -1;
+ }
+
+ *bytes_read = http->peek_bytes(buffer, read_length);
+ return nsavi::READ_OK;
+}
+
+int AVIReaderHTTP::Seek(uint64_t seek_position)
+{
+
+ // if position is forward of our current position, see if we have enough in the buffer to just advance the buffer
+ if (seek_position > position
+ && seek_position - position <= http->bytes_available())
+ {
+ int bytes_read = http->get_bytes(0, (int)(seek_position - position));
+ position += bytes_read;
+ return nsavi::READ_OK;
+ }
+ else
+ {
+ // otherwise, close connection and re-open with a start position
+ char *url = _strdup(http->get_url());
+ Close();
+ Open(url, seek_position);
+ free(url);
+ return Connect();
+ }
+}
+
+uint64_t AVIReaderHTTP::Tell()
+{
+ return position;
+}
+
+int AVIReaderHTTP::Skip(uint32_t skip_bytes)
+{
+ // see if we have enough room in our buffer
+ if (http->bytes_available() >= (int)skip_bytes)
+ {
+ int bytes_read = http->get_bytes(0, skip_bytes);
+ position += bytes_read;
+ return nsavi::READ_OK;
+ }
+ else
+ {
+ // close connection and re-open with a start position
+ char *url = _strdup(http->get_url());
+ Close();
+ Open(url, position+skip_bytes);
+ free(url);
+ return Connect();
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_avi/http_avi_reader.h b/Src/Plugins/Input/in_avi/http_avi_reader.h
new file mode 100644
index 00000000..66eb6d49
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/http_avi_reader.h
@@ -0,0 +1,31 @@
+#pragma once
+#include "../nsavi/avi_reader.h"
+#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
+
+class AVIReaderHTTP : public nsavi::avi_reader
+{
+public:
+ AVIReaderHTTP(HANDLE killswitch, HANDLE seek_event);
+ int Open(const wchar_t *url);
+ void Close();
+ int Connect();
+ int Buffer();
+
+ /* avi_reader implementation */
+ int Read(void *buffer, uint32_t read_length, uint32_t *bytes_read);
+ int Peek(void *buffer, uint32_t read_length, uint32_t *bytes_read);
+ //void OverlappedHint(uint32_t read_length);
+ int Seek(uint64_t position);
+ uint64_t Tell();
+ int Skip(uint32_t skip_bytes);
+ uint64_t GetContentLength();
+ void GetFilename(wchar_t *fn, size_t len);
+private:
+ /* internal methods */
+ int Open(const char *url, uint64_t start_offset=0);
+private:
+ api_httpreceiver *http;
+ uint64_t position;
+ bool seekable;
+ HANDLE handles[2];
+}; \ No newline at end of file
diff --git a/Src/Plugins/Input/in_avi/ifc_aviaudiodecoder.h b/Src/Plugins/Input/in_avi/ifc_aviaudiodecoder.h
new file mode 100644
index 00000000..00994bb8
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/ifc_aviaudiodecoder.h
@@ -0,0 +1,67 @@
+#pragma once
+#include <bfc/dispatch.h>
+
+class NOVTABLE ifc_aviaudiodecoder : public Dispatchable
+{
+protected:
+ ifc_aviaudiodecoder() {}
+ ~ifc_aviaudiodecoder() {}
+public:
+ enum
+ {
+ AVI_SUCCESS = 0,
+ AVI_NEED_MORE_INPUT = -1,
+ AVI_FAILURE=1,
+ AVI_RESYNC=2,
+ };
+ int OutputFrameSize(uint32_t *frame_size);
+ int GetOutputProperties(unsigned int *sampleRate, unsigned int *channels, unsigned int *bitsPerSample, bool *isFloat); // can return an error code for "havn't locked to stream yet"
+ // many AVI files arbitrarily divide the data stream (e.g. an MP3 frame might span two chunks). Others put ALL the audio into ONE chunk. awesome.
+ // for this reason, you are given double pointers to the data buffer and a pointer to the data size
+ // and you are expected to update it on return
+ // if inputBufferBytes != 0, you will be called again with the same data (return AVI_SUCCESS, though)
+ // if you were unable to decode because of bitstream errors, return AVI_RESYNC so in_avi can try to correct timing
+ int DecodeChunk(uint16_t type, const void **inputBuffer, uint32_t *inputBufferBytes, void *outputBuffer, uint32_t *outputBufferBytes);
+ void Flush();
+ void Close(); // self-destructs
+ void EndOfStream(); // no more input, output anything you have buffered
+ DISPATCH_CODES
+ {
+ OUTPUT_FRAME_SIZE = 0,
+ GET_OUTPUT_PROPERTIES = 1,
+ DECODE_CHUNK = 2,
+ FLUSH = 3,
+ CLOSE = 4,
+ END_OF_STREAM = 5,
+ };
+};
+
+inline int ifc_aviaudiodecoder::OutputFrameSize(uint32_t *frame_size)
+{
+ return _call(OUTPUT_FRAME_SIZE, (int)AVI_FAILURE, frame_size);
+}
+
+inline int ifc_aviaudiodecoder::GetOutputProperties(unsigned int *sampleRate, unsigned int *channels, unsigned int *bitsPerSample, bool *isFloat)
+{
+ return _call(GET_OUTPUT_PROPERTIES, (int)AVI_FAILURE, sampleRate, channels, bitsPerSample, isFloat);
+}
+
+inline int ifc_aviaudiodecoder::DecodeChunk(uint16_t type, const void **inputBuffer, uint32_t *inputBufferBytes, void *outputBuffer, uint32_t *outputBufferBytes)
+{
+ return _call(DECODE_CHUNK, (int)AVI_FAILURE, type, inputBuffer, inputBufferBytes, outputBuffer, outputBufferBytes);
+}
+
+inline void ifc_aviaudiodecoder::Flush()
+{
+ _voidcall(FLUSH);
+}
+
+inline void ifc_aviaudiodecoder::Close()
+{
+ _voidcall(CLOSE);
+}
+
+inline void ifc_aviaudiodecoder::EndOfStream()
+{
+ _voidcall(END_OF_STREAM);
+}
diff --git a/Src/Plugins/Input/in_avi/ifc_avivideodecoder.h b/Src/Plugins/Input/in_avi/ifc_avivideodecoder.h
new file mode 100644
index 00000000..27b158f9
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/ifc_avivideodecoder.h
@@ -0,0 +1,77 @@
+#pragma once
+#include <bfc/dispatch.h>
+#include <bfc/platform/types.h>
+
+class NOVTABLE ifc_avivideodecoder : public Dispatchable
+{
+protected:
+ ifc_avivideodecoder() {}
+ ~ifc_avivideodecoder() {}
+public:
+ enum
+ {
+ AVI_SUCCESS = 0,
+ AVI_NEED_MORE_INPUT = -1,
+ AVI_FAILURE=1,
+ };
+ int GetOutputProperties(int *x, int *y, int *color_format, double *aspect_ratio, int *flip);
+ int DecodeChunk(uint16_t type, const void *inputBuffer, size_t inputBufferBytes);
+ void Flush();
+ void Close();
+ int GetPicture(void **data, void **decoder_data);
+ void FreePicture(void *data, void *decoder_data);
+ void EndOfStream(); // signal to the decoder that the video bitstream is over - flush any buffered frames
+ void HurryUp(int state); // 1 = hurry up (drop unnecessary frames), 0 = revert to normal
+ int GetPalette(RGB32 **palette);
+ DISPATCH_CODES
+ {
+ GET_OUTPUT_PROPERTIES = 0,
+ DECODE_CHUNK = 1,
+ FLUSH = 2,
+ CLOSE = 3,
+ GET_PICTURE = 4,
+ FREE_PICTURE = 5,
+ END_OF_STREAM = 6,
+ HURRY_UP = 7,
+ GET_PALETTE = 8,
+ };
+};
+
+inline int ifc_avivideodecoder::GetOutputProperties(int *x, int *y, int *color_format, double *aspect_ratio, int *flip)
+{
+ return _call(GET_OUTPUT_PROPERTIES, (int)AVI_FAILURE, x, y, color_format, aspect_ratio, flip);
+}
+inline int ifc_avivideodecoder::DecodeChunk(uint16_t type, const void *inputBuffer, size_t inputBufferBytes)
+{
+ return _call(DECODE_CHUNK, (int)AVI_FAILURE, type, inputBuffer, inputBufferBytes);
+}
+inline void ifc_avivideodecoder::Flush()
+{
+ _voidcall(FLUSH);
+}
+inline void ifc_avivideodecoder::Close()
+{
+ _voidcall(CLOSE);
+}
+inline int ifc_avivideodecoder::GetPicture(void **data, void **decoder_data)
+{
+ return _call(GET_PICTURE, (int)AVI_FAILURE, data, decoder_data);
+}
+inline void ifc_avivideodecoder::FreePicture(void *data, void *decoder_data)
+{
+ _voidcall(FREE_PICTURE, data, decoder_data);
+}
+inline void ifc_avivideodecoder::EndOfStream()
+{
+ _voidcall(END_OF_STREAM);
+}
+
+inline void ifc_avivideodecoder::HurryUp(int state)
+{
+ _voidcall(HURRY_UP, state);
+}
+
+inline int ifc_avivideodecoder::GetPalette(RGB32 **palette)
+{
+ return _call(GET_PALETTE, (int)AVI_FAILURE, palette);
+}
diff --git a/Src/Plugins/Input/in_avi/in_avi.rc b/Src/Plugins/Input/in_avi/in_avi.rc
new file mode 100644
index 00000000..0191bd2c
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/in_avi.rc
@@ -0,0 +1,159 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "#include ""version.rc2""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_INFODIALOG DIALOGEX 0, 0, 339, 266
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "AVI File Properties"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "Close",IDOK,282,245,50,14
+ CONTROL "",IDC_TAB1,"SysTabControl32",0x0,7,7,325,235
+END
+
+IDD_TRACKS DIALOGEX 0, 0, 316, 183
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ CONTROL "",IDC_TRACKLIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_TABSTOP,7,7,302,169
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_INFODIALOG, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 332
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 259
+ END
+
+ IDD_TRACKS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 309
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 176
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_AVI "Nullsoft AVI Demuxer v%s"
+ 65535 "{CA36E14A-3742-4edc-A40F-2BC87F26B347}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_AVI_OLD "Nullsoft AVI Demuxer"
+ IDS_BUFFERING "Buffering"
+ IDS_FAMILY_STRING "AVI Video"
+ IDS_TAB_TRACKS "Tracks"
+ IDS_TAB_METADATA "Metadata"
+ IDS_COLUMN_TRACK_TYPE "Type"
+ IDS_COLUMN_CODEC_NAME "Codec Name"
+ IDS_COLUMN_CODEC_ID "Codec ID"
+ IDS_COLUMN_DESCRIPTION "Description"
+ IDS_COLUMN_STREAM_NAME "Stream Name"
+ IDS_COLUMN_FIELD "Field"
+ IDS_COLUMN_FOURCC "FOURCC"
+ IDS_COLUMN_VALUE "Value"
+ IDS_TYPE_VIDEO "Video"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_TYPE_AUDIO "Audio"
+ IDS_FIELD_TOOL "Tool"
+ IDS_FIELD_ARTIST "Artist"
+ IDS_FIELD_PUBLISHER "Publisher"
+ IDS_FIELD_ALBUM "Album"
+ IDS_FIELD_COMPOSER "Composer"
+ IDS_FIELD_GENRE "Genre"
+ IDS_FIELD_COMMENT "Comment"
+ IDS_FIELD_TITLE "Title"
+ IDS_FIELD_COPYRIGHT "Copyright"
+ IDS_KBPS "kbps"
+ IDS_AVI_DESC "Audio/Video Interleave (AVI)"
+ IDS_ABOUT_TEXT "%s\n© 2009-2023 Winamp SA\nWritten by: Ben Allison\nBuild date: %s"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/Plugins/Input/in_avi/in_avi.sln b/Src/Plugins/Input/in_avi/in_avi.sln
new file mode 100644
index 00000000..0f8142f8
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/in_avi.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29509.3
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "in_avi", "in_avi.vcxproj", "{FE01A3D7-A137-4AEB-9061-20BE2ACF2D5F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {FE01A3D7-A137-4AEB-9061-20BE2ACF2D5F}.Debug|Win32.ActiveCfg = Debug|Win32
+ {FE01A3D7-A137-4AEB-9061-20BE2ACF2D5F}.Debug|Win32.Build.0 = Debug|Win32
+ {FE01A3D7-A137-4AEB-9061-20BE2ACF2D5F}.Debug|x64.ActiveCfg = Debug|x64
+ {FE01A3D7-A137-4AEB-9061-20BE2ACF2D5F}.Debug|x64.Build.0 = Debug|x64
+ {FE01A3D7-A137-4AEB-9061-20BE2ACF2D5F}.Release|Win32.ActiveCfg = Release|Win32
+ {FE01A3D7-A137-4AEB-9061-20BE2ACF2D5F}.Release|Win32.Build.0 = Release|Win32
+ {FE01A3D7-A137-4AEB-9061-20BE2ACF2D5F}.Release|x64.ActiveCfg = Release|x64
+ {FE01A3D7-A137-4AEB-9061-20BE2ACF2D5F}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {81F95D61-31E0-4423-B565-752889A9A18B}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/Input/in_avi/in_avi.vcxproj b/Src/Plugins/Input/in_avi/in_avi.vcxproj
new file mode 100644
index 00000000..e2688993
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/in_avi.vcxproj
@@ -0,0 +1,284 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{FE01A3D7-A137-4AEB-9061-20BE2ACF2D5F}</ProjectGuid>
+ <RootNamespace>in_avi</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnabled>false</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;IN_AVI_EXPORTS;UNICODE_INPUT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;IN_AVI_EXPORTS;UNICODE_INPUT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;IN_AVI_EXPORTS;UNICODE_INPUT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4244;4018;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;IN_AVI_EXPORTS;UNICODE_INPUT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4244;4018;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\..\nsavi\demuxer.cpp" />
+ <ClCompile Include="..\..\..\nsavi\duration.cpp" />
+ <ClCompile Include="..\..\..\nsavi\info.cpp" />
+ <ClCompile Include="..\..\..\nsavi\metadata.cpp" />
+ <ClCompile Include="..\..\..\nsavi\ParserBase.cpp" />
+ <ClCompile Include="..\..\..\nsavi\read.cpp" />
+ <ClCompile Include="..\..\..\nsavi\seektable.cpp" />
+ <ClCompile Include="..\..\..\nu\listview.cpp" />
+ <ClCompile Include="..\..\..\nu\RingBuffer.cpp" />
+ <ClCompile Include="..\..\..\nu\SpillBuffer.cpp" />
+ <ClCompile Include="api.cpp" />
+ <ClCompile Include="ExtendedFileInfo.cpp" />
+ <ClCompile Include="http_avi_reader.cpp" />
+ <ClCompile Include="InfoDialog.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="PlayThread.cpp" />
+ <ClCompile Include="VideoThread.cpp" />
+ <ClCompile Include="win32_avi_reader.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\..\nsavi\avi_header.h" />
+ <ClInclude Include="..\..\..\nsavi\avi_reader.h" />
+ <ClInclude Include="..\..\..\nsavi\demuxer.h" />
+ <ClInclude Include="..\..\..\nsavi\duration.h" />
+ <ClInclude Include="..\..\..\nsavi\info.h" />
+ <ClInclude Include="..\..\..\nsavi\metadata.h" />
+ <ClInclude Include="..\..\..\nsavi\nsavi.h" />
+ <ClInclude Include="..\..\..\nsavi\ParserBase.h" />
+ <ClInclude Include="..\..\..\nsavi\read.h" />
+ <ClInclude Include="..\..\..\nsavi\seektable.h" />
+ <ClInclude Include="..\..\..\nu\AudioOutput.h" />
+ <ClInclude Include="..\..\..\nu\listview.h" />
+ <ClInclude Include="..\..\..\nu\RingBuffer.h" />
+ <ClInclude Include="..\..\..\nu\SpillBuffer.h" />
+ <ClInclude Include="..\..\..\nu\VideoClock.h" />
+ <ClInclude Include="api__in_avi.h" />
+ <ClInclude Include="http_avi_reader.h" />
+ <ClInclude Include="ifc_aviaudiodecoder.h" />
+ <ClInclude Include="ifc_avivideodecoder.h" />
+ <ClInclude Include="interfaces.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="player.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="StreamSelector.h" />
+ <ClInclude Include="svc_avidecoder.h" />
+ <ClInclude Include="VideoThread.h" />
+ <ClInclude Include="win32_avi_reader.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="in_avi.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Input/in_avi/in_avi.vcxproj.filters b/Src/Plugins/Input/in_avi/in_avi.vcxproj.filters
new file mode 100644
index 00000000..e6323f44
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/in_avi.vcxproj.filters
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="win32_avi_reader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="api.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ExtendedFileInfo.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="http_avi_reader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="InfoDialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PlayThread.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VideoThread.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nsavi\demuxer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nsavi\duration.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nsavi\info.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\listview.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nsavi\metadata.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nsavi\ParserBase.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nsavi\read.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\RingBuffer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nsavi\seektable.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\SpillBuffer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api__in_avi.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="http_avi_reader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ifc_aviaudiodecoder.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ifc_avivideodecoder.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="interfaces.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="player.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="StreamSelector.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="svc_avidecoder.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VideoThread.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="win32_avi_reader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nu\AudioOutput.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nsavi\avi_header.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nsavi\avi_reader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nsavi\demuxer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nsavi\duration.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nsavi\info.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nu\listview.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nsavi\metadata.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nsavi\nsavi.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nsavi\ParserBase.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nsavi\read.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nu\RingBuffer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nsavi\seektable.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nu\SpillBuffer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nu\VideoClock.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{e77a3e6b-2a9d-49ed-9c56-aae5b1e712ec}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{799c4db0-c7b2-4460-ac6a-f399052539a6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{386d35de-b42e-4963-8764-99635e1efddb}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="in_avi.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Input/in_avi/interfaces.h b/Src/Plugins/Input/in_avi/interfaces.h
new file mode 100644
index 00000000..e2fb2f0a
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/interfaces.h
@@ -0,0 +1,4 @@
+#pragma once
+#include "svc_avidecoder.h"
+#include "ifc_aviaudiodecoder.h"
+#include "ifc_avivideodecoder.h" \ No newline at end of file
diff --git a/Src/Plugins/Input/in_avi/main.cpp b/Src/Plugins/Input/in_avi/main.cpp
new file mode 100644
index 00000000..844da8fc
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/main.cpp
@@ -0,0 +1,330 @@
+#include "main.h"
+#include "../winamp/wa_ipc.h"
+#include "api__in_avi.h"
+#include "../nsavi/nsavi.h"
+#include "win32_avi_reader.h"
+#include "resource.h"
+#include <strsafe.h>
+
+#define AVI_PLUGIN_VERSION L"0.78"
+
+// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
+static const GUID playbackConfigGroupGUID =
+{ 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf } };
+
+void SetFileExtensions(void)
+{
+ static char fileExtensionsString[256] = {0}; // "AVI\0Audio/Video Interleave (AVI)\0"
+ char* end = 0;
+ size_t remaining = 0;
+ StringCchCopyExA(fileExtensionsString, 256, "AVI", &end, &remaining, 0);
+ StringCchCopyExA(end+1, remaining-1, WASABI_API_LNGSTRING(IDS_AVI_DESC), 0, 0, 0);
+ plugin.FileExtensions = fileExtensionsString;
+}
+
+HANDLE killswitch = 0, seek_event = 0;
+volatile LONG seek_position=-1;
+int g_duration = -1;
+nu::VideoClock video_clock;
+int paused = 0;
+static HANDLE play_thread = 0;
+int video_only=0;
+In_Module *dshow_mod = 0;
+HMODULE in_dshow=0;
+wchar_t pluginName[256] = {0};
+
+static int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
+{
+ MSGBOXPARAMS msgbx = {sizeof(MSGBOXPARAMS),0};
+ msgbx.lpszText = message;
+ msgbx.lpszCaption = title;
+ msgbx.lpszIcon = MAKEINTRESOURCE(102);
+ msgbx.hInstance = GetModuleHandle(0);
+ msgbx.dwStyle = MB_USERICON;
+ msgbx.hwndOwner = parent;
+ return MessageBoxIndirect(&msgbx);
+}
+
+void About(HWND hwndParent)
+{
+ wchar_t message[1024] = {0}, text[1024] = {0};
+ WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_AVI_OLD,text,1024);
+ StringCchPrintf(message, 1024, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
+ plugin.description, TEXT(__DATE__));
+ DoAboutMessageBox(hwndParent,text,message);
+}
+
+int Init()
+{
+ if (!IsWindow(plugin.hMainWindow))
+ return IN_INIT_FAILURE;
+
+ WasabiInit();
+
+ StringCchPrintfW(pluginName, ARRAYSIZE(pluginName),
+ WASABI_API_LNGSTRINGW(IDS_NULLSOFT_AVI), AVI_PLUGIN_VERSION);
+ plugin.description = (char*)pluginName;
+ SetFileExtensions();
+ return IN_INIT_SUCCESS;
+}
+
+void Quit()
+{
+ WasabiQuit();
+ if (in_dshow)
+ {
+ FreeLibrary(in_dshow);
+ in_dshow = NULL;
+ }
+}
+
+void GetFileInfo(const wchar_t *file, wchar_t *title, int *length_in_ms)
+{
+ if (title)
+ *title=0;
+ if (length_in_ms)
+ {
+ if (file && *file)
+ {
+ *length_in_ms = -1000; // fallback if anything fails
+ AVIReaderWin32 reader;
+ if (reader.Open(file) == nsavi::READ_OK)
+ {
+ nsavi::Duration duration(&reader);
+ duration.GetDuration(length_in_ms);
+ reader.Close();
+ }
+ }
+ else
+ {
+ if (dshow_mod)
+ {
+ dshow_mod->GetFileInfo(file, title, length_in_ms);
+ }
+ else
+ {
+
+ *length_in_ms=g_duration;
+ }
+ }
+ }
+
+
+}
+
+int InfoBox(const wchar_t *file, HWND hwndParent)
+{
+ AVIReaderWin32 reader;
+ if (reader.Open(file) == nsavi::READ_OK)
+ {
+ nsavi::Metadata metadata(&reader);
+ uint32_t type;
+ if (metadata.GetRIFFType(&type) == nsavi::READ_OK && type == ' IVA')
+ {
+ WASABI_API_DIALOGBOXPARAMW(IDD_INFODIALOG, hwndParent, InfoDialog, (LPARAM)&metadata);
+ }
+ reader.Close();
+ }
+
+ return INFOBOX_UNCHANGED;
+}
+
+int IsOurFile(const wchar_t *fn)
+{
+ return 0;
+}
+
+
+int Play(const wchar_t *fn) // return zero on success, -1 on file-not-found, some other value on other (stopping winamp) error
+{
+ g_duration = -1;
+ seek_position = -1;
+ video_clock.Reset();
+ video_only=false;
+ paused=0;
+ if (!killswitch)
+ killswitch = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!seek_event)
+ seek_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ ResetEvent(killswitch);
+ ResetEvent(seek_event);
+
+ play_thread = CreateThread(0, 0, AVIPlayThread, _wcsdup(fn), 0, 0);
+ SetThreadPriority(play_thread, (int)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
+ return 0; // success
+}
+
+
+void Pause()
+{
+ paused = 1;
+
+ if (dshow_mod)
+ {
+ dshow_mod->Pause();
+ }
+ else if (video_only)
+ {
+ video_clock.Pause();
+ }
+ else
+ {
+ plugin.outMod->Pause(1);
+ }
+}
+
+void UnPause()
+{
+ paused = 0;
+ if (dshow_mod)
+ {
+ dshow_mod->UnPause();
+ }
+ else if (video_only)
+ {
+ video_clock.Unpause();
+ }
+ else
+ {
+ plugin.outMod->Pause(0);
+ }
+}
+
+int IsPaused()
+{
+ if (dshow_mod)
+ return dshow_mod->IsPaused();
+
+ return paused;
+}
+
+void Stop()
+{
+ if (dshow_mod)
+ {
+ dshow_mod->Stop();
+ dshow_mod=0;
+ }
+ else if (play_thread)
+ {
+ SetEvent(killswitch);
+ WaitForSingleObject(play_thread, INFINITE);
+ ResetEvent(killswitch);
+ play_thread=0;
+ }
+}
+
+// time stuff
+int GetLength()
+{
+ if (dshow_mod)
+ return dshow_mod->GetLength();
+ else
+ return g_duration;
+}
+
+int GetOutputTime()
+{
+ if (dshow_mod)
+ return dshow_mod->GetOutputTime();
+ else if (video_only)
+ return video_clock.GetOutputTime();
+ if (plugin.outMod)
+ return plugin.outMod->GetOutputTime();
+ else
+ return 0;
+}
+
+void SetOutputTime(int time_in_ms)
+{
+ if (dshow_mod)
+ {
+ dshow_mod->SetOutputTime(time_in_ms);
+ }
+ else if (seek_event)
+ {
+ InterlockedExchange(&seek_position, time_in_ms);
+ SetEvent(seek_event);
+ }
+}
+
+void SetVolume(int volume)
+{
+ if (dshow_mod)
+ {
+ dshow_mod->SetVolume(volume);
+ }
+ else
+ {
+ plugin.outMod->SetVolume(volume);
+ }
+}
+
+void SetPan(int pan)
+{
+ if (dshow_mod)
+ {
+ dshow_mod->SetPan(pan);
+ }
+ else
+ {
+ plugin.outMod->SetPan(pan);
+ }
+}
+
+void EQSet(int on, char data[10], int preamp)
+{
+ if (dshow_mod)
+ {
+ dshow_mod->EQSet(on, data, preamp);
+ return;
+ }
+}
+
+In_Module plugin =
+{
+ IN_VER_RET,
+ "nullsoft(in_avi.dll)",
+ NULL, // hMainWindow
+ NULL, // hDllInstance
+ 0 /*"AVI\0Audio/Video Interleave (AVI)\0"*/,
+ 1, // is seekable
+ IN_MODULE_FLAG_USES_OUTPUT_PLUGIN, //UsesOutputPlug
+ About,
+ About,
+ Init,
+ Quit,
+ GetFileInfo,
+ InfoBox,
+ IsOurFile,
+ Play,
+ Pause,
+ UnPause,
+ IsPaused,
+ Stop,
+ GetLength,
+ GetOutputTime,
+ SetOutputTime,
+ SetVolume,
+ SetPan,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ EQSet,
+ 0,
+ 0
+};
+
+extern "C" __declspec(dllexport) In_Module * winampGetInModule2()
+{
+ return &plugin;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_avi/main.h b/Src/Plugins/Input/in_avi/main.h
new file mode 100644
index 00000000..fb905612
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/main.h
@@ -0,0 +1,19 @@
+#pragma once
+#include "../winamp/in2.h"
+#include "../nu/VideoClock.h"
+#include "../nsavi/nsavi.h"
+extern In_Module plugin, *dshow_mod;
+DWORD CALLBACK AVIPlayThread(LPVOID param);
+extern HANDLE killswitch, seek_event;
+extern volatile LONG seek_position;
+extern int g_duration;
+extern nu::VideoClock video_clock;
+extern int video_only;
+extern HMODULE in_dshow;
+
+/* InfoDialog.cpp */
+INT_PTR CALLBACK InfoDialog(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+void GetVideoCodecName(wchar_t *str, size_t str_cch, nsavi::STRF *stream_format);
+void GetVideoCodecDescription(wchar_t *str, size_t str_cch, nsavi::STRF *stream_format);
+void GetAudioCodecName(wchar_t *str, size_t str_cch, nsavi::STRF *stream_format);
+void GetAudioCodecDescription(wchar_t *str, size_t str_cch, nsavi::STRF *stream_format); \ No newline at end of file
diff --git a/Src/Plugins/Input/in_avi/player.h b/Src/Plugins/Input/in_avi/player.h
new file mode 100644
index 00000000..f7699309
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/player.h
@@ -0,0 +1,3 @@
+#pragma once
+
+extern int audio_stream_num, video_stream_num; \ No newline at end of file
diff --git a/Src/Plugins/Input/in_avi/resource.h b/Src/Plugins/Input/in_avi/resource.h
new file mode 100644
index 00000000..990d046b
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/resource.h
@@ -0,0 +1,50 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by in_avi.rc
+//
+#define IDS_NULLSOFT_AVI_OLD 0
+#define IDS_BUFFERING 2
+#define IDS_AVI_VIDEO 3
+#define IDS_FAMILY_STRING 3
+#define IDS_STRING4 4
+#define IDS_TAB_TRACKS 5
+#define IDS_TAB_METADATA 6
+#define IDS_COLUMN_TRACK_TYPE 7
+#define IDS_COLUMN_CODEC_NAME 8
+#define IDS_COLUMN_CODEC_ID 9
+#define IDS_COLUMN_DESCRIPTION 10
+#define IDS_COLUMN_STREAM_NAME 11
+#define IDS_COLUMN_FIELD 12
+#define IDS_COLUMN_FOURCC 13
+#define IDS_COLUMN_VALUE 14
+#define IDS_TYPE_VIDEO 15
+#define IDS_TYPE_AUDIO 16
+#define IDS_FIELD_TOOL 17
+#define IDS_FIELD_ARTIST 18
+#define IDS_FIELD_PUBLISHER 19
+#define IDS_FIELD_ALBUM 20
+#define IDS_FIELD_COMPOSER 21
+#define IDS_FIELD_GENRE 22
+#define IDS_FIELD_COMMENT 23
+#define IDS_FIELD_TITLE 24
+#define IDS_FIELD_COPYRIGHT 25
+#define IDS_STRING105 26
+#define IDS_KBPS 26
+#define IDS_AVI_DESC 27
+#define IDS_ABOUT_TEXT 28
+#define IDD_INFODIALOG 103
+#define IDD_TRACKS 104
+#define IDC_TRACKLIST 1001
+#define IDC_TAB1 1002
+#define IDS_NULLSOFT_AVI 65534
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 108
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1003
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/Input/in_avi/svc_avidecoder.h b/Src/Plugins/Input/in_avi/svc_avidecoder.h
new file mode 100644
index 00000000..28b7300b
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/svc_avidecoder.h
@@ -0,0 +1,74 @@
+#pragma once
+#include <bfc/dispatch.h>
+#include "../nsavi/avi_header.h"
+#include <api/service/services.h>
+class ifc_avivideodecoder;
+class ifc_aviaudiodecoder;
+class ifc_avitextdecoder;
+class ifc_avimididecoder;
+
+class NOVTABLE svc_avidecoder : public Dispatchable
+{
+protected:
+ svc_avidecoder() {}
+ ~svc_avidecoder() {}
+public:
+ static FOURCC getServiceType() { return WaSvc::AVIDECODER; }
+ enum
+ {
+ CREATEDECODER_SUCCESS = 0,
+ CREATEDECODER_NOT_MINE = -1, // graceful failure
+ CREATEDECODER_FAILURE = 1, // generic failure - codec_id is ours but we weren't able to create the decoder (e.g. track_entry_data)
+ };
+ int CreateAudioDecoder(const nsavi::AVIH *avi_header, const nsavi::STRH *stream_header, const nsavi::STRF *stream_format, const nsavi::STRD *stream_data,
+ unsigned int preferred_bits, unsigned int max_channels, bool floating_point,
+ ifc_aviaudiodecoder **decoder);
+ int CreateVideoDecoder(const nsavi::AVIH *avi_header, const nsavi::STRH *stream_header, const nsavi::STRF *stream_format, const nsavi::STRD *stream_data, ifc_avivideodecoder **decoder);
+
+ // for retrieving short codec names, e.g. "MPEG Audio" or "H.264"
+ int GetAudioCodec(const nsavi::AVIH *avi_header, const nsavi::STRH *stream_header, const nsavi::STRF *stream_format, const nsavi::STRD *stream_data, wchar_t *buf, size_t buf_cch);
+ int GetVideoCodec(const nsavi::AVIH *avi_header, const nsavi::STRH *stream_header, const nsavi::STRF *stream_format, const nsavi::STRD *stream_data, wchar_t *buf, size_t buf_cch);
+
+ // for longer description, e.g. "H.264 320x240" or "PCM 16bit stereo 44.1khz"
+ int GetAudioDescription(const nsavi::AVIH *avi_header, const nsavi::STRH *stream_header, const nsavi::STRF *stream_format, const nsavi::STRD *stream_data, wchar_t *buf, size_t buf_cch);
+ int GetVideoDescription(const nsavi::AVIH *avi_header, const nsavi::STRH *stream_header, const nsavi::STRF *stream_format, const nsavi::STRD *stream_data, wchar_t *buf, size_t buf_cch);
+ DISPATCH_CODES
+ {
+ CREATE_AUDIO_DECODER = 0,
+ CREATE_VIDEO_DECODER = 1,
+ GET_AUDIO_CODEC = 2,
+ GET_VIDEO_CODEC = 3,
+ GET_AUDIO_DESCRIPTION = 4,
+ GET_VIDEO_DESCRIPTION = 5,
+ };
+};
+
+inline int svc_avidecoder::CreateAudioDecoder(const nsavi::AVIH *avi_header, const nsavi::STRH *stream_header, const nsavi::STRF *stream_format, const nsavi::STRD *stream_data, unsigned int preferred_bits, unsigned int max_channels, bool floating_point, ifc_aviaudiodecoder **decoder)
+{
+ return _call(CREATE_AUDIO_DECODER, (int)CREATEDECODER_NOT_MINE, avi_header, stream_header, stream_format, stream_data, preferred_bits, max_channels, floating_point, decoder);
+}
+
+inline int svc_avidecoder::CreateVideoDecoder(const nsavi::AVIH *avi_header, const nsavi::STRH *stream_header, const nsavi::STRF *stream_format, const nsavi::STRD *stream_data, ifc_avivideodecoder **decoder)
+{
+ return _call(CREATE_VIDEO_DECODER, (int)CREATEDECODER_NOT_MINE, avi_header, stream_header, stream_format, stream_data, decoder);
+}
+
+inline int svc_avidecoder::GetAudioCodec(const nsavi::AVIH *avi_header, const nsavi::STRH *stream_header, const nsavi::STRF *stream_format, const nsavi::STRD *stream_data, wchar_t *buf, size_t buf_cch)
+{
+ return _call(GET_AUDIO_CODEC, (int)CREATEDECODER_NOT_MINE, avi_header, stream_header, stream_format, stream_data, buf, buf_cch);
+}
+
+inline int svc_avidecoder::GetVideoCodec(const nsavi::AVIH *avi_header, const nsavi::STRH *stream_header, const nsavi::STRF *stream_format, const nsavi::STRD *stream_data, wchar_t *buf, size_t buf_cch)
+{
+ return _call(GET_VIDEO_CODEC, (int)CREATEDECODER_NOT_MINE, avi_header, stream_header, stream_format, stream_data, buf, buf_cch);
+}
+
+inline int svc_avidecoder::GetAudioDescription(const nsavi::AVIH *avi_header, const nsavi::STRH *stream_header, const nsavi::STRF *stream_format, const nsavi::STRD *stream_data, wchar_t *buf, size_t buf_cch)
+{
+ return _call(GET_AUDIO_DESCRIPTION, (int)CREATEDECODER_NOT_MINE, avi_header, stream_header, stream_format, stream_data, buf, buf_cch);
+}
+
+inline int svc_avidecoder::GetVideoDescription(const nsavi::AVIH *avi_header, const nsavi::STRH *stream_header, const nsavi::STRF *stream_format, const nsavi::STRD *stream_data, wchar_t *buf, size_t buf_cch)
+{
+ return _call(GET_VIDEO_DESCRIPTION, (int)CREATEDECODER_NOT_MINE, avi_header, stream_header, stream_format, stream_data, buf, buf_cch);
+}
diff --git a/Src/Plugins/Input/in_avi/version.rc2 b/Src/Plugins/Input/in_avi/version.rc2
new file mode 100644
index 00000000..eec411af
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,78,0,0
+ PRODUCTVERSION WINAMP_PRODUCTVER
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Winamp SA"
+ VALUE "FileDescription", "Winamp Input Plug-in"
+ VALUE "FileVersion", "0,78,0,0"
+ VALUE "InternalName", "Nullsoft AVI Demuxer"
+ VALUE "LegalCopyright", "Copyright © 2009-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "in_avi.dll"
+ VALUE "ProductName", "Winamp"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/Src/Plugins/Input/in_avi/win32_avi_reader.cpp b/Src/Plugins/Input/in_avi/win32_avi_reader.cpp
new file mode 100644
index 00000000..cc716d86
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/win32_avi_reader.cpp
@@ -0,0 +1,174 @@
+#include "win32_avi_reader.h"
+#include <strsafe.h>
+
+const int _BUFFER_SIZE_ = 16384;
+
+AVIReaderWin32::AVIReaderWin32()
+{
+ hFile = 0;
+
+ _ring_buffer.reserve( _BUFFER_SIZE_ );
+
+ end_of_file = false;
+
+ position.QuadPart = 0;
+ local_filename = 0;
+}
+
+AVIReaderWin32::~AVIReaderWin32()
+{
+ free( local_filename );
+}
+
+void AVIReaderWin32::Close()
+{
+ if ( hFile && hFile != INVALID_HANDLE_VALUE )
+ {
+ //CancelIo(hFile);
+ CloseHandle( hFile );
+ }
+}
+
+uint64_t AVIReaderWin32::GetContentLength()
+{
+ LARGE_INTEGER position;
+ position.QuadPart = 0;
+ position.LowPart = GetFileSize( hFile, (LPDWORD)&position.HighPart );
+
+ if ( position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR )
+ return 0;
+ else
+ return position.QuadPart;
+}
+
+void AVIReaderWin32::GetFilename( wchar_t *fn, size_t len )
+{
+ StringCchCopyW( fn, len, local_filename );
+}
+
+int AVIReaderWin32::Open( const wchar_t *filename )
+{
+ free( local_filename );
+ local_filename = _wcsdup( filename );
+
+ hFile = CreateFile( filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0 );
+ if ( hFile == INVALID_HANDLE_VALUE )
+ return nsavi::READ_NOT_FOUND;
+
+ return nsavi::READ_OK;
+}
+
+/* used by RingBuffer::fill() */
+size_t AVIReaderWin32::Read( void *dest, size_t len )
+{
+ // TODO: use overlapped I/O so can we wait on the read simultaneously with the killswitch and seek_event
+ DWORD bytes_read = 0;
+ if ( ReadFile( hFile, dest, (DWORD)len, &bytes_read, NULL ) && bytes_read != len )
+ end_of_file = true;
+
+ return bytes_read;
+}
+
+int AVIReaderWin32::Read( void *p_read_buffer, uint32_t read_length, uint32_t *bytes_read )
+{
+ if ( end_of_file && _ring_buffer.empty() )
+ return nsavi::READ_EOF;
+
+ size_t total_bytes_read = 0;
+
+ while ( read_length && !( end_of_file && _ring_buffer.empty() ) )
+ {
+ // read what we can from the buffer
+ size_t bytes_read = _ring_buffer.read( p_read_buffer, read_length );
+ p_read_buffer = (uint8_t *)p_read_buffer + bytes_read;
+ read_length -= (uint32_t)bytes_read;
+ total_bytes_read += bytes_read;
+ position.QuadPart += bytes_read;
+
+ if ( read_length > _BUFFER_SIZE_ )
+ {
+ // read directly from the file if we have a large read
+ bytes_read = Read( p_read_buffer, read_length );
+ p_read_buffer = (uint8_t *)p_read_buffer + bytes_read;
+ read_length -= (uint32_t)bytes_read;
+ total_bytes_read += bytes_read;
+ position.QuadPart += bytes_read;
+ }
+ else
+ {
+ // refill buffer if necessary
+ _ring_buffer.fill( this, _BUFFER_SIZE_ );
+ }
+ }
+
+ *bytes_read = (uint32_t)total_bytes_read;
+
+ return nsavi::READ_OK;
+}
+
+int AVIReaderWin32::Peek( void *read_buffer, uint32_t read_length, uint32_t *bytes_read )
+{
+ if ( end_of_file && _ring_buffer.empty() )
+ return nsavi::READ_EOF;
+
+ // refill buffer if necessary
+ if ( _ring_buffer.size() < read_length )
+ _ring_buffer.fill( this, _BUFFER_SIZE_ );
+
+ *bytes_read = (uint32_t)_ring_buffer.peek( read_buffer, read_length );
+
+ return nsavi::READ_OK;
+}
+
+static LONGLONG Seek64( HANDLE hf, __int64 distance, DWORD MoveMethod )
+{
+ LARGE_INTEGER li;
+ li.QuadPart = distance;
+ li.LowPart = SetFilePointer( hf, li.LowPart, &li.HighPart, MoveMethod );
+ if ( li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR )
+ {
+ li.QuadPart = -1;
+ }
+
+ return li.QuadPart;
+}
+
+int AVIReaderWin32::Seek( uint64_t new_position )
+{
+ _ring_buffer.clear();
+
+ position.QuadPart = Seek64( hFile, new_position, SEEK_SET );
+ end_of_file = ( position.QuadPart != new_position );
+
+ return nsavi::READ_OK;
+}
+
+uint64_t AVIReaderWin32::Tell()
+{
+ return position.QuadPart;
+}
+
+int AVIReaderWin32::Skip( uint32_t skip_bytes )
+{
+ if ( end_of_file && _ring_buffer.empty() )
+ return nsavi::READ_EOF;
+
+ if ( skip_bytes < _ring_buffer.size() )
+ {
+ _ring_buffer.advance( skip_bytes );
+
+ position.QuadPart += skip_bytes;
+
+ return nsavi::READ_OK;
+ }
+ else
+ {
+ return Seek( position.QuadPart + skip_bytes );
+ }
+}
+
+void AVIReaderWin32::OverlappedHint( uint32_t read_length )
+{
+ if ( read_length > _ring_buffer.size() )
+ _ring_buffer.fill( this, _BUFFER_SIZE_ );
+}
diff --git a/Src/Plugins/Input/in_avi/win32_avi_reader.h b/Src/Plugins/Input/in_avi/win32_avi_reader.h
new file mode 100644
index 00000000..f853fad8
--- /dev/null
+++ b/Src/Plugins/Input/in_avi/win32_avi_reader.h
@@ -0,0 +1,36 @@
+#pragma once
+#include "../nsavi/avi_reader.h"
+#include "../nu/ringbuffer.h"
+
+class AVIReaderWin32 : public nsavi::avi_reader, private Filler
+{
+public:
+ AVIReaderWin32();
+ ~AVIReaderWin32();
+ int Open(const wchar_t *filename);
+ void Close();
+
+/* avi_reader implementation */
+ uint64_t GetContentLength();
+ int Seek(uint64_t position);
+
+private:
+ int Read(void *p_read_buffer, uint32_t read_length, uint32_t *bytes_read);
+ int Peek(void *p_read_buffer, uint32_t read_length, uint32_t *bytes_read);
+ void OverlappedHint(uint32_t read_length);
+ uint64_t Tell();
+ int Skip(uint32_t skip_bytes);
+ void GetFilename(wchar_t *fn, size_t len);
+
+ /* internal helpers */
+ void DoRead();
+/* RingBuffer Filler implementation */
+ size_t Read(void *dest, size_t len);
+
+ HANDLE hFile; // i hate hungarian notation, but calling this hFile is a force of habit :)
+ RingBuffer _ring_buffer;
+ bool end_of_file;
+ LARGE_INTEGER position; // since we read ahead, we need to keep track of this separately
+ wchar_t *local_filename;
+
+}; \ No newline at end of file