Multi-DJ livestream toolkit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

94 lines
3.2 KiB

import sys
import os
import tempfile
import shutil
import base64
import functools
import trio
from trio_cdp import page, emulation, dom
from ..chromium import Chromium
from ..filters import FFmpegInput, FFmpegChain, FFmpegFilter
from .base import OBSSource
class BrowserCapturer:
def __init__(self, url, outfile, dimensions, rate=25):
self.chromium = Chromium()
self.url = url
self.frame = b''
self.outfile = outfile
self.dimensions = dimensions
self.rate = rate
def run(self):
trio.run(self.do_run, restrict_keyboard_interrupt_to_checkpoints=True)
async def do_run(self):
async with trio.open_nursery() as nursery:
nursery.start_soon(self.capture_frames)
nursery.start_soon(self.output_frames)
async def capture_frames(self):
async with await self.chromium.connect() as conn, await self.chromium.select(conn) as sess, sess.page_enable():
# Set viewport size and transparency.
await emulation.set_device_metrics_override(
width=self.dimensions[0], height=self.dimensions[1],
device_scale_factor=1.0, mobile=False
)
await emulation.set_default_background_color_override(dom.RGBA(0, 0, 0, 0))
# Navigate to the website.
async with sess.wait_for(page.LoadEventFired):
await page.navigate(self.url)
# Start capture.
await page.start_screencast(format_='png')
async for frame in sess.listen(page.ScreencastFrame):
self.frame = base64.b64decode(frame.data)
await page.screencast_frame_ack(frame.session_id)
async def output_frames(self):
async with await trio.open_file(self.outfile, 'wb') as f:
while True:
t = trio.current_time()
if self.frame:
await f.write(self.frame)
await trio.sleep_until(t + 1.0 / self.rate)
@OBSSource.type('browser_source')
class OBSBrowserSource(OBSSource):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.local = False
self.url = None
self.width = 1280
self.height = 720
self.audio = False
self.tempdir = tempfile.mkdtemp()
def __del__(self):
shutil.rmtree(self.tempdir, ignore_errors=True)
def load(self, data):
self.name = data['name']
self.local = data['settings'].get('is_local_file', False)
if self.local:
self.url = data['settings']['local_file']
else:
self.url = data['settings']['url']
self.width = data['settings']['width']
self.height = data['settings']['height']
self.audio = data['settings'].get('reroute_audio', False)
def to_ffmpeg(self, scene):
fifo_path = os.path.join(self.tempdir, 'frame.bin')
os.mkfifo(fifo_path)
print(self.width, self.height)
runner = BrowserCapturer(self.url, fifo_path, dimensions=(self.width, self.height))
return [FFmpegChain(
inputs=[FFmpegInput(fifo_path, f='image2pipe', framerate=runner.rate, follow=1, probesize=8192)],
runners=[runner.run]
)]