视频在线点播功能如何实现?

news2025/1/18 17:03:37

1. 视频点播需求分析

1.1 需求描述

视频点播需求如下:

1、学生可以在windows浏览器上在线观看视频。

2、播放器具有快进、快退、暂停等基本功能。

1.2 视频点播解决方案

1.2.1 流媒体

详细参考:https://baike.baidu.com/item/%E6%B5%81%E5%AA%92%E4%BD%93/98740?fr=aladdin

概括理解:流媒体就是将视频文件分成许多小块儿,将这些小块儿作为数据包通过网络发送出去,实现一边传输视频 数据 包一边观看视频。

流式传输

在网络上传输音、视频信息有两个方式:下载和流式传输。

下载:就是把音、视频文件完全下载到本机后开始播放,它的特点是必须等到视频文件下载完成方可播放,播放等待时间较长,无法去播放还未下载的部分视频。

流式传输:就是客户端通过链接视频服务器实时传输音、视频信息,实现“边下载边播放”。

流式传输包括如下两种方式:

1) 顺序流式传输

即顺序下载音、视频文件,可以实现边下载边播放,不过,用户只能观看已下载的视频内容,无法快进到未下载的视频部分,顺序流式传输可以使用Http服务器来实现,比如Nginx、Apache等。

2)实时流式传输

实时流式传输可以解决顺序流式传输无法快进的问题,它与Http流式传输不同,它必须使用流媒体服务器并且使用流媒体协议来传输视频,它比Http流式传输复杂。常见的实时流式传输协议有RTSP、RTMP、RSVP等。

流媒体系统的概要结构

通过流媒体系统的概要结构学习流媒体系统的基本业务流程。

1、将原始的视频文件通过编码器转换为适合网络传输的流格式,编码后的视频直接输送给媒体服务器。

原始的视频文件通常是事先录制好的视频,比如通过摄像机、摄像头等录像、录音设备采集到的音视频文件,体积较大,要想在网络上传输需要经过压缩处理,即通过编码器进行编码 。

2、媒体服务获取到编码好的视频文件,对外提供流媒体数据传输接口,接口协议包括 :HTTP、RTSP、RTMP等

3、播放器通过流媒体协议与媒体服务器通信,获取视频数据,播放视频。

1.2.2 点播方案

本项目包括点播和直播两种方式,我们先调研点播的方案,如下:

1、 播放器通过 http协议从http服务器上下载视频文件进行播放

问题:必须等到视频下载完才可以播放,不支持快进到某个时间点进行播放

2、 播放器通过rtmp协议连接媒体服务器以实时流方式播放视频

使用rtmp协议需要架设媒体服务器,造价高,对于直播多采用此方案。

3、 播放器使用HLS协议连接http服务器(Nginx、Apache等)实现近实时流方式播放视频

HLS协议规定:基于Http协议,视频封装格式为ts,视频的编码格式为H264,音频编码格式为MP3、AAC或者AC-3

HLS是什么 ?

HLS的工作方式是:将视频拆分成若干ts格式的小文件,通过m3u8格式的索引文件对这些ts小文件建立索引。一般10秒一个ts文件,播放器连接m3u8文件播放,当快进时通过m3u8即可找到对应的索引文件,并去下载对应的ts文件,从而实现快进、快退以近实时 的方式播放视频。

IOS、Android设备、及各大浏览器都支持HLS协议。

详细参考:https://baike.baidu.com/item/HLS/8328931?fr=aladdin

采用HLS方案即可实现边下载边播放,可以不用使用rtmp等流媒体协议,不用构建专用的媒体服务器,节省成本。

本项目点播方案确定为方案3。

2 FFmpeg 的基本使用

我们将视频录制完成后,使用视频编码软件对视频进行编码,本项目 使用FFmpeg对视频进行编码 。

下载 :ffmpeg-20180227-fa0c9d6-win64-static.zip,并解压,本教程将ffmpeg解压到了C:\Java_Soft\xczx\ffmpeg-20180227-fa0c9d6-win64-static下。

