/* gstreamermm - a C++ wrapper for gstreamer
 *
 * Copyright 2008 The gstreamermm Development Team
 *
 * 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 2.1 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 library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <gst/base/gstbasesink.h>
#include <gstreamermm/element.h>
#include <gstreamermm/buffer.h>
#include <gstreamermm/pad.h>
#include <gstreamermm/caps.h>

_DEFS(gstreamermm,gst)

namespace Gst
{

class BufferList;

/** The base class for sink elements.
 * Gst::BaseSink is the base class for sink elements in GStreamer, such as
 * xvimagesink or filesink. It is a layer on top of Gst::Element that provides
 * a simplified interface to plugin writers. Gst::BaseSink handles many details
 * for you, for example: preroll, clock synchronization, state changes,
 * activation in push or pull mode, and queries.
 *
 * In most cases, when writing sink elements, there is no need to implement
 * class methods from Gst::Element or to set functions on pads, because the
 * Gst::BaseSink infrastructure should be sufficient.
 *
 * TODO: correct paragraph below for C++ and include example from C API:
 *
 * Gst::BaseSink provides support for exactly one sink pad, which should be
 * named "sink". A sink implementation (subclass of Gst::BaseSink) should
 * install a pad template in its base_init function, like so:
 *
 * Gst::BaseSink will handle the prerolling correctly. This means that it will
 * return Gst::STATE_CHANGE_ASYNC from a state change to PAUSED until the first
 * buffer arrives in this element. The base class will call the
 * Gst::BaseSink::preroll_vfunc() vmethod with this preroll buffer and will
 * then commit the state change to the next asynchronously pending state.
 *
 * When the element is set to PLAYING, Gst::BaseSink will synchronise on the
 * clock using the times returned from get_times_vfunc(). If this function
 * returns Gst::CLOCK_TIME_NONE for the start time, no synchronisation will be
 * done.  Synchronisation can be disabled entirely by setting the object "sync"
 * property to FALSE.
 *
 * After synchronisation the virtual method Gst::BaseSink::render_vfunc() will
 * be called. Subclasses should minimally implement this method.
 *
 * Since GStreamer 0.10.3 subclasses that synchronise on the clock in the
 * render_vfunc() vmethod are supported as well. These classes typically
 * receive a buffer in the render method and can then potentially block on the
 * clock while rendering. A typical example is an audiosink. Since GStreamer
 * 0.10.11 these subclasses can use wait_preroll() to perform the blocking
 * wait.
 *
 * Upon receiving the EOS event in the PLAYING state, Gst::BaseSink will wait
 * for the clock to reach the time indicated by the stop time of the last
 * get_times_vfunc() call before posting an EOS message. When the element
 * receives EOS in PAUSED, preroll completes, the event is queued and an EOS
 * message is posted when going to PLAYING.
 *
 * Gst::BaseSink will internally use the Gst::EVENT_NEWSEGMENT events to
 * schedule synchronisation and clipping of buffers. Buffers that fall
 * completely outside of the current segment are dropped. Buffers that fall
 * partially in the segment are rendered (and prerolled). Subclasses should do
 * any subbuffer clipping themselves when needed.
 *
 * Gst::BaseSink will by default report the current playback position in
 * Gst::FORMAT_TIME based on the current clock time and segment information. If
 * no clock has been set on the element, the query will be forwarded upstream.
 *
 * The set_caps_vfunc() function will be called when the subclass should
 * configure itself to process a specific media type.
 *
 * The start_vfunc() and stop_vfunc() virtual methods will be called when
 * resources should be allocated. Any preroll_vfunc(), render_vfunc() and
 * set_caps_vfunc() function will be called between the start_vfunc() and
 * stop_vfunc() calls.
 *
 * The event_vfunc() virtual method will be called when an event is received by
 * Gst::BaseSink. Normally this method should only be overriden by very
 * specific elements (such as file sinks) which need to handle the newsegment
 * event specially.
 * The unlock_vfunc() method is called when the elements should unblock any
 * blocking operations they perform in the render_vfunc() method. This is
 * mostly useful when the render_vfunc() method performs a blocking write on a
 * file descriptor, for example.
 *
 * The max-lateness property affects how the sink deals with buffers that
 * arrive too late in the sink. A buffer arrives too late in the sink when the
 * presentation time (as a combination of the last segment, buffer timestamp
 * and element base_time) plus the duration is before the current time of the
 * clock. If the frame is later than max-lateness, the sink will drop the
 * buffer without calling the render method. This feature is disabled if sync
 * is disabled, the get_times_vfunc() method does not return a valid start time
 * or max-lateness is set to -1 (the default). Subclasses can use
 * set_max_lateness() to configure the max-lateness value.
 *
 * The qos property will enable the quality-of-service features of the basesink
 * which gather statistics about the real-time performance of the clock
 * synchronisation. For each buffer received in the sink, statistics are
 * gathered and a QOS event is sent upstream with these numbers. This
 * information can then be used by upstream elements to reduce their processing
 * rate, for example.
 *
 * Since GStreamer 0.10.15 the async property can be used to instruct the sink
 * to never perform an ASYNC state change. This feature is mostly usable when
 * dealing with non-synchronized streams or sparse streams.
 *
 * Last reviewed on 2007-08-29 (0.10.15)
 *
 * @ingroup GstBaseClasses
 */
