当前位置: 首页 > article >正文

在ffmpeg中,网络视频流h264为什么默认的转为YUV而不是其他格式

在做网络视频的时候,有些视频的编程概念,早点知道,早点弄清楚会少走很多的弯路。对应视频的转码,传输,一开始如果直接跟着代码跑的话,很容易觉得自己都明白了,但是为什么这样做,好像也讲出去个一二三来。

比如:在ffmpeg中,网络视频流h264为什么默认的转为YUV而不是其他格式?

从这个问题开始,我们慢慢的把一些概念理清楚,这样一来,编程的时候就容易理解了。

什么是h264

也被称为AVC(高级视频编码),是一种视频压缩标准。这是一种高效的视频编码方法,可以在保持高质量的同时,大幅度减少所需的带宽和存储空间。H264编码的视频可以在各种设备上播放,包括电视、电脑、智能手机等。在视频处理中,H264用于压缩视频数据,使其更便于传输和存储。

什么是YUV

这是一种颜色编码系统,常用于视频系统。YUV模型定义了一个颜色空间,其中Y表示亮度(灰度),而U和V表示色度(色彩和饱和度)。这种颜色编码方式的优点是可以更有效地压缩颜色信息,因为人眼对亮度的敏感度远高于色度。在视频处理中,YUV通常用于在保留视觉质量的同时减少所需的带宽或存储。

还有一个概念那就是我们经常提到的RGB格式。

什么是RGB

RGB是一种加色模型,其中R代表红色,G代表绿色,B代表蓝色。RGB模型采用红、绿、蓝三种颜色的光以不同的比例混合,以产生其他颜色。RGB模型主要用于显示设备,如电脑屏幕、电视和手机等,因为这些设备通过发射红、绿、蓝三种颜色的光来显示图像。RGB模型的优点是可以表示大范围的颜色,并且直观易于理解。

可以这么说RGB,YUV是不同时代的产物,一开始我们在设计黑白电视机的时候,只要有灰度就能显示图片,图片知识黑白的而已,到了彩色电视以后,又引入了U和V这两个向量,颜色的问题也就兼容了。

到了LED的时代,采用红、绿、蓝三种颜色的光以不同的比例混合,以产生其他颜色,也就是RGB。

为什么H264编码通常解码为YUV格式,而不是RGB格式的

H264编码的视频通常首先解码为YUV格式,而不是RGB格式,这主要是由于以下几个原因:

  1. 压缩效率:YUV格式的颜色编码更适合于视频压缩。在YUV格式中,亮度信息(Y)和色度信息(UV)是分开的,这使得在压缩过程中可以对色度信息进行更大程度的压缩,因为人眼对亮度的敏感度远高于色度。这就意味着,在相同的视频质量下,YUV格式的视频数据通常比RGB格式的视频数据更小。

  2. 兼容性:许多视频设备和系统,包括电视和DVD播放器等,都使用YUV格式。因此,解码为YUV格式可以确保视频在这些设备和系统上的兼容性。

  3. 色彩空间转换:虽然H264编码的视频可以被解码为RGB格式,但这通常需要额外的色彩空间转换步骤。相比之下,直接解码为YUV格式则更为简单和高效。

因此,虽然H264编码的视频可以被解码为RGB格式,但由于压缩效率、兼容性和处理效率的考虑,通常首先解码为YUV格式。

在视频的传输中用的是yuv还是h264

在视频的传输中,通常使用的是H264格式。这是因为H264是一种高效的视频压缩标准,它可以在保持高质量的同时,大幅度减少所需的带宽和存储空间。由于其高效的压缩性能,H264已经成为网络视频和流媒体的主流编码格式。

然而,这并不意味着YUV在视频传输中没有用处。实际上,YUV是一种颜色编码系统,用于在视频处理中有效地压缩颜色信息。在视频被编码为H264格式之前,通常会先将其转换为YUV格式。

因此,虽然在视频传输中主要使用H264格式,但YUV格式在视频处理和编码的过程中仍然起着重要的作用。

利用ffmpeg,把h264转为YUV

以下是一个使用FFmpeg库的基本示例,它展示了如何打开一个H.264格式的视频文件,读取每一帧,并将其转换为YUV格式:

extern "C" {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
}