FFmpeg被许多开源项目采用,QQ影音、暴风影音、VLC等。

下载:FFmpeg https://www.ffmpeg.org/download.html#build-windows

将C:\Java_Soft\xczx\ffmpeg-20180227-fa0c9d6-win64-static\bin目录配置在path环境变量中。

简单的测试:

将一个.avi文件转成mp4、mp3、gif等。

比如我们将lucene.avi文件转成mp4,运行如下命令:

ffmpeg -i lucene.avi lucene.mp4

转成mp3:ffmpeg -i lucene.avi lucene.mp3

转成gif:ffmpeg -i lucene.avi lucene.gif

官方文档(英文):http://ffmpeg.org/ffmpeg.html

2.1 生成m3u8/ts文件

使用ffmpeg生成 m3u8的步骤如下:

第一步:先将avi视频转成mp4

ffmpeg.exe -i  lucene.avi -c:v libx264 -s 1280x720 -pix_fmt yuv420p -b:a 63k -b:v 753k -r 18.\lucene.mp4

下面把各参数意思大概讲讲,大概了解意思即可,不再此展开流媒体专业知识的讲解。

-c:v 视频编码为x264 ,x264编码是H264的一种开源编码格式。

-s 设置分辨率

-pix_fmt yuv420p:设置像素采样方式,主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0,它的作用是根据采样方式来从码流中还原每个像素点的YUV(亮度信息与色彩信息)值。

-b 设置码率,-b:a和-b:v分别表示音频的码率和视频的码率,-b表示音频加视频的总码率。码率对一个视频质量有很大的作用。

-r:帧率,表示每秒更新图像画面的次数,通常大于24肉眼就没有连贯与停顿的感觉了。

第二步:将mp4生成m3u8

ffmpeg -i  lucene.mp4   -hls_time 10-hls_list_size 0-hls_segment_filename ./hls/lucene_%05d.ts ./hls/lucene.m3u8

-hls_time 设置每片的长度,单位为秒

-hls_list_size n: 保存的分片的数量,设置为0表示保存所有分片

-hls_segment_filename :段文件的名称,%05d表示5位数字

生成的效果是:将lucene.mp4视频文件每10秒生成一个ts文件,最后生成一个m3u8文件,m3u8文件是ts的索引文件。

使用VLC打开m3u8文件,测试播放效果,VLC 是一款自由、开源的跨平台多媒体播放器及框架,可播放大多数多媒体文件,以及 DVD、音频 CD、VCD 及各类流媒体协议。(http://www.videolan.org/)

3. 视频(媒资)处理

开发环境

1. 创建媒资数据库

导入shcool.sql

2. 创建媒资服务工程

基于springboot创建工程

3. 上传文件

3.1 断点续传解决方案

通常视频文件都比较大,所以对于媒资系统上传文件的需求要满足大文件的上传要求。http协议本身对上传文件大小没有限制,但是客户的网络环境质量、电脑硬件环境等参差不齐,如果一个大文件快上传完了网断了,电断了没有上传完成,需要客户重新上传,这是致命的,所以对于大文件上传的要求最基本的是断点续传。

什么是断点续传:

引用百度百科:断点续传指的是在下载或上传时,将下载或上传任务(一个文件或一个压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载,断点续传可以提高节省操作时间,提高用户体验性。

上传流程如下:

1、上传前先把文件分成块

2、一块一块的上传,上传中断后重新上传,已上传的分块则不用再上传

3、各分块上传完成最后合并文件

文件下载则同理。

3.2 文件分块与合并

为了更好的理解文件分块上传的原理,下边用java代码测试文件的分块与合并。

3.3 文件分块

文件分块的流程如下:

1、获取源文件长度

2、根据设定的分块文件的大小计算出块数

3、从源文件读数据依次向每一个块文件写数据

//测试文件分块方法@TestpublicvoidtestChunk()throwsIOException{File sourceFile =newFile("F:/develop/ffmpeg/lucene.mp4");//        File sourceFile = new File("d:/logo.png");String chunkPath ="F:/develop/ffmpeg/chunk/";File chunkFolder =newFile(chunkPath);if(!chunkFolder.exists()){
        chunkFolder.mkdirs();}//分块大小long chunkSize =1024*1024*1;//分块数量long chunkNum =(long)Math.ceil(sourceFile.length()*1.0/ chunkSize );if(chunkNum<=0){
        chunkNum =1;}//缓冲区大小byte[] b =newbyte[1024];//使用RandomAccessFile访问文件RandomAccessFile raf_read =newRandomAccessFile(sourceFile,"r");//分块for(int i=0;i
        //创建分块文件File file =newFile(chunkPath+i);boolean newFile = file.createNewFile();if(newFile){//向分块文件中写数据RandomAccessFile raf_write =newRandomAccessFile(file,"rw");int len =-1;while((len = raf_read.read(b))!=-1){
                raf_write.write(b,0,len);if(file.length()>chunkSize){break;}}
            raf_write.close();}}
    raf_read.close();}

