AVAssetWriter输出大文件(即使应用压缩设置时) [英] AVAssetWriter Outputting Large File (even when applying compression settings)

查看:3924
本文介绍了AVAssetWriter输出大文件(即使应用压缩设置时)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个个人iOS项目,需要通过4G连接将全屏视频(长度为15秒)上传到后端。虽然我可以拍摄视频很好,文件的输出大小出来30MB,这让我想我在做一些事情大大错误,当谈到压缩。下面是我用来搭配AssetWriter的代码:

I'm working on a personal iOS project that requires full screen videos (15 seconds in length) to be uploaded to a backend over a 4G connection. While I can take videos just fine, the output size of the file comes out to 30MB which makes me think I'm doing something drastically wrong when it comes to compression. Below is the code I'm using to se up the AssetWriter:

-(void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections
{
    NSLog(@"Started Recording! *******************");
    self.movieWriter = [AVAssetWriter assetWriterWithURL:fileURL fileType:AVFileTypeMPEG4 error:nil];
    [self.movieWriter setShouldOptimizeForNetworkUse:YES];

    NSDictionary *videoCleanApertureSettings = @{
                                                 AVVideoCleanApertureWidthKey: [NSNumber numberWithFloat:self.view.frame.size.width],
                                                 AVVideoCleanApertureHeightKey: [NSNumber numberWithFloat:self.view.frame.size.height],
                                                 AVVideoCleanApertureHorizontalOffsetKey: [NSNumber numberWithInt:10],
                                                 AVVideoCleanApertureVerticalOffsetKey: [NSNumber numberWithInt:10],
                                                 };

    NSDictionary *videoCompressionSettings = @{
                                          AVVideoAverageBitRateKey: [NSNumber numberWithFloat:5000000.0],
                                          AVVideoMaxKeyFrameIntervalKey: [NSNumber numberWithInteger:1],
                                          AVVideoProfileLevelKey: AVVideoProfileLevelH264Baseline30,
                                          AVVideoCleanApertureKey: videoCleanApertureSettings,
                                          };

    NSDictionary *videoSettings = @{AVVideoCodecKey: AVVideoCodecH264,
                                    AVVideoWidthKey: [NSNumber numberWithFloat:self.view.frame.size.width],
                                    AVVideoHeightKey: [NSNumber numberWithFloat:self.view.frame.size.height],
                                    AVVideoCompressionPropertiesKey: videoCompressionSettings,
                                    };

    self.movieWriterVideoInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
    self.movieWriterVideoInput.expectsMediaDataInRealTime = YES;
    [self.movieWriter addInput:self.movieWriterVideoInput];

    NSDictionary *audioSettings = @{AVFormatIDKey: [NSNumber numberWithInteger:kAudioFormatMPEG4AAC],
                                    AVSampleRateKey: [NSNumber numberWithFloat:44100.0],
                                    AVNumberOfChannelsKey: [NSNumber numberWithInteger:1],
                                    };

    self.movieWriterAudioInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:audioSettings];
    self.movieWriterAudioInput.expectsMediaDataInRealTime = YES;
    [self.movieWriter addInput:self.movieWriterAudioInput];


    [self.movieWriter startWriting];
}

-(void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
{
    NSLog(@"Done Recording!");
    [self.movieWriterVideoInput markAsFinished];
    [self.movieWriterAudioInput markAsFinished];
    [self.movieWriter finishWritingWithCompletionHandler:^{
        AVURLAsset *compressedVideoAsset = [[AVURLAsset alloc] initWithURL:self.movieWriter.outputURL options:nil];
        //Upload video to server

    }];
}

对于实际会话的设置,我使用以下代码: / p>

For the setup of the actual session I'm using the following code:

            //Indicate that some changes will be made to the session
            [self.captureSession beginConfiguration];
            self.captureSession.sessionPreset = AVCaptureSessionPresetHigh;

            AVCaptureInput* currentCameraInput = [self.captureSession.inputs objectAtIndex:0];
            for (AVCaptureInput *captureInput in self.captureSession.inputs) {
                [self.captureSession removeInput:captureInput];
            }


            //Get currently selected camera and use for input
            AVCaptureDevice *videoCamera = nil;
            if(((AVCaptureDeviceInput*)currentCameraInput).device.position == AVCaptureDevicePositionBack)
            {
                videoCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
            }
            else
            {
                videoCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
            }

            //Add input to session
            AVCaptureDeviceInput *newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:videoCamera error:nil];
            [self.captureSession addInput:newVideoInput];

            //Add mic input to the session
            AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
            AVCaptureInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:nil];
            [self.captureSession addInput:audioInput];

            //Add movie output to session
            for (AVCaptureOutput *output in self.captureSession.outputs) {
                [self.captureSession removeOutput:output];
            }

            self.movieOutput = [AVCaptureMovieFileOutput new];
            int32_t preferredTimeScale = 30; //Frames per second
            self.movieOutput.maxRecordedDuration = CMTimeMakeWithSeconds(15, preferredTimeScale); //Setting the max video length
            [self.captureSession addOutput:self.movieOutput];

            //Commit all the configuration changes at once
            [self.captureSession commitConfiguration];

我知道如果我将 AVCaptureSessionPresetHigh 更改为不同的预设,最终视频的文件大小,但不幸的是,看起来像 AVCaptureSessionPresetiFrame1280x720 是唯一一个提供了我想要捕获的全帧(这让我的输出大小约20MB,仍然对于4G上传来说太大了)。

I know that if I change AVCaptureSessionPresetHigh to a different preset I can reduce the file size of the final video, but unfortunately is looks like AVCaptureSessionPresetiFrame1280x720 is the only one that provides the full frame I'm trying to capture (which leaves me with an output size of about 20MB and is still too large for 4G uploads).

我花了很多时间来搜索和搜索Stack Overflow上的其他帖子,但我似乎无法找出

I've spent a lot of time googling and searching through other posts on Stack Overflow, but I can't seem to figure out what I'm doing wrong for the life of me and any help at all would be greatly appreciated.

推荐答案

你需要一个博士学位使用AVAssetWriter - 这是不平凡的: https://developer.apple.com/library/mac/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/05_Export.html#//apple_ref/doc/uid/TP40010188- CH9-SW1

You need a PhD to work with AVAssetWriter - it's non-trivial: 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

There's an amazing library for doing exactly what you want which is just an AVAssetExportSession drop-in replacement with more crucial features like changing the bit rate: 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);
    }
  }];
}

这篇关于AVAssetWriter输出大文件(即使应用压缩设置时)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