gstfilesink.c 14.3 KB
Newer Older
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/* GStreamer
 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
 *                    2000 Wim Taymans <wtay@chello.be>
 *
 * gstfilesink.c: 
 *
 * 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */


24 25 26 27
#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

28 29
#include "../gst-i18n-lib.h"

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
30 31 32 33
#include <gst/gst.h>
#include <errno.h>
#include "gstfilesink.h"
#include <string.h>
34 35
#include <sys/stat.h>
#include <sys/types.h>
36
#ifdef HAVE_UNISTD_H
37
#include <unistd.h>
38
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
39

40

41 42 43 44 45
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS_ANY);

46
GST_DEBUG_CATEGORY_STATIC (gst_filesink_debug);
47 48
#define GST_CAT_DEFAULT gst_filesink_debug

49 50 51 52
GstElementDetails gst_filesink_details = GST_ELEMENT_DETAILS ("File Sink",
    "Sink/File",
    "Write stream to a file",
    "Thomas <thomas@apestaart.org>");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
53 54 55


/* FileSink signals and args */
56 57
enum
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
58 59 60 61 62
  /* FILL ME */
  SIGNAL_HANDOFF,
  LAST_SIGNAL
};

63 64
enum
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
65
  ARG_0,
66
  ARG_LOCATION
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
67 68
};

69
static const GstFormat *
70
gst_filesink_get_formats (GstPad * pad)
71 72 73 74 75
{
  static const GstFormat formats[] = {
    GST_FORMAT_BYTES,
    0,
  };
76

77 78 79 80
  return formats;
}

static const GstQueryType *
81
gst_filesink_get_query_types (GstPad * pad)
82 83 84 85 86 87
{
  static const GstQueryType types[] = {
    GST_QUERY_TOTAL,
    GST_QUERY_POSITION,
    0
  };
88

89 90
  return types;
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
91

92 93 94 95 96 97
static void gst_filesink_dispose (GObject * object);

static void gst_filesink_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_filesink_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
98

99 100
static gboolean gst_filesink_open_file (GstFileSink * sink);
static void gst_filesink_close_file (GstFileSink * sink);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
101

102 103 104 105
static gboolean gst_filesink_handle_event (GstPad * pad, GstEvent * event);
static gboolean gst_filesink_pad_query (GstPad * pad, GstQueryType type,
    GstFormat * format, gint64 * value);
static void gst_filesink_chain (GstPad * pad, GstData * _data);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
106

107 108
static void gst_filesink_uri_handler_init (gpointer g_iface,
    gpointer iface_data);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
109

110
static GstElementStateReturn gst_filesink_change_state (GstElement * element);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
111 112 113

static guint gst_filesink_signals[LAST_SIGNAL] = { 0 };

114 115
static void
_do_init (GType filesink_type)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
116
{
117 118 119 120 121
  static const GInterfaceInfo urihandler_info = {
    gst_filesink_uri_handler_init,
    NULL,
    NULL
  };
122

123 124 125 126
  g_type_add_interface_static (filesink_type, GST_TYPE_URI_HANDLER,
      &urihandler_info);
  GST_DEBUG_CATEGORY_INIT (gst_filesink_debug, "filesink", 0,
      "filesink element");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
127 128
}

129 130 131
GST_BOILERPLATE_FULL (GstFileSink, gst_filesink, GstElement, GST_TYPE_ELEMENT,
    _do_init);

132

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
133
static void
134
gst_filesink_base_init (gpointer g_class)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
135
{
136
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
137

138
  gstelement_class->change_state = gst_filesink_change_state;
139 140
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&sinktemplate));
141 142 143
  gst_element_class_set_details (gstelement_class, &gst_filesink_details);
}
static void
144
gst_filesink_class_init (GstFileSinkClass * klass)
145 146
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
147 148


149
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION,
150
      g_param_spec_string ("location", "File Location",
151
          "Location of the file to write", NULL, G_PARAM_READWRITE));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
152 153

  gst_filesink_signals[SIGNAL_HANDOFF] =
154 155 156
      g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstFileSinkClass, handoff), NULL, NULL,
      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
157 158 159

  gobject_class->set_property = gst_filesink_set_property;
  gobject_class->get_property = gst_filesink_get_property;
160
  gobject_class->dispose = gst_filesink_dispose;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
161
}
162 163
static void
gst_filesink_init (GstFileSink * filesink)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
164 165 166
{
  GstPad *pad;

167 168 169
  pad =
      gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate),
      "sink");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
