| /* GStreamer |
| * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> |
| * 2000 Wim Taymans <wtay@chello.be> |
| * |
| * audioclock.c: Clock for use by audio plugins |
| * |
| * 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., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| /** |
| * SECTION:gstaudioclock |
| * @title: GstAudioClock |
| * @short_description: Helper object for implementing audio clocks |
| * @see_also: #GstAudioBaseSink, #GstSystemClock |
| * |
| * #GstAudioClock makes it easy for elements to implement a #GstClock, they |
| * simply need to provide a function that returns the current clock time. |
| * |
| * This object is internally used to implement the clock in #GstAudioBaseSink. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include "gstaudioclock.h" |
| |
| GST_DEBUG_CATEGORY_STATIC (gst_audio_clock_debug); |
| #define GST_CAT_DEFAULT gst_audio_clock_debug |
| |
| static void gst_audio_clock_dispose (GObject * object); |
| |
| static GstClockTime gst_audio_clock_get_internal_time (GstClock * clock); |
| |
| #define parent_class gst_audio_clock_parent_class |
| G_DEFINE_TYPE (GstAudioClock, gst_audio_clock, GST_TYPE_SYSTEM_CLOCK); |
| |
| static void |
| gst_audio_clock_class_init (GstAudioClockClass * klass) |
| { |
| GstClockClass *gstclock_class; |
| GObjectClass *gobject_class; |
| |
| gobject_class = (GObjectClass *) klass; |
| gstclock_class = (GstClockClass *) klass; |
| |
| gobject_class->dispose = gst_audio_clock_dispose; |
| gstclock_class->get_internal_time = gst_audio_clock_get_internal_time; |
| |
| GST_DEBUG_CATEGORY_INIT (gst_audio_clock_debug, "audioclock", 0, |
| "audioclock"); |
| } |
| |
| static void |
| gst_audio_clock_init (GstAudioClock * clock) |
| { |
| GST_DEBUG_OBJECT (clock, "init"); |
| clock->last_time = 0; |
| clock->time_offset = 0; |
| GST_OBJECT_FLAG_SET (clock, GST_CLOCK_FLAG_CAN_SET_MASTER); |
| } |
| |
| static void |
| gst_audio_clock_dispose (GObject * object) |
| { |
| GstAudioClock *clock = GST_AUDIO_CLOCK (object); |
| |
| if (clock->destroy_notify && clock->user_data) |
| clock->destroy_notify (clock->user_data); |
| clock->destroy_notify = NULL; |
| clock->user_data = NULL; |
| |
| G_OBJECT_CLASS (parent_class)->dispose (object); |
| } |
| |
| /** |
| * gst_audio_clock_new: |
| * @name: the name of the clock |
| * @func: a function |
| * @user_data: user data |
| * @destroy_notify: #GDestroyNotify for @user_data |
| * |
| * Create a new #GstAudioClock instance. Whenever the clock time should be |
| * calculated it will call @func with @user_data. When @func returns |
| * #GST_CLOCK_TIME_NONE, the clock will return the last reported time. |
| * |
| * Returns: (transfer full): a new #GstAudioClock casted to a #GstClock. |
| */ |
| GstClock * |
| gst_audio_clock_new (const gchar * name, GstAudioClockGetTimeFunc func, |
| gpointer user_data, GDestroyNotify destroy_notify) |
| { |
| GstAudioClock *aclock = |
| GST_AUDIO_CLOCK (g_object_new (GST_TYPE_AUDIO_CLOCK, "name", name, |
| "clock-type", GST_CLOCK_TYPE_OTHER, NULL)); |
| |
| aclock->func = func; |
| aclock->user_data = user_data; |
| aclock->destroy_notify = destroy_notify; |
| |
| gst_object_ref_sink (aclock); |
| |
| return (GstClock *) aclock; |
| } |
| |
| /** |
| * gst_audio_clock_reset: |
| * @clock: a #GstAudioClock |
| * @time: a #GstClockTime |
| * |
| * Inform @clock that future calls to #GstAudioClockGetTimeFunc will return values |
| * starting from @time. The clock will update an internal offset to make sure that |
| * future calls to internal_time will return an increasing result as required by |
| * the #GstClock object. |
| */ |
| void |
| gst_audio_clock_reset (GstAudioClock * clock, GstClockTime time) |
| { |
| GstClockTimeDiff time_offset; |
| |
| if (clock->last_time >= time) |
| time_offset = clock->last_time - time; |
| else |
| time_offset = -(time - clock->last_time); |
| |
| clock->time_offset = time_offset; |
| |
| GST_DEBUG_OBJECT (clock, |
| "reset clock to %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT |
| ", offset %" GST_STIME_FORMAT, GST_TIME_ARGS (time), |
| GST_TIME_ARGS (clock->last_time), GST_STIME_ARGS (time_offset)); |
| } |
| |
| static GstClockTime |
| gst_audio_clock_func_invalid (GstClock * clock, gpointer user_data) |
| { |
| return GST_CLOCK_TIME_NONE; |
| } |
| |
| static GstClockTime |
| gst_audio_clock_get_internal_time (GstClock * clock) |
| { |
| GstAudioClock *aclock; |
| GstClockTime result; |
| |
| aclock = GST_AUDIO_CLOCK_CAST (clock); |
| |
| result = aclock->func (clock, aclock->user_data); |
| if (result == GST_CLOCK_TIME_NONE) { |
| result = aclock->last_time; |
| } else { |
| result += aclock->time_offset; |
| /* clock must be increasing */ |
| if (aclock->last_time < result) |
| aclock->last_time = result; |
| else |
| result = aclock->last_time; |
| } |
| |
| GST_DEBUG_OBJECT (clock, |
| "result %" GST_TIME_FORMAT ", last_time %" GST_TIME_FORMAT, |
| GST_TIME_ARGS (result), GST_TIME_ARGS (aclock->last_time)); |
| |
| return result; |
| } |
| |
| /** |
| * gst_audio_clock_get_time: |
| * @clock: a #GstAudioClock |
| * |
| * Report the time as returned by the #GstAudioClockGetTimeFunc without applying |
| * any offsets. |
| * |
| * Returns: the time as reported by the time function of the audio clock |
| */ |
| GstClockTime |
| gst_audio_clock_get_time (GstAudioClock * clock) |
| { |
| GstClockTime result; |
| |
| result = clock->func (GST_CLOCK_CAST (clock), clock->user_data); |
| if (result == GST_CLOCK_TIME_NONE) { |
| GST_DEBUG_OBJECT (clock, "no time, reuse last"); |
| result = clock->last_time - clock->time_offset; |
| } |
| |
| GST_DEBUG_OBJECT (clock, |
| "result %" GST_TIME_FORMAT ", last_time %" GST_TIME_FORMAT, |
| GST_TIME_ARGS (result), GST_TIME_ARGS (clock->last_time)); |
| |
| return result; |
| } |
| |
| /** |
| * gst_audio_clock_adjust: |
| * @clock: a #GstAudioClock |
| * @time: a #GstClockTime |
| * |
| * Adjust @time with the internal offset of the audio clock. |
| * |
| * Returns: @time adjusted with the internal offset. |
| */ |
| GstClockTime |
| gst_audio_clock_adjust (GstAudioClock * clock, GstClockTime time) |
| { |
| GstClockTime result; |
| |
| result = time + clock->time_offset; |
| |
| return result; |
| } |
| |
| /** |
| * gst_audio_clock_invalidate: |
| * @clock: a #GstAudioClock |
| * |
| * Invalidate the clock function. Call this function when the provided |
| * #GstAudioClockGetTimeFunc cannot be called anymore, for example, when the |
| * user_data becomes invalid. |
| * |
| * After calling this function, @clock will return the last returned time for |
| * the rest of its lifetime. |
| */ |
| void |
| gst_audio_clock_invalidate (GstAudioClock * clock) |
| { |
| clock->func = gst_audio_clock_func_invalid; |
| } |