1背景
前一篇分析到了图像缩放以及像素格式转换,这一篇继续后续的分析。
2步骤
hffplayer open后续
2.1 av_frame
packet = av_packet_alloc();
frame = av_frame_alloc();
hframe.w = dw;
hframe.h = dh;
// ARGB
hframe.buf.resize(dw * dh * 4);
if (dst_pix_fmt == AV_PIX_FMT_YUV420P) {
hframe.type = PIX_FMT_IYUV;
hframe.bpp = 12;
int y_size = dw * dh;
hframe.buf.len = y_size * 3 / 2;
data[0] = (uint8_t*)hframe.buf.base;
data[1] = data[0] + y_size;
data[2] = data[1] + y_size / 4;
linesize[0] = dw;
linesize[1] = linesize[2] = dw / 2;
}
else {
dst_pix_fmt = AV_PIX_FMT_BGR24;
hframe.type = PIX_FMT_BGR;
hframe.bpp = 24;
hframe.buf.len = dw * dh * 3;
data[0] = (uint8_t*)hframe.buf.base;
linesize[0] = dw * 3;
}
压缩数据和原始帧数据
rgb ARGB每个像素占4个字节(a占一个,rgb分别各占一个)
三原色光模式又称RGB颜色模型或红绿蓝颜色模型,是一种加色模型,将红(Red)、绿(Green)、蓝(Blue)三原色的色光以不同的比例相加,便合成各种色彩的光
在典型使用上,数字视频的RGB不是全值域的。视频RGB使用有比例和偏移量的约定,即(16,16,16)是黑色,(235,235,235)是白色。
使用8位位深对RGB像素进行编码,每像素需要24位,这是当前主流的标准表示方法
- yuv 在彩色电视中,除了Y以外,还使用U和V来表示图像的色度(Chrominance或Chroma,C)。U和V也分别称为Cb、Cr,分别代表蓝色通道和红色通道与亮度的差值。 yuv=ycbcr
从历史的演变来说,YUV和Y′UV通常用来编码电视的模拟信号,Y′的上标符号一般表征经过了伽玛校正
- YUV444格式 YUV444表示4:4:4的YUV取样,水平每4像素中YUV各取4个,即每像素中YUV各取1个。所以每1×1像素Y占1字节,U占1字节,V占1字节,YUV444格式下平均每像素占(1+1+1)× 8bit/1pix = 24bpp(bpp为Bit Per Pixel,即每像素位数),即3字节。
YUV444格式的图像可以有两种存储格式:按像素存储和按平面存储。以2×2的图像为例,像素存储格式为Y1U1V1 Y2U2V2 Y3U3V3 Y4U4V4,平面存储格式为Y1Y2Y3Y4 U1U2U3U4 V1V2V3V4
-
YUV422格式 YUV422表示4:2:2的YUV取样,水平每4像素中Y取4个,u取样2个,v取样2个,也就是说 水平每2像素(即2×1的2像素)中Y取样2个,U取样1个,V取样1个,所以每2×1像素Y占2字节,U占1字节,V占1字节,YUV422格式下平均每像素占(2+1+1)×8bit/2pix = 16bpp。
-
YUV411格式 YUV411表示4:1:1的YUV取样,水平每4像素(即4×1的4像素)中Y取样4个,U取样1个,V取样1个,所以每4×1像素Y占4字节,U占1字节,V占1字节,YUV411格式下平均每像素占(4+1+1)×8bit/4pix = 12bpp。
-
YUV420格式 YUV420表示4:2:0的YUV取样,水平每2像素与垂直每2像素(即2×2的2像素)中Y取样4个,U取样1个,V取样1个,所以每2×2像素Y占4字节,U占1字节,V占1字节,YUV420格式下平均每像素占(4+1+1) × 8bit/4pix = 12bpp。
对于水平每4像素,Y取4个,U取2个,V取0个,这便是4:2:0的含义。但是,这个解释并不完整。在下一行取样时,应该是Y取4个,U取0个,V取2个,即4:0:2。所以说,这里的4:2:0其实是代表了4:2:0和4:0:2两种情况,它们在奇偶行交错出现。
YUVI420 这种图像格式又称为YUVI420,其实就是把邻近的4像素(2×2,即当前像素、右、下、右下)都用同一个U和V,而原先的Y不变。 正是基于这个原因,一般的编码器都要求原始图像的宽和高是偶数。
I420或IYUV uv使用了2×2图像区域中最左上角的U和V值,实际上,可以使用4个值中的任意一个,甚至也可以使用它们的平均值。但由于这4个值其实非常接近,并且人眼对它们也不敏感,因而在实际使用时一般都是用最简单的方法来随便选取一个。
YV12 还有一种YV12格式,与I420的区别是U和V平面的顺序相反。在安卓系统中,普遍使用NV12的像素格式,它与I420格式相比,Y平面没有区别,但U和V平面像素是交错存储的,是一种“半平面半交错”的存储方式。
总结下来就是 yuv420,每4像素取4个y,2个u以及0个v或者2个v以及0个u 每像素占比 (4+2)*8/4=12bpp 总长度 y+y/2(u或者v占 二分之一)
这段代码用于初始化视频帧的缓冲区,根据目标像素格式(YUV420P或BGR24)配置内存布局,以便后续处理(如编码或转换)。以下是详细解释:
-
分配基础结构:
av_packet_alloc()和av_frame_alloc()是FFmpeg函数,分别分配AVPacket(存储压缩数据)和AVFrame(存储原始帧数据)的内存。
-
设置帧参数:
hframe.w和hframe.h设为目标宽度(dw)和高度(dh)。- 初始缓冲区大小设为
dw * dh * 4,适用于ARGB格式(每个像素4字节)。
-
处理YUV420P格式:
- 类型与位深:设为
PIX_FMT_IYUV(FFmpeg中对应AV_PIX_FMT_YUV420P),平均每像素12位(YUV420的色度分量下采样)。 - 缓冲区计算:
- 亮度(Y)分量大小
y_size = dw * dh。 - 总缓冲区大小
y_size * 3/2(Y占全尺寸,U/V各占1/4)。
- 亮度(Y)分量大小
- 数据指针:
data0指向Y分量,data1指向U,data2指向V。
- 行宽:
- Y的行宽为
dw,U/V的行宽为dw/2(因水平下采样)。
- Y的行宽为
- 类型与位深:设为
-
处理BGR24格式:
- 类型与位深:设为
PIX_FMT_BGR(对应AV_PIX_FMT_BGR24),每像素24位(3字节)。 - 缓冲区大小:
dw * dh * 3(每个像素B、G、R各1字节)。 - 行宽:每行
dw * 3字节(连续排列,无填充)。
- 类型与位深:设为
-
用途:
- 准备转换后的帧数据缓冲区,供后续操作(如编码、渲染)使用。
- 根据目标格式(YUV420P用于视频编码,BGR24用于图像处理)分配内存,确保数据布局符合库函数(如
sws_scale)的要求。
关键点:根据像素格式正确划分内存区域,设置数据指针和行宽,确保兼容性。YUV420P需要三个平面,BGR24则是连续的三字节排列。
2.2 fps
// HVideoPlayer member vars
if (video_stream->avg_frame_rate.num && video_stream->avg_frame_rate.den) {
fps = video_stream->avg_frame_rate.num / video_stream->avg_frame_rate.den;
}
width = sw;
height = sh;
duration = 0;
start_time = 0;
eof = 0;
error = 0;
if (video_time_base_num && video_time_base_den) {
if (video_stream->duration > 0) {
duration = video_stream->duration / (double)video_time_base_den * video_time_base_num * 1000;
}
if (video_stream->start_time > 0) {
start_time = video_stream->start_time / (double)video_time_base_den * video_time_base_num * 1000;
}
}
hlogi("fps=%d duration=%lldms start_time=%lldms", fps, duration, start_time);
HThread::setSleepPolicy(HThread::SLEEP_UNTIL, 1000 / fps);
通过设置线程的精准休眠策略,确保视频按预设帧率流畅播放。