购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

4.1
YUV图像

本节介绍FFmpeg对YUV图像的处理办法,首先描述为什么引入YUV格式,YUV格式有哪些种类,以及如何在YUV与RGB之间转换数据;然后叙述如何把视频文件中的某帧画面保存为YUV文件;最后阐述如何使用YUView工具浏览YUV文件,并论证视频画面默认采取哪种色度标准。

4.1.1 为什么要用YUV格式

初学者觉得音视频开发很难,不仅因为音视频领域充斥着大量的专业术语,还因为有些地方与通常的认知截然不同,使人怀疑自己是不是白读了这么多年书或者白做了这么多年技术。比如音视频领域对视频画面采用YUV格式,而非常见的RGB格式,初学者刚接触时会觉得这是哪个平行世界的东西,都20XX年了怎么RGB还没有一统江湖吗?

众所周知,自然界的缤纷色彩由三原色组成,无论是以红、黄、青(Cyan)、洋红(Magenta)、黄色(Yellow)、黑色(Black),简称CMYK)为代表的色彩三原色,还是以红、绿、蓝(红色(Red)、绿色(Green)、蓝色(Blue),简称RGB)为代表的光学三原色,都能按照三原色的多少混合成各种颜色。然而YUV根本就是另一套体系,Y表示亮度(Luminance),也就是从黑到白的灰度值,UV联合起来表示色度(Chrominance或Chroma)。有的地方认为U表示色度,V表示浓度或者饱和度,这是望文生义了。实际上,Chrominance译作色度、彩度、彩色信号,而Chroma译作色度、彩度、饱和度、纯度,这两个单词其实表达同一个含义,也就是色度、色彩饱和度。至于浓度对应的英文单词为Concentration,该单词表示物质溶解量的多寡,比如浓盐水、酒精浓度等,并非颜色的深浅程度,跟UV扯不上关系。之所以产生这种误会,应该是中英文的词汇表达范围有差异,很多时候不是一一对应的关系,比如英文的Chrominance和Chroma均可表达中文的色度,而中文的浓度会被拆解为英文的Chroma(用于颜色的深浅程度)和Concentration(用于酒精浓度等溶解多寡程度)。

那么为何视频采用YUV格式而非RGB格式呢?此事说来话长。早在20世纪,人类就发明了黑白电视机,几十年后才发明彩色电视机。两种电视机的区别在于,黑白电视机只有黑、白、灰等灰阶色值,没有赤橙黄绿青蓝紫等彩色,所以黑白电视机只有灰度信号,也就是YUV中的Y值。可是彩色电视机发明之后没有立即取代黑白电视机,二者在日常生活中并行使用了很长一段时间。电视台发出的节目信号既要适配黑白电视机,又要适配彩色电视机,于是人们想办法增加了UV两个通道,连同原来的Y通道组成了新的YUV格式。如此一来,黑白电视机收到节目信号之后,只处理Y通道的灰度值,而彩色电视机对YUV三通道都要处理,于是成功让一套电视信号同时兼容黑白电视机与彩色电视机。

考虑到视频的图像数据较大,有必要对YUV格式先进行数据压缩,鉴于人眼对亮度信息很敏感,对颜色信息相对不那么敏感,故而压缩UV代表的色度数据比较合适。压缩前和压缩后的YUV格式主要有以下4种。

(1)YUV444格式:原始的YUV数据是一一对应的,即一个像素包含一个Y值、一个U值和一个V值。此时在两行两列的四宫格区域中,就有4个Y值、4个U值和4个V值,这种YUV格式简写为YUV444。

(2)YUV422格式:在YUV444的基础上减少UV的数量,横轴的每两个像素才安排一对UV值。此时在两行两列的四宫格区域中,就有4个Y值、两个U值和两个V值,这种YUV格式简写为YUV422。

(3)YUV411格式:在YUV422的基础上再压缩的话有两个方向,一个方向是横轴的每4像素才安排一对UV值。此时在一行4个相邻的区域中,就有4个Y值、一个U值和一个V值,这种YUV格式简写为YUV411。

