Initial community commit

This commit is contained in:
Jef
2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit 20d28e80a5
16810 changed files with 4640254 additions and 2 deletions

View File

@ -0,0 +1,80 @@
#include <bfc/platform/types.h>
#include <windows.h>
#include "api__in_mkv.h"
#include "MKVInfo.h"
#include <strsafe.h>
#include "resource.h"
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>3 && L'.' == fn[len-4]))
{
p = &fn[len - 3];
if (!_wcsicmp(p, L"MKV") && S_OK == StringCchCopyW(dest, destlen, WASABI_API_LNGSTRINGW(IDS_FAMILY_STRING))) return 1;
}
else if (len>4 && L'.' == fn[len-5])
{
p = &fn[len - 4];
if (!_wcsicmp(p, L"webm") && S_OK == StringCchCopyW(dest, destlen, WASABI_API_LNGSTRINGW(IDS_FAMILY_STRING_WEBM))) return 1;
}
return 0;
}
else
{
MKVInfo info;
dest[0]=0;
if (!_stricmp(data, "length"))
{
if (info.Open(fn))
StringCchPrintf(dest, destlen, L"%d", info.GetLengthMilliseconds());
}
else if (!_stricmp(data, "bitrate"))
{
if (info.Open(fn))
StringCchPrintf(dest, destlen, L"%d", info.GetBitrate());
}
else if (!_stricmp(data, "title"))
{
return 1;
}
else if (!_stricmp(data, "width"))
{
if (info.Open(fn))
{
int width;
if (info.GetWidth(width))
StringCchPrintf(dest, destlen, L"%d", width);
}
}
else if (!_stricmp(data, "height"))
{
if (info.Open(fn))
{
int height;
if (info.GetHeight(height))
StringCchPrintf(dest, destlen, L"%d", height);
}
}
else
return 0;
return 1;
}
return 0;
}

View File