170 171 172
  gst_element_add_pad (GST_ELEMENT (filesink), pad);
  gst_pad_set_chain_function (pad, gst_filesink_chain);

173
  GST_FLAG_SET (GST_ELEMENT (filesink), GST_ELEMENT_EVENT_AWARE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
174

175 176 177 178
  gst_pad_set_query_function (pad, gst_filesink_pad_query);
  gst_pad_set_query_type_function (pad, gst_filesink_get_query_types);
  gst_pad_set_formats_function (pad, gst_filesink_get_formats);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
179 180 181
  filesink->filename = NULL;
  filesink->file = NULL;
}
Benjamin Otte's avatar
Benjamin Otte committed
182
static void
183
gst_filesink_dispose (GObject * object)
Benjamin Otte's avatar
Benjamin Otte committed
184 185
{
  GstFileSink *sink = GST_FILESINK (object);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
186

Benjamin Otte's avatar
Benjamin Otte committed
187
  G_OBJECT_CLASS (parent_class)->dispose (object);
188

Benjamin Otte's avatar
Benjamin Otte committed
189 190 191 192 193
  g_free (sink->uri);
  sink->uri = NULL;
  g_free (sink->filename);
  sink->filename = NULL;
}
194

Benjamin Otte's avatar
Benjamin Otte committed
195
static gboolean
196
gst_filesink_set_location (GstFileSink * sink, const gchar * location)
Benjamin Otte's avatar
Benjamin Otte committed
197 198 199 200 201 202 203 204 205 206
{
  /* the element must be stopped or paused in order to do this */
  if (GST_STATE (sink) > GST_STATE_PAUSED)
    return FALSE;
  if (GST_STATE (sink) == GST_STATE_PAUSED &&
      GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN))
    return FALSE;

  g_free (sink->filename);
  g_free (sink->uri);
207 208 209 210 211 212 213
  if (location != NULL) {
    sink->filename = g_strdup (location);
    sink->uri = gst_uri_construct ("file", location);
  } else {
    sink->filename = NULL;
    sink->uri = NULL;
  }
214

Benjamin Otte's avatar
Benjamin Otte committed
215 216 217 218 219
  if (GST_STATE (sink) == GST_STATE_PAUSED)
    gst_filesink_open_file (sink);

  return TRUE;
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
220
static void
221 222
gst_filesink_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
223 224 225 226 227 228 229 230
{
  GstFileSink *sink;

  /* it's not null if we got it, but it might not be ours */
  sink = GST_FILESINK (object);

  switch (prop_id) {
    case ARG_LOCATION:
Benjamin Otte's avatar
Benjamin Otte committed
231
      gst_filesink_set_location (sink, g_value_get_string (value));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
232 233
      break;
    default:
234
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
235 236 237 238
      break;
  }
}

239 240 241
static void
gst_filesink_get_property (GObject * object, guint prop_id, GValue * value,
    GParamSpec * pspec)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
242 243
{
  GstFileSink *sink;
244

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
245 246
  /* it's not null if we got it, but it might not be ours */
  g_return_if_fail (GST_IS_FILESINK (object));
247

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
248
  sink = GST_FILESINK (object);
249

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
250 251
  switch (prop_id) {
    case ARG_LOCATION:
252
      g_value_set_string (value, sink->filename);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
253 254 255 256 257 258 259 260
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static gboolean
261
gst_filesink_open_file (GstFileSink * sink)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
262 263 264 265
{
  g_return_val_if_fail (!GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN), FALSE);

  /* open the file */
266
  if (sink->filename == NULL || sink->filename[0] == '\0') {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
267
    GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
268
        (_("No file name specified for writing.")), (NULL));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
269 270
    return FALSE;
  }
271

272
  sink->file = fopen (sink->filename, "wb");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
273
  if (sink->file == NULL) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
274
    GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
275 276
        (_("Could not open file \"%s\" for writing."), sink->filename),
        GST_ERROR_SYSTEM);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
277
    return FALSE;
278
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
279 280 281 282 283 284 285 286 287

  GST_FLAG_SET (sink, GST_FILESINK_OPEN);

  sink->data_written = 0;

  return TRUE;
}

static void
288
gst_filesink_close_file (GstFileSink * sink)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
289 290 291
{
  g_return_if_fail (GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN));

292
  if (fclose (sink->file) != 0) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
293
    GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
294
        (_("Error closing file \"%s\"."), sink->filename), GST_ERROR_SYSTEM);
