pyTivo
[pyTivo/krkeegan.git] / transcode.py
blobf04b1109e8878a2a4c830164cd9eb62de3d291dc
1 import subprocess, shutil, os, re, sys
3 SCRIPTDIR = os.path.dirname(__file__)
5 # XXX BIG HACK
6 # subprocess is broken for me on windows so super hack
7 def patchSubprocess():
8 o = subprocess.Popen._make_inheritable
10 def _make_inheritable(self, handle):
11 if not handle: return subprocess.GetCurrentProcess()
12 return o(self, handle)
14 subprocess.Popen._make_inheritable = _make_inheritable
15 mswindows = (sys.platform == "win32")
16 if mswindows:
17 patchSubprocess()
19 def output_video(inFile, outFile):
20 if tivo_compatable(inFile):
21 f = file(inFile, 'rb')
22 shutil.copyfileobj(f, outFile)
23 f.close()
24 else:
25 transcode(inFile, outFile)
27 def transcode(inFile, outFile):
29 cmd = SCRIPTDIR + "\\ffmpeg_mp2.exe -i \"%s\" -vcodec mpeg2video -r 29.97 -b 4096 %s -ac 2 -ab 192 -f vob -" % (inFile, select_aspect(inFile))
30 # subprocess is busted in my python 2.5 when run without a console. Setting all to PIPE fixes it.
31 # but now we get this nice select loop to dump the stderr data
32 ffmpeg = subprocess.Popen(cmd, stdout=subprocess.PIPE)
33 try:
34 shutil.copyfileobj(ffmpeg.stdout, outFile)
35 except:
36 win32kill(ffmpeg.pid)
38 def select_aspect(inFile):
39 type, height, width, fps = video_info(inFile)
41 d = gcd(height,width)
43 rheight, rwidth = height/d, width/d
45 if (rheight, rwidth) in [(4, 3), (10, 11), (15, 11), (59, 54), (59, 72), (59, 36), (59, 54)]:
46 return '-aspect 4:3 -s 720x480'
47 elif (rheight, rwidth) in [(16, 9), (20, 11), (40, 33), (118, 81), (59, 27)]:
48 return '-aspect 16:9 -s 720x480'
49 else:
50 settings = []
51 settings.append('-aspect 16:9')
53 endHeight = (720*width)/height
54 if endHeight % 2:
55 endHeight -= 1
57 settings.append('-s 720x' + str(endHeight))
59 topPadding = ((480 - endHeight)/2)
60 if topPadding % 2:
61 topPadding -= 1
63 settings.append('-padtop ' + str(topPadding))
64 bottomPadding = (480 - endHeight) - topPadding
65 settings.append('-padbottom ' + str(bottomPadding))
67 return ' '.join(settings)
69 def tivo_compatable(inFile):
70 suportedModes = [[720, 480], [704, 480], [544, 480], [480, 480], [352, 480]]
71 type, height, width, fps = video_info(inFile)
73 if not type == 'mpeg2video':
74 return False
76 if not fps == '29.97':
77 return False
79 for mode in suportedModes:
80 if (mode[0], mode[1]) == (height, width):
81 return True
82 return False
84 def video_info(inFile):
85 cmd = SCRIPTDIR + "\\ffmpeg_mp2.exe -i \"%s\"" % inFile
86 ffmpeg = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
87 output = ffmpeg.stderr.read()
89 rezre = re.compile(r'.*Video: (.+), (\d+)x(\d+), (.+) fps.*')
90 m = rezre.search(output)
91 if m:
92 return m.group(1), int(m.group(2)), int(m.group(3)), m.group(4)
93 else:
94 return None, None, None, None
96 def suported_format(inFile):
97 if video_info(inFile)[0]:
98 return True
99 else:
100 return False
102 def win32kill(pid):
103 import ctypes
104 handle = ctypes.windll.kernel32.OpenProcess(1, False, pid)
105 ctypes.windll.kernel32.TerminateProcess(handle, -1)
106 ctypes.windll.kernel32.CloseHandle(handle)
108 def gcd(a,b):
109 while b:
110 a, b = b, a % b
111 return a