windows平台 ffmpeg视频滤镜 python 验证指南

windows下ffmpeg视频滤镜验证,验证方式如下,可以二选一

第一种 ffplay实时播放

第二种 ffmpeg 输出gif 或 ts文件

ffmpeg python3在windows上搭建环境方法请参考:

windows平台python3.x调用ffmpeg指南_Zymill's Blog-CSDN博客

依赖项hysmm_def / ffmpeg_run,请参考:

GitHub - zymill/ffmpeg_hys: Python or Bat files for Running ffmpeg on Windows

#!/usr/bin/python
# -*- coding: UTF-8 -*-

#/**************************************************************************
#* ffmpeg_vf.py: video filter cases on windows: python 3.x, ffmpeg-4.3.1
#***************************************************************************
#* Copyright (c) 2020-2022 Hybase@qq.com
#*
#* This program is free software: you can redistribute it and/or modify
#* it under the terms of the GNU General Public License as published by
#* the Free Software Foundation, either version 3 of the License, or
#* (at your option) any later version.
#*
#* This program is distributed in the hope that it will be useful,
#* but WITHOUT ANY WARRANTY; without even the implied warranty of
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#* GNU General Public License for more details.
#*
#****************************************************************************/
import os
import re
import sys
import hysmm_def
import ffmpeg_run

################################################################################
# 通用参数字符串

dst_dir        = hysmm_def.dst_dir
log_param      = hysmm_def.log_param
tt_src_dir     = 'e:/material/ffmpeg_hys_python'
input_mkv_file = ' -i ' + tt_src_dir + '/tt.mkv '
input_mp4_file = ' -i ' + tt_src_dir + '/tt.mp4 '
input_ts_file  = ' -i ' + tt_src_dir + '/tt.ts '
input_mp3_file = ' -i ' + tt_src_dir + '/es_mp3.mp3 '
input_png_file = ' -f image2 -i ' + tt_src_dir + '/image/image-%03.png '
hls_cancat_file= tt_src_dir + '/hls_part/hls_concat.txt '
vf_out = dst_dir + '/vf_effect'

#log, vf_str, 'd:/otest/case019_vf_crop.mp4', flag_vf_crop, 'case_vf_crop'
def ffmpeg_vf_exec(log, vf_str, output_file_name, flag, case_name):
    ffmpeg_run.ffmpeg_vf_run(case_name, flag, log, input_mkv_file, vf_str, output_file_name)
    return
################################################################################
# case flags: (default: the first be set, please set others by yourself)
overlay_flag = False
play_flag    = False    # true - ffplay, false - ffmpeg
sfx          = '.ts'   # .gif or .ts
start_num    = 3
end_num      = 39

