IT TIP

UIImagePickerController로 만든 동영상의 파일 크기를 줄이려면 어떻게해야합니까?

itqueen 2020. 12. 28. 22:22
반응형

UIImagePickerController로 만든 동영상의 파일 크기를 줄이려면 어떻게해야합니까?


사용자가 동영상을 녹화 한 UIImagePickerController다음 YouTube에 업로드 할 수있는 앱이 있습니다 . 문제는 동영상의 UIImagePickerController길이가 5 초에 불과하더라도 생성 되는 동영상 파일 이 거대 하다는 것 입니다. 예를 들어 5 초 길이의 비디오는 16-20MB입니다. 동영상을 540 또는 720 화질로 유지하고 싶지만 파일 크기를 줄이고 싶습니다.

나는 AVFoundation을 실험하고 AVAssetExportSession있으며 더 작은 파일 크기를 얻으려고 노력했습니다. 다음 코드를 시도했습니다.

AVAsset *video = [AVAsset assetWithURL:videoURL];
AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:video presetName:AVAssetExportPresetPassthrough];
exportSession.shouldOptimizeForNetworkUse = YES;
exportSession.outputFileType = AVFileTypeMPEG4;
exportSession.outputURL = [pathToSavedVideosDirectory URLByAppendingPathComponent:@"vid1.mp4"];
[exportSession exportAsynchronouslyWithCompletionHandler:^{
    NSLog(@"done processing video!");
}];

그러나 이것은 파일 크기를 전혀 줄이지 않았습니다 . Apple의 사진 앱에서 "YouTube에서 공유" 를 선택 하면 동영상 파일을 자동으로 처리하여 업로드하기에 충분히 작기 때문에 제가하는 일이 가능하다는 것을 알고 있습니다. 내 앱에서 동일한 작업을하고 싶습니다.

어떻게하면 되나요?


를 사용 AVCaptureSession하여 AVAssetWriter다음 같이 압축 설정을 지정할 수 있습니다.

NSDictionary *settings = @{AVVideoCodecKey:AVVideoCodecH264,
                           AVVideoWidthKey:@(video_width),
                           AVVideoHeightKey:@(video_height),
                           AVVideoCompressionPropertiesKey:
                               @{AVVideoAverageBitRateKey:@(desired_bitrate),
                                 AVVideoProfileLevelKey:AVVideoProfileLevelH264Main31, /* Or whatever profile & level you wish to use */
                                 AVVideoMaxKeyFrameIntervalKey:@(desired_keyframe_interval)}};

AVAssetWriterInput* writer_input = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:settings];

편집 : UIImagePicker애초에 영화를 만들기 위해 를 사용한다고 주장한다면 트랜스 코딩을 하기 위해 AVAssetReader's copyNextSampleBufferAVAssetWriter's appendSampleBuffer방법을 사용해야 할 것 입니다.


yourfriendzak이 옳습니다 : 설정 cameraUI.videoQuality = UIImagePickerControllerQualityTypeLow;은 여기서 해결책이 아닙니다. 해결책은 jgh가 제안하는 데이터 전송률 또는 비트 전송률을 줄이는 것입니다.

세 가지 방법이 있습니다. 첫 번째 메서드는 UIImagePicker대리자 메서드를 처리합니다 .

// For responding to the user accepting a newly-captured picture or movie
- (void) imagePickerController: (UIImagePickerController *) picker didFinishPickingMediaWithInfo: (NSDictionary *) info {

// Handle movie capture
NSURL *movieURL = [info objectForKey:
                            UIImagePickerControllerMediaURL];

NSURL *uploadURL = [NSURL fileURLWithPath:[[NSTemporaryDirectory() stringByAppendingPathComponent:[self randomString]] stringByAppendingString:@".mp4"]];

// Compress movie first
[self convertVideoToLowQuailtyWithInputURL:movieURL outputURL:uploadURL];
}

두 번째 방법은 비디오를 더 낮은 크기가 아닌 더 낮은 비트 전송률로 변환합니다.

