1/*
2 * ExportCommand.c --
3 *
4 *		Process the "export" subcommand.
5 *
6 * Copyright (c) 2000-2003  Mats Bengtsson
7 *
8 * $Id: ExportCommand.c,v 1.2 2005/06/02 12:39:29 matben Exp $
9 */
10
11#include "MoviePlayer.h"
12
13
14/*
15 * For dispatching export options. Order!!!
16 */
17
18static char *allExportOptions[] = {
19	"-codecsubtype", "-dialog", "-file",
20	"-mediatype", "-onlytrack", "-readsettingsfromfile",
21	"-restrictexport", "-savesettingstofile", "-uselatestsettings",
22    (char *) NULL
23};
24
25enum {
26    kExportOptionCodecSubType                   = 0L,
27    kExportOptionDialog,
28    kExportOptionFile,
29    kExportOptionMediaType,
30    kExportOptionOnlyTrack,
31    kExportOptionReadSettingsFromFile,
32    kExportOptionRestrictExport,
33    kExportOptionSaveSettingsToFile,
34    kExportOptionUseLatestSettings
35};
36
37/*
38 * Hash table for mapping codec to QTAtomContainer that contains latest settings
39 * for an export component.
40 */
41
42static Tfp_ArrayType    *exportComponentSettingsArr = NULL;
43static OSType           exportComponentSettingsSubType = MovieFileType;
44static OSType           exportComponentSettingsManufacturer = 0L;
45
46
47static void     	DeleteExportComponentSettings( ClientData clientData );
48
49
50
51/*
52 *----------------------------------------------------------------------
53 *
54 * ProcessExportCmd --
55 *
56 *		Process the "export" command
57 *
58 * Results:
59 *  	Normal TCL results
60 *
61 * Side effects:
62 *		Exports, with or without dialog.
63 *
64 *----------------------------------------------------------------------
65 */
66
67int
68ProcessExportCmd( Tcl_Interp *interp,
69		Movie theMovie,
70		int objc,
71		Tcl_Obj *CONST objv[] )
72{
73    int             iarg;
74    int             optIndex;
75    int             showDialog = true;
76    int             useLatestSettings = false;
77    int             haveFile = false;
78    int             haveCodecSubType = false;
79    int             haveMediaType = false;
80    int				booleanInt;
81    long            flags = 0L;
82    long            trackID;
83    char            codecKey[9];
84    char            pathname[256];
85    char            *charPtr;
86    unsigned long   lType;
87	Tcl_Obj 		*resultObjPtr;
88    Tcl_Obj			*saveSettingsPathPtr = NULL;
89    Tcl_Obj			*readSettingsPathPtr = NULL;
90	Tcl_DString     ds;
91    Boolean         cancelled = false;
92    FSSpec          theFSSpec;
93    Track           track = NULL;
94	Component               exportComp;
95    MovieExportComponent    exporter = NULL;
96    ComponentDescription    compDesc;
97    ComponentDescription    compInfo;
98    QTAtomContainer settingsAtomContainer = NULL;
99    int             result = TCL_OK;
100    OSErr           err = noErr;
101    ComponentResult compErr = noErr;
102
103    /*
104     * Initial settings for the exporter component description.
105     * 0 entries mean default values.
106     */
107
108    compDesc.componentType = MovieExportType;
109    compDesc.componentSubType = MovieFileType;
110    compDesc.componentManufacturer = 0L;
111    compDesc.componentFlags = 0L;
112    compDesc.componentFlagsMask = 0L;
113
114    flags = createMovieFileDeleteCurFile | movieToFileOnlyExport;
115
116    strcpy( pathname, "0Untitled.mov" );
117    BlockMove( pathname, theFSSpec.name, strlen(pathname) );
118    theFSSpec.name[0] = strlen(pathname);
119
120    for (iarg = 0; iarg < objc; iarg += 2) {
121
122    	if (Tcl_GetIndexFromObj( interp, objv[iarg], allExportOptions,
123    	        "export option", TCL_EXACT, &optIndex ) != TCL_OK ) {
124    	    result = TCL_ERROR;
125    	    goto bail;
126    	}
127    	if (iarg + 1 == objc) {
128    		resultObjPtr = Tcl_GetObjResult( interp );
129    		Tcl_AppendStringsToObj( resultObjPtr, "value for \"",
130    				Tcl_GetString(objv[iarg]), "\"missing", (char *) NULL );
131    	    result = TCL_ERROR;
132    	    goto bail;
133    	}
134
135        /*
136         * Dispatch the export option to the right branch.
137         */
138
139        switch (optIndex) {
140
141            case kExportOptionCodecSubType: {
142           	    charPtr = Tcl_UtfToExternalDString( gQTTclTranslationEncoding,
143           	    		Tcl_GetString( objv[iarg+1] ), -1, &ds);
144                if (Tcl_DStringLength( &ds ) != 4) {
145          			Tcl_SetObjResult( interp, Tcl_NewStringObj(
146          					"-codecsubtype must be four characters", -1 ) );
147            	    result = TCL_ERROR;
148        	        goto bail;
149                }
150    			memcpy( &lType, charPtr, 4 );
151                compDesc.componentSubType = EndianU32_NtoB( lType );
152                haveCodecSubType = true;
153                break;
154            }
155
156            case kExportOptionDialog: {
157				if (Tcl_GetBooleanFromObj( interp, objv[iarg+1],
158						&showDialog ) != TCL_OK) {
159					Tcl_AddErrorInfo( interp,
160							"\n	(processing -dialog option)" );
161					result = TCL_ERROR;
162					goto bail;
163				}
164                break;
165            }
166
167            case kExportOptionFile: {
168        		err = QTTclNativePathNameToFSSpec( interp,
169        				Tcl_GetString( objv[iarg+1] ), &theFSSpec );
170          		if ((err != fnfErr) && (err != noErr)) {
171          			Tcl_SetObjResult( interp, Tcl_NewStringObj(
172          					"failed making FSSpec from filename", -1 ) );
173        	        result = TCL_ERROR;
174            	    goto bail;
175          		}
176                flags |= movieFileSpecValid;
177                haveFile = true;
178                break;
179            }
180
181            case kExportOptionReadSettingsFromFile: {
182                readSettingsPathPtr = objv[iarg+1];
183                break;
184            }
185
186            case kExportOptionMediaType: {
187           	    charPtr = Tcl_UtfToExternalDString( gQTTclTranslationEncoding,
188           	    		Tcl_GetString( objv[iarg+1] ), -1, &ds);
189                if (Tcl_DStringLength( &ds ) != 4) {
190          			Tcl_SetObjResult( interp, Tcl_NewStringObj(
191          					"-mediatype must be four characters", -1 ) );
192            	    result = TCL_ERROR;
193        	        goto bail;
194                }
195    			memcpy( &lType, charPtr, 4 );
196                compDesc.componentManufacturer = EndianU32_NtoB( lType );
197                haveMediaType = true;
198           	    Tcl_DStringFree(&ds);
199                break;
200            }
201
202            case kExportOptionOnlyTrack: {
203				if (Tcl_GetLongFromObj( interp, objv[iarg+1], &trackID ) != TCL_OK) {
204					Tcl_AddErrorInfo( interp, "\n	(processing -onlytrack option)" );
205					result = TCL_ERROR;
206					goto bail;
207				}
208        		track = GetMovieTrack( theMovie, trackID );
209       			if (track == NULL) {
210                    CheckAndSetErrorResult( interp, noErr );
211            	    result = TCL_ERROR;
212        	        goto bail;
213    			}
214                break;
215            }
216
217            case kExportOptionRestrictExport: {
218				if (Tcl_GetBooleanFromObj( interp, objv[iarg+1], &booleanInt )
219							!= TCL_OK) {
220					Tcl_AddErrorInfo( interp,
221							"\n	(processing -restrictexport option)" );
222					result = TCL_ERROR;
223					goto bail;
224				}
225                if (booleanInt == 0) {
226                    flags &= ~movieToFileOnlyExport;
227                }
228                break;
229            }
230
231            case kExportOptionSaveSettingsToFile: {
232                saveSettingsPathPtr = objv[iarg+1];
233                break;
234            }
235
236            case kExportOptionUseLatestSettings: {
237				if (Tcl_GetBooleanFromObj( interp, objv[iarg+1], &useLatestSettings )
238							!= TCL_OK) {
239					Tcl_AddErrorInfo( interp,
240							"\n	(processing -uselatestsettings option)" );
241					result = TCL_ERROR;
242					goto bail;
243				}
244                break;
245            }
246        }
247    }
248
249    /*
250     * Error checking.
251     */
252
253    if (!showDialog && !haveFile) {
254        Tcl_SetObjResult( interp, Tcl_NewStringObj(
255        		"must have either -file or -dialog", -1 ) );
256    	result = TCL_ERROR;
257    	goto bail;
258    }
259    if (haveMediaType && !haveCodecSubType) {
260        Tcl_SetObjResult( interp, Tcl_NewStringObj(
261        		"must have -codecsubtype together with -mediatype", -1 ) );
262       	result = TCL_ERROR;
263       	goto bail;
264    }
265    if (!haveFile && (haveCodecSubType || haveMediaType)) {
266        Tcl_SetObjResult( interp, Tcl_NewStringObj(
267        		"must have -file option here", -1 ) );
268       	result = TCL_ERROR;
269       	goto bail;
270    }
271    if (readSettingsPathPtr && useLatestSettings) {
272        Tcl_SetObjResult( interp, Tcl_NewStringObj(
273        		"can't combine -uselatestsettings and -readsettingsfromfile", -1 ) );
274       	result = TCL_ERROR;
275       	goto bail;
276    }
277    if (readSettingsPathPtr && !( haveCodecSubType && haveMediaType )) {
278        Tcl_SetObjResult( interp, Tcl_NewStringObj(
279        		"must specify -codecsubtype and -mediatype to use -readsettingsfromfile", -1 ) );
280       	result = TCL_ERROR;
281       	goto bail;
282    }
283    if (saveSettingsPathPtr && !( haveCodecSubType && haveMediaType )) {
284        Tcl_SetObjResult( interp, Tcl_NewStringObj(
285        		"must specify -codecsubtype and -mediatype to use -savesettingstofile", -1 ) );
286       	result = TCL_ERROR;
287       	goto bail;
288    }
289
290    if (useLatestSettings) {
291        compDesc.componentSubType = exportComponentSettingsSubType;
292        compDesc.componentManufacturer = exportComponentSettingsManufacturer;
293    }
294    if (exportComponentSettingsArr == NULL) {
295        exportComponentSettingsArr = Tfp_ArrayInit( DeleteExportComponentSettings );
296    }
297
298    if (haveCodecSubType || haveMediaType) {
299        exportComp = FindNextComponent( NULL, &compDesc );
300        exporter = OpenComponent( exportComp );
301        if (exporter == NULL) {
302            Tcl_SetObjResult( interp, Tcl_NewStringObj(
303            		"didn't find an export component", -1 ) );
304    	    result = TCL_ERROR;
305    	    goto bail;
306        }
307
308       /*
309        * Form codecKey which is the entry to the hash table mapping to any
310        * previously stored settings for this component.
311        * The codecKey is a string of length 8: SubType + Manufacturer ("MooVappl").
312        */
313
314        if (useLatestSettings) {
315			err = GetComponentInfo( exportComp, &compInfo, NULL, NULL, NULL );
316			if (err == noErr) {
317                memset( codecKey, 0, 9 );
318                memcpy( codecKey, &compInfo.componentSubType, 4 );
319                memcpy( codecKey + 4, &compInfo.componentManufacturer, 4 );
320
321                if (Tfp_ArrayGet( exportComponentSettingsArr, codecKey,
322                        (ClientData *) &settingsAtomContainer )) {
323                    compErr = MovieExportSetSettingsFromAtomContainer( exporter,
324                            settingsAtomContainer );
325                    if (compErr != noErr) {
326                        CheckAndSetErrorResult( interp, compErr );
327            	        result = TCL_ERROR;
328            	        goto bail;
329                    }
330                }
331            }
332        } else if (readSettingsPathPtr) {
333        	Tcl_Channel     readChannel = NULL;
334        	Tcl_Obj         *readObj = Tcl_NewObj();
335        	int             nread;
336        	int             len;
337            unsigned char   *contentPtr;
338
339            readChannel = Tcl_FSOpenFileChannel( interp, readSettingsPathPtr, "r", 0666 );
340            if (readChannel == NULL) {
341                result = TCL_ERROR;
342                goto bail;
343            }
344            result = Tcl_SetChannelOption( interp, readChannel, "-translation", "binary" );
345            if (result != TCL_OK) {
346                goto bail;
347            }
348            nread = Tcl_ReadChars( readChannel, readObj, 99999, 0 );
349            if (nread == -1) {
350                Tcl_SetObjResult( interp,
351                		Tcl_NewStringObj( Tcl_ErrnoMsg( Tcl_GetErrno() ), -1 ) );
352      	        result = TCL_ERROR;
353  	            goto bail;
354            }
355            contentPtr = Tcl_GetByteArrayFromObj( readObj, &len );
356
357            /* Copy the file content to a handle which is our AtomContainer. */
358
359            err = PtrToHand( contentPtr, &settingsAtomContainer, len );
360            if (err != noErr) {
361                CheckAndSetErrorResult( interp, err );
362      	        result = TCL_ERROR;
363  	            goto bail;
364            }
365            compErr = MovieExportSetSettingsFromAtomContainer( exporter,
366            		settingsAtomContainer );
367            Tcl_Close( interp, readChannel );
368            Tcl_DecrRefCount( readObj );
369            if (compErr != noErr) {
370                CheckAndSetErrorResult( interp, compErr );
371                result = TCL_ERROR;
372                goto bail;
373            }
374        }
375    }
376    if (showDialog) {
377        if (haveCodecSubType || haveMediaType) {
378
379            /*
380             * Check first that the export component has a dialog. Error?
381             */
382
383            if (ComponentFunctionImplemented( exporter,
384                    kMovieExportDoUserDialogSelect ) == false) {
385                Tcl_SetObjResult( interp, Tcl_NewStringObj(
386                		"the selected export component has no dialog", -1 ) );
387            	result = TCL_ERROR;
388        	    goto bail;
389            }
390            compErr = MovieExportDoUserDialog( exporter, theMovie, NULL, 0,
391                    GetMovieDuration(theMovie), &cancelled );
392            if (compErr != noErr) {
393                CheckAndSetErrorResult( interp, compErr );
394        	    result = TCL_ERROR;
395        	    goto bail;
396            }
397            if (cancelled) {
398
399                /* Shall return an empty string. */
400        	    goto bail;
401            }
402
403			err = GetComponentInfo( exportComp, &compInfo, NULL, NULL, NULL );
404			if (err == noErr) {
405
406			    /*
407			     * Store the just selected settings for this specific component
408			     * for future use.
409			     */
410
411                memset( codecKey, 0, 9 );
412                memcpy( codecKey, &compInfo.componentSubType, 4 );
413                memcpy( codecKey + 4, &compInfo.componentManufacturer, 4 );
414
415                compErr = MovieExportGetSettingsAsAtomContainer( exporter, &settingsAtomContainer );
416                if (compErr != noErr) {
417                    CheckAndSetErrorResult( interp, compErr );
418            	    result = TCL_ERROR;
419            	    goto bail;
420                }
421                Tfp_ArraySet( exportComponentSettingsArr, codecKey,
422                        (ClientData) settingsAtomContainer );
423
424                if (saveSettingsPathPtr) {
425                    Tcl_Channel     saveChannel = NULL;
426                    int             size;
427                    int             written;
428
429                    saveChannel = Tcl_FSOpenFileChannel( interp, saveSettingsPathPtr, "w", 0666 );
430                    if (saveChannel == NULL) {
431                	    result = TCL_ERROR;
432                	    goto bail;
433                    }
434                    result = Tcl_SetChannelOption( interp, saveChannel,
435                            "-translation", "binary" );
436                    if (result != TCL_OK) {
437                	    goto bail;
438                    }
439                    size = GetHandleSize( settingsAtomContainer );
440                    if (size == 0) {
441                        Tcl_SetObjResult( interp, Tcl_NewStringObj(
442                        		"GetHandleSize failed", -1 ) );
443                	    result = TCL_ERROR;
444            	        goto bail;
445                    }
446                    HLock( settingsAtomContainer );
447                    written = Tcl_Write( saveChannel, *settingsAtomContainer, size );
448                    if (written == -1) {
449                        Tcl_SetObjResult( interp, Tcl_NewStringObj(
450                        		Tcl_ErrnoMsg( Tcl_GetErrno() ), -1 ) );
451                	    result = TCL_ERROR;
452            	        goto bail;
453                    }
454                    Tcl_Close( interp, saveChannel );
455                    HUnlock( settingsAtomContainer );
456                }
457            }
458        } else {
459            flags |= showUserSettingsDialog;
460        }
461    }
462
463    /* Export the movie into a file. */
464
465    err = ConvertMovieToFile(
466            theMovie,     	/* The movie to convert. */
467            track,        	/* NULL is all tracks in the movie. */
468            &theFSSpec,    	/* The output file. */
469            0L,         	/* The output file type. */
470            ksigMoviePlayer,/* The output file creator. */
471            smSystemScript,	/* The script. */
472            NULL,         	/* No resource ID to be returned. */
473            flags,        	/* Export flags. */
474            exporter );    	/* Specific export component. NULL means all. */
475	if (err == userCanceledErr) {
476        goto bail;
477	}
478    if (noErr != CheckAndSetErrorResult( interp, err )) {
479        result = TCL_ERROR;
480        goto bail;
481    }
482
483    /*
484     * When 'ConvertMovieToFile' exits the FSSpec contains the picked file.
485     */
486
487    result = QTTclFSSpecToNativePathName( interp, pathname, &theFSSpec );
488
489bail:
490    if (exporter != NULL) {
491        CloseComponent( exporter );
492    }
493    return result;
494}
495
496
497static void
498DeleteExportComponentSettings( ClientData clientData )
499{
500    Handle      atomContainer = (QTAtomContainer) clientData;
501
502    if (atomContainer != NULL) {
503        QTDisposeAtomContainer( atomContainer );
504    }
505}
506
507void
508ExportComponentSettingsFree( void )
509{
510    if (exportComponentSettingsArr != NULL) {
511        Tfp_ArrayDestroy( exportComponentSettingsArr );
512    }
513}
514
515/*---------------------------------------------------------------------------*/
516