示例#1
0
def ffmpeg_write_image(filename, image, logfile=False):
    """ Writes an image (HxWx3 or HxWx4 numpy array) to a file, using
        ffmpeg. """

    if image.dtype != 'uint8':
        image = image.astype("uint8")

    cmd = [
        get_setting("FFMPEG_BINARY"), '-y', '-s',
        "%dx%d" % (image.shape[:2][::-1]), "-f", 'rawvideo', '-pix_fmt',
        "rgba" if (image.shape[2] == 4) else "rgb24", '-i', '-', filename
    ]

    if logfile:
        log_file = open(filename + ".log", 'w+')
    else:
        log_file = sp.PIPE

    popen_params = {"stdout": DEVNULL, "stderr": log_file, "stdin": sp.PIPE}

    if os.name == "nt":
        popen_params["creationflags"] = 0x08000000

    proc = sp.Popen(cmd, **popen_params)
    out, err = proc.communicate(image.tostring())

    if proc.returncode:
        err = "\n".join([
            "[CartoonPy] Running : %s\n" % cmd,
            "WARNING: this command returned an error:",
            err.decode('utf8')
        ])
        raise IOError(err)

    del proc
示例#2
0
    def initialize(self, starttime=0):
        """Opens the file, creates the pipe. """

        self.close()  # if any

        if starttime != 0:
            offset = min(1, starttime)
            i_arg = [
                '-ss',
                "%.06f" % (starttime - offset), '-i', self.filename, '-ss',
                "%.06f" % offset
            ]
        else:
            i_arg = ['-i', self.filename]

        cmd = ([get_setting("FFMPEG_BINARY")] + i_arg + [
            '-loglevel', 'error', '-f', 'image2pipe', "-pix_fmt", self.pix_fmt,
            '-vcodec', 'rawvideo', '-'
        ])

        popen_params = {
            "bufsize": self.bufsize,
            "stdout": sp.PIPE,
            "stderr": sp.PIPE,
            "stdin": DEVNULL
        }

        if os.name == "nt":
            popen_params["creationflags"] = 0x08000000

        self.proc = sp.Popen(cmd, **popen_params)
示例#3
0
    def __init__(self, filename, fps_input, nbytes=2,
                 nchannels = 2, codec='libfdk_aac', bitrate=None,
                 input_video=None, logfile=None, ffmpeg_params=None):

        self.filename = filename
        self.codec= codec

        if logfile is None:
          logfile = sp.PIPE

        cmd = ([ get_setting("FFMPEG_BINARY"), '-y',
            "-loglevel", "error" if logfile==sp.PIPE else "info",
            "-f", 's%dle'%(8*nbytes),
            "-acodec",'pcm_s%dle'%(8*nbytes),
            '-ar', "%d"%fps_input,
            '-ac',"%d"%nchannels,
            '-i', '-']
            + (['-vn'] if input_video is None else
                 [ "-i", input_video, '-vcodec', 'copy'])
            + ['-acodec', codec]
            + ['-ar', "%d"%fps_input]
            + ['-strict', '-2']  # needed to support codec 'aac'
            + (['-ab',bitrate] if (bitrate is not None) else [])
            + (ffmpeg_params if ffmpeg_params else [])
            + [ filename ])

        popen_params = {"stdout": DEVNULL,
                        "stderr": logfile,
                        "stdin": sp.PIPE}

        if os.name == "nt":
            popen_params["creationflags"] = 0x08000000

        self.proc = sp.Popen(cmd, **popen_params)
示例#4
0
    def initialize(self, starttime = 0):
        """ Opens the file, creates the pipe. """

        self.close_proc() # if any

        if starttime !=0 :
            offset = min(1,starttime)
            i_arg = ["-ss", "%.05f"%(starttime-offset),
                    '-i', self.filename, '-vn',
                    "-ss", "%.05f"%offset]
        else:
            i_arg = [ '-i', self.filename,  '-vn']


        cmd = ([get_setting("FFMPEG_BINARY")] + i_arg +
               [ '-loglevel', 'error',
                 '-f', self.f,
                '-acodec', self.acodec,
                '-ar', "%d"%self.fps,
                '-ac', '%d'%self.nchannels, '-'])

        popen_params = {"bufsize": self.buffersize,
                        "stdout": sp.PIPE,
                        "stderr": sp.PIPE,
                        "stdin": DEVNULL}

        if os.name == "nt":
            popen_params["creationflags"] = 0x08000000

        self.proc = sp.Popen( cmd, **popen_params)

        self.pos = np.round(self.fps*starttime)
