0
votes

I have many simultaneous videos. Through a Int var (var test1 and var test2) I would like to be able to add only a certain video and remove all the others so as not to have memory problems

As soon as the view is loaded, the value "nil" is assigned to each player and when test1 == test 2 it should load the video in a certain player and in the other "nil"

The problem is that despite being the testing variable in binding (struct VideoPlayer @Binding var testing) it does not update the state of the player which always remains in Nil

Below the best solution I have obtained so far

Some idea? Thank you all

struct CustomPlayer: View {
    @Binding var test1:Int
    @Binding var test2:Int
    @State var path:String

    @State var testing:AVPlayer? = nil

    var body: some View {
            if(test1 == test2 ) {
                    self.testing? = AVPlayer(url: URL(fileURLWithPath:  Bundle.main.path(forResource: "\(path)", ofType: "mp4")!)  )
                    self.testing?.play()
            } else {
                    self.testing?.replaceCurrentItem(with: nil)
            }
             return ZStack{
                VideoPlayer(testing: self.$testing)

            }




struct VideoPlayer : UIViewControllerRepresentable {

    @Binding var testing : AVPlayer?


    func makeUIViewController(context: UIViewControllerRepresentableContext<VideoPlayer>) -> AVPlayerViewController {

        let controller = AVPlayerViewController()

        controller.player = testing
        controller.showsPlaybackControls = false
        controller.view.backgroundColor = UIColor.white
        return controller

    }

    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<VideoPlayer>) {


    }
}
2

2 Answers

1
votes

Here an example how to load multiple videos simultaneous (i added an autoplay feuature), and remove all other, but not the selected one. To remove videos tap on video you want to save

And i'm not sure that i solve your initial problem, but this can give you a clue where to look next

Code can be copy/pasted, as i declare it in one file to convenient stackoverflow use

  import SwiftUI
  import AVKit

  struct VideoModel: Identifiable {
    let id: Int
    let name: String
    let type: String = ".mp4"
  }

  final class VideoData: ObservableObject {
    @Published var videos = [VideoModel(id: 100, name: "video"),
                             VideoModel(id: 101, name: "wow"),
                             VideoModel(id: 102, name: "okay")]
  }


  //Multiple item player
  struct MultipleVideoPlayer: View {
    @EnvironmentObject var userData: VideoData

    var body: some View {
      VStack(alignment: .center, spacing: 8) {
        ForEach(userData.videos) { video in
          VideoPlayer(video: .constant(video))
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 250, maxHeight: 250, alignment: .center)
        }
      }
    }
  }

  //Single item player
  struct VideoPlayer : UIViewControllerRepresentable {
    @EnvironmentObject var userData: VideoData
    @Binding var video: VideoModel

    func makeUIViewController(context: Context) -> AVPlayerViewController {
      guard let path = Bundle.main.path(forResource: video.name, ofType: video.type) else {
        fatalError("\(video.name)\(video.type) not found")
      }
      let url = URL(fileURLWithPath: path)
      let playerItem = AVPlayerItem(url: url)

      context.coordinator.player = AVPlayer(playerItem: playerItem)
      context.coordinator.player?.isMuted = true
      context.coordinator.player?.actionAtItemEnd = .none

      NotificationCenter.default.addObserver(context.coordinator,
                                             selector: #selector(Coordinator.playerItemDidReachEnd(notification:)),
                                             name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
                                             object: context.coordinator.player?.currentItem)

      let controller = AVPlayerViewController()
      controller.player = context.coordinator.player
      controller.showsPlaybackControls = false
      controller.view.backgroundColor = UIColor.white

      controller.delegate = context.coordinator

      let tapRecognizer = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.playerDidTap))
      controller.view.addGestureRecognizer(tapRecognizer)
      return controller
    }

    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {
      uiViewController.player?.play()
    }

    func makeCoordinator() -> Coordinator {
      let coord = Coordinator(self)
      return coord
    }


    class Coordinator: NSObject, AVPlayerViewControllerDelegate {
      var parent: VideoPlayer
      var player: AVPlayer?

      init(_ playerViewController: VideoPlayer) {
        self.parent = playerViewController
      }

      @objc func playerItemDidReachEnd(notification: NSNotification) {
        if let playerItem: AVPlayerItem = notification.object as? AVPlayerItem {
          playerItem.seek(to: CMTime.zero, completionHandler: nil)
        }
      }


      @objc func playerDidTap(){
        parent.userData.videos = parent.userData.videos.filter { videoItem in
          return videoItem.id == parent.video.id
        }
      }

    }
  }



  //preview
  struct AnotherEntry_Previews: PreviewProvider {
    static var previews: some View {
      MultipleVideoPlayer()
    }
  }

And in 'SceneDelegate.swift' replace app entry point with

  window.rootViewController = UIHostingController(rootView: MultipleVideoPlayer().environmentObject(VideoData()))

The key thing of this move is to have "VideoData" resource, you can achieve it with EnvironmentObject, or with some other shared data example

Videos below i added to project, including them to Target Membership of a project

enter image description here

@Published var videos = [VideoModel(id: 100, name: "video"),
                         VideoModel(id: 101, name: "wow"),
                         VideoModel(id: 102, name: "okay")]
0
votes

Your code seems to be ok. Are you sure the AVPlayer(...) path is correct and testing is not nil. Put

.....
self.testing?.play()
print("-----> testing: \(testing.debugDescription)")

is testing = nil at that point?