Wednesday, January 28, 2009

Status update...

The only work I've been able to do on Saya this week is copying the Main Menu from wx to Qt, and that was on the weekend.

The reason: I had to sleep at 3 o'clock on Monday and this has exhausted me. Probably I won't be able to work on Saya until Saturday after I've had a VERY GOOD sleep.

On the other hand, this Friday we'll have the developers meeting. Two people will be joining the team, and two people will be leaving. Let's hope that I can finish the Qt conversion for the end-of-March meeting so we can move on.

Wish me luck!

Monday, January 19, 2009

Report on the non-advance of the project.

My job's killing me. I haven't been able to work on Saya the last week, and I this week there will be only a 50/50 chance I can get to work on it.

On the other hand, it seems I've managed to gather not one, but TWO volunteers. One will work on the mascot (yay! ^_^) and also has some Qt experience. The other one... I forgot, sorry.

Wish me luck and some free time!

Good night.

Sunday, January 11, 2009

Report on the Qt conversion

Hi everyone. Just a quick report on what I've been doing today.

I've been having a hard time converting the application to Qt. Basically, I've had to take a decision: To adapt the C++ code directly to use the eventid#'s and get rid of Qt designer advantages, or workaround the autogenerated code by writing some wrapper functions and write LOTS, LOTS AND LOTS OF CODE.

The advantages to the Qt designer are obvious. Easy code generation, forward compatibility, etc.
But I've grown tired of how wxWidgets loads the UI using xml libraries. Is that REALLY necessary?

I realized I wouldn't need to choose between the two. Why? Because Qt designer lets you see the generated code! :D

I only have to copy/paste the code, just like I've been doing with wxFormBuilder. Also, I realized I didn't need to write a lot of code. Remember this?


int idFileOpen = XRCID("idFileOpen");


I can easily rewrite the Id registering functions into this:


unsigned int idFileOpen = syActionEvent::RegisterId("idFileOpen","OnFileOpen()");


Wait, what's that second string for?
This is the beauty of thinking out of the box. We don't have to follow wxWidgets' model anymore! We're FREE To do whatever we want! So besides registering a numeric id for the File-Open event, I can also register the Qt slot name that gets triggered by the corresponding action!

Later I only have to write a small function that recursively iterates over all QActions in the main menu, gets the corresponding Id, and connects to the slot at the same time.

But all this thinking really made me get tired of the Qt conversion issue. Yes, I have to admit it, it's boring (coding without visual feedback isn't exactly a rollercoaster ride). So it's better if I rest for a bit and then later next week I'll go on with the conversion.

Oyasumi nasai (good night).

Saturday, January 10, 2009

Actions, events, slots, OH MY!

I've come to realize that Menu Event Handling in Qt is versatile, but it suffers from a fatal flaw:
It relies on slots. In other words, if I have to run a specific event (like Opening a File), I need to call the object's specific slot either directly, or via a signal. For that I need to actually include the class' header file. And that's a no-no.

So what am I gonna do? At this time I'm glad I implemented Saya's event system. With it, I can replicate the old "call an event with a specific ID" behavior. That's much better IMHO than having to include a huge header file.

But before that, we need to create a new class: syActionEvent. Which basically is a reimplementation of wxWidgets' menu event.

Update (Jan 10, 2008, 21:50):

I just realized that to send a menu event you just have to allocate an action. So you'll end up
with a series of action pointers. That would be the elegant solution - but isn't it more elegant to simply have a series of integer values, with no pointers allocated to them? So that's what we're doing in here. Yes, I know, we'll have to create the actions ANYWAY because that's how Qt works. But as I already said, I don't want to be tied to a specific event model. And using numeric id's for events is simpler, IMHO.

Thursday, January 8, 2009

GAAAAAAAAAAHHHHHHH!!!!!!

There's so much code to convert to Qt!!!! (sorry, had to get it out of my system)

I need to take a break.

