YUV和PCM文件格式和如何计算一帧数据大小

1 YUV文件格式

简述

YUV存储纯数据. 没有帧头信息;

向解码器(以ffmpeg为例)传递 分辨率色彩编码格式

解码器根据给定的参数计算每帧具体大小为多少

1920*1080 uyvy422 就可以知道 一帧约为 4 MB

每个像素2字节(uyvy422属于YUV4:2:2)

2x1920x1080 = 4147200 B = 3.955078125 MB ~= 4MB

存储亮度(Y)和色度(U和V)信息

1.1 YUV格式

YUV格式分为两大类: planar(平面) 和 packed(打包)

  • planar格式

    依次连续存储所有像素点的Y分量/U分量/V分量

    YY................YY
    UU................UU
    VV................VV
    

    类似于纵向读取单个像素点的YUV信息; 实际存储数据不是三维而是一维

    提取像素点的YUV数据通过固定的偏移量获取

  • packed格式,每个像素点的Y, U, V分量是连续存储的

    …待补充

1.2 YUV采样方式

不同的采样方式决定了 像素点 数据大小

采样方式的主流方式分为: YUV4:4:4 / YUV4:2:2 / YUV4:2:0

在这里插入图片描述

⚫️ 表示Y分量; ⚪️ 表示一组UV分量

  • YUV4:4:4

    每组UV分量被1个Y分量使用

    平均每个像素点的YUV占 8+8+8=24bits 3个字节

  • YUV4:2:2

    每组UV分量被2个Y分量共享

    平均每个像素点的YUV占 8+4+4=16bits 2个字节

  • YUV4:2:0

    每组UV分量被4个Y分量共享

    平均每个像素点的YUV占 8+2+2=12bits 1.5个字节


如何采样出每组UV被多个Y分量共享?

像素块(4x4) 以YUV4:2:0为例;

获取16个点的Y分量后; 通常UV分量的值通过相邻像素的色度平均或其他插值方法计算

在平面格式中的存在形式

# 实际数据是一维; 分量均为 8bit
# 不会出现字节对齐问题
Y Y Y Y
Y Y Y Y
Y Y Y Y
Y Y Y Y

U U
U U

V V
V V

1.3 YUV存储方式

这里的存储方式就是我们在编码中实际遇到的 色彩编码格式

1.3.1 UYVY格式

属于YUV422采样; 使用打包格式

以4x4像素块为例; 应该有16个Y分量和8组uv分量

# 实际数据是一维; 分量均为 8bit
U1 Y01 V1 Y02    U2 Y03 V2 Y04
U3 Y05 V3 Y06    U4 Y07 V4 Y08
U5 Y09 V5 Y10    U6 Y11 V6 Y12
U7 Y13 V7 Y14    U8 Y15 V8 Y16

UYVY格式通常用于视频编码和传输中,它以较高的色度采样率为基础,同时在相对较小的数据量下提供了良好的图像质量。

1.3.2 YUVY格式

属于YUV422采样; 使用打包格式

与UYVY的不同在于U和V分量的位置交换

以4x4像素块为例; 应该有16个Y分量和8组uv分量

# 实际数据是一维;分量均为 8 位
Y01 U1 Y02 V1    Y03 U2 Y04 V2
Y05 U3 Y06 V3    Y07 U4 Y08 V4
Y09 U5 Y10 V5    Y11 U6 Y12 V6
Y13 U7 Y14 V7    Y15 U8 Y16 V8

1.3.3 YUV422P格式

属于YUV422采样; 使用平面格式

以4x4像素块为例; 应该有16个Y分量和8组uv分量

# 实际数据是一维;分量均为 8 位
Y01 Y02 Y03 Y04
Y05 Y06 Y07 Y08
Y09 Y10 Y11 Y12
Y13 Y14 Y15 Y16

U1 U2 U3 U4 U5 U6 U7 U8

V1 V2 V3 V4 V5 V6 V7 V8

1.3.4 YV12格式

属于YUV420采样; 使用平面格式

以4x4像素块为例; 应该有16个Y分量和4组uv分量

Y01 Y02 Y03 Y04
Y05 Y06 Y07 Y08
Y09 Y10 Y11 Y12
Y13 Y14 Y15 Y16

V1 V2 V3 V4

U1 U2 U3 U4

1.3.5 YU12格式

属于YUV420采样; 使用平面格式

以4x4像素块为例; 应该有16个Y分量和4组uv分量

Y01 Y02 Y03 Y04
Y05 Y06 Y07 Y08
Y09 Y10 Y11 Y12
Y13 Y14 Y15 Y16

U1 U2 U3 U4

V1 V2 V3 V4

1.3.6 NV12格式

属于YUV420采样; 使用平面格式(类似)

U和V分量被交错存储在同一平面中

以4x4像素块为例; 应该有16个Y分量和4组uv分量

# 实际数据是一维;分量均为 8 位
# Y分量
Y01 Y02 Y03 Y04
Y05 Y06 Y07 Y08
Y09 Y10 Y11 Y12
Y13 Y14 Y15 Y16

# UV分量(交错存储)
U1 V1 U2 V2 U3 V3 U4 V4

1.3.7 NV21格式

# 实际数据是一维;分量均为 8 位
# Y分量
Y01 Y02 Y03 Y04
Y05 Y06 Y07 Y08
Y09 Y10 Y11 Y12
Y13 Y14 Y15 Y16

# UV分量(交错存储,顺序相反)
V1 U1 V2 U2 V3 U3 V4 U4

2 PCM文件格式

PCM文件有多种格式(线性PCM/对数PCM/差分PCM)

计算pcm一帧数据大小 只需要知道采样率 位深度 通道数