- (void)convertVideoToLowQuailtyWithInputURL:(NSURL*)inputURL
                               outputURL:(NSURL*)outputURL
{
//setup video writer
AVAsset *videoAsset = [[AVURLAsset alloc] initWithURL:inputURL options:nil];

AVAssetTrack *videoTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];

CGSize videoSize = videoTrack.naturalSize;

NSDictionary *videoWriterCompressionSettings =  [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:1250000], AVVideoAverageBitRateKey, nil];

NSDictionary *videoWriterSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecH264, AVVideoCodecKey, videoWriterCompressionSettings, AVVideoCompressionPropertiesKey, [NSNumber numberWithFloat:videoSize.width], AVVideoWidthKey, [NSNumber numberWithFloat:videoSize.height], AVVideoHeightKey, nil];

AVAssetWriterInput* videoWriterInput = [AVAssetWriterInput
                                         assetWriterInputWithMediaType:AVMediaTypeVideo
                                         outputSettings:videoWriterSettings];

videoWriterInput.expectsMediaDataInRealTime = YES;

videoWriterInput.transform = videoTrack.preferredTransform;

AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:outputURL fileType:AVFileTypeQuickTimeMovie error:nil];

[videoWriter addInput:videoWriterInput];

//setup video reader
NSDictionary *videoReaderSettings = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] forKey:(id)kCVPixelBufferPixelFormatTypeKey];

AVAssetReaderTrackOutput *videoReaderOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:videoTrack outputSettings:videoReaderSettings];

AVAssetReader *videoReader = [[AVAssetReader alloc] initWithAsset:videoAsset error:nil];

[videoReader addOutput:videoReaderOutput];

//setup audio writer
AVAssetWriterInput* audioWriterInput = [AVAssetWriterInput
                                        assetWriterInputWithMediaType:AVMediaTypeAudio
                                        outputSettings:nil];

audioWriterInput.expectsMediaDataInRealTime = NO;

[videoWriter addInput:audioWriterInput];

//setup audio reader
AVAssetTrack* audioTrack = [[videoAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];

AVAssetReaderOutput *audioReaderOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:audioTrack outputSettings:nil];

AVAssetReader *audioReader = [AVAssetReader assetReaderWithAsset:videoAsset error:nil];

[audioReader addOutput:audioReaderOutput];    

[videoWriter startWriting];

//start writing from video reader
[videoReader startReading];

[videoWriter startSessionAtSourceTime:kCMTimeZero];

dispatch_queue_t processingQueue = dispatch_queue_create("processingQueue1", NULL);

[videoWriterInput requestMediaDataWhenReadyOnQueue:processingQueue usingBlock:
 ^{

     while ([videoWriterInput isReadyForMoreMediaData]) {

         CMSampleBufferRef sampleBuffer;

         if ([videoReader status] == AVAssetReaderStatusReading &&
             (sampleBuffer = [videoReaderOutput copyNextSampleBuffer])) {

             [videoWriterInput appendSampleBuffer:sampleBuffer];
             CFRelease(sampleBuffer);
         }

         else {

             [videoWriterInput markAsFinished];

             if ([videoReader status] == AVAssetReaderStatusCompleted) {

                 //start writing from audio reader
                 [audioReader startReading];

                 [videoWriter startSessionAtSourceTime:kCMTimeZero];

                 dispatch_queue_t processingQueue = dispatch_queue_create("processingQueue2", NULL);

                 [audioWriterInput requestMediaDataWhenReadyOnQueue:processingQueue usingBlock:^{

                     while (audioWriterInput.readyForMoreMediaData) {

                         CMSampleBufferRef sampleBuffer;

                         if ([audioReader status] == AVAssetReaderStatusReading &&
                             (sampleBuffer = [audioReaderOutput copyNextSampleBuffer])) {

                            [audioWriterInput appendSampleBuffer:sampleBuffer];
                                    CFRelease(sampleBuffer);
                         }

                         else {

                             [audioWriterInput markAsFinished];

                             if ([audioReader status] == AVAssetReaderStatusCompleted) {

                                 [videoWriter finishWritingWithCompletionHandler:^(){
                                     [self sendMovieFileAtURL:outputURL];
                                 }];

                             }
                         }
                     }

                 }
                  ];
             }
         }
     }
 }
 ];
}

