'From etoys3.0 of 7 March 2008 [latest update: #2059] on 26 July 2008 at 9:03:22 pm'! "Change Set: GStreamer-UI Date: 26 July 2008 Author: John McIntosh, Joshua Gargus Initial import of GStreamer code base from squeaksource.com/GStreamer Name: GStreamer-UI-jcg.15 Author: jcg Time: 11 May 2008, 4:07:17 pm UUID: 3f76c8bb-1108-4a28-a100-d888e0791485 Ancestors: GStreamer-UI-jcg.14 "! Morph subclass: #GStreamerDisplayMorph instanceVariableNames: 'frameBuffer gstreamer running desiredFrameRate repeat soundTrack volume stopFrame fullScreen pendingMoviePosition' classVariableNames: '' poolDictionaries: '' category: 'GStreamer-UI'! AlignmentMorph subclass: #GStreamerMoviePlayerMorph instanceVariableNames: 'moviePlayer positionSlider volumeSlider' classVariableNames: '' poolDictionaries: '' category: 'GStreamer-UI'! Object subclass: #GStreamerPlayer instanceVariableNames: 'soundTrack pipeLine filesrc hasAudio hasVideo volumePlayer' classVariableNames: '' poolDictionaries: '' category: 'GStreamer-UI'! !GStreamerDisplayMorph methodsFor: 'private' stamp: 'JMM 3/27/2008 15:28'! advancedSubMenu "private - create the advanced submenu" | subMenu | subMenu := MenuMorph new. subMenu defaultTarget: self. repeat ifTrue: [subMenu add: 'turn off repeat (now on)' translated action: #toggleRepeat] ifFalse: [subMenu add: 'turn on repeat (now off)' translated action: #toggleRepeat]. ^ subMenu ! ! !GStreamerDisplayMorph methodsFor: 'private' stamp: 'JMM 3/27/2008 14:33'! magnifyBy: aNumber "private - scale the video (if any) to a scale of the normalExtent" | ne | fullScreen := false."" ne := self normalExtent. ne isNil ifFalse: [self extent: (ne * aNumber) rounded]! ! !GStreamerDisplayMorph methodsFor: 'private' stamp: 'JMM 3/27/2008 15:22'! normalExtent "private - answer the extent of the video, if any" (gstreamer isNil or: [gstreamer hasVideo not]) ifTrue: [^ nil]. "" ^ (gstreamer videoFrameWidth) @ (gstreamer videoFrameHeight)! ! !GStreamerDisplayMorph methodsFor: 'private' stamp: 'JMM 3/27/2008 14:33'! zoomSubMenu "private - create the zoom submenu" | subMenu | subMenu := MenuMorph new. subMenu defaultTarget: self. self fullScreen ifTrue: [subMenu add: 'turn off full screen' translated action: #toggleFullScreen] ifFalse: [subMenu add: 'turn on full screen' translated action: #toggleFullScreen]. subMenu addLine. subMenu add: '50%' action: #halfSize. subMenu add: '100%' action: #normalSize. subMenu add: '200%' action: #doubleSize. ^ subMenu ! ! !GStreamerDisplayMorph methodsFor: 'stepping' stamp: 'JMM 3/28/2008 14:31'! advanceFrame | changed | gstreamer processAudioGiveTo: soundTrack. gstreamer hasVideo ifTrue: [changed := gstreamer processVideoGiveTo: frameBuffer. changed ifTrue: [self changed]]! ! !GStreamerDisplayMorph methodsFor: 'stepping' stamp: 'JMM 3/28/2008 19:24'! step "If I'm running and the and has video, advance to the next frame. Stop if we we hit the end of the video." running ifFalse: [^ self]. pendingMoviePosition ifNotNil: [gstreamer seekToTimeInSecondsEnsurePlaying: (gstreamer totalSeconds*pendingMoviePosition) asInteger. pendingMoviePosition := nil]. (gstreamer hasVideo and: [gstreamer getSimplifiedState = #null]) ifTrue: [ "end of video" repeat ifTrue: [ self rewindMovie. self startPlaying]] ifFalse: [self advanceFrame]. ! ! !GStreamerDisplayMorph methodsFor: 'stepping' stamp: 'JMM 3/31/2008 22:19'! stepTime desiredFrameRate ifNil: [^500]. ^200 "OLPC" "^ 1.0 / desiredFrameRate * 1000.0" ! ! !GStreamerDisplayMorph methodsFor: 'drawing' stamp: 'JMM 3/27/2008 14:33'! areasRemainingToFill: aRectangle "Drawing optimization. Since I completely fill my bounds with opaque pixels, this method tells Morphic that it isn't necessary to draw any morphs covered by me." ^ aRectangle areasOutside: self bounds ! ! !GStreamerDisplayMorph methodsFor: 'drawing' stamp: 'JMM 3/27/2008 14:33'! drawOn: aCanvas "Draw the current frame image, if there is one. Otherwise, fill screen with gray." frameBuffer ifNil: [aCanvas fillRectangle: self bounds color: (Color gray: 0.75)] ifNotNil: [ self extent = frameBuffer extent ifTrue: [aCanvas drawImage: frameBuffer at: bounds origin] ifFalse: [self drawScaledOn: aCanvas]]. ! ! !GStreamerDisplayMorph methodsFor: 'drawing' stamp: 'JMM 3/27/2008 14:33'! drawScaledOn: aCanvas "Draw the current frame image scaled to my bounds." | outForm destPoint warpBlt | ((aCanvas isKindOf: FormCanvas) and: [aCanvas form = Display]) ifTrue: [ "optimization: when canvas is the Display, Warpblt directly to it" outForm := Display. destPoint := bounds origin + aCanvas origin] ifFalse: [ outForm := Form extent: self extent depth: aCanvas form depth. destPoint := 0@0]. warpBlt := (WarpBlt current toForm: outForm) sourceForm: frameBuffer; colorMap: (frameBuffer colormapIfNeededForDepth: outForm depth); cellSize: 1; "installs a new colormap if cellSize > 1" combinationRule: Form over. outForm == Display ifTrue: [warpBlt clipRect: aCanvas clipRect]. warpBlt copyQuad: frameBuffer boundingBox innerCorners toRect: (destPoint extent: self extent). outForm == Display ifFalse: [ aCanvas drawImage: outForm at: bounds origin]. ! ! !GStreamerDisplayMorph methodsFor: 'file open/close' stamp: 'JMM 3/28/2008 13:45'! closeFile gstreamer ifNil: [^self]. gstreamer closeFile. gstreamer := nil. ! ! !GStreamerDisplayMorph methodsFor: 'file open/close' stamp: 'JMM 4/1/2008 13:45'! openFileNamed: aFileName "Try to open the MPEG file with the given name. Answer true if successful." | e | (FileDirectory default fileExists: aFileName) ifFalse: [self inform: ('File not found: {1}' translated format: {aFileName}). ^ false]. gstreamer := GStreamerPlayer openFile: aFileName. "initialize soundTrack" gstreamer hasAudio ifTrue: [soundTrack := gstreamer soundTrack] ifFalse: [soundTrack := nil]. gstreamer hasVideo ifTrue: [ "set screen size and display first frame" desiredFrameRate := gstreamer videoFrameRate. e := (gstreamer videoFrameWidth)@(gstreamer videoFrameHeight). frameBuffer := Form extent: e depth: (Display depth max: 16). super extent: e. self nextFrame] ifFalse: [ "hide screen for audio-only files" desiredFrameRate := 100. super extent: 250@0]. ! ! !GStreamerDisplayMorph methodsFor: 'file open/close' stamp: 'JMM 3/28/2008 09:52'! openOGGFile "Invoked by the 'Open' button. Prompt for a file name and try to open that file as an Ogg file." | result | result := (FileList2 modalFileSelectorForSuffixes: #('ogg')) . result ifNil: [^ self]. self stopPlaying. self openFileNamed: (result fullName). ! ! !GStreamerDisplayMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:33'! currentFrameScaled "Answer a Form containing the current frame scaled to my current size." | f | f := Form extent: self extent depth: 32. frameBuffer ifNil: [^ f fillColor: (Color gray: 0.75)]. self drawScaledOn: ((FormCanvas on: f) copyOffset: self topLeft negated). ^ f! ! !GStreamerDisplayMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:33'! extent: aPoint "Overridden to maintain movie aspect ratio." | scale | frameBuffer ifNil: [^ super extent: aPoint]. scale := (aPoint x / frameBuffer width) max: (aPoint y / frameBuffer height). scale := scale max: (16 / frameBuffer width). super extent: (frameBuffer extent * scale) rounded. ! ! !GStreamerDisplayMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:33'! fullScreen "answer whatever the receiver is fullScreen Note: comparation with true to make it work with instances created before the introduccion of the variable" ^ fullScreen == true! ! !GStreamerDisplayMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:33'! fullScreen: aBoolean "change the receiver's fullScreen" fullScreen := aBoolean! ! !GStreamerDisplayMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:33'! isRunning "answer whatever the receiver is running" ^ running! ! !GStreamerDisplayMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 22:53'! moviePosition "Answer a number between 0.0 and 1.0 indicating the current position within the movie." gstreamer ifNil: [^ 0.0]. ^gstreamer queryPositionPercent ! ! !GStreamerDisplayMorph methodsFor: 'accessing' stamp: 'JMM 3/28/2008 19:23'! moviePosition: aFloat pendingMoviePosition := aFloat. ! ! !GStreamerDisplayMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:33'! repeat "Answer the repeat flag." repeat ifNil: [repeat := false]. ^ repeat ! ! !GStreamerDisplayMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:33'! repeat: aBoolean "Set the repeat flag. If true, the movie will loop back to the beginning when it gets to the end." repeat := aBoolean. ! ! !GStreamerDisplayMorph methodsFor: 'accessing' stamp: 'JMM 3/28/2008 10:24'! totalFrames "Answer the total number of frames in this movie." ^gstreamer totalFrames! ! !GStreamerDisplayMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 15:26'! totalSeconds "Answer the total number of seconds in this movie." | seconds | gstreamer hasVideo ifFalse: [^ 0]. seconds := gstreamer pipeline queryDurationInSecondsOrNilAsFloat. seconds ifNil: [^0.0]. ^seconds! ! !GStreamerDisplayMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 15:20'! videoFileShortName "answer the receiver's videoFileShortName" | fileFull defaultDirFull fileShort | gstreamer isNil ifTrue: [^ '']. " answer the shortest path to the file to make easier to move morphs with references to files between different platforms" fileFull := gstreamer fileName. "" defaultDirFull := FileDirectory default fullName. fileShort := (fileFull beginsWith: defaultDirFull) ifTrue: [fileFull allButFirst: defaultDirFull size + 1] ifFalse: [fileFull]. "" ^ fileShort! ! !GStreamerDisplayMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:33'! videoFileShortName: aString "change the receiver's videoFileShortName, that means open the video file named aString" | fullName | self stopPlaying. fullName := FileDirectory default fullNameFor: aString. self openFileNamed: fullName! ! !GStreamerDisplayMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:33'! volume "Answer the sound playback volume." ^ volume ! ! !GStreamerDisplayMorph methodsFor: 'accessing' stamp: 'JMM 3/28/2008 12:23'! volume: aNumber "Set the sound playback volume to the given level, between 0.0 and 10.0." volume := aNumber asFloat. volume := volume max: 0.0. volume := volume min: 10.0. soundTrack ifNotNil: [gstreamer volume: volume]! ! !GStreamerDisplayMorph methodsFor: 'menu' stamp: 'JMM 3/27/2008 14:33'! doubleSize "change the receiver's extent to double of the normal size" self magnifyBy: 2! ! !GStreamerDisplayMorph methodsFor: 'menu' stamp: 'JMM 3/27/2008 14:33'! halfSize "change the receiver's extent to a half of the normal size" self magnifyBy: 1 / 2! ! !GStreamerDisplayMorph methodsFor: 'menu' stamp: 'JMM 3/28/2008 09:52'! invokeMenu "Invoke a menu of additonal functions." | aMenu | aMenu := MenuMorph new. aMenu defaultTarget: self. aMenu addList: { {'open file (o)' translated. #openOGGFile}. #-. {'rewind (r)' translated. #rewindMovie}. {'play (p)' translated. #startPlaying}. {'stop (s)' translated. #stopPlaying}. {'previous frame (<-)' translated. #previousFrame}. {'next frame (->)' translated. #nextFrame}. #-. }. aMenu addLine. aMenu add: 'zoom' translated subMenu: self zoomSubMenu. aMenu add: 'advanced' translated subMenu: self advancedSubMenu. aMenu popUpEvent: self world activeHand lastEvent in: self world ! ! !GStreamerDisplayMorph methodsFor: 'menu' stamp: 'JMM 3/27/2008 14:33'! normalSize "change the receiver's extent to the normal size" self magnifyBy: 1! ! !GStreamerDisplayMorph methodsFor: 'menu' stamp: 'JMM 3/27/2008 15:19'! toggleFullScreen "Toggle the fullScreen flag." gstreamer isNil ifTrue: [^ self]. gstreamer hasVideo ifFalse: [^ self]. "" self fullScreen: self fullScreen not. "" "set screen size" self fullScreen ifTrue: ["" self extent: Display extent. World activeHand newMouseFocus: self. self comeToFront] ifFalse: [self extent: self normalExtent]. "" (self fullScreen and: [self owner isKindOf: GStreamerMoviePlayerMorph]) ifTrue: [self owner position: -6 @ -6] ifFalse: [self owner == self world ifFalse: [self owner position: 0 @ 0] ifTrue:[self position:0@0]]. "" self nextFrame! ! !GStreamerDisplayMorph methodsFor: 'menu' stamp: 'JMM 3/27/2008 14:33'! toggleRepeat "Toggle the repeat flag." repeat := repeat not. ! ! !GStreamerDisplayMorph methodsFor: 'event handling' stamp: 'JMM 3/27/2008 14:33'! handlesKeyboard: evt ^ true! ! !GStreamerDisplayMorph methodsFor: 'event handling' stamp: 'JMM 3/27/2008 14:33'! handlesMouseDown: evt ^ evt yellowButtonPressed! ! !GStreamerDisplayMorph methodsFor: 'event handling' stamp: 'JMM 3/28/2008 09:52'! keyStroke: evt | char asc | char := evt keyCharacter. asc := char asciiValue. (char = $o or:[ char = $O]) ifTrue: ["open o/O" self openOGGFile. ^self]. (char = $m or:[ char = $M]) ifTrue: ["menu key m/M" self invokeMenu. ^self]. (char = $r or:[ char = $R]) ifTrue: ["rewind r/R" self rewindMovie. ^self]. (char = $p or:[ char = $P]) ifTrue: ["play p/P" self startPlaying. ^self]. (char = $s or:[ char = $S]) ifTrue: ["stop s/S" self stopPlaying. ^self]. (asc = 28) ifTrue: [ "left arrow key" self previousFrame. ^self]. (asc = 29) ifTrue: [ "right arrow key" self nextFrame. ^self]. ! ! !GStreamerDisplayMorph methodsFor: 'event handling' stamp: 'JMM 3/27/2008 14:33'! mouseDown: evt evt yellowButtonPressed ifTrue: [^ self invokeMenu]. super mouseDown: evt! ! !GStreamerDisplayMorph methodsFor: 'initialization' stamp: 'JMM 3/28/2008 11:19'! initialize "initialize the state of the receiver" super initialize."" super extent: 250 @ 0. frameBuffer := nil. gstreamer := nil. running := false. repeat := false. soundTrack := nil. volume := 1.0. fullScreen := false.! ! !GStreamerDisplayMorph methodsFor: 'commands' stamp: 'JMM 3/28/2008 11:29'! nextFrame "Fetch the next frame into the frame buffer." gstreamer ifNil: [^ self]. gstreamer processVideoGiveTo: frameBuffer. self changed. ! ! !GStreamerDisplayMorph methodsFor: 'commands' stamp: 'JMM 3/27/2008 14:33'! playUntilPosition: finalPosition "Play the movie until the given position, then stop." | totalFrames | totalFrames := self totalFrames. (totalFrames > 0 and: [finalPosition > 0]) ifFalse: [^ self]. "do nothing" self startPlaying. stopFrame := (finalPosition * totalFrames) asInteger min: totalFrames! ! !GStreamerDisplayMorph methodsFor: 'commands' stamp: 'JMM 3/28/2008 10:25'! previousFrame "Go to the previous frame." | | self halt. gstreamer ifNil: [^ self]. running ifTrue: [^ self]. self nextFrame. ! ! !GStreamerDisplayMorph methodsFor: 'commands' stamp: 'JMM 3/28/2008 11:22'! rewindMovie "Rewind to the beginning of the movie." self stopPlaying. gstreamer rewind! ! !GStreamerDisplayMorph methodsFor: 'commands' stamp: 'JMM 4/1/2008 14:15'! startPlaying "Start playing the movie at the current position." | fileName | self stopPlaying. stopFrame := nil. (gstreamer isNil or: [(FileStream isAFileNamed: gstreamer fileName) not]) ifTrue: [ | newFileResult newFileName | fileName := gstreamer ifNil: ['none choosen'] ifNotNil: [FileDirectory localNameFor: gstreamer fileName]. self inform: 'Path changed. Enter new one for: ', fileName. newFileResult := StandardFileMenu oldFile. newFileName := newFileResult directory fullNameFor: newFileResult name. self openFileNamed: newFileName]. gstreamer hasAudio ifTrue: [gstreamer hasVideo ifTrue: ["set movie frame position from soundTrack position" soundTrack reset. "ensure file is open before positioning"]. SoundPlayer stopReverb. gstreamer volume: volume] ifFalse: [soundTrack := nil.]. gstreamer play. running := true! ! !GStreamerDisplayMorph methodsFor: 'commands' stamp: 'JMM 3/28/2008 10:43'! stopPlaying "Stop playing the movie." running := false. gstreamer ifNotNil: [gstreamer pause]. soundTrack ifNotNil: [soundTrack pause]. ! ! !GStreamerMoviePlayerMorph methodsFor: 'private' stamp: 'JMM 3/28/2008 09:52'! addButtonRow "private - add the button row" | r | r := AlignmentMorph newRow vResizing: #shrinkWrap; color: Color transparent; listCentering: #center. r addMorphBack: (self buttonName: 'Menu' translated action: #invokeMenu). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). r addMorphBack: (self buttonName: 'Open' translated action: #openOGGFile). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). r addMorphBack: (self buttonName: 'Rewind' translated action: #rewindMovie). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). r addMorphBack: (self buttonName: 'Play' translated action: #startPlaying). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). r addMorphBack: (self buttonName: 'Stop' translated action: #stopPlaying). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). " r addMorphBack: (self buttonName: '<' action: #previousFrame). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). r addMorphBack: (self buttonName: '>' action: #nextFrame). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). r addMorphBack: (self buttonName: 'Subtitles' translated action: #openSubtitlesFile). r addMorphBack: (Morph new extent: 3@1; color: Color transparent). " r addMorphBack: (self buildQuitButton). self addMorphBack: r. ^ r fullBounds extent. ! ! !GStreamerMoviePlayerMorph methodsFor: 'private' stamp: 'JMM 3/27/2008 14:02'! addPositionSlider "private - add the position slider" | r | positionSlider := SimpleSliderMorph new color: (Color r: 0.71 g: 0.871 b: 1.0); extent: 200@2; target: moviePlayer; actionSelector: #moviePosition:; adjustToValue: 0. r := AlignmentMorph newRow color: Color transparent; layoutInset: 0; wrapCentering: #center; cellPositioning: #leftCenter; listCentering: #center; hResizing: #shrinkWrap; vResizing: #rigid; height: 24. r addMorphBack: (StringMorph contents: 'start ' translated). r addMorphBack: positionSlider. r addMorphBack: (StringMorph contents: ' end' translated). self addMorphBack: r. ! ! !GStreamerMoviePlayerMorph methodsFor: 'private' stamp: 'sw 9/5/2007 15:26'! addPositionSliderIn: anExtent "private - add the position slider" | r | positionSlider := SimpleSliderMorph new color: (Color r: 0.653 g: 0.802 b: 0.92); extent: ((anExtent x*0.8)@(anExtent y * 0.5)) asIntegerPoint; target: moviePlayer; actionSelector: #moviePosition:; adjustToValue: 0. r := AlignmentMorph newRow color: Color transparent; layoutInset: 0; wrapCentering: #center; cellPositioning: #leftCenter; listCentering: #center; hResizing: #shrinkWrap; vResizing: #rigid; height: anExtent y. r addMorphBack: (StringMorph contents: 'start ' translated font: Preferences standardEToysButtonFont). r addMorphBack: positionSlider. r addMorphBack: (StringMorph contents: ' end' translated font: Preferences standardEToysButtonFont). self addMorphBack: r. ! ! !GStreamerMoviePlayerMorph methodsFor: 'private' stamp: 'JMM 3/27/2008 14:02'! addVolumeSlider "private - add the volume slider" | r | volumeSlider := SimpleSliderMorph new color: (Color r: 0.71 g: 0.871 b: 1.0); extent: 200@2; target: moviePlayer; actionSelector: #volume:; adjustToValue: 0.5. r := AlignmentMorph newRow color: Color transparent; layoutInset: 0; wrapCentering: #center; cellPositioning: #leftCenter; listCentering: #center; hResizing: #shrinkWrap; vResizing: #rigid; height: 24. r addMorphBack: (StringMorph contents: ' soft ' translated). r addMorphBack: volumeSlider. r addMorphBack: (StringMorph contents: ' loud' translated). self addMorphBack: r. ! ! !GStreamerMoviePlayerMorph methodsFor: 'private' stamp: 'sw 9/5/2007 15:26'! addVolumeSliderIn: anExtent "private - add the volume slider" | r | volumeSlider := SimpleSliderMorph new color: (Color r: 0.653 g: 0.802 b: 0.92); extent: ((anExtent x*0.8)@(anExtent y * 0.5)) asIntegerPoint; target: moviePlayer; actionSelector: #volume:; adjustToValue: 0.5. r := AlignmentMorph newRow color: Color transparent; layoutInset: 0; wrapCentering: #center; cellPositioning: #leftCenter; listCentering: #center; hResizing: #shrinkWrap; vResizing: #rigid; height: anExtent y. r addMorphBack: (StringMorph contents: ' soft ' translated font: Preferences standardEToysButtonFont). r addMorphBack: volumeSlider. r addMorphBack: (StringMorph contents: ' loud' translated font: Preferences standardEToysButtonFont). self addMorphBack: r. ! ! !GStreamerMoviePlayerMorph methodsFor: 'private' stamp: 'JMM 3/27/2008 14:02'! buildQuitButton "private - create the [quit] button" ^ self buttonName: 'Quit' translated target: self action: #quit! ! !GStreamerMoviePlayerMorph methodsFor: 'private' stamp: 'JMM 3/27/2008 14:02'! buttonFillStyle | fill | fill := GradientFillStyle ramp: { 0.0->(Color r: 0.742 g: 0.903 b: 1.0). 1.0->(Color r: 0.516 g: 0.71 b: 1.0) }. fill origin: self bounds topLeft + (14@3). fill direction: 8@6. fill radial: false. ^ fill ! ! !GStreamerMoviePlayerMorph methodsFor: 'private' stamp: 'JMM 3/27/2008 14:02'! buttonName: aString action: aSymbol ^ self buttonName: aString target: moviePlayer action: aSymbol ! ! !GStreamerMoviePlayerMorph methodsFor: 'private' stamp: 'sw 9/5/2007 15:26'! buttonName: aString target: anObject action: selector "private - create a button" ^ SimpleButtonMorph new target: anObject; label: aString font: Preferences standardEToysButtonFont; actionSelector: selector; color: (Color gray: 0.8); "old color" fillStyle: self buttonFillStyle; borderWidth: 0; borderColor: #raised. ! ! !GStreamerMoviePlayerMorph methodsFor: 'private' stamp: 'JMM 3/27/2008 14:02'! quit "quit the receiver" self delete! ! !GStreamerMoviePlayerMorph methodsFor: 'initialization' stamp: 'JMM 3/27/2008 14:02'! defaultColor "answer the default color/fill style for the receiver" | fill | fill := GradientFillStyle ramp: {0.0 -> (Color r: 0.355 g: 0.548 b: 1.0). 1.0 -> (Color r: 0.774 g: 0.935 b: 1.0)}. fill origin: self bounds topLeft + (61 @ 7). fill direction: 33 @ 37. fill radial: false. ^ fill! ! !GStreamerMoviePlayerMorph methodsFor: 'initialization' stamp: 'JMM 3/28/2008 10:38'! initialize "initialize the state of the receiver" | ext | super initialize. "" self hResizing: #shrinkWrap; vResizing: #shrinkWrap. borderWidth := 2. self listDirection: #topToBottom. self cornerStyle: #rounded. self layoutInset: 4. moviePlayer := GStreamerDisplayMorph new. self addMorphFront: moviePlayer. ext := self addButtonRow. self addVolumeSliderIn: ext. self addPositionSliderIn: ext. self extent: 10 @ 10! ! !GStreamerMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'JMM 3/27/2008 14:02'! defaultFloatPrecisionFor: aGetSelector "Answer a number indicating the default float precision to be used in a numeric readout for which the receiver provides the data. Individual morphs can override this. Showing fractional values for readouts of getCursor was in response to an explicit request from ack" aGetSelector == #getVolume ifTrue: [^ 0.01]. aGetSelector == #getPosition ifTrue: [^ 0.001]. ^ super defaultFloatPrecisionFor: aGetSelector! ! !GStreamerMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'JMM 3/27/2008 14:02'! getCurrentFrameForm "Answer a Form containing the current frame scaled to the size of my display." ^ moviePlayer currentFrameScaled ! ! !GStreamerMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'JMM 3/27/2008 14:02'! getCurrentFrameImageMorph "Answer an ImageMorph containing the current frame scaled to the size of my display." ^ ImageMorph new image: (moviePlayer currentFrameScaled) ! ! !GStreamerMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'JMM 3/27/2008 14:02'! getIsRunning "answer whateve the receiver is running" ^ moviePlayer isRunning! ! !GStreamerMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'JMM 3/27/2008 14:02'! getRepeat "answer whateve the receiver is running" ^ moviePlayer repeat! ! !GStreamerMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'JMM 3/27/2008 14:02'! getVideoFileName "answer the receiver's videoFileName" ^ moviePlayer videoFileShortName! ! !GStreamerMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'JMM 3/27/2008 14:02'! play "play the receiver" moviePlayer startPlaying! ! !GStreamerMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'JMM 3/27/2008 14:02'! playUntilPosition: finalPosition "Play the movie until the given position, then stop." moviePlayer playUntilPosition: finalPosition! ! !GStreamerMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'JMM 3/27/2008 14:02'! rewind "rewind the receiver" moviePlayer rewindMovie! ! !GStreamerMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'JMM 3/27/2008 14:02'! setRepeat: aBoolean "answer whateve the receiver is running" moviePlayer repeat: aBoolean! ! !GStreamerMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'JMM 3/27/2008 14:02'! setVideoFileName: aString "change the videoFileName" moviePlayer videoFileShortName: aString! ! !GStreamerMoviePlayerMorph methodsFor: 'e-toy support' stamp: 'JMM 3/27/2008 14:02'! stop "stop the receiver" moviePlayer stopPlaying! ! !GStreamerMoviePlayerMorph methodsFor: 'submorphs-add/remove' stamp: 'JMM 3/27/2008 14:02'! delete "the receiver is being deleted" moviePlayer stopPlaying. moviePlayer closeFile. "" super delete! ! !GStreamerMoviePlayerMorph methodsFor: 'drawing' stamp: 'JMM 3/27/2008 14:02'! drawOn: aCanvas "Optimization: Do not draw myself if the movie player is one of my submorphs and the only damage is contained within it. This avoids overdrawing while playing a movie." ((moviePlayer owner == self) and: [moviePlayer bounds containsRect: aCanvas clipRect]) ifFalse: [super drawOn: aCanvas]. ! ! !GStreamerMoviePlayerMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:02'! getPosition "answer the receiver's movie position" ^ positionSlider getScaledValue! ! !GStreamerMoviePlayerMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:02'! getVolume "answer the receiver's movie position" ^ self volumeSlider isNil ifFalse:[self volumeSlider getScaledValue] ifTrue:[0.0]! ! !GStreamerMoviePlayerMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:02'! guessVolumeSlider "private - look for a morph that is the receiver's volumeSlider" ^ self allMorphs detect: [:each | "first look in my own morphs" each class == SimpleSliderMorph and: [each actionSelector == #volume:]] ifNone: [| w | "second try, look all over the world (if any)" w := self world. w isNil ifFalse: ["" w allMorphs detect: [:each | "" each class == SimpleSliderMorph and: [each actionSelector == #volume:] and: [each target == moviePlayer]] ifNone: []]]! ! !GStreamerMoviePlayerMorph methodsFor: 'accessing' stamp: 'JMM 3/31/2008 12:02'! moviePlayer ^moviePlayer! ! !GStreamerMoviePlayerMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:02'! setPosition: aNumber "changes the receiver's movie position" | newPosition | newPosition := aNumber asFloat min: 1.0 max: 0.0. positionSlider value: newPosition. moviePlayer moviePosition: newPosition! ! !GStreamerMoviePlayerMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:02'! setVolume: aNumber "changes the receiver's movie position" | newVolume | newVolume := aNumber asFloat min: 1.0 max: 0.0. self volumeSlider isNil ifFalse:[self volumeSlider value: newVolume]. moviePlayer volume: newVolume! ! !GStreamerMoviePlayerMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:02'! totalFrames "Answer the total number of frames in this movie." ^ moviePlayer totalFrames! ! !GStreamerMoviePlayerMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:02'! totalSeconds "Answer the total number of seconds in this movie." ^ moviePlayer totalSeconds! ! !GStreamerMoviePlayerMorph methodsFor: 'accessing' stamp: 'JMM 3/27/2008 14:02'! volumeSlider "answer the receiver's volumeSlider note: if the instance var is undefined, try to get the sliders from the allMorphs chain. in this way an instance of the receiver created before the instVars was added can works fine" ^ volumeSlider ifNil: [volumeSlider := self guessVolumeSlider]! ! !GStreamerMoviePlayerMorph methodsFor: 'event handling' stamp: 'JMM 3/27/2008 14:02'! handlesKeyboard: evt ^ moviePlayer handlesKeyboard: evt! ! !GStreamerMoviePlayerMorph methodsFor: 'event handling' stamp: 'JMM 3/27/2008 14:02'! keyStroke: evt moviePlayer keyStroke: evt ! ! !GStreamerMoviePlayerMorph methodsFor: 'stepping' stamp: 'JMM 3/27/2008 14:02'! step "Update the position slider from the current movie position." positionSlider adjustToValue: moviePlayer moviePosition. ! ! !GStreamerMoviePlayerMorph methodsFor: 'stepping' stamp: 'JMM 3/27/2008 14:02'! stepTime "Update the position slider a few times a second." ^ 500 ! ! !GStreamerMoviePlayerMorph class methodsFor: 'scripting' stamp: 'JMM 3/27/2008 14:13'! additionsToViewerCategories "Answer a list of ( ) pairs that characterize the phrases this kind of morph wishes to add to various Viewer categories." ^ #( (basic ( (command play 'Start playing the movie/sound') (command stop 'Stop playing the movie/sound') (command rewind 'Rewind the movie/sound'))) (#'movie controls' ( (slot videoFileName 'The name for the video file' String readWrite Player getVideoFileName Player setVideoFileName:) (slot position 'A number representing the current position of the movie/sound.' Number readWrite Player getPosition Player setPosition:) (slot volume 'A number representing the volume of the movie.' Number readWrite Player getVolume Player setVolume:) (command play 'Start playing the movie/sound') (command playUntilPosition: 'Play until the given position, then stop' Number) (command stop 'Stop playing the movie/sound') (command rewind 'Rewind the movie/sound') (slot isRunning 'Whether the movie/sound is being played' Boolean readOnly Player getIsRunning unused unused) (slot repeat 'Whether the movie/sound will play in an endless loop' Boolean readWrite Player getRepeat Player setRepeat:) (slot totalFrames 'Length of this movie in number of frames' Number readOnly Player getTotalFrames unused unused) (slot totalSeconds 'Length of this movie in seconds' Number readOnly Player getTotalSeconds unused unused) (slot frameGraphic 'A graphic for the current frame' Graphic readOnly Player getFrameGraphic unused unused) ) ) )! ! !GStreamerMoviePlayerMorph class methodsFor: 'parts bin' stamp: 'jcg 5/10/2008 18:37'! descriptionForPartsBin ^ self partName: 'GStreamerPlayer' categories: {'Multimedia' } documentation: 'A player for OGG movies ' ! ! !GStreamerMoviePlayerMorph class methodsFor: 'fileIn/Out' stamp: 'JMM 3/27/2008 14:19'! fileReaderServicesForFile: fullName suffix: suffix ^((GStreamerPlayer registeredVideoFileSuffixes includes: suffix ) or: [ (GStreamerPlayer registeredAudioFileSuffixes includes: suffix) or: [ suffix = '*' ]] ) ifTrue: [ self services ] ifFalse: [ #() ]! ! !GStreamerMoviePlayerMorph class methodsFor: 'fileIn/Out' stamp: 'JMM 3/27/2008 14:17'! services "Answer the receiver's services" ^ OrderedCollection with: self servicePlayInGStreamerPlayer with: self serviceOpenInGStreamerPlayer ! ! !GStreamerMoviePlayerMorph class methodsFor: 'class initialization' stamp: 'JMM 3/27/2008 14:14'! initialize "GStreamerMoviePlayerMorph initialize." FileList registerFileReader: self. self registerInFlapsRegistry. ! ! !GStreamerMoviePlayerMorph class methodsFor: 'class initialization' stamp: 'JMM 3/27/2008 14:41'! registerInFlapsRegistry "Register the receiver in the system's flaps registry" self environment at: #Flaps ifPresent: [:cl | cl registerQuad: {#GStreamerMoviePlayerMorph. #authoringPrototype. 'Movie Player' translatedNoop. 'A Player for OGG movies' translatedNoop} forFlapNamed: 'Widgets'] ! ! !GStreamerMoviePlayerMorph class methodsFor: 'class initialization' stamp: 'JMM 3/27/2008 14:02'! unload "Unload the receiver from global registries" self environment at: #Flaps ifPresent: [:cl | cl unregisterQuadsWithReceiver: self]. FileList unregisterFileReader: self.! ! !GStreamerMoviePlayerMorph class methodsFor: 'registering' stamp: 'JMM 3/27/2008 14:02'! openFile: aFileName "Open the given file (if not nil) in an instance of the receiver." | wrapper | aFileName ifNil: [^ Beeper beep]. wrapper := self openOn: aFileName. wrapper openInWorld. ^ wrapper! ! !GStreamerMoviePlayerMorph class methodsFor: 'registering' stamp: 'JMM 3/31/2008 11:55'! openOn: fileNameString "open a new instance of the receiver on a file named fileNameString " | wrapper | wrapper := self new. wrapper moviePlayer openFileNamed: fileNameString. ^ wrapper! ! !GStreamerMoviePlayerMorph class methodsFor: 'registering' stamp: 'JMM 3/27/2008 14:21'! playFile: aFileName "Play the given file (if not nil) in an MPEGMoviePlayerMorph" | wrapper | aFileName ifNil: [^ Beeper beep]. wrapper := self openOn: aFileName. wrapper moviePlayer startPlaying. wrapper openInWorld. ^wrapper! ! !GStreamerMoviePlayerMorph class methodsFor: 'registering' stamp: 'JMM 3/27/2008 14:17'! serviceOpenInGStreamerPlayer "Answer a service for opening a file in an GStreamerMoviePlayer" ^ SimpleServiceEntry provider: self label: 'open' selector: #openFile: description: 'open file in an GStreamer player' buttonLabel: 'open'! ! !GStreamerMoviePlayerMorph class methodsFor: 'registering' stamp: 'JMM 3/27/2008 14:16'! servicePlayInGStreamerPlayer "Answer a service for opening in a GStreamer player" ^ SimpleServiceEntry provider: self label: 'play in GStreamer player' selector: #playFile: description: 'play in GStreamer player' buttonLabel: 'play'! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 3/31/2008 13:12'! audiosink (SmalltalkImage current platformName = 'Mac OS') ifTrue: [^'osxaudiosink']. "Linux systems but most importantly OLPC" ^'alsasink'! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 3/27/2008 15:20'! fileName ^self filesrc! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 3/27/2008 15:16'! filesrc "Answer the value of filesrc" ^ filesrc! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 3/27/2008 15:16'! filesrc: anObject "Set the value of filesrc" filesrc := anObject! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 4/1/2008 12:51'! getSimplifiedState self pipeLineIsSane ifFalse:[^nil]. ^self pipeLine getSimplifiedState! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 3/27/2008 15:16'! pipeLine "Answer the value of pipeLine" ^ pipeLine! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 4/1/2008 12:50'! pipeLineIsSane ^pipeLine notNil and: [pipeLine isHandleSane]! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 3/27/2008 15:16'! pipeLine: anObject "Set the value of pipeLine" pipeLine := anObject! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 4/1/2008 12:53'! queryPositionPercent | time where | self pipeLineIsSane ifFalse:[^0.0]. time := self pipeLine queryDurationTime. time ifNil: [^0.0]. where := self pipeLine queryPositionTime. where ifNil: [^0.0]. ^where asFloat / time ! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 3/28/2008 13:38'! soundTrack "Answer the value of soundTrack" soundTrack ifNil: [SoundPlayer stopReverb. soundTrack := QueueSound new startTime: 0]. ^ soundTrack! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 3/28/2008 10:24'! totalFrames self halt. ! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 4/1/2008 12:53'! totalSeconds | time | self pipeLineIsSane ifFalse:[^0.0]. time := self pipeLine queryDurationTime. time ifNil: [^0.0]. ^time/1000000000.0.! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 4/1/2008 12:54'! videoFrameHeight self pipeLineIsSane ifFalse:[^1]. self pipeLine checkDimension. ^self pipeLine height! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 4/1/2008 12:54'! videoFrameRate self pipeLineIsSane ifFalse:[^1]. self pipeLine checkDimension. ^self pipeLine fps.! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 4/1/2008 12:54'! videoFrameWidth self pipeLineIsSane ifFalse:[^1]. self pipeLine checkDimension. ^self pipeLine width! ! !GStreamerPlayer methodsFor: 'accessing' stamp: 'JMM 3/28/2008 11:20'! volume: aValue volumePlayer setKey: 'volume' toDoubleValue: aValue asFloat! ! !GStreamerPlayer methodsFor: 'actions' stamp: 'JMM 4/1/2008 13:21'! closeFile volumePlayer := nil. self pipeLineIsSane ifTrue: [self setStateToThisValue: #paused. self pipeLine release. self pipeLine: nil. ]! ! !GStreamerPlayer methodsFor: 'actions' stamp: 'JMM 4/1/2008 13:21'! pause self setStateToThisValue: #paused! ! !GStreamerPlayer methodsFor: 'actions' stamp: 'JMM 4/1/2008 13:23'! play self setStateToThisValue: #playing. self soundTrack play. ! ! !GStreamerPlayer methodsFor: 'actions' stamp: 'JMM 4/1/2008 12:53'! rewind self pipeLineIsSane ifFalse:[^self]. self pipeLine seekToTimeInSeconds: 0. self processAudioGiveTo: nil. " clean out pending data" self pause.! ! !GStreamerPlayer methodsFor: 'actions' stamp: 'JMM 4/1/2008 12:53'! seekToTimeInSecondsEnsurePlaying: seconds self pipeLineIsSane ifFalse:[^nil]. self hasVideo ifFalse: [self pipeLine seekToTimeInSecondsEnsurePlaying: seconds] ifTrue: [self pause. self halt. self pipeLine seekToTimeInSecondsEnsurePlaying: seconds. self play] ! ! !GStreamerPlayer methodsFor: 'actions' stamp: 'JMM 4/1/2008 14:23'! setStateToThisValue: newState self pipeLineIsSane ifFalse:[^nil]. self pipeLine setStateToThisValue: newState! ! !GStreamerPlayer methodsFor: 'testing' stamp: 'JMM 3/28/2008 14:26'! hasAudio ^hasAudio! ! !GStreamerPlayer methodsFor: 'testing' stamp: 'JMM 3/28/2008 14:26'! hasVideo ^hasVideo! ! !GStreamerPlayer methodsFor: 'initialize-release' stamp: 'JMM 3/27/2008 15:16'! initialize filesrc := '/Users/johnmci/Shared/072_Free_as_in_Market_the_misunderstood_entanglement_of_ethics_software_and_profits.ogg'! ! !GStreamerPlayer methodsFor: 'initialize-release' stamp: 'jcg 4/22/2008 11:18'! oggHookupToSqueakAudio | oggdemux vorbisdec vorbisdecSinkPad file audiosink audioconvert result | "gst-launch filesrc location=test1.ogg !! vorbisdec !! audioconvert !! ..." "Note QUEUES are needed, otherwise the video/sound will not play" file := GStreamerElement elementFactoryMake: 'filesrc' name: 'filesrc'. file setKey: 'location' toStringValue: self filesrc. oggdemux := GStreamerElement elementFactoryMake: 'oggdemux' name: 'oggdemux'. vorbisdec := GStreamerElement elementFactoryMake: 'vorbisdec' name: 'vorbisdec'. audiosink := GStreamerElement elementFactoryMake: self audiosink name: 'audiosink'. audioconvert := GStreamerElement elementFactoryMake: 'audioconvert' name: 'audioconvert'. volumePlayer := GStreamerElement elementFactoryMake: 'volume' name: 'volume'. "Setup the pipeline" pipeLine := GStreamerPipeline name: 'my-pipeline'. pipeLine addElement: file. pipeLine addElement: oggdemux. pipeLine addElement: vorbisdec. pipeLine addElement: audiosink. pipeLine addElement: audioconvert. pipeLine addElement: volumePlayer. result := GStreamer default linkElementSrc: file toDest: oggdemux. result := GStreamer default linkElementSrc: vorbisdec toDest: audioconvert. result := GStreamer default linkElementSrc: audioconvert toDest: volumePlayer. result := GStreamer default linkElementSrc: volumePlayer toDest: audiosink. "Now lets setup the dynamic pad" vorbisdecSinkPad := vorbisdec requestStaticPadByName: 'sink'. oggdemux requestCallBackForSignal: 'pad-added' useArray: (Array with: vorbisdecSinkPad). hasVideo := false. hasAudio := true. ! ! !GStreamerPlayer methodsFor: 'initialize-release' stamp: 'jcg 4/22/2008 10:53'! oggHookupToSqueakAudioAndVideo | oggdemux vorbisdec vorbisdecSinkPad result file audiosink audioconvert theoradec ffmpegcolorspace ximagesink theoradecSinkPad queueVideo oggdemuxSinkPad fileSrcPad error audioresample caps capsfilterVideo | "Play both audio and video using a mux, this uses two dynamically created pads just to show how they are done" "gst-launch filesrc location=test1.ogg !! oggdemux name=demux demux. !! vorbisdec !! audioconvert !! SqueakAudioSink demux. !! queue !! theoradec !! ffmpegcolorspace !! SqueakVideoSink" "Note QUEUES are needed, otherwise the video/sound will not play" file := GStreamerElement elementFactoryMake: 'filesrc' name: 'filesrc'. file setKey: 'location' toStringValue: self filesrc. oggdemux := GStreamerElement elementFactoryMake: 'oggdemux' name: 'oggdemux'. vorbisdec := GStreamerElement elementFactoryMake: 'vorbisdec' name: 'vorbisdec'. audiosink := GStreamerElement elementFactoryMake: self audiosink name: 'osxaudiosink'. audioconvert := GStreamerElement elementFactoryMake: 'audioconvert' name: 'audioconvert'. audioresample := GStreamerElement elementFactoryMake: 'audioresample' name: 'audioresample'. theoradec := GStreamerElement elementFactoryMake: 'theoradec' name: 'theoradec'. ffmpegcolorspace := GStreamerElement elementFactoryMake: 'ffmpegcolorspace' name: 'ffmpegcolorspace'. caps := Display depth = 32 ifTrue: [GStreamerPipelineSqueakSink videoCapsBig32] ifFalse: [GStreamerPipelineSqueakSink videoCapsBig16]. capsfilterVideo := GStreamerElement elementFactoryMake: 'capsfilter' name: 'capsfilterVideo'. capsfilterVideo setKey: 'caps' toPointerValue: caps handle. ximagesink := GStreamerElementForSqueakSinkVideo elementFactoryMake: 'fakesink' name: 'fakesinkVideo'. ximagesink setKey: 'sync' toBooleanValue: true. queueVideo := GStreamerElement elementFactoryMake: 'queue' name: 'queueVideo'. volumePlayer := GStreamerElement elementFactoryMake: 'volume' name: 'volume'. "Setup the pipeline" pipeLine := GStreamerPipelineSqueakVideoSink name: 'my-pipeline'. result := pipeLine addElement: oggdemux. result := pipeLine addElement: vorbisdec. result := pipeLine addElement: file. result := pipeLine addElement: audiosink. result := pipeLine addElement: audioconvert. result := pipeLine addElement: audioresample. result := pipeLine addElement: theoradec. result := pipeLine addElement: ffmpegcolorspace. result := pipeLine addElement: ximagesink. result := pipeLine addElement: capsfilterVideo. result := pipeLine addElement: queueVideo. result := pipeLine addElement: volumePlayer. " NOTE SETUP OF SEMAPHORE AND REGISTER STEP" pipeLine setupSemaphore. pipeLine register. result := GStreamer default linkElementSrc: vorbisdec toDest: audioconvert. result := GStreamer default linkElementSrc: audioconvert toDest: audioresample. result := GStreamer default linkElementSrc: audioresample toDest: volumePlayer. result := GStreamer default linkElementSrc: volumePlayer toDest: audiosink. result := GStreamer default linkElementSrc: queueVideo toDest: theoradec. result := GStreamer default linkElementSrc: theoradec toDest: ffmpegcolorspace. result := GStreamer default linkElementSrc: ffmpegcolorspace toDest: capsfilterVideo. result := GStreamer default linkElementSrc: capsfilterVideo toDest: ximagesink. "Ok here we get the demux sink pad and hook to the filesrc we could just hook the elements together but let's explore hooking pad to pad" vorbisdecSinkPad := vorbisdec requestStaticPadByName: 'sink'. theoradecSinkPad := queueVideo requestStaticPadByName: 'sink'. oggdemuxSinkPad := oggdemux requestStaticPadByName: 'sink'. fileSrcPad := file requestPadThatIsCompatibleTo: oggdemuxSinkPad capsFilter: oggdemuxSinkPad getCaps. error := GStreamer default linkPadSrc: fileSrcPad toDest: oggdemuxSinkPad. "Now lets setup the dynamic pad" oggdemux requestCallBackForSignal: 'pad-added' useArray: (Array with: vorbisdecSinkPad with: theoradecSinkPad). hasVideo := true. hasAudio := true.! ! !GStreamerPlayer methodsFor: 'initialize-release' stamp: 'jcg 4/22/2008 11:18'! oggHookupToSqueakAudioViaSqueakSink | oggdemux vorbisdec vorbisdecSinkPad result file audiosink audioconvert audioresample caps capsfilter | "gst-launch filesrc location=test1.ogg !! vorbisdec !! audioconvert !! ..." "Note QUEUES are needed, otherwise the video/sound will not play" file := GStreamerElement elementFactoryMake: 'filesrc' name: 'filesrc'. file setKey: 'location' toStringValue: self filesrc. oggdemux := GStreamerElement elementFactoryMake: 'oggdemux' name: 'oggdemux'. vorbisdec := GStreamerElement elementFactoryMake: 'vorbisdec' name: 'vorbisdec'. caps := GStreamerPipelineSqueakSink audioCapsLittle. capsfilter := GStreamerElement elementFactoryMake: 'capsfilter' name: 'capsfilter'. capsfilter setKey: 'caps' toPointerValue: caps handle. audiosink := GStreamerElementForSqueakSinkAudio elementFactoryMake: 'fakesink' name: 'fakesinkAudio'. audiosink setKey: 'sync' toBooleanValue: true. audioconvert := GStreamerElement elementFactoryMake: 'audioconvert' name: 'audioconvert'. audioresample := GStreamerElement elementFactoryMake: 'audioresample' name: 'audioresample'. volumePlayer := GStreamerElement elementFactoryMake: 'volume' name: 'volume'. "Setup the pipeline" pipeLine := GStreamerPipelineSqueakAudioSink name: 'my-pipeline'. result := pipeLine addElement: oggdemux. result := pipeLine addElement: vorbisdec. result := pipeLine addElement: file. result := pipeLine addElement: audiosink. result := pipeLine addElement: audioconvert. result := pipeLine addElement: capsfilter. result := pipeLine addElement: audioresample. result := pipeLine addElement: volumePlayer. " NOTE SETUP OF SEMAPHORE AND REGISTER STEP" pipeLine setupSemaphore. pipeLine register. result := GStreamer default linkElementSrc: file toDest: oggdemux. result := GStreamer default linkElementSrc: vorbisdec toDest: audioconvert. result := GStreamer default linkElementSrc: audioconvert toDest: audioresample. result := GStreamer default linkElementSrc: audioresample toDest: volumePlayer. result := GStreamer default linkElementSrc: volumePlayer toDest: capsfilter. result := GStreamer default linkElementSrc: capsfilter toDest: audiosink. "Now lets setup the dynamic pad" vorbisdecSinkPad := vorbisdec requestStaticPadByName: 'sink'. oggdemux requestCallBackForSignal: 'pad-added' useArray: (Array with: vorbisdecSinkPad). hasVideo := false. hasAudio := true.! ! !GStreamerPlayer methodsFor: 'stepping' stamp: 'JMM 4/1/2008 12:52'! processAudioGiveTo: player self pipeLineIsSane ifFalse:[^nil]. self pipeLine getAudiodataIfFoundGiveTo: player! ! !GStreamerPlayer methodsFor: 'stepping' stamp: 'JMM 4/1/2008 12:52'! processVideoGiveTo: player self pipeLineIsSane ifFalse:[^nil]. ^self pipeLine getVideodataIfFoundGiveTo: player! ! !GStreamerPlayer class methodsFor: 'file list services' stamp: 'JMM 3/28/2008 19:04'! openFile: aFileName | player checkForPending | player := self new. player filesrc: aFileName. player oggHookupToSqueakAudioAndVideo. player pause. checkForPending := player getSimplifiedState. checkForPending = #pending ifTrue: [player closeFile. player oggHookupToSqueakAudio. player pause]. ^player! ! !GStreamerPlayer class methodsFor: 'file list services' stamp: 'JMM 3/27/2008 15:04'! registeredAudioFileSuffixes "Answer the file extensions for which MPEGPlayer registers audio services with FileList." "GStreamerPlayer registeredAudioFileSuffixes" ^{'ogg' } ! ! !GStreamerPlayer class methodsFor: 'file list services' stamp: 'JMM 3/27/2008 15:04'! registeredVideoFileSuffixes "Answer the file extensions for which MPEGPlayer registers video services with FileList." "GStreamerPlayer registeredVideoFileSuffixes" ^{ 'ogg'. } ! ! GStreamerMoviePlayerMorph initialize!