二.音视频编辑-媒体组合-播放
引言
当涉及到音视频编辑时,媒体资源的提取和组合是至关重要的环节。在iOS平台上,AVFoundation框架提供了丰富而强大的功能,使得媒体资源的操作变得轻松而高效。从原始的媒体中提取片段,然后将它们巧妙地组合成一个完整的作品,这是音视频编辑过程中的常见任务之一。在这篇博客中,我们将深入探讨iOS AVFoundation框架中的媒体组合功能,探索其如何为开发者提供丰富的工具和技术,帮助他们实现创意无限的音视频编辑项目。
概述
上图是关于媒体功能中的核心类,以及类直接的关系图。有关资源组合的功能就源于AVAsset的子类AVComposition。一个组合就是将多种媒体资源组合成一个自定义的临时排列,再将这个临时排列视为一个可呈现的独立媒体项目。就比如AVAsset对象,组合相当于包含了一个或多个给定类型的媒体轨道的容器。AVComposition中的轨道都是AVAssetTrack的子类AVCompositionTrack。一个组合轨道本身由一个或多个媒体片段组成,由AVCompositionTrackSegment类定义,代表这个组合中的实际媒体区域。
组合后的对象关系如下:
AVComposition和AVCompositionTrack都是不可变对象,提供对资源的只读操作。这些对象提供了一个合适的接口让应用程序的一部分可以进行播放或处理。不过,当创建自己的组合时,就需要使用AVMutableComposition和AVMutableCompositionTrack所提供的可变子类。这些对象提供的类接口需要操作轨道和轨道分段,这样我们就可以创建所需的临时排列了。
基础方法
这个基础的实例会将两个视频片段中的前5秒内容提取出来,并按照组合视频轨道的顺序进行排序。还会从MP3文件中奖音频轨道整合到视频中,期间会用到Core Media框架中定义的CMTime数据类型作为时间格式,相关内容可以查看其它博客。
let composition = AVMutableComposition()var videoTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)!var audioTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
上面的示例创建了一个AVMutableComposition并用它的addMutableTrackWithMediaType:preferredTrackID:方法添加了两个轨道对象。当创建组合轨道时,开发者必须指明它所能支持的媒体类型,并给出一个轨道标识符。设置preferredTrackID:参数为CMPersistentTrackID,这是一个32位的整数值。虽然我们可以传递任意标识符作为参数,这个标识符在我们之后需要返回轨道时会用到,不过一般来说都是赋给它一个kCMPersistentTrackID_Invalid常量。这个有着奇怪名字的常量的意思是我们需要创建一个合适轨道ID的任务委托给框架,标识符会以1..n排列。
现在我们已经实现了一个组合资源:
下一步就是将独立的媒体片段插入到组合的轨道中。
//1.创建资源let goldenGateAsset = AVURLAsset(url: URL(string: "1")!, options: nil)let teaGardenAsset = AVURLAsset(url: URL(string: "2")!, options: nil)let soundTrackAsset = AVURLAsset(url: URL(string: "3")!, options: nil)//2.定义插入点var cursorTime = CMTime.zero//3.定义片段时长let videoDuration = CMTime(value: 5, timescale: 1)let videoTimeRange = CMTimeRange(start: cursorTime, duration: videoDuration)//4.提取资源中的视频轨道并插入到组合中的视频轨道let goldenGateAssetTrack = goldenGateAsset.tracks(withMediaType: .video).first!do {try videoTrack.insertTimeRange(videoTimeRange, of: goldenGateAssetTrack, at: cursorTime)} catch {print("Error inserting time range: \(error)")}//5.调整插入时间cursorTime = CMTimeAdd(cursorTime, videoDuration) //6.提取资源中的视频轨道并插入到组合中的视频轨道let teaGardenAssetTrack = teaGardenAsset.tracks(withMediaType: .video).first!do {try videoTrack.insertTimeRange(videoTimeRange, of: teaGardenAssetTrack, at: cursorTime)} catch {print("Error inserting time range: \(error)")}//7.调整插入时间和时长cursorTime = CMTime.zerolet audioDuration = composition.durationlet audioTimeRange = CMTimeRangeMake(start: cursorTime, duration: audioDuration)//8.提取音频轨道并插入到组合中的音频轨道let soundTrackAssetTrack = soundTrackAsset.tracks(withMediaType: .audio).first!do {try audioTrack?.insertTimeRange(audioTimeRange, of: soundTrackAssetTrack, at: cursorTime)} catch {print("Error inserting time range: \(error)")}
- 首先我们创建了3个AVAsset资源,当然这里面是模拟创建的,其中前2个表示视频,第3个表示音频。
- 定义了资源的插入时间点。
- 定义每个视频片段资源的插入时长。
- 提取第1个视频资源的视频轨道,默认视频资源只有一个视频轨道,插入到组合的视频轨道。
- 调整下一个视频资源的插入时间为上一个视频资源的结束时间点。
- 同样获取第2个视频资源的视频轨道,插入到组合的视频轨道。
- 调整插入时间为0,并设置音频的时长。
- 提取音频资源的音频轨道并插入到组合的音频轨道中。
这样我们的组合就构建完成了:
使用示例
下面我们将着色创建一个视频编辑的应用程序,接下来的博客也将围绕这个程序不断的添加和完善视频编辑的功能。
项目介绍
应用程序将包含两个不同的部分,一个是视频播放器,我们只需在之前博客的视频播放器中稍作改动,一个是可以选择媒体和允许媒体排列组合的视频编辑部分,重点会放在视频编辑的部分。
播放器
播放器和视频播放相关博客的播放器大致相同,只是原来传入播放器的是视频地址,而现在传入的需要是一个完整的AVPlayerItem。因此需要重写了init方法,并且添加另一个用于替换当前播放AVPlayerItem的方法。
init方法:
override init() {super.init()self.player = AVPlayer(playerItem: playerItem)if let player = player {playerView = PHPlayerView(player: player)}addObserverForPlayerItem()}/// 自定义初始化方法////// - Parameters:/// - playerItem: AVPlayerIteminit(playerItem: AVPlayerItem? = nil) {super.init()self.playerItem = playerItemself.player = AVPlayer(playerItem: playerItem)if let player = player {playerView = PHPlayerView(player: player)}addObserverForPlayerItem()}
替换当前AVPlayerItem方法:
/// AVPlayer的同名方法,替换当前播的资源////// - Parameters:/// - playerItem: AVPlayerItemfunc replaceCurrentItem(playerItem:AVPlayerItem?) {guard let player = self.player else { return }self.playerItem = playerItemplayer.replaceCurrentItem(with: playerItem)addObserverForPlayerItem()}/// 为AVPlayerItem添加监听func addObserverForPlayerItem() {guard let playerItem = playerItem else { return }playerItem.addObserver(self, forKeyPath: status_keypath, context: &playerItemContext)}
另外我们将播放进度的监听由原来的0.5改为了1/60秒,因为我们需要使用它来同步动画,而不仅仅是显示当前时间。
/// 监听播放进度func addPlayerItemTimeObserver() {guard let player = player else { return }let interval = CMTimeMakeWithSeconds(1/60.0, preferredTimescale: Int32(NSEC_PER_SEC))let queue = DispatchQueue.maintimeObserver = player.addPeriodicTimeObserver(forInterval: interval, queue: queue, using: {[weak self] time inguard let self = self else { return }guard let playerItem = self.playerItem else { return }guard let delegate = self.delegate else { return }let currentTime = CMTimeGetSeconds(time)let duration = CMTimeGetSeconds(playerItem.duration)delegate.setCuttentTime(time: currentTime, duration: duration)})}
编辑器
编辑器由两部分构成,媒体资源选择器,和媒体资源编辑区域。
博客的示例项目中,我们只获取了视频媒体资源,音频媒体资源的做法与视频完全相同,只是传入的mediaType为audio。
媒体资源选择器为一个简单的列表,点击加号后会根据所选择的媒体资源创建一个PHMediaItem,PHMediaItem是一个基类,它的子类又分为PHVideoItem和PHAudioItem,后续的功能我们也许会用到PHAudioItem,但目前我们只需要使用PHVideoItem即可。
媒体资源编辑器就是页面除播放器以外的下半部分,有显示媒体选择器的按钮,和控制播放器播放和暂停的按钮,以及一个显示媒体剪辑状态的时间轴区域。
创建组合
我们的核心任务就是通过页面上的一些操作来创建一个媒体组合,首先声明一个PHComposition协议,协议中定义了两个方法分别用来生成组合的可播放版本和可导出版本。
import UIKit
import AVFoundationprotocol PHComposition {/// 协议方法-生成AVPlayerItem////// - Returns: 返回一个可播放的AVPlayerItemfunc makePlayerItem() -> AVPlayerItem?/// 协议方法-生成AVAssetExportSession////// - Returns: 返回一个可导出的AVAssetExportSessionfunc makeAssetExportSession() -> AVAssetExportSession?}
创建一个遵循PHComposition协议的类,并提供协议方法的实现。
// 负责创建 视频的可播放资源和可导出资源import UIKit
import AVFoundationclass PHBaseComposition: NSObject,PHComposition {//只读compositionprivate var compostion:AVComposition?//自定义初始化init(compostion: AVComposition? = nil) {self.compostion = compostion}//MARK: PHComposition - 生成 AVPlayerItemfunc makePlayerItem() -> AVPlayerItem? {if let compostion = compostion {let playerItem = AVPlayerItem(asset: compostion)return playerItem}return nil}//MARK: PHComposition - 生成 AVAssetExportSessionfunc makeAssetExportSession() -> AVAssetExportSession? {return nil}
}
创建一个组合的构建器,同样我们创建一个协议,负责来创建遵循PHComposition协议的对象。
import UIKitprotocol PHCompositionBuilder {/// 协议方法-生成一个遵循PHComposition协议的对象////// - Returns: 返回一个最新PHComposition协议的对象func buildComposition() -> PHComposition?
}
这个协议的具体方法由PHBaseCompositionBuilder来实现,代码如下。
import UIKit
import AVFoundationclass PHBaseCompositionBuilder: NSObject,PHCompositionBuilder {/// 时间线var timeLine:PHTimeLine!/// compositionprivate var composition = AVMutableComposition()init(timeLine: PHTimeLine!) {self.timeLine = timeLine}//MARK: PHCompositionBuilder - 生成 PHCompositionfunc buildComposition() -> PHComposition? {addCompositionTrack(mediaType: .video, mediaItems: timeLine.videoItmes)return PHBaseComposition(compostion: self.composition)}/// 私有方法-添加媒体资源轨道/// - Parameters:/// - mediaType: 媒体类型/// - mediaItems: 媒体媒体资源数组/// - Returns: 返回一个可播放的AVPlayerItemprivate func addCompositionTrack(mediaType:AVMediaType,mediaItems:[PHMediaItem]?) {if PHIsEmpty(array: mediaItems) {return}let trackID = kCMPersistentTrackID_Invalidguard let compositionTrack = composition.addMutableTrack(withMediaType: mediaType, preferredTrackID: trackID) else { return }//设置起始时间var cursorTime = CMTime.zeroguard let mediaItems = mediaItems else { return }for item in mediaItems {//这里默认时间都是从0开始guard let asset = item.asset else { continue }guard let assetTrack = asset.tracks(withMediaType: mediaType).first else { continue }do {try compositionTrack.insertTimeRange(item.timeRange, of: assetTrack, at: cursorTime)} catch {print("addCompositionTrack error")}cursorTime = CMTimeAdd(cursorTime, item.timeRange.duration)}}
}
- PHBaseCompositionBuilder在初始化的时候会默认创建一个AVMutableComposition对象用于媒体编辑操作。
- 获取timeLine对象中的所有视频资源,调用addCompositionTrack方法进行拼接。
- addCompositionTrack方法中首先判断了传入的资源数组是否为空。
- 当媒体资源数组不为空的时候,从composition中获取对应的媒体轨道。
- 设置起始时间,遍历媒体资源数组,从每个资源中获取对应的媒体轨道并添加到组合媒体轨道中。
- 修改下一个媒体资源的插入起始时间。
实现播放组合媒体
选择媒体资源
点击页面上的加号按钮,显示媒体选择列表,点击列表后会将选择的媒体资源创建为PHMediaItem并添加到当前的timeLine对应的资源数组下。
//MARK: 显示选择视频视图@objc func showItemPickerView() {let resourcePickerView = PHResourcePickerView(frame: CGRect(x: 0, y: UIScreen.main.bounds.height * 0.5, width: 150.0, height: 200.0))resourcePickerView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1.0)resourcePickerView.layer.masksToBounds = trueresourcePickerView.layer.cornerRadius = 5.0resourcePickerView.layer.borderColor = UIColor.white.cgColorresourcePickerView.layer.borderWidth = 1.0resourcePickerView.showDialog()resourcePickerView.addMediaItemBlock = { [weak self] mediaItem inguard let self = self else { return }if var videoItmes = timeLine.videoItmes {videoItmes.append(mediaItem as! PHVideoItem)timeLine.videoItmes = videoItmes} else {var videoItmes = [PHVideoItem]()videoItmes.append(mediaItem as! PHVideoItem)timeLine.videoItmes = videoItmes}self.collectionView?.reloadData()self.needReplay = true}}
点击播放按钮
点击播放按钮后判断是否有可播放资源,再进行播放。播放分为两种情况,从头开始播放和暂停后的继续播放。
//MARK: 播放按钮点击@objc func playerButtonOnlick(button:UIButton) {if PHIsEmpty(array: timeLine.videoItmes) {playerButton.isSelected = falsereturn}button.isSelected = !button.isSelected//回调guard let delegate = self.delegate else { return }if button.isSelected {if needReplay {player()needReplay = false} else {delegate.play()}} else {delegate.pause()}}func player() {guard let delegate = self.delegate else { return }let compositionBuilder = PHBaseCompositionBuilder(timeLine: timeLine)let composition = compositionBuilder.buildComposition()let playerItem = composition?.makePlayerItem()delegate.replaceCurrentItem(playerItem: playerItem)}
同步播放进度
PHEditorView视频编辑器遵循了PHControlDelegate协议,这里我们只关注setCuttentTime和playbackComplete方法。
setCuttentTime方法用来同步编辑器时间轴的进度。
playbackComplete用来同步播放按钮的状态。
extension PHEditorView:PHControlDelegate{func playpause(currentTime: TimeInterval) {}func playstart(duration: TimeInterval) {}func setCuttentTime(time: TimeInterval, duration: TimeInterval) {let origin_offsetX = -UIScreen.main.bounds.width * 0.5self.collectionView?.contentOffset = CGPointMake(origin_offsetX + time * item_size.width, 0.0)}func playbackComplete() {self.playerButton.isSelected = false}}
结语
在示例项目中,我们仅仅涉及了视频媒体资源,并默认这些资源都是单轨道的。然而,在实际的应用开发中,我们可能会面对更加复杂的情况,涉及到多种类型的媒体资源,以及多轨道的组合。iOS AVFoundation框架为我们提供了强大的工具和灵活的接口,让我们能够处理各种各样的媒体资源,并将它们巧妙地组合成为精彩纷呈的作品。通过深入理解和灵活运用AVFoundation框架,我们可以实现更加复杂和令人惊叹的音视频编辑应用,为用户带来全新的体验和享受。在今后的开发过程中,让我们继续探索和挖掘AVFoundation框架的潜力,创造出更加优秀和创新的音视频编辑应用!
项目地址:PHEditorPlayer: AV Foundation 音视频编辑
相关文章:
二.音视频编辑-媒体组合-播放
引言 当涉及到音视频编辑时,媒体资源的提取和组合是至关重要的环节。在iOS平台上,AVFoundation框架提供了丰富而强大的功能,使得媒体资源的操作变得轻松而高效。从原始的媒体中提取片段,然后将它们巧妙地组合成一个完整的作品&am…...
前端安全-面试题(2024)
1. 面试总结话术: 前端常见的安全问题主要包括以下几种: 跨站脚本攻击(XSS):攻击者通过在目标网站注入恶意脚本,当用户访问网站时,恶意脚本会被执行,从而窃取用户信息或进行其他恶意操作。这种攻击通常利用表单提交、URL参数等方式注入脚本。存储型 xss 恶意代码存在数…...
CVE-2022-29405 Apache Archiva任意用户密码重置漏洞分析
Apache Archiva是一套可扩展的Artifact Repository管理系统。它能够与Maven,Continuum和ANT等构建工具完美结合。Archiva提供的功能包括:远程Repository代理,基于角色的安全访问管理,Artifact分发、维护、查询,生成使用…...
ssm框架配置文件例子
emmm。。。。 就是说,正常ssm的配置文件长啥样? 就最基础的? 贴一下,备忘吧。 第一个:applicationContext.xml <beans xmlns"http://www.springframework.org/schema/beans"xmlns:context"http…...
maven构建项目报错:Failure to find com.microsoft.sqlserver:sqljdbc4:jar:4.0 in
背景 今天在项目里面查询sqlserver的数据库的时候,本地maven中引入依赖: <dependency><groupId>com.microsoft.sqlserver</groupId><artifactId>sqljdbc4</artifactId><version>4.0</version></dependenc…...
已解决rabbitmq AMQPConnectionClosedException:管道破裂或连接关闭异常的正确解决方法,亲测有效!!!
已解决rabbitmq AMQPConnectionClosedException:管道破裂或连接关闭异常的正确解决方法,亲测有效!!! 目录 一、问题分析 二、报错原因 三、解决思路 四、解决方法 五、总结 博主v:XiaoMing_Java 一、…...
Excel 隔几行批量插入空白行
例如如下表格,每隔6行插入一行数据: 1)第7个单元格输入1 2)选中6个单元格,然后双击填充数据: 3)F5 找到常量 Ctrlshift 复制插入的数据,然后选中数据 按F5,定位到空值...
2024年04月在线IDE流行度最新排名
点击查看最新在线IDE流行度最新排名(每月更新) 2024年04月在线IDE流行度最新排名 TOP 在线IDE排名是通过分析在线ide名称在谷歌上被搜索的频率而创建的 在线IDE被搜索的次数越多,人们就会认为它越受欢迎。原始数据来自谷歌Trends 如果您相…...
如何通过Elasticsearch实现搜索的关键词达到高亮的效果
高亮 首先介绍一下什么是搜索的关键词达到高亮的效果,如图所示 当在百度里面搜索elasticsearch的时候,可以看到出现的搜索结果里面elasticsearch这个关键词明显与其他的条文不一样,用红颜色凸显了“高亮效果”。当我们想要在自己的项目里面…...
真实sql注入以及小xss--BurpSuite联动sqlmap篇
前几天漏洞检测的时候无意发现一个sql注入 首先我先去网站的robots.txt去看了看无意间发现很多资产 而我意外发现admin就是后台 之后我通过基础的万能账号密码测试or ‘1‘’1也根本没有效果 而当我注入列的时候情况出现了 出现了报错,有报错必有注入点 因此我…...
Java类和对象练习题
练习一 下面代码的运行结果是() public static void main(String[] args){String s;System.out.println("s"s);} 解析:本题中的代码不能编译通过,因为在Java当中局部变量必须先初始化,后使用。所以此处编译不…...
Qt 实现简易的视频播放器,功能选择视频,播放,暂停,前进,后退,进度条拖拉,视频时长显示
1.效果图 2.代码实现 2.1 .pro文件 QT core gui multimedia multimediawidgets 2.2 .h文件 #ifndef VIDEOPLAYING_H #define VIDEOPLAYING_H#include <QWidget> #include<QFileDialog>#include<QMediaPlayer> #include<QMediaRecorder> #in…...
vue基础教程(6)——构建项目级登录页
同学们可以私信我加入学习群! 正文开始 前言一、创建首页二、登录页代码讲解三、对应的vue知识点:四、附件-各文件代码总结 前言 前面我们已经把vue自带的页面删除,也搭建了最简单的router路由,下面就可以真正开发我们自己的项目…...
C++宝强越狱1.0.6版本
没啥好说的,更新了一关,上代码 #include"bits/stdc.h" #include"Windows.h" #define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0) using namespace std; int w3,s3,a3,d3; bool nfalse,iptrue,mfals…...
构建高可用性数据库架构:深入探索Oracle Active Data Guard(ADG)
随着企业数据规模的不断增长和业务的复杂化,数据库的高可用性和可靠性变得尤为重要。Oracle Active Data Guard(ADG)作为Oracle数据库提供的一种高可用性解决方案,在实时备份和灾难恢复方面发挥着重要作用。本文将深入探讨ADG的原…...
记录-rosbag的处理
https://blog.csdn.net/qq_39607707/article/details/123716925 https://blog.csdn.net/weixin_51060040/article/details/126612496...
用Wireshark解码H.264
H264,你不知道的小技巧-腾讯云开发者社区-腾讯云 这篇文章写的非常好 这里仅做几点补充 init.lua内容: -- Set enable_lua to false to disable Lua support. enable_lua trueif not enable_lua thenreturn end-- If false and Wireshark was start…...
Flink中几个关键问题总结
硬核!八张图搞懂 Flink 端到端精准一次处理语义 Exactly-once(深入原理,建议收藏) Flink可靠性的基石-checkpoint机制详细解析 硬核!一文学完Flink流计算常用算子(Flink算子大全)...
华为配置ARP安全综合功能实验
华为配置ARP安全综合功能实验 组网图形 图1 配置ARP安全功能组网图 ARP安全简介配置注意事项组网需求配置思路操作步骤配置文件 ARP安全简介 ARP(Address Resolution Protocol)安全是针对ARP攻击的一种安全特性,它通过一系列对ARP表项学…...
new mars3d.layer.XyzLayer({的rectangle瓦片数据的矩形区域范围说明
new mars3d.layer.XyzLayer({的rectangle瓦片数据的矩形区域范围说明 2.这个xyz图层的矩形区域范围rectangle从图层文件中无法获取,但是看图层文件可以知道这个是12-21级的数据。 3.一般这个图层数据文件服务会有提供相应的rectangle范围,在服务的xml文…...
数据分析之Tebleau可视化:折线图、饼图、环形图
1.折线图的绘制 方法一: 拖入订单日期和销售金额,自动生成一个折线图 方法二: 选中订单日期和销售金额(摁住ctrl可以选择多个纬度) 点击右边的智能推荐,选择折线图 2.双线图的绘制、双轴的设置 方法一&…...
【Frida】【Android】 07_爬虫之网络通信库HttpURLConnection
🛫 系列文章导航 【Frida】【Android】01_手把手教你环境搭建 https://blog.csdn.net/kinghzking/article/details/136986950【Frida】【Android】02_JAVA层HOOK https://blog.csdn.net/kinghzking/article/details/137008446【Frida】【Android】03_RPC https://bl…...
算法2.6基数排序
基数排序 属于分配式排序,又称桶子法,通过键值的各个位上的值,将要排序的元素分配至某些桶中,达到排序的作用. 基数排序属于稳定性排序,是效率高的稳定性排序法 是桶排序的扩展,将整数按照位数进行切割,再按各个位数进行比较 是用空间换时间的经典算法 在使用8kw个数据进行…...
redis -List
一,List(列表) 1,所应用场景 list实际上是一个链表,before Node after , left, right 都可以插入值如果key不存在,则创建新的链表如果key存在,新增内容如果移除了所有值,空链表,也代表不存在在…...
ARMv8-A架构下的外部debug模型(external debug)简介
Armv8-A external debug Armv8-A debug模型一,外部调试 External debug 简介二,Debug state2.1 Debug state的进入与退出 三,DAP,Debug Access Port3.1 EDSCR, External Debug Status and Control Register调试状态标识࿰…...
DevOps入门
DevOps入门 1. 基础概念和原则 了解DevOps的定义、历史和主要目标 DevOps是一种将软件开发(Dev)与信息技术运维(Ops)结合起来的文化、运动或实践,旨在缩短系统开发生命周期,同时提供高质量的持续交付。DevOps的历史可以追溯到敏捷软件开发的兴起,它强调了开发和运维团队之…...
Docker搭建私有镜像仓库
1.Docker镜像仓库 搭建镜像仓库可以基于Docker官方提供的DockerRegistry来实现。 官网地址:https://hub.docker.com/_/registry 1.1.简化版镜像仓库 Docker官方的Docker Registry是一个基础版本的Docker镜像仓库,具备仓库管理的完整功能,…...
流行的API架构学习
几种流行的API架构风格图 SOAP(Simple Object Access Protocol) 优点:SOAP 是一种基于 XML 的通信协议,具有良好的跨平台和跨语言支持。它提供了丰富的安全性和事务管理功能,并支持复杂的消息交换模式。 缺点…...
问题解决:Fatal Python error: initfsencoding: unable to load the file system codec
问题: "D:\...Climb_C_site\venv\Scripts\python.exe" "D:\...\Small_Case\change_suffix.py" Fatal Python error: initfsencoding: unable to load the file system codec ModuleNotFoundError: No module named encodingsCurrent thread 0x…...
WPF —— TreeView树形控件
1 TreeView简介 TreeView 表示一个控件,该控件在树结构(其中的项可以展开和折叠)中显示分层数据。 TreeView 是一个 ItemsControl,这意味着它可以包含任何类型的对象的集合 (,例如字符串、图像或面板) 。 2 Tree Vie…...
单页网站订单系统怎么改邮箱/新品上市的营销方案
题库来源:安全生产模拟考试一点通公众号小程序 安全生产模拟考试一点通:2021年高处安装、维护、拆除考试报名为正在备考高处安装、维护、拆除操作证的学员准备的理论考试专题,每个月更新的高处安装、维护、拆除考试资料祝您顺利通过高处安装…...
泉州建设网站开发/靠谱seo外包定制
可以设置收、发邮件白名单,当终端用户使用天锐绿盾终端自带的邮件工具、outlook、foxmail 或闪电邮往白名单邮箱地址收、发送邮件时,加密的附件就会自动解密,减少解密申请次数,方便用户使用。工具:绿盾加密软件收件人白…...
哈尔滨做网站seo的/西安企业seo外包服务公司
故事是这样发生的:前几天和同事在公司吃饭,同事讲起他早年间相亲的故事。女方是某IT公司的HR,研究生学历,两人第一次见面的一段对话,觉得非常的有意思。 两人是共同的朋友介绍的,第一次见面地点商定在一家餐…...
旅游景点网站建设设计说明/蚂蚁bt
暖气来了,嗓子眼儿冒火、口腔溃疡、大便干燥,该怎么办呢?解放军309医院营养科主任医师张晔开出四字饮食处方:降、清、润、补。 降火汤——冬瓜配紫菜 很多家庭最爱做西红柿黄瓜片汤,其实冬季最好的汤是冬瓜汤ÿ…...
长沙做网站公司哪家/推广之家官网
1.什么是异常 在java中针对问题的反馈以及处理的一套机制。 2.具体介绍 异常分两种: Exception:是一个合理的应用程序,出现之后可以处理也可以不处理。jvm发生,并告诉使用者。可以进行针对性处理 Error:是一个合理(符合语法且代码执行逻辑没有…...
wordpress 变换模板/百度seo快速排名优化
newFixedThreadPool和newCachedThreadPool是创建线程池的两种方式,其中newFixedThreadPool是创建固定数量为 threads的线程数,其阻塞队列用的是LinkedBlockingQueue,队列大小容量为Integer.MAX_VALUE;newCachedThreadPool是创建一个可缓存的线…...