(4)YUV420格式:在YUV422的基础上再压缩的另一个方向是在横轴压缩的基础上继续压缩纵轴数据,纵轴的每两个像素才安排一对UV值。此时在两行两列的四宫格区域中,就有4个Y值、一个U值和一个V值。因为纵轴数据被压缩,相当于第一行有一对UV值,第二行没有UV值,所以这种格式简写为YUV420。

包括YUV444、YUV422、YUV411、YUV420在内的像素压缩结构如图4-1所示。

图4-1 YUV444、YUV422、YUV411、YUV420四种格式的像素压缩结构

由图4-1可见,YUV420在压缩纵轴时,实际上在四宫格区域的第一行放一个U值,在第二行放一个V值,并非U、V都挤在第一行。只是YUV411已经另有所属,并且YUV411的代号也看不出纵轴被压缩,所以同时压缩横轴和纵轴的方式还是叫作YUV420比较合适。

讲了这么多,表示亮度的Y通道倒是明白了,另外两个U、V通道又是做什么的呢?毕竟色度这个词太笼统了,而且U、V都说是色度,还是不明白U和V到底是什么,它们之间的区别又在哪里。其实YUV与RGB两种格式之间存在数值转换关系,主要包括BT601(标清)、BT709(高清)、BT2020(超高清)三种标准,每个标准又分为模拟电视版、数字电视版和全范围版三个版本。其中数字电视版的Y值区间是[16,235],U、V值区间是[16,240],这是主流的YUV格式;全范围版的色度区间是[0,255],这是JPEG格式的色彩空间,属于YUV格式的JPEG变种,简称YUVJ,末尾的J代表JPEG。于是三种标准乘以三个版本,构成了9套YUV与RGB转换体系,因为计算机只处理数字信号,所以这里不讨论模拟电视版的模拟信号,那就剩下数字电视版和全范围版总共6套色彩转换体系,这些体系对应的数值转换式子列举如下(因YUV与RGB的转换过程较复杂,故以下式子的系数均为近似值)。

下面是BT601标准的数字电视版转换式子。

    R = 1.164 * (Y - 16) + 1.596 * (V - 128)
    G = 1.164 * (Y - 16) - 0.392 * (U - 128) - 0.813 * (V - 128)
    B = 1.164 * (Y - 16) + 2.017 * (U - 128)
    Y = 0.257 * R + 0.504 * G + 0.098 * B + 16
    U = -0.148 * R - 0.291 * G + 0.439 * B + 128
    V = 0.439 * R - 0.368 * G - 0.071 * B + 128

下面是BT601标准的全范围版转换式子。

    R = Y + 1.4075 * (V - 128)
    G = Y - 0.3455 *(U -128) - 0.7169 *(V -128)
    B = Y + 1.779 *(U - 128)
    Y = 0.299 * R + 0.587 * G + 0.114 * B
    U = -0.169 * R - 0.331 * G + 0.500 * B + 128
    V = 0.500 * R - 0.419 * G - 0.081 * B + 128

下面是BT709标准的数字电视版转换式子。

    R = 1.164 * (Y - 16) + 1.792 * (V - 128)
    G = 1.164 * (Y - 16) - 0.213 * (U - 128) - 0.534 * (V - 128)
    B = 1.164 * (Y - 16) + 2.016 * (U - 128)
    Y = 0.183 * R + 0.614 * G + 0.062 * B + 16
    U = -0.101 * R - 0.339 * G + 0.439 * B + 128
    V = 0.439 * R - 0.399 * G - 0.040 * B + 128

下面是BT709标准的全范围版转换式子。

    R = Y + 1.5748 * (V - 128)
    G = Y - 0.1868 *(U -128) - 0.4680 *(V -128)
    B = Y + 1.856 *(U - 128)
    Y = 0.2126 * R + 0.7154 * G + 0.072 * B
    U = -0.1145 * R - 0.3855 * G + 0.500 * B + 128
    V = 0.500 * R - 0.4543 * G - 0.0457 * B + 128

