yerang commited on
Commit
148b602
1 Parent(s): eef7546

Delete stf/stf-api-tools

Browse files
stf/stf-api-tools/.gitignore DELETED
@@ -1,160 +0,0 @@
1
- # Byte-compiled / optimized / DLL files
2
- __pycache__/
3
- *.py[cod]
4
- *$py.class
5
-
6
- # C extensions
7
- *.so
8
-
9
- # Distribution / packaging
10
- .Python
11
- build/
12
- develop-eggs/
13
- dist/
14
- downloads/
15
- eggs/
16
- .eggs/
17
- lib/
18
- lib64/
19
- parts/
20
- sdist/
21
- var/
22
- wheels/
23
- share/python-wheels/
24
- *.egg-info/
25
- .installed.cfg
26
- *.egg
27
- MANIFEST
28
-
29
- # PyInstaller
30
- # Usually these files are written by a python script from a template
31
- # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
- *.manifest
33
- *.spec
34
-
35
- # Installer logs
36
- pip-log.txt
37
- pip-delete-this-directory.txt
38
-
39
- # Unit test / coverage reports
40
- htmlcov/
41
- .tox/
42
- .nox/
43
- .coverage
44
- .coverage.*
45
- .cache
46
- nosetests.xml
47
- coverage.xml
48
- *.cover
49
- *.py,cover
50
- .hypothesis/
51
- .pytest_cache/
52
- cover/
53
-
54
- # Translations
55
- *.mo
56
- *.pot
57
-
58
- # Django stuff:
59
- *.log
60
- local_settings.py
61
- db.sqlite3
62
- db.sqlite3-journal
63
-
64
- # Flask stuff:
65
- instance/
66
- .webassets-cache
67
-
68
- # Scrapy stuff:
69
- .scrapy
70
-
71
- # Sphinx documentation
72
- docs/_build/
73
-
74
- # PyBuilder
75
- .pybuilder/
76
- target/
77
-
78
- # Jupyter Notebook
79
- .ipynb_checkpoints
80
-
81
- # IPython
82
- profile_default/
83
- ipython_config.py
84
-
85
- # pyenv
86
- # For a library or package, you might want to ignore these files since the code is
87
- # intended to run in multiple environments; otherwise, check them in:
88
- # .python-version
89
-
90
- # pipenv
91
- # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
- # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
- # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
- # install all needed dependencies.
95
- #Pipfile.lock
96
-
97
- # poetry
98
- # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
- # This is especially recommended for binary packages to ensure reproducibility, and is more
100
- # commonly ignored for libraries.
101
- # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
- #poetry.lock
103
-
104
- # pdm
105
- # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
- #pdm.lock
107
- # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
- # in version control.
109
- # https://pdm.fming.dev/#use-with-ide
110
- .pdm.toml
111
-
112
- # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113
- __pypackages__/
114
-
115
- # Celery stuff
116
- celerybeat-schedule
117
- celerybeat.pid
118
-
119
- # SageMath parsed files
120
- *.sage.py
121
-
122
- # Environments
123
- .env
124
- .venv
125
- env/
126
- venv/
127
- ENV/
128
- env.bak/
129
- venv.bak/
130
-
131
- # Spyder project settings
132
- .spyderproject
133
- .spyproject
134
-
135
- # Rope project settings
136
- .ropeproject
137
-
138
- # mkdocs documentation
139
- /site
140
-
141
- # mypy
142
- .mypy_cache/
143
- .dmypy.json
144
- dmypy.json
145
-
146
- # Pyre type checker
147
- .pyre/
148
-
149
- # pytype static type analyzer
150
- .pytype/
151
-
152
- # Cython debug symbols
153
- cython_debug/
154
-
155
- # PyCharm
156
- # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157
- # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158
- # and can be added to the global gitignore or merged into this file. For a more nuclear
159
- # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160
- #.idea/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
stf/stf-api-tools/.ipynb_checkpoints/README-checkpoint.md DELETED
File without changes
stf/stf-api-tools/.ipynb_checkpoints/pyproject-checkpoint.toml DELETED
@@ -1,14 +0,0 @@
1
- [tool.poetry]
2
- name = "stf-tools"
3
- version = "0.1.0"
4
- description = "stf-alternative tools"
5
- authors = ["Kim Minjong <make.dirty.code@gmail.com>"]
6
- readme = "README.md"
7
- packages = [
8
- {include = "stf_tools", from="src"}
9
- ]
10
-
11
-
12
- [build-system]
13
- requires = ["poetry-core"]
14
- build-backend = "poetry.core.masonry.api"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
stf/stf-api-tools/README.md DELETED
File without changes
stf/stf-api-tools/poetry.lock DELETED
@@ -1,7 +0,0 @@
1
- # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
2
- package = []
3
-
4
- [metadata]
5
- lock-version = "2.0"
6
- python-versions = "*"
7
- content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8"
 
 
 
 
 
 
 
 
stf/stf-api-tools/pyproject.toml DELETED
@@ -1,14 +0,0 @@
1
- [tool.poetry]
2
- name = "stf-tools"
3
- version = "0.1.0"
4
- description = "stf-alternative tools"
5
- authors = ["Kim Minjong <make.dirty.code@gmail.com>"]
6
- readme = "README.md"
7
- packages = [
8
- {include = "stf_tools", from="src"}
9
- ]
10
-
11
-
12
- [build-system]
13
- requires = ["poetry-core"]
14
- build-backend = "poetry.core.masonry.api"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
stf/stf-api-tools/src/stf_tools/.ipynb_checkpoints/__init__-checkpoint.py DELETED
File without changes
stf/stf-api-tools/src/stf_tools/.ipynb_checkpoints/build_template-checkpoint.py DELETED
@@ -1,200 +0,0 @@
1
- import pathlib
2
- import subprocess
3
- import tempfile
4
-
5
- import av
6
- import numpy as np
7
- from PIL import Image
8
-
9
-
10
- def alpha_crop_detect(path):
11
- result = subprocess.check_output(
12
- [
13
- "bash",
14
- "-c",
15
- f"""ffmpeg -c:v libvpx -i {path} -filter_complex "[0:v]alphaextract, cropdetect=limit=0:round=16:reset=0" -f null - 2>&1 | grep -oP 'crop=\K\d+:\d+:\d+:\d+' """,
16
- ]
17
- )
18
- return result.decode().strip().split("\n")[-1]
19
-
20
-
21
- def crop_resize_overlay(
22
- path, background_path, range, out, left=0.5, top=0.15, height=0.85, crf=17
23
- ):
24
- with av.open(path, "r") as f:
25
- fps = f.streams.video[0].base_rate
26
-
27
- with av.open(background_path, "r") as f:
28
- background_width, background_height = (
29
- f.streams.video[0].width,
30
- f.streams.video[0].height,
31
- )
32
-
33
- if isinstance(top, float):
34
- top = int(background_height * top)
35
-
36
- if isinstance(height, float):
37
- height = int(background_height * height)
38
-
39
- height -= height % 2
40
-
41
- w, h, _, _ = map(int, range.split(":"))
42
- width = int(height / h * w)
43
- width -= width % 2
44
-
45
- if isinstance(left, float):
46
- left = int(background_width * left) - width // 2
47
-
48
- subprocess.call(
49
- [
50
- "bash",
51
- "-c",
52
- f"""ffmpeg -y -c:v libvpx -r {fps} -i {path} -r {fps} -i {background_path} -filter_complex "[0:v]crop={range},scale={width}:{height} [vidi]; [1:v][vidi] overlay={left}:{top}" -crf {crf} -pix_fmt yuva420p -c:v libvpx-vp9 -c:a copy {out}""",
53
- ]
54
- )
55
-
56
- return background_width, background_height, int(fps), (left, top, height)
57
-
58
-
59
- import json
60
- import os
61
- import shutil
62
- import tempfile
63
- from pathlib import Path
64
-
65
- import av
66
- import pandas as pd
67
- import stf_alternative
68
- from stf_alternative.util import get_crop_mp4_dir, get_frame_dir, get_preprocess_dir
69
-
70
- from stf_tools.silent import create_silent_video
71
- from stf_tools.writers import WebmWriter
72
-
73
-
74
- def create_template(
75
- template_video_path,
76
- background_path,
77
- out_path,
78
- config_path,
79
- reference_face,
80
- work_root_path,
81
- checkpoint_path,
82
- left,
83
- top,
84
- height,
85
- crf=17,
86
- ):
87
- crop_range = alpha_crop_detect(template_video_path)
88
- result_width, result_height, fps, (left, top, height) = crop_resize_overlay(
89
- template_video_path,
90
- background_path,
91
- crop_range,
92
- out_path,
93
- left=left,
94
- top=top,
95
- height=height,
96
- crf=crf,
97
- )
98
-
99
- stf_alternative.preprocess_template(
100
- config_path=config_path,
101
- template_video_path=template_video_path,
102
- reference_face=reference_face,
103
- work_root_path=work_root_path,
104
- template_frame_ratio=1.0,
105
- template_video_ratio=[1.0],
106
- silent_video_path=None,
107
- callback=None,
108
- device="cuda:0",
109
- verbose=True,
110
- save_frames=False,
111
- )
112
-
113
- model = stf_alternative.create_model(
114
- config_path=config_path,
115
- checkpoint_path=checkpoint_path,
116
- work_root_path=work_root_path,
117
- device="cuda:0",
118
- verbose=True,
119
- wavlm_path="microsoft/wavlm-large",
120
- )
121
-
122
- preprocess_dir = Path(get_preprocess_dir(work_root_path, model.args.name))
123
- crop_mp4_dir = Path(get_crop_mp4_dir(preprocess_dir, template_video_path))
124
- dataset_dir = crop_mp4_dir / f"{Path(template_video_path).stem}_000"
125
- template_frames_path = Path(
126
- get_frame_dir(preprocess_dir, template_video_path, ratio=1.0)
127
- )
128
-
129
- with open(preprocess_dir / "metadata.json", "w") as f:
130
- json.dump(
131
- {
132
- "fps": fps,
133
- "width": result_width,
134
- "height": result_height,
135
- },
136
- f,
137
- )
138
-
139
- df = pd.read_pickle(dataset_dir / "df_fan.pickle")
140
-
141
- w, h, x, y = map(int, crop_range.split(":"))
142
- scale = height / h
143
-
144
- id_set = set()
145
- for it in df["cropped_box"]:
146
- if id(it) in id_set:
147
- continue
148
- id_set.add(id(it))
149
- x1, y1, x2, y2 = it
150
- x1 = (x1 - x) * scale + left
151
- x2 = (x2 - x) * scale + left
152
- y1 = (y1 - y) * scale + top
153
- y2 = (y2 - y) * scale + top
154
- it[:] = (x1, y1, x2, y2)
155
-
156
- df.to_pickle(dataset_dir / "df_fan.pickle")
157
-
158
- template_frames_path.mkdir(exist_ok=True, parents=True)
159
- with av.open(out_path) as container:
160
- for frame in container.decode(video=0):
161
- Image.fromarray(frame.to_ndarray(format="rgb24"), mode="RGB").save(
162
- f"{template_frames_path}/%05d.webp" % frame.index,
163
- format="webp",
164
- lossless=True,
165
- )
166
-
167
- with tempfile.TemporaryDirectory() as tempdir:
168
- silent_video_path = f"{tempdir}/silent.webm"
169
- template = stf_alternative.Template(
170
- config_path=config_path,
171
- model=model,
172
- template_video_path=template_video_path,
173
- wav_std=False,
174
- ref_wav=None,
175
- verbose=True,
176
- )
177
- writer = WebmWriter(
178
- silent_video_path,
179
- width=result_width,
180
- height=result_height,
181
- fps=fps,
182
- crf=crf,
183
- audio_sample_rate=16000,
184
- quiet=False,
185
- )
186
- create_silent_video(template, writer)
187
-
188
- silent_frames_path = Path(
189
- get_frame_dir(preprocess_dir, silent_video_path, ratio=1.0)
190
- )
191
- silent_frames_path.mkdir(exist_ok=True, parents=True)
192
- with av.open(silent_video_path) as container:
193
- for frame in container.decode(video=0):
194
- Image.fromarray(frame.to_ndarray(format="rgb24"), mode="RGB").save(
195
- f"{silent_frames_path}/%05d.webp" % frame.index,
196
- format="webp",
197
- lossless=True,
198
- )
199
- shutil.rmtree(template_frames_path, ignore_errors=False)
200
- silent_frames_path.rename(template_frames_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
stf/stf-api-tools/src/stf_tools/.ipynb_checkpoints/silent-checkpoint.py DELETED
@@ -1,28 +0,0 @@
1
- from concurrent.futures import ThreadPoolExecutor
2
-
3
- from pydub import AudioSegment
4
-
5
-
6
- def create_silent_video(template, writer):
7
- reader = iter(template._get_reader(num_skip_frames=0))
8
- audio_segment = AudioSegment.silent(10000)
9
- pivot = 0
10
-
11
- with ThreadPoolExecutor(4) as p:
12
- try:
13
- while True:
14
- gen_infer = template.gen_infer_concurrent(
15
- p,
16
- audio_segment,
17
- pivot,
18
- )
19
- for idx, (it, chunk) in enumerate(gen_infer, pivot):
20
- frame = next(reader)
21
- composed = template.compose(idx, frame, it)
22
- writer.video_writer.write(composed)
23
- writer.audio_writer.write(chunk)
24
- pivot = idx + 1
25
- except StopIteration as e:
26
- pass
27
-
28
- writer.finish(forced=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
stf/stf-api-tools/src/stf_tools/__init__.py DELETED
File without changes
stf/stf-api-tools/src/stf_tools/build_template.py DELETED
@@ -1,200 +0,0 @@
1
- import pathlib
2
- import subprocess
3
- import tempfile
4
-
5
- import av
6
- import numpy as np
7
- from PIL import Image
8
-
9
-
10
- def alpha_crop_detect(path):
11
- result = subprocess.check_output(
12
- [
13
- "bash",
14
- "-c",
15
- f"""ffmpeg -c:v libvpx -i {path} -filter_complex "[0:v]alphaextract, cropdetect=limit=0:round=16:reset=0" -f null - 2>&1 | grep -oP 'crop=\K\d+:\d+:\d+:\d+' """,
16
- ]
17
- )
18
- return result.decode().strip().split("\n")[-1]
19
-
20
-
21
- def crop_resize_overlay(
22
- path, background_path, range, out, left=0.5, top=0.15, height=0.85, crf=17
23
- ):
24
- with av.open(path, "r") as f:
25
- fps = f.streams.video[0].base_rate
26
-
27
- with av.open(background_path, "r") as f:
28
- background_width, background_height = (
29
- f.streams.video[0].width,
30
- f.streams.video[0].height,
31
- )
32
-
33
- if isinstance(top, float):
34
- top = int(background_height * top)
35
-
36
- if isinstance(height, float):
37
- height = int(background_height * height)
38
-
39
- height -= height % 2
40
-
41
- w, h, _, _ = map(int, range.split(":"))
42
- width = int(height / h * w)
43
- width -= width % 2
44
-
45
- if isinstance(left, float):
46
- left = int(background_width * left) - width // 2
47
-
48
- subprocess.call(
49
- [
50
- "bash",
51
- "-c",
52
- f"""ffmpeg -y -c:v libvpx -r {fps} -i {path} -r {fps} -i {background_path} -filter_complex "[0:v]crop={range},scale={width}:{height} [vidi]; [1:v][vidi] overlay={left}:{top}" -crf {crf} -pix_fmt yuva420p -c:v libvpx-vp9 -c:a copy {out}""",
53
- ]
54
- )
55
-
56
- return background_width, background_height, int(fps), (left, top, height)
57
-
58
-
59
- import json
60
- import os
61
- import shutil
62
- import tempfile
63
- from pathlib import Path
64
-
65
- import av
66
- import pandas as pd
67
- import stf_alternative
68
- from stf_alternative.util import get_crop_mp4_dir, get_frame_dir, get_preprocess_dir
69
-
70
- from stf_tools.silent import create_silent_video
71
- from stf_tools.writers import WebmWriter
72
-
73
-
74
- def create_template(
75
- template_video_path,
76
- background_path,
77
- out_path,
78
- config_path,
79
- reference_face,
80
- work_root_path,
81
- checkpoint_path,
82
- left,
83
- top,
84
- height,
85
- crf=17,
86
- ):
87
- crop_range = alpha_crop_detect(template_video_path)
88
- result_width, result_height, fps, (left, top, height) = crop_resize_overlay(
89
- template_video_path,
90
- background_path,
91
- crop_range,
92
- out_path,
93
- left=left,
94
- top=top,
95
- height=height,
96
- crf=crf,
97
- )
98
-
99
- stf_alternative.preprocess_template(
100
- config_path=config_path,
101
- template_video_path=template_video_path,
102
- reference_face=reference_face,
103
- work_root_path=work_root_path,
104
- template_frame_ratio=1.0,
105
- template_video_ratio=[1.0],
106
- silent_video_path=None,
107
- callback=None,
108
- device="cuda:0",
109
- verbose=True,
110
- save_frames=False,
111
- )
112
-
113
- model = stf_alternative.create_model(
114
- config_path=config_path,
115
- checkpoint_path=checkpoint_path,
116
- work_root_path=work_root_path,
117
- device="cuda:0",
118
- verbose=True,
119
- wavlm_path="microsoft/wavlm-large",
120
- )
121
-
122
- preprocess_dir = Path(get_preprocess_dir(work_root_path, model.args.name))
123
- crop_mp4_dir = Path(get_crop_mp4_dir(preprocess_dir, template_video_path))
124
- dataset_dir = crop_mp4_dir / f"{Path(template_video_path).stem}_000"
125
- template_frames_path = Path(
126
- get_frame_dir(preprocess_dir, template_video_path, ratio=1.0)
127
- )
128
-
129
- with open(preprocess_dir / "metadata.json", "w") as f:
130
- json.dump(
131
- {
132
- "fps": fps,
133
- "width": result_width,
134
- "height": result_height,
135
- },
136
- f,
137
- )
138
-
139
- df = pd.read_pickle(dataset_dir / "df_fan.pickle")
140
-
141
- w, h, x, y = map(int, crop_range.split(":"))
142
- scale = height / h
143
-
144
- id_set = set()
145
- for it in df["cropped_box"]:
146
- if id(it) in id_set:
147
- continue
148
- id_set.add(id(it))
149
- x1, y1, x2, y2 = it
150
- x1 = (x1 - x) * scale + left
151
- x2 = (x2 - x) * scale + left
152
- y1 = (y1 - y) * scale + top
153
- y2 = (y2 - y) * scale + top
154
- it[:] = (x1, y1, x2, y2)
155
-
156
- df.to_pickle(dataset_dir / "df_fan.pickle")
157
-
158
- template_frames_path.mkdir(exist_ok=True, parents=True)
159
- with av.open(out_path) as container:
160
- for frame in container.decode(video=0):
161
- Image.fromarray(frame.to_ndarray(format="rgb24"), mode="RGB").save(
162
- f"{template_frames_path}/%05d.webp" % frame.index,
163
- format="webp",
164
- lossless=True,
165
- )
166
-
167
- with tempfile.TemporaryDirectory() as tempdir:
168
- silent_video_path = f"{tempdir}/silent.webm"
169
- template = stf_alternative.Template(
170
- config_path=config_path,
171
- model=model,
172
- template_video_path=template_video_path,
173
- wav_std=False,
174
- ref_wav=None,
175
- verbose=True,
176
- )
177
- writer = WebmWriter(
178
- silent_video_path,
179
- width=result_width,
180
- height=result_height,
181
- fps=fps,
182
- crf=crf,
183
- audio_sample_rate=16000,
184
- quiet=False,
185
- )
186
- create_silent_video(template, writer)
187
-
188
- silent_frames_path = Path(
189
- get_frame_dir(preprocess_dir, silent_video_path, ratio=1.0)
190
- )
191
- silent_frames_path.mkdir(exist_ok=True, parents=True)
192
- with av.open(silent_video_path) as container:
193
- for frame in container.decode(video=0):
194
- Image.fromarray(frame.to_ndarray(format="rgb24"), mode="RGB").save(
195
- f"{silent_frames_path}/%05d.webp" % frame.index,
196
- format="webp",
197
- lossless=True,
198
- )
199
- shutil.rmtree(template_frames_path, ignore_errors=False)
200
- silent_frames_path.rename(template_frames_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
stf/stf-api-tools/src/stf_tools/silent.py DELETED
@@ -1,28 +0,0 @@
1
- from concurrent.futures import ThreadPoolExecutor
2
-
3
- from pydub import AudioSegment
4
-
5
-
6
- def create_silent_video(template, writer):
7
- reader = iter(template._get_reader(num_skip_frames=0))
8
- audio_segment = AudioSegment.silent(10000)
9
- pivot = 0
10
-
11
- with ThreadPoolExecutor(4) as p:
12
- try:
13
- while True:
14
- gen_infer = template.gen_infer_concurrent(
15
- p,
16
- audio_segment,
17
- pivot,
18
- )
19
- for idx, (it, chunk) in enumerate(gen_infer, pivot):
20
- frame = next(reader)
21
- composed = template.compose(idx, frame, it)
22
- writer.video_writer.write(composed)
23
- writer.audio_writer.write(chunk)
24
- pivot = idx + 1
25
- except StopIteration as e:
26
- pass
27
-
28
- writer.finish(forced=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
stf/stf-api-tools/src/stf_tools/writers/.ipynb_checkpoints/ffmpeg-checkpoint.py DELETED
@@ -1,100 +0,0 @@
1
- import os
2
- import queue
3
- import shutil
4
- import subprocess
5
- import tempfile
6
- import threading
7
- import time
8
- import traceback
9
- from abc import ABC, abstractmethod
10
- from contextlib import contextmanager
11
- from pathlib import Path
12
- from queue import Queue
13
-
14
- import ffmpeg
15
- import numpy as np
16
- import pydub
17
- from pydub import AudioSegment
18
-
19
- from stf_tools.writers._async import AudioAsyncWriter, VideoAsyncWriter
20
- from stf_tools.writers._thread import AudioThreadWriter, VideoThreadWriter
21
-
22
- video_pipe_name = "video"
23
- audio_pipe_name = "audio"
24
-
25
-
26
- class BaseFFMPEGWriter(ABC):
27
- def __init__(
28
- self,
29
- path,
30
- width,
31
- height,
32
- fps,
33
- crf=17,
34
- audio_sample_rate=16000,
35
- quiet=True,
36
- ):
37
- self.path = Path(path)
38
- self.width = width
39
- self.height = height
40
- self.fps = fps
41
- self.crf = crf
42
-
43
- self.path.parent.mkdir(exist_ok=True, parents=True)
44
-
45
- pipe_root = tempfile.mkdtemp()
46
- self.pipe_dir = Path(pipe_root)
47
- self.video_pipe_path = self.pipe_dir / video_pipe_name
48
- self.audio_pipe_path = self.pipe_dir / audio_pipe_name
49
-
50
- os.mkfifo(self.video_pipe_path)
51
- os.mkfifo(self.audio_pipe_path)
52
-
53
- self.audio_sample_rate = audio_sample_rate
54
-
55
- self.write_process = self._run_ffmpeg(
56
- quiet=quiet,
57
- )
58
-
59
- @abstractmethod
60
- def _run_ffmpeg(self, quiet):
61
- """ffmpeg writer using named pipe"""
62
-
63
-
64
- class ThreadFFMPEGWriter(BaseFFMPEGWriter):
65
- def __init__(self, *args, **kwargs):
66
- super().__init__(*args, **kwargs)
67
-
68
- self.video_writer = VideoThreadWriter(self.video_pipe_path, self.fps)
69
- self.audio_writer = AudioThreadWriter(
70
- self.audio_pipe_path, self.audio_sample_rate
71
- )
72
-
73
- def finish(self, forced=False):
74
- self.video_writer.finish(forced=forced)
75
- self.audio_writer.finish(forced=forced)
76
-
77
- if forced:
78
- self.write_process.kill()
79
- else:
80
- self.write_process.wait()
81
-
82
- shutil.rmtree(self.pipe_dir, ignore_errors=True)
83
-
84
-
85
- class AsyncFFMPEGWriter(BaseFFMPEGWriter):
86
- def __init__(self, *args, **kwargs):
87
- super().__init__(*args, **kwargs)
88
-
89
- self.video_writer = VideoAsyncWriter(self.video_pipe_path, self.fps)
90
- self.audio_writer = AudioAsyncWriter(
91
- self.audio_pipe_path, self.audio_sample_rate
92
- )
93
-
94
- def finish(self, forced=False):
95
- if forced:
96
- self.write_process.kill()
97
- else:
98
- self.write_process.wait()
99
-
100
- shutil.rmtree(self.pipe_dir, ignore_errors=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
stf/stf-api-tools/src/stf_tools/writers/.ipynb_checkpoints/webm-checkpoint.py DELETED
@@ -1,60 +0,0 @@
1
- import subprocess
2
-
3
- from stf_tools.writers.ffmpeg import ThreadFFMPEGWriter
4
-
5
-
6
- class WebmWriter(ThreadFFMPEGWriter):
7
- def _run_ffmpeg(self, quiet):
8
- return subprocess.Popen(
9
- [
10
- "ffmpeg",
11
- "-f",
12
- "rawvideo",
13
- "-pix_fmt",
14
- "rgba",
15
- "-r",
16
- f"{self.fps}",
17
- "-s",
18
- f"{self.width}x{self.height}",
19
- "-thread_queue_size",
20
- "1024",
21
- "-probesize",
22
- f"{self.width*self.height}",
23
- "-i",
24
- self.video_pipe_path,
25
- "-f",
26
- "s16le",
27
- "-ac",
28
- "1",
29
- "-acodec",
30
- "pcm_s16le",
31
- "-ar",
32
- "16k",
33
- "-thread_queue_size",
34
- "4096",
35
- "-probesize",
36
- "32",
37
- "-i",
38
- self.audio_pipe_path,
39
- "-map",
40
- "0:v:0",
41
- "-map",
42
- "1:a:0",
43
- "-pix_fmt",
44
- "yuva420p",
45
- "-crf",
46
- f"{self.crf}",
47
- "-r",
48
- f"{self.fps}",
49
- "-s",
50
- f"{self.width//2*2}x{self.height//2*2}",
51
- "-threads",
52
- "16",
53
- "-vcodec",
54
- "libvpx-vp9",
55
- str(self.path),
56
- "-y",
57
- ],
58
- stdout=subprocess.DEVNULL if quiet else None,
59
- stderr=subprocess.STDOUT if quiet else None,
60
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
stf/stf-api-tools/src/stf_tools/writers/__init__.py DELETED
@@ -1,2 +0,0 @@
1
- from stf_tools.writers.ffmpeg import AsyncFFMPEGWriter, ThreadFFMPEGWriter
2
- from stf_tools.writers.webm import WebmWriter
 
 
 
stf/stf-api-tools/src/stf_tools/writers/_async.py DELETED
@@ -1,42 +0,0 @@
1
- import asyncio
2
-
3
- import aiofiles
4
- import numpy as np
5
- import pydub
6
-
7
-
8
- class AsyncWriter:
9
- def __init__(self, path):
10
- self.queue = asyncio.Queue(maxsize=240)
11
- self.path = path
12
-
13
- async def pipeline(self):
14
- try:
15
- async with aiofiles.open(self.path, "wb", 0) as f:
16
- while (bytes := await self.queue.get()) is not None:
17
- await f.write(bytes)
18
- except:
19
- pass
20
-
21
- async def write_bytes(self, bytes):
22
- await self.queue.put(bytes)
23
-
24
-
25
- class VideoAsyncWriter(AsyncWriter):
26
- def __init__(self, path, fps):
27
- super().__init__(path)
28
- self.fps = fps
29
-
30
- async def write(self, video: np.array):
31
- return await self.write_bytes(video.astype(np.uint8).tobytes())
32
-
33
-
34
- class AudioAsyncWriter(AsyncWriter):
35
- def __init__(self, path, audio_sample_rate):
36
- super().__init__(path)
37
- self.audio_sample_rate = audio_sample_rate
38
-
39
- async def write(self, audio: pydub.AudioSegment):
40
- return await self.write_bytes(
41
- audio.set_frame_rate(self.audio_sample_rate).raw_data
42
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
stf/stf-api-tools/src/stf_tools/writers/_thread.py DELETED
@@ -1,57 +0,0 @@
1
- import threading
2
- import time
3
- import traceback
4
- from queue import Queue
5
-
6
- import numpy as np
7
- import pydub
8
-
9
-
10
- class ThreadWriter:
11
- def __init__(self, path):
12
- queue = Queue(maxsize=240)
13
- self.finished = False
14
-
15
- def write_bytes():
16
- try:
17
- with open(path, "wb", 0) as f:
18
- while (bytes := queue.get()) is not None:
19
- f.write(bytes)
20
- except Exception as e:
21
- traceback.print_exc()
22
- self.finished = True
23
-
24
- self.thread = threading.Thread(target=write_bytes)
25
- self.queue = queue
26
-
27
- self.thread.start()
28
-
29
- def write_bytes(self, bytes):
30
- if self.finished:
31
- return
32
- self.queue.put(bytes)
33
-
34
- def finish(self, forced=False):
35
- self.queue.put(None)
36
- if forced:
37
- self.finished = True
38
- else:
39
- self.thread.join()
40
-
41
-
42
- class VideoThreadWriter(ThreadWriter):
43
- def __init__(self, path, fps):
44
- super().__init__(path)
45
- self.fps = fps
46
-
47
- def write(self, video: np.array):
48
- return self.write_bytes(video.astype(np.uint8).tobytes())
49
-
50
-
51
- class AudioThreadWriter(ThreadWriter):
52
- def __init__(self, path, audio_sample_rate):
53
- super().__init__(path)
54
- self.audio_sample_rate = audio_sample_rate
55
-
56
- def write(self, audio: pydub.AudioSegment):
57
- return self.write_bytes(audio.set_frame_rate(self.audio_sample_rate).raw_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
stf/stf-api-tools/src/stf_tools/writers/ffmpeg.py DELETED
@@ -1,100 +0,0 @@
1
- import os
2
- import queue
3
- import shutil
4
- import subprocess
5
- import tempfile
6
- import threading
7
- import time
8
- import traceback
9
- from abc import ABC, abstractmethod
10
- from contextlib import contextmanager
11
- from pathlib import Path
12
- from queue import Queue
13
-
14
- import ffmpeg
15
- import numpy as np
16
- import pydub
17
- from pydub import AudioSegment
18
-
19
- from stf_tools.writers._async import AudioAsyncWriter, VideoAsyncWriter
20
- from stf_tools.writers._thread import AudioThreadWriter, VideoThreadWriter
21
-
22
- video_pipe_name = "video"
23
- audio_pipe_name = "audio"
24
-
25
-
26
- class BaseFFMPEGWriter(ABC):
27
- def __init__(
28
- self,
29
- path,
30
- width,
31
- height,
32
- fps,
33
- crf=17,
34
- audio_sample_rate=16000,
35
- quiet=True,
36
- ):
37
- self.path = Path(path)
38
- self.width = width
39
- self.height = height
40
- self.fps = fps
41
- self.crf = crf
42
-
43
- self.path.parent.mkdir(exist_ok=True, parents=True)
44
-
45
- pipe_root = tempfile.mkdtemp()
46
- self.pipe_dir = Path(pipe_root)
47
- self.video_pipe_path = self.pipe_dir / video_pipe_name
48
- self.audio_pipe_path = self.pipe_dir / audio_pipe_name
49
-
50
- os.mkfifo(self.video_pipe_path)
51
- os.mkfifo(self.audio_pipe_path)
52
-
53
- self.audio_sample_rate = audio_sample_rate
54
-
55
- self.write_process = self._run_ffmpeg(
56
- quiet=quiet,
57
- )
58
-
59
- @abstractmethod
60
- def _run_ffmpeg(self, quiet):
61
- """ffmpeg writer using named pipe"""
62
-
63
-
64
- class ThreadFFMPEGWriter(BaseFFMPEGWriter):
65
- def __init__(self, *args, **kwargs):
66
- super().__init__(*args, **kwargs)
67
-
68
- self.video_writer = VideoThreadWriter(self.video_pipe_path, self.fps)
69
- self.audio_writer = AudioThreadWriter(
70
- self.audio_pipe_path, self.audio_sample_rate
71
- )
72
-
73
- def finish(self, forced=False):
74
- self.video_writer.finish(forced=forced)
75
- self.audio_writer.finish(forced=forced)
76
-
77
- if forced:
78
- self.write_process.kill()
79
- else:
80
- self.write_process.wait()
81
-
82
- shutil.rmtree(self.pipe_dir, ignore_errors=True)
83
-
84
-
85
- class AsyncFFMPEGWriter(BaseFFMPEGWriter):
86
- def __init__(self, *args, **kwargs):
87
- super().__init__(*args, **kwargs)
88
-
89
- self.video_writer = VideoAsyncWriter(self.video_pipe_path, self.fps)
90
- self.audio_writer = AudioAsyncWriter(
91
- self.audio_pipe_path, self.audio_sample_rate
92
- )
93
-
94
- def finish(self, forced=False):
95
- if forced:
96
- self.write_process.kill()
97
- else:
98
- self.write_process.wait()
99
-
100
- shutil.rmtree(self.pipe_dir, ignore_errors=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
stf/stf-api-tools/src/stf_tools/writers/webm.py DELETED
@@ -1,60 +0,0 @@
1
- import subprocess
2
-
3
- from stf_tools.writers.ffmpeg import ThreadFFMPEGWriter
4
-
5
-
6
- class WebmWriter(ThreadFFMPEGWriter):
7
- def _run_ffmpeg(self, quiet):
8
- return subprocess.Popen(
9
- [
10
- "ffmpeg",
11
- "-f",
12
- "rawvideo",
13
- "-pix_fmt",
14
- "rgba",
15
- "-r",
16
- f"{self.fps}",
17
- "-s",
18
- f"{self.width}x{self.height}",
19
- "-thread_queue_size",
20
- "1024",
21
- "-probesize",
22
- f"{self.width*self.height}",
23
- "-i",
24
- self.video_pipe_path,
25
- "-f",
26
- "s16le",
27
- "-ac",
28
- "1",
29
- "-acodec",
30
- "pcm_s16le",
31
- "-ar",
32
- "16k",
33
- "-thread_queue_size",
34
- "4096",
35
- "-probesize",
36
- "32",
37
- "-i",
38
- self.audio_pipe_path,
39
- "-map",
40
- "0:v:0",
41
- "-map",
42
- "1:a:0",
43
- "-pix_fmt",
44
- "yuva420p",
45
- "-crf",
46
- f"{self.crf}",
47
- "-r",
48
- f"{self.fps}",
49
- "-s",
50
- f"{self.width//2*2}x{self.height//2*2}",
51
- "-threads",
52
- "16",
53
- "-vcodec",
54
- "libvpx-vp9",
55
- str(self.path),
56
- "-y",
57
- ],
58
- stdout=subprocess.DEVNULL if quiet else None,
59
- stderr=subprocess.STDOUT if quiet else None,
60
- )