网站首页
手机版

音视频大白话系列编码篇11为什么要压缩

更新时间:作者:小小条

为什么要压缩?1小时视频为什么不是几TB?

用大白话讲清楚音视频压缩的必要性和基本原理

从一个问题开始

你有没有想过这样的问题:

为什么1小时的4K电影只有几GB,而不是几TB?为什么同样的视频,不同格式文件大小差别这么大?为什么压缩后的视频看起来和原来差不多?为什么有些压缩是"无损"的,有些是"有损"的?

今天我们就来聊聊音视频压缩这个神奇的技术,看看它是如何让庞大的数据变得"苗条"的。

音视频大白话系列编码篇11为什么要压缩


回顾基础知识

在讲压缩技术之前,先回顾一下我们需要的基础知识:

数字化过程:音视频转换为数字信号 文件大小计算:分辨率×帧率×位深度×时长 人眼人耳特性:感知的局限性 数据冗余:重复和不必要的信息

如果你还不熟悉这些概念,建议先看: 音视频大白话系列-05-视频参数


为什么需要压缩?

大白话解释

想象你要搬家,所有东西都要装进卡车:

不压缩:每样东西都用最大的盒子装,卡车装不下压缩:把衣服压缩袋抽真空,书本叠整齐,空间利用最大化智能压缩:重要的东西(珠宝)小心包装,不重要的(旧报纸)随便塞

音视频压缩就是这样:去掉不必要的信息,保留重要的内容,让文件变小但质量不会明显下降。

技术原理

未压缩视频的恐怖大小:

1小时4K视频未压缩大小计算:分辨率:3840×2160 = 829万像素帧率:30fps色彩:24位RGB (每像素3字节)时长:3600秒大小 = 829万 × 30 × 3 × 3600 = 2.68TB!

压缩的必要性:

存储限制:硬盘装不下传输限制:网络传不动成本考虑:存储和带宽都要钱实用性:谁也不想等半天下载一个视频

深入理解

压缩效果对比:未压缩4K视频(1小时):2.68TBH.264压缩后: 8GB (压缩比 340:1)H.265压缩后: 4GB (压缩比 680:1)AV1压缩后: 3GB (压缩比 900:1)压缩率 = 原始大小 / 压缩后大小


代码示例

基础示例:压缩原理演示