성공하면 세 번째 메서드 sendMovieFileAtURL:가 호출되어 압축 된 비디오를 outputURL서버에 업로드 합니다.

참고 내 프로젝트에서 ARC를 활성화 한 일부 추가해야합니다, 그래서 releaseARC가 당신에 꺼져 있으면 전화를.


UImagePickerController당신이 videoQuality재산 UIImagePickerControllerQualityType유형을 기록 영화뿐만 아니라 당신이 (트랜스 코딩 단계에서 발생) 라이브러리에서 선택 고른하는 사람에 적용됩니다.

또는 라이브러리에없는 기존 자산 (파일)을 처리해야하는 경우 다음 사전 설정을 살펴볼 수 있습니다.

AVAssetExportPresetLowQuality
AVAssetExportPresetMediumQuality
AVAssetExportPresetHighestQuality

AVAssetExportPreset640x480
AVAssetExportPreset960x540
AVAssetExportPreset1280x720
AVAssetExportPreset1920x1080

그 중 하나 클래스의 이니셜 라이저전달합니다 AVAssetExportSession. 나는 당신이 무엇인지에 대한 정확한 설명이없는 한 특정 콘텐츠에 대한 사람들과 재생이 두려워 lowmedium품질이나되는 품질이 사용됩니다 640x480또는에 대한 1280x720사전 설정은. 문서에서 유일하게 유용한 정보는 다음과 같습니다.

장치에 적합한 QuickTime 파일의 사전 설정 이름 내보내기 이러한 내보내기 옵션을 사용하여 현재 장치에 적합한 비디오 크기로 QuickTime .mov 파일을 생성 할 수 있습니다.

내보내기는 더 작은 크기에서 비디오를 확대하지 않습니다. 비디오는 H.264를 사용하여 압축됩니다. 오디오는 AAC를 사용하여 압축됩니다.

일부 장치는 일부 크기를 지원하지 않습니다.

그 외에도 프레임 속도 또는 자유형 크기 등과 같은 품질을 정확하게 제어 한 기억이 없습니다. AVFoundation

내가 틀렸다. 언급 한 모든 매개 변수를 조정하는 방법이 있으며 실제로 AVAssetWriter입니다. UIImage 배열을 동영상으로 내보내려면 어떻게해야합니까?

btw, 여기에 코드 샘플이있는 유사한 질문에 대한 링크가 있습니다. iPhone : 녹화 된 비디오를 프로그래밍 방식으로 압축하여 공유 하시겠습니까?


Erik의 대답은 그가 작성했을 때 정확했을 수 있습니다. 그러나 이제 iOS8에서는 왼쪽과 오른쪽이 충돌하고 몇 시간을 보냈습니다.

당신은 AVAssetWriter와 작업에 박사 학위를 필요로 - 그것은 아닌 사소한 : https://developer.apple.com/library/mac/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/05_Export.html#//apple_ref/doc/uid/ TP40010188-CH9-SW1

비트 전송률 변경과 같은 더 중요한 기능으로 AVAssetExportSession 드롭 인 대체물 인 원하는 작업을 정확하게 수행 할 수있는 놀라운 라이브러리가 있습니다 : https://github.com/rs/SDAVAssetExportSession