3.4 文件合并

文件合并流程:

1、找到要合并的文件并按文件合并的先后进行排序

2、创建合并文件

3、依次从合并的文件中读取数据向合并文件写入数据

//测试文件合并方法@TestpublicvoidtestMerge()throwsIOException{//块文件目录File chunkFolder =newFile("F:/develop/ffmpeg/chunk/");//合并文件File mergeFile =newFile("F:/develop/ffmpeg/lucene1.mp4");if(mergeFile.exists()){
        mergeFile.delete();}//创建新的合并文件
    mergeFile.createNewFile();//用于写文件RandomAccessFile raf_write =newRandomAccessFile(mergeFile,"rw");//指针指向文件顶端
    raf_write.seek(0);//缓冲区byte[] b =newbyte[1024];//分块列表File[] fileArray = chunkFolder.listFiles();// 转成集合,便于排序List<File> fileList =newArrayList<File>(Arrays.asList(fileArray));// 从小到大排序Collections.sort(fileList,newComparator<File>(){@Overridepublicintcompare(File o1,File o2){if(Integer.parseInt(o1.getName())<Integer.parseInt(o2.getName())){return-1;}return1;}});//合并文件for(File chunkFile:fileList){RandomAccessFile raf_read =newRandomAccessFile(chunkFile,"rw");int len =-1;while((len=raf_read.read(b))!=-1){
            raf_write.write(b,0,len);}
        raf_read.close();}
    raf_write.close();}

4. 前端页面

WebUploader介绍

如何在web页面实现断点续传?

常见的方案有:

1、通过Flash上传,比如SWFupload、Uploadify。

2、安装浏览器插件,变相的pc客户端,用的比较少。

3、Html5

随着html5的流行,本项目采用Html5完成文件分块上传。

本项目使用WebUploader完成大文件上传功能的开发,WebUploader官网地址:http://fexteam.gz01.bdysite.com/webuploader/

使用WebUploader上传流程如下:

钩子方法

在webuploader中提供很多钩子方法,下边列出一些重要的:

本项目使用如下钩子方法:

1)before-send-file

在开始对文件分块儿之前调用,可以做一些上传文件前的准备工作,比如检查文件目录是否创建完成等。

2)before-send

在上传文件分块之前调用此方法,可以请求服务端检查分块是否存在,如果已存在则此分块儿不再上传。

3)after-send-file

在所有分块上传完成后触发,可以请求服务端合并分块文件。

注册钩子方法源代码:

