






Hotspots: Admin Pages | Turn-in Site |
Current Links: Case Final Project Summer 2007
DBMM-Milestone 6
Super-Secret Milestone 6: Reflective Components
- Now it should be possible to add components that can modify the way the movie is played back. The following components should be accessible to users:
- buttons that pause and resume the playback (one button that does both).
- buttons that skip to a certain position in the movie, specified as a time in seconds
- buttons that allow advanced users to insert code. The code should be able to access the movie object, and ask it to do things like pause or jump to a specific frame.
- Work out playing back a movie on a second OS, just as was done in Milestone 4. Again, submit instructions and a sample movie.
CLARIFICATION: the fancy buttons execute when the viewer clicks on them. Note that there are two users for the movie editor: the person creating a movie, and the person viewing a movie.
The David Bowie Design
It was immediately obivous that this milestone was going to require a new type of movie item. Facing this, we did what any sensible person faced with the circumstances would do: we made one. Because of some initial misunderstanding of the assignment, the class was named MovieCode, a subclass of MovieItem. The reason for it being called MovieCode, instead of something such as MovieButton, was that, at first, we didn't know we were going to be representing buttons in the movie itself; we figured that the user would be able to add buttons to a separate panel or something. Which meant that the part of the item that was internal to the movie was the code that the button would execute.
Even after finding out what the requirements were supposed to be, MovieCode doesn't seem like such a bad name. After all, the essence of the item is that it executes code. In this case, the code is specified by the pressing of a button that's displayed in the movie. But, it would be possible that one would want the execution of the code to be triggered by another action. So, while for the scope of this project, MovieCode might have been better as MovieButton, in the overall scheme of things, it turns out to be a better, and more general name.
If you're a little confused about why this matters, it's because, for this assignment, we were supposed to place on the "screen" playing the movie not only the regular items, such as MPEG movies, pictures, as well as playing WAVs and MIDIs, but also some buttons that have all the regular properties of a MovieItem as well as provide some interactivity for the user. One of the best ways to think about it is similar to a DVD movie, where not only can you view the movie, but at the beginning the movie usually provides a table of contents, where you can click on a certain area and be taken to a certain aspect of the movie.
One might also want to note that the author of this page is picky about class names.
A MovieCode item is meant to behave just like any other movie item–when it is time for it to be drawn on the screen, the message #activateOn: is sent, and to be removed, #deactivate is sent. The only main difference is that, because the item has to evaluate code that acts on the MovieEditor, it needs a reference to the MovieEditor.
Since each MovieCode item needs to execute some code, it obviously needs an instance variable to take care of this. Storing it as String works just fine, because then we can use Compiler#evaluate:. A point of consideration was how the user would reference the MovieEditor. We decided it would be easiest on the user if any reference made to the MovieEditor was made using the keyword self. Using this, we could use Compiler#evaluate:for:logged: to have the compiler evaluate code for a certain object. So calling this with Compiler evaluate: 'Transcript show: self className' for: model logged: false would print the class-name of the model object to the Transcript.
Because the user needs to be able to add predefined types of MovieCode items to the movie (the ones given for this milestone were the ability to jump to a certain second, and the ability to pause/resume the movie), we wanted to the MovieCode class to be able to generate MovieCode items of this type for us, instead of having to do it explicitily. Two class methods (3, actually in the end because of some more confusion about the jump to functionality) were created that perform these tasks. Essentially, they call another class method in the MovieCode, passing as the parameter that specifies the code that is to be executed the code for that predefined type.
To be able to jump to a certain second in the movie, more backend changes needed to be made. Since the MovieCode items reference the MovieEditor as their model, then the MovieEditor needs to be able to tell it's parts to jump to a certain second. Most of this would be done in the collection, though, so we knew we would need another method in the collection that jumps to certain second. Furthermore, wouldn't we make our lives a lot easier if we just gave each MovieItem the ability to jump to a certain second. So, #jumpTo: was going to be added to the MovieItem class, and some classes, such as MPEGMovies and MIDISequences would need to overwrite this to provide the specific behavior for their file.
While this may look like a lot of decisions made with respect to design, there weren't that many changes made, aside from the necessary addition of a new class, as well as some new methods in the back end classes. I'd reckon that deciding on this between the group probably took not more than 10-15 minutes of discussion. The only interface class that would need some modification would be the MovieItemDialog, since it is the interface between the user and the item itself and it needed to provide the functionality for the MovieCode items.
The David Bowie Implementation
MovieCode
Nothing earth-shaking here; the design had it all layed out for us, so it was pretty simple. I think the biggest issue was figuring out what the make the buttons look like. We made them look like the rest of our buttons on our GUI morphs.
Pause/Resume
Again, this was incredibly simple for us. During milestone 3, we decided that it would be nice to have the movie player function like most media players do, where the pause button not only pauses the media, but also restarts it. This was implemented at this point, so come milestone 6 it only required a message send. This type of predefined MovieCode was set up to execute the string 'self pause'.
Jumping to a second
This single aspect of the milestone probably took up 80-90% of development time, most because of tweaking that needed to be made and the fact that the lab computers don't have functioning sound, meaning that we had to do it at home, and fix it from there as well (with most of the work being done in the lab, keeping and updated copy of everything was easy; it gets kinda weird when people are changing stuff at home).
The first step was figuring out how to made the specific items jump to the right time within themselves. For Pictures and MovieCodes, this wasn't an issue, since the MovieItemCollection was set up to do this automattically (since the displaying of a MovieCode item is simply adding a morph, it doesn't need any kind of specific jumping mechanism). For MPEG movies and MIDI files, it was a bit more complicated.
To jump to a specific second in an MPEG movie, one needs to tell the MPEGFile in the MPEGDisplayMorph to jump to a certain second. Since the MPEGDisplayMorph didn't give us direct access to the MPEGFile by default, and there wasn't another way to do it without this access, a method was added to the MPEGDisplayMorph class, called getFile, that returns the MPEGFile currently used by that MPEGDisplayMorph. The bulk of the work for jumping to a second in an MPEG is in these three lines (aSecond is the second passed as the second to jump to):
| count mpegFile |
mpegFile _ aMPEGDisplayMorph getFile.
count _ aSecond ( mpegFile videoFrameRate: 0)
mpegFile videoSetFrame: count stream: 0.
aMPEGMorph nextFrame.
One should onte, however, that isn't all there is to it; some more work needs to be done for certain cases of jumping to a second in an MPEG, but this is essentially the work that needs to be done on the MPEGFile itself.
Jumping to a certain second in a MIDI file is even easier that jumping to a certain second in a MPEG. The idea is to figure out how many seconds there are per tick in the MIDI. Then, simply jump to the tick corresponding to the specified second. Don't forget to pause before doing this, and resumePlaying afterwards.
Now we needed to give the MovieItemCollection the ability to jump to a certain second. Throughout the first 5 milestones, the desgin was to have a sets of tables for playing the entire movie. One tells which movie items to end at at a certain timetime, and the other had information on which movie items to start at a particular second. In order to incorporate a way to jump to a particular second, there needed to be some small adjustments made.
The problem was that both tables were ordered by start/end time, and there was essentailly nothing in the middle. For example if a movie starts at second 0, the next one starts at second 5 then you couldn't just go to second 3 because there is no node for a second 3. The simplest way to overcome this was simply genearte a table that starts at the second the user wants to jump to. So if the user wanted to jump to second 5. We would generate a TimeTable and enforce it to have a starting TimeNode at second 5. Anything ended before that would never be added to the table and anything started after that would be added as normal.
Which brings us to the MovieEditor. Since the buttons are using this as their model, it needs to be able to communicate to the MovieItemCollection that there needs to be a jump performed. The addition of a few methods took care of this rather well. All jumping code, however, had to run the gauntlet before it could be accepted. The method #validateJumpToSecond accomplished this, making sure that the jump to second was a valid number, and that it wasn't beyond the bounds of the movie. In some cases, though, a user may want to specify a jump button before completing the rest of the movie. So, the user is given the option of allowing a button to jump beyond the last second, although if it's called during the playing of the movie and still tries to jump beyond the last second, nothing happens.
Now, for another story of our (my) confusion about this milestone. I interpreted the mechanism of jumping to a second in the movie as something like this: 1) the user sees a button that lets them jump to a second 2) the user presses said button 3) the movie maker prompts the user for a second to jump to 4) some validation stuff is done, and if the second passes, then boot-skoot over to that second in the movie. But! As I discovered from some newsgroup inqueries, that's just plain dumb! Would you want to specify the specific second in a DVD movie that you're watching? In most cases, it's highly doubtful. Instead, what I should have been doing was letting the editor of the movie specify the second that the button should jump to while designing the movie.
What to do now since I had already implemented the previous way? Well, why not have both? The #validateJumpToSecond function was terribly useful, and abstracting a method in MovieEditor called #jumpTo: into two methods was all it took on that side. But, I needed a way to differentiate between the two types of movies. So, the new buzzwords for the David Bowie Movie Maker became static jump-to and dynamic jump-to. As I'm sure you can guess, a static jump-to is one that is specified during movie-editing time, while a dynamic jump to is one where the second is specified during playback. How's that for robustness, 'eh?
Now, to do a dynamic jump-to, #jumpToSecond prompts the user for the second, and passes it to #jumpTo: which does validation and convertion and passes it to #doJumpTo:, the method that performs the nitty gritty jump-to stuff (so nitty gritty that it's two lines of code). For a static jump-to, validation is performed when the button is being added to the movie, and the movie code sends the #doJumpTo: message to the movie editor.
Fixing some other stuff
One probably he had that was left over from milestone 5 was that, when the user exited the Movie Maker, and the temporary directory was supposed to be deleted, more often than not, it wouldn't delete correctly, mostly because there were MPEG files lingering. In addition to this, if the user tried to open up a movie file that contained a file of the same name as one in the temporary directory, then it would get all pissy because it can't write over that file that's existing. As should be fairly obvious to even a mucous-laden infant, the file streams for the MPEG files weren't being closed. This was easy enough to fix, though, since MPEGDisplayMorph provides the #closeFiles method that would close the open MPEGFiles. So now, when the user exits, before we try to delete the files in the temporary directory, and the directory itself, we make sure that all the file streams are closed, via a method in the MovieItemCollection called #closeFiles (same name as the MPEGDisplayMorph method.. but different!). Having done this, the directory was able to be removed just fine.
User specified code
To create a MovieCode item that performs any code, predefined or not, one need simply call the rediculously long-named class method that takes in arguments that specify the code to be executed, the start time, stop time, position, model, and button label.
We needed to enable to user to edit this code. So, some special modifications were made to the MovieItemDialog whereby the user can edit a larger block of code. This is also the way that the user can edit previously existing MovieCode items, predefined or not. See, we figured flexibility is key here, and rather than just providing strictly predefined types of MovieCode items, it would be nice to have the predefined types be more of templates. This way, even if the user specifies a pause type button, they can still change the code via the MovieItem dialog (after it has been initially added). The same goes for a jump-to MovieCode; it's even more essential for this type of MovieCode, since the user should be able to edit the time that the MovieCode will jump to.
Opening a DBMM File In Linux
While opening a movie file in Linux was a bit more frustrating than making the movie open up automatically in Windows, the worst part of all of this was finding the 3.1alpha version of Squeak for Linux.
Once more, the Double-Clickable Movies, Step by Step page was a great help to us.
Running a specific movie from the command line, much like it is in Windows, is just a matter of executing the command:
Squeak Squeak3.1a.image PlayDavidBowieMovie.st SomeMovie.dbm
The problem for us was figuring out how all of these files are opened by Squeak. It seems reasonable that, putting this in a file, perhaps setting some of the properties as unix-script variables, and running it would work fair easily, right? Psha. On all of our early attempts, the error that we got when a Squeak window opened up was something reporting a syntax error, and it displayed the error, indicating where it was nestled in some HTML code. The hell? This part of the project, for that matter none of the project, had anything to do with HTML tags.
After fiddling around with every permutation of paths-before-files and otherwise we could come up with, we finally arrived at the solution. Apparently Squeak will look whereever you tell it for the image file, and whereever you tell it to look for the .dbm parameter, but god forbid you should try to specify the path of the .st file. This file had to be located in the default Squeak directory. After fixing this, it finally worked. To make it a little easier for the user to customize the script, most of the customizable stuff was abstracted into variables. The line that performs the execution was
${SQUEAKHOME}/${SQUEAKEXE} ${SQUEAKHOME}/${SQUEAKIMG} PlayDavidBowieMovie.st ${SQUEAKHOME}/$*
The stuff wrapped up in ${} are varables that the user can specify at the beginning of the file. The $* indicates the command-line arguments passed to the the script itself. Now, you can run and play your favourite David Bowie Movies quickly and easily with
$ PlayDavidBowieMovie SomeMovie.dbm
The world wasn't the same after this.
I apologize for my inability to stick with one point-of-view in writing this, as well as for any liberties I may have taken with the English language.
The Final Class Diagram
If you're into art, this should be your bag. The final version of the David Bowie Movie Maker class diagram. In very blurry gray scale. Sorry 'bout that.. it was very large.

Bonus!
For interested parties, here's the final zip file that we turned in of the David Bowie Movie Maker, complete with a read-me file by yours truly that was VERY poorly editted. The spelling mistakes and grammatical errors make this page look like Charles Dickens or something. There's also the sample movie we turned in with it and all of the auto loading stuff. This might prove very helpful if you want to learn a bit about easy FileStream stuff in Squeak, as well as how to deal with many different media types in the same.
P6-DBMMv4.zip
Requirements for running:
The David Bowie Movie Maker has been tested and known to run on Squeak 3.0, and Squeak 3.1a build 4164. In order to play MPEG files correctly, you need to have the MPEG Player plug-in, as well the classes that are in the MPEG-Player class (that is, MPEGDisplayMorph, MPEGMoviePlayerMorph, and something else we don't make use of). For optimum performace, use Squeak 3.1a build 4164, and run your image using the Squeak-D3D.exe executable; running with Squeak-GL.exe has produced poor results when we attempt to play a WAV sound (it plays it, but it sounds like crap).
Link to this Page