居右连
1月前来过
全职 · 300/日  ·  6525/月
工作时间: 工作日09:00-24:00、周末09:00-24:00工作地点: 远程
服务企业: 0家累计提交: 0工时
联系方式:
********
********
********
聊一聊

使用APP扫码聊一聊

个人介绍

我是程序员客栈的【居右连】,一名【后端开发方向】; 我毕业于【南昌大学】,担任过【江西掌悦网络科技有限公司】的【高级后端开发工程师】 负责过【云短信】,【花花直播】,【电商后台管理系统】的开发; 熟练使用【Springboot】,【ThinkPHP】; 如果我能帮上您的忙,请点击“立即预约”或“发布需求”!

工作经历

  • 2021-08-12 -至今江西掌悦网络科技有限公司高级后端工程师

    1.设计和实现数据存储方案2.实现业务逻辑3.开发和维护Web API4.管理服务器端基础设施5.进行性能调优 负责过【云短信】,【花花直播】,【电商后台管理系统】的开发; 熟练使用【Springboot】,【ThinkPHP】;

教育经历

  • 2017-09-01 - 2021-07-01南昌大学计算机科学与技术本科

技能

C++
Android
HTML5
CSS
0
1
2
3
4
5
0
1
2
3
4
5
作品
花花直播

1 概述 1.1通用直播总体架构 Java在用于视频开发时主要采用的技术框架是FFmpeg和OpenCV。数据库和其他的功能实现可以使用Java Web的相关技术就可以实现。 1 FFmpeg主要有两种网络传输协议;RTSP、RTMP RTSP;实时流传输协议,是TCP/IP协议体系中的一个应用层协议.RTSP在体系结构上位于RTP和RTCP之上,它使用TCP或UDP完成数据传输。 RTMP;实时消息传输协议)的首字母缩写。该协议基于TCP,是一个协议族,包括RTMP基本协议及RTMPT/RTMPS/RTMPE等多种变种。 从上面的简单描述可以看出,这两种协议完全符合808和809协议的。 1.2 视频直播流程 2 具体实现方案 2.1 图像和声音同步流程 2.2 框架作用说明 OpenCV; 内置数据结构和输入/输出(In-build data structures and input/output) 关于OpenCV的好处之一就是它提供了许多内置的用于图像处理和计算机视觉相关操作的基础元素。如果你需要通过scratch写入某些内容,你将不得不定义一些东西,比如图像、点、角度等等,这些几乎是任何计算机视觉算法的基础。OpenCV提供了这些开箱即用的基础数据结构,它们都包含在core模块中。另外一个好处是,这些数据结构都已经针对速度和内存做了优化,因此,你不用担心实现细节。 imgcodecs模块用于处理读取和写入图像文件(image file)。 图像处理操作(Image processing operations) 构建图形用户界面(Build GUI) 视频分析(Video analysis) 3D重建(3D reconstruction) 特征提取(Feature extraction) 目标检测(Object detection) 机器学习(Machine learning) 计算摄影(Computational photography) 形状分析(Shape analysis) 光流算法(Optical flow algorithms) 人脸和目标识别(Face and object recognition) 表面匹配(Surface matching) 文本检测和识别(Text detection and recognition) FFmpeg; 1、AVUtil: 核心工具库 2、AVFormat:提供了音视频容器格式的封装和解析以及所支持的协议文件格式和协议库 3、AVCodec: 编解码库 4、AVFilter: 音视频滤镜库 如视频加水印、音频变声 5、AVDevice: 输入输出设备库,提供设备数据的输入与输出,如读取摄像头数据、屏幕录制 6、SwrRessample: 音频重采样 7、SWScale: 图像格式转换的模块,yuv -> rgb 8、PostProc:后期处理 3 编解码 3.1编解码流程 编码流程; 1、avformat_alloc_output_context2: 初始化输出码流的 AVFormatContext 2、avio_open: 打开输出文件 3、av_new_stream: 创建输出码流的 AVStream 4、avcodec_find_encoder: 查找编码器。 5、avcodec_open2: 打开编码器 6、avformat_write_header: 写文件头(对于某些没有文件头的封装格式,不需要此函数。比如说 MPEG2TS) 7、avcodec_encode_video2: 编码一帧视频。即将 AVFrame(存储 YUV 像素数据)编码为 AVPacket(存储 H.264 等格式的码流数据) 8、av_write_frame: 将编码后的视频码流写入文件。 9、flush_encoder: 输入的像素数据读取完成后调用此函数。用于输出编码器中剩余的 AVPacket。 10、av_write_trailer: 写文件尾(对于某些没有文件头的封装格式,不需要此函数。比如说 MPEG2TS)。 11、释放资源 解码流程: 1、av_register_all: 先调用avcodec_register_all来注册所有config.h里面开放的编解码器,然后会注册所有的Muxer和Demuxer(也就是封装格式),最后注册所有的Protocol(即协议层的东西) 2、avformat_alloc_context: 分配初始化一个AVFormatContext结构体 3、avformat_open_input: 解码时,根据文件路径判断文件格式,决定使用哪个Demuxer,构建好AVStream 4、avformat_find_stream_info: 解码时,作用是从文件中提取流信,将所有的Stream的MetaData信息填充好,先read_packet一段数据解码分析流数据 5、穷举所有的流,查找其中种类为 CODEC_TYPE_VIDEO 6、avcodec_find_decoder: 作用是找解码器,avcodec_resgister_all已经将解码器和编码器放到一个链表中,根据codec Id和name循环遍历找出 7、avcodec_open2: avcodec_open2(AVCodecContext, Codec, NULL) 作用是打开编码器或解码器 8、avcodec_alloc_frame: 为解码帧分配内存 9、不停地从码流中提取出帧数据 av_read_frame: 解码时,读取出AVPacket,对应音频流,一个AVPacket可能包含多个AVFrame,对应视频流,一个AVPacket对应一个AVFrame 10、判断帧的类型,对于视频帧调用: avcodec_decode_video: 11、avcodec_close: 释放解码器 12、av_close_input_file: 关闭输入文件 3.2 编解码相关技术 由于我们源程序采用的视频文件格式是flv,所以我这里先以flv的视频文件格式为例。 FFMPEG如果是压缩为FLV文件 3个编码可选 -c:v flv 标准FLV编码 这个好处是速度快 清晰度高的话 视频文件会比较大 -c:v vp6 VP6编码 这个大家都很少使用 其实这个也算不错 -c:v libx264 H.264编码 估计使用这个的比较多 优点是同等清晰度 视频文件更小 缺点就是转换慢的吐血 几种常见的视频编码格式; 首先纠正一下上面说的一个观点,其实mkv和avi并不能说成是高清格式,所谓的高清(High Definition )指的是分辨率达到一定水平之上才称之为高清,像720p、1080i、1080p、a1080、a720、816p 。进行高清视频编码时一般使用的编码格式为H.264、VC-1,而进行封装时可以使用mkv、mp4、avi这类文件格式进行封装。所以mkv、avi只是一种封装格式,里面装的视频如果是一个320x240分辨率的,那这个视频就不是一个高清视频。下面说说这几种格式的特点。 1、mkv:mkv不等同于音频或视频编码格式,它只是为这些进行过音视频编码的数据提供了一个封装的格式,简单的说就是指定音视频数据在文件中如何排列放置。 MKV最大的特点就是能容纳多种不同类型编码的视频、音频及字幕流,俗称万能媒体容器。 MKV加入AVI所没有的EDC错误检测代码,这意味着即使是没有下载完毕的MKV文件也可以顺利回放,这些对AVI来说完全是不可想象的。虽然MKV加入了错误检测代码,但由于采用了新的更高效的组织结构,用MKV封装后的电影还是比AVI源文件要小了约1%,这就是说即使加上了多个字幕,MKV文件的体积也不可能比AVI文件大。 MKV支持可变帧率,它可在动态画面中使用较大的帧率,而在静态画面中使用较小的帧率,这样可以有效的减少视频文件的体积,并改善动态画面的质量。它的作用比目前广泛使用的VBR(可变码率)更为明显。 2、avi 可容纳多种类型的音频和视频流,他的封装格式比较老了,在功能上不能像mkv那样满足更多的需求 3、rmvb 是rm的升级版本,vb代表变比特率,意思是在画面平缓的时候采用低比特率,画面变化剧烈的时候采用高比特率,有效降低文件尺寸,又不影响太多画质。一般来说,一个700MB的 DVDrip 采用平均比特率为450Kbps的压缩率,生成的 RMVB 大小仅为400MB,但是画质并没有太大变化。但是由于编码器的关系,在画质上还是略输于h.264,所以现在压缩高清视频时更偏重于使用mkv封装。 4、mp4 视频MP4格式实际上指的是使用MPEG-4编码格式、或使用MPEG-4衍生出来的编码格式进行编码的文件,比如DivX、XviD、H.263、H.264、 MS MPEG-4 3688 、 Microsoft Video1 、Microsoft RLE,此种文件格式功能不如mkv丰富。 5、flv FLV文件体积小巧,清晰的FLV视频1分钟在1MB左右,一部电影在100MB左右,是普通视频文件体积的1/3。再加上CPU占有率低、视频质量良好等特点使其在网络上盛行,目前网上的几家著名视频共享网站均采用FLV格式文件提供视频 6、wmv WMV是微软推出的一种流媒体格式,它是在“同门”的ASF(AdvancedStreamFormat)格式升级延伸来得。在同等视频质量下,WMV格式的文件可以边下载边播放,因此很适合在网上播放和传输。 可是由于微软本身的局限性其WMV的应用发展并不顺利。第一, WM9是微软的产品它必定要依赖着Windows,Windows 意味着解码部分也要有PC,起码要有PC机的主板。这就大大增加了机顶盒的造价,从而影响了视频广播点播的普及。第二,WMV技术的视频传输延迟非常大,通常要10几秒钟,正是由于这种局限性,目前WMV也仅限于在计算机上浏览WM9视频文件。

