/home/nand

Entries Comments


Decentralized Brainstorm?

7 May, 2008 (22:14) | Ubuntu | 1 comment

I was thinking the other day about Brainstorm and upstream interaction. The fact is, lots of ideas belongs to upstream projects, so how should we help them to use these ideas?

Adding a new “software” field, and making something like [project_name].brainstorm.ubuntu.com would not require too much effort, but we can’t expect upstream projects to use one distro’s brainstorm website as their idea tracker.

So I had a crazy idea. What about a decentralized Brainstorm?

A Brainstorm website would be able to “connect” to another: both would have to validate the “connection”. Once the “connection” is established, the Brainstorm website would regularly update a given subset of its data from the other. New ideas would be downloaded. New votes would be added. Globally, data is synchronized: a same idea will have the same nb of votes, comments, on all Brainstorms.

Example: Let’s say we have the Firefox project Brainstorm, the Ubuntu Brainstorm and the Red Hat Brainstorm. The Firefox Brainstorm have quite some success, but quite a lot of Firefox-related ideas are voted/discussed on the others Brainstorm. Also, the firefox folks want the input of both average users (Ubuntu) and enterprise user (Red hat). So the FF Brainstorm “connects” to the two others Brainstorm. The two distro Brainstorms download the current FF ideas, and display them. They get votes, comments. Then the FF Brainstorm regularly get the “results” from the distro Brainstorms and cumulate the results. The FF Brainstorm as well send its results to the others ones. In the end, the same idea on different Brainstorms are identical.

On the security side, you would keep the control of your Brainstorm website: you would only connect to others Brainstorm instances you trust.

Quite crazy idea, I agree. Lots of changes needed, I agree. I wonder if it’s worth the work involved. Anyone would be interested by this?

New ways to handle third party software installations

7 May, 2008 (21:44) | Ubuntu | 3 comments

At the moment, for an average user, it’s pretty hard to install third party software. Why?

  • The software is not shipped with the distribution package format : E.g. the software is bundled in tar.gz archives with custom command line installation scripts (such as the very new VIA Linux drivers).
  • When it is shipped with the distribution package format, it can still be unfriendly: When you are asked to enter cryptic commands in a console as root to add a repository, you know something is wrong.
  • When it is shipped with the distribution package format as a direct package download, you don’t get updates like you do with repositories.

While we can’t do much for the first problem, I’ve seen several projects trying to fix the two last problems: AptURL, AptThirdParty and one-click-installer. (I will not talk here of PackageKit, which does not seem to specifically cover the third party install issue). Some people are against those projects, citing security concerns : but I don’t think the statu quo where users are asked to run commands they don’t understand as root is any better!

But first, let’s see this from a user point of view. What are the use cases involved here?

  1. Patou see a great software on a website. He clicks on the “install now” button. The software was not installed: It gets installed.
  2. Patou see a great software on a website. He clicks on the “install now” button. An old version of the software was installed: It gets upgraded.
  3. Patou see a great software on a website. He clicks on the “install now” button. An newer version of the software was installed: It gets downgraded, after a warning.
  4. Fred want to uninstall a software. He goes to the software manager and remove it.
  5. An update of one of Bébert’s installed software is installed. Bébert gets notified and can launch the upgrade.

Abstracting from all the packaging system mechanisms, that’s the basic level of freedom I’d like to have as a user (5. is a plus).

1) and 2), and consequently 5) and 4) are the use cases trying to be solved by the above projects: optionally a repository gets transparently added, and the software is installed. Software updates are tracked, and it’s easy to uninstall the soft.

The 3) use case seems to be forgotten in all the above projects, and that bothers me. Please don’t put artificial constraints! The common dogme is that new versions are always better. That’s unfortunately not always true.

AptThirdParty seems the most promising project to me. Unfortunately there is no trace of its current status. Now let’s hope one of these projects get mature and cover all these use cases soon enough, for a much better third party software installation experience… (I know I am oversimplifying things, but I didn’t want to knock out with a long dissertation)

Python and webcam, part 2

29 April, 2008 (18:37) | Linux | 7 comments

A few weeks ago, I published a small webcam viewer script written in python using GTK+ and Gstreamer. Rich submitted in a comment some nice enhancements to the code while keeping it under 200 lines: the ability to record videos from your webcam, in high and low quality, and with sound! Good job!

videorecorder.jpg

That may be the start of a Cheese clone :) Here is the code:


#!/usr/bin/env python

import sys, os, time
import pygtk, gtk, gobject
import pygst
pygst.require("0.10")
import gst

