Created
March 15, 2018 19:26
-
-
Save aoloe/7bddc219214c4ca626b09006939299cf to your computer and use it in GitHub Desktop.
webm-project-draft.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
import os | |
import sys | |
import datetime | |
import argparse | |
import yaml | |
import json | |
import subprocess | |
import tempfile | |
import base64 | |
parser = argparse.ArgumentParser(description='Merge webm files according to a proeject file.') | |
parser.add_argument('-info', action='store_true', | |
help='return the encoding, width and height of webm file') | |
parser.add_argument('-svg', action='store_true', | |
help='create the svg template') | |
parser.add_argument('-svg-frame', dest='svg_frame', action='store', | |
type=lambda t: datetime.datetime.strptime(t, '%M:%S'), | |
default=None, | |
help='create an svg with the screenshot from a specific frame') | |
parser.add_argument('-to-vp9', dest='to_vp9', action='store_true', | |
help='convert a webm file to use the vp9 video codec') | |
parser.add_argument('-generate', dest='generate', action='store_true', | |
help='generate the project yaml file') | |
parser.add_argument('-o', dest='output_file', metavar='out-file', type=argparse.FileType('wt'), | |
help='output file') | |
parser.add_argument('file', type=argparse.FileType('r'), nargs=1, | |
default=None, | |
help='file to be processed') | |
parser.add_argument('-debug', action='store_true', | |
help='output debug information') | |
args = parser.parse_args() | |
debug = args.debug | |
def get_if_debug(*args): | |
return locals()['args'] if debug else [] | |
def get_if_not_debug(*args): | |
return locals()['args'] if not debug else [] | |
svg_template = """<svg version="1.1" | |
baseProfile="full" | |
width="{width}" height="{height}" | |
xmlns="http://www.w3.org/2000/svg"> | |
{content} | |
</svg>""" | |
svg_image_template = '<image id="image1JPEG" x="0" y="0" width="{width}" height="{height}" xlink:href="data:image/png;base64,{image}">' | |
def get_webm_info(filename): | |
# mkvmerge --identification-format json --identify 01.webm | |
data = subprocess.check_output(['mkvmerge', '--identification-format', 'json', '--identify', filename]) | |
data_json = json.loads(data) | |
# print(data_json) | |
track_0 = data_json['tracks'][0] | |
dimensions = track_0['properties']['pixel_dimensions'].split('x') # or display_dimensions | |
return {'encoding': track_0['codec'], 'width': dimensions[0], 'height': dimensions[1]} | |
# returns VP9 or VP8 | |
def get_webm_codec(filename): | |
data = subprocess.check_output(['mkvmerge', '--identification-format', 'json', '--identify', filename]) | |
data_json = json.loads(data) | |
return data_json['tracks'][0]['codec'] # VP9 | VP8 | |
def webm_extract_frame(filename, duration): | |
# https://stackoverflow.com/questions/8577137/creating-a-tmp-file-in-python | |
fd, path = tempfile.mkstemp(suffix='.png') | |
os.remove(path) | |
call_args = ['ffmpeg', '-i', filename, '-ss', duration, '-vframes', '1', path] | |
subprocess.call(call_args) | |
with open(path, "rb") as f: | |
image = base64.b64encode(f.read()).decode('utf-8') | |
f.close() | |
os.close(fd) | |
return image | |
def webm_to_vp9(filename, output): | |
call_args = ['ffmpeg', '-i', filename, '-c:v', 'libvpx-vp9', path] | |
subprocess.call(call_args) | |
def write_svg_file(filename, dimension, content = ''): | |
svg = svg_template.format(width = dimension[0], height = dimension[1], content = content) | |
put_file_data(filename, svg) | |
with open(filename, 'w') as f: | |
f.write(svg + '\n') | |
f.close() | |
def generate_project_yaml(filename, info, reference_filename): | |
project = { | |
'size' : { | |
'width': info[width], | |
'height': info[height] | |
}, | |
'tracks' : [ | |
{'file': 'cover.svg', 'duration': 4}, | |
{'file': reference_filename}, | |
{'file': 'closing.svg', 'duration': 4} | |
] | |
} | |
print(project) | |
with open(filename, 'w') as f: | |
yaml.dump(project, f, default_flow_style=False) | |
f.close() | |
def get_png_from_svg(filename, dimension): | |
fd, path = tempfile.mkstemp(suffix='.png') | |
os.remove(path) | |
call_args = ['convert', '-density', str(300), '-resize', '{}x{}'.format(dimension[0], dimension[1]), filename, path] | |
subprocess.call(call_args) | |
os.close(fd) | |
return path | |
def get_webm_from_png(filename, duration): | |
fd, path = tempfile.mkstemp(suffix='.webm') | |
os.remove(path) | |
# eventually '-vf scale=320:240' after yuv420p | |
# libvpx-vp9 for vp9 | |
call_args = ['ffmpeg', *get_if_not_debug('-loglevel', 'panic'), '-loop', str(1), '-i', filename, '-t', str(duration), '-c:v', 'libvpx-vp9', '-pix_fmt', 'yuv420p', path] | |
print(' '.join(call_args)) | |
subprocess.call(call_args) | |
os.close(fd) | |
return path | |
def generate_merged_webm(filename, tracks): | |
print(tracks) | |
call_args = ['mkvmerge', '-o', filename, '-w', ' +'.join(tracks)] | |
print(' '.join(call_args)) | |
subprocess.call(call_args) | |
# first, process the "special" actions and exit | |
if args.info: | |
print(get_webm_info(args.file[0].name)) | |
sys.exit() | |
if args.svg: | |
output_filename = 'template.svg' if args.output_file is None else args.output_file.name | |
info = get_webm_info(args.file[0].name) | |
write_svg_file(output_filename, info['width'], info['height']) | |
sys.exit() | |
if args.svg_frame: | |
# TODO: add the timestamp to the default file name | |
output_filename = 'frame.svg' if args.output_file is None else args.output_file.name | |
# TODO: make sure that the timestamp is inside the webm length | |
image = webm_extract_frame(args.file[0].name, '{0:%M:%S}'.format(args.svg_frame)) | |
info = get_webm_info(args.file[0].name) | |
width, height = (info['width'], info['height']) | |
write_svg_file(output_filename, [width, height], svg_image_template.format(width=width, height=height, image=image)) | |
sys.exit() | |
if args.to_vp9: | |
output_filename = 'output.webm' if args.output_file is None else args.output_file.name | |
webm_to_vp9(args.file[0].name, output_filename) | |
sys.exit() | |
if args.generate: | |
output_filename = 'webm-project.yaml' if args.output_file is None else args.output_file.name | |
generate_project_yaml(output_filename, get_webm_info(args.file[0].name), args.file[0].name) | |
sys.exit() | |
# how to require another argumets when one is set | |
# if ... | |
# parser.error("-svg-frame requires -o") | |
# sys.exit() | |
# if we got here, we're in the default mode and are processing the project | |
project = yaml.load(args.file[0], Loader=yaml.Loader) | |
# print(project) | |
output_filename = 'output.webm' if args.output_file is None else args.output_file.name | |
webm_tracks = [] | |
print(project['tracks']) | |
for track in project['tracks']: | |
file_basename, file_extension = os.path.splitext(track['file']) | |
# print(track['file']) | |
# print(file_extension) | |
if file_extension == '.webm': | |
filename = track['file'] | |
elif file_extension == '.svg': | |
filename_png = get_png_from_svg(track['file'], [project['size']['width'], project['size']['height']]) | |
# TODO: how to cleanup this file? | |
filename = get_webm_from_png(filename_png, track['duration']) | |
os.remove(filename_png) | |
elif file_extension == '.png': | |
filename = get_webm_from_png(filename, track['duration']) | |
# print(filename) | |
webm_tracks.append(filename) | |
if webm_tracks: | |
generate_merged_webm(output_filename, webm_tracks) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment