RV1126的OSD模块和SDL_TTF结合输出H264文件
目录
一.RV1126多线程处理输出OSD字符叠加图层的流程
1.1. VI模块的初始化
1.2. 初始化VENC模块:
1.3. 初始化RGN模块:
1.4. 绑定VI模块和VENC模块,伪代码如下
1.5. 创建多线程进行OSD字库的叠加:
1.6. 获取每一帧处理过后的VENC数据:
二.程序代码
三.运行结果
一.RV1126多线程处理输出OSD字符叠加图层的流程
用RV1126多线程输出OSD叠加需要经过上面几个重要步骤,分别是VI模块初始化、VENC模块初始化、RGN模块初始化、多线程进行OSD字库的叠加(里面主要是进行字库的渲染和RV1126的OSD模块叠加)、多线程获取每一帧OSD处理过后的编码数据
1.1. VI模块的初始化
VI模块的初始化实际上就是对VI_CHN_ATTR_S的参数进行设置、然后调用RK_MPI_VI_SetChnAttr设置VI模块并使能RK_MPI_VI_EnableChn,伪代码如下:
VI_CHN_ATTR_S vi_chn_attr;
。。。。。。。。。。。。。。。(这里是设置VI的属性)
ret = RK_MPI_VI_SetChnAttr(CAMERA_ID, 0, &vi_chn_attr);
ret |= RK_MPI_VI_EnableChn(CAMERA_ID, 0);
1.2. 初始化VENC模块:
VENC模块的初始化实际上就是对VENC_CHN_ATTR_S的参数进行设置、然后调用RK_MPI_VENC_CreateChn创建编码器,伪代码如下:
VENC_CHN_ATTR_S venc_chn_attr;
venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;
。。。。。。。。。。。。。。。。(这里是设置VENC的属性)
ret = RK_MPI_VENC_CreateChn(0, &venc_chn_attr);
1.3. 初始化RGN模块:
RGN是RV1126图层区域功能管理的功能,比方说OSD图层、Bitmap位图都属于RGN模块。所以开发者要用到OSD功能都需要初始化RGN,具体的API描述如下:
RK_S32_RK_MPI_VENC_RGN_Init(VENC_CHN_VeChn, VENC_COLOR_TBL_S *stColorTbl);
第一个传参数:VENC编码通道号
第二个传参数:VENC_COLOR_TBL_S结构体指针,默认是NULL
RK_MPI_VENC_RGN_Init(0, NULL);
1.4. 绑定VI模块和VENC模块,伪代码如下
MPP_CHN_S vi_chn_s;
MPP_CHN_S venc_chn_s;
ret = RK_MPI_SYS_Bind(&vi_chn_s, &venc_chn_s);
1.5. 创建多线程进行OSD字库的叠加:
创建OSD叠加线程主要是对VENC的数据进行字库的创建和OSD的数据叠加:
......................................................................
char *pstr = "2019-11-21 15:40:29";
if(TTF_Init() < 0)
{
fprintf(stderr, "Couldn't initialize TTF: %s\n", SDL_GetError());
}
ttf_font = TTF_OpenFont("./fzlth.ttf", 48);
if(ttf_font == NULL)
{
fprintf(stderr, "Couldn't load %d pt font from %s: %s\n", 48, "ptsize", SDL_GetError());
}
text = TTF_RenderText_Solid(ttf_font, pstr, sdl_color);
............................................................
SDL_PixelFormat *pixel_format = (SDL_PixelFormat *)malloc(sizeof(SDL_PixelFormat));
........................
temp = SDL_ConvertSurface(text, pixel_format, 0);
static int get_align16_value(int input_value, int align)
{
int handle_value = 0;
if (align && (input_value % align))
handle_value = (input_value/ align + 1) * align;
return handle_value;
}
{
......................
bitmap_width = get_align16_value(temp->w, 16);
bitmap_height = get_align16_value(temp->h, 16);
if (bitmap_width < 64)
bitmap_width = 64;
if (bitmap_height < 64)
bitmap_height = 64;
BITMAP_S BitMap;
BitMap.enPixelFormat = PIXEL_FORMAT_ARGB_8888;
BitMap.u32Width = bitmap_width;
BitMap.u32Height = bitmap_height;
BitMap.pData = malloc(wxh_size * fmt->BytesPerPixel);
if (!BitMap.pData)
{
printf("ERROR: no mem left for argb8888(%d)!\n", wxh_size * fmt->BytesPerPixel);
break;
}
memcpy(BitMap.pData, temp->pixels, (temp->w) * (temp->h) * fmt->BytesPerPixel);
OSD_REGION_INFO_S RngInfo;
RngInfo.enRegionId = REGION_ID_7;
RngInfo.u32PosX = 0;
RngInfo.u32PosY = 0;
RngInfo.u32Width = bitmap_width;
RngInfo.u32Height = bitmap_height;
RngInfo.u8Enable = 1;
RngInfo.u8Inverse = 0;
ret = RK_MPI_VENC_RGN_SetBitMap(0, &RngInfo, &BitMap);
...........
}
上面是OSD叠加的代码,主要是通过RV1126的RNG模块对VENC进行OSD进行字库叠加。其中划黑色粗体是这个程序的核心,它需要把每个width和height进行16位对齐,因为OSD需要处理16位对齐的数据(所谓十六位对齐可以理解为,width和height能够被16位整除)。这里面用到16位对齐的方法是get_align16_value,input_value指的是输入的数值,align是要对齐的数值。
1.6. 获取每一帧处理过后的VENC数据:
开启一个线程去采集每一帧VENC模块的数据,使用的API是RK_MPI_SYS_GetMediaBuffer, 模块ID是RK_ID_VENC,通道号ID是VENC创建的ID号。这个API的具体作用已我们直接上伪代码:
while(1)
{
.............................
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, s32_chn_id, -1);
fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, h264_file);
............................
}
二.程序代码
#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>// #include "common/sample_common.h"
#include "rkmedia_api.h"#include <stdio.h>
#include "SDL.h"
#include "SDL_ttf.h"
#include <time.h>#define CAMERA_PATH "rkispp_scale0"
#define CAMERA_ID 0
#define CAMERA_CHN 0
#define VENC_CHN 0//对某个数值进行对齐
static int get_align16_value(int input_value, int align)
{int handle_value = 0;if (align && (input_value % align))handle_value = (input_value/ align + 1) * align;return handle_value;
}void * bitmap_osd_handle_thread(void * args)
{pthread_detach(pthread_self());int ret ;TTF_Font * ttf_font;char *pstr = "2019-11-21 15:40:29";SDL_Surface * text_surface;SDL_Surface * convert_text_surface;SDL_PixelFormat * pixel_format;//TTF模块的初始化ret = TTF_Init();if(ret < 0){printf("TTF_Init Failed...\n");}//打开TTF的字库ttf_font = TTF_OpenFont("./fzlth.ttf", 48);if(ttf_font == NULL){printf("TTF_OpenFont Failed...\n");}//SDL_COLOR黑色,RGB(0,0,0)SDL_Color sdl_color;sdl_color.r = 0;sdl_color.g = 0;sdl_color.b = 0;text_surface = TTF_RenderText_Solid(ttf_font, pstr, sdl_color); 渲染文字//ARGB_8888pixel_format = (SDL_PixelFormat *)malloc(sizeof(SDL_PixelFormat));pixel_format->BitsPerPixel = 32; //每个像素所占的比特位数 pixel_format->BytesPerPixel = 4; //每个像素所占的字节数 pixel_format->Amask = 0XFF000000;//ARGB的A掩码,A位0xffpixel_format->Rmask = 0X00FF0000;//ARGB的R掩码,R位0xffpixel_format->Gmask = 0X0000FF00;//ARGB的G掩码,G位0xffpixel_format->Bmask = 0X000000FF;//ARGB的B掩码,B位0xffconvert_text_surface = SDL_ConvertSurface(text_surface, pixel_format, 0);if(convert_text_surface == NULL){printf("convert_text_surface failed...\n");}BITMAP_S bitmap;//Bitmap位图结构体bitmap.u32Width = get_align16_value(convert_text_surface->w, 16);//Bitmap的宽度bitmap.u32Height = get_align16_value(convert_text_surface->h, 16);//Bitmap的高度bitmap.enPixelFormat = PIXEL_FORMAT_ARGB_8888;像素格式ARGB8888bitmap.pData = malloc((bitmap.u32Width) * (bitmap.u32Height) * pixel_format->BytesPerPixel); bitmap的data的分配大小memcpy(bitmap.pData, convert_text_surface->pixels, (convert_text_surface->w) * (convert_text_surface->h) *pixel_format->BytesPerPixel);bitmap的data赋值OSD_REGION_INFO_S rgn_info; //OSD_RGN_INFO结构体rgn_info.enRegionId = REGION_ID_0; //rgn的区域IDrgn_info.u32Width = bitmap.u32Width ; //osd的长度rgn_info.u32Height = bitmap.u32Height ; //osd的高度rgn_info.u32PosX = 128; //Osd的X轴方向rgn_info.u32PosY = 128; //Osd的Y轴方向rgn_info.u8Enable = 1; 使能OSD模块,1是使能,0为禁止。rgn_info.u8Inverse = 0; //禁止翻转ret = RK_MPI_VENC_RGN_SetBitMap(VENC_CHN, &rgn_info, &bitmap); //设置OSD位图if(ret){printf("RK_MPI_VENC_RGN_SetBitMap failed...\n");}else{printf("RK_MPI_VENC_RGN_SetBitMap Success...\n");}return NULL;
}void * get_h264_data_thread(void * args)
{pthread_detach(pthread_self());FILE *h264_file = fopen("test_osd_venc.h264", "w+");MEDIA_BUFFER mb ;while (1){mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, VENC_CHN, -1);if(!mb){printf("RK_MPI_SYS_GetMediaBuffer failed...\n");break;}printf("Get osd buffer success.....\n");fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, h264_file);RK_MPI_MB_ReleaseBuffer(mb);}return NULL;
}int main()
{int ret;VI_CHN_ATTR_S vi_chn_attr;vi_chn_attr.pcVideoNode = CAMERA_PATH; //设置视频设备节点路径vi_chn_attr.u32Width = 1920; //设置分辨率的宽度vi_chn_attr.u32Height = 1080; //设置分辨率的高度vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12; //设置图像类型vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP; //设置VI获取类型vi_chn_attr.u32BufCnt = 3; //设置缓冲数量vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL; //设置VI工作类型ret = RK_MPI_VI_SetChnAttr(CAMERA_ID, CAMERA_CHN, &vi_chn_attr);if (ret){printf("Vi Set Attr Failed.....\n");return 0;}else{printf("Vi Set Attr Success.....\n");}ret = RK_MPI_VI_EnableChn(CAMERA_ID, CAMERA_CHN); // if (ret){printf("Vi Enable Attr Failed.....\n");return 0;}else{printf("Vi Enable Attr Success.....\n");}VENC_CHN_ATTR_S venc_chn_attr;memset(&venc_chn_attr, 0, sizeof(venc_chn_attr));venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;//设置编码器类型venc_chn_attr.stVencAttr.imageType = IMAGE_TYPE_NV12;//设置编码图像类型venc_chn_attr.stVencAttr.u32PicWidth = 1920;//设置编码分辨率宽度venc_chn_attr.stVencAttr.u32PicHeight = 1080;//设置编码分辨率高度venc_chn_attr.stVencAttr.u32VirWidth = 1920;//设置编码分辨率虚宽venc_chn_attr.stVencAttr.u32VirHeight = 1080;//设置编码分辨率虚高venc_chn_attr.stVencAttr.u32Profile = 77;//设置编码等级venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;//设置H264的CBR码率控制模式venc_chn_attr.stRcAttr.stH264Cbr.u32Gop = 25;venc_chn_attr.stRcAttr.stH264Cbr.u32BitRate = 2000000; // 2Mbvenc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;//设置源帧率分母venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25;//设置源帧率分子venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;//设置目标帧率分母venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25;//设置目标帧率分子ret = RK_MPI_VENC_CreateChn(0, &venc_chn_attr); //VENC模块的初始化if (ret){printf("ERROR: create VENC[0] error! ret=%d\n", ret);return 0;}else{printf("VENC SUCCESS\n");}ret = RK_MPI_VENC_RGN_Init(VENC_CHN, NULL);//RGN模块的初始化if(ret){printf("Create VENC_RGN Failed .....\n");return 0;}else{printf("Create VENC_RGN Success .....\n");}MPP_CHN_S vi_chn_s;MPP_CHN_S venc_chn_s;vi_chn_s.enModId = RK_ID_VI;vi_chn_s.s32ChnId = 0;venc_chn_s.enModId = RK_ID_VENC;venc_chn_s.s32ChnId = 0;ret = RK_MPI_SYS_Bind(&vi_chn_s, &venc_chn_s); VI模块节点和VENC节点绑定if(ret){printf("RK_MPI_SYS_Bind Failed .....\n");}else{printf("RK_MPI_SYS_Bind Success .....\n");}pthread_t bitmap_pid, venc_pid;pthread_create(&bitmap_pid, NULL, bitmap_osd_handle_thread, NULL); //创建OSD叠加线程pthread_create(&venc_pid, NULL, get_h264_data_thread, NULL); //获取H264码流线程while (1){sleep(2);}RK_MPI_SYS_UnBind(&vi_chn_s, &venc_chn_s);RK_MPI_VENC_DestroyChn(VENC_CHN);RK_MPI_VI_DisableChn(CAMERA_ID, CAMERA_CHN);return 0;
}
三.运行结果
注意:假设出现水印显示不完整,这种情况下需要调整X轴和Y轴的坐标轴,让其水印显示完整。
相关文章:

