1/* 2 * Broadcast.c -- 3 * 4 * This module implements a "broadcast" widget that is object 5 * based. It is part of the QuickTimeTcl package. 6 * It provides an interface to the (ill docoumented) QuickTime broadcaster 7 * API's. This includes the Presentation and Sourcer classes of APIs. 8 * 9 * Copyright (c) 2003 Mats Bengtsson 10 * 11 * $Id: Broadcast.c,v 1.8 2008/02/15 15:23:05 matben Exp $ 12 */ 13 14#include "Broadcast.h" 15 16/* 17 * We need a (single) linked list for our broadcasters. 18 */ 19 20static MovieBroadcastList *gMovieBroadcastListPtr = NULL; 21 22static GWorldPtr gOffscreenGWorldPtr = NULL; 23 24#if TARGET_API_MAC_CARBON 25EventLoopTimerRef gCarbonPresentationTimerRef = NULL; 26const EventTime kCarbonPresentationTimerSecs = kEventDurationSecond / 10; 27//long kMinimumIdleDurationInMillis = 20; 28#endif 29 30static char *broadcastCommands[] = { 31 "audioconfigure", 32 "cget", 33 "configure", 34 "export", 35 "getsettings", 36 "getstatistics", 37 "pause", 38 "picture", 39 "preview", 40 "settingsdialog", 41 "skipto", 42 "sourcer", 43 "start", 44 "stop", 45 "stream", 46 "videoconfigure", 47 (char *) NULL 48}; 49 50enum { 51 kBcastCmdAudioConfigure = 0L, 52 kBcastCmdCget, 53 kBcastCmdConfigure, 54 kBcastCmdExport, 55 kBcastCmdGetSettings, 56 kBcastCmdGetStatistics, 57 kBcastCmdPause, 58 kBcastCmdPicture, 59 kBcastCmdPreview, 60 kBcastCmdSettingsDialog, 61 kBcastCmdSkipTo, 62 kBcastCmdSourcer, 63 kBcastCmdStart, 64 kBcastCmdStop, 65 kBcastCmdStream, 66 kBcastCmdVideoConfigure 67}; 68 69static char *sourcerCommands[] = { 70 "add", "info", "remove", 71 (char *) NULL 72}; 73 74enum { 75 kSourcerCmdAdd = 0L, 76 kSourcerCmdInfo, 77 kSourcerCmdRemove 78}; 79 80static char *videoConfigOptions[] = { 81 "-brightness", "-contrast", "-hue", 82 "-saturation", "-sharpness", 83 (char *) NULL 84}; 85 86enum { 87 kVideoConfigOptionBrightness = 0L, 88 kVideoConfigOptionContrast, 89 kVideoConfigOptionHue, 90 kVideoConfigOptionSaturation, 91 kVideoConfigOptionSharpness 92}; 93 94static char *audioConfigOptions[] = { 95 "-autogain", "-gain", 96 (char *) NULL 97}; 98 99enum { 100 kAudioConfigOptionAutoGainOnOff = 0L, 101 kAudioConfigOptionGain 102}; 103 104/* 105 * Strings for the partial parsing of SDP data. 106 */ 107 108#define kSDPAudioDefString "m=audio" 109#define kSDPVideoDefString "m=video" 110 111/* 112 * Information used for parsing configuration options. 113 * Mask bits for options changed. 114 */ 115 116enum { 117 BCAST_CONF_NEWGWORLD = (1L << 0) 118}; 119 120/* 121 * The following table defines the legal values for the -size option. 122 */ 123 124static CONST char *widgetSizeST[] = { 125 "quarter", "half", "full", (char *) NULL 126}; 127#define BCAST_WIDGET_SIZE_QUARTER 0 128#define BCAST_WIDGET_SIZE_HALF 1 129#define BCAST_WIDGET_SIZE_FULL 2 130 131/* 132 * Information used for objv parsing. 133 */ 134 135static Tk_OptionSpec optionSpecs[] = { 136 {TK_OPTION_STRING, "-command", "command", "Command", 137 NULL, -1, Tk_Offset(MovieBroadcast, command), TK_OPTION_NULL_OK, 138 (ClientData) NULL, 0}, 139 {TK_OPTION_PIXELS, "-height", "height", "Height", 140 "0", -1, Tk_Offset(MovieBroadcast, height), 0, 141 (ClientData) NULL, BCAST_CONF_NEWGWORLD}, 142 {TK_OPTION_STRING_TABLE, "-size", "size", "Size", 143 "half", -1, Tk_Offset(MovieBroadcast, indSize), 0, 144 (ClientData) widgetSizeST, BCAST_CONF_NEWGWORLD}, 145 {TK_OPTION_PIXELS, "-width", "width", "Width", 146 "0", -1, Tk_Offset(MovieBroadcast, width), 0, 147 (ClientData) NULL, BCAST_CONF_NEWGWORLD}, 148 {TK_OPTION_END} 149}; 150 151/* 152 * Forward declarations for procedures defined later in this file: 153 */ 154 155static void BroadcastDeletedProc( ClientData clientData ); 156static int BroadcastConfigure( Tcl_Interp *interp, 157 MovieBroadcast *bcastPtr, int objc, 158 Tcl_Obj *CONST objv[] ); 159static void BroadcastWorldChanged( ClientData clientData ); 160static int BroadcastComputeGeometry( MovieBroadcast *bcastPtr, 161 int *width, int *height ); 162static void DisplayBroadcast( ClientData clientData ); 163static void DrawBluescreen( MovieBroadcast *bcastPtr ); 164static void DestroyBroadcast( ClientData clientData ); 165static void BroadcastEventProc( ClientData clientData, 166 XEvent *eventPtr ); 167static int BroadcastWidgetObjCmd( ClientData clientData, 168 Tcl_Interp *, int objc, Tcl_Obj * CONST objv[] ); 169static ComponentResult PresentationNotification( ComponentResult inErr, 170 OSType inNotificationType, 171 void *inNotificationParams, void *inRefCon ); 172static ComponentResult HandleErrorNotification( MovieBroadcast *bcastPtr, 173 const char *inNotificationTypeString, 174 ComponentResult inErr, 175 QTSErrorParams *inErrorParams ); 176static int GetSDPFromFile( MovieBroadcast *bcastPtr, 177 Tcl_Obj *fileNamePtr ); 178static int GetSDPFromURL( MovieBroadcast *bcastPtr, 179 Tcl_Obj *urlPtr ); 180static int GetSDPFromData( MovieBroadcast *bcastPtr, 181 Tcl_Obj *sdpListPtr ); 182static void AddToBroadcastList( MovieBroadcast *bcastPtr ); 183static void RemoveFromBroadcastList( MovieBroadcast *bcastPtr ); 184static void IdleAllPresentations( void ); 185static void IdleSourcersForPresentation( MovieBroadcast *bcastPtr ); 186static int PresentationExportToFile( MovieBroadcast *bcastPtr, 187 int objc, Tcl_Obj *CONST objv[] ); 188static int SourcerObjCmd( Tcl_Interp *interp, 189 MovieBroadcast *movPtr, 190 int objc, Tcl_Obj *CONST objv[] ); 191static int StreamObjCmd( Tcl_Interp *interp, 192 MovieBroadcast *bcastPtr, 193 int objc, Tcl_Obj *CONST objv[] ); 194static void GetAllStreamMediaTypes( QTSPresentation presentation, 195 int *haveAudio, int *haveVideo, int *haveText ); 196static int SourcerGetInfo( Tcl_Interp *interp, 197 MovieBroadcast *bcastPtr, QTSStream inStream, 198 int objc, Tcl_Obj *CONST objv[] ); 199static OSErr GetSourcerType( QTSSourcer sourcer, OSType *typePtr ); 200static OSErr GetSeqGrabberFullInputRect( QTSPresentation presentation, 201 int *fullWidth, int *fullHeight ); 202static int PictureObjCmd( Tcl_Interp *interp, 203 MovieBroadcast *bcastPtr, 204 int objc, Tcl_Obj *CONST objv[] ); 205static int SetupTrackSourcerForStream( MovieBroadcast *bcastPtr, 206 QTSStream inStream ); 207static void PresentationFree( MovieBroadcast *bcastPtr ); 208static void SourcerFree( BcastSourcerInfo *sourcerPtr ); 209static pascal ComponentResult MovieSourcingCallbackProc( 210 ComponentResult inErr, OSType inSelector, 211 void *ioParams, void *inRefCon ); 212void QTSDisposeMediaParams( QTSMediaParams *inMediaParams ); 213static pascal Boolean MyModalFilterDialogProc( DialogPtr dialogPtr, 214 EventRecord * event, SInt16 *itemHit, long *refCon ); 215 216#if TARGET_API_MAC_CARBON 217static OSStatus InstallBroadcastIdlingEventLoopTimer( void ); 218static pascal void PresentationCarbonTimer( EventLoopTimerRef theTimer, 219 void *userData ); 220#endif 221 222/* 223 *-------------------------------------------------------------- 224 * 225 * BroadcastCmd -- 226 * 227 * This procedure is invoked to process the "qtbroadcast" Tcl 228 * command. It creates a new "qtbroadcast" widget. 229 * 230 * Results: 231 * A standard Tcl result. 232 * 233 * Side effects: 234 * A new widget is created and configured. 235 * 236 *-------------------------------------------------------------- 237 */ 238 239int 240BroadcastObjCmd( 241 ClientData clientData, /* NULL. */ 242 Tcl_Interp *interp, /* Current interpreter. */ 243 int objc, /* Number of arguments. */ 244 Tcl_Obj *CONST objv[] ) /* Argument objects. */ 245{ 246 MovieBroadcast *bcastPtr; 247 long eventFlags = 0L; 248 Tk_Window tkwin; 249 Tk_OptionTable optionTable; 250 static int isInitialized = false; 251 OSErr err = noErr; 252 253 if (objc < 3) { 254 Tcl_WrongNumArgs( interp, 1, objv, "pathName sdpFile ?options?" ); 255 return TCL_ERROR; 256 } 257 258 /* 259 * We use one and the same offscreen GWorld for all movies. 260 */ 261 262 if (gOffscreenGWorldPtr == NULL) { 263 Rect aRect = { 0, 0, 10, 10 }; /* Just a dummy rect. */ 264 265 err = MySafeNewGWorld( &gOffscreenGWorldPtr, 32, &aRect, NULL, NULL, 0 ); 266 if (err != noErr) { 267 CheckAndSetErrorResult( interp, err ); 268 return TCL_ERROR; 269 } 270 } 271 272 if (!isInitialized) { 273 //err = InitializeQTS(); 274 if (err != noErr) { 275 276 return TCL_ERROR; 277 } 278 isInitialized = true; 279 280 QTTclDebugPrintf( interp, 2, 281 "FocusIn=%2d FocusOut=%2d Expose=%2d DestroyNotify=%2d UnmapNotify=%2d MapNotify=%2d ConfigureNotify=%2d", 282 FocusIn, FocusOut, Expose, DestroyNotify, UnmapNotify, MapNotify, ConfigureNotify ); 283 } 284 285 tkwin = Tk_CreateWindowFromPath( interp, Tk_MainWindow( interp ), 286 Tcl_GetStringFromObj( objv[1], NULL ), (char *) NULL ); 287 if (tkwin == NULL) { 288 return TCL_ERROR; 289 } 290 Tk_SetClass( tkwin, "QTBroadcast" ); 291 292 /* 293 * Create the option table for this widget class. If it has 294 * already been created, the refcount will get bumped and just 295 * the pointer will be returned. The refcount getting bumped 296 * does not concern us, because Tk will ensure the table is 297 * deleted when the interpreter is destroyed. 298 */ 299 300 optionTable = Tk_CreateOptionTable( interp, optionSpecs ); 301 302 /* 303 * Allocate and initialize the widget record. The memset allows 304 * us to set just the non-NULL/0 items. 305 */ 306 307 bcastPtr = (MovieBroadcast *) ckalloc( sizeof(MovieBroadcast) ); 308 memset( (void *) bcastPtr, 0, sizeof(MovieBroadcast) ); 309 310 bcastPtr->tkwin = tkwin; 311 bcastPtr->display = Tk_Display( tkwin ); 312 bcastPtr->interp = interp; 313 bcastPtr->widgetCmd = Tcl_CreateObjCommand( interp, 314 Tk_PathName( bcastPtr->tkwin ), BroadcastWidgetObjCmd, 315 (ClientData) bcastPtr, BroadcastDeletedProc ); 316 bcastPtr->optionTable = optionTable; 317 318 if (Tk_InitOptions( interp, (char *) bcastPtr, optionTable, tkwin) 319 != TCL_OK) { 320 Tk_DestroyWindow( bcastPtr->tkwin ); 321 ckfree( (char *) bcastPtr ); 322 return TCL_ERROR; 323 } 324 bcastPtr->srcWidth = 320; 325 bcastPtr->srcHeight = 240; 326 bcastPtr->command = NULL; 327 bcastPtr->grafPtr = NULL; 328 bcastPtr->presentation = kQTSInvalidPresentation; 329 330 if (GetSDPFromFile( bcastPtr, objv[2] ) != TCL_OK) { 331 goto error; 332 } 333 bcastPtr->state = kPresentationStateIdle; 334 bcastPtr->targetState = kPresentationStateIdle; 335 336 GetAllStreamMediaTypes( bcastPtr->presentation, 337 &bcastPtr->haveAudioStream, &bcastPtr->haveVideoStream, 338 &bcastPtr->haveTextStream ); 339 340 eventFlags = ExposureMask | StructureNotifyMask; 341 Tk_CreateEventHandler( bcastPtr->tkwin, eventFlags, 342 BroadcastEventProc, (ClientData) bcastPtr ); 343 344 if (BroadcastConfigure( interp, bcastPtr, objc - 3, objv + 3) != TCL_OK) { 345 goto error; 346 } 347 348 bcastPtr->flags |= NEWGWORLD; 349 BroadcastWorldChanged( (ClientData) bcastPtr ); 350 AddToBroadcastList( bcastPtr ); 351 352#if TARGET_API_MAC_CARBON 353 /* 354 * If this is the first presentation we open on Carbon, be sure to set a timer 355 * to serv it and all that may follow. 356 */ 357 358 if (gMovieBroadcastListPtr == NULL) { 359 InstallBroadcastIdlingEventLoopTimer(); 360 } 361#endif 362 363 Tcl_SetObjResult( interp, 364 Tcl_NewStringObj( Tk_PathName( bcastPtr->tkwin ), -1) ); 365 return TCL_OK; 366 367error: 368 369 Tk_DestroyWindow( bcastPtr->tkwin ); 370 return TCL_ERROR; 371} 372 373/* 374 *-------------------------------------------------------------- 375 * 376 * BroadcastWidgetObjCmd -- 377 * 378 * This procedure is invoked to process the Tcl command 379 * that corresponds to a widget managed by this module. 380 * See the user documentation for details on what it does. 381 * 382 * Results: 383 * A standard Tcl result. 384 * 385 * Side effects: 386 * See the user documentation. 387 * 388 *-------------------------------------------------------------- 389 */ 390 391static int 392BroadcastWidgetObjCmd( 393 ClientData clientData, /* Information about square widget. */ 394 Tcl_Interp *interp, /* Current interpreter. */ 395 int objc, /* Number of arguments. */ 396 Tcl_Obj *CONST objv[] ) /* Argument objects. */ 397{ 398 //MovieBroadcast *bcastPtr = (MovieBroadcast *) clientData; 399 MovieBroadcast *bcastPtr; 400 Tcl_Obj *resultObjPtr; 401 int index; 402 OSErr err = noErr; 403 int result = TCL_OK; 404 405 bcastPtr = (MovieBroadcast *) clientData; 406 407 QTTclDebugPrintf( bcastPtr->interp, 2, 408 "BroadcastWidgetObjCmd: targetState=%2d, state=%2d", 409 bcastPtr->targetState, bcastPtr->state ); 410 411 if (objc < 2) { 412 Tcl_WrongNumArgs( interp, 1, objv, "option ?arg arg...?" ); 413 return TCL_ERROR; 414 } 415 if (bcastPtr->presentation == kQTSInvalidPresentation) { 416 Tcl_SetObjResult( interp, 417 Tcl_NewStringObj( "Invalid presentation", -1) ); 418 goto error; 419 } 420 if (Tcl_GetIndexFromObj( interp, objv[1], broadcastCommands, "command", 421 0, &index ) != TCL_OK) { 422 return TCL_ERROR; 423 } 424 425 Tcl_Preserve( (ClientData) bcastPtr ); 426 427 switch (index) { 428 429 case kBcastCmdAudioConfigure: { 430 431 432 break; 433 } 434 435 case kBcastCmdCget: { 436 if (objc != 3) { 437 Tcl_WrongNumArgs( interp, 2, objv, "option" ); 438 goto error; 439 } 440 resultObjPtr = Tk_GetOptionValue( interp, (char *) bcastPtr, 441 bcastPtr->optionTable, objv[2], bcastPtr->tkwin ); 442 if (resultObjPtr == NULL) { 443 result = TCL_ERROR; 444 } else { 445 Tcl_SetObjResult( interp, resultObjPtr ); 446 } 447 break; 448 } 449 450 case kBcastCmdConfigure: { 451 resultObjPtr = NULL; 452 if (objc <= 3) { 453 resultObjPtr = Tk_GetOptionInfo( interp, (char *) bcastPtr, 454 bcastPtr->optionTable, 455 (objc == 2) ? (Tcl_Obj *) NULL : objv[2], 456 bcastPtr->tkwin ); 457 if (resultObjPtr == NULL) { 458 result = TCL_ERROR; 459 } else { 460 Tcl_SetObjResult( interp, resultObjPtr ); 461 } 462 } else { 463 464 /* 465 * Change one or more attributes. Reschedule a new display via 466 * 'BroadcastWorldChanged'. Be sure to only set the objv values by the flag. 467 * The NEWGWORLD bit in flags is set if we need a redisplay. 468 */ 469 470 result = BroadcastConfigure( interp, bcastPtr, objc - 2, objv + 2 ); 471 472 /* 473 * Only if we made a configuration that needs a redisplay. 474 */ 475 476 if ((result == TCL_OK) && (bcastPtr->flags & NEWGWORLD)) { 477 BroadcastWorldChanged( (ClientData) bcastPtr ); 478 } 479 } 480 break; 481 } 482 483 case kBcastCmdExport: { 484 if (PresentationExportToFile( bcastPtr, objc - 2, objv + 2 ) 485 != TCL_OK) { 486 goto error; 487 } 488 break; 489 } 490 491 case kBcastCmdGetSettings: { 492 char nul = 0; 493 Ptr p = NULL; 494 Handle outTextHand = NULL; 495 Tcl_DString ds; 496 497 // kQTSSettingsTextSummary 498 // kQTSSettingsTextDetails 499 err = QTSPresGetSettingsAsText( bcastPtr->presentation, 500 kQTSAllStreams, 0, kQTSSettingsTextDetails, 501 &outTextHand, NULL, NULL ); 502 if (err != noErr) { 503 504 goto error; 505 } 506 HLock( outTextHand ); 507 508 /* null-terminate the string in the handle */ 509 PtrAndHand( &nul, outTextHand, 1 ); 510 p = *outTextHand; 511 while (*p) { 512 if (*p == kReturnChar) { 513 *p = kNewlineChar; 514 } 515 p++; 516 }; 517 Tcl_ExternalToUtfDString( gQTTclTranslationEncoding, *outTextHand, 518 -1, &ds ); 519 Tcl_SetObjResult( interp, Tcl_NewStringObj( 520 Tcl_DStringValue(&ds), -1 ) ); 521 HUnlock( outTextHand ); 522 Tcl_DStringFree( &ds ); 523 break; 524 } 525 526 case kBcastCmdGetStatistics: { 527 528 529 530 break; 531 } 532 533 case kBcastCmdPause: { 534 err = QTSPresStop( bcastPtr->presentation, kQTSAllStreams, 0L ); 535 if (err != noErr) { 536 err = QTSPresPreview( bcastPtr->presentation, kQTSAllStreams, 537 NULL, kQTSNormalForwardRate, 0 ); 538 } 539 if (err != noErr) { 540 541 result = TCL_ERROR; 542 } 543 bcastPtr->targetState = kPresentationStateIdle; 544 break; 545 } 546 case kBcastCmdPicture: { 547 if (PictureObjCmd( interp, bcastPtr, objc - 2, objv + 2 ) 548 != TCL_OK) { 549 550 goto error; 551 } 552 break; 553 } 554 555 case kBcastCmdPreview: { 556 Fixed rate; 557 int doStart = true; 558 559 if (objc == 2) { 560 rate = kQTSNormalForwardRate; 561 } else if (objc == 3) { 562 if (Tcl_GetBooleanFromObj( interp, objv[2], &doStart ) 563 != TCL_OK) { 564 Tcl_AddErrorInfo( interp, 565 "\n (processing preview command)" ); 566 result = TCL_ERROR; 567 goto error; 568 } 569 if (doStart) { 570 rate = kQTSNormalForwardRate; 571 } else { 572 rate = kQTSStoppedRate; 573 } 574 } else { 575 Tcl_WrongNumArgs( interp, 2, objv, "?boolean?" ); 576 return TCL_ERROR; 577 } 578 if (doStart) { 579 if (bcastPtr->state >= kPresentationStateStartingPreview) { 580 581 /* we're already playing or trying to start */ 582 goto error; 583 } 584 bcastPtr->targetState = kPresentationStatePreviewing; 585 bcastPtr->state = kPresentationStateStartingPreview; 586 } else { 587 bcastPtr->targetState = kPresentationStateIdle; 588 } 589 err = QTSPresPreview( bcastPtr->presentation, kQTSAllStreams, NULL, 590 rate, 0 ); 591 592 break; 593 } 594 595 case kBcastCmdSettingsDialog: { 596 SInt32 flags = 0L; 597 598 err = QTSPresSettingsDialog( bcastPtr->presentation, 599 kQTSAllStreams, flags, 600 NewQTSModalFilterUPP(MyModalFilterDialogProc), bcastPtr ); 601 602 break; 603 } 604 605 case kBcastCmdSkipTo: { 606 TimeValue64 timeValue64; 607 608 if (objc < 3) { 609 Tcl_WrongNumArgs( interp, 2, objv, "?time?" ); 610 return TCL_ERROR; 611 } 612 // get TimeValue64 from string????????? 613 614 err = QTSPresSkipTo64( bcastPtr->presentation, &timeValue64 ); 615 break; 616 } 617 618 case kBcastCmdSourcer: { 619 if (objc < 3) { 620 Tcl_WrongNumArgs( interp, 2, objv, "command ?arg arg...?" ); 621 return TCL_ERROR; 622 } 623 if (SourcerObjCmd( interp, bcastPtr, objc - 2, objv + 2 ) 624 != TCL_OK) { 625 626 goto error; 627 } 628 break; 629 } 630 631 case kBcastCmdStart: { 632 if (bcastPtr->state >= kPresentationStateStarting) { 633 634 /* we're already playing or trying to start */ 635 goto error; 636 } 637 638 /* Right now, we have to preroll before we start. */ 639 bcastPtr->targetState = kPresentationStatePlaying; 640 bcastPtr->state = kPresentationStateStartingPreroll; 641 err = QTSPresPreroll( bcastPtr->presentation, kQTSAllStreams, 642 0, kQTSNormalForwardRate, 0L ); 643 if (err != noErr) { 644 645 goto error; 646 } 647 break; 648 } 649 650 case kBcastCmdStop: { 651 bcastPtr->targetState = kPresentationStateIdle; 652 if (bcastPtr->state != kPresentationStateIdle) { 653 bcastPtr->state = kPresentationStateIdle; 654 err = QTSPresStop( bcastPtr->presentation, kQTSAllStreams, 0L ); 655 if (err != noErr) { 656 657 goto error; 658 } 659 } 660 661 662 break; 663 } 664 665 case kBcastCmdStream: { 666 if (StreamObjCmd( interp, bcastPtr, objc - 2, objv + 2 ) 667 != TCL_OK) { 668 669 goto error; 670 } 671 672 break; 673 } 674 675 case kBcastCmdVideoConfigure: { 676 677 678 break; 679 } 680 } 681 Tcl_Release( (ClientData) bcastPtr ); 682 return result; 683 684error: 685 Tcl_Release( (ClientData) bcastPtr ); 686 return TCL_ERROR; 687} 688 689/* 690 *---------------------------------------------------------------------- 691 * 692 * BroadcastConfigure -- 693 * 694 * This procedure is called to process an objv/objc list in 695 * conjunction with the Tk option database to configure (or 696 * reconfigure) a square widget. 697 * 698 * Results: 699 * The return value is a standard Tcl result. If TCL_ERROR is 700 * returned, then the interp's result contains an error message. 701 * 702 * Side effects: 703 * Configuration information, such as colors, border width, 704 * etc. get set for bcastPtr; old resources get freed, 705 * if there were any. 706 * 707 *---------------------------------------------------------------------- 708 */ 709 710static int 711BroadcastConfigure( 712 Tcl_Interp *interp, /* Used for error reporting. */ 713 MovieBroadcast *bcastPtr, /* Information about widget. */ 714 int objc, 715 Tcl_Obj *CONST objv[] ) 716{ 717 int ierror; 718 int mask = 0L; 719 int width, height; 720 Tk_SavedOptions savedOptions; 721 Tcl_Obj *resultObjPtr = NULL; 722 Tcl_Obj *errorResult = NULL; 723 OSErr err = noErr; 724 725 QTTclDebugPrintf( bcastPtr->interp, 2, "BroadcastConfigure" ); 726 727 /* 728 * The following loop is potentially executed twice. During the 729 * first pass configuration options get set to their new values. 730 * If there is an error in this pass, we execute a second pass 731 * to restore all the options to their previous values. 732 * 733 * A 'continue' within this loop signals an error condition; 734 * 'break' when everything went OK. 735 */ 736 737 for (ierror = 0; ierror <= 1; ierror++) { 738 if (!ierror) { 739 /* 740 * First pass: set options to new values. 741 */ 742 743 if (Tk_SetOptions( interp, (char *) bcastPtr, bcastPtr->optionTable, 744 objc, objv, bcastPtr->tkwin, &savedOptions, &mask) 745 != TCL_OK ) { 746 continue; 747 } 748 } else { 749 /* 750 * Second pass: restore options to old values. 751 */ 752 753 errorResult = Tcl_GetObjResult( interp ); 754 Tcl_IncrRefCount( errorResult ); 755 Tk_RestoreSavedOptions( &savedOptions ); 756 } 757 758 if (mask & BCAST_CONF_NEWGWORLD) { 759 if (BroadcastComputeGeometry( bcastPtr, &width, &height ) != TCL_OK) { 760 continue; 761 } 762 err = QTSPresSetDimensions( bcastPtr->presentation, 763 kQTSAllStreams, Long2Fix( width ), Long2Fix( height ) ); 764 bcastPtr->flags |= NEWGWORLD; 765 } 766 767 768 769 770 /* 771 * If we came so far break out of the ierror loop. 772 */ 773 774 break; 775 } 776 if (ierror) { 777 Tcl_SetObjResult( interp, errorResult ); 778 Tcl_DecrRefCount( errorResult ); 779 return TCL_ERROR; 780 } else { 781 Tk_FreeSavedOptions( &savedOptions ); 782 return TCL_OK; 783 } 784} 785 786/* 787 *---------------------------------------------------------------------- 788 * 789 * BroadcastWorldChanged -- 790 * 791 * Something changed, arrange for the widget to be redisplayed. 792 * Compute geometry. 793 * 794 * Results: 795 * None. 796 * 797 * Side effects: 798 * Broadcast Widget displayed: if already on display it is scheduled for 799 * a renewed display, else, the size is requested by the tk geometry manager, 800 * and displayed upon a MapNotify event. 801 * 802 *---------------------------------------------------------------------- 803 */ 804 805static void 806BroadcastWorldChanged( ClientData clientData ) 807{ 808 MovieBroadcast *bcastPtr = (MovieBroadcast *) clientData; 809 int width, height; 810 811 QTTclDebugPrintf( bcastPtr->interp, 2, "BroadcastConfigure" ); 812 813 /* 814 * If not already scheduled for (re)display, it should be if it's mapped, 815 * else upon a MapNotify event. 816 */ 817 818 if (Tk_IsMapped(bcastPtr->tkwin) && !(bcastPtr->flags & REDRAW_PENDING)) { 819 Tcl_DoWhenIdle( DisplayBroadcast, (ClientData) bcastPtr ); 820 bcastPtr->flags |= REDRAW_PENDING; 821 } 822 823 /* 824 * Get the desired width and height to request. 825 */ 826 827 BroadcastComputeGeometry( bcastPtr, &width, &height ); 828 829 if (bcastPtr->tkwin != NULL) { 830 831 /* 832 * After getting our geometry above, let tk also know. 833 */ 834 835 Tk_GeometryRequest( bcastPtr->tkwin, width, height ); 836 Tk_SetInternalBorder( bcastPtr->tkwin, 0 ); 837 bcastPtr->flags |= NEWGWORLD; 838 } 839} 840 841/* 842 *---------------------------------------------------------------------- 843 * 844 * BroadcastComputeGeometry -- 845 * 846 * Finds the widget size to request at tk. 847 * 848 * Results: 849 * The return value is a standard Tcl result. If TCL_ERROR is 850 * returned, then the interp's result contains an error message. 851 * 852 * Side effects: 853 * Returns width and height in function arguments. Note that these 854 * are the actual width and height to request from tk, and not the 855 * options. 856 * 857 *---------------------------------------------------------------------- 858 */ 859 860static int 861BroadcastComputeGeometry( 862 MovieBroadcast *bcastPtr, 863 int *width, int *height ) 864{ 865 Tcl_Interp *interp = bcastPtr->interp; 866 int divisor; 867 int srcWidth = 0, srcHeight = 0; 868 double goldenRatio; 869 OSErr err = noErr; 870 871 QTTclDebugPrintf( bcastPtr->interp, 2, "BroadcastComputeGeometry" ); 872 873 *width = 0; 874 *height = 0; 875 876 err = GetSeqGrabberFullInputRect( bcastPtr->presentation, 877 &srcWidth, &srcHeight ); 878 if (err == noErr) { 879 bcastPtr->srcWidth = srcWidth; 880 bcastPtr->srcHeight = srcHeight; 881 QTTclDebugPrintf( bcastPtr->interp, 2, 882 "\tsrcWidth=%d, srcHeight=%d", srcWidth, srcHeight ); 883 } 884 885 /* 886 * There are two possibilities here: either we have got one of 887 * '-width' or '-height' > 0; use these in this case. Or use the 888 * '-size' option (quarter, half, or full). 889 */ 890 891 if ((bcastPtr->width > 0) || (bcastPtr->height > 0)) { 892 goldenRatio = (double) bcastPtr->srcWidth/(double) bcastPtr->srcHeight; 893 if ((bcastPtr->width > 0) && (bcastPtr->height == 0)) { 894 *width = bcastPtr->width; 895 *height = (int) ((double) bcastPtr->width/goldenRatio); 896 } else if ((bcastPtr->width == 0) && (bcastPtr->height > 0)) { 897 *height = bcastPtr->height; 898 *width = (int) (goldenRatio * bcastPtr->height); 899 } else { 900 901 /* This code should never be executed; my QuickCam becomes weired! */ 902 *width = bcastPtr->width; 903 *height = bcastPtr->height; 904 } 905 906 /* 907 * Check max source size. 908 */ 909 910 if ((bcastPtr->width > bcastPtr->srcWidth) || 911 (bcastPtr->height > bcastPtr->srcHeight)) { 912 bcastPtr->width = bcastPtr->srcWidth; 913 *width = bcastPtr->srcWidth; 914 bcastPtr->height = bcastPtr->srcHeight; 915 *height = bcastPtr->srcHeight; 916 } 917 } else { 918 switch (bcastPtr->indSize) { 919 case BCAST_WIDGET_SIZE_QUARTER: 920 divisor = 4; 921 break; 922 case BCAST_WIDGET_SIZE_HALF: 923 divisor = 2; 924 break; 925 case BCAST_WIDGET_SIZE_FULL: 926 divisor = 1; 927 break; 928 } 929 *width = bcastPtr->srcWidth/divisor; 930 *height = bcastPtr->srcHeight/divisor; 931 } 932 QTTclDebugPrintf( bcastPtr->interp, 2, "\twidth=%d, height=%d", 933 *width, *height ); 934 935 return TCL_OK; 936} 937 938/* 939 *-------------------------------------------------------------- 940 * 941 * GetSDPFromFile -- 942 * 943 * Fills in the necessary elements necessary for getting 944 * settings from an SDP file. 945 * 946 * Results: 947 * Standard TCL result. 948 * 949 * Side effects: 950 * None. 951 * 952 *-------------------------------------------------------------- 953 */ 954 955static int 956GetSDPFromFile( 957 MovieBroadcast *bcastPtr, 958 Tcl_Obj *fileNamePtr ) 959{ 960 Tcl_Interp *interp = bcastPtr->interp; 961 Tcl_Channel readChannel = NULL; 962 Tcl_Obj *readObj = Tcl_NewObj(); 963 unsigned char *contentPtr; 964 int nread; 965 int len; 966 QTSPresentation presentation = kQTSInvalidPresentation; 967 QTSPresParams presParams; 968 QTSMediaParams mediaParams; 969 FSSpec fsSpec; 970 OSErr err = noErr; 971 int result = TCL_OK; 972 973 err = QTTclNativePathNameToFSSpec( interp, 974 Tcl_GetString( fileNamePtr ), &fsSpec ); 975 if (err != noErr) { 976 Tcl_SetObjResult( bcastPtr->interp, Tcl_NewStringObj( 977 "File not found", -1 ) ); 978 return TCL_ERROR; 979 } 980 981 /* 982 * Make a partial parsing... 983 */ 984 985 readChannel = Tcl_FSOpenFileChannel( interp, Tcl_GetString(fileNamePtr), 986 "r", 0666 ); 987 if (readChannel == NULL) { 988 result = TCL_ERROR; 989 goto bail; 990 } 991 result = Tcl_SetChannelOption( interp, readChannel, "-translation", "binary" ); 992 if (result != TCL_OK) { 993 goto bail; 994 } 995 nread = Tcl_ReadChars( readChannel, readObj, 99999, 0 ); 996 if (nread == -1) { 997 Tcl_SetObjResult( interp, 998 Tcl_NewStringObj( Tcl_ErrnoMsg( Tcl_GetErrno() ), -1 ) ); 999 result = TCL_ERROR; 1000 goto bail; 1001 } 1002 contentPtr = Tcl_GetByteArrayFromObj( readObj, &len ); 1003 1004 1005 memset( &presParams, 0, sizeof(presParams) ); 1006 memset( &mediaParams, 0, sizeof(mediaParams) ); 1007 QTSInitializeMediaParams( &mediaParams ); 1008 1009 mediaParams.v.width = Long2Fix(320); 1010 mediaParams.v.height = Long2Fix(240); 1011 //mediaParams.v.gWorld = gOffscreenGWorldPtr; 1012 mediaParams.v.gWorld = TkMacOSXGetDrawablePort(Tk_WindowId(bcastPtr->tkwin)); 1013 mediaParams.v.gdHandle = GetMainDevice(); 1014 1015 presParams.version = kQTSPresParamsVersion1; 1016 presParams.flags = kQTSSendMediaFlag | /* Send, not receive, 1017 * kQTSReceiveMediaFlag */ 1018 kQTSAutoModeFlag | /* Use seq grabber */ 1019 kQTSDontShowStatusFlag; 1020 presParams.timeScale = kDefaultPresTimeScale; 1021 presParams.mediaParams = &mediaParams; 1022 presParams.notificationProc = (QTSNotificationUPP) 1023 NewQTSNotificationUPP( PresentationNotification ); 1024 presParams.notificationRefCon = bcastPtr; 1025 1026 /* 1027 * Create a new presentation. 1028 */ 1029 1030 err = QTSNewPresentationFromFile( &fsSpec, &presParams, &bcastPtr->presentation ); 1031 if (err != noErr) { 1032 Tcl_SetObjResult( interp, 1033 Tcl_NewStringObj( "Failed creating new presentation", -1) ); 1034 result = TCL_ERROR; 1035 goto bail; 1036 } 1037 //bcastPtr->presentation = presentation; 1038 1039bail: 1040 1041 Tcl_Close( interp, readChannel ); 1042 Tcl_DecrRefCount( readObj ); 1043 QTSDisposeMediaParams( &mediaParams ); 1044 return result; 1045} 1046 1047/* 1048 *-------------------------------------------------------------- 1049 * 1050 * GetSDPFromData -- 1051 * 1052 * Fills in the necessary elements necessary for getting 1053 * settings from SDP Tcl list. Each item corresponding to a 1054 * line in an SDP file. 1055 * 1056 * Results: 1057 * Standard TCL result. 1058 * 1059 * Side effects: 1060 * None. 1061 * 1062 *-------------------------------------------------------------- 1063 */ 1064 1065static int 1066GetSDPFromData( 1067 MovieBroadcast *bcastPtr, 1068 Tcl_Obj *sdpListPtr ) 1069{ 1070 Tcl_Interp *interp = bcastPtr->interp; 1071 char *charPtr; 1072 int nlines; 1073 int i; 1074 Tcl_DString ds; 1075 Tcl_DString dsSDP; 1076 SInt64 dataLength; 1077 Tcl_Obj *objPtr = NULL; 1078 QTSMediaParams mediaParams; 1079 QTSPresParams presParams; 1080 OSErr err = noErr; 1081 int result = TCL_OK; 1082 1083 if (Tcl_ListObjLength( interp, sdpListPtr, &nlines ) != TCL_OK) { 1084 result = TCL_ERROR; 1085 goto bail; 1086 } 1087 Tcl_DStringInit( &dsSDP ); 1088 1089 for (i = 0; i < nlines; i++) { 1090 if (Tcl_ListObjIndex( interp, sdpListPtr, i, &objPtr ) != TCL_OK) { 1091 result = TCL_ERROR; 1092 goto bail; 1093 } 1094 charPtr = Tcl_UtfToExternalDString( gQTTclTranslationEncoding, 1095 Tcl_GetString( objPtr ), -1 , &ds ); 1096 Tcl_DStringAppendElement( &dsSDP, charPtr ); 1097 Tcl_DStringFree( &ds ); 1098 if (i < nlines - 1) { 1099 Tcl_DStringAppend( &dsSDP, "\r", 1 ); 1100 } 1101 } 1102 QTTclDebugPrintf( bcastPtr->interp, 2, 1103 "GetSDPFromData: SDP=%s", Tcl_DStringValue( &dsSDP ) ); 1104 1105 memset( &presParams, 0, sizeof(presParams) ); 1106 memset( &mediaParams, 0, sizeof(mediaParams) ); 1107 QTSInitializeMediaParams( &mediaParams ); 1108 1109 mediaParams.v.width = Long2Fix(320); 1110 mediaParams.v.height = Long2Fix(240); 1111 mediaParams.v.gWorld = gOffscreenGWorldPtr; 1112 mediaParams.v.gdHandle = GetMainDevice(); 1113 1114 presParams.version = kQTSPresParamsVersion1; 1115 presParams.flags = kQTSSendMediaFlag | kQTSAutoModeFlag | 1116 kQTSDontShowStatusFlag; 1117 presParams.timeScale = kDefaultPresTimeScale; 1118 presParams.mediaParams = &mediaParams; 1119 presParams.notificationProc = (QTSNotificationUPP) 1120 NewQTSNotificationUPP( PresentationNotification ); 1121 presParams.notificationRefCon = bcastPtr; 1122 1123 dataLength = Tcl_DStringLength( &dsSDP ); 1124 err = QTSNewPresentationFromData( kQTSSDPDataType, 1125 Tcl_DStringValue( &dsSDP ), &dataLength, 1126 &presParams, &bcastPtr->presentation ); 1127 if (err != noErr) { 1128 Tcl_SetObjResult( interp, 1129 Tcl_NewStringObj( "Failed creating new presentation", -1) ); 1130 goto bail; 1131 } 1132 1133bail: 1134 1135 Tcl_DStringFree( &dsSDP ); 1136 return result; 1137} 1138 1139static int 1140GetSDPFromURL( 1141 MovieBroadcast *bcastPtr, 1142 Tcl_Obj *urlPtr ) 1143{ 1144 char *charPtr; 1145 int len; 1146 QTSMediaParams mediaParams; 1147 QTSPresParams presParams; 1148 OSErr err = noErr; 1149 Handle urlDataRef = NULL; 1150 Tcl_DString ds; 1151 int result = TCL_OK; 1152 1153 memset( &presParams, 0, sizeof(presParams) ); 1154 memset( &mediaParams, 0, sizeof(mediaParams) ); 1155 QTSInitializeMediaParams( &mediaParams ); 1156 1157 mediaParams.v.width = Long2Fix(320); 1158 mediaParams.v.height = Long2Fix(240); 1159 mediaParams.v.gWorld = gOffscreenGWorldPtr; 1160 mediaParams.v.gdHandle = GetMainDevice(); 1161 1162 presParams.version = kQTSPresParamsVersion1; 1163 presParams.flags = kQTSSendMediaFlag | kQTSAutoModeFlag | 1164 kQTSDontShowStatusFlag; 1165 presParams.timeScale = kDefaultPresTimeScale; 1166 presParams.mediaParams = &mediaParams; 1167 presParams.notificationProc = (QTSNotificationUPP) 1168 NewQTSNotificationUPP( PresentationNotification ); 1169 presParams.notificationRefCon = bcastPtr; 1170 1171 charPtr = Tcl_UtfToExternalDString( gQTTclTranslationEncoding, 1172 Tcl_GetString( urlPtr ), -1, &ds); 1173 len = Tcl_DStringLength( &ds ); 1174 urlDataRef = MySafeNewHandle( len + 1, 1 ); 1175 BlockMoveData( charPtr, *urlDataRef, len ); 1176 err = QTSNewPresentationFromDataRef( urlDataRef, 1177 URLDataHandlerSubType, &presParams, &bcastPtr->presentation ); 1178 Tcl_DStringFree( &ds ); 1179 if (err != noErr) { 1180 Tcl_SetObjResult( bcastPtr->interp, 1181 Tcl_NewStringObj( "Failed creating new presentation", -1) ); 1182 goto bail; 1183 } 1184 1185bail: 1186 1187 if (urlDataRef != NULL) { 1188 DisposeHandle( urlDataRef ); 1189 } 1190 return result; 1191} 1192 1193/* 1194 *-------------------------------------------------------------- 1195 * 1196 * BroadcastEventProc -- 1197 * 1198 * This procedure is invoked by the Tk dispatcher for various 1199 * events on squares. 1200 * 1201 * Results: 1202 * None. 1203 * 1204 * Side effects: 1205 * When the window gets deleted, internal structures get 1206 * cleaned up. When it gets exposed, it is redisplayed. 1207 * 1208 *-------------------------------------------------------------- 1209 */ 1210 1211static void 1212BroadcastEventProc( 1213 ClientData clientData, /* Information about window. */ 1214 XEvent *eventPtr ) /* Information about event. */ 1215{ 1216 MovieBroadcast *bcastPtr = (MovieBroadcast *) clientData; 1217 OSErr err = noErr; 1218 1219 QTTclDebugPrintf( bcastPtr->interp, 2, 1220 "BroadcastEventProc: type=%d, flags=%1d%1d%1d%1d", 1221 eventPtr->type, (bcastPtr->flags >> 3) & 0x1, (bcastPtr->flags >> 2) & 0x1, 1222 (bcastPtr->flags >> 1) & 0x1, (bcastPtr->flags >> 0) & 0x1 ); 1223 1224 if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { 1225 1226 goto redraw; 1227 } else if (eventPtr->type == ConfigureNotify) { 1228 1229 goto redraw; 1230 } else if (eventPtr->type == UnmapNotify) { 1231 err = QTSPresSetGWorld( bcastPtr->presentation, kQTSAllStreams, 1232 gOffscreenGWorldPtr, NULL ); 1233 bcastPtr->grafPtr = NULL; 1234 } else if (eventPtr->type == MapNotify) { 1235 bcastPtr->flags |= NEWGWORLD; 1236 goto redraw; 1237 } else if (eventPtr->type == DestroyNotify) { 1238 1239 /* 1240 * We are being destroyed. 1241 */ 1242 1243 if (bcastPtr->tkwin != NULL) { 1244 bcastPtr->tkwin = NULL; 1245 Tcl_DeleteCommandFromToken( bcastPtr->interp, bcastPtr->widgetCmd ); 1246 } 1247 if (bcastPtr->flags & REDRAW_PENDING) { 1248 Tcl_CancelIdleCall( DisplayBroadcast, (ClientData) bcastPtr ); 1249 } 1250 DestroyBroadcast( bcastPtr ); 1251 } 1252 return; 1253 1254redraw: 1255 1256 /* 1257 * Now we know that the event type was such that the widget needs to be redrawn. 1258 * Schedule the redrawing procedure. 1259 */ 1260 1261 if ((bcastPtr->tkwin != NULL) && Tk_IsMapped(bcastPtr->tkwin) && 1262 !(bcastPtr->flags & REDRAW_PENDING)) { 1263 Tcl_DoWhenIdle( DisplayBroadcast, (ClientData) bcastPtr ); 1264 bcastPtr->flags |= REDRAW_PENDING; 1265 } 1266} 1267 1268/* 1269 *-------------------------------------------------------------- 1270 * 1271 * DisplayBroadcast -- 1272 * 1273 * This procedure redraws the contents of a square window. 1274 * It is invoked as a do-when-idle handler, so it only runs 1275 * when there's nothing else for the application to do. 1276 * 1277 * Results: 1278 * None. 1279 * 1280 * Side effects: 1281 * Information appears on the screen. 1282 * 1283 *-------------------------------------------------------------- 1284 */ 1285 1286static void 1287DisplayBroadcast( 1288 ClientData clientData ) /* Information about window. */ 1289{ 1290 MovieBroadcast *bcastPtr = (MovieBroadcast *) clientData; 1291 Tk_Window tkwin = bcastPtr->tkwin; 1292 CGrafPtr saveWorld = NULL; 1293 GDHandle saveDevice = NULL; 1294 GWorldPtr theGWorldPtr = NULL; 1295 Rect tkRect; 1296 MatrixRecord matrix; 1297 OSErr err = noErr; 1298 1299 QTTclDebugPrintf( bcastPtr->interp, 2, 1300 "DisplayBroadcast: targetState=%2d, state=%2d", 1301 bcastPtr->targetState, bcastPtr->state ); 1302 1303 bcastPtr->flags &= ~REDRAW_PENDING; 1304 if (!Tk_IsMapped( tkwin)) { 1305 return; 1306 } 1307 if (bcastPtr->presentation == kQTSInvalidPresentation) { 1308 return; 1309 } 1310 GetGWorld( &saveWorld, &saveDevice ); 1311 1312 theGWorldPtr = QTTclMacGetDrawablePort( Tk_WindowId(tkwin) ); 1313 MacSetPort( (GrafPtr) theGWorldPtr ); 1314 //GetClip( saveRegion ); 1315 QTTclMacWinBounds( (TkWindow *) tkwin, &tkRect ); 1316 1317 /* 1318 * Setting QTSPresSetGWorld stops presentation if rolling. 1319 */ 1320 1321 if (bcastPtr->grafPtr == NULL) { 1322 err = QTSPresSetGWorld( bcastPtr->presentation, kQTSAllStreams, 1323 theGWorldPtr, NULL ); 1324 bcastPtr->grafPtr = theGWorldPtr; 1325 } 1326 SetIdentityMatrix( &matrix ); 1327 TranslateMatrix( &matrix, Long2Fix( tkRect.left ), Long2Fix( tkRect.top ) ); 1328 err = QTSPresSetMatrix( bcastPtr->presentation, kQTSAllStreams, &matrix ); 1329 1330 err = QTSPresSetDimensions( bcastPtr->presentation, kQTSAllStreams, 1331 Long2Fix( tkRect.right - tkRect.left ), 1332 Long2Fix( tkRect.bottom - tkRect.top ) ); 1333 1334 if (bcastPtr->targetState <= kPresentationStateIdle) { 1335 DrawBluescreen( bcastPtr ); 1336 } 1337 1338 SetGWorld( saveWorld, saveDevice ); 1339} 1340 1341/* 1342 *-------------------------------------------------------------- 1343 * 1344 * DrawBluescreen -- 1345 * 1346 * Draws a "pause" screen when broadcaster idle. 1347 * 1348 * Results: 1349 * Standard TCL result. 1350 * 1351 * Side effects: 1352 * None. 1353 * 1354 *-------------------------------------------------------------- 1355 */ 1356 1357static void 1358DrawBluescreen( MovieBroadcast *bcastPtr ) 1359{ 1360 short x, y; 1361 short dx; 1362 short rad; 1363 short faceNum; 1364 short strWidth; 1365 short width; 1366 int i; 1367 RGBColor blueColor = {0x0000, 0x0000, 0xFFFF}; 1368 RGBColor blackColor = {0x0000, 0x0000, 0x0000}; 1369 RGBColor whiteColor = {0xFFFF, 0xFFFF, 0xFFFF}; 1370 RGBColor color; 1371 Rect tkRect; 1372 Rect r; 1373 GWorldPtr theGWorldPtr = NULL; 1374 CGrafPtr saveWorld = NULL; 1375 GDHandle saveDevice = NULL; 1376 1377 QTTclDebugPrintf( bcastPtr->interp, 2, "DrawBluescreen" ); 1378 1379 QTTclMacWinBounds( (TkWindow *) bcastPtr->tkwin, &tkRect ); 1380 theGWorldPtr = QTTclMacGetDrawablePort( Tk_WindowId(bcastPtr->tkwin) ); 1381 GetGWorld( &saveWorld, &saveDevice ); 1382 SetGWorld( theGWorldPtr, NULL ); 1383#if TARGET_API_MAC_CARBON 1384 TkMacOSXSetUpClippingRgn( (Drawable) Tk_WindowId( bcastPtr->tkwin ) ); 1385#else 1386 TkMacSetUpClippingRgn( (Drawable) Tk_WindowId( bcastPtr->tkwin ) ); 1387#endif 1388 1389 RGBForeColor( &blueColor ); 1390 PenMode( patCopy ); 1391 PaintRect( &tkRect ); 1392 1393 width = tkRect.right - tkRect.left; 1394 dx = width/15; 1395 x = (tkRect.right + tkRect.left)/2. - 5.5 * dx; 1396 r.top = tkRect.top + (1./3.) * (tkRect.bottom - tkRect.top); 1397 r.bottom = tkRect.top + (2./3.) * (tkRect.bottom - tkRect.top); 1398 for (i = 0; i <= 10; i++) { 1399 color.red = (10 - i)/10.0 * 0xFFFF; 1400 color.green = (10 - i)/10.0 * 0xFFFF; 1401 color.blue = (10 - i)/10.0 * 0xFFFF; 1402 RGBForeColor( &color ); 1403 r.left = x + i * dx; 1404 r.right = r.left + dx; 1405 PaintRect( &r ); 1406 } 1407 rad = 0.4 * (tkRect.bottom - tkRect.top); 1408 x = tkRect.left + 0.5 * (tkRect.right - tkRect.left); 1409 y = tkRect.top + 0.5 * (tkRect.bottom - tkRect.top); 1410 r.left = x - rad; 1411 r.right = x + rad; 1412 r.top = y - rad; 1413 r.bottom = y + rad; 1414 RGBForeColor( &blackColor ); 1415 FrameOval( &r ); 1416 1417 RGBForeColor( &whiteColor ); 1418#if TARGET_API_MAC_CARBON 1419 faceNum = FMGetFontFamilyFromName( "\pHelvetica" ); 1420#else 1421 GetFNum( "\pHelvetica", &faceNum ); 1422#endif 1423 TextFont( faceNum ); 1424 TextSize( 24 ); 1425 strWidth = StringWidth( "\pQuickTime TV" ); 1426 if (strWidth > 0.8 * width) { 1427 TextSize( 18 ); 1428 strWidth = StringWidth( "\pQuickTime TV" ); 1429 } 1430 MoveTo( width/2 - strWidth/2, 32 ); 1431 DrawString( "\pQuickTime TV" ); 1432 1433 SetGWorld( saveWorld, saveDevice ); 1434} 1435 1436/* 1437 *---------------------------------------------------------------------- 1438 * 1439 * BroadcastDeletedProc -- 1440 * 1441 * This procedure is invoked when a widget command is deleted. If 1442 * the widget isn't already in the process of being destroyed, 1443 * this command destroys it. 1444 * 1445 * Results: 1446 * None. 1447 * 1448 * Side effects: 1449 * The widget is destroyed. 1450 * 1451 *---------------------------------------------------------------------- 1452 */ 1453 1454static void 1455BroadcastDeletedProc( 1456 ClientData clientData ) /* Pointer to widget record for widget. */ 1457{ 1458 MovieBroadcast *bcastPtr = (MovieBroadcast *) clientData; 1459 Tk_Window tkwin = bcastPtr->tkwin; 1460 1461 /* 1462 * This procedure could be invoked either because the window was 1463 * destroyed and the command was then deleted (in which case tkwin 1464 * is NULL) or because the command was deleted, and then this procedure 1465 * destroys the widget. 1466 */ 1467 1468 if (tkwin != NULL) { 1469 bcastPtr->tkwin = NULL; 1470 Tk_DestroyWindow( tkwin ); 1471 } 1472} 1473 1474/* 1475 *---------------------------------------------------------------------- 1476 * 1477 * DestroyBroadcast -- 1478 * 1479 * Deletes a Broadcast Widget. Most things cleaned up with Tk_FreeOptions 1480 * but some things are freed up by me. 1481 * 1482 * Results: 1483 * None. 1484 * 1485 * Side effects: 1486 * Hopefully frees memory. 1487 * 1488 *---------------------------------------------------------------------- 1489 */ 1490 1491static void 1492DestroyBroadcast( ClientData clientData ) 1493{ 1494 MovieBroadcast *bcastPtr = (MovieBroadcast *) clientData; 1495 1496 1497 QTTclDebugPrintf( bcastPtr->interp, 2, "DestroyBroadcast" ); 1498 1499 RemoveFromBroadcastList( bcastPtr ); 1500 PresentationFree( bcastPtr ); 1501 1502 1503 1504 Tk_FreeConfigOptions( (char *) bcastPtr, bcastPtr->optionTable, 1505 bcastPtr->tkwin ); 1506 Tcl_EventuallyFree( (ClientData) bcastPtr, TCL_DYNAMIC ); 1507} 1508 1509static void 1510PresentationFree( MovieBroadcast *bcastPtr ) 1511{ 1512 BcastSourcerInfo *sourcerPtr = NULL; 1513 OSErr err = noErr; 1514 1515 sourcerPtr = bcastPtr->sourcerInfoListPtr; 1516 while (sourcerPtr != NULL) { 1517 SourcerFree( sourcerPtr ); 1518 sourcerPtr = sourcerPtr->next; 1519 } 1520 1521 err = QTSDisposePresentation( bcastPtr->presentation, 0 ); 1522 bcastPtr->presentation = kQTSInvalidPresentation; 1523} 1524 1525static void 1526SourcerFree( BcastSourcerInfo *sourcerPtr ) 1527{ 1528 OSErr err = noErr; 1529 1530 1531 QTSDisposeStream( sourcerPtr->stream, 0 ); 1532} 1533 1534/* 1535 *---------------------------------------------------------------------- 1536 * 1537 * PresentationNotification -- 1538 * 1539 * Notification callback function for the presentation. 1540 * 1541 * Results: 1542 * Apple error code. 1543 * 1544 * Side effects: 1545 * Depending in notification type. 1546 * 1547 *---------------------------------------------------------------------- 1548 */ 1549 1550static ComponentResult 1551PresentationNotification( 1552 ComponentResult inErr, 1553 OSType inNotificationType, 1554 void *inNotificationParams, 1555 void *inRefCon ) 1556{ 1557 MovieBroadcast *bcastPtr = (MovieBroadcast *) inRefCon; 1558 ComponentResult err = noErr; 1559 char tmp1[64]; 1560 char cmd[255]; 1561 int callTclCommand = false; 1562 int result = TCL_OK; 1563 1564 if (bcastPtr->tkwin == NULL) { 1565 return noErr; 1566 } 1567 1568 memset( tmp1, 0, 64 ); 1569 1570 switch (inNotificationType) { 1571 case kQTSPreviewAckNotification: strcpy( tmp1, "kQTSPreviewAckNotification" ); break; 1572 case kQTSPrerollAckNotification: strcpy( tmp1, "kQTSPrerollAckNotification" ); break; 1573 case kQTSStartAckNotification: strcpy( tmp1, "kQTSStartAckNotification" ); break; 1574 case kQTSStopAckNotification: strcpy( tmp1, "kQTSStopAckNotification" ); break; 1575 case kQTSStatusNotification: strcpy( tmp1, "kQTSStatusNotification" ); break; 1576 case kQTSErrorNotification: strcpy( tmp1, "kQTSErrorNotification" ); break; 1577 default: memcpy( tmp1, &inNotificationType, 4 ); 1578 } 1579 1580 QTTclDebugPrintf( bcastPtr->interp, 2, 1581 "PresentationNotification targetState=%2d, state=%2d, type=%s", 1582 bcastPtr->targetState, bcastPtr->state, tmp1 ); 1583 1584 if (bcastPtr->command != NULL) { 1585 memset( cmd, 0, 255 ); 1586 strcpy( cmd, bcastPtr->command ); 1587 strcat( cmd, " " ); 1588 strcat( cmd, Tcl_GetCommandName(bcastPtr->interp, bcastPtr->widgetCmd) ); 1589 } 1590 1591 switch (inNotificationType) { 1592 1593 case kQTSPreviewAckNotification: { 1594 if (bcastPtr->state != kPresentationStateStartingPreview) { 1595 // we're in the wrong state to be getting this ack 1596 // ignore it... 1597 1598 } else { 1599 if (inErr != noErr) { 1600 bcastPtr->targetState = kPresentationStateIdle; 1601 bcastPtr->state = kPresentationStateIdle; 1602 HandleErrorNotification( bcastPtr, "Preview", inErr, NULL ); 1603 } else { 1604 bcastPtr->state = kPresentationStatePreviewing; 1605 strcat( cmd, " preview" ); 1606 callTclCommand = true; 1607 } 1608 } 1609 break; 1610 } 1611 1612 case kQTSPrerollAckNotification: { 1613 if (bcastPtr->state != kPresentationStateStartingPreroll) { 1614 // we're in the wrong state to be getting this ack 1615 // ignore it... 1616 1617 } else { 1618 if (inErr != noErr) { 1619 bcastPtr->targetState = kPresentationStateIdle; 1620 bcastPtr->state = kPresentationStateIdle; 1621 HandleErrorNotification( bcastPtr, "Preroll", inErr, NULL ); 1622 } else { 1623 bcastPtr->state = kPresentationStateReadyToPlay; 1624 if (bcastPtr->targetState > kPresentationStateReadyToPlay) { 1625 bcastPtr->state = kPresentationStateStarting; 1626 err = QTSPresStart( bcastPtr->presentation, 1627 kQTSAllStreams, 0L ); 1628 strcat( cmd, " preroll" ); 1629 callTclCommand = true; 1630 } 1631 } 1632 } 1633 break; 1634 } 1635 1636 case kQTSStartAckNotification: { 1637 if (bcastPtr->state != kPresentationStateStarting) { 1638 // we're in the wrong state to be getting this ack 1639 // ignore it... 1640 1641 } else { 1642 if (inErr != noErr) { 1643 bcastPtr->targetState = kPresentationStateIdle; 1644 bcastPtr->state = kPresentationStateIdle; 1645 HandleErrorNotification( bcastPtr, "Start", inErr, NULL ); 1646 } else { 1647 bcastPtr->state = kPresentationStatePlaying; 1648 strcat( cmd, " start" ); 1649 callTclCommand = true; 1650 } 1651 } 1652 break; 1653 } 1654 1655 case kQTSStopAckNotification: { 1656 if (bcastPtr->state != kPresentationStateStarting) { 1657 // we're in the wrong state to be getting this ack 1658 // ignore it... 1659 1660 } else { 1661 if (inErr != noErr) { 1662 bcastPtr->targetState = kPresentationStateIdle; 1663 bcastPtr->state = kPresentationStateIdle; 1664 HandleErrorNotification( bcastPtr, "Preroll", inErr, NULL ); 1665 } else { 1666 bcastPtr->state = kPresentationStateIdle; 1667 strcat( cmd, " stop" ); 1668 callTclCommand = true; 1669 } 1670 } 1671 break; 1672 } 1673 1674 case kQTSStatusNotification: { 1675 if (inErr != noErr) { 1676 bcastPtr->targetState = kPresentationStateIdle; 1677 bcastPtr->state = kPresentationStateIdle; 1678 HandleErrorNotification( bcastPtr, "Status", inErr, NULL ); 1679 } 1680 break; 1681 } 1682 1683 case kQTSErrorNotification: { 1684 if (inErr != noErr) { 1685 HandleErrorNotification( bcastPtr, "Error", inErr, NULL ); 1686 } 1687 break; 1688 } 1689 1690 default: { 1691 // it's ok to get a notification you don't know about 1692 // just silently ignore it - this is not an error 1693 break; 1694 } 1695 1696 // all you can do in a kQTSNewPresentationNotification 1697 // is store the presentation - do not make any presentation calls 1698 // when you that notification 1699 1700 } 1701 if (Tk_IsMapped(bcastPtr->tkwin) && 1702 (bcastPtr->state == kPresentationStateIdle)) { 1703 DrawBluescreen( bcastPtr ); 1704 } 1705 if ((bcastPtr->command != NULL) && callTclCommand) { 1706 result = Tcl_Eval( bcastPtr->interp, cmd ); 1707 } 1708 return err; 1709} 1710 1711static ComponentResult 1712HandleErrorNotification( MovieBroadcast *bcastPtr, 1713 const char *inNotificationTypeString, 1714 ComponentResult inErr, 1715 QTSErrorParams *inErrorParams) 1716{ 1717 char cmd[255]; 1718 int result = TCL_OK; 1719 1720 /* 1721 * Tell the user about the error in some nice way. 1722 */ 1723 1724 if (bcastPtr->command != NULL) { 1725 memset( cmd, 0, 255 ); 1726 strcpy( cmd, bcastPtr->command ); 1727 strcat( cmd, " " ); 1728 strcat( cmd, Tcl_GetCommandName(bcastPtr->interp, bcastPtr->widgetCmd) ); 1729 strcat( cmd, " error " ); 1730 strcat( cmd, inNotificationTypeString ); 1731 } 1732 result = Tcl_Eval( bcastPtr->interp, cmd ); 1733 1734 return noErr; 1735} 1736 1737/* 1738 *---------------------------------------------------------------------- 1739 * 1740 * AddToBroadcastList, RemoveFromBroadcastList -- 1741 * 1742 * Adds/removes broadcast from our linked list. 1743 * 1744 * Results: 1745 * None. 1746 * 1747 * Side effects: 1748 * None. 1749 * 1750 *---------------------------------------------------------------------- 1751 */ 1752 1753static void 1754AddToBroadcastList( MovieBroadcast *bcastPtr ) 1755{ 1756 MovieBroadcastList *newEntryPtr = NULL; 1757 1758 // check!!!! 1759 newEntryPtr = (MovieBroadcastList *) ckalloc( sizeof(MovieBroadcastList) ); 1760 newEntryPtr->bcastPtr = bcastPtr; 1761 newEntryPtr->next = gMovieBroadcastListPtr; 1762 gMovieBroadcastListPtr = newEntryPtr; 1763} 1764 1765static void 1766RemoveFromBroadcastList( MovieBroadcast *bcastPtr ) 1767{ 1768 MovieBroadcastList *currentPtr = NULL; 1769 MovieBroadcastList *prevPtr = NULL; 1770 1771 /* 1772 * Find the entry in the linked list for a start. 1773 */ 1774 1775 // check!!!! 1776 currentPtr = gMovieBroadcastListPtr; 1777 while (currentPtr != NULL) { 1778 if (currentPtr->bcastPtr == bcastPtr) { 1779 if (prevPtr != NULL) { 1780 prevPtr->next = currentPtr->next; 1781 } else { 1782 gMovieBroadcastListPtr = currentPtr->next; 1783 } 1784 ckfree( (char *) currentPtr ); 1785 break; 1786 } 1787 prevPtr = currentPtr; 1788 currentPtr = currentPtr->next; 1789 } 1790} 1791 1792void 1793BroadcastMacEvent( void ) 1794{ 1795 IdleAllPresentations(); 1796} 1797 1798/* 1799 *---------------------------------------------------------------------- 1800 * 1801 * IdleAllPresentations, IdleSourcersForPresentation -- 1802 * 1803 * Idles presentations in our linked list. 1804 * Also idles any sourcers for each presentation. 1805 * 1806 * Results: 1807 * None. 1808 * 1809 * Side effects: 1810 * Just gives cpu cycles. 1811 * 1812 *---------------------------------------------------------------------- 1813 */ 1814 1815static void 1816IdleAllPresentations( void ) 1817{ 1818 int clipInvalid = false; 1819 RgnHandle rgnHandle = NULL; 1820 MovieBroadcastList *currentPtr; 1821 MovieBroadcast *bcastPtr; 1822 OSErr err = noErr; 1823 1824 currentPtr = gMovieBroadcastListPtr; 1825 while (currentPtr != NULL) { 1826 bcastPtr = currentPtr->bcastPtr; 1827 if ((bcastPtr->presentation != kQTSInvalidPresentation) && 1828 (bcastPtr->presentation != NULL)) { 1829 1830 clipInvalid = false; 1831 if (MyIsClipRegionInvalid( bcastPtr->tkwin )) { 1832 clipInvalid = true; 1833 QTTclDebugPrintf( bcastPtr->interp, 3, "\tMyIsClipRegionInvalid" ); 1834 } 1835 if (clipInvalid) { 1836 rgnHandle = QTTclMacVisableClipRgn( (TkWindow *) bcastPtr->tkwin ); 1837 if (rgnHandle == NULL) { 1838 clipInvalid = false; 1839 } 1840 } 1841 if (clipInvalid) { 1842 err = QTSPresSetClip( bcastPtr->presentation, kQTSAllStreams, 1843 rgnHandle ); 1844 } 1845 QTSPresIdle( bcastPtr->presentation, NULL ); 1846 IdleSourcersForPresentation( bcastPtr ); 1847 } 1848 currentPtr = currentPtr->next; 1849 }; 1850} 1851 1852static void 1853IdleSourcersForPresentation( MovieBroadcast *bcastPtr ) 1854{ 1855 BcastSourcerInfo *sourcerPtr = NULL; 1856 1857 sourcerPtr = bcastPtr->sourcerInfoListPtr; 1858 while (sourcerPtr != NULL) { 1859 QTSSourcerIdle( sourcerPtr->sourcer, 0, 0, 0 ); 1860 sourcerPtr = sourcerPtr->next; 1861 } 1862} 1863 1864/* 1865 *---------------------------------------------------------------------- 1866 * 1867 * InstallBroadcastIdlingEventLoopTimer, TaskNeededSoonerCallback, 1868 * CarbonTimerNextTime -- 1869 * 1870 * Sets up Carbon timers to serve presentations. 1871 * 1872 * Results: 1873 * . 1874 * 1875 * Side effects: 1876 * Timers installed etc. 1877 * 1878 *---------------------------------------------------------------------- 1879 */ 1880 1881#if TARGET_API_MAC_CARBON 1882static OSStatus 1883InstallBroadcastIdlingEventLoopTimer( void ) 1884{ 1885 OSStatus err; 1886 1887 int kMinimumIdleDurationInMillis = 100; 1888 1889 err = InstallEventLoopTimer( GetMainEventLoop(), 1890 0, /* firedelay */ 1891 kEventDurationMillisecond * kMinimumIdleDurationInMillis, /* interval */ 1892 NewEventLoopTimerUPP( PresentationCarbonTimer ), 1893 NULL, 1894 &gCarbonPresentationTimerRef ); 1895 if (err != noErr) { 1896 /* 1897 * Install a callback that the Idle Manager will use when 1898 * QuickTime needs to wake me up immediately. 1899 */ 1900 1901// err = QTInstallNextTaskNeededSoonerCallback( 1902// NewQTNextTaskNeededSoonerCallbackUPP( TaskNeededSoonerCallback ), 1903// 1000, /* Millisecond timescale */ 1904// 0, /* No flags */ 1905// (void *) gCarbonMovieTimerRef ); 1906 1907 } 1908 return err; 1909} 1910#endif 1911 1912/* 1913 *---------------------------------------------------------------------- 1914 * 1915 * MoviePlayerCarbonTimer -- 1916 * 1917 * Timer callback to serve movies under Carbon. 1918 * 1919 * Results: 1920 * None. 1921 * 1922 * Side effects: 1923 * Calls 'MoviePlayerMacEvent'. 1924 * 1925 *---------------------------------------------------------------------- 1926 */ 1927 1928#if TARGET_API_MAC_CARBON 1929static pascal void 1930PresentationCarbonTimer( EventLoopTimerRef theTimer, 1931 void *userData ) 1932{ 1933 IdleAllPresentations(); 1934} 1935#endif 1936 1937static int 1938PresentationExportToFile( MovieBroadcast *bcastPtr, 1939 int objc, Tcl_Obj *CONST objv[] ) 1940{ 1941 OSErr err = noErr; 1942 QTSExportParams exportParams; 1943 1944 if (bcastPtr->presentation != kQTSInvalidPresentation) { 1945 memset( &exportParams, 0, sizeof(exportParams) ); 1946 exportParams.version = kQTSExportParamsVersion1; 1947 exportParams.exportType = ksigMoviePlayer; 1948 exportParams.flagsIn = kQTSExportFlag_ShowDialog; 1949 exportParams.filterProc = NewQTSModalFilterUPP( MyModalFilterDialogProc ); 1950 exportParams.filterProcRefCon = bcastPtr; 1951 1952 err = QTSPresExport( bcastPtr->presentation, kQTSAllStreams, &exportParams ); 1953 DisposeQTSNotificationUPP( exportParams.filterProc ); 1954 if (err != noErr) { 1955 1956 } 1957 } else { 1958 1959 err = qtsBadStateErr; 1960 } 1961 return err; 1962} 1963 1964static int 1965SourcerObjCmd( Tcl_Interp *interp, 1966 MovieBroadcast *bcastPtr, 1967 int objc, Tcl_Obj *CONST objv[] ) 1968{ 1969 int index; 1970 OSErr err = noErr; 1971 int result = TCL_OK; 1972 1973 if (objc < 1) { 1974 Tcl_WrongNumArgs( interp, 1, objv, "option ?arg arg...?" ); 1975 return TCL_ERROR; 1976 } 1977 if (Tcl_GetIndexFromObj( interp, objv[0], sourcerCommands, "command", 1978 0, &index) != TCL_OK) { 1979 return TCL_ERROR; 1980 } 1981 1982 Tcl_Preserve( (ClientData) bcastPtr ); 1983 1984 switch (index) { 1985 1986 case kSourcerCmdAdd: { 1987 1988 break; 1989 } 1990 case kSourcerCmdInfo: { 1991 SourcerGetInfo( interp, bcastPtr, kQTSAllStreams, objc - 1, objv + 1 ); 1992 break; 1993 } 1994 case kSourcerCmdRemove: { 1995 1996 break; 1997 } 1998 } 1999 Tcl_Release( (ClientData) bcastPtr ); 2000 return result; 2001 2002error: 2003 Tcl_Release( (ClientData) bcastPtr ); 2004 return TCL_ERROR; 2005} 2006 2007/* 2008 *---------------------------------------------------------------------- 2009 * 2010 * GetAllStreamMediaTypes -- 2011 * 2012 * Investigate the presentation for stream types. 2013 * 2014 * Results: 2015 * None. 2016 * 2017 * Side effects: 2018 * None. 2019 * 2020 *---------------------------------------------------------------------- 2021 */ 2022 2023static void 2024GetAllStreamMediaTypes( QTSPresentation presentation, int *haveAudio, 2025 int *haveVideo, int *haveText ) 2026{ 2027 int i; 2028 int numStreams; 2029 QTSStream stream; 2030 OSType mediaType; 2031 OSErr err = noErr; 2032 2033 *haveAudio = 0; 2034 *haveVideo = 0; 2035 *haveText = 0; 2036 numStreams = QTSPresGetNumStreams( presentation ); 2037 2038 for (i = 1; i <= numStreams; ++i) { 2039 stream = QTSPresGetIndStream( presentation, i ); 2040 if (err == noErr) { 2041 err = QTSPresGetInfo( presentation, stream, 2042 kQTSMediaTypeInfo, &mediaType ); 2043 if (err == noErr) { 2044 switch (mediaType) { 2045 case SoundMediaType: { 2046 *haveAudio = 1; 2047 break; 2048 } 2049 case VideoMediaType: { 2050 *haveVideo = 1; 2051 break; 2052 } 2053 case TextMediaType: { 2054 *haveText = 1; 2055 break; 2056 } 2057 case MPEGMediaType: { 2058 /* empty */ 2059 } 2060 } 2061 } 2062 } 2063 } 2064} 2065 2066/* 2067 *---------------------------------------------------------------------- 2068 * 2069 * SourcerGetInfo -- 2070 * 2071 * Returns a hierarchical list of various info for a particular stream, 2072 * or for all if kQTSAllStreams. 2073 * 2074 * Results: 2075 * Standard Tcl result. 2076 * 2077 * Side effects: 2078 * None. 2079 * 2080 *---------------------------------------------------------------------- 2081 */ 2082 2083static int 2084SourcerGetInfo( Tcl_Interp *interp, 2085 MovieBroadcast *bcastPtr, 2086 QTSStream inStream, /* Investigate only this stream */ 2087 int objc, 2088 Tcl_Obj *CONST objv[] ) 2089{ 2090 int i; 2091 int numSourcers; 2092 long lType; 2093 char type8Char[8]; 2094 QTSSourcer sourcer = 0; 2095 Tcl_Obj *listObjPtr; 2096 Tcl_Obj *subListObjPtr; 2097 Tcl_Obj *rectObjPtr; 2098 Rect rect; 2099 QTSSourcerTimingParams timingParams; 2100 UInt16 uint16; 2101 UInt32 uint32; 2102 Handle handle = NULL; 2103 OSType osType; 2104 Fixed fixed; 2105 OSErr err = noErr; 2106 int result = TCL_OK; 2107 2108 listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL ); 2109 numSourcers = QTSPresGetNumSourcers( bcastPtr->presentation, 2110 inStream ); 2111 if (numSourcers == 0) { 2112 Tcl_SetObjResult( interp, Tcl_NewStringObj("", -1) ); 2113 } 2114 2115 for (i = 1; i <= numSourcers; ++i) { 2116 subListObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL ); 2117 2118 QTSPresGetIndSourcer( bcastPtr->presentation, inStream, 2119 i, &sourcer ); 2120 2121 err = GetSourcerType( sourcer, &osType ); 2122 if (err == noErr) { 2123 Tcl_ListObjAppendElement( interp, subListObjPtr, 2124 Tcl_NewStringObj("-sourcertype", -1) ); 2125 lType = EndianU32_BtoN( osType ); 2126 memset( type8Char, '\0', 8 ); 2127 memcpy( type8Char, &lType, 4 ); 2128 Tcl_ListObjAppendElement( interp, subListObjPtr, 2129 Tcl_NewStringObj(type8Char, -1) ); 2130 } 2131 2132 err = QTSSourcerGetInfo( sourcer, kQTSMediaTypeInfo, &osType ); 2133 if (err == noErr) { 2134 Tcl_ListObjAppendElement( interp, subListObjPtr, 2135 Tcl_NewStringObj("-mediatype", -1) ); 2136 lType = EndianU32_BtoN( osType ); 2137 memset( type8Char, '\0', 8 ); 2138 memcpy( type8Char, &lType, 4 ); 2139 Tcl_ListObjAppendElement( interp, subListObjPtr, 2140 Tcl_NewStringObj(type8Char, -1) ); 2141 } 2142 2143 err = QTSSourcerGetInfo( sourcer, kQTSInfo_TargetFrameRate, &fixed ); 2144 if (err == noErr) { 2145 Tcl_ListObjAppendElement( interp, subListObjPtr, 2146 Tcl_NewStringObj("-targetframerate", -1) ); 2147 Tcl_ListObjAppendElement( interp, subListObjPtr, 2148 Tcl_NewDoubleObj( Fix2X(fixed) ) ); 2149 } 2150 2151 err = QTSSourcerGetInfo( sourcer, kQTSInfo_TargetDataRate, &uint32 ); 2152 if (err == noErr) { 2153 Tcl_ListObjAppendElement( interp, subListObjPtr, 2154 Tcl_NewStringObj("-targetdatarate", -1) ); 2155 Tcl_ListObjAppendElement( interp, subListObjPtr, 2156 Tcl_NewLongObj( uint32 ) ); 2157 } 2158 2159 err = QTSSourcerGetInfo( sourcer, kQTSInfo_InputDeviceName, &handle ); 2160 if (err == noErr) { 2161 Tcl_ListObjAppendElement( interp, subListObjPtr, 2162 Tcl_NewStringObj("-inputdevicename", -1) ); 2163 Tcl_ListObjAppendElement( interp, subListObjPtr, 2164 Tcl_NewStringObj( *handle, -1) ); 2165 DisposeHandle( handle ); 2166 } 2167 2168 err = QTSSourcerGetInfo( sourcer, kQTSInfo_InputSourceName, &handle ); 2169 if (err == noErr) { 2170 Tcl_ListObjAppendElement( interp, subListObjPtr, 2171 Tcl_NewStringObj("-inputsourcename", -1) ); 2172 Tcl_ListObjAppendElement( interp, subListObjPtr, 2173 Tcl_NewStringObj( *handle, -1) ); 2174 DisposeHandle( handle ); 2175 } 2176 2177 err = QTSSourcerGetInfo( sourcer, kQTSInfo_FullInputRect, &rect ); 2178 if (err == noErr) { 2179 Tcl_ListObjAppendElement( interp, subListObjPtr, 2180 Tcl_NewStringObj("-fullinputrect", -1) ); 2181 rectObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL ); 2182 Tcl_ListObjAppendElement( interp, rectObjPtr, 2183 Tcl_NewIntObj(rect.left) ); 2184 Tcl_ListObjAppendElement( interp, rectObjPtr, 2185 Tcl_NewIntObj(rect.top) ); 2186 Tcl_ListObjAppendElement( interp, rectObjPtr, 2187 Tcl_NewIntObj(rect.right) ); 2188 Tcl_ListObjAppendElement( interp, rectObjPtr, 2189 Tcl_NewIntObj(rect.bottom) ); 2190 Tcl_ListObjAppendElement( interp, subListObjPtr, rectObjPtr ); 2191 } 2192 2193 /* 2194 * Video device settings. 2195 */ 2196 2197 err = QTSSourcerGetInfo( sourcer, kQTSInfo_VideoHue, &uint16 ); 2198 if (err == noErr) { 2199 Tcl_ListObjAppendElement( interp, subListObjPtr, 2200 Tcl_NewStringObj("-videohue", -1) ); 2201 Tcl_ListObjAppendElement( interp, subListObjPtr, 2202 Tcl_NewIntObj( uint16 ) ); 2203 } 2204 2205 2206 /* 2207 * For tracks sourcers only. 2208 */ 2209 2210 err = QTSSourcerGetInfo( sourcer, kQTSInfo_SourcerTiming, &timingParams ); 2211 if (err == noErr) { 2212 Tcl_ListObjAppendElement( interp, subListObjPtr, 2213 Tcl_NewStringObj("-timescale", -1) ); 2214 Tcl_ListObjAppendElement( interp, subListObjPtr, 2215 Tcl_NewLongObj(timingParams.timeScale) ); 2216 /* TimeValue64 !!!! wrong!!!!!! */ 2217 Tcl_ListObjAppendElement( interp, subListObjPtr, 2218 Tcl_NewStringObj("-presentationstarttime", -1) ); 2219 Tcl_ListObjAppendElement( interp, subListObjPtr, 2220 Tcl_NewLongObj(timingParams.presentationStartTime) ); 2221 Tcl_ListObjAppendElement( interp, subListObjPtr, 2222 Tcl_NewStringObj("-presentationendtime", -1) ); 2223 Tcl_ListObjAppendElement( interp, subListObjPtr, 2224 Tcl_NewLongObj(timingParams.presentationEndTime) ); 2225 Tcl_ListObjAppendElement( interp, subListObjPtr, 2226 Tcl_NewStringObj("-presentationcurrenttime", -1) ); 2227 Tcl_ListObjAppendElement( interp, subListObjPtr, 2228 Tcl_NewLongObj(timingParams.presentationCurrentTime) ); 2229 Tcl_ListObjAppendElement( interp, subListObjPtr, 2230 Tcl_NewStringObj("-localstarttime", -1) ); 2231 Tcl_ListObjAppendElement( interp, subListObjPtr, 2232 Tcl_NewLongObj(timingParams.localStartTime) ); 2233 Tcl_ListObjAppendElement( interp, subListObjPtr, 2234 Tcl_NewStringObj("-localendtime", -1) ); 2235 Tcl_ListObjAppendElement( interp, subListObjPtr, 2236 Tcl_NewLongObj(timingParams.localEndTime) ); 2237 Tcl_ListObjAppendElement( interp, subListObjPtr, 2238 Tcl_NewStringObj("-localcurrenttime", -1) ); 2239 Tcl_ListObjAppendElement( interp, subListObjPtr, 2240 Tcl_NewLongObj(timingParams.localCurrentTime) ); 2241 } 2242 2243 Tcl_ListObjAppendElement( interp, listObjPtr, subListObjPtr ); 2244 } 2245 Tcl_SetObjResult( interp, listObjPtr ); 2246 2247 return result; 2248} 2249 2250static OSErr 2251GetSourcerType( QTSSourcer sourcer, OSType *typePtr ) 2252{ 2253 ComponentDescription cd; 2254 OSErr err = noErr; 2255 2256 err = GetComponentInfo( (Component) sourcer, &cd, NULL, NULL, NULL ); 2257 if (err == noErr) { 2258 *typePtr = cd.componentSubType; 2259 } 2260 return err; 2261} 2262 2263static OSErr 2264GetSeqGrabberFullInputRect( QTSPresentation presentation, 2265 int *fullWidth, int *fullHeight ) 2266{ 2267 int i; 2268 int numSourcers; 2269 QTSSourcer sourcer = 0; 2270 Rect rect; 2271 OSErr err = paramErr; 2272 2273 numSourcers = QTSPresGetNumSourcers( presentation, 2274 kQTSAllStreams ); 2275 for (i = 1; i <= numSourcers; ++i) { 2276 QTSPresGetIndSourcer( presentation, kQTSAllStreams, 2277 i, &sourcer ); 2278 err = QTSSourcerGetInfo( sourcer, kQTSInfo_FullInputRect, &rect ); 2279 if (err == noErr) { 2280 MacOffsetRect( &rect, (short) -rect.left, (short) -rect.top ); 2281 *fullWidth = rect.right; 2282 *fullHeight = rect.bottom; 2283 break; 2284 } 2285 } 2286 return err; 2287} 2288 2289static int 2290StreamObjCmd( Tcl_Interp *interp, 2291 MovieBroadcast *bcastPtr, 2292 int objc, Tcl_Obj *CONST objv[] ) 2293{ 2294 int numStreams; 2295 int numSourcers; 2296 int i; 2297 char type8Char[8]; 2298 long lType; 2299 QTSStream stream; 2300 OSType trackType; 2301 OSErr err = noErr; 2302 Tcl_Obj *listObjPtr; 2303 Tcl_Obj *subListObjPtr; 2304 int result = TCL_OK; 2305 2306 2307 listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL ); 2308 numStreams = QTSPresGetNumStreams( bcastPtr->presentation ); 2309 2310 for (i = 1; i <= numStreams; ++i) { 2311 stream = QTSPresGetIndStream( bcastPtr->presentation, i ); 2312 err = QTSPresGetInfo( bcastPtr->presentation, stream, 2313 kQTSMediaTypeInfo, &trackType ); 2314 2315 subListObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL ); 2316 Tcl_ListObjAppendElement( interp, subListObjPtr, 2317 Tcl_NewStringObj("-mediatype", -1) ); 2318 lType = EndianU32_BtoN( trackType ); 2319 memset( type8Char, '\0', 8 ); 2320 memcpy( type8Char, &lType, 4 ); 2321 Tcl_ListObjAppendElement( interp, subListObjPtr, 2322 Tcl_NewStringObj(type8Char, -1) ); 2323 2324 2325 if (stream != kQTSInvalidStream) { 2326 numSourcers = QTSPresGetNumSourcers( bcastPtr->presentation, stream ); 2327 } 2328 Tcl_ListObjAppendElement( interp, listObjPtr, subListObjPtr ); 2329 } 2330 2331 Tcl_SetObjResult( interp, listObjPtr ); 2332 return result; 2333} 2334 2335static int 2336PictureObjCmd( Tcl_Interp *interp, 2337 MovieBroadcast *bcastPtr, 2338 int objc, Tcl_Obj *CONST objv[] ) 2339{ 2340 int dstWidth = 0; 2341 int dstHeight = 0; 2342 PicHandle picHand = NULL; 2343 OSErr err = noErr; 2344 int result = TCL_OK; 2345 2346 err = QTSPresGetPicture( bcastPtr->presentation, kQTSAllStreams, 2347 &picHand ); 2348 if ((err == noErr) && (picHand != NULL)) { 2349 result = ConvertPictureToTkPhoto( interp, picHand, 2350 dstWidth, dstHeight, Tcl_GetString(objv[0]) ); 2351 } else { 2352 Tcl_SetObjResult( interp, 2353 Tcl_NewStringObj( "Failed taking picture", -1) ); 2354 result = TCL_ERROR; 2355 } 2356 if (picHand != NULL) { 2357 KillPicture( picHand ); 2358 } 2359 return result; 2360} 2361 2362static int 2363SetupTrackSourcerForStream( 2364 MovieBroadcast *bcastPtr, 2365 QTSStream inStream ) 2366{ 2367 QTSSourcer sourcer = 0; 2368 ComponentDescription cd; 2369 Component component; 2370 OSType trackType = SoundMediaType; 2371 //QTSSourcerCallbackProcParams cParams; 2372 OSErr tmpErr = noErr; 2373 OSErr err = noErr; 2374 BcastSourcerInfo *sourcerInfoPtr = NULL; 2375 int result = TCL_OK; 2376 2377 /* 2378 * Ask the presentation if it already has a media type. 2379 */ 2380 2381 tmpErr = QTSPresGetInfo( bcastPtr->presentation, inStream, 2382 kQTSMediaTypeInfo, &trackType ); 2383 if (tmpErr != noErr) { 2384 trackType = SoundMediaType; 2385 } 2386 2387 memset( &cd, 0, sizeof(cd) ); 2388 cd.componentType = kQTSSourcerType; 2389 cd.componentSubType = kQTSMovieTrackSourcerType; 2390 cd.componentManufacturer = trackType; 2391 component = FindNextComponent( 0, &cd ); 2392 if (component == 0) { 2393 2394 goto exit; 2395 } 2396 2397 if ((err = OpenAComponent( component, &sourcer ) ) != noErr) { 2398 2399 goto exit; 2400 } 2401 if ((err = QTSSourcerInitialize( sourcer, NULL ) ) != noErr) { 2402 2403 goto exit; 2404 } 2405/* 2406 if (inHandler->loop) { 2407 memset(&loopParams, 0, sizeof(loopParams)); 2408 loopParams.loopFlags = kQTSLoopFlag_Loop; 2409 loopParams.flagsMask = kQTSLoopFlag_Loop; 2410 QTSSourcerSetInfo( sourcer, kQTSInfo_Loop, &loopParams ); 2411 } 2412*/ 2413/* 2414 memset( &cParams, 0, sizeof(cParams) ); 2415 cParams.version = kQTSSourcerCallbackProcParamsVersion1; 2416 if (gMovieSourcingCallbackUPP == NULL) { 2417 gMovieSourcingCallbackUPP = 2418 NewQTSNotificationUPP( MovieSourcingCallbackProc ); 2419 } 2420 cParams.proc = gMovieSourcingCallbackUPP; 2421 cParams.refCon = inHandler; 2422 2423 if ((err = QTSSourcerSetInfo( sourcer, kQTSInfo_SourcerCallbackProc, 2424 &cParams) ) != noErr) { 2425 2426 goto error; 2427 } 2428*/ 2429 /* 2430 * Create and fill in a SourcerInfo object. 2431 */ 2432 2433 sourcerInfoPtr = (BcastSourcerInfo *) ckalloc(sizeof(BcastSourcerInfo) ); 2434 memset( (void *) sourcerInfoPtr, 0, (sizeof(BcastSourcerInfo)) ); 2435 sourcerInfoPtr->presentation = bcastPtr->presentation; 2436 sourcerInfoPtr->stream = inStream; 2437 sourcerInfoPtr->component = component; 2438 sourcerInfoPtr->sourcer = sourcer; 2439 sourcerInfoPtr->trackType = trackType; 2440 sourcerInfoPtr->track = NULL; 2441 sourcerInfoPtr->done = false; 2442 2443 /* 2444 * The presentation now owns the sourcer and will dispose of it unless you 2445 * set the right flags. 2446 */ 2447 2448 if ((err = QTSPresAddSourcer( bcastPtr->presentation, inStream, 2449 sourcer, 0L) ) != noErr) { 2450 2451 goto exit; 2452 } 2453 2454exit: 2455 if (err != noErr) { 2456 if (sourcer != 0) { 2457 CloseComponent( sourcer ); 2458 } 2459 } 2460 2461 return result; 2462} 2463 2464 2465static pascal ComponentResult 2466MovieSourcingCallbackProc( 2467 ComponentResult inErr, 2468 OSType inSelector, 2469 void *ioParams, 2470 void *inRefCon ) 2471{ 2472 MovieBroadcast *bcastPtr = (MovieBroadcast *) inRefCon; 2473 ComponentResult err = noErr; 2474 2475 if (inErr != noErr) { 2476 2477 } 2478 2479 switch (inSelector) { 2480 case 9999: 2481 2482 break; 2483 2484 default: 2485 /* Ignore any selectors you don't know about. */ 2486 break; 2487 2488 } 2489 return err; 2490} 2491 2492void 2493QTSDisposeMediaParams( QTSMediaParams *inMediaParams ) 2494{ 2495 if (inMediaParams != NULL) { 2496 if (inMediaParams->v.clip != NULL) { 2497 DisposeRgn( inMediaParams->v.clip ); 2498 } 2499 DisposePtr( (Ptr) inMediaParams->a.frequencyBands ); 2500 } 2501} 2502 2503#if TARGET_OS_MAC && !TARGET_API_MAC_CARBON 2504 2505static pascal Boolean 2506MyModalFilterDialogProc( DialogPtr dialogPtr, 2507 EventRecord *event, 2508 SInt16 *itemHit, 2509 long *refCon ) 2510{ 2511 Boolean handled = false; 2512 WindowRef eventWindow = NULL; 2513 WindowRef dialogWindow = NULL; 2514 2515#if TARGET_API_MAC_CARBON 2516 dialogWindow = GetDialogWindow( dialogPtr ); 2517#else 2518 dialogWindow = dialogPtr; 2519#endif 2520 2521 switch (event->what) { 2522 2523 case updateEvt: 2524 eventWindow = (WindowRef) event->message; 2525 if ((eventWindow != NULL) && (eventWindow != dialogWindow)) { 2526 2527 /* 2528 * Handle update events to background windows here. 2529 * First, translate mac event to a number of tcl events. 2530 * If any tcl events generated, execute them until empty, and don't wait. 2531 */ 2532 2533 if (TkMacConvertEvent( event )) { 2534 while ( Tcl_DoOneEvent( TCL_IDLE_EVENTS | TCL_DONT_WAIT | TCL_WINDOW_EVENTS ) ) 2535 /* empty */ 2536 ; 2537 } 2538 } 2539 break; 2540 } 2541 return handled; 2542} 2543 2544#endif // TARGET_OS_MAC && !TARGET_API_MAC_CARBON 2545 2546#if TARGET_API_MAC_CARBON 2547static Boolean 2548MyModalFilterDialogProc( DialogPtr dialogPtr, 2549 EventRecord *event, 2550 SInt16 *itemHit, 2551 long *refCon ) 2552{ 2553 Boolean handled = false; 2554 2555 return handled; 2556} 2557#endif // TARGET_API_MAC_CARBON 2558 2559/*---------------------------------------------------------------------------*/ 2560 2561