Draw overlay on AVPlayer SwiftUI

Viewed 554

I am building an app where I want to draw rectangles on a video. The rectangles will come from a neural network and the position will be relative to the top left corner och the image frame.

To achieve that, I need to figure out a way to draw overlays on the AVPlayer view and use the actual frame as reference.

Is there an easy way of doing it in SwiftUI (which I started to learn a couple of days ago)?

Code that I have now, this is just about drawing something the way I want it. I.e. I want to find out how to position the rectangle with respect to the image/video. The orange dot should be origin if the black rectangle is the video.

Example illustration

Current code:

import SwiftUI
import AVKit


struct PlayVideo: View {
    var moviePath: URL?
    var player: AVPlayer
    init(fileName: String) {
        moviePath = fileURL(for: fileName)
        player = AVPlayer(url: moviePath!)
    }
    var body: some View {
        VideoPlayer(player: player)
            .scaledToFill()
            .overlay(
                Rectangle()
                    .fill(Color.blue)
                    .frame(width: 20, height: 20),
                alignment: .center)
        Button(action: {
            player.rate = 0.04
        }, label: {
            Text("Play Slow")
        })
    }
}

I tried with .position(x: ,y: ), but that seems to be for the parent frame.

EDIT

I have tried a new approach:

import SwiftUI
import AVKit

var player = AVPlayer()

struct PlayVideo: View {
    var moviePath: URL
    init(fileName: String) {
        moviePath = fileURL(for: fileName)
    }
    var body: some View {
        PlayerView(url: moviePath)
        Button(action: {
            player.rate = 0.04
        }, label: {
            Text("Play Slow")
        })
    }
}

struct PlayVideo_Previews: PreviewProvider {
    static var previews: some View {
        PlayVideo(fileName: "path")
    }
}

struct PlayerView: UIViewRepresentable {
    var url_: URL
    init(url: URL) {
        url_ = url
    }
    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PlayerView>) {
    }
    func makeUIView(context: Context) -> UIView {
        return PlayerUIView(frame: .zero, url: url_)
    }
}

class PlayerUIView: UIView {
    private let playerLayer = AVPlayerLayer()
    
    init(frame: CGRect, url: URL) {
        super.init(frame: frame)
        
        player = AVPlayer(url: url)
        player.play()
        
        playerLayer.player = player
        layer.addSublayer(playerLayer)
        
        // Create a CGRect object which is used to render a rectangle.
        let rectFrame: CGRect = CGRect(x:CGFloat(0), y:CGFloat(0), width:CGFloat(100), height:CGFloat(50))
        
        // Create a UIView object which use above CGRect object.
        let greenView = UIView(frame: rectFrame)
        
        // Set UIView background color.
        greenView.backgroundColor = UIColor.green
        
        layer.addSublayer(greenView.layer)
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        playerLayer.frame = bounds
    }
}

As you can see, there is a problem with the offset of the video and the view.

Problem with x-offset

I imagine one could do a second view and place on top of the video view with the same size and position. Then one can draw on that. But the problem remains how to get size and position of the video correctly.

0 Answers
Related