示例#5
0
def ffmpeg_extract_audio(inputfile, output, bitrate=3000, fps=44100):
    """ extract the sound from a video file and save it in ``output`` """
    cmd = [
        get_setting("FFMPEG_BINARY"), "-y", "-i", inputfile, "-ab",
        "%dk" % bitrate, "-ar",
        "%d" % fps, output
    ]
    subprocess_call(cmd)
示例#6
0
def ffmpeg_resize(video, output, size):
    """ resizes ``video`` to new size ``size`` and write the result
        in file ``output``. """
    cmd = [
        get_setting("FFMPEG_BINARY"), "-i", video, "-vf",
        "scale=%d:%d" % (res[0], res[1]), output
    ]

    subprocess_call(cmd)
示例#7
0
def ffmpeg_merge_video_audio(video,
                             audio,
                             output,
                             vcodec='copy',
                             acodec='copy',
                             ffmpeg_output=False,
                             verbose=True):
    """ merges video file ``video`` and audio file ``audio`` into one
        movie file ``output``. """
    cmd = [
        get_setting("FFMPEG_BINARY"), "-y", "-i", audio, "-i", video,
        "-vcodec", vcodec, "-acodec", acodec, output
    ]

    subprocess_call(cmd, verbose=verbose)
示例#8
0
def ffmpeg_extract_subclip(filename, t1, t2, targetname=None):
    """ makes a new video file playing video file ``filename`` between
        the times ``t1`` and ``t2``. """
    name, ext = os.path.splitext(filename)
    if not targetname:
        T1, T2 = [int(1000 * t) for t in [t1, t2]]
        targetname = name + "%sSUB%d_%d.%s" (name, T1, T2, ext)

    cmd = [
        get_setting("FFMPEG_BINARY"), "-y", "-i", filename, "-ss",
        "%0.2f" % t1, "-t",
        "%0.2f" % (t2 - t1), "-vcodec", "copy", "-acodec", "copy", targetname
    ]

    subprocess_call(cmd)
示例#9
0
def ffmpeg_movie_from_frames(filename, folder, fps, digits=6):
    """
    Writes a movie out of the frames (picture files) in a folder.
    Almost deprecated.
    """
    s = "%" + "%02d" % digits + "d.png"
    cmd = [
        get_setting("FFMPEG_BINARY"), "-y", "-f", "image2", "-r",
        "%d" % fps, "-i",
        os.path.join(folder, folder) + '/' + s, "-b",
        "%dk" % bitrate, "-r",
        "%d" % self.fps, filename
    ]

    subprocess_call(cmd)
