You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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ê

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 13:59:45