下面是BT2020标准的数字电视版转换式子。

    R = 1.164 * (Y - 16) + 1.6853 * (V - 128)
    G = 1.164 * (Y - 16) - 0.1881 * (U - 128) - 0.6529 * (V - 128)
    B = 1.164 * (Y - 16) + 2.1501 * (U - 128)
    Y = 0.2256 * R + 0.5823 * G + 0.0509 * B + 16
    U = -0.1222 * R - 0.3154 * G + 0.4375 * B + 128
    V = 0.4375 * R - 0.4023 * G - 0.0352 * B + 128

下面是BT2020标准的全范围版转换式子。

    R = Y + 1.4746 * (V - 128)
    G = Y - 0.1645 *(U -128) - 0.5713 *(V -128)
    B = Y + 1.8814 *(U - 128)
    Y = 0.2627 * R + 0.6780 * G + 0.0593 * B
    U = -0.1396 * R - 0.3604 * G + 0.500 * B + 128
    V = 0.500 * R - 0.4598 * G - 0.0402 * B + 128

在以上6套转换式子中,Y值均由R、G、B三原色按比例混合合成,说明Y通道表达RGB的亮度分量。U值在B通道(蓝色)加强,且在R、G两方面按比例减弱,说明U通道表达色度的蓝色投影。V值在R通道(红色)加强,且在G、B两方面按比例减弱,说明V通道表达色度的红色投影。由于U通道投影蓝色,V通道投影红色,因此U通道也称作Cb,V通道也称作Cr,故而YUV在数字电视领域也可称作YCbCr。当Y值拉到最高亮度时,把U值作为平面坐标系的横轴,把V值作为平面坐标系的纵轴,则U、V取值及其对应的色彩变化如图4-2所示。

图4-2 U、V取值及其对应的色彩变化

如果Y、U、V三通道均为0,又会呈现什么颜色呢?以BT601标准的数字电视版为例,把YUV全0代入RGB的转换式子,可得R≈−185.7,G≈135.6,B≈−239.6。因为有效的色值范围是[0,255],且色值必须为整数,所以实际展现的色值为R=0,G=136,B=0。可见红、蓝两色均为0,只有绿色值为136,想来应是绿油油的色彩。不过以上仅是理论计算结果,还需实际运行加以验证。于是编写FFmpeg代码,向视频文件写入数据帧的时候,把Y、U、V三个通道均赋值为0,样例代码如下(完整代码见chapter08/writeyuv.c):

    AVFrame *frame = av_frame_alloc();          // 分配一个数据帧
    frame->format = video_encode_ctx->pix_fmt;  // 像素格式
    frame->width  = video_encode_ctx->width;    // 视频宽度
    frame->height = video_encode_ctx->height;   // 视频高度
    int ret = av_frame_get_buffer(frame, 0);    // 为数据帧分配缓冲区
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Can't allocate frame data %d.\n", ret);
        return -1;
    }
    int index = 0;
    while (index < 200) {                                          // 写入200帧
        ret = av_frame_make_writable(frame);                       // 确保数据帧是可写的
        if (ret < 0) {
            av_log(NULL, AV_LOG_ERROR, "Can't make frame writable %d.\n", ret);
            return -1;
        }
        int x, y;
        // 写入Y值
        for (y = 0; y < video_encode_ctx->height; y++)
            for (x = 0; x < video_encode_ctx->width; x++)
                frame->data[0][y * frame->linesize[0] + x] = 0;               // Y值填0
        // 写入U值(Cb)和V值(Cr)
        for (y = 0; y < video_encode_ctx->height / 2; y++) {
            for (x = 0; x < video_encode_ctx->width / 2; x++) {
                frame->data[1][y * frame->linesize[1] + x] = 0;               // U值填0
                frame->data[2][y * frame->linesize[2] + x] = 0;               // V值填0
            }
        }
        frame->pts = index++;                           // 时间戳递增
        output_video(frame);                            // 给视频帧编码,并写入压缩后的视频包
    }
    av_log(NULL, AV_LOG_INFO, "Success write yuv video.\n");

接着执行下面的编译命令:

     gcc writeyuv.c -o writeyuv -I/usr/local/ffmpeg/include -L/usr/local/ffmpeg/lib
