예제 #1
0
def posterize(files, posterfile=None, background_color="black", margin=5):
    min_h = max_height(files)
    min_w = max_width(files)
    util.logger.debug("Max W x H = %d x %d", min_w, min_h)
    gap = (min_w * margin) // 100

    nb_files = len(files)
    root = math.sqrt(nb_files)
    rows = int(round(root))
    if rows < root:
        rows += 1
    cols = (nb_files + rows-1) // rows

    full_w = (cols*min_w) + (cols+1)*gap
    full_h = (rows*min_h) + (rows+1)*gap

    util.logger.debug("W x H = %d x %d / Gap = %d / c,r = %d, %d => Full W x H = %d x %d",
                      min_w, min_h, gap, cols, rows, full_w, full_h)
    bgfile = "white-square.jpg" if background_color == "white" else "black-square.jpg"
    tmpbg = "bg.tmp.jpg"
    rescale(bgfile, full_w, full_h, tmpbg)

    file_list = util.build_ffmpeg_file_list(files)

    cmplx = util.build_ffmpeg_complex_prep(files)
    cmplx = cmplx + __build_poster_fcomplex(rows, cols, gap, min_w, min_h, len(files))

    posterfile = util.automatic_output_file_name(posterfile, files[0], "poster")
    util.run_ffmpeg('-i "%s" %s -filter_complex "%s" "%s"' % (tmpbg, file_list, cmplx, posterfile))
    util.logger.info("Generated %s", posterfile)
    util.delete_files(tmpbg)
    return posterfile
