diff --git a/README.md b/README.md index 3d9f8c4..562270a 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ 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 @@ -71,7 +71,7 @@ Ideally, this project would be unnecessary (other than the codec installation). - $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, diff --git a/krkrwine.cpp b/krkrwine.cpp index 7eff406..e7e356d 100644 --- a/krkrwine.cpp +++ b/krkrwine.cpp @@ -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 @@ -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. @@ -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)) { @@ -157,18 +159,10 @@ class com_base : public com_base_embedded { 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; @@ -316,7 +310,7 @@ class base_filter : public com_base { public: void debug(const char * fmt, ...) { - return; +#if DEBUG >= 1 char buf[1024]; va_list args; @@ -331,6 +325,7 @@ class base_filter : public com_base { #endif fprintf(stdout, "plm %lu %s %s\n", GetCurrentThreadId(), my_typename, buf); fflush(stdout); +#endif } Touter* parent() @@ -434,7 +429,7 @@ class base_pin : public Tbase { void debug(const char * fmt, ...) { - return; +#if DEBUG >= 1 char buf[1024]; va_list args; @@ -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() @@ -810,7 +806,7 @@ class CMPEG1Splitter : public base_filter { 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; } @@ -1320,7 +1316,7 @@ class CMpegVideoCodec : public base_filter { .biWidth = -1, .biHeight = -1, .biPlanes = 1, - .biBitCount = 0, + .biBitCount = -1, .biCompression = 0, .biSizeImage = 0xFFFFFFFF, }, @@ -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(); - CoRegisterClassObject(CLSID_MPEG1Splitter, fac_mpegsplit, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ignore); - - IClassFactory* fac_mpegvideo = new ClassFactory(); - CoRegisterClassObject(CLSID_CMpegVideoCodec, fac_mpegvideo, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ignore); + // if CLSID_CMpegVideoCodec already exists, don't install my own + CComPtr fac; + if (FAILED(CoGetClassObject(CLSID_CMpegVideoCodec, CLSCTX_INPROC_SERVER, NULL, IID_PPV_ARGS(&fac)))) + { + IClassFactory* fac_mpegsplit = new ClassFactory(); + CoRegisterClassObject(CLSID_MPEG1Splitter, fac_mpegsplit, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ignore); + + IClassFactory* fac_mpegvideo = new ClassFactory(); + 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(); CoRegisterClassObject(CLSID_VideoMixingRenderer9, fac_vmr9, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ignore); @@ -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); @@ -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) { @@ -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(); - 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(); + CoRegisterClassObject(CLSID_CWMVDecMediaObject, fac_dmo, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ignore); + CoRegisterClassObject(CLSID_CWMADecMediaObject, fac_dmo, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ignore); + } } #ifdef __i386__ diff --git a/reformat.py b/reformat.py index abf06ba..ef4a4e1 100755 --- a/reformat.py +++ b/reformat.py @@ -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 = '' diff --git a/x/homemade-source-and-sink.cpp b/x/homemade-source-and-sink.cpp index c34f045..1ddf3f1 100644 --- a/x/homemade-source-and-sink.cpp +++ b/x/homemade-source-and-sink.cpp @@ -28,6 +28,9 @@ static char* guid_to_str(const GUID& guid) return ret; } +template T min(T a, T b) { return a < b ? a : b; } +template T max(T a, T b) { return a > b ? a : b; } + template class CComPtr { void assign(T* ptr) @@ -226,7 +229,8 @@ class base_filter : public com_base { 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); } @@ -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() @@ -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; } }; @@ -614,7 +634,7 @@ class customFilterSink : public base_filter { class customFilterSource : public base_filter { public: - class out_pin : public base_pin, out_pin> { + class out_pin : public base_pin, out_pin> { public: customFilterSource* parent() { return container_of<&customFilterSource::pin>(this); } @@ -661,6 +681,40 @@ class customFilterSource : public base_filter { 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; @@ -719,6 +773,7 @@ puts(""); puts("TRYCONNECTPAIR"); if (SUCCEEDED(graph->ConnectDirect(src_pin, dst_pin, nullptr))) return S_OK; +puts("FAILCONNECTPAIR"); } } return E_FAIL; @@ -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)); @@ -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")); @@ -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 diff --git a/x/nanodecode.cpp b/x/nanodecode.cpp index 2cf59cc..867765a 100644 --- a/x/nanodecode.cpp +++ b/x/nanodecode.cpp @@ -81,7 +81,7 @@ template class CComPtr { static HRESULT connect_filters(IGraphBuilder* graph, IBaseFilter* src, IBaseFilter* dst) { -puts("connect."); +puts("TRYCONNECTFILT"); CComPtr src_enum; if (FAILED(src->EnumPins(&src_enum))) return E_FAIL; @@ -89,12 +89,14 @@ puts("connect."); CComPtr src_pin; while (src_enum->Next(1, &src_pin, nullptr) == S_OK) { -puts("src."); PIN_INFO src_info; if (FAILED(src_pin->QueryPinInfo(&src_info))) return E_FAIL; if (src_info.pFilter) src_info.pFilter->Release(); +printf("TRYCONNECTSRC "); +for (int i=0;src_info.achName[i];i++)putchar(src_info.achName[i]); +puts(""); if (src_info.dir != PINDIR_OUTPUT) continue; @@ -108,10 +110,12 @@ puts("src."); CComPtr dst_pin; while (dst_enum->Next(1, &dst_pin, nullptr) == S_OK) { -puts("dst."); PIN_INFO dst_info; if (FAILED(dst_pin->QueryPinInfo(&dst_info))) return E_FAIL; +printf("TRYCONNECTDST "); +for (int i=0;src_info.achName[i];i++)putchar(src_info.achName[i]); +puts(""); if (dst_info.pFilter) dst_info.pFilter->Release(); if (dst_info.dir != PINDIR_INPUT) @@ -121,14 +125,15 @@ puts("dst."); if (check_pin != nullptr) continue; -puts("match."); - //if (SUCCEEDED(graph->Connect(src_pin, dst_pin))) - if (SUCCEEDED(graph->ConnectDirect(src_pin, dst_pin, nullptr))) +puts("TRYCONNECTPAIR"); + //HRESULT hr = graph->Connect(src_pin, dst_pin); + HRESULT hr = graph->ConnectDirect(src_pin, dst_pin, nullptr); + if (SUCCEEDED(hr)) { puts("match2."); return S_OK; } -puts("match3."); +printf("FAILCONNECTPAIR %.8lx\n", hr); } } return E_FAIL;