MPP音视频总结

基础篇

1.常用图像格式介绍

常用图像像素格式 RGB 和 YUV。

1.1RGB

RGB分类通常指的是将图像或颜色按照RGB(红、绿、蓝)颜色空间进行分组或分类。RGB图像格式通常包括RGB24(RGB888)、RGB32、RGBA、RGB565等。

RGB24是一种常用的图像颜色格式,表示每个像素使用24位(8位红、8位绿、8位蓝)来存储颜色信息。

RGB32是在RGB24的基础上增加了一个8位的alpha通道,用于表示透明度。每个像素总共占用32位。

RGB565使用16位表示颜色,其中5位用于红色,6位用于绿色,5位用于蓝色,适用于减少内存占用的场景

1.2YUV

YUV 图像是指将亮度参量 Y 和色度参量 U/V 分开表示的像素格式,主要用于优化彩色视频信号的传输。YUV 相比于 RGB 格式最大的好处是可以做到在保持图像质量降低不明显的前提下,减小文件大小。YUV 格式之所以能够做到,是因为进行了采样操作。主流的采样方式有三种:YUV 4:4:4 (YUV444),YUV 4:2:2 (YUV422),YUV 4:2:0(YUV420)。

若以以黑点表示采样该像素点的 Y 分量,以空心圆圈表示采用该像素点的 UV 分量,则这三种采样方式如下:

YUV 存储格式

YUV 存储可以分为两种:packed(打包)和 planar(平面);

  • packed:Y、U、V 分量穿插着排列,三个分量存在一个 Byte 型数组里;

  • planar:Y、U、V 分量分别存在三个 Byte 型数组中;

常见的像素格式

YUV422:YUYV、YVYU、UYVY、VYUY

这四种格式每一种又可以分为 2 类(packed和planar),以 YUYV 为例,一个 6*4 的图像的存储方式如下:

bash 复制代码
	Y Y Y Y Y Y                   
	Y Y Y Y Y Y                  
	Y Y Y Y Y Y                   
	Y Y Y Y Y Y                    
	U U U U U U                  Y U Y V Y U Y V Y U Y V
	U U U U U U                  Y U Y V Y U Y V Y U Y V
    V V V V V V                  Y U Y V Y U Y V Y U Y V
	V V V V V V                  Y U Y V Y U Y V Y U Y V
	- Planar -                          - Packed - 
YUV420:I420(YU12)、YV12、NV12、NV21
  • YUV420p: I420、YV12
  • YUV420sp: NV12、NV21

同样,对于一个6*4的图像,这四种像素格式的存储方式如下:

bash 复制代码
	Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
	Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
	Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
	Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
	U U U U U U      V V V V V V      U V U V U V      V U V U V U
	V V V V V V      U U U U U U      U V U V U V      V U V U V U
     - I420 -          - YV12 -         - NV12 -         - NV21 -
  • I420、YV12 三个分量均为平面格式,即分别存在三个数组中;
  • NV12、NV21 的存储格式为 Y 平面,UV 打包,即 Y 信息存储在一个数组中,UV 信息存储在一个另一个数组中。

2.链表

2.1普通链表

2.1.1链表的难点

链表的难点 ---> 指针。

口诀:

  • 变量变量,能变,就是能读能写,必定在内存里
  • 指针指针,保存的是地址,32处理器中地址都是32位的,无论是什么类型的指针变量,都是4字节

2.2改进链表

2.3常见双向链表宏
cpp 复制代码
struct list_head
{
  struct list_head *next, *prev;
};
  • next:指向链表中下一个节点的指针。
  • prev:指向链表中上一个节点的指针。
  • 这个结构体用于表示链表的每个节点,允许在链表中双向遍历。
初始化链表头部
cpp 复制代码
#define LIST_HEAD_INIT(name) { &(name), &(name) }
  • 这是一个宏,用于初始化链表头部。
  • 它将链表头部的 nextprev 指针都指向自身,表示这是一个空链表。
声明和初始化一个新的链表头部
cpp 复制代码
#define LIST_HEAD(name) \
    struct list_head name = LIST_HEAD_INIT(name)
  • 这个宏用于声明和初始化一个新的链表头部。
  • name 是用户指定的链表头的名称。该宏创建一个 struct list_head 类型的变量,并使用 LIST_HEAD_INIT 进行初始化。
