Playing QuickTime to a texture

Based on the TexturePlugin example project from the wiki, I’ve written a plug-in that plays the video frames of a QuickTime movie to a Unity texture.

It is easily able to play audio plus 30 FPS video on a dual G5, but there are a couple of other issues with it.

It’s currently macintosh-only as I haven’t had the time to do the buffer creation bookkeeping tasks on Windows. Things like creating an offscreen graphics context, or whatever that’s called on Windows, where QuickTime can render its frames.

Also, the textures come out upside down, since QuickTime and GL use different origins for their coordinate systems. I was hoping it would be possible to flip the texture via UV mapping; this is out of my area, can someone tell me how to do that part? I could flip it in the plug-in but I’d rather not if there’s a better way.

I’ve included the important snippets from the code below, but have removed all the error checking you’d want to do if you were going to use this.

Thanks!
Frank

Here’s how I set up quicktime:

		EnterMovies();
		// Create an off-screen gworld where QT can draw
		gworldBuffer = calloc(width * height * 3, 1);
		QTNewGWorldFromPtr(&gworld, k24RGBPixelFormat, &movieBox, NULL, NULL, 0,
						gworldBuffer, movieBox.right * 3);
		gworldPixMap = GetGWorldPixMap(gworld);
		LockPixels(gworldPixMap);
		gworldPixBase = GetPixBaseAddr(gworldPixMap);
		
		// Bind the texture and paint it black
		lastTextureID = textureID;
		glBindTexture(GL_TEXTURE_2D, textureID);
		void *bytes = calloc(width * height, 3);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, 
					 GL_UNSIGNED_BYTE, bytes);
		free(bytes);
	        movieURLRef = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, 
												CFStringCreateWithCString(kCFAllocatorDefault, moviePath, kCFStringEncodingASCII),
												kCFURLPOSIXPathStyle, false);
	CFURLGetFSRef(movieURLRef, &movieFSRef);
	FSGetCatalogInfo(&movieFSRef, kFSCatInfoNone, NULL, NULL, &movieFileSpec, NULL);
	     CFRelease(movieURLRef);
	     OpenMovieFile(&movieFileSpec, &movieRefNum, fsRdPerm);
	     NewMovieFromFile(&sMovie, movieRefNum, NULL, NULL, 0, NULL);
	
	    // Tell the movie to draw to the gworld we created
	    SetMovieGWorld(sMovie, gworld, NULL);

UpdateTexture is really simple after that:

void UpdateTexture(int textureID) {
	// Make sure we're bound to the correct textureID
	if (textureID != lastTextureID) {
		glBindTexture(GL_TEXTURE_2D, textureID);
		lastTextureID = textureID;
	}
	if (sMovie) {
		// Yield to the movie so it can draw the newest frame
		MoviesTask(sMovie, 0);
		// Here, I could programatically flip the texture upside down, since the image we get from QuickTime is going
		// to be upside down from the perspective of GL. However I would rather just use UV mapping to fix this. 
		// Pass in the rendered frame as a texture
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, movieWidth, movieHeight, 0, 
					 GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)gworldPixBase);
	}
}

On the Unity side, I’ve used a coroutine to play the movie:

SetMovie("PathToSomeMovie.mov", width, height, textureID);
StartCoroutine("MovieLoop");


	IEnumerator MovieLoop() {
		StartMoviePlaying();
		while (!IsMovieDonePlaying()) {
			UpdateTexture(m_Texture.GetInstanceID());
                        // There might be a better way to control the playback rate
			yield return new WaitForSeconds(.03f);
		}
		Debug.Log("Done with the first movie loop!");
		DisposeResources();
		yield break;
	}

NCarter on the IRC channel told me how to change the UV mapping of the texture via the material to flip the y axis. Select the material in the editor, click on the Placement button, and enter -1 in the Y Tiling box.

Thanks for the help!

This looks awesome! A start on getting videos supported across the board, to be sure!

Just a note; the plugin referenced is from the docs not the wiki; http://unity3d.com/Documentation/Manual/Plugins.html

Scroll down to the texture example.

Working Mac version up on the Wiki:

