Как сохранить записанное видео с помощью AVAssetWriter? - PullRequest
0 голосов
/ 20 ноября 2018

Я перепробовал много других блогов и переполнение стека.Я не получил решение для этого, я могу создать собственную камеру с предварительным просмотром.Мне нужно видео с пользовательским кадром, поэтому я использую AVAssetWriter.Но я не могу сохранить записанное видео в документы.Я пытался так,

-(void) initilizeCameraConfigurations {

if(!captureSession) {

    captureSession = [[AVCaptureSession alloc] init];
    [captureSession beginConfiguration];
    captureSession.sessionPreset = AVCaptureSessionPresetHigh;
    self.view.backgroundColor = UIColor.blackColor;
    CGRect bounds = self.view.bounds;
    captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:captureSession];
    captureVideoPreviewLayer.backgroundColor = [UIColor clearColor].CGColor;
    captureVideoPreviewLayer.bounds = self.view.frame;
    captureVideoPreviewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortrait;
    captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    captureVideoPreviewLayer.position = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
    [self.view.layer addSublayer:captureVideoPreviewLayer];
    [self.view bringSubviewToFront:self.controlsBgView];
}


// Add input to session
NSError *err;
videoCaptureDeviceInput  = [AVCaptureDeviceInput deviceInputWithDevice:videoCaptureDevice error:&err];

if([captureSession canAddInput:videoCaptureDeviceInput]) {
    [captureSession addInput:videoCaptureDeviceInput];
}

docPathUrl = [[NSURL alloc] initFileURLWithPath:[self getDocumentsUrl]];

assetWriter = [AVAssetWriter assetWriterWithURL:docPathUrl fileType:AVFileTypeQuickTimeMovie error:&err];
NSParameterAssert(assetWriter);
//assetWriter.movieFragmentInterval = CMTimeMakeWithSeconds(1.0, 1000);

NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                               AVVideoCodecH264, AVVideoCodecKey,
                               [NSNumber numberWithInt:300], AVVideoWidthKey,
                               [NSNumber numberWithInt:300], AVVideoHeightKey,
                               nil];




 writerInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
 writerInput.expectsMediaDataInRealTime = YES;
 writerInput.transform = CGAffineTransformMakeRotation(M_PI);

 NSDictionary *sourcePixelBufferAttributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey,
 [NSNumber numberWithInt:300], kCVPixelBufferWidthKey,
 [NSNumber numberWithInt:300], kCVPixelBufferHeightKey,
 nil];

 assetWriterPixelBufferInput = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput sourcePixelBufferAttributes:sourcePixelBufferAttributesDictionary];


 if([assetWriter canAddInput:writerInput]) {
 [assetWriter addInput:writerInput];
 }

     // Set video stabilization mode to preview layer
AVCaptureVideoStabilizationMode stablilizationMode = AVCaptureVideoStabilizationModeCinematic;
if([videoCaptureDevice.activeFormat isVideoStabilizationModeSupported:stablilizationMode]) {
    [captureVideoPreviewLayer.connection setPreferredVideoStabilizationMode:stablilizationMode];
}


// image output
stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys: AVVideoCodecJPEG, AVVideoCodecKey, nil];
[stillImageOutput setOutputSettings:outputSettings];
[captureSession addOutput:stillImageOutput];

[captureSession commitConfiguration];
if (![captureVideoPreviewLayer.connection isEnabled]) {
    [captureVideoPreviewLayer.connection setEnabled:YES];
}
[captureSession startRunning];

}
-(IBAction)startStopVideoRecording:(id)sender {

if(captureSession) {
    if(isVideoRecording) {
        [writerInput markAsFinished];

        [assetWriter finishWritingWithCompletionHandler:^{
            NSLog(@"Finished writing...checking completion status...");
            if (assetWriter.status != AVAssetWriterStatusFailed && assetWriter.status == AVAssetWriterStatusCompleted)
            {
                // Video saved
            } else
            {
                NSLog(@"#123 Video writing failed: %@", assetWriter.error);
            }

        }];

    } else {

        [assetWriter startWriting];
        [assetWriter startSessionAtSourceTime:kCMTimeZero];
        isVideoRecording = YES;

    }
}
}
-(NSString *) getDocumentsUrl {

NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
docPath = [[docPath stringByAppendingPathComponent:@"Movie"] stringByAppendingString:@".mov"];
if([[NSFileManager defaultManager] fileExistsAtPath:docPath]) {
    NSError *err;
    [[NSFileManager defaultManager] removeItemAtPath:docPath error:&err];
}
NSLog(@"Movie path : %@",docPath);
return docPath;


}


