iOS中AVPlayerItem色度键滤镜使目标像素变黑而非透明的问题
AVPlayerItem色度键滤镜绿色像素显示黑色而非透明(AVPlayerViewController场景)
我参照官方文档实现了iOS平台AVPlayerItem的色度键滤镜,预期将色相值在0.3至0.4之间的绿色像素转为透明,但实际效果是绿色像素变成黑色。改用AVPlayerLayer时问题解决,但业务需求必须使用AVPlayerViewController,需要找到适配方案。
滤镜核心实现代码
- (CGFloat) hueFromRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue { UIColor* color = [UIColor colorWithRed:red green:green blue:blue alpha:1]; CGFloat hue, saturation, brightness; [color getHue:&hue saturation:&saturation brightness:&brightness alpha:nil]; return hue; } - (CIFilter<CIColorCube> *) chromaKeyFilterHuesFrom:(CGFloat)minHue to:(CGFloat)maxHue { const unsigned int size = 64; const size_t cubeDataSize = size * size * size * 4; NSMutableData* cubeData = [[NSMutableData alloc] initWithCapacity:(cubeDataSize * sizeof(float))]; for (int z = 0; z < size; z++) { CGFloat blue = ((double)z)/(size-1); for (int y = 0; y < size; y++) { CGFloat green = ((double)y)/(size-1); for (int x = 0; x < size; x++) { CGFloat red = ((double)x)/(size-1); CGFloat hue = [self hueFromRed:red green:green blue:blue]; float alpha = (hue >= minHue && hue <= maxHue) ? 0 : 1; float premultipliedRed = red * alpha; float premultipliedGreen = green * alpha; float premultipliedBlue = blue * alpha; [cubeData appendBytes:&premultipliedRed length:sizeof(float)]; [cubeData appendBytes:&premultipliedGreen length:sizeof(float)]; [cubeData appendBytes:&premultipliedBlue length:sizeof(float)]; [cubeData appendBytes:&alpha length:sizeof(float)]; } } } CIFilter<CIColorCube> *colorCubeFilter = CIFilter.colorCubeFilter; colorCubeFilter.cubeDimension = size; colorCubeFilter.cubeData = cubeData; return colorCubeFilter; }
ViewController中的视频合成与播放代码
- (AVVideoComposition*) createVideoComposition:(AVPlayerItem *)_playerItem { CIFilter<CIColorCube>* chromaKeyFilter = [self chromaKeyFilterHuesFrom:0.3 to:0.4]; AVMutableVideoComposition *composition = [AVMutableVideoComposition videoCompositionWithAsset: _playerItem.asset applyingCIFiltersWithHandler:^(AVAsynchronousCIImageFilteringRequest *_Nonnull request) { CIImage *image = request.sourceImage.imageByClampingToExtent; [chromaKeyFilter setValue:image forKey:kCIInputImageKey]; CIImage *output = [chromaKeyFilter.outputImage imageByCroppingToRect:request.sourceImage.extent]; [request finishWithImage:output context:nil]; }]; return composition; } - (IBAction)playVideo:(id)sender { NSString *path = [[NSBundle mainBundle] pathForResource:@"my_video_has_green_pixels_zone" ofType:@"mp4"]; NSURL *url = [NSURL fileURLWithPath:path]; AVPlayerItem* playerItem = [AVPlayerItem playerItemWithURL:url]; playerItem.videoComposition = [self createVideoComposition:playerItem]; AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem]; player.actionAtItemEnd = AVPlayerActionAtItemEndNone; AVPlayerViewController *controller = [[AVPlayerViewController alloc] init]; controller.player = player; controller.view.frame = self.view.bounds; controller.view.backgroundColor = UIColor.redColor; [[self view] addSubview:controller.view]; [self presentViewController:controller animated:YES completion:nil]; [player play]; }
已验证的临时解决方案(改用AVPlayerLayer)
改用AVPlayerLayer并指定像素格式后,透明效果正常,但不符合业务需求:
NSDictionary* pixelBufferAttributes = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) }; AVPlayerLayer *layerPlayer = [AVPlayerLayer playerLayerWithPlayer:player]; layerPlayer.videoGravity = AVLayerVideoGravityResizeAspect; layerPlayer.frame = self.view.frame; layerPlayer.pixelBufferAttributes = pixelBufferAttributes; [self.view.layer addSublayer:layerPlayer];
适配AVPlayerViewController的最终解决方案
问题根源在于AVPlayerViewController默认使用的像素格式不包含Alpha通道,导致透明像素被渲染为黑色。只需为AVPlayerViewController的playerLayer指定支持Alpha的像素格式即可修复:
修改playVideo:方法,在初始化AVPlayerViewController后添加一行配置:
// 为playerLayer设置支持Alpha通道的像素格式 controller.playerLayer.pixelBufferAttributes = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) };
完整修改后的playVideo:方法:
- (IBAction)playVideo:(id)sender { NSString *path = [[NSBundle mainBundle] pathForResource:@"my_video_has_green_pixels_zone" ofType:@"mp4"]; NSURL *url = [NSURL fileURLWithPath:path]; AVPlayerItem* playerItem = [AVPlayerItem playerItemWithURL:url]; playerItem.videoComposition = [self createVideoComposition:playerItem]; AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem]; player.actionAtItemEnd = AVPlayerActionAtItemEndNone; AVPlayerViewController *controller = [[AVPlayerViewController alloc] init]; controller.player = player; // 添加像素格式配置 controller.playerLayer.pixelBufferAttributes = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) }; controller.view.frame = self.view.bounds; controller.view.backgroundColor = UIColor.redColor; [[self view] addSubview:controller.view]; [self presentViewController:controller animated:YES completion:nil]; [player play]; }
原理说明:kCVPixelFormatType_32BGRA格式包含Alpha通道,能够正确承载滤镜处理后的透明像素,避免被默认格式转换为黑色。
内容的提问来源于stack exchange,提问作者Hoài Vũ Lê
相关产品推荐
相关产品推荐