class vf_case_c:
    def __init__(self):

        # crop=w=%d:h=%d:x=%d:y=%d
        # 注释:宽度:高度:x:y(x,y如果不写则从中心裁剪)
        # 注意事项:width, height, x, y 不要超出原始视频边界
        self.case002 = [vf_out + '/d002_crop' + sfx,               " crop=w=960:h=540:x=80:y=40 "]
        self.case003 = [vf_out + '/d003_rotate_1_dynamic' + sfx,   " rotate='PI*2/10*t' "]            # 视频动态旋转 (T为旋转360度的时间常量, 单位:秒)
        self.case004 = [vf_out + '/d004_rotate_2_45degress' + sfx, " rotate='PI/4' "]                 # 视频旋转45度 (保留原始尺寸)
        self.case005 = [vf_out + '/d005_rotate_3_45degress' + sfx, " rotate='PI/4:ow=floor(hypot(iw,ih)/4)*4:oh=ow:c=none' "] # 视频旋转45度,以对角线长度为宽高
        self.case006 = [vf_out + '/d006_transpose_1_cw90' + sfx,   " transpose=1:portrait "]          # 视频顺时针旋转90 (transpose=1)
        self.case007 = [vf_out + '/d007_transpose_2_ccw90' + sfx,  " transpose=2 "]                   # 视频逆时针旋转90 (transpose=2)
        self.case008 = [vf_out + '/d008_transpose_3' + sfx,        " transpose=3 "]                   # 视频逆时针旋转90度后垂直镜像 (transpose=3)
        self.case009 = [vf_out + '/d009_hflip_vflip' + sfx,        " hflip,vflip "]                   # 视频顺时针旋转180度 (hflip,vflip)
        self.case010 = [vf_out + '/d010_hflip' + sfx,              " hflip "]                         # 视频水平镜像 (hflip)
        self.case011 = [vf_out + '/d011_vflip' + sfx,              " vflip "]                         # 视频竖直镜像 (vflip)
        # 视频文字跑马灯效果 (drawtext)
        vf_str1= '''drawtext=fontfile=c:/Windows/Fonts/Medium.ttf:text='welcome-hr-left2right':x=90+t*40:y=60:fontsize=36:fontcolor=#ffffff@0.7:shadowy=-1'''
        vf_str2= '''drawtext=fontfile=c:/Windows/Fonts/Medium.ttf:text='welcome-hr-right2left':x=1200-t*40:y=360:fontsize=36:fontcolor=#ffffff@0.7:shadowy=-1'''
        vf_str3= '''drawtext=fontfile=c:/Windows/Fonts/Medium.ttf:text='welcome-ve-up2down':x=100:y=10+t*20:fontsize=36:fontcolor=#ffffff@0.7:shadowy=-1'''
        vf_str4= '''drawtext=fontfile=c:/Windows/Fonts/Medium.ttf:text='welcome-ve-down2up':x=400:y=700-t*20:fontsize=36:fontcolor=#ffffff@0.7:shadowy=-1'''
        vf_marquee_str= vf_str1 + ',' + vf_str2 + ',' + vf_str3 + ',' + vf_str4
        self.case012 = [vf_out + '/d012_drawtext' + sfx, vf_marquee_str]

        self.case013 = [vf_out + '/d013_drawgrid' + sfx,           " drawgrid=width=80:height=80:thickness=2:color=yellow@0.9 "] # 网格
        self.case014 = [vf_out + '/d014_drawbox' + sfx,            " drawbox=x=10:y=10:w=860:h=480:color=pink@0.5:t=fill "] # box框
        self.case015 = [vf_out + '/d015_boxblur' + sfx,            " boxblur=2:1:cr=0:ar=0 "]
        self.case016 = [vf_out + '/d016_edgedetect' + sfx,         " edgedetect=low=0.1:high=0.4 "]
        self.case017 = [vf_out + '/d017_eq' + sfx,                 " eq=contrast=1.5:brightness=0.5 "]
        self.case018 = [vf_out + '/d018_fadein' + sfx,             " fade=in:0:250 "]
        self.case019 = [vf_out + '/d019_fadeout' + sfx,            " fade=out:0:250 "]
        self.case020 = [vf_out + '/d020_geq' + sfx,                " format=gray,geq=lum_expr='(p(X,Y)+(256-p(X-4,Y-4)))/2' "]
        self.case021 = [vf_out + '/d021_histogram' + sfx,          " histogram "]
        self.case022 = [vf_out + '/d022_histeq' + sfx,             " histeq=strength=0.5:intensity=0.4:antibanding=weak "]
        self.case023 = [vf_out + '/d023_hqdn3d' + sfx,             " hqdn3d "]
        self.case024 = [vf_out + '/d024_hue' + sfx,                " hue=h=90:s=1:b=5 "]
        self.case025 = [vf_out + '/d025_il' + sfx,                 " il=l=d:c=d "]
        self.case026 = [vf_out + '/d026_lutyuv' + sfx,             " lutyuv='y=maxval+minval-val:u=maxval+minval-val:v=maxval+minval-val' "]
        self.case027 = [vf_out + '/d027_negate' + sfx,             " negate "]
        self.case028 = [vf_out + '/d028_oscilloscope' + sfx,       " oscilloscope=x=1:y=0.5:s=1:t=1 "]
        self.case029 = [vf_out + '/d029_pad' + sfx,                " pad='iw*1.1:ih*1.1:(ow-iw)/2:(oh-ih)/2:color=violet' "]
        self.case030 = [vf_out + '/d030_showpalette' + sfx,        " showpalette "]
        self.case031 = [vf_out + '/d031_shuffleplanes' + sfx,      " shuffleplanes=0:2:1:3 "]
        self.case032 = [vf_out + '/d032_sobel' + sfx,              " sobel "]
        self.case033 = [vf_out + '/d033_stereo3d' + sfx,           " stereo3d=abl:sbsr "]
        self.case034 = [vf_out + '/d034_swapuv' + sfx,             " swapuv "]
        self.case035 = [vf_out + '/d035_tile' + sfx,               " scale=224:180,tile=4x3:nb_frames=12:padding=2:margin=2 "]# 42 tile 填充参数需注意,确保不造成奇数宽或高
        self.case036 = [vf_out + '/d036_unsharp' + sfx,            " unsharp "]
        self.case037 = [vf_out + '/d037_vignette' + sfx,           " vignette='PI/4+random(1)*PI/50' "] # 闪烁渐晕
        self.case038 = [vf_out + '/d038_delogo' + sfx,             " delogo=w=200:h=80:x=1000:y=50 "]

