MPEG, Windows and Director

by Al Hospers (1999/2000)

MPEG playback in Director should be a piece-of-cake. Instead, it can be like wrestling a bear. The following article attempts to make it a little less painful.

The Options

Currently the only MPEG solution for the Mac is Quicktime 4 . It supports MPEG with,from what I hear, good quality and a choice between half raster and full raster allowing for playback on slower machines. There is talk about an update for Quicktime 3 on Windows that promises MPEG playback, but as of this writing it does not seem to be available. So this is not a fully cross-platform solution. Keep your eyes open and ears to the Apple QT developer site for late breaking news.

In Windows you have 2 options for playing native MPEG files: 1) purchase a MPEG dedicated 3rd party Xtra or 2) use a combination of MCI, ActiveMovie and the BudAPI Xtra (or possibly the old dllGlue Xobject) to get you the interactivity during playback that you need. Which of these solutions you choose depends on how much money you have available and how much you want to write code. I've always been a bit of a code-horse myself.

Clearly the simplest solution is to purchase one of the available Xtras. The two I am aware of currently are:

Xtra

Manufacturer

Contact

OnStage

Visible Light

http://www.visiblelight.com/mall/onstage/

MpegXtra

Tabuleiro da Baiana

http://www.tbaiana.com

Both work well and the support is good. Mauricio Piacentini, from Tabuleiro da Baiana, is often on Direct-L and has been very free with his advice, even to those using MCI. I have used OnStage for a project and their support was also good. They each have somewhat similar feature sets and licensing agreements and each also has upgrade paths to more powerful versions. You should check out their web sites for more information. A real plus for the MpegXtra is the fact that it uses Microsoft's new technology, DirectShow, to play. This is the technology of the future.

MCI and Director

You should be aware that Microsoft is phasing out MCI. DirectShow is the technology that is taking its place. But if you still want to roll-your-own using MCI, for whatever reasons maybe you're a masochist, you will certainly learn a lot.

First you will want to read Technote ID # 3521 at Macromedia Support. It gives you a lot of information about the various MCI related commands that are available to you. Right now it is the only documentation available on the web since Microsoft removed its references. Through MCI Director gives you quite a lot of control over the various media types that Windows deals with. You can use MCI to control MPEG video and audio, Wave audio and AVI files using Lingo.

Second, you will need to make sure that your system is configured to use MCI. You will need to install the ActiveMovie drivers on your machine, and/or your target machine, if it is not already installed. The latest version of DirectShow (the new name for ActiveMovie) can be downloaded from Microsoft.

There is a direct link to Microsoft's download page on the Tabulario site. If you are running Windows 98 and IE4 you might find yourself someplace else, but the end result should be the same.

Most machines shipped after November 1996 already contain these drivers. However some older machines running Windows 95A, version number 4.00.950, may need to be upgraded. You can actually use MCI itself to check and see if ActiveMovie is installed. You can use the following Lingo to check:

mci( "info mpegvideo product" )
put the result

-- "ActiveMovie"

Obviously ActiveMovie is what you want to see here.

If this comes up with something other than ActiveMovie, or if the test movie does not play, you'll need to make sure that your WIN.INI file is configured properly. Look for a line in the [mci extensions] section that looks like:

mpg=ActiveMovie

If it says something like mpg=MpegVideo, then it is probably configured for a RealMagic driver. You might also see mpg=SoftPeg. This is for the SoftPeg software MPEG driver. In any event, you want to change it to ActiveMovie.

You also want to look in the [mci] section and make sure you have a line:

ActiveMovie=mciqtz.drv

This is the driver that ActiveMovie uses to play your files. As Mauricio from Tabulario says, "This is the stuff that will give you headache and a lot of tech support calls, and one of the reasons Microsoft is now promoting the use of the ActiveMovie/DirectShow interface instead of MCI."

Is it interactive yet Daddy?

There is one last piece of this puzzle that you will need to deal with and that is interactivity. Director is a real HOG for CPU clock cycles. There is no way internally to tell it to back off, and this has a real effect on video playback. The 3rd party media playback Xtras like OnStage and DirectMedia both have some code inside that gives some extra priority to the media playback process, and takes a little away from Director itself. That way each gets what they need to perform properly. Without this you get either stuttering media playback, or you cannot do anything in Director until the media is finished.

Both are pretty unacceptable for most multimedia applications. Currently the only way around this unless you have Director 7 is with the "baSleep" command in the BuddyAPI Xtra. You can download an evaluation copy that will allow you to use any 3 commands for FREE. FWIW - I can almost guarantee you that there are over 3 commands in this Swiss Army Knife of Xtras that you will find a use for. That's fair warning! You can play with the values, but for this use a setting of 50 seems to work well.

NOTE: Director 7 contains a command that will do this, and not require any Xtra. This example does not use this function, but you could try it easily.

And now, for some real CODE

While it is easy to use MCI just by writing a few lines of code and attaching them to a button or a mouseUp event, that's really inefficient. The best way to work with something like MCI for ActiveMovie, or even Quicktime for that matter, is to wrap it up in an OOP layer. You know, an Object. That way, if you keep the same methods in a series of objects you use to play different media types you can play one of those other media types simply by replacing the object.

If your Quicktime object has playMedia, stopMedia, pauseMedia and rewindMedia methods, and your ActiveMovie MCI object has the same AND you pass info into then in the same manner and you give them the same global variable reference they will be basically interchangeable. It's very cool, and very flexible. Try it, you'll love it!

So, we're going to create an object that performs the following functions:

on New me
-- instantiates the object and loads the MPEG

on PlayMedia me
-- play the MPEG

