[gst-devel] Plugin help/samba streaming

Gert van Valkenhoef gertvv at hccnet.nl
Sat Jan 10 15:15:09 CET 2004


Hello gstreamer developers,

I have written a gstreamer source element for gstreamer 0.6.x, that can
stream files from a samba share. I used gstreamer 0.6.x because it needs
to run on a debian box we have in the living room :)

I have a problem with it: if I play back an MP3 file
gst-launch --gst-plugin-spew --gst-plugin-path=. smbsrc
  location=smb://GERT/music/metal/Marilyn\ Manson/Cleansing.mp3 ! spider !
  osssink
for example, everything is fine.

With ogg files however, the ogg filter complains:
ERROR: /pipeline0/spider0/vorbisfile0: this is not a vorbis file
execution ended after 1 iterations (sum 50130000 ns, average 50130000 ns,
min 50130000 ns, max 50130000 ns)

I can't really figure out what I did wrong (especially since I've never
written anything for gstreamer or samba before). Perhaps you can help me
out with this? I'm probably making a lot of mistakes...

I do think a samba streaming element is useful in general so if there is
interest I could work on porting to 0.7.x :)

It's only dependency except for standard gstreamer dependencies is
libsmbclient (included in samba, 2.2.x and 3.0.x both work). I did a lot
of copy/paste/adjust from the fdsrc and filesrc elements to make this.

The code listings are below.

Thank you,

Gert van Valkenhoef


=========== .h ===========
#ifndef __GST_SMBSRC_H__
#define __GST_SMBSRC_H__


/*#include <config.h>*/
#include <gst/gst.h>

G_BEGIN_DECLS

GstElementDetails gst_smbsrc_details;

typedef enum {
  GST_SMBSRC_OPEN              = GST_ELEMENT_FLAG_LAST,

  GST_SMBSRC_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 2
} GstSmbSrcFlags;

#define GST_TYPE_SMBSRC \
  (gst_smbsrc_get_type())
#define GST_SMBSRC(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SMBSRC,GstSmbSrc))
#define GST_SMBSRC_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SMBSRC,GstSmbSrcClass))
#define GST_IS_SMBSRC(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SMBSRC))
#define GST_IS_SMBSRC_CLASS(obj) \
  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SMBSRC))


typedef struct _GstSmbSrc GstSmbSrc;
typedef struct _GstSmbSrcClass GstSmbSrcClass;

struct _GstSmbSrc {
  GstElement element;
  /* pads */
  GstPad *srcpad;


  /* location */
  gchar *location;
  /* fd */
  gint fd;

  gulong curoffset;			/* current offset in file */
  gulong blocksize;		/* bytes per read */

  gulong seq;				/* buffer sequence number */
};

struct _GstSmbSrcClass {
  GstElementClass parent_class;
};

GType gst_smbsrc_get_type(void);

G_END_DECLS

#endif


================== .c ==========
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>

#include <libsmbclient.h>
#include "gstsmbsrc.h"

#define DEFAULT_BLOCKSIZE	4096

#define VERSION "0.0"

GstElementDetails gst_smbsrc_details =
{
  "Samba Source",
  "Source/File",
  "LGPL",
  "Synchronous read from a samba source",
  VERSION,
  "Gert van Valkenhoef <gertvv at hccnet.nl>",
  "(C) 2003",
};


/* SmbSrc signals and args */
enum {
  /* FILL ME */
  LAST_SIGNAL
};

enum {
  ARG_0,
  ARG_FD,
  ARG_LOCATION,
  ARG_BLOCKSIZE,
};


static void		gst_smbsrc_class_init	(GstSmbSrcClass *klass);
static void		gst_smbsrc_init		(GstSmbSrc *smbsrc);

static void		gst_smbsrc_set_property	(GObject *object, guint prop_id,
						 const GValue *value, GParamSpec *pspec);
static void		gst_smbsrc_get_property	(GObject *object, guint prop_id,
						 GValue *value, GParamSpec *pspec);

static GstBuffer *	gst_smbsrc_get		(GstPad *pad);

static void 		gst_smbsrc_dispose 	(GObject *object);

static gboolean		gst_smbsrc_open_file	(GstSmbSrc *src);

static GstElementStateReturn gst_smbsrc_change_state (GstElement *element);

static GstElementClass *parent_class = NULL;
/*static guint gst_fdsrc_signals[LAST_SIGNAL] = { 0 };*/

