Skip to content
This repository was archived by the owner on Mar 6, 2025. It is now read-only.

Commit febf409

Browse files
committed
dedupe, allow multistream files, reconfigure tee mapping
1 parent c584483 commit febf409

File tree

3 files changed

+51
-50
lines changed

3 files changed

+51
-50
lines changed

PyLivestream/__init__.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,17 @@ def golive(self, sinks: List[str]=None):
5454
cmd = cmdstem + ['-flags:v', '+global_header',
5555
'-f', 'tee']
5656

57-
if not self.vidsource == 'file':
57+
if self.image:
58+
# connect image to video stream, audio file to audio stream
5859
cmd += ['-map', '0:v', '-map', '1:a']
5960
else:
60-
cmd += ['-map', '0:v', '-map', '0:a:0']
61+
if self.vidsource == 'file':
62+
# picks first video and audio stream, often correct
63+
cmd += ['-map', '0:v', '-map', '0:a:0']
64+
else: # device (webcam)
65+
# connect video device to video stream,
66+
# audio device to audio stream
67+
cmd += ['-map', '0:v', '-map', '1:a']
6168

6269
cmd += ['[f=flv]' + '|[f=flv]'.join(sinks)] # no double quotes
6370
print(' '.join(cmd))

PyLivestream/sio.py

+23-25
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,23 @@ def getexe(exein: Path=None) -> Tuple[str, str]:
3232
return exe, probeexe
3333

3434

35+
def get_streams(fn: Path, exe: Path) -> Union[None, dict]:
36+
if not fn: # audio-only
37+
return None
38+
39+
fn = Path(fn).expanduser()
40+
41+
assert fn.is_file(), f'{fn} is not a file'
42+
43+
cmd = [str(exe), '-v', 'error', '-print_format', 'json',
44+
'-show_streams', '-show_format', str(fn)]
45+
46+
ret = subprocess.check_output(cmd, universal_newlines=True)
47+
# %% decode JSON from FFprobe
48+
js = json.loads(ret)
49+
return js['streams']
50+
51+
3552
def get_resolution(fn: Path, exe: Path) -> Union[None, Tuple[int, int]]:
3653
"""
3754
get resolution (widthxheight) of video file
@@ -49,20 +66,11 @@ def get_resolution(fn: Path, exe: Path) -> Union[None, Tuple[int, int]]:
4966
5067
if not a video file, None is returned.
5168
"""
52-
if not fn: # audio-only
53-
return None
54-
55-
fn = Path(fn).expanduser()
5669

57-
assert fn.is_file(), f'{fn} is not a file'
58-
59-
cmd = [str(exe), '-v', 'error', '-print_format', 'json',
60-
'-show_streams', str(fn)]
70+
streams = get_streams(fn, exe)
71+
if streams is None:
72+
return None
6173

62-
ret = subprocess.check_output(cmd, universal_newlines=True)
63-
# %% decode JSON from FFprobe
64-
js = json.loads(ret)
65-
streams = js['streams']
6674
res = None
6775

6876
for s in streams:
@@ -90,21 +98,11 @@ def get_framerate(fn: Path, exe: Path) -> Union[None, float]:
9098
9199
if not a video file, None is returned.
92100
"""
93-
if not fn: # audio-only
94-
return None
95-
96-
fn = Path(fn).expanduser()
97-
98-
assert fn.is_file(), f'{fn} is not a file'
99-
100-
cmd = [str(exe), '-v', 'error', '-print_format', 'json',
101-
'-show_streams', str(fn)]
102101

103-
ret = subprocess.check_output(cmd, universal_newlines=True)
102+
streams = get_streams(fn, exe)
103+
if streams is None:
104+
return None
104105

105-
# %% decode JSON from FFprobe
106-
js = json.loads(ret)
107-
streams = js['streams']
108106
fps = None
109107

110108
for s in streams:

tests/test_all.py

+19-23
Original file line numberDiff line numberDiff line change
@@ -24,49 +24,43 @@ def test_webcam(self):
2424
S = PyLivestream.Webcam(inifn, sites)
2525
for s in S.streams:
2626
if s == 'periscope':
27-
assert S.streams[s].video_kbps == 800
27+
self.assertEqual(S.streams[s].video_kbps, 800)
2828
else:
2929
if int(S.streams[s].res[1]) == 480:
3030
assert S.streams[s].video_kbps == 500
3131
elif int(S.streams[s].res[1]) == 720:
3232
assert S.streams[s].video_kbps == 1800
3333

3434
def test_filein_video(self):
35-
for s in sites:
36-
p = PyLivestream.FileIn(inifn, s, rdir/'star_collapse_out.avi')
37-
35+
S = PyLivestream.FileIn(inifn, sites, rdir/'star_collapse_out.avi')
36+
for s in S.streams:
3837
if s == 'periscope':
39-
assert p.stream.video_kbps == 800
38+
self.assertEqual(S.streams[s].video_kbps, 800)
4039
else:
41-
if p.stream.res is None:
42-
continue
43-
44-
if int(p.stream.res[1]) == 480:
45-
self.assertEqual(p.stream.video_kbps, 500)
46-
elif int(p.stream.res[1]) == 720:
47-
self.assertEqual(p.stream.video_kbps, 1800)
40+
if int(S.streams[s].res[1]) == 480:
41+
self.assertEqual(S.streams[s].video_kbps, 500)
42+
elif int(S.streams[s].res[1]) == 720:
43+
self.assertEqual(S.streams[s].video_kbps, 1800)
4844

4945
def test_filein_audio(self):
5046
flist = list(rdir.glob('*.ogg'))
5147

52-
for s in sites:
53-
p = PyLivestream.FileIn(inifn, s, flist[0])
54-
48+
S = PyLivestream.FileIn(inifn, sites, flist[0])
49+
for s in S.streams:
5550
if s == 'periscope':
56-
self.assertEqual(p.stream.video_kbps, 800)
51+
self.assertEqual(S.streams[s].video_kbps, 800)
5752
else:
58-
self.assertEqual(p.stream.video_kbps, 500)
53+
self.assertEqual(S.streams[s].video_kbps, 500)
5954

6055
def test_microphone(self):
61-
for s in sites:
62-
S = PyLivestream.Microphone(inifn, s,
63-
rdir.parent / 'doc' / 'logo.png')
56+
S = PyLivestream.Microphone(inifn, sites,
57+
rdir.parent / 'doc' / 'logo.png')
6458

6559
for s in S.streams:
6660
if s == 'periscope':
67-
assert S.streams[s].video_kbps == 800
61+
self.assertEqual(S.streams[s].video_kbps, 800)
6862
else:
69-
assert S.streams[s].video_kbps == 500
63+
self.assertEqual(S.streams[s].video_kbps, 500)
7064

7165
def test_disk(self):
7266
for s in sites:
@@ -78,7 +72,9 @@ def test_stream(self):
7872
"""stream to NUL"""
7973

8074
s = PyLivestream.FileIn(inifn, 'localhost',
81-
rdir / 'orch_short.ogg', yes=True)
75+
rdir / 'orch_short.ogg',
76+
image=rdir.parent / 'doc' / 'logo.png',
77+
yes=True)
8278
s.golive()
8379

8480

0 commit comments

Comments
 (0)