@end

Поправьте меня, если что-то не так.Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 22 ноября 2018

Концептуально, вы должны выделить основные функциональные области: одну, которая генерирует видеокадры - это AVCaptureSession и все, что к ней прикреплено, и другую, которая записывает эти кадры в файл - в вашем случае AVAssetWriterс подключенными входами.
Проблема с вашим кодом: между этими двумя нет связи.Никакие видеокадры / изображения, выходящие из сеанса захвата, не передаются на входы средства записи ресурсов.

Кроме того, метод AVCaptureStillImageOutput -captureStillImageAsynchronouslyFromConnection:completionHandler: нигде не вызывается, поэтому сеанс захвата фактически не создает кадров.

Итак, как минимум, реализуйте что-то вроде этого:

-(IBAction)captureStillImageAndAppend:(id)sender
{
    [stillImageOutput captureStillImageAsynchronouslyFromConnection:stillImageOutput.connections.firstObject completionHandler:
        ^(CMSampleBufferRef imageDataSampleBuffer, NSError* error)
        {
            // check error, omitted here
            if (CMTIME_IS_INVALID( startTime)) // startTime is an ivar
                [assetWriter startSessionAtSourceTime:(startTime = CMSampleBufferGetPresentationTimeStamp( imageDataSampleBuffer))];
            [writerInput appendSampleBuffer:imageDataSampleBuffer];
        }];
}

Удалите AVAssetWriterInputPixelBufferAdaptor, он не используется.

Но есть проблемы с AVCaptureStillImageOutput:

  • он предназначен только для создания неподвижных изображений, а не видео

  • он должен быть настроен на создание несжатых выборочных буферов, если вход средства записи ресурсанастроен для сжатия добавленных примеров буферов (stillImageOutput.outputSettings = @{ (NSString*)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)};)

  • устарел под iOS

Если вы действительно хотите создать видео, каквместо последовательности неподвижных изображений вместо AVCaptureStillImageOutput добавьте AVCaptureVideoDataOutput к сеансу захвата.Для вывода примеров буфера необходимы делегат и очередь последовательной отправки.Делегат должен реализовать что-то вроде этого:

-(void)captureOutput:(AVCaptureOutput*)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection
{
    if (CMTIME_IS_INVALID( startTime)) // startTime is an ivar
        [assetWriter startSessionAtSourceTime:(startTime = CMSampleBufferGetPresentationTimeStamp( sampleBuffer))];
    [writerInput appendSampleBuffer:sampleBuffer];
}

Обратите внимание, что

  • вы захотите убедиться, что AVCaptureVideoDataOutput выводит только кадры, когда высобственно запись;добавьте / удалите его из сеанса захвата или включите / отключите его подключение в действии startStopVideoRecording

  • сбросьте startTime в kCMTimeInvalid перед началом другой записи

0 голосов
/ 21 ноября 2018

Вы не говорите, что на самом деле идет не так, но две вещи выглядят неправильно с вашим кодом:

docPath = [[docPath stringByAppendingPathComponent:@"Movie"] stringByAppendingString:@".mov"];

выглядит так, как будто он создает нежелательный путь, подобный этому @"/path/Movie/.mov", когда вы хотите это:

docPath = [docPath stringByAppendingPathComponent:@"Movie.mov"];

И ваш график неверен.Ваше средство записи активов начинается в момент времени 0, но sampleBuffer s начинается в CMSampleBufferGetPresentationTimestamp(sampleBuffer) > 0, поэтому вместо этого сделайте следующее:

-(void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
    if(firstSampleBuffer) {
        [assetWriter startSessionAtSourceTime:CMSampleBufferGetPresentationTimestamp(sampleBuffer)];
    }

    [writerInput appendSampleBuffer:sampleBuffer];

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...