295
  } else {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
296 297 298 299
    GST_FLAG_UNSET (sink, GST_FILESINK_OPEN);
  }
}

300
static gboolean
301 302
gst_filesink_pad_query (GstPad * pad, GstQueryType type,
    GstFormat * format, gint64 * value)
303 304 305 306 307 308
{
  GstFileSink *sink = GST_FILESINK (GST_PAD_PARENT (pad));

  switch (type) {
    case GST_QUERY_TOTAL:
      switch (*format) {
309 310 311 312 313 314 315 316
        case GST_FORMAT_BYTES:
          if (GST_FLAG_IS_SET (GST_ELEMENT (sink), GST_FILESINK_OPEN)) {
            *value = sink->data_written;        /* FIXME - doesn't the kernel provide
                                                   such a function? */
            break;
          }
        default:
          return FALSE;
317 318 319 320
      }
      break;
    case GST_QUERY_POSITION:
      switch (*format) {
321 322 323 324 325 326 327
        case GST_FORMAT_BYTES:
          if (GST_FLAG_IS_SET (GST_ELEMENT (sink), GST_FILESINK_OPEN)) {
            *value = ftell (sink->file);
            break;
          }
        default:
          return FALSE;
328 329 330 331 332 333 334 335 336
      }
      break;
    default:
      return FALSE;
  }

  return TRUE;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
337 338
/* handle events (search) */
static gboolean
339
gst_filesink_handle_event (GstPad * pad, GstEvent * event)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
340 341 342 343 344 345
{
  GstEventType type;
  GstFileSink *filesink;

  filesink = GST_FILESINK (gst_pad_get_parent (pad));

346 347
  if (!(GST_FLAG_IS_SET (filesink, GST_FILESINK_OPEN))) {
    gst_event_unref (event);
348
    return FALSE;
349 350
  }

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
351 352 353 354
  type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;

  switch (type) {
    case GST_EVENT_SEEK:
355 356 357 358
      if (GST_EVENT_SEEK_FORMAT (event) != GST_FORMAT_BYTES) {
        gst_event_unref (event);
        return FALSE;
      }
359

360 361 362
      if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) {
        if (fflush (filesink->file)) {
          gst_event_unref (event);
363 364 365
          GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
              (_("Error while writing to file \"%s\"."), filesink->filename),
              GST_ERROR_SYSTEM);
366
          return FALSE;
367 368
        }
      }
369 370

      switch (GST_EVENT_SEEK_METHOD (event)) {
371 372 373 374 375 376 377 378 379 380 381 382
        case GST_SEEK_METHOD_SET:
          fseek (filesink->file, GST_EVENT_SEEK_OFFSET (event), SEEK_SET);
          break;
        case GST_SEEK_METHOD_CUR:
          fseek (filesink->file, GST_EVENT_SEEK_OFFSET (event), SEEK_CUR);
          break;
        case GST_SEEK_METHOD_END:
          fseek (filesink->file, GST_EVENT_SEEK_OFFSET (event), SEEK_END);
          break;
        default:
          g_warning ("unknown seek method!");
          break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
383
      }
384
      gst_event_unref (event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
385
      break;
386 387 388
    case GST_EVENT_DISCONTINUOUS:
    {
      gint64 offset;
389

390
      if (gst_event_discont_get_value (event, GST_FORMAT_BYTES, &offset))
391
        fseek (filesink->file, offset, SEEK_SET);
392

393
      gst_event_unref (event);
394 395
      break;
    }
396 397
    case GST_EVENT_FLUSH:
      if (fflush (filesink->file)) {
398
        gst_event_unref (event);
399 400 401
        GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
            (_("Error while writing to file \"%s\"."), filesink->filename),
            GST_ERROR_SYSTEM);
402
      }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
403
      break;
404
    case GST_EVENT_EOS:
405
      gst_event_unref (event);
406 407
      gst_filesink_close_file (filesink);
      gst_element_set_eos (GST_ELEMENT (filesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
      break;
    default:
      gst_pad_event_default (pad, event);
      break;
  }

  return TRUE;
}

/**
 * gst_filesink_chain:
 * @pad: the pad this filesink is connected to
 * @buf: the buffer that has to be absorbed
 *
 * take the buffer from the pad and write to file if it's open
 */
424 425
static void
gst_filesink_chain (GstPad * pad, GstData * _data)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
426
{
427
  GstBuffer *buf = GST_BUFFER (_data);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
428 429 430 431 432 433 434 435
  GstFileSink *filesink;

  g_return_if_fail (pad != NULL);
  g_return_if_fail (GST_IS_PAD (pad));
  g_return_if_fail (buf != NULL);

  filesink = GST_FILESINK (gst_pad_get_parent (pad));

436 437
  if (GST_IS_EVENT (buf)) {
    gst_filesink_handle_event (pad, GST_EVENT (buf));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
438 439 440
    return;
  }

441
  if (GST_FLAG_IS_SET (filesink, GST_FILESINK_OPEN)) {
442
    guint bytes_written = 0, back_pending = 0;
443 444 445

    if (ftell (filesink->file) < filesink->data_written)
      back_pending = filesink->data_written - ftell (filesink->file);
446
    while (bytes_written < GST_BUFFER_SIZE (buf)) {
447
      size_t wrote = fwrite (GST_BUFFER_DATA (buf) + bytes_written, 1,
448 449
          GST_BUFFER_SIZE (buf) - bytes_written,
          filesink->file);
450

451
      if (wrote <= 0) {
452 453 454 455 456
        GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
            (_("Error while writing to file \"%s\"."), filesink->filename),
            ("Only %d of %d bytes written: %s",
                bytes_written, GST_BUFFER_SIZE (buf), strerror (errno)));
        break;
457 458
      }
      bytes_written += wrote;
459
    }
460

461
    filesink->data_written += bytes_written - back_pending;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
462 463 464 465
  }

  gst_buffer_unref (buf);

466
  g_signal_emit (G_OBJECT (filesink),
467
      gst_filesink_signals[SIGNAL_HANDOFF], 0, filesink);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
468 469 470
}

