1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
|
{-# 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"
|