Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions entry/src/main/ets/pages/StreamPage.ets
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@ struct StreamPage {
},
onToggleWindowMode: async () => {
await this.windowManager?.toggleWindowMode();
this.recalculateXComponentSize();
await this.recalculateXComponentSize();
},
onTogglePanZoom: () => {
this.isTouchOverrideEnabled = !this.isTouchOverrideEnabled;
Expand Down Expand Up @@ -1044,15 +1044,28 @@ struct StreamPage {

if (this.windowManager.isFullscreen) {
// 全屏:按屏幕尺寸精确计算 letterbox/pillarbox
const screenSize = await this.windowManager.updateScreenSize();
this.viewModel.displayInfo.screenWidth = screenSize.width;
this.viewModel.displayInfo.screenHeight = screenSize.height;
this.panZoomHandler.setParentSize(screenSize.width, screenSize.height);
const size = this.windowManager.calculateXComponentSize(
this.viewModel.displayInfo.stretchVideo
);
this.xComponentWidth = size.width;
this.xComponentHeight = size.height;
} else {
// 窗口模式:直接填满窗口,视频缩放由 Surface 处理
this.xComponentWidth = '100%';
this.xComponentHeight = '100%';
// 窗口/浮窗:按实际窗口内容区等比适配,避免 Surface 被父容器裁剪
const viewportSize = await this.windowManager.getCurrentWindowViewportSize();
const size = this.windowManager.calculateXComponentSizeForViewport(
viewportSize.width,
viewportSize.height,
this.viewModel.displayInfo.stretchVideo
);
this.viewModel.displayInfo.screenWidth = viewportSize.width;
this.viewModel.displayInfo.screenHeight = viewportSize.height;
this.panZoomHandler.setParentSize(viewportSize.width, viewportSize.height);
this.xComponentWidth = size.width;
this.xComponentHeight = size.height;
}
// 更新画面位置和偏移
this.updateDisplayPosition();
Expand Down
70 changes: 44 additions & 26 deletions entry/src/main/ets/service/streaming/StreamLifecycleManager.ets
Original file line number Diff line number Diff line change
Expand Up @@ -193,21 +193,31 @@ export class StreamLifecycleManager {
*/
async updateScreenSizeAfterRotation(): Promise<void> {
try {
const screenSize = await this.windowManager?.updateScreenSize();
if (screenSize) {
if (this.viewModel) {
this.viewModel.displayInfo.screenWidth = screenSize.width;
this.viewModel.displayInfo.screenHeight = screenSize.height;
}
if (!this.windowManager) return;

let viewportSize = await this.windowManager.updateScreenSize();
if (!this.windowManager.isFullscreen) {
viewportSize = await this.windowManager.getCurrentWindowViewportSize();
}

if (this.viewModel) {
this.viewModel.displayInfo.screenWidth = viewportSize.width;
this.viewModel.displayInfo.screenHeight = viewportSize.height;
}

// 重新计算 XComponent 尺寸
const xComponentSize = this.windowManager?.calculateXComponentSize(
// 重新计算 XComponent 尺寸
const xComponentSize = this.windowManager.isFullscreen
? this.windowManager.calculateXComponentSize(
this.viewModel?.displayInfo.stretchVideo ?? false
)
: this.windowManager.calculateXComponentSizeForViewport(
viewportSize.width,
viewportSize.height,
this.viewModel?.displayInfo.stretchVideo ?? false
);
if (xComponentSize) {
this.callbacks?.updateXComponentSize(xComponentSize.width, xComponentSize.height);
this.callbacks?.updatePanZoomParentSize(screenSize.width, screenSize.height);
}
if (xComponentSize) {
this.callbacks?.updateXComponentSize(xComponentSize.width, xComponentSize.height);
this.callbacks?.updatePanZoomParentSize(viewportSize.width, viewportSize.height);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
} catch (err) {
console.warn('[StreamPage] 更新屏幕尺寸失败:', err);
Expand All @@ -220,29 +230,37 @@ export class StreamLifecycleManager {
*/
async handleServerResolutionChanged(width: number, height: number): Promise<void> {
console.info(`[StreamPage] 服务端分辨率变化: ${width}x${height}`);
if (!this.windowManager) return;

// 更新窗口管理器的串流分辨率
this.windowManager?.setStreamResolution(width, height);
this.windowManager.setStreamResolution(width, height);

// 重新配置窗口方向(可能会触发屏幕旋转)
await this.windowManager?.configureOrientation();
await this.windowManager.configureOrientation();

// 更新屏幕尺寸(等待旋转完成后获取正确的屏幕尺寸)
const screenSize = await this.windowManager?.updateScreenSize();
if (screenSize && this.viewModel) {
this.viewModel.displayInfo.screenWidth = screenSize.width;
this.viewModel.displayInfo.screenHeight = screenSize.height;
// 更新视口尺寸(浮窗模式下使用实际窗口内容区)
let viewportSize = await this.windowManager.updateScreenSize();
if (!this.windowManager.isFullscreen) {
viewportSize = await this.windowManager.getCurrentWindowViewportSize();
}
if (this.viewModel) {
this.viewModel.displayInfo.screenWidth = viewportSize.width;
this.viewModel.displayInfo.screenHeight = viewportSize.height;
}

// 重新计算 XComponent 尺寸以避免画面拉伸
const xComponentSize = this.windowManager?.calculateXComponentSize(
this.viewModel?.displayInfo.stretchVideo ?? false
);
// 重新计算 XComponent 尺寸以避免画面拉伸/裁剪
const xComponentSize = this.windowManager.isFullscreen
? this.windowManager.calculateXComponentSize(
this.viewModel?.displayInfo.stretchVideo ?? false
)
: this.windowManager.calculateXComponentSizeForViewport(
viewportSize.width,
viewportSize.height,
this.viewModel?.displayInfo.stretchVideo ?? false
);
if (xComponentSize) {
this.callbacks?.updateXComponentSize(xComponentSize.width, xComponentSize.height);
if (screenSize) {
this.callbacks?.updatePanZoomParentSize(screenSize.width, screenSize.height);
}
this.callbacks?.updatePanZoomParentSize(viewportSize.width, viewportSize.height);
console.info(`[StreamPage] XComponent 尺寸更新: ${JSON.stringify(xComponentSize)}`);
}
}
Expand Down
97 changes: 86 additions & 11 deletions entry/src/main/ets/service/streaming/StreamWindowManager.ets
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export class StreamWindowManager {
private onWindowSizeChanged: (() => void) | null = null;
/** windowSizeChange 监听器回调引用(用于 off 取消注册) */
private windowSizeChangeHandler: ((size: window.Size) => void) | null = null;
/** windowSizeChange 防抖定时器 */
private windowSizeDebounceTimer: number = -1;

/** 查询当前是否全屏 */
get isFullscreen(): boolean {
Expand Down Expand Up @@ -161,21 +163,23 @@ export class StreamWindowManager {
const win = await window.getLastWindow(this.context);

if (this._isFullscreen) {
// 先注册监听,再执行 recover,避免窗口恢复过程中的首个尺寸事件丢失
this.registerWindowSizeListener(win);
await this.exitImmersiveFullscreen(win);
try {
await win.recover();
} catch (_e) {
console.info('StreamWindowManager: recover() 不可用,保持最大化');
}
this._isFullscreen = false;
// 注册窗口大小变化监听(窗口模式下用户可能拖拽缩放)
this.registerWindowSizeListener(win);
this.notifyWindowSizeChanged();
console.info('StreamWindowManager: 已切换到窗口模式');
} else {
// 取消窗口大小变化监听
this.unregisterWindowSizeListener(win);
await this.enterImmersiveFullscreen(win);
this._isFullscreen = true;
this.notifyWindowSizeChanged();
console.info('StreamWindowManager: 已切换到全屏模式');
}
} catch (err) {
Expand All @@ -188,16 +192,8 @@ export class StreamWindowManager {
*/
private registerWindowSizeListener(win: window.Window): void {
this.unregisterWindowSizeListener(win);
let debounceTimer: number = -1;
this.windowSizeChangeHandler = (_size: window.Size) => {
// 防抖:拖拽缩放时高频触发,200ms 后才真正回调
if (debounceTimer !== -1) {
clearTimeout(debounceTimer);
}
debounceTimer = Number(setTimeout(() => {
debounceTimer = -1;
this.onWindowSizeChanged?.();
}, 200));
this.notifyWindowSizeChanged();
};
try {
win.on('windowSizeChange', this.windowSizeChangeHandler);
Expand All @@ -214,6 +210,51 @@ export class StreamWindowManager {
} catch (_e) {}
this.windowSizeChangeHandler = null;
}
if (this.windowSizeDebounceTimer !== -1) {
clearTimeout(this.windowSizeDebounceTimer);
this.windowSizeDebounceTimer = -1;
}
}

/**
* 防抖通知布局重算。窗口 recover/maximize、用户拖拽和系统布局 settle
* 都从这里汇总,避免页面层用固定延时猜窗口何时稳定。
*/
private notifyWindowSizeChanged(): void {
if (this.windowSizeDebounceTimer !== -1) {
clearTimeout(this.windowSizeDebounceTimer);
}
this.windowSizeDebounceTimer = Number(setTimeout(() => {
this.windowSizeDebounceTimer = -1;
this.onWindowSizeChanged?.();
}, 120));
}

/**
* 获取当前窗口内容区域尺寸(vp)。
*
* 浮窗/自由窗口下默认屏幕尺寸不等于实际可绘制窗口尺寸,继续按屏幕计算会导致
* XComponent 超出窗口后被父容器裁剪,看起来像画面被放大裁切。
*/
async getCurrentWindowViewportSize(): Promise<DisplaySize> {
try {
const win = await window.getLastWindow(this.context);
const properties = win.getWindowProperties();
let rect: window.Rect = properties.windowRect;
if (properties.drawableRect.width > 0 && properties.drawableRect.height > 0) {
rect = properties.drawableRect;
}
const displayInfo = display.getDefaultDisplaySync();
const density = displayInfo.densityPixels;

return {
width: rect.width / density,
height: rect.height / density
};
} catch (err) {
console.warn('StreamWindowManager: 获取窗口内容区尺寸失败:', err);
return this.getScreenSize();
}
}

// ==================== 沉浸式全屏辅助方法 ====================
Expand Down Expand Up @@ -424,6 +465,40 @@ export class StreamWindowManager {
}
}

/**
* 按指定视口尺寸计算 XComponent 尺寸。
* 全屏和浮窗共用同一套等比适配逻辑,避免窗口模式下 Surface 拉伸/裁剪。
*/
calculateXComponentSizeForViewport(viewportWidth: number, viewportHeight: number,
stretchVideo: boolean = false): XComponentSize {
if (stretchVideo) {
return { width: '100%', height: '100%' };
}
if (viewportWidth <= 0 || viewportHeight <= 0) {
return { width: '100%', height: '100%' };
}

const videoAspectRatio = this.streamWidth / this.streamHeight;
const viewportAspectRatio = viewportWidth / viewportHeight;
let measuredWidth: number;
let measuredHeight: number;

if (viewportAspectRatio > videoAspectRatio) {
measuredHeight = viewportHeight;
measuredWidth = Math.floor(measuredHeight * videoAspectRatio);
} else {
measuredWidth = viewportWidth;
measuredHeight = Math.floor(measuredWidth / videoAspectRatio);
}

console.info('StreamWindowManager: 视口视频尺寸计算 - 视口=' +
viewportWidth.toFixed(1) + 'x' + viewportHeight.toFixed(1) + 'vp, 视频=' +
this.streamWidth.toString() + 'x' + this.streamHeight.toString() + ', XComponent=' +
measuredWidth.toFixed(1) + 'x' + measuredHeight.toFixed(1) + 'vp');

return { width: measuredWidth, height: measuredHeight };
}

// ==================== 初始化 ====================

/**
Expand Down
Loading