The plug-in now also works on windows.

Wow, I’m still evaluating Unity but am extremely impressed by the community! Great contribution!

I’m impressed both by the implementation and your generosity. Thanks!

This is really really cool!

d.

Giving this a try and having a little trouble (it’s probably my fault). Can the paths be relative and if so how would that have to look (e.g. …/movie.mov or \movie.mov, etc)

Also what about for Mac and PCs? In Director for instance you can do a “@” to represent your current location and use “:” as I recall for cross-platform path structure. Is anything like that possible?

And last, would it be a simple matter to expose the paths to the Unity inspector so one can just type them in there without messing with your script?

Oh woops I lied, one more (sorry), does the plugin folder have to reside somewhere special or just within the assets folder?

Thanks!

You can use this (and work from there):

http://unity3d.com/Documentation/ScriptReference/Application-dataPath.html

Simple indeed. Just replace tthe absolute path strings (it’s twice in the script it seems) with a variable name (say, moviePath), and add

var moviePath = "/path/to/file.mov"

At the top.

Yes. According to the manual (http://unity3d.com/Documentation/Manual/Plugins.html):

Once you have built your bundle you have to copy it to Assets/Plugins folder.

d.

Thanks for the pointers, David!

It looks great. It seems the video is dark, which would be good if you want it to interact with the scene, but is there a way to make the texture fully illuminated? (probably another newb question, sorry)

If you attach the texture script to a GUITexture, it will show up at full brightness. However, it will play back upside down because I couldn’t find a way to invert the UV mapping across the Y axis of a GUITexture, and GL and QuickTime have opposite Y origins.

We plan to use GUITextures in our game, we’re just going to render the movies upside down so they play back right-side up. :smile:

excellent work!

would this be a smart option for playing animations of lots of 2d characters/gameobjects or would it be very slow? (ive always had sad-pants times using quicktime)

Makes sense, thanks.
I get a GL Render error when I try to change the script’s height and width to a non-square size. So what’s the procedure to get say a 320x240 movie to play on a GUItexture?

Thanks!

David has already mentioned Application.dataPath for the “@” location. For directory seperators just use normal forward slashes (“/”), as they are supported on both windows and MacOS X. (Yes, Windows usually uses the backslash, but has supported paths with forward slashes since around MS-DOS 2.0 iirc)

Wow, this fills a big gap in Unity’s features, thanks Frank! Can’t wait to give it a try!

Thanks for the encouragement on this plug-in.

I’ve thought a little bit about playing non-square movies. Really, the issue is non-power-of-2 sizes on either side, since GL likes textures better that are power of 2 based.

There’s an extension type GL_TEXTURE_RECTANGLE_EXT that might work instead of GL_TEXTURE_2D, and would give you powers rectangular sizes that aren’t powers of 2, but it may not work on older hardware. I’ll give it a try sometime, or maybe someone from OTEE would know?

Rectangle textures work on all Mac hardware for the past many years. If your card doesn’t support it (Rage128, IIRC) it won’t be running OSX in the first place.

In short, if you just want to run on osx, you’re fine.

On windows, it’s another story - here, you’ve got quite recent Intel integrated chips that doesn’t have it. The solution we use for non-rectangular textures is to pad them up to the next power-of-two size use the scale offset values in the material to make them work correctly.

What about these two ideas:

  1. In a video program resize your video to being square but then stretch the plane or 3D object to the right dimensions in Unity? Or…

  2. Create a square video by adding padding (like a letterbox approach) Or…

  3. Do the above but use an alpha channel or something to make those areas transparent once it’s in Unity.

Any of those viable options?

Dcorwin: We have thought of using both approaches 1 and 2, but since we control the movies we’re using, we’re probably just going to make square movies. If you try any of those approaches please let me know how they work!

3 might work as well, we were either going to make the letterbox transparent or the same color as the border around the movie playback area.

Clint figured out how to play the movie right-side up in a GUITexture:

When the texture script is attached to a GUITexture, the approach we’re taking on our current project, you can get the movie to play right side up by making the Pixel Inset Y Min greater than the Y Max.