├── s ├── ffmpeg ├── vmaf ├── mediainfo ├── start_jupyter.sh ├── cut_smaller_video.sh └── download_video.sh ├── i ├── DAR.png ├── PAR.png ├── drm.png ├── eyes.jpg ├── range.png ├── vbr.png ├── video.png ├── ycbcr.png ├── minimal.png ├── smw_bg.png ├── applicat.jpg ├── dct_basis.png ├── dctfrequ.jpg ├── gray_image.png ├── nal_units.png ├── quantize.png ├── range_show.png ├── resolution.png ├── z_char_8x8.png ├── avc_vs_hevc.png ├── frame_types.png ├── re-quantize.png ├── slice_header.png ├── smw_bg_block.png ├── smw_residual.png ├── original_frames.png ├── pixel_matrice.png ├── second_subrange.png ├── smw_background.png ├── ycbcr_420_merge.png ├── yuv_histogram.png ├── arithimetic_range.png ├── difference_frames.png ├── drm_decoder_flow.jpeg ├── drm_general_flow.jpeg ├── motion_estimation.png ├── nes-color-palette.png ├── slice_nal_idr_bin.png ├── smw_bg_prediction.png ├── solid_background.png ├── super_mario_head.jpg ├── super_mario_head.png ├── super_mario_world.png ├── token_protection.png ├── adaptive_streaming.png ├── av1_browser_analyzer.png ├── ffmpeg_oscilloscope.png ├── general_architecture.png ├── image_3d_matrix_rgb.png ├── kernel_convolution.jpg ├── luminance_vs_color.png ├── macro_blocks_ffmpeg.png ├── mediainfo_details_1.png ├── mediainfo_details_2.png ├── minimal_yuv420_bin.png ├── minimal_yuv420_hex.png ├── new_pixel_geometry.jpg ├── picture_partitioning.png ├── progressive_download.png ├── repetition_in_time.png ├── repetitions_in_space.png ├── solid_background_1.png ├── solid_background_2.png ├── solid_background_3.png ├── solid_background_4.png ├── codec_history_timeline.png ├── dct_coefficient_image.png ├── dct_coefficient_values.png ├── dct_coefficient_zeroed.png ├── h264_intra_predictions.png ├── motion_vectors_ffmpeg.png ├── original_vs_quantized.png ├── rgb_channels_intensity.png ├── smw_background_ball_1.png ├── smw_background_ball_2.png ├── smw_background_ball_3.png ├── smw_background_ball_4.png ├── intel-video-pro-analyzer.png ├── interlaced_vs_progressive.png ├── jpeg_quantization_table.png ├── minimal_yuv420_hex_string.png ├── solid_background_ball_1.png ├── solid_background_ball_2.png ├── thor_codec_block_diagram.png ├── chroma_subsampling_examples.jpg ├── smw_background_ball_2_diff.png ├── h264_bitstream_macro_diagram.png ├── real_world_motion_compensation.png ├── ycbcr_subsampling_resolution.png ├── original_frames_motion_estimation.png ├── comparison_delta_vs_motion_estimation.png ├── H.264_block_diagram_with_quality_score.jpg ├── paritions_view_intel_video_pro_analyzer.png ├── inter_prediction_intel_video_pro_analyzer.png └── intra_prediction_intel_video_pro_analyzer.png ├── .gitignore ├── v ├── small_bunny_1080p_30fps.mp4 └── small_bunny_1080p_60fps.mp4 ├── setup.sh ├── clean_docker.sh ├── LICENSE ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md ├── encoding_pratical_examples.md ├── README-cn.md ├── README-ja.md └── README-ko.md /s/ffmpeg: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker run --rm -v $(pwd):/files jrottenberg/ffmpeg:3.3 $@ 3 | -------------------------------------------------------------------------------- /s/vmaf: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker run --rm -v $(pwd):/files nonatomiclabs/vmaf:1.0 $@ 3 | -------------------------------------------------------------------------------- /i/DAR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/DAR.png -------------------------------------------------------------------------------- /i/PAR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/PAR.png -------------------------------------------------------------------------------- /i/drm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/drm.png -------------------------------------------------------------------------------- /i/eyes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/eyes.jpg -------------------------------------------------------------------------------- /i/range.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/range.png -------------------------------------------------------------------------------- /i/vbr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/vbr.png -------------------------------------------------------------------------------- /i/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/video.png -------------------------------------------------------------------------------- /i/ycbcr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/ycbcr.png -------------------------------------------------------------------------------- /s/mediainfo: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker run --rm -v $(pwd):/files leandromoreira/mediainfo $@ 3 | -------------------------------------------------------------------------------- /i/minimal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/minimal.png -------------------------------------------------------------------------------- /i/smw_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/smw_bg.png -------------------------------------------------------------------------------- /i/applicat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/applicat.jpg -------------------------------------------------------------------------------- /i/dct_basis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/dct_basis.png -------------------------------------------------------------------------------- /i/dctfrequ.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/dctfrequ.jpg -------------------------------------------------------------------------------- /i/gray_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/gray_image.png -------------------------------------------------------------------------------- /i/nal_units.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/nal_units.png -------------------------------------------------------------------------------- /i/quantize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/quantize.png -------------------------------------------------------------------------------- /i/range_show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/range_show.png -------------------------------------------------------------------------------- /i/resolution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/resolution.png -------------------------------------------------------------------------------- /i/z_char_8x8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/z_char_8x8.png -------------------------------------------------------------------------------- /i/avc_vs_hevc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/avc_vs_hevc.png -------------------------------------------------------------------------------- /i/frame_types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/frame_types.png -------------------------------------------------------------------------------- /i/re-quantize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/re-quantize.png -------------------------------------------------------------------------------- /i/slice_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/slice_header.png -------------------------------------------------------------------------------- /i/smw_bg_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/smw_bg_block.png -------------------------------------------------------------------------------- /i/smw_residual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/smw_residual.png -------------------------------------------------------------------------------- /i/original_frames.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/original_frames.png -------------------------------------------------------------------------------- /i/pixel_matrice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/pixel_matrice.png -------------------------------------------------------------------------------- /i/second_subrange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/second_subrange.png -------------------------------------------------------------------------------- /i/smw_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/smw_background.png -------------------------------------------------------------------------------- /i/ycbcr_420_merge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/ycbcr_420_merge.png -------------------------------------------------------------------------------- /i/yuv_histogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/yuv_histogram.png -------------------------------------------------------------------------------- /i/arithimetic_range.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/arithimetic_range.png -------------------------------------------------------------------------------- /i/difference_frames.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/difference_frames.png -------------------------------------------------------------------------------- /i/drm_decoder_flow.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/drm_decoder_flow.jpeg -------------------------------------------------------------------------------- /i/drm_general_flow.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/drm_general_flow.jpeg -------------------------------------------------------------------------------- /i/motion_estimation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/motion_estimation.png -------------------------------------------------------------------------------- /i/nes-color-palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/nes-color-palette.png -------------------------------------------------------------------------------- /i/slice_nal_idr_bin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/slice_nal_idr_bin.png -------------------------------------------------------------------------------- /i/smw_bg_prediction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/smw_bg_prediction.png -------------------------------------------------------------------------------- /i/solid_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/solid_background.png -------------------------------------------------------------------------------- /i/super_mario_head.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/super_mario_head.jpg -------------------------------------------------------------------------------- /i/super_mario_head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/super_mario_head.png -------------------------------------------------------------------------------- /i/super_mario_world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/super_mario_world.png -------------------------------------------------------------------------------- /i/token_protection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/token_protection.png -------------------------------------------------------------------------------- /i/adaptive_streaming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/adaptive_streaming.png -------------------------------------------------------------------------------- /i/av1_browser_analyzer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/av1_browser_analyzer.png -------------------------------------------------------------------------------- /i/ffmpeg_oscilloscope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/ffmpeg_oscilloscope.png -------------------------------------------------------------------------------- /i/general_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/general_architecture.png -------------------------------------------------------------------------------- /i/image_3d_matrix_rgb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/image_3d_matrix_rgb.png -------------------------------------------------------------------------------- /i/kernel_convolution.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/kernel_convolution.jpg -------------------------------------------------------------------------------- /i/luminance_vs_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/luminance_vs_color.png -------------------------------------------------------------------------------- /i/macro_blocks_ffmpeg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/macro_blocks_ffmpeg.png -------------------------------------------------------------------------------- /i/mediainfo_details_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/mediainfo_details_1.png -------------------------------------------------------------------------------- /i/mediainfo_details_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/mediainfo_details_2.png -------------------------------------------------------------------------------- /i/minimal_yuv420_bin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/minimal_yuv420_bin.png -------------------------------------------------------------------------------- /i/minimal_yuv420_hex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/minimal_yuv420_hex.png -------------------------------------------------------------------------------- /i/new_pixel_geometry.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/new_pixel_geometry.jpg -------------------------------------------------------------------------------- /i/picture_partitioning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/picture_partitioning.png -------------------------------------------------------------------------------- /i/progressive_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/progressive_download.png -------------------------------------------------------------------------------- /i/repetition_in_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/repetition_in_time.png -------------------------------------------------------------------------------- /i/repetitions_in_space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/repetitions_in_space.png -------------------------------------------------------------------------------- /i/solid_background_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/solid_background_1.png -------------------------------------------------------------------------------- /i/solid_background_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/solid_background_2.png -------------------------------------------------------------------------------- /i/solid_background_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/solid_background_3.png -------------------------------------------------------------------------------- /i/solid_background_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/solid_background_4.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | v/* 2 | !v/small_bunny_1080p_60fps.mp4 3 | !v/small_bunny_1080p_30fps.mp4 4 | .ipynb_checkpoints/ 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /i/codec_history_timeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/codec_history_timeline.png -------------------------------------------------------------------------------- /i/dct_coefficient_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/dct_coefficient_image.png -------------------------------------------------------------------------------- /i/dct_coefficient_values.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/dct_coefficient_values.png -------------------------------------------------------------------------------- /i/dct_coefficient_zeroed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/dct_coefficient_zeroed.png -------------------------------------------------------------------------------- /i/h264_intra_predictions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/h264_intra_predictions.png -------------------------------------------------------------------------------- /i/motion_vectors_ffmpeg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/motion_vectors_ffmpeg.png -------------------------------------------------------------------------------- /i/original_vs_quantized.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/original_vs_quantized.png -------------------------------------------------------------------------------- /i/rgb_channels_intensity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/rgb_channels_intensity.png -------------------------------------------------------------------------------- /i/smw_background_ball_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/smw_background_ball_1.png -------------------------------------------------------------------------------- /i/smw_background_ball_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/smw_background_ball_2.png -------------------------------------------------------------------------------- /i/smw_background_ball_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/smw_background_ball_3.png -------------------------------------------------------------------------------- /i/smw_background_ball_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/smw_background_ball_4.png -------------------------------------------------------------------------------- /i/intel-video-pro-analyzer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/intel-video-pro-analyzer.png -------------------------------------------------------------------------------- /i/interlaced_vs_progressive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/interlaced_vs_progressive.png -------------------------------------------------------------------------------- /i/jpeg_quantization_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/jpeg_quantization_table.png -------------------------------------------------------------------------------- /i/minimal_yuv420_hex_string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/minimal_yuv420_hex_string.png -------------------------------------------------------------------------------- /i/solid_background_ball_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/solid_background_ball_1.png -------------------------------------------------------------------------------- /i/solid_background_ball_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/solid_background_ball_2.png -------------------------------------------------------------------------------- /i/thor_codec_block_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/thor_codec_block_diagram.png -------------------------------------------------------------------------------- /v/small_bunny_1080p_30fps.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/v/small_bunny_1080p_30fps.mp4 -------------------------------------------------------------------------------- /v/small_bunny_1080p_60fps.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/v/small_bunny_1080p_60fps.mp4 -------------------------------------------------------------------------------- /i/chroma_subsampling_examples.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/chroma_subsampling_examples.jpg -------------------------------------------------------------------------------- /i/smw_background_ball_2_diff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/smw_background_ball_2_diff.png -------------------------------------------------------------------------------- /i/h264_bitstream_macro_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/h264_bitstream_macro_diagram.png -------------------------------------------------------------------------------- /i/real_world_motion_compensation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/real_world_motion_compensation.png -------------------------------------------------------------------------------- /i/ycbcr_subsampling_resolution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/ycbcr_subsampling_resolution.png -------------------------------------------------------------------------------- /i/original_frames_motion_estimation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/original_frames_motion_estimation.png -------------------------------------------------------------------------------- /i/comparison_delta_vs_motion_estimation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/comparison_delta_vs_motion_estimation.png -------------------------------------------------------------------------------- /i/H.264_block_diagram_with_quality_score.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/H.264_block_diagram_with_quality_score.jpg -------------------------------------------------------------------------------- /i/paritions_view_intel_video_pro_analyzer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/paritions_view_intel_video_pro_analyzer.png -------------------------------------------------------------------------------- /i/inter_prediction_intel_video_pro_analyzer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/inter_prediction_intel_video_pro_analyzer.png -------------------------------------------------------------------------------- /i/intra_prediction_intel_video_pro_analyzer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobizt/digital_video_introduction/master/i/intra_prediction_intel_video_pro_analyzer.png -------------------------------------------------------------------------------- /s/start_jupyter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Run Jupyter Notebook (Python 3.5.2, scipy 0.17.1) 3 | docker run --rm -p 8888:8888 -v $(pwd):/home/jovyan/work jupyter/scipy-notebook:387f29b6ca83 4 | -------------------------------------------------------------------------------- /s/cut_smaller_video.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #./s/ffmpeg -y -i /files/v/bunny_1080p_60fps.mp4 -ss 00:01:24 -t 00:00:10 /files/v/small_bunny_1080p_60fps.mp4 4 | #./s/ffmpeg -y -i /files/v/bunny_1080p_30fps.mp4 -ss 00:01:24 -t 00:00:10 /files/v/small_bunny_1080p_30fps.mp4 5 | 6 | echo "These files are already downloaded" 7 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | check_cmd() 3 | { 4 | if ! which $1 &>/dev/null; then 5 | error "$1 command not found, you must install it before." 6 | fi 7 | } 8 | 9 | error() 10 | { 11 | echo "Error: $*" >&2 12 | exit 1 13 | } 14 | 15 | check_cmd docker 16 | check_cmd wget 17 | 18 | ./s/download_video.sh 19 | ./s/cut_smaller_video.sh 20 | -------------------------------------------------------------------------------- /s/download_video.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # the links aren't work anymore :( 4 | # wget -c -O v/bunny_1080p_60fps.mp4 http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_60fps_normal.mp4 5 | # wget -c -O v/bunny_1080p_30fps.mp4 http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4 6 | 7 | echo "we already provide the smaller versions at https://github.com/leandromoreira/digital_video_introduction/tree/master/v" 8 | -------------------------------------------------------------------------------- /clean_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | EXITED=$(docker ps -q -f status=exited) 3 | DANGLING=$(docker images -q -f "dangling=true") 4 | DANGLING_VOLUME=$(docker volume ls -qf "dangling=true") 5 | 6 | if [ "$1" == "--dry-run" ]; then 7 | echo "==> Would stop containers:" 8 | echo $EXITED 9 | echo "==> And images:" 10 | echo $DANGLING 11 | else 12 | if [ -n "$EXITED" ]; then 13 | docker rm $EXITED 14 | else 15 | echo "No containers to remove." 16 | fi 17 | if [ -n "$DANGLING" ]; then 18 | docker rmi $DANGLING 19 | else 20 | echo "No images to remove." 21 | fi 22 | if [ -n "$DANGLING_VOLUME" ]; then 23 | docker volume rm $DANGLING_VOLUME 24 | else 25 | echo "No volumes to remove." 26 | fi 27 | fi 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Leandro Moreira 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | First off, thank you for considering contributing to this repo. It's people 4 | like you that make this material such a great tutorial. 5 | 6 | ### 1. Found a bug, have a question or want to add a new feature 7 | 8 | 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)! 9 | 10 | ### 2. Fork & create a branch 11 | 12 | > * **Ensure the bug was not already reported** by [searching all issues](https://github.com/leandromoreira/digital_video_introduction/issues?q=). 13 | 14 | If this is something you think you can fix, then 15 | [fork digital_video_introduction](https://help.github.com/articles/fork-a-repo) 16 | and create a branch with a descriptive name. 17 | 18 | A good branch name would be (where issue #325 is the ticket you're working on): 19 | 20 | ```sh 21 | git checkout -b 325-add-japanese-translations 22 | ``` 23 | 24 | ### 3. Implement your fix or feature 25 | 26 | At this point, you're ready to make your changes! 27 | 28 | ### 4. Make a Pull Request 29 | 30 | At this point, you should switch back to your master branch and make sure it's 31 | up to date with Active Admin's master branch: 32 | 33 | ```sh 34 | git remote add upstream git@github.com:leandromoreira/digital_video_introduction.git 35 | git checkout master 36 | git pull upstream master 37 | ``` 38 | 39 | Then update your feature branch from your local copy of master, and push it! 40 | 41 | ```sh 42 | git checkout 325-add-japanese-translations 43 | git rebase master 44 | git push --set-upstream origin 325-add-japanese-translations 45 | ``` 46 | 47 | Finally, go to GitHub and 48 | [make a Pull Request](https://help.github.com/articles/creating-a-pull-request) 49 | :D 50 | 51 | ### 5. Merging a PR (maintainers only) 52 | 53 | A PR can only be merged into master by a maintainer if: 54 | 55 | * It was reviewed. 56 | * It is up to date with current master. 57 | 58 | Any maintainer is allowed to merge a PR if all of these conditions are 59 | met. 60 | 61 | PS: this contributing text was inspired by active admin contributing. 62 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | 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. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | 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. 28 | 29 | 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. 30 | 31 | ## Scope 32 | 33 | 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. 34 | 35 | ## Enforcement 36 | 37 | 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. 38 | 39 | 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. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /encoding_pratical_examples.md: -------------------------------------------------------------------------------- 1 | [![license](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg) 2 | 3 | # Introduction 4 | 5 | Please make sure you run `./setup.sh` first. 6 | 7 | # General commands 8 | 9 | ## Inspect stream 10 | 11 | To see some details: 12 | 13 | ``` 14 | ./s/mediainfo /files/v/small_bunny_1080p_30fps.mp4 15 | ``` 16 | 17 | To see full details: 18 | 19 | ``` 20 | ./s/mediainfo --Details /files/v/small_bunny_1080p_30fps.mp4 21 | # I don't know why Details is not on man page 22 | ``` 23 | 24 | To see only the frame, slice types: 25 | 26 | ``` 27 | ./s/mediainfo --Details /files/v/small_bunny_1080p_30fps.mp4 | grep slice_type 28 | ``` 29 | ## Transmuxing 30 | 31 | From `mp4` to `ts`: 32 | 33 | ``` 34 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 /files/v/small_bunny_1080p_30fps.ts 35 | ``` 36 | 37 | From `mp4` to `ts` explicitly telling to copy audio and video codec: 38 | 39 | ``` 40 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -c:a copy -c:v copy /files/v/small_bunny_1080p_30fps.ts 41 | ``` 42 | 43 | ## Transcoding 44 | 45 | From `h264` to `vp9`: 46 | 47 | ``` 48 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -c:v libvpx-vp9 -c:a libvorbis /files/v/small_bunny_1080p_30fps_vp9.webm 49 | ``` 50 | 51 | From `h264` to `h265`: 52 | 53 | ``` 54 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -c:v libx265 /files/v/small_bunny_1080p_30fps_h265.mp4 55 | ``` 56 | 57 | From `h264` to `h264` with I-frame at each second (for a 30FPS video): 58 | 59 | ``` 60 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -c:v libx264 -x264-params keyint=30:min-keyint=30:no-scenecut=1 -c:a copy /files/v/small_bunny_1080p_30fps_h264_keyframe_each_second.mp4 61 | ``` 62 | 63 | Count how many `I-slice` (keyframes) were inserted: 64 | 65 | ``` 66 | ./s/mediainfo --Details /files/v/small_bunny_1080p_30fps_h264_keyframe_each_second.mp4 | grep "slice_type I" | wc -l 67 | ``` 68 | 69 | ## Split and merge smoothly 70 | 71 | To work with smaller videos you can split the whole video into segments and you can also merge then after. 72 | 73 | ``` 74 | # spliting into several likely 2s segments 75 | ./s/ffmpeg -fflags +genpts -i /files/v/small_bunny_1080p_30fps.mp4 -map 0 -c copy -f segment -segment_format mp4 -segment_time 2 -segment_list video.ffcat -reset_timestamps 1 -v error chunk-%03d.mp4 76 | 77 | # joining them 78 | ./s/ffmpeg -y -v error -i video.ffcat -map 0 -c copy output.mp4 79 | ``` 80 | 81 | ## 1 I-Frames per second vs 0.5 I-Frames per second 82 | 83 | From `h264` to `h264` with I-frame at each second (for a 30FPS video): 84 | 85 | ``` 86 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -c:v libx264 -x264-params keyint=30:min-keyint=30:no-scenecut=1 -c:a copy /files/v/small_bunny_1080p_30fps_h264_keyframe_each_one_second.mp4 87 | ``` 88 | From `h264` to `h264` with I-frame at each two seconds (for a 30FPS video): 89 | 90 | ``` 91 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -c:v libx264 -x264-params keyint=60:min-keyint=60:no-scenecut=1 -c:a copy /files/v/small_bunny_1080p_30fps_h264_keyframe_each_two_seconds.mp4 92 | ``` 93 | 94 | ## 1 I-frame and the rest P-Frames 95 | 96 | Generates a video with a `single I frame` and the `rest are P frames`. 97 | 98 | ``` 99 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -c:v libx264 -x264-params keyint=300:min-keyint=300:no-scenecut=1:bframes=0 -c:a copy /files/v/small_bunny_1080p_30fps_single_I_rest_P.mp4 100 | ``` 101 | 102 | You can check if that's true: 103 | 104 | ``` 105 | ./s/mediainfo --Details /files/v/small_bunny_1080p_30fps_single_I_rest_P.mp4 | grep "slice_type I" | wc -l 106 | 107 | ./s/mediainfo --Details /files/v/small_bunny_1080p_30fps_single_I_rest_P.mp4 | grep "slice_type P" | wc -l 108 | 109 | ./s/mediainfo --Details /files/v/small_bunny_1080p_30fps_single_I_rest_P.mp4 | grep "slice_type B" | wc -l 110 | 111 | ``` 112 | 113 | ## No B-frames at all 114 | 115 | Generates a video with 0 B-frames. 116 | 117 | ``` 118 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -c:v libx264 -x264-params keyint=30:min-keyint=30:no-scenecut=1:bframes=0 -c:a copy /files/v/small_bunny_1080p_30fps_zero_b_frames.mp4 119 | ``` 120 | 121 | Check if that's right and also compare the size. 122 | 123 | ``` 124 | ./s/mediainfo --Details /files/v/small_bunny_1080p_30fps_zero_b_frames.mp4 | grep "slice_type B" | wc -l 125 | 126 | ls -lah v/ 127 | ``` 128 | 129 | ## CABAC vs CAVLC 130 | 131 | Generates `h264` using CAVLC (faster, less cpu intensive, less compression): 132 | 133 | ``` 134 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -c:v libx264 -x264-params keyint=30:min-keyint=30:no-scenecut=1:no-cabac=1 -c:a copy /files/v/small_bunny_1080p_30fps_h264_keyframe_each_second_CAVLC.mp4 135 | ``` 136 | 137 | Generates `h264` using CABAC ("slower", more cpu intensive, more compression): 138 | 139 | ``` 140 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -c:v libx264 -x264-params keyint=30:min-keyint=30:no-scenecut=1:coder=1 -c:a copy /files/v/small_bunny_1080p_30fps_h264_keyframe_each_second_CABAC.mp4 141 | ``` 142 | 143 | ## Transrating 144 | 145 | CBR from `1928 kbps` to `964 kbps`: 146 | 147 | ``` 148 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -b:v 964K -minrate 964K -maxrate 964K -bufsize 2000K /files/v/small_bunny_1080p_30fps_transrating_964.mp4 149 | ``` 150 | 151 | Constrained VBR or ABR from `1928 kbps` to `max=3856 kbps ,min=964 kbps`: 152 | 153 | ``` 154 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -minrate 964K -maxrate 3856K -bufsize 2000K /files/v/small_bunny_1080p_30fps_transrating_964_3856.mp4 155 | ``` 156 | 157 | ## Transsizing 158 | 159 | From `1080p` to `480p`: 160 | 161 | ``` 162 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -vf scale=480:-1 /files/v/small_bunny_1080p_30fps_transsizing_480.mp4 163 | ``` 164 | 165 | ## Demuxing 166 | 167 | Extracting `audio` from `container`: 168 | 169 | ``` 170 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -vn -c:a copy /files/v/small_bunny_audio.aac 171 | ``` 172 | 173 | ## Muxing 174 | 175 | Joining `audio` with `video`: 176 | 177 | ``` 178 | ./s/ffmpeg -i /files/v/small_bunny_audio.aac -i /files/v/small_bunny_1080p_30fps.mp4 /files/v/small_bunny_1080p_30fps_muxed.mp4 179 | ``` 180 | 181 | ## Generates YUV histogram 182 | 183 | It generates a video with color histogram as an overlay. 184 | ``` 185 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -vf "split=2[a][b],[b]histogram,format=yuv420p[hh],[a][hh]overlay" /files/v/small_bunny_yuv_histogram.mp4 186 | ``` 187 | 188 | ## Generate debug video 189 | 190 | It generates a video with macro blocks debug over the video. Please refer to https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors to understand the meaning of each block color. 191 | ``` 192 | ./s/ffmpeg -debug vis_mb_type -i /files/v/small_bunny_1080p_30fps.mp4 /files/v/small_bunny_1080p_30fps_vis_mb.mp4 193 | ``` 194 | 195 | It generates a video with motion vector over the video. 196 | ``` 197 | ./s/ffmpeg -flags2 +export_mvs -i /files/v/small_bunny_1080p_30fps.mp4 -vf codecview=mv=pf+bf+bb /files/v/small_bunny_1080p_30fps_vis_mv.mp4 198 | ``` 199 | 200 | ## Generate images from video 201 | 202 | Get `images` from `1s video`: 203 | 204 | ``` 205 | ./s/ffmpeg -y -i /files/v/small_bunny_1080p_30fps.mp4 -t 00:00:01 /files/v/smallest_bunny_1080p_30fps_%3d.jpg 206 | ``` 207 | 208 | ## Generate video from images 209 | 210 | ``` 211 | # from one image 212 | ./s/ffmpeg -loop 1 -i /files/v/smallest_bunny_1080p_30fps_001.jpg -c:v libx264 -pix_fmt yuv420p -t 10 /files/v/smallest_bunny_1080p_30fps_frame_001.mp4 213 | 214 | # from multiple images (repeating 10s) 215 | ./s/ffmpeg -loop 1 -i /files/v/smallest_bunny_1080p_30fps_%03d.jpg -c:v libx264 -pix_fmt yuv420p -t 10 /files/v/smallest_bunny_1080p_30fps_from_images.mp4 216 | ``` 217 | 218 | ## Generate a single frame video 219 | 220 | It generates a single frame video which is great for learning and analysis. 221 | 222 | ``` 223 | ./s/ffmpeg -i /files/i/minimal.png -pix_fmt yuv420p /files/v/minimal_yuv420.mp4 224 | 225 | ./s/ffmpeg -i /files/i/minimal.png /files/v/minimal_yuv444.mp4 226 | 227 | # we can inspect h264 bitstream 228 | ./s/mediainfo --Details /files/v/minimal_yuv420.mp4 | less 229 | ``` 230 | 231 | ## Generate a simple video 232 | 233 | It generates a video from a sequence of images. 234 | 235 | ``` 236 | # 2 simple images with white background 237 | ./s/ffmpeg -i /files/i/solid_background_ball_%d.png -pix_fmt yuv420p /files/v/solid_background_ball_yuv420.mp4 238 | 239 | # 4 simple images with background 240 | ./s/ffmpeg -i /files/i/smw_background_ball_%d.png -pix_fmt yuv420p /files/v/smw_background_ball_yuv420.mp4 241 | 242 | # 4 white images as a video (great to test predicitons) 243 | 244 | for i in {1..4}; do cp i/solid_background.png i/solid_background_$i.png; done 245 | ./s/ffmpeg -i /files/i/solid_background_%d.png -pix_fmt yuv420p /files/v/solid_background_yuv420.mp4 246 | ``` 247 | 248 | ## Generate a single frame h264 bitstream 249 | 250 | ``` 251 | ./s/ffmpeg -i /files/i/minimal.png -pix_fmt yuv420p /files/v/minimal_yuv420.h264 252 | 253 | # you can check the raw h264 bit stream 254 | hexdump v/minimal_yuv420.h264 255 | ``` 256 | 257 | ## Audio sampling 258 | 259 | From `original` to `8kHz`: 260 | 261 | ``` 262 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -ar 8000 /files/v/small_bunny_1080p_30fps_8khz.mp4 263 | ``` 264 | 265 | ## Audio bit depth 266 | 267 | From `original` to `8 bits`: 268 | 269 | ``` 270 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps.mp4 -sample_fmt:0:1 u8p /files/v/small_bunny_1080p_30fps_8bits.mp4 -y 271 | ``` 272 | 273 | > Technically speaking, bit depth is only meaningful when applied to pure PCM devices. Non-PCM formats, such as lossy compression systems like MP3, have bit depths that are not defined in the same sense as PCM. In lossy audio compression, where bits are allocated to other types of information, the bits actually allocated to individual samples are allowed to fluctuate within the constraints imposed by the allocation algorithm. 274 | 275 | ## Adaptive bitrate streaming 276 | 277 | [HLS](https://tools.ietf.org/html/draft-pantos-http-live-streaming-20) streaming: 278 | 279 | ### A VOD stream with 1s chunk size 280 | ``` 281 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps_h264_keyframe_each_second.mp4 -c:a copy -c:v libx264 -x264-params keyint=30:min-keyint=30:no-scenecut=1 -hls_playlist_type vod -hls_time 1 /files/v/playlist_keyframe_each_second.m3u8 282 | ``` 283 | 284 | ### Playlists for 720p(2628kbs), 480p(480p1128kbs) and 240p(264kbs) streams 285 | 286 | ``` 287 | ./s/ffmpeg -i /files/v/small_bunny_1080p_30fps_h264_keyframe_each_second.mp4 \ 288 | -c:a copy -c:v libx264 -x264-params keyint=30:min-keyint=30:no-scenecut=1 \ 289 | -b:v 2500k -s 1280x720 -profile:v high -hls_time 1 -hls_playlist_type vod /files/v/720p2628kbs.m3u8 \ 290 | -c:a copy -c:v libx264 -x264-params keyint=30:min-keyint=30:no-scenecut=1 \ 291 | -b:v 1000k -s 854x480 -profile:v high -hls_time 1 -hls_playlist_type vod /files/v/480p1128kbs.m3u8 \ 292 | -c:a copy -c:v libx264 -x264-params keyint=30:min-keyint=30:no-scenecut=1 \ 293 | -b:v 200k -s 426x240 -profile:v high -hls_time 1 -hls_playlist_type vod /files/v/240p264kbs.m3u8 294 | ``` 295 | 296 | ### The variant playlist 297 | ``` 298 | cat < v/variant.m3u8 299 | #EXTM3U 300 | #EXT-X-VERSION:6 301 | #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2500000,CODECS="avc1.640028,mp4a.40.2",RESOLUTION=1280x720 302 | 720p2628kbs.m3u8 303 | #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1000000,CODECS="avc1.4d001f,mp4a.40.2",RESOLUTION=854x480 304 | 480p1128kbs.m3u8 305 | #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=200000,CODECS="avc1.42001f,mp4a.40.2",RESOLUTION=426x240 306 | 240p264kbs.m3u8 307 | EOF 308 | ``` 309 | 310 | ## Video quality perception 311 | 312 | You can learn more about [vmaf](http://techblog.netflix.com/2016/06/toward-practical-perceptual-video.html) and [general video quality perception](https://leandromoreira.com.br/2016/10/09/how-to-measure-video-quality-perception/). 313 | 314 | ``` 315 | # generating a 2 seconds example video 316 | ./s/ffmpeg -y -i /files/v/bunny_1080p_30fps.mp4 -ss 00:01:24 -t 00:00:02 /files/v/smallest_bunny_1080p_30fps.mp4 317 | 318 | # generate a transcoded video (600kbps vp9) 319 | ./s/ffmpeg -i /files/v/smallest_bunny_1080p_30fps.mp4 -c:v libvpx-vp9 -b:v 600K -c:a libvorbis /files/v/smallest_bunny_1080p_30fps_vp9.webm 320 | 321 | # extract the yuv (yuv420p) color space from them 322 | ./s/ffmpeg -i /files/v/smallest_bunny_1080p_30fps.mp4 -c:v rawvideo -pix_fmt yuv420p /files/v/smallest_bunny_1080p_30fps.yuv 323 | ./s/ffmpeg -i /files/v/smallest_bunny_1080p_30fps_vp9.webm -c:v rawvideo -pix_fmt yuv420p /files/v/smallest_bunny_1080p_30fps_vp9.yuv 324 | 325 | # run vmaf original h264 vs transcoded vp9 326 | ./s/vmaf run_vmaf yuv420p 1080 720 /files/v/smallest_bunny_1080p_30fps.yuv /files/v/smallest_bunny_1080p_30fps_vp9.yuv --out-fmt json 327 | ``` 328 | 329 | ## FFMpeg as a library 330 | 331 | There are some documentations, examples and tutorials: 332 | 333 | * http://dranger.com/ffmpeg/tutorial01.html 334 | * https://github.com/leandromoreira/player-ffmpeg 335 | * https://github.com/FFmpeg/FFmpeg/tree/master/doc/examples 336 | * https://www.ffmpeg.org/doxygen/3.2/index.html 337 | -------------------------------------------------------------------------------- /README-cn.md: -------------------------------------------------------------------------------- 1 | [![license](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg) 2 | 3 | # 介绍 4 | 5 | 这是一份循序渐进的视频技术的介绍。尽管它面向的是软件开发人员/工程师,但我们希望**对任何人而言**,这份文档都能简单易学。这个点子产生于一个[视频技术新手小型研讨会](https://docs.google.com/presentation/d/17Z31kEkl_NGJ0M66reqr9_uTG6tI5EDDVXpdPKVuIrs/edit#slide=id.p)期间。 6 | 7 | 本文档旨在尽可能使用**浅显的词语,丰富的图像和实际例子**介绍数字视频概念,使这些知识能适用于各种场合。你可以随时反馈意见或建议,以改进这篇文档。 8 | 9 | “**自己动手**”需要安装 docker,并将这个 repo clone 到你的计算机。 10 | 11 | ```bash 12 | git clone https://github.com/leandromoreira/digital_video_introduction.git 13 | cd digital_video_introduction 14 | ./setup.sh 15 | ``` 16 | 17 | > **注意**:当你看到 `./s/ffmpeg` 或 `./s/mediainfo` 命令时,说明我们运行的是 docker 容器中的版本,容器已经包含了程序所需的依赖。 18 | 19 | 所有的 **“自己动手”应从本 repo 的根目录运行**。**jupyter 的示例**应使用 `./s/start_jupyter.sh` 启动服务器,然后复制 URL 到你的浏览器中使用。 20 | 21 | # 更新日志 22 | 23 | * 增加 DRM 系统 24 | * 发布版本 1.0.0 25 | * 添加简体中文翻译 26 | * 添加FFmpeg oscilloscope滤镜示例 27 | 28 | # 目录 29 | 30 | - [介绍](#介绍) 31 | - [目录](#目录) 32 | - [基本术语](#基本术语) 33 | * [编码彩色图像的其它方法](#编码彩色图像的其它方法) 34 | * [自己动手:玩转图像和颜色](#自己动手玩转图像和颜色) 35 | * [DVD 的 DAR 是 4:3](#dvd-的-dar-是-43) 36 | * [自己动手:检查视频属性](#自己动手检查视频属性) 37 | - [消除冗余](#消除冗余) 38 | * [颜色,亮度和我们的眼睛](#颜色亮度和我们的眼睛) 39 | + [颜色模型](#颜色模型) 40 | + [YCbCr 和 RGB 之间的转换](#ycbcr-和-rgb-之间的转换) 41 | + [色度子采样](#色度子采样) 42 | + [自己动手:检查 YCbCr 直方图](#自己动手检查-ycbcr-直方图) 43 | * [帧类型](#帧类型) 44 | + [I 帧(帧内,关键帧)](#i-帧帧内关键帧) 45 | + [P 帧(预测)](#p-帧预测) 46 | - [自己动手:具有单个 I 帧的视频](#自己动手具有单个-i-帧的视频) 47 | + [B 帧(双向预测)](#b-帧双向预测) 48 | - [自己动手:使用 B 帧比较视频](#自己动手使用-b-帧比较视频) 49 | + [小结](#小结) 50 | * [时间冗余(帧间预测)](#时间冗余帧间预测) 51 | - [自己动手:查看运动向量](#自己动手查看运动向量) 52 | * [空间冗余(帧内预测)](#空间冗余帧内预测) 53 | - [自己动手:查看帧间预测](#自己动手查看帧间预测) 54 | - [视频编解码器是如何工作的?](#视频编解码器是如何工作的) 55 | * [是什么?为什么?怎么做?](#是什么为什么怎么做) 56 | * [历史](#历史) 57 | + [AV1 的诞生](#av1-的诞生) 58 | * [通用编解码器](#通用编解码器) 59 | * [第一步 - 图片分区](#第一步---图片分区) 60 | + [自己动手:查看分区](#自己动手查看分区) 61 | * [第二步 - 预测](#第二步---预测) 62 | * [第三步 - 转换](#第三步---转换) 63 | + [自己动手:丢弃不同的系数](#自己动手丢弃不同的系数) 64 | * [第四步 - 量化](#第四步---量化) 65 | + [自己动手:量化](#自己动手量化) 66 | * [第五步 - 熵编码](#第五步---熵编码) 67 | + [VLC 编码](#vlc-编码) 68 | + [算术编码](#算术编码) 69 | + [自己动手:CABAC vs CAVLC](#自己动手cabac-vs-cavlc) 70 | * [第六步 - 比特流格式](#第六步---比特流格式) 71 | + [H.264 比特流](#h264-比特流) 72 | + [自己动手:检查 H.264 比特流](#自己动手检查-h264-比特流) 73 | * [回顾](#回顾) 74 | * [H.265 如何实现比 H.264 更好的压缩率?](#h265-如何实现比-h264-更好的压缩率) 75 | - [在线流媒体](#在线流媒体) 76 | * [通用架构](#通用架构) 77 | * [逐行下载和自适应流](#逐行下载和自适应流) 78 | * [内容保护](#内容保护) 79 | - [如何使用 jupyter](#如何使用-jupyter) 80 | - [会议](#会议) 81 | - [参考](#参考) 82 | 83 | # 基本术语 84 | 85 | 一个**图像**可以视作一个**二维矩阵**。如果将**色彩**考虑进来,我们可以做出推广:将这个图像视作一个**三维矩阵**——多出来的维度用于储存色彩信息。 86 | 87 | 如果我们选择三原色(红、绿、蓝)代表这些色彩,这就定义了三个平面:第一个是红色平面,第二个是绿色平面,最后一个是蓝色平面。 88 | 89 | ![an image is a 3d matrix RGB](/i/image_3d_matrix_rgb.png "An image is a 3D matrix") 90 | 91 | 我们把这个矩阵里的每一个点称为**像素**(图像元素)。像素的色彩由三原色的**强度**(通常用数值表示)表示。例如,一个**红色像素**是指强度为 0 的绿色,强度为 0 的蓝色和强度最大的红色。**粉色像素**可以通过三种颜色的组合表示。如果规定强度的取值范围是 0 到 255,**红色 255、绿色 192、蓝色 203** 则表示粉色。 92 | 93 | > ### 编码彩色图像的其它方法 94 | > 95 | > 还有许多其它模型也可以用来表示色彩,进而组成图像。例如,给每种颜色都标上序号(如下图),这样每个像素仅需一个字节就可以表示出来,而不是 RGB 模型通常所需的 3 个。在这样一个模型里我们可以用一个二维矩阵来代替三维矩阵去表示我们的色彩,这将节省存储空间,但色彩的数量将会受限。 96 | > 97 | > ![NES palette](/i/nes-color-palette.png "NES palette") 98 | 99 | 100 | 例如以下几张图片。第一张包含所有颜色平面。剩下的分别是红、绿、蓝色平面(显示为灰调)(译注:颜色强度高的地方显示为亮色,强度低为暗色)。 101 | 102 | 103 | ![RGB channels intensity](/i/rgb_channels_intensity.png "RGB channels intensity") 104 | 105 | 我们可以看到,对于最终的成像,红色平面对强度的贡献更多(三个平面最亮的是红色平面),蓝色平面(最后一张图片)的贡献大多只在马里奥的眼睛和他衣服的一部分。所有颜色平面对马里奥的胡子(最暗的部分)均贡献较少。 106 | 107 | 存储颜色的强度,需要占用一定大小的数据空间,这个大小被称为颜色深度。假如每个颜色(平面)的强度占用 8 bit(取值范围为 0 到 255),那么颜色深度就是 24(8*3)bit,我们还可以推导出我们可以使用 2 的 24 次方种不同的颜色。 108 | 109 | > 很棒的学习材料:[现实世界的照片是如何拍摄成 0 和 1 的](http://www.cambridgeincolour.com/tutorials/camera-sensors.htm)。 110 | 111 | 图片的另一个属性是**分辨率**,即一个平面内像素的数量。通常表示成宽*高,例如下面这张 **4x4** 的图片。 112 | 113 | ![image resolution](/i/resolution.png "image resolution") 114 | 115 | > ### 自己动手:玩转图像和颜色 116 | > 117 | > 你可以使用 [jupyter](#如何使用-jupyter)(python, numpy, matplotlib 等等)[玩转图像](/image_as_3d_array.ipynb)。 118 | > 119 | > 你也可以学习[图像滤镜(边缘检测,锐化,模糊。。。)的原理](/filters_are_easy.ipynb)。 120 | 121 | 图像或视频还有一个属性是宽高比,它简单地描述了图像或像素的宽度和高度之间的比例关系。 122 | 123 | 当人们说这个电影或照片是 16:9 时,通常是指显示宽高比(DAR),然而我们也可以有不同形状的单个像素,我们称为像素宽高比(PAR)。 124 | 125 | ![display aspect ratio](/i/DAR.png "display aspect ratio") 126 | 127 | ![pixel aspect ratio](/i/PAR.png "pixel aspect ratio") 128 | 129 | > ## DVD 的 DAR 是 4:3 130 | > 131 | > 虽然 DVD 的实际分辨率是 704x480,但它依然保持 4:3 的宽高比,因为它有一个 10:11(704x10/480x11)的 PAR。 132 | 133 | 现在我们可以将**视频**定义为在**单位时间**内**连续的 n 帧**,这可以视作一个新的维度,n 即为帧率,若单位时间为秒,则等同于 FPS (每秒帧数 Frames Per Second)。 134 | 135 | ![video](/i/video.png "video") 136 | 137 | 播放一段视频每秒所需的数据量就是它的**比特率**(即常说的码率)。 138 | > 比特率 = 宽 * 高 * 颜色深度 * 帧每秒 139 | 140 | 例如,一段每秒 30 帧,每像素 24 bits,分辨率是 480x240 的视频,如果我们不做任何压缩,它将需要 **82,944,000 比特每秒**或 82.944 Mbps (30x480x240x24)。 141 | 142 | 当**比特率**几乎恒定时称为恒定比特率(**CBR**);但它也可以变化,称为可变比特率(**VBR**)。 143 | 144 | > 这个图形显示了一个受限的 VBR,当帧为黑色时不会花费太多的数据量。 145 | > 146 | > ![constrained vbr](/i/vbr.png "constrained vbr") 147 | 148 | 在早期,工程师们想出了一项技术能将视频的感官帧率加倍而**没有消耗额外带宽**。这项技术被称为**隔行扫描**;总的来说,它在一个时间点发送一个画面——画面用于填充屏幕的一半,而下一个时间点发送的画面用于填充屏幕的另一半。 149 | 150 | 如今的屏幕渲染大多使用**逐行扫描技术**。这是一种显示、存储、传输运动图像的方法,每帧中的所有行都会被依次绘制。 151 | 152 | ![interlaced vs progressive](/i/interlaced_vs_progressive.png "interlaced vs progressive") 153 | 154 | 现在我们知道了数字化**图像**的原理;它的**颜色**的编排方式;给定**帧率**和**分辨率**时,展示一个视频需要花费多少**比特率**;它是恒定的(CBR)还是可变的(VBR);还有很多其它内容,如隔行扫描和 PAR。 155 | 156 | > ## 自己动手:检查视频属性 157 | > 你可以[使用 ffmpeg 或 mediainfo 检查大多数属性的解释](https://github.com/leandromoreira/introduction_video_technology/blob/master/encoding_pratical_examples.md#inspect-stream)。 158 | 159 | # 消除冗余 160 | 161 | 我们认识到,不对视频进行压缩是不行的;**一个单独的一小时长的视频**,分辨率为 720p 和 30fps 时将**需要 278GB\***。仅仅使用无损数据压缩算法——如 DEFLATE(被PKZIP, Gzip, 和 PNG 使用)——也无法充分减少视频所需的带宽,我们需要找到其它压缩视频的方法。 162 | 163 | > *我们使用乘积得出这个数字 1280 x 720 x 24 x 30 x 3600 (宽,高,每像素比特数,fps 和秒数) 164 | 165 | 为此,我们可以**利用视觉特性**:和区分颜色相比,我们区分亮度要更加敏锐。**时间上的重复**:一段视频包含很多只有一点小小改变的图像。**图像内的重复**:每一帧也包含很多颜色相同或相似的区域。 166 | 167 | ## 颜色,亮度和我们的眼睛 168 | 169 | 我们的眼睛[对亮度比对颜色更敏感](http://vanseodesign.com/web-design/color-luminance/),你可以看看下面的图片自己测试。 170 | 171 | ![luminance vs color](/i/luminance_vs_color.png "luminance vs color") 172 | 173 | 如果你看不出左图的**方块 A 和方块 B** 的颜色是**相同的**,那么好,是我们的大脑玩了一个小把戏,这让我们更多的去注意光与暗,而不是颜色。右边这里有一个使用同样颜色的连接器,那么我们(的大脑)就能轻易分辨出事实,它们是同样的颜色。 174 | 175 | > **简单解释我们的眼睛工作的原理** 176 | > 177 | > [眼睛是一个复杂的器官](http://www.biologymad.com/nervoussystem/eyenotes.htm),有许多部分组成,但我们最感兴趣的是视锥细胞和视杆细胞。眼睛有[大约1.2亿个视杆细胞和6百万个视锥细胞](https://en.wikipedia.org/wiki/Photoreceptor_cell)。 178 | > 179 | > **简单来说**,让我们把颜色和亮度放在眼睛的功能部位上。[视杆细胞](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)。 180 | > 181 | > 既然我们的视杆细胞(亮度)比视锥细胞多很多,一个合理的推断是相比颜色,我们有更好的能力去区分黑暗和光亮。 182 | > 183 | > ![eyes composition](/i/eyes.jpg "eyes composition") 184 | 185 | 一旦我们知道我们对**亮度**(图像中的亮度)更敏感,我们就可以利用它。 186 | 187 | ### 颜色模型 188 | 189 | 我们最开始学习的[彩色图像的原理](#基本术语)使用的是 **RGB 模型**,但也有其他模型。有一种模型将亮度(光亮)和色度(颜色)分离开,它被称为 **YCbCr***。 190 | 191 | > * 有很多种模型做同样的分离。 192 | 193 | 这个颜色模型使用 **Y** 来表示亮度,还有两种颜色通道:Cb(蓝色色度) 和 Cr(红色色度)。YCbCr 可以由 RGB 转换得来,也可以转换回 RGB。使用这个模型我们可以创建拥有完整色彩的图像,如下图。 194 | 195 | ![ycbcr 例子](/i/ycbcr.png "ycbcr 例子") 196 | 197 | ### YCbCr 和 RGB 之间的转换 198 | 199 | 有人可能会问,在 **不使用绿色(色度)** 的情况下,我们如何表现出所有的色彩? 200 | 201 | 为了回答这个问题,我们将介绍从 RGB 到 YCbCr 的转换。我们将使用 [ITU-R 小组](https://en.wikipedia.org/wiki/ITU-R)*建议的[标准 BT.601](https://en.wikipedia.org/wiki/Rec._601) 中的系数。 202 | 203 | 第一步是计算亮度,我们将使用 ITU 建议的常量,并替换 RGB 值。 204 | 205 | ``` 206 | Y = 0.299R + 0.587G + 0.114B 207 | ``` 208 | 209 | 一旦我们有了亮度后,我们就可以拆分颜色(蓝色色度和红色色度): 210 | 211 | ``` 212 | Cb = 0.564(B - Y) 213 | Cr = 0.713(R - Y) 214 | ``` 215 | 216 | 并且我们也可以使用 YCbCr 转换回来,甚至得到绿色。 217 | 218 | ``` 219 | R = Y + 1.402Cr 220 | B = Y + 1.772Cb 221 | G = Y - 0.344Cb - 0.714Cr 222 | ``` 223 | 224 | > *组织和标准在数字视频领域中很常见,它们通常定义什么是标准,例如,[什么是 4K?我们应该使用什么帧率?分辨率?颜色模型?](https://en.wikipedia.org/wiki/Rec._2020) 225 | 226 | 通常,**显示屏**(监视器,电视机,屏幕等等)**仅使用 RGB 模型**,并以不同的方式来组织,看看下面这些放大效果: 227 | 228 | ![pixel geometry](/i/new_pixel_geometry.jpg "pixel geometry") 229 | 230 | ### 色度子采样 231 | 232 | 一旦我们能从图像中分离出亮度和色度,我们就可以利用人类视觉系统对亮度比色度更敏感的特点,选择性地剔除信息。**色度子采样**是一种编码图像时,使**色度分辨率低于亮度**的技术。 233 | 234 | ![ycbcr 子采样分辨率](/i/ycbcr_subsampling_resolution.png "ycbcr 子采样分辨率") 235 | 236 | 我们应该减少多少色度分辨率呢?已经有一些模式定义了如何处理分辨率和合并(`最终的颜色 = Y + Cb + Cr`)。 237 | 238 | 这些模式称为子采样系统,并被表示为 3 部分的比率 - `a:x:y`,其定义了色度平面的分辨率,与亮度平面上的、分辨率为 `a x 2` 的小块之间的关系。 239 | * `a` 是水平采样参考 (通常是 4), 240 | * `x` 是第一行的色度样本数(相对于 a 的水平分辨率), 241 | * `y` 是第二行的色度样本数。 242 | 243 | > 存在的一个例外是 4:1:0,其在每个亮度平面分辨率为 4 x 4 的块内提供一个色度样本。 244 | 245 | 现代编解码器中使用的常用方案是: 4:4:4 (没有子采样), 4:2:2, 4:1:1, 4:2:0, 4:1:0 and 3:1:1。 246 | 247 | > YCbCr 4:2:0 合并 248 | > 249 | > 这是使用 YCbCr 4:2:0 合并的一个图像的一块,注意我们每像素只花费 12bit。 250 | > 251 | > ![YCbCr 4:2:0 合并](/i/ycbcr_420_merge.png "YCbCr 4:2:0 合并") 252 | 253 | 下图是同一张图片使用几种主要的色度子采样技术进行编码,第一行图像是最终的 YCbCr,而最后一行图像展示了色度的分辨率。这么小的损失确实是一个伟大的胜利。 254 | 255 | ![色度子采样例子](/i/chroma_subsampling_examples.jpg "色度子采样例子") 256 | 257 | 前面我们计算过我们需要 [278GB 去存储一个一小时长,分辨率在720p和30fps的视频文件](#消除冗余)。如果我们使用 `YCbCr 4:2:0` 我们能减少`一半的大小(139GB)`*,但仍然不够理想。 258 | > * 我们通过将宽、高、颜色深度和 fps 相乘得出这个值。前面我们需要 24 bit,现在我们只需要 12 bit。 259 | 260 | > ### 自己动手:检查 YCbCr 直方图 261 | > 你可以[使用 ffmpeg 检查 YCbCr 直方图](/encoding_pratical_examples.md#generates-yuv-histogram)。这个场景有更多的蓝色贡献,由[直方图](https://en.wikipedia.org/wiki/Histogram)显示。 262 | > 263 | > ![ycbcr 颜色直方图](/i/yuv_histogram.png "ycbcr 颜色直方图") 264 | 265 | ### 颜色, 亮度, 视频亮度, 伽马 视频回顾 266 | 267 | 观看这段精彩的视频,它解释什么是亮度并了解视频亮度、伽马和颜色。 268 | [![模拟亮度 - 视频的历史和解释](http://img.youtube.com/vi/Ymt47wXUDEU/0.jpg)](http://www.youtube.com/watch?v=Ymt47wXUDEU) 269 | 270 | > ### 自己动手: 检查 YCbCr 强度 271 | > 你可以使用[FFmpeg's oscilloscope滤镜](https://ffmpeg.org/ffmpeg-filters.html#oscilloscope)可视化给定视频行的Y强度. 272 | > ```bash 273 | > ffplay -f lavfi -i 'testsrc2=size=1280x720:rate=30000/1001,format=yuv420p' -vf oscilloscope=x=0.5:y=200/720:s=1:c=1 274 | > ``` 275 | > ![y 颜色示波器](/i/ffmpeg_oscilloscope.png "y 颜色 示波器") 276 | 277 | ## 帧类型 278 | 279 | 现在我们进一步消除`时间冗余`,但在这之前让我们来确定一些基本术语。假设我们一段 30fps 的影片,这是最开始的 4 帧。 280 | 281 | ![球 1](/i/smw_background_ball_1.png "球 1") ![球 2](/i/smw_background_ball_2.png "球 2") ![球 3](/i/smw_background_ball_3.png "球 3") 282 | ![球 4](/i/smw_background_ball_4.png "球 4") 283 | 284 | 我们可以在帧内看到**很多重复内容**,如**蓝色背景**,从 0 帧到第 3 帧它都没有变化。为了解决这个问题,我们可以将它们**抽象地分类**为三种类型的帧。 285 | 286 | ### I 帧(帧内,关键帧) 287 | 288 | I 帧(可参考,关键帧,帧内编码)是一个**自足的帧**。它不依靠任何东西来渲染,I 帧与静态图片相似。第一帧通常是 I 帧,但我们将看到 I 帧被定期插入其它类型的帧之间。 289 | 290 | ![球 1](/i/smw_background_ball_1.png "球 1") 291 | 292 | ### P 帧(预测) 293 | 294 | P 帧利用了一个事实:当前的画面几乎总能**使用之前的一帧进行渲染**。例如,在第二帧,唯一的改变是球向前移动了。仅仅使用(第二帧)对前一帧的引用和差值,我们就能重建前一帧。 295 | 296 | ![球 1](/i/smw_background_ball_1.png "球 1") <- ![球 2](/i/smw_background_ball_2_diff.png "球 2") 297 | 298 | > #### 自己动手:具有单个 I 帧的视频 299 | > 既然 P 帧使用较少的数据,为什么我们不能用[单个 I 帧和其余的 P 帧](/encoding_pratical_examples.md#1-i-frame-and-the-rest-p-frames)来编码整个视频? 300 | > 301 | > 编码完这个视频之后,开始观看它,并**快进到视频的末尾部分**,你会注意到**它需要花一些时间**才真正跳转到这部分。这是因为 **P 帧需要一个引用帧**(比如 I 帧)才能渲染。 302 | > 303 | > 你可以做的另一个快速试验,是使用单个 I 帧编码视频,然后[再次编码且每 2 秒插入一个 I 帧](/encoding_pratical_examples.md#1-i-frames-per-second-vs-05-i-frames-per-second),并**比较成品的大小**。 304 | 305 | ### B 帧(双向预测) 306 | 307 | 如何引用前面和后面的帧去做更好的压缩?!简单地说 B 帧就是这么做的。 308 | 309 | ![球 1](/i/smw_background_ball_1.png "球 1") <- ![球 2](/i/smw_background_ball_2_diff.png "球 2") -> ![球 3](/i/smw_background_ball_3.png "球 3") 310 | 311 | > #### 自己动手:使用 B 帧比较视频 312 | > 你可以生成两个版本,一个使用 B 帧,另一个[全部不使用 B 帧](/encoding_pratical_examples.md#no-b-frames-at-all),然后查看文件的大小以及画质。 313 | 314 | ### 小结 315 | 316 | 这些帧类型用于提供更好的压缩率,我们将在下一章看到这是如何发生的。现在,我们可以想到 I 帧是昂贵的,P 帧是便宜的,最便宜的是 B 帧。 317 | 318 | ![帧类型例子](/i/frame_types.png "帧类型例子") 319 | 320 | ## 时间冗余(帧间预测) 321 | 322 | 让我们探究去除**时间上的重复**,去除这一类冗余的技术就是**帧间预测**。 323 | 324 | 我们将尝试**花费较少的数据量**去编码在时间上连续的 0 号帧和 1 号帧。 325 | 326 | ![原始帧](/i/original_frames.png "原始帧") 327 | 328 | 我们可以做个减法,我们简单地**用 0 号帧减去 1 号帧**,得到残差,这样我们就只需要**对残差进行编码**。 329 | 330 | ![残差帧](/i/difference_frames.png "残差帧") 331 | 332 | 但我们有一个**更好的方法**来节省数据量。首先,我们将`0 号帧` 视为一个个分块的集合,然后我们将尝试将 `帧 1` 和 `帧 0` 上的块相匹配。我们可以将这看作是**运动预测**。 333 | 334 | > ### 维基百科—块运动补偿 335 | > “运动补偿是一种描述相邻帧(相邻在这里表示在编码关系上相邻,在播放顺序上两帧未必相邻)差别的方法,具体来说是描述前面一帧(相邻在这里表示在编码关系上的前面,在播放顺序上未必在当前帧前面)的每个小块怎样移动到当前帧中的某个位置去。” 336 | 337 | ![原始帧运动预测](/i/original_frames_motion_estimation.png "原始帧运动预测") 338 | 339 | 我们预计那个球会从 `x=0, y=25` 移动到 `x=6, y=26`,**x** 和 **y** 的值就是**运动向量**。**进一步**节省数据量的方法是,只编码这两者运动向量的差。所以,最终运动向量就是 `x=6 (6-0), y=1 (26-25)`。 340 | 341 | > 实际情况下,这个球会被切成 n 个分区,但处理过程是相同的。 342 | 343 | 帧上的物体**以三维方式移动**,当球移动到背景时会变小。当我们尝试寻找匹配的块,**找不到完美匹配的块**是正常的。这是一张运动预测与实际值相叠加的图片。 344 | 345 | ![运动预测](/i/motion_estimation.png "运动预测") 346 | 347 | 但我们能看到当我们使用**运动预测**时,**编码的数据量少于**使用简单的残差帧技术。 348 | 349 | ![运动预测 vs 残差 ](/i/comparison_delta_vs_motion_estimation.png "运动预测 vs 残差") 350 | 351 | 你可以[使用 jupyter 玩转这些概念](/frame_difference_vs_motion_estimation_plus_residual.ipynb)。 352 | 353 | > ### 自己动手:查看运动向量 354 | > 355 | > 我们可以[使用 ffmpeg 生成包含帧间预测(运动向量)的视频](/encoding_pratical_examples.md#generate-debug-video)。 356 | > 357 | > ![ffmpeg 帧间预测(运动向量)](/i/motion_vectors_ffmpeg.png "ffmpeg 帧间预测(运动向量)") 358 | > 359 | > 或者我们也可使用 [Intel® Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer)(需要付费,但也有只能查看前 10 帧的免费试用版)。 360 | > 361 | > ![Intel® Video Pro Analyzer 使用帧间预测](/i/inter_prediction_intel_video_pro_analyzer.png "inter prediction intel video pro analyzer") 362 | 363 | ## 空间冗余(帧内预测) 364 | 如果我们分析一个视频里的**每一帧**,我们会看到有**许多区域是相互关联的**。 365 | 366 | ![空间内重复](/i/repetitions_in_space.png "空间内重复") 367 | 368 | 让我们举一个例子。这个场景大部分由蓝色和白色组成。 369 | 370 | ![smw 背景](/i/smw_bg.png "smw 背景") 371 | 372 | 这是一个 `I 帧`,我们**不能使用前面的帧来预测**,但我们仍然可以压缩它。我们将编码我们选择的那块红色区域。如果我们**看看它的周围**,我们可以**估计它周围颜色的变化**。 373 | 374 | ![smw 背景块](/i/smw_bg_block.png "smw 背景块") 375 | 376 | 我们预测:帧中的颜色在垂直方向上保持一致,这意味着**未知像素的颜色与临近的像素相同**。 377 | 378 | ![smw 背景预测](/i/smw_bg_prediction.png "smw 背景预测") 379 | 380 | 我们的**预测会出错**,所以我们需要先利用这项技术(**帧内预测**),然后**减去实际值**,算出残差,得出的矩阵比原始数据更容易压缩。 381 | 382 | ![smw 残差](/i/smw_residual.png "smw 残差") 383 | 384 | > ### 自己动手:查看帧内预测 385 | > 你可以[使用 ffmpeg 生成包含宏块及预测的视频](/encoding_pratical_examples.md#generate-debug-video)。请查看 ffmpeg 文档以了解[每个块颜色的含义](https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors)。 386 | > 387 | > ![ffmpeg 帧内预测(宏块)](/i/macro_blocks_ffmpeg.png "ffmpeg 帧内预测(宏块)") 388 | > 389 | > 或者我们也可使用 [Intel® Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer)(需要付费,但也有只能查看前 10 帧的免费试用版)。 390 | > 391 | > ![Intel® Video Pro Analyzer 帧内预测](/i/intra_prediction_intel_video_pro_analyzer.png "Intel® Video Pro Analyzer 帧内预测") 392 | 393 | # 视频编解码器是如何工作的? 394 | 395 | ## 是什么?为什么?怎么做? 396 | 397 | **是什么?** 就是用于压缩或解压数字视频的软件或硬件。**为什么?** 人们需要在有限带宽或存储空间下提高视频的质量。还记得当我们计算每秒 30 帧,每像素 24 bit,分辨率是 480x240 的视频[需要多少带宽](#基本术语)吗?没有压缩时是 **82.944 Mbps**。电视或互联网提供 HD/FullHD/4K 只能靠视频编解码器。**怎么做?** 我们将简单介绍一下主要的技术。 398 | 399 | > 视频编解码 vs 容器 400 | > 401 | > 初学者一个常见的错误是混淆数字视频编解码器和[数字视频容器](https://en.wikipedia.org/wiki/Digital_container_format)。我们可以将**容器**视为包含视频(也很可能包含音频)元数据的包装格式,**压缩过的视频**可以看成是它承载的内容。 402 | > 403 | > 通常,视频文件的格式定义其视频容器。例如,文件 `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) 来完全确定编解码器和容器格式。 404 | 405 | ## 历史 406 | 407 | 在我们跳进通用编解码器内部工作之前,让我们回头了解一些旧的视频编解码器。 408 | 409 | 视频编解码器 [H.261](https://en.wikipedia.org/wiki/H.261) 诞生在 1990(技术上是 1988),被设计为以 **64 kbit/s 的数据速率**工作。它已经使用如色度子采样、宏块,等等理念。在 1995 年,**H.263** 视频编解码器标准被发布,并继续延续到 2001 年。 410 | 411 | 在 2003 年 **H.264/AVC** 的第一版被完成。在同一年,一家叫做 **TrueMotion** 的公司发布了他们的**免版税**有损视频压缩的视频编解码器,称为 **VP3**。在 2008 年,**Google 收购了**这家公司,在同一年发布 **VP8**。在 2012 年 12 月,Google 发布了 **VP9**,**市面上大约有 3/4 的浏览器**(包括手机)支持。 412 | 413 | [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 号**。 414 | 415 | ![编解码器历史线路图](/i/codec_history_timeline.png "编解码器历史线路图") 416 | 417 | > ### AV1 的诞生 418 | > 419 | > 2015 年早期,Google 正在开发VP10,Xiph (Mozilla) 正在开发Daala,Cisco 开源了其称为 Thor 的免版税视频编解码器。 420 | > 421 | > 接着 MPEG LA 宣布了 HEVC (H.265) 每年版税的的上限,比 H.264 高 8 倍,但很快他们又再次改变了条款: 422 | > * **不设年度收费上限** 423 | > * **收取内容费**(收入的 0.5%) 424 | > * **每单位费用高于 h264 的 10 倍** 425 | > 426 | > [开放媒体联盟](http://aomedia.org/about/)由硬件厂商(Intel, AMD, ARM , Nvidia, Cisco),内容分发商(Google, Netflix, Amazon),浏览器维护者(Google, Mozilla),等公司创建。 427 | > 428 | > 这些公司有一个共同目标,一个免版税的视频编解码器,所以 AV1 诞生时使用了一个更[简单的专利许可证](http://aomedia.org/license/patent/)。**Timothy B. Terriberry** 做了一个精彩的介绍,[关于 AV1 的概念,许可证模式和它当前的状态](https://www.youtube.com/watch?v=lzPaldsmJbk),就是本节的来源。 429 | > 430 | > 前往 [https://arewecompressedyet.com/analyzer/](https://arewecompressedyet.com/analyzer/), 你会惊讶于**使用你的浏览器就可以分析 AV1 编解码器**。 431 | > ![av1 浏览器分析器](/i/av1_browser_analyzer.png "浏览器分析器") 432 | > 433 | > 附:如果你想了解更多编解码器的历史,你需要了解[视频压缩专利](https://www.vcodex.com/video-compression-patents/)背后的基本知识。 434 | 435 | ## 通用编解码器 436 | 437 | 我们接下来要介绍**通用视频编解码器背后的主要机制**,大多数概念都很实用,并被现代编解码器如 VP9, AV1 和 HEVC 使用。需要注意:我们将简化许多内容。有时我们会使用真实的例子(主要是 H.264)来演示技术。 438 | 439 | ## 第一步 - 图片分区 440 | 441 | 第一步是**将帧**分成几个**分区**,**子分区**甚至更多。 442 | 443 | ![图片分区](/i/picture_partitioning.png "图片分区") 444 | 445 | **但是为什么呢**有许多原因,比如,当我们分割图片时,我们可以更精确的处理预测,在微小移动的部分使用较小的分区,而在静态背景上使用较大的分区。 446 | 447 | 通常,编解码器**将这些分区组织**成切片(或瓦片),宏(或编码树单元)和许多子分区。这些分区的最大大小有所不同,HEVC 设置成 64x64,而 AVC 使用 16x16,但子分区可以达到 4x4 的大小。 448 | 449 | 还记得我们学过的**帧的分类**吗?你也可以**把这些概念应用到块**,因此我们可以有 I 切片,B 切片,I 宏块等等。 450 | 451 | > ### 自己动手:查看分区 452 | > 453 | > 我们也可以使用 [Intel® Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer)(需要付费,但也有只能查看前 10 帧的免费试用版)。这是 [VP9 分区](/encoding_pratical_examples.md#transcoding)的分析。 454 | > 455 | > ![Intel® Video Pro Analyzer VP9 分区视图 ](/i/paritions_view_intel_video_pro_analyzer.png "Intel® Video Pro Analyzer VP9 分区视图") 456 | 457 | ## 第二步 - 预测 458 | 459 | 一旦我们有了分区,我们就可以在它们之上做出预测。对于[帧间预测](#时间冗余帧间预测),我们需要**发送运动向量和残差**;至于[帧内预测](#空间冗余帧内预测),我们需要**发送预测方向和残差**。 460 | 461 | ## 第三步 - 转换 462 | 463 | 在我们得到残差块(`预测分区-真实分区`)之后,我们可以用一种方式**变换**它,这样我们就知道**哪些像素我们应该丢弃**,还依然能保持**整体质量**。这个确切的行为有几种变换方式。 464 | 465 | 尽管有[其它的变换方式](https://en.wikipedia.org/wiki/List_of_Fourier-related_transforms#Discrete_transforms),但我们重点关注离散余弦变换(DCT)。[DCT](https://en.wikipedia.org/wiki/Discrete_cosine_transform) 的主要功能有: 466 | * 将**像素**块**转换**为相同大小的**频率系数块**。 467 | * **压缩**能量,更容易消除空间冗余。 468 | * **可逆的**,也意味着你可以还原回像素。 469 | 470 | > 2017 年 2 月 2 号,F. M. Bayer 和 R. J. Cintra 发表了他们的论文:[图像压缩的 DCT 类变换只需要 14 个加法](https://arxiv.org/abs/1702.00817)。 471 | 472 | 如果你不理解每个要点的好处,不用担心,我们会尝试进行一些实验,以便从中看到真正的价值。 473 | 474 | 我们来看下面的**像素块**(8x8): 475 | 476 | ![像素值矩形](/i/pixel_matrice.png "像素值矩形") 477 | 478 | 下面是其渲染的块图像(8x8): 479 | 480 | ![像素值矩形](/i/gray_image.png "像素值矩形") 481 | 482 | 当我们对这个像素块**应用 DCT** 时, 得到如下**系数块**(8x8): 483 | 484 | ![系数值 values](/i/dct_coefficient_values.png "系数值") 485 | 486 | 接着如果我们渲染这个系数块,就会得到这张图片: 487 | 488 | ![dct 系数图片](/i/dct_coefficient_image.png "dct 系数图片") 489 | 490 | 如你所见它看起来完全不像原图像,我们可能会注意到**第一个系数**与其它系数非常不同。第一个系数被称为直流分量,代表了输入数组中的**所有样本**,有点**类似于平均值**。 491 | 492 | 这个系数块有一个有趣的属性:高频部分和低频部分是分离的。 493 | 494 | ![dct 频率系数属性](/i/dctfrequ.jpg "dct 频率系数属性") 495 | 496 | 在一张图像中,**大多数能量**会集中在[低频部分](https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm),所以如果我们将图像转换成频率系数,并**丢掉高频系数**,我们就能**减少描述图像所需的数据量**,而不会牺牲太多的图像质量。 497 | > 频率是指信号变化的速度。 498 | 499 | 让我们通过实验学习这点,我们将使用 DCT 把原始图像转换为频率(系数块),然后丢掉最不重要的系数。 500 | 501 | 首先,我们将它转换为其**频域**。 502 | 503 | ![系数值](/i/dct_coefficient_values.png "系数值") 504 | 505 | 然后我们丢弃部分(67%)系数,主要是它的右下角部分。 506 | 507 | ![系数清零](/i/dct_coefficient_zeroed.png "系数清零") 508 | 509 | 然后我们从丢弃的系数块重构图像(记住,这需要可逆),并与原始图像相比较。 510 | 511 | ![原始 vs 量化](/i/original_vs_quantized.png "原始 vs 量化") 512 | 513 | 如我们所见它酷似原始图像,但它引入了许多与原来的不同,我们**丢弃了67.1875%**,但我们仍然得到至少类似于原来的东西。我们可以更加智能的丢弃系数去得到更好的图像质量,但这是下一个主题。 514 | 515 | > ### 使用全部像素形成每个系数 516 | > 517 | > 需要注意的是,每个系数并不直接映射到单个像素,而是所有像素的加权和。这个神奇的图形展示了如何使用每个指数唯一的权重来计算第一个和第二个系数。 518 | > 519 | > ![dct 计算](/i/applicat.jpg "dct 计算") 520 | > 521 | > 来源:[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) 522 | > 523 | > 你也可以尝试[通过查看在 DCT 基础上形成的简单图片来可视化 DCT](/dct_better_explained.ipynb)。例如,这是使用每个系数权重[形成的字符 A](https://en.wikipedia.org/wiki/Discrete_cosine_transform#Example_of_IDCT)。 524 | > 525 | > ![](https://upload.wikimedia.org/wikipedia/commons/5/5e/Idct-animation.gif ) 526 | 527 |
528 | 529 | > ### 自己动手:丢弃不同的系数 530 | > 你可以玩转 [DCT 变换](/uniform_quantization_experience.ipynb) 531 | 532 | ## 第四步 - 量化 533 | 534 | 当我们丢弃一些系数时,在最后一步(变换),我们做了一些形式的量化。这一步,我们选择性地剔除信息(**有损部分**)或者简单来说,我们将**量化系数以实现压缩**。 535 | 536 | 我们如何量化一个系数块?一个简单的方法是均匀量化,我们取一个块并**将其除以单个的值**(10),并舍入值。 537 | 538 | ![量化](/i/quantize.png "量化") 539 | 540 | 我们如何**逆转**(重新量化)这个系数块?我们可以通过**乘以我们先前除以的相同的值**(10)来做到。 541 | 542 | ![逆转量化](/i/re-quantize.png "逆转量化") 543 | 544 | 这**不是最好的方法**,因为它没有考虑到每个系数的重要性,我们可以使用一个**量化矩阵**来代替单个值,这个矩阵可以利用 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)。 545 | 546 | > ### 自己动手:量化 547 | > 你可以玩转[量化](/dct_experiences.ipynb) 548 | 549 | ## 第五步 - 熵编码 550 | 551 | 在我们量化数据(图像块/切片/帧)之后,我们仍然可以以无损的方式来压缩它。有许多方法(算法)可用来压缩数据。我们将简单体验其中几个,你可以阅读这本很棒的书去深入理解:[Understanding Compression: Data Compression for Modern Developers](https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/)。 552 | 553 | ### VLC 编码: 554 | 555 | 让我们假设我们有一个符号流:**a**, **e**, **r** 和 **t**,它们的概率(从0到1)由下表所示。 556 | 557 | | | a | e | r | t | 558 | |-----|-----|-----|------|-----| 559 | | 概率 | 0.3 | 0.3 | 0.2 | 0.2 | 560 | 561 | 562 | 我们可以分配不同的二进制码,(最好是)小的码给最可能(出现的字符),大些的码给最少可能(出现的字符)。 563 | 564 | | | a | e | r | t | 565 | |--------|-----|-----|------|-----| 566 | | 概率 | 0.3 | 0.3 | 0.2 | 0.2 | 567 | | 二进制码 | 0 | 10 | 110 | 1110 | 568 | 569 | 570 | 让我们压缩 **eat** 流,假设我们为每个字符花费 8 bit,在没有做任何压缩时我们将花费 **24 bit**。但是在这种情况下,我们使用各自的代码来替换每个字符,我们就能节省空间。 571 | 572 | 第一步是编码字符 **e** 为 `10`,第二个字符是 **a**,追加(不是数学加法)后是 `[10][0]`,最后是第三个字符 **t**,最终组成已压缩的比特流 `[10][0][1110]` 或 `1001110`,这只需 **7 bit**(比原来的空间少 3.4 倍)。 573 | 574 | 请注意每个代码必须是唯一的前缀码,[Huffman 能帮你找到这些数字](https://en.wikipedia.org/wiki/Huffman_coding)。虽然它有一些问题,但是[视频编解码器仍然提供该方法](https://en.wikipedia.org/wiki/Context-adaptive_variable-length_coding),它也是很多应用程序的压缩算法。 575 | 576 | 编码器和解码器都**必须知道**这个(包含编码的)字符表,因此,你也需要传送这个表。 577 | 578 | ### 算术编码 579 | 580 | 让我们假设我们有一个符号流:**a**, **e**, **r**, **s** 和 **t**,它们的概率由下表所示。 581 | 582 | | | a | e | r | s | t | 583 | |-----|-----|-----|------|------|-----| 584 | | 概率 | 0.3 | 0.3 | 0.15 | 0.05 | 0.2 | 585 | 586 | 587 | 考虑到这个表,我们可以构建一个区间,区间包含了所有可能的字符,字符按出现概率排序。 588 | 589 | ![初始算法区间](/i/range.png "初始算法区间") 590 | 591 | 让我们编码 **eat** 流,我们选择第一个字符 **e** 位于 **0.3 到 0.6** (但不包括 0.6)的子区间,我们选择这个子区间,按照之前同等的比例再次分割。 592 | 593 | ![第二个子区间](/i/second_subrange.png "第二个子区间") 594 | 595 | 让我们继续编码我们的流 **eat**,现在使第二个 **a** 字符位于 **0.3 到 0.39** 的区间里,接着再次用同样的方法编码最后的字符 **t**,得到最后的子区间 **0.354 到 0.372**。 596 | 597 | ![最终算法区间](/i/arithimetic_range.png "最终算法区间") 598 | 599 | 我们只需从最后的子区间 0.354 到 0.372 里选择一个数,让我们选择 0.36,不过我们可以选择这个子区间里的任何数。仅靠这个数,我们将可以恢复原始流 **eat**。就像我们在区间的区间里画了一根线来编码我们的流。 600 | 601 | ![最终区间横断面](/i/range_show.png "最终区间横断面") 602 | 603 | **反向过程**(又名解码)一样简单,用数字 **0.36** 和我们原始区间,我们可以进行同样的操作,不过现在是使用这个数字来还原被编码的流。 604 | 605 | 在第一个区间,我们发现数字落入了一个子区间,因此,这个子区间是我们的第一个字符,现在我们再次切分这个子区间,像之前一样做同样的过程。我们会注意到 **0.36** 落入了 **a** 的区间,然后我们重复这一过程直到得到最后一个字符 **t**(形成我们原始编码过的流 eat)。 606 | 607 | 编码器和解码器都**必须知道**字符概率表,因此,你也需要传送这个表。 608 | 609 | 非常巧妙,不是吗?人们能想出这样的解决方案实在是太聪明了,一些[视频编解码器使用](https://en.wikipedia.org/wiki/Context-adaptive_binary_arithmetic_coding)这项技术(或至少提供这一选择)。 610 | 611 | 关于无损压缩量化比特流的办法,这篇文章无疑缺少了很多细节、原因、权衡等等。作为一个开发者你[应该学习更多](https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/)。刚入门视频编码的人可以尝试使用不同的[熵编码算法,如ANS](https://en.wikipedia.org/wiki/Asymmetric_Numeral_Systems)。 612 | 613 | > ### 自己动手:CABAC vs CAVLC 614 | > 你可以[生成两个流,一个使用 CABAC,另一个使用 CAVLC](https://github.com/leandromoreira/introduction_video_technology/blob/master/encoding_pratical_examples.md#cabac-vs-cavlc),并比较生成每一个的时间以及最终的大小。 615 | 616 | ## 第六步 - 比特流格式 617 | 618 | 完成所有这些步之后,我们需要将**压缩过的帧和内容打包进去**。需要明确告知解码器**编码定义**,如颜色深度,颜色空间,分辨率,预测信息(运动向量,帧内预测方向),档次\*,级别\*,帧率,帧类型,帧号等等更多信息。 619 | > * 译注:原文为 profile 和 level,没有通用的译名 620 | 621 | 我们将简单地学习 H.264 比特流。第一步是[生成一个小的 H.264\* 比特流](/encoding_pratical_examples.md#generate-a-single-frame-h264-bitstream),可以使用本 repo 和 [ffmpeg](http://ffmpeg.org/) 来做。 622 | 623 | ``` 624 | ./s/ffmpeg -i /files/i/minimal.png -pix_fmt yuv420p /files/v/minimal_yuv420.h264 625 | ``` 626 | 627 | > * ffmpeg 默认将所有参数添加为 **SEI NAL**,很快我们会定义什么是 NAL。 628 | 629 | 这个命令会使用下面的图片作为帧,生成一个具有**单个帧**,64x64 和颜色空间为 yuv420 的原始 h264 比特流。 630 | > ![使用帧来生成极简 h264 比特流](/i/minimal.png "使用帧来生成极简 h264 比特流") 631 | 632 | ### H.264 比特流 633 | 634 | AVC (H.264) 标准规定信息将在宏帧(网络概念上的)内传输,称为 [NAL](https://en.wikipedia.org/wiki/Network_Abstraction_Layer)(网络抽象层)。NAL 的主要目标是提供“网络友好”的视频呈现方式,该标准必须适用于电视(基于流),互联网(基于数据包)等。 635 | 636 | ![H.264 NAL 单元](/i/nal_units.png "H.264 NAL 单元") 637 | 638 | [同步标记](https://en.wikipedia.org/wiki/Frame_synchronization)用来定义 NAL 单元的边界。每个同步标记的值固定为 `0x00 0x00 0x01` ,最开头的标记例外,它的值是 `0x00 0x00 0x00 0x01` 。如果我们在生成的 h264 比特流上运行 **hexdump**,我们可以在文件的开头识别至少三个 NAL。 639 | 640 | ![NAL 单元上的同步标记](/i/minimal_yuv420_hex.png "NAL 单元上的同步标记") 641 | 642 | 我们之前说过,解码器需要知道不仅仅是图片数据,还有视频的详细信息,如:帧、颜色、使用的参数等。每个 NAL 的**第一位**定义了其分类和**类型**。 643 | 644 | | NAL type id | 描述 | 645 | |--- |---| 646 | | 0 | Undefined | 647 | | 1 | Coded slice of a non-IDR picture | 648 | | 2 | Coded slice data partition A | 649 | | 3 | Coded slice data partition B | 650 | | 4 | Coded slice data partition C | 651 | | 5 | **IDR** Coded slice of an IDR picture | 652 | | 6 | **SEI** Supplemental enhancement information | 653 | | 7 | **SPS** Sequence parameter set | 654 | | 8 | **PPS** Picture parameter set | 655 | | 9 | Access unit delimiter | 656 | | 10 | End of sequence | 657 | | 11 | End of stream | 658 | | ... | ... | 659 | 660 | 通常,比特流的第一个 NAL 是 **SPS**,这个类型的 NAL 负责传达通用编码参数,如**档次,级别,分辨率**等。 661 | 662 | 如果我们跳过第一个同步标记,就可以通过解码**第一个字节**来了解第一个 **NAL 的类型**。 663 | 664 | 例如同步标记之后的第一个字节是 `01100111`,第一位(`0`)是 **forbidden_zero_bit** 字段,接下来的两位(`11`)告诉我们是 **nal_ref_idc** 字段,其表示该 NAL 是否是参考字段,其余 5 位(`00111`)告诉我们是 **nal_unit_type** 字段,在这个例子里是 NAL 单元 **SPS** (7)。 665 | 666 | SPS NAL 的第 2 位 (`binary=01100100, hex=0x64, dec=100`) 是 **profile_idc** 字段,显示编码器使用的配置,在这个例子里,我们使用[高阶档次](https://en.wikipedia.org/wiki/H.264/MPEG-4_AVC#Profiles),一种没有 B(双向预测) 切片支持的高阶档次。 667 | 668 | ![SPS 二进制视图](/i/minimal_yuv420_bin.png "SPS 二进制视图") 669 | 670 | 当我们阅读 SPS NAL 的 H.264 比特流规范时,会为**参数名称**,**分类**和**描述**找到许多值,例如,看看字段 `pic_width_in_mbs_minus_1` 和 `pic_height_in_map_units_minus_1`。 671 | 672 | | 参数名称 | 分类 | 描述 | 673 | |--- |---|---| 674 | | pic_width_in_mbs_minus_1 | 0 | ue(v) | 675 | | pic_height_in_map_units_minus_1 | 0 | ue(v) | 676 | 677 | > **ue(v)**: 无符号整形 [Exp-Golomb-coded](https://pythonhosted.org/bitstring/exp-golomb.html) 678 | 679 | 如果我们对这些字段的值进行一些计算,将最终得出**分辨率**。我们可以使用值为 `119( (119 + 1) * macroblock_size = 120 * 16 = 1920)`的 `pic_width_in_mbs_minus_1` 表示 `1920 x 1080`,再次为了减少空间,我们使用 `119` 来代替编码 `1920`。 680 | 681 | 如果我们再次使用二进制视图检查我们创建的视频 (ex: `xxd -b -c 11 v/minimal_yuv420.h264`),可以跳到帧自身上一个 NAL。 682 | 683 | ![h264 idr 切片头](/i/slice_nal_idr_bin.png "h264 idr 切片头") 684 | 685 | 我们可以看到最开始的 6 个字节:`01100101 10001000 10000100 00000000 00100001 11111111`。我们已经知道第一个字节告诉我们 NAL 的类型,在这个例子里, (`00101`) 是 **IDR 切片 (5)**,可以进一步检查它: 686 | 687 | ![h264 切片头规格](/i/slice_header.png "h264 切片头规格") 688 | 689 | 对照规范,我们能解码切片的类型(**slice_type**),帧号(**frame_num**)等重要字段。 690 | 691 | 为了获得一些字段(`ue(v), me(v), se(v) 或 te(v)`)的值,我们需要称为 [Exponential-Golomb](https://pythonhosted.org/bitstring/exp-golomb.html) 的特定解码器来解码它。当存在很多默认值时,这个方法编码变量值特别高效。 692 | 693 | > 这个视频里 **slice_type** 和 **frame_num** 的值是 7(I 切片)和 0(第一帧)。 694 | 695 | 我们可以将**比特流视为一个协议**,如果你想学习更多关于比特流的内容,请参考 [ITU H.264 规范](http://www.itu.int/rec/T-REC-H.264-201610-I)。这个宏观图展示了图片数据(压缩过的 YUV)所在的位置。 696 | 697 | ![h264 比特流宏观图](/i/h264_bitstream_macro_diagram.png "h264 比特流宏观图") 698 | 699 | 我们可以探究其它比特流,如 [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/),但只要学习了其中之一,学习其他的就简单多了。 700 | 701 | > ### 自己动手:检查 H.264 比特流 702 | > 703 | > 我们可以[生成一个单帧视频](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)。 704 | > 705 | > ![mediainfo h264 比特流的详情 ](/i/mediainfo_details_1.png "mediainfo h264 比特流的详情") 706 | > 707 | > 我们也可使用 [Intel® Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer),需要付费,但也有只能查看前 10 帧的免费试用版,这已经够达成学习目的了。 708 | > 709 | > ![Intel® Video Pro Analyzer h264 比特流的详情](/i/intel-video-pro-analyzer.png "Intel® Video Pro Analyzer h264 比特流的详情") 710 | 711 | ## 回顾 712 | 713 | 我们可以看到我们学了许多**使用相同模型的现代编解码器**。事实上,让我们看看 Thor 视频编解码器框图,它包含所有我们学过的步骤。你现在应该能更好地理解数字视频领域内的创新和论文。 714 | ![thor 编解码器块图](/i/thor_codec_block_diagram.png "thor 编解码器块图") 715 | 716 | 之前我们计算过我们[需要 139GB 来保存一个一小时,720p 分辨率和30fps的视频文件](#色度子采样),如果我们使用在这里学过的技术,如**帧间和帧内预测,转换,量化,熵编码和其它**我们能实现——假设我们**每像素花费 0.031 bit**——同样观感质量的视频,**对比 139GB 的存储,只需 367.82MB**。 717 | > 我们根据这里提供的示例视频选择**每像素使用 0.031 bit**。 718 | 719 | ## H.265 如何实现比 H.264 更好的压缩率 720 | 721 | 我们已经更多地了解了编解码器的工作原理,那么就容易理解新的编解码器如何使用更少的数据量传输更高分辨率的视频。 722 | 723 | 我们将比较 AVC 和 HEVC,要记住的是:我们几乎总是要在压缩率和更多的 CPU 周期(复杂度)之间作权衡。 724 | 725 | HEVC 比 AVC 有更大和更多的**分区**(和**子分区**)选项,更多**帧内预测方向**,**改进的熵编码**等,所有这些改进使得 H.265 比 H.264 的压缩率提升 50%。 726 | 727 | ![h264 vs h265](/i/avc_vs_hevc.png "H.264 vs H.265") 728 | 729 | # 在线流媒体 730 | ## 通用架构 731 | 732 | ![general_architecture](/i/general_architecture.png) 733 | 734 | [TODO] 735 | 736 | ## 渐进式下载和自适应流 737 | 738 | ![progressive_download](/i/progressive_download.png) 739 | 740 | ![adaptive_streaming](/i/adaptive_streaming.png) 741 | 742 | [TODO] 743 | 744 | ## 内容保护 745 | 746 | 我们可以用一个简单的令牌认证系统来保护视频。用户需要拥有一个有效的令牌才可以播放视频,CDN 会拒绝没有令牌的用户的请求。它与大多数网站的身份认证系统非常相似。 747 | 748 | ![token_protection](/i/token_protection.png) 749 | 750 | 仅仅使用令牌认证系统,用户仍然可以下载并重新分发视频。DRM 系统可以用来避免这种情况。 751 | 752 | ![drm](/i/drm.png) 753 | 754 | 实际情况下,人们通常同时使用这两种技术提供授权和认证。 755 | 756 | ### DRM 757 | #### 主要系统 758 | 759 | * FPS - [**FairPlay Streaming**](https://developer.apple.com/streaming/fps/) 760 | * PR - [**PlayReady**](https://www.microsoft.com/playready/) 761 | * WV - [**Widevine**](http://www.widevine.com/) 762 | 763 | #### 是什么 764 | 765 | DRM 指的是数字版权管理,是一种**为数字媒体提供版权保护**的方法,例如数字视频和音频。尽管用在了很多场合,但它并[没有被普遍接受](https://en.wikipedia.org/wiki/Digital_rights_management#DRM-free_works). 766 | 767 | #### 为什么 768 | 769 | 内容的创作者(大多是工作室/制片厂)希望保护他们的知识产权,使他们的数字媒体免遭未经授权的分发。 770 | 771 | #### 怎么做 772 | 773 | 我们将用一种简单的、抽象的方式描述 DRM 774 | 775 | 现有一份**内容 C1**(如 HLS 或 DASH 视频流),一个**播放器 P1**(如 shaka-clappr, exo-player 或 iOS),装在**设备 D1**(如智能手机、电视或台式机/笔记本)上,使用 **DRM 系统 DRM1**(如 FairPlay Streaming, PlayReady, Widevine) 776 | 777 | 内容 C1 由 DRM1 用一个**对称密钥 K1** 加密,生成**加密内容 C'1** 778 | 779 | ![DRM 一般流程](/i/drm_general_flow.jpeg "DRM 一般流程") 780 | 781 | 设备 D1 上的播放器 P1 有一个非对称密钥对,密钥对包含一个**私钥 PRK1**(这个密钥是受保护的1,只有 **D1** 知道密钥内容),和一个**公钥 PUK1** 782 | 783 | > **1受保护的**: 这种保护可以**通过硬件**进行保护,例如, 将这个密钥存储在一个特殊的芯片(只读)中,芯片的工作方式就像一个用来解密的[黑箱]。 或**通过软件**进行保护(较低的安全系数)。DRM 系统提供了识别设备所使用的保护类型的方法。 784 | 785 | 当 **播放器 P1 希望播放***加密内容 C'1** 时,它需要与 **DRM1** 协商,将公钥 **PUK1** 发送给 DRM1, DRM1 会返回一个被公钥 **PUK1** **加密过的 K1**。按照推论,结果就是**只有 D1 能够解密**。 786 | 787 | `K1P1D1 = enc(K1, PUK1)` 788 | 789 | **P1** 使用它的本地 DRM 系统(这可以使用 [SoC](https://zh.wikipedia.org/wiki/系统芯片) ,一个专门的硬件和软件,这个系统可以使用它的私钥 PRK1 用来**解密**内容,它可以解密被加密过的**K1P1D1 的对称密钥 K1**。理想情况下,密钥不会被导出到内存以外的地方。 790 | 791 | ``` 792 | K1 = dec(K1P1D1, PRK1) 793 | 794 | P1.play(dec(C'1, K1)) 795 | ``` 796 | 797 | ![DRM 解码流程](/i/drm_decoder_flow.jpeg "DRM 解码流程") 798 | 799 | # 如何使用 jupyter 800 | 801 | 确保你已安装 docker,只需运行 `./s/start_jupyter.sh`,然后按照控制台的说明进行操作。 802 | 803 | # 会议 804 | 805 | * [DEMUXED](https://demuxed.com/) - 您可以[查看最近的2个活动演示](https://www.youtube.com/channel/UCIc_DkRxo9UgUSTvWVNCmpA)。 806 | 807 | # 参考 808 | 809 | 这里有最丰富的资源,这篇文档包含的信息,均摘录、依据或受它们启发。你可以用这些精彩的链接,书籍,视频等深化你的知识。 810 | 811 | 在线课程和教程: 812 | 813 | * [https://www.coursera.org/learn/digital/](https://www.coursera.org/learn/digital/) 814 | * [https://people.xiph.org/~tterribe/pubs/lca2012/auckland/intro_to_video1.pdf](https://people.xiph.org/~tterribe/pubs/lca2012/auckland/intro_to_video1.pdf) 815 | * [https://xiph.org/video/vid1.shtml](https://xiph.org/video/vid1.shtml) 816 | * [https://xiph.org/video/vid2.shtml](https://xiph.org/video/vid2.shtml) 817 | * [http://slhck.info/ffmpeg-encoding-course](http://slhck.info/ffmpeg-encoding-course) 818 | * [http://www.cambridgeincolour.com/tutorials/camera-sensors.htm](http://www.cambridgeincolour.com/tutorials/camera-sensors.htm) 819 | * [http://www.slideshare.net/vcodex/a-short-history-of-video-coding](http://www.slideshare.net/vcodex/a-short-history-of-video-coding) 820 | * [http://www.slideshare.net/vcodex/introduction-to-video-compression-1339433](http://www.slideshare.net/vcodex/introduction-to-video-compression-13394338) 821 | * [https://developer.android.com/guide/topics/media/media-formats.html](https://developer.android.com/guide/topics/media/media-formats.html) 822 | * [http://www.slideshare.net/MadhawaKasun/audio-compression-23398426](http://www.slideshare.net/MadhawaKasun/audio-compression-23398426) 823 | * [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) 824 | 825 | 书籍: 826 | 827 | * [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) 828 | * [https://www.amazon.com/H-264-Advanced-Video-Compression-Standard/dp/0470516925](https://www.amazon.com/H-264-Advanced-Video-Compression-Standard/dp/0470516925) 829 | * [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) 830 | * [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) 831 | 832 | 比特流规范: 833 | 834 | * [http://www.itu.int/rec/T-REC-H.264-201610-I](http://www.itu.int/rec/T-REC-H.264-201610-I) 835 | * [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) 836 | * [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) 837 | * [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) 838 | * [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) 839 | * [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) 840 | 841 | 软件: 842 | 843 | * [https://ffmpeg.org/](https://ffmpeg.org/) 844 | * [https://ffmpeg.org/ffmpeg-all.html](https://ffmpeg.org/ffmpeg-all.html) 845 | * [https://ffmpeg.org/ffprobe.html](https://ffmpeg.org/ffprobe.html) 846 | * [https://trac.ffmpeg.org/wiki/](https://trac.ffmpeg.org/wiki/) 847 | * [https://software.intel.com/en-us/intel-video-pro-analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer) 848 | * [https://medium.com/@mbebenita/av1-bitstream-analyzer-d25f1c27072b#.d5a89oxz8](https://medium.com/@mbebenita/av1-bitstream-analyzer-d25f1c27072b#.d5a89oxz8) 849 | 850 | 非-ITU 编解码器: 851 | 852 | * [https://aomedia.googlesource.com/](https://aomedia.googlesource.com/) 853 | * [https://github.com/webmproject/libvpx/tree/master/vp9](https://github.com/webmproject/libvpx/tree/master/vp9) 854 | * [https://people.xiph.org/~xiphmont/demo/daala/demo1.shtml](https://people.xiph.org/~xiphmont/demo/daala/demo1.shtml) 855 | * [https://people.xiph.org/~jm/daala/revisiting/](https://people.xiph.org/~jm/daala/revisiting/) 856 | * [https://www.youtube.com/watch?v=lzPaldsmJbk](https://www.youtube.com/watch?v=lzPaldsmJbk) 857 | * [https://fosdem.org/2017/schedule/event/om_av1/](https://fosdem.org/2017/schedule/event/om_av1/) 858 | 859 | 编码概念: 860 | 861 | * [http://x265.org/hevc-h265/](http://x265.org/hevc-h265/) 862 | * [http://slhck.info/video/2017/03/01/rate-control.html](http://slhck.info/video/2017/03/01/rate-control.html) 863 | * [http://slhck.info/video/2017/02/24/vbr-settings.html](http://slhck.info/video/2017/02/24/vbr-settings.html) 864 | * [http://slhck.info/video/2017/02/24/crf-guide.html](http://slhck.info/video/2017/02/24/crf-guide.html) 865 | * [https://arxiv.org/pdf/1702.00817v1.pdf](https://arxiv.org/pdf/1702.00817v1.pdf) 866 | * [https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors](https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors) 867 | * [http://web.ece.ucdavis.edu/cerl/ReliableJPEG/Cung/jpeg.html](http://web.ece.ucdavis.edu/cerl/ReliableJPEG/Cung/jpeg.html) 868 | * [http://www.adobe.com/devnet/adobe-media-server/articles/h264_encoding.html](http://www.adobe.com/devnet/adobe-media-server/articles/h264_encoding.html) 869 | * [https://prezi.com/8m7thtvl4ywr/mp3-and-aac-explained/](https://prezi.com/8m7thtvl4ywr/mp3-and-aac-explained/) 870 | 871 | 测试用视频序列: 872 | 873 | * [http://bbb3d.renderfarming.net/download.html](http://bbb3d.renderfarming.net/download.html) 874 | * [https://www.its.bldrdoc.gov/vqeg/video-datasets-and-organizations.aspx](https://www.its.bldrdoc.gov/vqeg/video-datasets-and-organizations.aspx) 875 | 876 | 杂项: 877 | 878 | * [http://stackoverflow.com/a/24890903](http://stackoverflow.com/a/24890903) 879 | * [http://stackoverflow.com/questions/38094302/how-to-understand-header-of-h264](http://stackoverflow.com/questions/38094302/how-to-understand-header-of-h264) 880 | * [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) 881 | * [http://vanseodesign.com/web-design/color-luminance/](http://vanseodesign.com/web-design/color-luminance/) 882 | * [http://www.biologymad.com/nervoussystem/eyenotes.htm](http://www.biologymad.com/nervoussystem/eyenotes.htm) 883 | * [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) 884 | * [http://www.csc.villanova.edu/~rschumey/csc4800/dct.html](http://www.csc.villanova.edu/~rschumey/csc4800/dct.html) 885 | * [http://www.explainthatstuff.com/digitalcameras.html](http://www.explainthatstuff.com/digitalcameras.html) 886 | * [http://www.hkvstar.com](http://www.hkvstar.com) 887 | * [http://www.hometheatersound.com/](http://www.hometheatersound.com/) 888 | * [http://www.lighterra.com/papers/videoencodingh264/](http://www.lighterra.com/papers/videoencodingh264/) 889 | * [http://www.red.com/learn/red-101/video-chroma-subsampling](http://www.red.com/learn/red-101/video-chroma-subsampling) 890 | * [http://www.slideshare.net/ManoharKuse/hevc-intra-coding](http://www.slideshare.net/ManoharKuse/hevc-intra-coding) 891 | * [http://www.slideshare.net/mwalendo/h264vs-hevc](http://www.slideshare.net/mwalendo/h264vs-hevc) 892 | * [http://www.slideshare.net/rvarun7777/final-seminar-46117193](http://www.slideshare.net/rvarun7777/final-seminar-46117193) 893 | * [http://www.springer.com/cda/content/document/cda_downloaddocument/9783642147029-c1.pdf](http://www.springer.com/cda/content/document/cda_downloaddocument/9783642147029-c1.pdf) 894 | * [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) 895 | * [http://www.streamingmediaglobal.com/Articles/ReadArticle.aspx?ArticleID=116505&PageNum=1](http://www.streamingmediaglobal.com/Articles/ReadArticle.aspx?ArticleID=116505&PageNum=1) 896 | * [http://yumichan.net/video-processing/video-compression/introduction-to-h264-nal-unit/](http://yumichan.net/video-processing/video-compression/introduction-to-h264-nal-unit/) 897 | * [https://cardinalpeak.com/blog/the-h-264-sequence-parameter-set/](https://cardinalpeak.com/blog/the-h-264-sequence-parameter-set/) 898 | * [https://cardinalpeak.com/blog/worlds-smallest-h-264-encoder/](https://cardinalpeak.com/blog/worlds-smallest-h-264-encoder/) 899 | * [https://codesequoia.wordpress.com/category/video/](https://codesequoia.wordpress.com/category/video/) 900 | * [https://developer.apple.com/library/content/technotes/tn2224/_index.html](https://developer.apple.com/library/content/technotes/tn2224/_index.html) 901 | * [https://en.wikibooks.org/wiki/MeGUI/x264_Settings](https://en.wikibooks.org/wiki/MeGUI/x264_Settings) 902 | * [https://en.wikipedia.org/wiki/Adaptive_bitrate_streaming](https://en.wikipedia.org/wiki/Adaptive_bitrate_streaming) 903 | * [https://en.wikipedia.org/wiki/AOMedia_Video_1](https://en.wikipedia.org/wiki/AOMedia_Video_1) 904 | * [https://en.wikipedia.org/wiki/Chroma_subsampling#/media/File:Colorcomp.jpg](https://en.wikipedia.org/wiki/Chroma_subsampling#/media/File:Colorcomp.jpg) 905 | * [https://en.wikipedia.org/wiki/Cone_cell](https://en.wikipedia.org/wiki/Cone_cell) 906 | * [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) 907 | * [https://en.wikipedia.org/wiki/Inter_frame](https://en.wikipedia.org/wiki/Inter_frame) 908 | * [https://en.wikipedia.org/wiki/Intra-frame_coding](https://en.wikipedia.org/wiki/Intra-frame_coding) 909 | * [https://en.wikipedia.org/wiki/Photoreceptor_cell](https://en.wikipedia.org/wiki/Photoreceptor_cell) 910 | * [https://en.wikipedia.org/wiki/Pixel_aspect_ratio](https://en.wikipedia.org/wiki/Pixel_aspect_ratio) 911 | * [https://en.wikipedia.org/wiki/Presentation_timestamp](https://en.wikipedia.org/wiki/Presentation_timestamp) 912 | * [https://en.wikipedia.org/wiki/Rod_cell](https://en.wikipedia.org/wiki/Rod_cell) 913 | * [https://it.wikipedia.org/wiki/File:Pixel_geometry_01_Pengo.jpg](https://it.wikipedia.org/wiki/File:Pixel_geometry_01_Pengo.jpg) 914 | * [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/) 915 | * [https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping](https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping) 916 | * [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/) 917 | * [https://tools.ietf.org/html/draft-fuldseth-netvc-thor-03](https://tools.ietf.org/html/draft-fuldseth-netvc-thor-03) 918 | * [https://www.encoding.com/android/](https://www.encoding.com/android/) 919 | * [https://www.encoding.com/http-live-streaming-hls/](https://www.encoding.com/http-live-streaming-hls/) 920 | * [https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm](https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm) 921 | * [https://www.lifewire.com/cmos-image-sensor-493271](https://www.lifewire.com/cmos-image-sensor-493271) 922 | * [https://www.linkedin.com/pulse/brief-history-video-codecs-yoav-nativ](https://www.linkedin.com/pulse/brief-history-video-codecs-yoav-nativ) 923 | * [https://www.linkedin.com/pulse/video-streaming-methodology-reema-majumdar](https://www.linkedin.com/pulse/video-streaming-methodology-reema-majumdar) 924 | * [https://www.vcodex.com/h264avc-intra-precition/](https://www.vcodex.com/h264avc-intra-precition/) 925 | * [https://www.youtube.com/watch?v=9vgtJJ2wwMA](https://www.youtube.com/watch?v=9vgtJJ2wwMA) 926 | * [https://www.youtube.com/watch?v=LFXN9PiOGtY](https://www.youtube.com/watch?v=LFXN9PiOGtY) 927 | * [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) 928 | * [https://www.youtube.com/watch?v=LWxu4rkZBLw](https://www.youtube.com/watch?v=LWxu4rkZBLw) 929 | -------------------------------------------------------------------------------- /README-ja.md: -------------------------------------------------------------------------------- 1 | [![license](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg) 2 | 3 | # はじめに 4 | 5 | これは、ビデオ技術に関するやさしい解説資料です。ソフトウェア開発者やエンジニアを対象にしていますが、**誰でも理解できる**解説にしたいと思っています。このアイディアは、[ビデオ技術初学者のためのミニワークショップ](https://docs.google.com/presentation/d/17Z31kEkl_NGJ0M66reqr9_uTG6tI5EDDVXpdPKVuIrs/edit#slide=id.p)から生まれました。 6 | 7 | できるだけ**簡潔な言葉、多くの視覚的要素、具体的な例**を使うことで、誰でもデジタルビデオの概念が理解できることを目標にしています。気軽に訂正や提案を送り、改善してください。 8 | 9 | 本資料には**ハンズオン**による解説が含まれています。ハンズオンに取り組む方は、事前にdockerをインストールし、このレポジトリをクローンしておいてください。 10 | 11 | ```bash 12 | git clone https://github.com/leandromoreira/digital_video_introduction.git 13 | cd digital_video_introduction 14 | ./setup.sh 15 | ``` 16 | > **注意**: `./s/ffmpeg` や `./s/mediainfo` コマンドは、そのプログラムが**Dockerコンテナ上**で実行されることを意味しています。コンテナの中には、必要な依存関係が全て含まれています。 17 | 18 | **ハンズオンはすべて、このレポジトリをクローンしたフォルダで実行してください**。 **jupyter examples**については、`./s/start_jupyter.sh`でサーバーを起動して、表示されるURLをブラウザで開いてください。 19 | 20 | # 変更履歴 21 | 22 | * DRMシステムの追加 23 | * 1.0.0版のリリース 24 | * 簡体字訳の追加 25 | * FFmpeg oscilloscopeフィルターの例を追加 26 | 27 | # 目次 28 | 29 | - [はじめに](#はじめに) 30 | - [目次](#目次) 31 | - [基本用語](#基本用語) 32 | * [カラー画像を符号化する別の方法](#カラー画像を符号化する別の方法) 33 | * [ハンズオン: 画像と色の実験](#ハンズオン-画像と色の実験) 34 | * [DVDの画面アスペクト比は4:3](#dvdの画面アスペクト比は43) 35 | * [ハンズオン: ビデオプロパティを調べる](#ハンズオン-ビデオプロパティを調べる) 36 | - [冗長性除去](#冗長性除去) 37 | * [色、明るさと私たちの目](#色、明るさと私たちの目) 38 | + [カラーモデル](#カラーモデル) 39 | + [YCbCrとRGB間の変換](#ycbcrとrgb間の変換) 40 | + [クロマサブサンプリング](#クロマサブサンプリング) 41 | + [ハンズオン: YCbCrヒストグラムを調べる](#ハンズオン-ycbcrヒストグラムを調べる) 42 | * [フレームの種類](#フレームの種類) 43 | + [Iフレーム (イントラ、キーフレーム)](#iフレーム-イントラ、キーフレーム) 44 | + [Pフレーム (予測)](#pフレーム-予測) 45 | - [ハンズオン: Iフレームが1つだけのビデオ](#ハンズオン-iフレームが1つだけのビデオ) 46 | + [Bフレーム (双方向予測)](#bフレーム-双方向予測) 47 | - [ハンズオン: Bフレーム付きのビデオとの比較](#ハンズオン-bフレーム付きのビデオとの比較) 48 | + [まとめ](#まとめ) 49 | * [時間的冗長性 (インター予測)](#時間的冗長性-インター予測) 50 | - [ハンズオン: 動きベクトルを見る](#ハンズオン-動きベクトルを見る) 51 | * [空間的冗長性 (イントラ予測)](#空間的冗長性-イントラ予測) 52 | - [ハンズオン: イントラ予測を調べる](#ハンズオン-イントラ予測を調べる) 53 | - [ビデオコーデックの仕組み](#ビデオコーデックの仕組み) 54 | * [何か? なぜ? どのように?](#何か-なぜ-どのように) 55 | * [歴史](#歴史) 56 | + [AV1の誕生](#av1の誕生) 57 | * [一般的コーデック](#一般的コーデック) 58 | * [ステップ1 - 画像分割](#ステップ1---画像分割) 59 | + [ハンズオン: パーティションを調べる](#ハンズオン-パーティションを調べる) 60 | * [ステップ2 - 予測](#ステップ2---予測) 61 | * [ステップ3 - 変換](#ステップ3---変換) 62 | + [ハンズオン: 種々の係数を捨てる](#ハンズオン-種々の係数を捨てる) 63 | * [ステップ4 - 量子化](#ステップ4---量子化) 64 | + [ハンズオン: 量子化](#ハンズオン-量子化) 65 | * [ステップ5 - エントロピー符号化](#ステップ5---エントロピー符号化) 66 | + [可変長符号](#可変長符号) 67 | + [算術符号](#算術符号) 68 | + [ハンズオン: CABAC対CAVLC](#ハンズオン-cabac対cavlc) 69 | * [ステップ6 - ビットストリームフォーマット](#ステップ6---ビットストリームフォーマット) 70 | + [H.264ビットストリーム](#h264ビットストリーム) 71 | + [ハンズオン: H.264ビットストリームを調べる](#ハンズオン-h264ビットストリームを調べる) 72 | * [おさらい](#おさらい) 73 | * [どのようにH.265はH.264よりも良い圧縮率を実現しているのか?](#どのようにh265はh264よりも良い圧縮率を実現しているのか) 74 | - [オンラインストリーミング](#オンラインストリーミング) 75 | * [一般的なアーキテクチャ](#一般的なアーキテクチャ) 76 | * [プログレッシブダウンロードとアダプティブストリーミング](#プログレッシブダウンロードとアダプティブストリーミング) 77 | * [コンテンツ保護](#コンテンツ保護) 78 | - [jupyterの使い方](#jupyterの使い方) 79 | - [カンファレンス](#カンファレンス) 80 | - [参考文献](#参考文献) 81 | 82 | # 基本用語 83 | 84 | **画像**は、**二次元マトリクス**として考えることができます。さらに次元を増やして**三次元マトリクス**にすることで画像の色を表現することも可能です。 85 | 86 | 画像の色を[原色 (赤、緑、青)](https://ja.wikipedia.org/wiki/%E5%8E%9F%E8%89%B2)で表現すると、三つの平面を定義することになります。一つめが**赤**、二つ目が**緑**、そして三つ目が**青**です。 87 | 88 | ![an image is a 3d matrix RGB](/i/image_3d_matrix_rgb.png "画像は三次元マトリクスです") 89 | 90 | マトリクスのそれぞれの要素を**ピクセル** (画素)と呼びます。一つのピクセルはその色の**強度** (通常は数値)を表します。例えば、**赤色のピクセル**は、緑が0、青が0、赤が最大の強度により表現できます。**ピンク色のピクセル**も同様に3つの値で表現できます。0から255の数値で表現することにより、ピンクピクセルは**赤=255、緑=192、青=203**と定義できます。 91 | 92 | > #### カラー画像を符号化する別の方法 93 | > 色を表現する方法は、他にもたくさんあります。例えば、RGBモデルでは各ピクセルで3バイトを必要としますが、インデックスパレットは1バイトしか必要ありません。そういったモデルでは、色を表現するために三次元モデルを使わずに二次元モデルを使用できるでしょう。メモリを節約できますが、色の選択肢を狭めることになります。 94 | > 95 | > ![NES palette](/i/nes-color-palette.png "ファミコンパレット") 96 | 97 | 例えば、下の画像をみてください。1番左の画像は色付けされており、他の画像は赤、緑、青の強度を表す平面です(グレートーンで表示しています)。 98 | 99 | ![RGB channels intensity](/i/rgb_channels_intensity.png "RGB要素の輝度") 100 | 101 | **赤色**が最も多く使われていることが分かります(左から二番目の顔の最も明るい部分)。一方**青色** は服の一部と**マリオの目にしかみられません**(最後の顔) 。**マリオのひげ**に対しては、**どの色もあまり使われていない**(最も暗い部分)ことが分かります。 102 | 103 | 各色の強度は**ビット深度**とよばれる一定量のビットで表現されます。色(平面)ごとに**8ビット**(0から255の値で表現する)を使う場合、**24ビット**(8ビット x 3次元 R/G/B)の**色深度**を持つことになり、2の24乗種類の色を使えることが推測できます。 104 | 105 | > [画像がどのように万物をビットとしてとらえるのか](http://www.cambridgeincolour.com/tutorials/camera-sensors.htm)を学ぶと **良い**でしょう 106 | 107 | もう一つの画像のプロパティは **解像度**です。解像度は長さあたりのピクセルの数です。解像度はよく幅 x 高さとして表現されます。例えば、下記は**4×4**の画像です。 108 | 109 | ![image resolution](/i/resolution.png "画像解像度") 110 | 111 | > #### ハンズオン: 画像と色の実験 112 | > [jupyter](#jupyterの使い方) (python、numpy、matplotlib、その他)を使って、[画像と色の実験](/image_as_3d_array.ipynb)をしましょう。 113 | > 114 | > [(エッジ検出, シャープ化, ぼかし等の)画像フィルタがどのように動くか](/filters_are_easy.ipynb)を学びましょう。 115 | 116 | 画像やビデオの作業をする時にみるもう一つのプロパティは **アスペクト比**です。アスペクト比は、画像やピクセルの幅と高さの比率を表します。 117 | 118 | 動画や画像が**16x9**であると言うときは、たいてい**画面アスペクト比 (DAR)** のことを指します。しかし、個々のピクセルを様々な形状にすることができ、これを **ピクセルアスペクト比 (PAR)** といいます。 119 | 120 | ![display aspect ratio](/i/DAR.png "画面アスペクト比") 121 | 122 | ![pixel aspect ratio](/i/PAR.png "ピクセルアスペクト比") 123 | 124 | > #### DVDの画面アスペクト比は4:3 125 | > DVDの実際の解像度は704x480ですが、10:11のピクセルアスペクト比を持っているため、4:3のアスペクト比を保っています(704x10/480x11)。 126 | 127 | 最後に、**ビデオ**を**単位時間**内の***n*フレームの並び**として定義でき、もう一つの特性と見ることができます。*n*はフレームレートもしくは秒間フレーム数 (FPS)です。 128 | 129 | ![video](/i/video.png "ビデオ") 130 | 131 | ビデオを表すために必要な秒間あたりのビット数は**ビットレート**です。 132 | 133 | > ビットレート = 幅 x 高さ x ビット深度 x フレームレート 134 | 135 | 例えば、圧縮を全く使わないなら、フレームレートが30で、ピクセルあたりが24ビットで、解像度が480x240のビデオは、**秒間あたり82,944,000ビット**もしくは82.944 Mbps (30x480x240x24)が必要です。 136 | 137 | **ビットレート**がほとんど一定なら、固定ビットレート(**CBR**)と呼ばれます。ビットレートが変動するなら、可変ビットレート (**VBR**)と呼ばれます。 138 | 139 | > このグラフはフレームが真っ黒の間はあまりビットを使わない、制約付きのVBRを示しています。 140 | > 141 | > ![constrained vbr](/i/vbr.png "制約付きのvbr") 142 | 143 | 黎明期に、技術者たちが**帯域幅を増やさずに**ビデオ画面で認識できるフレームレートを倍にする技術を思いつきました。この技術は**インターレース動画**として知られています。基本的には一つ目の「フレーム」で画面の半分を送り、次の「フレーム」で残りの半分を送ります。 144 | 145 | 今日では **プログレッシブスキャン技術**を使って画面に描画されます。プログレッシブは、動画を描画、保存、転送する手段の一つで、各フレームの全走査線を順番に描画します。 146 | 147 | ![interlaced vs progressive](/i/interlaced_vs_progressive.png "インターレース対プログレッシブ") 148 | 149 | これで**画像**がどのようにデジタル表現されるのかが分かりました。**色**がどのように配置され、ビデオを表すのにどのくらいの**秒間あたりのビット**が必要なのか、それが固定なのか(CBR)可変なのか(VBR)、**解像度**と**フレームレート**、他にもインターレースやピクセルアスペクト比、その他のたくさんの用語を学びました。 150 | 151 | > #### ハンズオン: ビデオプロパティを調べる 152 | > [ffmpegやmediainfoを使って説明したプロパティのほとんどを調べる](https://github.com/leandromoreira/introduction_video_technology/blob/master/encoding_pratical_examples.md#inspect-stream)ことができます。 153 | 154 | # 冗長性除去 155 | 156 | 圧縮なしにビデオを扱うことは不可能であることを学びました。解像度が720pで30fpsの場合、**1時間のビデオ一つ**に**278GB*が必要**です。(PKZIP、Gzip、PNGで使われている)DEFLATE のような**可逆圧縮アルゴリズム一つを使うだけでは**必要な帯域幅を十分に削減**できない**ため、ビデオを圧縮する別の方法を見つける必要があるのです。 157 | 158 | > * この数値は1280 x 720 x 24 x 30 x 3600 (幅、高さ、ピクセルあたりのビット数、フレームレート、秒単位での時間)を掛けることで算出しました 159 | 160 | これを成し遂げるために、**私たちの視覚の仕組みを利用する**ことができます。私たちは色よりも明るさを見分けることが得意です。また、ビデオには少しの変化しかない多くの画像が含まれている**時間的繰り返し**があります。それぞれのフレームもまた、多くの領域では同じか似ている色が使われている**画像内の繰り返し**があります。 161 | 162 | ## 色、明るさと私たちの目 163 | 164 | 私たちの目は[色よりも明るさにより敏感](http://vanseodesign.com/web-design/color-luminance/)です。この画像をみてそれを自分自身で確認できます。 165 | 166 | ![luminance vs color](/i/luminance_vs_color.png "明るさ 対 色") 167 | 168 | 左側の**正方形Aと正方形B**の色は**同じ**であることが分からないとしても大丈夫です。私たちの脳は**色よりも明暗に注意をはらう**ことで錯覚を起こさせているのです。右側では、同じ色のコネクターがあるため、私たち(私たちの脳)は、それらは実際は同じ色であるということを簡単に気づきます。 169 | 170 | > **私たちの目がどのように機能するかの簡単な説明** 171 | > 172 | > [眼は複雑な器官](http://www.biologymad.com/nervoussystem/eyenotes.htm)で、たくさんのパーツから成り立っていますが、主に錐体細胞と桿体細胞に関心があります。眼は [1億2000万の桿体細胞と600万の錐体細胞を含んでいる](https://en.wikipedia.org/wiki/Photoreceptor_cell)のです。 173 | > 174 | > **ものすごく簡単にする**ため、眼のパーツの機能のうち色と明るさに焦点を当てましょう。**[桿体細胞](https://en.wikipedia.org/wiki/Rod_cell)は主に明るさに対して責任を持っています**。一方 **[錐体細胞](https://en.wikipedia.org/wiki/Cone_cell)は色に対して責任を持っています**。異なった色素を持つ3種類の錐体があり、名前は[S錐体(青)、M錐体(緑)、L錐体(赤)](https://upload.wikimedia.org/wikipedia/commons/1/1e/Cones_SMJ2_E.svg)です。 175 | > 176 | > 私たちは錐体細胞(色)よりも多くの桿体細胞(明るさ)を持っているため、色よりも明暗をより識別することができることが推論できます。 177 | > 178 | > ![eyes composition](/i/eyes.jpg "眼の構成") 179 | > 180 | > **コントラスト感度特性 (Contrast sensitivity functions)** 181 | > 182 | > 実験心理学をはじめさまざまな分野の研究者が、人間の視覚について多くの理論を作り上げてきました。そのひとつが「コントラスト感度特性」です。コントラスト感度特性は光の時空間に関するものです。まず、観察者にある光を提示し、光を変化させていきます。この特性は、観察者が変化に気付いたと報告するまで、どれだけ光を変化させる必要があるかを示します。複数形の `functions` となっているのは、白黒だけでなく、カラーでも感度を測定できるためです。実験結果から、多くの場合、人間の目は色よりも明るさに敏感だとわかりました。 183 | 184 | 185 | 私たちは**輝度**(画像の明るさの度合い)により敏感であることが分かりました。この特性を利用しましょう。 186 | 187 | ### カラーモデル 188 | 189 | **RGBモデル**を使って[カラー画像がどのように](#基本用語)機能するのか最初に学びましたが、他のモデルも存在します。実は、輝度(明るさ)と色度(色)を別にするモデルが存在します。それは**YCbCr***として知られています。 190 | 191 | > * 同じ分離を行うモデルはもっと存在します。 192 | 193 | このカラーモデルは明るさを表すために**Y**を使い、2つの色チャンネル**Cb** (青の色差)と**Cr** (赤の色差)を使います。[YCbCr](https://en.wikipedia.org/wiki/YCbCr)はRGBから生成することができ、RGBに戻すこともできます。下の写真のように、このモデルを使ってフルカラーの画像を作ることができます。 194 | 195 | ![ycbcr example](/i/ycbcr.png "ycbcr例") 196 | 197 | ### YCbCrとRGB間の変換 198 | 199 | 中には **緑なしで色**の全てを生成できるのかと異議を唱える方もいるでしょう。 200 | 201 | この質問に答えるために、RGBからYCbCrへの変換を一通り説明します。**[ITU-Rグループ*](https://en.wikipedia.org/wiki/ITU-R)** によって推奨される **[BT.601標準](https://en.wikipedia.org/wiki/Rec._601)** からの係数を使います。最初のステップは、**輝度を計算する**ことです。ITUに提案されている定数を使い、RGB値を置き換えます。 202 | 203 | ``` 204 | Y = 0.299R + 0.587G + 0.114B 205 | ``` 206 | 207 | 輝度をえると次に、**色を分ける** (青の色差と赤の色差)ことができます。 208 | 209 | ``` 210 | Cb = 0.564(B - Y) 211 | Cr = 0.713(R - Y) 212 | ``` 213 | 214 | それを**戻す**ことができ、**YCbCrを使って緑**を得ることもできます。 215 | 216 | ``` 217 | R = Y + 1.402Cr 218 | B = Y + 1.772Cb 219 | G = Y - 0.344Cb - 0.714Cr 220 | ``` 221 | 222 | > * グループや標準はデジタルビデオではよくでてきます。彼らは何が標準なのかを定義します。例えば[4Kとは何か?どのフレームレート、解像度、カラーモデルを使うべきか?](https://en.wikipedia.org/wiki/Rec._2020)などです。 223 | 224 | 一般的に**ディスプレイ** (モニター、テレビ、スクリーン等) は様々な方法で構成された**RGBモデルだけ**を利用します。下の写真でいくつか拡大したもの示しています。 225 | 226 | ![pixel geometry](/i/new_pixel_geometry.jpg "画素形状") 227 | 228 | ### クロマサブサンプリング 229 | 230 | 画像は輝度と色度成分で表現することができるので、人間の視覚システムが色度よりも輝度に敏感であることを利用して情報を選択的に削減することができます。**クロマサブサンプリング**は**色度の解像度を輝度の解像度より小さくする**画像符号化技術です。 231 | 232 | 233 | ![ycbcr subsampling resolutions](/i/ycbcr_subsampling_resolution.png "ycbcrサブサンプリング解像度") 234 | 235 | 236 | 色度の解像度をどのくらい小さくすべきでしょうか?!解像度と結合 (`最終的な色 = Y + Cb + Cr`)の仕方をどのようにするかを定義したいくつかの方式がすでに存在しています。 237 | 238 | これらの方式はサブサンプリングシステムとして知られ、3つの部分からなる比`a:x:y`として表現されます。これは `a x 2`ブロックの輝度ピクセルに対する色度解像度を定義しています。 239 | 240 | * `a`:横方向のサンプルの基本数。通常は4。 241 | * `x`:1ライン目の`a`に現れる色信号サンプルの数。(`a`に対する水平解像度) 242 | * `y`:1ライン目と2ライン目での変化数 243 | 244 | > 4:1:0は例外で、`4 x 4`ブロックの輝度に1つの色信号サンプルだけを含んでいます。 245 | 246 | 現代のコーデックでよく使われる方式は**4:4:4(サブサンプリング無し)**、**4:2:2、4:1:1、4:2:0、4:1:0、3:1:1**です。 247 | 248 | > **YCbCr 4:2:0結合** 249 | > 250 | > これがYCbCr 4:2:0を使って結合された画像の断片です。1ピクセルに12ビットしか使わないことに注意してください。 251 | > 252 | > ![YCbCr 4:2:0 merge](/i/ycbcr_420_merge.png "YCbCr 4:2:0結合") 253 | 254 | クロマサブサンプリングの主要な形式で符号化された同じ画像を見て下さい。一行目の画像は最終的なYCbCrで、二行目の画像は色度解像度を示しています。実に小さな劣化で素晴らしい結果です。 255 | 256 | ![chroma subsampling examples](/i/chroma_subsampling_examples.jpg "クロマサブサンプリング例") 257 | 258 | [解像度が720pで30fpsのビデオを1時間ファイルに保存するためには278GBの領域](#冗長性除去)が必要になることを先に計算しました。**YCbCr 4:2:0**を使うと、**半分のサイズ(139 GB)***にすることができます。しかしまだ理想には程遠いです。 259 | 260 | > * 幅、高さ、ピクセルごとのビット数、フレームレートを掛けることによりこの値を計算しました。先に計算した時は24ビット必要でしたが、今は12ビットしか必要ありません。 261 | 262 |
263 | 264 | > ### ハンズオン: YCbCrヒストグラムを調べる 265 | > [ffmpegでYCbCrヒストグラムを調べる](/encoding_pratical_examples.md#generates-yuv-histogram)ことができます。 このシーンでは青の寄与率がより高くなっています。 [ヒストグラム](https://en.wikipedia.org/wiki/Histogram)でそのことが示されます。 266 | > 267 | > ![ycbcr color histogram](/i/yuv_histogram.png "ycbcrカラーヒストグラム") 268 | 269 | ### 色、輝度、明るさ、ガンマについての映像 270 | 271 | 輝度や明るさ、ガンマ、色について解説している素晴らしい動画があります。ぜひご覧ください。 272 | 273 | [![Analog Luma - A history and explanation of video](http://img.youtube.com/vi/Ymt47wXUDEU/0.jpg)](http://www.youtube.com/watch?v=Ymt47wXUDEU) 274 | 275 | > ### ハンズオン: YCbCr 強度を調べる 276 | > [FFmpegのoscilloscope filter](https://ffmpeg.org/ffmpeg-filters.html#oscilloscope)を使って、映像のYの強度を可視化できます。 277 | > ```bash 278 | > ffplay -f lavfi -i 'testsrc2=size=1280x720:rate=30000/1001,format=yuv420p' -vf oscilloscope=x=0.5:y=200/720:s=1:c=1 279 | > ``` 280 | > ![y color oscilloscope](/i/ffmpeg_oscilloscope.png "y color oscilloscope") 281 | 282 | ## フレームの種類 283 | 284 | 次に**時間的な冗長性**の削減を試みましょう。しかし、その前にいくつかの基本的用語について押さえておきましょう。30 fpsの動画があり、下記が最初の4フレームであるとします。 285 | 286 | ![ball 1](/i/smw_background_ball_1.png "ボール1") ![ball 2](/i/smw_background_ball_2.png "ボール2") ![ball 3](/i/smw_background_ball_3.png "ボール3") 287 | ![ball 4](/i/smw_background_ball_4.png "ボール4") 288 | 289 | **青い背景**のようにフレーム間に**たくさんの繰り返し**を見ることができます。背景はフレーム0からフレーム3まで変化しません。この問題に取り組むために、これらを3種類のフレームに**抽象的に分類**しましょう。 290 | 291 | ### Iフレーム (イントラ、キーフレーム) 292 | 293 | Iフレーム(参照、キーフレーム、イントラ)は**自己完結的なフレーム**です。Iフレームは他に依存せずに描画することができ、静止画と似ています。最初のフレームは普通はIフレームですが、他の種類のフレームの間にも規則的にIフレームが挿入されています。 294 | 295 | ![ball 1](/i/smw_background_ball_1.png "ボール1") 296 | 297 | ### Pフレーム (予測) 298 | 299 | 現在の画像は、ほとんど毎回**1つ前のフレームを使って描画する**ことができるという事実をPフレームは利用しています。例えば、2番目のフレームでは、ボールが前に動いたという変化しかありません。**1つ前のフレームを参照し、そのフレームとの差分だけを用いてフレーム1を再構築**できます。 300 | 301 | ![ball 1](/i/smw_background_ball_1.png "ボール1") <- ![ball 2](/i/smw_background_ball_2_diff.png "ボール2") 302 | 303 | > #### ハンズオン: Iフレームが1つだけのビデオ 304 | > Pフレームはより小さなデータしか使わないので、全体を[Iフレームは1つだけで、他は全てPフレームのビデオ](/encoding_pratical_examples.md#1-i-frame-and-the-rest-p-frames)にエンコードしたらどうでしょう? 305 | > 306 | > このビデオをエンコードした後、再生してビデオの**前方にシーク**してください。シーク先に移るのに**時間がかかる**ことに気づくでしょう。これは、描画のために**Pフレームが参照フレームを必要とする** (例えばIフレーム)ためです。 307 | > 308 | > もう1つ手軽にできるテストとして、まず1つのI-Frameだけを使ってビデオをエンコードして、次に[2秒間隔でIフレームを挿入してエンコード](/encoding_pratical_examples.md#1-i-frames-per-second-vs-05-i-frames-per-second)してから、**それぞれのエンコード結果のサイズを比較**してください。 309 | 310 | ### Bフレーム (双方向予測) 311 | 312 | 過去と未来のフレーム両方を参照したら、さらに高い圧縮率を得ることができるのではないでしょうか?!それがBフレームの基本です。 313 | 314 | ![ball 1](/i/smw_background_ball_1.png "ボール1") <- ![ball 2](/i/smw_background_ball_2_diff.png "ボール2") -> ![ball 3](/i/smw_background_ball_3.png "ボール3") 315 | 316 | > #### ハンズオン: Bフレーム付きのビデオとの比較 317 | > 2つの方法でエンコードしてみましょう。1つはBフレーム付きで、もう一方は[Bフレームなし](/encoding_pratical_examples.md#no-b-frames-at-all)でエンコードして、ファイルサイズおよび画質を確認してください。 318 | 319 | ### まとめ 320 | 321 | これらの異なる種類のフレームは**より高い圧縮率を得る**ために使われます。次の節でどうやってこれが行われるのか見ていきます。しかし、今のところは**Iフレームは重く、Pフレームは比較的軽いですが、最も軽いのはBフレーム**と考えておいてよいでしょう。 322 | 323 | ![frame types example](/i/frame_types.png "フレームの種類の例") 324 | 325 | ## 時間的冗長性 (インター予測) 326 | 327 | **時間的繰り返し**を削減するために何ができるか見ていきましょう。この種の冗長性は**インター予測**という技術で解決することができます。 328 | 329 | 330 | **できるだけ少ないビットを使って**、連続したフレーム0とフレーム1をエンコードしてみましょう。 331 | 332 | ![original frames](/i/original_frames.png "元のフレーム") 333 | 334 | 1つできることとして、引き算があります。単純に**フレーム0からフレーム1を引く**と、**エンコード**する必要がある**差分**を得ることができます。 335 | 336 | ![delta frames](/i/difference_frames.png "差分フレーム") 337 | 338 | しかし、さらに少ないビットしか使わない**もっとよい方法**があると言ったらどうでしょう!まず、`フレーム0`をきちんと定められた区画の集合であるとします。そして`フレーム0`のそれぞれのブロックを`フレーム1`上にマッチさせようとします。.これは **動き推定**として考えることができます。 339 | 340 | > ### Wikipedia - ブロック動き補償 341 | > "**ブロック動き補償**は現在のフレームを重ならないブロックに分けて、動き補償ベクトルは**これらのブロックがどこからのものかを表します** (前回のフレームが重ならないブロックに分けられ、動き補償ベクトルはこれらのブロックがどこへ行くかを表すというのは、よくある誤解です)。 元のブロックは元のフレーム内で通常は重なり合います。ビデオ圧縮アルゴリズムの中には、 すでに送信された異なったいくつかのフレームの断片から現在のフレームを組み立てるものもあります。" 342 | 343 | ![delta frames](/i/original_frames_motion_estimation.png "フレーム差分") 344 | 345 | ボールが`x=0、y=25`から`x=6、y=26`へ移動したと推定することができます。**x**と**y**の値は**動きベクトル**です。ビットを節約するためのもう1つの**さらなるステップ**は、前回のブロック位置と予測されるブロック位置との**動きベクトルの差分だけをエンコードする**ことです。 最終的な動きベクトルは`x=6 (6-0)、y=1 (26-25)`のようになります。 346 | 347 | > 実際には、この**ボールは分割されてn個の区画にまたがるでしょう**。しかし処理は同じです 348 | 349 | フレーム上の物体は**3D空間で移動します**。ボールは背景の方に動くと小さくもなります。 **完全にマッチするブロックを見つけられない**ことはよくあります。 推定画像と実際の画像を重ねると下記のようになります。 350 | 351 | ![motion estimation](/i/motion_estimation.png "動き推定") 352 | 353 | しかし、**動き推定**を適用すると、単純なフレーム差分手法を使うよりも**エンコードするデータがより小さくなる**ことが分かります。 354 | 355 | ![motion estimation vs delta ](/i/comparison_delta_vs_motion_estimation.png "動き推定 差分") 356 | 357 | > ### 可視化された実際の動き補償 358 | > この手法は全てのブロックに適用され、高い確率でボールは1つ以上のブロックに配置されます。 359 | > ![real world motion compensation](/i/real_world_motion_compensation.png "現実世界の動き補償") 360 | > 引用元: https://web.stanford.edu/class/ee398a/handouts/lectures/EE398a_MotionEstimation_2012.pdf 361 | 362 | [jupyterを使ってこれらの概念を実験](/frame_difference_vs_motion_estimation_plus_residual.ipynb)できます。 363 | 364 | > #### ハンズオン: 動きベクトルを見る 365 | > [ffmpegでインター予測 (動きベクトル)付きのビデオを生成する](/encoding_pratical_examples.md#generate-debug-video)ことができます。 366 | > 367 | > ![inter prediction (motion vectors) with ffmpeg](/i/motion_vectors_ffmpeg.png "ffmpegでインター予測 (動きベクトル)") 368 | > 369 | > または、[Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer) (有料ですが、最初の10フレームに制限された無料お試し版もあります)を使うこともできます。 370 | > 371 | > ![inter prediction intel video pro analyzer](/i/inter_prediction_intel_video_pro_analyzer.png "インター予測 intel video pro analyzer") 372 | 373 | ## 空間的冗長性 (イントラ予測) 374 | 375 | ビデオの中の**1つ1つのフレーム**を分析すると、**たくさんの相関性のある領域**が存在することが分かります。 376 | 377 | ![](/i/repetitions_in_space.png) 378 | 379 | 例を通してみていきましょう。このシーンはほとんど青と白で構成されています。 380 | 381 | ![](/i/smw_bg.png) 382 | 383 | これは `Iフレーム`で、予測のために**前のフレームを使えません**が、圧縮することはできます。赤いブロックを選択してエンコードしてみましょう。**隣接する部分を見る**と、**その周りの色に傾向**があることを**推定**することができます。 384 | 385 | ![](/i/smw_bg_block.png) 386 | 387 | このフレームでは**色が垂直方向に広がり**続けることを**予測**してみます。**未知のピクセルの色が隣接するピクセルの色を持つ**ことを意味します。 388 | 389 | ![](/i/smw_bg_prediction.png) 390 | 391 | **予測が間違うかもしれません**。そのため、この手法(**イントラ予測**)を適用してから、**実際の値を引いて**差分を生成する必要があります。その差分は予測前よりもさらに圧縮しやすいマトリクスになります。 392 | 393 | ![](/i/smw_residual.png) 394 | 395 | 他にも様々な予測方法があります。ここで紹介した方法は、直線状に並んだ領域の予測を行い、ブロックの上の行のピクセルをブロック内の各行にコピーしていました。ここにさらに角度成分を加え、左隣のピクセルの値も利用してブロック内のピクセル値を予測する方法もあります。また左隣と上隣のピクセル値の平均を利用する、DC予測という方法もあります。 396 | 397 | > #### ハンズオン: イントラ予測を調べる 398 | > [ffmpegでマクロブロックとそれらの予測付きのビデオを生成する](/encoding_pratical_examples.md#generate-debug-video)ことができます。[それぞれのブロックの色の意味](https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors#AnalyzingMacroblockTypes)を理解するためにffmpegのドキュメントを調べてください。 399 | > 400 | > ![intra prediction (macro blocks) with ffmpeg](/i/macro_blocks_ffmpeg.png "ffmpegでインター予測 (動きベクトル)") 401 | > 402 | > または[Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer) (有料ですが、最初の10フレームに制限された無料お試し版もあります)を使うことができます。 403 | > 404 | > ![intra prediction intel video pro analyzer](/i/intra_prediction_intel_video_pro_analyzer.png "イントラ予測 intel video pro analyzer") 405 | 406 | # ビデオコーデックの仕組み 407 | 408 | ## What? Why? How? 409 | 410 | **What?** デジタルビデオを圧縮、解凍するソフトウェア / ハードウェアの部品。**Why?** 制限された帯域とストレージの下でより高い品質のビデオへの要求が、市場と社会で高まっているため。毎秒30フレーム、ピクセルあたり24ビット、解像度が480x240のビデオに[必要な帯域を計算](#基本用語)したのを覚えていますか。圧縮なしでは**82.944 Mbps**でした。テレビやインターネットでHD/FullHD/4Kを配信するためには圧縮するしかありません。**How?** ここで主な技法について簡単に見ていきます。 411 | 412 | > **コーデック 対 コンテナ** 413 | > 414 | > 初心者がよく誤解することの1つに、デジタルビデオコーデックと[デジタルビデオコンテナ](https://en.wikipedia.org/wiki/Digital_container_format)を混同するというものがあります。**コンテナ**はビデオ(と音声もありえる)のメタデータとペイロードである**圧縮されたビデオ**を包括するラッパーフォーマットとして考えることができます。 415 | > 416 | > たいてい、ビデオファイルの拡張子はそのビデオコンテナを定義します。例えば、ファイル`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)が使えます。 417 | 418 | ## 歴史 419 | 420 | 一般的なコーデックの内部動作に入って行く前に、いくつかの古いビデオコーデックについて少し理解するために過去を振り返ってみましょう。 421 | 422 | ビデオコーデックである[H.261](https://en.wikipedia.org/wiki/H.261)は1990年(厳密には1988年)に生まれました。H.261は**64 kbit/sのデータレート**で動作するように設計されました。クロマサブサンプリングやマクロブロックなどの考えをすでに使っていました。1995年に、**H.263**ビデオコーデック標準が発表され2001年まで拡張され続けました。 423 | 424 | 2003年に**H.264/AVC**の初版が完成しました。同じ年に **On2 Technologies** (旧Duck Corporation)という会社が、**ロイヤリティーフリー**で非可逆ビデオ圧縮の **VP3**と呼ばれるビデオコーデックをリリースしました。2008年にこの会社を**Googleが買収**し、同じ年に**VP8**をリリースしました。2012年の12月にGoogleが**VP9**をリリースしました。VP9は(モバイルを含む)**ブラウザ市場のおよそ¾でサポートされています**。 425 | 426 | **[AV1](https://en.wikipedia.org/wiki/AOMedia_Video_1)**は新しい**ロイヤリティーフリー**でオープンソースのビデオコーデックで、[Alliance for Open Media (AOMedia)](http://aomedia.org/)によって設計されました。AOMediaは**複数の会社: Google、Mozilla、Microsoft、Amazon、Netflix、AMD、ARM、NVidia、Intel、Cisco**と他のいくつかの会社から成り立っています。リファレンスコーデックの**初版** 0.1.0が**2016年4月7日に公開されました**。 427 | 428 | ![codec history timeline](/i/codec_history_timeline.png "コーデック歴史年表") 429 | 430 | > #### AV1の誕生 431 | > 432 | > 2015年の初期にGoogleが[VP10](https://en.wikipedia.org/wiki/VP9#Successor:_from_VP10_to_AV1)の開発、Xiph (Mozilla)は[Daala](https://xiph.org/daala/)の開発、Ciscoはオープンソースでロイヤリティーフリーの[Thor](https://tools.ietf.org/html/draft-fuldseth-netvc-thor-03)と呼ばれるビデオコーデックの開発に取り組んでいました。 433 | > 434 | > MPEG LAは、当初はHEVC (H.265)の年間ロイヤリティーの上限とH.264の8倍高いライセンス料を発表しましたが、すぐにルールを変更しました。: 435 | > * **年間ロイヤリティーの上限なし** 436 | > * **コンテンツ料金** (収入の0.5%) 437 | > * **h264より10倍高い単位あたり料金** 438 | > 439 | > [alliance for open media](http://aomedia.org/about/)はハードウェアメーカー(Intel、AMD、ARM、Nvidia、Cisco)、コンテンツ配信 (Google、Netflix、Amazon)、ブラウザ開発(Google, Mozilla)、その他の会社によって作られました。 440 | > 441 | > これらの会社にはロイヤリティーフリーのビデオコーデックという共通の目的があり、AV1はより [簡単な特許ライセンス](http://aomedia.org/license/patent/)で誕生しました。**Timothy B. Terriberry**が [AV1の概念、ライセンスモデル、現状](https://www.youtube.com/watch?v=lzPaldsmJbk)についての素晴らしいプレゼンテーションを行いました。この節はこのプレゼンテーションを元に書いています。 442 | > 443 | > **ブラウザーを使ってAV1コーデックを分析**できることを知って驚くことでしょう。https://arewecompressedyet.com/analyzer/ を見てください。 444 | > 445 | > ![av1 browser analyzer](/i/av1_browser_analyzer.png "av1ブラウザーアナライザー") 446 | > 447 | > 追記: コーデックの歴史についてもっと学びたいなら、背後にある[ビデオ圧縮の特許](https://www.vcodex.com/video-compression-patents/)の基本を学ばなくてはなりません。 448 | 449 | ## 一般的コーデック 450 | 451 | **一般的なビデオコーデックの背後にある主な機構**を紹介していきますが、これらの概念のほとんどは VP9、AV1、HEVCのような最新のコーデックでも役に立ち、使われています。物事をかなり単純にして説明することを理解してください。ときどき実際の例(だいたいはH.264)を使って、技法のデモを行います。 452 | 453 | ## ステップ1 - 画像分割 454 | 455 | 最初のステップは、いくつかの**パーティション、サブパーティション**、もっと細かい単位に**フレームを分割**することです。 456 | 457 | ![picture partitioning](/i/picture_partitioning.png "画像分割") 458 | 459 | **しかしなぜ?** たくさんの理由があります。例えば、画像を分割すると小さなパーティションを動きのある小さな部分に使い、より大きなパーティションを静的な背景に使って、予測をより正確に行うことができます。 460 | 461 | 通常、コーデックは、スライス(もしくはタイル)、マクロ(もしくは符号ツリーユニット)やたくさんのサブパーティションで **パーティションを構成します**。これらのパーティションの最大サイズは様々で、HEVCでは64x64、AVC16x16ですが、サブパーティションは4x4までです。 462 | 463 | **フレームはいくつかの形式に分けられている**のを学んだことを覚えていますか?!さて、**これらのアイデアをブロックに適用する**こともできます。つまりIスライス、Bスライス、Iマクロブロックなどを持つことができます。 464 | 465 | > ### ハンズオン: パーティションを調べる 466 | > [Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer) (有料ですが、最初の10フレームに制限された無料お試し版もあります)を使うこともできます。これが分析された[VP9パーティション](/encoding_pratical_examples.md#transcoding)です。 467 | > 468 | > ![VP9 partitions view inte ](/i/paritions_view_intel_video_pro_analyzer.png "VP9 パーティションビュー intel video pro analyzer") 469 | 470 | ## ステップ2 - 予測 471 | 472 | パーティションに分割すると、それらについて予測を行うことができます。[インター予測](#時間的冗長性-インター予測)のために、**動きベクトルと差分を送信する**必要があり、また[イントラ予測](#空間的冗長性-イントラ予測)のために、**予測方向と差分を送信する**必要があります。 473 | 474 | ## ステップ3 - 変換 475 | 476 | 差分ブロック (`予測パーティション - 実際のパーティション`)を生成した後、それを**変換**することで**画質をある程度**保ったまま、どの**ピクセルを捨てられるか**が分かるようになります。それにはいくつかの変換が存在します。 477 | 478 | [他の変換](https://en.wikipedia.org/wiki/List_of_Fourier-related_transforms#Discrete_transforms)もありますが、離散コサイン変換(DCT)をしっかり見ていきます。[**DCT**](https://en.wikipedia.org/wiki/Discrete_cosine_transform)の主な特徴は: 479 | 480 | * **ピクセル**ブロックを同じサイズの**周波数係数**ブロックに**変換する**。 481 | * エネルギーを**偏らせて**、空間的冗長性を削減しやすくする。 482 | * **元に戻せる**、またはピクセルに戻せる 483 | 484 | > 2017年2月2日にCintra, R. J.とBayer, F. Mが[14加算のみの画像圧縮用DCT近似変換](https://arxiv.org/abs/1702.00817)という論文を発表しました。 485 | 486 | 上記の箇条書きの利点を全て理解しなかったとしても心配いりません。その本当の価値を見い出すためにいくつかの実験を試みます。 487 | 488 | 次の**ピクセルのブロック**(8x8)を例にとりましょう: 489 | 490 | ![pixel values matrix](/i/pixel_matrice.png "ピクセル値マトリクス") 491 | 492 | これはブロック画像(8x8)を描画します: 493 | 494 | ![pixel values matrix](/i/gray_image.png "ピクセル値マトリクス") 495 | 496 | このピクセルのブロックに**DCTを適用する**と**係数のブロック** (8x8)を得ます: 497 | 498 | ![coefficients values](/i/dct_coefficient_values.png "係数の値") 499 | 500 | この係数のブロックを描画すれば、次の画像が得られるでしょう: 501 | 502 | ![dct coefficients image](/i/dct_coefficient_image.png "dct係数画像") 503 | 504 | 元の画像とは似ても似つかないことが分かり、**最初の係数**は他の係数とは大きく異なっていることにお気づきかもしれません。この最初の係数はDC係数として知られ、入力配列の**サンプル全体**を表します。**平均に似た**何かです。 505 | 506 | この係数のブロックは高周波数成分を低周波数成分から切り離すという面白い特性を持っています。 507 | 508 | ![dct frequency coefficients property](/i/dctfrequ.jpg "dct周波数係数特性") 509 | 510 | 画像では、**エネルギーのほとんど**は[**低周波**](https://web.archive.org/web/20150129171151/https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm)に集中されます。それで画像を周波数成分に変換して**高周波数係数を捨て**れば、画質をそれほど犠牲にせずに画像を表現するのに必要な**データ量を削減**できます。 511 | 512 | > 周波数は信号がどれだけ速く変化するかを意味します。 513 | 514 | では得られた知識を使って、元の画像をDCTを使って周波数(係数のブロック)に変換して、もっとも重要でない係数の部分を捨てる実験をしてみましょう。 515 | 516 | まず、画像を**周波数領域**に変換します。 517 | 518 | ![coefficients values](/i/dct_coefficient_values.png "周波数値") 519 | 520 | 次に、係数の一部(67%)を捨てます。捨てるのはほとんどは右下の部分です。 521 | 522 | ![zeroed coefficients](/i/dct_coefficient_zeroed.png "ゼロにした係数") 523 | 524 | 最後に、この一部が捨てられた係数のブロックから画像を再生成し(元に戻せる必要があることを覚えておいてください)、元の画像と比較します。 525 | 526 | ![original vs quantized](/i/original_vs_quantized.png "元 vs 量子化後") 527 | 528 | この画像は元の画像と似ていますが、多くの相違点が発生しています。**67.1875%を捨てました**が、 少なくとも元の画像に似ている画像を得ることができました。より賢い方法で係数を捨てて、もっと画質を良くすることもできたのですが、それは次のトピックで扱います。 529 | 530 | > **それぞれの係数は画素全体を使って形成される** 531 | > 532 | > それぞれの係数は、1つの画素に直接マッピングしているわけではなく、画素全体の重み付き合計であることに留意することは重要です。下記の素晴らしいグラフは、1番目と2番目の係数が、それぞれのインデックスで異なる重みを使って、どのように計算されるかを示しています。 533 | > 534 | > ![dct calculation](/i/applicat.jpg "dct計算") 535 | > 536 | > 原典: https://web.archive.org/web/20150129171151/https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm 537 | > 538 | > DCT基底ごとの[単純な画像の形成を見てDCTを視覚化する](/dct_better_explained.ipynb)こともできます。例えば、下記はそれぞれの係数の重みを使って[1つの文字が形成されていく](https://en.wikipedia.org/wiki/Discrete_cosine_transform#Example_of_IDCT)過程です。 539 | > 540 | > ![](https://upload.wikimedia.org/wikipedia/commons/5/5e/Idct-animation.gif ) 541 | 542 |
543 | 544 | > ### ハンズオン: 種々の係数を捨てる 545 | > [DCT変換](/uniform_quantization_experience.ipynb)を実験しましょう。 546 | 547 | ## ステップ4 - 量子化 548 | 549 | 前のステップ (変換)で係数をいくつか捨てるときに、量子化のようなものを行いました。このステップでは、捨てる情報(**損失部分**)を選びます。単純な言葉でいうと、**圧縮を成し遂げるために係数を量子化**します。 550 | 551 | どのように係数のブロックを量子化できるでしょうか?1つの単純な方法は、均一量子化でしょう。ブロックを**単一値** (10) **で割り**、小数点を切り捨てます。 552 | 553 | ![quantize](/i/quantize.png "量子化") 554 | 555 | どのようにこの係数のブロックを**元に戻せる**(再量子化)でしょうか?**同じ値** (10)**を掛ける**ことにより戻すことができます。 556 | 557 | ![re-quantize](/i/re-quantize.png "再量子化") 558 | 559 | この**やり方は最適な方法ではありません**。それぞれの係数の重要度を考慮していないからです。 単一値の代わりに**量子化マトリクス**を使うことができます。このマトリクスで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)ことができます。 560 | 561 | > ### ハンズオン: 量子化 562 | > [量子化](/dct_experiences.ipynb)を実験しましょう。 563 | 564 | ## ステップ5 - エントロピー符号化 565 | 566 | データ(画像 ブロック/スライス/フレーム)を量子化した後、さらに可逆圧縮することができます。データ圧縮のためのたくさんの方法(アルゴリズム)が存在します。それらのいくつかを簡単に体験していきます。より深い理解のためには、この素晴らしい本[Understanding Compression: Data Compression for Modern Developers](https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/)を読むとよいでしょう。 567 | 568 | ### 可変長符号: 569 | 570 | 記号のストリームを持っているとします: **a**、**e**、**r**、**t**とそれらの確率(0から1)がこのテーブルで表されています。 571 | 572 | | | a | e | r | t | 573 | |-------------|-----|-----|------|-----| 574 | | 確率 | 0.3 | 0.3 | 0.2 | 0.2 | 575 | 576 | もっとも確率が高いものには(より小さな)ユニークなバイナリコードを、もっとも確率が低いものにはより大きなバイナリコードを割り当てることができます。 577 | 578 | | | a | e | r | t | 579 | |-------------|-----|-----|------|-----| 580 | | probability | 0.3 | 0.3 | 0.2 | 0.2 | 581 | | binary code | 0 | 10 | 110 | 1110 | 582 | 583 | ストリーム **eat**を圧縮してみましょう。それぞれの記号に8ビット使うと、圧縮なしで**24ビット**使うことになります。しかし、それぞれの記号をそのコードで置き換えると、スペースを節約できます。 584 | 585 | まず記号**e**を符号化して`10`になります。2つ目の記号**a**を符号化すると、足されて(算数の方法ではなく) `[10][0]`となります。最後に3つ目の記号**t**を符号化すると、最終的な圧縮されたビットストリームは `[10][0][1110]`もしくは`1001110`となり、(もとより3.4倍小さなスペースである)**7ビット**しか使いません。 586 | 587 | それぞれのコードはユニークな接頭符号を持つ必要があることに注意してください [ハフマンがこれらの数字を見つけることを助けてくれます](https://en.wikipedia.org/wiki/Huffman_coding)。いくつかの問題がありますが、この方法は[いくつかのビデオコーデックでまだサポート](https://en.wikipedia.org/wiki/Context-adaptive_variable-length_coding)しています。これは圧縮を必要とする多くのアプリケーションに有用なアルゴリズムです。 588 | 589 | エンコーダーとデコーダーの両方がそのコードの記号テーブルを**知らなくてはいけません**。そのためテーブルも送信する必要があります。 590 | 591 | ### 算術符号: 592 | 593 | 記号: **a**, **e**, **r**, **s**, **t** のストリームを持っていて、それらの確率がこの表によって表されると仮定しましょう。 594 | 595 | | | a | e | r | s | t | 596 | |-------------|-----|-----|------|------|-----| 597 | | probability | 0.3 | 0.3 | 0.15 | 0.05 | 0.2 | 598 | 599 | この表を考慮に入れて、出現順にソートされた全ての可能な記号を含む範囲グラフを作ります。 600 | 601 | ![initial arithmetic range](/i/range.png "初期算術範囲") 602 | 603 | さて、ストリーム **eat**を符号化してみましょう。最初の記号**e**を取り上げます。それは**0.3以上0.6未満**に位置しています。この部分範囲を取り上げ、それを再び同じ割合で分割します。 604 | 605 | ![second sub range](/i/second_subrange.png "2番目の部分範囲") 606 | 607 | ストリーム**eat**の符号化を続けましょう。次に記号**a**を取り上げます。これは**0.3以上0.39未満**に位置しています。そして、最後の記号 **t**を取り上げます。もう一度同じ処理を行うと**0.354以上0.372未満**という最終的な範囲を得ます。 608 | 609 | ![final arithmetic range](/i/arithimetic_range.png "最終的な算術範囲") 610 | 611 | 最終範囲**0.354以上0.372未満**から1つの数値を取り上げる必要があります。**0.36**を取り上げましょう。しかしこの部分範囲内ならどんな数値を選んでもかまいません。この数値**だけで**、元のストリーム**eat**を復元することができます。これは、ストリームを符号化する為に範囲の範囲に線を引くかのように捉えることができます。 612 | ![final range traverse](/i/range_show.png "最終範囲横断") 613 | 614 | **逆過程** (別名 復号化) は同様に簡単で、数値**0.36**と元の範囲を使って、同じ処理を行い、この数値から符号化されたストリームを明らかにします。 615 | 616 | 最初の範囲で、その数値が1つの部分に一致し、それが最初の記号であることに気づきます。それから、その部分範囲を以前行ったようにまた分割します。すると**0.36**が記号**a**に合うことがわかり、同じ処理を繰り返すと、最後の記号である**t**を見つけます(符号前のストリーム*eat*を形成します)。 617 | 618 | 符号化と復号化の両方は、記号確率テーブルを**知る必要があります**。それでテーブルを送信する必要があります。 619 | 620 | 素晴らしいですよね。人々は本当に賢く、このような解決策を生み出しました。いくつかの[ビデオコーデック](https://en.wikipedia.org/wiki/Context-adaptive_binary_arithmetic_coding)はこの技法を使っています。(もしくは少なくともオプションとして提供しています)。 621 | 622 | この考えは、量子化ビットストリームを可逆圧縮する為のものです。間違いなくこの記事では伝えきれていない詳細、理由、トレードオフ等が山のようにあります。しかし、読者はデベロッパーとして[もっと学ぶべきです](https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/)。より新しいコーデックは別の[ANSのようなエントロピー符号化アルゴリズム](https://en.wikipedia.org/wiki/Asymmetric_Numeral_Systems)を使おうとしています。 623 | 624 | > ### ハンズオン: CABAC対CAVLC 625 | > [1つはCABAC、もう一方はCAVLCを使って2つのストリームを生成](https://github.com/leandromoreira/introduction_video_technology/blob/master/encoding_pratical_examples.md#cabac-vs-cavlc)し、生成する為にかかる**時間と最終的なサイズを比較**してみましょう。 626 | 627 | ## ステップ6 - ビットストリームフォーマット 628 | 629 | これらのステップ全てを行った後、**圧縮されたフレームとこれらのステップまでのコンテキストを一つにまとめる**必要があります。 **エンコーダによってなされた決定**をデコーダへ明示的に知らせる必要があります。ビット深度、色空間、解像度、予測情報(動きベクトル、イントラ予測方向)、プロファイルレベル、フレームレート、フレームタイプ、フレーム数、その他多数です。 630 | 631 | H.264ビットストリームについて表面的に学んでいきます。最初のステップは[最小限のH.264 *ビットストリームを生成する](/encoding_pratical_examples.md#generate-a-single-frame-h264-bitstream)ことです。このレポジトリと[ffmpeg](http://ffmpeg.org/)を使って、これができます。 632 | 633 | ``` 634 | ./s/ffmpeg -i /files/i/minimal.png -pix_fmt yuv420p /files/v/minimal_yuv420.h264 635 | ``` 636 | 637 | > * ffmpegは、デフォルトで**SEI NAL**として符号化された全パラメータを加えます。NALが何であるかは後ほど定義します。 638 | 639 | このコマンドは、**単一フレーム**、64x64、色空間がyuv420、次の画像をフレームとして使っている生のh264ビットストリームを生成します。 640 | 641 | > ![used frame to generate minimal h264 bitstream](/i/minimal.png "最小h264ビットストリームを生成するフレームを使った") 642 | 643 | ### H.264ビットストリーム 644 | 645 | AVC (H.264)標準は、情報が **[NAL (Network Abstraction Layer)](https://en.wikipedia.org/wiki/Network_Abstraction_Layer)** と呼ばれる **マクロフレーム** (ネットワーク的には)で送信されることを定義しています。NALの主な目的は、"ネットワークフレンドリー"なビデオ表現を提供することです。この標準は、TV (ストリームベース)、インターネット(パケットベース)やその他で動作しなければなりません。 646 | 647 | ![NAL units H.264](/i/nal_units.png "NALユニット H.264") 648 | 649 | NALユニットの境界を定義する **[同期マーカー](https://en.wikipedia.org/wiki/Frame_synchronization)**があります。それぞれの同期マーカーは、最初は`0x00 0x00 0x00 0x01`で、それ以降は`0x00 0x00 0x01`の値を持ちます。もし生成されたh264ビットストリーム上で**16進ダンプ**を行えば、ファイルの最初の方に、少なくとも3つのNALを見つけることができます。 650 | 651 | ![synchronization marker on NAL units](/i/minimal_yuv420_hex.png "NALユニットの同期マーカー") 652 | 653 | 先に述べたとおり、デコーダは画像データだけではなく、動画、フレーム、色、使用されたパラメータ、その他の詳細を知る必要があります。それぞれのNALの**最初のバイト**は、そのカテゴリと**タイプ**を定義します。. 654 | 655 | | NAL タイプID | 説明 | 656 | |--- |---| 657 | | 0 | 未定義 | 658 | | 1 | 非IDRピクチャの符号化スライス | 659 | | 2 | 符号化スライスデータパーティション A | 660 | | 3 | 符号化スライスデータパーティション B | 661 | | 4 | 符号化スライスデータパーティション C | 662 | | 5 | IDRピクチャの**IDR**符号化スライス | 663 | | 6 | **SEI** 付加拡張情報 | 664 | | 7 | **SPS** シーケンスパラメータセット | 665 | | 8 | **PPS** ピクチャパラメータセット | 666 | | 9 | アクセスユニットデリミター | 667 | | 10 | シーケンスの最後 | 668 | | 11 | ストリームの最後 | 669 | | ... | ... | 670 | 671 | 通常、ビットストリームの最初のNALは**SPS**です。このNALの種類は、**プロファイル**、**レベル**、**解像度**やその他の汎用エンコーディング変数を知らせる役割を持ちます。 672 | 673 | 最初の同期マーカーを飛ばすと、**最初のバイト**を復号化して**NALのタイプ**が何かを知ることができます。 674 | 675 | 例えば、同期マーカーの最初のバイトは`01100111`です。最初のビット (`0`)は**forbidden_zero_bit**フィールドで、次の2ビット(`11`)は**nal_ref_idc**フィールドで、このNALが参照フィールドかどうかを示します。残りの5ビット (`00111`)は**nal_unit_type**フィールドで、この例では**SPS** (7) NALユニットです。 676 | 677 | SPS NALの2バイト目(`2進数=01100100、16進数=0x64、10進数=100`)は**profile_idc**フィールドで、エンコーダが使ったプロファイルを示します。この例では、 **[制約付きハイプロファイル](https://en.wikipedia.org/wiki/H.264/MPEG-4_AVC#Profiles)** を使っています。これはB (双方向予測)スライスをサポートしないハイプロファイルです。 678 | 679 | ![SPS binary view](/i/minimal_yuv420_bin.png "SPSバイナリビュー") 680 | 681 | SPS NALについてH.264ビットストリーム仕様を読むと、**パラメータ名**、**カテゴリ**、**説明**の表に多くの値を見つけるでしょう。例えば、`pic_width_in_mbs_minus_1`と`pic_height_in_map_units_minus_1`フィールドについて見てみましょう。 682 | 683 | | パラメータ名 | カテゴリ | 説明 | 684 | |--- |---|---| 685 | | pic_width_in_mbs_minus_1 | 0 | ue(v) | 686 | | pic_height_in_map_units_minus_1 | 0 | ue(v) | 687 | 688 | > **ue(v)**: 符号なし整数 [Exp-Golomb-coded](https://ghostarchive.org/archive/JBwdI) 689 | 690 | これらのフィールドの値に対してある計算をすると、**解像度**を得ることができます。`1920 x 1080`を`pic_width_in_mbs_minus_1`が`119 ( (119 + 1) * macroblock_size = 120 * 16 = 1920) `として表現することができます。空間をさらに節約するために、`1920`を符号化する代わりに、`119`を使っています。 691 | 692 | 生成されたビデオをバイナリビュー (例えば: `xxd -b -c 11 v/minimal_yuv420.h264`)で検査し続けると、最後のNALまで飛ばすことができます。それはフレーム自体です。 693 | 694 | ![h264 idr slice header](/i/slice_nal_idr_bin.png "h264 IDRスライスヘッダー") 695 | 696 | 最初の6バイトの値を見ましょう: `01100101 10001000 10000100 00000000 00100001 11111111`。すでに知ってる通り、最初のバイトでNALが何かを知ることができます。この例では、(`00101`)で **IDRスライス (5)** です。さらに検査してみます: 697 | 698 | ![h264 slice header spec](/i/slice_header.png "h264スライスヘッダ仕様") 699 | 700 | 仕様の情報を使い、スライスのタイプ (**slice_type**)、フレーム番号(**frame_num**)や他の重要なフィールドを復号することができます。 701 | 702 | いくつかのフィールドの値を得るために(`ue(v)、me(v)、se(v)、te(v)`)、それを[Exponential-Golomb](https://ghostarchive.org/archive/JBwdI)と呼ばれる特別なデコーダーを使って、デコードする必要があります。この方法は、デフォルト値が多いケースではたいてい、**変数値を符号化するのにとても効率的**です。 703 | 704 | > このビデオの**slice_type**と**frame_num**の値は7 (Iスライス)と0 (最初のフレーム)です。 705 | 706 | **ビットストリームをプロトコルとして**見ることができます。このビットストリームについてもっと学びたい、もしくは学ぶ必要があるなら、[ITU H.264 spec.]( http://www.itu.int/rec/T-REC-H.264-201610-I)を参照してください。下記はマクロ図表で、ピクチャデータ(圧縮YUV)がどこに位置するかを示しています。 707 | 708 | ![h264 bitstream macro diagram](/i/h264_bitstream_macro_diagram.png "h264ビットストリームマクロ図表") 709 | 710 | [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/)、しかし1つを学ぶと、他は簡単に理解できます。 711 | 712 | > ### ハンズオン: H.264ビットストリームを調べる 713 | > [単一フレームのビデオを生成](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)を見ることもできます。 714 | > 715 | > ![mediainfo details h264 bitstream](/i/mediainfo_details_1.png "mediainfoがh264ビットストリームを詳述する") 716 | > 717 | > [Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer)を使うこともできます。有料ですが、最初の10フレームに制限された無料お試し版もあり、学習目的としては問題ありません。 718 | > 719 | > ![intel video pro analyzer details h264 bitstream](/i/intel-video-pro-analyzer.png "intel video pro analyzerがh264ビットストリームを詳述する") 720 | 721 | ## おさらい 722 | 723 | 多くの**現代のコーデックが、これまで学んできた同じモデルを使っている**ことに気づくでしょう。実際のビデオコーデックのブロック図をみてみましょう。これは学んできた全てのステップを含んでいます。少なくともコーデック関連の発明や文献についてより理解することができるようになったことになります。 724 | 725 | ![thor_codec_block_diagram](/i/thor_codec_block_diagram.png "thor_codec_block_diagram") 726 | 727 | まず[720p解像度で30fpsで1時間のビデオファイルを保存するのに139GBのストレージ](#クロマサブサンプリング)が必要になることを計算しました。ここで学んだ技法つまり**インター予測、イントラ予測、変形、量子化、エントロピー符号化、その他**を駆使して、**ピクセルあたり0.031ビット**で表現できると仮定すると、**139GBに対したった367.82MBだけ**で同じ知覚画質のビデオを保存できます。 728 | 729 | > 今回使用したビデオを元に**ピクセルあたり0.031ビット**が必要になることを導きました。 730 | 731 | ## どのようにH.265はH.264よりも良い圧縮率を実現しているのか? 732 | 733 | 今、コーデックの仕組みについてより理解しています。そのため、新しいコーデックがより高い解像度をより少ないビットでどのように配信できるのか簡単に理解できます。 734 | 735 | AVCとHEVCを比較してみましょう。より多くのCPUサイクル(複雑さ)と圧縮率は、ほとんどいつでもトレードオフであることを心に止めておきましょう。 736 | 737 | HEVCはAVCに比べて、より大きく、より多くの**パーティション** (と **サブパーティション**)のオプションを持っています。そしてより多くの**方向性イントラ予測や角度イントラ予測**、**改善されたエントロピー符号化**やその他を持っています。これら全ての改良のおかげで、H.265はH.264に比べて50%以上の圧縮をすることができるのです。 738 | 739 | ![H.264 vs H.265](/i/avc_vs_hevc.png "h264対h265") 740 | 741 | # オンラインストリーミング 742 | ## 一般的なアーキテクチャ 743 | 744 | ![general architecture](/i/general_architecture.png "一般的なアーキテクチャ") 745 | 746 | [TODO] 747 | 748 | ## プログレッシブダウンロードとアダプティブストリーミング 749 | 750 | ![progressive download](/i/progressive_download.png "プログレッシブダウンロード") 751 | 752 | ![adaptive streaming](/i/adaptive_streaming.png "アダプティブストリーミング") 753 | 754 | [TODO] 755 | 756 | ## コンテンツ保護 757 | 758 | **単純なトークンシステム**を使ってコンテンツを保護することができます。トークンを持っていないユーザーはビデオをリクエストしようとしても、CDNが禁止します。一方有効なトークンを持つユーザーはそのコンテンツを再生することができます。これはたいていのウェブ認証システムとほとんど同じように動作します。 759 | 760 | ![token_protection](/i/token_protection.png "トークン保護") 761 | 762 | このトークンシステムの一人のユーザーが、あるユーザーにビデオをダウンロードさせて、それを配布させることもできます。**DRM (デジタル著作権管理)** システムを使ってこれを避けることができます。 763 | 764 | ![drm](/i/drm.png "drm") 765 | 766 | 実際の製品システムで、人々はしばしばこれら両方の技術を使って、認可と認証を提供します。 767 | 768 | ### DRM 769 | #### メインシステム 770 | 771 | * FPS - [**FairPlay Streaming**](https://developer.apple.com/streaming/fps/) 772 | * PR - [**PlayReady**](https://www.microsoft.com/playready/) 773 | * WV - [**Widevine**](http://www.widevine.com/) 774 | 775 | 776 | #### What? 777 | 778 | DRMは[Digital rights management(デジタル著作権管理)](https://sander.saares.eu/categories/drm-is-not-a-black-box/)を意味します。これは、ビデオやオーディオなどの**デジタルメディアに著作権保護を提供する**方法です。多くの場所で使われていますが、[広くは受け入れられていません](https://en.wikipedia.org/wiki/Digital_rights_management#DRM-free_works)。 779 | 780 | #### Why? 781 | 782 | コンテンツ製作者(たいていはスタジオ)は、デジタルメディアの不正な再配布を防ぐために、 知的財産が複製されることから守りたいためです。 783 | 784 | #### How? 785 | 786 | DRMの抽象的で一般的な形式を、とても単純な方法で説明していきます。 787 | 788 | **コンテンツC1** (例えば hlsやdashビデオストリーミング)と**プレイヤーP1** (例えば shaka-clappr、exo-player、ios)が**デバイスD1** (例えば スマートフォン、テレビ、タブレット、デスクトップ/ノートブック)上にあり、**DRMシステムDRM1** (widevine、playready、FairPlayなど)を使っているとしましょう。 789 | 790 | コンテンツC1は、システムDRM1からの**対称鍵K1**で暗号化され、**暗号化コンテンツC'1**を生成します。 791 | 792 | ![drm general flow](/i/drm_general_flow.jpeg "drm生成フロー") 793 | 794 | デバイスD1のプレイヤーP1は2つの(非対称)鍵、**秘密鍵PRK1** (この鍵は保護され1**D1**にしか知られていない)と**公開鍵PUK1**を持っています。 795 | 796 | > **1保護される**: この保護は、**ハードウェアを介して**なされます。例えば、この鍵は、特別な(読み取り専用)チップの中に保存されます。これは、復号を提供する[ブラックボックス](https://en.wikipedia.org/wiki/Black_box)のように働きます。もしくは(あまり安全でない)**ソフトウェアにより**なされます。DRM システムは、与えられたデバイスがどのタイプの保護を持っているかを知る方法を提供します。 797 | 798 | **プレイヤーP1がコンテンツC'1を再生したい**とき、**DRMシステムDRM1**を使う必要があり、まず公開鍵**PUK1**を与えます。DRMシステムDRM1は クライアントの公開鍵**PUK1**で**暗号化された鍵K1**を返します。理論上、このレスポンスは**D1だけが復号可能**です。 799 | 800 | `K1P1D1 = enc(K1, PUK1)` 801 | 802 | **P1**は、DRMローカルシステム (それは特別なハードウェアかソフトウェアである[SOC](https://en.wikipedia.org/wiki/System_on_a_chip)もなりえる)を使います。このシステムは、秘密鍵PRK1を使って、コンテンツを**復号することができます**。**K1P1D1からの対称鍵K1**を復号化して、**C'1を再生**することができます。鍵がRAM上で外にさらされないのがベストです。 803 | 804 | ``` 805 | K1 = dec(K1P1D1, PRK1) 806 | 807 | P1.play(dec(C'1, K1)) 808 | ``` 809 | 810 | ![drm decoder flow](/i/drm_decoder_flow.jpeg "drmデコードフロー") 811 | 812 | # jupyterの使い方 813 | 814 | **dockerがインストール**されていることを確認して、`./s/start_jupyter.sh`を実行し、ターミナル上の指示に従ってください。 815 | 816 | # カンファレンス 817 | 818 | * [DEMUXED](https://demuxed.com/) - [最後の2つのイベントプレゼンテーションをチェック](https://www.youtube.com/channel/UCIc_DkRxo9UgUSTvWVNCmpA)することができます。 819 | 820 | # 参考文献 821 | 822 | ここに最高のコンテンツがあります。このテキストで見られる全ては、ここから抽出されたか、元になっているか、何か影響を受けています。この驚くべきリンク、本、動画などで、知識をより深くすることができます。 823 | 824 | オンラインコースとチュートリアル: 825 | 826 | * https://www.coursera.org/learn/digital/ 827 | * https://people.xiph.org/~tterribe/pubs/lca2012/auckland/intro_to_video1.pdf 828 | * https://xiph.org/video/vid1.shtml 829 | * https://xiph.org/video/vid2.shtml 830 | * https://wiki.multimedia.cx 831 | * https://mahanstreamer.net 832 | * http://slhck.info/ffmpeg-encoding-course 833 | * http://www.cambridgeincolour.com/tutorials/camera-sensors.htm 834 | * http://www.slideshare.net/vcodex/a-short-history-of-video-coding 835 | * http://www.slideshare.net/vcodex/introduction-to-video-compression-13394338 836 | * https://developer.android.com/guide/topics/media/media-formats.html 837 | * http://www.slideshare.net/MadhawaKasun/audio-compression-23398426 838 | * http://inst.eecs.berkeley.edu/~ee290t/sp04/lectures/02-Motion_Compensation_girod.pdf 839 | 840 | 本: 841 | 842 | * https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/ref=sr_1_1?s=books&ie=UTF8&qid=1486395327&sr=1-1 843 | * https://www.amazon.com/H-264-Advanced-Video-Compression-Standard/dp/0470516925 844 | * https://www.amazon.com/High-Efficiency-Video-Coding-HEVC/dp/3319068946 845 | * 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 846 | * 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 847 | 848 | ビットストリーム仕様: 849 | 850 | * http://www.itu.int/rec/T-REC-H.264-201610-I 851 | * http://www.itu.int/ITU-T/recommendations/rec.aspx?rec=12904&lang=en 852 | * https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf 853 | * http://iphome.hhi.de/wiegand/assets/pdfs/2012_12_IEEE-HEVC-Overview.pdf 854 | * http://phenix.int-evry.fr/jct/doc_end_user/current_document.php?id=7243 855 | * http://gentlelogic.blogspot.com.br/2011/11/exploring-h264-part-2-h264-bitstream.html 856 | * https://forum.doom9.org/showthread.php?t=167081 857 | * https://forum.doom9.org/showthread.php?t=168947 858 | 859 | ソフトウェア: 860 | 861 | * https://ffmpeg.org/ 862 | * https://ffmpeg.org/ffmpeg-all.html 863 | * https://ffmpeg.org/ffprobe.html 864 | * https://mediaarea.net/en/MediaInfo 865 | * https://www.jongbel.com/ 866 | * https://trac.ffmpeg.org/wiki/ 867 | * https://software.intel.com/en-us/intel-video-pro-analyzer 868 | * https://medium.com/@mbebenita/av1-bitstream-analyzer-d25f1c27072b#.d5a89oxz8 869 | 870 | 非ITUコーデック: 871 | 872 | * https://aomedia.googlesource.com/ 873 | * https://github.com/webmproject/libvpx/tree/master/vp9 874 | * https://ghostarchive.org/archive/0W0d8 (was: https://people.xiph.org/~xiphmont/demo/daala/demo1.shtml) 875 | * https://people.xiph.org/~jm/daala/revisiting/ 876 | * https://www.youtube.com/watch?v=lzPaldsmJbk 877 | * https://fosdem.org/2017/schedule/event/om_av1/ 878 | 879 | エンコードの概念: 880 | 881 | * http://x265.org/hevc-h265/ 882 | * http://slhck.info/video/2017/03/01/rate-control.html 883 | * http://slhck.info/video/2017/02/24/vbr-settings.html 884 | * http://slhck.info/video/2017/02/24/crf-guide.html 885 | * https://arxiv.org/pdf/1702.00817v1.pdf 886 | * https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors 887 | * http://web.ece.ucdavis.edu/cerl/ReliableJPEG/Cung/jpeg.html 888 | * http://www.adobe.com/devnet/adobe-media-server/articles/h264_encoding.html 889 | * https://prezi.com/8m7thtvl4ywr/mp3-and-aac-explained/ 890 | * https://blogs.gnome.org/rbultje/2016/12/13/overview-of-the-vp9-video-codec/ 891 | 892 | テスト用ビデオシーケンス: 893 | 894 | * http://bbb3d.renderfarming.net/download.html 895 | * https://www.its.bldrdoc.gov/vqeg/video-datasets-and-organizations.aspx 896 | 897 | その他: 898 | 899 | * https://github.com/Eyevinn/streaming-onboarding 900 | * http://stackoverflow.com/a/24890903 901 | * http://stackoverflow.com/questions/38094302/how-to-understand-header-of-h264 902 | * http://techblog.netflix.com/2016/08/a-large-scale-comparison-of-x264-x265.html 903 | * http://vanseodesign.com/web-design/color-luminance/ 904 | * http://www.biologymad.com/nervoussystem/eyenotes.htm 905 | * http://www.compression.ru/video/codec_comparison/h264_2012/mpeg4_avc_h264_video_codecs_comparison.pdf 906 | * 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) 907 | * http://www.explainthatstuff.com/digitalcameras.html 908 | * http://www.hkvstar.com 909 | * http://www.hometheatersound.com/ 910 | * http://www.lighterra.com/papers/videoencodingh264/ 911 | * http://www.red.com/learn/red-101/video-chroma-subsampling 912 | * http://www.slideshare.net/ManoharKuse/hevc-intra-coding 913 | * http://www.slideshare.net/mwalendo/h264vs-hevc 914 | * http://www.slideshare.net/rvarun7777/final-seminar-46117193 915 | * http://www.springer.com/cda/content/document/cda_downloaddocument/9783642147029-c1.pdf 916 | * http://www.streamingmedia.com/Articles/Editorial/Featured-Articles/A-Progress-Report-The-Alliance-for-Open-Media-and-the-AV1-Codec-110383.aspx 917 | * http://www.streamingmediaglobal.com/Articles/ReadArticle.aspx?ArticleID=116505&PageNum=1 918 | * http://yumichan.net/video-processing/video-compression/introduction-to-h264-nal-unit/ 919 | * https://cardinalpeak.com/blog/the-h-264-sequence-parameter-set/ 920 | * https://cardinalpeak.com/blog/worlds-smallest-h-264-encoder/ 921 | * https://codesequoia.wordpress.com/category/video/ 922 | * https://developer.apple.com/library/content/technotes/tn2224/_index.html 923 | * https://en.wikibooks.org/wiki/MeGUI/x264_Settings 924 | * https://en.wikipedia.org/wiki/Adaptive_bitrate_streaming 925 | * https://en.wikipedia.org/wiki/AOMedia_Video_1 926 | * https://en.wikipedia.org/wiki/Chroma_subsampling#/media/File:Colorcomp.jpg 927 | * https://en.wikipedia.org/wiki/Cone_cell 928 | * https://en.wikipedia.org/wiki/File:H.264_block_diagram_with_quality_score.jpg 929 | * https://en.wikipedia.org/wiki/Inter_frame 930 | * https://en.wikipedia.org/wiki/Intra-frame_coding 931 | * https://en.wikipedia.org/wiki/Photoreceptor_cell 932 | * https://en.wikipedia.org/wiki/Pixel_aspect_ratio 933 | * https://en.wikipedia.org/wiki/Presentation_timestamp 934 | * https://en.wikipedia.org/wiki/Rod_cell 935 | * https://it.wikipedia.org/wiki/File:Pixel_geometry_01_Pengo.jpg 936 | * https://leandromoreira.com.br/2016/10/09/how-to-measure-video-quality-perception/ 937 | * https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping 938 | * https://softwaredevelopmentperestroika.wordpress.com/2014/02/11/image-processing-with-python-numpy-scipy-image-convolution/ 939 | * https://tools.ietf.org/html/draft-fuldseth-netvc-thor-03 940 | * https://www.encoding.com/android/ 941 | * https://www.encoding.com/http-live-streaming-hls/ 942 | * https://web.archive.org/web/20150129171151/https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm 943 | * https://www.lifewire.com/cmos-image-sensor-493271 944 | * https://www.linkedin.com/pulse/brief-history-video-codecs-yoav-nativ 945 | * https://www.linkedin.com/pulse/video-streaming-methodology-reema-majumdar 946 | * https://www.vcodex.com/h264avc-intra-precition/ 947 | * https://www.youtube.com/watch?v=9vgtJJ2wwMA 948 | * https://www.youtube.com/watch?v=LFXN9PiOGtY 949 | * https://www.youtube.com/watch?v=Lto-ajuqW3w&list=PLzH6n4zXuckpKAj1_88VS-8Z6yn9zX_P6 950 | * https://www.youtube.com/watch?v=LWxu4rkZBLw 951 | * https://web.stanford.edu/class/ee398a/handouts/lectures/EE398a_MotionEstimation_2012.pdf 952 | * https://sander.saares.eu/categories/drm-is-not-a-black-box/ 953 | -------------------------------------------------------------------------------- /README-ko.md: -------------------------------------------------------------------------------- 1 | [![license](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg) 2 | 3 | # Intro 4 | 5 | 비디오 기술에 대한 친절한 소개 자료입니다. 비록 소프트웨어 개발자/엔지니어를 대상으로 작성 했지만, **누구나 배울 수 있는** 글이 되었으면 합니다. 아이디어는 [비디오 기술 뉴비를 위한 미니 워크샵](https://docs.google.com/presentation/d/17Z31kEkl_NGJ0M66reqr9_uTG6tI5EDDVXpdPKVuIrs/edit#slide=id.p)에서 비롯되었습니다. 6 | 7 | 가능한 **쉬운 단어, 많은 시각 자료 그리고 실용적인 예제**들로 디지털 비디오의 컨셉을 소개하며 어디서든 사용할 수 있는 지식이 될 수 있는 것이 목표입니다. 언제나 수정 사항, 제안 등을 보내셔서 개선해주세요. 8 | 9 | **실습** 섹션에서는 **도커**가 필요하며 이 레포지토리를 클론하셔야 합니다. 10 | 11 | 12 | ```bash 13 | git clone https://github.com/leandromoreira/digital_video_introduction.git 14 | cd digital_video_introduction 15 | ./setup.sh 16 | ``` 17 | 18 | > **알림**: `./s/ffmpeg`나 `./s/mediainfo` 명령을 사용하는 경우,필요로 하는 모든 요구사항이 포함된 **도커 앱**을 실행하는 것을 의미합니다. 19 | 20 | **모든 실습 내용은 여러분이 클론한 레포지토리 폴더에서 실행되어야 합니다**. 21 | **주피터 예제**의 경우, `./s/start_jupyter.sh`로 서버를 반드시 실행해야하며 URL을 복사해 브라우저에서 사용하시면 됩니다. 22 | 23 | # 변경로그 24 | 25 | * DRM 시스템이 추가되었습니다. 26 | * 1.0.0 버전이 릴리즈 되었습니다. 27 | * 요약 버전의 중국어 번역이 추가되었습니다. 28 | 29 | # 색인 30 | 31 | - [Intro](#intro) 32 | - [변경로그](#%EB%B3%80%EA%B2%BD%EB%A1%9C%EA%B7%B8) 33 | - [색인](#%EC%83%89%EC%9D%B8) 34 | - [기초 용어](#%EA%B8%B0%EC%B4%88-%EC%9A%A9%EC%96%B4) 35 | - [중복 제거](#%EC%A4%91%EB%B3%B5-%EC%A0%9C%EA%B1%B0) 36 | - [색상, 휘도, 시각](#%EC%83%89%EC%83%81-%ED%9C%98%EB%8F%84-%EC%8B%9C%EA%B0%81) 37 | - [색상 모델](#%EC%83%89%EC%83%81-%EB%AA%A8%EB%8D%B8) 38 | - [YCbCr <-> RGB 변환](#ycbcr---rgb-%EB%B3%80%ED%99%98) 39 | - [크로마 서브샘플링](#%ED%81%AC%EB%A1%9C%EB%A7%88-%EC%84%9C%EB%B8%8C%EC%83%98%ED%94%8C%EB%A7%81) 40 | - [프레임 유형](#%ED%94%84%EB%A0%88%EC%9E%84-%EC%9C%A0%ED%98%95) 41 | - [I Frame (인트라, 키프레임)](#i-frame-%EC%9D%B8%ED%8A%B8%EB%9D%BC-%ED%82%A4%ED%94%84%EB%A0%88%EC%9E%84) 42 | - [P Frame (predicted)](#p-frame-predicted) 43 | - [B Frame (bi-predictive)](#b-frame-bi-predictive) 44 | - [요약](#%EC%9A%94%EC%95%BD) 45 | - [시간적 중복 (인터 프리딕션)](#%EC%8B%9C%EA%B0%84%EC%A0%81-%EC%A4%91%EB%B3%B5-%EC%9D%B8%ED%84%B0-%ED%94%84%EB%A6%AC%EB%94%95%EC%85%98) 46 | - [공간적 중복 (인트라 프리딕션)](#%EA%B3%B5%EA%B0%84%EC%A0%81-%EC%A4%91%EB%B3%B5-%EC%9D%B8%ED%8A%B8%EB%9D%BC-%ED%94%84%EB%A6%AC%EB%94%95%EC%85%98) 47 | - [코덱은 어떻게 동작할까요?](#%EC%BD%94%EB%8D%B1%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%A0%EA%B9%8C%EC%9A%94) 48 | - [무엇을? 왜? 어떻게?](#%EB%AC%B4%EC%97%87%EC%9D%84-%EC%99%9C-%EC%96%B4%EB%96%BB%EA%B2%8C) 49 | - [역사](#%EC%97%AD%EC%82%AC) 50 | - [일반 코덱](#%EC%9D%BC%EB%B0%98-%EC%BD%94%EB%8D%B1) 51 | - [첫 번째 스텝 - 사진 파티셔닝](#%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EC%8A%A4%ED%85%9D---%EC%82%AC%EC%A7%84-%ED%8C%8C%ED%8B%B0%EC%85%94%EB%8B%9D) 52 | - [두 번째 스텝 - 프리딕션](#%EB%91%90-%EB%B2%88%EC%A7%B8-%EC%8A%A4%ED%85%9D---%ED%94%84%EB%A6%AC%EB%94%95%EC%85%98) 53 | - [세 번째 스탭 - 변환](#%EC%84%B8-%EB%B2%88%EC%A7%B8-%EC%8A%A4%ED%83%AD---%EB%B3%80%ED%99%98) 54 | - [네 번째 단계 - 양자화](#%EB%84%A4-%EB%B2%88%EC%A7%B8-%EB%8B%A8%EA%B3%84---%EC%96%91%EC%9E%90%ED%99%94) 55 | - [다섯번째 단계 - 엔트로피 인코딩](#%EB%8B%A4%EC%84%AF%EB%B2%88%EC%A7%B8-%EB%8B%A8%EA%B3%84---%EC%97%94%ED%8A%B8%EB%A1%9C%ED%94%BC-%EC%9D%B8%EC%BD%94%EB%94%A9) 56 | - [VLC coding:](#vlc-coding) 57 | - [산술적 코딩:](#%EC%82%B0%EC%88%A0%EC%A0%81-%EC%BD%94%EB%94%A9) 58 | - [여섯번째 단계 - 비트 스트림 포맷](#%EC%97%AC%EC%84%AF%EB%B2%88%EC%A7%B8-%EB%8B%A8%EA%B3%84---%EB%B9%84%ED%8A%B8-%EC%8A%A4%ED%8A%B8%EB%A6%BC-%ED%8F%AC%EB%A7%B7) 59 | - [H.264 비트스트림](#h264-%EB%B9%84%ED%8A%B8%EC%8A%A4%ED%8A%B8%EB%A6%BC) 60 | - [리뷰](#%EB%A6%AC%EB%B7%B0) 61 | - [어떻게 H.265는 H.264보다 더 높은 압축률을 달성했는가](#%EC%96%B4%EB%96%BB%EA%B2%8C-h265%EB%8A%94-h264%EB%B3%B4%EB%8B%A4-%EB%8D%94-%EB%86%92%EC%9D%80-%EC%95%95%EC%B6%95%EB%A5%A0%EC%9D%84-%EB%8B%AC%EC%84%B1%ED%96%88%EB%8A%94%EA%B0%80) 62 | - [온라인 스트리밍](#%EC%98%A8%EB%9D%BC%EC%9D%B8-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D) 63 | - [일반적인 아키텍쳐](#%EC%9D%BC%EB%B0%98%EC%A0%81%EC%9D%B8-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90) 64 | - [점진적 다운로드와 적응형 스트리밍](#%EC%A0%90%EC%A7%84%EC%A0%81-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%EC%99%80-%EC%A0%81%EC%9D%91%ED%98%95-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D) 65 | - [컨텐츠 보호](#%EC%BB%A8%ED%85%90%EC%B8%A0-%EB%B3%B4%ED%98%B8) 66 | - [DRM](#drm) 67 | - [Main systems](#main-systems) 68 | - [무엇인가?](#%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80) 69 | - [왜?](#%EC%99%9C) 70 | - [어떻게?](#%EC%96%B4%EB%96%BB%EA%B2%8C) 71 | - [How to use jupyter](#how-to-use-jupyter) 72 | - [Conferences](#conferences) 73 | - [References](#references) 74 | 75 | # 기초 용어 76 | 77 | **이미지**는 2차원 매트릭스로 생각할 수 있습니다. 여기에 **색상**을 고려한다면, **색상 데이터** 제공에 사용되는 **추가적인 차원**이 있는 **3차원 매트릭스**로 여길 수 있습니다. 78 | 79 | [삼원색(빨간색, 초록색, 파란색)](https://en.wikipedia.org/wiki/Primary_color)을 사용해 색상들을 표현하는 경우, **빨간색**, **초록색**, **파란색** 순의 세 가지 평면을 정의할 수 있습니다. 80 | 81 | ![an image is a 3d matrix RGB](/i/image_3d_matrix_rgb.png "An image is a 3D matrix") 82 | 83 | 이 매트릭스의 각각의 점을 **픽셀**(화소)라 부를 것입니다. 하나의 픽셀은 주어진 색상의 **강도**(보통 숫자 값)를 표현합니다. 예를들어, **빨간 픽셀**은 초록생이 0, 파란색이 0이고 빨간색이 최대인 것을 의미하지요. **핑크 색상 픽셀**은 세 가지 색상의 조합으로 구성되어 있습니다. 대표적인 0부터 255까지의 숫자 범위를 사용하면 핑크색 픽셀은 **빨간색=255, 초록색=192, 파란색=203**으로 정의됩니다. 84 | 85 | > #### 색상 이미지를 인코딩하는 또 다른 방법 86 | > 이미지를 구성하는 색상들을 표현하는데에는 다양한 모델들이 존재합니다. 예를들면 RGB 모델을 사용할 때 3바이트를 사용하는 것 대신, 각 픽셀을 표현하는데 한 바이트만 필요한 인덱스 팔레트를 사용할 수 있지요. 이러한 모델에서는 3차원 매트릭스 대신 2차원 매트릭스를 사용하여 색상을 표현할 수 있으며, 메모리를 아낄 수 있지만 색상에 대한 몇 가지 옵션들을 포기해야하죠. 87 | > 88 | > ![NES palette](/i/nes-color-palette.png "NES palette") 89 | 90 | 예를들어 아래의 사진을 한 번 보세요. 첫 번째 얼굴은 모든 색상이 표현되어있지요. 다른 것들은 빨간색, 초록색, 파란색 평면입니다. (회색 톤으로 나옴) 91 | 92 | ![RGB channels intensity](/i/rgb_channels_intensity.png "RGB channels intensity") 93 | 94 | 여기서 **빨간색**(두 번째 얼굴의 밝은 부분들)이 색상에 많이 나타나는 것을 볼 수 있습니다. 반면 **파란색**(마지막 얼굴)은 대부분 **마리오의 눈**과 옷의 일부에만 나타납니다. **모든 색상**의 영향력이 낮은 부분은 **마리오의 수염**(가장 어두운 부분)입니다. 95 | 96 | 각각의 색상 강도는 특정한 양의 비트를 필요로 합니다. 여기서 양이란 **비트 깊이** 입니다. 각 색상 평면당 8비트(0부터 255 사이의 값을 가지는)를 할애했다고 가정하면 **24비트**의 **색 깊이**(8 비트 * R/G/B의 3가지 평면)를 가지게 되며, 2의 24승 가지의 서로 다른 색상을 사용할 수 있음을 의미합니다. 97 | 98 | > [세상에서 비트로 이미지가 캡쳐되는 원리](http://www.cambridgeincolour.com/tutorials/camera-sensors.htm)에 대해 배울 수 있는 **훌륭한 글** 입니다. 99 | 100 | 이미지의 또 다른 속성은 **해상도**입니다. 해상도는 차원 하나에 포함될 수 있는 픽셀의 수를 의미합니다. 이는 보통 width x height로 표현됩니다. 그 예로 아래에는 **4x4** 이미지가 있습니다. 101 | 102 | ![image resolution](/i/resolution.png "image resolution") 103 | 104 | > #### 실습: 이미지와 색상 가지고 놀기 105 | > [주피터](#how-to-use-jupyter)를 사용하여 [이미지와 색 가지고 놀기](/image_as_3d_array.ipynb)를 해볼 수 있어요. 106 | > 107 | > 108 | > [이미지 필터(외곽선 검출, 강조, 블러)](/filters_are_easy.ipynb)에 대해 학습할 수 있습니다. 109 | 110 | 이미지나 비디오를 다루다보면 볼 수 있는 또 다른 속성인 **종횡비(aspect ratio)** 가 있습니다. 이는 이미지나 픽셀의 폭과 높이의 비례 관계를 간단하게 표현하는 방법입니다. 111 | 112 | 사람들이 이 영상 혹은 사진은 **16x9**야라고 한다면 이는 일반적으로 **화면 종횡비(DAR)** 에 대한 이야기를 하는 것입니다. 반면에 각각의 픽셀의 서로 다른 형태에 대한 것은 **픽셀 종횡비(PAR)** 라고 부릅니다. 113 | 114 | ![display aspect ratio](/i/DAR.png "display aspect ratio") 115 | 116 | ![pixel aspect ratio](/i/PAR.png "pixel aspect ratio") 117 | 118 | > #### DVD는 DAR 4:3이다. 119 | > DVD의 실제 해상도는 704x480이지만 10:11(704x10/480x11)의 PAR로 인해 4:3의 종횡비를 유지합니다. 120 | 121 | 마지막으로 **비디오**는 또다른 차원으로 볼 수 있는 시간의 축에 나열된 **n개의 프레임의 연속**으로 정의할 수 있습니다. 122 | 여기서 n은 프레임레이트 혹은 초당 프레임 수 입니다. 123 | 124 | ![video](/i/video.png "video") 125 | 126 | 비디오의 **비트 레이트**는 동영상을 표시하는데 초당 필요한 비트의 수입니다. 127 | 128 | > 비트레이트 = 폭 * 높이 * 비트 깊이 * 초당 프레임 수 129 | 130 | 예를들어 어떤 영상이 30fps, 24비트 픽셀 깊이, 480x240의 해상도에 압축을 하지 않은 경우라면 **초당 82,944,000비트** 혹은 82.944 Mbps (30x480x240x24)가 필요로 합니다. 131 | 132 | **비트 레이트**가 거의 일정한 경우 이를 고정 비트레이트(**CBR**)라고 부르며 가변의 경우에는 가변 비트레이트(**VBR**)라 부릅니다. 133 | 134 | > 이 그래프는 프레임이 검은색인 동안에는 그리 많은 비트를 소비하지 않는 제한된 VBR을 보여줍니다. 135 | > 136 | > ![constrained vbr](/i/vbr.png "constrained vbr") 137 | 138 | 초기에는 엔지니어들이 **여분의 대역폭 소모 없이** 두 배의 인지 가능한 프레임 레이트로 비디오를 표시하는 기술을 개발했습니다. 이 기술은 **인터레이싱 비디오**라고 부릅니다. 이는 1프레임에서 화면의 절반을 보내고 나머지 절반을 그 다음 프레임에 보냅니다. 139 | 140 | 오늘날의 화면은 대부분 **프로그레시브 스캔 기술**을 사용합니다. 프로그레시브는 각 프레임의 모든 라인을 순서대로 그리는 영상을 출력, 저장, 전송하는 기술입니다. 141 | 142 | ![interlaced vs progressive](/i/interlaced_vs_progressive.png "interlaced vs progressive") 143 | 144 | 이제 어떻게 **이미지**가 디지털로 표현되는지, **색상**이 어떻게 배치되어 영상 표현에 어느 정도의 **초당 비트 수**가 필요한지, 그게 고정(CBR)인지 가변(VBR)인지에 대한 것과 **해상도**와 **프레임 레이트**와 인터레이스드, PAR와 같은 많은 용어들을 배웠습니다. 145 | 146 | > #### 연습: 비디오 속성 확인 147 | > [ffmpeg이나 mediainfo로 속성 살펴보기](https://github.com/leandromoreira/introduction_video_technology/blob/master/encoding_pratical_examples.md#inspect-stream) 148 | 149 | # 중복 제거 150 | 151 | 압축 없이는 비디오를 사용할 수 없다는 것을 배웠습니다. 30fps인 720p 해상도의 **한 시간 짜리 영상**은 **278GB***를 필요로합니다. DEFLATE(PKZIP, Gzip, PNG에 사용되는)와 같은 **무손실 데이터 압축 알고리즘**을 사용하는 경우 대역폭을 충분히 줄일 수 없으므로 비디오를 압축할 다른 방법을 찾아야합니다. 152 | 153 | > * 1280 x 720 x 24 x 30 x 3600 (폭, 높이, 픽셀 당 비트 수, fps, 영상 시간) 154 | 155 | 이를 위해서는, **우리의 눈을 속여야합니다**. 눈은 색상보다 명암 구분을 더 잘합니다. 영상은 변화가 거의 없는 많은 이미지들로 이루어져있으며, 각 프레임들은 같거나 유사한 색상을 사용하는 많은 영역들이 포함되어 있습니다. 156 | 157 | ## 색상, 휘도, 시각 158 | 159 | 시각은 [색상보다 명암에 더 민감하며](http://vanseodesign.com/web-design/color-luminance/), 이를 테스트 해볼 수 있습니다. 이 사진을 보세요. 160 | 161 | ![luminance vs color](/i/luminance_vs_color.png "luminance vs color") 162 | 163 | 만일 왼쪽의 이미지에서 사각형 **A와 B가 동일한 색상**이라는 사실을 눈치채지 못하셨다면 정상입니다. 우리의 뇌는 **색상보다는 명암에 더 신경쓰도록** 만들기 때문이지요. 오른쪽의 사진에서는 같은 색상을 연결해주는 커넥터가 있기 때문에 우리의 뇌는 두 사각형이 동일한 색상이라는 사실을 쉽게 파악할 수 있습니다. 164 | 165 | > **시각은 어떻게 동작하는가에 대한 간단한 설명** 166 | > 167 | > [눈은 복잡한 장기이며](http://www.biologymad.com/nervoussystem/eyenotes.htm), 많은 것들로 구성되어 있으나 여기서는 대부분은 원추세포와 간상세포에 집중합니다. 눈은 [약 1억 2천개의 간상세포와 6백만개의 원추세포로 구성되어 있습니다](https://en.wikipedia.org/wiki/Photoreceptor_cell). 168 | > **더 간단하게 설명하기 위해**, 색상과 명암을 눈의 각 기능들에 입력한다고 가정해봅시다. 169 | > **[간상세포](https://en.wikipedia.org/wiki/Rod_cell) 는 대부분 빛에 반응하는 반면에**, **[원추세포](https://en.wikipedia.org/wiki/Cone_cell)는 색상에 반응합니다**. 서로 다른 색소를 지닌 세 가지 타입의 원추 세포가 존재합니다: [S-cones (파란색), M-cones (초록색) and L-cones (빨간색)](https://upload.wikimedia.org/wikipedia/commons/1/1e/Cones_SMJ2_E.svg). 170 | > 171 | > 간상 세포(명암)가 원추 세포(색상)보다 많기 때문에, 색상보다 명암을 더 잘 구분할 것이라는 사실을 짐작할 수 있을 것입니다. 172 | > 173 | > ![eyes composition](/i/eyes.jpg "eyes composition") 174 | > 175 | > **대비 인식 능력** 176 | > 177 | > 실험 심리학 및 다른 분야들의 연구자들은 사람의 시각에 대한 많은 이론을 개발했습니다. 그 중 하나를 대비 인식 능력이라고 부릅니다. 이것들은 빛의 공간과 시간과 관련 있으며, 주어진 초기에 관측한 빛이 나타내는 값이, 관측자가 변화가 있다는 사실을 말하기까지 얼마나 많은 변화가 필요한지에 대한 것입니다. `function`이 복수형으로 되어 있는데, 그 이유는 바로 대비 인식 능력은 명암 뿐만 아니라 색상도 관계되어 있기 때문입니다. 이 실험의 결과는 대부분의 경우에 우리의 눈이 색상보다 명암에 더 민감하다는 사실을 말해줍니다. 178 | 179 | 이제 **루마**(이미지의 명도)에 더 민감하다는 사실을 알게되었으므로 이를 이용해볼 수 있습니다. 180 | 181 | ### 색상 모델 182 | 183 | 초반에 배웠던 **RGB 모델**을 이용한 [이미지에 색상 지정하는 방법](#basic-terminology)외에도 다른 모델들도 있다는 것을 배웠습니다. 사실 모델 중 크로미넌스(색상)로부터 루마(명도)를 분리하는 모델이 있습니다. 이는 **YCbCr***이라고도 알려져있습니다. 184 | 185 | > * 이와 같은 방식을 사용하는 다른 모델들도 존재합니다. 186 | 187 | 이 색상 모델은 명도를 표현하는 **Y**와 두 가지 색상 채널인 **Cb**(크로마 블루)와 **Cr**(크로마 레드)를 사용합니다. [YCbCr](https://en.wikipedia.org/wiki/YCbCr)은 RGB로부터 유도 가능하며, 또한 반대로 RGB로 역변환도 가능합니다. 이 모델을 사용하여 아래에 보이는 이미지와 같은 완전한 색상의 이미지를 만들 수 있습니다. 188 | 189 | ![ycbcr example](/i/ycbcr.png "ycbcr example") 190 | 191 | ### YCbCr <-> RGB 변환 192 | 193 | 논쟁이 있을 수 있는데요, **어떻게 초록색을 사용하지 않고** 모든 색상을 표현할 수 있을까요? 194 | 195 | 이 질문에 답하기 위해 RGB를 YCbCr로 변환하는 일을 같이 해봅시다. **[ITU-R 그룹*](https://en.wikipedia.org/wiki/ITU-R)** 에서 권장하는 **[표준 BT.601](https://en.wikipedia.org/wiki/Rec._601)** 계수를 사용할 것입니다. 첫 번째 단계는 **루마 계산**입니다. ITU에서 제안한 상수를 사용하고 RGB 값을 대체할 것입니다. 196 | 197 | 198 | ``` 199 | Y = 0.299R + 0.587G + 0.114B 200 | ``` 201 | 202 | 루마를 얻었으니 이제 **색상 분리**(크로마 블루, 크로마 레드)를 할 수 있습니다: 203 | 204 | ``` 205 | Cb = 0.564(B - Y) 206 | Cr = 0.713(R - Y) 207 | ``` 208 | 209 | 또한 이를 **역변환** 가능하며 **YCbCr을 사용하여 초록색**을 얻을 수도 있습니다. 210 | 211 | ``` 212 | R = Y + 1.402Cr 213 | B = Y + 1.772Cb 214 | G = Y - 0.344Cb - 0.714Cr 215 | ``` 216 | 217 | > * 무엇이 표준인지 정의합니다. 가령 [4K란 무엇인가? 어떤 프레임 레이트를 사용해야 하는가? 해상도는? 컬러 모델은?](https://en.wikipedia.org/wiki/Rec._2020)과 같은 것들입니다. 218 | 일반적으로 **디스플레이**(모니터, TV, 스크린 등)는 **오직 RGB 모델**만 활용하며 이를 각자 다른 방식으로 배열합니다. 확대된 모습은 다음과 같습니다: 219 | 220 | ![pixel geometry](/i/new_pixel_geometry.jpg "pixel geometry") 221 | 222 | ### 크로마 서브샘플링 223 | 224 | 루마와 크로마로 표현되는 이미지를 인간의 시각이 크로마보다 루마 해상도에 민감하다는 사실을 이용하여 선택적으로 정보를 제거하는데에 있어 이점을 취할 수 있습니다. **크로마 서브샘플링**은 **루마 대비 크로마 정보를 줄여** 이미지를 인코딩하는 기술입니다. 225 | 226 | ![ycbcr subsampling resolutions](/i/ycbcr_subsampling_resolution.png "ycbcr subsampling resolutions") 227 | 228 | 얼마만큼의 크로마 해상도를 줄여야 하는 것일까요?! 이미 답은 나와 있습니다. 해상도와 병합(`final color = Y + Cb + Cr`)을 다루는 스키마가 이미 존재합니다. 229 | 230 | 이러한 스키마는 서브 샘플링 시스템으로 알려져있으며 루마 픽셀의 `a x 2` 블록에 대한 크로마 해상도를 정의하는 3 부분의 비율 `a:x:y`로 정의됩니다. 231 | 232 | * `a`는 수평 샘플링 레퍼런스입니다. (일반적으로 4) 233 | * `x`는 `a` 픽셀의 행(row)의 크로마 샘플 수입니다. (`a`에 대한 수평해상도) 234 | * `y`는 `a` 픽셀의 첫 번째와 두 번째 행 사이의 크로마 샘플의 변화에 대한 개수입니다. 235 | 236 | > 예외적으로 4:1:0은 각각의 루마 해상도의 `4 x 4`블록 내의 단일 크로마 샘플을 제공합니다. 237 | 238 | 현대 코덱에 사용되는 보편적인 기법은 **4:4:4(서브 샘플링 없음)**, **4:2:2, 4:1:1, 4:2:0, 4:1:0 and 3:1:1**입니다. 239 | 240 | > **YCbCr 4:2:0 병합** 241 | > 242 | > 여기에 YCbCr 4:2:0을 사용한 이미지의 병합된 조각입니다. 픽셀 당 12 비트만 소모했을 뿐이라는 사실을 기억하세요. 243 | > 244 | > ![YCbCr 4:2:0 merge](/i/ycbcr_420_merge.png "YCbCr 4:2:0 merge") 245 | 246 | 동일한 이미지에 대해 주요한 크로마 서브샘플링 타입들로 인코딩한 이미지를 보실 수 있을겁니다. 첫 번째 행의 이미지들은 최종 YCbCr 이미지이며 두 번째 열의 이미지들은 크로마 해상도를 나타냅니다. 적은 손실로 훌륭한 결과를 얻었습니다. 247 | 248 | ![chroma subsampling examples](/i/chroma_subsampling_examples.jpg "chroma subsampling examples") 249 | 250 | 앞서 우리는 [한 시간 분량의 720p 해상도에 30fps인 영상을 저장하기 위해 278GB가 필요하다고 계산](#redundancy-removal)하였습니다. **YCbCr 4:2:0**을 사용하면 이를 **절반(139GB)의 크기***로 줄일 수 있습니다. 하지만 아직 그리 이상적이진 않네요. 251 | 252 | > * 이 값은 width, height, 픽셀 당 비트 수 그리고 fps를 곱하여 얻었습니다. 이전에는 24비트가 필요했지만 이젠 12비트면 됩니다. 253 | 254 |
255 | 256 | > ### 실습: YCbCr 히스토그램 확인하기 257 | > [ffmpeg로 YCbCr 히스토그램을 확인할 수 있습니다.](/encoding_pratical_examples.md#generates-yuv-histogram) 이 장면은 [히스토그램](https://en.wikipedia.org/wiki/Histogram)에 보여지는 바와 같이 파란색 비율이 높습니다. 258 | > 259 | > ![ycbcr color histogram](/i/yuv_histogram.png "ycbcr color histogram") 260 | 261 | ## 프레임 유형 262 | 263 | 이제 계속해서 **시간 상에서의 중복 제거**를 할 수도 있지만 그 전에 몇 가지 기본적인 용어를 정립해보겠습니다. 30fps의 영화가 있다고 가정해봅시다. 여기에 이 영화의 첫 번째부분의 4장의 프레임이 있습니다. 264 | 265 | ![ball 1](/i/smw_background_ball_1.png "ball 1") ![ball 2](/i/smw_background_ball_2.png "ball 2") ![ball 3](/i/smw_background_ball_3.png "ball 3") 266 | ![ball 4](/i/smw_background_ball_4.png "ball 4") 267 | 268 | **파란색 배경**과 같이 프레임 내에 **많은 중복**을 볼 수 있을겁니다. 프레임 0부터 3까지 변한게 없지요. 이 문제를 해결하기 위해 프레임을 세 가지 유형으로 **추상적으로 분류**할 수 있습니다. 269 | 270 | ### I Frame (인트라, 키프레임) 271 | 272 | `I-frame(레퍼런스, 키프레임, 인트라)`은 **독립적인 프레임**입니다. 렌더링 하기 위해 어떤 것에도 의존적이지 않지요. I-frame은 마치 정적인 사진같습니다. 첫 번째 프레임은 보통 I-frame입니다. 하지만 I-frame은 일반적으로 다른 유형의 프레임의 사이사이에 삽입된다는 사실을 볼 수 있을 겁니다. 273 | 274 | ![ball 1](/i/smw_background_ball_1.png "ball 1") 275 | 276 | ### P Frame (predicted) 277 | 278 | `P-frame`은 항상 **이전의 프레임을 이용하여 렌더링 될 수 있다는 점**을 이점으로 취할 수 있습니다. 예를 들어 두 번째 프레임에서 변화라곤 공이 앞으로 움직인 것 뿐입니다. **변경된 부분을 사용해서 이 전의 프레임을 참고하여 프레임 1번을 다시 만들 수 있지요.** 279 | 280 | ![ball 1](/i/smw_background_ball_1.png "ball 1") <- ![ball 2](/i/smw_background_ball_2_diff.png "ball 2") 281 | 282 | > #### 실습: I-frame이 한장 뿐인 동영상 283 | > P-frame이 데이터를 적게 사용하는데 왜 전체를 [I-frame 한장에 나머지 전부가 p-frame인 영상](/encoding_pratical_examples.md#1-i-frame-and-the-rest-p-frames)으로 인코딩하지 않을까요? 284 | > 285 | > 이 비디오를 인코딩 후 영상을 재생해 비디오의 **앞부분으로 이동**해보면, 이동하는데 **시간이 좀 걸림**을 발견하게 될 것입니다. 왜냐하면 **P-frame은 렌더링하기 위해 레퍼런스 프레임(가령 I-frame)이 필요하기 때문**입니다. 286 | > 287 | > 빨리 테스트 해볼 수 있는 다른 방법은 I-frame 한 장으로 비디오를 인코딩한 뒤, [2초 간격으로 I-frame을 삽입하여 인코딩한 뒤](/encoding_pratical_examples.md#1-i-frames-per-second-vs-05-i-frames-per-second) **각 렌디션의 사이즈를 확인해보세요.** 288 | 289 | ### B Frame (bi-predictive) 290 | 291 | 이전과 앞의 프레임을 참조하여 더 나은 압축률을 제공할 수 있다면?! 그것이 바로 B-Frame이라는 것입니다. 292 | 293 | ![ball 1](/i/smw_background_ball_1.png "ball 1") <- ![ball 2](/i/smw_background_ball_2_diff.png "ball 2") -> ![ball 3](/i/smw_background_ball_3.png "ball 3") 294 | 295 | > #### 실습: B-frame으로 비디오 비교하기 296 | > 두 개의 렌디션을 생성해, 첫 번째에는 B-frame을 주고 다른 것에는 [B-frame을 주지 않고](/encoding_pratical_examples.md#no-b-frames-at-all) 퀄리티를 비롯하여 파일 사이즈도 확인을 해보세요. 297 | 298 | ### 요약 299 | 300 | 이러한 프레임들은 **더 나은 압축 성능을 제공**하는데 사용됩니다. 어떻게 이런 일이 가능한지 다음 섹션에서 알아볼 거에요. 하지만 여러분은 이제 **I-frame은 코스트가 높고 P-frame은 좀 더 낮고 B-frame이 제일 낮다는 사실**에 대해 생각할 수 있게 되었습니다. 301 | 302 | ![frame types example](/i/frame_types.png "frame types example") 303 | 304 | ## 시간적 중복 (인터 프리딕션) 305 | 306 | 반드시 줄여야 하는 **시간 상에서의 반복**에 대해 알아봅시다. 이러한 중복의 유형은 **인터 프리딕션**이라는 기법을 활용해 해결할 수 있습니다. 307 | 308 | 프레임 0과 1의 시퀀스를 **더 적은 비트를 활용해** 인코딩 해볼 것 입니다. 309 | 310 | ![original frames](/i/original_frames.png "original frames") 311 | 312 | 시도해 볼 수 있는 한 가지는 바로 제거입니다. 단순히 **프레임 0에서 프레임 1을 빼면** 오로지 **남은 부분만 인코딩**하면 됩니다. 313 | 314 | ![delta frames](/i/difference_frames.png "delta frames") 315 | 316 | 하지만 이보다 더 적은 비트를 사용하는 **더 나은 방법**이 있다면 어떠시겠나요? 우선, `frame 0`을 잘 정의된 파티션의 집합으로 여기고, `frame 0`에 `frame 1`을 매칭시켜보세요. 이를 **모션 추정**으로 생각할 수 있지요. 317 | 318 | > ### 위키페디아 - 블록 모션 보정 319 | > "**블록 모션 보정**은 현재 프레임을 겹치지 않은 블록으로 분할하고, 모션 보정 벡터가 **블록이 어디에서 왔는지 알려줍니다.** (일반적인 오개념으로 이전의 프레임이 겹치지 않은 블록으로 분할되어 있고, 모션 보정 벡터가 이 블록들이 어디로 이동할 지 알려주는 정보라고 여기는 것이 있습니다.) 원본 블록은 일반적으로 원본 프레임에 겹칩니다. 몇몇의 비디오 압축 알고리즘은 서로 다른 이전에 전송된 프레임의 일부 조각을 현재 프레임과 합칩니다." 320 | 321 | ![delta frames](/i/original_frames_motion_estimation.png "delta frames") 322 | 323 | 여기서 `x=0, y=25`에서 `x=6, y=26`으로 공이 움직였다고 추정할 수 있습니다. **x**와 **y**의 값들이 **모션 벡터**입니다. 비트를 절약할 수 있는 **추가적인 단계**로는 최종 블록의 위치와 예측되는 벡터의 **모션 벡터의 차이만 인코딩 하는 것**입니다. 따라서 최종적인 모션 벡터는 `x=6 (6-0), y=1 (26-25)`가 될 것입니다. 324 | 325 | > 현실 세계에서는 이 **공이 n개의 파티션으로 조각**나지만 과정은 동일합니다. 326 | 327 | 프레임 상의 객체는 **3차원 상으로 움직이며**, 이 공이 배경으로 이동하는 경우에 더 작아질 수도 있습니다. 일치점을 찾으려 했던 블록과 **완전히 일치하는 것을 찾을 수 없는 일은** 일반적이지요. 328 | 329 | ![motion estimation](/i/motion_estimation.png "motion estimation") 330 | 331 | 그럼에도 **모션 추정**을 적용하는 것이 단순히 델타 프레임 기법을 사용하는 것 보다 **인코딩 할 데이터가 더 적다는** 사실입니다. 332 | 333 | ![motion estimation vs delta ](/i/comparison_delta_vs_motion_estimation.png "motion estimation delta") 334 | 335 | > ### 모션 보정은 실제로 어떻게 보이는가 336 | > 이 기법은 모든 블록에 대해 적용되며, 공이 매우 빈번하게 하나 이상의 블록에서 분할됩니다. 337 | > ![real world motion compensation](/i/real_world_motion_compensation.png "real world motion compensation") 338 | > Source: https://web.stanford.edu/class/ee398a/handouts/lectures/EE398a_MotionEstimation_2012.pdf 339 | 340 | [주피터를 활용하여 이러한 기법들을 실험해볼 수 있습니다](/frame_difference_vs_motion_estimation_plus_residual.ipynb). 341 | 342 | > #### Hands-on: See the motion vectors 343 | > We can [generate a video with the inter prediction (motion vectors) with ffmpeg.](/encoding_pratical_examples.md#generate-debug-video) 344 | > 345 | > ![inter prediction (motion vectors) with ffmpeg](/i/motion_vectors_ffmpeg.png "inter prediction (motion vectors) with ffmpeg") 346 | > 347 | > Or we can use the [Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer) (which is paid but there is a free trial version which limits you to only the first 10 frames). 348 | > 349 | > ![inter prediction intel video pro analyzer](/i/inter_prediction_intel_video_pro_analyzer.png "inter prediction intel video pro analyzer") 350 | 351 | ## 공간적 중복 (인트라 프리딕션) 352 | 353 | 영상의 **각각의 프레임을** 분석해보면 **많은 영역이 연관되어 있다는 사실**을 발견할 수 있을겁니다. 354 | 355 | ![](/i/repetitions_in_space.png) 356 | 357 | 예제를 살펴보죠. 이 장면은 대부분 파란색과 하얀색으로 구성되어 있습니다. 358 | 359 | ![](/i/smw_bg.png) 360 | 361 | 이는 `I-frame`이며 **이전의 프레임을 사용하여** 예측할 수는 없지만, 여전히 압축할 여지는 남아 있지요. **빨간 블록 부분을** 인코딩해볼거에요. **인접한 부분을 살펴보면**, **블록 주변의 색상의 경향을 추산**할 수 있습니다. 362 | 363 | ![](/i/smw_bg_block.png) 364 | 365 | 프레임이 계속하여 **수직으로 색상이 확산될 것**이라고 예측할 것입니다. 이 의미는 **블록의 인접한 값을 알 수 없는 픽셀의** 색상이 지니고 있다는 것이지요. 366 | 367 | ![](/i/smw_bg_prediction.png) 368 | 369 | **예측이 틀렸을 수도 있습니다.** 그 이유는 바로 이 기법(**인트라 프리딕션**)을 적용한 뒤 잔여 블록을 반환하는 **실제 값을 빼야할** 필요가 있기 때문에, 원본과 비교했을 때 훨씬 더 압축률이 높은 행렬이 생성됩니다. 370 | 371 | ![](/i/smw_residual.png) 372 | 373 | > #### 실습: 인트라 프리딕션 확인해보기 374 | > [매크로 블록과 프리딕션을 사용해 ffmpeg로 영상을 생성할 수 있습니다.](/encoding_pratical_examples.md#generate-debug-video) [각 블록의 색상의 의미](https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors#AnalyzingMacroblockTypes)를 ffmpeg 문서를 확인해 이해해보세요. 375 | > 376 | > ![intra prediction (macro blocks) with ffmpeg](/i/macro_blocks_ffmpeg.png "inter prediction (motion vectors) with ffmpeg") 377 | > 378 | > 혹은 [Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer)(유료지만 처음 10 프레임 제한이 걸려 있는 무료 트라이얼 버전도 있습니다)를 사용해보세요. 379 | > 380 | > ![intra prediction intel video pro analyzer](/i/intra_prediction_intel_video_pro_analyzer.png "intra prediction intel video pro analyzer") 381 | 382 | # 코덱은 어떻게 동작할까요? 383 | 384 | ## 무엇을? 왜? 어떻게? 385 | 386 | **무엇을?** 코덱은 디지털 비디오를 압축 혹은 해제하는 소프트웨어/하드웨어 입니다. **왜?** 시장과 사회는 제한적인 대역폭과 스토리지를 활용한 높은 퀄리티의 영상을 필요로 합니다. 30fps, 픽셀당 24비트, 480x240 해상도의 비디오에 [필요한 대역폭 계산](#%EA%B8%B0%EC%B4%88-%EC%9A%A9%EC%96%B4)을 떠올려보세요. 압축을 하지 않은 경우 **82.944 Mbps**였지요. TV와 인터넷에서 HD/FullHD/4K를 전송할 유일한 방법입니다. **어떻게?** 여기서는 주요 기법들에 대해 간략히 살펴볼 것입니다. 387 | 388 | > **코덱 vs 컨테이너** 389 | > 390 | > 초심자들이 흔히 범하는 실수 중 하나로 디지털 동영상 코덱과 [디지털 비디오 컨테이너](https://en.wikipedia.org/wiki/Digital_container_format)를 혼동하는 것입니다. **컨테이너**를 동영상의 메타 데이터(오디오 포함)를 포함하는 래퍼 포맷으로 여길 수 있으며 **압축된 비디오**를 컨테이너의 페이로드로 볼 수도 있습니다. 391 | > 392 | > 일반적으로 동영상 파일의 확장자는 비디오 파일의 동영상 컨테이너를 정의합니다. 예를들어 `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)를 사용하면 됩니다. 393 | 394 | ## 역사 395 | 396 | 일반적인 코덱의 내부 동작을 파헤치기에 앞서, 구세대 동영상 코덱에 대해 좀 더 자세히 살펴보도록 합시다. 397 | 398 | 비디오 코덱인 [H.261](https://en.wikipedia.org/wiki/H.261)는 1990년(정확히는 1988년)에 탄생하였으며 **64 kibt/s의 데이터 전송비**로 동작하기 위해 설계되었습니다. 이 코덱에서 이미 크로마 서브샘플링, 매크로 블록 등의 아이디어를 사용했습니다. 1995년, **H.263** 비디오 코덱 표준이 탄생하였으며 2001년까지 지속적으로 확장되었습니다. 399 | 400 | **H.264/AVC**의 첫 버전은 2003년에 완성되었습니다. 같은 해에, **TrueMotion**이라는 회사가 **로얄티가 없는** **VP3**이라 부르는 영상 압축 코덱을 공개하였습니다. 2008년, **구글이 이 회사를 인수하고** 같은 해에 **VP8**을 공개하였습니다. 2012년 12월, 구글은 **VP9**을 출시하였으며 VP9은 **브라우저 시장의 약 ¾(모바일 포함)에서 지원됩니다.** 401 | 402 | **[AV1](https://en.wikipedia.org/wiki/AOMedia_Video_1)** 은 **구글, 모질라, 마이크로소프트, 아마존, 넷플릭스, AMD, ARM, 엔비디아, 인텔 그리고 시스코**를 비롯한 여러 회사로 구성된 [오픈 미디어 연합 (AOMedia)](http://aomedia.org/)이 설계를 주도하는 **로얄티 없는** 새로운 오픈 소스 동영상 코덱입니다. 403 | 404 | ![codec history timeline](/i/codec_history_timeline.png "codec history timeline") 405 | 406 | > #### AV1의 탄생 407 | > 408 | > 2015년 초, 구글은 [VP10](https://en.wikipedia.org/wiki/VP9#Successor:_from_VP10_to_AV1)를, Xiph(모질라)는 [Daala](https://xiph.org/daala/) 그리고 시스코는 [Thor](https://tools.ietf.org/html/draft-fuldseth-netvc-thor-03)라는 로얄티 프리 비디오 코덱을 만들었습니다. 409 | > 410 | > MPEG LA는 HEVC(H.265)에 대한 애뉴얼 캡을 발표하고, H.264보다 8배 높은 수수료를 발표했으나 태세전환을 하고 규칙을 변경했습니다: 411 | > * **애뉴얼 캡 없음**, 412 | > * **컨텐츠 수수료** (수익의 0.5%) 그리고 413 | > * **단위당 수수료는 H.264보다 약 10배 높음**. 414 | > 415 | > [오픈 미디어 연합](http://aomedia.org/about/)은 하드웨어 제조사(인텔, AMD, ARM, 엔비디아, 시스코), 컨텐츠 딜리버리(구글, 넷플릭스, 아마존), 브라우저 메인테이너(구글, 모질라)를 비롯한 다른 여러 회사들에 의해 설립되었습니다. 416 | > 417 | > 이 회사들은 동일한 목표를 가지고 있었으며, 로얄티 프리의 동영상 코덱과 훨씬 [단순한 특허 라이센스](http://aomedia.org/license/patent/)로 무장한 AV1의 탄생이였죠. **티모시 B. 테리베리**는 [AV1 컨셉, 라이센스 모델, 현황](https://www.youtube.com/watch?v=lzPaldsmJbk)에 대한 멋진 프리젠테이션을 진행했습니다. 이 섹션의 출처이기도 하지요. 418 | > 419 | > **브라우저로 AV1 코덱을 분석** 가능하다는 사실을 알게되면 아주 놀라실거에요. https://arewecompressedyet.com/analyzer/ 에 방문해보세요. 420 | > ![av1 browser analyzer](/i/av1_browser_analyzer.png "av1 browser analyzer") 421 | > 422 | > 추신: 코덱의 역사에 대해 더 자세히 알고 싶으면 [동영상 압축 특허](https://www.vcodex.com/video-compression-patents/)에 대한 기초를 공부하셔야합니다. 423 | 424 | ## 일반 코덱 425 | 426 | **일반 코덱의 바탕이 되는 주요 메커니즘을** 소개할 것이며, 이러한 컨셉들의 대부분은 유용하고 VP9, AV1, HEVC와 같은 현대 코덱들에서 사용됩니다. 설명을 아주 굉장히 단순화한다는 점 양해 부탁드립니다. 글의 간간히 현업 예제(대부분 H.264)를 시연해볼 것입니다. 427 | 428 | ## 첫 번째 스텝 - 사진 파티셔닝 429 | 430 | 첫 번째 단계는 **프레임을 여러개의 파티션, 서브 파티션** 그 이상으로 **분할**하는 것입니다. 431 | 432 | ![picture partitioning](/i/picture_partitioning.png "picture partitioning") 433 | 434 | **그런데 왜?** 많은 이유가 있어요. 예로, 사진을 분할하게 되면 정적인 배경에 커다란 파티션을 사용하고, 적은 움직임들에 대해서는 작은 파티션을 사용하면 프리딕션을 보다 더 정확하게 수행할 수 있답니다. 435 | 436 | 일반적으로 코덱은 **이러한 파티션들을 슬라이스(혹은 타일), 매크로(혹은 코딩 트리 유닛) 그리고 수 많은 서브 파티션**으로 정리를 합니다. 이러한 파티션의 최대 크기는 다양한데, AVC는 16x16을 사용하는 반면 HEVC는 64x64를 설정하지만 서브 파티션들은 4x4의 사이즈까지도 사용합니다. 437 | 438 | 앞서 공부한 **프레임 유형**을 떠올려봅시다. **이러한 아이디어들을 블록들에도** 적용해볼 수 있지요. 그러므로 I-Slice, B-Slice, I-Macroblock등을 이용할 수 있습니다. 439 | 440 | > ### 실습: 파티션 확인해보기 441 | > 여기서도 [Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer)(유료도 있지만 첫 10프레임 제약의 무료 트라이얼 버전도 있음)를 사용할 것입니다. [VP9 파티션](/encoding_pratical_examples.md#transcoding) 분석본입니다. 442 | > 443 | > ![VP9 partitions view intel video pro analyzer ](/i/paritions_view_intel_video_pro_analyzer.png "VP9 partitions view intel video pro analyzer") 444 | 445 | ## 두 번째 스텝 - 프리딕션 446 | 447 | 파티션을 확보하고 나면, 이제 이에 대한 프리딕션을 수행할 수 있습니다. [인터 프리딕션](#temporal-redundancy-inter-prediction)에 대해서는 **모션 벡터와 나머지**를 전송해야하고, [인트라 프리딕션](#spatial-redundancy-intra-prediction)에 대해서는 **프리딕션의 방향과 나머지를 전송할 것입니다.** 448 | 449 | ## 세 번째 스탭 - 변환 450 | 451 | 잔여 블록(`예측된 파티션 - 실제 파티션`)을 확보하고 난 후에는 **변환**을 통하여 **전반적인 품질을** 유지하면서 **버려야 할 픽셀**을 알 수 있게됩니다. 이 정확한 동작을 위한 몇 가지 변환들이 존재합니다. 452 | 453 | [다른 다양한 변환](https://en.wikipedia.org/wiki/List_of_Fourier-related_transforms#Discrete_transforms)들이 존재하지만, 여기서는 이산 코사인 변환(DCT)에 대해 주도 면밀히 살펴볼 것입니다. [**DCT**](https://en.wikipedia.org/wiki/Discrete_cosine_transform)의 주된 성질로는: 454 | 455 | * **픽셀들의** 블록을 동일한 사이즈의 **주파수 계수**의 블록으로 변환함 456 | * 에너지를 **압축**하여 공간적 중복을 쉽게 제거함 457 | * **가역적임**. 바꿔말해 픽셀로 되돌릴 수 있다는 뜻 458 | 459 | > 2017년 2월 2일, 신트라 R.J. 와 바이엘 F.M은 [14개의 덧셈만 필요로하는 이미지 압축에 대한 DCT 유사 변환](https://arxiv.org/abs/1702.00817)을 논문으로 발표하였습니다. (역자: 8-포인트 직교 근사 이산 코사인 변환으로, 곱셈이나 비트 시프트가 필요가 없음) 460 | 461 | 모든 불렛에 소개된 이점들에 대해 이해하지 못한다고 해서 걱정할 필요는 없습니다. 실제 값을 확인하기 위해 몇 가지 실험을 해볼거에요. 462 | 463 | 다음 **픽셀의 블록** (8x8)을 사용해볼 것입니다: 464 | 465 | ![pixel values matrix](/i/pixel_matrice.png "pixel values matrix") 466 | 467 | 렌더링된 블록 이미지는 다음과 같습니다(8x8): 468 | 469 | ![pixel values matrix](/i/gray_image.png "pixel values matrix") 470 | 471 | 픽셀의 블록에 대하여 **이산 코사인 변환을 수행하면** **계수의 블록**(8x8)을 얻게됩니다: 472 | 473 | ![coefficients values](/i/dct_coefficient_values.png "coefficients values") 474 | 475 | 그리고 이 계수의 블록을 렌더링하면 다음과 같은 이미지를 얻게 됩니다: 476 | 477 | ![dct coefficients image](/i/dct_coefficient_image.png "dct coefficients image") 478 | 479 | 보시는 바와 같이 원본 이미지같이 생기진 않죠. 눈치 채셨을지 모르겠지만 **첫 번째 계수**는 다른 모든 것들과 아주 다릅니다. 첫 번째 계수는 DC 계수로, 입력 배열에서 **샘플의 모든 것**을 표현하며 **평균과 유사합니다.** 480 | 481 | 이 계수 블록은 흥미로운 성질을 가지고 있는데, 높은 주파수 성분을 낮은 주파수 성분과 분리한다는 점이지요. 482 | 483 | ![dct frequency coefficients property](/i/dctfrequ.jpg "dct frequency coefficients property") 484 | 485 | 이미지 상에서, **에너지의 대부분은** [**낮은 주파수**](https://web.archive.org/web/20150129171151/https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm)에 집중되기 때문에, 이미지를 주파수 성분으로 변환하고 **높은 주파수 계수를 날려버리면**, 이미지 품질을 그리 많이 희생하지 않고도 이미지를 표현하는데 필요한 **데이터의 양을 줄일 수 있습니다.** 486 | 487 | > 주파수는 신호가 얼마나 빠르게 변화하는가를 의미합니다. 488 | 489 | 테스트에서 얻은 지식을 활용하여 DCT를 사용해 원본 이미지를 주파수(계수 블록)를 변환 후 중요하지 않은 계수들을 날려봅시다. 490 | 491 | 우선, 이를 **주파수 영역**으로 변환합니다. 492 | 493 | ![coefficients values](/i/dct_coefficient_values.png "coefficients values") 494 | 495 | 다음으로, 오른쪽 하단의 대부분에 해당되는 계수의 부분(67%)를 버립니다. 496 | 497 | ![zeroed coefficients](/i/dct_coefficient_zeroed.png "zeroed coefficients") 498 | 499 | 마침내, 폐기한 계수 블록으로부터 이미지를 재구성해보고 이를 원본과 비교해봅시다. (명심하세요. 이는 가역적이여야 합니다.) 500 | 501 | ![original vs quantized](/i/original_vs_quantized.png "original vs quantized") 502 | 503 | 여러분이 보시는 바와 같이 원본 이미지와 닮긴 했지만 원본과 비교하면 다른 부분이 많이 보입니다. **67.1875%를 날려버리고도** 원본과 유사한 무언가를 얻을 수 있었지요. 더 높은 이미지 품질을 위해 더 똑똑한 방법으로 계수를 폐기하는 방법도 있지만 이는 다음의 주제입니다. 504 | 505 | > **각 계수는 모든 픽셀을 사용하여 만듭니다** 506 | > 507 | > 여기서 기억하셔야 할 중요한 점은 각 계수가 각각의 픽셀과 직접적으로 매핑되지는 않으나 계수는 모든 픽셀의 가중치 합이라는 점입니다. 다음 이 놀라운 도식은 어떻게 첫 번째와 두 번째 계수가 각 인덱스에 대한 고유한 가중치를 사용하여 계산되는지 보여줍니다. 508 | > 509 | > ![dct calculation](/i/applicat.jpg "dct calculation") 510 | > 511 | > 출처: https://web.archive.org/web/20150129171151/https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm 512 | > 513 | > 또한 DCT를 기반으로 [간단한 이미지 형성을 보며 DCT를 시각화](/dct_better_explained.ipynb)해볼 수도 있습니다. 그 예로 각 계수 가중치를 사용하여 [알파벳 A가 형성되는 모습입니다.](https://en.wikipedia.org/wiki/Discrete_cosine_transform#Example_of_IDCT) 514 | > 515 | > ![](https://upload.wikimedia.org/wikipedia/commons/5/5e/Idct-animation.gif ) 516 | 517 | 518 | 519 | 520 |
521 | 522 | > ### 실습: 다른 계수 날리기 523 | > [DCT 변환](/uniform_quantization_experience.ipynb) 524 | 525 | ## 네 번째 단계 - 양자화 526 | 527 | 몇몇 계수를 버릴 때 마지막 단계(변환)에서 일종의 양자화를 수행하였습니다. 이 단계에서는 잃을 정보(간단한 용어로 **손실부**)를 선택하고, **계수를 양자화 하여 압축을 합니다.** 528 | 529 | 어떻게 계수 블록을 양자화 시킬까요? 간단한 방법으로는 균일 양자화가 있습니다. 균일 양자화는 블록을 가지고 **단일 값(10)으로 이를 나누어** 이 값을 내림합니다. 530 | 531 | ![quantize](/i/quantize.png "quantize") 532 | 533 | 이 계수 블록을 어떻게 **변환**(재양자화) 할까요? 처음에 나누었던 값과 **동일한 값을 곱하여** 수행할 수 있습니다. 534 | 535 | ![re-quantize](/i/re-quantize.png "re-quantize") 536 | 537 | **이 방법이 최선은 아닙니다.** 왜냐하면 각 계수의 중요성을 고려하지 않았기 때문입니다. 단일 값 대신 **양자화 행렬**을 사용할 수 있습니다. 이 행렬은 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)에서 확인할 수 있습니다. 538 | 539 | > ### 실습: 양자화 540 | > [양자화](/dct_experiences.ipynb)를 실습해보실 수 있습니다. 541 | 542 | ## 다섯번째 단계 - 엔트로피 인코딩 543 | 544 | 데이터(이미지 블록/슬라이스/프레임)를 양자화 하고 나면 손실 없이 압축할 수 있는 방법이 여전히 남아 있습니다. 데이터를 압축하는 많은 방법(알고리즘)이 존재합니다. 여기서는 그 중 일부를 간략히 알아볼 것입니다. 더 깊은 이해를 바란다면 [압축에 대한 이해: 현대 개발자를 위한 데이터 압축](https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/)이라는 놀라운 책을 읽어보세요. 545 | 546 | ### VLC coding: 547 | 548 | 심볼 스트림이 있다고 가정해봅시다: **a**, **e**, **r**, **t**이며 이 심볼들에 대한 확률(0과 1사이)은 다음 테이블에 표현됩니다. 549 | | | a | e | r | t | 550 | |-------------|-----|-----|------|-----| 551 | | 확률 | 0.3 | 0.3 | 0.2 | 0.2 | 552 | 553 | 가장 가능성이 크고 큰 코드부터 낮은 것까지 고유한 이진 코드(작은 값 추천)를 할당할 수 있습니다. 554 | 555 | | | a | e | r | t | 556 | |-------------|-----|-----|------|-----| 557 | | 확률 | 0.3 | 0.3 | 0.2 | 0.2 | 558 | | 이진 코드 | 0 | 10 | 110 | 1110 | 559 | 560 | 스트림 **eat**를 압축해봅시다. 각 심볼당 8비트를 소비한다고 가정하면 압축하지 않은 경우에 24비트를 소비하게 됩니다. 하지만 각각의 심볼을 코드로 대체하면 공간을 절약할 수 있습니다. 561 | 562 | 첫 번째 단계는 `10`에 해당하는 심볼 **e**를 인코딩하고 두 번째 심볼 **a**를 더하고(산술 연산 아님) `[10][0]` 마지막으로 최종 압축된 비트스트림을 `[10][0][1110]` 혹은 `1001110`으로 만드는 심볼 **t**를 붙여주면 오직 **7비트**만 필요로 합니다. (원본보다 3.4배 적은 공간) 563 | 564 | 각각의 코드는 반드시 고유한 접두사 코드여야 하며 [허프만이 이러한 숫자들을 찾는 데 도움이 됩니다.](https://en.wikipedia.org/wiki/Huffman_coding) 몇 가지 문제가 있음에도 **비디오 코덱들은 여전히 이 방법을 제공**하며 압축을 필요로하는 많은 애플리케이션들을 위한 알고리즘이기도 합니다. 565 | 566 | 엔코더와 디코더 모두 **반드시 이 코드가 있는 심볼 테이블을 알고 있어야 합니다.** 그러므로 테이블도 함께 보내야하죠. 567 | 568 | ### 산술적 코딩: 569 | 570 | 심볼 스트림이 있다고 가정해봅시다: **a**, **e**, **r**, **s**, **t**이며 이 들의 확률은 다음 테이블과 같이 표현됩니다. 571 | 572 | | | a | e | r | s | t | 573 | |-------------|-----|-----|------|------|-----| 574 | | 확률 | 0.3 | 0.3 | 0.15 | 0.05 | 0.2 | 575 | 576 | 이 테이블을 참고하여 빈도순으로 정렬된 모든 심볼을 포함하는 범위를 만들 수 있습니다. 577 | 578 | ![initial arithmetic range](/i/range.png "initial arithmetic range") 579 | 580 | 이제 스트림 **eat**를 인코딩해보죠. 하위 범위 **0.3부터 0.6** 안에 있는 (그러나 포함되진 않음) 첫 번째 심볼 **e**를 선택하고 이 하위 범위를 가지고 이전에 사용한 것과 동일한 비율로 분할해 새로운 범위 내에서 다시 분할합니다. 581 | 582 | ![second sub range](/i/second_subrange.png "second sub range") 583 | 584 | 스트림 **eat**를 이어서 인코딩 해봅시다. 새로운 하위 범위 **0.3부터 0.39**에 포함된 두 번째 심볼 **e**를 취한 뒤 마지막 심볼 **t**로 동일한 과정을 반복하여 마지막 하위 범주인 **0.354부터 0.372**를 얻게됩니다. 585 | 586 | ![final arithmetic range](/i/arithimetic_range.png "final arithmetic range") 587 | 588 | 마지막 하위 범위인 **0.354부터 0.372**내에 있는 숫자를 선택해봅시다. **0.36**을 골라보죠. 이 하위 범주에 내에 있는 수라면 어떤 것이든 상관 없습니다. 그리고 **이 숫자만 있으면** 원본 스트림인 **eat**를 복구할 수 있습니다. 이는 스트림을 인코딩할 범위의 범위 안에 선을 긋는 것과 같습니다. 589 | 590 | ![final range traverse](/i/range_show.png "final range traverse") 591 | 592 | **역과정** (소위 말하는 디코딩)은 인코딩과 동일하게 간단합니다. 선택했던 숫자인 **0.36**으로 동일한 과정을 통하여 원래의 범위를 구하는데, 이번에는 이 숫자를 인코딩된 스트림을 밝혀내는데 사용합니다. 593 | 594 | 첫 번째 범위에서 앞서 선택한 숫자(0.36)가 슬라이스와 일치한다는 사실을 알 수 있습니다. 그러므로 이게 첫 번째 심볼이며 이제 하위 범위로 분할할 차례입니다. 이전과 동일한 과정으로 **0.36**이 심볼 **a**와 일치한다는 사실을 파악할 수 잇죠. 이 과정을 반복하고 나면 마지막 심볼인 **t**에 이르게 됩니다. (원본의 인코딩 된 스트림 *eat*를 구성함) 595 | 596 | 엔코더와 디코더 모두 심볼 확률 테이블에 대해 **반드시 알고 있어야 합니다.** 그러므로 이 테이블도 보내야겠죠. 597 | 598 | 깔끔하쥬? 그쥬? 이 해법을 떠올린 사람들은 겁나 똑똑합니다. 몇몇 [비디오 코덱](https://en.wikipedia.org/wiki/Context-adaptive_binary_arithmetic_coding)은 이 기법을 사용합니다. (적어도 이를 옵션으로 제공하거나 말이죠) 599 | 600 | 이 아이디어는 무손실로 양자화된 비트스트림을 압축하는 것입니다. 이 글은 아주 많은 세부사항, 이유, 절충안 등등이 생략되어 있습니다. 그러나 개발자로서 [더 배우셔야 합니다](https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/). 최신 코덱은 다른 종류의 [ANS와 같은 엔트로피 코딩 알고리즘](https://en.wikipedia.org/wiki/Asymmetric_Numeral_Systems)을 사용하려는 시도를 하고 있습니다. 601 | 602 | > ### 예제: CABAC vs CAVLC 603 | > [CABAC와 CAVLC 이 두 개의 스트림을 생성](https://github.com/leandromoreira/introduction_video_technology/blob/master/encoding_pratical_examples.md#cabac-vs-cavlc)하고 각각의 스트림이 생성하는데 소모하는 **시간을 비교**해 보세요. 또, **결과물의 사이즈도** 비교해보세요. 604 | 605 | ## 여섯번째 단계 - 비트 스트림 포맷 606 | 607 | 이 모든 과정을 마친 후에는 **압축된 프레임과 이 과정들에 대한 컨텍스트를 패킹**해야합니다. **엔코더가 결정한 내용들**, 가령 비트 깊이, 색공간, 해상도, 프리딕션 정보(모션 벡터, 인트라 프리딕션 방향), 프로파일, 레벨, 프레임레이트, 프레임 타입, 프레임 넘버 등의 무수히 많은 정보들에 대하여 디코더에게 이를 명시적으로 알려주어야 합니다. 608 | 609 | 이제 겉핥기로 H.264 비트스트림을 공부해볼거에요. 첫 번째 단계는 [미니멀한 H.264 * 비트스트림](/encoding_pratical_examples.md#generate-a-single-frame-h264-bitstream)을 생성하는 것으로, 이 저장소와 [ffmpeg](http://ffmpeg.org/)를 사용하면 되지요. 610 | 611 | ``` 612 | ./s/ffmpeg -i /files/i/minimal.png -pix_fmt yuv420p /files/v/minimal_yuv420.h264 613 | ``` 614 | 615 | > * ffmpeg는 기본 값으로 모든 인코딩 파라미터에 **SEI NAL**을 붙이는데 NAL이 무엇인지 곧 정의 해볼 것입니다. 616 | 617 | 이 명령어는 **단일 프레임**, 64x64, 색공간 yuv420의 로우 h264 비트스트림을 생성하며 프레임으로는 다음 이미지를 사용할거에요. 618 | 619 | > ![used frame to generate minimal h264 bitstream](/i/minimal.png "used frame to generate minimal h264 bitstream") 620 | 621 | ### H.264 비트스트림 622 | 623 | AVC(H.264) 표준은 **[NAL](https://en.wikipedia.org/wiki/Network_Abstraction_Layer)** (네트워크 추상 계층)이라 부르는 **매크로 프레임** (네트워크 용어)에 전송하는 정보를 정의합니다. NAL의 주된 목적은 "네트워크 친화적"인 비디오 표현의 프로비전으로, 이 표준은 반드시 TV(스트림 기반), 인터넷(패킷 기반) 등을 비롯한 것들에서 반드시 동작해야합니다. 624 | 625 | ![NAL units H.264](/i/nal_units.png "NAL units H.264") 626 | 627 | NAL 유닛의 경계를 정의하는 [동기화 마커](https://en.wikipedia.org/wiki/Frame_synchronization)라는 것이 있습니다. 각 동기화 마커는 맨 첫 번째인 `0x00 0x00 0x00 0x01`를 제외하고 `0x00 0x00 0x01`의 값을 가집니다. 앞서 생성한 h264 비트스트림에 **hexdump**를 떠보면, 파일 시작 부분에 세계의 `NAL`을 확인할 수 있습니다. 628 | 629 | ![synchronization marker on NAL units](/i/minimal_yuv420_hex.png "synchronization marker on NAL units") 630 | 631 | 앞서 말씀드린 바와 같이, 디코더는 사진 정보 뿐만 아니라 비디오의 세부사항, 색, 사용된 파라미터 등의 여러 정보를 필요로 합니다. 각 NAL의 **첫 번째 바이트**는 이에 대한 카테고리와 **타입**을 정의합니다. 632 | 633 | | NAL type id | Description | 634 | |--- |---| 635 | | 0 | 정의되지 않음 | 636 | | 1 | 인코딩된 non-IDR 사진 슬라이스 | 637 | | 2 | 인코딩된 슬라이스 데이터 파티션 A | 638 | | 3 | 인코딩된 슬라이스 데이터 파티션 B | 639 | | 4 | 인코딩된 슬라이스 데이터 파티션 C | 640 | | 5 | **IDR** 인코딩의 IDR 사진 슬라이스 | 641 | | 6 | **SEI** 추가 보완 정보 | 642 | | 7 | **SPS** 시퀀스 파라미터 셋 | 643 | | 8 | **PPS** 사진 파라미터 셋 | 644 | | 9 | 엑세스 단위 델리미터 | 645 | | 10 | 시퀀스의 끝 | 646 | | 11 | 스트림의 끝 | 647 | | ... | ... | 648 | 649 | 보편적으로 비트스트림의 첫 번째 NAL은 **SPS**입니다. 이 타입은 **프로파일**, **레벨**, **해상도** 등과 같은 일반적인 인코딩 변수를 알려주는 역할을 합니다. 650 | 651 | 동기화 마커를 건너 뛰는 경우 **첫 번째 바이트**를 디코드하여 **어떤 타입의 NAL**이 첫 번째 바이트인지 알아낼 수 있습니다. 652 | 653 | 동기화 마커 다음의 첫 번째 바이트는 `01100111`입니다. 이 첫 번째 비트(`0`)는 **forbidden_zero_bit**에 해당하며, 다음 2비트(`11`)는 해당 필드가 이 NAL이 레퍼런스 필드인지 아닌지를 가리키는 **nal_ref_idc**를 알려주며, 나머지 5비트(`00111`)은 `nal_unit_type` 필드를 알려줍니다. 이 경우에는 **SPS** (7) NAL 유닛입니다. 654 | 655 | SPS NAL의 두 번째 바이트(`binary=01100100, hex=0x64, dec=100`)는 인코더가 사용한 프로파일을 보여주는 **profile_idc** 필드로, 이 경우에는 **[constrained high-profile](https://en.wikipedia.org/wiki/H.264/MPEG-4_AVC#Profiles)** 를 사용했습니다. 이는 B(bi-predictive) 슬라이스의 지원이 없는 하이프로파일입니다. 656 | 657 | ![SPS binary view](/i/minimal_yuv420_bin.png "SPS binary view") 658 | 659 | SPS NAL에 대한 H.264 비트스트림 스펙을 읽는 경우, **파라미터 이름**, **카테고리** 그리고 **디스크립션**에 대한 많은 값을 참고할 것입니다. 예를들어 `pic_width_in_mbs_minus_1`와 `pic_height_in_map_units_minus_1`필드를 봅시다. 660 | 661 | | Parameter name | Category | Description | 662 | |--- |---|---| 663 | | pic_width_in_mbs_minus_1 | 0 | ue(v) | 664 | | pic_height_in_map_units_minus_1 | 0 | ue(v) | 665 | 666 | > **ue(v)**: unsigned integer [Exp-Golomb-coded](https://pythonhosted.org/bitstring/exp-golomb.html) 667 | 668 | 이 필드들의 값으로 계산을 때려보면, **해상도**에 이르게 됩니다. `119 ( (119 + 1) * macroblock_size = 120 * 16 = 1920) `의 값과 `pic_width_in_mbs_minus_1`를 사용하여 `1920 x 1080`를 표현할 수 있습니다. `1920`을 인코딩하는 대신 `119`를 사용하여 또 공간을 절약한것이지요. 669 | 670 | 바이너리 뷰(ex: `xxd -b -c 11 v/minimal_yuv420.h264`)로 앞서 생성한 비디오를 계속하여 검사하는 경우, frame 자체를 의미하는 마지막 NAL은 생략할 수 있습니다. 671 | 672 | ![h264 idr slice header](/i/slice_nal_idr_bin.png "h264 idr slice header") 673 | 674 | 앞서 생성한 비디오의 첫 번째 6바이트의 값을 볼 수 있습니다: `01100101 10001000 10000100 00000000 00100001 11111111`. 이미 아는 바와 같이 첫 번째 바이트는 NAL의 타입을 의미하며, 이 경우에는 (`00101`)이며 이는 **IDR Slice (5)** 이고 더 자세히 확인해보면: 675 | 676 | ![h264 slice header spec](/i/slice_header.png "h264 slice header spec") 677 | 678 | 스펙 정보를 바탕으로 슬라스의 타입(**slice_type**), 프레임 번호(**frame_num**)을 비롯한 다른 중요한 필드들을 디코딩할 수 있습니다. 679 | 680 | 몇몇 필드(`ue(v), me(v), se(v) 혹은 te(v)`)의 값을 얻기 위해, [Exponential-Golomb](https://pythonhosted.org/bitstring/exp-golomb.html)라 부르는 특별한 디코더를 사용하여 이를 디코딩할 수 있습니다. 이 방법은 대부분 수많은 기본 값들이 존재하는 경우에 **변수 값을 인코딩하는데에 있어 매우 효과적입니다.** 681 | 682 | > **slice_type**의 값과 이 비디오의 **frame_num**는 각각 7(I slice)과 0 (첫 번째 프레임)입니다. 683 | 684 | **비트스트림을 프로토콜**로 볼 수 있으며 이 비트스트림에 대하여 더 공부하고 싶으시다면 [ITU H.264 spec.](http://www.itu.int/rec/T-REC-H.264-201610-I)를 살펴보세요. 다음은 사진 데이터(압축된 YUV)가 어디에 위치하는지 나타내는 매크로 다이어그램입니다. 685 | 686 | ![h264 bitstream macro diagram](/i/h264_bitstream_macro_diagram.png "h264 bitstream macro diagram") 687 | 688 | [VP9 bitstream](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** bitstream](https://medium.com/@mbebenita/av1-bitstream-analyzer-d25f1c27072b#.d5a89oxz8 689 | )도 말이죠. [모든 비트스트림들이 비슷할까요? 응 아니야](http://www.gpac-licensing.com/2016/07/12/vp9-av1-bitstream-format/), 하지만 한 번 터득하고 나면 다른 것들도 쉽게 배울 수 있습니다. 690 | 691 | > ### 실습: H.264 비트스트림 살펴보기 692 | > [단일 프레임의 비디오를 만들고](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)도 볼 수 있지요. 693 | > 694 | > ![mediainfo details h264 bitstream](/i/mediainfo_details_1.png "mediainfo details h264 bitstream") 695 | > 696 | > [Intel Video Pro Analyzer](https://software.intel.com/en-us/intel-video-pro-analyzer)는 유료이긴 하지만 트라이얼 버전에서는 초반의 10프레임 제한으로 사용해보실 수 있습니다. 학습 용도로는 충분합니다. 697 | > 698 | > ![intel video pro analyzer details h264 bitstream](/i/intel-video-pro-analyzer.png "intel video pro analyzer details h264 bitstream") 699 | 700 | ## 리뷰 701 | 702 | **현대 코덱의 대부분은 지금까지 학습한 것과 동일한 모델을 사용한다는 사실**을 명심해주세요. 실제로 Thor 비디오 코덱의 블록 다이어 그램을 살펴봅시다. 여기에는 앞서 학습한 모든 단계가 포함되어 있습니다. 이 아이디어는 이 분야에 대한 혁신과 논문들에 대해 최소한의 이해를 필요로 합니다. 703 | 704 | ![thor_codec_block_diagram](/i/thor_codec_block_diagram.png "thor_codec_block_diagram") 705 | 706 | 이전에 계산했던 [한 시간 분량의 720p 해상도를 가진 30fps 비디오를 저장하기 위해 139GB의 용량](#%ED%81%AC%EB%A1%9C%EB%A7%88-%EC%84%9C%EB%B8%8C%EC%83%98%ED%94%8C%EB%A7%81)이 필요했었습니다. 여기서 배운 **인터 프리딕션, 인트라 프리딕션, 변환, 양자화, 엔트로피 코딩 등**의 기법을 사용하면, 대략 **픽셀 당 0.031비트**를 소비하며, 볼 수 있을만한 수준의 품질의 비디오에 **139GB와 달리 367.82MB의 용량만을 필요로** 합니다. 707 | 708 | > 여기서 제공된 예제 비디오를 기반으로 **픽셀 당 0.031비트**를 사용하도록 선택하였습니다. 709 | 710 | ## 어떻게 H.265는 H.264보다 더 높은 압축률을 달성했는가 711 | 712 | 이제 코덱이 어떻게 동작하는지에 대해 많이 배웠으니, 새로운 코덱들이 적은 비트를 가지고 더 높은 해상도를 제공하는 방법들에 대해 쉽게 이해하실 수 있을거에요. 713 | 714 | AVC와 HEVC를 비교해볼텐데요, CPU 싸이클(복잡도)와 압축률 간에는 항상 트레이드 오프가 존재한다는 사실을 명심하세요. 715 | 716 | HEVC는 AVC에 비해 더 크고 더 많은 **파티션(그리고 서브 파티션)** 옵션, **인트라 프리딕션 방향**, **향상된 엔트로피 코딩** 등을 가지고 있습니다. 이러한 모든 향상은 H.265가 H.264보다 50% 높은 압축 성능을 발휘할 수 있게 해주었죠. 717 | 718 | ![h264 vs h265](/i/avc_vs_hevc.png "H.264 vs H.265") 719 | 720 | # 온라인 스트리밍 721 | ## 일반적인 아키텍쳐 722 | 723 | ![general architecture](/i/general_architecture.png "general architecture") 724 | 725 | [TODO] 726 | 727 | ## 점진적 다운로드와 적응형 스트리밍 728 | 729 | ![progressive download](/i/progressive_download.png "progressive download") 730 | 731 | ![adaptive streaming](/i/adaptive_streaming.png "adaptive streaming") 732 | 733 | [TODO] 734 | 735 | ## 컨텐츠 보호 736 | 737 | **간단한 토큰 시스템**을 사용하여 컨텐츠를 보호할 수 있습니다. 토큰이 없는 사용자가 비디오를 요청하면 CDN은 사용자를 막고, 유효한 토큰을 지닌 사용자라면 컨텐츠를 재생할 수 있도록 말이죠. 이는 대부분의 웹 인증 시스템과 굉장히 유사합니다. 738 | 739 | ![token protection](/i/token_protection.png "token_protection") 740 | 741 | 이 토큰 시스템만 사용하면 사용자가 비디오를 다운로드 받아 배포가 가능합니다. 그러나 **DRM(디지털 저작권 관리)** 시스템은 이를 막을 수 있습니다. 742 | 743 | ![drm](/i/drm.png "drm") 744 | 745 | 실제 현업의 시스템과 실무자들은 두 기법을 사용하여 인증과 인가를 제공합니다. 746 | 747 | ### DRM 748 | #### Main systems 749 | 750 | * FPS - [**FairPlay Streaming**](https://developer.apple.com/streaming/fps/) 751 | * PR - [**PlayReady**](https://www.microsoft.com/playready/) 752 | * WV - [**Widevine**](http://www.widevine.com/) 753 | 754 | 755 | #### 무엇인가? 756 | 757 | DRM은 디지털 저작권 관리를 의미하며, **디지털 미디어에 대한 권리 보호**를 제공합니다. 많은 곳에서 사용되고 있으나 [보편적으로는 수긍받지 못하고 있습니다](https://en.wikipedia.org/wiki/Digital_rights_management#DRM-free_works). 758 | 759 | #### 왜? 760 | 761 | 컨텐츠 제작자(대부분 스튜디오들)들은 저작물의 무단 배포를 막아 자신의 지적 재산권을 보호하고자 합니다. 762 | 763 | #### 어떻게? 764 | 765 | 극도로 간략화된 DRM의 추상적이고 일반적인 형태만 소개할 것입니다. 766 | 767 | **컨텐츠 C1**(예를들어 HLS나 dash 비디오 스트리밍)을 **DRM 시스템 DRM1**(와이드바인, 플레이레디, 페어플레이)을 사용하는 **디바이스 D1**(예를들어 스마트폰, TV, 태블릿, 데스크탑, 노트북)에서 **플레이어 P1**(예를들어 샤카 클래퍼, 엑소 플레이어, ios)로 본다고 해봅시다. 768 | 769 | 컨텐츠 C1은 시스템 DRM1의 **대칭키 K1**으로 암호화되어 **암호화된 컨텐츠 C'1**을 생성합니다. 770 | 771 | ![drm general flow](/i/drm_general_flow.jpeg "drm general flow") 772 | 773 | 디바이스 D1의 플레이어 P1은 **개인키 PRK1**(이 키는 보호되어 있으며1 **D1**만 알고 있음)와 **공개키 PUK1**라는 두 개의 키(비대칭)를 가지고 있습니다. 774 | 775 | > **1보호됨**: [블랙박스](https://en.wikipedia.org/wiki/Black_box)처럼 동작하는 내부의 특별한(읽기 전용) 칩에 저장된 키로 해독을 제공하는 **하드웨어를 통한** 방법이 있고, 혹은 DRM 시스템이 제공하는 **소프트웨어를 통한**(덜 안전함) 방법이 있습니다. 이 보호는 해당 디바이스가 어떤 방법(하드웨어적, 소프트웨어적)을 가지고 있는지 알 수 있습니다. 776 | 777 | **플레이어 P1이 컨텐츠 C'1을 보고 싶은 경우**, 공개키 **PUK1**를 부여한 **DRM 시스템 DRM1**를 통해 처리해야합니다. DRM시스템 DRM1는 클라이언트의 공개키 **PUK1**를 가지고 **암호화된 키 K1**을 반환합니다. 이론적으로 이 응답은 **D1만이 해독할 수 있습니다**. 778 | 779 | `K1P1D1 = enc(K1, PUK1)` 780 | 781 | **P1**은 DRM 로컬 시스템([SOC](https://en.wikipedia.org/wiki/System_on_a_chip)가 해당될 수 있으며, 특화된 하드웨어나 소프트웨어)을 사용하며, 이 시스템은 개인키 PRK1을 사용한 컨텐츠를 **해독 가능**합니다. **K1P1D1로부터 비대칭 키 K1**를 해독할 수 있으며 **C'1을 재생할 수 있습니다**. 가장 좋은 점은 RAM을 통해 이 키들이 노출이 되지 않는 다는 것이지요. 782 | 783 | ``` 784 | K1 = dec(K1P1D1, PRK1) 785 | 786 | P1.play(dec(C'1, K1)) 787 | ``` 788 | 789 | ![drm decoder flow](/i/drm_decoder_flow.jpeg "drm decoder flow") 790 | 791 | # How to use jupyter 792 | 793 | Make sure you have **docker installed** and just run `./s/start_jupyter.sh` and follow the instructions on the terminal. 794 | 795 | # Conferences 796 | 797 | * [DEMUXED](https://demuxed.com/) - you can [check the last 2 events presentations.](https://www.youtube.com/channel/UCIc_DkRxo9UgUSTvWVNCmpA). 798 | 799 | # References 800 | 801 | The richest content is here, it's where all the info we saw in this text was extracted, based or inspired by. You can deepen your knowledge with these amazing links, books, videos and etc. 802 | 803 | Online Courses and Tutorials: 804 | 805 | * https://www.coursera.org/learn/digital/ 806 | * https://people.xiph.org/~tterribe/pubs/lca2012/auckland/intro_to_video1.pdf 807 | * https://xiph.org/video/vid1.shtml 808 | * https://xiph.org/video/vid2.shtml 809 | * http://slhck.info/ffmpeg-encoding-course 810 | * http://www.cambridgeincolour.com/tutorials/camera-sensors.htm 811 | * http://www.slideshare.net/vcodex/a-short-history-of-video-coding 812 | * http://www.slideshare.net/vcodex/introduction-to-video-compression-13394338 813 | * https://developer.android.com/guide/topics/media/media-formats.html 814 | * http://www.slideshare.net/MadhawaKasun/audio-compression-23398426 815 | * http://inst.eecs.berkeley.edu/~ee290t/sp04/lectures/02-Motion_Compensation_girod.pdf 816 | 817 | Books: 818 | 819 | * https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/ref=sr_1_1?s=books&ie=UTF8&qid=1486395327&sr=1-1 820 | * https://www.amazon.com/H-264-Advanced-Video-Compression-Standard/dp/0470516925 821 | * 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 822 | * 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 823 | 824 | Bitstream Specifications: 825 | 826 | * http://www.itu.int/rec/T-REC-H.264-201610-I 827 | * http://www.itu.int/ITU-T/recommendations/rec.aspx?rec=12904&lang=en 828 | * https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf 829 | * http://iphome.hhi.de/wiegand/assets/pdfs/2012_12_IEEE-HEVC-Overview.pdf 830 | * http://phenix.int-evry.fr/jct/doc_end_user/current_document.php?id=7243 831 | * http://gentlelogic.blogspot.com.br/2011/11/exploring-h264-part-2-h264-bitstream.html 832 | * https://forum.doom9.org/showthread.php?t=167081 833 | * https://forum.doom9.org/showthread.php?t=168947 834 | 835 | Software: 836 | 837 | * https://ffmpeg.org/ 838 | * https://ffmpeg.org/ffmpeg-all.html 839 | * https://ffmpeg.org/ffprobe.html 840 | * https://trac.ffmpeg.org/wiki/ 841 | * https://software.intel.com/en-us/intel-video-pro-analyzer 842 | * https://medium.com/@mbebenita/av1-bitstream-analyzer-d25f1c27072b#.d5a89oxz8 843 | 844 | Non-ITU Codecs: 845 | 846 | * https://aomedia.googlesource.com/ 847 | * https://github.com/webmproject/libvpx/tree/master/vp9 848 | * https://people.xiph.org/~xiphmont/demo/daala/demo1.shtml 849 | * https://people.xiph.org/~jm/daala/revisiting/ 850 | * https://www.youtube.com/watch?v=lzPaldsmJbk 851 | * https://fosdem.org/2017/schedule/event/om_av1/ 852 | 853 | Encoding Concepts: 854 | 855 | * http://x265.org/hevc-h265/ 856 | * http://slhck.info/video/2017/03/01/rate-control.html 857 | * http://slhck.info/video/2017/02/24/vbr-settings.html 858 | * http://slhck.info/video/2017/02/24/crf-guide.html 859 | * https://arxiv.org/pdf/1702.00817v1.pdf 860 | * https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors 861 | * http://web.ece.ucdavis.edu/cerl/ReliableJPEG/Cung/jpeg.html 862 | * http://www.adobe.com/devnet/adobe-media-server/articles/h264_encoding.html 863 | * https://prezi.com/8m7thtvl4ywr/mp3-and-aac-explained/ 864 | * https://blogs.gnome.org/rbultje/2016/12/13/overview-of-the-vp9-video-codec/ 865 | 866 | Video Sequences for Testing: 867 | 868 | * http://bbb3d.renderfarming.net/download.html 869 | * https://www.its.bldrdoc.gov/vqeg/video-datasets-and-organizations.aspx 870 | 871 | Miscellaneous: 872 | 873 | * http://stackoverflow.com/a/24890903 874 | * http://stackoverflow.com/questions/38094302/how-to-understand-header-of-h264 875 | * http://techblog.netflix.com/2016/08/a-large-scale-comparison-of-x264-x265.html 876 | * http://vanseodesign.com/web-design/color-luminance/ 877 | * http://www.biologymad.com/nervoussystem/eyenotes.htm 878 | * http://www.compression.ru/video/codec_comparison/h264_2012/mpeg4_avc_h264_video_codecs_comparison.pdf 879 | * http://www.csc.villanova.edu/~rschumey/csc4800/dct.html 880 | * http://www.explainthatstuff.com/digitalcameras.html 881 | * http://www.hkvstar.com 882 | * http://www.hometheatersound.com/ 883 | * http://www.lighterra.com/papers/videoencodingh264/ 884 | * http://www.red.com/learn/red-101/video-chroma-subsampling 885 | * http://www.slideshare.net/ManoharKuse/hevc-intra-coding 886 | * http://www.slideshare.net/mwalendo/h264vs-hevc 887 | * http://www.slideshare.net/rvarun7777/final-seminar-46117193 888 | * http://www.springer.com/cda/content/document/cda_downloaddocument/9783642147029-c1.pdf 889 | * http://www.streamingmedia.com/Articles/Editorial/Featured-Articles/A-Progress-Report-The-Alliance-for-Open-Media-and-the-AV1-Codec-110383.aspx 890 | * http://www.streamingmediaglobal.com/Articles/ReadArticle.aspx?ArticleID=116505&PageNum=1 891 | * http://yumichan.net/video-processing/video-compression/introduction-to-h264-nal-unit/ 892 | * https://cardinalpeak.com/blog/the-h-264-sequence-parameter-set/ 893 | * https://cardinalpeak.com/blog/worlds-smallest-h-264-encoder/ 894 | * https://codesequoia.wordpress.com/category/video/ 895 | * https://developer.apple.com/library/content/technotes/tn2224/_index.html 896 | * https://en.wikibooks.org/wiki/MeGUI/x264_Settings 897 | * https://en.wikipedia.org/wiki/Adaptive_bitrate_streaming 898 | * https://en.wikipedia.org/wiki/AOMedia_Video_1 899 | * https://en.wikipedia.org/wiki/Chroma_subsampling#/media/File:Colorcomp.jpg 900 | * https://en.wikipedia.org/wiki/Cone_cell 901 | * https://en.wikipedia.org/wiki/File:H.264_block_diagram_with_quality_score.jpg 902 | * https://en.wikipedia.org/wiki/Inter_frame 903 | * https://en.wikipedia.org/wiki/Intra-frame_coding 904 | * https://en.wikipedia.org/wiki/Photoreceptor_cell 905 | * https://en.wikipedia.org/wiki/Pixel_aspect_ratio 906 | * https://en.wikipedia.org/wiki/Presentation_timestamp 907 | * https://en.wikipedia.org/wiki/Rod_cell 908 | * https://it.wikipedia.org/wiki/File:Pixel_geometry_01_Pengo.jpg 909 | * https://leandromoreira.com.br/2016/10/09/how-to-measure-video-quality-perception/ 910 | * https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping 911 | * https://softwaredevelopmentperestroika.wordpress.com/2014/02/11/image-processing-with-python-numpy-scipy-image-convolution/ 912 | * https://tools.ietf.org/html/draft-fuldseth-netvc-thor-03 913 | * https://www.encoding.com/android/ 914 | * https://www.encoding.com/http-live-streaming-hls/ 915 | * https://web.archive.org/web/20150129171151/https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm 916 | * https://www.lifewire.com/cmos-image-sensor-493271 917 | * https://www.linkedin.com/pulse/brief-history-video-codecs-yoav-nativ 918 | * https://www.linkedin.com/pulse/video-streaming-methodology-reema-majumdar 919 | * https://www.vcodex.com/h264avc-intra-precition/ 920 | * https://www.youtube.com/watch?v=9vgtJJ2wwMA 921 | * https://www.youtube.com/watch?v=LFXN9PiOGtY 922 | * https://www.youtube.com/watch?v=Lto-ajuqW3w&list=PLzH6n4zXuckpKAj1_88VS-8Z6yn9zX_P6 923 | * https://www.youtube.com/watch?v=LWxu4rkZBLw 924 | * https://web.stanford.edu/class/ee398a/handouts/lectures/EE398a_MotionEstimation_2012.pdf 925 | --------------------------------------------------------------------------------