(On the other hand, I'm so glad I decided to convert before advancing any further! :D )

The MOC: Qt's lethal trap!

Having learned how cool the QT+Python combo is, it had never occurred to me that for C++ you need to go through a very different path to implement signals and slots with Qt.

What are signals and slots? They're the coolest possible way to implement events. A slot is defined by a specific type of function (which receives a specific type of parameters). A signal is just a function. When you connect a signal and a slot (or various slots), all slots get triggered by the same signal.

But to implement this in C++, the Qt guys chose to use a meta-object-compiler (MOC), which means you have to PREPROCESS your cpp file in order to compile it. UGH!

I've been trying to find an alternative way, but all my attempts have failed. The code generated by the moc is both cryptic and hideous, worthy of an obfuscated C contest.

But not everything is lost: After we change a c++ file, all we have to do is run the MOC on the file and redirect it to a moc header (which we will manually include by the cpp file). The trick is generating the moc file automatically.

Enter Qmake.

*disc scratch sound* NOOOOOOOOOOOOOO! I don't want to run Qmake, dammit!

OK then, enter CMake.

Ah, that's much better :)

Guess what? CMake has built-in support for Qt's MOC! All you have to do is add some parameters to your CMake file, and voila... I think.

Sigh. I didn't want to do this already, it's too soon! I don't want to be a father! (sob). Sorry, got carried away :P

For now, I'll build the moc files by hand (which will suffice for now), and then, I'll add the necessary stuff when I make the move to CMake.

This is Rick, and I approve this message.

Monday, January 5, 2009

It's official: We're moving to Qt4!

I finally made up my mind. After an e-mail conversation with one of the VLC developers, I was told that they submitted quite a few bug reports to wxWidgets, and they were all dismissed. I was also told that wxWidgets was very difficult to work with, and recommended to work with GTK, QT, or anything _else_ (wow, is wxWidgets that bad?).

And my experience confirms it.

Now, I tried the QT4 dialog designer, and once you get the hang of it (you need to use a right-click menu to put all the controls in a layout, there's no standard button for that), it's a bliss. But is it compatible with GPLv3? Version 4.3.4 and up are. Hurray! :)

Sigh, so here goes YET ANOTHER month of changing existing code for arbitrary reasons. I'm lucky I had already adapted a lot of code from wxWidgets ;-).

I'll close this announcement with a famous quote:

"Chance favors the prepared mind".
Louis Pasteur

Saturday, January 3, 2009

VLC knows best.

I hadn't realized it, but turns out that VLC, my favorite video player (due to its stability, etc), uses wxWidgets!

And I just found a Doxygen page documenting every single bit of it. Man, this thing is a masterpiece of art. I think I'm going to start stealing^H^H^H^H^H^H^H^H borrrowing code from VLC :).

So far, I learned that VLC uses the following approaches to draw video:

XV (hardware-accelerated Xvideo extension to the X11 server)
X11 (100% software)
GLX
SDL

All of these, in the form of plugins. Just what I was looking for.

Unfortunately, that means one thing: My current framework design doesn't seem to be extensible enough. I wrote a VideoOutputDevice class, which was supposed to take care of this. But, how to integrate it with a hardwired set of video control widgets?

VideoOutputDevice has a function to Set the size. And now I'm starting to see something else that I missed. Before now, I didn't actually NEED the VideoOutputDevice to remember the width and height - that was something that wxVideoPanel had. But now I'll need to specify not only the width and height, but also top and left (relative to the current screen), so that we can use any direct rendering plugins.

I'll also need to add a registry of VideoOutputDevice classes, and generalize wxVideoOutputDevice into a more generic class. Or actually, add a new class: syBitmapSink. The only thing this class will do is to load data from a syBitmap. This way, we will be able to use a "Default" VideoOutputDevice, which on Flushing, will send the data to a syBitmapSink. And wxVideoPanel will be that sink.

Since the object coordinates, width and height will change dynamically, the new VideoOutputDevice subclasses will have everything they need to perform the rendering. So it's all matter of which class gets instantiated on startup.

Neat, huh? :)

...and Pandora's Box was opened.

Oh my, look at all these bugs! Will the Playback framework will ever be completed?

And just when I thought everything was going fine, I realized that playback had a lot of bugs and design flaws.

