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