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;
}