#include <iostream>#include <vector>#include <string>#include <map>#include <algorithm>// 简单的数据压缩演示class CompressionDemo {public: // 演示数据冗余 static void demonstrateRedundancy() { std::cout << "=== 数据冗余演示 ===" << std::endl; // 原始数据(很多重复) std::string original = "AAAAAABBBBBBCCCCCCDDDDDDEEEEEE"; std::cout << "原始数据: " << original << std::endl; std::cout << "原始大小: " << original.length() << " 字符" << std::endl; // 简单的行程编码压缩 std::string compressed = runLengthEncode(original); std::cout << "压缩后: " << compressed << std::endl; std::cout << "压缩大小: " << compressed.length() << " 字符" << std::endl; std::cout << "压缩比: " << (double)original.length() / compressed.length() << ":1" << std::endl; } // 演示视频帧间冗余 static void demonstrateFrameRedundancy() { std::cout << "\n=== 视频帧间冗余演示 ===" << std::endl; // 模拟连续的视频帧(大部分内容相同) std::vector<std::string> frames = { "背景山脉,蓝天白云,小鸟在左上角", "背景山脉,蓝天白云,小鸟在左中", "背景山脉,蓝天白云,小鸟在左下角", "背景山脉,蓝天白云,小鸟在中上角", "背景山脉,蓝天白云,小鸟在中心" }; // 计算未压缩大小 int totalSize = 0; for (const auto& frame : frames) { totalSize += frame.length(); } std::cout << "5帧未压缩总大小: " << totalSize << " 字符" << std::endl; // 模拟帧间压缩:只存储变化的部分 std::cout << "帧间压缩后:" << std::endl; std::cout << "帧1(关键帧): " << frames[0] << std::endl; int compressedSize = frames[0].length(); for (int i = 1; i < frames.size(); i++) { std::string diff = "小鸟位置变化"; // 只存储变化 std::cout << "帧" << (i+1) << "(差分): " << diff << std::endl; compressedSize += diff.length(); } std::cout << "压缩后总大小: " << compressedSize << " 字符" << std::endl; std::cout << "压缩比: " << (double)totalSize / compressedSize << ":1" << std::endl; } // 演示感知压缩 static void demonstratePerceptualCompression() { std::cout << "\n=== 感知压缩演示 ===" << std::endl; // 模拟音频频率数据 std::vector<int> audioFrequencies = { 20, // 人耳刚能听到 440, // 中频,很敏感 1000, // 中频,很敏感 8000, // 高频,较敏感 18000, // 超高频,不太敏感 22000 // 超高频,几乎听不到 }; std::vector<int> amplitudes = {50, 100, 100, 80, 30, 10}; std::cout << "原始音频频谱:" << std::endl; int originalData = 0; for (int i = 0; i < audioFrequencies.size(); i++) { std::cout << audioFrequencies[i] << "Hz: " << amplitudes[i] << "dB" << std::endl; originalData += amplitudes[i]; } // 感知压缩:去掉人耳不敏感的频率 std::cout << "\n感知压缩后:" << std::endl; int compressedData = 0; for (int i = 0; i < audioFrequencies.size(); i++) { if (audioFrequencies[i] >= 20 && audioFrequencies[i] <= 16000) { std::cout << audioFrequencies[i] << "Hz: " << amplitudes[i] << "dB (保留)" << std::endl; compressedData += amplitudes[i]; } else { std::cout << audioFrequencies[i] << "Hz: 0dB (丢弃)" << std::endl; } } std::cout << "数据量减少: " << (double)(originalData - compressedData) / originalData * 100 << "%" << std::endl; std::cout << "听觉质量: 几乎无损失" << std::endl; }private: // 简单的行程编码 static std::string runLengthEncode(const std::string& input) { std::string result; char current = input[0]; int count = 1; for (int i = 1; i < input.length(); i++) { if (input[i] == current) { count++; } else { result += current + std::to_string(count); current = input[i]; count = 1; } } result += current + std::to_string(count); return result; }};// 压缩类型分析器class CompressionAnalyzer {public: // 分析不同压缩类型 static void analyzeCompressionTypes() { std::cout << "\n=== 压缩类型分析 ===" << std::endl; struct CompressionType { std::string name; std::string principle; std::string advantage; std::string disadvantage; std::string example; }; CompressionType types[] = { { "无损压缩", "去除冗余,完全可恢复", "质量无损失", "压缩比有限", "FLAC音频, PNG图像" }, { "有损压缩", "去除人眼/耳不敏感信息", "压缩比很高", "质量有损失", "MP3音频, JPEG图像" }, { "帧内压缩", "压缩单个帧内的冗余", "每帧独立,便于编辑", "压缩比相对较低", "MJPEG视频" }, { "帧间压缩", "利用帧与帧间的相似性", "压缩比很高", "帧间有依赖关系", "H.264, H.265视频" } }; for (const auto& type : types) { std::cout << "\n" << type.name << ":" << std::endl; std::cout << " 原理: " << type.principle << std::endl; std::cout << " 优点: " << type.advantage << std::endl; std::cout << " 缺点: " << type.disadvantage << std::endl; std::cout << " 例子: " << type.example << std::endl; } } // 计算不同场景的存储需求 static void calculateStorageNeeds() { std::cout << "\n=== 存储需求计算 ===" << std::endl; struct VideoScenario { std::string name; int width, height; double fps; int duration_hours; }; VideoScenario scenarios[] = { {"手机录制", 1920, 1080, 30, 1}, {"4K电影", 3840, 2160, 24, 2}, {"监控录像", 1280, 720, 15, 24}, {"直播存档", 1920, 1080, 60, 4} }; for (const auto& scenario : scenarios) { // 计算未压缩大小 long long pixels_per_second = (long long)scenario.width * scenario.height * scenario.fps; long long total_seconds = scenario.duration_hours * 3600; long long total_bytes = pixels_per_second * total_seconds * 3; // RGB double uncompressed_gb = total_bytes / 1024.0 / 1024.0 / 1024.0; // 估算压缩后大小 double h264_gb = uncompressed_gb / 200; // H.264压缩比约200:1 double h265_gb = uncompressed_gb / 400; // H.265压缩比约400:1 std::cout << "\n" << scenario.name << " (" << scenario.width << "x" << scenario.height << ", " << scenario.fps << "fps, " << scenario.duration_hours << "小时):" << std::endl; std::cout << " 未压缩: " << std::fixed << std::setprecision(1) << uncompressed_gb << " GB" << std::endl; std::cout << " H.264压缩: " << h264_gb << " GB" << std::endl; std::cout << " H.265压缩: " << h265_gb << " GB" << std::endl; } }};int main() { std::cout << "=== 音视频压缩原理演示 ===" << std::endl; // 演示各种压缩原理 CompressionDemo::demonstrateRedundancy(); CompressionDemo::demonstrateFrameRedundancy(); CompressionDemo::demonstratePerceptualCompression(); // 分析压缩类型 CompressionAnalyzer::analyzeCompressionTypes(); // 计算存储需求 CompressionAnalyzer::calculateStorageNeeds(); std::cout << "\n=== 压缩技术总结 ===" << std::endl; std::cout << "1. 数据冗余消除:去除重复信息" << std::endl; std::cout << "2. 感知压缩:利用人眼人耳的局限性" << std::endl; std::cout << "3. 帧间预测:利用视频帧的相似性" << std::endl; std::cout << "4. 变换编码:将数据转换到更易压缩的域" << std::endl; std::cout << "5. 量化:减少数据精度换取压缩比" << std::endl; return 0;}

