一、问题描述
在用Python爬取网络视频时,利用了ffmpeg下载并合并m3u8文件,在CMD上运行结果如下:
C:\Users\fz.000>ffmpeg -i "https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535101557-0-0-fedf56c648bfe0d60ef8e0aa89b6a297&expiration=1535101557&disable_local_cache=0" -c copy "E:/PycharmProjects/Video_Crack/video/video1.mp4"
ffmpeg version N-91646-g78d4b6bd43 Copyright (c) 2000-2018 the FFmpeg developers
built with gcc 8.2.1 (GCC) 20180813
configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth
libavutil 56. 19.100 / 56. 19.100
libavcodec 58. 23.100 / 58. 23.100
libavformat 58. 17.103 / 58. 17.103
libavdevice 58. 4.101 / 58. 4.101
libavfilter 7. 26.100 / 7. 26.100
libswscale 5. 2.100 / 5. 2.100
libswresample 3. 2.100 / 3. 2.100
libpostproc 55. 2.100 / 55. 2.100
[hls,applehttp @ 00000263fedba840] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00001.ts?auth_key=1535101557-0-0-e6986ea2f49dd0a3ad5954823a79b3d0' for reading
[hls,applehttp @ 00000263fedba840] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00002.ts?auth_key=1535101557-0-0-1f6b4fb3db9df8067d972990c71a74cb' for reading
Input #0, hls,applehttp, from 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535101557-0-0-fedf56c648bfe0d60ef8e0aa89b6a297&expiration=1535101557&disable_local_cache=0':
Duration: 00:00:59.12, start: 1.433556, bitrate: N/A
Program 0
Metadata:
variant_bitrate : 0
Stream #0:0: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p, 1280x2276, 25 fps, 25 tbr, 90k tbn, 50 tbc
Metadata:
variant_bitrate : 0
Stream #0:1: Audio: aac (LC) ([15][0][0][0] / 0x000F), 44100 Hz, stereo, fltp
Metadata:
variant_bitrate : 0
Output #0, mp4, to 'E:/PycharmProjects/Video_Crack/video/video1.mp4':
Metadata:
encoder : Lavf58.17.103
Stream #0:0: Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x2276, q=2-31, 25 fps, 25 tbr, 90k tbn, 90k tbc
Metadata:
variant_bitrate : 0
Stream #0:1: Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp
Metadata:
variant_bitrate : 0
Stream mapping:
Stream #0:0 -> #0:0 (copy)
Stream #0:1 -> #0:1 (copy)
Press [q] to stop, [?] for help
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00003.ts?auth_key=1535101557-0-0-91103599f3d036c53025d4469811f47f' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00004.ts?auth_key=1535101557-0-0-5425a38189bbdc37cce8e75c99ab8bc0' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00005.ts?auth_key=1535101557-0-0-9b2cfb83e2058f2904be5b9fc3cd2665' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00006.ts?auth_key=1535101557-0-0-7c76c5b983dec9efe285a19beafe7fa4' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00007.ts?auth_key=1535101557-0-0-973c55d77a4dc646c11d79aeba2bac4f' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00008.ts?auth_key=1535101557-0-0-76f76084ff3e5bd058aa7e00336b07d4' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00009.ts?auth_key=1535101557-0-0-82b119c4d5e5e756c4449e5870e18f00' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00010.ts?auth_key=1535101557-0-0-3530886b5ebacb68c63dca9b1b0d31af' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00011.ts?auth_key=1535101557-0-0-22cba48926d6bfedd02711f7da42a577' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00012.ts?auth_key=1535101557-0-0-af00d2111cf04e0d1d81f6523cb3d2ab' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00013.ts?auth_key=1535101557-0-0-64bd48246a8ba362cedfff4a78d1ebfa' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00014.ts?auth_key=1535101557-0-0-b82190c2c1a4ddebeea337688c1a351e' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00015.ts?auth_key=1535101557-0-0-72da7559f7fbe2ec24f30ef0be2cbc50' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00016.ts?auth_key=1535101557-0-0-7c93a14b6c6c10b09d24b30ea4bf7db3' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00017.ts?auth_key=1535101557-0-0-a7ae4f217482020d64e85c2e78704cd2' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00018.ts?auth_key=1535101557-0-0-f74f9fb587e0b96e1a1a053bcb2e511d' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00019.ts?auth_key=1535101557-0-0-21fcb34bb2c06fb165ed29083d278014' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00020.ts?auth_key=1535101557-0-0-36b552cde620d564fe469a5d7138111a' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00021.ts?auth_key=1535101557-0-0-fd86d0f8d8e7815f3da90626f4855233' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00022.ts?auth_key=1535101557-0-0-6cfd887896c6292abb643c8fc6d05535' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00023.ts?auth_key=1535101557-0-0-513a1071888bd474984b9b37345b997c' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00024.ts?auth_key=1535101557-0-0-126f976c659bf9bc55eacb6dfdb0f1e0' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00025.ts?auth_key=1535101557-0-0-1a990e2b3bbb8167c9dc1c3ae4fe644b' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00026.ts?auth_key=1535101557-0-0-9fa465a24d3329a415484e286fbd252d' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00027.ts?auth_key=1535101557-0-0-6cb593e7c453f0d981e3ddc7baff6ace' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00028.ts?auth_key=1535101557-0-0-fbdf9f5f84c52e39c5d973e53eb01f75' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00029.ts?auth_key=1535101557-0-0-95cf329432d5c39f98c8a7554194477e' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00030.ts?auth_key=1535101557-0-0-a601529344546d2b3733e2e6454b475c' for reading
frame= 1479 fps= 58 q=-1.0 Lsize= 15220kB time=00:00:59.11 bitrate=2109.0kbits/s speed=2.33x
video:14263kB audio:924kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.210394%
C:\Users\fz.000>
运行结束后,在相应的目录下就会有下载好的视频。
运行命令后的结果中有这么一句:
也就是说下载过程中按下“q”键会终止下载。用同样的视频m3u8链接尝试了一下,果然还没下载完就立刻停止了,从文件的大小就可以看出来,是比原来要小的,不过这时的视频仍然能够正常播放(这就是后面我们要的效果)。
下面我们不用CMD,而是用python程序实现这个过程:
该程序使用subprocess模块创建并返回一个子进程,并在这个子进程中执行指定的程序(也就是ffmpeg命令)。同时可以进行进程间通信,将子程序的输出信息(也就是采用命令行时的那一大片输出信息)打印出来。
subprocess模块用法的相关用法参见:https://blog.csdn.net/polyhedronx/article/details/82015271
import shlex
import subprocess
if __name__ == '__main__':
try:
shell_cmd = 'ffmpeg -i "https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535101557-0-0-fedf56c648bfe0d60ef8e0aa89b6a297&expiration=1535101557&disable_local_cache=0" -c copy "E:/PycharmProjects/Video_Crack/video/video1.mp4"'
cmd = shlex.split(shell_cmd)
p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while p.poll() is None:
line = p.stdout.readline().decode('gbk').strip()
print(line)
if p.returncode == 0:
print('subprocess success')
else:
print('subprocess failed with code', p.returncode)
except (OSError, ValueError):
print('stop here')
程序写好之后,我们仍然不满足,想要把它做成一个图形界面,其中需要有三个按钮,“下载”、“停止”和“退出”,如下图。“下载”和“退出”都比较容易,问题就是这个停止按钮,怎么样才能实现按下“停止”按钮视频停止下载,并且已经下载好的部分还能正常播放呢?也就是说我们要写一个函数,按下“停止”按钮会启动该函数,而该函数的功能就是停止视频下载(并且保证已下载的部分能正常播放)。
二、初步尝试
首先,我们很自然的会想到,可以向子进程发送一个“q”,实现CMD中按下“q”的效果,而且查一下资料,的确有向subprocess子进程发送信息的命令:
Popen.communicate(input='something', timeout=None)
# or
Popen.stdin.write('something')
但是...这其实是行不通的,因为这样发送给子进程的是数据而不是命令,效果就像是在CMD上打印了一个“q”,并不能使视频下载停止。
好吧,我们不要放弃,那有没有向子进程发送信号的命令呢?答案是有的:
Popen.send_signal(signal) # 向子进程发送signal信号
Popen.terminate() # 终止(stop)子进程
Popen.kill() # 杀死子进程。在Windows上,kill()和terminate()作用相同
其中,signal信号的相关知识参见:https://blog.csdn.net/polyhedronx/article/details/81989918
通过上面这篇博客,我们知道在Windows中还有两种方法可以分别向进程和进程组发送信号:
os.kill(pid, sid)
os.killpg(pgid, sid)
其中,pid为进程号,比如“Popen.pid”,sid为要发送的信号。Windows中可以使用的信号是有限制的,详见:https://blog.csdn.net/polyhedronx/article/details/81988335
下面两种方法的效果是一样的:
Popen.send_signal(signal.SIGTERM)
os.kill(Popen.pid, signal.SIGTERM)
有了这么多可以用的命令,我们先来尝试一下 terminate() 和 kill() :
import shlex
import subprocess
if __name__ == '__main__':
try:
shell_cmd = 'ffmpeg -i "https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535110216-0-0-3d035cc32139ce884c8a1cbb14d0d04d&expiration=1535110216&disable_local_cache=0" -c copy "E:/PycharmProjects/Video_Crack/video/video1.mp4"'
cmd = shlex.split(shell_cmd)
p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
count = 0
while p.poll() is None:
line = p.stdout.readline().decode('gbk').strip()
print(line)
count += 1
if count == 50:
p.terminate()
# p.kill()
if p.returncode == 0:
print('subprocess success')
else:
print('subprocess failed with code', p.returncode)
except (OSError, ValueError, KeyboardInterrupt):
print('stop here')
上面程序的意思是打印出50条语句时终止子进程,因为下载完成的话CMD中打印的语句肯定大于50条,也就是我们在它还没有下载完成时就把进程终止了:
从图中可以看出,视频确实没有下载完成,因为下载完成之后视频会有15220KB,这样好像是已经满足我们的要求了,因为我们只要在按下“停止”按钮触发的函数里把子进程kill掉就好了。
但是!!!这个视频是无法正常播放的。。。
也即是说terminate() 和 kill()虽然可以终止视频下载,但也破坏了下载过程,使得下载好的部分无法播放。
三、出现转机
terminate() 和 kill()尝试失败后,现在我们只有下面两条命令可以用了:
Popen.send_signal(signal)
os.kill(Popen.pid, signal)
希望全部寄托在了Windows可用的signal上,通过这篇文章python-subprocess模块用法了解到我们可用下面几种signal:
# SIGTERM信号
Popen.send_signal(signal.SIGTERM)
os.kill(Popen.pid, signal.SIGTERM)
# CTRL_C信号
Popen.send_signal(signal.CTRL_C_EVENT)
os.kill(Popen.pid, signal.CTRL_C_EVENT)
# CTRL_BREAK信号
Popen.send_signal(signal.CTRL_BREAK_EVENT)
os.kill(Popen.pid, signal.CTRL_BREAK_EVENT)
此外,需要注意的是,对于 os.kill(pid, sig) ,在python文档中有如下描述:
Windows:signal.CTRL_C_EVENT和signal.CTRL_BREAK_EVENT信号是特殊信号,只能发送到共享公共控制台窗口的控制台进程,例如某些子进程(subprocesses)。 sig的任何其他值都将导致进程被TerminateProcess API无条件地终止,并且退出代码将被设置为sig。 Windows版本的 kill() 还会占用进程句柄。
原文:https://docs.python.org/3.6/library/os.html
对于signal.
CTRL_C_EVENT
(signal.CTRL_BREAK_EVENT与此类似),
python文档有如下描述:
The signal corresponding to the Ctrl+C keystroke event. This signal can only be used with os.kill()
.
Availability: Windows.
New in version 3.2.
原文:https://docs.python.org/3.6/library/signal.html#signal.CTRL_C_EVENT
也就是说 signal.
CTRL_C_EVENT
和 signal.CTRL_BREAK_EVENT 只能用于os.kill()。此外,
由于在 Windows 上,SIGTERM和terminate() 的作用相同,所以我们弃用该方法。综上,只剩下了两种方法:
# CTRL_C信号
os.kill(Popen.pid, signal.CTRL_C_EVENT)
# CTRL_BREAK信号
os.kill(Popen.pid, signal.CTRL_BREAK_EVENT)
看到CTRL+C,我们好像明白了什么,这不就是中断程序的命令吗!
先在命令行试一下,下载过程中按下CTRL+C,程序终止,视频也下了一半,最重要的是,视频是可以正常播放的!
这说明程序运行的过程中,我们只要给子进程发送一个Ctrl+C信号,就可以完美达到我们的目的,而Ctrl+C信号也正是我们有的:
# CTRL_C信号
os.kill(Popen.pid, signal.CTRL_C_EVENT)
二话不说,立马在python程序中试一试:
import os
import shlex
import signal
import subprocess
if __name__ == '__main__':
try:
shell_cmd = 'ffmpeg -i "https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535110216-0-0-3d035cc32139ce884c8a1cbb14d0d04d&expiration=1535110216&disable_local_cache=0" -c copy "E:/PycharmProjects/Video_Crack/video/video1.mp4"'
cmd = shlex.split(shell_cmd)
p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
count = 0
while p.poll() is None:
line = p.stdout.readline().decode('gbk').strip()
print(line)
count += 1
if count == 50:
os.kill(p.pid, signal.CTRL_C_EVENT)
if p.returncode == 0:
print('subprocess success')
else:
print('subprocess failed with code', p.returncode)
except (OSError, ValueError, KeyboardInterrupt):
print('stop here')
但是,可能是为了让我深刻认识到理论和实践的差距,果然程序又出了其它幺蛾子:视频并没有停止下载,而是下载完了之后才触发CTRL+C信号。。。
请注意视频的大小是15220KB,也就是完整的下载了整个视频。
当然,我也试了 CTRL_BREAK_EVENT 信号,结果也是下载完视频才触发信号:
四、最终圣战
到现在,所有想到的方法都试了一遍,但是没有一个既能中止视频下载,已经下载好的那部分又能正常播放的。当然,我们的努力还是有收获的,最接近我们最终目的的还是 CTRL_C_EVENT 信号,下面就继续拿他寻找解决办法。
# CTRL_C信号
os.kill(Popen.pid, signal.CTRL_C_EVENT)
好吧,我就不卖关子了,最终我还是找到解决办法,很简单,只要这样就行了:
os.kill(0, signal.CTRL_C_EVENT)
详见:https://stackoverflow.com/questions/7085604/sending-c-to-python-subprocess-objects-on-windows
上程序试一试:
import os
import shlex
import signal
import subprocess
if __name__ == '__main__':
try:
shell_cmd = 'ffmpeg -i "https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535110216-0-0-3d035cc32139ce884c8a1cbb14d0d04d&expiration=1535110216&disable_local_cache=0" -c copy "E:/PycharmProjects/Video_Crack/video/video1.mp4"'
cmd = shlex.split(shell_cmd)
p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
count = 0
while p.poll() is None:
line = p.stdout.readline().decode('gbk').strip()
print(line)
count += 1
if count == 50:
os.kill(0, signal.CTRL_C_EVENT)
if p.returncode == 0:
print('subprocess success')
else:
print('subprocess failed with code', p.returncode)
except (OSError, ValueError, KeyboardInterrupt):
print('stop here')
能看到它能正常播放真是太舒服了,那这个终极方法的原理到底是什么呢?
我们知道,os.kill() 有两个参数,即os.kill(pid, sig),如果这两个参数中其中一个为0会是什么情况呢?
(1)os.kill(pid, 0)
若sig为0,则不发送任何信号,但仍执行错误检查;这可用于检查进程ID或进程组ID的存在。
原文链接:https://unix.stackexchange.com/questions/169898/what-does-kill-0-do
(2)os.kill(0, sig)
例如:os.kill(0, signal.CTRL_C_EVENT) ,表示程序通过向cmd窗口中的所有进程发送CTRL_C_EVENT来获取CTRL-C信号。
看一个例子(来源:https://www.programcreek.com/python/example/13966/signal.CTRL_C_EVENT):
def pipe_server():
''' Part of attach/set_attach for Windows '''
while 1:
pipe = Pipe('vdb_%d' % os.getpid(), server=True)
knock = pipe.read(3)
if knock == 'vsi':
os.kill(0, signal.CTRL_C_EVENT)
#ctypes.windll.kernel32.GenerateConsoleCtrlEvent(0, os.getpid())
pipe.disconnect()
pipe.close()
更新(2018/08/28):
有一个小bug,使用os.kill(0, signal.CTRL_C_EVENT)之后,CTRL_C_EVENT信号发送给了当前子进程及其所有相关的父进程,所以我的图形界面也被关闭了!解决方法如下。原理就是接收到CTRL_C_EVENT信号之后会引发KeyboardInterrupt异常,我们捕捉这个异常,并在异常发生之后(此时GUI被关闭了)再次触发GUI,这个过程是很快的,所以我们不会发现GUI关闭过!
class GUIOperate(object):
# 运行GUI
@staticmethod
def gui_loop():
try:
top.mainloop()
except KeyboardInterrupt:
print('下载中止...')
GUIOperate.gui_loop()
参考:
Python错误:AttributeError: module 'signal' has no attribute 'SIGALRM'
https://docs.python.org/3.6/library/os.html
https://docs.python.org/3.6/library/signal.html#signal.CTRL_C_EVENT