2014-01-12 2 views
9

Я использую AVCaptureMovieFileOutput для записи некоторого видео. У меня есть слой предварительного просмотра, отображаемый с помощью AVLayerVideoGravityResizeAspectFill, который слегка масштабируется. Проблема заключается в том, что окончательное видео больше, содержащее дополнительное изображение, которое не помещалось на экране во время предварительного просмотра.Обрезка видео AVAsset с AVFoundation

Это предварительный просмотр и получившееся видео

enter image description hereenter image description here

Есть ли способ, я могу указать CGRect, что я хочу, чтобы вырезать из видео с помощью AVAssetExportSession?

EDIT ----

Когда я применить CGAffineTransformScale к AVAssetTrack она увеличивает масштаб видео, и с набором AVMutableVideoCompositionrenderSize к view.bounds он обрезает от концов. Отлично, осталось всего одну проблему. Ширина видео не растягивается до правильной ширины, она просто заполняется черным.

EDIT 2 ---- Предлагаемый вопрос/ответ является неполным ..

Некоторые из моего кода:

В моем - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error методе я это обрезать и изменять размер видео.

- (void)flipAndSave:(NSURL *)videoURL withCompletionBlock:(void(^)(NSURL *returnURL))completionBlock 
{ 
    AVURLAsset *firstAsset = [AVURLAsset assetWithURL:videoURL]; 

    // 1 - Create AVMutableComposition object. This object will hold your AVMutableCompositionTrack instances. 
    AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init]; 
    // 2 - Video track 
    AVMutableCompositionTrack *firstTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo 
                     preferredTrackID:kCMPersistentTrackID_Invalid]; 
    [firstTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, firstAsset.duration) 
         ofTrack:[[firstAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:kCMTimeZero error:nil]; 

    // 2.1 - Create AVMutableVideoCompositionInstruction 
    AVMutableVideoCompositionInstruction *mainInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; 
    mainInstruction.timeRange = CMTimeRangeMake(CMTimeMakeWithSeconds(0, 600), firstAsset.duration); 

    // 2.2 - Create an AVMutableVideoCompositionLayerInstruction for the first track 
    AVMutableVideoCompositionLayerInstruction *firstlayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:firstTrack]; 
    AVAssetTrack *firstAssetTrack = [[firstAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; 
    UIImageOrientation firstAssetOrientation_ = UIImageOrientationUp; 
    BOOL isFirstAssetPortrait_ = NO; 
    CGAffineTransform firstTransform = firstAssetTrack.preferredTransform; 
    if (firstTransform.a == 0 && firstTransform.b == 1.0 && firstTransform.c == -1.0 && firstTransform.d == 0) { 
     firstAssetOrientation_ = UIImageOrientationRight; 
     isFirstAssetPortrait_ = YES; 
    } 
    if (firstTransform.a == 0 && firstTransform.b == -1.0 && firstTransform.c == 1.0 && firstTransform.d == 0) { 
     firstAssetOrientation_ = UIImageOrientationLeft; 
     isFirstAssetPortrait_ = YES; 
    } 
    if (firstTransform.a == 1.0 && firstTransform.b == 0 && firstTransform.c == 0 && firstTransform.d == 1.0) { 
     firstAssetOrientation_ = UIImageOrientationUp; 

    } 
    if (firstTransform.a == -1.0 && firstTransform.b == 0 && firstTransform.c == 0 && firstTransform.d == -1.0) { 
     firstAssetOrientation_ = UIImageOrientationDown; 
    } 
// [firstlayerInstruction setTransform:firstAssetTrack.preferredTransform atTime:kCMTimeZero]; 

// [firstlayerInstruction setCropRectangle:self.view.bounds atTime:kCMTimeZero]; 





    CGFloat scale = [self getScaleFromAsset:firstAssetTrack]; 

    firstTransform = CGAffineTransformScale(firstTransform, scale, scale); 

    [firstlayerInstruction setTransform:firstTransform atTime:kCMTimeZero]; 

    // 2.4 - Add instructions 
    mainInstruction.layerInstructions = [NSArray arrayWithObjects:firstlayerInstruction,nil]; 
    AVMutableVideoComposition *mainCompositionInst = [AVMutableVideoComposition videoComposition]; 
    mainCompositionInst.instructions = [NSArray arrayWithObject:mainInstruction]; 
    mainCompositionInst.frameDuration = CMTimeMake(1, 30); 

// CGSize videoSize = firstAssetTrack.naturalSize; 
    CGSize videoSize = self.view.bounds.size; 
    BOOL isPortrait_ = [self isVideoPortrait:firstAsset]; 
    if(isPortrait_) { 
     videoSize = CGSizeMake(videoSize.height, videoSize.width); 
    } 
    NSLog(@"%@", NSStringFromCGSize(videoSize)); 
    mainCompositionInst.renderSize = videoSize; 




    // 3 - Audio track 
    AVMutableCompositionTrack *AudioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio 
                     preferredTrackID:kCMPersistentTrackID_Invalid]; 
    [AudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, firstAsset.duration) 
         ofTrack:[[firstAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:kCMTimeZero error:nil]; 

    // 4 - Get path 
    NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"cutoutput.mov"]; 
    NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:outputPath]; 
    NSFileManager *manager = [[NSFileManager alloc] init]; 
    if ([manager fileExistsAtPath:outputPath]) 
    { 
     [manager removeItemAtPath:outputPath error:nil]; 
    } 
    // 5 - Create exporter 
    AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition 
                     presetName:AVAssetExportPresetHighestQuality]; 
    exporter.outputURL=outputURL; 
    exporter.outputFileType = AVFileTypeQuickTimeMovie; 
    exporter.shouldOptimizeForNetworkUse = YES; 
    exporter.videoComposition = mainCompositionInst; 
    [exporter exportAsynchronouslyWithCompletionHandler:^{ 
     switch ([exporter status]) 
     { 
      case AVAssetExportSessionStatusFailed: 
       NSLog(@"Export failed: %@ : %@", [[exporter error] localizedDescription], [exporter error]); 
       completionBlock(nil); 

       break; 
      case AVAssetExportSessionStatusCancelled: 

       NSLog(@"Export canceled"); 
       completionBlock(nil); 

       break; 
      default: { 
       NSURL *outputURL = exporter.outputURL; 
       dispatch_async(dispatch_get_main_queue(), ^{ 
        completionBlock(outputURL); 
       }); 

       break; 
      } 
     } 
    }]; 
} 
+0

