Esempio n. 1
0
def test_CompositeAudioClip_by__init__():
    """The difference between the CompositeAudioClip returned by
    ``concatenate_audioclips`` and a CompositeAudioClip created using the class
    directly, is that audios in ``concatenate_audioclips`` are played one after
    other and AudioClips passed to CompositeAudioClip can be played at different
    times, it depends on their ``start`` attributes.
    """
    frequencies = [440, 880, 1760]
    durations = [2, 5, 1]
    fpss = [44100, 22050, 11025]
    starts = [0, 1, 2]

    clips = [
        AudioClip(
            lambda t: [np.sin(frequency * 2 * np.pi * t)], duration=duration, fps=fps
        ).with_start(start)
        for frequency, duration, fps, start in zip(frequencies, durations, fpss, starts)
    ]

    compound_clip = CompositeAudioClip(clips)

    # should return a CompositeAudioClip
    assert isinstance(compound_clip, CompositeAudioClip)

    # fps of the greatest fps passed into it
    assert compound_clip.fps == 44100

    # duration depends on clips starts and durations
    ends = [start + duration for start, duration in zip(starts, durations)]
    assert compound_clip.duration == max(ends)
    assert list(compound_clip.ends) == ends
    assert list(compound_clip.starts) == starts

    # channels are maximum number of channels of the clips
    assert compound_clip.nchannels == max(clip.nchannels for clip in clips)
Esempio n. 2
0
def add_voice(video_path, term):
    videoclip = VideoFileClip(video_path)
    audioclip = AudioFileClip(speech(term))
    new_audioclip = CompositeAudioClip([videoclip.audio, audioclip])
    videoclip.audio = new_audioclip
    videoclip.write_videofile(video_path)
    os.remove('/home/benjamim/PycharmProjects/Nyte/ImagesAndSounds/' + term[0:3] + '.mp3')
def calculate_loudness(audio_subclip, fps):
    CompositeAudioClip([audio_subclip]).write_audiofile(AUDIO_SUBCLIP_NAME,
                                                        fps=fps)
    data, rate = sf.read(AUDIO_SUBCLIP_NAME)  # load audio
    meter = pyln.Meter(rate)  # create BS.1770 meter
    loudness = meter.integrated_loudness(data)  # measure loudness
    return loudness
Esempio n. 4
0
    def __init__(self,
                 clips,
                 size=None,
                 bg_color=None,
                 transparent=False,
                 ismask=False):

        if size is None:
            size = clips[0].size

        if bg_color is None:
            bg_color = 0.0 if ismask else (0, 0, 0)

        VideoClip.__init__(self)

        self.size = size
        self.ismask = ismask
        self.clips = clips
        self.transparent = transparent
        self.bg_color = bg_color
        self.bg = ColorClip(size, col=self.bg_color).get_frame(0)

        # compute duration
        ends = [c.end for c in self.clips]
        if not any([(e is None) for e in ends]):
            self.duration = max(ends)
            self.end = max(ends)

        # compute audio
        audioclips = [v.audio for v in self.clips if v.audio != None]
        if len(audioclips) > 0:
            self.audio = CompositeAudioClip(audioclips)

        # compute mask
        if transparent:
            maskclips = [
                c.mask.set_pos(c.pos) for c in self.clips if c.mask is not None
            ]
            if maskclips != []:
                self.mask = CompositeVideoClip(maskclips,
                                               self.size,
                                               transparent=False,
                                               ismask=True)

        def make_frame(t):
            """ The clips playing at time `t` are blitted over one
                another. """

            f = self.bg
            for c in self.playing_clips(t):
                f = c.blit_on(f, t)
            return f

        self.make_frame = make_frame
Esempio n. 5
0
def audio_compose(audio_or_file1, audio_or_file2, t1=0, t2=None):
    """
    Concatenates or superposes two sounds.
    Ajoute ou superpose deux sons.

    @param      audio_or_file1      son 1
    @param      audio_or_file2      son 2
    @param      t1                  start of the first sound
    @param      t2                  start of the second sound (or None to add it ad
    @return                         new sound

    Example:

    ::

        from code_beatrix.art.video import audio_compose
        son = audio_compose('son1.mp3', 'son2.mp3', 0, 10)
    """
    with AudioContext(audio_or_file1) as audio1:
        with AudioContext(audio_or_file2) as audio2:
            add = []
            if t1 != 0:
                add.append(audio1.set_start(t1))
            else:
                add.append(audio1)
            if t2 is None:
                add.append(audio2.set_start(audio1.duration + t1))
            else:
                add.append(audio2.set_start(t2))
            comp = CompositeAudioClip(add)
            fps1 = audio1.fps if hasattr(audio1, 'fps') else None
            fps2 = audio2.fps if hasattr(audio2, 'fps') else None
            if fps1 is not None and fps2 is not None:
                fps = max(fps1, fps2)
                return comp.set_fps(fps)
            elif fps1 is None and fps2 is None:
                return comp
            else:
                return comp.set_fps(fps1 or fps2)
Esempio n. 6
0
    def add_sfx(self, clip):
        logger.info('Adding sfx...')
        new_audio = [clip.audio]
        for i in range(self.sfx_num):
            sfx_path = config.SFX_PATH + str(i) + '.mp3'
            sfx_clip = AudioFileClip(sfx_path)

            if sfx_clip.duration > clip.duration:
                sfx_clip = sfx_clip.set_duration(clip.duration)
                new_audio.append(sfx_clip)
            else:
                new_audio.append(sfx_clip)

        return clip.set_audio(CompositeAudioClip(new_audio))
