ffmpeg将任何东西转换为mp4

时间:2015-09-22 10:30:58

标签: python ffmpeg subprocess

我试图熟悉python子进程,这是我的小代码:

import subprocess
import os
import re
import subprocess as sp
import logging

the_file = "/home/vagrant/test/out.pkg"
out_file = "/home/vagrant/test/result.mp4"

ffmpeg = sp.Popen(['/usr/bin/ffmpeg', '-i', the_file, out_file], stdout = sp.PIPE, stderr = sp.STDOUT)
process_output =  ffmpeg.communicate()
print "communicate:", process_output

结果:

communicate: ("ffmpeg version N-75410-g58fe57d Copyright (c) 2000-2015 the FFmpeg developers\n  built with gcc 4.4.7 (GCC) 20120313 (Red Hat 4.4.7-16)\n  configuration: --prefix=/home/vagrant/ffmpeg_build --extra-cflags=-I/home/vagrant/ffmpeg_build/include --extra-ldflags=-L/home/vagrant/ffmpeg_build/lib --bindir=/usr/local/bin --enable-gpl --enable-nonfree --enable-libfdk_aac --enable-libmp3lame --enable-libvorbis --enable-libopus --enable-libvpx --enable-libx264 --enable-libfreetype\n  libavutil      55.  2.100 / 55.  2.100\n  libavcodec     57.  3.100 / 57.  3.100\n  libavformat    57.  2.100 / 57.  2.100\n  libavdevice    57.  0.100 / 57.  0.100\n  libavfilter     6.  5.100 /  6.  5.100\n  libswscale      4.  0.100 /  4.  0.100\n  libswresample   2.  0.100 /  2.  0.100\n  libpostproc    54.  0.100 / 54.  0.100\nInput #0, tty, from '/home/vagrant/test/out.txt':\n  Duration: 00:00:00.04, bitrate: 1 kb/s\n    Stream #0:0: Video: ansi, pal8, 640x400, 25 fps, 25 tbr, 25 tbn, 25 tbc\nNo pixel format specified, yuv444p for H.264 encoding chosen.\nUse -pix_fmt yuv420p for compatibility with outdated media players.\n[libx264 @ 0x379f300] using cpu capabilities: MMX2 SSE2Fast SSSE3 Cache64\n[libx264 @ 0x379f300] profile High 4:4:4 Predictive, level 3.0, 4:4:4 8-bit\n[libx264 @ 0x379f300] 264 - core 148 r2597 e86f3a1 - H.264/MPEG-4 AVC codec - Copyleft 2003-2015 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=4 threads=1 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00\nOutput #0, mp4, to '/home/vagrant/test/result.mp4':\n  Metadata:\n    encoder         : Lavf57.2.100\n    Stream #0:0: Video: h264 (libx264) ([33][0][0][0] / 0x0021), yuv444p, 640x400, q=-1--1, 25 fps, 12800 tbn, 25 tbc\n    Metadata:\n      encoder         : Lavc57.3.100 libx264\nStream mapping:\n  Stream #0:0 -> #0:0 (ansi (native) -> h264 (libx264))\nPress [q] to stop, [?] for help\nframe=    1 fps=0.0 q=28.0 Lsize=       2kB time=00:00:00.04 bitrate= 362.4kbits/s    \nvideo:1kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 78.698227%\n[libx264 @ 0x379f300] frame I:1     Avg QP:13.08  size:   326\n[libx264 @ 0x379f300] mb I  I16..4:  0.4% 99.1%  0.5%\n[libx264 @ 0x379f300] 8x8 transform intra:99.1%\n[libx264 @ 0x379f300] coded y,u,v intra: 0.4% 0.0% 0.0%\n[libx264 @ 0x379f300] i16 v,h,dc,p:  0% 75% 25%  0%\n[libx264 @ 0x379f300] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu:  0% 92%  8%  0%  0%  0%  0%  0%  0%\n[libx264 @ 0x379f300] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 24% 35% 35%  2%  1%  0%  2%  0%  0%\n[libx264 @ 0x379f300] kb/s:65.20\n", None)

正如预期的那样,给我发回一个元组(stdout,stderr)。但问题是,ffmpeg会转换任何文件(如上所示,甚至是pkg文件)而不会让我返回错误或退出状态1. stderr = None

任何人都能解释一下这个错误吗?非常感谢。

2 个答案:

答案 0 :(得分:1)

检查ffmpeg.returncode,查看退出状态。

设置stderr=sp.PIPE以从communicate()获取相应的非None值。

答案 1 :(得分:1)

