From c7b084ed63f766d3b8df1b269c37b1ddbc6f13cc Mon Sep 17 00:00:00 2001 From: Maxime Chapelet <umxprime@videolabs.io> Date: Thu, 2 Jan 2025 19:00:52 +0100 Subject: [PATCH] Add Picture In Picture support --- .../pip.enter.imageset/Contents.json | 23 ++++++++ .../pip.enter.imageset/pip.enter.24x24.png | Bin 0 -> 486 bytes .../pip.enter.imageset/pip.enter.24x24@2x.png | Bin 0 -> 872 bytes .../pip.enter.imageset/pip.enter.24x24@3x.png | Bin 0 -> 1249 bytes .../PictureInPictureMediaController.swift | 53 ++++++++++++++++++ Sources/Playback/Control/VLCPlaybackService.h | 1 + Sources/Playback/Control/VLCPlaybackService.m | 43 ++++++++++++-- .../Subviews/MediaNavigationBar.swift | 16 ++++++ .../VideoPlayerViewController.swift | 4 ++ VLC.xcodeproj/project.pbxproj | 4 ++ 10 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 Resources/iOS/Images.xcassets/NewPlayer/pip.enter.imageset/Contents.json create mode 100644 Resources/iOS/Images.xcassets/NewPlayer/pip.enter.imageset/pip.enter.24x24.png create mode 100644 Resources/iOS/Images.xcassets/NewPlayer/pip.enter.imageset/pip.enter.24x24@2x.png create mode 100644 Resources/iOS/Images.xcassets/NewPlayer/pip.enter.imageset/pip.enter.24x24@3x.png create mode 100644 Sources/Playback/Control/PictureInPictureMediaController.swift diff --git a/Resources/iOS/Images.xcassets/NewPlayer/pip.enter.imageset/Contents.json b/Resources/iOS/Images.xcassets/NewPlayer/pip.enter.imageset/Contents.json new file mode 100644 index 000000000..6fab6174b --- /dev/null +++ b/Resources/iOS/Images.xcassets/NewPlayer/pip.enter.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "pip.enter.24x24.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "pip.enter.24x24@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "pip.enter.24x24@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Resources/iOS/Images.xcassets/NewPlayer/pip.enter.imageset/pip.enter.24x24.png b/Resources/iOS/Images.xcassets/NewPlayer/pip.enter.imageset/pip.enter.24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..9be881d5ef21709c9a7618790055792bb7e7338f GIT binary patch literal 486 zcmV<C0U7>@P)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00009a7bBm000id z000id0mpBsWB>pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10dq-2 zK~zYIwU)U`1VI!<PqmE;;=VDFAc%p2h=Gx7<`)=frm3lsk>*B*nwe<g2PiHd;R2#Y zGNT~On7MRagKouXoSB-^x$xk@E8eMoeXF{SBx<Rkrrm)si4|ZL==A9E3S0tPKrUT0 zk~V>LzybviKoigiTmhrNdr?)=wWOS+UP*XFouoa*8cWv$pbxkJo}wsP07epaeG-)v z0jEixNf!ga5RV6A%#5}6448_d=-67j>Nut^E1Gm6ETRR$s-!M!?YS`q;LZ_HQfZqu zSZlu@Q_|3X1BQtm@d99s>HjMLNqI>}zqlhKX)wv#B<)CgkehOev%14F?Cg(x16ZzN z-+jO$@Zow@OjgnSr>0#2?Vf;+0C*!l?ln;e+yE268gO4?U9&I1NSXr<fmYupWmGW9 z2d4)Z2U<!j+y;980;r^0V88>AcXiRDM{|IzD`4LfaNs&311tga#P6$;o`Dl!*A?Kc cn*UXN0@~vCu;}YITmS$707*qoM6N<$g330@$N&HU literal 0 HcmV?d00001 diff --git a/Resources/iOS/Images.xcassets/NewPlayer/pip.enter.imageset/pip.enter.24x24@2x.png b/Resources/iOS/Images.xcassets/NewPlayer/pip.enter.imageset/pip.enter.24x24@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b0f011328ea45f444bbe1e1321a9140efe88368b GIT binary patch literal 872 zcmV-u1DE`XP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00009a7bBm0013_ z0013_0gvVJWdHyG8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10`*Bm zK~!jg?U_Aj6hRn<pPkDuYJ}4mB@l^76hRS7V->+5L2RQ4LBT?_wMb)OX=P(2f`v_j zBI%PVK?x!XiHIrGAYvh?Awd(po7ZAbGI!+e=5lkliR6Lf_Pg2n?tOQ5cfQ?;B(a$> zNeif!Edxo@P!M+lr+~dcOC5DA0`Gw-V5y>>l8}^^G$JXhWLc0jEGbf~tLz6!O_HXQ zP+NlA@-z@h8Uh{`49p7fImEf)K_l_0>t2s5KsWHEY;Q@A9p@QImn0eABM|7bUDEx6 zQMl~W*8n|^pC`a0z=k`(UMvAOfFI6pJw^7-!vH&+_)S6kE+DE(nm;SRoD;YCbTZ@% z{PS<aT6@h}`&H5y&|J`O574nrp{4lkY<nHJQWM!>Ad+-W(o<GV9JkiaT5HGGDKaQU z4H#o?Nty<bbT((A*IGLPv;#+g*80dSPN@-CNTpK!t_f@HB#?Ic9;~&%a+Iru7E-BH zKkzCS=be|?Z1zx1R8@daG1m?3U#lUCBClIrEh%T<Sngs?7ir(vBOvJx@mx@>J#hnt z<#ZlV6y22cf_PRul>2UV+IQDhUU^Cy0A`FaGfo@<U71X##~9ONjCr|6pDy4!(3jh8 ztU!-j6<h%Zo!Al=n3KRg;4J`>J~{=|aFs<_pW>Rn)+|f9s9Z4@iZ);r&l;vmnt=zv zfsh6+P~w8G0WHAQkOoc$%;igONCWKwbNSK{(m+$dT)s4K`u9SG2I}?n6$}IXbmBFO zwvCJdJ~{Cq8=yd|ga%*&IFpOpfY-#Y_`X+|>u5k--b9kpz$~yY2?13yZ-8&W0PrDU zG3z4I>IzL0aGv<>bSJR0!dxdP{@_z9=3N8TG7g*$mCTd*|863yh9p)C71F?Bz+Ap8 zgfuW$ewX093u$01U@q_f3~AswFcL6dIi3Ndq3=RNz+GTDa9)=z@gmFskO?^txTJ|E y#4cc4pq#&m*UYAH*3N2Wpl)nUWJ$B>4g3X_vrIwF*B*)h0000<MNUMnLSTa5Z+e*k literal 0 HcmV?d00001 diff --git a/Resources/iOS/Images.xcassets/NewPlayer/pip.enter.imageset/pip.enter.24x24@3x.png b/Resources/iOS/Images.xcassets/NewPlayer/pip.enter.imageset/pip.enter.24x24@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..c93663fb47704e1b849aa25906fd642200c01d93 GIT binary patch literal 1249 zcmV<71Rnc|P)<h;3K|Lk000e1NJLTq002k;002k`1^@s6RqeA!00009a7bBm001mX z001mX0e5<IO#lD@8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H11a3)0 zK~#90?VL|+R7D(sznNF4Rz+&6XaX1#X;I+hAA(p(l$dBzZ$>@9ML{@Fu3Wt6)u<Rf zNz{mN(u)_OG0{Y0A;yC>F=C8bln5Awf<(a1jEC7Z>vMMJ{bS~B`@Uq;W_I@X`|a2F zc4ywqt1!mU&>b1OfV$HN^3VwK&<OI-2=Xw{<GBP_1}p`x4pUBHE&^wPQ@~u6fzyH( z15W^3fepZQRVE9-1>g(dAn*=WTq#7yIA|1j4_H?=Xib;_9>WSNOk2<`z?ZF`^NI7o zJ;1j`1GGP+_cQ<Gfc=Pgy-WaMj9CkO4Tvs%eg}>Nx=f-ghyYdqH}>eW5javL_yF)Q zZaD{my}&O;0!QFx;5lHs<GDvDC`e+@(KMd|+vp!T04ISRz+uO8uJ@P;3EbppKSX^` zDIW(MZOe)Vlfd<k_V0=YtO2gtUGFgy68CiDe7Zp?^_157?YX(Rhl(Z(#5qS>QZ!hm zxtLOFLI|-R*eazwtd!c;qx%NnuA&9ijZA`s5UV?1BZPR<B}r?&OKbhP*7^vrzN#W) zl1Y%1@&NF2XH!~|)_M={S}SIvwccE5rIF}0oA2Z|Ddo7<`eR^KtF0|b2q8v*7gDbo zt*pw3WEBK3Bc<G;wf+P<as?~t4ID?J5MoUQ^+hM|1+yQdl;gmUz8J=svBFi=h$2@M zXQY%{fHR(c%YmT+@oPn)ORie$M}grCu~&Qg)SldewAPcrOIi9VrTYFf%Oyx_y%Tsj zYkwicf)ca>c$Mzgja)1!L1%!U@&um`-(-2lL4Qgq?*Q&pO05w>j0z#fS`u!<u85?m zbO|N~c?1Dm2BwlEnQAu(A;8X-S1F}NTLZ)~;F=^!Ui8FU0z3(fW9M$ez_mGRO5-{! zmIHq{+Am_yjPHTZu-Cy({HKR8<}34m?l#77g&%v2F|&@`F}ZAv*=LLyG_5*gEj_=2 z{We?@DStN>0h7Q7t>2>vdK}mtA#WZw1J6TvK>~O-aL%Ia21Y^)S`VxZoU<rHz(i<4 z_Xf^amQA4rtqh#6EGt6`S{gWCS%yOkS`;{6Sr#{2@H{kvJT!tlNZd2-8$uUSS0r|K zXqtJkWK#ta_|?(=G;memT55^Jz4K}v@F8~6)^B5%N)g7GTd^06dR;u5rJ4;@L%?7K zaY8XYfZb1g2)pI38-+I@^*ima`AOhd%tGo)ThJ_UAMOp13!2Vb5!!6T-m9?*yIRrr z>+2<$b_q%|NOh9-VdeJ=(^-~%F0~$2P*t2@G;=WzjUW%91@+tReHB{JY~XxlIU8Eg z>A?BQayqo2qk;34<!F|1P?Zc~Z_abwVN^E^aC_(@=n{70LO<|6a4hr@1a|4?BjEnf ziF0rgdqd8N7~`P7feGN<2)VQH4e%hXjZ`s?piUkDp26-gi+R`eIbaHS3;T!rcOfIT zpiV}xt0^J>+rwYjEqJHsGKj5{pjy(*#XK~EJT!tlG=e-df;{{KTeI_-E7wq^00000 LNkvXXu0mjfcFr*6 literal 0 HcmV?d00001 diff --git a/Sources/Playback/Control/PictureInPictureMediaController.swift b/Sources/Playback/Control/PictureInPictureMediaController.swift new file mode 100644 index 000000000..b13e7ddff --- /dev/null +++ b/Sources/Playback/Control/PictureInPictureMediaController.swift @@ -0,0 +1,53 @@ +/***************************************************************************** + * PictureInPictureMediaController.swift + * VLC for iOS + ***************************************************************************** + * Copyright (c) 2025 VLC authors and VideoLAN + * $Id$ + * + * Authors: Maxime Chapelet <umxprime # videolabs.io> + * + * Refer to the COPYING file of the official project for license. + *****************************************************************************/ + +import Foundation +import VLCKit + +@objc(VLCPictureInPictureMediaController) +final class PictureInPictureMediaController: NSObject { + private let mediaPlayer: VLCMediaPlayer + @objc(initWithMediaPlayer:) + init(_ mediaPlayer: VLCMediaPlayer) { + self.mediaPlayer = mediaPlayer + } +} + +extension PictureInPictureMediaController: VLCPictureInPictureMediaControlling { + func play() { + mediaPlayer.play() + } + + func pause() { + mediaPlayer.pause() + } + + func seek(by offset: Int64, completion: (() -> Void)!) { + mediaPlayer.jump(withOffset: Int32(offset), completion: completion) + } + + func mediaLength() -> Int64 { + return mediaPlayer.media?.length.value?.int64Value ?? 0 + } + + func mediaTime() -> Int64 { + return mediaPlayer.time.value?.int64Value ?? 0 + } + + func isMediaSeekable() -> Bool { + return mediaPlayer.isSeekable + } + + func isMediaPlaying() -> Bool { + return mediaPlayer.isPlaying + } +} diff --git a/Sources/Playback/Control/VLCPlaybackService.h b/Sources/Playback/Control/VLCPlaybackService.h index 3e7cb019a..f4fc636e2 100644 --- a/Sources/Playback/Control/VLCPlaybackService.h +++ b/Sources/Playback/Control/VLCPlaybackService.h @@ -168,6 +168,7 @@ NS_SWIFT_NAME(PlaybackService) - (void)addSubtitlesToCurrentPlaybackFromURL:(NSURL *)subtitleURL; - (void)setAmplification:(CGFloat)amplification forBand:(unsigned int)index; +- (void)togglePictureInPicture; #if TARGET_OS_IOS - (void)savePlaybackState; diff --git a/Sources/Playback/Control/VLCPlaybackService.m b/Sources/Playback/Control/VLCPlaybackService.m index 4941fd630..362b6de28 100644 --- a/Sources/Playback/Control/VLCPlaybackService.m +++ b/Sources/Playback/Control/VLCPlaybackService.m @@ -2,7 +2,7 @@ * VLCPlaybackService.m * VLC for iOS ***************************************************************************** - * Copyright (c) 2013-2023 VLC authors and VideoLAN + * Copyright (c) 2013-2025 VLC authors and VideoLAN * $Id$ * * Authors: Felix Paul Kühne <fkuehne # videolan.org> @@ -44,9 +44,9 @@ NSString *const VLCPlaybackServiceShuffleModeUpdated = @"VLCPlaybackServiceShuff NSString *const VLCPlaybackServicePlaybackDidMoveOnToNextItem = @"VLCPlaybackServicePlaybackDidMoveOnToNextItem"; #if TARGET_OS_IOS -@interface VLCPlaybackService () <VLCMediaPlayerDelegate, VLCMediaDelegate, VLCMediaListPlayerDelegate, EqualizerViewDelegate> +@interface VLCPlaybackService () <VLCMediaPlayerDelegate, VLCMediaDelegate, VLCMediaListPlayerDelegate, EqualizerViewDelegate, VLCDrawable, VLCPictureInPictureDrawable> #else -@interface VLCPlaybackService () <VLCMediaPlayerDelegate, VLCMediaDelegate, VLCMediaListPlayerDelegate> +@interface VLCPlaybackService () <VLCMediaPlayerDelegate, VLCMediaDelegate, VLCMediaListPlayerDelegate, VLCDrawable, VLCPictureInPictureDrawable> #endif { VLCMediaPlayer *_backgroundDummyPlayer; @@ -86,6 +86,9 @@ NSString *const VLCPlaybackServicePlaybackDidMoveOnToNextItem = @"VLCPlaybackSer BOOL _openInMiniPlayer; } +@property (weak, atomic) id<VLCPictureInPictureWindowControlling> pipController; +@property (atomic) id<VLCPictureInPictureMediaControlling> mediaController; + @end @implementation VLCPlaybackService @@ -251,9 +254,9 @@ NSString *const VLCPlaybackServicePlaybackDidMoveOnToNextItem = @"VLCPlaybackSer } if (libVLCOptions.count > 0) { _listPlayer = [[VLCMediaListPlayer alloc] initWithOptions:libVLCOptions - andDrawable:_actualVideoOutputView]; + andDrawable:self]; } else { - _listPlayer = [[VLCMediaListPlayer alloc] initWithDrawable:_actualVideoOutputView]; + _listPlayer = [[VLCMediaListPlayer alloc] initWithDrawable:self]; } _listPlayer.delegate = self; @@ -298,6 +301,9 @@ NSString *const VLCPlaybackServicePlaybackDidMoveOnToNextItem = @"VLCPlaybackSer newFilter.enabled = _adjustFilter.mediaPlayerAdjustFilter.isEnabled; _adjustFilter = [[VLCPlaybackServiceAdjustFilter alloc] initWithMediaPlayerAdjustFilter:newFilter]; _mediaPlayer = _listPlayer.mediaPlayer; +#if TARGET_OS_IOS + _mediaController = [[VLCPictureInPictureMediaController alloc] initWithMediaPlayer:_mediaPlayer]; +#endif [_mediaPlayer setDelegate:self]; CGFloat defaultPlaybackSpeed = [[defaults objectForKey:kVLCSettingPlaybackSpeedDefaultValue] floatValue]; @@ -827,6 +833,10 @@ NSString *const VLCPlaybackServicePlaybackDidMoveOnToNextItem = @"VLCPlaybackSer - (void)mediaPlayerStateChanged:(VLCMediaPlayerState)currentState { + id<VLCPictureInPictureWindowControlling> pipController = _pipController; + dispatch_async(dispatch_get_main_queue(), ^{ + [pipController invalidatePlaybackState]; + }); switch (currentState) { case VLCMediaPlayerStateBuffering: { /* attach delegate */ @@ -1830,4 +1840,27 @@ NSString *const VLCPlaybackServicePlaybackDidMoveOnToNextItem = @"VLCPlaybackSer object:self]; } +#pragma mark - VLCDrawable + +- (void)addSubview:(UIView *)view { + [_actualVideoOutputView addSubview:view]; +} + +- (CGRect)bounds { + return [_actualVideoOutputView bounds]; +} + +#pragma mark - VLCPictureInPictureDrawable + +- (void (^)(id<VLCPictureInPictureWindowControlling>))pictureInPictureReady { + __weak typeof(self) drawable = self; + return ^(id<VLCPictureInPictureWindowControlling> pipController){ + drawable.pipController = pipController; + }; +} + +- (void)togglePictureInPicture { + [self.pipController startPictureInPicture]; +} + @end diff --git a/Sources/Playback/Player/VideoPlayer-iOS/Subviews/MediaNavigationBar.swift b/Sources/Playback/Player/VideoPlayer-iOS/Subviews/MediaNavigationBar.swift index b3ee0ded4..8962a0fb9 100644 --- a/Sources/Playback/Player/VideoPlayer-iOS/Subviews/MediaNavigationBar.swift +++ b/Sources/Playback/Player/VideoPlayer-iOS/Subviews/MediaNavigationBar.swift @@ -16,6 +16,7 @@ import MediaPlayer @objc (VLCMediaNavigationBarDelegate) protocol MediaNavigationBarDelegate { func mediaNavigationBarDidTapClose(_ mediaNavigationBar: MediaNavigationBar) + @objc optional func mediaNavigationBarDidTapPictureInPicture(_ mediaNavigationBar: MediaNavigationBar) @objc optional func mediaNavigationBarDidToggleQueueView(_ mediaNavigationBar: MediaNavigationBar) @objc optional func mediaNavigationBarDidToggleChromeCast(_ mediaNavigationBar: MediaNavigationBar) func mediaNavigationBarDidCloseLongPress(_ mediaNavigationBar: MediaNavigationBar) @@ -104,6 +105,16 @@ private enum RendererActionSheetContent: Int, CaseIterable { return chromeButton }() + lazy var pictureInPictureButton: UIButton = { + var button = UIButton(type: .system) + button.addTarget(self, action: #selector(togglePictureInPicture), + for: .touchDown) + button.setImage(UIImage(named: "pip.enter"), for: .normal) + button.tintColor = .white + button.setContentHuggingPriority(.defaultHigh, for: .horizontal) + return button + }() + private var closureQueue: (() -> Void)? = nil private lazy var deviceActionSheet: ActionSheet = { @@ -187,6 +198,7 @@ private enum RendererActionSheetContent: Int, CaseIterable { addArrangedSubview(rotateButton) addArrangedSubview(queueButton) addArrangedSubview(deviceButton) + addArrangedSubview(pictureInPictureButton) } // MARK: Gesture recognizer @@ -210,6 +222,10 @@ private enum RendererActionSheetContent: Int, CaseIterable { animated: true) } + func togglePictureInPicture() { + delegate?.mediaNavigationBarDidTapPictureInPicture?(self) + } + func handleCloseTap() { assert(delegate != nil, "Delegate not set for MediaNavigationBar") delegate?.mediaNavigationBarDidTapClose(self) diff --git a/Sources/Playback/Player/VideoPlayer-iOS/VideoPlayerViewController.swift b/Sources/Playback/Player/VideoPlayer-iOS/VideoPlayerViewController.swift index 1388c3a09..25e428b44 100644 --- a/Sources/Playback/Player/VideoPlayer-iOS/VideoPlayerViewController.swift +++ b/Sources/Playback/Player/VideoPlayer-iOS/VideoPlayerViewController.swift @@ -1453,6 +1453,10 @@ extension VideoPlayerViewController { func mediaNavigationBarDisplayCloseAlert(_ mediaNavigationBar: MediaNavigationBar) { statusLabel.showStatusMessage(NSLocalizedString("MINIMIZE_HINT", comment: "")) } + + func mediaNavigationBarDidTapPictureInPicture(_ mediaNavigationBar: MediaNavigationBar) { + playbackService.togglePictureInPicture() + } } // MARK: - MediaScrubProgressBarDelegate diff --git a/VLC.xcodeproj/project.pbxproj b/VLC.xcodeproj/project.pbxproj index 0eb5a9afb..0d48f6bcc 100644 --- a/VLC.xcodeproj/project.pbxproj +++ b/VLC.xcodeproj/project.pbxproj @@ -104,6 +104,7 @@ 597B403F2625E85000C0D81E /* SliderInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 597B403E2625E85000C0D81E /* SliderInfoView.swift */; }; 6C5B0C9E27A43098005AE25B /* PlaybackServiceAdjustFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C5B0C9C27A43098005AE25B /* PlaybackServiceAdjustFilter.swift */; }; 6C5B0C9F27A46258005AE25B /* PlaybackServiceAdjustFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C5B0C9C27A43098005AE25B /* PlaybackServiceAdjustFilter.swift */; }; + 6CC3F6B32D230AEF00C15E33 /* PictureInPictureMediaController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC3F6B22D230AEF00C15E33 /* PictureInPictureMediaController.swift */; }; 6D0B038825E7CBF90013DEF4 /* PopupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D0B038725E7CBF90013DEF4 /* PopupView.swift */; }; 6D3C676C23CDF1FC0039ACFD /* public in Resources */ = {isa = PBXBuildFile; fileRef = 6D3C676B23CDF1FC0039ACFD /* public */; }; 6D4756B123607D4A005F670E /* EditActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4756B023607D49005F670E /* EditActions.swift */; }; @@ -688,6 +689,7 @@ 57087A12E77ACEB9D1D30E33 /* Pods-VLC-tvOS.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VLC-tvOS.distribution.xcconfig"; path = "Target Support Files/Pods-VLC-tvOS/Pods-VLC-tvOS.distribution.xcconfig"; sourceTree = "<group>"; }; 597B403E2625E85000C0D81E /* SliderInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderInfoView.swift; sourceTree = "<group>"; }; 6C5B0C9C27A43098005AE25B /* PlaybackServiceAdjustFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlaybackServiceAdjustFilter.swift; path = Sources/Playback/Control/PlaybackServiceAdjustFilter.swift; sourceTree = SOURCE_ROOT; }; + 6CC3F6B22D230AEF00C15E33 /* PictureInPictureMediaController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PictureInPictureMediaController.swift; sourceTree = "<group>"; }; 6D0B038725E7CBF90013DEF4 /* PopupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupView.swift; sourceTree = "<group>"; }; 6D3C676B23CDF1FC0039ACFD /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; }; 6D4756B023607D49005F670E /* EditActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditActions.swift; sourceTree = "<group>"; }; @@ -1738,6 +1740,7 @@ 418DFE9E211C93C6005D3652 /* CustomDialogRendererHandler.swift */, 6C5B0C9C27A43098005AE25B /* PlaybackServiceAdjustFilter.swift */, 8D43712C2056AF1600F36458 /* VLCRendererDiscovererManager.swift */, + 6CC3F6B22D230AEF00C15E33 /* PictureInPictureMediaController.swift */, ); path = Control; sourceTree = "<group>"; @@ -3859,6 +3862,7 @@ 91C1BB8025EFD7A40096F97E /* ColorThemeExtension.swift in Sources */, 7D3784C2183A9938009EE944 /* VLCSlider.m in Sources */, 7D3784C3183A9938009EE944 /* VLCStatusLabel.m in Sources */, + 6CC3F6B32D230AEF00C15E33 /* PictureInPictureMediaController.swift in Sources */, 4144156C20ECE6330078EC37 /* FileServerView.swift in Sources */, 40C95A07256E929D002DD208 /* PlaybackSpeedView.swift in Sources */, 416DACB720B6DB9A001BC75D /* PlayingExternallyView.swift in Sources */, -- GitLab