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.
main.c
/* |
File: main.c |
Description: Implementation of the Background Movie Export Server |
Author: QuickTime DTS |
Copyright: © Copyright 2004 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): 07/20/04 initial release |
*/ |
#pragma mark * includes & imports * |
#include <CoreFoundation/CoreFoundation.h> |
#include <QuickTime/QuickTime.h> |
#include <pthread.h> |
#include <unistd.h> // for sleep() |
#include "ExportCommon.h" |
#pragma mark- |
#pragma mark * typedef's, struct's, enums, defines, etc. * |
typedef struct { |
SInt32 numberOfActiveRequests; // number of requests that have been created but not yet released |
pthread_t workerThread; |
CFMessagePortRef localPort; |
CFMessagePortRef remotePort; |
CFRunLoopSourceRef runLoopSource; |
CFRunLoopRef runLoopRef; |
QHdr requestQueue; // messages going from main thread to worker thread |
Boolean shutdown; // set to ask worker thread to shut down when all request queue is empty |
} WorkerThread, *WorkerThreadRef; |
typedef struct WorkerRequest { |
SInt32 referenceCount; |
WorkerThreadRef worker; // note: each active request maintains a reference to its worker |
QElemPtr nextRequest; // used when linked into worker->requestQueue |
Boolean wasCancelledOrStarted; |
Boolean actionFinished; |
// client-use fields |
CFDataRef data; |
} WorkerRequest, *WorkerRequestRef; |
#pragma mark - |
#pragma mark * local (static) globals * |
static pthread_mutex_t mutex; |
static pthread_cond_t condition; |
#pragma mark - |
#pragma mark * exported function implementations * |
OSErr CreateWorkerRequest(WorkerThreadRef worker, WorkerRequestRef *outRequest) |
{ |
WorkerRequestRef request; |
if (!outRequest) return paramErr; |
request = calloc(1, sizeof(WorkerRequest)); |
if (!request) return memFullErr; |
request->worker = worker; |
IncrementAtomic(&request->referenceCount); |
IncrementAtomic(&worker->numberOfActiveRequests); |
*outRequest = request; |
return noErr; |
} |
void ReleaseWorkerRequest(WorkerRequestRef request) |
{ |
if (1 == DecrementAtomic(&request->referenceCount)) { |
DecrementAtomic(&request->worker->numberOfActiveRequests); |
CFRelease(request->data); |
free(request); |
} |
} |
pascal OSErr myMovieProgressProc(Movie theMovie, short theMessage, short theOperation, Fixed thePercentDone, long refcon) |
{ |
char message[255]; |
CFDataRef data, ignore = NULL; |
WorkerThreadRef worker = (WorkerThreadRef)refcon; |
// if we're shutting down don't bother sending anything back |
// to the remote port because the client is gone |
if (worker->shutdown) return noErr; |
switch (theMessage) { |
case movieProgressOpen: |
sprintf(message, "open"); |
break; |
case movieProgressUpdatePercent: |
sprintf(message, "%% complete: %ld\n", Fix2Long(FixMul(thePercentDone, Long2Fix(100)))); |
break; |
case movieProgressClose: |
sprintf(message, "close"); |
break; |
} |
data = CFDataCreate(NULL, (UInt8 *)message, strlen(message)+1); |
CFMessagePortSendRequest(worker->remotePort, 0, data, 1, 1, NULL /*kCFRunLoopDefaultMode*/, &ignore); |
CFRelease(data); |
return noErr; |
} |
OSErr DoExport(WorkerRequestRef request) |
{ |
Movie sourceMovie = 0; |
Handle sourceDataRef = NULL, |
destDataRef = NULL; |
OSType sourceDataRefType, |
destDataRefType; |
MovieProgressUPP theMovieProgressUPP = NULL; |
ComponentInstance ci = 0; |
QTAtomContainer theSettingsAtomContainer = 0; |
short resId = movieInDataForkResID; |
OSErr err = paramErr; |
CFDataRef data = request->data; |
if (NULL == data) goto bail; |
// myMovieExportData should now contain the needed export data |
MovieExportData *myMovieExportData = (MovieExportDataRef)CFDataGetBytePtr(data); |
if (NULL == myMovieExportData) goto bail; |
// create data references from the FSRefs |
err = QTNewDataReferenceFromFSRef(&myMovieExportData->sourceRef, 0, &sourceDataRef, &sourceDataRefType); |
if (err) goto bail; |
err = QTNewDataReferenceFromFSRef(&myMovieExportData->destRef, 0, &destDataRef, &destDataRefType); |
if (err) goto bail; |
// open the source movie |
err = NewMovieFromDataRef(&sourceMovie, newMovieActive, &resId, sourceDataRef, sourceDataRefType); |
if (err) goto bail; |
// set the movie progress proc - used to send update status to the client |
theMovieProgressUPP = NewMovieProgressUPP(myMovieProgressProc); |
if (NULL == theMovieProgressUPP) goto bail; |
//SetMovieProgressProc(sourceMovie, theMovieProgressUPP, (long)request->worker->remotePort); |
SetMovieProgressProc(sourceMovie, theMovieProgressUPP, (long)request->worker); |
// open the movie export component we want |
err = OpenADefaultComponent(myMovieExportData->componentType, myMovieExportData->componentSubType, &ci); |
if (err) goto bail; |
// turn the settings into the atom container |
PtrToHand(myMovieExportData->exportSettings, &theSettingsAtomContainer, myMovieExportData->exportSettingsSize); |
// configre the movie export component |
err = MovieExportSetSettingsFromAtomContainer(ci, theSettingsAtomContainer); |
if (err) goto bail; |
// export the movie |
err = ConvertMovieToDataRef(sourceMovie, 0, destDataRef, destDataRefType, |
kQTFileTypeMovie, FOUR_CHAR_CODE('TVOD'), |
createMovieFileDeleteCurFile | createMovieFileDontCreateResFile, |
ci); |
bail: |
request->actionFinished = true; |
if (ci) CloseComponent(ci); |
if (sourceDataRef) DisposeHandle(sourceDataRef); |
if (destDataRef) DisposeHandle(destDataRef); |
if (theSettingsAtomContainer) QTDisposeAtomContainer(theSettingsAtomContainer); |
if (sourceMovie) DisposeMovie(sourceMovie); |
if (theMovieProgressUPP) DisposeMovieProgressUPP(theMovieProgressUPP); |
return err; |
} |
void DoCancelExport(WorkerRequestRef request) |
{ |
// myMovieExportData will contain our needed export data including our settings |
MovieExportData *myMovieExportData = (MovieExportDataRef)CFDataGetBytePtr((CFDataRef)request->data); |
if (NULL == myMovieExportData) return; |
FSDeleteObject(&myMovieExportData->destRef); |
} |
CFDataRef myMessagePortListenerProc(CFMessagePortRef local, SInt32 msgid, CFDataRef inData, void *info) |
{ |
WorkerThreadRef worker = NULL; |
WorkerRequestRef request = NULL; |
CFDataRef replyData = NULL; |
char reply[255]; |
UInt8 requestType; |
if (!inData || !info) return NULL; |
worker = (WorkerThreadRef)info; |
pthread_mutex_lock(&mutex); |
// what type of request is this? |
CFDataGetBytes(inData, CFRangeMake(0, sizeof(UInt8)), &requestType); |
switch(requestType) { |
case kExportRequest: |
// create and configure the request |
if (memFullErr == CreateWorkerRequest(worker, &request)) { |
fprintf(stderr, "MovieExportServer: CreateWorkerRequest memFullErr\n"); |
break; |
} |
// make a copy of the export data |
request->data = CFDataCreateCopy(kCFAllocatorDefault, inData); |
Enqueue((QElemPtr)&request->nextRequest, &request->worker->requestQueue); |
sprintf(reply, "Reply: Reqest %ld queued.\n\n", request->worker->numberOfActiveRequests); |
replyData = CFDataCreate(kCFAllocatorDefault, (UInt8 *)reply, strlen(reply)+1); |
break; |
case kCancelRequest: |
{ |
// walk the pending request queue and cancel everything |
// requestElem will be NULL, or the address of a nextRequest field in a WorkerRequest |
QElemPtr requestElem = worker->requestQueue.qHead; |
if (requestElem) { |
do { |
request = (WorkerRequestRef)((long)requestElem - offsetof(WorkerRequest, nextRequest)); |
if (request->worker != worker) { |
fprintf(stderr, "MovieExportServer: bad request in queue!\n"); |
} else { |
TestAndSet(0, &request->wasCancelledOrStarted); |
} |
} while (requestElem = request->nextRequest); |
sprintf(reply, "Reply: All pending requests canceled.\n\n"); |
} else { |
sprintf(reply, "Reply: No pending requests.\n\n"); |
} |
replyData = CFDataCreate(kCFAllocatorDefault, (UInt8 *)reply, strlen(reply)+1); |
break; |
} |
case kShutdownRequest: |
TestAndSet(0, &worker->shutdown); |
fprintf(stderr, "MovieExportServer: will shutdown\n"); |
break; |
default: |
break; |
} |
pthread_cond_signal(&condition); |
pthread_mutex_unlock(&mutex); |
return replyData; // inData and replyData will be released for us after callback returns |
} |
void *MessagePortListener(void *inWorkerThreadRef) |
{ |
CFMessagePortContext messagePortContext = { 0 }; |
WorkerThreadRef worker = (WorkerThreadRef)inWorkerThreadRef; |
messagePortContext.info = (void *)worker; |
worker->localPort = CFMessagePortCreateLocal(kCFAllocatorDefault, CFSTR("TheMessagePort"), myMessagePortListenerProc, &messagePortContext, false); |
worker->remotePort = CFMessagePortCreateRemote(kCFAllocatorDefault, CFSTR("TheProgressPort")); |
worker->runLoopSource = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, worker->localPort, 0); |
worker->runLoopRef = CFRunLoopGetCurrent(); |
CFRunLoopAddSource(worker->runLoopRef, worker->runLoopSource, kCFRunLoopDefaultMode); |
CFRunLoopRun(); |
sleep(1); |
CFMessagePortInvalidate(worker->localPort); |
CFRunLoopRemoveSource(worker->runLoopRef, worker->runLoopSource, kCFRunLoopDefaultMode); |
CFRelease(worker->localPort); |
CFRelease(worker->remotePort); |
CFRelease(worker->runLoopSource); |
pthread_exit(NULL); |
} |
int main (int argc, const char * argv[]) |
{ |
OSErr err; |
WorkerThreadRef worker; |
EnterMovies(); |
fprintf(stderr, "MovieExportServer: started\n"); |
// allocate memory for the worker thread data |
worker = calloc(1, sizeof(WorkerThread)); |
if (!worker) return memFullErr; |
// create the actual worker thread to listen for messages from the client |
err = pthread_create(&worker->workerThread, NULL, MessagePortListener, worker); |
if (err) return err; |
// initialize mutex and condition variable objects |
pthread_mutex_init(&mutex, NULL); |
pthread_cond_init(&condition, NULL); |
pthread_mutex_lock(&mutex); |
while (1) { |
// when there is work to be done, handle all queued requests then wait again |
// requestElem will be NULL, or the address of a nextRequest field in a WorkerRequest |
QElemPtr requestElem = worker->requestQueue.qHead; |
if (requestElem) { |
WorkerRequestRef request = (WorkerRequestRef)((long)requestElem - offsetof(WorkerRequest, nextRequest)); |
Dequeue(requestElem, &worker->requestQueue); |
if (request->worker != worker) { |
fprintf(stderr, "MovieExportServer: bad request in queue!\n"); |
continue; |
} |
if (TestAndSet(0, &request->wasCancelledOrStarted)) { |
// request was cancelled |
DoCancelExport(request); |
ReleaseWorkerRequest(request); |
} else { |
// request started, so run it |
err = DoExport(request); |
ReleaseWorkerRequest(request); |
if (err) fprintf(stderr, "MovieExportServer: DoExport error %d\n", err); |
} |
} else if (worker->shutdown) { |
// shutting down |
CFRunLoopStop(worker->runLoopRef); |
pthread_join(worker->workerThread, NULL); |
break; |
} else { |
// hang out and wait |
pthread_cond_wait(&condition, &mutex); |
pthread_mutex_unlock(&mutex); |
} |
} |
free(worker); |
fprintf(stderr, "MovieExportServer: shutdown\n"); |
return 0; |
} |
Copyright © 2005 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2005-07-22