2017-01-11 9 views
2

간단한 작업이 필요합니다 : 동영상을 회전하면서 재생하고 CIFilter을 적용하십시오.AVPlayer가 비디오 구성 결과를 잘못 재생했습니다.

AVPlayerItem *item = [AVPlayerItem playerItemWithURL:videoURL]; 

// DEBUG LOGGING 
AVAssetTrack *track = [[item.asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; 
NSLog(@"Natural size is: %@", NSStringFromCGSize(track.naturalSize)); 
NSLog(@"Preffered track transform is: %@", NSStringFromCGAffineTransform(track.preferredTransform)); 
NSLog(@"Preffered asset transform is: %@", NSStringFromCGAffineTransform(item.asset.preferredTransform)); 

가 그럼 난 비디오 구성을 적용 할 필요가 : 첫째

, 나는 플레이어 항목을 만들 수 있습니다. 원래는 2 개의 명령어로 AVVideoComposition을 만들려고했는데 하나는 회전을 위해 AVVideoCompositionLayerInstruction이고 다른 하나는 CIFilter 응용 프로그램이 될 것입니다. 그러나 "AVCoreImageFilterVideoCompositionInstruction 만 포함하도록 예상되는 비디오 컴포지션"을 말하는 예외가 있습니다. 이는 Apple에서이 두 가지 명령어를 결합 할 수 없음을 의미합니다.

AVAsset *asset = playerItem.asset; 
CGAffineTransform rotation = [self transformForItem:playerItem]; 

AVVideoComposition *composition = [AVVideoComposition videoCompositionWithAsset:asset applyingCIFiltersWithHandler:^(AVAsynchronousCIImageFilteringRequest * _Nonnull request) { 
    // Step 1: get the input frame image (screenshot 1) 
    CIImage *sourceImage = request.sourceImage; 

    // Step 2: rotate the frame 
    CIFilter *transformFilter = [CIFilter filterWithName:@"CIAffineTransform"]; 
    [transformFilter setValue:sourceImage forKey: kCIInputImageKey]; 
    [transformFilter setValue: [NSValue valueWithCGAffineTransform: rotation] forKey: kCIInputTransformKey]; 
    sourceImage = transformFilter.outputImage; 
    CGRect extent = sourceImage.extent; 
    CGAffineTransform translation = CGAffineTransformMakeTranslation(-extent.origin.x, -extent.origin.y); 
    [transformFilter setValue:sourceImage forKey: kCIInputImageKey]; 
    [transformFilter setValue: [NSValue valueWithCGAffineTransform: translation] forKey: kCIInputTransformKey]; 
    sourceImage = transformFilter.outputImage; 

    // Step 3: apply the custom filter chosen by the user 
    extent = sourceImage.extent; 
    sourceImage = [sourceImage imageByClampingToExtent]; 
    [filter setValue:sourceImage forKey:kCIInputImageKey]; 
    sourceImage = filter.outputImage; 
    sourceImage = [sourceImage imageByCroppingToRect:extent]; 

    // Step 4: finish processing the frame (screenshot 2) 
    [request finishWithImage:sourceImage context:nil]; 
}]; 

playerItem.videoComposition = composition; 

I 이미지가 성공적으로 회전 필터이 예에서는 (인가 보여 디버깅 중에 스크린 샷이 식별 필터했다 : 따라서, I는 모두 필터링의 여기 코드 결합 어떤 이미지를 변경하지 않습니다).

enter image description here

enter image description here

당신이 회전에 성공, 볼 수 있듯이, 그 결과 프레임의 범위 : 다음은 위의 의견에 표시된 지점에서 촬영 한 스크린 샷 1 스크린 샷이 있습니다 또한 정확했다.

플레이어에서이 비디오를 재생하려고하면 문제가 발생합니다. 모든 프레임 크기를 조절하고 아래로 이동하는 것처럼

enter image description here

그래서 것 같다 여기에 내가 무엇을 얻을 수 있습니다. 녹색 영역은 빈 프레임 정보입니다. 프레임 크기를 무한대로 설정하면 녹색 대신 테두리 픽셀이 표시됩니다.

Natural size is: {1920, 1080} 
Preffered track transform is: [0, 1, -1, 0, 1080, 0] 
Preffered asset transform is: [1, 0, 0, 1, 0, 0] 

플레이어입니다 : 내가 왜 첫 번째 코드 조각에서 나는 크기와 변환을 기록했다 위에 그건, 플레이어는 여전히 AVPlayerItem에서 회전하기 전에 오래된 크기 정보 걸리는 느낌이, 로그있다 다음과 같이 설정 :이 장치 만 저장에 풍경에 아이폰 [기가] 방향을 카메라 를 사용하여 응용 프로그램 자체에 의해 기록되고 저장된 동영상을 발생합니다

layer.videoGravity = AVLayerVideoGravityResizeAspectFill; 
layer.needsDisplayOnBoundsChange = YES; 

가장 중요한 것은 참고 사항 미리 우리. 앱이 인물 모드로 녹화하는 동영상은 완전히 괜찮습니다 (그런데 인물 사진의 크기가 똑같고 풍경 비디오와 같은 로그를 변형합니다! 이상한 ... 어쩌면 아이폰은 동영상에 회전 정보를 넣고 수정합니다). 따라서 비디오를 확대/축소하고 이동하는 것은 회전하기 전에 "화면 채우기"와 오래된 해상도 정보의 조합처럼 보입니다. 그런데 종횡비가 다른 플레이어 영역을 채우기 위해 크기 조정을하기 때문에 세로 비디오 프레임이 부분적으로 표시되지만 이는 예상되는 동작입니다.

내 생각을 알려주세요. 내가 원하는 것을 성취하는 더 좋은 방법을 안다면, 알아두면 좋을 것입니다.

답변

2

UPDATE : 재생 중에 "변화"를 AVPlayerItem 비디오 크기는 쉬운 방법으로 밝혀 온다 - 비디오 조성물의 renderSize 속성을 설정 (AVMutableVideoComposition 클래스를 사용하여 수행 할 수 있습니다). 아래

MY OLD 답변 : 디버깅을 많이 후


나는이 문제를 이해하고 해결책을 발견했다. 내 초기 추측은 AVPlayer는 여전히 비디오가 원래 크기 인 것으로 간주합니다.은 정확했습니다. 아래 이미지에서 무슨 일이 일어 났는지 설명 :

enter image description here

솔루션에 관해서는, 나는 AVAsset 또는 AVPlayerItem 내부의 영상 크기를 변경하는 방법을 찾을 수 없습니다. 그래서 방금 AVPlayer이 기대하는 크기와 눈금에 맞게 비디오를 조작 한 다음 올바른 가로 세로 비율로 플레이어에서 플레이하고 플레이어 영역을 채우고 채우도록 플래그를 지정하면 모든 것이 잘 보입니다. 여기에 그래픽 설명입니다

enter image description here

는 그리고 여기 질문에서 언급 한 applyingCIFiltersWithHandler 블록에 삽입해야하는 추가 코드 진행됩니다

... after Step 3 in the question codes above 

// make the frame the same aspect ratio as the original input frame 
// by adding empty spaces at the top and the bottom of the extent rectangle 
CGFloat newHeight = originalExtent.size.height * originalExtent.size.height/extent.size.height; 
CGFloat inset = (extent.size.height - newHeight)/2; 
extent = CGRectInset(extent, 0, inset); 
sourceImage = [sourceImage imageByCroppingToRect:extent]; 

// scale down to the original frame size 
CGFloat scale = originalExtent.size.height/newHeight; 
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(scale, scale); 
[transformFilter setValue:sourceImage forKey: kCIInputImageKey]; 
[transformFilter setValue: [NSValue valueWithCGAffineTransform: scaleTransform] forKey: kCIInputTransformKey]; 
sourceImage = transformFilter.outputImage; 

// translate the frame to make it's origin start at (0, 0) 
CGAffineTransform translation = CGAffineTransformMakeTranslation(0, -inset * scale); 
[transformFilter setValue:sourceImage forKey: kCIInputImageKey]; 
[transformFilter setValue: [NSValue valueWithCGAffineTransform: translation] forKey: kCIInputTransformKey]; 
sourceImage = transformFilter.outputImage;