This post is a brief overview of how to create a simple 360 (x180) live stream viewer in Unity. Other people have made live streaming apps for this camera but I couldn't find a how-to anywhere so I thought I'd post a quick overview of one easy way of doing it. I specifically made this to be used with the Kodak SP 360 (the first model I have not tested this with the 4K version, firmware 1.04) but I suppose it could be used with any 360 IP Camera that outputs to MJPEG
1. Kodak SP360
The Kodak SP360 [pic source] outputs an MJPEG stream but for some reason Kodak didn't shout about it. Once you have connected to the Kodak SP360 via wifi you can view this stream using VLC by opening a network stream with the following address: http://172.16.0.254:9176, I have also seen http://172.16.0.254:9176/mjpg/video.mjpg somewhere, both seem to work for me [source].
2. Make a Dome in Blender
There are plenty of tutorials out there on ho to make a skydome for Unity. I used Blender, the important thing to remember is to 'flip the normals' so that any texture added to the dome will appear on the inside of the dome. You can just save the blender file into your Unity assets folder and it will automatically import it.
Here is a picture of my dome which I was testing with a random Kodak SP360 image offline [source].
3. Streaming the Video
I used the brilliant 'AVI Player' asset on the Unity asset store. The creator ShuuGames is very helpful and is active on his support forums.Once you have bought the asset simply import it into your project and add the 'Movie Streamer' script to your dome.
Point the source to the MJPEG stream (http://172.16.0.254:9176/mjpg/video.mjpg), increase the connection timeout (I chose 10 as it would often fail with low settings), and select the 'play' setting.
4. Camera
I found the best view came from placing the camera inside the dome at the same level as the dome's base, in other words set both objects to 0X 0Y. My application was designed to be used with PC so I used a generic mouselook script [source] for my camera. However, I didnt want to see anything but the dome so I set up restrictions to the Y axis of the camera movement.
A simplified version of the mouselook script I made can be found at the bottom of this post, just copy and paste it into a javascript file (in my case lookscript.js) and add it to the camera.
Because the camera only sees the inside of the dome the draw distance (far clipping plane) of the camera can be brought down to make the application run more efficiently (I found this especially important on mobile) reduce draw distance
Add a light and you should be good to go. When you press play it should play the stream from the Kodak SP360. Here is a screen on the Unity editor, the view from the Kodak sitting on my desk.
Further work:
This is an extremely basic streamer that allows you to look around inside the 360 (x180) video. With a simple menu or GUI other IP addresses could be entered if you wanted to stream from across a network. I successfully broadcasted the Kodak SP360 video from a laptop using VLC and accessed it within a LAN using the application I have described here. Of course a full dome could be used for true 360 video, but this was just designed as a test with the Kodak. Shouldn't be too much work to split the image to use with Google Cardboard or some other VR application.
Notes:
lookscript.js:
#pragma strict
enum RotationAxes {MouseX, MouseY, MouseXandY};
var axes : RotationAxes;
private var MouseX = RotationAxes.MouseX;
private var MouseY = RotationAxes.MouseY;
private var MouseXandY = RotationAxes.MouseXandY;
var sensitivityX : float = 15F;
var sensitivityY : float = 15F;
var minimumX : float = -360F;
var maximumX : float = 360F;
var minimumY : float= 20F;
var maximumY : float = 60F;
var rotationY : float = 0F;
function Update ()
{
//stop the camera jumping on mobile
if (Input.GetMouseButtonDown(0))
return;
if (axes == MouseXandY)
{
var rotationX : float = transform.localEulerAngles.y + Input.GetAxis("Mouse X") * sensitivityX;
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotationY = Mathf.Clamp (rotationY, minimumY, maximumY);
transform.localEulerAngles = Vector3(-rotationY, rotationX, 0);
}
else if (axes == MouseX)
{
transform.Rotate(0, Input.GetAxis("Mouse X") * sensitivityX, 0);
}
else
{
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotationY = Mathf.Clamp (rotationY, minimumY, maximumY);
transform.localEulerAngles = new Vector3(-rotationY, transform.localEulerAngles.y, 0);
}
}