2012-11-20 15 views
7

Problem z kontrolowaniem orientacji wideo podczas przechwytywania i po jego zakończeniu na urządzeniu z systemem iOS zmaga się z kilkoma wymiarami. Dzięki wcześniejszym odpowiedziom i dokumentacji od Apple udało mi się to rozgryźć. Jednak teraz, gdy chcę przesłać trochę wideo na stronę internetową, mam pewne szczególne problemy. Podkreślam ten problem, w szczególności in this question, i okazuje się, że wymagane są opcje orientacji, które mają być ustawione podczas kodowania wideo.Funkcja iOS AVFoundation: Ustawianie orientacji wideo

To może być, ale nie mam pojęcia, jak to zrobić. Dokumentacja dotycząca orientacji ustawień dotyczy jej poprawnego ustawienia do wyświetlania na urządzeniu, a ja wdrożyłem tę poradę. found here. Jednak ta rada nie odnosi się do prawidłowego ustawienia orientacji oprogramowania innego niż Apple, takiego jak VLC lub przeglądarka Chrome .

Czy każdy może uzyskać wgląd w sposób prawidłowego ustawienia orientacji na urządzeniu, tak aby wyświetlał się poprawnie dla wszystkich programów do wyświetlania?

+2

Rzeczywiste dane zawsze ma orientację statyczną podczas przechwytywania. Orientacja jest przechowywana w wartości 'preferredTransform'. Sądzę więc, że musisz wyeksportować wideo, aby obrócić dane. Zajrzałbym do 'AVAssetExportSession'' AVMutableVideoComposition' 'setTransform: atTime:', to może pomóc. – Davyd

+0

Mam zgłoszenie incydentu pomocy technicznej do firmy Apple, aby pomóc rozwiązać ten problem. Ale przyjrzę się, jak sugerujesz. Czy zastanawiam się, czy oznaczałoby to oddzielny krok kodowania po nagraniu wideo? To może być kosztowne obliczeniowo ... –

+0

Tak, to byłby dodatkowy krok. Jednak może nie być tak drogie, jeśli eksportuje bez zmiany oryginalnego kodowania. Daj mi znać, jeśli znajdziesz lepsze rozwiązanie. – Davyd

Odpowiedz

3

na wypadek gdyby ktoś inny szuka tej odpowiedzi, jak również jest to metoda, że ​​gotowane (zmodyfikowany nieco uprościć):

- (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; 
    } 
}]; 

} 

Ten materiał jest słabo udokumentowana, niestety, ale przez sznurka razem z innymi pytaniami SO i odczytaniem plików nagłówkowych, udało mi się to sprawić. Mam nadzieję, że to pomoże każdemu innemu!

2

używać tych poniżej method ustawić correctorientation według videoassetorientation w 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

Od iOS 5 można zażądać obróconych CVPixelBuffers użyciu AVCaptureVideoDataOutput udokumentowane here. Zapewnia to poprawną orientację bez ponownego przetwarzania wideo za pomocą AVAssetExportSession.

5

W dokumentacji Apple here stwierdza:

Klienci mogą teraz odbierać fizycznie obróconych CVPixelBuffers w ich AVCaptureVideoDataOutput -captureOutput: didOutputSampleBuffer: fromConnection: delegować zwrotnego. W poprzednich wersjach iOS przednia kamera zawsze dostarczała bufory w AVCaptureVideoOrientationLandscapeLeft, a kamera cofania zawsze dostarczała bufory w AVCaptureVideoOrientationLandscapeRight. Wszystkie 4 AVCaptureVideoOrientations są obsługiwane, a rotacja jest przyspieszana sprzętowo. Aby zażądać rotacji bufora, klient wywołuje polecenie -setVideoOrientation: w wideo AVCaptureVideoDataOutput AVCaptureConnection. Zwróć uwagę, że fizycznie obracające się bufory są dostarczane z kosztem wydajności, więc tylko żądaj rotacji, jeśli jest to konieczne. Jeśli na przykład chcesz, aby obrócone wideo zapisywane do pliku filmu QuickTime przy użyciu AVAssetWriter, lepiej jest ustawić właściwość -transform na AVAssetWriterInput zamiast fizycznie obracać bufory w AVCaptureVideoDataOutput.

Tak więc wysłana przez Aarona Vegha aplikacja wykorzystująca AVAssetExportSession działa, ale nie jest potrzebna. Podobnie jak w przypadku docenta Apple'a, jeśli chcesz, aby orientacja była poprawnie ustawiona, aby była odtwarzana w grach typu QuickScript, takich jak VLC lub w Internecie przy użyciu Chrome, musisz ustawić orientację wideo w AVCaptureConnection dla AVCaptureVideoDataOutput. Jeśli spróbujesz ustawić dla AVAssetWriterInput, otrzymasz nieprawidłową orientację dla graczy takich jak VLC i Chrome.

Oto mój kod gdzie mogę ustawić go podczas konfigurowania sesji przechwytywania:

// 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

Chciałbym skorzystać z tego rodzaju rozwiązania. Rozwiązałoby to wiele problemów. Dokumenty wskazują na "koszt wydajności". Czy koszty te okazały się znaczące w twoim doświadczeniu? Jestem ciekawy presetów o najwyższej przepływności (np. Rozdzielczość HD przy 120 klatkach na sekundę na iPhone 5s)? – otto

9

Wreszcie, oparty na odpowiedziach @Aaron Vegha i @Prince, pomyślałem mój rozdzielczość : // Konwersja wideo

+(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; 
    } 
}]; 
} 

// uzyskać orientację bieżącego

+(AVMutableVideoComposition *) getVideoComposition:(AVAsset *)asset composition:(AVMutableComposition*)composition{ 
    BOOL isPortrait_ = [self isVideoPortrait:asset]; 


    AVMutableCompositionTrack *compositionVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; 


    AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; 
    [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:videoTrack atTime:kCMTimeZero error:nil]; 

    AVMutableVideoCompositionLayerInstruction *layerInst = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTrack]; 

    CGAffineTransform transform = videoTrack.preferredTransform; 
    [layerInst setTransform:transform atTime:kCMTimeZero]; 


    AVMutableVideoCompositionInstruction *inst = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; 
    inst.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration); 
    inst.layerInstructions = [NSArray arrayWithObject:layerInst]; 


    AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition]; 
    videoComposition.instructions = [NSArray arrayWithObject:inst]; 

    CGSize videoSize = videoTrack.naturalSize; 
    if(isPortrait_) { 
     NSLog(@"video is portrait "); 
     videoSize = CGSizeMake(videoSize.height, videoSize.width); 
    } 
    videoComposition.renderSize = videoSize; 
    videoComposition.frameDuration = CMTimeMake(1,30); 
    videoComposition.renderScale = 1.0; 
    return videoComposition; 
    } 

// Pobierz wideo

+(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

Witam @Jagie dzięki za przykładowy kod. Mam to działa, chyba że przy korzystaniu z kamery przedniej wyeksportowane wideo jest wyświetlane tylko w połowie. Co oznacza, że ​​muszę przenieść go z tłumaczeniem w dół wzdłuż osi Y. Działa to jednak dobrze, jeśli korzystam z tylnego aparatu. Czy masz podobny problem z przednim aparatem? – doorman

+0

Kiedy wypróbowałem twój kod, wyświetlił mi się błąd "Nie można skomponować filmu". –

Powiązane problemy