-lavformat -lavdevice -lavfilter -lavcodec -lavutil -lswscale -lswresample -lpostproc -lm

编译完成后,执行以下命令启动测试程序:

    ./writeyuv

程序运行完毕,发现控制台输出以下日志信息,说明生成了Y、U、V三通道均为0的视频文件。

    Success open output_file output_writeyuv.mp4.
    Success write yuv video.

最后打开影音播放器可以正常观看output_writeyuv.mp4,并且视频画面呈现一片油绿色,如图4-3所示,表明上述代码正确实现了向视频文件输出Y、U、V全0画面的功能。

图4-3 Y、U、V三通道全为0时的视频画面

如此看来,难怪有的视频存在油绿色的斑块,原来它的局部YUV数据已经损坏了。

4.1.2 把视频帧保存为YUV文件

把YUV图像数据保存为文件的话,它的存储格式有两种,分别是平面模式(Planar Mode)和交错模式(Packed Mode,直译为紧密模式、拥挤模式)。

对于视频帧来说,平面模式把各像素的YUV数值分类存放,也就是先连续存储所有像素的Y值,然后存储所有像素的U值,最后存储所有像素的V值。具体而言,首先将所有像素的Y值从左往右、从上到下依次排列;等到Y值都排完了,再把所有像素的U值从左往右、从上到下依次排列;等到U值都排完了,再把所有像素的V值从左往右、从上到下依次排列。假设某帧画面宽度为6、高度为4,那么采取YUV420平面模式的话,YUV文件的存储内容为YYYYYYYYYYYYYYYYYYYYYYYYUUUUUUVVVVVV(24个Y加6个U再加6个V),这里的Y代表Y分量的数值,U代表U分量的数值,V代表V分量的数值。

至于另一种交错模式,则是把各像素的YUV数值交错存放,也就是交替存储每个像素的YUV分量数值,且所有像素按照从左往右、从上到下的顺序依次存储。假设某帧画面宽度为6、高度为4,那么采取YUYV420交错模式的话,YUV文件的存储内容为YUYYUYYUYYVYYVYYVYYUYYUYYUYYVYYVYYVY(奇数行的每两个像素为YUY,偶数行的每两个像素为YVY,然后奇偶各行依次连接,总共24个Y、6个U和6个V),这里的Y代表Y分量的数值,U代表U分量的数值,V代表V分量的数值。

在枚举类型AVPixelFormat的定义源码中,使用AV_PIX_FMT_YUV420P表示YUV420平面模式,使用AV_PIX_FMT_YUYV422表示YUYV422交错模式。其中代号末尾有P的就表示平面模式,代号末尾没有P的就表示交错模式。

考虑到大多数视频的像素格式采用AV_PIX_FMT_YUV420P,故而可将视频帧按照YUV420平面模式保存为YUV文件。此时AVFrame结构的data数组分别保存YUV三通道的数据,其中data[0]存放Y分量(灰度数值),data[1]存放U分量(色度数值的蓝色投影),data[2]存放V分量(色度数值的红色投影)。下面是把原始的视频帧转存为YUV文件的FFmpeg代码片段(完整代码见chapter04/saveyuv.c)。

    // 把视频帧保存为YUV图像。save_index表示要把第几个视频帧保存为图片
    int save_yuv_file(AVFrame *frame, int save_index) {
       // 视频帧的format字段为AVPixelFormat枚举类型,为0时表示AV_PIX_FMT_YUV420P
       av_log(NULL, AV_LOG_INFO, "format = %d, width = %d, height = %d\n",
                                        frame->format, frame->width, frame->height);
       char yuv_name[20] = { 0 };
       sprintf(yuv_name, "output_%03d.yuv", save_index);
       av_log(NULL, AV_LOG_INFO, "target image file is %s\n", yuv_name);
       FILE *fp = fopen(yuv_name, "wb");  // 以写方式打开文件
       if (!fp) {
           av_log(NULL, AV_LOG_ERROR, "open file %s fail.\n", yuv_name);
           return -1;
       }
       // 把YUV数据依次写入文件(按照YUV420P格式分解视频帧数据)
       int i = 0;
       while (i++ < frame->height) {  // 写入Y分量(灰度数值)
           fwrite(frame->data[0] + frame->linesize[0] * i, 1, frame->width, fp);
       }
       i = 0;
       while (i++ < frame->height / 2) {  // 写入U分量(色度数值的蓝色投影)
           fwrite(frame->data[1] + frame->linesize[1] * i, 1, frame->width / 2, fp);
       }
       i = 0;
       while (i++ < frame->height / 2) {  // 写入V分量(色度数值的红色投影)
           fwrite(frame->data[2] + frame->linesize[2] * i, 1, frame->width / 2, fp);
       }
       fclose(fp);  // 关闭文件
       return 0;
    }