/* Samba Authentication Function !FIXME! */
void auth_fn(const char *server, const char *share,
	     char *workgroup, int wgmaxlen, char *username, int unmaxlen,
	     char *password, int pwmaxlen)
{
   strncpy (workgroup, "HARDWIRE", wgmaxlen-1);
   strncpy (username, "guest", unmaxlen-1);
   strncpy (password, "", pwmaxlen-1);
}

GType
gst_smbsrc_get_type (void)
{
  static GType smbsrc_type = 0;

  if (!smbsrc_type) {
    static const GTypeInfo smbsrc_info = {
      sizeof(GstSmbSrcClass),
      NULL,
      NULL,
      (GClassInitFunc)gst_smbsrc_class_init,
      NULL,
      NULL,
      sizeof(GstSmbSrc),
      0,
      (GInstanceInitFunc)gst_smbsrc_init,
    };
    smbsrc_type = g_type_register_static (GST_TYPE_ELEMENT, "smbsrc", &smbsrc_info, 0);
  }
  return smbsrc_type;
}

static void
gst_smbsrc_dispose (GObject *object)
{
  GstSmbSrc *src;

  src = GST_SMBSRC (object);

  G_OBJECT_CLASS (parent_class)->dispose (object);

  if (src->location)
    g_free (src->location);
}


static void
gst_smbsrc_class_init (GstSmbSrcClass *klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

  /* init sambaclient - FIXME: handle errors gracefully */
  if(smbc_init ( auth_fn, 10 ))
    exit (1);

  gobject_class = (GObjectClass*)klass;
  gstelement_class = (GstElementClass*)klass;

  parent_class = g_type_class_ref(GST_TYPE_ELEMENT);


  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION,
    g_param_spec_int ("fd", "fd", "A SMB file descriptor",
                      0, G_MAXINT, 0, G_PARAM_READABLE));
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION,
    g_param_spec_string ("location", "location", "A URL smb://host/share/path",
                      NULL, G_PARAM_READWRITE));
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BLOCKSIZE,
    g_param_spec_ulong ("blocksize", "Block size", "Size in bytes to read per buffer",
                        1, G_MAXULONG, DEFAULT_BLOCKSIZE, G_PARAM_READWRITE));

  gobject_class->dispose = gst_smbsrc_dispose;
  gobject_class->set_property = gst_smbsrc_set_property;
  gobject_class->get_property = gst_smbsrc_get_property;
  gstelement_class->change_state = gst_smbsrc_change_state;
}

static void gst_smbsrc_init(GstSmbSrc *smbsrc) {
  smbsrc->srcpad = gst_pad_new ("src", GST_PAD_SRC);

  gst_pad_set_get_function (smbsrc->srcpad, gst_smbsrc_get);
  gst_element_add_pad (GST_ELEMENT (smbsrc), smbsrc->srcpad);

  smbsrc->location = NULL;
  smbsrc->fd = 0;
  smbsrc->curoffset = 0;
  smbsrc->blocksize = DEFAULT_BLOCKSIZE;
  smbsrc->seq = 0;
}


static void
gst_smbsrc_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
  GstSmbSrc *src;

  g_return_if_fail (object);
  /* it's not null if we got it, but it might not be ours */
  g_return_if_fail (GST_IS_SMBSRC (object));

  src = GST_SMBSRC (object);

  switch (prop_id) {
    case ARG_LOCATION:
      /* the element must be stopped in order to do this */
      g_return_if_fail (GST_STATE (src) < GST_STATE_PLAYING);

      if (src->location) g_free (src->location);
      /* clear the filename if we get a NULL (is that possible?) */
      if (g_value_get_string (value) == NULL) {
        gst_element_set_state (GST_ELEMENT (object), GST_STATE_NULL);
        src->location = NULL;
      /* otherwise set the new filename */
      } else {
        src->location = g_strdup (g_value_get_string (value));
      }
      g_object_notify (G_OBJECT (src), "location");
      break;
    case ARG_BLOCKSIZE:
      src->blocksize = g_value_get_ulong (value);
      break;
    default:
      break;
  }
}

