IOS KeyChain

转:
https://cnbin.github.io/blog/2015/08/18/ios-keychain-ji-chu/

根据苹果的介绍,iOS设备中的Keychain是一个安全的存储容器,可以用来为不同应用保存敏感信息比如用户名,密码,网络密码,认证令牌。苹果自己用keychain来保存Wi-Fi网络密码,VPN凭证等等。它是一个sqlite数据库,位于/private/var/Keychains/keychain-2.db,其保存的所有数据都是加密过的。

开发者通常会希望能够利用操作系统提供的功能来保存凭证(credentials)而不是把它们(凭证)保存到NSUserDefaults,plist文件等地方。保存这些数据的原因是开发者不想用户每次都要登录,因此会把认证信息保存到设备上的某个地方并且在用户再次打开应用的时候用这些数据自动登录。Keychain的信息是存在于每个应用(app)的沙盒之外的。

通过keychain access groups可以在应用之间共享keychain中的数据。要求在保存数据到keychain的时候指定group。把数据保存到keychain的最好方法就是用苹果提供的KeychainItemWrapper。可以到这下载例子工程。第一步就是创建这个类的实例。

1
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@”Password” accessGroup:nil];

标识符(Identifier)在后面我们要从keychain中取数据的时候会用到。如果你想要在应用之间共享信息,那么你需要指定访问组(access group)。有同样的访问组 的应用能够访问同样的keychain信息。

1
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@”Account Number” accessGroup:@”YOUR_APP_ID_HERE.com.yourcompany.GenericKeychainSuite”];

要把信息保存到keychain中,使用 setObject:forKey: 方法。在这里, (id)kSecAttrAccount 是一个预先定义好的键(key),我们可以用它来保存账号名称。 kSecClass指定了我们要保存的某类信息,在这里是一个通用的密码。kSecValueData可以被用来保存任意的数据,在这里是一个密码。

1
2
3
4
5
6
7
8
9
[wrapper setObject:kSecClassGenericPassword forKey:(id)kSecClass];
[wrapper setObject:@"username" forKey:(id)kSecAttrAccount];
[wrapper setObject:@"password"forKey:(id)kSecValueData];
[wrapper setObject:(id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(id)kSecAttrAccessible];
kSecAttrAccessiblein变量用来指定这个应用合适需要访问这个数据。我们需要对这个选项特别注意,并且使用最严格的选项。这个键(key)可以设置6种值。

当然,我们应该绝对不要使用kSecAttrAccessibleAlways。一个安全点的选项是kSecAttrAccessibleWhenUnlocked。有些选项是以 ThisDeviceOnly 结尾的,如果选中了这个选项,那么数据就会被以硬件相关的密钥(key)加密,因此不能被传输到或者被其他设备看到。即使它们提供了进一步的安全性,使用它们可能不是一个好主意,除非你有一个更好的理由不允许数据在备份之间迁移。

要从keychain中获取数据,可以用

1
NSString *accountName = [wrapper objectForKey:(id)kSecAttrAccount];

钥匙串中的条目称为SecItem,但它是存储在CFDictionary中的。SecItemRef类型并不存在。SecItem有五类:通用密码、互联网密码、证书、密钥和身份。在大多数情况下,我们用到的都是通用密码。许多问题都是开发人员尝试用互联网密码造成的。互联网密码要复杂得多,而且相比之下优势寥寥无几,除非开发Web浏览器,否则没必要用它。KeyChainItemWrapper只使用通用密码,这也是我喜欢它的原因之一。iOS应用很少将密钥和身份存储起来,所以我们在本书中不会讨论这方面的内容。只有公钥的证书通常应该存储在文件中,而不是钥匙串中。

最后,我们需要在钥匙串中搜索需要的内容。密钥有很多个部分可用来搜索,但最好的办法是将自己的标识符赋给它,然后搜索。通用密码条目都包含属性kSecAttrGeneric,可以用它来存储标识符。这也是KeyChainItemWrapper的处理方式。

钥匙串中的条目都有几个可搜索的属性和一个加密过的值。对于通用密码条目,比较重要的属性有账户(kSecAttrAccount)、服务(kSecAttrService)和标识符(kSecAttrGeneric)。而值通常是密码。

设备唯一字符串
http://www.jianshu.com/p/faa4854ce180

, 主要用到上述的KeyChain 保存唯一的字符串
上述简书 上有个demo , 特此记录原理和代码

IOS 加密整理

SHA 加密:

oc 写法,转得忘记出处了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//
//NSString+SHA.h
//
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonCryptor.h>
@interface NSString(SHA)
-(NSString *) sha1;
-(NSString *) sha224;
-(NSString *) sha256;
-(NSString *) sha384;
-(NSString *) sha512;
@end
#import "NSString+SHA.h"
@implementation NSString (SHA)
-(NSString *) sha1{
const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:self.length];
uint8_t digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(data.bytes, (CC_LONG)data.length, digest);
NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
for (int i=0; i<CC_SHA1_DIGEST_LENGTH; i++) {
[output appendFormat:@"%02x",digest[i]];
}
return output;
}
-(NSString *) sha224{
const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:self.length];
uint8_t digest[CC_SHA224_DIGEST_LENGTH];
CC_SHA224(data.bytes, (CC_LONG)data.length, digest);
NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA224_DIGEST_LENGTH*2];
for (int i=0; i<CC_SHA224_DIGEST_LENGTH; i++) {
[output appendFormat:@"%02x",digest[i]];
}
return output;
}
- (NSString*) sha256
{
const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:self.length];
uint8_t digest[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(data.bytes, (CC_LONG)data.length, digest);
NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++)
[output appendFormat:@"%02x", digest[i]];
return output;
}
- (NSString*) sha384
{
const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:self.length];
uint8_t digest[CC_SHA384_DIGEST_LENGTH];
CC_SHA384(data.bytes, (CC_LONG)data.length, digest);
NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA384_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_SHA384_DIGEST_LENGTH; i++)
[output appendFormat:@"%02x", digest[i]];
return output;
}
- (NSString*) sha512
{
const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:self.length];
uint8_t digest[CC_SHA512_DIGEST_LENGTH];
CC_SHA512(data.bytes, (CC_LONG)data.length, digest);
NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA512_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_SHA512_DIGEST_LENGTH; i++)
[output appendFormat:@"%02x", digest[i]];
return output;
}
@end