Esempio n. 7
0
def images_to_video_with_audio(images: List[np.ndarray],
                               output_dir: str,
                               video_name: str,
                               audios: List[str],
                               sr: int,
                               fps: int = 1,
                               quality: Optional[float] = 5,
                               **kwargs):
    r"""Calls imageio to run FFMPEG on a list of images. For more info on
    parameters, see https://imageio.readthedocs.io/en/stable/format_ffmpeg.html
    Args:
        images: The list of images. Images should be HxWx3 in RGB order.
        output_dir: The folder to put the video in.
        video_name: The name for the video.
        audios: raw audio files
        fps: Frames per second for the video. Not all values work with FFMPEG,
            use at your own risk.
        quality: Default is 5. Uses variable bit rate. Highest quality is 10,
            lowest is 0.  Set to None to prevent variable bitrate flags to
            FFMPEG so you can manually specify them using output_params
            instead. Specifying a fixed bitrate using ‘bitrate’ disables
            this parameter.
    """
    assert 0 <= quality <= 10
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    video_name = video_name.replace(" ", "_").replace("\n", "_") + ".mp4"

    assert len(images) == len(audios) * fps
    audio_clips = []
    temp_file_name = '/tmp/{}.wav'.format(random.randint(0, 10000))
    # use amplitude scaling factor to reduce the volume of sounds
    amplitude_scaling_factor = 100
    for i, audio in enumerate(audios):
        # def f(t):
        #     return audio[0, t], audio[1: t]
        #
        # audio_clip = mpy.AudioClip(f, duration=1, fps=audio.shape[1])
        wavfile.write(temp_file_name, sr, audio.T / amplitude_scaling_factor)
        audio_clip = mpy.AudioFileClip(temp_file_name)
        audio_clip = audio_clip.set_duration(1)
        audio_clip = audio_clip.set_start(i)
        audio_clips.append(audio_clip)
    composite_audio_clip = CompositeAudioClip(audio_clips)
    video_clip = mpy.ImageSequenceClip(images, fps=fps)
    video_with_new_audio = video_clip.set_audio(composite_audio_clip)
    video_with_new_audio.write_videofile(os.path.join(output_dir, video_name))
    os.remove(temp_file_name)
Esempio n. 8
0
def audio_delay(clip, offset=0.2, n_repeats=8, decay=1):
    """Repeats audio certain number of times at constant intervals multiplying
    their volume levels using a linear space in the range 1 to ``decay`` argument
    value.

    Parameters
    ----------

    offset : float, optional
      Gap between repetitions start times, in seconds.

    n_repeats : int, optional
      Number of repetitions (without including the clip itself).

    decay : float, optional
      Multiplication factor for the volume level of the last repetition. Each
      repetition will have a value in the linear function between 1 and this value,
      increasing or decreasing constantly. Keep in mind that the last repetition
      will be muted if this is 0, and if is greater than 1, the volume will increase
      for each repetition.

    Examples
    --------

    >>> from moviepy import *
    >>> videoclip = AudioFileClip('myaudio.wav').fx(
    ...     audio_delay, offset=.2, n_repeats=10, decayment=.2
    ... )

    >>> # stereo A note
    >>> make_frame = lambda t: np.array(
    ...     [np.sin(440 * 2 * np.pi * t), np.sin(880 * 2 * np.pi * t)]
    ... ).T
    ... clip = AudioClip(make_frame=make_frame, duration=0.1, fps=44100)
    ... clip = audio_delay(clip, offset=.2, n_repeats=11, decay=0)
    """
    decayments = np.linspace(1, max(0, decay), n_repeats + 1)
    return CompositeAudioClip([
        clip.copy(),
        *[
            multiply_volume(clip.with_start(
                (rep + 1) * offset), decayments[rep + 1])
            for rep in range(n_repeats)
        ],
    ])
Esempio n. 9
0
def test_find_audio_period(mono_wave, stereo_wave, wave_type):
    if wave_type == "mono":
        wave1 = mono_wave(freq=400)
        wave2 = mono_wave(freq=100)
    else:
        wave1 = stereo_wave(left_freq=400, right_freq=220)
        wave2 = stereo_wave(left_freq=100, right_freq=200)
    clip = CompositeAudioClip([
        AudioClip(make_frame=wave1, duration=0.3, fps=22050),
        multiply_volume(
            AudioClip(make_frame=wave2, duration=0.3, fps=22050),
            0,
            end_time=0.1,
        ),
    ])
    loop_clip = loop(clip, 4)
    assert round(find_audio_period(loop_clip),
                 6) == pytest.approx(0.29932, 0.1)
Esempio n. 10
0
def make_round(stack: ExitStack, output_config: OutputConfig, r_i: int,
               cutter_lock: Lock, max_threads_semaphore: Semaphore):
    max_threads_semaphore.acquire()
    round_config = output_config.rounds[r_i]
    ext, codec = _get_ext_codec(output_config.raw)

    # Skip rounds that have been saved
    if round_config._is_on_disk:
        name = get_round_name(output_config.name, round_config.name, ext)
        max_threads_semaphore.release()
        return name

    # Assemble beatmeter video from beat images
    bmcfg = (round_config.beatmeter_config if round_config.bmcfg else None)
    if round_config.beatmeter is not None:
        print("\r\nAssembling beatmeter #{}...".format(r_i + 1))
        beatmeter_thread = ThreadWithReturnValue(
            target=lambda: make_beatmeter(
                stack,
                round_config.beatmeter,
                bmcfg.fps if bmcfg else output_config.fps,
                round_config.duration,
                (output_config.xdim, output_config.ydim),
            ),
            daemon=True,
        )
        beatmeter_thread.start()

    # Get list of clips cut from sources using chosen cutter
    # TODO: get duration, bpm from music track; generate beatmeter
    print("\r\nLoading sources for round #%i..." % (r_i + 1))
    cutter = get_cutter(stack, output_config, round_config)
    print("\r\nShuffling input videos for round #%i..." % (r_i + 1))
    if output_config.versions > 1:
        # Await previous cutter, if still previewing
        cutter_lock.acquire()
        # TODO: pass in cutter lock to release on preview exit
        clips = cutter.get_compilation()
        cutter_lock.release()
    else:
        clips = cutter.get_compilation()

    # Assemble audio from music and beats
    audio = None
    if (round_config.music is not None or round_config.beats is not None):
        audio = [
            stack.enter_context(AudioFileClip(clip)) for clip in [
                round_config.beats,
                round_config.music,
            ] if clip is not None
        ]

    # Concatenate this round's video clips together and add audio
    round_video = concatenate_videoclips(clips)
    if audio is not None:
        beat_audio = CompositeAudioClip(audio)
        level = round_config.audio_level
        if level > 1:
            beat_audio = volumex(beat_audio, 1 / level)
        else:
            round_video = volumex(round_video, level)
        audio = CompositeAudioClip([beat_audio, round_video.audio])
        round_video = round_video.set_audio(audio)

    # Add beatmeter, if supplied
    if round_config.beatmeter is not None:
        # Wait for beatmeter, if it exists
        print("\r\nWaiting for beatmeter #%i..." % (r_i + 1))
        beatmeter = beatmeter_thread.join()
        round_video = CompositeVideoClip([round_video, beatmeter])
    round_video = round_video.set_duration(round_config.duration)

    # Fade in and out
    round_video = crossfade([
        get_black_clip((output_config.xdim, output_config.ydim)),
        round_video,
        get_black_clip((output_config.xdim, output_config.ydim)),
    ])

    if output_config.cache == "round":
        # Save each round video to disk
        filename = get_round_name(output_config.name, round_config.name, ext)
        filename = _write_video(stack, round_video, filename, codec,
                                output_config.fps, ext)
        round_config._is_on_disk = filename is not None
        max_threads_semaphore.release()
        return filename
    else:  # output_config.cache == "all":
        max_threads_semaphore.release()
        return round_video  # Store round in memory instead
