http://bugs.winehq.org/show_bug.cgi?id=60156 Bug ID: 60156 Summary: amstream: IAMMultiMediaStream::OpenFile() renders only the first output pin of the source filter Product: Wine Version: 11.0 Hardware: x86-64 OS: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: quartz Assignee: wine-bugs@list.winehq.org Reporter: wehrwolfmann@gmail.com Target Milestone: --- Distribution: --- Created attachment 81775 --> http://bugs.winehq.org/attachment.cgi?id=81775 Proposed patch: render all output pins of the source filter in IAMMultiMediaStream::OpenFile() IAMMultiMediaStream::OpenFile() asks the source filter's pin enumerator for exactly one pin and renders exactly that pin. When the source filter parses the file itself and therefore exposes one output pin per elementary stream -- as the WM ASF reader in dlls/qasf does -- every pin after the first is silently left unconnected. Where it is ----------- dlls/amstream/multimedia.c, multimedia_stream_OpenFile(), lines 429-510 in wine-11.15/master (identical, same line numbers, in 8.0, 9.0, 10.0, 11.0 and 11.14): 455 if (SUCCEEDED(ret)) 456 ret = IBaseFilter_EnumPins(BaseFilter, &EnumPins); 457 458 if (SUCCEEDED(ret)) 459 ret = IEnumPins_Next(EnumPins, 1, &ipin, NULL); 460 461 if (SUCCEEDED(ret)) 462 { 463 ret = IPin_QueryDirection(ipin, &pin_direction); 464 if (ret == S_OK && pin_direction == PINDIR_OUTPUT) 465 This->ipin = ipin; 466 } ... 476 ret = IFilterGraph2_RenderEx(graph, This->ipin, renderflags, NULL); There is no loop. The enumerator is queried once, and RenderEx() is called once, always with This->ipin. This does not show up with .avi because CLSID_AsyncReader has a single output pin and quartz's autoplug_through_filter() (dlls/quartz/filtergraph.c) then recursively renders all output pins of the filters it inserts, so the AVI splitter's video and audio pins both get connected. A source filter that is itself the parser never gets that treatment: dlls/qasf/asfreader.c creates one source pin per stream inside Load(), before AddSourceFilter() returns, and quartz does not enumerate the source filter's own pins -- that is amstream's job. Application: Sacred Gold ------------------------ Sacred Gold (Steam appid 12320, also sold by GOG) plays its .wmv cutscenes through DirectShow Multimedia Streaming. It creates a video stream and an audio stream, the latter with AMMSF_ADDDEFAULTRENDERER, then calls OpenFile() with flags 0. Trace with +quartz, Proton 11.0 (wine-11.0), ASF Reader registered as the source filter for the ASF media type: multimedia_stream_AddMediaStream ... id {a35ff56a-...} (MSPID_PrimaryVideo), flags 0 multimedia_stream_AddMediaStream ... id {a35ff56b-...} (MSPID_PrimaryAudio), flags 0x1 dsound_render_create Created DirectSound renderer 001D5218. multimedia_stream_OpenFile (001D0780/001D0780)->(L".\\MOVIE\\ASCARON.WMV",0) FilterGraph2_AddSourceFilter Using source filter {187463a0-5bb7-11d3-acbe-0080c75e246e}. asf_reader_get_pin iface 001D56B0, index 0. asf_reader_get_pin iface 001D56B0, index 1. asf_reader_get_pin iface 001D56B0, index 2. pin_QueryDirection pin 001D5850 L"Source":L"Raw Video 0", dir 02292C44. FilterGraph2_RenderEx graph 001D4FD0, source 001D5850, flags 0x1, context 00000000. ... source_Connect pin 001D5850 L"Source":L"Raw Video 0", peer 001D53D0, mt 00000000. The reader exposes two output pins, "Raw Video 0" and "Raw Audio 1". RenderEx() is called exactly once, for "Raw Video 0". "Raw Audio 1" is never rendered, and the DirectSound renderer that amstream itself created a few lines earlier stays unconnected. Result: the cutscenes play with picture and no sound at all. Measurements ------------ Captured from the PipeWire monitor of the output sink with parec, analysed with ffmpeg -af volumedetect, system volume 100%, not muted. Intro.wmv (WMA2, 44100 Hz, stereo, 128 kbit/s; WMV3 video, 640x480): while the cutscene plays, ASF Reader as source filter: mean -91.0 dB, max -91.0 dB (digital silence) main menu music for reference (played by the game's own Miles engine, not through DirectShow): mean -36.3 dB, max -23.7 dB So the silence is specific to the DirectShow path, and the audio device is fine. Second symptom: hang on aborting playback ----------------------------------------- Pressing Escape during a cutscene tears the graph down while it is running. The game window disappears, the process stays alive and one thread spins at 100% CPU forever: wmvcore/winegstreamer loops in wg_parser_get_next_read_offset() on the same offset and never leaves. When a cutscene is allowed to finish normally the loop does terminate ("Reader is shutting down; exiting"); on an abort it does not. The same happens when quitting the game from the main menu after a cutscene has been played. Both symptoms have the same origin: the WM ASF reader (qasf + wmvcore) being in the graph at all. There is no bugzilla entry mentioning wg_parser_get_next_read_offset. Confirmation that this is the right place ----------------------------------------- Switching the source filter for the ASF media type away from the ASF reader, in the prefix registry: HKCR\Media Type\{e436eb83-524f-11ce-9f53-0020af0ba770}\{6b6d0801-9ada-11d0-a520-00a0d10129c0} "Source Filter" = {187463a0-5bb7-11d3-acbe-0080c75e246e} (WM ASF Reader) -> {e436ebb5-524f-11ce-9f53-0020af0ba770} (File Source (Async.)) makes File Source (Async.) the source. It has a single output pin, so amstream's single-pin walk happens to be enough; quartz then autoplugs the GStreamer splitter filter {F9D8D64E-A144-47DC-8EE0-F53498372C29}, and because the splitter is an *inserted* filter, quartz does render both of its output pins recursively. cutscene audio after the change: mean -29.6 dB, max -14.1 dB (8 s window) mean -22.0 dB, max -0.4 dB (whole 143 s cutscene) Escape during a cutscene: returns to the main menu, CPU back to ~19% quitting the game: exits cleanly Picture, resolution and the game's own audio are unaffected either way. This is exactly the workaround that was applied upstream for bug 53748 (same game, same symptom): commit 409d0f4247ea reverted the ASF media type registration in dlls/qasf "so that the File Source filter is preferred instead". That hides the amstream defect but does not fix it -- any environment where the ASF reader is registered as the ASF source filter, as in the Proton prefix here, walks straight back into it, and any future attempt to enable the ASF reader upstream will too. Correct fix ----------- OpenFile() should render every output pin of the source filter, not just the first. A patch is attached. It walks the enumerator, renders each output pin, keeps the first output pin in This->ipin for multimedia_stream_Render(), and reports success when at least one pin could be rendered. It also fixes three smaller issues in the same block: IEnumPins_Next() returning S_FALSE passing SUCCEEDED() and leaving ipin uninitialised, the leaked reference when the first pin is an input pin, and This->ipin being overwritten without releasing the previous reference. The patch applies cleanly to master (11.15) and to 11.0. Wine was built with it (--enable-archs=x86_64) and dlls/amstream's test suite passes, with and without the patch, so it causes no regression. I have not run the patched build against the game itself: the build is 64-bit only and sacred.exe is 32-bit. The evidence that this is the right place is the registry experiment above, not the patched build. There is currently no test coverage for a multi-pin source: test_openfile() and test_mmstream_get_duration() in dlls/amstream/tests/amstream.c both load test.avi through the async reader, and struct testfilter has a single strmbase_source. A test is cheap to add using the existing mock graph in the same file -- graph_AddSourceFilter() and graph_RenderEx() are currently ok(0, "Unexpected call.\n") stubs; making the former return a two-output-pin filter and counting calls to the latter fails with 1 instead of 2 before the patch. That stays valid on Windows, since it only observes how many times amstream calls RenderEx(). Related bugs ------------ 53748 - same game, same symptom, closed by the qasf revert rather than fixed 39597 - open; its last comment already quotes the offending RenderEx() call 25329 - open umbrella bug about WM ASF Reader being needed for .wmv intros 51324 - the one previous real fix to this function (AMMSF_RENDERALLSTREAMS, Wine 6.12) Steps to reproduce ------------------ 1. Install Sacred Gold (Steam appid 12320, or the GOG build). 2. In the prefix registry, make sure the source filter for the ASF media type is the WM ASF reader (this is the case in a Proton 11.0 prefix out of the box): HKCR\Media Type\{e436eb83-524f-11ce-9f53-0020af0ba770} \{6b6d0801-9ada-11d0-a520-00a0d10129c0} "Source Filter" = {187463a0-5bb7-11d3-acbe-0080c75e246e} 3. Start the game with WINEDEBUG=+quartz. 4. The opening cutscene plays with picture and no sound. 5. In the trace, FilterGraph2_RenderEx appears exactly once, for the pin named "Raw Video 0"; the pin "Raw Audio 1" is never rendered. 6. Press Escape during a cutscene: the window disappears and the process spins at 100% CPU indefinitely. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.