视频太大? 了解一下如何进行压缩 1G --> 100M
2026/9/1 0:22:10 网站建设 项目流程

目录

第一刀:砍分辨率

第二刀:降帧率

第三刀:拉CRF

三个参数怎么组合

进阶:GOP和帧类型

先说结论:
你不懂编码原理也能压视频。懂原理只是为了压得更聪明,不是必须。你需要的就三个数:分辨率、帧率、CRF。调这三个,视频大小随便控。
下面说为什么是这三个,不管你看不看得懂,记住结论拿去用就行。

第一刀:砍分辨率

分辨率就是画面的宽×高。1080p是1920×1080,720p是1280×720。像素数差了55%,同样一段视频,720p的文件大小大约是1080p的一半。

肉眼看区别?手机屏幕上基本分不出来,人眼对分辨率感知有个临界点,视频在动的时候,你能分辨的细节远比静态图片少。一段运动的视频,720p和1080p在6英寸手机屏幕上几乎看不出差别,在电脑显示器上能看出一点,但代价是文件大一倍。

所以别跟你手机原厂的分辨率死磕。录的时候开4K是方便你后期裁剪,但最后输出的时候降到1080p或者720p,省出来的空间足够再存两段视频。

代码实现:用FFmpeg缩分辨率

ffmpeg -i input.mp4 -vf scale=1280:720 output.mp4

scale=1280:720就是缩到720p。如果你只想限制宽度让高度自适应,写成scale=1280:-1,FFmpeg会自动计算高度保持比例。如果你想让高度限制宽度自适应,写scale=-1:720。

代码实现:用Python配合OpenCV缩分辨率

import cv2 cap = cv2.VideoCapture('input.mp4') width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) fps = int(cap.get(cv2.CAP_PROP_FPS)) # 缩到720p scale = 720 / height new_w = int(width * scale) new_h = 720 fourcc = cv2.VideoWriter_fourcc(*'mp4v') out = cv2.VideoWriter('output.mp4', fourcc, fps, (new_w, new_h)) while True: ret, frame = cap.read() if not ret: break resized = cv2.resize(frame, (new_w, new_h), interpolation=cv2.INTER_LANCZOS4) out.write(resized) cap.release() out.release()

INTER_LANCZOS4是OpenCV里质量最高的缩放算法,比默认的线性插值好,速度慢一点但值得。如果你处理的视频多或者机器性能差,用INTER_AREA也行,速度更快。

实操建议: 发微信,720p。传B站,1080p。自己收藏,保留原片。

第二刀:降帧率

帧率就是一秒钟有多少张图。30fps就是一秒30张,60fps就是60张。

电影标准是24fps,所以24帧足够保证流畅,从30降到24,数据量减少20%,视觉上基本没差别。从60降到30,数据直接砍半,动作还是一样的顺滑,因为人眼对30帧以上的敏感度本来就低。

但帧率不是越低越好。降到15fps,画面就开始有卡顿感了,看久了眼睛累。动画类内容降到15fps还能接受,因为动画本身帧率就低。实拍视频最少24fps,运动场景最少30fps,这是底线。

FFmpeg降帧率

ffmpeg -i input.mp4 -r 24 output.mp4

-r 24就是强制输出24fps。FFmpeg会从原视频里均匀抽帧,丢掉多余的帧。如果你不想丢帧只想改播放速度(慢放快放),用-filter:v "setpts=...",那是另一回事。

Python配合OpenCV降帧率

import cv2 cap = cv2.VideoCapture('input.mp4') fps = int(cap.get(cv2.CAP_PROP_FPS)) width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) target_fps = 24 # 计算抽帧间隔:原帧率除以目标帧率 frame_interval = round(fps / target_fps) fourcc = cv2.VideoWriter_fourcc(*'mp4v') out = cv2.VideoWriter('output.mp4', fourcc, target_fps, (width, height)) frame_count = 0 while True: ret, frame = cap.read() if not ret: break # 每隔 frame_interval 帧取一帧 if frame_count % frame_interval == 0: out.write(frame) frame_count += 1 cap.release() out.release()

抽帧的逻辑是取整帧间隔,比如30fps降到24fps,间隔是30/24=1.25,取整就是每1-2帧取一帧。OpenCV没法处理小数帧率,所以实际输出会是24fps近似值。如果你追求精确,用FFmpeg更准。

