Tutorial 19: Recording QuickTime movies
This tutorial shows how you can record a single matrix or a sequence of matrices to disk as a QuickTime movie. We'll demonstrate the use of the jit.record object for recording in real time and non-real time. Along the way, we'll also show you how to adjust some of the settings of the output movie.
Like most Jitter objects, the jit.record object operates according to an event-driven model rather than a time-driven one -- frames sent to a record-enabled jit.record object are appended to the movie in progress as "the next frame" based on the timing characteristics of the output movie, without regard for the relative time between the arriving frames. This results in very smooth movies with consistent timing between frames, but it requires some preparation before recording.
Your mileage may vary
Each and every matrix that arrives at a recording jit.record object must be compressed before becoming part of your output movie. The speed of this compression depends on several factors: the speed of your processor and your disk drive, and -- most importantly -- the type of compression used (referred to as the codec, short for compression/decompression). The examples in this tutorial will use the Photo-JPEG codec. It should yield fairly consistent results from machine to machine without a lot of high CPU usage and disk access. On older or slower systems, it's possible that you'll notice some timing inaccuracy with the first set of examples for this tutorial. If you do, don't worry; the second part of this tutorial will show you how to achieve excellent results on even the most modest system by working in non-realtime mode.
On the clock
The first example shows a very basic recording setup. Notice that the jit.record object takes two arguments ( and ) that specify the width and height of any movies we record with this object.
The jit.record object sends a message out its right outlet after a write operation to confirm that the movie was successfully written. Since we've connected that outlet to a print object in our patch , we can see the results by looking at the Max Console. If everything went as expected, you should see in the Max Console. If there was a problem, the number after the filename will not be a .
Let's examine the jit.record object for a moment. The message puts jit.record into a record-enabled mode. The actual recording doesn't begin until the object receives a matrix in its inlet. The message can take several optional arguments to specify a file name, frame rate, the codec type, the codec quality and the timescale for the output movie. In this example we're only using two arguments ( and ) to specify the frame rate ( indicates 15 frames per second) and the codec type ( indicates Photo-JPEG) of the output movie. For a full description of all the arguments for the message, see the Object Reference entry for the jit.record object.message to the
Note that we have the metro object set to send a message every 66.67 milliseconds. This is done to match the input movie's frame rate. If you change the metro object's rate and record a few more movies, you will see that a faster metro rate causes the movie to be longer and slower, while a slower metro rate results in a shorter, faster movie. This happens because of the event-driven model being used by jit.record.
The second example patch is a little more complicated, but it has two important additions (and several minor ones).
First, we're automatically detecting the frame rate of the original movie (using the metro object driving jit.record and the output movie's frame rate, as well. Let's look at that portion of the patch in detail:message), and using it to set both the rate of the
- When a movie is successfully loaded into jit.movie, the object sends the message out its right outlet. (If the operation is unsuccessful, the number will not be 1.) We're using the route object to retrieve this message.
- The unpack object breaks up the remaining elements of the message. We're not interested in the file name, only in the success or failure of the read operation, so we've attached nothing to the left outlet of unpack, and attached a select object, which tests for the integer 1, to the right outlet. If our operation was successful, the select object will output a .
- The jit.movie, which will respond by sending the message out its right outlet. The message element is a floating-point number representing the movie's frame rate. This value is sent from the route object's middle outlet, and used to set the metro object's speed and the output movie's frame rate. is used to send the message to
This patch includes a second improvement. Rather than locking the entire patch to the frame rate of the original movie, we've added a jit.matrix object with turned off to collect the output of the patch, and we're using a second metro to send it messages at the correct frame rate for jit.record. This permits the rest of our patch to operate independently, at as fast or slow a rate as we want, with only the very last part of the patch specifically timed to the recording process.
Finally, we've added a neat crosshatching filter (the jit.hatch object) with its own editing control (the number box and the message box). You can use this to modify your movie while you're recording it and watch the effects on playback. You can substitute any kind of Jitter object or patch you want at this point in the recording patch and modify what you record in real time.
Off the clock
Both of the previous patches have a problem: under high processor loads, these patches will drop frames. In many cases, this is not a problem, and the patches we've looked at are well suited to recording in any context, including live performance.
But how can we record a sequence in which every frame in an original movie has a corresponding frame in the output movie? How do you render movies in Jitter so that, even under heavy processor or disk load, every processed frame is captured?
On first inspection, the third example patch may not seem a lot more complicated than the previous one. But don't be fooled -- this unassuming patch will bring even the speediest computer to its knees.
While this patch runs slowly in real time, it does a great job of creating a QuickTime movie in non-realtime. Using the jit.movie object's message and the jit.record object, the patch will capture every frame of the original movie, process it, and produce an output movie of identical length.
Here's how it works: the jit.movie object to stop playing its movie, and then sends each frame out, one at a time. (N.B. You don't need to send a or message to the jit.movie object for the frames to be sent.) Because of the way the Max scheduler works, a new frame will not be sent until the previous frame has been fully processed and recorded. Let's try it out.message tells the
The example patch also takes advantage of the fact that the jit.record object sends a matrix out its left outlet after recording a frame. We're using that frame to trigger a message to a counter object that is driving the attribute of the jit.hatch filter. This creates a repeatable sequence of processing changes that happen as the QuickTime movie is captured.
The jit.record object provides event-driven ways to record Jitter matrices as QuickTime movies. You can record the output of a Jitter patch in real time as you manipulate your patch, or out of real time, in render-like operation. You can use the jit.movie object's message to process an entire movie file without dropping any frames. This method lets you record high quality movies regardless of the load being placed on your computer's processor.
|jit.fastblur||Blur/sharpen using optimized algorithm|
|jit.hatch||Perform crosshatch filtering|
|jit.matrix||The Jitter Matrix!|
|jit.pwindow||Display Jitter data and images|
|jit.movie||Play a QuickTime movie|
|jit.record||Record a QuickTime movie|
|jit.robcross||Robert's Cross edge detection|
|jit.wake||Feedback with convolution stage|