gstfilesink.c 11 KB
Newer Older
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1 2 3 4
/* GStreamer
 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
 *                    2000 Wim Taymans <wtay@chello.be>
 *
5
 * gstfilesink.c:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * 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 47
GST_DEBUG_CATEGORY_STATIC (gst_file_sink_debug);
#define GST_CAT_DEFAULT gst_file_sink_debug
48

49
GstElementDetails gst_file_sink_details = GST_ELEMENT_DETAILS ("File Sink",
50 51 52
    "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 void gst_file_sink_dispose (GObject * object);
70

71
static void gst_file_sink_set_property (GObject * object, guint prop_id,
72
    const GValue * value, GParamSpec * pspec);
73
static void gst_file_sink_get_property (GObject * object, guint prop_id,
74
    GValue * value, GParamSpec * pspec);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
75

76 77
static gboolean gst_file_sink_open_file (GstFileSink * sink);
static void gst_file_sink_close_file (GstFileSink * sink);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
78

79 80
static gboolean gst_file_sink_event (GstBaseSink * sink, GstEvent * event);
static GstFlowReturn gst_file_sink_render (GstBaseSink * sink,
81 82
    GstBuffer * buffer);

83
static gboolean gst_file_sink_query (GstPad * pad, GstQuery * query);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
84

85
static void gst_file_sink_uri_handler_init (gpointer g_iface,
86
    gpointer iface_data);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
87

88
static GstElementStateReturn gst_file_sink_change_state (GstElement * element);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
89

90
//static guint gst_file_sink_signals[LAST_SIGNAL] = { 0 };
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
91

92 93
static void
_do_init (GType filesink_type)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
94
{
95
  static const GInterfaceInfo urihandler_info = {
96
    gst_file_sink_uri_handler_init,
97 98 99
    NULL,
    NULL
  };
100

101 102
  g_type_add_interface_static (filesink_type, GST_TYPE_URI_HANDLER,
      &urihandler_info);
103
  GST_DEBUG_CATEGORY_INIT (gst_file_sink_debug, "filesink", 0,
104
      "filesink element");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
105 106
}

107
GST_BOILERPLATE_FULL (GstFileSink, gst_file_sink, GstBaseSink,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
108
    GST_TYPE_BASE_SINK, _do_init);
109

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
110
static void
111
gst_file_sink_base_init (gpointer g_class)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
112
{
113
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
114

115
  gstelement_class->change_state = gst_file_sink_change_state;
116 117
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&sinktemplate));
118
  gst_element_class_set_details (gstelement_class, &gst_file_sink_details);
119
}
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
120

121
static void
122
gst_file_sink_class_init (GstFileSinkClass * klass)
123 124
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
125
  GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
126

127 128
  gobject_class->set_property = gst_file_sink_set_property;
  gobject_class->get_property = gst_file_sink_get_property;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
129

130
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION,
131
      g_param_spec_string ("location", "File Location",
132
          "Location of the file to write", NULL, G_PARAM_READWRITE));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
133

134
  gobject_class->dispose = gst_file_sink_dispose;
135

136 137
  gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_file_sink_render);
  gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_file_sink_event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
138
}
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
139

140
static void
141
gst_file_sink_init (GstFileSink * filesink)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
142 143 144
{
  GstPad *pad;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
145
  pad = GST_BASE_SINK_PAD (filesink);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
146

147
  gst_pad_set_query_function (pad, gst_file_sink_query);
148

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
149 150 151
  filesink->filename = NULL;
  filesink->file = NULL;
}
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
152

Benjamin Otte's avatar
Benjamin Otte committed
153
static void
154
gst_file_sink_dispose (GObject * object)
Benjamin Otte's avatar
Benjamin Otte committed
155
{
156
  GstFileSink *sink = GST_FILE_SINK (object);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
157

Benjamin Otte's avatar
Benjamin Otte committed
158
  G_OBJECT_CLASS (parent_class)->dispose (object);
159

Benjamin Otte's avatar
Benjamin Otte committed
160 161 162 163 164
  g_free (sink->uri);
  sink->uri = NULL;
  g_free (sink->filename);
  sink->filename = NULL;
}
165

Benjamin Otte's avatar
Benjamin Otte committed
166
static gboolean
167
gst_file_sink_set_location (GstFileSink * sink, const gchar * location)
Benjamin Otte's avatar
Benjamin Otte committed
168 169
{
  /* the element must be stopped or paused in order to do this */
170
  if (GST_STATE (sink) >= GST_STATE_PAUSED)
