Initial community commit

This commit is contained in:
Jef 2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit fc06254474
16440 changed files with 4239995 additions and 2 deletions

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

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

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

View 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);
};

View 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();
}
*/

View 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);

View 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);
}

View 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();

View 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();
}
}

View 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];
};

View 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);
}

View 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);
}

View 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

View 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

View 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>

View 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>

View file

@ -0,0 +1,4 @@
#pragma once
#include "svc_avidecoder.h"
#include "ifc_aviaudiodecoder.h"
#include "ifc_avivideodecoder.h"

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

View 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);

View file

@ -0,0 +1,3 @@
#pragma once
extern int audio_stream_num, video_stream_num;

View 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

View 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);
}

View 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

View 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_ );
}

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