Tuesday, April 28, 2015

Fullscreen video streaming via www for Unity iOS


Unity is a flexible and powerful development platform for creating multiplatform 3D and 2D games and interactive experiences, It's goal is to create a fluid and fun to use cross platform apps. Unity is made to empower you to create the best interactive entertainment or multimedia experience that you can.

In this article we will be discussing one of the most interesting feature of Unity, Which is "Video streaming"

Based on the unity manual, in order to stream a Video in fullscreen mode for mobile devices, we should use the "Handheld.PlayFullScreenMovie"  function, but unfortunately, its not really helping us in our case, because this function can only stream movies from a local storage, and not from the web media server. Loading a movie from an asset bundle is painful because the bundle takes so long to load especially if you have few movies that are very large.

The way to overcome this problem is to use a method called Plugin. In Unity, you normally use scripts to create functionality but you can also include code created outside Unity in the form of a Plugin.

Before we begin, we should be aware that The Mono Runtime (Unity engine's scripting is built on Mono, Which is an open source development platform based on the .NET framework, allows developers to build cross-platform applications ) makes calls into native code using C standards. Any language that follows the C calling convention can be called. Unfortunately Objective-C does not follow this convention. We will have to wrap Objective-C APIs with C code to make it available to Unity. This is done by putting our calls inside C functions and using a C compiler to compile it. Our file will contain calls into C++ so we need to use a .mm file with Extern "C" { at the top and } at the bottom to avoid name mangling issues.

The first phase, should be to write a wrapper with a C named ( Unity needs the plugins to be C named. That's why, we need to wrap it in an extern “C”.), and from that wrapper we can call to the video class.

So, Let's begin :

1) In your Unity project, create a folder Assets/Plugin/iOS. Files in that folder (can’t be in a subfolder) automatically get added to your iOS project when you have Unity create an iOS build.

First, we create the iOS wrapper plugin file. In that file, we put the code for the iOS side of the plugin. Unity needs the plugins to be c named. So, we wrap it in an extern “C”.

Here is an example plugin:

videopluginwrapper.mm:

#import "videoplugin.h"
extern "C"
{
    bool videoIosPlay( const char* url)
    {
            return [videoPlayerHelper play:[NSString stringWithUTF8String:url]];
    }
}


2) Second, we create the actual iOS video class.

videoplugin.h:

#import <MediaPlayer/MediaPlayer.h>

@interface VideoPlayerHelper : NSObject {
@private
        // Native playback
    MPMoviePlayerController* _moviePlayer;
   
     }

- (BOOL)play:(NSString*)url;

@end


videoplugin.m:

#import "videoplugin.h"
#import <AVFoundation/AVAudioSession.h>

@implementation VideoPlayer

////////////////////////////////////////////////////////////////////////////////

// Play the asset
- (BOOL)play:(NSString*)url
{
    BOOL ret = YES;
   
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    NSURL *urlMovie = [NSURL URLWithString:url];
    _moviePlayer = [[MPMoviePlayerController alloc]
                                       initWithContentURL:urlMovie];

    _moviePlayer.controlStyle = MPMovieControlStyleFullscreen;
    _moviePlayer.shouldAutoplay = YES;
    _moviePlayer.view.transform = CGAffineTransformConcat

(_moviePlayer.view.transform,CGAffineTransformMakeRotation(M_PI_2));
   
    UIWindow *backgroundWindow = [[UIApplication sharedApplication] keyWindow];
   
    [_moviePlayer.view setFrame:backgroundWindow.frame];
    [backgroundWindow addSubview:_moviePlayer.view];
    [_moviePlayer prepareToPlay];
    [_moviePlayer play];
   
    return ret;
}

@end


3) Now that we’ve built the plugin, we are ready to call it from Unity code. first, we need to declare the native functions that will be using in managed code(we put this file in the Assets folder) :

VideoPlayer.cs :

using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;

public class VideoPlayer{

    /* Interface to native implementation */
   
    [DllImport ("__Internal")]
    private static extern void videoIosPlay(string strUrl);
     
    // play movie
    public static void PlayVideo(string strUrl)
    {
        // Call plugin only when running on real device
        if (Application.platform != RuntimePlatform.OSXEditor)
            videoIosPlay(strUrl);
    }

}


*** Note that the “__Internal” name is used for iOS plugins.


4) Finally, we create a Unity script to use it.

TestPlugin.cs :

using UnityEngine;
using System.Collections;
public class PlayerScript : MonoBehaviour {
   
    private float    _buttonX = 30f;
    private float    _buttonY = 30f;
    private float    _buttonWidth = 30f;
    private float    _buttonHeight = 30f;
   
    void OnGUI ()
   {
        if(GUI.Button(new Rect(buttonX ,  _buttonY, _buttonWidth, _buttonHeight), "Play"))
        {
            VideoPlayer.PlayVideo("Movie WWW Link);
        }
    }

}


That's all for now.
please feel free to contact me If you have any questions.

Good Luck.

Moris Oz