static GstStateChangeReturn gst_rsvg_dec_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn res; GstRsvgDec *dec = GST_RSVG_DEC (element); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: break; default: break; } res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); if (res == GST_STATE_CHANGE_FAILURE) return res; switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: gst_rsvg_dec_reset (dec); break; default: break; } return res; }
static GstFlowReturn gst_rsvg_dec_parse (GstVideoDecoder * decoder, GstVideoCodecFrame * frame, GstAdapter * adapter, gboolean at_eos) { GstRsvgDec *rsvg = GST_RSVG_DEC (decoder); gboolean completed = FALSE; const guint8 *data; guint size; guint i; GST_LOG_OBJECT (rsvg, "parse start"); size = gst_adapter_available (adapter); /* "<svg></svg>" */ if (size < 5 + 6) return GST_VIDEO_DECODER_FLOW_NEED_DATA; data = gst_adapter_map (adapter, size); if (data == NULL) { GST_ERROR_OBJECT (rsvg, "Unable to map memory"); return GST_FLOW_ERROR; } for (i = 0; i < size - 4; i++) { if (memcmp (data + i, "<svg", 4) == 0) { gst_adapter_flush (adapter, i); size = gst_adapter_available (adapter); if (size < 5 + 6) return GST_VIDEO_DECODER_FLOW_NEED_DATA; data = gst_adapter_map (adapter, size); if (data == NULL) { GST_ERROR_OBJECT (rsvg, "Unable to map memory"); return GST_FLOW_ERROR; } break; } } /* If start wasn't found: */ if (i == size - 4) { gst_adapter_flush (adapter, size - 4); return GST_VIDEO_DECODER_FLOW_NEED_DATA; } for (i = size - 6; i >= 5; i--) { if (memcmp (data + i, "</svg>", 6) == 0) { completed = TRUE; size = i + 6; break; } } if (completed) { GST_LOG_OBJECT (rsvg, "have complete svg of %u bytes", size); gst_video_decoder_add_to_frame (decoder, size); return gst_video_decoder_have_frame (decoder); } return GST_VIDEO_DECODER_FLOW_NEED_DATA; }
static gboolean gst_rsvg_dec_reset (GstVideoDecoder * decoder, gboolean hard) { GstRsvgDec *dec = GST_RSVG_DEC (decoder); dec_reset (dec); return TRUE; }
static void gst_rsvg_dec_finalize (GObject * object) { GstRsvgDec *rsvg = GST_RSVG_DEC (object); if (rsvg->adapter) { g_object_unref (rsvg->adapter); rsvg->adapter = NULL; } G_OBJECT_CLASS (parent_class)->finalize (object); }
static gboolean gst_rsvg_dec_stop (GstVideoDecoder * decoder) { GstRsvgDec *rsvg = GST_RSVG_DEC (decoder); if (rsvg->input_state) { gst_video_codec_state_unref (rsvg->input_state); rsvg->input_state = NULL; } return TRUE; }
static gboolean gst_rsvg_dec_sink_set_caps (GstPad * pad, GstCaps * caps) { GstRsvgDec *rsvg = GST_RSVG_DEC (gst_pad_get_parent (pad)); gboolean ret = TRUE; GstStructure *s = gst_caps_get_structure (caps, 0); gst_structure_get_fraction (s, "framerate", &rsvg->fps_n, &rsvg->fps_d); gst_object_unref (rsvg); return ret; }
static gboolean gst_rsvg_dec_src_set_caps (GstPad * pad, GstCaps * caps) { GstRsvgDec *rsvg = GST_RSVG_DEC (gst_pad_get_parent (pad)); gboolean ret = TRUE; GstStructure *s = gst_caps_get_structure (caps, 0); ret &= gst_structure_get_int (s, "width", &rsvg->width); ret &= gst_structure_get_int (s, "height", &rsvg->height); gst_object_unref (rsvg); return ret; }
static gboolean gst_rsvg_dec_sink_event (GstPad * pad, GstEvent * event) { GstRsvgDec *rsvg = GST_RSVG_DEC (gst_pad_get_parent (pad)); gboolean res = FALSE; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_NEWSEGMENT:{ gdouble rate, arate; gboolean update; gint64 start, stop, position; GstFormat fmt; gst_event_parse_new_segment_full (event, &update, &rate, &arate, &fmt, &start, &stop, &position); gst_segment_set_newsegment_full (&rsvg->segment, update, rate, arate, fmt, start, stop, position); if (fmt == GST_FORMAT_TIME) { rsvg->need_newsegment = FALSE; res = gst_pad_push_event (rsvg->srcpad, event); } else { gst_event_unref (event); res = TRUE; } break; } case GST_EVENT_EOS: case GST_EVENT_FLUSH_STOP: gst_adapter_clear (rsvg->adapter); /* fall through */ case GST_EVENT_FLUSH_START: res = gst_pad_push_event (rsvg->srcpad, event); break; default: if (GST_PAD_CAPS (rsvg->srcpad)) { res = gst_pad_push_event (rsvg->srcpad, event); } else { res = TRUE; rsvg->pending_events = g_list_append (rsvg->pending_events, event); } break; } gst_object_unref (rsvg); return res; }
static gboolean gst_rsvg_dec_src_query (GstPad * pad, GstQuery * query) { GstRsvgDec *rsvg = GST_RSVG_DEC (gst_pad_get_parent (pad)); gboolean res = TRUE; switch (GST_QUERY_TYPE (query)) { default: res = gst_pad_query_default (pad, query); break; } gst_object_unref (rsvg); return res; }
static gboolean gst_rsvg_dec_src_event (GstPad * pad, GstEvent * event) { GstRsvgDec *rsvg = GST_RSVG_DEC (gst_pad_get_parent (pad)); gboolean res = FALSE; switch (GST_EVENT_TYPE (event)) { default: res = gst_pad_push_event (rsvg->sinkpad, event); break; } gst_object_unref (rsvg); return res; }
static gboolean gst_rsvg_dec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state) { GstRsvgDec *rsvg = GST_RSVG_DEC (decoder); GstVideoInfo *info = &state->info; if (rsvg->input_state) gst_video_codec_state_unref (rsvg->input_state); rsvg->input_state = gst_video_codec_state_ref (state); /* Create the output state */ gst_video_decoder_set_output_state (decoder, GST_RSVG_VIDEO_FORMAT, GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info), rsvg->input_state); return TRUE; }
static GstFlowReturn gst_rsvg_dec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame) { GstRsvgDec *rsvg = GST_RSVG_DEC (decoder); gboolean ret; ret = gst_rsvg_decode_image (rsvg, frame->input_buffer, frame); switch (ret) { case GST_FLOW_OK: ret = gst_video_decoder_finish_frame (decoder, frame); break; default: gst_video_codec_frame_unref (frame); break; } GST_LOG_OBJECT (rsvg, "Handle frame done"); return ret; }
static GstFlowReturn gst_rsvg_dec_chain (GstPad * pad, GstBuffer * buffer) { GstRsvgDec *rsvg = GST_RSVG_DEC (GST_PAD_PARENT (pad)); gboolean completed = FALSE; const guint8 *data; guint size; gboolean ret = GST_FLOW_OK; /* first_timestamp is used slightly differently where a framerate is given or not. If there is a frame rate, it will be used as a base. If there is not, it will be used to keep track of the timestamp of the first buffer, to be used as the timestamp of the output buffer. When a buffer is output, first timestamp will resync to the next buffer's timestamp. */ if (rsvg->first_timestamp == GST_CLOCK_TIME_NONE) { if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) rsvg->first_timestamp = GST_BUFFER_TIMESTAMP (buffer); else if (rsvg->fps_n != 0) rsvg->first_timestamp = 0; } gst_adapter_push (rsvg->adapter, buffer); size = gst_adapter_available (rsvg->adapter); /* "<svg></svg>" */ while (size >= 5 + 6 && ret == GST_FLOW_OK) { guint i; data = gst_adapter_peek (rsvg->adapter, size); for (i = size - 6; i >= 5; i--) { if (memcmp (data + i, "</svg>", 6) == 0) { completed = TRUE; size = i + 6; break; } } if (completed) { GstBuffer *outbuf = NULL; GST_LOG_OBJECT (rsvg, "have complete svg of %u bytes", size); data = gst_adapter_peek (rsvg->adapter, size); ret = gst_rsvg_decode_image (rsvg, data, size, &outbuf); if (ret != GST_FLOW_OK) break; if (rsvg->first_timestamp != GST_CLOCK_TIME_NONE) { GST_BUFFER_TIMESTAMP (outbuf) = rsvg->first_timestamp; GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE; if (GST_BUFFER_DURATION_IS_VALID (buffer)) { GstClockTime end = GST_BUFFER_TIMESTAMP_IS_VALID (buffer) ? GST_BUFFER_TIMESTAMP (buffer) : rsvg->first_timestamp; end += GST_BUFFER_DURATION (buffer); GST_BUFFER_DURATION (outbuf) = end - GST_BUFFER_TIMESTAMP (outbuf); } if (rsvg->fps_n == 0) { rsvg->first_timestamp = GST_CLOCK_TIME_NONE; } else { GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale (rsvg->frame_count, rsvg->fps_d, rsvg->fps_n * GST_SECOND); } } else if (rsvg->fps_n != 0) { GST_BUFFER_TIMESTAMP (outbuf) = rsvg->first_timestamp + gst_util_uint64_scale (rsvg->frame_count, rsvg->fps_d, rsvg->fps_n * GST_SECOND); GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale (rsvg->frame_count, rsvg->fps_d, rsvg->fps_n * GST_SECOND); } else { GST_BUFFER_TIMESTAMP (outbuf) = rsvg->first_timestamp; GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE; } rsvg->frame_count++; if (rsvg->need_newsegment) { gst_pad_push_event (rsvg->srcpad, gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0)); rsvg->need_newsegment = FALSE; } if (rsvg->pending_events) { GList *l; for (l = rsvg->pending_events; l; l = l->next) gst_pad_push_event (rsvg->srcpad, l->data); g_list_free (rsvg->pending_events); rsvg->pending_events = NULL; } GST_LOG_OBJECT (rsvg, "image rendered okay"); ret = gst_pad_push (rsvg->srcpad, outbuf); if (ret != GST_FLOW_OK) break; gst_adapter_flush (rsvg->adapter, size); size = gst_adapter_available (rsvg->adapter); continue; } else { break; } } return GST_FLOW_OK; }
static GstFlowReturn gst_rsvg_dec_chain (GstPad * pad, GstBuffer * buffer) { GstRsvgDec *rsvg = GST_RSVG_DEC (gst_pad_get_parent (pad)); gboolean completed = FALSE; const guint8 *data; guint size; gboolean ret = GST_FLOW_OK; if (rsvg->timestamp_offset == GST_CLOCK_TIME_NONE) { if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) rsvg->timestamp_offset = GST_BUFFER_TIMESTAMP (buffer); else rsvg->timestamp_offset = 0; } gst_adapter_push (rsvg->adapter, buffer); size = gst_adapter_available (rsvg->adapter); /* "<svg></svg>" */ while (size >= 5 + 6 && ret == GST_FLOW_OK) { guint i; data = gst_adapter_peek (rsvg->adapter, size); for (i = size - 6; i >= 5; i--) { if (memcmp (data + i, "</svg>", 6) == 0) { completed = TRUE; size = i + 6; break; } } if (completed) { GstBuffer *outbuf = NULL; data = gst_adapter_peek (rsvg->adapter, size); ret = gst_rsvg_decode_image (rsvg, data, size, &outbuf); if (ret != GST_FLOW_OK) break; if (rsvg->fps_n != 0) { GST_BUFFER_TIMESTAMP (outbuf) = rsvg->timestamp_offset + gst_util_uint64_scale (rsvg->frame_count, rsvg->fps_d, rsvg->fps_n * GST_SECOND); GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale (rsvg->frame_count, rsvg->fps_d, rsvg->fps_n * GST_SECOND); } else { GST_BUFFER_TIMESTAMP (outbuf) = 0; } rsvg->frame_count++; if (rsvg->need_newsegment) { gst_pad_push_event (rsvg->srcpad, gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0)); rsvg->need_newsegment = FALSE; } if (rsvg->pending_events) { GList *l; for (l = rsvg->pending_events; l; l = l->next) gst_pad_push_event (rsvg->srcpad, l->data); g_list_free (rsvg->pending_events); rsvg->pending_events = NULL; } if (rsvg->pending_tags) { gst_element_found_tags (GST_ELEMENT_CAST (rsvg), rsvg->pending_tags); rsvg->pending_tags = NULL; } ret = gst_pad_push (rsvg->srcpad, outbuf); if (ret != GST_FLOW_OK) break; gst_adapter_flush (rsvg->adapter, size); size = gst_adapter_available (rsvg->adapter); continue; } else { break; } } gst_object_unref (rsvg); return GST_FLOW_OK; }