Benjamin Otte's avatar
Benjamin Otte committed
171 172 173 174
    return FALSE;

  g_free (sink->filename);
  g_free (sink->uri);
175 176 177 178 179 180 181
  if (location != NULL) {
    sink->filename = g_strdup (location);
    sink->uri = gst_uri_construct ("file", location);
  } else {
    sink->filename = NULL;
    sink->uri = NULL;
  }
182

Benjamin Otte's avatar
Benjamin Otte committed
183 184
  return TRUE;
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
185
static void
186
gst_file_sink_set_property (GObject * object, guint prop_id,
187
    const GValue * value, GParamSpec * pspec)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
188 189 190
{
  GstFileSink *sink;

191
  sink = GST_FILE_SINK (object);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
192 193 194

  switch (prop_id) {
    case ARG_LOCATION:
195
      gst_file_sink_set_location (sink, g_value_get_string (value));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
196 197
      break;
    default:
198
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
199 200 201 202
      break;
  }
}

203
static void
204
gst_file_sink_get_property (GObject * object, guint prop_id, GValue * value,
205
    GParamSpec * pspec)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
206 207
{
  GstFileSink *sink;
208

209
  g_return_if_fail (GST_IS_FILE_SINK (object));
210

211
  sink = GST_FILE_SINK (object);
212

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
213 214
  switch (prop_id) {
    case ARG_LOCATION:
215
      g_value_set_string (value, sink->filename);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
216 217 218 219 220 221 222 223
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static gboolean
224
gst_file_sink_open_file (GstFileSink * sink)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
225 226
{
  /* open the file */
227
  if (sink->filename == NULL || sink->filename[0] == '\0') {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
228
    GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
229
        (_("No file name specified for writing.")), (NULL));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
230 231
    return FALSE;
  }
232

233
  sink->file = fopen (sink->filename, "wb");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
234
  if (sink->file == NULL) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
235
    GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
236 237
        (_("Could not open file \"%s\" for writing."), sink->filename),
        GST_ERROR_SYSTEM);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
238
    return FALSE;
239
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
240 241 242 243 244 245 246

  sink->data_written = 0;

  return TRUE;
}

static void
247
gst_file_sink_close_file (GstFileSink * sink)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
248
{
249
  if (fclose (sink->file) != 0) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
250
    GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
251
        (_("Error closing file \"%s\"."), sink->filename), GST_ERROR_SYSTEM);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
252 253 254
  }
}

