Blob Blame History Raw
/*
 * Farstream - Farstream RTP specific types
 *
 * Copyright 2011 Collabora Ltd.
 *  @author: Olivier Crete <olivier.crete@collabora.co.uk>
 * Copyright 2011 Nokia Corp.
 *
 * fs-rtp.c - Farstream RTP specific types
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 */


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

#include "fs-rtp.h"

#include <string.h>

typedef GList FsRtpHeaderExtensionGList;

G_DEFINE_BOXED_TYPE (FsRtpHeaderExtension, fs_rtp_header_extension,
    fs_rtp_header_extension_copy, fs_rtp_header_extension_destroy)
G_DEFINE_BOXED_TYPE (FsRtpHeaderExtensionGList, fs_rtp_header_extension_list,
    fs_rtp_header_extension_list_copy, fs_rtp_header_extension_list_destroy)


/**
 * fs_rtp_header_extension_new:
 * @id: The identifier of the RTP header extension
 * @direction: the direction in which this extension can be used
 * @uri: The URI that defines this extension
 *
 * Creates a new #FsRtpHeaderExtension
 *
 * Returns: a new #FsRtpHeaderExtension
 */

FsRtpHeaderExtension *
fs_rtp_header_extension_new (guint id, FsStreamDirection direction,
    const gchar *uri)
{
  FsRtpHeaderExtension *extension;

  extension = g_slice_new (FsRtpHeaderExtension);

  extension->id = id;
  extension->direction = direction;
  extension->uri = g_strdup (uri);

  return extension;
}

/**
 * fs_rtp_header_extension_copy: (skip)
 * @extension: The RTP header extension definition to copy
 *
 * Copies a #FsRtpHeaderExtension
 *
 * Returns: a new #FsRtpHeaderExtension
 */

FsRtpHeaderExtension *
fs_rtp_header_extension_copy (FsRtpHeaderExtension *extension)
{
  if (extension)
    return fs_rtp_header_extension_new (extension->id, extension->direction,
        extension->uri);
  else
    return NULL;
}

/**
 * fs_rtp_header_extension_are_equal:
 * @extension1: The first #FsRtpHeaderExtension
 * @extension2: The second #FsRtpHeaderExtension
 *
 * Compares two #FsRtpHeaderExtension structures
 *
 * Returns: %TRUE if they are identical, %FALSE otherwise
 */

gboolean
fs_rtp_header_extension_are_equal (FsRtpHeaderExtension *extension1,
    FsRtpHeaderExtension *extension2)
{
  if (extension1 == extension2)
    return TRUE;

  if (!extension1 || !extension2)
    return FALSE;

  if (extension1->id == extension2->id &&
      extension1->direction == extension2->direction &&
      (extension1->uri == extension2->uri ||
          (extension1->uri && extension2->uri &&
              !strcmp (extension1->uri, extension2->uri))))
    return TRUE;
  else
    return FALSE;
}

/**
 * fs_rtp_header_extension_destroy: (skip)
 * @extension: A RTP header extension to free
 *
 * Frees the passed #FsRtpHeaderExtension
 */

void
fs_rtp_header_extension_destroy (FsRtpHeaderExtension *extension)
{
  if (extension)
  {
    g_free (extension->uri);
    g_slice_free (FsRtpHeaderExtension, extension);
  }
}

/**
 * fs_rtp_header_extension_list_copy:
 * @extensions:  (element-type FsRtpHeaderExtension) (transfer none):
 *   a #GList of #FsRtpHeaderExtension
 *
 * Does a deep copy of a #GList of #FsRtpHeaderExtension
 *
 * Returns: (element-type FsRtpHeaderExtension) (transfer full): a new
 * #GList of #FsRtpHeaderExtension
 */

GList *
fs_rtp_header_extension_list_copy (GList *extensions)
{
  GQueue copy = G_QUEUE_INIT;
  const GList *lp;

  for (lp = extensions; lp; lp = g_list_next (lp)) {
    FsRtpHeaderExtension *ext = lp->data;

    g_queue_push_tail (&copy, fs_rtp_header_extension_copy (ext));
  }

  return copy.head;
}

/**
 * fs_rtp_header_extension_list_destroy: (skip)
 * @extensions: a #GList of #FsRtpHeaderExtension
 *
 * Frees the passed #GList of #FsRtpHeaderExtension
 */

void
fs_rtp_header_extension_list_destroy (GList *extensions)
{
  g_list_foreach (extensions, (GFunc) fs_rtp_header_extension_destroy, NULL);
  g_list_free (extensions);
}

#define RTP_HDREXT_PREFIX "rtp-hdrext:"
#define RTP_HDREXT_AUDIO_PREFIX "audio:"
#define RTP_HDREXT_VIDEO_PREFIX "video:"
#define RTP_HDREXT_APPLICATION_PREFIX "application:"

