Avplayer with Playback Controls of Avplayerviewcontroller

AVPlayer with playback controls of avplayerviewcontroller

No. The usual solution (explicitly advised by Apple) is to use the AVPlayerViewController as an embedded view controller - i.e., make your view controller a custom parent view controller and the AVPlayerViewController its child, and now you can place its view (the movie and the controls) inside your view as a subview in good order. Example (self is your view controller):

let url = // whatever
let player = AVPlayer(URL:url)
let avPlayerViewController = AVPlayerViewController()
avPlayerViewController.player = player
avPlayerViewController.view.frame = // whatever
self.addChild(avPlayerViewController)
self.view.addSubview(avPlayerViewController.view)
avPlayerViewController.didMove(toParent: self)

AVPlayerViewController inside a view is not showing the playback controls

This is an expected behaviour:

AVPlayerViewController is designed such that when in full screen, the
full playback experience (scrubbing, info panel access, etc) are all
available. When in a space less than full screen, the assumption is
that it is just one of several interactive elements on screen, and
thus the view should not absorb all touch surface events as transport
control.

You can read more about this on this thread: https://forums.developer.apple.com/thread/19526

AVPlayerViewController playback controls missing in iOS14

I found a solution. It looks like the UIContextMenuInteraction breaks the the playback controls. When I set the interaction setup after the whole AVPlayerViewController setup code I got my playback controls back.

Display playback controls in AVPlayer

I got the answer with description. See here. To display system playback controls like play, pause button etc. we need AVPlayerViewController.
Below is the implementation in Objective-C.

@interface ViewController ()
@property (strong,nonatomic) AVPlayerViewController *avPlayerViewController;
@property (strong,nonatomic) AVPlayer *avPlayer;
@end

    - (void)viewDidLoad {
[super viewDidLoad];
//URL for the file. It can be over HTTP or local file URL.
NSURL *folderURL=[NSURL fileURLWithPath:uploadFolderLocation];
NSURL *movieURL=[folderURL URLByAppendingPathComponent:@"video.mp4"];

self.avPlayer = [AVPlayer playerWithURL:movieURL];

self.avPlayerViewController=[[AVPlayerViewController alloc]init];
self.avPlayerViewController.player=self.avPlayer;
}

- (IBAction)playButtonTapped:(id)sender {
//Trigger the video to play

//AVPlayer object can direct its visual output to AVPlayer. AVPlayerVC is a AVPlayerViewController. You can add it via objects in bottom-right corner.
AVPlayerVC *avPLayerVC=[[AVPlayerVC alloc] init];
avPLayerVC.player=self.avPlayer;

[self addChildViewController:self.avPlayerViewController];
[self.view addSubview:self.avPlayerViewController.view];
self.avPlayerViewController.view.frame=self.view.frame;
[self.avPlayerViewController.player play];
}

The image of the video with control buttons: Sample Image

Playback controls in swiftui

VideoPlayer does not appear to have any methods on it that I can find in the documentation to control whether the playback controls are shown.

showPlaybackControls doesn't work because AVPlayer doesn't have that property either.

It looks like for now you'll have to do something like wrap an AVPlayerViewController:

Note that this is a very limited example and doesn't take into account a lot of scenarios you may need to consider, like what happens when AVPlayerControllerRepresented gets reloaded because it's parent changes -- will you need to use updateUIViewController to update its properties? You will also probably need a more stable solution to storing your AVPlayer than what I used, which will also get recreated any time the parent view changes. But, all of these are relatively easily solved architectural decisions (look into ObservableObject, StateObject, Equatable, etc)

struct ContentView: View {
let player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "IMG_0226", ofType: "mp4")!))
var body: some View {
AVPlayerControllerRepresented(player: player)
.onAppear {
player.play()
}
}
}

struct AVPlayerControllerRepresented : UIViewControllerRepresentable {
var player : AVPlayer

func makeUIViewController(context: Context) -> AVPlayerViewController {
let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = false
return controller
}

func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {

}
}

Custom control for playback speed for AVPlayerViewController

  1. Can we add custom playback speed (0.5, 1.0, 1.5, 2.0) control to AVPlayerViewController?

You can add a custom playback speed button to AVPlayerViewController by doing a little hack.

In iOS 11, i.e., you can add a button to the AVPlayerViewController by adding the control next to the "Play/Pause" button. That button has an Accessible Identifier which will help you find that control.

iOS 11 AVPlayerViewController

So, what I did was basically find that "Play/Pause" button and add my Playback Speed button to the superview of the "Play/Pause" button.

func findPlayPauseButton(view: UIView) -> UIView? {
if view.accessibilityIdentifier == "Play/Pause" {
return view
}
for subview in view.subviews {
if let result = findPlayPauseButton(view: subview) {
return result
}
}
return nil
}

guard let playerView = playerViewController?.view else {
return
}

guard let playPauseButton = findPlayPauseButton(view: playerView) else {
return
}

if let playerControlsView = playPauseButton.superview {
let playbackSpeedButton = UIButton(type: .system)
playbackSpeedButton.accessibilityIdentifier = "PlaybackSpeed"
playbackSpeedButton.setTitle("1 X", for: .normal)
playbackSpeedButton.addTarget(self, action: #selector(self.playbackSpeedButtonTapped), for: .touchUpInside)
playbackSpeedButton.translatesAutoresizingMaskIntoConstraints = false
playerControlsView.addSubview(playbackSpeedButton)
NSLayoutConstraint.activate([
playbackSpeedButton.heightAnchor.constraint(equalTo: playPauseButton.heightAnchor),
playbackSpeedButton.widthAnchor.constraint(equalToConstant: 50),
playbackSpeedButton.leadingAnchor.constraint(equalTo: playPauseButton.trailingAnchor, constant: 70),
playbackSpeedButton.bottomAnchor.constraint(equalTo: playPauseButton.bottomAnchor),
])
}

Result:

Playback Speed Button Example

As you may know, this has a lot of downsides because of all the edge cases that you will find, i.e., you need to take care the landscape as well, you need to check if the hack is still valid on every new iOS version, etc. If you app supports iOS 10, then things are different because the UI of the AVPlayerViewController has changed in iOS 11.


  1. Is there a way we can refresh speed rate using an UISlider value change?

Yes, instead of a UIButton for changing the playback speed, just add an UISlider.


  1. Can we change the playback speed if it is playing currently?

Yes, if it's playing then you just need to change the rate property of the player: for example to 1.25 X => playerViewController.player?.rate = 1.25.

UPDATE:

Enhancement requested! Radar number is 46022646: Playback speed control for AVPlayerViewController (Video).



Related Topics



Leave a reply



Submit