0
2023-08-02 01:19
电商后台管理系统

异常处理 Spring为了整合全局异常的处理(不用在每个方法下捕获或者抛出异常从而达到处理异常的机制),开发了如下注解: @RestControllerAdvice 定义全局异常的处理类 返回值JSON串 @ExceptionHandler 标识拦截的异常的类型,如果类型匹配,则执行方法 @RestControllerAdvice public class MyExceptionAdvice { //写法:1.运行时异常(通用) 2.自定义异常信息 3.拦截所有异常Exception @ExceptionHandler(RuntimeException.class) public Object exception(Exception e){ //需求: 如果遇到异常,应该提示用户201/失败信息. e.printStackTrace(); return SysResult.fail(); } } 业务返回 满足不同业务需求的返回不同结果,方便前端识别业务执行成功与否,以便执行不同的操作. @Data @Accessors(chain = true) @NoArgsConstructor//无参构造 @AllArgsConstructor//全参构造 public class SysResult implements Serializable {//实现接口是一种规范性写法 private Integer status;//状态码信息200 201 private String msg;//服务器返回的提示信息 private Object data;//返回的业务数据,不清楚是什么类型,就用object //为了用户使用VO对象更加的方便,重载一些方法,简化程序的调用 public static SysResult fail(){ return new SysResult(201,"业务执行失败",null); } //1.不带参数的正确返回 public static SysResult success(){ return new SysResult(200,"业务执行成功",null); } //2.带返回值的正确返回 用户传递什么/返回值就是什么 public static SysResult success(Object data){ return new SysResult(200,"服务器处理成功",data); } //3.带返回值,带提示信息 public static SysResult success(String msg, Object data){ return new SysResult(200,msg,data); } } 用户登录 登录安全问题(token) 为了防止直接跳转网页,用户登录成功后会返回一个token标识,后续跳转页面时,会监测是否带有token标识,有则可以跳转,没有则不能执行相关业务.若后续关闭系统则需要重新登录并获取token标识. 业务实现 业务需求文档 需求:检测用户登录操作 URL: /login 请求类型: Post请求 接收参数: User对象 返回值: Sysresult对象 Controller层: //检测用户登录操作 @PostMapping("/login") public SysResult login(@RequestBody User user){ System.out.println(user); //token用于用户名和密码的校验,防止直接跳转网页 //token 有值则业务正确 null 业务操作失败 String token = userService.login(user); if (token == null){ return SysResult.fail(); } return SysResult.success(token); } Service层: public String login(User user) { String password = user.getPassword();//明文密码,需要加密,利用DigestUtils //byte[] passByte = password.getBytes(); String md5Pass = DigestUtils.md5DigestAsHex(password.getBytes()); //根据用户名和密文查询数据库 //根据对象中不为null的属性充当where条件 user.setPassword(md5Pass); QueryWrapper qw = new QueryWrapper(user); User userDB = userMapper.selectOne(qw); //查询结果是否有效 if (userDB == null){//用户名密码错误 返回null return null; } //用户名密码均正确 返回一个独一无二的token 利用UUID return UUID.randomUUID().toString() .replace("-", ""); } 用户管理 用户列表 列表分页 需求:分页查询用户列表信息 URL:/list?query,pagenum,pagesize 请求类型:get 接收参数:PageResult对象 返回值:SysResult Controller层: /** * 获取用户列表信息 * 参数:PageResult对象进行接收 传递3个参数 * 返回值:SysResult对象 参数(PageResult对象) 返回5个参数 * 我们想要的数据封装在sysResult对象的data属性里,data属性也即PageResult对象, * 前端再根据需要,从data属性里面抽取需要的数据 */ @GetMapping("/list") public SysResult getUserList(PageResult pageResult){ return SysResult.success(userService.getUserList(pageResult)); } Service层: public PageResult getUserList(PageResult pageResult){ //参数1: page分页对象 Page page = new Page(pageResult.getPageNum(),pageResult.getPageSize()); //参数2:分页的查询条件 username模糊查询 注意bug:如果用户没有传递query,模糊查询一样拼接,与理想不符 QueryWrapper qw = new QueryWrapper(); boolean flag = StringUtils.hasLength(pageResult.getQuery()); qw.like(flag,"username",pageResult.getQuery()); //page 原本两个参数 根据分页查询返回 total/分页后的记录records 4个参数 page = userMapper.selectPage(page, qw); List userList = page.getRecords(); long total = page.getTotal(); System.out.println(total); return pageResult.setTotal(total).setRows(userList); } 商品管理 商品列表 列表分页 需求:分页查询商品 URL:/item/getItemList?query,pagenum,pagesize 请求类型:get 接收参数:PageResult对象 返回值:Sysresult Controller层: @GetMapping("/getItemList") public SysResult getItemList(PageResult pageResult){ pageResult = itemService.getItemList(pageResult); return SysResult.success(pageResult); } Service层: public PageResult getItemList(PageResult pageResult) { Page page = new Page(pageResult.getPageNum(),pageResult.getPageSize()); QueryWrapper qw = new QueryWrapper(); //query有值才拼接 boolean flag = StringUtils.hasLength(pageResult.getQuery()); qw.like(flag, "title", pageResult.getQuery()); page = itemMapper.selectPage(page, qw); return pageResult.setTotal(page.getTotal()).setRows(page.getRecords()); } 商品分类 查询商品等级分类信息 需求:按照商品的level等级,查询出对应等级的商品分类信息 URL:/itemCat/findItemCatList/{level} 请求类型:get 接收参数:level 返回值:SysResult对象 Controller层: @GetMapping("/findItemCatList/{level}") public SysResult findItemCatList(@PathVariable Integer level){ List itemCatList = itemCatService.findItemCatList(level); return SysResult.success(itemCatList); } Service层: 方式1:利用map集合封装所有的父子级关系 public List findItemCatList(Integer level) { long start = System.currentTimeMillis(); //map集合封装的是所有父子级关系 Map map = initMap(); //只获取一级,即parend_id=0 if (level == 1) return map.get(0); //获取二级 1/2 if (level == 2) return getLevel2List(map); //获取1/2/3级 else return getLevel3List(map); } /** * 1.封装Map集合 Map

0
2023-08-02 01:15
云短信

一:什么是短信路由 短信路由是指当短信发送时,不通的关键字内容,不同的地区进行线路分流。实现压缩成本速度和质量保证。 二:扣量功能的作用 主要用于客户端用户提交短信时按照设置好的比例进行扣量 三:可设置不同用户扣量比例和规则 可通过短信后台设置不同短信客户的扣量比例和扣量规则 四:用户管理模块的整体作用 通过后台管理整个短信平台的客户 管理内容有1:开户 (普通用户,代理商用户)。2:删除用户,锁定用户,充值。 3:用户审核设置。4:用户通道设置。5:用户通道分流。用户扣量设置。 五:短信路由流程 这里说下我们在开发实践过程中实现短信路由的流程 1:设置账号的全网通道。2:设置通道可发的地区。3:设置内容关键字的通道 流程: 1.1:当客户端账号没开通通道路由 当某个账号提交短信后,系统先判断此账号是否开启了通道路由。如果账号没有开启通道路由那么发送的*和内容直接通过全网通道直接发出。 1.2:当客户端账号开通通道路由 且内容中有关键词的 当账号提交短信后,系统判断如果此账号开启了通道路由。如果当前提交的内容中有符合关键词的通道,则当前内容不管什么地区的*都会通过此通道发出。 1.3:当客户端账号开通通道路由 且内容中没有关键词的 当账号提交短信后,系统判断如果此账号开启了通道路由并且提交的内容中没有符合关键词通道通道或者是后台没有设置关键词通道。那么当前提交的*则按照地区进行发送,没有地区的则按照全网通道发送。

0
2023-08-02 01:12
更新于: 2023-08-02 浏览: 152