RV1126的OSD模块和SDL_TTF结合输出H264文件
目录 一.RV1126多线程处理输出OSD字符叠加图层的流程 1.1. VI模块的初始化 1.2. 初始化VENC模块: 1.3. 初始化RGN模块: 1.4. 绑定VI模块和VENC模块,伪代码如下 1.5. 创建多线程进行OSD字库的叠加: 1.6. 获取每一帧处理过后的…...

GEE:计算长时间序列NPP与NDVI之间的相关系数
GEE中内置了计算相关系数的函数,可以分析两个变量之间的相关性,比如要分析两个波段之间的相关性,主要用到ee.Reducer.pearsonsCorrelation()函数。 ee.Reducer.pearsonsCorrelation() 内容:创建一个双输入归约器,用于…...

水仙花数(华为OD)
题目描述 所谓水仙花数,是指一个n位的正整数,其各位数字的n次方和等于该数本身。 例如153是水仙花数,153是一个3位数,并且153 13 53 33。 输入描述 第一行输入一个整数n,表示一个n位的正整数。n在3到7之间&#x…...

【对话状态跟踪】关心整个对话过程用户完整意图变化
对话状态管理器 核心逻辑是解决键冲突和验证范围有效性, 但需依赖外部输入的正确性。在实际应用中, 可能需要结合用户提示或自动修正逻辑以提高鲁棒性。 NLU 槽 值 对儿 NLU的目的是把自然语言解析成结构化语义。结构化语义有多种表示方式,…...

