diff options
Diffstat (limited to 'glive.py')
-rw-r--r-- | glive.py | 110 |
1 files changed, 66 insertions, 44 deletions
@@ -39,8 +39,8 @@ import utils logger = logging.getLogger('glive') OGG_TRAITS = { - 0: { 'width': 160, 'height': 120, 'quality': 16 }, - 1: { 'width': 400, 'height': 300, 'quality': 16 } } + 0: { 'width': 160, 'height': 120, 'quality': 16 }, + 1: { 'width': 400, 'height': 300, 'quality': 16 } } class Glive: PHOTO_MODE_PHOTO = 0 @@ -50,6 +50,7 @@ class Glive: self.activity = activity_obj self.model = model + self._eos_cb = None self._has_camera = False self._can_limit_framerate = False @@ -68,6 +69,7 @@ class Glive: self._detect_camera() self._pipeline = Gst.Pipeline() + self._create_photobin() self._create_audiobin() self._create_videobin() @@ -86,7 +88,7 @@ class Glive: v4l2src = Gst.ElementFactory.make('v4l2src', 'v4l2src') - if v4l2src.props.device_name is None: + if v4l2src.get_property('device-name') is None: return self._has_camera = True @@ -97,15 +99,25 @@ class Glive: # can't find a way to do this (at this time, XO-1 cafe camera driver # doesn't support framerate changes, but gstreamer caps suggest # otherwise) + pipeline = Gst.Pipeline() + + # WARNING **: 0.10-style raw video caps are being created. Should be video/x-raw,format=(string) caps = Gst.Caps.from_string('video/x-raw-yuv,framerate=10/1') + camerafilter = Gst.ElementFactory.make("capsfilter", "capsfilter") + camerafilter.set_property("caps", caps) + fsink = Gst.ElementFactory.make('fakesink', 'fakesink') + pipeline.add(v4l2src) - # FIXME: TypeError: argument dest: Expected Gst.Element, but got gi.repository.Gst.Caps - #pipeline.add(caps) + pipeline.add(camerafilter) pipeline.add(fsink) - v4l2src.link(fsink) + + v4l2src.link(camerafilter) + camerafilter.link(fsink) + self._can_limit_framerate = pipeline.set_state(Gst.State.PAUSED) != Gst.StateChangeReturn.FAILURE + pipeline.set_state(Gst.State.NULL) def get_has_camera(self): @@ -115,7 +127,7 @@ class Glive: def _create_photobin(self): queue = Gst.ElementFactory.make("queue", "pbqueue") - queue.set_property("leaky", True) + queue.set_property("leaky", 1) queue.set_property("max-size-buffers", 1) colorspace = Gst.ElementFactory.make("videoconvert", "pbcolorspace") @@ -151,8 +163,9 @@ class Glive: if not hwdev_available: src.set_property("device", "default") + # WARNING **: 0.10-style raw audio caps are being created. Should be audio/x-raw,format=(string) srccaps = Gst.Caps.from_string("audio/x-raw-int,rate=16000,channels=1,depth=16") - + # guarantee perfect stream, important for A/V sync rate = Gst.ElementFactory.make("audiorate", 'audiorate') @@ -161,7 +174,7 @@ class Glive: # (possibly a gstreamer/ALSA bug -- even if it gets caught up, it # should be able to resync without problem) queue = Gst.ElementFactory.make("queue", "audioqueue") - queue.set_property("leaky", True) # prefer fresh data + queue.set_property("leaky", 1) # prefer fresh data queue.set_property("max-size-time", 5000000000) # 5 seconds queue.set_property("max-size-buffers", 500) queue.connect("overrun", self._log_queue_overrun) @@ -172,13 +185,14 @@ class Glive: sink.set_property("location", os.path.join(Instance.instancePath, "output.wav")) self._audiobin = Gst.Bin() + self._audiobin.add(src) self._audiobin.add(rate) self._audiobin.add(queue) self._audiobin.add(enc) self._audiobin.add(sink) - rate.link(rate) + src.link(rate) rate.link(queue) queue.link(enc) enc.link(sink) @@ -192,9 +206,9 @@ class Glive: scale = Gst.ElementFactory.make("videoscale", "vbscale") - scalecapsfilter = Gst.ElementFactory.make("capsfilter", "scalecaps") - + # WARNING **: 0.10-style raw video caps are being created. Should be video/x-raw,format=(string) scalecaps = Gst.Caps.from_string('video/x-raw-yuv,width=160,height=120') + scalecapsfilter = Gst.ElementFactory.make("capsfilter", "scalecaps") scalecapsfilter.set_property("caps", scalecaps) colorspace = Gst.ElementFactory.make("videoconvert", "vbcolorspace") @@ -208,6 +222,7 @@ class Glive: sink.set_property("location", os.path.join(Instance.instancePath, "output.ogg")) self._videobin = Gst.Bin() + self._videobin.add(queue) self._videobin.add(scale) self._videobin.add(scalecapsfilter) @@ -225,7 +240,7 @@ class Glive: pad = queue.get_static_pad("sink") self._videobin.add_pad(Gst.GhostPad.new("sink", pad)) - + def _create_xbin(self): scale = Gst.ElementFactory.make("videoscale", 'videoscale') @@ -237,6 +252,7 @@ class Glive: xsink.set_property("sync", False) self._xbin = Gst.Bin() + self._xbin.add(scale) self._xbin.add(cspace) self._xbin.add(xsink) @@ -260,13 +276,6 @@ class Glive: return src = Gst.ElementFactory.make("v4l2src", "camsrc") - - try: - # old gst-plugins-good does not have this property - src.set_property("queue-size", 2) - - except: - pass # if possible, it is important to place the framerate limit directly # on the v4l2src so that it gets communicated all the way down to the @@ -286,23 +295,27 @@ class Glive: # the FPS value. rate = Gst.ElementFactory.make("videorate", 'videorate') ratecaps = Gst.Caps.from_string('video/x-raw-yuv,framerate=10/1') - + tee = Gst.ElementFactory.make("tee", "tee") queue = Gst.ElementFactory.make("queue", "dispqueue") # prefer fresh frames - queue.set_property("leaky", True) + queue.set_property("leaky", 1) queue.set_property("max-size-buffers", 2) self._pipeline.add(src) self._pipeline.add(rate) self._pipeline.add(tee) self._pipeline.add(queue) + src.link(rate) - # FIXME: TypeError: argument dest: Expected Gst.Element, but got gi.repository.Gst.Caps + # FIXME: TypeError: Expected Gst.Element, but got GObjectMeta #rate.link(srccaps) + rate.link(tee) + # FIXME: TypeError: Expected Gst.Element, but got GObjectMeta #tee.link(ratecaps) + tee.link(queue) self._xvsink = Gst.ElementFactory.make("xvimagesink", "xsink") @@ -406,6 +419,7 @@ class Glive: return self._pipeline.get_state(0)[1] def stop_recording_audio(self): + # We should be able to simply pause and remove the audiobin, but # this seems to cause a gstreamer segfault. So we stop the whole # pipeline while manipulating it. @@ -423,16 +437,16 @@ class Glive: self.model.still_ready(self._audio_pixbuf) line = 'filesrc location=' + audio_path + ' name=audioFilesrc ! wavparse name=audioWavparse ! audioconvert name=audioAudioconvert ! vorbisenc name=audioVorbisenc ! oggmux name=audioOggmux ! filesink name=audioFilesink' - audioline = gst.parse_launch(line) + audioline = Gst.parse_launch(line) taglist = self._get_tags(constants.TYPE_AUDIO) if self._audio_pixbuf: pixbuf_b64 = utils.getStringFromPixbuf(self._audio_pixbuf) - taglist[Gst.TAG_EXTENDED_COMMENT] = "coverart=" + pixbuf_b64 + #taglist[Gst.TAG_EXTENDED_COMMENT] = "coverart=" + pixbuf_b64 vorbis_enc = audioline.get_by_name('audioVorbisenc') - vorbis_enc.merge_tags(taglist, Gst.TAG_MERGE_REPLACE_ALL) + #vorbis_enc.merge_tags(taglist, Gst.TagMergeMode.REPLACE_ALL) audioFilesink = audioline.get_by_name('audioFilesink') audioOggFilepath = os.path.join(Instance.instancePath, "output.ogg") @@ -442,11 +456,13 @@ class Glive: audioBus.add_signal_watch() self._audio_transcode_handler = audioBus.connect('message', self._onMuxedAudioMessageCb, audioline) self._transcode_id = GObject.timeout_add(200, self._transcodeUpdateCb, audioline) + audioline.set_state(Gst.State.PLAYING) def _get_tags(self, type): tl = Gst.TagList() + """ tl[Gst.TAG_ARTIST] = self.model.get_nickname() tl[Gst.TAG_COMMENT] = "olpc" #this is unfortunately, unreliable @@ -457,7 +473,7 @@ class Glive: # Translators: photo by photographer, e.g. "Photo by Mary" tl[Gst.TAG_TITLE] = _('%(type)s by %(name)s') % {'type': stringType, - 'name': self.model.get_nickname()} + 'name': self.model.get_nickname()}""" return tl def _take_photo(self, photo_mode): @@ -510,10 +526,11 @@ class Glive: return self._ogg_quality = quality + self._config_videobin(OGG_TRAITS[quality]['quality'], OGG_TRAITS[quality]['width'], OGG_TRAITS[quality]['height']) - + # If we use pad blocking and adjust the pipeline on-the-fly, the # resultant video has bad A/V sync :( # If we pause the pipeline while adjusting it, the A/V sync is better @@ -552,7 +569,7 @@ class Glive: self._eos_cb = self._video_eos 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 _video_eos(self): self._pipeline.set_state(Gst.State.NULL) @@ -561,7 +578,7 @@ class Glive: self._pipeline.remove(self._audiobin) self.model.shutter_sound() - + if len(self._thumb_pipes) > 0: thumbline = self._thumb_pipes[-1] thumbline.get_by_name('thumb_fakesink').disconnect(self._thumb_handoff_handler) @@ -572,10 +589,12 @@ class Glive: # FIXME: inform model of failure? return - line = 'filesrc location=' + ogg_path + ' name=thumbFilesrc ! oggdemux name=thumbOggdemux ! theoradec name=thumbTheoradec ! tee name=thumb_tee ! queue name=thumb_queue ! ffmpegcolorspace name=thumbFfmpegcolorspace ! jpegenc name=thumbJPegenc ! fakesink name=thumb_fakesink' + line = 'filesrc location=' + ogg_path + ' name=thumbFilesrc ! oggdemux name=thumbOggdemux ! theoradec name=thumbTheoradec ! tee name=thumb_tee ! queue name=thumb_queue ! videoconvert name=thumbFfmpegcolorspace ! jpegenc name=thumbJPegenc ! fakesink name=thumb_fakesink' + thumbline = Gst.parse_launch(line) + thumb_queue = thumbline.get_by_name('thumb_queue') - thumb_queue.set_property("leaky", True) + thumb_queue.set_property("leaky", 1) thumb_queue.set_property("max-size-buffers", 1) thumb_tee = thumbline.get_by_name('thumb_tee') thumb_fakesink = thumbline.get_by_name('thumb_fakesink') @@ -584,18 +603,19 @@ class Glive: self._thumb_pipes.append(thumbline) self._thumb_exposure_open = True thumbline.set_state(Gst.State.PLAYING) - + def copyThumbPic(self, fsink, buffer, pad, user_data=None): if not self._thumb_exposure_open: return self._thumb_exposure_open = False - loader = GdkPibuf.PixbufLoader.new_with_mime_type("image/jpeg") - loader.write(buffer) - loader.close() - self.thumbBuf = loader.get_pixbuf() - self.model.still_ready(self.thumbBuf) + #loader = GdkPixbuf.PixbufLoader.new_with_mime_type("image/jpeg") + # FIXME: TypeError: Must be sequence, not Buffer + #loader.write(buffer) + #loader.close() + #self.thumbBuf = loader.get_pixbuf() + #self.model.still_ready(self.thumbBuf) self._thumb_element('thumb_tee').unlink(self._thumb_element('thumb_queue')) @@ -604,9 +624,10 @@ class Glive: muxFilepath = os.path.join(Instance.instancePath, "mux.ogg") muxline = Gst.parse_launch('filesrc location=' + str(oggFilepath) + ' name=muxVideoFilesrc ! oggdemux name=muxOggdemux ! theoraparse ! 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._get_tags(constants.TYPE_VIDEO) vorbis_enc = muxline.get_by_name('muxVorbisenc') - vorbis_enc.merge_tags(taglist, Gst.TAG_MERGE_REPLACE_ALL) + #vorbis_enc.merge_tags(taglist, Gst.TagMergeMode.REPLACE_ALL) muxBus = muxline.get_bus() muxBus.add_signal_watch() @@ -630,13 +651,13 @@ class Glive: def _query_position(self, pipe): try: - position, format = pipe.query_position(Gst.FORMAT_TIME) + position, format = pipe.query_position(Gst.Format.TIME) except: - position = Gste.CLOCK_TIME_NONE + position = Gst.CLOCK_TIME_NONE try: - duration, format = pipe.query_duration(Gst.FORMAT_TIME) + duration, format = pipe.query_duration(Gst.Format.TIME) except: duration = Gst.CLOCK_TIME_NONE @@ -645,7 +666,7 @@ class Glive: def _onMuxedVideoMessageCb(self, bus, message, pipe): - if message.type != Gst.MESSAGE_EOS: + if message.type != Gst.MessageType.EOS: return True GObject.source_remove(self._video_transcode_handler) @@ -667,7 +688,7 @@ class Glive: def _onMuxedAudioMessageCb(self, bus, message, pipe): - if message.type != Gst.MESSAGE_EOS: + if message.type != Gst.MessageType.EOS: return True GObject.source_remove(self._audio_transcode_handler) @@ -682,6 +703,7 @@ class Glive: oggFilepath = os.path.join(Instance.instancePath, "output.ogg") os.remove( wavFilepath ) self.model.save_audio(oggFilepath, self._audio_pixbuf) + return False def _bus_message_handler(self, bus, message): |