在这种情况下,您最好使用subprocess.check_output()

from subprocess import check_output, STDOUT, CalledProcessError

args = ['/usr/bin/ffmpeg', '-i', the_file, out_file]
try:
    txt = check_output(args, stderr=STDOUT)
except CalledProcessError as e:
    print "conversion failed", e
else:
    print the_file, 'converted to', out_file

以上代码捕获ffmpegtxt的标准输出和标准错误流。并且它通过捕获异常来处理非零返回值。

如果要运行多个程序,使用Popen是很好的。下面给出的程序并行运行多个ffmpeg实例,以将大量视频(例如从相机或手机)转换为MP4格式。

#!/usr/bin/env python3
# vim:fileencoding=utf-8:ft=python
#
# Author: R.F. Smith <rsmith@xs4all.nl>
# Last modified: 2015-09-22 21:41:17 +0200
#
# To the extent possible under law, Roland Smith has waived all copyright and
# related or neighboring rights to vid2mp4.py. This work is published from the
# Netherlands. See http://creativecommons.org/publicdomain/zero/1.0/

"""Convert all video files given on the command line to H.264/AAC streams in
an MP4 container."""

__version__ = '1.1.1'

from multiprocessing import cpu_count
from time import sleep
import logging
import os
import subprocess
import sys


def main(argv):
    """
    Entry point for vid2mp4.

    Arguments:
        argv: All command line arguments.
    """
    if len(argv) == 1:
        binary = os.path.basename(argv[0])
        print("{} version {}".format(binary, __version__), file=sys.stderr)
        print("Usage: {} [file ...]".format(binary), file=sys.stderr)
        sys.exit(0)
    logging.basicConfig(level="INFO", format='%(levelname)s: %(message)s')
    checkfor(['ffmpeg', '-version'])
    vids = argv[1:]
    procs = []
    maxprocs = cpu_count()
    for ifile in vids:
        while len(procs) == maxprocs:
            manageprocs(procs)
        procs.append(startencoder(ifile))
    while len(procs) > 0:
        manageprocs(procs)


def checkfor(args, rv=0):
    """
    Make sure that a program necessary for using this script is available.

    Arguments:
        args: String or list of strings of commands. A single string may
            not contain spaces.
        rv: Expected return value from evoking the command.
    """
    if isinstance(args, str):
        if ' ' in args:
            raise ValueError('no spaces in single command allowed')
        args = [args]
    try:
        rc = subprocess.call(args, stdout=subprocess.DEVNULL,
                             stderr=subprocess.DEVNULL)
        if rc != rv:
            raise OSError
    except OSError as oops:
        outs = "required program '{}' not found: {}."
        logging.error(outs.format(args[0], oops.strerror))
        sys.exit(1)


def startencoder(fname):
    """
    Use ffmpeg to convert a video file to H.264/AAC streams in an MP4
    container.

    Arguments:
        fname: Name of the file to convert.

    Returns:
        A 3-tuple of a Process, input path and output path.
    """
    basename, ext = os.path.splitext(fname)
    known = ['.mp4', '.avi', '.wmv', '.flv', '.mpg', '.mpeg', '.mov', '.ogv']
    if ext.lower() not in known:
        ls = "File {} has unknown extension, ignoring it.".format(fname)
        logging.warning(ls)
        return (None, fname, None)
    ofn = basename + '.mp4'
    args = ['ffmpeg', '-i', fname, '-c:v', 'libx264', '-crf', '29', '-flags',
            '+aic+mv4', '-c:a', 'libfaac', '-sn', ofn]
    with open(os.devnull, 'w') as bitbucket:
        try:
            p = subprocess.Popen(args, stdout=bitbucket, stderr=bitbucket)
            logging.info("Conversion of {} to {} started.".format(fname, ofn))
        except:
            logging.error("Starting conversion of {} failed.".format(fname))
    return (p, fname, ofn)


def manageprocs(proclist):
    """
    Check a list of subprocesses tuples for processes that have ended and
    remove them from the list.

    Arguments:
        proclist: a list of (process, input filename, output filename)
    tuples.
    """
    nr = '# of conversions running: {}\r'.format(len(proclist))
    logging.info(nr, end='')
    sys.stdout.flush()
    for p in proclist:
        pr, ifn, ofn = p
        if pr is None:
            proclist.remove(p)
        elif pr.poll() is not None:
            logging.info('Conversion of {} to {} finished.'.format(ifn, ofn))
            proclist.remove(p)
    sleep(0.5)


if __name__ == '__main__':
    main(sys.argv)