사용 방법은 다음과 같습니다.

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{

  SDAVAssetExportSession *encoder = [SDAVAssetExportSession.alloc initWithAsset:[AVAsset assetWithURL:[info objectForKey:UIImagePickerControllerMediaURL]]];
  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  NSString *documentsDirectory = [paths objectAtIndex:0];
  self.myPathDocs =  [documentsDirectory stringByAppendingPathComponent:
                      [NSString stringWithFormat:@"lowerBitRate-%d.mov",arc4random() % 1000]];
  NSURL *url = [NSURL fileURLWithPath:self.myPathDocs];
  encoder.outputURL=url;
  encoder.outputFileType = AVFileTypeMPEG4;
  encoder.shouldOptimizeForNetworkUse = YES;

  encoder.videoSettings = @
  {
  AVVideoCodecKey: AVVideoCodecH264,
  AVVideoCompressionPropertiesKey: @
    {
    AVVideoAverageBitRateKey: @2300000, // Lower bit rate here
    AVVideoProfileLevelKey: AVVideoProfileLevelH264High40,
    },
  };
  encoder.audioSettings = @
  {
  AVFormatIDKey: @(kAudioFormatMPEG4AAC),
  AVNumberOfChannelsKey: @2,
  AVSampleRateKey: @44100,
  AVEncoderBitRateKey: @128000,
  };

  [encoder exportAsynchronouslyWithCompletionHandler:^
  {
    int status = encoder.status;

    if (status == AVAssetExportSessionStatusCompleted)
    {
      AVAssetTrack *videoTrack = nil;
      AVURLAsset *asset = [AVAsset assetWithURL:encoder.outputURL];
      NSArray *videoTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
      videoTrack = [videoTracks objectAtIndex:0];
      float frameRate = [videoTrack nominalFrameRate];
      float bps = [videoTrack estimatedDataRate];
      NSLog(@"Frame rate == %f",frameRate);
      NSLog(@"bps rate == %f",bps/(1024.0 * 1024.0));
      NSLog(@"Video export succeeded");
      // encoder.outputURL <- this is what you want!!
    }
    else if (status == AVAssetExportSessionStatusCancelled)
    {
      NSLog(@"Video export cancelled");
    }
    else
    {
      NSLog(@"Video export failed with error: %@ (%d)", encoder.error.localizedDescription, encoder.error.code);
    }
  }];
}

Erik Wegener 코드가 신속한 3으로 재 작성되었습니다.

class func convertVideoToLowQuailtyWithInputURL(inputURL: NSURL, outputURL: NSURL, onDone: @escaping () -> ()) {
            //setup video writer
            let videoAsset = AVURLAsset(url: inputURL as URL, options: nil)
            let videoTrack = videoAsset.tracks(withMediaType: AVMediaTypeVideo)[0]
            let videoSize = videoTrack.naturalSize
            let videoWriterCompressionSettings = [
                AVVideoAverageBitRateKey : Int(125000)
            ]

            let videoWriterSettings:[String : AnyObject] = [
                AVVideoCodecKey : AVVideoCodecH264 as AnyObject,
                AVVideoCompressionPropertiesKey : videoWriterCompressionSettings as AnyObject,
                AVVideoWidthKey : Int(videoSize.width) as AnyObject,
                AVVideoHeightKey : Int(videoSize.height) as AnyObject
            ]

