iOS開(kāi)發(fā)中音視頻的獲取、壓縮上傳

隨著市場(chǎng)直播的日益發(fā)展,現(xiàn)在市場(chǎng)上媒體流的重要性被越來(lái)越多的開(kāi)發(fā)者重視。本菜鳥(niǎo)在上個(gè)項(xiàng)目的開(kāi)發(fā)中,很是尷尬的也遇到的視頻流以及音頻流的處理、上傳。當(dāng)中也遇到的不少坑,因?yàn)楫?dāng)初的項(xiàng)目是社交類的服務(wù)項(xiàng)目,需要用戶能夠上傳視頻、語(yǔ)音等,所以涉及到音視頻的處理。

時(shí)間過(guò)得很快,2016年馬上過(guò)去,最近又比較閑。所有用有限的時(shí)間總結(jié)一下這一年多來(lái)遇到的一些比較有意思的知識(shí)點(diǎn)與大家共勉,希望各路大神多多指教,不喜勿噴。謝謝。本菜鳥(niǎo)必將再去奮斗。

這邊文章分享一些本菜鳥(niǎo)的對(duì)視頻的一些處理,音頻后面有時(shí)間在上來(lái)分享共勉。

視頻獲取上傳方式
1:直接攝像頭拍攝,壓縮上傳給服務(wù)端
2:從相冊(cè)中選取已有視頻,壓縮上傳給服務(wù)端。

第一種方法好處理,拍攝完直接拿到視頻流和路徑了,這個(gè)應(yīng)該不難。
第二種方法,我在做項(xiàng)目的時(shí)候遇到不少坑(項(xiàng)目兼容7.0以上),所有在獲取視頻流的時(shí)候,8.0以上、8.0以下的方法不一樣,被坑了。

下面直接貼代碼并解釋,但愿有遇到的朋友可以互勉,本菜鳥(niǎo)也是菜鳥(niǎo)一個(gè),做的也不是很好。

-------------------1:直接拍攝視頻,上傳服務(wù)端--------------------

#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分鐘   本項(xiàng)目只允許最長(zhǎng)時(shí)間
        //videoPicker.mediaTypes = mediaTypes;
        videoPicker.mediaTypes = [NSArray arrayWithObject:@"public.movie"];
        [self presentViewController:videoPicker animated:YES completion:nil];
    }else{
        UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"設(shè)備不支持拍照" 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];//未壓縮的視頻流
    //這里是視頻流處理的方法   下面會(huì)解釋
    VideoServer *videoServer = [[VideoServer alloc] init];
    
    NSString *randomName = @"隨便給視頻取個(gè)名字吧";//當(dāng)然自己的項(xiàng)目不是這樣寫(xiě)的,這里為了解釋
    
    [videoServer compressVideo:videoURL andVideoName:randomName andSave:YES successCompress:^(NSData *resultData) {
        //這里就是視頻流壓縮后回調(diào)的方法
        if (resultData == nil || resultData.length == 0 ) {
            //如果壓縮失敗,我就把未壓縮的視頻流直接上傳給服務(wù)端咯
            [self postFormData:beforeVideoData AndName:[NSString stringWithFormat:@"%@%@",randomName,@".mp4"]];
        }else{
            //壓縮成功的視頻流   上傳給服務(wù)端
            [self postFormData:resultData AndName:[NSString stringWithFormat:@"%@%@",randomName,@".mp4"]];
        }//這里我發(fā)現(xiàn)拍攝的視頻,壓縮前壓縮后大小差不多,會(huì)不會(huì)是拍攝完蘋(píng)果已經(jīng)做處理?
    }];
    [picker dismissViewControllerAnimated:YES completion:nil];
}

-------------------2:從相冊(cè)選取視頻,上傳服務(wù)端--------------------

#pragma mark 用戶選擇好了視頻   這里項(xiàng)目需求用到了一個(gè)第三方的選取照片和視頻《TZImagePickerController》,感謝作者,但是在使用的時(shí)候也遇到有bug,不能滿足項(xiàng)目需求,自己做了部分修改,謝謝作者分享這個(gè)選取照片視頻的
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingVideo:(UIImage *)coverImage sourceAssets:(id)asset
{
    NSString *randomName = @"隨便給視頻取個(gè)名字吧";//當(dāng)然自己的項(xiàng)目不是這樣寫(xiě)的,這里為了解釋
    //下面本菜鳥(niǎo)遇到的坑-->視頻流路徑竟然不一樣,分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{
                //如果壓縮成功  就用壓縮的上傳服務(wù)端,但是注意了   這里如果不用異步的,上傳的時(shí)候程序掛了
                
                //ios 8 以下一定必須加入這個(gè)再去請(qǐng)求,否則會(huì)掛
                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 是否保存視頻到相冊(cè)
{
    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];//設(shè)置壓縮后視頻流導(dǎo)出的路徑
        exportSession.shouldOptimizeForNetworkUse = true;
        //轉(zhuǎn)換后的格式
        exportSession.outputFileType = AVFileTypeMPEG4;
        //異步導(dǎo)出
        [exportSession exportAsynchronouslyWithCompletionHandler:^{
            // 如果導(dǎo)出的狀態(tài)為完成
            if ([exportSession status] == AVAssetExportSessionStatusCompleted) {
                //NSLog(@"視頻壓縮成功,壓縮后大小 %f MB",[self fileSize:[self compressedURL]]);
                if (saveState) {
                    [self saveVideo:[self compressedURL]];//保存視頻到相冊(cè)
                }
                //壓縮成功視頻流回調(diào)回去
                successCompress([NSData dataWithContentsOfURL:[self compressedURL]].length > 0?[NSData dataWithContentsOfURL:[self compressedURL]]:nil);
            }else{
                //壓縮失敗的回調(diào)
                successCompress(nil);
            }
        }];
    }
}

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

#pragma mark 計(jì)算視頻大小
- (CGFloat)fileSize:(NSURL *)path
{
    return [[NSData dataWithContentsOfURL:path] length]/1024.00 /1024.00;
}

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

/**
 *  通過(guò)視頻的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];
    // 根據(jù)asset構(gòu)造一張圖
    AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:urlAsset];
    // 設(shè)定縮略圖的方向
    // 如果不設(shè)定,可能會(huì)在視頻旋轉(zhuǎn)90/180/270°時(shí),獲取到的縮略圖是被旋轉(zhuǎn)過(guò)的,而不是正向的(自己的理解)
    generator.appliesPreferredTrackTransform = YES;
    // 設(shè)置圖片的最大size(分辨率)
    generator.maximumSize = CGSizeMake(600, 450);
    NSError *error = nil;
    // 根據(jù)時(shí)間,獲得第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

---------------------------------總結(jié)一下-------------------------------
本菜鳥(niǎo)的一些總結(jié),當(dāng)然分享出來(lái)的跟自己項(xiàng)目的做了適當(dāng)?shù)男薷模康木褪欠窒砉裁悖M笊穸喽嘀附蹋幌参饑姡绻梢詫?duì)您有好處,那本菜鳥(niǎo)已心滿意足。
獲取視頻的首幀縮略圖,是因?yàn)楸静锁B(niǎo)的項(xiàng)目需要,服務(wù)端需要客戶端上傳一張圖片,用于后面頁(yè)面顯示視頻的,所有...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容