下面是一个加盐的SHA加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//
// HmacUtils.m
// Inspector
//
// Created by yaoliangjun on 16/5/20.
// Copyright © 2016年 yaoliangjun. All rights reserved.
//
#import "HmacUtils.h"
#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonHMAC.h>
@implementation HmacUtils
/**
* 加密方式,MAC算法: HmacSHA256
*
* @param plaintext 要加密的文本
* @param key 秘钥
*
* @return 加密后的字符串
*/
+ (NSString *)hmac:(NSString *)plaintext withKey:(NSString *)key
{
const charchar *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding];
const charchar *cData = [plaintext cStringUsingEncoding:NSASCIIStringEncoding];
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *HMACData = [NSData dataWithBytes:cHMAC length:sizeof(cHMAC)];
const unsigned charchar *buffer = (const unsigned charchar *)[HMACData bytes];
NSMutableString *HMAC = [NSMutableString stringWithCapacity:HMACData.length * 2];
for (int i = 0; i < HMACData.length; ++i){
[HMAC appendFormat:@"%02x", buffer[i]];
}
return HMAC;
}
@end

swift 3 加密 ,其他SHA 类似方法 , 桥接头中加入系统加密类库
转自: http://stackoverflow.com/questions/25388747/sha256-in-swift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* 加密系统头
*/
//#import <CommonCrypto/CommonDigest.h>
//#import <CommonCrypto/CommonCrypto.h>
// Created by boosj on 16/10/20.
// Copyright © 2016年 cz. All rights reserved.
//
import Foundation
public extension String{
func sha256() -> String{
if let stringData = self.data(using: String.Encoding.utf8) {
return hexStringFromData(input: digestSha256(input: stringData as NSData))
}
return ""
}
private func digestSha256(input : NSData) -> NSData {
let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA256(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
private func hexStringFromData(input: NSData) -> String {
var bytes = [UInt8](repeating: 0, count: input.length)
input.getBytes(&bytes, length: input.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02x", UInt8(byte))
}
return hexString
}
}

IOS swift3 设备型号获取

swift 版本很多设备获取都会改变不像oc 那么稳定,
这次是看到一个好的方式觉得比我以前的觉得代码要优雅特此整理完善

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//
// Created by boosj on 16/10/19.
// Copyright © 2016年 cz. All rights reserved.
// UIDevice.current.modelName.rawValue
//判断也不需要直接string字符串 防止代码写错
public enum Model : String {
case iPod1 = "iPod 1",
iPod2 = "iPod 2",
iPod3 = "iPod 3",
iPod4 = "iPod 4",
iPod5 = "iPod 5",
iPad2 = "iPad 2",
iPad3 = "iPad 3",
iPad4 = "iPad 4",
iPhone4 = "iPhone 4",
iPhone4S = "iPhone 4S",
iPhone5 = "iPhone 5",
iPhone5S = "iPhone 5S",
iPhone5C = "iPhone 5C",
iPadMini1 = "iPad Mini 1",
iPadMini2 = "iPad Mini 2",
iPadMini3 = "iPad Mini 3",
iPadAir1 = "iPad Air 1",
iPadAir2 = "iPad Air 2",
iPhone6 = "iPhone 6",
iPhone6plus = "iPhone 6 Plus",
iPhone6S = "iPhone 6S",
iPhone6Splus = "iPhone 6S Plus",
iPhoneSE = "iPhone SE",
iPhone7 = "iPhone 7",
iPhone7plus = "iPhone 7 Plus",
iPadPro9 = "iPad Pro(9.7)",
iPadPro = "iPad Pro",
AppleTV = "Apple TV",
iPad = "iPad",
iPod = "iPod",
simulator = "simulator",
iPhone = "iPhone",
unrecognized = "unrecognized"
}
public extension UIDevice {
public var modelName: Model {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8 , value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}
var modelMap : [ String : Model ] = [
"i386" : .simulator,
"x86_64" : .simulator,
"iPod1,1" : .iPod1,
"iPod2,1" : .iPod2,
"iPod3,1" : .iPod3,
"iPod4,1" : .iPod4,
"iPod5,1" : .iPod5,
"iPad2,1" : .iPad2,
"iPad2,2" : .iPad2,
"iPad2,3" : .iPad2,
"iPad2,4" : .iPad2,
"iPad2,5" : .iPadMini1,
"iPad2,6" : .iPadMini1,
"iPad2,7" : .iPadMini1,
"iPhone3,1" : .iPhone4,
"iPhone3,2" : .iPhone4,
"iPhone3,3" : .iPhone4,
"iPhone4,1" : .iPhone4S,
"iPhone5,1" : .iPhone5,
"iPhone5,2" : .iPhone5,
"iPhone5,3" : .iPhone5C,
"iPhone5,4" : .iPhone5C,
"iPad3,1" : .iPad3,
"iPad3,2" : .iPad3,
"iPad3,3" : .iPad3,
"iPad3,4" : .iPad4,
"iPad3,5" : .iPad4,
"iPad3,6" : .iPad4,
"iPad6,4" : .iPadPro9,
"iPad6,3" : .iPadPro9,
"iPad6,7" : .iPadPro,
"iPad6,8" : .iPadPro,
"iPhone6,1" : .iPhone5S,
"iPhone6,2" : .iPhone5S,
"iPad4,1" : .iPadAir1,
"iPad4,2" : .iPadAir2,
"iPad4,4" : .iPadMini2,
"iPad4,5" : .iPadMini2,
"iPad4,6" : .iPadMini2,
"iPad4,7" : .iPadMini3,
"iPad4,8" : .iPadMini3,
"iPad4,9" : .iPadMini3,
"iPhone7,1" : .iPhone6plus,
"iPhone7,2" : .iPhone6,
"iPhone8,1" : .iPhone6S,
"iPhone8,2" : .iPhone6Splus,
"iPhone9,1" : .iPhone7,
"iPhone9,3" : .iPhone7,
"iPhone9,2" : .iPhone7plus,
"iPhone9,4" : .iPhone7plus
]
if let model = modelMap[identifier] {
return model
}
if identifier.range(of: "iPad") != nil{
return Model.iPad
}
if identifier.range(of: "iPod") != nil{
return Model.iPod
}
if identifier.range(of: "iPhone") != nil{
return Model.iPhone
}
return Model.unrecognized
}
}

主要知识从http://stackoverflow.com/questions/26028918/ios-how-to-determine-iphone-model-in-swift
转载

IOS 音视频合成

相关链接:
http://www.jianshu.com/p/8e1c7815af0e
http://www.devzhang.cn/2016/09/09/%E7%BC%96%E8%BE%91Assets/

今天去查询音视频合成相关资料,找到一个demo 觉得很是不错

转文:http://www.jianshu.com/p/9f83af9dbbef

####音视频主要是利用AVFoundation框架下的AVMutableComposition来合成音视频.

####在AVMutableComposition中传入两个数据流,一个是音频一个是视频,之后调用合成方法就可以了

#上代码

##storyBoard中拖入一个button,一个imageView
这里写图片描述

##为了效果好可以将IamgeView的背景色调为黑色

##然后在ViewController中添加以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import "MBProgressHUD+MJ.h"
@interface ViewController ()
/** 用于播放 */
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction)mergeAction:(UIButton *)sender {
[self merge];
}
// 混合音乐
- (void)merge{
// mbp提示框
[MBProgressHUD showMessage:@"正在处理中"];
// 路径
NSString *documents = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
// 声音来源
NSURL *audioInputUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"五环之歌" ofType:@"mp3"]];
// 视频来源
NSURL *videoInputUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"myPlayer" ofType:@"mp4"]];
// 最终合成输出路径
NSString *outPutFilePath = [documents stringByAppendingPathComponent:@"merge.mp4"];
// 添加合成路径
NSURL *outputFileUrl = [NSURL fileURLWithPath:outPutFilePath];
// 时间起点
CMTime nextClistartTime = kCMTimeZero;
// 创建可变的音视频组合
AVMutableComposition *comosition = [AVMutableComposition composition];
// 视频采集
AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:videoInputUrl options:nil];
// 视频时间范围
CMTimeRange videoTimeRange = CMTimeRangeMake(kCMTimeZero, videoAsset.duration);
// 视频通道 枚举 kCMPersistentTrackID_Invalid = 0
AVMutableCompositionTrack *videoTrack = [comosition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
// 视频采集通道
AVAssetTrack *videoAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] firstObject];
// 把采集轨道数据加入到可变轨道之中
[videoTrack insertTimeRange:videoTimeRange ofTrack:videoAssetTrack atTime:nextClistartTime error:nil];
// 声音采集
AVURLAsset *audioAsset = [[AVURLAsset alloc] initWithURL:audioInputUrl options:nil];
// 因为视频短这里就直接用视频长度了,如果自动化需要自己写判断
CMTimeRange audioTimeRange = videoTimeRange;
// 音频通道
AVMutableCompositionTrack *audioTrack = [comosition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
// 音频采集通道
AVAssetTrack *audioAssetTrack = [[audioAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
// 加入合成轨道之中
[audioTrack insertTimeRange:audioTimeRange ofTrack:audioAssetTrack atTime:nextClistartTime error:nil];
// 创建一个输出
AVAssetExportSession *assetExport = [[AVAssetExportSession alloc] initWithAsset:comosition presetName:AVAssetExportPresetMediumQuality];
// 输出类型
assetExport.outputFileType = AVFileTypeQuickTimeMovie;
// 输出地址
assetExport.outputURL = outputFileUrl;
// 优化
assetExport.shouldOptimizeForNetworkUse = YES;
// 合成完毕
[assetExport exportAsynchronouslyWithCompletionHandler:^{
// 这里还得把状态监听下 assetExport status ,AVAssetExportSessionStatusCompleted
// 回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
// 调用播放方法
[self playWithUrl:outputFileUrl];
});
}];
// assetExport.progress 进度 time 定时去检测
}
/** 播放方法 */
- (void)playWithUrl:(NSURL *)url{
// 传入地址
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:url];
// 播放器
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];
// 播放器layer
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
playerLayer.frame = self.imageView.frame;
// 视频填充模式
playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
// 添加到imageview的layer上
[self.imageView.layer addSublayer:playerLayer];
// 隐藏提示框 开始播放
[MBProgressHUD hideHUD];
[MBProgressHUD showSuccess:@"合成完成"];
// 播放
[player play];
}