编译运行:

g++ -o compression_demo compression_demo.cpp./compression_demo


实战应用

常见场景

场景1:选择压缩策略

// 不同应用场景的压缩选择struct CompressionStrategy { std::string scenario; std::string compression_type; std::string reason; double typical_ratio;};CompressionStrategy strategies[] = { {"网络视频", "H.264有损压缩", "平衡质量和传输速度", 200}, {"专业存档", "无损或轻度有损", "保证质量用于后期制作", 50}, {"移动设备", "高压缩比有损", "节省存储空间和流量", 400}, {"实时直播", "快速有损压缩", "减少编码延迟", 150}};

场景2:压缩参数调整

问题:如何在质量和文件大小间平衡?解决方案:高质量需求:降低压缩比,增大文件存储限制:提高压缩比,接受质量损失网络传输:根据带宽动态调整

场景3:压缩格式选择

问题:什么时候用有损,什么时候用无损?答案:最终观看:有损压缩(MP4/H.264)专业制作:无损压缩(ProRes/DNxHD)存档保存:无损或轻度有损

常见误区

❌ 误区1:压缩一定会降低质量

错误理解: "压缩过的视频质量肯定不如原来"

正确理解:

无损压缩:质量完全不变有损压缩:在人眼察觉阈值内,主观质量几乎无损合理的有损压缩可以在大幅减小文件的同时保持良好质量

❌ 误区2:压缩比越高越好

错误理解: "压缩比1000:1比100:1好"

正确理解:

压缩比过高会导致明显的质量损失要在压缩比和质量间找平衡不同内容的最佳压缩比不同

❌ 误区3:所有视频都能压缩到很小

错误理解: "任何视频都能压缩到几MB"

正确理解:

压缩效果取决于内容复杂度静态场景:压缩效果好快速运动、噪点多:压缩效果差已经压缩过的内容:再压缩效果有限

性能和优化

性能考虑

⚡ 编码速度:实时应用需要快速压缩 内存使用:压缩算法的内存需求 质量控制:在质量和大小间找平衡 硬件加速:利用专用硬件提高效率

优化策略

