Showing preview only (3,384K chars total). Download the full file or copy to clipboard to get everything.
Repository: leandromoreira/digital_video_introduction
Branch: master
Commit: 4547487189a5
Files: 28
Total size: 3.2 MB
Directory structure:
gitextract__0xyor60/
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README-cn.md
├── README-es.md
├── README-it.md
├── README-ja.md
├── README-ko.md
├── README-pt.md
├── README-ru.md
├── README.md
├── clean_docker.sh
├── dct_better_explained.ipynb
├── dct_experiences.ipynb
├── encoding_pratical_examples.md
├── filters_are_easy.ipynb
├── frame_difference_vs_motion_estimation_plus_residual.ipynb
├── image_as_3d_array.ipynb
├── image_transform_frequency_domain.ipynb
├── s/
│ ├── cut_smaller_video.sh
│ ├── download_video.sh
│ ├── ffmpeg
│ ├── mediainfo
│ ├── start_jupyter.sh
│ └── vmaf
├── setup.sh
└── uniform_quantization_experience.ipynb
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
v/*
!v/small_bunny_1080p_60fps.mp4
!v/small_bunny_1080p_30fps.mp4
.ipynb_checkpoints/
.DS_Store
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at leandro.ribeiro.moreira@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
================================================
FILE: CONTRIBUTING.md
================================================
## Contributing
First off, thank you for considering contributing to this repo. It's people
like you that make this material such a great tutorial.
### 1. Found a bug, have a question or want to add a new feature
If you've noticed a bug, a feature or have a question go ahead and [open an issue](https://github.com/leandromoreira/digital_video_introduction/issues/new)!
### 2. Fork & create a branch
> * **Ensure the bug was not already reported** by [searching all issues](https://github.com/leandromoreira/digital_video_introduction/issues?q=).
If this is something you think you can fix, then
[fork digital_video_introduction](https://help.github.com/articles/fork-a-repo)
and create a branch with a descriptive name.
A good branch name would be (where issue #325 is the ticket you're working on):
```sh
git checkout -b 325-add-japanese-translations
```
### 3. Implement your fix or feature
At this point, you're ready to make your changes!
### 4. Make a Pull Request
At this point, you should switch back to your master branch and make sure it's
up to date with Active Admin's master branch:
```sh
git remote add upstream git@github.com:leandromoreira/digital_video_introduction.git
git checkout master
git pull upstream master
```
Then update your feature branch from your local copy of master, and push it!
```sh
git checkout 325-add-japanese-translations
git rebase master
git push --set-upstream origin 325-add-japanese-translations
```
Finally, go to GitHub and
[make a Pull Request](https://help.github.com/articles/creating-a-pull-request)
:D
### 5. Merging a PR (maintainers only)
A PR can only be merged into master by a maintainer if:
* It was reviewed.
* It is up to date with current master.
Any maintainer is allowed to merge a PR if all of these conditions are
met.
PS: this contributing text was inspired by active admin contributing.
================================================
FILE: LICENSE
================================================
BSD 3-Clause License
Copyright (c) 2017, Leandro Moreira
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README-cn.md
================================================
[🇺🇸](/README.md "English")
[🇨🇳](/README-cn.md "Simplified Chinese")
[🇯🇵](/README-ja.md "Japanese")
[🇮🇹](/README-it.md "Italian")
[🇰🇷](/README-ko.md "Korean")
[🇷🇺](/README-ru.md "Russian")
[🇧🇷](/README-pt.md "Portuguese")
[🇪🇸](/README-es.md "Spanish")
[](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)
# 介绍
这是一份循序渐进的视频技术的介绍。尽管它面向的是软件开发人员/工程师,但我们希望**对任何人而言**,这份文档都能简单易学。这个点子产生于一个[视频技术新手小型研讨会](https://docs.google.com/presentation/d/17Z31kEkl_NGJ0M66reqr9_uTG6tI5EDDVXpdPKVuIrs/edit#slide=id.p)期间。
本文档旨在尽可能使用**浅显的词语,丰富的图像和实际例子**介绍数字视频概念,使这些知识能适用于各种场合。你可以随时反馈意见或建议,以改进这篇文档。
“**自己动手**”需要安装 docker,并将这个 repo clone 到你的计算机。
```bash
git clone https://github.com/leandromoreira/digital_video_introduction.git
cd digital_video_introduction
./setup.sh
```
> **注意**:当你看到 `./s/ffmpeg` 或 `./s/mediainfo` 命令时,说明我们运行的是 docker 容器中的版本,容器已经包含了程序所需的依赖。
所有的 **“自己动手”应从本 repo 的根目录运行**。**jupyter 的示例**应使用 `./s/start_jupyter.sh` 启动服务器,然后复制 URL 到你的浏览器中使用。
# 更新日志
* 增加 DRM 系统
* 发布版本 1.0.0
* 添加简体中文翻译
* 添加FFmpeg oscilloscope滤镜示例
# 目录
- [介绍](#介绍)
- [目录](#目录)
- [基本术语](#基本术语)
* [编码彩色图像的其它方法](#编码彩色图像的其它方法)
* [自己动手:玩转图像和颜色](#自己动手玩转图像和颜色)
* [DVD 的 DAR 是 4:3](#dvd-的-dar-是-43)
* [自己动手:检查视频属性](#自己动手检查视频属性)
- [消除冗余](#消除冗余)
* [颜色,亮度和我们的眼睛](#颜色亮度和我们的眼睛)
+ [颜色模型](#颜色模型)
+ [YCbCr 和 RGB 之间的转换](#ycbcr-和-rgb-之间的转换)
+ [色度子采样](#色度子采样)
+ [自己动手:检查 YCbCr 直方图](#自己动手检查-ycbcr-直方图)
* [帧类型](#帧类型)
+ [I 帧(帧内,关键帧)](#i-帧帧内关键帧)
+ [P 帧(预测)](#p-帧预测)
- [自己动手:具有单个 I 帧的视频](#自己动手具有单个-i-帧的视频)
+ [B 帧(双向预测)](#b-帧双向预测)
- [自己动手:使用 B 帧比较视频](#自己动手使用-b-帧比较视频)
+ [小结](#小结)
* [时间冗余(帧间预测)](#时间冗余帧间预测)
- [自己动手:查看运动向量](#自己动手查看运动向量)
* [空间冗余(帧内预测)](#空间冗余帧内预测)
- [自己动手:查看帧间预测](#自己动手查看帧间预测)
- [视频编解码器是如何工作的?](#视频编解码器是如何工作的)
* [是什么?为什么?怎么做?](#是什么为什么怎么做)
* [历史](#历史)
+ [AV1 的诞生](#av1-的诞生)
* [通用编解码器](#通用编解码器)
* [第一步 - 图片分区](#第一步---图片分区)
+ [自己动手:查看分区](#自己动手查看分区)
* [第二步 - 预测](#第二步---预测)
* [第三步 - 转换](#第三步---转换)
+ [自己动手:丢弃不同的系数](#自己动手丢弃不同的系数)
* [第四步 - 量化](#第四步---量化)
+ [自己动手:量化](#自己动手量化)
* [第五步 - 熵编码](#第五步---熵编码)
+ [VLC 编码](#vlc-编码)
+ [算术编码](#算术编码)
+ [自己动手:CABAC vs CAVLC](#自己动手cabac-vs-cavlc)
* [第六步 - 比特流格式](#第六步---比特流格式)
+ [H.264 比特流](#h264-比特流)
+ [自己动手:检查 H.264 比特流](#自己动手检查-h264-比特流)
* [回顾](#回顾)
* [H.265 如何实现比 H.264 更好的压缩率?](#h265-如何实现比-h264-更好的压缩率)
- [在线流媒体](#在线流媒体)
* [通用架构](#通用架构)
* [逐行下载和自适应流](#逐行下载和自适应流)
* [内容保护](#内容保护)
- [如何使用 jupyter](#如何使用-jupyter)
- [会议](#会议)
- [参考](#参考)
# 基本术语
一个**图像**可以视作一个**二维矩阵**。如果将**色彩**考虑进来,我们可以做出推广:将这个图像视作一个**三维矩阵**——多出来的维度用于储存色彩信息。
如果我们选择三原色(红、绿、蓝)代表这些色彩,这就定义了三个平面:第一个是红色平面,第二个是绿色平面,最后一个是蓝色平面。

我们把这个矩阵里的每一个点称为**像素**(图像元素)。像素的色彩由三原色的**强度**(通常用数值表示)表示。例如,一个**红色像素**是指强度为 0 的绿色,强度为 0 的蓝色和强度最大的红色。**粉色像素**可以通过三种颜色的组合表示。如果规定强度的取值范围是 0 到 255,**红色 255、绿色 192、蓝色 203** 则表示粉色。
> ### 编码彩色图像的其它方法
>
> 还有许多其它模型也可以用来表示色彩,进而组成图像。例如,给每种颜色都标上序号(如下图),这样每个像素仅需一个字节就可以表示出来,而不是 RGB 模型通常所需的 3 个。在这样一个模型里我们可以用一个二维矩阵来代替三维矩阵去表示我们的色彩,这将节省存储空间,但色彩的数量将会受限。
>
> 
例如以下几张图片。第一张包含所有颜色平面。剩下的分别是红、绿、蓝色平面(显示为灰调)(译注:颜色强度高的地方显示为亮色,强度低为暗色)。

我们可以看到,对于最终的成像,红色平面对强度的贡献更多(三个平面最亮的是红色平面),蓝色平面(最后一张图片)的贡献大多只在马里奥的眼睛和他衣服的一部分。所有颜色平面对马里奥的胡子(最暗的部分)均贡献较少。
存储颜色的强度,需要占用一定大小的数据空间,这个大小被称为颜色深度。假如每个颜色(平面)的强度占用 8 bit(取值范围为 0 到 255),那么颜色深度就是 24(8*3)bit,我们还可以推导出我们可以使用 2 的 24 次方种不同的颜色。
> 很棒的学习材料:[现实世界的照片是如何拍摄成 0 和 1 的](http://www.cambridgeincolour.com/tutorials/camera-sensors.htm)。
图片的另一个属性是**分辨率**,即一个平面内像素的数量。通常表示成宽*高,例如下面这张 **4x4** 的图片。

> ### 自己动手:玩转图像和颜色
>
> 你可以使用 [jupyter](#如何使用-jupyter)(python, numpy, matplotlib 等等)[玩转图像](/image_as_3d_array.ipynb)。
>
> 你也可以学习[图像滤镜(边缘检测,锐化,模糊。。。)的原理](/filters_are_easy.ipynb)。
图像或视频还有一个属性是宽高比,它简单地描述了图像或像素的宽度和高度之间的比例关系。
当人们说这个电影或照片是 16:9 时,通常是指显示宽高比(DAR),然而我们也可以有不同形状的单个像素,我们称为像素宽高比(PAR)。


> ## DVD 的 DAR 是 4:3
>
> 虽然 DVD 的实际分辨率是 704x480,但它依然保持 4:3 的宽高比,因为它有一个 10:11(704x10/480x11)的 PAR。
现在我们可以将**视频**定义为在**单位时间**内**连续的 n 帧**,这可以视作一个新的维度,n 即为帧率,若单位时间为秒,则等同于 FPS (每秒帧数 Frames Per Second)。

播放一段视频每秒所需的数据量就是它的**比特率**(即常说的码率)。
> 比特率 = 宽 * 高 * 颜色深度 * 帧每秒
例如,一段每秒 30 帧,每像素 24 bits,分辨率是 480x240 的视频,如果我们不做任何压缩,它将需要 **82,944,000 比特每秒**或 82.944 Mbps (30x480x240x24)。
当**比特率**几乎恒定时称为恒定比特率(**CBR**);但它也可以变化,称为可变比特率(**VBR**)。
> 这个图形显示了一个受限的 VBR,当帧为黑色时不会花费太多的数据量。
>
> 
在早期,工程师们想出了一项技术能将视频的感官帧率加倍而**没有消耗额外带宽**。这项技术被称为**隔行扫描**;总的来说,它在一个时间点发送一个画面——画面用于填充屏幕的一半,而下一个时间点发送的画面用于填充屏幕的另一半。
如今的屏幕渲染大多使用**逐行扫描技术**。这是一种显示、存储、传输运动图像的方法,每帧中的所有行都会被依次绘制。

现在我们知道了数字化**图像**的原理;它的**颜色**的编排方式;给定**帧率**和**分辨率**时,展示一个视频需要花费多少**比特率**;它是恒定的(CBR)还是可变的(VBR);还有很多其它内容,如隔行扫描和 PAR。
> ## 自己动手:检查视频属性
> 你可以[使用 ffmpeg 或 mediainfo 检查大多数属性的解释](https://github.com/leandromoreira/introduction_video_technology/blob/master/encoding_pratical_examples.md#inspect-stream)。
# 消除冗余
我们认识到,不对视频进行压缩是不行的;**一个单独的一小时长的视频**,分辨率为 720p 和 30fps 时将**需要 278GB<sup>\*</sup>**。仅仅使用无损数据压缩算法——如 DEFLATE(被PKZIP, Gzip, 和 PNG 使用)——也无法充分减少视频所需的带宽,我们需要找到其它压缩视频的方法。
> <sup>*</sup>我们使用乘积得出这个数字 1280 x 720 x 24 x 30 x 3600 (宽,高,每像素比特数,fps 和秒数)
为此,我们可以**利用视觉特性**:和区分颜色相比,我们区分亮度要更加敏锐。**时间上的重复**:一段视频包含很多只有一点小小改变的图像。**图像内的重复**:每一帧也包含很多颜色相同或相似的区域。
## 颜色,亮度和我们的眼睛
我们的眼睛[对亮度比对颜色更敏感](http://vanseodesign.com/web-design/color-luminance/),你可以看看下面的图片自己测试。

如果你看不出左图的**方块 A 和方块 B** 的颜色是**相同的**,那么好,是我们的大脑玩了一个小把戏,这让我们更多的去注意光与暗,而不是颜色。右边这里有一个使用同样颜色的连接器,那么我们(的大脑)就能轻易分辨出事实,它们是同样的颜色。
> **简单解释我们的眼睛工作的原理**
>
> [眼睛是一个复杂的器官](http://www.biologymad.com/nervoussystem/eyenotes.htm),有许多部分组成,但我们最感兴趣的是视锥细胞和视杆细胞。眼睛有[大约1.2亿个视杆细胞和6百万个视锥细胞](https://en.wikipedia.org/wiki/Photoreceptor_cell)。
>
> **简单来说**,让我们把颜色和亮度放在眼睛的功能部位上。[视杆细胞](https://en.wikipedia.org/wiki/Rod_cell)**主要负责亮度**,而[视锥细胞](https://en.wikipedia.org/wiki/Cone_cell)**负责颜色**,有三种类型的视锥,每个都有不同的颜料,叫做:[S-视锥(蓝色),M-视锥(绿色)和L-视锥(红色)](https://upload.wikimedia.org/wikipedia/commons/1/1e/Cones_SMJ2_E.svg)。
>
> 既然我们的视杆细胞(亮度)比视锥细胞多很多,一个合理的推断是相比颜色,我们有更好的能力去区分黑暗和光亮。
>
> 
一旦我们知道我们对**亮度**(图像中的亮度)更敏感,我们就可以利用它。
### 颜色模型
我们最开始学习的[彩色图像的原理](#基本术语)使用的是 **RGB 模型**,但也有其他模型。有一种模型将亮度(光亮)和色度(颜色)分离开,它被称为 **YCbCr**<sup>*</sup>。
> <sup>*</sup> 有很多种模型做同样的分离。
这个颜色模型使用 **Y** 来表示亮度,还有两种颜色通道:Cb(蓝色色度) 和 Cr(红色色度)。YCbCr 可以由 RGB 转换得来,也可以转换回 RGB。使用这个模型我们可以创建拥有完整色彩的图像,如下图。

### YCbCr 和 RGB 之间的转换
有人可能会问,在 **不使用绿色(色度)** 的情况下,我们如何表现出所有的色彩?
为了回答这个问题,我们将介绍从 RGB 到 YCbCr 的转换。我们将使用 [ITU-R 小组](https://en.wikipedia.org/wiki/ITU-R)*建议的[标准 BT.601](https://en.wikipedia.org/wiki/Rec._601) 中的系数。
第一步是计算亮度,我们将使用 ITU 建议的常量,并替换 RGB 值。
```
Y = 0.299R + 0.587G + 0.114B
```
一旦我们有了亮度后,我们就可以拆分颜色(蓝色色度和红色色度):
```
Cb = 0.564(B - Y)
Cr = 0.713(R - Y)
```
并且我们也可以使用 YCbCr 转换回来,甚至得到绿色。
```
R = Y + 1.402Cr
B = Y + 1.772Cb
G = Y - 0.344Cb - 0.714Cr
```
> <sup>*</sup>组织和标准在数字视频领域中很常见,它们通常定义什么是标准,例如,[什么是 4K?我们应该使用什么帧率?分辨率?颜色模型?](https://en.wikipedia.org/wiki/Rec._2020)
通常,**显示屏**(监视器,电视机,屏幕等等)**仅使用 RGB 模型**,并以不同的方式来组织,看看下面这些放大效果:

### 色度子采样
一旦我们能从图像中分离出亮度和色度,我们就可以利用人类视觉系统对亮度比色度更敏感的特点,选择性地剔除信息。**色度子采样**是一种编码图像时,使**色度分辨率低于亮度**的技术。

我们应该减少多少色度分辨率呢?已经有一些模式定义了如何处理分辨率和合并(`最终的颜色 = Y + Cb + Cr`)。
这些模式称为子采样系统,并被表示为 3 部分的比率 - `a:x:y`,其定义了色度平面的分辨率,与亮度平面上的、分辨率为 `a x 2` 的小块之间的关系。
* `a` 是水平采样参考 (通常是 4),
* `x` 是第一行的色度样本数(相对于 a 的水平分辨率),
* `y` 是第二行的色度样本数。
> 存在的一个例外是 4:1:0,其在每个亮度平面分辨率为 4 x 4 的块内提供一个色度样本。
现代编解码器中使用的常用方案是: 4:4:4 (没有子采样), 4:2:2, 4:1:1, 4:2:0, 4:1:0 and 3:1:1。
> YCbCr 4:2:0 合并
>
> 这是使用 YCbCr 4:2:0 合并的一个图像的一块,注意我们每像素只花费 12bit。
>
> 
下图是同一张图片使用几种主要的色度子采样技术进行编码,第一行图像是最终的 YCbCr,而最后一行图像展示了色度的分辨率。这么小的损失确实是一个伟大的胜利。

前面我们计算过我们需要 [278GB 去存储一个一小时长,分辨率在720p和30fps的视频文件](#消除冗余)。如果我们使用 `YCbCr 4:2:0` 我们能减少`一半的大小(139GB)`<sup>*</sup>,但仍然不够理想。
> <sup>*</sup> 我们通过将宽、高、颜色深度和 fps 相乘得出这个值。前面我们需要 24 bit,现在我们只需要 12 bit。
> ### 自己动手:检查 YCbCr 直方图
> 你可以[使用 ffmpeg 检查 YCbCr 直方图](/encoding_pratical_examples.md#generates-yuv-histogram)。这个场景有更多的蓝色贡献,由[直方图](https://en.wikipedia.org/wiki/Histogram)显示。
>
> 
### 颜色, 亮度, 视频亮度, 伽马 视频回顾
观看这段精彩的视频,它解释什么是亮度并了解视频亮度、伽马和颜色。
[](http://www.youtube.com/watch?v=Ymt47wXUDEU)
> ### 自己动手: 检查 YCbCr 强度
> 你可以使用[FFmpeg's oscilloscope滤镜](https://ffmpeg.org/ffmpeg-filters.html#oscilloscope)可视化给定视频行的Y强度.
> ```bash
> ffplay -f lavfi -i 'testsrc2=size=1280x720:rate=30000/1001,format=yuv420p' -vf oscilloscope=x=0.5:y=200/720:s=1:c=1
> ```
> 
## 帧类型
现在我们进一步消除`时间冗余`,但在这之前让我们来确定一些基本术语。假设我们一段 30fps 的影片,这是最开始的 4 帧。
  

我们可以在帧内看到**很多重复内容**,如**蓝色背景**,从 0 帧到第 3 帧它都没有变化。为了解决这个问题,我们可以将它们**抽象地分类**为三种类型的帧。
### I 帧(帧内,关键帧)
I 帧(可参考,关键帧,帧内编码)是一个**自足的帧**。它不依靠任何东西来渲染,I 帧与静态图片相似。第一帧通常是 I 帧,但我们将看到 I 帧被定期插入其它类型的帧之间。

### P 帧(预测)
P 帧利用了一个事实:当前的画面几乎总能**使用之前的一帧进行渲染**。例如,在第二帧,唯一的改变是球向前移动了。仅仅使用(第二帧)对前一帧的引用和差值,我们就能重建前一帧。
 <- 
> #### 自己动手:具有单个 I 帧的视频
> 既然 P 帧使用较少的数据,为什么我们不能用[单个 I 帧和其余的 P 帧](/encoding_pratical_examples.md#1-i-frame-and-the-rest-p-frames)来编码整个视频?
>
> 编码完这个视频之后,开始观看它,并**快进到视频的末尾部分**,你会注意到**它需要花一些时间**才真正跳转到这部分。这是因为 **P 帧需要一个引用帧**(比如 I 帧)才能渲染。
>
> 你可以做的另一个快速试验,是使用单个 I 帧编码视频,然后[再次编码且每 2 秒插入一个 I 帧](/encoding_pratical_examples.md#1-i-frames-per-second-vs-05-i-frames-per-second),并**比较成品的大小**。
### B 帧(双向预测)
如何引用前面和后面的帧去做更好的压缩?!简单地说 B 帧就是这么做的。
 <-  -> 
> #### 自己动手:使用 B 帧比较视频
> 你可以生成两个版本,一个使用 B 帧,另一个[全部不使用 B 帧](/encoding_pratical_examples.md#no-b-frames-at-all),然后查看文件的大小以及画质。
### 小结
这些帧类型用于提供更好的压缩率,我们将在下一章看到这是如何发生的。现在,我们可以想到 I 帧是昂贵的,P 帧是便宜的,最便宜的是 B 帧。

## 时间冗余(帧间预测)
让我们探究去除**时间上的重复**,去除这一类冗余的技术就是**帧间预测**。
我们将尝试**花费较少的数据量**去编码在时间上连续的 0 号帧和 1 号帧。

我们可以做个减法,我们简单地**用 0 号帧减去 1 号帧**,得到残差,这样我们就只需要**对残差进行编码**。

但我们有一个**更好的方法**来节省数据量。首先,我们将`0 号帧` 视为一个个分块的集合,然后我们将尝试将 `帧 1` 和 `帧 0` 上的块相匹配。我们可以将这看作是**运动预测**。
> ### 维基百科—块运动补偿
> “运动补偿是一种描述相邻帧(相邻在这里表示在编码关系上相邻,在播放顺序上两帧未必相邻)差别的方法,具体来说是描述前面一帧(相邻在这里表示在编码关系上的前面,在播放顺序上未必在当前帧前面)的每个小块怎样移动到当前帧中的某个位置去。”

我们预计那个球会从 `x=0, y=25` 移动到 `x=6, y=26`,**x** 和 **y** 的值就是**运动向量**。**进一步**节省数据量的方法是,只编码这两者运动向量的差。所以,最终运动向量就是 `x=6 (6-0), y=1 (26-25)`。
> 实际情况下,这个球会被切成 n 个分区,但处理过程是相同的。
帧上的物体**以三维方式移动**,当球移动到背景时会变小。当我们尝试寻找匹配的块,**找不到完美匹配的块**是正常的。这是一张运动预测与实际值相叠加的图片。

但我们能看到当我们使用**运动预测**时,**编码的数据量少于**使用简单的残差帧技术。

你可以[使用 jupyter 玩转这些概念](/frame_difference_vs_motion_estimation_plus_residual.ipynb)。
> ### 自己动手:查看运动向量
>
> 我们可以[使用 ffmpeg 生成包含帧间预测(运动向量)的视频](/encoding_pratical_examples.md#generate-debug-video)。
>
> ")
>
> 或者我们也可使用 [Intel® Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer)(需要付费,但也有只能查看前 10 帧的免费试用版)。
>
> 
## 空间冗余(帧内预测)
如果我们分析一个视频里的**每一帧**,我们会看到有**许多区域是相互关联的**。

让我们举一个例子。这个场景大部分由蓝色和白色组成。

这是一个 `I 帧`,我们**不能使用前面的帧来预测**,但我们仍然可以压缩它。我们将编码我们选择的那块红色区域。如果我们**看看它的周围**,我们可以**估计它周围颜色的变化**。

我们预测:帧中的颜色在垂直方向上保持一致,这意味着**未知像素的颜色与临近的像素相同**。

我们的**预测会出错**,所以我们需要先利用这项技术(**帧内预测**),然后**减去实际值**,算出残差,得出的矩阵比原始数据更容易压缩。

> ### 自己动手:查看帧内预测
> 你可以[使用 ffmpeg 生成包含宏块及预测的视频](/encoding_pratical_examples.md#generate-debug-video)。请查看 ffmpeg 文档以了解[每个块颜色的含义](https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors?version=7)。
>
> ")
>
> 或者我们也可使用 [Intel® Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer)(需要付费,但也有只能查看前 10 帧的免费试用版)。
>
> 
# 视频编解码器是如何工作的?
## 是什么?为什么?怎么做?
**是什么?** 就是用于压缩或解压数字视频的软件或硬件。**为什么?** 人们需要在有限带宽或存储空间下提高视频的质量。还记得当我们计算每秒 30 帧,每像素 24 bit,分辨率是 480x240 的视频[需要多少带宽](#基本术语)吗?没有压缩时是 **82.944 Mbps**。电视或互联网提供 HD/FullHD/4K 只能靠视频编解码器。**怎么做?** 我们将简单介绍一下主要的技术。
> 视频编解码 vs 容器
>
> 初学者一个常见的错误是混淆数字视频编解码器和[数字视频容器](https://en.wikipedia.org/wiki/Digital_container_format)。我们可以将**容器**视为包含视频(也很可能包含音频)元数据的包装格式,**压缩过的视频**可以看成是它承载的内容。
>
> 通常,视频文件的格式定义其视频容器。例如,文件 `video.mp4` 可能是 [MPEG-4 Part 14](https://en.wikipedia.org/wiki/MPEG-4_Part_14) 容器,一个叫 `video.mkv` 的文件可能是 [matroska](https://en.wikipedia.org/wiki/Matroska)。我们可以使用 [ffmpeg 或 mediainfo](/encoding_pratical_examples.md#inspect-stream) 来完全确定编解码器和容器格式。
## 历史
在我们跳进通用编解码器内部工作之前,让我们回头了解一些旧的视频编解码器。
视频编解码器 [H.261](https://en.wikipedia.org/wiki/H.261) 诞生在 1990(技术上是 1988),被设计为以 **64 kbit/s 的数据速率**工作。它已经使用如色度子采样、宏块,等等理念。在 1995 年,**H.263** 视频编解码器标准被发布,并继续延续到 2001 年。
在 2003 年 **H.264/AVC** 的第一版被完成。在同一年,一家叫做 **TrueMotion** 的公司发布了他们的**免版税**有损视频压缩的视频编解码器,称为 **VP3**。在 2008 年,**Google 收购了**这家公司,在同一年发布 **VP8**。在 2012 年 12 月,Google 发布了 **VP9**,**市面上大约有 3/4 的浏览器**(包括手机)支持。
[AV1](https://en.wikipedia.org/wiki/AOMedia_Video_1) 是由 **Google, Mozilla, Microsoft, Amazon, Netflix, AMD, ARM, NVidia, Intel, Cisco** 等公司组成的[开放媒体联盟(AOMedia)](http://aomedia.org/)设计的一种新的免版税和开源的视频编解码器。**第一版** 0.1.0 参考编解码器**发布于 2016 年 4 月 7 号**。

> ### AV1 的诞生
>
> 2015 年早期,Google 正在开发VP10,Xiph (Mozilla) 正在开发Daala,Cisco 开源了其称为 Thor 的免版税视频编解码器。
>
> 接着 MPEG LA 宣布了 HEVC (H.265) 每年版税的的上限,比 H.264 高 8 倍,但很快他们又再次改变了条款:
> * **不设年度收费上限**
> * **收取内容费**(收入的 0.5%)
> * **每单位费用高于 h264 的 10 倍**
>
> [开放媒体联盟](http://aomedia.org/about/)由硬件厂商(Intel, AMD, ARM , Nvidia, Cisco),内容分发商(Google, Netflix, Amazon),浏览器维护者(Google, Mozilla),等公司创建。
>
> 这些公司有一个共同目标,一个免版税的视频编解码器,所以 AV1 诞生时使用了一个更[简单的专利许可证](http://aomedia.org/license/patent/)。**Timothy B. Terriberry** 做了一个精彩的介绍,[关于 AV1 的概念,许可证模式和它当前的状态](https://www.youtube.com/watch?v=lzPaldsmJbk),就是本节的来源。
>
> 前往 [https://arewecompressedyet.com/analyzer/](https://arewecompressedyet.com/analyzer/), 你会惊讶于**使用你的浏览器就可以分析 AV1 编解码器**。
> 
>
> 附:如果你想了解更多编解码器的历史,你需要了解[视频压缩专利](https://www.vcodex.com/video-compression-patents/)背后的基本知识。
## 通用编解码器
我们接下来要介绍**通用视频编解码器背后的主要机制**,大多数概念都很实用,并被现代编解码器如 VP9, AV1 和 HEVC 使用。需要注意:我们将简化许多内容。有时我们会使用真实的例子(主要是 H.264)来演示技术。
## 第一步 - 图片分区
第一步是**将帧**分成几个**分区**,**子分区**甚至更多。

**但是为什么呢**有许多原因,比如,当我们分割图片时,我们可以更精确的处理预测,在微小移动的部分使用较小的分区,而在静态背景上使用较大的分区。
通常,编解码器**将这些分区组织**成切片(或瓦片),宏(或编码树单元)和许多子分区。这些分区的最大大小有所不同,HEVC 设置成 64x64,而 AVC 使用 16x16,但子分区可以达到 4x4 的大小。
还记得我们学过的**帧的分类**吗?你也可以**把这些概念应用到块**,因此我们可以有 I 切片,B 切片,I 宏块等等。
> ### 自己动手:查看分区
>
> 我们也可以使用 [Intel® Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer)(需要付费,但也有只能查看前 10 帧的免费试用版)。这是 [VP9 分区](/encoding_pratical_examples.md#transcoding)的分析。
>
> 
## 第二步 - 预测
一旦我们有了分区,我们就可以在它们之上做出预测。对于[帧间预测](#时间冗余帧间预测),我们需要**发送运动向量和残差**;至于[帧内预测](#空间冗余帧内预测),我们需要**发送预测方向和残差**。
## 第三步 - 转换
在我们得到残差块(`预测分区-真实分区`)之后,我们可以用一种方式**变换**它,这样我们就知道**哪些像素我们应该丢弃**,还依然能保持**整体质量**。这个确切的行为有几种变换方式。
尽管有[其它的变换方式](https://en.wikipedia.org/wiki/List_of_Fourier-related_transforms#Discrete_transforms),但我们重点关注离散余弦变换(DCT)。[DCT](https://en.wikipedia.org/wiki/Discrete_cosine_transform) 的主要功能有:
* 将**像素**块**转换**为相同大小的**频率系数块**。
* **压缩**能量,更容易消除空间冗余。
* **可逆的**,也意味着你可以还原回像素。
> 2017 年 2 月 2 号,F. M. Bayer 和 R. J. Cintra 发表了他们的论文:[图像压缩的 DCT 类变换只需要 14 个加法](https://arxiv.org/abs/1702.00817)。
如果你不理解每个要点的好处,不用担心,我们会尝试进行一些实验,以便从中看到真正的价值。
我们来看下面的**像素块**(8x8):

下面是其渲染的块图像(8x8):

当我们对这个像素块**应用 DCT** 时, 得到如下**系数块**(8x8):

接着如果我们渲染这个系数块,就会得到这张图片:

如你所见它看起来完全不像原图像,我们可能会注意到**第一个系数**与其它系数非常不同。第一个系数被称为直流分量,代表了输入数组中的**所有样本**,有点**类似于平均值**。
这个系数块有一个有趣的属性:高频部分和低频部分是分离的。

在一张图像中,**大多数能量**会集中在[低频部分](https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm),所以如果我们将图像转换成频率系数,并**丢掉高频系数**,我们就能**减少描述图像所需的数据量**,而不会牺牲太多的图像质量。
> 频率是指信号变化的速度。
让我们通过实验学习这点,我们将使用 DCT 把原始图像转换为频率(系数块),然后丢掉最不重要的系数。
首先,我们将它转换为其**频域**。

然后我们丢弃部分(67%)系数,主要是它的右下角部分。

然后我们从丢弃的系数块重构图像(记住,这需要可逆),并与原始图像相比较。

如我们所见它酷似原始图像,但它引入了许多与原来的不同,我们**丢弃了67.1875%**,但我们仍然得到至少类似于原来的东西。我们可以更加智能的丢弃系数去得到更好的图像质量,但这是下一个主题。
> ### 使用全部像素形成每个系数
>
> 需要注意的是,每个系数并不直接映射到单个像素,而是所有像素的加权和。这个神奇的图形展示了如何使用每个指数唯一的权重来计算第一个和第二个系数。
>
> 
>
> 来源:[https://web.archive.org/web/20150129171151/https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm](https://web.archive.org/web/20150129171151/https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm)
>
> 你也可以尝试[通过查看在 DCT 基础上形成的简单图片来可视化 DCT](/dct_better_explained.ipynb)。例如,这是使用每个系数权重[形成的字符 A](https://en.wikipedia.org/wiki/Discrete_cosine_transform#Example_of_IDCT)。
>
> 
<br/>
> ### 自己动手:丢弃不同的系数
> 你可以玩转 [DCT 变换](/uniform_quantization_experience.ipynb)
## 第四步 - 量化
当我们丢弃一些系数时,在最后一步(变换),我们做了一些形式的量化。这一步,我们选择性地剔除信息(**有损部分**)或者简单来说,我们将**量化系数以实现压缩**。
我们如何量化一个系数块?一个简单的方法是均匀量化,我们取一个块并**将其除以单个的值**(10),并舍入值。

我们如何**逆转**(重新量化)这个系数块?我们可以通过**乘以我们先前除以的相同的值**(10)来做到。

这**不是最好的方法**,因为它没有考虑到每个系数的重要性,我们可以使用一个**量化矩阵**来代替单个值,这个矩阵可以利用 DCT 的属性,多量化右下部,而少(量化)左上部,[JPEG 使用了类似的方法](https://www.hdm-stuttgart.de/~maucher/Python/MMCodecs/html/jpegUpToQuant.html),你可以通过[查看源码看看这个矩阵](https://github.com/google/guetzli/blob/master/guetzli/jpeg_data.h#L40)。
> ### 自己动手:量化
> 你可以玩转[量化](/dct_experiences.ipynb)
## 第五步 - 熵编码
在我们量化数据(图像块/切片/帧)之后,我们仍然可以以无损的方式来压缩它。有许多方法(算法)可用来压缩数据。我们将简单体验其中几个,你可以阅读这本很棒的书去深入理解:[Understanding Compression: Data Compression for Modern Developers](https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/)。
### VLC 编码:
让我们假设我们有一个符号流:**a**, **e**, **r** 和 **t**,它们的概率(从0到1)由下表所示。
| | a | e | r | t |
|-----|-----|-----|------|-----|
| 概率 | 0.3 | 0.3 | 0.2 | 0.2 |
我们可以分配不同的二进制码,(最好是)小的码给最可能(出现的字符),大些的码给最少可能(出现的字符)。
| | a | e | r | t |
|--------|-----|-----|------|-----|
| 概率 | 0.3 | 0.3 | 0.2 | 0.2 |
| 二进制码 | 0 | 10 | 110 | 1110 |
让我们压缩 **eat** 流,假设我们为每个字符花费 8 bit,在没有做任何压缩时我们将花费 **24 bit**。但是在这种情况下,我们使用各自的代码来替换每个字符,我们就能节省空间。
第一步是编码字符 **e** 为 `10`,第二个字符是 **a**,追加(不是数学加法)后是 `[10][0]`,最后是第三个字符 **t**,最终组成已压缩的比特流 `[10][0][1110]` 或 `1001110`,这只需 **7 bit**(比原来的空间少 3.4 倍)。
请注意每个代码必须是唯一的前缀码,[Huffman 能帮你找到这些数字](https://en.wikipedia.org/wiki/Huffman_coding)。虽然它有一些问题,但是[视频编解码器仍然提供该方法](https://en.wikipedia.org/wiki/Context-adaptive_variable-length_coding),它也是很多应用程序的压缩算法。
编码器和解码器都**必须知道**这个(包含编码的)字符表,因此,你也需要传送这个表。
### 算术编码
让我们假设我们有一个符号流:**a**, **e**, **r**, **s** 和 **t**,它们的概率由下表所示。
| | a | e | r | s | t |
|-----|-----|-----|------|------|-----|
| 概率 | 0.3 | 0.3 | 0.15 | 0.05 | 0.2 |
考虑到这个表,我们可以构建一个区间,区间包含了所有可能的字符,字符按出现概率排序。

让我们编码 **eat** 流,我们选择第一个字符 **e** 位于 **0.3 到 0.6** (但不包括 0.6)的子区间,我们选择这个子区间,按照之前同等的比例再次分割。

让我们继续编码我们的流 **eat**,现在使第二个 **a** 字符位于 **0.3 到 0.39** 的区间里,接着再次用同样的方法编码最后的字符 **t**,得到最后的子区间 **0.354 到 0.372**。

我们只需从最后的子区间 0.354 到 0.372 里选择一个数,让我们选择 0.36,不过我们可以选择这个子区间里的任何数。仅靠这个数,我们将可以恢复原始流 **eat**。就像我们在区间的区间里画了一根线来编码我们的流。

**反向过程**(又名解码)一样简单,用数字 **0.36** 和我们原始区间,我们可以进行同样的操作,不过现在是使用这个数字来还原被编码的流。
在第一个区间,我们发现数字落入了一个子区间,因此,这个子区间是我们的第一个字符,现在我们再次切分这个子区间,像之前一样做同样的过程。我们会注意到 **0.36** 落入了 **a** 的区间,然后我们重复这一过程直到得到最后一个字符 **t**(形成我们原始编码过的流 eat)。
编码器和解码器都**必须知道**字符概率表,因此,你也需要传送这个表。
非常巧妙,不是吗?人们能想出这样的解决方案实在是太聪明了,一些[视频编解码器使用](https://en.wikipedia.org/wiki/Context-adaptive_binary_arithmetic_coding)这项技术(或至少提供这一选择)。
关于无损压缩量化比特流的办法,这篇文章无疑缺少了很多细节、原因、权衡等等。作为一个开发者你[应该学习更多](https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/)。刚入门视频编码的人可以尝试使用不同的[熵编码算法,如ANS](https://en.wikipedia.org/wiki/Asymmetric_Numeral_Systems)。
> ### 自己动手:CABAC vs CAVLC
> 你可以[生成两个流,一个使用 CABAC,另一个使用 CAVLC](https://github.com/leandromoreira/introduction_video_technology/blob/master/encoding_pratical_examples.md#cabac-vs-cavlc),并比较生成每一个的时间以及最终的大小。
## 第六步 - 比特流格式
完成所有这些步之后,我们需要将**压缩过的帧和内容打包进去**。需要明确告知解码器**编码定义**,如颜色深度,颜色空间,分辨率,预测信息(运动向量,帧内预测方向),档次<sup>\*</sup>,级别<sup>\*</sup>,帧率,帧类型,帧号等等更多信息。
> <sup>*</sup> 译注:原文为 profile 和 level,没有通用的译名
我们将简单地学习 H.264 比特流。第一步是[生成一个小的 H.264<sup>\*</sup> 比特流](/encoding_pratical_examples.md#generate-a-single-frame-h264-bitstream),可以使用本 repo 和 [ffmpeg](http://ffmpeg.org/) 来做。
```
./s/ffmpeg -i /files/i/minimal.png -pix_fmt yuv420p /files/v/minimal_yuv420.h264
```
> <sup>*</sup> ffmpeg 默认将所有参数添加为 **SEI NAL**,很快我们会定义什么是 NAL。
这个命令会使用下面的图片作为帧,生成一个具有**单个帧**,64x64 和颜色空间为 yuv420 的原始 h264 比特流。
> 
### H.264 比特流
AVC (H.264) 标准规定信息将在宏帧(网络概念上的)内传输,称为 [NAL](https://en.wikipedia.org/wiki/Network_Abstraction_Layer)(网络抽象层)。NAL 的主要目标是提供“网络友好”的视频呈现方式,该标准必须适用于电视(基于流),互联网(基于数据包)等。

[同步标记](https://en.wikipedia.org/wiki/Frame_synchronization)用来定义 NAL 单元的边界。每个同步标记的值固定为 `0x00 0x00 0x01` ,最开头的标记例外,它的值是 `0x00 0x00 0x00 0x01` 。如果我们在生成的 h264 比特流上运行 **hexdump**,我们可以在文件的开头识别至少三个 NAL。

我们之前说过,解码器需要知道不仅仅是图片数据,还有视频的详细信息,如:帧、颜色、使用的参数等。每个 NAL 的**第一位**定义了其分类和**类型**。
| NAL type id | 描述 |
|--- |---|
| 0 | Undefined |
| 1 | Coded slice of a non-IDR picture |
| 2 | Coded slice data partition A |
| 3 | Coded slice data partition B |
| 4 | Coded slice data partition C |
| 5 | **IDR** Coded slice of an IDR picture |
| 6 | **SEI** Supplemental enhancement information |
| 7 | **SPS** Sequence parameter set |
| 8 | **PPS** Picture parameter set |
| 9 | Access unit delimiter |
| 10 | End of sequence |
| 11 | End of stream |
| ... | ... |
通常,比特流的第一个 NAL 是 **SPS**,这个类型的 NAL 负责传达通用编码参数,如**档次,级别,分辨率**等。
如果我们跳过第一个同步标记,就可以通过解码**第一个字节**来了解第一个 **NAL 的类型**。
例如同步标记之后的第一个字节是 `01100111`,第一位(`0`)是 **forbidden_zero_bit** 字段,接下来的两位(`11`)告诉我们是 **nal_ref_idc** 字段,其表示该 NAL 是否是参考字段,其余 5 位(`00111`)告诉我们是 **nal_unit_type** 字段,在这个例子里是 NAL 单元 **SPS** (7)。
SPS NAL 的第 2 位 (`binary=01100100, hex=0x64, dec=100`) 是 **profile_idc** 字段,显示编码器使用的配置,在这个例子里,我们使用[高阶档次](https://en.wikipedia.org/wiki/H.264/MPEG-4_AVC#Profiles),一种没有 B(双向预测) 切片支持的高阶档次。

当我们阅读 SPS NAL 的 H.264 比特流规范时,会为**参数名称**,**分类**和**描述**找到许多值,例如,看看字段 `pic_width_in_mbs_minus_1` 和 `pic_height_in_map_units_minus_1`。
| 参数名称 | 分类 | 描述 |
|--- |---|---|
| pic_width_in_mbs_minus_1 | 0 | ue(v) |
| pic_height_in_map_units_minus_1 | 0 | ue(v) |
> **ue(v)**: 无符号整形 [Exp-Golomb-coded](https://pythonhosted.org/bitstring/exp-golomb.html)
如果我们对这些字段的值进行一些计算,将最终得出**分辨率**。我们可以使用值为 `119( (119 + 1) * macroblock_size = 120 * 16 = 1920)`的 `pic_width_in_mbs_minus_1` 表示 `1920 x 1080`,再次为了减少空间,我们使用 `119` 来代替编码 `1920`。
如果我们再次使用二进制视图检查我们创建的视频 (ex: `xxd -b -c 11 v/minimal_yuv420.h264`),可以跳到帧自身上一个 NAL。

我们可以看到最开始的 6 个字节:`01100101 10001000 10000100 00000000 00100001 11111111`。我们已经知道第一个字节告诉我们 NAL 的类型,在这个例子里, (`00101`) 是 **IDR 切片 (5)**,可以进一步检查它:

对照规范,我们能解码切片的类型(**slice_type**),帧号(**frame_num**)等重要字段。
为了获得一些字段(`ue(v), me(v), se(v) 或 te(v)`)的值,我们需要称为 [Exponential-Golomb](https://pythonhosted.org/bitstring/exp-golomb.html) 的特定解码器来解码它。当存在很多默认值时,这个方法编码变量值特别高效。
> 这个视频里 **slice_type** 和 **frame_num** 的值是 7(I 切片)和 0(第一帧)。
我们可以将**比特流视为一个协议**,如果你想学习更多关于比特流的内容,请参考 [ITU H.264 规范](http://www.itu.int/rec/T-REC-H.264-201610-I)。这个宏观图展示了图片数据(压缩过的 YUV)所在的位置。

我们可以探究其它比特流,如 [VP9 比特流](https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf),[H.265(HEVC)](http://handle.itu.int/11.1002/1000/11885-en?locatt=format:pdf)或是我们的新朋友 [AV1 比特流](https://medium.com/@mbebenita/av1-bitstream-analyzer-d25f1c27072b#.d5a89oxz8),[他们很相似吗?不](http://www.gpac-licensing.com/2016/07/12/vp9-av1-bitstream-format/),但只要学习了其中之一,学习其他的就简单多了。
> ### 自己动手:检查 H.264 比特流
>
> 我们可以[生成一个单帧视频](https://github.com/leandromoreira/introduction_video_technology/blob/master/encoding_pratical_examples.md#generate-a-single-frame-video),使用 [mediainfo](https://en.wikipedia.org/wiki/MediaInfo) 检查它的 H.264 比特流。事实上,你甚至可以查看[解析 h264(AVC) 视频流的源代码](https://github.com/MediaArea/MediaInfoLib/blob/master/Source/MediaInfo/Video/File_Avc.cpp)。
>
> 
>
> 我们也可使用 [Intel® Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer),需要付费,但也有只能查看前 10 帧的免费试用版,这已经够达成学习目的了。
>
> 
## 回顾
我们可以看到我们学了许多**使用相同模型的现代编解码器**。事实上,让我们看看 Thor 视频编解码器框图,它包含所有我们学过的步骤。你现在应该能更好地理解数字视频领域内的创新和论文。

之前我们计算过我们[需要 139GB 来保存一个一小时,720p 分辨率和30fps的视频文件](#色度子采样),如果我们使用在这里学过的技术,如**帧间和帧内预测,转换,量化,熵编码和其它**我们能实现——假设我们**每像素花费 0.031 bit**——同样观感质量的视频,**对比 139GB 的存储,只需 367.82MB**。
> 我们根据这里提供的示例视频选择**每像素使用 0.031 bit**。
## H.265 如何实现比 H.264 更好的压缩率
我们已经更多地了解了编解码器的工作原理,那么就容易理解新的编解码器如何使用更少的数据量传输更高分辨率的视频。
我们将比较 AVC 和 HEVC,要记住的是:我们几乎总是要在压缩率和更多的 CPU 周期(复杂度)之间作权衡。
HEVC 比 AVC 有更大和更多的**分区**(和**子分区**)选项,更多**帧内预测方向**,**改进的熵编码**等,所有这些改进使得 H.265 比 H.264 的压缩率提升 50%。

# 在线流媒体
## 通用架构

[TODO]
## 渐进式下载和自适应流


[TODO]
## 内容保护
我们可以用一个简单的令牌认证系统来保护视频。用户需要拥有一个有效的令牌才可以播放视频,CDN 会拒绝没有令牌的用户的请求。它与大多数网站的身份认证系统非常相似。

仅仅使用令牌认证系统,用户仍然可以下载并重新分发视频。DRM 系统可以用来避免这种情况。

实际情况下,人们通常同时使用这两种技术提供授权和认证。
### DRM
#### 主要系统
* FPS - [**FairPlay Streaming**](https://developer.apple.com/streaming/fps/)
* PR - [**PlayReady**](https://www.microsoft.com/playready/)
* WV - [**Widevine**](http://www.widevine.com/)
#### 是什么
DRM 指的是数字版权管理,是一种**为数字媒体提供版权保护**的方法,例如数字视频和音频。尽管用在了很多场合,但它并[没有被普遍接受](https://en.wikipedia.org/wiki/Digital_rights_management#DRM-free_works).
#### 为什么
内容的创作者(大多是工作室/制片厂)希望保护他们的知识产权,使他们的数字媒体免遭未经授权的分发。
#### 怎么做
我们将用一种简单的、抽象的方式描述 DRM
现有一份**内容 C1**(如 HLS 或 DASH 视频流),一个**播放器 P1**(如 shaka-clappr, exo-player 或 iOS),装在**设备 D1**(如智能手机、电视或台式机/笔记本)上,使用 **DRM 系统 DRM1**(如 FairPlay Streaming, PlayReady, Widevine)
内容 C1 由 DRM1 用一个**对称密钥 K1** 加密,生成**加密内容 C'1**

设备 D1 上的播放器 P1 有一个非对称密钥对,密钥对包含一个**私钥 PRK1**(这个密钥是受保护的<sup>1</sup>,只有 **D1** 知道密钥内容),和一个**公钥 PUK1**
> **<sup>1</sup>受保护的**: 这种保护可以**通过硬件**进行保护,例如, 将这个密钥存储在一个特殊的芯片(只读)中,芯片的工作方式就像一个用来解密的[黑箱]。 或**通过软件**进行保护(较低的安全系数)。DRM 系统提供了识别设备所使用的保护类型的方法。
当 **播放器 P1 希望播放***加密内容 C'1** 时,它需要与 **DRM1** 协商,将公钥 **PUK1** 发送给 DRM1, DRM1 会返回一个被公钥 **PUK1** **加密过的 K1**。按照推论,结果就是**只有 D1 能够解密**。
`K1P1D1 = enc(K1, PUK1)`
**P1** 使用它的本地 DRM 系统(这可以使用 [SoC](https://zh.wikipedia.org/wiki/系统芯片) ,一个专门的硬件和软件,这个系统可以使用它的私钥 PRK1 用来**解密**内容,它可以解密被加密过的**K1P1D1 的对称密钥 K1**。理想情况下,密钥不会被导出到内存以外的地方。
```
K1 = dec(K1P1D1, PRK1)
P1.play(dec(C'1, K1))
```

# 如何使用 jupyter
确保你已安装 docker,只需运行 `./s/start_jupyter.sh`,然后按照控制台的说明进行操作。
# 会议
* [DEMUXED](https://demuxed.com/) - 您可以[查看最近的2个活动演示](https://www.youtube.com/channel/UCIc_DkRxo9UgUSTvWVNCmpA)。
# 参考
这里有最丰富的资源,这篇文档包含的信息,均摘录、依据或受它们启发。你可以用这些精彩的链接,书籍,视频等深化你的知识。
在线课程和教程:
* [https://www.coursera.org/learn/digital/](https://www.coursera.org/learn/digital/)
* [https://people.xiph.org/~tterribe/pubs/lca2012/auckland/intro_to_video1.pdf](https://people.xiph.org/~tterribe/pubs/lca2012/auckland/intro_to_video1.pdf)
* [https://xiph.org/video/vid1.shtml](https://xiph.org/video/vid1.shtml)
* [https://xiph.org/video/vid2.shtml](https://xiph.org/video/vid2.shtml)
* [http://slhck.info/ffmpeg-encoding-course](http://slhck.info/ffmpeg-encoding-course)
* [http://www.cambridgeincolour.com/tutorials/camera-sensors.htm](http://www.cambridgeincolour.com/tutorials/camera-sensors.htm)
* [http://www.slideshare.net/vcodex/a-short-history-of-video-coding](http://www.slideshare.net/vcodex/a-short-history-of-video-coding)
* [http://www.slideshare.net/vcodex/introduction-to-video-compression-1339433](http://www.slideshare.net/vcodex/introduction-to-video-compression-13394338)
* [https://developer.android.com/guide/topics/media/media-formats.html](https://developer.android.com/guide/topics/media/media-formats.html)
* [http://www.slideshare.net/MadhawaKasun/audio-compression-23398426](http://www.slideshare.net/MadhawaKasun/audio-compression-23398426)
* [http://inst.eecs.berkeley.edu/~ee290t/sp04/lectures/02-Motion_Compensation_girod.pdf](http://inst.eecs.berkeley.edu/~ee290t/sp04/lectures/02-Motion_Compensation_girod.pdf)
书籍:
* [https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/ref=sr_1_1?s=books&ie=UTF8&qid=1486395327&sr=1-1](https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/ref=sr_1_1?s=books&ie=UTF8&qid=1486395327&sr=1-1)
* [https://www.amazon.com/H-264-Advanced-Video-Compression-Standard/dp/0470516925](https://www.amazon.com/H-264-Advanced-Video-Compression-Standard/dp/0470516925)
* [https://www.amazon.com/Practical-Guide-Video-Audio-Compression/dp/0240806301/ref=sr_1_3?s=books&ie=UTF8&qid=1486396914&sr=1-3&keywords=A+PRACTICAL+GUIDE+TO+VIDEO+AUDIO](https://www.amazon.com/Practical-Guide-Video-Audio-Compression/dp/0240806301/ref=sr_1_3?s=books&ie=UTF8&qid=1486396914&sr=1-3&keywords=A+PRACTICAL+GUIDE+TO+VIDEO+AUDIO)
* [https://www.amazon.com/Video-Encoding-Numbers-Eliminate-Guesswork/dp/0998453005/ref=sr_1_1?s=books&ie=UTF8&qid=1486396940&sr=1-1&keywords=jan+ozer](https://www.amazon.com/Video-Encoding-Numbers-Eliminate-Guesswork/dp/0998453005/ref=sr_1_1?s=books&ie=UTF8&qid=1486396940&sr=1-1&keywords=jan+ozer)
比特流规范:
* [http://www.itu.int/rec/T-REC-H.264-201610-I](http://www.itu.int/rec/T-REC-H.264-201610-I)
* [http://www.itu.int/ITU-T/recommendations/rec.aspx?rec=12904&lang=en](http://www.itu.int/ITU-T/recommendations/rec.aspx?rec=12904&lang=en)
* [https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf](https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf)
* [http://iphome.hhi.de/wiegand/assets/pdfs/2012_12_IEEE-HEVC-Overview.pdf](http://iphome.hhi.de/wiegand/assets/pdfs/2012_12_IEEE-HEVC-Overview.pdf)
* [http://phenix.int-evry.fr/jct/doc_end_user/current_document.php?id=7243](http://phenix.int-evry.fr/jct/doc_end_user/current_document.php?id=7243)
* [http://gentlelogic.blogspot.com.br/2011/11/exploring-h264-part-2-h264-bitstream.html](http://gentlelogic.blogspot.com.br/2011/11/exploring-h264-part-2-h264-bitstream.html)
软件:
* [https://ffmpeg.org/](https://ffmpeg.org/)
* [https://ffmpeg.org/ffmpeg-all.html](https://ffmpeg.org/ffmpeg-all.html)
* [https://ffmpeg.org/ffprobe.html](https://ffmpeg.org/ffprobe.html)
* [https://trac.ffmpeg.org/wiki/](https://trac.ffmpeg.org/wiki/)
* [https://software.intel.com/en-us/intel-video-pro-analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer)
* [https://medium.com/@mbebenita/av1-bitstream-analyzer-d25f1c27072b#.d5a89oxz8](https://medium.com/@mbebenita/av1-bitstream-analyzer-d25f1c27072b#.d5a89oxz8)
非-ITU 编解码器:
* [https://aomedia.googlesource.com/](https://aomedia.googlesource.com/)
* [https://github.com/webmproject/libvpx/tree/master/vp9](https://github.com/webmproject/libvpx/tree/master/vp9)
* [https://people.xiph.org/~xiphmont/demo/daala/demo1.shtml](https://people.xiph.org/~xiphmont/demo/daala/demo1.shtml)
* [https://people.xiph.org/~jm/daala/revisiting/](https://people.xiph.org/~jm/daala/revisiting/)
* [https://www.youtube.com/watch?v=lzPaldsmJbk](https://www.youtube.com/watch?v=lzPaldsmJbk)
* [https://fosdem.org/2017/schedule/event/om_av1/](https://fosdem.org/2017/schedule/event/om_av1/)
编码概念:
* [http://x265.org/hevc-h265/](http://x265.org/hevc-h265/)
* [http://slhck.info/video/2017/03/01/rate-control.html](http://slhck.info/video/2017/03/01/rate-control.html)
* [http://slhck.info/video/2017/02/24/vbr-settings.html](http://slhck.info/video/2017/02/24/vbr-settings.html)
* [http://slhck.info/video/2017/02/24/crf-guide.html](http://slhck.info/video/2017/02/24/crf-guide.html)
* [https://arxiv.org/pdf/1702.00817v1.pdf](https://arxiv.org/pdf/1702.00817v1.pdf)
* [https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors](https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors)
* [http://web.ece.ucdavis.edu/cerl/ReliableJPEG/Cung/jpeg.html](http://web.ece.ucdavis.edu/cerl/ReliableJPEG/Cung/jpeg.html)
* [http://www.adobe.com/devnet/adobe-media-server/articles/h264_encoding.html](http://www.adobe.com/devnet/adobe-media-server/articles/h264_encoding.html)
* [https://prezi.com/8m7thtvl4ywr/mp3-and-aac-explained/](https://prezi.com/8m7thtvl4ywr/mp3-and-aac-explained/)
测试用视频序列:
* [http://bbb3d.renderfarming.net/download.html](http://bbb3d.renderfarming.net/download.html)
* [https://www.its.bldrdoc.gov/vqeg/video-datasets-and-organizations.aspx](https://www.its.bldrdoc.gov/vqeg/video-datasets-and-organizations.aspx)
杂项:
* [http://stackoverflow.com/a/24890903](http://stackoverflow.com/a/24890903)
* [http://stackoverflow.com/questions/38094302/how-to-understand-header-of-h264](http://stackoverflow.com/questions/38094302/how-to-understand-header-of-h264)
* [http://techblog.netflix.com/2016/08/a-large-scale-comparison-of-x264-x265.html](http://techblog.netflix.com/2016/08/a-large-scale-comparison-of-x264-x265.html)
* [http://vanseodesign.com/web-design/color-luminance/](http://vanseodesign.com/web-design/color-luminance/)
* [http://www.biologymad.com/nervoussystem/eyenotes.htm](http://www.biologymad.com/nervoussystem/eyenotes.htm)
* [http://www.compression.ru/video/codec_comparison/h264_2012/mpeg4_avc_h264_video_codecs_comparison.pdf](http://www.compression.ru/video/codec_comparison/h264_2012/mpeg4_avc_h264_video_codecs_comparison.pdf)
* [http://www.csc.villanova.edu/~rschumey/csc4800/dct.html](http://www.csc.villanova.edu/~rschumey/csc4800/dct.html)
* [http://www.explainthatstuff.com/digitalcameras.html](http://www.explainthatstuff.com/digitalcameras.html)
* [http://www.hkvstar.com](http://www.hkvstar.com)
* [http://www.hometheatersound.com/](http://www.hometheatersound.com/)
* [http://www.lighterra.com/papers/videoencodingh264/](http://www.lighterra.com/papers/videoencodingh264/)
* [http://www.red.com/learn/red-101/video-chroma-subsampling](http://www.red.com/learn/red-101/video-chroma-subsampling)
* [http://www.slideshare.net/ManoharKuse/hevc-intra-coding](http://www.slideshare.net/ManoharKuse/hevc-intra-coding)
* [http://www.slideshare.net/mwalendo/h264vs-hevc](http://www.slideshare.net/mwalendo/h264vs-hevc)
* [http://www.slideshare.net/rvarun7777/final-seminar-46117193](http://www.slideshare.net/rvarun7777/final-seminar-46117193)
* [http://www.springer.com/cda/content/document/cda_downloaddocument/9783642147029-c1.pdf](http://www.springer.com/cda/content/document/cda_downloaddocument/9783642147029-c1.pdf)
* [http://www.streamingmedia.com/Articles/Editorial/Featured-Articles/A-Progress-Report-The-Alliance-for-Open-Media-and-the-AV1-Codec-110383.aspx](http://www.streamingmedia.com/Articles/Editorial/Featured-Articles/A-Progress-Report-The-Alliance-for-Open-Media-and-the-AV1-Codec-110383.aspx)
* [http://www.streamingmediaglobal.com/Articles/ReadArticle.aspx?ArticleID=116505&PageNum=1](http://www.streamingmediaglobal.com/Articles/ReadArticle.aspx?ArticleID=116505&PageNum=1)
* [http://yumichan.net/video-processing/video-compression/introduction-to-h264-nal-unit/](http://yumichan.net/video-processing/video-compression/introduction-to-h264-nal-unit/)
* [https://cardinalpeak.com/blog/the-h-264-sequence-parameter-set/](https://cardinalpeak.com/blog/the-h-264-sequence-parameter-set/)
* [https://cardinalpeak.com/blog/worlds-smallest-h-264-encoder/](https://cardinalpeak.com/blog/worlds-smallest-h-264-encoder/)
* [https://codesequoia.wordpress.com/category/video/](https://codesequoia.wordpress.com/category/video/)
* [https://developer.apple.com/library/content/technotes/tn2224/_index.html](https://developer.apple.com/library/content/technotes/tn2224/_index.html)
* [https://en.wikibooks.org/wiki/MeGUI/x264_Settings](https://en.wikibooks.org/wiki/MeGUI/x264_Settings)
* [https://en.wikipedia.org/wiki/Adaptive_bitrate_streaming](https://en.wikipedia.org/wiki/Adaptive_bitrate_streaming)
* [https://en.wikipedia.org/wiki/AOMedia_Video_1](https://en.wikipedia.org/wiki/AOMedia_Video_1)
* [https://en.wikipedia.org/wiki/Chroma_subsampling#/media/File:Colorcomp.jpg](https://en.wikipedia.org/wiki/Chroma_subsampling#/media/File:Colorcomp.jpg)
* [https://en.wikipedia.org/wiki/Cone_cell](https://en.wikipedia.org/wiki/Cone_cell)
* [https://en.wikipedia.org/wiki/File:H.264_block_diagram_with_quality_score.jpg](https://en.wikipedia.org/wiki/File:H.264_block_diagram_with_quality_score.jpg)
* [https://en.wikipedia.org/wiki/Inter_frame](https://en.wikipedia.org/wiki/Inter_frame)
* [https://en.wikipedia.org/wiki/Intra-frame_coding](https://en.wikipedia.org/wiki/Intra-frame_coding)
* [https://en.wikipedia.org/wiki/Photoreceptor_cell](https://en.wikipedia.org/wiki/Photoreceptor_cell)
* [https://en.wikipedia.org/wiki/Pixel_aspect_ratio](https://en.wikipedia.org/wiki/Pixel_aspect_ratio)
* [https://en.wikipedia.org/wiki/Presentation_timestamp](https://en.wikipedia.org/wiki/Presentation_timestamp)
* [https://en.wikipedia.org/wiki/Rod_cell](https://en.wikipedia.org/wiki/Rod_cell)
* [https://it.wikipedia.org/wiki/File:Pixel_geometry_01_Pengo.jpg](https://it.wikipedia.org/wiki/File:Pixel_geometry_01_Pengo.jpg)
* [https://leandromoreira.com.br/2016/10/09/how-to-measure-video-quality-perception/](https://leandromoreira.com.br/2016/10/09/how-to-measure-video-quality-perception/)
* [https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping](https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping)
* [https://softwaredevelopmentperestroika.wordpress.com/2014/02/11/image-processing-with-python-numpy-scipy-image-convolution/](https://softwaredevelopmentperestroika.wordpress.com/2014/02/11/image-processing-with-python-numpy-scipy-image-convolution/)
* [https://tools.ietf.org/html/draft-fuldseth-netvc-thor-03](https://tools.ietf.org/html/draft-fuldseth-netvc-thor-03)
* [https://www.encoding.com/android/](https://www.encoding.com/android/)
* [https://www.encoding.com/http-live-streaming-hls/](https://www.encoding.com/http-live-streaming-hls/)
* [https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm](https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm)
* [https://www.lifewire.com/cmos-image-sensor-493271](https://www.lifewire.com/cmos-image-sensor-493271)
* [https://www.linkedin.com/pulse/brief-history-video-codecs-yoav-nativ](https://www.linkedin.com/pulse/brief-history-video-codecs-yoav-nativ)
* [https://www.linkedin.com/pulse/video-streaming-methodology-reema-majumdar](https://www.linkedin.com/pulse/video-streaming-methodology-reema-majumdar)
* [https://www.vcodex.com/h264avc-intra-precition/](https://www.vcodex.com/h264avc-intra-precition/)
* [https://www.youtube.com/watch?v=9vgtJJ2wwMA](https://www.youtube.com/watch?v=9vgtJJ2wwMA)
* [https://www.youtube.com/watch?v=LFXN9PiOGtY](https://www.youtube.com/watch?v=LFXN9PiOGtY)
* [https://www.youtube.com/watch?v=Lto-ajuqW3w&list=PLzH6n4zXuckpKAj1_88VS-8Z6yn9zX_P6](https://www.youtube.com/watch?v=Lto-ajuqW3w&list=PLzH6n4zXuckpKAj1_88VS-8Z6yn9zX_P6)
* [https://www.youtube.com/watch?v=LWxu4rkZBLw](https://www.youtube.com/watch?v=LWxu4rkZBLw)
================================================
FILE: README-es.md
================================================
[🇺🇸](/README.md "English")
[🇨🇳](/README-cn.md "Simplified Chinese")
[🇯🇵](/README-ja.md "Japanese")
[🇮🇹](/README-it.md "Italian")
[🇰🇷](/README-ko.md "Korean")
[🇷🇺](/README-ru.md "Russian")
[🇧🇷](/README-pt.md "Portuguese")
[🇪🇸](/README-es.md "Spanish")
[](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)
# Introducción
Esto es una breve introducción a la tecnología de vídeo, principalmente dirigido a desarrolladores o ingenieros de software. La intención es que sea **fácil de aprender para cualquier persona**. La idea nació durante un [breve taller para personas nuevas en vídeo](https://docs.google.com/presentation/d/17Z31kEkl_NGJ0M66reqr9_uTG6tI5EDDVXpdPKVuIrs/edit#slide=id.p).
El objetivo es introducir algunos conceptos de vídeo digital con un **vocabulario simple, muchas figuras y ejemplos prácticos** en cuanto sea posible, y dejar disponible este conocimiento. Por favor, siéntete libre de enviar correcciones, sugerencias y mejoras.
Habrán **prácticas** que requiere tener instalado **docker** y este repositorio clonado.
```bash
git clone https://github.com/leandromoreira/digital_video_introduction.git
cd digital_video_introduction
./setup.sh
```
> **ADVERTENCIA**: cuando observes el comando `./s/ffmpeg` o `./s/mediainfo`, significa que estamos ejecutando la **versión contenerizada** del mismo, que a su vez incluye todos los requerimientos necesarios.
Todas las **prácticas deberán ser ejecutadas desde el directorio donde has clonado** este repositorio. Para ejecutar los **ejemplos de jupyter** deberás primero iniciar el servicio `./s/start_jupyter.sh`, copiar la URL y pegarla en tu navegador.
# Control de cambios
* Detalles sobre sistemas DRM
* Versión 1.0.0 liberada
* Traducción a Chino Simplificado
* Ejemplo de FFmpeg oscilloscope filter
* Traducción a Portugués (Brasil)
* Traducción a Español
# Tabla de contenidos
- [Introducción](#introducción)
- [Tabla de contenidos](#tabla-de-contenidos)
- [Terminología Básica](#terminología-básica)
* [Otras formas de codificar una imagen a color](#otras-formas-de-codificar-una-imagen-a-color)
* [Práctica: juguemos con una imagen y colores](#práctica-juguemos-con-una-imagen-y-colores)
* [DVD es DAR 4:3](#dvd-es-dar-43)
* [Práctica: Verifiquemos las propiedades del vídeo](#práctica-verifiquemos-las-propiedades-del-vídeo)
- [Eliminación de Redundancias](#eliminación-de-redundancias)
* [Colores, Luminancia y nuestros ojos](#colores-luinancia-y-nuestros-ojos)
+ [Modelo de color](#modelo-de-color)
+ [Conversiones entre YCbCr y RGB](#conversiones-entre-ycbcr-y-rgb)
+ [Chroma subsampling](#chroma-subsampling)
+ [Práctica: Observemos el histograma YCbCr](#práctica-observemos-el-histograma-ycbcr)
+ [Color, luma, luminancia, gamma](#color-luma-luminancia-gamma)
+ [Práctica: Observa la intensidad YCbCr](#práctica-observa-la-intensidad-ycbcr)
* [Tipos de fotogramas](#tipos-de-fotogramas)
+ [I Frame (intra, keyframe)](#i-frame-intra-keyframe)
+ [P Frame (predicted)](#p-frame-predicted)
- [Práctica: Un vídeo con un solo I-frame](#práctica-un-vídeo-con-un-solo-i-frame)
+ [B Frame (bi-predictive)](#b-frame-bi-predictive)
- [Práctica: Compara vídeos con B-frame](#práctica-compara-videos-con-b-frame)
+ [Resumen](#resumen)
* [Redundancia Temporal (inter prediction)](#redundancia-temporal-inter-prediction)
- [Práctica: Observa los vectores de movimiento](#práctica-observa-los-vectores-de-movimiento)
* [Redundancia Espacial (intra prediction)](#redundancia-espacial-intra-prediction)
- [Práctica: Observa las intra predictions](#práctica-observa-las-intra-predictions)
- [¿Cómo funciona un códec de vídeo?](#cómo-funciona-un-códec-de-vídeo)
* [¿Qué? ¿Por qué? ¿Cómo?](#qué-por-qué-cómo)
* [Historia](#historia)
+ [El nacimiento de AV1](#el-nacimiento-de-av1)
* [Un códec genérico](#un-códec-genérico)
* [Paso 1 - Particionado de imágenes](#paso-1---particionado-de-imágenes)
+ [Práctica: Verificar particiones](#práctica-verificar-particiones)
* [Paso 2 - Predicciones](#paso-2---predicciones)
* [Paso 3 - Transformación](#paso-3---transformación)
+ [Práctica: Descartar diferentes coeficientes](#práctica-descartar-diferentes-coeficientes)
* [Paso 4 - Cuantización](#paso-4---cuantización)
+ [Práctica: Cuantización](#práctica-cuantización)
* [Paso 5 - Codificación de la entropía](#paso-5---codificación-de-la-entropía)
+ [Codificación VLC](#codificación-vlc)
+ [Codificación aritmética](#codificación-aritmética)
+ [Práctica: CABAC vs CAVLC](#práctica-cabac-vs-cavlc)
* [Paso 6 - formato *bitstream*](#paso-6---formato-bitstream)
+ [H.264 bitstream](#h264-bitstream)
+ [Práctica: Inspeccionar el *bitstream* H.264](#práctica-inspeccionar-el-bitstream-h264)
* [Repaso](#repaso)
* [¿Cómo logra H.265 una mejor relación de compresión que H.264?](#cómo-logra-h265-una-mejor-relación-de-compresión-que-h264)
- [Online streaming](#online-streaming)
* [Arquitectura general](#arquitectura-general)
* [Descarga progresiva y streaming adaptativo](#descarga-progresiva-y-streaming-adaptativo)
* [Protección de contenido](#protección-de-contenido)
- [¿Cómo usar jupyter?](#cómo-usar-jupyter)
- [Conferencias](#conferencias)
- [Referencias](#referencias)
# Terminología Básica
Una **imagen** puede ser pensada como una **matriz 2D**. Si pensamos en **colores**, podemos extrapolar este concepto a que una imagen es una **matriz 3D** donde la **dimensión adicional** es usada para indicar la **información de color**.
Si elegimos representar esos colores usando los [colores primarios (rojo, verde y azul)](https://es.wikipedia.org/wiki/Color_primario), definimos tres planos: el primero para el **rojo**, el segundo para el **verde**, y el último para el **azul**.

Llamaremos a cada punto en esta matriz **un píxel** (del inglés, *picture element*). Un píxel representa la **intensidad** (usualmente un valor numérico) de un color dado. Por ejemplo, un **píxel rojo** significa 0 de verde, 0 de azul y el máximo de rojo. El **píxel de color rosa** puede formarse de la combinación de estos tres colores. Usando la representación numérica con un rango desde 0 a 255, el píxel rosa es definido como **Rojo=255, Verde=192 y Azul=203**.
> #### Otras formas de codificar una imagen a color
> Otros modelos pueden ser utilizados para representar los colores que forman una imagen. Por ejemplo, podemos usar una paleta indexada donde necesitaremos solamente un byte para representar cada píxel en vez de los 3 necesarios cuando usamos el modelo RGB. En un modelo como este, podemos utilizar una matriz 2D para representar nuestros colores, esto nos ayudaría a ahorrar memoria aunque tendríamos menos colores para representar.
>
> 
Observemos la figura que se encuentra a continuación. La primer cara muestra todos los colores utilizados. Mientras las demás muestran a intencidad de cada plano rojo, verde y azul (representado en escala de grises).

Podemos ver que el **color rojo** es el que **contribuye más** (las partes más brillantes en la segunda cara) mientras que en la cuarta cara observamos que el **color azul** su contribución se **limita a los ojos de Mario** y parte de su ropa. También podemos observar como **todos los planos no contribuyen demasiado** (partes oscuras) al **bigote de Mario**.
Cada intensidad de color requiere de una cantidad de bits para ser representada, esa cantidad es conocida como **bit depth**. Digamos que utilizamos **8 bits** (aceptando valores entre 0 y 255) por color (plano), entonces tendremos una **color depth** (profundidad de color) de **24 bits** (8 bits * 3 planos R/G/B), por lo que podemos inferir que tenemos disponibles 2^24 colores diferentes.
> **Es asombroso** aprender [como una imagen es capturada desde el Mundo a los bits](http://www.cambridgeincolour.com/tutorials/camera-sensors.htm).
Otra propiedad de una images es la **resolución**, que es el número de píxeles en una dimensión. Frecuentemente es presentado como ancho × alto (*width × height*), por ejemplo, la siguiente imagen es **4×4**.

> #### Práctica: juguemos con una imagen y colores
> Puedes [jugar con una imagen y colores](/image_as_3d_array.ipynb) usando [jupyter](#how-to-use-jupyter) (python, numpy, matplotlib, etc).
>
> También puedes aprender [cómo funcionan los filtros de imágenes (edge detection, sharpen, blur...)](/filters_are_easy.ipynb).
Trabajando con imágenes o vídeos, te encontrarás con otra propiedad llamada **aspect ratio** (relación de aspecto) que simplemente describe la relación proporcional que existe entre el ancho y alto de una imagen o píxel.
Cuando las personas dicen que una película o imagen es **16x9** usualmente se están refiriendo a la relación de aspecto de la pantalla (**Display Aspect Ratio (DAR)**), sin embargo podemos tener diferentes formas para cada píxel, a ello le llamamos relación de aspecto del píxel (**Pixel Aspect Ratio (PAR)**).


> #### DVD es DAR 4:3
> La resolución del formato de DVD es 704x480, igualmente mantiente una relación de aspecto (DAR) de 4:3 porque tiene PAR de 10:11 (704x10/480x11)
Finalmente, podemos definir a un **vídeo** como una **sucesión de *n* fotogramas** en el **tiempo**, la cual la podemos definir como otra dimensión, *n* es el *frame rate* o fotogramas por segundo (**FPS**, del inglés *frames per second*).

El número necesario de bits por segundo para mostrar un vídeo es llamado **bit rate**.
> bit rate = ancho * alto * bit depth * FPS
Por ejemplo, un vídeo de 30 fotogramas por segundo, 24 bits por píxel, 480x240 de resolución necesitaría de **82,944,000 bits por segundo** o 82.944 Mbps (30x480x240x24) si no queremos aplicar ningún tipo de compresión.
Cuando el **bit rate** es casi constante es llamado bit rate constante (**CBR**, del inglés *constant bit rate*), pero también puede ser variable por lo que lo llamaremos bit rate variable (**VBR**, del inglés *variable bit rate*).
> La siguiente gráfica muestra como utilizando VBR no se necesita gastar demasiados bits para un fotograma es negro.
>
> 
En otros tiempos, unos ingenieros idearon una técnica para duplicar la cantidad de fotogramas por segundo percividos en una pantalla **sin consumir ancho de banda adicional**. Esta técnica es conocida como **vídeo entrelazado** (*interlaced video*); básicamente envía la mitad de la pantalla en un "fotograma" y la otra mitad en el siguiente "fotograma".
Hoy en día, las pantallas representan imágenes principalmente utilizando la **técnica de escaneo progresivo** (*progressive vídeo*). El vídeo progresivo es una forma de mostrar, almacenar o transmitir imágenes en movimiento en la que todas las líneas de cada fotograma se dibujan en secuencia.

Ahora ya tenemos una idea acerca de cómo una **imagen** es representada digitalmente, cómo sus **colores** son codificados, cuántos **bits por segundo** necesitamos para mostrar un vídeo, si es constante (CBR) o variable (VBR), con una **resolución** dada a determinado **frame rate** y otros términos como entrelazado, PAR, etc.
> #### Práctica: Verifiquemos las propiedades del vídeo
> Puedes [verificar la mayoría de las propiedades mencionadas con ffmpeg o mediainfo.](https://github.com/leandromoreira/introduction_video_technology/blob/master/encoding_pratical_examples.md#inspect-stream)
# Eliminación de Redundancias
Aprendimos que no es factible utilizar vídeo sin ninguna compresión; **un solo vídeo de una hora** a una resolución de 720p y 30fps **requeriría 278GB<sup>*</sup>**. Dado que **utilizar únicamente algoritmos de compresión de datos sin pérdida** como DEFLATE (utilizado en PKZIP, Gzip y PNG), **no disminuiría** suficientemente el ancho de banda necesario, debemos encontrar otras formas de comprimir el vídeo.
> <sup>*</sup> Encontramos este número multiplicando 1280 x 720 x 24 x 30 x 3600 (ancho, alto, bits por píxel, fps y tiempo en segundos).
Para hacerlo, podemos **aprovechar cómo funciona nuestra visión**. Somos mejores para distinguir el brillo que los colores, las **repeticiones en el tiempo**, un vídeo contiene muchas imágenes con pocos cambios, y las **repeticiones dentro de la imagen**, cada fotograma también contiene muchas áreas que utilizan colores iguales o similares.
## Colores, Luminancia y nuestros ojos
Nuestros ojos son [más sensibles al brillo que a los colores](http://vanseodesign.com/web-design/color-luminance/), puedes comprobarlo por ti mismo, mira esta imagen.

Si no puedes ver que los colores de los **cuadrados A y B son idénticos** en el lado izquierdo, no te preocupes, es nuestro cerebro jugándonos una pasada para que **prestemos más atención a la luz y la oscuridad que al color**. Hay un conector, del mismo color, en el lado derecho para que nosotros (nuestro cerebro) podamos identificar fácilmente que, de hecho, son del mismo color.
> **Explicación simplista de cómo funcionan nuestros ojos**
> El [ojo es un órgano complejo](http://www.biologymad.com/nervoussystem/eyenotes.htm), compuesto por muchas partes, pero principalmente nos interesan las células de conos y bastones. El ojo [contiene alrededor de 120 millones de células de bastones y 6 millones de células de conos](https://en.wikipedia.org/wiki/Photoreceptor_cell).
>
> Para **simplificar absurdamente**, intentemos relacionar los colores y el brillo con las funciones de las partes del ojo. Los **[bastones](https://es.wikipedia.org/wiki/Bast%C3%B3n_(c%C3%A9lula)) son principalmente responsables del brillo**, mientras que los **[conos](https://es.wikipedia.org/wiki/Cono_(c%C3%A9lula)) son responsables del color**. Hay tres tipos de conos, cada uno con un pigmento diferente, a saber: [Tipo S (azul), Tipo M (verde) y Tipo L (rojos)](https://upload.wikimedia.org/wikipedia/commons/1/1e/Cones_SMJ2_E.svg).
>
> Dado que tenemos muchas más células de bastones (brillo) que células de conos (color), se puede inferir que somos más capaces de distinguir el contraste entre la luz y la oscuridad que los colores.
>
> 
>
> **Funciones de sensibilidad al contraste**
>
> Los investigadores de psicología experimental y muchas otras disciplinas han desarrollado varias teorías sobre la visión humana. Una de ellas se llama funciones de sensibilidad al contraste. Están relacionadas con el espacio y el tiempo de la luz y su valor se presenta en una luz inicial dada, cuánto cambio se requiere antes de que un observador informe que ha habido un cambio. Observa el plural de la palabra "función", esto se debe a que podemos medir las funciones de sensibilidad al contraste no solo con blanco y negro, sino también con colores. El resultado de estos experimentos muestra que en la mayoría de los casos nuestros ojos son más sensibles al brillo que al color.
Una vez que sabemos que somos más sensibles a la **luminancia** (*luma*, el brillo en una imagen), podemos aprovecharlo.
### Modelo de color
Primero aprendimos [cómo funcionan las imágenes en color](#terminología-básica) utilizando el **modelo RGB**, pero existen otros modelos también. De hecho, hay un modelo que separa la luminancia (brillo) de la crominancia (colores) y se conoce como **YCbCr**<sup>*</sup>.
> <sup>*</sup> hay más modelos que hacen la misma separación.
Este modelo de color utiliza **Y** para representar el brillo y dos canales de color, **Cb** (croma azul) y **Cr** (croma rojo). El [YCbCr](https://es.wikipedia.org/wiki/YCbCr) se puede derivar a partir de RGB y también se puede convertir de nuevo a RGB. Utilizando este modelo, podemos crear imágenes a todo color, como podemos ver a continuación.

### Conversiones entre YCbCr y RGB
Algunos pueden preguntarse, ¿cómo podemos producir **todos los colores sin usar el verde**?
Para responder a esta pregunta, vamos a realizar una conversión de RGB a YCbCr. Utilizaremos los coeficientes del **[estándar BT.601](https://en.wikipedia.org/wiki/Rec._601)** que fue recomendado por el **[grupo ITU-R<sup>*</sup>](https://en.wikipedia.org/wiki/ITU-R)**. El primer paso es **calcular la luminancia**, utilizaremos las constantes sugeridas por la ITU y sustituiremos los valores RGB.
```
Y = 0.299R + 0.587G + 0.114B
```
Una vez obtenida la luminancia, podemos **separar los colores** (croma azul y croma rojo):
```
Cb = 0.564(B - Y)
Cr = 0.713(R - Y)
```
Y también podemos **covertirlo de vuelta** e incluse obtener el **verde utilizando YCbCr**.
```
R = Y + 1.402Cr
B = Y + 1.772Cb
G = Y - 0.344Cb - 0.714Cr
```
> <sup>*</sup> Los grupos y estándares son comunes en el vídeo digital y suelen definir cuáles son los estándares, por ejemplo, [¿qué es 4K? ¿qué FPS debemos usar? ¿resolución? ¿modelo de color?](https://en.wikipedia.org/wiki/Rec._2020)
Generalmente, las **pantallas** (monitores, televisores, etc.) utilizan **solo el modelo RGB**, organizado de diferentes maneras, como se muestra a continuación:

### Chroma subsampling
Con la imagen representada en componentes de luminancia y crominancia, podemos aprovechar la mayor sensibilidad del sistema visual humano a la resolución de luminancia en lugar de la crominancia para eliminar selectivamente información. El **chroma subsampling** es la técnica de codificar imágenes utilizando **menor resolución para la crominancia que para la luminancia**.

¿Cuánto debemos reducir la resolución de la crominancia? Resulta que ya existen algunos esquemas que describen cómo manejar la resolución y la combinación (`color final = Y + Cb + Cr`).
Estos esquemas se conocen como sistemas de *subsampling* y se expresan como una relación de 3 partes: `a:x:y`, que define la resolución de la crominancia en relación con un bloque `a x 2` de píxeles de luminancia.
* `a` es la referencia de muestreo horizontal (generalmente 4).
* `x` es el número de muestras de crominancia en la primera fila de `a` píxeles (resolución horizontal en relación con `a`).
* `y` es el número de cambios de muestras de crominancia entre la primera y segunda filas de a píxeles.
> Una excepción a esto es el 4:1:0, que proporciona una sola muestra de crominancia dentro de cada bloque de `4 x 4` píxeles de resolución de luminancia.
Los esquemas comunes utilizados en códecs modernos son: **4:4:4** *(sin subsampling)*, **4:2:2, 4:1:1, 4:2:0, 4:1:0 y 3:1:1**.
> Puedes seguir algunas discusiones para [aprender más sobre el Chroma Subsampling](https://github.com/leandromoreira/digital_video_introduction/issues?q=YCbCr).
> **Fusión YCbCr 4:2:0**
>
> Aquí tienes una parte fusionada de una imagen utilizando YCbCr 4:2:0, observa que solo utilizamos 12 bits por píxel.
>
> 
Puedes ver la misma imagen codificada por los principales tipos de *chroma subsampling*, las imágenes en la primera fila son las YCbCr finales, mientras que la última fila de imágenes muestra la resolución de crominancia. Es realmente una gran ganancia con una pérdida tan pequeña.

Anteriormente habíamos calculado que necesitábamos [278 GB de almacenamiento para mantener un archivo de video con una hora de resolución de 720p y 30 fps](#eliminación-de-redundancias). Si usamos **YCbCr 4:2:0**, podemos reducir **este tamaño a la mitad (139 GB)**<sup>*</sup>, pero aún está lejos del ideal.
> <sup>*</sup> encontramos este valor multiplicando el ancho, alto, bits por píxel y fps. Anteriormente necesitábamos 24 bits, ahora solo necesitamos 12.
<br/>
> ### Práctica: Observemos el histograma YCbCr
> Puedes [observar el histograma YCbCr con ffmpeg](/encoding_pratical_examples.md#generates-yuv-histogram). Esta excena tiene una mayor contribución de azul la cual se muestra en el [histograma](https://es.wikipedia.org/wiki/Histograma).
>
> 
### Color, luma, luminancia, gamma
Mira este increíble video que explica qué es la luma y aprende sobre luminancia, gamma y color.
**Vídeo en Inglés**
[](http://www.youtube.com/watch?v=Ymt47wXUDEU)
> ### Práctica: Observa la intensidad YCbCr
> Puedes visualizar la intensidad Y (luma) para una línea específica de un vídeo utilizando el [filtro de osciloscopio de FFmpeg](https://ffmpeg.org/ffmpeg-filters.html#oscilloscope).
> ```bash
> ffplay -f lavfi -i 'testsrc2=size=1280x720:rate=30000/1001,format=yuv420p' -vf oscilloscope=x=0.5:y=200/720:s=1:c=1
> ```
> 
## Tipos de fotogramas
Ahora podemos continuar y tratar de eliminar la **redundancia en el tiempo**, pero antes de hacerlo, establezcamos algunas terminologías básicas. Supongamos que tenemos una película con 30 fps. Aquí están sus primeros 4 fotogramas.
  

Podemos ver **muchas repeticiones** dentro de los fotogramas, como **el fondo azul**, que no cambia del fotograma 0 al fotograma 3. Para abordar este problema, podemos **categorizarlos abstractamente** en tres tipos de fotogramas.
### I Frame (intra, keyframe)
Un *I-frame* (*reference*, *keyframe*, *intra*, en español fotograma o cuadro de referencia) es un **fotograma autónomo**. No depende de nada para ser renderizado, un *I-frame* se parece a una fotografía estática. Por lo general, el primer fotograma es un *I-frame*, pero veremos *I-frames* insertados regularmente entre otros tipos de fotogramas.

### P Frame (predicted)
Un *P-frame* (en español, fotograma o cuadro predictivo) aprovecha el hecho de que casi siempre l**a imagen actual se puede renderizar utilizando el fotograma anterior**. Por ejemplo, en el segundo fotograma, el único cambio fue que la pelota se movió hacia adelante. Podemos **reconstruir el fotograma 1, utilizando solo la diferencia y haciendo referencia al fotograma anterior**.
 <- 
> #### Práctica: Un vídeo con un solo I-frame
> Dado que un fotograma P utiliza menos datos, ¿por qué no podemos codificar un [video entero con un solo *I-frame* y todos los demás siendo *P-frames*?](/encoding_pratical_examples.md#1-i-frame-and-the-rest-p-frames)
>
> Después de codificar este vídeo, comienza a verlo y **busca una parte avanzada** del vídeo; notarás que **toma algo de tiempo** llegar realmente a esa parte. Esto se debe a que un ***P-frame* necesita un fotograma de referencia** (como un *I-frame*, por ejemplo) para renderizarse.
>
> Otra prueba rápida que puedes hacer es codificar un vídeo utilizando un solo *I-frame* y luego [codificarlo insertando un *I-frame* cada 2 segundos](/encoding_pratical_examples.md#1-i-frames-per-second-vs-05-i-frames-per-second) y **comprobar el tamaño de cada versión**.
### B Frame (bi-predictive)
¿Qué pasa si hacemos referencia a los fotogramas pasados y futuros para proporcionar una compresión aún mejor? Eso es básicamente lo que hace un *B-frame*.
 <-  -> 
> #### Práctica: Compara vídeos con B-frame
> Puedes generar dos versiones, una con *B-frames* y otra sin ningún [*B-frame* en absoluto](/encoding_pratical_examples.md#no-b-frames-at-all) y verificar el tamaño del archivo y la calidad.
### Resumen
Estos tipos de fotogramas se utilizan para **proporcionar una mejor compresión**. Veremos cómo sucede esto en la próxima sección, pero por ahora podemos pensar en un ***I-frame* como costoso, un *P-frame* como más económico y el más económico es el *B-frame*.**

## Redundancia temporal (inter prediction)
Vamos a explorar las opciones que tenemos para reducir las **repeticiones en el tiempo**, este tipo de redundancia se puede resolver con técnicas de **interpredicción**.
Intentaremos **utilizar menos bits** para codificar la secuencia de los fotogramas 0 y 1.

Una cosa que podemos hacer es una resta, simplemente **restamos el fotograma 1 del fotograma 0** y obtenemos lo que necesitamos para **codificar el residual**.

Pero, ¿qué pasa si te digo que hay un **método mejor** que utiliza incluso menos bits? Primero, tratemos el `fotograma 0` como una colección de particiones bien definidas y luego intentaremos emparejar los bloques del `fotograma 0` en el `fotograma 1`. Podemos pensar en ello como una **estimación de movimiento**.
> ### Wikipedia - compensación de movimiento por bloques
> "La **compensación de movimiento por bloques** divide el fotograma actual en bloques no superpuestos, y el vector de compensación de movimiento **indica de dónde provienen esos bloques** (una idea errónea común es que el fotograma anterior se divide en bloques no superpuestos y los vectores de compensación de movimiento indican hacia dónde se mueven esos bloques). Los bloques de origen suelen superponerse en el fotograma fuente. Algunos algoritmos de compresión de vídeo ensamblan el fotograma actual a partir de piezas de varios fotogramas previamente transmitidos."

Podríamos estimar que la pelota se movió de `x=0, y=25` a `x=6, y=26`, los valores de **x** e **y** son los **vectores de movimiento**. Un **paso adicional** que podemos dar para ahorrar bits es **codificar solo la diferencia del vector de movimiento** entre la última posición del bloque y la predicción, por lo que el vector de movimiento final sería `x=6 (6-0), y=1 (26-25)`.
> En una situación del mundo real, esta **pelota se dividiría en n particiones**, pero el proceso es el mismo.
Los objetos en el fotograma se **mueven de manera tridimensional**, la pelota puede volverse más pequeña cuando se mueve al fondo. Es normal que no encontremos una coincidencia perfecta con el bloque que intentamos encontrar. Aquí hay una vista superpuesta de nuestra estimación frente a la imagen real.

Pero podemos ver que cuando aplicamos la **estimación de movimiento**, los **datos a codificar son más pequeños** que cuando simplemente usamos técnicas de fotograma delta.

> ### Cómo se vería la compensación de movimiento real
> Esta técnica se aplica a todos los bloques, muy a menudo una pelota se dividiría en más de un bloque.
> 
> Fuente: https://web.stanford.edu/class/ee398a/handouts/lectures/EE398a_MotionEstimation_2012.pdf
Puedes [experimentar con estos conceptos utilizando jupyter](/frame_difference_vs_motion_estimation_plus_residual.ipynb).
> #### Práctica: Observa los vectores de movimiento
> Podemos [generar un vídeo con la interpredicción (vectores de movimiento) con ffmpeg.](/encoding_pratical_examples.md#generate-debug-video)
>
>  with ffmpeg")
>
> O podemos usar el [Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer) (que es de pago, pero hay una versión de prueba gratuita que te limita a trabajar solo con los primeros 10 fotogramas).
>
> 
## Redundancia Espacial (intra prediction)
Si analizamos **cada fotograma** en un vídeo, veremos que también hay **muchas áreas que están correlacionadas**.

Vamos a través de un ejemplo. Esta escena está compuesta principalmente por colores azules y blancos.

Este es un `I-frame` y **no podemos usar fotogramas anteriores** para hacer una predicción, pero aún podemos comprimirlo. Codificaremos la selección de bloques en rojo. Si **observamos a sus vecinos**, podemos **estimar** que hay una **tendencia de colores a su alrededor**.

Vamos a **predecir** que el fotograma continuará **extendiendo los colores verticalmente**, lo que significa que los colores de los **píxeles desconocidos mantendrán los valores de sus vecinos**.

Nuestra **predicción puede ser incorrecta**, por esa razón necesitamos aplicar esta técnica (**intra prediction**) y luego **restar los valores reales**, lo que nos da el bloque residual, lo que resulta en una matriz mucho más compresible en comparación con la original.

Existen muchos tipos diferentes de este tipo de predicción. La que se muestra aquí es una forma de predicción plana recta, donde los píxeles de la fila superior del bloque se copian fila por fila dentro del bloque. La predicción plana también puede tener una componente angular, donde se utilizan píxeles tanto de la izquierda como de la parte superior para ayudar a predecir el bloque actual. Y también existe la predicción DC, que implica tomar el promedio de las muestras justo arriba y a la izquierda del bloque.
> #### Práctica: Observa las intra predictions
> Puedes [generar con ffmpeg un vídeo con macrobloques y sus predicciones.](/encoding_pratical_examples.md#generate-debug-video) Por favor verifica la documentación de ffmpeg para comprender el [significado de cada bloque de color](https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors?version=7#AnalyzingMacroblockTypes).
>
>  with ffmpeg")
>
> O puedes usar [Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer) (que es de pago, pero hay una versión de prueba gratuita que te limita a trabajar solo con los primeros 10 fotogramas).
>
> 
# ¿Cómo funciona un códec de vídeo?
## ¿Qué? ¿Por qué? ¿Cómo?
**¿Qué?** Es un software o hardware que comprime o descomprime vídeo digital. **¿Por qué?** El mercado y la sociedad demandan vídeos de mayor calidad con ancho de banda o almacenamiento limitados. ¿Recuerdas cuando [calculamos el ancho de banda necesario](#terminología-básica) para 30 fps, 24 bits por píxel, resolución de un vídeo de 480x240? Fueron **82.944 Mbps** sin aplicar compresión. Es la única forma de entregar HD/FullHD/4K en televisores e Internet. **¿Cómo?** Echaremos un vistazo breve a las técnicas principales aquí.
> **CODEC vs Contenedor**
>
> Un error común que cometen los principiantes es confundir el CODEC de vídeo digital y el [contenedor de vídeo digital](https://en.wikipedia.org/wiki/Container_format). Podemos pensar en los **contenedores** como un formato que contiene metadatos del vídeo (y posiblemente también audio), y el **vídeo comprimido** se puede ver como su contenido.
>
> Por lo general, la extensión de un archivo de vídeo define su contenedor de vídeo. Por ejemplo, el archivo `video.mp4` probablemente es un contenedor **[MPEG-4 Part 14](https://en.wikipedia.org/wiki/MP4_file_format)** y un archivo llamado `video.mkv` probablemente es un **[matroska](https://en.wikipedia.org/wiki/Matroska)**. Para estar completamente seguro sobre el códec y el formato del contenedor, podemos usar [ffmpeg o mediainfo](/encoding_pratical_examples.md#inspect-stream).
## Historia
Antes de adentrarnos en el funcionamiento interno de un códec genérico, echemos un vistazo atrás para comprender un poco mejor algunos códecs de vídeo antiguos.
El códec de vídeo [H.261](https://en.wikipedia.org/wiki/H.261) nació en 1990 (técnicamente en 1988) y fue diseñado para trabajar con **tasas de datos de 64 kbit/s**. Ya utilizaba ideas como el *chroma subsampling*, el macrobloque, etc. En el año 1995, se publicó el estándar de códec de vídeo **H.263** y continuó siendo extendido hasta 2001.
En 2003 se completó la primera versión de **H.264/AVC**. En el mismo año, **On2 Technologies** (anteriormente conocida como Duck Corporation) lanzó su códec de vídeo como una compresión de vídeo con pérdida **libre de regalías** llamada **VP3**. En 2008, **Google compró** esta empresa y lanzó **VP8** en el mismo año. En diciembre de 2012, Google lanzó **VP9**, el cual es **compatible con aproximadamente el ¾ del mercado de navegadores** (incluyendo móviles).
**[AV1](https://en.wikipedia.org/wiki/AOMedia_Video_1)** es un nuevo códec de vídeo **libre de regalías** y de código abierto que está siendo diseñado por la [Alliance for Open Media (AOMedia)](http://aomedia.org/), la cual está compuesta por **empresas como Google, Mozilla, Microsoft, Amazon, Netflix, AMD, ARM, NVidia, Intel y Cisco**, entre otras. La **primera versión**, 0.1.0 del códec de referencia, **se publicó el 7 de abril de 2016**.

> #### El nacimiento de AV1
>
> A principios de 2015, Google estaba trabajando en [VP10](https://en.wikipedia.org/wiki/VP9#Successor:_from_VP10_to_AV1), Xiph (Mozilla) estaba trabajando en [Daala](https://xiph.org/daala/) y Cisco lanzó su códec de video libre de regalías llamado [Thor](https://tools.ietf.org/html/draft-fuldseth-netvc-thor-03).
>
> Entonces, MPEG LA anunció por primera vez límites anuales para HEVC (H.265) y tarifas 8 veces más altas que H.264, pero pronto cambiaron las reglas nuevamente:
> * **sin límite anual**,
> * **tarifa por contenido** (0.5% de los ingresos) y
> * **tarifas por unidad aproximadamente 10 veces más altas que H.264**.
>
> La [Alliance for Open Media](http://aomedia.org/about/) fue creada por empresas de fabricantes de hardware (Intel, AMD, ARM, Nvidia, Cisco), distribución de contenido (Google, Netflix, Amazon), mantenimiento de navegadores (Google, Mozilla) y otros.
>
> Las empresas tenían un objetivo común, un códec de vídeo libre de regalías, y así nació AV1 con una [licencia de patente mucho más simple](http://aomedia.org/license/patent/). **Timothy B. Terriberry** hizo una excelente presentación, que es la fuente de esta sección, sobre la [concepción de AV1, el modelo de licencia y su estado actual](https://www.youtube.com/watch?v=lzPaldsmJbk).
>
> Te sorprenderá saber que puedes **analizar el códec AV1 a través de tu navegador**, visita https://arewecompressedyet.com/analyzer/
>
> 
>
> PD: Si deseas aprender más sobre la historia de los códecs, debes comprender los conceptos básicos detrás de las [patentes de compresión de vídeo](https://www.vcodex.com/video-compression-patents/).
## Un códec genérico
Vamos a presentar los **mecanismos principales detrás de un códec de vídeo genérico**, pero la mayoría de estos conceptos son útiles y se utilizan en códecs modernos como VP9, AV1 y HEVC. Asegúrate de entender que vamos a simplificar las cosas MUCHO. A veces usaremos un ejemplo real (principalmente H.264) para demostrar una técnica.
## Paso 1 - Particionado de imágenes
El primer paso es **dividir el fotograma** en varias **particiones, sub-particiones** y más allá.

Pero, **¿por qué?**. Hay muchas razones, por ejemplo, cuando dividimos la imagen, podemos trabajar las predicciones de manera más precisa, utilizando pequeñas particiones para las partes móviles y particiones más grandes para un fondo estático.
Por lo general, los códecs **organizan estas particiones** en *slices* (o *tiles*), macrobloques (o unidades de árbol de codificación) y muchas sub-particiones. El tamaño máximo de estas particiones varía, HEVC establece 64x64 mientras que AVC usa 16x16, pero las sub-particiones pueden alcanzar tamaños de 4x4.
¿Recuerdas que aprendimos cómo se **clasifican los fotogramas**? Bueno, también puedes **aplicar esas ideas a los bloques**, por lo tanto, podemos tener *I-Slice*, *B-Slice*, *I-Macroblock*, etc.
> ### Práctica: Verificar particiones
> Podemos usar [Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer) (que es de pago, pero hay una versión de prueba gratuita que limita el trabajo a los primeros 10 fotogramas). Aquí se analizan las [particiones de VP90](/encoding_pratical_examples.md#transcoding).
>
> 
## Paso 2 - Predicciones
Una vez que tenemos las particiones, podemos hacer predicciones sobre ellas. Para la [inter prediction](#redundancia-temporal-inter-prediction), necesitamos **enviar los vectores de movimiento y el residual**, y para la [intra prediction](#redundancia-espacial-intra-prediction), **enviaremos la dirección de predicción y el residual** también.
## Paso 3 - Transformación
Después de obtener el bloque residual (`partición predicha - partición real`), podemos **transformarlo** de tal manera que nos permita saber cuáles **píxeles podemos descartar** mientras mantenemos la **calidad general**. Hay algunas transformaciones para este comportamiento específico.
Aunque existen [otras transformaciones](https://en.wikipedia.org/wiki/List_of_Fourier-related_transforms#Discrete_transforms), examinaremos más de cerca la transformada coseno discreta (DCT). Las principales características de la [**DCT**](https://en.wikipedia.org/wiki/Discrete_cosine_transform) son las siguientes:
* **convierte** bloques de **píxeles** en bloques del mismo tamaño de los **coeficientes de frecuencia**.
* **compacta** la energía, lo que facilita eliminar la redundancia espacial.
* es **reversible**, es decir, se puede revertir a píxeles.
> El 2 de febrero de 2017, Cintra, R. J. y Bayer, F. M publicaron su artículo [DCT-like Transform for Image Compression Requires 14 Additions Only](https://arxiv.org/abs/1702.00817).
No te preocupes si no comprendiste los beneficios de cada punto, intentaremos realizar algunos experimentos para ver el valor real de esto.
Tomemos el siguiente **bloque de píxeles** (8x8):

Lo cual se representa en la siguiente imagen de bloque (8x8):

Cuando **aplicamos la DCT** a este bloque de píxeles, obtenemos el **bloque de coeficientes** (8x8):

Y si representamos este bloque de coeficientes, obtendremos esta imagen:

Como puedes ver, no se parece en nada a la imagen original, podríamos notar que el **primer coeficiente** es muy diferente de todos los demás. Este primer coeficiente se conoce como el coeficiente DC, que representa **todas las muestras** en el array de entrada, algo **similar a un promedio**.
Este bloque de coeficientes tiene una propiedad interesante, que es que separa los componentes de alta frecuencia de los de baja frecuencia.

En una imagen, la **mayor parte de la energía** se concentrará en las [**frecuencias más bajas**](https://web.archive.org/web/20150129171151/https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm), por lo que si transformamos una imagen en sus componentes de frecuencia y **eliminamos los coeficientes de frecuencia más altos**, podemos r**educir la cantidad de datos necesarios** para describir la imagen sin sacrificar demasiada calidad de imagen.
> La frecuencia significa cuán rápido cambia una señal.
Intentemos aplicar el conocimiento que adquirimos en la prueba, convirtiendo la imagen original a su dominio de frecuencia (bloque de coeficientes) usando DCT y luego descartando parte (67%) de los coeficientes menos importantes, principalmente la parte inferior derecha.
Primero, lo convertimos a su **dominio de frecuencia**.

A continuación, descartamos parte (67%) de los coeficientes, principalmente la parte inferior derecha.

Finalmente, reconstruimos la imagen a partir de este bloque de coeficientes descartados (recuerda, debe ser reversible) y la comparamos con la original.

Como podemos ver, se asemeja a la imagen original pero introduce muchas diferencias con respecto a la original. **Descartamos el 67.1875%** y aún así pudimos obtener algo similar a la original. Podríamos descartar de manera más inteligente los coeficientes para tener una mejor calidad de imagen, pero eso es el próximo tema.
> **Cada coeficiente se forma utilizando todos los píxeles**
>
> Es importante destacar que cada coeficiente no se asigna directamente a un solo píxel, sino que es una suma ponderada de todos los píxeles. Este gráfico asombroso muestra cómo se calcula el primer y segundo coeficiente, utilizando pesos únicos para cada índice.
>
> 
>
> Fuente: https://web.archive.org/web/20150129171151/https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm
>
> También puedes intentar [visualizar la DCT mirando una imagen simple](/dct_better_explained.ipynb) formada sobre la base de la DCT. Por ejemplo, aquí tienes cómo [se forma el carácter A](https://en.wikipedia.org/wiki/Discrete_cosine_transform#Example_of_IDCT) utilizando el peso de cada coeficiente.
>
> 
<br/>
> ### Práctica: Descartar diferentes coeficientes
> Puedes jugar con la [transformación DCT](/uniform_quantization_experience.ipynb).
## Paso 4 - Cuantización
Cuando descartamos algunos de los coeficientes en el último paso (transformación), de alguna manera hicimos una forma de cuantización. En este paso es donde elegimos perder información (la **parte perdida**) o, en términos simples, **cuantificamos coeficientes para lograr la compresión**.
¿Cómo podemos cuantificar un bloque de coeficientes? Un método simple sería una cuantificación uniforme, donde tomamos un bloque, lo **dividimos por un valor único** (10) y redondeamos este valor.

¿Cómo podemos **revertir** (re-cuantificar) este bloque de coeficientes? Podemos hacerlo **multiplicando el mismo valor** (10) por el que lo dividimos inicialmente.

Este **enfoque no es el mejor** porque no tiene en cuenta la importancia de cada coeficiente. Podríamos usar una **matriz de cuantizadores** en lugar de un solo valor. Esta matriz puede explotar la propiedad de la DCT, cuantificando más los coeficientes de la parte inferior derecha y menos los de la parte superior izquierda. El [JPEG utiliza un enfoque similar](https://www.hdm-stuttgart.de/~maucher/Python/MMCodecs/html/jpegUpToQuant.html); puedes consultar el [código fuente para ver esta matriz](https://github.com/google/guetzli/blob/master/guetzli/jpeg_data.h#L40).
> ### Práctica: Cuantización
> Puedes experimentar con la [cuantización aquí](/dct_experiences.ipynb).
## Paso 5 - Codificación de la entropía
Después de cuantificar los datos (bloques/slices/fotogramas de imágenes), aún podemos comprimirlos de manera sin pérdida. Hay muchas formas (algoritmos) de comprimir datos. Vamos a experimentar brevemente con algunos de ellos. Para una comprensión más profunda, puedes leer el increíble libro [Understanding Compression: Data Compression for Modern Developers](https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/).
### Codificación VLC
Supongamos que tenemos una secuencia de símbolos: **a**, **e**, **r** y **t**; y su probabilidad (de 0 a 1) se representa en esta tabla.
| | a | e | r | t |
|-------------|-----|-----|------|-----|
| probabilidad| 0.3 | 0.3 | 0.2 | 0.2 |
Podemos asignar códigos binarios únicos (preferiblemente pequeños) a los más probables y códigos más grandes a los menos probables.
| | a | e | r | t |
|-------------|-----|-----|------|-----|
| probabilidad | 0.3 | 0.3 | 0.2 | 0.2 |
| código binario | 0 | 10 | 110 | 1110 |
Comprimamos la secuencia **eat**, asumiendo que gastaríamos 8 bits para cada símbolo, gastaríamos **24 bits** sin ninguna compresión. Pero en caso de que reemplacemos cada símbolo por su código, podemos ahorrar espacio.
El primer paso es codificar el símbolo **e**, que es `10`, y el segundo símbolo es **a**, que se agrega (no en un sentido matemático) como `[10][0]`, y finalmente el tercer símbolo **t**, lo que hace que nuestra secuencia de bits comprimida final sea `[10][0][1110]` o `1001110`, que solo requiere **7 bits** (3.4 veces menos espacio que el original).
Observa que cada código debe ser un código único y prefijado [Huffman puede ayudarte a encontrar estos números](https://en.wikipedia.org/wiki/Huffman_coding). Aunque tiene algunos problemas, todavía hay [códecs de vídeo que ofrecen este método](https://en.wikipedia.org/wiki/Context-adaptive_variable-length_coding) y es el algoritmo para muchas aplicaciones que requieren compresión.
Tanto el codificador como el decodificador **deben conocer** la tabla de símbolos con sus códigos, por lo tanto, también debes enviar la tabla.
### Codificación aritmética
Supongamos que tenemos una secuencia de símbolos: **a**, **e**, **r**, **s** y **t**; y su probabilidad se representa en esta tabla.
| | a | e | r | s | t |
|--------------|-----|-----|------|------|-----|
| probabilidad | 0.3 | 0.3 | 0.15 | 0.05 | 0.2 |
Con esta tabla en mente, podemos construir rangos que contengan todos los posibles símbolos ordenados por los más frecuentes.

Ahora codifiquemos la secuencia **eat**, tomamos el primer símbolo **e**, que se encuentra dentro del subrango **0.3 a 0.6** (pero no se incluye), y tomamos este subrango y lo dividimos nuevamente utilizando las mismas proporciones utilizadas anteriormente, pero dentro de este nuevo rango.

Continuemos codificando nuestra secuencia **eat**, ahora tomamos el segundo símbolo **a**, que está dentro del nuevo subrango **0.3 a 0.39**, y luego tomamos nuestro último símbolo **t** y hacemos el mismo proceso nuevamente y obtenemos el último subrango **0.354 a 0.372**.

Solo necesitamos elegir un número dentro del último subrango **0.354 a 0.372**, elijamos **0.36**, pero podríamos elegir cualquier número dentro de este subrango. Con **solo** este número podremos recuperar nuestra secuencia original **eat**. Si lo piensas, es como si estuviéramos dibujando una línea dentro de rangos de rangos para codificar nuestra secuencia.

El **proceso inverso** (también conocido como decodificación) es igualmente sencillo, con nuestro número **0.36** y nuestro rango original, podemos realizar el mismo proceso pero ahora usando este número para revelar la secuencia codificada original detrás de este número.
Con el primer rango, notamos que nuestro número encaja en la porción, por lo tanto, es nuestro primer símbolo, ahora dividimos este subrango nuevamente, haciendo el mismo proceso que antes, y notamos que **0.36** encaja en el símbolo **a**, y después de repetir el proceso llegamos al último símbolo **t** (formando nuestra secuencia original codificada *eat*).
Tanto el codificador como el decodificador **tienen que conocer** la tabla de probabilidades de los símbolos, por lo tanto, también debes enviar la tabla.
¿Bastante ingenioso, verdad? Las personas son realmente inteligentes para idear una solución así. Algunos [códecs de vídeo utilizan esta técnica](https://en.wikipedia.org/wiki/Context-adaptive_binary_arithmetic_coding) (o al menos la ofrecen como opción).
La idea es comprimir sin pérdidas el flujo de bits cuantificados. Sin duda, este artículo carece de muchos detalles, razones, compensaciones, etc. Pero [puedes aprender más](https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/). Los códecs más nuevos están tratando de utilizar diferentes[ algoritmos de codificación de entropía como ANS.](https://en.wikipedia.org/wiki/Asymmetric_Numeral_Systems)
> ### Práctica: CABAC vs CAVLC
> Puedes [generar 2 streams, uno con CABAC y otro con CAVLC](https://github.com/leandromoreira/introduction_video_technology/blob/master/encoding_pratical_examples.md#cabac-vs-cavlc) y **compara cuánto tiempo** toma generar cada uno de ellos, como así también el **tamaño final**.
## Paso 6 - formato *bitstream*
Después de completar todos estos pasos, necesitamos **empaquetar los fotogramas comprimidos y el contexto de estos pasos**. Debemos informar explícitamente al decodificador sobre **las decisiones tomadas por el codificador**, como el bit depth, el espacio de color, la resolución, la información de predicciones (vectores de movimiento, dirección de intra prediction), perfil, nivel, fps, tipo de fotograma, número de fotograma y mucho más.
Vamos a estudiar superficialmente el *bitstream* de H.264. Nuestro primer paso es [generar un *bitstream* H.264<sup>*</sup> mínimo](/encoding_pratical_examples.md#generate-a-single-frame-h264-bitstream), lo podemos hacer utilizando nuestro propio repositorio y [ffmpeg](http://ffmpeg.org/).
```
./s/ffmpeg -i /files/i/minimal.png -pix_fmt yuv420p /files/v/minimal_yuv420.h264
```
> <sup>*</sup> fmpeg agrega, por defecto, todos los parámetros de codificación como un **SEI NAL**, pronto definiremos qué es un NAL.
Este comando generará un *bitstream* H.264 en bruto con un **único fotograma**, de 64x64 píxeles, con espacio de color yuv420, utilizando la siguiente imagen como fotograma.
> 
### H.264 bitstream
El estándar AVC (H.264) define que la información se enviará en **macro frames** (en el sentido de red), llamadas **[NAL](https://en.wikipedia.org/wiki/Network_Abstraction_Layer)** (Network Abstraction Layer). El objetivo principal de la NAL es proporcionar una representación de vídeo "amigable para la red". Este estándar debe funcionar en televisores (basados en transmisión) y en Internet (basado en paquetes), entre otros.

Hay un **[marcador de sincronización](https://en.wikipedia.org/wiki/Frame_synchronization)** para definir los límites de las unidades NAL. Cada marcador de sincronización tiene un valor de `0x00 0x00 0x01`, excepto el primero, que es `0x00 0x00 0x00 0x01`. Si ejecutamos el comando **hexdump** en el flujo de bits H.264 generado, podemos identificar al menos tres NAL al principio del archivo.

Como mencionamos antes, el decodificador necesita conocer no solo los datos de la imagen, sino también los detalles del vídeo, el fotograma, los colores, los parámetros utilizados y otros. El primer byte de cada NAL define su categoría y **tipo**.
| NAL type id | Description |
|--- |---|
| 0 | Undefined |
| 1 | Coded slice of a non-IDR picture |
| 2 | Coded slice data partition A |
| 3 | Coded slice data partition B |
| 4 | Coded slice data partition C |
| 5 | **IDR** Coded slice of an IDR picture |
| 6 | **SEI** Supplemental enhancement information |
| 7 | **SPS** Sequence parameter set |
| 8 | **PPS** Picture parameter set |
| 9 | Access unit delimiter |
| 10 | End of sequence |
| 11 | End of stream |
| ... | ... |
Normalmente, el primer NAL de un flujo de bits es un **SPS** (Sequence Parameter Set). Este tipo de NAL es responsable de proporcionar información sobre las variables de codificación generales como **perfil**, **nivel**, **resolución** y otros.
Si omitimos el primer marcador de sincronización, podemos decodificar el **primer byte** para saber qué **tipo de NAL** es el primero.
Por ejemplo, el primer byte después del marcador de sincronización es `01100111`, donde el primer bit (`0`) es el campo **forbidden_zero_bit**, los siguientes 2 bits (`11`) nos indican el campo **nal_ref_idc**, que indica si este NAL es un campo de referencia o no, y los siguientes 5 bits (`00111`) nos informan sobre el campo **nal_unit_type**, en este caso, es un NAL de tipo **SPS** (7).
El segundo byte (`binary=01100100, hex=0x64, dec=100`) de un NAL SPS es el campo **profile_idc**, que muestra el perfil que el codificador ha utilizado. En este caso, hemos utilizado el **[high profile](https://en.wikipedia.org/wiki/H.264/MPEG-4_AVC#Profiles)**. Además, el tercer byte contiene varias banderas que determinan el perfil exacto (como restringido o progresivo). Pero en nuestro caso, el tercer byte es 0x00 y, por lo tanto, el codificador ha utilizado solo el *high profile*.

Cuando leemos la especificación de flujo de bits H.264 para un NAL SPS, encontraremos muchos valores para el **parameter name**, **category** y **description**. Por ejemplo, veamos los campos `pic_width_in_mbs_minus_1` y `pic_height_in_map_units_minus_1`.
| Parameter name | Category | Description |
|--- |---|---|
| pic_width_in_mbs_minus_1 | 0 | ue(v) |
| pic_height_in_map_units_minus_1 | 0 | ue(v) |
> **ue(v)**: unsigned integer [Exp-Golomb-coded](https://ghostarchive.org/archive/JBwdI)
Si realizamos algunos cálculos con el valor de estos campos, obtendremos la **resolución**. Podemos representar `1920 x 1080` usando un valor de `pic_width_in_mbs_minus_1` de `119 ((119 + 1) * macroblock_size = 120 * 16 = 1920)`, nuevamente ahorrando espacio, en lugar de codificar `1920`, lo hicimos con `119`.
Si continuamos examinando nuestro vídeo creado con una vista binaria (por ejemplo, `xxd -b -c 11 v/minimal_yuv420.h264`), podemos saltar al último NAL que es el propio cuadro.

Podemos ver los valores de sus primeros 6 bytes: `01100101 10001000 10000100 00000000 00100001 11111111`. Como ya sabemos, el primer byte nos dice qué tipo de NAL es, en este caso, (`00101`) es un **IDR Slice (5)** y podemos inspeccionarlo más a fondo:

Utilizando la información de la especificación, podemos decodificar qué tipo de slice (**slice_type**), el número de fotograma (**frame_num**) y otros campos importantes.
Para obtener los valores de algunos campos (`ue(v), me(v), se(v) o te(v)`), debemos decodificarlos utilizando un decodificador especial llamado [Exponential-Golomb](https://ghostarchive.org/archive/JBwdI). Este método es **muy eficiente para codificar valores variables**, sobre todo cuando hay muchos valores predeterminados.
> Los valores de **slice_type** y **frame_num** de este vídeo son 7 (I slice) y 0 (el primer fotograma).
Podemos ver el ***bitstream* como un protocolo**, y si deseas aprender más sobre este *bitstream*, consulta la especificación [ITU H.264]( http://www.itu.int/rec/T-REC-H.264-201610-I). Aquí tienes un diagrama macro que muestra dónde reside los datos de la imagen (YUV comprimido).

Podemos explorar otros *bitstreams* como el [*bitstreams* VP9](https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf), [H.265 (HEVC)](http://handle.itu.int/11.1002/1000/11885-en?locatt=format:pdf) o incluso nuestro **nuevo mejor amigo** el [*bitstream* AV1](https://medium.com/@mbebenita/av1-bitstream-analyzer-d25f1c27072b#.d5a89oxz8), [¿se ven todos similares, no?](http://www.gpac-licensing.com/2016/07/12/vp9-av1-bitstream-format/) Pero una vez que aprendes uno, puedes entender fácilmente los demás.
> ### Práctica: Inspeccionar el *bitstream* H.264
> Podemos [generar un video de un solo fotograma](https://github.com/leandromoreira/introduction_video_technology/blob/master/encoding_pratical_examples.md#generate-a-single-frame-video) y usar [mediainfo](https://en.wikipedia.org/wiki/MediaInfo) para inspeccionar su *bitstream* H.264. De hecho, incluso puedes ver el [código fuente que analiza el *bitstream* h264 (AVC)](https://github.com/MediaArea/MediaInfoLib/blob/master/Source/MediaInfo/Video/File_Avc.cpp).
>
> 
>
> También podemos utilizar el [Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer) que es de pago, pero hay una versión de prueba gratuita que limita el trabajo a solo los primeros 10 fotogramas, lo cual está bien para fines de aprendizaje.
>
> 
## Repaso
Es evidente que muchos de los **códecs modernos utilizan el mismo modelo que aprendimos**. De hecho, echemos un vistazo al diagrama de bloques del códec de vídeo Thor, que contiene todos los pasos que estudiamos. La idea es que ahora deberías ser capaz de al menos comprender mejor las innovaciones y los documentos de esta área.

Anteriormente calculamos que necesitaríamos [139GB de almacenamiento para mantener un archivo de vídeo de una hora a una resolución de 720p y 30 fps](#chroma-subsampling) si utilizamos las técnicas que aprendimos aquí, como **inter e intra predictions, transformación, cuantización, codificación de entropía y otras**. Podemos lograrlo, asumiendo que estamos utilizando **0.031 bit por píxel**, el mismo vídeo de calidad perceptible **requiriendo solo 367.82MB en lugar de 139GB** de almacenamiento.
> Elegimos usar **0.031 bit por píxel** basándonos en el ejemplo de vídeo proporcionado aquí.
## ¿Cómo logra H.265 una mejor relación de compresión que H.264?
Ahora que sabemos más sobre cómo funcionan los códecs, es fácil entender cómo los nuevos códecs pueden ofrecer mayores resoluciones con menos bits.
Compararemos AVC y HEVC, ten en cuenta que casi siempre hay un equilibrio entre más ciclos de CPU (complejidad) y la velocidad de compresión.
HEVC tiene más opciones de **particiones** (y **sub-particiones**) que AVC, más **direcciones/ángulos de intra predictions**, **codificación de entropía mejorada** y más, todas estas mejoras hicieron que H.265 fuera capaz de comprimir un 50% más que H.264.

# Online streaming
## Arquitectura general

[TODO]
## Descarga progresiva y streaming adaptativo


[TODO]
## Protección de contenido
Podemos utilizar **un sistema de tokens simple** para proteger el contenido. El usuario sin un token intenta solicitar un video y la CDN (Red de Entrega de Contenido, del inglés *Content Delivery Network*) le prohíbe el acceso, mientras que un usuario con un token válido puede reproducir el contenido, funciona de manera bastante similar a la mayoría de los sistemas de autenticación web.

El uso exclusivo de este sistema de tokens todavía permite que un usuario descargue un video y lo distribuya. Es aquí dónde, los sistemas de **DRM (gestión de derechos digitales, del inglés *digital rights management*)** se pueden utilizar para tratar de evitar esto.

En los sistemas de producción del mundo real, a menudo se utilizan ambas técnicas para proporcionar autorización y autenticación.
### DRM
#### Soluciones principales
* FPS - [**FairPlay Streaming**](https://developer.apple.com/streaming/fps/)
* PR - [**PlayReady**](https://www.microsoft.com/playready/)
* WV - [**Widevine**](http://www.widevine.com/)
#### ¿Qué es?
DRM significa [*Digital Rights Management* o gestión de derechos digitales](https://sander.saares.eu/categories/drm-is-not-a-black-box/), es una forma de proporcionar protección de derechos de autor para medios digitales, como vídeo y audio digitales. Aunque se utiliza en muchos lugares, no es universalmente aceptado.
#### ¿Por qué?
Los creadores de contenido (principalmente estudios) desean proteger su propiedad intelectual contra la copia para prevenir la redistribución no autorizada de medios digitales.
#### ¿Cómo?
Vamos a describir una forma abstracta y genérica de DRM de manera muy simplificada.
Dado un **contenido C1** (por ejemplo, un vídeo en streaming HLS o DASH), con un **reproductor P1** (por ejemplo, shaka-clappr, exo-player o iOS) en un **dispositivo D1** (por ejemplo, un teléfono inteligente, una televisión, una tableta o una computadora de escritorio/portátil) utilizando un **sistema DRM llamado DRM1** (widevine, playready o FairPlay).
El contenido C1 está encriptado con una **clave simétrica K1** del sistema DRM1, generando el **contenido encriptado C'1**.

El reproductor P1, de un dispositivo D1, tiene dos claves (asimétricas), una **clave privada PRK1** (esta clave está protegida<sup>1</sup> y solo la conoce **D1**) y una **clave pública PUK1**.
> <sup>1</sup>Protegida: esta protección puede ser **mediante hardware**, por ejemplo, esta clave puede almacenarse dentro de un chip especial (de solo lectura) que funciona como [una caja negra](https://en.wikipedia.org/wiki/Black_box) para proporcionar la descifrado, o **por software** (menos seguro), el sistema DRM proporciona medios para saber qué tipo de protección tiene un dispositivo dado.
Cuando el **reproductor P1 quiere reproducir** el **contenido C'1**, debe interactuar con el **sistema DRM DRM1**, proporcionando su clave pública **PUK1**. El sistema DRM DRM1 devuelve la **clave K1 cifrada** con la clave pública del cliente **PUK1**. En teoría, esta respuesta es algo que **solo D1 es capaz de descifrar**.
`K1P1D1 = enc(K1, PUK1)`
**P1** utiliza su sistema local de DRM (puede ser un [SOC](https://en.wikipedia.org/wiki/System_on_a_chip), hardware especializado o software), este sistema es **capaz de descifrar** el contenido utilizando su clave privada PRK1, puede descifrar **la clave simétrica K1 de la K1P1D1** y **reproducir C'1**. En el mejor caso, las claves no se exponen a través de la RAM.
```
K1 = dec(K1P1D1, PRK1)
P1.play(dec(C'1, K1))
```

# ¿Cómo usar jupyter?
Asegúrate de tener **docker instalado**, ejecuta `./s/start_jupyter.sh` y sigue las instrucciones en la consola.
# Conferencias
* [DEMUXED](https://demuxed.com/) - puedes [mirar las ediciones pasadas aquí.](https://www.youtube.com/channel/UCIc_DkRxo9UgUSTvWVNCmpA).
# Referencias
El contenido más rico se encuentra aquí, es de donde se extrajo, basó o inspiró toda la información que vimos en este texto. Puedes profundizar tu conocimiento con estos increíbles enlaces, libros, vídeos, etc.
Cursos online y tutoriales:
* https://www.coursera.org/learn/digital/
* https://people.xiph.org/~tterribe/pubs/lca2012/auckland/intro_to_video1.pdf
* https://xiph.org/video/vid1.shtml
* https://xiph.org/video/vid2.shtml
* https://wiki.multimedia.cx
* https://mahanstreamer.net
* http://slhck.info/ffmpeg-encoding-course
* http://www.cambridgeincolour.com/tutorials/camera-sensors.htm
* http://www.slideshare.net/vcodex/a-short-history-of-video-coding
* http://www.slideshare.net/vcodex/introduction-to-video-compression-13394338
* https://developer.android.com/guide/topics/media/media-formats.html
* http://www.slideshare.net/MadhawaKasun/audio-compression-23398426
* http://inst.eecs.berkeley.edu/~ee290t/sp04/lectures/02-Motion_Compensation_girod.pdf
Libros:
* https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/ref=sr_1_1?s=books&ie=UTF8&qid=1486395327&sr=1-1
* https://www.amazon.com/H-264-Advanced-Video-Compression-Standard/dp/0470516925
* https://www.amazon.com/High-Efficiency-Video-Coding-HEVC/dp/3319068946
* https://www.amazon.com/Practical-Guide-Video-Audio-Compression/dp/0240806301/ref=sr_1_3?s=books&ie=UTF8&qid=1486396914&sr=1-3&keywords=A+PRACTICAL+GUIDE+TO+VIDEO+AUDIO
* https://www.amazon.com/Video-Encoding-Numbers-Eliminate-Guesswork/dp/0998453005/ref=sr_1_1?s=books&ie=UTF8&qid=1486396940&sr=1-1&keywords=jan+ozer
Material de *onboarding*:
* https://github.com/Eyevinn/streaming-onboarding
* https://howvideo.works/
* https://www.aws.training/Details/eLearning?id=17775
* https://www.aws.training/Details/eLearning?id=17887
* https://www.aws.training/Details/Video?id=24750
Especificaciones de *Bitstream*:
* http://www.itu.int/rec/T-REC-H.264-201610-I
* http://www.itu.int/ITU-T/recommendations/rec.aspx?rec=12904&lang=en
* https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf
* http://iphome.hhi.de/wiegand/assets/pdfs/2012_12_IEEE-HEVC-Overview.pdf
* http://phenix.int-evry.fr/jct/doc_end_user/current_document.php?id=7243
* http://gentlelogic.blogspot.com.br/2011/11/exploring-h264-part-2-h264-bitstream.html
* https://forum.doom9.org/showthread.php?t=167081
* https://forum.doom9.org/showthread.php?t=168947
Software:
* https://ffmpeg.org/
* https://ffmpeg.org/ffmpeg-all.html
* https://ffmpeg.org/ffprobe.html
* https://mediaarea.net/en/MediaInfo
* https://www.jongbel.com/
* https://trac.ffmpeg.org/wiki/
* https://software.intel.com/en-us/intel-video-pro-analyzer
* https://medium.com/@mbebenita/av1-bitstream-analyzer-d25f1c27072b#.d5a89oxz8
Códecs *Non-ITU*:
* https://aomedia.googlesource.com/
* https://github.com/webmproject/libvpx/tree/master/vp9
* https://ghostarchive.org/archive/0W0d8 (was: https://people.xiph.org/~xiphmont/demo/daala/demo1.shtml)
* https://people.xiph.org/~jm/daala/revisiting/
* https://www.youtube.com/watch?v=lzPaldsmJbk
* https://fosdem.org/2017/schedule/event/om_av1/
* https://jmvalin.ca/papers/AV1_tools.pdf
Conceptos de codificación:
* http://x265.org/hevc-h265/
* http://slhck.info/video/2017/03/01/rate-control.html
* http://slhck.info/video/2017/02/24/vbr-settings.html
* http://slhck.info/video/2017/02/24/crf-guide.html
* https://arxiv.org/pdf/1702.00817v1.pdf
* https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors
* http://web.ece.ucdavis.edu/cerl/ReliableJPEG/Cung/jpeg.html
* http://www.adobe.com/devnet/adobe-media-server/articles/h264_encoding.html
* https://prezi.com/8m7thtvl4ywr/mp3-and-aac-explained/
* https://blogs.gnome.org/rbultje/2016/12/13/overview-of-the-vp9-video-codec/
* https://videoblerg.wordpress.com/2017/11/10/ffmpeg-and-how-to-use-it-wrong/
Ejemplos de vídeos para pruebas:
* http://bbb3d.renderfarming.net/download.html
* https://www.its.bldrdoc.gov/vqeg/video-datasets-and-organizations.aspx
Miscelánea:
* https://github.com/Eyevinn/streaming-onboarding
* http://stackoverflow.com/a/24890903
* http://stackoverflow.com/questions/38094302/how-to-understand-header-of-h264
* http://techblog.netflix.com/2016/08/a-large-scale-comparison-of-x264-x265.html
* http://vanseodesign.com/web-design/color-luminance/
* http://www.biologymad.com/nervoussystem/eyenotes.htm
* http://www.compression.ru/video/codec_comparison/h264_2012/mpeg4_avc_h264_video_codecs_comparison.pdf
* https://web.archive.org/web/20100728070421/http://www.csc.villanova.edu/~rschumey/csc4800/dct.html (was: http://www.csc.villanova.edu/~rschumey/csc4800/dct.html)
* http://www.explainthatstuff.com/digitalcameras.html
* http://www.hkvstar.com
* http://www.hometheatersound.com/
* http://www.lighterra.com/papers/videoencodingh264/
* http://www.red.com/learn/red-101/video-chroma-subsampling
* http://www.slideshare.net/ManoharKuse/hevc-intra-coding
* http://www.slideshare.net/mwalendo/h264vs-hevc
* http://www.slideshare.net/rvarun7777/final-seminar-46117193
* http://www.springer.com/cda/content/document/cda_downloaddocument/9783642147029-c1.pdf
* http://www.streamingmedia.com/Articles/Editorial/Featured-Articles/A-Progress-Report-The-Alliance-for-Open-Media-and-the-AV1-Codec-110383.aspx
* http://www.streamingmediaglobal.com/Articles/ReadArticle.aspx?ArticleID=116505&PageNum=1
* http://yumichan.net/video-processing/video-compression/introduction-to-h264-nal-unit/
* https://cardinalpeak.com/blog/the-h-264-sequence-parameter-set/
* https://cardinalpeak.com/blog/worlds-smallest-h-264-encoder/
* https://codesequoia.wordpress.com/category/video/
* https://developer.apple.com/library/content/technotes/tn2224/_index.html
* https://en.wikibooks.org/wiki/MeGUI/x264_Settings
* https://en.wikipedia.org/wiki/Adaptive_bitrate_streaming
* https://en.wikipedia.org/wiki/AOMedia_Video_1
* https://en.wikipedia.org/wiki/Chroma_subsampling#/media/File:Colorcomp.jpg
* https://en.wikipedia.org/wiki/Cone_cell
* https://en.wikipedia.org/wiki/File:H.264_block_diagram_with_quality_score.jpg
* https://en.wikipedia.org/wiki/Inter_frame
* https://en.wikipedia.org/wiki/Intra-frame_coding
* https://en.wikipedia.org/wiki/Photoreceptor_cell
* https://en.wikipedia.org/wiki/Pixel_aspect_ratio
* https://en.wikipedia.org/wiki/Presentation_timestamp
* https://en.wikipedia.org/wiki/Rod_cell
* https://it.wikipedia.org/wiki/File:Pixel_geometry_01_Pengo.jpg
* https://leandromoreira.com.br/2016/10/09/how-to-measure-video-quality-perception/
* https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping
* https://softwaredevelopmentperestroika.wordpress.com/2014/02/11/image-processing-with-python-numpy-scipy-image-convolution/
* https://tools.ietf.org/html/draft-fuldseth-netvc-thor-03
* https://www.encoding.com/android/
* https://www.encoding.com/http-live-streaming-hls/
* https://web.archive.org/web/20150129171151/https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm
* https://www.lifewire.com/cmos-image-sensor-493271
* https://www.linkedin.com/pulse/brief-history-video-codecs-yoav-nativ
* https://www.linkedin.com/pulse/video-streaming-methodology-reema-majumdar
* https://www.vcodex.com/h264avc-intra-precition/
* https://www.youtube.com/watch?v=9vgtJJ2wwMA
* https://www.youtube.com/watch?v=LFXN9PiOGtY
* https://www.youtube.com/watch?v=Lto-ajuqW3w&list=PLzH6n4zXuckpKAj1_88VS-8Z6yn9zX_P6
* https://www.youtube.com/watch?v=LWxu4rkZBLw
* https://web.stanford.edu/class/ee398a/handouts/lectures/EE398a_MotionEstimation_2012.pdf
* https://sander.saares.eu/categories/drm-is-not-a-black-box/
================================================
FILE: README-it.md
================================================
[🇺🇸](/README.md "English")
[🇨🇳](/README-cn.md "Simplified Chinese")
[🇯🇵](/README-ja.md "Japanese")
[🇮🇹](/README-it.md "Italian")
[🇰🇷](/README-ko.md "Korean")
[🇷🇺](/README-ru.md "Russian")
[🇧🇷](/README-pt.md "Portuguese")
[🇪🇸](/README-es.md "Spanish")
[](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)
# Introduzione
Una introduzione alla tecnologia del video digitale, destinata prevalentemente a ingegneri o sviluppatori software, ma sufficientemente **semplice per consentire a chiunque di imparare**. L'idea è nata durante un [workshop sulla tecnologia video per principianti](https://docs.google.com/presentation/d/17Z31kEkl_NGJ0M66reqr9_uTG6tI5EDDVXpdPKVuIrs/edit#slide=id.p).
L'obiettivo è introdurre alcuni importanti concetti sul video digitale, usando un **vocabolario semplice, molte figure e esempi pratici** quando possibile, e rendere questa conoscenza disponibile ovunque. Non esitare a inviare correzioni o suggerimenti!
Ci saranno delle sezioni con esempi e esercizi che richiedono di avere Docker installato e una copia di questa repository scaricata in locale.
```bash
git clone https://github.com/leandromoreira/digital_video_introduction.git
cd digital_video_introduction
./setup.sh
```
> **ATTENZIONE**: quando vedi scritto un comando come `./s/ffmpeg` oppure `./s/mediainfo`, significa che si sta eseguendo quel programma all'interno di un container Docker, che contiene tutte le dipendenze richieste per funzionare.
**Tutti gli esempi pratici devono essere eseguiti dalla repository locale che hai scaricato**. Per gli esempi di jupyter, devi avviare il server eseguendo `./s/start_jupyter.sh`, e aprire in un browser l'URL mostrato.
# Indice
- [Introduzione](#introduzione)
- [Indice](#indice)
- [Terminologia base](#terminologia-di-base)
* [Altri modi per codificare un'immagine a colori](#altri-modi-per-codificare-unimmagine-a-colori)
* [Esercizio: sperimentare con le immagini e i colori](#esercizio-sperimentare-con-le-immagini-e-i-colori)
* [I DVD hanno un DAR 4:3](#i-dvd-hanno-un-dar-43)
* [Esercizio: controllare le proprietà di un file video](#esercizio-controllare-le-propriet%C3%A0-di-un-file-video)
- [Rimozione delle informazioni ridondanti](#rimozione-delle-informazioni-ridondanti)
* [I colori, la luminanza e i nostri occhi](#i-colori-la-luminanza-e-i-nostri-occhi)
+ [Modelli di colore](#modelli-di-colore)
+ [Conversione tra YCbCr e RGB](#conversione-tra-ycbcr-e-rgb)
+ [Sottocampionamento della crominanza](#sottocampionamento-della-crominanza)
+ [Esercizio: vedere l'istogramma YCbCr](#esercizio-vedere-listogramma-ycbcr)
* [Tipi di fotogramma](#tipi-di-fotogramma)
+ [I Frame (intra, keyframe)](#i-frame-intra-keyframe)
+ [P Frame (predicted)](#p-frame-predicted)
- [Esercizio: un video con un solo I-frame](#esercizio-un-video-con-un-solo-i-frame)
+ [B Frame (bi-predictive)](#b-frame-bi-predictive)
- [Esercizio: confronta video con e senza B-frame](#esercizio-confronta-video-con-e-senza-b-frame)
+ [Sommario](#sommario)
* [Ridondanza temporale (predizione inter-frame)](#ridondanza-temporale-predizione-inter-frame)
- [Esercizio: vedere i vettori di movimento](#esercizio-vedere-i-vettori-del-moto)
* [Ridondanza spaziale (predizione intra-frame)](#ridondanza-spaziale-predizione-intra-frame)
- [Esercizio: vedere la predizione intra-frame](#esercizio-vedere-la-predizione-intra-frame)
- [Come funziona un codec video?](#come-funziona-un-codec-video)
* [Cosa? Perché? Come?](#cosa-perch%C3%A9-come)
* [Storia](#storia)
+ [La nascita di AV1](#la-nascita-di-av1)
* [Un codec generico](#un-codec-generico)
* [1° passo - partizionamento dell'immagine](#1-passo---partizionamento-dellimmagine)
+ [Esercizio: vedere le partizioni](#esercizio-vedere-le-partizioni)
* [2° passo - predizioni](#2-passo---predizioni)
* [3° passo - trasformazione](#3-passo---trasformazione)
+ [Esercizio: scartare diversi coefficienti](#esercizio-scartare-diversi-coefficienti)
* [4° passo - quantizzazione](#4-passo---quantizzazione)
+ [Esercizio: quantizzazione](#esercizio-quantizzazione)
* [5° passo - codifica dell'entropia](#5-passo---codifica-dellentropia)
+ [Codifica VLC](#codifica-vlc)
+ [Codifica aritmetica](#codifica-aritmetica)
+ [Esercizio: CABAC vs CAVLC](#esercizio-cabac-vs-cavlc)
* [6° passo - formato bitstream](#6-passo---formato-bitstream)
+ [Bitstream di H.264](#bitstream-di-h264)
+ [Esercizio: ispezionare il bitstream H.264](#esercizio-ispezionare-il-bitstream-h264)
* [Ripasso](#ripasso)
* [Come fa H.265 a comprimere più di H.264?](#come-fa-h265-a-comprimere-pi%C3%B9-di-h264)
- [Streaming online](#streaming-online)
* [Architettura generale](#architettura-generale)
* [Download progressivo e download adattivo](#download-progressivo-vs-streaming-adattivo)
* [Protezione dei contenuti](#protezione-dei-contenuti)
- [Come usare jupyter](#come-usare-jupyter)
- [Conferenze](#conferenze)
- [Riferimenti](#riferimenti)
# Terminologia di base
Un'**immagine** può essere pensata come una **matrice bidimensionale**. Se pensiamo al fatto che ci sono anche i **colori**, possiamo evolvere questa idea pensando l'immagine come una **matrice tridimensionale**, dove la **terza dimensione** (o le dimensioni aggiuntive) viene utilizzata per memorizzare le **informazioni sul colore**.
Se scegliamo di rappresentare i colori utilizzando i [tre colori primari (rosso, verde e blu)](https://it.wikipedia.org/wiki/Colore_primario), possiamo definire tre piani: uno per il **rosso**, uno per il **verde** e uno per il **blu**.

Chiameremo ogni punto di questa matrice un **pixel** (dall'inglese *picture element*). Un pixel rappresenta l'**intensità** (che in genere è un valore numerico) di un determinato colore. Ad esempio, un **pixel rosso** può essere rappresentato con zero verde, zero blu e il massimo rosso possibile. Un **pixel di colore rosa** può essere formato con una combinazione di tre colori: usando una rappresentazione numerica in un intervallo che va da 0 a 255, il rosa può essere definito come **Rosso=255, Verde=192 e Blu=203**.
> #### Altri modi per codificare un'immagine a colori
> Si possono utilizzare molti altri modelli per rappresentare i colori che compongono un'immagine. Potremmo ad esempio utilizzare una tavolozza di colori (con un indice associato a ciascun colore), rappresentando poi i pixel colorati con l'indice del colore nella tavolozza. In questo modo ogni pixel richiederebbe soltanto un byte di memoria, anziché tre byte come nel caso del modello RGB. Il vantaggio di un modello a _palette_ è quello di occupare meno memoria, riducendo però il numero di colori a disposizione.
>
> 
Per fare un esempio, guarda le immagini qui sotto. La prima figura è completamente colorata. Le altre mostrano rispettivamente i piani (o canali) rosso, verde e blu, in scala di grigi.

Possiamo notare che il **colore rosso** è quello che **contribuisce di più** a creare il colore finale (si notino le parti più chiare nella seconda figura, sotto "Red"), mentre il **colore blu** contribuisce prevalentemente a rendere il colore degli **occhi di Mario** e parte dei suoi vestiti (ultima figura a destra). Si può anche notare come nessun canale RGB dia un forte contributo a realizzare i colori scuri, come i baffi di Mario.
Ogni colore richiede un certo numero di bit per essere rappresentato. Questo valore si chiama **profondità di bit**. Se ad esempio decidiamo di utilizzare **8 bit** per canale RGB (potendo rappresentare quindi valori che vanno da 0 a 255), avremo una **profondità di colore** di **8 * 3 = 24 bit**, per un totale di 2<sup>24</sup> colori diversi.
> **È molto interessante** imparare [come un'immagine viene catturata dal mondo reale e convertita in bit](http://www.cambridgeincolour.com/tutorials/camera-sensors.htm).
Un'altra proprietà delle immagini è la **risoluzione**, cioè il numero di pixel per ogni dimensione. Viene spesso indicata utilizzando la notazione "larghezza x altezza", ad esempio **4x4** per indicare un'immagine larga 4 pixel e alta 4 pixel.

> #### Esercizio: sperimentare con le immagini e i colori
> Puoi esercitarti [con le immagini e i colori](/image_as_3d_array.ipynb) utilizzando [jupyter](#how-to-use-jupyter) (python, numpy, matplotlib, ecc.).
>
> Puoi anche imparare [come funzionano i filtri (rilevazione dei contorni, nitidezza, sfocatura, ecc.)](/filters_are_easy.ipynb).
Lavorando con immagini e video incontreremo anche una proprietà che si chiama **rapporto d'aspetto**, che descrive la relazione di proporzionalità tra la larghezza e l'altezza di un'immagine o di un pixel.
Quando senti dire che un film o un'immagine è in 16:9, di solito ci si riferisce al **rapporto d'aspetto dell'immagine** (anche chiamato *DAR*, dall'inglese *Display Aspect Ratio*). Tuttavia, anche i pixel possono avere forme diverse, per cui si parla di rapporto d'aspetto anche per i pixel (più precisamente *PAR*, dall'inglese **Pixel Aspect Ratio**).


> #### I DVD hanno un DAR 4:3
> Anche se la risoluzione video di un DVD è 704x480, il rapporto d'aspetto è 4:3, perché i pixel hanno un PAR di 10:11 (704x10/480x11).
Infine, possiamo definire un **video** come una **successione di *n* fotogrammi per unità di tempo**. Il valore *n* è la frequenza dei fotogrammi (*frame rate* in inglese), spesso abbreviata con FPS (fotogrammi al secondo).

Il numero dei bit necessari per rappresentare un secondo di video è chiamato **bitrate**, o meno comunemente *frequenza di bit*.
> bitrate = larghezza * altezza * profondità di bit * fotogrammi al secondo
Ad esempio, un video con 30 fotogrammi al secondo e 24 bit per pixel, con una risoluzione di 480x240 ha bisogno (se non utilizziamo nessun tipo di compressione) di **82˙944˙000 bit al secondo**, cioè 82,944 Mbps (il calcolo è 30x480x240x24 byte).
Quando il **bitrate** rimane quasi uguale nel tempo, parliamo di **bitrate costante (CBR)**, mentre quando varia di **bitrate variabile (VBR)**.
> Il grafico mostra il bitrate di un video codificato in modalità VBR limitata (dai valori min e max). Si può notare come il bitrate è più basso quando il fotogramma è nero.
>
> 
Molto tempo fa è stata inventata una tecnica per **raddoppiare il framerate percepito** da chi sta guardando un video, **senza richiedere l'utilizzo di dati aggiuntivi**. Questa tecnica è conosciuta come **interlacciamento**. Semplificando, in un video interlacciato metà dell'immagine è contenuta in un fotogramma, mentre l'altra metà nel fotogramma successivo.
Al giorno d'oggi la maggior parte degli schermi utilizza la **scansione progressiva**, una tecnica per cui la trasmissione, memorizzazione e visualizzazione delle immagini in movimento avviene disegnando progressivamente e in sequenza tutte le linee che compongono il fotogramma.

A questo punto abbiamo un'idea di come un'**immagine** viene rappresentata digitalmente, come i suoi **colori** sono disposti, quanti **bit al secondo** sono richiesti per mostrare un video, con bitrate costante (CBR) o variabile (VBR), e cosa sono i concetti di **risoluzione**, **framerate** (FPS), interlacciamento, PAR, ecc.
> #### Esercizio: controllare le proprietà di un file video
> Puoi vedere la maggior parte delle proprietà che abbiamo appena visto [usando ffmpeg oppure mediainfo](https://github.com/leandromoreira/introduction_video_technology/blob/master/encoding_pratical_examples.md#inspect-stream) su un file video.
# Rimozione delle informazioni ridondanti
Abbiamo imparato che memorizzare un video senza nessuna compressione è impraticabile. **Un'ora di video** a una risoluzione 720p30 (720 linee verticali con scansione progressiva, con 30 fotogrammi al secondo) **richiederebbe 278 GB**<sup>\*</sup>. Dato che utilizzando i normali algoritmi di compressione lossless (come DEFLATE, utilizzato in PKZIP, Gzip e PNG) non otterremmo una diminuzione significativa della dimensione del file, dobbiamo trovare altri modi per comprimere il video.
> <sup>\*</sup>Si può ottenere questo risultato moltiplicando 1280 x 720 x 24 x 30 x 3600 (larghezza, altezza, bit per pixel, fps e durata in secondi).
Per farlo possiamo sfruttare il modo in cui la nostra vista funziona. Possiamo infatti sfruttare il fatto che l'occhio riesce a distinguere meglio diverse luminosità che diversi colori. È importante anche considerare le **ripetizioni nel tempo** (un video contiene molti fotogrammi molto simili, con poche differenze) e le **ripetizioni all'interno delle immagini** (un fotogramma può contenere aree che hanno lo stesso colore o un colore molto simile).
## I colori, la luminanza e i nostri occhi
I nostri occhi sono [più sensibili alla luminosità che ai colori](http://vanseodesign.com/web-design/color-luminance/), puoi vederlo tu stesso con l'immagine seguente.

Se nell'immagine a sinistra non riesci a vedere che **i colori dei quadrati A e B sono identici**, è normale. È il nostro cervello che ci induce a **prestare più attenzione alla luce e al buio che al colore**. Nell'immagine a destra i due quadrati sono collegati con una striscia dello stesso colore dei quadrati, così riusciamo molto più facilmente a vedere che A e B sono effettivamente dello stesso colore.
> **Spiegazione semplificata del funzionamento dell'occhio**
>
> L'occhio [è un organo complesso](http://www.biologymad.com/nervoussystem/eyenotes.htm): è composto da molte parti ma siamo prevalentemente interessati alle cellule fotorecettrici, i coni e i bastoncelli. L'occhio contiene [circa 120 milioni di bastoncelli e 6 milioni di coni](https://it.wikipedia.org/wiki/Fotorecettore).
>
> In modo **molto semplificato**, proviamo ad associare colori e luminosità alle parti degli occhi. I **[bastoncelli](https://it.wikipedia.org/wiki/Bastoncello) sono quasi solo responsabili della luminosità**, mentre **[i coni](https://it.wikipedia.org/wiki/Cellula_cono) sono responsabili del colore**. I coni sono di tre tipi, ognuno con un pigmento diverso e sensibile a un colore diverso. In particolare, esistono [coni S (blu), coni M (verde) e coni L (rosso)](https://upload.wikimedia.org/wikipedia/commons/1/1e/Cones_SMJ2_E.svg).
>
> Dato che l'occhio ha molti più bastoncelli (luminosità) che coni (colore), si può dedurre che l'occhio è più bravo a distinguere il chiaro dallo scuro che i colori.
>
> 
>
> **Curve di sensibilità al contrasto**
>
> I ricercatori di psicologia sperimentale (e altri campi) hanno sviluppato molte teorie sull'occhio umano. Una di queste si chiama "curve di sensibilità al contrasto", e studia le funzioni che descrivono il legame spaziotemporale della luce, il loro valore data una certa quantità di luce iniziale, e quanto cambiamento è richiesto prima che un osservatore si accorga che c'è stato un cambiamento. Il plurale della parola "curve" è dovuto al fatto che possiamo usare queste funzioni non solo per studiare il bianco e il nero, ma anche i colori. Il risultato di questi esperimenti mostra che nella maggior parte dei casi l'occhio è più sensibile alla luce che al colore.
Ora che sappiamo che siamo più sensibili alla lumimanza (la luminosità di un'immagine), possiamo provare a sfruttare questo fatto.
### Modelli di colore
Abbiamo inizialmente imparato [come colorare le immagini](#terminologia-di-base) usando il **modello RGB**, ma esistono altri modelli. Ad esempio esiste un modello che separa la luminanza (la luminosità) dalla crominanza (i colori), ed è conosciuto come **YCbCr**<sup>\*</sup>.
> <sup>\*</sup>esistono anche altri modelli che fanno la stessa distinzione.
Questo modello di colore utilizza la **Y** per rappresentare la luminosità, e due canali **Cb** (*chroma blue*) e **Cr** (*chroma red*) per i colori. Il modello [YCbCr](https://it.wikipedia.org/wiki/YCbCr) può essere derivato da RGB, e viceversa. Con i tre canali possiamo creare immagini pienamente colorate, come mostrato nell'immagine:

### Conversione tra YCbCr e RGB
Qualcuno si potrebbe chiedere: "**come è possibile produrre tutti i colori senza usare il verde?**"
Per rispondere a questa domanda, proveremo a fare una conversione da RGB a YCbCr. Utilizzeremo i coefficienti definiti dallo **[standard BT.601](https://it.wikipedia.org/wiki/BT.601)**, una delle raccomandazioni del gruppo [ITU-R](https://it.wikipedia.org/wiki/ITU-R)<sup>\*</sup>.
Il primo passo è **calcolare la luminanza**, utilizzando le costanti suggerite dall'ITU e i valori RGB.
```
Y = 0.299R + 0.587G + 0.114B
```
Ora che abbiamo la luminanza, possiamo **dividere i colori** (colore blu e rosso):
```
Cb = 0.564(B - Y)
Cr = 0.713(R - Y)
```
Possiamo anche **tornare indietro** facendo la conversione inversa, e anche **ottenere il valore del verde utilizzando YCbCr**.
```
R = Y + 1.402Cr
B = Y + 1.772Cb
G = Y - 0.344Cb - 0.714Cr
```
> <sup>\*</sup> i gruppi e gli standard sono comuni nel mondo del video digitale, di solito definiscono quali sono gli standard da usare, ad esempio [cosa si intende con 4K? quale framerate dovrei usare? quale risoluzione e modello di colore?](https://it.wikipedia.org/wiki/BT.2020)
In genere i display (monitor, tv, schermi, ecc.) utilizzano **solo il modello RGB**, anche se con geometrie dei pixel diverse, come si vede nelle immagini, che rappresentano schermi di tipo diverso, ingranditi.

### Sottocampionamento della crominanza
Con la rappresentazione dell'immagine in termini di luminanza e crominanza, possiamo trarre vantaggio dalle caratteristiche dell'occhio (che privilegiano la luminanza) per rimuovere selettivamente delle informazioni. La tecnica di **sottocampionamento della crominanza** fa proprio questo, e cioè utilizza **meno risoluzione per la crominanza rispetto alla luminanza**.

Quanto dovremmo ridurre la risoluzione della crominanza? Si scopre che esistono già degli schemi che descrivono come gestire la risoluzione e l'unione `colore finale = Y + Cb + Cr`.
Questi schemi sono noti come **sistemi di sottocampionamento** e sono espressi utilizzando un rapporto composto da tre valori. In particolare `a:x:y` definisce la risoluzione di crominanza di un blocco di pixel di luminanza di dimensione `a x 2` pixel.
* `a` numero di pixel orizzontali di riferimento per il campionamento (di solito 4)
* `x` numero di campioni di crominanza nella prima riga di `a` pixel (risoluzione orizzontale in relazione a `a`)
* `y` numero di cambiamenti di campioni di crominanza tra la prima e la seconda riga della griglia `a x 2`
> C'è un'eccezione a questa definizione, in particolare nello schema 4:1:0, che prevede un singolo campione di crominanza per ogni blocco `4 x 4` di luminanza.
Tra gli schemi più comuni nei codec moderni ci sono **4:4:4** *(nessun sottocampionamento)*, **4:2:2, 4:1:1, 4:2:0, 4:1:0 and 3:1:1**.
> **Unione di YCbCr 4:2:0**
>
> Ecco un esempio di come funziona l'unione di un'immagine usando YCbCr con schema di sottocampionamento della crominanza 4:2:0. Nota che vengono utilizzati soltanto 12 bit per pixel.
>
> 
Le immagini che seguono mostrano la stessa foto codificata con diversi tipi di sottocampionamento della crominanza. Le immagini della prima riga sono quelle finali, mentre quelle della seconda riga mostrano la risoluzione della crominanza. Il risultato è ottimo dato che la perdita di qualità nell'immagine a colori è molto bassa.

Prima abbiamo calcolato che sono necessari [278 GB di spazio per archiviare un file video dalla durata di un'ora, con risoluzione 720p e framerate di 30 fps](#rimozione-delle-informazioni-ridondanti). Se utilizziamo il modello colore **YCbCr 4:2:0**, possiamo **dimezzare la dimensione (139 GB)**<sup>\*</sup>, anche se siamo ben lontani dall'ideale.
> <sup>\*</sup>si ottiene questo valore moltiplicando tra loro larghezza, altezza, bit per pixel e fps. Con il modello RGB avevamo bisogno di 24 bit per pixel, ora ne abbiamo bisogno soltanto 12.
<br/>
> ### Esercizio: vedere l'istogramma YCbCr
> Puoi controllare [l'istogramma di un video YCbCr con ffmpeg](/encoding_pratical_examples.md#generates-yuv-histogram). Ad esempio, in questa scena il colore blu porta un alto contributo, come evidenziato dall'[istogramma](https://it.wikipedia.org/wiki/Istogramma).
>
> 
## Tipi di fotogramma
Prima di occuparci della **ridondanza delle informazioni nel tempo**, chiariamo la terminologia che useremo. Supponiamo di avere un filmato con 30 fps, i cui primi 4 fotogrammi sono:
  

È evidente che ci sono **molte ripetizioni** all'interno e tra i fotogrammi. Ad esempio lo sfondo blu non cambia tra il primo e l'ultimo fotogramma. Per sfruttare questa caratteristica dei fotogrammi, possiamo **categorizzare in modo astratto** i fotogrammi in [tre tipi diversi](https://it.wikipedia.org/wiki/Tipi_di_fotogramma_nella_compressione_video).
### I-frame (intra, keyframe)
Un I-frame (chiamato anche *reference frame*, *keyframe*, *intra-frame*) è un fotogramma **indipendente**. Non si affida a nessun'altra informazione esterna per essere renderizzato. È come una foto statica. Di solito il primo fotogramma di un video è un I-frame, ma vedremo che ce ne possono essere più di uno in un video, mischiati tra gli altri fotogrammi di tipo diverso.

### P-frame (predicted)
Un P-frame (*fotogramma predetto*) sfrutta il fatto che quasi sempre il fotogramma può essere **renderizzato utilizzando il fotogramma precedente**. Ad esempio, nel secondo fotogramma l'unico cambiamento rispetto al precedente è il movimento della palla. Possiamo quindi **ricostruire questo fotogramma utilizzando le differenze rispetto al precedente**.
 <- 
> #### Esercizio: un video con un solo I-frame
> Dato che un P-frame utilizza meno dati rispetto a un I-frame, perché non possiamo codificare un intero video [usando solo un I-frame e poi solo P-frame?](/encoding_pratical_examples.md#1-i-frame-and-the-rest-p-frames)
>
> Per esercizio codifica il video esempio, riproducilo e **salta a una posizione avanzata** del video. Noterai che sarà **richiesto del tempo** perché il player completi l'operazione di *seek*. Questo avviene perché un **P-frame ha bisogno di un fotogramma di riferimento** (ad esempio un I-Frame, ma non per forza) per essere renderizzato.
>
> Un altro veloce esperimento che puoi fare è [codificare un video sia utilizzando un singolo I-frame che impostato un I-frame ogni 2 secondi](/encoding_pratical_examples.md#1-i-frames-per-second-vs-05-i-frames-per-second), e confrontare la **dimensione dei due file risultanti**.
### B-frame (bi-predictive)
E se potessimo fare riferimento sia a fotogrammi passati che futuri, per ottenere una compressione migliore? È proprio come funzionano i B-frame, o frame bi-predittivi.
 <-  -> 
> #### Esercizio: confronta video con e senza B-frame
> Genera due video, uno con i B-frame e uno [senza B-frame](/encoding_pratical_examples.md#no-b-frames-at-all), e confronta la differenza di dimensione del file e la qualità dei risultati.
### Sommario
Diversi tipi di frame possono essere utilizzati per **fornire una migliore compressione**. Scopriremo meglio come nella prossima sezione, ma per ora è importante capire che **gli I-frame sono costosi, i P-frame un po' meno, e infine i B-frame sono i più leggeri**.

## Ridondanza temporale (predizione inter-frame)
Esploriamo le possibilità che abbiamo per cercare di ridurre la **ripetizione delle informazioni nel tempo**. Questo tipo di ridondanza può essere affrontata usando la **predizione tra fotogrammi** (*inter-frame prediction* o *inter prediction*).
L'obiettivo è **utilizzare meno bit** per codificare la sequenza di fotogrammi 0 e 1.

Una cosa che potremmo fare è **sottrarre il fotogramma 1 dal fotogramma 0**, in modo da ottenere soltanto il **residuo da codificare**.

E se ti dicessi che esiste un **metodo migliore**, che consente di utilizzare ancora meno informazione? Consideriamo il `fotogramma 0` come un insieme di partizioni (riquadri) ben definite, e proviamo a creare un'associazione tra i blocchi del `fotogramma 0` e il `fotogramma 1`. Possiamo pensare a questo meccanismo come una **stima del moto**.
> ### Da Wikipedia: compensazione del moto dei blocchi
> "La **compensazione del moto dei blocchi** divide il fotogramma corrente in blocchi non sovrapposti, e il vettore di compensazione del moto **indica da dove si sono spostati quei blocchi** (una convinzione comune ma errata è che il fotogramma precedente viene diviso in blocchi non sovrapposti, e i vettori di compensazione del moto indicano dove i blocchi si sposteranno). I blocchi sorgente tipicamente si sovrappongono nel fotogramma sorgente. Alcuni algoritmi di compressione video costruiscono il fotogramma corrente utilizzando pezzi di diversi fotogrammi creati precedentemente."

Possiamo stimare che la palla si è spostata dalla posizione `x=0, y=25` alla posizione `x=6, y=26`. I valori di **x** e **y** determinano i **vettori del moto**. Un passo aggiuntivo che possiamo fare per risparmiare spazio è **codificare soltanto il vettore di spostamento ottenuto dalla differenza** tra l'ultima posizione del blocco e quella predetta. Nell'esempio sopra il vettore del moto sarebbe quindi `x=6 (6-0), y=1 (26-25)`.
> In una situazione di codifica reale, **la palla sarebbe divisa tra più partizioni**, ma il procedimento è lo stesso.
Gli oggetti nel fotogramma **si muovono in 3 dimensioni**, cioè ad esempio la palla può diventare più piccola se si muove verso lo sfondo. È quindi normale non trovare **la corrispondenza perfetta** tra blocchi. Ad esempio questo è il risultato della nostra stima del moto in confronto alla figura reale:

Il vantaggio di applicare la tecnica della **stima del moto** è che **i dati da codificare sono di meno** in confronto a una semplice differenza tra fotogrammi.

> ### Come funziona realmente la compensazione del moto
> La tecnica appena vista viene applicata a tutti i blocchi, ma probabilmente la palla dell'esempio verrebbe partizionata in più di un blocco.
> 
> Fonte: https://web.stanford.edu/class/ee398a/handouts/lectures/EE398a_MotionEstimation_2012.pdf
Puoi esercitarti con questi concetti [usando jupyter](/frame_difference_vs_motion_estimation_plus_residual.ipynb).
> #### Esercizio: vedere i vettori del moto
> Possiamo generare un video [che mostra la predizione tra frame (e i vettori del moto) con ffmpeg.](/encoding_pratical_examples.md#generate-debug-video)
>
>  con ffmpeg")
>
> In alternativa possiamo usare il software [Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer) (è a pagamento ma esiste una versione di prova che limita l'analisi ai primi 10 fotogrammi).
>
> 
## Ridondanza spaziale (predizione intra-frame)
Se analizziamo un **singolo fotogramma** di un video noteremo che ci sono **molte aree correlate**.

Facciamo un esempio. Questa scena è composta prevalentemente dai colori blu e bianco:

Questo fotogramma è un `I-frame`, per cui **non possiamo usare fotogrammi precedenti** per costruirlo. Possiamo però comunque comprimerlo. Proviamo ad esempio a codificare il blocco evidenziato in rosso. Se guardiamo **intorno al blocco** possiamo **stimare** che c'è una **tendenza di colore attorno al blocco**.

Possiamo ad esempio **predire** che i pixel colorati sopra il blocco **continuino allo stesso modo verticalmente**. In altre parole **i pixel del blocco che hanno colore sconosciuto assumeranno attraverso la predizione il colore dei pixel che si trovano sopra** (in questo caso).

La **predizione che abbiamo fatto può essere sbagliata**, per cui dopo aver applicato la predizione dobbiamo **sottrarre i valori reali** dei pixel, in modo da ottenere un blocco residuo. Il risultato è una matrice molto più facilmente comprimibile in confronto all'originale.

> #### Esercizio: vedere la predizione intra-frame
> Puoi [generare un video con i macro-blocchi e le sue predizioni con ffmpeg](/encoding_pratical_examples.md#generate-debug-video). Consulta la documentazione di ffmpeg per comprendere meglio [il significato dei colori dei blocchi](https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors?version=7#AnalyzingMacroblockTypes).
>
>  con ffmpeg")
>
> In alternativa puoi anche usare il software [Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer) (è a pagamento ma esiste una versione di prova che limita l'analisi ai primi 10 fotogrammi).
>
> 
# Come funziona un codec video?
## Cosa? Perché? Come?
**Cos'è?** È un software o un dispositivo hardware che comprime o decomprime un video digitale. **Perché?** Il mercato e la società richiedono video di qualità alta con banda o spazio di archiviazione limitati. Ti ricordi che abbiamo [calcolato la banda necessaria](#terminologia-di-base) per trasmettere un video con 30 fotogrammi al secondo, 24 bit per pixel e una risoluzione di 480x240 pixel? Risultava **83 Mbps**, senza applicare alcuna compressione. I codec sono l'unico modo per fornire video HD/Full HD/UHD alle tv e tramite Internet. **Come?** Stiamo per dare un'occhiata alle principali tecniche usate.
> **CODEC vs Container**
>
> Un errore che viene spesso fatto dai principianti è confondere il CODEC video con il [contenitore video](https://it.wikipedia.org/wiki/Formato_contenitore). Possiamo pensare al **contenitore** come un formato "wrapper" che contiene i metadati sulle tracce video (e audio), e il vero e proprio **video compresso** come carico pagante (*payload*).
>
> Solitamente l'estensione di un file video determina il suo formato contenitore. Ad esempio, un file `video.mp4` indica con molta probabilità il formato **[MPEG-4 Part 14](https://it.wikipedia.org/wiki/MPEG-4_Part_14)**, mentre un file chiamato `video.mkv` si riferisce al formato **[Matroska](https://it.wikipedia.org/wiki/Matroska)**. Per controllare con certezza il codec e il formato contenitore di un file possiamo utilizzare [ffmpeg oppure mediainfo](/encoding_pratical_examples.md#inspect-stream).
## Storia
Prima di passare ad analizzare il funzionamento interno di un codec generico, facciamo un salto indietro nella storia per comprendere meglio alcuni vecchi codec.
Il codec video [H.261](https://it.wikipedia.org/wiki/H.261) nacque nel 1990 (tecnicamente nel 1988) e fu progettato per funzionare con **bitrate di 64 kbit/s**. Il codec utilizzava già idee come il sottocampionamento della crominanza, i macro-blocchi, ecc. Nell'anno 1955 lo standard del codec video **H.263** fu pubblicato, poi modificato ed esteso fino al 2001.
Nel 2003 la prima versione del codec **H.264/AVC** fu completata. Nello stesso anno, un'azienda chiamata **TrueMotion** rilasciò un altro codec per la compressione video, lossy e **royalty-free**, chiamato **VP3**. Nel 2008, **Google acquisto l'azienda**, rilasciando **VP8** nello stesso anno. Nel dicembre del 2012, Google rilasciò **VP9**, oggi **supportato da circa ¾ dei browser sul mercato** (mobile incluso).
**[AV1](https://it.wikipedia.org/wiki/AOMedia_Video_1)** è un nuovo codec video **royalty-free** e open source che è stato progettato dalla [Alliance for Open Media (AOMedia)](http://aomedia.org/), composta da grandi aziende come **Google, Mozilla, Microsoft, Amazon, Netflix, AMD, ARM, NVidia, Intel e Cisco**. La **prima versione** 0.1.0 del codec di riferimento è stata pubblicata il 7 aprile 2016.

> #### La nascita di AV1
>
> All'inizio del 2015, Google stava lavorando su [VP10](https://en.wikipedia.org/wiki/VP9#Successor:_from_VP10_to_AV1), Xiph (Mozilla) su [Daala](https://xiph.org/daala/) e Cisco ha reso open source il suo codec video royalty-free chiamato [Thor](https://tools.ietf.org/html/draft-fuldseth-netvc-thor-03).
>
> MPEG LA ha annunciato dei limiti annuali per HEVC (H.265) e canoni di utilizzo 8 volte superiori a H.264, per poi cambiare le regole di nuovo:
> * **nessun limite annuale**,
> * **commissione sui contenuti** (lo 0,5% delle entrate)
> * **commissioni per unità circa 10 volte più alte di H.264**.
>
> La [*Alliance for Open Media*](http://aomedia.org/about/) è stata creata da aziende produttrici di hardware (Intel, AMD, ARM , Nvidia, Cisco), aziende che forniscono contenuti (Google, Netflix, Amazon), browser (Google, Mozilla), e altre.
>
> Le aziende si sono poste un obiettivo comune: un video codec royalty-free. È quindi nato AV1, con un [sistema di licenze molto più semplice](http://aomedia.org/license/patent/). **Timothy B. Terriberry** ha fatto [un'ottima presentazione](https://www.youtube.com/watch?v=lzPaldsmJbk) (fonte di questa sezione) sul concepimento di AV1, il modello di licenza e lo stato attuale del codec.
>
> Ti sorprenderà sapere che puoi **analizzare il codec AV1 tramite il tuo browser**, andando alla pagina https://arewecompressedyet.com/analyzer/
>
> 
>
> P.S.: se vuoi imparare di più sulla storia dei codec è importante conoscere le basi che stanno dietro ai [brevetti legati alla compressione video](https://www.vcodex.com/video-compression-patents/).
## Un codec generico
Introdurremo ora i **meccanismi principali che stanno dietro a un generico codec video**, ma che si applicano anche ai principali codec moderni come VP9, AV1 e HEVC. Tieni presente che la spiegazione sarà *molto* semplificata rispetto alla realtà, anche se useremo qualche esempio reale (come H.264) per mostrare il funzionamento di una tecnica.
## 1° passo - partizionamento dell'immagine
Il primo passo è **dividere il fotogramma** in molte **partizioni, sotto-partizioni** e via dicendo.

**Perché si fa?** Ci sono molte ragioni: ad esempio, dividendo l'immagine possiamo calcolare le predizioni più precisamente, e utilizzare le partizioni più piccole per le parti in movimento, mentre quelle più grandi per rappresentare sfondi statici.
Solitamente, i CODEC **organizzano le partizioni** in *slice* (o *tile*), *macro* (o *coding tree unit*) e tante altre sotto-partizioni. La dimensione massima di queste partizioni varia, ad esempio per HEVC è 64x64 pixel, per AVC (H.264) è 16x16, con le sotto-partizioni che possono essere piccole fino a 4x4 pixel.
Ti ricordi che i fotogrammi possono essere di **diverso tipo**? Si può **applicare la stessa idea anche ai blocchi**, per cui possiamo avere I-slice, B-slice, I-macroblock, ecc.
> ### Esercizio: vedere le partizioni
> Puoi usare il software [Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer) (è a pagamento ma esiste una versione di prova che limita l'analisi ai primi 10 fotogrammi). Ecco un esempio che mostra le [partizioni di VP9](/encoding_pratical_examples.md#transcoding).
>
> 
## 2° passo - predizioni
Ora che abbiamo le partizioni, possiamo usarle per fare le predizioni. Per la predizione [inter-frame](#ridondanza-temporale-predizione-inter-frame) dobbiamo raccogliere informazioni sui **vettori del moto e sul residuo**, mentre per la predizione [intra-frame](#ridondanza-spaziale-predizione-intra-frame) ci servono **la direzione della predizione e il residuo**.
## 3° passo - trasformazione
Una volta che abbiamo il blocco residuo (ottenuto da `partizione predetta - partizione reale`), possiamo **trasformarlo** in un modo che ci consenta di vedere **quali pixel possiamo scartare** pur mantenendo la **qualità generale**. Ci sono diverse trasformazioni che ci permettono di farlo.
Anche se esistono [molte altre trasformazioni](https://en.wikipedia.org/wiki/List_of_Fourier-related_transforms#Discrete_transforms), daremo uno sguardo più approfondito alla trasformata discreta del coseno (*DCT*). La [DCT](https://it.wikipedia.org/wiki/Trasformata_discreta_del_coseno) ci permette di:
* **convertire** blocchi di pixel in blocchi di **coefficienti di frequenza** della stessa dimensione
* **compattare** l'energia, aiutandoci a eliminare la ridondanza spaziale
* **invertire** il processo, cioè tornare a blocchi di pixel
> Il 2 febbraio 2017, Cintra, R. J. e Bayer, F. M hanno pubblicato l'articolo scientifico [DCT-like Transform for Image Compression Requires 14 Additions Only](https://arxiv.org/abs/1702.00817).
Non preoccuparti se non hai capito a pieno i benefici di ciascun punto, faremo qualche esperimento in modo da capire meglio il reale valore della trasformata.
Prendiamo il seguente **blocco di pixel** (8x8):

Che corrisponde alla seguente immagine (8x8):

**Applicando la DCT** a questo blocco di pixel otteniamo un **blocco dei coefficienti** (8x8):

Il blocco dei coefficienti ottenuto, convertito in un'immagine, appare così:

Come puoi vedere non assomiglia per niente all'immagine originale. Si può anche notare che il **primo coefficiente** è molto diverso dagli altri. Questo perché il primo coefficiente della DCT è quello che rappresenta **tutti i campioni** della matrice di ingresso, qualcosa di **simile a una media**.
Il blocco dei coefficienti ha un'interessante proprietà, e cioè che separa i componenti relativi alle alte frequenze da quelli delle basse frequenze.

In un'immagine, **la maggior parte dell'energia** è concentrata nelle [**basse frequenze**](https://web.archive.org/web/20150129171151/https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm), quindi dopo aver applicato la trasformata possiamo **scartare i coefficienti delle alte frequenze**, in modo da **ridurre la quantità di dati** richiesta per descrivere l'immagine, pur senza sacrificare troppo la qualità dell'immagine.
> la frequenza determina quando rapidamente un segnale cambia.
Proviamo ad applicare le conoscenze acquisite: convertiamo l'immagine originale nelle sue frequenze usando la DCT, e poi scartiamo i coefficienti meno importanti.
Per prima cosa, convertiamo il blocco nel **dominio della frequenza**.

Poi, scartiamo parte dei coefficienti (il 67%), prevalentemente nell'angolo in basso a destra del blocco.

Infine, ricostruiamo l'immagine utilizzando il blocco dei coefficienti modificato, e confrontiamo il risultato con l'immagine originale.

L'immagine a destra è simile a quella originale, ma introduce anche molte differenze. Abbiamo però **buttato via il 67,1875%** delle informazioni e comunque siamo in grado di ottenere un'immagine che assomiglia a quella originale. Il prossimo passo è capire come modificare i coefficienti in modo più intelligente, per ottenere così una migliore qualità dell'immagine.
> **Ogni coefficiente viene calcolato utilizzando tutti i pixel**
>
> È importante evidenziare che ogni coefficiente del blocco non corrisponde direttamente a un singolo pixel, ma è una somma pesata di tutti i pixel. Il grafico che segue mostra come il primo e il secondo coefficiente vengono calcolati, utilizzando persi che sono diversi per ogni indice.
>
> 
>
> Fonte: https://web.archive.org/web/20150129171151/https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm
>
> Puoi anche provare a [osservare visivamente la DCT con la formazione di un'immagine](/dct_better_explained.ipynb). Ad esempio, qua puoi vedere [la formazione della lettera A](https://en.wikipedia.org/wiki/Discrete_cosine_transform#Example_of_IDCT) utilizzando diversi pesi per i coefficienti.
>
> 
<br/>
> ### Esercizio: scartare diversi coefficienti
> Puoi sperimentare con [la trasformata DCT](/uniform_quantization_experience.ipynb).
## 4° passo - quantizzazione
Quando scartiamo dei coefficienti, come abbiamo fatto alla fine dell'ultimo passo, stiamo già facendo una sorta di quantizzazione. Questo passo è quello in cui decidiamo quali informazioni "perdere", e lo facciamo **quantizzando i coefficienti per ottenere della compressione**.
Come possiamo quantizzare un blocco di coefficienti? Un metodo semplice potrebbe essere una quantizzazione uniforme, in cui prendiamo un blocco e **dividiamo tutti i valori per lo stesso numero** (10), per poi arrotondare.

Come possiamo **invertire** (ri-quantizzare) il blocco dei coefficienti? È sufficiente **moltiplicare i valori ottenuti per lo stesso numero** con cui li abbiamo divisi (10).

**Questo approccio non è però il migliore**, perché non tiene in considerazione l'importanza dei diversi coefficienti. Anziché un singolo valore per la divisione, potremmo utilizzare una **matrice di quantizzazione**, che sfrutterebbe quindi le proprietà della DCT quantizzando maggiormente l'angolo in basso a destra e in modo minore quello in alto a sinistra. La [compressione JPEG](https://www.hdm-stuttgart.de/~maucher/Python/MMCodecs/html/jpegUpToQuant.html) utilizza un metodo simile, e puoi vedere un esempio di matrice di quantizzazione [nel codice sorgente](https://github.com/google/guetzli/blob/master/guetzli/jpeg_data.h#L40) di un compressore JPEG.
> ### Esercizio: quantizzazione
> Puoi sperimentare [con la quantizzazione](/dct_experiences.ipynb).
## 5° passo - codifica dell'entropia
Dopo aver quantizzato i dati (i blocchi/slice/fotogrammi), possiamo comprimerli ulteriormenti utilizzando metodi di compressione lossless (senza perdita). Ci sono moltissimi algoritmi per comprimere i dati, e andremo a vederne alcuni. Per comprendere a fondo i concetti legati alla compressione dei dati, una lettura consigliata è il fantastico libro [Understanding Compression: Data Compression for Modern Developers](https://www.amazon.it/Understanding-Compression-Data-Modern-Developers/dp/1491961538/).
### Codifica VLC:
Supponiamo di avere un flusso di simboli: **a**, **e**, **r** e **t**, con le seguenti probabilità (valori da 0 a 1) di comparire nel flusso.
| | a | e | r | t |
|-------------|-----|-----|-----|-----|
| probabilità | 0,3 | 0,3 | 0,2 | 0,2 |
Possiamo assegnare un codice binario (preferibilmente piccolo) al simbolo più probabile, e un codice più lungo ai simboli meno probabili.
| | a | e | r | t |
|----------------|-----|-----|-----|------|
| probabilità | 0,3 | 0,3 | 0,2 | 0,2 |
| codice binario | 0 | 10 | 110 | 1110 |
Proviamo ora a comprimere il flusso rappresentato dalla parola **eat**. Assumendo di utilizzare 8 bit per ogni simbolo (carattere), avremmo bisogno di **24 bit** per rappresenta la parola, senza utilizzare nessun metodo di compressione. Ma se invece utilizziamo i codici binari associati alle lettere, possiamo risparmiare spazio.
Il primo passo è codificare il simbolo **e**, che corrisponde a `10`, per poi passare a **a**, con cui otteniamo `[10][0]` e infine il simbolo **t**, per ottenere `[10][0][1110]`, o `1001110`. Il risultato richiede soltanto **7 bit** per essere memorizzato, 3,4 volte volte in meno rispetto ai 24 bit iniziali.
È importante notare che ogni codice binario deve avere un prefisso unico (*proprietà del prefisso*). La [codifica di Huffman](https://it.wikipedia.org/wiki/Codifica_di_Huffman) aiuta a calcolare questi codici. Nonostante questa tecnica abbia qualche problema, ci sono ancora [alcuni codec video che la offrono](https://en.wikipedia.org/wiki/Context-adaptive_variable-length_coding) come opzione.
Sia il codificatore (encoder) che il decodificatore (decoder) **devono conoscere la tabella simboli-codici**, per cui è necessario salvare anche la tabella.
### Codifica aritmetica:
Supponiamo di avere il flusso di simboli **a**, **e**, **r**, **s** e **t**, le cui probabilità sono rappresentate dalla tabella.
| | a | e | r | s | t |
|-------------|-----|-----|------|------|-----|
| probabilità | 0,3 | 0,3 | 0,15 | 0,05 | 0,2 |
Usando le probabilità possiamo associare degli intervalli ai simboli, ordinandoli per probabilità decrescente.

Ora codifichiamo il flusso **eat**: prendiamo il primo simbolo **a**, il cui intervallo va **da 0,3 a 0,6** (non incluso). Lo dividiamo nuovamente, usando le stesse proporzioni della divisione originale, solo che applicate al sottointervallo.

Continuiamo con la codifica del flusso **eat**, prendendo il simbolo **a** che corrisponde ora all'intervallo che va **da 0,3 a 0,39**. Infine ripetiamo il processo con il simbolo **t**, ottenendo l'ultimo intervallo tra **0,354 e 0,372**.

Dobbiamo ora semplicemente prendere un qualsiasi valore contenuto tra **0,354 e 0,372**, ad esempio **0,36**, ma potremmo prenderne uno qualsiasi. Con **solo** questo numero siamo in grado di ricostruire il flusso di simboli originale **eat**. Se ci pensi, è come se stessimo tracciando una riga che attraversa gli intervalli e i sottointervalli per codificare il flusso.

Il **processo inverso** (la decodifica) è ugualmente facile. Avendo a disposizione il numero **0,36** e l'intervallo originale possiamo seguire la stessa procedura, usando il numero per scoprire sottointervallo per sottointervallo il flusso di simboli associato.
In pratica, usando il primo intervallo troviamo che il numero è compreso nell'intervallo del simbolo **e**, per cui prendiamo quell'intervallo e lo dividiamo di nuovo. Ripetiamo quanto appena fatto per trovare che il valore **0,36** è compreso nell'intervallo di **a**, che è quindi il secondo simbolo del flusso. Infine troviamo l'ultimo simbolo **t**, che va a completare il flusso finale **eat**.
Sia l'encoder che il decoder **devono conoscere** la tabella dei simboli con le probabilità, per cui la tabella deve essere salvata e trasmessa.
Interessante vero? Ci devono essere persone incredibilmente intelligenti per riuscire a inventare una soluzione del genere, che oggi viene usata [in molti codec video](https://en.wikipedia.org/wiki/Context-adaptive_binary_arithmetic_coding).
L'idea che deve restare è che si può prendere il flusso di bit (bitstream) risultante dalla quantizzazione e comprimerlo in modo lossless, cioè senza perdita di informazioni. Sicuramente questo articolo manca di molti dettagli, spiegazioni, vantaggi e compromessi, ma puoi [imparare facilmente di più se sei uno sviluppatore](https://www.amazon.it/Understanding-Compression-Data-Modern-Developers/dp/1491961538/). I nuovi codec stanno iniziando ad usare differenti sistemi di codifica dell'entropia, [come ANS](https://en.wikipedia.org/wiki/Asymmetric_numeral_systems).
> ### Esercizio: CABAC vs CAVLC
> Puoi [generare due video, uno con codifica dell'entropia CABAC e l'altro con CAVLC](https://github.com/leandromoreira/introduction_video_technology/blob/master/encoding_pratical_examples.md#cabac-vs-cavlc), e **confrontare il tempo di codifica**, così come la **dimensione del file finale**.
## 6° passo - formato bitstream
Dopo aver completato tutti questi passaggi, ora dobbiamo **compattare tutti i fotogrammi compressi con le loro informazioni di contesto**. Dobbiamo informare in modo esplicito il decoder delle **decisioni che sono state prese dall'encoder durante la codifica**, tra cui la profondità di bit, lo spazio colore, la risoluzione, le predizioni (vettori di moto inter-frame, direzione della predizione intra-frame), il profilo, il livello, il frame rate, il tipo di fotogramma, il numero di fotogramma e molto altro.
Stiamo per studiare, superficialmente, il bitstream del codec H.264. La prima cosa da fare è [generare un bitstream H.264 minimo<sup>\*</sup>](/encoding_pratical_examples.md#generate-a-single-frame-h264-bitstream), usando [ffmpeg](http://ffmpeg.org/).
```
./s/ffmpeg -i /files/i/minimal.png -pix_fmt yuv420p /files/v/minimal_yuv420.h264
```
> <sup>\*</sup>ffmpeg aggiunge, in modo predefinito, tutti i parametri di codifica come un **NAL SEI**. Presto definiremo che cos'è un NAL.
Il comando genera un bitstream H.264 "raw" (senza formato contenitore), con un **singolo fotogramma**, di dimensione 64x64 pixel, con spazio colore yuv420 (cioè YCbCr 4:2:0) e usando l'immagine seguente come fotogramma.
> 
### Bitstream di H.264
Lo standard AVC (H.264) stabilisce che le informazioni vengano trasmesse in **macro frame** (nel significato legato alle reti di telecomunicazioni), chiamati **[NAL](https://en.wikipedia.org/wiki/Network_Abstraction_Layer)** (Network Abstraction Layer). L'obiettivo principale dei NAL è fornire una rappresentazione del video che sia adatta per la trasmissione in rete, considerato che lo standard deve funzionare sulle tv (basate su flussi) e tramite Internet (basato su pacchetti), tra gli altri.

Viene usato un **[segnale di sincronizzazione](https://en.wikipedia.org/wiki/Frame_synchronization)** (*synchronization marker*) per delimitare le unità NAL. Ogni segnale di sincronizzazione corrisponde al valore fisso `0x00 0x00 0x01`, ad eccezione del primo che ha il valore `0x00 0x00 0x00 0x01`. Se eseguiamo il comando **hexdump** sul bitstream H.264 che abbiamo generato, riusciamo facilmente ad identificare almeno tre unità NAL all'inizio del file.

Come detto prima, il decodificatore ha bisogno non solo dei dati dell'immagine, ma anche di dettagli come il numero di fotogramma, i colori, i parametri usati e altro. Il **primo byte** di ciascun NAL definisce la sua categoria e il **tipo**.
| ID del tipo di NAL | Descrizione |
|--- |---|
| 0 | Non definito |
| 1 | Slice codificato di un'immagine non IDR |
| 2 | Slice codificato di una partizione A |
| 3 | Slice codificato di una partizione B |
| 4 | Slice codificato di una partizione C |
| 5 | **IDR** Slice codificato di un'immagine IDR |
| 6 | **SEI** Informazioni aggiuntive (*supplemental enhancement information*) |
| 7 | **SPS** Insieme parametri di sequenza (*sequence parameter set*) |
| 8 | **PPS** Insieme parametri d'immagine (*picture parameter set*) |
| 9 | Delimitatore di unità di accesso |
| 10 | Fine della sequenza |
| 11 | Fine del flusso |
| ... | ... |
Solitamente il primo NAL di un bitstream è di tipo **SPS**. Questo tipo di NAL contiene informazioni generiche sulle variabili di codifica, come il **profilo**, il **livello**, la **risoluzione** e altro.
Se saltiamo il primo segnale di sincronizzazione possiamo decodificare il **primo byte** per capire il **tipo del primo NAL** del bitstream di esempio.
Ad esempio in questo caso il primo byte dopo la sincronizzazione è `01100111`, dove il primo bit (`0`) si riferisce al campo **forbidden_zero_bit**, i due bit successivi (`11`) indicano il campo **nal_ref_idc**, che stabilisce se il NAL è un campo di riferimento oppure no, e infine gli ultimi cinque bit (`00111`) sono il campo **nal_unit_type**, cioè il tipo di NAL, in questo caso **SPS** (7).
Il secondo byte (`bin=01100100, hex=0x64, dec=100`) di un NAL SPS è il campo **profile_idc**, che indica il profilo che è stato usato per la codifica. In questo esempio è stato usato il profilo ["Constrained High"](https://en.wikipedia.org/wiki/H.264/MPEG-4_AVC#Profiles), che è un profilo "alto" senza il supporto agli slice di tipo B (bi-predittive).

Se leggiamo le specifiche del bitstream H.264 troveremo diversi valori per i campi **nome parametro**, **categoria** e **descrizione**. Ad esempio consideriamo i campi `pic_width_in_mbs_minus_1` e `pic_height_in_map_units_minus_1`.
| Nome parametro | Categoria | Descrizione |
|--- |---|---|
| pic_width_in_mbs_minus_1 | 0 | ue(v) |
| pic_height_in_map_units_minus_1 | 0 | ue(v) |
> **ue(v)**: intero senza segno [codificato con Exp-Golomb](https://pythonhosted.org/bitstring/exp-golomb.html)
Facendo un po' di calcoli possiamo utilizzare il valore di questi campi per ricavare la **risoluzione**. Ad esempio una risoluzione di `1920 x 1080` può essere ottenuta dando al parametro `pic_width_in_mbs_minus_1` il valore di `119 ( (119 + 1) * dimensione_macroblocco = 120 * 16 = 1920)`. Abbiamo risparmiato spazio, memorizzando `119` anziché `1920`.
Se continuiamo a esaminare il video creato con un visualizzatore binario (es. `xxd -b -c 11 v/minimal_yuv420.h264`), possiamo saltare all'ultimo NAL che è il fotogramma vero e proprio.

Il valore dei primi 6 byte è `01100101 10001000 10000100 00000000 00100001 11111111`. Come abbiamo visto dal primo byte si può ricavare il tipo di NAL, in questo caso `00101`, che corrisponde a **Slice IDR (5)**. Continuiamo con l'ispezione.

Utilizzando le specifiche dell'header degli slice H.264 possiamo decodificare il tipo di slice (**slice_type**), il numero del fotogramma (**frame_num**), assieme ad altre importanti informazioni.
Per ottenere il reale valore di alcuni campi (`ue(v), me(v), se(v), te(v)`) dobbiamo decodificarli utilizzando un metodo speciale chiamato [Exponential-Golomb](https://pythonhosted.org/bitstring/exp-golomb.html), che è un **modo molto efficiente per codificare valori variabili**, specialmente quando vengono usati molti valori predefiniti.
> I valori **slice_type** e **frame_num** sono 7 (slice di tipo I) e 0 (primo fotogramma).
Possiamo vedere il **bitstream come un protocollo**, e se vuoi o devi imparare di più a proposito del bitstream fai riferimento alle [specifiche H.264 dell'ITU](http://www.itu.int/rec/T-REC-H.264-201610-I). Ecco un diagramma semplificato che mostra dove vengono memorizzati i dati dell'immagine vera e propria (YCbCr/YUV compresso).

Possiamo esplorare altri bitstream come [quello di VP9](https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf), [H.265 (HEVC)](http://handle.itu.int/11.1002/1000/11885-en?locatt=format:pdf) o anche il nostro **nuovo miglior amico** [**AV1**](https://medium.com/@mbebenita/av1-bitstream-analyzer-d25f1c27072b#.d5a89oxz8
). Si assomigliano? [No, ma una volta imparato uno capire gli altri è facile](http://www.gpac-licensing.com/2016/07/12/vp9-av1-bitstream-format/).
> ### Esercizio: ispezionare il bitstream H.264
> Possiamo [generare un video con un singolo fotogramma](https://github.com/leandromoreira/introduction_video_technology/blob/master/encoding_pratical_examples.md#generate-a-single-frame-video) e usare [mediainfo](https://en.wikipedia.org/wiki/MediaInfo) per ispezionare il suo bitstream H.264. Infatti puoi anche vedere il [codice sorgente che si occupa del parsing del bitstream H.264 (AVC)](https://github.com/MediaArea/MediaInfoLib/blob/master/Source/MediaInfo/Video/File_Avc.cpp).
>
> 
>
> In alternativa possiamo usare il software [Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer) (è a pagamento ma esiste una versione di prova che limita l'analisi ai primi 10 fotogrammi).
>
> 
## Ripasso
Molti dei **codec moderni utilizzano lo stesso modello di codifica che abbiamo imparato**. Ad esempio, diamo un'occhiata al diagramma a blocchi del codec video Thor, che contiene tutti i passi che abbiamo studiato. A questo punto dovresti essere in grado almeno di comprendere meglio le innovazioni e gli articoli che riguardano questi argomenti.

Inizialmente abbiamo calcolato che ci servono [139 GB di spazio per archiviare un file di un'ora nel formato 720p30](#sottocampionamento-della-crominanza). Se applichiamo le tecniche imparate qui, come **la predizione intra-frame e inter-frame, la trasformata, la quantizzazione, la codifica dell'entropia**, assumendo che spendiamo **0,031 bit per pixel** possiamo raggiungere la stessa qualità percepita utilizzando soltanto **368 MB anziché 139 GB**.
> Abbiamo scelto di usare **0,031 bit per pixel** basandoci sull'esempio precedente.
## Come fa H.265 a comprimere più di H.264?
Ora che sappiamo come funziona un codec, risulta più facile capire come i nuovi codec sono in grado di memorizzare risoluzioni più alte usando meno bit.
Confronteremo AVC e HEVC, tenendo in mente che il processo è quasi sempre un compromesso tra la complessità dell'algoritmo (cicli di CPU) e il tasso di compressione.
HEVC ha **partizioni** (e **sottopartizioni**) più grandi e più numerose rispetto ad AVC, inoltre ha **più direzioni di predizione intra-frame**, una **migliore codifica dell'entropia**, e altro. Tutto insieme fa in modo che H.265 sia in grado di comprimere il 50% in più rispetto ad H.264.

# Streaming online
## Architettura generale

[TODO]
## Download progressivo vs streaming adattivo


[TODO]
## Protezione dei contenuti
Possiamo usare un semplice **sistema a token** (gettone) per proteggere i contenuti. Un utente senza token che prova a richiedere un video viene bloccato dalla CDN, mentre un utente con un token valido può riprodurre il contenuto. Funziona in modo abbastanza simile alla maggior parte dei sistemi di autenticazione sul web.

L'utilizzo di questo sistema consente comunque all'utente di scaricare e ridistribuire il video. Un sistema **DRM (digital rights management)** permette di evitare anche questo problema.

I sistemi di produzione nel mondo reale in genere usano entrambe le tecniche, per offrire sia autorizzazione che autenticazione.
### DRM
#### Sistemi principali
* FPS - [**FairPlay Streaming**](https://developer.apple.com/streaming/fps/)
* PR - [**PlayReady**](https://www.microsoft.com/playready/)
* WV - [**Widevine**](http://www.widevine.com/)
#### Cos'è?
DRM sta per *gestione dei diritti digitali* (*Digital Rights Management*) ed è un modo per **aggiungere una protezione del diritto d'autore ai contenuti digitali**, quindi anche al video e all'audio. Nonostante sia molto usato, [non è universalmente accettato](https://en.wikipedia.org/wiki/Digital_rights_management#DRM-free_works).
#### Perché?
I creatori di contenuti (principalmente gli studi) vogliono proteggere la proprietà intellettuale dalla copia, per prevenire la ridistribuzione non autorizzata dei contenuti digitali.
#### Come?
Descriveremo un modello generico ed astratto di DRM in un modo molto semplificato.
Sia dato un **contenuto C1** (es. uno streaming video HLS o DASH), con un **player P1** (es. shaka-clappr, exo-player o iOS) e un **dispositivo D1** (es. uno smartphone, una tv, un tablet o un computer), che utilizzano un **sistema DRM che chiamiamo DRM1** (es. Widevine, PlayReady, FairPlay).
Il contenuto C1 viene cifrato con una **chiave simmetrica K1** fornita dal sistema DRM1, così da ottenere il **contenuto cifrato C'1**.

Il player P1 del dispositivo D1 possiede due chiavi (asimmetriche), cioè una **chiave privata PRK1** (questa chiave è protetta<sup>1</sup> e conosciuta solo da **D1**) e una **chiave pubblica PUK1**.
> **<sup>1</sup>protetta**: questa protezione può essere ottenuta sia **tramite hardware**, memorizzando la chiave all'interno di un chip speciale in sola lettura che agisce come una [scatola nera](https://it.wikipedia.org/wiki/Modello_black_box) per fornire la decifratura, sia **tramite software** (in modo meno sicuro). Il sistema DRM offre dei modi per sapere quale tipo di protezione il dispositivo ha a disposizione.
Quando il **player P1 vuole riprodurre il contenuto C'1**, deve contrattare con il **sistema DRM1**, fornendogli la sua chiave pubblica **PUK1**. Il sistema DRM1 ritorna la **chiave K1 cifrata** con la chiave pubblica **PUK1** del client. A livello teorico, questa risposta è qualcosa che **soltanto D1 è in grado di decifrare**.
`K1P1D1 = enc(K1, PUK1)`
Il player **P1** utilizza il suo sistema DRM locale (che potrebbe essere un [SoC](https://it.wikipedia.org/wiki/System-on-a-chip), una parte di software o hardware specializzata), **in grado di decifrare** il contenuto utilizzando la sua chiave privata PRK1. In questo modo può **decifrare la chiave simmetrica K1 da K1P1D1** e quindi riprodurre **C'1**. Nel migliore dei casi, le chiavi non sono esposte nella memoria RAM.
```
K1 = dec(K1P1D1, PRK1)
P1.play(dec(C'1, K1))
```

# Come usare jupyter
Assicurati di avere **Docker installato** e avvia `./s/start_jupyter.sh`. Segui poi le istruzioni mostrate nel terminale.
# Conferenze
* [DEMUXED](https://demuxed.com/) - puoi [guardare le presentazioni degli ultimi 2 eventi](https://www.youtube.com/channel/UCIc_DkRxo9UgUSTvWVNCmpA).
# Riferimenti
Il contenuto più ricco è qua, puoi trovare le informazioni che hanno ispirato questo testo e da cui sono stati estratti i concetti. Puoi approfondire la tua conoscenza leggendo questi link, libri, video, ecc.
Corsi online e tutorial:
* https://www.coursera.org/learn/digital/
* https://people.xiph.org/~tterribe/pubs/lca2012/auckland/intro_to_video1.pdf
* https://xiph.org/video/vid1.shtml
* https://xiph.org/video/vid2.shtml
* http://slhck.info/ffmpeg-encoding-course
* http://www.cambridgeincolour.com/tutorials/camera-sensors.htm
* http://www.slideshare.net/vcodex/a-short-history-of-video-coding
* http://www.slideshare.net/vcodex/introduction-to-video-compression-13394338
* https://developer.android.com/guide/topics/media/media-formats.html
* http://www.slideshare.net/MadhawaKasun/audio-compression-23398426
* http://inst.eecs.berkeley.edu/~ee290t/sp04/lectures/02-Motion_Compensation_girod.pdf
Libri:
* https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/ref=sr_1_1?s=books&ie=UTF8&qid=1486395327&sr=1-1
* https://www.amazon.com/H-264-Advanced-Video-Compression-Standard/dp/0470516925
* https://www.amazon.com/Practical-Guide-Video-Audio-Compression/dp/0240806301/ref=sr_1_3?s=books&ie=UTF8&qid=1486396914&sr=1-3&keywords=A+PRACTICAL+GUIDE+TO+VIDEO+AUDIO
* https://www.amazon.com/Video-Encoding-Numbers-Eliminate-Guesswork/dp/0998453005/ref=sr_1_1?s=books&ie=UTF8&qid=1486396940&sr=1-1&keywords=jan+ozer
Specifiche bitstream:
* http://www.itu.int/rec/T-REC-H.264-201610-I
* http://www.itu.int/ITU-T/recommendations/rec.aspx?rec=12904&lang=en
* https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf
* http://iphome.hhi.de/wiegand/assets/pdfs/2012_12_IEEE-HEVC-Overview.pdf
* http://phenix.int-evry.fr/jct/doc_end_user/current_document.php?id=7243
* http://gentlelogic.blogspot.com.br/2011/11/exploring-h264-part-2-h264-bitstream.html
* https://forum.doom9.org/showthread.php?t=167081
* https://forum.doom9.org/showthread.php?t=168947
Software:
* https://ffmpeg.org/
* https://ffmpeg.org/ffmpeg-all.html
* https://ffmpeg.org/ffprobe.html
* https://trac.ffmpeg.org/wiki/
* https://software.intel.com/en-us/intel-video-pro-analyzer
* https://medium.com/@mbebenita/av1-bitstream-analyzer-d25f1c27072b#.d5a89oxz8
Codec non ITU:
* https://aomedia.googlesource.com/
* https://github.com/webmproject/libvpx/tree/master/vp9
* https://people.xiph.org/~xiphmont/demo/daala/demo1.shtml
* https://people.xiph.org/~jm/daala/revisiting/
* https://www.youtube.com/watch?v=lzPaldsmJbk
* https://fosdem.org/2017/schedule/event/om_av1/
* https://jmvalin.ca/papers/AV1_tools.pdf
Concetti di codifica:
* http://x265.org/hevc-h265/
* http://slhck.info/video/2017/03/01/rate-control.html
* http://slhck.info/video/2017/02/24/vbr-settings.html
* http://slhck.info/video/2017/02/24/crf-guide.html
* https://arxiv.org/pdf/1702.00817v1.pdf
* https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors
* http://web.ece.ucdavis.edu/cerl/ReliableJPEG/Cung/jpeg.html
* http://www.adobe.com/devnet/adobe-media-server/articles/h264_encoding.html
* https://prezi.com/8m7thtvl4ywr/mp3-and-aac-explained/
* https://blogs.gnome.org/rbultje/2016/12/13/overview-of-the-vp9-video-codec/
* https://videoblerg.wordpress.com/2017/11/10/ffmpeg-and-how-to-use-it-wrong/
Sequenze video per i test:
* http://bbb3d.renderfarming.net/download.html
* https://www.its.bldrdoc.gov/vqeg/video-datasets-and-organizations.aspx
Varie:
* http://stackoverflow.com/a/24890903
* http://stackoverflow.com/questions/38094302/how-to-understand-header-of-h264
* http://techblog.netflix.com/2016/08/a-large-scale-comparison-of-x264-x265.html
* http://vanseodesign.com/web-design/color-luminance/
* http://www.biologymad.com/nervoussystem/eyenotes.htm
* http://www.compression.ru/video/codec_comparison/h264_2012/mpeg4_avc_h264_video_codecs_comparison.pdf
* http://www.csc.villanova.edu/~rschumey/csc4800/dct.html
* http://www.explainthatstuff.com/digitalcameras.html
* http://www.hkvstar.com
* http://www.hometheatersound.com/
* http://www.lighterra.com/papers/videoencodingh264/
* http://www.red.com/learn/red-101/video-chroma-subsampling
* http://www.slideshare.net/ManoharKuse/hevc-intra-coding
* http://www.slideshare.net/mwalendo/h264vs-hevc
* http://www.slideshare.net/rvarun7777/final-seminar-46117193
* http://www.springer.com/cda/content/document/cda_downloaddocument/9783642147029-c1.pdf
* http://www.streamingmedia.com/Articles/Editorial/Featured-Articles/A-Progress-Report-The-Alliance-for-Open-Media-and-the-AV1-Codec-110383.aspx
* http://www.streamingmediaglobal.com/Articles/ReadArticle.aspx?ArticleID=116505&PageNum=1
* http://yumichan.net/video-processing/video-compression/introduction-to-h264-nal-unit/
* https://cardinalpeak.com/blog/the-h-264-sequence-parameter-set/
* https://cardinalpeak.com/blog/worlds-smallest-h-264-encoder/
* https://codesequoia.wordpress.com/category/video/
* https://developer.apple.com/library/content/technotes/tn2224/_index.html
* https://en.wikibooks.org/wiki/MeGUI/x264_Settings
* https://en.wikipedia.org/wiki/Adaptive_bitrate_streaming
* https://en.wikipedia.org/wiki/AOMedia_Video_1
* https://en.wikipedia.org/wiki/Chroma_subsampling#/media/File:Colorcomp.jpg
* https://en.wikipedia.org/wiki/Cone_cell
* https://en.wikipedia.org/wiki/File:H.264_block_diagram_with_quality_score.jpg
* https://en.wikipedia.org/wiki/Inter_frame
* https://en.wikipedia.org/wiki/Intra-frame_coding
* https://en.wikipedia.org/wiki/Photoreceptor_cell
* https://en.wikipedia.org/wiki/Pixel_aspect_ratio
* https://en.wikipedia.org/wiki/Presentation_timestamp
* https://en.wikipedia.org/wiki/Rod_cell
* https://it.wikipedia.org/wiki/File:Pixel_geometry_01_Pengo.jpg
* https://leandromoreira.com.br/2016/10/09/how-to-measure-video-quality-perception/
* https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping
* https://softwaredevelopmentperestroika.wordpress.com/2014/02/11/image-processing-with-python-numpy-scipy-image-convolution/
* https://tools.ietf.org/html/draft-fuldseth-netvc-thor-03
* https://www.encoding.com/android/
* https://www.encoding.com/http-live-streaming-hls/
* https://web.archive.org/web/20150129171151/https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm
* https://www.lifewire.com/cmos-image-sensor-493271
* https://www.linkedin.com/pulse/brief-history-video-codecs-yoav-nativ
* https://www.linkedin.com/pulse/video-streaming-methodology-reema-majumdar
* https://www.vcodex.com/h264avc-intra-precition/
* https://www.youtube.com/watch?v=9vgtJJ2wwMA
* https://www.youtube.com/watch?v=LFXN9PiOGtY
* https://www.youtube.com/watch?v=Lto-ajuqW3w&list=PLzH6n4zXuckpKAj1_88VS-8Z6yn9zX_P6
* https://www.youtube.com/watch?v=LWxu4rkZBLw
* https://web.stanford.edu/class/ee398a/handouts/lectures/EE398a_MotionEstimation_2012.pdf
================================================
FILE: README-ja.md
================================================
[🇺🇸](/README.md "English")
[🇨🇳](/README-cn.md "Simplified Chinese")
[🇯🇵](/README-ja.md "Japanese")
[🇮🇹](/README-it.md "Italian")
[🇰🇷](/README-ko.md "Korean")
[🇷🇺](/README-ru.md "Russian")
[🇧🇷](/README-pt.md "Portuguese")
[🇪🇸](/README-es.md "Spanish")
[](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)
# はじめに
これは、ビデオ技術に関するやさしい解説資料です。ソフトウェア開発者やエンジニアを対象にしていますが、**誰でも理解できる**解説にしたいと思っています。このアイディアは、[ビデオ技術初学者のためのミニワークショップ](https://docs.google.com/presentation/d/17Z31kEkl_NGJ0M66reqr9_uTG6tI5EDDVXpdPKVuIrs/edit#slide=id.p)から生まれました。
できるだけ**簡潔な言葉、多くの視覚的要素、具体的な例**を使うことで、誰でもデジタルビデオの概念が理解できることを目標にしています。気軽に訂正や提案を送り、改善してください。
本資料には**ハンズオン**による解説が含まれています。ハンズオンに取り組む方は、事前にdockerをインストールし、このレポジトリをクローンしておいてください。
```bash
git clone https://github.com/leandromoreira/digital_video_introduction.git
cd digital_video_introduction
./setup.sh
```
> **注意**: `./s/ffmpeg` や `./s/mediainfo` コマンドは、そのプログラムが**Dockerコンテナ上**で実行されることを意味しています。コンテナの中には、必要な依存関係が全て含まれています。
**ハンズオンはすべて、このレポジトリをクローンしたフォルダで実行してください**。 **jupyter examples**については、`./s/start_jupyter.sh`でサーバーを起動して、表示されるURLをブラウザで開いてください。
# 変更履歴
* DRMシステムの追加
* 1.0.0版のリリース
* 簡体字訳の追加
* FFmpeg oscilloscopeフィルターの例を追加
# 目次
- [はじめに](#はじめに)
- [目次](#目次)
- [基本用語](#基本用語)
* [カラー画像を符号化する別の方法](#カラー画像を符号化する別の方法)
* [ハンズオン: 画像と色の実験](#ハンズオン-画像と色の実験)
* [DVDの画面アスペクト比は4:3](#dvdの画面アスペクト比は43)
* [ハンズオン: ビデオプロパティを調べる](#ハンズオン-ビデオプロパティを調べる)
- [冗長性除去](#冗長性除去)
* [色、明るさと私たちの目](#色、明るさと私たちの目)
+ [カラーモデル](#カラーモデル)
+ [YCbCrとRGB間の変換](#ycbcrとrgb間の変換)
+ [クロマサブサンプリング](#クロマサブサンプリング)
+ [ハンズオン: YCbCrヒストグラムを調べる](#ハンズオン-ycbcrヒストグラムを調べる)
* [フレームの種類](#フレームの種類)
+ [Iフレーム (イントラ、キーフレーム)](#iフレーム-イントラ、キーフレーム)
+ [Pフレーム (予測)](#pフレーム-予測)
- [ハンズオン: Iフレームが1つだけのビデオ](#ハンズオン-iフレームが1つだけのビデオ)
+ [Bフレーム (双方向予測)](#bフレーム-双方向予測)
- [ハンズオン: Bフレーム付きのビデオとの比較](#ハンズオン-bフレーム付きのビデオとの比較)
+ [まとめ](#まとめ)
* [時間的冗長性 (インター予測)](#時間的冗長性-インター予測)
- [ハンズオン: 動きベクトルを見る](#ハンズオン-動きベクトルを見る)
* [空間的冗長性 (イントラ予測)](#空間的冗長性-イントラ予測)
- [ハンズオン: イントラ予測を調べる](#ハンズオン-イントラ予測を調べる)
- [ビデオコーデックの仕組み](#ビデオコーデックの仕組み)
* [何か? なぜ? どのように?](#何か-なぜ-どのように)
* [歴史](#歴史)
+ [AV1の誕生](#av1の誕生)
* [一般的コーデック](#一般的コーデック)
* [ステップ1 - 画像分割](#ステップ1---画像分割)
+ [ハンズオン: パーティションを調べる](#ハンズオン-パーティションを調べる)
* [ステップ2 - 予測](#ステップ2---予測)
* [ステップ3 - 変換](#ステップ3---変換)
+ [ハンズオン: 種々の係数を捨てる](#ハンズオン-種々の係数を捨てる)
* [ステップ4 - 量子化](#ステップ4---量子化)
+ [ハンズオン: 量子化](#ハンズオン-量子化)
* [ステップ5 - エントロピー符号化](#ステップ5---エントロピー符号化)
+ [可変長符号](#可変長符号)
+ [算術符号](#算術符号)
+ [ハンズオン: CABAC対CAVLC](#ハンズオン-cabac対cavlc)
* [ステップ6 - ビットストリームフォーマット](#ステップ6---ビットストリームフォーマット)
+ [H.264ビットストリーム](#h264ビットストリーム)
+ [ハンズオン: H.264ビットストリームを調べる](#ハンズオン-h264ビットストリームを調べる)
* [おさらい](#おさらい)
* [どのようにH.265はH.264よりも良い圧縮率を実現しているのか?](#どのようにh265はh264よりも良い圧縮率を実現しているのか)
- [オンラインストリーミング](#オンラインストリーミング)
* [一般的なアーキテクチャ](#一般的なアーキテクチャ)
* [プログレッシブダウンロードとアダプティブストリーミング](#プログレッシブダウンロードとアダプティブストリーミング)
* [コンテンツ保護](#コンテンツ保護)
- [jupyterの使い方](#jupyterの使い方)
- [カンファレンス](#カンファレンス)
- [参考文献](#参考文献)
# 基本用語
**画像**は、**二次元マトリクス**として考えることができます。さらに次元を増やして**三次元マトリクス**にすることで画像の色を表現することも可能です。
画像の色を[原色 (赤、緑、青)](https://ja.wikipedia.org/wiki/%E5%8E%9F%E8%89%B2)で表現すると、三つの平面を定義することになります。一つめが**赤**、二つ目が**緑**、そして三つ目が**青**です。

マトリクスのそれぞれの要素を**ピクセル** (画素)と呼びます。一つのピクセルはその色の**強度** (通常は数値)を表します。例えば、**赤色のピクセル**は、緑が0、青が0、赤が最大の強度により表現できます。**ピンク色のピクセル**も同様に3つの値で表現できます。0から255の数値で表現することにより、ピンクピクセルは**赤=255、緑=192、青=203**と定義できます。
> #### カラー画像を符号化する別の方法
> 色を表現する方法は、他にもたくさんあります。例えば、RGBモデルでは各ピクセルで3バイトを必要としますが、インデックスパレットは1バイトしか必要ありません。そういったモデルでは、色を表現するために三次元モデルを使わずに二次元モデルを使用できるでしょう。メモリを節約できますが、色の選択肢を狭めることになります。
>
> 
例えば、下の画像をみてください。1番左の画像は色付けされており、他の画像は赤、緑、青の強度を表す平面です(グレートーンで表示しています)。

**赤色**が最も多く使われていることが分かります(左から二番目の顔の最も明るい部分)。一方**青色** は服の一部と**マリオの目にしかみられません**(最後の顔) 。**マリオのひげ**に対しては、**どの色もあまり使われていない**(最も暗い部分)ことが分かります。
各色の強度は**ビット深度**とよばれる一定量のビットで表現されます。色(平面)ごとに**8ビット**(0から255の値で表現する)を使う場合、**24ビット**(8ビット x 3次元 R/G/B)の**色深度**を持つことになり、2の24乗種類の色を使えることが推測できます。
> [画像がどのように万物をビットとしてとらえるのか](http://www.cambridgeincolour.com/tutorials/camera-sensors.htm)を学ぶと **良い**でしょう
もう一つの画像のプロパティは **解像度**です。解像度は長さあたりのピクセルの数です。解像度はよく幅 x 高さとして表現されます。例えば、下記は**4×4**の画像です。

> #### ハンズオン: 画像と色の実験
> [jupyter](#jupyterの使い方) (python、numpy、matplotlib、その他)を使って、[画像と色の実験](/image_as_3d_array.ipynb)をしましょう。
>
> [(エッジ検出, シャープ化, ぼかし等の)画像フィルタがどのように動くか](/filters_are_easy.ipynb)を学びましょう。
画像やビデオの作業をする時にみるもう一つのプロパティは **アスペクト比**です。アスペクト比は、画像やピクセルの幅と高さの比率を表します。
動画や画像が**16x9**であると言うときは、たいてい**画面アスペクト比 (DAR)** のことを指します。しかし、個々のピクセルを様々な形状にすることができ、これを **ピクセルアスペクト比 (PAR)** といいます。


> #### DVDの画面アスペクト比は4:3
> DVDの実際の解像度は704x480ですが、10:11のピクセルアスペクト比を持っているため、4:3のアスペクト比を保っています(704x10/480x11)。
最後に、**ビデオ**を**単位時間**内の***n*フレームの並び**として定義でき、もう一つの特性と見ることができます。*n*はフレームレートもしくは秒間フレーム数 (FPS)です。

ビデオを表すために必要な秒間あたりのビット数は**ビットレート**です。
> ビットレート = 幅 x 高さ x ビット深度 x フレームレート
例えば、圧縮を全く使わないなら、フレームレートが30で、ピクセルあたりが24ビットで、解像度が480x240のビデオは、**秒間あたり82,944,000ビット**もしくは82.944 Mbps (30x480x240x24)が必要です。
**ビットレート**がほとんど一定なら、固定ビットレート(**CBR**)と呼ばれます。ビットレートが変動するなら、可変ビットレート (*
gitextract__0xyor60/ ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README-cn.md ├── README-es.md ├── README-it.md ├── README-ja.md ├── README-ko.md ├── README-pt.md ├── README-ru.md ├── README.md ├── clean_docker.sh ├── dct_better_explained.ipynb ├── dct_experiences.ipynb ├── encoding_pratical_examples.md ├── filters_are_easy.ipynb ├── frame_difference_vs_motion_estimation_plus_residual.ipynb ├── image_as_3d_array.ipynb ├── image_transform_frequency_domain.ipynb ├── s/ │ ├── cut_smaller_video.sh │ ├── download_video.sh │ ├── ffmpeg │ ├── mediainfo │ ├── start_jupyter.sh │ └── vmaf ├── setup.sh └── uniform_quantization_experience.ipynb
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,432K chars).
[
{
"path": ".gitignore",
"chars": 96,
"preview": "v/*\n!v/small_bunny_1080p_60fps.mp4\n!v/small_bunny_1080p_30fps.mp4\n.ipynb_checkpoints/\n.DS_Store\n"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3230,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "CONTRIBUTING.md",
"chars": 1875,
"preview": "## Contributing\n\nFirst off, thank you for considering contributing to this repo. It's people\nlike you that make this mat"
},
{
"path": "LICENSE",
"chars": 1515,
"preview": "BSD 3-Clause License\n\nCopyright (c) 2017, Leandro Moreira\nAll rights reserved.\n\nRedistribution and use in source and bin"
},
{
"path": "README-cn.md",
"chars": 41659,
"preview": "[🇺🇸](/README.md \"English\")\n[🇨🇳](/README-cn.md \"Simplified Chinese\")\n[🇯🇵](/README-ja.md \"Japanese\")\n[🇮🇹](/README-it.md \"I"
},
{
"path": "README-es.md",
"chars": 72459,
"preview": "[🇺🇸](/README.md \"English\")\n[🇨🇳](/README-cn.md \"Simplified Chinese\")\n[🇯🇵](/README-ja.md \"Japanese\")\n[🇮🇹](/README-it.md \"I"
},
{
"path": "README-it.md",
"chars": 72774,
"preview": "[🇺🇸](/README.md \"English\")\n[🇨🇳](/README-cn.md \"Simplified Chinese\")\n[🇯🇵](/README-ja.md \"Japanese\")\n[🇮🇹](/README-it.md \"I"
},
{
"path": "README-ja.md",
"chars": 43630,
"preview": "[🇺🇸](/README.md \"English\")\n[🇨🇳](/README-cn.md \"Simplified Chinese\")\n[🇯🇵](/README-ja.md \"Japanese\")\n[🇮🇹](/README-it.md \"I"
},
{
"path": "README-ko.md",
"chars": 46810,
"preview": "[🇺🇸](/README.md \"English\")\n[🇨🇳](/README-cn.md \"Simplified Chinese\")\n[🇯🇵](/README-ja.md \"Japanese\")\n[🇮🇹](/README-it.md \"I"
},
{
"path": "README-pt.md",
"chars": 70605,
"preview": "[🇺🇸](/README.md \"English\")\n[🇨🇳](/README-cn.md \"Simplified Chinese\")\n[🇯🇵](/README-ja.md \"Japanese\")\n[🇮🇹](/README-it.md \"I"
},
{
"path": "README-ru.md",
"chars": 60051,
"preview": "[🇺🇸](/README.md \"English\")\n[🇨🇳](/README-cn.md \"Simplified Chinese\")\n[🇯🇵](/README-ja.md \"Japanese\")\n[🇮🇹](/README-it.md \"I"
},
{
"path": "README.md",
"chars": 66672,
"preview": "[🇺🇸](/README.md \"English\")\n[🇨🇳](/README-cn.md \"Simplified Chinese\")\n[🇯🇵](/README-ja.md \"Japanese\")\n[🇮🇹](/README-it.md \"I"
},
{
"path": "clean_docker.sh",
"chars": 608,
"preview": "#!/bin/bash\nEXITED=$(docker ps -q -f status=exited)\nDANGLING=$(docker images -q -f \"dangling=true\")\nDANGLING_VOLUME=$(do"
},
{
"path": "dct_better_explained.ipynb",
"chars": 195646,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {\n \"slideshow\": {\n \"slide_type\": \"slide\"\n }\n },"
},
{
"path": "dct_experiences.ipynb",
"chars": 94831,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {\n \"slideshow\": {\n \"slide_type\": \"slide\"\n }\n },"
},
{
"path": "encoding_pratical_examples.md",
"chars": 11710,
"preview": "[](https://img.shields.io/badge/license-BSD--3--"
},
{
"path": "filters_are_easy.ipynb",
"chars": 214394,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {\n \"slideshow\": {\n \"slide_type\": \"slide\"\n }\n },"
},
{
"path": "frame_difference_vs_motion_estimation_plus_residual.ipynb",
"chars": 54579,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {\n \"slideshow\": {\n \"slide_type\": \"slide\"\n }\n },"
},
{
"path": "image_as_3d_array.ipynb",
"chars": 448637,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {\n \"slideshow\": {\n \"slide_type\": \"slide\"\n }\n },"
},
{
"path": "image_transform_frequency_domain.ipynb",
"chars": 1752407,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {\n \"slideshow\": {\n \"slide_type\": \"slide\"\n }\n },"
},
{
"path": "s/cut_smaller_video.sh",
"chars": 280,
"preview": "#!/bin/bash\n\n#./s/ffmpeg -y -i /files/v/bunny_1080p_60fps.mp4 -ss 00:01:24 -t 00:00:10 /files/v/small_bunny_1080p_60fps"
},
{
"path": "s/download_video.sh",
"chars": 430,
"preview": "#!/bin/bash\n\n# the links aren't work anymore :(\n# wget -c -O v/bunny_1080p_60fps.mp4 http://distribution.bbb3d.renderfar"
},
{
"path": "s/ffmpeg",
"chars": 71,
"preview": "#!/bin/bash\ndocker run --rm -v $(pwd):/files jrottenberg/ffmpeg:3.3 $@\n"
},
{
"path": "s/mediainfo",
"chars": 73,
"preview": "#!/bin/bash\ndocker run --rm -v $(pwd):/files leandromoreira/mediainfo $@\n"
},
{
"path": "s/start_jupyter.sh",
"chars": 160,
"preview": "#!/bin/bash\n# Run Jupyter Notebook (Python 3.5.2, scipy 0.17.1)\ndocker run --rm -p 8888:8888 -v \"$(pwd)\":/home/jovyan/wo"
},
{
"path": "s/vmaf",
"chars": 71,
"preview": "#!/bin/bash\ndocker run --rm -v $(pwd):/files nonatomiclabs/vmaf:1.0 $@\n"
},
{
"path": "setup.sh",
"chars": 255,
"preview": "#!/bin/bash\ncheck_cmd()\n{\n if ! which $1 &>/dev/null; then\n error \"$1 command not found, you must install it before."
},
{
"path": "uniform_quantization_experience.ipynb",
"chars": 124187,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {\n \"slideshow\": {\n \"slide_type\": \"slide\"\n }\n },"
}
]
About this extraction
This page contains the full source code of the leandromoreira/digital_video_introduction GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (3.2 MB), approximately 846.1k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.