目录
1.TCP UDP IP协议放一起看看(这是一切传输层协议的鼻祖)
10.图片硬件存取及编解码流程(以hisi为例吧,地平线也是类似接口不一样而已根据芯片手册决定)
八、send stream to vdec(选定需要解码的文件路径和文件名)
指针模块
普通指针
类型 * 变量
类型有:double,int, long,float,各种类容器等都可以作为指针类型
例如:int*p = NULL;
指针数组
指针数组可以说成是”指针的数组”,由于*p没有括号,首先这个变量是一个数组,其次,”指针p”修饰这个数组,意思是说这个数组的所有元素都是指针类型,而指针所占的字节数和其类型无关,只与系统有关,在32位系统下,任何类型的指针占据4个字节,在64位系统下,任何类型的指针占据8个字节。
一维数组
int* p[n];
二维数组
int* p[n][m];
多维数组
int*p[a][b][c]
数组指针
由于()的优先级高,首先说明p是一个int类型指针,它是指向一个整型(int)的一维数组,这个一维数组的长度是n,也可以说是总共有n个格子。数组指针也称指向一维数组的指针,亦称行指针。数组指针也可以称为“数组的指针”,首先这个变量是一个指针,其次,”数组”修饰这个指针,意思是说这个指针存放着一个数组的首地址,或者说这个指针指向一个数组的首地址。
一维数组
int(*p)[n];
二维数组
int(*p)[n][m];
多维数组
当成一个立体的模块就行了相当于一本书由很多页组成的即多为就是由很多个二维数组堆积而成
int(*p)[a][b][c]
函数指针
跟数组指针类似。只不过之函数的入口地址,本质差不多
int (*fiction)(int a,int b);
指针函数
跟指针数组类似。函数的指针返回值是个函数
int *fiction(int a,int b);
函数模块
内联函数
用于一个多次调用且简单的模块可以封装成内联函数,内联函数和普通函数相比可以加快程序的运行速度,在编译的时候内联函数可以直接镶嵌到目标代码中,有函数特性,在代码展开时,会做安全检查或自动类型转换
inline void fun();

回调函数
#include <iostream>
using namespace std;
//函数指针取别名
typedef void (*StreamCallbackFunc)(int streamType, int chn);
void add (int streamType,int chn)
{
return streamType + chn;
}
void callbackFunc(StreamCallbackFunc callback)
{
callback(1,2);
}
int main()
{
callback(add);
return 0;
}
宏函数
替换在预编译时进行,称作宏展开。宏函数归根结底还是一种宏定义,比普通的宏定义要复杂一些,通常是一组实现特定功能的语句的组合, 跟宏定义本质没啥区别,也不具有函数特性,只是在编译预处理阶段将程序中的有关字符串替换成宏体。
#define FUN(a,b){cout<<(a > b ? a : b)<<endl;}
shell模块
相关命令
进程相关:
ps(查看进程列表具体参数可以通过help查看,一般开发板系统都是被裁剪了的命令不一定全)配合grep用可具体查找 例如:ps -a | grep " 具体要查的或者模糊要查的某些进程"。
pstree每一个进程都是由其父进程创建的。此命令以可视化方式显示进程,通过显示进程的树状图来展示进程间关系。如果指定了pid了,那么树的根是该pid,不然将会是init(pid: 1)。
top 显示进程的数据包括PID、进程属主、优先级、%CPU、%memory等

htop
htop与top很类似,但是htop是交互式的文本模式的进程查看器。它通过文字图形化地显示每一个进程的CPU和内存使用量、swap使用量。使用上下光标键选择进程,F7和F8改变优先级,F9杀死进程。Htop不是系统默认安装的,所以需要额外安装。
nice 改变进程优先级一般不用
nice <优先值> <进程名> - 通过给定的优先值启动一个程序 renice renice命令类似nice命令。使用这个命令可以改变正在运行的进程优先值。注意,用户只能改变属于他们自己的进程的优先值。用法:renice -n -p - 改变指定进程的优先值。
kill
杀死进程 killall -9 进程名/pid 或者kill -9 进程名/pid。
ulimit
可以管理重度使用和存在性能问题的系统。限制资源大小可以确保重要进程持续运行,其他进程不会占用过多资源。ulimit -a。具体参数用ulimit man查看。

w
w 提供当前登录的用户及其正在执行的进程的信息。显示信息头包含信息,如当前时间、系统运行时长、登录用户总数、过去的1,5,15分钟内的负载均衡数。

pgrep
pgrep的意思是"进程号全局正则匹配输出"。该命令扫描当前运行进程,然后按照命令匹配条件列出匹配结果到标准输出。对于通过名字检索进程号是很有用。pgrep -u mint sh
fg 和 bg
使用‘bg’命令可以将任务放在后台执行,而用‘fg’可以调到前台来使用。fg %进程id。
bg - 将程序放到后台运行。
ipcs
ipcs命令报告进程间通信设施状态。(共享内存,信号量和消息队列)用-p参数联合-m、-s或-q使用,可以获得相关的进程间通信的进程ID。

文本及目录相关:
cd、mkdir、touchu、pwd、ls、tree、cp、mv、rm、chown、chmod、
ln 硬链接与软连接
redlink 查看符号连接文件的内容 readlink [参数选项] [文件]
md5sum 计算和校验文件的MD5值 md5sum 文件名
find 查找目录下的文件 find [路径] [操作语句] [执行的动作]

内存及系统日志相关:
cat 文件名 | grep “具体内容”
grep -n "YuvQueMan* yuvQueHandle [2]" ./ -r 查看当前目录所有文件包含 YuvQueMan* yuvQueHandle [2]文件。
more,tail -f 文件名
以上是查看日志信息够用了就不过多介绍了。
内存相关
free、cat /proc/meminfo
hisi都是裁剪系统可以看手册
内存命令
mmz
cat /proc/media-mem
系统
cat /proc/meminfo
x3都是裁剪系统可以看手册
显示DDR
hrut_ddr -t all -p 1000
显示ion内存
cat /sys/kernel/debug/ion/heaps/ion_cma
内存分配
memstat
显示温度、cpu/bpu工作频率
hrut_somstatus
分区修复及重置命令
fdisk -l查看挂在分区
umount -a /dev/mmcblk2p1 取消分区所挂在的磁盘
mkfs.ext4 -F /dev/mmcblk2p1 修改分区格式
fsck.ext4 /dev/mmcblk2p1 修复分区
内存泄漏
yum -y install valgrind 工具安装
valgrind --tool=memcheck 进程名 -t
dmesg |tail -10
内存溢出(out of memory,OOM),当进程运行向系统申请内存时,系统没有更多的进程分配给该进程了,就会出现内存溢出。内存溢出后系统会杀掉系统中的一些进程来释放内存,通常OOM Killer杀死的都是占用内存较多的服务,直到内存够用为止,所以内存溢出的直观现象通常是某些服务异常或宕机。当发生内存溢出后可以通过dmesg命令或者通过/var/log/messages来快速确定。
cat /proc/sys/vm/panic_on_oom
echo 1 > /proc/sys/vm/panic_on_oom

panic_on_oom该参数的值共有三个选项:
0 是触发oom killer进行杀进程处理(/proc/sys/vm/oom_kill_allocating_task默认值为0,结束占用内存多的进程;如果设置为1,就杀死当前申请内存的进程)
2 是直接触发kernel panic(类似于Windows的蓝屏),此时系统开发人员可以连接进行debug,但是对其他人并没有什么用。更多希望系统快速重启(/proc/sys/kernel/panic设置多少秒后重启)
1 是根据不同情况会有不同处理。
/proc/sys/vm/overcommit_ratio就是系统最大可以分配内存的百分比。

函数及变量用法
makefile模块
数据结构与算法
什么是数据结构?
数据
1.数据不是单纯的数值,而是一个类似于集合的概念
2.数据元素是数据的基本单位,数据元素由若干个基本项组成(图书编号,作者,出版日期 一行)
3.数据项是数据元素由若干个数据项组成,数据项是数据的最小单位(数据项 书名 数据项 作者)
4.节点节点数据元素又叫节点
结构
1.数据之间的关系(逻辑和存储结构)。
2.逻辑结构(线性关系,层次关系,网状关系)
3.存储结构(顺序存储结构,链式存储结构,索引存储结构,散列存储结构)
总而言之数据结构就是按指定逻辑存储的一组数据。
为什么要用数据结构呢?
逻辑关系清楚方便操作一组数据的增删改查)
算法
什么是算法?
算法就是程序解决问题的办法。
算法与程序的关系
算法 + 数据结构 = 程序
算法的设计: 取决于选定的逻辑结构
算法的实现: 依赖于采用的存储结构 (数组还是链表)
算法的特性
(1)有穷性 算法的执行步骤是有限的
(2)确定性 算法的每一个步骤,无二义性 ,没有歧义
(3)可行性 算法能够在有限的时间内完成
(4)输入 一个算法可以有一个或多个输入
(5)输出 一个算法可以有一个或多个输出
算法的好坏
(1)消耗时间的多少 越少越好
(2)消耗内存的多少 越小越好
(3)程序的可读性 、维护、调试 、移植 好坏
计算方式根据时间复杂度计算
T(n) = O(f(n))
T(n) //问题规模的时间函数
n // 代表的是问题的规模 输入数据量的大小 举例 对一个int a[100];进行冒泡排序 100 === n
O //时间数量级
f(n) //算法中可执语句重复执行的次数 用问题规模n的某个函数f(n)来表达
线性结构线性表
顺序表 链表(单向链表 双向链表 单向循环链表 双向循环链表) 栈 队列都是线性结构
(1)逻辑结构:线性结构
(2)存储结构://有两种方式可以选择
1)顺序存储 --->数组
2)链式存储 --->链表
(3)线性表的特点
一对一,每个节点最多一个前驱和一个后继
首尾节点特殊:首节点无前驱 ,尾节点无后继
顺序表
逻辑结构:线性结构
存储结构:顺序存储结构 在内存当中存储是连续的 数组
#include <stdio.h>
#include <stdlib.h>
#define N 20
typedef struct
{
int data[N];
int last;//last代表的是数组中最后一个有效元素的下标
}seqlist_t;
//1.创建一个空的顺序表
seqlist_t *createEmptySeqList()//返回的是申请空间的首地址
{
//动态申请一个结构体空间
seqlist_t *p =(seqlist_t *)malloc(sizeof(seqlist_t));
if(NULL == p)
{
perror("p malloc failed");
return NULL;
}
//对last初始化
p->last = -1;//当前数组为空,无有效元素个数
return p;
}
//2.向顺序表的指定位置插入数据
int insertIntoSeqList(seqlist_t *p, int post,int data)//post第几个位置,data插入的数据
{
int i;
//1.进行容错判断
if(isFullSeqList(p) || post < 0 || post > p->last+1)
{
printf("insertIntoSeqList post failed!!\n");
return -1;
}
//2.将last ---- post位置所有数据向后移动一个位置
for(i = p->last; i >= post; i--)
{
p->data[i+1] = p->data[i];
}
//3.将数据放入到顺序表
p->data[post] = data;
//4.最后一个元素的下标 +1 ,因为插入了一个数据
p->last++;
return 0;
}
//3.遍历顺序表sequence 顺序 list 表
void showSeqList(seqlist_t *p)
{
int i;
for(i = 0; i <= p->last; i++)
{
printf("%d ",p->data[i]);
}
printf("\n");
}
//4.判断顺序表是否为满,满返回1 未满返回0
int isFullSeqList(seqlist_t *p)
{
// return p->last+1 == N ? 1 : 0;
return p->last+1 == N;//如果条件为真 1代表真 如果条件不成立0代表假
}
//5.删除顺序表中指定位置的数据post删除位置
int deletePostSeqList(seqlist_t *p, int post)
{
int i;
//1.容错判断
if(isEmptySeqList(p) || post < 0 || post > p->last)
{
printf("deletePostSeqList failed!!\n");
return -1;
}
//2.将从post+1 ---- last位置下标的所有数据整体向前移动一个位置
for(i = post+1; i <= p->last; i++)
{
p->data[i-1] = p->data[i];
}
//3,有效元素个数-1,所以最后一个元素的下标last--
p->last--;
return 0;
}
//6.判断顺序表是否为空
int isEmptySeqList(seqlist_t *p)
{
return p->last == -1;//如果last == 0 说明最后一个有效元素下标为0,元素个数n = last + 1
}
//7.清空顺序表
void clearSeqList(seqlist_t *p)
{
p->last = -1;
}
//8.修改指定位置的数据
int changePostSeqList(seqlist_t *p,int post,int data)//post被修改的位置,data修改成的数据
{
//1.容错判断
if(post < 0 || post > p->last)
{
printf("changePostSeqList post is failed!!\n");
return -1;
}
//2.修改数据
p->data[post] = data;
return 0;
}
//9.查找指定数据出现的位置
int searchDataSeqList(seqlist_t *p,int data)//data代表被查找的数据
{
int i;
for(i = 0; i <= p->last; i++)
{
if(p->data[i] == data)
{
return i;
}
}
return -1;//-1代表没有找到该数据
}
void combineAB(seqlist_t *pa,seqlist_t *pb)
{
int i;
for(i = 0 ; i <= pb->last; i++)//遍历pb表
{
if(searchDataSeqList(pa,pb->data[i]) == -1)//说明pa表中没有当前pb表中的这个元素
{
insertIntoSeqList(pa,pa->last+1,pb->data[i]);//将不重复的元素插入到pa表的尾
}
}
}
int main(int argc, const char *argv[])
{
seqlist_t *pa = createEmptySeqList();//创建一个空的顺序表
seqlist_t *pb = createEmptySeqList();//创建一个空的顺序表
insertIntoSeqList(pa,0,1);
insertIntoSeqList(pa,1,3);
insertIntoSeqList(pa,2,5);
insertIntoSeqList(pa,3,7);
showSeqList(pa);
insertIntoSeqList(pb,0,5);
insertIntoSeqList(pb,1,7);
insertIntoSeqList(pb,2,9);
insertIntoSeqList(pb,3,11);
showSeqList(pb);
combineAB(pa,pb);
showSeqList(pa);
return 0;
}
顺序表总结:
(1)顺序表在内存当中是连续存储的
(2)顺序表的长度是固定 #define N 20
(3)顺序表查找数据的时候方便的,插入和删除麻烦
链表
单向链表 双向链表 单向循环链表 双向循环链表
解决:长度固定的问题,插入和删除麻烦的问题
(1)逻辑结构: 线性结构
(2)存储结构: 链式存储
(3)操作: 增 删 改 查
单链表
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node_t
{
datatype data;//数据域
struct node_t *next;//指针域,指向自身结构体的指针
}link_node_t,*link_list_t;
//1.创建一个空的单向链表(有头单向链表)
link_node_t *createEmptyLinkList()
{
//思想:创建一个节点,作为链表的头结点
link_node_t *p = (link_node_t *)malloc(sizeof(link_node_t));
if(NULL == p)
{
perror("createEmptyLinkList malloc failed");
return NULL;
}
p->next = NULL;
return p;
}
//2.向单向链表的指定位置插入数据
////p保存链表的头指针 post 插入的位置 data插入的数据
int insertIntoPostLinkList(link_node_t *p,int post, datatype data)
{
int i;
link_node_t *pnew = NULL;//用来保存新创建的节点
//1.容错处理
if(post < 0 || post > lengthLinkList(p))
{
printf("insertIntoPostLinkList post failed!!\n");
return -1;//失败
}
//2.将头指针,指向插入位置的前一个节点
for(i = 0; i < post; i++)
p = p->next;
//3.创建一个新的节点,保存即将插入的数据
pnew = (link_node_t *)malloc(sizeof(link_node_t));
if(NULL == pnew)
{
perror("pnew malloc failed");
return -1;
}
pnew->data = data;//将新节点装上数据
pnew->next = NULL;
//4.将新节点插入到链表中,先连后面(pnew->next),再连前面(p->next)
pnew->next = p->next;//连后面
p->next = pnew;//连前面
return 0;
}
//3.求单向链表长度的函数
int lengthLinkList(link_node_t *p)
{
int len = 0;
while(p->next != NULL)
{
p = p->next;
len++;
}
return len;
}
//4.遍历单向链表
void showLinkList(link_node_t *p)
{
while(p->next != NULL)
{
p = p->next;
printf("%d ",p->data);
}
printf("\n----------------------\n");
}
//5.删除单向链表中指定位置的数据 post 代表的是删除的位置
int deletePostLinkList(link_node_t *p, int post)
{
int i;
link_node_t *pdel = NULL;
//1.容错处理
if(isEmptyLinkList(p) || post < 0 || post >= lengthLinkList(p))
{
printf("deletePostLinkList post is failed!!\n");
return -1;
}
//2.将头指针指向删除节点的前一个位置
for(i = 0; i < post; i++)
p = p->next;
//3.进行删除操作
//(1)定义一个指针pdel,指向被删除节点
pdel = p->next;
//(2)跨国被删除节点
p->next = pdel->next;
//(3)释放被删除节点
free(pdel);
pdel = NULL;
return 0;
}
//6.判断单向链表是否为空 1代表空 0代表非空
int isEmptyLinkList(link_node_t *p)
{
return p->next == NULL;//条件为真, C语言中真用1表达 假用0表达
}
//7.清空单向链表
void clearLinkList(link_node_t *p)
{
link_node_t *pdel = NULL;//pdel指向被删除节点
while(p->next != NULL)//注意头指针p没有被移动过
{
//(1)将pdel指向被删除节点即头节点下一个节点
pdel = p->next;//每次删除的都是头节点的下一个节点
//(2)跨过被删除节点
p->next = pdel->next;
//(3)释放被删除节点
free(pdel);
pdel = NULL;
}
}
//8.修改指定位置的数据 post 被修改的位置 data修改成的数据
int changePostLinkList(link_node_t *p, int post, datatype data)
{
int i;
//1.容错判断
if(post < 0 || post >= lengthLinkList(p))
{
printf("changePostLinkList post is failed!!\n");
return -1;
}
//2.将头指针移动到修改节点的位置
for(i = 0; i < post+1; i++)
{
p = p->next;
}
//3.修改数据
p->data = data;
return 0;
}
//9.查找指定数据出现的位置 data被查找的数据 //search 查找
int searchDataLinkList(link_node_t *p, datatype data)
{
int post = 0;//记录查找的位置
//遍历链表
while(p->next != NULL)
{
p = p->next;
if(p->data == data)
{
return post;
}
post++;
}
return -1;//说明数据不存在
}
//10.删除单向链表中出现的指定数据,data代表将单向链表中出现的所有data数据删除
int deleteDataLinkList(link_node_t *p, datatype data)
{
link_node_t *pdel = NULL;//用于指向被删除节点
//1.定义一个指针q,指向头节点的下一个节点,那么此时又可以将q看做无头链表的头指针
link_node_t *q = p->next;
//2.用q来遍历无头链表,将每一个节点与data做比较,如果相同就删除
while(q != NULL)
{
if(q->data == data)//说明需要删除q指向的节点
{
//(1)将pdel指向被删除节点
pdel = q;
//(2)跨过删除节点
p->next = pdel->next;
//(3)释放被删除节点
free(pdel);
pdel = NULL;
//(4)将q指向链表删除后头结点的下一个节点
q = p->next;
}
else
{
p = p->next;//将头指针向后移动一个位置
q = p->next;//始终保证p指向q的前一个节点
}
}
return 0;
}
//11.转置链表
//解题思想:
//(1) 将头节点与当前链表断开,断开前保存下头节点的下一个节点,保证后面链表能找得到,定义一个q保存头节点的下一个节点,断开后前面相当于一个空的链表,后面是一个无头的单向链表
//(2) 遍历无头链表的所有节点,将每一个节点当做新节点插入空链表头节点的下一个节点(每次插入的头节点的下一个节点位置)
void reverseLinkList(link_node_t *p)
{
link_node_t *temp = NULL;//用来临时保存q节点的下一个节点
//1.断开前,保存头节点的下一个节点的地址
link_node_t *q = p->next;//此时q相当于无头表的头指针
//2.断开链表
p->next = NULL;
//3.遍历无头链表
while(q != NULL)
{
temp = q->next;//提前将q的下一个节点保存起来
//先连后面,在连前面,将无头表的节点插入头结点的下一个位置
q->next = p->next;//执行完这样代码后,q无法再找到下一个节点,所以需要在此行执行之前将q的下一个节点的地址提前保存起来
p->next = q;
q = temp;//将q移动,指向无头表的下一个节点
}
}
int main(int argc, const char *argv[])
{
link_node_t *h = createEmptyLinkList();
insertIntoPostLinkList(h,0,2);
insertIntoPostLinkList(h,1,2);
insertIntoPostLinkList(h,2,2);
insertIntoPostLinkList(h,3,2);
insertIntoPostLinkList(h,4,2);
showLinkList(h);
deleteDataLinkList(h,2);
showLinkList(h);
return 0;
}
总结:
顺序表和单向链表比较
(1)顺序表在内存当中连续存储的(数组),但是链表在内存当中是不连续存储的,通过指针将数据链接在一起
(2)顺序表的长度是固定的,但是链表长度不固定
(3)顺序表查找方便,但是插入和删除麻烦,链表,插入和删除方便,查找麻烦
单向循环链表
#include <stdio.h>
#include <stdlib.h>
typedef struct node_t
{
int data;
struct node_t *next;
}link_node_t,*link_list_t;
int main(int argc, const char *argv[])
{
int i;
link_list_t pdel = NULL;//用于指向被删除节点
link_list_t ptail = NULL;//永远指向当前链表的尾
link_list_t pnew = NULL;//永远指向新创建的节点
link_list_t h = NULL;
int all_num = 7;//猴子总数
int start_num = 2; //从几号猴子开始数
int kill_num = 3;//数到几杀死猴
printf("请您入猴子总数 起始号码 数到几杀死:\n");
scanf("%d%d%d",&all_num,&start_num,&kill_num);
//1.创建出一个单向循环链表
//(1)创建有all_num个节点的单向链表
h = (link_list_t)malloc(sizeof(link_node_t));
if(NULL == h)
{
perror("malloc failed");
return -1;
}
h->data = 1;
h->next = NULL;
ptail = h;//尾指针指向当前的第一个节点
for(i = 2; i <= all_num; i++)
{
//创建新的节点
pnew = (link_list_t)malloc(sizeof(link_node_t));
if(NULL == pnew)
{
perror("malloc failed");
return -1;
}
//将新节点装上数据
pnew->data = i;
pnew->next = NULL;
//将新节点链接到链表尾
ptail->next = pnew;//链接到链表的尾
ptail = pnew;//尾指针继续指向当前链表的尾
}
//(2)将头指针保存到链表的尾形成单向循环链表
ptail->next = h;//形成单向循环链表
#if 0 //用于调试程序
while(1)
{
printf("%d\n",h->data);
h = h->next;
sleep(1);
}
#endif
//2.开始杀猴子
//(1)将头指针移动到开始猴子的号码处
for(i = 0; i < start_num-1; i++)
h = h->next;
//(2)循环进行杀猴子
while(h != h->next)//条件不成的时候,就剩一个猴子,只有一个节点
{
//将头指针移动到即将删除节点的前一个节点
for(i = 0; i < kill_num-2; i++)
h = h->next;
pdel = h->next;
//跨过删除节点
h->next = pdel->next;
printf("kill is -------------%d\n",pdel->data);
free(pdel);
pdel = NULL;
//杀死猴子猴,从下一个节点开始继续开始数,将头指针移动到开始数的地方
h = h->next;
}
printf("king is=================== %d\n",h->data);
return 0;
}
双向链表
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node_t
{
datatype data;//数据域
struct node_t *next;//指向下一个节点的指针 next 下一个
struct node_t *prior;//指向前一个节点的指针 prior 前一个
}link_node_t,*link_list_t;
//将双向链表的头指针和尾指针封装到一个结构体里
//思想上有点像上周学的链式队列
typedef struct doublelinklist
{
link_list_t head; //指向双向链表的头指针
link_list_t tail; //指向双向链表的尾指针
int len;
}double_node_t,*double_list_t;
//1.创建一个空的双向链表
double_list_t createEmptyDoubleLinkList()
{
//1.申请保存头指针和尾指针的空间
double_list_t p = (double_list_t)malloc(sizeof(double_node_t));
if(NULL == p)
{
perror("createEmptyDoubleLinkList malloc failed");
return NULL;
}
//2.将头指针和尾指针同时指向头节点,因为当前链表为空
p->len = 0;//当前链表的长度为0
p->head = p->tail = (link_list_t)malloc(sizeof(link_node_t));
if(NULL == p->head)
{
perror("p->head malloc failed!!");
return NULL;
}
//3.对双向链表的头结点进行初始化
p->head->prior = NULL;
p->head->next = NULL;
return p;
}
//2.向双向链表的指定位置插入数据 post位置, data数据
int insertIntoDoubleLinkList(double_list_t p, int post, datatype data)
{
int i;
link_list_t temp = NULL;//用来临时保存head或tail的位置
//1.容错判断
if(post < 0 || post > p->len)
{
printf("insertIntoDoubleLinkList post failed!!");
return -1;
}
//2.创建一个新的节点,用来保存插入的数据
link_list_t pnew = (link_list_t)malloc(sizeof(link_node_t));
if(NULL == pnew)
{
perror("pnew malloc failed");
return -1;
}
pnew->data = data;//将参数上插入的数据装入节点
pnew->prior = NULL;
pnew->next = NULL;
//3.将节点链接到链表中
//先对插入位置进行判断,分两种情况
if(post == p->len)//说明插入的是链表的尾巴
{
p->tail->next = pnew;
pnew->prior = p->tail;
p->tail = pnew;//将尾指针再次移动到当前链表的尾,永远指向尾
}
else//0-len-1
{
//(1)将temp移动到插入位置
if(post < p->len/2)//说明在前半段
{
temp = p->head;//用头向后移动
for(i = 0; i < post+1; i++)
temp = temp->next;
}
else//说明插入位置在后半部分
{
temp = p->tail;//用尾向前移动
for(i = 0; i < p->len-post-1; i++)
temp = temp->prior;
}
//(2)进行插入操作(先连前面,再连后面)
pnew->prior = temp->prior;
temp->prior->next = pnew;
pnew->next = temp;
temp->prior = pnew;
}
p->len++;//链表的长度+1
return 0;
}
//3.遍历双向链表
void showDoubleLinkList(double_list_t p)
{
link_list_t temp = NULL;
printf("正向遍历:");
temp = p->head;
while(temp->next != NULL)//类似于遍历有头单向链表
{
temp = temp->next;
printf("%d ",temp->data);
}
printf("\n");
printf("反向遍历:");
temp = p->tail;
while(temp != p->head)//类似于遍历无头单向链表
{
printf("%d ",temp->data);
temp = temp->prior;
}
printf("\n---------------------------------\n");
}
//4.删除双向链表指定位置的数据
int deletePostDoubleLinkList(double_list_t p, int post)
{
int i;
link_list_t temp = NULL;//临时用来保存头或尾指针
//1.容错处理
if(post < 0 || post >= p->len)
{
printf("deletePostDoubleLinkList post failed!!\n");
return -1;
}
//2.对删除位置进行分析,分为两种情况
if(post == p->len-1)//删除的是链表最后一个节点
{
//(1)将尾指针向前移动一个位置
p->tail = p->tail->prior;
//(2)释放被删除节点,也就是最后一个节点
free(p->tail->next);
//(3)将最后一个节点与链表断开
p->tail->next = NULL;
}
else
{
//将temp移动到删除位置
if(post < p->len/2)//说明在前半段
{
temp = p->head;
for(i = 0; i < post+1; i++)
temp = temp->next;
}
else//说明在后半段
{
temp = p->tail;
for(i = 0; i < p->len-1-post; i++)
temp = temp->prior;
}
//进行删除操作
temp->prior->next = temp->next;
temp->next->prior = temp->prior;
free(temp);
temp = NULL;
}
//3.双向链表的长度-1
p->len--;
return 0;
}
//5.判断双向链表是否为空
int isEmptyDoubleLinkList(double_list_t p)
{
return p->len == 0;
}
//6.求双向链表的长度
int lengthDoubleLinkList(double_list_t p)
{
return p->len;
}
//7.查找指定数据出现的位置 data被查找的数据
int searchPostDoubleLinkList(double_list_t p,datatype data)
{
link_list_t temp = p->head;
int post = 0;
while(temp->next != NULL)//类似与遍历一个有头的单向链表
{
temp = temp->next;
if(temp->data == data)
return post;
post++;
}
return -1;//查找失败,数据不存在
}
//8.修改指定位置的数据,post修改的位置 data被修改的数据
int changeDataDoubleLinkList(double_list_t p,int post, datatype data)
{
link_list_t temp = NULL;
int i;
//1.容错判断
if(post < 0 || post >= p->len)
{
printf("changeDataDoubleLinkList post is failed!!\n");
return -1;
}
//2.将temp移动到修改数据的位置
if(post < p->len/2)
{
temp = p->head;
for(i = 0; i < post+1; i++)
temp = temp->next;
}
else
{
temp = p->tail;
for(i = 0; i < p->len-post-1; i++)
temp = temp->prior;
}
//3. 修改数据
temp->data = data;
return 0;
}
//9.删除双向链表中的指定数据 data代表删除所有出现的data数据
int deleteDataDoubleLinkList(double_list_t p, datatype data)
{
link_list_t pdel = NULL;//指向被删除节点
link_list_t h = p->head->next;
while(h != NULL)
{
if(h->data == data)
{
if(h == p->tail)
{
p->tail = p->tail->prior;
free(p->tail->next);
p->tail->next = NULL;
}
else
{
h->prior->next = h->next;
h->next->prior = h->prior;
pdel = h;
h = h->next;
free(pdel);
pdel = NULL;
}
}
else
{
h = h->next;
}
}
}
//10.转置双向链表
void reverseDoubleLinkList(double_list_t p)
{
link_list_t temp = NULL;
link_list_t newtail = p->head;
while(p->tail != p->head)
{
temp = p->tail->prior;
p->tail->prior = newtail;
newtail->next = p->tail;
p->tail->next = NULL;
p->tail = temp;
newtail = newtail->next;
}
p->tail = newtail;
}
int main(int argc, const char *argv[])
{
double_list_t p = createEmptyDoubleLinkList();
insertIntoDoubleLinkList(p,0,0);
insertIntoDoubleLinkList(p,1,1);
insertIntoDoubleLinkList(p,2,2);
insertIntoDoubleLinkList(p,3,3);
insertIntoDoubleLinkList(p,4,4);
// insertIntoDoubleLinkList(p,5,5);
showDoubleLinkList(p);
printf("转置之后:---------\n");
reverseDoubleLinkList(p);
showDoubleLinkList(p);
return 0;
}
双向循环链表
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node_t
{
datatype data;
struct node_t * prior;
struct node_t * next;
}link_node_t,*link_list_t;
typedef struct doublelinklist
{
link_list_t head;
link_list_t tail;
}double_node_t,*double_list_t;
int main(int argc, const char *argv[])
{
int i;
int all_num = 8;//猴子总数
int start_num = 3;//从3号猴子开始数
int kill_num = 3;//数到几杀死猴子
link_list_t h = NULL;
link_list_t pdel = NULL;//用来指向被杀死猴子的节点
printf("请您输入猴子的总数,开始号码,出局号码:\n");
scanf("%d%d%d",&all_num,&start_num,&kill_num);
//1.创建一个双向的循环链表
double_list_t p = (double_list_t)malloc(sizeof(double_node_t));//申请头指针和尾指针
if(NULL == p)
{
perror("malloc failed");
return -1;
}
p->head = p->tail = (link_list_t)malloc(sizeof(link_node_t));
if(NULL == p->tail)
{
perror("p->tail malloc failed");
return -1;
}
p->head->data = 1;
p->head->prior = NULL;
p->head->next = NULL;
//将创建n个新的节点,链接到链表的尾
for(i = 2; i <= all_num; i++)
{
link_list_t pnew = (link_list_t)malloc(sizeof(link_node_t));
if(NULL == pnew)
{
perror("pnew malloc failed");
return -1;
}
pnew->data = i;
pnew->prior = NULL;
pnew->next = NULL;
//(1)将新的节点链接到链表的尾
p->tail->next = pnew;
pnew->prior = p->tail;
//(2)尾指针向后移动,指向当前链表的尾
p->tail = pnew;
}
//(3)形成双向循环链表
p->tail->next = p->head;
p->head->prior = p->tail;
//调试程序
#if 0
while(1)
{
printf("%d\n",p->head->data);
p->head = p->head->next;
sleep(1);
}
#endif
//2.循环进行杀死猴子
h = p->head;
//(1)先将h移动到start_num处,也就是开始数数的猴子号码处
for(i = 0; i < start_num-1; i++)
h = h->next;
while(h->next != h)//当h->next == h 就剩一个节点了,循环结束
{
//(2)将h移动到即将杀死猴子号码的位置
for(i = 0; i < kill_num-1; i++)
h = h->next;
//(3)进行杀死猴子,经过上面的循环后,此时的h指向即将杀死的猴子
h->prior->next = h->next;
h->next->prior = h->prior;
pdel = h;//pdel指向被杀死猴子的位置
printf("kill is -------%d\n",pdel->data);
h = h->next;//需要移动,从杀死猴子后的下一个位置开始数
free(pdel);
}
printf("猴王是%d\n",h->data);
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//9.删除双向链表中的指定数据 data代表删除所有出现的data数据
int deleteDataDoubleLinkList(double_list_t p, datatype data)
{
}
//10.转置双向链表
void reverseDoubleLinkList(double_list_t p)
{
}
//这两个答案,我给你,你自己思考,先弄懂
//9.删除双向链表中的指定数据 data代表删除所有出现的data数据
int deleteDataDoubleLinkList(double_list_t p, datatype data)
{
link_list_t pdel = NULL;//指向被删除节点
link_list_t h = p->head->next;
while(h != NULL)
{
if(h->data == data)
{
if(h == p->tail)
{
p->tail = p->tail->prior;
free(p->tail->next);
p->tail->next = NULL;
}
else
{
h->prior->next = h->next;
h->next->prior = h->prior;
pdel = h;
h = h->next;
free(pdel);
pdel = NULL;
}
}
else
{
h = h->next;
}
}
}
//10.转置双向链表
void reverseDoubleLinkList(double_list_t p)
{
link_list_t temp = NULL;
link_list_t newtail = p->head;
while(p->tail != p->head)
{
temp = p->tail->prior;
p->tail->prior = newtail;
newtail->next = p->tail;
p->tail->next = NULL;
p->tail = temp;
newtail = newtail->next;
}
p->tail = newtail;
}
队列
栈
标准IO
姑且写一个实现文件拷贝,文件比对(用MD5最快,实现的话跟拷贝是类似的),内容查找的,一一介绍太繁琐。还有fgets含读取,fputs行写入 fflush刷新缓存区(频繁刷新缓存,对资源消耗大,且容易内存有芯片有损伤)格式化读写fprintf和fscanf等等。
#include<stdio.h>
#include <unistd.h>
#define LEN 255
#define BUF_SIZE 50
//目录操作函数 pathname 打开目录名绝对路径
int chdirtest(char * pathname)
{
if(pathname == NULL)
{
printf("please input chdir name!!!\r\n");
return -1;
}
int ret=-1;
char buf[LEN]={0};
if(!getcwd(buf,LEN))//获取当前目录绝对路径
{
perror("getcwd");
return -1;
}
printf("getcwd is:%s\r\n",buf);
printf("chdir---------\r\n");
ret=chdir(pathname);//改变目录
if(ret)
{
//没有就循环创建
if (pathname == NULL)
{
return -1;
}
int i = 0, len = 0;
char str[256] = {0};
strcpy(str, pathname);
len = strlen(str);
for (i = 0; i < len; i++)
{
if (str[i] == '/')
{
str[i] = '\0';
if (access(str, 0) != 0) //断言函数判断目录是否存在
{
mkdir(str, S_IRWXU | S_IRWXG | S_IRWXO);
}
str[i] = '/';
}
}
}
if(!getcwd(buf,LEN))
{
perror("getcwd error");
return -1;
}
printf("2:getcwd is:%s\r\n",buf);//新的目录
return 0;
}
//文件操作函数 pathname目标文件 pathcpy拷贝目标文件 buf1查找内容 buf2替换内容
int fwitretest(char *pathname,char *pathcpy,char *buf1, char *buf2)
{
if(pathname == NULL || pathcpy == NULL || buf1 == NULL)
{
printf("please input file name!!!\r\n");
return -1;
}
//open src file
FILE *src=fopen(pathname,"r");
if(NULL==src)
{
printf("open src file error %d,%s\r\n",errno,strerror(errno));
perror("error:");//printf error
return -1;
}
printf("open src file ok\r\n");
//open dest file
FILE *dest=fopen(pathcpy,"w");//没有就创建
if(NULL==dest)
{
printf("open dest file error %d,%s\r\n",errno,strerror(errno));
perror("error:");//printf error
return -1;
}
printf("open dest file ok\r\n");
//read file 拷贝
char buf[BUF_SIZE]={0};
while(fread(buf,1,BUF_SIZE,src))
{
int ret = strlen(buf);
printf("buf:%s ,ret=%d\r\n",buf,ret);
ret=fwrite(buf,1,ret,dest);//从pathname读写道pathcpy中
printf("fwrite ret=%d\r\n",ret);
memset(buf,0,BUF_SIZE);
}
//查找
//拷贝完了file指针已经到文件最末去了用fseek从新定位到最前面就好了
fseek(src, 0, SEEK_SET);
int pos = 0;
int npos = 0;
//既然是查找就读去与被查找的相同字节数
while(fread(buf,1,BUF_SIZE,src))
{
if(pos > 0)
{
fseek(src, pos, SEEK_SET);
}
if(strlen(buf) < strlen(buf1))
{
printf("fwrite 读取小于查找,已经查找完了没有必要进行下去了\r\n");
break;
}
if(strlen(buf) == strlen(buf1))
{
if(memcmp(buf,buf1,strlen(buf)) == 0)
{
npos = npos + 1;
printf("查到了一处 npos = %d\n",npos);
pos = pos + strlen(buf);
}
else
{
pos++;
}
}
}
//close file
fclose(src);
fclose(dest);
return 0;
}
int main(int argc,char *argv[])
{
return 0;
}
文件IO
跟文件io差不到把函数换一下就好了
文件io和标准io差别
主要的区别在于有无缓冲区,标准IO在进行系统调用操作时,会先操作缓冲区,而文件IO则直接进行系统调用。(常见的系统调用write、read、printf等)。
优劣:因为系统调用的过程需要从用户太切换到内核态,使用文件IO的时候,会没进行一个字节的读写都需要从用户态切换到内核态,如此频繁的进行切换极大的消耗了系统的资源。而标准IO在进行读写的时候,会先把读写的内容存放在缓冲区,等待全缓冲或者行缓冲出现,然后把缓冲区里面的数据通过一次系统调用和文件进行数据交互。
进程模块
进程和程序的区别以及概念
A.程序是静态的,它是一些保存在磁盘上的指令的有序集合,没有任何执行的概念。
B.进程是一个动态的概念,它是程序执行的过程,包括了动态创建、调度和消亡的整个过程。
C.进程是程序执行和资源管理的最小单位。
D.每个进程都是由父进程创建。
idle是由内核创建,nit进程由idle进程创建,在内核空间完成初始化后, 加载init程序;kthreadd进程由idle进程创建,并始终运行在内核空间;(kthreadd如图主要负责内核线程列表的调度)

进程地址空间
一旦进程建立之后,系统则要为这个进程分配相应的资源,一般系统会为每个进程分配 4G虚拟地址 的地址空间,如图所示。

1.一般4G 的进程地址空间主要分为两部分:0 - 3G : 用户空间 与 3G - 4G 内核空间。
2.用户空间又分为 :
stack : 存放非静态的局部变量(栈区就是局部变量如int a,代码段运行结束自行释放)
heap : 动态申请的内存(堆区malloc和new通过free和delete释放)
.bss : 未初始化过的全局变量(包括初始化为0的,未初始化过的静态变量)
.data : 初始化过并且值不为0的全局变量,初始化过的静态变量(包括初始化为0)
.rodata : 只读变量(字符串之类)
.text : 程序文本段(包括函数,符号常量)
这里浅浅说一下malloc/new和free/delete主要区别吧,详细可以查一下,主要还是运算符特性,c++在细说吧
malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符
3.当用户进程需要通过内核获取资源时,会切换到内核态运行,这时当前进程会使用内核空间分配资源
4.用户需要切换到内核态运行时,主要是通过 系统调用
虚拟地址与物理地址
1.在程序执行过程中,操作系统会分配 4G 的 虚拟地址空间。
2.虚拟地址空间中的每个地址都是一个 虚拟地址。
3.虚拟地址 : 虚拟地址并不代表真实的内存空间,而是一个用于寻址的编号。
4.物理地址 : 是指内存设备中真实存在的存储空间的编号。
5.虚拟地址通过映射的方式建立与物理地址的关联,从而达到访问虚拟地址就可以访问到对应的物理地址。
6.在操作系统中使用虚拟地址空间主要是基于以下原因:直接访问物理地址,会导致地址空间没有隔离,很容易导致数据被修改。
7.通过虚拟地址空间可以实现每个进程地址空间都是独立的,操作系统会映射到不用的物理地址区间,在访问时互不干扰。
进程的状态管理和task_struct 结构体
Linux系统中的进程状态:R(Running),S(sleeping),D(disk sleeping),Z(僵尸进程)和X(死亡态),T(stopping),t(trace stopping)
1. R(running):运行态:进程中的进程如果一直被运行,就会处于运行状态。
2. S(sleeping):睡眠状态:是一种阻塞状态,表示的是等待外设的就绪,是一种浅度睡眠状态,可以随时被中断
3. D(disk sleeping):阻塞状态:属于一种深度睡眠的状态,这个状态一旦发生,就不容易被中断,一般情况下只能通过关机或者断电才能够中断,其发生本质原因是等待磁盘资源的分配,磁盘的资源压力过大
4.X(死亡态):表示进程已经死亡,处于终止态
5.Z(僵尸进程):表示进程处于僵尸状态,已经不用上处理机运行的状态
6.T(Stopping)暂停状态:当进程被kill -19 pid 暂停的时候,此时进程会处于暂停状态
7.t(tracing stopping):追踪暂停状态:一般是在调试的过程中遇到断点的状态
task_struct
用于描述每一个进程,每产生一个进程,则会分配一个 task_struct 结构体对象。在Linux内核中,进程通常被叫做任务,所以进程控制块(PCB)也被命名为struct task_struct。而Linux中是没有明确的线程的概念的,被称为轻量级进程,和进程使用相同的PCB结构,内核使用clone()来创建线程。
task_struct (PCB):Linux内核通过一个被称为进程描述符的task_struct结构体来管理进程,这个结构体包含了一个进程所需的所有信息。(进程管理模块)。task_struct 结构体如下不深入研究了
cat /usr/src/linux-headers-4.15.0-136-generic/include/linux/sched.h
struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
/*
* For reasons of header soup (see current_thread_info()), this
* must be the first element of task_struct.
*/
struct thread_info thread_info;
#endif
/* -1 unrunnable, 0 runnable, >0 stopped: */
volatile long state; //进程状态 -1 未运行,0运行 >0 停止
/*
* This begins the randomizable portion of task_struct. Only
* scheduling-critical items should be added above here.
*/
randomized_struct_fields_start
void *stack;
atomic_t usage;
/* Per task flags (PF_*), defined further below: */
unsigned int flags;
unsigned int ptrace;
#ifdef CONFIG_SMP
struct llist_node wake_entry;
int on_cpu;
#ifdef CONFIG_THREAD_INFO_IN_TASK
/* Current CPU: */
unsigned int cpu;
#endif
unsigned int wakee_flips;
unsigned long wakee_flip_decay_ts;
struct task_struct *last_wakee;
int wake_cpu;
#endif
int on_rq;
int prio;
int static_prio;
int normal_prio;
unsigned int rt_priority;
const struct sched_class *sched_class;
struct sched_entity se;
struct sched_rt_entity rt;
#ifdef CONFIG_CGROUP_SCHED
struct task_group *sched_task_group;
#endif
struct sched_dl_entity dl;
#ifdef CONFIG_PREEMPT_NOTIFIERS
/* List of struct preempt_notifier: */
struct hlist_head preempt_notifiers;
#endif
#ifdef CONFIG_BLK_DEV_IO_TRACE
unsigned int btrace_seq;
#endif
unsigned int policy;
int nr_cpus_allowed;
cpumask_t cpus_allowed;
#ifdef CONFIG_PREEMPT_RCU
int rcu_read_lock_nesting;
union rcu_special rcu_read_unlock_special;
struct list_head rcu_node_entry;
struct rcu_node *rcu_blocked_node;
#endif /* #ifdef CONFIG_PREEMPT_RCU */
#ifdef CONFIG_TASKS_RCU
unsigned long rcu_tasks_nvcsw;
u8 rcu_tasks_holdout;
u8 rcu_tasks_idx;
int rcu_tasks_idle_cpu;
struct list_head rcu_tasks_holdout_list;
#endif /* #ifdef CONFIG_TASKS_RCU */
struct sched_info sched_info;
struct list_head tasks;
#ifdef CONFIG_SMP
struct plist_node pushable_tasks;
struct rb_node pushable_dl_tasks;
#endif
struct mm_struct *mm;
struct mm_struct *active_mm;
/* Per-thread vma caching: */
struct vmacache vmacache;
#ifdef SPLIT_RSS_COUNTING
struct task_rss_stat rss_stat;
#endif
int exit_state;//表示进程的退出状态
int exit_code;
int exit_signal;
/* The signal sent when the parent dies: */
int pdeath_signal;
/* JOBCTL_*, siglock protected: */
unsigned long jobctl;
/* Used for emulating ABI behavior of previous Linux versions: */
unsigned int personality;
/* Scheduler bits, serialized by scheduler locks: */
unsigned sched_reset_on_fork:1;
unsigned sched_contributes_to_load:1;
unsigned sched_migrated:1;
unsigned sched_remote_wakeup:1;
/* Force alignment to the next boundary: */
unsigned :0;
/* Unserialized, strictly 'current' */
/* Bit to tell LSMs we're in execve(): */
unsigned in_execve:1;
unsigned in_iowait:1;
#ifndef TIF_RESTORE_SIGMASK
unsigned restore_sigmask:1;
#endif
#ifdef CONFIG_MEMCG
unsigned memcg_may_oom:1;
#ifndef CONFIG_SLOB
unsigned memcg_kmem_skip_account:1;
#endif
#endif
#ifdef CONFIG_COMPAT_BRK
unsigned brk_randomized:1;
#endif
#ifdef CONFIG_CGROUPS
/* disallow userland-initiated cgroup migration */
unsigned no_cgroup_migration:1;
#endif
unsigned long atomic_flags; /* Flags requiring atomic access. */
struct restart_block restart_block;
pid_t pid;
pid_t tgid;
#ifdef CONFIG_CC_STACKPROTECTOR
/* Canary value for the -fstack-protector GCC feature: */
unsigned long stack_canary;
#endif
/*
* Pointers to the (original) parent process, youngest child, younger sibling,
* older sibling, respectively. (p->father can be replaced with
* p->real_parent->pid)
*/
/* Real parent process: */
struct task_struct __rcu *real_parent;
/* Recipient of SIGCHLD, wait4() reports: */
struct task_struct __rcu *parent;
/*
* Children/sibling form the list of natural children:
*/
struct list_head children;
struct list_head sibling;
struct task_struct *group_leader;
/*
* 'ptraced' is the list of tasks this task is using ptrace() on.
*
* This includes both natural children and PTRACE_ATTACH targets.
* 'ptrace_entry' is this task's link on the p->parent->ptraced list.
*/
struct list_head ptraced;
struct list_head ptrace_entry;
/* PID/PID hash table linkage. */
struct pid_link pids[PIDTYPE_MAX];
struct list_head thread_group;
struct list_head thread_node;
struct completion *vfork_done;
/* CLONE_CHILD_SETTID: */
int __user *set_child_tid;
/* CLONE_CHILD_CLEARTID: */
int __user *clear_child_tid;
u64 utime;
u64 stime;
#ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME
u64 utimescaled;
u64 stimescaled;
#endif
u64 gtime;
struct prev_cputime prev_cputime;
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
struct vtime vtime;
#endif
#ifdef CONFIG_NO_HZ_FULL
atomic_t tick_dep_mask;
#endif
/* Context switch counts: */
unsigned long nvcsw;
unsigned long nivcsw;
/* Monotonic time in nsecs: */
u64 start_time;
/* Boot based time in nsecs: */
u64 real_start_time;
/* MM fault and swap info: this can arguably be seen as either mm-specific or thread-specific: */
unsigned long min_flt;
unsigned long maj_flt;
#ifdef CONFIG_POSIX_TIMERS
struct task_cputime cputime_expires;
struct list_head cpu_timers[3];
#endif
/* Process credentials: */
/* Tracer's credentials at attach: */
const struct cred __rcu *ptracer_cred;
/* Objective and real subjective task credentials (COW): */
const struct cred __rcu *real_cred;
/* Effective (overridable) subjective task credentials (COW): */
const struct cred __rcu *cred;
/*
* executable name, excluding path.
*
* - normally initialized setup_new_exec()
* - access it with [gs]et_task_comm()
* - lock it with task_lock()
*/
char comm[TASK_COMM_LEN];
struct nameidata *nameidata;
#ifdef CONFIG_SYSVIPC
struct sysv_sem sysvsem;
struct sysv_shm sysvshm;
#endif
#ifdef CONFIG_DETECT_HUNG_TASK
unsigned long last_switch_count;
#endif
/* Filesystem information: */
struct fs_struct *fs;
/* Open file information: */
struct files_struct *files;
/* Namespaces: */
struct nsproxy *nsproxy;
/* Signal handlers: */
struct signal_struct *signal;
struct sighand_struct *sighand;
sigset_t blocked;
sigset_t real_blocked;
/* Restored if set_restore_sigmask() was used: */
sigset_t saved_sigmask;
struct sigpending pending;
unsigned long sas_ss_sp;
size_t sas_ss_size;
unsigned int sas_ss_flags;
struct callback_head *task_works;
struct audit_context *audit_context;
#ifdef CONFIG_AUDITSYSCALL
kuid_t loginuid;
unsigned int sessionid;
#endif
struct seccomp seccomp;
/* Thread group tracking: */
u64 parent_exec_id;
u64 self_exec_id;
/* Protection against (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed, mempolicy: */
spinlock_t alloc_lock;
/* Protection of the PI data structures: */
raw_spinlock_t pi_lock;
struct wake_q_node wake_q;
#ifdef CONFIG_RT_MUTEXES
/* PI waiters blocked on a rt_mutex held by this task: */
struct rb_root_cached pi_waiters;
/* Updated under owner's pi_lock and rq lock */
struct task_struct *pi_top_task;
/* Deadlock detection and priority inheritance handling: */
struct rt_mutex_waiter *pi_blocked_on;
#endif
#ifdef CONFIG_DEBUG_MUTEXES
/* Mutex deadlock detection: */
struct mutex_waiter *blocked_on;
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
unsigned int irq_events;
unsigned long hardirq_enable_ip;
unsigned long hardirq_disable_ip;
unsigned int hardirq_enable_event;
unsigned int hardirq_disable_event;
int hardirqs_enabled;
int hardirq_context;
unsigned long softirq_disable_ip;
unsigned long softirq_enable_ip;
unsigned int softirq_disable_event;
unsigned int softirq_enable_event;
int softirqs_enabled;
int softirq_context;
#endif
#ifdef CONFIG_LOCKDEP
# define MAX_LOCK_DEPTH 48UL
u64 curr_chain_key;
int lockdep_depth;
unsigned int lockdep_recursion;
struct held_lock held_locks[MAX_LOCK_DEPTH];
#endif
#ifdef CONFIG_UBSAN
unsigned int in_ubsan;
#endif
/* Journalling filesystem info: */
void *journal_info;
/* Stacked block device info: */
struct bio_list *bio_list;
#ifdef CONFIG_BLOCK
/* Stack plugging: */
struct blk_plug *plug;
#endif
/* VM state: */
struct reclaim_state *reclaim_state;
struct backing_dev_info *backing_dev_info;
struct io_context *io_context;
/* Ptrace state: */
unsigned long ptrace_message;
siginfo_t *last_siginfo;
struct task_io_accounting ioac;
#ifdef CONFIG_TASK_XACCT
/* Accumulated RSS usage: */
u64 acct_rss_mem1;
/* Accumulated virtual memory usage: */
u64 acct_vm_mem1;
/* stime + utime since last update: */
u64 acct_timexpd;
#endif
#ifdef CONFIG_CPUSETS
/* Protected by ->alloc_lock: */
nodemask_t mems_allowed;
/* Seqence number to catch updates: */
seqcount_t mems_allowed_seq;
int cpuset_mem_spread_rotor;
int cpuset_slab_spread_rotor;
#endif
#ifdef CONFIG_CGROUPS
/* Control Group info protected by css_set_lock: */
struct css_set __rcu *cgroups;
/* cg_list protected by css_set_lock and tsk->alloc_lock: */
struct list_head cg_list;
#endif
#ifdef CONFIG_INTEL_RDT
u32 closid;
u32 rmid;
#endif
#ifdef CONFIG_FUTEX
struct robust_list_head __user *robust_list;
#ifdef CONFIG_COMPAT
struct compat_robust_list_head __user *compat_robust_list;
#endif
struct list_head pi_state_list;
struct futex_pi_state *pi_state_cache;
struct mutex futex_exit_mutex;
unsigned int futex_state;
#endif
#ifdef CONFIG_PERF_EVENTS
struct perf_event_context *perf_event_ctxp[perf_nr_task_contexts];
struct mutex perf_event_mutex;
struct list_head perf_event_list;
#endif
#ifdef CONFIG_DEBUG_PREEMPT
unsigned long preempt_disable_ip;
#endif
#ifdef CONFIG_NUMA
/* Protected by alloc_lock: */
struct mempolicy *mempolicy;
short il_prev;
short pref_node_fork;
#endif
#ifdef CONFIG_NUMA_BALANCING
int numa_scan_seq;
unsigned int numa_scan_period;
unsigned int numa_scan_period_max;
int numa_preferred_nid;
unsigned long numa_migrate_retry;
/* Migration stamp: */
u64 node_stamp;
u64 last_task_numa_placement;
u64 last_sum_exec_runtime;
struct callback_head numa_work;
struct list_head numa_entry;
struct numa_group *numa_group;
/*
* numa_faults is an array split into four regions:
* faults_memory, faults_cpu, faults_memory_buffer, faults_cpu_buffer
* in this precise order.
*
* faults_memory: Exponential decaying average of faults on a per-node
* basis. Scheduling placement decisions are made based on these
* counts. The values remain static for the duration of a PTE scan.
* faults_cpu: Track the nodes the process was running on when a NUMA
* hinting fault was incurred.
* faults_memory_buffer and faults_cpu_buffer: Record faults per node
* during the current scan window. When the scan completes, the counts
* in faults_memory and faults_cpu decay and these values are copied.
*/
unsigned long *numa_faults;
unsigned long total_numa_faults;
/*
* numa_faults_locality tracks if faults recorded during the last
* scan window were remote/local or failed to migrate. The task scan
* period is adapted based on the locality of the faults with different
* weights depending on whether they were shared or private faults
*/
unsigned long numa_faults_locality[3];
unsigned long numa_pages_migrated;
#endif /* CONFIG_NUMA_BALANCING */
struct tlbflush_unmap_batch tlb_ubc;
struct rcu_head rcu;
/* Cache last used pipe for splice(): */
struct pipe_inode_info *splice_pipe;
struct page_frag task_frag;
#ifdef CONFIG_TASK_DELAY_ACCT
struct task_delay_info *delays;
#endif
#ifdef CONFIG_FAULT_INJECTION
int make_it_fail;
unsigned int fail_nth;
#endif
/*
* When (nr_dirtied >= nr_dirtied_pause), it's time to call
* balance_dirty_pages() for a dirty throttling pause:
*/
int nr_dirtied;
int nr_dirtied_pause;
/* Start of a write-and-pause period: */
unsigned long dirty_paused_when;
#ifdef CONFIG_LATENCYTOP
int latency_record_count;
struct latency_record latency_record[LT_SAVECOUNT];
#endif
/*
* Time slack values; these are used to round up poll() and
* select() etc timeout values. These are in nanoseconds.
*/
u64 timer_slack_ns;
u64 default_timer_slack_ns;
#ifdef CONFIG_KASAN
unsigned int kasan_depth;
#endif
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
/* Index of current stored address in ret_stack: */
int curr_ret_stack;
int curr_ret_depth;
/* Stack of return addresses for return function tracing: */
struct ftrace_ret_stack *ret_stack;
/* Timestamp for last schedule: */
unsigned long long ftrace_timestamp;
/*
* Number of functions that haven't been traced
* because of depth overrun:
*/
atomic_t trace_overrun;
/* Pause tracing: */
atomic_t tracing_graph_pause;
#endif
#ifdef CONFIG_TRACING
/* State flags for use by tracers: */
unsigned long trace;
/* Bitmask and counter of trace recursion: */
unsigned long trace_recursion;
#endif /* CONFIG_TRACING */
#ifdef CONFIG_KCOV
/* Coverage collection mode enabled for this task (0 if disabled): */
enum kcov_mode kcov_mode;
/* Size of the kcov_area: */
unsigned int kcov_size;
/* Buffer for coverage collection: */
void *kcov_area;
/* KCOV descriptor wired with this task or NULL: */
struct kcov *kcov;
#endif
#ifdef CONFIG_MEMCG
struct mem_cgroup *memcg_in_oom;
gfp_t memcg_oom_gfp_mask;
int memcg_oom_order;
/* Number of pages to reclaim on returning to userland: */
unsigned int memcg_nr_pages_over_high;
#endif
#ifdef CONFIG_UPROBES
struct uprobe_task *utask;
#endif
#if defined(CONFIG_BCACHE) || defined(CONFIG_BCACHE_MODULE)
unsigned int sequential_io;
unsigned int sequential_io_avg;
#endif
#ifdef CONFIG_DEBUG_ATOMIC_SLEEP
unsigned long task_state_change;
#endif
int pagefault_disabled;
#ifdef CONFIG_MMU
struct task_struct *oom_reaper_list;
#endif
#ifdef CONFIG_VMAP_STACK
struct vm_struct *stack_vm_area;
#endif
#ifdef CONFIG_THREAD_INFO_IN_TASK
/* A live task holds one reference: */
atomic_t stack_refcount;
#endif
#ifdef CONFIG_LIVEPATCH
int patch_state;
#endif
#ifdef CONFIG_SECURITY
/* Used by LSM modules for access restriction: */
void *security;
#endif
/*
* New fields for task_struct should be added above here, so that
* they are included in the randomized portion of task_struct.
*/
randomized_struct_fields_end
/* CPU-specific state of this task: */
struct thread_struct thread;
/*
* WARNING: on x86, 'thread_struct' contains a variable-sized
* structure. It *MUST* be at the end of 'task_struct'.
*
* Do not put anything below here!
*/
};

