Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
VideoOutputComponent/SoftVOut.c
/* |
File: SoftVOut.c |
Description: This is an example video output component. |
Author: QuickTime Engineering, QuickTime DTS |
Copyright: © Copyright 1998 - 2005 Apple Computer, Inc. All rights reserved. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Change History (most recent first): <2> dts 08/02/2005 Added echo port support for 'raw ' source and filled out |
implementation by adding sample routines for GetIndSoundOutput, |
GetClock and CopyIndAudioOutputDeviceUID |
Updated Xcode project for universal binary support |
<1> 12/20/2001 Initial Release |
*/ |
/* |
A video output component receives QuickTime video data and delivers data to a video output |
device for display. If the incoming data is in a format that the video output device can |
display directly, the video output component can simply send the data to the video output device. |
If the incoming data cannot be displayed directly, the video output component must use a transfer |
codec or decompressor component to convert the data to a format that the video output device can display. |
If a video output device cannot directly display 32-bit RGB data or data in one of the other |
supported QuickTime pixel formats, the developers of the device are strongly encouraged to provide |
a transfer codec that accepts data in one of the supported QuickTime pixel formats (preferably 32-bit RGB) |
and converts it to data that can be displayed on the device. When this transfer codec is available, |
any QuickTime video can be displayed on the video output device: the Image Compression Manager can convert |
any QuickTime images to a supported QuickTime pixel format and then invoke the transfer codec to display |
the result. |
If any special decompressors, such as a transfer codec, are needed for a video output device, the |
decompressors are included in the definitions of the component's display modes. |
*/ |
#if __APPLE_CC__ || __MACH__ |
#include <Carbon/Carbon.h> |
#include <QuickTime/QuickTime.h> |
#include <CoreFoundation/CoreFoundation.h> |
#include <CoreAudio/CoreAudio.h> |
#else |
#include <Components.h> |
#include <Movies.h> |
#include <ImageCodec.h> |
#include <ImageCompression.h> |
#include <QuickTimeComponents.h> |
#include <string.h> |
#endif |
// VOut component shared globas |
typedef struct { |
CGrafPtr echoPort; |
Ptr baseAddr; |
long rowBytes; |
short width; |
short height; |
Boolean isEchoPortOn; |
} SharedGlobalsRecord, *SharedGlobalsPtr; |
// Per instance globals |
typedef struct { |
ComponentInstance self; |
ComponentInstance target; |
ComponentInstance baseVideoOutput; |
Component soundOutput; |
ComponentInstance clock; |
SharedGlobalsPtr sharedGlobals; |
QTAtomContainer displayModeList; |
long theCustomPixelFormat; |
short width; |
short height; |
Boolean ownsHardware; |
} SoftVoutGlobalsRecord, *SoftVoutGlobalsPtr; |
// Our Private Pixel Format |
// To register a new fourCC please send mail to qtfourCC@apple.com. |
// Include an email address (for future correspondence), the fourCC you |
// would like to register, and a brief description of the fourCC format. |
// For more information refer to IceFloe #20 - QuickTime Pixel Format FourCCs |
// http://developer.apple.com/quicktime/icefloe/dispatch020.html |
enum { |
kSoftPixelFormat = FOUR_CHAR_CODE('soft') |
}; |
enum { |
kSoftCodecType = FOUR_CHAR_CODE('raw '), |
kSoftCodecManufacturer = FOUR_CHAR_CODE('dts ') |
}; |
// Prototypes |
static ComponentResult CreateVOutSharedGlobals(SoftVoutGlobalsPtr storage); |
static OSStatus GetMyAudioDriverUID(CFStringRef *outUID); |
static pascal ComponentResult CreateDisplayModeList(SoftVoutGlobalsPtr storage); |
static pascal ComponentResult CreateDisplayModeAtom(SoftVoutGlobalsPtr storage, OSType pixelType, const char *name); |
static pascal ComponentResult AddDecompressorsAtomToDisplayMode(SoftVoutGlobalsPtr storage, UInt8 index, ComponentDescription *cd, Boolean continuous); |
/************************************************************************************/ |
// Setup required for ComponentDispatchHelper.c |
#define CALLCOMPONENT_BASENAME() SoftVout_ |
#define CALLCOMPONENT_GLOBALS() SoftVoutGlobalsPtr storage |
#define QTVIDEOOUTPUT_BASENAME() CALLCOMPONENT_BASENAME() |
#define QTVIDEOOUTPUT_GLOBALS() CALLCOMPONENT_GLOBALS() |
#define COMPONENT_DISPATCH_FILE "SoftVOutDispatch.h" |
#define GET_DELEGATE_COMPONENT() (storage->baseVideoOutput) |
#define COMPONENT_UPP_SELECT_ROOT() QTVideoOutput |
#if __APPLE_CC__ || __MACH__ |
#include <CoreServices/Components.k.h> // Standard .k.h |
#include <QuickTime/QuickTimeComponents.k.h> // Type & SubType .k.h |
#include <QuickTime/ComponentDispatchHelper.c> // Make the dispatcher and canDo |
#else |
#include "Components.k.h" // Standard .k.h |
#include "QuickTimeComponents.k.h" // Type & SubType .k.h |
#include "ComponentDispatchHelper.c" // Make the dispatcher and canDo |
#endif |
/************************************************************************************/ |
// Component Manager Calls |
// Component Open Request - Required |
pascal ComponentResult SoftVout_Open(SoftVoutGlobalsPtr storage, ComponentInstance self) |
{ |
ICMPixelFormatInfo pixelInfo; |
ComponentResult err; |
// Allocate globals |
storage = (SoftVoutGlobalsPtr)NewPtrClear(sizeof(SoftVoutGlobalsRecord)); |
if ((err = MemError())) goto bail; |
SetComponentInstanceStorage(self, (Handle)storage); |
storage->self = self; |
storage->target = self; |
storage->soundOutput = 0; |
storage->clock = 0; |
storage->sharedGlobals = NULL; |
// Open the base VOut for future delegation |
// To use the services of the base video output component, |
// your video output component must open a connection to the base video output component |
err = OpenADefaultComponent(QTVideoOutputComponentType, QTVideoOutputComponentBaseSubType, &storage->baseVideoOutput); |
if (err) goto bail; |
// Set us as the base component's target |
err = ComponentSetTarget(storage->baseVideoOutput, self); |
if (err) goto bail; |
// SoftVOut a.k.a Mr. Fake Video Output is 320 x 240 |
storage->width = 320; |
storage->height = 240; |
storage->theCustomPixelFormat = kSoftPixelFormat; |
// Register information about our custom pixel format with the ICM, this is |
// needed only for the case where the VOut is using a transfer codec (SoftCodec) |
BlockZero(&pixelInfo, sizeof(pixelInfo)); |
pixelInfo.size = sizeof(ICMPixelFormatInfo); |
pixelInfo.formatFlags = 0; |
pixelInfo.bitsPerPixel[0] = 32; |
// Ignore any errors as this could be a duplicate registration |
ICMSetPixelFormatInfo(storage->theCustomPixelFormat, &pixelInfo); |
// Create our shared globals with the transfer codec so our echo port stuff will work |
err = CreateVOutSharedGlobals(storage); |
bail: |
if (err) { |
SoftVout_Close(storage, self); |
} |
return err; |
} |
// Component Close Request - Required |
pascal ComponentResult SoftVout_Close(SoftVoutGlobalsPtr storage, ComponentInstance self) |
{ |
SharedGlobalsPtr mySharedGlobals = NULL; |
if (storage) { |
if (storage->baseVideoOutput) { |
// When your video output component closes, it must close its connection |
// to the base video output component |
CloseComponent(storage->baseVideoOutput); |
storage->baseVideoOutput = 0; |
} |
if (storage->displayModeList) { |
QTDisposeAtomContainer(storage->displayModeList); |
storage->displayModeList = NULL; |
} |
if (storage->sharedGlobals) { |
mySharedGlobals = (SharedGlobalsPtr)GetComponentRefcon((Component)storage->self); |
if (mySharedGlobals) { |
DisposePtr((Ptr)mySharedGlobals); |
SetComponentRefcon((Component)storage->self, (long)NULL); |
} |
} |
DisposePtr((Ptr)storage); |
} |
return noErr; |
} |
// Component Version Request - Required |
pascal ComponentResult SoftVout_Version(SoftVoutGlobalsPtr storage) |
{ |
#pragma unused(storage) |
const int videoOutputInterfaceVersion = 1; |
return (videoOutputInterfaceVersion << 16) + 1; |
} |
// Component Target Request |
pascal ComponentResult SoftVout_Target(SoftVoutGlobalsPtr storage, ComponentInstance target) |
{ |
ComponentResult err; |
// Tell the base video output to target the instance |
err = CallComponentTarget(storage->baseVideoOutput, target); |
if (err) goto bail; |
// Remember our target |
storage->target = target; |
bail: |
return err; |
} |
// Component Register Request |
pascal ComponentResult SoftVout_Register(SoftVoutGlobalsPtr storage) |
{ |
#pragma unused(storage) |
// Always register |
return noErr; |
} |
#pragma mark- |
/************************************************************************************/ |
// Video Output Component Calls |
// QTVideoOutputGetDisplayModeList |
// Return a copy of the list to the caller, they are responsible for disposing of it. |
// See below for more information. |
pascal ComponentResult SoftVout_GetDisplayModeList(SoftVoutGlobalsPtr storage, QTAtomContainer* outputs) |
{ |
ComponentResult err = noErr; |
// Create our list of display modes if not done yet |
if (NULL == storage->displayModeList) { |
err = CreateDisplayModeList(storage); |
if (err) goto bail; |
} |
// Copy it all into a new atom container |
err = QTNewAtomContainer(outputs); |
if (noErr == err) { |
err = QTCopyAtom(storage->displayModeList, kParentAtomIsContainer, outputs); |
if (noErr != err) { |
QTDisposeAtomContainer(*outputs); |
} |
} |
bail: |
return err; |
} |
// QTVideoOutputBegin |
// This is basically when you should turn your Video Output hardware on. |
pascal ComponentResult SoftVout_Begin(SoftVoutGlobalsPtr storage) |
{ |
long mode; |
ComponentResult err; |
// Call the default implementation |
// The default implementation of the QTVideoOutputBegin function ensures that |
// the hardware is not currently in use by other software. It also ensures that a |
// valid display mode has been set with either the QTVideoOutputSetDisplayMode function or the |
// QTVideoRestoreSettings function. |
err = QTVideoOutputBegin(storage->baseVideoOutput); |
if (noErr == err) { |
// Get the selected mode |
err = QTVideoOutputGetDisplayMode(storage->self, &mode); |
if (noErr == err) { |
// NOTE: -> This is a good place to switch your hardware to the selected mode |
// Remember that we now own the hardware |
storage->ownsHardware = true; |
} |
} |
if (noErr != err && storage->ownsHardware) { |
QTVideoOutputEnd(storage->baseVideoOutput); |
} |
return err; |
} |
// QTVideoOutputEnd |
// This is basically when you should turn your Video Output hardware off. |
// In the implementation of QTVideoOutputEnd, your component should also display a |
// default image on the video output device to indicate that the device is no longer in use |
// by other software. |
pascal ComponentResult SoftVout_End(SoftVoutGlobalsPtr storage) |
{ |
ComponentResult err; |
// Check that Begin has been called |
if (!storage->ownsHardware) return paramErr; |
// NOTE: -> This is a good place to release your hardware |
// Call the default implementation |
err = QTVideoOutputEnd(storage->baseVideoOutput); |
if (storage->clock) { |
CloseComponent(storage->clock); |
storage->clock = 0; |
} |
// Remember that we no longer own the hardware |
storage->ownsHardware = false; |
return err; |
} |
// QTVideoOutputGetGWorldParameters - Required |
// Return the parameters of the Video Output GWorld. |
// Your video output component must implement this function, it is not called by applications or other |
// clients of your component; it is called by the base video output component as part of the implementation |
// of the QTVideoOutputGetGWorld function. |
// baseAddr - return the address at which to display pixels. If your component does not display |
// pixels, return 0. |
// rowBytes - return the width of each scan line in bytes. If your component does not display |
// pixels, return the width of the current display mode. |
// colorTable - return the color table to be used. If your component does not use a color table, return NULL. |
// In the software case, we are simply returning a pointer to a portion of the main screen. |
pascal ComponentResult SoftVout_GetGWorldParameters(SoftVoutGlobalsPtr storage, Ptr *baseAddr, long *rowBytes, CTabHandle *colorTable) |
{ |
GDHandle mainGD = GetMainDevice(); |
ComponentResult err = internalComponentErr; |
// The VOut is going to use the lower right hand corner of the main device |
if (mainGD) { |
PixMapHandle gdPMap = (*mainGD)->gdPMap; |
// Get the rowBytes from the main device's PixMap |
*rowBytes = QTGetPixMapPtrRowBytes(*gdPMap); |
// Get the color table from the main device |
if ((*gdPMap)->pixelSize < 16) { |
*colorTable = (*gdPMap)->pmTable; |
} else { |
*colorTable = NULL; |
} |
// Offset the screen's baseAddr to the top of our VOut |
*baseAddr = (*gdPMap)->baseAddr |
+ ((*gdPMap)->bounds.bottom - storage->height) * (*rowBytes) |
+ ((*gdPMap)->bounds.right - storage->width) * ((*gdPMap)->pixelSize / 8); |
// Save the required information for the transfer codec in our shared globals |
storage->sharedGlobals->baseAddr = *baseAddr; |
storage->sharedGlobals->rowBytes = *rowBytes; |
storage->sharedGlobals->width = storage->width; |
storage->sharedGlobals->height = storage->height; |
err = noErr; |
} |
return err; |
} |
#if TARGET_OS_MAC |
// QTVideoOutputGetIndSoundOutput |
// Return the sound output component associated with the video output component |
// specified by the index parameter. The index of the first component should be 1. |
pascal ComponentResult SoftVout_GetIndSoundOutput(SoftVoutGlobalsPtr storage, long index, Component *outputComponent) |
{ |
Component c = 0; |
ComponentDescription cd = { kSoundOutputDeviceType, kHALCustomComponentSubType, 0, 0, 0 }; |
CFStringRef myUID = NULL; |
CFStringRef halUID = NULL; |
SMStatus ignore; |
ComponentResult err; |
*outputComponent = NULL; |
// we only have one Sound Output Component |
// so if index is anything other than 1 it's an error |
if (index != 1 ) return paramErr; |
// already have it, we're done |
if (storage->soundOutput) { |
*outputComponent = storage->soundOutput; |
return noErr; |
} |
// find our audio drivers UID using core audio |
err = GetMyAudioDriverUID(&myUID); |
if (err) goto bail; |
// cheaply init the Sound Manager - must be done so the Sound Manager will |
// synthesize Sound Output Components for each Core Audio Driver on the system |
SndManagerStatus(sizeof(SMStatus), &ignore); |
// find our synthesized Sound Output Component |
while (c = FindNextComponent(c, &cd)) { |
// don't release the returned CFString here, it's not retained as in the property case |
err = SoundComponentGetInfo((ComponentInstance)c, 0, siHALAudioDeviceUniqueID, &halUID); |
if (err) goto bail; |
// if it's us, we're done |
if (CFEqual(myUID, halUID)) { |
*outputComponent = c; |
storage->soundOutput = c; |
break; |
} |
} |
// we didn't find the corresponding sDev, that's bad! |
if (NULL == *outputComponent) err = badComponentType; |
bail: |
if (myUID) CFRelease(myUID); |
return err; |
} |
// QTVideoOutputGetClock |
// Returns a pointer to the clock component associated with the video output component. |
pascal ComponentResult SoftVout_GetClock(SoftVoutGlobalsPtr storage, ComponentInstance *clock ) |
{ |
ComponentDescription cd = { clockComponentType, systemMicrosecondClock, kAppleManufacturer, 0, 0 }; |
ComponentResult err; |
*clock = 0; |
// Check that Begin has been called |
if (!storage->ownsHardware) return badCallOrderErr; |
if (storage->clock) { |
*clock = storage->clock; |
return noErr; |
} |
err = OpenAComponent(FindNextComponent(0, &cd), &storage->clock); |
if (noErr == err) |
*clock = storage->clock; |
return err; |
} |
// QTVideoOutputSetEchoPort |
// Specifies a window on the desktop in which to display video sent to the device. |
pascal ComponentResult SoftVout_SetEchoPort(SoftVoutGlobalsPtr storage, CGrafPtr echoPort) |
{ |
ComponentResult err = noErr; |
// QuickTime 6 - Mach-O only |
err = QTVideoOutputBaseSetEchoPort(storage->baseVideoOutput, echoPort); |
// Set the information in the shared globals for the transfer codec |
if (err == noErr) { |
storage->sharedGlobals->echoPort = echoPort; |
storage->sharedGlobals->isEchoPortOn = (NULL != echoPort); |
} else { |
storage->sharedGlobals->echoPort = NULL; |
storage->sharedGlobals->isEchoPortOn = false; |
} |
return err; |
} |
// QTVideoOutputCopyIndAudioOutputDeviceUID |
// Identifies the audio device being used by a video output component. |
pascal ComponentResult SoftVout_CopyIndAudioOutputDeviceUID(SoftVoutGlobalsPtr storage, long index, CFStringRef *audioDeviceUID) |
{ |
#pragma unused (storage) |
CFStringRef myUID = NULL; |
ComponentResult err = paramErr; |
// if we were passed a CFStringRef |
if ((index == 1) && (NULL != audioDeviceUID)) { |
// find our audio driver UID using core audio |
err = GetMyAudioDriverUID(&myUID); |
// if there was no error and we got the UID |
if ((noErr == err) && myUID) { |
// copy the UID for the caller |
*audioDeviceUID = CFStringCreateCopy(kCFAllocatorDefault, myUID); |
// done with this UID, release our reference |
CFRelease(myUID); |
} |
} |
return err; |
} |
#endif |
#pragma mark- |
/************************************************************************************/ |
// Local Component Calls |
// Create some globals to be shared by the transfer codec |
ComponentResult CreateVOutSharedGlobals(SoftVoutGlobalsPtr storage) |
{ |
SharedGlobalsPtr mySharedGlobals = NULL; |
ComponentResult err = noErr; |
// Create shared variables if they donÕt exist |
mySharedGlobals = (SharedGlobalsPtr)GetComponentRefcon((Component)storage->self); |
if (mySharedGlobals == NULL) { |
mySharedGlobals = (SharedGlobalsPtr)NewPtrClear(sizeof(SharedGlobalsRecord)); |
if (mySharedGlobals == NULL) { |
err = MemError(); |
goto bail; |
} |
SetComponentRefcon((Component)storage->self, (long)mySharedGlobals); |
} |
storage->sharedGlobals = (SharedGlobalsPtr)mySharedGlobals; |
bail: |
return err; |
} |
#define kMaxStringSize 1024 |
#if TARGET_OS_MAC |
// GetMyAudioDriverUID |
// Find your drivers UID and return it in a CFString the calling |
// function is responsible for releasing. |
OSStatus GetMyAudioDriverUID(CFStringRef *outUID) |
{ |
UInt32 theSize; |
char theString[kMaxStringSize]; |
UInt32 theNumberDevices; |
AudioDeviceID *theDeviceList = NULL; |
UInt32 theDeviceIndex; |
CFStringRef theCFString = NULL; |
// this is us |
const char *nameString = "Built-in Audio"; |
const char *manufacturerString = "Apple"; |
OSStatus theStatus = noErr; |
*outUID = NULL; |
// device list size |
theSize = 0; |
theStatus = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &theSize, NULL); |
if (theStatus) goto done; |
theNumberDevices = theSize / sizeof(AudioDeviceID); |
// allocate the device list |
theDeviceList = (AudioDeviceID*)malloc(theNumberDevices * sizeof(AudioDeviceID)); |
// get the device list |
theSize = theNumberDevices * sizeof(AudioDeviceID); |
theStatus = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &theSize, theDeviceList); |
// iterate through the device list, find our device and return the UID |
for(theDeviceIndex = 0; theDeviceIndex < theNumberDevices; ++theDeviceIndex) { |
// get a name |
theSize = kMaxStringSize; |
theStatus = AudioDeviceGetProperty(theDeviceList[theDeviceIndex], 0, 0, kAudioDevicePropertyDeviceName, &theSize, theString); |
if (theStatus) goto done; |
// is it me? |
if (strncmp(theString, nameString, strlen(nameString)) == 0) { |
// get a manufacturer |
theSize = kMaxStringSize; |
theStatus = AudioDeviceGetProperty(theDeviceList[theDeviceIndex], 0, 0, kAudioDevicePropertyDeviceManufacturer, &theSize, theString); |
if (theStatus) goto done; |
// is it really me? |
if (strncmp(theString, manufacturerString, strlen(manufacturerString)) == 0) { |
// get device UID |
// Core Audio retains the returned CFString so make sure and release it when you're done |
theSize = sizeof(CFStringRef); |
theStatus = AudioDeviceGetProperty(theDeviceList[theDeviceIndex], 0, 0, kAudioDevicePropertyDeviceUID, &theSize, &theCFString); |
if (theStatus) goto done; |
*outUID = theCFString; |
break; |
} |
} |
} |
// we didn't find ourselves, that's bad! |
if (NULL == *outUID) theStatus = badComponentType; |
done: |
// free the device list |
if (theDeviceList) free(theDeviceList); |
return theStatus; |
} |
#endif |
/* |
A video output device has one or more display modes. The characteristics of each mode determine |
how video is displayed. When any software displays video on a video output device, it must select |
which of the device's display modes to use. |
The characteristics of a display mode include |
+ the height of the displayed image, in pixels |
+ the width of the displayed image, in pixels |
+ the horizontal resolution of the display, in pixels per inch |
+ the vertical resolution of the display, in pixels per inch |
+ the refresh rate of the display, in Hertz |
+ the pixel type of the display |
+ a text description of the display mode |
The characteristics can also include a list of decompressor components required for the mode that are |
provided specifically for the video output device. If a video output device cannot directly display any of |
the pixel formats supported by QuickTime, the vendor of the device must provide one or more special decompressors |
to convert supported pixel formats to a format the device can display. If a video output device can display one or |
more of the pixel formats supported by QuickTime, the Image Compression Manager can use standard decompressors that |
are included with QuickTime, and the list of special decompressor components can be empty. |
These characteristics, returned by the QTVideoOutputGetDisplayModeList function, are stored in a QT atom container. |
For a description of this QT atom container, see: |
http://developer.apple.com/techpubs/quicktime/qtdevdocs/REF/refVidOutComp.1e.htm#23578 |
*/ |
// CreateDisplayModeList |
// Builds the entire display list hierarchy and returns the QTAtom if successful. |
// |
// SoftVOut supports two modes: |
// 1) The first mode provides a GWorld by wrapping a portion of the screen. |
// Codecs write directly to the GWorld since QuickTime understands the pixel format. |
// 2) The second mode uses a custom pixel type (kSoftPixelFormat). The fake VOut transfer |
// codec (SoftCodec) will take care of the drawing. |
// |
// Below is the structure of the display mode list. It contains two displays modes. |
// At the root of the QT atom container are one or more atoms of type kQTVODisplayModeItem. |
// |
// kQTVODisplayModeItem Mode 1 |
// kQTVODimensions 320 x 240 |
// kQTVOResolution ??, ?? |
// kQTVORefreshRate 60 |
// kQTVOPixelType ?? |
// kQTVOName SoftVOut |
// |
// kQTVODisplayModeItem Mode 2 |
// kQTVODimensions 320 x 240 |
// kQTVOResolution ??, ?? |
// kQTVORefreshRate 60 |
// kQTVOPixelType soft |
// kQTVOName SoftVOut using Codec |
// kQTVODecompressors |
// kQTVODecompressorType 'raw ' |
// kQTVODecompressorContinuous true |
// kQTVODecompressorComponent ???? |
pascal ComponentResult CreateDisplayModeList(SoftVoutGlobalsPtr storage) |
{ |
const char * const mode1Name = "SoftVOut"; |
const char * const mode2Name = "SoftVOut using Codec"; |
ComponentDescription cd = { decompressorComponentType, kSoftCodecType, kSoftCodecManufacturer, 0, 0L }; |
GDHandle mainGD = GetMainDevice(); |
ComponentResult err = noErr; |
// Create the container |
err = QTNewAtomContainer(&storage->displayModeList); |
if (err) goto bail; |
// Create the 'SoftVOut' mode 1 |
err = CreateDisplayModeAtom(storage, GETPIXMAPPIXELFORMAT(*(*mainGD)->gdPMap), mode1Name); |
if (err) goto bail; |
// Create the 'SoftVOut using Codec' mode 2 |
err = CreateDisplayModeAtom(storage, storage->theCustomPixelFormat, mode2Name); |
// Add the kQTVODecompressors atom for mode 2 |
err = AddDecompressorsAtomToDisplayMode(storage, 2, &cd, true); |
bail: |
if (err && storage->displayModeList) { |
QTDisposeAtomContainer(storage->displayModeList); |
storage->displayModeList = NULL; |
} |
return err; |
} |
// CreateDisplayModeAtom |
// Add an atom for a given display mode to our current list of display modes. |
pascal ComponentResult CreateDisplayModeAtom(SoftVoutGlobalsPtr storage, OSType pixelType, const char *name) |
{ |
QTAtom displayModeAtom; |
long dimensions[2]; |
Fixed resolution[2]; |
Fixed rate = (60 << 16); // make up a refresh rate -> 60Hz |
OSType nPixelType; |
GDHandle mainGD = GetMainDevice(); |
ComponentResult err; |
dimensions[0] = EndianU32_NtoB(storage->width); // width |
dimensions[1] = EndianU32_NtoB(storage->height); // height |
resolution[0] = EndianU32_NtoB((*(*mainGD)->gdPMap)->hRes); // horizontal |
resolution[1] = EndianU32_NtoB((*(*mainGD)->gdPMap)->vRes); // vertical |
rate = EndianU32_NtoB(rate); // refresh rate |
nPixelType = EndianU32_NtoB(pixelType); // pixel format |
// Create an atom to describe our display mode... |
err = QTInsertChild(storage->displayModeList, |
kParentAtomIsContainer, |
kQTVODisplayModeItem, |
0, // ask QTInsertChild to assign a unique ID |
0, // insert at the beginning |
0, NULL, // no data in this atom |
&displayModeAtom); |
if (err) goto bail; |
// ...with given dimensions... |
err = QTInsertChild(storage->displayModeList, |
displayModeAtom, |
kQTVODimensions, |
1, // ID |
0, // insert at the beginning |
sizeof(dimensions), dimensions, |
NULL); |
if (err) goto bail; |
// ...and given resolutions... |
err = QTInsertChild(storage->displayModeList, |
displayModeAtom, |
kQTVOResolution, |
1, // ID |
0, // insert at the beginning |
sizeof(resolution), resolution, |
NULL); |
if (err) goto bail; |
// ...and with a given refresh rate... |
err = QTInsertChild(storage->displayModeList, |
displayModeAtom, |
kQTVORefreshRate, |
1, // ID |
0, // insert at the beginning |
sizeof(rate), &rate, |
NULL); |
if (err) goto bail; |
// ...and a given pixel type... |
err = QTInsertChild(storage->displayModeList, |
displayModeAtom, |
kQTVOPixelType, |
1, // ID |
0, // insert at the beginning |
sizeof(nPixelType), &nPixelType, |
NULL); |
if (err) goto bail; |
// ...and a given name. |
// NOTE: Name is added without the trailing length byte. This is |
// to spec. Clients should determine the string length by examining |
// the length of the atom data. |
err = QTInsertChild(storage->displayModeList, |
displayModeAtom, |
kQTVOName, |
1, // ID |
0, // insert at the beginning |
strlen(name), (void *)name, |
NULL); |
bail: |
return err; |
} |
// AddDecompressorsAtomToDisplayMode |
// Add a kQTVODecompressors atom for a given display mode item. |
pascal ComponentResult AddDecompressorsAtomToDisplayMode(SoftVoutGlobalsPtr storage, UInt8 index, ComponentDescription *cd, Boolean continuous) |
{ |
DecompressorComponent c = 0; |
QTAtom theParent = 0; |
OSType theDecompressorType; |
long theComponent; |
ComponentResult err; |
theDecompressorType = EndianU32_NtoB(cd->componentSubType); // decompressor type |
// Find the asked for kQTVODisplayModeItem atom |
theParent = QTFindChildByIndex(storage->displayModeList, |
kParentAtomIsContainer, |
kQTVODisplayModeItem, |
index, |
NULL); |
if (theParent == 0) goto bail; |
// Create an atom to describe our special decompressor |
err = QTInsertChild(storage->displayModeList, |
theParent, |
kQTVODecompressors, |
0, // ask QTInsertChild to assign a unique ID |
0, // insert at the beginning |
0, NULL, // no data in this atom |
&theParent); |
if (err) goto bail; |
// ...with a decompression type... |
err = QTInsertChild(storage->displayModeList, |
theParent, |
kQTVODecompressorType, |
1, // ID |
0, // insert at the beginning |
sizeof(theDecompressorType), &theDecompressorType, |
NULL); |
if (err) goto bail; |
// ...the continuous display setting... |
err = QTInsertChild(storage->displayModeList, |
theParent, |
kQTVODecompressorContinuous, |
1, // ID |
0, // insert at the beginning |
sizeof(continuous), &continuous, |
NULL); |
if (err) goto bail; |
c = FindNextComponent(c, cd); |
if (c) { |
// ...and a decompression component value. |
theComponent = EndianU32_NtoB((long)c); |
err = QTInsertChild(storage->displayModeList, |
theParent, |
kQTVODecompressorComponent, |
1, // ID |
0, // insert at the beginning |
sizeof(theComponent), &theComponent, |
NULL); |
} |
bail: |
return err; |
} |
Copyright © 2005 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2005-08-10