예제 #2
0
    def shake_horizontal(self, nbr_slices = 10 , shake_pct = 3, background_color = "black", out_file = None):
        w, h = self.get_dimensions()
        w_jitter = w * shake_pct // 100
        slice_height = max(h // nbr_slices, 16)
        slices = self.slice_horizontal(nbr_slices)
        tmpbg = get_rectangle(background_color, w + w_jitter, slice_height * len(slices))
        filelist = util.build_ffmpeg_file_list(slices) + ' -i "%s"' % tmpbg
        cmplx = util.build_ffmpeg_complex_prep(slices)

        step = 0
        n_slices = len(slices)
        cmplx = cmplx + "[%d][pip0]overlay=0:0[step%d]; " % (n_slices, step)
        first_slice = slices.pop(0)

        for j in range(n_slices):
            x = random.randint(1, w_jitter)
            y = (j+1) * slice_height
            cmplx = cmplx + "[step%d][pip%d]overlay=%d:%d" % (j, j+1, x, y)
            if j < n_slices-1:
                cmplx = cmplx + '[step%d]; ' % (j+1)
            j = j+1

        out_file = util.automatic_output_file_name(out_file, self.filename, "shake")
        util.run_ffmpeg(' %s -filter_complex "%s" %s' % (filelist, cmplx, out_file))
        util.delete_files(*slices, first_slice, tmpbg)
        return out_file
예제 #3
0
    def encode(self, target_file, profile, **kwargs):
        '''Encodes a file
        - target_file is the name of the output file. Optional
        - Profile is the encoding profile as per the VideoTools.properties config file
        - **kwargs accepts at large panel of other ptional options'''

        util.logger.debug("Encoding %s with profile %s and args %s",
                          self.filename, profile, str(kwargs))
        if target_file is None:
            target_file = media.build_target_file(self.filename, profile)

        media_opts = self.get_properties()
        util.logger.debug("Input file settings = %s", str(media_opts))
        media_opts.update(
            util.get_ffmpeg_cmdline_params(
                util.get_conf_property(profile + '.cmdline')))
        util.logger.debug("After profile settings(%s) = %s", profile,
                          str(media_opts))
        media_opts.update(kwargs)
        util.logger.debug("After cmd line settings(%s) = %s", str(kwargs),
                          str(media_opts))

        ffopts = opt.media2ffmpeg(media_opts)
        util.logger.debug("FFOPTS = %s", str(ffopts))
        util.run_ffmpeg('-i "%s" %s "%s"' %
                        (self.filename, util.dict2str(ffopts), target_file))
        util.logger.info("File %s encoded", target_file)
        return target_file
예제 #4
0
    def crop(self, width, height, top, left, out_file, **kwargs):
        ''' Applies crop video filter for width x height pixels '''

        if width is str:
            width = int(width)
        if height is str:
            height = int(height)
        i_bitrate = self.get_video_bitrate()
        i_w, i_h = self.get_dimensions()
        media_opts = self.get_properties()
        media_opts[opt.media.ACODEC] = 'copy'
        # Target bitrate proportional to crop level (+ 20%)
        media_opts[opt.media.VBITRATE] = int(
            int(i_bitrate) * (width * height) / (int(i_w) * int(i_h)) * 1.2)

        media_opts.update(util.cleanup_options(kwargs))
        util.logger.info("Cmd line settings = %s", str(media_opts))
        out_file = util.automatic_output_file_name(out_file, self.filename, \
            "crop_{0}x{1}-{2}x{3}".format(width, height, top, left))
        aspect = __get_aspect_ratio__(width, height, **kwargs)


        cmd = '-i "%s" %s %s -aspect %s "%s"' % (self.filename, \
            media.build_ffmpeg_options(media_opts), media.get_crop_filter_options(width, height, top, left), \
            aspect, out_file)
        util.run_ffmpeg(cmd)
        return out_file
예제 #5
0
 def add_stream_property(self, stream_index, prop, value=None):
     direct_copy = '-vcodec copy -c:a copy -map 0'
     output_file = util.add_postfix(self.filename, "meta")
     if value is None:
         stream_index, value = stream_index.split(':')
     util.run_ffmpeg('-i "{0}" {1} -metadata:s:a:{2} {3}="{4}" "{5}"'.format( \
         self.filename, direct_copy, stream_index, prop, value, output_file))
     return output_file
예제 #6
0
 def set_tracks_property(self, prop, **props):
     util.logger.debug("Set tracks properties: %s-->%s", prop, str(props))
     meta = VideoFile.AV_PASSTHROUGH
     for idx, propval in props.items():
         meta += '-metadata:s:a:{0} {1}="{2}" '.format(idx, prop, propval)
     output_file = util.add_postfix(self.filename, prop)
     util.run_ffmpeg('-i "{0}" {1} "{2}"'.format(self.filename,
                                                 meta.strip(), output_file))
     return output_file
예제 #7
0
    def encode(self, target_file, profile, **kwargs):
        '''Encodes a file
        - target_file is the name of the output file. Optional
        - Profile is the encoding profile as per the VideoTools.properties config file
        - **kwargs accepts at large panel of other ptional options'''

        util.logger.debug("Encoding %s with profile %s and args %s",
                          self.filename, profile, str(kwargs))
        if target_file is None:
            target_file = media.build_target_file(self.filename, profile)

        media_opts = {}
        video_filters = []
        media_opts = self.get_properties()
        util.logger.debug("File settings(%s) = %s", self.filename,
                          str(media_opts))
        media_opts.update(
            util.get_ffmpeg_cmdline_params(
                util.get_conf_property(profile + '.cmdline')))
        util.logger.debug("After profile settings(%s) = %s", profile,
                          str(media_opts))
        media_opts.update(kwargs)
        util.logger.debug("After cmd line settings(%s) = %s", str(kwargs),
                          str(media_opts))

        media_opts['input_params'] = ''
        if 'hw_accel' in kwargs and kwargs['hw_accel'] is True:
            if re.search(r'[xh]264', media_opts[opt.media.VCODEC]):
                util.logger.debug("Patching settings for H264 hw acceleration")
                media_opts[opt.media.VCODEC] = 'h264_nvenc'
                media_opts['input_params'] = '-hwaccel cuvid -c:v h264_cuvid'
            elif re.search(r'[xh]265', media_opts[opt.media.VCODEC]):
                util.logger.debug("Patching settings for H265 hw acceleration")
                media_opts[opt.media.VCODEC] = 'hevc_nvenc'
                media_opts['input_params'] = '-hwaccel cuvid -c:v h264_cuvid'
            if media_opts[
                    'input_params'] != '' and opt.media.SIZE in media_opts and media_opts[
                        opt.media.SIZE] is not None:
                media_opts['input_params'] += ' -resize ' + media_opts[
                    opt.media.SIZE]
                del media_opts[opt.media.SIZE]

        util.logger.debug("After hw acceleration = %s", str(media_opts))

        ffopts = opt.media2ffmpeg(media_opts)
        util.logger.debug("After converting to ffmpeg params = %s",
                          str(ffopts))

        # Hack for channels selection
        mapping = __get_audio_channel_mapping__(**kwargs)

        video_filters.append(self.__get_fader_filter__(**kwargs))

        util.run_ffmpeg('%s -i "%s" %s %s %s "%s"' % (media_opts['input_params'], self.filename, util.dict2str(ffopts), \
                        media.build_video_filters_options(video_filters), mapping, target_file))
        util.logger.info("File %s encoded", target_file)
        return target_file
예제 #8
0
def concat(target_file, file_list):
#    ffmpeg -i opening.mkv -i episode.mkv -i ending.mkv \
#  -filter_complex "[0:v] [0:a] [1:v] [1:a] [2:v] [2:a] concat=n=3:v=1:a=1 [v] [a]" \
#  -map "[v]" -map "[a]" output.mkv
    util.logger.info("Concatenating %s", str(file_list))
    cmd = util.build_ffmpeg_file_list(file_list)
    cmd = cmd + '-filter_complex "'
    for i in range(len(file_list)):
        cmd = cmd + ('[%d:v] [%d:a] ' % (i, i))
    cmd = cmd + 'concat=n=%d:v=1:a=1 [v] [a]" -map "[v]" -map "[a]" %s' % (len(file_list), target_file)
    util.run_ffmpeg(cmd)
예제 #9
0
    def blindify(self, out_file = None, **kwargs):
        nbr_slices = int(kwargs.pop('blinds', 10))
        blinds_size_pct = int(kwargs.pop('blinds_ratio', 3))
        background_color = kwargs.pop('background_color', 'black')
        direction = kwargs.pop('direction', 'vertical')
        w, h = self.get_dimensions()

        w_gap = w * blinds_size_pct // 100
        h_gap = h * blinds_size_pct // 100

        if direction == 'horizontal':
            tmpbg = get_rectangle(background_color, w, (h//nbr_slices*nbr_slices) + h_gap*(nbr_slices-1))
        else:
            tmpbg = get_rectangle(background_color, (w//nbr_slices*nbr_slices) + w_gap*(nbr_slices-1), h)

        # ffmpeg -i file1.jpg -i file2.jpg -i bg.tmp.jpg \
        # -filter_complex "[0]scale=iw:-1:flags=lanczos[pip0]; \
        # [1]scale=iw:-1:flags=lanczos[pip1]; \
        # [8]scale=iw:-1:flags=lanczos[pip8]; \
        # [9][pip0]overlay=204:204[step0] ; \
        # [step0][pip1]overlay=2456:204[step1]; \
        # [step7][pip8]overlay=4708:3374" outfile.jpg

        slices = self.slice(nbr_slices, direction)
        filelist = util.build_ffmpeg_file_list(slices)
        filelist = filelist + ' -i "%s"' % tmpbg
        cmplx = util.build_ffmpeg_complex_prep(slices)

        i = 0
        cmplx = ''
        for slicefile in slices:
            cmplx = cmplx + "[%d]scale=iw:-1:flags=lanczos[pip%d]; " % (i, i)
            i = i + 1

        step = 0
        cmplx = cmplx + "[%d][pip0]overlay=0:0[step%d]; " % (i, step)
        first_slice = slices.pop(0)
        j = 0
        x = 0
        y = 0
        for slicefile in slices:
            if direction == 'horizontal':
                y = (j+1) * (h // nbr_slices + h_gap)
            else:
                x = (j+1) * (w // nbr_slices + w_gap)
            cmplx = cmplx + "[step%d][pip%d]overlay=%d:%d" % (j, j+1, x, y)
            if slicefile != slices[len(slices)-1]:
                cmplx = cmplx + '[step%d]; ' % (j+1)
            j = j+1

        out_file = util.automatic_output_file_name(out_file, self.filename, "blind")
        util.run_ffmpeg('%s -filter_complex "%s" %s' % (filelist, cmplx, out_file))
        util.delete_files(*slices, first_slice, tmpbg)
예제 #10
0
 def add_audio_tracks(self, *audio_files):
     inputs = '-i "{0}"'.format(self.filename)
     maps = '-map 0'
     i = 1
     for audio_file in audio_files:
         inputs += ' -i "{0}"'.format(audio_file)
         maps += ' -map {0}'.format(i)
         i += 1
     output_file = util.add_postfix(self.filename, "muxed")
     util.run_ffmpeg('{0} {1} -dn -codec copy "{2}"'.format(
         inputs, maps, output_file))
     return output_file
예제 #11
0
 def add_metadata(self, **metadatas):
     # ffmpeg -i in.mp4 -vcodec copy -c:a copy -map 0 -metadata year=<year>
     # -metadata copyright="(c) O. Korach <year>"  -metadata author="Olivier Korach"
     # -metadata:s:a:0 language=fre -metadata:s:a:0 title="Avec musique"
     # -metadata:s:v:0 language=fre -disposition:a:0 default -disposition:a:1 none "%~1.meta.mp4"
     util.logger.debug("Add metadata: %s", str(metadatas))
     opts = VideoFile.AV_PASSTHROUGH
     for key, value in metadatas.items():
         opts += '-metadata {0}="{1}" '.format(key, value)
     output_file = util.add_postfix(self.filename, "meta")
     util.run_ffmpeg('-i "{0}" {1} "{2}"'.format(self.filename,
                                                 opts.strip(), output_file))
     return output_file
예제 #12
0
 def set_default_track(self, track):
     # ffmpeg -i in.mp4 -vcodec copy -c:a copy -map 0
     # -disposition:a:0 default -disposition:a:1 none out.mp4
     util.logger.debug("Set default track: %s", track)
     disp = VideoFile.AV_PASSTHROUGH
     for i in range(self.__get_number_of_audio_tracks() + 1):
         util.logger.debug("i = %d, nb tracks = %d", i,
                           self.__get_number_of_audio_tracks())
         is_default = "default" if i == track else "none"
         disp += "-disposition:a:{0} {1} ".format(i, is_default)
     output_file = util.add_postfix(self.filename, "track")
     util.run_ffmpeg('-i "{0}" {1} "{2}"'.format(self.filename,
                                                 disp.strip(), output_file))
     return output_file
예제 #13
0
def concat(target_file, file_list):
    '''Concatenates several video files - They must have same video+audio format and bitrate'''
    util.logger.info("%s = %s", target_file, ' + '.join(file_list))
    cmd = ''
    for file in file_list:
        cmd += (' -i "%s" ' % file)
    count = 0
    cmd += '-filter_complex "'
    for file in file_list:
        cmd += ("[%d:v][%d:a]" % (count, count))
        count += 1
    cmd += 'concat=n=%d:v=1:a=1[outv][outa]" -map "[outv]" -map "[outa]" "%s"' % (
        count, target_file)
    util.run_ffmpeg(cmd.strip())
예제 #14
0
def encode_album_art(source_file, album_art_file, **kwargs):
    """Encodes album art image in an audio file after optionally resizing"""
    # profile = 'album_art' - # For the future, we'll use the cmd line associated to the profile in the config file

    album_art_std_settings = '-metadata:s:v title="Album cover" -metadata:s:v comment="Cover (Front)"'
    target_file = util.add_postfix(source_file, 'album_art')

    if kwargs['scale'] is not None:
        w, h = re.split("x", kwargs['scale'])
        album_art_file = image.rescale(album_art_file, int(w), int(h))
        delete_aa_file = True

    # ffmpeg -i %1 -i %2 -map 0:0 -map 1:0 -c copy -id3v2_version 3 -metadata:s:v title="Album cover"
    # -metadata:s:v comment="Cover (Front)" %1.mp3
    util.run_ffmpeg('-i "%s" -i "%s"  -map 0:0 -map 1:0 -c copy -id3v2_version 3 %s "%s"' % \
        (source_file, album_art_file, album_art_std_settings, target_file))
    shutil.copy(target_file, source_file)
    os.remove(target_file)
    if delete_aa_file:
        os.remove(album_art_file)
예제 #15
0
 def resize(self, width = None, height = None, out_file = None):
     '''Resizes an image file
     If one of width or height is None, then it is calculated to
     preserve the image aspect ratio'''
     if width is None and height is None:
         util.logger.error("Resize requested with neither width not height")
         return None
     if isinstance(width, str):
         width = int(width)
     if isinstance(height, str):
         height = int(height)
     if width is None:
         w, h = self.get_dimensions()
         width = w * height // h
     elif height is None:
         w, h = self.get_dimensions()
         height = h * width // w
     util.logger.debug("Resizing %s to %d x %d into %s", self.filename, width, height, out_file)
     out_file = util.automatic_output_file_name(out_file, self.filename, "resized-%dx%d" % (width, height))
     util.run_ffmpeg('-i "%s" -vf scale=%d:%d "%s"' % (self.filename, width, height, out_file))
     return out_file
예제 #16
0
def stack(file1, file2, direction, out_file = None):
    util.logger.debug("stack(%s, %s, %s, _)", file1, file2, direction)
    if not util.is_image_file(file1):
        raise media.FileTypeError('File %s is not an image file' % file1)
    if not util.is_image_file(file2):
        raise media.FileTypeError('File %s is not an image file' % file2)
    out_file = util.automatic_output_file_name(out_file, file1, "stacked")
    w1, h1 = ImageFile(file1).get_dimensions()
    w2, h2 = ImageFile(file2).get_dimensions()
    tmpfile1 = file1
    tmpfile2 = file2
    util.logger.debug("Images dimensions: %d x %d and %d x %d", w1, h1, w2, h2)
    if direction == 'horizontal':
        filter_name = 'hstack'
        if h1 > h2:
            new_w2 = w2 * h1 // h2
            tmpfile2 = rescale(file2, new_w2, h1)
        elif h2 > h1:
            new_w1 = w1 * h2 // h1
            tmpfile1 = rescale(file1, new_w1, h2)
    else:
        filter_name = 'vstack'
        if w1 > w2:
            new_h2 = h2 * w1 // w2
            tmpfile2 = rescale(file2, w1, new_h2)
        elif w2 > w1:
            new_h1 = h1 * w2 // w1
            tmpfile1 = rescale(file1, w2, new_h1)

    # ffmpeg -i a.jpg -i b.jpg -filter_complex hstack output

    util.run_ffmpeg('-i "%s" -i "%s" -filter_complex %s "%s"' % (tmpfile1, tmpfile2, filter_name, out_file))
    if tmpfile1 is not file1:
        util.delete_files(tmpfile1)
    if tmpfile2 is not file2:
        util.delete_files(tmpfile2)
    return out_file
예제 #17
0
    def cut(self, start, stop, out_file=None, **kwargs):
        if out_file is None:
            out_file = util.automatic_output_file_name(
                out_file, self.filename, "cut_%s-to-%s" % (start, stop))
        util.logger.debug("Cutting %s from %s to %s into %s", self.filename,
                          start, stop, out_file)
        media_opts = self.get_properties()
        kwargs['start'] = start
        kwargs['stop'] = stop

        video_filters = []
        if 'fade' in kwargs and kwargs['fade'] is not None:
            fade_d = int(kwargs['fade'])
            fmt = "fade=type={0}:duration={1}:start_time={2}"
            fader = fmt.format('in', fade_d, start) + "," + fmt.format(
                'out', fade_d, stop - fade_d)
            video_filters.append(fader)

        util.run_ffmpeg(
            '-i "%s" %s %s "%s"' %
            (self.filename, opt.media2ffmpeg(media_opts),
             media.build_video_filters_options(video_filters), out_file))

        return out_file
예제 #18
0
    def deshake(self, width, height, out_file, **kwargs):
        ''' Applies deshake video filter for width x height pixels '''
        media_opts = self.get_properties()
        media_opts.update({
            opt.media.DEINTERLACE: None,
            opt.media.ASPECT: self.get_aspect_ratio()
        })
        media_opts.update(util.cleanup_options(kwargs))

        if out_file is None or 'nocrop' in kwargs:
            output_file = util.add_postfix(self.filename,
                                           "deshake_%dx%d" % (width, height))
        else:
            output_file = out_file

        ffopts = opt.media2ffmpeg(media_opts)
        cmd = '-i "%s" %s %s "%s"' % (self.filename, \
            util.dict2str(ffopts), get_deshake_filter_options(width, height), output_file)
        util.run_ffmpeg(cmd)

        if 'nocrop' not in kwargs:
            return output_file

        new_w = self.get_width() - width
        new_h = self.get_height() - height
        if out_file is None:
            output_file2 = util.add_postfix(
                self.filename, "deshake_crop_%dx%d" % (new_w, new_h))
        else:
            output_file2 = out_file
        deshake_file_o = VideoFile(output_file)
        kwargs.update({opt.media.ASPECT: self.get_aspect_ratio()})
        deshake_file_o.crop(new_w, new_h, width // 2, height // 2,
                            output_file2, **kwargs)
        os.remove(output_file)
        return output_file2
예제 #19
0
def rescale(image_file, width, height, out_file = None):
    util.logger.debug("Rescaling %s to %d x %d into %s", image_file, width, height, out_file)
    # ffmpeg -i input.jpg -vf scale=320:240 output_320x240.png
    out_file = util.automatic_output_file_name(out_file, image_file, "scale-%dx%d" % (width, height))
    util.run_ffmpeg('-i "%s" -vf scale=%d:%d "%s"' % (image_file, width, height, out_file))
    return out_file
예제 #20
0
 def crop(self, w, h, x, y, out_file = None):
     util.logger.debug("%s(->%s, %d, %d, %d, %d)", 'crop', self.filename, w, h, x, y)
     out_file = util.automatic_output_file_name(out_file, self.filename, "crop.%dx%d" % (w, h))
     # ffmpeg -i input.png -vf  "crop=w:h:x:y" input_crop.png
     util.run_ffmpeg('-y -i "%s" -vf crop=%d:%d:%d:%d "%s"' % (self.filename, w, h, x, y, out_file))