forked from FutureDays/thm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
transcodes.py
229 lines (217 loc) · 8.36 KB
/
transcodes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#!/usr/bin/env python
'''
handles all transcodes and concatenations
'''
import sys
import os
import asyncio
from asyncio.subprocess import PIPE
import subprocess
import logging
import pathlib
import traceback
logger = logging.getLogger(__name__)
def format_ffmpeg_log():
'''
formats ffmpeg.log for THM log file
'''
fflog_raw = []
fflog_proc = ''
fflog_path = pathlib.Path("ffmpeg.log")
try:
with open(str(fflog_path),"r") as ffmpeg_log:
while True:
line = ffmpeg_log.readline()
if not line.startswith("frame"):
fflog_raw.append(line)
else:
break
for line in fflog_raw:
fflog_proc += line
logger.debug(fflog_proc)
fflog_path.unlink()
return True
except Exception as e:
logger.error("there was an issue formatting the ffmpeg log")
logger.error(traceback.format_exc())
return False
def run_ffmpeg(cmd):
'''
runs cmd for ffmpeg
'''
try:
logger.info("running ffmpeg with below command:")
logger.info("%s", cmd)
output = subprocess.run(cmd, shell=True)
if not output.returncode == 0:
logger.error("there was an error transcoding that file, see log for details")
return False
else:
format_ffmpeg_log()
logger.info("ffmpeg ran successfully")
return True
except Exception as e:
logger.error("there was an issue running ffmpeg")
logger.error(traceback.format_exc())
return False
def make_mpg_dvd(accession, file, kwargs):
'''
make mpg for dvd
'''
logger.info("creating mpeg derivative for DVD")
mpeg = accession + "_dvd.mpg"
mpeg_fullpath = kwargs.config.raw_captures / accession / mpeg
segment = accession.split("_")[-1]
if kwargs.is_interlaced:
yadif = "yadif,"
else:
yadif = ""
drawtext = '"drawtext=fontfile=' + r"'C\:\\Windows\\Fonts\\arial.ttf':timecode='"+ segment[-2:] + \
"\:00\:00\;00':r=29.97:x=(w-tw)/2:y=h-(2*lh):fontcolor=white:fontsize=72:box=1:boxcolor=0x00000099"
ffmpeg_cmd = 'ffmpeg -i ' + str(file) + ' -target ntsc-dvd -ac 2 -b:v 5000k -vtag xvid -vf ' + yadif + drawtext + \
',scale=720:480" -threads 0 -y ' + str(mpeg_fullpath) + kwargs.ffmpeg_suffix
ffmpeg_ok = run_ffmpeg(ffmpeg_cmd)
if not ffmpeg_ok:
return False
return mpeg_fullpath
def make_mp4_with_tc(accession, file, kwargs):
'''
creates mp4 with burned in timecode
'''
logger.info("creating mp4 derivative with burned-in timecode")
mp4 = accession + "_tc.mp4"
mp4_fullpath = kwargs.config.raw_captures / accession / mp4
segment = accession.split("_")[-1]
if kwargs.is_interlaced:
yadif = "yadif,"
else:
yadif = ""
drawtext = '"drawtext=fontfile=' + r"'C\:\\Windows\\Fonts\\arial.ttf':timecode='"+ segment[-2:] + \
"\:00\:00\;00':r=29.97:x=(w-text_w)/2:y=(h-text_h)/1.2:fontcolor=white:fontsize=72:box=1:boxcolor=0x00000099"
ffmpeg_cmd = 'ffmpeg -i ' + str(file) + \
' -c:v libx264 -b:v 372k -pix_fmt yuv420p -r 29.97 -vf ' + yadif + drawtext + \
',scale=420:270" -c:a aac -ar 44100 -ac 2 -map -0:d? -threads 0 -y ' + str(mp4_fullpath) + kwargs.ffmpeg_suffix
ffmpeg_ok = run_ffmpeg(ffmpeg_cmd)
if not ffmpeg_ok:
return False
return mp4_fullpath
def make_mp4_with_logo(accession, file, kwargs):
'''
creates mp4 derivative with logo
'''
logger.info("creating mp4 derivative with logo")
mp4 = accession + "_wm.mp4"
mp4_fullpath = kwargs.config.raw_captures / accession / mp4
if kwargs.is_interlaced:
yadif = "[0]yadif,"
else:
yadif = ""
ffmpeg_cmd = 'ffmpeg -i ' + str(file) + ' -i ' + str(kwargs.config.watermark_white) + \
' -filter_complex ' + yadif + 'overlay=0:0,scale=420:270 ' \
+ '-c:v libx264 -b:v 372k -pix_fmt yuv420p -r 29.97 -c:a aac -ar 44100 -ac 2 -map -0:d? -threads 0 -y ' \
+ str(mp4_fullpath) + kwargs.ffmpeg_suffix
ffmpeg_ok = run_ffmpeg(ffmpeg_cmd)
if not ffmpeg_ok:
return False
return mp4_fullpath
def make_mxf_mezz(accession, file, kwargs):
'''
creates mxf mezzanine file
'''
logger.info("creating mxf mezzanine file")
mxf = accession + "_mezz.mxf"
mxf_fullpath = kwargs.config.raw_captures / accession / mxf
ffmpeg_cmd = 'ffmpeg -i ' + str(file) + \
' -c:v libx264 -pix_fmt yuv422p -b:v 15000k -r 30/1.001 -c:a pcm_s24le -map 0:v -map 0:a -map -0:d? -threads 0 -y ' + str(mxf_fullpath)
ffmpeg_ok = run_ffmpeg(ffmpeg_cmd)
if not ffmpeg_ok:
return False
return mxf_fullpath
def concatenate_raw_captures(accession, files, kwargs):
'''
concatenates raw captures in accession folder
'''
accession_dir = files[0].parent
file_ext = files[0].suffix
if ".MOV" in file_ext:
file_ext = ".mov"
segment = accession.split("_")[-1]
logger.info("concatenating input files in directory %s", str(accession_dir))
concat_txt_path = accession_dir / "concat.txt"
concat_vid = concat_txt_path.with_suffix(file_ext)
accession_pres = concat_txt_path.with_name(accession + "_pres" + file_ext)
with open(concat_txt_path,"a") as concat_txt:
for file in files:
concat_txt.write('file ' + str(file.name) + "\n")
ffmpeg_cmd = 'ffmpeg -f concat -dn -i concat.txt -map 0:v -map 0:a -c:v copy -c:a copy -ignore_unknown -timecode ' \
+ segment[-2:] + ':00:00;00 -y ' + str(concat_vid) + kwargs.ffmpeg_suffix
ffmpeg_ok = run_ffmpeg(ffmpeg_cmd)
if not ffmpeg_ok:
logger.error("ffmpeg encountered an error during concatenation")
return False
else:
logger.info("concatenation completed successfully")
concat_vid.replace(accession_pres)
concat_txt_path.unlink()
return str(accession_pres)
def detect_interlaced_video(file, kwargs):
'''
detects if input file is interlaced
'''
logger.info("testing %s file for interlaced video", str(file))
ffmpeg_cmd = "ffmpeg -filter:v idet -frames:v 360 -an -f rawvideo -y NUL -i " + str(file)
ffmpeg_cmd = "ffprobe -v quiet -select_streams v -show_entries stream=field_order -of csv=p=0 -i " + str(file)
logger.info(ffmpeg_cmd)
ffmpeg_ok = subprocess.run(ffmpeg_cmd, capture_output=True)
logger.info(ffmpeg_ok.stdout.decode("utf-8").strip())
if not ffmpeg_ok.returncode == 0:
logger.error("ffmpeg encountered an error during interlace detection")
return False
else:
output = ffmpeg_ok.stdout.decode("utf-8").strip()
if "tff" in output or "bff" in output or "tb" in output or "bt" in output:
kwargs.is_interlaced = True
return kwargs
elif "progressive" in output or "unknown" in output:
kwargs.is_interlaced = False
return kwargs
logger.error("ffprobe unable to detect progressive or interlaced video")
return False
def rewrap_mp4_streams_in_mov(mp4_file, kwargs):
'''
takes input file, assumed mp4 with (non-spec) pcm audio
and rewraps the streams in mov
'''
logger.info("re-wraping audio and video streams in mov")
segment = str(file.stem).split("_")[-1]
mov_file = mp4_file.with_suffix(".mov")
ffmpeg_cmd = "ffmpeg -i " + str(mp4_file) + " -c copy -map -0:d? -timecode " \
+ segment[-2:] + ":00:00;00 -y " + str(mov_file) + kwargs.ffmpeg_suffix
output = run_ffmpeg(ffmpeg_cmd)
if not output:
logger.error("ffmpeg encountered an error during re-wrap")
return False
else:
return mov_file
def rewrap_single_file_accession(accession, input_file, kwargs):
'''
for single file accessions, we need to rewrap the files with correct timecode
'''
logger.info("rewrapping single video file accession with correct timecode")
segment = accession.split("_")[-1]
pres_file = input_file.parent / pathlib.Path(accession + "_pres" + ".mov")
ffmpeg_cmd = "ffmpeg -i " + str(input_file) + " -c copy -map -0:d? -timecode " \
+ segment[-2:] + ":00:00;00 -y " + str(pres_file) + kwargs.ffmpeg_suffix
output = run_ffmpeg(ffmpeg_cmd)
if not output:
logger.error("there was an issue rewrapping that file")
return False
else:
return pres_file
def main():
'''
do the thing
'''
print("howdy")
if __name__ == "__main__":
main()