static void gst_multi_file_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object); switch (prop_id) { case PROP_LOCATION: g_value_set_string (value, sink->filename); break; case PROP_INDEX: g_value_set_int (value, sink->index); break; case PROP_POST_MESSAGES: g_value_set_boolean (value, sink->post_messages); break; case PROP_NEXT_FILE: g_value_set_enum (value, sink->next_file); break; case PROP_MAX_FILES: g_value_set_uint (value, sink->max_files); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void gst_multi_file_sink_finalize (GObject * object) { GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object); g_free (sink->filename); G_OBJECT_CLASS (parent_class)->finalize (object); }
static gboolean gst_multi_file_sink_start (GstBaseSink * bsink) { GstMultiFileSink *sink = GST_MULTI_FILE_SINK (bsink); if (sink->aggregate_gops) sink->gop_adapter = gst_adapter_new (); sink->potential_next_gop = NULL; sink->file_pts = GST_CLOCK_TIME_NONE; return TRUE; }
static gboolean gst_multi_file_sink_stop (GstBaseSink * sink) { GstMultiFileSink *multifilesink; multifilesink = GST_MULTI_FILE_SINK (sink); if (multifilesink->file != NULL) { fclose (multifilesink->file); multifilesink->file = NULL; } return TRUE; }
static gboolean gst_multi_file_sink_stop (GstBaseSink * sink) { GstMultiFileSink *multifilesink; int i; multifilesink = GST_MULTI_FILE_SINK (sink); if (multifilesink->file != NULL) { fclose (multifilesink->file); multifilesink->file = NULL; } if (multifilesink->streamheaders) { for (i = 0; i < multifilesink->n_streamheaders; i++) { gst_buffer_unref (multifilesink->streamheaders[i]); } g_free (multifilesink->streamheaders); multifilesink->streamheaders = NULL; } if (multifilesink->gop_adapter != NULL) { g_object_unref (multifilesink->gop_adapter); multifilesink->gop_adapter = NULL; } if (multifilesink->potential_next_gop != NULL) { g_list_free_full (multifilesink->potential_next_gop, (GDestroyNotify) gst_buffer_unref); multifilesink->potential_next_gop = NULL; } multifilesink->force_key_unit_count = -1; g_queue_foreach (&multifilesink->old_files, (GFunc) g_free, NULL); g_queue_clear (&multifilesink->old_files); return TRUE; }
static gboolean gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps) { GstMultiFileSink *multifilesink; GstStructure *structure; multifilesink = GST_MULTI_FILE_SINK (sink); structure = gst_caps_get_structure (caps, 0); if (structure) { const GValue *value; value = gst_structure_get_value (structure, "streamheader"); if (GST_VALUE_HOLDS_ARRAY (value)) { int i; if (multifilesink->streamheaders) { for (i = 0; i < multifilesink->n_streamheaders; i++) { gst_buffer_unref (multifilesink->streamheaders[i]); } g_free (multifilesink->streamheaders); } multifilesink->n_streamheaders = gst_value_array_get_size (value); multifilesink->streamheaders = g_malloc (sizeof (GstBuffer *) * multifilesink->n_streamheaders); for (i = 0; i < multifilesink->n_streamheaders; i++) { multifilesink->streamheaders[i] = gst_buffer_ref (gst_value_get_buffer (gst_value_array_get_value (value, i))); } } } return TRUE; }
static gboolean gst_multi_file_sink_stop (GstBaseSink * sink) { GstMultiFileSink *multifilesink; int i; multifilesink = GST_MULTI_FILE_SINK (sink); if (multifilesink->file != NULL) { fclose (multifilesink->file); multifilesink->file = NULL; } if (multifilesink->streamheaders) { for (i = 0; i < multifilesink->n_streamheaders; i++) { gst_buffer_unref (multifilesink->streamheaders[i]); } g_free (multifilesink->streamheaders); multifilesink->streamheaders = NULL; } return TRUE; }
static void gst_multi_file_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object); switch (prop_id) { case PROP_LOCATION: gst_multi_file_sink_set_location (sink, g_value_get_string (value)); break; case PROP_INDEX: sink->index = g_value_get_int (value); break; case PROP_POST_MESSAGES: sink->post_messages = g_value_get_boolean (value); break; case PROP_NEXT_FILE: sink->next_file = g_value_get_enum (value); break; case PROP_MAX_FILES: sink->max_files = g_value_get_uint (value); break; case PROP_MAX_FILE_SIZE: sink->max_file_size = g_value_get_uint64 (value); break; case PROP_MAX_FILE_DURATION: sink->max_file_duration = g_value_get_uint64 (value); break; case PROP_AGGREGATE_GOPS: sink->aggregate_gops = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static GstFlowReturn gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer) { GstMultiFileSink *multifilesink; guint size; guint8 *data; gchar *filename; gboolean ret; GError *error = NULL; size = GST_BUFFER_SIZE (buffer); data = GST_BUFFER_DATA (buffer); multifilesink = GST_MULTI_FILE_SINK (sink); switch (multifilesink->next_file) { case GST_MULTI_FILE_SINK_NEXT_BUFFER: filename = g_strdup_printf (multifilesink->filename, multifilesink->index); ret = g_file_set_contents (filename, (char *) data, size, &error); if (!ret) goto write_error; gst_multi_file_sink_post_message (multifilesink, buffer, filename); multifilesink->index++; g_free (filename); break; case GST_MULTI_FILE_SINK_NEXT_DISCONT: if (GST_BUFFER_IS_DISCONT (buffer)) { if (multifilesink->file) { fclose (multifilesink->file); multifilesink->file = NULL; filename = g_strdup_printf (multifilesink->filename, multifilesink->index); gst_multi_file_sink_post_message (multifilesink, buffer, filename); g_free (filename); multifilesink->index++; } } if (multifilesink->file == NULL) { filename = g_strdup_printf (multifilesink->filename, multifilesink->index); multifilesink->file = g_fopen (filename, "wb"); g_free (filename); if (multifilesink->file == NULL) goto stdio_write_error; } ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1, multifilesink->file); if (ret != 1) goto stdio_write_error; break; case GST_MULTI_FILE_SINK_NEXT_KEY_FRAME: if (multifilesink->next_segment == GST_CLOCK_TIME_NONE) { if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) { multifilesink->next_segment = GST_BUFFER_TIMESTAMP (buffer) + 10 * GST_SECOND; } } if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) && GST_BUFFER_TIMESTAMP (buffer) >= multifilesink->next_segment && !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) { if (multifilesink->file) { fclose (multifilesink->file); multifilesink->file = NULL; filename = g_strdup_printf (multifilesink->filename, multifilesink->index); gst_multi_file_sink_post_message (multifilesink, buffer, filename); g_free (filename); multifilesink->index++; } multifilesink->next_segment += 10 * GST_SECOND; } if (multifilesink->file == NULL) { filename = g_strdup_printf (multifilesink->filename, multifilesink->index); multifilesink->file = g_fopen (filename, "wb"); g_free (filename); if (multifilesink->file == NULL) goto stdio_write_error; } ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1, multifilesink->file); if (ret != 1) goto stdio_write_error; break; default: g_assert_not_reached (); } return GST_FLOW_OK; /* ERRORS */ write_error: { switch (error->code) { case G_FILE_ERROR_NOSPC: { GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT, (NULL), (NULL)); break; } default: { GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE, ("Error while writing to file \"%s\".", filename), ("%s", g_strerror (errno))); } } g_error_free (error); g_free (filename); return GST_FLOW_ERROR; } stdio_write_error: GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE, ("Error while writing to file."), (NULL)); return GST_FLOW_ERROR; }
static gboolean gst_multi_file_sink_event (GstBaseSink * sink, GstEvent * event) { GstMultiFileSink *multifilesink; gchar *filename; multifilesink = GST_MULTI_FILE_SINK (sink); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CUSTOM_DOWNSTREAM: { GstClockTime timestamp, duration; GstClockTime running_time, stream_time; guint64 offset, offset_end; gboolean all_headers; guint count; if (multifilesink->next_file != GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT || !gst_video_event_is_force_key_unit (event)) goto out; gst_video_event_parse_downstream_force_key_unit (event, ×tamp, &stream_time, &running_time, &all_headers, &count); if (multifilesink->force_key_unit_count != -1 && multifilesink->force_key_unit_count == count) goto out; multifilesink->force_key_unit_count = count; if (multifilesink->file) { duration = GST_CLOCK_TIME_NONE; offset = offset_end = -1; filename = g_strdup_printf (multifilesink->filename, multifilesink->index); gst_multi_file_sink_close_file (multifilesink, NULL); gst_multi_file_sink_post_message_full (multifilesink, timestamp, duration, offset, offset_end, running_time, stream_time, filename); g_free (filename); } if (multifilesink->file == NULL) { if (!gst_multi_file_sink_open_next_file (multifilesink)) goto stdio_write_error; } break; } case GST_EVENT_EOS: if (multifilesink->aggregate_gops) { GstBuffer *buf = gst_buffer_new (); /* push key unit buffer to force writing out the pending GOP data */ GST_INFO_OBJECT (sink, "EOS, write pending GOP data"); GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DELTA_UNIT); gst_multi_file_sink_render (sink, buf); gst_buffer_unref (buf); } if (multifilesink->file) { gchar *filename; filename = g_strdup_printf (multifilesink->filename, multifilesink->index); gst_multi_file_sink_close_file (multifilesink, NULL); gst_multi_file_sink_post_message_from_time (multifilesink, GST_BASE_SINK (multifilesink)->segment.position, -1, filename); g_free (filename); } break; default: break; } out: return GST_BASE_SINK_CLASS (parent_class)->event (sink, event); /* ERRORS */ stdio_write_error: { GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE, ("Error while writing to file."), (NULL)); gst_event_unref (event); return FALSE; } }
static GstFlowReturn gst_multi_file_sink_render (GstBaseSink * bsink, GstBuffer * buffer) { GstMultiFileSink *sink = GST_MULTI_FILE_SINK (bsink); GstFlowReturn flow = GST_FLOW_OK; gboolean key_unit, header; header = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_HEADER); key_unit = !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); if (sink->aggregate_gops) { GstBuffer *gop_buffer = NULL; guint avail; avail = gst_adapter_available (sink->gop_adapter); GST_LOG_OBJECT (sink, "aggregate GOP: received %s%s unit buffer: " "%" GST_PTR_FORMAT, (key_unit) ? "key" : "delta", (header) ? " header" : "", buffer); /* If it's a header buffer, it might potentially be for the next GOP */ if (header) { GST_LOG_OBJECT (sink, "Accumulating buffer to potential next GOP"); sink->potential_next_gop = g_list_append (sink->potential_next_gop, gst_buffer_ref (buffer)); } else { if (key_unit && avail > 0) { GstClockTime pts, dts; GST_LOG_OBJECT (sink, "Grabbing pending completed GOP"); pts = gst_adapter_prev_pts_at_offset (sink->gop_adapter, 0, NULL); dts = gst_adapter_prev_dts_at_offset (sink->gop_adapter, 0, NULL); gop_buffer = gst_adapter_take_buffer (sink->gop_adapter, avail); GST_BUFFER_PTS (gop_buffer) = pts; GST_BUFFER_DTS (gop_buffer) = dts; } /* just accumulate the buffer */ if (sink->potential_next_gop) { GList *tmp; GST_LOG_OBJECT (sink, "Carrying over pending next GOP data into adapter"); /* If we have pending data, put that first in the adapter */ for (tmp = sink->potential_next_gop; tmp; tmp = tmp->next) { GstBuffer *tmpb = (GstBuffer *) tmp->data; gst_adapter_push (sink->gop_adapter, tmpb); } g_list_free (sink->potential_next_gop); sink->potential_next_gop = NULL; } GST_LOG_OBJECT (sink, "storing buffer in adapter"); gst_adapter_push (sink->gop_adapter, gst_buffer_ref (buffer)); if (gop_buffer != NULL) { GST_DEBUG_OBJECT (sink, "writing out pending GOP, %u bytes", avail); GST_DEBUG_OBJECT (sink, "gop buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT " duration:%" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (gop_buffer)), GST_TIME_ARGS (GST_BUFFER_DTS (gop_buffer)), GST_TIME_ARGS (GST_BUFFER_DURATION (gop_buffer))); flow = gst_multi_file_sink_write_buffer (sink, gop_buffer); gst_buffer_unref (gop_buffer); } } } else { flow = gst_multi_file_sink_write_buffer (sink, buffer); } return flow; }