iOS开发中音视频的获取、压缩上传

随着市场直播的日益发展,现在市场上媒体流的重要性被越来越多的开发者重视。本菜鸟在上个项目的开发中,很是尴尬的也遇到的视频流以及音频流的处理、上传。当中也遇到的不少坑,因为当初的项目是社交类的服务项目,需要用户能够上传视频、语音等,所以涉及到音视频的处理。

时间过得很快,2016年马上过去,最近又比较闲。所有用有限的时间总结一下这一年多来遇到的一些比较有意思的知识点与大家共勉,希望各路大神多多指教,不喜勿喷。谢谢。本菜鸟必将再去奋斗。

这边文章分享一些本菜鸟的对视频的一些处理,音频后面有时间在上来分享共勉。

视频获取上传方式
1:直接摄像头拍摄,压缩上传给服务端
2:从相册中选取已有视频,压缩上传给服务端。

第一种方法好处理,拍摄完直接拿到视频流和路径了,这个应该不难。
第二种方法,我在做项目的时候遇到不少坑(项目兼容7.0以上),所有在获取视频流的时候,8.0以上、8.0以下的方法不一样,被坑了。

下面直接贴代码并解释,但愿有遇到的朋友可以互勉,本菜鸟也是菜鸟一个,做的也不是很好。

-------------------1:直接拍摄视频,上传服务端--------------------

#pragma mark 用户拍摄视频
-(void)shotVideoEvent
{
    if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
        UIImagePickerController *videoPicker = [[UIImagePickerController alloc] init];
        videoPicker.delegate = self;
        videoPicker.allowsEditing = YES;
        videoPicker.sourceType = UIImagePickerControllerSourceTypeCamera;
        NSArray *mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
        videoPicker.videoQuality = UIImagePickerControllerQualityTypeMedium;
        videoPicker.videoMaximumDuration = 1200.0f;//1200秒  20分钟   本项目只允许最长时间
        //videoPicker.mediaTypes = mediaTypes;
        videoPicker.mediaTypes = [NSArray arrayWithObject:@"public.movie"];
        [self presentViewController:videoPicker animated:YES completion:nil];
    }else{
        UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"设备不支持拍照" message:nil delegate:nil cancelButtonTitle:@"知道了" otherButtonTitles:nil, nil];
        [alertView show];
    }
}
#pragma mark 拍摄视频完的方法
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];//视频路径
    NSData *beforeVideoData = [NSData dataWithContentsOfURL:videoURL];//未压缩的视频流
    //这里是视频流处理的方法   下面会解释
    VideoServer *videoServer = [[VideoServer alloc] init];
    
    NSString *randomName = @"随便给视频取个名字吧";//当然自己的项目不是这样写的,这里为了解释
    
    [videoServer compressVideo:videoURL andVideoName:randomName andSave:YES successCompress:^(NSData *resultData) {
        //这里就是视频流压缩后回调的方法
        if (resultData == nil || resultData.length == 0 ) {
            //如果压缩失败,我就把未压缩的视频流直接上传给服务端咯
            [self postFormData:beforeVideoData AndName:[NSString stringWithFormat:@"%@%@",randomName,@".mp4"]];
        }else{
            //压缩成功的视频流   上传给服务端
            [self postFormData:resultData AndName:[NSString stringWithFormat:@"%@%@",randomName,@".mp4"]];
        }//这里我发现拍摄的视频,压缩前压缩后大小差不多,会不会是拍摄完苹果已经做处理?
    }];
    [picker dismissViewControllerAnimated:YES completion:nil];
}

-------------------2:从相册选取视频,上传服务端--------------------

#pragma mark 用户选择好了视频   这里项目需求用到了一个第三方的选取照片和视频《TZImagePickerController》,感谢作者,但是在使用的时候也遇到有bug,不能满足项目需求,自己做了部分修改,谢谢作者分享这个选取照片视频的
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingVideo:(UIImage *)coverImage sourceAssets:(id)asset
{
    NSString *randomName = @"随便给视频取个名字吧";//当然自己的项目不是这样写的,这里为了解释
    //下面本菜鸟遇到的坑-->视频流路径竟然不一样,分iOS 8 以上、以下视频流的获取处理
    if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0f) { //如果是iOS 8以上
        PHAsset *myAsset = asset;
        PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
        [[PHImageManager defaultManager] requestAVAssetForVideo:myAsset options:options resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
            NSURL *fileRUL = [asset valueForKey:@"URL"];
            
            NSData *beforeVideoData = [NSData dataWithContentsOfURL:fileRUL];//未压缩的视频流
            
            VideoServer *videoServer = [[VideoServer alloc] init];
            [videoServer compressVideo:fileRUL andVideoName:randomName andSave:NO successCompress:^(NSData *resultData) {
                if (resultData == nil || resultData.length == 0 ) {
                    [self postFormData:beforeVideoData AndName:[NSString stringWithFormat:@"%@%@",randomName,@".mp4"]];
                }else{
                    [self postFormData:resultData AndName:[NSString stringWithFormat:@"%@%@",randomName,@".mp4"]];
                }
            }];
        }];
        
    }else{//如果是iOS 8以下获取视频流
        ALAssetRepresentation *rep = [asset defaultRepresentation];//视频流路径
        VideoServer *videoServer = [[VideoServer alloc] init];
        [videoServer compressVideo:rep.url andVideoName:randomName andSave:NO successCompress:^(NSData *resultData) {
            if (resultData == nil || resultData.length == 0 ) {
                //iOS 8 以下 压缩失败 直接获取未压缩的视频流
                ALAssetRepresentation *rep = [asset defaultRepresentation];
                Byte *buffer = (Byte *)malloc(rep.size);
                NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
                NSData *videoData = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
                [self postFormData:videoData AndName:[NSString stringWithFormat:@"%@%@",randomName,@".mp4"]];
            }else{
                //如果压缩成功  就用压缩的上传服务端,但是注意了   这里如果不用异步的,上传的时候程序挂了
                
                //ios 8 以下一定必须加入这个再去请求,否则会挂
                dispatch_async(dispatch_get_main_queue(),^{
                    [self postFormData:resultData AndName:[NSString stringWithFormat:@"%@%@",randomName,@".mp4"]];
                });
            }
        }];
    }
}

