<commit_before>/*
 * Clutter-GStreamer.
 *
 * GStreamer integration library for Clutter.
 *
 * clutter-gst-player.c - Wrap some convenience functions around playbin2
 *
 * Authored By <NAME>    <<EMAIL>>
 *             <NAME> <<EMAIL>>
 *
 * Copyright (C) 2011 Intel Corporation
 *
 * 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 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/**
 * SECTION:clutter-gst-player
 * @short_description: An interface for controlling playback of media data
 *
 * #ClutterGstPlayer is an interface for controlling playback of media
 *  sources. Contrary to most interfaces, you don't need to implement
 *  #ClutterGstPlayer. It already provides an implementation/logic
 *  leaving you only tweak a few properties to get the desired behavior.
 *
 * #ClutterGstPlayer extends and implements #ClutterMedia to create
 * enhanced player.
 *
 * #ClutterMedia is available since Clutter 0.2
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>

#include <gst/video/video.h>
#include <gst/tag/tag.h>
#include <gst/interfaces/streamvolume.h>

#include "clutter-gst-debug.h"
#include "clutter-gst-enum-types.h"
#include "clutter-gst-marshal.h"
#include "clutter-gst-player.h"
#include "clutter-gst-private.h"

typedef ClutterGstPlayerIface       ClutterGstPlayerInterface;

G_DEFINE_INTERFACE_WITH_CODE (ClutterGstPlayer, clutter_gst_player, G_TYPE_OBJECT,
                              g_type_interface_add_prerequisite (g_define_type_id,
                                                                 CLUTTER_TYPE_MEDIA))

#define PLAYER_GET_PRIVATE(player)                              \
  (g_object_get_qdata (G_OBJECT (player),                       \
                       clutter_gst_player_private_quark))
#define PLAYER_SET_PRIVATE(player,private)                      \
  (g_object_set_qdata (G_OBJECT (player),                       \
                       clutter_gst_player_private_quark,        \
                       private))

#define PLAYER_GET_CLASS_PRIVATE(player)                     \
  (g_type_get_qdata (G_OBJECT_TYPE (player),                 \
                     clutter_gst_player_class_quark))

/* idle timeouts (in ms) */
#define TICK_TIMEOUT        500
#define BUFFERING_TIMEOUT   250

enum
{
  DOWNLOAD_BUFFERING,

  LAST_SIGNAL
};

enum
{
  PROP_0,

  /* ClutterMedia properties */
  PROP_URI,
  PROP_PLAYING,
  PROP_PROGRESS,
  PROP_SUBTITLE_URI,
  PROP_SUBTITLE_FONT_NAME,
  PROP_AUDIO_VOLUME,
  PROP_CAN_SEEK,
  PROP_BUFFER_FILL,
  PROP_DURATION,

  /* ClutterGstPlayer properties */
  PROP_IDLE,
  PROP_USER_AGENT,
  PROP_SEEK_FLAGS,
  PROP_AUDIO_STREAMS,
  PROP_AUDIO_STREAM,
  PROP_SUBTITLE_TRACKS,
  PROP_SUBTITLE_TRACK
};

struct _ClutterGstPlayerIfacePrivate
{
  void       (*set_property)		(GObject        *object,
                                         guint           property_id,
                                         const GValue   *value,
                                         GParamSpec     *pspec);
  void       (*get_property)		(GObject        *object,
                                         guint           property_id,
                                         GValue         *value,
                                         GParamSpec     *pspec);
};

typedef struct _ClutterGstPlayerPrivate ClutterGstPlayerPrivate;

/* Elements don't expose header files */
typedef enum {
  GST_PLAY_FLAG_VIDEO         = (1 << 0),
  GST_PLAY_FLAG_AUDIO         = (1 << 1),
  GST_PLAY_FLAG_TEXT          = (1 << 2),
  GST_PLAY_FLAG_VIS           = (1 << 3),
  GST_PLAY_FLAG_SOFT_VOLUME   = (1 << 4),
  GST_PLAY_FLAG_NATIVE_AUDIO  = (1 << 5),
  GST_PLAY_FLAG_NATIVE_VIDEO  = (1 << 6),
  GST_PLAY_FLAG_DOWNLOAD      = (1 << 7),
  GST_PLAY_FLAG_BUFFERING     = (1 << 8),
  GST_PLAY_FLAG_DEINTERLACE   = (1 << 9)
} GstPlayFlags;