##MBP是一个第三方提示类,如果不关心这个功能可以删除这三行代码和头文件

1
2
3
4
5
// mbp提示框
[MBProgressHUD showMessage:@"正在处理中"];
// 隐藏提示框 开始播放
[MBProgressHUD hideHUD];
[MBProgressHUD showSuccess:@"合成完成"];

这里写图片描述

##GitHub:https://github.com/Lafree317/MergeVideoAndMusic

补充: 单音轨, 多音频合成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 声音采集2
AVURLAsset *audioAsset2 = [[AVURLAsset alloc] initWithURL:audioInputUrl2 options:nil];
// 因为视频短这里就直接用视频长度了,如果自动化需要自己写判断 , 注: 如果两个音频接着播放的得,得考虑 两个长度问题, 这个会以长的做节点, 自己计算基准事件和音频时间
CMTime aduioTime2 = CMTimeMakeWithSeconds(10, audioAsset2.duration.timescale);//audioAsset2.duration;//CMTimeMake(videoAsset.duration.value - audioAsset.duration.value, audioAsset2.duration.timescale);
CMTimeRange audioTimeRange2 = CMTimeRangeMake(kCMTimeZero, aduioTime2);//videoTimeRange;
// 音频通道
// AVMutableCompositionTrack *audioTrack2 = [comosition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
// 音频采集通道
AVAssetTrack *audioAssetTrack2 = [[audioAsset2 tracksWithMediaType:AVMediaTypeAudio] firstObject];
// 加入合成轨道之中
//[audioTrack insertTimeRange:audioTimeRange ofTrack:audioAssetTrack atTime:nextClistartTime error:nil];
[audioTrack insertTimeRanges:@[[NSValue valueWithCMTimeRange:audioTimeRange],[NSValue valueWithCMTimeRange:audioTimeRange2]] ofTracks:@[audioAssetTrack,audioAssetTrack2] atTime:nextClistartTime error:nil];