            let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoWriterSettings)
            videoWriterInput.expectsMediaDataInRealTime = true
            videoWriterInput.transform = videoTrack.preferredTransform
            let videoWriter = try! AVAssetWriter(outputURL: outputURL as URL, fileType: AVFileTypeQuickTimeMovie)
            videoWriter.add(videoWriterInput)
            //setup video reader
            let videoReaderSettings:[String : AnyObject] = [
                kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) as AnyObject
            ]

            let videoReaderOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoReaderSettings)
            let videoReader = try! AVAssetReader(asset: videoAsset)
            videoReader.add(videoReaderOutput)
            //setup audio writer
            let audioWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeAudio, outputSettings: nil)
            audioWriterInput.expectsMediaDataInRealTime = false
            videoWriter.add(audioWriterInput)
            //setup audio reader
            let audioTrack = videoAsset.tracks(withMediaType: AVMediaTypeAudio)[0]
            let audioReaderOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: nil)
            let audioReader = try! AVAssetReader(asset: videoAsset)
            audioReader.add(audioReaderOutput)
            videoWriter.startWriting()





            //start writing from video reader
            videoReader.startReading()
            videoWriter.startSession(atSourceTime: kCMTimeZero)
            let processingQueue = DispatchQueue(label: "processingQueue1")
            videoWriterInput.requestMediaDataWhenReady(on: processingQueue, using: {() -> Void in
                while videoWriterInput.isReadyForMoreMediaData {
                    let sampleBuffer:CMSampleBuffer? = videoReaderOutput.copyNextSampleBuffer();
                    if videoReader.status == .reading && sampleBuffer != nil {
                        videoWriterInput.append(sampleBuffer!)
                    }
                    else {
                        videoWriterInput.markAsFinished()
                        if videoReader.status == .completed {
                            //start writing from audio reader
                            audioReader.startReading()
                            videoWriter.startSession(atSourceTime: kCMTimeZero)
                            let processingQueue = DispatchQueue(label: "processingQueue2")
                            audioWriterInput.requestMediaDataWhenReady(on: processingQueue, using: {() -> Void in
                                while audioWriterInput.isReadyForMoreMediaData {
                                    let sampleBuffer:CMSampleBuffer? = audioReaderOutput.copyNextSampleBuffer()
                                    if audioReader.status == .reading && sampleBuffer != nil {
                                        audioWriterInput.append(sampleBuffer!)
                                    }
                                    else {
                                        audioWriterInput.markAsFinished()
                                        if audioReader.status == .completed {
                                            videoWriter.finishWriting(completionHandler: {() -> Void in
                                                onDone();
                                            })
                                        }
                                    }
                                }
                            })
                        }
                    }
                }
            })
        }

UIImagePickerController다음 중 하나 를 열 때 비디오 품질을 설정할 수 있습니다 .

UIImagePickerControllerQualityType640x480
UIImagePickerControllerQualityTypeLow
UIImagePickerControllerQualityTypeMedium
UIImagePickerControllerQualityTypeHigh
UIImagePickerControllerQualityTypeIFrame960x540
UIImagePickerControllerQualityTypeIFrame1280x720

다음을 열 때 품질 유형을 변경하려면이 코드를 사용해보십시오 UIImagePickerController.

if (([UIImagePickerController isSourceTypeAvailable:
      UIImagePickerControllerSourceTypeCamera] == NO))
    return NO;
UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
cameraUI.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *) kUTTypeMovie, nil];

cameraUI.allowsEditing = NO;
cameraUI.delegate = self;
cameraUI.videoQuality = UIImagePickerControllerQualityTypeLow;//you can change the quality here
[self presentModalViewController:cameraUI animated:YES]; 

Erik Wegener 코드가 신속하게 재 작성되었습니다.

class func convertVideoToLowQuailtyWithInputURL(inputURL: NSURL, outputURL: NSURL, onDone: () -> ()) {
    //setup video writer
    let videoAsset = AVURLAsset(URL: inputURL, options: nil)
    let videoTrack = videoAsset.tracksWithMediaType(AVMediaTypeVideo)[0]
    let videoSize = videoTrack.naturalSize
    let videoWriterCompressionSettings = [
        AVVideoAverageBitRateKey : Int(125000)
    ]

    let videoWriterSettings:[String : AnyObject] = [
        AVVideoCodecKey : AVVideoCodecH264,
        AVVideoCompressionPropertiesKey : videoWriterCompressionSettings,
        AVVideoWidthKey : Int(videoSize.width),
        AVVideoHeightKey : Int(videoSize.height)
    ]

