Q: I'm working with a 30 fps 720x480 DV movie and
I'd like to extract each field of each frame into something
I can add to a new file. Any ideas how to do this?
A: Each field can be extracted by using MoviesTask
to draw each frame into a GWorld with movie play hints high
quality turned on. Both fields will be rendered in the same
GWorld, and alternate lines may then be extracted from the
source GWorld allowing access to each field.
A quick way to extract alternate lines from a
GWorld is to use QTNewGWorldFromPtr.
Using the technique outlined in Technical Q&A
1014 Creating Sub GWorlds, create a 720x480 source GWorld in
which to draw the DV movie, then use QTNewGWorldFromPtr to
create two new 'field' GWorlds which address alternate lines
of the source GWorld.
QTNewGWorldFromPtr allows you to create an
offscreen graphics world, which may have a non-Macintosh
pixel format, where you define the base address and row
bytes. Because the application is responsible for passing a
pointer that isn't going to change during the lifetime of
the GWorld, it should ensure the source GWorld pixels are
locked. The application is also responsible for disposing of
the pixel data when done.
High quality play hints can be turned on by using
the SetMoviePlayHints API.
SetMoviePlayHints(theMovie, hintsHighQuality, hintsHighQuality);
|
Listing 1 demonstrates one way of creating the 'field'
GWorlds.
Listing 1. MakeDVFieldGWorlds
|
/*
MakeDVFieldGWorlds
This function returns two new GWorlds which point to
alternate lines of a main source GWorld. The QuickTime
DV codec draws both fields of a DV source in the same
GWorld when Movie Play Hints High Quality is turned on.
You can use this function to return two GWorlds which
will give you access to both separate fields.
In inGWorld - source GWorld
Out outTopFieldGW - GWorld containing the top field
of a DV frame
outBottomFieldGW - GWorld containing the
bottom field of a DV frame
*/
OSErr MakeDVFieldGWorlds(GWorldPtr inGWorld,
GWorldPtr *outTopFieldGW,
GWorldPtr *outBottomFieldGW)
{
PixMapHandle hPixMap = NULL;
long theRowBytes = 0L;
Ptr theBaseAddr = NULL;
OSType thePixelFormat;
Rect theBounds;
OSErr err = noErr;
if (NULL == inSrcGWorld || NULL == outTopFieldGW ||
NULL == outBottomFieldGW) return paramErr;
// get what we need from the source GWorld
hPixMap = GetGWorldPixMap(inGWorld);
/* must be locked as we are making two new GWorlds
that point to the pixels in this source GWorld */
if (!(GetPixelsState(hPixMap) && pixelsLocked))
LockPixels(hPixMap);
theBaseAddr = GetPixBaseAddr(hPixMap);
theRowBytes = QTGetPixMapHandleRowBytes(hPixMap);
thePixelFormat = GETPIXMAPPIXELFORMAT(*hPixMap);
theBounds = (**hPixMap).bounds;
// should end up as 240 for NTSC DV
theBounds.bottom = theBounds.bottom / 2;
// create a new GWorld for the top field
// we use rowBytes * 2 because we want every other line
err = QTNewGWorldFromPtr(
outTopFieldGW, // returned GWorld
thePixelFormat, // pixel format for new GWorld
&theBounds, // boundary rectangle
(**hPixMap).pmTable, // color table
0, // GDevice handle
0, // flags
theBaseAddr, // address of the pixel data
theRowBytes * 2); // number of bytes per row
if (err) goto bail;
// create a new GWorld for the bottom field
err = QTNewGWorldFromPtr(
outBottomFieldGW,
thePixelFormat,
&bounds,
(**hPixMap).pmTable,
0,
0,
(void *)(theBaseAddr + theRowBytes),
theRowBytes * 2);
bail:
return err;
}
|
After MoviesTask is used to draw into the main GWorld, the
individual fields can be accessed using the sub-GWorlds and
drawn with CopyBits.
Depending on the source, the fields may or may not be
spatially coherent. If they are not, a filter should be
applied to move the top field down half a pixel and the
bottom field up half a pixel. Differences in the two fields
will only be noticeable if something in the source is moving
fast.
References:
Technical Q&A QA1014, "Creating Sub GWorlds using QTNewGWorldFromPtr"
Technical Q&A QA1007, "LockPixels and DisposeGWorld with QTNewGWorldFromPtr"
[Mar 20 2001]
|