struct _ClutterGstPlayerPrivate
{
  GObject parent;

  GstElement *pipeline;
  GstBus *bus;

  gchar *uri;

  guint is_idle : 1;
  guint can_seek : 1;
  guint in_seek : 1;
  guint is_changing_uri : 1;
  guint in_error : 1;
  guint in_eos : 1;
  guint in_download_buffering : 1;
  /* when in progressive download, we use the buffer-fill property to signal
   * that we have enough data to play the stream. This flag allows to send
   * the notify that buffer-fill is 1.0 only once */
  guint virtual_stream_buffer_signalled : 1;

  gdouble stacked_progress;

  gdouble target_progress;
  GstState target_state;

  guint tick_timeout_id;
  guint buffering_timeout_id;

  /* This is a cubic volume, suitable for use in a UI cf. StreamVolume doc */
  gdouble volume;

  gdouble buffer_fill;
  gdouble duration;
  gchar *font_name;
  gchar *user_agent;

  GstSeekFlags seek_flags;    /* flags for the seek in set_progress(); */

  GstElement *download_buffering_element;

  GList *audio_streams;
  GList *subtitle_tracks;
};

static GQuark clutter_gst_player_private_quark = 0;
static GQuark clutter_gst_player_class_quark = 0;

static guint signals[LAST_SIGNAL] = { 0, };

static gboolean player_buffering_timeout (gpointer data);

/* Logic */

#ifdef CLUTTER_GST_ENABLE_DEBUG
static gchar *
get_stream_description (GstTagList *tags,
                        gint        track_num)
{
  gchar *description = NULL;

  if (tags)
    {

      gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &description);

      if (description)
        {
          const gchar *language = gst_tag_get_language_name (description);

          if (language)
            {
              g_free (description);
              description = g_strdup (language);
            }
        }

      if (!description)
        gst_tag_list_get_string (tags, GST_TAG_CODEC, &description);
    }

  if (!description)
    description = g_strdup_printf ("Track %d", track_num);

  return description;
}

gchar *
list_to_string (GList *list)
{
  GstTagList *tags;
  gchar *description;
  GString *string;
  GList *l;
  gint n, i;

  if (!list)
    return g_strdup ("<empty list>");

  string = g_string_new (NULL);
  n = g_list_length (list);
  for (i = 0, l = list; i < n - 1; i++, l = g_list_next (l))
    {
      tags = l->data;
      description = get_stream_description (tags, i);
      g_string_append_printf (string, "%s, ", description);
      g_free (description);
    }

  tags = l->data;
  description = get_stream_description (tags, i);
  g_string_append_printf (string, "%s", (gchar *) description);
  g_free (description);

  return g_string_free (string, FALSE);
}
#endif

static const gchar *
gst_state_to_string (GstState state)
{
  switch (state)
    {
    case GST_STATE_VOID_PENDING:
      return "pending";
    case GST_STATE_NULL:
      return "null";
    case GST_STATE_READY:
      return "ready";
    case GST_STATE_PAUSED:
      return "paused";
    case GST_STATE_PLAYING:
      return "playing";
    }

  return "Unknown state";
}

