pandora_client/pandora_client/extract.py

327 lines
9.7 KiB
Python
Raw Normal View History

2010-11-16 20:22:30 +00:00
#!/usr/bin/python
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
# GPL 2010
2016-06-15 14:12:37 +00:00
from __future__ import division, with_statement, print_function
2010-11-16 20:22:30 +00:00
import os
import subprocess
import sys
import shutil
import ox
2015-10-09 13:09:39 +00:00
from utils import AspectRatio, run_command, basedir
2010-11-16 20:22:30 +00:00
2012-02-14 06:38:01 +00:00
def command(program):
2015-10-09 13:09:39 +00:00
local = os.path.join(basedir(), 'bin', program)
if sys.platform.startswith('win'):
local += '.exe'
2012-02-14 06:38:01 +00:00
if os.path.exists(local):
program = local
return program
2010-11-16 20:22:30 +00:00
def frame(video, target, position):
fdir = os.path.dirname(target)
if fdir and not os.path.exists(fdir):
os.makedirs(fdir)
2010-12-28 14:14:48 +00:00
'''
2016-06-15 14:12:37 +00:00
# oxframe
2010-12-28 14:14:48 +00:00
cmd = ['oxframe', '-i', video, '-p', str(position), '-o', target]
2016-06-15 14:12:37 +00:00
print(cmd)
2010-12-28 14:14:48 +00:00
r = run_command(cmd)
return r == 0
'''
2011-08-23 19:20:01 +00:00
'''
2016-06-15 14:12:37 +00:00
# mplayer
2010-11-16 20:22:30 +00:00
cwd = os.getcwd()
target = os.path.abspath(target)
framedir = tempfile.mkdtemp()
os.chdir(framedir)
cmd = ['mplayer', '-noautosub', video, '-ss', str(position), '-frames', '2', '-vo', 'png:z=9', '-ao', 'null']
2016-06-15 14:12:37 +00:00
print(cmd)
2010-11-16 20:22:30 +00:00
r = run_command(cmd)
2015-10-09 13:09:39 +00:00
images = glob('%s%s*.png' % (framedir, os.sep))
2010-11-16 20:22:30 +00:00
if images:
shutil.move(images[-1], target)
r = 0
else:
r = 1
os.chdir(cwd)
shutil.rmtree(framedir)
return r == 0
2011-08-23 19:20:01 +00:00
'''
2016-06-15 14:12:37 +00:00
# ffmpeg
2013-07-22 07:26:22 +00:00
pre = position - 2
if pre < 0:
pre = 0
else:
position = 2
cmd = [command('ffmpeg'), '-y', '-ss', str(pre), '-i', video, '-ss', str(position),
2016-06-15 14:12:37 +00:00
'-vf', 'scale=iw*sar:ih',
'-an', '-vframes', '1', target]
2011-08-23 19:20:01 +00:00
r = run_command(cmd)
return r == 0
2010-11-16 20:22:30 +00:00
def supported_formats():
p = subprocess.Popen([command('ffmpeg'), '-codecs'],
2016-06-15 14:12:37 +00:00
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
return {
2016-06-15 14:12:37 +00:00
# 'ogg': 'libtheora' in stdout and 'libvorbis' in stdout,
'webm': 'libvpx' in stdout and 'libvorbis' in stdout,
'mp4': 'libx264' in stdout and 'libvo_aacenc' in stdout,
}
2012-01-02 12:32:15 +00:00
def video_cmd(video, target, profile, info):
2010-11-16 20:22:30 +00:00
if not os.path.exists(target):
2011-04-06 14:42:40 +00:00
ox.makedirs(os.path.dirname(target))
2011-02-23 11:55:40 +00:00
2010-11-16 20:22:30 +00:00
'''
look into
lag
mb_static_threshold
qmax/qmin
rc_buf_aggressivity=0.95
token_partitions=4
level / speedlevel
bt?
'''
2011-02-06 12:43:50 +00:00
profile, format = profile.split('.')
2011-12-28 15:00:17 +00:00
bpp = 0.17
2011-02-06 12:43:50 +00:00
2013-04-22 11:41:16 +00:00
if profile == '1080p':
height = 1080
audiorate = 48000
audioquality = 6
audiobitrate = None
audiochannels = None
2016-08-03 09:40:48 +00:00
elif profile == '720p':
2010-11-16 20:22:30 +00:00
height = 720
audiorate = 48000
audioquality = 5
audiobitrate = None
audiochannels = None
2016-08-03 09:40:48 +00:00
elif profile == '480p':
2010-11-16 20:22:30 +00:00
height = 480
audiorate = 44100
2011-02-04 12:17:10 +00:00
audioquality = 3
2010-11-16 20:22:30 +00:00
audiobitrate = None
audiochannels = 2
2012-04-19 16:08:13 +00:00
elif profile == '432p':
height = 432
audiorate = 44100
audioquality = 2
audiobitrate = None
audiochannels = 2
2010-11-16 20:22:30 +00:00
elif profile == '360p':
height = 360
audiorate = 44100
audioquality = 1
audiobitrate = None
audiochannels = 1
2012-04-19 16:08:13 +00:00
elif profile == '288p':
height = 288
audiorate = 44100
audioquality = 0
audiobitrate = None
audiochannels = 1
2011-02-06 12:49:36 +00:00
elif profile == '240p':
height = 240
2010-11-16 20:22:30 +00:00
audiorate = 44100
audioquality = 0
audiobitrate = None
audiochannels = 1
2012-04-19 16:08:13 +00:00
elif profile == '144p':
height = 144
audiorate = 22050
audioquality = -1
audiobitrate = '22k'
audiochannels = 1
2010-11-16 20:22:30 +00:00
else:
height = 96
audiorate = 22050
audioquality = -1
audiobitrate = '22k'
audiochannels = 1
if info['video'] and 'display_aspect_ratio' in info['video'][0]:
# dont make video bigger
height = min(height, info['video'][0]['height'])
2011-04-06 14:42:40 +00:00
dar = AspectRatio(info['video'][0]['display_aspect_ratio'])
if info['video'][0]['framerate'] == '0:0':
fps = 25
else:
fps = AspectRatio(info['video'][0]['framerate'])
2011-02-23 11:55:40 +00:00
width = int(dar * height)
width += width % 2
2012-01-04 09:04:23 +00:00
extra = []
if fps == 50:
fps = 25
extra += ['-r', '25']
if fps == 60:
fps = 30
extra += ['-r', '30']
2013-06-06 10:29:40 +00:00
fps = min(float(fps), 30)
2011-02-23 11:55:40 +00:00
bitrate = height*width*fps*bpp/1000
aspect = dar.ratio
2016-06-15 14:12:37 +00:00
# use 1:1 pixel aspect ratio if dar is close to that
2011-02-23 11:55:40 +00:00
if abs(width/height - dar) < 0.02:
aspect = '%s:%s' % (width, height)
video_settings = [
2016-06-15 14:12:37 +00:00
'-vb', '%dk' % bitrate,
2011-02-23 11:55:40 +00:00
'-aspect', aspect,
2012-01-05 15:42:45 +00:00
'-g', '%d' % int(fps*5),
2016-06-15 14:12:37 +00:00
'-vf', 'yadif,hqdn3d,scale=%s:%s' % (width, height),
2012-01-04 09:04:23 +00:00
] + extra
2012-03-21 23:17:40 +00:00
if format == 'webm':
video_settings += [
2015-10-17 12:35:38 +00:00
'-vcodec', 'libvpx',
2015-12-24 15:46:43 +00:00
'-quality', 'good',
2012-03-21 23:17:40 +00:00
'-cpu-used', '0',
'-lag-in-frames', '16',
'-auto-alt-ref', '1',
]
if format == 'mp4':
2016-06-15 14:12:37 +00:00
# quicktime does not support bpyramid
'''
video_settings += [
'-vcodec', 'libx264',
'-flags', '+loop+mv4',
'-cmp', '256',
'-partitions', '+parti4x4+parti8x8+partp4x4+partp8x8+partb8x8',
'-me_method', 'hex',
'-subq', '7',
'-trellis', '1',
'-refs', '5',
'-bf', '3',
'-flags2', '+bpyramid+wpred+mixed_refs+dct8x8',
'-coder', '1',
'-me_range', '16',
'-keyint_min', '25', #FIXME: should this be related to fps?
'-sc_threshold','40',
'-i_qfactor', '0.71',
'-qmin', '10', '-qmax', '51',
'-qdiff', '4'
]
'''
video_settings += [
'-vcodec', 'libx264',
'-flags', '+loop+mv4',
'-cmp', '256',
'-partitions', '+parti4x4+parti8x8+partp4x4+partp8x8+partb8x8',
'-me_method', 'hex',
'-subq', '7',
'-trellis', '1',
'-refs', '5',
'-bf', '0',
'-flags2', '+mixed_refs',
'-coder', '0',
'-me_range', '16',
'-sc_threshold', '40',
'-i_qfactor', '0.71',
'-qmin', '10', '-qmax', '51',
'-qdiff', '4'
]
2016-06-15 14:12:37 +00:00
video_settings += ['-map', '0:%s,0:0' % info['video'][0]['id']]
2011-02-23 11:55:40 +00:00
else:
video_settings = ['-vn']
2010-11-16 20:22:30 +00:00
if info['audio']:
2013-02-08 12:39:16 +00:00
if video_settings == ['-vn'] or not info['video']:
n = 0
else:
n = 1
# mix 2 mono channels into stereo(common for fcp dv mov files)
if len(info['audio']) == 2 \
and len(filter(None, [a['channels'] == 1 or None for a in info['audio']])) == 2:
video_settings += [
'-filter_complex',
'[0:%s][0:%s] amerge' % (info['audio'][0]['id'], info['audio'][1]['id'])
]
mono_mix = True
else:
video_settings += ['-map', '0:%s,0:%s' % (info['audio'][0]['id'], n)]
mono_mix = False
2010-11-16 20:22:30 +00:00
audio_settings = ['-ar', str(audiorate), '-aq', str(audioquality)]
if mono_mix:
ac = 2
else:
ac = info['audio'][0].get('channels')
if not ac:
2016-08-04 19:32:35 +00:00
ac = audiochannels
if audiochannels:
ac = min(ac, audiochannels)
audio_settings += ['-ac', str(ac)]
2010-11-16 20:22:30 +00:00
if audiobitrate:
audio_settings += ['-ab', audiobitrate]
if format == 'mp4':
audio_settings += ['-acodec', 'libvo_aacenc']
else:
audio_settings += ['-acodec', 'libvorbis']
2010-11-16 20:22:30 +00:00
else:
audio_settings = ['-an']
cmd = [command('ffmpeg'), '-y', '-i', video, '-threads', '4', '-map_metadata', '-1'] \
2010-11-16 20:22:30 +00:00
+ audio_settings \
+ video_settings
if format == 'webm':
cmd += ['-f', 'webm', target]
elif format == 'mp4':
2016-06-15 14:12:37 +00:00
# mp4 needs postprocessing(qt-faststart), write to temp file
cmd += ["%s.mp4" % target]
else:
cmd += [target]
2012-01-02 12:32:15 +00:00
return cmd
def video(video, target, profile, info):
enc_target = target if target.endswith('.mp4') else target + '.tmp.webm'
cmd = video_cmd(video, enc_target, profile, info)
2013-02-08 11:02:46 +00:00
profile, format = profile.split('.')
2016-06-15 14:12:37 +00:00
# r = run_command(cmd, -1)
2010-11-27 12:15:11 +00:00
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
try:
p.wait()
r = p.returncode
if format == 'mp4':
cmd = [command('qt-faststart'), "%s.mp4" % target, target]
p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
2016-06-15 14:12:37 +00:00
stdout=subprocess.PIPE if sys.platform.startswith('win') else open('/dev/null', 'w'),
stderr=subprocess.STDOUT)
p.communicate()
os.unlink("%s.mp4" % target)
elif r == 0 and target != enc_target:
shutil.move(enc_target, target)
2016-06-15 14:12:37 +00:00
print('Input:\t', video)
print('Output:\t', target)
except KeyboardInterrupt:
2013-02-10 09:07:36 +00:00
p.kill()
r = 1
if os.path.exists(enc_target):
2016-06-15 14:12:37 +00:00
print("\n\ncleanup unfinished encoding:\nremoving", enc_target)
print("\n")
os.unlink(enc_target)
2011-12-30 13:17:28 +00:00
if os.path.exists(target):
2016-06-15 14:12:37 +00:00
print("\n\ncleanup unfinished encoding:\nremoving", target)
print("\n")
2011-12-30 13:17:28 +00:00
os.unlink(target)
if format == 'mp4' and os.path.exists("%s.mp4" % target):
os.unlink("%s.mp4" % target)
sys.exit(1)
2010-11-16 20:22:30 +00:00
return r == 0