diff options
author | Pootle daemon <pootle@sugarlabs.org> | 2009-04-30 18:41:25 (GMT) |
---|---|---|
committer | Pootle daemon <pootle@sugarlabs.org> | 2009-04-30 18:41:25 (GMT) |
commit | 54aa87226609cc31f1adb1802734e590ecccf71f (patch) | |
tree | 16a72857aeb415ed1d8e36328bbfe702f61c1f25 | |
parent | f94b7440eaeebac247566fd8a31a7283343d72a8 (diff) | |
parent | 32b3af2dcf4b5ce855c07b3056e8c52e740dcc44 (diff) |
Merge branch 'master' of gitorious@git.sugarlabs.org:record/mainline
-rw-r--r-- | .gitignore | 27 | ||||
-rw-r--r-- | MANIFEST | 363 | ||||
-rw-r--r-- | NEWS | 19 | ||||
-rwxr-xr-x | _camera.so | bin | 32196 -> 0 bytes | |||
-rw-r--r-- | activity/activity.info | 8 | ||||
-rw-r--r-- | aplay.py | 39 | ||||
-rwxr-xr-x | bin/Record.sh | 4 | ||||
-rw-r--r-- | button.py | 136 | ||||
-rw-r--r-- | camerac/Makefile (renamed from Makefile) | 26 | ||||
-rw-r--r-- | camerac/__init__.py | 21 | ||||
-rw-r--r-- | camerac/camera.c (renamed from _camera.c) | 6 | ||||
-rw-r--r-- | color.py | 66 | ||||
-rw-r--r-- | constants.py | 672 | ||||
-rw-r--r-- | glive.py | 927 | ||||
-rw-r--r-- | glivex.py | 147 | ||||
-rw-r--r-- | gplay.py | 160 | ||||
-rw-r--r-- | greplay.py | 84 | ||||
-rw-r--r-- | gst/AUTHORS | 0 | ||||
-rw-r--r-- | gst/COPYING | 674 | ||||
-rw-r--r-- | gst/ChangeLog | 0 | ||||
-rw-r--r-- | gst/INSTALL | 291 | ||||
-rw-r--r-- | gst/Makefile.am | 20 | ||||
-rw-r--r-- | gst/NEWS | 0 | ||||
-rw-r--r-- | gst/README | 0 | ||||
-rwxr-xr-x | gst/autogen.sh | 92 | ||||
-rw-r--r-- | gst/configure.ac | 136 | ||||
-rw-r--r-- | gst/gst-autogen.sh | 308 | ||||
-rw-r--r-- | gst/gstvalve.c | 311 | ||||
-rw-r--r-- | gst/gstvalve.h | 82 | ||||
-rw-r--r-- | gst/gstvideorate.c | 877 | ||||
-rw-r--r-- | gst/gstvideorate.h | 81 | ||||
-rw-r--r-- | gst/m4/Makefile.am | 1 | ||||
-rw-r--r-- | gst/m4/as-compiler-flag.m4 | 25 | ||||
-rw-r--r-- | gst/m4/as-version.m4 | 66 | ||||
-rw-r--r-- | instance.py | 50 | ||||
-rw-r--r-- | model.py | 639 | ||||
-rw-r--r-- | p5.py | 228 | ||||
-rw-r--r-- | p5_button.py | 196 | ||||
-rw-r--r-- | po/zh_TW.po | 2 | ||||
-rw-r--r-- | port/AUTHORS | 1 | ||||
-rw-r--r-- | port/COPYING | 340 | ||||
-rw-r--r-- | port/NEWS | 8 | ||||
-rw-r--r-- | port/README | 13 | ||||
-rw-r--r-- | port/TODO | 0 | ||||
-rw-r--r-- | port/__init__.py | 0 | ||||
-rw-r--r-- | port/json.py | 33 | ||||
-rw-r--r-- | record.py | 906 | ||||
-rw-r--r-- | recorded.py | 282 | ||||
-rw-r--r-- | recordtube.py | 226 | ||||
-rw-r--r-- | serialize.py | 567 | ||||
-rwxr-xr-x | setup.py | 3 | ||||
-rw-r--r-- | ui.py | 4112 | ||||
-rw-r--r-- | utils.py | 126 |
53 files changed, 8136 insertions, 5265 deletions
@@ -1,3 +1,28 @@ -_camera.o +*.o +*.so +linux* dist locale + +aclocal.m4 +autom4te.cache +autoregen.sh +config.log +config.status +configure +libtool +stamp-h1 +*.la +config.h +*.in +Makefile +config.guess +config.sub +depcomp +ltmain.sh +install-sh +missing +.deps +.libs +*.lo + diff --git a/MANIFEST b/MANIFEST deleted file mode 100644 index 557abf7..0000000 --- a/MANIFEST +++ /dev/null @@ -1,363 +0,0 @@ -COPYING -Makefile -NEWS -_camera.c -_camera.so -button.py -color.py -constants.py -glive.py -gplay.py -greplay.py -instance.py -model.py -p5.py -p5_button.py -record.py -recorded.py -recordtube.py -serialize.py -setup.py -toolbarcombobox.py -tray.py -ui.py -utils.py -_camera.o -activity/activity-record.svg -activity/activity.info -gfx/corner-info.svg -gfx/full-insensitive.png -gfx/info-on.svg -gfx/max-enlarge.svg -gfx/max-reduce.svg -gfx/media-circle.png -gfx/media-insensitive.png -gfx/media-pause.png -gfx/media-play.png -gfx/media-record-red.png -gfx/media-record.png -gfx/media-record.svg -gfx/object-audio.svg -gfx/object-photo.svg -gfx/object-video.svg -gfx/photoShutter.wav -gfx/xo-guy.svg -icons/media-audio.svg -icons/media-photo.svg -icons/media-video.svg -po/Record.pot -po/af.po -po/am.po -po/ar.po -po/ay.po -po/bg.po -po/bn.po -po/bn_IN.po -po/ca.po -po/de.po -po/dz.po -po/el.po -po/en.po -po/es.po -po/fa.po -po/fa_AF.po -po/ff.po -po/fr.po -po/gu.po -po/ha.po -po/hi.po -po/ht.po -po/ig.po -po/is.po -po/it.po -po/ja.po -po/km.po -po/ko.po -po/mk.po -po/ml.po -po/mn.po -po/mr.po -po/mvo.po -po/nb.po -po/ne.po -po/nl.po -po/pa.po -po/pap.po -po/pis.po -po/pl.po -po/ps.po -po/pseudo.po -po/pt.po -po/pt_BR.po -po/qu.po -po/ro.po -po/ru.po -po/rw.po -po/sd.po -po/si.po -po/sl.po -po/ta.po -po/te.po -po/th.po -po/tpi.po -po/tr.po -po/ur.po -po/yo.po -po/zh_CN.po -po/zh_TW.po - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -po/vi.po -po/sk.po -po/cs.po -po/bi.po -po/na.po -po/sw.po -po/sv.po -locale/ay/activity.linfo -locale/ay/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/af/activity.linfo -locale/af/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/ar/activity.linfo -locale/ar/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/is/activity.linfo -locale/is/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/ht/activity.linfo -locale/ht/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/tr/activity.linfo -locale/tr/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/pt_BR/activity.linfo -locale/pt_BR/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/yo/activity.linfo -locale/yo/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/sd/activity.linfo -locale/sd/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/mn/activity.linfo -locale/mn/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/ca/activity.linfo -locale/ca/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/zh_TW/activity.linfo -locale/zh_TW/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/ig/activity.linfo -locale/ig/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/ja/activity.linfo -locale/ja/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/am/activity.linfo -locale/am/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/qu/activity.linfo -locale/qu/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/vi/activity.linfo -locale/vi/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/ps/activity.linfo -locale/ps/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/hi/activity.linfo -locale/hi/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/gu/activity.linfo -locale/gu/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/sk/activity.linfo -locale/sk/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/de/activity.linfo -locale/de/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/ur/activity.linfo -locale/ur/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/ta/activity.linfo -locale/ta/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/ha/activity.linfo -locale/ha/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/bg/activity.linfo -locale/bg/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/pt/activity.linfo -locale/pt/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/pap/activity.linfo -locale/pap/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/it/activity.linfo -locale/it/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/tpi/activity.linfo -locale/tpi/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/mk/activity.linfo -locale/mk/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/el/activity.linfo -locale/el/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/ru/activity.linfo -locale/ru/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/nl/activity.linfo -locale/nl/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/fa_AF/activity.linfo -locale/fa_AF/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/pa/activity.linfo -locale/pa/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/rw/activity.linfo -locale/rw/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/fr/activity.linfo -locale/fr/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/sw/activity.linfo -locale/sw/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/te/activity.linfo -locale/te/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/fa/activity.linfo -locale/fa/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/zh_CN/activity.linfo -locale/zh_CN/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/en/activity.linfo -locale/en/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/mvo/activity.linfo -locale/mvo/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/sl/activity.linfo -locale/sl/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/th/activity.linfo -locale/th/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/ro/activity.linfo -locale/ro/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/sv/activity.linfo -locale/sv/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/dz/activity.linfo -locale/dz/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/es/activity.linfo -locale/es/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/km/activity.linfo -locale/km/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/na/activity.linfo -locale/na/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/ne/activity.linfo -locale/ne/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/cs/activity.linfo -locale/cs/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/bi/activity.linfo -locale/bi/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/pl/activity.linfo -locale/pl/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/bn_IN/activity.linfo -locale/bn_IN/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/si/activity.linfo -locale/si/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/mr/activity.linfo -locale/mr/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/ff/activity.linfo -locale/ff/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/nb/activity.linfo -locale/nb/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/pis/activity.linfo -locale/pis/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/ml/activity.linfo -locale/ml/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/ko/activity.linfo -locale/ko/LC_MESSAGES/org.laptop.RecordActivity.mo -locale/bn/activity.linfo -locale/bn/LC_MESSAGES/org.laptop.RecordActivity.mo -glivex.py @@ -1,3 +1,22 @@ +63 + +* Change only volumes for "Capture" channels and let user change others +* Record controls only appear on second launch #762 + +62 + +* Select quality for enconding video +* Unmute/set-to-max capture channels + workaround code until sugar controls capture volumes in the shell +* Revert two pass video encoding to support limited hardware +* Save GUI state in Journal objects + +61 + +* Use uuid and do not md5 video files +* Fallback to ximagesink if xvimagesink is unaccessible +* Use one pass for encodings + 60 * pipeline rework; makes the activity work with new gstreamer diff --git a/_camera.so b/_camera.so Binary files differdeleted file mode 100755 index ec00d95..0000000 --- a/_camera.so +++ /dev/null diff --git a/activity/activity.info b/activity/activity.info index 04d5623..ad7ad8b 100644 --- a/activity/activity.info +++ b/activity/activity.info @@ -1,9 +1,9 @@ [Activity] name = Record -service_name = org.laptop.RecordActivity -class = record.Record +bundle_id = org.laptop.RecordActivity +exec = Record.sh icon = activity-record -activity_version = 60 +activity_version = 63 show_launcher = yes license = MIT -update_url = http://wiki.laptop.org/go/Activities/G1G1 +update_url = http://activities.sugarlabs.org/en-US/sugar/addon/4081 diff --git a/aplay.py b/aplay.py new file mode 100644 index 0000000..596d880 --- /dev/null +++ b/aplay.py @@ -0,0 +1,39 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import gst + +import logging +logger = logging.getLogger('record:aplay.py') + +def play(file): + player.set_state(gst.STATE_NULL) + player.props.uri = 'file://' + file + player.set_state(gst.STATE_PLAYING) + +def _gstmessage_cb(bus, message): + if message.type == gst.MESSAGE_EOS: + player.set_state(gst.STATE_NULL) + elif message.type == gst.MESSAGE_ERROR: + err, debug = message.parse_error() + logger.error('play_pipe: %s %s' % (err, debug)) + player.set_state(gst.STATE_NULL) + +player = gst.element_factory_make('playbin') +fakesink = gst.element_factory_make('fakesink') +player.set_property("video-sink", fakesink) + +bus = player.get_bus() +bus.add_signal_watch() +bus.connect('message', _gstmessage_cb) diff --git a/bin/Record.sh b/bin/Record.sh new file mode 100755 index 0000000..8dc1c0c --- /dev/null +++ b/bin/Record.sh @@ -0,0 +1,4 @@ +#!/bin/sh +root=`cd $(dirname $0)/..; pwd` +export GST_PLUGIN_PATH="$GST_PLUGIN_PATH:$root/gst" +exec sugar-activity record.Record "$@" @@ -12,97 +12,97 @@ from constants import Constants import utils class RecdButton(TrayButton, gobject.GObject): - def __init__(self, ui, recd): - TrayButton.__init__(self) - self.ui = ui - self.recd = recd + def __init__(self, ui, recd): + TrayButton.__init__(self) + self.ui = ui + self.recd = recd - img = self.getImg( ) - self.set_icon_widget( img ) + img = self.getImg( ) + self.set_icon_widget( img ) - self.ACTIVATE_COPY_ID = 0 - self.ACTIVATE_REMOVE_ID = 0 - self.setup_rollover_options( recd.title ) + self.ACTIVATE_COPY_ID = 0 + self.ACTIVATE_REMOVE_ID = 0 + self.setup_rollover_options( recd.title ) - def getImg( self ): - img = gtk.Image() - ipb = self.recd.getThumbPixbuf() - xoff = 8 - yoff = 8 - pb = None - if (self.recd.type == Constants.TYPE_PHOTO): - if (self.recd.buddy): - thumbPhotoSvg = utils.loadSvg(Constants.thumbPhotoSvgData, self.recd.colorStroke.hex, self.recd.colorFill.hex) - pb = thumbPhotoSvg.get_pixbuf() - else: - pb = Constants.thumbPhotoSvg.get_pixbuf() + def getImg( self ): + img = gtk.Image() + ipb = self.recd.getThumbPixbuf() + xoff = 8 + yoff = 8 + pb = None + if (self.recd.type == Constants.TYPE_PHOTO): + if (self.recd.buddy): + thumbPhotoSvg = utils.loadSvg(Constants.thumbPhotoSvgData, self.recd.colorStroke.hex, self.recd.colorFill.hex) + pb = thumbPhotoSvg.get_pixbuf() + else: + pb = Constants.thumbPhotoSvg.get_pixbuf() - elif (self.recd.type == Constants.TYPE_VIDEO): - if (self.recd.buddy): - thumbVideoSvg = utils.loadSvg(Constants.thumbVideoSvgData, self.recd.colorStroke.hex, self.recd.colorFill.hex) - pb = thumbVideoSvg.get_pixbuf() - else: - pb = Constants.thumbVideoSvg.get_pixbuf() + elif (self.recd.type == Constants.TYPE_VIDEO): + if (self.recd.buddy): + thumbVideoSvg = utils.loadSvg(Constants.thumbVideoSvgData, self.recd.colorStroke.hex, self.recd.colorFill.hex) + pb = thumbVideoSvg.get_pixbuf() + else: + pb = Constants.thumbVideoSvg.get_pixbuf() - elif (self.recd.type == Constants.TYPE_AUDIO): - if (self.recd.buddy): - thumbAudioSvg = utils.loadSvg(Constants.thumbAudioSvgData, self.recd.colorStroke.hex, self.recd.colorFill.hex) - pb = thumbAudioSvg.get_pixbuf() - else: - pb = Constants.thumbAudioSvg.get_pixbuf() + elif (self.recd.type == Constants.TYPE_AUDIO): + if (self.recd.buddy): + thumbAudioSvg = utils.loadSvg(Constants.thumbAudioSvgData, self.recd.colorStroke.hex, self.recd.colorFill.hex) + pb = thumbAudioSvg.get_pixbuf() + else: + pb = Constants.thumbAudioSvg.get_pixbuf() - img.set_from_pixbuf(pb) - img.show() - ipb.composite(pb, xoff, yoff, ipb.get_width(), ipb.get_height(), xoff, yoff, 1, 1, gtk.gdk.INTERP_BILINEAR, 255) - img.set_from_pixbuf(pb) + img.set_from_pixbuf(pb) + img.show() + ipb.composite(pb, xoff, yoff, ipb.get_width(), ipb.get_height(), xoff, yoff, 1, 1, gtk.gdk.INTERP_BILINEAR, 255) + img.set_from_pixbuf(pb) - gc.collect() + gc.collect() - return img + return img - def setButtClickedId( self, id ): - self.BUTT_CLICKED_ID = id + def setButtClickedId( self, id ): + self.BUTT_CLICKED_ID = id - def getButtClickedId( self ): - return self.BUTT_CLICKED_ID + def getButtClickedId( self ): + return self.BUTT_CLICKED_ID - def setup_rollover_options( self, info ): - palette = Palette(info) - self.set_palette(palette) + def setup_rollover_options( self, info ): + palette = Palette(info) + self.set_palette(palette) - self.rem_menu_item = gtk.MenuItem( Constants.istrRemove ) - self.ACTIVATE_REMOVE_ID = self.rem_menu_item.connect('activate', self._itemRemoveCb) - palette.menu.append(self.rem_menu_item) - self.rem_menu_item.show() + self.rem_menu_item = gtk.MenuItem( Constants.istrRemove ) + self.ACTIVATE_REMOVE_ID = self.rem_menu_item.connect('activate', self._itemRemoveCb) + palette.menu.append(self.rem_menu_item) + self.rem_menu_item.show() - self.addCopyMenuItem() + self.addCopyMenuItem() - def addCopyMenuItem( self ): - if (self.recd.buddy and not self.recd.downloadedFromBuddy): - return - if (self.ACTIVATE_COPY_ID != 0): - return + def addCopyMenuItem( self ): + if (self.recd.buddy and not self.recd.downloadedFromBuddy): + return + if (self.ACTIVATE_COPY_ID != 0): + return - self.copy_menu_item = gtk.MenuItem( Constants.istrCopyToClipboard ) - self.ACTIVATE_COPY_ID = self.copy_menu_item.connect('activate', self._itemCopyToClipboardCb) - self.get_palette().menu.append(self.copy_menu_item) - self.copy_menu_item.show() + self.copy_menu_item = gtk.MenuItem( Constants.istrCopyToClipboard ) + self.ACTIVATE_COPY_ID = self.copy_menu_item.connect('activate', self._itemCopyToClipboardCb) + self.get_palette().menu.append(self.copy_menu_item) + self.copy_menu_item.show() - def cleanUp( self ): - self.rem_menu_item.disconnect( self.ACTIVATE_REMOVE_ID ) - if (self.ACTIVATE_COPY_ID != 0): - self.copy_menu_item.disconnect( self.ACTIVATE_COPY_ID ) + def cleanUp( self ): + self.rem_menu_item.disconnect( self.ACTIVATE_REMOVE_ID ) + if (self.ACTIVATE_COPY_ID != 0): + self.copy_menu_item.disconnect( self.ACTIVATE_COPY_ID ) - def _itemRemoveCb(self, widget): - self.ui.deleteThumbSelection( self.recd ) + def _itemRemoveCb(self, widget): + self.ui.deleteThumbSelection( self.recd ) - def _itemCopyToClipboardCb(self, widget): - self.ui.copyToClipboard( self.recd )
\ No newline at end of file + def _itemCopyToClipboardCb(self, widget): + self.ui.copyToClipboard( self.recd )
\ No newline at end of file diff --git a/Makefile b/camerac/Makefile index 265b3ee..12f6429 100644 --- a/Makefile +++ b/camerac/Makefile @@ -17,21 +17,23 @@ PYCAIRO_INCLUDES=`pkg-config --cflags pycairo` PYCAIRO_LIBS=`pkg-config --libs pycairo` INCLUDES=-I. -I/usr/include/${PYTHON} ${GLIB_INCLUDES} ${PYGTK_INCLUDES} ${CAIRO_INCLUDES} ${PYCAIRO_INCLUDES} ${GTK_INCLUDES} -ARCHFLAGS=-m32 -march=i386 -mtune=generic OPTFLAGS=-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -fasynchronous-unwind-tables -CFLAGS=-g -fPIC -DPIC -LDFLAGS=-shared -nostdlib -Wl,--export-dynamic -pthread +CFLAGS=-g -fPIC -DPIC $(OPTFLAGS) $(INCLUDES) +LDFLAGS=-shared -nostdlib -Wl,--export-dynamic -pthread ${GLIB_LIBS} ${PYGTK_LIBS} ${CAIRO_LIBS} ${PYCAIRO_LIBS} ${GTK_LIBS} -all: build link +ARCH = $(shell arch | grep 64 >/dev/null && echo linux64 || echo linux32) +LIB_DIR = $(ARCH)_$(PYVER) -build: _camera.o -_camera.o: _camera.c - gcc ${INCLUDES} ${ARCHFLAGS} ${OPTFLAGS} ${CFLAGS} -c _camera.c -o _camera.o +all: camera.so + rm -rf $(LIB_DIR) + mkdir $(LIB_DIR) + strip -s $^ + mv $^ $(LIB_DIR)/ + touch $(LIB_DIR)/__init__.py -link: _camera.so -_camera.so: _camera.o - g++ ${LDFLAGS} _camera.o ${GLIB_LIBS} ${PYGTK_LIBS} ${CAIRO_LIBS} ${PYCAIRO_LIBS} ${GTK_LIBS} -Wl,-soname -Wl,_camera.so -o _camera.so +camera.so: camera.o + $(CXX) $(LDFLAGS) -o $@ $^ clean: - @find -name "*.o" -exec rm {} \; - @find -name "*.so" -exec rm {} \; + rm -f *.o + rm -f *.so diff --git a/camerac/__init__.py b/camerac/__init__.py new file mode 100644 index 0000000..dba55ae --- /dev/null +++ b/camerac/__init__.py @@ -0,0 +1,21 @@ +import os +import sys +import logging + +_sys_path = sys.path +_root_path = os.path.dirname(__file__) + +for i in os.listdir(_root_path): + path = os.path.join(_root_path, i) + if (os.path.isdir(path)): + sys.path = _sys_path + [os.path.join('.', path)] + try: + from camera import * + logging.debug('use %s blobs' % path) + _sys_path = None + break + except Exception, e: + logging.debug('skip %s blobs: %s' % (path, e)) + +if _sys_path: + raise('cannot find proper binary blobs') diff --git a/_camera.c b/camerac/camera.c index 694f0a4..cf4795d 100644 --- a/_camera.c +++ b/camerac/camera.c @@ -260,17 +260,17 @@ py_camera_register_classes(PyObject *d) } DL_EXPORT(void) -init_camera(void) +initcamera(void) { PyObject *m, *d; Pycairo_IMPORT; - m = Py_InitModule ("_camera", py_camera_functions); + m = Py_InitModule ("camera", py_camera_functions); d = PyModule_GetDict (m); py_camera_register_classes (d); if (PyErr_Occurred ()) { - Py_FatalError ("can't initialise module _camera"); + Py_FatalError ("can't initialise module camera"); } } @@ -22,50 +22,50 @@ import gtk class Color: - def __init__(self): - pass + def __init__(self): + pass - def init_rgba(self, r, g, b, a): - self._ro = r - self._go = g - self._bo = b - self._ao = a; - self._r = self._ro / 255.0 - self._g = self._go / 255.0 - self._b = self._bo / 255.0 - self._a = self._ao / 255.0 + def init_rgba(self, r, g, b, a): + self._ro = r + self._go = g + self._bo = b + self._ao = a; + self._r = self._ro / 255.0 + self._g = self._go / 255.0 + self._b = self._bo / 255.0 + self._a = self._ao / 255.0 - self._opaque = False - if (self._a == 1): - self.opaque = True + self._opaque = False + if (self._a == 1): + self.opaque = True - rgb_tup = ( self._ro, self._go, self._bo ) - self.hex = self.rgb_to_hex( rgb_tup ) - self.gColor = gtk.gdk.color_parse( self.hex ) + rgb_tup = ( self._ro, self._go, self._bo ) + self.hex = self.rgb_to_hex( rgb_tup ) + self.gColor = gtk.gdk.color_parse( self.hex ) - def init_gdk(self, col): - self.init_hex( col.get_html() ) + def init_gdk(self, col): + self.init_hex( col.get_html() ) - def init_hex(self, hex): - cTup = self.hex_to_rgb( hex ) - self.init_rgba( cTup[0], cTup[1], cTup[2], 255 ) + def init_hex(self, hex): + cTup = self.hex_to_rgb( hex ) + self.init_rgba( cTup[0], cTup[1], cTup[2], 255 ) - def get_int(self): - return int(self._a * 255) + (int(self._b * 255) << 8) + (int(self._g * 255) << 16) + (int(self._r * 255) << 24) + def get_int(self): + return int(self._a * 255) + (int(self._b * 255) << 8) + (int(self._g * 255) << 16) + (int(self._r * 255) << 24) - def rgb_to_hex(self, rgb_tup): - hexcolor = '#%02x%02x%02x' % rgb_tup - return hexcolor + def rgb_to_hex(self, rgb_tup): + hexcolor = '#%02x%02x%02x' % rgb_tup + return hexcolor - def hex_to_rgb(self, h): - c = eval('0x' + h[1:]) - r = (c >> 16) & 0xFF - g = (c >> 8) & 0xFF - b = c & 0xFF - return (int(r), int(g), int(b))
\ No newline at end of file + def hex_to_rgb(self, h): + c = eval('0x' + h[1:]) + r = (c >> 16) & 0xFF + g = (c >> 8) & 0xFF + b = c & 0xFF + return (int(r), int(g), int(b))
\ No newline at end of file diff --git a/constants.py b/constants.py index 64fbc4f..0da125a 100644 --- a/constants.py +++ b/constants.py @@ -10,345 +10,345 @@ from instance import Instance from sugar import profile from color import Color import utils -import _camera +import camerac import cairo import pango import pangocairo class Constants: - VERSION = 54 - - SERVICE = "org.laptop.Record" - IFACE = SERVICE - PATH = "/org/laptop/Record" - activityId = None - - recdTitle = "title" - recdTags = "tags" - recdTime = "time" - recdRecorderName = "photographer" - recdRecorderHash = "recorderHash" - recdColorStroke = "colorStroke" - recdColorFill = "colorFill" - recdHashKey = "hashKey" - recdBuddy = "buddy" - recdMediaMd5 = "mediaMd5" - recdThumbMd5 = "thumbMd5" - recdMediaBytes = "mediaBytes" - recdThumbBytes = "thumbBytes" - recdBase64Thumb = "base64Thumb" - recdDatastoreId = "datastoreId" - recdAudioImage = "audioImage" - recdAlbum = "album" - recdType = "type" - recdRecd = "recd" - recdRecordVersion = "version" - - keyName = "name" - keyMime = "mime" - keyExt = "ext" - keyIstr = "istr" - - MODE_PHOTO = 0 - MODE_VIDEO = 1 - MODE_AUDIO = 2 - TYPE_PHOTO = MODE_PHOTO - TYPE_VIDEO = MODE_VIDEO - TYPE_AUDIO = MODE_AUDIO - - TIMER_0 = 0 - TIMER_5 = 5 - TIMER_10 = 10 - TIMERS = [] - TIMERS.append(TIMER_0) - TIMERS.append(TIMER_5) - TIMERS.append(TIMER_10) - - DURATION_2 = 2 - DURATION_4 = 4 - DURATION_6 = 6 - DURATIONS = [] - DURATIONS.append(DURATION_2) - DURATIONS.append(DURATION_4) - DURATIONS.append(DURATION_6) - - colorBlack = Color() - colorBlack.init_rgba( 0, 0, 0, 255 ) - colorWhite = Color() - colorWhite.init_rgba( 255, 255, 255, 255 ) - colorRed = Color() - colorRed.init_rgba( 255, 0, 0, 255) - colorGreen = Color() - colorGreen.init_rgba( 0, 255, 0, 255) - colorBlue = Color() - colorBlue.init_rgba( 0, 0, 255, 255) - colorButton = Color() - colorButton.init_gdk( sugar.graphics.style.COLOR_BUTTON_GREY ) - - gfxPath = os.path.join(activity.get_bundle_path(), "gfx") - soundClick = os.path.join(gfxPath, 'photoShutter.wav') - - - #defensive method against variables not translated correctly - def _(s): - #todo: permanent variable - istrsTest = {} - for i in range (0,4): - istrsTest[str(i)] = str(i) - - i = s - try: - #test translating the string with many replacements - i = gt(s) - test = i % istrsTest - except: - #if it doesn't work, revert - i = s - - return i - - - istrActivityName = _('Record') - istrPhoto = _('Photo') - istrVideo = _('Video') - istrAudio = _('Audio') - istrTimelapse = _('Time Lapse') - istrAnimation = _('Animation') - istrPanorama = _('Panorama') - istrInterview= _('Interview') - #TRANS: photo by photographer, e.g., "Photo by Mary" - istrBy = _("%(1)s by %(2)s") - istrTitle = _('Title:') - istrRecorder = _('Recorder:') - istrDate = _('Date:') - istrTags = _('Tags:') - istrSaving = _('Saving') - istrFinishedRecording = _("Finished recording") - istrHoursMinutesSecondsRemaining = _("%(1)s hours, %(2)s minutes, %(3)s seconds remaining") - istrMinutesSecondsRemaining = _("%(1)s minutes, %(2)s seconds remaining") - istrHoursRemaining = _("%(1)s hours remaining") - istrMinutesRemaining = _("%(1)s minutes remaining") - istrSecondsRemaining = _("%(1)s seconds remaining") - istrHours = _("%(1)s hours") - istrMinutes = _("%(1)s minutes") - istrSeconds = _("%(1)s seconds") - istrRemove = _("Remove") - istrStoppedRecording = _("Stopped recording") - istrCopyToClipboard = _("Copy to clipboard") - istrTimer = _("Timer:") - istrDuration = _("Duration:") - istrNow = _("Immediate") - istrPlay = _("Play") - istrPause = _("Pause") - istrAddFrame = _("Add frame") - istrRemoveFrame = _("Remove frame") - istrFramesPerSecond = _("%(1)s frames per second") - istrQuality = _("Quality") - istrDefault = _("Default") - istrBestQuality = _("Best") - istrHighQuality = _("High") - istrLowQuality = _("Low") - istrLargeFile = _("Large file") - istrSmallFile = _("Small file") - istrSilent = _("Silent") - istrRotate = _("Rotate") - istrWidth = _("Width") - istrHeight = _("Height") - istrClickToTakePicture = _("Click to take picture") - istrClickToAddPicture = _("Click to add picture") - #TRANS: Downloading Photo from Mary - istrDownloadingFrom = _("Downloading %(1)s from %(2)s") - #TRANS: Cannot download this Photo - istrCannotDownload = _("Cannot download this %(1)s") - #TRANS: Save Photo to: - istrSaveTo = _("Save %(1)s to:") - istrYourDiskIsFull = _("Your %(1)s is full") - istrJournal = _("Journal") - istrUSB = _("USB") - istrCompactFlash = _("SD Card") - istrPreferences = _("Preferences") - istrFreeSpace = _("Free space:") - #TRANS: 7 photos - istrNumPhotos = _("%(1)s photos") - istrBitrate = _("Bitrate") - istrMaxBitrate = _("Maximum Bitrate") - istrMinBitrate = _("Minumum Bitrate") - istrManageBitrate = _("Manage Bitrate") - istrBorder = _("Border") - istrCenter = _("Center") - istrFrames = _("Frames") - istrKeyframeAuto = _("Automatic keyframe detection") - istrKeyframeForce = _("Force keyframe") - istrKeyframeFrequency = _("Keyframe frequency") - istrKeyframeMinDist = _("Keyframe minimum distance") - istrKeyframeThreshold = _("Keyframe threshold") - istrNoiseSensitivity = _("Noise Sensitivity") - istrQuick = _("Quick") - istrSharpness = _("Sharpness") - istrCapacity = _("Capacity") - - mediaTypes = {} - mediaTypes[TYPE_PHOTO] = {keyName:"photo", keyMime:"image/jpeg", keyExt:"jpg", keyIstr:istrPhoto} - mediaTypes[TYPE_VIDEO] = {keyName:"video", keyMime:"video/ogg", keyExt:"ogg", keyIstr:istrVideo} - mediaTypes[TYPE_AUDIO] = {keyName:"audio", keyMime:"audio/ogg", keyExt:"ogg", keyIstr:istrAudio} - - thumbPhotoSvgData = None - thumbPhotoSvg = None - thumbVideoSvg = None - maxEnlargeSvg = None - maxReduceSvg = None - infoOnSvg = None - xoGuySvgData = None - - recImg = None - recRedImg = None - recCircleCairo = None - recInsensitiveImg = None - recPlayImg = None - recPauseImg = None - countdownImgs = {} - - dim_CONTROLBAR_HT = 55 - - keepFreeKbOnXo = 200000 - - def __init__( self, ca ): - self.__class__.activityId = ca._activity_id - - thumbPhotoSvgPath = os.path.join(self.__class__.gfxPath, 'object-photo.svg') - thumbPhotoSvgFile = open(thumbPhotoSvgPath, 'r') - self.__class__.thumbPhotoSvgData = thumbPhotoSvgFile.read() - self.__class__.thumbPhotoSvg = utils.loadSvg(self.__class__.thumbPhotoSvgData, Instance.colorStroke.hex, Instance.colorFill.hex) - thumbPhotoSvgFile.close() - - thumbVideoSvgPath = os.path.join(self.__class__.gfxPath, 'object-video.svg') - thumbVideoSvgFile = open(thumbVideoSvgPath, 'r') - self.__class__.thumbVideoSvgData = thumbVideoSvgFile.read() - self.__class__.thumbVideoSvg = utils.loadSvg(self.__class__.thumbVideoSvgData, Instance.colorStroke.hex, Instance.colorFill.hex) - thumbVideoSvgFile.close() - - thumbAudioSvgPath = os.path.join(self.__class__.gfxPath, 'object-audio.svg') - thumbAudioSvgFile = open(thumbAudioSvgPath, 'r') - self.__class__.thumbAudioSvgData = thumbAudioSvgFile.read() - self.__class__.thumbAudioSvg = utils.loadSvg(self.__class__.thumbAudioSvgData, Instance.colorStroke.hex, Instance.colorFill.hex) - thumbAudioSvgFile.close() - - maxEnlargeSvgPath = os.path.join(self.__class__.gfxPath, 'max-enlarge.svg') - maxEnlargeSvgFile = open(maxEnlargeSvgPath, 'r') - maxEnlargeSvgData = maxEnlargeSvgFile.read() - self.__class__.maxEnlargeSvg = utils.loadSvg(maxEnlargeSvgData, None, None ) - maxEnlargeSvgFile.close() - - maxReduceSvgPath = os.path.join(self.__class__.gfxPath, 'max-reduce.svg') - maxReduceSvgFile = open(maxReduceSvgPath, 'r') - maxReduceSvgData = maxReduceSvgFile.read() - self.__class__.maxReduceSvg = utils.loadSvg(maxReduceSvgData, None, None ) - maxReduceSvgFile.close() - - infoOnSvgPath = os.path.join(self.__class__.gfxPath, 'corner-info.svg') - infoOnSvgFile = open(infoOnSvgPath, 'r') - infoOnSvgData = infoOnSvgFile.read() - self.__class__.infoOnSvg = utils.loadSvg(infoOnSvgData, None, None ) - infoOnSvgFile.close() - - xoGuySvgPath = os.path.join(self.__class__.gfxPath, 'xo-guy.svg') - xoGuySvgFile = open(xoGuySvgPath, 'r') - self.__class__.xoGuySvgData = xoGuySvgFile.read() - xoGuySvgFile.close() - - recFile = os.path.join(self.__class__.gfxPath, 'media-record.png') - recPixbuf = gtk.gdk.pixbuf_new_from_file(recFile) - self.__class__.recImg = gtk.Image() - self.__class__.recImg.set_from_pixbuf( recPixbuf ) - - recRedFile = os.path.join(self.__class__.gfxPath, 'media-record-red.png') - recRedPixbuf = gtk.gdk.pixbuf_new_from_file(recRedFile) - self.__class__.recRedImg = gtk.Image() - self.__class__.recRedImg.set_from_pixbuf( recRedPixbuf ) - - recCircleFile = os.path.join(self.__class__.gfxPath, 'media-circle.png') - recCirclePixbuf = gtk.gdk.pixbuf_new_from_file(recCircleFile) - self.__class__.recCircleCairo = _camera.cairo_surface_from_gdk_pixbuf(recCirclePixbuf) - - recInsFile = os.path.join(self.__class__.gfxPath, 'media-insensitive.png') - recInsPixbuf = gtk.gdk.pixbuf_new_from_file(recInsFile) - self.__class__.recInsensitiveImg = gtk.Image() - self.__class__.recInsensitiveImg.set_from_pixbuf( recInsPixbuf ) - - fullInsFile = os.path.join(self.__class__.gfxPath, 'full-insensitive.png') - fullInsPixbuf = gtk.gdk.pixbuf_new_from_file(fullInsFile) - self.__class__.fullInsensitiveImg = gtk.Image() - self.__class__.fullInsensitiveImg.set_from_pixbuf( fullInsPixbuf ) - - recPlayFile = os.path.join(self.__class__.gfxPath, 'media-play.png') - recPlayPixbuf = gtk.gdk.pixbuf_new_from_file(recPlayFile) - self.__class__.recPlayImg = gtk.Image() - self.__class__.recPlayImg.set_from_pixbuf( recPlayPixbuf ) - - recPauseFile = os.path.join(self.__class__.gfxPath, 'media-pause.png') - recPausePixbuf = gtk.gdk.pixbuf_new_from_file(recPauseFile) - self.__class__.recPauseImg = gtk.Image() - self.__class__.recPauseImg.set_from_pixbuf( recPausePixbuf ) - - self._ts = self.__class__.TIMERS - longestTime = self._ts[len(self._ts)-1] - for i in range (0, longestTime): - self.createCountdownPng( i ) - - - def createCountdownPng(self, num): - todisk = True - - rendered = False - if (todisk): - path = os.path.join(Instance.dataPath, str(num)+".png") - if (os.path.exists(path)): - rendered = True - - - if (not rendered): - w = self.__class__.dim_CONTROLBAR_HT - h = w - if (todisk): - cimg = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) - ctx = cairo.Context(cimg) - else: - pixmap = gtk.gdk.Pixmap(None, w, h, 24) - ctx = pixmap.cairo_create() - ctx.rectangle(0, 0, w, h) - ctx.set_source_rgb(0, 0, 0) - ctx.fill() - x = 0 - y = 4 - ctx.translate(x,y) - ctx.set_source_surface (self.__class__.recCircleCairo, 0, 0) - ctx.paint() - ctx.translate(-x,-y) - - ctx.set_source_rgb(255, 255, 255) - pctx = pangocairo.CairoContext(ctx) - play = pctx.create_layout() - font = pango.FontDescription("sans 30") - play.set_font_description(font) - play.set_text( ""+str(num) ) - dim = play.get_pixel_extents() - ctx.translate( -dim[0][0], -dim[0][1] ) - xoff = (w-dim[0][2])/2 - yoff = (h-dim[0][3])/2 - ctx.translate( xoff, yoff ) - ctx.translate( -3, 0 ) - pctx.show_layout(play) - - img = gtk.Image() - if (todisk): - path = os.path.join(Instance.dataPath, str(num)+".png") - if (not rendered): - path = utils.getUniqueFilepath(path, 0) - cimg.write_to_png(path) - numPixbuf = gtk.gdk.pixbuf_new_from_file(path) - img.set_from_pixbuf( numPixbuf ) - else: - img.set_from_pixmap(pixmap, None) - - self.__class__.countdownImgs[int(num)] = img + VERSION = 54 + + SERVICE = "org.laptop.Record" + IFACE = SERVICE + PATH = "/org/laptop/Record" + activityId = None + + recdTitle = "title" + recdTags = "tags" + recdTime = "time" + recdRecorderName = "photographer" + recdRecorderHash = "recorderHash" + recdColorStroke = "colorStroke" + recdColorFill = "colorFill" + recdHashKey = "hashKey" + recdBuddy = "buddy" + recdMediaMd5 = "mediaMd5" + recdThumbMd5 = "thumbMd5" + recdMediaBytes = "mediaBytes" + recdThumbBytes = "thumbBytes" + recdBase64Thumb = "base64Thumb" + recdDatastoreId = "datastoreId" + recdAudioImage = "audioImage" + recdAlbum = "album" + recdType = "type" + recdRecd = "recd" + recdRecordVersion = "version" + + keyName = "name" + keyMime = "mime" + keyExt = "ext" + keyIstr = "istr" + + MODE_PHOTO = 0 + MODE_VIDEO = 1 + MODE_AUDIO = 2 + TYPE_PHOTO = MODE_PHOTO + TYPE_VIDEO = MODE_VIDEO + TYPE_AUDIO = MODE_AUDIO + + TIMER_0 = 0 + TIMER_5 = 5 + TIMER_10 = 10 + TIMERS = [] + TIMERS.append(TIMER_0) + TIMERS.append(TIMER_5) + TIMERS.append(TIMER_10) + + DURATION_2 = 2 + DURATION_4 = 4 + DURATION_6 = 6 + DURATIONS = [] + DURATIONS.append(DURATION_2) + DURATIONS.append(DURATION_4) + DURATIONS.append(DURATION_6) + + colorBlack = Color() + colorBlack.init_rgba( 0, 0, 0, 255 ) + colorWhite = Color() + colorWhite.init_rgba( 255, 255, 255, 255 ) + colorRed = Color() + colorRed.init_rgba( 255, 0, 0, 255) + colorGreen = Color() + colorGreen.init_rgba( 0, 255, 0, 255) + colorBlue = Color() + colorBlue.init_rgba( 0, 0, 255, 255) + colorButton = Color() + colorButton.init_gdk( sugar.graphics.style.COLOR_BUTTON_GREY ) + + gfxPath = os.path.join(activity.get_bundle_path(), "gfx") + soundClick = os.path.join(gfxPath, 'photoShutter.wav') + + + #defensive method against variables not translated correctly + def _(s): + #todo: permanent variable + istrsTest = {} + for i in range (0,4): + istrsTest[str(i)] = str(i) + + i = s + try: + #test translating the string with many replacements + i = gt(s) + test = i % istrsTest + except: + #if it doesn't work, revert + i = s + + return i + + + istrActivityName = _('Record') + istrPhoto = _('Photo') + istrVideo = _('Video') + istrAudio = _('Audio') + istrTimelapse = _('Time Lapse') + istrAnimation = _('Animation') + istrPanorama = _('Panorama') + istrInterview= _('Interview') + #TRANS: photo by photographer, e.g., "Photo by Mary" + istrBy = _("%(1)s by %(2)s") + istrTitle = _('Title:') + istrRecorder = _('Recorder:') + istrDate = _('Date:') + istrTags = _('Tags:') + istrSaving = _('Saving') + istrFinishedRecording = _("Finished recording") + istrHoursMinutesSecondsRemaining = _("%(1)s hours, %(2)s minutes, %(3)s seconds remaining") + istrMinutesSecondsRemaining = _("%(1)s minutes, %(2)s seconds remaining") + istrHoursRemaining = _("%(1)s hours remaining") + istrMinutesRemaining = _("%(1)s minutes remaining") + istrSecondsRemaining = _("%(1)s seconds remaining") + istrHours = _("%(1)s hours") + istrMinutes = _("%(1)s minutes") + istrSeconds = _("%(1)s seconds") + istrRemove = _("Remove") + istrStoppedRecording = _("Stopped recording") + istrCopyToClipboard = _("Copy to clipboard") + istrTimer = _("Timer:") + istrDuration = _("Duration:") + istrNow = _("Immediate") + istrPlay = _("Play") + istrPause = _("Pause") + istrAddFrame = _("Add frame") + istrRemoveFrame = _("Remove frame") + istrFramesPerSecond = _("%(1)s frames per second") + istrQuality = _("Quality") + istrDefault = _("Default") + istrBestQuality = _("Best") + istrHighQuality = _("High") + istrLowQuality = _("Low") + istrLargeFile = _("Large file") + istrSmallFile = _("Small file") + istrSilent = _("Silent") + istrRotate = _("Rotate") + istrWidth = _("Width") + istrHeight = _("Height") + istrClickToTakePicture = _("Click to take picture") + istrClickToAddPicture = _("Click to add picture") + #TRANS: Downloading Photo from Mary + istrDownloadingFrom = _("Downloading %(1)s from %(2)s") + #TRANS: Cannot download this Photo + istrCannotDownload = _("Cannot download this %(1)s") + #TRANS: Save Photo to: + istrSaveTo = _("Save %(1)s to:") + istrYourDiskIsFull = _("Your %(1)s is full") + istrJournal = _("Journal") + istrUSB = _("USB") + istrCompactFlash = _("SD Card") + istrPreferences = _("Preferences") + istrFreeSpace = _("Free space:") + #TRANS: 7 photos + istrNumPhotos = _("%(1)s photos") + istrBitrate = _("Bitrate") + istrMaxBitrate = _("Maximum Bitrate") + istrMinBitrate = _("Minumum Bitrate") + istrManageBitrate = _("Manage Bitrate") + istrBorder = _("Border") + istrCenter = _("Center") + istrFrames = _("Frames") + istrKeyframeAuto = _("Automatic keyframe detection") + istrKeyframeForce = _("Force keyframe") + istrKeyframeFrequency = _("Keyframe frequency") + istrKeyframeMinDist = _("Keyframe minimum distance") + istrKeyframeThreshold = _("Keyframe threshold") + istrNoiseSensitivity = _("Noise Sensitivity") + istrQuick = _("Quick") + istrSharpness = _("Sharpness") + istrCapacity = _("Capacity") + + mediaTypes = {} + mediaTypes[TYPE_PHOTO] = {keyName:"photo", keyMime:"image/jpeg", keyExt:"jpg", keyIstr:istrPhoto} + mediaTypes[TYPE_VIDEO] = {keyName:"video", keyMime:"video/ogg", keyExt:"ogg", keyIstr:istrVideo} + mediaTypes[TYPE_AUDIO] = {keyName:"audio", keyMime:"audio/ogg", keyExt:"ogg", keyIstr:istrAudio} + + thumbPhotoSvgData = None + thumbPhotoSvg = None + thumbVideoSvg = None + maxEnlargeSvg = None + maxReduceSvg = None + infoOnSvg = None + xoGuySvgData = None + + recImg = None + recRedImg = None + recCircleCairo = None + recInsensitiveImg = None + recPlayImg = None + recPauseImg = None + countdownImgs = {} + + dim_CONTROLBAR_HT = 55 + + keepFreeKbOnXo = 100000 + + def __init__( self, ca ): + self.__class__.activityId = ca._activity_id + + thumbPhotoSvgPath = os.path.join(self.__class__.gfxPath, 'object-photo.svg') + thumbPhotoSvgFile = open(thumbPhotoSvgPath, 'r') + self.__class__.thumbPhotoSvgData = thumbPhotoSvgFile.read() + self.__class__.thumbPhotoSvg = utils.loadSvg(self.__class__.thumbPhotoSvgData, Instance.colorStroke.hex, Instance.colorFill.hex) + thumbPhotoSvgFile.close() + + thumbVideoSvgPath = os.path.join(self.__class__.gfxPath, 'object-video.svg') + thumbVideoSvgFile = open(thumbVideoSvgPath, 'r') + self.__class__.thumbVideoSvgData = thumbVideoSvgFile.read() + self.__class__.thumbVideoSvg = utils.loadSvg(self.__class__.thumbVideoSvgData, Instance.colorStroke.hex, Instance.colorFill.hex) + thumbVideoSvgFile.close() + + thumbAudioSvgPath = os.path.join(self.__class__.gfxPath, 'object-audio.svg') + thumbAudioSvgFile = open(thumbAudioSvgPath, 'r') + self.__class__.thumbAudioSvgData = thumbAudioSvgFile.read() + self.__class__.thumbAudioSvg = utils.loadSvg(self.__class__.thumbAudioSvgData, Instance.colorStroke.hex, Instance.colorFill.hex) + thumbAudioSvgFile.close() + + maxEnlargeSvgPath = os.path.join(self.__class__.gfxPath, 'max-enlarge.svg') + maxEnlargeSvgFile = open(maxEnlargeSvgPath, 'r') + maxEnlargeSvgData = maxEnlargeSvgFile.read() + self.__class__.maxEnlargeSvg = utils.loadSvg(maxEnlargeSvgData, None, None ) + maxEnlargeSvgFile.close() + + maxReduceSvgPath = os.path.join(self.__class__.gfxPath, 'max-reduce.svg') + maxReduceSvgFile = open(maxReduceSvgPath, 'r') + maxReduceSvgData = maxReduceSvgFile.read() + self.__class__.maxReduceSvg = utils.loadSvg(maxReduceSvgData, None, None ) + maxReduceSvgFile.close() + + infoOnSvgPath = os.path.join(self.__class__.gfxPath, 'corner-info.svg') + infoOnSvgFile = open(infoOnSvgPath, 'r') + infoOnSvgData = infoOnSvgFile.read() + self.__class__.infoOnSvg = utils.loadSvg(infoOnSvgData, None, None ) + infoOnSvgFile.close() + + xoGuySvgPath = os.path.join(self.__class__.gfxPath, 'xo-guy.svg') + xoGuySvgFile = open(xoGuySvgPath, 'r') + self.__class__.xoGuySvgData = xoGuySvgFile.read() + xoGuySvgFile.close() + + recFile = os.path.join(self.__class__.gfxPath, 'media-record.png') + recPixbuf = gtk.gdk.pixbuf_new_from_file(recFile) + self.__class__.recImg = gtk.Image() + self.__class__.recImg.set_from_pixbuf( recPixbuf ) + + recRedFile = os.path.join(self.__class__.gfxPath, 'media-record-red.png') + recRedPixbuf = gtk.gdk.pixbuf_new_from_file(recRedFile) + self.__class__.recRedImg = gtk.Image() + self.__class__.recRedImg.set_from_pixbuf( recRedPixbuf ) + + recCircleFile = os.path.join(self.__class__.gfxPath, 'media-circle.png') + recCirclePixbuf = gtk.gdk.pixbuf_new_from_file(recCircleFile) + self.__class__.recCircleCairo = camerac.cairo_surface_from_gdk_pixbuf(recCirclePixbuf) + + recInsFile = os.path.join(self.__class__.gfxPath, 'media-insensitive.png') + recInsPixbuf = gtk.gdk.pixbuf_new_from_file(recInsFile) + self.__class__.recInsensitiveImg = gtk.Image() + self.__class__.recInsensitiveImg.set_from_pixbuf( recInsPixbuf ) + + fullInsFile = os.path.join(self.__class__.gfxPath, 'full-insensitive.png') + fullInsPixbuf = gtk.gdk.pixbuf_new_from_file(fullInsFile) + self.__class__.fullInsensitiveImg = gtk.Image() + self.__class__.fullInsensitiveImg.set_from_pixbuf( fullInsPixbuf ) + + recPlayFile = os.path.join(self.__class__.gfxPath, 'media-play.png') + recPlayPixbuf = gtk.gdk.pixbuf_new_from_file(recPlayFile) + self.__class__.recPlayImg = gtk.Image() + self.__class__.recPlayImg.set_from_pixbuf( recPlayPixbuf ) + + recPauseFile = os.path.join(self.__class__.gfxPath, 'media-pause.png') + recPausePixbuf = gtk.gdk.pixbuf_new_from_file(recPauseFile) + self.__class__.recPauseImg = gtk.Image() + self.__class__.recPauseImg.set_from_pixbuf( recPausePixbuf ) + + self._ts = self.__class__.TIMERS + longestTime = self._ts[len(self._ts)-1] + for i in range (0, longestTime): + self.createCountdownPng( i ) + + + def createCountdownPng(self, num): + todisk = True + + rendered = False + if (todisk): + path = os.path.join(Instance.dataPath, str(num)+".png") + if (os.path.exists(path)): + rendered = True + + + if (not rendered): + w = self.__class__.dim_CONTROLBAR_HT + h = w + if (todisk): + cimg = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) + ctx = cairo.Context(cimg) + else: + pixmap = gtk.gdk.Pixmap(None, w, h, 24) + ctx = pixmap.cairo_create() + ctx.rectangle(0, 0, w, h) + ctx.set_source_rgb(0, 0, 0) + ctx.fill() + x = 0 + y = 4 + ctx.translate(x,y) + ctx.set_source_surface (self.__class__.recCircleCairo, 0, 0) + ctx.paint() + ctx.translate(-x,-y) + + ctx.set_source_rgb(255, 255, 255) + pctx = pangocairo.CairoContext(ctx) + play = pctx.create_layout() + font = pango.FontDescription("sans 30") + play.set_font_description(font) + play.set_text( ""+str(num) ) + dim = play.get_pixel_extents() + ctx.translate( -dim[0][0], -dim[0][1] ) + xoff = (w-dim[0][2])/2 + yoff = (h-dim[0][3])/2 + ctx.translate( xoff, yoff ) + ctx.translate( -3, 0 ) + pctx.show_layout(play) + + img = gtk.Image() + if (todisk): + path = os.path.join(Instance.dataPath, str(num)+".png") + if (not rendered): + path = utils.getUniqueFilepath(path, 0) + cimg.write_to_png(path) + numPixbuf = gtk.gdk.pixbuf_new_from_file(path) + img.set_from_pixbuf( numPixbuf ) + else: + img.set_from_pixmap(pixmap, None) + + self.__class__.countdownImgs[int(num)] = img @@ -18,541 +18,440 @@ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN #THE SOFTWARE. - +import re import os import gtk import pygtk pygtk.require('2.0') -import sys import gst -import gst.interfaces import pygst pygst.require('0.10') import time -import threading import gobject gobject.threads_init() +import logging +logger = logging.getLogger('record:glive.py') + +from sugar.activity.activity import get_activity_root + from instance import Instance from constants import Constants -import record import utils import ui -class Glive: - def __init__(self, pca): - self.window = None - self.ca = pca - self._eos_cb = None - - self.playing = False - self.picExposureOpen = False - - self.AUDIO_TRANSCODE_ID = 0 - self.TRANSCODE_ID = 0 - self.VIDEO_TRANSCODE_ID = 0 - - self.PHOTO_MODE_PHOTO = 0 - self.PHOTO_MODE_AUDIO = 1 - - self.TRANSCODE_UPDATE_INTERVAL = 200 - - - self.VIDEO_WIDTH_SMALL = 160 - self.VIDEO_HEIGHT_SMALL = 120 - self.VIDEO_FRAMERATE_SMALL = 10 - - self.VIDEO_WIDTH_LARGE = 200 - self.VIDEO_HEIGHT_LARGE = 150 - self.VIDEO_FRAMERATE_SMALL = 10 - - self.pipeline = gst.Pipeline("my-pipeline") - self.createPhotoBin() - self.createAudioBin() - self.createVideoBin() - self.createPipeline() - - self.thumbPipes = [] - self.muxPipes = [] - - bus = self.pipeline.get_bus() - bus.enable_sync_message_emission() - bus.add_signal_watch() - self.SYNC_ID = bus.connect('sync-message::element', self._onSyncMessageCb) - self.MESSAGE_ID = bus.connect('message', self._onMessageCb) - - def createPhotoBin ( self ): - queue = gst.element_factory_make("queue", "pbqueue") - queue.set_property("leaky", True) - queue.set_property("max-size-buffers", 1) - - colorspace = gst.element_factory_make("ffmpegcolorspace", "pbcolorspace") - jpeg = gst.element_factory_make("jpegenc", "pbjpeg") - - sink = gst.element_factory_make("fakesink", "pbsink") - self.HANDOFF_ID = sink.connect("handoff", self.copyPic) - sink.set_property("signal-handoffs", True) - - self.photobin = gst.Bin("photobin") - self.photobin.add(queue, colorspace, jpeg, sink) - - gst.element_link_many(queue, colorspace, jpeg, sink) - - pad = queue.get_static_pad("sink") - self.photobin.add_pad(gst.GhostPad("sink", pad)) - - def createAudioBin ( self ): - src = gst.element_factory_make("alsasrc", "absrc") - srccaps = gst.Caps("audio/x-raw-int,rate=16000,channels=1,depth=16") - - enc = gst.element_factory_make("wavenc", "abenc") - - sink = gst.element_factory_make("filesink", "absink") - sink.set_property("location", os.path.join(Instance.instancePath, "output.wav")) - - self.audiobin = gst.Bin("audiobin") - self.audiobin.add(src, enc, sink) - - src.link(enc, srccaps) - enc.link(sink) - - def createVideoBin ( self ): - queue = gst.element_factory_make("queue", "vbqueue") - - rate = gst.element_factory_make("videorate", "vbrate") - ratecaps = gst.Caps('video/x-raw-yuv,framerate='+str(self.VIDEO_FRAMERATE_SMALL)+'/1') +TMP_OGV = os.path.join(get_activity_root(), 'instance', 'output.ogv') +TMP_OGG = os.path.join(get_activity_root(), 'instance', 'output.ogg') +TMP_WAV = os.path.join(get_activity_root(), 'instance', 'output.wav') - scale = gst.element_factory_make("videoscale", "vbscale") - scalecaps = gst.Caps('video/x-raw-yuv,width='+str(self.VIDEO_WIDTH_SMALL)+',height='+str(self.VIDEO_HEIGHT_SMALL)) +PLAYBACK_WIDTH = 640 +PLAYBACK_HEIGHT = 480 - colorspace = gst.element_factory_make("ffmpegcolorspace", "vbcolorspace") - - enc = gst.element_factory_make("theoraenc", "vbenc") - enc.set_property("quality", 16) - - mux = gst.element_factory_make("oggmux", "vbmux") - - sink = gst.element_factory_make("filesink", "vbfile") - sink.set_property("location", os.path.join(Instance.instancePath, "output.ogg")) - - self.videobin = gst.Bin("videobin") - self.videobin.add(queue, rate, scale, colorspace, enc, mux, sink) - - queue.link(rate) - rate.link(scale, ratecaps) - scale.link(colorspace, scalecaps) - gst.element_link_many(colorspace, enc, mux, sink) - - pad = queue.get_static_pad("sink") - self.videobin.add_pad(gst.GhostPad("sink", pad)) - - def createPipeline ( self ): - src = gst.element_factory_make("v4l2src", "camsrc") - try: - # old gst-plugins-good does not have this property - src.set_property("queue-size", 2) - except: - pass - - tee = gst.element_factory_make("tee", "tee") - queue = gst.element_factory_make("queue", "dispqueue") - xvsink = gst.element_factory_make("xvimagesink", "xvsink") - self.pipeline.add(src, tee, queue, xvsink) - gst.element_link_many(src, tee, queue, xvsink) - - def thumbPipe(self): - return self.thumbPipes[ len(self.thumbPipes)-1 ] - - - def thumbEl(self, name): - return self.thumbPipe().get_by_name(name) - - - def muxPipe(self): - return self.muxPipes[ len(self.muxPipes)-1 ] - - - def muxEl(self, name): - return self.muxPipe().get_by_name(name) - - - def play(self): - self.pipeline.set_state(gst.STATE_PLAYING) - self.playing = True - - def pause(self): - self.pipeline.set_state(gst.STATE_PAUSED) - self.playing = False - - - def stop(self): - self.pipeline.set_state(gst.STATE_NULL) - self.playing = False - - def is_playing(self): - return self.playing - - def idlePlayElement(self, element): - element.set_state(gst.STATE_PLAYING) - return False - - def stopRecordingAudio( self ): - self.audiobin.set_state(gst.STATE_NULL) - self.pipeline.remove(self.audiobin) - gobject.idle_add( self.stoppedRecordingAudio ) - - - def stoppedRecordingVideo(self): - if ( len(self.thumbPipes) > 0 ): - thumbline = self.thumbPipes[len(self.thumbPipes)-1] - thumbline.get_by_name('thumbFakesink').disconnect(self.THUMB_HANDOFF_ID) - - oggFilepath = os.path.join(Instance.instancePath, "output.ogg") #ogv - if (not os.path.exists(oggFilepath)): - self.record = False - self.ca.m.cannotSaveVideo() - self.ca.m.stoppedRecordingVideo() - return - oggSize = os.path.getsize(oggFilepath) - if (oggSize <= 0): - self.record = False - self.ca.m.cannotSaveVideo() - self.ca.m.stoppedRecordingVideo() - return - - line = 'filesrc location=' + str(oggFilepath) + ' name=thumbFilesrc ! oggdemux name=thumbOggdemux ! theoradec name=thumbTheoradec ! tee name=thumbTee ! queue name=thumbQueue ! ffmpegcolorspace name=thumbFfmpegcolorspace ! jpegenc name=thumbJPegenc ! fakesink name=thumbFakesink' - thumbline = gst.parse_launch(line) - thumbQueue = thumbline.get_by_name('thumbQueue') - thumbQueue.set_property("leaky", True) - thumbQueue.set_property("max-size-buffers", 1) - thumbTee = thumbline.get_by_name('thumbTee') - thumbFakesink = thumbline.get_by_name('thumbFakesink') - self.THUMB_HANDOFF_ID = thumbFakesink.connect("handoff", self.copyThumbPic) - thumbFakesink.set_property("signal-handoffs", True) - self.thumbPipes.append(thumbline) - self.thumbExposureOpen = True - gobject.idle_add( self.idlePlayElement, thumbline ) - - - def stoppedRecordingAudio( self ): - record.Record.log.debug("stoppedRecordingAudio") - if (self.audioPixbuf != None): - audioFilepath = os.path.join(Instance.instancePath, "output.wav")#self.el("audioFilesink").get_property("location") - if (not os.path.exists(audioFilepath)): - self.record = False - self.audio = False - self.ca.m.cannotSaveVideo() - return - wavSize = os.path.getsize(audioFilepath) - if (wavSize <= 0): - self.record = False - self.ca.m.cannotSaveVideo() - return - - self.ca.ui.setPostProcessPixBuf(self.audioPixbuf) - - line = 'filesrc location=' + str(audioFilepath) + ' name=audioFilesrc ! wavparse name=audioWavparse ! audioconvert name=audioAudioconvert ! vorbisenc name=audioVorbisenc ! oggmux name=audioOggmux ! filesink name=audioFilesink' - audioline = gst.parse_launch(line) - - taglist = self.getTags(Constants.TYPE_AUDIO) - base64AudioSnapshot = utils.getStringFromPixbuf(self.audioPixbuf) - taglist[gst.TAG_EXTENDED_COMMENT] = "coverart="+str(base64AudioSnapshot) - vorbisEnc = audioline.get_by_name('audioVorbisenc') - vorbisEnc.merge_tags(taglist, gst.TAG_MERGE_REPLACE_ALL) - - audioFilesink = audioline.get_by_name('audioFilesink') - audioOggFilepath = os.path.join(Instance.instancePath, "output.ogg") - audioFilesink.set_property("location", audioOggFilepath ) - - audioBus = audioline.get_bus() - audioBus.add_signal_watch() - self.AUDIO_TRANSCODE_ID = audioBus.connect('message', self._onMuxedAudioMessageCb, audioline) - self.TRANSCODE_ID = gobject.timeout_add(self.TRANSCODE_UPDATE_INTERVAL, self._transcodeUpdateCb, audioline) - gobject.idle_add( self.idlePlayElement, audioline ) - else: - self.record = False - self.audio = False - self.ca.m.cannotSaveVideo() - - - def getTags( self, type ): - tl = gst.TagList() - tl[gst.TAG_ARTIST] = str(Instance.nickName) - tl[gst.TAG_COMMENT] = "olpc" - #this is unfortunately, unreliable - #record.Record.log.debug("self.ca.metadata['title']->" + str(self.ca.metadata['title']) ) - tl[gst.TAG_ALBUM] = "olpc" #self.ca.metadata['title'] - tl[gst.TAG_DATE] = utils.getDateString(int(time.time())) - stringType = Constants.mediaTypes[type][Constants.keyIstr] - tl[gst.TAG_TITLE] = Constants.istrBy % {"1":stringType, "2":str(Instance.nickName)} - return tl - - def blockedCb(self, x, y, z): - pass - - def _takePhoto(self): - if self.picExposureOpen: - return - - self.picExposureOpen = True - pad = self.photobin.get_static_pad("sink") - pad.set_blocked_async(True, self.blockedCb, None) - self.pipeline.add(self.photobin) - self.photobin.set_state(gst.STATE_PLAYING) - self.pipeline.get_by_name("tee").link(self.photobin) - pad.set_blocked_async(False, self.blockedCb, None) - - def takePhoto(self): - self.photoMode = self.PHOTO_MODE_PHOTO - self._takePhoto() - - def copyPic(self, fsink, buffer, pad, user_data=None): - if not self.picExposureOpen: - return - - pad = self.photobin.get_static_pad("sink") - pad.set_blocked_async(True, self.blockedCb, None) - self.pipeline.get_by_name("tee").unlink(self.photobin) - self.pipeline.remove(self.photobin) - pad.set_blocked_async(False, self.blockedCb, None) - - self.picExposureOpen = False - pic = gtk.gdk.pixbuf_loader_new_with_mime_type("image/jpeg") - pic.write( buffer ) - pic.close() - pixBuf = pic.get_pixbuf() - del pic - - self.savePhoto( pixBuf ) - - - def savePhoto(self, pixbuf): - if self.photoMode == self.PHOTO_MODE_AUDIO: - self.audioPixbuf = pixbuf - else: - self.ca.m.savePhoto(pixbuf) - - - def startRecordingVideo(self): - self.record = True - self.audio = True - - # It would be nicer to connect the video/audio-recording elements - # without stopping the pipeline. However, that seems to cause a - # very long delay at the start of the video recording where the first - # frame is 'frozen' for several seconds. MikeS from #gstreamer - # suggested that the videorate element might not be receiving a - # "new segment" signal soon enough. - # - # Stopping the pipeline while we reshuffle neatly works around this - # with minimal user experience impact. - self.pipeline.set_state(gst.STATE_NULL) - self.pipeline.add(self.videobin) - self.pipeline.get_by_name("tee").link(self.videobin) - self.pipeline.add(self.audiobin) - self.pipeline.set_state(gst.STATE_PLAYING) - - def startRecordingAudio(self): - self.audioPixbuf = None - - self.photoMode = self.PHOTO_MODE_AUDIO - self._takePhoto() - - self.record = True - self.pipeline.add(self.audiobin) - self.audiobin.set_state(gst.STATE_PLAYING) - - def stopRecordingVideo(self): - # Similarly to as when we start recording, we also stop the pipeline - # while we are adjusting the pipeline to stop recording. If we do - # it on-the-fly, the following video live feed to the screen becomes - # several seconds delayed. Weird! - self._eos_cb = self.stopRecordingVideoEOS - self.pipeline.get_by_name('camsrc').send_event(gst.event_new_eos()) - self.audiobin.get_by_name('absrc').send_event(gst.event_new_eos()) - - def stopRecordingVideoEOS(self): - self.pipeline.set_state(gst.STATE_NULL) - self.pipeline.get_by_name("tee").unlink(self.videobin) - self.pipeline.remove(self.videobin) - self.pipeline.remove(self.audiobin) - self.pipeline.set_state(gst.STATE_PLAYING) - gobject.idle_add( self.stoppedRecordingVideo ) - - - def copyThumbPic(self, fsink, buffer, pad, user_data=None): - if (self.thumbExposureOpen): - self.thumbExposureOpen = False - pic = gtk.gdk.pixbuf_loader_new_with_mime_type("image/jpeg") - pic.write(buffer) - pic.close() - self.thumbBuf = pic.get_pixbuf() - del pic - self.thumbEl('thumbTee').unlink(self.thumbEl('thumbQueue')) - - oggFilepath = os.path.join(Instance.instancePath, "output.ogg") #ogv - if (self.audio): - self.ca.ui.setPostProcessPixBuf(self.thumbBuf) - - wavFilepath = os.path.join(Instance.instancePath, "output.wav") - muxFilepath = os.path.join(Instance.instancePath, "mux.ogg") #ogv - - muxline = gst.parse_launch('filesrc location=' + str(oggFilepath) + ' name=muxVideoFilesrc ! oggdemux name=muxOggdemux ! theoradec name=muxTheoradec ! theoraenc name=muxTheoraenc ! oggmux name=muxOggmux ! filesink location=' + str(muxFilepath) + ' name=muxFilesink filesrc location=' + str(wavFilepath) + ' name=muxAudioFilesrc ! wavparse name=muxWavparse ! audioconvert name=muxAudioconvert ! vorbisenc name=muxVorbisenc ! muxOggmux.') - taglist = self.getTags(Constants.TYPE_VIDEO) - vorbisEnc = muxline.get_by_name('muxVorbisenc') - vorbisEnc.merge_tags(taglist, gst.TAG_MERGE_REPLACE_ALL) - - muxBus = muxline.get_bus() - muxBus.add_signal_watch() - self.VIDEO_TRANSCODE_ID = muxBus.connect('message', self._onMuxedVideoMessageCb, muxline) - self.muxPipes.append(muxline) - #add a listener here to monitor % of transcoding... - self.TRANSCODE_ID = gobject.timeout_add(self.TRANSCODE_UPDATE_INTERVAL, self._transcodeUpdateCb, muxline) - muxline.set_state(gst.STATE_PLAYING) - else: - self.record = False - self.audio = False - self.ca.m.saveVideo(self.thumbBuf, str(oggFilepath), self.VIDEO_WIDTH_SMALL, self.VIDEO_HEIGHT_SMALL) - self.ca.m.stoppedRecordingVideo() - - - def _transcodeUpdateCb( self, pipe ): - position, duration = self.queryPosition( pipe ) - if position != gst.CLOCK_TIME_NONE: - value = position * 100.0 / duration - value = value/100.0 - self.ca.ui.progressWindow.updateProgress(value, Constants.istrSaving) - return True - - - def queryPosition( self, pipe ): - try: - position, format = pipe.query_position(gst.FORMAT_TIME) - except: - position = gst.CLOCK_TIME_NONE - - try: - duration, format = pipe.query_duration(gst.FORMAT_TIME) - except: - duration = gst.CLOCK_TIME_NONE - - return (position, duration) - - - def _onMuxedVideoMessageCb(self, bus, message, pipe): - t = message.type - if (t == gst.MESSAGE_EOS): - self.record = False - self.audio = False - gobject.source_remove(self.VIDEO_TRANSCODE_ID) - self.VIDEO_TRANSCODE_ID = 0 - gobject.source_remove(self.TRANSCODE_ID) - self.TRANSCODE_ID = 0 - pipe.set_state(gst.STATE_NULL) - pipe.get_bus().remove_signal_watch() - pipe.get_bus().disable_sync_message_emission() - - wavFilepath = os.path.join(Instance.instancePath, "output.wav") - oggFilepath = os.path.join(Instance.instancePath, "output.ogg") #ogv - muxFilepath = os.path.join(Instance.instancePath, "mux.ogg") #ogv - os.remove( wavFilepath ) - os.remove( oggFilepath ) - self.ca.m.saveVideo(self.thumbBuf, str(muxFilepath), self.VIDEO_WIDTH_SMALL, self.VIDEO_HEIGHT_SMALL) - self.ca.m.stoppedRecordingVideo() - return False - else: - return True - - - def _onMuxedAudioMessageCb(self, bus, message, pipe): - t = message.type - if (t == gst.MESSAGE_EOS): - record.Record.log.debug("audio gst.MESSAGE_EOS") - self.record = False - self.audio = False - gobject.source_remove(self.AUDIO_TRANSCODE_ID) - self.AUDIO_TRANSCODE_ID = 0 - gobject.source_remove(self.TRANSCODE_ID) - self.TRANSCODE_ID = 0 - pipe.set_state(gst.STATE_NULL) - pipe.get_bus().remove_signal_watch() - pipe.get_bus().disable_sync_message_emission() - - wavFilepath = os.path.join(Instance.instancePath, "output.wav") - oggFilepath = os.path.join(Instance.instancePath, "output.ogg") - os.remove( wavFilepath ) - self.ca.m.saveAudio(oggFilepath, self.audioPixbuf) - return False - else: - return True - - - def _onSyncMessageCb(self, bus, message): - if message.structure is None: - return - if message.structure.get_name() == 'prepare-xwindow-id': - self.window.set_sink(message.src) - message.src.set_property('force-aspect-ratio', True) - - - def _onMessageCb(self, bus, message): - t = message.type - if t == gst.MESSAGE_EOS: - if self._eos_cb: - cb = self._eos_cb - self._eos_cb = None - cb() - elif t == gst.MESSAGE_ERROR: - #todo: if we come out of suspend/resume with errors, then get us back up and running... - #todo: handle "No space left on the resource.gstfilesink.c" - #err, debug = message.parse_error() - pass - - def abandonMedia(self): - self.stop() - - if (self.AUDIO_TRANSCODE_ID != 0): - gobject.source_remove(self.AUDIO_TRANSCODE_ID) - self.AUDIO_TRANSCODE_ID = 0 - if (self.TRANSCODE_ID != 0): - gobject.source_remove(self.TRANSCODE_ID) - self.TRANSCODE_ID = 0 - if (self.VIDEO_TRANSCODE_ID != 0): - gobject.source_remove(self.VIDEO_TRANSCODE_ID) - self.VIDEO_TRANSCODE_ID = 0 - - wavFilepath = os.path.join(Instance.instancePath, "output.wav") - if (os.path.exists(wavFilepath)): - os.remove(wavFilepath) - oggFilepath = os.path.join(Instance.instancePath, "output.ogg") #ogv - if (os.path.exists(oggFilepath)): - os.remove(oggFilepath) - muxFilepath = os.path.join(Instance.instancePath, "mux.ogg") #ogv - if (os.path.exists(muxFilepath)): - os.remove(muxFilepath) +OGG_TRAITS = { + 0: { 'width': 160, 'height': 120, 'quality': 16 }, + 1: { 'width': 400, 'height': 300, 'quality': 16 }, + 2: { 'width': 640, 'height': 480, 'quality': 32 } } +class Glive: + def play(self): + logger.debug('play') + + if not self.play_pipe: + self.src_str = \ + 'v4l2src ' \ + '! video/x-raw-yuv,width=%s,height=%s ' \ + % (PLAYBACK_WIDTH, PLAYBACK_HEIGHT) + self.play_str = \ + 'xvimagesink force-aspect-ratio=true name=xsink' + + self.play_pipe = gst.parse_launch( + '%s ' \ + '! valve name=valve ' \ + '! queue name=queue ' \ + '! %s' \ + % (self.src_str, self.play_str)) + self.valve = self.play_pipe.get_by_name('valve') + + def message_cb(bus, message): + if message.type == gst.MESSAGE_ERROR: + err, debug = message.parse_error() + logger.error('play_pipe: %s %s' % (err, debug)) + + if not self.fallback: + logger.warning('use fallback_bin') + self.fallback = True + + self.play_str = \ + 'ffmpegcolorspace ' \ + '! ximagesink force-aspect-ratio=true ' \ + ' name=xsink' + + self.play_pipe.remove( + self.play_pipe.get_by_name('xsink')) + + c = gst.element_factory_make('ffmpegcolorspace') + s = gst.element_factory_make('ximagesink', 'xsink') + s.props.force_aspect_ratio = True + + self.play_pipe.add(c, s) + gst.element_link_many( + self.play_pipe.get_by_name('queue'), c, s) + + if [i for i in self.pipeline.get_state() \ + if id(i) == id(gst.STATE_PLAYING)]: + self.pipeline = None + self._switch_pipe(self.play_pipe) + + bus = self.play_pipe.get_bus() + bus.add_signal_watch() + bus.connect('message', message_cb) + + self._switch_pipe(self.play_pipe) + + def thumb_play(self, use_fallback=False): + if not self.fallback and not use_fallback: + # use xv to scale video + self.play() + return + + logger.debug('thumb_play') + + if not self.fallback_pipe: + self.fallback_pipe = gst.parse_launch( + '%s ' \ + '! queue ' \ + '! videoscale ' \ + '! video/x-raw-yuv,width=%s,height=%s ' \ + '! ffmpegcolorspace ' \ + '! ximagesink force-aspect-ratio=true name=xsink' \ + % (self.src_str, ui.UI.dim_PIPW, ui.UI.dim_PIPH)) + + def message_cb(bus, message): + if message.type == gst.MESSAGE_ERROR: + err, debug = message.parse_error() + logger.error('fallback_pipe: %s %s' % (err, debug)) + + bus = self.fallback_pipe.get_bus() + bus.add_signal_watch() + bus.connect('message', message_cb) + + self._switch_pipe(self.fallback_pipe) + + def pause(self): + logger.debug('pause') + if self.pipeline: + self.pipeline.set_state(gst.STATE_PAUSED) + + def stop(self): + logger.debug('stop') + if self.pipeline: + self.pipeline.set_state(gst.STATE_NULL) + + def takePhoto(self, after_photo_cb=None): + logger.debug('takePhoto') + + if not self.photo: + def sink_handoff(sink, buffer, pad, self): + sink.props.signal_handoffs = False + + pixbuf = gtk.gdk.pixbuf_loader_new_with_mime_type('image/jpeg') + pixbuf.write(buffer) + pixbuf.close() + + structure = gst.Structure('record.photo') + structure['pixbuf'] = pixbuf.get_pixbuf() + msg = gst.message_new_custom(gst.MESSAGE_APPLICATION, sink, + structure) + self.play_pipe.get_bus().post(msg) + + self.photo = gst.element_factory_make('ffmpegcolorspace') + self.photo_jpegenc = gst.element_factory_make('jpegenc') + self.photo_sink = gst.element_factory_make('fakesink') + self.photo_sink.connect('handoff', sink_handoff, self) + + def message_cb(bus, message, self): + if message.type == gst.MESSAGE_APPLICATION \ + and message.structure.get_name() == 'record.photo': + self.valve.props.drop = True + self.play_pipe.remove(self.photo) + self.play_pipe.remove(self.photo_jpegenc) + self.play_pipe.remove(self.photo_sink) + self.valve.link(self.play_pipe.get_by_name('queue')) + self.valve.props.drop = False + self.after_photo_cb(self, message.structure['pixbuf']) + + bus = self.play_pipe.get_bus() + bus.add_signal_watch() + bus.connect('message', message_cb, self) + + def process_cb(self, pixbuf): + self.ca.m.savePhoto(pixbuf) + self._switch_pipe(self.play_pipe) + + self.after_photo_cb = after_photo_cb and after_photo_cb or process_cb + + self.valve.props.drop = True + self.valve.unlink(self.play_pipe.get_by_name('queue')) + self.play_pipe.add(self.photo, self.photo_jpegenc, self.photo_sink) + gst.element_link_many(self.valve, self.photo, self.photo_jpegenc, + self.photo_sink) + self.photo_sink.props.signal_handoffs = True + self.valve.props.drop = False + + self._switch_pipe(self.play_pipe) + + def startRecordingVideo(self, quality): + logger.debug('startRecordingVideo quality=%s' % quality) + + if not self.video_pipe or quality != self.ogg_quality: + self.video_pipe = gst.parse_launch( \ + '%s ' \ + '! tee name=tee ' \ + 'tee.! queue ! %s ' \ + 'tee.! queue ' \ + '! ffmpegcolorspace ' \ + '! videorate skip_to_first=true ' \ + '! video/x-raw-yuv,framerate=10/1 ' \ + '! videoscale ' \ + '! video/x-raw-yuv,width=%s,height=%s ' \ + '! theoraenc quality=%s ' \ + '! oggmux ' \ + '! filesink location=%s ' \ + 'alsasrc ' \ + '! queue ' \ + '! audio/x-raw-int,rate=16000,channels=1,depth=16 ' \ + '! wavenc ' \ + '! filesink location=%s ' \ + % (self.src_str, self.play_str, + OGG_TRAITS[quality]['width'], + OGG_TRAITS[quality]['height'], + OGG_TRAITS[quality]['quality'], + TMP_OGV, TMP_WAV)) + + def message_cb(bus, message, self): + if message.type == gst.MESSAGE_ERROR: + err, debug = message.parse_error() + logger.error('video_pipe: %s %s' % (err, debug)) + + bus = self.video_pipe.get_bus() + bus.add_signal_watch() + bus.connect('message', message_cb, self) + + def process_cb(self, pixbuf): + self.pixbuf = pixbuf + self._switch_pipe(self.video_pipe) + + self.ogg_quality = quality + # take photo first + self.takePhoto(process_cb) + + def stopRecordingVideo(self): + logger.debug('stopRecordingVideo') + + self._switch_pipe(self.play_pipe) + + if not os.path.exists(TMP_OGV) \ + or not os.path.exists(TMP_WAV): + self.ca.m.cannotSaveVideo() + self.ca.m.stoppedRecordingVideo() + return + + if os.path.getsize(TMP_OGV) <= 0 \ + or os.path.getsize(TMP_WAV) <= 0: + self.ca.m.cannotSaveVideo() + self.ca.m.stoppedRecordingVideo() + return + + if self.mux_pipe: + self.mux_pipe.set_state(gst.STATE_NULL) + del self.mux_pipe + + self.mux_pipe = gst.parse_launch( \ + 'filesrc location=%s ' \ + '! oggdemux ' \ + '! theoraparse ' \ + '! oggmux name=mux ' \ + '! filesink location=%s ' \ + 'filesrc location=%s ' \ + '! wavparse ' \ + '! audioconvert ' \ + '! vorbisenc name=vorbisenc ' \ + '! mux.' \ + % (TMP_OGV, TMP_OGG, TMP_WAV)) + + taglist = self.getTags(Constants.TYPE_VIDEO) + vorbisenc = self.mux_pipe.get_by_name('vorbisenc') + vorbisenc.merge_tags(taglist, gst.TAG_MERGE_REPLACE_ALL) + + def done(bus, message, self): + if message.type == gst.MESSAGE_ERROR: + err, debug = message.parse_error() + logger.error('audio_pipe: %s %s' % (err, debug)) + return + elif message.type != gst.MESSAGE_EOS: + return + + logger.debug('stopRecordingVideo.done') + self.mux_pipe.set_state(gst.STATE_NULL) + + os.remove(TMP_OGV) + os.remove(TMP_WAV) + + ogg_w = OGG_TRAITS[self.ogg_quality]['width'] + ogg_h = OGG_TRAITS[self.ogg_quality]['height'] + + thumb = self.pixbuf.scale_simple(ogg_w, ogg_h, + gtk.gdk.INTERP_HYPER) + + self.ca.ui.setPostProcessPixBuf(thumb) + self.ca.m.saveVideo(thumb, TMP_OGG, ogg_w, ogg_h) + self.ca.m.stoppedRecordingVideo() + self.ca.ui.updateVideoComponents() + + bus = self.mux_pipe.get_bus() + bus.add_signal_watch() + bus.connect('message', done, self) + + self.mux_pipe.set_state(gst.STATE_PLAYING) + + def startRecordingAudio(self): + logger.debug('startRecordingAudio') + + # XXX re-create pipe every time + # to supress gst glitches during the second invoking + if True: + self.audio_pipe = gst.parse_launch( \ + '%s ' \ + '! queue ' \ + '! %s ' \ + 'alsasrc ' \ + '! queue ' \ + '! audioconvert ' \ + '! vorbisenc name=vorbisenc ' \ + '! oggmux ' \ + '! filesink location=%s ' \ + % (self.src_str, self.play_str, TMP_OGG)) + + def message_cb(bus, message, self): + if message.type == gst.MESSAGE_ERROR: + err, debug = message.parse_error() + logger.error('audio_pipe: %s %s' % (err, debug)) + + bus = self.audio_pipe.get_bus() + bus.add_signal_watch() + bus.connect('message', message_cb, self) + + def process_cb(self, pixbuf): + taglist = self.getTags(Constants.TYPE_AUDIO) + cover = utils.getStringFromPixbuf(pixbuf) + taglist[gst.TAG_EXTENDED_COMMENT] = 'coverart=%s' % cover + + vorbisenc = self.audio_pipe.get_by_name('vorbisenc') + vorbisenc.merge_tags(taglist, gst.TAG_MERGE_REPLACE_ALL) + + self.pixbuf = pixbuf + self._switch_pipe(self.audio_pipe) + + # take photo first + self.takePhoto(process_cb) + + def stopRecordingAudio( self ): + logger.debug('stopRecordingAudio') + + self._switch_pipe(self.play_pipe) + + if (not os.path.exists(TMP_OGG)): + self.ca.m.cannotSaveVideo() + return + if (os.path.getsize(TMP_OGG) <= 0): + self.ca.m.cannotSaveVideo() + return + + self.ca.ui.setPostProcessPixBuf(self.pixbuf) + self.ca.m.saveAudio(TMP_OGG, self.pixbuf) + + def abandonMedia(self): + logger.debug('abandonMedia') + self.stop() + + if (self.AUDIO_TRANSCODE_ID != 0): + gobject.source_remove(self.AUDIO_TRANSCODE_ID) + self.AUDIO_TRANSCODE_ID = 0 + if (self.TRANSCODE_ID != 0): + gobject.source_remove(self.TRANSCODE_ID) + self.TRANSCODE_ID = 0 + if (self.VIDEO_TRANSCODE_ID != 0): + gobject.source_remove(self.VIDEO_TRANSCODE_ID) + self.VIDEO_TRANSCODE_ID = 0 + + if (os.path.exists(TMP_OGG)): + os.remove(TMP_OGG) + + def __init__(self, pca): + self.window = None + self.ca = pca + + self.pipeline = None + self.play_pipe = None + self.fallback_pipe = None + self.photo = None + self.video_pipe = None + self.mux_pipe = None + self.audio_pipe = None + + self.fallback = False + + # XXX since sugar doesn't control capture volumes (see #800) + # we have to do it by ourselves + alsasrc = gst.element_factory_make('alsasrc') + alsasrc.set_state(gst.STATE_PAUSED) + for i in alsasrc.list_tracks(): + if i.props.flags & gst.interfaces.MIXER_TRACK_INPUT \ + and re.search('capture', i.label, flags=re.IGNORECASE): + alsasrc.set_record(i, True) + volume = i.props.min_volume \ + + int((i.props.max_volume - i.props.min_volume) \ + / 100. * 90.) + alsasrc.set_volume(i, tuple([volume] * i.props.num_channels)) + logger.debug('Set volume %s to %s' % (volume, i.label)) + alsasrc.set_state(gst.STATE_NULL) + del alsasrc + + def _switch_pipe(self, new_pipe): + if self.pipeline != new_pipe: + if self.pipeline: + self.pipeline.set_state(gst.STATE_NULL) + self.pipeline = new_pipe + + if self.pipeline: + xsink = new_pipe.get_by_name('xsink') + if xsink: + xsink.set_xwindow_id(self.window.window.xid) + self.pipeline.set_state(gst.STATE_PLAYING) + + def getTags( self, type ): + tl = gst.TagList() + tl[gst.TAG_ARTIST] = str(Instance.nickName) + tl[gst.TAG_COMMENT] = "sugar" + #this is unfortunately, unreliable + #record.Record.log.debug("self.ca.metadata['title']->" + str(self.ca.metadata['title']) ) + tl[gst.TAG_ALBUM] = "sugar" #self.ca.metadata['title'] + tl[gst.TAG_DATE] = utils.getDateString(int(time.time())) + stringType = Constants.mediaTypes[type][Constants.keyIstr] + tl[gst.TAG_TITLE] = Constants.istrBy % {"1":stringType, "2":str(Instance.nickName)} + return tl class LiveVideoWindow(gtk.Window): - def __init__(self, bgd ): - gtk.Window.__init__(self) - - self.imagesink = None - self.glive = None - - self.modify_bg( gtk.STATE_NORMAL, bgd ) - self.modify_bg( gtk.STATE_INSENSITIVE, bgd ) - self.unset_flags(gtk.DOUBLE_BUFFERED) - self.set_flags(gtk.APP_PAINTABLE) + def __init__(self, bgd ): + gtk.Window.__init__(self) - def set_glive(self, pglive): - self.glive = pglive - self.glive.window = self + self.glive = None - def set_sink(self, sink): - if (self.imagesink != None): - assert self.window.xid - self.imagesink = None - del self.imagesink + self.modify_bg( gtk.STATE_NORMAL, bgd ) + self.modify_bg( gtk.STATE_INSENSITIVE, bgd ) + self.unset_flags(gtk.DOUBLE_BUFFERED) + self.set_flags(gtk.APP_PAINTABLE) - self.imagesink = sink - self.imagesink.set_xwindow_id(self.window.xid) + def set_glive(self, pglive): + self.glive = pglive + self.glive.window = self diff --git a/glivex.py b/glivex.py deleted file mode 100644 index 5b8267f..0000000 --- a/glivex.py +++ /dev/null @@ -1,147 +0,0 @@ -#Copyright (c) 2008, Media Modifications Ltd. - -#Permission is hereby granted, free of charge, to any person obtaining a copy -#of this software and associated documentation files (the "Software"), to deal -#in the Software without restriction, including without limitation the rights -#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -#copies of the Software, and to permit persons to whom the Software is -#furnished to do so, subject to the following conditions: - -#The above copyright notice and this permission notice shall be included in -#all copies or substantial portions of the Software. - -#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -#THE SOFTWARE. - -# This class is a cut-down version of glive which uses an ximagesink -# rather than an xvimagesink. This is used in video playback mode, where -# our only Xv port is used for Theora playback. -# -# I tried to modify the glive pipeline to allow swapping an xvimagesink for -# an ximagesink and vice-versa, but that didn't work out (all kinds of strange -# behaviour, perhaps a gstreamer bug). So we resort to using a separate -# pipeline - ugly, but it works... - -import os -import gtk -import pygtk -pygtk.require('2.0') -import sys -import gst -import gst.interfaces -import pygst -pygst.require('0.10') -import time -import threading -import gobject -gobject.threads_init() - -from instance import Instance -from constants import Constants -import record -import utils -import ui - -class GliveX: - def __init__(self, pca): - self.window = None - self.ca = pca - - self.playing = False - - self.pipeline = gst.Pipeline("slow-pipeline") - self.createPipeline() - - bus = self.pipeline.get_bus() - bus.enable_sync_message_emission() - bus.add_signal_watch() - self.SYNC_ID = bus.connect('sync-message::element', self._onSyncMessageCb) - self.MESSAGE_ID = bus.connect('message', self._onMessageCb) - - def createPipeline ( self ): - src = gst.element_factory_make("v4l2src", "camsrc") - try: - # old gst-plugins-good does not have this property - src.set_property("queue-size", 2) - except: - pass - - queue = gst.element_factory_make("queue", "dispqueue") - queue.set_property("leaky", True) - queue.set_property('max-size-buffers', 1) - - scale = gst.element_factory_make("videoscale", "scale") - scalecaps = gst.Caps('video/x-raw-yuv,width='+str(ui.UI.dim_PIPW)+',height='+str(ui.UI.dim_PIPH)) - colorspace = gst.element_factory_make("ffmpegcolorspace", "colorspace") - xsink = gst.element_factory_make("ximagesink", "xsink") - self.pipeline.add(src, queue, scale, colorspace, xsink) - gst.element_link_many(src, queue, scale) - scale.link(colorspace, scalecaps) - colorspace.link(xsink) - - def play(self): - self.pipeline.set_state(gst.STATE_PLAYING) - self.playing = True - - def pause(self): - self.pipeline.set_state(gst.STATE_PAUSED) - self.playing = False - - def stop(self): - self.pipeline.set_state(gst.STATE_NULL) - self.playing = False - - def is_playing(self): - return self.playing - - def idlePlayElement(self, element): - element.set_state(gst.STATE_PLAYING) - return False - - def _onSyncMessageCb(self, bus, message): - if message.structure is None: - return - if message.structure.get_name() == 'prepare-xwindow-id': - self.window.set_sink(message.src) - message.src.set_property('force-aspect-ratio', True) - - def _onMessageCb(self, bus, message): - t = message.type - if t == gst.MESSAGE_EOS: - #print("MESSAGE_EOS") - pass - elif t == gst.MESSAGE_ERROR: - #todo: if we come out of suspend/resume with errors, then get us back up and running... - #todo: handle "No space left on the resource.gstfilesink.c" - #err, debug = message.parse_error() - pass - -class SlowLiveVideoWindow(gtk.Window): - def __init__(self, bgd ): - gtk.Window.__init__(self) - - self.imagesink = None - self.glivex = None - - self.modify_bg( gtk.STATE_NORMAL, bgd ) - self.modify_bg( gtk.STATE_INSENSITIVE, bgd ) - self.unset_flags(gtk.DOUBLE_BUFFERED) - self.set_flags(gtk.APP_PAINTABLE) - - def set_glivex(self, pglivex): - self.glivex = pglivex - self.glivex.window = self - - def set_sink(self, sink): - if (self.imagesink != None): - assert self.window.xid - self.imagesink = None - del self.imagesink - - self.imagesink = sink - self.imagesink.set_xwindow_id(self.window.xid) @@ -32,123 +32,103 @@ import gobject import time gobject.threads_init() +import logging +logger = logging.getLogger('record:gplay.py') + import record class Gplay: - def __init__(self): - self.window = None - self.players = [] - self.playing = False - self.nextMovie() - - def nextMovie(self): - if ( len(self.players) > 0 ): - self.playing = False - self.getPlayer().set_property("video-sink", None) - self.getPlayer().get_bus().disconnect(self.SYNC_ID) - self.getPlayer().get_bus().remove_signal_watch() - self.getPlayer().get_bus().disable_sync_message_emission() - - player = gst.element_factory_make("playbin", "playbin") - xis = gst.element_factory_make("xvimagesink", "xvimagesink") - player.set_property("video-sink", xis) - bus = player.get_bus() - bus.enable_sync_message_emission() - bus.add_signal_watch() - self.SYNC_ID = bus.connect('sync-message::element', self.onSyncMessage) - self.players.append(player) - + def __init__(self, ca): + self.ca = ca + self.window = None + self.players = [] + self.playing = False - def getPlayer(self): - return self.players[len(self.players)-1] + self.player = gst.element_factory_make('playbin') + bus = self.player.get_bus() + bus.enable_sync_message_emission() + bus.add_signal_watch() + bus.connect('message', self._onMessageCb) - def onSyncMessage(self, bus, message): - if message.structure is None: - return True - if message.structure.get_name() == 'prepare-xwindow-id': - self.window.set_sink(message.src) - message.src.set_property('force-aspect-ratio', True) - return True + def _onMessageCb(self, bus, message): + if message.type == gst.MESSAGE_ERROR: + err, debug = message.parse_error() + logger.error('_onMessageCb: error=%s debug=%s' % (err, debug)) + def setLocation(self, location): + if (self.player.get_property('uri') == location): + self.seek(gst.SECOND*0) + return - def setLocation(self, location): - if (self.getPlayer().get_property('uri') == location): - self.seek(gst.SECOND*0) - return + self.player.set_state(gst.STATE_READY) + self.player.set_property('uri', location) + ext = location[len(location)-3:] + record.Record.log.debug("setLocation: ext->"+str(ext)) + if (ext == "jpg"): + self.pause() - self.getPlayer().set_state(gst.STATE_READY) - self.getPlayer().set_property('uri', location) - ext = location[len(location)-3:] - record.Record.log.debug("setLocation: ext->"+str(ext)) - if (ext == "jpg"): - self.pause() + def queryPosition(self): + "Returns a (position, duration) tuple" + try: + position, format = self.player.query_position(gst.FORMAT_TIME) + except: + position = gst.CLOCK_TIME_NONE - def queryPosition(self): - "Returns a (position, duration) tuple" - try: - position, format = self.getPlayer().query_position(gst.FORMAT_TIME) - except: - position = gst.CLOCK_TIME_NONE + try: + duration, format = self.player.query_duration(gst.FORMAT_TIME) + except: + duration = gst.CLOCK_TIME_NONE - try: - duration, format = self.getPlayer().query_duration(gst.FORMAT_TIME) - except: - duration = gst.CLOCK_TIME_NONE + return (position, duration) - return (position, duration) + def seek(self, location): + event = gst.event_new_seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_TYPE_SET, location, gst.SEEK_TYPE_NONE, 0) + res = self.player.send_event(event) + if res: + self.player.set_new_stream_time(0L) - def seek(self, location): - event = gst.event_new_seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_TYPE_SET, location, gst.SEEK_TYPE_NONE, 0) - res = self.getPlayer().send_event(event) - if res: - self.getPlayer().set_new_stream_time(0L) + def pause(self): + self.playing = False + self.player.set_state(gst.STATE_PAUSED) - def pause(self): - self.playing = False - self.getPlayer().set_state(gst.STATE_PAUSED) + def play(self): + if not self.player.props.video_sink: + if self.ca.glive.fallback: + sink = gst.element_factory_make('ximagesink') + else: + sink = gst.element_factory_make('xvimagesink') + sink.props.force_aspect_ratio = True + self.player.props.video_sink = sink - def play(self): - self.playing = True - self.getPlayer().set_state(gst.STATE_PLAYING) + self.player.props.video_sink.set_xwindow_id(self.window.window.xid) + self.playing = True + self.player.set_state(gst.STATE_PLAYING) - def stop(self): - self.playing = False - self.getPlayer().set_state(gst.STATE_NULL) - self.nextMovie() + def stop(self): + self.playing = False + self.player.set_state(gst.STATE_NULL) - def get_state(self, timeout=1): - return self.getPlayer().get_state(timeout=timeout) + def get_state(self, timeout=1): + return self.player.get_state(timeout=timeout) - def is_playing(self): - return self.playing + def is_playing(self): + return self.playing class PlayVideoWindow(gtk.Window): - def __init__(self, bgd): - gtk.Window.__init__(self) - - self.imagesink = None - - self.modify_bg( gtk.STATE_NORMAL, bgd ) - self.modify_bg( gtk.STATE_INSENSITIVE, bgd ) - self.unset_flags(gtk.DOUBLE_BUFFERED) - self.set_flags(gtk.APP_PAINTABLE) - - - def set_sink(self, sink): - if (self.imagesink != None): - assert self.window.xid - self.imagesink = None - del self.imagesink + def __init__(self, bgd): + gtk.Window.__init__(self) - self.imagesink = sink - self.imagesink.set_xwindow_id(self.window.xid)
\ No newline at end of file + self.modify_bg( gtk.STATE_NORMAL, bgd ) + self.modify_bg( gtk.STATE_INSENSITIVE, bgd ) + self.unset_flags(gtk.DOUBLE_BUFFERED) + self.set_flags(gtk.APP_PAINTABLE) @@ -30,51 +30,51 @@ import utils class Greplay(gobject.GObject): - __gsignals__ = { - 'coverart-found': - (gobject.SIGNAL_RUN_FIRST, None, [object]) - } + __gsignals__ = { + 'coverart-found': + (gobject.SIGNAL_RUN_FIRST, None, [object]) + } - def findAlbumArt( self, path ): - record.Record.log.debug("getAlbumArt") - if (path == None): - record.Record.log.debug("getAlbumArt: path==None") - self.emit('coverart-found', None) - return - if (not os.path.exists(path)): - record.Record.log.debug("getAlbumArt: path doesn't exist") - self.emit('coverart-found', None) - return + def findAlbumArt( self, path ): + record.Record.log.debug("getAlbumArt") + if (path == None): + record.Record.log.debug("getAlbumArt: path==None") + self.emit('coverart-found', None) + return + if (not os.path.exists(path)): + record.Record.log.debug("getAlbumArt: path doesn't exist") + self.emit('coverart-found', None) + return - self.pp = gst.parse_launch("filesrc location="+str(path)+" ! oggdemux ! vorbisdec ! fakesink") - self.pp.get_bus().add_signal_watch() - self.pp.get_bus().connect("message", self._onMessageCb) - self.pp.set_state(gst.STATE_PLAYING) + self.pp = gst.parse_launch("filesrc location="+str(path)+" ! oggdemux ! vorbisdec ! fakesink") + self.pp.get_bus().add_signal_watch() + self.pp.get_bus().connect("message", self._onMessageCb) + self.pp.set_state(gst.STATE_PLAYING) - def _onMessageCb(self, bus, message): - t = message.type - if t == gst.MESSAGE_EOS: - record.Record.log.debug("Greplay:MESSAGE_EOS") - self.emit('coverart-found', None) - self.pp.set_state(gst.STATE_NULL) - return False - elif t == gst.MESSAGE_ERROR: - record.Record.log.debug("Greplay:MESSAGE_ERROR") - self.emit('coverart-found', None) - self.pp.set_state(gst.STATE_NULL) - return False - elif t == gst.MESSAGE_TAG: - tags = message.parse_tag() - for tag in tags.keys(): - if (str(tag) == "extended-comment"): - record.Record.log.debug("Found the tag!") - #todo, check for tagname - base64imgString = str(tags[tag])[len("coverart="):] + def _onMessageCb(self, bus, message): + t = message.type + if t == gst.MESSAGE_EOS: + record.Record.log.debug("Greplay:MESSAGE_EOS") + self.emit('coverart-found', None) + self.pp.set_state(gst.STATE_NULL) + return False + elif t == gst.MESSAGE_ERROR: + record.Record.log.debug("Greplay:MESSAGE_ERROR") + self.emit('coverart-found', None) + self.pp.set_state(gst.STATE_NULL) + return False + elif t == gst.MESSAGE_TAG: + tags = message.parse_tag() + for tag in tags.keys(): + if (str(tag) == "extended-comment"): + record.Record.log.debug("Found the tag!") + #todo, check for tagname + base64imgString = str(tags[tag])[len("coverart="):] - pixbuf = utils.getPixbufFromString(base64imgString) - self.pp.set_state(gst.STATE_NULL) - self.emit('coverart-found', pixbuf) - return False - return True + pixbuf = utils.getPixbufFromString(base64imgString) + self.pp.set_state(gst.STATE_NULL) + self.emit('coverart-found', pixbuf) + return False + return True diff --git a/gst/AUTHORS b/gst/AUTHORS new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/gst/AUTHORS diff --git a/gst/COPYING b/gst/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/gst/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/gst/ChangeLog b/gst/ChangeLog new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/gst/ChangeLog diff --git a/gst/INSTALL b/gst/INSTALL new file mode 100644 index 0000000..8b82ade --- /dev/null +++ b/gst/INSTALL @@ -0,0 +1,291 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006, 2007, 2008 Free Software Foundation, Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 6. Often, you can also type `make uninstall' to remove the installed + files again. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `<wchar.h>' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *Note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/gst/Makefile.am b/gst/Makefile.am new file mode 100644 index 0000000..31e0a31 --- /dev/null +++ b/gst/Makefile.am @@ -0,0 +1,20 @@ +plugin_LTLIBRARIES = libgstvideorate.la libgstvalve.la + +libgstvideorate_la_SOURCES = gstvideorate.c gstvideorate.h +libgstvideorate_la_CFLAGS = $(GST_CFLAGS) $(GST_AUDIO_CFLAGS) +libgstvideorate_la_LIBADD = $(GST_LIBS) $(GST_AUDIO_LIBS) +libgstvideorate_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstvideorate_la_LIBTOOLFLAGS = --tag=disable-static + +libgstvalve_la_SOURCES = gstvalve.c gstvalve.h +libgstvalve_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(ERROR_CFLAGS) +libgstvalve_la_LIBADD = $(GST_LIBS_LIBS) +libgstvalve_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) +libgstvalve_la_LIBTOOLFLAGS = --tag=disable-static + +ARCH = $(shell arch | grep 64 >/dev/null && echo linux64 || echo linux32) + +dist_xo: all + rm -rf $(ARCH) + mkdir $(ARCH) + cp .libs/libgst*.so $(ARCH)/ diff --git a/gst/NEWS b/gst/NEWS new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/gst/NEWS diff --git a/gst/README b/gst/README new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/gst/README diff --git a/gst/autogen.sh b/gst/autogen.sh new file mode 100755 index 0000000..aa82ce7 --- /dev/null +++ b/gst/autogen.sh @@ -0,0 +1,92 @@ +#!/bin/sh +# you can either set the environment variables AUTOCONF and AUTOMAKE +# to the right versions, or leave them unset and get the RedHat 7.3 defaults + +NOCONFIGURE=1 +DIE=0 +package=gst-plugin +srcfile=src/main.c + +# autogen.sh helper functions (copied from GStreamer's common/ CVS module) +if test ! -f ./gst-autogen.sh; +then + echo There is something wrong with your source tree. + echo You are either missing ./gst-autogen.sh or not + echo running autogen.sh from the top-level source + echo directory. + exit 1 +fi +. ./gst-autogen.sh + +CONFIGURE_DEF_OPT='--enable-maintainer-mode --enable-debug' + +autogen_options $@ + +echo -n "+ check for build tools" +if test ! -z "$NOCHECK"; then echo " skipped"; else echo; fi +version_check "autoconf" "$AUTOCONF autoconf autoconf259 autoconf257 autoconf-2.54 autoconf-2.53 autoconf-2.52" \ + "ftp://ftp.gnu.org/pub/gnu/autoconf/" 2 52 || DIE=1 +version_check "automake" "$AUTOMAKE automake automake-1.9 automake19 automake-1.7 automake-1.6 automake-1.5" \ + "ftp://ftp.gnu.org/pub/gnu/automake/" 1 7 || DIE=1 +###version_check "autopoint" "autopoint" \ +### "ftp://ftp.gnu.org/pub/gnu/gettext/" 0 11 5 || DIE=1 +version_check "libtoolize" "$LIBTOOLIZE libtoolize glibtoolize" \ + "ftp://ftp.gnu.org/pub/gnu/libtool/" 1 5 0 || DIE=1 +version_check "pkg-config" "" \ + "http://www.freedesktop.org/software/pkgconfig" 0 8 0 || DIE=1 + +die_check $DIE + +autoconf_2_52d_check || DIE=1 +aclocal_check || DIE=1 +autoheader_check || DIE=1 + +die_check $DIE + +# if no arguments specified then this will be printed +if test -z "$*"; then + echo "+ checking for autogen.sh options" + echo " This autogen script will automatically run ./configure as:" + echo " ./configure $CONFIGURE_DEF_OPT" + echo " To pass any additional options, please specify them on the $0" + echo " command line." +fi + +tool_run "$aclocal" "-I m4/ $ACLOCAL_FLAGS" +tool_run "$libtoolize" "--copy --force" +tool_run "$autoheader" +tool_run "$autoconf" +tool_run "$automake" "-a -c" + +# if enable exists, add an -enable option for each of the lines in that file +if test -f enable; then + for a in `cat enable`; do + CONFIGURE_FILE_OPT="--enable-$a" + done +fi + +# if disable exists, add an -disable option for each of the lines in that file +if test -f disable; then + for a in `cat disable`; do + CONFIGURE_FILE_OPT="$CONFIGURE_FILE_OPT --disable-$a" + done +fi + +test -n "$NOCONFIGURE" && { + echo "+ skipping configure stage for package $package, as requested." + echo "+ autogen.sh done." + exit 0 +} + +echo "+ running configure ... " +test ! -z "$CONFIGURE_DEF_OPT" && echo " ./configure default flags: $CONFIGURE_DEF_OPT" +test ! -z "$CONFIGURE_EXT_OPT" && echo " ./configure external flags: $CONFIGURE_EXT_OPT" +test ! -z "$CONFIGURE_FILE_OPT" && echo " ./configure enable/disable flags: $CONFIGURE_FILE_OPT" +echo + +./configure $CONFIGURE_DEF_OPT $CONFIGURE_EXT_OPT $CONFIGURE_FILE_OPT || { + echo " configure failed" + exit 1 +} + +echo "Now type 'make' to compile $package." diff --git a/gst/configure.ac b/gst/configure.ac new file mode 100644 index 0000000..cd84f56 --- /dev/null +++ b/gst/configure.ac @@ -0,0 +1,136 @@ +AC_INIT([gst-plugins-espeak], [0.3]) + +dnl versions of gstreamer and plugins-base +GST_MAJORMINOR=0.10 +GST_REQUIRED=0.10.0 +GSTPB_REQUIRED=0.10.0 + +dnl fill in your package name and version here +dnl the fourth (nano) number should be 0 for a release, 1 for CVS, +dnl and 2... for a prerelease + +dnl when going to/from release please set the nano correctly ! +dnl releases only do Wall, cvs and prerelease does Werror too +AS_VERSION(gst-plugin, GST_PLUGIN_VERSION, 0, 10, 0, 1, + GST_PLUGIN_CVS="no", GST_PLUGIN_CVS="yes") + +dnl AM_MAINTAINER_MODE provides the option to enable maintainer mode +#AM_MAINTAINER_MODE + +AM_INIT_AUTOMAKE($PACKAGE, $VERSION) + +dnl make aclocal work in maintainer mode +AC_SUBST(ACLOCAL_AMFLAGS, "-I m4") + +AM_CONFIG_HEADER(config.h) + +dnl check for tools +AC_PROG_CC +AC_PROG_LIBTOOL + + +dnl decide on error flags +AS_COMPILER_FLAG(-Wall, GST_WALL="yes", GST_WALL="no") + +if test "x$GST_WALL" = "xyes"; then + GST_ERROR="$GST_ERROR -Wall" + + if test "x$GST_PLUGIN_CVS" = "xyes"; then + AS_COMPILER_FLAG(-Werror,GST_ERROR="$GST_ERROR -Werror",GST_ERROR="$GST_ERROR") + fi +fi + +dnl Check for pkgconfig first +AC_CHECK_PROG(HAVE_PKGCONFIG, pkg-config, yes, no) + +dnl Give error and exit if we don't have pkgconfig +if test "x$HAVE_PKGCONFIG" = "xno"; then + AC_MSG_ERROR(you need to have pkgconfig installed !) +fi + +dnl Now we're ready to ask for gstreamer libs and cflags +dnl And we can also ask for the right version of gstreamer + + +PKG_CHECK_MODULES(GST, \ + gstreamer-$GST_MAJORMINOR >= $GST_REQUIRED, + HAVE_GST=yes,HAVE_GST=no) + +dnl Give error and exit if we don't have gstreamer +if test "x$HAVE_GST" = "xno"; then + AC_MSG_ERROR(you need gstreamer development packages installed !) +fi + +dnl append GST_ERROR cflags to GST_CFLAGS +GST_CFLAGS="$GST_CFLAGS $GST_ERROR" + +dnl make GST_CFLAGS and GST_LIBS available +AC_SUBST(GST_CFLAGS) +AC_SUBST(GST_LIBS) + +dnl make GST_MAJORMINOR available in Makefile.am +AC_SUBST(GST_MAJORMINOR) + +dnl If we need them, we can also use the base class libraries +PKG_CHECK_MODULES(GST_BASE, gstreamer-base-$GST_MAJORMINOR >= $GST_REQUIRED, + HAVE_GST_BASE=yes, HAVE_GST_BASE=no) + +dnl Give a warning if we don't have gstreamer libs +dnl you can turn this into an error if you need them +if test "x$HAVE_GST_BASE" = "xno"; then + AC_MSG_NOTICE(no GStreamer base class libraries found (gstreamer-base-$GST_MAJORMINOR)) +fi + +dnl make _CFLAGS and _LIBS available +AC_SUBST(GST_BASE_CFLAGS) +AC_SUBST(GST_BASE_LIBS) + +dnl If we need them, we can also use the gstreamer-plugins-base libraries +PKG_CHECK_MODULES(GSTPB_BASE, + gstreamer-plugins-base-$GST_MAJORMINOR >= $GSTPB_REQUIRED, + HAVE_GSTPB_BASE=yes, HAVE_GSTPB_BASE=no) + +dnl Give a warning if we don't have gstreamer libs +dnl you can turn this into an error if you need them +if test "x$HAVE_GSTPB_BASE" = "xno"; then + AC_MSG_NOTICE(no GStreamer Plugins Base libraries found (gstreamer-plugins-base-$GST_MAJORMINOR)) +fi + +dnl make _CFLAGS and _LIBS available +AC_SUBST(GSTPB_BASE_CFLAGS) +AC_SUBST(GSTPB_BASE_LIBS) + +dnl If we need them, we can also use the gstreamer-controller libraries +PKG_CHECK_MODULES(GSTCTRL, + gstreamer-controller-$GST_MAJORMINOR >= $GSTPB_REQUIRED, + HAVE_GSTCTRL=yes, HAVE_GSTCTRL=no) + +dnl Give a warning if we don't have gstreamer-controller +dnl you can turn this into an error if you need them +if test "x$HAVE_GSTCTRL" = "xno"; then + AC_MSG_NOTICE(no GStreamer Controller libraries found (gstreamer-controller-$GST_MAJORMINOR)) +fi + +dnl make _CFLAGS and _LIBS available +AC_SUBST(GSTCTRL_CFLAGS) +AC_SUBST(GSTCTRL_LIBS) + +dnl set the plugindir where plugins should be installed +if test "x${prefix}" = "x$HOME"; then + plugindir="$HOME/.gstreamer-$GST_MAJORMINOR/plugins" +else + plugindir="\$(libdir)/gstreamer-$GST_MAJORMINOR" +fi +AC_SUBST(plugindir) + +dnl set proper LDFLAGS for plugins +GST_PLUGIN_LDFLAGS='-module -avoid-version -export-symbols-regex [_]*\(gst_\|Gst\|GST_\).*' +AC_SUBST(GST_PLUGIN_LDFLAGS) + +PKG_CHECK_MODULES(OGG, ogg) + +AC_DEFINE_UNQUOTED(GST_LICENSE, "LGPL", [ ]) +AC_DEFINE_UNQUOTED(GST_PACKAGE_NAME, "GStreamer Base Plug-ins CVS/prerelease", [ ]) +AC_DEFINE_UNQUOTED(GST_PACKAGE_ORIGIN, "Unknown package origin", [ ]) + +AC_OUTPUT(Makefile) diff --git a/gst/gst-autogen.sh b/gst/gst-autogen.sh new file mode 100644 index 0000000..7b31212 --- /dev/null +++ b/gst/gst-autogen.sh @@ -0,0 +1,308 @@ +# a silly hack that generates autoregen.sh but it's handy +# Remove the old autoregen.sh first to create a new file, +# as the current one may be being read by the shell executing +# this script. +if [ -f "autoregen.sh" ]; then + rm autoregen.sh +fi +echo "#!/bin/sh" > autoregen.sh +echo "./autogen.sh $@ \$@" >> autoregen.sh +chmod +x autoregen.sh + +# helper functions for autogen.sh + +debug () +# print out a debug message if DEBUG is a defined variable +{ + if test ! -z "$DEBUG" + then + echo "DEBUG: $1" + fi +} + +version_check () +# check the version of a package +# first argument : package name (executable) +# second argument : optional path where to look for it instead +# third argument : source download url +# rest of arguments : major, minor, micro version +# all consecutive ones : suggestions for binaries to use +# (if not specified in second argument) +{ + PACKAGE=$1 + PKG_PATH=$2 + URL=$3 + MAJOR=$4 + MINOR=$5 + MICRO=$6 + + # for backwards compatibility, we let PKG_PATH=PACKAGE when PKG_PATH null + if test -z "$PKG_PATH"; then PKG_PATH=$PACKAGE; fi + debug "major $MAJOR minor $MINOR micro $MICRO" + VERSION=$MAJOR + if test ! -z "$MINOR"; then VERSION=$VERSION.$MINOR; else MINOR=0; fi + if test ! -z "$MICRO"; then VERSION=$VERSION.$MICRO; else MICRO=0; fi + + debug "major $MAJOR minor $MINOR micro $MICRO" + + for SUGGESTION in $PKG_PATH; do + COMMAND="$SUGGESTION" + + # don't check if asked not to + test -z "$NOCHECK" && { + echo -n " checking for $COMMAND >= $VERSION ... " + } || { + # we set a var with the same name as the package, but stripped of + # unwanted chars + VAR=`echo $PACKAGE | sed 's/-//g'` + debug "setting $VAR" + eval $VAR="$COMMAND" + return 0 + } + + debug "checking version with $COMMAND" + ($COMMAND --version) < /dev/null > /dev/null 2>&1 || + { + echo "not found." + continue + } + # strip everything that's not a digit, then use cut to get the first field + pkg_version=`$COMMAND --version|head -n 1|sed 's/^.*)[^0-9]*//'|cut -d' ' -f1` + debug "pkg_version $pkg_version" + # remove any non-digit characters from the version numbers to permit numeric + # comparison + pkg_major=`echo $pkg_version | cut -d. -f1 | sed s/[a-zA-Z\-].*//g` + pkg_minor=`echo $pkg_version | cut -d. -f2 | sed s/[a-zA-Z\-].*//g` + pkg_micro=`echo $pkg_version | cut -d. -f3 | sed s/[a-zA-Z\-].*//g` + test -z "$pkg_major" && pkg_major=0 + test -z "$pkg_minor" && pkg_minor=0 + test -z "$pkg_micro" && pkg_micro=0 + debug "found major $pkg_major minor $pkg_minor micro $pkg_micro" + + #start checking the version + debug "version check" + + # reset check + WRONG= + + if [ ! "$pkg_major" -gt "$MAJOR" ]; then + debug "major: $pkg_major <= $MAJOR" + if [ "$pkg_major" -lt "$MAJOR" ]; then + debug "major: $pkg_major < $MAJOR" + WRONG=1 + elif [ ! "$pkg_minor" -gt "$MINOR" ]; then + debug "minor: $pkg_minor <= $MINOR" + if [ "$pkg_minor" -lt "$MINOR" ]; then + debug "minor: $pkg_minor < $MINOR" + WRONG=1 + elif [ "$pkg_micro" -lt "$MICRO" ]; then + debug "micro: $pkg_micro < $MICRO" + WRONG=1 + fi + fi + fi + + if test ! -z "$WRONG"; then + echo "found $pkg_version, not ok !" + continue + else + echo "found $pkg_version, ok." + # we set a var with the same name as the package, but stripped of + # unwanted chars + VAR=`echo $PACKAGE | sed 's/-//g'` + debug "setting $VAR" + eval $VAR="$COMMAND" + return 0 + fi + done + + echo "not found !" + echo "You must have $PACKAGE installed to compile $package." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at $URL" + return 1; +} + +aclocal_check () +{ + # normally aclocal is part of automake + # so we expect it to be in the same place as automake + # so if a different automake is supplied, we need to adapt as well + # so how's about replacing automake with aclocal in the set var, + # and saving that in $aclocal ? + # note, this will fail if the actual automake isn't called automake* + # or if part of the path before it contains it + if [ -z "$automake" ]; then + echo "Error: no automake variable set !" + return 1 + else + aclocal=`echo $automake | sed s/automake/aclocal/` + debug "aclocal: $aclocal" + if [ "$aclocal" != "aclocal" ]; + then + CONFIGURE_DEF_OPT="$CONFIGURE_DEF_OPT --with-aclocal=$aclocal" + fi + if [ ! -x `which $aclocal` ]; then + echo "Error: cannot execute $aclocal !" + return 1 + fi + fi +} + +autoheader_check () +{ + # same here - autoheader is part of autoconf + # use the same voodoo + if [ -z "$autoconf" ]; then + echo "Error: no autoconf variable set !" + return 1 + else + autoheader=`echo $autoconf | sed s/autoconf/autoheader/` + debug "autoheader: $autoheader" + if [ "$autoheader" != "autoheader" ]; + then + CONFIGURE_DEF_OPT="$CONFIGURE_DEF_OPT --with-autoheader=$autoheader" + fi + if [ ! -x `which $autoheader` ]; then + echo "Error: cannot execute $autoheader !" + return 1 + fi + fi + +} +autoconf_2_52d_check () +{ + # autoconf 2.52d has a weird issue involving a yes:no error + # so don't allow it's use + test -z "$NOCHECK" && { + ac_version=`$autoconf --version|head -n 1|sed 's/^[a-zA-Z\.\ ()]*//;s/ .*$//'` + if test "$ac_version" = "2.52d"; then + echo "autoconf 2.52d has an issue with our current build." + echo "We don't know who's to blame however. So until we do, get a" + echo "regular version. RPM's of a working version are on the gstreamer site." + exit 1 + fi + } + return 0 +} + +die_check () +{ + # call with $DIE + # if set to 1, we need to print something helpful then die + DIE=$1 + if test "x$DIE" = "x1"; + then + echo + echo "- Please get the right tools before proceeding." + echo "- Alternatively, if you're sure we're wrong, run with --nocheck." + exit 1 + fi +} + +autogen_options () +{ + if test "x$1" = "x"; then + return 0 + fi + + while test "x$1" != "x" ; do + optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + case "$1" in + --noconfigure) + NOCONFIGURE=defined + AUTOGEN_EXT_OPT="$AUTOGEN_EXT_OPT --noconfigure" + echo "+ configure run disabled" + shift + ;; + --nocheck) + AUTOGEN_EXT_OPT="$AUTOGEN_EXT_OPT --nocheck" + NOCHECK=defined + echo "+ autotools version check disabled" + shift + ;; + --debug) + DEBUG=defined + AUTOGEN_EXT_OPT="$AUTOGEN_EXT_OPT --debug" + echo "+ debug output enabled" + shift + ;; + --prefix=*) + CONFIGURE_EXT_OPT="$CONFIGURE_EXT_OPT --prefix=$optarg" + echo "+ passing --prefix=$optarg to configure" + shift + ;; + --prefix) + shift + echo "DEBUG: $1" + CONFIGURE_EXT_OPT="$CONFIGURE_EXT_OPT --prefix=$1" + echo "+ passing --prefix=$1 to configure" + shift + ;; + + -h|--help) + echo "autogen.sh (autogen options) -- (configure options)" + echo "autogen.sh help options: " + echo " --noconfigure don't run the configure script" + echo " --nocheck don't do version checks" + echo " --debug debug the autogen process" + echo " --prefix will be passed on to configure" + echo + echo " --with-autoconf PATH use autoconf in PATH" + echo " --with-automake PATH use automake in PATH" + echo + echo "to pass options to configure, put them as arguments after -- " + exit 1 + ;; + --with-automake=*) + AUTOMAKE=$optarg + echo "+ using alternate automake in $optarg" + CONFIGURE_DEF_OPT="$CONFIGURE_DEF_OPT --with-automake=$AUTOMAKE" + shift + ;; + --with-autoconf=*) + AUTOCONF=$optarg + echo "+ using alternate autoconf in $optarg" + CONFIGURE_DEF_OPT="$CONFIGURE_DEF_OPT --with-autoconf=$AUTOCONF" + shift + ;; + --disable*|--enable*|--with*) + echo "+ passing option $1 to configure" + CONFIGURE_EXT_OPT="$CONFIGURE_EXT_OPT $1" + shift + ;; + --) shift ; break ;; + *) echo "- ignoring unknown autogen.sh argument $1"; shift ;; + esac + done + + for arg do CONFIGURE_EXT_OPT="$CONFIGURE_EXT_OPT $arg"; done + if test ! -z "$CONFIGURE_EXT_OPT" + then + echo "+ options passed to configure: $CONFIGURE_EXT_OPT" + fi +} + +toplevel_check () +{ + srcfile=$1 + test -f $srcfile || { + echo "You must run this script in the top-level $package directory" + exit 1 + } +} + + +tool_run () +{ + tool=$1 + options=$2 + run_if_fail=$3 + echo "+ running $tool $options..." + $tool $options || { + echo + echo $tool failed + eval $run_if_fail + exit 1 + } +} diff --git a/gst/gstvalve.c b/gst/gstvalve.c new file mode 100644 index 0000000..702b3e5 --- /dev/null +++ b/gst/gstvalve.c @@ -0,0 +1,311 @@ +/* + * Farsight Voice+Video library + * + * Copyright 2007 Collabora Ltd, + * Copyright 2007 Nokia Corporation + * @author: Olivier Crete <olivier.crete@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ +/** + * SECTION:element-valve + * + * The valve is a simple element that drops buffers when the #GstValve::drop + * property is set to %TRUE and lets then through otherwise. + * + * Any downstream error received while the #GstValve::drop property is %FALSE + * is ignored. So downstream element can be set to %GST_STATE_NULL and removed, + * without using pad blocking. + * + * Last reviewed on 2008-02-10 (0.10.11) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstvalve.h" + +#include <string.h> + +GST_DEBUG_CATEGORY (valve_debug); +#define GST_CAT_DEFAULT (valve_debug) + +/* elementfactory information */ +static const GstElementDetails gst_valve_details = +GST_ELEMENT_DETAILS ("Valve element", + "Filter", + "This element drops all packets when drop is TRUE", + "Olivier Crete <olivier.crete@collabora.co.uk>"); + + +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +/* Valve signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + ARG_0, + ARG_DROP, +}; + + + + +static void gst_valve_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_valve_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static gboolean gst_valve_event (GstPad * pad, GstEvent * event); +static GstFlowReturn gst_valve_buffer_alloc (GstPad * pad, guint64 offset, + guint size, GstCaps * caps, GstBuffer ** buf); +static GstFlowReturn gst_valve_chain (GstPad * pad, GstBuffer * buffer); +static GstCaps *gst_valve_getcaps (GstPad * pad); + +static void +_do_init (GType type) +{ + GST_DEBUG_CATEGORY_INIT (valve_debug, "valve", 0, "Valve"); +} + +GST_BOILERPLATE_FULL (GstValve, gst_valve, GstElement, + GST_TYPE_ELEMENT, _do_init); + +static void +gst_valve_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&srctemplate)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sinktemplate)); + + gst_element_class_set_details (element_class, &gst_valve_details); +} + +static void +gst_valve_class_init (GstValveClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_valve_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_valve_get_property); + + g_object_class_install_property (gobject_class, ARG_DROP, + g_param_spec_boolean ("drop", + "Drops all buffers if TRUE", + "If this property if TRUE, the element will drop all buffers, if its FALSE, it will let them through", + FALSE, G_PARAM_READWRITE)); + + parent_class = g_type_class_peek_parent (klass); +} + +static void +gst_valve_init (GstValve * valve, GstValveClass * klass) +{ + valve->drop = FALSE; + valve->discont = FALSE; + + valve->srcpad = gst_pad_new_from_static_template (&srctemplate, "src"); + gst_pad_set_getcaps_function (valve->srcpad, + GST_DEBUG_FUNCPTR (gst_valve_getcaps)); + gst_element_add_pad (GST_ELEMENT (valve), valve->srcpad); + + valve->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink"); + gst_pad_set_chain_function (valve->sinkpad, + GST_DEBUG_FUNCPTR (gst_valve_chain)); + gst_pad_set_event_function (valve->sinkpad, + GST_DEBUG_FUNCPTR (gst_valve_event)); + gst_pad_set_bufferalloc_function (valve->sinkpad, + GST_DEBUG_FUNCPTR (gst_valve_buffer_alloc)); + gst_pad_set_getcaps_function (valve->sinkpad, + GST_DEBUG_FUNCPTR (gst_valve_getcaps)); + gst_element_add_pad (GST_ELEMENT (valve), valve->sinkpad); +} + + +static void +gst_valve_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstValve *valve = GST_VALVE (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + case ARG_DROP: + GST_OBJECT_LOCK (object); + valve->drop = g_value_get_boolean (value); + GST_OBJECT_UNLOCK (object); + break; + } +} + +static void +gst_valve_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstValve *valve = GST_VALVE (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + case ARG_DROP: + GST_OBJECT_LOCK (object); + g_value_set_boolean (value, valve->drop); + GST_OBJECT_UNLOCK (object); + break; + } +} + +static GstFlowReturn +gst_valve_chain (GstPad * pad, GstBuffer * buffer) +{ + GstValve *valve = GST_VALVE (gst_pad_get_parent_element (pad)); + GstFlowReturn ret = GST_FLOW_OK; + gboolean drop; + + GST_OBJECT_LOCK (GST_OBJECT (valve)); + drop = valve->drop; + + if (!drop && valve->discont) { + buffer = gst_buffer_make_metadata_writable (buffer); + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); + valve->discont = FALSE; + } + GST_OBJECT_UNLOCK (GST_OBJECT (valve)); + + if (drop) + gst_buffer_unref (buffer); + else + ret = gst_pad_push (valve->srcpad, buffer); + + + GST_OBJECT_LOCK (GST_OBJECT (valve)); + if (valve->drop) + ret = GST_FLOW_OK; + GST_OBJECT_UNLOCK (GST_OBJECT (valve)); + + gst_object_unref (valve); + + return ret; +} + + +static gboolean +gst_valve_event (GstPad * pad, GstEvent * event) +{ + GstValve *valve = GST_VALVE (gst_pad_get_parent_element (pad)); + gboolean ret = TRUE; + gboolean drop; + + GST_OBJECT_LOCK (GST_OBJECT (valve)); + drop = valve->drop; + GST_OBJECT_UNLOCK (GST_OBJECT (valve)); + + if (drop) + gst_event_unref (event); + else + ret = gst_pad_push_event (valve->srcpad, event); + + GST_OBJECT_LOCK (GST_OBJECT (valve)); + if (valve->drop) + ret = TRUE; + GST_OBJECT_UNLOCK (GST_OBJECT (valve)); + + gst_object_unref (valve); + return ret; +} + +static GstFlowReturn +gst_valve_buffer_alloc (GstPad * pad, guint64 offset, guint size, + GstCaps * caps, GstBuffer ** buf) +{ + GstValve *valve = GST_VALVE (gst_pad_get_parent_element (pad)); + GstFlowReturn ret = GST_FLOW_OK; + gboolean drop; + + GST_OBJECT_LOCK (GST_OBJECT (valve)); + drop = valve->drop; + GST_OBJECT_UNLOCK (GST_OBJECT (valve)); + + if (drop) + *buf = NULL; + else + ret = gst_pad_alloc_buffer (valve->srcpad, offset, size, caps, buf); + + GST_OBJECT_LOCK (GST_OBJECT (valve)); + if (valve->drop) + ret = GST_FLOW_OK; + GST_OBJECT_UNLOCK (GST_OBJECT (valve)); + + gst_object_unref (valve); + + return ret; +} + +static GstCaps * +gst_valve_getcaps (GstPad * pad) +{ + GstValve *valve = GST_VALVE (gst_pad_get_parent (pad)); + GstCaps *caps; + + if (pad == valve->sinkpad) + caps = gst_pad_peer_get_caps (valve->srcpad); + else + caps = gst_pad_peer_get_caps (valve->sinkpad); + + if (caps == NULL) + caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); + + gst_object_unref (valve); + + return caps; +} + + +static gboolean +plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "valve", + GST_RANK_MARGINAL, GST_TYPE_VALVE); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "valve", + "Valve", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/gstvalve.h b/gst/gstvalve.h new file mode 100644 index 0000000..cc7cd38 --- /dev/null +++ b/gst/gstvalve.h @@ -0,0 +1,82 @@ +/* + * Farsight Voice+Video library + * + * Copyright 2007 Collabora Ltd, + * Copyright 2007 Nokia Corporation + * @author: Olivier Crete <olivier.crete@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef __GST_VALVE_H__ +#define __GST_VALVE_H__ + +#include <gst/gst.h> + +G_BEGIN_DECLS +/* #define's don't like whitespacey bits */ +#define GST_TYPE_VALVE \ + (gst_valve_get_type()) +#define GST_VALVE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + GST_TYPE_VALVE,GstValve)) +#define GST_VALVE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), \ + GST_TYPE_VALVE,GstValveClass)) +#define GST_IS_VALVE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VALVE)) +#define GST_IS_VALVE_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VALVE)) +typedef struct _GstValve GstValve; +typedef struct _GstValveClass GstValveClass; +typedef struct _GstValvePrivate GstValvePrivate; + +/** + * GstValve: + * + * The private valve structure + */ +struct _GstValve +{ + /*< private >*/ + GstElement parent; + + /* Protected by the object lock */ + gboolean drop; + + /* Protected by the stream lock */ + gboolean discont; + + GstPad *srcpad; + GstPad *sinkpad; + + /*< private > */ + gpointer _gst_reserved[GST_PADDING]; +}; + +struct _GstValveClass +{ + GstElementClass parent_class; + + /*< private > */ + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_valve_get_type (void); + +G_END_DECLS +#endif /* __GST_VALVE_H__ */ diff --git a/gst/gstvideorate.c b/gst/gstvideorate.c new file mode 100644 index 0000000..b728357 --- /dev/null +++ b/gst/gstvideorate.c @@ -0,0 +1,877 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-videorate + * + * This element takes an incoming stream of timestamped video frames. + * It will produce a perfect stream that matches the source pad's framerate. + * + * The correction is performed by dropping and duplicating frames, no fancy + * algorithm is used to interpolate frames (yet). + * + * By default the element will simply negotiate the same framerate on its + * source and sink pad. + * + * This operation is useful to link to elements that require a perfect stream. + * Typical examples are formats that do not store timestamps for video frames, + * but only store a framerate, like Ogg and AVI. + * + * A conversion to a specific framerate can be forced by using filtered caps on + * the source pad. + * + * The properties #GstVideoRate:in, #GstVideoRate:out, #GstVideoRate:duplicate + * and #GstVideoRate:drop can be read to obtain information about number of + * input frames, output frames, dropped frames (i.e. the number of unused input + * frames) and duplicated frames (i.e. the number of times an input frame was + * duplicated, beside being used normally). + * + * An input stream that needs no adjustments will thus never have dropped or + * duplicated frames. + * + * When the #GstVideoRate:silent property is set to FALSE, a GObject property + * notification will be emitted whenever one of the #GstVideoRate:duplicate or + * #GstVideoRate:drop values changes. + * This can potentially cause performance degradation. + * Note that property notification will happen from the streaming thread, so + * applications should be prepared for this. + * + * <refsect2> + * <title>Example pipelines</title> + * |[ + * gst-launch -v filesrc location=videotestsrc.ogg ! oggdemux ! theoradec ! videorate ! video/x-raw-yuv,framerate=15/1 ! xvimagesink + * ]| Decode an Ogg/Theora file and adjust the framerate to 15 fps before playing. + * To create the test Ogg/Theora file refer to the documentation of theoraenc. + * |[ + * gst-launch -v v4lsrc ! videorate ! video/x-raw-yuv,framerate=25/2 ! theoraenc ! oggmux ! filesink location=v4l.ogg + * ]| Capture video from a V4L device, and adjust the stream to 12.5 fps before + * encoding to Ogg/Theora. + * </refsect2> + * + * Last reviewed on 2006-09-02 (0.10.11) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstvideorate.h" + +GST_DEBUG_CATEGORY_STATIC (video_rate_debug); +#define GST_CAT_DEFAULT video_rate_debug + +/* elementfactory information */ +static const GstElementDetails video_rate_details = +GST_ELEMENT_DETAILS ("Video rate adjuster", + "Filter/Effect/Video", + "Drops/duplicates/adjusts timestamps on video frames to make a perfect stream", + "Wim Taymans <wim@fluendo.com>"); + +/* GstVideoRate signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +#define DEFAULT_SILENT TRUE +#define DEFAULT_NEW_PREF 1.0 +#define DEFAULT_SKIP_TO_FIRST FALSE + +enum +{ + ARG_0, + ARG_IN, + ARG_OUT, + ARG_DUP, + ARG_DROP, + ARG_SILENT, + ARG_NEW_PREF, + ARG_SKIP_TO_FIRST + /* FILL ME */ +}; + +static GstStaticPadTemplate gst_video_rate_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb; image/jpeg; image/png") + ); + +static GstStaticPadTemplate gst_video_rate_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb; image/jpeg; image/png") + ); + +static void gst_video_rate_swap_prev (GstVideoRate * videorate, + GstBuffer * buffer, gint64 time); +static gboolean gst_video_rate_event (GstPad * pad, GstEvent * event); +static gboolean gst_video_rate_query (GstPad * pad, GstQuery * query); +static GstFlowReturn gst_video_rate_chain (GstPad * pad, GstBuffer * buffer); + +static void gst_video_rate_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_video_rate_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static GstStateChangeReturn gst_video_rate_change_state (GstElement * element, + GstStateChange transition); + +/*static guint gst_video_rate_signals[LAST_SIGNAL] = { 0 }; */ + +GST_BOILERPLATE (GstVideoRate, gst_video_rate, GstElement, GST_TYPE_ELEMENT); + +static void +gst_video_rate_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details (element_class, &video_rate_details); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_video_rate_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_video_rate_src_template)); +} +static void +gst_video_rate_class_init (GstVideoRateClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->set_property = gst_video_rate_set_property; + object_class->get_property = gst_video_rate_get_property; + + g_object_class_install_property (object_class, ARG_IN, + g_param_spec_uint64 ("in", "In", + "Number of input frames", 0, G_MAXUINT64, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, ARG_OUT, + g_param_spec_uint64 ("out", "Out", "Number of output frames", 0, + G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, ARG_DUP, + g_param_spec_uint64 ("duplicate", "Duplicate", + "Number of duplicated frames", 0, G_MAXUINT64, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, ARG_DROP, + g_param_spec_uint64 ("drop", "Drop", "Number of dropped frames", 0, + G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, ARG_SILENT, + g_param_spec_boolean ("silent", "silent", + "Don't emit notify for dropped and duplicated frames", DEFAULT_SILENT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, ARG_NEW_PREF, + g_param_spec_double ("new-pref", "New Pref", + "Value indicating how much to prefer new frames (unused)", 0.0, 1.0, + DEFAULT_NEW_PREF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, ARG_SKIP_TO_FIRST, + g_param_spec_boolean ("skip-to-first", "Skip to first buffer", + "Don't produce buffers before the first one we receive", + DEFAULT_SKIP_TO_FIRST, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + element_class->change_state = gst_video_rate_change_state; +} + +/* return the caps that can be used on out_pad given in_caps on in_pad */ +static gboolean +gst_video_rate_transformcaps (GstPad * in_pad, GstCaps * in_caps, + GstPad * out_pad, GstCaps ** out_caps) +{ + GstCaps *intersect; + const GstCaps *in_templ; + gint i; + + in_templ = gst_pad_get_pad_template_caps (in_pad); + intersect = gst_caps_intersect (in_caps, in_templ); + + /* all possible framerates are allowed */ + for (i = 0; i < gst_caps_get_size (intersect); i++) { + GstStructure *structure; + + structure = gst_caps_get_structure (intersect, i); + + gst_structure_set (structure, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL); + } + *out_caps = intersect; + + return TRUE; +} + +static GstCaps * +gst_video_rate_getcaps (GstPad * pad) +{ + GstVideoRate *videorate; + GstPad *otherpad; + GstCaps *caps; + + videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad)); + + otherpad = (pad == videorate->srcpad) ? videorate->sinkpad : + videorate->srcpad; + + /* we can do what the peer can */ + caps = gst_pad_peer_get_caps (otherpad); + if (caps) { + GstCaps *transform; + + gst_video_rate_transformcaps (otherpad, caps, pad, &transform); + gst_caps_unref (caps); + caps = transform; + } else { + /* no peer, our padtemplate is enough then */ + caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); + } + + return caps; +} + +static gboolean +gst_video_rate_setcaps (GstPad * pad, GstCaps * caps) +{ + GstVideoRate *videorate; + GstStructure *structure; + gboolean ret = TRUE; + GstPad *otherpad, *opeer; + gint rate_numerator, rate_denominator; + + videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad)); + + GST_DEBUG ("setcaps called %" GST_PTR_FORMAT, caps); + + structure = gst_caps_get_structure (caps, 0); + if (!gst_structure_get_fraction (structure, "framerate", + &rate_numerator, &rate_denominator)) + goto no_framerate; + + if (pad == videorate->srcpad) { + videorate->to_rate_numerator = rate_numerator; + videorate->to_rate_denominator = rate_denominator; + otherpad = videorate->sinkpad; + } else { + videorate->from_rate_numerator = rate_numerator; + videorate->from_rate_denominator = rate_denominator; + otherpad = videorate->srcpad; + } + /* now try to find something for the peer */ + opeer = gst_pad_get_peer (otherpad); + if (opeer) { + if (gst_pad_accept_caps (opeer, caps)) { + /* the peer accepts the caps as they are */ + gst_pad_set_caps (otherpad, caps); + + ret = TRUE; + } else { + GstCaps *peercaps; + GstCaps *intersect; + GstCaps *transform = NULL; + + ret = FALSE; + + /* see how we can transform the input caps */ + if (!gst_video_rate_transformcaps (pad, caps, otherpad, &transform)) + goto no_transform; + + /* see what the peer can do */ + peercaps = gst_pad_get_caps (opeer); + + GST_DEBUG ("icaps %" GST_PTR_FORMAT, peercaps); + GST_DEBUG ("transform %" GST_PTR_FORMAT, transform); + + /* filter against our possibilities */ + intersect = gst_caps_intersect (peercaps, transform); + gst_caps_unref (peercaps); + gst_caps_unref (transform); + + GST_DEBUG ("intersect %" GST_PTR_FORMAT, intersect); + + /* take first possibility */ + caps = gst_caps_copy_nth (intersect, 0); + gst_caps_unref (intersect); + structure = gst_caps_get_structure (caps, 0); + + /* and fixate */ + gst_structure_fixate_field_nearest_fraction (structure, "framerate", + rate_numerator, rate_denominator); + + gst_structure_get_fraction (structure, "framerate", + &rate_numerator, &rate_denominator); + + if (otherpad == videorate->srcpad) { + videorate->to_rate_numerator = rate_numerator; + videorate->to_rate_denominator = rate_denominator; + } else { + videorate->from_rate_numerator = rate_numerator; + videorate->from_rate_denominator = rate_denominator; + } + gst_pad_set_caps (otherpad, caps); + gst_caps_unref (caps); + ret = TRUE; + } + gst_object_unref (opeer); + } +done: + /* After a setcaps, our caps may have changed. In that case, we can't use + * the old buffer, if there was one (it might have different dimensions) */ + GST_DEBUG ("swapping old buffers"); + gst_video_rate_swap_prev (videorate, NULL, GST_CLOCK_TIME_NONE); + + gst_object_unref (videorate); + return ret; + +no_framerate: + { + GST_DEBUG_OBJECT (videorate, "no framerate specified"); + goto done; + } +no_transform: + { + GST_DEBUG_OBJECT (videorate, "no framerate transform possible"); + ret = FALSE; + goto done; + } +} + +static void +gst_video_rate_reset (GstVideoRate * videorate) +{ + GST_DEBUG ("resetting internal variables"); + + videorate->in = 0; + videorate->out = 0; + videorate->segment_out = 0; + videorate->drop = 0; + videorate->dup = 0; + videorate->next_ts = GST_CLOCK_TIME_NONE; + gst_video_rate_swap_prev (videorate, NULL, 0); + + gst_segment_init (&videorate->segment, GST_FORMAT_TIME); +} + +static void +gst_video_rate_init (GstVideoRate * videorate, GstVideoRateClass * klass) +{ + GST_DEBUG ("gst_video_rate_init"); + videorate->sinkpad = + gst_pad_new_from_static_template (&gst_video_rate_sink_template, "sink"); + gst_pad_set_event_function (videorate->sinkpad, gst_video_rate_event); + gst_pad_set_chain_function (videorate->sinkpad, gst_video_rate_chain); + gst_pad_set_getcaps_function (videorate->sinkpad, gst_video_rate_getcaps); + gst_pad_set_setcaps_function (videorate->sinkpad, gst_video_rate_setcaps); + gst_element_add_pad (GST_ELEMENT (videorate), videorate->sinkpad); + + videorate->srcpad = + gst_pad_new_from_static_template (&gst_video_rate_src_template, "src"); + gst_pad_set_query_function (videorate->srcpad, gst_video_rate_query); + gst_pad_set_getcaps_function (videorate->srcpad, gst_video_rate_getcaps); + gst_pad_set_setcaps_function (videorate->srcpad, gst_video_rate_setcaps); + gst_element_add_pad (GST_ELEMENT (videorate), videorate->srcpad); + + gst_video_rate_reset (videorate); + videorate->silent = DEFAULT_SILENT; + videorate->new_pref = DEFAULT_NEW_PREF; + + videorate->from_rate_numerator = 0; + videorate->from_rate_denominator = 0; + videorate->to_rate_numerator = 0; + videorate->to_rate_denominator = 0; + + videorate->first_pass_buffer = TRUE; +} + +/* flush the oldest buffer */ +static GstFlowReturn +gst_video_rate_flush_prev (GstVideoRate * videorate) +{ + GstFlowReturn res; + GstBuffer *outbuf; + GstClockTime push_ts; + + if (!videorate->prevbuf) + goto eos_before_buffers; + + /* make sure we can write to the metadata */ + outbuf = gst_buffer_make_metadata_writable + (gst_buffer_ref (videorate->prevbuf)); + + GST_BUFFER_OFFSET (outbuf) = videorate->out; + GST_BUFFER_OFFSET_END (outbuf) = videorate->out + 1; + + /* this is the timestamp we put on the buffer */ + push_ts = videorate->next_ts; + + videorate->out++; + videorate->segment_out++; + if (videorate->to_rate_numerator) { + /* interpolate next expected timestamp in the segment */ + videorate->next_ts = videorate->segment.accum + videorate->segment.start + + gst_util_uint64_scale (videorate->segment_out, + videorate->to_rate_denominator * GST_SECOND, + videorate->to_rate_numerator); + GST_BUFFER_DURATION (outbuf) = videorate->next_ts - push_ts; + } + + /* adapt for looping, bring back to time in current segment. */ + GST_BUFFER_TIMESTAMP (outbuf) = push_ts - videorate->segment.accum; + + gst_buffer_set_caps (outbuf, GST_PAD_CAPS (videorate->srcpad)); + + GST_LOG_OBJECT (videorate, + "old is best, dup, pushing buffer outgoing ts %" GST_TIME_FORMAT, + GST_TIME_ARGS (push_ts)); + + res = gst_pad_push (videorate->srcpad, outbuf); + + return res; + + /* WARNINGS */ +eos_before_buffers: + { + GST_INFO_OBJECT (videorate, "got EOS before any buffer was received"); + return GST_FLOW_OK; + } +} + +static void +gst_video_rate_swap_prev (GstVideoRate * videorate, GstBuffer * buffer, + gint64 time) +{ + GST_LOG_OBJECT (videorate, "swap_prev: storing buffer %p in prev", buffer); + if (videorate->prevbuf) + gst_buffer_unref (videorate->prevbuf); + videorate->prevbuf = buffer; + videorate->prev_ts = time; +} + +#define MAGIC_LIMIT 25 +static gboolean +gst_video_rate_event (GstPad * pad, GstEvent * event) +{ + GstVideoRate *videorate; + gboolean ret; + + videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT: + { + gint64 start, stop, time; + gdouble rate, arate; + gboolean update; + GstFormat format; + + gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, + &start, &stop, &time); + + if (format != GST_FORMAT_TIME) + goto format_error; + + GST_DEBUG_OBJECT (videorate, "handle NEWSEGMENT"); + + /* close up the previous segment, if appropriate */ + if (!update && videorate->prevbuf) { + gint count = 0; + GstFlowReturn res; + + res = GST_FLOW_OK; + /* fill up to the end of current segment, + * or only send out the stored buffer if there is no specific stop. + * regardless, prevent going loopy in strange cases */ + while (res == GST_FLOW_OK && count <= MAGIC_LIMIT && + ((GST_CLOCK_TIME_IS_VALID (videorate->segment.stop) && + videorate->next_ts - videorate->segment.accum + < videorate->segment.stop) + || count < 1)) { + gst_video_rate_flush_prev (videorate); + count++; + } + if (count > 1) { + videorate->dup += count - 1; + if (!videorate->silent) + g_object_notify (G_OBJECT (videorate), "duplicate"); + } else if (count == 0) { + videorate->drop++; + if (!videorate->silent) + g_object_notify (G_OBJECT (videorate), "drop"); + } + /* clean up for the new one; _chain will resume from the new start */ + videorate->segment_out = 0; + gst_video_rate_swap_prev (videorate, NULL, 0); + videorate->next_ts = GST_CLOCK_TIME_NONE; + } + + /* We just want to update the accumulated stream_time */ + gst_segment_set_newsegment_full (&videorate->segment, update, rate, arate, + format, start, stop, time); + + GST_DEBUG_OBJECT (videorate, "updated segment: %" GST_SEGMENT_FORMAT, + &videorate->segment); + break; + } + case GST_EVENT_EOS: + /* flush last queued frame */ + GST_DEBUG_OBJECT (videorate, "Got EOS"); + gst_video_rate_flush_prev (videorate); + break; + case GST_EVENT_FLUSH_STOP: + /* also resets the segment */ + GST_DEBUG_OBJECT (videorate, "Got FLUSH_STOP"); + gst_video_rate_reset (videorate); + break; + default: + break; + } + + ret = gst_pad_push_event (videorate->srcpad, event); + +done: + gst_object_unref (videorate); + + return ret; + + /* ERRORS */ +format_error: + { + GST_WARNING_OBJECT (videorate, + "Got segment but doesn't have GST_FORMAT_TIME value"); + gst_event_unref (event); + ret = FALSE; + goto done; + } +} + +static gboolean +gst_video_rate_query (GstPad * pad, GstQuery * query) +{ + GstVideoRate *videorate; + gboolean res = FALSE; + + videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad)); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_LATENCY: + { + GstClockTime min, max; + gboolean live; + guint64 latency; + GstPad *peer; + + if ((peer = gst_pad_get_peer (videorate->sinkpad))) { + if ((res = gst_pad_query (peer, query))) { + gst_query_parse_latency (query, &live, &min, &max); + + GST_DEBUG_OBJECT (videorate, "Peer latency: min %" + GST_TIME_FORMAT " max %" GST_TIME_FORMAT, + GST_TIME_ARGS (min), GST_TIME_ARGS (max)); + + /* add latency. We don't really know since we hold on to the frames + * until we get a next frame, which can be anything. We assume + * however that this will take from_rate time. */ + latency = gst_util_uint64_scale (GST_SECOND, + videorate->from_rate_denominator, videorate->from_rate_numerator); + + GST_DEBUG_OBJECT (videorate, "Our latency: %" + GST_TIME_FORMAT, GST_TIME_ARGS (latency)); + + min += latency; + if (max != -1) + max += latency; + + GST_DEBUG_OBJECT (videorate, "Calculated total latency : min %" + GST_TIME_FORMAT " max %" GST_TIME_FORMAT, + GST_TIME_ARGS (min), GST_TIME_ARGS (max)); + + gst_query_set_latency (query, live, min, max); + } + gst_object_unref (peer); + } + break; + } + default: + res = gst_pad_query_default (pad, query); + break; + } + gst_object_unref (videorate); + + return res; +} + +static GstFlowReturn +gst_video_rate_chain (GstPad * pad, GstBuffer * buffer) +{ + GstVideoRate *videorate; + GstFlowReturn res = GST_FLOW_OK; + GstClockTime intime, in_ts; + + videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad)); + + /* make sure the denominators are not 0 */ + if (videorate->from_rate_denominator == 0 || + videorate->to_rate_denominator == 0) + goto not_negotiated; + + in_ts = GST_BUFFER_TIMESTAMP (buffer); + + if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE)) + goto invalid_buffer; + + GST_DEBUG_OBJECT (videorate, "got buffer with timestamp %" GST_TIME_FORMAT, + GST_TIME_ARGS (in_ts)); + + /* the input time is the time in the segment + all previously accumulated + * segments */ + intime = in_ts + videorate->segment.accum; + + /* we need to have two buffers to compare */ + if (videorate->prevbuf == NULL) { + gst_video_rate_swap_prev (videorate, buffer, intime); + videorate->in++; + if (!GST_CLOCK_TIME_IS_VALID (videorate->next_ts)) { + /* new buffer, we expect to output a buffer that matches the first + * timestamp in the segment */ + if (videorate->skip_to_first) { + videorate->next_ts = in_ts; + videorate->segment_out = gst_util_uint64_scale (in_ts, + videorate->to_rate_numerator, + videorate->to_rate_denominator * GST_SECOND) - + (videorate->segment.accum + videorate->segment.start); + } + else { + videorate->next_ts = videorate->segment.start + videorate->segment.accum; + } + } + } else { + GstClockTime prevtime; + gint count = 0; + gint64 diff1, diff2; + + prevtime = videorate->prev_ts; + + GST_LOG_OBJECT (videorate, + "BEGINNING prev buf %" GST_TIME_FORMAT " new buf %" GST_TIME_FORMAT + " outgoing ts %" GST_TIME_FORMAT, GST_TIME_ARGS (prevtime), + GST_TIME_ARGS (intime), GST_TIME_ARGS (videorate->next_ts)); + + videorate->in++; + + /* drop new buffer if it's before previous one */ + if (intime < prevtime) { + GST_DEBUG_OBJECT (videorate, + "The new buffer (%" GST_TIME_FORMAT + ") is before the previous buffer (%" + GST_TIME_FORMAT "). Dropping new buffer.", + GST_TIME_ARGS (intime), GST_TIME_ARGS (prevtime)); + videorate->drop++; + if (!videorate->silent) + g_object_notify (G_OBJECT (videorate), "drop"); + gst_buffer_unref (buffer); + goto done; + } + + /* got 2 buffers, see which one is the best */ + do { + + diff1 = prevtime - videorate->next_ts; + diff2 = intime - videorate->next_ts; + + /* take absolute values, beware: abs and ABS don't work for gint64 */ + if (diff1 < 0) + diff1 = -diff1; + if (diff2 < 0) + diff2 = -diff2; + + GST_LOG_OBJECT (videorate, + "diff with prev %" GST_TIME_FORMAT " diff with new %" + GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT, + GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2), + GST_TIME_ARGS (videorate->next_ts)); + + /* output first one when its the best */ + if (diff1 < diff2) { + count++; + + /* on error the _flush function posted a warning already */ + if ((res = gst_video_rate_flush_prev (videorate)) != GST_FLOW_OK) { + gst_buffer_unref (buffer); + goto done; + } + + // XXX let downstream theoraenc treat first buffer + // as a buffer with keyframe + if (videorate->first_pass_buffer == TRUE) { + videorate->first_pass_buffer = FALSE; + break; + } + } + /* continue while the first one was the best */ + } + while (diff1 < diff2); + + /* if we outputed the first buffer more then once, we have dups */ + if (count > 1) { + videorate->dup += count - 1; + if (!videorate->silent) + g_object_notify (G_OBJECT (videorate), "duplicate"); + } + /* if we didn't output the first buffer, we have a drop */ + else if (count == 0) { + videorate->drop++; + + if (!videorate->silent) + g_object_notify (G_OBJECT (videorate), "drop"); + + GST_LOG_OBJECT (videorate, + "new is best, old never used, drop, outgoing ts %" + GST_TIME_FORMAT, GST_TIME_ARGS (videorate->next_ts)); + } + GST_LOG_OBJECT (videorate, + "END, putting new in old, diff1 %" GST_TIME_FORMAT + ", diff2 %" GST_TIME_FORMAT ", next_ts %" GST_TIME_FORMAT + ", in %lld, out %lld, drop %lld, dup %lld", GST_TIME_ARGS (diff1), + GST_TIME_ARGS (diff2), GST_TIME_ARGS (videorate->next_ts), + videorate->in, videorate->out, videorate->drop, videorate->dup); + + /* swap in new one when it's the best */ + gst_video_rate_swap_prev (videorate, buffer, intime); + } +done: + return res; + + /* ERRORS */ +not_negotiated: + { + GST_WARNING_OBJECT (videorate, "no framerate negotiated"); + gst_buffer_unref (buffer); + res = GST_FLOW_NOT_NEGOTIATED; + goto done; + } + +invalid_buffer: + { + GST_WARNING_OBJECT (videorate, + "Got buffer with GST_CLOCK_TIME_NONE timestamp, discarding it"); + gst_buffer_unref (buffer); + goto done; + } +} + +static void +gst_video_rate_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstVideoRate *videorate = GST_VIDEO_RATE (object); + + switch (prop_id) { + case ARG_SILENT: + videorate->silent = g_value_get_boolean (value); + break; + case ARG_NEW_PREF: + videorate->new_pref = g_value_get_double (value); + break; + case ARG_SKIP_TO_FIRST: + videorate->skip_to_first = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_video_rate_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstVideoRate *videorate = GST_VIDEO_RATE (object); + + switch (prop_id) { + case ARG_IN: + g_value_set_uint64 (value, videorate->in); + break; + case ARG_OUT: + g_value_set_uint64 (value, videorate->out); + break; + case ARG_DUP: + g_value_set_uint64 (value, videorate->dup); + break; + case ARG_DROP: + g_value_set_uint64 (value, videorate->drop); + break; + case ARG_SILENT: + g_value_set_boolean (value, videorate->silent); + break; + case ARG_NEW_PREF: + g_value_set_double (value, videorate->new_pref); + break; + case ARG_SKIP_TO_FIRST: + g_value_set_boolean (value, videorate->skip_to_first); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstStateChangeReturn +gst_video_rate_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstVideoRate *videorate; + + videorate = GST_VIDEO_RATE (element); + + switch (transition) { + default: + break; + } + + ret = parent_class->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_video_rate_reset (videorate); + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + videorate->first_pass_buffer = TRUE; + default: + break; + } + + return ret; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (video_rate_debug, "videorate", 0, + "VideoRate stream fixer"); + + return gst_element_register (plugin, "videorate", GST_RANK_NONE, + GST_TYPE_VIDEO_RATE); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "videorate", + "Adjusts video frames", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/gstvideorate.h b/gst/gstvideorate.h new file mode 100644 index 0000000..01abcae --- /dev/null +++ b/gst/gstvideorate.h @@ -0,0 +1,81 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VIDEO_RATE_H__ +#define __GST_VIDEO_RATE_H__ + +#include <gst/gst.h> + +G_BEGIN_DECLS + +#define GST_TYPE_VIDEO_RATE \ + (gst_video_rate_get_type()) +#define GST_VIDEO_RATE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_RATE,GstVideoRate)) +#define GST_VIDEO_RATE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_RATE,GstVideoRateClass)) +#define GST_IS_VIDEO_RATE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_RATE)) +#define GST_IS_VIDEO_RATE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_RATE)) + +typedef struct _GstVideoRate GstVideoRate; +typedef struct _GstVideoRateClass GstVideoRateClass; + +/** + * GstVideoRate: + * + * Opaque data structure. + */ +struct _GstVideoRate +{ + GstElement element; + + GstPad *sinkpad, *srcpad; + + /* video state */ + gint from_rate_numerator, from_rate_denominator; + gint to_rate_numerator, to_rate_denominator; + guint64 next_ts; /* Timestamp of next buffer to output */ + GstBuffer *prevbuf; + guint64 prev_ts; /* Previous buffer timestamp */ + guint64 segment_out; /* in-segment counting */ + + /* segment handling */ + GstSegment segment; + + /* properties */ + guint64 in, out, dup, drop; + gboolean silent; + gdouble new_pref; + gboolean skip_to_first; + + // XXX let downstream theoraenc treat first buffer + // as a buffer with keyframe + gboolean first_pass_buffer; +}; + +struct _GstVideoRateClass +{ + GstElementClass parent_class; +}; + +G_END_DECLS + +#endif /* __GST_VIDEO_RATE_H__ */ diff --git a/gst/m4/Makefile.am b/gst/m4/Makefile.am new file mode 100644 index 0000000..4a44032 --- /dev/null +++ b/gst/m4/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = as-version.m4 as-compiler-flag.m4 diff --git a/gst/m4/as-compiler-flag.m4 b/gst/m4/as-compiler-flag.m4 new file mode 100644 index 0000000..2f0ba19 --- /dev/null +++ b/gst/m4/as-compiler-flag.m4 @@ -0,0 +1,25 @@ +dnl as-compiler-flag.m4 0.0.1 +dnl autostars m4 macro for detection of compiler flags +dnl +dnl ds@schleef.org + +AC_DEFUN([AS_COMPILER_FLAG], +[ + AC_MSG_CHECKING([to see if compiler understands $1]) + + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $1" + + AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + CFLAGS="$save_CFLAGS" + + if test "X$flag_ok" = Xyes ; then + $2 + true + else + $3 + true + fi + AC_MSG_RESULT([$flag_ok]) +]) + diff --git a/gst/m4/as-version.m4 b/gst/m4/as-version.m4 new file mode 100644 index 0000000..0bee437 --- /dev/null +++ b/gst/m4/as-version.m4 @@ -0,0 +1,66 @@ +dnl as-version.m4 0.1.0 + +dnl autostars m4 macro for versioning + +dnl Thomas Vander Stichele <thomas at apestaart dot org> + +dnl $Id: as-version.m4,v 1.2 2004-09-17 22:18:03 leroutier Exp $ + +dnl AS_VERSION(PACKAGE, PREFIX, MAJOR, MINOR, MICRO, NANO, +dnl ACTION-IF-NO-NANO, [ACTION-IF-NANO]) + +dnl example +dnl AS_VERSION(gstreamer, GST_VERSION, 0, 3, 2,) +dnl for a 0.3.2 release version + +dnl this macro +dnl - defines [$PREFIX]_MAJOR, MINOR and MICRO +dnl - if NANO is empty, then we're in release mode, else in cvs/dev mode +dnl - defines [$PREFIX], VERSION, and [$PREFIX]_RELEASE +dnl - executes the relevant action +dnl - AC_SUBST's PACKAGE, VERSION, [$PREFIX] and [$PREFIX]_RELEASE +dnl as well as the little ones +dnl - doesn't call AM_INIT_AUTOMAKE anymore because it prevents +dnl maintainer mode from running ok +dnl +dnl don't forget to put #undef [$2] and [$2]_RELEASE in acconfig.h +dnl if you use acconfig.h + +AC_DEFUN([AS_VERSION], +[ + PACKAGE=[$1] + [$2]_MAJOR=[$3] + [$2]_MINOR=[$4] + [$2]_MICRO=[$5] + NANO=[$6] + [$2]_NANO=$NANO + if test "x$NANO" = "x" || test "x$NANO" = "x0"; + then + AC_MSG_NOTICE(configuring [$1] for release) + VERSION=[$3].[$4].[$5] + [$2]_RELEASE=1 + dnl execute action + ifelse([$7], , :, [$7]) + else + AC_MSG_NOTICE(configuring [$1] for development with nano $NANO) + VERSION=[$3].[$4].[$5].$NANO + [$2]_RELEASE=0.`date +%Y%m%d.%H%M%S` + dnl execute action + ifelse([$8], , :, [$8]) + fi + + [$2]=$VERSION + AC_DEFINE_UNQUOTED([$2], "$[$2]", [Define the version]) + AC_SUBST([$2]) + AC_DEFINE_UNQUOTED([$2]_RELEASE, "$[$2]_RELEASE", [Define the release version]) + AC_SUBST([$2]_RELEASE) + + AC_SUBST([$2]_MAJOR) + AC_SUBST([$2]_MINOR) + AC_SUBST([$2]_MICRO) + AC_SUBST([$2]_NANO) + AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Define the package name]) + AC_SUBST(PACKAGE) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Define the version]) + AC_SUBST(VERSION) +]) diff --git a/instance.py b/instance.py index 1ab5407..d0178df 100644 --- a/instance.py +++ b/instance.py @@ -9,41 +9,41 @@ from color import Color import record class Instance: - key = profile.get_pubkey() - if hasattr(util, '_sha_data'): - # sugar-0.82 and previous - keyHash = util._sha_data(key) - else: - keyHash = util.sha_data(key) + key = profile.get_pubkey() + if hasattr(util, '_sha_data'): + # sugar-0.82 and previous + keyHash = util._sha_data(key) + else: + keyHash = util.sha_data(key) - keyHashPrintable = util.printable_hash(keyHash) - nickName = profile.get_nick_name() + keyHashPrintable = util.printable_hash(keyHash) + nickName = profile.get_nick_name() - colorFill = Color() - colorFill.init_hex( profile.get_color().get_fill_color() ) - colorStroke = Color() - colorStroke.init_hex( profile.get_color().get_stroke_color() ) + colorFill = Color() + colorFill.init_hex( profile.get_color().get_fill_color() ) + colorStroke = Color() + colorStroke.init_hex( profile.get_color().get_stroke_color() ) - instanceId = None - instancePath = None - dataPath = None + instanceId = None + instancePath = None + dataPath = None - def __init__(self, ca): - self.__class__.instanceId = ca._activity_id + def __init__(self, ca): + self.__class__.instanceId = ca._activity_id - self.__class__.instancePath = os.path.join(ca.get_activity_root(), "instance") - recreateTmp() + self.__class__.instancePath = os.path.join(ca.get_activity_root(), "instance") + recreateTmp() - self.__class__.dataPath = os.path.join(ca.get_activity_root(), "data") - recreateData() + self.__class__.dataPath = os.path.join(ca.get_activity_root(), "data") + recreateData() def recreateTmp(): - if (not os.path.exists(Instance.instancePath)): - os.makedirs(Instance.instancePath) + if (not os.path.exists(Instance.instancePath)): + os.makedirs(Instance.instancePath) def recreateData(): - if (not os.path.exists(Instance.dataPath)): - os.makedirs(Instance.dataPath) + if (not os.path.exists(Instance.dataPath)): + os.makedirs(Instance.dataPath) @@ -20,6 +20,7 @@ #THE SOFTWARE. +import uuid import urllib import string import fnmatch @@ -37,6 +38,9 @@ from time import strftime import gobject import operator +import logging +logger = logging.getLogger('record:model.py') + import sugar.env from constants import Constants @@ -50,410 +54,405 @@ import serialize class Model: - def __init__( self, pca ): - self.ca = pca - self.MODE = Constants.MODE_PHOTO - self.UPDATING = True - self.RECORDING = False - self.FULL = self._isXoFull() + def __init__( self, pca ): + self.ca = pca + self.MODE = Constants.MODE_PHOTO + self.UPDATING = True + self.RECORDING = False + self.FULL = self._isXoFull() + + self.mediaHashs = {} + for key,value in Constants.mediaTypes.items(): + self.mediaHashs[key] = [] + + + def updateXoFullStatus( self ): + self.FULL = self._isXoFull() + + + def _isXoFull( self ): + full = False + if (utils.getFreespaceKb() <= Constants.keepFreeKbOnXo): + full = True + + return full + + + def getRecdByMd5( self, md5 ): + for mh in range (0, len(self.mediaHashs)): + for r in range (0, len(self.mediaHashs[mh])): + recd = self.mediaHashs[mh][r] + if (recd.thumbMd5 == md5): + return recd + elif (recd.mediaMd5 == md5): + return recd + + return None + + + def isVideoMode( self ): + return self.MODE == Constants.MODE_VIDEO + + + def isPhotoMode( self ): + return self.MODE == Constants.MODE_PHOTO + + + def displayThumb( self, recd, forceUpdating ): + #to avoid Xlib: unexpected async reply error when taking a picture on a gst callback, always call with idle_add + #this happens b/c this might get called from a gstreamer callback + if (not recd.type == self.MODE): + return + + if (forceUpdating): + self.setUpdating( True ) + hash = self.mediaHashs[recd.type] + if (len(hash) > 0): + self.ca.ui.addThumb(recd, forceUpdating) + if (forceUpdating): + self.setUpdating( False ) + + + def setupMode( self, type, update ): + if (not type == self.MODE): + return + + self.setUpdating( True ) + self.ca.ui.removeThumbs() + hash = self.mediaHashs[type] + for i in range (0, len(hash)): + self.ca.ui.addThumb( hash[i], True ) + if (update): + self.ca.ui.updateModeChange() + self.setUpdating(False) + + + def showNextThumb( self, shownRecd ): + if (shownRecd == None): + self.showLastThumb() + else: + hash = self.mediaHashs[self.MODE] + if (len(hash) > 0): + hash = self.mediaHashs[self.MODE] + i = operator.indexOf( hash, shownRecd ) + i = i+1 + if (i>=len(hash)): + i = 0 + self.ca.ui.showThumbSelection( hash[i] ) + + + def showPrevThumb( self, shownRecd ): + if (shownRecd == None): + self.showLastThumb() + else: + hash = self.mediaHashs[self.MODE] + if (len(hash) > 0): + hash = self.mediaHashs[self.MODE] + i = operator.indexOf( hash, shownRecd ) + i = i-1 + if (i<0): + i = len(hash)-1 + self.ca.ui.showThumbSelection( hash[i] ) - self.mediaHashs = {} - for key,value in Constants.mediaTypes.items(): - self.mediaHashs[key] = [] + def showLastThumb( self ): + hash = self.mediaHashs[self.MODE] + if (len(hash) > 0): + self.ca.ui.showThumbSelection( hash[len(hash)-1] ) - def updateXoFullStatus( self ): - self.FULL = self._isXoFull() + def doShutter( self ): + if (self.UPDATING): + return + + if (self.MODE == Constants.MODE_PHOTO): + self.startTakingPhoto() + elif (self.MODE == Constants.MODE_VIDEO): + if (not self.RECORDING): + self.startRecordingVideo() + else: + #post-processing begins now, so queue up this gfx + self.ca.ui.showPostProcessGfx(True) + self.stopRecordingVideo() + elif (self.MODE == Constants.MODE_AUDIO): + if (not self.RECORDING): + self.startRecordingAudio() + else: + #post-processing begins now, so queue up this gfx + self.ca.ui.showPostProcessGfx(True) + self.stopRecordingAudio() - def _isXoFull( self ): - full = False - if (utils.getFreespaceKb() <= Constants.keepFreeKbOnXo): - full = True - - return full - - - def getRecdByMd5( self, md5 ): - for mh in range (0, len(self.mediaHashs)): - for r in range (0, len(self.mediaHashs[mh])): - recd = self.mediaHashs[mh][r] - if (recd.thumbMd5 == md5): - return recd - elif (recd.mediaMd5 == md5): - return recd - - return None - - - def isVideoMode( self ): - return self.MODE == Constants.MODE_VIDEO - - - def isPhotoMode( self ): - return self.MODE == Constants.MODE_PHOTO - - - def displayThumb( self, recd, forceUpdating ): - #to avoid Xlib: unexpected async reply error when taking a picture on a gst callback, always call with idle_add - #this happens b/c this might get called from a gstreamer callback - if (not recd.type == self.MODE): - return - - if (forceUpdating): - self.setUpdating( True ) - hash = self.mediaHashs[recd.type] - if (len(hash) > 0): - self.ca.ui.addThumb(recd, forceUpdating) - if (forceUpdating): - self.setUpdating( False ) - - - def setupMode( self, type, update ): - if (not type == self.MODE): - return - - self.setUpdating( True ) - self.ca.ui.removeThumbs() - hash = self.mediaHashs[type] - for i in range (0, len(hash)): - self.ca.ui.addThumb( hash[i], True ) - if (update): - self.ca.ui.updateModeChange() - self.setUpdating(False) - - - def showNextThumb( self, shownRecd ): - if (shownRecd == None): - self.showLastThumb() - else: - hash = self.mediaHashs[self.MODE] - if (len(hash) > 0): - hash = self.mediaHashs[self.MODE] - i = operator.indexOf( hash, shownRecd ) - i = i+1 - if (i>=len(hash)): - i = 0 - self.ca.ui.showThumbSelection( hash[i] ) - - def showPrevThumb( self, shownRecd ): - if (shownRecd == None): - self.showLastThumb() - else: - hash = self.mediaHashs[self.MODE] - if (len(hash) > 0): - hash = self.mediaHashs[self.MODE] - i = operator.indexOf( hash, shownRecd ) - i = i-1 - if (i<0): - i = len(hash)-1 - self.ca.ui.showThumbSelection( hash[i] ) + def stopRecordingAudio( self ): + gobject.source_remove( self.ca.ui.UPDATE_DURATION_ID ) + self.ca.ui.progressWindow.updateProgress( 0, "" ) + self.setUpdating( True ) + self.setRecording( False ) + self.ca.ui.FULLSCREEN = False + self.ca.ui.updateVideoComponents() + self.ca.glive.stopRecordingAudio( ) - def showLastThumb( self ): - hash = self.mediaHashs[self.MODE] - if (len(hash) > 0): - self.ca.ui.showThumbSelection( hash[len(hash)-1] ) + def saveAudio( self, tmpPath, pixbuf ): + self.setUpdating( True ) - def doShutter( self ): - if (self.UPDATING): - return + recd = self.createNewRecorded( Constants.TYPE_AUDIO ) + os.rename( tmpPath, os.path.join(Instance.instancePath,recd.mediaFilename)) + + thumbPath = os.path.join(Instance.instancePath, recd.thumbFilename) + scale = float((UI.dim_THUMB_WIDTH+0.0)/(pixbuf.get_width()+0.0)) + thumbImg = utils.generateThumbnail(pixbuf, scale, UI.dim_THUMB_WIDTH, UI.dim_THUMB_HEIGHT) + thumbImg.write_to_png(thumbPath) - if (self.MODE == Constants.MODE_PHOTO): - self.startTakingPhoto() - elif (self.MODE == Constants.MODE_VIDEO): - if (not self.RECORDING): - self.startRecordingVideo() - else: - #post-processing begins now, so queue up this gfx - self.ca.ui.showPostProcessGfx(True) - self.stopRecordingVideo() - elif (self.MODE == Constants.MODE_AUDIO): - if (not self.RECORDING): - self.startRecordingAudio() - else: - #post-processing begins now, so queue up this gfx - self.ca.ui.showPostProcessGfx(True) - self.stopRecordingAudio() - - - def stopRecordingAudio( self ): - gobject.source_remove( self.ca.ui.UPDATE_DURATION_ID ) - self.ca.ui.progressWindow.updateProgress( 0, "" ) - self.setUpdating( True ) - self.setRecording( False ) - self.ca.ui.TRANSCODING = True - self.ca.ui.FULLSCREEN = False - self.ca.ui.updateVideoComponents() + imagePath = os.path.join(Instance.instancePath, "audioPicture.png") + imagePath = utils.getUniqueFilepath( imagePath, 0 ) + pixbuf.save( imagePath, "png", {} ) + recd.audioImageFilename = os.path.basename(imagePath) - self.ca.glive.stopRecordingAudio( ) + #at this point, we have both audio and thumb sapath, so we can save the recd + self.createNewRecordedMd5Sums( recd ) + audioHash = self.mediaHashs[Constants.TYPE_AUDIO] + audioHash.append( recd ) + gobject.idle_add(self.displayThumb, recd, True) + self.doPostSaveVideo() + self.meshShareRecd( recd ) - def saveAudio( self, tmpPath, pixbuf ): - self.setUpdating( True ) - - recd = self.createNewRecorded( Constants.TYPE_AUDIO ) - os.rename( tmpPath, os.path.join(Instance.instancePath,recd.mediaFilename)) - - thumbPath = os.path.join(Instance.instancePath, recd.thumbFilename) - scale = float((UI.dim_THUMB_WIDTH+0.0)/(pixbuf.get_width()+0.0)) - thumbImg = utils.generateThumbnail(pixbuf, scale, UI.dim_THUMB_WIDTH, UI.dim_THUMB_HEIGHT) - thumbImg.write_to_png(thumbPath) - - imagePath = os.path.join(Instance.instancePath, "audioPicture.png") - imagePath = utils.getUniqueFilepath( imagePath, 0 ) - pixbuf.save( imagePath, "png", {} ) - recd.audioImageFilename = os.path.basename(imagePath) - - #at this point, we have both audio and thumb sapath, so we can save the recd - self.createNewRecordedMd5Sums( recd ) - - audioHash = self.mediaHashs[Constants.TYPE_AUDIO] - audioHash.append( recd ) - gobject.idle_add(self.displayThumb, recd, True) - self.doPostSaveVideo() - self.meshShareRecd( recd ) + def startRecordingVideo( self ): + self.setUpdating( True ) + self.setRecording( True ) + #let the red eye kick in before we start the video underway + gobject.idle_add( self.beginRecordingVideo ) - def startRecordingVideo( self ): - self.setUpdating( True ) - self.setRecording( True ) - #let the red eye kick in before we start the video underway - gobject.idle_add( self.beginRecordingVideo ) + def beginRecordingVideo( self ): + self.ca.ui.recordVideo() + self.setUpdating( False ) - def beginRecordingVideo( self ): - self.ca.ui.recordVideo() - self.setUpdating( False ) + def startRecordingAudio( self ): + self.setUpdating( True ) + self.setRecording( True ) + self.ca.ui.recordAudio() + self.setUpdating( False ) - def startRecordingAudio( self ): - self.setUpdating( True ) - self.setRecording( True ) - self.ca.ui.recordAudio() - self.setUpdating( False ) + def setUpdating( self, upd ): + self.UPDATING = upd + self.ca.ui.updateButtonSensitivities() - def setUpdating( self, upd ): - self.UPDATING = upd - self.ca.ui.updateButtonSensitivities() + def setRecording( self, rec ): + self.RECORDING = rec + self.ca.ui.updateButtonSensitivities() - def setRecording( self, rec ): - self.RECORDING = rec - self.ca.ui.updateButtonSensitivities() + def stopRecordingVideo( self ): + self.ca.glive.stopRecordingVideo() + gobject.source_remove( self.ca.ui.UPDATE_DURATION_ID ) + self.setUpdating( True ) + self.setRecording( False ) + self.ca.ui.FULLSCREEN = False + self.ca.ui.updateVideoComponents() - def stopRecordingVideo( self ): - self.ca.glive.stopRecordingVideo() - gobject.source_remove( self.ca.ui.UPDATE_DURATION_ID ) - self.ca.ui.progressWindow.updateProgress( 0, "" ) - self.setUpdating( True ) - self.setRecording( False ) - self.ca.ui.TRANSCODING = True - self.ca.ui.FULLSCREEN = False - self.ca.ui.updateVideoComponents() + def saveVideo( self, pixbuf, tmpPath, wid, hit ): + recd = self.createNewRecorded( Constants.TYPE_VIDEO ) + os.rename( tmpPath, os.path.join(Instance.instancePath,recd.mediaFilename)) - def saveVideo( self, pixbuf, tmpPath, wid, hit ): - recd = self.createNewRecorded( Constants.TYPE_VIDEO ) - os.rename( tmpPath, os.path.join(Instance.instancePath,recd.mediaFilename)) + thumbPath = os.path.join(Instance.instancePath, recd.thumbFilename) + scale = float((UI.dim_THUMB_WIDTH+0.0)/(wid+0.0)) + thumbImg = utils.generateThumbnail(pixbuf, scale, UI.dim_THUMB_WIDTH, UI.dim_THUMB_HEIGHT) + thumbImg.write_to_png(thumbPath) - thumbPath = os.path.join(Instance.instancePath, recd.thumbFilename) - scale = float((UI.dim_THUMB_WIDTH+0.0)/(wid+0.0)) - thumbImg = utils.generateThumbnail(pixbuf, scale, UI.dim_THUMB_WIDTH, UI.dim_THUMB_HEIGHT) - thumbImg.write_to_png(thumbPath) + self.createNewRecordedMd5Sums( recd ) - self.createNewRecordedMd5Sums( recd ) + videoHash = self.mediaHashs[Constants.TYPE_VIDEO] + videoHash.append( recd ) + self.doPostSaveVideo() + gobject.idle_add(self.displayThumb, recd, True) + self.meshShareRecd( recd ) - videoHash = self.mediaHashs[Constants.TYPE_VIDEO] - videoHash.append( recd ) - self.doPostSaveVideo() - gobject.idle_add(self.displayThumb, recd, True) - self.meshShareRecd( recd ) + def meshShareRecd( self, recd ): + #hey, i just took a cool video.audio.photo! let me show you! + if (self.ca.recTube != None): + logger.debug('meshShareRecd') + recdXml = serialize.getRecdXmlMeshString(recd) + self.ca.recTube.notifyBudsOfNewRecd( Instance.keyHashPrintable, recdXml ) - def meshShareRecd( self, recd ): - record.Record.log.debug('meshShareRecd') - #hey, i just took a cool video.audio.photo! let me show you! - if (self.ca.recTube != None): - recdXml = serialize.getRecdXmlMeshString(recd) - self.ca.recTube.notifyBudsOfNewRecd( Instance.keyHashPrintable, recdXml ) + def cannotSaveVideo( self ): + self.doPostSaveVideo() - def cannotSaveVideo( self ): - self.doPostSaveVideo() + def doPostSaveVideo( self ): + self.ca.ui.showPostProcessGfx(False) - def doPostSaveVideo( self ): - self.ca.ui.showPostProcessGfx(False) + #prep the ui for your return + self.ca.ui.LAST_MODE = -1 + self.ca.ui.TRANSCODING = False - #prep the ui for your return - self.ca.ui.LAST_MODE = -1 - self.ca.ui.TRANSCODING = False + #resume live video from the camera (if the activity is active) + if (self.ca.ui.ACTIVE): + self.ca.ui.updateVideoComponents() - #resume live video from the camera (if the activity is active) - if (self.ca.ui.ACTIVE): - self.ca.ui.updateVideoComponents() + self.ca.ui.progressWindow.updateProgress( 0, "" ) + self.setRecording( False ) + self.setUpdating( False ) - self.ca.ui.progressWindow.updateProgress( 0, "" ) - self.setRecording( False ) - self.setUpdating( False ) + def abandonRecording( self ): - def abandonRecording( self ): + self.ca.ui.LAST_MODE = -1 + self.ca.ui.TRANSCODING = False + self.ca.ui.completeTimer() + self.ca.ui.completeCountdown() + self.setRecording(False) - self.ca.ui.LAST_MODE = -1 - self.ca.ui.TRANSCODING = False - self.ca.ui.completeTimer() - self.ca.ui.completeCountdown() - self.setRecording(False) + self.ca.ui.progressWindow.updateProgress( 0, "" ) - self.ca.ui.progressWindow.updateProgress( 0, "" ) + self.ca.glive.abandonMedia() - self.ca.glive.abandonMedia() + def stoppedRecordingVideo( self ): + self.setUpdating( False ) - def stoppedRecordingVideo( self ): - self.setUpdating( False ) + def startTakingPhoto( self ): + self.setUpdating( True ) + self.ca.glive.takePhoto() - def startTakingPhoto( self ): - self.setUpdating( True ) - self.ca.glive.takePhoto() + def savePhoto( self, pixbuf ): + recd = self.createNewRecorded( Constants.TYPE_PHOTO ) - def savePhoto( self, pixbuf ): - recd = self.createNewRecorded( Constants.TYPE_PHOTO ) + imgpath = os.path.join(Instance.instancePath, recd.mediaFilename) + pixbuf.save( imgpath, "jpeg" ) - imgpath = os.path.join(Instance.instancePath, recd.mediaFilename) - pixbuf.save( imgpath, "jpeg" ) + thumbpath = os.path.join(Instance.instancePath, recd.thumbFilename) + scale = float((UI.dim_THUMB_WIDTH+0.0)/(pixbuf.get_width()+0.0)) + thumbImg = utils.generateThumbnail(pixbuf, scale, UI.dim_THUMB_WIDTH, UI.dim_THUMB_HEIGHT) + thumbImg.write_to_png(thumbpath) + gc.collect() + #now that we've saved both the image and its pixbuf, we get their md5s + self.createNewRecordedMd5Sums( recd ) - thumbpath = os.path.join(Instance.instancePath, recd.thumbFilename) - scale = float((UI.dim_THUMB_WIDTH+0.0)/(pixbuf.get_width()+0.0)) - thumbImg = utils.generateThumbnail(pixbuf, scale, UI.dim_THUMB_WIDTH, UI.dim_THUMB_HEIGHT) - thumbImg.write_to_png(thumbpath) - gc.collect() - #now that we've saved both the image and its pixbuf, we get their md5s - self.createNewRecordedMd5Sums( recd ) + photoHash = self.mediaHashs[Constants.TYPE_PHOTO] + photoHash.append( recd ) + gobject.idle_add(self.displayThumb, recd, True) - photoHash = self.mediaHashs[Constants.TYPE_PHOTO] - photoHash.append( recd ) - gobject.idle_add(self.displayThumb, recd, True) + self.meshShareRecd( recd ) - self.meshShareRecd( recd ) + def addMeshRecd( self, recd ): + #todo: sort on time-taken, not on their arrival time over the mesh (?) + self.mediaHashs[recd.type].append( recd ) - def addMeshRecd( self, recd ): - #todo: sort on time-taken, not on their arrival time over the mesh (?) - self.mediaHashs[recd.type].append( recd ) + #updateUi, but don't lock up the buttons if they're recording or whatever + gobject.idle_add(self.displayThumb, recd, False) - #updateUi, but don't lock up the buttons if they're recording or whatever - gobject.idle_add(self.displayThumb, recd, False) + def createNewRecorded( self, type ): + recd = Recorded( ) - def createNewRecorded( self, type ): - recd = Recorded( ) + recd.recorderName = Instance.nickName + recd.recorderHash = Instance.keyHashPrintable - recd.recorderName = Instance.nickName - recd.recorderHash = Instance.keyHashPrintable + #to create a file, use the hardware_id+time *and* check if available or not + nowtime = int(time.time()) + recd.time = nowtime + recd.type = type - #to create a file, use the hardware_id+time *and* check if available or not - nowtime = int(time.time()) - recd.time = nowtime - recd.type = type + mediaThumbFilename = str(recd.recorderHash) + "_" + str(recd.time) + mediaFilename = mediaThumbFilename + mediaFilename = mediaFilename + "." + Constants.mediaTypes[type][Constants.keyExt] + mediaFilepath = os.path.join( Instance.instancePath, mediaFilename ) + mediaFilepath = utils.getUniqueFilepath( mediaFilepath, 0 ) + recd.mediaFilename = os.path.basename( mediaFilepath ) - mediaThumbFilename = str(recd.recorderHash) + "_" + str(recd.time) - mediaFilename = mediaThumbFilename - mediaFilename = mediaFilename + "." + Constants.mediaTypes[type][Constants.keyExt] - mediaFilepath = os.path.join( Instance.instancePath, mediaFilename ) - mediaFilepath = utils.getUniqueFilepath( mediaFilepath, 0 ) - recd.mediaFilename = os.path.basename( mediaFilepath ) + thumbFilename = mediaThumbFilename + "_thumb.jpg" + thumbFilepath = os.path.join( Instance.instancePath, thumbFilename ) + thumbFilepath = utils.getUniqueFilepath( thumbFilepath, 0 ) + recd.thumbFilename = os.path.basename( thumbFilepath ) - thumbFilename = mediaThumbFilename + "_thumb.jpg" - thumbFilepath = os.path.join( Instance.instancePath, thumbFilename ) - thumbFilepath = utils.getUniqueFilepath( thumbFilepath, 0 ) - recd.thumbFilename = os.path.basename( thumbFilepath ) + stringType = Constants.mediaTypes[type][Constants.keyIstr] + recd.title = Constants.istrBy % {"1":stringType, "2":str(recd.recorderName)} - stringType = Constants.mediaTypes[type][Constants.keyIstr] - recd.title = Constants.istrBy % {"1":stringType, "2":str(recd.recorderName)} + recd.colorStroke = Instance.colorStroke + recd.colorFill = Instance.colorFill - recd.colorStroke = Instance.colorStroke - recd.colorFill = Instance.colorFill + logger.debug('createNewRecorded: ' + str(recd) + ", thumbFilename:" + str(recd.thumbFilename)) + return recd - record.Record.log.debug('createNewRecorded: ' + str(recd) + ", thumbFilename:" + str(recd.thumbFilename)) - return recd + def createNewRecordedMd5Sums( self, recd ): + recd.thumbMd5 = recd.mediaMd5 = uuid.uuid4() - def createNewRecordedMd5Sums( self, recd ): - #load the thumbfile - thumbFile = os.path.join(Instance.instancePath, recd.thumbFilename) - thumbMd5 = utils.md5File( thumbFile ) - recd.thumbMd5 = thumbMd5 - tBytes = os.stat(thumbFile)[6] - recd.thumbBytes = tBytes + #load the thumbfile + thumbFile = os.path.join(Instance.instancePath, recd.thumbFilename) + tBytes = os.stat(thumbFile)[6] + recd.thumbBytes = tBytes - recd.tags = "" + recd.tags = "" - #load the mediafile - mediaFile = os.path.join(Instance.instancePath, recd.mediaFilename) - mediaMd5 = utils.md5File( mediaFile ) - recd.mediaMd5 = mediaMd5 - mBytes = os.stat(mediaFile)[6] - recd.mediaBytes = mBytes + #load the mediafile + mediaFile = os.path.join(Instance.instancePath, recd.mediaFilename) + mBytes = os.stat(mediaFile)[6] + recd.mediaBytes = mBytes - def deleteRecorded( self, recd ): - recd.deleted = True + def deleteRecorded( self, recd ): + recd.deleted = True - #clear the index - hash = self.mediaHashs[recd.type] - index = hash.index(recd) - hash.remove( recd ) + #clear the index + hash = self.mediaHashs[recd.type] + index = hash.index(recd) + hash.remove( recd ) - if (not recd.meshUploading): - self.doDeleteRecorded( recd ) + if (not recd.meshUploading): + self.doDeleteRecorded( recd ) - def doDeleteRecorded( self, recd ): - #remove files from the filesystem if not on the datastore - if (recd.datastoreId == None): - mediaFile = recd.getMediaFilepath() - if (os.path.exists(mediaFile)): - os.remove(mediaFile) + def doDeleteRecorded( self, recd ): + #remove files from the filesystem if not on the datastore + if (recd.datastoreId == None): + mediaFile = recd.getMediaFilepath() + if (os.path.exists(mediaFile)): + os.remove(mediaFile) - thumbFile = recd.getThumbFilepath( ) - if (os.path.exists(thumbFile)): - os.remove(thumbFile) - else: - #remove from the datastore here, since once gone, it is gone... - serialize.removeMediaFromDatastore( recd ) + thumbFile = recd.getThumbFilepath( ) + if (os.path.exists(thumbFile)): + os.remove(thumbFile) + else: + #remove from the datastore here, since once gone, it is gone... + serialize.removeMediaFromDatastore( recd ) - def doVideoMode( self ): - if (self.MODE == Constants.MODE_VIDEO): - return + def doVideoMode( self ): + if (self.MODE == Constants.MODE_VIDEO): + return - self.MODE = Constants.MODE_VIDEO - self.setUpdating(True) - gobject.idle_add( self.setupMode, self.MODE, True ) + self.MODE = Constants.MODE_VIDEO + self.setUpdating(True) + gobject.idle_add( self.setupMode, self.MODE, True ) - def doPhotoMode( self ): - if (self.MODE == Constants.MODE_PHOTO): - return + def doPhotoMode( self ): + if (self.MODE == Constants.MODE_PHOTO): + return - self.MODE = Constants.MODE_PHOTO - self.setUpdating(True) - gobject.idle_add( self.setupMode, self.MODE, True ) + self.MODE = Constants.MODE_PHOTO + self.setUpdating(True) + gobject.idle_add( self.setupMode, self.MODE, True ) - def doAudioMode( self ): - if (self.MODE == Constants.MODE_AUDIO): - return + def doAudioMode( self ): + if (self.MODE == Constants.MODE_AUDIO): + return - self.MODE = Constants.MODE_AUDIO - self.setUpdating(True) - gobject.idle_add( self.setupMode, self.MODE, True ) + self.MODE = Constants.MODE_AUDIO + self.setUpdating(True) + gobject.idle_add( self.setupMode, self.MODE, True ) @@ -27,164 +27,164 @@ import math #Sometimes a gtk.Image is a useful alternative to a drawing area. You can put a gtk.gdk.Pixmap in the gtk.Image and draw to the gtk.gdk.Pixmap, calling the gtk.Widget.queue_draw() method on the gtk.Image when you want to refresh to the screen. class P5(gtk.DrawingArea): - def __init__(self): - super(P5, self).__init__() + def __init__(self): + super(P5, self).__init__() - # gtk.Widget signals - self.connect("expose_event", self.expose) - self.connect("button_press_event", self.button_press) - self.connect("button_release_event", self.button_release) - self.connect("motion_notify_event", self.motion_notify) + # gtk.Widget signals + self.connect("expose_event", self.expose) + self.connect("button_press_event", self.button_press) + self.connect("button_release_event", self.button_release) + self.connect("motion_notify_event", self.motion_notify) - # ok, we have to listen to mouse events here too - self.add_events(gdk.BUTTON_PRESS_MASK | gdk.BUTTON_RELEASE_MASK | gdk.POINTER_MOTION_MASK) - self._dragging = False - self._mouseX = 0 - self._mouseY = 0 + # ok, we have to listen to mouse events here too + self.add_events(gdk.BUTTON_PRESS_MASK | gdk.BUTTON_RELEASE_MASK | gdk.POINTER_MOTION_MASK) + self._dragging = False + self._mouseX = 0 + self._mouseY = 0 - self._w = -1 - self._h = -1 + self._w = -1 + self._h = -1 - #ok, this calls an initial painting & setting of painterly variables - #e.g. time for the clock widget - #(but not through to redraw_canvas when called here first time) - self._msecUpdate = 100 - self._looping = False - self.noloop() + #ok, this calls an initial painting & setting of painterly variables + #e.g. time for the clock widget + #(but not through to redraw_canvas when called here first time) + self._msecUpdate = 100 + self._looping = False + self.noloop() - def loop(self): - if (self._looping): - return - else: - self._looping = True - # this is our maybe-threaded refresh (in millisecs) - gobject.timeout_add( self._msecUpdate, self.update ) + def loop(self): + if (self._looping): + return + else: + self._looping = True + # this is our maybe-threaded refresh (in millisecs) + gobject.timeout_add( self._msecUpdate, self.update ) - def noloop(self): - self._looping = False - self.redraw() + def noloop(self): + self._looping = False + self.redraw() - def redraw(self): - self.update() + def redraw(self): + self.update() - def expose(self, widget, event): - ctx = widget.window.cairo_create() + def expose(self, widget, event): + ctx = widget.window.cairo_create() - # set a clip region for the expose event - ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) - ctx.clip() + # set a clip region for the expose event + ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) + ctx.clip() - rect = widget.allocation - #self.draw(ctx, event.area.width, event.area.height) - self.draw( ctx, rect.width, rect.height ) + rect = widget.allocation + #self.draw(ctx, event.area.width, event.area.height) + self.draw( ctx, rect.width, rect.height ) - def button_press(self, widget, event): - self._mouseX = event.x - self._mouseY = event.y - self._dragging = True + def button_press(self, widget, event): + self._mouseX = event.x + self._mouseY = event.y + self._dragging = True - def button_release(self, widget, event): - if self._dragging: - self._dragging = False + def button_release(self, widget, event): + if self._dragging: + self._dragging = False - def motion_notify(self, widget, event): - self._mouseX = event.x - self._mouseY = event.y + def motion_notify(self, widget, event): + self._mouseX = event.x + self._mouseY = event.y - def draw(self, ctx, w, h): - ctx.set_antialias( cairo.ANTIALIAS_NONE ) - ctx.set_line_width( 1 ) - ctx.identity_matrix( ) - if ((w != self._w) or (h != self._h)): - self._w = w - self._h = h - self.doResize( ) + def draw(self, ctx, w, h): + ctx.set_antialias( cairo.ANTIALIAS_NONE ) + ctx.set_line_width( 1 ) + ctx.identity_matrix( ) + if ((w != self._w) or (h != self._h)): + self._w = w + self._h = h + self.doResize( ) - def doResize(self): - pass + def doResize(self): + pass - #called from update - def redraw_canvas(self): - if self.window: - alloc = self.get_allocation() - #this is odd behavior, but once we add this widget to a parent (vbox) - #it requires setting the q_d_a x,y to 0, 0 - #self.queue_draw_area(alloc.x, alloc.y, alloc.width, alloc.height) - self.queue_draw_area(0, 0, alloc.width, alloc.height) - self.window.process_updates(True) + #called from update + def redraw_canvas(self): + if self.window: + alloc = self.get_allocation() + #this is odd behavior, but once we add this widget to a parent (vbox) + #it requires setting the q_d_a x,y to 0, 0 + #self.queue_draw_area(alloc.x, alloc.y, alloc.width, alloc.height) + self.queue_draw_area(0, 0, alloc.width, alloc.height) + self.window.process_updates(True) - def update(self): - #paint thread -- call redraw_canvas, which calls expose - self.redraw_canvas() - if (self._looping): - return True # keep running this event - else: - return False + def update(self): + #paint thread -- call redraw_canvas, which calls expose + self.redraw_canvas() + if (self._looping): + return True # keep running this event + else: + return False - def drawShape( self, ctx, poly, col ): - self.setColor( ctx, col ) + def drawShape( self, ctx, poly, col ): + self.setColor( ctx, col ) - for i in range ( 0, len(poly._xs) ): - ctx.line_to ( poly._xs[i], poly._ys[i] ) - ctx.close_path() - ctx.set_line_width(1) - ctx.stroke() + for i in range ( 0, len(poly._xs) ): + ctx.line_to ( poly._xs[i], poly._ys[i] ) + ctx.close_path() + ctx.set_line_width(1) + ctx.stroke() - def fillShape( self, ctx, poly, col ): - self.setColor( ctx, col ) - for i in range ( 0, len(poly._xs) ): - ctx.line_to (poly._xs[i], poly._ys[i]) - ctx.close_path() + def fillShape( self, ctx, poly, col ): + self.setColor( ctx, col ) + for i in range ( 0, len(poly._xs) ): + ctx.line_to (poly._xs[i], poly._ys[i]) + ctx.close_path() - ctx.fill() + ctx.fill() - def background( self, ctx, col, w, h ): - self.setColor( ctx, col ) + def background( self, ctx, col, w, h ): + self.setColor( ctx, col ) - ctx.line_to(0, 0) - ctx.line_to(w, 0) - ctx.line_to(w, h) - ctx.line_to(0, h) - ctx.close_path() + ctx.line_to(0, 0) + ctx.line_to(w, 0) + ctx.line_to(w, h) + ctx.line_to(0, h) + ctx.close_path() - ctx.fill() + ctx.fill() - def rect( self, ctx, x, y, w, h ): - ctx.line_to(x, y) - ctx.line_to(x+w, y) - ctx.line_to(x+w, y+h) - ctx.line_to(x, y+h) - ctx.close_path() + def rect( self, ctx, x, y, w, h ): + ctx.line_to(x, y) + ctx.line_to(x+w, y) + ctx.line_to(x+w, y+h) + ctx.line_to(x, y+h) + ctx.close_path() - def setColor( self, ctx, col ): - if (not col._opaque): - ctx.set_source_rgba( col._r, col._g, col._b, col._a ) - else: - ctx.set_source_rgb( col._r, col._g, col._b ) + def setColor( self, ctx, col ): + if (not col._opaque): + ctx.set_source_rgba( col._r, col._g, col._b, col._a ) + else: + ctx.set_source_rgb( col._r, col._g, col._b ) - def line( self, ctx, x1, y1, x2, y2 ): - ctx.move_to (x1, y1) - ctx.line_to (x2, y2) - ctx.stroke() + def line( self, ctx, x1, y1, x2, y2 ): + ctx.move_to (x1, y1) + ctx.line_to (x2, y2) + ctx.stroke() - def point( self, ctx, x1, y1 ): - self.line( ctx, x1, y1, x1+1, y1 )
\ No newline at end of file + def point( self, ctx, x1, y1 ): + self.line( ctx, x1, y1, x1+1, y1 )
\ No newline at end of file diff --git a/p5_button.py b/p5_button.py index 0461b3c..cf76a34 100644 --- a/p5_button.py +++ b/p5_button.py @@ -22,152 +22,152 @@ from p5 import P5 class P5Button(P5): - def __init__(self): - P5.__init__(self) - self.noloop() - self._butts = [] - self._buttonPressed = False + def __init__(self): + P5.__init__(self) + self.noloop() + self._butts = [] + self._buttonPressed = False - def button_press(self, widget, event): - P5.button_press(self, widget, event) + def button_press(self, widget, event): + P5.button_press(self, widget, event) - #iterate through the buttons to see if you've pressed any down - bp = False - for i in range ( 0, len(self._butts) ): - if (self._butts[i]._enabled): - contains = self._butts[i].contains(event.x, event.y) - self._butts[i]._pressed = contains - if (contains): - bp = True + #iterate through the buttons to see if you've pressed any down + bp = False + for i in range ( 0, len(self._butts) ): + if (self._butts[i]._enabled): + contains = self._butts[i].contains(event.x, event.y) + self._butts[i]._pressed = contains + if (contains): + bp = True - self._buttonPressed = bp - self.redraw() + self._buttonPressed = bp + self.redraw() - def button_release(self, widget, event): - P5.button_release(self, widget, event) - self._buttonPressed = False + def button_release(self, widget, event): + P5.button_release(self, widget, event) + self._buttonPressed = False - pressed = [] - #iterate through the buttons to see if you've released on any - for i in range ( 0, len(self._butts) ): - if (self._butts[i]._enabled): - if (self._butts[i]._pressed): - if (self._butts[i].contains(event.x, event.y)): - pressed.append( self._butts[i] ) + pressed = [] + #iterate through the buttons to see if you've released on any + for i in range ( 0, len(self._butts) ): + if (self._butts[i]._enabled): + if (self._butts[i]._pressed): + if (self._butts[i].contains(event.x, event.y)): + pressed.append( self._butts[i] ) - if (self._butts[i]._toggle): - self._butts[i]._pressed = not self._butts[i]._pressed - else: - self._butts[i]._pressed = False + if (self._butts[i]._toggle): + self._butts[i]._pressed = not self._butts[i]._pressed + else: + self._butts[i]._pressed = False - for i in range( 0, len(pressed) ): - pressed[i].doPressed() + for i in range( 0, len(pressed) ): + pressed[i].doPressed() - self.redraw() + self.redraw() class Polygon: - def __init__( self, xs, ys ): - self.setPoints( xs, ys ) + def __init__( self, xs, ys ): + self.setPoints( xs, ys ) - def setPoints( self, xs, ys ): - self._xs = xs - self._ys = ys + def setPoints( self, xs, ys ): + self._xs = xs + self._ys = ys - self._boundingX = self._xs[0] - self._boundingY = self._ys[0] - self._boundingW = self._xs[0] - self._boundingH = self._ys[0] + self._boundingX = self._xs[0] + self._boundingY = self._ys[0] + self._boundingW = self._xs[0] + self._boundingH = self._ys[0] - for i in range ( 1, len(self._xs) ): - if (self._xs[i] > self._boundingW): - self._boundingW = self._xs[i] - if (self._ys[i] > self._boundingH): - self._boundingH = self._ys[i] - if (self._xs[i] < self._boundingX): - self._boundingX = self._xs[i] - if (self._ys[i] < self._boundingY): - self._boundingY = self._ys[i] + for i in range ( 1, len(self._xs) ): + if (self._xs[i] > self._boundingW): + self._boundingW = self._xs[i] + if (self._ys[i] > self._boundingH): + self._boundingH = self._ys[i] + if (self._xs[i] < self._boundingX): + self._boundingX = self._xs[i] + if (self._ys[i] < self._boundingY): + self._boundingY = self._ys[i] - def contains( self, mx, my ): - if (not self.bbox_contains(mx, my)): - return False + def contains( self, mx, my ): + if (not self.bbox_contains(mx, my)): + return False - #insert simple path tracing check on the polygon here + #insert simple path tracing check on the polygon here - return True + return True - def bbox_contains( self, mx, my ): - if ( not((mx>=self._boundingX) and (my>=self._boundingY) and (mx<self._boundingW) and (my<self._boundingH)) ): - return False - else: - return True + def bbox_contains( self, mx, my ): + if ( not((mx>=self._boundingX) and (my>=self._boundingY) and (mx<self._boundingW) and (my<self._boundingH)) ): + return False + else: + return True class Button: - def __init__(self, poly, offX, offY): - self._poly = poly - self._offX = offX - self._offY = offY + def __init__(self, poly, offX, offY): + self._poly = poly + self._offX = offX + self._offY = offY - self._enabled = True - self._pressed = False - self._toggle = False + self._enabled = True + self._pressed = False + self._toggle = False - self._listeners = [] + self._listeners = [] - self._actionCommand = None + self._actionCommand = None - self._img = None + self._img = None - def setOffsets(self, offs): - self._offX = offs[0] - self._offY = offs[1] + def setOffsets(self, offs): + self._offX = offs[0] + self._offY = offs[1] - def addActionListener(self, listen): - self._listeners.append(listen) + def addActionListener(self, listen): + self._listeners.append(listen) - def removeActionListener(self, listen): - try: - self._listeners.remove(listen) - except ValueError: - pass + def removeActionListener(self, listen): + try: + self._listeners.remove(listen) + except ValueError: + pass - def setActionCommand(self, command): - self._actionCommand = command + def setActionCommand(self, command): + self._actionCommand = command - def getActionCommand(self): - return self._actionCommand + def getActionCommand(self): + return self._actionCommand - def setImage(self, img): - self._img = img + def setImage(self, img): + self._img = img - def contains( self, mx, my ): - x = mx - self._offX - y = my - self._offY + def contains( self, mx, my ): + x = mx - self._offX + y = my - self._offY - contains = self._poly.contains( x, y ) - return contains + contains = self._poly.contains( x, y ) + return contains - def doPressed( self ): - for i in range ( 0, len(self._listeners) ): - self._listeners[i].fireButton( self._actionCommand ) + def doPressed( self ): + for i in range ( 0, len(self._listeners) ): + self._listeners[i].fireButton( self._actionCommand ) - def isImg( self ): - return self._img != None
\ No newline at end of file + def isImg( self ): + return self._img != None
\ No newline at end of file diff --git a/po/zh_TW.po b/po/zh_TW.po index 57aa2d0..54b45fe 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -343,4 +343,4 @@ msgstr "容量" #~ msgstr "低品質" #~ msgid "Your disk is full" -#~ msgstr " +#~ msgstr "" diff --git a/port/AUTHORS b/port/AUTHORS new file mode 100644 index 0000000..47ead6c --- /dev/null +++ b/port/AUTHORS @@ -0,0 +1 @@ +Aleksey Lim <alsroot@member.fsf.org> diff --git a/port/COPYING b/port/COPYING new file mode 100644 index 0000000..623b625 --- /dev/null +++ b/port/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/port/NEWS b/port/NEWS new file mode 100644 index 0000000..0dcfeb5 --- /dev/null +++ b/port/NEWS @@ -0,0 +1,8 @@ +1 + +* Add tarball.py +* Add json import wrapper +* Add object chooser +* Add activity classes +* Add pixbuf methods +* Add TempoSlider and ScrolledBox widgets diff --git a/port/README b/port/README new file mode 100644 index 0000000..bd0dade --- /dev/null +++ b/port/README @@ -0,0 +1,13 @@ +About +----- + +A set of sugar components/libraries/etc to simplify writing activities. + +Cornerstone purposes for this project: +* Total backwards compatibility for sugar-port API +* Run on all sugar platforms beginning from 0.82 + +Get it +------ + +http://wiki.sugarlabs.org/go/Development_Team/sugar-port diff --git a/port/TODO b/port/TODO new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/port/TODO diff --git a/port/__init__.py b/port/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/port/__init__.py diff --git a/port/json.py b/port/json.py new file mode 100644 index 0000000..d464abb --- /dev/null +++ b/port/json.py @@ -0,0 +1,33 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" +Unify usage of simplejson in Python 2.5/2.6 + +In Python 2.5 it imports simplejson module, in 2.6 native json module. + +Usage: + + import port.json as json + + # and using regular simplejson interface with module json + json.dumps([]) + +""" + +try: + from json import * + dumps +except (ImportError, NameError): + from simplejson import * @@ -27,17 +27,21 @@ import telepathy.client import logging import xml.dom.minidom import time +from xml.dom.minidom import parse + +import logging +logger = logging.getLogger('record:record.py') from sugar.activity import activity from sugar.presence import presenceservice from sugar.presence.tubeconn import TubeConnection from sugar import util +import port.json from model import Model from ui import UI from recordtube import RecordTube from glive import Glive -from glivex import GliveX from gplay import Gplay from greplay import Greplay from recorded import Recorded @@ -49,472 +53,486 @@ import utils class Record(activity.Activity): - log = logging.getLogger('record-activity') + log = logging.getLogger('record-activity') + + def __init__(self, handle): + activity.Activity.__init__(self, handle) + #flags for controlling the writing to the datastore + self.I_AM_CLOSING = False + self.I_AM_SAVED = False + + self.props.enable_fullscreen_mode = False + Instance(self) + Constants(self) + self.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + + #wait a moment so that our debug console capture mistakes + gobject.idle_add( self._initme, None ) + + + def _initme( self, userdata=None ): + #totally tubular + self.meshTimeoutTime = 10000 + self.recTube = None + self.connect( "shared", self._sharedCb ) + + #the main classes + self.m = Model(self) + self.glive = Glive(self) + self.gplay = Gplay(self) + self.ui = UI(self) + + #CSCL + if self._shared_activity: + #have you joined or shared this activity yourself? + if self.get_shared(): + self._meshJoinedCb( self ) + else: + self.connect("joined", self._meshJoinedCb) + + return False + + + def read_file(self, file): + try: + dom = parse(file) + except Exception, e: + logger.error('read_file: %s' % e) + return + + serialize.fillMediaHash(dom, self.m.mediaHashs) + + for i in dom.documentElement.getElementsByTagName('ui'): + for ui_el in i.childNodes: + self.ui.deserialize(port.json.loads(ui_el.data)) + + + def write_file(self, file): + self.I_AM_SAVED = False + + self.m.mediaHashs['ui'] = self.ui.serialize() - def __init__(self, handle): - activity.Activity.__init__(self, handle) - #flags for controlling the writing to the datastore - self.I_AM_CLOSING = False - self.I_AM_SAVED = False + dom = serialize.saveMediaHash(self.m.mediaHashs) - self.props.enable_fullscreen_mode = False - Instance(self) - Constants(self) - self.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + ui_data = port.json.dumps(self.ui.serialize()) + ui_el = dom.createElement('ui') + ui_el.appendChild(dom.createTextNode(ui_data)) + dom.documentElement.appendChild(ui_el) - #wait a moment so that our debug console capture mistakes - gobject.idle_add( self._initme, None ) + xmlFile = open( file, "w" ) + dom.writexml(xmlFile) + xmlFile.close() + allDone = True + for h in range (0, len(self.m.mediaHashs)-1): + mhash = self.m.mediaHashs[h] + for i in range (0, len(mhash)): + recd = mhash[i] - def _initme( self, userdata=None ): - #totally tubular - self.meshTimeoutTime = 10000 - self.recTube = None - self.connect( "shared", self._sharedCb ) + if ( (not recd.savedMedia) or (not recd.savedXml) ): + allDone = False - #the main classes - self.m = Model(self) - self.glive = Glive(self) - self.glivex = GliveX(self) - self.gplay = Gplay() - self.ui = UI(self) + if (self.I_AM_CLOSING): + mediaObject = recd.datastoreOb + if (mediaObject != None): + recd.datastoreOb = None + mediaObject.destroy() + del mediaObject - #CSCL - if self._shared_activity: - #have you joined or shared this activity yourself? - if self.get_shared(): - self._meshJoinedCb( self ) - else: - self.connect("joined", self._meshJoinedCb) + self.I_AM_SAVED = True + if (self.I_AM_SAVED and self.I_AM_CLOSING): + self.destroy() - return False + def stopPipes(self): + self.ui.doMouseListener( False ) + self.m.setUpdating( False ) - def read_file(self, file): - serialize.fillMediaHash(file, self.m.mediaHashs) + if (self.ui.COUNTINGDOWN): + self.m.abandonRecording() + elif (self.m.RECORDING): + self.m.doShutter() + else: + self.glive.stop() - def write_file(self, file): - self.I_AM_SAVED = False + def restartPipes(self): + if (not self.ui.TRANSCODING): + self.ui.updateModeChange( ) + self.ui.doMouseListener( True ) - dom = serialize.saveMediaHash(self.m.mediaHashs) - xmlFile = open( file, "w" ) - dom.writexml(xmlFile) - xmlFile.close() - allDone = True - for h in range (0, len(self.m.mediaHashs)): - mhash = self.m.mediaHashs[h] - for i in range (0, len(mhash)): - recd = mhash[i] + def close( self ): + self.I_AM_CLOSING = True - if ( (not recd.savedMedia) or (not recd.savedXml) ): - allDone = False + self.m.UPDATING = False + if (self.ui != None): + self.ui.updateButtonSensitivities( ) + self.ui.doMouseListener( False ) + self.ui.hideAllWindows() + if (self.gplay != None): + self.gplay.stop( ) + if (self.glive != None): + self.glive.stop( ) - if (self.I_AM_CLOSING): - mediaObject = recd.datastoreOb - if (mediaObject != None): - recd.datastoreOb = None - mediaObject.destroy() - del mediaObject + #this calls write_file + activity.Activity.close( self ) - self.I_AM_SAVED = True - if (self.I_AM_SAVED and self.I_AM_CLOSING): - self.destroy() + def destroy( self ): + if self.I_AM_SAVED: + activity.Activity.destroy( self ) - def stopPipes(self): - self.ui.doMouseListener( False ) - self.m.setUpdating( False ) - if (self.ui.COUNTINGDOWN): - self.m.abandonRecording() - elif (self.m.RECORDING): - self.m.doShutter() - else: - self.glive.stop() - self.glivex.stop() + def _sharedCb( self, activity ): + self._setup() + id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( Constants.SERVICE, {}) - def restartPipes(self): - if (not self.ui.TRANSCODING): - self.ui.updateModeChange( ) - self.ui.doMouseListener( True ) + def _meshJoinedCb( self, activity ): + if not self._shared_activity: + return + + self._setup() - def close( self ): - self.I_AM_CLOSING = True + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb) - self.m.UPDATING = False - if (self.ui != None): - self.ui.updateButtonSensitivities( ) - self.ui.doMouseListener( False ) - self.ui.hideAllWindows() - if (self.gplay != None): - self.gplay.stop( ) - if (self.glive != None): - self.glive.stop( ) - if (self.glivex != None): - self.glivex.stop( ) - #this calls write_file - activity.Activity.close( self ) - - - def destroy( self ): - if self.I_AM_SAVED: - activity.Activity.destroy( self ) - - - def _sharedCb( self, activity ): - self._setup() - - id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( Constants.SERVICE, {}) - - - def _meshJoinedCb( self, activity ): - if not self._shared_activity: - return - - self._setup() - - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb) - - - def _list_tubes_reply_cb(self, tubes): - for tube_info in tubes: - self._newTubeCb(*tube_info) - - - def _list_tubes_error_cb(self, e): - self.__class__.log.error('ListTubes() failed: %s', e) - - - def _setup(self): - #sets up the tubes... - if self._shared_activity is None: - self.__class__.log.error('_setup: Failed to share or join activity') - return - - pservice = presenceservice.get_instance() - try: - name, path = pservice.get_preferred_connection() - self.conn = telepathy.client.Connection(name, path) - except: - self.__class__.log.error('_setup: Failed to get_preferred_connection') - - # Work out what our room is called and whether we have Tubes already - bus_name, conn_path, channel_paths = self._shared_activity.get_channels() - room = None - tubes_chan = None - text_chan = None - for channel_path in channel_paths: - channel = telepathy.client.Channel(bus_name, channel_path) - htype, handle = channel.GetHandle() - if htype == telepathy.HANDLE_TYPE_ROOM: - self.__class__.log.debug('Found our room: it has handle#%d "%s"', handle, self.conn.InspectHandles(htype, [handle])[0]) - room = handle - ctype = channel.GetChannelType() - if ctype == telepathy.CHANNEL_TYPE_TUBES: - self.__class__.log.debug('Found our Tubes channel at %s', channel_path) - tubes_chan = channel - elif ctype == telepathy.CHANNEL_TYPE_TEXT: - self.__class__.log.debug('Found our Text channel at %s', channel_path) - text_chan = channel - - if room is None: - self.__class__.log.error("Presence service didn't create a room") - return - if text_chan is None: - self.__class__.log.error("Presence service didn't create a text channel") - return - - # Make sure we have a Tubes channel - PS doesn't yet provide one - if tubes_chan is None: - self.__class__.log.debug("Didn't find our Tubes channel, requesting one...") - tubes_chan = self.conn.request_channel(telepathy.CHANNEL_TYPE_TUBES, telepathy.HANDLE_TYPE_ROOM, room, True) - - self.tubes_chan = tubes_chan - self.text_chan = text_chan - - tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube', self._newTubeCb) - - - def _newTubeCb(self, id, initiator, type, service, params, state): - self.__class__.log.debug('New tube: ID=%d initator=%d type=%d service=%s params=%r state=%d', id, initiator, type, service, params, state) - if (type == telepathy.TUBE_TYPE_DBUS and service == Constants.SERVICE): - if state == telepathy.TUBE_STATE_LOCAL_PENDING: - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) - tube_conn = TubeConnection(self.conn, self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) - self.recTube = RecordTube(tube_conn) - self.recTube.connect("new-recd", self._newRecdCb) - self.recTube.connect("recd-request", self._recdRequestCb) - self.recTube.connect("recd-bits-arrived", self._recdBitsArrivedCb) - self.recTube.connect("recd-unavailable", self._recdUnavailableCb) - - - def _newRecdCb( self, objectThatSentTheSignal, recorder, xmlString ): - self.__class__.log.debug('_newRecdCb') - dom = None - try: - dom = xml.dom.minidom.parseString(xmlString) - except: - self.__class__.log.error('Unable to parse mesh xml') - if (dom == None): - return - - recd = Recorded() - recd = serialize.fillRecdFromNode(recd, dom.documentElement) - if (recd != None): - self.__class__.log.debug('_newRecdCb: adding new recd thumb') - recd.buddy = True - recd.downloadedFromBuddy = False - self.m.addMeshRecd( recd ) - else: - self.__class__.log.debug('_newRecdCb: recd is None. Unable to parse XML') - - - def requestMeshDownload( self, recd ): - if (recd.meshDownloading): - return True - - self.m.updateXoFullStatus() - if (self.m.FULL): - return True - - #this call will get the bits or request the bits if they're not available - if (recd.buddy and (not recd.downloadedFromBuddy)): - self.meshInitRoundRobin(recd) - return True - - else: - return False - - - def meshInitRoundRobin( self, recd ): - if (recd.meshDownloading): - self.__class__.log.debug("meshInitRoundRobin: we are in midst of downloading this file...") - return - - if (self.recTube == None): - gobject.idle_add(self.ui.updateMeshProgress, False, recd) - return - - #start with who took the photo - recd.triedMeshBuddies = [] - recd.triedMeshBuddies.append(Instance.keyHashPrintable) - self.meshReqRecFromBuddy( recd, recd.recorderHash, recd.recorderName ) - - - def meshNextRoundRobinBuddy( self, recd ): - self.__class__.log.debug('meshNextRoundRobinBuddy') - if (recd.meshReqCallbackId != 0): - gobject.source_remove(recd.meshReqCallbackId) - recd.meshReqCallbackId = 0 - - #delete any stub of a partially downloaded file - filepath = recd.getMediaFilepath() - if (filepath != None): - if (os.path.exists(filepath)): - os.remove( filepath ) - - goodBudObj = None - buds = self._shared_activity.get_joined_buddies() - for i in range (0, len(buds)): - nextBudObj = buds[i] - nextBud = util._sha_data(nextBudObj.props.key) - nextBud = util.printable_hash(nextBud) - if (recd.triedMeshBuddies.count(nextBud) > 0): - self.__class__.log.debug('mnrrb: weve already tried bud ' + str(nextBudObj.props.nick)) - else: - self.__class__.log.debug('mnrrb: ask next buddy: ' + str(nextBudObj.props.nick)) - goodBudObj = nextBudObj - break - - if (goodBudObj != None): - goodNick = goodBudObj.props.nick - goodBud = util._sha_data(goodBudObj.props.key) - goodBud = util.printable_hash(goodBud) - self.meshReqRecFromBuddy(recd, goodBud, goodNick) - else: - self.__class__.log.debug('weve tried all buddies here, and no one has this recd') - recd.meshDownloading = False - recd.triedMeshBuddies = [] - recd.triedMeshBuddies.append(Instance.keyHashPrintable) - self.ui.updateMeshProgress(False, recd) - - - def meshReqRecFromBuddy( self, recd, fromWho, fromWhosNick ): - recd.triedMeshBuddies.append( fromWho ) - recd.meshDownloadingFrom = fromWho - recd.meshDownloadingFromNick = fromWhosNick - recd.meshDownloadingProgress = False - recd.meshDownloading = True - recd.meshDownlodingPercent = 0.0 - self.ui.updateMeshProgress(True, recd) - recd.meshReqCallbackId = gobject.timeout_add(self.meshTimeoutTime, self._meshCheckOnRecdRequest, recd) - self.recTube.requestRecdBits( Instance.keyHashPrintable, fromWho, recd.mediaMd5 ) - - - def _meshCheckOnRecdRequest( self, recdRequesting ): - #todo: add category for "not active activity, so go ahead and delete" - - if (recdRequesting.downloadedFromBuddy): - self.__class__.log.debug('_meshCheckOnRecdRequest: recdRequesting.downloadedFromBuddy') - if (recdRequesting.meshReqCallbackId != 0): - gobject.source_remove(recdRequesting.meshReqCallbackId) - recdRequesting.meshReqCallbackId = 0 - return False - if (recdRequesting.deleted): - self.__class__.log.debug('_meshCheckOnRecdRequest: recdRequesting.deleted') - if (recdRequesting.meshReqCallbackId != 0): - gobject.source_remove(recdRequesting.meshReqCallbackId) - recdRequesting.meshReqCallbackId = 0 - return False - if (recdRequesting.meshDownloadingProgress): - self.__class__.log.debug('_meshCheckOnRecdRequest: recdRequesting.meshDownloadingProgress') - #we've received some bits since last we checked, so keep waiting... they'll all get here eventually! - recdRequesting.meshDownloadingProgress = False - return True - else: - self.__class__.log.debug('_meshCheckOnRecdRequest: ! recdRequesting.meshDownloadingProgress') - #that buddy we asked info from isn't responding; next buddy! - #self.meshNextRoundRobinBuddy( recdRequesting ) - gobject.idle_add(self.meshNextRoundRobinBuddy, recdRequesting) - return False - - - def _recdRequestCb( self, objectThatSentTheSignal, whoWantsIt, md5sumOfIt ): - #if we are here, it is because someone has been told we have what they want. - #we need to send them that thing, whatever that thing is - recd = self.m.getRecdByMd5( md5sumOfIt ) - if (recd == None): - self.__class__.log.debug('_recdRequestCb: we dont have the recd they asked for') - self.recTube.unavailableRecd(md5sumOfIt, Instance.keyHashPrintable, whoWantsIt) - return - if (recd.deleted): - self.__class__.log.debug('_recdRequestCb: we have the recd, but it has been deleted, so we wont share') - self.recTube.unavailableRecd(md5sumOfIt, Instance.keyHashPrintable, whoWantsIt) - return - if (recd.buddy and not recd.downloadedFromBuddy): - self.__class__.log.debug('_recdRequestCb: we have an incomplete recd, so we wont share') - self.recTube.unavailableRecd(md5sumOfIt, Instance.keyHashPrintable, whoWantsIt) - return - - recd.meshUploading = True - filepath = recd.getMediaFilepath() - - if (recd.type == Constants.TYPE_AUDIO): - audioImgFilepath = recd.getAudioImageFilepath() - - destPath = os.path.join(Instance.instancePath, "audioBundle") - destPath = utils.getUniqueFilepath(destPath, 0) - cmd = "cat " + str(filepath) + " " + str(audioImgFilepath) + " > " + str(destPath) - self.__class__.log.debug(cmd) - os.system(cmd) - filepath = destPath - - sent = self.recTube.broadcastRecd(recd.mediaMd5, filepath, whoWantsIt) - recd.meshUploading = False - #if you were deleted while uploading, now throw away those bits now - if (recd.deleted): - recd.doDeleteRecorded(recd) - - - def _recdBitsArrivedCb( self, objectThatSentTheSignal, md5sumOfIt, part, numparts, bytes, fromWho ): - #self.__class__.log.debug('_recdBitsArrivedCb: ' + str(part) + "/" + str(numparts)) - recd = self.m.getRecdByMd5( md5sumOfIt ) - if (recd == None): - self.__class__.log.debug('_recdBitsArrivedCb: thx 4 yr bits, but we dont even have that photo') - return - if (recd.deleted): - self.__class__.log.debug('_recdBitsArrivedCb: thx 4 yr bits, but we deleted that photo') - return - if (recd.downloadedFromBuddy): - self.__class__.log.debug('_recdBitsArrivedCb: weve already downloadedFromBuddy') - return - if (not recd.buddy): - self.__class__.log.debug('_recdBitsArrivedCb: uh, we took this photo, so dont need your bits') - return - if (recd.meshDownloadingFrom != fromWho): - self.__class__.log.debug('_recdBitsArrivedCb: wrong bits ' + str(fromWho) + ", exp:" + str(recd.meshDownloadingFrom)) - return - - #update that we've heard back about this, reset the timeout - gobject.source_remove(recd.meshReqCallbackId) - recd.meshReqCallbackId = gobject.timeout_add(self.meshTimeoutTime, self._meshCheckOnRecdRequest, recd) - - #update the progress bar - recd.meshDownlodingPercent = (part+0.0)/(numparts+0.0) - recd.meshDownloadingProgress = True - self.ui.updateMeshProgress(True, recd) - f = open(recd.getMediaFilepath(), 'a+') - f.write(bytes) - f.close() - - if part == numparts: - self.__class__.log.debug('Finished receiving %s' % recd.title) - gobject.source_remove( recd.meshReqCallbackId ) - recd.meshReqCallbackId = 0 - recd.meshDownloading = False - recd.meshDownlodingPercent = 1.0 - recd.downloadedFromBuddy = True - if (recd.type == Constants.TYPE_AUDIO): - filepath = recd.getMediaFilepath() - bundlePath = os.path.join(Instance.instancePath, "audioBundle") - bundlePath = utils.getUniqueFilepath(bundlePath, 0) - - cmd = "split -a 1 -b " + str(recd.mediaBytes) + " " + str(filepath) + " " + str(bundlePath) - self.__class__.log.debug( cmd ) - os.system( cmd ) - - bundleName = os.path.basename(bundlePath) - mediaFilename = str(bundleName) + "a" - mediaFilepath = os.path.join(Instance.instancePath, mediaFilename) - mediaFilepathExt = os.path.join(Instance.instancePath, mediaFilename+".ogg") - os.rename(mediaFilepath, mediaFilepathExt) - audioImageFilename = str(bundleName) + "b" - audioImageFilepath = os.path.join(Instance.instancePath, audioImageFilename) - audioImageFilepathExt = os.path.join(Instance.instancePath, audioImageFilename+".png") - os.rename(audioImageFilepath, audioImageFilepathExt) - - recd.mediaFilename = os.path.basename(mediaFilepathExt) - recd.audioImageFilename = os.path.basename(audioImageFilepathExt) - - self.ui.showMeshRecd( recd ) - elif part > numparts: - self.__class__.log.error('More parts than required have arrived') - - - def _getAlbumArtCb( self, objectThatSentTheSignal, pixbuf, recd ): - - if (pixbuf != None): - imagePath = os.path.join(Instance.instancePath, "audioPicture.png") - imagePath = utils.getUniqueFilepath( imagePath, 0 ) - pixbuf.save( imagePath, "png", {} ) - recd.audioImageFilename = os.path.basename(imagePath) - - self.ui.showMeshRecd( recd ) - return False - - - def _recdUnavailableCb( self, objectThatSentTheSignal, md5sumOfIt, whoDoesntHaveIt ): - self.__class__.log.debug('_recdUnavailableCb: sux, we want to see that photo') - recd = self.m.getRecdByMd5( md5sumOfIt ) - if (recd == None): - self.__class__.log.debug('_recdUnavailableCb: actually, we dont even know about that one..') - return - if (recd.deleted): - self.__class__.log.debug('_recdUnavailableCb: actually, since we asked, we deleted.') - return - if (not recd.buddy): - self.__class__.log.debug('_recdUnavailableCb: uh, odd, we took that photo and have it already.') - return - if (recd.downloadedFromBuddy): - self.__class__.log.debug('_recdUnavailableCb: we already downloaded it... you might have been slow responding.') - return - if (recd.meshDownloadingFrom != whoDoesntHaveIt): - self.__class__.log.debug('_recdUnavailableCb: we arent asking you for a copy now. slow response, pbly.') - return - - #self.meshNextRoundRobinBuddy( recd ) + def _list_tubes_reply_cb(self, tubes): + for tube_info in tubes: + self._newTubeCb(*tube_info) + + + def _list_tubes_error_cb(self, e): + self.__class__.log.error('ListTubes() failed: %s', e) + + + def _setup(self): + #sets up the tubes... + if self._shared_activity is None: + self.__class__.log.error('_setup: Failed to share or join activity') + return + + pservice = presenceservice.get_instance() + try: + name, path = pservice.get_preferred_connection() + self.conn = telepathy.client.Connection(name, path) + except: + self.__class__.log.error('_setup: Failed to get_preferred_connection') + + # Work out what our room is called and whether we have Tubes already + bus_name, conn_path, channel_paths = self._shared_activity.get_channels() + room = None + tubes_chan = None + text_chan = None + for channel_path in channel_paths: + channel = telepathy.client.Channel(bus_name, channel_path) + htype, handle = channel.GetHandle() + if htype == telepathy.HANDLE_TYPE_ROOM: + self.__class__.log.debug('Found our room: it has handle#%d "%s"', handle, self.conn.InspectHandles(htype, [handle])[0]) + room = handle + ctype = channel.GetChannelType() + if ctype == telepathy.CHANNEL_TYPE_TUBES: + self.__class__.log.debug('Found our Tubes channel at %s', channel_path) + tubes_chan = channel + elif ctype == telepathy.CHANNEL_TYPE_TEXT: + self.__class__.log.debug('Found our Text channel at %s', channel_path) + text_chan = channel + + if room is None: + self.__class__.log.error("Presence service didn't create a room") + return + if text_chan is None: + self.__class__.log.error("Presence service didn't create a text channel") + return + + # Make sure we have a Tubes channel - PS doesn't yet provide one + if tubes_chan is None: + self.__class__.log.debug("Didn't find our Tubes channel, requesting one...") + tubes_chan = self.conn.request_channel(telepathy.CHANNEL_TYPE_TUBES, telepathy.HANDLE_TYPE_ROOM, room, True) + + self.tubes_chan = tubes_chan + self.text_chan = text_chan + + tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube', self._newTubeCb) + + + def _newTubeCb(self, id, initiator, type, service, params, state): + self.__class__.log.debug('New tube: ID=%d initator=%d type=%d service=%s params=%r state=%d', id, initiator, type, service, params, state) + if (type == telepathy.TUBE_TYPE_DBUS and service == Constants.SERVICE): + if state == telepathy.TUBE_STATE_LOCAL_PENDING: + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) + tube_conn = TubeConnection(self.conn, self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) + self.recTube = RecordTube(tube_conn) + self.recTube.connect("new-recd", self._newRecdCb) + self.recTube.connect("recd-request", self._recdRequestCb) + self.recTube.connect("recd-bits-arrived", self._recdBitsArrivedCb) + self.recTube.connect("recd-unavailable", self._recdUnavailableCb) + + + def _newRecdCb( self, objectThatSentTheSignal, recorder, xmlString ): + self.__class__.log.debug('_newRecdCb') + dom = None + try: + dom = xml.dom.minidom.parseString(xmlString) + except: + self.__class__.log.error('Unable to parse mesh xml') + if (dom == None): + return + + recd = Recorded() + recd = serialize.fillRecdFromNode(recd, dom.documentElement) + if (recd != None): + self.__class__.log.debug('_newRecdCb: adding new recd thumb') + recd.buddy = True + recd.downloadedFromBuddy = False + self.m.addMeshRecd( recd ) + else: + self.__class__.log.debug('_newRecdCb: recd is None. Unable to parse XML') + + + def requestMeshDownload( self, recd ): + if (recd.meshDownloading): + return True + + self.m.updateXoFullStatus() + if (self.m.FULL): + return True + + #this call will get the bits or request the bits if they're not available + if (recd.buddy and (not recd.downloadedFromBuddy)): + self.meshInitRoundRobin(recd) + return True + + else: + return False + + + def meshInitRoundRobin( self, recd ): + if (recd.meshDownloading): + self.__class__.log.debug("meshInitRoundRobin: we are in midst of downloading this file...") + return + + if (self.recTube == None): + gobject.idle_add(self.ui.updateMeshProgress, False, recd) + return + + #start with who took the photo + recd.triedMeshBuddies = [] + recd.triedMeshBuddies.append(Instance.keyHashPrintable) + self.meshReqRecFromBuddy( recd, recd.recorderHash, recd.recorderName ) + + + def meshNextRoundRobinBuddy( self, recd ): + self.__class__.log.debug('meshNextRoundRobinBuddy') + if (recd.meshReqCallbackId != 0): + gobject.source_remove(recd.meshReqCallbackId) + recd.meshReqCallbackId = 0 + + #delete any stub of a partially downloaded file + filepath = recd.getMediaFilepath() + if (filepath != None): + if (os.path.exists(filepath)): + os.remove( filepath ) + + goodBudObj = None + buds = self._shared_activity.get_joined_buddies() + for i in range (0, len(buds)): + nextBudObj = buds[i] + nextBud = util._sha_data(nextBudObj.props.key) + nextBud = util.printable_hash(nextBud) + if (recd.triedMeshBuddies.count(nextBud) > 0): + self.__class__.log.debug('mnrrb: weve already tried bud ' + str(nextBudObj.props.nick)) + else: + self.__class__.log.debug('mnrrb: ask next buddy: ' + str(nextBudObj.props.nick)) + goodBudObj = nextBudObj + break + + if (goodBudObj != None): + goodNick = goodBudObj.props.nick + goodBud = util._sha_data(goodBudObj.props.key) + goodBud = util.printable_hash(goodBud) + self.meshReqRecFromBuddy(recd, goodBud, goodNick) + else: + self.__class__.log.debug('weve tried all buddies here, and no one has this recd') + recd.meshDownloading = False + recd.triedMeshBuddies = [] + recd.triedMeshBuddies.append(Instance.keyHashPrintable) + self.ui.updateMeshProgress(False, recd) + + + def meshReqRecFromBuddy( self, recd, fromWho, fromWhosNick ): + recd.triedMeshBuddies.append( fromWho ) + recd.meshDownloadingFrom = fromWho + recd.meshDownloadingFromNick = fromWhosNick + recd.meshDownloadingProgress = False + recd.meshDownloading = True + recd.meshDownlodingPercent = 0.0 + self.ui.updateMeshProgress(True, recd) + recd.meshReqCallbackId = gobject.timeout_add(self.meshTimeoutTime, self._meshCheckOnRecdRequest, recd) + self.recTube.requestRecdBits( Instance.keyHashPrintable, fromWho, recd.mediaMd5 ) + + + def _meshCheckOnRecdRequest( self, recdRequesting ): + #todo: add category for "not active activity, so go ahead and delete" + + if (recdRequesting.downloadedFromBuddy): + self.__class__.log.debug('_meshCheckOnRecdRequest: recdRequesting.downloadedFromBuddy') + if (recdRequesting.meshReqCallbackId != 0): + gobject.source_remove(recdRequesting.meshReqCallbackId) + recdRequesting.meshReqCallbackId = 0 + return False + if (recdRequesting.deleted): + self.__class__.log.debug('_meshCheckOnRecdRequest: recdRequesting.deleted') + if (recdRequesting.meshReqCallbackId != 0): + gobject.source_remove(recdRequesting.meshReqCallbackId) + recdRequesting.meshReqCallbackId = 0 + return False + if (recdRequesting.meshDownloadingProgress): + self.__class__.log.debug('_meshCheckOnRecdRequest: recdRequesting.meshDownloadingProgress') + #we've received some bits since last we checked, so keep waiting... they'll all get here eventually! + recdRequesting.meshDownloadingProgress = False + return True + else: + self.__class__.log.debug('_meshCheckOnRecdRequest: ! recdRequesting.meshDownloadingProgress') + #that buddy we asked info from isn't responding; next buddy! + #self.meshNextRoundRobinBuddy( recdRequesting ) + gobject.idle_add(self.meshNextRoundRobinBuddy, recdRequesting) + return False + + + def _recdRequestCb( self, objectThatSentTheSignal, whoWantsIt, md5sumOfIt ): + #if we are here, it is because someone has been told we have what they want. + #we need to send them that thing, whatever that thing is + recd = self.m.getRecdByMd5( md5sumOfIt ) + if (recd == None): + self.__class__.log.debug('_recdRequestCb: we dont have the recd they asked for') + self.recTube.unavailableRecd(md5sumOfIt, Instance.keyHashPrintable, whoWantsIt) + return + if (recd.deleted): + self.__class__.log.debug('_recdRequestCb: we have the recd, but it has been deleted, so we wont share') + self.recTube.unavailableRecd(md5sumOfIt, Instance.keyHashPrintable, whoWantsIt) + return + if (recd.buddy and not recd.downloadedFromBuddy): + self.__class__.log.debug('_recdRequestCb: we have an incomplete recd, so we wont share') + self.recTube.unavailableRecd(md5sumOfIt, Instance.keyHashPrintable, whoWantsIt) + return + + recd.meshUploading = True + filepath = recd.getMediaFilepath() + + if (recd.type == Constants.TYPE_AUDIO): + audioImgFilepath = recd.getAudioImageFilepath() + + destPath = os.path.join(Instance.instancePath, "audioBundle") + destPath = utils.getUniqueFilepath(destPath, 0) + cmd = "cat " + str(filepath) + " " + str(audioImgFilepath) + " > " + str(destPath) + self.__class__.log.debug(cmd) + os.system(cmd) + filepath = destPath + + sent = self.recTube.broadcastRecd(recd.mediaMd5, filepath, whoWantsIt) + recd.meshUploading = False + #if you were deleted while uploading, now throw away those bits now + if (recd.deleted): + recd.doDeleteRecorded(recd) + + + def _recdBitsArrivedCb( self, objectThatSentTheSignal, md5sumOfIt, part, numparts, bytes, fromWho ): + #self.__class__.log.debug('_recdBitsArrivedCb: ' + str(part) + "/" + str(numparts)) + recd = self.m.getRecdByMd5( md5sumOfIt ) + if (recd == None): + self.__class__.log.debug('_recdBitsArrivedCb: thx 4 yr bits, but we dont even have that photo') + return + if (recd.deleted): + self.__class__.log.debug('_recdBitsArrivedCb: thx 4 yr bits, but we deleted that photo') + return + if (recd.downloadedFromBuddy): + self.__class__.log.debug('_recdBitsArrivedCb: weve already downloadedFromBuddy') + return + if (not recd.buddy): + self.__class__.log.debug('_recdBitsArrivedCb: uh, we took this photo, so dont need your bits') + return + if (recd.meshDownloadingFrom != fromWho): + self.__class__.log.debug('_recdBitsArrivedCb: wrong bits ' + str(fromWho) + ", exp:" + str(recd.meshDownloadingFrom)) + return + + #update that we've heard back about this, reset the timeout + gobject.source_remove(recd.meshReqCallbackId) + recd.meshReqCallbackId = gobject.timeout_add(self.meshTimeoutTime, self._meshCheckOnRecdRequest, recd) + + #update the progress bar + recd.meshDownlodingPercent = (part+0.0)/(numparts+0.0) + recd.meshDownloadingProgress = True + self.ui.updateMeshProgress(True, recd) + f = open(recd.getMediaFilepath(), 'a+') + f.write(bytes) + f.close() + + if part == numparts: + self.__class__.log.debug('Finished receiving %s' % recd.title) + gobject.source_remove( recd.meshReqCallbackId ) + recd.meshReqCallbackId = 0 + recd.meshDownloading = False + recd.meshDownlodingPercent = 1.0 + recd.downloadedFromBuddy = True + if (recd.type == Constants.TYPE_AUDIO): + filepath = recd.getMediaFilepath() + bundlePath = os.path.join(Instance.instancePath, "audioBundle") + bundlePath = utils.getUniqueFilepath(bundlePath, 0) + + cmd = "split -a 1 -b " + str(recd.mediaBytes) + " " + str(filepath) + " " + str(bundlePath) + self.__class__.log.debug( cmd ) + os.system( cmd ) + + bundleName = os.path.basename(bundlePath) + mediaFilename = str(bundleName) + "a" + mediaFilepath = os.path.join(Instance.instancePath, mediaFilename) + mediaFilepathExt = os.path.join(Instance.instancePath, mediaFilename+".ogg") + os.rename(mediaFilepath, mediaFilepathExt) + audioImageFilename = str(bundleName) + "b" + audioImageFilepath = os.path.join(Instance.instancePath, audioImageFilename) + audioImageFilepathExt = os.path.join(Instance.instancePath, audioImageFilename+".png") + os.rename(audioImageFilepath, audioImageFilepathExt) + + recd.mediaFilename = os.path.basename(mediaFilepathExt) + recd.audioImageFilename = os.path.basename(audioImageFilepathExt) + + self.ui.showMeshRecd( recd ) + elif part > numparts: + self.__class__.log.error('More parts than required have arrived') + + + def _getAlbumArtCb( self, objectThatSentTheSignal, pixbuf, recd ): + + if (pixbuf != None): + imagePath = os.path.join(Instance.instancePath, "audioPicture.png") + imagePath = utils.getUniqueFilepath( imagePath, 0 ) + pixbuf.save( imagePath, "png", {} ) + recd.audioImageFilename = os.path.basename(imagePath) + + self.ui.showMeshRecd( recd ) + return False + + + def _recdUnavailableCb( self, objectThatSentTheSignal, md5sumOfIt, whoDoesntHaveIt ): + self.__class__.log.debug('_recdUnavailableCb: sux, we want to see that photo') + recd = self.m.getRecdByMd5( md5sumOfIt ) + if (recd == None): + self.__class__.log.debug('_recdUnavailableCb: actually, we dont even know about that one..') + return + if (recd.deleted): + self.__class__.log.debug('_recdUnavailableCb: actually, since we asked, we deleted.') + return + if (not recd.buddy): + self.__class__.log.debug('_recdUnavailableCb: uh, odd, we took that photo and have it already.') + return + if (recd.downloadedFromBuddy): + self.__class__.log.debug('_recdUnavailableCb: we already downloaded it... you might have been slow responding.') + return + if (recd.meshDownloadingFrom != whoDoesntHaveIt): + self.__class__.log.debug('_recdUnavailableCb: we arent asking you for a copy now. slow response, pbly.') + return + + #self.meshNextRoundRobinBuddy( recd ) diff --git a/recorded.py b/recorded.py index 488a01c..8639a70 100644 --- a/recorded.py +++ b/recorded.py @@ -30,144 +30,144 @@ import record class Recorded: - def __init__( self ): - self.type = -1 - self.time = None - self.recorderName = None - self.recorderHash = None - self.title = None - self.colorStroke = None - self.colorFill = None - self.mediaMd5 = None - self.thumbMd5 = None - self.mediaBytes = None - self.thumbBytes = None - self.tags = None - - #flag to alert need to re-datastore the title - self.metaChange = False - - #when you are datastore-serialized, you get one of these ids... - self.datastoreId = None - self.datastoreOb = None - - #if not from the datastore, then your media is here... - self.mediaFilename = None - self.thumbFilename = None - self.audioImageFilename = None - - #for flagging when you are being saved to the datastore for the first time... - #and just because you have a datastore id, doesn't mean you're saved - self.savedMedia = False - self.savedXml = False - - #assume you took the picture - self.buddy = False - self.downloadedFromBuddy = False - self.triedMeshBuddies = [] - self.meshDownloading = False - self.meshDownloadingFrom = "" - self.meshDownloadingFromNick = "" - self.meshDownlodingPercent = 0.0 - self.meshDownloadingProgress = False - #if someone is downloading this, then hold onto it - self.meshUploading = False - self.meshReqCallbackId = 0 - - self.deleted = False - - - def setTitle( self, newTitle ): - self.title = newTitle - self.metaChange = True - - - def setTags( self, newTags ): - self.tags = newTags - self.metaChange = True - - - def isClipboardCopyable( self ): - copyme = True - if (self.buddy): - if (not self.downloadedFromBuddy): - return False - return copyme - - - #scenarios: - #launch, your new thumb -- Journal/session - #launch, your new media -- Journal/session - #launch, their new thumb -- Journal/session/buddy - #launch, their new media -- ([request->]) Journal/session/buddy - #relaunch, your old thumb -- metadataPixbuf on request (or save to Journal/session..?) - #relaunch, your old media -- datastoreObject->file (hold onto the datastore object, delete if deleted) - #relaunch, their old thumb -- metadataPixbuf on request (or save to Journal/session..?) - #relaunch, their old media -- datastoreObject->file (hold onto the datastore object, delete if deleted) | ([request->]) Journal/session/buddy - - def getThumbPixbuf( self ): - thumbPixbuf = None - thumbFilepath = self.getThumbFilepath() - if ( os.path.isfile(thumbFilepath) ): - thumbPixbuf = gtk.gdk.pixbuf_new_from_file(thumbFilepath) - return thumbPixbuf - - - def getThumbFilepath( self ): - return os.path.join(Instance.instancePath, self.thumbFilename) - - - def getAudioImagePixbuf( self ): - audioPixbuf = None - - if (self.audioImageFilename == None): - audioPixbuf = self.getThumbPixbuf() - else: - audioFilepath = self.getAudioImageFilepath() - if (audioFilepath != None): - audioPixbuf = gtk.gdk.pixbuf_new_from_file(audioFilepath) - - return audioPixbuf - - - def getAudioImageFilepath( self ): - if (self.audioImageFilename != None): - audioFilepath = os.path.join(Instance.instancePath, self.audioImageFilename) - return os.path.abspath(audioFilepath) - else: - return self.getThumbFilepath() - - - def getMediaFilepath(self): - if (self.datastoreId == None): - if (not self.buddy): - #just taken by you, so it is in the tempSessionDir - mediaFilepath = os.path.join(Instance.instancePath, self.mediaFilename) - return os.path.abspath(mediaFilepath) - else: - if (self.downloadedFromBuddy): - #the user has requested the high-res version, and it has downloaded - mediaFilepath = os.path.join(Instance.instancePath, self.mediaFilename) - return os.path.abspath(mediaFilepath) - else: - if self.mediaFilename == None: - #creating a new filepath, probably just got here from the mesh - ext = Constants.mediaTypes[self.type][Constants.keyExt] - recdPath = os.path.join(Instance.instancePath, "recdFile_"+self.mediaMd5+"."+ext) - recdPath = utils.getUniqueFilepath(recdPath, 0) - self.mediaFilename = os.path.basename(recdPath) - mediaFilepath = os.path.join(Instance.instancePath, self.mediaFilename) - return os.path.abspath(mediaFilepath) - else: - mediaFilepath = os.path.join(Instance.instancePath, self.mediaFilename) - return os.path.abspath(mediaFilepath) - - else: #pulling from the datastore, regardless of who took it, cause we got it - #first, get the datastoreObject and hold the reference in this Recorded instance - if (self.datastoreOb == None): - self.datastoreOb = serialize.getMediaFromDatastore( self ) - if (self.datastoreOb == None): - print("RecordActivity error -- unable to get datastore object in getMediaFilepath") - return None - - return self.datastoreOb.file_path
\ No newline at end of file + def __init__( self ): + self.type = -1 + self.time = None + self.recorderName = None + self.recorderHash = None + self.title = None + self.colorStroke = None + self.colorFill = None + self.mediaMd5 = None + self.thumbMd5 = None + self.mediaBytes = None + self.thumbBytes = None + self.tags = None + + #flag to alert need to re-datastore the title + self.metaChange = False + + #when you are datastore-serialized, you get one of these ids... + self.datastoreId = None + self.datastoreOb = None + + #if not from the datastore, then your media is here... + self.mediaFilename = None + self.thumbFilename = None + self.audioImageFilename = None + + #for flagging when you are being saved to the datastore for the first time... + #and just because you have a datastore id, doesn't mean you're saved + self.savedMedia = False + self.savedXml = False + + #assume you took the picture + self.buddy = False + self.downloadedFromBuddy = False + self.triedMeshBuddies = [] + self.meshDownloading = False + self.meshDownloadingFrom = "" + self.meshDownloadingFromNick = "" + self.meshDownlodingPercent = 0.0 + self.meshDownloadingProgress = False + #if someone is downloading this, then hold onto it + self.meshUploading = False + self.meshReqCallbackId = 0 + + self.deleted = False + + + def setTitle( self, newTitle ): + self.title = newTitle + self.metaChange = True + + + def setTags( self, newTags ): + self.tags = newTags + self.metaChange = True + + + def isClipboardCopyable( self ): + copyme = True + if (self.buddy): + if (not self.downloadedFromBuddy): + return False + return copyme + + + #scenarios: + #launch, your new thumb -- Journal/session + #launch, your new media -- Journal/session + #launch, their new thumb -- Journal/session/buddy + #launch, their new media -- ([request->]) Journal/session/buddy + #relaunch, your old thumb -- metadataPixbuf on request (or save to Journal/session..?) + #relaunch, your old media -- datastoreObject->file (hold onto the datastore object, delete if deleted) + #relaunch, their old thumb -- metadataPixbuf on request (or save to Journal/session..?) + #relaunch, their old media -- datastoreObject->file (hold onto the datastore object, delete if deleted) | ([request->]) Journal/session/buddy + + def getThumbPixbuf( self ): + thumbPixbuf = None + thumbFilepath = self.getThumbFilepath() + if ( os.path.isfile(thumbFilepath) ): + thumbPixbuf = gtk.gdk.pixbuf_new_from_file(thumbFilepath) + return thumbPixbuf + + + def getThumbFilepath( self ): + return os.path.join(Instance.instancePath, self.thumbFilename) + + + def getAudioImagePixbuf( self ): + audioPixbuf = None + + if (self.audioImageFilename == None): + audioPixbuf = self.getThumbPixbuf() + else: + audioFilepath = self.getAudioImageFilepath() + if (audioFilepath != None): + audioPixbuf = gtk.gdk.pixbuf_new_from_file(audioFilepath) + + return audioPixbuf + + + def getAudioImageFilepath( self ): + if (self.audioImageFilename != None): + audioFilepath = os.path.join(Instance.instancePath, self.audioImageFilename) + return os.path.abspath(audioFilepath) + else: + return self.getThumbFilepath() + + + def getMediaFilepath(self): + if (self.datastoreId == None): + if (not self.buddy): + #just taken by you, so it is in the tempSessionDir + mediaFilepath = os.path.join(Instance.instancePath, self.mediaFilename) + return os.path.abspath(mediaFilepath) + else: + if (self.downloadedFromBuddy): + #the user has requested the high-res version, and it has downloaded + mediaFilepath = os.path.join(Instance.instancePath, self.mediaFilename) + return os.path.abspath(mediaFilepath) + else: + if self.mediaFilename == None: + #creating a new filepath, probably just got here from the mesh + ext = Constants.mediaTypes[self.type][Constants.keyExt] + recdPath = os.path.join(Instance.instancePath, "recdFile_"+self.mediaMd5+"."+ext) + recdPath = utils.getUniqueFilepath(recdPath, 0) + self.mediaFilename = os.path.basename(recdPath) + mediaFilepath = os.path.join(Instance.instancePath, self.mediaFilename) + return os.path.abspath(mediaFilepath) + else: + mediaFilepath = os.path.join(Instance.instancePath, self.mediaFilename) + return os.path.abspath(mediaFilepath) + + else: #pulling from the datastore, regardless of who took it, cause we got it + #first, get the datastoreObject and hold the reference in this Recorded instance + if (self.datastoreOb == None): + self.datastoreOb = serialize.getMediaFromDatastore( self ) + if (self.datastoreOb == None): + print("RecordActivity error -- unable to get datastore object in getMediaFilepath") + return None + + return self.datastoreOb.file_path
\ No newline at end of file diff --git a/recordtube.py b/recordtube.py index 5332823..7f63922 100644 --- a/recordtube.py +++ b/recordtube.py @@ -10,117 +10,117 @@ import record class RecordTube(ExportedGObject): - __gsignals__ = { - 'recd-bits-arrived': - (gobject.SIGNAL_RUN_FIRST, None, [object,object,object,object,object]), - 'recd-request': - (gobject.SIGNAL_RUN_FIRST, None, [object,object]), - 'new-recd': - (gobject.SIGNAL_RUN_FIRST, None, [object,object]), - 'recd-unavailable': - (gobject.SIGNAL_RUN_FIRST, None, [object,object]) - } - - - def __init__(self, tube): - super(RecordTube, self).__init__(tube, Constants.PATH) - self.tube = tube - - self.idNotify = self.tube.add_signal_receiver(self._newRecdTubeCb, 'notifyBudsOfNewRecd', Constants.IFACE, path=Constants.PATH, sender_keyword='sender') - self.idRequest = self.tube.add_signal_receiver(self._reqRecdTubeCb, 'requestRecdBits', Constants.IFACE, path=Constants.PATH, sender_keyword='sender') - self.idBroadcast = self.tube.add_signal_receiver(self._getRecdTubeCb, 'broadcastRecdBits', Constants.IFACE, path=Constants.PATH, sender_keyword='sender', byte_arrays=True) - self.idUnavailable = self.tube.add_signal_receiver(self._unavailableRecdTubeCb, 'unavailableRecd', Constants.IFACE, path=Constants.PATH, sender_keyword='sender') - - - @signal(dbus_interface=Constants.IFACE, signature='ss') #dual s for 2x strings - def notifyBudsOfNewRecd(self, recorder, recdXml): - record.Record.log.debug('Ive taken a new pho-ideo-audio! I hereby send you an xml thumb of said media via this interface.') - - - def _newRecdTubeCb(self, recorder, recdXml, sender=None): - record.Record.log.debug("_newRecdTubeCb from " + recorder ) - if sender == self.tube.get_unique_name(): - record.Record.log.debug("_newRecdTubeCb: sender is my bus name, so ignore my own signal") - return - elif (recorder == Instance.keyHashPrintable): - record.Record.log.debug('_newRecdTubeCb: excuse me? you are asking me to share a photo with myself?') - return - - self.emit( "new-recd", str(recorder), str(recdXml) ) - - - @signal(dbus_interface=Constants.IFACE, signature='sss') #triple s for 3x strings - def requestRecdBits(self, whoWantsIt, whoTheyWantItFrom, recdMd5sumOfIt ): - record.Record.log.debug('I am requesting a high-res version of someones media.') - - - def _reqRecdTubeCb(self, whoWantsIt, whoTheyWantItFrom, recdMd5sumOfIt, sender=None): - if sender == self.tube.get_unique_name(): - record.Record.log.debug("_reqRecdTubeCb: sender is my bus name, so ignore my own signal") - return - elif (whoWantsIt == Instance.keyHashPrintable): - record.Record.log.debug('_reqRecdTubeCb: excuse me? you are asking me to share a photo with myself?') - return - elif (whoTheyWantItFrom != Instance.keyHashPrintable): - record.Record.log.debug('_reqRecdTubeCb: ive overhead someone wants a photo, but not from me') - return - - self.emit( "recd-request", str(whoWantsIt), str(recdMd5sumOfIt) ) - - - def broadcastRecd(self, md5, filepath, sendThisTo ): - size = os.path.getsize(filepath) - f = open(filepath) - chunk_size = 1000 - chunks = size / chunk_size - if (size%chunk_size != 0): - chunks += 1 - - for chunk in range(chunks): - bytes = f.read(chunk_size) - if (chunk == 0): - record.Record.log.debug("sending " + str(chunk+1) + " of " + str(chunks) + " to " + sendThisTo ) - if (chunk == chunks-1): - record.Record.log.debug("sending " + str(chunk+1) + " of " + str(chunks) + " to " + sendThisTo ) - self.broadcastRecdBits(md5, chunk+1, chunks, bytes, sendThisTo, Instance.keyHashPrintable) - - f.close() - return True - - - @signal(dbus_interface=Constants.IFACE, signature='suuayss') - def broadcastRecdBits(self, md5, part, numparts, bytes, sendTo, fromWho ): - pass - - - def _getRecdTubeCb(self, md5, part, numparts, bytes, sentTo, fromWho, sender=None): - if sender == self.tube.get_unique_name(): - #record.Record.log.debug("_reqRecdTubeCb: sender is my bus name, so ignore my own signal") - return - if (fromWho == Instance.keyHashPrintable): - #record.Record.log.debug('_getRecdTubeCb: i dont want bits from meself, thx anyway. schizophrenic?') - return - if (sentTo != Instance.keyHashPrintable): - #record.Record.log.debug('_getRecdTubeCb: ive overhead someone sending bits, but not to me!') - return - - self.emit( "recd-bits-arrived", md5, part, numparts, bytes, fromWho ) - - - @signal(dbus_interface=Constants.IFACE, signature='sss') #triple s for 3x strings - def unavailableRecd(self, md5sumOfIt, whoDoesntHaveIt, whoAskedForIt): - record.Record.log.debug('unavailableRecd: id love to share this photo, but i am without a copy meself chum') - - - def _unavailableRecdTubeCb( self, md5sumOfIt, whoDoesntHaveIt, whoAskedForIt, sender=None): - if sender == self.tube.get_unique_name(): - record.Record.log.debug("_unavailableRecdTubeCb: sender is my bus name, so ignore my own signal") - return - if (whoDoesntHaveIt == Instance.keyHashPrintable): - record.Record.log.debug('_unavailableRecdTubeCb: yes, i know i dont have it, i just told you/me/us.') - return - if (whoAskedForIt != Instance.keyHashPrintable): - record.Record.log.debug('_unavailableRecdTubeCb: ive overheard someone doesnt have a photo, but i didnt ask for that one anyways') - return + __gsignals__ = { + 'recd-bits-arrived': + (gobject.SIGNAL_RUN_FIRST, None, [object,object,object,object,object]), + 'recd-request': + (gobject.SIGNAL_RUN_FIRST, None, [object,object]), + 'new-recd': + (gobject.SIGNAL_RUN_FIRST, None, [object,object]), + 'recd-unavailable': + (gobject.SIGNAL_RUN_FIRST, None, [object,object]) + } + + + def __init__(self, tube): + super(RecordTube, self).__init__(tube, Constants.PATH) + self.tube = tube + + self.idNotify = self.tube.add_signal_receiver(self._newRecdTubeCb, 'notifyBudsOfNewRecd', Constants.IFACE, path=Constants.PATH, sender_keyword='sender') + self.idRequest = self.tube.add_signal_receiver(self._reqRecdTubeCb, 'requestRecdBits', Constants.IFACE, path=Constants.PATH, sender_keyword='sender') + self.idBroadcast = self.tube.add_signal_receiver(self._getRecdTubeCb, 'broadcastRecdBits', Constants.IFACE, path=Constants.PATH, sender_keyword='sender', byte_arrays=True) + self.idUnavailable = self.tube.add_signal_receiver(self._unavailableRecdTubeCb, 'unavailableRecd', Constants.IFACE, path=Constants.PATH, sender_keyword='sender') + + + @signal(dbus_interface=Constants.IFACE, signature='ss') #dual s for 2x strings + def notifyBudsOfNewRecd(self, recorder, recdXml): + record.Record.log.debug('Ive taken a new pho-ideo-audio! I hereby send you an xml thumb of said media via this interface.') + + + def _newRecdTubeCb(self, recorder, recdXml, sender=None): + record.Record.log.debug("_newRecdTubeCb from " + recorder ) + if sender == self.tube.get_unique_name(): + record.Record.log.debug("_newRecdTubeCb: sender is my bus name, so ignore my own signal") + return + elif (recorder == Instance.keyHashPrintable): + record.Record.log.debug('_newRecdTubeCb: excuse me? you are asking me to share a photo with myself?') + return + + self.emit( "new-recd", str(recorder), str(recdXml) ) + + + @signal(dbus_interface=Constants.IFACE, signature='sss') #triple s for 3x strings + def requestRecdBits(self, whoWantsIt, whoTheyWantItFrom, recdMd5sumOfIt ): + record.Record.log.debug('I am requesting a high-res version of someones media.') + + + def _reqRecdTubeCb(self, whoWantsIt, whoTheyWantItFrom, recdMd5sumOfIt, sender=None): + if sender == self.tube.get_unique_name(): + record.Record.log.debug("_reqRecdTubeCb: sender is my bus name, so ignore my own signal") + return + elif (whoWantsIt == Instance.keyHashPrintable): + record.Record.log.debug('_reqRecdTubeCb: excuse me? you are asking me to share a photo with myself?') + return + elif (whoTheyWantItFrom != Instance.keyHashPrintable): + record.Record.log.debug('_reqRecdTubeCb: ive overhead someone wants a photo, but not from me') + return + + self.emit( "recd-request", str(whoWantsIt), str(recdMd5sumOfIt) ) + + + def broadcastRecd(self, md5, filepath, sendThisTo ): + size = os.path.getsize(filepath) + f = open(filepath) + chunk_size = 1000 + chunks = size / chunk_size + if (size%chunk_size != 0): + chunks += 1 + + for chunk in range(chunks): + bytes = f.read(chunk_size) + if (chunk == 0): + record.Record.log.debug("sending " + str(chunk+1) + " of " + str(chunks) + " to " + sendThisTo ) + if (chunk == chunks-1): + record.Record.log.debug("sending " + str(chunk+1) + " of " + str(chunks) + " to " + sendThisTo ) + self.broadcastRecdBits(md5, chunk+1, chunks, bytes, sendThisTo, Instance.keyHashPrintable) + + f.close() + return True + + + @signal(dbus_interface=Constants.IFACE, signature='suuayss') + def broadcastRecdBits(self, md5, part, numparts, bytes, sendTo, fromWho ): + pass + + + def _getRecdTubeCb(self, md5, part, numparts, bytes, sentTo, fromWho, sender=None): + if sender == self.tube.get_unique_name(): + #record.Record.log.debug("_reqRecdTubeCb: sender is my bus name, so ignore my own signal") + return + if (fromWho == Instance.keyHashPrintable): + #record.Record.log.debug('_getRecdTubeCb: i dont want bits from meself, thx anyway. schizophrenic?') + return + if (sentTo != Instance.keyHashPrintable): + #record.Record.log.debug('_getRecdTubeCb: ive overhead someone sending bits, but not to me!') + return + + self.emit( "recd-bits-arrived", md5, part, numparts, bytes, fromWho ) + + + @signal(dbus_interface=Constants.IFACE, signature='sss') #triple s for 3x strings + def unavailableRecd(self, md5sumOfIt, whoDoesntHaveIt, whoAskedForIt): + record.Record.log.debug('unavailableRecd: id love to share this photo, but i am without a copy meself chum') + + + def _unavailableRecdTubeCb( self, md5sumOfIt, whoDoesntHaveIt, whoAskedForIt, sender=None): + if sender == self.tube.get_unique_name(): + record.Record.log.debug("_unavailableRecdTubeCb: sender is my bus name, so ignore my own signal") + return + if (whoDoesntHaveIt == Instance.keyHashPrintable): + record.Record.log.debug('_unavailableRecdTubeCb: yes, i know i dont have it, i just told you/me/us.') + return + if (whoAskedForIt != Instance.keyHashPrintable): + record.Record.log.debug('_unavailableRecdTubeCb: ive overheard someone doesnt have a photo, but i didnt ask for that one anyways') + return - self.emit("recd-unavailable", md5sumOfIt, whoDoesntHaveIt)
\ No newline at end of file + self.emit("recd-unavailable", md5sumOfIt, whoDoesntHaveIt)
\ No newline at end of file diff --git a/serialize.py b/serialize.py index f86c373..8e56d5f 100644 --- a/serialize.py +++ b/serialize.py @@ -14,329 +14,320 @@ import record import utils import recorded -def fillMediaHash( index, mediaHashs ): - doc = None - if (os.path.exists(index)): - try: - doc = parse( os.path.abspath(index) ) - except: - doc = None - if (doc == None): - return - - for key,value in Constants.mediaTypes.items(): - recdElements = doc.documentElement.getElementsByTagName(value[Constants.keyName]) - for el in recdElements: - _loadMediaIntoHash( el, mediaHashs[key] ) +def fillMediaHash( doc, mediaHashs ): + for key,value in Constants.mediaTypes.items(): + recdElements = doc.documentElement.getElementsByTagName(value[Constants.keyName]) + for el in recdElements: + _loadMediaIntoHash( el, mediaHashs[key] ) def _loadMediaIntoHash( el, hash ): - addToHash = True - recd = record.Recorded() - recd = fillRecdFromNode(recd, el) - if (recd != None): - if (recd.datastoreId != None): - #quickly check: if you have a datastoreId that the file hasn't been deleted, - #cause if you do, we need to flag your removal - #2904 trac - recd.datastoreOb = getMediaFromDatastore( recd ) - if (recd.datastoreOb == None): - addToHash = False - else: - #name might have been changed in the journal, so reflect that here - if (recd.title != recd.datastoreOb.metadata['title']): - recd.setTitle(recd.datastoreOb.metadata['title']) - if (recd.tags != recd.datastoreOb.metadata['tags']): - recd.setTags(recd.datastoreOb.metadata['tags']) - if (recd.buddy): - recd.downloadedFromBuddy = True - - recd.datastoreOb == None - - if (addToHash): - hash.append( recd ) + addToHash = True + recd = record.Recorded() + recd = fillRecdFromNode(recd, el) + if (recd != None): + if (recd.datastoreId != None): + #quickly check: if you have a datastoreId that the file hasn't been deleted, + #cause if you do, we need to flag your removal + #2904 trac + recd.datastoreOb = getMediaFromDatastore( recd ) + if (recd.datastoreOb == None): + addToHash = False + else: + #name might have been changed in the journal, so reflect that here + if (recd.title != recd.datastoreOb.metadata['title']): + recd.setTitle(recd.datastoreOb.metadata['title']) + if (recd.tags != recd.datastoreOb.metadata['tags']): + recd.setTags(recd.datastoreOb.metadata['tags']) + if (recd.buddy): + recd.downloadedFromBuddy = True + + recd.datastoreOb == None + + if (addToHash): + hash.append( recd ) def getMediaFromDatastore( recd ): - if (recd.datastoreId == None): - return None + if (recd.datastoreId == None): + return None - if (recd.datastoreOb != None): - #already have the object - return recd.datastoreOb + if (recd.datastoreOb != None): + #already have the object + return recd.datastoreOb - mediaObject = None - try: - mediaObject = datastore.get( recd.datastoreId ) - finally: - if (mediaObject == None): - return None + mediaObject = None + try: + mediaObject = datastore.get( recd.datastoreId ) + finally: + if (mediaObject == None): + return None - return mediaObject + return mediaObject def removeMediaFromDatastore( recd ): - #before this method is called, the media are removed from the file - if (recd.datastoreId == None): - return - if (recd.datastoreOb == None): - return + #before this method is called, the media are removed from the file + if (recd.datastoreId == None): + return + if (recd.datastoreOb == None): + return - try: - recd.datastoreOb.destroy() - datastore.delete( recd.datastoreId ) + try: + recd.datastoreOb.destroy() + datastore.delete( recd.datastoreId ) - del recd.datastoreId - recd.datastoreId = None + del recd.datastoreId + recd.datastoreId = None - del recd.datastoreOb - recd.datastoreOb = None + del recd.datastoreOb + recd.datastoreOb = None - finally: - #todo: add error message here - pass + finally: + #todo: add error message here + pass def fillRecdFromNode( recd, el ): - if (el.getAttributeNode(Constants.recdType) != None): - typeInt = int(el.getAttribute(Constants.recdType)) - recd.type = typeInt - - if (el.getAttributeNode(Constants.recdTitle) != None): - recd.title = el.getAttribute(Constants.recdTitle) - - if (el.getAttributeNode(Constants.recdTime) != None): - timeInt = int(el.getAttribute(Constants.recdTime)) - recd.time = timeInt - - if (el.getAttributeNode(Constants.recdRecorderName) != None): - recd.recorderName = el.getAttribute(Constants.recdRecorderName) - - if (el.getAttributeNode(Constants.recdTags) != None): - recd.tags = el.getAttribute(Constants.recdTags) - else: - recd.tags = "" - - if (el.getAttributeNode(Constants.recdRecorderHash) != None): - recd.recorderHash = el.getAttribute(Constants.recdRecorderHash) - - if (el.getAttributeNode(Constants.recdColorStroke) != None): - try: - colorStrokeHex = el.getAttribute(Constants.recdColorStroke) - colorStroke = Color() - colorStroke.init_hex(colorStrokeHex) - recd.colorStroke = colorStroke - except: - record.Record.log.error("unable to load recd colorStroke") - - if (el.getAttributeNode(Constants.recdColorFill) != None): - try: - colorFillHex = el.getAttribute(Constants.recdColorFill) - colorFill = Color() - colorFill.init_hex( colorFillHex ) - recd.colorFill = colorFill - except: - record.Record.log.error("unable to load recd colorFill") - - if (el.getAttributeNode(Constants.recdBuddy) != None): - recd.buddy = (el.getAttribute(Constants.recdBuddy) == "True") - - if (el.getAttributeNode(Constants.recdMediaMd5) != None): - recd.mediaMd5 = el.getAttribute(Constants.recdMediaMd5) - - if (el.getAttributeNode(Constants.recdThumbMd5) != None): - recd.thumbMd5 = el.getAttribute(Constants.recdThumbMd5) - - if (el.getAttributeNode(Constants.recdMediaBytes) != None): - recd.mediaBytes = el.getAttribute(Constants.recdMediaBytes) - - if (el.getAttributeNode(Constants.recdThumbBytes) != None): - recd.thumbBytes = el.getAttribute(Constants.recdThumbBytes) - - bt = el.getAttributeNode(Constants.recdBase64Thumb) - if (bt != None): - try: - thumbPath = os.path.join(Instance.instancePath, "datastoreThumb.jpg") - thumbPath = utils.getUniqueFilepath( thumbPath, 0 ) - thumbImg = utils.getPixbufFromString( bt.nodeValue ) - thumbImg.save(thumbPath, "jpeg", {"quality":"85"} ) - recd.thumbFilename = os.path.basename(thumbPath) - record.Record.log.debug("saved thumbFilename") - except: - record.Record.log.error("unable to getRecdBase64Thumb") - - ai = el.getAttributeNode(Constants.recdAudioImage) - if (not ai == None): - try: - audioImagePath = os.path.join(Instance.instancePath, "audioImage.png") - audioImagePath = utils.getUniqueFilepath( audioImagePath, 0 ) - audioImage = utils.getPixbufFromString( ai.nodeValue ) - audioImage.save(audioImagePath, "png", {} ) - recd.audioImageFilename = os.path.basename(audioImagePath) - record.Record.log.debug("loaded audio image and set audioImageFilename") - except: - record.Record.log.error("unable to load audio image") - - datastoreNode = el.getAttributeNode(Constants.recdDatastoreId) - if (datastoreNode != None): - recd.datastoreId = datastoreNode.nodeValue - - return recd + if (el.getAttributeNode(Constants.recdType) != None): + typeInt = int(el.getAttribute(Constants.recdType)) + recd.type = typeInt + + if (el.getAttributeNode(Constants.recdTitle) != None): + recd.title = el.getAttribute(Constants.recdTitle) + + if (el.getAttributeNode(Constants.recdTime) != None): + timeInt = int(el.getAttribute(Constants.recdTime)) + recd.time = timeInt + + if (el.getAttributeNode(Constants.recdRecorderName) != None): + recd.recorderName = el.getAttribute(Constants.recdRecorderName) + + if (el.getAttributeNode(Constants.recdTags) != None): + recd.tags = el.getAttribute(Constants.recdTags) + else: + recd.tags = "" + + if (el.getAttributeNode(Constants.recdRecorderHash) != None): + recd.recorderHash = el.getAttribute(Constants.recdRecorderHash) + + if (el.getAttributeNode(Constants.recdColorStroke) != None): + try: + colorStrokeHex = el.getAttribute(Constants.recdColorStroke) + colorStroke = Color() + colorStroke.init_hex(colorStrokeHex) + recd.colorStroke = colorStroke + except: + record.Record.log.error("unable to load recd colorStroke") + + if (el.getAttributeNode(Constants.recdColorFill) != None): + try: + colorFillHex = el.getAttribute(Constants.recdColorFill) + colorFill = Color() + colorFill.init_hex( colorFillHex ) + recd.colorFill = colorFill + except: + record.Record.log.error("unable to load recd colorFill") + + if (el.getAttributeNode(Constants.recdBuddy) != None): + recd.buddy = (el.getAttribute(Constants.recdBuddy) == "True") + + if (el.getAttributeNode(Constants.recdMediaMd5) != None): + recd.mediaMd5 = el.getAttribute(Constants.recdMediaMd5) + + if (el.getAttributeNode(Constants.recdThumbMd5) != None): + recd.thumbMd5 = el.getAttribute(Constants.recdThumbMd5) + + if (el.getAttributeNode(Constants.recdMediaBytes) != None): + recd.mediaBytes = el.getAttribute(Constants.recdMediaBytes) + + if (el.getAttributeNode(Constants.recdThumbBytes) != None): + recd.thumbBytes = el.getAttribute(Constants.recdThumbBytes) + + bt = el.getAttributeNode(Constants.recdBase64Thumb) + if (bt != None): + try: + thumbPath = os.path.join(Instance.instancePath, "datastoreThumb.jpg") + thumbPath = utils.getUniqueFilepath( thumbPath, 0 ) + thumbImg = utils.getPixbufFromString( bt.nodeValue ) + thumbImg.save(thumbPath, "jpeg", {"quality":"85"} ) + recd.thumbFilename = os.path.basename(thumbPath) + record.Record.log.debug("saved thumbFilename") + except: + record.Record.log.error("unable to getRecdBase64Thumb") + + ai = el.getAttributeNode(Constants.recdAudioImage) + if (not ai == None): + try: + audioImagePath = os.path.join(Instance.instancePath, "audioImage.png") + audioImagePath = utils.getUniqueFilepath( audioImagePath, 0 ) + audioImage = utils.getPixbufFromString( ai.nodeValue ) + audioImage.save(audioImagePath, "png", {} ) + recd.audioImageFilename = os.path.basename(audioImagePath) + record.Record.log.debug("loaded audio image and set audioImageFilename") + except: + record.Record.log.error("unable to load audio image") + + datastoreNode = el.getAttributeNode(Constants.recdDatastoreId) + if (datastoreNode != None): + recd.datastoreId = datastoreNode.nodeValue + + return recd def getRecdXmlMeshString( recd ): - impl = getDOMImplementation() - recdXml = impl.createDocument(None, Constants.recdRecd, None) - root = recdXml.documentElement - _addRecdXmlAttrs( root, recd, True ) + impl = getDOMImplementation() + recdXml = impl.createDocument(None, Constants.recdRecd, None) + root = recdXml.documentElement + _addRecdXmlAttrs( root, recd, True ) - writer = cStringIO.StringIO() - recdXml.writexml(writer) - return writer.getvalue() + writer = cStringIO.StringIO() + recdXml.writexml(writer) + return writer.getvalue() def _addRecdXmlAttrs( el, recd, forMeshTransmit ): - el.setAttribute(Constants.recdType, str(recd.type)) - - if ((recd.type == Constants.TYPE_AUDIO) and (not forMeshTransmit)): - aiPixbuf = recd.getAudioImagePixbuf( ) - aiPixbufString = str( utils.getStringFromPixbuf(aiPixbuf) ) - el.setAttribute(Constants.recdAudioImage, aiPixbufString) - - if ((recd.datastoreId != None) and (not forMeshTransmit)): - el.setAttribute(Constants.recdDatastoreId, str(recd.datastoreId)) - - el.setAttribute(Constants.recdTitle, recd.title) - el.setAttribute(Constants.recdTime, str(recd.time)) - el.setAttribute(Constants.recdRecorderName, recd.recorderName) - el.setAttribute(Constants.recdRecorderHash, str(recd.recorderHash) ) - el.setAttribute(Constants.recdColorStroke, str(recd.colorStroke.hex) ) - el.setAttribute(Constants.recdColorFill, str(recd.colorFill.hex) ) - el.setAttribute(Constants.recdBuddy, str(recd.buddy)) - el.setAttribute(Constants.recdMediaMd5, str(recd.mediaMd5)) - el.setAttribute(Constants.recdThumbMd5, str(recd.thumbMd5)) - el.setAttribute(Constants.recdMediaBytes, str(recd.mediaBytes)) - el.setAttribute(Constants.recdThumbBytes, str(recd.thumbBytes)) - el.setAttribute(Constants.recdRecordVersion, str(Constants.VERSION)) - - pixbuf = recd.getThumbPixbuf( ) - thumb64 = str( utils.getStringFromPixbuf(pixbuf) ) - el.setAttribute(Constants.recdBase64Thumb, thumb64) + el.setAttribute(Constants.recdType, str(recd.type)) + + if ((recd.type == Constants.TYPE_AUDIO) and (not forMeshTransmit)): + aiPixbuf = recd.getAudioImagePixbuf( ) + aiPixbufString = str( utils.getStringFromPixbuf(aiPixbuf) ) + el.setAttribute(Constants.recdAudioImage, aiPixbufString) + + if ((recd.datastoreId != None) and (not forMeshTransmit)): + el.setAttribute(Constants.recdDatastoreId, str(recd.datastoreId)) + + el.setAttribute(Constants.recdTitle, recd.title) + el.setAttribute(Constants.recdTime, str(recd.time)) + el.setAttribute(Constants.recdRecorderName, recd.recorderName) + el.setAttribute(Constants.recdRecorderHash, str(recd.recorderHash) ) + el.setAttribute(Constants.recdColorStroke, str(recd.colorStroke.hex) ) + el.setAttribute(Constants.recdColorFill, str(recd.colorFill.hex) ) + el.setAttribute(Constants.recdBuddy, str(recd.buddy)) + el.setAttribute(Constants.recdMediaMd5, str(recd.mediaMd5)) + el.setAttribute(Constants.recdThumbMd5, str(recd.thumbMd5)) + el.setAttribute(Constants.recdMediaBytes, str(recd.mediaBytes)) + el.setAttribute(Constants.recdThumbBytes, str(recd.thumbBytes)) + el.setAttribute(Constants.recdRecordVersion, str(Constants.VERSION)) + + pixbuf = recd.getThumbPixbuf( ) + thumb64 = str( utils.getStringFromPixbuf(pixbuf) ) + el.setAttribute(Constants.recdBase64Thumb, thumb64) def saveMediaHash( mediaHashs ): - impl = getDOMImplementation() - album = impl.createDocument(None, Constants.recdAlbum, None) - root = album.documentElement - - #flag everything for saving... - atLeastOne = False - for type,value in Constants.mediaTypes.items(): - typeName = value[Constants.keyName] - hash = mediaHashs[type] - for i in range (0, len(hash)): - recd = hash[i] - recd.savedXml = False - recd.savedMedia = False - atLeastOne = True - - #and if there is anything to save, save it - if (atLeastOne): - for type,value in Constants.mediaTypes.items(): - typeName = value[Constants.keyName] - hash = mediaHashs[type] - - for i in range (0, len(hash)): - recd = hash[i] - mediaEl = album.createElement( typeName ) - root.appendChild( mediaEl ) - _saveMedia( mediaEl, recd ) - - return album + impl = getDOMImplementation() + album = impl.createDocument(None, Constants.recdAlbum, None) + root = album.documentElement + + #flag everything for saving... + atLeastOne = False + for type,value in Constants.mediaTypes.items(): + typeName = value[Constants.keyName] + hash = mediaHashs[type] + for i in range (0, len(hash)): + recd = hash[i] + recd.savedXml = False + recd.savedMedia = False + atLeastOne = True + + #and if there is anything to save, save it + if (atLeastOne): + for type,value in Constants.mediaTypes.items(): + typeName = value[Constants.keyName] + hash = mediaHashs[type] + + for i in range (0, len(hash)): + recd = hash[i] + mediaEl = album.createElement( typeName ) + root.appendChild( mediaEl ) + _saveMedia( mediaEl, recd ) + + return album def _saveMedia( el, recd ): - if ( (recd.buddy == True) and (recd.datastoreId == None) and (not recd.downloadedFromBuddy) ): - recd.savedMedia = True - _saveXml( el, recd ) - else: - recd.savedMedia = False - _saveMediaToDatastore( el, recd ) + if ( (recd.buddy == True) and (recd.datastoreId == None) and (not recd.downloadedFromBuddy) ): + recd.savedMedia = True + _saveXml( el, recd ) + else: + recd.savedMedia = False + _saveMediaToDatastore( el, recd ) def _saveXml( el, recd ): - if (recd.thumbFilename != None): - _addRecdXmlAttrs( el, recd, False ) - else: - record.Record.log.debug("WOAH, ERROR: recd has no thumbFilename?! " + str(recd) ) - recd.savedXml = True + if (recd.thumbFilename != None): + _addRecdXmlAttrs( el, recd, False ) + else: + record.Record.log.debug("WOAH, ERROR: recd has no thumbFilename?! " + str(recd) ) + recd.savedXml = True def _saveMediaToDatastore( el, recd ): - #note that we update the recds that go through here to how they would - #look on a fresh load from file since this won't just happen on close() - - if (recd.datastoreId != None): - #already saved to the datastore, don't need to re-rewrite the file since the mediums are immutable - #However, they might have changed the name of the file - if (recd.metaChange): - recd.datastoreOb = getMediaFromDatastore( recd ) - if (recd.datastoreOb.metadata['title'] != recd.title): - recd.datastoreOb.metadata['title'] = recd.title - datastore.write(recd.datastoreOb) - if (recd.datastoreOb.metadata['tags'] != recd.tags): - recd.datastoreOb.metadata['tags'] = recd.tags - datastore.write(recd.datastoreOb) - - #reset for the next title change if not closing... - recd.metaChange = False - #save the title to the xml - recd.savedMedia = True - _saveXml( el, recd ) - else: - recd.savedMedia = True - _saveXml( el, recd ) - - else: - #this will remove the media from being accessed on the local disk since it puts it away into cold storage - #therefore this is only called when write_file is called by the activity superclass - mediaObject = datastore.create() - mediaObject.metadata['title'] = recd.title - mediaObject.metadata['tags'] = recd.tags - - datastorePreviewPixbuf = recd.getThumbPixbuf() - if (recd.type == Constants.TYPE_AUDIO): - datastorePreviewPixbuf = recd.getAudioImagePixbuf() - elif (recd.type == Constants.TYPE_PHOTO): - datastorePreviewFilepath = recd.getMediaFilepath() - datastorePreviewPixbuf = gtk.gdk.pixbuf_new_from_file(datastorePreviewFilepath) - - datastorePreviewWidth = 300 - datastorePreviewHeight = 225 - if (datastorePreviewPixbuf.get_width() != datastorePreviewWidth): - datastorePreviewPixbuf = datastorePreviewPixbuf.scale_simple(datastorePreviewWidth, datastorePreviewHeight, gtk.gdk.INTERP_NEAREST) - - datastorePreviewBase64 = utils.getStringFromPixbuf(datastorePreviewPixbuf) - mediaObject.metadata['preview'] = datastorePreviewBase64 - - colors = str(recd.colorStroke.hex) + "," + str(recd.colorFill.hex) - mediaObject.metadata['icon-color'] = colors - - mtype = Constants.mediaTypes[recd.type] - mime = mtype[Constants.keyMime] - mediaObject.metadata['mime_type'] = mime - - mediaObject.metadata['activity_id'] = Constants.activityId - - mediaFile = recd.getMediaFilepath() - mediaObject.file_path = mediaFile - mediaObject.transfer_ownership = True - - datastore.write( mediaObject ) - - recd.datastoreId = mediaObject.object_id - recd.savedMedia = True - - _saveXml( el, recd ) - - recd.mediaFilename = None
\ No newline at end of file + #note that we update the recds that go through here to how they would + #look on a fresh load from file since this won't just happen on close() + + if (recd.datastoreId != None): + #already saved to the datastore, don't need to re-rewrite the file since the mediums are immutable + #However, they might have changed the name of the file + if (recd.metaChange): + recd.datastoreOb = getMediaFromDatastore( recd ) + if (recd.datastoreOb.metadata['title'] != recd.title): + recd.datastoreOb.metadata['title'] = recd.title + datastore.write(recd.datastoreOb) + if (recd.datastoreOb.metadata['tags'] != recd.tags): + recd.datastoreOb.metadata['tags'] = recd.tags + datastore.write(recd.datastoreOb) + + #reset for the next title change if not closing... + recd.metaChange = False + #save the title to the xml + recd.savedMedia = True + _saveXml( el, recd ) + else: + recd.savedMedia = True + _saveXml( el, recd ) + + else: + #this will remove the media from being accessed on the local disk since it puts it away into cold storage + #therefore this is only called when write_file is called by the activity superclass + mediaObject = datastore.create() + mediaObject.metadata['title'] = recd.title + mediaObject.metadata['tags'] = recd.tags + + datastorePreviewPixbuf = recd.getThumbPixbuf() + if (recd.type == Constants.TYPE_AUDIO): + datastorePreviewPixbuf = recd.getAudioImagePixbuf() + elif (recd.type == Constants.TYPE_PHOTO): + datastorePreviewFilepath = recd.getMediaFilepath() + datastorePreviewPixbuf = gtk.gdk.pixbuf_new_from_file(datastorePreviewFilepath) + + datastorePreviewWidth = 300 + datastorePreviewHeight = 225 + if (datastorePreviewPixbuf.get_width() != datastorePreviewWidth): + datastorePreviewPixbuf = datastorePreviewPixbuf.scale_simple(datastorePreviewWidth, datastorePreviewHeight, gtk.gdk.INTERP_NEAREST) + + datastorePreviewBase64 = utils.getStringFromPixbuf(datastorePreviewPixbuf) + mediaObject.metadata['preview'] = datastorePreviewBase64 + + colors = str(recd.colorStroke.hex) + "," + str(recd.colorFill.hex) + mediaObject.metadata['icon-color'] = colors + + mtype = Constants.mediaTypes[recd.type] + mime = mtype[Constants.keyMime] + mediaObject.metadata['mime_type'] = mime + + mediaObject.metadata['activity_id'] = Constants.activityId + + mediaFile = recd.getMediaFilepath() + mediaObject.file_path = mediaFile + mediaObject.transfer_ownership = True + + datastore.write( mediaObject ) + + recd.datastoreId = mediaObject.object_id + recd.savedMedia = True + + _saveXml( el, recd ) + + recd.mediaFilename = None @@ -17,9 +17,6 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from sugar.activity import bundlebuilder -import os - -os.system("make") bundlebuilder.start() @@ -34,6 +34,9 @@ import time import pango import hippo +import logging +logger = logging.getLogger('record:ui.py') + #from sugar.graphics.toolcombobox import ToolComboBox #from sugar.graphics.tray import HTray from sugar.graphics.toolbutton import ToolButton @@ -50,2314 +53,2349 @@ from p5_button import P5Button from p5_button import Polygon from p5_button import Button from glive import LiveVideoWindow -from glivex import SlowLiveVideoWindow from gplay import PlayVideoWindow from recorded import Recorded from button import RecdButton import utils import record -import _camera +import camerac +import aplay from tray import HTray from toolbarcombobox import ToolComboBox class UI: - dim_THUMB_WIDTH = 108 - dim_THUMB_HEIGHT = 81 - dim_INSET = 10 - dim_PIPW = 160 - dim_PIPH = 120 #pipBorder - dim_PIP_BORDER = 4 - dim_PGDW = dim_PIPW + (dim_PIP_BORDER*2) - dim_PGDH = dim_PIPH + (dim_PIP_BORDER*2) - dim_CONTROLBAR_HT = 55 - - def __init__( self, pca ): - self.ca = pca - self.ACTIVE = False - self.LAUNCHING = True - self.ca.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) - self.ca.connect("visibility-notify-event", self._visibleNotifyCb) - - self.inset = self.__class__.dim_INSET - self.pgdh = self.__class__.dim_PGDH - self.pgdw = self.__class__.dim_PGDW - self.thumbTrayHt = 150 #todo: get sugar constant here - self.thumbSvgW = 124 - self.thumbSvgH = 124 - self.maxw = 49 - self.maxh = 49 - self.controlBarHt = 60 - self.recordButtWd = 55 - self.pipw = self.__class__.dim_PIPW - self.piph = self.__class__.dim_PIPH - - #ui modes - - # True when we're in full-screen mode, False otherwise - self.FULLSCREEN = False - - # True when we're showing live video feed in the primary screen - # area, False otherwise (even when we are still showing live video - # in a p-i-p) - self.LIVEMODE = True - - self.LAST_MODE = -1 - self.LAST_FULLSCREEN = False - self.LAST_LIVE = True - self.LAST_MESHING = False - self.LAST_RECD_INFO = False - self.LAST_TRANSCODING = False - self.TRANSCODING = False - self.MESHING = False - - # RECD_INFO_ON is True when the 'info' for a recording is being - # display on-screen (who recorded it, tags, etc), and False otherwise. - self.RECD_INFO_ON = False - - self.UPDATE_DURATION_ID = 0 - self.UPDATE_TIMER_ID = 0 - self.COUNTINGDOWN = False - - #init - self.mapped = False - self.centered = False - self.setup = False - - #prep for when to show - self.shownRecd = None - - #this includes the default sharing tab - self.toolbox = activity.ActivityToolbox(self.ca) - self.ca.set_toolbox(self.toolbox) - self.photoToolbar = PhotoToolbar() - self.photoToolbar.set_sensitive( False ) - self.toolbox.add_toolbar( Constants.istrPhoto, self.photoToolbar ) - self.videoToolbar = VideoToolbar() - self.videoToolbar.set_sensitive( False ) - self.toolbox.add_toolbar( Constants.istrVideo, self.videoToolbar ) - self.audioToolbar = AudioToolbar() - self.audioToolbar.set_sensitive( False ) - self.toolbox.add_toolbar( Constants.istrAudio, self.audioToolbar ) - self.tbars = {Constants.MODE_PHOTO:self.photoToolbar,Constants.MODE_VIDEO:self.videoToolbar,Constants.MODE_AUDIO:self.audioToolbar} - self.toolbox.set_current_toolbar(self.ca.m.MODE+1) - - self.toolbox.remove(self.toolbox._separator) - #taken directly from toolbox.py b/c I don't know how to mod the hongry hippo - separator = hippo.Canvas() - box = hippo.CanvasBox( - border_color=Constants.colorBlack.get_int(), - background_color=Constants.colorBlack.get_int(), - box_height=style.TOOLBOX_SEPARATOR_HEIGHT, - border_bottom=style.LINE_WIDTH) - separator.set_root(box) - self.toolbox.pack_start(separator, False) - self.toolbox.separator = separator - - self.TOOLBOX_SIZE_ALLOCATE_ID = self.toolbox.connect_after("size-allocate", self._toolboxSizeAllocateCb) - self.toolbox._notebook.set_property("can-focus", False) - self.toolbox.connect("current-toolbar-changed", self._toolbarChangeCb) - self.toolbox.show_all() - - - def _toolboxSizeAllocateCb( self, widget, event ): - self.toolbox.disconnect( self.TOOLBOX_SIZE_ALLOCATE_ID) - - toolboxHt = self.toolbox.size_request()[1] - self.vh = gtk.gdk.screen_height()-(self.thumbTrayHt+toolboxHt+self.controlBarHt) - self.vw = int(self.vh/.75) - self.letterBoxW = (gtk.gdk.screen_width() - self.vw)/2 - self.letterBoxVW = (self.vw/2)-(self.inset*2) - self.letterBoxVH = int(self.letterBoxVW*.75) - - self.setUpWindows() - - #now that we know how big the toolbox is, we can layout more - gobject.idle_add( self.layout ) - - - def layout( self ): - self.mainBox = gtk.VBox() - self.ca.set_canvas(self.mainBox) - - topBox = gtk.HBox() - self.mainBox.pack_start(topBox, expand=True) - - leftFill = gtk.VBox() - leftFill.set_size_request( self.letterBoxW, -1 ) - self.leftFillBox = gtk.EventBox( ) - self.leftFillBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - leftFill.add( self.leftFillBox ) - - topBox.pack_start( leftFill, expand=True ) - - centerVBox = gtk.VBox() - centerVBox.modify_bg(gtk.STATE_NORMAL, Constants.colorBlack.gColor) - topBox.pack_start( centerVBox, expand=True ) - self.centerBox = gtk.EventBox() - self.centerBox.set_size_request(self.vw, -1) - self.centerBox.modify_bg(gtk.STATE_NORMAL, Constants.colorBlack.gColor) - centerVBox.pack_start( self.centerBox, expand=True ) - centerSizer = gtk.VBox() - centerSizer.set_size_request(self.vw, -1) - centerSizer.modify_bg(gtk.STATE_NORMAL, Constants.colorBlack.gColor) - self.centerBox.add(centerSizer) - - self.bottomCenter = gtk.EventBox() - self.bottomCenter.modify_bg(gtk.STATE_NORMAL, Constants.colorBlack.gColor) - self.bottomCenter.set_size_request(self.vw, self.controlBarHt) - centerVBox.pack_start( self.bottomCenter, expand=False ) - - #into the center box we can put this guy... - self.backgdCanvasBox = gtk.VBox() - self.backgdCanvasBox.modify_bg(gtk.STATE_NORMAL, Constants.colorBlack.gColor) - self.backgdCanvasBox.set_size_request(self.vw, -1) - self.backgdCanvas = PhotoCanvas() - self.backgdCanvas.set_size_request(self.vw, self.vh) - self.backgdCanvasBox.pack_start( self.backgdCanvas, expand=False ) - - #or this guy... - self.infoBox = gtk.EventBox() - self.infoBox.modify_bg( gtk.STATE_NORMAL, Constants.colorButton.gColor ) - iinfoBox = gtk.VBox(spacing=self.inset) - self.infoBox.add( iinfoBox ) - iinfoBox.set_size_request(self.vw, -1) - iinfoBox.set_border_width(self.inset) - - rightFill = gtk.VBox() - rightFill.set_size_request( self.letterBoxW, -1 ) - rightFillBox = gtk.EventBox() - rightFillBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - rightFill.add( rightFillBox ) - topBox.pack_start( rightFill, expand=True ) - - #info box innards: - self.infoBoxTop = gtk.HBox() - iinfoBox.pack_start( self.infoBoxTop, expand=True ) - self.infoBoxTopLeft = gtk.VBox(spacing=self.inset) - self.infoBoxTop.pack_start( self.infoBoxTopLeft ) - self.infoBoxTopRight = gtk.VBox() - self.infoBoxTopRight.set_size_request(self.letterBoxVW, -1) - self.infoBoxTop.pack_start( self.infoBoxTopRight ) - - self.namePanel = gtk.HBox() - leftInfBalance = gtk.VBox() - self.nameLabel = gtk.Label("<b><span foreground='white'>"+Constants.istrTitle+"</span></b>") - self.nameLabel.set_use_markup( True ) - self.namePanel.pack_start( self.nameLabel, expand=False, padding=self.inset ) - self.nameLabel.set_alignment(0, .5) - self.nameTextfield = gtk.Entry(140) - self.nameTextfield.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) - self.nameTextfield.connect('changed', self._nameTextfieldEditedCb ) - self.nameTextfield.set_alignment(0) - self.nameTextfield.set_size_request( -1, self.controlBarHt-(self.inset*2) ) - self.namePanel.pack_start(self.nameTextfield) - - self.photographerPanel = gtk.VBox(spacing=self.inset) - self.infoBoxTopLeft.pack_start(self.photographerPanel, expand=False) - photographerLabel = gtk.Label("<b>" + Constants.istrRecorder + "</b>") - photographerLabel.set_use_markup( True ) - self.photographerPanel.pack_start(photographerLabel, expand=False) - photographerLabel.set_alignment(0, .5) - photoNamePanel = gtk.HBox(spacing=self.inset) - self.photographerPanel.pack_start(photoNamePanel) - - self.photoXoPanel = xoPanel() - photoNamePanel.pack_start( self.photoXoPanel, expand=False ) - self.photoXoPanel.set_size_request( 40, 40 ) - - self.photographerNameLabel = gtk.Label("") - self.photographerNameLabel.set_alignment(0, .5) - photoNamePanel.pack_start(self.photographerNameLabel) - - self.datePanel = gtk.HBox(spacing=self.inset) - self.infoBoxTopLeft.pack_start(self.datePanel, expand=False) - dateLabel = gtk.Label("<b>"+Constants.istrDate+"</b>") - dateLabel.set_use_markup(True) - self.datePanel.pack_start(dateLabel, expand=False) - self.dateDateLabel = gtk.Label("") - self.dateDateLabel.set_alignment(0, .5) - self.datePanel.pack_start(self.dateDateLabel) - - self.tagsPanel = gtk.VBox(spacing=self.inset) - tagsLabel = gtk.Label("<b>"+Constants.istrTags+"</b>") - tagsLabel.set_use_markup(True) - tagsLabel.set_alignment(0, .5) - self.tagsPanel.pack_start(tagsLabel, expand=False) - self.tagsBuffer = gtk.TextBuffer() - self.tagsBuffer.connect('changed', self._tagsBufferEditedCb) - self.tagsField = gtk.TextView(self.tagsBuffer) - self.tagsField.set_size_request( 100, 100 ) - self.tagsPanel.pack_start(self.tagsField, expand=True) - self.infoBoxTopLeft.pack_start(self.tagsPanel, expand=True) - - infoBotBox = gtk.HBox() - infoBotBox.set_size_request( -1, self.pgdh+self.inset ) - iinfoBox.pack_start(infoBotBox, expand=False) - - thumbnailsEventBox = gtk.EventBox() - thumbnailsEventBox.set_size_request( -1, self.thumbTrayHt ) - thumbnailsBox = gtk.HBox( ) - thumbnailsEventBox.add( thumbnailsBox ) - - self.thumbTray = HTray() - self.thumbTray.set_size_request( -1, self.thumbTrayHt ) - self.mainBox.pack_end( self.thumbTray, expand=False ) - self.thumbTray.show() - - self.CENTER_SIZE_ALLOCATE_ID = self.centerBox.connect_after("size-allocate", self._centerSizeAllocateCb) - self.ca.show_all() - - - def _centerSizeAllocateCb( self, widget, event ): - #initial setup of the panels - self.centerBox.disconnect(self.CENTER_SIZE_ALLOCATE_ID) - self.centerBoxPos = self.centerBox.translate_coordinates( self.ca, 0, 0 ) - - centerKid = self.centerBox.get_child() - if (centerKid != None): - self.centerBox.remove( centerKid ) - - self.centered = True - self.setUp() - - - def _mapEventCb( self, widget, event ): - #when your parent window is ready, turn on the feed of live video - self.liveVideoWindow.disconnect(self.MAP_EVENT_ID) - self.mapped = True - self.setUp() - - - def setUp( self ): - if (self.mapped and self.centered and not self.setup): - self.setup = True - - #set correct window sizes - self.setUpWindowsSizes() - - #listen for ctrl+c & game key buttons - self.ca.connect('key-press-event', self._keyPressEventCb) - #overlay widgets can go away after they've been on screen for a while - self.HIDE_WIDGET_TIMEOUT_ID = 0 - self.hiddenWidgets = False - self.resetWidgetFadeTimer() - self.showLiveVideoTags() - - self.photoToolbar.set_sensitive( True ) - self.videoToolbar.set_sensitive( True ) - self.audioToolbar.set_sensitive( True ) - - #initialize the app with the default thumbs - self.ca.m.setupMode( self.ca.m.MODE, False ) - - gobject.idle_add( self.finalSetUp ) - - - def finalSetUp( self ): - self.LAUNCHING = False - self.ACTIVE = self.ca.get_property( "visible" ) - self.updateVideoComponents() - - if (self.ACTIVE): - self.ca.glive.play() - - - def setUpWindows( self ): - #image windows - self.windowStack = [] - - #live video windows - self.livePhotoWindow = gtk.Window() - self.livePhotoWindow.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - self.livePhotoWindow.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) - self.addToWindowStack( self.livePhotoWindow, self.ca ) - self.livePhotoCanvas = PhotoCanvas() - self.livePhotoWindow.add(self.livePhotoCanvas) - self.livePhotoWindow.connect("button_release_event", self._mediaClickedForPlayback) - self.livePhotoWindow.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) - self.livePhotoWindow.connect("visibility-notify-event", self._visibleNotifyCb) - - #video playback windows - self.playOggWindow = PlayVideoWindow(Constants.colorBlack.gColor) - self.addToWindowStack( self.playOggWindow, self.windowStack[len(self.windowStack)-1] ) - #self.playOggWindow.set_gplay(self.ca.gplay) - self.ca.gplay.window = self.playOggWindow - self.playOggWindow.set_events(gtk.gdk.BUTTON_RELEASE_MASK) - self.playOggWindow.connect("button_release_event", self._mediaClickedForPlayback) - self.playOggWindow.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) - self.playOggWindow.connect("visibility-notify-event", self._visibleNotifyCb) - - #border behind - self.pipBgdWindow = gtk.Window() - self.pipBgdWindow.modify_bg( gtk.STATE_NORMAL, Constants.colorWhite.gColor ) - self.pipBgdWindow.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorWhite.gColor ) - self.addToWindowStack( self.pipBgdWindow, self.windowStack[len(self.windowStack)-1] ) - - self.liveVideoWindow = LiveVideoWindow(Constants.colorBlack.gColor) - self.addToWindowStack( self.liveVideoWindow, self.windowStack[len(self.windowStack)-1] ) - self.liveVideoWindow.set_glive(self.ca.glive) - self.liveVideoWindow.set_events(gtk.gdk.BUTTON_RELEASE_MASK) - self.liveVideoWindow.connect("button_release_event", self._liveButtonReleaseCb) - self.liveVideoWindow.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) - self.liveVideoWindow.connect("visibility-notify-event", self._visibleNotifyCb) - - self.slowLiveVideoWindow = SlowLiveVideoWindow(Constants.colorBlack.gColor) - self.addToWindowStack( self.slowLiveVideoWindow, self.windowStack[len(self.windowStack)-1] ) - self.slowLiveVideoWindow.set_glivex(self.ca.glivex) - self.slowLiveVideoWindow.set_events(gtk.gdk.BUTTON_RELEASE_MASK) - self.slowLiveVideoWindow.connect("button_release_event", self._returnButtonReleaseCb) - self.slowLiveVideoWindow.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) - self.slowLiveVideoWindow.connect("visibility-notify-event", self._visibleNotifyCb) - - self.recordWindow = RecordWindow(self) - self.addToWindowStack( self.recordWindow, self.windowStack[len(self.windowStack)-1] ) - - self.progressWindow = ProgressWindow(self) - self.addToWindowStack( self.progressWindow, self.windowStack[len(self.windowStack)-1] ) - - self.maxWindow = gtk.Window() - self.maxWindow.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - self.maxWindow.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) - maxButton = MaxButton(self) - self.maxWindow.add( maxButton ) - self.addToWindowStack( self.maxWindow, self.windowStack[len(self.windowStack)-1] ) - - self.scrubWindow = ScrubberWindow(self) - self.addToWindowStack( self.scrubWindow, self.windowStack[len(self.windowStack)-1] ) - - self.infWindow = gtk.Window() - self.infWindow.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - self.infWindow.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) - infButton= InfButton(self) - self.infWindow.add(infButton) - self.addToWindowStack( self.infWindow, self.windowStack[len(self.windowStack)-1] ) - - self.hideAllWindows() - self.MAP_EVENT_ID = self.liveVideoWindow.connect_after("map-event", self._mapEventCb) - for i in range (0, len(self.windowStack)): -# self.windowStack[i].add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) -# self.windowStack[i].connect("visibility-notify-event", self._visibleNotifyCb) - self.windowStack[i].show_all() - - - def _visibleNotifyCb( self, widget, event ): - - if (self.LAUNCHING): - return - - temp_ACTIVE = True - - if (event.state == gtk.gdk.VISIBILITY_FULLY_OBSCURED): - - if (not self.FULLSCREEN): - if (widget == self.ca): - temp_ACTIVE = False - - else: - if (self.ca.m.MODE == Constants.MODE_PHOTO): - if (not self.LIVEMODE and widget == self.livePhotoWindow): - temp_ACTIVE = False - if ( self.LIVEMODE and widget == self.liveVideoWindow): - temp_ACTIVE = False - - if (self.ca.m.MODE == Constants.MODE_VIDEO): - if (not self.LIVEMODE and widget == self.playOggWindow): - temp_ACTIVE = False - if ( self.LIVEMODE and widget == self.liveVideoWindow): - temp_ACTIVE = False - - - if (temp_ACTIVE != self.ACTIVE): - self.ACTIVE = temp_ACTIVE - if (self.ACTIVE): - self.ca.restartPipes() - else: - self.ca.stopPipes() - - - def setUpWindowsSizes( self ): - pipDim = self.getPipDim(False) - eyeDim = self.getEyeDim(False) - imgDim = self.getImgDim( False ) - pgdDim = self.getPgdDim( False ) - maxDim = self.getMaxDim( False ) - prgDim = self.getPrgDim( False ) - infDim = self.getInfDim( False ) - self.livePhotoWindow.resize( imgDim[0], imgDim[1] ) - self.pipBgdWindow.resize( pgdDim[0], pgdDim[1] ) - self.liveVideoWindow.resize( imgDim[0], imgDim[1] ) - self.playOggWindow.resize( imgDim[0], imgDim[1] ) - self.recordWindow.resize( eyeDim[0], eyeDim[1] ) - self.maxWindow.resize( maxDim[0], maxDim[1] ) - self.progressWindow.resize( prgDim[0], prgDim[1] ) - self.infWindow.resize( infDim[0], infDim[1] ) - - - def _toolbarChangeCb( self, tbox, num ): - if (num != 0) and (self.ca.m.RECORDING or self.ca.m.UPDATING): - self.toolbox.set_current_toolbar( self.ca.m.MODE+1 ) - else: - num = num - 1 #offset the default activity tab - if (num == Constants.MODE_PHOTO) and (self.ca.m.MODE != Constants.MODE_PHOTO): - self.ca.m.doPhotoMode() - elif(num == Constants.MODE_VIDEO) and (self.ca.m.MODE != Constants.MODE_VIDEO): - self.ca.m.doVideoMode() - elif(num == Constants.MODE_AUDIO) and (self.ca.m.MODE != Constants.MODE_AUDIO): - self.ca.m.doAudioMode() - - - def addToWindowStack( self, win, parent ): - self.windowStack.append( win ) - win.set_transient_for( parent ) - win.set_type_hint( gtk.gdk.WINDOW_TYPE_HINT_DIALOG ) - win.set_decorated( False ) - win.set_focus_on_map( False ) - win.set_property("accept-focus", False) - - - def resetWidgetFadeTimer( self ): - #only show the clutter when the mouse moves - self.mx = -1 - self.my = -1 - self.hideWidgetsTimer = time.time() - if (self.hiddenWidgets): - self.showWidgets() - self.hiddenWidgets = False - - #remove, then add - self.doMouseListener( False ) - if (self.HIDE_WIDGET_TIMEOUT_ID != 0): - gobject.source_remove( self.HIDE_WIDGET_TIMEOUT_ID) - - self.HIDE_WIDGET_TIMEOUT_ID = gobject.timeout_add( 500, self._mouseMightaMovedCb ) - - - def doMouseListener( self, listen ): - if (listen): - self.resetWidgetFadeTimer() - else: - if (self.HIDE_WIDGET_TIMEOUT_ID != None): - if (self.HIDE_WIDGET_TIMEOUT_ID != 0): - gobject.source_remove( self.HIDE_WIDGET_TIMEOUT_ID ) - - - def hideWidgets( self ): - self.moveWinOffscreen( self.maxWindow ) - self.moveWinOffscreen( self.pipBgdWindow ) - self.moveWinOffscreen( self.infWindow ) - self.moveWinOffscreen( self.slowLiveVideoWindow ) - - if (self.FULLSCREEN): - self.moveWinOffscreen( self.recordWindow ) - self.moveWinOffscreen( self.progressWindow ) - self.moveWinOffscreen( self.scrubWindow ) - - if (self.ca.m.MODE == Constants.MODE_PHOTO): - if (not self.LIVEMODE): - self.moveWinOffscreen( self.liveVideoWindow ) - elif (self.ca.m.MODE == Constants.MODE_VIDEO): - if (not self.LIVEMODE): - self.moveWinOffscreen( self.liveVideoWindow ) - elif (self.ca.m.MODE == Constants.MODE_AUDIO): - if (not self.LIVEMODE): - self.moveWinOffscreen( self.liveVideoWindow ) - self.LAST_MODE = -1 - - - def _mouseMightaMovedCb( self ): - x, y = self.ca.get_pointer() - passedTime = 0 - - if (x != self.mx or y != self.my): - self.hideWidgetsTimer = time.time() - if (self.hiddenWidgets): - self.showWidgets() - self.hiddenWidgets = False - else: - passedTime = time.time() - self.hideWidgetsTimer - - if (self.ca.m.RECORDING): - self.hideWidgetsTimer = time.time() - passedTime = 0 - - if (passedTime >= 3): - if (not self.hiddenWidgets): - if (self.mouseInWidget(x,y)): - self.hideWidgetsTimer = time.time() - elif (self.RECD_INFO_ON): - self.hideWidgetsTimer = time.time() - elif (self.UPDATE_TIMER_ID != 0): - self.hideWidgetsTimer = time.time() - else: - self.hideWidgets() - self.hiddenWidgets = True - - self.mx = x - self.my = y - return True - - - def mouseInWidget( self, mx, my ): - if (self.ca.m.MODE != Constants.MODE_AUDIO): - if (self.inWidget( mx, my, self.getLoc("max", self.FULLSCREEN), self.getDim("max", self.FULLSCREEN))): - return True - - if (not self.LIVEMODE): - if (self.inWidget( mx, my, self.getLoc("pgd", self.FULLSCREEN), self.getDim("pgd", self.FULLSCREEN))): - return True - - if (self.inWidget( mx, my, self.getLoc("inb", self.FULLSCREEN), self.getDim("inb", self.FULLSCREEN))): - return True - - if (self.inWidget( mx, my, self.getLoc("prg", self.FULLSCREEN), self.getDim("prg", self.FULLSCREEN))): - return True - - if (self.inWidget( mx, my, self.getLoc("inf", self.FULLSCREEN), self.getDim("inf", self.FULLSCREEN))): - return True - - if (self.LIVEMODE): - if (self.inWidget( mx, my, self.getLoc("eye", self.FULLSCREEN), self.getDim("eye", self.FULLSCREEN))): - return True - - return False - - - def _mediaClickedForPlayback(self, widget, event): - if (not self.LIVEMODE): - if (self.shownRecd != None): - if (self.ca.m.MODE != Constants.MODE_PHOTO): - self.showThumbSelection( self.shownRecd ) - - - def inWidget( self, mx, my, loc, dim ): - if ( (mx > loc[0]) and (my > loc[1]) ): - if ( (mx < loc[0]+dim[0]) and (my < loc[1]+dim[1]) ): - return True - - - def _nameTextfieldEditedCb(self, widget): - if (self.shownRecd != None): - if (self.nameTextfield.get_text() != self.shownRecd.title): - self.shownRecd.setTitle( self.nameTextfield.get_text() ) - - - def _tagsBufferEditedCb(self, widget): - if (self.shownRecd != None): - txt = self.tagsBuffer.get_text( self.tagsBuffer.get_start_iter(), self.tagsBuffer.get_end_iter() ) - if (txt != self.shownRecd.tags): - self.shownRecd.setTags( txt ) - - - def _keyPressEventCb( self, widget, event): - #todo: trac #4144 - - self.resetWidgetFadeTimer() - - #we listen here for CTRL+C events and game keys, and pass on events to gtk.Entry fields - keyname = gtk.gdk.keyval_name(event.keyval) - - if (keyname == 'KP_Page_Up'): #O, up - if (self.LIVEMODE): - if (not self.ca.m.UPDATING): - self.doShutter() - else: - if (self.COUNTINGDOWN): - self.doShutter() - else: - if (self.ca.m.MODE == Constants.MODE_PHOTO): - self.resumeLiveVideo() - else: - self.resumePlayLiveVideo() - elif (keyname == 'KP_Page_Down'): #x, down - if (not self.ca.m.UPDATING and not self.ca.m.RECORDING): - self.ca.m.showLastThumb() - elif (keyname == 'KP_Home'): #square, left - if (not self.ca.m.UPDATING and not self.ca.m.RECORDING and not self.LIVEMODE): - self.ca.m.showPrevThumb( self.shownRecd ) - elif (keyname == 'KP_End'): #check, right - if (not self.ca.m.UPDATING and not self.ca.m.RECORDING and not self.LIVEMODE): - self.ca.m.showNextThumb( self.shownRecd ) - elif (keyname == 'c' and event.state == gtk.gdk.CONTROL_MASK): - if (self.shownRecd != None): - self.copyToClipboard( self.shownRecd ) - elif (keyname == 'Escape'): - if (self.FULLSCREEN): - self.FULLSCREEN = False - if (self.RECD_INFO_ON): - self.infoButtonClicked() - else: - self.updateVideoComponents() - elif (keyname == 'i' and event.state == gtk.gdk.CONTROL_MASK): - if (not self.LIVEMODE): - self.infoButtonClicked() - - return False - - - def copyToClipboard( self, recd ): - if (recd.isClipboardCopyable( )): - tmpImgPath = self.doClipboardCopyStart( recd ) - gtk.Clipboard().set_with_data( [('text/uri-list', 0, 0)], self._clipboardGetFuncCb, self._clipboardClearFuncCb, tmpImgPath ) - return True + dim_THUMB_WIDTH = 108 + dim_THUMB_HEIGHT = 81 + dim_INSET = 10 + dim_PIPW = 160 + dim_PIPH = 120 #pipBorder + dim_PIP_BORDER = 4 + dim_PGDW = dim_PIPW + (dim_PIP_BORDER*2) + dim_PGDH = dim_PIPH + (dim_PIP_BORDER*2) + dim_CONTROLBAR_HT = 55 + + def __init__( self, pca ): + self.ca = pca + self.ACTIVE = False + self.LAUNCHING = True + self.ca.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) + self.ca.connect("visibility-notify-event", self._visibleNotifyCb) + + self.inset = self.__class__.dim_INSET + self.pgdh = self.__class__.dim_PGDH + self.pgdw = self.__class__.dim_PGDW + self.thumbTrayHt = 150 #todo: get sugar constant here + self.thumbSvgW = 124 + self.thumbSvgH = 124 + self.maxw = 49 + self.maxh = 49 + self.controlBarHt = 60 + self.recordButtWd = 55 + self.pipw = self.__class__.dim_PIPW + self.piph = self.__class__.dim_PIPH + + #ui modes + + # True when we're in full-screen mode, False otherwise + self.FULLSCREEN = False + + # True when we're showing live video feed in the primary screen + # area, False otherwise (even when we are still showing live video + # in a p-i-p) + self.LIVEMODE = True + + self.LAST_MODE = -1 + self.LAST_FULLSCREEN = False + self.LAST_LIVE = True + self.LAST_MESHING = False + self.LAST_RECD_INFO = False + self.LAST_TRANSCODING = False + self.TRANSCODING = False + self.MESHING = False + + # RECD_INFO_ON is True when the 'info' for a recording is being + # display on-screen (who recorded it, tags, etc), and False otherwise. + self.RECD_INFO_ON = False + + self.UPDATE_DURATION_ID = 0 + self.UPDATE_TIMER_ID = 0 + self.COUNTINGDOWN = False + + #init + self.mapped = False + self.centered = False + self.setup = False + + #prep for when to show + self.shownRecd = None + + #this includes the default sharing tab + self.toolbox = activity.ActivityToolbox(self.ca) + self.ca.set_toolbox(self.toolbox) + self.photoToolbar = PhotoToolbar() + self.photoToolbar.set_sensitive( False ) + self.toolbox.add_toolbar( Constants.istrPhoto, self.photoToolbar ) + self.videoToolbar = VideoToolbar() + self.videoToolbar.set_sensitive( False ) + self.toolbox.add_toolbar( Constants.istrVideo, self.videoToolbar ) + self.audioToolbar = AudioToolbar() + self.audioToolbar.set_sensitive( False ) + self.toolbox.add_toolbar( Constants.istrAudio, self.audioToolbar ) + self.tbars = {Constants.MODE_PHOTO:self.photoToolbar,Constants.MODE_VIDEO:self.videoToolbar,Constants.MODE_AUDIO:self.audioToolbar} + self.toolbox.set_current_toolbar(self.ca.m.MODE+1) + + self.toolbox.remove(self.toolbox._separator) + #taken directly from toolbox.py b/c I don't know how to mod the hongry hippo + separator = hippo.Canvas() + box = hippo.CanvasBox( + border_color=Constants.colorBlack.get_int(), + background_color=Constants.colorBlack.get_int(), + box_height=style.TOOLBOX_SEPARATOR_HEIGHT, + border_bottom=style.LINE_WIDTH) + separator.set_root(box) + self.toolbox.pack_start(separator, False) + self.toolbox.separator = separator + + self.TOOLBOX_SIZE_ALLOCATE_ID = self.toolbox.connect_after("size-allocate", self._toolboxSizeAllocateCb) + self.toolbox._notebook.set_property("can-focus", False) + self.toolbox.connect("current-toolbar-changed", self._toolbarChangeCb) + self.toolbox.show_all() + + def serialize(self): + data = {} + data['photo_timer'] = self.photoToolbar.timerCb.combo.get_active() + data['video_timer'] = self.videoToolbar.timerCb.combo.get_active() + data['video_duration'] = self.videoToolbar.durCb.combo.get_active() + data['video_quality'] = self.videoToolbar.quality.combo.get_active() + data['audio_timer'] = self.audioToolbar.timerCb.combo.get_active() + data['audio_duration'] = self.audioToolbar.durCb.combo.get_active() + return data + + def deserialize(self, data): + self.photoToolbar.timerCb.combo.set_active(data.get('photo_timer', 0)) + self.videoToolbar.timerCb.combo.set_active(data.get('video_timer', 0)) + self.videoToolbar.durCb.combo.set_active(data.get('video_duration', 0)) + self.videoToolbar.quality.combo.set_active(data.get('video_quality', 0)) + self.audioToolbar.timerCb.combo.set_active(data.get('audio_timer', 0)) + self.audioToolbar.durCb.combo.set_active(data.get('audio_duration')) + + def _toolboxSizeAllocateCb( self, widget, event ): + self.toolbox.disconnect( self.TOOLBOX_SIZE_ALLOCATE_ID) + + toolboxHt = self.toolbox.size_request()[1] + self.vh = gtk.gdk.screen_height()-(self.thumbTrayHt+toolboxHt+self.controlBarHt) + self.vw = int(self.vh/.75) + self.letterBoxW = (gtk.gdk.screen_width() - self.vw)/2 + self.letterBoxVW = (self.vw/2)-(self.inset*2) + self.letterBoxVH = int(self.letterBoxVW*.75) + + self.setUpWindows() + + #now that we know how big the toolbox is, we can layout more + gobject.idle_add( self.layout ) + + + def layout( self ): + self.mainBox = gtk.VBox() + self.ca.set_canvas(self.mainBox) + + topBox = gtk.HBox() + self.mainBox.pack_start(topBox, expand=True) + + leftFill = gtk.VBox() + leftFill.set_size_request( self.letterBoxW, -1 ) + self.leftFillBox = gtk.EventBox( ) + self.leftFillBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + leftFill.add( self.leftFillBox ) + + topBox.pack_start( leftFill, expand=True ) + + centerVBox = gtk.VBox() + centerVBox.modify_bg(gtk.STATE_NORMAL, Constants.colorBlack.gColor) + topBox.pack_start( centerVBox, expand=True ) + self.centerBox = gtk.EventBox() + self.centerBox.set_size_request(self.vw, -1) + self.centerBox.modify_bg(gtk.STATE_NORMAL, Constants.colorBlack.gColor) + centerVBox.pack_start( self.centerBox, expand=True ) + centerSizer = gtk.VBox() + centerSizer.set_size_request(self.vw, -1) + centerSizer.modify_bg(gtk.STATE_NORMAL, Constants.colorBlack.gColor) + self.centerBox.add(centerSizer) + + self.bottomCenter = gtk.EventBox() + self.bottomCenter.modify_bg(gtk.STATE_NORMAL, Constants.colorBlack.gColor) + self.bottomCenter.set_size_request(self.vw, self.controlBarHt) + centerVBox.pack_start( self.bottomCenter, expand=False ) + + #into the center box we can put this guy... + self.backgdCanvasBox = gtk.VBox() + self.backgdCanvasBox.modify_bg(gtk.STATE_NORMAL, Constants.colorBlack.gColor) + self.backgdCanvasBox.set_size_request(self.vw, -1) + self.backgdCanvas = PhotoCanvas() + self.backgdCanvas.set_size_request(self.vw, self.vh) + self.backgdCanvasBox.pack_start( self.backgdCanvas, expand=False ) + + #or this guy... + self.infoBox = gtk.EventBox() + self.infoBox.modify_bg( gtk.STATE_NORMAL, Constants.colorButton.gColor ) + iinfoBox = gtk.VBox(spacing=self.inset) + self.infoBox.add( iinfoBox ) + iinfoBox.set_size_request(self.vw, -1) + iinfoBox.set_border_width(self.inset) + + rightFill = gtk.VBox() + rightFill.set_size_request( self.letterBoxW, -1 ) + rightFillBox = gtk.EventBox() + rightFillBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + rightFill.add( rightFillBox ) + topBox.pack_start( rightFill, expand=True ) + + #info box innards: + self.infoBoxTop = gtk.HBox() + iinfoBox.pack_start( self.infoBoxTop, expand=True ) + self.infoBoxTopLeft = gtk.VBox(spacing=self.inset) + self.infoBoxTop.pack_start( self.infoBoxTopLeft ) + self.infoBoxTopRight = gtk.VBox() + self.infoBoxTopRight.set_size_request(self.letterBoxVW, -1) + self.infoBoxTop.pack_start( self.infoBoxTopRight ) + + self.namePanel = gtk.HBox() + leftInfBalance = gtk.VBox() + self.nameLabel = gtk.Label("<b><span foreground='white'>"+Constants.istrTitle+"</span></b>") + self.nameLabel.set_use_markup( True ) + self.namePanel.pack_start( self.nameLabel, expand=False, padding=self.inset ) + self.nameLabel.set_alignment(0, .5) + self.nameTextfield = gtk.Entry(140) + self.nameTextfield.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) + self.nameTextfield.connect('changed', self._nameTextfieldEditedCb ) + self.nameTextfield.set_alignment(0) + self.nameTextfield.set_size_request( -1, self.controlBarHt-(self.inset*2) ) + self.namePanel.pack_start(self.nameTextfield) + + self.photographerPanel = gtk.VBox(spacing=self.inset) + self.infoBoxTopLeft.pack_start(self.photographerPanel, expand=False) + photographerLabel = gtk.Label("<b>" + Constants.istrRecorder + "</b>") + photographerLabel.set_use_markup( True ) + self.photographerPanel.pack_start(photographerLabel, expand=False) + photographerLabel.set_alignment(0, .5) + photoNamePanel = gtk.HBox(spacing=self.inset) + self.photographerPanel.pack_start(photoNamePanel) + + self.photoXoPanel = xoPanel() + photoNamePanel.pack_start( self.photoXoPanel, expand=False ) + self.photoXoPanel.set_size_request( 40, 40 ) + + self.photographerNameLabel = gtk.Label("") + self.photographerNameLabel.set_alignment(0, .5) + photoNamePanel.pack_start(self.photographerNameLabel) + + self.datePanel = gtk.HBox(spacing=self.inset) + self.infoBoxTopLeft.pack_start(self.datePanel, expand=False) + dateLabel = gtk.Label("<b>"+Constants.istrDate+"</b>") + dateLabel.set_use_markup(True) + self.datePanel.pack_start(dateLabel, expand=False) + self.dateDateLabel = gtk.Label("") + self.dateDateLabel.set_alignment(0, .5) + self.datePanel.pack_start(self.dateDateLabel) + + self.tagsPanel = gtk.VBox(spacing=self.inset) + tagsLabel = gtk.Label("<b>"+Constants.istrTags+"</b>") + tagsLabel.set_use_markup(True) + tagsLabel.set_alignment(0, .5) + self.tagsPanel.pack_start(tagsLabel, expand=False) + self.tagsBuffer = gtk.TextBuffer() + self.tagsBuffer.connect('changed', self._tagsBufferEditedCb) + self.tagsField = gtk.TextView(self.tagsBuffer) + self.tagsField.set_size_request( 100, 100 ) + self.tagsPanel.pack_start(self.tagsField, expand=True) + self.infoBoxTopLeft.pack_start(self.tagsPanel, expand=True) + + infoBotBox = gtk.HBox() + infoBotBox.set_size_request( -1, self.pgdh+self.inset ) + iinfoBox.pack_start(infoBotBox, expand=False) + + thumbnailsEventBox = gtk.EventBox() + thumbnailsEventBox.set_size_request( -1, self.thumbTrayHt ) + thumbnailsBox = gtk.HBox( ) + thumbnailsEventBox.add( thumbnailsBox ) + + self.thumbTray = HTray() + self.thumbTray.set_size_request( -1, self.thumbTrayHt ) + self.mainBox.pack_end( self.thumbTray, expand=False ) + self.thumbTray.show() + + self.CENTER_SIZE_ALLOCATE_ID = self.centerBox.connect_after("size-allocate", self._centerSizeAllocateCb) + self.ca.show_all() + + + def _centerSizeAllocateCb( self, widget, event ): + #initial setup of the panels + self.centerBox.disconnect(self.CENTER_SIZE_ALLOCATE_ID) + self.centerBoxPos = self.centerBox.translate_coordinates( self.ca, 0, 0 ) + + centerKid = self.centerBox.get_child() + if (centerKid != None): + self.centerBox.remove( centerKid ) + + self.centered = True + self.setUp() + + + def _mapEventCb( self, widget, event ): + #when your parent window is ready, turn on the feed of live video + self.liveVideoWindow.disconnect(self.MAP_EVENT_ID) + self.mapped = True + self.setUp() + + + def setUp( self ): + if (self.mapped and self.centered and not self.setup): + self.setup = True + + #set correct window sizes + self.setUpWindowsSizes() + + #listen for ctrl+c & game key buttons + self.ca.connect('key-press-event', self._keyPressEventCb) + #overlay widgets can go away after they've been on screen for a while + self.HIDE_WIDGET_TIMEOUT_ID = 0 + self.hiddenWidgets = False + self.resetWidgetFadeTimer() + self.showLiveVideoTags() + + self.photoToolbar.set_sensitive( True ) + self.videoToolbar.set_sensitive( True ) + self.audioToolbar.set_sensitive( True ) + + #initialize the app with the default thumbs + self.ca.m.setupMode( self.ca.m.MODE, True ) + + gobject.idle_add( self.finalSetUp ) + + + def finalSetUp( self ): + self.LAUNCHING = False + self.ACTIVE = self.ca.get_property( "visible" ) + self.updateVideoComponents() + + if (self.ACTIVE): + self.ca.glive.play() + + + def setUpWindows( self ): + #image windows + self.windowStack = [] + + #live video windows + self.livePhotoWindow = gtk.Window() + self.livePhotoWindow.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.livePhotoWindow.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) + self.addToWindowStack( self.livePhotoWindow, self.ca ) + self.livePhotoCanvas = PhotoCanvas() + self.livePhotoWindow.add(self.livePhotoCanvas) + self.livePhotoWindow.connect("button_release_event", self._mediaClickedForPlayback) + self.livePhotoWindow.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) + self.livePhotoWindow.connect("visibility-notify-event", self._visibleNotifyCb) + + #video playback windows + self.playOggWindow = PlayVideoWindow(Constants.colorBlack.gColor) + self.addToWindowStack( self.playOggWindow, self.windowStack[len(self.windowStack)-1] ) + #self.playOggWindow.set_gplay(self.ca.gplay) + self.ca.gplay.window = self.playOggWindow + self.playOggWindow.set_events(gtk.gdk.BUTTON_RELEASE_MASK) + self.playOggWindow.connect("button_release_event", self._mediaClickedForPlayback) + self.playOggWindow.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) + self.playOggWindow.connect("visibility-notify-event", self._visibleNotifyCb) + + #border behind + self.pipBgdWindow = gtk.Window() + self.pipBgdWindow.modify_bg( gtk.STATE_NORMAL, Constants.colorWhite.gColor ) + self.pipBgdWindow.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorWhite.gColor ) + self.addToWindowStack( self.pipBgdWindow, self.windowStack[len(self.windowStack)-1] ) + + self.liveVideoWindow = LiveVideoWindow(Constants.colorBlack.gColor) + self.addToWindowStack( self.liveVideoWindow, self.windowStack[len(self.windowStack)-1] ) + self.liveVideoWindow.set_glive(self.ca.glive) + self.liveVideoWindow.set_events(gtk.gdk.BUTTON_RELEASE_MASK) + self.liveVideoWindow.connect("button_release_event", self._liveButtonReleaseCb) + self.liveVideoWindow.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) + self.liveVideoWindow.connect("visibility-notify-event", self._visibleNotifyCb) + + self.recordWindow = RecordWindow(self) + self.addToWindowStack( self.recordWindow, self.windowStack[len(self.windowStack)-1] ) + + self.progressWindow = ProgressWindow(self) + self.addToWindowStack( self.progressWindow, self.windowStack[len(self.windowStack)-1] ) + + self.maxWindow = gtk.Window() + self.maxWindow.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.maxWindow.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) + maxButton = MaxButton(self) + self.maxWindow.add( maxButton ) + self.addToWindowStack( self.maxWindow, self.windowStack[len(self.windowStack)-1] ) + + self.scrubWindow = ScrubberWindow(self) + self.addToWindowStack( self.scrubWindow, self.windowStack[len(self.windowStack)-1] ) + + self.infWindow = gtk.Window() + self.infWindow.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.infWindow.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) + infButton= InfButton(self) + self.infWindow.add(infButton) + self.addToWindowStack( self.infWindow, self.windowStack[len(self.windowStack)-1] ) + + self.hideAllWindows() + self.MAP_EVENT_ID = self.liveVideoWindow.connect_after("map-event", self._mapEventCb) + for i in range (0, len(self.windowStack)): +# self.windowStack[i].add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) +# self.windowStack[i].connect("visibility-notify-event", self._visibleNotifyCb) + self.windowStack[i].show_all() + + + def _visibleNotifyCb( self, widget, event ): + + if (self.LAUNCHING): + return + + temp_ACTIVE = True + + if (event.state == gtk.gdk.VISIBILITY_FULLY_OBSCURED): + + if (not self.FULLSCREEN): + if (widget == self.ca): + temp_ACTIVE = False + + else: + if (self.ca.m.MODE == Constants.MODE_PHOTO): + if (not self.LIVEMODE and widget == self.livePhotoWindow): + temp_ACTIVE = False + if ( self.LIVEMODE and widget == self.liveVideoWindow): + temp_ACTIVE = False + + if (self.ca.m.MODE == Constants.MODE_VIDEO): + if (not self.LIVEMODE and widget == self.playOggWindow): + temp_ACTIVE = False + if ( self.LIVEMODE and widget == self.liveVideoWindow): + temp_ACTIVE = False + + + if (temp_ACTIVE != self.ACTIVE): + self.ACTIVE = temp_ACTIVE + if (self.ACTIVE): + self.ca.restartPipes() + else: + self.ca.stopPipes() + + + def setUpWindowsSizes( self ): + pipDim = self.getPipDim(False) + eyeDim = self.getEyeDim(False) + imgDim = self.getImgDim( False ) + pgdDim = self.getPgdDim( False ) + maxDim = self.getMaxDim( False ) + prgDim = self.getPrgDim( False ) + infDim = self.getInfDim( False ) + self.livePhotoWindow.resize( imgDim[0], imgDim[1] ) + self.pipBgdWindow.resize( pgdDim[0], pgdDim[1] ) + self.liveVideoWindow.resize( imgDim[0], imgDim[1] ) + self.playOggWindow.resize( imgDim[0], imgDim[1] ) + self.recordWindow.resize( eyeDim[0], eyeDim[1] ) + self.maxWindow.resize( maxDim[0], maxDim[1] ) + self.progressWindow.resize( prgDim[0], prgDim[1] ) + self.infWindow.resize( infDim[0], infDim[1] ) + + + def _toolbarChangeCb( self, tbox, num ): + if (num != 0) and (self.ca.m.RECORDING or self.ca.m.UPDATING): + self.toolbox.set_current_toolbar( self.ca.m.MODE+1 ) + else: + num = num - 1 #offset the default activity tab + if (num == Constants.MODE_PHOTO) and (self.ca.m.MODE != Constants.MODE_PHOTO): + self.ca.m.doPhotoMode() + elif(num == Constants.MODE_VIDEO) and (self.ca.m.MODE != Constants.MODE_VIDEO): + self.ca.m.doVideoMode() + elif(num == Constants.MODE_AUDIO) and (self.ca.m.MODE != Constants.MODE_AUDIO): + self.ca.m.doAudioMode() + + + def addToWindowStack( self, win, parent ): + self.windowStack.append( win ) + win.set_transient_for( parent ) + win.set_type_hint( gtk.gdk.WINDOW_TYPE_HINT_DIALOG ) + win.set_decorated( False ) + win.set_focus_on_map( False ) + win.set_property("accept-focus", False) + win.props.destroy_with_parent = True + + + def resetWidgetFadeTimer( self ): + #only show the clutter when the mouse moves + self.mx = -1 + self.my = -1 + self.hideWidgetsTimer = time.time() + if (self.hiddenWidgets): + self.showWidgets() + self.hiddenWidgets = False + + #remove, then add + self.doMouseListener( False ) + if (self.HIDE_WIDGET_TIMEOUT_ID != 0): + gobject.source_remove( self.HIDE_WIDGET_TIMEOUT_ID) + + self.HIDE_WIDGET_TIMEOUT_ID = gobject.timeout_add( 500, self._mouseMightaMovedCb ) + + + def doMouseListener( self, listen ): + if (listen): + self.resetWidgetFadeTimer() + else: + if (self.HIDE_WIDGET_TIMEOUT_ID != None): + if (self.HIDE_WIDGET_TIMEOUT_ID != 0): + gobject.source_remove( self.HIDE_WIDGET_TIMEOUT_ID ) + + + def hideWidgets( self ): + self.moveWinOffscreen( self.maxWindow ) + self.moveWinOffscreen( self.pipBgdWindow ) + self.moveWinOffscreen( self.infWindow ) + + if (self.FULLSCREEN): + self.moveWinOffscreen( self.recordWindow ) + self.moveWinOffscreen( self.progressWindow ) + self.moveWinOffscreen( self.scrubWindow ) + + if (self.ca.m.MODE == Constants.MODE_PHOTO): + if (not self.LIVEMODE): + self.moveWinOffscreen( self.liveVideoWindow ) + elif (self.ca.m.MODE == Constants.MODE_VIDEO): + if (not self.LIVEMODE): + self.moveWinOffscreen( self.liveVideoWindow ) + elif (self.ca.m.MODE == Constants.MODE_AUDIO): + if (not self.LIVEMODE): + self.moveWinOffscreen( self.liveVideoWindow ) + self.LAST_MODE = -1 + + + def _mouseMightaMovedCb( self ): + x, y = self.ca.get_pointer() + passedTime = 0 + + if (x != self.mx or y != self.my): + self.hideWidgetsTimer = time.time() + if (self.hiddenWidgets): + self.showWidgets() + self.hiddenWidgets = False + else: + passedTime = time.time() - self.hideWidgetsTimer + + if (self.ca.m.RECORDING): + self.hideWidgetsTimer = time.time() + passedTime = 0 + + if (passedTime >= 3): + if (not self.hiddenWidgets): + if (self.mouseInWidget(x,y)): + self.hideWidgetsTimer = time.time() + elif (self.RECD_INFO_ON): + self.hideWidgetsTimer = time.time() + elif (self.UPDATE_TIMER_ID != 0): + self.hideWidgetsTimer = time.time() + else: + self.hideWidgets() + self.hiddenWidgets = True + + self.mx = x + self.my = y + return True + + + def mouseInWidget( self, mx, my ): + if (self.ca.m.MODE != Constants.MODE_AUDIO): + if (self.inWidget( mx, my, self.getLoc("max", self.FULLSCREEN), self.getDim("max", self.FULLSCREEN))): + return True + + if (not self.LIVEMODE): + if (self.inWidget( mx, my, self.getLoc("pgd", self.FULLSCREEN), self.getDim("pgd", self.FULLSCREEN))): + return True + + if (self.inWidget( mx, my, self.getLoc("inb", self.FULLSCREEN), self.getDim("inb", self.FULLSCREEN))): + return True + + if (self.inWidget( mx, my, self.getLoc("prg", self.FULLSCREEN), self.getDim("prg", self.FULLSCREEN))): + return True + + if (self.inWidget( mx, my, self.getLoc("inf", self.FULLSCREEN), self.getDim("inf", self.FULLSCREEN))): + return True + + if (self.LIVEMODE): + if (self.inWidget( mx, my, self.getLoc("eye", self.FULLSCREEN), self.getDim("eye", self.FULLSCREEN))): + return True + + return False + + + def _mediaClickedForPlayback(self, widget, event): + if (not self.LIVEMODE): + if (self.shownRecd != None): + if (self.ca.m.MODE != Constants.MODE_PHOTO): + self.showThumbSelection( self.shownRecd ) + + + def inWidget( self, mx, my, loc, dim ): + if ( (mx > loc[0]) and (my > loc[1]) ): + if ( (mx < loc[0]+dim[0]) and (my < loc[1]+dim[1]) ): + return True + + + def _nameTextfieldEditedCb(self, widget): + if (self.shownRecd != None): + if (self.nameTextfield.get_text() != self.shownRecd.title): + self.shownRecd.setTitle( self.nameTextfield.get_text() ) + + + def _tagsBufferEditedCb(self, widget): + if (self.shownRecd != None): + txt = self.tagsBuffer.get_text( self.tagsBuffer.get_start_iter(), self.tagsBuffer.get_end_iter() ) + if (txt != self.shownRecd.tags): + self.shownRecd.setTags( txt ) + + + def _keyPressEventCb( self, widget, event): + #todo: trac #4144 + self.resetWidgetFadeTimer() + + #we listen here for CTRL+C events and game keys, and pass on events to gtk.Entry fields + keyname = gtk.gdk.keyval_name(event.keyval) - def doClipboardCopyStart( self, recd ): - imgPath_s = recd.getMediaFilepath() - if (imgPath_s == None): - record.Record.log.error("doClipboardCopyStart: imgPath_s==None") - return None + if (keyname == 'KP_Page_Up'): #O, up + if (self.LIVEMODE): + if (not self.ca.m.UPDATING): + self.doShutter() + else: + if (self.COUNTINGDOWN): + self.doShutter() + else: + if (self.ca.m.MODE == Constants.MODE_PHOTO): + self.resumeLiveVideo() + else: + self.resumePlayLiveVideo() + elif (keyname == 'KP_Page_Down'): #x, down + if (not self.ca.m.UPDATING and not self.ca.m.RECORDING): + self.ca.m.showLastThumb() + elif (keyname == 'KP_Home'): #square, left + if (not self.ca.m.UPDATING and not self.ca.m.RECORDING and not self.LIVEMODE): + self.ca.m.showPrevThumb( self.shownRecd ) + elif (keyname == 'KP_End'): #check, right + if (not self.ca.m.UPDATING and not self.ca.m.RECORDING and not self.LIVEMODE): + self.ca.m.showNextThumb( self.shownRecd ) + elif (keyname == 'c' and event.state == gtk.gdk.CONTROL_MASK): + if (self.shownRecd != None): + self.copyToClipboard( self.shownRecd ) + elif (keyname == 'Escape'): + if (self.FULLSCREEN): + self.FULLSCREEN = False + if (self.RECD_INFO_ON): + self.infoButtonClicked() + else: + self.updateVideoComponents() + elif (keyname == 'i' and event.state == gtk.gdk.CONTROL_MASK): + if (not self.LIVEMODE): + self.infoButtonClicked() - tmpImgPath = recd.getMediaFilepath() - tmpImgPath = utils.getUniqueFilepath(tmpImgPath, 0) - shutil.copyfile( imgPath_s, tmpImgPath ) - return tmpImgPath + return False - def doClipboardCopyCopy( self, tmpImgPath, selection_data ): - tmpImgUri = "file://" + tmpImgPath - selection_data.set( "text/uri-list", 8, tmpImgUri ) - - - def doClipboardCopyFinish( self, tmpImgPath ): - if (tmpImgPath != None): - if (os.path.exists(tmpImgPath)): - os.remove( tmpImgPath ) - tmpImgPath = None - - - def _clipboardGetFuncCb( self, clipboard, selection_data, info, data): - self.doClipboardCopyCopy( data, selection_data ) - - - def _clipboardClearFuncCb( self, clipboard, data): - self.doClipboardCopyFinish( data ) - - - def showPhoto( self, recd ): - pixbuf = self.getPhotoPixbuf( recd ) - if (pixbuf != None): - #self.shownRecd = recd - - img = _camera.cairo_surface_from_gdk_pixbuf(pixbuf) - self.livePhotoCanvas.setImage( img ) - - self.LIVEMODE = False - self.updateVideoComponents() - - self.showRecdMeta(recd) - - - def getPhotoPixbuf( self, recd ): - pixbuf = None - - downloading = self.ca.requestMeshDownload(recd) - self.MESHING = downloading - - if (not downloading): - self.progressWindow.updateProgress(0, "") - imgPath = recd.getMediaFilepath() - if (not imgPath == None): - if ( os.path.isfile(imgPath) ): - pixbuf = gtk.gdk.pixbuf_new_from_file(imgPath) - - if (pixbuf == None): - #maybe it is not downloaded from the mesh yet... - #but we can show the low res thumb in the interim - pixbuf = recd.getThumbPixbuf() - - return pixbuf + def copyToClipboard( self, recd ): + if (recd.isClipboardCopyable( )): + tmpImgPath = self.doClipboardCopyStart( recd ) + gtk.Clipboard().set_with_data( [('text/uri-list', 0, 0)], self._clipboardGetFuncCb, self._clipboardClearFuncCb, tmpImgPath ) + return True - def showLiveVideoTags( self ): - self.shownRecd = None - self.livePhotoCanvas.setImage( None ) - self.nameTextfield.set_text("") - self.tagsBuffer.set_text("") + def doClipboardCopyStart( self, recd ): + imgPath_s = recd.getMediaFilepath() + if (imgPath_s == None): + record.Record.log.error("doClipboardCopyStart: imgPath_s==None") + return None - self.scrubWindow.removeCallbacks() - self.scrubWindow.reset() - self.MESHING = False - self.progressWindow.updateProgress( 0, "" ) + tmpImgPath = recd.getMediaFilepath() + tmpImgPath = utils.getUniqueFilepath(tmpImgPath, 0) + shutil.copyfile( imgPath_s, tmpImgPath ) + return tmpImgPath + + + def doClipboardCopyCopy( self, tmpImgPath, selection_data ): + tmpImgUri = "file://" + tmpImgPath + selection_data.set( "text/uri-list", 8, tmpImgUri ) + + + def doClipboardCopyFinish( self, tmpImgPath ): + if (tmpImgPath != None): + if (os.path.exists(tmpImgPath)): + os.remove( tmpImgPath ) + tmpImgPath = None + + + def _clipboardGetFuncCb( self, clipboard, selection_data, info, data): + self.doClipboardCopyCopy( data, selection_data ) + + + def _clipboardClearFuncCb( self, clipboard, data): + self.doClipboardCopyFinish( data ) + + + def showPhoto( self, recd ): + pixbuf = self.getPhotoPixbuf( recd ) + if (pixbuf != None): + #self.shownRecd = recd + + img = camerac.cairo_surface_from_gdk_pixbuf(pixbuf) + self.livePhotoCanvas.setImage( img ) + + self.ca.glive.thumb_play() + self.LIVEMODE = False + self.updateVideoComponents() + + self.showRecdMeta(recd) + + + def getPhotoPixbuf( self, recd ): + pixbuf = None + + downloading = self.ca.requestMeshDownload(recd) + self.MESHING = downloading + + if (not downloading): + self.progressWindow.updateProgress(0, "") + imgPath = recd.getMediaFilepath() + if (not imgPath == None): + if ( os.path.isfile(imgPath) ): + pixbuf = gtk.gdk.pixbuf_new_from_file(imgPath) + + if (pixbuf == None): + #maybe it is not downloaded from the mesh yet... + #but we can show the low res thumb in the interim + pixbuf = recd.getThumbPixbuf() + + return pixbuf + + + def showLiveVideoTags( self ): + self.shownRecd = None + self.livePhotoCanvas.setImage( None ) + self.nameTextfield.set_text("") + self.tagsBuffer.set_text("") - self.resetWidgetFadeTimer( ) + self.scrubWindow.removeCallbacks() + self.scrubWindow.reset() + self.MESHING = False + self.progressWindow.updateProgress( 0, "" ) + self.resetWidgetFadeTimer( ) - def updateButtonSensitivities( self ): - switchStuff = ((not self.ca.m.UPDATING) and (not self.ca.m.RECORDING)) - self.photoToolbar.set_sensitive( switchStuff ) - self.videoToolbar.set_sensitive( switchStuff ) - self.audioToolbar.set_sensitive( switchStuff ) - if (not self.COUNTINGDOWN): - if (self.ca.m.UPDATING): - self.ca.ui.setWaitCursor( self.ca.window ) - for i in range (0, len(self.windowStack)): - self.ca.ui.setWaitCursor( self.windowStack[i].window ) - else: - self.ca.ui.setDefaultCursor( self.ca.window ) - for i in range (0, len(self.windowStack)): - self.ca.ui.setDefaultCursor( self.windowStack[i].window ) + def updateButtonSensitivities( self ): + switchStuff = ((not self.ca.m.UPDATING) and (not self.ca.m.RECORDING)) + self.photoToolbar.set_sensitive( switchStuff ) + self.videoToolbar.set_sensitive( switchStuff ) + self.audioToolbar.set_sensitive( switchStuff ) - #display disc is full messages - self.ca.m.updateXoFullStatus() - self.recordWindow.displayDiscFullText(self.ca.m.FULL) - if (self.ca.m.FULL): - self.recordWindow.shutterButton.set_sensitive( False, True) - fullMessage = Constants.istrYourDiskIsFull % {"1":Constants.istrJournal} - self.progressWindow.updateProgress( 1, fullMessage, "gray" ) - else: - self.recordWindow.shutterButton.set_sensitive( not self.ca.m.UPDATING, False ) - if (self.ca.m.RECORDING): - self.recordWindow.shutterButton.doRecordButton() - else: - self.recordWindow.shutterButton.doNormalButton() + if (not self.COUNTINGDOWN): + if (self.ca.m.UPDATING): + self.ca.ui.setWaitCursor( self.ca.window ) + for i in range (0, len(self.windowStack)): + self.ca.ui.setWaitCursor( self.windowStack[i].window ) + else: + self.ca.ui.setDefaultCursor( self.ca.window ) + for i in range (0, len(self.windowStack)): + self.ca.ui.setDefaultCursor( self.windowStack[i].window ) - kids = self.thumbTray.get_children() - for i in range (0, len(kids)): - if (self.ca.m.UPDATING or self.ca.m.RECORDING): - if (kids[i].getButtClickedId() != 0): - kids[i].disconnect( kids[i].getButtClickedId() ) - kids[i].setButtClickedId(0) - else: - if (kids[i].getButtClickedId() == 0): - BUTT_CLICKED_ID = kids[i].connect( "clicked", self._thumbClicked, kids[i].recd ) - kids[i].setButtClickedId(BUTT_CLICKED_ID) + #display disc is full messages + self.ca.m.updateXoFullStatus() + self.recordWindow.displayDiscFullText(self.ca.m.FULL) + if (self.ca.m.FULL): + self.recordWindow.shutterButton.set_sensitive( False, True) + fullMessage = Constants.istrYourDiskIsFull % {"1":Constants.istrJournal} + self.progressWindow.updateProgress( 1, fullMessage, "gray" ) + else: + self.recordWindow.shutterButton.set_sensitive( not self.ca.m.UPDATING, False ) + if (self.ca.m.RECORDING): + self.recordWindow.shutterButton.doRecordButton() + else: + self.recordWindow.shutterButton.doNormalButton() + kids = self.thumbTray.get_children() + for i in range (0, len(kids)): + if (self.ca.m.UPDATING or self.ca.m.RECORDING): + if (kids[i].getButtClickedId() != 0): + kids[i].disconnect( kids[i].getButtClickedId() ) + kids[i].setButtClickedId(0) + else: + if (kids[i].getButtClickedId() == 0): + BUTT_CLICKED_ID = kids[i].connect( "clicked", self._thumbClicked, kids[i].recd ) + kids[i].setButtClickedId(BUTT_CLICKED_ID) - def hideAllWindows( self ): - for i in range (0, len(self.windowStack)): - self.moveWinOffscreen( self.windowStack[i] ) + def hideAllWindows( self ): + for i in range (0, len(self.windowStack)): + self.moveWinOffscreen( self.windowStack[i] ) - def _liveButtonReleaseCb(self, widget, event): - self.resumeLiveVideo() + def _liveButtonReleaseCb(self, widget, event): + self.ca.gplay.stop() + self.ca.glive.play() + self.resumeLiveVideo() - def _returnButtonReleaseCb(self, widget, event): - self.ca.gplay.stop() - self.ca.glivex.stop() - self.ca.glive.play() - self.resumeLiveVideo() + def resumeLiveVideo( self ): + self.livePhotoCanvas.setImage( None ) - def resumeLiveVideo( self ): - self.livePhotoCanvas.setImage( None ) + bottomKid = self.bottomCenter.get_child() + if (bottomKid != None): + self.bottomCenter.remove( bottomKid ) - bottomKid = self.bottomCenter.get_child() - if (bottomKid != None): - self.bottomCenter.remove( bottomKid ) + self.RECD_INFO_ON = False - self.RECD_INFO_ON = False + if (not self.LIVEMODE): + self.ca.m.setUpdating(True) + self.ca.gplay.stop() + self.showLiveVideoTags() + self.LIVEMODE = True + self.updateVideoComponents() + self.ca.m.setUpdating(False) - if (not self.LIVEMODE): - self.ca.m.setUpdating(True) - self.ca.gplay.stop() - self.showLiveVideoTags() - self.LIVEMODE = True - self.updateVideoComponents() - self.ca.m.setUpdating(False) + def _playLiveButtonReleaseCb(self, widget, event): + self.resumePlayLiveVideo() - def _playLiveButtonReleaseCb(self, widget, event): - self.resumePlayLiveVideo() + def resumePlayLiveVideo( self ): + self.ca.gplay.stop() - def resumePlayLiveVideo( self ): - self.ca.gplay.stop() + self.RECD_INFO_ON = False + #if you are big on the screen, don't go changing anything, ok? + if (self.LIVEMODE): + return - self.RECD_INFO_ON = False - #if you are big on the screen, don't go changing anything, ok? - if (self.LIVEMODE): - return + self.showLiveVideoTags() + self.LIVEMODE = True + self.startLiveVideo( False ) + self.updateVideoComponents() - self.showLiveVideoTags() - self.LIVEMODE = True - self.startLiveVideo( False ) - self.updateVideoComponents() + def recordVideo( self ): + self.ca.glive.startRecordingVideo(self.videoToolbar.getQuality()) + self.beginRecordingTimer( ) - def recordVideo( self ): - self.ca.glive.startRecordingVideo( ) - self.beginRecordingTimer( ) + def recordAudio( self ): + self.ca.glive.startRecordingAudio( ) + self.beginRecordingTimer( ) - def recordAudio( self ): - self.ca.glive.startRecordingAudio( ) - self.beginRecordingTimer( ) + def beginRecordingTimer( self ): + self.recTime = time.time() + self.UPDATE_DURATION_ID = gobject.timeout_add( 500, self._updateDurationCb ) - def beginRecordingTimer( self ): - self.recTime = time.time() - self.UPDATE_DURATION_ID = gobject.timeout_add( 500, self._updateDurationCb ) + def _updateDurationCb( self ): + passedTime = time.time() - self.recTime - def _updateDurationCb( self ): - passedTime = time.time() - self.recTime + duration = 10.0 + if (self.ca.m.MODE == Constants.MODE_VIDEO): + duration = self.videoToolbar.getDuration()+0.0 + elif (self.ca.m.MODE == Constants.MODE_AUDIO): + duration = self.audioToolbar.getDuration()+0.0 - duration = 10.0 - if (self.ca.m.MODE == Constants.MODE_VIDEO): - duration = self.videoToolbar.getDuration()+0.0 - elif (self.ca.m.MODE == Constants.MODE_AUDIO): - duration = self.audioToolbar.getDuration()+0.0 + if (passedTime >= duration ): + self.completeCountdown() + self.progressWindow.updateProgress( 1, Constants.istrFinishedRecording ) + if (self.ca.m.RECORDING): + gobject.idle_add( self.doShutter ) - if (passedTime >= duration ): - self.completeCountdown() - self.progressWindow.updateProgress( 1, Constants.istrFinishedRecording ) - if (self.ca.m.RECORDING): - gobject.idle_add( self.doShutter ) + return False + else: + secsRemaining = duration - passedTime + timeRemainStr = Constants.istrSecondsRemaining % {"1":str(int(secsRemaining))} + if (secsRemaining >= 60): + mins = int( secsRemaining/60 ) + secs = int( secsRemaining%60 ) + timeRemainStr = Constants.istrMinutesSecondsRemaining % {"1":str(int(mins)), "2":str(int(secs))} - return False - else: - secsRemaining = duration - passedTime - timeRemainStr = Constants.istrSecondsRemaining % {"1":str(int(secsRemaining))} - if (secsRemaining >= 60): - mins = int( secsRemaining/60 ) - secs = int( secsRemaining%60 ) - timeRemainStr = Constants.istrMinutesSecondsRemaining % {"1":str(int(mins)), "2":str(int(secs))} + self.progressWindow.updateProgress( passedTime/duration, Constants.istrDuration + " " + timeRemainStr ) + return True - self.progressWindow.updateProgress( passedTime/duration, Constants.istrDuration + " " + timeRemainStr ) - return True + def completeCountdown( self ): + if (self.UPDATE_DURATION_ID != 0): + gobject.source_remove( self.UPDATE_DURATION_ID ) + self.UPDATE_DURATION_ID = 0 - def completeCountdown( self ): - if (self.UPDATE_DURATION_ID != 0): - gobject.source_remove( self.UPDATE_DURATION_ID ) - self.UPDATE_DURATION_ID = 0 + def updateModeChange(self): + #this is called when a menubar button is clicked + self.LIVEMODE = True + self.FULLSCREEN = False + self.RECD_INFO_ON = False + self.MESHING = False - def updateModeChange(self): - #this is called when a menubar button is clicked - self.LIVEMODE = True - self.FULLSCREEN = False - self.RECD_INFO_ON = False - self.MESHING = False + self.progressWindow.updateProgress(0, "") - self.progressWindow.updateProgress(0, "") + #set up the x & xv x-ition (if need be) + self.ca.gplay.stop() + self.startLiveVideo( True ) - #set up the x & xv x-ition (if need be) - self.ca.gplay.stop() - self.startLiveVideo( True ) + bottomKid = self.bottomCenter.get_child() + if (bottomKid != None): + self.bottomCenter.remove( bottomKid ) - bottomKid = self.bottomCenter.get_child() - if (bottomKid != None): - self.bottomCenter.remove( bottomKid ) + self.doMouseListener( True ) + self.showLiveVideoTags() + self.LAST_MODE = -1 #force an update + self.updateVideoComponents() + self.resetWidgetFadeTimer() - self.doMouseListener( True ) - self.showLiveVideoTags() - self.LAST_MODE = -1 #force an update - self.updateVideoComponents() - self.resetWidgetFadeTimer() + def startLiveVideo(self, force): + #We need to know which window and which pipe here - def startLiveVideo(self, force): - #We need to know which window and which pipe here + #if returning from another activity, active won't be false and needs to be to get started + if (self.ca.glive.window == self.liveVideoWindow + and self.ca.props.active + and not force): + return - #if returning from another activity, active won't be false and needs to be to get started - if (self.ca.glive.window == self.liveVideoWindow - and self.ca.props.active - and not force): - return + self.liveVideoWindow.set_glive(self.ca.glive) + self.ca.glive.play() - self.liveVideoWindow.set_glive(self.ca.glive) - self.ca.glive.play() + def doFullscreen( self ): + self.FULLSCREEN = not self.FULLSCREEN + self.updateVideoComponents() - def doFullscreen( self ): - self.FULLSCREEN = not self.FULLSCREEN - self.updateVideoComponents() + def moveWinOffscreen( self, win ): + #we move offscreen to resize or else we get flashes on screen, and setting hide() doesn't allow resize & moves + offW = (gtk.gdk.screen_width() + 100) + offH = (gtk.gdk.screen_height() + 100) + self.smartMove(win, offW, offH) - def moveWinOffscreen( self, win ): - #we move offscreen to resize or else we get flashes on screen, and setting hide() doesn't allow resize & moves - offW = (gtk.gdk.screen_width() + 100) - offH = (gtk.gdk.screen_height() + 100) - self.smartMove(win, offW, offH) + def setImgLocDim( self, win ): + imgDim = self.getImgDim( self.FULLSCREEN ) + self.smartResize( win, imgDim[0], imgDim[1] ) + imgLoc = self.getImgLoc( self.FULLSCREEN ) + self.smartMove( win, imgLoc[0], imgLoc[1] ) - def setImgLocDim( self, win ): - imgDim = self.getImgDim( self.FULLSCREEN ) - self.smartResize( win, imgDim[0], imgDim[1] ) - imgLoc = self.getImgLoc( self.FULLSCREEN ) - self.smartMove( win, imgLoc[0], imgLoc[1] ) + def setPrgLocDim( self, win ): + prgDim = self.getPrgDim( self.FULLSCREEN ) + self.smartResize( win, prgDim[0], prgDim[1] ) + prgLoc = self.getPrgLoc( self.FULLSCREEN ) + self.smartMove( win, prgLoc[0], prgLoc[1] ) - def setPrgLocDim( self, win ): - prgDim = self.getPrgDim( self.FULLSCREEN ) - self.smartResize( win, prgDim[0], prgDim[1] ) - prgLoc = self.getPrgLoc( self.FULLSCREEN ) - self.smartMove( win, prgLoc[0], prgLoc[1] ) + def setTmrLocDim( self, win ): + tmrDim = self.getTmrDim( self.FULLSCREEN ) + self.smartResize( win, tmrDim[0], tmrDim[1] ) + tmrLoc = self.getTmrLoc( self.FULLSCREEN ) + self.smartMove( win, tmrLoc[0], tmrLoc[1] ) - def setTmrLocDim( self, win ): - tmrDim = self.getTmrDim( self.FULLSCREEN ) - self.smartResize( win, tmrDim[0], tmrDim[1] ) - tmrLoc = self.getTmrLoc( self.FULLSCREEN ) - self.smartMove( win, tmrLoc[0], tmrLoc[1] ) + def setScrLocDim( self, win ): + scrDim = self.getScrDim( self.FULLSCREEN ) + self.smartResize( win, scrDim[0], scrDim[1] ) + scrLoc = self.getScrLoc( self.FULLSCREEN ) + self.smartMove( win, scrLoc[0], scrLoc[1] ) - def setScrLocDim( self, win ): - scrDim = self.getScrDim( self.FULLSCREEN ) - self.smartResize( win, scrDim[0], scrDim[1] ) - scrLoc = self.getScrLoc( self.FULLSCREEN ) - self.smartMove( win, scrLoc[0], scrLoc[1] ) + def setInfLocDim( self, win ): + infDim = self.getInfDim( self.FULLSCREEN ) + self.smartResize( win, infDim[0], infDim[1] ) + infLoc = self.getInfLoc( self.FULLSCREEN ) + self.smartMove( win, infLoc[0], infLoc[1] ) - def setInfLocDim( self, win ): - infDim = self.getInfDim( self.FULLSCREEN ) - self.smartResize( win, infDim[0], infDim[1] ) - infLoc = self.getInfLoc( self.FULLSCREEN ) - self.smartMove( win, infLoc[0], infLoc[1] ) + def getScrDim( self, full ): + if (full): + return [gtk.gdk.screen_width()-(self.inset+self.pgdw+self.inset+self.inset), self.controlBarHt] + else: + return [self.vw, self.controlBarHt] - def getScrDim( self, full ): - if (full): - return [gtk.gdk.screen_width()-(self.inset+self.pgdw+self.inset+self.inset), self.controlBarHt] - else: - return [self.vw, self.controlBarHt] + def getScrLoc( self, full ): + if (full): + return [(self.inset+self.pgdw+self.inset), gtk.gdk.screen_height()-(self.inset+self.controlBarHt)] + else: + return [self.centerBoxPos[0], self.centerBoxPos[1]+self.vh] - def getScrLoc( self, full ): - if (full): - return [(self.inset+self.pgdw+self.inset), gtk.gdk.screen_height()-(self.inset+self.controlBarHt)] - else: - return [self.centerBoxPos[0], self.centerBoxPos[1]+self.vh] + def getImgDim( self, full ): + if (full): + return [gtk.gdk.screen_width(), gtk.gdk.screen_height()] + else: + return [self.vw, self.vh] - def getImgDim( self, full ): - if (full): - return [gtk.gdk.screen_width(), gtk.gdk.screen_height()] - else: - return [self.vw, self.vh] + def getImgLoc( self, full ): + if (full): + return[0, 0] + else: + return[self.centerBoxPos[0], self.centerBoxPos[1]] - def getImgLoc( self, full ): - if (full): - return[0, 0] - else: - return[self.centerBoxPos[0], self.centerBoxPos[1]] + def getTmrLoc( self, full ): + if (not full): + return [self.centerBoxPos[0], self.centerBoxPos[1]+self.vh] + else: + return [self.inset, gtk.gdk.screen_height()-(self.inset+self.controlBarHt)] - def getTmrLoc( self, full ): - if (not full): - return [self.centerBoxPos[0], self.centerBoxPos[1]+self.vh] - else: - return [self.inset, gtk.gdk.screen_height()-(self.inset+self.controlBarHt)] + def getTmrDim( self, full ): + if (not full): + return [self.vw, self.controlBarHt] + else: + return [gtk.gdk.screen_width()-(self.inset+self.inset), self.controlBarHt] - def getTmrDim( self, full ): - if (not full): - return [self.vw, self.controlBarHt] - else: - return [gtk.gdk.screen_width()-(self.inset+self.inset), self.controlBarHt] + def setPipLocDim( self, win ): + self.smartResize( win, self.pipw, self.piph ) - def setPipLocDim( self, win ): - self.smartResize( win, self.pipw, self.piph ) + loc = self.getPipLoc( self.FULLSCREEN ) + self.smartMove( win, loc[0], loc[1] ) - loc = self.getPipLoc( self.FULLSCREEN ) - self.smartMove( win, loc[0], loc[1] ) + def getPipLoc( self, full ): + if (full): + return [self.inset+self.__class__.dim_PIP_BORDER, gtk.gdk.screen_height()-(self.inset+self.piph+self.__class__.dim_PIP_BORDER)] + else: + return [self.centerBoxPos[0]+self.inset+self.__class__.dim_PIP_BORDER, (self.centerBoxPos[1]+self.vh)-(self.inset+self.piph+self.__class__.dim_PIP_BORDER)] - def getPipLoc( self, full ): - if (full): - return [self.inset+self.__class__.dim_PIP_BORDER, gtk.gdk.screen_height()-(self.inset+self.piph+self.__class__.dim_PIP_BORDER)] - else: - return [self.centerBoxPos[0]+self.inset+self.__class__.dim_PIP_BORDER, (self.centerBoxPos[1]+self.vh)-(self.inset+self.piph+self.__class__.dim_PIP_BORDER)] + def setPipBgdLocDim( self, win ): + pgdLoc = self.getPgdLoc( self.FULLSCREEN ) + self.smartMove( win, pgdLoc[0], pgdLoc[1] ) - def setPipBgdLocDim( self, win ): - pgdLoc = self.getPgdLoc( self.FULLSCREEN ) - self.smartMove( win, pgdLoc[0], pgdLoc[1] ) + def getPgdLoc( self, full ): + if (full): + return [self.inset, gtk.gdk.screen_height()-(self.inset+self.pgdh)] + else: + return [self.centerBoxPos[0]+self.inset, (self.centerBoxPos[1]+self.vh)-(self.inset+self.pgdh)] - def getPgdLoc( self, full ): - if (full): - return [self.inset, gtk.gdk.screen_height()-(self.inset+self.pgdh)] - else: - return [self.centerBoxPos[0]+self.inset, (self.centerBoxPos[1]+self.vh)-(self.inset+self.pgdh)] + def setMaxLocDim( self, win ): + maxLoc = self.getMaxLoc( self.FULLSCREEN ) + self.smartMove( win, maxLoc[0], maxLoc[1] ) - def setMaxLocDim( self, win ): - maxLoc = self.getMaxLoc( self.FULLSCREEN ) - self.smartMove( win, maxLoc[0], maxLoc[1] ) + def getMaxLoc( self, full ): + if (full): + return [gtk.gdk.screen_width()-(self.maxw+self.inset), self.inset] + else: + return [(self.centerBoxPos[0]+self.vw)-(self.inset+self.maxw), self.centerBoxPos[1]+self.inset] - def getMaxLoc( self, full ): - if (full): - return [gtk.gdk.screen_width()-(self.maxw+self.inset), self.inset] - else: - return [(self.centerBoxPos[0]+self.vw)-(self.inset+self.maxw), self.centerBoxPos[1]+self.inset] + def getInfLoc( self, full ): + if (full): + return [gtk.gdk.screen_width()+100,gtk.gdk.screen_height()+100 ] + else: + dim = self.getInfDim(self.FULLSCREEN) + return [(self.centerBoxPos[0]+self.vw)-dim[0], (self.centerBoxPos[1]+self.vh)-dim[1]] - def getInfLoc( self, full ): - if (full): - return [gtk.gdk.screen_width()+100,gtk.gdk.screen_height()+100 ] - else: - dim = self.getInfDim(self.FULLSCREEN) - return [(self.centerBoxPos[0]+self.vw)-dim[0], (self.centerBoxPos[1]+self.vh)-dim[1]] + def setEyeLocDim( self, win ): + dim = self.getEyeDim( self.FULLSCREEN ) + self.smartResize( win, dim[0], dim[1] ) + loc = self.getEyeLoc( self.FULLSCREEN ) + self.smartMove( win, loc[0], loc[1] ) - def setEyeLocDim( self, win ): - dim = self.getEyeDim( self.FULLSCREEN ) - self.smartResize( win, dim[0], dim[1] ) - loc = self.getEyeLoc( self.FULLSCREEN ) - self.smartMove( win, loc[0], loc[1] ) + def getEyeLoc( self, full ): + if (not full): + return [self.centerBoxPos[0], self.centerBoxPos[1]+self.vh] + else: + return [self.inset, gtk.gdk.screen_height()-(self.inset+self.controlBarHt)] - def getEyeLoc( self, full ): - if (not full): - return [self.centerBoxPos[0], self.centerBoxPos[1]+self.vh] - else: - return [self.inset, gtk.gdk.screen_height()-(self.inset+self.controlBarHt)] + def getEyeDim( self, full ): + if (not full): + if (self.ca.m.MODE == Constants.MODE_PHOTO): + return [self.vw, self.controlBarHt] + else: + return [self.recordButtWd, self.controlBarHt] + else: + if (self.ca.m.MODE == Constants.MODE_PHOTO): + return [gtk.gdk.screen_width()-(self.inset*2), self.controlBarHt] + else: + return [self.recordButtWd, self.controlBarHt] - def getEyeDim( self, full ): - if (not full): - if (self.ca.m.MODE == Constants.MODE_PHOTO): - return [self.vw, self.controlBarHt] - else: - return [self.recordButtWd, self.controlBarHt] - else: - if (self.ca.m.MODE == Constants.MODE_PHOTO): - return [gtk.gdk.screen_width()-(self.inset*2), self.controlBarHt] - else: - return [self.recordButtWd, self.controlBarHt] + def getInbLoc( self, full ): + return [(self.centerBoxPos[0]+self.vw)-(self.inset+self.letterBoxVW), self.centerBoxPos[1]+self.inset] - def getInbLoc( self, full ): - return [(self.centerBoxPos[0]+self.vw)-(self.inset+self.letterBoxVW), self.centerBoxPos[1]+self.inset] + def setInbLocDim( self, win ): + dim = self.getInbDim( self.FULLSCREEN ) + self.smartResize( win, dim[0], dim[1] ) + loc = self.getInbLoc(self.FULLSCREEN) + self.smartMove( win, loc[0], loc[1] ) - def setInbLocDim( self, win ): - dim = self.getInbDim( self.FULLSCREEN ) - self.smartResize( win, dim[0], dim[1] ) - loc = self.getInbLoc(self.FULLSCREEN) - self.smartMove( win, loc[0], loc[1] ) + def smartResize( self, win, w, h ): + winSize = win.get_size() + if ( (winSize[0] != w) or (winSize[1] != h) ): + win.resize( w, h ) + return True + else: + return False - def smartResize( self, win, w, h ): - winSize = win.get_size() - if ( (winSize[0] != w) or (winSize[1] != h) ): - win.resize( w, h ) - return True - else: - return False + def smartMove( self, win, x, y ): + winLoc = win.get_position() + if ( (winLoc[0] != x) or (winLoc[1] != y) ): + win.move( x, y ) + return True + else: + return False - def smartMove( self, win, x, y ): - winLoc = win.get_position() - if ( (winLoc[0] != x) or (winLoc[1] != y) ): - win.move( x, y ) - return True - else: - return False + def getDim( self, pos, full ): + if (pos == "pip"): + return self.getPipDim( full ) + elif(pos == "pgd"): + return self.getPgdDim( full ) + elif(pos == "max"): + return self.getMaxDim( full ) + elif(pos == "img"): + return self.getImgDim( full ) + elif(pos == "eye"): + return self.getEyeDim( full ) + elif(pos == "inb"): + return self.getInbDim( full ) + elif(pos == "prg"): + return self.getPrgDim( full ) + elif(pos == "inf"): + return self.getInfDim( full ) + + + def getMaxDim( self, full ): + return [self.maxw, self.maxh] - def getDim( self, pos, full ): - if (pos == "pip"): - return self.getPipDim( full ) - elif(pos == "pgd"): - return self.getPgdDim( full ) - elif(pos == "max"): - return self.getMaxDim( full ) - elif(pos == "img"): - return self.getImgDim( full ) - elif(pos == "eye"): - return self.getEyeDim( full ) - elif(pos == "inb"): - return self.getInbDim( full ) - elif(pos == "prg"): - return self.getPrgDim( full ) - elif(pos == "inf"): - return self.getInfDim( full ) - - - def getMaxDim( self, full ): - return [self.maxw, self.maxh] + def getInfDim( self, full ): + return [75, 75] + + + def getPipDim( self, full ): + return [self.pipw, self.piph] + + + def getPgdDim( self, full ): + return [self.pgdw, self.pgdh] + + + def getInbDim( self, full ): + return [self.letterBoxVW, self.letterBoxVH] + + + def getPrgDim( self, full ): + if (not full): + return [self.vw-self.recordButtWd, self.controlBarHt] + else: + return [gtk.gdk.screen_width()-(self.inset+self.inset+self.recordButtWd), self.controlBarHt] + + + def getPrgLoc( self, full ): + if (not full): + return [self.centerBoxPos[0]+self.recordButtWd, self.centerBoxPos[1]+self.vh] + else: + return [self.inset+self.recordButtWd, gtk.gdk.screen_height()-(self.inset+self.controlBarHt)] + + + def getLoc( self, pos, full ): + if (pos == "pip"): + return self.getPipLoc( full ) + elif(pos == "pgd"): + return self.getPgdLoc( full ) + elif(pos == "max"): + return self.getMaxLoc( full ) + elif(pos == "img"): + return self.getImgLoc( full ) + elif(pos == "eye"): + return self.getEyeLoc( full ) + elif(pos == "inb"): + return self.getInbLoc( full ) + elif(pos == "prg"): + return self.getPrgLoc( full ) + elif(pos == "inf"): + return self.getInfLoc( full ) + + + def _shutterClickCb( self, arg ): + self.doShutter() + + + def doShutter( self ): + if (self.UPDATE_TIMER_ID == 0): + if (not self.ca.m.RECORDING): + + self.ca.m.updateXoFullStatus() + if (self.ca.m.FULL): + self.updateButtonSensitivities() + return + + #there is no update timer running, so we need to find out if there is a timer needed + timerTime = 0 + if (self.ca.m.MODE == Constants.MODE_PHOTO): + timerTime = self.photoToolbar.getTimer() + elif (self.ca.m.MODE == Constants.MODE_VIDEO): + timerTime = self.videoToolbar.getTimer() + elif (self.ca.m.MODE == Constants.MODE_AUDIO): + timerTime = self.audioToolbar.getTimer() + + if (timerTime > 0): + self.timerStartTime = time.time() + self.UPDATE_TIMER_ID = gobject.timeout_add( 500, self._updateTimerCb ) + self.COUNTINGDOWN = True + self.ca.m.setUpdating(True) + else: + self.clickShutter() + else: + #or, if there is no countdown, it might be because we are recording + self.clickShutter() + + else: + #we're timing down something, but interrupted by user click or the timer completing + self.completeTimer() + gobject.idle_add( self.clickShutter ) + + + def completeTimer( self ): + self.COUNTINGDOWN = False + self.ca.m.setUpdating(False) + self.recordWindow.updateCountdown(-1) + self.progressWindow.updateProgress( 1, "" ) + gobject.source_remove( self.UPDATE_TIMER_ID ) + self.UPDATE_TIMER_ID = 0 + + + def _updateTimerCb( self ): + nowTime = time.time() + passedTime = nowTime - self.timerStartTime + + timerTime = 0 + if (self.ca.m.MODE == Constants.MODE_PHOTO): + timerTime = self.photoToolbar.getTimer() + elif (self.ca.m.MODE == Constants.MODE_VIDEO): + timerTime = self.videoToolbar.getTimer() + elif (self.ca.m.MODE == Constants.MODE_AUDIO): + timerTime = self.audioToolbar.getTimer() + + if (passedTime >= timerTime): + self.COUNTINGDOWN = False + self.ca.m.setUpdating(False) + self.doShutter() + return False + else: + secsRemaining = timerTime-passedTime + self.progressWindow.updateProgress( passedTime/timerTime, Constants.istrTimer + " " + Constants.istrSecondsRemaining % {"1":str(int(secsRemaining))} ) + self.recordWindow.updateCountdown( int(secsRemaining) ) + return True + + + def clickShutter( self ): + if (not self.ca.m.RECORDING): + aplay.play(Constants.soundClick) + + wasRec = self.ca.m.RECORDING + self.ca.m.doShutter() + if (wasRec): + aplay.play(Constants.soundClick) + + + def updateVideoComponents( self ): + logger.debug('updateVideoComponents: MODE=(%s,%s) FULLSCREEN=(%s,%s)' \ + ' LIVE=(%s,%s) RECD_INFO=(%s,%s) TRANSCODING=(%s,%s)' \ + ' MESHING=(%s,%s) windowStack=%s' \ + % (self.LAST_MODE, self.ca.m.MODE, + self.LAST_FULLSCREEN, self.FULLSCREEN, + self.LAST_LIVE, self.LIVEMODE, + self.LAST_RECD_INFO, self.RECD_INFO_ON, + self.LAST_TRANSCODING, self.TRANSCODING, + self.LAST_MESHING, self.MESHING, + len(self.windowStack))) + + if ( (self.LAST_MODE == self.ca.m.MODE) + and (self.LAST_FULLSCREEN == self.FULLSCREEN) + and (self.LAST_LIVE == self.LIVEMODE) + and (self.LAST_RECD_INFO == self.RECD_INFO_ON) + and (self.LAST_TRANSCODING == self.TRANSCODING) + and (self.LAST_MESHING == self.MESHING) + ): + return + + #something's changing so start counting anew + self.resetWidgetFadeTimer() + + pos = [] + if (self.RECD_INFO_ON and not self.TRANSCODING): + if (self.ca.m.MODE == Constants.MODE_PHOTO): + pos.append({"position":"pgd", "window":self.pipBgdWindow} ) + pos.append({"position":"pip", "window":self.liveVideoWindow} ) + pos.append({"position":"inb", "window":self.livePhotoWindow} ) + pos.append({"position":"inf", "window":self.infWindow} ) + elif (self.ca.m.MODE == Constants.MODE_VIDEO): + pos.append({"position":"pgd", "window":self.pipBgdWindow} ) + pos.append({"position":"pip", "window":self.liveVideoWindow} ) + pos.append({"position":"inb", "window":self.playOggWindow} ) + pos.append({"position":"inf", "window":self.infWindow} ) + elif (self.ca.m.MODE == Constants.MODE_AUDIO): + pos.append({"position":"pgd", "window":self.pipBgdWindow} ) + pos.append({"position":"pip", "window":self.liveVideoWindow} ) + pos.append({"position":"inb", "window":self.livePhotoWindow} ) + pos.append({"position":"inf", "window":self.infWindow} ) + elif (not self.RECD_INFO_ON and not self.TRANSCODING): + if (self.ca.m.MODE == Constants.MODE_PHOTO): + if (self.LIVEMODE): + pos.append({"position":"img", "window":self.liveVideoWindow} ) + pos.append({"position":"max", "window":self.maxWindow} ) + pos.append({"position":"eye", "window":self.recordWindow} ) + else: + pos.append({"position":"img", "window":self.livePhotoWindow} ) + pos.append({"position":"pgd", "window":self.pipBgdWindow} ) + pos.append({"position":"pip", "window":self.liveVideoWindow} ) + if (not self.MESHING): + pos.append({"position":"max", "window":self.maxWindow} ) + pos.append({"position":"inf", "window":self.infWindow} ) + else: + pos.append({"position":"tmr", "window":self.progressWindow} ) + elif (self.ca.m.MODE == Constants.MODE_VIDEO): + if (self.LIVEMODE): + pos.append({"position":"img", "window":self.liveVideoWindow} ) + pos.append({"position":"max", "window":self.maxWindow} ) + pos.append({"position":"eye", "window":self.recordWindow} ) + pos.append({"position":"prg", "window":self.progressWindow} ) + else: + pos.append({"position":"img", "window":self.playOggWindow} ) + pos.append({"position":"pgd", "window":self.pipBgdWindow} ) + pos.append({"position":"pip", "window":self.liveVideoWindow} ) + if (not self.MESHING): + pos.append({"position":"max", "window":self.maxWindow} ) + pos.append({"position":"scr", "window":self.scrubWindow} ) + pos.append({"position":"inf", "window":self.infWindow} ) + else: + pos.append({"position":"tmr", "window":self.progressWindow} ) + elif (self.ca.m.MODE == Constants.MODE_AUDIO): + if (self.LIVEMODE): + pos.append({"position":"img", "window":self.liveVideoWindow} ) + pos.append({"position":"eye", "window":self.recordWindow} ) + pos.append({"position":"prg", "window":self.progressWindow} ) + else: + pos.append({"position":"img", "window":self.livePhotoWindow} ) + pos.append({"position":"pgd", "window":self.pipBgdWindow} ) + pos.append({"position":"pip", "window":self.liveVideoWindow} ) + if (not self.MESHING): + pos.append({"position":"scr", "window":self.scrubWindow} ) + pos.append({"position":"inf", "window":self.infWindow} ) + else: + pos.append({"position":"tmr", "window":self.progressWindow} ) + elif (self.TRANSCODING): + pos.append({"position":"tmr", "window":self.progressWindow} ) + + for i in range (0, len(self.windowStack)): + self.windowStack[i].hide_all() + + self.hideAllWindows() + self.updatePos( pos ) + + for i in range (0, len(self.windowStack)): + self.windowStack[i].show_all() + + self.LAST_MODE = self.ca.m.MODE + self.LAST_FULLSCREEN = self.FULLSCREEN + self.LAST_LIVE = self.LIVEMODE + self.LAST_RECD_INFO = self.RECD_INFO_ON + self.LAST_TRANSCODING = self.TRANSCODING + self.LAST_MESHING = self.MESHING + + + def debugWindows( self ): + for i in range (0, len(self.windowStack)): + print self.windowStack[i], self.windowStack[i].get_size(), self.windowStack[i].get_position() + + + def showWidgets( self ): + pos = [] + if (self.ca.m.MODE == Constants.MODE_PHOTO): + if (not self.LIVEMODE): + pos.append({"position":"pgd", "window":self.pipBgdWindow} ) + pos.append({"position":"pip", "window":self.liveVideoWindow} ) + if (not self.MESHING): + pos.append({"position":"max", "window":self.maxWindow} ) + pos.append({"position":"inf", "window":self.infWindow} ) + else: + pos.append({"position":"max", "window":self.maxWindow} ) + pos.append({"position":"eye", "window":self.recordWindow} ) + elif (self.ca.m.MODE == Constants.MODE_VIDEO): + if (not self.LIVEMODE): + pos.append({"position":"pgd", "window":self.pipBgdWindow} ) + pos.append({"position":"pip", "window":self.liveVideoWindow} ) + if (not self.MESHING): + pos.append({"position":"max", "window":self.maxWindow} ) + pos.append({"position":"scr", "window":self.scrubWindow} ) + pos.append({"position":"inf", "window":self.infWindow} ) + else: + pos.append({"position":"max", "window":self.maxWindow} ) + pos.append({"position":"eye", "window":self.recordWindow} ) + pos.append({"position":"prg", "window":self.progressWindow} ) + elif (self.ca.m.MODE == Constants.MODE_AUDIO): + if (not self.LIVEMODE): + pos.append({"position":"pgd", "window":self.pipBgdWindow} ) + pos.append({"position":"pip", "window":self.liveVideoWindow} ) + if (not self.MESHING): + pos.append({"position":"scr", "window":self.scrubWindow} ) + pos.append({"position":"inf", "window":self.infWindow} ) + else: + pos.append({"position":"eye", "window":self.recordWindow} ) + pos.append({"position":"prg", "window":self.progressWindow} ) + + self.updatePos( pos ) + + + def updatePos( self, pos ): + #now move those pieces where they need to be... + for i in range (0, len(self.windowStack)): + for j in range (0, len(pos)): + if (self.windowStack[i] == pos[j]["window"]): + if (pos[j]["position"] == "img"): + self.setImgLocDim( pos[j]["window"] ) + elif (pos[j]["position"] == "max"): + self.setMaxLocDim( pos[j]["window"] ) + elif (pos[j]["position"] == "pip"): + self.setPipLocDim( pos[j]["window"] ) + elif (pos[j]["position"] == "pgd"): + self.setPipBgdLocDim( pos[j]["window"] ) + elif (pos[j]["position"] == "eye"): + self.setEyeLocDim( pos[j]["window"] ) + elif (pos[j]["position"] == "inb"): + self.setInbLocDim( pos[j]["window"]) + elif (pos[j]["position"] == "prg"): + self.setPrgLocDim( pos[j]["window"]) + elif (pos[j]["position"] == "tmr"): + self.setTmrLocDim( pos[j]["window"]) + elif (pos[j]["position"] == "scr"): + self.setScrLocDim( pos[j]["window"]) + elif (pos[j]["position"] == "inf"): + self.setInfLocDim( pos[j]["window"]) + + + def removeThumb( self, recd ): + kids = self.thumbTray.get_children() + for i in range (0, len(kids)): + if (kids[i].recd == recd): + self.thumbTray.remove_item(kids[i]) + kids[i].cleanUp() + kids[i].disconnect( kids[i].getButtClickedId() ) + kids[i].setButtClickedId(0) + + + def addThumb( self, recd, forceScroll ): + butt = RecdButton( self, recd ) + BUTT_CLICKED_ID = butt.connect( "clicked", self._thumbClicked, recd ) + butt.setButtClickedId(BUTT_CLICKED_ID) + self.thumbTray.add_item( butt, len(self.thumbTray.get_children()) ) + butt.show() + if (forceScroll): + self.thumbTray.scroll_to_end() + + + def removeThumbs( self ): + kids = self.thumbTray.get_children() + for i in range (0, len(kids)): + self.thumbTray.remove_item(kids[i]) + kids[i].cleanUp() + if (kids[i].getButtClickedId() != 0): + kids[i].disconnect( kids[i].getButtClickedId() ) + + + def _thumbClicked( self, button, recd ): + self.showThumbSelection( recd ) + + + def infoButtonClicked( self ): + self.RECD_INFO_ON = not self.RECD_INFO_ON + + centerKid = self.centerBox.get_child() + if (centerKid != None): + self.centerBox.remove( centerKid ) + + bottomKid = self.bottomCenter.get_child() + if (bottomKid != None): + self.bottomCenter.remove( bottomKid ) + + if (not self.RECD_INFO_ON): + if (self.ca.m.MODE == Constants.MODE_PHOTO): + self.bottomCenter.add( self.namePanel ) + self.bottomCenter.show_all( ) + else: + self.centerBox.add( self.infoBox ) + self.centerBox.show_all( ) + self.bottomCenter.add( self.namePanel ) + self.bottomCenter.show_all( ) + + self.updateVideoComponents( ) + + + def showMeshRecd( self, recd ): + record.Record.log.debug('showMeshRecd: heres the downloaded recd to display...') + + #if this thumbnail is being shown, add the option to copy it now + kids = self.thumbTray.get_children() + for i in range (0, len(kids)): + if (kids[i].recd == recd): + kids[i].addCopyMenuItem() + + if (recd == self.shownRecd): + record.Record.log.debug('showMeshRecd: and since were still looking at same recd, here it is!') + self.showThumbSelection( recd ) + + + def updateMeshProgress( self, progressMade, recd ): + self.resetWidgetFadeTimer() + if (self.shownRecd != recd): + if (self.shownRecd == None): + type = Constants.mediaTypes[recd.type][Constants.keyIstr] + if (progressMade): + msg = Constants.istrDownloadingFrom% {"1":type, "2":recd.meshDownloadingFromNick} + self.progressWindow.updateProgress(recd.meshDownlodingPercent, msg) + + else: + type = Constants.mediaTypes[recd.type][Constants.keyIstr] + if (progressMade): + msg = Constants.istrDownloadingFrom% {"1":type, "2":recd.meshDownloadingFromNick} + self.progressWindow.updateProgress(recd.meshDownlodingPercent, msg) + + else: + type = Constants.mediaTypes[recd.type][Constants.keyIstr] + msg = Constants.istrCannotDownload % {"1":type} + self.progressWindow.updateProgress(0, msg) + + + def showThumbSelection( self, recd ): + lastRecd = self.shownRecd + self.shownRecd = recd + + #do we need to know the type, since we're showing based on the mode of the app? + if (recd.type == Constants.TYPE_PHOTO): + self.showPhoto( recd ) + elif (recd.type == Constants.TYPE_VIDEO): + self.showVideo( recd ) + elif (recd.type == Constants.TYPE_AUDIO): + self.showAudio( recd ) + + if (self.shownRecd != lastRecd): + self.photoXoPanel.updateXoColors(self.shownRecd.colorStroke.hex, self.shownRecd.colorFill.hex) + + bottomKid = self.bottomCenter.get_child() + if (bottomKid != None): + self.bottomCenter.remove( bottomKid ) + + if (recd.type == Constants.TYPE_PHOTO): + self.bottomCenter.add( self.namePanel ) + elif (recd.type == Constants.TYPE_VIDEO or recd.type == Constants.TYPE_AUDIO): + if (self.RECD_INFO_ON): + self.bottomCenter.add( self.namePanel ) + self.bottomCenter.show_all() + + self.resetWidgetFadeTimer() + + + def showAudio( self, recd ): + self.ca.glive.thumb_play() + self.LIVEMODE = False + + #if (recd != self.shownRecd): + pixbuf = recd.getAudioImagePixbuf() + img = camerac.cairo_surface_from_gdk_pixbuf(pixbuf) + self.livePhotoCanvas.setImage( img ) + #self.shownRecd = recd + self.showRecdMeta(recd) + + downloading = self.ca.requestMeshDownload(recd) + self.MESHING = downloading + record.Record.log.debug("showAudio: downloading->" + str(downloading)) + if (not downloading): + self.progressWindow.updateProgress(0, "") + mediaFilepath = recd.getMediaFilepath( ) + record.Record.log.debug("showAudio: mediaFilepath->" + str(mediaFilepath)) + if (mediaFilepath != None): + videoUrl = "file://" + str( mediaFilepath ) + self.ca.gplay.setLocation(videoUrl) + self.scrubWindow.doPlay() + + self.updateVideoComponents() + + + def showVideo( self, recd ): + logger.debug('showVideo') + + downloading = self.ca.requestMeshDownload(recd) + + if (not downloading): + self.progressWindow.updateProgress(0, "") + + self.MESHING = downloading + self.LIVEMODE = False + #self.shownRecd = recd + self.updateVideoComponents() + gobject.idle_add( self.showVideo2, recd, downloading ) + + + def showVideo2( self, recd, downloading ): + self.showRecdMeta(recd) - def getInfDim( self, full ): - return [75, 75] - - - def getPipDim( self, full ): - return [self.pipw, self.piph] - - - def getPgdDim( self, full ): - return [self.pgdw, self.pgdh] - - - def getInbDim( self, full ): - return [self.letterBoxVW, self.letterBoxVH] - - - def getPrgDim( self, full ): - if (not full): - return [self.vw-self.recordButtWd, self.controlBarHt] - else: - return [gtk.gdk.screen_width()-(self.inset+self.inset+self.recordButtWd), self.controlBarHt] - - - def getPrgLoc( self, full ): - if (not full): - return [self.centerBoxPos[0]+self.recordButtWd, self.centerBoxPos[1]+self.vh] - else: - return [self.inset+self.recordButtWd, gtk.gdk.screen_height()-(self.inset+self.controlBarHt)] - - - def getLoc( self, pos, full ): - if (pos == "pip"): - return self.getPipLoc( full ) - elif(pos == "pgd"): - return self.getPgdLoc( full ) - elif(pos == "max"): - return self.getMaxLoc( full ) - elif(pos == "img"): - return self.getImgLoc( full ) - elif(pos == "eye"): - return self.getEyeLoc( full ) - elif(pos == "inb"): - return self.getInbLoc( full ) - elif(pos == "prg"): - return self.getPrgLoc( full ) - elif(pos == "inf"): - return self.getInfLoc( full ) - - - def _shutterClickCb( self, arg ): - self.doShutter() - - - def doShutter( self ): - if (self.UPDATE_TIMER_ID == 0): - if (not self.ca.m.RECORDING): - - self.ca.m.updateXoFullStatus() - if (self.ca.m.FULL): - self.updateButtonSensitivities() - return - - #there is no update timer running, so we need to find out if there is a timer needed - timerTime = 0 - if (self.ca.m.MODE == Constants.MODE_PHOTO): - timerTime = self.photoToolbar.getTimer() - elif (self.ca.m.MODE == Constants.MODE_VIDEO): - timerTime = self.videoToolbar.getTimer() - elif (self.ca.m.MODE == Constants.MODE_AUDIO): - timerTime = self.audioToolbar.getTimer() - - if (timerTime > 0): - self.timerStartTime = time.time() - self.UPDATE_TIMER_ID = gobject.timeout_add( 500, self._updateTimerCb ) - self.COUNTINGDOWN = True - self.ca.m.setUpdating(True) - else: - self.clickShutter() - else: - #or, if there is no countdown, it might be because we are recording - self.clickShutter() - - else: - #we're timing down something, but interrupted by user click or the timer completing - self.completeTimer() - gobject.idle_add( self.clickShutter ) - - - def completeTimer( self ): - self.COUNTINGDOWN = False - self.ca.m.setUpdating(False) - self.recordWindow.updateCountdown(-1) - self.progressWindow.updateProgress( 1, "" ) - gobject.source_remove( self.UPDATE_TIMER_ID ) - self.UPDATE_TIMER_ID = 0 - - - def _updateTimerCb( self ): - nowTime = time.time() - passedTime = nowTime - self.timerStartTime - - timerTime = 0 - if (self.ca.m.MODE == Constants.MODE_PHOTO): - timerTime = self.photoToolbar.getTimer() - elif (self.ca.m.MODE == Constants.MODE_VIDEO): - timerTime = self.videoToolbar.getTimer() - elif (self.ca.m.MODE == Constants.MODE_AUDIO): - timerTime = self.audioToolbar.getTimer() - - if (passedTime >= timerTime): - self.COUNTINGDOWN = False - self.ca.m.setUpdating(False) - self.doShutter() - return False - else: - secsRemaining = timerTime-passedTime - self.progressWindow.updateProgress( passedTime/timerTime, Constants.istrTimer + " " + Constants.istrSecondsRemaining % {"1":str(int(secsRemaining))} ) - self.recordWindow.updateCountdown( int(secsRemaining) ) - return True - - - def clickShutter( self ): - if (not self.ca.m.RECORDING): - os.system( "aplay -t wav " + str(Constants.soundClick) ) - - wasRec = self.ca.m.RECORDING - self.ca.m.doShutter() - if (wasRec): - os.system( "aplay -t wav " + str(Constants.soundClick) ) - - - def updateVideoComponents( self ): - if ( (self.LAST_MODE == self.ca.m.MODE) - and (self.LAST_FULLSCREEN == self.FULLSCREEN) - and (self.LAST_LIVE == self.LIVEMODE) - and (self.LAST_RECD_INFO == self.RECD_INFO_ON) - and (self.LAST_TRANSCODING == self.TRANSCODING) - and (self.LAST_MESHING == self.MESHING) - ): - return - - #something's changing so start counting anew - self.resetWidgetFadeTimer() - - pos = [] - if (self.RECD_INFO_ON and not self.TRANSCODING): - if (self.ca.m.MODE == Constants.MODE_PHOTO): - pos.append({"position":"pgd", "window":self.pipBgdWindow} ) - pos.append({"position":"pip", "window":self.liveVideoWindow} ) - pos.append({"position":"inb", "window":self.livePhotoWindow} ) - pos.append({"position":"inf", "window":self.infWindow} ) - elif (self.ca.m.MODE == Constants.MODE_VIDEO): - pos.append({"position":"pgd", "window":self.pipBgdWindow} ) - pos.append({"position":"pip", "window":self.slowLiveVideoWindow} ) - pos.append({"position":"inb", "window":self.playOggWindow} ) - pos.append({"position":"inf", "window":self.infWindow} ) - elif (self.ca.m.MODE == Constants.MODE_AUDIO): - pos.append({"position":"pgd", "window":self.pipBgdWindow} ) - pos.append({"position":"pip", "window":self.liveVideoWindow} ) - pos.append({"position":"inb", "window":self.livePhotoWindow} ) - pos.append({"position":"inf", "window":self.infWindow} ) - elif (not self.RECD_INFO_ON and not self.TRANSCODING): - if (self.ca.m.MODE == Constants.MODE_PHOTO): - if (self.LIVEMODE): - pos.append({"position":"img", "window":self.liveVideoWindow} ) - pos.append({"position":"max", "window":self.maxWindow} ) - pos.append({"position":"eye", "window":self.recordWindow} ) - else: - pos.append({"position":"img", "window":self.livePhotoWindow} ) - pos.append({"position":"pgd", "window":self.pipBgdWindow} ) - pos.append({"position":"pip", "window":self.liveVideoWindow} ) - if (not self.MESHING): - pos.append({"position":"max", "window":self.maxWindow} ) - pos.append({"position":"inf", "window":self.infWindow} ) - else: - pos.append({"position":"tmr", "window":self.progressWindow} ) - elif (self.ca.m.MODE == Constants.MODE_VIDEO): - if (self.LIVEMODE): - pos.append({"position":"img", "window":self.liveVideoWindow} ) - pos.append({"position":"max", "window":self.maxWindow} ) - pos.append({"position":"eye", "window":self.recordWindow} ) - pos.append({"position":"prg", "window":self.progressWindow} ) - else: - pos.append({"position":"img", "window":self.playOggWindow} ) - pos.append({"position":"pgd", "window":self.pipBgdWindow} ) - pos.append({"position":"pip", "window":self.slowLiveVideoWindow} ) - if (not self.MESHING): - pos.append({"position":"max", "window":self.maxWindow} ) - pos.append({"position":"scr", "window":self.scrubWindow} ) - pos.append({"position":"inf", "window":self.infWindow} ) - else: - pos.append({"position":"tmr", "window":self.progressWindow} ) - elif (self.ca.m.MODE == Constants.MODE_AUDIO): - if (self.LIVEMODE): - pos.append({"position":"img", "window":self.liveVideoWindow} ) - pos.append({"position":"eye", "window":self.recordWindow} ) - pos.append({"position":"prg", "window":self.progressWindow} ) - else: - pos.append({"position":"img", "window":self.livePhotoWindow} ) - pos.append({"position":"pgd", "window":self.pipBgdWindow} ) - pos.append({"position":"pip", "window":self.liveVideoWindow} ) - if (not self.MESHING): - pos.append({"position":"scr", "window":self.scrubWindow} ) - pos.append({"position":"inf", "window":self.infWindow} ) - else: - pos.append({"position":"tmr", "window":self.progressWindow} ) - elif (self.TRANSCODING): - pos.append({"position":"tmr", "window":self.progressWindow} ) - - for i in range (0, len(self.windowStack)): - self.windowStack[i].hide_all() - - self.hideAllWindows() - self.updatePos( pos ) - - for i in range (0, len(self.windowStack)): - self.windowStack[i].show_all() - - self.LAST_MODE = self.ca.m.MODE - self.LAST_FULLSCREEN = self.FULLSCREEN - self.LAST_LIVE = self.LIVEMODE - self.LAST_RECD_INFO = self.RECD_INFO_ON - self.LAST_TRANSCODING = self.TRANSCODING - self.LAST_MESHING = self.MESHING - - - def debugWindows( self ): - for i in range (0, len(self.windowStack)): - print self.windowStack[i], self.windowStack[i].get_size(), self.windowStack[i].get_position() - - - def showWidgets( self ): - pos = [] - if (self.ca.m.MODE == Constants.MODE_PHOTO): - if (not self.LIVEMODE): - pos.append({"position":"pgd", "window":self.pipBgdWindow} ) - pos.append({"position":"pip", "window":self.liveVideoWindow} ) - if (not self.MESHING): - pos.append({"position":"max", "window":self.maxWindow} ) - pos.append({"position":"inf", "window":self.infWindow} ) - else: - pos.append({"position":"max", "window":self.maxWindow} ) - pos.append({"position":"eye", "window":self.recordWindow} ) - elif (self.ca.m.MODE == Constants.MODE_VIDEO): - if (not self.LIVEMODE): - pos.append({"position":"pgd", "window":self.pipBgdWindow} ) - pos.append({"position":"pip", "window":self.slowLiveVideoWindow} ) - if (not self.MESHING): - pos.append({"position":"max", "window":self.maxWindow} ) - pos.append({"position":"scr", "window":self.scrubWindow} ) - pos.append({"position":"inf", "window":self.infWindow} ) - else: - pos.append({"position":"max", "window":self.maxWindow} ) - pos.append({"position":"eye", "window":self.recordWindow} ) - pos.append({"position":"prg", "window":self.progressWindow} ) - elif (self.ca.m.MODE == Constants.MODE_AUDIO): - if (not self.LIVEMODE): - pos.append({"position":"pgd", "window":self.pipBgdWindow} ) - pos.append({"position":"pip", "window":self.liveVideoWindow} ) - if (not self.MESHING): - pos.append({"position":"scr", "window":self.scrubWindow} ) - pos.append({"position":"inf", "window":self.infWindow} ) - else: - pos.append({"position":"eye", "window":self.recordWindow} ) - pos.append({"position":"prg", "window":self.progressWindow} ) - - self.updatePos( pos ) - - - def updatePos( self, pos ): - #now move those pieces where they need to be... - for i in range (0, len(self.windowStack)): - for j in range (0, len(pos)): - if (self.windowStack[i] == pos[j]["window"]): - if (pos[j]["position"] == "img"): - self.setImgLocDim( pos[j]["window"] ) - elif (pos[j]["position"] == "max"): - self.setMaxLocDim( pos[j]["window"] ) - elif (pos[j]["position"] == "pip"): - self.setPipLocDim( pos[j]["window"] ) - elif (pos[j]["position"] == "pgd"): - self.setPipBgdLocDim( pos[j]["window"] ) - elif (pos[j]["position"] == "eye"): - self.setEyeLocDim( pos[j]["window"] ) - elif (pos[j]["position"] == "inb"): - self.setInbLocDim( pos[j]["window"]) - elif (pos[j]["position"] == "prg"): - self.setPrgLocDim( pos[j]["window"]) - elif (pos[j]["position"] == "tmr"): - self.setTmrLocDim( pos[j]["window"]) - elif (pos[j]["position"] == "scr"): - self.setScrLocDim( pos[j]["window"]) - elif (pos[j]["position"] == "inf"): - self.setInfLocDim( pos[j]["window"]) - - - def removeThumb( self, recd ): - kids = self.thumbTray.get_children() - for i in range (0, len(kids)): - if (kids[i].recd == recd): - self.thumbTray.remove_item(kids[i]) - kids[i].cleanUp() - kids[i].disconnect( kids[i].getButtClickedId() ) - kids[i].setButtClickedId(0) - - - def addThumb( self, recd, forceScroll ): - butt = RecdButton( self, recd ) - BUTT_CLICKED_ID = butt.connect( "clicked", self._thumbClicked, recd ) - butt.setButtClickedId(BUTT_CLICKED_ID) - self.thumbTray.add_item( butt, len(self.thumbTray.get_children()) ) - butt.show() - if (forceScroll): - self.thumbTray.scroll_to_end() - - - def removeThumbs( self ): - kids = self.thumbTray.get_children() - for i in range (0, len(kids)): - self.thumbTray.remove_item(kids[i]) - kids[i].cleanUp() - if (kids[i].getButtClickedId() != 0): - kids[i].disconnect( kids[i].getButtClickedId() ) - - - def _thumbClicked( self, button, recd ): - self.showThumbSelection( recd ) - - - def infoButtonClicked( self ): - self.RECD_INFO_ON = not self.RECD_INFO_ON - - centerKid = self.centerBox.get_child() - if (centerKid != None): - self.centerBox.remove( centerKid ) - - bottomKid = self.bottomCenter.get_child() - if (bottomKid != None): - self.bottomCenter.remove( bottomKid ) - - if (not self.RECD_INFO_ON): - if (self.ca.m.MODE == Constants.MODE_PHOTO): - self.bottomCenter.add( self.namePanel ) - self.bottomCenter.show_all( ) - else: - self.centerBox.add( self.infoBox ) - self.centerBox.show_all( ) - self.bottomCenter.add( self.namePanel ) - self.bottomCenter.show_all( ) - - self.updateVideoComponents( ) - - - def showMeshRecd( self, recd ): - record.Record.log.debug('showMeshRecd: heres the downloaded recd to display...') - - #if this thumbnail is being shown, add the option to copy it now - kids = self.thumbTray.get_children() - for i in range (0, len(kids)): - if (kids[i].recd == recd): - kids[i].addCopyMenuItem() - - if (recd == self.shownRecd): - record.Record.log.debug('showMeshRecd: and since were still looking at same recd, here it is!') - self.showThumbSelection( recd ) - - - def updateMeshProgress( self, progressMade, recd ): - self.resetWidgetFadeTimer() - if (self.shownRecd != recd): - if (self.shownRecd == None): - type = Constants.mediaTypes[recd.type][Constants.keyIstr] - if (progressMade): - msg = Constants.istrDownloadingFrom% {"1":type, "2":recd.meshDownloadingFromNick} - self.progressWindow.updateProgress(recd.meshDownlodingPercent, msg) - - else: - type = Constants.mediaTypes[recd.type][Constants.keyIstr] - if (progressMade): - msg = Constants.istrDownloadingFrom% {"1":type, "2":recd.meshDownloadingFromNick} - self.progressWindow.updateProgress(recd.meshDownlodingPercent, msg) - - else: - type = Constants.mediaTypes[recd.type][Constants.keyIstr] - msg = Constants.istrCannotDownload % {"1":type} - self.progressWindow.updateProgress(0, msg) - - - def showThumbSelection( self, recd ): - lastRecd = self.shownRecd - self.shownRecd = recd - - #do we need to know the type, since we're showing based on the mode of the app? - if (recd.type == Constants.TYPE_PHOTO): - self.showPhoto( recd ) - elif (recd.type == Constants.TYPE_VIDEO): - self.showVideo( recd ) - elif (recd.type == Constants.TYPE_AUDIO): - self.showAudio( recd ) - - if (self.shownRecd != lastRecd): - self.photoXoPanel.updateXoColors(self.shownRecd.colorStroke.hex, self.shownRecd.colorFill.hex) - - bottomKid = self.bottomCenter.get_child() - if (bottomKid != None): - self.bottomCenter.remove( bottomKid ) - - if (recd.type == Constants.TYPE_PHOTO): - self.bottomCenter.add( self.namePanel ) - elif (recd.type == Constants.TYPE_VIDEO or recd.type == Constants.TYPE_AUDIO): - if (self.RECD_INFO_ON): - self.bottomCenter.add( self.namePanel ) - self.bottomCenter.show_all() + ableToShowVideo = False + if (not downloading): + mediaFilepath = recd.getMediaFilepath() + if (mediaFilepath != None): + self.ca.glive.thumb_play(use_fallback=True) + logger.debug('showVideo2 file=%s' % mediaFilepath) + videoUrl = "file://" + str( mediaFilepath ) + self.ca.gplay.setLocation(videoUrl) + self.scrubWindow.doPlay() + ableToShowVideo = True - self.resetWidgetFadeTimer() - - - def showAudio( self, recd ): - self.LIVEMODE = False - - #if (recd != self.shownRecd): - pixbuf = recd.getAudioImagePixbuf() - img = _camera.cairo_surface_from_gdk_pixbuf(pixbuf) - self.livePhotoCanvas.setImage( img ) - #self.shownRecd = recd - self.showRecdMeta(recd) - - downloading = self.ca.requestMeshDownload(recd) - self.MESHING = downloading - record.Record.log.debug("showAudio: downloading->" + str(downloading)) - if (not downloading): - self.progressWindow.updateProgress(0, "") - mediaFilepath = recd.getMediaFilepath( ) - record.Record.log.debug("showAudio: mediaFilepath->" + str(mediaFilepath)) - if (mediaFilepath != None): - videoUrl = "file://" + str( mediaFilepath ) - self.ca.gplay.setLocation(videoUrl) - self.scrubWindow.doPlay() - - self.updateVideoComponents() - - - def showVideo( self, recd ): - downloading = self.ca.requestMeshDownload(recd) - - if (not downloading): - self.progressWindow.updateProgress(0, "") - - self.MESHING = downloading - self.LIVEMODE = False - #self.shownRecd = recd - self.updateVideoComponents() - gobject.idle_add( self.showVideo2, recd, downloading ) - - - def showVideo2( self, recd, downloading ): - self.showRecdMeta(recd) + if (not ableToShowVideo): + self.ca.glive.thumb_play(use_fallback=True) + # FIXME is this correct? + thumbFilepath = recd.getThumbFilepath( ) + logger.debug('showVideo3 file=%s' % thumbFilepath) + thumbUrl = "file://" + str( thumbFilepath ) + self.ca.gplay.setLocation(thumbUrl) - ableToShowVideo = False - if (not downloading): - mediaFilepath = recd.getMediaFilepath() - if (mediaFilepath != None): - self.ca.glive.stop() - self.ca.glivex.play() - videoUrl = "file://" + str( mediaFilepath ) - self.ca.gplay.setLocation(videoUrl) - self.scrubWindow.doPlay() - ableToShowVideo = True - if (not ableToShowVideo): - # FIXME is this correct? - self.ca.glive.stop() - self.ca.glivex.play() - thumbFilepath = recd.getThumbFilepath( ) - thumbUrl = "file://" + str( thumbFilepath ) - self.ca.gplay.setLocation(thumbUrl) + def deleteThumbSelection( self, recd ): + self.ca.m.deleteRecorded( recd ) + self.ca.glive.play() + self.removeThumb( recd ) + self.removeIfSelectedRecorded( recd ) - def deleteThumbSelection( self, recd ): - self.ca.m.deleteRecorded( recd ) - self.removeThumb( recd ) - self.removeIfSelectedRecorded( recd ) + def removeIfSelectedRecorded( self, recd ): + if (recd == self.shownRecd): + if (recd.type == Constants.TYPE_PHOTO): + self.livePhotoCanvas.setImage( None ) + elif (recd.type == Constants.TYPE_VIDEO): + self.ca.gplay.stop() + self.startLiveVideo( False ) + elif (recd.type == Constants.TYPE_AUDIO): + self.livePhotoCanvas.setImage( None ) + self.startLiveAudio() + self.RECD_INFO_ON = False + self.LIVEMODE = True + self.updateVideoComponents() - def removeIfSelectedRecorded( self, recd ): - if (recd == self.shownRecd): - if (recd.type == Constants.TYPE_PHOTO): - self.livePhotoCanvas.setImage( None ) - elif (recd.type == Constants.TYPE_VIDEO): - self.ca.gplay.stop() - self.startLiveVideo( False ) - elif (recd.type == Constants.TYPE_AUDIO): - self.livePhotoCanvas.setImage( None ) - self.startLiveAudio() + self.showLiveVideoTags() - self.LIVEMODE = True - self.updateVideoComponents() - self.showLiveVideoTags() + def startLiveAudio( self ): + self.ca.m.setUpdating(True) + self.ca.gplay.stop() + self.liveVideoWindow.set_glive(self.ca.glive) - def startLiveAudio( self ): - self.ca.m.setUpdating(True) - self.ca.gplay.stop() + self.showLiveVideoTags() + self.LIVEMODE = True + self.updateVideoComponents() + self.ca.m.setUpdating(False) - self.liveVideoWindow.set_glive(self.ca.glive) - self.showLiveVideoTags() - self.LIVEMODE = True - self.updateVideoComponents() - self.ca.m.setUpdating(False) + def showPostProcessGfx( self, show ): + #not self.FULLSCREEN + centerKid = self.centerBox.get_child() + if (centerKid != None): + self.centerBox.remove( centerKid ) + if ( show ): + self.centerBox.add( self.backgdCanvasBox ) + self.centerBox.show_all() + else: + self.backgdCanvas.setImage( None ) - def showPostProcessGfx( self, show ): - #not self.FULLSCREEN - centerKid = self.centerBox.get_child() - if (centerKid != None): - self.centerBox.remove( centerKid ) - if ( show ): - self.centerBox.add( self.backgdCanvasBox ) - self.centerBox.show_all() - else: - self.backgdCanvas.setImage( None ) + def setPostProcessPixBuf( self, pixbuf ): + if (pixbuf.get_width()>self.__class__.dim_THUMB_WIDTH): + pixbuf = pixbuf.scale_simple(self.__class__.dim_THUMB_WIDTH, self.__class__.dim_THUMB_HEIGHT, gtk.gdk.INTERP_NEAREST) + pixbuf = utils.grayScalePixBuf(pixbuf, True) + img = camerac.cairo_surface_from_gdk_pixbuf(pixbuf) + self.backgdCanvas.setImage(img) - def setPostProcessPixBuf( self, pixbuf ): - if (pixbuf.get_width()>self.__class__.dim_THUMB_WIDTH): - pixbuf = pixbuf.scale_simple(self.__class__.dim_THUMB_WIDTH, self.__class__.dim_THUMB_HEIGHT, gtk.gdk.INTERP_NEAREST) - pixbuf = utils.grayScalePixBuf(pixbuf, True) - img = _camera.cairo_surface_from_gdk_pixbuf(pixbuf) - self.backgdCanvas.setImage(img) + def showRecdMeta( self, recd ): + self.photographerNameLabel.set_label( recd.recorderName ) + self.nameTextfield.set_text( recd.title ) + self.nameTextfield.set_sensitive( True ) + self.tagsBuffer.set_text( recd.tags ) + self.dateDateLabel.set_label( utils.getDateString(recd.time) ) + self.photographerPanel.show() + self.namePanel.show() + self.datePanel.show() + self.tagsPanel.show() + self.tagsField.set_sensitive(True) - def showRecdMeta( self, recd ): - self.photographerNameLabel.set_label( recd.recorderName ) - self.nameTextfield.set_text( recd.title ) - self.nameTextfield.set_sensitive( True ) - self.tagsBuffer.set_text( recd.tags ) - self.dateDateLabel.set_label( utils.getDateString(recd.time) ) - self.photographerPanel.show() - self.namePanel.show() - self.datePanel.show() - self.tagsPanel.show() - self.tagsField.set_sensitive(True) + def setWaitCursor( self, win ): + win.set_cursor( gtk.gdk.Cursor(gtk.gdk.WATCH) ) - def setWaitCursor( self, win ): - win.set_cursor( gtk.gdk.Cursor(gtk.gdk.WATCH) ) - - - def setDefaultCursor( self, win ): - win.set_cursor( None ) + def setDefaultCursor( self, win ): + win.set_cursor( None ) class PhotoCanvas(P5): - def __init__(self): - P5.__init__(self) - self.img = None - self.drawImg = None - self.SCALING_IMG_ID = 0 - self.cacheWid = -1 - self.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - self.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) + def __init__(self): + P5.__init__(self) + self.img = None + self.drawImg = None + self.SCALING_IMG_ID = 0 + self.cacheWid = -1 + self.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) - def draw(self, ctx, w, h): - self.background( ctx, Constants.colorBlack, w, h ) + def draw(self, ctx, w, h): + self.background( ctx, Constants.colorBlack, w, h ) - if (self.img != None): + if (self.img != None): - if (w == self.img.get_width()): - self.cacheWid == w - self.drawImg = self.img + if (w == self.img.get_width()): + self.cacheWid == w + self.drawImg = self.img - #only scale images when you need to, otherwise you're wasting cycles, fool! - if (self.cacheWid != w): - if (self.SCALING_IMG_ID == 0): - self.drawImg = None - self.SCALING_IMG_ID = gobject.idle_add( self.resizeImage, w, h ) + #only scale images when you need to, otherwise you're wasting cycles, fool! + if (self.cacheWid != w): + if (self.SCALING_IMG_ID == 0): + self.drawImg = None + self.SCALING_IMG_ID = gobject.idle_add( self.resizeImage, w, h ) - if (self.drawImg != None): - #center the image based on the image size, and w & h - ctx.set_source_surface(self.drawImg, (w/2)-(self.drawImg.get_width()/2), (h/2)-(self.drawImg.get_height()/2)) - ctx.paint() + if (self.drawImg != None): + #center the image based on the image size, and w & h + ctx.set_source_surface(self.drawImg, (w/2)-(self.drawImg.get_width()/2), (h/2)-(self.drawImg.get_height()/2)) + ctx.paint() - self.cacheWid = w + self.cacheWid = w - def setImage(self, img): - self.cacheWid = -1 - self.img = img - self.drawImg = None - self.queue_draw() + def setImage(self, img): + self.cacheWid = -1 + self.img = img + self.drawImg = None + self.queue_draw() - def resizeImage(self, w, h): - self.SCALING_IMG_ID = 0 - if (self.img == None): - return + def resizeImage(self, w, h): + self.SCALING_IMG_ID = 0 + if (self.img == None): + return - #use image size in case 640 no more - scaleImg = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) - sCtx = cairo.Context(scaleImg) - sScl = (w+0.0)/(self.img.get_width()+0.0) - sCtx.scale( sScl, sScl ) - sCtx.set_source_surface( self.img, 0, 0 ) - sCtx.paint() - self.drawImg = scaleImg - self.cacheWid = w - self.queue_draw() + #use image size in case 640 no more + scaleImg = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) + sCtx = cairo.Context(scaleImg) + sScl = (w+0.0)/(self.img.get_width()+0.0) + sCtx.scale( sScl, sScl ) + sCtx.set_source_surface( self.img, 0, 0 ) + sCtx.paint() + self.drawImg = scaleImg + self.cacheWid = w + self.queue_draw() class xoPanel(P5): - def __init__(self): - P5.__init__(self) - self.xoGuy = None - self.lastStroke = None - self.lastFill = None + def __init__(self): + P5.__init__(self) + self.xoGuy = None + self.lastStroke = None + self.lastFill = None - def updateXoColors( self, strokeHex, fillHex ): - if (self.lastStroke != None): - if ((self.lastStroke == strokeHex) and (self.lastFill == fillHex)): - return + def updateXoColors( self, strokeHex, fillHex ): + if (self.lastStroke != None): + if ((self.lastStroke == strokeHex) and (self.lastFill == fillHex)): + return - lastStroke = strokeHex - lastFill = fillHex - self.xoGuy = utils.loadSvg(Constants.xoGuySvgData, strokeHex, fillHex) - self.queue_draw() + lastStroke = strokeHex + lastFill = fillHex + self.xoGuy = utils.loadSvg(Constants.xoGuySvgData, strokeHex, fillHex) + self.queue_draw() - def draw(self, ctx, w, h): - #todo: 2x buffer - self.background( ctx, Constants.colorButton, w, h ) + def draw(self, ctx, w, h): + #todo: 2x buffer + self.background( ctx, Constants.colorButton, w, h ) - if (self.xoGuy != None): - #todo: scale mr xo to fit in his box - ctx.scale( .6, .6 ) - self.xoGuy.render_cairo( ctx ) + if (self.xoGuy != None): + #todo: scale mr xo to fit in his box + ctx.scale( .6, .6 ) + self.xoGuy.render_cairo( ctx ) class ScrubberWindow(gtk.Window): - def __init__(self, ui): - gtk.Window.__init__(self) - self.ui = ui - self.UPDATE_INTERVAL = 500 - self.UPDATE_SCALE_ID = 0 - self.CHANGED_ID = 0 - self.was_playing = False - self.p_position = gst.CLOCK_TIME_NONE - self.p_duration = gst.CLOCK_TIME_NONE - - self.hbox = gtk.HBox() - self.hbox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - self.hbox.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) - self.add( self.hbox ) - - self.button = gtk.Button() - buttBox = gtk.EventBox() - buttBox.add(self.button) - buttBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - self.button.set_image( Constants.recPlayImg ) - self.button.set_property('can-default', True) - self.button.set_relief(gtk.RELIEF_NONE) - self.button.set_size_request( self.ui.recordButtWd, self.ui.recordButtWd ) - buttBox.set_size_request( self.ui.recordButtWd, self.ui.recordButtWd ) - #self.button.set_border_width( UI.dim_INSET/2 ) - self.button.show() - - buttBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - self.button.modify_bg( gtk.STATE_ACTIVE, Constants.colorBlack.gColor ) - - self.button.connect('clicked', self._buttonClickedCb) - self.hbox.pack_start(buttBox, expand=False) - - self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0) - self.hscale = gtk.HScale(self.adjustment) - self.hscale.set_draw_value(False) - self.hscale.set_update_policy(gtk.UPDATE_CONTINUOUS) - hscaleBox = gtk.EventBox() - hscaleBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - hscaleBox.add( self.hscale ) - self.hscale.connect('button-press-event', self._scaleButtonPressCb) - self.hscale.connect('button-release-event', self._scaleButtonReleaseCb) - self.hbox.pack_start(hscaleBox, expand=True) - - - def removeCallbacks( self ): - if (self.UPDATE_SCALE_ID != 0): - gobject.source_remove(self.UPDATE_SCALE_ID) - self.UPDATE_SCALE_ID = 0 - if (self.CHANGED_ID != 0): - gobject.source_remove(self.CHANGED_ID) - self.CHANGED_ID = 0 + def __init__(self, ui): + gtk.Window.__init__(self) + self.ui = ui + self.UPDATE_INTERVAL = 500 + self.UPDATE_SCALE_ID = 0 + self.CHANGED_ID = 0 + self.was_playing = False + self.p_position = gst.CLOCK_TIME_NONE + self.p_duration = gst.CLOCK_TIME_NONE + + self.hbox = gtk.HBox() + self.hbox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.hbox.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) + self.add( self.hbox ) + + self.button = gtk.Button() + buttBox = gtk.EventBox() + buttBox.add(self.button) + buttBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.button.set_image( Constants.recPlayImg ) + self.button.set_property('can-default', True) + self.button.set_relief(gtk.RELIEF_NONE) + self.button.set_size_request( self.ui.recordButtWd, self.ui.recordButtWd ) + buttBox.set_size_request( self.ui.recordButtWd, self.ui.recordButtWd ) + #self.button.set_border_width( UI.dim_INSET/2 ) + self.button.show() + + buttBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.button.modify_bg( gtk.STATE_ACTIVE, Constants.colorBlack.gColor ) + + self.button.connect('clicked', self._buttonClickedCb) + self.hbox.pack_start(buttBox, expand=False) + + self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0) + self.hscale = gtk.HScale(self.adjustment) + self.hscale.set_draw_value(False) + self.hscale.set_update_policy(gtk.UPDATE_CONTINUOUS) + hscaleBox = gtk.EventBox() + hscaleBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + hscaleBox.add( self.hscale ) + self.hscale.connect('button-press-event', self._scaleButtonPressCb) + self.hscale.connect('button-release-event', self._scaleButtonReleaseCb) + self.hbox.pack_start(hscaleBox, expand=True) + + + def removeCallbacks( self ): + if (self.UPDATE_SCALE_ID != 0): + gobject.source_remove(self.UPDATE_SCALE_ID) + self.UPDATE_SCALE_ID = 0 + if (self.CHANGED_ID != 0): + gobject.source_remove(self.CHANGED_ID) + self.CHANGED_ID = 0 - def reset(self): - self.adjustment.set_value(0) + def reset(self): + self.adjustment.set_value(0) - def _buttonClickedCb(self, widget): - self.play_toggled() + def _buttonClickedCb(self, widget): + self.play_toggled() - def set_button_play(self): - self.button.set_image(Constants.recPlayImg) + def set_button_play(self): + self.button.set_image(Constants.recPlayImg) - def set_button_pause(self): - self.button.set_image(Constants.recPauseImg) + def set_button_pause(self): + self.button.set_image(Constants.recPauseImg) - def play_toggled(self): - self.p_position, self.p_duration = self.ui.ca.gplay.queryPosition() - if (self.p_position == self.p_duration): - self.ui.ca.gplay.seek(0) - self.ui.ca.gplay.pause() + def play_toggled(self): + self.p_position, self.p_duration = self.ui.ca.gplay.queryPosition() + if (self.p_position == self.p_duration): + self.ui.ca.gplay.seek(0) + self.ui.ca.gplay.pause() - if self.ui.ca.gplay.is_playing(): - self.ui.ca.gplay.pause() - self.set_button_play() - else: - #if self.ui.ca.gplay.error: - # #todo: check if we have "error", and also to disable everything - # self.button.set_disabled() - #else: - self.doPlay() + if self.ui.ca.gplay.is_playing(): + self.ui.ca.gplay.pause() + self.set_button_play() + else: + #if self.ui.ca.gplay.error: + # #todo: check if we have "error", and also to disable everything + # self.button.set_disabled() + #else: + self.doPlay() - def doPlay(self): - self.ui.ca.gplay.play() - if self.UPDATE_SCALE_ID == 0: - self.UPDATE_SCALE_ID = gobject.timeout_add(self.UPDATE_INTERVAL, self._updateScaleCb) - self.set_button_pause() + def doPlay(self): + self.ui.ca.gplay.play() + if self.UPDATE_SCALE_ID == 0: + self.UPDATE_SCALE_ID = gobject.timeout_add(self.UPDATE_INTERVAL, self._updateScaleCb) + self.set_button_pause() - def _scaleButtonPressCb(self, widget, event): - #self.button.set_sensitive(False) - self.was_playing = self.ui.ca.gplay.is_playing() - if self.was_playing: - self.ui.ca.gplay.pause() + def _scaleButtonPressCb(self, widget, event): + #self.button.set_sensitive(False) + self.was_playing = self.ui.ca.gplay.is_playing() + if self.was_playing: + self.ui.ca.gplay.pause() - # don't timeout-update position during seek - if self.UPDATE_SCALE_ID != 0: - gobject.source_remove(self.UPDATE_SCALE_ID) - self.UPDATE_SCALE_ID = 0 + # don't timeout-update position during seek + if self.UPDATE_SCALE_ID != 0: + gobject.source_remove(self.UPDATE_SCALE_ID) + self.UPDATE_SCALE_ID = 0 - # make sure we get changed notifies - if self.CHANGED_ID == 0: - self.CHANGED_ID = self.hscale.connect('value-changed', self._scaleValueChangedCb) + # make sure we get changed notifies + if self.CHANGED_ID == 0: + self.CHANGED_ID = self.hscale.connect('value-changed', self._scaleValueChangedCb) - def _scaleButtonReleaseCb(self, widget, event): - # see seek.cstop_seek - widget.disconnect(self.CHANGED_ID) - self.CHANGED_ID = 0 + def _scaleButtonReleaseCb(self, widget, event): + # see seek.cstop_seek + widget.disconnect(self.CHANGED_ID) + self.CHANGED_ID = 0 - #self.button.set_sensitive(True) - if self.was_playing: - self.ui.ca.gplay.play() + #self.button.set_sensitive(True) + if self.was_playing: + self.ui.ca.gplay.play() - if self.UPDATE_SCALE_ID != 0: - pass - #print('Had a previous update timeout id') - else: - self.UPDATE_SCALE_ID = gobject.timeout_add(self.UPDATE_INTERVAL, self._updateScaleCb) + if self.UPDATE_SCALE_ID != 0: + pass + #print('Had a previous update timeout id') + else: + self.UPDATE_SCALE_ID = gobject.timeout_add(self.UPDATE_INTERVAL, self._updateScaleCb) - def _scaleValueChangedCb(self, scale): - real = long(scale.get_value() * self.p_duration / 100) # in ns - self.ui.ca.gplay.seek(real) - # allow for a preroll - self.ui.ca.gplay.get_state(timeout=50*gst.MSECOND) # 50 ms + def _scaleValueChangedCb(self, scale): + real = long(scale.get_value() * self.p_duration / 100) # in ns + self.ui.ca.gplay.seek(real) + # allow for a preroll + self.ui.ca.gplay.get_state(timeout=50*gst.MSECOND) # 50 ms - def _updateScaleCb(self): - self.p_position, self.p_duration = self.ui.ca.gplay.queryPosition() - if self.p_position != gst.CLOCK_TIME_NONE: - value = self.p_position * 100.0 / self.p_duration - if (value > 99): - value = 99 - elif (value < 0): - value = 0 + def _updateScaleCb(self): + self.p_position, self.p_duration = self.ui.ca.gplay.queryPosition() + if self.p_position != gst.CLOCK_TIME_NONE: + value = self.p_position * 100.0 / self.p_duration + if (value > 99): + value = 99 + elif (value < 0): + value = 0 - self.adjustment.set_value(value) + self.adjustment.set_value(value) - if self.ui.ca.gplay.is_playing() and (self.p_position == self.p_duration): - self.ui.ca.gplay.pause() - self.set_button_play() + if self.ui.ca.gplay.is_playing() and (self.p_position == self.p_duration): + self.ui.ca.gplay.pause() + self.set_button_play() - return True + return True class MaxButton(P5Button): - def __init__(self, ui): - P5Button.__init__(self) - self.ui = ui - - self.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - self.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) - - xs = [] - ys = [] - xs.append(0) - ys.append(0) - xs.append(self.ui.maxw) - ys.append(0) - xs.append(self.ui.maxw) - ys.append(self.ui.maxh) - xs.append(0) - ys.append(self.ui.maxh) - poly = Polygon( xs, ys ) - butt = Button( poly, 0, 0) - butt.addActionListener( self ) - self.maxS = "max" - butt.setActionCommand( self.maxS ) - self._butts.append( butt ) - - - def draw(self, ctx, w, h): - if (self.ui.FULLSCREEN): - Constants.maxEnlargeSvg.render_cairo( ctx ) - else: - Constants.maxReduceSvg.render_cairo( ctx ) - - - def fireButton(self, actionCommand): - if (actionCommand == self.maxS): - self.ui.doFullscreen() + def __init__(self, ui): + P5Button.__init__(self) + self.ui = ui + + self.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) + + xs = [] + ys = [] + xs.append(0) + ys.append(0) + xs.append(self.ui.maxw) + ys.append(0) + xs.append(self.ui.maxw) + ys.append(self.ui.maxh) + xs.append(0) + ys.append(self.ui.maxh) + poly = Polygon( xs, ys ) + butt = Button( poly, 0, 0) + butt.addActionListener( self ) + self.maxS = "max" + butt.setActionCommand( self.maxS ) + self._butts.append( butt ) + + + def draw(self, ctx, w, h): + if (self.ui.FULLSCREEN): + Constants.maxEnlargeSvg.render_cairo( ctx ) + else: + Constants.maxReduceSvg.render_cairo( ctx ) + + + def fireButton(self, actionCommand): + if (actionCommand == self.maxS): + self.ui.doFullscreen() class InfButton(P5Button): - #todo: just a gtk.Image here, no? - def __init__(self, ui): - P5Button.__init__(self) - self.ui = ui + #todo: just a gtk.Image here, no? + def __init__(self, ui): + P5Button.__init__(self) + self.ui = ui - self.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - self.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) + self.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) - self.set_size_request( 75, 75 ) + self.set_size_request( 75, 75 ) - xs = [] - ys = [] - xs.append(0) - ys.append(0) - xs.append(75) - ys.append(0) - xs.append(75) - ys.append(75) - xs.append(0) - ys.append(75) - poly = Polygon( xs, ys ) - butt = Button( poly, 0, 0) - butt.addActionListener( self ) - self.infS = "inf" - butt.setActionCommand( self.infS ) - self._butts.append( butt ) + xs = [] + ys = [] + xs.append(0) + ys.append(0) + xs.append(75) + ys.append(0) + xs.append(75) + ys.append(75) + xs.append(0) + ys.append(75) + poly = Polygon( xs, ys ) + butt = Button( poly, 0, 0) + butt.addActionListener( self ) + self.infS = "inf" + butt.setActionCommand( self.infS ) + self._butts.append( butt ) - def draw(self, ctx, w, h): - self.background( ctx, Constants.colorBlack, w, h ) - Constants.infoOnSvg.render_cairo( ctx ) + def draw(self, ctx, w, h): + self.background( ctx, Constants.colorBlack, w, h ) + Constants.infoOnSvg.render_cairo( ctx ) - def fireButton(self, actionCommand): - if (actionCommand == self.infS): - self.ui.infoButtonClicked() + def fireButton(self, actionCommand): + if (actionCommand == self.infS): + self.ui.infoButtonClicked() class RecordButton(gtk.Button): - def __init__(self): - gtk.Button.__init__(self) - self.sens = True - self.img = None - #todo: check on record state, compare button imgs + def __init__(self): + gtk.Button.__init__(self) + self.sens = True + self.img = None + #todo: check on record state, compare button imgs - def set_sensitive(self, sen, full): - if (sen == self.sens): - return - self.sens = sen + def set_sensitive(self, sen, full): + if (sen == self.sens): + return + self.sens = sen - if (self.sens): - self.set_image( Constants.recImg ) - else: - if (full): - self.set_image( Constants.fullInsensitiveImg ) - else: - self.set_image( Constants.recInsensitiveImg ) + if (self.sens): + self.set_image( Constants.recImg ) + else: + if (full): + self.set_image( Constants.fullInsensitiveImg ) + else: + self.set_image( Constants.recInsensitiveImg ) - super(RecordButton, self).set_sensitive(self.sens) + super(RecordButton, self).set_sensitive(self.sens) - def doRecordButton(self): - if (not self.sens): - return - self.set_image( Constants.recRedImg ) + def doRecordButton(self): + if (not self.sens): + return + self.set_image( Constants.recRedImg ) - def doNormalButton(self): - if (not self.sens): - return - self.set_image( Constants.recImg ) + def doNormalButton(self): + if (not self.sens): + return + self.set_image( Constants.recImg ) class RecordWindow(gtk.Window): - def __init__(self, ui): - gtk.Window.__init__(self) - self.ui = ui - self.num = -1 + def __init__(self, ui): + gtk.Window.__init__(self) + self.ui = ui + self.num = -1 - self.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - self.shutterButton = RecordButton() - self.shutterButton.set_size_request(self.ui.recordButtWd, self.ui.recordButtWd) - self.shutterButton.set_relief(gtk.RELIEF_NONE) - self.shutterButton.set_image( Constants.recImg ) - self.shutterButton.connect("clicked", self.ui._shutterClickCb) + self.shutterButton = RecordButton() + self.shutterButton.set_size_request(self.ui.recordButtWd, self.ui.recordButtWd) + self.shutterButton.set_relief(gtk.RELIEF_NONE) + self.shutterButton.set_image( Constants.recImg ) + self.shutterButton.connect("clicked", self.ui._shutterClickCb) - shutterBox = gtk.EventBox() - shutterBox.add( self.shutterButton ) - shutterBox.set_size_request( self.ui.controlBarHt, self.ui.controlBarHt ) + shutterBox = gtk.EventBox() + shutterBox.add( self.shutterButton ) + shutterBox.set_size_request( self.ui.controlBarHt, self.ui.controlBarHt ) - shutterBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - self.shutterButton.modify_bg( gtk.STATE_ACTIVE, Constants.colorBlack.gColor ) + shutterBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.shutterButton.modify_bg( gtk.STATE_ACTIVE, Constants.colorBlack.gColor ) - hbox = gtk.HBox() - self.add( hbox ) - leftPanel = gtk.VBox() - self.leftEvent = gtk.EventBox() - self.leftEvent.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - self.leftEvent.add( leftPanel ) - self.leftEvent.set_size_request(self.ui.vw/2-self.ui.controlBarHt, -1) - hbox.pack_start( self.leftEvent, expand=True ) + hbox = gtk.HBox() + self.add( hbox ) + leftPanel = gtk.VBox() + self.leftEvent = gtk.EventBox() + self.leftEvent.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.leftEvent.add( leftPanel ) + self.leftEvent.set_size_request(self.ui.vw/2-self.ui.controlBarHt, -1) + hbox.pack_start( self.leftEvent, expand=True ) - hbox.pack_start( shutterBox, expand=False ) + hbox.pack_start( shutterBox, expand=False ) - rightPanel = gtk.VBox() - self.rightEvent = gtk.EventBox() - self.rightEvent.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - self.rightEvent.add( rightPanel ) - hbox.pack_start( self.rightEvent, expand=True ) + rightPanel = gtk.VBox() + self.rightEvent = gtk.EventBox() + self.rightEvent.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.rightEvent.add( rightPanel ) + hbox.pack_start( self.rightEvent, expand=True ) - self.rightPanelLabel = gtk.Label() - rightPanel.pack_start( self.rightPanelLabel ) + self.rightPanelLabel = gtk.Label() + rightPanel.pack_start( self.rightPanelLabel ) - def updateCountdown(self, num): - if(num>0): - if (num != self.num): - ok = self.getCairoCountdown(num) - self.shutterButton.set_image(ok) - self.num = num - else: - self.num = -1 + def updateCountdown(self, num): + if(num>0): + if (num != self.num): + ok = self.getCairoCountdown(num) + self.shutterButton.set_image(ok) + self.num = num + else: + self.num = -1 - def getCairoCountdown(self, num): - return Constants.countdownImgs[int(num)] + def getCairoCountdown(self, num): + return Constants.countdownImgs[int(num)] - def minimize( self ): - self.leftEvent.set_size_request(-1, -1) - self.rightEvent.set_size_request(-1, -1) + def minimize( self ): + self.leftEvent.set_size_request(-1, -1) + self.rightEvent.set_size_request(-1, -1) - def maximize( self ): - w = self.ui.vw/2-self.ui.controlBarHt - self.rightEvent.set_size_request(w, -1) - self.leftEvent.set_size_request(w, -1) + def maximize( self ): + w = self.ui.vw/2-self.ui.controlBarHt + self.rightEvent.set_size_request(w, -1) + self.leftEvent.set_size_request(w, -1) - def displayDiscFullText( self, full ): - if (not full or self.ui.ca.m.MODE != Constants.MODE_PHOTO): - self.rightPanelLabel.set_text("") - self.minimize() - else: - fullMessage = Constants.istrYourDiskIsFull % {"1":Constants.istrJournal} - self.rightPanelLabel.set_text("<b><span foreground='gray'>" + fullMessage + "</span></b>") - self.rightPanelLabel.set_use_markup( True ) - self.rightPanelLabel.set_alignment(1, 1) - self.maximize() + def displayDiscFullText( self, full ): + if (not full or self.ui.ca.m.MODE != Constants.MODE_PHOTO): + self.rightPanelLabel.set_text("") + self.minimize() + else: + fullMessage = Constants.istrYourDiskIsFull % {"1":Constants.istrJournal} + self.rightPanelLabel.set_text("<b><span foreground='gray'>" + fullMessage + "</span></b>") + self.rightPanelLabel.set_use_markup( True ) + self.rightPanelLabel.set_alignment(1, 1) + self.maximize() class ProgressWindow(gtk.Window): - def __init__(self, ui): - gtk.Window.__init__(self) - self.ui = ui - self.update = "" - - self.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - self.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) - - eb = gtk.EventBox() - eb.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - eb.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) - self.add( eb ) - - vb = gtk.VBox() - vb.set_border_width(5) #todo: use variable - eb.add(vb) - - self.progBar = gtk.ProgressBar() - self.progBar.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) - self.progBar.modify_bg( gtk.STATE_ACTIVE, Constants.colorBlack.gColor ) - self.progBar.modify_bg( gtk.STATE_PRELIGHT, Constants.colorBlack.gColor ) - self.progBar.modify_bg( gtk.STATE_SELECTED, Constants.colorBlack.gColor ) - self.progBar.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) - vb.add( self.progBar ) - - hbox = gtk.HBox() - vb.add( hbox ) - self.infoLabel = gtk.Label() - self.infoLabel.set_alignment( 1, .5 ) - self.infoLabel.set_text( "<b><span foreground='black'>SPACE</span></b>") - self.infoLabel.set_use_markup( True ) - hbox.pack_start(self.infoLabel) - - - def updateProgress( self, amt, update, color='white' ): - self.progBar.set_fraction( amt ) - if (update != None and update != self.update): - self.update = update - self.infoLabel.set_text( "<b><span foreground='" + color + "'>"+self.update+"</span></b>") - self.infoLabel.set_use_markup( True ) - - if (self.update==""): - self.infoLabel.set_text( "<b><span foreground='black'>SPACE</span></b>") - self.infoLabel.set_use_markup( True ) - - if (amt >= 1): - self.progBar.set_fraction( 0 ) - - -class PhotoToolbar(gtk.Toolbar): - def __init__(self): - gtk.Toolbar.__init__(self) + def __init__(self, ui): + gtk.Window.__init__(self) + self.ui = ui + self.update = "" - img = ToolButton('media-photo') - img.connect('clicked', self._shutterClickCb) - img.get_icon_widget().set_property( 'fill-color', Instance.colorFill.hex ) - img.get_icon_widget().set_property( 'stroke-color', Instance.colorStroke.hex ) - self.insert(img, -1) - img.set_sensitive(False) - img.show() + self.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) - separator = gtk.SeparatorToolItem() - separator.set_draw(False) - separator.set_expand(True) - self.insert(separator, -1) - separator.show() + eb = gtk.EventBox() + eb.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + eb.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) + self.add( eb ) - timerCbb = gtk.combo_box_new_text() - self.timerCb = ToolComboBox(combo=timerCbb, label_text=Constants.istrTimer) - for i in range (0, len(Constants.TIMERS)): - if (i == 0): - self.timerCb.combo.append_text( Constants.istrNow ) - else: - self.timerCb.combo.append_text( Constants.istrSeconds % {"1":(str(Constants.TIMERS[i]))} ) - self.timerCb.combo.set_active(0) - self.insert( self.timerCb, -1 ) + vb = gtk.VBox() + vb.set_border_width(5) #todo: use variable + eb.add(vb) + self.progBar = gtk.ProgressBar() + self.progBar.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.progBar.modify_bg( gtk.STATE_ACTIVE, Constants.colorBlack.gColor ) + self.progBar.modify_bg( gtk.STATE_PRELIGHT, Constants.colorBlack.gColor ) + self.progBar.modify_bg( gtk.STATE_SELECTED, Constants.colorBlack.gColor ) + self.progBar.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) + vb.add( self.progBar ) - def _shutterClickCb(self, button): - pass - #self.ui.doShutter() + hbox = gtk.HBox() + vb.add( hbox ) + self.infoLabel = gtk.Label() + self.infoLabel.set_alignment( 1, .5 ) + self.infoLabel.set_text( "<b><span foreground='black'>SPACE</span></b>") + self.infoLabel.set_use_markup( True ) + hbox.pack_start(self.infoLabel) - def getTimer(self): - return Constants.TIMERS[self.timerCb.combo.get_active()] + def updateProgress( self, amt, update, color='white' ): + logging.debug('updateProgress %s' % amt) + self.progBar.set_fraction( amt ) + if (update != None and update != self.update): + self.update = update + self.infoLabel.set_text( "<b><span foreground='" + color + "'>"+self.update+"</span></b>") + self.infoLabel.set_use_markup( True ) -class VideoToolbar(gtk.Toolbar): - def __init__(self): - gtk.Toolbar.__init__(self) + if (self.update==""): + self.infoLabel.set_text( "<b><span foreground='black'>SPACE</span></b>") + self.infoLabel.set_use_markup( True ) - img = ToolButton('media-video') - img.connect('clicked', self._shutterClickCb) - img.get_icon_widget().set_property( 'fill-color', Instance.colorFill.hex ) - img.get_icon_widget().set_property( 'stroke-color', Instance.colorStroke.hex ) - self.insert(img, -1) - img.set_sensitive(False) - img.show() + if (amt >= 1): + self.progBar.set_fraction( 0 ) - separator = gtk.SeparatorToolItem() - separator.set_draw(False) - separator.set_expand(True) - self.insert(separator, -1) - separator.show() - timerCbb = gtk.combo_box_new_text() - self.timerCb = ToolComboBox(combo=timerCbb, label_text=Constants.istrTimer) - for i in range (0, len(Constants.TIMERS)): - if (i == 0): - self.timerCb.combo.append_text( Constants.istrNow ) - else: - self.timerCb.combo.append_text( Constants.istrSeconds % {"1":(str(Constants.TIMERS[i]))} ) - self.timerCb.combo.set_active(0) - self.insert( self.timerCb, -1 ) +class PhotoToolbar(gtk.Toolbar): + def __init__(self): + gtk.Toolbar.__init__(self) - separator2 = gtk.SeparatorToolItem() - separator2.set_draw(False) - separator2.set_expand(False) - separator2.set_size_request(UI.dim_INSET, -1) - self.insert( separator2, -1 ) + img = ToolButton('media-photo') + img.connect('clicked', self._shutterClickCb) + img.get_icon_widget().set_property( 'fill-color', Instance.colorFill.hex ) + img.get_icon_widget().set_property( 'stroke-color', Instance.colorStroke.hex ) + self.insert(img, -1) + img.set_sensitive(False) + img.show() - durCbb = gtk.combo_box_new_text() - self.durCb = ToolComboBox(combo=durCbb, label_text=Constants.istrDuration) - for i in range (0, len(Constants.DURATIONS)): - self.durCb.combo.append_text( Constants.istrMinutes % {"1":(str(Constants.DURATIONS[i]))} ) - self.durCb.combo.set_active(0) - self.insert(self.durCb, -1 ) + separator = gtk.SeparatorToolItem() + separator.set_draw(False) + separator.set_expand(True) + self.insert(separator, -1) + separator.show() + timerCbb = gtk.combo_box_new_text() + self.timerCb = ToolComboBox(combo=timerCbb, label_text=Constants.istrTimer) + for i in range (0, len(Constants.TIMERS)): + if (i == 0): + self.timerCb.combo.append_text( Constants.istrNow ) + else: + self.timerCb.combo.append_text( Constants.istrSeconds % {"1":(str(Constants.TIMERS[i]))} ) + self.timerCb.combo.set_active(0) + self.insert( self.timerCb, -1 ) - def _shutterClickCb(self, button): - pass - #self.ui.doShutter() + def _shutterClickCb(self, button): + pass + #self.ui.doShutter() - def getTimer(self): - return Constants.TIMERS[self.timerCb.combo.get_active()] + def getTimer(self): + return Constants.TIMERS[self.timerCb.combo.get_active()] - def getDuration(self): - return 60 * Constants.DURATIONS[self.durCb.combo.get_active()] +class VideoToolbar(gtk.Toolbar): + def __init__(self): + gtk.Toolbar.__init__(self) + + img = ToolButton('media-video') + img.connect('clicked', self._shutterClickCb) + img.get_icon_widget().set_property( 'fill-color', Instance.colorFill.hex ) + img.get_icon_widget().set_property( 'stroke-color', Instance.colorStroke.hex ) + self.insert(img, -1) + img.set_sensitive(False) + img.show() + + separator = gtk.SeparatorToolItem() + separator.set_draw(False) + separator.set_expand(True) + self.insert(separator, -1) + separator.show() + + combo = gtk.combo_box_new_text() + self.quality = ToolComboBox(combo=combo, + label_text=Constants.istrQuality+':') + self.quality.combo.append_text(Constants.istrLowQuality) + self.quality.combo.append_text(Constants.istrHighQuality) + self.quality.combo.append_text(Constants.istrBestQuality) + self.quality.combo.set_active(0) + self.insert(self.quality, -1 ) + + timerCbb = gtk.combo_box_new_text() + self.timerCb = ToolComboBox(combo=timerCbb, label_text=Constants.istrTimer) + for i in range (0, len(Constants.TIMERS)): + if (i == 0): + self.timerCb.combo.append_text( Constants.istrNow ) + else: + self.timerCb.combo.append_text( Constants.istrSeconds % {"1":(str(Constants.TIMERS[i]))} ) + self.timerCb.combo.set_active(0) + self.insert( self.timerCb, -1 ) + + separator2 = gtk.SeparatorToolItem() + separator2.set_draw(False) + separator2.set_expand(False) + separator2.set_size_request(UI.dim_INSET, -1) + self.insert( separator2, -1 ) + + durCbb = gtk.combo_box_new_text() + self.durCb = ToolComboBox(combo=durCbb, label_text=Constants.istrDuration) + for i in range (0, len(Constants.DURATIONS)): + self.durCb.combo.append_text( Constants.istrMinutes % {"1":(str(Constants.DURATIONS[i]))} ) + self.durCb.combo.set_active(0) + self.insert(self.durCb, -1 ) + + + def _shutterClickCb(self, button): + pass + #self.ui.doShutter() + + + def getTimer(self): + return Constants.TIMERS[self.timerCb.combo.get_active()] + + + def getDuration(self): + return 60 * Constants.DURATIONS[self.durCb.combo.get_active()] + + + def getQuality(self): + return self.quality.combo.get_active() class AudioToolbar(gtk.Toolbar): - def __init__(self): - gtk.Toolbar.__init__(self) - - img = ToolButton('media-audio') - img.connect('clicked', self._shutterClickCb) - img.get_icon_widget().set_property( 'fill-color', Instance.colorFill.hex ) - img.get_icon_widget().set_property( 'stroke-color', Instance.colorStroke.hex ) - self.insert(img, -1) - img.set_sensitive(False) - img.show() - - separator = gtk.SeparatorToolItem() - separator.set_draw(False) - separator.set_expand(True) - self.insert(separator, -1) - separator.show() - - timerCbb = gtk.combo_box_new_text() - self.timerCb = ToolComboBox(combo=timerCbb, label_text=Constants.istrTimer) - for i in range (0, len(Constants.TIMERS)): - if (i == 0): - self.timerCb.combo.append_text( Constants.istrNow ) - else: - self.timerCb.combo.append_text( Constants.istrSeconds % {"1":(str(Constants.TIMERS[i]))} ) - self.timerCb.combo.set_active(0) - self.insert( self.timerCb, -1 ) - - separator2 = gtk.SeparatorToolItem() - separator2.set_draw(False) - separator2.set_expand(False) - separator2.set_size_request(UI.dim_INSET, -1) - self.insert( separator2, -1 ) - - durCbb = gtk.combo_box_new_text() - self.durCb = ToolComboBox(combo=durCbb, label_text=Constants.istrDuration) - for i in range (0, len(Constants.DURATIONS)): - self.durCb.combo.append_text( Constants.istrMinutes % {"1":(str(Constants.DURATIONS[i]))} ) - self.durCb.combo.set_active(0) - self.insert(self.durCb, -1 ) - - - def _shutterClickCb(self, button): - pass - #self.ui.doShutter() - - - def getTimer(self): - return Constants.TIMERS[self.timerCb.combo.get_active()] - - - def getDuration(self): - return 60 * Constants.DURATIONS[self.durCb.combo.get_active()] + def __init__(self): + gtk.Toolbar.__init__(self) + + img = ToolButton('media-audio') + img.connect('clicked', self._shutterClickCb) + img.get_icon_widget().set_property( 'fill-color', Instance.colorFill.hex ) + img.get_icon_widget().set_property( 'stroke-color', Instance.colorStroke.hex ) + self.insert(img, -1) + img.set_sensitive(False) + img.show() + + separator = gtk.SeparatorToolItem() + separator.set_draw(False) + separator.set_expand(True) + self.insert(separator, -1) + separator.show() + + timerCbb = gtk.combo_box_new_text() + self.timerCb = ToolComboBox(combo=timerCbb, label_text=Constants.istrTimer) + for i in range (0, len(Constants.TIMERS)): + if (i == 0): + self.timerCb.combo.append_text( Constants.istrNow ) + else: + self.timerCb.combo.append_text( Constants.istrSeconds % {"1":(str(Constants.TIMERS[i]))} ) + self.timerCb.combo.set_active(0) + self.insert( self.timerCb, -1 ) + + separator2 = gtk.SeparatorToolItem() + separator2.set_draw(False) + separator2.set_expand(False) + separator2.set_size_request(UI.dim_INSET, -1) + self.insert( separator2, -1 ) + + durCbb = gtk.combo_box_new_text() + self.durCb = ToolComboBox(combo=durCbb, label_text=Constants.istrDuration) + for i in range (0, len(Constants.DURATIONS)): + self.durCb.combo.append_text( Constants.istrMinutes % {"1":(str(Constants.DURATIONS[i]))} ) + self.durCb.combo.set_active(0) + self.insert(self.durCb, -1 ) + + + def _shutterClickCb(self, button): + pass + #self.ui.doShutter() + + + def getTimer(self): + return Constants.TIMERS[self.timerCb.combo.get_active()] + + + def getDuration(self): + return 60 * Constants.DURATIONS[self.durCb.combo.get_active()] @@ -6,113 +6,103 @@ import statvfs import cairo import gc import gtk -from hashlib import md5 import time from time import strftime from sugar import util -import _camera +import camerac def getStringFromPixbuf(pixbuf): - data = [""] - pixbuf.save_to_callback(_saveDataToBufferCb, "png", {}, data) - return base64.b64encode(str(data[0])) + data = [""] + pixbuf.save_to_callback(_saveDataToBufferCb, "png", {}, data) + return base64.b64encode(str(data[0])) def _saveDataToBufferCb(buf, data): - data[0] += buf - return True + data[0] += buf + return True def getPixbufFromString(str): - pbl = gtk.gdk.PixbufLoader() - data = base64.b64decode( str ) - pbl.write(data) - pbl.close() - return pbl.get_pixbuf() + pbl = gtk.gdk.PixbufLoader() + data = base64.b64decode( str ) + pbl.write(data) + pbl.close() + return pbl.get_pixbuf() def loadSvg( data, stroke, fill ): - if ((stroke == None) or (fill == None)): - return rsvg.Handle( data=data ) + if ((stroke == None) or (fill == None)): + return rsvg.Handle( data=data ) - entity = '<!ENTITY fill_color "%s">' % fill - data = re.sub('<!ENTITY fill_color .*>', entity, data) + entity = '<!ENTITY fill_color "%s">' % fill + data = re.sub('<!ENTITY fill_color .*>', entity, data) - entity = '<!ENTITY stroke_color "%s">' % stroke - data = re.sub('<!ENTITY stroke_color .*>', entity, data) + entity = '<!ENTITY stroke_color "%s">' % stroke + data = re.sub('<!ENTITY stroke_color .*>', entity, data) - return rsvg.Handle( data=data ) + return rsvg.Handle( data=data ) def getUniqueFilepath( path, i ): - pathOb = os.path.abspath( path ) - newPath = os.path.join( os.path.dirname(pathOb), str( str(i) + os.path.basename(pathOb) ) ) - if (os.path.exists(newPath)): - i = i + 1 - return getUniqueFilepath( pathOb, i ) - else: - return os.path.abspath( newPath ) - - -def md5File( filepath ): - md = md5() - f = file( filepath, 'rb' ) - md.update( f.read() ) - digest = md.hexdigest() - hash = util.printable_hash(digest) - return hash + pathOb = os.path.abspath( path ) + newPath = os.path.join( os.path.dirname(pathOb), str( str(i) + os.path.basename(pathOb) ) ) + if (os.path.exists(newPath)): + i = i + 1 + return getUniqueFilepath( pathOb, i ) + else: + return os.path.abspath( newPath ) def generateThumbnail( pixbuf, scale, thumbw, thumbh ): - #need to generate thumbnail version here - thumbImg = cairo.ImageSurface(cairo.FORMAT_ARGB32, thumbw, thumbh) - tctx = cairo.Context(thumbImg) - img = _camera.cairo_surface_from_gdk_pixbuf(pixbuf) - tctx.scale(scale, scale) - tctx.set_source_surface(img, 0, 0) - tctx.paint() - gc.collect() - return thumbImg + #need to generate thumbnail version here + thumbImg = cairo.ImageSurface(cairo.FORMAT_ARGB32, thumbw, thumbh) + tctx = cairo.Context(thumbImg) + img = camerac.cairo_surface_from_gdk_pixbuf(pixbuf) + tctx.scale(scale, scale) + tctx.set_source_surface(img, 0, 0) + tctx.paint() + gc.collect() + return thumbImg def scaleSvgToDim( handle, dim ): - #todo... - scale = 1.0 + #todo... + scale = 1.0 - svgDim = handle.get_dimension_data() - if (svgDim[0] > dim[0]): - pass + svgDim = handle.get_dimension_data() + if (svgDim[0] > dim[0]): + pass - return scale + return scale def getDateString( when ): - return strftime( "%a, %b %d, %I:%M:%S %p", time.localtime(when) ) + return strftime( "%a, %b %d, %I:%M:%S %p", time.localtime(when) ) def grayScalePixBuf2( pb, copy ): - arr = pb.get_pixels_array() - if (copy): - arr = arr.copy() - for row in arr: - for pxl in row: - y = 0.3*pxl[0][0]+0.59*pxl[1][0]+0.11*pxl[2][0] - pxl[0][0] = y - pxl[1][0] = y - pxl[2][0] = y - return gtk.gdk.pixbuf_new_from_array(arr, pb.get_colorspace(), pb.get_bits_per_sample()) + arr = pb.get_pixels_array() + if (copy): + arr = arr.copy() + for row in arr: + for pxl in row: + y = 0.3*pxl[0][0]+0.59*pxl[1][0]+0.11*pxl[2][0] + pxl[0][0] = y + pxl[1][0] = y + pxl[2][0] = y + return gtk.gdk.pixbuf_new_from_array(arr, pb.get_colorspace(), pb.get_bits_per_sample()) def grayScalePixBuf( pb, copy ): - pb2 = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, pb.get_width(), pb.get_height()) - pb.saturate_and_pixelate(pb2, 0, 0) - return pb2 + pb2 = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, pb.get_width(), pb.get_height()) + pb.saturate_and_pixelate(pb2, 0, 0) + return pb2 def getFreespaceKb( ): - stat = os.statvfs("/home") - freebytes = stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL] - freekb = freebytes / 1024 - return freekb + stat = os.statvfs("/home") + freebytes = stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL] + freekb = freebytes / 1024 + return freekb |