进程常用函数
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
//exec() 函数族
int extecltest(void);
static int idata = 200;
int main()
{
int a,b,c;
write(STDOUT_FILENO,"Hello",6);
fputs("Hello",stdout);//会打印三次子父各一次刷新缓存一次
//pid_t getpid(void); 获取当前进程的 pid
//pid_t getppid(void);获取当前进程的父进程的 pid
//创建子进程
//会复制父进程的所有资源,包括缓冲区即fork以上所有资源
//返回值子进程的是 0,父进程给到的是子进程的pid
//pid_t fork(void);
pid_t pid;
int istack = 300;
getpid();
pid = fork();
if (pid < 0)
{
perror("fork():");
//exit 与 _exit 函数功能相似都是退出进程,但是实现机制与完成的功能有差异
//_exit()属于系统调用,能够使进程停止运行,并释放空间以及销毁内核中的各种数据结构
//exit()基于_exit()函数实现,属于库函数, 可以清理I/O缓冲区
exit(-1);
}
else if (pid == 0)
{
idata *= 2;
istack *= 3;
getppid();
a = getpid();
exit(20);
}
int status;
//waitpid(pid,&status,0); //阻塞方式
while(waitpid(a ,&status,WNOHANG) == 0)//非阻塞方式
{
;;
}
printf("status : %d\n",WEXITSTATUS(status));
printf(" %s %d %d\n",(pid == 0)?("child"):("parent"),idata,istack);
//_exit(-1);用这个hello就打印两次
return 0;
}
int extecltest(void)
{
#if 0
if (execl("/bin/ls","ls","-l",NULL) < 0)
{
perror("execlp():");
exit(-1);
}
#endif
#if 0
if (execlp("ls","ls","-l",NULL) < 0)
{
perror("execlp():");
exit(-1);
}
#endif
#if 1
char * const envp[] = {"USER=root","PATH=/bin",NULL};
if (execle("./app","./app","1",NULL,envp) < 0)
{
perror("execlp():");
exit(-1);
}
#endif
#if 0
char * const argv[] = {"ls","-l",NULL};
if (execv("/bin/ls",argv) < 0)
{
perror("execlp():");
exit(-1);
}
#endif
#if 0
char * const argv[] = {"ls","-l",NULL};
if (execvp("ls",argv) < 0)
{
perror("execlp():");
exit(-1);
}
#endif
#if 0
char * const argv[] = {"ls","-l",NULL};
char * const envp[] = {"USER=root","PATH=/bin",NULL};
if (execvpe("ls",argv,envp) < 0)
{
perror("execlp():");
exit(-1);
}
#endif
return 0;
}
wait和waitpid的区别
阻塞方式不同:wait函数会阻塞调用进程,直到有子进程结束;而waitpid函数可以选择是否阻塞调用进程,可以通过设置options参数来控制阻塞行为。
可以处理的子进程状态不同:wait函数只能获取已经结束的子进程的状态信息,而waitpid函数可以根据传入的options参数,等待特定状态的子进程结束并获取其状态信息。
进程的替换
在 Linux 系统提供了 exec() 函数族来实现进程的替换,exec() 函数族包含很多函数,具体的函数原型如下:
int execl(const char *path, const char *arg, ... /* (char *) NULL */);
int execlp(const char *file, const char *arg, ... /* (char *) NULL */);
int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
参数:
@param path : 可执行文件的路径名
@param file : 可执行文件名,只能搜索环境变量 PATH 指定的路径
@parma arg : 可执行文件名以及参数,参数列表需要以 NULL 结尾
@param argv[] : 参数数组,可以取代参数列表
@param evnp[] : 环境变量数组
守护进程
1.定义:周期性的执行某项任务或等待某个事件发生的进程, 它的运行不依赖shell终端,它的生存周期较长,从开机
2.如何创建守护进程:
1.创建子进程,让父进程退出
2.在子进程中创建新会话
3.改变当前目录为根目录
4.重设文件权限掩码
5.关闭文件描述符

简单写一个通过守护进程定期读写吧
#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#define BUF_SIZE 50
int main(int argc,char *argv[])
{
time_t stime;
struct tm *daytime=NULL;
int i=0;
char buf[BUF_SIZE]={0};
//fork
pid_t pid=-1;
pid=fork();
if(-1==pid)
{
return -1;
}
if(pid>0) //父进程退出
{
exit(0);
}
//setsid
setsid();//设置会话
//chdir
chdir("/");//更改目录
//umask
umask(0);//设置掩码
//close
int files=getdtablesize();
printf("files=%d\r\n",files);
for(;i<3;i++)//除标准输入输出异常其他文件描述符都关闭
{
close(i);
}
//打开log文件注意:文件路径为绝对路经
int fp=open(argv[1],O_RDWR,0777);
if(-1==fp)
{
printf("open file error\r\n");
return -1;
}
//每隔10s往log文件中写入当前时间
while(1)
{
memset(buf,0,sizeof(buf));
stime=time(NULL);
daytime=localtime(&stime);
sprintf(buf,"%d-%02d-%02d %02d:%02d:%2d\r\n",
daytime->tm_year+1900,daytime->tm_mon+1,daytime->tm_mday,
daytime->tm_hour,daytime->tm_min,daytime->tm_sec);
int ret=write(fp,buf,strlen(buf));
sleep(10);
}
return 0;
}
进程间通讯
传统通信
1.有名管道
有名管道是半双工的通信方式,但是它允许无亲缘关系进程间的通信。先进先出原则,管道文件不支持文件指针重定向即不支持fseelk和seelk

有名管道模拟浅浅的一起看一下
文件a.ccp
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#define BUF_SIZE 20
int main()
{
//mkfifo
int ret=-1;
ret=mkfifo("fifo",0666);//创建管道文件权限
if(ret<0 && EEXIST!=errno)
{
return -1;
}
printf("create fifo ok\r\n");
//open
int fd=open("fifo",O_WRONLY,0666);
if(-1==fd)
{
printf("open fifo error\r\n");
}
printf("open fifo ok\r\n");
//write
char buf[BUF_SIZE]={0};
strcpy(buf,"hello");
write(fd,buf,BUF_SIZE);
close(fd);
return 0;
}
文件b.cpp
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#define BUF_SIZE 20
int main()
{
//mkfifo
int ret=-1;
ret=mkfifo("fifo",0666);
if(ret<0 && EEXIST!=errno)
{
return -1;
}
printf("create fifo ok\r\n");
//open
int fd=open("fifo",O_RDONLY,0666);
if(-1==fd)
{
printf("open fifo error\r\n");
}
printf("open fifo ok\r\n");
//write
char buf[BUF_SIZE]={0};
read(fd,buf,BUF_SIZE);
printf("buf:%s\r\n",buf);
close(fd);
return 0;
}
2.无名管道
管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系

#include<stdio.h>
#include <unistd.h>
#include <string.h>
#define BUF_SIZE 20
int main()
{
//pipe
int fd[2]={0};
if(0!=pipe(fd))
{
return -1;
}
printf("pipe ok\r\n");
//fork
pid_t pid=fork();
if(-1==pid)
{
return -1;
}
//pid 0
char buf[BUF_SIZE]={0};
if(0==pid)
{
close(fd[0]);
printf("child process,pid=%d\r\n",getpid());
strcpy(buf,"child to parent");
write(fd[1],buf,BUF_SIZE);
}
//pid 1
else
{
printf("parent process,ppid=%d\r\n",getppid());
close(fd[1]);
read(fd[0],buf,BUF_SIZE);
printf("buf:%s\r\n",buf);
}
return 0;
}
3.信号
信号通信是在软件层面上对中断的一种模拟(异步通信)一般用于内核进程和用户进程进行系统信息交互
中断:在程序执行的过程中插入了另外一段程序的执行过程,该段程序执行完后,回到中断点续原来的程序
信号:有64个信号 kill -l查看;Linux系统中的常见信号 进程对信号的处理方式:忽略(SIGKILL/SIGSTOP不能忽略) 捕获 采用默认处理 alarm(2)--->SIGALRM--->默认动作(终止当前进程)
alarm()是一个闹钟函数,它可以在进程中设置一个定时器当定时时间到时,内核就向当前进程发送SIGALRM信号。
pause():挂起当前进程,等待一个信号的到来
raise(int sig);发送信号
signal(int ,void (*sighandler_t)(int));接收信号
异步通信:通信中接受方并不知道信号(数据)什么时候会到达,当前进程一直准备着接受信号(数据)同时也在做自己的事情一旦信号(数据)到达接受处理。所以是非阻塞模式。
同步通信:发送方发数据,接收方接数据,双方需要在很短的时间内完成数据交换,否则会造成一方阻塞。所以是阻塞模式。

浅浅的模拟一下
#include<stdio.h>
#include <unistd.h>
#include <signal.h>
void SigFunc(int signo)
{
printf("test alarm:%d\r\n",signo);
}
int main()
{
printf("hello\r\n");
signal(SIGALRM,SigFunc);
alarm(2);//两秒后发送SIGALRM信号,调用SigFunc函数
printf("aaaaaaaa\r\n");
pause();//挂起当前进程,等待一个信号的到来防止信号没来程序推出了
printf("world\r\n");
return 0;
}
4.信号量(效率太低弃用了不做过多介绍)
p操作(wait):申请一个单位资源,进程进入。
v操作(signal):释放一个单位资源,进程出来。
信号量S≥0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。而执行一个V操作意味着释放一个单位资源,因此S的值加1;若S≤0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。
ipc通讯

1.共享内存
共享内存是在物理内存开辟一段空间,映射到进程虚拟地址空间当中,进程通过操作自己进程虚拟地址空间当中的虚拟地址,来操作共享内存。

共享内存的特点:
(1)共享内存是双向通信(全双工)。
(2)共享内存是IPC通信方式中速度最快的。它的数据传输不需要通过内核,直接在物理内存上进行通信。
(3)共享内存不支持同步与互斥
(4)共享内存的生命周期随进程
(5)共享内存可用于任意多个进程,支持随机访问
往共享内存中写数据
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#define SHM_SIZE 20
int main()
{
/*
ftok()--->创建一个key值,用于通信双方找到对方,是一个外部标识符
参数1:文件名或路径名,该文件或路径必须存在
参数2: 0~255 pro_id(计划ID)
返回值:key值,用来通信双方找到对方的标识,是外部标识
*/
//ftok
key_t key=ftok(".",11);
if(-1==key)
{
return -1;
}
printf("ftok ok\r\n");
/*
shmget()--->创建一片共享内存(3-4G),得到标识共享内存区域的标志,此标志是内部标志
参数1:key值,或者是IPC_PRIVATE(创建的共享内存只能用于具有亲缘关系的进程间通信)
参数2:要创建的共享内存的大小
参数3:IPC_CREAT和共享内存的访问权限
返回值:标识共享内存的标识符shmID(int)
*/
//shm_get
int shmID=shmget(key,SHM_SIZE,IPC_CREAT|0666);
if(-1==shmID)
{
return -1;
}
printf("shmget ok\r\n");
/*
shmat()----->将本地的一块的内存和共享内存建立映射
参数1:共享内存的标识符
参数2:本地内存地址(0~3G间),或NULL----》自己找空闲的未用的地址
参数3: 0:共享内存可读写
SHM_RDONLY,共享内存只读
返回值:映射后的本地内存地址(void *)
*/
//shm_mat
void *p=shmat(shmID,NULL,0);
if((void *)-1==p)
{
shmctl(shmID,IPC_RMID,NULL);
return -1;
}
printf("ahmat ok\r\n");
//memmet
memset(p,'a',SHM_SIZE);
/*
shmdt()-->取消映射
参数1 :本地地址
*/
shmdt(p);
/*
shmctl()---->控制共享内存
参数1:要操作的共享内存标识符
参数2:IPC_STAT,获取共享内存属性
IPC_SET,设置共享内存属性
IPC_RMID,删除共享内存
参数3:buf,当第二个参数是IPC_STAT或IPC_SET时,此buf放共享内存的属性
当第二个参数是IPC_RMID时,此参数为NULL
*/
//shmctl(shmID,IPC_RMID,NULL);
return 0;
}
往共享内存中读数据
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#define SHM_SIZE 20
int main()
{
key_t key = ftok(".",11);
int shgid = shgget(key,SHM_SIZE,0777);
if( shgid == -1)
{
perror("shgget is error");
retrun -1;
}
printf("shgget is ok\n");
(void*) p = shmmat(shgid,NULL,0);
if((void *)-1==p)
{
shmctl(shmID,IPC_RMID,NULL);//删除共享内存
return -1;
}
for(int i = 0; i < 20; i++)
{
print("%c",p+i);
}
printf("\n");
memset(p,"0",SHM_SIZE);
shmdt(p);//取消映射
shmctl(shmID,IPC_RMID,NULL);//删除共享内存
}
2.消息队列
特点:
流量削峰:主要用于在高并发情况下,业务异步处理,提供高峰期业务处理能力,避免系统瘫痪
应用解耦:主要用于当一个业务需要多个模块共同实现,或者一条消息有多个系统需要对应处理时,只需要主业务完成以后,发送一条MQ,其余模块消费MQ消息,即可实现业务,降低模块之间的耦合
异步通信:主业务执行结束后从属业务通过MQ,异步执行,减低业务的响应时间,提高用户体验。
详情:

1、消息队列中的消息是有类型的。 (比如说快递柜里面有那么多快递,我到底取哪个快递)
2、消息队列中的消息是有格式的。 (发送的消息呢,要按照一定的格式组织数据包,比如:消息类型+消息内容)
3、消息队列可以实现消息的随机查询。消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取。
4、消息队列允许一个或多个进程向它写入或者读取消息。
5、与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除。
6、每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。
7、只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队列,消息队列会一直存在于系统中。
消息发送
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#define BUF_SIZE 10
struct mybuf
{
long m_type;
char m_buf[BUF_SIZE];
};
int main()
{
//ftok
key_t key=ftok(".",18);
if(-1==key)
{
return -1;
}
printf("ftok ok\r\n");
//msgget
int msgID=msgget(key,IPC_CREAT|0666);
if(-1==msgID)
{
return -1;
}
printf("create msgID ok\r\n");
//msgsnd
struct mybuf buf;
buf.m_type=2;
strcpy(buf.m_buf,"hello");
if(0==msgsnd(msgID,(void *)&buf,BUF_SIZE,0))
{
printf("send data ok:%s\r\n",buf.m_buf);
}
return 0;
}
接收消息
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#define BUF_SIZE 10
struct mybuf
{
long m_type;
char m_buf[BUF_SIZE];
};
int main()
{
//ftok
key_t key=ftok(".",18);
if(-1==key)
{
return -1;
}
printf("ftok ok\r\n");
//msgget
int msgID=msgget(key,IPC_CREAT|0666);
if(-1==msgID)
{
return -1;
}
printf("create msgID ok\r\n");
//msgsnd
struct mybuf buf;
memset(&buf,0,BUF_SIZE);
int ret=msgrcv(msgID,(void *)&buf,BUF_SIZE,0,0);
if(ret>0)
{
printf("recv data ok:%s %d\r\n",buf.m_buf,ret);
}
return 0;
}
3.信号灯(信号量)
信号量是一种并发控制机制,用于在多线程或多进程环境中协调和控制对共享资源的访问(pv操作)