实操建议: 实拍视频锁24fps,游戏录屏或者快速运动保留30fps,日常记录不需要60。

第三刀:拉CRF

这个最狠,也是最容易被忽略的。

CRF是编码器的质量参数,范围0到51。数值越小画质越好文件越大,数值越大画质越差文件越小。CRF每增加6,文件大小减半。

CRF=18是视觉无损,文件巨大,CRF=23是高质量,专业剪辑用的。CRF=28是标准质量,绝大多数在线视频就这个水平。CRF=35往上是能看但能看出糊,适合做缩略预览。CRF=40以上就是马赛克,除非你只传个草稿确认内容。

为什么CRF每增加6文件就减半?因为编码器里量化参数QP和CRF对应关系,QP每增加6,DCT系数里被清零的高频分量大约多一倍。简单说就是丢弃的细节多一倍,文件自然小一倍。

FFmpeg设CRF

ffmpeg -i input.mp4 -crf 28 -c:v libx264 output.mp4

-crf 28是质量参数,-c:v libx264是编码器。H.264用CRF,H.265(HEVC)也支持CRF,取值范围一样是0-51。想用H.265就把libx264改成libx265,文件还能再小20%-30%,但播放兼容性差一些。

直接调编码器参数(通过FFmpeg-python库)

import ffmpeg input_file = 'input.mp4' output_file = 'output.mp4' ( ffmpeg .input(input_file) .output(output_file, vcodec='libx264', crf=28) .run(overwrite_output=True) )

ffmpeg-python是对FFmpeg命令的Python封装,适合在脚本里集成。如果你不想每次调用命令行,用这个库在代码里控制所有参数。overwrite_output=True是覆盖已有文件,避免每次都要手动确认。

实操建议: 日常压缩无脑设28。画面静态内容多(采访、讲课)可以设30,运动多(球赛、舞蹈)设26。别纠结,先设28试试。

三个参数怎么组合

方案分辨率帧率CRF1分钟文件大小适用场景
微信/钉钉发送720p (1280×720)243015-25 MB聊天发送、邮件附件、快速预览
B站/抖音投稿1080p (1920×1080)302640-60 MB社交媒体发布、平台投稿
自己收藏/备份原片分辨率原片帧率23不固定(画质优先)剪辑素材、家庭记录、归档存储

FFmpeg命令:

ffmpeg -i input.mp4 -vf scale=1280:720 -r 24 -crf 30 -c:v libx264 -preset medium output.mp4

-preset medium是编码速度预设,可选ultrafast、superfast、veryfast、faster、fast、medium、slow、slower、veryslow。速度越快压出来的文件越大画质越差,medium是平衡点。如果你不赶时间,用slow能多压10%-15%的体积。

关于码率

很多人喜欢问码率要设多少。码率就是每秒的数据量,单位是kbps或Mbps。你可以直接设码率,比如-b:v 2M表示每秒最多2Mbit数据

但设码率的问题是你得知道什么内容设多少合适。静态画面2M够用,运动画面2M就糊。所以更推荐用CRF而不是码率,CRF是"质量优先,动态分配码率",画面复杂的地方多用一点,简单的地方少用一点。效果比固定码率好,操作还简单。

如果你一定要设码率,参考值:1080p视频,普通内容2-4Mbps,运动内容4-6Mbps,静态内容1-2Mbps

固定码率模式(设码率为2Mbps)

ffmpeg -i input.mp4 -b:v 2M -maxrate 2M -bufsize 4M output.mp4

-b:v是平均码率-maxrate限制峰值码率,-bufsize是缓冲区大小。固定码率模式下,编码器保证文件大小精确可控,但复杂画面会糊,简单画面浪费带宽。一般只在直播或上传大小被严格限制时用。

这些参数为什么能压这么多

简单给你讲一下原理,知道是怎么回事你用起来更安心。

视频压缩干两件事:第一是砍单帧里的细节,这叫空间压缩。一张图里相邻像素颜色差不多,编码器把图像转换到频率域,高频细节纹理、噪点)直接扔掉,只保留低频(大块颜色区域)。这就是为什么压过的视频看起来柔和不锐利了,因为细节没了。

