zoukankan      html  css  js  c++  java
  • 用python开发视频压缩器

    第一步:安装PIL库

    cmd--pip install Pillow--enter

    要等许久出现下面就成功了

     接着就可以在pycharm导入包了:from PIL import Image

    第二步:安装ffmpeg

    下载:ffmpeg-20200831-4a11a6f-win64-static.zip

    链接:https://pan.baidu.com/s/17gygYd9FAEeW8vkhADF-Ig
    提取码:29m2
    把下载下来的压缩包解压把bin路径添加到环境变量path里面

     

     接着cmd窗口查询命令ffmpeg -version(如下就配置成功了)

    第三步:弄懂ffmpeg的常见参数

    ffmpeg命令解释
    ffmpeg -i E:\yasuoship\test.mp4 -r 10 -pix_fmt yuv420p -vcodec libx264 -preset veryslow -profile:v baseline -crf 23 -acodec aac -b:a 32k -strict -5 E:\yasuoship\test1.mp4
    -i 输入的视频文件
    -r 每一秒的帧数,一秒10帧大概就是人眼的速度
    -pix_fmt 设置视频颜色空间 yuv420p网络传输用的颜色空间 ffmpeg -pix_fmts可以查看有哪些颜色空间选择
    -vcodec 软件编码器,libx264通用稳定
    -preset 编码机预设 编码机预设越高占用CPU越大 有十个参数可选 ultrafast superfast veryfast(录制视频选用) faster fast medium(默认) slow slower veryslow(压制视频时一般选用) pacebo
    -profile:v 压缩比的配置 越往左边压缩的越厉害,体积越小 baseline(实时通信领域一般选用,画面损失越大) Extended Main(流媒体选用) High(超清视频) High 10 High 4:2:2 High 4:4:4(Predictive)
    -level:v 对编码机的规范和限制针对不通的使用场景来操作,也就是不同分辨率设置不同的值(这个我没有设置,因为这个要根据不同的分辨率进行设置的,具体要去官方文档查看)
    -crf 码率控制模式 用于对画面有要求,对文件大小无关紧要的场景 0-51都可以选择 0为无损 一般设置18 - 28之间 大于28画面损失严重
    -acodec 设置音频编码器

    尝试在命令行执行以下命令等待五分钟之后出现了压缩好的视频:

    ffmpeg -i E:\yasuoship\test.mp4 -s 1920x1080 -c:v libx265 -c:a aac -b:v 200k -r 25 E:\yasuoship\test1.mp4

    第四步ffmpeg常见用法(转自https://www.cnblogs.com/frost-yen/p/5848781.html)

    [FFmpeg] ffmpeg 常用命令

    1. 视频转换

    比如一个avi文件,想转为mp4,或者一个mp4想转为ts。
    ffmpeg -i input.avi output.mp4
    ffmpeg -i input.mp4 output.ts

    2. 提取音频

    ffmpeg -i test.mp4 -acodec copy -vn output.aac
    上面的命令,默认mp4的audio codec是aac,如果不是,可以都转为最常见的aac。
    ffmpeg -i test.mp4 -acodec aac -vn output.aac

    3. 提取视频

    ffmpeg -i input.mp4 -vcodec copy -an output.mp4

    4. 视频剪切

    下面的命令,可以从时间为00:00:15开始,截取5秒钟的视频。
    ffmpeg -ss 00:00:15 -t 00:00:05 -i input.mp4 -vcodec copy -acodec copy output.mp4
    -ss表示开始切割的时间,-t表示要切多少。上面就是从15秒开始,切5秒钟出来。

    5. 码率控制

    码率控制对于在线视频比较重要。因为在线视频需要考虑其能提供的带宽。

    那么,什么是码率?很简单:
    bitrate = file size / duration
    比如一个文件20.8M,时长1分钟,那么,码率就是:
    biterate = 20.8M bit/60s = 20.8*1024*1024*8 bit/60s= 2831Kbps
    一般音频的码率只有固定几种,比如是128Kbps,
    那么,video的就是
    video biterate = 2831Kbps -128Kbps = 2703Kbps。

    那么ffmpeg如何控制码率。
    ffmpg控制码率有3种选择,-minrate -b:v -maxrate
    -b:v主要是控制平均码率。
    比如一个视频源的码率太高了,有10Mbps,文件太大,想把文件弄小一点,但是又不破坏分辨率。
    ffmpeg -i input.mp4 -b:v 2000k output.mp4
    上面把码率从原码率转成2Mbps码率,这样其实也间接让文件变小了。目测接近一半。
    不过,ffmpeg官方wiki比较建议,设置b:v时,同时加上 -bufsize
    -bufsize 用于设置码率控制缓冲器的大小,设置的好处是,让整体的码率更趋近于希望的值,减少波动。(简单来说,比如1 2的平均值是1.5, 1.49 1.51 也是1.5, 当然是第二种比较好)
    ffmpeg -i input.mp4 -b:v 2000k -bufsize 2000k output.mp4

    -minrate -maxrate就简单了,在线视频有时候,希望码率波动,不要超过一个阈值,可以设置maxrate。
    ffmpeg -i input.mp4 -b:v 2000k -bufsize 2000k -maxrate 2500k output.mp4

    6. 视频编码格式转换

    比如一个视频的编码是MPEG4,想用H264编码,咋办?
    ffmpeg -i input.mp4 -vcodec h264 output.mp4
    相反也一样
    ffmpeg -i input.mp4 -vcodec mpeg4 output.mp4

    当然了,如果ffmpeg当时编译时,添加了外部的x265或者X264,那也可以用外部的编码器来编码。(不知道什么是X265,可以 Google一下,简单的说,就是她不包含在ffmpeg的源码里,是独立的一个开源代码,用于编码HEVC,ffmpeg编码时可以调用它。当然 了,ffmpeg自己也有编码器)
    ffmpeg -i input.mp4 -c:v libx265 output.mp4
    ffmpeg -i input.mp4 -c:v libx264 output.mp4

    7. 只提取视频ES数据

    ffmpeg –i input.mp4 –vcodec copy –an –f m4v output.h264

    8. 过滤器的使用

    8.1 将输入的1920x1080缩小到960x540输出:

    ffmpeg -i input.mp4 -vf scale=960:540 output.mp4
    //ps: 如果540不写,写成-1,即scale=960:-1, 那也是可以的,ffmpeg会通知缩放滤镜在输出时保持原始的宽高比。

    比如,我有这么一个图片
    iqiyi logo
    想要贴到一个视频上,那可以用如下命令:
    ./ffmpeg -i input.mp4 -i iQIYI_logo.png -filter_complex overlay output.mp4
    结果如下所示:
    add logo left
    要贴到其他地方?看下面:
    右上角:
    ./ffmpeg -i input.mp4 -i logo.png -filter_complex overlay=W-w output.mp4
    左下角:
    ./ffmpeg -i input.mp4 -i logo.png -filter_complex overlay=0:H-h output.mp4
    右下角:
    ./ffmpeg -i input.mp4 -i logo.png -filter_complex overlay=W-w:H-h output.mp4

    语法:-vf delogo=x:y:w:h[:t[:show]]
    x:y 离左上角的坐标
    w:h logo的宽和高
    t: 矩形边缘的厚度默认值4
    show:若设置为1有一个绿色的矩形,默认值0。

    ffmpeg -i input.mp4 -vf delogo=0:0:220:90:100:1 output.mp4
    结果如下所示:
    de logo

    9. 截取视频图像

    ffmpeg -i input.mp4 -r 1 -q:v 2 -f image2 pic-%03d.jpeg
    -r 表示每一秒几帧
    -q:v表示存储jpeg的图像质量,一般2是高质量。
    如此,ffmpeg会把input.mp4,每隔一秒,存一张图片下来。假设有60s,那会有60张。


    可以设置开始的时间,和你想要截取的时间。
    ffmpeg -i input.mp4 -ss 00:00:20 -t 10 -r 1 -q:v 2 -f image2 pic-%03d.jpeg
    -ss 表示开始时间
    -t 表示共要多少时间。
    如此,ffmpeg会从input.mp4的第20s时间开始,往下10s,即20~30s这10秒钟之间,每隔1s就抓一帧,总共会抓10帧。

    10. 序列帧与视频的相互转换

    把darkdoor.[001-100].jpg序列帧和001.mp3音频文件利用mpeg4编码方式合成视频文件darkdoor.avi:
    $ ffmpeg -i 001.mp3 -i darkdoor.%3d.jpg -s 1024x768 -author fy -vcodec mpeg4 darkdoor.avi

    还可以把视频文件导出成jpg序列帧:
    $ ffmpeg -i bc-cinematic-en.avi example.%d.jpg


    其他用法

    1.输出YUV420原始数据

    对于一下做底层编解码的人来说,有时候常要提取视频的YUV原始数据,如下:
    ffmpeg -i input.mp4 output.yuv

    那如果我只想要抽取某一帧YUV呢?
    你先用上面的方法,先抽出jpeg图片,然后把jpeg转为YUV。
    比如:
    你先抽取10帧图片。
    ffmpeg -i input.mp4 -ss 00:00:20 -t 10 -r 1 -q:v 2 -f image2 pic-%03d.jpeg

    然后,你就随便挑一张,转为YUV:
    ffmpeg -i pic-001.jpeg -s 1440x1440 -pix_fmt yuv420p xxx3.yuv
    如果-s参数不写,则输出大小与输入一样。

    当然了,YUV还有yuv422p啥的,你在-pix_fmt 换成yuv422p就行啦!

    2. H264编码profile & level控制

    背景知识

    先科普一下profile&level。(这里讨论最常用的H264)
    H.264有四种画质级别,分别是baseline, extended, main, high:
      1、Baseline Profile:基本画质。支持I/P 帧,只支持无交错(Progressive)和CAVLC;
      2、Extended profile:进阶画质。支持I/P/B/SP/SI 帧,只支持无交错(Progressive)和CAVLC;(用的少)
      3、Main profile:主流画质。提供I/P/B 帧,支持无交错(Progressive)和交错(Interlaced),
        也支持CAVLC 和CABAC 的支持;
      4、High profile:高级画质。在main Profile 的基础上增加了8x8内部预测、自定义量化、 无损视频编码和更多的YUV 格式;
    H.264 Baseline profile、Extended profile和Main profile都是针对8位样本数据、4:2:0格式(YUV)的视频序列。在相同配置情况下,High profile(HP)可以比Main profile(MP)降低10%的码率。
    根据应用领域的不同,Baseline profile多应用于实时通信领域,Main profile多应用于流媒体领域,High profile则多应用于广电和存储领域。

    下图清楚的给出不同的profile&level的性能区别。
    profile
    这里写图片描述

    level
    这里写图片描述

    2.1 ffmpeg如何控制profile&level

    举3个例子吧
    ffmpeg -i input.mp4 -profile:v baseline -level 3.0 output.mp4

    ffmpeg -i input.mp4 -profile:v main -level 4.2 output.mp4

    ffmpeg -i input.mp4 -profile:v high -level 5.1 output.mp4

    如果ffmpeg编译时加了external的libx264,那就这么写:
    ffmpeg -i input.mp4 -c:v libx264 -x264-params "profile=high:level=3.0" output.mp4

    从压缩比例来说,baseline< main < high,对于带宽比较局限的在线视频,可能会选择high,但有些时候,做个小视频,希望所有的设备基本都能解码(有些低端设备或早期的设备只能解码 baseline),那就牺牲文件大小吧,用baseline。自己取舍吧!

    苹果的设备对不同profile的支持。
    这里写图片描述

    2.2. 编码效率和视频质量的取舍(preset, crf)

    除了上面提到的,强行配置biterate,或者强行配置profile/level,还有2个参数可以控制编码效率。
    一个是preset,一个是crf。
    preset也挺粗暴,基本原则就是,如果你觉得编码太快或太慢了,想改改,可以用profile。
    preset有如下参数可用:

    ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow and placebo.
    编码加快,意味着信息丢失越严重,输出图像质量越差。

    CRF(Constant Rate Factor): 范围 0-51: 0是编码毫无丢失信息, 23 is 默认, 51 是最差的情况。相对合理的区间是18-28.
    值越大,压缩效率越高,但也意味着信息丢失越严重,输出图像质量越差。

    举个例子吧。
    ffmpeg -i input -c:v libx264 -profile:v main -preset:v fast -level 3.1 -x264opts crf=18
    (参考自:https://trac.ffmpeg.org/wiki/Encode/H.264)

    2.3. H265 (HEVC)编码tile&level控制

    背景知识

    和H264的profile&level一样,为了应对不同应用的需求,HEVC制定了“层级”(tier) 和“等级”(level)。
    tier只有main和high。
    level有13级,如下所示:

    不多说,直接给出怎么用。(supposed你用libx265编码)
    ffmpeg -i input.mp4 -c:v libx265 -x265-params "profile=high:level=3.0" output.mp4

    第五步使用python实现压缩功能

    代码如下:可以扩展批量读取和批量压缩,图片压缩,格式转换等等,这个请发挥哦!

    import sys
    import os
    import zlib
    import threading
    import platform
    from PIL import Image
    
    
    
    def SaveVideo(self):
        # fpsize = os.path.getsize(self.fileInputPath) / 1024
        fpsize = os.path.getsize(r'E:\yasuoship\test.mp4') / 1024
        if fpsize >= 150.0:  # 大于150KB的视频需要压缩
            if self.outName:
                compress = "ffmpeg -i {} -r 10 -pix_fmt yuv420p -vcodec libx264 -preset veryslow -profile:v baseline  -crf 23 -acodec aac -b:a 32k -strict -5 {}".format(
                    self.fileInputPath, self.fileOutPath)
                isRun = os.system(compress)
            else:
                compress = "ffmpeg -i {} -r 10 -pix_fmt yuv420p -vcodec libx264 -preset veryslow -profile:v baseline  -crf 23 -acodec aac -b:a 32k -strict -5 {}".format(
                    self.fileInputPath, self.fileInputPath)
                isRun = os.system(compress)
            if isRun != 0:
                return (isRun, "没有安装ffmpeg")
            return True
        else:
            return True
    
    
    if __name__ == "__main__":
        #判断文件有多少兆,getsize返回文件大小kb除以1024为K大小,再除以1024等于兆M
        fpsize1 = os.path.getsize(r'E:\yasuoship\test.mp4')/1024/1024
        print(fpsize1)
        #命令行执行成功
        compress = r'ffmpeg -i E:\yasuoship\test1.mp4 -s 1920x1080 -c:v libx265 -c:a aac -b:v 200k -r 25 E:\yasuoship\test3.mp4'
        isRun = os.system(compress)
        print(isRun)

    小贴士:这里必需把ffmpeg的bin里面三个文件拉到项目根目录,否则程序运行不了

    第七步:合并多个视频

    FFMpeg无损合并视频的多种方法(转自https://www.jianshu.com/p/a9bccc12229b)

    众所周知,从某些视频网站下载的视频是分段的。比如新浪视频每隔6分钟分段,俗称“6分钟诅咒”。

    现在的任务是将这些视频片段合并起来,并且尽量无损。

    方法一:FFmpeg concat 协议

    对于 MPEG 格式的视频,可以直接连接:

    ffmpeg -i "concat:input1.mpg|input2.mpg|input3.mpg" -c copy output.mpg
    

    对于非 MPEG 格式容器,但是是 MPEG 编码器(H.264、DivX、XviD、MPEG4、MPEG2、AAC、MP2、MP3 等),可以包装进 TS 格式的容器再合并。在新浪视频,有很多视频使用 H.264 编码器,可以采用这个方法

    ffmpeg -i input1.flv -c copy -bsf:v h264_mp4toannexb -f mpegts input1.ts
    ffmpeg -i input2.flv -c copy -bsf:v h264_mp4toannexb -f mpegts input2.ts
    ffmpeg -i input3.flv -c copy -bsf:v h264_mp4toannexb -f mpegts input3.ts
    ffmpeg -i "concat:input1.ts|input2.ts|input3.ts" -c copy -bsf:a aac_adtstoasc -movflags +faststart output.mp4
    

    保存 QuickTime/MP4 格式容器的时候,建议加上 -movflags +faststart。这样分享文件给别人的时候可以边下边看。

    方法二:FFmpeg concat 分离器

    这种方法成功率很高,也是最好的,但是需要 FFmpeg 1.1 以上版本。先创建一个文本文件filelist.txt:

    file 'input1.mkv'
    file 'input2.mkv'
    file 'input3.mkv'

    然后:

    ffmpeg -f concat -i filelist.txt -c copy output.mkv

    注意:使用 FFmpeg concat 分离器时,如果文件名有奇怪的字符,要在 filelist.txt中转义。

    方法三:Mencoder 连接文件并重建索引

    这种方法只对很少的视频格式生效。幸运的是,新浪视频使用的 FLV 格式是可以这样连接的。对于没有使用 MPEG 编码器的视频(如 FLV1 编码器),可以尝试这种方法,或许能够成功。

    mencoder -forceidx -of lavf -oac copy -ovc copy -o output.flv input1.flv input2.flv input3.flv

    方法四:使用 FFmpeg concat 过滤器重新编码(有损)

    语法有点复杂,但是其实不难。这个方法可以合并不同编码器的视频片段,也可以作为其他方法失效的后备措施。

    ffmpeg -i input1.mp4 -i input2.webm -i input3.avi -filter_complex '[0:0] [0:1] [1:0] [1:1] [2:0] [2:1] concat=n=3:v=1:a=1 [v] [a]' -map '[v]' -map '[a]' <编码器选项> output.mkv

    如你所见,上面的命令合并了三种不同格式的文件,FFmpeg concat 过滤器会重新编码它们。注意这是有损压缩。

    [0:0] [0:1] [1:0] [1:1] [2:0] [2:1分别表示第一个输入文件的视频、音频、第二个输入文件的视频、音频、第三个输入文件的视频、音频。concat=n=3:v=1:a=1表示有三个输入文件,输出一条视频流和一条音频流。[v] [a] 就是得到的视频流和音频流的名字,注意在 bash 等 shell 中需要用引号,防止通配符扩展。

    提示

    1. 以上三种方法,在可能的情况下,最好使用第二种。第一种次之,第三种更次。第四种是后备方案,尽量避免。
    2. 规格不同的视频合并后可能会有无法预测的结果。
    3. 有些媒体需要先分离视频和音频,合并完成后再封装回去。
    4. 对于 Packed B-Frames 的视频,如果封装成 MKV 格式的时候提示 -fflags +genpts</tt>](https://trac.ffmpeg.org/ticket/1552 "1552 (Packed B-frames cannot be remuxed into mkv) – FFmpeg")

    ==========实战===========

    下面要压缩这圈圈两个文件,

     filelist这样写

    命令行:ffmpeg -f concat -i C:\Users\Administrator\Desktop\testys\filelist.txt -c copy C:\Users\Administrator\Desktop\testys\output.mp4

    执行报错了

     报错信息:

    [concat @ 00000000004d4bc0] Unsafe file name 'C:\Users\Administrator\Desktop\tes
    tys\5sessionys.mp4'
    C:\Users\Administrator\Desktop\testys\filelist.txt: Operation not permitted

    解决方案:

     在命令行中添加 -safe 参数来避免这个问题

    正确写法:ffmpeg -f concat -safe 0 -i C:\Users\Administrator\Desktop\testys\filelist.txt -c copy C:\Users\Administrator\Desktop\testys\output.mp4

    错误写法:ffmpeg -f concat -i C:\Users\Administrator\Desktop\testys\filelist.txt -c copy C:\Users\Administrator\Desktop\testys\output.mp4

    修改之后执行成功如下:

    作者:Elaine
    交流QQ:392989505
  • 相关阅读:
    27. Remove Element
    列表变成字典
    1. Two Sum
    CVPR2019:What and How Well You Performed? A Multitask Learning Approach to Action Quality Assessment
    959. Regions Cut By Slashes
    118. Pascal's Triangle
    loj3117 IOI2017 接线 wiring 题解
    题解 NOI2019 序列
    题解 省选联考2020 组合数问题
    题解 Educational Codeforces Round 90 (Rated for Div. 2) (CF1373)
  • 原文地址:https://www.cnblogs.com/jameswohu/p/15774345.html
Copyright © 2011-2022 走看看