【分享】网间数据摆渡系统,如何打破传输瓶颈,实现安全流转?
在数字化浪潮中,企业对数据安全愈发重视,网络隔离成为保护核心数据的重要手段。内外网隔离、办公网与研发网隔离等措施,虽为数据筑牢了防线,却也给数据传输带来了诸多难题。传统的数据传输方式在安全性、效率、管理等方面暴露出明…...

TikTok创作者市场关闭!全新平台TikTok One将带来哪些改变?
TikTok创作者市场关闭,全新平台TikTok One上线,创作者和品牌将迎来哪些新机遇? 近日,TikTok宣布关闭其原有的创作者市场(TikTok Creator Marketplace),并推出全新平台TikTok One。这一消息在社…...
LeetCode hot 100—矩阵置零
题目 给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 示例 1: 输入:matrix [[1,1,1],[1,0,1],[1,1,1]] 输出:[[1,0,1],[0,0,0],[1,0,1]]示例 2࿱…...

部署Windows Server自带“工作文件夹”实现企业网盘功能完整步骤
前文已经讲解过Windows Server自带的“工作文件夹”功能,现以Windows Server 2025为例介绍部署工作文件夹的完整步骤: 为了确保您能够顺利部署和充分利用工作文件夹的功能,我将按照以下步骤进行讲解。 请注意,在域环境中部署工作…...

