ffmpeg开发教程(3) : 关于视频的一些基本概念

后续教程参考ffmpeg-libav-tutorialAn ffmpeg and SDL Tutorial, 对代码进行了一些修改,而且采用C++作为开发语言(其实为C++和C混合编程,ffmpeg 软件包提供的API为C语言接口)

视频

我们知道视频其实是利用的人的视觉暂留现象(比如1秒钟显示24帧图像),经过我们的大脑处理后就成了连贯的图像。

编解码 Codec –压缩数据

原始的视频图像,如果没有经过压缩,那我们来计算一个30分钟的视频,假定图像的分辨率为 1080X1920,采用RGB格式(3个字节,16,777,216种颜色),帧率为24/秒。

toppf = 1080 * 1920 //total_of_pixels_per_frame
cpp = 3 //cost_per_pixel
tis = 30 * 60 //time_in_seconds
fps = 24 //frames_per_second

required_storage = tis * fps * toppf * cpp

那么这段30分钟视频大约需要250.28G的存储空间,带宽需要1.11Gbps。数据量很大,因此需要对图像进行压缩,这也是我们为什么需要编解码codec 算法。常见的codec 算法有h264。

视频文件 — 存储音频和视频

一般的影像同时包含图像和声音,如何有效的将图像和声音保存在一个文件,并提供图像和声音的同步,比如我们常见的视频文件avi, mp4, mkv就是不同的视频文件格式,有些视频同时还包含了字幕信息。这些视频文件就像一个包装盒,将不同的声音,图像和其它比如字幕信息有效的管理保存起来。

FFmpeg libav 基本架构

下图是libav解码的一个基本流程:

ffmpeg开发的一个基本过程就是先把视频文件(比如bunny.mp4) 载入到一个AVFormatContext 结构中(实际是读入视频文件头信息)

然后从载入的视频头信息中,我们可以从中了解到该视频有几个视频,几个音频(比如左右声道),我们本教程只关心视频。可以将视频读入到AVStream数据结构中。

从AVSteam (Stream,流意为连续的数据流)。可以分解出视频数据包AVPacket。AVPacket 还是编码过(压缩)的数据,需要经过解码后(使用相应的AVCodec)得到AVFrame,AVFrame 不在是压缩的,可以直接显示在屏幕上。有了AVFrame后,FFmpeg提供了很多滤镜算法,可以对图像进行处理后再显示,或者在使用不同的编解码器(Codec)转换成其它视频格式,比如从mp4到webm等等。

ffmpeg开发教程(2) :编译第一个示例项目

ffmpeg-tutorial 克隆到本地, 其项目结构如下:

.
├── CMakeLists.txt
├── README.md
├── apps
│   ├── CMakeLists.txt
│   └── tutorial01
│       └── CMakeLists.txt
├── bunny.mp4
├── cmake-build-debug
├── docs
│   └── CMakeLists.txt
├── include
│   ├── fact.hpp
│   └── tutorial.hpp
├── libs
│   ├── CMakeLists.txt
│   └── FFmpeg
│       └── CMakeLists.txt
├── src
│   ├── fact.cpp
│   ├── tutorial01.cpp
│   └── tutorial02.cpp
└── tests
    ├── CMakeLists.txt
    └── all_tests.cpp

根目录下CMakeLists.txt为总控CMakeLists文件,子目录下的CMakeLists.txt比如apps, docs, libs, tests下为子目录CMakeLists文件,分别用来编译应用,文档,引用到的软件包和单元测试。

我们先来看下根目录下的CMakeLists定义:

cmake_minimum_required(VERSION 3.12)
if (DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
    set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
            CACHE STRING "")
endif ()

set(VCPKG_TARGET_TRIPLET $ENV{VCPKG_DEFAULT_TRIPLET} CACHE INTERNAL "" FORCE)

project(ffmpeg_tutorial)

function(TUTORIAL name)
    set(TUTORIAL_TARGET ${name})
    add_executable(${TUTORIAL_TARGET} ${CMAKE_HOME_DIRECTORY}/src/${TUTORIAL_TARGET}.cpp)
    target_link_libraries(${TUTORIAL_TARGET} PRIVATE FFmpeg)
    if (WIN32)
        install(TARGETS ${name}
                RUNTIME DESTINATION ${CMAKE_BINARY_DIR})
    endif ()

endfunction(TUTORIAL)

include_directories("include" )
add_subdirectory(libs)
add_subdirectory(apps)
add_subdirectory(docs)
add_subdirectory(tests)

上篇文章我们提到使用vcpkg来管理C++软件包,因此在根CMakeLists文件中我们可以设定MAKE_TOOLCHAIN_FILE,指到vcpkg的目录,这样使用vcpkg安装所依赖的软件包后,CMake可以找到所需的库文件定义和引用。

而set(VCPKG_TARGET_TRIPLET $ENV{VCPKG_DEFAULT_TRIPLET} CACHE INTERNAL “” FORCE)
定义库定义对应的操作系统和计算机硬件架构比如32位或是64位, 使用静态库还是动态库。
最后添加各个子目录的CMakeLists定义。

使用命令行编译 (Mac,Linux,Windows)

mkdir build
cd build
cmake ..
ninja

使用CLion(Mac,Linux,Windows)

使用CLion前需要配置CMake

-DCMAKE_TOOLCHAIN_FILE=~/Workspace/vcpkg/scripts/buildsystems/vcpkg.cmake -GNinja

使用XCode (Mac)

首先使用CMake 生成XCode workspace 文件。

mkdir xcode
cd xcode
cmake -G Xcode ..

-- The C compiler identification is AppleClang 11.0.0.11000033
-- The CXX compiler identification is AppleClang 11.0.0.11000033
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done

生成ffmpeg_tutorial.xcodeproj,就可以使用XCode编译调试该项目了:

使用 Visual Studio (Windows)

同样可以使用-G 来生成 Visual Studio 项目

cmake -G "Visual Studio 16 2019" -A x64

cmake -G "Visual Studio 15 2017" -A Win32

cmake -G "Visual Studio 15 2017" -A x64

cmake -G "Visual Studio 15 2017" -A ARM

cmake -G "Visual Studio 15 2017" -A ARM64

对于不同的Visual Studio 版本和 32位,64位静态使用不同的参数,比如使用Visual Studio 2017,64位机器

C:\Workspace\ffmpeg-tutorial\vs2017 (master -> origin)
λ cmake -G "Visual Studio 15 2017" -A x64 ..
-- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.18363.
-- The C compiler identification is MSVC 19.16.27035.0
-- The CXX compiler identification is MSVC 19.16.27035.0
-- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2017/Enterprise/VC/Tools/MSVC/14.16.27023/bin/Hostx86/x64/cl.exe
-- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2017/Enterprise/VC/Tools/MSVC/14.16.27023/bin/Hostx86/x64/cl.exe - works -- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2017/Enterprise/VC/Tools/MSVC/14.16.27023/bin/Hostx86/x64/cl.exe
-- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2017/Enterprise/VC/Tools/MSVC/14.16.27023/bin/Hostx86/x64/cl.exe - works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: C:/Workspace/ffmpeg-tutorial/vs2017

然后使用Visual Studio 2017打开ffmpeg_tutorial.sln

使用VSCode (Mac,Linux, Windows)

VSCode可以直接打开该项目,只是需要创建一个运行的配置文件,参考如下:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(lldb) Launch",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/build/apps/tutorial01/tutorial01",
            "args": ["./bunny.mp4"],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "lldb"
        }
    ]
}

之后的教程,你可以选择自己喜欢的开发环境,或者直接命令行。