WebUploader.Uploader.register({"before-send-file":"beforeSendFile","before-send":"beforeSend","after-send-file":"afterSendFile"}

构建WebUploader

使用webUploader前需要创建webUploader对象。

指定上传分块的地址:/api/media/upload/uploadchunk

// 创建uploader对象,配置参数this.uploader =WebUploader.create({
    swf:"/static/plugins/webuploader/dist/Uploader.swf",//上传文件的flash文件,浏览器不支持h5时启动flash
    server:"/api/media/upload/uploadchunk",//上传分块的服务端地址,注意跨域问题
    fileVal:"file",//文件上传域的name
    pick:"#picker",//指定选择文件的按钮容器
    auto:false,//手动触发上传
    disableGlobalDnd:true,//禁掉整个页面的拖拽功能
    chunked:true,// 是否分块上传
    chunkSize:1*1024*1024,// 分块大小(默认5M)
    threads:3,// 开启多个线程(默认3个)
    prepareNextFile:true// 允许在文件传输时提前把下一个文件准备好})

before-send-file

文件开始上传前前端请求服务端准备上传工作。

type:"POST",
url:"/api/media/upload/register",
data:{// 文件唯一表示
  fileMd5:this.fileMd5,
  fileName: file.name,
  fileSize:file.size,
  mimetype:file.type,
  fileExt:file.ext
}

before-send

上传分块前前端请求服务端校验分块是否存在。

type:"POST",
url:"/api/media/upload/checkchunk",
data:{// 文件唯一表示
  fileMd5:this.fileMd5,// 当前分块下标
  chunk:block.chunk,// 当前分块大小
  chunkSize:block.end-block.start
}

after-send-file

在所有分块上传完成后触发,可以请求服务端合并分块文件。

type:"POST",
url:"/api/media/upload/mergechunks",
data:{
  fileMd5:this.fileMd5,
  fileName: file.name,
  fileSize:file.size,
  mimetype:file.type,
  fileExt:file.ext
}

页面效果

学员使用:

直接解压资料xc-ui-pc-teach到webstorm工作目录

通过nginx访问,在nginx中配置 (解决跨域)

server {
  listen       82;     
  server_name localhost;     
  
  #视频中心     
  location /{    
    proxy_pass http://127.0.0.1:12000; 
    proxy_set_header Host $http_host;   
    add_header Access-Control-Allow-Origin*;         
    add_header Access-Control-Allow-Credentialstrue;           
    add_header Access-Control-Allow-Methods GET;} 
  #媒资管理后台跨域     
  location ^~/api/media/{       
    proxy_pass http://127.0.0.1:9000/media/; 
    proxy_set_header Host $http_host;  
    add_header Access-Control-Allow-Origin*;         
    add_header Access-Control-Allow-Credentialstrue;           
    add_header Access-Control-Allow-Methods"GET,POST,OPTIONS";}}

可以访问 http://127.0.0.1:12000/#/media/upload 或者 http://localhost:82/#/media/upload/ 查看页面效果。

5. 媒资服务端编写

服务端需要实现如下功能:

1、上传前检查上传环境

检查文件是否上传,已上传则直接返回。

检查文件上传路径是否存在,不存在则创建。

2、分块检查

检查分块文件是否上传,已上传则返回true。

未上传则检查上传路径是否存在,不存在则创建。

3、分块上传

将分块文件上传到指定的路径。

4、合并分块

将所有分块文件合并为一个文件。

在数据库记录文件信息。

由于上传过程复杂,开发时按业务流程分别实现。

配置

application.yml配置上传文件的路径:

xc-service-manage-media:
  upload-location:C:/school/video/  # 媒资保存路径
  ffmpeg-path:C:/Java_Soft/xczx/ffmpeg-20180227-fa0c9d6-win64-static/bin/ffmpeg.exe # ffmpeg路径

定义Dao

使用mybatis-plus

@Mapper@ComponentpublicinterfaceFileMsgMapperextendsBaseMapper<FileMsg>{}

定义controller

@RestController@RequestMapping("/media/upload")publicclassMediaUploadController{@AutowiredMediaUploadService mediaUploadService;/**
     * 文件上传前的注册
     */@PostMapping("/register")publicResponseResultregister(String fileMd5,String fileName,Long fileSize,String mimetype,String fileExt){return mediaUploadService.register(fileMd5, fileName, fileSize, mimetype, fileExt);}/**
     * 检测分块
     *
     * @paramfileMd5
     * @paramchunk
     * @paramchunkSize
     * @return
     */@PostMapping("/checkchunk")publicResponseResultcheckchunk(String fileMd5,Integer chunk,Integer chunkSize){return mediaUploadService.checkchunk(fileMd5, chunk, chunkSize);}/**
     * 上传分块
     *
     * @paramfile
     * @paramfileMd5
     * @paramchunk
     * @return
     */@PostMapping("/uploadchunk")publicResponseResultuploadchunk(MultipartFile file,String fileMd5,Integer chunk){return mediaUploadService.uploadchunk(file, fileMd5, chunk);}/**
     * 合并分块
     *
     * @paramfileMd5
     * @paramfileName
     * @paramfileSize
     * @parammimetype
     * @paramfileExt
     * @return
     */@PostMapping("/mergechunks")publicResponseResultmergechunks(String fileMd5,String fileName,Long fileSize,String mimetype,String fileExt){return mediaUploadService.mergechunks(fileMd5, fileName, fileSize, mimetype, fileExt);}}

定义service (略)由于代码过多,请参见源码。

现在视频已经通过断点续传的方式,上传到了我们的媒资服务器, 接下来就要使用FFmpeg将视频转换成流媒体。

6.视频处理技术方案

如何通过程序进行视频处理?

ffmpeg是一个可行的视频处理程序,可以通过Java调用ffmpeg.exe完成视频处理。

在java中可以使用Runtime类和Process Builder类两种方式来执行外部程序,工作中至少掌握一种。

本项目使用Process Builder的方式来调用ffmpeg完成视频处理。

关于Process Builder的测试如下 :

@TestpublicvoidtestProcessBuilder(){ProcessBuilder processBuilder =newProcessBuilder();//       processBuilder.command("ping","127.0.0.1");
       processBuilder.command("ipconfig");//将标准输入流和错误输入流合并,通过标准输入流读取信息
        processBuilder.redirectErrorStream(true);try{//启动进程Process start = processBuilder.start();//获取输入流InputStream inputStream = start.getInputStream();//转成字符输入流InputStreamReader inputStreamReader =newInputStreamReader(inputStream,"gbk");int len =-1;char[] c =newchar[1024];StringBuffer outputString =newStringBuffer();//读取进程输入流中的内容while((len= inputStreamReader.read(c))!=-1){String s =newString(c,0,len);
                outputString.append(s);System.out.print(s);}
            inputStream.close();}catch(IOException e){
            e.printStackTrace();}}@TestpublicvoidtestFFmpeg(){ProcessBuilder processBuilder =newProcessBuilder();//定义命令内容List<String> command =newArrayList<>();
        command.add("D:\\Program Files\\ffmpeg-20180227-fa0c9d6-win64-static\\bin\\ffmpeg.exe");
        command.add("-i");
        command.add("E:\\ffmpeg_test\\1.avi");
        command.add("-y");//覆盖输出文件
        command.add("-c:v");
        command.add("libx264");
        command.add("-s");
        command.add("1280x720");
        command.add("-pix_fmt");
        command.add("yuv420p");
        command.add("-b:a");
        command.add("63k");
        command.add("-b:v");
        command.add("753k");
        command.add("-r");
        command.add("18");
        command.add("E:\\ffmpeg_test\\1.mp4");
        processBuilder.command(command);//将标准输入流和错误输入流合并,通过标准输入流读取信息
        processBuilder.redirectErrorStream(true);try{//启动进程Process start = processBuilder.start();//获取输入流InputStream inputStream = start.getInputStream();//转成字符输入流InputStreamReader inputStreamReader =newInputStreamReader(inputStream,"gbk");int len =-1;char[] c =newchar[1024];StringBuffer outputString =newStringBuffer();//读取进程输入流中的内容while((len= inputStreamReader.read(c))!=-1){String s =newString(c,0,len);
                outputString.append(s);System.out.print(s);}
            inputStream.close();}catch(IOException e){
            e.printStackTrace();}}