    let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoWriterSettings)
    videoWriterInput.expectsMediaDataInRealTime = true
    videoWriterInput.transform = videoTrack.preferredTransform
    let videoWriter = try! AVAssetWriter(URL: outputURL, fileType: AVFileTypeQuickTimeMovie)
    videoWriter.addInput(videoWriterInput)
    //setup video reader
    let videoReaderSettings:[String : AnyObject] = [
        kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)
    ]

    let videoReaderOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoReaderSettings)
    let videoReader = try! AVAssetReader(asset: videoAsset)
    videoReader.addOutput(videoReaderOutput)
    //setup audio writer
    let audioWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeAudio, outputSettings: nil)
    audioWriterInput.expectsMediaDataInRealTime = false
    videoWriter.addInput(audioWriterInput)
    //setup audio reader
    let audioTrack = videoAsset.tracksWithMediaType(AVMediaTypeAudio)[0]
    let audioReaderOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: nil)
    let audioReader = try! AVAssetReader(asset: videoAsset)
    audioReader.addOutput(audioReaderOutput)
    videoWriter.startWriting()





    //start writing from video reader
    videoReader.startReading()
    videoWriter.startSessionAtSourceTime(kCMTimeZero)
    let processingQueue = dispatch_queue_create("processingQueue1", nil)
    videoWriterInput.requestMediaDataWhenReadyOnQueue(processingQueue, usingBlock: {() -> Void in
        while videoWriterInput.readyForMoreMediaData {
            let sampleBuffer:CMSampleBuffer? = videoReaderOutput.copyNextSampleBuffer();
            if videoReader.status == .Reading && sampleBuffer != nil {
                videoWriterInput.appendSampleBuffer(sampleBuffer!)
            }
            else {
                videoWriterInput.markAsFinished()
                if videoReader.status == .Completed {
                    //start writing from audio reader
                    audioReader.startReading()
                    videoWriter.startSessionAtSourceTime(kCMTimeZero)
                    let processingQueue = dispatch_queue_create("processingQueue2", nil)
                    audioWriterInput.requestMediaDataWhenReadyOnQueue(processingQueue, usingBlock: {() -> Void in
                        while audioWriterInput.readyForMoreMediaData {
                            let sampleBuffer:CMSampleBufferRef? = audioReaderOutput.copyNextSampleBuffer()
                            if audioReader.status == .Reading && sampleBuffer != nil {
                                audioWriterInput.appendSampleBuffer(sampleBuffer!)
                            }
                            else {
                                audioWriterInput.markAsFinished()
                                if audioReader.status == .Completed {
                                    videoWriter.finishWritingWithCompletionHandler({() -> Void in
                                        onDone();
                                    })
                                }
                            }
                        }
                    })
                }
            }
        }
    })
}

비디오 압축을 수행 하는 멋진 사용자 정의 클래스 ( SDAVAssetExportSession )가 있습니다. 링크 에서 다운로드 할 수 있습니다 .

다운로드 후 SDAVAssetExportSession.h 및 SDAVAssetExportSession.m 파일을 프로젝트에 추가 한 다음 아래 코드를 사용하여 압축을 수행합니다. 아래 코드에서 해상도와 비트 전송률을 지정하여 비디오를 압축 할 수 있습니다.

#import "SDAVAssetExportSession.h"


- (void)compressVideoWithInputVideoUrl:(NSURL *) inputVideoUrl
{
    /* Create Output File Url */

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSString *finalVideoURLString = [documentsDirectory stringByAppendingPathComponent:@"compressedVideo.mp4"];
    NSURL *outputVideoUrl = ([[NSURL URLWithString:finalVideoURLString] isFileURL] == 1)?([NSURL URLWithString:finalVideoURLString]):([NSURL fileURLWithPath:finalVideoURLString]); // Url Should be a file Url, so here we check and convert it into a file Url


    SDAVAssetExportSession *compressionEncoder = [SDAVAssetExportSession.alloc initWithAsset:[AVAsset assetWithURL:inputVideoUrl]]; // provide inputVideo Url Here
    compressionEncoder.outputFileType = AVFileTypeMPEG4;
    compressionEncoder.outputURL = outputVideoUrl; //Provide output video Url here
    compressionEncoder.videoSettings = @
    {
    AVVideoCodecKey: AVVideoCodecH264,
    AVVideoWidthKey: @800,   //Set your resolution width here
    AVVideoHeightKey: @600,  //set your resolution height here
    AVVideoCompressionPropertiesKey: @
        {
        AVVideoAverageBitRateKey: @45000, // Give your bitrate here for lower size give low values
        AVVideoProfileLevelKey: AVVideoProfileLevelH264High40,
        },
    };
    compressionEncoder.audioSettings = @
    {
    AVFormatIDKey: @(kAudioFormatMPEG4AAC),
    AVNumberOfChannelsKey: @2,
    AVSampleRateKey: @44100,
    AVEncoderBitRateKey: @128000,
    };

    [compressionEncoder exportAsynchronouslyWithCompletionHandler:^
     {
         if (compressionEncoder.status == AVAssetExportSessionStatusCompleted)
         {
            NSLog(@"Compression Export Completed Successfully");
         }
         else if (compressionEncoder.status == AVAssetExportSessionStatusCancelled)
         {
             NSLog(@"Compression Export Canceled");
         }
         else
         {
              NSLog(@"Compression Failed");

         }
     }];

}

