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