上述代码的文件转存函数save_yuv_file要在对数据包解码之后调用,注意输入参数save_index指定了把第几个视频帧保存为图片。下面是对视频帧解码的FFmpeg代码例子。

    int packet_index = -1;  // 数据包的索引序号
    // 对视频帧解码。save_index表示要把第几个视频帧保存为图片
    int decode_video(AVPacket *packet, AVFrame *frame, int save_index) {
        // 把未解压的数据包发给解码器实例
        int ret = avcodec_send_packet(video_decode_ctx, packet);
        if (ret < 0) {
            av_log(NULL, AV_LOG_ERROR, "send packet occur error %d.\n", ret);
            return ret;
        }
        while (1) {
            // 从解码器实例获取还原后的数据帧
            ret = avcodec_receive_frame(video_decode_ctx, frame);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                break;
            } else if (ret < 0) {
                av_log(NULL, AV_LOG_ERROR, "decode frame occur error %d.\n", ret);
                break;
            }
            packet_index++;
            if (packet_index < save_index) {            // 还没找到对应序号的帧
                return AVERROR(EAGAIN);
            }
            save_yuv_file(frame, save_index);           // 把视频帧保存为YUV图像
            break;
        }
        return ret;
    }

接着执行下面的编译命令:

     gcc saveyuv.c -o saveyuv -I/usr/local/ffmpeg/include -L/usr/local/ffmpeg/lib -lavformat
-lavdevice -lavfilter -lavcodec -lavutil -lswscale -lswresample -lpostproc -lm

编译完成后,执行以下命令启动测试程序(默认保存首帧视频画面,即save_index为0)。

    ./saveyuv ../fuzhous.mp4

程序运行完毕,发现控制台输出以下日志信息,说明完成了将视频首帧保存为YUV文件的操作。

    Success open input_file ../fuzhous.mp4.
    format = 0, width = 480, height = 270
    target image file is output_000.yuv
    Success save 0_index frame as yuv file.

根据以上日志可以看到,视频帧的像素格式代号为0,查找枚举类型AVPixelFormat的源码可知,0对应AV_PIX_FMT_YUV420P,同时视频画面的宽度为480、高度为270,这个尺寸也是YUV图像的宽高。

运行下面的ffmpeg命令也可以把指定的视频帧保存为YUV文件。命令中的-vframes 1表示输出一帧。

    ffmpeg -i ../fuzhous.mp4 -ss 00:00:10 -vframes 1 ff_capture.yuv

4.1.3 YUV图像浏览工具

4.1.2节虽然把视频帧转为YUV文件,可是常用的看图软件无法识别YUV格式,怎么证明新生成的YUV文件是正确的呢?YUView正是为了浏览YUV文件而诞生的开源工具,其源码的GitHub主页是https://github.com/IENT/YUView,可执行程序的下载页面为https://github.com/IENT/YUView/releases,在此可下载YUView最新版本的安装包。比如2023年9月发布的YUView 2.14,在下载列表中单击对应系统版本的YUView压缩包,比如YUView-Win.zip。下载后的ZIP压缩包免安装,解压后即可正常使用YUView。

双击解压目录下的YUView.exe,打开YUView的初始界面,如图4-4所示。

图4-4 YUView的初始界面

依次选择YUView的顶部菜单File→Open File,在弹出的“文件”对话框中选择某个YUV文件,比如output_000.yuv,此时YUView的主界面如图4-5所示。