Возможный дубликат [Как использовать AVFoundation для обрезки видео] (http://stackoverflow.com/questions/5198245/how-do-i-use-avfoundation-to-crop-a-video) – matt

+1

I раньше смотрел этот код. Даже ответы на ответ говорят, что они неполны. – Darren

+0

OK, извините за то. Вы можете показать свой фактический код ... – matt

ответ

19

Вот моя интерпретация вашего вопроса: Вы видеосъемка на устройстве с соотношением экрана 4: 3, при этом ваш AVCaptureVideoPreviewLayer составляет 4: 3, но устройство ввода видео захватывает видео в формате 16: 9 поэтому результирующее видео «больше», чем показано в предварительном просмотре.

Если вы просто хотите обрезать лишние пиксели, не попавшие в предварительный просмотр, тогда ознакомьтесь с этим http://www.netwalk.be/article/record-square-video-ios. В этой статье показано, как обрезать видео в квадрат. Однако вам потребуется всего несколько модификаций для обрезки до 4: 3. Я пошел и протестировал это, вот изменения, которые я сделал:

Как только у вас есть AVAssetTrack для видео, вам нужно будет рассчитать новую высоту.

// we convert the captured height i.e. 1080 to a 4:3 screen ratio and get the new height 
CGFloat newHeight = clipVideoTrack.naturalSize.height/3*4; 

Затем измените эти две строки, используя newHeight.

videoComposition.renderSize = CGSizeMake(clipVideoTrack.naturalSize.height, newHeight); 

CGAffineTransform t1 = CGAffineTransformMakeTranslation(clipVideoTrack.naturalSize.height, -(clipVideoTrack.naturalSize.width - newHeight)/2); 

Так что мы сделали здесь установить RenderSize в соотношении 4: 3 - точное измерение основано на устройстве ввода. Затем мы используем CGAffineTransform для перевода позиции видео, чтобы то, что мы видели в AVCaptureVideoPreviewLayer, является тем, что отображается в нашем файле.

Редактировать: Если вы хотите собрать все это вместе и обрезать видео на основе соотношения экрана устройства (3: 2, 4: 3, 16: 9) и учитывать ориентацию видео, нам нужно добавить несколько вещей.

Сначала здесь модифицированный пример кода с несколькими критическими изменениями:

// output file 
NSString* docFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; 
NSString* outputPath = [docFolder stringByAppendingPathComponent:@"output2.mov"]; 
if ([[NSFileManager defaultManager] fileExistsAtPath:outputPath]) 
    [[NSFileManager defaultManager] removeItemAtPath:outputPath error:nil]; 

// input file 
AVAsset* asset = [AVAsset assetWithURL:outputFileURL]; 

AVMutableComposition *composition = [AVMutableComposition composition]; 
[composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; 

// input clip 
AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; 

// crop clip to screen ratio 
UIInterfaceOrientation orientation = [self orientationForTrack:asset]; 
BOOL isPortrait = (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) ? YES: NO; 
CGFloat complimentSize = [self getComplimentSize:videoTrack.naturalSize.height]; 
CGSize videoSize; 

if(isPortrait) { 
    videoSize = CGSizeMake(videoTrack.naturalSize.height, complimentSize); 
} else { 
    videoSize = CGSizeMake(complimentSize, videoTrack.naturalSize.height); 
} 

AVMutableVideoComposition* videoComposition = [AVMutableVideoComposition videoComposition]; 
videoComposition.renderSize = videoSize; 
videoComposition.frameDuration = CMTimeMake(1, 30); 

AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; 
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60, 30)); 