* Playback didn't sleep at all, so it was caught in a loop waiting for the time when the next frame had to be painted. (fixed in my PC).

* The current frame is reset exactly at 4.0 seconds from playback. This is a very curious behavior, but I haven't found out (yet) why it happens.

* After pressing pause, pressing play resets the current frame to 0 again. And I don't know either why this happens. Sigh.

* Rendering the current frame into the screen is SLOW AS HELL. Why? Because we're using wxWidgets events for painting, and that's just wrong. Rendering should be done IN THE RENDERING THREAD. But there's a problem... how do I paint directly to the client window from a thread? I need to research more about this.

In any case, I'm glad I wrote one unit test for the time/frame conversion. I also wrote some other unit tests in the form of painting algorithms. The two bugs mentioned above were caught with a straight line painting video: A vertical line, whose x coordinates are equal to the current frame. I don't have the means to print a text in a bitmap, so I have to use what is available.

Friday, January 2, 2009

Video playback engine WORKS!

Finally I realized why the CPU hogging. I hadn't implemented the Video Output code (all video output was still done in the testing code, in the main thread :P ). But now we can play, pause, fast forward... reverse playback hasn't been tested yet. I need to modify the Demo Video code to see if it actually works (audio, on the other hand, hasn't been taken care of yet).

On the other hand, a friend JUST recommended me to use the Xine engine. Sigh. Why didn't I find out earlier!! Still, xine only works partially on Windows... on CVS. Oh well, what can we do about it?

This leds me to think about it. Why is it that when someone tries to make something that works, nobody helps him but everyone is eager to criticize? People from IRC laughed at me. Meanwhile, support and/or suggestions didn't come. Well, some people did help me, that's why they're on the team. But still, no experienced programmer. What am I supposed to do all alone? Oh, I forgot about the mailing list guys, they also provided valuable help. Unfortunately, everyone's working on his own project.

If a good C++ programmer is reading this, PLEASE HELP! I can't do it all by myself! It's been more than 7 months already, and I'm still on my own. Maybe someone will help when the project advances further.

See ya.

Thursday, January 1, 2009

It's alive, Igor!!! It's... having seizures?

After a lot of debugging and refactoring code, I finally got the Playback manager to work... and crash / hang.

First of all, I had overlooked a lot of things while coding the playback framework. I hadn't been given a single opportunity to ACTUALLY test it. But now that it kinda works... the bugs/flaws started to appear, one after another.

For starters, CPU utilization goes to 50% when PAUSED, and 30% when RUNNING. O.o
Second, on exit, for some reason that I haven't determined yet, sometimes I get segfaults, and sometimes hangs.

But at least I'm getting closer: The play/pause button interrupts and continues playback :)

I'll commit the code to the SVN tree when these bugs get fixed.

Update (Jan 1st, 2009):

I *think* I know what's happening. It's during ~AVController(). The VideoInputDevice object generates a segfault when accessing one of its mutexes. But if we get a segfault, it means that this location is no longer valid. In other words, we're dealing with a dangling pointer. Which means that somehow, the VideoInputDevice already got deleted somewhere else. But when, and how?

Update (Jan 1st, 2009, 19:55):

Ah, I see what happened.

AVController's destructor calls AVDevice::ShutDown(). But when InputMonitor (which is a subclass of AVDevice) deletes its Video Input Device on destruction, it fails to reset the pointer. So when ~AVController() calls ShutDown(), we're operating on an already-released object. In other words, in the destructor chain of InputMonitor, we deleted an object prematurely. The problem was easily fixed by simply commenting the deletion line.

Also, there's no resource problem with ~InputMonitor, because it always uses the same InputDevice. Besides, InputMonitor only gets created and deleted once. So how is InputMonitor's FileVID object deleted?

With a new class I created, AVDeviceRegistry. On deletion (that means the program's shutdown) it deletes all created AVDevices. And when an AVDevice is deleted, it unregisters itself from the list. Ta-da!

Now that the app doesn't crash on exit anymore, we're ready to commit to SVN, and later fix the CPU-thrashing Issue. Yay!