ffmpeg播放器9

107 阅读7分钟

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)配置内存布局,以便后续处理(如编码或转换)。以下是详细解释:

  1. 分配基础结构:

    • av_packet_alloc()av_frame_alloc() 是FFmpeg函数,分别分配AVPacket(存储压缩数据)和AVFrame(存储原始帧数据)的内存。
  2. 设置帧参数:

    • hframe.whframe.h 设为目标宽度(dw)和高度(dh)。
    • 初始缓冲区大小设为 dw * dh * 4,适用于ARGB格式(每个像素4字节)。
  3. 处理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)。
    • 数据指针:
      • data0 指向Y分量,data1指向U,data2指向V。
    • 行宽:
      • Y的行宽为dw,U/V的行宽为dw/2(因水平下采样)。
  4. 处理BGR24格式:

    • 类型与位深:设为PIX_FMT_BGR(对应AV_PIX_FMT_BGR24),每像素24位(3字节)。
    • 缓冲区大小:dw * dh * 3(每个像素B、G、R各1字节)。
    • 行宽:每行dw * 3字节(连续排列,无填充)。
  5. 用途:

    • 准备转换后的帧数据缓冲区,供后续操作(如编码、渲染)使用。
    • 根据目标格式(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);

通过设置线程的精准休眠策略,确保视频按预设帧率流畅播放。

未完待续