BKGRNDR=0.0
BKGRNDG=0.2
BKGRNDB=0.7
BKGRNDA=0.8
WINDOW_STICK=False
WINDOW_KEEPABOVE=False
WINDOW_DECORATED=True

WIDTH=790
HEIGHT=480
TOP=100
LEFT=100

timeElapsed = 0
videoDevice="/dev/video0"
videoProperties="image/jpeg,width=640,height=480,framerate=30/1"
audioDevice="hw:1,0"
audioProperties="audio/x-raw-int,rate=16000,channels=1,depth=16"
videoSink="ximagesink"
filePath=os.path.expanduser("~") + '/Desktop'
mpeg4Properties=""

class GTK_Main:

	def __init__(self):
		global filePath, videoDevice, videoProperties, videoSink
		window = gtk.Window(gtk.WINDOW_TOPLEVEL)
		window.set_title("Video-Recorder")
		window.set_size_request(WIDTH, HEIGHT)
		window.set_decorated(WINDOW_DECORATED)
		window.connect("destroy", gtk.main_quit, "WM destroy")
		window.move(LEFT, TOP)
		mainBox = gtk.HBox()
		window.add(mainBox)
		vbox = gtk.VBox()
		mainBox.add(vbox)
		self.movie_window = gtk.DrawingArea()
		vbox.add(self.movie_window)
		controlBox = gtk.VBox()
		mainBox.pack_start(controlBox, False)
		controlBox.set_size_request(150,480)
		controlBox.set_border_width(10)
		controlBox.pack_start(gtk.Label())
		self.button = gtk.Button("Record (HQ)")
		self.button.connect("clicked", self.start_stop_recordHQ)
		controlBox.pack_start(self.button, False)
		self.button1 = gtk.Button("Record (LQ)")
		self.button1.connect("clicked", self.start_stop_recordLQ)
		controlBox.pack_start(self.button1, False)
		self.button3 = gtk.Button("Quit")
		self.button3.connect("clicked", self.exit)
		controlBox.pack_start(self.button3, False)
		controlBox.add(gtk.Label())
		self.ET = gtk.Label()
		self.ET.set_line_wrap(True)
		controlBox.pack_start(self.ET, False)
		window.show_all()

		self.player = gst.parse_launch ("v4l2src device="+videoDevice+" queue-size=16 ! "+videoProperties+" ! ffdec_mjpeg ! queue2 max-size-buffers=10000 max-size-bytes=0 max-size-time=0 ! ffmpegcolorspace ! "+videoSink)
		bus = self.player.get_bus()
		bus.add_signal_watch()
		bus.enable_sync_message_emission()
		bus.connect('message', self.on_message)
		bus.connect('sync-message::element', self.on_sync_message)
		self.start(firstRun = True)
		self.recorderLQ = None
		self.recorderHQ = None

	def SetPipelines(self, Type):
		global filePath, videoDevice, videoProperties, videoSink, audioDevice, audioProperties, mpeg4Properties
		if Type == "LQ":
			self.recorderLQ = gst.parse_launch ("v4l2src device="+videoDevice+" queue-size=16 ! "+videoProperties+" ! ffdec_mjpeg ! queue max-size-buffers=10000 max-size-bytes=0 max-size-time=0 ! queue ! tee name=tee tee. ! ffmpegcolorspace ! queue ! ffenc_mpeg4 ! queue ! mux. avimux name=mux alsasrc device="+audioDevice+" ! audiorate ! "+audioProperties+" ! queue ! audioconvert ! lame !  mux. mux. ! queue ! filesink location="+filePath+time.strftime("%Y%m%d%H%M%S", time.localtime()) +".avi tee. ! ffmpegcolorspace ! queue ! "+videoSink)
			busrecLQ = self.recorderLQ.get_bus()
			busrecLQ.add_signal_watch()
			busrecLQ.enable_sync_message_emission()
			busrecLQ.connect('message', self.on_message)
			busrecLQ.connect('sync-message::element', self.on_sync_message)
			self.recorderHQ = None
		elif Type == "HQ":
			self.recorderHQ = gst.parse_launch ("v4l2src device="+videoDevice+" queue-size=16 ! "+videoProperties+" ! tee name=tee tee. ! queue ! mux. avimux name=mux alsasrc device="+audioDevice+" ! audiorate ! "+audioProperties+" ! queue ! audioconvert ! lame !  mux. mux. ! queue ! filesink location="+filePath+time.strftime("%Y%m%d%H%M%S", time.localtime()) +".avi tee. ! ffdec_mjpeg ! queue max-size-buffers=10000 max-size-bytes=0 max-size-time=0 ! queue ! ffmpegcolorspace ! "+videoSink)
			busrecHQ = self.recorderHQ.get_bus()
			busrecHQ.add_signal_watch()
			busrecHQ.enable_sync_message_emission()
			busrecHQ.connect('message', self.on_message)
			busrecHQ.connect('sync-message::element', self.on_sync_message)
			self.busrecLQ = None

	def start(self, firstRun = False):
		if (firstRun == True):
			time.sleep(1)
		self.player.set_state(gst.STATE_PLAYING)
	def start_stop_recordHQ(self, w):
		global timeElapsed
		if self.button.get_label() == "Record (HQ)":
			self.SetPipelines("HQ")
			timeElapsed = 0
			self.button.set_label("Stop Recording")
			self.player.set_state(gst.STATE_NULL)
			if self.recorderLQ != None:
				self.recorderLQ.set_state(gst.STATE_NULL)
			time.sleep(1)
			self.recorderHQ.set_state(gst.STATE_PLAYING)
			self.button.set_sensitive(True)
			self.button1.set_sensitive(False)
			self.updateET()
		else:
			self.recorderHQ.set_state(gst.STATE_NULL)
			time.sleep(1)
			if self.recorderLQ != None:
				self.recorderLQ.set_state(gst.STATE_NULL)
			self.player.set_state(gst.STATE_PLAYING)
			self.button.set_label("Record (HQ)")
			self.button.set_sensitive(True)
			self.button1.set_sensitive(True)
			self.ET.set_label("")
	def start_stop_recordLQ(self, w):
		global timeElapsed
		if self.button1.get_label() == "Record (LQ)":
			self.SetPipelines("LQ")
			timeElapsed = 0
			self.button1.set_label("Stop Recording")
			self.player.set_state(gst.STATE_NULL)
			if self.recorderHQ != None:
				self.recorderHQ.set_state(gst.STATE_NULL)
			time.sleep(1)
			self.recorderLQ.set_state(gst.STATE_PLAYING)
			self.button1.set_sensitive(True)
			self.button.set_sensitive(False)
			self.updateET()
		else:
			self.recorderLQ.set_state(gst.STATE_NULL)
			time.sleep(1)
			if self.recorderHQ != None:
				self.recorderHQ.set_state(gst.STATE_NULL)
			self.player.set_state(gst.STATE_PLAYING)
			self.button1.set_label("Record (LQ)")
			self.button.set_sensitive(True)
			self.button1.set_sensitive(True)
			self.ET.set_label("")
	def updateET(self):
		global timeElapsed
		self.ET.set_label(str(timeElapsed)+" sec.")
		print str(timeElapsed)+" sec."
		timeElapsed = timeElapsed + 1
		if self.button.get_label() == "Stop Recording" or self.button1.get_label() == "Stop Recording":
			gobject.timeout_add(1000, self.updateET)
		else:
			self.ET.set_label("")

	def exit(self, widget, data=None):
		gtk.main_quit()

	def on_message(self, bus, message):
		t = message.type
		if t == gst.MESSAGE_EOS:
			self.player.set_state(gst.STATE_NULL)
			if self.recorderHQ != None:
				self.recorderHQ.set_state(gst.STATE_NULL)
			if self.recorderLQ != None:
				self.recorderLQ.set_state(gst.STATE_NULL)
			self.button.set_label("Record (HQ)")
			self.button1.set_label("Record (LQ)")
			self.button.set_sensitive(True)
			self.button1.set_sensitive(True)
		elif t == gst.MESSAGE_ERROR:
			err, debug = message.parse_error()
			print "Error: %s" % err, debug
			gobject.timeout_add(1000, self.ET.set_label, "Error: %s" % err)
			self.player.set_state(gst.STATE_NULL)
			if self.recorderHQ != None:
				self.recorderHQ.set_state(gst.STATE_NULL)
			if self.recorderLQ != None:
				self.recorderLQ.set_state(gst.STATE_NULL)
			self.button.set_label("Record (HQ)")
			self.button1.set_label("Record (LQ)")
			self.button.set_sensitive(True)
			self.button1.set_sensitive(True)
	def on_sync_message(self, bus, message):
		if message.structure is None:
			return
		message_name = message.structure.get_name()
		if message_name == 'prepare-xwindow-id':
			# Assign the viewport
			imagesink = message.src
			imagesink.set_property('force-aspect-ratio', True)
			imagesink.set_xwindow_id(self.movie_window.window.xid)

