| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- //
- // TXVideoLoadingController.m
- // TCLVBIMDemo
- //
- // Created by annidyfeng on 2017/4/17.
- // Copyright © 2017年 tencent. All rights reserved.
- //
- #import "TXVideoLoadingController.h"
- //#import <QBImagePickerController/QBImagePickerController.h>
- #import "QBImagePickerController.h"
- #import "TCVideoCutViewController.h"
- #import "TCVideoJoinViewController.h"
- #import <Photos/Photos.h>
- @interface TXVideoLoadingController ()
- @property IBOutlet UIImageView *loadingImageView;
- @property AVAssetExportSession *exportSession;
- @property NSTimer *timer;
- @property NSMutableArray *localPaths;
- @property NSArray *videosAssets;
- @property NSMutableArray *videosToEditAssets;
- @property NSUInteger exportIndex;
- @property AVMutableComposition *mutableComposition;
- @property AVMutableVideoComposition *mutableVideoComposition;
- @end
- @implementation TXVideoLoadingController
- {
- BOOL _loadingIsInterrupt;
- }
- - (void)viewDidLoad {
- [super viewDidLoad];
- // Do any additional setup after loading the view from its nib.
- self.title = NSLocalizedString(@"TCVideoLoading.ChoosingVideo", nil);
- self.view.backgroundColor = UIColor.blackColor;
-
- }
- - (void)viewWillAppear:(BOOL)animated
- {
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(onAudioSessionEvent:)
- name:AVAudioSessionInterruptionNotification
- object:nil];
- }
- - (void)viewWillDisappear:(BOOL)animated
- {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
- - (void) onAudioSessionEvent: (NSNotification *) notification
- {
- NSDictionary *info = notification.userInfo;
- AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
- if (type == AVAudioSessionInterruptionTypeBegan) {
- _loadingIsInterrupt = YES;
- [self exportAssetError];
- }
- }
- - (void)didReceiveMemoryWarning {
- [super didReceiveMemoryWarning];
- // Dispose of any resources that can be recreated.
- }
- - (void)exportAssetList:(NSArray *)videosAssets
- {
- _videosAssets = videosAssets;
- _exportIndex = 0;
- _localPaths = [NSMutableArray new];
- self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];
-
- _videosToEditAssets = [NSMutableArray array];
- [self exportAssetInternal];
- }
- - (void)exportAssetInternal
- {
- if (_exportIndex == _videosAssets.count) {
- [self.timer invalidate],
- self.timer = nil;
-
- if (!self.composeMode) {
- TCVideoCutViewController *vc = [TCVideoCutViewController new];
- //vc.videoPath = _localPaths[0];
- vc.videoAsset = _videosToEditAssets[0];
- if(!_loadingIsInterrupt) [self.navigationController pushViewController:vc animated:YES];
- return;
- } else {
- TCVideoJoinViewController *vc = [TCVideoJoinViewController new];
- // vc.videoList = _localPaths;
- vc.videoAssertList = _videosToEditAssets;
- if(!_loadingIsInterrupt) [self.navigationController pushViewController:vc animated:YES];
- return;
- }
- }
-
- self.mutableComposition = nil;
- self.mutableVideoComposition = nil;
-
- //__weak typeof(self) weakSelf = self;
- // __block AVAssetExportSession *weakExportSession = _exportSession;
- PHAsset *expAsset = _videosAssets[_exportIndex];
- [[PHImageManager defaultManager] requestAVAssetForVideo:expAsset options:nil resultHandler:^(AVAsset * _Nullable avAsset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
- //SDK内部通过avAsset 读取视频数据,会极大的降低视频loading时间
- // LBVideoOrientation or = avAsset.videoOrientation;
- // if (or == LBVideoOrientationUp || or == LBVideoOrientationDown) {
- // CGFloat angle = 0;
- // if (or == LBVideoOrientationUp) angle = 90.0;
- // if (or == LBVideoOrientationDown) angle = -90.0;
- // [self performWithAsset:avAsset rotate:angle];
- // weakExportSession = [[AVAssetExportSession alloc] initWithAsset:self.mutableComposition presetName:AVAssetExportPresetHighestQuality];
- // weakExportSession.videoComposition = self.mutableVideoComposition;
- // } else {
- // weakExportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:AVAssetExportPresetHighestQuality];
- // }
- //
- //
- // NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
- // NSString *documentsDirectory = [paths objectAtIndex:0];
- // NSString* videoPath = [documentsDirectory stringByAppendingPathComponent:[expAsset orignalFilename]];
- // NSFileManager *manager = [NSFileManager defaultManager];
- //
- // NSError *error;
- // if ([manager fileExistsAtPath:videoPath]) {
- // BOOL success = [manager removeItemAtPath:videoPath error:&error];
- // if (success) {
- // NSLog(@"Already exist. Removed!");
- // }
- // }
- // [self.localPaths addObject:videoPath];
- //
- // NSURL *outputURL = [NSURL fileURLWithPath:videoPath];
- // weakExportSession.outputURL = outputURL;
- // weakExportSession.outputFileType = AVFileTypeMPEG4;
- // [weakExportSession exportAsynchronouslyWithCompletionHandler:^{
- // if(weakExportSession.status == AVAssetExportSessionStatusCompleted){
- dispatch_async(dispatch_get_main_queue(), ^(void) {
- [_videosToEditAssets addObject:avAsset];
- _exportIndex++;
- [self exportAssetInternal];
- });
- // }else{
- // dispatch_async(dispatch_get_main_queue(), ^(void) {
- // [weakSelf exportAssetError];
- // });
- // }
- // }];
- //
- // _exportSession = weakExportSession;
- }];
- }
- - (void)exportAssetError
- {
- UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error" message:NSLocalizedString(@"TCVideoLoading.HintVideoExportingFailed", nil) preferredStyle:UIAlertControllerStyleAlert];
- UIAlertAction *ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"Common.OK", nil) style:0 handler:^(UIAlertAction * _Nonnull action) {
- [self.navigationController dismissViewControllerAnimated:YES completion:nil];
- }];
- [alert addAction:ok];
- [self presentViewController:alert animated:YES completion:nil];
- }
- - (void)updateProgress
- {
- dispatch_async(dispatch_get_main_queue(), ^{
- CGFloat progress = _exportSession.progress;
- if (_exportSession.status == AVAssetExportSessionStatusFailed ||
- _exportSession.status == AVAssetExportSessionStatusCompleted) {
- progress = 1;
- }
- CGFloat allp = (progress + _exportIndex)/_videosAssets.count;
- self.loadingImageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"video_record_share_loading_%d", (int)(allp * 8)]];
- });
- }
- #define degreesToRadians( degrees ) ( ( degrees ) / 180.0 * M_PI )
- - (void)performWithAsset:(AVAsset*)asset rotate:(CGFloat)angle
- {
- AVMutableVideoCompositionInstruction *instruction = nil;
- AVMutableVideoCompositionLayerInstruction *layerInstruction = nil;
- CGAffineTransform t1;
- CGAffineTransform t2;
-
- AVAssetTrack *assetVideoTrack = nil;
- AVAssetTrack *assetAudioTrack = nil;
- // Check if the asset contains video and audio tracks
- if ([[asset tracksWithMediaType:AVMediaTypeVideo] count] != 0) {
- assetVideoTrack = [asset tracksWithMediaType:AVMediaTypeVideo][0];
- }
- if ([[asset tracksWithMediaType:AVMediaTypeAudio] count] != 0) {
- assetAudioTrack = [asset tracksWithMediaType:AVMediaTypeAudio][0];
- }
-
- CMTime insertionPoint = kCMTimeZero;
- NSError *error = nil;
-
-
- // Step 1
- // Create a composition with the given asset and insert audio and video tracks into it from the asset
- if (!self.mutableComposition) {
-
- // Check whether a composition has already been created, i.e, some other tool has already been applied
- // Create a new composition
- self.mutableComposition = [AVMutableComposition composition];
-
- // Insert the video and audio tracks from AVAsset
- if (assetVideoTrack != nil) {
- AVMutableCompositionTrack *compositionVideoTrack = [self.mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
- [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, [asset duration]) ofTrack:assetVideoTrack atTime:insertionPoint error:&error];
- }
- if (assetAudioTrack != nil) {
- AVMutableCompositionTrack *compositionAudioTrack = [self.mutableComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
- [compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, [asset duration]) ofTrack:assetAudioTrack atTime:insertionPoint error:&error];
- }
-
- }
-
-
- // Step 2
- // Translate the composition to compensate the movement caused by rotation (since rotation would cause it to move out of frame)
- if (angle == 90)
- {
- t1 = CGAffineTransformMakeTranslation(assetVideoTrack.naturalSize.height, 0.0);
- }else if (angle == -90){
- t1 = CGAffineTransformMakeTranslation(0.0, assetVideoTrack.naturalSize.width);
- } else {
- return;
- }
- // Rotate transformation
- t2 = CGAffineTransformRotate(t1, degreesToRadians(angle));
-
-
- // Step 3
- // Set the appropriate render sizes and rotational transforms
- if (!self.mutableVideoComposition) {
-
- // Create a new video composition
- self.mutableVideoComposition = [AVMutableVideoComposition videoComposition];
- self.mutableVideoComposition.renderSize = CGSizeMake(assetVideoTrack.naturalSize.height,assetVideoTrack.naturalSize.width);
- self.mutableVideoComposition.frameDuration = CMTimeMake(1, 30);
-
- // The rotate transform is set on a layer instruction
- instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
- instruction.timeRange = CMTimeRangeMake(kCMTimeZero, [self.mutableComposition duration]);
- layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:(self.mutableComposition.tracks)[0]];
- [layerInstruction setTransform:t2 atTime:kCMTimeZero];
-
- } else {
-
- self.mutableVideoComposition.renderSize = CGSizeMake(self.mutableVideoComposition.renderSize.height, self.mutableVideoComposition.renderSize.width);
-
- // Extract the existing layer instruction on the mutableVideoComposition
- instruction = (self.mutableVideoComposition.instructions)[0];
- layerInstruction = (instruction.layerInstructions)[0];
-
- // Check if a transform already exists on this layer instruction, this is done to add the current transform on top of previous edits
- CGAffineTransform existingTransform;
-
- if (![layerInstruction getTransformRampForTime:[self.mutableComposition duration] startTransform:&existingTransform endTransform:NULL timeRange:NULL]) {
- [layerInstruction setTransform:t2 atTime:kCMTimeZero];
- } else {
- // Note: the point of origin for rotation is the upper left corner of the composition, t3 is to compensate for origin
- CGAffineTransform t3 = CGAffineTransformMakeTranslation(-1*assetVideoTrack.naturalSize.height/2, 0.0);
- CGAffineTransform newTransform = CGAffineTransformConcat(existingTransform, CGAffineTransformConcat(t2, t3));
- [layerInstruction setTransform:newTransform atTime:kCMTimeZero];
- }
-
- }
-
-
- // Step 4
- // Add the transform instructions to the video composition
- instruction.layerInstructions = @[layerInstruction];
- self.mutableVideoComposition.instructions = @[instruction];
-
-
- // Step 5
- // Notify AVSEViewController about rotation operation completion
- // [[NSNotificationCenter defaultCenter] postNotificationName:AVSEEditCommandCompletionNotification object:self];
- }
- @end
- @implementation PHAsset (My)
- - (NSString *)orignalFilename {
- NSString *filename;
- if ([[PHAssetResource class] instancesRespondToSelector:@selector(assetResourcesForAsset:)]) {
- NSArray *resources = [PHAssetResource assetResourcesForAsset:self];
- PHAssetResource *resource = resources.firstObject;
- if (resources) {
- filename = resource.originalFilename;
- }
- }
- if (filename == nil) {
- filename = [self valueForKey:@"filename"];
- if (filename == nil ||
- ![filename isKindOfClass:[NSString class]]) {
- filename = [NSString stringWithFormat:@"temp%ld", time(NULL)];
- }
- }
-
- return filename;
- }
- @end
- //static inline CGFloat RadiansToDegrees(CGFloat radians) {
- // return radians * 180 / M_PI;
- //};
- @implementation AVAsset (My)
- @dynamic videoOrientation;
- - (LBVideoOrientation)videoOrientation
- {
- NSArray *videoTracks = [self tracksWithMediaType:AVMediaTypeVideo];
- if ([videoTracks count] == 0) {
- return LBVideoOrientationNotFound;
- }
-
- AVAssetTrack* videoTrack = [videoTracks objectAtIndex:0];
- CGAffineTransform txf = [videoTrack preferredTransform];
- CGFloat videoAngleInDegree = RadiansToDegrees(atan2(txf.b, txf.a));
-
- LBVideoOrientation orientation = 0;
- switch ((int)videoAngleInDegree) {
- case 0:
- orientation = LBVideoOrientationRight;
- break;
- case 90:
- orientation = LBVideoOrientationUp;
- break;
- case 180:
- orientation = LBVideoOrientationLeft;
- break;
- case -90:
- orientation = LBVideoOrientationDown;
- break;
- default:
- orientation = LBVideoOrientationNotFound;
- break;
- }
-
- return orientation;
- }
- @end
|