From aa75a64ea2660dff59b30756ecdeb9a4acd94be4 Mon Sep 17 00:00:00 2001 From: nixzhu Date: Wed, 22 Apr 2015 16:19:39 +0800 Subject: [PATCH] sendVideo --- Yep.xcodeproj/project.pbxproj | 4 + Yep/Extensions/NSFileManager+Yep.swift | 22 +++ Yep/Helpers/YepAsset.swift | 26 ++++ Yep/Services/YepService.swift | 5 + .../ConversationViewController.swift | 127 ++++++++++++++++-- 5 files changed, 171 insertions(+), 13 deletions(-) create mode 100644 Yep/Helpers/YepAsset.swift diff --git a/Yep.xcodeproj/project.pbxproj b/Yep.xcodeproj/project.pbxproj index 52600b1f..5b6e806f 100644 --- a/Yep.xcodeproj/project.pbxproj +++ b/Yep.xcodeproj/project.pbxproj @@ -126,6 +126,7 @@ 50E114F91ABC137A00F13000 /* YepServiceSync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E114F81ABC137A00F13000 /* YepServiceSync.swift */; }; 50E114FC1ABC549300F13000 /* ContactsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E114FA1ABC549300F13000 /* ContactsCell.swift */; }; 50E114FD1ABC549300F13000 /* ContactsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50E114FB1ABC549300F13000 /* ContactsCell.xib */; }; + 50EB8C5E1AE78AB9001AC1EE /* YepAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EB8C5D1AE78AB9001AC1EE /* YepAsset.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -266,6 +267,7 @@ 50E114F81ABC137A00F13000 /* YepServiceSync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepServiceSync.swift; path = Services/YepServiceSync.swift; sourceTree = ""; }; 50E114FA1ABC549300F13000 /* ContactsCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ContactsCell.swift; path = Views/Cells/Contacts/ContactsCell.swift; sourceTree = ""; }; 50E114FB1ABC549300F13000 /* ContactsCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ContactsCell.xib; path = Views/Cells/Contacts/ContactsCell.xib; sourceTree = ""; }; + 50EB8C5D1AE78AB9001AC1EE /* YepAsset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepAsset.swift; path = Helpers/YepAsset.swift; sourceTree = ""; }; B33BE6A6016453F6673C8317 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; FD88F6095DB46DFAD41AB5DB /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -608,6 +610,7 @@ children = ( 502AE5391AB871C1005BD199 /* YepAlert.swift */, 502AE53C1AB87C0D005BD199 /* YepUserDefaults.swift */, + 50EB8C5D1AE78AB9001AC1EE /* YepAsset.swift */, ); name = Helpers; sourceTree = ""; @@ -1101,6 +1104,7 @@ 50E114F41ABBF9CC00F13000 /* Models.swift in Sources */, 50A9D4571ACD393A000B2599 /* YepWaverView.swift in Sources */, 50165C451AC2860900C7AEBE /* ConversationLayout.swift in Sources */, + 50EB8C5E1AE78AB9001AC1EE /* YepAsset.swift in Sources */, 508C2AA41AD661B3002B8097 /* UserStateCell.swift in Sources */, 0A5D83941AC5D3C8000045BF /* YepTabBarController.swift in Sources */, 50E114FC1ABC549300F13000 /* ContactsCell.swift in Sources */, diff --git a/Yep/Extensions/NSFileManager+Yep.swift b/Yep/Extensions/NSFileManager+Yep.swift index e9aa3950..287b68d5 100644 --- a/Yep/Extensions/NSFileManager+Yep.swift +++ b/Yep/Extensions/NSFileManager+Yep.swift @@ -108,4 +108,26 @@ extension NSFileManager { return nil } + // Video + + class func yepMessageVideoURLWithName(name: String) -> NSURL? { + + if let messageCachesURL = yepMessageCachesURL() { + return messageCachesURL.URLByAppendingPathComponent("\(name).mp4") + } + + return nil + } + + class func saveMessageVideoData(messageVideoData: NSData, withName name: String) -> NSURL? { + + if let messageVideoURL = yepMessageVideoURLWithName(name) { + if NSFileManager.defaultManager().createFileAtPath(messageVideoURL.path!, contents: messageVideoData, attributes: nil) { + return messageVideoURL + } + } + + return nil + } + } diff --git a/Yep/Helpers/YepAsset.swift b/Yep/Helpers/YepAsset.swift new file mode 100644 index 00000000..bb61ca8e --- /dev/null +++ b/Yep/Helpers/YepAsset.swift @@ -0,0 +1,26 @@ +// +// YepAsset.swift +// Yep +// +// Created by NIX on 15/4/22. +// Copyright (c) 2015年 Catch Inc. All rights reserved. +// + +import UIKit +import AVFoundation + +func thumbnailImageOfVideoInVideoURL(videoURL: NSURL) -> UIImage? { + let asset = AVURLAsset(URL: videoURL, options: [:]) + let imageGenerator = AVAssetImageGenerator(asset: asset) + + imageGenerator.appliesPreferredTrackTransform = true + + var actualTime: CMTime = CMTimeMake(0, 0) + var error: NSError? + + let cgImage = imageGenerator.copyCGImageAtTime(CMTimeMakeWithSeconds(0.0, 600), actualTime: &actualTime, error: &error) + + let thumbnail = UIImage(CGImage: cgImage) + + return thumbnail +} \ No newline at end of file diff --git a/Yep/Services/YepService.swift b/Yep/Services/YepService.swift index b8ffb97d..6b797fd0 100644 --- a/Yep/Services/YepService.swift +++ b/Yep/Services/YepService.swift @@ -777,6 +777,11 @@ func sendAudioInFilePath(filePath: String?, orFileData fileData: NSData?, #metaD sendMessageWithMediaType(.Audio, inFilePath: filePath, orFileData: fileData, metaData: metaData, text: nil, toRecipient: recipientID, recipientType: recipientType, afterCreatedMessage: afterCreatedMessage, failureHandler: failureHandler, completion: completion) } +func sendVideoInFilePath(filePath: String?, orFileData fileData: NSData?, #metaData: String?, toRecipient recipientID: String, #recipientType: String, #afterCreatedMessage: (Message) -> Void, #failureHandler: ((Reason, String?) -> Void)?, #completion: (success: Bool) -> Void) { + + sendMessageWithMediaType(.Video, inFilePath: filePath, orFileData: fileData, metaData: metaData, text: nil, toRecipient: recipientID, recipientType: recipientType, afterCreatedMessage: afterCreatedMessage, failureHandler: failureHandler, completion: completion) +} + func sendMessageWithMediaType(mediaType: MessageMediaType, inFilePath filePath: String?, orFileData fileData: NSData?, #metaData: String?, #text: String?, toRecipient recipientID: String, #recipientType: String, #afterCreatedMessage: (Message) -> Void, #failureHandler: ((Reason, String?) -> Void)?, #completion: (success: Bool) -> Void) { // 因为 message_id 必须来自远端,线程无法切换,所以这里暂时没用 realmQueue // TOOD: 也许有办法 diff --git a/Yep/ViewControllers/Conversation/ConversationViewController.swift b/Yep/ViewControllers/Conversation/ConversationViewController.swift index bd60644c..77d45e8c 100644 --- a/Yep/ViewControllers/Conversation/ConversationViewController.swift +++ b/Yep/ViewControllers/Conversation/ConversationViewController.swift @@ -9,6 +9,7 @@ import UIKit import Realm import AVFoundation +import MobileCoreServices class ConversationViewController: UIViewController { @@ -369,7 +370,9 @@ class ConversationViewController: UIViewController { if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary){ let imagePicker = UIImagePickerController() imagePicker.delegate = self - imagePicker.sourceType = UIImagePickerControllerSourceType.PhotoLibrary + imagePicker.sourceType = .PhotoLibrary + imagePicker.mediaTypes = [kUTTypeImage, kUTTypeMovie] + imagePicker.videoQuality = .TypeMedium imagePicker.allowsEditing = false self.presentViewController(imagePicker, animated: true, completion: nil) @@ -380,7 +383,9 @@ class ConversationViewController: UIViewController { if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera){ let imagePicker = UIImagePickerController() imagePicker.delegate = self - imagePicker.sourceType = UIImagePickerControllerSourceType.Camera + imagePicker.sourceType = .Camera + imagePicker.mediaTypes = [kUTTypeImage, kUTTypeMovie] + imagePicker.videoQuality = .TypeMedium imagePicker.allowsEditing = false self.presentViewController(imagePicker, animated: true, completion: nil) @@ -855,7 +860,7 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatLeftTextCellIdentifier, forIndexPath: indexPath) as! ChatLeftTextCell cell.configureWithMessage(message, textContentLabelWidth: textContentLabelWidthOfMessage(message)) - + return cell } @@ -881,7 +886,7 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatRightTextCellIdentifier, forIndexPath: indexPath) as! ChatRightTextCell cell.configureWithMessage(message, textContentLabelWidth: textContentLabelWidthOfMessage(message)) - + return cell } } @@ -1105,8 +1110,30 @@ extension ConversationViewController: AVAudioPlayerDelegate { extension ConversationViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { - func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage!, editingInfo: [NSObject : AnyObject]!) { + func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) { + if let mediaType = info[UIImagePickerControllerMediaType] as? String { + println("mediaType \(mediaType)") + + switch mediaType { + case kUTTypeImage as! String: + if let image = info[UIImagePickerControllerOriginalImage] as? UIImage { + sendImage(image) + } + case kUTTypeMovie as! String: + if let videoURL = info[UIImagePickerControllerMediaURL] as? NSURL { + println("videoURL \(videoURL)") + sendVideoWithVideoURL(videoURL) + } + default: + break + } + } + + dismissViewControllerAnimated(true, completion: nil) + } + + func sendImage(image: UIImage) { // Prepare meta data var metaData: String? = nil @@ -1121,11 +1148,11 @@ extension ConversationViewController: UIImagePickerControllerDelegate, UINavigat // Do send - var imageData = UIImageJPEGRepresentation(image, YepConfig.messageImageCompressionQuality()) + let imageData = UIImageJPEGRepresentation(image, YepConfig.messageImageCompressionQuality()) let messageImageName = NSUUID().UUIDString - if let withFriend = self.conversation.withFriend { + if let withFriend = conversation.withFriend { sendImageInFilePath(nil, orFileData: imageData, metaData: metaData, toRecipient: withFriend.userID, recipientType: "User", afterCreatedMessage: { message -> Void in @@ -1153,7 +1180,7 @@ extension ConversationViewController: UIImagePickerControllerDelegate, UINavigat println("sendImage to friend: \(success)") }) - } else if let withGroup = self.conversation.withGroup { + } else if let withGroup = conversation.withGroup { sendImageInFilePath(nil, orFileData: imageData, metaData: nil, toRecipient: withGroup.groupID, recipientType: "Circle", afterCreatedMessage: { message -> Void in dispatch_async(dispatch_get_main_queue()) { @@ -1167,19 +1194,93 @@ extension ConversationViewController: UIImagePickerControllerDelegate, UINavigat } realm.commitWriteTransaction() } - + self.updateConversationCollectionView() } - + }, failureHandler: {(reason, errorMessage) -> () in defaultFailureHandler(reason, errorMessage) // TODO: sendImage 错误提醒 - + }, completion: { success -> Void in - println("sendImage to friend: \(success)") + println("sendImage to group: \(success)") }) } + } - dismissViewControllerAnimated(true, completion: nil) + func sendVideoWithVideoURL(videoURL: NSURL) { + + // Prepare meta data + + var metaData: String? = nil + + var thumbnailData: NSData? + + if let image = thumbnailImageOfVideoInVideoURL(videoURL) { + let videoMetaDataInfo = ["video_width": image.size.width, "video_height": image.size.height] + + if let videoMetaData = NSJSONSerialization.dataWithJSONObject(videoMetaDataInfo, options: nil, error: nil) { + let videoMetaDataString = NSString(data: videoMetaData, encoding: NSUTF8StringEncoding) as? String + metaData = videoMetaDataString + } + + thumbnailData = UIImageJPEGRepresentation(image, YepConfig.messageImageCompressionQuality()) + } + + let messageVideoName = NSUUID().UUIDString + + let afterCreatedMessageAction = { (message: Message) in + dispatch_async(dispatch_get_main_queue()) { + + if let videoData = NSData(contentsOfURL: videoURL) { + + if let messageVideoURL = NSFileManager.saveMessageImageData(videoData, withName: messageVideoName) { + let realm = message.realm + realm.beginWriteTransaction() + + if let thumbnailData = thumbnailData { + if let thumbnailURL = NSFileManager.saveMessageImageData(thumbnailData, withName: messageVideoName) { + message.localThumbnailName = messageVideoName + } + } + + message.localAttachmentName = messageVideoName + + message.mediaType = MessageMediaType.Video.rawValue + if let metaData = metaData { + message.metaData = metaData + } + realm.commitWriteTransaction() + } + + self.updateConversationCollectionView() + } + } + } + + if let withFriend = conversation.withFriend { + sendVideoInFilePath(videoURL.path!, orFileData: nil, metaData: metaData, toRecipient: withFriend.userID, recipientType: "User", afterCreatedMessage: afterCreatedMessageAction, failureHandler: {(reason, errorMessage) -> () in + defaultFailureHandler(reason, errorMessage) + // TODO: sendVideo 错误提醒 + + }, completion: { success -> Void in + println("sendVideo to friend: \(success)") + }) + + } else if let withGroup = conversation.withGroup { + sendVideoInFilePath(videoURL.path!, orFileData: nil, metaData: nil, toRecipient: withGroup.groupID, recipientType: "Circle", afterCreatedMessage: afterCreatedMessageAction, failureHandler: {(reason, errorMessage) -> () in + defaultFailureHandler(reason, errorMessage) + // TODO: sendVideo 错误提醒 + + }, completion: { success -> Void in + println("sendVideo to group: \(success)") + }) + } } } + + + + + +