植物大战僵尸杂交版v3.3最新版本(附下载链接)
B站游戏作者潜艇伟伟迷于12月21日更新了植物大战僵尸杂交版3.3版本!!!,有b站账户的记得要给作者三连关注一下呀! 不多废话下载链接放上: 夸克网盘链接::https://pan.quark.cn/s/6f2a…...
非关系型数据库和关系型数据库的区别
非关系型数据库(NoSQL)和关系型数据库(SQL)的主要区别体现在以下几个方面: 数据模型: 关系型数据库(SQL):数据以表格形式存储,数据行和列组成,每个…...

CPU负载高告警问题的定位与优化建议
#作者:猎人 文章目录 背景一.问题排查1.1 找到相应的容器1.2 找到对应的deployment1.3 查看pod日志1.4 查看nginx配置文件1.5 查看deployment的yaml文件 二.优化建议 背景 Docker 版本:19.03.14 Operating System: Red Hat Ent…...

2月28日,三极管测量,水利-51单片机
众所周知,三极管(BJT)有三个管脚,基极(B)、集电极(C)、发射极(E),在实际应用中,不可避免地会遇到引脚辨别的问题。接下来就讲下三极管…...

批量提取 Word 文档中的图片
在 Word 文档中,我们可以插入图片、文本、链接等各种各样的资源。在某些场景下我们需要提取这些信息,比如我们需要提取 Word 文档中的图片,将每一个 Word 文档中的图片都提取出来放到一个单独的文件夹中,那么我们应该怎么做呢&…...