可见output_000.yuv打开之后空空如也,令人十分困惑。这是因为YUV文件缺少头部信息,无法读取宽高等规格参数,YUView读取不到这些参数也没辙。这时需要开发者手动设置YUV文件的规格信息,在YUView界面右边找到Width和Height两个输入框,在Width框填入YUV文件的宽度值(比如480),在Height框填入YUV文件的高度值(比如270)。再往下找到YUV Format下拉框,选择YUV 4:2:0 8-bit这项,表示YUV文件采用YUV420编码格式。不出意外的话,YUView的主界面变成了如图4-6所示的模样,可见成功渲染了YUV图像。

图4-5 YUView打开了一幅YUV图像

图4-6 填写宽高之后的YUView界面

不过YUView展示的图像色彩跟播放器的视频画面有少许差异,不仔细看还真发现不了,只有对视频画面截图再用制图软件取色,才可能发现RGB色彩存在小范围的数值差异。注意到YUView主界面右边有个Color Conversation下拉框,默认选择ITU-R.BT709这项,表示色彩转换采用BT709标准。虽然编码器实例的AVCodecContext结构拥有4个色度相关字段,分别是色度坐标color_primaries、色度转换特性color_trc、色度空间colorspace(AVCodecContext和AVFrame的结构内部叫作colorspace,AVCodecParameters结构内部叫作color_space)、色度范围color_range,但是从大部分视频获取这4个字段,发现它们的取值均为未定义,也就是不能确定这些视频究竟采用BT709标准还是其他标准。那么不妨更改YUView的Color Conversation这栏,将其改为选择ITU-R.BT601这项,改完之后YUView的图像浏览界面如图4-7所示。

图4-7 选择BT601之后的YUView界面

由图4-7可见,YUView也能正常渲染BT601标准的图像画面,尽管BT601标准与BT709标准在色彩上有些差异。但是这样仍然无法判定视频文件到底默认采用哪个色度标准,无论是BT601标准还是BT709标准,在展示景物色彩时都没有太失真,谁知道哪个才是视频默认的色度标准呢?确实对于细微的色彩偏差,肉眼分得不是很清楚,尤其是彩色的视频画面,光照强一点或者弱一点,屏幕高一点或者低一点,都可能影响人眼对于色彩的感知。下面做个实验,检查看看视频画面究竟采用BT601标准还是采用BT709标准。

前面在4.1.1节运行测试程序writeyuv生成了视频文件output_writeyuv.mp4,该视频的Y、U、V三通道全为0,其画面展示了油绿的色彩。下面执行以下命令启动测试程序,期望把output_writeyuv.mp4的油绿画面保存为YUV图像。

    ./saveyuv output_writeyuv.mp4 5

命令执行完毕后,使用YUView打开新生成的YUV图像output_005.yuv,填写Width和Height两个数值之后,Color Conversion一栏保持默认的ITU-R.BT709,此时YUView的图像浏览界面如图4-8所示。

图4-8 选择BT709之后的YUView界面所呈现的YUV全0图像

接着把Color Conversion一栏改为选择ITU-R.BT601这项,改完之后YUView的图像浏览界面如图4-9所示。

图4-9 选择BT601之后的YUView界面所呈现的YUV全0图像

继续把Color Conversion一栏改为选择ITU-R.BT2020这项,改完之后YUView的图像浏览界面如图4-10所示。

图4-10 选择BT2020之后的YUView界面所呈现的YUV全0图像

然后启动播放器打开output_writeyuv.mp4,实际的视频播放界面如图4-11所示。

图4-11 小尺寸视频在播放器呈现的YUV全0图像

对比图4-8~图4-11,发现采取BT601标准的图4-9所呈现的油绿色彩才完全符合图4-11的视频画面。

可是这能说明视频文件默认采用BT601标准的色度坐标吗?分明很多地方都说视频文件默认采用BT709标准呀,这是怎么回事?考虑到output_writeyuv.mp4的宽高仅为480×270,会不会视频尺寸太小了,使得画面颜色产生偏差了呢?那不妨把视频画面改大一些,打开writeyuv.c的源码,把以下两行:

    video_encode_ctx->width = 480;   // 视频画面的宽度
    video_encode_ctx->height = 270;  // 视频画面的高度