----------------下面是视频压缩的方法以及获取视频首帧缩略图的类-----------------

#import <Foundation/Foundation.h>

@interface VideoServer : NSObject

///视频名字
@property (nonatomic,strong) NSString *videoName;

/// 压缩视频
-(void)compressVideo:(NSURL *)path andVideoName:(NSString *)name andSave:(BOOL)saveState
          successCompress:(void(^)(NSData *))successCompress;

/// 获取视频的首帧缩略图
- (UIImage *)imageWithVideoURL:(NSURL *)url;

@end
#import "VideoServer.h"
#import <Photos/Photos.h>
#import <AssetsLibrary/AssetsLibrary.h>

@implementation VideoServer
//压缩视频
-(void)compressVideo:(NSURL *)path andVideoName:(NSString *)name andSave:(BOOL)saveState
          successCompress:(void(^)(NSData *))successCompress  //saveState 是否保存视频到相册
{
    self.videoName = name;
    AVURLAsset *avAsset = [[AVURLAsset alloc] initWithURL:path options:nil];
    NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
    if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality]) {
        
        AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:AVAssetExportPreset640x480];
        exportSession.outputURL = [self compressedURL];//设置压缩后视频流导出的路径
        exportSession.shouldOptimizeForNetworkUse = true;
        //转换后的格式
        exportSession.outputFileType = AVFileTypeMPEG4;
        //异步导出
        [exportSession exportAsynchronouslyWithCompletionHandler:^{
            // 如果导出的状态为完成
            if ([exportSession status] == AVAssetExportSessionStatusCompleted) {
                //NSLog(@"视频压缩成功,压缩后大小 %f MB",[self fileSize:[self compressedURL]]);
                if (saveState) {
                    [self saveVideo:[self compressedURL]];//保存视频到相册
                }
                //压缩成功视频流回调回去
                successCompress([NSData dataWithContentsOfURL:[self compressedURL]].length > 0?[NSData dataWithContentsOfURL:[self compressedURL]]:nil);
            }else{
                //压缩失败的回调
                successCompress(nil);
            }
        }];
    }
}

#pragma mark 保存压缩
- (NSURL *)compressedURL
{
    return [NSURL fileURLWithPath:[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true) lastObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp4",self.videoName]]];
}

#pragma mark 计算视频大小
- (CGFloat)fileSize:(NSURL *)path
{
    return [[NSData dataWithContentsOfURL:path] length]/1024.00 /1024.00;
}

#pragma mark 保存视频到相册
- (void)saveVideo:(NSURL *)outputFileURL
{
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    [library writeVideoAtPathToSavedPhotosAlbum:outputFileURL completionBlock:^(NSURL *assetURL, NSError *error) {
        if (error) {
            //(@"保存视频失败:%@",error);
        } else {
            //NSLog(@"保存视频到相册成功");
        }
    }];
}

/**
 *  通过视频的URL,获得视频缩略图
 *  @param url 视频URL
 *  @return首帧缩略图
 */
#pragma mark 获取视频的首帧缩略图
- (UIImage *)imageWithVideoURL:(NSURL *)url
{
    NSDictionary *opts = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:AVURLAssetPreferPreciseDurationAndTimingKey];
    AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:url options:opts];
    // 根据asset构造一张图
    AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:urlAsset];
    // 设定缩略图的方向
    // 如果不设定,可能会在视频旋转90/180/270°时,获取到的缩略图是被旋转过的,而不是正向的(自己的理解)
    generator.appliesPreferredTrackTransform = YES;
    // 设置图片的最大size(分辨率)
    generator.maximumSize = CGSizeMake(600, 450);
    NSError *error = nil;
    // 根据时间,获得第N帧的图片
    // CMTimeMake(a, b)可以理解为获得第a/b秒的frame
    CGImageRef img = [generator copyCGImageAtTime:CMTimeMake(0, 10000) actualTime:NULL error:&error];
    UIImage *image = [UIImage imageWithCGImage: img];
    return image;
}

@end

---------------------------------总结一下-------------------------------
本菜鸟的一些总结,当然分享出来的跟自己项目的做了适当的修改,目的就是分享共勉,希望大神多多指教,不喜勿喷,如果可以对您有好处,那本菜鸟已心满意足。
获取视频的首帧缩略图,是因为本菜鸟的项目需要,服务端需要客户端上传一张图片,用于后面页面显示视频的,所有...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容