Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
138
Src/Plugins/Input/in_avi/ExtendedFileInfo.cpp
Normal file
138
Src/Plugins/Input/in_avi/ExtendedFileInfo.cpp
Normal file
|
@ -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;
|
||||
}
|
390
Src/Plugins/Input/in_avi/InfoDialog.cpp
Normal file
390
Src/Plugins/Input/in_avi/InfoDialog.cpp
Normal file
|
@ -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;
|
||||
}
|
964
Src/Plugins/Input/in_avi/PlayThread.cpp
Normal file
964
Src/Plugins/Input/in_avi/PlayThread.cpp
Normal file
|
@ -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;
|
||||
}
|
32
Src/Plugins/Input/in_avi/StreamSelector.h
Normal file
32
Src/Plugins/Input/in_avi/StreamSelector.h
Normal file
|
@ -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);
|
||||
};
|
428
Src/Plugins/Input/in_avi/VideoThread.cpp
Normal file
428
Src/Plugins/Input/in_avi/VideoThread.cpp
Normal file
|
@ -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();
|
||||
}
|
||||
*/
|
12
Src/Plugins/Input/in_avi/VideoThread.h
Normal file
12
Src/Plugins/Input/in_avi/VideoThread.h
Normal file
|
@ -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);
|
50
Src/Plugins/Input/in_avi/api.cpp
Normal file
50
Src/Plugins/Input/in_avi/api.cpp
Normal file
|
@ -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);
|
||||
}
|
15
Src/Plugins/Input/in_avi/api__in_avi.h
Normal file
15
Src/Plugins/Input/in_avi/api__in_avi.h
Normal file
|
@ -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();
|
255
Src/Plugins/Input/in_avi/http_avi_reader.cpp
Normal file
255
Src/Plugins/Input/in_avi/http_avi_reader.cpp
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
31
Src/Plugins/Input/in_avi/http_avi_reader.h
Normal file
31
Src/Plugins/Input/in_avi/http_avi_reader.h
Normal file
|
@ -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];
|
||||
};
|
67
Src/Plugins/Input/in_avi/ifc_aviaudiodecoder.h
Normal file
67
Src/Plugins/Input/in_avi/ifc_aviaudiodecoder.h
Normal file
|
@ -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);
|
||||
}
|
77
Src/Plugins/Input/in_avi/ifc_avivideodecoder.h
Normal file
77
Src/Plugins/Input/in_avi/ifc_avivideodecoder.h
Normal file
|
@ -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);
|
||||
}
|
159
Src/Plugins/Input/in_avi/in_avi.rc
Normal file
159
Src/Plugins/Input/in_avi/in_avi.rc
Normal file
|
@ -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
|
||||
|
31
Src/Plugins/Input/in_avi/in_avi.sln
Normal file
31
Src/Plugins/Input/in_avi/in_avi.sln
Normal file
|
@ -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
|
284
Src/Plugins/Input/in_avi/in_avi.vcxproj
Normal file
284
Src/Plugins/Input/in_avi/in_avi.vcxproj
Normal file
|
@ -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>
|
158
Src/Plugins/Input/in_avi/in_avi.vcxproj.filters
Normal file
158
Src/Plugins/Input/in_avi/in_avi.vcxproj.filters
Normal file
|
@ -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>
|
4
Src/Plugins/Input/in_avi/interfaces.h
Normal file
4
Src/Plugins/Input/in_avi/interfaces.h
Normal file
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
#include "svc_avidecoder.h"
|
||||
#include "ifc_aviaudiodecoder.h"
|
||||
#include "ifc_avivideodecoder.h"
|
330
Src/Plugins/Input/in_avi/main.cpp
Normal file
330
Src/Plugins/Input/in_avi/main.cpp
Normal file
|
@ -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;
|
||||
}
|
19
Src/Plugins/Input/in_avi/main.h
Normal file
19
Src/Plugins/Input/in_avi/main.h
Normal file
|
@ -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);
|
3
Src/Plugins/Input/in_avi/player.h
Normal file
3
Src/Plugins/Input/in_avi/player.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
extern int audio_stream_num, video_stream_num;
|
50
Src/Plugins/Input/in_avi/resource.h
Normal file
50
Src/Plugins/Input/in_avi/resource.h
Normal file
|
@ -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
|
74
Src/Plugins/Input/in_avi/svc_avidecoder.h
Normal file
74
Src/Plugins/Input/in_avi/svc_avidecoder.h
Normal file
|
@ -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);
|
||||
}
|
39
Src/Plugins/Input/in_avi/version.rc2
Normal file
39
Src/Plugins/Input/in_avi/version.rc2
Normal file
|
@ -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
|
174
Src/Plugins/Input/in_avi/win32_avi_reader.cpp
Normal file
174
Src/Plugins/Input/in_avi/win32_avi_reader.cpp
Normal file
|
@ -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_ );
|
||||
}
|
36
Src/Plugins/Input/in_avi/win32_avi_reader.h
Normal file
36
Src/Plugins/Input/in_avi/win32_avi_reader.h
Normal file
|
@ -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;
|
||||
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue