一、图像的基本概念
视频:
- 由一组图像组成
- 为了传输/占用更小的空间而被压缩
- 最终在显示设备上展示(未被压缩)
图像:
- 图像由像素组成,像素由RGB组成
- 分辨率:横向的像素点 * 纵向的像素点
每个像素的位深: - RGB888(24位),每个颜色都是8位
- RGBA(32位),其中A表示alpha(8位),表示透明度
屏幕指标:
- PPI:屏幕的质量,一寸长的空间放了多少像素点
- DPI:每英寸的点数,基本上 DPI = PPI
- PPI>300就属于视网膜级别,人眼区分不出来,认为是一体的
二、码流的计算
分辨率:
- X轴的像素个数*Y轴的像素个数
- 常见的宽高比 16:9 / 4:3
- 360P(640*360)/720P/1K/2K 16:9
帧率:
- 每秒钟采集/播放的图像的个数
- 动画的帧率是25帧/s
- 常见的帧率:15帧/s,30帧/s,60帧/s
未编码的视频 RGB的码流:
- RGB码率 = 分辨率(宽*高)x3(Byte)x 帧率(25帧)
- 列如: 12807203*25 = 69120000 约69M
- 通常我们说的码流是以位为单位的。即69M*8=552Mbit
三、什么是YUV
为什么使用YUV:
- 显示器从黑白显示器演变过来的,为了和以前格式兼容
- YUV存储的数据比RGB要少很多。
YUV:
- YUV(也称YCbCr):Y表示明亮度,UV的作用是描述影像色彩及饱和度。
- 主要的采样格式有YUV4:2:0、YUV4:2:2和YUV4:4:4,YUV420 是标准格式。
RGB与YUV的关系:
- RGB用于屏幕图像的展示
- YUV用于采集与编码
RGB转YUV:
YUV转RGB:
四、YUV的常见格式
- YUV4:4:4
- YUV4:2:2
- YUV4:2:0
YUV4:2:0的数据量的计算:
- YUV = Y * 1.5
- YUV = RGB / 2
五、YUV的存储格式
YUV存储格式:
- planar(平面)
I420 : YYYYYYYY UU VV -> YUV420P
YV12: YYYYYYYY VV UU -> YUV420P - packed(打包)
**NV12:**YYYYYYYY UVUV -> YUV420SP
**NV21:**YYYYYYYY VUVU -> YUV420SP
以上两个的主要区别是先存U还是先存V。IOS是YV12格式存储的;android系统一般都是NV21存储。
未编码视频的YUV码流:
- RGB码流 = 分辨率(宽 x 高)x 3 x 帧率
- YUV码流 = 分辨率(宽 x 高)x 1.5 x 帧率(YUV420)
YUV 参考资料: https://en.wikipedia.org/wiki/YUV
六、YUV实战
生成 YUV:
ffmpeg -i input.mp4 \
-an \
-c:v rawvideo \
-pix_fmt yuv420p out.yuv
-i 输入文件
-an a表示音频,n关闭,过滤音频
-c:v rawvideo 使用rawvideo对视频处理
-pix_fmt: 像素格式
out.yuv 输出文件
播放YUV文件:
ffplay -pix_fmt yuv420p -s 1280*720 out.yuv
播放Y分量:
ffplay -pix_fmt yuv420p -s 1280*720 -vf extractplanes='y' out.yuv
提取各分量:
ffmpeg.exe -i "周深-大鱼 (《大鱼海棠》电影印象曲)(超清).mp4"
-filter_complex
"extractplanes=y+u+v[y][u][v]"
-map "[y]" y.yuv
-map "[u]" u.yuv
-map "[v]" v.yuv
播放各分量:
ffplay.exe -s 1280*720 -pix_fmt gray y.yuv
ffplay.exe -s 640*360 -pix_fmt gray u.yuv
ffplay.exe -s 640*360 -pix_fmt gray v.yuv
完整的示例代码:
#include <iostream>
using namespace std;
//包含ffmpeg头文件
extern "C"
{
#include "libavutil/avutil.h"
#include "libavdevice/avdevice.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswresample/swresample.h"
#include "libavutil/audio_fifo.h"
}
#include <windows.h>
#include <vector>
#include <string>
#include <memory>
using std::vector;
using std::string;
using std::shared_ptr;
/**
* @brief open audio device
* @param audio device name
* @return succ: AVFormatContext*, fail: nullptr
*/
AVFormatContext *open_dev(void)
{
int ret = 0;
char errors[1024];
AVFormatContext *fmt_ctx = nullptr;
AVDictionary *options = nullptr;
//get format
//录制摄像头
string sDeviceName = "video=HD Camera";
const AVInputFormat *iformat = av_find_input_format("dshow");
//录制桌面
//string sDeviceName = "desktop";
//const AVInputFormat *iformat = av_find_input_format("gdigrab");
//录制摄像头
//string sDeviceName = "0";
//const AVInputFormat *iformat = av_find_input_format("vfwcap");
av_dict_set(&options, "video_size", "640x480", 0);
av_dict_set(&options, "framerate", "30", 0);
av_dict_set(&options, "pixel_format", "yuyv422", 0);
//open device
if ((ret = avformat_open_input(&fmt_ctx, sDeviceName.data(),
iformat, &options)) < 0) {
av_strerror(ret, errors, 1024);
printf("Failed to open video device, [%d]%s\n", ret, errors);
return nullptr;
}
return fmt_ctx;
}
void rec_video()
{
AVFormatContext *fmt_ctx = nullptr;
AVPacket pkt;
int ret = 0;
int count = 0;
//set log level
av_log_set_level(AV_LOG_DEBUG);
//create file
FILE *outfile = fopen("D:/Study/ffmpeg/av_base/video.yuv", "wb");
if (! outfile) {
printf("Error, Failed to open file!\n");
goto __ERROR;
}
//register audio device
avdevice_register_all();
//打开设备
fmt_ctx = open_dev();
if (! fmt_ctx) {
printf("Error, Failed to open device!\n");
goto __ERROR;
}
//read data from device
while((ret = av_read_frame(fmt_ctx, &pkt)) == 0) {
av_log(nullptr, AV_LOG_INFO,
"packet size is %d\n", pkt.size);
fwrite(pkt.data, 1, static_cast<size_t>(pkt.size), outfile);
fflush(outfile);
av_packet_unref(&pkt);
if ( count++ >= 50)
break;
}
__ERROR:
//close device and release ctx
if (fmt_ctx) {
avformat_close_input(&fmt_ctx);
}
//close file
if (outfile) {
fclose(outfile);
}
av_log(nullptr, AV_LOG_DEBUG, "finish!\n");
return;
}
int main()
{
rec_video();
return 0;
}