示例#10
0
def ffmpeg_parse_infos(filename, print_infos=False, check_duration=True):
    """Get file infos using ffmpeg.

    Returns a dictionnary with the fields:
    "video_found", "video_fps", "duration", "video_nframes",
    "video_duration", "audio_found", "audio_fps"

    "video_duration" is slightly smaller than "duration" to avoid
    fetching the uncomplete frames at the end, which raises an error.

    """

    # open the file in a pipe, provoke an error, read output
    is_GIF = filename.endswith('.gif')
    cmd = [get_setting("FFMPEG_BINARY"), "-i", filename]
    if is_GIF:
        cmd += ["-f", "null", "/dev/null"]

    popen_params = {
        "bufsize": 10**5,
        "stdout": sp.PIPE,
        "stderr": sp.PIPE,
        "stdin": DEVNULL
    }

    if os.name == "nt":
        popen_params["creationflags"] = 0x08000000

    proc = sp.Popen(cmd, **popen_params)

    proc.stdout.readline()
    proc.terminate()
    infos = proc.stderr.read().decode('utf8')
    del proc

    if print_infos:
        # print the whole info text returned by FFMPEG
        print(infos)

    lines = infos.splitlines()
    if "No such file or directory" in lines[-1]:
        raise IOError(("CartoonPy error: the file %s could not be found !\n"
                       "Please check that you entered the correct "
                       "path.") % filename)

    result = dict()

    # get duration (in seconds)
    result['duration'] = None

    if check_duration:
        try:
            keyword = ('frame=' if is_GIF else 'Duration: ')
            line = [l for l in lines if keyword in l][0]
            match = re.findall("([0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9])",
                               line)[0]
            result['duration'] = cvsecs(match)
        except:
            raise IOError(
                ("CartoonPy error: failed to read the duration of file %s.\n"
                 "Here are the file infos returned by ffmpeg:\n\n%s") %
                (filename, infos))

    # get the output line that speaks about video
    lines_video = [
        l for l in lines if ' Video: ' in l and re.search('\d+x\d+', l)
    ]

    result['video_found'] = (lines_video != [])

    if result['video_found']:

        try:
            line = lines_video[0]

            # get the size, of the form 460x320 (w x h)
            match = re.search(" [0-9]*x[0-9]*(,| )", line)
            s = list(map(int, line[match.start():match.end() - 1].split('x')))
            result['video_size'] = s
        except:
            raise IOError((
                "CartoonPy error: failed to read video dimensions in file %s.\n"
                "Here are the file infos returned by ffmpeg:\n\n%s") %
                          (filename, infos))

        # get the frame rate. Sometimes it's 'tbr', sometimes 'fps', sometimes
        # tbc, and sometimes tbc/2...
        # Current policy: Trust tbr first, then fps. If result is near from x*1000/1001
        # where x is 23,24,25,50, replace by x*1000/1001 (very common case for the fps).

        try:
            match = re.search("( [0-9]*.| )[0-9]* tbr", line)
            tbr = float(line[match.start():match.end()].split(' ')[1])
            result['video_fps'] = tbr

        except:
            match = re.search("( [0-9]*.| )[0-9]* fps", line)
            result['video_fps'] = float(
                line[match.start():match.end()].split(' ')[1])

        # It is known that a fps of 24 is often written as 24000/1001
        # but then ffmpeg nicely rounds it to 23.98, which we hate.
        coef = 1000.0 / 1001.0
        fps = result['video_fps']
        for x in [23, 24, 25, 30, 50]:
            if (fps != x) and abs(fps - x * coef) < .01:
                result['video_fps'] = x * coef

        if check_duration:
            result['video_nframes'] = int(
                result['duration'] * result['video_fps']) + 1
            result['video_duration'] = result['duration']
        else:
            result['video_nframes'] = 1
            result['video_duration'] = None
        # We could have also recomputed the duration from the number
        # of frames, as follows:
        # >>> result['video_duration'] = result['video_nframes'] / result['video_fps']

    lines_audio = [l for l in lines if ' Audio: ' in l]

    result['audio_found'] = lines_audio != []

    if result['audio_found']:
        line = lines_audio[0]
        try:
            match = re.search(" [0-9]* Hz", line)
            result['audio_fps'] = int(line[match.start() + 1:match.end()])
        except:
            result['audio_fps'] = 'unknown'

    return result
示例#11
0
    def __init__(self,
                 filename,
                 size,
                 fps,
                 codec="libx264",
                 audiofile=None,
                 preset="medium",
                 bitrate=None,
                 withmask=False,
                 logfile=None,
                 threads=None,
                 ffmpeg_params=None):

        if logfile is None:
            logfile = sp.PIPE

        self.filename = filename
        self.codec = codec
        self.ext = self.filename.split(".")[-1]

        # order is important
        cmd = [
            get_setting("FFMPEG_BINARY"),
            '-y',
            '-loglevel',
            'error' if logfile == sp.PIPE else 'info',
            '-f',
            'rawvideo',
            '-vcodec',
            'rawvideo',
            '-s',
            '%dx%d' % (size[0], size[1]),
            '-pix_fmt',
            'rgba' if withmask else 'rgb24',
            '-r',
            '%.02f' % fps,
            '-i',
            '-',
            '-an',
        ]
        if audiofile is not None:
            cmd.extend(['-i', audiofile, '-acodec', 'copy'])
        cmd.extend([
            '-vcodec',
            codec,
            '-preset',
            preset,
        ])
        if ffmpeg_params is not None:
            cmd.extend(ffmpeg_params)
        if bitrate is not None:
            cmd.extend(['-b', bitrate])

        if threads is not None:
            cmd.extend(["-threads", str(threads)])

        if ((codec == 'libx264') and (size[0] % 2 == 0)
                and (size[1] % 2 == 0)):
            cmd.extend(['-pix_fmt', 'yuv420p'])
        cmd.extend([filename])

        popen_params = {"stdout": DEVNULL, "stderr": logfile, "stdin": sp.PIPE}

        # This was added so that no extra unwanted window opens on windows
        # when the child process is created
        if os.name == "nt":
            popen_params["creationflags"] = 0x08000000

        self.proc = sp.Popen(cmd, **popen_params)