补充3 : 多音轨合成
2 也可以直接取出视频的音轨进行合并,达到保留视频音轨(没测试)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 声音采集2
AVURLAsset *audioAsset2 = [[AVURLAsset alloc] initWithURL:audioInputUrl2 options:nil];
// 因为视频短这里就直接用视频长度了,如果自动化需要自己写判断 , 注: 如果两个音频接着播放的得,得考虑 两个长度问题, 这个会以长的做节点, 自己计算基准事件和音频时间
CMTime aduioTime2 = audioAsset2.duration;//CMTimeMake(videoAsset.duration.value - audioAsset.duration.value, audioAsset2.duration.timescale);
CMTimeRange audioTimeRange2 = CMTimeRangeMake(kCMTimeZero, aduioTime2);//videoTimeRange;
// 音频通道
AVMutableCompositionTrack *audioTrack2 = [comosition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
// 音频采集通道
AVAssetTrack *audioAssetTrack2 = [[audioAsset2 tracksWithMediaType:AVMediaTypeAudio] firstObject];
// 加入合成轨道之中
[audioTrack insertTimeRange:audioTimeRange ofTrack:audioAssetTrack atTime:nextClistartTime error:nil];
[audioTrack2 insertTimeRange:audioTimeRange2 ofTrack:audioAssetTrack2 atTime:nextClistartTime error:nil];

// 翻译https://changjianfeishui.gitbooks.io/avfoundation-programming-guide/content/chapter1.html
挺不错,可供参考


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
双轨道合成备注下 但是最好一个轨道
/**
片头拼接
@param exportVideoFilePath <#exportVideoFilePath description#>
@param inputeVideoURL <#inputeVideoURL description#>
@param videoHiveURL <#videoHiveURL description#>
*/
-(void)builderVideoHiveToMp4Pth: (NSString *)exportVideoFilePath
withInputeVideoURL: (NSURL *)inputeVideoURL
withVideoHiveURL: (NSURL*)videoHiveURL{
_effectType = 1;
unlink([exportVideoFilePath UTF8String]);
if (!inputeVideoURL || ![inputeVideoURL isFileURL] || !exportVideoFilePath || [exportVideoFilePath isEqualToString:@""]){
//没有输入或者输出等, 返回NO
NSLog(@"inputeVideoURL 或者 exportVideoFilePath 地址有错");
return;
}
// 创建asset 从输入url
AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:inputeVideoURL options:nil];
AVURLAsset *videoAsset2 = [[AVURLAsset alloc] initWithURL:videoHiveURL options:nil];
if (videoAsset == nil || [[videoAsset tracksWithMediaType:AVMediaTypeVideo] count]<1 || [[videoAsset2 tracksWithMediaType:AVMediaTypeVideo] count]<1){
// 没有建立成功,或者没有视频轨道 + 音频判断
return;
}
NSParameterAssert(videoAsset);
// 视频和音频的合成类
AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init];
/// MARK视频轨道 合成类 先add 的会显示在最底层
// 空的轨道
AVMutableCompositionTrack *videoTrack2 = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *videoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
// 轨道数据
AVAssetTrack *assetVideoTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
AVAssetTrack *assetVideoTrack2 = [[videoAsset2 tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
[videoTrack2 insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset2.duration ) ofTrack:assetVideoTrack2 atTime:kCMTimeZero error:nil];
[videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration ) ofTrack:assetVideoTrack atTime:videoAsset2.duration error:nil];
// layer 数据轨道的合成指令集
AVMutableVideoCompositionLayerInstruction *videolayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:assetVideoTrack];
AVMutableVideoCompositionLayerInstruction *videolayerInstruction2 = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:assetVideoTrack2];
[videolayerInstruction2 setOpacity:0.0 atTime:videoAsset2.duration];
// AVMutableVideoCompositionLayerInstruction *audiolayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:assetAudioTrack];
//Make a "pass through video track" video composition.
/// 视频合成的指令
AVMutableVideoCompositionInstruction *mainInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
mainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeAdd(videoAsset.duration, videoAsset2.duration));
mainInstruction.layerInstructions = [NSArray arrayWithObjects:videolayerInstruction,videolayerInstruction2, nil];
// 视频合成类
AVMutableVideoComposition *mainVideoComposition = [[AVMutableVideoComposition alloc] init];
mainVideoComposition.instructions = [NSArray arrayWithObjects:mainInstruction, nil];
mainVideoComposition.frameDuration = CMTimeMake(1, 30);
mainVideoComposition.renderSize = assetVideoTrack.naturalSize;
// 所有效果的父类
CALayer *parentLayer = [CALayer layer];
CALayer *videoLayer = [CALayer layer];
parentLayer.contentsScale = [UIScreen mainScreen].scale;
videoLayer.contentsScale = [UIScreen mainScreen].scale;
parentLayer.frame = CGRectMake(0, 0, assetVideoTrack.naturalSize.width, assetVideoTrack.naturalSize.height);
videoLayer.frame = parentLayer.frame;
[parentLayer addSublayer:videoLayer];
mainVideoComposition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
if (_animationlayer){
[_animationlayer setAllFrame:parentLayer.bounds];
[parentLayer addSublayer:_animationlayer];
}
// export to mp4
NSString *mp4Quality = AVAssetExportPresetHighestQuality;
NSString *exportPath = exportVideoFilePath;
NSURL *exportUrl = [NSURL fileURLWithPath:exportPath];
_videotoTalTime = CMTimeGetSeconds(videoAsset.duration);
_exportSession = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:mp4Quality];
_exportSession.outputURL = exportUrl;
_exportSession.outputFileType = AVFileTypeQuickTimeMovie;// 转换格式
_exportSession.shouldOptimizeForNetworkUse = YES;
_exportSession.videoComposition = mainVideoComposition;
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
// 定时更新进度
_timerEffect = [NSTimer CZ_scheduledTimerWithTimeInterval:0.1f repeats:YES callback:^{
[weakSelf retrievingProgressMP4];
}];
});
[_exportSession exportAsynchronouslyWithCompletionHandler:^{
if (!weakSelf){return;}
NSLog(@"转码状态 %ld",(long)[weakSelf.exportSession status]);
switch ([weakSelf.exportSession status]) {
case AVAssetExportSessionStatusCompleted:
{
dispatch_async(dispatch_get_main_queue(), ^{
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(AVAssetExportVideoHiveMP4SessionStatus:)]){
[weakSelf.delegate AVAssetExportVideoHiveMP4SessionStatus:[weakSelf.exportSession status]];
}
[weakSelf clearAll];
});
NSLog(@"视频转码成功");
break;
}
case AVAssetExportSessionStatusFailed:
{
dispatch_async(dispatch_get_main_queue(), ^{
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(AVAssetExportVideoHiveMP4SessionStatus:)]){
[weakSelf.delegate AVAssetExportVideoHiveMP4SessionStatus:[weakSelf.exportSession status]];
}
[weakSelf clearAll];
});
NSLog(@"视频转码失败%@",[weakSelf.exportSession error]);
break;
}
case AVAssetExportSessionStatusCancelled:
break;
case AVAssetExportSessionStatusWaiting:
break;
case AVAssetExportSessionStatusExporting:
break;
case AVAssetExportSessionStatusUnknown:
default:
break;
}
}];
}