255
static gboolean
256
gst_file_sink_query (GstPad * pad, GstQuery * query)
257
{
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
258 259
  GstFileSink *self;
  GstFormat format;
260

261
  self = GST_FILE_SINK (GST_PAD_PARENT (pad));
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
262 263

  switch (GST_QUERY_TYPE (query)) {
264
    case GST_QUERY_POSITION:
265
      gst_query_parse_position (query, &format, NULL, NULL);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
266 267
      switch (format) {
        case GST_FORMAT_DEFAULT:
268
        case GST_FORMAT_BYTES:
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
269 270 271
          gst_query_set_position (query, GST_FORMAT_BYTES,
              self->data_written, self->data_written);
          return TRUE;
272 273
        default:
          return FALSE;
274
      }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
275 276 277 278 279

    case GST_QUERY_FORMATS:
      gst_query_set_formats (query, 2, GST_FORMAT_DEFAULT, GST_FORMAT_BYTES);
      return TRUE;

280
    default:
281
      return gst_pad_query_default (pad, query);
282 283 284
  }
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
285 286
/* handle events (search) */
static gboolean
287
gst_file_sink_event (GstBaseSink * sink, GstEvent * event)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
288 289 290 291
{
  GstEventType type;
  GstFileSink *filesink;

292
  filesink = GST_FILE_SINK (sink);
293

Wim Taymans's avatar
Wim Taymans committed
294
  type = GST_EVENT_TYPE (event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
295 296

  switch (type) {
Wim Taymans's avatar
Wim Taymans committed
297
    case GST_EVENT_NEWSEGMENT:
298
    {
299
      gint64 soffset, eoffset;
Wim Taymans's avatar
Wim Taymans committed
300
      GstFormat format;
301

Wim Taymans's avatar
Wim Taymans committed
302 303 304 305
      gst_event_parse_newsegment (event, NULL, &format, &soffset, &eoffset,
          NULL);

      if (format == GST_FORMAT_BYTES) {
306
        fseek (filesink->file, soffset, SEEK_SET);
Wim Taymans's avatar
Wim Taymans committed
307
      }
308 309
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
310
    case GST_EVENT_EOS:
311
      if (fflush (filesink->file)) {
312 313 314
        GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
            (_("Error while writing to file \"%s\"."), filesink->filename),
            GST_ERROR_SYSTEM);
315
      }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
316 317 318 319 320 321 322 323 324
      break;
    default:
      break;
  }

  return TRUE;
}

/**
325
 * gst_file_sink_chain:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
326 327 328 329 330
 * @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
 */
331
static GstFlowReturn
332
gst_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
333 334
{
  GstFileSink *filesink;
335
  guint size, back_pending = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
336

337
  size = GST_BUFFER_SIZE (buffer);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
338

339
  filesink = GST_FILE_SINK (sink);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
340

341 342
  if (ftell (filesink->file) < filesink->data_written)
    back_pending = filesink->data_written - ftell (filesink->file);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
343

344 345 346 347 348
  if (fwrite (GST_BUFFER_DATA (buffer), size, 1, filesink->file) != 1) {
    GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
        (_("Error while writing to file \"%s\"."), filesink->filename),
        ("%s", g_strerror (errno)));
    return GST_FLOW_ERROR;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
349 350
  }

351
  filesink->data_written += size - back_pending;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
352

353
  return GST_FLOW_OK;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
354 355 356
}

static GstElementStateReturn
357
gst_file_sink_change_state (GstElement * element)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
358
{
359 360
  GstElementStateReturn ret;
  gint transition;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
361

362
  transition = GST_STATE_TRANSITION (element);
363

364 365 366
  switch (transition) {
    case GST_STATE_NULL_TO_READY:
      break;
367
    case GST_STATE_READY_TO_PAUSED:
368
      if (!gst_file_sink_open_file (GST_FILE_SINK (element)))
369 370 371 372 373
        goto open_error;
      break;
    case GST_STATE_PAUSED_TO_PLAYING:
      break;
    default:
374
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
375 376
  }

377
  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
378

379 380 381 382
  switch (transition) {
    case GST_STATE_PLAYING_TO_PAUSED:
      break;
    case GST_STATE_PAUSED_TO_READY:
383
      gst_file_sink_close_file (GST_FILE_SINK (element));
384 385 386 387 388 389 390 391 392 393 394 395 396
      break;
    case GST_STATE_READY_TO_NULL:
      break;
    default:
      break;
  }

  return ret;

open_error:
  {
    return GST_STATE_FAILURE;
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
397
}
Benjamin Otte's avatar
Benjamin Otte committed
398 399 400 401

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

static guint
402
gst_file_sink_uri_get_type (void)
Benjamin Otte's avatar
Benjamin Otte committed
403 404 405 406
{
  return GST_URI_SINK;
}
static gchar **
407
gst_file_sink_uri_get_protocols (void)
Benjamin Otte's avatar
Benjamin Otte committed
408
{
409
  static gchar *protocols[] = { "file", NULL };
410

Benjamin Otte's avatar
Benjamin Otte committed
411 412 413
  return protocols;
}
static const gchar *
414
gst_file_sink_uri_get_uri (GstURIHandler * handler)
Benjamin Otte's avatar
Benjamin Otte committed
415
{
416
  GstFileSink *sink = GST_FILE_SINK (handler);
417

Benjamin Otte's avatar
Benjamin Otte committed
418 419
  return sink->uri;
}
420

Benjamin Otte's avatar
Benjamin Otte committed
421
static gboolean
422
gst_file_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri)
Benjamin Otte's avatar
Benjamin Otte committed
423 424 425
{
  gchar *protocol, *location;
  gboolean ret;
426
  GstFileSink *sink = GST_FILE_SINK (handler);
Benjamin Otte's avatar
Benjamin Otte committed
427 428 429 430 431 432 433 434

  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);
435
  ret = gst_file_sink_set_location (sink, location);
Benjamin Otte's avatar
Benjamin Otte committed
436 437 438 439 440 441
  g_free (location);

  return ret;
}

static void
442
gst_file_sink_uri_handler_init (gpointer g_iface, gpointer iface_data)
Benjamin Otte's avatar
Benjamin Otte committed
443 444 445
{
  GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;

446 447 448 449
  iface->get_type = gst_file_sink_uri_get_type;
  iface->get_protocols = gst_file_sink_uri_get_protocols;
  iface->get_uri = gst_file_sink_uri_get_uri;
  iface->set_uri = gst_file_sink_uri_set_uri;
Benjamin Otte's avatar
Benjamin Otte committed
450
}