#################################################################################
# main process
if __name__ == "__main__":
    # init logger
    log = hysmm_def.Logger('sys_all.log',level='debug')
    hysmm_def.Logger('sys_err.log', level='error')
    log.logger.info("========= program start ============")

    # check source and output dir, create output dir if necessary
    hysmm_def.checkDir(log, tt_src_dir)
    hysmm_def.createDirIfNotFound(log, dst_dir)
    hysmm_def.createDirIfNotFound(log, vf_out)

    # init vf case
    vf_case      = vf_case_c()
    vf_case_dict = vf_case.__dict__

    #####################################################
    # overlay image logos(png/bmp/webp/gif/jpeg) , draw text
    #   ffmpeg -fflags +genpts -i main_video.mkv -i logo1.png -i logo2.jpg -max_muxing_queue_size 256
    #          -filter_complex "[0:v][1:v]overlay=40:40[bkg1];[bkg1][2:v]overlay=200:40,"\
    #          "drawtext=fontfile=/fonts/mingliu.ttc:text='hybase@qq.com 视频水印1':x=50:y=450:fontsize=32:fontcolor=0xFFEE00@0.7:shadowy=-1,"\
    #          "drawtext=fontfile=/fonts/mingliu.ttc:text='hybase@qq.com 视频水印2':x=350:y=450:fontsize=32:fontcolor=0xFFEE00@0.7:shadowy=-1"\
    #          -map 0:0 -c:v:0 libx264 -s 1280x720 -b:v:0 1500000 -minrate 1500000 -maxrate 3000000 -bufsize 1500000 -g 25 -bf 1 -pix_fmt yuv420p -r:v:0 25.000000 \
    #          -map 0:1 -c:a:0 aac -ab:a:0 64k -ar:a:0 48000 -ac:a:0 2 -vol:a:0 256 \
    #          -map 0:1 -c:a:1 ac3 -ab:a:1 128k -ar:a:1 48000 -ac:a:1 2 -vol:a:1 256 \
    #          -muxrate 0 -f mpegts d:/output.ts
    tsmux_str   = hysmm_def.tsmux_vbr_str
    png_logo    = ' -i e:/material/image/logo/jxtv_96x96.png '
    jpg_logo    = ' -i e:/material/image/logo/jxtv_96x96.jpg '
    overlay_str = '[0:v][1:v]overlay=60:40[bkg1];[bkg1][2:v]overlay=200:40'
    drawtext1   = '''drawtext=fontfile=c:/Windows/Fonts/msyh.ttf:text='hybase@qq.com视频水印1':x=50:y=550:fontsize=36:fontcolor=0xFFFF00@0.7:shadowy=-1,'''
    drawtext2   = '''drawtext=fontfile=c:/Windows/Fonts/msyh.ttf:text='hybase@qq.com视频水印2':x=50:y=650:fontsize=36:fontcolor=0xFFFF00@0.7:shadowy=-1'''
    video_param = ' -map 0:0 -c:v:0 libx264 -s 1280x720 -b:v:0 1500000 -minrate 1500000 -maxrate 3000000 -bufsize 1500000 -g 25 -bf 1 -pix_fmt yuv420p -r:v:0 25.000000 '
    aac_str     = ' -map 0:1 -c:a:0 aac -ab:a:0 64k -ar:a:0 48000 -ac:a:0 2 -vol:a:0 256 '
    ac3_str     = ' -map 0:1 -c:a:1 ac3 -ab:a:1 128k -ar:a:1 48000 -ac:a:1 2 -vol:a:1 256 '

    common_str = 'ffmpeg -y -fflags +genpts ' + log_param + input_ts_file + png_logo + jpg_logo + ' -max_muxing_queue_size 256 -filter_complex ' + overlay_str
    cmd1 = common_str + video_param + aac_str + ac3_str + tsmux_str + vf_out + '/case000_overlay_logos.ts'
    cmd2 = common_str + ',' + drawtext1 + drawtext2 + video_param + aac_str + ac3_str + tsmux_str + vf_out + '/case001_overlay_logo_drawtext.ts '
    ffmpeg_run.ffmpeg_run('case_vf_overlay_logos'   , overlay_flag, log, cmd1)
    ffmpeg_run.ffmpeg_run('case_vf_overlay_drawtext', overlay_flag, log, cmd2)

    # ***********************************
    # gif or video format string
    video_param_str = ' '
    gif_str    = ' -an -s 1280x720 -r 15 -pix_fmt rgb8 '
    mpegts_str = ' -an -s 1280x720 -r 25 -pix_fmt yuv420p -vcodec libx264 -b 1500k -minrate 1500k -maxrate 2000k -bufsize 1500k -g 25 -bf 2 ' + hysmm_def.tsmux_vbr_str
    if (sfx == '.gif'):
        video_param_str = gif_str
    else:
        video_param_str = mpegts_str
    log.logger.info(video_param_str)

    for i in range(start_num, end_num):
        idx = '%03d' %i
        key = "case" + idx
        input_file  = input_mp4_file
        output_file = vf_case_dict[key][0]

        if (play_flag):
            vf_str = ' -vf ' + vf_case_dict[key][1]
            log.logger.info("key:%s, vf: %s, dst: %s", key, vf_str, output_file)
            cmd = 'ffplay ' + log_param + input_file + vf_str
            ffmpeg_run.ffplay_run(key, True, log, cmd)
        else:
            vf_str = ' -filter_complex ' + vf_case_dict[key][1]
            log.logger.info("key:%s, vf: %s, dst: %s", key, vf_str, output_file)
            cmd = 'ffmpeg -y ' + log_param + input_file + ' -t 10 ' + vf_str  + video_param_str + output_file
            ffmpeg_run.ffmpeg_run(key, True, log, cmd)

    #  frei0r cases please reference to "ffmpeg_frei0r.py"
    ## end of all cases
    log.logger.info("=== all cases run ===")
    log.logger.info("============ program exit ============")
    sys.exit(0)

################################################################################
# end
#

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值