初始化已定义的链表头部
cpp 复制代码
#define INIT_LIST_HEAD(ptr) do { \
    (ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
  • 这个宏用于初始化已定义的链表头部。
  • ptr 是指向链表头的指针,宏将其 nextprev 指针都设置为指向自身,以表示它是一个空链表。
在链表中增加链表项
cpp 复制代码
static __inline__ void __list_add(struct list_head *new,
                                   struct list_head *prev,
                                   struct list_head *next)
{
    next->prev = new;   // 将新节点的下一个节点的 prev 指针指向新节点
    new->next = next;   // 新节点的 next 指向原本的下一个节点
    new->prev = prev;   // 新节点的 prev 指向前一个节点
    prev->next = new;   // 前一个节点的 next 指向新节点
}
  • 参数

    • new:要添加的新节点。
    • prev:新节点要插入的位置之前的节点。
    • next:新节点要插入的位置之后的节点。
  • 功能 :将新节点 new 插入到链表中,位置在 prevnext 之间。函数首先更新 next 节点的 prev 指针,使其指向新节点,然后更新新节点的 nextprev 指针,最后更新 prev 节点的 next 指针,以完成插入操作。

在链表中替换列表项
cpp 复制代码
static inline void list_replace(struct list_head *old,
                                 struct list_head *new)
{
    new->next = old->next;               // 新节点的 next 指向旧节点的下一个节点
    new->next->prev = new;               // 旧节点下一个节点的 prev 指向新节点
    new->prev = old->prev;               // 新节点的 prev 指向旧节点的前一个节点
    new->prev->next = new;               // 旧节点的前一个节点的 next 指向新节点
}
  • 参数

    • old:要被替换的旧节点。
    • new:新的节点,将替换旧节点。
  • 功能 :替换链表中的旧节点 old 为新节点 new。函数更新新节点的 nextprev 指针,并相应地调整链表中相邻节点的指针,使链表保持连贯。

VO篇

1.如何读取一张YUV420格式的图片到内存(通常用于保存固定背景图)

  1. 根据显示图片文件名获取图片长和宽的大小(调用strstr函数获取文件名中特定字段的位置)
  2. 由图片长款大小,分别计算亮度、色度、图片字节大小(亮度大小=图片分辨率,色度大小=(图片分辨率>>4)*2,图片大小=二者相加)
  3. 创建视频缓存池VB(默认D1分辨率704*576,ipc子码流通常为D1大小)
  4. 从创建好的缓存池中,获取缓存块(大小为要显示图片的大小)
  5. 一个帧缓存块所在缓存池的 ID
  6. 获取缓存块的物理地址,并转化对应的虚拟地址。
  7. 到此准备工作结束
  8. 图像帧结构体进行赋值,(缓存池ID、图像数据物理地址、图像数据虚拟地址、宽、高、视频图像像素格式-yuv420、图像数据跨距{图像宽大小}、帧场模式)
  9. 只读模式fopen打开要显示图像文件,fread读取图像数据到虚拟地址中

1.如何通过VO的设备ID来获取VO设备的亮度、色度、对比度、锐度等参数

  1. 遍历VO设备的链表,从链表节点 pos 中获取对应的 VoDevEntry 结构体指针
  2. 判断找到的VO链表项的voID是否和传入的设备ID相等
  3. 如果相等,获取VO设备结构体中的亮度、色度、对比度等参数
cpp 复制代码
static int GetVoDevData(int voId, VoDevStruct **devStruct)
{
    int result = ERR_DEVID;
    struct list_head *pos = NULL;
    struct list_head *save = NULL;

    //确保 voId 和 devStruct 参数的有效性
    VO_CHECK_VOID(voId);
    CHECK_POINTER(devStruct);

    //使用 list_for_each_safe 宏安全遍历链表 voDevs
    list_for_each_safe(pos, save, &voDevs) 
    {
        //通过链表节点获取对应的 VoDevStruct 结构体指针
        (*devStruct) = list_entry(pos, VoDevStruct , node);
        //检查 (*devStruct) 是否为 NULL,如果是,记录错误并返回
        if (NULL == (*devStruct))
        {
            LogErr("find vo info err. voId = %d\n", voId);
            result = ERR_INPOITERNULL;
            goto EXIT_0;
        }

        //如果找到匹配的 voId,则设置 result 为成功状态 ERR_OK,并跳出循环
        if ((*devStruct)->voId == voId)
        {
            result = ERR_OK;
            break;
        }
    }
    
EXIT_0:

    return result;
}
相关推荐
yaosheng_VALVE9 小时前
探究全金属硬密封蝶阀的奥秘-耀圣控制
运维·eclipse·自动化·pyqt·1024程序员节
dami_king9 小时前
SSH特性|组成|SSH是什么?
运维·ssh·1024程序员节
一个通信老学姐5 天前
专业125+总分400+南京理工大学818考研经验南理工电子信息与通信工程,真题,大纲,参考书。
考研·信息与通信·信号处理·1024程序员节
sheng12345678rui5 天前
mfc140.dll文件缺失的修复方法分享,全面分析mfc140.dll的几种解决方法
游戏·电脑·dll文件·dll修复工具·1024程序员节
huipeng9266 天前
第十章 类和对象(二)
java·开发语言·学习·1024程序员节
earthzhang20216 天前
《深入浅出HTTPS》读书笔记(19):密钥
开发语言·网络协议·算法·https·1024程序员节
在狂风暴雨中奔跑7 天前
Android+FFmpeg+x264重编码压缩你的视频
音视频开发
爱吃生蚝的于勒7 天前
计算机基础 原码反码补码问题
经验分享·笔记·计算机网络·其他·1024程序员节
earthzhang20217 天前
《深入浅出HTTPS》读书笔记(20):口令和PEB算法
开发语言·网络协议·算法·https·1024程序员节
一个通信老学姐8 天前
专业140+总分410+浙江大学842信号系统与数字电路考研经验浙大电子信息与通信工程,真题,大纲,参考书。
考研·信息与通信·信号处理·1024程序员节