/// 后期看到
来源
AVURLAssetPreferPreciseDurationAndTimingKey的选项,选项带有@YES值可以确保当资源的属性使用AVAsynchronousKeyValueLoading协议载入时可以计算出准确的时长和时间信息。虽然使用这个option时还会载入过程增添一些额外开销,不过这可以保证资源正处于合适的编辑状态。生成水印的代码如下:
NSDictionary opts = [NSDictionary dictionaryWithObject:@(YES) forKey:AVURLAssetPreferPreciseDurationAndTimingKey];
AVURLAsset
videoAsset = [AVURLAsset URLAssetWithURL:videoPathURL options:opts]; //初始化视频媒体文件

IOS 通知

ios 的通知

swift2.3

1
2
3
4
5
6
7
8
9
10
11
NSNotificationCenter.defaultCenter().postNotificationName("HomRefresh", object: data)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "homeRefresh:", name: "HomRefresh", object: nil)//接受消息
func homeRefresh(Notification:NSNotification){
// 传递的各种类型的数据
let data = Notification.object as XX
}

swift 3 里面写法变了 更新下写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "namexxx"), object: "数据", userInfo: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.collcetNotifiaction(_:)), name: NSNotification.Name(rawValue:VideoCollectMessage), object: nil)
/// 收藏 消息接受
func collcetNotifiaction(_ sender:Notification) {
let cell = self.tableView.cellForRow(at: IndexPath(row: 0, section: 2)) as? VideoNewPageOtherViewMenuCell
if cell != nil{
DispatchQueue.main.async {
cell!.setMenuBtnStatus(3, status: true)
}
}
}

