Monday, June 30, 2008

Starting to Look Good


The goocanvas version of the advance timeline is taking shape. The above screen shot shows a timelinew with a single source zoomed in. I haven't tried messing with multiple sources, but it shoul work alright. This revision is a bit of a regression, as the timeline now lacks the ability to trim sources, but you can move them. The timeline is also miss-aligned. My gut tells me that it's in the wrong spot to begin with, and the best thing to do would be to drop it down below the toolbar. This would leave more room for other tools, like a trimming tool.

Now is where I need your help: color schemes, layout suggestions, usability concerns. The current source widgets are just rectangles, do people want thumbnails or just text to cover those? How should effects be represente visually? I don't really want to go too much farther without a good idea of what people want to see.

Sunday, June 22, 2008

Got something done! Yea!

Testing screenshot showing a ComplexTrack with several sources, two of which are overlapping. Sorry about the colors: I'm a programmer, not an artist.

I'm currently in Prague, which is a gorgeous town. Primarily due to lack of planning, I've got a couple of dead days which I used to work on PiTiVi. I had left the States with out a complete gstreamer install, without svn installed, without goocanvas, and a whole host of other silly things that are easy to take care of when you have reliable internet access, but impossible without it. Today, after two weeks, I have finally made another commit.

Basically, I figured out the easy way of making some of making some of my utility functions a bit more friendly to MVC. I have a function calle make_dragable which works on any goocanvas.Item object. It connects signals and sets appropriate data items to make the canvas item dragable. Previously, this function and its associated callbacks handled all the interaction from button_press to button_release, including actually moving the object in question. This posed a problem for the MVC design pattern, as the object should not actually be moved until the appropriate callback is received. I wanted to save as much of this code as possible, as it handles a lot of nice details, like keeping track of the mouse-down offset (so that the object being moved doesn't suddenly jump to the cursor position). I had tried various ways around the problem, such as having a flag to not set object position, using part of the code and connecting signal callbacks manually, even starting over from scratch. Everything I tried had problems from breaking existing code to just being downright buggy. There's no substitute for already working code.

I finally hit on the solution in the shower yesterday morning, and it involved changing about 3 lines of code to add an extra callback. Worked like a charm. I can finally get moving again. This will also make the simple timeline code easier to manage, so though I haven't written many lines of code in the last 2 weeks, I have actually made a good deal of progress in terms of thinking about the problem.

Wednesday, June 18, 2008

Update

Well, i've been a bit preoccupied with travel preparations and the traveling itself. I'm also finding development on the eeepc a challenge, partially internet access was hard to comeby. I'm slowly chipping away at the problem, working bout 45 minutes a day. I haven't got anything commit-worthy yet, i'm still trying to sort out some issues. Also, a lot of the gstreamer encoding plugins failed to build, and the sample video i brought is a little too intense for the eeepc. I can't re-encode it, though, without the encoding plugins. Anyways, I'll post another upddate later this evening. I have a long train ride ahead of me tomrrow, so I ought to get a lot of work done.

Sunday, June 8, 2008

Started Porting Advanced Timeline

While it is true that the advanced timeline presents a simpler visualization than the simple timeline, the simple timeline is still simpler: it only represents the video composition of a timeline, and relies on the fact that video and audio sources are linked to each other to make it appear as though sources are complete movie files. The complex timeline presents a simpler visualization, but it might manage dozens of instances of each. So, my initial optimism has begun to fade somewhat. I'm going to just focus on re-implementing existing functionality, while also adding support for resizing clips which currently can't done. Transitions and effects will be easier to implement in the simple timeline first.

Did some light refactoring yesterday, eliminated a redundant class, and restructured the simple timeline code somewhat so that it will be easier to add elements to it that aren't scrolled along with the timeline. The structure of the simple timeline now follows the structure of the complex timeline more closely. I've been studying the advanced timeline code to better understand what has to change, but I'm still confused about a few things.

I've started working on a ComplexTrack() class, based on SmartGroup which emulates the existing timeline widget pretty closely. I could easily spawn separate instances of the class to connect to the video and audio compositions. The real issue is that pitivi emits separate signals for effects and transitions. I'm wondering if another layer of indirection might be needed, but of course, a lot of this depends on just what the complex timeline is supposed to look like. Will effects overlap sources? will transitions appear on on a separate track? My primary goal is just to replace the existing layout code with goocanvas, then go one step further and add trimming support (as opposed to mere cutting support, which splits clips in half). What will be the semantics of multiple tracks in PiTiVi? Priority? Nothing at all?

One thing I've realized over the last few days is that the scale_x property is not going to work for handling zooming. It's a great way to handle resizing the simple timeline, but if I use it to provide zooming in the complex timeline, there will be side effects. All the images, fonts, outlines -- everything in the canvas -- will be stretched or compressed by scale_x, which will result in unreadable text and distorted images at higher levels of zoom. Instead, I'll retain the existing zoomable widget interface (possibly in a slightly modified form) and use it to adjust scale "manually". One change I would like to make, however, is in the zooming widget itself. I'd like to provide meaninful levels of zoom, instead of the "bigger-or-smaller" caveman controls we have now. The sequence would go something like ... 1-frame, 5-frames, 10-frames, 50 frames, which are meaningful for movie projects, as well as .1s .25s .5s, 1s, 10s, 1m, 10m...The frame adjustments would be based on the current project framerate, and would essentially make each quantity of frames a standard width. For video projects, it would also be nice to constrain the playback head (red line) by 1/FPS milisecond for precise edits.

There are two custom canvas items that would be good to have in the complex timeline, but for now I'll just use the existing widgets: the first is the top level ruler, and the second the thumbnail viewer. For the complex timeline, though, I want more than that: I want a "filmstrip" widget, one which presents a sequence of thumbnails, the number of which is proportional to the visible width of the the widget.

Oh one more thing...I've been sloppy about the distinction between widgets and canvas items. There is a big difference, so I'm going to be more careful about this in the future. In general, though, they are equivilent because you can embed gtk.Widget objects in a canvas, and goocanvas.Canvas() objects are gtk.Widget objects. You can wrap one in the other, essentially.

Thursday, June 5, 2008

Simple timeline is ported over to goocanvas, at least to the point where it's usable. There are some graphic glitches that I'll fix later, and I want to improve the code which handles external drag-and-drop (currently sources are always appended, I'd like to have some interaction which allows you to drop sources anywhere). I'm switching my attention to the advanced timeline.

I'm probably going to gut all the existing timeline code completely, because most of what's there is redundant with the facilities goocanvas provides. For example, zooming is easily handled by goocanvas's scale_x property. In addition, scrolling is taken care of automatically if you use a gtk.ScrolledWindow, so the layers of indirection are really, really unecessary. I can handle the informational messages directly in goocanvas...basically the structure of the timeline will look like this (more-or-less, parent classes in parens, trailing * indicates more-than-one).

* Generic timeline widget (gtk.ScrolledWindow)
**SimpleTimeline (goocanvas.Canvas)
*** SimpleSourceWidget*
**ComplexTimeline (goocanvas.Canvas)
*** PiTiViTrack*(smartgroup)
**** PiTiViSourceWidget* (goocanvas.Rect)

Monday, June 2, 2008

Conversion of SimpleTimeline to Goocanvas almost complete

I solved my conceptual dillema by introducing Yet Another Layer of Abstraction: I subclassed HList() to create TimelineList(), which harbors all the application-specific code. This required reworking the HList() class a bit, but it still works as it did before. I even manged to get rid of some code I didn't like in the process. So, now the mouse events trigger changes to the underlying composition, and the UI is only updated in the callback. All that remains now is to handle the external drag-and-drop events (of FileFactories onto the timeline) a little better, so that the user can see where their source will end up before making the drop.

I asked about my pointer-event propagation problem on the goocanvas developer mailing list. It turns out that the only way to get signals from goocanvas.Widget items that contain a widget with an xwindow is to connect to the widget itself, rather than the goocanvas.Widget item. This will require conversion of the coordinates. That might not be so hard, so it's worth pursuing. The current "ghostly" SimpleSourceWidgets are rather distracting, but it's low priority for now

After that, I'm going to start porting the advanced timeline over to goocanvas. This should be relatively straightforward. Each source in the timeline will have a goocanvas.Rect() object, or perhaps a derivative, which will represent it in the timeline. There will be a direct mapping from time to position along the x axis. Zooming will be handled by adjusting the scale_x property of the canvas, and scrolling will be handled automatically by a gtk.ScrolledWindow (that will also contain the SimpleTimeline, and handle scrolling for that as well). Once that's done, PiTiVi will basically work as it did before, except that now it will be easier to add new features using goocanvas. By this time I hope to have development versions of gstreamer built, because I'll be on my way to europe.

Sunday, June 1, 2008

Setbacks

Today worked on the SimpleTimeline using Goocanvas. It wasn't easy: a number of unexpected obstacles presented themselves. The first was the challenge of merging the existing SimpleTimeline code into the new code, and getting drag-and-drop to work as it did before. Once I had done that, I was able to add and delete the existing source widgets from the timeline.

Erhm, well kindof. Actually this is a transient glitch, it goes away as soon as you resize the timeline.

See, now that's better. Except once I got to this point I found that the re-orderable list that I'd spent so much time on didn't work. Event signals never make it to the canvas. They seem to be trapped by the widget. I modified my test code to use widgets instead of native canvas items. A little experimentation showed that only widgets which don't have their own xwindow (as do gtk.EventBox and gtk.DrawingArea) will emit events within goocanvas. So, if I just change the parent of the SimpleSourceWidget to something like HBox, it should work...right?
Well, sortof...now they look like "GhostSourceWidgets"...wooooooooooo! Hmm, I know. I'll make a ghetto hack! If I just plop a goocanvas.Rect behind the widget, and group the two together, then I'll have an opaque background that will prevent widgets from blending together...
Ermmm...no.