我们已经准备好了工具类:

Mp4VideoUtil.java完成avi转mp4

HlsVideoUtil.java完成mp4转hls

可以直接使用。

6.1 处理流程

当视频合并成功后

把视频处理成m3u8流媒体

把视频信息写到数据库

具体代码: 在合并完成方法中调用

@Value("${xc-service-manage-media.ffmpeg-path}")String ffmpeg_path;privatevoidChangeHLS(File mergeFile,String fileMd5){// mp4 文件保存目录String fileFolderPath =getFileFolderPath(fileMd5);//生成的mp4的文件名称String mp4_name = fileMd5 +".mp4";//生成的mp4所在的路径String mp4folder_path = fileFolderPath;//创建工具类对象Mp4VideoUtil mp4VideoUtil =newMp4VideoUtil(ffmpeg_path, mergeFile.getAbsolutePath(), mp4_name, mp4folder_path);//进行处理String result = mp4VideoUtil.generateMp4();//4、将mp4生成m3u8和ts文件//String ffmpeg_path, String video_path, String m3u8_name,String m3u8folder_path//mp4视频文件路径String mp4_video_path = mp4folder_path + mp4_name;//m3u8_name文件名称String m3u8_name = fileMd5 +".m3u8";//m3u8文件所在目录String m3u8folder_path = fileFolderPath +"hls/";HlsVideoUtil hlsVideoUtil =newHlsVideoUtil(ffmpeg_path, mp4_video_path, m3u8_name, m3u8folder_path);//生成m3u8和ts文件String tsResult = hlsVideoUtil.generateM3u8();//保存fileUrl(此url就是视频播放的相对路径)String filePath = fileFolderPath +"hls/"+ m3u8_name;// 将fileUrl 保存到数据库FileMsg fileMsg =newFileMsg();
        fileMsg.setFileId(fileMd5);
        fileMsg.setFileName(mergeFile.getName());
        fileMsg.setFilePath(filePath);
        fileMsg.setFileUrl(filePath.split("C:/school/video")[1]);
        fileMsgMapper.insert(fileMsg);//优化:  文件处理成功后, 可以删除mp4文件}

说明:

mp4转成m3u8如何判断转换成功?

第一、根据视频时长来判断,同mp4转换成功的判断方法。

第二、最后还要判断m3u8文件内容是否完整。

4 播放器

4.1 技术选型

视频编码后要使用播放器对其进行解码、播放视频内容。在web应用中常用的播放器有flash播放器、H5播放器或浏览器插件播放器,其中以flash和H5播放器最常见。

flash播放器:缺点是需要在客户机安装Adobe Flash Player播放器,优点是flash播放器已经很成熟了,并且浏览器对flash支持也很好。

H5播放器:基于h5自带video标签进行构建,优点是大部分浏览器支持H5,不用再安装第三方的flash播放器,并且随着前端技术的发展,h5技术会越来越成熟。

本项目采用H5播放器,使用Video.js开源播放器。

Video.js是一款基于HTML5世界的网络视频播放器。它支持HTML5和Flash视频,它支持在台式机和移动设备上播放视频。这个项目于2010年中开始,目前已在40万网站使用。

官方地址:http://videojs.com/

4.2 下载video.js

Video.js: https://github.com/videojs/video.js

videojs-contrib-hls: https://github.com/videojs/videojs-contrib-hls#installation

(videojs-contrib-hls是播放hls的一个插件)

使用文档:http://docs.videojs.com/tutorial-videojs_.html

本教程使用 video.js 6.7.3 版本,videojs-contrib-hls 5.14.1版本

将资料中提供的plugins赋值到xc-ui-pc-video项目目录下

4.3 搭建媒体服务器

正常使用video.js播放视频是通过一个网页,用户通过浏览器打开网页去播放视频,网页和视频都从web服务器请求。

4.3.1 Nginx媒体服务器

根据上边的流程,我们在媒体服务器上安装Nginx,并配置如下 :

# 视频静态资源
  location /{    
      alias   C:/CODE/JAVA/school/xc-ui-pc-video/;
      index  index.html index.htm;}}