Esempio n. 11
0
def concatenate(clipslist,
                transition=None,
                bg_color=(0, 0, 0),
                transparent=False,
                ismask=False,
                padding=0):
    """ Concatenates several video clips
    
    Returns a video clip made by clip by concatenating several video clips.
    (Concatenated means that they will be played one after another).
    if the clips do not have the same resolution, the final
    resolution will be such that no clip has to be resized. As
    a consequence the final clip has the height of the highest
    clip and the width of the widest clip of the list. All the
    clips with smaller dimensions will appear centered. The border
    will be transparent if mask=True, else it will be of the
    color specified by ``bg_color``.
    
    Returns a VideoClip instance if all clips have the same size and
    there is no transition, else a composite clip.
    
    Parameters
    -----------

    clipslist
      A list of video clips which must all have their ``duration``
      attributes set.

    transition
      A clip that will be played between each two clips of the list.  
    
    bg_color
      Color of the background, if any.

    transparent
      If True, the resulting clip's mask will be the concatenation of
      the masks of the clips in the list. If the clips do not have the
      same resolution, the border around the smaller clips will be
      transparent.

    padding
      Duration during two consecutive clips. If negative, a clip will
      play at the same time as the clip it follows. A non-null padding
      automatically sets the method to `compose`.
           
    """

    if transition != None:
        l = [[v, transition] for v in clipslist[:-1]]
        clipslist = reduce(lambda x, y: x + y, l) + [clipslist[-1]]
        transition = None

    tt = np.cumsum([0] + [c.duration for c in clipslist])

    sizes = [v.size for v in clipslist]
    w = max([r[0] for r in sizes])
    h = max([r[1] for r in sizes])

    tt = np.maximum(0, tt + padding * np.arange(len(tt)))
    result = CompositeVideoClip(
        [c.set_start(t).set_pos('center') for (c, t) in zip(clipslist, tt)],
        size=(w, h),
        bg_color=bg_color,
        ismask=ismask,
        transparent=transparent)

    result.tt = tt
    result.clipslist = clipslist
    result.start_times = tt[:-1]
    result.start, result.duration, result.end = 0, tt[-1], tt[-1]

    audio_t = [(c.audio, t) for c, t in zip(clipslist, tt) if c.audio != None]
    if len(audio_t) > 0:
        result.audio = CompositeAudioClip([a.set_start(t) for a, t in audio_t])
    return result
Esempio n. 12
0
class CompositeVideoClip(VideoClip):
    """
    A VideoClip made of other videoclips displayed together. This is the
    base class for most compositions.

    Parameters
    ----------

    size
      The size (width, height) of the final clip.

    clips
      A list of videoclips.

      Clips with a higher ``layer`` attribute will be dislayed
      on top of other clips in a lower layer.
      If two or more clips share the same ``layer``,
      then the one appearing latest in ``clips`` will be displayed
      on top (i.e. it has the higher layer).

      For each clip:

      - The attribute ``pos`` determines where the clip is placed.
          See ``VideoClip.set_pos``
      - The mask of the clip determines which parts are visible.

      Finally, if all the clips in the list have their ``duration``
      attribute set, then the duration of the composite video clip
      is computed automatically

    bg_color
      Color for the unmasked and unfilled regions. Set to None for these
      regions to be transparent (will be slower).

    use_bgclip
      Set to True if the first clip in the list should be used as the
      'background' on which all other clips are blitted. That first clip must
      have the same size as the final clip. If it has no transparency, the final
      clip will have no mask.

    The clip with the highest FPS will be the FPS of the composite clip.

    """

    def __init__(
        self, clips, size=None, bg_color=None, use_bgclip=False, is_mask=False
    ):

        if size is None:
            size = clips[0].size

        if use_bgclip and (clips[0].mask is None):
            transparent = False
        else:
            transparent = bg_color is None

        if bg_color is None:
            bg_color = 0.0 if is_mask else (0, 0, 0)

        fpss = [clip.fps for clip in clips if getattr(clip, "fps", None)]
        self.fps = max(fpss) if fpss else None

        VideoClip.__init__(self)

        self.size = size
        self.is_mask = is_mask
        self.clips = clips
        self.bg_color = bg_color

        if use_bgclip:
            self.bg = clips[0]
            self.clips = clips[1:]
            self.created_bg = False
        else:
            self.clips = clips
            self.bg = ColorClip(size, color=self.bg_color, is_mask=is_mask)
            self.created_bg = True

        # order self.clips by layer
        self.clips = sorted(self.clips, key=lambda clip: clip.layer)

        # compute duration
        ends = [clip.end for clip in self.clips]
        if None not in ends:
            duration = max(ends)
            self.duration = duration
            self.end = duration

        # compute audio
        audioclips = [v.audio for v in self.clips if v.audio is not None]
        if audioclips:
            self.audio = CompositeAudioClip(audioclips)

        # compute mask if necessary
        if transparent:
            maskclips = [
                (clip.mask if (clip.mask is not None) else clip.add_mask().mask)
                .with_position(clip.pos)
                .with_end(clip.end)
                .with_start(clip.start, change_end=False)
                .with_layer(clip.layer)
                for clip in self.clips
            ]

            self.mask = CompositeVideoClip(
                maskclips, self.size, is_mask=True, bg_color=0.0
            )

    def make_frame(self, t):
        """The clips playing at time `t` are blitted over one another."""
        frame = self.bg.get_frame(t).astype("uint8")
        im = Image.fromarray(frame)

        if self.bg.mask is not None:
            frame_mask = self.bg.mask.get_frame(t)
            im_mask = Image.fromarray(255 * frame_mask).convert("L")
            im = im.putalpha(im_mask)

        for clip in self.playing_clips(t):
            im = clip.blit_on(im, t)

        return np.array(im)

    def playing_clips(self, t=0):
        """Returns a list of the clips in the composite clips that are
        actually playing at the given time `t`.
        """
        return [clip for clip in self.clips if clip.is_playing(t)]

    def close(self):
        """Closes the instance, releasing all the resources."""
        if self.created_bg and self.bg:
            # Only close the background clip if it was locally created.
            # Otherwise, it remains the job of whoever created it.
            self.bg.close()
            self.bg = None
        if hasattr(self, "audio") and self.audio:
            self.audio.close()
            self.audio = None
