Repository navigation
fix(media): report unavailable MediaSource playback as unsupported - #1588
lanyue-llk wants to merge 4 commits into
Conversation
68a1a9c to
d7d7901
Compare
Sequential Navigation Soak A/B✅ HEAD completed the 200-navigation resilience and memory observation. One browser process, one target, and one CDP session navigate CSDN → SegmentFault → Huaban → example.com repeatedly. This run issued 200 Common ancestor Session resilience
A public-page failure is reported separately from an unrecoverable session. The soak requires all navigation attempts, zero failed recovery, zero lifecycle/network ordering violations, and complete resource evidence. Process-tree memory
HEAD memory by 50-navigation quarter
Raw reports and per-navigation boundary samples: workflow run and full Public-site timing and memory are observational. Use the A/B deltas and trend shape as evidence, not as a deterministic performance threshold. |
Spider Bench A/BPublic HEAD: 169 / 240 rows; 35 / 48 sites produced rows; 13 unexpected empty sites. ✅ Deterministic fixture contract is clean. Common ancestor Public 48-site run · informational
Public-site content, timing, and memory are noisy. This single A/B run reports evidence only; site outcome counts explain missing rows without treating them as a deterministic regression. CPU and memory timelinesThese are bounded 40-point views of the same complete process-tree samples used by the HTML report. CPU uses 100% per occupied logical core; memory lines are RSS first and PSS second when complete PSS samples are available. Basexychart-beta
title "Base CPU"
x-axis "Elapsed seconds" [1.536, 2.537, 4.039, 5.041, 5.542, 6.541, 7.042, 8.044, 9.547, 11.049, 12.551, 13.553, 15.056, 16.559, 17.559, 19.061, 20.564, 22.066, 23.068, 24.57, 25.07, 26.07, 27.072, 28.572, 30.074, 31.577, 32.579, 34.08, 35.581, 37.084, 38.085, 39.588, 41.088, 42.09, 43.594, 45.096, 46.598, 47.6]
y-axis "CPU percent" 0 --> 200
line [119.86, 127.86, 139.74, 121.74, 133.96, 110.05, 145.86, 85.83, 49.9, 109.95, 25.95, 0, 77.85, 107.95, 42.01, 5.99, 37.92, 70.03, 115.71, 100.01, 114.04, 101.86, 105.93, 115.93, 117.95, 101.92, 115.89, 43.98, 113.94, 97.77, 9.99, 105.93, 101.88, 99.84, 99.23, 105.85, 13.98, 131.65]
xychart-beta
title "Base memory: RSS then PSS"
x-axis "Elapsed seconds" [0.035, 1.536, 2.537, 4.039, 5.041, 5.542, 6.541, 7.042, 8.044, 9.547, 11.049, 12.551, 13.553, 15.056, 16.559, 17.559, 19.061, 20.564, 22.066, 23.068, 24.57, 25.07, 26.07, 27.072, 28.572, 30.074, 31.577, 32.579, 34.08, 35.581, 37.084, 38.085, 39.588, 41.088, 42.09, 43.594, 45.096, 46.598, 47.6, 49.037]
y-axis "Memory MiB" 0 --> 500
line [13.05, 97.93, 109.6, 137.5, 154.31, 144.66, 132.29, 145.82, 130.31, 148.52, 131.78, 138.15, 122.49, 125.71, 153.11, 123.59, 120.39, 129.7, 118.42, 135.41, 232.69, 268.57, 184.93, 207.49, 137.5, 144.29, 145.71, 146.05, 141.16, 234.34, 156.41, 147.09, 148.74, 172.79, 172.92, 173.05, 162.75, 127.22, 151.49, 0]
line [9.81, 94.87, 106.59, 134.54, 151.38, 141.67, 129.54, 142.97, 127.44, 145.31, 129.08, 135.28, 119.65, 122.82, 150.37, 120.9, 117.51, 126.79, 115.79, 132.46, 229.51, 265.32, 181.67, 204.43, 134.31, 141.14, 143.05, 142.93, 138.12, 231.1, 153.27, 143.93, 146.05, 169.51, 169.63, 169.76, 159.78, 123.94, 148.37, 0]
HEADxychart-beta
title "HEAD CPU"
x-axis "Elapsed seconds" [1.036, 2.037, 2.538, 3.538, 4.54, 6.041, 7.042, 8.043, 9.043, 10.546, 11.546, 12.547, 14.05, 15.051, 16.051, 16.551, 17.553, 18.554, 19.555, 21.056, 22.059, 23.06, 24.061, 25.562, 26.565, 27.567, 29.068, 30.071, 31.073, 32.574, 33.575, 34.076, 34.577, 35.579, 37.08, 38.082, 39.083, 40.584]
y-axis "CPU percent" 0 --> 200
line [31.97, 79.91, 133.87, 111.98, 147.71, 124.03, 75.94, 147.8, 44, 49.9, 130.01, 58.01, 35.91, 2, 40.01, 157.95, 67.84, 107.96, 99.84, 101.98, 109.82, 73.96, 119.97, 99.86, 105.79, 109.74, 12, 139.6, 103.77, 51.97, 15.98, 0, 13.97, 123.74, 99.93, 99.75, 99.89, 99.89]
xychart-beta
title "HEAD memory: RSS then PSS"
x-axis "Elapsed seconds" [0.036, 1.036, 2.037, 2.538, 3.538, 4.54, 6.041, 7.042, 8.043, 9.043, 10.546, 11.546, 12.547, 14.05, 15.051, 16.051, 16.551, 17.553, 18.554, 19.555, 21.056, 22.059, 23.06, 24.061, 25.562, 26.565, 27.567, 29.068, 30.071, 31.073, 32.574, 33.575, 34.076, 34.577, 35.579, 37.08, 38.082, 39.083, 40.584, 41.57]
y-axis "Memory MiB" 0 --> 500
line [13.07, 83.2, 103.01, 111.66, 123.45, 129.42, 145.1, 153.12, 144.92, 130.46, 135.27, 135.36, 125.83, 143.73, 114.58, 128.52, 142.41, 123.64, 119.71, 168.45, 177.61, 161.7, 143.25, 173.47, 131.07, 138.65, 130.09, 138.11, 192.13, 240.68, 136.86, 125.88, 125.72, 125.74, 156.11, 162.7, 162.73, 162.73, 164.64, 0]
line [9.78, 79.72, 99.56, 108.23, 119.94, 125.98, 141.58, 149.61, 141.4, 126.94, 131.63, 131.84, 122.38, 140.31, 111.06, 125, 138.96, 120.13, 116.19, 165.39, 174.09, 158.98, 139.74, 169.97, 127.5, 135.13, 126.58, 134.67, 188.74, 237.5, 133.38, 122.39, 122.24, 122.34, 152.7, 159.08, 159.12, 159.3, 161.92, 0]
Deterministic fixture · required contractThe fixture exercises 15 routes: 8 are expected to emit rows and 7 intentionally exercise empty, loading, or timeout behavior.
Full HTML, JSON, CSV, logs, and page snapshots: workflow run and full The public-web A/B is informational. The exact HEAD fixture contract runs as a separate required CI check; timing and public-site content deltas remain non-blocking. |
CI Regression ReportSource CI run · source state at render:
Release regression — ✅ HEAD/base failures 0/0; raw binary −0.000066%Package and image size
Startup latency and PSS
Concurrency matrix
Frontend differential — ✅ 1,020/1,020 cases matched; 0 issues
Agent episodes · Moli vs Chromium — ✅ 8/8 Moli episodes passed; 0 failures
Runtime and CDP session contracts — ✅ 26 contract cases; 0 failures
CDP smoke — ✅ 52/52 groups passed; 622 scenariosWorkers: 4 · cumulative group time: 156.67 s · failed groups: 0
WebMainBench · 545 pages — ✅ 545/545 completed; 0 unexpected failures
Frozen HTML · Linux · Diagnostics: All artifact fields are parsed by the trusted default-branch renderer; missing or invalid inputs remain visible as unavailable. |
lanyue-llk
left a comment
There was a problem hiding this comment.
复核提交 d7d79015443c,未发现本次改动新增的明确阻断问题。
当前 MediaSource 只有构造和能力查询表面,没有可用的 SourceBuffer/addSourceBuffer 流程;继续返回 true 会让页面选择无法执行的播放路径。MSE 的能力查询应反映实现实际支持的类型。现阶段统一返回 false 比伪造一份支持列表合适,也无需为此实现整个 MSE。
参数仍先经过 WebIDL 转换,缺参、Symbol 和用户转换异常没有被提前返回吞掉;改动也没有修改普通媒体的 MIME 判断。已核对相关代码和转换测试;本机缺少原生开发工具,未运行该提交的 V8 测试。
|
It's part of fingerprint, I think it's by design. |
d7d7901 to
b7e9d21
Compare
|
I checked this against the implemented surface and kept the change.
The fingerprint concern is real, but advertising an unusable capability is not a safe general fingerprint strategy. If Moli later adds an explicit fingerprint-emulation mode, that should be a separate policy decision rather than the default Web API contract. The branch has also been rebased onto current |
lanyue-llk
left a comment
There was a problem hiding this comment.
This PR changes MediaSource.isTypeSupported() to always return false (after WebIDL argument conversion), because Moli exposes MediaSource without a SourceBuffer/addSourceBuffer lifecycle, so players should take a progressive or other fallback path instead of an MSE path that can never play. Returning false conforms to the MSE spec (a UA may report any type as unsupported) and keeps hls.js/dash.js/video.js-style isTypeSupported gates correct. However, the fix covers only one of the two MSE capability probes and leaves the old predicate as dead code, so I am requesting changes.
[major] navigator.mediaCapabilities.decodingInfo({type: 'media-source'}) still claims support, which now contradicts isTypeSupported. media_capabilities_decoding_info_callback (moli-renderer-v8/src/context_bootstrap/navigator_runtime/media_capabilities.rs:357) computes supported through configuration_supported() -> media_mime_support() and ignores configuration.decoding_type. So decodingInfo({type:'media-source', video:{contentType:'video/mp4; codecs=\"avc1.42E01E\"', ...}}) resolves {supported: true} while MediaSource.isTypeSupported on the same type now returns false. Media Capabilities defines media-source as a configuration for MSE playback, and players such as Shaka and YouTube's player filter variants with decodingInfo (Shaka even polyfills decodingInfo on top of isTypeSupported). Those players can still pick the unusable MSE path, and the two answers are now internally incoherent, which is also a fingerprinting signal. Suggested fix: put the MSE answer in one shared capability function (for example moli_web_mime::media_source_support() returning false until SourceBuffer exists) and call it from both isTypeSupported and the MediaDecodingType::MediaSource branch of decodingInfo. Add a test asserting that both APIs agree.
[minor] Dead code left behind. moli_web_mime::is_media_source_type_supported (moli-web-mime/src/media.rs:34, re-exported in lib.rs:32) no longer has any production caller; only moli-web-mime/src/tests.rs:334-347 uses it, and those tests still assert true for avc1 and so document behavior Moli no longer has. Under the repo's one-implementation rule, either remove the function and its tests, or keep it as the single owner and have it return the honest answer.
[minor] Anti-bot fingerprint impact is not addressed. The flipped expectations live in fixtures named zhihu_bot_detection_harness / zhihu_capability_probe ("stable moli baseline"). The UA is Chrome-like, and real Chrome returns true for avc1.42E01E. Open-source Chromium/headless builds without proprietary codecs return false, a known bot-detection heuristic. The PR rewrites these baselines without saying whether Zhihu (or similar) gates content on this probe. The CI A/B runs (Spider, WebMainBench, soak) show no regression, but they do not exercise Zhihu's anti-bot path specifically. Please state the tradeoff in the PR description, or show that the Zhihu fixtures do not gate on avc1.
[nit] Remaining partial-exposure limitation. window.MediaSource (constructible) and SourceBuffer/SourceBufferList (illegal-constructor globals) are still exposed, so players that feature-detect only by 'MediaSource' in window still take MSE. The PR deliberately accepts this; consider noting it in the PR description so it is not mistaken for full coverage.
Tests: the new test mostly re-asserts the hard-coded false. Its operational/route logic depends only on addSourceBuffer being absent (SourceBuffer is already a global function), and the 'video/mp4' and '' cases returned false before this PR too, so only the first two claims fail without the fix. Acceptable, but the cross-API agreement test above would be more valuable. CI: all checks pass (fmt, clippy, nextest, A/B benches).
Verdict: changes requested (posted as a comment because GitHub does not allow requesting changes on your own PR).
Automated review by Claude Code.
ece5986 to
f39415b
Compare
lanyue-llk
left a comment
There was a problem hiding this comment.
Re-review of the updated version (f39415bc5)
Round 2. The PR now reports MSE as unavailable from both MediaSource.isTypeSupported() and navigator.mediaCapabilities.decodingInfo({type:'media-source'}), gated through one shared moli_web_mime::media_source_available(). File, WebRTC and canPlayType answers are unchanged. The design is right, but the new gate breaks an existing test that still expects media-source support, and Nextest is red on the new head. Verdict: request changes.
Previous findings
| # | Finding | Status | Evidence |
|---|---|---|---|
| 1 | [major] decodingInfo({type:'media-source'}) disagreed with isTypeSupported |
FIXED | media_capabilities.rs:522-528 returns false for Decoding(MediaSource) when !media_source_available(). media_source.rs:51 goes through is_media_source_type_supported, which is gated by the same function. |
| 2 | [nit] no-op drop(parsed.media_type) |
FIXED (moot) | The callback now passes parsed.media_type to the shared predicate (media_source.rs:51). |
| 3 | [minor] test only re-asserted the hard-coded false |
FIXED | media.rs:2150-2181 checks both APIs for two MP4 types. Without the decodingInfo gate it would produce [[false,true],...] and fail. |
| 4 | [minor] dead is_media_source_type_supported |
PARTIAL | The function has a caller again, but its codec body is now unreachable behind a const false and has lost its test coverage. It also does not match decodingInfo's type rules. See new finding 2. |
| 5 | [minor] anti-bot fingerprint tradeoff undocumented | PUSHBACK PARTLY ACCEPTED | The author's argument holds: advertising an unusable capability is not a sound default. The PR body still does not state the visible change, though, and the scope grew: Chrome-UA pages now also see decodingInfo media-source false for VP9/AV1, and canPlayType still says probably. Maintainer @ldm0 commented "It's part of fingerprint, I think it's by design", and that thread has no resolution. |
| 6 | [nit] window.MediaSource/SourceBuffer still exposed |
NOT ADDRESSED (accepted limitation) | The PR body still does not mention it. Fine to leave, but say it in one line. |
New findings
- [blocker] An existing test now fails.
navigator_devices.rs::media_capabilities_codec_colors_constrain_support_without_rejecting_the_dictionaryrunsmedia_capabilities_codec_color.js, which loopsfor (const type of ['file','media-source'])(line 261) and requiressupported === expected. Itsexpectedignorestypeand istruefor every well-formed VP9/AV1 case. With the new gate every media-source row returnsfalse, so the test fails. This matches the Nextest failure (exit 100) on f39415b; full logs are not available yet because the run is still in progress. Fix: makeexpectedincludetype === 'file', or keep the color matrix onfileonly and add one media-source row that assertssupported:false, smooth:false, powerEfficient:false. The PR's suggested-Efilter misses this test, so run all ofnavigator_devices/media_capabilities_*. - [minor] Only the availability gate is shared, not the type decision. Once
media_source_available()becomestrue,isTypeSupportedwould accept onlyvideo/mp4+avc1.*, while decodingInfo accepts anyvideo/mp4|webm|oggthroughmedia_mime_support. For example,video/webm; codecs="vp09.00.10.08"would givefalsefrom one API andtruefrom the other, which brings back the round-1 disagreement. Today the avc1 body is unreachable, and the tests that covered it (case-insensitiveVideo/MP4,hev1,notvideo/mp4) were deleted. Fix: have onemedia_source_type_supported(content_type)that both callers use (decodingInfo per audio/video member), or makeis_media_source_type_supportedjustmedia_source_available() && media_mime_support(t) != Unsupported. Then delete the avc1-only branch rather than keep untested dead logic. - [nit] The coherence test does not show the gate is scoped to media-source. Add the same content type with
type:'file'and expecttrue, so a regression that disables all decodingInfo support fails here too. - [nit] PR body and fingerprint. Add one sentence on the observable change for Chrome-UA probes (MSE
isTypeSupportedanddecodingInfomedia-source bothfalse,canPlayTypeunchanged,MediaSource/SourceBufferglobals still present). Get an explicit maintainer OK on @ldm0's "by design" comment before marking ready. Author thread replies citeece5986ef, which is not in the PR (head is f39415b).
CI / merge: On f39415b, Nextest and CDP smoke (Chromium) fail, and both release builds are queued. CDP smoke has also failed on a recent main run (45be4e3), so it may be unrelated, but I could not confirm that. All A/B bot reports are for the old head b7e9d21. git merge-tree --write-tree origin/main (14d3c08) merges cleanly. Commit stack: 2 commits, both on topic.
Verdict: changes requested (posted as a comment because GitHub does not allow requesting changes on your own PR).
Automated review by Claude Code.
lanyue-llk
left a comment
There was a problem hiding this comment.
Final re-review (P0/P1 only) (6cad52920)
Final round (head 6cad529). Verdict: approve, provided Nextest goes green. The round-2 blocker is fixed in the fixture. isTypeSupported and decodingInfo({type:'media-source'}) now share one complete type predicate.
| # | Previous finding | Status | Evidence |
|---|---|---|---|
| R1-1 | [major] decodingInfo media-source disagreed with isTypeSupported |
FIXED | media_capabilities.rs:522-532: every audio/video member goes through moli_web_mime::is_media_source_type_supported. media_source.rs:51 calls the same predicate. |
| R2-1 | [blocker] navigator_devices.rs::media_capabilities_codec_colors_constrain_support_without_rejecting_the_dictionary broken, Nextest red |
FIXED (static) | media_capabilities_codec_color.js:266: expected = type === 'file' && .... Media-source rows now expect supported:false. smooth and powerEfficient are always false (media_capabilities.rs:303-306), so the (supported || (!smooth && !powerEfficient)) guard passes too. I checked the other fixtures that query media-source (media_capabilities_mime.js:671, media_capabilities_video_options.js). They only assert supported===false or a rejection, so they still pass. window_surface.rs:620 and platform_compat.rs:186/419 already expect false. |
| R2-2 | Type rules differ between the two APIs (latent regression of R1-1) | FIXED | moli-web-mime/src/media.rs:45-47: media_source_available() && media_mime_support(input) != Unsupported. The avc1-only branch is gone. Both APIs now use the same rule per member; decodingInfo additionally applies the codec color check, which is correct. Test added at tests.rs:341 (webm vp09 → false). |
| R2-3 | [nit] no file control in the coherence test |
FIXED | media.rs:2167-2183 expects [[false,false,true],[false,false,true]]. |
New P0/P1 findings: none. The delta from the previous round is 5 files and +21/-16 lines, all scoped to the MSE gate and its tests.
CI on 6cad529: Format, Clippy, Windows persistent storage, Node harness, Python harness and CDP smoke (Chromium) pass. CDP smoke failed last round and now passes. Nextest and both release builds are still queued. The Nextest log from the previous head is gone (BlobNotFound), so I could not confirm that the codec-color test was its only failure. Approve only after Nextest is green. The PR merges cleanly with origin/main (14d3c08). It is still a draft.
Verdict: approve (posted as a comment; self-approval is not allowed).
Automated review by Claude Code.
|
Follow-up on the current-head Nextest failure: the capability implementation was correct; the new test observed a two-stage async/await chain after only the test VM checkpoint. Commit 70465db issues all four capability promises before one |
Problem
Moli reported Media Source Extensions (MSE) as supported for some codecs even though it does not implement the
SourceBufferplayback lifecycle. The two public probes could also disagree:MediaSource.isTypeSupported()andMediaCapabilities.decodingInfo({ type: "media-source" })did not share the complete capability decision.That sends feature-detecting players toward a playback path that cannot run instead of allowing an ordinary media fallback.
Change
Both public APIs now use one shared MSE type predicate. It combines the MIME decision with the actual MSE pipeline availability, which remains false until Moli implements the
SourceBufferlifecycle. This also keeps the APIs coherent if that availability changes later.The observable Chrome-UA fingerprint is explicit:
MediaSource.isTypeSupported()and media-sourcedecodingInfo()return unsupported, including VP9 and AV1 configurations.HTMLMediaElement.canPlayType()is unchanged. TheMediaSource,SourceBuffer, andSourceBufferListglobals remain exposed, so pages that check only for a global still see the partial surface.The maintainer concern that this probe contributes to fingerprinting is valid. Reporting an unusable playback path as supported would break ordinary feature detection, so this PR keeps the default API answer tied to the implemented capability rather than adding a site-specific fingerprint exception.
Reproduce and verify
For the same H.264 content type, compare:
The two MSE probes return unsupported while the file control remains supported. The codec-color matrix now expects unsupported for every media-source row and continues to validate file decoding across VP9/AV1 color configurations.
cargo nextest run --locked -p moli-renderer-v8 --lib \ -E 'test(media_source_) | test(media_capabilities_) | test(zhihu_capability_probe_) | test(zhihu_bot_detection_harness_) | test(zhihu_probe_window_)' \ --no-fail-fast cargo nextest run --locked -p moli-web-mime --lib --no-fail-fastLocal execution on macOS is blocked before compilation because Apple Command Line Tools are unavailable.
cargo fmt --all -- --checkandgit diff --checkpass. CI runs the Rust suites.The current 430-task Web Fetch cohort does not score MSE playback, so this PR makes no benchmark-score claim.
Dependencies
Independent change targeting
main; no other open PR is required.