4.4 测试video.js

1.把资料中的video.html 复制到 C:/CODE/JAVA/school/xc-ui-pc-video/ 如图:

上面已经使用nginx 代理 C:/CODE/JAVA/school/xc-ui-pc-video/ 目录。 所以可以通过http请求直接访问video.html页面

把src路径修改为自己的媒资访问路径

测试:

用户打开浏览器输入 http://localhost:81/video.html 。即可打开视频播放页面,并播放。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/161762.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年网络安全比赛--网络安全应急响应中职组(超详细)

一、竞赛时间 180分钟 共计3小时 二、竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 1.找出被黑客修改的系统别名,并将倒数第二个别名作为Flag值提交; 2.找出系统中被植入的后门用户删除掉,并将后门用户的账号作为Flag值提交(多个用户名之间以英文逗号分割,如:admi…

【项目】小帽教育(一)

小帽教育 第一章 项目开发环境搭建 一、项目背景 1.在线教育市场环境 在线教育行业是一个有着极强的广度和深度的行业&#xff0c;从校内到校外&#xff1b;从早幼教到职业培训&#xff1b;从教育工具到全信息化平台等等。 2020年的新冠疫情外生冲击&#xff0c;让在线教育…

Android 深入系统完全讲解(14)

2 如何定位编译问题 编译不出来&#xff0c;编译不生效&#xff0c;都是可以用比对的方式去处理。之前遇见只改资源单编译不生效&#xff0c;跟正常的一比较发现是系统编译机制是判断代码的时间戳&#xff0c;而不管资源目录&#xff0c;于是处理的方式就是单编配置强制&#…

过年了,我采集采集m3u8格式做做动态壁纸不过分吧

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 首先澄清一下&#xff0c;我用Python收集这些视频&#xff0c;绝不是想做别的什么&#xff0c; 真的只是用来做动态壁纸&#xff0c;大家不要误会&#xff01;我不是那样的人~ 这样的不过份吧 &#x1f63b; 环境 Python…

二叉树的深度 | 分治 +回溯 + 迭代

二叉树的最大深度 leetcode https://leetcode.cn/problems/maximum-depth-of-binary-tree/ 回溯基本思路 二叉树的最大深度就是 : 从根节点到叶子节点的最大的深度 定义一个全局变量 int res 0 , 以及局部变量 depth 表示当前层的深度当我们访问到根节点的时候(前序位置), 就…

亿信华辰出席2022DAMA中国数据管理峰会,共话数据治理

2023年1月7日-8日&#xff0c;万众瞩目的2022DAMA中国数据管理峰会在上海成功举办&#xff0c;国内外知名行业领袖、业内优秀企业代表出席了会议。会上&#xff0c;围绕 “数据基础制度和数字化转型”主题&#xff0c;前沿观点高能碰撞、专家学者共话未来。亿信华辰首席运营官毛…

CTP开发(1)CTP开发的概述

CTP是上海期货交易所开发的一套程序化交易的API。这篇文章呢&#xff0c;是进行扫盲和引路的&#xff0c;让你在开发时可以少走弯路。1、如果你要进行CTP开发&#xff0c;那么先是在模拟环境下调试&#xff0c;这就需要去上期的仿真模拟网站simnow下载相应的API包。地址如下&am…

[数字信号处理]时域中的离散时间信号和系统

离散时间信号最基本的形式是定义在等间隔的时间离散值上&#xff0c;且在这些离散时间上的信号振幅值是连续的。离散时间系统则是输入为离散时间信号输出也是离散时间信号的系统。对于线性时不变&#xff08;LTI&#xff09;系统来说&#xff0c;我们可以发现LTI系统与该系统的…

【4】Kubesphere_多租户

目录 创建企业空间角色 邀请新成员 部门管理 准备工作 创建部门 分配用户至部门 从部门中移除用户 删除和编辑部门 进入访问控制 可以配置企业空间、用户和用户角色 企业空间角色页面列出了以下四个可用的内置角色。创建企业空间时&#xff0c;KubeSphere 会自动创建内…

对称加密面临的那些挑战

密码对于多数人&#xff0c;是既熟悉又陌生的存在&#xff0c;注册账户、登录账号、网购&#xff0c;网络上的开展大多数行为都需要基于输入密码这一前提&#xff0c;从这方面谈&#xff0c;我们熟悉且熟知密码。然而这些由简单的字母、数字、符号等构成的简单口令&#xff0c;…

Spring Data默认值的错误

Spring Data有很多配置的默认值&#xff0c;但不一定都适合你。如一个依赖Cassandra 的项目&#xff0c;有时写入数据后&#xff0c;并不能立马读到。这种错误并没有什么报错&#xff0c;一切都是正常的&#xff0c;就是读不到数据。 1 源码解析 直接使用 Spring Data Cassan…

化繁为简,数字化推动企业数据库升级焕新 | 爱分析报告

报告编委 黄勇 爱分析合伙人&首席分析师 洪逸群 爱分析高级分析师 张良筠 爱分析分析师 外部专家&#xff08;按姓氏拼音排序&#xff09; 李远志 柏睿数据 副总裁 谢寅 镜舟科技 资深解决方案架构师 许哲 中信建投证券 数据组VP 姚延栋 YMatrix 创始人&#xff06;CEO 张晨…

实体店店铺管理软件应该怎么挑?有这几个功能的错不了!

现在开实体店的都是勇士&#xff0c;不仅要和同行竞争&#xff0c;还要和电商、直播竞争&#xff0c;没有点秘密武器根本不能生存。秘密武器之一&#xff0c;就是在做生意路上摸爬滚打多年的实体店老板们都在用的——实体店店铺管理软件&#xff01;它会帮助你从手工记账迈入智…

【敲敲云】零代码平台快速入门指南 — 下篇

敲敲云快速入门指南—上篇&#xff0c;我们已经介绍了工作表、视图的使用&#xff0c;现在我们再来看一下自动化工作流和仪表盘的使用 6、创建自动化工作流 工作流是由触发器和若干自动化任务节点组成。为触发器设置一个触发条件&#xff0c;当数据的变更符合触发条件时&#…

Linux系统中的文件查看和查询命令

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;Java案例分…

向“全栈”进发,大型线上商城实战项目,Spring Boot + Vue 前后端分离版本的商城来了(文末有视频)

新蜂商城 Vue 版本&#xff0c;它来了&#xff01;&#xff08;文末有视频&#xff09; 如上图所示&#xff0c;新蜂商城 Vue 版本已经开发完成&#xff0c;这是新蜂商城开源项目的第一个大版本更新&#xff0c;根据大量的用户调研&#xff0c;最终决定将新蜂商城升级为 Spring…

DC-2 靶场实操

靶场搭建 ​ 靶场部署 下载网址&#xff1a;https://www.vulnhub.com/?qDC- tips&#xff1a;强烈建议各位师傅们使用迅雷或idm要不然下载靶场将是一个十分痛苦的环节 之后环境搭建操作步骤一致故此处搭建环境以DC-1为例 将下好的压缩包解压&#xff0c;你就会看到一个.ov…

堆排序(升序降序)

堆排序是利用堆这种数据结构而设计的一种排序算法&#xff0c;堆排序是一种选择排序&#xff0c;它的最坏、最好、平均时间复杂度均为O(nlogn)&#xff0c;是不稳定排序。 小根堆&#xff08;最小堆&#xff09;&#xff1a;每个结点的值都<其左右孩子结点的值。 大根堆&…

跨境电商独立站多语言,fecify saas商城解决方案

跨境独立站&#xff0c;面向的是全球国家&#xff0c;每个国家都有自己的语言&#xff0c;譬如&#xff1a;英语&#xff0c;德语&#xff0c;法语&#xff0c;俄语&#xff0c;西班牙语&#xff0c;葡萄牙语&#xff0c;韩语&#xff0c;日语&#xff0c;阿拉伯语&#xff0c;…

Latex-表格和图片

双栏排版表格跨栏加*号\begin{table*}...\end{table*}表格整体尺寸修改\resizebox{列宽}{行高}{...}列宽、行高可以为数值&#xff08;如50mm&#xff09;&#xff0c;也可以根据文字调整&#xff08;如\textwidth指自适应文字宽度&#xff09;表格内文字居中\begin{tabular}{c…