ubuntu 自带vi 不好用问题解决

安装vim full版本
由于Ubuntu预安装的是tiny版本,就会导致我们在使用上的产生不便。所以我们要安装vim的full版本。
首先,先卸掉旧版的vi,输入以下命令: sudo apt-get remove vim-common
然后安装full版的vim,输入命令:sudo apt-get install vim
这样安装好了之后的VI就没有那么难用了。
此方法是我自己使用的,很有效,但就是得联网。

VMWare中ubuntu虚拟机设置分辨率

安装虚拟机ubuntu 分辨率问题

安装好之后分辨率不对, 得安装vmware tools

  1. 启动虚拟机
  2. 虚拟机菜单,安装tools
  3. 会再虚拟机上产生一个cd盘
  4. 随意一个建一个文件夹把cd 光驱里面的文件拷贝到自建文件夹
  5. 打开终端 cd 到自建文件夹 如 cd/temp , 或者先ls 下看看自己的位置在一步步cd 到位置
  6. tar zxvf VMwareTools-xxxx.tar.gz 解压这个文件夹,xxx 是版本信息
  7. cd 到解压的文件夹
  8. sudo ./VMware-install.pl 根据提示输入密码(sudo 的本机授权密码), 一路 yes 然后等待安装好,重启虚拟机就ok了

