Skip to content

Commit

Permalink
feat: fetch canvas size from OBS
Browse files Browse the repository at this point in the history
  • Loading branch information
grantjbutler committed Mar 12, 2022
1 parent be275eb commit 8be8402
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 29 deletions.
4 changes: 4 additions & 0 deletions packages/main/src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,8 @@ export function install(options: InstallationOptions): void {
ipcMain.handle('sync-layout-to-scene', (_, nodes: Node[], sceneName: string) => {
return options.obsSocket.syncLayout(nodes, sceneName);
});

ipcMain.handle('get-obs-canvas-size', () => {
return options.obsSocket.canvasSize;
});
}
25 changes: 25 additions & 0 deletions packages/main/src/obs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import dataUriToBuffer from 'data-uri-to-buffer';
import broadcast from './broadcast';
import type { OBSConnectionOptions, Source } from '../../shared/src/obs';
import { OBSConnectionState } from '../../shared/src/obs';
import type { Size } from '../../shared/src/layout';

interface OBSSocketOptions {
sourceFilter: string
Expand Down Expand Up @@ -42,6 +43,8 @@ export default class OBSSocket {
_sources: Source[] = [];
_scenes: string[] = [];

_canvasSize: Size = { width: 1920, height: 1080 };

constructor(options: OBSSocketOptions) {
this._sourceFilter = options.sourceFilter;

Expand All @@ -51,6 +54,8 @@ export default class OBSSocket {
this._socket.on('SourceRenamed', (data) => this._sourceRenamed(data));

this._socket.on('ScenesChanged', (data) => this._scenesChanged(data));

this._socket.on('ProfileChanged', () => this._profileChanged());
}

connect(options: OBSConnectionOptions): Promise<unknown> {
Expand Down Expand Up @@ -159,8 +164,19 @@ export default class OBSSocket {
this._fetchScenes();
}

get canvasSize(): Size {
return this._canvasSize;
}

set canvasSize(size: Size) {
this._canvasSize = size;

broadcast('obs-canvas-size', size);
}

_fullUpdate(): Promise<unknown> {
return Promise.all([
this._fetchCanvasSize(),
this._fetchSources(),
this._fetchScenes(),
]);
Expand Down Expand Up @@ -232,6 +248,11 @@ export default class OBSSocket {
.then(screenshot => screenshot.img);
}

_fetchCanvasSize(): Promise<void> {
return this._socket.send('GetVideoInfo')
.then(info => { this.canvasSize = { width: info.baseWidth, height: info.baseHeight }; });
}

_sourceCreated({ sourceName, sourceType }: { sourceName: string, sourceType: string }): void {
if (sourceType != 'input') { return; }
this._getSourceSize(sourceName)
Expand Down Expand Up @@ -278,4 +299,8 @@ export default class OBSSocket {
.filter(scene => scene.name.includes(this._sceneFilter))
.map(scene => scene.name);
}

_profileChanged() {
this._fetchCanvasSize();
}
}
3 changes: 3 additions & 0 deletions packages/preload/exposedInMainWorld.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { OBSConnectionOptions, OBSConnectionState, Source } from '../shared/src/obs';
import type { MenuItemDefinition } from '../shared/src/menu';
import type { Size } from '../shared/src/layout';
import type { Node } from './src/obs';

export declare global {
Expand All @@ -17,11 +18,13 @@ export declare global {
getConnectionStatus(): Promise<OBSConnectionState>;
getSources(): Promise<Source[]>;
getScenes(): Promise<string[]>;
getCanvasSize(): Promise<Size>;
sync(nodes: Node[], sceneName: string)

onConnectionStateChanged: (handler: (value: OBSConnectionState) => void) => void;
onSourcesChanged: (handler: (value: Source[]) => void) => void;
onScenesChanged: (handler: (value: string[]) => void) => void;
onCanvasSizeChanged: (handler: (value: Size) => void) => void;
};
readonly preferences: {
loadConnectionOptions: () => Promise<OBSConnectionOptions | null>;
Expand Down
9 changes: 9 additions & 0 deletions packages/preload/src/obs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ipcRenderer } from 'electron';
import type { OBSConnectionOptions, OBSConnectionState, Source } from '../../shared/src/obs';
import type { Size } from '../../shared/src/layout';

export function connect(options: OBSConnectionOptions) {
ipcRenderer.send('connect-to-obs', options);
Expand All @@ -25,6 +26,10 @@ export function onScenesChanged(handler: (value: string[]) => void) {
ipcRenderer.on('obs-scenes', (_, value: string[]) => handler(value));
}

export function onCanvasSizeChanged(handler: (value: Size) => void) {
ipcRenderer.on('obs-canvas-size', (_, value: Size) => handler(value));
}

export function getSources(): Promise<Source[]> {
return ipcRenderer.invoke('get-obs-sources');
}
Expand All @@ -33,6 +38,10 @@ export function getScenes(): Promise<string[]> {
return ipcRenderer.invoke('get-obs-scenes');
}

export function getCanvasSize(): Promise<Size> {
return ipcRenderer.invoke('get-obs-canvas-size');
}

export interface Node {
sourceName: string;
frame: {
Expand Down
55 changes: 28 additions & 27 deletions packages/renderer/src/components/Preview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<canvas
ref="canvas"
class="absolute bg-white dark:bg-black"
width="1920"
height="1080"
:width="canvasSize.width"
:height="canvasSize.height"
/>
</div>
</div>
Expand All @@ -17,33 +17,45 @@
<script lang="ts" setup>
import { useLayoutStore } from '/@/store/layout';
import { computed, ref, watch } from 'vue';
import type { LayoutNode, Size } from '/@/layout';
import { ContainerLayoutNode } from '/@/layout';
import type { LayoutNode } from '/@/layout';
import { ContainerLayoutNode , Size } from '/@/layout';
import { usePreferredDark } from '@vueuse/core';
import { useObsStore } from '../store/obs';
const store = useLayoutStore();
const obsStore = useObsStore();
const scale = ref(1);
const canvas = ref<HTMLCanvasElement | null>(null);
const node = computed(() => store.rootNode);
const prefersDarkMode = usePreferredDark();
const canvasSize = computed(() => obsStore.canvasSize);
const previewSize = ref<Size>(new Size(1920, 1080));
const didChangeSize = (newSize: Size) => {
previewSize.value = newSize;
resizeCanvas();
};
watch(canvasSize, () => resizeCanvas());
const resizeCanvas = () => {
let el = canvas.value;
if (!el) { return; }
el.style.transformOrigin = '0 0';
let aspectRatio = 16 / 9;
let width = Math.min(newSize.width, newSize.height * aspectRatio);
let height = Math.min(newSize.height, newSize.width / aspectRatio);
if (newSize.width > newSize.height) {
scale.value = width / 1920;
let aspectRatio = canvasSize.value.width / canvasSize.value.height;
let width = Math.min(previewSize.value.width, previewSize.value.height * aspectRatio);
let height = Math.min(previewSize.value.height, previewSize.value.width / aspectRatio);
if (previewSize.value.width > previewSize.value.height) {
scale.value = width / canvasSize.value.width;
el.style.transform = `scale(${scale.value}, ${scale.value})`;
el.style.left = ((newSize.width - width) / 2) + 'px';
el.style.top = ((newSize.height - (width / aspectRatio)) / 2) + 'px';
el.style.left = ((previewSize.value.width - width) / 2) + 'px';
el.style.top = ((previewSize.value.height - (width / aspectRatio)) / 2) + 'px';
} else {
scale.value = height / 1080;
scale.value = height / canvasSize.value.height;
el.style.transform = `scale(${scale.value}, ${scale.value})`;
el.style.left = ((newSize.width - (height * aspectRatio)) / 2) + 'px';
el.style.top = ((newSize.height - height) / 2) + 'px';
el.style.left = ((previewSize.value.width - (height * aspectRatio)) / 2) + 'px';
el.style.top = ((previewSize.value.height - height) / 2) + 'px';
}
};
Expand Down Expand Up @@ -71,20 +83,9 @@ const render = () => {
if (!rootNode) { return; }
const context = canvas.value?.getContext('2d');
if (!context) { return; }
context.clearRect(0, 0, 1920, 1080);
context.clearRect(0, 0, canvasSize.value.width, canvasSize.value.height);
renderNode(rootNode, context);
};
watch(node, () => {
render();
});
watch(canvas, () => {
render();
});
watch(scale, () => {
render();
});
watch(prefersDarkMode, () => {
render();
});
watch([node, canvas, scale, prefersDarkMode, canvasSize], () => render());
</script>
4 changes: 4 additions & 0 deletions packages/renderer/src/integration/obs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,13 @@ export function useObs() {
window.obs.getScenes()
.then(obsStore.setScenes);

window.obs.getCanvasSize()
.then(obsStore.setCanvasSize);

window.obs.onConnectionStateChanged(obsStore.setConnectionState);
window.obs.onSourcesChanged(obsStore.setSources);
window.obs.onScenesChanged(obsStore.setScenes);
window.obs.onCanvasSizeChanged(obsStore.setCanvasSize);
}

export function syncLayout(rootNode: LayoutNode, sceneName: string): void {
Expand Down
4 changes: 3 additions & 1 deletion packages/renderer/src/store/layout.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { defineStore } from 'pinia';
import { useLayoutsStore } from './layouts';
import { useObsStore } from './obs';
import type { LayoutNode, Component } from '/@/layout';
import { LayoutExerciser, Size, ContainerComponent } from '/@/layout';

Expand Down Expand Up @@ -30,7 +31,8 @@ export const useLayoutStore = defineStore('layout', {
},
exerciseLayout() {
if (!this.rootComponent) { return; }
this.rootNode = new LayoutExerciser().execute(this.rootComponent, new Size(1920, 1080));
const obsStore = useObsStore();
this.rootNode = new LayoutExerciser().execute(this.rootComponent, new Size(obsStore.canvasSize.width, obsStore.canvasSize.height));
},
addChild(component: Component, parentId: string) {
if (!this.rootComponent) { return; }
Expand Down
15 changes: 14 additions & 1 deletion packages/renderer/src/store/obs.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { defineStore } from 'pinia';
import type { Source } from '../../../shared/src/obs';
import { OBSConnectionState } from '../../../shared/src/obs';
import type { Size } from '../../../shared/src/layout';
import { useLayoutStore } from './layout';

interface State {
connectionState: OBSConnectionState,
sources: Source[],
scenes: string[]
scenes: string[],
canvasSize: Size
}

export const useObsStore = defineStore('obs', {
Expand All @@ -14,6 +17,10 @@ export const useObsStore = defineStore('obs', {
connectionState: OBSConnectionState.Disconnected,
sources: [],
scenes: [],
canvasSize: {
width: 1920,
height: 1080,
},
};
},
actions: {
Expand All @@ -26,5 +33,11 @@ export const useObsStore = defineStore('obs', {
setScenes(scenes: string[]) {
this.scenes = scenes;
},
setCanvasSize(size: Size) {
this.canvasSize = size;

const layoutStore = useLayoutStore();
layoutStore.exerciseLayout();
},
},
});
4 changes: 4 additions & 0 deletions packages/shared/src/layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface Size {
width: number,
height: number
}

0 comments on commit 8be8402

Please sign in to comment.