Esempio n. 13
0
def concatenate_videoclips(
    clips, method="chain", transition=None, bg_color=None, is_mask=False, padding=0
):
    """Concatenates several video clips

    Returns a video clip made by clip by concatenating several video clips.
    (Concatenated means that they will be played one after another).

    There are two methods:

    - method="chain": will produce a clip that simply outputs
      the frames of the succesive clips, without any correction if they are
      not of the same size of anything. If none of the clips have masks the
      resulting clip has no mask, else the mask is a concatenation of masks
      (using completely opaque for clips that don't have masks, obviously).
      If you have clips of different size and you want to write directly the
      result of the concatenation to a file, use the method "compose" instead.

    - method="compose", if the clips do not have the same
      resolution, the final resolution will be such that no clip has
       to be resized.
       As a consequence the final clip has the height of the highest
       clip and the width of the widest clip of the list. All the
       clips with smaller dimensions will appear centered. The border
       will be transparent if mask=True, else it will be of the
       color specified by ``bg_color``.

    The clip with the highest FPS will be the FPS of the result clip.

    Parameters
    -----------
    clips
      A list of video clips which must all have their ``duration``
      attributes set.
    method
      "chain" or "compose": see above.
    transition
      A clip that will be played between each two clips of the list.

    bg_color
      Only for method='compose'. Color of the background.
      Set to None for a transparent clip

    padding
      Only for method='compose'. Duration during two consecutive clips.
      Note that for negative padding, a clip will partly play at the same
      time as the clip it follows (negative padding is cool for clips who fade
      in on one another). A non-null padding automatically sets the method to
      `compose`.

    """

    if transition is not None:
        clip_transition_pairs = [[v, transition] for v in clips[:-1]]
        clips = reduce(lambda x, y: x + y, clip_transition_pairs) + [clips[-1]]
        transition = None

    timings = np.cumsum([0] + [clip.duration for clip in clips])

    sizes = [clip.size for clip in clips]

    w = max(size[0] for size in sizes)
    h = max(size[1] for size in sizes)

    timings = np.maximum(0, timings + padding * np.arange(len(timings)))
    timings[-1] -= padding  # Last element is the duration of the whole

    if method == "chain":

        def make_frame(t):
            i = max([i for i, e in enumerate(timings) if e <= t])
            return clips[i].get_frame(t - timings[i])

        def get_mask(clip):
            mask = clip.mask or ColorClip([1, 1], color=1, is_mask=True)
            if mask.duration is None:
                mask.duration = clip.duration
            return mask

        result = VideoClip(is_mask=is_mask, make_frame=make_frame)
        if any([clip.mask is not None for clip in clips]):
            masks = [get_mask(clip) for clip in clips]
            result.mask = concatenate_videoclips(masks, method="chain", is_mask=True)
            result.clips = clips
    elif method == "compose":
        result = CompositeVideoClip(
            [
                clip.with_start(t).with_position("center")
                for (clip, t) in zip(clips, timings)
            ],
            size=(w, h),
            bg_color=bg_color,
            is_mask=is_mask,
        )
    else:
        raise Exception(
            "Moviepy Error: The 'method' argument of "
            "concatenate_videoclips must be 'chain' or 'compose'"
        )

    result.timings = timings

    result.start_times = timings[:-1]
    result.start, result.duration, result.end = 0, timings[-1], timings[-1]

    audio_t = [
        (clip.audio, t) for clip, t in zip(clips, timings) if clip.audio is not None
    ]
    if audio_t:
        result.audio = CompositeAudioClip([a.with_start(t) for a, t in audio_t])

    fpss = [clip.fps for clip in clips if getattr(clip, "fps", None) is not None]
    result.fps = max(fpss) if fpss else None
    return result
Esempio n. 14
0
class CompositeVideoClip(VideoClip):

    """ 
    
    A VideoClip made of other videoclips displayed together. This is the
    base class for most compositions.
    
    Parameters
    ----------

    size
      The size (height x width) of the final clip.

    clips
      A list of videoclips. Each clip of the list will
      be displayed below the clips appearing after it in the list.
      For each clip:
       
      - The attribute ``pos`` determines where the clip is placed.
          See ``VideoClip.set_pos``
      - The mask of the clip determines which parts are visible.
        
      Finally, if all the clips in the list have their ``duration``
      attribute set, then the duration of the composite video clip
      is computed automatically

    bg_color
      Color for the unmasked and unfilled regions. Set to None for these
      regions to be transparent (will be slower).

    use_bgclip
      Set to True if the first clip in the list should be used as the
      'background' on which all other clips are blitted. That first clip must
      have the same size as the final clip. If it has no transparency, the final
      clip will have no mask. 
    
    The clip with the highest FPS will be the FPS of the composite clip.

    """

    def __init__(self, clips, size=None, bg_color=None, use_bgclip=False, ismask=False):

        if size is None:
            size = clips[0].size

        if use_bgclip and (clips[0].mask is None):
            transparent = False
        else:
            transparent = bg_color is None

        if bg_color is None:
            bg_color = 0.0 if ismask else (0, 0, 0)

        fpss = [c.fps for c in clips if getattr(c, "fps", None)]
        self.fps = max(fpss) if fpss else None

        VideoClip.__init__(self)

        self.size = size
        self.ismask = ismask
        self.clips = clips
        self.bg_color = bg_color

        if use_bgclip:
            self.bg = clips[0]
            self.clips = clips[1:]
            self.created_bg = False
        else:
            self.clips = clips
            self.bg = ColorClip(size, color=self.bg_color, ismask=ismask)
            self.created_bg = True

        # compute duration
        ends = [c.end for c in self.clips]
        if None not in ends:
            duration = max(ends)
            self.duration = duration
            self.end = duration

        # compute audio
        audioclips = [v.audio for v in self.clips if v.audio is not None]
        if audioclips:
            self.audio = CompositeAudioClip(audioclips)

        # compute mask if necessary
        if transparent:
            maskclips = [
                (c.mask if (c.mask is not None) else c.add_mask().mask)
                .set_position(c.pos)
                .set_end(c.end)
                .set_start(c.start, change_end=False)
                for c in self.clips
            ]

            self.mask = CompositeVideoClip(
                maskclips, self.size, ismask=True, bg_color=0.0
            )

        def make_frame(t):
            """ The clips playing at time `t` are blitted over one
                another. """

            f = self.bg.get_frame(t)
            for c in self.playing_clips(t):
                f = c.blit_on(f, t)
            return f

        self.make_frame = make_frame

    def playing_clips(self, t=0):
        """ Returns a list of the clips in the composite clips that are
            actually playing at the given time `t`. """
        return [c for c in self.clips if c.is_playing(t)]

    def close(self):
        if self.created_bg and self.bg:
            # Only close the background clip if it was locally created.
            # Otherwise, it remains the job of whoever created it.
            self.bg.close()
            self.bg = None
        if hasattr(self, "audio") and self.audio:
            self.audio.close()
            self.audio = None