GTK_Main()
gtk.gdk.threads_init()
gtk.main()

You know something must be going on…

25 April, 2008 (23:40) | Ubuntu | 1 comment

 … when you are beginning to see that many spontaneous user fanwork! (don’t worry, just invented this word) Here are the latest I encountered:

2436225528_e5c1887fe1_o.png

And a pretty cool promotion website here.

What I’d like to discuss at the FOSScamp…

23 April, 2008 (09:17) | Ubuntu | No comments



because I believe a summer-of-code-like event is the next logical step after Ubuntu Brainstorm.



because I think a good developer documentation center would help much for new and “foreign”(others OS) developers.

Ubuntu Hardy Heron final testing!

22 April, 2008 (22:09) | Ubuntu | No comments

We are now two days before the final release of Ubuntu 8.04, and the candidate CD images are already here and need testing! Help us make sure they are critical-bug-free by following these instructions.

While fighting with sudo…

22 April, 2008 (18:46) | Linux | 4 comments

… I stumble across this strip:

<a mce_thref=

:) Man I love the Xkcd webcomic!

(Original strip location: http://xkcd.com/149/)

Ubuntu Hardy Heron RC, call for testing

15 April, 2008 (20:51) | Ubuntu | No comments

As you may know, we are getting close to the release of the next LTS version of Ubuntu, Hardy Heron 8.04! The release candidate is due thursday, and the final release the next week.

And in these last development days, you can help to polish it! Testers are needed to test the daily images builds, either in a virtual machine, or in real hardware. There are lots of testcases to check! If you are interested, find all the details here and join the QA team at #ubuntu-testing on irc.freenode.net.

Weekly pick of Brainstorm ideas

9 April, 2008 (20:57) | Ubuntu | No comments

Just a quick link to a nice weekly pick of Brainstorm ideas.

Python power

4 April, 2008 (13:13) | Linux | 4 comments

Since I started hacking with python a few months ago, I never stopped being amazed by its power.

Wanna display you webcam video on a very simple GTK+ app? Less than one hundred lines, thanks to pygst (gstreamer) and pygtk!

 #!/usr/bin/env python

import sys, os
import pygtk, gtk, gobject
import pygst
pygst.require("0.10")
import gst

class GTK_Main:

	def __init__(self):
		window = gtk.Window(gtk.WINDOW_TOPLEVEL)
		window.set_title("Webcam-Viewer")
		window.set_default_size(500, 400)
		window.connect("destroy", gtk.main_quit, "WM destroy")
		vbox = gtk.VBox()
		window.add(vbox)
		self.movie_window = gtk.DrawingArea()
		vbox.add(self.movie_window)
		hbox = gtk.HBox()
		vbox.pack_start(hbox, False)

		hbox.set_border_width(10)
		hbox.pack_start(gtk.Label())
		self.button = gtk.Button("Start")
		self.button.connect("clicked", self.start_stop)
		hbox.pack_start(self.button, False)
		self.button2 = gtk.Button("Quit")
		self.button2.connect("clicked", self.exit)
		hbox.pack_start(self.button2, False)
		hbox.add(gtk.Label())

		window.show_all()

		# Set up the gstreamer pipepile
		self.player = gst.parse_launch ("v4l2src  !  autovideosink")

		bus = self.player.get_bus()
		bus.add_signal_watch()
		bus.enable_sync_message_emission()
		bus.connect('message', self.on_message)
		bus.connect('sync-message::element', self.on_sync_message)

	def start_stop(self, w):
		if self.button.get_label() == "Start":
			self.button.set_label("Stop")
			self.player.set_state(gst.STATE_PLAYING)
		else:
			self.player.set_state(gst.STATE_NULL)
			self.button.set_label("Start")

	def exit(self, widget, data=None):
		gtk.main_quit()

	def on_message(self, bus, message):
		t = message.type
		if t == gst.MESSAGE_EOS:
			self.player.set_state(gst.STATE_NULL)
			self.button.set_label("Start")
		elif t == gst.MESSAGE_ERROR:
			err, debug = message.parse_error()
			print "Error: %s" % err, debug
			self.player.set_state(gst.STATE_NULL)
			self.button.set_label("Start")

	def on_sync_message(self, bus, message):
		if message.structure is None:
			return
		message_name = message.structure.get_name()
		if message_name == 'prepare-xwindow-id':
			# Assign the viewport
			imagesink = message.src
			imagesink.set_property('force-aspect-ratio', True)
			imagesink.set_xwindow_id(self.movie_window.window.xid)

GTK_Main()
gtk.gdk.threads_init()
gtk.main()

And here you are! I could have reduced even more the code by using Glade to design and dynamically load the UI.

webcam-viewer.png

(there is supposed to be an image instead of the blackbox. Anyone know how to screenshot a Xv window?)

« Older entries