2012-11-20 15 views
7

Ich habe mich mit verschiedenen Aspekten des Problems der Steuerung der Videoausrichtung während und nach der Aufnahme auf einem iOS-Gerät beschäftigt. Dank vorheriger Antworten und Dokumentation von Apple konnte ich es herausfinden. Aber jetzt, da ich etwas Video auf eine Website pushen möchte, stoße ich auf bestimmte Probleme. Ich habe dieses Problem insbesondere in this question skizziert, und the proposed solution stellt sich heraus, dass die Ausrichtung Optionen während der Videokodierung festgelegt werden müssen.iOS AVFoundation: Einstellung der Ausrichtung des Videos

Das mag sein, aber ich habe keine Ahnung, wie man das macht. Die Dokumentation zur Einstellung der Ausrichtung bezieht sich auf die richtige Einstellung für die Anzeige auf dem Gerät, und ich habe den Hinweis found here. implementiert. Dieser Hinweis behandelt jedoch nicht die korrekte Ausrichtung von Nicht-Apple-Software wie VLC oder dem Chrome-Browser .

Kann jemand einen Einblick geben, wie man die Ausrichtung auf dem Gerät richtig einstellt, so dass es für alle Anzeigesoftware korrekt angezeigt wird?

+2

Die aktuellen Daten hat immer statisch Orientierung während der Aufnahme. Die Ausrichtung wird im 'preferredTransform'-Wert gespeichert. Also, ich denke, Sie müssen Video exportieren, um die Daten zu drehen. Ich würde mir 'AVAssetExportSession'' AVMutableVideoComposition' 'setTransform: atTime:' anschauen, das könnte helfen. – Davyd

+0

Ich habe eine technische Support-Anfrage in Apple, um dies herauszufinden. Aber ich werde einen Blick darauf werfen, wie Sie vorschlagen. Wäre das ein separater Kodierungsschritt, nachdem das Video aufgenommen wurde? Das könnte rechenintensiv sein ... –

+0

Ja, das wäre ein zusätzlicher Schritt. Es kann jedoch nicht so teuer sein, wenn Sie exportieren, ohne die ursprüngliche Codierung zu ändern. Lassen Sie es mich wissen, wenn Sie eine bessere Lösung finden. – Davyd

Antwort

3

Falls jemand anderes für diese Antwort sucht auch, das ist die Methode, die ich (ein wenig geändert zu vereinfachen) gekocht:

- (void)encodeVideoOrientation:(NSURL *)anOutputFileURL 
{ 
CGAffineTransform rotationTransform; 
CGAffineTransform rotateTranslate; 
CGSize renderSize; 

switch (self.recordingOrientation) 
{ 
    // set these 3 values based on orientation 

} 


AVURLAsset * videoAsset = [[AVURLAsset alloc]initWithURL:anOutputFileURL options:nil]; 

AVAssetTrack *sourceVideoTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; 
AVAssetTrack *sourceAudioTrack = [[videoAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; 

AVMutableComposition* composition = [AVMutableComposition composition]; 

AVMutableCompositionTrack *compositionVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; 
[compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) 
           ofTrack:sourceVideoTrack 
           atTime:kCMTimeZero error:nil]; 
[compositionVideoTrack setPreferredTransform:sourceVideoTrack.preferredTransform]; 

AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio 
                      preferredTrackID:kCMPersistentTrackID_Invalid]; 
[compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) 
           ofTrack:sourceAudioTrack 
           atTime:kCMTimeZero error:nil]; 



AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; 
AVMutableVideoCompositionLayerInstruction *layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTrack]; 
[layerInstruction setTransform:rotateTranslate atTime:kCMTimeZero]; 

AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition]; 
videoComposition.frameDuration = CMTimeMake(1,30); 
videoComposition.renderScale = 1.0; 
videoComposition.renderSize = renderSize; 
instruction.layerInstructions = [NSArray arrayWithObject: layerInstruction]; 
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, videoAsset.duration); 
videoComposition.instructions = [NSArray arrayWithObject: instruction]; 

AVAssetExportSession * assetExport = [[AVAssetExportSession alloc] initWithAsset:composition 
                     presetName:AVAssetExportPresetMediumQuality]; 