on StopMedia me
-- stop the MPEG

on SearchToLocation me
-- jump to a specified location

It uses the actorList to activate the baSleep function when the media is playing and also to check to see if the movie is finished and rewind to the start if it does.

We'll pass the full path and filename of our movie into the object as we instantiate it. We are going to use the BudApi Xtra to create a short file name from this path. The reason for this is that some machines, NT for one, choke on long filenames or ones containing spaces and will give you an MCI Error message. So why take a chance? (See, I told you we couldn't use just one function!)

We're also going to pass in the location on the stage that we want to place the MPEG and its size using a list consisting of the MovieTop, MovieLeft, MovieWidth and MovieHeight parameters in pixels. All this makes the object a lot more flexible.

Here is the code we'll put in the startMovie handler to set things up:

set mediaPath = the moviePath & "test.mpg"
set screenLocList = [#movieLeft: 80, #movieTop: 10, ¬
                        #movieWidth: 160, ¬
                        #movieHeight: 120]
set gMediaDeviceObject = new(script ¬
                        "MediaDeviceObject", ¬                         mediaPath, screenLocList)

This code assumes a movie stage size of 320 x 240, requires an MPEG video in the same path as the movie and named "test.mpg", and creates a list that gets passed into the object to position the video on the screen.

Now let's look at the "on new me" handler. I've commented almost every line:

on new me, mediaPath, ScreenLocList 

  -- make the short file name
  set mediaPath = baShortFileName(mediaPath ) 

  -- setup the video position values
  setMovieWindowLoc me, ScreenLocList 

  -- the video window is going to be 
  -- a child of the stage 
  set MovieStyle = "child" 

  -- get the window handle of the stage 
  set hWinStage = baStageHandle() 

  -- open the file 
  mci "open " & mediaPath && "alias" && "myMovie" ¬ 
             && "style" && MovieStyle && "parent" ¬ 
             && string(hWinStage) 

  -- position the video window mci "put" && "myMovie" ¬
             && "window at" && MovieLeft ¬ 
             && MovieTop && MovieWidth && MovieHeight 

  -- a little error checking is a good thing 
  mci "status myMovie mode" 
  set mciStatus = the Result 

  if mciStatus contains "MCI Error: " then 
    set errorMessage = "Movie not loaded due to - ¬ "
    & the result alert errorMessage HALT 
  end if 

  -- set the correct time format for the video 
  mci "set myMovie time format milliseconds" 

  -- we need the length so we can rewind at the end 
  set MediaLength = getMediaLength() 

  -- put the object into the actorList, check 
  -- out the "on stepFrame" handler 
  add the actorList me 

  -- pass a global reference to the object 
  -- back to the movie 
  return me 

end 

Have a good look at the way the MCI commands are built to open the video and then position it on the screen. Notice that all MCI commands are strings. Later you may want to step through this using the Debugger.

Next here's the stepFrame handler that gets executed on every exitFrame, but is completely contained in the object.


on stepFrame me

  if  playStatus = TRUE then baSleep (50)
  endOfMediaCallback

end
        

The playStatus property is set when we press the Play or Stop buttons. Notice that the baSleep command is only executed if the playStatus is TRUE. After all, it is unnecessary if the video is not playing!

Here is the handler that checks to see if the movie has gotten to the end. We check the position by using the MCI "status" command. If we are at the end, we make sure that we pause the video and then use the searchToMediaPosition handler to go back to the start.


on endOfMediaCallback me
  
  mci "status myMovie position"
  
  if the result > (MediaLength - 1) then 
    
    -- pause the video
    pauseMedia me
    
    -- rewind to the beginning
    SearchToMediaPosition me, 0
    
  end if
  
end
        

We've created play and pause handlers that we drop onto a button in the score. Each contains an MCI command and sets the current playStatus flag appropriately. When you create these handlers make sure that they are Score Scripts. Here is the pauseMedia handler:


on pauseMedia me  

  set playStatus = FALSE
  mci "pause myMovie"
  
end 
        

The playMedia handler looks almost identical.

Last here is the code in the killObject handler which is executed by the stopMovie handler.


on killObject me

  deleteOne (the actorList, me )
  mci "close myMovie"
  set me = VOID

end
        

It is important when working with the actorList to make sure that you remove objects from it when you are done. You'll notice that we explicitly close the video and VOID out the object reference. This is all good programming practice. Also, if you try to open a new movie without explicitly closing the movie alias you already have open, you will get an MCI Error.

So get it, and get with it!

So there you have it. There are a few other handlers, but you can look through the code in the example movie for yourself. The example movie (PC format only), contains all you will need to get started. You might think about adding other MCI commands that you find in the Macromedia Technote quoted above to the object. All of this could be rolled into a behavior that could be attached to a button on the stage. There are a lot of possibilities, even make your own Media Player! Give them a try and have fun. MCI is a powerful tool that will open up your programming capabilities.

Also, remember, this is just a demo. You may find various anomalies on different machines that I have not personally tested it on. This code is provided to you as-is, with absolutely no warrenties of any kind  and WE ARE UNABLE TO PROVIDE FREE SUPPORT.

Additional on-line resources

Here are some resources on the web that you should definitely check out:

MPEG.ORG
MARICOPA Director Web

You might also want to visit our Links page for possible additional pointers.

Credits
Several people have been very helpful to me in figuring out how to deal with MCI and object programming in general. They include:

    Peter Small               Gary Smith
    Cyril Bouaziz             Mauricio Piacentini
    Paul Hamilton            John Dowdell
    Mikhail Sherman       Paul Hemmer    

Thanks guys. <grin>