View this PageEdit this PageAttachments to this PageHistory of this PageHomeRecent ChangesSearch the SwikiHelp Guide
Hotspots: Admin Pages | Turn-in Site |
Current Links: Case Final Project Summer 2007

DBMM-Milestone 5

This is Ground Control to Major Tom...

Not-So-Secret Milestone 5: Saving to Disk



http://minnow.cc.gatech.edu/squeak/425 has some info on this, as does http://pbl.cc.gatech.edu/myswiki/137 (read the FAQ on automatic startup).



This is Major Tom to Ground Control...

This project seems to be a natural extension to any commercially viable movie player, and ours, while not in the least commercially viable, should probably include this functionality as well. After all, what's the fun in creating a masterpiece with our hunky-dory little Movie Maker and not being able to share it with the ones dearest to you who might not be able to see it directly?

The ability to save and load movie files was actually functionality that I was under the impression we were to account for in the 2nd (design) and 3rd (initial implementation) milestones. My team members set me straight on this, though. Even so, I think we all suspected that this would be something we would probably have to do in the future.

Oh yeah, there's that part about playing the movies "automatically," but I'll get to that later.

Design changes


As I'm sure you are aware, one of the most important steps that one can take before embarking on a programming project (especially with regard to projects in an Object Oriented Programming Language), is developing a design for the project. The design we came up with for the David Bowie Movie Maker is pretty dadgum solid, so the number of changes that needed to be made in order for us to implement reading and writing files was few. Mainly, it necessitated the writing of two classes: one to read in the file, and one to write to the file. Furthermore, it was determined that, in writing to the file, it would be best to let the MovieItems themselves indicate how they would like to be represented in the file.

The class that writes to the file, called MovieFileWriter, simply asks each of the MovieItems currently stored in the MovieEditor to generate a string representing themselves that can be written to the file. To accomplish this, each MovieItem class is required to have a method that generates a serialized form of itself.

The class that reads the file, called MovieFileReader, has the job of parsing the file and generating a MovieItemCollection instance containing the MovieItems that are specified in the file. This MovieItemCollection is returned to the caller, who can then set the collection to be used. In this case, the returned MovieItemCollection was simply set, in the MovieEditor, to be the current list of items used in playing the movie.

The hook-up on the class diagram looks like this:

+————-+ +———————-+
| MovieEditor | | MovieItemsCollection |
+————-+ +———————-+
| | ^
| +————+ |
v v |
+—————–+ +—————–+
| MovieFileWriter | | MovieFileReader |
+—————–+ +—————–+

The a specific instance of a MovieFileWriter or a MovieFileReader is never even necessary, since all of their functionality can be implemented with class messages. The MovieFileReader, however, requires an instance of a MovieItemCollection, since it builds onto this and returns it to the calling MovieEditor.

Developing the file format


Implementing the ability to save and load files into the movie player necessitates the development of some file format that can store the information about the movie. The tricky part about this, however, was that the contents of the files in movie had to be stored in the file as well. For us, this was nearly the knife in the marathon, but we persisted.

There are countless ways to approach this. One of the ways presented in class was to use the Squeak lexical analyzer and parser generator TGen. Then, one need only decide on the grammar for one's file format and develop a TGen-thing to parse it. However, during the in class presentation, one thing was abudantly clear to me: TGen was ugly and I didn't want to use it. My team members agreed.

Another approach might just be to just write the contents to a file deliminated by some character, or string, and parse it by hand. This isn't a bad idea, but it just didn't seem very cool. Since this is the David Bowie Movie Maker we're talking about, cool is of the essence.

Some groups did some MIME encoding. I think that's all fine and dandy, but I had no idea it existed in Squeak, and didn't know how to use it anyway; our group didn't even consider this option for this reason