Esempio n. 15
0
class CompositeVideoClip(VideoClip):

    """ 
    
    A VideoClip made of other videoclips displayed together. This is the
    base class for most compositions.
    
    Parameters
    ----------

    size
      The size (height x width) of the final clip.

    clips
      A list of videoclips. Each clip of the list will
      be displayed below the clips appearing after it in the list.
      For each clip:
       
      - The attribute ``pos`` determines where the clip is placed.
          See ``VideoClip.set_pos``
      - The mask of the clip determines which parts are visible.
        
      Finally, if all the clips in the list have their ``duration``
      attribute set, then the duration of the composite video clip
      is computed automatically

    bg_color
      Color for the unmasked and unfilled regions. Set to None for these
      regions to be transparent (will be slower).

    use_bgclip
      Set to True if the first clip in the list should be used as the
      'background' on which all other clips are blitted. That first clip must
      have the same size as the final clip. If it has no transparency, the final
      clip will have no mask. 
    
    The clip with the highest FPS will be the FPS of the composite clip.

    """

    def __init__(self, clips, size=None, bg_color=None, use_bgclip=False,
                 ismask=False):

        if size is None:
            size = clips[0].size

        
        if use_bgclip and (clips[0].mask is None):
            transparent = False
        else:
            transparent = (bg_color is None)
        
        if bg_color is None:
            bg_color = 0.0 if ismask else (0, 0, 0)

        fpss = [c.fps for c in clips if hasattr(c, 'fps') and c.fps is not None]
        if len(fpss) == 0:
            self.fps = None
        else:
            self.fps = max(fpss)

        VideoClip.__init__(self)
        
        self.size = size
        self.ismask = ismask
        self.clips = clips
        self.bg_color = bg_color

        if use_bgclip:
            self.bg = clips[0]
            self.clips = clips[1:]
            self.created_bg = False
        else:
            self.clips = clips
            self.bg = ColorClip(size, col=self.bg_color)
            self.created_bg = True

        
        
        # compute duration
        ends = [c.end for c in self.clips]
        if not any([(e is None) for e in ends]):
            self.duration = max(ends)
            self.end = max(ends)

        # compute audio
        audioclips = [v.audio for v in self.clips if v.audio is not None]
        if len(audioclips) > 0:
            self.audio = CompositeAudioClip(audioclips)

        # compute mask if necessary
        if transparent:
            maskclips = [(c.mask if (c.mask is not None) else
                          c.add_mask().mask).set_pos(c.pos).set_end(c.end).set_start(c.start, change_end=False)
                          for c in self.clips]

            self.mask = CompositeVideoClip(maskclips,self.size, ismask=True,
                                               bg_color=0.0)

        def make_frame(t):
            """ The clips playing at time `t` are blitted over one
                another. """

            f = self.bg.get_frame(t)
            for c in self.playing_clips(t):
                    f = c.blit_on(f, t)
            return f

        self.make_frame = make_frame

    def playing_clips(self, t=0):
        """ Returns a list of the clips in the composite clips that are
            actually playing at the given time `t`. """
        return [c for c in self.clips if c.is_playing(t)]

    def close(self):
        if self.created_bg and self.bg:
            # Only close the background clip if it was locally created.
            # Otherwise, it remains the job of whoever created it.
            self.bg.close()
            self.bg = None
        if hasattr(self, "audio") and self.audio:
            self.audio.close()
            self.audio = None
Esempio n. 16
0
    def __init__(self, clips, size=None, bg_color=None, use_bgclip=False,
                 ismask=False):

        if size is None:
            size = clips[0].size

        
        if use_bgclip and (clips[0].mask is None):
            transparent = False
        else:
            transparent = (bg_color is None)
        
        if bg_color is None:
            bg_color = 0.0 if ismask else (0, 0, 0)

        fpss = [c.fps for c in clips if hasattr(c, 'fps') and c.fps is not None]
        if len(fpss) == 0:
            self.fps = None
        else:
            self.fps = max(fpss)

        VideoClip.__init__(self)
        
        self.size = size
        self.ismask = ismask
        self.clips = clips
        self.bg_color = bg_color

        if use_bgclip:
            self.bg = clips[0]
            self.clips = clips[1:]
            self.created_bg = False
        else:
            self.clips = clips
            self.bg = ColorClip(size, col=self.bg_color)
            self.created_bg = True

        
        
        # compute duration
        ends = [c.end for c in self.clips]
        if not any([(e is None) for e in ends]):
            self.duration = max(ends)
            self.end = max(ends)

        # compute audio
        audioclips = [v.audio for v in self.clips if v.audio is not None]
        if len(audioclips) > 0:
            self.audio = CompositeAudioClip(audioclips)

        # compute mask if necessary
        if transparent:
            maskclips = [(c.mask if (c.mask is not None) else
                          c.add_mask().mask).set_pos(c.pos).set_end(c.end).set_start(c.start, change_end=False)
                          for c in self.clips]

            self.mask = CompositeVideoClip(maskclips,self.size, ismask=True,
                                               bg_color=0.0)

        def make_frame(t):
            """ The clips playing at time `t` are blitted over one
                another. """

            f = self.bg.get_frame(t)
            for c in self.playing_clips(t):
                    f = c.blit_on(f, t)
            return f

        self.make_frame = make_frame