直接上代码吧,这个用的少实际情况中,可以用于非亲缘进程
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <linux/ipc.h>
#include <linux/sem.h>
#include <stdlib.h>
int main()
{
//create semaphore
key_t key = ftok(".", 28);
if (-1 == key)
{
return -1;
}
int iSemID = semget(key, 1, IPC_CREAT | 0666);
if (-1 == iSemID)
{
return -1;
}
printf("create ok\r\n");
//set value
union semun stSetVal;
stSetVal.val = 1;
if (-1 == semctl(iSemID, 0, SETVAL, stSetVal))
{
semctl(iSemID, 0, IPC_RMID);
return -1;
}
printf("set val ok\r\n");
//fork
struct sembuf stBuf;
pid_t pid = fork();
if (-1 == pid)
{
semctl(iSemID, 0, IPC_RMID);
return -1;
}
//pid 0
if (0 == pid)
{
//p
stBuf.sem_num = 0;
stBuf.sem_op = -1;
stBuf.sem_flg = SEM_UNDO;
semop(iSemID, &stBuf, 1);
printf("child process\r\n");
sleep(5);
//v
stBuf.sem_num = 0;
stBuf.sem_op = 1;
stBuf.sem_flg = SEM_UNDO;
semop(iSemID, &stBuf, 1);
exit(5);
}
//pid > 0
else
{
sleep(1);
//p
stBuf.sem_num = 0;
stBuf.sem_op = -1;
stBuf.sem_flg = SEM_UNDO;
semop(iSemID, &stBuf, 1);
printf("parent process\r\n");
//v
stBuf.sem_num = 0;
stBuf.sem_op = 1;
stBuf.sem_flg = SEM_UNDO;
semop(iSemID, &stBuf, 1);
wait(NULL);
semctl(iSemID, 0, IPC_RMID);
}
}
信号量和信号的区别:
1.信号:(signal)是一种处理异步事件的方式。信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程外,还可以发送信号给进程本身。
2.信号量:(Semaphore)进程间通信处理同步互斥的机制。是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。
简单地说,信号就是一种异步通信,通知进程某种事件的发生;信号量是进程/线程同步与互斥的一种机制,保证进程/线程间之间的有序执行或对公共资源的有序访问
BSD
套接字(socket)网络编程再说
进程间的同步和互斥
进程同步: 对多个进程在执行次序上进行协调,使并发执行的各进程间能按照一定的规则共享系统资源(内存打印机等),以免进程无序争夺资源而导致系统混乱。
进程互斥: 某一时刻不允许多个进程同时访问临界资源,只能单个进程访问
线程模块
基础线程知识
直接上代码吧,主要讲一下线程互斥(信号量和互斥锁),信息交互
#include<stdio.h>
#include <pthread.h>
#include <string.h>
#include <semaphore.h>
#define BUF_SIZE 20
pthread_mutex_t mutex;
sem_t sem1;
sem_t sem2;
void *ThreadFunc1(void *arg)
{
int i=10;
while(i--)
{
sem_wait(&sem1);//p
printf("hello\r\n");
sleep(1);
sem_post(&sem2);//v
}
//pthread_exit(NULL);
}
void *ThreadFunc2(void *arg)
{
int i=10;
while(i--)
{
sem_wait(&sem2);
printf("world\r\n");
sleep(1);
sem_post(&sem1);
}
//pthread_exit(NULL);
}
void *ThreadFunc3(void *arg)
{
if(NULL==arg)
{
return (void *)NULL;
}
char *p=(char *)arg;
pthread_mutex_lock(&mutex);//加锁
static char buf[BUF_SIZE]={0};
int i=0;
for(;i<9;i++)
{
buf[i]=p[i];
usleep(5000);
}
printf("%s\r\n",buf);
pthread_mutex_unlock(&mutex);//解锁
//pthread_mutex_destroy(&mutex);//销毁锁
}
int main()
{
pthread_t tID1=-1;
pthread_t tID2=-1;
pthread_t tID3=-1;
pthread_t tID4=-1;
char arr1[]="abcdefghi";
char arr2[]="123456789";
if(-1==sem_init(&sem1,0,1)||-1==sem_init(&sem2,0,0))//初始化信号量
{
return -1;
}
if(0!=pthread_create(&tID1,NULL,ThreadFunc1,NULL))
{
printf("create pthread1 error\r\n");
return -1;
}
if(0!=pthread_create(&tID2,NULL,ThreadFunc2,NULL))
{
printf("create pthread2 error\r\n");
return -1;
}
if(-1==pthread_mutex_init(&mutex,NULL))
{
return -1;
}
if(0!=pthread_create(&tID3,NULL,ThreadFunc3,(void *)arr1))
{
printf("create pthread3 error\r\n");
return -1;
}
if(0!=pthread_create(&tID4,NULL,ThreadFunc3,(void *)arr2))
{
printf("create pthread4 error\r\n");
return -1;
}
pthread_join(tID1,NULL);
pthread_join(tID2,NULL);
pthread_join(tID3,NULL);
pthread_join(tID4,NULL);
return 0;
}
控制线程相关函数:
pthread_count_t cond = PTHREAD_COND_INITALIZER;//定义条件变量并且初始化
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
功能:阻塞线程,让线程进入到休眠状态,并释放锁
参数:
@param cond : 条件变量
@param mutex : 互斥锁变量
int pthread_cond_broadcast(pthread_cond_t *cond);
功能:唤醒所有等待的线程
参数:
@param cond : 条件变量的指针
int pthread_cond_signal(pthread_cond_t *cond);
功能:唤醒一个等待的线程
参数:
@param cond : 条件变量的指针
线程池
网络编程(先看下面ip、tcp、udp协议吧)
网络编程就结合多线程多进程写一篇吧,连带粘包处理问题一起讲
sever.cpp
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
int server_init(const char *ipstr, ushort port, int backlog)
{
//创建流式IPv6套接字
int s = socket(AF_INET6, SOCK_STREAM, 0);
if(-1 == s){
perror("socket");
return -1;
}
//初始本地地址结构
struct sockaddr_in6 addr = {
.sin6_family = AF_INET6, //IPv6
.sin6_port = htons(port), //服务器端口号
//.sin6_scope_id = 0,
};
//服务器ip,in6addr_any是一个宏,可以是本地任意IP
//给监听套接字绑定的IP是in6addr_any,有两个好处
//1.本地有多个网卡,那么客户端可以通过任意网卡连接的网络来连接服务器
//2.服务器运行在不同主机上,不用改代码重新编译,把当前主机的IP告诉客户端就可以了
//弊端:由于没有指定唯一IP,故此,网络收发包的时候要路由下(找哪个网卡来收发数据)
addr.sin6_addr =in6addr_any;
if(ipstr){
if(inet_pton(AF_INET6, ipstr, addr.sin6_addr.s6_addr16) <= 0){
addr.sin6_addr = in6addr_any;
}else{
addr.sin6_scope_id = 0;//网卡的id
}
}
//地址结构的长度
socklen_t len = sizeof(addr);
//给套接字绑定本地ip和端口
if(0 > bind(s, (struct sockaddr *)&addr, len)){
perror("bind");
goto ERR_STEP;
}
//设置监听数
if(0 > listen(s, backlog)){
perror("listen");
goto ERR_STEP;
}
return s;
ERR_STEP:
close(s);
return -1;
}
int main()
{
int s = server_init(NULL, 59999, 10);
if(0 > s){
return -1;
}
printf("wait for client.\n");
struct sockaddr_in6 c_addr;
//c_len输入参数:告诉accept地址有多少字节数;输出参数:实际接收的字节数
socklen_t c_len = sizeof(c_addr);
int rws = accept(s, (struct sockaddr *)&c_addr, &c_len);
if(0 > rws){
perror("accept");
goto ERR_STEP;
}
//把客户端的IP转成字符串IP并打印
char ipstr[INET6_ADDRSTRLEN];
if(inet_ntop(AF_INET6, c_addr.sin6_addr.s6_addr16, ipstr, INET6_ADDRSTRLEN)){
printf("client ip : %s\n", ipstr);
}
//打印客户端的端口
printf("client port : %u\n", ntohs(c_addr.sin6_port));
//如果知道发送对端发送的字节,使用MSG_WAITALL就可以解决粘包问题
//强调:MSG_WAITALL不是解决粘包问题用的
//熟悉粘包造成原因:内核协议层会把应用传递的缓存数据依据协议拆包、组包,从而造成接收端少收,多收
//从而造成粘包
#if 0
while(1){
#define MAX (1024*1024)
static char buf[MAX];
//接收数据:从套接字接收缓存读出数据
//使用MSG_WAITALL标志:表示,想读出MAX字节数据,则一定要读出才会返回
//否则返回就是出错或连接断开了(阻塞)
int num = recv(rws, buf, MAX, MSG_WAITALL);
if(-1 == num){
//本地网络出现问题
perror("read");
break;
}else if(0 == num){
//连接断开
printf("link off\n");
break;
}
printf("recv %d bytes\n", num);
}
#else
while(1){
#define MAX (100)
char buf[MAX];
int size;
//rws是读写套接字:套接字是文件描述符,读"文件"就是从套接字缓存读出数据,就是网络接收数据
if(4 != recv(rws, &size, 4, MSG_WAITALL)){
printf("recv head fail.\n");
break;
}
size = ntohl(size);
int num;
while(size){
if(size > MAX){
num = recv(rws, buf, MAX, 0);
if(0 >= num){
//本地网络出现问题
printf("read context fail.\n");
goto ERR_STEP1;
}
}else{
num = recv(rws, buf, size, 0);
if(0 >= num){
//本地网络出现问题
printf("read context fail.\n");
goto ERR_STEP1;
}
}
printf("recv %d bytes\n", num);
size -= num;
}
printf("recv done\n");
}
#endif
close(rws);
ERR_STEP:
close(s);
printf("server is leave.\n");
return 0;
}
client.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
// app 127.0.0.1 59999 0
//num : 4
//arg[0] ---> "app"
//arg[1] ---> "127.0.0."
//arg[2] ---> "59999"
//arg[3] ---> "0"
int main(int num , char *arg[])
{
char *ipstr = "::1";
ushort port = 59999;
uint32_t id = 0;
if(4 == num){
ipstr = arg[1];
port = atoi(arg[2]);
id = atoi(arg[3]);
}
int s = socket(AF_INET6, SOCK_STREAM, 0);
if(-1 == s){
perror("socket");
return -1;
}
//初始化服务器的地址结构
struct sockaddr_in6 addr = {
.sin6_family = AF_INET6, //IPv6
.sin6_port = htons(port), //服务器的端口号
.sin6_scope_id = id, //网卡ID
};
//把字符串ip装换成整型ip,并填充到地址结构
inet_pton(AF_INET6, ipstr, addr.sin6_addr.s6_addr16);
socklen_t len = sizeof(addr);
printf("port : %u , ip : %s, id: %d\n", port, ipstr, id);
if(0 > connect(s, (struct sockaddr *)&addr, len)){
perror("connect");
goto ERR_STEP;
}
printf("connected.\n");
#if 0
while(1){
#define MAX (1024*1024)
static char buf[MAX];
//发送数据:向传输层发送队列写入数据
int size = send(s, buf, MAX, 0);
if(0 >= size){
printf("link down.\n");
break;
}
printf("snd %dbytes done.\n", size);
}
#else
while(1){
#define MAX (1024*1024)
static char buf[MAX];
int size = MAX;
int num = htonl(size);
if(4 != send(s, &num, 4, 0)){
printf("snd head fail. link down.\n");
break;
}
printf("snd head done.\n");
//阻塞版:有多少就要发多少
if(size != send(s, buf, size, 0)){
printf("link down.\n");
break;
}
printf("snd %dbytes done.\n", size);
}
#endif
ERR_STEP:
close(s);
printf("client is leave.\n");
}
IO多路复用模块
阻塞非阻塞和轮询
1、阻塞与非阻塞
阻塞:当发起Input/Output的时候,I/O的条件不满足,则休眠等,直到条件满足,完成I/O。也叫条件休眠
非阻塞:当发起I/O的时候,I/O的条件不满足,则立即返回错误码EAGAIN;条件满足,则完成完成I/O。
2、阻塞与轮询的区别
轮询:忙等,耗费CPU时间
阻塞:休眠等,不耗费CPU时间
----------------------------------------------------------------------------------------------
譬如键盘:
1、使用系统调用时
getchar()
^
|
V
read(...){
if(输入缓存没有数据){
if(非阻塞){
//立即报错返回
return -EAGAIN;
}
//阻塞
wait(可读条件){
休眠
}
}
拷贝数据到参数传递的输出缓存;
}
2、键盘输入缓存是否有数据可以读出用户程序并不知道
read() = wait(输入缓存可读)--->read(从输入缓存)
=========================================================================
总结
非阻塞IO模型,是一个线程一个socket,需要轮询的通过IO系统调用来判断数据是不是准备好了,这个模型会导致IO API的频繁调用,会浪费CPU时间片,并且客户端连接多的时候,线程开启太多,服务端压力很大,为此使用IO复用来代替非阻塞IO判断数据是不是准备好了(内核代替判断),并且一个线程里面的一个IO复用可以挂载多个IO句柄,减少线程开辟.
IO多路复用原理
操作系统中IO多路复用中多路就是多个TCP连接,复用就是指复用一个或少量线程,理解起来就是多个网络连接的IO事件复用一个或少量线程来处理这些连接。一句话概括就是,IO多路复用就是复用一个线程处理多个IO请求。

举例

IO模型
模型一(阻塞)
阻塞IO: 在内核将数据准备好之前, 系统调用会一直等待. 所有的套接字, 默认都是阻塞方式

模型二(非阻塞)
非阻塞IO: 如果内核还未将数据准备好, 系统调用仍然会直接返回, 并且返回EWOULDBLOCK错误码。

模型三(信号驱动)
信号驱动IO: 内核将数据准备好的时候, 使用SIGIO信号通知应用程序进行IO操作。

模型四(多路转接)
IO多路转接: 虽然从流程图上看起来和阻塞IO类似. 实际上最核心在于IO多路转接能够同时等待多个文件描述符的就绪状态。

模型五(异步IO)
异步IO: 由内核在数据拷贝完成时, 通知应用程序(而信号驱动是告诉应用程序何时可以开始拷贝数据)。

1.select简单讲一下内部机制直接上代码
select原理
1.服务端文件描述符lfd(监听套接字)和cfd(通用描述符)
lfd是服务器端调用socket()函数创建的
sock = socket(PF_INET, SOCK_STREAM, 0);
上面的sock会传入listen函数的第一个参数,使得sock成为了监听套接字lfd——所以也相当于是listen的作用使得服务器套接字成为了监听套接字,之前没有指定具体功能。
int listen(int sock, int backlog);//成功时返回0,失败返回-1
客户端请求连接的函数是connect();创建的并不是cfd,因为cfd和lfd是针对服务器说的。客户端那边就一种文件描述符,服务器端有两种,所以加以区分
int connect(int sock, struct sockaddr* servaddr, socklen_t addrlen);//成功时返回0,失败返回-1
//sock,是客户端程序socket()函数创建的套接字,传入这里
//servaddr是保存目标服务器端地址信息的结构体变量的地址serv_addr
//addrlen是sizeof(servaddr),即指针变量长度
服务器接收客户端的请求,需要调用accept();这个accept()返回cfd,即负责与客户端进行I/O的套接字文件描述符
int accept(int sock, struct sockaddr* addr, socklen_t* addrlen);//成功时返回cfd,失败返回-1
//第一个参数传入lfd
//第二个参数指向保存了发起连接请求的客户端地址信息
总结:
lfd就是服务器端用于监听有没有客户端连接的套接字,或者说监听有没有来自客户端的connect();而cfd就是负责连接客户端,看看有没有来自客户端的read() / write()这种I/O请求;只有先lfd监听到客户端的连接请求,然后答应连接accept(),才会创建出cfd。
2. selcet概念及作用
如下图 select之于服务器server,相当于秘书之于老板,select是由内核提供的。之前没有select时,server要一直accept()阻塞,等待有客户端的连接。有了select之后,相当于给各个客户端留电话,谁有事就给秘书打电话,然后秘书告诉老板去调用accept(),创建cfd,和客户端建立连接。select自己不会创建出cfd,与客户端连接。所以select做的就是监听,lfd。其实请求连接本质是一个读事件,只是读到的数据是连接请求。既服务器不再像以前那样一直accept()阻塞,在等着有客户端连接而是让select监听到有连接请求事件之后再调用accept()。

3. 工作流程如下图
select监控集合就是文件描述符的位域有最大大小限制(1024),工作流程就是创建集合;把集合交给内核轮询,内核返回是否可操作结果,再由应用程序轮询

select函数
1.select函
#include <sys/select.h>
/*
* 功能:通知内核监测集合,并返回结果
* 参数:
int nfds - 文件描述符最大值加1
fd_set *readfds -
输入参数:读集合,告诉内核要监测的对象读事件
输出参数:读集合,返回值结果,没有发生位置0,发生的位置1
fd_set *writefds-
输入参数:写集合,告诉内核要监测的对象写事件
输出参数:写集合,返回值结果,没有发生位置0,发生的位置1
fd_set *exceptfds-
; 输入参数:异常集合,告诉内核要监测的对象异常事件
输出参数:异常集合,返回值结果,没有发生位置0,发生的位置1
struct timeval *timeout - 超时时间结构
0 - 非阻塞
>0且时间值合理 - 超时值
很长很长的是时间值 - 阻塞
* 返回值:
成功 - >0 事件发生的数量
超时 =0
失败 - -1,错误码保存在errno
*/
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
参数一: int nfds,一组绑定到fd_set上的socket的最大句柄数值+1,比如说,绑定了句柄20,30,40,则nfds = 40+1。linux下要严格执行,但是win下这个nfds没有意义,win自己忽略不计,但是为了保证平台兼容,也会找到最大句柄数,并+1传入,因为win下的socket句柄并不是整型。
tip: 句柄不是内核对象的地址,而是内核句柄表中的索引
参数二: 检测一组socket的读事件的时候,将socket传入readfds
参数三: 检测一组socket的写事件的时候,将socket传入writefds
参数四: 检测一组socket的异常的时候,将socket传入exceptfds,一般设置为NULL
参数五: 超时时间,可以设置为NULL、0、>0三种情况:
为NULL的时候,select会一直等待,直到绑定到上面的某个socket有事件,会返回,返回值会告诉你有几个socket有事件
为0的时候,秒和微秒都是0,select不管有没有事件都会返回,有返回事件数,没有也是直接返回,不会阻塞。
>0的时候,在设定时间内,有事件就会返回,没有事件就等到这么长时间再返回。
如果三个fd_set都没有事件,且超时时间设置为NULL,就会永久阻塞.
返回值: 返回值是返回有有事件的socket数量,如果三个fd_set都设置,那么有两个有事件,返回2,只有一个有事件返回1,如果三个都不设置就是个超时函数。
2.select函数的相关函数来操作fd_set

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <net/if.h>
/*
* 功能:打开“文件”(两个参数版本),创建打开“文件”(三个参数版本)
* 参数:
const char *pathname - 要被打开的文件名
“文件”:普通文件(存储在外存上的数据文件)
目录(归类普通文件)
符号链接(软/硬链接)
管道
字符设备(譬如:输入输出设备(键盘、显示设备))
块设备(譬如:硬盘、U盘,存储数据的设备,有分区及文件系统)
套接字 -socket()创建
int flags - 标志
O_NONBLOCK 非阻塞,反之不加就是阻塞
mode_t mode - 读写权限
* 返回值:
成功 - 文件描述符
失败 - -1,错误码保存在errno
*/
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
#include <unistd.h>
#include <fcntl.h>
/*
* 功能:操作文件描述
* 参数:
int fd - 被操作的文件描述符
int cmd, ... - 命令码及参数
F_GETFL - 得到文件描述符的标志(标志是整型),返回值是标志
F_SETFL - 把参数指定的标志设置到文件描述符(标志是整型)
* 返回值:
失败 - -1,错误码保存在errno
*/
int fcntl(int fd, int cmd, ... /* arg */ );
--->
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, unsigned long arg);
/*
* 功能:得到指定网卡的ID
* 参数:
const char *ifname - 网卡(网络适配器)的物理接口名
使用ifconfig命令,列表就是网卡
如下范例:其中ens33就是网卡的物理接口名
# ifconfig
ens33 Link encap:Ethernet HWaddr 00:0c:29:1f:68:17
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:326106 errors:286 dropped:23 overruns:0 frame:0
TX packets:97178 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:282122016 (282.1 MB) TX bytes:38913176 (38.9 MB)
Interrupt:19 Base address:0x2000
* 返回值:
成功 - 指定网卡的ID
失败 - 0
*/
unsigned int if_nametoindex(const char *ifname);
/*把fd位置0:从集合中删除fd*/
void FD_CLR(int fd, fd_set *set);
/*测试fd位是否为1:测试fd是否在集合中*/
int FD_ISSET(int fd, fd_set *set);
/*把fd位置1:把fd加入集合*/
void FD_SET(int fd, fd_set *set);
/*把所有置0:清空集合*/
void FD_ZERO(fd_set *set);
关于select传入fd_set的过程:
1、应用程序调用select之后,会将fet_set拷贝到内核空间,应用程序会阻塞等待函数返回。
2、内核根据fd_set得到需要处理的句柄,根据句柄是否准备好数据,给fd_set置位。
3、线程/进程被唤醒,拿到内核处理过的fd_set,就可以通过FD_ISSET来判断套接字是不是准备好了。
3.select实现io多路复用的sever
1.创建网络套接字socket
2.绑定bind
3.监听listen
4.FD_ZERO初始化监听集合
5.把监听套接字加入监听集合FD_SET
6.select 内核轮询测试监听集合
7.等连接FD_ISSET判断套接字是否可读,既是否有新的连接进入
8.accept读出连接并建立连接
9.把连接加入集合
10.正常测试通信
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/tcp.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/select.h>
int server_init(ushort port, int backlog)
{
int s = socket(AF_INET6, SOCK_STREAM, 0);
if(0 > s){
perror("socket");
return -1;
}
struct sockaddr_in6 addr = {
.sin6_family = AF_INET6,
.sin6_port = htons(port),
.sin6_addr = in6addr_any,
};
socklen_t len = sizeof(addr);
if(0 > bind(s, (struct sockaddr *)&addr, len)){
perror("bind");
goto ERR_STEP;
}
if(0 > listen(s, backlog)){
perror("listen");
goto ERR_STEP;
}
return s;
ERR_STEP:
close(s);
return -1;
}
int main()
{
int s = server_init(59999, 10);
if(0 > s){
return -1;
}
//准备两个集合:
// fdset用于保存文件描述符(保存有无不是保存值)
// testset:用于测试的集合
fd_set fdset, testset;
//初始化保存集合
FD_ZERO(&fdset);
//把监听套接字加到保存集合
FD_SET(s, &fdset);
//计算文件描述符最大值+1
//因为集合最多可以有1024个文件描述符,那么maxfd的作用是让测试到最大的文件描述符就停止测试了
//这个就少测试了,相对效率高
int maxfd = s+1;
printf("wait for a client ...\n");
while(1){
//初始化测试集合
//因为select每次要修改测试集合告诉我们监测对象的事件发生实际情况
//有事件发生的还在集合中,没有事情发生的监测对象被从集合中清除了
//故此下次监测的时候,集合得重新初始化一次
testset = fdset;
//把测试集合testset传递给内核轮询测试,并等结果
//有且只要有一个文件描述符可读,则select就被唤醒返回,且通测试集合testfd
//告诉应用层结果:即,发生事情的文件描述符还在集合中,没有发生事情的被清除了
int ret = select(maxfd, &testset, NULL, NULL, NULL);
if(0 >= ret){
printf("select error.\n");
break;
}
//因为只有一个连接后,测试集合中就可能既有监听套接字,也有读写套接字
//故此,都得测试下,看是新连接进来了,还读写套接字可读了
if(FD_ISSET(s, &testset)){ //有新连接来了
struct sockaddr_in6 addr;
socklen_t len = sizeof(addr);
//读出连接
int rws = accept(s, (struct sockaddr *)&addr, &len);
if(0 > rws){
perror("accept");
goto ERR_STEP;
}
char ipstr[INET6_ADDRSTRLEN];
printf("socket %d: client %s : %u entry.\n", rws,
inet_ntop(AF_INET6, &addr.sin6_addr, ipstr, INET6_ADDRSTRLEN),
ntohs(addr.sin6_port));
//把连接的读写套接字加到保存集合
FD_SET(rws, &fdset);
//重新计算文件描述符最大值+1
maxfd = (maxfd <= rws) ? (rws+1) : maxfd;
}
for(int i = s+1; i < maxfd; i++){ //用户轮询是哪个读写套接字可读了:有多个连接后,就会这样
if(FD_ISSET(i, &testset)){ //测试指定读写套接字是否可读:true可读
#define MAX 512
char buf[MAX+1];
//非阻塞读:select已经等到i可读,那么调用recv就必须要读到东西
//如果读不到一定出问题了,连接是断开的
int num = recv(i, buf, MAX, MSG_DONTWAIT);
if(0 >= num){ //如果连接是断开的
//把该读写套接字从保存集合中清除
FD_CLR(i, &fdset);
//关闭该文件描述符
close(i);
//找出保存集合中最大的文件描述符
int max = s;
for(int i = s+1; i < maxfd; i++){
if(FD_ISSET(i, &fdset)){
max = i;
}
}
//重新计算文件描述符+1
maxfd = max+1;
printf("socket %d leave.\n", i);
}else{
buf[num] = 0;
printf("recv[socket %d] %d bytes: %s\n", i, num, buf);
}
}
}
}
ERR_STEP:
close(s);
printf("server is leave.\n");
return 0;
}
4.select的缺点:
1、监测文件描述符不是从0开始,则前面的文件描述符会被内核空测试(效率太低)
2、监测文件描述符不是连续的,则也会造成内核遇到空的文件描述符空测试
3、返回结果是修改传入集合,则集合每次都必须初始化
4、每次调用 select 函数,都需要把 fd 集合(fd_set)从用户态拷贝到内核态(内核态切换),这个开销在 fd 较多时会很大,同时每次调用 select 函数都需要在内核遍历传递进来的所有 fd,这个开销在 fd 较多时也很大;
5、单个进程能够监视的文件描述符的数量存在最大限制,在 Linux 上一般为 1024,可以通过修改宏定义然后重新编译内核的方式提升这一限制,这样非常麻烦而且效率低下;
6、select 函数在每次调用之前都要对传入参数进行重新设定,这样做也比较麻烦。因为select函数返回后,会改变fd_set数组的标志为,如果是发送了事件标志为1,没发送标志为0.
7、如果有事件发生,select需要轮询所有挂载的socketfd,取寻找是哪个触发了事件。无差别轮询,时间复杂度O(n), 挂载的socketfd越多,效率越低。
5.select的适用场景
监测文件描述是连续的,且最好是从0开始,监测对象不要太多,这样性能相对较好
2.poll简单讲一下内部机制直接上代码
poll工作原理

poll是一个结构体数组多大取决于自己定义的,工作流程和select基本一致,就是没有大小限制,也需要内核和应用程序轮询;
poll结构体和函数介绍
A、数据结构
组织管理监测对象及事件的结构体
struct pollfd {
int fd; /* 输入参数:监测对象file descriptor */
short events;/* 输入参数:监测事件requested events:不用多个结构表示同一个监测对象的事件*/
short revents;/* 输出参数:反馈结果returned events :监测结构不用每次初始化*/
};
事件宏:
读事件:POLLIN
写事件:POLLOUT
...
B、把数据结构管理的对象及事件传递给内核
C、等事件发生[内核轮询监测对象有没有事件]
/*
* 功能:通知内核监测集合,并返回结果
* 参数:
struct pollfd *fds - 监测结构体首地址
nfds_t nfds - 监测结构体的数量
int timeout - 超时时间(毫秒)
0 - 非阻塞(一般不用)
>0的合理的是时间 超时值
很长很长的是时间值 - 类似阻塞效果
* 返回值:
成功 - >0 事件发生的数量
超时 =0
失败 - -1,错误码保存在errno
*/
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
poll服务器
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/tcp.h>
#include <sys/stat.h>
#include <unistd.h>
#include <poll.h>
#include <fcntl.h>
int server_init(ushort port, int backlog)
{
int s = socket(AF_INET6, SOCK_STREAM, 0);
if(0 > s){
perror("socket");
return -1;
}
struct sockaddr_in6 addr = {
.sin6_family = AF_INET6,
.sin6_port = htons(port),
.sin6_addr = in6addr_any,
};
socklen_t len = sizeof(addr);
if(0 > bind(s, (struct sockaddr *)&addr, len)){
perror("bind");
goto ERR_STEP;
}
if(0 > listen(s, backlog)){
perror("listen");
goto ERR_STEP;
}
return s;
ERR_STEP:
close(s);
return -1;
}
int setNoblock(int fd)
{
//得到指定文件描述符的标志
int flags = fcntl(fd, F_GETFL);
if(0 > flags){
perror("fcntl");
return -1;
}
//在原有标志的基础上追加一个非阻塞标志
flags |= O_NONBLOCK;
//把标志设置回文件描述符
if(0 > fcntl(fd, F_SETFL, flags)){
perror("fcntl");
return -1;
}
return 0;
}
int sendfile(int rws, char *filp)
{
struct stat info;
if(0 > stat(filp, &info)){
perror("stat");
return -1;
}
int size = htonl(info.st_size);
if(4 != send(rws, &size, 4, MSG_NOSIGNAL)){
printf("snd %s size fail.\n", filp);
return -1;
}
printf("snd %s size %ld done.\n", filp, info.st_size);
FILE *fp = fopen(filp, "r");
if(NULL == fp){
perror("fopen");
return -1;
}
while(1){
#define RECVMAX 512
char buf[RECVMAX];
size = fread(buf, 1, RECVMAX, fp);
if(0 == size){
break;
}
if(size != send(rws, buf, size, MSG_NOSIGNAL)){
printf("snd %s context fail, link off.\n", filp);
fclose(fp);
return -1;
}
}
fclose(fp);
printf("snd %s done.\n", filp);
return 0;
}
#define INVALIDFD -1
int main()
{
int s = server_init(59999, 10);
if(0 > s){
return -1;
}
//设置监听套接字为非阻塞:这里想说明poll等连接了【等监听套接字的读事件】,accept仅仅是读出连接
setNoblock(s);
#define MAX 12
struct pollfd pfd[MAX];
//初始化结构体数组:为poll轮询监测对象及事件做准备
for(int i = 0; i < MAX; i++){
pfd[i].fd = INVALIDFD; //刚开始监测对象没有那么多,必须初始化为不可能是文件描述符
pfd[i].events = POLLIN;//读事件:等连接--等到连接-->读出连接;等接收--等到接收缓存有数据-->读出数据
}
pfd[0].fd = s; //把监听放到数组的标号0位置,这里设计文件描述符和数组标号一一对应
while(1){
printf("wait for a client ...\n");
//把结构体数组传递给内核的IO多路复用代码,等监测对象的指定事件发生
//如果数组中初始化的监测对象的事件没有一个发生,则poll休眠
//有且有一个发生了(来连接或读写套接字接收缓存有数据了),则Poll返回[有事情发生了!!!]
int ret = poll(pfd, MAX, ~0);
if(0 > ret){ //poll错了
perror("poll");
break;
}else if(0 == ret){ //超时了
printf("timeout.\n");
continue;
}
//poll返回了就是监测对象的监测事情发生了
//轮询是谁是什么事情发生了
if(pfd[0].revents & POLLIN){ // true:是连接来了
struct sockaddr_in6 addr;
socklen_t len = sizeof(addr);
//读出连接
int rws = accept(s, (struct sockaddr *)&addr, &len);
if(0 > rws){
perror("accept");
goto ERR_STEP;
}
char ipstr[INET6_ADDRSTRLEN];
printf("socket %d: client %s : %u entry.\n", rws,
inet_ntop(AF_INET6, &addr.sin6_addr, ipstr, INET6_ADDRSTRLEN),
ntohs(addr.sin6_port));
int id = rws - s;
if(id >= MAX){
printf("NO more space to save %d.\n", rws);
close(rws);
}else{
//把读写套接字放到监测数组中
pfd[id].fd = rws;
}
}
//
for(int i = 1; i < MAX; i++){ //轮询连接是后有数据来
if(pfd[i].revents & POLLIN){ //有请求来
#define BUFMAX 64
char buf[BUFMAX];
//读出来请求
if(BUFMAX == recv(pfd[i].fd, buf, BUFMAX, MSG_WAITALL)){
if('G' == buf[0]){
char *filep = buf+1;
if(!sendfile(pfd[i].fd, filep)){ //发送文件
continue;
}
}
}
printf("%d is leave.\n", pfd[i].fd);
close(pfd[i].fd);
pfd[i].fd = INVALIDFD;
}
}
}
ERR_STEP:
close(s);
printf("server is leave.\n");
return 0;
}
弊端

3.epoll简单讲一下内部机制直接上代码
epoll工作原理


1、当进程调用epoll_create时,内核创建一个红黑树,红黑树的结点包含对应的事件,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是lgn,其中n为树的高度)
而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当响应的事件发生时会调用这个回调方法,这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到就绪队列中
2、在epoll中,对于每一个事件,都会建立一个epitem结构体.
3、epoll_ctrl将要监控的文件描述符进行注册;
4、epoll_wait, 则把发生的事件复制到用户态,同时将事件数量返回给用户. 这个操作的时间复杂度
是O(1).只需检查就绪队列是否有结点。
epoll优点
1、接口使用方便: 虽然拆分成了三个函数, 但是反而使用起来更方便高效. 不需要每次循环都设置关注的文件描述符, 也做到了输入输出参数分离开
2、数据拷贝轻量: 只在合适的时候调用 EPOLL_CTL_ADD 将文件描述符结构拷贝到内核中, 这个操作并不频繁(而select/poll都是每次循环都要进行拷贝)
3、事件回调机制: 避免使用遍历, 而是使用回调函数的方式, 将就绪的文件描述符结构加入到就绪队列中, epoll_wait 返回直接访问就绪队列就知道哪些文件描述符就绪. 这个操作时间复杂度O(1). 即使文件描述符数目很多, 效率也不会受到影响.
4、没有数量限制: 文件描述符数目无上限
epoll函数
#include <sys/epoll.h>
1、epoll API设计说明
/*
* 功能:创建epoll
* 参数:
* int size - 监测对象最大数量,已经弃用,接口参数保留
* 返回值:
* 成功:epoll文件描述符
* 失败:-1,错误原因保存在errno
*/
int epoll_create(int size);
/*
* 功能:epoll链表操作函数
* 参数:
* int epfd - epoll文件描述符
* int op - 操作码
* EPOLL_CTL_ADD - 添加监测对象及事件
* EPOLL_CTL_MOD - 修改监测对象或事件
* EPOLL_CTL_DEL - 删除监测对象及事件
* int fd - 监测文件描述符
* struct epoll_event *event - 事件结构
* 返回值:
* 失败:-1,错误原因保存在errno
*/
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
/*
* 功能:等并读出结果:等监测epoll链表中登记的对象的事情是否发,发生了则通过参数输出结果
* 参数:
* int epfd - epoll文件描述符
* struct epoll_event *event - 指向保存事件结构缓存的首地址
* int maxevents - 保存缓存的最大数量
* int timeout - 超时值(单位毫秒)
* 返回值:
* >0:实际发生的数量
* =0:超时
* -1:错误,错误原因保存在errno
*/
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
===============================================================================
2、流程
创建epoll
int epoll_create(int size);
A、数据结构
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
读事件:EPOLLIN
写事件:EPOLLOUT
B、把数据结构管理的对象及事件传递给内核
//epoll链表节点添减修改
int r = epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
C、内核轮询监测对象有没有事件:没有无效测试
struct epoll_event events[MAX];//用于保存事件结果的缓存
int num = epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout)
{
while(1){
int counter = 0;//实际发生数量
for(遍历epoll链表,测试监测对象){
if(有事件发生){
保存到用户传入缓存
counter++;
}
}
if(counter){ //有且有一个对象有事件发生,则返回
return counter;
}
休眠;//监测对象有事件时,叫醒内核轮询代码,重新轮询
}
}
D、处理事件:没有无效测试
for(int i = 0; i < num; i++){
}
epoll适用场景:监测对象及事件数量不定,监测过程中会变化,且数量较多时
===============================================================================
3、IO效率
Level-triggered :水平触发,缺省模式
edge-triggered :边缘触发
通知模式:
LT模式时,事件就绪时,假设对事件没做处理,内核会反复通知事件就绪
poll/select不能设置,默认就是LT模式
ET模式时,事件就绪时,假设对事件没做处理,内核不会反复通知事件就绪
设置epoll为ET模式的宏:EPOLLET
-------------------------------------------------------------------
效率是有利有弊的:(不能简单说谁好谁坏:合适的才是好的!!!)
如果使用ET模式,要求单次把对象发生的事件处理完成,实质就是把输出(读)/输入(写)缓存读写空
那么,如果缓存数据量较大,则就会造成其他监测对象处理不及时。
epoll工作方式
epoll有2种工作方式-水平触发(LT)和边缘触发(ET)
水平触发Level Triggered 工作模式
1、epoll默认状态下就是LT工作模式.
2、当epoll检测到socket上事件就绪的时候, 可以不立刻进行处理. 或者只处理一部分.
例:只读了1K数据, 缓冲区中还剩1K数据, 在第二次调用 epoll_wait 时, epoll_wait仍然会立刻返回并通知socket读事件就绪;直到缓冲区上所有的数据都被处理完, epoll_wait 才不会立刻返回.
3、支持阻塞读写和非阻塞读写
边缘触发Edge Triggered工作模式
1、如果我们在第1步将socket添加到epoll描述符的时候使用了EPOLLET标志, epoll进入ET工作模式.
2、当epoll检测到socket上事件就绪时, 必须立刻处理
如上面的例子, 虽然只读了1K的数据, 缓冲区还剩1K的数据, 在第二次调用 epoll_wait 的时候, epoll_wait 不会再返回了;也就是说, ET模式下, 文件描述符上的事件就绪后, 只有一次处理机会
ET的性能比LT性能更高( epoll_wait 返回的次数少了很多). Nginx默认采用ET模式使用epoll.
3、只支持非阻塞的读写
4、select和poll其实也是工作在LT模式下. epoll既可以支持LT, 也可以支持ET
5、在ET模式下必须要将fd设置位非阻塞
epoll服务器
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/tcp.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <fcntl.h>
//初始化监听套接字
int server_init(ushort port, int backlog)
{
int s = socket(AF_INET6, SOCK_STREAM, 0);
if(0 > s){
perror("socket");
return -1;
}
struct sockaddr_in6 addr = {
.sin6_family = AF_INET6,
.sin6_port = htons(port),
.sin6_addr = in6addr_any,
};
socklen_t len = sizeof(addr);
if(0 > bind(s, (struct sockaddr *)&addr, len)){
perror("bind");
goto ERR_STEP;
}
if(0 > listen(s, backlog)){
perror("listen");
goto ERR_STEP;
}
return s;
ERR_STEP:
close(s);
return -1;
}
//设置指定文件描述符为非阻塞工作模式
int setNoblock(int fd)
{
//得到指定文件描述符的标志
int flags = fcntl(fd, F_GETFL);
if(0 > flags){
perror("fcntl");
return -1;
}
//在原有标志的基础上追加一个非阻塞标志
flags |= O_NONBLOCK;
//把标志设置回文件描述符
if(0 > fcntl(fd, F_SETFL, flags)){
perror("fcntl");
return -1;
}
return 0;
}
//读出连接
int getlink(int efd, int s)
{
struct sockaddr_in6 addr;
socklen_t len = sizeof(addr);
//读出连接
int rws = accept(s, (struct sockaddr *)&addr, &len);
if(0 > rws){
perror("accept");
return -1;
}
char ipstr[INET6_ADDRSTRLEN];
printf("socket %d: client %s : %u entry.\n", rws,
inet_ntop(AF_INET6, &addr.sin6_addr, ipstr, INET6_ADDRSTRLEN),
ntohs(addr.sin6_port));
//事件结构
struct epoll_event evt = {
.events = EPOLLIN,//监测读事件
.data = { //用户数据
.fd = rws,//至少要存监测对象(这里放的是什么,epoll_wait读出就是什么)
},
};
//向efd指定的epoll链表中添加节点
//其中 rws:被添加的监测对象,evt被添加的监测事件结构
if(0 > epoll_ctl(efd, EPOLL_CTL_ADD, rws, &evt)){
perror("epoll_ctl - EPOLL_CTL_ADD");
close(rws); //添加不成功则应该关闭连接
return -1;
}
return 0;
}
//接收数据
int recvdata(int efd, int rws)
{
#define MAX 512
char buf[MAX];
//从接收缓存读出数据:如果连接断开会失败
int num = recv(rws, buf, MAX-1, 0);
if(0 >= num){ //连接断开
//从epoll链表中删除指定对象
epoll_ctl(efd, EPOLL_CTL_DEL, rws, NULL);
close(rws); //接收不成功则应该关闭连接
printf("%d is leave.\n", rws);
return -1;
}
buf[num]= 0;
printf("socket %d, recv %dbytes: %s\n", rws, num, buf);
return num;
}
int main()
{
//创建epoll
int efd = epoll_create(1);
if(0 > efd){
perror("epoll_create");
return -1;
}
//初始化监听套接字
int s = server_init(59999, 10);
if(0 > s){
goto ERR_STEP;
}
printf("epoll = %d; listen = %d\n", efd, s);
//设置监听套接字为非阻塞
if(0 > setNoblock(s)){
goto ERR_STEP1;
}
//刚开始只有监听套接字被监测,把监听套接字插入到epoll链表
//监测事件结构
struct epoll_event evt = {
.events = EPOLLIN,//监测事件
.data = { //用户数据
.fd = s,//至少要存监测对象
},
};
//s:监测对象,evt是监测事件结构
if(0 > epoll_ctl(efd, EPOLL_CTL_ADD, s, &evt)){
perror("epoll_ctl - EPOLL_CTL_ADD");
goto ERR_STEP1;
}
while(1){
printf("wait for a client ...\n");
#define EMAX 6
struct epoll_event evts[EMAX];
//从efd[指定epoll]监测的对象及事件等到结果,并到保存evts中,返回值是实际发生的数量
int num = epoll_wait(efd, evts, EMAX, 3000);
if(0 > num){
perror("epoll_wait");
break;
}else if(0 == num){
printf("timeout.\n");
continue;
}
//每个事件处理一次:因为监测的都是读事件EPOLLIN,故此不用测试事件
for(int i = 0; i < num; i++){
//连接处理和接收数据都需要文件描述符,
//故此必须在添加事件节点时,在事件结构的用户数据放文件描述
if(s == evts[i].data.fd){ //是有连接了
getlink(efd, s);
}else{
recvdata(efd, evts[i].data.fd);
}
}
}
ERR_STEP1:
close(s);
ERR_STEP:
close(efd);
printf("server is leave.\n");
return 0;
}
4.poll、select、epoll区别
select
存储对象及事件的数据结构为位域:
缺点:
1、监测文件描述符不是从0开始,则前面的文件描述符会被内核空测试
2、监测文件描述符不是连续的,则也会造成内核遇到空的文件描述符空测试
3、返回结果是修改传入集合,则集合每次都必须初始化
4、内核和用户都要轮询
优点:占用空间小,测试速度快(&|操作比=+-这些操作要快)
适用场景:监测文件描述是连续的,且最好是从0开始,监测对象及事件不多的时候,这样性能相对较好
poll
存储对象及事件的数据结构为结构体数组:
缺点:占用空间相对位域较大,长度定长(针对监测对象是变化的情况不好)、测试相对位域耗时
用户和内核都要遍历一次才知道结果
优点:不必重复初始化监测对象,不要求监测对象是连续的,把对象和事件一一对应从而简化参数
适用场景:监测对象及事件数量固定,且数量相对select较多时,较之epoll较少
epoll
存储对象及事件的数据结构为链表:
缺点:占用空间较大,遍历相对数组、位域耗时
优点:结合函数改进,用户不用遍历,内核及用户没有无效测试,不必重复初始化监测对象
适用场景:监测对象及事件数量不定,监测过程中对象或事件可以变化,且监测对象数量较多时
各种协议(个人GB28181开发所用协议不做详细介绍)
协议模型