One of the other formats could be encoding the movie into XML. This one didn't appeal to 3/4ths of the group, mainly because we didn't know jack about XML. One persistant group member, however, expoused to us the virtues of XML, such as how easy it would make reading the file in once you got ahold of an XML-parser. One downside of this, however, is that the XML tags and attributes take up quite a few bytes, so the file size had the potential to be even larger than it would have been before (which was pretty big, considering that the user could save, within the file, MPEGs and WAVs, which aren't easy on the bandwidth).

So, of these 3 choices, the one that won us over was.. XML! Sure, the file-size issue was a disadvantage, as was the fact that the majority of our group didn't know much about XML, but the one persistant group member insisted that it would make our lives easier to just use XML. Not only that, but this group member dedicated themselves to educating the rest of the group about how this XML stuff works.

Now that we had the format picked out, we had to decide on the tags and attributes for the XML file. For most of the MovieItem types, the XML would be the same, which meant that we didn't have to write a method to generate XML for each MovieItem type. The only items that needed their own XML would be the MovieText items. Had we thought about it a little harder, though, we wouldn't have even had to write a different one for MovieText.

As part of the development of the file type, we had to decide how we would get the contents of the file for each MovieItem saved in the file back into a MovieItem. We would have preferred to ahve stream the bytes directly into a new morph, but that didn't seem to be an option. Instead, we decided that we would write the contents of the file out to a new file in a temporary directory. Then, during the initialization of the MovieItem, we would open that file for a specific MovieItem type, and at when the user exits the program we would remove the directory and it's contents, so it's more or less transparent to the average user.

We also had to decide on a file extension. So, we chose ".dbm". It's meaning should be obvious.

Furthermore, we had to decide which of the Squeak XML parsers we would use. Luckily (or not), the decision was made for us! Our choices were at http://minnow.cc.gatech.edu/squeak/1408. Of these 4, the only ones we could get to download were the CampSmalltalkXmlParser, the Yax parser, and BhrXmlParser. Well, the CampSmalltalkXmlParser, after unzipped, left a mangled and unreadable change set. The BhrXmlParser was very simple; so simple in fact that it sucked, and seemed to require us writing some sub-classes. Ha! That's more work that we don't need. And it didn't even provide any kinds of examples of how to use it. Which left the Yax parser. It filed in correctly AND had some examples AND didn't require us to write any other classes. So, that's the one we used.

The format for the XML tag for each movie item was planned to be as follows:

<{Item class name} startTime="{start time}" stopTime="{stop time}" position="{position}">{contents of file}<{Item class name}>

For MovieText, the tag is very similiar, except a "text" attribute is added.

This wasn't originally the plan, as we were originally going to have a "fileContents" attribute, however we asserted that the XML parser wouldn't be happy if the contents of the file contained a quotation mark. We ran into a similar problem soon after this.

While this seemed satisfactory, it wasn't exactly complete. When we tried doing this, we got all kinds of strange errors, mainly one that said "Does not understand expected:". While we're still not sure exactly why this was the error that popped up, we figured out what was causing it: the XML parser was having a fit when it was parsing the contents of the file and found an ampersand or a less-than sign.

The solution to this was fairly simple: find the characters that are causing the errors and replace them with something else, then make sure that you keep track of where the replaced characters were. After this, during the reading back in of the file, replace the replaced characters with the characters they should be and save that to the file. Implmenting this was simple enough, although it has a fairly significant disadvantage: it slows saving and loading A LOT. Suprisingly, in saving the file, the slowdown wasn't that significant. Using the method #contentsOfEntireFileAsString in FileStream, reading in the contents of the file from the file itself was fast, and using a combination of #at: and #at:put: (which are implemented as primatives) to search for and replace offending characters, has everything to do with this efficiency. For reading the file in, however, it becomes much more tedious. The XML parser wasn't very fast on files that were really large. On top of that, there had to be a good deal (in terms of CPU usgae) of time spent parsing out all the places that charcters had been replaced, and then putting the correct charcters back in place; some of the MPEG files we used had hundres and hundreds of offending characters.

However, we decided that dispite these disadvantages, it wasn't really all that slow (about 1 or 2 minutes to parse a 900KB file), and it sure was easy to do, so we stuck with it.

So, in the end, if you had a movie that contained the following:

Picture fileName: db.gif startTime: 3 stopTime: 4 position: 0@0
MPEGMovie fileName: wm.mpeg startTime: 5 stopTime: 10 position: 3@4
MovieText text: 'This is happy text!' startTime: 2 stopTime: 33 position: 2389@23

Then the corresponding contents of the movie file would be:

<movie>
  <Picture fileName="db.gif" startTime="3" stopTime="4" position="0@0" ltReplaced="23-34-100" ampReplaced="2-46">{contents of db.gif, with the less-than and ampersands replaced}</Picture>
  <MPEGMovie fileName="wm.mpeg" startTime="5" stopTime="10" position="3@4" ltReplaced="12-23-33-439-440-900-2332" ampReplaced="5-10-50-60-70-800-999">{contents of wm.mpeg, with th eless-than and ampersands replaced}</MPEGMovie>
  <MovieText text="This is happy text!" startTime="2" stopTime="33" position="2389@23"></MovieText>
</movie>

(spaces and line breaks added for readability; the real thing does'nt care about those).


Drag-and-drop Movies in Windows


Having implemented the file reading and writing protion of this milestone, our next mission was to make it easy for a user to view a movie saved with the David Bowie Movie Maker.

Basically, we had to set it up so that, when the command

Squeak Squeak3.1a.image PlayDavidBowieMovie.st SomeMovie.dbm

was executed, it would open up squeak, and open up the David Bowie Movie Maker, and then open up the specified movie file into the Movie Maker.

Most our our help on this came from the Double-Clickable Movies, Step by Step page. First, we wrote a .st file that would load a David Bowie Movie Maker file into the David Bowie Movie Maker for playback or editting. The only trick to this is figuring out how to determine which file the user passed on the command line. Well, the class SystemDictionary helped us with this, since it keeps track of the parameters passed to the Squeak executable. By creating an instance of the SystemDictionary, and then telling the Movie Maker to open the 3rd parameter sent to Squeak, we could open up the movie specified on the command line.

The hard part was in writing a batch file that would open a movie file if that movie file was dragged on top of it. The basic contents of the batch needed to be

Squeak Squeak3.1a.image PlayDavidBowieMovie.st %1

that much we knew. (the %1, in case you don't know, will be replaced by the first paramber passed to the batch file). This batch file worked when executed on the command line. But when a .dbm file was dragged on top of the batch file, and DOS prompt window would open for up for a fraction of a second, and then close again.

After some searching on the internet, I figured out that the way around this was to put a call to "start" before the call to Squeak. So, he executable line was changed to this:

start Squeak Squeak3.1a.image PlayDavidBowieMovie.st %1

And now it worked! Drag your favourite David Bowie Movie on top of the batch file and enjoy!

Something I learned


Well, I learned lots with every milestone, but this one is more like tough-learning. Learning after the fact, if you will. Basically, always remember to read the assignment. Read it bunches of times. Have your teammates read it to you, and then talk it over. When you're done with that, write it out for yourself so you always remember. Why? Well, one of the files we were required to turn in for this assignment was a sample movie file. Can you guess what I did? I forgot to turn it in. In fact, I didn't just forget, I had no freakin' idea I was supposed to.

Furthermore, test your program. Test it lots. I think we went too much on assumptions with this, and when I got home and was testing it on my machine (Windows 2000), it wouldn't open a sample movie correctly (mainly, it wasn't writing the temporary files right). And when I tried to parse it, I got a big error on some file types because I sort of assumed something wrong about XML and didn't put certain attributes in that needed to be there.

There, a David Bowie Programmer layed bare.


Link to this Page