@ -0,0 +1,247 @@
#include "MKVInfo.h"
#include "api__in_mkv.h"
#include "../nu/ListView.h"
#include "../nu/AutoWide.h"
#include "resource.h"
#include "main.h"
#include <strsafe.h>
enum
{
COLUMN_TRACK_TYPE = 0,
COLUMN_CODEC_NAME = 1,
COLUMN_CODEC_ID = 2,
COLUMN_DESCRIPTION = 3,
COLUMN_STREAM_NAME = 4,
COLUMN_LANGUAGE = 5,
};
#if 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(L"Field", 100);
list_view.AddCol(L"FOURCC", 75);
list_view.AddCol(L"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_UTF8));
}
}
}
return 1;
}
return 0;
}
#endif
static INT_PTR CALLBACK InfoDialog_Tracks(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
MKVInfo *metadata = (MKVInfo *)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);
list_view.AddCol(WASABI_API_LNGSTRINGW(IDS_COLUMN_LANGUAGE), 50);
const nsmkv::Tracks *tracks = metadata->GetTracks();
if (tracks)
{
const nsmkv::TrackEntry *track_entry;
size_t i=0;
while (track_entry = tracks->EnumTrack(i++))
{
int n;
switch(track_entry->track_type)
{
case mkv_track_type_audio:
{
n = list_view.AppendItem(WASABI_API_LNGSTRINGW(IDS_TYPE_AUDIO), 0);
}
break;
case mkv_track_type_video:
{
n = list_view.AppendItem(WASABI_API_LNGSTRINGW(IDS_TYPE_VIDEO), 0);
}
break;
case mkv_track_type_subtitle:
{
n = list_view.AppendItem(WASABI_API_LNGSTRINGW(IDS_TYPE_SUBTITLE), 0);
}
break;
default:
{
wchar_t track_type[64] = {0};
StringCchPrintf(track_type, 64, L"%X", track_entry->track_type);
n = list_view.AppendItem(track_type, 0);
}
break;
}
if (track_entry->codec_id)
list_view.SetItemText(n, COLUMN_CODEC_ID, AutoWide(track_entry->codec_id, CP_UTF8));
if (track_entry->codec_name)
{
list_view.SetItemText(n, COLUMN_CODEC_NAME, AutoWide(track_entry->codec_name, CP_UTF8));
}
else
{
// TODO: enumerate through a list of known codecs
if (track_entry->codec_id)
list_view.SetItemText(n, COLUMN_CODEC_NAME, AutoWide(track_entry->codec_id, CP_UTF8));
}
if (track_entry->name)
{
list_view.SetItemText(n, COLUMN_STREAM_NAME, AutoWide(track_entry->name, CP_UTF8));
}
if (track_entry->language && stricmp(track_entry->language, "und"))
{
list_view.SetItemText(n, COLUMN_LANGUAGE, AutoWide(track_entry->language, 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
{
MKVInfo *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_Tracks, (LPARAM)context->metadata);
break;
case 1:
// context->active_tab = WASABI_API_CREATEDIALOGPARAMW(IDD_TRACKS, hwndDlg, InfoDialog_Metadata, (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 = (MKVInfo *)lParam;
context->active_tab = 0;
SetWindowLongPtr(hwndDlg,GWLP_USERDATA, (LPARAM)context);
TCITEMW tie;
tie.mask = TCIF_TEXT;
tie.pszText = WASABI_API_LNGSTRINGW(IDS_TAB_TRACKS);
SendMessageW(hwndTab, TCM_INSERTITEMW, 0, (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,115 @@
#include "MKVDuration.h"
#include "../nsmkv/global_elements.h"
#include "../nsmkv/read.h"
#include "../nsmkv/segment.h"
#include "../nsmkv/file_mkv_reader.h"
MKVDuration::MKVDuration()
{
segment_info_found = false;
content_length = 0;
}
bool MKVDuration::Open(const wchar_t *filename)
{
FILE *f = _wfopen(filename, L"rb");
if (!f)
return false;
MKVReaderFILE reader(f);
content_length = reader.GetContentLength();
ebml_node node;
while (!segment_info_found)
{
if (read_ebml_node(&reader, &node) == 0)
break;
switch(node.id)
{
case mkv_header:
if (nsmkv::ReadHeader(&reader, node.size, header) == 0)
{
return false;
}
break;
case mkv_segment:
if (ReadSegment(&reader, node.size) == 0)
{
return false;
}
break;
default:
nsmkv::SkipNode(&reader, node.id, node.size);
}
}
return segment_info_found;
}
uint64_t MKVDuration::ReadSegment(nsmkv::MKVReader *reader, uint64_t size)
{
uint64_t total_bytes_read=0;
while (size && !segment_info_found)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_segment_segmentinfo:
{
printf("SegmentInfo\n");
if (ReadSegmentInfo(reader, node.size, segment_info) == 0)
return 0;
segment_info_found=true;
}
break;
default:
if (nsmkv::SkipNode(reader, node.id, node.size) == 0)
return 0;
}
}
return total_bytes_read;
}
int MKVDuration::GetLengthMilliseconds()
{
if (!segment_info_found)
return -1000;
else
return segment_info.GetDurationMilliseconds();
}
const char *MKVDuration::GetTitle()
{
return segment_info.title;
}
int MKVDuration::GetBitrate()
{
if (segment_info_found)
{
int time_ms = segment_info.GetDurationMilliseconds();
if (time_ms)
return (int) (8ULL * content_length / (uint64_t)time_ms);
}
return 0;
}

View File

@ -0,0 +1,20 @@
#pragma once
#include "../nsmkv/header.h"
#include "../nsmkv/segmentinfo.h"
// parses only enough information to determine the file duration
class MKVDuration
{
public:
MKVDuration();
bool Open(const wchar_t *filename);
int GetLengthMilliseconds();
const char *GetTitle();
int GetBitrate();
private:
uint64_t ReadSegment(nsmkv::MKVReader *reader, uint64_t size);
bool segment_info_found;
nsmkv::Header header;
nsmkv::SegmentInfo segment_info;
uint64_t content_length;
};

View File

@ -0,0 +1,168 @@
#include "MKVInfo.h"
#include "../nsmkv/global_elements.h"
#include "../nsmkv/read.h"
#include "../nsmkv/segment.h"
#include "../nsmkv/tracks.h"
#include "../nsmkv/file_mkv_reader.h"
MKVInfo::MKVInfo()
{
segment_info_found = false;
content_length = 0;
tracks_found = false;
}
bool MKVInfo::Open(const wchar_t *filename)
{
FILE *f = _wfopen(filename, L"rb");
if (!f)
return false;
MKVReaderFILE reader(f);
content_length = reader.GetContentLength();
ebml_node node;
while (!segment_info_found)
{
if (read_ebml_node(&reader, &node) == 0)
break;
switch(node.id)
{
case mkv_header:
if (nsmkv::ReadHeader(&reader, node.size, header) == 0)
{
return false;
}
break;
case mkv_segment:
if (ReadSegment(&reader, node.size) == 0)
{
return false;
}
break;
default:
nsmkv::SkipNode(&reader, node.id, node.size);
}
}
return segment_info_found;
}
uint64_t MKVInfo::ReadSegment(nsmkv::MKVReader *reader, uint64_t size)
{
uint64_t total_bytes_read=0;
while (size && (!segment_info_found || !tracks_found))
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(reader, &node);
if (bytes_read == 0)
return 0;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return 0;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return 0;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_segment_segmentinfo:
{
if (ReadSegmentInfo(reader, node.size, segment_info) == 0)
return 0;
segment_info_found=true;
}
break;
case mkv_segment_tracks:
{
if (ReadTracks(reader, node.size, tracks) == 0)
return 0;
tracks_found=true;
}
break;
default:
if (nsmkv::SkipNode(reader, node.id, node.size) == 0)
return 0;
}
}
return total_bytes_read;
}
int MKVInfo::GetLengthMilliseconds()
{
if (!segment_info_found)
return -1000;
else
return segment_info.GetDurationMilliseconds();
}
const char *MKVInfo::GetTitle()
{
return segment_info.title;
}
int MKVInfo::GetBitrate()
{
if (segment_info_found)
{
int time_ms = segment_info.GetDurationMilliseconds();
if (time_ms)
return (int) (8ULL * content_length / (uint64_t)time_ms);
}
return 0;
}
bool MKVInfo::GetHeight(int &height)
{
if (tracks_found)
{
size_t i=0;
const nsmkv::TrackEntry *track=0;
while (track = tracks.EnumTrack(i++))
{
if (track->track_type == mkv_track_type_video)
{
height = (int)track->video.pixel_height;
return true;
}
}
}
return false;
}
bool MKVInfo::GetWidth(int &width)
{
if (tracks_found)
{
size_t i=0;
const nsmkv::TrackEntry *track=0;
while (track = tracks.EnumTrack(i++))
{
if (track->track_type == mkv_track_type_video)
{
width = (int)track->video.pixel_width;
return true;
}
}
}
return false;
}
const nsmkv::Tracks *MKVInfo::GetTracks()
{
if (tracks_found)
return &tracks;
else
return 0;
}

View File

@ -0,0 +1,26 @@
#pragma once
#include "../nsmkv/header.h"
#include "../nsmkv/segmentinfo.h"
#include "../nsmkv/tracks.h"
// parses only enough information to use in GetExtendedFileInfo
class MKVInfo
{
public:
MKVInfo();
bool Open(const wchar_t *filename);
int GetLengthMilliseconds();
const char *GetTitle();
int GetBitrate();
bool GetHeight(int &height);
bool GetWidth(int &width);
const nsmkv::Tracks *GetTracks();
private:
uint64_t ReadSegment(nsmkv::MKVReader *reader, uint64_t size);
bool segment_info_found;
bool tracks_found;
nsmkv::Header header;
nsmkv::SegmentInfo segment_info;
nsmkv::Tracks tracks;
uint64_t content_length;
};

View File

@ -0,0 +1,138 @@
#pragma once
#include <windows.h>
#include <bfc/platform/types.h>
// nsmkv stuff
#include "../nsmkv/Cluster.h"
#include "../nsmkv/header.h"
#include "../nsmkv/SeekTable.h"
#include "../nsmkv/SegmentInfo.h"
#include "../nsmkv/Tracks.h"
#include "../nsmkv/Cues.h"
#include "../nsmkv/Attachments.h"
#include "../nsmkv/mkv_reader.h"
#include "ifc_mkvvideodecoder.h"
#include "ifc_mkvaudiodecoder.h"
#include "../nu/AutoLock.h"
#include "../nu/AudioOutput.h"
class MKVPlayer
{
public:
MKVPlayer(const wchar_t *_filename);
~MKVPlayer();
DWORD CALLBACK ThreadFunction();
DWORD CALLBACK VideoThreadFunction();
void Kill();
void Seek(int seek_pos);
int GetOutputTime() const;
private:
// subfunctions to make the code cleaner
// they all have "side effects", which is a coding style I don't like
// but it makes it easier to read & modify
// TODO: move these to step, and have a state variable to know where we are
bool ParseHeader(); // on completion, header will be filled, file pointer will be at next level 0 node
bool FindSegment(); // on completion, segment_position will be calculated, file pointer will be at first level 1 node under segment
// file position is restored at end of function
bool FindCues();
int ParseCluster(nsmkv::MKVReader *stream, uint64_t size, uint64_t *track_numbers, size_t track_numbers_len);
enum
{
// values that you can return from OnXXXX()
MKV_CONTINUE = 0, // continue processing
MKV_ABORT = 1, // abort parsing gracefully (maybe 'stop' was pressed)
MKV_STOP = 2, // stop parsing completely - usually returned when mkv version is too new or codecs not supported
// values returned from errors within the Step() function itself
MKV_EOF = 3, // end of file
MKV_ERROR = 4, // parsing error
};
int OnHeader(const nsmkv::Header &header);
void OnSegmentInfo(const nsmkv::SegmentInfo &segment_info);
int OnTracks(const nsmkv::Tracks &tracks);
int OnBlock(const nsmkv::Cluster &cluster, const nsmkv::Block &block);
int OnFirstCluster(uint64_t position);
int OnAudio(const nsmkv::Cluster &cluster, const nsmkv::BlockBinary &binary);
int OnVideo(const nsmkv::Cluster &cluster, const nsmkv::BlockBinary &binary);
int OutputPictures(uint64_t default_timestamp);
/* start calling with cluster_number = 0 and block_number = 0 (or whatever appropriate based on CuePoints when seeking
will return 0 on success, 1 on EOF and -1 on failure
*/
int GetBlock(nsmkv::MKVReader *stream, uint64_t track_number, nsmkv::BlockBinary &binary, const nsmkv::Cluster **cluster, size_t &cluster_number, size_t &block_number);
static void CALLBACK SeekAPC(ULONG_PTR data);
int Step(nsmkv::MKVReader *stream, uint64_t *track_numbers, size_t track_numbers_len); // only gives you block data for the passed track number
private:
/* nsmkv internal implementation */
nsmkv::Header header;
uint64_t segment_position; // position of the start of the first level 1 element in the segment(for SeekHead relative positions)
uint64_t segment_size; // size of that segment
nsmkv::SeekTable seek_table;
nsmkv::SegmentInfo segment_info;
nsmkv::Tracks tracks;
nsmkv::Clusters clusters;
nsmkv::Cues cues;
nsmkv::Attachments attachments;
bool cues_searched;
bool first_cluster_found;
/* player implementation */
nsmkv::MKVReader *main_reader; // also gets used as audio_stream
Nullsoft::Utility::LockGuard cluster_guard;
HANDLE killswitch, seek_event;
wchar_t *filename;
volatile int m_needseek;
/* Audio */
ifc_mkvaudiodecoder *audio_decoder;
bool audio_opened;
uint64_t audio_track_num;
uint8_t audio_buffer[65536]; // TODO: dynamically allocate from OutputFrameSize
size_t audio_output_len;
size_t audio_buffered;
int audio_first_timestamp;
enum FlushState
{
FLUSH_NONE=0,
FLUSH_START=1,
FLUSH_SEEK=2,
};
FlushState audio_flushing;
unsigned int audio_bitrate;
/* Video */
ifc_mkvvideodecoder *video_decoder;
const nsmkv::TrackEntry *video_track_entry;
bool video_opened;
HANDLE video_thread;
double video_timecode_scale;
uint64_t video_track_num;
uint64_t video_cluster_position;
nsmkv::MKVReader *video_stream;
HANDLE video_break, video_flush, video_flush_done, video_resume, video_ready;
unsigned int video_bitrate;
int consecutive_early_frames;
/* AudioOutput implementation */
class MKVWait
{
public:
void Wait_SetEvents(HANDLE killswitch, HANDLE seek_event);
protected:
int WaitOrAbort(int time_in_ms);
private:
HANDLE handles[2];
};
nu::AudioOutput<MKVWait> audio_output;
};

View File

@ -0,0 +1,902 @@
#include "MKVPlayer.h"
#include "api__in_mkv.h"
#include "main.h"
#include "../Winamp/wa_ipc.h"
#include "../nsmkv/nsmkv.h"
#include "../nu/AutoLock.h"
#include "../nu/ns_wc.h"
#include "../nu/AutoWide.h"
#include "../nu/AudioOutput.h"
#include "ifc_mkvaudiodecoder.h"
#include "svc_mkvdecoder.h"
#include "ifc_mkvvideodecoder.h"
#include "../nsmkv/file_mkv_reader.h"
#include <api/service/waservicefactory.h>
#include <api/service/services.h>
#include <windows.h>
#include <strsafe.h>
// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
static const GUID playbackConfigGroupGUID =
{
0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf }
};
IVideoOutput *videoOutput = 0;
/* benski>
TODO: keep track of "fully parsed position" we don't have to always start over at segment_position
TODO: if we have multiple audio or video tracks, do that weird winamp interface for it
*/
void MKVPlayer::MKVWait::Wait_SetEvents(HANDLE killswitch, HANDLE seek_event)
{
handles[0]=killswitch;
handles[1]=seek_event;
}
int MKVPlayer::MKVWait::WaitOrAbort(int time_in_ms)
{
switch(WaitForMultipleObjects(2, handles, FALSE, 55))
{
case WAIT_TIMEOUT: // all good, wait successful
return 0;
case WAIT_OBJECT_0: // killswitch
return MKVPlayer::MKV_STOP;
case WAIT_OBJECT_0+1: // seek event
return MKVPlayer::MKV_ABORT;
default: // some OS error?
return MKVPlayer::MKV_ERROR;
}
}
MKVPlayer::MKVPlayer(const wchar_t *_filename) : audio_output(&plugin)
{
first_cluster_found = false;
cues_searched = false;
segment_position=0;
segment_size = 0;
filename = _wcsdup(_filename);
m_needseek = -1;
audio_decoder=0;
audio_track_num=0;
audio_output_len = 65536;
audio_opened=false;
audio_flushing=FLUSH_START;
audio_first_timestamp=0;
audio_buffered=0;
audio_bitrate=0;
video_decoder=0;
video_track_num=0;
video_opened = false;
video_stream = 0;
video_thread = 0;
video_timecode_scale = 0;
video_cluster_position = 0;
video_track_entry = 0;
video_bitrate = 0;
consecutive_early_frames = 0;
if (!videoOutput)
videoOutput = (IVideoOutput *)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GET_IVIDEOOUTPUT);
killswitch = CreateEvent(NULL, TRUE, FALSE, NULL);
seek_event = CreateEvent(NULL, TRUE, FALSE, NULL);
/* video events */
video_break = CreateEvent(NULL, TRUE, FALSE, NULL);
video_flush_done = CreateEvent(NULL, FALSE, FALSE, NULL);
video_flush = CreateEvent(NULL, TRUE, FALSE, NULL);
video_resume = CreateEvent(NULL, TRUE, FALSE, NULL);
video_ready = CreateEvent(NULL, TRUE, FALSE, NULL);
audio_output.Wait_SetEvents(killswitch, seek_event);
}
MKVPlayer::~MKVPlayer() {
free(filename);
CloseHandle(killswitch);
CloseHandle(seek_event);
CloseHandle(video_break);
CloseHandle(video_flush_done);
CloseHandle(video_flush);
CloseHandle(video_resume);
CloseHandle(video_ready);
if (audio_decoder) {
audio_decoder->Close();
}
delete main_reader;
}
void MKVPlayer::Kill()
{
SetEvent(killswitch);
if (video_thread)
WaitForSingleObject(video_thread, INFINITE);
video_thread = 0;
if (video_decoder)
video_decoder->Close();
video_decoder=0;
}
void MKVPlayer::Seek(int seek_pos)
{
m_needseek = seek_pos;
SetEvent(seek_event);
}
int MKVPlayer::GetOutputTime() const
{
if (m_needseek != -1)
return m_needseek;
else
return plugin.outMod->GetOutputTime() + audio_first_timestamp;
}
DWORD CALLBACK MKVThread(LPVOID param)
{
MKVPlayer *player = (MKVPlayer *)param;
DWORD ret = player->ThreadFunction();
return ret;
}
bool MKVPlayer::FindCues()
{
if (cues_searched)
return true;
uint64_t original_position = main_reader->Tell();
// first, let's try the seek table
uint64_t cues_position = 0;
if (seek_table.GetEntry(mkv_segment_cues, &cues_position))
{
main_reader->Seek(cues_position+segment_position);
ebml_node node;
if (read_ebml_node(main_reader, &node) == 0)
return false;
if (node.id == mkv_segment_cues) // great success!
{
if (nsmkv::ReadCues(main_reader, node.size, cues) == 0)
return false;
cues_searched=true;
main_reader->Seek(original_position);
return true;
}
}
main_reader->Seek(segment_position); // TODO: keep track of how far Step() has gotten so we don't have to start from scratch
/* --- TODO: make this block into a function in nsmkv --- */
while (1) // TODO: key off segment size to make sure we don't overread
{
uint64_t this_position = main_reader->Tell();
ebml_node node;
if (read_ebml_node(main_reader, &node) == 0)
return false;
if (node.id != mkv_void)
{
nsmkv::SeekEntry seek_entry(node.id, this_position-segment_position);
seek_table.AddEntry(seek_entry, nsmkv::SeekTable::ADDENTRY_FOUND);
}
if (node.id == mkv_segment_cues)
{
if (nsmkv::ReadCues(main_reader, node.size, cues) == 0)
return false;
break;
}
else
{
main_reader->Skip(node.size);
}
}
/* ------ */
cues_searched=true;
main_reader->Seek(original_position);
return true;
}
bool MKVPlayer::ParseHeader()
{
ebml_node node;
if (read_ebml_node(main_reader, &node) == 0)
return false;
if (node.id != mkv_header)
return false;
if (nsmkv::ReadHeader(main_reader, node.size, header) == 0)
return false;
if (OnHeader(header) != MKV_CONTINUE)
return false;
return true;
}
bool MKVPlayer::FindSegment()
{
ebml_node node;
while (segment_position == 0)
{
if (read_ebml_node(main_reader, &node) == 0)
return false;
if (node.id == mkv_segment)
{
segment_position = main_reader->Tell();
segment_size = node.size;
}
else
{
if (nsmkv::SkipNode(main_reader, node.id, node.size) == 0)
return false;
}
}
return true;
}
static ifc_mkvvideodecoder *FindVideoDecoder(const nsmkv::TrackEntry *track_entry)
{
size_t n = 0;
waServiceFactory *sf = 0;
while (sf = plugin.service->service_enumService(WaSvc::MKVDECODER, n++))
{
svc_mkvdecoder *dec = static_cast<svc_mkvdecoder *>(sf->getInterface());
if (dec)
{
ifc_mkvvideodecoder *decoder=0;
if (dec->CreateVideoDecoder(track_entry->codec_id, track_entry, &track_entry->video, &decoder) == svc_mkvdecoder::CREATEDECODER_SUCCESS)
{
sf->releaseInterface(dec);
return decoder;
}
sf->releaseInterface(dec);
}
}
return 0;
}
static ifc_mkvaudiodecoder *FindAudioDecoder(const nsmkv::TrackEntry *track_entry)
{
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::MKVDECODER, n++))
{
svc_mkvdecoder *dec = static_cast<svc_mkvdecoder *>(sf->getInterface());
if (dec)
{
ifc_mkvaudiodecoder *decoder=0;
// TODO: read from api_config!!
if (dec->CreateAudioDecoder(track_entry->codec_id, track_entry, &track_entry->audio, bits_per_sample, max_channels, false, &decoder) == svc_mkvdecoder::CREATEDECODER_SUCCESS)
{
sf->releaseInterface(dec);
return decoder;
}
sf->releaseInterface(dec);
}
}
return 0;
}
int MKVPlayer::OnAudio(const nsmkv::Cluster &cluster, const nsmkv::BlockBinary &binary)
{
if (video_decoder)
{
HANDLE handles[3] = {killswitch, seek_event, video_ready};
if (WaitForMultipleObjects(3, handles, FALSE, INFINITE) != WAIT_OBJECT_0+2)
return MKV_ABORT;
}
if (audio_flushing != FLUSH_NONE)
{
uint64_t timestamp=cluster.time_code + binary.time_code;
uint64_t timestamp_ms = segment_info.time_code_scale * timestamp / 1000000ULL;
if (audio_flushing == FLUSH_START || !audio_opened)
{
audio_first_timestamp = (int)timestamp_ms;
}
else
{
audio_first_timestamp=0;
audio_output.Flush((int)timestamp_ms);
m_needseek = -1;
}
audio_flushing=FLUSH_NONE;
}
nsmkv::LacingState lacing_state;
uint32_t i = 0;
if (nsmkv::Lacing::GetState(binary.flags, (const uint8_t *)binary.data, binary.data_size, &lacing_state))
{
const uint8_t *frame = 0;
size_t frame_len = 0;
while (nsmkv::Lacing::GetFrame(i++, (const uint8_t *)binary.data, binary.data_size, &frame, &frame_len, &lacing_state))
{
size_t decoded_size = audio_output_len-audio_buffered;
if (audio_decoder->DecodeBlock((void *)frame, frame_len, audio_buffer+audio_buffered, &decoded_size) == ifc_mkvaudiodecoder::MKV_SUCCESS)
{
decoded_size+=audio_buffered;
audio_buffered=0;
if (!audio_opened)
{
unsigned int sample_rate, channels, bps;
bool is_float;
if (audio_decoder->GetOutputProperties(&sample_rate, &channels, &bps, &is_float) == ifc_mkvaudiodecoder::MKV_SUCCESS)
{
// TODO: pass -666 for 5th param if video
if (!audio_output.Open(audio_first_timestamp, channels, sample_rate, bps, -1, -1))
{
return MKV_STOP;
}
audio_opened=true;
}
}
if (audio_opened && decoded_size)
{
int ret = audio_output.Write((char *)audio_buffer, decoded_size);
if (ret != 0)
return ret;
}
else
{
audio_buffered=decoded_size;
}
}
}
}
return MKV_CONTINUE;
}
int MKVPlayer::OutputPictures(uint64_t default_timestamp)
{
void *data=0, *decoder_data=0;
uint64_t timestamp=default_timestamp;
while (video_decoder->GetPicture(&data, &decoder_data, &timestamp) == ifc_mkvvideodecoder::MKV_SUCCESS)
{
if (!video_opened)
{
int color_format = 0;
int width = 0, height = 0;
double aspect_ratio = 1.0;
if (video_decoder->GetOutputProperties(&width, &height, &color_format, &aspect_ratio) == ifc_mkvvideodecoder::MKV_SUCCESS)
{
if (video_track_entry && video_track_entry->video.display_height && video_track_entry->video.display_width && video_track_entry->video.pixel_height && video_track_entry->video.pixel_width)
{
aspect_ratio = (double)video_track_entry->video.pixel_width / (double)video_track_entry->video.pixel_height / ((double)video_track_entry->video.display_width / (double)video_track_entry->video.display_height);
}
else
{
// winamp wants an "aspect correction value" not the true aspect ratio itself
aspect_ratio = 1.0/aspect_ratio;
}
videoOutput->extended(VIDUSER_SET_THREAD_SAFE, 1, 0);
videoOutput->open(width, height, 0, aspect_ratio, color_format);
video_opened=true;
SetEvent(video_ready);
}
}
if (video_opened)
{
uint64_t timestamp_ms;
if (video_timecode_scale == 0)
timestamp_ms = segment_info.time_code_scale * timestamp / 1000000ULL;
else
timestamp_ms = (uint64_t) (video_timecode_scale * (double)segment_info.time_code_scale * (double)timestamp / 1000000.0);
again:
int realTime = plugin.outMod->GetOutputTime() + audio_first_timestamp;
int time_diff = (int)timestamp_ms - realTime;
if (time_diff > 12 && consecutive_early_frames) // plenty of time, go ahead and turn off frame dropping
{
if (--consecutive_early_frames == 0)
video_decoder->HurryUp(0);
}
else if (time_diff < -50) // shit we're way late, start dropping frames
{
video_decoder->HurryUp(1);
consecutive_early_frames += 3;
}
if (time_diff > 3)
{
HANDLE handles[] = {killswitch, video_break};
int ret= WaitForMultipleObjects(2, handles, FALSE, (DWORD)(timestamp_ms-realTime));
if (ret != WAIT_TIMEOUT)
{
video_decoder->FreePicture(data, decoder_data);
if (ret == WAIT_OBJECT_0+1) /* second event doesn't stop stream*/
return MKV_ABORT;
return MKV_STOP;
}
goto again; // TODO: handle paused state a little better than this
}
videoOutput->draw(data);
}
video_decoder->FreePicture(data, decoder_data);
}
return MKV_CONTINUE;
}
int MKVPlayer::OnVideo(const nsmkv::Cluster &cluster, const nsmkv::BlockBinary &binary)
{
if (video_decoder)
{
nsmkv::LacingState lacing_state;
uint32_t i = 0;
if (nsmkv::Lacing::GetState(binary.flags, (const uint8_t *)binary.data, binary.data_size, &lacing_state))
{
const uint8_t *frame = 0;
size_t frame_len = 0;
while (nsmkv::Lacing::GetFrame(i++, (const uint8_t *)binary.data, binary.data_size, &frame, &frame_len, &lacing_state))
{
// matroska epic fail: laced frames don't have separate timestamps!
if (video_decoder) video_decoder->DecodeBlock(frame, frame_len, cluster.time_code+binary.time_code);
uint64_t timestamp=cluster.time_code + binary.time_code;
int ret = OutputPictures(timestamp);
if (ret != MKV_CONTINUE)
return ret;
}
}
}
return MKV_CONTINUE;
}
DWORD CALLBACK MKVPlayer::VideoThreadFunction()
{
//video_stream = _wfopen(filename, L"rb");
if (!video_stream)
return 1;
video_stream->Seek(video_cluster_position);
HANDLE handles[] = { killswitch, video_break, video_flush, video_resume };
DWORD waitTime = 0;
while (1)
{
int ret = WaitForMultipleObjects(4, handles, FALSE, waitTime);
if (ret == WAIT_TIMEOUT)
{
int ret = Step(video_stream, &video_track_num, 1);
if (ret == MKV_EOF)
{
video_decoder->EndOfStream();
OutputPictures(0);
// TODO: tell decoder about end-of-stream to flush buffers
waitTime = INFINITE;
}
else if (ret == MKV_ERROR || ret == MKV_STOP)
{
waitTime = INFINITE; // wait for killswitch
}
}
else if (ret == WAIT_OBJECT_0)
{
break;
}
else if (ret == WAIT_OBJECT_0 + 1) // video break
{
waitTime = INFINITE; // this will stop us from decoding samples for a while
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);
waitTime = 0;
SetEvent(video_flush_done);
}
else if (ret == WAIT_OBJECT_0 + 3) // resume video
{
ResetEvent(video_resume);
waitTime = 0;
SetEvent(video_flush_done);
}
}
if (videoOutput)
videoOutput->close();
delete video_stream;
video_stream=0;
return 0;
}
int MKVPlayer::OnHeader(const nsmkv::Header &header)
{
// TODO: figure out if the file is really matroska, and if we can support the ebml version
return MKV_CONTINUE;
}
void MKVPlayer::OnSegmentInfo(const nsmkv::SegmentInfo &segment_info)
{
g_duration = segment_info.GetDurationMilliseconds();
uint64_t content_length = main_reader->GetContentLength();
if (content_length && g_duration)
{
int bitrate = (int)(8ULL * content_length / (uint64_t)g_duration);
plugin.SetInfo(bitrate, -1, -1, -1);
}
}
int MKVPlayer::OnTracks(const nsmkv::Tracks &tracks)
{
wchar_t audio_info[256] = {0};
wchar_t video_info[256] = {0};
// ===== enumerate tracks and find decoders =====
size_t i=0;
const nsmkv::TrackEntry *track_entry;
while (track_entry = tracks.EnumTrack(i++))
{
if (track_entry->track_type == mkv_track_type_audio && !audio_decoder)
{
audio_decoder = FindAudioDecoder(track_entry);
if (audio_decoder)
{
MultiByteToWideCharSZ(CP_UTF8, 0, track_entry->codec_id, -1, audio_info, 256);
audio_track_num = track_entry->track_number;
}
}
else if (track_entry->track_type == mkv_track_type_video && !video_decoder)
{
video_decoder = FindVideoDecoder(track_entry);
if (video_decoder)
{
StringCbPrintfW(video_info, sizeof(video_info), L"%s %I64ux%I64u", AutoWide(track_entry->codec_id, CP_UTF8), track_entry->video.pixel_width, track_entry->video.pixel_height);
video_track_num = track_entry->track_number;
video_stream = new MKVReaderFILE(filename);
video_timecode_scale = track_entry->track_timecode_scale;
video_track_entry = track_entry;
}
}
}
// TODO this prevents trying to play video only files
// which the plug-in is not at all happy playing
/*if (!audio_decoder)// && !video_decoder)
return MKV_STOP;*/
wchar_t video_status[512] = {0};
if (audio_decoder && video_decoder)
{
StringCbPrintf(video_status, sizeof(video_status), L"MKV: %s, %s", audio_info, video_info);
videoOutput->extended(VIDUSER_SET_INFOSTRINGW,(INT_PTR)video_status,0);
}
else if (audio_decoder)
{
StringCbPrintf(video_status, sizeof(video_status), L"MKV: %s", audio_info);
videoOutput->extended(VIDUSER_SET_INFOSTRINGW,(INT_PTR)video_status,0);
}
else if (video_decoder)
{
StringCbPrintf(video_status, sizeof(video_status), L"MKV: %s, %s", audio_info, video_info);
videoOutput->extended(VIDUSER_SET_INFOSTRINGW,(INT_PTR)video_status,0);
}
return MKV_CONTINUE;
}
DWORD CALLBACK VideoThread(LPVOID param)
{
MKVPlayer *player = (MKVPlayer *)param;
return player->VideoThreadFunction();
}
int MKVPlayer::ParseCluster(nsmkv::MKVReader *stream, uint64_t size, uint64_t *track_numbers, size_t track_numbers_len)
{
nsmkv::Cluster cluster;
uint64_t total_bytes_read=0;
while (size)
{
ebml_node node;
uint64_t bytes_read = read_ebml_node(stream, &node);
if (bytes_read == 0)
return MKV_ERROR;
// benski> checking bytes_read and node.size separately prevents possible integer overflow attack
if (bytes_read > size)
return MKV_ERROR;
total_bytes_read+=bytes_read;
size-=bytes_read;
if (node.size > size)
return MKV_ERROR;
total_bytes_read+=node.size;
size-=node.size;
switch(node.id)
{
case mkv_cluster_timecode:
{
uint64_t val;
if (read_unsigned(stream, node.size, &val) == 0)
return MKV_ERROR;
printf("Time Code: %I64u\n", val);
cluster.time_code = val;
}
break;
case mkv_cluster_blockgroup:
{
printf("Block Group\n");
nsmkv::Block block;
if (nsmkv::ReadBlockGroup(stream, node.size, block, track_numbers, track_numbers_len) == 0)
return MKV_ERROR;
if (block.binary.data)
{
int ret = OnBlock(cluster, block);
if (ret != MKV_CONTINUE)
return ret;
}
}
break;
case mkv_cluster_simpleblock:
{
printf("simple block, size: %I64u\n", node.size);
nsmkv::Block block;
if (ReadBlockBinary(stream, node.size, block.binary, track_numbers, track_numbers_len) == 0)
return 0;
if (block.binary.data)
{
int ret = OnBlock(cluster, block);
if (ret != MKV_CONTINUE)
return ret;
}
}
break;
default:
nsmkv::ReadGlobal(stream, node.id, node.size);
}
}
return MKV_CONTINUE;
}
int MKVPlayer::OnBlock(const nsmkv::Cluster &cluster, const nsmkv::Block &block)
{
if (WaitForSingleObject(killswitch, 0) == WAIT_TIMEOUT)
{
if (block.binary.track_number == audio_track_num)
{
return OnAudio(cluster, block.binary);
}
else if (block.binary.track_number == video_track_num)
{
return OnVideo(cluster, block.binary);
}
return MKV_CONTINUE;
}
else
return MKV_ABORT;
}
int MKVPlayer::OnFirstCluster(uint64_t position)
{
if (video_decoder)
{
video_cluster_position = position;
video_thread = CreateThread(0, 0, VideoThread, this, 0, 0);
SetThreadPriority(video_thread, (int)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
}
return MKV_CONTINUE;
}
int MKVPlayer::Step(nsmkv::MKVReader *stream, uint64_t *track_numbers, size_t track_numbers_len)
{
uint64_t this_position = stream->Tell();
ebml_node node;
if (read_ebml_node(stream, &node) == 0)
return MKV_EOF;
if (node.id != mkv_void)
{
nsmkv::SeekEntry seek_entry(node.id, this_position-segment_position);
seek_table.AddEntry(seek_entry, nsmkv::SeekTable::ADDENTRY_FOUND);
}
switch(node.id)
{
case mkv_segment_segmentinfo:
if (nsmkv::ReadSegmentInfo(stream, node.size, segment_info) == 0)
return MKV_EOF;
OnSegmentInfo(segment_info);
break;
case mkv_metaseek_seekhead:
if (nsmkv::ReadSeekHead(stream, node.size, seek_table) == 0)
return MKV_EOF;
break;
case mkv_segment_tracks:
if (nsmkv::ReadTracks(stream, node.size, tracks) == 0)
return MKV_EOF;
return OnTracks(tracks);
break;
case mkv_segment_cues:
if (!cues_searched)
{
if (nsmkv::ReadCues(stream, node.size, cues) == 0)
return MKV_EOF;
cues_searched=true;
}
else
{
stream->Skip(node.size);
}
break;
case mkv_segment_cluster:
if (!first_cluster_found)
{
first_cluster_found=true;
OnFirstCluster(this_position);
}
return ParseCluster(stream, node.size, track_numbers, track_numbers_len);
break;
case mkv_segment_attachments:
if (nsmkv::ReadAttachment(stream, node.size, attachments) == 0)
return MKV_EOF;
break;
default:
if (nsmkv::ReadGlobal(stream, node.id, node.size) == 0)
return MKV_EOF;
break;
}
return MKV_CONTINUE;
}
DWORD CALLBACK MKVPlayer::ThreadFunction()
{
// ===== tell audio output helper object about the output plugin =====
audio_output.Init(plugin.outMod);
FILE *f = _wfopen(filename, L"rb");
if (!f)
goto btfo;
main_reader = new MKVReaderFILE(f);
// ===== read the header =====
if (!ParseHeader())
goto btfo;
// ===== find segment start =====
if (!FindSegment())
goto btfo;
// TODO: try to find more segments?
HANDLE handles[] = {killswitch, seek_event};
while(1)
{
int ret = WaitForMultipleObjects(2, handles, FALSE, 0);
if (ret == WAIT_TIMEOUT)
{
int ret = Step(main_reader, &audio_track_num, 1);
if (ret == MKV_EOF)
{
break;
}
else if (ret == MKV_ERROR || ret == MKV_STOP)
{
break;
}
}
else if (ret == WAIT_OBJECT_0) // kill
{
break;
}
else if (ret == WAIT_OBJECT_0+1) // seek event
{
ResetEvent(seek_event);
// pause video thread
if (video_decoder)
{
SetEvent(video_break);
WaitForSingleObject(video_flush_done, INFINITE);
}
FindCues();
uint64_t seek_time = segment_info.ConvertMillisecondsToTime(m_needseek);
uint64_t curr_time = segment_info.ConvertMillisecondsToTime(plugin.outMod->GetOutputTime() + audio_first_timestamp);
int direction = (curr_time < seek_time)?nsmkv::Cues::SEEK_FORWARD:nsmkv::Cues::SEEK_BACKWARD;
nsmkv::CuePoint *cue_point = cues.GetCuePoint(seek_time, curr_time, direction);
if (cue_point)
{
nsmkv::CueTrackPosition *position = cue_point->GetPosition(audio_track_num);
if (!position) // some files don't have the audio track. we're going to assume the data is interleaved and just use the video track
position = cue_point->GetPosition(video_track_num);
if (position)
{
audio_flushing=FLUSH_SEEK;
if (audio_decoder) audio_decoder->Flush();
main_reader->Seek(position->cluster_position + segment_position);
}
if (video_stream)
{
position = cue_point->GetPosition(video_track_num);
if (position)
{
video_stream->Seek(position->cluster_position + segment_position);
}
}
}
else
{
// TODO enumerate clusters & blocks to find closest time (ugh)
}
if (video_decoder)
{
SetEvent(video_flush);
WaitForSingleObject(video_flush_done, INFINITE);
}
}
}
delete main_reader;
main_reader=0;
if (WaitForSingleObject(killswitch, 0) != WAIT_OBJECT_0)
{
// TODO: tell audio decoder about end-of-stream and get remaining audio
audio_output.Write(0,0);
audio_output.WaitWhilePlaying();
if (WaitForSingleObject(killswitch, 0) != WAIT_OBJECT_0)
PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
}
if (audio_decoder)
{
audio_decoder->Close();
audio_decoder=0;
audio_output.Close();
}
return 0;
btfo: // bail the fuck out
if (WaitForSingleObject(killswitch, 0) != WAIT_OBJECT_0)
PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
delete main_reader;
main_reader=0;
if (audio_decoder)
{
audio_decoder->Close();
audio_decoder=0;
audio_output.Close();
}
return 1;
}

View File

@ -0,0 +1,45 @@
#include "api__in_mkv.h"
#include "main.h"
#include <api/service/waservicefactory.h>
api_config *AGAVE_API_CONFIG = 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_LNG, languageApiGUID);
// need to have this initialised before we try to do anything with localisation features
WASABI_API_START_LANG(plugin.hDllInstance,InMkvLangGUID);
}
void WasabiQuit()
{
ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID);
ServiceRelease(WASABI_API_LNG, languageApiGUID);
}

