Skip to content

Instantly share code, notes, and snippets.

@aoloe
Created March 15, 2018 19:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aoloe/7bddc219214c4ca626b09006939299cf to your computer and use it in GitHub Desktop.
Save aoloe/7bddc219214c4ca626b09006939299cf to your computer and use it in GitHub Desktop.
webm-project-draft.py
#!/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