// rotate and position video 
AVMutableVideoCompositionLayerInstruction* transformer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack]; 

CGFloat tx = (videoTrack.naturalSize.width-complimentSize)/2; 
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationLandscapeRight) { 
    // invert translation 
    tx *= -1; 
} 

// t1: rotate and position video since it may have been cropped to screen ratio 
CGAffineTransform t1 = CGAffineTransformTranslate(videoTrack.preferredTransform, tx, 0); 
// t2/t3: mirror video horizontally 
CGAffineTransform t2 = CGAffineTransformTranslate(t1, isPortrait?0:videoTrack.naturalSize.width, isPortrait?videoTrack.naturalSize.height:0); 
CGAffineTransform t3 = CGAffineTransformScale(t2, isPortrait?1:-1, isPortrait?-1:1); 

[transformer setTransform:t3 atTime:kCMTimeZero]; 
instruction.layerInstructions = [NSArray arrayWithObject: transformer]; 
videoComposition.instructions = [NSArray arrayWithObject: instruction]; 

// export 
exporter = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetHighestQuality] ; 
exporter.videoComposition = videoComposition; 
exporter.outputURL=[NSURL fileURLWithPath:outputPath]; 
exporter.outputFileType=AVFileTypeQuickTimeMovie; 

[exporter exportAsynchronouslyWithCompletionHandler:^(void){ 
    NSLog(@"Exporting done!"); 

    // added export to library for testing 
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; 
    if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:[NSURL fileURLWithPath:outputPath]]) { 
     [library writeVideoAtPathToSavedPhotosAlbum:[NSURL fileURLWithPath:outputPath] 
            completionBlock:^(NSURL *assetURL, NSError *error) { 
      NSLog(@"Saved to album"); 
      if (error) { 

      } 
     }]; 
    } 
}]; 