计算公式: 一帧大小(字节) = (采样率 × 位深度 × 通道数) / 8

例: 采样率: 48000Hz; 位每采样: 32; 通道数:1

一帧大小为 187.5 KB

48000 x 32 / (8 x 1024) == 187.5KB

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YUVPCM数据编码成视频和音频文件需要使用FFmpeg库,具体的实现步骤如下: 1. 初始化FFmpeg库 在使用FFmpeg库之前,需要先进行初始化。使用av_register_all函数可以注册FFmpeg库中的所有编解码器、格式器和协议等。 ``` av_register_all(); ``` 2. 打开输出文件 使用avformat_alloc_output_context2和avio_open2函数打开输出文件,创建AVFormatContext结构体并分配内存,将输出文件与该结构体关联。 ``` AVFormatContext *out_ctx = NULL; int ret = avformat_alloc_output_context2(&out_ctx, NULL, NULL, output_file); if (ret < 0) { // 创建AVFormatContext失败 return; } if (!(out_ctx->oformat->flags & AVFMT_NOFILE)) { ret = avio_open2(&out_ctx->pb, output_file, AVIO_FLAG_WRITE, NULL, NULL); if (ret < 0) { // 打开输出文件失败 return; } } ``` 3. 创建音视频流 使用avformat_new_stream函数创建音视频流,并设置音视频流的相关参数,如编码器、率、码率、采样率等。 ``` AVStream *video_stream = avformat_new_stream(out_ctx, NULL); if (video_stream == NULL) { // 创建视频流失败 return; } AVCodecParameters *codecpar = video_stream->codecpar; codecpar->codec_type = AVMEDIA_TYPE_VIDEO; codecpar->width = width; codecpar->height = height; codecpar->format = AV_PIX_FMT_YUV420P; codecpar->codec_id = AV_CODEC_ID_H264; codecpar->bit_rate = bit_rate; codecpar->framerate = {fps, 1}; AVStream *audio_stream = avformat_new_stream(out_ctx, NULL); if (audio_stream == NULL) { // 创建音频流失败 return; } codecpar = audio_stream->codecpar; codecpar->codec_type = AVMEDIA_TYPE_AUDIO; codecpar->sample_rate = sample_rate; codecpar->format = AV_SAMPLE_FMT_S16; codecpar->channels = channels; codecpar->channel_layout = av_get_default_channel_layout(channels); codecpar->codec_id = AV_CODEC_ID_AAC; codecpar->bit_rate = bit_rate; ``` 4. 打开视频和音频编码器 使用avcodec_find_encoder函数查找视频和音频编码器,并使用avcodec_open2打开编码器。 ``` AVCodec *video_codec = avcodec_find_encoder(video_stream->codecpar->codec_id); if (video_codec == NULL) { // 查找视频编码器失败 return; } AVCodecContext *video_cctx = avcodec_alloc_context3(video_codec); if (video_cctx == NULL) { // 创建视频编码器上下文失败 return; } ret = avcodec_open2(video_cctx, video_codec, NULL); if (ret < 0) { // 打开视频编码器失败 return; } AVCodec *audio_codec = avcodec_find_encoder(audio_stream->codecpar->codec_id); if (audio_codec == NULL) { // 查找音频编码器失败 return; } AVCodecContext *audio_cctx = avcodec_alloc_context3(audio_codec); if (audio_cctx == NULL) { // 创建音频编码器上下文失败 return; } ret = avcodec_open2(audio_cctx, audio_codec, NULL); if (ret < 0) { // 打开音频编码器失败 return; } ``` 5. 写入视频和音频数据 使用av_frame_alloc函数创建AVFrame结构体,填充YUVPCM数据,并使用avcodec_send_frame和avcodec_receive_packet函数将数据编码成视频和音频包,最后使用av_write_frame函数将包写入输出文件。 ``` AVFrame *video_frame = av_frame_alloc(); // 填充YUV数据到video_frame中 AVPacket *video_packet = av_packet_alloc(); ret = avcodec_send_frame(video_cctx, video_frame); if (ret < 0) { // 向视频编码器发送数据失败 return; } while (ret >= 0) { ret = avcodec_receive_packet(video_cctx, video_packet); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } if (ret < 0) { // 从视频编码器接收数据失败 return; } av_packet_rescale_ts(video_packet, video_cctx->time_base, video_stream->time_base); video_packet->stream_index = video_stream->index; ret = av_write_frame(out_ctx, video_packet); if (ret < 0) { // 写入视频数据失败 return; } } AVFrame *audio_frame = av_frame_alloc(); // 填充PCM数据到audio_frame中 AVPacket *audio_packet = av_packet_alloc(); ret = avcodec_send_frame(audio_cctx, audio_frame); if (ret < 0) { // 向音频编码器发送数据失败 return; } while (ret >= 0) { ret = avcodec_receive_packet(audio_cctx, audio_packet); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } if (ret < 0) { // 从音频编码器接收数据失败 return; } av_packet_rescale_ts(audio_packet, audio_cctx->time_base, audio_stream->time_base); audio_packet->stream_index = audio_stream->index; ret = av_write_frame(out_ctx, audio_packet); if (ret < 0) { // 写入音频数据失败 return; } } ``` 6. 关闭编码器和输出文件 使用av_write_trailer、avcodec_free_context和avformat_free_context函数释放资源并关闭编码器和输出文件。 ``` av_write_trailer(out_ctx); avcodec_free_context(&video_cctx); avcodec_free_context(&audio_cctx); avformat_free_context(out_ctx); ``` 以上是将YUVPCM数据编码成视频和音频文件的基本流程,需要注意的是各项参数的设置和数据的填充。如果需要进行更详细的配置和处理,可以参考FFmpeg库的官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值