Skip to content

Commit 25e1f3d

Browse files
committed
added new playback commands
1 parent 5df34a0 commit 25e1f3d

File tree

7 files changed

+169
-67
lines changed

7 files changed

+169
-67
lines changed

Sources/swiftui-loop-videoplayer/enum/PlaybackCommand.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ public enum PlaybackCommand: Equatable {
6464
/// Command to remove all applied filters from the video playback.
6565
case removeAllFilters
6666

67+
/// Represents a command to create and possibly clear existing vectors using a shape layer builder.
68+
/// - Parameters:
69+
/// - builder: An instance conforming to `ShapeLayerBuilderProtocol` which will provide the shape layer.
70+
/// - clear: A Boolean value that determines whether existing vector graphics should be cleared before applying the new vector. Defaults to `false`.
71+
case addVector(any ShapeLayerBuilderProtocol, clear: Bool = false)
72+
73+
/// Represents a command to remove all vector graphics from the current view or context.
74+
case removeAllVectors
75+
6776
/// Command to select a specific audio track based on language code.
6877
/// - Parameter languageCode: The language code (e.g., "en" for English) of the desired audio track.
6978
case audioTrack(languageCode: String)
@@ -72,7 +81,7 @@ public enum PlaybackCommand: Equatable {
7281
switch (lhs, rhs) {
7382
case (.play, .play), (.pause, .pause), (.begin, .begin), (.end, .end),
7483
(.mute, .mute), (.unmute, .unmute), (.loop, .loop), (.unloop, .unloop),
75-
(.removeAllFilters, .removeAllFilters):
84+
(.removeAllFilters, .removeAllFilters), (.removeAllVectors, .removeAllVectors):
7685
return true
7786

7887
case (.seek(let lhsTime), .seek(let rhsTime)):
@@ -98,6 +107,8 @@ public enum PlaybackCommand: Equatable {
98107

99108
case (.filter(let lhsFilter, let lhsClear), .filter(let rhsFilter, let rhsClear)):
100109
return lhsFilter == rhsFilter && lhsClear == rhsClear
110+
case let (.addVector(lhsBuilder, lhsClear), .addVector(rhsBuilder, rhsClear)):
111+
return lhsBuilder.id == rhsBuilder.id && lhsClear == rhsClear
101112
default:
102113
return false
103114
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// ShapeLayerProtocol.swift
3+
//
4+
//
5+
// Created by Igor on 13.08.24.
6+
//
7+
8+
import QuartzCore
9+
import CoreGraphics
10+
11+
/// A protocol defining a builder for creating shape layers with a unique identifier.
12+
///
13+
/// Conforming types will be able to construct a CAShapeLayer based on provided frame, bounds, and center.
14+
@available(iOS 14, macOS 11, tvOS 14, *)
15+
public protocol ShapeLayerBuilderProtocol: Identifiable {
16+
17+
var id : UUID { get }
18+
19+
/// Builds a CAShapeLayer using specified geometry.
20+
///
21+
/// - Parameters:
22+
/// - geometry: A tuple containing frame, bounds, and center as `CGRect` and `CGPoint`.
23+
/// - Returns: A configured `CAShapeLayer`.
24+
@MainActor
25+
func build(with geometry: (frame: CGRect, bounds: CGRect)) -> CAShapeLayer
26+
27+
}

Sources/swiftui-loop-videoplayer/protocol/view/AbstractPlayer.swift

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -357,62 +357,5 @@ extension AbstractPlayer{
357357
}
358358
#endif
359359
}
360-
361-
/// Sets the playback command for the video player.
362-
/// - Parameter value: The `PlaybackCommand` to set. This can be one of the following:
363-
/// - `play`: Command to play the video.
364-
/// - `pause`: Command to pause the video.
365-
/// - `seek(to:)`: Command to seek to a specific time in the video.
366-
/// - `begin`: Command to position the video at the beginning.
367-
/// - `end`: Command to position the video at the end.
368-
/// - `mute`: Command to mute the video.
369-
/// - `unmute`: Command to unmute the video.
370-
/// - `volume`: Command to adjust the volume of the video playback.
371-
/// - `subtitles`: Command to set subtitles to a specified language or turn them off.
372-
/// - `playbackSpeed`: Command to adjust the playback speed of the video.
373-
/// - `loop`: Command to enable looping of the video playback.
374-
/// - `unloop`: Command to disable looping of the video playback.
375-
/// - `brightness`: Command to adjust the brightness of the video playback.
376-
/// - `contrast`: Command to adjust the contrast of the video playback.
377-
/// - `filter`: Command to apply a specific Core Image filter to the video.
378-
/// - `removeAllFilters`: Command to remove all applied filters from the video playback.
379-
/// - `audioTrack`: Command to select a specific audio track based on language code.
380-
func setCommand(_ value: PlaybackCommand) {
381-
switch value {
382-
case .play:
383-
play()
384-
case .pause:
385-
pause()
386-
case .seek(to: let time):
387-
seek(to: time)
388-
case .begin:
389-
seekToStart()
390-
case .end:
391-
seekToEnd()
392-
case .mute:
393-
mute()
394-
case .unmute:
395-
unmute()
396-
case .volume(let volume):
397-
setVolume(volume)
398-
case .subtitles(let language):
399-
setSubtitles(to: language)
400-
case .playbackSpeed(let speed):
401-
setPlaybackSpeed(speed)
402-
case .loop:
403-
loop()
404-
case .unloop:
405-
unloop()
406-
case .brightness(let brightness):
407-
adjustBrightness(to: brightness)
408-
case .contrast(let contrast):
409-
adjustContrast(to: contrast)
410-
case .filter(let value, let clear):
411-
applyFilter(value, clear)
412-
case .removeAllFilters:
413-
removeAllFilters()
414-
case .audioTrack(let languageCode):
415-
selectAudioTrack(languageCode: languageCode)
416-
}
417-
}
360+
418361
}

Sources/swiftui-loop-videoplayer/protocol/view/LoopingPlayerProtocol.swift

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@ import AppKit
1818
/// handle errors, and notify a delegate of important events.
1919
@available(iOS 14, macOS 11, tvOS 14, *)
2020
@MainActor
21-
public protocol LoopingPlayerProtocol: AbstractPlayer{
22-
23-
var playerLayer : AVPlayerLayer { get }
21+
public protocol LoopingPlayerProtocol: AbstractPlayer, LayerMakerProtocol{
2422

2523
#if canImport(UIKit)
2624
var layer : CALayer { get }
2725
#elseif canImport(AppKit)
2826
var layer : CALayer? { get set }
2927
var wantsLayer : Bool { get set }
3028
#endif
29+
30+
var playerLayer : AVPlayerLayer { get }
3131

3232
/// The delegate to be notified about errors encountered by the player.
3333
var delegate: PlayerErrorDelegate? { get set }
@@ -143,13 +143,16 @@ extension LoopingPlayerProtocol {
143143
#if canImport(UIKit)
144144
playerLayer.backgroundColor = UIColor.clear.cgColor
145145
layer.addSublayer(playerLayer)
146+
layer.addSublayer(compositeLayer)
146147
#elseif canImport(AppKit)
147148
playerLayer.backgroundColor = NSColor.clear.cgColor
148-
layer = CALayer()
149-
layer?.addSublayer(playerLayer)
150-
wantsLayer = true
149+
let layer = CALayer()
150+
layer.addSublayer(playerLayer)
151+
layer.addSublayer(compositeLayer)
152+
self.layer = layer
153+
self.wantsLayer = true
151154
#endif
152-
155+
compositeLayer.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height)
153156
loop()
154157
player.play()
155158
}
@@ -199,4 +202,69 @@ extension LoopingPlayerProtocol {
199202
guard let error = player.error else { return }
200203
delegate?.didReceiveError(.remoteVideoError(error))
201204
}
205+
206+
func setCommand(_ value: PlaybackCommand) {
207+
/// Sets the playback command for the video player.
208+
/// - Parameter value: The `PlaybackCommand` to set. This can be one of the following:
209+
/// - `play`: Command to play the video.
210+
/// - `pause`: Command to pause the video.
211+
/// - `seek(to:)`: Command to seek to a specific time in the video.
212+
/// - `begin`: Command to position the video at the beginning.
213+
/// - `end`: Command to position the video at the end.
214+
/// - `mute`: Command to mute the video.
215+
/// - `unmute`: Command to unmute the video.
216+
/// - `volume`: Command to adjust the volume of the video playback.
217+
/// - `subtitles`: Command to set subtitles to a specified language or turn them off.
218+
/// - `playbackSpeed`: Command to adjust the playback speed of the video.
219+
/// - `loop`: Command to enable looping of the video playback.
220+
/// - `unloop`: Command to disable looping of the video playback.
221+
/// - `brightness`: Command to adjust the brightness of the video playback.
222+
/// - `contrast`: Command to adjust the contrast of the video playback.
223+
/// - `filter`: Command to apply a specific Core Image filter to the video.
224+
/// - `removeAllFilters`: Command to remove all applied filters from the video playback.
225+
/// - `audioTrack`: Command to select a specific audio track based on language code.
226+
/// - `vector`: Sets a vector graphic operation on the video player.
227+
/// - `removeAllVectors`: Clears all vector graphics from the video player.
228+
switch value {
229+
case .play:
230+
play()
231+
case .pause:
232+
pause()
233+
case .seek(to: let time):
234+
seek(to: time)
235+
case .begin:
236+
seekToStart()
237+
case .end:
238+
seekToEnd()
239+
case .mute:
240+
mute()
241+
case .unmute:
242+
unmute()
243+
case .volume(let volume):
244+
setVolume(volume)
245+
case .subtitles(let language):
246+
setSubtitles(to: language)
247+
case .playbackSpeed(let speed):
248+
setPlaybackSpeed(speed)
249+
case .loop:
250+
loop()
251+
case .unloop:
252+
unloop()
253+
case .brightness(let brightness):
254+
adjustBrightness(to: brightness)
255+
case .contrast(let contrast):
256+
adjustContrast(to: contrast)
257+
case .filter(let value, let clear):
258+
applyFilter(value, clear)
259+
case .removeAllFilters:
260+
removeAllFilters()
261+
case .audioTrack(let languageCode):
262+
selectAudioTrack(languageCode: languageCode)
263+
case .addVector(let builder, let clear):
264+
addVectorLayer(builder: builder, clear: clear)
265+
case .removeAllVectors:
266+
removeAllVectors()
267+
}
268+
}
269+
202270
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//
2+
// VectorLayerProtocol.swift
3+
//
4+
//
5+
// Created by Igor Shelopaev on 13.08.24.
6+
//
7+
8+
#if canImport(UIKit)
9+
import UIKit
10+
#elseif canImport(AppKit)
11+
import AppKit
12+
#endif
13+
import QuartzCore
14+
15+
@available(iOS 14, macOS 11, tvOS 14, *)
16+
@MainActor
17+
public protocol LayerMakerProtocol{
18+
19+
var compositeLayer : CALayer{ get }
20+
21+
var frame: CGRect { get set }
22+
23+
var bounds : CGRect { get set }
24+
25+
func addVectorLayer(builder : any ShapeLayerBuilderProtocol, clear: Bool)
26+
27+
func removeAllVectors()
28+
}
29+
30+
extension LayerMakerProtocol{
31+
32+
@MainActor
33+
/// Sets a vector graphic operation on the video player.
34+
/// - Parameters:
35+
/// - builder: An instance conforming to `ShapeLayerBuilderProtocol` to provide the shape layer.
36+
/// - clear: A Boolean value indicating whether to clear existing vector graphics before applying the new one. Defaults to `false`.
37+
func addVectorLayer(builder : any ShapeLayerBuilderProtocol, clear: Bool){
38+
if clear{ removeAllVectors() }
39+
let layer = builder.build(with: (frame, bounds))
40+
compositeLayer.addSublayer(layer)
41+
}
42+
43+
@MainActor
44+
/// Clears all vector graphics from the video player.
45+
func removeAllVectors(){
46+
compositeLayer.sublayers?.forEach { $0.removeFromSuperlayer() }
47+
}
48+
49+
}

Sources/swiftui-loop-videoplayer/view/loop/player/ios/LoopingPlayerUIView.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class LoopingPlayerUIView: UIView, LoopingPlayerProtocol {
2727
/// `contrast` indicates the level of contrast adjustment for the video content.
2828
internal var contrast: Float = 1
2929

30+
internal let compositeLayer = CALayer()
31+
3032
/// The AVPlayerLayer that displays the video content.
3133
internal let playerLayer = AVPlayerLayer()
3234

@@ -52,7 +54,7 @@ class LoopingPlayerUIView: UIView, LoopingPlayerProtocol {
5254
/// - gravity: The AVLayerVideoGravity to be applied to the video layer.
5355
required init(asset: AVURLAsset, gravity: AVLayerVideoGravity) {
5456
super.init(frame: .zero)
55-
setupPlayerComponents(asset: asset, gravity: gravity)
57+
setupPlayerComponents(asset: asset, gravity: gravity)
5658
}
5759

5860
required init?(coder: NSCoder) {

Sources/swiftui-loop-videoplayer/view/loop/player/mac/LoopingPlayerNSView.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class LoopingPlayerNSView: NSView, LoopingPlayerProtocol {
2929
/// `contrast` indicates the level of contrast adjustment for the video content.
3030
internal var contrast: Float = 1
3131

32+
internal let compositeLayer = CALayer()
33+
3234
/// The AVPlayerLayer that displays the video content.
3335
internal let playerLayer = AVPlayerLayer()
3436

0 commit comments

Comments
 (0)