内容自适应压缩:double chooseCompressionRatio(std::string content_type) { if (content_type == "静态画面") return 500; // 高压缩比 else if (content_type == "慢动作") return 300; else if (content_type == "快动作") return 150; else if (content_type == "噪点多") return 100; // 低压缩比 else return 200; // 默认 }多级压缩策略:第一级:快速粗压缩第二级:精细优化压缩自适应:根据内容选择策略

实战练*

练*1:计算压缩收益

要求: 计算以下场景使用压缩技术的收益:

一个视频网站,每天上传1000小时1080p视频不压缩 vs H.264压缩的存储成本差异网络传输成本差异

参考答案:

点击查看答案

#include <iostream>void calculateCompressionBenefits() { // 基础参数 int daily_hours = 1000; int width = 1920, height = 1080; double fps = 30; // 计算每小时未压缩大小 long long pixels_per_hour = (long long)width * height * fps * 3600; double uncompressed_gb_per_hour = pixels_per_hour * 3.0 / 1024 / 1024 / 1024; // 每天的数据量 double daily_uncompressed = daily_hours * uncompressed_gb_per_hour; double daily_h264 = daily_uncompressed / 200; // H.264压缩比200:1 // 年度数据量 double yearly_uncompressed = daily_uncompressed * 365; double yearly_h264 = daily_h264 * 365; // 成本计算(假设存储成本0.1元/GB/月,带宽成本1元/GB) double storage_cost_uncompressed = yearly_uncompressed * 0.1 * 12; double storage_cost_h264 = yearly_h264 * 0.1 * 12; double bandwidth_cost_uncompressed = yearly_uncompressed * 1.0; double bandwidth_cost_h264 = yearly_h264 * 1.0; std::cout << "视频网站压缩收益分析:" << std::endl; std::cout << "每日上传:" << daily_hours << "小时1080p视频" << std::endl; std::cout << "\n存储需求:" << std::endl; std::cout << " 未压缩:" << yearly_uncompressed / 1024 << " TB/年" << std::endl; std::cout << " H.264压缩:" << yearly_h264 / 1024 << " TB/年" << std::endl; std::cout << " 节省存储:" << (yearly_uncompressed - yearly_h264) / 1024 << " TB/年" << std::endl; std::cout << "\n成本对比:" << std::endl; std::cout << " 存储成本节省:" << (storage_cost_uncompressed - storage_cost_h264) / 10000 << " 万元/年" << std::endl; std::cout << " 带宽成本节省:" << (bandwidth_cost_uncompressed - bandwidth_cost_h264) / 10000 << " 万元/年" << std::endl; std::cout << " 总计节省:" << ((storage_cost_uncompressed + bandwidth_cost_uncompressed) - (storage_cost_h264 + bandwidth_cost_h264)) / 10000 << " 万元/年" << std::endl;}

练*2:理解压缩权衡

思考题: 为什么直播平台通常使用较高的压缩比,而电影制作使用较低的压缩比?

答案:

直播:实时性要求高,网络带宽有限,观众对质量要求相对较低电影:质量要求极高,有充足的制作时间,需要保留后期制作的空间应用场景决定了压缩策略的选择

本文要点回顾

✨ 压缩必要性:未压缩的音视频文件大得无法实用✨ 压缩原理:去除冗余信息和人眼人耳不敏感的信息✨ 压缩类型:无损vs有损,帧内vs帧间✨ 压缩效果:现代编码可达到数百倍的压缩比✨ 应用导向:根据具体需求选择合适的压缩策略

实用建议

开发建议

理解不同压缩技术的原理和适用场景 根据应用需求选择合适的压缩参数 考虑压缩的计算成本和质量损失 利用硬件加速提高压缩效率

学*建议

实际体验不同压缩参数的效果 了解人眼人耳的感知特性 学*信息论和数据压缩的基本原理 关注新的压缩技术发展

扩展阅读

信息论基础:香农定理和压缩的理论极限感知编码:心理声学和心理视觉模型变换编码:DCT、小波变换等数学工具熵编码:霍夫曼编码、算术编码等无损压缩

下期预告:H.264编码原理:帧内预测和帧间预测。我们将深入讲解最流行的视频编码标准H.264的工作原理。


互动时间

思考题:

如果网络带宽无限大,我们还需要压缩技术吗?为什么音频压缩比通常比视频压缩比小?

实践建议:

用不同的压缩参数编码同一个视频,比较文件大小和质量观察不同类型内容(静态vs动态)的压缩效果差异了解你常用的视频平台使用什么压缩技术

如果本文对你有帮助,欢迎:

点赞支持 关注不迷路 评论区讨论⭐ 收藏慢慢看

本文为"音视频大白话"系列第 11 篇

版权声明:本文转载于今日头条,版权归作者所有,如果侵权,请联系本站编辑删除

为您推荐

Python基础之数字(Number)超级详解

来源:AI入门学习作者:小伍哥Python中有6个标准的数据类型:Number(数字)、String(字符串)、List(列表)、Tuple(元组)、Set(集合)、Dictionary(字典),每种类型有其固有的属性和方法,学会这六种

2026-01-17 14:23

小学生都能学会的冒泡排序

01故事起源幼儿园放学,小朋友们集合,需要先从低到高排队,应该怎么排呢? 02开始行动小K身高180,是班里最高的,自然得往后排啦。小K先和身后的小B比较,然后和小B交换。小K接着和身后

2026-01-17 14:23

军考干货!来自“上岸”师兄的征战“宝典”!(一)

又是一年军考季正是复习备考时 为了一道杠的青春为了追逐心中梦想你无惧风雨、无畏艰难一次次的挑灯夜战一遍遍的跌倒重来只为成就更好的自己 此刻的你是不是在畅想“上岸”

2026-01-17 14:22

到底什么是军考?

提到军考相信肯定很多人都并不熟悉,通常大家所知道的高考、艺考、和体考等都是升学考试常见类型,对于军考很多小伙伴不禁感到陌生,就连很多入伍的士兵也有时模糊不清。今天就向

2026-01-17 14:22

军考作文的出题特色,考学战友提前知晓一下

众所周知,作文在语文试卷中分值占比很大,即便是军考也不例外。详看历年军考作文真题,我们可以很明显地发现,军考作文出题具有以下三个显著特色。一、具有军营热度在经过部队大熔

2026-01-17 14:21

2020优秀士兵选拔干部综合知识考试大纲

2020 年从优秀士兵中选拔干部综合知识考试大纲(大学毕业生士兵提干推荐对象、优秀士兵保送入学对象)为便于大学生士兵提干推荐对象、优秀士兵保送入学对象了解从优秀士兵中

2026-01-17 14:21