IOS 协议的记录

ios Swift 协议

1
2
3
4
5
6
7
8
9
10
11
protocol xxx:class {}
protocol xxx{}
@objc protocol
三种方式,
1 完全swift 类型的协议, 继承class (或者 NSObjectProtocol)的类特性, 可以weak ,这个很重要不然容易循环引用,内存释放不掉
2 第二个正常的, 通用的,纯净的,但是因为weak 问题,所以我一般用第一种 用得多
3 第三个 对OC 开放, 也就有了OC 的特性, 也就可以weak 了,

swift 协议的可选协议
1 @objc 方式创建, 然后加个可选的参数

1
2
3
@objc protocol BottomBtnViewDelegate{
@objc optional func bottomBtnViewClick(_ index:Int)
}

2 swift 方式创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protocol xx:class{
func test()
}
extension xx{
func test(){
}
func test2(){
// 默认的实现方式, 然后重写这个就ok
}
}
// 哈哈 我自己还没这么写过,应该可以

Array特定类型扩展

本想字符串数组排序的,后来发现sorted 一下就好.所以只把扩展array方法记录下
扩展特定Array swift3 ,

记录下字典的排序

1
2
var parameterDic = ["dede":"dede","233":"344"]
let parameterArray = parameterDic.sorted(by: { $0.0 < $1.0 })
1
2
3
4
5
6
7
8
9
10
11
12
13
extension Sequence where Iterator.Element == Int{
func count(index:Int)->Int{
print("In Sequence")
return index * index
}
}
extension Collection where Iterator.Element == Int{
func count(index:Int)->Int{
print("In Collection")
return index * index
}
}

