美文网首页iOS技术点iOS 音视频ios开发小技巧
iOS 视频/图片 方向问题及压缩转码

iOS 视频/图片 方向问题及压缩转码

作者: 百里潋長 | 来源:发表于2016-12-01 16:37 被阅读0次

    上传到服务器的图片,在电脑上打开发现都是横着的


    c.jpeg

    查询原因发现因为图片中有一个Exif头信息,上传的时候直接把UIImage转为NSData后它就丢失了。iPhone拍照以横屏作为正方向,Windows以竖直作为正方向。偏偏这个方向信息就存储在这个Exif中,丢失了方向的照片在Windows上就……迷路了 : )

    我翻开历史一查,这历史没有年代,歪歪斜斜的每页上都写着“仁义道德”几个字。我横竖睡不着,仔细看了半夜,才从字缝里看出字来,满本都写着两个字是“吃人”! —— 鲁迅


    怎么解决?

    1. 在UIImage转换为NSData的时候加入Exif使其不丢失
    2. 传输NSData的时候将方向一同传入服务器,由接收方处理
    3. 上传前先读取方向信息,将图片进行旋转

    第一条肯定是最好最合理的,不过Windows上还存在不读取Exif的照片查看器,并且也着实没找到相关方法只能另辟蹊径了。
    第二条过于复杂,而且不仅自己没处理好问题还影响了接受方的逻辑。
    最后只好按第三条,自己处理好再给别人使用。

    将图片按照正确方向旋转####

    图片很好处理,Graphics在绘制UIImage的时候会读取Exif绘制出正确的图像。利用这一点可以用很少的代码获取修正后的UIImage。
    我们给UIImage写一个分类来方便使用

    - (UIImage *)normalizedImage {
        if (self.imageOrientation == UIImageOrientationUp) return self;
        UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
        [self drawInRect:(CGRect){0, 0, self.size}];    
        UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return normalizedImage;
    }
    

    这样就很简单地解决了转化为NSData之后图片Exif信息丢失导致图片旋转的问题,然而还没高兴起来就发现视频同样有这个事儿……
    在视频文件的信息中同样包含一个Rotation信息,它包含了此视频拍摄时的方向,视频播放时播放器会读取它然后用正确的方向播放,如果它丢了

    你的产品经理会教你如何做人 —— 百里潋長

    b.jpg
    万幸的是AVAssetExportSession可以对视频压缩转码,同时可以编辑视频方向,正好视频上传有压缩和将wav转码为mp4的需求,这下可以顺带解决。

    视频转码压缩及方向修正####

    使用AVAsset读取到文件,然后访问AVAssetTrack粗暴直接地做一个判断就可以得到方向

    + (NSUInteger)degressFromVideoFileWithURL:(NSURL *)url {
        NSUInteger degress = 0;
       
        AVAsset *asset = [AVAsset assetWithURL:url];
        NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
        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;
            }   
       }       
       return degress;
    }
    

    AVAssetExportSession需要一个AVComposition,我们将上面的代码整合一下,利用获取到的方向信息来决定视频大小

    - (AVMutableVideoComposition *)getVideoComposition:(AVAsset *)asset {
        AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
        AVMutableComposition *composition = [AVMutableComposition composition];
        AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
        CGSize videoSize = videoTrack.naturalSize;
         
        NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
        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) ||
               (t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0)){
                videoSize = CGSizeMake(videoSize.height, videoSize.width);
            }
        }
        composition.naturalSize    = videoSize;
        videoComposition.renderSize = videoSize;
        videoComposition.frameDuration = CMTimeMakeWithSeconds( 1 / videoTrack.nominalFrameRate, 600);
        
        AVMutableCompositionTrack *compositionVideoTrack;
        compositionVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
        [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:videoTrack atTime:kCMTimeZero error:nil];
        AVMutableVideoCompositionLayerInstruction *layerInst;
        layerInst = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
        [layerInst setTransform:videoTrack.preferredTransform atTime:kCMTimeZero];
        AVMutableVideoCompositionInstruction *inst = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
        inst.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
        inst.layerInstructions = [NSArray arrayWithObject:layerInst];
        videoComposition.instructions = [NSArray arrayWithObject:inst];
        return videoComposition;
    }
    

    最后只需要在压缩转码的时候将它添入AVAssetExportSessionvideoComposition中,这里我将输出的视频文件写入到Temp下的VideoCompression目录中,文件名叫VideoCompressionTemp.mp4

    - (void)lowQuailtyWithInputURL:(NSURL *)inputURL blockHandler:(void (^)(AVAssetExportSession *session, NSURL *compressionVideoURL))handler {
        AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
        AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetMediumQuality];
        NSString *path = [NSString stringWithFormat:@"%@VideoCompression/",NSTemporaryDirectory()];
        
       NSFileManager *fileManage = [[NSFileManager alloc] init];    static dispatch_once_t predicate;
        dispatch_once(&predicate, ^{
            if(![fileManage fileExistsAtPath:path]){
                [fileManage createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
            }
        });
        if([fileManage fileExistsAtPath:[NSString stringWithFormat:@"%@VideoCompressionTemp.mp4",path]]){
            [fileManage removeItemAtPath:[NSString stringWithFormat:@"%@VideoCompressionTemp.mp4",path] error:nil];
        }
       
        NSURL *compressionVideoURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@VideoCompressionTemp.mp4",path]];
        session.outputURL = compressionVideoURL;
        session.outputFileType = AVFileTypeMPEG4;
        session.shouldOptimizeForNetworkUse = YES;
        session.videoComposition = [self getVideoComposition:asset];
        [session exportAsynchronouslyWithCompletionHandler:^{
            dispatch_async(dispatch_get_main_queue(),^{
                switch ([session status]) {
                    case AVAssetExportSessionStatusFailed:{
                        NSLog(@"Export failed: %@ : %@", [[session error] localizedDescription], [session error]);
                        handler(session, nil);
                                           break;
                    }case AVAssetExportSessionStatusCancelled:{
                        NSLog(@"Export canceled");
                        handler(session, nil);
                                           break;
                    }default:
                        handler(session,compressionVideoURL);
                        break;
                }
            });
        }];
    }
    

    相关文章

      网友评论

        本文标题:iOS 视频/图片 方向问题及压缩转码

        本文链接:https://www.haomeiwen.com/subject/kdjxmttx.html