mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-17 15:25:45 -04:00
Initial community commit
This commit is contained in:
80
Src/Plugins/Input/in_mkv/ExtendedFileInfo.cpp
Normal file
80
Src/Plugins/Input/in_mkv/ExtendedFileInfo.cpp
Normal 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;
|
||||
}
|
247
Src/Plugins/Input/in_mkv/InfoDialog.cpp
Normal file
247
Src/Plugins/Input/in_mkv/InfoDialog.cpp
Normal 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;
|
||||
}
|
115
Src/Plugins/Input/in_mkv/MKVDuration.cpp
Normal file
115
Src/Plugins/Input/in_mkv/MKVDuration.cpp
Normal 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;
|
||||
}
|
20
Src/Plugins/Input/in_mkv/MKVDuration.h
Normal file
20
Src/Plugins/Input/in_mkv/MKVDuration.h
Normal 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;
|
||||
};
|
168
Src/Plugins/Input/in_mkv/MKVInfo.cpp
Normal file
168
Src/Plugins/Input/in_mkv/MKVInfo.cpp
Normal 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;
|
||||
}
|
26
Src/Plugins/Input/in_mkv/MKVInfo.h
Normal file
26
Src/Plugins/Input/in_mkv/MKVInfo.h
Normal 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;
|
||||
};
|
138
Src/Plugins/Input/in_mkv/MKVPlayer.h
Normal file
138
Src/Plugins/Input/in_mkv/MKVPlayer.h
Normal 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;
|
||||
|
||||
};
|
902
Src/Plugins/Input/in_mkv/PlayThread.cpp
Normal file
902
Src/Plugins/Input/in_mkv/PlayThread.cpp
Normal 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, ×tamp) == 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;
|
||||
}
|
45
Src/Plugins/Input/in_mkv/api__in_mkv.cpp
Normal file
45
Src/Plugins/Input/in_mkv/api__in_mkv.cpp
Normal 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);
|
||||
}
|
9
Src/Plugins/Input/in_mkv/api__in_mkv.h
Normal file
9
Src/Plugins/Input/in_mkv/api__in_mkv.h
Normal 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();
|
61
Src/Plugins/Input/in_mkv/ifc_mkvaudiodecoder.h
Normal file
61
Src/Plugins/Input/in_mkv/ifc_mkvaudiodecoder.h
Normal 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);
|
||||
}
|
69
Src/Plugins/Input/in_mkv/ifc_mkvvideodecoder.h
Normal file
69
Src/Plugins/Input/in_mkv/ifc_mkvvideodecoder.h
Normal 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);
|
||||
}
|
153
Src/Plugins/Input/in_mkv/in_mkv.rc
Normal file
153
Src/Plugins/Input/in_mkv/in_mkv.rc
Normal 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
|
||||
|
31
Src/Plugins/Input/in_mkv/in_mkv.sln
Normal file
31
Src/Plugins/Input/in_mkv/in_mkv.sln
Normal 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
|
285
Src/Plugins/Input/in_mkv/in_mkv.vcxproj
Normal file
285
Src/Plugins/Input/in_mkv/in_mkv.vcxproj
Normal 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>
|
155
Src/Plugins/Input/in_mkv/in_mkv.vcxproj.filters
Normal file
155
Src/Plugins/Input/in_mkv/in_mkv.vcxproj.filters
Normal 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>
|
242
Src/Plugins/Input/in_mkv/main.cpp
Normal file
242
Src/Plugins/Input/in_mkv/main.cpp
Normal 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;
|
||||
}
|
5
Src/Plugins/Input/in_mkv/main.h
Normal file
5
Src/Plugins/Input/in_mkv/main.h
Normal 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);
|
40
Src/Plugins/Input/in_mkv/resource.h
Normal file
40
Src/Plugins/Input/in_mkv/resource.h
Normal 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
|
37
Src/Plugins/Input/in_mkv/svc_mkvdecoder.h
Normal file
37
Src/Plugins/Input/in_mkv/svc_mkvdecoder.h
Normal 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);
|
||||
}
|
39
Src/Plugins/Input/in_mkv/version.rc2
Normal file
39
Src/Plugins/Input/in_mkv/version.rc2
Normal 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
|
Reference in New Issue
Block a user