I have several AVPlayerViewControllers set as the ViewControllers of a UITabBarController. What I want is having the video of the currently visible AVPlayerViewController automatically pause playing when the user switches to another tab (which is another AVPlayerViewController)
I tried this approach:
override func viewWillDisappear(animated: Bool) {
player?.pause()
super.viewWillDisappear(animated)
}
but the video just keeps running in the background. (audio is still running at least) Debugger says player property is not nil in viewWillDisappear. I already tried implicitly and forced unwrapping, to no avail.
I was able to identify the problem. It seems to be that you can't pause in viewWillDisappear, neither with pause() nor with player?.rate = 0.0. So in order to prevent your video from playing in the background, you have to set AVPlayerViewController's player property to nil in viewWillDisappear.
Which sadly means that you have to write some code for preserving your playback state.
Seems a bit like a bug to me, hope this will get fixed later.
Related
I try to add a skip-intro-button (like Netflix) to the AVPlayerViewController of a tvOS movie app. I added it as subview to the contentOverlayView and when I let it appear I force giving it the focus with preferredFocusEnvironments. Everything's fine...
Until the user navigates somewhere else (e.g. seek bar or video asset info view). The button then loses focus (expected) and never can be focused again by user interaction.
I tried to:
add a UIFocusGuide()
put my button directly on the AVPlayerViewController's view
add a own subview, which contains the buttons to the AVPlayerViewController's view
add a own subview, which contains the buttons to the contentOverlayView
add several other buttons next to, below, above my button on the same subview (for each of the cases above)
The last approach shows that none of the other buttons can ever get focus by user interaction, so it seems, that, for the same reason, the skip-intro-button cannot be focused by the user. But what is this reason?
What is the right practice to add custom interaction elements to AVPlayerViewController?
Anyone any ideas?
Try to answer my main question "What is the right practice to add custom interaction elements to AVPlayerViewController?" myself:
Since I posted this question, 1.5 years ago, I didn't change the implementation of the skip intro feature very much. So whenever the user uses the remote, the button will lose focus and the only thing I changed is, that I hide the button whenever this happens, by implementing didUpdateFocus(in:with:), similar to this:
if let previouslyFocusedView = context.previouslyFocusedView {
if previouslyFocusedView == skipIntroButton {
changeSkipIntroButtonVisibility(to: 0.0)//animates alpha value
}
}
(I'm not completely sure, why I don't set it to isHidden = true)
However, in the meantime I had to implement a more complex overlay to our player, i.e. a "Start Next Video / Watch Credits" thing, with a teaser, some buttons, a countdown/progress bar and more. With the problems described above, it is obvious, that I couldn't go with the contentOverlayView approach.
So I decided to implement it the "traditional way", by presenting a complete UIViewController on top of the player's view, like this:
func showNextVideoOverlay() {
guard let nextVideoTeaser = nextVideoTeaser else { return }
let nextVideoOverlay = NextVideoAnnouncementViewController(withTeaser: nextVideoTeaser, player: player)
nextVideoOverlay.nextVideoAnnouncementDelegate = self
nextVideoOverlay.modalPresentationStyle = .overFullScreen
present(nextVideoOverlay, animated: true, completion: nil)
}
Of course the NextVideoAnnouncementViewController is transparent, so video watching is still possible. I turned out that this straight forward approach works pretty well and I really don't know, why I haven't thought about it, when implementing skip intro.
My colleagues in QA found one tricky thing, you should be aware of (and I think, I remember, that this is different with different Devices and different remotes and on different tvOS versions - try it):
The overlying view controller blocks most of the commands coming from the remote, respectively you can navigate inside the view controller without affecting the player - except the play/pause button. That one will pause the player, but it's not possible to resume from there. This is, because a playing (av)player always listens to this button, while a paused one doesn't. So I also had to implement something like this:
func addRemoteButtonRecognizer() {
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(playPauseButtonPressed))
tapRecognizer.allowedPressTypes = [NSNumber(value: UIPress.PressType.playPause.rawValue)]
self.view.addGestureRecognizer(tapRecognizer)
}
#objc func playPauseButtonPressed(sender: AnyObject) {
nextVideoAnnouncementDelegate?.remotePlayButtonPressed()
}
func remotePlayButtonPressed() {
if player?.playerStatus == .paused {
player?.play()
} else {
player?.pause()
}
}
I hope this helps some of you, who come by here and find no other answer.
I'm programatically changing focus in a tvOS app in response to a UISwipeGestureRecognizer. This all works fine, except there the nice 'boop' sound that plays when changing focus normally doesn't play, which makes the user experience a bit odd. Is there a way to programatically play this sound, or is there some other way I should be trying to handle the focus change?
You can make the button that is in focus have a background border in a different color or something.
In regards to sounds why don't you run a SKAction or play a AVFoundation file when changing focus.
You said you did all the focus stuff programmatically with gesture recognisers (I have done the same for my SpriteKit game) so I assume in the methods that get called when swiping you need to add the sound effect.
In my game those methods look something like this
func swipedRightTV() {
if menuButton.isFocused {
menuButton.isFocused = false
// play your sound here
playButton.isFocused = true
}
func swipeLeftTV() {
if playButton.isFocused {
playButton.isFocused = false
// play your sound here
menuButton.isFocused = true
}
If I have more than 2 buttons I use more if/else if statements in the methods, for simplicity tho I just used 2 in the example.
Does this help or are your methods totally different?
I have a flash app using fl.controls.ScrollPane as a container for a Sprite form.
The ScrollPane.source is referenced to the Sprite form with flash.text.TextField as some of the form's children.
I tested using Firefox and Chrome browsers.
When the flash file is called directly from the development server, the flash application runs correctly, except for the fact that one needs to click twice before being able to select the editable TextField.
The situation worsens when the flash is embedded on a page with strict control attributes such as:
<embed type="application/x-shockwave-flash"
src="https://localhost:flashfile.swf" width="400" height="300"
quality="high" scale="scale" allowfullscreen="true"
allowscriptaccess="never" salign="tl" wmode="opaque">
I read that wmode=opaque can bring some undesirable side effects, but it is ridiculous, that I can't even select the TextField objects. Event the mouse scroll isn't captured anymore. The mouse events are not hitting the TextField objects.
This is a peculiar case because other objects such as fl.controls.CheckBox and fl.controls.ComboBox works, as they still are clickable/selectable.
Question: Is there something I should know about in this special scenario of ScrollPane and embedding using wmode=opaque that I'm missing out here?
After searching the web for half a day, and combing the AS3 documentation till my eyes go blind, I finally found a hint to a solution to my problem. I'm just doing a brain dump here to fix the TextField selection issue.
A lot of search results hinted at the problems of wmode=opaque and how browsers control the mouse events, but not many turn up direct answers or solutions.
http://www.actionscript.org/forums/showthread.php3?t=170310
The forum question above gives a hint, that the ScrollPane object, actually captures the first mouse click for focus, then lets the subsequent click go through to the children object. The tip given is as shown:
myScrollPane.focusEnabled = false
setting focusEnabled property to false.
For completeness, I've explicitly set more properties just to be sure:
mouseFocusEnabled = false; // disable mouse focus on the scrollpane
focusEnabled = false; // disable focus on the scrollpane
mouseEabled = true; // receives mouse input
mouseChildren = true; // enable for selecting children objects
Miraculously, my clicks are able to select the editable TextField objects when the flash is embedded using the wmode=opaque (I'm still not actually sure whether this is the issue).
The mouse scroll events however, seems to be only solvable with external javascript. There's no internal AS3 solutions.
That said, the unanswered question is: I'm still not sure where or how web browsers capture the mouse events then selectively pass them into the embedded flash containers. If someone can shed light on this, and also point to some solution, that would be great.
As an example of how this might be handled, YouTube will unload the video and start loading it from the position the user clicked on the timeline.
Currently all I have is:
private function seekHandler(event:MouseEvent):void {
player.playheadTime = event.localX/playheadBar.width*player.totalTime;
}
This works perfectly if the target position is already buffered. However, if I want to seek ahead of the buffer, I would expect it to pause the video until the file has buffered to the selected point. Instead, it pauses for a moment, then acts like "nope, ain't gonna do it" and continues playing from where it was.
Note that I'm only just learning Flex/AS3, so I may well have missed something obvious ;)
Is there some straightforward technique to play only a certain part of a HTML5 video? For example in a 30 second clip I would like to play only the part 5-20 sec. Additionally the rest of the video should not be accessible from the UI at all (meaning the video timeline should only show the 5-20 sec part).
I've been going through some HTML5 video players but none of them seem to be supporting this kind of functionality. If anyone knows a (good) way to implement this feature please give me a hint.
Thanks in advance!
Even though this question has already been marked as answered, here's something that may interest you and anyone else who stumbles across here: Specifying playback range.
It's part of the Media Fragment API and currently works in the latest versions of Firefox, Chrome and Safari 6+.
You can implement it there is a Player.Play() event in a players, whenever Play() called start a timer and call the Player.stop() on the specific time you want.
I got the same problem and I didn't found something that can solves this problem, what you can do is to implement your own controls and display the video with a canvas...
and if you are trying to implement this in IOS you will not be able to do it.
My thought would be to use custom controls, as I don't believe that functionality is available natively. All the functionality of html5 controls (play, pause, start at timestamp, etc.) can be called via javascript. In this case you are going to want to edit the currentTime variable.
So you may want to consider setting up your own slider, where the start of the slider represents your starting point, and the end your ending point. Set the video to not play on page load. Then on page load have a javascript function change the currentTime to your starting point. For stopping you could occasionally query the currentTime. I wouldn't use a timer as delays like slow loading could throw it off.
I am trying to implement a similar video in "Preview Mode". I am utilizing the methods stated above by adding an event listener and then pausing the video at a currentTime()=='x' postition. To prevent user from simply hitting play again, the currentTime listener wont allow playback past the 'x' time in the timeline so each time the user hits play, it is automatically instantly paused again. Furthermore, at time 'x' the video container will become hidden through CSS thus preventing interaction from the user with the video.