parent
f3b3327afe
commit
7aaef13e18
3 changed files with 123 additions and 4 deletions
@ -1,2 +1,2 @@ |
||||
from .base import OBSProfile, OBSSource, OBSScene |
||||
from . import browser, scene, text, video |
||||
from . import cef_browser, scene, text, video |
||||
|
@ -0,0 +1,120 @@ |
||||
import os |
||||
import shutil |
||||
import tempfile |
||||
import sys |
||||
import time |
||||
from cefpython3 import cefpython as cef |
||||
|
||||
from ..filters import FFmpegInput, FFmpegChain, FFmpegFilter |
||||
from .base import OBSSource |
||||
|
||||
sys.excepthook = cef.ExceptHook |
||||
|
||||
|
||||
class CEFHandler: |
||||
def __init__(self, capturer): |
||||
self.capturer = capturer |
||||
|
||||
def OnLoadError(self, browser, frame, error_code, failed_url, **_): |
||||
print('load_error', error_code, failed_url) |
||||
if not frame.IsMain(): |
||||
return |
||||
cef.PostTask(cef.TID_UI, self.capturer.exit, browser) |
||||
|
||||
def GetViewRect(self, rect_out, **_): |
||||
# rect_out --> [x, y, width, height] |
||||
print('get_view_rect') |
||||
rect_out.extend([0, 0, self.capturer.dimensions[0], self.capturer.dimensions[1]]) |
||||
return True |
||||
|
||||
def OnPaint(self, browser, element_type, paint_buffer, **_): |
||||
print('paint') |
||||
if element_type == cef.PET_VIEW: |
||||
self.capturer.frame = paint_buffer.GetBytes(mode="rgba", origin="top-left") |
||||
|
||||
class CEFBrowserCapturer: |
||||
def __init__(self, url, outfile, dimensions=(1280, 720), rate=10): |
||||
self.headless = True |
||||
self.dimensions = dimensions |
||||
self.url = url |
||||
self.outfile = outfile |
||||
self.frame = None |
||||
self.rate = rate |
||||
|
||||
settings = { |
||||
"windowless_rendering_enabled": True, |
||||
} |
||||
switches = { |
||||
"disable-gpu": "", |
||||
"disable-gpu-compositing": "", |
||||
"enable-begin-frame-scheduling": "", |
||||
"disable-surfaces": "", |
||||
} |
||||
browser_settings = { |
||||
"windowless_frame_rate": 30, |
||||
} |
||||
cef.Initialize(settings=settings, switches=switches) |
||||
window_info = cef.WindowInfo() |
||||
parent_window_handle = 0 |
||||
window_info.SetAsOffscreen(parent_window_handle) |
||||
self.browser = cef.CreateBrowserSync(window_info=window_info, settings=browser_settings, url=self.url) |
||||
|
||||
def run_browser(self): |
||||
self.browser.SetClientHandler(CEFHandler(self)) |
||||
self.browser.SendFocusEvent(True) |
||||
self.browser.WasResized() |
||||
cef.MessageLoop() |
||||
cef.Shutdown() |
||||
|
||||
def output_frames(self): |
||||
with open(self.outfile, 'wb') as f: |
||||
while True: |
||||
t = time.monotonic() |
||||
if self.frame: |
||||
f.write(self.frame) |
||||
time.sleep(1.0 / self.rate - (time.monotonic() - t)) |
||||
|
||||
def exit(self, browser=None): |
||||
browser = browser or self.browser |
||||
browser.CloseBrowser() |
||||
cef.QuitMessageLoop() |
||||
|
||||
|
||||
|
||||
@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) |
||||
|
||||
runner = CEFBrowserCapturer(self.url, fifo_path, dimensions=(self.width, self.height)) |
||||
return [FFmpegChain( |
||||
inputs=[FFmpegInput(fifo_path, |
||||
f='rawvideo', r=runner.rate, s='{}x{}'.format(self.width, self.height), pix_fmt='rgba', |
||||
follow=1, probesize=8192 |
||||
)], |
||||
runners=[(runner.run_browser, True), runner.output_frames] |
||||
)] |
Loading…
Reference in new issue