gstreamer-0.11.0: Media/Streaming/GStreamer/Core/Clock.chs
{-# LANGUAGE CPP #-}
-- GIMP Toolkit (GTK) Binding for Haskell: binding to gstreamer -*-haskell-*-
--
-- Author : Peter Gavin
-- Created: 1-Apr-2007
--
-- Copyright (c) 2007 Peter Gavin
--
-- This library is free software: you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public License
-- as published by the Free Software Foundation, either version 3 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
-- Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public
-- License along with this program. If not, see
-- <http://www.gnu.org/licenses/>.
--
-- GStreamer, the C library which this Haskell library depends on, is
-- available under LGPL Version 2. The documentation included with
-- this library is based on the original GStreamer documentation.
--
-- |
-- Maintainer : gtk2hs-devel@lists.sourceforge.net
-- Stability : alpha
-- Portability : portable (depends on GHC)
--
-- Abstract class of global clocks.
module Media.Streaming.GStreamer.Core.Clock (
-- * Detail
-- | GStreamer uses a global clock to synchronize the plugins in a
-- pipeline. Different clock implementations are possible by
-- implementing this abstract base class.
--
-- The 'Clock' returns a monotonically increasing time with the
-- method 'clockGetTime'. Its accuracy and base time depend
-- on the specific clock implementation but time is always
-- expressed in nanoseconds. Since the baseline of the clock is
-- undefined, the clock time returned is not meaningful in itself,
-- what matters are the deltas between two clock times. The time
-- returned by a clock is called the absolute time.
--
-- The pipeline uses the clock to calculate the stream
-- time. Usually all renderers synchronize to the global clock
-- using the buffer timestamps, the newsegment events and the
-- element's base time, see GstPipeline.
--
-- A clock implementation can support periodic and single shot
-- clock notifications both synchronous and asynchronous.
--
-- One first needs to create a 'ClockID' for the periodic or
-- single shot notification using 'clockNewSingleShotID' or
-- 'clockNewPeriodicID'.
--
-- To perform a blocking wait for the specific time of the
-- 'ClockID' use 'clockIDWait'. This calls can be interrupted with
-- the 'clockIDUnschedule' call. If the blocking wait is
-- unscheduled a return value of 'ClockUnscheduled' is returned.
--
-- Periodic callbacks scheduled async will be repeadedly called
-- automatically until it is unscheduled. To schedule a sync
-- periodic callback, 'clockIDWait' should be called repeatedly.
--
-- The async callbacks can happen from any thread, either provided
-- by the core or from a streaming thread. The application should
-- be prepared for this.
--
-- A 'ClockID' that has been unscheduled cannot be used again for
-- any wait operation; a new 'ClockID' should be created.
--
-- It is possible to perform a blocking wait on the same 'ClockID'
-- from multiple threads. However, registering the same 'ClockID'
-- for multiple async notifications is not possible, the callback
-- will only be called for the thread registering the entry last.
--
-- These clock operations do not operate on the stream time, so
-- the callbacks will also occur when not in the playing state as
-- if the clock just keeps on running. Some clocks however do not
-- progress when the element that provided the clock is not
-- playing.
--
-- When a clock has the 'ClockFlagCanSetMaster' flag set, it can
-- be slaved to another 'Clock' with 'clockSetMaster'. The clock
-- will then automatically be synchronized to this master clock by
-- repeatedly sampling the master clock and the slave clock and
-- recalibrating the slave clock with 'clockSetCalibration'. This
-- feature is mostly useful for plugins that have an internal
-- clock but must operate with another clock selected by the
-- GstPipeline. They can track the offset and rate difference of
-- their internal clock relative to the master clock by using the
-- 'clockGetCalibration' function.
--
-- The master\/slave synchronisation can be tuned with the
-- the 'clockTimeout', 'clockWindowSize' and 'clockWindowThreshold' properties.
-- The 'clockTimeout' property defines the interval to
-- sample the master clock and run the calibration
-- functions. 'clockWindowSize' defines the number of samples to
-- use when calibrating and 'clockWindowThreshold' defines the
-- minimum number of samples before the calibration is performed.
-- * Types
Clock,
ClockClass,
castToClock,
gTypeClock,
-- | A time value measured in nanoseconds.
ClockTime,
-- | The 'ClockTime' value representing an invalid time.
clockTimeNone,
clockTimeIsValid,
-- | The 'ClockTime' value representing 1 second, i.e. 1e9.
second,
-- | The 'ClockTime' value representing 1 millisecond, i.e. 1e6.
msecond,
-- | The 'ClockTime' value representing 1 microsecond, i.e. 1e3.
usecond,
-- | The 'ClockTime' value representing 1 nanosecond, i.e. 1.
nsecond,
-- | A value holding the difference between two 'ClockTime's.
ClockTimeDiff,
-- | An opaque identifier for a timer event.
ClockID,
-- | An enumeration type returned by 'clockIDWait'.
ClockReturn(..),
-- | The flags a 'Clock' may have.
ClockFlags(..),
clockGetFlags,
clockSetFlags,
clockUnsetFlags,
-- * Clock Operations
clockAddObservation,
clockSetMaster,
clockGetMaster,
clockSetResolution,
clockGetResolution,
clockGetTime,
clockNewSingleShotID,
clockNewPeriodicID,
clockGetInternalTime,
clockGetCalibration,
clockSetCalibration,
clockIDGetTime,
clockIDWait,
clockIDUnschedule,
-- * Clock Properties
clockTimeout,
clockWindowSize,
clockWindowThreshold
) where
import Data.Ratio ( Ratio
, (%)
, numerator
, denominator )
import Control.Monad ( liftM
, liftM4)
{#import Media.Streaming.GStreamer.Core.Types#}
import System.Glib.FFI
import System.Glib.Attributes ( Attr
, newAttr )
import System.Glib.Properties
{# context lib = "gstreamer" prefix = "gst" #}
-- | Get the flags set on the clock.
clockGetFlags :: ClockClass clockT
=> clockT -- ^ @clock@
-> IO [ClockFlags] -- ^ the flags currently set on the clock
clockGetFlags = mkObjectGetFlags
-- | Set the given flags on the clock.
clockSetFlags :: ClockClass clockT
=> clockT -- ^ @clock@
-> [ClockFlags] -- ^ @flags@ - the flags to be set
-> IO ()
clockSetFlags = mkObjectSetFlags
-- | Unset the given flags on the clock.
clockUnsetFlags :: ClockClass clockT
=> clockT -- ^ @clock@
-> [ClockFlags] -- ^ @flags@ - the flags to be unset
-> IO ()
clockUnsetFlags = mkObjectUnsetFlags
-- | Returns 'True' if the given 'ClockTime' is valid, and 'False'
-- otherwise.
clockTimeIsValid :: ClockTime -- ^ @clockTime@
-> Bool -- ^ 'True' if @clockTime@ is valid, 'False' otherwise
clockTimeIsValid = (/= clockTimeNone)
-- | The time master of the master clock and the time slave of the
-- slave clock are added to the list of observations. If enough
-- observations are available, a linear regression algorithm is run
-- on the observations and clock is recalibrated.
--
-- If a calibration is performed, the correlation coefficient of the
-- interpolation will be returned. A value of 1.0 means the clocks
-- are in perfect sync. This value can be used to control the
-- sampling frequency of the master and slave clocks.
clockAddObservation :: ClockClass clock
=> clock
-> ClockTime
-> ClockTime
-> IO (Maybe Double)
clockAddObservation clock slave master =
alloca $ \rSquaredPtr ->
do success <- {# call clock_add_observation #} (toClock clock)
(fromIntegral slave)
(fromIntegral master)
rSquaredPtr
if toBool success
then liftM (Just . realToFrac) $ peek rSquaredPtr
else return Nothing
-- | Set @master@ as the master clock for @clock@. The @clock@ will
-- automatically be calibrated so that 'clockGetTime' reports the
-- same time as the @master@ clock.
--
-- A clock provider that slaves its clock to a master can get the
-- current calibration values with 'clockGetCalibration'.
--
-- The @master@ clock can be 'Nothing' in which case @clock@ will
-- not be slaved any longer. It will, however, continue to report
-- its time adjusted using the last configured rate and time
-- offsets.
--
-- Note that if @clock@ does not have the 'ClockFlagCanSetMaster'
-- flag set, this function will not succeed and return 'False'.
clockSetMaster :: (ClockClass clock, ClockClass master)
=> clock -- ^ @clock@
-> Maybe master -- ^ @master@
-> IO Bool -- ^ 'True' if @clock@ is capable of
-- being slaved to the @master@ clock, otherwise 'False'
clockSetMaster clock master =
withObject (toClock clock) $ \clockPtr ->
maybeWith withObject (liftM toClock $ master) $ \masterPtr ->
liftM toBool $ gst_clock_set_master clockPtr masterPtr
where
_ = {# call clock_set_master #}
-- | Return the master that @clock@ is slaved to, or 'Nothing' if
-- @clock@ is not slaved.
clockGetMaster :: ClockClass clock
=> clock -- ^ @clock@
-> IO (Maybe Clock) -- ^ the master that @clock@ is slaved to, or 'Nothing'
clockGetMaster clock =
{# call clock_get_master #} (toClock clock) >>= maybePeek takeObject
-- | Set the resolution of @clock@. Some clocks have the possibility
-- to operate with different resolution at the expense of more
-- resource usage. There is normally no need to change the default
-- resolution of a clock. The resolution of a clock can only be
-- changed if the clock has the 'ClockFlagCanSetResolution' flag
-- set.
clockSetResolution :: ClockClass clock
=> clock
-> ClockTime
-> IO ClockTime
clockSetResolution clock resolution =
liftM fromIntegral $
{# call clock_set_resolution #} (toClock clock)
(fromIntegral resolution)
-- | Get the resolution of the @clock@. The resolution of the clock is
-- the granularity of the values returned by 'clockGetTime'.
clockGetResolution :: ClockClass clock
=> clock -- ^ @clock@ -
-> IO ClockTime -- ^ the resolution currently set in @clock@
clockGetResolution clock =
liftM fromIntegral $
{# call clock_get_resolution #} (toClock clock)
-- | Get the current time stored in @clock@. The time is always
-- monotonically increasing and adjusted according to the current
-- offset and rate.
clockGetTime :: ClockClass clock
=> clock -- ^ @clock@
-> IO ClockTime -- ^ the current time in @clock@
clockGetTime clock =
liftM fromIntegral $
{# call clock_get_time #} (toClock clock)
-- | Get a 'ClockID' from @clock@ to trigger a single shot
-- notification at the requested time.
clockNewSingleShotID :: ClockClass clock
=> clock -- ^ @clock@
-> ClockTime -- ^ @clockTime@
-> IO ClockID -- ^ a single shot notification id triggered at @clockTime@
clockNewSingleShotID clock time =
{# call clock_new_single_shot_id #} (toClock clock)
(fromIntegral time) >>=
takeClockID . castPtr
-- | Get a 'ClockID' from @clock@ to trigger periodic
-- notifications. The notifications will start at time @startTime@
-- and then be fired at each @interval@ after.
clockNewPeriodicID :: ClockClass clock
=> clock -- ^ @clock@
-> ClockTime -- ^ @startTime@
-> ClockTime -- ^ @interval@
-> IO ClockID -- ^ a periodic notification id
clockNewPeriodicID clock startTime interval =
{# call clock_new_periodic_id #} (toClock clock)
(fromIntegral startTime)
(fromIntegral interval) >>=
takeClockID . castPtr
-- | Gets the current internal time of @clock@. The time is
-- returned unadjusted in the offset and rate.
clockGetInternalTime :: ClockClass clock
=> clock -- ^ @clock@
-> IO ClockTime -- ^ the clock's internal time value
clockGetInternalTime clock =
liftM fromIntegral $
{# call clock_get_internal_time #} (toClock clock)
-- | Gets the internal rate and reference time of @clock@. See
-- 'clockSetCalibration' for more information.
clockGetCalibration :: ClockClass clock
=> clock -- ^ @clock@
-> IO (ClockTime, ClockTime, Ratio ClockTime)
-- ^ the clock's internal time, external (adjusted) time, and skew rate
clockGetCalibration clock =
alloca $ \internalPtr ->
alloca $ \externalPtr ->
alloca $ \rateNumPtr ->
alloca $ \rateDenomPtr ->
do {# call clock_get_calibration #} (toClock clock)
internalPtr
externalPtr
rateNumPtr
rateDenomPtr
liftM4 (\a b c d ->
(fromIntegral a,
fromIntegral b,
fromIntegral c % fromIntegral d))
(peek internalPtr)
(peek externalPtr)
(peek rateNumPtr)
(peek rateDenomPtr)
-- | Adjusts the rate and time of clock. A rate of @1 % 1@ is the
-- normal speed of the clock. Larger values make the clock go
-- faster.
--
-- The parameters @internal@ and @external@ specifying that
-- 'clockGetTime' should have returned @external@ when the clock had
-- internal time @internal@. The parameter @internal@ should not be
-- in the future; that is, it should be less than the value returned
-- by 'clockGetInternalTime' when this function is called.
--
-- Subsequent calls to 'clockGetTime' will return clock times
-- computed as follows:
--
-- > (clock_internal - internal) * rate + external
--
-- Note that 'clockGetTime' always returns increasing values, so if
-- the clock is moved backwards, 'clockGetTime' will report the
-- previous value until the clock catches up.
clockSetCalibration :: ClockClass clock
=> clock -- ^ @clock@
-> ClockTime -- ^ @internal@
-> ClockTime -- ^ @external@
-> Ratio ClockTime -- ^ @rate@
-> IO ()
clockSetCalibration clock internal external rate =
{# call clock_set_calibration #} (toClock clock)
(fromIntegral internal)
(fromIntegral external)
(fromIntegral $ numerator rate)
(fromIntegral $ denominator rate)
-- | Get the time of @clockID@.
clockIDGetTime :: ClockID -- ^ @clockID@
-> IO ClockTime
clockIDGetTime clockID =
liftM fromIntegral $ withClockID clockID $
{# call clock_id_get_time #} . castPtr
-- | Perform a blocking wait on @clockID@. The parameter @clockID@
-- should have been created with 'clockNewSingleShotID' or
-- 'clockNewPeriodicID', and should not been unscheduled with a call
-- to 'clockIDUnschedule'.
--
-- If second value in the returned pair is not 'Nothing', it will
-- contain the difference against the clock and the time of
-- @clockID@ when this method was called. Positive values indicate
-- how late @clockID@ was relative to the clock. Negative values
-- indicate how much time was spend waiting on the clock before the
-- function returned.
clockIDWait :: ClockID -- ^ @clockID@
-> IO (ClockReturn, Maybe ClockTimeDiff)
clockIDWait clockID =
alloca $ \jitterPtr ->
do result <- liftM cToEnum $ withClockID clockID $ \clockIDPtr ->
{# call clock_id_wait #} (castPtr clockIDPtr) jitterPtr
jitter <- let peekJitter = liftM (Just . fromIntegral) $ peek jitterPtr
in case result of
ClockOk -> peekJitter
ClockEarly -> peekJitter
_ -> return Nothing
return (result, jitter)
-- | Cancel an outstanding request with @clockID@. After this call,
-- @clockID@ cannot be used anymore to recieve notifications; you
-- must create a new 'ClockID'.
clockIDUnschedule :: ClockID -- ^ @clockID@
-> IO ()
clockIDUnschedule clockID =
withClockID clockID $ {# call clock_id_unschedule #} . castPtr
-- | The amount of time, in nanoseconds, between samples.
clockTimeout :: ClockClass clockT
=> Attr clockT ClockTime
clockTimeout = newAttr
(objectGetPropertyUInt64 "timeout")
(objectSetPropertyUInt64 "timeout")
-- | The size of the window used to calculate rate and offset.
clockWindowSize :: ClockClass clockT
=> Attr clockT Int
clockWindowSize =
newAttrFromIntProperty "window-size"
-- | The threshold to start calculating rate and offset.
clockWindowThreshold :: ClockClass clockT
=> Attr clockT Int
clockWindowThreshold =
newAttrFromIntProperty "window-threshold"