NSString* videoName = @"export.mov"; 
NSString *exportPath = [NSTemporaryDirectory() stringByAppendingPathComponent:videoName]; 

NSURL * exportUrl = [NSURL fileURLWithPath:exportPath]; 

if ([[NSFileManager defaultManager] fileExistsAtPath:exportPath]) 
{ 
    [[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil]; 
} 

assetExport.outputFileType = AVFileTypeMPEG4; 
assetExport.outputURL = exportUrl; 
assetExport.shouldOptimizeForNetworkUse = YES; 
assetExport.videoComposition = videoComposition; 

[assetExport exportAsynchronouslyWithCompletionHandler: 
^(void) { 
    switch (assetExport.status) 
    { 
     case AVAssetExportSessionStatusCompleted: 
      //    export complete 
      NSLog(@"Export Complete"); 
      break; 
     case AVAssetExportSessionStatusFailed: 
      NSLog(@"Export Failed"); 
      NSLog(@"ExportSessionError: %@", [assetExport.error localizedDescription]); 
      //    export error (see exportSession.error) 
      break; 
     case AVAssetExportSessionStatusCancelled: 
      NSLog(@"Export Failed"); 
      NSLog(@"ExportSessionError: %@", [assetExport.error localizedDescription]); 
      //    export cancelled 
      break; 
    } 
}]; 

} 

schlecht Dieses Material dokumentiert, leider aber durch Bespannen Beispiele zusammen von anderen SO-Fragen und das Lesen der Header-Dateien, konnte ich das funktionieren. Hoffe, das hilft jedem anderen!

2

Verwendung unter method diese correctorientation nach videoassetorientation in AVMutableVideoComposition

-(AVMutableVideoComposition *) getVideoComposition:(AVAsset *)asset 
{ 
    AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; 
    AVMutableComposition *composition = [AVMutableComposition composition]; 
    AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition]; 
    CGSize videoSize = videoTrack.naturalSize; 
    BOOL isPortrait_ = [self isVideoPortrait:asset]; 
    if(isPortrait_) { 
     NSLog(@"video is portrait "); 
     videoSize = CGSizeMake(videoSize.height, videoSize.width); 
    } 
    composition.naturalSize  = videoSize; 
    videoComposition.renderSize = videoSize; 
    // videoComposition.renderSize = videoTrack.naturalSize; // 
    videoComposition.frameDuration = CMTimeMakeWithSeconds(1/videoTrack.nominalFrameRate, 600); 

    AVMutableCompositionTrack *compositionVideoTrack; 
    compositionVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; 
    [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:videoTrack atTime:kCMTimeZero error:nil]; 
    AVMutableVideoCompositionLayerInstruction *layerInst; 
    layerInst = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack]; 
    [layerInst setTransform:videoTrack.preferredTransform atTime:kCMTimeZero]; 
    AVMutableVideoCompositionInstruction *inst = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; 
    inst.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration); 
    inst.layerInstructions = [NSArray arrayWithObject:layerInst]; 
    videoComposition.instructions = [NSArray arrayWithObject:inst]; 
    return videoComposition; 
} 


-(BOOL) isVideoPortrait:(AVAsset *)asset 
{ 
    BOOL isPortrait = FALSE; 
    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) 
    { 
     isPortrait = YES; 
    } 
    // PortraitUpsideDown 
    if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0) { 

     isPortrait = YES; 
    } 
    // LandscapeRight 
    if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0) 
    { 
     isPortrait = FALSE; 
    } 
    // LandscapeLeft 
    if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0) 
    { 
     isPortrait = FALSE; 
    } 
    } 
    return isPortrait; 
} 
1

Seit iOS 5 setzen Sie gedreht CVPixelBuffers anfordern können mit AVCaptureVideoDataOutput here dokumentiert. Dadurch erhalten Sie die korrekte Ausrichtung, ohne das Video erneut mit AVAssetExportSession erneut verarbeiten zu müssen.

5

In Apples Dokumentation here heißt es:

Clients nun physisch gedreht CVPixelBuffers in ihren AVCaptureVideoDataOutput -captureOutput empfangen: didOutputSampleBuffer: fromConnection: Rückruf delegieren. In früheren iOS-Versionen würde die Frontkamera immer Puffer in AVCaptureVideoOrientationLandscapeLeft liefern, und die nach hinten gerichtete Kamera würde immer Puffer in AVCaptureVideoOrientationLandscapeRight liefern. Alle 4 AVCaptureVideoOrientations werden unterstützt und die Rotation wird hardwarebeschleunigt. Um die Rotation des Puffers anzufordern, ruft ein Client -setVideoOrientation: im Video AVCaptureConnection des AVCaptureVideoDataOutput auf. Beachten Sie, dass bei physisch rotierenden Puffern Performancekosten anfallen, also bei Bedarf nur eine Rotation anfordern. Wenn Sie beispielsweise gedrehtes Video in eine QuickTime-Filmdatei mit AVAssetWriter schreiben möchten, sollten Sie die Eigenschaft -transform auf AVAssetWriterInput setzen, statt die Puffer in AVCaptureVideoDataOutput physisch zu drehen.

Also die veröffentlichte Lösung von Aaron Vegh, die eine AVAssetExportSession verwendet, funktioniert, wird aber nicht benötigt. Wie der Apple-Doc sagt, wenn Sie die Ausrichtung richtig eingestellt haben möchten, so dass sie in Nicht-Apple-Quicktime-Playern wie VLC oder im Web mit Chrome wiedergegeben wird, müssen Sie die Videoausrichtung auf AVCaptureConnection für AVCaptureVideoDataOutput einstellen. Wenn Sie versuchen, es für den AVAssetWriterInput festzulegen, erhalten Sie eine falsche Ausrichtung für Spieler wie VLC und Chrome.

Hier ist mein Code, wo ich es stellen Sie die Aufnahmesitzung während des Einrichtens:

// DECLARED AS PROPERTIES ABOVE 
@property (strong,nonatomic) AVCaptureDeviceInput *audioIn; 
@property (strong,nonatomic) AVCaptureAudioDataOutput *audioOut; 
@property (strong,nonatomic) AVCaptureDeviceInput *videoIn; 
@property (strong,nonatomic) AVCaptureVideoDataOutput *videoOut; 
@property (strong,nonatomic) AVCaptureConnection *audioConnection; 
@property (strong,nonatomic) AVCaptureConnection *videoConnection; 
------------------------------------------------------------------ 
------------------------------------------------------------------ 

-(void)setupCaptureSession{ 
// Setup Session 
self.session = [[AVCaptureSession alloc]init]; 
[self.session setSessionPreset:AVCaptureSessionPreset640x480]; 

// Create Audio connection ---------------------------------------- 
self.audioIn = [[AVCaptureDeviceInput alloc]initWithDevice:[self getAudioDevice] error:nil]; 
if ([self.session canAddInput:self.audioIn]) { 
    [self.session addInput:self.audioIn]; 
} 

self.audioOut = [[AVCaptureAudioDataOutput alloc]init]; 
dispatch_queue_t audioCaptureQueue = dispatch_queue_create("Audio Capture Queue", DISPATCH_QUEUE_SERIAL); 
[self.audioOut setSampleBufferDelegate:self queue:audioCaptureQueue]; 
if ([self.session canAddOutput:self.audioOut]) { 
    [self.session addOutput:self.audioOut]; 
} 
self.audioConnection = [self.audioOut connectionWithMediaType:AVMediaTypeAudio]; 

// Create Video connection ---------------------------------------- 
self.videoIn = [[AVCaptureDeviceInput alloc]initWithDevice:[self videoDeviceWithPosition:AVCaptureDevicePositionBack] error:nil]; 
if ([self.session canAddInput:self.videoIn]) { 
    [self.session addInput:self.videoIn]; 
} 

self.videoOut = [[AVCaptureVideoDataOutput alloc]init]; 
[self.videoOut setAlwaysDiscardsLateVideoFrames:NO]; 
[self.videoOut setVideoSettings:nil]; 
dispatch_queue_t videoCaptureQueue = dispatch_queue_create("Video Capture Queue", DISPATCH_QUEUE_SERIAL); 
[self.videoOut setSampleBufferDelegate:self queue:videoCaptureQueue]; 
if ([self.session canAddOutput:self.videoOut]) { 
    [self.session addOutput:self.videoOut]; 
} 

self.videoConnection = [self.videoOut connectionWithMediaType:AVMediaTypeVideo]; 
// SET THE ORIENTATION HERE ------------------------------------------------- 
[self.videoConnection setVideoOrientation:AVCaptureVideoOrientationPortrait]; 
// -------------------------------------------------------------------------- 

// Create Preview Layer ------------------------------------------- 
AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.session]; 
CGRect bounds = self.videoView.bounds; 
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; 
previewLayer.bounds = bounds; 
previewLayer.position=CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); 
[self.videoView.layer addSublayer:previewLayer]; 