static GstElementStateReturn
471
gst_filesink_change_state (GstElement * element)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
472 473 474
{
  g_return_val_if_fail (GST_IS_FILESINK (element), GST_STATE_FAILURE);

475 476 477
  switch (GST_STATE_TRANSITION (element)) {
    case GST_STATE_PAUSED_TO_READY:
      if (GST_FLAG_IS_SET (element, GST_FILESINK_OPEN))
478
        gst_filesink_close_file (GST_FILESINK (element));
479 480 481 482
      break;

    case GST_STATE_READY_TO_PAUSED:
      if (!GST_FLAG_IS_SET (element, GST_FILESINK_OPEN)) {
483 484
        if (!gst_filesink_open_file (GST_FILESINK (element)))
          return GST_STATE_FAILURE;
485 486
      }
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
487 488 489 490 491 492 493
  }

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

  return GST_STATE_SUCCESS;
}
Benjamin Otte's avatar
Benjamin Otte committed
494 495 496 497 498 499 500 501 502

/*** GSTURIHANDLER INTERFACE *************************************************/

static guint
gst_filesink_uri_get_type (void)
{
  return GST_URI_SINK;
}
static gchar **
503
gst_filesink_uri_get_protocols (void)
Benjamin Otte's avatar
Benjamin Otte committed
504
{
505
  static gchar *protocols[] = { "file", NULL };
506

Benjamin Otte's avatar
Benjamin Otte committed
507 508 509
  return protocols;
}
static const gchar *
510
gst_filesink_uri_get_uri (GstURIHandler * handler)
Benjamin Otte's avatar
Benjamin Otte committed
511 512
{
  GstFileSink *sink = GST_FILESINK (handler);
513

Benjamin Otte's avatar
Benjamin Otte committed
514 515
  return sink->uri;
}
516

Benjamin Otte's avatar
Benjamin Otte committed
517
static gboolean
518
gst_filesink_uri_set_uri (GstURIHandler * handler, const gchar * uri)
Benjamin Otte's avatar
Benjamin Otte committed
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
{
  gchar *protocol, *location;
  gboolean ret;
  GstFileSink *sink = GST_FILESINK (handler);

  protocol = gst_uri_get_protocol (uri);
  if (strcmp (protocol, "file") != 0) {
    g_free (protocol);
    return FALSE;
  }
  g_free (protocol);
  location = gst_uri_get_location (uri);
  ret = gst_filesink_set_location (sink, location);
  g_free (location);

  return ret;
}

static void
gst_filesink_uri_handler_init (gpointer g_iface, gpointer iface_data)
{
  GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;

  iface->get_type = gst_filesink_uri_get_type;
  iface->get_protocols = gst_filesink_uri_get_protocols;
  iface->get_uri = gst_filesink_uri_get_uri;
  iface->set_uri = gst_filesink_uri_set_uri;
}