Что мы добавили вот вызов, чтобы получить новый размер визуализации видео, основанный на кадрирование его размеры формата экрана.Как только мы обрезаем размер вниз, нам нужно перевести позицию для повторного размещения видео. Поэтому мы берем его ориентацию, чтобы переместить его в правильном направлении. Это позволит устранить проблему вне центра, которую мы видели с помощью UIInterfaceOrientationLandscapeLeft. Наконец CGAffineTransform t2, t3 зеркало видео по горизонтали.

А вот два новых метода, которые делают это случиться:

- (CGFloat)getComplimentSize:(CGFloat)size { 
    CGRect screenRect = [[UIScreen mainScreen] bounds]; 
    CGFloat ratio = screenRect.size.height/screenRect.size.width; 

    // we have to adjust the ratio for 16:9 screens 
    if (ratio == 1.775) ratio = 1.77777777777778; 

    return size * ratio; 
} 

- (UIInterfaceOrientation)orientationForTrack:(AVAsset *)asset { 
    UIInterfaceOrientation orientation = UIInterfaceOrientationPortrait; 
    NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; 

    if([tracks count] > 0) { 
     AVAssetTrack *videoTrack = [tracks objectAtIndex:0]; 
     CGAffineTransform t = videoTrack.preferredTransform; 

     // Portrait 
     if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0) { 
      orientation = UIInterfaceOrientationPortrait; 
     } 
     // PortraitUpsideDown 
     if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0) { 
      orientation = UIInterfaceOrientationPortraitUpsideDown; 
     } 
     // LandscapeRight 
     if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0) { 
      orientation = UIInterfaceOrientationLandscapeRight; 
     } 
     // LandscapeLeft 
     if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0) { 
      orientation = UIInterfaceOrientationLandscapeLeft; 
     } 
    } 
    return orientation; 
} 

Это довольно прямо вперед. Единственное, что нужно отметить, это то, что в методе getComplimentSize: мы должны вручную отрегулировать соотношение для 16: 9, так как разрешение iPhone5 + математически застенчиво истинно 16: 9.

+0

Вау, спасибо. Это приближает меня к тому, что мне нужно. В вашем коде есть 2 проблемы. 1, Это отлично работает на iPad в портрете, но на iPhone 5 экран не 4/3, поэтому видео меньше. Можем ли мы использовать размер экрана вместо отношения? 2, При записи в Landscape, видео поворачивается, что означает, что он всегда отображается сбоку. – Darren

+1

@ Darren Я расширил свой первоначальный ответ, чтобы включить альтернативные отношения экрана и ориентацию. – Matt

+0

Спасибо Мэтту, отлично работает :) Было бы слишком много беспокоиться о том, как добавить к нему флип-трансформу? Поэтому он переворачивается горизонтально. Когда я пытаюсь добавить его, я просто получаю черный экран. Я действительно не понимаю эти преобразования, хотя я читал документы. – Darren

2

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

Экземпляр AVCaptureVideoDataOutput создает видеофрагменты, которые вы можете обрабатывать с использованием других медиа-интерфейсов. Вы можете получить доступ к фреймам с помощью метода делегата captureOutput:didOutputSampleBuffer:fromConnection:.

Конфигурирование сеанса Вы используете предустановку в сеансе, чтобы указать требуемое качество и разрешение изображения. Предустановка - это константа, которая идентифицирует один из нескольких возможных конфигураций; в некоторых случаях фактическая конфигурация конкретного устройства: «Фотосъемка»

https://developer.apple.com/library/mac/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/04_MediaCapture.html

фактические значения этих установок, представляют для различных устройств, см «Saving to a Movie File» и

Если вы хотите установить конфигурации размера конкретной, вы должны проверить, является ли она поддерживается перед установкой его:

if ([session canSetSessionPreset:AVCaptureSessionPreset1280x720]) { 
    session.sessionPreset = AVCaptureSessionPreset1280x720; 
} 
else { 
    // Handle the failure. 
} 
0

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

+1

вы можете предложить, как это сделать? –

Смежные вопросы