├── MyTransparentVideoExample
├── playdoh-bat.mp4
├── AVAsset+VideoSize.swift
├── AlphaFrameFilter.metal
├── CIKernel+DefaultMetalLibrary.swift
├── CIImage+VerticalSplit.swift
├── Info.plist
├── Base.lproj
│ ├── Main.storyboard
│ └── LaunchScreen.storyboard
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── AppDelegate.swift
├── AVPlayerView.swift
├── ViewController.swift
└── AlphaFrameFilter.swift
├── MyTransparentVideoExample.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcshareddata
│ └── xcschemes
│ │ └── MyTransparentVideoExample.xcscheme
└── project.pbxproj
└── .gitignore
/MyTransparentVideoExample/playdoh-bat.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quentinfasquel/MyTransparentVideoExample/HEAD/MyTransparentVideoExample/playdoh-bat.mp4
--------------------------------------------------------------------------------
/MyTransparentVideoExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/MyTransparentVideoExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/MyTransparentVideoExample/AVAsset+VideoSize.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AVAsset+VideoSize.swift
3 | // MyTransparentVideoExample
4 | //
5 | // Created by Quentin Fasquel on 22/03/2020.
6 | // Copyright © 2020 Quentin Fasquel. All rights reserved.
7 | //
8 |
9 | import AVFoundation
10 |
11 | extension AVAsset {
12 | var videoSize: CGSize { tracks(withMediaType: .video).first?.naturalSize ?? .zero }
13 | }
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
--------------------------------------------------------------------------------
/MyTransparentVideoExample/AlphaFrameFilter.metal:
--------------------------------------------------------------------------------
1 | //
2 | // AlphaFrameFilter.metal
3 | // MyTransparentVideoExample
4 | //
5 | // Created by Quentin Fasquel on 22/03/2020.
6 | // Copyright © 2020 Quentin Fasquel. All rights reserved.
7 | //
8 |
9 | #include
10 | #include // includes CIKernelMetalLib.h
11 |
12 | extern "C" {
13 | namespace coreimage {
14 | float4 alphaFrame(sampler source, sampler mask) {
15 | float4 color = source.sample(source.coord());
16 | float opacity = mask.sample(mask.coord()).r;
17 | return float4(color.rgb, opacity);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/MyTransparentVideoExample/CIKernel+DefaultMetalLibrary.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CIKernelExtension.swift
3 | // MyTransparentVideoExample
4 | //
5 | // Created by Quentin Fasquel on 22/03/2020.
6 | // Copyright © 2020 Quentin Fasquel. All rights reserved.
7 | //
8 |
9 | import CoreImage
10 | import Metal
11 |
12 | private func defaultMetalLibrary() throws -> Data {
13 | let url = Bundle.main.url(forResource: "default", withExtension: "metallib")!
14 | return try Data(contentsOf: url)
15 | }
16 |
17 | extension CIKernel {
18 | /// Init CI kernel with just a `functionName` directly from default metal library
19 | convenience init(functionName: String) throws {
20 | let metalLibrary = try defaultMetalLibrary()
21 | try self.init(functionName: functionName, fromMetalLibraryData: metalLibrary)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/MyTransparentVideoExample/CIImage+VerticalSplit.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CIImage+Split.swift
3 | // MyTransparentVideoExample
4 | //
5 | // Created by Quentin Fasquel on 27/03/2020.
6 | // Copyright © 2020 Quentin Fasquel. All rights reserved.
7 | //
8 |
9 | import CoreImage
10 |
11 | extension CIImage {
12 |
13 | typealias VerticalSplit = (topImage: CIImage, bottomImage: CIImage)
14 |
15 | func verticalSplit() -> VerticalSplit {
16 | let outputExtent = self.extent.applying(CGAffineTransform(scaleX: 1.0, y: 0.5))
17 |
18 | // Get the top region according to Core Image coordinate system, (0,0) being bottom left
19 | let translate = CGAffineTransform(translationX: 0, y: outputExtent.height)
20 | let topRegion = outputExtent.applying(translate)
21 | var topImage = self.cropped(to: topRegion)
22 | // Translate topImage back to origin
23 | topImage = topImage.transformed(by: translate.inverted())
24 |
25 | let bottomRegion = outputExtent
26 | let bottomImage = self.cropped(to: bottomRegion)
27 |
28 | return (topImage, bottomImage)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/MyTransparentVideoExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/MyTransparentVideoExample/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/MyTransparentVideoExample/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/MyTransparentVideoExample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | }
88 | ],
89 | "info" : {
90 | "version" : 1,
91 | "author" : "xcode"
92 | }
93 | }
--------------------------------------------------------------------------------
/MyTransparentVideoExample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // MyTransparentVideoExample
4 | //
5 | // Created by Quentin on 27/10/2017.
6 | // Copyright © 2017 Quentin Fasquel. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | func applicationWillResignActive(_ application: UIApplication) {
22 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
23 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
24 | }
25 |
26 | func applicationDidEnterBackground(_ application: UIApplication) {
27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
29 | }
30 |
31 | func applicationWillEnterForeground(_ application: UIApplication) {
32 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
33 | }
34 |
35 | func applicationDidBecomeActive(_ application: UIApplication) {
36 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
37 | }
38 |
39 | func applicationWillTerminate(_ application: UIApplication) {
40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
41 | }
42 |
43 |
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/MyTransparentVideoExample.xcodeproj/xcshareddata/xcschemes/MyTransparentVideoExample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/MyTransparentVideoExample/AVPlayerView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AVPlayerView.swift
3 | // MyTransparentVideoExample
4 | //
5 | // Created by Quentin on 27/10/2017.
6 | // Copyright © 2017 Quentin Fasquel. All rights reserved.
7 | //
8 |
9 | import AVFoundation
10 | import UIKit
11 |
12 | public class AVPlayerView: UIView {
13 |
14 | deinit {
15 | playerItem = nil
16 | }
17 |
18 | public override class var layerClass: AnyClass {
19 | return AVPlayerLayer.self
20 | }
21 |
22 | public var playerLayer: AVPlayerLayer {
23 | return layer as! AVPlayerLayer
24 | }
25 |
26 | public private(set) var player: AVPlayer? {
27 | set { playerLayer.player = newValue }
28 | get { return playerLayer.player }
29 | }
30 |
31 | private var playerItemStatusObserver: NSKeyValueObservation?
32 |
33 | private(set) var playerItem: AVPlayerItem? = nil {
34 | didSet {
35 | // If `isLoopingEnabled` is called before the AVPlayer was set
36 | setupLooping()
37 | }
38 | }
39 |
40 | public func loadPlayerItem(_ playerItem: AVPlayerItem, onReady: ((Result) -> Void)? = nil) {
41 | let player = AVPlayer(playerItem: playerItem)
42 |
43 | self.player = player
44 | self.playerItem = playerItem
45 |
46 | guard let completion = onReady else {
47 | playerItemStatusObserver = nil
48 | return
49 | }
50 |
51 | playerItemStatusObserver = playerItem.observe(\.status) { [weak self] item, _ in
52 | switch item.status {
53 | case .failed:
54 | completion(.failure(item.error!))
55 | case .readyToPlay:
56 | completion(.success(player))
57 | // Stop observing
58 | self?.playerItemStatusObserver = nil
59 | case .unknown:
60 | break
61 | @unknown default:
62 | fatalError()
63 | }
64 | }
65 | }
66 |
67 | // MARK: - Looping Handler
68 |
69 | /// When set to `true`, the player view automatically adds an observer on its AVPlayer,
70 | /// and it will play again from start every time playback ends.
71 | /// * Warning: This will not result in a smooth video loop.
72 | public var isLoopingEnabled: Bool = false {
73 | didSet { setupLooping() }
74 | }
75 |
76 | private var didPlayToEndTimeObsever: NSObjectProtocol? = nil {
77 | willSet(newObserver) {
78 | // When updating didPlayToEndTimeObserver,
79 | // automatically remove its previous value from the Notification Center
80 | if let observer = didPlayToEndTimeObsever, didPlayToEndTimeObsever !== newObserver {
81 | NotificationCenter.default.removeObserver(observer)
82 | }
83 | }
84 | }
85 |
86 | private func setupLooping() {
87 | guard let playerItem = self.playerItem, let player = self.player else {
88 | return
89 | }
90 |
91 | guard isLoopingEnabled else {
92 | didPlayToEndTimeObsever = nil
93 | return
94 | }
95 |
96 | didPlayToEndTimeObsever = NotificationCenter.default.addObserver(
97 | forName: .AVPlayerItemDidPlayToEndTime, object: playerItem, queue: nil, using: { _ in
98 | player.seek(to: CMTime.zero) { _ in
99 | player.play()
100 | }
101 | })
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/MyTransparentVideoExample/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // MyTransparentVideoExample
4 | //
5 | // Created by Quentin on 27/10/2017.
6 | // Copyright © 2017 Quentin Fasquel. All rights reserved.
7 | //
8 |
9 | import AVFoundation
10 | import UIKit
11 | import os.log
12 |
13 | class ViewController: UIViewController {
14 |
15 | override func viewDidLoad() {
16 | super.viewDidLoad()
17 |
18 | let videoSize = CGSize(width: 300, height: 187)
19 | let playerView = AVPlayerView(frame: CGRect(origin: .zero, size: videoSize))
20 | view.addSubview(playerView)
21 |
22 | // Use Auto Layout anchors to center our playerView
23 | playerView.translatesAutoresizingMaskIntoConstraints = false
24 | playerView.widthAnchor.constraint(equalToConstant: videoSize.width).isActive = true
25 | playerView.heightAnchor.constraint(equalToConstant: videoSize.height).isActive = true
26 | playerView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
27 | playerView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
28 |
29 | // Setup our playerLayer to hold a pixel buffer format with "alpha"
30 | let playerLayer: AVPlayerLayer = playerView.playerLayer
31 | playerLayer.pixelBufferAttributes = [
32 | (kCVPixelBufferPixelFormatTypeKey as String): kCVPixelFormatType_32BGRA]
33 |
34 | // Our AVPlayerLayer has a default backgroundColor to nil
35 | // Set a backgroundColor the viewController's view
36 | view.backgroundColor = .gray
37 |
38 | // Setup looping on our video
39 | playerView.isLoopingEnabled = true
40 |
41 | // Load our player item
42 | let itemUrl: URL = Bundle.main.url(forResource: "playdoh-bat", withExtension: "mp4")!
43 | let playerItem = createTransparentItem(url: itemUrl)
44 |
45 | playerView.loadPlayerItem(playerItem) { [weak self] result in
46 | switch result {
47 | case .failure(let error):
48 | return print("Something went wrong when loading our video", error)
49 |
50 | case .success(let player):
51 | // Finally, we can start playing
52 | player.play()
53 | // Animate background
54 | self?.animateBackgroundColor()
55 | }
56 |
57 | }
58 | }
59 |
60 | override func didReceiveMemoryWarning() {
61 | super.didReceiveMemoryWarning()
62 | // Dispose of any resources that can be recreated.
63 | }
64 |
65 | // MARK: - Player Item Configuration
66 |
67 | func createTransparentItem(url: URL) -> AVPlayerItem {
68 | let asset = AVAsset(url: url)
69 | let playerItem = AVPlayerItem(asset: asset)
70 | // Set the video so that seeking also renders with transparency
71 | playerItem.seekingWaitsForVideoCompositionRendering = true
72 | // Apply a video composition (which applies our custom filter)
73 | playerItem.videoComposition = createVideoComposition(for: asset)
74 | return playerItem
75 | }
76 |
77 | func createVideoComposition(for asset: AVAsset) -> AVVideoComposition {
78 | let filter = AlphaFrameFilter(renderingMode: .builtInFilter)
79 | let composition = AVMutableVideoComposition(asset: asset, applyingCIFiltersWithHandler: { request in
80 | do {
81 | let (inputImage, maskImage) = request.sourceImage.verticalSplit()
82 | let outputImage = try filter.process(inputImage, mask: maskImage)
83 | return request.finish(with: outputImage, context: nil)
84 | } catch {
85 | os_log("Video composition error: %s", String(describing: error))
86 | return request.finish(with: error)
87 | }
88 | })
89 |
90 | composition.renderSize = asset.videoSize.applying(CGAffineTransform(scaleX: 1.0, y: 0.5))
91 | return composition
92 | }
93 |
94 | // MARK: - Background Color
95 |
96 | func animateBackgroundColor() {
97 | let backgroundColors: [UIColor] = [.purple, .blue, .cyan, .green, .yellow, .orange, .red]
98 |
99 | let animator = UIViewPropertyAnimator(duration: 2.0, curve: .linear) {
100 | let colorIndex = backgroundColors.firstIndex(of: self.view.backgroundColor!) ?? 0
101 | let countColors = backgroundColors.count
102 | self.view.backgroundColor = backgroundColors[(colorIndex + 1) % countColors]
103 | }
104 |
105 | animator.addCompletion { _ in
106 | // Infinite animation
107 | self.animateBackgroundColor()
108 | }
109 |
110 | animator.startAnimation()
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/MyTransparentVideoExample/AlphaFrameFilter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChromaKeyFilter.swift
3 | // MyTransparentVideoExample
4 | //
5 | // Created by Quentin on 27/10/2017.
6 | // Copyright © 2017 Quentin Fasquel. All rights reserved.
7 | //
8 |
9 | import CoreImage
10 |
11 | typealias AlphaFrameFilterError = AlphaFrameFilter.Error
12 |
13 | final class AlphaFrameFilter: CIFilter {
14 |
15 | enum Error: Swift.Error {
16 | /// This error is thrown when using renderingMode `builtInFilter` and the filter named *CIBlendWithMask* was not found
17 | case buildInFilterNotFound
18 | /// This error is thrown when `inputImage` and `maskImage` have different **extents**
19 | case incompatibleExtents
20 | /// This error is thrown when a kernel couldn't be initialized,
21 | /// which may happen when using renderingMode `colorKernel` or `metalKernel`
22 | case invalidKernel
23 | /// This error is thrown when `inputImage` or `maskImage` is missing
24 | case invalidParameters
25 | /// This error would be thrown when output image is `nil` in any other case, it typically should not happen
26 | case unknown
27 | }
28 |
29 | private(set) var inputImage: CIImage?
30 | private(set) var maskImage: CIImage?
31 | private(set) var outputError: Swift.Error?
32 |
33 | private let renderingMode: RenderingMode
34 |
35 | required init(renderingMode: RenderingMode) {
36 | self.renderingMode = renderingMode
37 | super.init()
38 | }
39 |
40 | required init?(coder: NSCoder) {
41 | fatalError("init(coder:) has not been implemented")
42 | }
43 |
44 | override var outputImage: CIImage? {
45 | // Output is nil if an input image and a mask image aren't provided
46 | guard let inputImage = inputImage, let maskImage = maskImage else {
47 | outputError = Error.invalidParameters
48 | return nil
49 | }
50 |
51 | // Input image & mask image should have the same extent
52 | guard inputImage.extent == maskImage.extent else {
53 | outputError = Error.incompatibleExtents
54 | return nil
55 | }
56 |
57 | outputError = nil
58 |
59 | return render(using: renderingMode, inputImage: inputImage, maskImage: maskImage)
60 | }
61 |
62 | func process(_ inputImage: CIImage, mask maskImage: CIImage) throws -> CIImage {
63 | self.inputImage = inputImage
64 | self.maskImage = maskImage
65 |
66 | guard let outputImage = self.outputImage else {
67 | throw outputError ?? Error.unknown
68 | }
69 |
70 | return outputImage
71 | }
72 |
73 | // MARK: - Rendering
74 |
75 | enum RenderingMode {
76 | case builtInFilter
77 | case colorKernel
78 | case metalKernel
79 | }
80 |
81 | private static var colorKernel: CIColorKernel? = {
82 | // `init(source:)` was deprecated in iOS 12.0: Core Image Kernel Language API deprecated.
83 | // This warning is silent because of preprocessor macro `CI_SILENCE_GL_DEPRECATION`
84 | return CIColorKernel(source: """
85 | kernel vec4 alphaFrame(__sample s, __sample m) {
86 | return vec4( s.rgb, m.r );
87 | }
88 | """)
89 | }()
90 |
91 | private static var metalKernelError: Swift.Error?
92 | private static var metalKernel: CIKernel? = {
93 | do { return try CIKernel(functionName: "alphaFrame") }
94 | catch { metalKernelError = error; return nil }
95 | }()
96 |
97 | private func render(using renderingMode: RenderingMode, inputImage: CIImage, maskImage: CIImage) -> CIImage? {
98 | switch renderingMode {
99 |
100 | case .builtInFilter:
101 | guard let filter = CIFilter(name: "CIBlendWithMask") else {
102 | outputError = Error.buildInFilterNotFound
103 | return nil
104 | }
105 |
106 | let outputExtent = inputImage.extent
107 | let backgroundImage = CIImage(color: .clear).cropped(to: outputExtent)
108 | filter.setValue(backgroundImage, forKey: kCIInputBackgroundImageKey)
109 | filter.setValue(inputImage, forKey: kCIInputImageKey)
110 | filter.setValue(maskImage, forKey: kCIInputMaskImageKey)
111 | return filter.outputImage
112 |
113 | case .colorKernel:
114 | // Force a fatal error if our kernel source isn't correct
115 | guard let colorKernel = Self.colorKernel else {
116 | outputError = Error.invalidKernel
117 | return nil
118 | }
119 |
120 | // Apply our color kernel with the proper parameters
121 | let outputExtent = inputImage.extent
122 | let arguments = [inputImage, maskImage]
123 | return colorKernel.apply(extent: outputExtent, arguments: arguments)
124 |
125 | case .metalKernel:
126 | guard let metalKernel = Self.metalKernel else {
127 | outputError = Self.metalKernelError ?? Error.invalidKernel
128 | return nil
129 | }
130 |
131 | let outputExtent = inputImage.extent
132 | let roiCallback: CIKernelROICallback = { _, rect in rect }
133 | let arguments = [inputImage, maskImage]
134 | return metalKernel.apply(extent: outputExtent, roiCallback: roiCallback, arguments: arguments)
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/MyTransparentVideoExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 667F4C7E1FA3068900F9A900 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667F4C7D1FA3068900F9A900 /* AppDelegate.swift */; };
11 | 667F4C801FA3068900F9A900 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667F4C7F1FA3068900F9A900 /* ViewController.swift */; };
12 | 667F4C831FA3068900F9A900 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 667F4C811FA3068900F9A900 /* Main.storyboard */; };
13 | 667F4C851FA3068900F9A900 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 667F4C841FA3068900F9A900 /* Assets.xcassets */; };
14 | 667F4C881FA3068900F9A900 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 667F4C861FA3068900F9A900 /* LaunchScreen.storyboard */; };
15 | 667F4C901FA3071A00F9A900 /* AVPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667F4C8F1FA3071A00F9A900 /* AVPlayerView.swift */; };
16 | 667F4C921FA3126A00F9A900 /* AlphaFrameFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667F4C911FA3126A00F9A900 /* AlphaFrameFilter.swift */; };
17 | 6690684B1FA3656B00598D4C /* playdoh-bat.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 6690684A1FA3656B00598D4C /* playdoh-bat.mp4 */; };
18 | 669678FB242E7BF00085D444 /* AVAsset+VideoSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 669678FA242E7BF00085D444 /* AVAsset+VideoSize.swift */; };
19 | 669678FD242E7CFB0085D444 /* CIImage+VerticalSplit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 669678FC242E7CFB0085D444 /* CIImage+VerticalSplit.swift */; };
20 | 66E0467A242E85320096DEE5 /* CIKernel+DefaultMetalLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66E04678242E85320096DEE5 /* CIKernel+DefaultMetalLibrary.swift */; };
21 | 66E0467B242E85320096DEE5 /* AlphaFrameFilter.metal in Sources */ = {isa = PBXBuildFile; fileRef = 66E04679242E85320096DEE5 /* AlphaFrameFilter.metal */; };
22 | /* End PBXBuildFile section */
23 |
24 | /* Begin PBXFileReference section */
25 | 667F4C7A1FA3068900F9A900 /* MyTransparentVideoExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MyTransparentVideoExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
26 | 667F4C7D1FA3068900F9A900 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
27 | 667F4C7F1FA3068900F9A900 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
28 | 667F4C821FA3068900F9A900 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
29 | 667F4C841FA3068900F9A900 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
30 | 667F4C871FA3068900F9A900 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
31 | 667F4C891FA3068900F9A900 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
32 | 667F4C8F1FA3071A00F9A900 /* AVPlayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AVPlayerView.swift; sourceTree = ""; };
33 | 667F4C911FA3126A00F9A900 /* AlphaFrameFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlphaFrameFilter.swift; sourceTree = ""; };
34 | 6690684A1FA3656B00598D4C /* playdoh-bat.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = "playdoh-bat.mp4"; sourceTree = ""; };
35 | 669678FA242E7BF00085D444 /* AVAsset+VideoSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVAsset+VideoSize.swift"; sourceTree = ""; };
36 | 669678FC242E7CFB0085D444 /* CIImage+VerticalSplit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CIImage+VerticalSplit.swift"; sourceTree = ""; };
37 | 66E04678242E85320096DEE5 /* CIKernel+DefaultMetalLibrary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CIKernel+DefaultMetalLibrary.swift"; sourceTree = ""; };
38 | 66E04679242E85320096DEE5 /* AlphaFrameFilter.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = AlphaFrameFilter.metal; sourceTree = ""; };
39 | /* End PBXFileReference section */
40 |
41 | /* Begin PBXFrameworksBuildPhase section */
42 | 667F4C771FA3068800F9A900 /* Frameworks */ = {
43 | isa = PBXFrameworksBuildPhase;
44 | buildActionMask = 2147483647;
45 | files = (
46 | );
47 | runOnlyForDeploymentPostprocessing = 0;
48 | };
49 | /* End PBXFrameworksBuildPhase section */
50 |
51 | /* Begin PBXGroup section */
52 | 667F4C711FA3068800F9A900 = {
53 | isa = PBXGroup;
54 | children = (
55 | 667F4C7C1FA3068900F9A900 /* MyTransparentVideoExample */,
56 | 667F4C7B1FA3068900F9A900 /* Products */,
57 | );
58 | sourceTree = "";
59 | };
60 | 667F4C7B1FA3068900F9A900 /* Products */ = {
61 | isa = PBXGroup;
62 | children = (
63 | 667F4C7A1FA3068900F9A900 /* MyTransparentVideoExample.app */,
64 | );
65 | name = Products;
66 | sourceTree = "";
67 | };
68 | 667F4C7C1FA3068900F9A900 /* MyTransparentVideoExample */ = {
69 | isa = PBXGroup;
70 | children = (
71 | 6690684A1FA3656B00598D4C /* playdoh-bat.mp4 */,
72 | 66E04679242E85320096DEE5 /* AlphaFrameFilter.metal */,
73 | 667F4C911FA3126A00F9A900 /* AlphaFrameFilter.swift */,
74 | 669678FA242E7BF00085D444 /* AVAsset+VideoSize.swift */,
75 | 667F4C8F1FA3071A00F9A900 /* AVPlayerView.swift */,
76 | 667F4C7D1FA3068900F9A900 /* AppDelegate.swift */,
77 | 669678FC242E7CFB0085D444 /* CIImage+VerticalSplit.swift */,
78 | 66E04678242E85320096DEE5 /* CIKernel+DefaultMetalLibrary.swift */,
79 | 667F4C7F1FA3068900F9A900 /* ViewController.swift */,
80 | 667F4C811FA3068900F9A900 /* Main.storyboard */,
81 | 667F4C841FA3068900F9A900 /* Assets.xcassets */,
82 | 667F4C861FA3068900F9A900 /* LaunchScreen.storyboard */,
83 | 667F4C891FA3068900F9A900 /* Info.plist */,
84 | );
85 | path = MyTransparentVideoExample;
86 | sourceTree = "";
87 | };
88 | /* End PBXGroup section */
89 |
90 | /* Begin PBXNativeTarget section */
91 | 667F4C791FA3068800F9A900 /* MyTransparentVideoExample */ = {
92 | isa = PBXNativeTarget;
93 | buildConfigurationList = 667F4C8C1FA3068900F9A900 /* Build configuration list for PBXNativeTarget "MyTransparentVideoExample" */;
94 | buildPhases = (
95 | 667F4C761FA3068800F9A900 /* Sources */,
96 | 667F4C771FA3068800F9A900 /* Frameworks */,
97 | 667F4C781FA3068800F9A900 /* Resources */,
98 | );
99 | buildRules = (
100 | );
101 | dependencies = (
102 | );
103 | name = MyTransparentVideoExample;
104 | productName = MyTransparentVideoExample;
105 | productReference = 667F4C7A1FA3068900F9A900 /* MyTransparentVideoExample.app */;
106 | productType = "com.apple.product-type.application";
107 | };
108 | /* End PBXNativeTarget section */
109 |
110 | /* Begin PBXProject section */
111 | 667F4C721FA3068800F9A900 /* Project object */ = {
112 | isa = PBXProject;
113 | attributes = {
114 | LastSwiftUpdateCheck = 0910;
115 | LastUpgradeCheck = 1020;
116 | ORGANIZATIONNAME = "Quentin Fasquel";
117 | TargetAttributes = {
118 | 667F4C791FA3068800F9A900 = {
119 | CreatedOnToolsVersion = 9.1;
120 | LastSwiftMigration = 1020;
121 | ProvisioningStyle = Automatic;
122 | };
123 | };
124 | };
125 | buildConfigurationList = 667F4C751FA3068800F9A900 /* Build configuration list for PBXProject "MyTransparentVideoExample" */;
126 | compatibilityVersion = "Xcode 8.0";
127 | developmentRegion = en;
128 | hasScannedForEncodings = 0;
129 | knownRegions = (
130 | en,
131 | Base,
132 | );
133 | mainGroup = 667F4C711FA3068800F9A900;
134 | productRefGroup = 667F4C7B1FA3068900F9A900 /* Products */;
135 | projectDirPath = "";
136 | projectRoot = "";
137 | targets = (
138 | 667F4C791FA3068800F9A900 /* MyTransparentVideoExample */,
139 | );
140 | };
141 | /* End PBXProject section */
142 |
143 | /* Begin PBXResourcesBuildPhase section */
144 | 667F4C781FA3068800F9A900 /* Resources */ = {
145 | isa = PBXResourcesBuildPhase;
146 | buildActionMask = 2147483647;
147 | files = (
148 | 667F4C881FA3068900F9A900 /* LaunchScreen.storyboard in Resources */,
149 | 6690684B1FA3656B00598D4C /* playdoh-bat.mp4 in Resources */,
150 | 667F4C851FA3068900F9A900 /* Assets.xcassets in Resources */,
151 | 667F4C831FA3068900F9A900 /* Main.storyboard in Resources */,
152 | );
153 | runOnlyForDeploymentPostprocessing = 0;
154 | };
155 | /* End PBXResourcesBuildPhase section */
156 |
157 | /* Begin PBXSourcesBuildPhase section */
158 | 667F4C761FA3068800F9A900 /* Sources */ = {
159 | isa = PBXSourcesBuildPhase;
160 | buildActionMask = 2147483647;
161 | files = (
162 | 669678FB242E7BF00085D444 /* AVAsset+VideoSize.swift in Sources */,
163 | 667F4C901FA3071A00F9A900 /* AVPlayerView.swift in Sources */,
164 | 667F4C921FA3126A00F9A900 /* AlphaFrameFilter.swift in Sources */,
165 | 66E0467A242E85320096DEE5 /* CIKernel+DefaultMetalLibrary.swift in Sources */,
166 | 667F4C801FA3068900F9A900 /* ViewController.swift in Sources */,
167 | 669678FD242E7CFB0085D444 /* CIImage+VerticalSplit.swift in Sources */,
168 | 66E0467B242E85320096DEE5 /* AlphaFrameFilter.metal in Sources */,
169 | 667F4C7E1FA3068900F9A900 /* AppDelegate.swift in Sources */,
170 | );
171 | runOnlyForDeploymentPostprocessing = 0;
172 | };
173 | /* End PBXSourcesBuildPhase section */
174 |
175 | /* Begin PBXVariantGroup section */
176 | 667F4C811FA3068900F9A900 /* Main.storyboard */ = {
177 | isa = PBXVariantGroup;
178 | children = (
179 | 667F4C821FA3068900F9A900 /* Base */,
180 | );
181 | name = Main.storyboard;
182 | sourceTree = "";
183 | };
184 | 667F4C861FA3068900F9A900 /* LaunchScreen.storyboard */ = {
185 | isa = PBXVariantGroup;
186 | children = (
187 | 667F4C871FA3068900F9A900 /* Base */,
188 | );
189 | name = LaunchScreen.storyboard;
190 | sourceTree = "";
191 | };
192 | /* End PBXVariantGroup section */
193 |
194 | /* Begin XCBuildConfiguration section */
195 | 667F4C8A1FA3068900F9A900 /* Debug */ = {
196 | isa = XCBuildConfiguration;
197 | buildSettings = {
198 | ALWAYS_SEARCH_USER_PATHS = NO;
199 | CLANG_ANALYZER_NONNULL = YES;
200 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
201 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
202 | CLANG_CXX_LIBRARY = "libc++";
203 | CLANG_ENABLE_MODULES = YES;
204 | CLANG_ENABLE_OBJC_ARC = YES;
205 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
206 | CLANG_WARN_BOOL_CONVERSION = YES;
207 | CLANG_WARN_COMMA = YES;
208 | CLANG_WARN_CONSTANT_CONVERSION = YES;
209 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
210 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
211 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
212 | CLANG_WARN_EMPTY_BODY = YES;
213 | CLANG_WARN_ENUM_CONVERSION = YES;
214 | CLANG_WARN_INFINITE_RECURSION = YES;
215 | CLANG_WARN_INT_CONVERSION = YES;
216 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
217 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
218 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
219 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
220 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
221 | CLANG_WARN_STRICT_PROTOTYPES = YES;
222 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
223 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
224 | CLANG_WARN_UNREACHABLE_CODE = YES;
225 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
226 | CODE_SIGN_IDENTITY = "iPhone Developer";
227 | COPY_PHASE_STRIP = NO;
228 | DEBUG_INFORMATION_FORMAT = dwarf;
229 | ENABLE_STRICT_OBJC_MSGSEND = YES;
230 | ENABLE_TESTABILITY = YES;
231 | GCC_C_LANGUAGE_STANDARD = gnu11;
232 | GCC_DYNAMIC_NO_PIC = NO;
233 | GCC_NO_COMMON_BLOCKS = YES;
234 | GCC_OPTIMIZATION_LEVEL = 0;
235 | GCC_PREPROCESSOR_DEFINITIONS = (
236 | "DEBUG=1",
237 | "$(inherited)",
238 | );
239 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
240 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
241 | GCC_WARN_UNDECLARED_SELECTOR = YES;
242 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
243 | GCC_WARN_UNUSED_FUNCTION = YES;
244 | GCC_WARN_UNUSED_VARIABLE = YES;
245 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
246 | MTL_ENABLE_DEBUG_INFO = YES;
247 | ONLY_ACTIVE_ARCH = YES;
248 | SDKROOT = iphoneos;
249 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
250 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
251 | };
252 | name = Debug;
253 | };
254 | 667F4C8B1FA3068900F9A900 /* Release */ = {
255 | isa = XCBuildConfiguration;
256 | buildSettings = {
257 | ALWAYS_SEARCH_USER_PATHS = NO;
258 | CLANG_ANALYZER_NONNULL = YES;
259 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
260 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
261 | CLANG_CXX_LIBRARY = "libc++";
262 | CLANG_ENABLE_MODULES = YES;
263 | CLANG_ENABLE_OBJC_ARC = YES;
264 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
265 | CLANG_WARN_BOOL_CONVERSION = YES;
266 | CLANG_WARN_COMMA = YES;
267 | CLANG_WARN_CONSTANT_CONVERSION = YES;
268 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
269 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
270 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
271 | CLANG_WARN_EMPTY_BODY = YES;
272 | CLANG_WARN_ENUM_CONVERSION = YES;
273 | CLANG_WARN_INFINITE_RECURSION = YES;
274 | CLANG_WARN_INT_CONVERSION = YES;
275 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
276 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
277 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
278 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
279 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
280 | CLANG_WARN_STRICT_PROTOTYPES = YES;
281 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
282 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
283 | CLANG_WARN_UNREACHABLE_CODE = YES;
284 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
285 | CODE_SIGN_IDENTITY = "iPhone Developer";
286 | COPY_PHASE_STRIP = NO;
287 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
288 | ENABLE_NS_ASSERTIONS = NO;
289 | ENABLE_STRICT_OBJC_MSGSEND = YES;
290 | GCC_C_LANGUAGE_STANDARD = gnu11;
291 | GCC_NO_COMMON_BLOCKS = YES;
292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
294 | GCC_WARN_UNDECLARED_SELECTOR = YES;
295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
296 | GCC_WARN_UNUSED_FUNCTION = YES;
297 | GCC_WARN_UNUSED_VARIABLE = YES;
298 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
299 | MTL_ENABLE_DEBUG_INFO = NO;
300 | SDKROOT = iphoneos;
301 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
302 | VALIDATE_PRODUCT = YES;
303 | };
304 | name = Release;
305 | };
306 | 667F4C8D1FA3068900F9A900 /* Debug */ = {
307 | isa = XCBuildConfiguration;
308 | buildSettings = {
309 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
310 | CODE_SIGN_STYLE = Automatic;
311 | DEVELOPMENT_TEAM = H9EZHJZX4M;
312 | GCC_PREPROCESSOR_DEFINITIONS = (
313 | "$(inherited)",
314 | CI_SILENCE_GL_DEPRECATION,
315 | );
316 | INFOPLIST_FILE = MyTransparentVideoExample/Info.plist;
317 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
318 | MTLLINKER_FLAGS = "-cikernel";
319 | MTL_COMPILER_FLAGS = "-fcikernel";
320 | PRODUCT_BUNDLE_IDENTIFIER = com.quentinfasquel.MyTransparentVideoExample;
321 | PRODUCT_NAME = "$(TARGET_NAME)";
322 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG CI_SILENCE_GL_DEPRECATION";
323 | SWIFT_VERSION = 5.0;
324 | TARGETED_DEVICE_FAMILY = "1,2";
325 | };
326 | name = Debug;
327 | };
328 | 667F4C8E1FA3068900F9A900 /* Release */ = {
329 | isa = XCBuildConfiguration;
330 | buildSettings = {
331 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
332 | CODE_SIGN_STYLE = Automatic;
333 | DEVELOPMENT_TEAM = H9EZHJZX4M;
334 | GCC_PREPROCESSOR_DEFINITIONS = (
335 | "$(inherited)",
336 | CI_SILENCE_GL_DEPRECATION,
337 | );
338 | INFOPLIST_FILE = MyTransparentVideoExample/Info.plist;
339 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
340 | MTLLINKER_FLAGS = "-cikernel";
341 | MTL_COMPILER_FLAGS = "-fcikernel";
342 | PRODUCT_BUNDLE_IDENTIFIER = com.quentinfasquel.MyTransparentVideoExample;
343 | PRODUCT_NAME = "$(TARGET_NAME)";
344 | SWIFT_VERSION = 5.0;
345 | TARGETED_DEVICE_FAMILY = "1,2";
346 | };
347 | name = Release;
348 | };
349 | /* End XCBuildConfiguration section */
350 |
351 | /* Begin XCConfigurationList section */
352 | 667F4C751FA3068800F9A900 /* Build configuration list for PBXProject "MyTransparentVideoExample" */ = {
353 | isa = XCConfigurationList;
354 | buildConfigurations = (
355 | 667F4C8A1FA3068900F9A900 /* Debug */,
356 | 667F4C8B1FA3068900F9A900 /* Release */,
357 | );
358 | defaultConfigurationIsVisible = 0;
359 | defaultConfigurationName = Release;
360 | };
361 | 667F4C8C1FA3068900F9A900 /* Build configuration list for PBXNativeTarget "MyTransparentVideoExample" */ = {
362 | isa = XCConfigurationList;
363 | buildConfigurations = (
364 | 667F4C8D1FA3068900F9A900 /* Debug */,
365 | 667F4C8E1FA3068900F9A900 /* Release */,
366 | );
367 | defaultConfigurationIsVisible = 0;
368 | defaultConfigurationName = Release;
369 | };
370 | /* End XCConfigurationList section */
371 | };
372 | rootObject = 667F4C721FA3068800F9A900 /* Project object */;
373 | }
374 |
--------------------------------------------------------------------------------