static void
free_tags_list (GList **listp)
{
  GList *l;

  l = *listp;
  while (l)
    {
      gst_tag_list_free (l->data);
      l = g_list_delete_link (l, l);
    }

  *listp = NULL;
}<commit_msg>player: Don't try to free NULL GstTagList<commit_after>/*
 * Clutter-GStreamer.
 *
 * GStreamer integration library for Clutter.
 *
 * clutter-gst-player.c - Wrap some convenience functions around playbin2
 *
 * Authored By <NAME>    <<EMAIL>>
 *             <NAME> <<EMAIL>>
 *
 * Copyright (C) 2011 Intel Corporation
 *
 * 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 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/**
 * SECTION:clutter-gst-player
 * @short_description: An interface for controlling playback of media data
 *
 * #ClutterGstPlayer is an interface for controlling playback of media
 *  sources. Contrary to most interfaces, you don't need to implement
 *  #ClutterGstPlayer. It already provides an implementation/logic
 *  leaving you only tweak a few properties to get the desired behavior.
 *
 * #ClutterGstPlayer extends and implements #ClutterMedia to create
 * enhanced player.
 *
 * #ClutterMedia is available since Clutter 0.2
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>

#include <gst/video/video.h>
#include <gst/tag/tag.h>
#include <gst/interfaces/streamvolume.h>

#include "clutter-gst-debug.h"
#include "clutter-gst-enum-types.h"
#include "clutter-gst-marshal.h"
#include "clutter-gst-player.h"
#include "clutter-gst-private.h"

typedef ClutterGstPlayerIface       ClutterGstPlayerInterface;

G_DEFINE_INTERFACE_WITH_CODE (ClutterGstPlayer, clutter_gst_player, G_TYPE_OBJECT,
                              g_type_interface_add_prerequisite (g_define_type_id,
                                                                 CLUTTER_TYPE_MEDIA))

#define PLAYER_GET_PRIVATE(player)                              \
  (g_object_get_qdata (G_OBJECT (player),                       \
                       clutter_gst_player_private_quark))
#define PLAYER_SET_PRIVATE(player,private)                      \
  (g_object_set_qdata (G_OBJECT (player),                       \
                       clutter_gst_player_private_quark,        \
                       private))

#define PLAYER_GET_CLASS_PRIVATE(player)                     \
  (g_type_get_qdata (G_OBJECT_TYPE (player),                 \
                     clutter_gst_player_class_quark))

/* idle timeouts (in ms) */
#define TICK_TIMEOUT        500
#define BUFFERING_TIMEOUT   250

enum
{
  DOWNLOAD_BUFFERING,

  LAST_SIGNAL
};

enum
{
  PROP_0,

  /* ClutterMedia properties */
  PROP_URI,
  PROP_PLAYING,
  PROP_PROGRESS,
  PROP_SUBTITLE_URI,
  PROP_SUBTITLE_FONT_NAME,
  PROP_AUDIO_VOLUME,
  PROP_CAN_SEEK,
  PROP_BUFFER_FILL,
  PROP_DURATION,

  /* ClutterGstPlayer properties */
  PROP_IDLE,
  PROP_USER_AGENT,
  PROP_SEEK_FLAGS,
  PROP_AUDIO_STREAMS,
  PROP_AUDIO_STREAM,
  PROP_SUBTITLE_TRACKS,
  PROP_SUBTITLE_TRACK
};

struct _ClutterGstPlayerIfacePrivate
{
  void       (*set_property)		(GObject        *object,
                                         guint           property_id,
                                         const GValue   *value,
                                         GParamSpec     *pspec);
  void       (*get_property)		(GObject        *object,
                                         guint           property_id,
                                         GValue         *value,
                                         GParamSpec     *pspec);
};

typedef struct _ClutterGstPlayerPrivate ClutterGstPlayerPrivate;

/* Elements don't expose header files */
typedef enum {
  GST_PLAY_FLAG_VIDEO         = (1 << 0),
  GST_PLAY_FLAG_AUDIO         = (1 << 1),
  GST_PLAY_FLAG_TEXT          = (1 << 2),
  GST_PLAY_FLAG_VIS           = (1 << 3),
  GST_PLAY_FLAG_SOFT_VOLUME   = (1 << 4),
  GST_PLAY_FLAG_NATIVE_AUDIO  = (1 << 5),
  GST_PLAY_FLAG_NATIVE_VIDEO  = (1 << 6),
  GST_PLAY_FLAG_DOWNLOAD      = (1 << 7),
  GST_PLAY_FLAG_BUFFERING     = (1 << 8),
  GST_PLAY_FLAG_DEINTERLACE   = (1 << 9)
} GstPlayFlags;

