Search code examples
iosmethodsmpmovieplayercontrollerextern

Make a class defined in extern "C" block able to reference method outside


I'm trying to write a simple plugin for Unity3d iOS which you might have heard about that streams video. I actually managed to do that, the streaming video bit works.

Now i'm trying to add functionality that is part of the plugin to detect for a swipe gesture and send a message to Unity. I have no experience with Objective C, and currently not interested in learning the ins and outs as i'm only looking to find a solution for this one specific problem.

So i have managed to Google all the stuff required to stream the actual video, and also some code for registering swipe gestures. The problem is though that, in defining the UISwipeGestureRecognizer you are expected to assign an action method to it. But the function doing the video streaming is being defined in an extern "C" block which is required so that it can be referenced in Unity.

The method assigned to the gesture recognizer though has to be defined (i think) in the regular framework of an iOS app, but my suspicion is that this creates the problem of the gesture recognizer class being unaware of the method defined outside the extern "C" block.

So now when i run it, the video starts streaming but as soon i start swiping the screen it all crashes. Presumably because the method assigned cannot be referenced is my guess.

My question is... How do i make this happen, maybe there's something obvious that i'm unaware of ? The important thing is to make it work in a function that is defined in an extern "C" block, because that after all is required by Unity.

This is the actual code i put together so far:

http://www.hastebin.com/ragocorola.m <-- complete code

To surmise the loadLevel method how should it be declared ?

extern "C" {


    void _playVideo(const char *videoFilepath)
    {

        NSURL *url = [NSURL URLWithString:CreateNSString(videoFilepath)];

        MPMoviePlayerController *player = [[MPMoviePlayerController alloc]  
        initWithContentURL:url];

        player.controlStyle = MPMovieControlStyleFullscreen;
        player.view.transform = CGAffineTransformConcat(player.view.transform, 
                                CGAffineTransformMakeRotation(M_PI_2));

        UIWindow *backgroundWindow = [[UIApplication sharedApplication] keyWindow];

        [player.view setFrame:backgroundWindow.frame];
        [backgroundWindow addSubview:player.view];



       UISwipeGestureRecognizer * swipe = [[UISwipeGestureRecognizer alloc] 
       initWithTarget:swipe action:@selector(loadLevel:)];

       [swipe setDirection:(UISwipeGestureRecognizerDirectionUp | 
        UISwipeGestureRecognizerDirectionDown | UISwipeGestureRecognizerDirectionLeft 
       |UISwipeGestureRecognizerDirectionRight)];


       [player.view addGestureRecognizer:swipe];

       [player play];


    }


}

Solution

  • Your problem is that swipe is undefined when you are passing it as a target. Who know's what's on the stack when it's passed? This results in a method being sent to a bad location in memory when you swipe.

    UISwipeGestureRecognizer * swipe = [[UISwipeGestureRecognizer alloc] 
    initWithTarget:swipe action:@selector(loadLevel:)];
    

    this is equivalent to:

    id undefinedTarget;
    UISwipeGestureRecognizer * swipe = [[UISwipeGestureRecognizer alloc] 
        initWithTarget:undefinedTarget action:@selector(loadLevel:)];
    

    Your target needs to be an instance of a class that defines the loadLevel: method.


    Edit (after chasing your link): i.e. an instance of VideoPlugin.

    Though the second problem you will have is that method loadLevel: is different from loadLevel. Make sure they are consistent.