From 483befeb14490dcc35cc744a26e2c4810d54ec64 Mon Sep 17 00:00:00 2001 From: scream870102 Date: Thu, 16 Jul 2026 20:55:43 +0800 Subject: [PATCH 1/2] fix: treat PlaceMarker failure as non-fatal and shut down sink on error On some Windows builds (observed on 26200 with mfmp4srcsnk.dll 10.0.26100.8457) the OS MPEG4 media sink rejects every IMFStreamSink::PlaceMarker call with MF_E_INVALIDTYPE, which killed the stream event loop and made export fail with "channel closed" (#156). - PlaceMarker(ENDOFSEGMENT) failure is now logged and ignored so finalization still runs and writes the moov box. - The muxer task now calls sink.Shutdown() on the error path so the output file handle is not leaked until process exit. Co-Authored-By: Claude Fable 5 --- .../crates/unienc_windows_mf/src/mux/mod.rs | 127 +++++++++++------- 1 file changed, 77 insertions(+), 50 deletions(-) diff --git a/InstantReplay.Externals/unienc/crates/unienc_windows_mf/src/mux/mod.rs b/InstantReplay.Externals/unienc/crates/unienc_windows_mf/src/mux/mod.rs index a1b63bb9..03579871 100644 --- a/InstantReplay.Externals/unienc/crates/unienc_windows_mf/src/mux/mod.rs +++ b/InstantReplay.Externals/unienc/crates/unienc_windows_mf/src/mux/mod.rs @@ -97,64 +97,81 @@ impl MediaFoundationMuxer { let sink = UnsafeSend(unsafe { MFCreateMPEG4MediaSink(&*file, &*video_type, &*audio_type)? }); - assert_eq!( - unsafe { sink.GetCharacteristics()? } & MEDIASINK_RATELESS, - MEDIASINK_RATELESS - ); - let finalizable = sink.cast::().ok().map(UnsafeSend); - let sink_count = unsafe { sink.GetStreamSinkCount()? }; - assert_eq!(sink_count, 2); - let (video_stream, video_finish_rx) = - Stream::new(unsafe { sink.GetStreamSinkByIndex(0)? }, &runtime_clone)?; - let (audio_stream, audio_finish_rx) = - Stream::new(unsafe { sink.GetStreamSinkByIndex(1)? }, &runtime_clone)?; - - { - let presentation_clock = unsafe { MFCreatePresentationClock()? }; - let time_source = unsafe { MFCreateSystemTimeSource()? }; - unsafe { presentation_clock.SetTimeSource(&time_source)? }; - unsafe { sink.SetPresentationClock(&presentation_clock)? }; - - unsafe { presentation_clock.Start(0)? }; - } + let sink_inner = UnsafeSend(sink.clone()); + let result: Result<()> = async move { + let sink = sink_inner; + assert_eq!( + unsafe { sink.GetCharacteristics()? } & MEDIASINK_RATELESS, + MEDIASINK_RATELESS + ); + let finalizable = + sink.cast::().ok().map(UnsafeSend); + let sink_count = unsafe { sink.GetStreamSinkCount()? }; + assert_eq!(sink_count, 2); + let (video_stream, video_finish_rx) = + Stream::new(unsafe { sink.GetStreamSinkByIndex(0)? }, &runtime_clone)?; + let (audio_stream, audio_finish_rx) = + Stream::new(unsafe { sink.GetStreamSinkByIndex(1)? }, &runtime_clone)?; - video_stream_tx - .send(Ok(video_stream)) - .map_err(|_| WindowsError::StreamSendFailed)?; - audio_stream_tx - .send(Ok(audio_stream)) - .map_err(|_| WindowsError::StreamSendFailed)?; + { + let presentation_clock = unsafe { MFCreatePresentationClock()? }; + let time_source = unsafe { MFCreateSystemTimeSource()? }; + unsafe { presentation_clock.SetTimeSource(&time_source)? }; + unsafe { sink.SetPresentationClock(&presentation_clock)? }; - video_finish_rx.await?; - audio_finish_rx.await?; + unsafe { presentation_clock.Start(0)? }; + } - if let Some(finalizable) = finalizable { - let finalizable = UnsafeSend(finalizable); + video_stream_tx + .send(Ok(video_stream)) + .map_err(|_| WindowsError::StreamSendFailed)?; + audio_stream_tx + .send(Ok(audio_stream)) + .map_err(|_| WindowsError::StreamSendFailed)?; + + video_finish_rx.await?; + audio_finish_rx.await?; + + if let Some(finalizable) = finalizable { + let finalizable = UnsafeSend(finalizable); + + let finalizable_clone = UnsafeSend(finalizable.clone()); + let (done_tx, done_rx) = oneshot::channel(); + + { + let callback: IMFAsyncCallback = + AsyncCallback::new(move |result| unsafe { + let result: windows_core::Result<()> = (move || { + finalizable_clone.EndFinalize(result.ok()?)?; + Ok(()) + })(); + let _ = done_tx.send(result); + }) + .into(); + + unsafe { + finalizable.BeginFinalize(&callback, Option::<&IUnknown>::None) + }?; + } - let finalizable_clone = UnsafeSend(finalizable.clone()); - let (done_tx, done_rx) = oneshot::channel(); + done_rx + .await + .map_err(|e| WindowsError::Other(e.to_string()))??; - { - let callback: IMFAsyncCallback = AsyncCallback::new(move |result| unsafe { - let result: windows_core::Result<()> = (move || { - finalizable_clone.EndFinalize(result.ok()?)?; - Ok(()) - })(); - let _ = done_tx.send(result); - }) - .into(); - - unsafe { finalizable.BeginFinalize(&callback, Option::<&IUnknown>::None) }?; + let _ = unsafe { sink.Shutdown() }; } - done_rx - .await - .map_err(|e| WindowsError::Other(e.to_string()))??; + Result::<()>::Ok(()) + } + .await; + // Release the output file even when muxing fails; otherwise the + // sink keeps the file handle open until the process exits. + if result.is_err() { let _ = unsafe { sink.Shutdown() }; } - Result::<()>::Ok(()) + result } .await; @@ -204,13 +221,23 @@ impl Stream { if let Some(sample) = sample_rx.recv().await { unsafe { stream_cap.ProcessSample(&*sample)? }; } else { - unsafe { + // Some Windows builds (observed on 26200 with + // mfmp4srcsnk.dll 10.0.26100.8457) reject PlaceMarker with + // MF_E_INVALIDTYPE for every marker type. The marker only + // tells us the sink consumed everything; treat failure as + // non-fatal so finalization still runs and writes the moov. + if let Err(e) = unsafe { stream_cap.PlaceMarker( MFSTREAMSINK_MARKER_ENDOFSEGMENT, std::ptr::null(), std::ptr::null(), - )? - }; + ) + } { + println!( + "PlaceMarker(ENDOFSEGMENT) failed (non-fatal): {:?}", + e + ); + } if let Some(finish_tx) = finish_tx.take() { finish_tx .send(()) From 2fb5def05ea3e0aa161002f860a23d9d3aeb0689 Mon Sep 17 00:00:00 2001 From: ruccho Date: Fri, 17 Jul 2026 16:10:03 +0900 Subject: [PATCH 2/2] fmt --- .../unienc/crates/unienc_windows_mf/src/mux/mod.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/InstantReplay.Externals/unienc/crates/unienc_windows_mf/src/mux/mod.rs b/InstantReplay.Externals/unienc/crates/unienc_windows_mf/src/mux/mod.rs index 03579871..a1642ef4 100644 --- a/InstantReplay.Externals/unienc/crates/unienc_windows_mf/src/mux/mod.rs +++ b/InstantReplay.Externals/unienc/crates/unienc_windows_mf/src/mux/mod.rs @@ -104,8 +104,7 @@ impl MediaFoundationMuxer { unsafe { sink.GetCharacteristics()? } & MEDIASINK_RATELESS, MEDIASINK_RATELESS ); - let finalizable = - sink.cast::().ok().map(UnsafeSend); + let finalizable = sink.cast::().ok().map(UnsafeSend); let sink_count = unsafe { sink.GetStreamSinkCount()? }; assert_eq!(sink_count, 2); let (video_stream, video_finish_rx) = @@ -144,7 +143,8 @@ impl MediaFoundationMuxer { let result: windows_core::Result<()> = (move || { finalizable_clone.EndFinalize(result.ok()?)?; Ok(()) - })(); + })( + ); let _ = done_tx.send(result); }) .into(); @@ -233,10 +233,7 @@ impl Stream { std::ptr::null(), ) } { - println!( - "PlaceMarker(ENDOFSEGMENT) failed (non-fatal): {:?}", - e - ); + println!("PlaceMarker(ENDOFSEGMENT) failed (non-fatal): {:?}", e); } if let Some(finish_tx) = finish_tx.take() { finish_tx