| OSI中的层 | 功能 | TCP/IP协议族 |
|---|---|---|
| 应用层 | 文件传输,电子邮件,文件服务,虚拟终端 | TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet |
| 表示层 | 数据格式化,代码转换,数据加密 | 无协议 |
| 会话层 | 解除或建立与别的节点联系 | 无协议 |
| 传输层 | 提供端对端的接口 | TCP,UDP,RTP,RMTP |
| 网络层 | 为数据包选择路由器 | IP,ICMP,RIP,OSPF,BGP,IGMP |
| 数据链路层 | 传输有地址的帧以及错误检测功能 | SLIP,CSLIP,PPP,ARP,RARP,MTU |
| 物理层 | 以二进制数据形式在物理媒体上传输数据 | ISO2110,IEEE802,EEE802.2 |
1.TCP UDP IP协议放一起看看(这是一切传输层协议的鼻祖)
IP协议:
IP是什么?为什么要用IP?
IP是一种 「不可靠」的 「端到端」的数据包 「传输服务」,主要实现两个功能:数据传输 和 数据分片。

如上图所示每个设备(ABCD)都有个IP,通俗来讲传输层数据就相当于包裹,网络层IP就相当于是快递中转站地址(路由器),经过不同的站点转运到目的地。
主机:配有IP地址,但是不进行路由控制的设备。但实际现在几乎不存在不进行路由控制的设备了,就连你的笔记本也会进行路由控制。
路由器:既配有IP地址,又能进行路由控制。实际现在主流的路由器已经不仅仅具有路由的功能了,它甚至具备某些应用层的功能。
节点:主机和路由器的统称
IP分类
为了便于寻址以及层次化构造网络,每个IP地址分为「网络号码」和「主机号码」两个部分,同一个物理网络上的所有主机都使用同一个网络号码。

| 类别 | IP范围 | 子网掩码 | 描述 |
| A类(1~126) | 1.0.0.1 ~ 127.255.255.254 | 255.0.0.0 | 共有126个网络,每个网络有1600万台主机,适合大规模的网络。 |
| B类(128~191) | 128.0.0.1 ~ 191.255.255.254 | 255.255.0.0 | 共有16384个网络,每个网络有6万台主机,适合中等规模的网络 |
| C类(192~223) | 192.0.0.1 ~ 233.255.255.254 | 255.255.255.0 | 共有209万个网络,每个网络有254台主机,适合小型网络。 |
| D类(224~239) | 224.0.0.0 ~ 239.255.255.255 | 组播地址 | |
| E类(240~255) | 240.0.0.0 ~ 255.255.255.254 | 保留地址 |
公有和私有ip
公有IP就是可以注册购买的IP,私有IP就是无法注册的,用于内部使用的私有地址。
IP报文

1)版本(4位):IP协议版本,目标主机按照此版本解释数据,如果目标主机使用的是其他版本,则丢弃数据报。
2)首部长度(4位):数据报协议头长度,最小值为5,最大值为15。
3)服务(8位):用于分配优先级、延迟、吞吐量以及可靠性;前3位是优先级,后面4位成为服务类型,最后1位没有定义。
4)总长度(16位):IP数据报的字节长度(协议头部和数据),其最大值为65535字节。
5)标识(16位):一个整数,数据报分段时,用于识别当前数据报。
6)标记(3位):由3位字段构成,最低位(MF)控制分段,存在下一个分段置为1,否则置0代表该分段是最后一个分段;中间位(DF)指出数据报是否可进行分段,如果为1则机器不能将该数据报进行分段;第三位即最高位保留不使用,值为0。
7)分段偏移(13位):指出数据在源数据报中的相对位置,用于重组源数据。
8)生存时间(8位):一种计数器,在丢弃数据报的每个点值依次减1直至减少为0。这样确保数据报拥有有限的环路过程(即TTL),限制了数据报的寿命。
9)协议(8位):指明上层接收数据报的协议。
10)头部校验和(16位):该字段帮助确保IP协议头的完整性。由于某些协议头字段的改变,这就需要对每个点重新计算和检验。计算过程是先将校验和字段置为0,然后将整个头部每16位划分为一部分,将个部分相加,再将计算结果取反码,插入到校验和字段中。
11)源地址(32位):源主机IP地址,该字段在IPv4数据报从源主机到目的主机传输期间必须保持不变。
12)目的地址(32位):目标主机IP地址,该字段在IPv4数据报从源主机到目的主机传输期间同样必须保持不变。
13)选项字段:不定长,最多40字节。
14)整个数据包(首部+数据域)不能超过1500字节,超过了就得分包,网络编程粘包问题讲诉了该问题。
数据分割(也就是分包原理)
IP协议负责将网络层的IP数据报(IP分组)传输到链路层,由链路层将数据封装成帧。

帧最多只能封装 1500 字节(默认)的数据,当超过上限时,就需要分割数据,放到多个数据帧中传输,到达目标主机后,再将数据重组起来。
分割和重组的操作在路由器中完成,发送方发送的信息经过路由器时,路由器会将数据分割,放到不同的数据帧中,并在帧首部的字段(MF)标明数据的顺序;当数据传输到目的路由器后,再根据帧首部的字段将信息重组,发送给目标主机
UDB协议:

UDP:用户数据报协议,是一种非面向连接的不可靠传输协议。非面向连接:发送数据,收不收无所谓。(只管发你爱收不收,发了就完事了)
UDP 为应用程序提供面向无连接的服务(仅仅提供端口号)。传输数据之前源端和目的端不需要建立连接,不需要维护连接状态,转发状态等,因此服务器可同时向多个客户端传输相同的消息(UDP 支持一对一、一对多、多对一和多对多的交互通信),UDP适用于对传输效率要求高的运用。
报文:
(伪首部是让不可靠变得可靠,通常将的udb首部就是 八字节,这里一起将)

UDP 校验:
UDP 用户数据报首部中的校验和的计算方法有些特殊。在计算校验和时,要在 UDP 用户数据报之前增加 12 个字节的伪首部。所谓“伪首部”是因为这种伪首部并不是 UDP 用户数据报真正的首部。只是在计算校验和时,临时添加在 UDP 用户数据报前面,得到一个临时的 UDP 用户数据报。校验和就是按照这个临时的 UDP 用户数据报来计算。伪首部既不向下传送也不向上递交,而仅仅是为了计算校验和。
UDP 计算检验和的方法和计算 IP 数据报首部校验和的方法类似。但不同的是:IP 数据报的校验和只检验 IP 数据报的首部,但 UDP 的校验和是把首部和数据部分一起都检验。在发送方,首先是先把全零放入首部中的校验字段和字段,再把伪首部以及 UDP 用户数据报看成是由许多 16 位的字串接起来的。若 UDP 用户数据报的数据部分不是偶数个字节,则要填入一个全零字节(但此字节不发送)。然后按照二进制反码计算出这些 16 位字的和。将此和的二进制反码写入检验和字段后,就发送这样的 UDP 用户数据报。在接收方,把收到的 UDP 用户数据报连同伪首部(以及可能的填充全零字节)一起,按二进制反码求这些 16 位字的和。当无差错时其结果应为全 1。否则就表明有差错出现,接收方就应丢弃这个 UDP 用户数据报(也可以上交给应用层,但附上出现了差错的警告)

信息交互流程

TCP协议:


TCP:传输控制协议,是一种面向连接的可靠传输协议。TCP为应用程序提供一种面向连接的、可靠的服务。(面向连接:传输前进行沟通和协商,确保互相可以/愿意发送数据)
TCP是可靠传输,哪怎么保证是可靠传输的呢?(三次握手四次挥手)
- 面向连接的传输(准备好了传)
- 最大报文段长度(一共传多少)
- 传输确认机制(TCP发送的每一个数据都要进行确认,丢没丢)
- 首部和数据的校验和(错没错)
- 重传输(若没有收到ACK,则在等一定时间后重新发送数据)
- 重排序(对分片用序列号进行重新排序)
- 流量控制(量力而行,按需传递)
TCP报文

A)源/目的端口号:表示数据是从哪个进程来,到哪个进程去;
B) 32位序号/32位确认号:32位序号针对请求数据进行的编号,32位确认序号针对应答报文(ACK报文)进行的编号。每一个ACK报文都带有对应的确认序列号,意思是告诉发送者,我已经收到了哪些数据;下一次你从哪里开始发。
C) 4位TCP报头长度:表示该TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最大长度是15 * 4 = 60字节
D) TCP报头前20个字节是固定的,后面是留给选项的,选项可有可无(它是可变的),选项可以是0个字节,最多是40个字节
E) 6位保留位:现在虽然不用,先占个位置,以后可能会用,也就是留下的可扩展空间
F) 6个特殊标志位(6位):
G) URG:紧急指针是否有效
H) ACK:在数据通信中,接收方发给发送方的一种传输类控制字符,表示发来的数据已确认接收无误。如果接收方成功的接收到数据,那么这一位会被置为1,也就是应答报文(ack报文)这一位置为1;ACK是系统内核负责的,会在收到请求之后,立即就返回。
I) PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
J) RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文段
K) SYN:请求建立连接;我们把携带SYN标识的称为同步报文段;SYN这一位如果为1说明这是一个同步报文段(尝试和对方建立连接);SYN是系统内核负责的,会在收到请求之后,立即就返回
L) FIN:通知对方本端要关闭连接了,我们称携带FIN标识的为结束报文段;FIN这一位如果为1说明这是一个结束报文段(通知对方本端要关闭连接了);FIN是应用程序负责的(代码中主动调用close),何时返回取决于我们的代码
M) 16位窗口大小:存放当前接收方接收缓冲区的剩余大小,这个字段得是在ACK为1的时候(应答报文的时候)才有效,窗口大小是动态的(实时改变);窗口大小最大不是64kb(也就是16位的最大值),选项中有窗口大小扩展因子来表示更大的值
N) 16位校验和:校验和就是为了检查数据是否出错了(网络传输过程中,受到一些干扰,是容易导致传输的数据出错的),此处的检验和不光包含TCP首部,也包含TCP数据部分。TCP校验和使用了一个比较常见的CRC算法(循环冗余校验):把TCP报文中的每个字节都进行累加,和也放到一个两个字节的数字中(加的过程中如果溢出了就溢出),最终得到的结果就是校验和;发送方发送数据的时候,就先计算一个校验和,接收方接收的时候,按照同样的规则再算一次校验和,最后看一下两次校验和是不是一样的(这里出现问题的概率还是比较小的)
O) 16位紧急指针:标识哪部分数据是紧急数据;
三次握手四次挥手模型
握手(A客户端,B服务端):
1、在第一次通信过程中,A向B发送信息之后,B收到信息后可以确认自己的收信能力和A的发信能力没有问题。
2、在第二次通信中,B向A发送信息之后,A可以确认自己的发信能力和B的收信能力没有问题,但是B不知道自己的发信能力到底如何,所以就需要第三次通信。
3、在第三次通信中,A向B发送信息之后,B就可以确认自己的发信能力没有问题。
4、 小结:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。

挥手:
第一次挥手:Clien发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
第二次挥手:Server收到FIN后,发送一个ACK给Client,Server进入CLOSE_WAIT状态。
第三次挥手: Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,发送ACK给Server,Server进入CLOSED状态,完成四次握手。(如下图)

TCP/IP 通讯模型

数据封装与传递及解包流程

TCP与UDP区别总结:
1.TCP面向连接,通过三次握手建立连接,四次挥手接除连接;UDP是无连接的,即发送数据之前2.不需要建立连接,这种方式为UDP带来了高效的传输效率,但也导致无法确保数据的发送成功。
2.TCP是可靠的通信方式。通过TCP连接传送的数据,TCP通过超时重传、 数据校验等方式来确保数据无差错,不丢失,不重复,且按序到达;而UDP由于无需连接的原因,将会以最大速度进行传输,但不保证可靠交付,也就是会出现丢失、重复等等问题。
3.TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流,由于连接的问题,当网络出现波动时,连接可能出现响应问题;UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低。
4.每一条TCP连接只能是点到点的;而UDP不建立连接,所以可以支持一对一,一对多,多对一和多对多的交互通信,也就是可以同时接受多个人的包。
5.TCP需要建立连接,首部开销20字节相比8个字节的UDP显得比较大。
6.TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道。
2.sip协议(个人GB28181开发所用协议)
什么是SIP协议,用来干嘛的?
SIP协议SIP协议SIP协议全称“会话发起协议” 是一个应用层的控制协议, 可以用来建立、 修改、 和终止多媒体会话(或者会议)例如 Internet 电话/视频会议系统;会话的参与者可以通过组播(multicast)、网状单播(unicast)或两者的混合体进行通信。
SIP是一种信令协议,用于初始、管理、终止网络中的语音和视频会议,具体地说就是用来生成、修改和终结一个或多个参与者之间的会话;
SIP是点对点协议;
SIP协议的终端可以是PC机、对讲仪终端、安装特定软件的手机、平板、网页或者语音机器人等定制化设备。主要用于即时通信;
SIP是一种源于互联网的IP语音会话控制协议,具有灵活、易于实现、便于扩展等特点;
它是一种应用层协议,与其他应用层协议协同工作,通过Internet控制多媒体通信会话;
SIP与负责语音质量的预留协议(RSVP)互操作。它还与若干个其他协议进行协作,包括负责定位的轻型目录访问协议(LDAP)、负责身份验证的远程身份验证拨入用户服务 (RADIUS) 以及负责实时传输的 RTP 等多个协议;
sip会话基本流程

上图为基于 SIP 的会话发起的基本过程。 用 SIP 建立通讯通常需要六个步骤:
1) 注册, 发起和定位用户
2) 进行媒体协商– 通常采用 SDP 协议来携带媒体参数
3) 由被叫方来决定是否接纳该呼叫
4) 呼叫媒体流建立并交互{rtp 协议: 参考系列 23 或 24}
5) 呼叫更改或处理如呼叫转移等
6) 呼叫终止
SIP 请求方法的列表:
请求在两个 SIP 实体之间发起 SIP 事务, 以建立, 控制和终止会话。
常用的 SIP 请求消息如下:
INVITE: 用于与用户代理之间的媒体交换建立对话。
ACK: 客户端向服务器端证实它已经收到了对 INVITE 请求的最终响应。
PRACK: 表示对 1xx 响应消息的确认请求消息。
BYE: 表示终止一个已经建立的呼叫。
CANCEL: 表示在收到对请求的最终响应之前取消该请求, 对于已完成的请求则无影响。
REGISTER: 该方法为用户代理实施位置服务, 该位置服务向服务器指示其地址信息。
OPTIONS: 表示查询被叫的相关信息和功能。
NOTIFY: 通知用户有关新事件的通知
sip功能:
1) 用户定位: 检查终端用户的位置, 用于通讯。
2) 用户有效性: 检查用户参与会话的意愿程度。
3) 用户能力: 检查媒体和媒体的参数。
4) 建立会话: “振铃” 在被叫和主叫方建立会话参数。
5) 会话管理: 包括发送和终止会话, 修改会话参数, 激活服务等等。

3.https和http协议
http协议:
htpps协议:
4.RTP和RTCP协议(个人GB28181开发所用协议)
什么是RTP?RTP和RTCP有啥关系?什么情况下会用RTP传包?
RTP全名是Real-time Transport Protocol(实时传输协议)。它是IETF提出的一个标准,对应的RFC文档为RFC3550。RFC3550不仅定义了RTP,而且定义了配套的相关协议RTCP(Real-time Transport Control Protocol,即实时传输控制协议)。RTP用来为IP网上的语音、图像、传真等多种需要实时传输的多媒体数据提供端到端的实时传输服务。RTP为Internet上端到端的实时传输提供时间信息和流同步,但并不保证服务质量,服务质量由RTCP来提供。
是用于 Internet 上针对多媒体数据流的一种传输层协议.RTP 协议和 RTP 控制协议
RTCP 一起使用, 由 IETF(Internet 工程任务组)作为RFC1889 发布。 RTP 被定义为在一对一或一对多的传输情况下工作, 其目的是提供时间信息和实现流同步。 RTP 的典型应用建立在 UDP 上, 但也可以在 TCP 或 ATM 等其他协议之上工作。RTP 本身只保证实时数据的传输, 并不能为 按顺序传送数据包提供可靠的传送机制,也不提供流量控制或拥塞控制, 它依靠 RTCP 提供这些服务。
RTP+RTCP::兄弟协议, 他们两个端口是紧挨着的(实时传输和实时传输控制)。
Rtp:10240, rtcp:10241
RTP报文
RTP 头在基本格式下是12个字节,但如果启用了RTP头部扩展,则RTP头的长度会增加为最多24个字节。

①版本(V)
2 位, 标识 RTP 版本。
②填充标识(P) :padding
1 位, 如设置填充位, 在包尾将包含附加填充字, 它不属于有效载荷。 填充的最后一个
八进制包含应该忽略的八进制计数。 某些加密算法需要固定大小的填充字, 或为在底层协议
数据单元中携带几个 RTP 包。
③扩展(X)
1 位, 如设置扩展位, 固定头后跟一个头扩展。
④CSRC 计数(CC)
4 位, CSRC 计数包括紧接在固定头后 CSRC 标识符个数。
⑤标记(M)
1 位, 标记解释由设置定义, 目的在于允许重要事件在包流中标记出来。 设置可定义其
他标示位, 或通过改变位数量来指定没有标记位。
⑥载荷类型(PT)
7 位, 记录后面资料使用哪种 Codec , receiver 端找出相应的 decoder 解碼出來(如下图)。

⑦系列号(sn)
16 位, 系列号随每个 RTP 数据包而增加 1, 由接收者用来探测包损失。 系列号初值是
随机的, 使对加密的文本攻击更加困难。
⑧时标(ts:timestamp)
32 位, 时标反映 RTP 数据包中第一个八进制数的采样时刻, 采样时刻必须从单调、 线
性增加的时钟导出, 以允许同步与抖动计算。 时标可以让 receiver 端知道在正确的时间将资
料播放出来(如下图)

由上图可知, 如果只有系列号, 并不能完整按照顺序的将 data 播放出来, 因为如果 data
中间有一段是没有资料的, 只有系列号的话会造成错误, 需搭配上让它知道在哪个时间将
data 正确播放出来, 如此我们才能播放出正确无误的信息。
⑨SSRC
32 位, SSRC 段标识同步源。 此标识不是随机选择的, 目的在于使同一 RTP 包连接中没
有两个同步源有相同的 SSRC 标识。 尽管多个源选择同一个标识的概率很低, 所有 RTP 实现
都必须探测并解决冲突。 如源改变源传输地址, 也必须选择一个新 SSRC 标识以避免插入成
环行源。
⑩CSRC 列表
0 到 15 项, 每项 32 位。 CSRC 列表表示包内的对载荷起作用的源。 标识数量由 CC 段给
出。 如超出 15 个作用源, 也仅标识 15 个。 CSRC 标识由混合器插入, 采用作用源的 SSRC 标
识。 提供信源用来标识对一个 RTP 混合器产生的新包有贡献的所有 RTP 包的源。 是指当混
合器接收到一个或多个同步信源的 RTP 报文后, 经过混合处理产生一个新的组合 RTP 报文,
并把混合器作为组合 RTP 报文的 SSRC, 将原来所有的 SSRC 都作为 CSRC 传送给接收者, 是
接受者知道组成组合报文的各个 SSRC。
码流分析:

80 e0 00 1e 00 00 d2 f0 00 00 00 00 41 9b 6b 49
前4个字节80 e0 00 1e转成二进制
1000 0000 1110 0000 0000 0000 0001 1110
前两位版本V: 10
填充标识第三位P:0
扩展位第四位X:0
计数器5-8位CCSR:0000
标记位第九位M:1
核载类型10-16位PT:110 0000 十进制就是96(H264数据类型)
后两个字节就是序列号sequence number:0000 0000 0001 1110 对应十进制30
struct _RTP_FIXED_HEADER
{
/**/ /* byte 0 */
unsigned char csrc_len : 4; /**/ /* expect 0 */
unsigned char extension : 1; /**/ /* expect 1, see RTP_OP below */
unsigned char padding : 1; /**/ /* expect 0 */
unsigned char version : 2; /**/ /* expect 2 */
/**/ /* byte 1 */
unsigned char payload : 7; /**/ /* RTP_PAYLOAD_RTSP */
unsigned char marker : 1; /**/ /* expect 1 */
/**/ /* bytes 2, 3 */
unsigned short seq_no;
/**/ /* bytes 4-7 */
unsigned long timestamp;
/**/ /* bytes 8-11 */
unsigned long ssrc; /**/ /* stream number is used here. */
} __PACKED__;
typedef struct _RTP_FIXED_HEADER RTP_FIXED_HEADER;
struct _NALU_HEADER
{
// byte 0
unsigned char TYPE : 5;
unsigned char NRI : 2;
unsigned char F : 1;
} __PACKED__; /*** 1 BYTES */
typedef struct _NALU_HEADER NALU_HEADER;
struct _FU_INDICATOR
{
// byte 0
unsigned char TYPE : 5;
unsigned char NRI : 2;
unsigned char F : 1;
} __PACKED__; /*** 1 BYTES */
typedef struct _FU_INDICATOR FU_INDICATOR;
struct _FU_HEADER
{
// byte 0
unsigned char TYPE : 5;
unsigned char R : 1;
unsigned char E : 1;
unsigned char S : 1;
} __PACKED__; /*** 1 BYTES */
typedef struct _FU_HEADER FU_HEADER;
案例核载H264码流:
tp载荷h264媒体流:rtp协议头和h264码流

rtp头后是rtp载荷,rtp载荷第一个字节格式和NALU头一样。

F 和 NRI 也跟 NALU 头一样, 只有 Type 有些不一样: 拓展 24 – 31
| 0 | 没有定义 | |
| 1-23 | NAL 单元 | 单个 NAL 单元包 |
24 STAP-A 单一时间的组合包
25 STAP-B 单一时间的组合包
26 MTAP16 多个时间的组合包
27 MTAP24 多个时间的组合包
28 FU-A 分片的单元(fragment unit )
29 FU-B 分片的单元
30-31 没有定义
1) 单个NAL单元包: 荷载中只包含一个NAL单元。NAL头类型域等于原始 NAL单元(NALU)
类型, 即 Type 在范围 1 到 23 之间。
2) 组合包: 本类型用于聚合多个 NAL 单元到单个 RTP 荷载中。 本包有四种版本, 单时间聚
合包类型 A( STAP-A) 单时间聚合包类型 B(STAP-B) ,多时间聚合包类型(MTAP) 16 位位
移(MTAP16) ,多时间聚合包类型(MTAP) 24 位位移(MTAP24) 。 赋予 STAP-A, STAP-B,
MTAP16, MTAP24 的 NAL 单元类型号(Type) 分别是 24 25 26 27
3) 分片包: 用于分片单个 NAL 单元到多个 RTP 包。 现存两个版本 FU-A, FU-B, 用 NAL 单元
类型(Type) 28、 29 标识
常用的打包时的分包规则: 如果小于 MTU 采用单个 NAL 单元包, 如果大于 MTU 就采用 FUs
分片方式
单个NAL单元包:

void SendRtpData(int chn, char *buf, int len)
{
#if 1
int sendflag = 1;
#endif
unsigned int ts_current = 0;
int fTimestampFrequency = 90000; // 默认90000
struct timeval tv;
gettimeofday(&tv, NULL);
// 转换成时间戳单位
u_int32_t timestampIncrement = (fTimestampFrequency * tv.tv_sec);
if (chn == 0)
{
static unsigned int fTimestampBase = 0;
static bool fNextTimestampHasBeenPreset = false;
// 生成随机基数
if (fNextTimestampHasBeenPreset)
{
fTimestampBase = 200; // 随机数
fNextTimestampHasBeenPreset = false;
}
ts_current = timestampIncrement + fTimestampBase +
((unsigned int)((2.0 * fTimestampFrequency * tv.tv_usec + 1000000.0) / 2000000));
}
else
{
static unsigned int fTimestampBase1 = 0;
static bool fNextTimestampHasBeenPreset1 = false;
if (fNextTimestampHasBeenPreset1)
{
fTimestampBase1 = 300; // 随机数
fNextTimestampHasBeenPreset1 = false;
}
ts_current = timestampIncrement + fTimestampBase1 +
((unsigned int)((2.0 * fTimestampFrequency * tv.tv_usec + 1000000.0) / 2000000));
}
// NVR预览可以显示
// ts_current = timestampIncrement+fTimestampBase;
for (int i = 0; i < MAX_RTSP_CLIENT; i++)
{
if (g_rtsp_clients[i].status != RTSP_SENDING)
{
continue;
}
if (g_rtsp_clients[i].chn != chn)
{
continue;
}
if (g_rtsp_clients[i].socket <= 0)
{
continue;
}
#if 1
if (sendflag)
{
// frameCnt++;
}
sendflag = 0;
#endif
char *nodeChar = (char *)(buf) + 4;//去掉 4 个字节00 00 00 01
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(g_rtsp_clients[i].rtpport[0]);
server.sin_addr.s_addr = inet_addr(g_rtsp_clients[i].IP); // DEST_IP
int bytes = 0;
char sendbuf[1024 * 2] = {0};
int nAvFrmLen = len - 4; // 发送的视频数据长度;
unsigned char u8NaluBytes = nodeChar[0]; // nalu首byte
// rtp固定包头,为12字节,该句将sendbuf[0]的地址赋给rtp_hdr,
// 以后对rtp_hdr的写入操作将直接写入sendbuf。
char tcpsendbuf[1024 * 2] = {0};
tcpsendbuf[0] = '$';
tcpsendbuf[1] = 0;
int ret = 0;
if (nAvFrmLen <= 1400)
{
/** 1.设置 rtp 头 */
rtp_hdr = (RTP_FIXED_HEADER *)&sendbuf[0];
rtp_hdr->csrc_len = 0;
rtp_hdr->extension = 0;
rtp_hdr->padding = 0;
rtp_hdr->version = 2; // byte0
rtp_hdr->payload = RTP_H264; // 负载类型号,H265的封装还没做
rtp_hdr->marker = 0; // 标志位,由具体协议规定其值。
rtp_hdr->seq_no = htons(g_rtsp_clients[i].seqnum++); // 序列号,每发送一个RTP包增1
rtp_hdr->timestamp = htonl(ts_current);
rtp_hdr->ssrc = htonl(10); // 随机指定为10,并且在本RTP会话中全局唯一
/*2. 设置rtp荷载 single nal unit 头 */
nalu_hdr = (NALU_HEADER *)&sendbuf[12];
nalu_hdr->F = (u8NaluBytes & 0x80) >> 7;
nalu_hdr->NRI = (u8NaluBytes & 0x60) >> 5;
nalu_hdr->TYPE = u8NaluBytes & 0x1f;
/** 3. 填充nal内容*/
memcpy(sendbuf + 13, nodeChar + 1, nAvFrmLen - 1);
/** 4. 发送打包好的rtp到客户端*/
bytes = nAvFrmLen + 12;
if (g_rtsp_clients[i].sendtype == RTP_UDP)
{
sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server, sizeof(server));
}
else if (g_rtsp_clients[i].sendtype == RTP_TCP)
{
tcpsendbuf[2] = ((bytes)&0xFF00) >> 8;
tcpsendbuf[3] = (bytes)&0xFF;
memcpy(tcpsendbuf + 4, sendbuf, bytes);
// ret = send(g_rtsp_clients[i].socket, (void*)tcpsendbuf, bytes+4, 0);
ret = select_send(g_rtsp_clients[i].socket, (char *)tcpsendbuf, bytes + 4, 1000);
}
}
else // if(nAvFrmLen>1400)
{
int fu_pack_num = 0; /* nalu 需要分片发送时分割的个数 */
int last_fu_pack_size = 0; /* 最后一个分片的大小 */
int fu_seq = 0; /* fu-A 序号 */
/*
* 1. 计算分割的个数
*
* 除最后一个分片外,
* 每一个分片消耗 RTP_PAYLOAD_MAX_SIZE BYLE
*/
fu_pack_num = nAvFrmLen % 1400 ? (nAvFrmLen / 1400 + 1) : nAvFrmLen / 1400;
last_fu_pack_size = nAvFrmLen % 1400 ? nAvFrmLen % 1400 : 1400;
for (fu_seq = 0; fu_seq < fu_pack_num; fu_seq++)
{
/*
* 根据FU-A的类型设置不同的rtp头和rtp荷载头
*/
if (fu_seq == 0)
{ /* 第一个FU-A */
/*
* 1.设置 rtp 头
*/
rtp_hdr = (RTP_FIXED_HEADER *)&sendbuf[0];
rtp_hdr->csrc_len = 0;
rtp_hdr->extension = 0;
rtp_hdr->padding = 0;
rtp_hdr->version = 2; // byte0
rtp_hdr->payload = RTP_H264; // 负载类型号,
rtp_hdr->marker = 0; // 标志位,由具体协议规定其值。
rtp_hdr->seq_no = htons(g_rtsp_clients[i].seqnum++); // 序列号,每发送一个RTP包增1
rtp_hdr->timestamp = htonl(ts_current);
rtp_hdr->ssrc = htonl(10); // 随机指定为10,并且在本RTP会话中全局唯一
/*
* 2. 设置rtp荷载 single nal unit 头
*/
fu_ind = (FU_INDICATOR *)&sendbuf[12];
fu_ind->F = (u8NaluBytes & 0x80) >> 7;
fu_ind->NRI = (u8NaluBytes & 0x60) >> 5;
fu_ind->TYPE = 28;
fu_hdr = (FU_HEADER *)&sendbuf[13];
fu_hdr->E = 0;
fu_hdr->R = 0;
fu_hdr->S = 1;
fu_hdr->TYPE = u8NaluBytes & 0x1f; // n->nal_unit_type;
/*
* 3. 填充nalu内容
*/
memcpy(sendbuf + 14, nodeChar + 1, 1400 - 1); // 去掉NALU头
/*
* 4. 发送打包好的rtp包到客户端
*/
bytes = 1400 - 1 + 14;
if (g_rtsp_clients[i].sendtype == RTP_UDP)
{
sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server, sizeof(server));
}
else if (g_rtsp_clients[i].sendtype == RTP_TCP)
{
tcpsendbuf[2] = ((bytes)&0xFF00) >> 8;
tcpsendbuf[3] = (bytes)&0xFF;
memcpy(tcpsendbuf + 4, sendbuf, bytes);
// ret = send(g_rtsp_clients[i].socket, (void*)tcpsendbuf, bytes+4, 0);
ret = select_send(g_rtsp_clients[i].socket, (char *)tcpsendbuf, bytes + 4, 1000);
}
}
else if (fu_seq < fu_pack_num - 1)
{
/* 中间的FU-A */
/*
* 1.设置 rtp 头
*/
rtp_hdr = (RTP_FIXED_HEADER *)&sendbuf[0];
rtp_hdr->csrc_len = 0;
rtp_hdr->extension = 0;
rtp_hdr->padding = 0;
rtp_hdr->version = 2; // byte0
rtp_hdr->payload = RTP_H264; // 负载类型号,
rtp_hdr->marker = 0; // 标志位,由具体协议规定其值。
rtp_hdr->seq_no = htons(g_rtsp_clients[i].seqnum++); // 序列号,每发送一个RTP包增1
rtp_hdr->timestamp = htonl(ts_current);
rtp_hdr->ssrc = htonl(10); // 随机指定为10,并且在本RTP会话中全局唯一
/*
* 2. 设置rtp荷载 single nal unit 头
*/
fu_ind = (FU_INDICATOR *)&sendbuf[12];
fu_ind->F = (u8NaluBytes & 0x80) >> 7;
fu_ind->NRI = (u8NaluBytes & 0x60) >> 5;
fu_ind->TYPE = 28;
fu_hdr = (FU_HEADER *)&sendbuf[13];
fu_hdr->E = 0;
fu_hdr->R = 0;
fu_hdr->S = 0;
fu_hdr->TYPE = u8NaluBytes & 0x1f; // n->nal_unit_type;
/*
* 3. 填充nalu内容
*/
memcpy(sendbuf + 14, nodeChar + 1400 * fu_seq, 1400); // 去掉NALU头
/*
* 4. 发送打包好的rtp包到客户端
*/
bytes = 1400 + 14;
if (g_rtsp_clients[i].sendtype == RTP_UDP)
{
sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server, sizeof(server));
}
else if (g_rtsp_clients[i].sendtype == RTP_TCP)
{
tcpsendbuf[2] = ((bytes)&0xFF00) >> 8;
tcpsendbuf[3] = (bytes)&0xFF;
memcpy(tcpsendbuf + 4, sendbuf, bytes);
// ret = send(g_rtsp_clients[i].socket, (void*)tcpsendbuf, bytes+4, 0);
ret = select_send(g_rtsp_clients[i].socket, (char *)tcpsendbuf, bytes + 4, 1000);
}
}
else
{
/* 最后一个FU-A */
/*
* 1.设置 rtp 头
*/
rtp_hdr = (RTP_FIXED_HEADER *)&sendbuf[0];
rtp_hdr->csrc_len = 0;
rtp_hdr->extension = 0;
rtp_hdr->padding = 0;
rtp_hdr->version = 2; // byte0
rtp_hdr->payload = RTP_H264; // 负载类型号,
rtp_hdr->marker = 1; // 标志位,由具体协议规定其值。
rtp_hdr->seq_no = htons(g_rtsp_clients[i].seqnum++); // 序列号,每发送一个RTP包增1
rtp_hdr->timestamp = htonl(ts_current);
rtp_hdr->ssrc = htonl(10); // 随机指定为10,并且在本RTP会话中全局唯一
/*
* 2. 设置rtp荷载 single nal unit 头
*/
fu_ind = (FU_INDICATOR *)&sendbuf[12];
fu_ind->F = (u8NaluBytes & 0x80) >> 7;
fu_ind->NRI = (u8NaluBytes & 0x60) >> 5;
fu_ind->TYPE = 28;
fu_hdr = (FU_HEADER *)&sendbuf[13];
fu_hdr->E = 1;
fu_hdr->R = 0;
fu_hdr->S = 0;
fu_hdr->TYPE = u8NaluBytes & 0x1f; // n->nal_unit_type;
/*
* 3. 填充nalu内容
*/
memcpy(sendbuf + 14, nodeChar + 1400 * fu_seq, last_fu_pack_size); // 去掉NALU头
/*
* 4. 发送打包好的rtp包到客户端
*/
bytes = last_fu_pack_size + 14;
if (g_rtsp_clients[i].sendtype == RTP_UDP)
{
sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server, sizeof(server));
}
else if (g_rtsp_clients[i].sendtype == RTP_TCP)
{
tcpsendbuf[2] = ((bytes)&0xFF00) >> 8;
tcpsendbuf[3] = (bytes)&0xFF;
memcpy(tcpsendbuf + 4, sendbuf, bytes);
// ret = send(g_rtsp_clients[i].socket, (void*)tcpsendbuf, bytes+4, 0);
ret = select_send(g_rtsp_clients[i].socket, (char *)tcpsendbuf, bytes + 4, 1000);
}
}
}
}
}
}
12(或更多) 字节的 RTP 头后面的就是音视频数据, 比较简单。
一个封装单个 NAL 单元包到 RTP 的 NAL 单元流的 RTP 序号必须符合 NAL 单元的解码顺序。
对于 NALU 的长度小于 MTU 大小的包, 一般采用单一 NAL 单元模式.
对于一个原始的 H.264 NALU 单元常由 [Start Code] [NALU Header] [NALU Payload] 三
部分组成, 其中 Start Code 用于标示这是一个 NALU 单元的开始, 必须是 "00 00 00 01" 或
"00 00 01", NALU 头仅一个字节, 其后都是 NALU 单元内容.打包时去除 "00 00 01" 或 "00
00 00 01" 的开始码, 把其他数据封包的 RTP 包即可. 以下即是一个被打包进rtp 的NALU 单
元(不包含 rtp 头), 第一个字节是 NALU 头:
对于 NALU(NAL 单元) 的长度小于 MTU 大小的包, 一般采用单一 NAL 单元模式
定义在此的 NAL 单元包必须只包含一个。 RTP 序号必须符合 NAL 单元的解码顺序。 这种情
况下, NAL 单元的第一字节和 RTP 荷载头第一个字节重合。 如上图所示。
对于一个原始 H264 的 NALU 单元常由[start code] [NALU Header] [NALU Payload]三部分组成,
其中 start code 用于标识这是一个 NALU 单元的开始, 必须是“00 00 00 01”或“00 00 01”,NALU
头仅一个字节, 其后都是 NALU 单元载荷。
打包时去除“00 00 01” 或“00 00 00 01” 的开始码, 把其他数据封装成 RTP 包即可。
如有一个 H.264 的 NALU 是这样的:
[00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ] ///H.264.Annexb
F、 NRI、 Type(1、 2、 5) : 8bits: 1 字节(既头字节0x67)
0110 0111
Type--》 7: SPS、 8: PPS
这是一个序列参数集 NAL 单元。 [00 00 00 01] 是四个字节的开始码, 67 是 NALU 头, 42
开始的数据是 NALU 载荷.
封装成 RTP 包将如下:
[ RTP Header ] [ 67 42 A0 1E 23 56 0E 2F ... ]
即只要去掉 4 个字节的开始码就可以了.
组合封包格式:

当 NALU 的长度特别小时, 可以把几个 NALU 单元封在一个 RTP 包中
例:
如有一个 H.264 的 NALU 是这样的:
[00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ] //67: sps
[00 00 00 01 68 42 B0 12 58 6A D4 FF ... ]//68:pps
封装成 RTP 包将如下:
[ RTP Header ] [78 (STAP-A头, 占用1个字节)] [第一个NALU长度 (占用两个字节)] [ 67 42 A0
1E 23 56 0E 2F ] [第二个 NALU 长度 (占用两个字节)] [68 42 B0 12 58 6A D4 FF ... ]
分片单元(FU-A)

NALU 的长度超过 MTU 时, 就必须对 NALU 单元进行分片封包, 也称为 Fragmentation
Units (FUs)NAL 单元的一个分片由整数个连续 NAL 单元字节组成。 每个 NAL 单元字节必须正
好是该 NAL 单元一个分片的一部分。
数据比较大的 H264 视频包, 被 RTP 分片发送。 12 字节的 RTP 头后面跟随的就是 FU-A 分片:
FU indicator 有以下格式:

FU 指示字节的类型域 Type=28 表示 FU-A。 。 NRI 域的值必须根据分片 NAL 单元的 NRI
域的值设置。

S: 1 bit
当设置成 1,开始位指示分片 NAL 单元的开始。 当跟随的 FU 荷载不是分片 NAL 单元荷载
的开始, 开始位设为 0。
E: 1 bit
当设置成 1, 结束位指示分片 NAL 单元的结束, 即, 荷载的最后字节也是分片 NAL 单元的
最后一个字节。 当跟随的 FU 荷载不是分片 NAL 单元的最后分片,结束位设置为 0。
R: 1 bit
保留位必须设置为 0, 接收者必须忽略该位。
Type: 5 bits
NAL 单元荷载类型定义见下表
表 1. 单元类型以及荷载结构总结
| Type | Packet | Type name |
| 0 | undefined | - |
| 1-23 | NAL unit | Single NAL unit packet per H.264 |
24 STAP-A Single-time aggregation packet
25 STAP-B Single-time aggregation packet
26 MTAP16 Multi-time aggregation packet
27 MTAP24 Multi-time aggregation packet
28 FU-A Fragmentation unit
| 29 | FU-B | Fragmentation unit |
| 30-31 | undefined |
关于时间戳, 需要注意的是 h264 的采样率为 90000HZ(被标准固定死的), 因此时间戳
的单位为 1(秒)/90000, 因此如果当前视频帧率为 25fps, 那时间戳间隔或者说增量应该为
3600, 如果帧率为 30fps, 则增量为 3000, 以此类推。
关于 h264 拆包, 按照 FU-A 方式说明:
1) 第一个 FU-A 包的 FU indicator: F 应该为当前 NALU 头的 F, 而 NRI 应该为当前 NALU
头的 NRI, Type 则等于 28, 表明它是 FU-A 包。 FU header 生成方法: S = 1, E = 0, R = 0, Type
则等于 NALU 头中的 Type。
2) 后续的 N 个 FU-A 包的 FU indicator 和第一个是完全一样的, 如果不是最后一个包,
则 FU header 应该为: S = 0, E = 0, R = 0, Type 等于 NALU 头中的 Type。
3) 最后一个 FU-A 包 FU header 应该为: S = 0, E = 1, R = 0, Type 等于 NALU 头中的 Type。
因此总结就是: 同一个 NALU 分包后的 FU indicator 头是完全一致的, FU header 只有 S
以及 E 位有区别, 分别标记开始和结束, 它们的 RTP 分包的序列号应该是依次递增的, 并且
它们的时间戳必须一致, 而负载数据为 NALU 包去掉 1 个字节的 NALU 头后对剩余数据的拆
分, 这点很关键, 你可以认为 NALU 头被拆分成了 FU indicator 和 FU header, 所以不再需要
1 字节的 NALU 头了。
H264 帧的分类:
| 帧 的 分 类 | 全称 | 作用 |
| I 帧 | 帧内编码帧(intra picture) | I 帧通常是每个 GOP(MPEG 所使⽤的⼀种视 频压缩技术) 的第⼀个帧, 经过适度地压缩 做为随机访问的参考点, 可以当成图象。 I 帧可以看成是⼀个图像经过压缩后的产物。 ⾃身可以通过视频解压算法解压成⼀张单独 的完整的图⽚。 |
| P 帧 | 前向预测编码帧 (predictive-frame) | 通过充分将低于图像序列中前⾯已编码帧的 时间冗余信息来压缩传输数据量的编码图 像, 也叫预测帧。 需要参考其前⾯的⼀个 I frame 或者 P frame 来⽣成⼀张完整的图⽚ |
| B 帧 | 双向预测帧(bi-directional interpolated prediction frame) | 既考虑与源图像序列前⾯已编码帧, 也顾及 源图像序列后⾯已编码帧之间的时间冗余信 息来压缩传输数据量的编码图像,也叫双向 预测帧。 则要参考其前⼀个 I 或者 P 帧及其 后⾯的⼀个 P 帧来⽣成⼀张完整的图⽚。 |
5.RSTP协议
STP协议缺点
- 工作时收敛时间较长,响应速度慢
- 原始的802.1d不支持多个vlan
STP的问题
(一)、设备从初始化到收敛完成,最少经历30S
为了防止临时环路的出现,采用被等待的计时器
STP的计算,必须等待固定时长
(二)、交换机BP端口,切换到RP时,至少经历两个转发延时
(三)、交换机没有BP端口,RP端口down掉,DP端口至少等待50S 才能切换
(四)、交换机连接中端的端口,切换时间过长,30-50S
(五)、STP拓扑结构变更机制:复杂、效率低下
(六)、端口角色划分不充分,
没有面向指定用户/终端的端口
没有备份的跟的端口/指定端口
(七)、端口的状态有些重复,而且增加了转发延时

1) OPTIONS
Client--->Server
C--->S
客户端向服务器端发现 OPTIONS, 请求可用的方法。
S--->C
服务器端回复客户端, 消息中包含当前可用的方法。
2) DESCRIBE
C--->S
客户端向服务器请求媒体描述文件, 一般通过 rtsp 开头的 url 来发起请求, 格式为 sdp。
S--->C
服务器回复客户端 sdp 文件, 该文件告诉客户端服务器有哪些音视频流, 有什么属性, 如编解码器信息, 帧率等。
3) SETUP
为音视频数据的传输准备通道
C--->S
客户端向服务器端发起建立连接请求, 请求建立会话连接, 准备开始接收音视频数据,
请求信息描述了期望音视频数据包基于 UDP 还是 TCP 传输, 指定了 RTP, RTCP 端口, 以及
是单播还是组播等信息!
S--->C
服务器端收到客户端请求后, 根据客户端请求的端口号确定发送控制数据的端口以及音
视频数据的端口!
4) PLAY
C--->S
客户端向服务端请求播放媒体。
S--->C
服务器回复客户端 200 OK! 之后开始通过 SETUP 中指定的端口开始发送数据!
5) TEARDOWN
C---->S
结束播放的时候, 客户端向服务器端发起结束请求
S--->C
服务端收到消息后, 向客户端发送 200 OK, 之后断开连接
上述的流程基本涵盖了 RTSP 的流程, 当然, RTSP 除此之外, 还有 PAUSE, SCALE,
GET_PARAMETER, SET_PARAMETER 等参数。
ffmpeg -re -i ande10.mp4 -vcodec libx264 -acodec aac -f flv -y rtmp://127.0.0.1:1935/live/test1
http://localhost:8080/flv?port=1935&app=live&stream=test1
重要概念
1.集合控制
对多个流的同时控制。 对音频/视频来讲, 客户端仅需发送一条播放或者暂停消息就可
同时控制音频流和视频流。
音视频同步
延迟问题
2.实体(Entity)
作为请求或者回应的有效负荷传输的信息。
由以实体标题域[头, head](entity-header field) 形式存在的元信息和以实体主体[身体,body](entity body) 形式存在的内容组成。
如不受请求方法或响应状态编码限制, 请求和响应信息可传输实体, 实体则由实体头文
件和实体体组成, 有些响应仅包括实体头。 在此, 根据谁发送实体、 谁接收实体, 发送者和
接收者可分别指用户和服务器。
实体头定义实体体可选元信息, 如没有实体体, 指请求标识的资源。 扩展头机制允许定
义附加实体头段, 而不用改变协议, 但这些段不能假定接收者能识别。 不可识别头段应被接
收者忽略, 而让代理转发。
3.容器文件(Containerfile)
可以容纳多个媒体流的文件。 (mp4,avi,mkv,flv, ts, .....)
RTSP 服务器可以为这些容器文件提供集合控制。
RTSP 会话
RTSP 交互的全过程(session)。
对一个电影的观看过程,会话(session) 包括由客户端建立媒体流传输机制(SETUP) ,
使用播放(PLAY) 或录制(RECORD) 开始传送流, 用停止(TEARDOWN) 关闭流。
RTSP 协议具有如下特点:
可扩展性: 新方法和参数很容易加入 RTSP。
易解析: RTSP 可由标准 HTTP 或 MIME 解析器解析。
安全: RTSP 使用网页安全机制。
独立于传输: RTSP 可使用不可靠数据报协议(EDP) 、 可靠数据报协议(RDP) ; 如要实现
应用级可靠, 可使用可靠流协议。
多服务器支持: 每个流可放在不同服务器上, 用户端自动与不同服务器建立几个并发控制连
接, 媒体同步在传输层执行。
记录设备控制: 协议可控制记录和回放设备。
流控与会议开始分离: 仅要求会议初始化协议提供, 或可用来创建惟一会议标识号。 特殊情
况下, 可用 SIP 或 H.323 来邀请服务器入会。
适合专业应用: 通过 SMPTE 时标, RTSP 支持帧级精度, 允许远程数字编辑。
演示描述中立: 协议没强加特殊演示或元文件, 可传送所用格式类型; 然而, 演示描述至少
必须包括一个 RTSP URL。
代理与防火墙友好: 协议可由应用和传输层防火墙处理。 防火墙需要理解 SETUP 方法, 为
UDP 媒体流打开一个“缺口” 。
HTTP 友好: 此处, RTSP 明智地采用 HTTP 观念, 使现在结构都可重用。 结构包括 Internet
内容选择平台(PICS) 。 由于在大多数情况下控制连续媒体需要服务器状态, RTSP 不仅仅向
HTFP 添加方法。
适当的服务器控制: 如用户启动一个流, 必须也可以停止一个流。
传输协调: 实际处理连续媒体流前, 用户可协调传输方法。
性能协调: 如基本特征无效, 必须有一些清理机制让用户决定哪种方法没生效。 这允许用户
提出适合的用户界面。
RSTP信息交互
第一步: 查询服务器可用方法
c---s : OPTIONS request //查询 s 有哪些方法可用
s---c: OPTIONS response //s 回应信息的 public 头字段中提供的所有可用方法
第二步: 得到媒体描述信息
c--s:describe request //要求得到 s 提供的媒体描述信息
s---c: describe response //s 回应媒体描述信息, 一般是 sdp 信息。
c---s: setup request //通过 transport 头字段列出可接受的传输选项, 建立 s 建立会话
s---c: setup response //s 建立会话, 通过 transport 头字段返回选择的具体传输选项, 并
返回建立
第四步: 请求开始传输数据
C->S:PLAY request //C 请求 S 开始发送数据
S->C:PLAY response //S 回应该请求的信息
第五步: 数据传送播放中
S->C:发送流媒体数据 // 通过 RTP 协议传送数据
第六步: 关闭会话, 退出
C->S:TEARDOWN request //C 请求关闭会话
S->C:TEARDOWN response //S 回应该请求
上述的过程只是标准的、 友好的 rtsp 流程, 但实际的需求中并不一定按此过程。其中第三和第四步是必需的! 第一步, 只要服务器客户端约定好, 有哪些方法可用, 则 option
请求可以不要。 第二步, 如果我们有其他途径得到媒体初始化描述信息( 比如 http 请求等等) , 则我们也不需要通过 rtsp 中的 describe 请求来完成。
报文:

RTSP 的消息有两大类, 一是请求消息(request), 一是回应消息(response), 两种消息的格式
不同。
请求消息格式:
方法 URI RTSP 版本 CR LF
消息头 CR LF CR LF
消息体 CR LF
方法包括: OPTIONS、 SETUP、 PLAY、 TEARDOWN DESCRIBE
回应消息格式:
RTSP 版本 状态码 解释 CR LF
消息头 CR LF CR LF
消息体 CR LF
| 方法 | 方向 | 对象 | 要求 | 含义 |
| DESCRIBE | C->S | P, S | 推荐 | 检查演示或媒体对象的描述, 也允许使用接收头指定用户理解的描述格式。 DESCRIBE 的答复-响应组成媒体 RTSP 初始阶段 |
| ANNOUNCE | C->S S->C | P, S | 可选 | 当从用户发往服务器时, ANNOUNCE 将请求 URL 识别的演示或媒体对象 描述发送给服务器; 反之, ANNOUNCE 实时更新连接描述。 如新媒体流 加入演示, 整个演示描述再次发送, 而不仅仅是附加组件, 使组件能被删 除 |
| GET_PARAMETER | C->S S->C | P, S | 可选 | GET_PARAMETER 请求检查 URL 指定的演示与媒体的参数值。 没有实体 体时, GET_PARAMETER 也许能用来测试用户与服务器的连通情况 |
| OPTIONS | C->S S->C | P, S | 要求 | 可在任意时刻发出 OPTIONS 请求, 如用户打算尝试非标准请求, 并不影 响服务器状态 |
| PAUSE | C->S | P, S | 推荐 | PAUSE 请求引起流发送临时中断。 如请求 URL 命名一个流, 仅回放和记 录被停止; 如请求 URL 命名一个演示或流组, 演示或组中所有当前活动的 流发送都停止。 恢复回放或记录后, 必须维持同步。 在 SETUP 消息中连 接头超时参数所指定时段期间被暂停后, 尽管服务器可能关闭连接并释放 资源, 但服务器资源会被预订 |
| PLAY | C->S | P, S | 要求 | PLAY 告诉服务器以 SETUP 指定的机制开始发送数据; 直到一些 SETUP 请求被成功响应, 客户端才可发布 PLAY 请求。 PLAY 请求将正常播放时 间设置在所指定范围的起始处, 发送流数据直到范围的结束处。 PLAY 请 求可排成队列, 服务器将 PLAY 请求排成队列, 顺序执行 |
| RECORD | C->S | P, S | 可选 | 该方法根据演示描述初始化媒体数据记录范围, 时标反映开始和结束时间; 如没有给出时间范围, 使用演示描述提供的开始和结束时间。 如连接已经启动, 立即开始记录, 服务器数据请求 URL 或其他 URL 决定是否存储记 录的数据; 如服务器没有使用 URL 请求, 响应应为 201(创建) , 并包含 描述请求状态和参考新资源的实体与位置头。 支持现场演示记录的媒体服 务器必须支持时钟范围格式, smpte 格式没有意义 |
| REDIRECT | S->C | P, S | 可选 | 重定向请求通知客户端连接到另一服务器地址。 它包含强制头地址, 指示 客户端发布 URL 请求; 也可能包括参数范围, 以指明重定向何时生效。 若 客户端要继续发送或接收 URL 媒体, 客户端必须对当前连接发送 TEARD OWN 请求, 而对指定主执新连接发送 SETUP 请求 |
| SETUP | C->S | S | 要求 | 对 URL 的 SETUP 请求指定用于流媒体的传输机制。 客户端对正播放的流 发布一个 SETUP 请求, 以改变服务器允许的传输参数。 如不允许这样做, 响应错误为"455 Method Not Valid In This State”。 为了透过防火墙, 客 户端必须指明传输参数, 即使对这些参数没有影响 |
| SET_PARAMETER | C->S S->C | P, S | 可选 | 这个方法请求设置演示或 URL 指定流的参数值。 请求仅应包含单个参数, 允许客户端决定某个特殊请求为何失败。 如请求包含多个参数, 所有参数 可成功设置, 服务器必须只对该请求起作用。 服务器必须允许参数可重复 设置成同一值, 但不让改变参数值。 注意: 媒体流传输参数必须用 SETUP 命令设置。 将设置传输参数限制为 SETUP 有利于防火墙。 将参数划分成 规则排列形式, 结果有更多有意义的错误指示 |
| TEARDOWN | C->S | P, S | 要求 | TEARDOWN 请求停止给定 URL 流发送, 释放相关资源。 如 URL 是此演 示 URL, 任何 RTSP 连接标识不再有效。 除非全部传输参数是连接描述定 义的, SETUP 请求必须在连接可再次播放前发布 |
6.ONVIF协议
7.PS和TS(个人GB28181开发所用协议)
PS:
TS:
8.josn解析也放在这里说一下吧
9.opncv图片存取(软件存取合成)
Mat类
API:
imread(..url...): 加载本地图像
imshow("namexx", ...url...): 显示图像
getStructuringElement(...): 获取 腐蚀 的内核矩阵
erode(...): 腐蚀(源图像、 目的图像、 内核矩阵)
blur(...) : 模糊
cvtColor(...): 颜色转换
Canny(...): 提取边缘
waitKey(...): 等待按键
static int Image_Zoom_And_Copy2_Dst(cv::Mat *srcImg, int zoomWidth, int zoomHeight, cv::Mat *dstImg, int x, int y)
{
if(srcImg == NULL || dstImg == NULL)
{
LOG_DEBUG_FMT("srcImg: %p dstImg: %p,指针为空",srcImg,dstImg);
return -1;
}
if(dstImg->cols < zoomWidth-x || dstImg->rows < zoomHeight-y)
{
LOG_DEBUG_FMT("目标太小装不下dstImg->rows: %d zoomWidth-x: %d dstImg->cols: %d zoomHeight-y: %d\n",
dstImg->rows,zoomWidth-x,dstImg->cols,zoomHeight-y);
return -1;
}
cv::Mat zoomImg;
// 缩放
if(srcImg->rows != zoomWidth || srcImg->cols != zoomHeight)
{
zoomImg.create(cv::Size(zoomWidth+x,zoomHeight+y),srcImg->type());
//cvZero(zoomImg);
zoomImg.setTo(0);
LOG_DEBUG_FMT("图片缩放 srcImg->rows = %d srcImg->cols = %d zoomWidth = %d zoomHeight = %d x = %d y = %d",
srcImg->rows,srcImg->cols,zoomWidth,zoomHeight,x,y);
resize((*srcImg), zoomImg, cv::Size(zoomWidth+x,zoomHeight+y),0,0,CV_INTER_AREA);// 缩放到目标图像
(*srcImg) = zoomImg;//.clone();//使用缩放后的图像作为源
}
// 拷贝
LOG_DEBUG_FMT("cpy事件进行叠加osd");
(*dstImg) = (*srcImg);//.clone();
#if 0
cvSetImageROI(dstImg, cvRect(x,y,srcImg->width,srcImg->height));
cvCopy(srcImg, dstImg);
cvResetImageROI(dstImg);
// 释放中间图像
if(zoomImg)
{
cvReleaseImage(&zoomImg);
zoomImg = NULL;
}
#endif
return 0;
}
int Image_Stitch_Left_Right(cv::Mat *ppImg, int n, int width, int height, OSD_STRING *pOsd, AS_OSD* ptOsdCfg, char *img_file, AS_COMPOSE *ptComCfg,time_t * capture_time,int * capture_msec)
{
int ret = -1;
int i;
cv::Mat *dstImg = NULL;
dstImg = new cv::Mat();
cv::Scalar txtColor = CV_RGB(ptOsdCfg->font_color_r,ptOsdCfg->font_color_g,ptOsdCfg->font_color_b);
cv::Scalar bkColor = CV_RGB(ptOsdCfg->bk_color_r,ptOsdCfg->bk_color_g,ptOsdCfg->bk_color_b);
cv::Mat des[4];
// 背景高度=上边距+文字高度+(行距+文字高度...)+边距
int bkHeight = ptOsdCfg->fontsize + 2 * OSD_LINE_GAP + (pOsd->iLineCnt - 1) * (ptOsdCfg->fontsize + OSD_LINE_GAP);
switch(ptOsdCfg->model)
{
case OSD_SINGLE_STACK_INSIDE:
{
dstImg->create(cv::Size(width*n,height),ppImg->type());
dstImg->setTo(0);
cv::Mat synthetic;
synthetic.create(cv::Size(width*n,height),ppImg->type());
synthetic.setTo(0);
cv::Mat* singleImg = NULL;
singleImg = new cv::Mat();
singleImg->create(cv::Size(width,height),ppImg->type());
singleImg->setTo(0);
for(i=0;i<n;i++)
{
Image_Zoom_And_Copy2_Dst(&ppImg[i],width,height,singleImg,0,0);
int iStartX = ptOsdCfg->leftdistance*width/100;
int iStartY = ptOsdCfg->updistance*height/100;
if (!ptOsdCfg->bk_transparent)
{
OSD_FillBackground(singleImg, iStartX, iStartY, width, bkHeight, bkColor);
}
OSD_Put2Img(singleImg, iStartX, iStartY, pOsd, txtColor, ptOsdCfg->fontsize);
des[i] = synthetic(Rect(singleImg->cols*i, 0, singleImg->cols, singleImg->rows));
singleImg->copyTo(des[i]);
singleImg->setTo(0);
pOsd++;
//最后整图缩放
if(i == (n-1))
{
Image_Zoom_And_Copy2_Dst(&synthetic,width*n,height,dstImg,0,0);
}
}
delete(singleImg);
singleImg = NULL;
break;
}
case OSD_SINGLE_STACK_OUTSIDE:
{
dstImg->create(cv::Size(width*n,height + bkHeight),ppImg->type());
dstImg->setTo(0);
cv::Mat synthetic;
synthetic.create(cv::Size(width*n,(height + bkHeight)),ppImg->type());
synthetic.setTo(0);
cv::Mat* singleImg = NULL;
singleImg = new cv::Mat();
singleImg->create(cv::Size(width,height+bkHeight),ppImg->type());
singleImg->setTo(0);
for(i=0;i<n;i++)
{
//Image_Zoom_And_Copy2_Dst(&ppImg[i],width,height,singleImg,0,bkHeight);
cv::Mat centre;
centre.setTo(0);
centre = (*singleImg)(Rect(0, 0, ppImg[i].cols, ppImg[i].rows));
ppImg[i].copyTo(centre);
OSD_FillBackground(singleImg, 0, height, width, bkHeight, bkColor);
int iStartX = OSD_LINE_GAP;
int iStartY = height + OSD_LINE_GAP;
OSD_Put2Img(singleImg, iStartX, iStartY, pOsd, txtColor, ptOsdCfg->fontsize);
des[i] = synthetic(Rect(singleImg->cols*i, 0, singleImg->cols, singleImg->rows));
singleImg->copyTo(des[i]);
singleImg->setTo(0);
pOsd++;
//最后整图缩放
if(i == (n-1))
{
Image_Zoom_And_Copy2_Dst(&synthetic,width*n,height + bkHeight,dstImg,0,0);
}
}
delete(singleImg);
singleImg = NULL;
break;
}
case OSD_SINGLE_STACK_UPSIDE:
{
dstImg->create(cv::Size(width*n,height + bkHeight),ppImg->type());
dstImg->setTo(0);
cv::Mat synthetic;
synthetic.create(cv::Size(width*n,(height + bkHeight)),ppImg->type());
synthetic.setTo(0);
cv::Mat* singleImg = NULL;
singleImg = new cv::Mat();
singleImg->create(cv::Size(width,height+bkHeight),ppImg->type());
singleImg->setTo(0);
for(i=0;i<n;i++)
{
//Image_Zoom_And_Copy2_Dst(&ppImg[i],width,height,singleImg,0,bkHeight);
cv::Mat centre;
centre.setTo(0);
centre = (*singleImg)(Rect(0, bkHeight, ppImg[i].cols, ppImg[i].rows));
ppImg[i].copyTo(centre);
OSD_FillBackground(singleImg, 0, 0, width, bkHeight, bkColor);
int iStartX = OSD_LINE_GAP;
int iStartY = OSD_LINE_GAP;
OSD_Put2Img(singleImg, iStartX, iStartY, pOsd, txtColor, ptOsdCfg->fontsize);
des[i] = synthetic(Rect(singleImg->cols*i, 0, singleImg->cols, singleImg->rows));
singleImg->copyTo(des[i]);
singleImg->setTo(0);
pOsd++;
//最后整图缩放
if(i == (n-1))
{
Image_Zoom_And_Copy2_Dst(&synthetic,width*n,height + bkHeight,dstImg,0,0);
}
}
delete(singleImg);
singleImg = NULL;
break;
}
case OSD_COMPOSE_STACK_INSIDE:
{
dstImg->create(cv::Size(width*n, height),ppImg->type());
dstImg->setTo(0);
cv::Mat synthetic;
synthetic.create(cv::Size(width*n,height),ppImg->type());
synthetic.setTo(0);
for(i=0;i<n;i++)
{
des[i] = synthetic(Rect(ppImg->cols*i, 0, ppImg->cols, ppImg->rows));
ppImg->copyTo(des[i]);;
ppImg++;
if(i == (n-1))
{
Image_Zoom_And_Copy2_Dst(&synthetic,width*n,height,dstImg,0,bkHeight);
}
}
int iStartX = ptOsdCfg->leftdistance*width*n/100;
int iStartY = ptOsdCfg->updistance*height/100;
if (!ptOsdCfg->bk_transparent)
{
OSD_FillBackground(dstImg, iStartX, iStartY, width*n, bkHeight, bkColor);
}
OSD_Put2Img(dstImg, iStartX, iStartY, pOsd, txtColor, ptOsdCfg->fontsize);
break;
}
case OSD_COMPOSE_STACK_OUTSIDE:
{
dstImg->create(cv::Size(width*n, height+bkHeight),ppImg->type());
dstImg->setTo(0);
cv::Mat synthetic;
synthetic.create(cv::Size(width*n,height),ppImg->type());
synthetic.setTo(0);
for(i=0;i<n;i++)
{
//Image_Zoom_And_Copy2_Dst(ppImg,width,height,dstImg,i*width,0);
des[i] = synthetic(Rect(ppImg->cols*i, 0, ppImg->cols, ppImg->rows));
ppImg->copyTo(des[i]);;
ppImg++;
if(i == (n-1))
{
cv::Mat centre;
centre.setTo(0);
centre = (*dstImg)(Rect(0, 0, synthetic.cols, synthetic.rows));
synthetic.copyTo(centre);
//Image_Zoom_And_Copy2_Dst(&synthetic,width*n,height,dstImg,0,bkHeight);
}
}
OSD_FillBackground(dstImg,0,height,width*n,bkHeight,bkColor);
int iStartX = OSD_LINE_GAP;
int iStartY = height + OSD_LINE_GAP;
OSD_Put2Img(dstImg, iStartX, iStartY, pOsd, txtColor, ptOsdCfg->fontsize);
break;
}
case OSD_COMPOSE_STACK_UPSIDE:
{
dstImg->create(cv::Size(width*n, height+bkHeight),ppImg->type());
dstImg->setTo(0);
cv::Mat synthetic;
synthetic.create(cv::Size(width*n,height),ppImg->type());
synthetic.setTo(0);
for(i=0;i<n;i++)
{
//Image_Zoom_And_Copy2_Dst(ppImg,width,height,dstImg,i*width,bkHeight);
des[i] = synthetic(Rect(ppImg->cols*i, 0, ppImg->cols, ppImg->rows));
ppImg->copyTo(des[i]);;
ppImg++;
if(i == (n-1))
{
cv::Mat centre;
centre.setTo(0);
centre = (*dstImg)(Rect(0, bkHeight, synthetic.cols, synthetic.rows));
synthetic.copyTo(centre);
//Image_Zoom_And_Copy2_Dst(&synthetic,width*n,height,dstImg,0,bkHeight);
}
}
OSD_FillBackground(dstImg,0,0,width*n,bkHeight,bkColor);
int iStartX = OSD_LINE_GAP;
int iStartY = OSD_LINE_GAP;
OSD_Put2Img(dstImg, iStartX, iStartY, pOsd, txtColor, ptOsdCfg->fontsize);
break;
}
default:
printf("Image overlay Type Error, %d!\n",ptOsdCfg->model);
break;
}
if(dstImg)
{
//cvSaveImage(img_file,dstImg,quality);
SaveImageWithLimit(dstImg,img_file,ptComCfg->img_size,ptComCfg->img_quality,JPEG_COM_TIMES);
printf("[GEN JPG] %s OK!\n",img_file);
//cvReleaseImage(&dstImg);
delete(dstImg);
dstImg = NULL;
ret = 0;
}
return ret;
}
int Image_Stitch_Up_Down(cv::Mat *ppImg, int n, int width, int height, OSD_STRING *pOsd, AS_OSD* ptOsdCfg, char *img_file, AS_COMPOSE *ptComCfg,time_t * capture_time,int * capture_msec)
{
int ret = -1;
int i;
cv::Mat *dstImg = NULL;
dstImg = new cv::Mat();
cv::Scalar txtColor = CV_RGB(ptOsdCfg->font_color_r,ptOsdCfg->font_color_g,ptOsdCfg->font_color_b);
cv::Scalar bkColor = CV_RGB(ptOsdCfg->bk_color_r,ptOsdCfg->bk_color_g,ptOsdCfg->bk_color_b);
// 背景高度=上边距+文字高度+(行距+文字高度...)+边距
int bkHeight = ptOsdCfg->fontsize + 2 * OSD_LINE_GAP + (pOsd->iLineCnt - 1) * (ptOsdCfg->fontsize + OSD_LINE_GAP);
cv::Mat des[4];
switch(ptOsdCfg->model)
{
case OSD_SINGLE_STACK_INSIDE:
{
dstImg->create(cv::Size(width,height*n),ppImg->type());
dstImg->setTo(0);
cv::Mat synthetic;
synthetic.create(cv::Size(width,height*n),ppImg->type());
synthetic.setTo(0);
cv::Mat* singleImg = NULL;
singleImg = new cv::Mat();
singleImg->create(cv::Size(width,height),ppImg->type());
singleImg->setTo(0);
for(i=0;i<n;i++)
{
Image_Zoom_And_Copy2_Dst(&ppImg[i],width,height,singleImg,0,0);
int iStartX = ptOsdCfg->leftdistance*width/100;
int iStartY = ptOsdCfg->updistance*height/100;
if (!ptOsdCfg->bk_transparent)
{
OSD_FillBackground(singleImg, iStartX, iStartY, width, bkHeight, bkColor);
}
OSD_Put2Img(singleImg, iStartX, iStartY, pOsd, txtColor, ptOsdCfg->fontsize);
des[i] = synthetic(Rect(0, singleImg->rows*i, singleImg->cols, singleImg->rows));
singleImg->copyTo(des[i]);
singleImg->setTo(0);
pOsd++;
//最后整图缩放
if(i == (n-1))
{
Image_Zoom_And_Copy2_Dst(&synthetic,width,(height + bkHeight)*n,dstImg,0,0);
}
}
delete(singleImg);
singleImg = NULL;
break;
}
case OSD_SINGLE_STACK_OUTSIDE:
{
dstImg->create(cv::Size(width,(height + bkHeight)*n),ppImg->type());
dstImg->setTo(0);
cv::Mat synthetic;
synthetic.create(cv::Size(width,(height + bkHeight)*n),ppImg->type());
synthetic.setTo(0);
cv::Mat* singleImg = NULL;
singleImg = new cv::Mat();
singleImg->create(cv::Size(width,height+bkHeight),ppImg->type());
singleImg->setTo(0);
for(i=0;i<n;i++)
{
//Image_Zoom_And_Copy2_Dst(&ppImg[i],width,height,singleImg,0,bkHeight);
cv::Mat centre;
centre.setTo(0);
centre = (*singleImg)(Rect(0, 0, ppImg[i].cols, ppImg[i].rows));
ppImg[i].copyTo(centre);
OSD_FillBackground(singleImg, 0, height, width, bkHeight, bkColor);
int iStartX = OSD_LINE_GAP;
int iStartY = height + OSD_LINE_GAP;
OSD_Put2Img(singleImg, iStartX, iStartY, pOsd, txtColor, ptOsdCfg->fontsize);
des[i] = synthetic(Rect(0, singleImg->rows*i, singleImg->cols, singleImg->rows));
singleImg->copyTo(des[i]);
singleImg->setTo(0);
//ppImg++;
//ppFileName++;
pOsd++;
//最后整图缩放
if(i == (n-1))
{
Image_Zoom_And_Copy2_Dst(&synthetic,width,(height + bkHeight)*n,dstImg,0,0);
}
}
delete(singleImg);
singleImg = NULL;
break;
}
case OSD_SINGLE_STACK_UPSIDE:
{
dstImg->create(cv::Size(width,(height + bkHeight)*n),ppImg->type());
dstImg->setTo(0);
cv::Mat synthetic;
synthetic.create(cv::Size(width,(height + bkHeight)*n),ppImg->type());
synthetic.setTo(0);
cv::Mat* singleImg = NULL;
singleImg = new cv::Mat();
singleImg->create(cv::Size(width,height+bkHeight),ppImg->type());
singleImg->setTo(0);
for(i=0;i<n;i++)
{
//Image_Zoom_And_Copy2_Dst(&ppImg[i],width,height,singleImg,0,bkHeight);
cv::Mat centre;
centre.setTo(0);
centre = (*singleImg)(Rect(0, bkHeight, ppImg[i].cols, ppImg[i].rows));
ppImg[i].copyTo(centre);
OSD_FillBackground(singleImg, 0, 0, width, bkHeight, bkColor);
int iStartX = OSD_LINE_GAP;
int iStartY = OSD_LINE_GAP;
OSD_Put2Img(singleImg, iStartX, iStartY, pOsd, txtColor, ptOsdCfg->fontsize);
des[i] = synthetic(Rect(0, singleImg->rows*i, singleImg->cols, singleImg->rows));
singleImg->copyTo(des[i]);
singleImg->setTo(0);
pOsd++;
//最后整图缩放
if(i == (n-1))
{
Image_Zoom_And_Copy2_Dst(&synthetic,width,(height + bkHeight)*n,dstImg,0,0);
}
}
delete(singleImg);
singleImg = NULL;
break;
}
case OSD_COMPOSE_STACK_INSIDE:
{
dstImg->create(cv::Size(width,height*n),ppImg->type());
dstImg->setTo(0);
/*
for(i=0;i<n;i++)
{
Image_Zoom_And_Copy2_Dst(ppImg,width,height,dstImg,0,i*height);
ppImg++;
}
*/
cv::Mat synthetic;
synthetic.create(cv::Size(width,height*n),ppImg->type());
synthetic.setTo(0);
for(i=0;i<n;i++)
{
//Image_Zoom_And_Copy2_Dst(ppImg,width,height,dstImg,0,i*height);
//synthetic(Rect(0, 0, ppImg->cols, ppImg->rows));
des[i] = synthetic(Rect(0, ppImg->rows*i, ppImg->cols, ppImg->rows));
ppImg->copyTo(des[i]);
ppImg++;
if(i == (n-1))
{
Image_Zoom_And_Copy2_Dst(&synthetic,width,height*n,dstImg,0,0);
}
}
int iStartX = ptOsdCfg->leftdistance*width/100;
int iStartY = ptOsdCfg->updistance*height*n/100;
if (!ptOsdCfg->bk_transparent)
{
OSD_FillBackground(dstImg, iStartX, iStartY, width, bkHeight, bkColor);
}
OSD_Put2Img(dstImg, iStartX, iStartY, pOsd, txtColor, ptOsdCfg->fontsize);
break;
}
case OSD_COMPOSE_STACK_OUTSIDE:
{
dstImg->create(cv::Size(width,height*n+ bkHeight),ppImg->type());
dstImg->setTo(0);
cv::Mat synthetic;
synthetic.create(cv::Size(width,height*n),ppImg->type());
synthetic.setTo(0);
for(i=0;i<n;i++)
{
//Image_Zoom_And_Copy2_Dst(ppImg,width,height,dstImg,0,i*height);
//synthetic(Rect(0, 0, ppImg->cols, ppImg->rows));
des[i] = synthetic(Rect(0, ppImg->rows*i, ppImg->cols, ppImg->rows));
ppImg->copyTo(des[i]);
#if 0
//for (int i = 0; i < ptData->m_PicNum; i++)
{
//if(pMatImage)
{
char tmpBuf[40] = {0};
// 保存图片
static int WX_NOTIFY_JPG_INDEXA = 0;
char img_file[256]= {0};
sprintf(img_file, "/userdata/data/pics/WeiXin/ppImgnotify_%s_%03d.jpg",
TimeFormatString(time(NULL), tmpBuf, sizeof(tmpBuf), eYMDHMS2),
WX_NOTIFY_JPG_INDEXA++);
OAL_MakeDirByPath(img_file);
LOG_DEBUG_FMT("img_file = %s\n",img_file);
SaveImageWithLimit(ppImg, img_file, 300, 80, 5);
char img_file1[256]= {0};
sprintf(img_file1, "/userdata/data/pics/WeiXin/dstImgnotify_%s_%03d.jpg",
TimeFormatString(time(NULL), tmpBuf, sizeof(tmpBuf), eYMDHMS2),
WX_NOTIFY_JPG_INDEXA);
OAL_MakeDirByPath(img_file1);
LOG_DEBUG_FMT("img_file1 = %s\n",img_file1);
SaveImageWithLimit(&synthetic, img_file1, 300, 80, 5);
//cvReleaseImage(&pIplImage);
WX_NOTIFY_JPG_INDEXA %= 1000;
//strcpy(tNewNotify.ImageFile, img_file);
}
}
#endif
ppImg++;
if(i == (n-1))
{
cv::Mat centre;
centre.setTo(0);
centre = (*dstImg)(Rect(0, 0, synthetic.cols, synthetic.rows));
synthetic.copyTo(centre);
//Image_Zoom_And_Copy2_Dst(&synthetic,width,height*n,dstImg,0,bkHeight);
}
}
OSD_FillBackground(dstImg,0,height*n,width,bkHeight,bkColor);
int iStartX = OSD_LINE_GAP;
int iStartY = height*n + OSD_LINE_GAP;
OSD_Put2Img(dstImg, iStartX, iStartY, pOsd, txtColor, ptOsdCfg->fontsize);
break;
}
case OSD_COMPOSE_STACK_UPSIDE:
{
dstImg->create(cv::Size(width,height*n+ bkHeight),ppImg->type());
dstImg->setTo(0);
cv::Mat synthetic;
synthetic.create(cv::Size(width,height*n),ppImg->type());
synthetic.setTo(0);
for(i=0;i<n;i++)
{
//Image_Zoom_And_Copy2_Dst(ppImg,width,height,dstImg,0,i*height);
//synthetic(Rect(0, 0, ppImg->cols, ppImg->rows));
des[i] = synthetic(Rect(0, ppImg->rows*i, ppImg->cols, ppImg->rows));
ppImg->copyTo(des[i]);
ppImg++;
if(i == (n-1))
{
cv::Mat centre;
centre.setTo(0);
centre = (*dstImg)(Rect(0, bkHeight, synthetic.cols, synthetic.rows));
synthetic.copyTo(centre);
//Image_Zoom_And_Copy2_Dst(&synthetic,width,height*n,dstImg,0,bkHeight);
}
}
OSD_FillBackground(dstImg,0,0,width,bkHeight,bkColor);
int iStartX = OSD_LINE_GAP;
int iStartY = OSD_LINE_GAP;
OSD_Put2Img(dstImg, iStartX, iStartY, pOsd, txtColor, ptOsdCfg->fontsize);
break;
}
default:
printf("Image overlay Type Error, %d!\n",ptOsdCfg->model);
break;
}
if(dstImg)
{
//cvSaveImage(img_file,dstImg,quality);
SaveImageWithLimit(dstImg,img_file,ptComCfg->img_size,ptComCfg->img_quality,JPEG_COM_TIMES);
printf("[GEN JPG] %s OK!\n",img_file);
//cvReleaseImage(&dstImg);
delete(dstImg);
dstImg = NULL;
ret = 0;
}
return ret;
}
10.图片硬件存取及编解码流程(以hisi为例吧,地平线也是类似接口不一样而已根据芯片手册决定)
HISI mpp库初始化流程
一、初始化系统:
HI_MPI_SYS_Exit();
HI_MPI_VB_Exit();
HI_MPI_VB_SetConfig(pstVbConfig);//整个系统中可以容纳缓存池的个数,每个缓存池缓存块的个数,缓存块的大小
HI_MPI_VB_Init();
HI_MPI_SYS_Init();
/******************************************************************************
* function : vb init & MPI system init
******************************************************************************/
HI_S32 SAMPLE_COMM_SYS_Init(VB_CONFIG_S* pstVbConfig)
{
int i;
HI_S32 s32Ret = HI_FAILURE;
s32Ret = HI_MPI_SYS_Exit();
printf("%s:%d HI_MPI_SYS_Exit %#x\n", __FILE__, __LINE__, s32Ret);
s32Ret = HI_MPI_VB_Exit();
printf("%s:%d HI_MPI_VB_Exit %#x\n", __FILE__, __LINE__, s32Ret);
if (NULL == pstVbConfig)
{
LOG_DEBUG_FMT("input parameter is null, it is invaild!");
return HI_FAILURE;
}
printf("%s:%d Set u32MaxPoolCnt:%u\n", __FILE__, __LINE__, pstVbConfig->u32MaxPoolCnt);
for (i = 0; i < pstVbConfig->u32MaxPoolCnt && i < VB_MAX_COMM_POOLS; i++)
{
printf("%s:%d Set Pool%d u32BlkCnt:%u u64BlkSize:%llu\n", __FILE__, __LINE__, i,
pstVbConfig->astCommPool[i].u32BlkCnt, pstVbConfig->astCommPool[i].u64BlkSize);
}
s32Ret = HI_MPI_VB_SetConfig(pstVbConfig);
printf("%s:%d HI_MPI_VB_SetConfig %#x\n", __FILE__, __LINE__, s32Ret);
if (HI_SUCCESS != s32Ret)
{
LOG_DEBUG_FMT("HI_MPI_VB_SetConf failed, %#x!", s32Ret);
return HI_FAILURE;
}
// VB_CONFIG_S stVbConfig2 ={0};
// s32Ret = HI_MPI_VB_GetConfig(&stVbConfig2);
// printf("%s:%d HI_MPI_VB_GetConfig %d\n", __FILE__, __LINE__, s32Ret);
// printf("%s:%d Get u32MaxPoolCnt:%u\n", __FILE__, __LINE__, stVbConfig2.u32MaxPoolCnt);
// for (i = 0; i < stVbConfig2.u32MaxPoolCnt && i < VB_MAX_COMM_POOLS; i++)
// {
// printf("%s:%d Get Pool%d u32BlkCnt:%u u64BlkSize:%llu\n", __FILE__, __LINE__, i,
// stVbConfig2.astCommPool[i].u32BlkCnt, stVbConfig2.astCommPool[i].u64BlkSize);
// }
s32Ret = HI_MPI_VB_Init();
printf("%s:%d HI_MPI_VB_Init %#x\n", __FILE__, __LINE__, s32Ret);
if (HI_SUCCESS != s32Ret)
{
LOG_DEBUG_FMT("HI_MPI_VB_Init failed!\n");
return HI_FAILURE;
}
s32Ret = HI_MPI_SYS_Init();
printf("%s:%d HI_MPI_SYS_Init %#x\n", __FILE__, __LINE__, s32Ret);
if (HI_SUCCESS != s32Ret)
{
LOG_DEBUG_FMT("HI_MPI_SYS_Init failed!\n");
return HI_FAILURE;
}
return HI_SUCCESS;
}
二、VDEC初始化并获取VB:
根据每个解码通道的需要解码的视频的宽高,像素格式,位宽 以及不同的解码方式(如:h.264 h.265等)计算每个通道需要的VB大小。
模块公共视频缓存池
HI_MPI_VB_ExitModCommPool(VB_UID_VDEC);
HI_MPI_VB_SetModPoolConfig(VB_UID_VDEC, &stVbConf);//设置模块公共视频缓存池属性
HI_MPI_VB_InitModCommPool(VB_UID_VDEC);
HI_S32 SAMPLE_COMM_VDEC_InitVBPool(HI_U32 ChnNum, SAMPLE_VDEC_ATTR *pastSampleVdec)
{
VB_CONFIG_S stVbConf;
HI_S32 i, j, pos=0, s32Ret;
HI_BOOL bFindFlag;
SAMPLE_VDEC_BUF astSampleVdecBuf[VDEC_MAX_CHN_NUM];
VB_POOL_CONFIG_S stVbPoolCfg;
memset(astSampleVdecBuf, 0, sizeof(SAMPLE_VDEC_BUF)*VDEC_MAX_CHN_NUM);
memset(&stVbConf, 0, sizeof(VB_CONFIG_S));
for(i = 0; i < ChnNum; i++)
{
if(PT_H265 == pastSampleVdec[i].enType)
{
astSampleVdecBuf[i].u32PicBufSize = VDEC_GetPicBufferSize(pastSampleVdec[i].enType, pastSampleVdec[i].u32Width, pastSampleVdec[i].u32Height,
PIXEL_FORMAT_YVU_SEMIPLANAR_420, pastSampleVdec[i].stSapmleVdecVideo.enBitWidth, 0);
astSampleVdecBuf[i].u32TmvBufSize = VDEC_GetTmvBufferSize(pastSampleVdec[i].enType, pastSampleVdec[i].u32Width, pastSampleVdec[i].u32Height);
}
else if(PT_H264 == pastSampleVdec[i].enType)
{
astSampleVdecBuf[i].u32PicBufSize = VDEC_GetPicBufferSize(pastSampleVdec[i].enType, pastSampleVdec[i].u32Width, pastSampleVdec[i].u32Height,
PIXEL_FORMAT_YVU_SEMIPLANAR_420, pastSampleVdec[i].stSapmleVdecVideo.enBitWidth, 0);
if(VIDEO_DEC_MODE_IPB == pastSampleVdec[i].stSapmleVdecVideo.enDecMode)
{
astSampleVdecBuf[i].u32TmvBufSize = VDEC_GetTmvBufferSize(pastSampleVdec[i].enType, pastSampleVdec[i].u32Width, pastSampleVdec[i].u32Height);
}
}
else
{
astSampleVdecBuf[i].u32PicBufSize = VDEC_GetPicBufferSize(pastSampleVdec[i].enType, pastSampleVdec[i].u32Width, pastSampleVdec[i].u32Height,
pastSampleVdec[i].stSapmleVdecPicture.enPixelFormat, DATA_BITWIDTH_8, 0);
}
}
/* PicBuffer */
for(j = 0; j < VB_MAX_COMM_POOLS; j++)
{
bFindFlag = HI_FALSE;
for(i = 0; i < ChnNum; i++)
{
// printf("%s:%d j=%d i=%d bFindFlag:%d astSampleVdecBuf[%d].u32PicBufSize:%u astSampleVdecBuf[%d].bPicBufAlloc:%d\n", __FILE__, __LINE__,
// j, i, bFindFlag,
// i, astSampleVdecBuf[i].u32PicBufSize,
// i, astSampleVdecBuf[i].bPicBufAlloc);
if((HI_FALSE == bFindFlag) && (0 != astSampleVdecBuf[i].u32PicBufSize) && (HI_FALSE == astSampleVdecBuf[i].bPicBufAlloc) )
{
stVbConf.astCommPool[j].u64BlkSize = astSampleVdecBuf[i].u32PicBufSize;
stVbConf.astCommPool[j].u32BlkCnt = pastSampleVdec[i].u32FrameBufCnt;
astSampleVdecBuf[i].bPicBufAlloc = HI_TRUE;
bFindFlag = HI_TRUE;
pos = j;
// printf("%s:%d j=%d i=%d bFindFlag:%d pos:%d stVbConf.astCommPool[%d].u64BlkSize:%llu stVbConf.astCommPool[%d].u32BlkCnt:%d\n", __FILE__, __LINE__,
// j, i, bFindFlag,pos,
// j, stVbConf.astCommPool[j].u64BlkSize,
// j, stVbConf.astCommPool[j].u32BlkCnt);
}
if((HI_TRUE == bFindFlag) && (HI_FALSE == astSampleVdecBuf[i].bPicBufAlloc)
&& (stVbConf.astCommPool[j].u64BlkSize == astSampleVdecBuf[i].u32PicBufSize) )
{
stVbConf.astCommPool[j].u32BlkCnt += pastSampleVdec[i].u32FrameBufCnt;
astSampleVdecBuf[i].bPicBufAlloc = HI_TRUE;
// printf("%s:%d j=%d i=%d bFindFlag:%d pos:%d stVbConf.astCommPool[%d].u64BlkSize:%llu stVbConf.astCommPool[%d].u32BlkCnt:%d\n", __FILE__, __LINE__,
// j, i, bFindFlag,pos,
// j, stVbConf.astCommPool[j].u64BlkSize,
// j, stVbConf.astCommPool[j].u32BlkCnt);
}
}
}
// printf("%s:%d pos = %d\n\n", __FILE__, __LINE__, pos);
/* TmvBuffer */
for(j = pos + 1; j < VB_MAX_COMM_POOLS; j++)
{
bFindFlag = HI_FALSE;
for(i = 0; i < ChnNum; i++)
{
// printf("%s:%d j=%d i=%d bFindFlag:%d astSampleVdecBuf[%d].u32TmvBufSize:%u astSampleVdecBuf[%d].bTmvBufAlloc:%d\n", __FILE__, __LINE__,
// j, i, bFindFlag,
// i, astSampleVdecBuf[i].u32TmvBufSize,
// i, astSampleVdecBuf[i].bTmvBufAlloc);
if((HI_FALSE == bFindFlag) && (0 != astSampleVdecBuf[i].u32TmvBufSize) && (HI_FALSE == astSampleVdecBuf[i].bTmvBufAlloc) )
{
stVbConf.astCommPool[j].u64BlkSize = astSampleVdecBuf[i].u32TmvBufSize;
stVbConf.astCommPool[j].u32BlkCnt = pastSampleVdec[i].stSapmleVdecVideo.u32RefFrameNum+1;
astSampleVdecBuf[i].bTmvBufAlloc = HI_TRUE;
bFindFlag = HI_TRUE;
pos = j;
// printf("%s:%d j=%d i=%d bFindFlag:%d pos:%d stVbConf.astCommPool[%d].u64BlkSize:%llu stVbConf.astCommPool[%d].u32BlkCnt:%d\n", __FILE__, __LINE__,
// j, i, bFindFlag,pos,
// j, stVbConf.astCommPool[j].u64BlkSize,
// j, stVbConf.astCommPool[j].u32BlkCnt);
}
if((HI_TRUE == bFindFlag) && (HI_FALSE == astSampleVdecBuf[i].bTmvBufAlloc)
&& (stVbConf.astCommPool[j].u64BlkSize == astSampleVdecBuf[i].u32TmvBufSize) )
{
stVbConf.astCommPool[j].u32BlkCnt += pastSampleVdec[i].stSapmleVdecVideo.u32RefFrameNum+1;
astSampleVdecBuf[i].bTmvBufAlloc = HI_TRUE;
// printf("%s:%d j=%d i=%d bFindFlag:%d pos:%d stVbConf.astCommPool[%d].u64BlkSize:%llu stVbConf.astCommPool[%d].u32BlkCnt:%d\n", __FILE__, __LINE__,
// j, i, bFindFlag,pos,
// j, stVbConf.astCommPool[j].u64BlkSize,
// j, stVbConf.astCommPool[j].u32BlkCnt);
}
}
}
// printf("%s:%d pos = %d\n\n", __FILE__, __LINE__, pos);
stVbConf.u32MaxPoolCnt = pos + 1;
// printf("%s:%d stVbConf.u32MaxPoolCnt = %u\n", __FILE__, __LINE__, stVbConf.u32MaxPoolCnt);
// for (i = 0; i < stVbConf.u32MaxPoolCnt; i++)
// {
// printf("%s:%d stVbConf.astCommPool[%d] u32BlkCnt:%u u64BlkSize:%lu\n", __FILE__, __LINE__, i,
// stVbConf.astCommPool[i].u32BlkCnt,
// stVbConf.astCommPool[i].u64BlkSize);
// }
// for (i = 0; i < VDEC_MAX_CHN_NUM; i++)
// {
// printf("%s:%d astSampleVdecBuf[%d].u32PicBufSize:%u u32TmvBufSize:%u bPicBufAlloc:%d bTmvBufAlloc:%d\n", __FILE__, __LINE__, i,
// astSampleVdecBuf[i].u32PicBufSize,astSampleVdecBuf[i].u32TmvBufSize, astSampleVdecBuf[i].bPicBufAlloc, astSampleVdecBuf[i].bTmvBufAlloc);
// }
// printf("%s:%d g_enVdecVBSource:%d VB_SOURCE_MODULE:%d\n", __FILE__, __LINE__, g_enVdecVBSource, VB_SOURCE_MODULE);
if(VB_SOURCE_MODULE == g_enVdecVBSource)
{
s32Ret = HI_MPI_VB_ExitModCommPool(VB_UID_VDEC);
printf("%s:%d HI_MPI_VB_ExitModCommPool(VB_UID_VDEC %d) %d\n", __FILE__, __LINE__, VB_UID_VDEC, s32Ret);
CHECK_RET(HI_MPI_VB_SetModPoolConfig(VB_UID_VDEC, &stVbConf), "HI_MPI_VB_SetModPoolConfigig");
s32Ret = HI_MPI_VB_InitModCommPool(VB_UID_VDEC);
printf("%s:%d HI_MPI_VB_InitModCommPool(VB_UID_VDEC %d) %d\n", __FILE__, __LINE__, VB_UID_VDEC, s32Ret);
if (HI_SUCCESS != s32Ret)
{
printf("HI_MPI_VB_InitModCommPool fail for 0x%x\n", s32Ret);
HI_MPI_VB_ExitModCommPool(VB_UID_VDEC);
return HI_FAILURE;
}
}
else if (VB_SOURCE_USER == g_enVdecVBSource)
{
for (i = 0; i < ChnNum; i++)
{
if ( (0 != astSampleVdecBuf[i].u32PicBufSize) && (0 != pastSampleVdec[i].u32FrameBufCnt))
{
memset(&stVbPoolCfg, 0, sizeof(VB_POOL_CONFIG_S));
stVbPoolCfg.u64BlkSize = astSampleVdecBuf[i].u32PicBufSize;
stVbPoolCfg.u32BlkCnt = pastSampleVdec[i].u32FrameBufCnt;
stVbPoolCfg.enRemapMode = VB_REMAP_MODE_NONE;
g_ahPicVbPool[i] = HI_MPI_VB_CreatePool(&stVbPoolCfg);
if (VB_INVALID_POOLID == g_ahPicVbPool[i])
{
goto fail;
}
}
if (0 != astSampleVdecBuf[i].u32TmvBufSize)
{
memset(&stVbPoolCfg, 0, sizeof(VB_POOL_CONFIG_S));
stVbPoolCfg.u64BlkSize = astSampleVdecBuf[i].u32TmvBufSize;
stVbPoolCfg.u32BlkCnt = pastSampleVdec[i].stSapmleVdecVideo.u32RefFrameNum+1;
stVbPoolCfg.enRemapMode = VB_REMAP_MODE_NONE;
g_ahTmvVbPool[i] = HI_MPI_VB_CreatePool(&stVbPoolCfg);
if (VB_INVALID_POOLID == g_ahTmvVbPool[i])
{
goto fail;
}
}
}
}
return HI_SUCCESS;
fail:
for (;i>=0;i--)
{
if (VB_INVALID_POOLID != g_ahPicVbPool[i])
{
s32Ret = HI_MPI_VB_DestroyPool(g_ahPicVbPool[i]);
if(HI_SUCCESS != s32Ret)
{
printf("HI_MPI_VB_DestroyPool %d fail!\n",g_ahPicVbPool[i]);
}
g_ahPicVbPool[i] = VB_INVALID_POOLID;
}
if (VB_INVALID_POOLID != g_ahTmvVbPool[i])
{
s32Ret = HI_MPI_VB_DestroyPool(g_ahTmvVbPool[i]);
if(HI_SUCCESS != s32Ret)
{
printf("HI_MPI_VB_DestroyPool %d fail!\n",g_ahTmvVbPool[i]);
}
g_ahTmvVbPool[i] = VB_INVALID_POOLID;
}
}
return HI_FAILURE;
}
三、start VDEC:
HI_MPI_VDEC_SetModParam(&stModParam);//主要是设置VB 来源选择
以下的操作,在每个解码通道都需要操作一遍。
HI_MPI_VDEC_CreateChn(i, &stChnAttr[i])//创建视频解码的通道。
HI_MPI_VDEC_SetChnParam(i, &stChnParam)//设置解码通道i参数stChnParam
HI_MPI_VDEC_StartRecvStream(i)//使能解码通道i
HI_S32 SAMPLE_COMM_VDEC_Start(HI_S32 s32ChnNum, SAMPLE_VDEC_ATTR *pastSampleVdec)
{
HI_S32 i;
VDEC_CHN_ATTR_S stChnAttr[VDEC_MAX_CHN_NUM];
VDEC_CHN_POOL_S stPool;
VDEC_CHN_PARAM_S stChnParam;
VDEC_MOD_PARAM_S stModParam;
printf("%s:%d s32ChnNum %d\n", __FILE__, __LINE__, s32ChnNum);
CHECK_RET(HI_MPI_VDEC_GetModParam(&stModParam), "HI_MPI_VDEC_GetModParam");
stModParam.enVdecVBSource = g_enVdecVBSource;
CHECK_RET(HI_MPI_VDEC_SetModParam(&stModParam), "HI_MPI_VDEC_SetModParam");
printf("VDEC_ModParam\n");
printf("VBSource:%d\n", stModParam.enVdecVBSource);
printf("u32MiniBufMode:%u\n", stModParam.u32MiniBufMode);
printf("u32ParallelMode:%u\n", stModParam.u32ParallelMode);
printf("stVideoModParam: u32MaxPicWidth%u u32MaxPicHeight%u u32MaxSliceNum%u u32VdhMsgNum%u u32VdhBinSize%u u32VdhExtMemLevel%u\n", stModParam.stVideoModParam.u32MaxPicWidth, stModParam.stVideoModParam.u32MaxPicHeight,
stModParam.stVideoModParam.u32MaxSliceNum, stModParam.stVideoModParam.u32VdhMsgNum, stModParam.stVideoModParam.u32VdhBinSize, stModParam.stVideoModParam.u32VdhExtMemLevel);
printf("--------------\n");
for(i = 0; i < s32ChnNum; i++)
{
stChnAttr[i].enType = pastSampleVdec[i].enType;
stChnAttr[i].enMode = pastSampleVdec[i].enMode;
stChnAttr[i].u32PicWidth = pastSampleVdec[i].u32Width;
stChnAttr[i].u32PicHeight = pastSampleVdec[i].u32Height;
stChnAttr[i].u32StreamBufSize = pastSampleVdec[i].u32Width*pastSampleVdec[i].u32Height;
stChnAttr[i].u32FrameBufCnt = pastSampleVdec[i].u32FrameBufCnt;
if (PT_H264 == pastSampleVdec[i].enType || PT_H265 == pastSampleVdec[i].enType)
{
stChnAttr[i].stVdecVideoAttr.u32RefFrameNum = pastSampleVdec[i].stSapmleVdecVideo.u32RefFrameNum;
stChnAttr[i].stVdecVideoAttr.bTemporalMvpEnable = HI_TRUE;
if ((PT_H264 == pastSampleVdec[i].enType) && (VIDEO_DEC_MODE_IPB != pastSampleVdec[i].stSapmleVdecVideo.enDecMode))
{
stChnAttr[i].stVdecVideoAttr.bTemporalMvpEnable = HI_FALSE;
}
stChnAttr[i].u32FrameBufSize = VDEC_GetPicBufferSize(stChnAttr[i].enType, pastSampleVdec[i].u32Width, pastSampleVdec[i].u32Height,
PIXEL_FORMAT_YVU_SEMIPLANAR_420, pastSampleVdec[i].stSapmleVdecVideo.enBitWidth, 0);
stChnAttr[i].stVdecVideoAttr.u32TmvBufSize = VDEC_GetTmvBufferSize(stChnAttr[i].enType, pastSampleVdec[i].u32Width, pastSampleVdec[i].u32Height);
}
else if (PT_JPEG == pastSampleVdec[i].enType || PT_MJPEG == pastSampleVdec[i].enType)
{
stChnAttr[i].enMode = VIDEO_MODE_FRAME;
stChnAttr[i].u32FrameBufSize = VDEC_GetPicBufferSize(stChnAttr[i].enType, pastSampleVdec[i].u32Width, pastSampleVdec[i].u32Height,
pastSampleVdec[i].stSapmleVdecPicture.enPixelFormat, DATA_BITWIDTH_8, 0);
}
printf("ChnAttr%d\n", i);
printf("PAYLOAD_TYPE_E %d (PT_H264:%d)\n", stChnAttr[i].enType, PT_H264);
printf("VIDEO_MODE_E %d (VIDEO_MODE_FRAME:%d)\n", stChnAttr[i].enMode, VIDEO_MODE_FRAME);
printf("u32PicWidth %u u32PicHeight %u\n", stChnAttr[i].u32PicWidth, stChnAttr[i].u32PicHeight);
printf("u32StreamBufSize %u u32FrameBufSize %u u32FrameBufCnt%u\n", stChnAttr[i].u32StreamBufSize, stChnAttr[i].u32FrameBufSize, stChnAttr[i].u32FrameBufCnt);
printf("stVdecVideoAttr bTemporalMvpEnable%d u32RefFrameNum %u u32TmvBufSize%u\n", stChnAttr[i].stVdecVideoAttr.bTemporalMvpEnable,
stChnAttr[i].stVdecVideoAttr.u32RefFrameNum, stChnAttr[i].stVdecVideoAttr.u32TmvBufSize);
printf("--------------\n");
CHECK_CHN_RET(HI_MPI_VDEC_CreateChn(i, &stChnAttr[i]), i, "HI_MPI_VDEC_CreateChn");
printf("HI_MPI_VDEC_CreateChn %d OK\n", i);
if (VB_SOURCE_USER == g_enVdecVBSource)
{
stPool.hPicVbPool = g_ahPicVbPool[i];
stPool.hTmvVbPool = g_ahTmvVbPool[i];
CHECK_CHN_RET(HI_MPI_VDEC_AttachVbPool(i, &stPool), i, "HI_MPI_VDEC_AttachVbPool");
printf("not see this\n");
}
CHECK_CHN_RET(HI_MPI_VDEC_GetChnParam(i, &stChnParam), i, "HI_MPI_VDEC_GetChnParam");
if (PT_H264 == pastSampleVdec[i].enType || PT_H265 == pastSampleVdec[i].enType)
{
stChnParam.stVdecVideoParam.enDecMode = pastSampleVdec[i].stSapmleVdecVideo.enDecMode;
stChnParam.stVdecVideoParam.enCompressMode = COMPRESS_MODE_TILE;
stChnParam.stVdecVideoParam.enVideoFormat = VIDEO_FORMAT_TILE_64x16;
if(VIDEO_DEC_MODE_IPB == stChnParam.stVdecVideoParam.enDecMode)
{
stChnParam.stVdecVideoParam.enOutputOrder = VIDEO_OUTPUT_ORDER_DISP;
}
else
{
stChnParam.stVdecVideoParam.enOutputOrder = VIDEO_OUTPUT_ORDER_DEC;
}
}
else
{
stChnParam.stVdecPictureParam.enPixelFormat = pastSampleVdec[i].stSapmleVdecPicture.enPixelFormat;
stChnParam.stVdecPictureParam.u32Alpha = pastSampleVdec[i].stSapmleVdecPicture.u32Alpha;
}
stChnParam.u32DisplayFrameNum = pastSampleVdec[i].u32DisplayFrameNum;
CHECK_CHN_RET(HI_MPI_VDEC_SetChnParam(i, &stChnParam), i, "HI_MPI_VDEC_GetChnParam");
CHECK_CHN_RET(HI_MPI_VDEC_StartRecvStream(i), i, "HI_MPI_VDEC_StartRecvStream");
printf("HI_MPI_VDEC_StartRecvStream %d OK\n", i);
}
return HI_SUCCESS;
}
四、start VPSS:
每个解码通道一个VPSSgroup,每个VPSSgroup中有4个phy通道
/*****************************************************************************
* function : start vpss grp.
*****************************************************************************/
HI_S32 SAMPLE_COMM_VPSS_Start(VPSS_GRP VpssGrp, HI_BOOL* pabChnEnable, VPSS_GRP_ATTR_S* pstVpssGrpAttr, VPSS_CHN_ATTR_S* pastVpssChnAttr)
{
VPSS_CHN VpssChn;
HI_S32 s32Ret;
HI_S32 j;
s32Ret = HI_MPI_VPSS_CreateGrp(VpssGrp, pstVpssGrpAttr);
printf("%s:%d HI_MPI_VPSS_CreateGrp(grp:%d) %d\n", __FILE__, __LINE__, VpssGrp, s32Ret);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_PRT("HI_MPI_VPSS_CreateGrp(grp:%d) failed with %#x!\n", VpssGrp, s32Ret);
return HI_FAILURE;
}
for (j = 0; j < VPSS_MAX_PHY_CHN_NUM; j++)
{
if(HI_TRUE == pabChnEnable[j])
{
VpssChn = j;
s32Ret = HI_MPI_VPSS_SetChnAttr(VpssGrp, VpssChn, &pastVpssChnAttr[VpssChn]);
printf("%s:%d HI_MPI_VPSS_SetChnAttr(grp:%d chn:%d) %d\n", __FILE__, __LINE__, VpssGrp, VpssChn, s32Ret);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_PRT("HI_MPI_VPSS_SetChnAttr failed with %#x\n", s32Ret);
return HI_FAILURE;
}
s32Ret = HI_MPI_VPSS_EnableChn(VpssGrp, VpssChn);
printf("%s:%d HI_MPI_VPSS_EnableChn(grp:%d chn:%d) %d\n", __FILE__, __LINE__, VpssGrp, VpssChn, s32Ret);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_PRT("HI_MPI_VPSS_EnableChn failed with %#x\n", s32Ret);
return HI_FAILURE;
}
}
}
s32Ret = HI_MPI_VPSS_StartGrp(VpssGrp);
printf("%s:%d HI_MPI_VPSS_StartGrp(grp:%d) %d\n", __FILE__, __LINE__, VpssGrp, s32Ret);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_PRT("HI_MPI_VPSS_StartGrp failed with %#x\n", s32Ret);
return HI_FAILURE;
}
return HI_SUCCESS;
}
五、start VO:

DHD0: Device HD0, 超高清设备 0。
DHD1: Device HD1, 高清设备 1。
VHD0: Video layer of HD0, 超高清视频层 0, 隶属于 DHD0。
VHD1: Video layer of HD1,高清视频层 1,隶属于 DHD1。
VHD2: Video layer of HD 2, 高清视频层 2, Hi3559AV100 上隶属于 DHD0,
Hi3519AV100/Hi3556AV100 上可以绑定至 DHD0 或者 DHD1, 用作 PIP 层。
WD: Write Back Channel Device, 回写通道设备。
图形层 G3: Graphic layer 3, 用作鼠标层, DHD0 和 DHD1 中均有此项, 但只能绑定其中一个设备,
G3 默认绑定在 DHD1 上。
HI_MPI_VO_SetPubAttr(VoDev, pstPubAttr);// 配置视频输出设备VoDev(根据不同的芯片,有所不同)的公共属性(hdmi或者mipi,视频的输出格式如1080P60)。
HI_MPI_VO_Enable(VoDev);//使能视频输出设备
HI_MPI_VO_GetVideoLayerAttr(VoLayer, &stLayerAttr);// VoLayer(0.1.2即高清视频层0.1.2)获取视频层属性
HI_MPI_VO_SetChnAttr(VoLayer, i, &stChnAttr);//同一个图层VoLayer中,不同的视频输出通道i的属性
HI_MPI_VO_EnableChn(VoLayer, i);//使能同一个图层的不同的视频输出通道。
HI_MPI_VO_SetVideoLayerPartitionMode(VoLayer, enVoPartMode);// 设置当前视频层VoLayer的分割模式(VO_PART_MODE_MULTI,VO_PART_MODE_SINGLE)。
HI_MPI_VO_SetDisplayBufLen(VoLayer, pstVoConfig->u32DisBufLen);
HI_MPI_VO_SetVideoLayerAttr(VoLayer, pstLayerAttr);// 设备分辨率、显示分辨率和图像分辨率
HI_MPI_VO_EnableVideoLayer(VoLayer);
六、VDEC bind VPSS:
stSrcChn.enModId = HI_ID_VDEC;
stSrcChn.s32DevId = 0;
stSrcChn.s32ChnId = VdecChn;
stDestChn.enModId = HI_ID_VPSS;
stDestChn.s32DevId = VpssGrp;
stDestChn.s32ChnId = 0;
HI_MPI_SYS_Bind(&stSrcChn, &stDestChn)
HI_S32 SAMPLE_COMM_VDEC_Bind_VPSS(VDEC_CHN VdecChn, VPSS_GRP VpssGrp)
{
MPP_CHN_S stSrcChn;
MPP_CHN_S stDestChn;
stSrcChn.enModId = HI_ID_VDEC;
stSrcChn.s32DevId = 0;
stSrcChn.s32ChnId = VdecChn;
stDestChn.enModId = HI_ID_VPSS;
stDestChn.s32DevId = VpssGrp;
stDestChn.s32ChnId = 0;
CHECK_RET(HI_MPI_SYS_Bind(&stSrcChn, &stDestChn), "HI_MPI_SYS_Bind(VDEC-VPSS)");
return HI_SUCCESS;
}
七、VPSS bind VO:
stSrcChn.enModId = HI_ID_VPSS;
stSrcChn.s32DevId = VpssGrp;
stSrcChn.s32ChnId = VpssChn;//固定为0
stDestChn.enModId = HI_ID_VO;
stDestChn.s32DevId = VoLayer;//Vodev
stDestChn.s32ChnId = VoChn;//对应于VpssGrp
HI_MPI_SYS_Bind(&stSrcChn, &stDestChn)
HI_S32 SAMPLE_COMM_VO_Bind_VO(VO_LAYER SrcVoLayer, VO_CHN SrcVoChn, VO_LAYER DstVoLayer, VO_CHN DstVoChn)
{
MPP_CHN_S stSrcChn, stDestChn;
stSrcChn.enModId = HI_ID_VO;
stSrcChn.s32DevId = SrcVoLayer;
stSrcChn.s32ChnId = SrcVoChn;
stDestChn.enModId = HI_ID_VO;
stDestChn.s32DevId = DstVoLayer;
stDestChn.s32ChnId = DstVoChn;
return HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);
}
八、send stream to vdec(选定需要解码的文件路径和文件名)
- 每个解码通道创建一个相对应的线程,当应用程序退出时,直接退出线程即可。
void *MPP_VDEC_SendStream_Thread(void *arg)
{
if (arg == NULL)
{
return NULL;
}
int iChn = *((int *)arg);
if (iChn < 0 || iChn > MAX_VIDEO_CHANNEL_NUM - 1)
{
return NULL;
}
char threadName[128] = {0};
sprintf(threadName, "VdecSend%d", iChn);
prctl(PR_SET_NAME, threadName, 0,0,0);
fflush(stdout);
bool bStart = false;
while(AlgDecMan::GetInstance()->m_bExitThread == false)
{
H26X_Queue_Node * ptDataNode = AlgIPCMan::GetInstance()->GetH26QueHandle(iChn)->GetH26XFrameData();
if(ptDataNode == NULL)
{
usleep(20*1000);
continue;
}
unsigned char *frame = (unsigned char *)(ptDataNode->pFrame);
if (!bStart)
{
int tmp = *(frame+4) & 0x1F;
if ( *(frame) == 0 && *(frame+1) == 0 && *(frame+2) == 0 && *(frame+3) == 1 &&
(((tmp == 0x5 || tmp == 0x1) && ((*(frame+5)&0x80) == 0x80)) ||
(tmp == 20 && (*(frame+8)&0x80) == 0x80)
)
)
{
LOG_DEBUG_FMT("=================Chn%d Got Start Frame========", iChn);
bStart = true;
}
else
{
//printf("tmp:%x(%d) %d %d %d %d*%d %x %x %x %x %x %x %x\n",tmp,tmp, ptDataNode->h264_info.channel, ptDataNode->h264_info.frame_id, ptDataNode->h264_info.frame_type,
// ptDataNode->h264_info.width, ptDataNode->h264_info.height,
// ptDataNode->frame_data[0],ptDataNode->frame_data[1], ptDataNode->frame_data[2],ptDataNode->frame_data[3],
// ptDataNode->frame_data[4],ptDataNode->frame_data[5], ptDataNode->frame_data[6],ptDataNode->frame_data[7]);
}
}
#ifdef DECODE_DEBUG
AlgDecMan::GetInstance()->CountInputInfo(iChn, &ptDataNode->h26x_info);
#endif
if (!bStart)
{
AlgIPCMan::GetInstance()->GetH26QueHandle(iChn)->ReleaseH26XFrameData(ptDataNode);
continue;
}
#ifdef SAVE_H264
static unsigned int counth[MAX_VIDEO_CHANNEL_NUM] = {0};
if(counth[iChn] < 500)
{
fwrite(ptDataNode->pFrame, 1, ptDataNode->h26x_info.data_size, fp_h264[iChn]);
counth[iChn]++;
}
else
{
if(fp_h264[iChn]){
fclose(fp_h264[iChn]);
fp_h264[iChn] = NULL;
printf("\n===SAVE_H264%d DONE===\n",iChn);
}
}
#endif
VDEC_STREAM_S stStream;
stStream.u64PTS = -1;//ptDataNode->h264_info.timestamp;如果用户送入的 PTS 值为-1,则表示此图像不会被视频输出模块(VO)显示
stStream.pu8Addr = (unsigned char *)ptDataNode->pFrame;
stStream.u32Len = ptDataNode->h26x_info.data_size;
stStream.bEndOfFrame = HI_TRUE;
stStream.bEndOfStream = HI_FALSE;
stStream.bDisplay = HI_TRUE;
HI_S32 s32Ret = HI_MPI_VDEC_SendStream(iChn, &stStream, 100);
if(HI_SUCCESS != s32Ret)
{
printf("Chn%d HI_MPI_VDEC_SendStream failed, s32Ret=0x%X \n", iChn, s32Ret);
}
AlgIPCMan::GetInstance()->GetH26QueHandle(iChn)->ReleaseH26XFrameData(ptDataNode);
}
return NULL;
}
void * MPP_VDEC_GetPic_Thread(void *arg)
{
if (arg == NULL)
{
return NULL;
}
int iChn = *((int *)arg);
if (iChn < 0 || iChn > MAX_VIDEO_CHANNEL_NUM - 1)
{
return NULL;
}
char threadName[128] = {0};
sprintf(threadName, "VdecGetPic%d", iChn);
prctl(PR_SET_NAME, threadName, 0,0,0);
//set_cpu_thread(A73_CORE);
static int s_frame_id[MAX_VIDEO_CHANNEL_NUM] = {0};
while(AlgDecMan::GetInstance()->m_bExitThread == false)
{
VIDEO_FRAME_INFO_S stVFrameInfo;
HI_S32 s32Ret = HI_MPI_VPSS_GetChnFrame(iChn, VPSS_CHN0, &stVFrameInfo, 200);
if(s32Ret == HI_SUCCESS)
{
#ifdef DECODE_DEBUG
AlgDecMan::GetInstance()->CountOutputInfo(iChn, stVFrameInfo.stVFrame.u32Width, stVFrameInfo.stVFrame.u32Height);
#endif
Yuv_Queue_Node *pYuvNode = AlgIPCMan::GetInstance()->GetYuvQueHandle(iChn)->PutYuvNode();
if(pYuvNode != NULL)
{
struct timeval _tv;
gettimeofday(&_tv, NULL);
unsigned long long timestamp = _tv.tv_sec*1000 + _tv.tv_usec/1000;
pYuvNode->yuv_info.timestamp = timestamp;
pYuvNode->yuv_info.frame_id = s_frame_id[iChn]++;
pYuvNode->yuv_info.channel = iChn;
pYuvNode->yuv_info.width = stVFrameInfo.stVFrame.u32Width;
pYuvNode->yuv_info.height = stVFrameInfo.stVFrame.u32Height;
pYuvNode->yuv_info.data_size = 0;
HI_U32 u32Size, s32Ysize, u32UvHeight;
if (PIXEL_FORMAT_YVU_SEMIPLANAR_420 == stVFrameInfo.stVFrame.enPixelFormat)
{
s32Ysize = (stVFrameInfo.stVFrame.u64PhyAddr[1] - stVFrameInfo.stVFrame.u64PhyAddr[0]);
u32Size = s32Ysize*3/2;
u32UvHeight = stVFrameInfo.stVFrame.u32Height / 2;
}
else if(PIXEL_FORMAT_YVU_SEMIPLANAR_422 == stVFrameInfo.stVFrame.enPixelFormat)
{
s32Ysize = (stVFrameInfo.stVFrame.u64PhyAddr[1] - stVFrameInfo.stVFrame.u64PhyAddr[0]);
u32Size = s32Ysize*2;
u32UvHeight = stVFrameInfo.stVFrame.u32Height;
}
else
{
printf("%s %d: This YUV format is not support!\n", __FILE__, __LINE__);
HI_MPI_VPSS_ReleaseChnFrame(iChn, VPSS_CHN0, &stVFrameInfo);
continue;
}
pYuvNode->yuv_info.data_size = u32Size;
HI_U64 phy_addr = stVFrameInfo.stVFrame.u64PhyAddr[0];
HI_U8* pY_map = (HI_U8*) HI_MPI_SYS_Mmap(phy_addr, u32Size);
if (HI_NULL == pY_map)
{
printf("HI_MPI_SYS_Mmap for pY_map fail!!\n");
HI_MPI_VPSS_ReleaseChnFrame(iChn, VPSS_CHN0, &stVFrameInfo);
continue;
}
/*// Y
for (unsigned int h = 0; h < stVFrameInfo.stVFrame.u32Height; h++)
{
HI_U8* pMemContent = pY_map + h * stVFrameInfo.stVFrame.u32Stride[0];
memcpy(((char *)(pYuvNode->yuv_data) + pYuvNode->yuv_info.data_size), pMemContent, stVFrameInfo.stVFrame.u32Width);
pYuvNode->yuv_info.data_size += stVFrameInfo.stVFrame.u32Width;
}
// UV
HI_U8* pC_map = pY_map + s32Ysize;
for (unsigned int h = 0; h < u32UvHeight; h++)
{
HI_U8*pMemContent = pC_map + h * stVFrameInfo.stVFrame.u32Stride[1];
memcpy(((char *)(pYuvNode->yuv_data) + pYuvNode->yuv_info.data_size), pMemContent, stVFrameInfo.stVFrame.u32Width);
pYuvNode->yuv_info.data_size += stVFrameInfo.stVFrame.u32Width;
}*/
memcpy(pYuvNode->yuv_data, pY_map,pYuvNode->yuv_info.data_size);
HI_MPI_SYS_Munmap(pY_map, u32Size);
pY_map = HI_NULL;
#ifdef SAVE_YUV
static unsigned int count[MAX_VIDEO_CHANNEL_NUM] = {0};
if(count[iChn] < 500)
{
fwrite(pYuvNode->yuv_data, 1, pYuvNode->yuv_info.data_size, fp_yuv[iChn]);
count[iChn]++;
}
else
{
if(fp_yuv[iChn]){
fclose(fp_yuv[iChn]);
fp_yuv[iChn] = NULL;
printf("\n===SAVE_YUV%d DONE===\n",iChn);
}
}
#endif
}
else
{
#ifdef DECODE_DEBUG
AlgDecMan::GetInstance()->CountOutputInfoMiss(iChn);
#endif
}
HI_MPI_VPSS_ReleaseChnFrame(iChn, VPSS_CHN0, &stVFrameInfo);
AlgIPCMan::GetInstance()->GetYuvQueHandle(iChn)->finish();
//usleep(10000);
}
}
pthread_exit(arg);
return NULL;
}
九、应用退出:
pthread_join(pVdecThread[i], HI_NULL);
vpss vo unbind:HI_MPI_SYS_UnBind(&stSrcChn, &stDestChn);
vdec vpss unbind
HI_MPI_HDMI_Stop(enHdmiId)
HI_MPI_HDMI_Close(enHdmiId)
HI_MPI_HDMI_DeInit()
HI_MPI_VO_DisableChn(VoLayer, i);
HI_MPI_VO_DisableVideoLayer(VoLayer);
HI_MPI_VO_Disable(VoDev);
HI_MPI_VPSS_StopGrp(VpssGrp);//停止每个vpssgroup
HI_MPI_VPSS_DisableChn(VpssGrp, VpssChn);//禁止vpss下的通道
HI_MPI_VPSS_DestroyGrp(VpssGrp);//销毁VpssGrp
//多个解码通道的话,每个通道都要分别执行
HI_MPI_VDEC_StopRecvStream(i)//解码通道i停止接收数据流
HI_MPI_VDEC_DestroyChn(i)//销毁解码通道i
HI_MPI_VB_ExitModCommPool(VB_UID_VDEC);//VDEC退出模块公共视频缓存池
HI_MPI_SYS_Exit();
HI_MPI_VB_Exit();
视屏初始化流程
#define MAX_VIDEO_CHANNEL_NUM 4
static int Hi3559A_MPP_VDEC_INIT(int width,int height)
{
HI_S32 s32ret;
MPP_VERSION_S mppver = {0};
int SolutionW[MAX_VIDEO_CHANNEL_NUM]={0};
int SolutionH[MAX_VIDEO_CHANNEL_NUM]={0};
//读取配置中的视频流 w h,默认2560 1440
for(int i=0; i<MAX_VIDEO_CHANNEL_NUM; i++)
{
//GetVideoSolutionCfg(i,SolutionW[i],SolutionH[i]);
SolutionW[i] = width;
SolutionH[i] = height;
LOG_DEBUG_FMT("[Hi3559A_MPP_VDEC_INIT]i %d %s:%d SolutionW %u* SolutionH%u\n",i, __FILE__, __LINE__,SolutionW[i], SolutionH[i]);
}
s32ret = HI_MPI_SYS_GetVersion(&mppver);//芯片版本
LOG_DEBUG_FMT("[Hi3559A_MPP_VDEC_INIT]MPP Version:%s start...\n", mppver.aVersion);
HI_U32 u32ChipId;
s32ret = HI_MPI_SYS_GetChipId(&u32ChipId);//芯片id
if (s32ret == HI_SUCCESS)
{
printf("CHIP ID: %#x\n", u32ChipId);
}
HI_U32 u32CustomCode;
s32ret = HI_MPI_SYS_GetCustomCode(&u32CustomCode);//编号
if (s32ret == HI_SUCCESS)
{
printf("CustomCode: %#x\n", u32CustomCode);
}
HI_S32 s32Ret = HI_SUCCESS;
HI_U32 u32VdecChnNum = MAX_VIDEO_CHANNEL_NUM;
//获取4路配置的具体分辨率,直接读取配置文件
SIZE_S stDispSize[MAX_VIDEO_CHANNEL_NUM];
for(int i = 0; i<MAX_VIDEO_CHANNEL_NUM; i++)
{
stDispSize[i].u32Width = SolutionW[i];
stDispSize[i].u32Height = (SolutionH[i] == 1080)?1088:(SolutionH[i]);
LOG_DEBUG_FMT("[Hi3559A_MPP_VDEC_INIT]%s:%d u32Width %u* u32Height%u\n", __FILE__, __LINE__,stDispSize[i].u32Width, stDispSize[i].u32Height);
}
// 初始化公共视频缓存池属性 初始化MPP系统
VB_CONFIG_S stVbConfig;
memset(&stVbConfig, 0, sizeof(VB_CONFIG_S));
stVbConfig.u32MaxPoolCnt = 8;
stVbConfig.astCommPool[0].u32BlkCnt = 5*2;
stVbConfig.astCommPool[0].u64BlkSize = COMMON_GetPicBufferSize(stDispSize[0].u32Width, stDispSize[0].u32Height, PIXEL_FORMAT_YVU_SEMIPLANAR_420, DATA_BITWIDTH_8, COMPRESS_MODE_SEG, 0);
stVbConfig.astCommPool[0].u64BlkSize = ALIGN_UP(stVbConfig.astCommPool[0].u64BlkSize, 4096);
stVbConfig.astCommPool[1].u32BlkCnt = 5*2;
stVbConfig.astCommPool[1].u64BlkSize =COMMON_GetPicBufferSize(stDispSize[1].u32Width, stDispSize[1].u32Height, PIXEL_FORMAT_YVU_SEMIPLANAR_420, DATA_BITWIDTH_8, COMPRESS_MODE_SEG, 0);;
stVbConfig.astCommPool[1].u64BlkSize = ALIGN_UP(stVbConfig.astCommPool[1].u64BlkSize, 4096);
stVbConfig.astCommPool[2].u32BlkCnt = 5*2;
stVbConfig.astCommPool[2].u64BlkSize = COMMON_GetPicBufferSize(stDispSize[2].u32Width, stDispSize[2].u32Height, PIXEL_FORMAT_YVU_SEMIPLANAR_420, DATA_BITWIDTH_8, COMPRESS_MODE_SEG, 0);
stVbConfig.astCommPool[2].u64BlkSize = ALIGN_UP(stVbConfig.astCommPool[2].u64BlkSize, 4096);
stVbConfig.astCommPool[3].u32BlkCnt = 5*2;
stVbConfig.astCommPool[3].u64BlkSize = COMMON_GetPicBufferSize(stDispSize[3].u32Width, stDispSize[3].u32Height, PIXEL_FORMAT_YVU_SEMIPLANAR_420, DATA_BITWIDTH_8, COMPRESS_MODE_SEG, 0);
stVbConfig.astCommPool[3].u64BlkSize = ALIGN_UP(stVbConfig.astCommPool[3].u64BlkSize, 4096);
//按照最大一帧内存申请 加上osd的高度
stVbConfig.astCommPool[4].u32BlkCnt =u32VdecChnNum*2;//VENC yuv-->jpg
stVbConfig.astCommPool[4].u64BlkSize = COMMON_GetPicBufferSize(3840, 2360, PIXEL_FORMAT_YVU_SEMIPLANAR_420, DATA_BITWIDTH_8, COMPRESS_MODE_SEG, 0);
stVbConfig.astCommPool[4].u64BlkSize = ALIGN_UP(stVbConfig.astCommPool[4].u64BlkSize, 4096);
stVbConfig.astCommPool[5].u32BlkCnt =u32VdecChnNum*2;//alg use VENC NV21-->jpg
stVbConfig.astCommPool[5].u64BlkSize = stVbConfig.astCommPool[4].u64BlkSize;
stVbConfig.astCommPool[6].u32BlkCnt =u32VdecChnNum*2;//alg regsize
stVbConfig.astCommPool[6].u64BlkSize = stVbConfig.astCommPool[4].u64BlkSize;
s32Ret = SAMPLE_COMM_SYS_Init(&stVbConfig);
printf("%s:%d SAMPLE_COMM_SYS_Init %#x\n", __FILE__, __LINE__, s32Ret);
if(s32Ret != HI_SUCCESS)
{
LOG_DEBUG_FMT("SAMPLE_COMM_SYS_Init failed, %d!", s32Ret);
goto END1;
}
// init module VB or user VB(for VDEC)
SAMPLE_VDEC_ATTR astSampleVdec[2*MAX_VIDEO_CHANNEL_NUM];//增加一个解码通道jpg-->rgb
for(unsigned int i = 0; i < u32VdecChnNum; i++)
{
astSampleVdec[i].enType = PT_H264; // H264
astSampleVdec[i].u32Width = stDispSize[i].u32Width;
astSampleVdec[i].u32Height = stDispSize[i].u32Height;
astSampleVdec[i].enMode = VIDEO_MODE_FRAME; // Send By Frame
astSampleVdec[i].stSapmleVdecVideo.enDecMode = VIDEO_DEC_MODE_IP; // 不解B帧
astSampleVdec[i].stSapmleVdecVideo.enBitWidth = DATA_BITWIDTH_8;
astSampleVdec[i].stSapmleVdecVideo.u32RefFrameNum = 10;//参考帧的数目
astSampleVdec[i].u32DisplayFrameNum =4;//解码缓存图像的最小帧数
astSampleVdec[i].u32FrameBufCnt = astSampleVdec[i].stSapmleVdecVideo.u32RefFrameNum + astSampleVdec[i].u32DisplayFrameNum + 1;
}
for(unsigned int i = 0; i < u32VdecChnNum; i++)
{
//增加一个解码通道jpg-->NV21
astSampleVdec[MAX_VIDEO_CHANNEL_NUM+i].enType = PT_JPEG; // H264
astSampleVdec[MAX_VIDEO_CHANNEL_NUM+i].u32Width = stDispSize[i].u32Width;
astSampleVdec[MAX_VIDEO_CHANNEL_NUM+i].u32Height = stDispSize[i].u32Height;
astSampleVdec[MAX_VIDEO_CHANNEL_NUM+i].enMode = VIDEO_MODE_FRAME; // Send By Frame
astSampleVdec[MAX_VIDEO_CHANNEL_NUM+i].stSapmleVdecPicture.enPixelFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
astSampleVdec[MAX_VIDEO_CHANNEL_NUM+i].stSapmleVdecPicture.u32Alpha = 255;
astSampleVdec[MAX_VIDEO_CHANNEL_NUM+i].u32DisplayFrameNum =4;//解码缓存图像的最小帧数
astSampleVdec[MAX_VIDEO_CHANNEL_NUM+i].u32FrameBufCnt = astSampleVdec[MAX_VIDEO_CHANNEL_NUM+i].u32DisplayFrameNum + 1;
}
s32Ret = SAMPLE_COMM_VDEC_InitVBPool(2*MAX_VIDEO_CHANNEL_NUM, astSampleVdec);
if(s32Ret != HI_SUCCESS)
{
LOG_DEBUG_FMT("SAMPLE_COMM_VDEC_InitVBPool %d!", s32Ret);
goto END2;
}
s32Ret = SAMPLE_COMM_VDEC_Start(2*MAX_VIDEO_CHANNEL_NUM, astSampleVdec);
if(s32Ret != HI_SUCCESS)
{
LOG_DEBUG_FMT("start VDEC fail for %#X!", s32Ret);
goto END3;
}
else
{
LOG_DEBUG_FMT("SAMPLE_COMM_VDEC_Start OK", s32Ret);
}
/************************************************
step4: start VPSS
*************************************************/
VDEC_CHN VdecChn;
VPSS_GRP VpssGrp;
HI_BOOL abChnEnable[VPSS_MAX_CHN_NUM];
VPSS_CHN_ATTR_S astVpssChnAttr[VPSS_MAX_CHN_NUM];
VPSS_GRP_ATTR_S stVpssGrpAttr;
for (unsigned int i = 0; i < u32VdecChnNum; i++)
{
VpssGrp = i;
stVpssGrpAttr.u32MaxW = stDispSize[i].u32Width;
stVpssGrpAttr.u32MaxH = stDispSize[i].u32Height;
stVpssGrpAttr.stFrameRate.s32SrcFrameRate = -1;
stVpssGrpAttr.stFrameRate.s32DstFrameRate = -1;
stVpssGrpAttr.enDynamicRange = DYNAMIC_RANGE_SDR8;
stVpssGrpAttr.enPixelFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
stVpssGrpAttr.bNrEn = HI_FALSE;
memset(abChnEnable, 0, sizeof(abChnEnable));
abChnEnable[0] = HI_TRUE;
astVpssChnAttr[0].u32Width = stDispSize[i].u32Width;
astVpssChnAttr[0].u32Height = stDispSize[i].u32Height;
astVpssChnAttr[0].enChnMode = VPSS_CHN_MODE_USER;
astVpssChnAttr[0].enCompressMode = COMPRESS_MODE_NONE;
astVpssChnAttr[0].enDynamicRange = DYNAMIC_RANGE_SDR8;
astVpssChnAttr[0].enPixelFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
astVpssChnAttr[0].stFrameRate.s32SrcFrameRate = -1;
astVpssChnAttr[0].stFrameRate.s32DstFrameRate = -1;
astVpssChnAttr[0].u32Depth = 2;
astVpssChnAttr[0].bMirror = HI_FALSE;
astVpssChnAttr[0].bFlip = HI_FALSE;
astVpssChnAttr[0].stAspectRatio.enMode = ASPECT_RATIO_NONE;
astVpssChnAttr[0].enVideoFormat = VIDEO_FORMAT_LINEAR;
s32Ret = SAMPLE_COMM_VPSS_Start(VpssGrp, abChnEnable, &stVpssGrpAttr, astVpssChnAttr);
printf("%s:%d SAMPLE_COMM_VPSS_Start %#x\n", __FILE__, __LINE__, s32Ret);
if(s32Ret != HI_SUCCESS)
{
printf("start VPSS fail for %#x!\n", s32Ret);
goto END4;
}
}
/************************************************
step6: VDEC bind VPSS
*************************************************/
for (unsigned int i = 0; i < u32VdecChnNum; i++)
{
VdecChn = i;
VpssGrp = i;
s32Ret = SAMPLE_COMM_VDEC_Bind_VPSS(VdecChn, VpssGrp);
if(s32Ret != HI_SUCCESS)
{
printf("vdec bind vpss fail for %#x!\n", s32Ret);
goto END6;
}
}
return 0;
END6:
for (int i = 0; i < MAX_VIDEO_CHANNEL_NUM; i++)
{
VdecChn = i;
VpssGrp = 0;
s32Ret = SAMPLE_COMM_VDEC_UnBind_VPSS(VdecChn, VpssGrp);
if(s32Ret != HI_SUCCESS)
{
printf("vdec unbind vpss fail for %#x!\n", s32Ret);
}
}
END4:
SAMPLE_COMM_VPSS_Stop(VpssGrp, &abChnEnable[0]);
END3:
SAMPLE_COMM_VDEC_Stop(u32VdecChnNum);
END2:
SAMPLE_COMM_VDEC_ExitVBPool();
END1:
SAMPLE_COMM_SYS_Exit();
return s32Ret;
}
硬件拷贝图片
1.创建编码通道并开始接收输入信息
HI_MPI_VENC_CreateChn();
HI_MPI_VENC_StartRecvFrame();
//创建编码通道
int creatVENC_ChnAttr(int iPicWidth,int iPicHeight,int chn,int imgWidth,int imgHeifht)
{
LOG_DEBUG_FMT("[creatVENC_ChnAttr] iPicWidth %d , iPicHeight %d, chn %d imgWidth %di mgHeifht %d\n",iPicWidth,iPicHeight,chn,imgWidth,imgHeifht);
//最大的空间来 w*(h+OSDMAXHEIGHT)
int width = 0, height = 0, codec;
for(int i=0; i<4; i++)
{
AlgCapMan::GetInstance()->GetVedioSourceSolution(i, width, height, codec);
if(width>0 && height>0)
{
LOG_DEBUG_FMT("[creatVENC_ChnAttr] get width %d height %d ",width,height);
break;
}
}
if(width<=0 || height<=0)
{
LOG_DEBUG_FMT("[creatVENC_ChnAttr] get width height failed failed failed width %d height %d",width,height);
return -1;
}
//一般不会使用配置的分辨率,都是按照实际的视频源来的
if(imgWidth>100 && imgHeifht>100)
{
iPicWidth = imgWidth;
iPicHeight = imgHeifht;
}
/************************************************
创建VENC通道,各个通道开始接受输入
*************************************************/
VENC_CHN_ATTR_S stVencChnAttr;
HI_S32 s32Ret = HI_SUCCESS;
/******************************************
step 1: Create Venc Channel
******************************************/
stVencChnAttr.stVencAttr.enType = PT_JPEG;
stVencChnAttr.stVencAttr.u32MaxPicWidth = width;
stVencChnAttr.stVencAttr.u32MaxPicHeight = height+OSDMAXHEIGHT;
stVencChnAttr.stVencAttr.u32PicWidth = iPicWidth;
stVencChnAttr.stVencAttr.u32PicHeight = iPicHeight;
stVencChnAttr.stVencAttr.u32BufSize = width*height* 2;/*stream buffer size*/
//stVencChnAttr.stVencAttr.u32Profile = 0;
stVencChnAttr.stVencAttr.bByFrame = HI_TRUE;/*get stream mode is slice mode or frame mode?*/
stVencChnAttr.stVencAttr.stAttrJpege.bSupportDCF = HI_FALSE;
stVencChnAttr.stVencAttr.stAttrJpege.stMPFCfg.u8LargeThumbNailNum = 0;
//stVencChnAttr.stGopAttr.enGopMode = VENC_GOPMODE_NORMALP;
//stVencChnAttr.stGopAttr.stNormalP.s32IPQpDelta = 0;
s32Ret = HI_MPI_VENC_CreateChn(0, &stVencChnAttr);
if (HI_SUCCESS == s32Ret)
{
SAMPLE_PRT("[creatVENC_ChnAttr]HI_MPI_VENC_CreateChn ok with %#x! ===\n", s32Ret);
//return s32Ret;
}
else
{
SAMPLE_PRT("[creatVENC_ChnAttr]HI_MPI_VENC_CreateChn creat failed with s32Ret %#x! ===\n", s32Ret);
}
//开启接收
/******************************************
step 2: Start Recv Venc Pictures
******************************************/
VENC_RECV_PIC_PARAM_S stRecvParam;
stRecvParam.s32RecvPicNum =-1;
s32Ret = HI_MPI_VENC_StartRecvFrame(0,&stRecvParam);
if (HI_SUCCESS == s32Ret)
{
LOG_ERROR_FMT("[creatVENC_ChnAttr]HI_MPI_VENC_StartRecvPic ok with s32Re %#x! \n", s32Ret);
//return HI_FAILURE;
}
else
{
LOG_ERROR_FMT("[creatVENC_ChnAttr]HI_MPI_VENC_StartRecvPic failed s32Ret %#x \n", s32Ret);
}
return 0;
}
2.创建 一帧420sp数据
HI_MPI_VB_GetBlock 获取缓冲块
HI_MPI_VB_Handle2PhysAddr 获取缓冲块物理地址
HI_MPI_VB_Handle2PoolId获取该帧在缓存池id
HI_MPI_SYS_Mmap memory存储映射接口
初始化VIDEO_FRAME_INFO_S对象既该帧所有信息
VB_BLK VbBlk;
HI_U64 u64PhyAddr;
HI_VOID * u64VirAddr;
HI_U32 u32PoolId;
HI_U32 frameSize;
int creat_frameMmz(){
HI_U32 u32Size;
//创建mmz,按照最大的空间来 w*(h+OSDMAXHEIGHT)
int width = 0, height = 0, codec;
for(int i=0; i<4; i++)
{
AlgCapMan::GetInstance()->GetVedioSourceSolution(i, width, height, codec);
if(width>0 && height>0)
{
LOG_DEBUG_FMT("[creat_frameMmz] get width %d height %d ",width,height);
break;
}
}
if(width<=0 || height<=0)
{
LOG_DEBUG_FMT("[creat_frameMmz] get width height failed failed failed width %d height %d",width,height);
return -1;
}
height = height+OSDMAXHEIGHT;
u32Size = width*height*3/2;
LOG_DEBUG_FMT("[creat_frameMmz max] width %d height+OSDMAXHEIGHT %d frame mmz size u32Size %d ",width,height,u32Size);
VbBlk = HI_MPI_VB_GetBlock(VENCPOOLID, u32Size, HI_NULL);
if (VB_INVALID_HANDLE == VbBlk) {
LOG_DEBUG_FMT("[creat_frameMmz] HI_MPI_VB_GetBlock err! size:%d\n",u32Size);
return -1;
}
else
{
LOG_DEBUG_FMT("[creat_frameMmz] HI_MPI_VB_GetBlock ok ok o k! size:%d VbBlk %d\n",u32Size,VbBlk);
}
u64PhyAddr = HI_MPI_VB_Handle2PhysAddr(VbBlk);
if (0 == u64PhyAddr) {
return -1;
}
u32PoolId = HI_MPI_VB_Handle2PoolId(VbBlk);
if (VB_INVALID_POOLID == u32PoolId) {
return -1;
}
else
{
LOG_DEBUG_FMT("[creat_frameMmz] u32PoolId--------%d\n",u32PoolId);
}
u64VirAddr = HI_MPI_SYS_Mmap(u64PhyAddr, u32Size);//HI_VOID* HI_MPI_SYS_Mmap(HI_U64 u64PhyAddr, HI_U32 u32Size);
LOG_DEBUG_FMT("[creat_frameMmz]u64PhyAddr %p u64VirAddr %p u32PoolId %d\n",u64PhyAddr,u64VirAddr,u32PoolId);
return 0;
}
int create_frame(int width, int height, int stride, VIDEO_FRAME_INFO_S *pstVFrameInfo,int frame)
{
HI_U32 u32LStride,u32CStride, u32LumaSize,u32ChrmSize;
u32LStride = stride;
u32CStride = stride;
u32LumaSize = (u32LStride * height);
u32ChrmSize = (u32CStride * height)/4;/* YUV 420 */
frameSize = width*height*3/2;
static int flagOnce = 0;
if(flagOnce == 0)
{
int ret = creat_frameMmz();
if(ret != 0)
{
LOG_DEBUG_FMT("[create_frame] creat_frameMmz failed ");
return -1;
}
//VGS_BeginJob();
flagOnce++;
}
LOG_DEBUG_FMT("[create_frame] width %d height %d stride %d frame %d frameSize %d (HI_U64)u64VirAddr %p",width,height,stride,frame,frameSize,(HI_U64)u64VirAddr);
pstVFrameInfo->u32PoolId = u32PoolId;
pstVFrameInfo->stVFrame.u64PhyAddr[0] = u64PhyAddr;
pstVFrameInfo->stVFrame.u64PhyAddr[1] = pstVFrameInfo->stVFrame.u64PhyAddr[0] + u32LumaSize;
pstVFrameInfo->stVFrame.u64PhyAddr[2] = pstVFrameInfo->stVFrame.u64PhyAddr[1] + u32ChrmSize;
pstVFrameInfo->stVFrame.u64VirAddr[0] = (HI_U64)u64VirAddr;
pstVFrameInfo->stVFrame.u64VirAddr[1] = pstVFrameInfo->stVFrame.u64VirAddr[0] + u32LumaSize;
pstVFrameInfo->stVFrame.u64VirAddr[2] = pstVFrameInfo->stVFrame.u64VirAddr[1] + u32ChrmSize;
pstVFrameInfo->stVFrame.u32Width = width;
pstVFrameInfo->stVFrame.u32Height = height;
pstVFrameInfo->stVFrame.u32Stride[0] = width;
pstVFrameInfo->stVFrame.u32Stride[1] = width;
pstVFrameInfo->stVFrame.u32Stride[2] = width;
pstVFrameInfo->stVFrame.enPixelFormat = (PIXEL_FORMAT_E)26;
pstVFrameInfo->stVFrame.enField = (VIDEO_FIELD_E)4; /* Intelaced D1,otherwise VIDEO_FIELD_FRAME */
pstVFrameInfo->stVFrame.enVideoFormat = (VIDEO_FORMAT_E)0;
pstVFrameInfo->stVFrame.enCompressMode = (COMPRESS_MODE_E)0;
pstVFrameInfo->stVFrame.enDynamicRange=(DYNAMIC_RANGE_E)0;
pstVFrameInfo->stVFrame.enColorGamut = (COLOR_GAMUT_E)0;
pstVFrameInfo->stVFrame.u32MaxLuminance = 1000;
pstVFrameInfo->stVFrame.u32MinLuminance = 200;
//pstVFrameInfo->stVFrame.u32TimeRef = 2;
// pstVFrameInfo->stVFrame.u64PTS = -1;
pstVFrameInfo->stVFrame.u32TimeRef = frame*2;
pstVFrameInfo->stVFrame.u64PTS = frame*2;
pstVFrameInfo->stVFrame.u64PrivateData=0;
pstVFrameInfo->stVFrame.u32FrameFlag=0;
pstVFrameInfo->stVFrame.s16OffsetTop=0;
pstVFrameInfo->stVFrame.s16OffsetBottom=0;
pstVFrameInfo->stVFrame.s16OffsetLeft=0;
pstVFrameInfo->stVFrame.s16OffsetRight=0;
return 0;
}
3.VGS句柄设置
HI_MPI_VGS_BeginJob启动该帧对象
VGS_TASK_ATTR_S图像输入输出属性设置
//VGS
VGS_TASK_ATTR_S stTask;
VGS_HANDLE hHandle;
s32Ret = HI_MPI_VGS_BeginJob(&hHandle);
if(HI_SUCCESS != s32Ret)
{
LOG_DEBUG_FMT("HI_MPI_VGS_BeginJob failed, s32Ret:0x%x\n",s32Ret);
}
memcpy( &stTask.stImgIn,&pstFrame,sizeof(VIDEO_FRAME_INFO_S));
memcpy( &stTask.stImgOut,&pstFrame,sizeof(VIDEO_FRAME_INFO_S));
//osd区域初始化, 给每个个像素点赋值,初始化为黑色
memset((char *)osdMemInfo.u64VirAddr,0,osdMemInfo.u32Size);
unsigned int pos = 0;
for(int i=0; i<iPicWidth*osdheight; i++)
{
pos++;
*((char *)osdMemInfo.u64VirAddr + pos) = 0xf0;
pos++;
*((char *)osdMemInfo.u64VirAddr + pos) = 0x00;
}
OSDPoint point;
point.x=0;
point.y=0;
for (int j = 0; j < tImgOsd.iLineCnt; j++)
{
OsdCreatData((char*)osdMemInfo.u64VirAddr,tImgOsd.szContent[j],&point,tOsdCfg.fontsize,iPicWidth);
point.x=0;
point.y += (tOsdCfg.fontsize + OSD_LINE_GAP);
}
4.添加osd信息单一添加HI_MPI_VGS_AddOsdTask();可选;或者批量添加HI_MPI_VGS_AddOsdTaskArray();可选;
VGS_ADD_OSD_S pstVgsAddOsd;
int setOsdCfgParm(VGS_ADD_OSD_S * pstVgsAddOsd,int iPicWidth,int osdheight,HI_U64 osdPhyAddr,int osdStartX,int osdStartY)
{
pstVgsAddOsd->stRect.s32X = osdStartX;//stRect OSD 的起始坐标及宽高
pstVgsAddOsd->stRect.s32Y = osdStartY;
pstVgsAddOsd->stRect.u32Width = iPicWidth;
pstVgsAddOsd->stRect.u32Height = osdheight;
pstVgsAddOsd->u32BgColor = 0xf00f;
pstVgsAddOsd->enPixelFmt = PIXEL_FORMAT_ARGB_4444;
pstVgsAddOsd->u64PhyAddr = osdPhyAddr;
pstVgsAddOsd->u32Stride = iPicWidth*2;//w*一个像素字节数
pstVgsAddOsd->u32BgAlpha = 0;
pstVgsAddOsd->u32FgAlpha = 0;
pstVgsAddOsd->bOsdRevert = HI_FALSE;
pstVgsAddOsd->stOsdRevert.stSrcRect.s32X = 0;
pstVgsAddOsd->stOsdRevert.stSrcRect.s32Y = 0;
pstVgsAddOsd->stOsdRevert.stSrcRect.u32Width = iPicWidth;
pstVgsAddOsd->stOsdRevert.stSrcRect.u32Height = osdheight;
pstVgsAddOsd->stOsdRevert.enColorRevertMode = VGS_COLOR_REVERT_NONE;
pstVgsAddOsd->u16ColorLUT[0] = 32;
pstVgsAddOsd->u16ColorLUT[1] = 32;
return 0;
}
HI_MPI_VGS_AddOsdTask(hHandle, &stTask, &pstVgsAddOsd);
5.提交和取消VGS句柄
HI_MPI_VGS_EndJob提交
HI_MPI_VGS_CancelJob异常取消
s32Ret = HI_MPI_VGS_EndJob(hHandle);
if(HI_SUCCESS != s32Ret)
{
LOG_DEBUG_FMT("[DoImageEvidence]HI_MPI_VGS_EndJob failed, s32Ret:0x%x\n",s32Ret);
s32Ret = HI_MPI_VGS_CancelJob(hHandle);
if(HI_SUCCESS != s32Ret)
{
LOG_DEBUG_FMT("[DoImageEvidence] HI_MPI_VGS_CancelJob failed, s32Ret:0x%x\n",s32Ret);
}
}
6.发送编码通道
HI_MPI_VENC_SendFrame
//发送编码通道,编码为jpg
s32Ret = HI_MPI_VENC_SendFrame(0, &pstFrame, 100);
if(HI_SUCCESS != s32Ret)
{
LOG_DEBUG_FMT("[DoImageEvidence] HI_MPI_VENC_SendFrame failed, s32Ret=0x%X pic name %s", s32Ret,pData->pic_path[i]);
// 可以释放内存了
if(pData->m_pIplImageDateInfo[i].pImageData != NULL)
{
freeCnt++;
LOG_DEBUG_FMT("[CollectImageEvidence]free malloc freeCnt %d m_ImageData[%d].pImageData %p",freeCnt,i,pData->m_pIplImageDateInfo[i].pImageData);
free(pData->m_pIplImageDateInfo[i].pImageData);
pData->m_pIplImageDateInfo[i].pImageData = NULL;
}
continue;
}
7.接收码流
HI_MPI_VENC_QueryStatus查询编码状态
HI_MPI_VENC_GetStream获取码流
HI_MPI_VENC_ReleaseStream释放码流缓存
int VENC_GetPic(char * picNmae,HI_U32 frameSize)
{
HI_S32 s32Ret = HI_SUCCESS;
VENC_STREAM_S getpstFrame;
VENC_CHN_STATUS_S stStat;
unsigned int cntflag = 0;
usleep(500);
while(1)
{
cntflag++;
if(cntflag == 1000)
{
LOG_DEBUG_FMT("[VENC_GetPic]HI_MPI_VENC_Query time out ");
return -1;
}
usleep(10);
s32Ret = HI_MPI_VENC_QueryStatus(0, &stStat);
if (s32Ret != HI_SUCCESS)
{
LOG_DEBUG_FMT("[VENC_GetPic] HI_MPI_VENC_QueryStatus failed with %#x!\n", s32Ret);
continue;
}
if (0 == stStat.u32CurPacks)
{
LOG_DEBUG_FMT("[VENC_GetPic] NOTE: Current frame is NULL!\n");
continue;
}
getpstFrame.pstPack = (VENC_PACK_S*)malloc(sizeof(VENC_PACK_S) * stStat.u32CurPacks);
if (NULL == getpstFrame.pstPack)
{
LOG_DEBUG_FMT("[VENC_GetPic] malloc memory failed!\n");
return -1;
}
getpstFrame.u32PackCount = stStat.u32CurPacks;
s32Ret = HI_MPI_VENC_GetStream(0,&getpstFrame,-1);
if(HI_SUCCESS == s32Ret)
{
//保存图片
FILE * fpsave = fopen(picNmae, "wb+");
if(NULL == fpsave)
{
LOG_DEBUG_FMT("[VENC_GetPic] fpsavev file open error pic name %s\n",picNmae);
return -1;
//return NULL;
}
for (HI_U32 i = 0; i < getpstFrame.u32PackCount; i++)
{
fwrite(getpstFrame.pstPack[i].pu8Addr + getpstFrame.pstPack[i].u32Offset,
getpstFrame.pstPack[i].u32Len - getpstFrame.pstPack[i].u32Offset, 1, fpsave);
fflush(fpsave);
}
fclose(fpsave);
fpsave = NULL;
//释放
s32Ret = HI_MPI_VENC_ReleaseStream(0, &getpstFrame);
if (HI_SUCCESS != s32Ret)
{
LOG_DEBUG_FMT("[VENC_GetPic]HI_MPI_VENC_ReleaseStream error\n");
free(getpstFrame.pstPack);
getpstFrame.pstPack = NULL;
return -1;
}
else
{
free(getpstFrame.pstPack);
getpstFrame.pstPack = NULL;
}
return 0;
}
else
{
LOG_DEBUG_FMT("[VENC_GetPic] HI_MPI_VENC_GetStream failed, s32Ret=0x%X", s32Ret);
return -1;
}
}
return 0;
}
mpp相关接口
VB_BLK VbBlk;
HI_U64 u64PhyAddr;
HI_VOID * u64VirAddr;
HI_U32 u32PoolId;
HI_U32 frameSize;
//IVE
CSC_S stCSCNew[4];
IVE_HANDLE hIveHandleNew[4];
#pragma pack(1)
typedef struct{
short type;
int size;
short reserved1;
short reserved2;
int offset;
} BMPHeader;
typedef struct{
int size;
int width;
int height;
short planes;
short bitsPerPixel;
unsigned compression;
unsigned imageSize;
int xPelsPerMeter;
int yPelsPerMeter;
int clrUsed;
int clrImportant;
} BMPInfoHeader;
#pragma pack()
void YUV420PtoNV12(unsigned char * Src, unsigned char * Dst,int Width,int Height){
unsigned char* SrcU = Src + Width * Height;
unsigned char* SrcV = SrcU + Width * Height / 4 ;
memcpy(Dst, Src, Width * Height);
unsigned char* DstU = Dst + Width * Height;
for(int i = 0 ; i < Width * Height / 4 ; i++ ){
( *DstU++) = ( *SrcU++);
( *DstU++) = ( *SrcV++);
}
}
void YUV420PtoNV21(unsigned char *Src, unsigned char *Dst,int Width,int Height)
{
unsigned char* SrcU = Src + Width * Height;
unsigned char* SrcV = SrcU + Width * Height / 4 ;
memcpy(Dst, Src, Width * Height);
unsigned char* DstV = Dst + Width * Height;
for(int i = 0 ; i < Width * Height / 4 ; i++ )
{
(*DstV++) = (*SrcV++);
(*DstV++) = (*SrcU++);
}
}
int creat_frameMmz(){
HI_U32 u32Size;
//创建mmz,按照最大的空间来 w*(h+OSDMAXHEIGHT)
int width = 0, height = 0, codec;
for(int i=0; i<4; i++)
{
AlgCapMan::GetInstance()->GetVedioSourceSolution(i, width, height, codec);
if(width>0 && height>0)
{
LOG_DEBUG_FMT("[creat_frameMmz] get width %d height %d ",width,height);
break;
}
}
if(width<=0 || height<=0)
{
LOG_DEBUG_FMT("[creat_frameMmz] get width height failed failed failed width %d height %d",width,height);
return -1;
}
height = height+OSDMAXHEIGHT;
u32Size = width*height*3/2;
LOG_DEBUG_FMT("[creat_frameMmz max] width %d height+OSDMAXHEIGHT %d frame mmz size u32Size %d ",width,height,u32Size);
VbBlk = HI_MPI_VB_GetBlock(VENCPOOLID, u32Size, HI_NULL);
if (VB_INVALID_HANDLE == VbBlk) {
LOG_DEBUG_FMT("[creat_frameMmz] HI_MPI_VB_GetBlock err! size:%d\n",u32Size);
return -1;
}
else
{
LOG_DEBUG_FMT("[creat_frameMmz] HI_MPI_VB_GetBlock ok ok o k! size:%d VbBlk %d\n",u32Size,VbBlk);
}
u64PhyAddr = HI_MPI_VB_Handle2PhysAddr(VbBlk);
if (0 == u64PhyAddr) {
return -1;
}
u32PoolId = HI_MPI_VB_Handle2PoolId(VbBlk);
if (VB_INVALID_POOLID == u32PoolId) {
return -1;
}
else
{
LOG_DEBUG_FMT("[creat_frameMmz] u32PoolId--------%d\n",u32PoolId);
}
u64VirAddr = HI_MPI_SYS_Mmap(u64PhyAddr, u32Size);//HI_VOID* HI_MPI_SYS_Mmap(HI_U64 u64PhyAddr, HI_U32 u32Size);
LOG_DEBUG_FMT("[creat_frameMmz]u64PhyAddr %p u64VirAddr %p u32PoolId %d\n",u64PhyAddr,u64VirAddr,u32PoolId);
return 0;
}
int free_frameMmz(){
LOG_DEBUG_FMT("[free_frameMmz] u64VirAddr %p frameSize %d ",u64VirAddr,frameSize);
/* 释放掉获取的vb物理地址和虚拟地址 */
HI_MPI_SYS_Munmap(u64VirAddr, frameSize);
HI_MPI_VB_ReleaseBlock(VbBlk);
return 0;
}
int VGS_BeginJob()//启动一个 job
{
//创建VGS_BeginJob
HI_U32 s32Ret;
s32Ret = HI_MPI_VGS_BeginJob(&(AlgEviMan::GetInstance()->hHandle));
if(HI_SUCCESS == s32Ret)
{
LOG_DEBUG_FMT("[VGS_BeginJob]HI_MPI_VGS_BeginJob ok, s32Ret:0x%x\n",s32Ret);
}
else
{
LOG_DEBUG_FMT("[VGS_BeginJob]HI_MPI_VGS_BeginJob failed, s32Ret:0x%x\n",s32Ret);
}
return 0;
}
int VGS_CancelJob()//取消一个 job
{
HI_U32 s32Ret;
s32Ret = HI_MPI_VGS_CancelJob(AlgEviMan::GetInstance()->hHandle);
if(HI_SUCCESS != s32Ret)
{
LOG_DEBUG_FMT("[VGS_BeginJob] HI_MPI_VGS_CancelJob failed, s32Ret:0x%x\n",s32Ret);
}
return 0;
}
int create_frame(int width, int height, int stride, VIDEO_FRAME_INFO_S *pstVFrameInfo,int frame)
{
HI_U32 u32LStride,u32CStride, u32LumaSize,u32ChrmSize;
u32LStride = stride;
u32CStride = stride;
u32LumaSize = (u32LStride * height);
u32ChrmSize = (u32CStride * height)/4;/* YUV 420 */
frameSize = width*height*3/2;
static int flagOnce = 0;
if(flagOnce == 0)
{
int ret = creat_frameMmz();
if(ret != 0)
{
LOG_DEBUG_FMT("[create_frame] creat_frameMmz failed ");
return -1;
}
//VGS_BeginJob();
flagOnce++;
}
LOG_DEBUG_FMT("[create_frame] width %d height %d stride %d frame %d frameSize %d (HI_U64)u64VirAddr %p",width,height,stride,frame,frameSize,(HI_U64)u64VirAddr);
pstVFrameInfo->u32PoolId = u32PoolId;
pstVFrameInfo->stVFrame.u64PhyAddr[0] = u64PhyAddr;
pstVFrameInfo->stVFrame.u64PhyAddr[1] = pstVFrameInfo->stVFrame.u64PhyAddr[0] + u32LumaSize;
pstVFrameInfo->stVFrame.u64PhyAddr[2] = pstVFrameInfo->stVFrame.u64PhyAddr[1] + u32ChrmSize;
pstVFrameInfo->stVFrame.u64VirAddr[0] = (HI_U64)u64VirAddr;
pstVFrameInfo->stVFrame.u64VirAddr[1] = pstVFrameInfo->stVFrame.u64VirAddr[0] + u32LumaSize;
pstVFrameInfo->stVFrame.u64VirAddr[2] = pstVFrameInfo->stVFrame.u64VirAddr[1] + u32ChrmSize;
pstVFrameInfo->stVFrame.u32Width = width;
pstVFrameInfo->stVFrame.u32Height = height;
pstVFrameInfo->stVFrame.u32Stride[0] = width;
pstVFrameInfo->stVFrame.u32Stride[1] = width;
pstVFrameInfo->stVFrame.u32Stride[2] = width;
pstVFrameInfo->stVFrame.enPixelFormat = (PIXEL_FORMAT_E)26;
pstVFrameInfo->stVFrame.enField = (VIDEO_FIELD_E)4; /* Intelaced D1,otherwise VIDEO_FIELD_FRAME */
pstVFrameInfo->stVFrame.enVideoFormat = (VIDEO_FORMAT_E)0;
pstVFrameInfo->stVFrame.enCompressMode = (COMPRESS_MODE_E)0;
pstVFrameInfo->stVFrame.enDynamicRange=(DYNAMIC_RANGE_E)0;
pstVFrameInfo->stVFrame.enColorGamut = (COLOR_GAMUT_E)0;
pstVFrameInfo->stVFrame.u32MaxLuminance = 1000;
pstVFrameInfo->stVFrame.u32MinLuminance = 200;
//pstVFrameInfo->stVFrame.u32TimeRef = 2;
// pstVFrameInfo->stVFrame.u64PTS = -1;
pstVFrameInfo->stVFrame.u32TimeRef = frame*2;
pstVFrameInfo->stVFrame.u64PTS = frame*2;
pstVFrameInfo->stVFrame.u64PrivateData=0;
pstVFrameInfo->stVFrame.u32FrameFlag=0;
pstVFrameInfo->stVFrame.s16OffsetTop=0;
pstVFrameInfo->stVFrame.s16OffsetBottom=0;
pstVFrameInfo->stVFrame.s16OffsetLeft=0;
pstVFrameInfo->stVFrame.s16OffsetRight=0;
return 0;
}
int VENC_GetPic(char * picNmae,HI_U32 frameSize)
{
HI_S32 s32Ret = HI_SUCCESS;
VENC_STREAM_S getpstFrame;
VENC_CHN_STATUS_S stStat;
unsigned int cntflag = 0;
usleep(500);
while(1)
{
cntflag++;
if(cntflag == 1000)
{
LOG_DEBUG_FMT("[VENC_GetPic]HI_MPI_VENC_Query time out ");
return -1;
}
usleep(10);
s32Ret = HI_MPI_VENC_QueryStatus(0, &stStat);
if (s32Ret != HI_SUCCESS)
{
LOG_DEBUG_FMT("[VENC_GetPic] HI_MPI_VENC_QueryStatus failed with %#x!\n", s32Ret);
continue;
}
if (0 == stStat.u32CurPacks)
{
LOG_DEBUG_FMT("[VENC_GetPic] NOTE: Current frame is NULL!\n");
continue;
}
getpstFrame.pstPack = (VENC_PACK_S*)malloc(sizeof(VENC_PACK_S) * stStat.u32CurPacks);
if (NULL == getpstFrame.pstPack)
{
LOG_DEBUG_FMT("[VENC_GetPic] malloc memory failed!\n");
return -1;
}
getpstFrame.u32PackCount = stStat.u32CurPacks;
s32Ret = HI_MPI_VENC_GetStream(0,&getpstFrame,-1);
if(HI_SUCCESS == s32Ret)
{
//保存图片
FILE * fpsave = fopen(picNmae, "wb+");
if(NULL == fpsave)
{
LOG_DEBUG_FMT("[VENC_GetPic] fpsavev file open error pic name %s\n",picNmae);
return -1;
//return NULL;
}
for (HI_U32 i = 0; i < getpstFrame.u32PackCount; i++)
{
fwrite(getpstFrame.pstPack[i].pu8Addr + getpstFrame.pstPack[i].u32Offset,
getpstFrame.pstPack[i].u32Len - getpstFrame.pstPack[i].u32Offset, 1, fpsave);
fflush(fpsave);
}
fclose(fpsave);
fpsave = NULL;
//释放
s32Ret = HI_MPI_VENC_ReleaseStream(0, &getpstFrame);
if (HI_SUCCESS != s32Ret)
{
LOG_DEBUG_FMT("[VENC_GetPic]HI_MPI_VENC_ReleaseStream error\n");
free(getpstFrame.pstPack);
getpstFrame.pstPack = NULL;
return -1;
}
else
{
free(getpstFrame.pstPack);
getpstFrame.pstPack = NULL;
}
return 0;
}
else
{
LOG_DEBUG_FMT("[VENC_GetPic] HI_MPI_VENC_GetStream failed, s32Ret=0x%X", s32Ret);
return -1;
}
}
return 0;
}
int setVencPicQuality(u32 quality)
{
HI_S32 s32Ret = HI_SUCCESS;
//获取jpg编码高级参数,设置编码后的图片质量
VENC_JPEG_PARAM_S jpgPar = {0};
s32Ret = HI_MPI_VENC_GetJpegParam(0,&jpgPar);
if (HI_SUCCESS != s32Ret)
{
LOG_DEBUG_FMT("HI_MPI_VENC_GetJpegParam failed with s32Ret%d! \n", s32Ret);
}
else
{
LOG_DEBUG_FMT("HI_MPI_VENC_GetJpegParam ok with s32Ret%x! u32Qfactor %d u8YQt %d u8CbQt %d u8CrQt %d u32MCUPerECS %d \n",
s32Ret,jpgPar.u32Qfactor,jpgPar.u8YQt,jpgPar.u8CbQt,jpgPar.u8CrQt,jpgPar.u32MCUPerECS);
}
jpgPar.u32Qfactor = quality;//设置图片质量u32Qfactor 值小图片质量差,1--99,
s32Ret = HI_MPI_VENC_SetJpegParam(0,&jpgPar);
if (HI_SUCCESS == s32Ret)
{
LOG_DEBUG_FMT("HI_MPI_VENC_SetJpegParam ok with s32Ret%d! \n", s32Ret);
}
else
{
LOG_DEBUG_FMT("HI_MPI_VENC_SetJpegParam failed s32Ret %d! \n", s32Ret);
}
return 0;
}
int setOsdCfgParm(VGS_ADD_OSD_S * pstVgsAddOsd,int iPicWidth,int osdheight,HI_U64 osdPhyAddr,int osdStartX,int osdStartY)
{
pstVgsAddOsd->stRect.s32X = osdStartX;//stRect OSD 的起始坐标及宽高
pstVgsAddOsd->stRect.s32Y = osdStartY;
pstVgsAddOsd->stRect.u32Width = iPicWidth;
pstVgsAddOsd->stRect.u32Height = osdheight;
pstVgsAddOsd->u32BgColor = 0xf00f;
pstVgsAddOsd->enPixelFmt = PIXEL_FORMAT_ARGB_4444;
pstVgsAddOsd->u64PhyAddr = osdPhyAddr;
pstVgsAddOsd->u32Stride = iPicWidth*2;//w*一个像素字节数
pstVgsAddOsd->u32BgAlpha = 0;
pstVgsAddOsd->u32FgAlpha = 0;
pstVgsAddOsd->bOsdRevert = HI_FALSE;
pstVgsAddOsd->stOsdRevert.stSrcRect.s32X = 0;
pstVgsAddOsd->stOsdRevert.stSrcRect.s32Y = 0;
pstVgsAddOsd->stOsdRevert.stSrcRect.u32Width = iPicWidth;
pstVgsAddOsd->stOsdRevert.stSrcRect.u32Height = osdheight;
pstVgsAddOsd->stOsdRevert.enColorRevertMode = VGS_COLOR_REVERT_NONE;
pstVgsAddOsd->u16ColorLUT[0] = 32;
pstVgsAddOsd->u16ColorLUT[1] = 32;
return 0;
}
//该参数不能设置,0xa0088009
int setVENC_ChnAttr(int u32Width,int iPicHeight,int chn)
{
LOG_DEBUG_FMT("[setVENC_ChnAttr] u32Width %d , iPicHeight %d, chn %d\n",u32Width,iPicHeight,chn);
//最大的空间来 w*(h+OSDMAXHEIGHT)
int width = 0, height = 0, codec;
for(int i=0; i<4; i++)
{
AlgCapMan::GetInstance()->GetVedioSourceSolution(i, width, height, codec);
if(width>0 && height>0)
{
LOG_DEBUG_FMT("[setVENC_ChnAttr] get width %d height %d ",width,height);
break;
}
}
if(width<=0 || height<=0)
{
LOG_DEBUG_FMT("[setVENC_ChnAttr] get width height failed failed failed width %d height %d",width,height);
return -1;
}
//停止接收
HI_S32 s32Ret = HI_SUCCESS;
s32Ret = HI_MPI_VENC_StopRecvFrame(chn);
if(s32Ret != HI_SUCCESS)
{
LOG_DEBUG_FMT("[setVENC_ChnAttr] StopRecvFrame failed s32Ret \n",s32Ret);
}
VENC_CHN_ATTR_S stVencChnAttr;
stVencChnAttr.stVencAttr.enType = PT_JPEG;
stVencChnAttr.stVencAttr.u32MaxPicWidth = width;
stVencChnAttr.stVencAttr.u32MaxPicHeight = height+OSDMAXHEIGHT;
stVencChnAttr.stVencAttr.u32PicWidth = u32Width;/*the picture width*/
stVencChnAttr.stVencAttr.u32PicHeight = iPicHeight;/*the picture height*/
stVencChnAttr.stVencAttr.u32BufSize = u32Width*iPicHeight* 2;/*stream buffer size*/
//stVencChnAttr.stVencAttr.u32Profile = 0;
stVencChnAttr.stVencAttr.bByFrame = HI_TRUE;/*get stream mode is slice mode or frame mode?*/
stVencChnAttr.stVencAttr.stAttrJpege.bSupportDCF = HI_FALSE;
stVencChnAttr.stVencAttr.stAttrJpege.stMPFCfg.u8LargeThumbNailNum = 0;
//stVencChnAttr.stGopAttr.enGopMode = VENC_GOPMODE_NORMALP;
//stVencChnAttr.stGopAttr.stNormalP.s32IPQpDelta = 0;
s32Ret = HI_MPI_VENC_SetChnAttr(chn, &stVencChnAttr);
if (HI_SUCCESS == s32Ret)
{
LOG_DEBUG_FMT("HI_MPI_VENC_SetChnAttr ok with %#x! ===\n", s32Ret);
}
else
{
LOG_DEBUG_FMT("HI_MPI_VENC_SetChnAttr creat failed with s32Ret %#x! ===\n", s32Ret);
}
//开启接收
VENC_RECV_PIC_PARAM_S stRecvParam;
stRecvParam.s32RecvPicNum =-1;
s32Ret = HI_MPI_VENC_StartRecvFrame(0,&stRecvParam);
if (HI_SUCCESS == s32Ret)
{
LOG_DEBUG_FMT("HI_MPI_VENC_StartRecvFrame ok with s32Ret%d! \n", s32Ret);
}
else
{
LOG_DEBUG_FMT("HI_MPI_VENC_StartRecvFrame failed s32Ret %d! \n", s32Ret);
}
return 0;
}
//创建编码通道
int creatVENC_ChnAttr(int iPicWidth,int iPicHeight,int chn,int imgWidth,int imgHeifht)
{
LOG_DEBUG_FMT("[creatVENC_ChnAttr] iPicWidth %d , iPicHeight %d, chn %d imgWidth %di mgHeifht %d\n",iPicWidth,iPicHeight,chn,imgWidth,imgHeifht);
//最大的空间来 w*(h+OSDMAXHEIGHT)
int width = 0, height = 0, codec;
for(int i=0; i<4; i++)
{
AlgCapMan::GetInstance()->GetVedioSourceSolution(i, width, height, codec);
if(width>0 && height>0)
{
LOG_DEBUG_FMT("[creatVENC_ChnAttr] get width %d height %d ",width,height);
break;
}
}
if(width<=0 || height<=0)
{
LOG_DEBUG_FMT("[creatVENC_ChnAttr] get width height failed failed failed width %d height %d",width,height);
return -1;
}
//一般不会使用配置的分辨率,都是按照实际的视频源来的
if(imgWidth>100 && imgHeifht>100)
{
iPicWidth = imgWidth;
iPicHeight = imgHeifht;
}
/************************************************
创建VENC通道,各个通道开始接受输入
*************************************************/
VENC_CHN_ATTR_S stVencChnAttr;
HI_S32 s32Ret = HI_SUCCESS;
/******************************************
step 1: Create Venc Channel
******************************************/
stVencChnAttr.stVencAttr.enType = PT_JPEG;
stVencChnAttr.stVencAttr.u32MaxPicWidth = width;
stVencChnAttr.stVencAttr.u32MaxPicHeight = height+OSDMAXHEIGHT;
stVencChnAttr.stVencAttr.u32PicWidth = iPicWidth;
stVencChnAttr.stVencAttr.u32PicHeight = iPicHeight;
stVencChnAttr.stVencAttr.u32BufSize = width*height* 2;/*stream buffer size*/
//stVencChnAttr.stVencAttr.u32Profile = 0;
stVencChnAttr.stVencAttr.bByFrame = HI_TRUE;/*get stream mode is slice mode or frame mode?*/
stVencChnAttr.stVencAttr.stAttrJpege.bSupportDCF = HI_FALSE;
stVencChnAttr.stVencAttr.stAttrJpege.stMPFCfg.u8LargeThumbNailNum = 0;
//stVencChnAttr.stGopAttr.enGopMode = VENC_GOPMODE_NORMALP;
//stVencChnAttr.stGopAttr.stNormalP.s32IPQpDelta = 0;
s32Ret = HI_MPI_VENC_CreateChn(0, &stVencChnAttr);
if (HI_SUCCESS == s32Ret)
{
SAMPLE_PRT("[creatVENC_ChnAttr]HI_MPI_VENC_CreateChn ok with %#x! ===\n", s32Ret);
//return s32Ret;
}
else
{
SAMPLE_PRT("[creatVENC_ChnAttr]HI_MPI_VENC_CreateChn creat failed with s32Ret %#x! ===\n", s32Ret);
}
//开启接收
/******************************************
step 2: Start Recv Venc Pictures
******************************************/
VENC_RECV_PIC_PARAM_S stRecvParam;
stRecvParam.s32RecvPicNum =-1;
s32Ret = HI_MPI_VENC_StartRecvFrame(0,&stRecvParam);
if (HI_SUCCESS == s32Ret)
{
LOG_ERROR_FMT("[creatVENC_ChnAttr]HI_MPI_VENC_StartRecvPic ok with s32Re %#x! \n", s32Ret);
//return HI_FAILURE;
}
else
{
LOG_ERROR_FMT("[creatVENC_ChnAttr]HI_MPI_VENC_StartRecvPic failed s32Ret %#x \n", s32Ret);
}
return 0;
}
//初始化IVE 颜色空间转换
void CreateCSC(int iChn, HI_S32 sWidth,HI_S32 sHeight)
{
CSC_S* pstCSC = &stCSCNew[iChn];
pstCSC->sWidth = sWidth;
pstCSC->sHeight = sHeight;
char tmpMmzName[32] = {0};
sprintf(tmpMmzName,"InputImage%d",iChn);
// input image
memset(&pstCSC->stSrc,0,sizeof(IVE_SRC_IMAGE_S));
pstCSC->stSrc.enType = IVE_IMAGE_TYPE_YUV420SP;
HI_S32 s32Ret = HI_MPI_SYS_MmzAlloc_Cached(&pstCSC->stSrc.au64PhyAddr[0],
(HI_VOID**)&pstCSC->stSrc.au64VirAddr[0],
tmpMmzName,
HI_NULL,
sWidth * sHeight * 3 / 2);
if (HI_SUCCESS != s32Ret)
{
printf("can't alloc InputImage memory for %x\n", s32Ret);
}
pstCSC->stSrc.u32Width = sWidth;
pstCSC->stSrc.u32Height = sHeight;
pstCSC->stSrc.au32Stride[0] = sWidth;
// SP420
pstCSC->stSrc.au32Stride[1] = pstCSC->stSrc.au32Stride[0];
pstCSC->stSrc.au64PhyAddr[1] = pstCSC->stSrc.au64PhyAddr[0] + pstCSC->stSrc.au32Stride[0] * pstCSC->stSrc.u32Height;
pstCSC->stSrc.au64VirAddr[1] = pstCSC->stSrc.au64VirAddr[0] + pstCSC->stSrc.au32Stride[0] * pstCSC->stSrc.u32Height;
char tmpMmzNameOut[32] = {0};
sprintf(tmpMmzNameOut,"OutputImage%d",iChn);
// output image
memset(&pstCSC->stDst,
0,
sizeof(IVE_DST_IMAGE_S));
const HI_U32 u32Size = sWidth * sHeight * 3;
//pstCSC->stDst.enType = IVE_IMAGE_TYPE_U8C3_PLANAR;
pstCSC->stDst.enType = IVE_IMAGE_TYPE_U8C3_PACKAGE;
s32Ret = HI_MPI_SYS_MmzAlloc(&pstCSC->stDst.au64PhyAddr[0],
(void**)&pstCSC->stDst.au64VirAddr[0],
tmpMmzNameOut,
HI_NULL,
u32Size);
if (HI_SUCCESS != s32Ret)
{
printf("can't alloc OutputImage memory for %x\n", s32Ret);
}
pstCSC->stDst.u32Width = sWidth;
pstCSC->stDst.u32Height = sHeight;
pstCSC->stDst.au32Stride[0] = sWidth;
pstCSC->stDst.au64VirAddr[1] = pstCSC->stDst.au64VirAddr[0] + pstCSC->stDst.u32Height * pstCSC->stDst.au32Stride[0];
pstCSC->stDst.au64PhyAddr[1] = pstCSC->stDst.au64PhyAddr[0] + pstCSC->stDst.u32Height * pstCSC->stDst.au32Stride[0];
pstCSC->stDst.au32Stride[1] = pstCSC->stDst.au32Stride[0];
pstCSC->stDst.au64VirAddr[2] = pstCSC->stDst.au64VirAddr[1] + pstCSC->stDst.u32Height * pstCSC->stDst.au32Stride[1];
pstCSC->stDst.au64PhyAddr[2] = pstCSC->stDst.au64PhyAddr[1] + pstCSC->stDst.u32Height * pstCSC->stDst.au32Stride[1];
pstCSC->stDst.au32Stride[2] = pstCSC->stDst.au32Stride[0];
// ctrl
memset(&pstCSC->stCtrl, 0, sizeof(IVE_CSC_CTRL_S));
pstCSC->stCtrl.enMode = IVE_CSC_MODE_PIC_BT601_YUV2RGB;
}
//执行一次转化,完成整个过程
void BeginCSCOnceProcess(void * src,void * des,int width,int height,int chn)
{
HI_U32 ret;
//刷新stSrc cache 里的内容更新到内存
HI_MPI_SYS_MmzFlushCache((void *)stCSCNew[chn].stSrc.au64PhyAddr[0],(void *)stCSCNew[chn].stSrc.au64VirAddr[0], width*height*3/2);
usleep(200);
//开始IVE转化任务
ret=HI_MPI_IVE_CSC(&hIveHandleNew[chn],&stCSCNew[chn].stSrc,&stCSCNew[chn].stDst,&stCSCNew[chn].stCtrl,HI_TRUE);
if(ret !=0)
{
LOG_ERROR_FMT("[ScsBegin]HI_MPI_IVE_CSC , s32Ret=0x%X ", ret);
}
//判断转化是否成功
HI_BOOL bFinish;
ret = HI_ERR_IVE_QUERY_TIMEOUT;
while(ret == HI_ERR_IVE_QUERY_TIMEOUT)
{
ret = HI_MPI_IVE_Query(&hIveHandleNew[chn],&bFinish,HI_FALSE);
if(ret == HI_ERR_IVE_SYS_TIMEOUT)
{
LOG_DEBUG_FMT("updatedesImage720All chn %d HI_ERR_IVE_SYS_TIMEOUT",chn);
return -1;
}
else if(ret == HI_ERR_IVE_QUERY_TIMEOUT)
{
LOG_DEBUG_FMT("updatedesImage720All chn %d HI_ERR_IVE_QUERY_TIMEOUT ",chn);
usleep(1000);
}
}
//刷新stDst cache 里的内容更新到内存
HI_MPI_SYS_MmzFlushCache((void *)stCSCNew[chn].stDst.au64PhyAddr[0],(void *)stCSCNew[chn].stDst.au64VirAddr[0], width*height*3);
usleep(200);
//获取转化的数据
memcpy(des,(void *)stCSCNew[chn].stDst.au64VirAddr[0],width*height*3);
}
库编译
export CPPFLAGS="-I/home/jaya/wangyunqi/icode/baidu/pera-v8/ctl-dg_pudong/3rdParty/src/out/include/"
export LDFLAGS="-L/home/jaya/wangyunqi/icode/baidu/pera-v8/ctl-dg_pudong/3rdParty/src/out/lib"
./config no-asm shared --cross-compile-prefix=arm-himix410-linux- --prefix=/usr/local/ssl
./config no-asm shared --cross-compile-prefix=arm-himix410-linux- --prefix=/usr/local/ssl
./config no-asm shared --cross-compile-prefix=arm-himix410-linux- --prefix=/home/jaya/wangyunqi/icode/baidu/pera-v8/ctl-dg_pudong/3rdParty/src/wangyunqi1_1
编译openssl库
1.
./config no-asm zlib shared --cross-compile-prefix=aarch64-v01c01-linux-gnu- --prefix=/home/wyq/work/Onvif/onvif+gsoap/gsoap-2.8.116/out -DOPENSSL_NO_STATIC_ENGINE -fPIC -DOPENSSL_PIC
2.
删除-m64 make
3.
make install
./configure --host=arm-himix410-linux
export CC=aarm-himix410-linux-gcc
export CXX=arm-himix410-linux-g++
./config no-asm --shared --static --cross-compile-prefix=arm-himix410-linux-
1./config no-asm no-shared no-async --cross-compile-prefix=arm-himix410-linux-
sed -i 's/-m64//' Makefile
make
make install
cd /usr/local/ssl/lib
file libcrypto.so.1.1
编译curl库
./configure --host=arm-linux CC=aarch64-mix210-linux-gcc CXX=aarch64-mix210-linux-g++
CPPFLAGS="-I/sda3/openssl-1.0.2p/ -I/sda3/openssl-1.0.2p/include/" LDFLAGS="-L/sda3/openssl-1.0.2p/"
CPPFLAGS="-I/sda3/openssl-1.0.2p/include/" LDFLAGS="-L/sda3/openssl-1.0.2p/" ./configure --host=arm-linux CC=csky-abiv2-linux-gcc CXX=csky-abiv2-linux-g++
CPPFLAGS="-mcpu=ck860f -mfloat-abi=hard -I/sda3/openssl-1.0.2p/include/" LDFLAGS="-L/sda3/openssl-1.0.2p/" ./configure --host=arm-linux CC=csky-abiv2-linux-gcc CXX=csky-abiv2-linux-g++
编译libz.a库
export CFLAGS="-mcpu=ck860f -mfloat-abi=hard"
export CC=arm-himix200-linux-gcc
./configure
export CC=arm-hisiv300-linux-gcc
./configure --host=arm-linux CC=arm-himix410-linux-gcc CXX=arm-himix410-linux-g++
xt_sip库编译
rssip库修改
./configure --enable-static=yes --enable-shared=no --host=arm-linux CC=csky-abiv2-linux-gcc CXX=csky-abiv2-linux-g++ CFLAGS="-mcpu=ck860f -mfloat-abi=hard"
./configure --host=arm-linux CC=csky-abiv2-linux-gcc CXX=csky-abiv2-linux-g++ CFLAGS="-mcpu=ck860f -mfloat-abi=hard"
2.resip.cmake文件修改
SET(CMAKE_C_COMPILER "/home/bin/csky-abiv2-linux-gcc")
SET(CMAKE_CXX_COMPILER "/home/bin/csky-abiv2-linux-g++")
./bootstrap --prefix=/usr/local/bin CFLAGS = -DCMAKE_USE_OPENSSL=OFF
xt_sip库编译
rssip库修改
./configure --enable-static=yes --enable-shared=no --host=arm-linux CC=arm-himix200-linux-gcc CXX=arm-himix200-linux-g++
./configure --host=arm-linux CC=arm-himix200-linux-gcc CXX=arm-himix200-linux-g++
2.resip.cmake文件修改
SET(CMAKE_C_COMPILER "/home/bin/csky-abiv2-linux-gcc")
SET(CMAKE_CXX_COMPILER "/home/bin/csky-abiv2-linux-g++")
boost编译
1. export CC=/opt/hisi-linux/x86-arm/arm-himix200-linux/bin/arm-himix200-linux-gcc
2. ./bootstrap.sh --prefix=/home/boost
3.修改project-config.jam第12行 using gcc 改为 using gcc : arm : arm-himix200-linux-gcc
4. ./bjam stage link=static threading=multi --layout=tagged --build-type=complete
./configure --host=arm-linux CC=arm-himix200-linux-gcc CXX=arm-himix200-linux-g++
ffmepg源码编译
./configure --prefix=./install --enable-cross-compile --disable-doc --target-os=linux --cpu=cortex-a73.cortex-a53 --arch=aarch64 --cross-prefix=arm-himix410-linux-
./configure --prefix=./install --enable-cross-compile --disable-doc --target-os=linux --cpu=cortex-a73.cortex-a53 --arch=armv7-a --cross-prefix=arm-himix410-linux- --enable-nonfree --enable-static --disable-shared --disable-debug --disable-iconv --enable-small --disable-network --disable-programs --disable-swresample --disable-swscale --disable-avdevice --disable-postproc --disable-avfilter --enable-pic --disable-everything --enable-muxer=mp4 --enable-muxer=flv --enable-muxer=mov --enable-muxer=h264 --enable-muxer=hevc --enable-protocol=file --enable-demuxer=m4v --enable-demuxer=flv --enable-demuxer=mov --enable-demuxer=h264 --enable-demuxer=hevc --enable-bsf=h264_mp4toannexb --enable-bsf=hevc_mp4toannexb --enable-encoder=flv --enable-encoder=mpeg4 --enable-encoder=mpeg4_v4l2m2m --enable-encoder=msmpeg4v2 --enable-encoder=msmpeg4v3 --disable-gpl
gdb源码编译
./configure --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf CC=/home/ubuntu/maqiao/cambricon/cross_compiler/gcc-linaro-6.2.1-2016.11-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc
./configure --host=aarch64-linux-gnu --target=arm-linux-gnu CC=aarch64-linux-gnu-gcc --prefix=./tmp
./configure CC=aarch64-linux-gnu-gcc
./configure --host=aarch64-mix210-linux CC=aarch64-mix210-linux-gcc CPPFLAGS="-I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/src/termcap-1.3.1/" LDFLAGS="-L/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/src/termcap-1.3.1/"
编译修改文档
https://blog.csdn.net/panfei263031/article/details/123064571
poco库编译:
https://www.jianshu.com/p/a55129366530
在ARM-Linux
OPENSSL_INCLUDE = /usr/include
OPENSSL_LIB =/usr/local/ssl/lib
交叉编译osip库,exosip
./configure --host=arm-linux CC=aarch64-mix210-linux-gcc CFLAGS=-mcmodel=large
1./config no-asm --shared no-async --cross-compile-prefix=aarch64-mix210-linux-
编译jsoncpp】√
unzip jsoncpp-master.zip
cd jsoncpp-master
修改CMakeLists.txt 要求的cmake版本改成是2.8.1
cmake -DCMAKE_C_COMPILER=aarch64-mix210-linux-gcc -DCMAKE_CXX_COMPILER=aarch64-mix210-linux-g++ -DJSONCPP_WITH_TESTS=OFF -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=OFF -DCMAKE_INSTALL_PREFIX=./out
expart安装
./configure --host=arm-linux CC=aarch64-mix210-linux-gcc --prefix=/usr/local/expat
export CC=aarch64-mix210-linux-gcc
export CPP=aarch64-mix210-linux-g++
freetype编译
./configure --host=aarch64-mix210-linux CC=aarch64-mix210-linux-gcc --with-png=no --with-harfbuzz=no
export CPPFLAGS="-I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/src/zlib-1.2.11/out/include/"
export LDFLAGS="-L/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/src/zlib-1.2.11/out/lib"
parted编译
./configure --host=arm-linux CC=aarch64-mix210-linux-gcc --disable-device-mapper --without-readline --disable-shared
./configure --host=arm-linux CC=arm-linux-gnueabihf-gcc --prefix=`pwd`/install --disable-shared --without-ncurses --disable-fsck
--enable-device-mapper
export CPPFLAGS="-I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include/ -I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include/uuid/"
export LDFLAGS="-L/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/build/ivr/Libs"
export CPPFLAGS="-I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include/"
export LDFLAGS="-L/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/lib/iconv"
util_linux编译就是uuid
./configure --host=arm-linux CC=aarch64-mix210-linux-gcc --without-ncurses --with-gnu-ld=aarch64-mix210-linux
libxml2编译
./configure --host=arm-linux CC=aarch64-mix210-linux-gcc --disable-static --without-python --without-zlib
//阿里云编译
cmake . -DCURL_INCLUDE_DIR=/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include/curl/ -DCURL_LIBRARY=/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/lib/curl/libcurl.a -DAPR_INCLUDE_DIR=/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include/apr-1/ -DAPR_LIBRARY=/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/lib/apr/libapr-1.a -DAPR_UTIL_INCLUDE_DIR=/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include/apr-util-1/ -DAPR_UTIL_LIBRARY=/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/lib/aprutil/libaprutil-1.a -DMINIXML_INCLUDE_DIR=/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include -DMINIXML_LIBRARY=/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/lib/mxml/libmxml.a
//百度云bosd编译
export CPPFLAGS="-I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include/json/"
export LDFLAGS="-L/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/lib/json"
export CPPFLAGS="-I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include/curl/"
export LDFLAGS="-L/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/lib/curl"
export CPPFLAGS="-I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include/openssl/"
export LDFLAGS="-L/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/lib/openssl/"
export CPPFLAGS="-I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include"
export LDFLAGS="-L/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/lib/uuid"
./configure --host=arm-linux CC=aarch64-mix210-linux-g++
libs3交叉编译
export CPPFLAGS="-I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include/libxml/"
export LDFLAGS="-L/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/lib/xml2"
export CPPFLAGS="-I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include/"
export LDFLAGS="-L/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/lib/curl"
export CPPFLAGS="-I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include/openssl/"
export LDFLAGS="-L/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/lib/openssl/"
export CPPFLAGS="-I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include/"
export LDFLAGS="-L/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/lib/iconv"
export CPPFLAGS="-I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/src/zlib-1.2.11/out/include/"
export LDFLAGS="-L/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/src/zlib-1.2.11/out/lib"
openvpn源码编译
export CPPFLAGS="-fpermissive -I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/include/ -I/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/src/lzo-2.06/include/"
export LDFLAGS="-L/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/lib/openssl/ -L/home/wuhan/wangyunqi/icode/baidu/pera-v8/ctl-binocular/3rdParty/src/lzo-2.06/src/.libs"
./configure --host=arm-linux CC=aarch64-mix210-linux-g++ --disable-plugin-auth-pam
mp4v2交叉编译
./configure --host=arm-linux CC=aarch64-mix210-linux-gcc CXX=aarch64-mix210-linux-g++
export CPPFLAGS="-fpermissive"
LD_LIBRARY_PATH="./:$LD_LIBRARY_PATH"
export LD_LIBRARY_PATH
ntp源码编译
http://www.ntp.org/downloads/
https://www.likecs.com/show-203442944.html
./configure --host=arm-linux CC=aarch64-mix210-linux-gcc CXX=aarch64-mix210-linux-g++ --with-yielding-select=yes
timeout 10 ntpdate 203.107.6.88
mqttt
make CC=arm-himix410-linux-gcc CXX=arm-himix410-linux-g++

被折叠的 条评论
为什么被折叠?