static void
gst_smbsrc_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
  GstSmbSrc *src;

  /* it's not null if we got it, but it might not be ours */
  g_return_if_fail (GST_IS_FDSRC (object));

  src = GST_SMBSRC (object);

  switch (prop_id) {
    case ARG_LOCATION:
      g_value_set_string (value, src->location);
      break;
    case ARG_BLOCKSIZE:
      g_value_set_ulong (value, src->blocksize);
      break;
    case ARG_FD:
      g_value_set_int (value, src->fd);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static gboolean
gst_smbsrc_open_file (GstSmbSrc *src)
{
  g_return_val_if_fail (!GST_FLAG_IS_SET (src ,GST_SMBSRC_OPEN), FALSE);

  GST_DEBUG(0, "opening file %s",src->filename);

  /* open the file */
  src->fd = smbc_open (src->location, O_RDONLY, 0);
  if (src->fd < 0) {
    gst_element_error (GST_ELEMENT (src), "opening file \"%s\" (%s)",
                       src->location, strerror (errno), NULL);
    return FALSE;
  } else {
    src->curoffset = 0;
    GST_FLAG_SET (src, GST_SMBSRC_OPEN);
  }
  return TRUE;
}

static void
gst_smbsrc_close_file (GstSmbSrc *src)
{
  g_return_if_fail (GST_FLAG_IS_SET (src, GST_SMBSRC_OPEN));

  /* close the file */
  smbc_close (src->fd);

  /* zero out a lot of our state */
  src->fd = 0;
  src->curoffset = 0;

  GST_FLAG_UNSET (src, GST_SMBSRC_OPEN);
}

static GstElementStateReturn
gst_smbsrc_change_state (GstElement *element)
{
  GstSmbSrc *src = GST_SMBSRC(element);

  switch (GST_STATE_TRANSITION (element)) {
    case GST_STATE_NULL_TO_READY:
      break;
    case GST_STATE_READY_TO_NULL:
      break;
    case GST_STATE_READY_TO_PAUSED:
      if (!GST_FLAG_IS_SET (element, GST_SMBSRC_OPEN)) {
        if (!gst_smbsrc_open_file (GST_SMBSRC (element)))
          return GST_STATE_FAILURE;
      }
      break;
    case GST_STATE_PAUSED_TO_READY:
      if (GST_FLAG_IS_SET (element, GST_SMBSRC_OPEN))
        gst_smbsrc_close_file (GST_SMBSRC (element));
      /*src->seek_happened = TRUE;*/
      break;
    default:
      break;
  }

  if (GST_ELEMENT_CLASS (parent_class)->change_state)
    return GST_ELEMENT_CLASS (parent_class)->change_state (element);

  return GST_STATE_SUCCESS;
}


static GstBuffer *
gst_smbsrc_get(GstPad *pad)
{
  GstSmbSrc *src;
  GstBuffer *buf;
  glong readbytes;

  printf("smbsrc_get\n");

  src = GST_SMBSRC (gst_pad_get_parent (pad));

  /* create the buffer */
  buf = gst_buffer_new_and_alloc (src->blocksize);

  /* read it in from the file */
  readbytes = smbc_read (src->fd, GST_BUFFER_DATA (buf), src->blocksize);

  /* if nothing was read, we're in eos */
  if (readbytes == 0) {
    gst_element_set_eos (GST_ELEMENT (src));
    return GST_BUFFER (gst_event_new (GST_EVENT_EOS));
  }

  if (readbytes == -1) {
	  g_error ("Error reading from file descriptor. Ending stream.\n");
	  gst_element_set_eos (GST_ELEMENT (src));
	  return GST_BUFFER (gst_event_new (GST_EVENT_EOS));
	  }

  GST_BUFFER_OFFSET (buf) = src->curoffset;
  GST_BUFFER_SIZE (buf) = readbytes;
  GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE;
  src->curoffset += readbytes;

  /* we're done, return the buffer */
  return buf;
}

static gboolean
plugin_init (GModule *module, GstPlugin *plugin)
{
   GstElementFactory *factory;
   gst_plugin_set_longname (plugin, "Samba GST Element");
   factory = gst_element_factory_new ("smbsrc", (gst_smbsrc_get_type)(), &gst_smbsrc_details);
   if (!factory)
   {
     g_warning ("gst_element_factory_new failed for `smbsrc'");
     return TRUE;
   }
   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE(factory));
   return TRUE;
}
GstPluginDesc plugin_desc = {
  GST_VERSION_MAJOR,
  GST_VERSION_MINOR,
  "smbsrc",
  plugin_init
};





More information about the gstreamer-devel mailing list