iOS圖片,視頻上傳&視頻內容旋轉

#前言
我最近在接手一個智能盒子的iOS應用,上面有一個功能是這樣的。把你本地的照片和視頻能夠甩屏到你綁定的盒子上。
個人上一位前輩作的時候必需要求再同一個局域網,可是當我作的時候要求不一樣的局域網也要實現這樣的一個功能,優化用戶的使用感覺。javascript


#那麼 咱們下面就進入正題。java

#內容一:圖片上傳
我作的圖片上傳是用的AF,原理就是把你想上傳的照片取出來,若是有文件大小的要求就先作相應的圖片壓縮。
而後用AFHTTPSessionManager 類去作的上傳,下面就是代碼:session

AFHTTPSessionManager *session = [AFHTTPSessionManager manager];
    [session.requestSerializer willChangeValueForKey:@"timeoutInterval"];
    session.requestSerializer.timeoutInterval = 60.f;
    [session.requestSerializer didChangeValueForKey:@"timeoutInterval"];
    session.responseSerializer = [AFHTTPResponseSerializer serializer];

    NSString *imageTitle =_imageSource[pageIndex].title;
    imageTitle = [imageTitle substringToIndex:imageTitle.length-4];


    NSDictionary *params = @{
                          @"fileType":@"jpg",
                          @"fileName":imageTitle
                          };
    [session POST:@"你上傳的接口地址" parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
        for (NSString *key in dict.allKeys) {
            [formData appendPartWithFileData:[dict objectForKey:key] name:@"myFile" fileName:[NSString stringWithFormat:@"%@.jpg",key] mimeType:@"image/jpeg"];
        }

    } progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

    }];複製代碼

這裏主要就是和大家的後臺確認好相應的參數設置就行了。app


#內容二:視頻上傳
視頻上傳和圖片上傳實際上是相似的,只不過就是參數的設置不同而已。也是用上面的方法。關鍵也是設置好參數。
在這裏我想說的是,有的時候視頻太大要作相應的壓縮處理。由於我以前對視頻這面接觸的比較少,也上網找了一些資料而後看到這樣的一個方法:ide

- (void)lowQuailtyWithInputURL:(NSURL*)inputURL
                      outputURL:(NSURL*)outputURL
                   blockHandler:(void (^)(AVAssetExportSession*))handler
{
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
    AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:asset     presetName:AVAssetExportPresetMediumQuality];
    session.outputURL = outputURL;
    session.outputFileType = AVFileTypeQuickTimeMovie;
    [session exportAsynchronouslyWithCompletionHandler:^(void)
     {
        handler(session);
     }];
}複製代碼

經過這個方法就能夠對視頻進行相應的壓縮處理。優化


#下面就是視頻內容的旋轉問題了。
一開始我聽到了這個需求的時候個人腦子裏真的是瞬間萬馬飛奔啊!! 這是什麼鬼需求啊!鬼知道我經歷了什麼?
我想,那我就上網找一下吧!我找了一下,還真的有這麼樣的技術博客。可是一上來就是把他的代碼網上一貼。我先大概的瀏覽了一下那代碼的長度。寫的我實在是沒有耐心去仔細的看究竟是怎麼實現的。我就關掉了。但是後來一看,也沒有別的什麼博客供我參考了。在我走投無路的時候,我看到了這麼一個博客,說是Apple本身就有Demo,我就抱着好奇的態度去看了看。下載了那個Demo仔細的研究了一下。果真就好使了。 那麼,接下來就說一下。ui

再旋轉視頻以前咱們先要確認一下,這個視頻是要旋轉多少度。this

AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
    NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
    NSInteger degress;
    if([tracks count] > 0) {
        AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
        CGAffineTransform t = videoTrack.preferredTransform;//這裏的矩陣有旋轉角度,轉換一下便可
        if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0){
            // Portrait
            degress = 90;
        }else if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0){
            // PortraitUpsideDown
            degress = 270;
        }else if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0){
            // LandscapeRight
            degress = 0;
        }else if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0){
            // LandscapeLeft
            degress = 180;
        }
    }複製代碼

這樣咱們就能夠經過degress來確認旋轉的角度了。
那麼咱們接下來就開始旋轉視頻spa

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 (!mutableComposition) {
        // Check whether a composition has already been created, i.e, some other tool has already been applied
        // Create a new composition
        mutableComposition = [AVMutableComposition composition];
        // Insert the video and audio tracks from AVAsset
        if (assetVideoTrack != nil) {
            AVMutableCompositionTrack *compositionVideoTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
            [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, [asset duration]) ofTrack:assetVideoTrack atTime:insertionPoint error:&error];
        }
        if (assetAudioTrack != nil) {
            AVMutableCompositionTrack *compositionAudioTrack = [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)
// t1 = CGAffineTransformMakeTranslation(assetVideoTrack.naturalSize.height, 0.0);
// // Rotate transformation
// t2 = CGAffineTransformRotate(t1, degreesToRadians(90));
    if (degress == 0) {
        t1 = CGAffineTransformMakeTranslation(0.0, 0.0);
        t2 = CGAffineTransformRotate(t1, degreesToRadians(0));
    }else if (degress == 90){
        t1 = CGAffineTransformMakeTranslation(assetVideoTrack.naturalSize.height, 0.0);
        t2 = CGAffineTransformRotate(t1, degreesToRadians(90));
    }else if (degress == 180){
        t1 = CGAffineTransformMakeTranslation(assetVideoTrack.naturalSize.width, assetVideoTrack.naturalSize.height);
        t2 = CGAffineTransformRotate(t1, degreesToRadians(180));
    }else if (degress == 270){
        t1 = CGAffineTransformMakeTranslation(0,assetVideoTrack.naturalSize.height*1.78);
        t2 = CGAffineTransformRotate(t1, degreesToRadians(-90));
    }
    // Step 3
    // Set the appropriate render sizes and rotational transforms
    if (!mutableVideoComposition) {
        // Create a new video composition
        mutableVideoComposition = [AVMutableVideoComposition videoComposition];
        if (degress == 0 || degress == 180) {
            mutableVideoComposition.renderSize = CGSizeMake(assetVideoTrack.naturalSize.width,assetVideoTrack.naturalSize.height);
        }else{
            mutableVideoComposition.renderSize = CGSizeMake(assetVideoTrack.naturalSize.height,assetVideoTrack.naturalSize.width);
        }
        mutableVideoComposition.frameDuration = CMTimeMake(1, 30);
        // The rotate transform is set on a layer instruction
        instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
        instruction.timeRange = CMTimeRangeMake(kCMTimeZero, [mutableComposition duration]);
        layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:(mutableComposition.tracks)[0]];
        [layerInstruction setTransform:t2 atTime:kCMTimeZero];
    } else {
        mutableVideoComposition.renderSize = CGSizeMake(mutableVideoComposition.renderSize.height, mutableVideoComposition.renderSize.width);
        // Extract the existing layer instruction on the mutableVideoComposition
        instruction = (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:[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];
    mutableVideoComposition.instructions = @[instruction];複製代碼

到這個時候呢視頻的旋轉已經完成了。
上面咱們不是寫了一個視頻壓縮的方法嗎?把這個時候生成的mutableVideoComposition賦值給session.videoComposition = mutableVideoComposition;就行了。這樣就是先旋轉視頻而後直接壓縮完成咱們的需求。code