PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0
Showing posts with label avplayer. Show all posts
Showing posts with label avplayer. Show all posts

Thursday, September 29, 2022

[FIXED] How to listen to Apple TV Remote in Objective-C?

 September 29, 2022     avplayer, avplayerviewcontroller, objective-c, tvos, uitapgesturerecognizer     No comments   

Issue

This seems like it should be pretty straight-forward but I'm having trouble finding a working example, good documentation, or even many StackOverflow posts that are helpful.

I have a custom view which contains an AVPlayer, like so:

@implementation
{
    @private
    AVPlayerViewController *controller
}

- (id) init
{
    self = [super init];
    if (self)
    {
        controller = [[AVPlayerViewController alloc] init];
        controller.view.frame = self.view.bounds;
        [self.view addSubview:controller.view];
    }
    return self;
}

@end

(I have a few other views like a message that overlays the player, a poster that I display while swapping videos, etc - but this is the basic setup)

When I integrated the IMA SDK, I started to have issues. If you press the pause button on the remote control during an ad, it pauses the ad just fine. But if you press the pause button again it doesn't unpause the ad, but instead unpauses my content player behind the ad. I don't hear any audio, but I know the content player was unpaused because I have ID3 metadata in my video and an NSLog() statement when I hit it, and I begin to see these logs. If I press the pause button again, the logs pause. I press it a fourth time, the logs start up again.

To try and fix this I wanted to bind a listener to the remote's play/pause button and make sure that if I was playing an ad then the ad was resumed, not the content. So I tried adding the following to my init method on my view:

        UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self.view action:@selector(tapped:)];
        [tapRecognizer setAllowedPressTypes:@[ [NSNumber numberWithInt:UIPressTypePlayPause] ]];
        [self.view addGestureRecognizer:tapRecognizer];

I then created the following method:

- (void) tapped: (UITapGestureRecognizer *) sender
{
    NSLog(@"Tapped");
}

This isn't being called. I'm pretty confident I made a simple mistake, but the documentation isn't very clear, so I'm not sure what I should be doing instead. The official documentation on detecting button presses uses Swift and says:

    let tapRecognizer = UITapGestureRecognizer(target: self, action: "tapped:")
    tapRecognizer.allowedPressTypes = [NSNumber(integer: UIPressType.PlayPause.rawValue)];
    self.view.addGestureRecognizer(tapRecognizer)

I believe I translated those three lines well. The documentation then doesn't show what the tapped method should look like, but instead goes on a tangent about working with low-level event handling. So to get the appropriate method signature I looked at the documentation on UITagGestureRecognizer which had the following (Swift) example for writing a handler:

func handleTap(sender: UITapGestureRecognizer) {
    if sender.state == .ended {
        // handling code
    }
}

This is why I went with - (void) tapped: (UITapGestureRecognizer *) sender

Still, after all of that, it's not working.

Quick Update

I tried replacing:

initWithTarget:self.view

With:

initWithTarget:controller.view

And:

self.view addGestureRecognizer

With:

controller.view addGestureRecognizer

And this time it looks like something actually happened when I pressed the play/pause button. The app crashed and Xcode gave me the following error:

2019-12-17 12:16:50.937007-0500 Example tvOS App[381:48776] -[_AVPlayerViewControllerContainerView tapped:]: unrecognized selector sent to instance 0x10194e060

So it seems like (correct me if I'm wrong):

  • The AVPlayerViewController has the focus, not my view
  • The gesture recognizer calls the selector on whatever class you register it to, rather than the class that did the registering

So I guess an alternative question to my original would be: How do I allow my class to handle a gesture on some other class (e.g. AVPlayerViewController)?


Solution

The target does not need to equal the view that the gesture recognizer is attached to. You can set the target to MyView but still attach the gesture recognizer to controller.view

To work around the unrecognized selector crash, you need to make sure you're providing the correct object as the target for your gesture recognizer. The way UIGestureRecognizer works is that when the gesture is recognized, it will invoke the given selector on the given target object. In other words, when gesture fires, it's going to perform the equivalent of:

[target selector:self];

(You seem to be treating the target as the view that the gesture will be attached to, which isn't how it works)

So if you implemented tapped: on your class MyView, then the target you pass to the gesture recognizer initializer should be an instance of MyView. You probably want to provide self, not self.view.



Answered By - Justin Voss
Answer Checked By - Willingham (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Wednesday, September 28, 2022

[FIXED] How to detect volume button press on tvOS remote

 September 28, 2022     avplayer, avqueueplayer, swift, tvos     No comments   

Issue

Im trying to find a way to observe the player so that I can detect when a user increases or decreases the volume on the Apple TV. I have managed to get this to work for iOS by using:


 var audioSession: AVAudioSession?

 audioSession?.addObserver(self, forKeyPath: "outputVolume", options: [.new], context: &videoPlayerViewControllerKVOContext)


 if keyPath == "outputVolume" {
        guard let mute = (change?[NSKeyValueChangeKey.newKey] as? NSNumber)?.floatValue else {
            return
        }
        
        var isMuted = false
        
        if (mute == 0) && (!player.isMuted) {
            isMuted = true
        } else if (mute.isZero) && (player.isMuted) {
            isMuted = false
        }
        
    }

However this doesn't work for tvOS. Is there a way to do this on tvOS?


Solution

It is not clear all other code, but you have to keep reference to created observer.

Here is possible solution (tested with Xcode 12.1)

private var observer: NSKeyValueObservation?

// ... other code

self.observer = audioSession?.observe(\.outputVolume) { [weak self] (audioSession, _) in
    guard let `self` = self else { return }
    let mute = audioSession.outputVolume
    
    var isMuted = false
    if (mute == 0) && (!self.player.isMuted) {
        isMuted = true
    } else if (mute.isZero) && (self.player.isMuted) {
        isMuted = false
    }
    
    // do what's needed here with `isMuted`
}


Answered By - Asperi
Answer Checked By - Pedro (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Older Posts Home
View mobile version

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
All Comments
Atom
All Comments

Copyright © PHPFixing