Esempio n. 17
0
def concatenate(clipslist, method = 'chain', transition=None,
           bg_color=(0, 0, 0), transparent=False, ismask=False, crossover = 0):
    """ Concatenates several video clips
    
    Returns a video clip made by clip by concatenating several video clips.
    (Concatenated means that they will be played one after another).
    if the clips do not have the same resolution, the final
    resolution will be such that no clip has to be resized. As
    a consequence the final clip has the height of the highest
    clip and the width of the widest clip of the list. All the
    clips with smaller dimensions will appear centered. The border
    will be transparent if mask=True, else it will be of the
    color specified by ``bg_color``.
    
    Returns a VideoClip instance if all clips have the same size and
    there is no transition, else a composite clip.
    
    Parameters
    -----------

    clipslist
      A list of video clips which must all have their ``duration``
      attributes set.
    
    transition
      A clip that will be played between each two clips of the list.  
    
    bg_color
      Color of the background, if any.

    transparent
      If True, the resulting clip's mask will be the concatenation of
      the masks of the clips in the list. If the clips do not have the
      same resolution, the border around the smaller clips will be
      transparent.
    
                       
    """
    
    if transition != None:
        l = [[v, transition] for v in clipslist[:-1]]
        clipslist = reduce(lambda x, y: x + y, l) + [clipslist[-1]]
        transition = None
    
    tt = np.cumsum([0] + [c.duration for c in clipslist])
    sizes = [v.size for v in clipslist]
    w = max([r[0] for r in sizes])
    h = max([r[1] for r in sizes])
    
    if method == 'chain':
        result = VideoClip(ismask = ismask)
        result.size = (w,h)

        def gf(t):
            i = max([i for i, e in enumerate(tt) if e <= t])
            return clipslist[i].get_frame(t - tt[i])
        
        result.get_frame = gf
        if (len(set(map(tuple,sizes)))>1) and (bg_color is not None):
            # If not all clips have the same size, flatten the result
            # on some color
            result = result.fx( on_color, (w,h), bg_color, 'center')
        
    elif method == 'compose':
        tt = np.maximum(0, tt - crossover*np.arange(len(tt)))
        result = concatenate( [c.set_start(t).set_pos('center')
                                    for (c, t) in zip(clipslist, tt)],
                   size = (w, h), bg_color=bg_color, ismask=ismask,
                   transparent=transparent)
    
    result.tt = tt
    result.clipslist = clipslist
    result.start_times = tt[:-1]
    result.start, result.duration, result.end = 0, tt[-1] , tt[-1]
    
    # Compute the mask if any
    
    if transparent and (not ismask):
        # add a mask to the clips which have none
        clips_withmask = [(c if (c.mask!=None) else c.add_mask())
                          for c in clipslist] 
        result.mask = concatenate([c.mask for c in clips_withmask],
                    bg_color=0, ismask=True, transparent=False)
                    
                    
    # Compute the audio, if any.
    
    audio_t = [(c.audio,t) for c,t in zip(clipslist,tt) if c.audio!=None]
    if len(audio_t)>0:
        result.audio = CompositeAudioClip([a.set_start(t)
                                for a,t in audio_t])
    return result
Esempio n. 18
0
    def __init__(self,
                 clips,
                 size=None,
                 bg_color=None,
                 use_bgclip=True,
                 ismask=False):

        if size is None:
            size = clips[0].size

        if use_bgclip and (clips[0].mask is None):
            transparent = False
        else:
            transparent = (bg_color is None)

        if bg_color is None:
            bg_color = 0.0 if ismask else (0, 0, 0)

        fpss = [c.fps for c in clips if getattr(c, 'fps', None)]
        self.fps = max(fpss) if fpss else None

        VideoClip.__init__(self)

        self.size = size
        self.ismask = ismask
        self.clips = clips
        self.bg_color = bg_color

        if use_bgclip:
            self.bg = clips[0]
            self.clips = clips[1:]
            self.created_bg = False
        else:
            self.clips = clips
            self.bg = ColorClip(size, color=self.bg_color)
            self.created_bg = True

        # compute duration
        ends = [c.end for c in self.clips]
        if None not in ends:
            duration = max(ends)
            self.duration = duration
            self.end = duration

        # compute audio
        audioclips = [v.audio for v in self.clips if v.audio is not None]
        if audioclips:
            self.audio = CompositeAudioClip(audioclips)

        # compute mask if necessary
        if transparent:
            maskclips = [
                (c.mask if
                 (c.mask is not None) else c.add_mask().mask).set_position(
                     c.pos).set_end(c.end).set_start(c.start, change_end=False)
                for c in self.clips
            ]

            self.mask = CompositeVideoClip(maskclips,
                                           self.size,
                                           ismask=True,
                                           bg_color=0.0)

        def make_frame(t):
            full_w, full_h = self.bg.size
            f = self.bg.get_frame(t)
            bg_im = Image.fromarray(f)
            for c in self.playing_clips(t):
                img, pos, mask, ismask = c.new_blit_on(t, f)

                x, y = pos
                w, h = c.size

                out_x = x < -w or x == full_w
                out_y = y < -h or y == full_h

                if out_x and out_y:
                    continue

                pos = (int(round(min(max(-w, x), full_w))),
                       int(round(min(max(-h, y), full_h))))

                paste_im = Image.fromarray(img)

                if mask is not None:
                    mask_im = Image.fromarray(255 * mask).convert('L')
                    bg_im.paste(paste_im, pos, mask_im)
                else:
                    bg_im.paste(paste_im, pos)

            result_frame = np.array(bg_im)

            return result_frame.astype('uint8') if (
                not ismask) else result_frame

        self.make_frame = make_frame
Esempio n. 19
0
    def __init__(self, clips, size=None, bg_color=None, use_bgclip=False, ismask=False):

        if size is None:
            size = clips[0].size

        if use_bgclip and (clips[0].mask is None):
            transparent = False
        else:
            transparent = bg_color is None

        if bg_color is None:
            bg_color = 0.0 if ismask else (0, 0, 0)

        fpss = [c.fps for c in clips if getattr(c, "fps", None)]
        self.fps = max(fpss) if fpss else None

        VideoClip.__init__(self)

        self.size = size
        self.ismask = ismask
        self.clips = clips
        self.bg_color = bg_color

        if use_bgclip:
            self.bg = clips[0]
            self.clips = clips[1:]
            self.created_bg = False
        else:
            self.clips = clips
            self.bg = ColorClip(size, color=self.bg_color, ismask=ismask)
            self.created_bg = True

        # compute duration
        ends = [c.end for c in self.clips]
        if None not in ends:
            duration = max(ends)
            self.duration = duration
            self.end = duration

        # compute audio
        audioclips = [v.audio for v in self.clips if v.audio is not None]
        if audioclips:
            self.audio = CompositeAudioClip(audioclips)

        # compute mask if necessary
        if transparent:
            maskclips = [
                (c.mask if (c.mask is not None) else c.add_mask().mask)
                .set_position(c.pos)
                .set_end(c.end)
                .set_start(c.start, change_end=False)
                for c in self.clips
            ]

            self.mask = CompositeVideoClip(
                maskclips, self.size, ismask=True, bg_color=0.0
            )

        def make_frame(t):
            """ The clips playing at time `t` are blitted over one
                another. """

            f = self.bg.get_frame(t)
            for c in self.playing_clips(t):
                f = c.blit_on(f, t)
            return f

        self.make_frame = make_frame