转: Array扩展中的元素遵循某个协议(而不是等于某种类型)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protocol Lovable{
func fallInLove(with name:String)
}
struct Love:Lovable{
func fallInLove(with name:String){
print("fall in love with \(name)")
}
}
extension Array where Element:Lovable{
func count(index:Int)->Int{
print("In Array")
return index * index
}
}
let loves = [Love(),Love()]
loves.count(index: 12)

各种类型转换日记

写这篇日记是为了记录下我遇见的一些需要类型转换的地方 的类型转换

swift 3 中学习别人代码的时候遇到的一个c 的删除函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
unlink () //参数char *
函数类似功能swift 函数
try? FileManager.default.removeItem(atPath: pathToMovie)
//oc
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Movie.m4v"];
unlink([path UTF8String]);
// swift
unlink(UnsafePointer<Int8>!)
String 没看到转成这种类型的, 无奈 还是先转了NSString
unlink((path as NSString).utf8String!)
//swift3
let string = fileURL.path.cString(using: String.Encoding.utf8)
unlink(string)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Swift 3中,String有两个初始化器
public init(cString: UnsafePointer<CChar>)
public init(cString: UnsafePointer<UInt8>)
因此它可以从有符号和无符号字符的(空终止)序列创建。所以
let s = String(cString: yourCharPointer)
应该只是工作。
String 有另一个初始化
public init?(validatingUTF8 cString: UnsafePointer<CChar>)
其在形式不良的UTF-8序列上失败,而不是由替换字符替换它们。这个init方法没有对应的无符号字符。
以现有的实现在CString.swift为例,它不是太难添加此作为扩展:
extension String {
public init?(validatingUTF8 cString: UnsafePointer<UInt8>) {
guard let (s, _) = String.decodeCString(cString, as: UTF8.self,
repairingInvalidCodeUnits: false) else {
return nil
}
self = s
}
}
接着
if let s = String(validatingUTF8: yourCharPointer) {
print(s)
} else {
print("invalid UTF-8")
}
也与带符号和无符号字符的(空终止)序列一起工作。

一个 小数点保留问题, 最后转成string, 小数点问题

1
2
3
4
let sizeGB = 1.2234
let str = String(format: "%0.0f", sizeGB)
print(str)
// 0.0 是0位小数 0.1 是一位 依次类推 也可以.0 .1

swift string 转 float 等 现在只发现这个最简单 先转 NSString

1
2
3
4
5
var s1 = s as NSString
//有这一句之后,就可以用下方的方法转换为int、float、double
s1.integerValue
s1.doubleValue
s1.floatValue