Skip to content

Commit

Permalink
Various minor updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Alcaro committed Oct 1, 2023
1 parent 5a6b32f commit c202934
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 46 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ Ideally, this project would be unnecessary (other than the codec installation).

- $250 - CLSID_MPEG1Splitter video output, and CLSID_CMpegVideoCodec (these objects may use GStreamer, of course)
- $25 - CLSID_MPEG1Splitter IAMStreamSelect (doesn't need to be fully implemented, just needs the parts Kirikiri uses)
- $25 - CLSID_VideoMixingRenderer9 ChangeD3DDevice and NotifyEvent
- ~~$25 - CLSID_VideoMixingRenderer9 ChangeD3DDevice and NotifyEvent~~ Resolved and claimed
- $500 - WMCreateSyncReader compressed output, CLSID_CWMADecMediaObject, and CLSID_CWMVDecMediaObject
- $100 - Direct3D 9 on WS_CHILD windows under wined3d in the Debian package - the bug doesn't reproduce if I compile Wine from source
- $25 - make WMSyncReader resize its allocator, so it can output RGB32 properly
- $100 - figure out what's going on with the memory allocator and VFW_E_NOT_COMMITTED, and solve it
- $0 - anything involving gstkrkr and Proton's GStreamer. That's a patent issue; it's a question for lawyers, not programmers. It's only needed in Proton, not vanilla Wine.
- Anything that Kirikiri needs but isn't in the above list - that's a bug in this readme, contact me

I would do it myself if I could, but I've debugged some things too hard, and have single-stepped and disassembled Windows DLLs; I haven't disassembled the DirectShow DLLs, but I don't know how wide Wine's clean-room-only rule is.
though I'm working on them myself, so fair chance I'll finish first.

You are allowed, but not required, to base such efforts on this project. My architecture is very different from Wine's existing objects, and many of them are implemented in an awful way, so most of my code is unusable; but you're welcome to look for hints on the objects' expected behavior, or otherwise use them to help implement yours. You may also use the Kirikiri source code, <https://github.com/krkrz/krkr2/tree/master/kirikiri2/trunk>

Expand Down
69 changes: 40 additions & 29 deletions krkrwine.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// SPDX-License-Identifier: LGPL-2.0-or-later

#define DOIT

#ifdef __MINGW32__
# define _FILE_OFFSET_BITS 64
// mingw *really* wants to define its own printf/scanf, which adds ~20KB random stuff to the binary
Expand All @@ -14,6 +12,8 @@
# define __USE_MINGW_ANSI_STDIO 0 // (subsequent includes of c++config.h are harmless, there's an include guard)
#endif

#define DEBUG 1

// WARNING to anyone trying to use these objects to implement them for real in Wine:
// Wine's CLSID_CMpegAudioCodec demands packet boundaries to be in place. Therefore, this demuxer does that.
// It's as if there's a builtin mpegaudioparse element.
Expand Down Expand Up @@ -137,7 +137,9 @@ class com_base_embedded : public Tis... {
public:
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override
{
#if DEBUG >= 1
//printf("plm QI %s\n", guid_to_str(riid));
#endif
*ppvObject = nullptr;
if (riid == __uuidof(IUnknown))
{
Expand All @@ -157,18 +159,10 @@ class com_base : public com_base_embedded<Tis...> {
public:
ULONG STDMETHODCALLTYPE AddRef() override
{
//if (!strcmp(typeid(this).name(), "P8com_baseIJ11IBaseFilter27IVMRSurfaceAllocatorNotify9EE"))
//{
//printf("addref, now %u\n", refcount+1);
//}
return ++refcount;
}
ULONG STDMETHODCALLTYPE Release() override
{
//if (!strcmp(typeid(this).name(), "P8com_baseIJ11IBaseFilter27IVMRSurfaceAllocatorNotify9EE"))
//{
//printf("release, now %u\n", refcount-1);
//}
uint32_t new_refcount = --refcount;
if (!new_refcount)
delete this;
Expand Down Expand Up @@ -316,7 +310,7 @@ class base_filter : public com_base<IBaseFilter, bases...> {
public:
void debug(const char * fmt, ...)
{
return;
#if DEBUG >= 1
char buf[1024];

va_list args;
Expand All @@ -331,6 +325,7 @@ class base_filter : public com_base<IBaseFilter, bases...> {
#endif
fprintf(stdout, "plm %lu %s %s\n", GetCurrentThreadId(), my_typename, buf);
fflush(stdout);
#endif
}

Touter* parent()
Expand Down Expand Up @@ -434,7 +429,7 @@ class base_pin : public Tbase {

void debug(const char * fmt, ...)
{
return;
#if DEBUG >= 1
char buf[1024];

va_list args;
Expand All @@ -448,6 +443,7 @@ class base_pin : public Tbase {
const char * my_typename = "";
#endif
fprintf(stdout, "plm %lu %s %s\n", GetCurrentThreadId(), my_typename, buf);
#endif
}

Touter* parent()
Expand Down Expand Up @@ -810,7 +806,7 @@ class CMPEG1Splitter : public base_filter<CMPEG1Splitter, IAMStreamSelect, IMedi
};
am_mediatype = {
.majortype = MEDIATYPE_Video,
.subtype = MEDIASUBTYPE_MPEG1Packet,
.subtype = MEDIASUBTYPE_MPEG1Payload,
.bFixedSizeSamples = false,
.bTemporalCompression = true,
.lSampleSize = 0,
Expand Down Expand Up @@ -1165,7 +1161,7 @@ class CMPEG1Splitter : public base_filter<CMPEG1Splitter, IAMStreamSelect, IMedi
}
HRESULT STDMETHODCALLTYPE Enable(long lIndex, DWORD dwFlags) override
{
return S_OK; // just don't bother
return E_OUTOFMEMORY; // I don't know if this is used - leave it unimplemented until I find something that uses it
}
HRESULT STDMETHODCALLTYPE Info(long lIndex, AM_MEDIA_TYPE** ppmt, DWORD* pdwFlags, LCID* plcid,
DWORD* pdwGroup, LPWSTR* ppszName, IUnknown** ppObject, IUnknown** ppUnk) override
Expand All @@ -1186,7 +1182,7 @@ class CMPEG1Splitter : public base_filter<CMPEG1Splitter, IAMStreamSelect, IMedi
*ppmt = CreateMediaType(pin_a.media_type());
}
if (pdwFlags)
*pdwFlags = AMSTREAMSELECTINFO_ENABLED; // just don't bother
*pdwFlags = 0; // should be AMSTREAMSELECTINFO_ENABLED, but I don't know if it's used
if (pdwGroup)
*pdwGroup = is_audio;
// ignore the others, Kirikiri just leaves them as null (and it never uses the group, it just stores it somewhere)
Expand Down Expand Up @@ -1260,7 +1256,7 @@ class CMpegVideoCodec : public base_filter<CMpegVideoCodec> {

bool connect_input(IPin* pConnector, const AM_MEDIA_TYPE * pmt)
{
if (pmt->majortype == MEDIATYPE_Video && pmt->subtype == MEDIASUBTYPE_MPEG1Packet)
if (pmt->majortype == MEDIATYPE_Video && pmt->subtype == MEDIASUBTYPE_MPEG1Payload)
return parent()->update_size_from_mediatype(pmt);
return false;
}
Expand Down Expand Up @@ -1320,7 +1316,7 @@ class CMpegVideoCodec : public base_filter<CMpegVideoCodec> {
.biWidth = -1,
.biHeight = -1,
.biPlanes = 1,
.biBitCount = 0,
.biBitCount = -1,
.biCompression = 0,
.biSizeImage = 0xFFFFFFFF,
},
Expand Down Expand Up @@ -2269,23 +2265,32 @@ static void very_unsafe_init()
if (initialized)
return;
initialized = true;
//setvbuf(stdout, nullptr, _IONBF, 0);
//puts("krkrwine - hello world");
#if DEBUG >= 1
setvbuf(stdout, nullptr, _IONBF, 0);
puts("krkrwine - hello world");
#endif

// refuse to operate on Windows
auto wine_get_build_id = (const char*(*)())GetProcAddress(GetModuleHandle("ntdll.dll"), "wine_get_version");
auto wine_get_build_id = (const char*(*)())GetProcAddress(GetModuleHandle("ntdll.dll"), "wine_get_build_id");
if (!wine_get_build_id)
return;
is_debian_build = (strstr(wine_get_build_id(), "Debian"));

DWORD ignore;

IClassFactory* fac_mpegsplit = new ClassFactory<CMPEG1Splitter, CLSID_MPEG1Splitter>();
CoRegisterClassObject(CLSID_MPEG1Splitter, fac_mpegsplit, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ignore);

IClassFactory* fac_mpegvideo = new ClassFactory<CMpegVideoCodec, CLSID_CMpegVideoCodec>();
CoRegisterClassObject(CLSID_CMpegVideoCodec, fac_mpegvideo, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ignore);
// if CLSID_CMpegVideoCodec already exists, don't install my own
CComPtr<IClassFactory> fac;
if (FAILED(CoGetClassObject(CLSID_CMpegVideoCodec, CLSCTX_INPROC_SERVER, NULL, IID_PPV_ARGS(&fac))))
{
IClassFactory* fac_mpegsplit = new ClassFactory<CMPEG1Splitter, CLSID_MPEG1Splitter>();
CoRegisterClassObject(CLSID_MPEG1Splitter, fac_mpegsplit, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ignore);

IClassFactory* fac_mpegvideo = new ClassFactory<CMpegVideoCodec, CLSID_CMpegVideoCodec>();
CoRegisterClassObject(CLSID_CMpegVideoCodec, fac_mpegvideo, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ignore);
}

// no real way to check if this one needs to be patched, but not needed either,
// it calls the original functions and only intervenes if they misbehave
IClassFactory* fac_vmr9 = new ClassFactory<CVideoMixingRenderer9, CLSID_VideoMixingRenderer9>();
CoRegisterClassObject(CLSID_VideoMixingRenderer9, fac_vmr9, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ignore);

Expand All @@ -2301,6 +2306,7 @@ static void very_unsafe_init()
if (!mod)
return;

// I can't think of any real way to detect if this one needs to be monkeypatched...
uint8_t* base_addr = (uint8_t*)mod;
IMAGE_DOS_HEADER* head_dos = (IMAGE_DOS_HEADER*)base_addr;
IMAGE_NT_HEADERS* head_nt = (IMAGE_NT_HEADERS*)(base_addr + head_dos->e_lfanew);
Expand All @@ -2310,6 +2316,7 @@ static void very_unsafe_init()
void* find_function = (void*)GetProcAddress;
//void* find_function = (void*)GetProcAddress(GetModuleHandle("kernel32.dll"), "GetProcAddress"); // in case your compiler sucks
void* replace_function = (void*)myGetProcAddress;
bool found_function = false;

while (imports->Name)
{
Expand All @@ -2320,16 +2327,20 @@ static void very_unsafe_init()
{
// can't just *out = replace_function, import table is read only
WriteProcessMemory(GetCurrentProcess(), out, &replace_function, sizeof(replace_function), NULL);
found_function = true;
}
out++;
}
imports++;
}

// only register these if the fake WMCreateSyncReader can be injected
IClassFactory* fac_dmo = new AggregatingClassFactory<fake_DMOObject, CLSID_CWMVDecMediaObject>();
CoRegisterClassObject(CLSID_CWMVDecMediaObject, fac_dmo, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ignore);
CoRegisterClassObject(CLSID_CWMADecMediaObject, fac_dmo, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ignore);
if (found_function)
{
// only register these if the fake WMCreateSyncReader can be injected
IClassFactory* fac_dmo = new AggregatingClassFactory<fake_DMOObject, CLSID_CWMVDecMediaObject>();
CoRegisterClassObject(CLSID_CWMVDecMediaObject, fac_dmo, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ignore);
CoRegisterClassObject(CLSID_CWMADecMediaObject, fac_dmo, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ignore);
}
}

#ifdef __i386__
Expand Down
2 changes: 1 addition & 1 deletion reformat.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
args[n] = arg
# print("------")
# print(ret,name,args)
if args:
if args != ['']:
args_untyped = ','.join(re.search("[A-Za-z0-9_a+]+$", a)[0] for a in args)
else:
args_untyped = ''
Expand Down
78 changes: 71 additions & 7 deletions x/homemade-source-and-sink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ static char* guid_to_str(const GUID& guid)
return ret;
}

template<typename T> T min(T a, T b) { return a < b ? a : b; }
template<typename T> T max(T a, T b) { return a > b ? a : b; }


template<typename T> class CComPtr {
void assign(T* ptr)
Expand Down Expand Up @@ -226,7 +229,8 @@ class base_filter : public com_base<IBaseFilter/*, ISpecifyPropertyPages*/> {
vsnprintf(buf, 1024, fmt, args);
va_end(args);

fprintf(stdout, "%lu %s %s\n", GetCurrentThreadId(), typeid(Touter).name(), buf);
//fprintf(stdout, "%lu %s %s\n", GetCurrentThreadId(), typeid(Touter).name(), buf);
fprintf(stdout, "%lu %s %s\n", GetCurrentThreadId(), "base_filter", buf);
fflush(stdout);
}

Expand Down Expand Up @@ -333,7 +337,8 @@ class base_pin : public Tbase {
vsnprintf(buf, 1024, fmt, args);
va_end(args);

fprintf(stdout, "%lu %s %s\n", GetCurrentThreadId(), typeid(Touter).name(), buf);
//fprintf(stdout, "%lu %s %s\n", GetCurrentThreadId(), typeid(Touter).name(), buf);
fprintf(stdout, "%lu %s %s\n", GetCurrentThreadId(), "base_pin", buf);
}

Touter* parent()
Expand Down Expand Up @@ -532,12 +537,27 @@ pSample->GetTime(&time1, &time2);
HRESULT WINAPI SyncReadAligned(IMediaSample* pSample)
{
debug("IAsyncReader SyncReadAligned");
return E_OUTOFMEMORY;
REFERENCE_TIME start_t;
REFERENCE_TIME end_t;
pSample->GetTime(&start_t, &end_t);

size_t start = start_t/10000000; // microsoft, seriously?
size_t end = end_t/10000000;

int64_t len;
this->Length(&len, &len);
if (end > len)
end = len;
BYTE* ptr;
pSample->GetPointer(&ptr);
SyncRead(start, end-start, ptr);
pSample->SetActualDataLength(end-start);
return S_OK;
}
HRESULT WINAPI WaitForNext(DWORD dwTimeout, IMediaSample** ppSample, uintptr_t* pdwUser)
{
debug("IAsyncReader WaitForNext");
return E_OUTOFMEMORY;
return S_FALSE;
}
};

Expand Down Expand Up @@ -614,7 +634,7 @@ class customFilterSink : public base_filter<customFilterSink> {

class customFilterSource : public base_filter<customFilterSource> {
public:
class out_pin : public base_pin<true, com_base_embedded<IPin, IAsyncReader>, out_pin> {
class out_pin : public base_pin<true, com_base_embedded<IPin, IAsyncReader, IStream>, out_pin> {
public:
customFilterSource* parent() { return container_of<&customFilterSource::pin>(this); }

Expand Down Expand Up @@ -661,6 +681,40 @@ class customFilterSource : public base_filter<customFilterSource> {
memcpy(pBuffer, parent()->ptr+llPosition, lLength);
return S_OK;
}

ULONG pos = 0;
// ISequentialStream
HRESULT STDMETHODCALLTYPE Read(void* pv, ULONG cb, ULONG* pcbRead) override
{
puts("ISequentialStream Read");
ULONG total = parent()->len;
ULONG n = min(cb, total-pos);
memcpy(pv, parent()->ptr+pos, n);
*pcbRead = n;
return (n == cb ? S_OK : S_FALSE);
}
HRESULT STDMETHODCALLTYPE Write(const void* pv, ULONG cb, ULONG* pcbWritten) override
{ puts("ISequentialStream Write"); return E_OUTOFMEMORY; }

// IStream
HRESULT STDMETHODCALLTYPE Clone(IStream** ppstm) override
{ puts("IStream Clone"); return E_OUTOFMEMORY; }
HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags) override
{ puts("IStream Commit"); return E_OUTOFMEMORY; }
HRESULT STDMETHODCALLTYPE CopyTo(IStream* pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten) override
{ puts("IStream CopyTo"); return E_OUTOFMEMORY; }
HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) override
{ puts("IStream LockRegion"); return E_OUTOFMEMORY; }
HRESULT STDMETHODCALLTYPE Revert() override
{ puts("IStream Revert"); return E_OUTOFMEMORY; }
HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition) override
{ puts("IStream Seek"); return E_OUTOFMEMORY; }
HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize) override
{ puts("IStream SetSize"); return E_OUTOFMEMORY; }
HRESULT STDMETHODCALLTYPE Stat(STATSTG* pstatstg, DWORD grfStatFlag) override
{ puts("IStream Stat"); return E_OUTOFMEMORY; }
HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) override
{ puts("IStream UnlockRegion"); return E_OUTOFMEMORY; }
};
out_pin pin;

Expand Down Expand Up @@ -719,6 +773,7 @@ puts("");
puts("TRYCONNECTPAIR");
if (SUCCEEDED(graph->ConnectDirect(src_pin, dst_pin, nullptr)))
return S_OK;
puts("FAILCONNECTPAIR");
}
}
return E_FAIL;
Expand Down Expand Up @@ -757,7 +812,7 @@ int main()
require(4, mpegSplitter.CoCreateInstance(CLSID_MPEG1Splitter, nullptr, CLSCTX_INPROC_SERVER));
require(5, mpegVideoCodec.CoCreateInstance(CLSID_CMpegVideoCodec, nullptr, CLSCTX_INPROC_SERVER));
require(6, mpegAudioCodec.CoCreateInstance(CLSID_CMpegAudioCodec, nullptr, CLSCTX_INPROC_SERVER));
require(7, decodebin.CoCreateInstance(CLSID_decodebin_parser, nullptr, CLSCTX_INPROC_SERVER));
//require(7, decodebin.CoCreateInstance(CLSID_decodebin_parser, nullptr, CLSCTX_INPROC_SERVER));
require(8, dsound.CoCreateInstance(CLSID_DSoundRender, nullptr, CLSCTX_INPROC_SERVER));
require(9, vmr9.CoCreateInstance(CLSID_VideoMixingRenderer9, nullptr, CLSCTX_INPROC_SERVER));

Expand All @@ -767,7 +822,7 @@ int main()
require(14, filterGraph->AddFilter(mpegSplitter, L"mpegSplitter"));
require(15, filterGraph->AddFilter(mpegVideoCodec, L"mpegVideoCodec"));
require(16, filterGraph->AddFilter(mpegAudioCodec, L"mpegAudioCodec"));
require(17, filterGraph->AddFilter(decodebin, L"decodebin"));
//require(17, filterGraph->AddFilter(decodebin, L"decodebin"));
require(18, filterGraph->AddFilter(dsound, L"dsound"));
require(19, filterGraph->AddFilter(vmr9, L"vmr9"));

Expand Down Expand Up @@ -827,3 +882,12 @@ int main()

puts("exit");
}

#ifdef __MINGW32__
// deleting these things removes a few kilobytes of binary and a dependency on libstdc++-6.dll
void* operator new(std::size_t n) _GLIBCXX_THROW(std::bad_alloc) { return malloc(n); }
void operator delete(void* p) noexcept { free(p); }
void operator delete(void* p, std::size_t n) noexcept { operator delete(p); }
extern "C" void __cxa_pure_virtual() { __builtin_trap(); }
extern "C" void _pei386_runtime_relocator() {}
#endif
Loading

0 comments on commit c202934

Please sign in to comment.