View File

@ -0,0 +1,9 @@
#pragma once
#include "../Agave/Config/api_config.h"
extern api_config *AGAVE_API_CONFIG;
#include "../Agave/Language/api_language.h"
void WasabiInit();
void WasabiQuit();

View File

@ -0,0 +1,61 @@
#pragma once
#include <bfc/dispatch.h>
class NOVTABLE ifc_mkvaudiodecoder : public Dispatchable
{
protected:
ifc_mkvaudiodecoder() {}
~ifc_mkvaudiodecoder() {}
public:
enum
{
MKV_SUCCESS = 0,
MKV_NEED_MORE_INPUT = -1,
MKV_FAILURE=1,
};
int OutputFrameSize(size_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"
int DecodeBlock(const void *inputBuffer, size_t inputBufferBytes, void *outputBuffer, size_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_BLOCK = 2,
FLUSH = 3,
CLOSE = 4,
END_OF_STREAM = 5,
};
};
inline int ifc_mkvaudiodecoder::OutputFrameSize(size_t *frame_size)
{
return _call(OUTPUT_FRAME_SIZE, (int)MKV_FAILURE, frame_size);
}
inline int ifc_mkvaudiodecoder::GetOutputProperties(unsigned int *sampleRate, unsigned int *channels, unsigned int *bitsPerSample, bool *isFloat)
{
return _call(GET_OUTPUT_PROPERTIES, (int)MKV_FAILURE, sampleRate, channels, bitsPerSample, isFloat);
}
inline int ifc_mkvaudiodecoder::DecodeBlock(const void *inputBuffer, size_t inputBufferBytes, void *outputBuffer, size_t *outputBufferBytes)
{
return _call(DECODE_BLOCK, (int)MKV_FAILURE, inputBuffer, inputBufferBytes, outputBuffer, outputBufferBytes);
}
inline void ifc_mkvaudiodecoder::Flush()
{
_voidcall(FLUSH);
}
inline void ifc_mkvaudiodecoder::Close()
{
_voidcall(CLOSE);
}
inline void ifc_mkvaudiodecoder::EndOfStream()
{
_voidcall(END_OF_STREAM);
}

View File

@ -0,0 +1,69 @@
#pragma once
#include <bfc/dispatch.h>
class NOVTABLE ifc_mkvvideodecoder : public Dispatchable
{
protected:
ifc_mkvvideodecoder() {}
~ifc_mkvvideodecoder() {}
public:
enum
{
MKV_SUCCESS = 0,
MKV_NEED_MORE_INPUT = -1,
MKV_FAILURE=1,
};
int GetOutputProperties(int *x, int *y, int *color_format, double *aspect_ratio);
int DecodeBlock(const void *inputBuffer, size_t inputBufferBytes, uint64_t timestamp);
void Flush();
void Close();
int GetPicture(void **data, void **decoder_data, uint64_t *timestamp);
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
DISPATCH_CODES
{
GET_OUTPUT_PROPERTIES = 0,
DECODE_BLOCK = 1,
FLUSH = 2,
CLOSE = 3,
GET_PICTURE = 4,
FREE_PICTURE = 5,
END_OF_STREAM = 6,
HURRY_UP = 7,
};
};
inline int ifc_mkvvideodecoder::GetOutputProperties(int *x, int *y, int *color_format, double *aspect_ratio)
{
return _call(GET_OUTPUT_PROPERTIES, (int)MKV_FAILURE, x, y, color_format, aspect_ratio);
}
inline int ifc_mkvvideodecoder::DecodeBlock(const void *inputBuffer, size_t inputBufferBytes, uint64_t timestamp)
{
return _call(DECODE_BLOCK, (int)MKV_FAILURE, inputBuffer, inputBufferBytes, timestamp);
}
inline void ifc_mkvvideodecoder::Flush()
{
_voidcall(FLUSH);
}
inline void ifc_mkvvideodecoder::Close()
{
_voidcall(CLOSE);
}
inline int ifc_mkvvideodecoder::GetPicture(void **data, void **decoder_data, uint64_t *timestamp)
{
return _call(GET_PICTURE, (int)MKV_FAILURE, data, decoder_data, timestamp);
}
inline void ifc_mkvvideodecoder::FreePicture(void *data, void *decoder_data)
{
_voidcall(FREE_PICTURE, data, decoder_data);
}
inline void ifc_mkvvideodecoder::EndOfStream()
{
_voidcall(END_OF_STREAM);
}
inline void ifc_mkvvideodecoder::HurryUp(int state)
{
_voidcall(HURRY_UP, state);
}

View File

@ -0,0 +1,153 @@
// 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, 316, 183
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "MKV File Properties"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "Close",IDOK,259,162,50,14
CONTROL "",IDC_TAB1,"SysTabControl32",0x0,7,7,302,151
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, 309
TOPMARGIN, 7
BOTTOMMARGIN, 176
END
IDD_TRACKS, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 309
TOPMARGIN, 7
BOTTOMMARGIN, 176
END
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE
BEGIN
IDS_NULLSOFT_MKV "Nullsoft Matroska Demuxer v%s"
65535 "{5BDA8055-292D-4fcd-8404-884C2A34A8F9}"
END
STRINGTABLE
BEGIN
IDS_NULLSOFT_MKV_OLD "Nullsoft Matroska Demuxer"
IDS_BUFFERING "Buffering"
IDS_FAMILY_STRING "Matroska Video"
IDS_TAB_TRACKS "Tracks"
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_LANGUAGE "Language"
IDS_TYPE_VIDEO "Video"
IDS_TYPE_AUDIO "Audio"
IDS_TYPE_SUBTITLE "Subtitle"
IDS_MKV_DESC "Matroska Video (MKV)"
END
STRINGTABLE
BEGIN
IDS_WEBM_DESC "WebM Video (webm)"
IDS_FAMILY_STRING_WEBM "WebM Video"
END
STRINGTABLE
BEGIN
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.29613.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "in_mkv", "in_mkv.vcxproj", "{FD5581A9-33B6-4D0A-8F1B-C4DC448C2FAE}"
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
{FD5581A9-33B6-4D0A-8F1B-C4DC448C2FAE}.Debug|Win32.ActiveCfg = Debug|Win32
{FD5581A9-33B6-4D0A-8F1B-C4DC448C2FAE}.Debug|Win32.Build.0 = Debug|Win32
{FD5581A9-33B6-4D0A-8F1B-C4DC448C2FAE}.Debug|x64.ActiveCfg = Debug|x64
{FD5581A9-33B6-4D0A-8F1B-C4DC448C2FAE}.Debug|x64.Build.0 = Debug|x64
{FD5581A9-33B6-4D0A-8F1B-C4DC448C2FAE}.Release|Win32.ActiveCfg = Release|Win32
{FD5581A9-33B6-4D0A-8F1B-C4DC448C2FAE}.Release|Win32.Build.0 = Release|Win32
{FD5581A9-33B6-4D0A-8F1B-C4DC448C2FAE}.Release|x64.ActiveCfg = Release|x64
{FD5581A9-33B6-4D0A-8F1B-C4DC448C2FAE}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {05F9DA40-C414-4686-8FF9-A07CFB14A3C8}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,285 @@
<?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>{FD5581A9-33B6-4D0A-8F1B-C4DC448C2FAE}</ProjectGuid>
<RootNamespace>in_mkv</RootNamespace>
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" 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>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|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>
<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_MKV_EXPORTS;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4146;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.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_MKV_EXPORTS;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4146;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.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_MKV_EXPORTS;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<BufferSecurityCheck>true</BufferSecurityCheck>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>None</DebugInformationFormat>
<DisableSpecificWarnings>4146;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.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>WIN64;NDEBUG;_WINDOWS;_USRDLL;IN_MKV_EXPORTS;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<BufferSecurityCheck>true</BufferSecurityCheck>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>None</DebugInformationFormat>
<DisableSpecificWarnings>4146;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.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="..\..\..\nsmkv\Attachments.cpp" />
<ClCompile Include="..\..\..\nsmkv\Cluster.cpp" />
<ClCompile Include="..\..\..\nsmkv\Cues.cpp" />
<ClCompile Include="..\..\..\nsmkv\ebml_float.cpp" />
<ClCompile Include="..\..\..\nsmkv\ebml_signed.cpp" />
<ClCompile Include="..\..\..\nsmkv\ebml_unsigned.cpp" />
<ClCompile Include="..\..\..\nsmkv\file_mkv_reader.cpp" />
<ClCompile Include="..\..\..\nsmkv\global_elements.cpp" />
<ClCompile Include="..\..\..\nsmkv\header.cpp" />
<ClCompile Include="..\..\..\nsmkv\Lacing.cpp" />
<ClCompile Include="..\..\..\nsmkv\mkv_date.cpp" />
<ClCompile Include="..\..\..\nsmkv\read.cpp" />
<ClCompile Include="..\..\..\nsmkv\SeekTable.cpp" />
<ClCompile Include="..\..\..\nsmkv\SegmentInfo.cpp" />
<ClCompile Include="..\..\..\nsmkv\Tracks.cpp" />
<ClCompile Include="..\..\..\nsmkv\vint.cpp" />
<ClCompile Include="..\..\..\nu\listview.cpp" />
<ClCompile Include="..\..\..\nu\SpillBuffer.cpp" />
<ClCompile Include="api__in_mkv.cpp" />
<ClCompile Include="ExtendedFileInfo.cpp" />
<ClCompile Include="InfoDialog.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="MKVDuration.cpp" />
<ClCompile Include="MKVInfo.cpp" />
<ClCompile Include="PlayThread.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\nsmkv\Attachments.h" />
<ClInclude Include="..\..\..\nsmkv\Cluster.h" />
<ClInclude Include="..\..\..\nsmkv\Cues.h" />
<ClInclude Include="..\..\..\nsmkv\file_mkv_reader.h" />
<ClInclude Include="..\..\..\nsmkv\Lacing.h" />
<ClInclude Include="..\..\..\nsmkv\mkv_reader.h" />
<ClInclude Include="..\..\..\nsmkv\segment.h" />
<ClInclude Include="..\..\..\nsmkv\SegmentInfo.h" />
<ClInclude Include="..\..\..\nsmkv\vint.h" />
<ClInclude Include="..\..\..\nu\AudioOutput.h" />
<ClInclude Include="api__in_mkv.h" />
<ClInclude Include="ifc_mkvaudiodecoder.h" />
<ClInclude Include="ifc_mkvvideodecoder.h" />
<ClInclude Include="main.h" />
<ClInclude Include="MKVDuration.h" />
<ClInclude Include="MKVInfo.h" />
<ClInclude Include="MKVPlayer.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="svc_mkvdecoder.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="in_mkv.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,155 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="api__in_mkv.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ExtendedFileInfo.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="MKVDuration.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="MKVInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PlayThread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\Attachments.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\Cluster.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\Cues.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\ebml_float.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\ebml_signed.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\ebml_unsigned.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\file_mkv_reader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\global_elements.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\header.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\Lacing.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nu\listview.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\mkv_date.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\read.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\SeekTable.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\SegmentInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nu\SpillBuffer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\Tracks.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nsmkv\vint.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="api__in_mkv.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ifc_mkvaudiodecoder.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ifc_mkvvideodecoder.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="main.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="MKVDuration.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="MKVInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="MKVPlayer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="svc_mkvdecoder.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\nsmkv\Attachments.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\nu\AudioOutput.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\nsmkv\Cluster.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\nsmkv\Cues.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\nsmkv\file_mkv_reader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\nsmkv\Lacing.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\nsmkv\mkv_reader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\nsmkv\segment.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\nsmkv\SegmentInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\nsmkv\vint.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Header Files">
<UniqueIdentifier>{ed30a23b-d56f-472b-acf7-658076aa03a3}</UniqueIdentifier>
</Filter>
<Filter Include="Ressource Files">
<UniqueIdentifier>{06cdbd20-2120-4a72-9eeb-d8ad2546fd1b}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{3fcda994-3740-4059-81b9-3769d4d24f7b}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="in_mkv.rc">
<Filter>Ressource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,242 @@
#include "../Winamp/in2.h"
#include "api__in_mkv.h"
#include "MKVInfo.h"
#include "../Winamp/wa_ipc.h"
#include "main.h"
#include "MKVPlayer.h"
#include "MKVDuration.h"
#include "../nu/ns_wc.h"
#include "resource.h"
#include <strsafe.h>
#define MKV_PLUGIN_VERSION L"0.86"
static wchar_t pluginName[256] = {0};
int g_duration=0;
int paused = 0;
static HANDLE play_thread = 0;
static MKVPlayer *player = 0;
// {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}; // "MKV\0Matroska Video (MKV)\0"
char* end = 0;
size_t remaining;
StringCchCopyExA(fileExtensionsString, 255, "MKV", &end, &remaining, 0);
StringCchCopyExA(end+1, remaining-1, WASABI_API_LNGSTRING(IDS_MKV_DESC), &end, &remaining, 0);
StringCchCopyExA(end+1, remaining-1, "webm", &end, &remaining, 0);
StringCchCopyExA(end+1, remaining-1, WASABI_API_LNGSTRING(IDS_WEBM_DESC), &end, &remaining, 0);
plugin.FileExtensions = fileExtensionsString;
}
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_MKV_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,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_MKV),MKV_PLUGIN_VERSION);
plugin.description = (char*)pluginName;
SetFileExtensions();
return IN_INIT_SUCCESS;
}
void Quit()
{
WasabiQuit();
}
void GetFileInfo(const wchar_t *file, wchar_t *title, int *length_in_ms)
{
if (title)
*title=0;
if (length_in_ms)
{
if (file && *file)
{
MKVDuration duration;
if (duration.Open(file))
{
if (title)
{
const char *mkv_title = duration.GetTitle();
if (mkv_title)
MultiByteToWideCharSZ(CP_UTF8, 0, mkv_title, -1, title, GETFILEINFO_TITLE_LENGTH);
}
*length_in_ms=duration.GetLengthMilliseconds();
}
else
*length_in_ms=-1000;
}
else
*length_in_ms = g_duration;
}
}
int InfoBox(const wchar_t *file, HWND hwndParent)
{
MKVInfo info;
if (info.Open(file))
{
WASABI_API_DIALOGBOXPARAMW(IDD_INFODIALOG, hwndParent, InfoDialog, (LPARAM)&info);
}
return INFOBOX_UNCHANGED;
}
int IsOurFile(const wchar_t *fn)
{
return 0;
}
DWORD CALLBACK MKVThread(LPVOID param);
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=-1000;
delete player;
player = new MKVPlayer(fn);
play_thread = CreateThread(0, 0, MKVThread, player, 0, 0);
SetThreadPriority(play_thread, (int)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
return 0; // success
}
void Pause()
{
paused = 1;
plugin.outMod->Pause(1);
}
void UnPause()
{
paused = 0;
plugin.outMod->Pause(0);
}
int IsPaused()
{
return paused;
}
void Stop()
{
if (player)
{
player->Kill();
if (play_thread) {
WaitForSingleObject(play_thread, INFINITE);
}
play_thread = 0;
delete player;
player=0;
}
}
// time stuff
int GetLength()
{
return g_duration;
}
int GetOutputTime()
{
if (plugin.outMod && player)
return player->GetOutputTime();
else
return 0;
}
void SetOutputTime(int time_in_ms)
{
if (player)
player->Seek(time_in_ms);
}
void SetVolume(int volume)
{
plugin.outMod->SetVolume(volume);
}
void SetPan(int pan)
{
plugin.outMod->SetPan(pan);
}
void EQSet(int on, char data[10], int preamp)
{
}
In_Module plugin =
{
IN_VER_RET,
"nullsoft(in_mkv.dll)",
NULL, // hMainWindow
NULL, // hDllInstance
0 /*"mkv\0Matroska Video\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,5 @@
#pragma once
#include "../Winamp/in2.h"
extern int g_duration;
extern In_Module plugin;
INT_PTR CALLBACK InfoDialog(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);

View File

@ -0,0 +1,40 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by in_mkv.rc
//
#define IDS_NULLSOFT_MKV_OLD 0
#define IDS_BUFFERING 2
#define IDS_MKV_VIDEO 3
#define IDS_FAMILY_STRING 3
#define IDS_STRING4 4
#define IDS_TAB_TRACKS 5
#define IDS_COLUMN_TRACK_TYPE 6
#define IDS_COLUMN_CODEC_NAME 7
#define IDS_COLUMN_CODEC_ID 8
#define IDS_COLUMN_DESCRIPTION 9
#define IDS_COLUMN_STREAM_NAME 10
#define IDS_COLUMN_LANGUAGE 11
#define IDS_TYPE_VIDEO 12
#define IDS_TYPE_AUDIO 13
#define IDS_TYPE_SUBTITLE 14
#define IDS_MKV_DESC 15
#define IDS_ABOUT_TEXT 16
#define IDD_DIALOG1 103
#define IDD_INFODIALOG 103
#define IDD_TRACKS 104
#define IDS_WEBM_DESC 107
#define IDS_FAMILY_STRING_WEBM 108
#define IDC_TAB1 1001
#define IDC_TRACKLIST 1002
#define IDS_NULLSOFT_MKV 65534
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 17
#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,37 @@
#pragma once
#include <bfc/dispatch.h>
#include "../nsmkv/Tracks.h"
#include <api/service/services.h>
class ifc_mkvvideodecoder;
class ifc_mkvaudiodecoder;
class NOVTABLE svc_mkvdecoder : public Dispatchable
{
protected:
svc_mkvdecoder() {}
~svc_mkvdecoder() {}
public:
static FOURCC getServiceType() { return WaSvc::MKVDECODER; }
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 char *codec_id, const nsmkv::TrackEntryData *track_entry_data, const nsmkv::AudioData *audio_data, unsigned int preferred_bits, unsigned int max_channels, bool floating_point, ifc_mkvaudiodecoder **decoder);
int CreateVideoDecoder(const char *codec_id, const nsmkv::TrackEntryData *track_entry_data, const nsmkv::VideoData *video_data, ifc_mkvvideodecoder **decoder);
DISPATCH_CODES
{
CREATE_AUDIO_DECODER = 0,
CREATE_VIDEO_DECODER = 1,
};
};
inline int svc_mkvdecoder::CreateAudioDecoder(const char *codec_id, const nsmkv::TrackEntryData *track_entry_data, const nsmkv::AudioData *audio_data, unsigned int preferred_bits, unsigned int max_channels, bool floating_point, ifc_mkvaudiodecoder **decoder)
{
return _call(CREATE_AUDIO_DECODER, (int)CREATEDECODER_NOT_MINE, codec_id, track_entry_data, audio_data, preferred_bits, max_channels, floating_point, decoder);
}
inline int svc_mkvdecoder::CreateVideoDecoder(const char *codec_id, const nsmkv::TrackEntryData *track_entry_data, const nsmkv::VideoData *video_data, ifc_mkvvideodecoder **decoder)
{
return _call(CREATE_VIDEO_DECODER, (int)CREATEDECODER_NOT_MINE, codec_id, track_entry_data, video_data, decoder);
}

View File

@ -0,0 +1,39 @@
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
#include "../../../Winamp/buildType.h"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,86,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,86,0,0"
VALUE "InternalName", "Nullsoft Matroksa Demuxer"
VALUE "LegalCopyright", "Copyright <20> 2009-2023 Winamp SA"
VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
VALUE "OriginalFilename", "in_mkv.dll"
VALUE "ProductName", "Winamp"
VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END