示例#12
0
def write_gif_with_tempfiles(clip,
                             filename,
                             fps=None,
                             program='ImageMagick',
                             opt="OptimizeTransparency",
                             fuzz=1,
                             verbose=True,
                             loop=0,
                             dispose=True,
                             colors=None,
                             tempfiles=False):
    """ Write the VideoClip to a GIF file.


    Converts a VideoClip into an animated GIF using ImageMagick
    or ffmpeg. Does the same as write_gif (see this one for more
    docstring), but writes every frame to a file instead of passing
    them in the RAM. Useful on computers with little RAM.

    """

    fileName, fileExtension = os.path.splitext(filename)
    tt = np.arange(0, clip.duration, 1.0 / fps)

    tempfiles = []

    verbose_print(
        verbose,
        "\n[CartoonPy] Building file %s\n" % filename + 40 * "-" + "\n")

    verbose_print(verbose, "[CartoonPy] Generating GIF frames...\n")

    total = int(clip.duration * fps) + 1
    for i, t in tqdm(enumerate(tt), total=total):

        name = "%s_GIFTEMP%04d.png" % (fileName, i + 1)
        tempfiles.append(name)
        clip.save_frame(name, t, withmask=True)

    delay = int(100.0 / fps)

    if program == "ImageMagick":
        verbose_print(verbose,
                      "[CartoonPy] Optimizing GIF with ImageMagick... ")
        cmd = [
            get_setting("IMAGEMAGICK_BINARY"),
            '-delay',
            '%d' % delay,
            "-dispose",
            "%d" % (2 if dispose else 1),
            "-loop",
            "%d" % loop,
            "%s_GIFTEMP*.png" % fileName,
            "-coalesce",
            "-layers",
            "%s" % opt,
            "-fuzz",
            "%02d" % fuzz + "%",
        ] + (["-colors", "%d" %
              colors] if colors is not None else []) + [filename]

    elif program == "ffmpeg":

        cmd = [
            get_setting("FFMPEG_BINARY"), '-y', '-f', 'image2', '-r',
            str(fps), '-i', fileName + '_GIFTEMP%04d.png', '-r',
            str(fps), filename
        ]

    try:
        subprocess_call(cmd, verbose=verbose)
        verbose_print(verbose, "[CartoonPy] GIF %s is ready." % filename)

    except (IOError, OSError) as err:

        error = ("CartoonPy Error: creation of %s failed because "
                 "of the following error:\n\n%s.\n\n." % (filename, str(err)))

        if program == "ImageMagick":
            error = error + (
                "This error can be due to the fact that "
                "ImageMagick is not installed on your computer, or "
                "(for Windows users) that you didn't specify the "
                "path to the ImageMagick binary in file conf.py.")

        raise IOError(error)

    for f in tempfiles:
        os.remove(f)