struct _ClutterGstPlayerPrivate
{
  GObject parent;

  GstElement *pipeline;
  GstBus *bus;

  gchar *uri;

  guint is_idle : 1;
  guint can_seek : 1;
  guint in_seek : 1;
  guint is_changing_uri : 1;
  guint in_error : 1;
  guint in_eos : 1;
  guint in_download_buffering : 1;
  /* when in progressive download, we use the buffer-fill property to signal
   * that we have enough data to play the stream. This flag allows to send
   * the notify that buffer-fill is 1.0 only once */
  guint virtual_stream_buffer_signalled : 1;

  gdouble stacked_progress;

  gdouble target_progress;
  GstState target_state;

  guint tick_timeout_id;
  guint buffering_timeout_id;

  /* This is a cubic volume, suitable for use in a UI cf. StreamVolume doc */
  gdouble volume;

  gdouble buffer_fill;
  gdouble duration;
  gchar *font_name;
  gchar *user_agent;

  GstSeekFlags seek_flags;    /* flags for the seek in set_progress(); */

  GstElement *download_buffering_element;

  GList *audio_streams;
  GList *subtitle_tracks;
};

static GQuark clutter_gst_player_private_quark = 0;
static GQuark clutter_gst_player_class_quark = 0;

static guint signals[LAST_SIGNAL] = { 0, };

static gboolean player_buffering_timeout (gpointer data);

/* Logic */

#ifdef CLUTTER_GST_ENABLE_DEBUG
static gchar *
get_stream_description (GstTagList *tags,
                        gint        track_num)
{
  gchar *description = NULL;

  if (tags)
    {

      gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &description);

      if (description)
        {
          const gchar *language = gst_tag_get_language_name (description);

          if (language)
            {
              g_free (description);
              description = g_strdup (language);
            }
        }

      if (!description)
        gst_tag_list_get_string (tags, GST_TAG_CODEC, &description);
    }

  if (!description)
    description = g_strdup_printf ("Track %d", track_num);

  return description;
}

gchar *
list_to_string (GList *list)
{
  GstTagList *tags;
  gchar *description;
  GString *string;
  GList *l;
  gint n, i;

  if (!list)
    return g_strdup ("<empty list>");

  string = g_string_new (NULL);
  n = g_list_length (list);
  for (i = 0, l = list; i < n - 1; i++, l = g_list_next (l))
    {
      tags = l->data;
      description = get_stream_description (tags, i);
      g_string_append_printf (string, "%s, ", description);
      g_free (description);
    }

  tags = l->data;
  description = get_stream_description (tags, i);
  g_string_append_printf (string, "%s", (gchar *) description);
  g_free (description);

  return g_string_free (string, FALSE);
}
#endif

static const gchar *
gst_state_to_string (GstState state)
{
  switch (state)
    {
    case GST_STATE_VOID_PENDING:
      return "pending";
    case GST_STATE_NULL:
      return "null";
    case GST_STATE_READY:
      return "ready";
    case GST_STATE_PAUSED:
      return "paused";
    case GST_STATE_PLAYING:
      return "playing";
    }

  return "Unknown state";
}

static void
free_tags_list (GList **listp)
{
  GList *l;

  l = *listp;
  while (l)
    {
      if (l->data)
        gst_tag_list_free (l->data);
      l = g_list_delete_link (l, l);
    }

  *listp = NULL;
}
{'fraction_non_alphanumeric': 0.08598664159263342, 'fraction_numerical': 0.0083003696258349, 'mean_word_length': 2.5600184672206834, 'pattern_counts': {'":': 0, '<': 67, '<?xml version=': 0, '>': 32, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 34, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3929627', 'n_tokens_mistral': 5239, 'n_tokens_neox': 4956, 'n_words': 1316}