/**
 * fs_rtp_header_extension_list_from_keyfile:
 * @filename: Name of the #GKeyFile to read the RTP Header Extensions from
 * @media_type: The media type for which to get header extensions
 * @error: location of a #GError, or NULL if no error occured
 *
 * Reads the content of a #GKeyFile of the following format into a
 * #GList of #FsRtpHeaderExtension structures.
 *
 * The groups have a format "rtp-hdrext:audio:XXX" or
 * "rtp-hdrext:video:XXX" where XXX is a unique string (per media type).
 *
 * The valid keys are:
 * <itemizedlist>
 *  <listitem>id: a int between in the 1-255 and 4096-4351 ranges</listitem>
 *  <listitem>uri: a URI describing the RTP Header Extension</listitem>
 *  <listitem>direction (optional): To only send or receive a RTP Header
 *      Extension, possible values are "send", "receive", "none" or "both".
 *      Defaults to "both"</listitem>
 * </itemizedlist>
 *
 * Example:
 * |[
 * [rtp-hdrext:audio:a]
 * id=1
 * uri=urn:ietf:params:rtp-hdrext:toffset
 *
 * [rtp-hdrext:audio:abc]
 * id=3
 * uri=urn:ietf:params:rtp-hdrext:ntp-64
 * direction=receive
 * ]|
 *
 * Returns: (element-type FsRtpHeaderExtension) (transfer full): a
 * #GList of #FsRtpHeaderExtension that must be freed with
 * fs_rtp_header_extension_list_destroy()
 */

GList *
fs_rtp_header_extension_list_from_keyfile (const gchar *filename,
    FsMediaType media_type,
    GError **error)
{
  GKeyFile *keyfile = NULL;
  GList *extensions = NULL;
  gchar **groups = NULL;
  gsize groups_count = 0;
  int i;

  g_return_val_if_fail (filename, NULL);
  g_return_val_if_fail (media_type <= FS_MEDIA_TYPE_LAST, NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  keyfile = g_key_file_new ();

  if (!g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, error))
    goto out;

  groups = g_key_file_get_groups (keyfile, &groups_count);

  if (!groups)
    goto out;

  for (i=0; i < groups_count && groups[i]; i++)
  {
    FsStreamDirection direction = FS_DIRECTION_BOTH;
    gint id;
    gchar *uri;
    GError *gerror = NULL;
    gchar *str;

    if (g_ascii_strncasecmp (RTP_HDREXT_PREFIX, groups[i],
            strlen (RTP_HDREXT_PREFIX)))
      continue;

    if (!g_ascii_strncasecmp (RTP_HDREXT_AUDIO_PREFIX,
            groups[i] + strlen (RTP_HDREXT_PREFIX),
            strlen (RTP_HDREXT_AUDIO_PREFIX)))
    {
      if (media_type != FS_MEDIA_TYPE_AUDIO)
        continue;
    }
    else if (!g_ascii_strncasecmp (RTP_HDREXT_VIDEO_PREFIX,
            groups[i] + strlen (RTP_HDREXT_PREFIX),
            strlen (RTP_HDREXT_VIDEO_PREFIX)))
    {
      if (media_type != FS_MEDIA_TYPE_VIDEO)
        continue;
    }
    else if (!g_ascii_strncasecmp (RTP_HDREXT_APPLICATION_PREFIX,
            groups[i] + strlen (RTP_HDREXT_PREFIX),
            strlen (RTP_HDREXT_APPLICATION_PREFIX)))
    {
      if (media_type != FS_MEDIA_TYPE_APPLICATION)
        continue;
    }
    else
    {
      continue;
    }

    id = g_key_file_get_integer (keyfile, groups[i], "id", &gerror);
    if (gerror)
    {
      g_clear_error (&gerror);
      continue;
    }

    str = g_key_file_get_string (keyfile, groups[i], "direction", &gerror);
    if (gerror)
    {
      GQuark domain = gerror->domain;
      gint code = gerror->code;

      g_clear_error (&gerror);
      if (domain != G_KEY_FILE_ERROR || code != G_KEY_FILE_ERROR_KEY_NOT_FOUND)
        continue;
    }
    else
    {
      if (!g_ascii_strcasecmp (str, "none"))
        direction = FS_DIRECTION_NONE;
      else if (!g_ascii_strcasecmp (str, "send"))
        direction = FS_DIRECTION_SEND;
      else if (!g_ascii_strcasecmp (str, "recv") ||
          !g_ascii_strcasecmp (str, "receive"))
        direction = FS_DIRECTION_RECV;
      g_free (str);
    }

    uri = g_key_file_get_string (keyfile, groups[i], "uri", &gerror);
    if (gerror)
    {
      g_clear_error (&gerror);
      continue;
    }

    extensions = g_list_append (extensions, fs_rtp_header_extension_new (id,
            direction, uri));
    g_free (uri);
  }

  out:

  g_strfreev (groups);
  g_key_file_free (keyfile);

  return extensions;
}