// Start session 
[self.session startRunning]; 

}

+0

Ich würde gerne diese Art von Lösung verwenden. Es würde viele Probleme ansprechen. Die Dokumente weisen auf "Leistungskosten" hin. Haben sich diese Kosten in Ihrer Erfahrung als signifikant erwiesen? Ich bin neugierig auf Presets mit den höchsten Bitraten (z. B. HD-Auflösung bei 120 Bildern pro Sekunde auf einem iPhone 5s)? – otto

9

Schließlich basiert auf den Antworten von @Aaron Vegh und @Prince, ich meine Auflösung heraus Konvertieren // Video

+(void)convertMOVToMp4:(NSString *)movFilePath completion:(void (^)(NSString *mp4FilePath))block{ 


AVURLAsset * videoAsset = [[AVURLAsset alloc]initWithURL:[NSURL fileURLWithPath:movFilePath] options:nil]; 

AVAssetTrack *sourceAudioTrack = [[videoAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; 

AVMutableComposition* composition = [AVMutableComposition composition]; 


AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio 
                      preferredTrackID:kCMPersistentTrackID_Invalid]; 
[compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) 
           ofTrack:sourceAudioTrack 
           atTime:kCMTimeZero error:nil]; 




AVAssetExportSession * assetExport = [[AVAssetExportSession alloc] initWithAsset:composition 
                     presetName:AVAssetExportPresetMediumQuality]; 


NSString *exportPath = [movFilePath stringByReplacingOccurrencesOfString:@".MOV" withString:@".mp4"]; 


NSURL * exportUrl = [NSURL fileURLWithPath:exportPath]; 


assetExport.outputFileType = AVFileTypeMPEG4; 
assetExport.outputURL = exportUrl; 
assetExport.shouldOptimizeForNetworkUse = YES; 
assetExport.videoComposition = [self getVideoComposition:videoAsset composition:composition]; 

[assetExport exportAsynchronouslyWithCompletionHandler: 
^(void) { 
    switch (assetExport.status) 
    { 
     case AVAssetExportSessionStatusCompleted: 
      //    export complete 
        if (block) { 
         block(exportPath); 
       } 
      break; 
     case AVAssetExportSessionStatusFailed: 
      block(nil); 
      break; 
     case AVAssetExportSessionStatusCancelled: 
      block(nil); 
      break; 
    } 
}]; 
} 

// aktuelle Ausrichtung erhalten

0:

// erhalten Video

+(BOOL) isVideoPortrait:(AVAsset *)asset{ 
BOOL isPortrait = FALSE; 
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) 
    { 
     isPortrait = YES; 
    } 
    // PortraitUpsideDown 
    if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0) { 

     isPortrait = YES; 
    } 
    // LandscapeRight 
    if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0) 
    { 
     isPortrait = FALSE; 
    } 
    // LandscapeLeft 
    if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0) 
    { 
     isPortrait = FALSE; 
    } 
} 
return isPortrait; 

}

+0

Hallo @Jagie danke für den Beispielcode. Ich habe das funktioniert, außer bei der Verwendung der Frontkamera wird das exportierte Video nur zur Hälfte gezeigt. Was bedeutet, dass ich es mit der Übersetzung entlang der y-Achse nach unten verschieben muss. Das funktioniert aber einwandfrei wenn ich die Rückkamera benutze. Hattest du ein ähnliches Problem mit der Frontkamera? – doorman

+0

Wenn ich Ihren Code ausprobiert habe, erhalte ich den Fehler "Das Video konnte nicht zusammengesetzt werden." –

Verwandte Themen