압축을 취소하려면 코드 줄 아래 사용

 [compressionEncoder cancelExport]; //Video compression cancel

내가 지원하고 etayluz 의 응답 SDAVAssetExportSession은 비디오 압축을 할 수있는 멋진 사용자 정의 클래스입니다. 다음은 내 작업 코드입니다. 링크 에서 SDAVAssetExportSession다운로드 할 수 있습니다 .

다운로드 후 SDAVAssetExportSession.h 및 SDAVAssetExportSession.m 파일을 프로젝트에 추가 한 다음 아래 코드를 사용하여 압축을 수행합니다. 아래 코드에서 해상도와 비트 전송률을 지정하여 비디오를 압축 할 수 있습니다.

#import "SDAVAssetExportSession.h"


- (void)compressVideoWithInputVideoUrl:(NSURL *) inputVideoUrl
{
    /* Create Output File Url */

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSString *finalVideoURLString = [documentsDirectory stringByAppendingPathComponent:@"compressedVideo.mp4"];
    NSURL *outputVideoUrl = ([[NSURL URLWithString:finalVideoURLString] isFileURL] == 1)?([NSURL URLWithString:finalVideoURLString]):([NSURL fileURLWithPath:finalVideoURLString]); // Url Should be a file Url, so here we check and convert it into a file Url


    SDAVAssetExportSession *compressionEncoder = [SDAVAssetExportSession.alloc initWithAsset:[AVAsset assetWithURL:inputVideoUrl]]; // provide inputVideo Url Here
    compressionEncoder.outputFileType = AVFileTypeMPEG4;
    compressionEncoder.outputURL = outputVideoUrl; //Provide output video Url here
    compressionEncoder.videoSettings = @
    {
    AVVideoCodecKey: AVVideoCodecH264,
    AVVideoWidthKey: @800,   //Set your resolution width here
    AVVideoHeightKey: @600,  //set your resolution height here
    AVVideoCompressionPropertiesKey: @
        {
        AVVideoAverageBitRateKey: @45000, // Give your bitrate here for lower size give low values
        AVVideoProfileLevelKey: AVVideoProfileLevelH264High40,
        },
    };
    compressionEncoder.audioSettings = @
    {
    AVFormatIDKey: @(kAudioFormatMPEG4AAC),
    AVNumberOfChannelsKey: @2,
    AVSampleRateKey: @44100,
    AVEncoderBitRateKey: @128000,
    };

    [compressionEncoder exportAsynchronouslyWithCompletionHandler:^
     {
         if (compressionEncoder.status == AVAssetExportSessionStatusCompleted)
         {
            NSLog(@"Compression Export Completed Successfully");
         }
         else if (compressionEncoder.status == AVAssetExportSessionStatusCancelled)
         {
             NSLog(@"Compression Export Canceled");
         }
         else
         {
              NSLog(@"Compression Failed");

         }
     }];

}

압축을 취소하려면 코드 줄 아래 사용

 [compressionEncoder cancelExport]; //Video compression cancel

스위프트 4 :