Esempio n. 20
0
    def __init__(
        self,
        clips,
        size=None,
        bg_color=None,
        use_bgclip=False,
        ismask=False,
        allow_no_bg=False,
    ):

        if size is None:
            size = clips[0].size

        if use_bgclip and (clips[0].mask is None):
            transparent = False
        else:
            transparent = bg_color is None

        if bg_color is None:
            bg_color = 0.0 if ismask else (0, 0, 0)

        fpss = [
            c.fps for c in clips if hasattr(c, "fps") and c.fps is not None
        ]
        if len(fpss) == 0:
            self.fps = None
        else:
            self.fps = max(fpss)

        VideoClip.__init__(self)

        self.size = size
        self.ismask = ismask
        self.clips = clips
        self.bg_color = bg_color

        self.created_bg = False
        if use_bgclip:
            self.bg = clips[0]
            self.clips = clips[1:]
        elif allow_no_bg:
            # In some cases if we are rendering images,
            # we allow no bottom BG
            self.clips = clips
            self.bg = None
        else:
            self.bg = ColorClip(size, color=self.bg_color)
            self.created_bg = True

        # compute duration
        ends = [c.end for c in self.clips]
        if not any([(e is None) for e in ends]):
            self.duration = max(ends)
            self.end = max(ends)

        # compute audio
        audioclips = [v.audio for v in self.clips if v.audio is not None]
        if len(audioclips) > 0:
            self.audio = CompositeAudioClip(audioclips)

        # compute mask if necessary
        if transparent:
            maskclips = [
                (c.mask if
                 (c.mask is not None) else c.add_mask().mask).set_position(
                     c.pos).set_end(c.end).set_start(c.start, change_end=False)
                for c in self.clips
            ]

            self.mask = CompositeVideoClip(maskclips,
                                           self.size,
                                           ismask=True,
                                           bg_color=0.0)

        def make_frame(t):
            """ The clips playing at time `t` are blitted over one
                another. """

            f = None if self.bg is None else self.bg.get_frame(t)

            for c in self.playing_clips(t):
                if f is None:
                    f = c.get_frame(t)
                else:
                    f = c.blit_on(f, t)
            return f

        self.make_frame = make_frame
Esempio n. 21
0
def concatenate_videoclips(clips,
                           method="chain",
                           transition=None,
                           bg_color=None,
                           ismask=False,
                           padding=0):
    """ Concatenates several video clips
    
    Returns a video clip made by clip by concatenating several video clips.
    (Concatenated means that they will be played one after another).
    
    There are two methods:

    - method="chain": will produce a clip that simply outputs
      the frames of the succesive clips, without any correction if they are
      not of the same size of anything. If none of the clips have masks the
      resulting clip has no mask, else the mask is a concatenation of masks
      (using completely opaque for clips that don't have masks, obviously).
      If you have clips of different size and you want to write directly the
      result of the concatenation to a file, use the method "compose" instead.

    - method="compose", if the clips do not have the same
      resolution, the final resolution will be such that no clip has
       to be resized.
       As a consequence the final clip has the height of the highest
       clip and the width of the widest clip of the list. All the
       clips with smaller dimensions will appear centered. The border
       will be transparent if mask=True, else it will be of the
       color specified by ``bg_color``.

    If all clips with a fps attribute have the same fps, it becomes the fps of
    the result.

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

    clips
      A list of video clips which must all have their ``duration``
      attributes set.

    method
      "chain" or "compose": see above.

    transition
      A clip that will be played between each two clips of the list.
    
    bg_color
      Only for method='compose'. Color of the background.
      Set to None for a transparent clip
    
    padding
      Only for method='compose'. Duration during two consecutive clips.
      Note that for negative padding, a clip will partly play at the same
      time as the clip it follows (negative padding is cool for clips who fade
      in on one another). A non-null padding automatically sets the method to
      `compose`.
           
    """

    if transition is not None:
        l = [[v, transition] for v in clips[:-1]]
        clips = reduce(lambda x, y: x + y, l) + [clips[-1]]
        transition = None

    tt = np.cumsum([0] + [c.duration for c in clips])

    sizes = [v.size for v in clips]

    w = max([r[0] for r in sizes])
    h = max([r[1] for r in sizes])

    tt = np.maximum(0, tt + padding * np.arange(len(tt)))

    if method == "chain":

        def make_frame(t):
            i = max([i for i, e in enumerate(tt) if e <= t])
            return clips[i].get_frame(t - tt[i])

        result = VideoClip(ismask=ismask, make_frame=make_frame)
        if any([c.mask is not None for c in clips]):
            masks = [
                c.mask if (c.mask is not None) else ColorClip(
                    [1, 1], col=1, ismask=True, duration=c.duration)
                #ColorClip(c.size, col=1, ismask=True).set_duration(c.duration)
                for c in clips
            ]
            result.mask = concatenate_videoclips(masks,
                                                 method="chain",
                                                 ismask=True)
            result.clips = clips

    elif method == "compose":
        result = CompositeVideoClip(
            [c.set_start(t).set_pos('center') for (c, t) in zip(clips, tt)],
            size=(w, h),
            bg_color=bg_color,
            ismask=ismask)

    result.tt = tt

    result.start_times = tt[:-1]
    result.start, result.duration, result.end = 0, tt[-1], tt[-1]

    audio_t = [(c.audio, t) for c, t in zip(clips, tt) if c.audio is not None]
    if len(audio_t) > 0:
        result.audio = CompositeAudioClip([a.set_start(t) for a, t in audio_t])

    fps_list = list(set([c.fps for c in clips if hasattr(c, 'fps')]))
    if len(fps_list) == 1:
        result.fps = fps_list[0]

    return result
Esempio n. 22
0
    def __init__(
        self, clips, size=None, bg_color=None, use_bgclip=False, is_mask=False
    ):

        if size is None:
            size = clips[0].size

        if use_bgclip and (clips[0].mask is None):
            transparent = False
        else:
            transparent = bg_color is None

        if bg_color is None:
            bg_color = 0.0 if is_mask else (0, 0, 0)

        fpss = [clip.fps for clip in clips if getattr(clip, "fps", None)]
        self.fps = max(fpss) if fpss else None

        VideoClip.__init__(self)

        self.size = size
        self.is_mask = is_mask
        self.clips = clips
        self.bg_color = bg_color

        if use_bgclip:
            self.bg = clips[0]
            self.clips = clips[1:]
            self.created_bg = False
        else:
            self.clips = clips
            self.bg = ColorClip(size, color=self.bg_color, is_mask=is_mask)
            self.created_bg = True

        # order self.clips by layer
        self.clips = sorted(self.clips, key=lambda clip: clip.layer)

        # compute duration
        ends = [clip.end for clip in self.clips]
        if None not in ends:
            duration = max(ends)
            self.duration = duration
            self.end = duration

        # compute audio
        audioclips = [v.audio for v in self.clips if v.audio is not None]
        if audioclips:
            self.audio = CompositeAudioClip(audioclips)

        # compute mask if necessary
        if transparent:
            maskclips = [
                (clip.mask if (clip.mask is not None) else clip.add_mask().mask)
                .with_position(clip.pos)
                .with_end(clip.end)
                .with_start(clip.start, change_end=False)
                .with_layer(clip.layer)
                for clip in self.clips
            ]

            self.mask = CompositeVideoClip(
                maskclips, self.size, is_mask=True, bg_color=0.0
            )
