1 import subprocess
, shutil
, os
, re
, sys
, ConfigParser
, time
, lrucache
3 from Config
import config
5 info_cache
= lrucache
.LRUCache(1000)
7 FFMPEG
= config
.get('Server', 'ffmpeg')
8 #SCRIPTDIR = os.path.dirname(__file__)
9 #FFMPEG = os.path.join(SCRIPTDIR, 'ffmpeg_mp2.exe')
10 #FFMPEG = '/usr/bin/ffmpeg'
13 # subprocess is broken for me on windows so super hack
14 def patchSubprocess():
15 o
= subprocess
.Popen
._make
_inheritable
17 def _make_inheritable(self
, handle
):
18 if not handle
: return subprocess
.GetCurrentProcess()
19 return o(self
, handle
)
21 subprocess
.Popen
._make
_inheritable
= _make_inheritable
22 mswindows
= (sys
.platform
== "win32")
26 def output_video(inFile
, outFile
):
27 if tivo_compatable(inFile
):
28 f
= file(inFile
, 'rb')
29 shutil
.copyfileobj(f
, outFile
)
32 transcode(inFile
, outFile
)
34 def transcode(inFile
, outFile
):
35 cmd
= [FFMPEG
, '-i', inFile
, '-vcodec', 'mpeg2video', '-r', '29.97', '-b', '4096K'] + select_aspect(inFile
) + ['-comment', 'pyTivo.py', '-ac', '2', '-ab', '192','-ar', '44100', '-f', 'vob', '-' ]
36 ffmpeg
= subprocess
.Popen(cmd
, stdout
=subprocess
.PIPE
)
38 shutil
.copyfileobj(ffmpeg
.stdout
, outFile
)
42 def select_aspect(inFile
):
43 type, width
, height
, fps
, millisecs
= video_info(inFile
)
46 ratio
= (width
*100)/height
47 rheight
, rwidth
= height
/d
, width
/d
49 if (rheight
, rwidth
) in [(4, 3), (10, 11), (15, 11), (59, 54), (59, 72), (59, 36), (59, 54)]:
50 return ['-aspect', '4:3', '-s', '720x480']
51 elif (rheight
, rwidth
) in [(16, 9), (20, 11), (40, 33), (118, 81), (59, 27)]:
52 return ['-aspect', '16:9', '-s', '720x480']
53 #If video is nearly 4:3 or 16:9 go ahead and strech it
54 elif ((ratio
<= 141) and (ratio
>= 125)):
55 return ['-aspect', '4:3', '-s', '720x480']
56 elif ((ratio
<= 185) and (ratio
>= 169)):
57 return ['-aspect', '16:9', '-s', '720x480']
60 settings
.append('-aspect')
61 settings
.append('4:3')
62 #If video is wider than 4:3 add top and bottom padding
65 endHeight
= (720*height
)/width
70 settings
.append('720x' + str(endHeight
))
72 topPadding
= ((480 - endHeight
)/2)
76 settings
.append('-padtop')
77 settings
.append(str(topPadding
))
78 bottomPadding
= (480 - endHeight
) - topPadding
79 settings
.append('-padbottom')
80 settings
.append(str(bottomPadding
))
83 #If video is taller than 4:3 add left and right padding, this is rare
85 endWidth
= (480*width
)/height
90 settings
.append(str(endWidth
) + 'x480')
92 leftPadding
= ((720 - endWidth
)/2)
96 settings
.append('-padleft')
97 settings
.append(str(leftPadding
))
98 rightPadding
= (720 - endWidth
) - leftPadding
99 settings
.append('-padright')
100 settings
.append(str(rightPadding
))
103 def tivo_compatable(inFile
):
104 suportedModes
= [[720, 480], [704, 480], [544, 480], [480, 480], [352, 480]]
105 type, width
, height
, fps
, millisecs
= video_info(inFile
)
106 #print type, width, height, fps, millisecs
108 if (inFile
[-5:]).lower() == '.tivo':
111 if not type == 'mpeg2video':
112 #print 'Not Tivo Codec'
115 if not fps
== '29.97':
116 #print 'Not Tivo fps'
119 for mode
in suportedModes
:
120 if (mode
[0], mode
[1]) == (width
, height
):
123 #print 'Not Tivo dimensions'
126 def video_info(inFile
):
127 if inFile
in info_cache
:
128 return info_cache
[inFile
]
130 if (inFile
[-5:]).lower() == '.tivo':
131 info_cache
[inFile
] = (True, True, True, True, True)
132 return True, True, True, True, True
134 cmd
= [FFMPEG
, '-i', inFile
]
135 ffmpeg
= subprocess
.Popen(cmd
, stderr
=subprocess
.PIPE
, stdout
=subprocess
.PIPE
, stdin
=subprocess
.PIPE
)
137 # wait 4 sec if ffmpeg is not back give up
140 if not ffmpeg
.poll() == None:
143 if ffmpeg
.poll() == None:
145 info_cache
[inFile
] = (None, None, None, None, None)
146 return None, None, None, None, None
148 output
= ffmpeg
.stderr
.read()
150 durre
= re
.compile(r
'.*Duration: (.{2}):(.{2}):(.{2})\.(.),')
151 d
= durre
.search(output
)
153 rezre
= re
.compile(r
'.*Video: ([^,]+),.*')
154 x
= rezre
.search(output
)
158 info_cache
[inFile
] = (None, None, None, None, None)
159 return None, None, None, None, None
161 rezre
= re
.compile(r
'.*Video: .+, (\d+)x(\d+),.*')
162 x
= rezre
.search(output
)
164 width
= int(x
.group(1))
165 height
= int(x
.group(2))
167 info_cache
[inFile
] = (None, None, None, None, None)
168 return None, None, None, None, None
170 rezre
= re
.compile(r
'.*Video: .+, (.+) fps.*')
171 x
= rezre
.search(output
)
175 info_cache
[inFile
] = (None, None, None, None, None)
176 return None, None, None, None, None
178 rezre
= re
.compile(r
'.*film source: (\d+).*')
179 x
= rezre
.search(output
.lower())
183 millisecs
= ((int(d
.group(1))*3600) + (int(d
.group(2))*60) + int(d
.group(3)))*1000 + (int(d
.group(4))*100)
184 info_cache
[inFile
] = (codec
, width
, height
, fps
, millisecs
)
185 return codec
, width
, height
, fps
, millisecs
187 def suported_format(inFile
):
188 if video_info(inFile
)[0]:
189 return video_info(inFile
)[4]
198 os
.kill(pid
, signal
.SIGKILL
)
202 handle
= ctypes
.windll
.kernel32
.OpenProcess(1, False, pid
)
203 ctypes
.windll
.kernel32
.TerminateProcess(handle
, -1)
204 ctypes
.windll
.kernel32
.CloseHandle(handle
)