Summary
When publishing from a mobile device (e.g. iPhone camera via <hang-publish>), the video orientation is incorrect on the watch side. The phone captures in landscape natively (e.g. 640x480) even when held in portrait, but no rotation metadata is included in the catalog, and the watch-side renderer doesn't apply rotation even if it were present.
Steps to Reproduce
- Open a
<hang-publish> page on an iPhone (using WebSocket fallback via @moq/web-transport-ws)
- Select camera source, hold phone in portrait orientation
- Open the corresponding
<hang-watch> page in another browser
- Video appears rotated 90 degrees — the viewer sees the image sideways
Observed Behavior
- Catalog contains
640x480 coded dimensions with no rotation field
- The watch-side canvas renderer (
watch/video/renderer.js) handles flip but not rotation
- The publish side (
publish/video/index.d.ts) exposes flip as a Signal but has no rotation Signal
Expected Behavior
- The publish side should detect device orientation (e.g. via
VideoFrame.rotation, window.screen.orientation, or MediaStreamTrack settings) and include rotation in the catalog
- The watch-side renderer should read
catalog.rotation and apply the appropriate transform when drawing frames to canvas
Analysis
The catalog schema already supports rotation (it appears in the type definitions for both publish and watch catalog types). The gap is:
- Publish:
Video.Root has a flip: Signal<boolean> but no corresponding rotation signal, so rotation is never set in the catalog
- Watch:
renderer.js lines 100-105 check catalog.flip and apply ctx.scale(-1, 1) but have no corresponding rotation logic
Environment
@moq/hang v0.1.2
@moq/web-transport-ws (WebSocket fallback)
- Publishing from iPhone Safari (portrait), watching in Chrome desktop
- Relay:
cdn.moq.dev
Workaround
For now we're applying a CSS rotation on the watch side based on aspect ratio heuristics, but this is fragile and doesn't handle all cases correctly.
Summary
When publishing from a mobile device (e.g. iPhone camera via
<hang-publish>), the video orientation is incorrect on the watch side. The phone captures in landscape natively (e.g. 640x480) even when held in portrait, but norotationmetadata is included in the catalog, and the watch-side renderer doesn't apply rotation even if it were present.Steps to Reproduce
<hang-publish>page on an iPhone (using WebSocket fallback via@moq/web-transport-ws)<hang-watch>page in another browserObserved Behavior
640x480coded dimensions with norotationfieldwatch/video/renderer.js) handlesflipbut notrotationpublish/video/index.d.ts) exposesflipas a Signal but has norotationSignalExpected Behavior
VideoFrame.rotation,window.screen.orientation, or MediaStreamTrack settings) and includerotationin the catalogcatalog.rotationand apply the appropriate transform when drawing frames to canvasAnalysis
The catalog schema already supports
rotation(it appears in the type definitions for both publish and watch catalog types). The gap is:Video.Roothas aflip: Signal<boolean>but no correspondingrotationsignal, so rotation is never set in the catalogrenderer.jslines 100-105 checkcatalog.flipand applyctx.scale(-1, 1)but have no corresponding rotation logicEnvironment
@moq/hangv0.1.2@moq/web-transport-ws(WebSocket fallback)cdn.moq.devWorkaround
For now we're applying a CSS rotation on the watch side based on aspect ratio heuristics, but this is fragile and doesn't handle all cases correctly.