1/*
2 * MovieUtils.c --
3 *
4 *		Non-Tk Utility functions, part of QuickTimeTcl.
5 *
6 * Copyright (c) 2000-2003  Mats Bengtsson
7 *
8 * $Id: MovieUtils.c,v 1.2 2005/04/02 13:41:26 matben Exp $
9 */
10
11#include "MoviePlayer.h"
12
13
14/*
15 *----------------------------------------------------------------------
16 *
17 * GetFirstVideoTrackInMovie --
18 *
19 *	 	Return, through the theTrack parameter, the first video track in
20 *		the specified movie. Actually, we look for the first track that
21 *		has the kCharacteristicCanSendVideo characteristic, so we can
22 *		apply effects to MPEG or QD3D tracks as well.
23 *
24 * Results:
25 *  	OSErr: noErr if found track, returned in '*theTrack',
26 *		or 'invalidTrack' if none.
27 *
28 * Side effects:
29 *		None.
30 *
31 *----------------------------------------------------------------------
32 */
33
34OSErr
35GetFirstVideoTrackInMovie( Movie theMovie, Track *theTrack )
36{
37	*theTrack = GetMovieIndTrackType( theMovie, 1, kCharacteristicCanSendVideo,
38	        movieTrackCharacteristic | movieTrackEnabledOnly );
39
40	if (*theTrack == NULL) {
41		return(invalidTrack);
42	}
43	return(noErr);
44}
45
46/*
47 *----------------------------------------------------------------------
48 *
49 * ShowControllerButton, HideControllerButton --
50 *
51 *		Shows or hides controller buttons.
52 *
53 * Results:
54 *  	None.
55 *
56 * Side effects:
57 *		None.
58 *
59 *----------------------------------------------------------------------
60 */
61
62void
63ShowControllerButton( MovieController theMC, long theButton )
64{
65	long	flags;
66
67	/* Handle the custom button separately. */
68	if (theButton == mcFlagsUseCustomButton) {
69		MCDoAction( theMC, mcActionGetFlags, &flags );
70		MCDoAction( theMC, mcActionSetFlags,
71				(void *)((flags | theButton)));
72	} else {
73
74		/*
75		 * Get the current explicit flags and set the explicit flag for the
76		 * specified button.
77		 */
78
79		flags = mcFlagQTVRExplicitFlagSet;
80		MCDoAction(theMC, mcActionGetFlags, &flags);
81		MCDoAction(theMC, mcActionSetFlags,
82				(void *)((flags | theButton) | mcFlagQTVRExplicitFlagSet));
83
84		/*
85		 * Get the current control flags and clear the suppress flag for the
86		 * specified button.
87		 */
88
89		flags = 0;
90		MCDoAction(theMC, mcActionGetFlags, &flags);
91		MCDoAction(theMC, mcActionSetFlags,
92				(void *)(flags & ~theButton & ~mcFlagQTVRExplicitFlagSet));
93	}
94}
95
96void
97HideControllerButton( MovieController theMC, long theButton )
98{
99	long	flags;
100
101	/* Handle the custom button separately. */
102	if (theButton == mcFlagsUseCustomButton) {
103		MCDoAction(theMC, mcActionGetFlags, &flags);
104		MCDoAction(theMC, mcActionSetFlags, (void *)(flags & ~theButton));
105	} else {
106		flags = mcFlagQTVRExplicitFlagSet;
107		MCDoAction(theMC, mcActionGetFlags, &flags);
108		MCDoAction(theMC, mcActionSetFlags,
109				(void *)((flags | theButton) | mcFlagQTVRExplicitFlagSet));
110
111		flags = 0;
112		MCDoAction(theMC, mcActionGetFlags, &flags);
113		MCDoAction(theMC, mcActionSetFlags,
114				(void *)((flags | theButton) & ~mcFlagQTVRExplicitFlagSet));
115	}
116}
117
118/*
119 *----------------------------------------------------------------------
120 *
121 * SetLoopingState --
122 *
123 *		Sets the movies looping state via its controller.
124 *
125 * Results:
126 *  	None.
127 *
128 * Side effects:
129 *		None.
130 *
131 *----------------------------------------------------------------------
132 */
133
134void
135SetLoopingState( MovieController theMC, UInt8 theLoopState )
136{
137
138	switch (theLoopState) {
139
140		case kQTTclNormalLooping:
141			MCDoAction( theMC, mcActionSetLooping, (void *) true );
142			MCDoAction( theMC, mcActionSetLoopIsPalindrome, (void *) false );
143			break;
144
145		case kQTTclPalindromeLooping:
146			MCDoAction( theMC, mcActionSetLooping, (void *) true );
147			MCDoAction( theMC, mcActionSetLoopIsPalindrome, (void *) true );
148			break;
149
150		case kQTTclNoLooping:
151			MCDoAction( theMC, mcActionSetLooping, (void *) false );
152			MCDoAction( theMC, mcActionSetLoopIsPalindrome, (void *) false );
153
154		default:
155			break;
156	}
157}
158
159/*
160 *----------------------------------------------------------------------
161 *
162 * SetMovieLoopState --
163 *
164 *		Sets the movies looping state if it doesn't have a controller.
165 *
166 * Results:
167 *  	None.
168 *
169 * Side effects:
170 *		None.
171 *
172 *----------------------------------------------------------------------
173 */
174
175void
176SetMovieLoopState( Movie theMovie, UInt8 theLoopState )
177{
178	TimeBase		myTimeBase;
179	long			myFlags = 0L;
180
181	if (theMovie == NULL)
182		return;
183
184	myTimeBase = GetMovieTimeBase( theMovie );
185	myFlags = GetTimeBaseFlags( myTimeBase );
186
187	switch (theLoopState) {
188
189		case kQTTclNormalLooping:
190			SetMoviePlayHints( theMovie, hintsLoop, hintsLoop );
191			SetMoviePlayHints( theMovie, 0L, hintsPalindrome );
192			myFlags |= loopTimeBase;
193			myFlags &= ~palindromeLoopTimeBase;
194			break;
195
196		case kQTTclPalindromeLooping:
197			SetMoviePlayHints( theMovie, hintsLoop, hintsLoop );
198			SetMoviePlayHints( theMovie, hintsPalindrome, hintsPalindrome );
199			myFlags |= loopTimeBase;
200			myFlags |= palindromeLoopTimeBase;
201			break;
202
203		case kQTTclNoLooping:
204			myFlags &= ~loopTimeBase;
205			myFlags &= ~palindromeLoopTimeBase;
206			SetMoviePlayHints( theMovie, 0L, hintsLoop | hintsPalindrome );
207			break;
208		}
209	SetTimeBaseFlags( myTimeBase, myFlags );
210}
211
212/*
213 *----------------------------------------------------------------------
214 *
215 * IsMovieLooping --
216 *
217 *		Returns a boolean of 'true' if the movie is in a looping state,
218 *  	and 'false' else.
219 *
220 * Results:
221 *  	Boolean.
222 *
223 * Side effects:
224 *		None.
225 *
226 *----------------------------------------------------------------------
227 */
228
229Boolean
230IsMovieLooping( Movie theMovie )
231{
232	TimeBase	myTimeBase;
233	long		myFlags = 0L;
234
235	if (theMovie == NULL)
236		return(false);
237
238	myTimeBase = GetMovieTimeBase( theMovie );
239	myFlags = GetTimeBaseFlags( myTimeBase );
240	return((Boolean) (myFlags & loopTimeBase));
241}
242/*
243 *----------------------------------------------------------------------
244 *
245 * MoviePrePrerollCompleteProc --
246 *
247 *	.
248 *
249 * Results:
250 *  None.
251 *
252 * Side effects:
253 *	None.
254 *
255 *----------------------------------------------------------------------
256 */
257
258void
259MoviePrePrerollCompleteProc( Movie theMovie, OSErr err, void *refConst )
260{
261	TimeValue		myTimeValue;
262	Fixed			myPlayRate;
263
264	if (theMovie == NULL) {
265		return;
266	}
267	// Preroll the movie so that it's ready to play.
268	myTimeValue = GetMovieTime( theMovie, NULL );
269	myPlayRate = GetMoviePreferredRate( theMovie );
270}
271
272/*
273 *----------------------------------------------------------------------
274 *
275 * GetControllerType --
276 *
277 *	Gets a movie's type of controller by examining the movie's user data.
278 *
279 * Results:
280 *  Returns the movie's controller type which is a four-character code.
281 *
282 * Side effects:
283 *	None.
284 *
285 *----------------------------------------------------------------------
286 */
287
288OSType
289GetControllerType( Movie theMovie )
290{
291    UserData    theUserData;
292    OSType      theType = kUnknownType;
293    OSErr       err = noErr;
294
295    if (theMovie == NULL) {
296        return(theType);
297    }
298    theUserData = GetMovieUserData( theMovie );
299    if (theUserData != NULL) {
300        err = GetUserDataItem( theUserData, &theType, sizeof(theType),
301                kUserDataMovieControllerType, 0 );
302        if (err == noErr) {
303            theType = EndianU32_BtoN( theType );
304        }
305    }
306    return(theType);
307}
308
309/*
310 *----------------------------------------------------------------------
311 *
312 * IsQTVRMovie --
313 *
314 *  Returns a Boolean value of 'true' if we have got a VR panoramic movie,
315 *  and 'false' otherwise.
316 *
317 * Results:
318 *  Returns a Boolean value of 'true' if we have got a VR panoramic movie,
319 *  and 'false' otherwise.
320 *
321 * Side effects:
322 *	None.
323 *
324 *----------------------------------------------------------------------
325 */
326
327Boolean
328IsQTVRMovie( Movie theMovie )
329{
330    Boolean     isQTVRMovie = false;
331    OSType      theType = kUnknownType;
332
333    /*
334     * QTVR panos have a special piece of user data identifying the movie
335     * controller type.
336     */
337
338    theType = GetControllerType( theMovie );
339    if ((theType == kQTVRQTVRType) || (theType == kQTVROldPanoType) ||
340            (theType == kQTVROldObjectType)) {
341        isQTVRMovie = true;
342    }
343    return(isQTVRMovie);
344}
345
346/*
347 *----------------------------------------------------------------------
348 *
349 * GetControllerBarHeight --
350 *
351 *  Return the height of the controller bar displayed by the movie controller.
352 *	Note that MCGetControllerBoundsRect returns rectangle of bar and movie,
353 *	if attached, so we need to unattach the controller bar first.
354 *
355 * Results:
356 *  the height
357 *
358 * Side effects:
359 *	None.
360 *
361 *----------------------------------------------------------------------
362 */
363
364short GetControllerBarHeight( MovieController mc )
365{
366	Boolean		wasAttached = false;
367	Rect		myRect;
368	short		myHeight = 0;
369
370	/* If the controller bar is attached, detach it (and remember we did so) */
371	if (MCIsControllerAttached(mc) == 1) {
372		wasAttached = true;
373		MCSetControllerAttached( mc, false );
374	}
375
376	/* Get the rectangle of the controller */
377	MCGetControllerBoundsRect( mc, &myRect );
378	myHeight = myRect.bottom - myRect.top;
379
380	/* Now reattach the controller bar, if it was originally attached */
381	if (wasAttached) {
382		MCSetControllerAttached( mc, true );
383	}
384	return(myHeight);
385}
386
387/*
388 *----------------------------------------------------------------------
389 *
390 * GetQTVRInstanceFromController --
391 *
392 *  Returns either the QTVR instance or NULL for non QTVR movies.
393 *
394 * Results:
395 *  QTVR instance or NULL.
396 *
397 * Side effects:
398 *	None.
399 *
400 *----------------------------------------------------------------------
401 */
402
403QTVRInstance
404GetQTVRInstanceFromController( MovieController mc )
405{
406    Track           track = NULL;
407    QTVRInstance    qtvrInstance = NULL;
408    Movie           aMovie = NULL;
409
410    aMovie = MCGetMovie( mc );
411    if (aMovie != NULL) {
412        track = QTVRGetQTVRTrack( aMovie, 1 );
413        if (track != NULL) {
414            QTVRGetQTVRInstance( &qtvrInstance, track, mc );
415
416            /* Set units to degrees */
417            if (qtvrInstance != NULL) {
418                QTVRSetAngularUnits( qtvrInstance, kQTVRDegrees );
419            }
420        }
421    }
422    return qtvrInstance;
423}
424
425/*
426 *----------------------------------------------------------------------
427 *
428 * TranslateTclListToMatrixRecord --
429 *
430 *		Takes a tcl list, and fills in the matrix record.
431 *
432 * Results:
433 *		The return value is a standard Tcl result.  If TCL_ERROR is
434 *		returned, then the interp's result contains an error message.
435 *
436 * Side effects:
437 *      None.
438 *
439 *----------------------------------------------------------------------
440 */
441
442int
443TranslateTclListToMatrixRecord( Tcl_Interp *interp,
444		Tcl_Obj *listObjPtr,
445		MatrixRecord *mr )
446{
447    int         i;
448    int         j;
449    int			len;
450    double      doubleElement;
451	Tcl_Obj 	*subListObjPtr;
452	Tcl_Obj		*objPtr;
453
454	if (Tcl_ListObjLength( interp, listObjPtr, &len ) != TCL_OK) {
455		Tcl_AddErrorInfo( interp,
456				"\n	(processing matrix object)" );
457		return TCL_ERROR;
458	}
459    if (len != 3) {
460        Tcl_SetObjResult( interp, Tcl_NewStringObj(
461        		"Wrong number of items in matrix list", -1 ) );
462		return TCL_ERROR;
463    }
464	for (i = 0; i < 3; i++) {
465		if (Tcl_ListObjIndex( interp, listObjPtr, i, &subListObjPtr ) != TCL_OK) {
466			Tcl_AddErrorInfo( interp,
467					"\n	(processing matrix object)" );
468			return TCL_ERROR;
469		}
470		if (Tcl_ListObjLength( interp, subListObjPtr, &len ) != TCL_OK) {
471			Tcl_AddErrorInfo( interp,
472					"\n	(processing matrix object)" );
473			return TCL_ERROR;
474		}
475	    if (len != 3) {
476	        Tcl_SetObjResult( interp, Tcl_NewStringObj(
477	        		"Wrong number of items in matrix list", -1 ) );
478			return TCL_ERROR;
479	    }
480
481        /*
482         * Loop over all columns for this row.
483         */
484
485        for (j = 0; j < 2; j++) {
486			if (Tcl_ListObjIndex( interp, subListObjPtr, j, &objPtr )
487					!= TCL_OK) {
488				Tcl_AddErrorInfo( interp,
489						"\n	(processing matrix object)" );
490				return TCL_ERROR;
491			}
492        	if (Tcl_GetDoubleFromObj( interp, objPtr, &doubleElement )
493        			!= TCL_OK) {
494				Tcl_AddErrorInfo( interp,
495						"\n	(processing matrix object)" );
496				return TCL_ERROR;
497			}
498            mr->matrix[i][j] = X2Fix( doubleElement );
499        }
500    }
501
502    mr->matrix[0][2] = X2Frac(0.0);
503    mr->matrix[1][2] = X2Frac(0.0);
504    mr->matrix[2][2] = X2Frac(1.0);
505
506    return TCL_OK;
507}
508
509/*
510 *----------------------------------------------------------------------
511 *
512 * TranslateMatrixRecordToTclList --
513 *
514 *		Takes a matrix record and fills in the tcl object as a 3x3 tcl
515 *      list-sublist.
516 *
517 * Results:
518 *		The return value is a standard Tcl result.
519 *		3x3 matrix of Tcl_Obj* put in listObjPtrPtr.
520 *
521 * Side effects:
522 *      None.
523 *
524 *----------------------------------------------------------------------
525 */
526
527int
528TranslateMatrixRecordToTclList( Tcl_Interp *interp,
529		Tcl_Obj **listObjPtrPtr,
530        MatrixRecord *mr )
531{
532    int         i;
533    int         j;
534    double      doubleElement;
535    Tcl_Obj     *rowObj;
536
537    *listObjPtrPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
538
539    for (i = 0; i < 3; i++) {
540        rowObj = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
541        for (j = 0; j < 3; j++) {
542            if (j == 2) {
543                doubleElement = Frac2X( mr->matrix[i][j] );
544            } else {
545                doubleElement = Fix2X( mr->matrix[i][j] );
546            }
547            Tcl_ListObjAppendElement( interp, rowObj,
548                    Tcl_NewDoubleObj(doubleElement) );
549        }
550        Tcl_ListObjAppendElement( interp, *listObjPtrPtr, rowObj );
551    }
552    return TCL_OK;
553}
554
555int
556DoesCodecSupport( CodecType cType, long what, int value )
557{
558    int         answer = 0;
559    CodecInfo   cInfo;
560    OSErr       err = noErr;
561
562    err = GetCodecInfo( &cInfo, cType, 0 );
563
564    switch (what) {
565
566        case kDoesCodecPixmapDepth: {
567            long    compressFlags = cInfo.compressFlags;
568
569            switch (value) {
570                case 1: {
571                    answer = compressFlags | codecInfoDoes1;
572                    break;
573                }
574                case 2: {
575                    answer = compressFlags | codecInfoDoes2;
576                    break;
577                }
578                case 4: {
579                    answer = compressFlags | codecInfoDoes4;
580                    break;
581                }
582                case 8: {
583                    answer = compressFlags | codecInfoDoes8;
584                    break;
585                }
586                case 16: {
587                    answer = compressFlags | codecInfoDoes16;
588                    break;
589                }
590                case 32: {
591                    answer = compressFlags | codecInfoDoes32;
592                    break;
593                }
594                default: {
595
596                }
597            }
598            break;
599        }
600        case kDoesCodecCompressDepth: {
601            long    formatFlags = cInfo.formatFlags;
602
603            switch (value) {
604                case 1: {
605                    answer = formatFlags | codecInfoDepth1;
606                    break;
607                }
608                case 2: {
609                    answer = formatFlags | codecInfoDepth2;
610                    break;
611                }
612                case 4: {
613                    answer = formatFlags | codecInfoDepth4;
614                    break;
615                }
616                case 8: {
617                    answer = formatFlags | codecInfoDepth8;
618                    break;
619                }
620                case 16: {
621                    answer = formatFlags | codecInfoDepth16;
622                    break;
623                }
624                case 32: {
625                    answer = formatFlags | codecInfoDepth32;
626                    break;
627                }
628                case 24: {
629                    answer = formatFlags | codecInfoDepth24;
630                    break;
631                }
632                case 33: {
633                    answer = formatFlags | codecInfoDepth33;
634                    break;
635                }
636                case 34: {
637                    answer = formatFlags | codecInfoDepth34;
638                    break;
639                }
640                case 36: {
641                    answer = formatFlags | codecInfoDepth36;
642                    break;
643                }
644                case 40: {
645                    answer = formatFlags | codecInfoDepth40;
646                    break;
647                }
648                default: {
649
650                }
651            }
652        }
653    }
654    return answer;
655}
656
657/*
658 *----------------------------------------------------------------------
659 *
660 * IsTrackForEyes --
661 *
662 *		Returns false if sound track, and true if text or video track.
663 *
664 * Results:
665 *		Boolean.
666 *
667 * Side effects:
668 *		None.
669 *
670 *----------------------------------------------------------------------
671 */
672
673int
674IsTrackForEyes( Track myTrack )
675{
676    int     isForEyes = true;
677    Media   myMedia = NULL;
678    OSType  mediaType;
679
680	myMedia = GetTrackMedia( myTrack );
681	if (myMedia != NULL) {
682		GetMediaHandlerDescription( myMedia, &mediaType, NULL, NULL );
683		if (mediaType == SoundMediaType) {
684		    isForEyes = false;
685		}
686	}
687	return isForEyes;
688}
689
690/*----------------------------------------------------------------------*/
691