implement text support
This commit is contained in:
parent
f926b94c99
commit
cb210fcc3e
|
@ -50,10 +50,11 @@ class FFmpegFilter:
|
||||||
)
|
)
|
||||||
|
|
||||||
class FFmpegChain:
|
class FFmpegChain:
|
||||||
def __init__(self, *filters, inputs=None, runners=None):
|
def __init__(self, *filters, inputs=None, runners=None, overlay=True):
|
||||||
self.filters = list(filters)
|
self.filters = list(filters)
|
||||||
self.inputs = inputs or []
|
self.inputs = inputs or []
|
||||||
self.runners = runners or []
|
self.runners = runners or []
|
||||||
|
self.overlay = overlay
|
||||||
|
|
||||||
def append(self, f):
|
def append(self, f):
|
||||||
self.filters.append(f)
|
self.filters.append(f)
|
||||||
|
|
|
@ -67,7 +67,7 @@ class OBSSource:
|
||||||
def load(self, data):
|
def load(self, data):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def to_ffmpeg(self, scene):
|
def to_ffmpeg(self, scene, item):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -105,9 +105,9 @@ class OBSScene:
|
||||||
for src in data['sources']:
|
for src in data['sources']:
|
||||||
self.sources[src['name']] = OBSSource.find_and_load(src)
|
self.sources[src['name']] = OBSSource.find_and_load(src)
|
||||||
|
|
||||||
def to_ffmpeg(self, scene=None):
|
def to_ffmpeg(self, scene=None, item=None):
|
||||||
scene = scene or self.current_scene
|
scene = scene or self.current_scene
|
||||||
chains = self.sources[scene].to_ffmpeg(self)
|
chains = self.sources[scene].to_ffmpeg(self, item)
|
||||||
|
|
||||||
r = FFmpegGraph()
|
r = FFmpegGraph()
|
||||||
r.extend(chains)
|
r.extend(chains)
|
||||||
|
|
|
@ -107,7 +107,7 @@ class OBSBrowserSource(OBSSource):
|
||||||
self.height = data['settings']['height']
|
self.height = data['settings']['height']
|
||||||
self.audio = data['settings'].get('reroute_audio', False)
|
self.audio = data['settings'].get('reroute_audio', False)
|
||||||
|
|
||||||
def to_ffmpeg(self, scene):
|
def to_ffmpeg(self, scene, item):
|
||||||
fifo_path = os.path.join(self.tempdir, 'frame.bin')
|
fifo_path = os.path.join(self.tempdir, 'frame.bin')
|
||||||
os.mkfifo(fifo_path)
|
os.mkfifo(fifo_path)
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ class OBSBrowserSource(OBSSource):
|
||||||
self.height = data['settings']['height']
|
self.height = data['settings']['height']
|
||||||
self.audio = data['settings'].get('reroute_audio', False)
|
self.audio = data['settings'].get('reroute_audio', False)
|
||||||
|
|
||||||
def to_ffmpeg(self, scene):
|
def to_ffmpeg(self, scene, item):
|
||||||
fifo_path = os.path.join(self.tempdir, 'frame.bin')
|
fifo_path = os.path.join(self.tempdir, 'frame.bin')
|
||||||
os.mkfifo(fifo_path)
|
os.mkfifo(fifo_path)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ class OBSSceneSource(OBSSource):
|
||||||
self.name = data['name']
|
self.name = data['name']
|
||||||
self.items = data['settings']['items']
|
self.items = data['settings']['items']
|
||||||
|
|
||||||
def to_ffmpeg(self, scene):
|
def to_ffmpeg(self, scene, item):
|
||||||
prefix = identifiery(self.name)
|
prefix = identifiery(self.name)
|
||||||
i = 0
|
i = 0
|
||||||
graph = [FFmpegChain(
|
graph = [FFmpegChain(
|
||||||
|
@ -38,7 +38,7 @@ class OBSSceneSource(OBSSource):
|
||||||
if not item['visible']:
|
if not item['visible']:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
chains = scene.sources[item['name']].to_ffmpeg(scene)
|
chains = scene.sources[item['name']].to_ffmpeg(scene, item)
|
||||||
c = chains[-1]
|
c = chains[-1]
|
||||||
|
|
||||||
if item['scale']['x'] != 1.0 or item['scale']['y'] != 1.0:
|
if item['scale']['x'] != 1.0 or item['scale']['y'] != 1.0:
|
||||||
|
@ -59,15 +59,18 @@ class OBSSceneSource(OBSSource):
|
||||||
))
|
))
|
||||||
if not c[-1].outs:
|
if not c[-1].outs:
|
||||||
c[-1].outs.append(prefix + 'Out' + str(i))
|
c[-1].outs.append(prefix + 'Out' + str(i))
|
||||||
chains.append(FFmpegChain(
|
if c.overlay:
|
||||||
FFmpegFilter('overlay',
|
chains.append(FFmpegChain(
|
||||||
eval='init',
|
FFmpegFilter('overlay',
|
||||||
x=item['pos']['x'],
|
eval='init',
|
||||||
y=item['pos']['y'],
|
x=item['pos']['x'],
|
||||||
ins=[prefix + str(i - 1), c[-1].outs[-1]],
|
y=item['pos']['y'],
|
||||||
outs=[prefix + str(i)]
|
ins=[prefix + str(i - 1), c[-1].outs[-1]],
|
||||||
)
|
outs=[prefix + str(i)]
|
||||||
))
|
)
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
c[0].ins.append(prefix + str(i - 1))
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
graph.extend(chains)
|
graph.extend(chains)
|
||||||
|
|
|
@ -12,17 +12,21 @@ class TextSource(OBSSource):
|
||||||
self.shadow = 0
|
self.shadow = 0
|
||||||
self.shadow_color = '#000000'
|
self.shadow_color = '#000000'
|
||||||
|
|
||||||
def to_ffmpeg(self, scene):
|
def to_ffmpeg(self, scene, item):
|
||||||
return [FFmpegChain(
|
return [FFmpegChain(
|
||||||
FFmpegFilter('drawtext',
|
FFmpegFilter('drawtext',
|
||||||
expansion='none',
|
expansion='none',
|
||||||
fontcolor=self.color,
|
fontcolor=hex(self.color),
|
||||||
font=self.font,
|
font=self.font,
|
||||||
|
fontsize=self.size,
|
||||||
text=self.contents,
|
text=self.contents,
|
||||||
shadowcolor=self.shadow_color,
|
shadowcolor=self.shadow_color,
|
||||||
shadowx=self.shadow,
|
shadowx=self.shadow,
|
||||||
shadowy=self.shadow,
|
shadowy=self.shadow,
|
||||||
)
|
x=item['pos']['x'],
|
||||||
|
y=item['pos']['y'],
|
||||||
|
),
|
||||||
|
overlay=False
|
||||||
)]
|
)]
|
||||||
|
|
||||||
@OBSSource.type('text_gdiplus')
|
@OBSSource.type('text_gdiplus')
|
||||||
|
@ -32,9 +36,14 @@ class GDIPlusSource(TextSource):
|
||||||
self.size = data['settings']['font']['size']
|
self.size = data['settings']['font']['size']
|
||||||
self.color = data['settings']['color']
|
self.color = data['settings']['color']
|
||||||
|
|
||||||
|
import struct
|
||||||
|
def swap32(i):
|
||||||
|
return struct.unpack("<I", struct.pack(">I", i))[0]
|
||||||
|
|
||||||
@OBSSource.type('text_ft2_source')
|
@OBSSource.type('text_ft2_source')
|
||||||
class FreeType2Source(TextSource):
|
class FreeType2Source(TextSource):
|
||||||
def load(self, data):
|
def load(self, data):
|
||||||
self.font = data['settings']['font']['face']
|
self.font = data['settings']['font']['face'] + ((' ' + data['settings']['font']['style']) if data['settings']['font'].get('style') else '')
|
||||||
self.size = data['settings']['font']['size']
|
self.size = data['settings']['font']['size']
|
||||||
self.color = data['settings']['color0']
|
self.color = swap32(data['settings']['color1'])
|
||||||
|
self.contents = data['settings']['text']
|
||||||
|
|
|
@ -11,7 +11,7 @@ class ImageSource(OBSSource):
|
||||||
def load(self, data):
|
def load(self, data):
|
||||||
self.infile = data['settings']['file']
|
self.infile = data['settings']['file']
|
||||||
|
|
||||||
def to_ffmpeg(self, scene):
|
def to_ffmpeg(self, scene, item):
|
||||||
return [FFmpegChain(inputs=[FFmpegInput(self.infile)])]
|
return [FFmpegChain(inputs=[FFmpegInput(self.infile)])]
|
||||||
|
|
||||||
@OBSSource.type('ffmpeg_source')
|
@OBSSource.type('ffmpeg_source')
|
||||||
|
@ -25,7 +25,7 @@ class FFmpegSource(OBSSource):
|
||||||
self.infile = data['settings']['local_file']
|
self.infile = data['settings']['local_file']
|
||||||
self.loop = data['settings']['looping']
|
self.loop = data['settings']['looping']
|
||||||
|
|
||||||
def to_ffmpeg(self, scene):
|
def to_ffmpeg(self, scene, item):
|
||||||
return [FFmpegChain(
|
return [FFmpegChain(
|
||||||
inputs=[FFmpegInput(self.infile, re=None, stream_loop=-1 if self.loop else 0)]
|
inputs=[FFmpegInput(self.infile, re=None, stream_loop=-1 if self.loop else 0)]
|
||||||
)]
|
)]
|
||||||
|
|
Loading…
Reference in New Issue