Esempio n. 23
0
class CompositeVideoClip(VideoClip):
    """ 
    
    A VideoClip made of other videoclips displayed together. This is the
    base class for most compositions.
    
    Parameters
    ----------

    size
      The size (height x width) of the final clip.

    clips
      A list of videoclips. Each clip of the list will
      be displayed below the clips appearing after it in the list.
      For each clip:
       
      - The attribute ``pos`` determines where the clip is placed.
          See ``VideoClip.set_pos``
      - The mask of the clip determines which parts are visible.
        
      Finally, if all the clips in the list have their ``duration``
      attribute set, then the duration of the composite video clip
      is computed automatically

    bg_color
      Color for the unmasked and unfilled regions. Set to None for these
      regions to be transparent (will be slower).

    use_bgclip
      Set to True if the first clip in the list should be used as the
      'background' on which all other clips are blitted. That first clip must
      have the same size as the final clip. If it has no transparency, the final
      clip will have no mask. 
    
    The clip with the highest FPS will be the FPS of the composite clip.

    """
    def __init__(self,
                 clips,
                 size=None,
                 bg_color=None,
                 use_bgclip=True,
                 ismask=False):

        if size is None:
            size = clips[0].size

        if use_bgclip and (clips[0].mask is None):
            transparent = False
        else:
            transparent = (bg_color is None)

        if bg_color is None:
            bg_color = 0.0 if ismask else (0, 0, 0)

        fpss = [c.fps for c in clips if getattr(c, 'fps', None)]
        self.fps = max(fpss) if fpss else None

        VideoClip.__init__(self)

        self.size = size
        self.ismask = ismask
        self.clips = clips
        self.bg_color = bg_color

        if use_bgclip:
            self.bg = clips[0]
            self.clips = clips[1:]
            self.created_bg = False
        else:
            self.clips = clips
            self.bg = ColorClip(size, color=self.bg_color)
            self.created_bg = True

        # compute duration
        ends = [c.end for c in self.clips]
        if None not in ends:
            duration = max(ends)
            self.duration = duration
            self.end = duration

        # compute audio
        audioclips = [v.audio for v in self.clips if v.audio is not None]
        if audioclips:
            self.audio = CompositeAudioClip(audioclips)

        # compute mask if necessary
        if transparent:
            maskclips = [
                (c.mask if
                 (c.mask is not None) else c.add_mask().mask).set_position(
                     c.pos).set_end(c.end).set_start(c.start, change_end=False)
                for c in self.clips
            ]

            self.mask = CompositeVideoClip(maskclips,
                                           self.size,
                                           ismask=True,
                                           bg_color=0.0)

        def make_frame(t):
            full_w, full_h = self.bg.size
            f = self.bg.get_frame(t)
            bg_im = Image.fromarray(f)
            for c in self.playing_clips(t):
                img, pos, mask, ismask = c.new_blit_on(t, f)

                x, y = pos
                w, h = c.size

                out_x = x < -w or x == full_w
                out_y = y < -h or y == full_h

                if out_x and out_y:
                    continue

                pos = (int(round(min(max(-w, x), full_w))),
                       int(round(min(max(-h, y), full_h))))

                paste_im = Image.fromarray(img)

                if mask is not None:
                    mask_im = Image.fromarray(255 * mask).convert('L')
                    bg_im.paste(paste_im, pos, mask_im)
                else:
                    bg_im.paste(paste_im, pos)

            result_frame = np.array(bg_im)

            return result_frame.astype('uint8') if (
                not ismask) else result_frame

        self.make_frame = make_frame

    def playing_clips(self, t=0):
        """ Returns a list of the clips in the composite clips that are
            actually playing at the given time `t`. """
        return [c for c in self.clips if c.is_playing(t)]

    def close(self):
        if self.created_bg and self.bg:
            # Only close the background clip if it was locally created.
            # Otherwise, it remains the job of whoever created it.
            self.bg.close()
            self.bg = None
        if hasattr(self, "audio") and self.audio:
            self.audio.close()
            self.audio = None
Esempio n. 24
0
    def __init__(self,
                 clips,
                 size=None,
                 bg_color=None,
                 use_bgclip=False,
                 ismask=False):

        if size is None:
            size = clips[0].size

        if use_bgclip and (clips[0].mask is None):
            transparent = False
        else:
            transparent = (bg_color is None)

        if bg_color is None:
            bg_color = 0.0 if ismask else (0, 0, 0)

        fps_list = list(set([c.fps for c in clips if hasattr(c, 'fps')]))
        if len(fps_list) == 1:
            self.fps = fps_list[0]

        VideoClip.__init__(self)

        self.size = size
        self.ismask = ismask
        self.clips = clips
        self.bg_color = bg_color

        if use_bgclip:
            self.bg = clips[0]
            self.clips = clips[1:]
        else:
            self.clips = clips
            self.bg = ColorClip(size, col=self.bg_color)

        # compute duration
        ends = [c.end for c in self.clips]
        if not any([(e is None) for e in ends]):
            self.duration = max(ends)
            self.end = max(ends)

        # compute audio
        audioclips = [v.audio for v in self.clips if v.audio is not None]
        if len(audioclips) > 0:
            self.audio = CompositeAudioClip(audioclips)

        # compute mask if necessary
        if transparent:
            maskclips = [
                (c.mask if
                 (c.mask is not None) else c.add_mask().mask).set_pos(c.pos)
                for c in self.clips
            ]

            self.mask = CompositeVideoClip(maskclips,
                                           self.size,
                                           ismask=True,
                                           bg_color=0.0)

        def make_frame(t):
            """ The clips playing at time `t` are blitted over one
                another. """

            f = self.bg.get_frame(t)
            for c in self.playing_clips(t):
                f = c.blit_on(f, t)
            return f

        self.make_frame = make_frame