具体到代码层面,DCT变换把8×8像素块转成频率系数,量化矩阵把高频系数清零。你设的CRF值决定了量化矩阵的尺度因子,CRF越高,被清零的系数越多,文件越小。

第二是跨帧复用数据,这叫时间压缩。这一帧的蓝天上飘着一朵云,下一帧云挪了个位置,编码器不重新存整片天空,只记录云移动的方向和距离背景完全不变的地方直接复制上一帧的数据,不占新空间。

运动估计算法在参考帧里搜索匹配块,算出运动向量(从哪移到哪),然后把当前块减去参考块得到残差。残差通常很小,压缩率高。CRF也影响残差的量化程度,CRF越高残差被砍得越狠,运动区域的细节损失越大。

CRF控制的是第一件事,分辨率影响第二件事的效率。分辨率越高像素越多,需要复用的数据量就越大。帧率决定了每秒要处理多少帧,帧率越高时间压缩的工作量越大。

所以三个参数分别管三个维度:分辨率管总量,帧率管频次,CRF管每一帧的丢弃比例。组合起来,你想压多少就压多少。

进阶:GOP和帧类型

如果你还想再压狠一点,可以调GOP(Group of Pictures)长度。

GOP就是一组连续帧里包含多少个帧,其中第一个是I帧(完整图像),后面是P帧(单向预测)和B帧(双向预测)。I帧最大,P帧中等,B帧最小。GOP越长,I帧占比越小,文件越小。

默认GOP长度一般是250帧(约10秒),你可以拉到500帧甚至更高。但GOP太长有两个问题:一是拖进度条的时候卡(因为解码要从最近的I帧开始),二是画面变化剧烈的地方P帧质量会下降。

调GOP长度

ffmpeg -i input.mp4 -g 300 -c:v libx264 -crf 28 output.mp4

-g 300表示每300帧放一个I帧。如果做视频上传到平台,平台会重新编码你的GOP结构,所以调了也没用。如果是自己存着看或者发微信,GOP拉长能小5%-10%。

音频也得压,很多人只压视频忘了音频。一段视频里音频占的空间可能比你想象的多。AAC编码128kbps的音频,1分钟大约1MB,看起来不多,但如果你设成64kbps,文件再小一半,人耳基本分不出区别。

代码实现:音频压缩

ffmpeg -i input.mp4 -c:v libx264 -crf 28 -c:a aac -b:a 64k output.mp4

-c:a aac是音频编码器,-b:a 64k是音频码率。AAC比MP3效率高,同样码率下音质更好。64kbps适合语音和普通背景音乐,128kbps适合音乐类内容。

如果你不关心音频,甚至可以用-an直接去掉音轨,文件再小一截。

实战脚本:把上面所有参数打包成一个Python脚本:

import os import subprocess def compress_video(input_path, output_path=None, scale=720, fps=24, crf=28, audio_bitrate=64): """ 压缩视频:分辨率+帧率+CRF一次性搞定 """ if output_path is None: base, ext = os.path.splitext(input_path) output_path = f"{base}_compressed{ext}" cmd = [ 'ffmpeg', '-i', input_path, '-vf', f'scale=-1:{scale}', # 高度固定,宽度自适应 '-r', str(fps), '-c:v', 'libx264', '-crf', str(crf), '-preset', 'medium', '-c:a', 'aac', '-b:a', f'{audio_bitrate}k', '-movflags', '+faststart', # 便于网页播放 output_path ] print(f"执行: {' '.join(cmd)}") subprocess.run(cmd, check=True) original = os.path.getsize(input_path) / (1024 * 1024) compressed = os.path.getsize(output_path) / (1024 * 1024) ratio = (1 - compressed / original) * 100 print(f"原始: {original:.1f}MB -> 压缩后: {compressed:.1f}MB") print(f"压缩率: {ratio:.1f}%") # 调用 compress_video('my_video.mp4', scale=720, fps=24, crf=28)

视频压缩不是什么高深技术。分辨率往下降,帧率往下调,CRF往上拉,三个动作做完,文件随便压到原来的20%以下。画质损失?手机上看不出来。真要较真画质你就别压,直接存原片。

核心就一句:用看不到的细节换看得到的空间,这笔买卖不亏。

希望这篇文章能帮到你!如果觉得有用,欢迎点赞、收藏、关注三连!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询