class BaseSink
: public Element
{
  _CLASS_GOBJECT(BaseSink, GstBaseSink, GST_BASE_SINK, Element, GstElement)

public:
  _WRAP_METHOD_DOCS_ONLY(gst_base_sink_query_latency)
  bool query_latency(bool& live, bool& upstream_live, ClockTime& min_latency,
    ClockTime& max_latency) const;
  _WRAP_METHOD(ClockTime get_latency() const, gst_base_sink_get_latency)
  _WRAP_METHOD(Gst::FlowReturn do_preroll(const Glib::RefPtr<Gst::MiniObject>& obj), gst_base_sink_do_preroll)
  _WRAP_METHOD(FlowReturn wait_preroll(), gst_base_sink_wait_preroll)
  _WRAP_METHOD(ClockReturn wait_clock(ClockTime time, ClockTimeDiff& jitter), gst_base_sink_wait_clock)
  _WRAP_METHOD(FlowReturn wait(ClockTime time, ClockTimeDiff& jitter), gst_base_sink_wait)

  /** This function will block until time is reached.  It is usually called by
   * subclasses that use their own internal synchronisation.
   *
   * If time is not valid, no sycnhronisation is done and Gst::CLOCK_BADTIME is
   * returned. Likewise, if synchronisation is disabled in the element or there
   * is no clock, no synchronisation is done and Gst::CLOCK_BADTIME is
   * returned.
   *
   * This function should only be called with the PREROLL_LOCK held, like when
   * receiving an EOS event in the event vmethod or when receiving a buffer in
   * the render vmethod.
   *
   * The time argument should be the running_time of when this method should
   * return and is not adjusted with any latency or offset configured in the
   * sink.
   *
   * Since 0.10.20.
   *
   * @param time The running_time to be reached.
   * @return Gst::ClockReturn.
   */
  ClockReturn wait_clock(ClockTime time);


  /** This function will block until time is reached.  It is usually called by
   * subclasses that use their own internal synchronisation but want to let the
   * EOS be handled by the base class.
   *
   * This function should only be called with the PREROLL_LOCK held, like when
   * receiving an EOS event in the event vmethod.
   *
   * The time argument should be the running_time of when the EOS should happen
   * and will be adjusted with any latency and offset configured in the sink.
   * 
   * Since 0.10.15.
   * 
   * @param time The running time to be reached.
   * @return Gst::FlowReturn.
   */
  FlowReturn wait(ClockTime time);

  _WRAP_METHOD(void set_sync(bool sync), gst_base_sink_set_sync)
  _WRAP_METHOD(bool get_sync() const, gst_base_sink_get_sync)
  _WRAP_METHOD(void set_max_lateness(gint64 max_lateness), gst_base_sink_set_max_lateness)
  _WRAP_METHOD(gint64 get_max_lateness() const, gst_base_sink_get_max_lateness)
  _WRAP_METHOD(void set_qos_enabled(bool enabled), gst_base_sink_set_qos_enabled)
  _WRAP_METHOD(bool is_qos_enabled() const, gst_base_sink_is_qos_enabled)
  _WRAP_METHOD(void set_async_enabled(bool enabled), gst_base_sink_set_async_enabled) 
  _WRAP_METHOD(bool is_async_enabled() const, gst_base_sink_is_async_enabled)
  _WRAP_METHOD(void set_ts_offset(ClockTimeDiff offset), gst_base_sink_set_ts_offset)
  _WRAP_METHOD(ClockTimeDiff get_ts_offset() const, gst_base_sink_get_ts_offset)
  _WRAP_METHOD(void set_render_delay(ClockTime delay), gst_base_sink_set_render_delay)
  _WRAP_METHOD(ClockTime get_render_delay() const, gst_base_sink_get_render_delay)
  _WRAP_METHOD(void set_blocksize(guint blocksize), gst_base_sink_set_blocksize)
  _WRAP_METHOD(guint get_blocksize() const, gst_base_sink_get_blocksize)

  _WRAP_METHOD(guint64 get_throttle_time() const, gst_base_sink_get_throttle_time)
  _WRAP_METHOD(void set_throttle_time(guint64 throttle), gst_base_sink_set_throttle_time)

  /** Gets the sink Gst::Pad object of the element.
   */
  _MEMBER_GET_GOBJECT(sink_pad, sinkpad, Gst::Pad, GstPad*)

  _WRAP_PROPERTY("async", bool)
  _WRAP_PROPERTY("max-lateness", gint64)
  _WRAP_PROPERTY("qos", bool)
  _WRAP_PROPERTY("sync", bool)
  _WRAP_PROPERTY("ts-offset", gint64)
  _WRAP_PROPERTY("render-delay", guint64)
  _WRAP_PROPERTY("throttle-time", guint64)
  _WRAP_PROPERTY("blocksize", guint)

#m4 _CONVERSION(`Glib::RefPtr<Gst::Caps>', `GstCaps*', `Glib::unwrap($3)')
  /** Called to get sink pad caps from the subclass.
   */
  _WRAP_VFUNC(Glib::RefPtr<Gst::Caps> get_caps(Glib::RefPtr<Gst::Caps> caps) const, "get_caps")

#m4 _CONVERSION(`GstCaps*', `const Glib::RefPtr<Gst::Caps>&', `Glib::wrap($3, true)')
  /** Notify subclass of changed caps.
   */
   virtual bool set_caps_vfunc(const Glib::RefPtr<Gst::Caps>& caps);

#m4 _CONVERSION(`GstBuffer*', `const Glib::RefPtr<Gst::Buffer>&', `Glib::wrap($3, true)')
#m4 _CONVERSION(`GstClockTime*', `ClockTime&', `*($3)')
  /** Called to get the start and end times for synchronising the passed buffer
   * to the clock.
   */
  _WRAP_VFUNC(void get_times(const Glib::RefPtr<Gst::Buffer>& buffer, ClockTime& start, ClockTime& end) const, "get_times")

  /** Start processing. Ideal for opening resources in the subclass.
   */
  virtual bool start_vfunc();

  /** Stop processing. Subclasses should use this to close resources.
   */
  virtual bool stop_vfunc();

  /** Unlock any pending access to the resource. Subclasses should unblock any
   * blocked function ASAP.
   */
  virtual bool unlock_vfunc();

#m4 _CONVERSION(`GstEvent*', `const Glib::RefPtr<Gst::Event>&', `Glib::wrap($3, true)')
  /** Override this to handle events arriving on the sink pad.
   */
  virtual bool event_vfunc(const Glib::RefPtr<Gst::Event>& event);

  /** Called to present the preroll buffer if desired.
   */
  _WRAP_VFUNC(FlowReturn preroll(const Glib::RefPtr<Gst::Buffer>& buffer), "preroll")

  /** Called when a buffer should be presented or output, at the correct moment
   * if the Gst::BaseSink has been set to sync to the clock.
   */
  _WRAP_VFUNC(FlowReturn render(const Glib::RefPtr<Gst::Buffer>& buffer), "render")

  /** Subclasses should override this when they need to perform special
   * processing when changing to the PLAYING state asynchronously. Called with
   * the OBJECT_LOCK held.
   */

  /** Subclasses should override this when they can provide an alternate method
   * of spawning a thread to drive the pipeline in pull mode. Should start or
   * stop the pulling thread, depending on the value of the "active" argument.
   * Called after actually activating the sink pad in pull mode. The default
   * implementation starts a task on the sink pad. 
   */
  virtual bool activate_pull_vfunc(bool active);

  /** Only useful in pull mode, this vmethod will be called in response to
   * Gst::Pad::fixate_caps() being called on the sink pad. Implement if you
   * have ideas about what should be the default values for the caps you
   * support.
   */
  _WRAP_VFUNC(Glib::RefPtr<Gst::Caps> fixate(const Glib::RefPtr<Gst::Caps>& caps), "fixate")

  /** Clear the previous unlock request. Subclasses should clear any state they
   * set during unlock_vfunc(), such as clearing command queues.
   */
  virtual bool unlock_stop_vfunc();

#m4 _CONVERSION(`GstBufferList*', `const Glib::RefPtr<Gst::BufferList>&', `Glib::wrap($3, true)')
  /// Render a BufferList.
  _WRAP_VFUNC(FlowReturn render_list(const Glib::RefPtr<Gst::BufferList>& buffer_list), render_list)

protected:
#m4begin
  _PUSH(SECTION_PCC_CLASS_INIT_VFUNCS)
  klass->set_caps = &set_caps_vfunc_callback;
  klass->start = &start_vfunc_callback;
  klass->stop = &stop_vfunc_callback;
  klass->unlock = &unlock_vfunc_callback;
  klass->event = &event_vfunc_callback;
  klass->activate_pull = &activate_pull_vfunc_callback;
  klass->unlock_stop = &unlock_stop_vfunc_callback;
  _SECTION(SECTION_PH_VFUNCS)
  static gboolean set_caps_vfunc_callback(GstBaseSink* self, GstCaps* caps);
  static gboolean start_vfunc_callback(GstBaseSink* self);
  static gboolean stop_vfunc_callback(GstBaseSink* self);
  static gboolean unlock_vfunc_callback(GstBaseSink* self);
  static gboolean event_vfunc_callback(GstBaseSink* self, GstEvent* event);
  static gboolean activate_pull_vfunc_callback(GstBaseSink* self, gboolean active);
  static gboolean unlock_stop_vfunc_callback(GstBaseSink* self);
  _POP()
#m4end
};

} // namespace Gst