int main() {
    // 注册所有的编解码器和格式
    av_register_all();

    // 打开视频文件
    AVFormatContext* formatContext = NULL;
    if (avformat_open_input(&formatContext, "a.h264", NULL, NULL) < 0) {
        // 错误处理...
    }

    // 查找视频流
    if (avformat_find_stream_info(formatContext, NULL) < 0) {
        // 错误处理...
    }
    int videoStreamIndex = -1;
    for (int i = 0; i < formatContext->nb_streams; i++) {
        if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStreamIndex = i;
            break;
        }
    }
    if (videoStreamIndex == -1) {
        // 错误处理...
    }

    // 找到并打开解码器
    AVCodec* codec = avcodec_find_decoder(formatContext->streams[videoStreamIndex]->codecpar->codec_id);
    AVCodecContext* codecContext = avcodec_alloc_context3(codec);
    avcodec_parameters_to_context(codecContext, formatContext->streams[videoStreamIndex]->codecpar);
    if (avcodec_open2(codecContext, codec, NULL) < 0) {
        // 错误处理...
    }

    // 读取和解码视频帧
    AVPacket packet;
    AVFrame* frame = av_frame_alloc();
    while (av_read_frame(formatContext, &packet) >= 0) {
        if (packet.stream_index == videoStreamIndex) {
            if (avcodec_send_packet(codecContext, &packet) < 0) {
                // 错误处理...
            }
            while (avcodec_receive_frame(codecContext, frame) == 0) {
                // 此时,frame包含YUV数据
                // ... 处理frame的数据 ...
            }
        }
        av_packet_unref(&packet);
    }

    // 释放资源
    av_frame_free(&frame);
    avcodec_close(codecContext);
    avcodec_free_context(&codecContext);
    avformat_close_input(&formatContext);

    return 0;
}

在这个示例中,我们首先打开一个视频文件,并查找视频流。然后,我们找到并打开适当的解码器。接着,我们读取和解码视频帧,每当我们收到一个新的帧时,我们就处理它的YUV数据。最后,我们释放所有的资源。

利用SDL 把视频显示出来

在SDL2中,可以直接把YUV格式的数据显示出来,而且也比较容易,代码如下:

AVPacket packet;
	AVFrame* frame = av_frame_alloc();
	while (av_read_frame(pFormatCtx, &packet) >= 0) {
		if (packet.stream_index == videoindex) {
			if (avcodec_send_packet(codecContext, &packet) < 0) {
				// 错误处理...
			}
			while (avcodec_receive_frame(codecContext, frame) == 0) {
				// 此时,frame包含YUV数据

				//开始显示
				SDL_UpdateYUVTexture(sdlTexture, &sdlRect,
					frame->data[0], frame->linesize[0],
					frame->data[1], frame->linesize[1],
					frame->data[2], frame->linesize[2]);

				SDL_RenderClear(sdlRenderer);
				SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
				SDL_RenderPresent(sdlRenderer);
				//SDL End-----------------------
				//Delay 1000/60ms--假设每分钟60帧
				SDL_Delay(1000/60);
			}
			av_packet_unref(&packet);
		}
		
	}

显示的效果如下:
在这里插入图片描述
刚才我们说过,YUV其实可以不用UV通道,可以让他显示为黑白的视频。修改一下代码,把UV通道都赋值为128,就可以看到灰色的效果了。

代码修改如下:

				memset(frame->data[1], 128, frame->linesize[1] * frame->height / 2);
				memset(frame->data[2], 128, frame->linesize[2] * frame->height / 2);
				SDL_UpdateYUVTexture(sdlTexture, &sdlRect,
					frame->data[0], frame->linesize[0],
					frame->data[1], frame->linesize[1],
					frame->data[2], frame->linesize[2]);

在这里插入图片描述
所有的代码都已在git上。


http://www.kler.cn/a/106694.html

相关文章:

  • gitHub常用操作
  • 使用 Python 和 OpenCV 实现摄像头人脸检测并截图
  • 使用阿里云快速搭建 DataLight 平台
  • 高美GULMAY高压发生器维修X射线源维修CF160
  • 基于Spring Boot的在线性格测试系统设计与实现(源码+定制+开发)智能性格测试与用户个性分析平台、在线心理测评系统的开发、性格测试与个性数据管理系统
  • 前端学习八股资料CSS(二)
  • 工业4.0的安全挑战与解决方案
  • IDEA配置HTML和Thymeleaf热部署开发
  • yo!这里是进程间通信
  • Redis 的优势
  • mysql冷拷贝大表
  • (PyTorch)PyTorch中的常见运算(*、@、Mul、Matmul)
  • 如何使用SHC对Shell脚本进行封装和源码隐藏
  • 【C语言】memmove()函数(拷贝重叠内存块函数详解)
  • 基于ARM+FPGA+AD的多通道精密数据采集仪方案
  • 装饰者模式
  • R语言生物群落(生态)数据统计分析与绘图实践技术应用
  • 数组的最长递减子序列
  • Project Costs
  • 第四章 文件管理 十、文件系统的全局结构
  • 前端工程化面试题及答案【集合】
  • 网络通信 | 内网穿透
  • 机器视觉3D项目评估的基本要素及测量案例分析
  • Pandas数据导入和导出:CSV、Excel、MySQL、JSON
  • 大语言模型(LLM)综述(四):如何适应预训练后的大语言模型
  • QQ云端机器人登录系统php源码开心版