示例#13
0
def write_gif(clip,
              filename,
              fps=None,
              program='ImageMagick',
              opt="OptimizeTransparency",
              fuzz=1,
              verbose=True,
              withmask=True,
              loop=0,
              dispose=True,
              colors=None):
    """ Write the VideoClip to a GIF file, without temporary files.

    Converts a VideoClip into an animated GIF using ImageMagick
    or ffmpeg.


    Parameters
    -----------

    filename
      Name of the resulting gif file.

    fps
      Number of frames per second (see note below). If it
        isn't provided, then the function will look for the clip's
        ``fps`` attribute (VideoFileClip, for instance, have one).

    program
      Software to use for the conversion, either 'ImageMagick' or
      'ffmpeg'.

    opt
      (ImageMagick only) optimalization to apply, either
      'optimizeplus' or 'OptimizeTransparency'.

    fuzz
      (ImageMagick only) Compresses the GIF by considering that
      the colors that are less than fuzz% different are in fact
      the same.


    Notes
    -----

    The gif will be playing the clip in real time (you can
    only change the frame rate). If you want the gif to be played
    slower than the clip you will use ::

        >>> # slow down clip 50% and make it a gif
        >>> myClip.speedx(0.5).write_gif('myClip.gif')

    """

    #
    # We use processes chained with pipes.
    #
    # if program == 'ffmpeg'
    # frames --ffmpeg--> gif
    #
    # if program == 'ImageMagick' and optimize == (None, False)
    # frames --ffmpeg--> bmp frames --ImageMagick--> gif
    #
    #
    # if program == 'ImageMagick' and optimize != (None, False)
    # frames -ffmpeg-> bmp frames -ImagMag-> gif -ImagMag-> better gif
    #

    delay = 100.0 / fps

    if clip.mask is None:
        withmask = False

    cmd1 = [
        get_setting("FFMPEG_BINARY"), '-y', '-loglevel', 'error', '-f',
        'rawvideo', '-vcodec', 'rawvideo', '-r',
        "%.02f" % fps, '-s',
        "%dx%d" % (clip.w, clip.h), '-pix_fmt',
        ('rgba' if withmask else 'rgb24'), '-i', '-'
    ]

    popen_params = {"stdout": DEVNULL, "stderr": DEVNULL, "stdin": DEVNULL}

    if os.name == "nt":
        popen_params["creationflags"] = 0x08000000

    if program == "ffmpeg":
        popen_params["stdin"] = sp.PIPE
        popen_params["stdout"] = DEVNULL

        proc1 = sp.Popen(
            cmd1 + [
                '-pix_fmt', ('rgba' if withmask else 'rgb24'), '-r',
                "%.02f" % fps, filename
            ], **popen_params)
    else:

        popen_params["stdin"] = sp.PIPE
        popen_params["stdout"] = sp.PIPE

        proc1 = sp.Popen(cmd1 + ['-f', 'image2pipe', '-vcodec', 'bmp', '-'],
                         **popen_params)

    if program == 'ImageMagick':

        cmd2 = [
            get_setting("IMAGEMAGICK_BINARY"), '-delay',
            "%.02f" % (delay), "-dispose",
            "%d" % (2 if dispose else 1), '-loop',
            '%d' % loop, '-', '-coalesce'
        ]

        if (opt in [False, None]):
            popen_params["stdin"] = proc1.stdout
            popen_params["stdout"] = DEVNULL
            proc2 = sp.Popen(cmd2 + [filename], **popen_params)

        else:
            popen_params["stdin"] = proc1.stdout
            popen_params["stdout"] = sp.PIPE
            proc2 = sp.Popen(cmd2 + ['gif:-'], **popen_params)

        if opt:

            cmd3 = [
                get_setting("IMAGEMAGICK_BINARY"), '-', '-layers', opt,
                '-fuzz',
                '%d' % fuzz + '%'
            ] + (["-colors", "%d" %
                  colors] if colors is not None else []) + [filename]

            popen_params["stdin"] = proc2.stdout
            popen_params["stdout"] = DEVNULL
            proc3 = sp.Popen(cmd3, **popen_params)

    # We send all the frames to the first process
    verbose_print(verbose, "\n[CartoonPy] >>>> Building file %s\n" % filename)
    verbose_print(verbose, "[CartoonPy] Generating GIF frames...\n")

    try:

        for t, frame in clip.iter_frames(fps=fps,
                                         progress_bar=True,
                                         with_times=True,
                                         dtype="uint8"):
            if withmask:
                mask = 255 * clip.mask.get_frame(t)
                frame = np.dstack([frame, mask]).astype('uint8')
            proc1.stdin.write(frame.tostring())

    except IOError as err:

        error = ("[CartoonPy] Error: creation of %s failed because "
                 "of the following error:\n\n%s.\n\n." % (filename, str(err)))

        if program == "ImageMagick":
            error = error + (
                "This can be due to the fact that "
                "ImageMagick is not installed on your computer, or "
                "(for Windows users) that you didn't specify the "
                "path to the ImageMagick binary in file conf.py.")

        raise IOError(error)
    if program == 'ImageMagick':
        verbose_print(verbose,
                      "[CartoonPy] Optimizing the GIF with ImageMagick...\n")
    proc1.stdin.close()
    proc1.wait()
    if program == 'ImageMagick':
        proc2.wait()
        if opt:
            proc3.wait()
    verbose_print(verbose, "[CartoonPy] >>>> File %s is ready !" % filename)