改为下面两行,也就是把视频宽高扩大为原来的三倍。

    video_encode_ctx->width = 1440;  // 视频画面的宽度
    video_encode_ctx->height = 810;  // 视频画面的高度

接着执行下面的编译命令:

     gcc writeyuv.c -o writeyuv -I/usr/local/ffmpeg/include -L/usr/local/ffmpeg/lib
-lavformat -lavdevice -lavfilter -lavcodec -lavutil -lswscale -lswresample -lpostproc -lm

编译完成后,执行以下命令启动测试程序,期望重新生成YUV均为0的大尺寸视频文件。

    ./writeyuv

程序运行完毕后,继续执行以下命令启动测试程序,期望把大尺寸视频的油绿画面保存为YUV图像。

    ./saveyuv output_writeyuv.mp4 6

命令执行完毕后,使用YUView打开新生成的YUV图像output_006.yuv,填写Width=1440和Height=810两个数值之后,Color Conversion一栏保持默认的ITU-R.BT709,此时YUView的图像浏览界面如图4-12所示。

图4-12 选择BT709之后的YUView界面所呈现的YUV全0图像

接着把Color Conversion一栏改为选择ITU-R.BT601这项,改完之后YUView的图像浏览界面如图4-13所示。

图4-13 选择BT601之后的YUView界面所呈现的YUV全0图像

继续把Color Conversion一栏改为选择ITU-R.BT2020这项,改完之后YUView的图像浏览界面如图4-14所示。

图4-14 选择BT2020之后的YUView界面所呈现的YUV全0图像

然后启动播放器打开新生成的output_writeyuv.mp4,实际的视频播放界面如图4-15所示。对比图4-12~图4-15,发现采取BT709标准的图4-12所呈现的墨绿色彩才完全符合图4-15的视频画面。

图4-15 大尺寸视频在播放器呈现的YUV全0图像

真是奇怪,同样处理逻辑的代码文件writeyuv.c,仅仅修改了输出视频的宽高,结果大尺寸视频的色彩却与小尺寸视频截然不同。经过多次试验,确定了以下播放器色彩显示规则:如果视频文件未指定色度坐标,那么当视频高度大于或等于578时,无论视频宽度为何,播放器都默认采用色度坐标BT709;当视频高度小于或等于576时,无论视频宽度为何,播放器都默认采用色度坐标BT601。之所以缺了中间的577,是因为视频高度只能为偶数,不能为奇数。

YUView除浏览YUV文件外,还能用来分析MP4视频。虽然使用StreamEye即可有效分析H.264格式的视频,但是该工具需要授权,未授权的试用版在功能上受限,只能分析视频开头一小段的视频帧。而YUView属于开源软件,视频分析功能完整,对初学者而言完全足够用了。依次选择YUView的顶部菜单File→Open File,在弹出的“文件”对话框中选择某个MP4文件,比如seas.mp4,此时YUView的主界面如图4-16所示。

图4-16 YUView打开视频的默认界面

注意界面下方的进度条多了两排刻度,且进度条右边的方框为0,表示当前位于第0帧。拖动进度条至第525帧,此时YUView的主界面如图4-17所示,可见预览画面变成了第525帧的视频截图。

不同版本的YUView必须搭配对应的FFmpeg解码库,才能正常解析MP4文件的视频画面。比如YUView 2.13搭配avcodec-59.dll,又如YUView 2.14搭配avcodec-60.dll,等等。如果使用YUView无法正常打开MP4文件,就要检查Windows的PATH环境变量指向FFmpeg的bin目录是否存放对应版本的avcodec-**.dll。

图4-17 YUView查看视频的第525帧画面 cgx5xCkoPjR2U8/bBIvAGeEhlVPZcmfMFG3+zn/mY4LVs0K+UlXJzf9CQt/cuhMm

点击中间区域
呼出菜单
上一章
目录
下一章
×