V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
ZoomQuiet
V2EX  ›  Python

[FFmpeg] Python 2.7 怎样合理的包裹外部指令安定调用第三方工具并安全结束?

  •  
  •   ZoomQuiet ·
    ZoomQuiet · 2018-12-01 17:45:44 +08:00 · 3462 次点击
    这是一个创建于 2218 天前的主题,其中的信息可能已经有所发展或是发生改变。

    背景

    • 工程中原先是用 py 基于 opencv 从摄像设备连续获得视频帧处理后再输出到硬盘为视频 /图片
    • 但是, 发现对系统负荷过大
    • 所以, 尝试用 FFmpeg 代为处理
    • 但是, 通过摸索, 找到合理的指令, 并尝试用 py 进行调用时失败

    env.

    • macOS 10.12.6 i7 3.1Ghz 4 核, 16Gb DDR3 内存
    • Python 2.7.10 (default, Oct 13 2016, 22:16:45)
      • [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.38)] on darwin
    • ffmpeg version N-92539-g1035206102 Copyright (c) 2000-2018 the FFmpeg developers
      • built with Apple LLVM version 10.0.0 (clang-1000.11.45.5)
    • GNU bash,版本 4.4.12(1)-release (x86_64-apple-darwin16.3.0)

    现象

    类似指令:

    ffmpeg -f avfoundation -s 1920x1080 -framerate 25 -i 0: -vsync 0 -f image2 /path/2/.../wframe/%06d.jpg

    直接运行:

    ffmpeg version N-92539-g1035206102 Copyright (c) 2000-2018 the FFmpeg developers
      built with Apple LLVM version 10.0.0 (clang-1000.11.45.5)
    
    ...
    
    [avfoundation @ 0x7fa94c800000] Selected pixel format (yuv420p) is not supported by the input device.
    [avfoundation @ 0x7fa94c800000] Supported pixel formats:
    [avfoundation @ 0x7fa94c800000]   uyvy422
    [avfoundation @ 0x7fa94c800000]   yuyv422
    [avfoundation @ 0x7fa94c800000]   nv12
    [avfoundation @ 0x7fa94c800000]   0rgb
    [avfoundation @ 0x7fa94c800000]   bgr0
    [avfoundation @ 0x7fa94c800000] Overriding selected pixel format to use uyvy422 instead.
    [avfoundation @ 0x7fa94c800000] Stream #0: not enough frames to estimate rate; consider increasing probesize
    Input #0, avfoundation, from '0:':
      Duration: N/A, start: 11907.278033, bitrate: N/A
        Stream #0:0: Video: rawvideo (UYVY / 0x59565955), uyvy422, 1920x1080, 1000k tbr, 1000k tbn, 1000k tbc
    Stream mapping:
      Stream #0:0 -> #0:0 (rawvideo (native) -> mjpeg (native))
    Press [q] to stop, [?] for help
    [swscaler @ 0x7fa94b871600] deprecated pixel format used, make sure you did set range correctly
    Output #0, image2, to '/path/2/.../wframe/%06d.jpg':
      Metadata:
        encoder         : Lavf58.23.100
        Stream #0:0: Video: mjpeg, yuvj422p(pc), 1920x1080, q=2-31, 200 kb/s, 1000k fps, 1000k tbn, 1000k tbc
        Metadata:
          encoder         : Lavc58.40.100 mjpeg
        Side data:
          cpb: bitrate max/min/avg: 0/0/200000 buffer size: 0 vbv_delay: -1
    frame=   55 fps= 16 q=24.8 Lsize=N/A time=00:00:03.56 bitrate=N/A speed=1.01x
    ...
    

    没有问题...

    核心代码:

    import subprocess, time
    
    cmd = ['ffmpeg'
            , '-f', 'avfoundation'
            , '-s', '1920x1080' 
            , '-framerate', '25'    # frames per second
            , '-i', '{}:'.format(drivers['w'])
            , '-vsync', '0'
            , '-f', 'image2'
            , '{}/wframe/%06d.jpg'.format(_expath)
            ]
    
    
    p = subprocess.Popen(cmd
        , shell=False
        , stdin=subprocess.PIPE
        #, stdout=subprocess.PIPE
        # merge err>out
        #, stderr=subprocess.STDOUT
    )
    
    time.sleep(4)
    p.terminate()
    

    subprocess.Popen 包裹后, 在合理时机用 terminate() 来安全结束, 可是在 mac 中运行时报错:

    [avfoundation @ 0x7f9f01000000] Failed to create AV capture input device: Cannot Use UNIQUESKY_CAR_CAMERA
    0:: Input/output error
    

    修订 shell=True ,或是打开其它 PIPE 都不能解决;

    相同硬件, 到 win10 环境中, 用相同版本 FFmpeg 来调用, 调整好的指令类似:

    ffmpeg -f dshow -s:v 1920x1080 -framerate 25 video="Integrated Webcam" -f image2 /path/2/.../wframe/%06d.jpg

    一样直接在 cmd 中运行正常, 但是, 用 python 包装后, 一样出现不可用报错:

    : Input/output error

    [avfoundation @ 0x7fa5f7802c00] Failed to create AV capture input device: Cannot Use UNIQUESKY_CAR_CAMERA #2
    0:: Input/output error
    

    : 设备争用

    [dshow @ 0000014269bab740] Could not run graph (sometimes caused by a device already in use by other application)
    video=@device_pnp_\\?\usb#vid_05a3&pid_9230&mi_00#6&1d3a4c24&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global: I/O error
    

    此时检查:

    λ taskkill /IM ffmpeg.exe
    错误: 没有找到进程 "ffmpeg.exe"。
    

    并没有其它进程在使用硬件

    PS:

    用 FFmpeg 在 win10 环境中检验设备时是这样的

    λ ffmpeg -list_devices true -f dshow -i dummy
    ffmpeg version N-92539-g1035206102 Copyright (c) 2000-2018 the FFmpeg developers
      built with gcc 8.2.1 (GCC) 20181017
    
    ...
    
    [dshow @ 0000021f90a2a840] DirectShow video devices (some may be both video and audio devices)
    [dshow @ 0000021f90a2a840]  "Integrated Webcam"
    [dshow @ 0000021f90a2a840]     Alternative name "@device_pnp_\\?\usb#vid_1bcf&pid_0b09&mi_00#7&1bf8ceab&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
    [dshow @ 0000021f90a2a840] DirectShow audio devices
    [dshow @ 0000021f90a2a840]  "楹﹀厠椋?(4- USB PnP Sound Device)"
    [dshow @ 0000021f90a2a840]     Alternative name "@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{25AB1848-CB77-42E5-9985-767755AA0C9C}"
    dummy: Immediate exit requested
    

    即, 所有可用设备有两种指代:

    • USB ID, 类似 "Integrated Webcam"
    • 内部别名: 类似 "@device_pnp_\?\usb#vid_1bcf&pid_0b09&mi_00#7&1bf8ceab&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
    • 之前为防止多设备 /重名设备, 用别名来引用的
    • 效果和用 ID 名称没区别.

    suggest

    有老司机称:

    其实是这样的,windows 那个 cmd,如果你给它双引号,好像也是会被 shell 吃掉的,然后程序的 argv 没有引号,跟 bash 有点类似;

    • 比如你给 python a=1 b="2" "c=3"
    • 然后 python 里的 sys.argv == {blah, 'a=1', 'b=2', 'c=3'} 引号就被 cmd 吃掉了
    • 单引号不会被 cmd 吃掉,双引号都没了

    另外,

    • subprocess 运行的时候比较推荐的
    • 可能还是第一个参数是个 list,
    • 然后 shell=False,
    • 这样可以避免若干头疼的引号问题和命令行注入的问题

    但是, 按照以上建议无论怎么折腾都是无法正常通过 Python 完成 FFMpeg 的调用.

    大家有什么其它思路?

    参考:

    14 条回复    2018-12-06 16:16:45 +08:00
    menc
        1
    menc  
       2018-12-01 18:25:55 +08:00
    如果只是要定期杀掉,那不如用 os.system 运行 nohup 命令,然后把 pid 输出,sleep 后 kill 掉该 pid 就好
    hsfzxjy
        2
    hsfzxjy  
       2018-12-01 18:39:35 +08:00 via Android
    不明白为什么要 sleep(4)? p.communicate() 阻塞等待不好么
    Philippa
        3
    Philippa  
       2018-12-01 21:14:15 +08:00 via iPhone
    Subprocess 不能保证 terminated 能够杀死进程,你要弄一个父进程然后杀整个进程树,把这个进程挂在 setsid。不要自然堵塞,要实际监控。ffmpeg 应该重新编译确保插件完备不要用 apt。摄像头要用硬编码输出不要用软件编码,否则 Cpu 占比很高。最后用 output 输出查看情况不要用 Popen,虽说其实也是封装了它。最最后加个输出视频质量检查,用 opencv。或弄个超简单的机器学习模型也可以。我弄了一个去采集数据跑了很多了没什么问题,具体原理我也不感兴趣。如果搞不定也只是拍照,去找 v4l2capture。另外建议不要搞多平台,应该用 docker。
    e9e499d78f
        4
    e9e499d78f  
       2018-12-01 21:25:25 +08:00
    用 plumbum
    ZoomQuiet
        5
    ZoomQuiet  
    OP
       2018-12-01 23:43:11 +08:00
    @hsfzxjy 是也乎,( ̄▽ ̄)
    只是个样例, 实际用, 肯定是通过其它渠道影响用户 /事件, 进行异步中断的
    ZoomQuiet
        6
    ZoomQuiet  
    OP
       2018-12-01 23:48:51 +08:00
    @Philippa 是也乎,( ̄▽ ̄)
    多谢建议 v4l2capture , 只是这是 linux only 的?
    俺也不想折腾多平台, 只是当前被迫要先部署 win10 环境发布, 客户要求...
    所以.....

    不过, 也打开了另外的思路, FFmpeg 并不是唯一的高性能图像 /视频抓取平台...
    ZoomQuiet
        7
    ZoomQuiet  
    OP
       2018-12-01 23:49:42 +08:00
    @menc 是也乎,( ̄▽ ̄)
    多谢分享经验, 现在的主要问题, 不是进程无法关闭,
    而是相同的指令, 在 py 子进程中不可工作, 进程的关闭是正常的...
    ZoomQuiet
        8
    ZoomQuiet  
    OP
       2018-12-01 23:57:54 +08:00
    @e9e499d78f 是也乎,( ̄▽ ̄)
    plumbum got it ;-)
    以及 Pexpect 都是值得嗯哼的
    julyclyde
        9
    julyclyde  
       2018-12-02 10:37:36 +08:00
    zq 你现在做啥工作啊?咋有这么倒霉的需求要解决?
    ZoomQuiet
        10
    ZoomQuiet  
    OP
       2018-12-02 16:05:51 +08:00
    @julyclyde 是也乎,( ̄▽ ̄)
    被创业, 被 win10....你懂的...
    julyclyde
        11
    julyclyde  
       2018-12-02 17:47:39 +08:00
    @ZoomQuiet 还是在珠海吗?
    lolizeppelin
        12
    lolizeppelin  
       2018-12-06 14:25:08 +08:00
    这个和 python 没关系

    了解下 wait waitpid fork 父子进程 信号 相关的 linux 知识

    回头再来看你的问题就一目了然了
    ZoomQuiet
        13
    ZoomQuiet  
    OP
       2018-12-06 14:51:36 +08:00
    @lolizeppelin 是也乎,( ̄▽ ̄)
    问题是要解决的场景主要是 win10 中的...这就和以往 linux 进程信号机制没关系了...
    lolizeppelin
        14
    lolizeppelin  
       2018-12-06 16:16:45 +08:00
    win 的原理差不多 管道什么的 都差不多逻辑

    因为没 fork 处理起来比较复杂 subprocess 里怎么处理的详细读下就好,win 相关的代码还是蛮恶心的

    但主要是要清楚系统这部分的原理就好了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2728 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 11:52 · PVG 19:52 · LAX 03:52 · JFK 06:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.