func convertVideoToLowQuailtyWithInputURL(inputURL: NSURL, outputURL: NSURL, completion: @escaping (Bool) -> Void) {

    let videoAsset = AVURLAsset(url: inputURL as URL, options: nil)
    let videoTrack = videoAsset.tracks(withMediaType: AVMediaType.video)[0]
    let videoSize = videoTrack.naturalSize
    let videoWriterCompressionSettings = [
        AVVideoAverageBitRateKey : Int(125000)
    ]

    let videoWriterSettings:[String : AnyObject] = [
        AVVideoCodecKey : AVVideoCodecH264 as AnyObject,
        AVVideoCompressionPropertiesKey : videoWriterCompressionSettings as AnyObject,
        AVVideoWidthKey : Int(videoSize.width) as AnyObject,
        AVVideoHeightKey : Int(videoSize.height) as AnyObject
    ]

    let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoWriterSettings)
    videoWriterInput.expectsMediaDataInRealTime = true
    videoWriterInput.transform = videoTrack.preferredTransform
    let videoWriter = try! AVAssetWriter(outputURL: outputURL as URL, fileType: AVFileType.mov)
    videoWriter.add(videoWriterInput)
    //setup video reader
    let videoReaderSettings:[String : AnyObject] = [
        kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) as AnyObject
    ]

    let videoReaderOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoReaderSettings)
    var videoReader: AVAssetReader!

    do{

        videoReader = try AVAssetReader(asset: videoAsset)
    }
    catch {

        print("video reader error: \(error)")
        completion(false)
    }
    videoReader.add(videoReaderOutput)
    //setup audio writer
    let audioWriterInput = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: nil)
    audioWriterInput.expectsMediaDataInRealTime = false
    videoWriter.add(audioWriterInput)
    //setup audio reader
    let audioTrack = videoAsset.tracks(withMediaType: AVMediaType.audio)[0]
    let audioReaderOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: nil)
    let audioReader = try! AVAssetReader(asset: videoAsset)
    audioReader.add(audioReaderOutput)
    videoWriter.startWriting()

    //start writing from video reader
    videoReader.startReading()
    videoWriter.startSession(atSourceTime: kCMTimeZero)
    let processingQueue = DispatchQueue(label: "processingQueue1")
    videoWriterInput.requestMediaDataWhenReady(on: processingQueue, using: {() -> Void in
        while videoWriterInput.isReadyForMoreMediaData {
            let sampleBuffer:CMSampleBuffer? = videoReaderOutput.copyNextSampleBuffer();
            if videoReader.status == .reading && sampleBuffer != nil {
                videoWriterInput.append(sampleBuffer!)
            }
            else {
                videoWriterInput.markAsFinished()
                if videoReader.status == .completed {
                    //start writing from audio reader
                    audioReader.startReading()
                    videoWriter.startSession(atSourceTime: kCMTimeZero)
                    let processingQueue = DispatchQueue(label: "processingQueue2")
                    audioWriterInput.requestMediaDataWhenReady(on: processingQueue, using: {() -> Void in
                        while audioWriterInput.isReadyForMoreMediaData {
                            let sampleBuffer:CMSampleBuffer? = audioReaderOutput.copyNextSampleBuffer()
                            if audioReader.status == .reading && sampleBuffer != nil {
                                audioWriterInput.append(sampleBuffer!)
                            }
                            else {
                                audioWriterInput.markAsFinished()
                                if audioReader.status == .completed {
                                    videoWriter.finishWriting(completionHandler: {() -> Void in
                                        completion(true)
                                    })
                                }
                            }
                        }
                    })
                }
            }
        }
    })
}

Use exportSession.fileLengthLimit = 1048576 * 10 //10 MB

10MB는 하드 코딩 된 숫자입니다. 필요한 비트 전송률에 따라 사용하십시오.

fileLengthLimit

session should not exceed. Depending on the content of the source asset, it is possible for the output to slightly exceed the file length limit. The length of the output file should be tested if you require that a strict limit be observed before making use of the output. See also maxDuration and timeRange.Indicates the file length that the output of the

ReferenceURL : https://stackoverflow.com/questions/11751883/how-can-i-reduce-the-file-size-of-a-video-created-with-uiimagepickercontroller

반응형