Skip to content

Commit

Permalink
Merge branch 'release/1.0.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
Kofktu committed Jan 8, 2022
2 parents eb21eec + a57e97c commit f60e14a
Show file tree
Hide file tree
Showing 9 changed files with 275 additions and 132 deletions.
2 changes: 1 addition & 1 deletion Example/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class PIPXibViewController: UIViewController, PIPUsable {
}

@available(iOS 15.0, *)
extension ViewController: AVPIPKitUsable {
extension ViewController: AVPIPUIKitUsable {

var pipTargetView: UIView {
view
Expand Down
2 changes: 1 addition & 1 deletion PIPKit.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Pod::Spec.new do |s|
s.name = 'PIPKit'
s.version = '1.0.0'
s.version = '1.0.1'
s.summary = 'PIP(Picture in Picture) for iOS'

# This description is used to generate tags and improve search results.
Expand Down
12 changes: 10 additions & 2 deletions PIPKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
CE9657662782BCAF00303B7F /* AVPIPKitVideoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE9657652782BCAF00303B7F /* AVPIPKitVideoController.swift */; };
CE9657682782BEB500303B7F /* PIPKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEE9972F2782A5CC002FA127 /* PIPKit.framework */; };
CE9657692782BEB500303B7F /* PIPKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEE9972F2782A5CC002FA127 /* PIPKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CEE5C8E027899B1500BEDD03 /* AVPIPKitRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE5C8DF27899B1500BEDD03 /* AVPIPKitRenderer.swift */; };
CEE5C8E22789A0CE00BEDD03 /* AVPIPKitUsable+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE5C8E12789A0CE00BEDD03 /* AVPIPKitUsable+UIKit.swift */; };
CEE997332782A5CC002FA127 /* PIPKit.h in Headers */ = {isa = PBXBuildFile; fileRef = CEE997322782A5CC002FA127 /* PIPKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
CEE9973E2782A5F1002FA127 /* PIPKitEventDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE9973A2782A5F1002FA127 /* PIPKitEventDispatcher.swift */; };
CEE9973F2782A5F1002FA127 /* UIWindow+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE9973B2782A5F1002FA127 /* UIWindow+Extension.swift */; };
Expand Down Expand Up @@ -63,6 +65,8 @@
CE9657612782BC3900303B7F /* UIView+Associated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Associated.swift"; sourceTree = "<group>"; };
CE9657632782BC9100303B7F /* AVPIPKitVideoProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPIPKitVideoProvider.swift; sourceTree = "<group>"; };
CE9657652782BCAF00303B7F /* AVPIPKitVideoController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPIPKitVideoController.swift; sourceTree = "<group>"; };
CEE5C8DF27899B1500BEDD03 /* AVPIPKitRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPIPKitRenderer.swift; sourceTree = "<group>"; };
CEE5C8E12789A0CE00BEDD03 /* AVPIPKitUsable+UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVPIPKitUsable+UIKit.swift"; sourceTree = "<group>"; };
CEE9972F2782A5CC002FA127 /* PIPKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PIPKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CEE997322782A5CC002FA127 /* PIPKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PIPKit.h; sourceTree = "<group>"; };
CEE9973A2782A5F1002FA127 /* PIPKitEventDispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PIPKitEventDispatcher.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -101,6 +105,8 @@
isa = PBXGroup;
children = (
CE96575D2782BB6D00303B7F /* AVPIPKitUsable.swift */,
CEE5C8E12789A0CE00BEDD03 /* AVPIPKitUsable+UIKit.swift */,
CEE5C8DF27899B1500BEDD03 /* AVPIPKitRenderer.swift */,
CE9657632782BC9100303B7F /* AVPIPKitVideoProvider.swift */,
CE9657652782BCAF00303B7F /* AVPIPKitVideoController.swift */,
);
Expand Down Expand Up @@ -307,6 +313,8 @@
CE9657642782BC9100303B7F /* AVPIPKitVideoProvider.swift in Sources */,
CE9657662782BCAF00303B7F /* AVPIPKitVideoController.swift in Sources */,
CE9657602782BBCC00303B7F /* UIViewController+Associated.swift in Sources */,
CEE5C8E027899B1500BEDD03 /* AVPIPKitRenderer.swift in Sources */,
CEE5C8E22789A0CE00BEDD03 /* AVPIPKitUsable+UIKit.swift in Sources */,
CEE997412782A5F1002FA127 /* PIPUsable.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -496,7 +504,7 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 1.0.0;
MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = kr.kofktu.PIPKit;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
Expand Down Expand Up @@ -525,7 +533,7 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 1.0.0;
MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = kr.kofktu.PIPKit;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
Expand Down
93 changes: 93 additions & 0 deletions PIPKit/Classes/AVPIPKit/AVPIPKitRenderer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//
// AVPIPKitRenderer.swift
// PIPKit
//
// Created by Kofktu on 2022/01/08.
//

import Foundation
import Combine
import UIKit

@available(iOS 15.0, *)
public protocol AVPIPKitRenderer {

var policy: AVPIPKitRenderPolicy { get }
var renderPublisher: AnyPublisher<UIImage, Never> { get }

func start()
func stop()

}

@available(iOS 15.0, *)
final class AVPIPUIKitRenderer: AVPIPKitRenderer {

let policy: AVPIPKitRenderPolicy
var renderPublisher: AnyPublisher<UIImage, Never> {
render.eraseToAnyPublisher()
}

private var isRunning: Bool = false
private weak var targetView: UIView?
private var displayLink: CADisplayLink?
private let render = PassthroughSubject<UIImage, Never>()

deinit {
stop()
}

init(targetView: UIView, policy: AVPIPKitRenderPolicy) {
self.targetView = targetView
self.policy = policy
}

func start() {
if isRunning {
return
}

isRunning = true
onRender()

guard case .preferredFramesPerSecond(let preferredFramesPerSecond) = policy else {
return
}

displayLink = CADisplayLink(target: self, selector: #selector(onRender))
displayLink?.preferredFrameRateRange = CAFrameRateRange(minimum: 1, maximum: Float(preferredFramesPerSecond), preferred: 0)
displayLink?.add(to: .main, forMode: .default)
}

func stop() {
guard isRunning else {
return
}

displayLink?.invalidate()
displayLink = nil
isRunning = false
}

// MARK: - Private
@objc private func onRender() {
guard let targetView = targetView else {
stop()
return
}

render.send(targetView.uiImage)
}

}

@available(iOS 15.0, *)
private extension UIView {

var uiImage: UIImage {
UIGraphicsImageRenderer(bounds: bounds).image { context in
layer.render(in: context.cgContext)
}
}

}
82 changes: 82 additions & 0 deletions PIPKit/Classes/AVPIPKit/AVPIPKitUsable+UIKit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// AVPIPKitUsable+UIKit.swift
// PIPKit
//
// Created by Kofktu on 2022/01/08.
//

import Foundation
import UIKit

@available(iOS 15.0, *)
public protocol AVPIPUIKitUsable: AVPIPKitUsable {

var pipTargetView: UIView { get }
var renderPolicy: AVPIPKitRenderPolicy { get }

}

@available(iOS 15.0, *)
public extension AVPIPUIKitUsable {

var renderPolicy: AVPIPKitRenderPolicy {
.preferredFramesPerSecond(UIScreen.main.maximumFramesPerSecond)
}

var renderer: AVPIPKitRenderer {
AVPIPUIKitRenderer(targetView: pipTargetView, policy: renderPolicy)
}

}

@available(iOS 15.0, *)
public extension AVPIPUIKitUsable where Self: UIViewController {

var pipTargetView: UIView { view }

func startPictureInPicture() {
setupIfNeeded()
videoController?.start()
}

func stopPictureInPicture() {
assert(videoController != nil)
videoController?.stop()
}

// MARK: - Private
private func setupIfNeeded() {
guard videoController == nil else {
return
}

videoController = createVideoController()
}

}

@available(iOS 15.0, *)
public extension AVPIPUIKitUsable where Self: UIView {

var pipTargetView: UIView { self }

func startPictureInPicture() {
setupIfNeeded()
videoController?.start()
}

func stopPictureInPicture() {
assert(videoController != nil)
videoController?.stop()
}

// MARK: - Private
private func setupIfNeeded() {
guard videoController == nil else {
return
}

videoController = createVideoController()
}

}
70 changes: 15 additions & 55 deletions PIPKit/Classes/AVPIPKit/AVPIPKitUsable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,24 @@ public enum AVPIPKitRenderPolicy {

}

@available(iOS 15.0, *)
extension AVPIPKitRenderPolicy {

var preferredFramesPerSecond: Int {
switch self {
case .once:
return 1
case .preferredFramesPerSecond(let preferredFramesPerSecond):
return preferredFramesPerSecond
}
}

}

@available(iOS 15.0, *)
public protocol AVPIPKitUsable {

var pipTargetView: UIView { get }
var renderPolicy: AVPIPKitRenderPolicy { get }
var renderer: AVPIPKitRenderer { get }

func startPictureInPicture()
func stopPictureInPicture()
Expand All @@ -35,58 +48,5 @@ public extension AVPIPKitUsable {
AVPictureInPictureController.isPictureInPictureSupported()
}

var renderPolicy: AVPIPKitRenderPolicy {
.preferredFramesPerSecond(UIScreen.main.maximumFramesPerSecond)
}

}

@available(iOS 15.0, *)
public extension AVPIPKitUsable where Self: UIViewController {

func startPictureInPicture() {
setupIfNeeded()
videoController?.start()
}

func stopPictureInPicture() {
assert(videoController != nil)
videoController?.stop()
}

// MARK: - Private
private func setupIfNeeded() {
guard videoController == nil else {
return
}

videoController = createVideoController()
}

}

@available(iOS 15.0, *)
public extension AVPIPKitUsable where Self: UIView {

var pipTargetView: UIView { self }

func startPictureInPicture() {
setupIfNeeded()
videoController?.start()
}

func stopPictureInPicture() {
assert(videoController != nil)
videoController?.stop()
}

// MARK: - Private
private func setupIfNeeded() {
guard videoController == nil else {
return
}

videoController = createVideoController()
}

}
10 changes: 6 additions & 4 deletions PIPKit/Classes/AVPIPKit/AVPIPKitVideoController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ final class AVPIPKitVideoController: NSObject {
pipPossibleObservation?.invalidate()
}

init(targetView: UIView, renderPolicy: AVPIPKitRenderPolicy) {
videoProvider = PIPVideoProvider(targetView: targetView, renderPolicy: renderPolicy)
init(renderer: AVPIPKitRenderer) {
videoProvider = PIPVideoProvider(renderer: renderer)
super.init()
}

Expand Down Expand Up @@ -74,6 +74,10 @@ final class AVPIPKitVideoController: NSObject {

// MARK: - Private
private func cachedAndPrepareAudioSession() {
guard AVAudioSession.sharedInstance().category != .playback else {
return
}

audioSessionCategory = AVAudioSession.sharedInstance().category
audioSessionMode = AVAudioSession.sharedInstance().mode
audioSessionCategoryOptions = AVAudioSession.sharedInstance().categoryOptions
Expand Down Expand Up @@ -123,8 +127,6 @@ final class AVPIPKitVideoController: NSObject {
DispatchQueue.main.async {
if changed.newValue == true {
self?.start()
} else {
self?.stop()
}
}
})
Expand Down

0 comments on commit f60e14a

Please sign in to comment.