Skip to content

Commit

Permalink
Porting generic decoder
Browse files Browse the repository at this point in the history
  • Loading branch information
ApsOps committed Jul 25, 2014
1 parent 38d1892 commit cf3cfbf
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 57 deletions.
13 changes: 6 additions & 7 deletions flumotion/component/decodercomponent.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
Decoder component, participating in the stream
"""

import gst
import gst.interfaces
from gi.repository import Gst

from flumotion.common.i18n import N_, gettexter
from flumotion.common import errors, messages, gstreamer
Expand Down Expand Up @@ -62,21 +61,21 @@ def _add_video_effects(self):
width = props.get('width', None)
height = props.get('height', None)
# Expressed in ms
interval = props.get('keyunits-interval', 10000) * gst.MSECOND
interval = props.get('keyunits-interval', 10000) * Gst.MSECOND
fr = props.get('framerate', (25, 2))
framerate = gst.Fraction(fr[0], fr[1])
framerate = Gst.Fraction(fr[0], fr[1])

self.vr = videorate.Videorate('videorate', None,
self.pipeline, framerate)
self.addEffect(self.vr)
#self.vr.effectBin.set_state(gst.STATE_PLAYING)
#self.vr.effectBin.set_state(Gst.State.PLAYING)
self.debug("Videorate added")

self.videoscaler = videoscale.Videoscale('videoscale', self,
None, self.pipeline,
width, height, is_square, add_borders)
self.addEffect(self.videoscaler)
#self.videoscaler.effectBin.set_state(gst.STATE_PLAYING)
#self.videoscaler.effectBin.set_state(Gst.State.PLAYING)
self.debug("Videoscaler added")

self.vkuscheduler = kuscheduler.KeyUnitsScheduler('keyunits-scheduler',
Expand All @@ -90,7 +89,7 @@ def _add_audio_effects(self):
props = self.config['properties']
samplerate = props.get('samplerate', 44100)
channels = props.get('channels', 2)
interval = props.get('keyunits-interval', 10000) * gst.MSECOND
interval = props.get('keyunits-interval', 10000) * Gst.MSECOND

self.ar = audioconvert.Audioconvert('audioconvert', None,
self.pipeline, channels=channels,
Expand Down
119 changes: 69 additions & 50 deletions flumotion/component/decoders/generic/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
#
# Headers in this file shall remain intact.

import gst
import gobject
from gi.repository import Gst
from gi.repository import GObject
import threading

from flumotion.component import decodercomponent as dc
Expand All @@ -27,8 +27,8 @@

__version__ = "$Rev: 7162 $"

BASIC_AUDIO_CAPS = "audio/x-raw-int;audio/x-raw-float"
BASIC_VIDEO_CAPS = "video/x-raw-yuv;video/x-raw-rgb"
BASIC_AUDIO_CAPS = "audio/x-raw;audio/x-raw"
BASIC_VIDEO_CAPS = "video/x-raw;video/x-raw"

# FIXME: The GstAutoplugSelectResult enum has no bindings in gst-python.
# Replace this when the enum is exposed in the bindings.
Expand All @@ -44,44 +44,44 @@ def __init__(self, name, caps, linked=False):
self.caps = caps


class SyncKeeper(gst.Element):
__gstdetails__ = ('SyncKeeper', 'Generic',
class SyncKeeper(Gst.Element):
__gstmetadata__ = ('SyncKeeper', 'Generic',
'Retimestamp the output to be contiguous and maintain '
'the sync', 'Xavier Queralt')
_audiosink = gst.PadTemplate("audio-in",
gst.PAD_SINK,
gst.PAD_ALWAYS,
gst.caps_from_string(BASIC_AUDIO_CAPS))
_videosink = gst.PadTemplate("video-in",
gst.PAD_SINK,
gst.PAD_ALWAYS,
gst.caps_from_string(BASIC_VIDEO_CAPS))
_audiosrc = gst.PadTemplate("audio-out",
gst.PAD_SRC,
gst.PAD_ALWAYS,
gst.caps_from_string(BASIC_AUDIO_CAPS))
_videosrc = gst.PadTemplate("video-out",
gst.PAD_SRC,
gst.PAD_ALWAYS,
gst.caps_from_string(BASIC_VIDEO_CAPS))
_audiosink = Gst.PadTemplate.new("audio-in",
Gst.PadDirection.SINK,
Gst.PadPresence.ALWAYS,
Gst.Caps.from_string(BASIC_AUDIO_CAPS))
_videosink = Gst.PadTemplate.new("video-in",
Gst.PadDirection.SINK,
Gst.PadPresence.ALWAYS,
Gst.Caps.from_string(BASIC_VIDEO_CAPS))
_audiosrc = Gst.PadTemplate.new("audio-out",
Gst.PadDirection.SRC,
Gst.PadPresence.ALWAYS,
Gst.Caps.from_string(BASIC_AUDIO_CAPS))
_videosrc = Gst.PadTemplate.new("video-out",
Gst.PadDirection.SRC,
Gst.PadPresence.ALWAYS,
Gst.Caps.from_string(BASIC_VIDEO_CAPS))

def __init__(self):
gst.Element.__init__(self)
Gst.Element.__init__(self)

# create source pads
self.audiosrc = gst.Pad(self._audiosrc, "audio-out")
self.audiosrc = Gst.Pad.new_from_template(self._audiosrc, "audio-out")
self.add_pad(self.audiosrc)
self.videosrc = gst.Pad(self._videosrc, "video-out")
self.videosrc = Gst.Pad.new_from_template(self._videosrc, "video-out")
self.add_pad(self.videosrc)

# create the sink pads and set the chain and event function
self.audiosink = gst.Pad(self._audiosink, "audio-in")
self.audiosink = Gst.Pad.new_from_template(self._audiosink, "audio-in")
self.audiosink.set_chain_function(lambda pad, buffer:
self.chainfunc(pad, buffer, self.audiosrc))
self.audiosink.set_event_function(lambda pad, buffer:
self.eventfunc(pad, buffer, self.audiosrc))
self.add_pad(self.audiosink)
self.videosink = gst.Pad(self._videosink, "video-in")
self.videosink = Gst.Pad.new_from_template(self._videosink, "video-in")
self.videosink.set_chain_function(lambda pad, buffer:
self.chainfunc(pad, buffer, self.videosrc))
self.videosink.set_event_function(lambda pad, buffer:
Expand All @@ -99,7 +99,7 @@ def __init__(self):
def _send_new_segment(self):
for pad in [self.videosrc, self.audiosrc]:
pad.push_event(
gst.event_new_new_segment(True, 1.0, gst.FORMAT_TIME,
Gst.Event.new_segment(True, 1.0, Gst.Format.TIME,
self._syncTimestamp, -1, 0))
self._sendNewSegment = False

Expand All @@ -115,50 +115,50 @@ def _update_sync_point(self, start, position):
self._syncOffset = start
self._resetReceived = False
self.info("Update sync point to % r, offset to %r" %
(gst.TIME_ARGS(self._syncTimestamp),
(gst.TIME_ARGS(self._syncOffset))))
(Gst.TIME_ARGS(self._syncTimestamp),
(Gst.TIME_ARGS(self._syncOffset))))

def chainfunc(self, pad, buf, srcpad):
self.log("Input %s timestamp: %s, %s" %
(srcpad is self.audiosrc and 'audio' or 'video',
gst.TIME_ARGS(buf.timestamp),
gst.TIME_ARGS(buf.duration)))
Gst.TIME_ARGS(buf.pts),
Gst.TIME_ARGS(buf.duration)))

if not self._sendNewSegment:
self._send_new_segment()

try:
self._lock.acquire()
# Discard buffers outside the configured segment
if buf.timestamp < self._syncOffset:
if buf.pts < self._syncOffset:
self.warning("Could not clip buffer to segment")
return gst.FLOW_OK
if buf.timestamp == gst.CLOCK_TIME_NONE:
return gst.FLOW_OK
return Gst.FlowReturn.OK
if buf.pts == Gst.CLOCK_TIME_NONE:
return Gst.FlowReturn.OK
# Get the input stream time of the buffer
buf.timestamp -= self._syncOffset
buf.pts -= self._syncOffset
# Set the accumulated stream time
buf.timestamp += self._syncTimestamp
buf.pts += self._syncTimestamp
duration = 0
if buf.duration != gst.CLOCK_TIME_NONE:
if buf.duration != Gst.CLOCK_TIME_NONE:
duration = buf.duration
self._totalTime = max(buf.timestamp + duration, self._totalTime)
self._totalTime = max(buf.pts + duration, self._totalTime)

self.log("Output %s timestamp: %s, %s" %
(srcpad is self.audiosrc and 'audio' or 'video',
gst.TIME_ARGS(buf.timestamp),
gst.TIME_ARGS(buf.duration)))
Gst.TIME_ARGS(buf.pts),
Gst.TIME_ARGS(buf.duration)))
finally:
self._lock.release()

srcpad.push(buf)
return gst.FLOW_OK
return Gst.FlowReturn.OK

def eventfunc(self, pad, event, srcpad):
self.debug("Received event %r from %s" % (event, event.src))
try:
self._lock.acquire()
if event.type == gst.EVENT_NEWSEGMENT:
if event.type == Gst.EventType.SEGMENT:
u, r, f, start, s, position = event.parse_new_segment()
self._update_sync_point(start, position)
if gstreamer.event_is_flumotion_reset(event):
Expand All @@ -168,17 +168,36 @@ def eventfunc(self, pad, event, srcpad):
self._lock.release()

# forward all the events except the new segment events
if event.type != gst.EVENT_NEWSEGMENT:
if event.type != Gst.EventType.SEGMENT:
return srcpad.push_event(event)
return True

gobject.type_register(SyncKeeper)
gst.element_register(SyncKeeper, "synckeeper", gst.RANK_MARGINAL)
def plugin_init(plugin, userarg):
name = plugin.get_name()
pluginType = GObject.type_register(userarg)
Gst.Element.register(plugin, name, Gst.Rank.MARGINAL, pluginType)
return True

version = Gst.version()

Gst.Plugin.register_static_full(
version[0], # GST_VERSION_MAJOR
version[1], # GST_VERSION_MINOR
'synckeeper',
'sync keeper plugin',
plugin_init,
'12.06',
'LGPL',
'synckeeper',
'synckeeper',
'',
SyncKeeper,
)


class GenericDecoder(dc.DecoderComponent):
"""
Generic decoder component using decodebin2.
Generic decoder component using decodebin.
It listen to the custom gstreamer event flumotion-reset,
and reset the decoding pipeline by removing the old one
Expand Down Expand Up @@ -232,7 +251,7 @@ def configure_pipeline(self, pipeline, properties):
### Protected Methods ##

def _get_base_pipeline_string(self):
return 'decodebin2 name=decoder'
return 'decodebin name=decoder'

def _get_feeders_info(self):
"""
Expand Down Expand Up @@ -290,4 +309,4 @@ def _get_feeders_info(self):
FeederInfo('video', BASIC_VIDEO_CAPS))

def _get_base_pipeline_string(self):
return 'decodebin2 name=decoder synckeeper name=sync'
return 'decodebin name=decoder synckeeper name=sync'

0 comments on commit cf3cfbf

Please sign in to comment.