C#—Settings配置详解
C#—Settings配置详解 在C#项目中,全局配置通常指的是应用程序的设置(settings),这些设置可以跨多个类或组件使用,并且通常用于存储应用程序的配置信息,如数据库连接字符串、用户偏好设置等。 Settings配置…...
UI自动化框架介绍
selenium Pytest Allure 优势 1.1 更高效的自动化测试 Selenium 提供了强大的浏览器自动化能力,可以模拟用户与网页的交互。它能够在不同浏览器上运行测试,确保 Web 应用程序在多种环境下的兼容性。Pytest 是一个非常灵活、简洁的 Python 测试框架&a…...
【工具推荐】在线提取PDF、文档、图片、论文中的公式
网址1:https://simpletex.cn/ai/latex_ocr 网址2: https://www.latexlive.com/home 推荐理由:无需下载,在线使用,直接 截图 CTRLV 效果更佳。...
帮我设计一个c语言学习阶段
以下是一个针对不同学习阶段的C语言学习计划,你可以根据自己的基础和目标进行调整: 第一阶段:基础语法与程序结构(第1-4周) 目标 熟悉C语言的基本语法和程序结构。 能够编写简单的程序。 学习内容 环境搭建 安装…...
解决windows npm无法下载electron包的问题
1.将nsis.zip解压到C:\Users\XXX\AppData\Local\electron-builder\Cache 2.将winCodeSign.zip解压到C:\Users\XXX\AppData\Local\electron-builder\Cache 3.将electron-v20.3.8-win32-ia32.zip复制到C:\Users\XXX\AppData\Local\electron\Cache 4.将electron-v20.3.8-win32-…...

网络编程 day01
网络编程 day01 0. 网络编程课程介绍1. 认识网络1.网络发展史2.局域网与广域网局域网(LAN)广域网(Wan) 3.光猫4.路由器5.交换机与路由器6.网线 2. IP1. 基本概念2. 网络号/主机号(二级划分)3. IP地址分类整…...

【三.大模型实战应用篇】【4.智能学员辅导系统:docx转PDF的自动化流程】
去年团队庆功宴上,我司CTO端着酒杯过来:“老王啊,咱们现在文档解析做得挺溜了,但老师们总抱怨下载的作业格式乱码…” 我看了眼手机里凌晨三点收到的崩溃警报,把杯里的可乐一饮而尽——得,新的副本又开了。 一、为什么PDF转换比想象中难十倍? 某次用户调研中,数学教研…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...

JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...