From 5fe455e28fc1c63eaa66b545958e588b09807e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 20 Jul 2009 13:45:54 -0400 Subject: Revert "mimdec: Ignore the timestamps inside the buffers" This reverts commit 5e051fa98aeebdce2eca6b321ec1929e6f8fdf61. --- ext/mimic/gstmimdec.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++-- ext/mimic/gstmimdec.h | 2 ++ 2 files changed, 97 insertions(+), 2 deletions(-) (limited to 'ext') diff --git a/ext/mimic/gstmimdec.c b/ext/mimic/gstmimdec.c index 6a0abce5..1323b154 100644 --- a/ext/mimic/gstmimdec.c +++ b/ext/mimic/gstmimdec.c @@ -68,6 +68,8 @@ static GstFlowReturn gst_mimdec_chain (GstPad * pad, GstBuffer * in); static GstStateChangeReturn gst_mimdec_change_state (GstElement * element, GstStateChange transition); +static gboolean gst_mimdec_sink_event (GstPad * pad, GstEvent * event); + GST_BOILERPLATE (GstMimDec, gst_mimdec, GstElement, GST_TYPE_ELEMENT); @@ -116,6 +118,7 @@ gst_mimdec_init (GstMimDec * mimdec, GstMimDecClass * klass) "sink"); gst_element_add_pad (GST_ELEMENT (mimdec), mimdec->sinkpad); gst_pad_set_chain_function (mimdec->sinkpad, gst_mimdec_chain); + gst_pad_set_event_function (mimdec->sinkpad, gst_mimdec_sink_event); mimdec->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&src_factory), @@ -151,7 +154,6 @@ gst_mimdec_chain (GstPad * pad, GstBuffer * in) gint width, height; GstCaps *caps; GstFlowReturn res = GST_FLOW_OK; - GstClockTime in_time = GST_BUFFER_TIMESTAMP (in); GST_DEBUG ("in gst_mimdec_chain"); @@ -213,6 +215,9 @@ gst_mimdec_chain (GstPad * pad, GstBuffer * in) (guchar *) gst_adapter_peek (mimdec->adapter, mimdec->payload_size); if (mimdec->dec == NULL) { + GstEvent *event = NULL; + gboolean result = TRUE; + /* Check if its a keyframe, otherwise skip it */ if (GUINT32_FROM_LE (*((guint32 *) (frame_body + 12))) != 0) { gst_adapter_flush (mimdec->adapter, mimdec->payload_size); @@ -254,6 +259,20 @@ gst_mimdec_chain (GstPad * pad, GstBuffer * in) res = GST_FLOW_ERROR; goto out; } + + if (mimdec->need_newsegment) + event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, + mimdec->current_ts * GST_MSECOND, -1, 0); + mimdec->need_newsegment = FALSE; + GST_OBJECT_UNLOCK (mimdec); + if (event) + result = gst_pad_push_event (mimdec->srcpad, event); + GST_OBJECT_LOCK (mimdec); + if (!result) { + GST_WARNING_OBJECT (mimdec, "gst_pad_push_event failed"); + res = GST_FLOW_ERROR; + goto out; + } } out_buf = gst_buffer_new_and_alloc (mimdec->buffer_size); @@ -270,7 +289,7 @@ gst_mimdec_chain (GstPad * pad, GstBuffer * in) goto out; } - GST_BUFFER_TIMESTAMP (out_buf) = in_time; + GST_BUFFER_TIMESTAMP (out_buf) = mimdec->current_ts * GST_MSECOND; mimic_get_property (mimdec->dec, "width", &width); mimic_get_property (mimdec->dec, "height", &height); @@ -322,9 +341,83 @@ gst_mimdec_change_state (GstElement * element, GstStateChange transition) GST_OBJECT_UNLOCK (element); } break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_OBJECT_LOCK (element); + mimdec->need_newsegment = TRUE; + GST_OBJECT_UNLOCK (element); + break; default: break; } return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); } + +static gboolean +gst_mimdec_sink_event (GstPad * pad, GstEvent * event) +{ + gboolean res = TRUE; + GstMimDec *mimdec = GST_MIMDEC (gst_pad_get_parent (pad)); + + /* + * Ignore upstream newsegment event, its EVIL, we should implement + * proper seeking instead + */ + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT: + { + gboolean update; + GstFormat format; + gdouble rate, arate; + gint64 start, stop, time; + + gst_event_parse_new_segment_full (event, &update, &rate, &arate, + &format, &start, &stop, &time); + + /* we need TIME and a positive rate */ + if (format != GST_FORMAT_TIME) + goto newseg_wrong_format; + + if (rate <= 0.0) + goto newseg_wrong_rate; + + GST_OBJECT_LOCK (mimdec); + mimdec->need_newsegment = FALSE; + GST_OBJECT_UNLOCK (mimdec); + + res = gst_pad_push_event (mimdec->srcpad, event); + } + break; + case GST_EVENT_FLUSH_STOP: + GST_OBJECT_LOCK (mimdec); + mimdec->need_newsegment = TRUE; + GST_OBJECT_UNLOCK (mimdec); + + res = gst_pad_push_event (mimdec->srcpad, event); + break; + default: + res = gst_pad_push_event (mimdec->srcpad, event); + break; + } + +done: + + gst_object_unref (mimdec); + + return res; + +newseg_wrong_format: + { + GST_DEBUG_OBJECT (mimdec, "received non TIME newsegment"); + gst_event_unref (event); + goto done; + } +newseg_wrong_rate: + { + GST_DEBUG_OBJECT (mimdec, "negative rates not supported yet"); + gst_event_unref (event); + goto done; + } + + +} diff --git a/ext/mimic/gstmimdec.h b/ext/mimic/gstmimdec.h index 7db6b1b8..e1db38d1 100644 --- a/ext/mimic/gstmimdec.h +++ b/ext/mimic/gstmimdec.h @@ -57,6 +57,8 @@ struct _GstMimDec gboolean have_header; guint32 payload_size; guint32 current_ts; + + gboolean need_newsegment; }; struct _GstMimDecClass -- cgit v1.2.1 From 297ab50ca11936b155e6919625811fbd45ef2eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 20 Jul 2009 14:00:17 -0400 Subject: mimdec: Don't overwrite valid timestamps --- ext/mimic/gstmimdec.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'ext') diff --git a/ext/mimic/gstmimdec.c b/ext/mimic/gstmimdec.c index 1323b154..5b28f457 100644 --- a/ext/mimic/gstmimdec.c +++ b/ext/mimic/gstmimdec.c @@ -154,6 +154,7 @@ gst_mimdec_chain (GstPad * pad, GstBuffer * in) gint width, height; GstCaps *caps; GstFlowReturn res = GST_FLOW_OK; + GstClockTime in_time = GST_BUFFER_TIMESTAMP (in); GST_DEBUG ("in gst_mimdec_chain"); @@ -289,7 +290,10 @@ gst_mimdec_chain (GstPad * pad, GstBuffer * in) goto out; } - GST_BUFFER_TIMESTAMP (out_buf) = mimdec->current_ts * GST_MSECOND; + if (GST_CLOCK_TIME_IS_VALID (in_time)) + GST_BUFFER_TIMESTAMP (out_buf) = in_time; + else + GST_BUFFER_TIMESTAMP (out_buf) = mimdec->current_ts * GST_MSECOND; mimic_get_property (mimdec->dec, "width", &width); mimic_get_property (mimdec->dec, "height", &height); -- cgit v1.2.1 From a99348170e835662b51182414fbf25ad589f9e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 20 Jul 2009 21:52:59 -0400 Subject: mimenc: Unref clockid --- ext/mimic/gstmimenc.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'ext') diff --git a/ext/mimic/gstmimenc.c b/ext/mimic/gstmimenc.c index 2418e7c8..8ab76222 100644 --- a/ext/mimic/gstmimenc.c +++ b/ext/mimic/gstmimenc.c @@ -538,6 +538,8 @@ paused_mode_task (gpointer data) GST_OBJECT_LOCK (mimenc); mimenc->clock_id = NULL; GST_OBJECT_UNLOCK (mimenc); + + gst_clock_id_unref (id); } return; -- cgit v1.2.1 From 8f1301390de7662292dd18cc6dd863062953a777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 20 Jul 2009 22:08:24 -0400 Subject: mimenc: Use gst_pad_new_from_static_template Prevents leak found by valgrind --- ext/mimic/gstmimenc.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'ext') diff --git a/ext/mimic/gstmimenc.c b/ext/mimic/gstmimenc.c index 8ab76222..4a65b9fd 100644 --- a/ext/mimic/gstmimenc.c +++ b/ext/mimic/gstmimenc.c @@ -142,17 +142,13 @@ gst_mimenc_class_init (GstMimEncClass * klass) static void gst_mimenc_init (GstMimEnc * mimenc, GstMimEncClass * klass) { - mimenc->sinkpad = - gst_pad_new_from_template (gst_static_pad_template_get (&sink_factory), - "sink"); + mimenc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); gst_element_add_pad (GST_ELEMENT (mimenc), mimenc->sinkpad); gst_pad_set_setcaps_function (mimenc->sinkpad, gst_mimenc_setcaps); gst_pad_set_chain_function (mimenc->sinkpad, gst_mimenc_chain); gst_pad_set_event_function (mimenc->sinkpad, gst_mimenc_event); - mimenc->srcpad = - gst_pad_new_from_template (gst_static_pad_template_get (&src_factory), - "src"); + mimenc->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); gst_element_add_pad (GST_ELEMENT (mimenc), mimenc->srcpad); mimenc->enc = NULL; -- cgit v1.2.1 From 22b95ab4cdbbac7cde1487abde5d57ea2c311e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 20 Jul 2009 22:08:52 -0400 Subject: mimdec: Fix leaks Use gst_pad_new_from_static_template() to not leak the pad template Also properly chain up the finalize to the parent --- ext/mimic/gstmimdec.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'ext') diff --git a/ext/mimic/gstmimdec.c b/ext/mimic/gstmimdec.c index 5b28f457..9cd2c36c 100644 --- a/ext/mimic/gstmimdec.c +++ b/ext/mimic/gstmimdec.c @@ -113,16 +113,12 @@ gst_mimdec_class_init (GstMimDecClass * klass) static void gst_mimdec_init (GstMimDec * mimdec, GstMimDecClass * klass) { - mimdec->sinkpad = - gst_pad_new_from_template (gst_static_pad_template_get (&sink_factory), - "sink"); + mimdec->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); gst_element_add_pad (GST_ELEMENT (mimdec), mimdec->sinkpad); gst_pad_set_chain_function (mimdec->sinkpad, gst_mimdec_chain); gst_pad_set_event_function (mimdec->sinkpad, gst_mimdec_sink_event); - mimdec->srcpad = - gst_pad_new_from_template (gst_static_pad_template_get (&src_factory), - "src"); + mimdec->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); gst_element_add_pad (GST_ELEMENT (mimdec), mimdec->srcpad); mimdec->adapter = gst_adapter_new (); @@ -141,6 +137,8 @@ gst_mimdec_finalize (GObject * object) gst_adapter_clear (mimdec->adapter); g_object_unref (mimdec->adapter); + + GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); } static GstFlowReturn -- cgit v1.2.1 From e8c8725efecd06b4adf7dc812c02481c837c5abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 20 Jul 2009 22:13:11 -0400 Subject: mimdec: Lock element before unlocking --- ext/mimic/gstmimdec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'ext') diff --git a/ext/mimic/gstmimdec.c b/ext/mimic/gstmimdec.c index 9cd2c36c..a0878a13 100644 --- a/ext/mimic/gstmimdec.c +++ b/ext/mimic/gstmimdec.c @@ -333,6 +333,7 @@ gst_mimdec_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_READY_TO_NULL: + GST_OBJECT_LOCK (element); if (mimdec->dec != NULL) { mimic_close (mimdec->dec); mimdec->dec = NULL; @@ -340,8 +341,8 @@ gst_mimdec_change_state (GstElement * element, GstStateChange transition) mimdec->have_header = FALSE; mimdec->payload_size = -1; mimdec->current_ts = -1; - GST_OBJECT_UNLOCK (element); } + GST_OBJECT_UNLOCK (element); break; case GST_STATE_CHANGE_READY_TO_PAUSED: GST_OBJECT_LOCK (element); -- cgit v1.2.1 From 7de65d974eac1edeec207e1f1a27f9b62d9d0c01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 24 Jul 2009 07:40:17 +0200 Subject: musepackdec: Implement SEEKING query --- ext/musepack/gstmusepackdec.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'ext') diff --git a/ext/musepack/gstmusepackdec.c b/ext/musepack/gstmusepackdec.c index fea4bc05..30a34bff 100644 --- a/ext/musepack/gstmusepackdec.c +++ b/ext/musepack/gstmusepackdec.c @@ -300,6 +300,7 @@ gst_musepackdec_get_src_query_types (GstPad * pad) static const GstQueryType query_types[] = { GST_QUERY_POSITION, GST_QUERY_DURATION, + GST_QUERY_SEEKING, (GstQueryType) 0 }; @@ -358,6 +359,18 @@ gst_musepackdec_src_query (GstPad * pad, GstQuery * query) } break; } + case GST_QUERY_SEEKING:{ + GstFormat fmt; + + res = TRUE; + gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL); + if (fmt == GST_FORMAT_TIME || fmt == GST_FORMAT_DEFAULT) + gst_query_set_seeking (query, fmt, TRUE, 0, -1); + else + gst_query_set_seeking (query, fmt, FALSE, -1, -1); + + break; + } default: res = gst_pad_query_default (pad, query); break; -- cgit v1.2.1 From aaed93e1265959bb06c4759e68537788c9446681 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 24 Jul 2009 21:54:59 +0100 Subject: kate: use GST_ELEMENT_ERROR for error reporting See #525743. --- ext/kate/gstkatedec.c | 11 ++++++----- ext/kate/gstkateenc.c | 40 ++++++++++++++++++++++++++-------------- ext/kate/gstkateparse.c | 12 ++++++------ ext/kate/gstkatespu.c | 23 +++++++++++++++-------- ext/kate/gstkatetag.c | 2 -- ext/kate/gstkatetiger.c | 2 -- ext/kate/gstkateutil.c | 4 ++-- 7 files changed, 55 insertions(+), 39 deletions(-) (limited to 'ext') diff --git a/ext/kate/gstkatedec.c b/ext/kate/gstkatedec.c index fe5ac51c..eb4bb2ba 100644 --- a/ext/kate/gstkatedec.c +++ b/ext/kate/gstkatedec.c @@ -74,8 +74,6 @@ * */ -/* FIXME: post appropriate GST_ELEMENT_ERROR when returning FLOW_ERROR */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -298,7 +296,8 @@ gst_kate_dec_chain (GstPad * pad, GstBuffer * buf) gst_flow_get_name (rflow)); } } else { - GST_WARNING_OBJECT (kd, "failed to create buffer"); + GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL), + ("Failed to create buffer")); rflow = GST_FLOW_ERROR; } } else { @@ -307,7 +306,8 @@ gst_kate_dec_chain (GstPad * pad, GstBuffer * buf) } g_free (escaped); } else { - GST_WARNING_OBJECT (kd, "failed to allocate string"); + GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL), + ("Failed to allocate string")); rflow = GST_FLOW_ERROR; } @@ -329,7 +329,8 @@ gst_kate_dec_chain (GstPad * pad, GstBuffer * buf) gst_flow_get_name (rflow)); } } else { - GST_WARNING_OBJECT (kd, "failed to create SPU from paletted bitmap"); + GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL), + ("failed to create SPU from paletted bitmap")); rflow = GST_FLOW_ERROR; } } diff --git a/ext/kate/gstkateenc.c b/ext/kate/gstkateenc.c index 80709eb3..fd412d34 100644 --- a/ext/kate/gstkateenc.c +++ b/ext/kate/gstkateenc.c @@ -68,7 +68,6 @@ * */ -/* FIXME: post appropriate GST_ELEMENT_ERROR when returning FLOW_ERROR */ /* FIXME: should we automatically pick up the language code from the * upstream event tags if none was set via the property? */ @@ -457,7 +456,8 @@ gst_kate_enc_push_and_free_kate_packet (GstKateEnc * ke, kate_packet * kp, buffer = gst_kate_enc_create_buffer (ke, kp, granpos, timestamp, duration, header); if (G_UNLIKELY (!buffer)) { - GST_WARNING_OBJECT (ke, "Failed to create buffer, %u bytes", kp->nbytes); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("Failed to create buffer, %u bytes", kp->nbytes)); kate_packet_clear (kp); return GST_FLOW_ERROR; } @@ -561,7 +561,8 @@ gst_kate_enc_send_headers (GstKateEnc * ke) GST_LOG_OBJECT (ke, "Last header encoded"); break; } else { - GST_LOG_OBJECT (ke, "Error encoding header: %d", ret); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("kate_encode_headers: %d", ret)); rflow = GST_FLOW_ERROR; break; } @@ -634,7 +635,8 @@ gst_kate_enc_chain_push_packet (GstKateEnc * ke, kate_packet * kp, granpos = kate_encode_get_granule (&ke->k); if (G_UNLIKELY (granpos < 0)) { - GST_WARNING_OBJECT (ke, "Negative granpos for packet"); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("Negative granpos for packet")); kate_packet_clear (kp); return GST_FLOW_ERROR; } @@ -685,6 +687,8 @@ gst_kate_enc_flush_waiting (GstKateEnc * ke, GstClockTime now) ret = kate_encode_text (&ke->k, t0, t1, "", 0, &kp); if (G_UNLIKELY (ret < 0)) { + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("kate_encode_text: %d", ret)); rflow = GST_FLOW_ERROR; } else { rflow = @@ -747,7 +751,7 @@ gst_kate_enc_chain_spu (GstKateEnc * ke, GstBuffer * buf) g_free (kbitmap); if (kpalette) g_free (kpalette); - GST_ERROR_OBJECT (ke, "Out of memory"); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), ("Out of memory")); return GST_FLOW_ERROR; } @@ -802,17 +806,20 @@ gst_kate_enc_chain_spu (GstKateEnc * ke, GstBuffer * buf) kbitmap->width, kbitmap->height, GST_BUFFER_SIZE (buf), t0, t1); ret = kate_encode_set_region (&ke->k, kregion); if (G_UNLIKELY (ret < 0)) { - GST_WARNING_OBJECT (ke, "Failed to set event region (%d)", ret); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("kate_encode_set_region: %d", ret)); rflow = GST_FLOW_ERROR; } else { ret = kate_encode_set_palette (&ke->k, kpalette); if (G_UNLIKELY (ret < 0)) { - GST_WARNING_OBJECT (ke, "Failed to set event palette (%d)", ret); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("kate_encode_set_palette: %d", ret)); rflow = GST_FLOW_ERROR; } else { ret = kate_encode_set_bitmap (&ke->k, kbitmap); if (G_UNLIKELY (ret < 0)) { - GST_WARNING_OBJECT (ke, "Failed to set event bitmap (%d)", ret); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("kate_encode_set_bitmap: %d", ret)); rflow = GST_FLOW_ERROR; } else { /* Some SPUs have no hide time - so I'm going to delay the encoding of the packet @@ -833,8 +840,8 @@ gst_kate_enc_chain_spu (GstKateEnc * ke, GstBuffer * buf) } else { ret = kate_encode_text (&ke->k, t0, t1, "", 0, &kp); if (G_UNLIKELY (ret < 0)) { - GST_WARNING_OBJECT (ke, - "Failed to encode empty text for SPU buffer (%d)", ret); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("Failed to encode empty text for SPU buffer: %d", ret)); rflow = GST_FLOW_ERROR; } else { rflow = @@ -875,7 +882,8 @@ gst_kate_enc_chain_text (GstKateEnc * ke, GstBuffer * buf, } if (G_UNLIKELY (ret < 0)) { - GST_WARNING_OBJECT (ke, "Failed to set markup type (%d)", ret); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("kate_encode_set_markup_type: %d", ret)); rflow = GST_FLOW_ERROR; } else { const char *text = (const char *) GST_BUFFER_DATA (buf); @@ -888,13 +896,17 @@ gst_kate_enc_chain_text (GstKateEnc * ke, GstBuffer * buf, GST_BUFFER_SIZE (buf), t0, t1); ret = kate_encode_text (&ke->k, t0, t1, text, text_len, &kp); if (G_UNLIKELY (ret < 0)) { + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("Failed to encode text: %d", ret)); rflow = GST_FLOW_ERROR; } else { rflow = gst_kate_enc_chain_push_packet (ke, &kp, start, stop - start + 1); } } else { - GST_WARNING_OBJECT (ke, "No text in text packet"); + /* FIXME: this should not be an error, we should ignore it and move on */ + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("no text in text packet")); rflow = GST_FLOW_ERROR; } } @@ -918,8 +930,8 @@ gst_kate_enc_chain (GstPad * pad, GstBuffer * buf) /* get the type of the data we're being sent */ caps = GST_PAD_CAPS (pad); if (G_UNLIKELY (caps == NULL)) { - GST_ERROR_OBJECT (ke, ": Could not get caps of pad"); - rflow = GST_FLOW_ERROR; + GST_WARNING_OBJECT (ke, "No input caps set"); + rflow = GST_FLOW_NOT_NEGOTIATED; } else { const GstStructure *structure = gst_caps_get_structure (caps, 0); if (structure) diff --git a/ext/kate/gstkateparse.c b/ext/kate/gstkateparse.c index 89d17514..e9bcd5ae 100644 --- a/ext/kate/gstkateparse.c +++ b/ext/kate/gstkateparse.c @@ -58,8 +58,6 @@ * */ -/* FIXME: post appropriate GST_ELEMENT_ERROR when returning FLOW_ERROR */ - #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -174,7 +172,8 @@ gst_kate_parse_push_headers (GstKateParse * parse) gst_pad_get_negotiated_caps (parse->sinkpad), parse->streamheader); if (G_UNLIKELY (!caps)) { - GST_ERROR_OBJECT (parse, "Failed to set headers on caps"); + GST_ELEMENT_ERROR (parse, STREAM, DECODE, (NULL), + ("Failed to set headers on caps")); return GST_FLOW_ERROR; } @@ -182,8 +181,8 @@ gst_kate_parse_push_headers (GstKateParse * parse) res = gst_pad_set_caps (parse->srcpad, caps); gst_caps_unref (caps); if (G_UNLIKELY (!res)) { - GST_WARNING_OBJECT (parse, "Failed to set pad caps"); - return GST_FLOW_ERROR; + GST_WARNING_OBJECT (parse->srcpad, "Failed to set caps on source pad"); + return GST_FLOW_NOT_NEGOTIATED; } headers = parse->streamheader; @@ -343,7 +342,8 @@ gst_kate_parse_queue_buffer (GstKateParse * parse, GstBuffer * buf) if (granpos >= 0) { ret = gst_kate_parse_drain_queue (parse, granpos); } else { - GST_WARNING_OBJECT (parse, "granulepos < 0 (%lld)", granpos); + GST_ELEMENT_ERROR (parse, STREAM, DECODE, (NULL), + ("Bad granulepos %" G_GINT64_FORMAT, granpos)); ret = GST_FLOW_ERROR; } #endif diff --git a/ext/kate/gstkatespu.c b/ext/kate/gstkatespu.c index f05ae421..760662c4 100644 --- a/ext/kate/gstkatespu.c +++ b/ext/kate/gstkatespu.c @@ -253,7 +253,8 @@ gst_kate_spu_crop_bitmap (GstKateEnc * ke, kate_bitmap * kb, guint16 * dx, kb->height = h; } -#define CHECK(x) do { guint16 _ = (x); if (G_UNLIKELY((_) > sz)) { GST_WARNING_OBJECT (ke, "SPU overflow"); return GST_FLOW_ERROR; } } while (0) +/* FIXME: fix macros */ +#define CHECK(x) do { guint16 _ = (x); if (G_UNLIKELY((_) > sz)) { GST_ELEMENT_ERROR (ke, STREAM, ENCODE, ("Attempt to read outide buffer"), (NULL)); return GST_FLOW_ERROR; } } while (0) #define ADVANCE(x) do { guint16 _ = (x); ptr += (_); sz -= (_); } while (0) #define IGNORE(x) do { guint16 __ = (x); CHECK (__); ADVANCE (__); } while (0) @@ -267,8 +268,9 @@ gst_kate_spu_decode_command_sequence (GstKateEnc * ke, GstBuffer * buf, guint16 sz; if (command_sequence_offset >= GST_BUFFER_SIZE (buf)) { - GST_WARNING_OBJECT (ke, "Command sequence offset %u is out of range %u", - command_sequence_offset, GST_BUFFER_SIZE (buf)); + GST_ELEMENT_ERROR (ke, STREAM, DECODE, (NULL), + ("Command sequence offset %u is out of range %u", + command_sequence_offset, GST_BUFFER_SIZE (buf))); return GST_FLOW_ERROR; } @@ -348,10 +350,12 @@ gst_kate_spu_decode_command_sequence (GstKateEnc * ke, GstBuffer * buf, } break; default: - GST_WARNING_OBJECT (ke, "invalid SPU command: %u", cmd); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("Invalid SPU command: %u", cmd)); return GST_FLOW_ERROR; } } + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), ("Error parsing SPU")); return GST_FLOW_ERROR; } @@ -395,9 +399,10 @@ gst_kate_spu_create_spu_palette (GstKateEnc * ke, kate_palette * kp) kate_palette_init (kp); kp->ncolors = 4; kp->colors = (kate_color *) g_malloc (kp->ncolors * sizeof (kate_color)); - if (G_UNLIKELY (!kp->colors)) + if (G_UNLIKELY (!kp->colors)) { + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), ("Out of memory")); return GST_FLOW_ERROR; - + } #if 1 for (n = 0; n < kp->ncolors; ++n) { int idx = ke->spu_colormap[n]; @@ -478,7 +483,8 @@ gst_kate_spu_decode_spu (GstKateEnc * ke, GstBuffer * buf, kate_region * kr, if (G_UNLIKELY (ke->spu_right - ke->spu_left < 0 || ke->spu_bottom - ke->spu_top < 0 || ke->spu_pix_data[0] == 0 || ke->spu_pix_data[1] == 0)) { - GST_WARNING_OBJECT (ke, "SPU area is empty, nothing to encode"); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("SPU area is empty, nothing to encode")); return GST_FLOW_ERROR; } @@ -495,7 +501,8 @@ gst_kate_spu_decode_spu (GstKateEnc * ke, GstBuffer * buf, kate_region * kr, kb->type = kate_bitmap_type_paletted; kb->pixels = (unsigned char *) g_malloc (kb->width * kb->height); if (G_UNLIKELY (!kb->pixels)) { - GST_WARNING_OBJECT (ke, "Failed to allocate memory for pixel data"); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("Failed to allocate memory for pixel data")); return GST_FLOW_ERROR; } diff --git a/ext/kate/gstkatetag.c b/ext/kate/gstkatetag.c index f5734c12..ac4e925f 100644 --- a/ext/kate/gstkatetag.c +++ b/ext/kate/gstkatetag.c @@ -63,8 +63,6 @@ * */ -/* FIXME: post appropriate GST_ELEMENT_ERROR when returning FLOW_ERROR */ - #ifdef HAVE_CONFIG_H # include "config.h" #endif diff --git a/ext/kate/gstkatetiger.c b/ext/kate/gstkatetiger.c index 3e72285a..433a3106 100644 --- a/ext/kate/gstkatetiger.c +++ b/ext/kate/gstkatetiger.c @@ -73,8 +73,6 @@ * */ -/* FIXME: post appropriate GST_ELEMENT_ERROR when returning FLOW_ERROR */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ext/kate/gstkateutil.c b/ext/kate/gstkateutil.c index ca245f99..f99247be 100644 --- a/ext/kate/gstkateutil.c +++ b/ext/kate/gstkateutil.c @@ -17,7 +17,6 @@ * Boston, MA 02111-1307, USA. */ -/* FIXME: post appropriate GST_ELEMENT_ERROR when returning FLOW_ERROR */ /* FIXME: shouldn't all this GstKateDecoderBase stuff really be a base class? */ #ifdef HAVE_CONFIG_H @@ -165,7 +164,8 @@ gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, kate_packet_wrap (&kp, GST_BUFFER_SIZE (buf), GST_BUFFER_DATA (buf)); ret = kate_high_decode_packetin (&decoder->k, &kp, ev); if (G_UNLIKELY (ret < 0)) { - GST_WARNING_OBJECT (element, "kate_high_decode_packetin failed (%d)", ret); + GST_ELEMENT_ERROR (element, STREAM, DECODE, (NULL), + ("Failed to decode Kate packet: %d", ret)); return GST_FLOW_ERROR; } else if (G_UNLIKELY (ret > 0)) { GST_DEBUG_OBJECT (element, -- cgit v1.2.1 From a20d86f1f525f79779727972925283910dc4173d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sat, 25 Jul 2009 12:19:07 +0100 Subject: kate: break up macros into multiple lines --- ext/kate/gstkatespu.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'ext') diff --git a/ext/kate/gstkatespu.c b/ext/kate/gstkatespu.c index 760662c4..cc54cc4f 100644 --- a/ext/kate/gstkatespu.c +++ b/ext/kate/gstkatespu.c @@ -253,10 +253,21 @@ gst_kate_spu_crop_bitmap (GstKateEnc * ke, kate_bitmap * kb, guint16 * dx, kb->height = h; } -/* FIXME: fix macros */ -#define CHECK(x) do { guint16 _ = (x); if (G_UNLIKELY((_) > sz)) { GST_ELEMENT_ERROR (ke, STREAM, ENCODE, ("Attempt to read outide buffer"), (NULL)); return GST_FLOW_ERROR; } } while (0) -#define ADVANCE(x) do { guint16 _ = (x); ptr += (_); sz -= (_); } while (0) -#define IGNORE(x) do { guint16 __ = (x); CHECK (__); ADVANCE (__); } while (0) +#define CHECK(x) G_STMT_START { \ + guint16 _ = (x); \ + if (G_UNLIKELY((_) > sz)) { \ + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), ("Read outside buffer")); \ + return GST_FLOW_ERROR; \ + } \ + } G_STMT_END +#define ADVANCE(x) G_STMT_START { \ + guint16 _ = (x); ptr += (_); sz -= (_); \ + } G_STMT_END +#define IGNORE(x) G_STMT_START { \ + guint16 __ = (x); \ + CHECK (__); \ + ADVANCE (__); \ + } G_STMT_END static GstFlowReturn gst_kate_spu_decode_command_sequence (GstKateEnc * ke, GstBuffer * buf, -- cgit v1.2.1 From 29e39080324a4a5a3b4d5bbc3fc79213090c6b0a Mon Sep 17 00:00:00 2001 From: Iago Toral Date: Mon, 27 Jul 2009 19:55:27 +0200 Subject: amrwb: Remove AMR-WB parser and decoder and rename encoder plugin from amrwb to amrwbenc Partially fixes bug #584890. --- ext/Makefile.am | 4 +- ext/amrwb/Makefile.am | 18 - ext/amrwb/README | 12 - ext/amrwb/gstamrwb.c | 45 --- ext/amrwb/gstamrwbdec.c | 372 --------------------- ext/amrwb/gstamrwbdec.h | 78 ----- ext/amrwb/gstamrwbenc.c | 372 --------------------- ext/amrwb/gstamrwbenc.h | 70 ---- ext/amrwb/gstamrwbparse.c | 767 ------------------------------------------- ext/amrwb/gstamrwbparse.h | 77 ----- ext/amrwbenc/GstAmrwbEnc.prs | 11 + ext/amrwbenc/Makefile.am | 19 ++ ext/amrwbenc/README | 12 + ext/amrwbenc/gstamrwb.c | 39 +++ ext/amrwbenc/gstamrwbenc.c | 385 ++++++++++++++++++++++ ext/amrwbenc/gstamrwbenc.h | 70 ++++ 16 files changed, 538 insertions(+), 1813 deletions(-) delete mode 100644 ext/amrwb/Makefile.am delete mode 100644 ext/amrwb/README delete mode 100644 ext/amrwb/gstamrwb.c delete mode 100644 ext/amrwb/gstamrwbdec.c delete mode 100644 ext/amrwb/gstamrwbdec.h delete mode 100644 ext/amrwb/gstamrwbenc.c delete mode 100644 ext/amrwb/gstamrwbenc.h delete mode 100644 ext/amrwb/gstamrwbparse.c delete mode 100644 ext/amrwb/gstamrwbparse.h create mode 100644 ext/amrwbenc/GstAmrwbEnc.prs create mode 100644 ext/amrwbenc/Makefile.am create mode 100644 ext/amrwbenc/README create mode 100644 ext/amrwbenc/gstamrwb.c create mode 100644 ext/amrwbenc/gstamrwbenc.c create mode 100644 ext/amrwbenc/gstamrwbenc.h (limited to 'ext') diff --git a/ext/Makefile.am b/ext/Makefile.am index fafa1146..e2e4cc52 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -11,7 +11,7 @@ ASSRENDER_DIR = endif if USE_AMRWB -AMRWB_DIR = amrwb +AMRWB_DIR = amrwbenc else AMRWB_DIR = endif @@ -402,7 +402,7 @@ SUBDIRS=\ DIST_SUBDIRS = \ alsaspdif \ - amrwb \ + amrwbenc \ assrender \ apexsink \ bz2 \ diff --git a/ext/amrwb/Makefile.am b/ext/amrwb/Makefile.am deleted file mode 100644 index c4060d7f..00000000 --- a/ext/amrwb/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -plugin_LTLIBRARIES = libgstamrwb.la - -libgstamrwb_la_SOURCES = \ - gstamrwb.c \ - gstamrwbdec.c \ - gstamrwbenc.c \ - gstamrwbparse.c - -libgstamrwb_la_CFLAGS = $(GST_CFLAGS) $(AMRWB_CFLAGS) -libgstamrwb_la_LIBADD = $(GST_BASE_LIBS) $(AMRWB_LIBS) -libgstamrwb_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstamrwb_la_LIBTOOLFLAGS = --tag=disable-static - -noinst_HEADERS = \ - gstamrwbdec.h \ - gstamrwbenc.h \ - gstamrwbparse.h - diff --git a/ext/amrwb/README b/ext/amrwb/README deleted file mode 100644 index 82d33715..00000000 --- a/ext/amrwb/README +++ /dev/null @@ -1,12 +0,0 @@ -Compiling AMRWB codec: -====================== - -To compile the amrwb codec, you need to download the source code from -"http://www.3gpp.org/ftp/Specs/html-info/26204.htm" and uncompress the -files inside an amrwb-code directory; - -or execute this commands: -$cd amrwb-code -$sh ./run.sh - -and run the "autogen" script again. diff --git a/ext/amrwb/gstamrwb.c b/ext/amrwb/gstamrwb.c deleted file mode 100644 index fd47b371..00000000 --- a/ext/amrwb/gstamrwb.c +++ /dev/null @@ -1,45 +0,0 @@ -/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin - * Copyright (C) 2006 Edgard Lima - * - * 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstamrwbdec.h" -#include "gstamrwbenc.h" -#include "gstamrwbparse.h" - -static gboolean -plugin_init (GstPlugin * plugin) -{ - return gst_element_register (plugin, "amrwbdec", - GST_RANK_PRIMARY, GST_TYPE_AMRWBDEC) && - gst_element_register (plugin, "amrwbparse", - GST_RANK_PRIMARY, GST_TYPE_AMRWBPARSE) && - gst_element_register (plugin, "amrwbenc", - GST_RANK_NONE, GST_TYPE_AMRWBENC); -} - - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "amrwb", - "Adaptive Multi-Rate Wide-Band", - plugin_init, VERSION, GST_LICENSE_UNKNOWN, GST_PACKAGE_NAME, - GST_PACKAGE_ORIGIN); diff --git a/ext/amrwb/gstamrwbdec.c b/ext/amrwb/gstamrwbdec.c deleted file mode 100644 index 54e8a830..00000000 --- a/ext/amrwb/gstamrwbdec.c +++ /dev/null @@ -1,372 +0,0 @@ -/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin - * Copyright (C) 2004 Ronald Bultje - * - * 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. - */ - -/** - * SECTION:element-amrwbdec - * @see_also: #GstAmrwbEnc, #GstAmrwbParse - * - * AMR wideband decoder based on the - * reference codec implementation. - * - * - * Example launch line - * |[ - * gst-launch filesrc location=abc.amr ! amrwbparse ! amrwbdec ! audioresample ! audioconvert ! alsasink - * ]| - * - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstamrwbdec.h" - -static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/AMR-WB, " - "rate = (int) 16000, " "channels = (int) 1") - ); - -static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " - "width = (int) 16, " - "depth = (int) 16, " - "signed = (boolean) TRUE, " - "endianness = (int) BYTE_ORDER, " - "rate = (int) 16000, " "channels = (int) 1") - ); - -GST_DEBUG_CATEGORY_STATIC (gst_amrwbdec_debug); -#define GST_CAT_DEFAULT gst_amrwbdec_debug - -extern const UWord8 block_size[]; - -static gboolean gst_amrwbdec_event (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_amrwbdec_chain (GstPad * pad, GstBuffer * buffer); -static gboolean gst_amrwbdec_setcaps (GstPad * pad, GstCaps * caps); -static GstStateChangeReturn gst_amrwbdec_state_change (GstElement * element, - GstStateChange transition); - -static void gst_amrwbdec_finalize (GObject * object); - -#define _do_init(bla) \ - GST_DEBUG_CATEGORY_INIT (gst_amrwbdec_debug, "amrwbdec", 0, "AMR-WB audio decoder"); - -GST_BOILERPLATE_FULL (GstAmrwbDec, gst_amrwbdec, GstElement, GST_TYPE_ELEMENT, - _do_init); - -static void -gst_amrwbdec_base_init (gpointer klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - GstElementDetails details = GST_ELEMENT_DETAILS ("AMR-WB audio decoder", - "Codec/Decoder/Audio", - "Adaptive Multi-Rate Wideband audio decoder", - "Renato Araujo "); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_template)); - - gst_element_class_set_details (element_class, &details); -} - -static void -gst_amrwbdec_class_init (GstAmrwbDecClass * klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - - object_class->finalize = gst_amrwbdec_finalize; - - element_class->change_state = GST_DEBUG_FUNCPTR (gst_amrwbdec_state_change); -} - -static void -gst_amrwbdec_init (GstAmrwbDec * amrwbdec, GstAmrwbDecClass * klass) -{ - /* create the sink pad */ - amrwbdec->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink"); - gst_pad_set_setcaps_function (amrwbdec->sinkpad, gst_amrwbdec_setcaps); - gst_pad_set_event_function (amrwbdec->sinkpad, gst_amrwbdec_event); - gst_pad_set_chain_function (amrwbdec->sinkpad, gst_amrwbdec_chain); - gst_element_add_pad (GST_ELEMENT (amrwbdec), amrwbdec->sinkpad); - - /* create the src pad */ - amrwbdec->srcpad = gst_pad_new_from_static_template (&src_template, "src"); - gst_pad_use_fixed_caps (amrwbdec->srcpad); - gst_element_add_pad (GST_ELEMENT (amrwbdec), amrwbdec->srcpad); - - amrwbdec->adapter = gst_adapter_new (); - - /* init rest */ - amrwbdec->handle = NULL; - amrwbdec->channels = 0; - amrwbdec->rate = 0; - amrwbdec->duration = 0; - amrwbdec->ts = -1; -} - -static void -gst_amrwbdec_finalize (GObject * object) -{ - GstAmrwbDec *amrwbdec; - - amrwbdec = GST_AMRWBDEC (object); - - gst_adapter_clear (amrwbdec->adapter); - g_object_unref (amrwbdec->adapter); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static gboolean -gst_amrwbdec_setcaps (GstPad * pad, GstCaps * caps) -{ - GstStructure *structure; - GstAmrwbDec *amrwbdec; - GstCaps *copy; - - amrwbdec = GST_AMRWBDEC (gst_pad_get_parent (pad)); - - structure = gst_caps_get_structure (caps, 0); - - /* get channel count */ - gst_structure_get_int (structure, "channels", &amrwbdec->channels); - gst_structure_get_int (structure, "rate", &amrwbdec->rate); - - /* create reverse caps */ - copy = gst_caps_new_simple ("audio/x-raw-int", - "channels", G_TYPE_INT, amrwbdec->channels, - "width", G_TYPE_INT, 16, - "depth", G_TYPE_INT, 16, - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "rate", G_TYPE_INT, amrwbdec->rate, "signed", G_TYPE_BOOLEAN, TRUE, NULL); - - amrwbdec->duration = gst_util_uint64_scale_int (GST_SECOND, L_FRAME16k, - amrwbdec->rate * amrwbdec->channels); - - gst_pad_set_caps (amrwbdec->srcpad, copy); - gst_caps_unref (copy); - - gst_object_unref (amrwbdec); - - return TRUE; -} - -static gboolean -gst_amrwbdec_event (GstPad * pad, GstEvent * event) -{ - GstAmrwbDec *amrwbdec; - gboolean ret = TRUE; - - amrwbdec = GST_AMRWBDEC (gst_pad_get_parent (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - ret = gst_pad_push_event (amrwbdec->srcpad, event); - break; - case GST_EVENT_FLUSH_STOP: - ret = gst_pad_push_event (amrwbdec->srcpad, event); - gst_adapter_clear (amrwbdec->adapter); - amrwbdec->ts = -1; - break; - case GST_EVENT_EOS: - gst_adapter_clear (amrwbdec->adapter); - ret = gst_pad_push_event (amrwbdec->srcpad, event); - break; - case GST_EVENT_NEWSEGMENT: - { - GstFormat format; - gdouble rate, arate; - gint64 start, stop, time; - gboolean update; - - gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, - &start, &stop, &time); - - /* we need time for now */ - if (format != GST_FORMAT_TIME) - goto newseg_wrong_format; - - GST_DEBUG_OBJECT (amrwbdec, - "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT - ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, - update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), - GST_TIME_ARGS (time)); - - /* now configure the values */ - gst_segment_set_newsegment_full (&amrwbdec->segment, update, - rate, arate, format, start, stop, time); - ret = gst_pad_push_event (amrwbdec->srcpad, event); - } - break; - default: - ret = gst_pad_push_event (amrwbdec->srcpad, event); - break; - } -done: - gst_object_unref (amrwbdec); - - return ret; - - /* ERRORS */ -newseg_wrong_format: - { - GST_DEBUG_OBJECT (amrwbdec, "received non TIME newsegment"); - goto done; - } -} - -static GstFlowReturn -gst_amrwbdec_chain (GstPad * pad, GstBuffer * buffer) -{ - GstAmrwbDec *amrwbdec; - GstFlowReturn ret = GST_FLOW_OK; - - amrwbdec = GST_AMRWBDEC (gst_pad_get_parent (pad)); - - if (amrwbdec->rate == 0 || amrwbdec->channels == 0) - goto not_negotiated; - - /* discontinuity, don't combine samples before and after the - * DISCONT */ - if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) { - gst_adapter_clear (amrwbdec->adapter); - amrwbdec->ts = -1; - amrwbdec->discont = TRUE; - } - - /* take latest timestamp, FIXME timestamp is the one of the - * first buffer in the adapter. */ - if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) - amrwbdec->ts = GST_BUFFER_TIMESTAMP (buffer); - - gst_adapter_push (amrwbdec->adapter, buffer); - - while (TRUE) { - GstBuffer *out; - const guint8 *data; - gint block, mode; - - /* need to peek data to get the size */ - if (gst_adapter_available (amrwbdec->adapter) < 1) - break; - data = gst_adapter_peek (amrwbdec->adapter, 1); - - /* get size */ - mode = (data[0] >> 3) & 0x0F; - block = block_size[mode]; - - GST_DEBUG_OBJECT (amrwbdec, "mode %d, block %d", mode, block); - - if (!block || gst_adapter_available (amrwbdec->adapter) < block) - break; - - /* the library seems to write into the source data, hence the copy. */ - data = gst_adapter_take (amrwbdec->adapter, block); - - /* get output */ - out = gst_buffer_new_and_alloc (sizeof (gint16) * L_FRAME16k); - - GST_BUFFER_DURATION (out) = amrwbdec->duration; - GST_BUFFER_TIMESTAMP (out) = amrwbdec->ts; - - if (amrwbdec->ts != -1) - amrwbdec->ts += amrwbdec->duration; - if (amrwbdec->discont) { - GST_BUFFER_FLAG_SET (out, GST_BUFFER_FLAG_DISCONT); - amrwbdec->discont = FALSE; - } - - gst_buffer_set_caps (out, GST_PAD_CAPS (amrwbdec->srcpad)); - - /* decode */ - D_IF_decode (amrwbdec->handle, (UWord8 *) data, - (Word16 *) GST_BUFFER_DATA (out), _good_frame); - - g_free ((gpointer) data); - - /* send out */ - ret = gst_pad_push (amrwbdec->srcpad, out); - } - - gst_object_unref (amrwbdec); - return ret; - - /* ERRORS */ -not_negotiated: - { - GST_ELEMENT_ERROR (amrwbdec, STREAM, TYPE_NOT_FOUND, (NULL), - ("Decoder is not initialized")); - gst_object_unref (amrwbdec); - return GST_FLOW_NOT_NEGOTIATED; - } -} - -static GstStateChangeReturn -gst_amrwbdec_state_change (GstElement * element, GstStateChange transition) -{ - GstAmrwbDec *amrwbdec; - GstStateChangeReturn ret; - - amrwbdec = GST_AMRWBDEC (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - if (!(amrwbdec->handle = D_IF_init ())) - goto init_failed; - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_adapter_clear (amrwbdec->adapter); - amrwbdec->rate = 0; - amrwbdec->channels = 0; - amrwbdec->ts = -1; - amrwbdec->discont = TRUE; - gst_segment_init (&amrwbdec->segment, GST_FORMAT_TIME); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_NULL: - D_IF_exit (amrwbdec->handle); - break; - default: - break; - } - - return ret; - - /* ERRORS */ -init_failed: - { - GST_ELEMENT_ERROR (amrwbdec, LIBRARY, INIT, (NULL), - ("Failed to open AMR Decoder")); - return GST_STATE_CHANGE_FAILURE; - } -} diff --git a/ext/amrwb/gstamrwbdec.h b/ext/amrwb/gstamrwbdec.h deleted file mode 100644 index a7a5ce71..00000000 --- a/ext/amrwb/gstamrwbdec.h +++ /dev/null @@ -1,78 +0,0 @@ -/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin - * Copyright (C) 2006 Edgard Lima - * - * 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. - */ - -#ifndef __GST_AMRWBDEC_H__ -#define __GST_AMRWBDEC_H__ - -#include -#include -#include -#include -#include - -G_BEGIN_DECLS - -#define GST_TYPE_AMRWBDEC \ - (gst_amrwbdec_get_type()) -#define GST_AMRWBDEC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRWBDEC, GstAmrwbDec)) -#define GST_AMRWBDEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRWBDEC, GstAmrwbDecClass)) -#define GST_IS_AMRWBDEC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRWBDEC)) -#define GST_IS_AMRWBDEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRWBDEC)) - -typedef struct _GstAmrwbDec GstAmrwbDec; -typedef struct _GstAmrwbDecClass GstAmrwbDecClass; - -/** - * GstAmrwbDec: - * - * Opaque data structure. - */ -struct _GstAmrwbDec { - GstElement element; - - /* pads */ - GstPad *sinkpad, *srcpad; - guint64 ts; - - GstAdapter *adapter; - - /* library handle */ - void *handle; - - /* output settings */ - gint channels, rate; - gint duration; - - GstSegment segment; - gboolean discont; -}; - -struct _GstAmrwbDecClass { - GstElementClass parent_class; -}; - -GType gst_amrwbdec_get_type (void); - -G_END_DECLS - -#endif /* __GST_AMRWBDEC_H__ */ diff --git a/ext/amrwb/gstamrwbenc.c b/ext/amrwb/gstamrwbenc.c deleted file mode 100644 index caf2adb7..00000000 --- a/ext/amrwb/gstamrwbenc.c +++ /dev/null @@ -1,372 +0,0 @@ -/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin - * Copyright (C) 2006 Edgard Lima - * - * 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. - */ - -/** - * SECTION:element-amrwbenc - * @see_also: #GstAmrwbDec, #GstAmrwbParse - * - * AMR wideband encoder based on the - * reference codec implementation. - * - * - * Example launch line - * |[ - * gst-launch filesrc location=abc.wav ! wavparse ! audioresample ! audioconvert ! amrwbenc ! filesink location=abc.amr - * ]| - * Please not that the above stream misses the header, that is needed to play - * the stream. - * - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstamrwbenc.h" - -/* these defines are not in all .h files */ -#ifndef MR660 -#define MR660 0 -#define MR885 1 -#define MR1265 2 -#define MR1425 2 -#define MR1585 3 -#define MR1825 4 -#define MR1985 5 -#define MR2305 6 -#define MR2385 7 -#define MRDTX 8 -#endif - -static GType -gst_amrwbenc_bandmode_get_type () -{ - static GType gst_amrwbenc_bandmode_type = 0; - static GEnumValue gst_amrwbenc_bandmode[] = { - {MR660, "MR660", "MR660"}, - {MR885, "MR885", "MR885"}, - {MR1265, "MR1265", "MR1265"}, - {MR1425, "MR1425", "MR1425"}, - {MR1585, "MR1585", "MR1585"}, - {MR1825, "MR1825", "MR1825"}, - {MR1985, "MR1985", "MR1985"}, - {MR2305, "MR2305", "MR2305"}, - {MR2385, "MR2385", "MR2385"}, - {MRDTX, "MRDTX", "MRDTX"}, - {0, NULL, NULL}, - }; - if (!gst_amrwbenc_bandmode_type) { - gst_amrwbenc_bandmode_type = - g_enum_register_static ("GstAmrwbEncBandMode", gst_amrwbenc_bandmode); - } - return gst_amrwbenc_bandmode_type; -} - -#define GST_AMRWBENC_BANDMODE_TYPE (gst_amrwbenc_bandmode_get_type()) - -#define BANDMODE_DEFAULT MR660 - -enum -{ - PROP_0, - PROP_BANDMODE -}; - -static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " - "width = (int) 16, " - "depth = (int) 16, " - "signed = (boolean) TRUE, " - "endianness = (int) BYTE_ORDER, " - "rate = (int) 16000, " "channels = (int) 1") - ); - -static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/AMR-WB, " - "rate = (int) 16000, " "channels = (int) 1") - ); - -GST_DEBUG_CATEGORY_STATIC (gst_amrwbenc_debug); -#define GST_CAT_DEFAULT gst_amrwbenc_debug - -static void gst_amrwbenc_finalize (GObject * object); - -static GstFlowReturn gst_amrwbenc_chain (GstPad * pad, GstBuffer * buffer); -static gboolean gst_amrwbenc_setcaps (GstPad * pad, GstCaps * caps); -static GstStateChangeReturn gst_amrwbenc_state_change (GstElement * element, - GstStateChange transition); - -#define _do_init(bla) \ - GST_DEBUG_CATEGORY_INIT (gst_amrwbenc_debug, "amrwbenc", 0, "AMR-WB audio encoder"); - -GST_BOILERPLATE_FULL (GstAmrwbEnc, gst_amrwbenc, GstElement, GST_TYPE_ELEMENT, - _do_init); - -static void -gst_amrwbenc_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstAmrwbEnc *self = GST_AMRWBENC (object); - - switch (prop_id) { - case PROP_BANDMODE: - self->bandmode = g_value_get_enum (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - - return; -} - -static void -gst_amrwbenc_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstAmrwbEnc *self = GST_AMRWBENC (object); - - switch (prop_id) { - case PROP_BANDMODE: - g_value_set_enum (value, self->bandmode); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - - return; -} - -static void -gst_amrwbenc_base_init (gpointer klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - GstElementDetails details = GST_ELEMENT_DETAILS ("AMR-WB audio encoder", - "Codec/Encoder/Audio", - "Adaptive Multi-Rate Wideband audio encoder", - "Renato Araujo "); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_template)); - - gst_element_class_set_details (element_class, &details); -} - -static void -gst_amrwbenc_class_init (GstAmrwbEncClass * klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - - object_class->finalize = gst_amrwbenc_finalize; - object_class->set_property = gst_amrwbenc_set_property; - object_class->get_property = gst_amrwbenc_get_property; - - g_object_class_install_property (object_class, PROP_BANDMODE, - g_param_spec_enum ("band-mode", "Band Mode", - "Encoding Band Mode (Kbps)", GST_AMRWBENC_BANDMODE_TYPE, - BANDMODE_DEFAULT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); - - element_class->change_state = GST_DEBUG_FUNCPTR (gst_amrwbenc_state_change); -} - -static void -gst_amrwbenc_init (GstAmrwbEnc * amrwbenc, GstAmrwbEncClass * klass) -{ - /* create the sink pad */ - amrwbenc->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink"); - gst_pad_set_setcaps_function (amrwbenc->sinkpad, gst_amrwbenc_setcaps); - gst_pad_set_chain_function (amrwbenc->sinkpad, gst_amrwbenc_chain); - gst_element_add_pad (GST_ELEMENT (amrwbenc), amrwbenc->sinkpad); - - /* create the src pad */ - amrwbenc->srcpad = gst_pad_new_from_static_template (&src_template, "src"); - gst_pad_use_fixed_caps (amrwbenc->srcpad); - gst_element_add_pad (GST_ELEMENT (amrwbenc), amrwbenc->srcpad); - - amrwbenc->adapter = gst_adapter_new (); - - /* init rest */ - amrwbenc->handle = NULL; - amrwbenc->channels = 0; - amrwbenc->rate = 0; - amrwbenc->ts = 0; -} - -static void -gst_amrwbenc_finalize (GObject * object) -{ - GstAmrwbEnc *amrwbenc; - - amrwbenc = GST_AMRWBENC (object); - - g_object_unref (G_OBJECT (amrwbenc->adapter)); - amrwbenc->adapter = NULL; - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static gboolean -gst_amrwbenc_setcaps (GstPad * pad, GstCaps * caps) -{ - GstStructure *structure; - GstAmrwbEnc *amrwbenc; - GstCaps *copy; - - amrwbenc = GST_AMRWBENC (GST_PAD_PARENT (pad)); - - structure = gst_caps_get_structure (caps, 0); - - /* get channel count */ - gst_structure_get_int (structure, "channels", &amrwbenc->channels); - gst_structure_get_int (structure, "rate", &amrwbenc->rate); - - /* this is not wrong but will sound bad */ - if (amrwbenc->channels != 1) { - GST_WARNING ("amrwbdec is only optimized for mono channels"); - } - if (amrwbenc->rate != 16000) { - GST_WARNING ("amrwbdec is only optimized for 16000 Hz samplerate"); - } - - /* create reverse caps */ - copy = gst_caps_new_simple ("audio/AMR-WB", - "channels", G_TYPE_INT, amrwbenc->channels, - "rate", G_TYPE_INT, amrwbenc->rate, NULL); - - gst_pad_set_caps (amrwbenc->srcpad, copy); - gst_caps_unref (copy); - - return TRUE; -} - -static GstFlowReturn -gst_amrwbenc_chain (GstPad * pad, GstBuffer * buffer) -{ - GstAmrwbEnc *amrwbenc; - GstFlowReturn ret = GST_FLOW_OK; - const int buffer_size = sizeof (Word16) * L_FRAME16k; - - amrwbenc = GST_AMRWBENC (gst_pad_get_parent (pad)); - - g_return_val_if_fail (amrwbenc->handle, GST_FLOW_WRONG_STATE); - - if (amrwbenc->rate == 0 || amrwbenc->channels == 0) { - ret = GST_FLOW_NOT_NEGOTIATED; - goto done; - } - - /* discontinuity clears adapter, FIXME, maybe we can set some - * encoder flag to mask the discont. */ - if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) { - gst_adapter_clear (amrwbenc->adapter); - amrwbenc->ts = 0; - amrwbenc->discont = TRUE; - } - - if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) - amrwbenc->ts = GST_BUFFER_TIMESTAMP (buffer); - - ret = GST_FLOW_OK; - gst_adapter_push (amrwbenc->adapter, buffer); - - /* Collect samples until we have enough for an output frame */ - while (gst_adapter_available (amrwbenc->adapter) >= buffer_size) { - GstBuffer *out; - guint8 *data; - gint outsize; - - out = gst_buffer_new_and_alloc (buffer_size); - GST_BUFFER_DURATION (out) = GST_SECOND * L_FRAME16k / - (amrwbenc->rate * amrwbenc->channels); - GST_BUFFER_TIMESTAMP (out) = amrwbenc->ts; - if (amrwbenc->ts != -1) { - amrwbenc->ts += GST_BUFFER_DURATION (out); - } - if (amrwbenc->discont) { - GST_BUFFER_FLAG_SET (out, GST_BUFFER_FLAG_DISCONT); - amrwbenc->discont = FALSE; - } - gst_buffer_set_caps (out, gst_pad_get_caps (amrwbenc->srcpad)); - - data = (guint8 *) gst_adapter_peek (amrwbenc->adapter, buffer_size); - - /* encode */ - outsize = - E_IF_encode (amrwbenc->handle, amrwbenc->bandmode, (Word16 *) data, - (UWord8 *) GST_BUFFER_DATA (out), 0); - - gst_adapter_flush (amrwbenc->adapter, buffer_size); - GST_BUFFER_SIZE (out) = outsize; - - /* play */ - if ((ret = gst_pad_push (amrwbenc->srcpad, out)) != GST_FLOW_OK) - break; - } - -done: - - gst_object_unref (amrwbenc); - return ret; - -} - -static GstStateChangeReturn -gst_amrwbenc_state_change (GstElement * element, GstStateChange transition) -{ - GstAmrwbEnc *amrwbenc; - GstStateChangeReturn ret; - - amrwbenc = GST_AMRWBENC (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - if (!(amrwbenc->handle = E_IF_init ())) - return GST_STATE_CHANGE_FAILURE; - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - amrwbenc->rate = 0; - amrwbenc->channels = 0; - amrwbenc->ts = 0; - amrwbenc->discont = FALSE; - gst_adapter_clear (amrwbenc->adapter); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_NULL: - E_IF_exit (amrwbenc->handle); - break; - default: - break; - } - - return ret; -} diff --git a/ext/amrwb/gstamrwbenc.h b/ext/amrwb/gstamrwbenc.h deleted file mode 100644 index 034a5bed..00000000 --- a/ext/amrwb/gstamrwbenc.h +++ /dev/null @@ -1,70 +0,0 @@ -/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin - * Copyright (C) 2006 Edgard Lima - * - * 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. - */ - -#ifndef __GST_AMRWBENC_H__ -#define __GST_AMRWBENC_H__ - -#include -#include -#include -#include - -G_BEGIN_DECLS - -#define GST_TYPE_AMRWBENC \ - (gst_amrwbenc_get_type()) -#define GST_AMRWBENC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRWBENC, GstAmrwbEnc)) -#define GST_AMRWBENC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRWBENC, GstAmrwbEncClass)) -#define GST_IS_AMRWBENC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRWBENC)) -#define GST_IS_AMRWBENC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRWBENC)) - -typedef struct _GstAmrwbEnc GstAmrwbEnc; -typedef struct _GstAmrwbEncClass GstAmrwbEncClass; - -struct _GstAmrwbEnc { - GstElement element; - - /* pads */ - GstPad *sinkpad, *srcpad; - guint64 ts; - gboolean discont; - - GstAdapter *adapter; - - /* library handle */ - void *handle; - - /* input settings */ - gint bandmode; - gint channels, rate; -}; - -struct _GstAmrwbEncClass { - GstElementClass parent_class; -}; - -GType gst_amrwbenc_get_type (void); - -G_END_DECLS - -#endif /* __GST_AMRWBENC_H__ */ diff --git a/ext/amrwb/gstamrwbparse.c b/ext/amrwb/gstamrwbparse.c deleted file mode 100644 index bcee1704..00000000 --- a/ext/amrwb/gstamrwbparse.c +++ /dev/null @@ -1,767 +0,0 @@ -/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin - * Copyright (C) 2006 Edgard Lima - * - * 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. - */ - -/** - * SECTION:element-amrwbparse - * @see_also: #GstAmrwbDec, #GstAmrwbEnc - * - * This is an AMR wideband parser. - * - * - * Example launch line - * |[ - * gst-launch filesrc location=abc.amr ! amrwbparse ! amrwbdec ! audioresample ! audioconvert ! alsasink - * ]| - * - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include "gstamrwbparse.h" - -static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/AMR-WB, " - "rate = (int) 16000, " "channels = (int) 1") - ); - -static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-amr-wb-sh") - ); - -GST_DEBUG_CATEGORY_STATIC (gst_amrwbparse_debug); -#define GST_CAT_DEFAULT gst_amrwbparse_debug - -extern const UWord8 block_size[]; - -#define AMRWB_HEADER_SIZE 9 -#define AMRWB_HEADER_STR "#!AMR-WB\n" - -static void gst_amrwbparse_base_init (gpointer klass); -static void gst_amrwbparse_class_init (GstAmrwbParseClass * klass); -static void gst_amrwbparse_init (GstAmrwbParse * amrwbparse, - GstAmrwbParseClass * klass); - -static const GstQueryType *gst_amrwbparse_querytypes (GstPad * pad); -static gboolean gst_amrwbparse_query (GstPad * pad, GstQuery * query); - -static gboolean gst_amrwbparse_sink_event (GstPad * pad, GstEvent * event); -static gboolean gst_amrwbparse_src_event (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_amrwbparse_chain (GstPad * pad, GstBuffer * buffer); -static void gst_amrwbparse_loop (GstPad * pad); -static gboolean gst_amrwbparse_sink_activate (GstPad * sinkpad); -static gboolean gst_amrwbparse_sink_activate_pull (GstPad * sinkpad, - gboolean active); -static gboolean gst_amrwbparse_sink_activate_push (GstPad * sinkpad, - gboolean active); -static GstStateChangeReturn gst_amrwbparse_state_change (GstElement * element, - GstStateChange transition); - -static void gst_amrwbparse_finalize (GObject * object); - -#define _do_init(bla) \ - GST_DEBUG_CATEGORY_INIT (gst_amrwbparse_debug, "amrwbparse", 0, "AMR-WB audio stream parser"); - -GST_BOILERPLATE_FULL (GstAmrwbParse, gst_amrwbparse, GstElement, - GST_TYPE_ELEMENT, _do_init); - -static void -gst_amrwbparse_base_init (gpointer klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - GstElementDetails details = GST_ELEMENT_DETAILS ("AMR-WB audio stream parser", - "Codec/Parser/Audio", - "Adaptive Multi-Rate WideBand audio parser", - "Renato Filho "); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_template)); - - gst_element_class_set_details (element_class, &details); - -} - -static void -gst_amrwbparse_class_init (GstAmrwbParseClass * klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - - object_class->finalize = gst_amrwbparse_finalize; - - element_class->change_state = GST_DEBUG_FUNCPTR (gst_amrwbparse_state_change); -} - -static void -gst_amrwbparse_init (GstAmrwbParse * amrwbparse, GstAmrwbParseClass * klass) -{ - /* create the sink pad */ - amrwbparse->sinkpad = - gst_pad_new_from_static_template (&sink_template, "sink"); - gst_pad_set_chain_function (amrwbparse->sinkpad, - GST_DEBUG_FUNCPTR (gst_amrwbparse_chain)); - gst_pad_set_event_function (amrwbparse->sinkpad, - GST_DEBUG_FUNCPTR (gst_amrwbparse_sink_event)); - gst_pad_set_activate_function (amrwbparse->sinkpad, - gst_amrwbparse_sink_activate); - gst_pad_set_activatepull_function (amrwbparse->sinkpad, - gst_amrwbparse_sink_activate_pull); - gst_pad_set_activatepush_function (amrwbparse->sinkpad, - gst_amrwbparse_sink_activate_push); - gst_element_add_pad (GST_ELEMENT (amrwbparse), amrwbparse->sinkpad); - - /* create the src pad */ - amrwbparse->srcpad = gst_pad_new_from_static_template (&src_template, "src"); - gst_pad_set_event_function (amrwbparse->srcpad, - GST_DEBUG_FUNCPTR (gst_amrwbparse_src_event)); - gst_pad_set_query_function (amrwbparse->srcpad, - GST_DEBUG_FUNCPTR (gst_amrwbparse_query)); - gst_pad_set_query_type_function (amrwbparse->srcpad, - GST_DEBUG_FUNCPTR (gst_amrwbparse_querytypes)); - gst_element_add_pad (GST_ELEMENT (amrwbparse), amrwbparse->srcpad); - - amrwbparse->adapter = gst_adapter_new (); - - /* init rest */ - amrwbparse->ts = 0; -} - -static void -gst_amrwbparse_finalize (GObject * object) -{ - GstAmrwbParse *amrwbparse; - - amrwbparse = GST_AMRWBPARSE (object); - - gst_adapter_clear (amrwbparse->adapter); - g_object_unref (amrwbparse->adapter); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - - -static const GstQueryType * -gst_amrwbparse_querytypes (GstPad * pad) -{ - static const GstQueryType list[] = { - GST_QUERY_POSITION, - GST_QUERY_DURATION, - 0 - }; - - return list; -} - -static gboolean -gst_amrwbparse_query (GstPad * pad, GstQuery * query) -{ - GstAmrwbParse *amrwbparse; - gboolean res = TRUE; - - amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - GstFormat format; - gint64 cur; - - gst_query_parse_position (query, &format, NULL); - - if (format != GST_FORMAT_TIME) { - res = FALSE; - break; - } - - cur = amrwbparse->ts; - - gst_query_set_position (query, GST_FORMAT_TIME, cur); - res = TRUE; - break; - } - case GST_QUERY_DURATION: - { - GstFormat format; - gint64 tot; - GstPad *peer; - - gst_query_parse_duration (query, &format, NULL); - - if (format != GST_FORMAT_TIME) { - res = FALSE; - break; - } - - tot = -1; - res = FALSE; - - peer = gst_pad_get_peer (amrwbparse->sinkpad); - if (peer) { - GstFormat pformat; - gint64 ptot; - - pformat = GST_FORMAT_BYTES; - res = gst_pad_query_duration (peer, &pformat, &ptot); - if (res && amrwbparse->block) { - tot = gst_util_uint64_scale_int (ptot, 20 * GST_MSECOND, - amrwbparse->block); - } - gst_object_unref (GST_OBJECT (peer)); - } - gst_query_set_duration (query, GST_FORMAT_TIME, tot); - res = TRUE; - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } - - gst_object_unref (amrwbparse); - return res; -} - - -static gboolean -gst_amrwbparse_handle_pull_seek (GstAmrwbParse * amrwbparse, GstPad * pad, - GstEvent * event) -{ - GstFormat format; - gdouble rate; - GstSeekFlags flags; - GstSeekType cur_type, stop_type; - gint64 cur, stop; - gint64 byte_cur = -1, byte_stop = -1; - gboolean flush; - - gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, - &stop_type, &stop); - - GST_DEBUG_OBJECT (amrwbparse, "Performing seek to %" GST_TIME_FORMAT, - GST_TIME_ARGS (cur)); - - /* For any format other than TIME, see if upstream handles - * it directly or fail. For TIME, try upstream, but do it ourselves if - * it fails upstream */ - if (format != GST_FORMAT_TIME) { - return gst_pad_push_event (amrwbparse->sinkpad, event); - } else { - if (gst_pad_push_event (amrwbparse->sinkpad, event)) - return TRUE; - } - - flush = flags & GST_SEEK_FLAG_FLUSH; - - /* send flush start */ - if (flush) - gst_pad_push_event (amrwbparse->sinkpad, gst_event_new_flush_start ()); - /* we only handle FLUSH seeks at the moment */ - else - return FALSE; - - /* grab streaming lock, this should eventually be possible, either - * because the task is paused or our streaming thread stopped - * because our peer is flushing. */ - GST_PAD_STREAM_LOCK (amrwbparse->sinkpad); - - /* Convert the TIME to the appropriate BYTE position at which to resume - * decoding. */ - cur = cur / (20 * GST_MSECOND) * (20 * GST_MSECOND); - if (cur != -1) - byte_cur = amrwbparse->block * (cur / 20 / GST_MSECOND) + AMRWB_HEADER_SIZE; - if (stop != -1) - byte_stop = - amrwbparse->block * (stop / 20 / GST_MSECOND) + AMRWB_HEADER_SIZE; - amrwbparse->offset = byte_cur; - amrwbparse->ts = cur; - - GST_DEBUG_OBJECT (amrwbparse, "Seeking to byte range %" G_GINT64_FORMAT - " to %" G_GINT64_FORMAT, byte_cur, cur); - - /* and prepare to continue streaming */ - /* send flush stop, peer will accept data and events again. We - * are not yet providing data as we still have the STREAM_LOCK. */ - gst_pad_push_event (amrwbparse->sinkpad, gst_event_new_flush_stop ()); - gst_pad_push_event (amrwbparse->srcpad, gst_event_new_new_segment (FALSE, - rate, format, cur, -1, cur)); - - /* and restart the task in case it got paused explicitely or by - * the FLUSH_START event we pushed out. */ - gst_pad_start_task (amrwbparse->sinkpad, - (GstTaskFunction) gst_amrwbparse_loop, amrwbparse->sinkpad); - - /* and release the lock again so we can continue streaming */ - GST_PAD_STREAM_UNLOCK (amrwbparse->sinkpad); - - return TRUE; -} - -static gboolean -gst_amrwbparse_handle_push_seek (GstAmrwbParse * amrwbparse, GstPad * pad, - GstEvent * event) -{ - GstFormat format; - gdouble rate; - GstSeekFlags flags; - GstSeekType cur_type, stop_type; - gint64 cur, stop; - gint64 byte_cur = -1, byte_stop = -1; - - gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, - &stop_type, &stop); - - GST_DEBUG_OBJECT (amrwbparse, "Performing seek to %" GST_TIME_FORMAT, - GST_TIME_ARGS (cur)); - - /* For any format other than TIME, see if upstream handles - * it directly or fail. For TIME, try upstream, but do it ourselves if - * it fails upstream */ - if (format != GST_FORMAT_TIME) { - return gst_pad_push_event (amrwbparse->sinkpad, event); - } else { - if (gst_pad_push_event (amrwbparse->sinkpad, event)) - return TRUE; - } - - /* Convert the TIME to the appropriate BYTE position at which to resume - * decoding. */ - cur = cur / (20 * GST_MSECOND) * (20 * GST_MSECOND); - if (cur != -1) - byte_cur = amrwbparse->block * (cur / 20 / GST_MSECOND) + AMRWB_HEADER_SIZE; - if (stop != -1) - byte_stop = - amrwbparse->block * (stop / 20 / GST_MSECOND) + AMRWB_HEADER_SIZE; - amrwbparse->ts = cur; - - GST_DEBUG_OBJECT (amrwbparse, "Seeking to byte range %" G_GINT64_FORMAT - " to %" G_GINT64_FORMAT, byte_cur, byte_stop); - - /* Send BYTE based seek upstream */ - event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, - byte_cur, stop_type, byte_stop); - - return gst_pad_push_event (amrwbparse->sinkpad, event); -} - -static gboolean -gst_amrwbparse_src_event (GstPad * pad, GstEvent * event) -{ - GstAmrwbParse *amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (pad)); - gboolean res; - - GST_DEBUG_OBJECT (amrwbparse, "handling event %d", GST_EVENT_TYPE (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - if (amrwbparse->seek_handler) - res = amrwbparse->seek_handler (amrwbparse, pad, event); - else - res = FALSE; - break; - default: - res = gst_pad_push_event (amrwbparse->sinkpad, event); - break; - } - gst_object_unref (amrwbparse); - - return res; -} - - -/* - * Data reading. - */ -static gboolean -gst_amrwbparse_sink_event (GstPad * pad, GstEvent * event) -{ - GstAmrwbParse *amrwbparse; - gboolean res; - - amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (pad)); - - GST_LOG ("handling event %d", GST_EVENT_TYPE (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - res = gst_pad_push_event (amrwbparse->srcpad, event); - break; - case GST_EVENT_FLUSH_STOP: - gst_adapter_clear (amrwbparse->adapter); - gst_segment_init (&amrwbparse->segment, GST_FORMAT_TIME); - res = gst_pad_push_event (amrwbparse->srcpad, event); - break; - case GST_EVENT_EOS: - res = gst_pad_push_event (amrwbparse->srcpad, event); - break; - case GST_EVENT_NEWSEGMENT: - { - /* eat for now, we send a newsegment at start with infinite - * duration. */ - gst_event_unref (event); - res = TRUE; - break; - } - default: - res = gst_pad_push_event (amrwbparse->srcpad, event); - break; - } - gst_object_unref (amrwbparse); - - return res; -} - -/* streaming mode */ -static GstFlowReturn -gst_amrwbparse_chain (GstPad * pad, GstBuffer * buffer) -{ - GstAmrwbParse *amrwbparse; - GstFlowReturn res = GST_FLOW_OK; - gint mode; - const guint8 *data; - GstBuffer *out; - GstClockTime timestamp; - - amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (pad)); - - timestamp = GST_BUFFER_TIMESTAMP (buffer); - if (GST_CLOCK_TIME_IS_VALID (timestamp)) { - GST_DEBUG_OBJECT (amrwbparse, "Lock on timestamp %" GST_TIME_FORMAT, - GST_TIME_ARGS (timestamp)); - amrwbparse->ts = timestamp; - } - - gst_adapter_push (amrwbparse->adapter, buffer); - - /* init */ - if (amrwbparse->need_header) { - GstEvent *segev; - GstCaps *caps; - - if (gst_adapter_available (amrwbparse->adapter) < AMRWB_HEADER_SIZE) - goto done; - - data = gst_adapter_peek (amrwbparse->adapter, AMRWB_HEADER_SIZE); - if (memcmp (data, AMRWB_HEADER_STR, AMRWB_HEADER_SIZE) != 0) - goto done; - - gst_adapter_flush (amrwbparse->adapter, AMRWB_HEADER_SIZE); - - amrwbparse->need_header = FALSE; - - caps = gst_caps_new_simple ("audio/AMR-WB", - "rate", G_TYPE_INT, 16000, "channels", G_TYPE_INT, 1, NULL); - gst_pad_set_caps (amrwbparse->srcpad, caps); - gst_caps_unref (caps); - - GST_DEBUG_OBJECT (amrwbparse, "Sending first segment"); - segev = gst_event_new_new_segment_full (FALSE, 1.0, 1.0, - GST_FORMAT_TIME, 0, -1, 0); - - gst_pad_push_event (amrwbparse->srcpad, segev); - } - - while (TRUE) { - if (gst_adapter_available (amrwbparse->adapter) < 1) - break; - - data = gst_adapter_peek (amrwbparse->adapter, 1); - - /* get size */ - mode = (data[0] >> 3) & 0x0F; - amrwbparse->block = block_size[mode] + 1; /* add one for the mode */ - - if (gst_adapter_available (amrwbparse->adapter) < amrwbparse->block) - break; - - out = gst_buffer_new_and_alloc (amrwbparse->block); - - data = gst_adapter_peek (amrwbparse->adapter, amrwbparse->block); - memcpy (GST_BUFFER_DATA (out), data, amrwbparse->block); - - /* timestamp, all constants that won't overflow */ - GST_BUFFER_DURATION (out) = GST_SECOND * L_FRAME16k / 16000; - GST_BUFFER_TIMESTAMP (out) = amrwbparse->ts; - if (GST_CLOCK_TIME_IS_VALID (amrwbparse->ts)) - amrwbparse->ts += GST_BUFFER_DURATION (out); - - gst_buffer_set_caps (out, GST_PAD_CAPS (amrwbparse->srcpad)); - - GST_DEBUG_OBJECT (amrwbparse, "Pushing %d bytes of data", - amrwbparse->block); - - res = gst_pad_push (amrwbparse->srcpad, out); - - gst_adapter_flush (amrwbparse->adapter, amrwbparse->block); - } -done: - - gst_object_unref (amrwbparse); - return res; -} - -static gboolean -gst_amrwbparse_pull_header (GstAmrwbParse * amrwbparse) -{ - GstBuffer *buffer; - GstFlowReturn ret; - guint8 *data; - gint size; - - ret = gst_pad_pull_range (amrwbparse->sinkpad, G_GUINT64_CONSTANT (0), - AMRWB_HEADER_SIZE, &buffer); - if (ret != GST_FLOW_OK) - return FALSE; - - data = GST_BUFFER_DATA (buffer); - size = GST_BUFFER_SIZE (buffer); - - if (size < AMRWB_HEADER_SIZE) - goto not_enough; - - if (memcmp (data, AMRWB_HEADER_STR, AMRWB_HEADER_SIZE)) - goto no_header; - - gst_buffer_unref (buffer); - - amrwbparse->offset = AMRWB_HEADER_SIZE; - return TRUE; - -not_enough: - { - gst_buffer_unref (buffer); - return FALSE; - } -no_header: - { - gst_buffer_unref (buffer); - return FALSE; - } -} - -/* random access mode, could just read a fixed size buffer and push it to - * the chain function but we don't... */ -static void -gst_amrwbparse_loop (GstPad * pad) -{ - GstAmrwbParse *amrwbparse; - GstBuffer *buffer; - guint8 *data; - gint size; - gint block, mode; - GstFlowReturn ret = GST_FLOW_OK; - - amrwbparse = GST_AMRWBPARSE (GST_PAD_PARENT (pad)); - - /* init */ - if (G_UNLIKELY (amrwbparse->need_header)) { - GstCaps *caps; - - if (!gst_amrwbparse_pull_header (amrwbparse)) { - GST_ELEMENT_ERROR (amrwbparse, STREAM, WRONG_TYPE, (NULL), (NULL)); - GST_LOG_OBJECT (amrwbparse, "could not read header"); - goto need_pause; - } - - caps = gst_caps_new_simple ("audio/AMR-WB", - "rate", G_TYPE_INT, 16000, "channels", G_TYPE_INT, 1, NULL); - gst_pad_set_caps (amrwbparse->srcpad, caps); - gst_caps_unref (caps); - - GST_DEBUG_OBJECT (amrwbparse, "Sending newsegment event"); - gst_pad_push_event (amrwbparse->srcpad, - gst_event_new_new_segment_full (FALSE, 1.0, 1.0, - GST_FORMAT_TIME, 0, -1, 0)); - - amrwbparse->need_header = FALSE; - } - - ret = - gst_pad_pull_range (amrwbparse->sinkpad, amrwbparse->offset, 1, &buffer); - - if (ret == GST_FLOW_UNEXPECTED) - goto eos; - else if (ret != GST_FLOW_OK) - goto need_pause; - - data = GST_BUFFER_DATA (buffer); - size = GST_BUFFER_SIZE (buffer); - - /* EOS */ - if (size < 1) { - gst_buffer_unref (buffer); - goto eos; - } - - /* get size */ - mode = (data[0] >> 3) & 0x0F; - block = block_size[mode]; /* add one for the mode */ - - gst_buffer_unref (buffer); - - ret = gst_pad_pull_range (amrwbparse->sinkpad, - amrwbparse->offset, block, &buffer); - - if (ret == GST_FLOW_UNEXPECTED) - goto eos; - else if (ret != GST_FLOW_OK) - goto need_pause; - - amrwbparse->offset += block; - - /* output */ - GST_BUFFER_DURATION (buffer) = GST_SECOND * L_FRAME16k / 16000; - GST_BUFFER_TIMESTAMP (buffer) = amrwbparse->ts; - - gst_buffer_set_caps (buffer, - (GstCaps *) gst_pad_get_pad_template_caps (amrwbparse->srcpad)); - - ret = gst_pad_push (amrwbparse->srcpad, buffer); - - if (ret != GST_FLOW_OK) { - GST_DEBUG_OBJECT (amrwbparse, "Flow: %s", gst_flow_get_name (ret)); - if (GST_FLOW_IS_FATAL (ret)) { - GST_ELEMENT_ERROR (amrwbparse, STREAM, FAILED, (NULL), /* _("Internal data flow error.")), */ - ("streaming task paused, reason: %s", gst_flow_get_name (ret))); - gst_pad_push_event (pad, gst_event_new_eos ()); - } - goto need_pause; - } - - amrwbparse->ts += GST_BUFFER_DURATION (buffer); - - return; - -need_pause: - { - GST_LOG_OBJECT (amrwbparse, "pausing task"); - gst_pad_pause_task (pad); - return; - } -eos: - { - GST_LOG_OBJECT (amrwbparse, "pausing task %d", ret); - gst_pad_push_event (amrwbparse->srcpad, gst_event_new_eos ()); - gst_pad_pause_task (pad); - return; - } -} - -static gboolean -gst_amrwbparse_sink_activate (GstPad * sinkpad) -{ - gboolean result = FALSE; - GstAmrwbParse *amrwbparse; - - amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (sinkpad)); - - if (gst_pad_check_pull_range (sinkpad)) { - GST_DEBUG ("Trying to activate in pull mode"); - amrwbparse->seekable = TRUE; - amrwbparse->ts = 0; - result = gst_pad_activate_pull (sinkpad, TRUE); - } else { - GST_DEBUG ("Try to activate in push mode"); - amrwbparse->seekable = FALSE; - result = gst_pad_activate_push (sinkpad, TRUE); - } - - gst_object_unref (amrwbparse); - return result; -} - - - -static gboolean -gst_amrwbparse_sink_activate_push (GstPad * sinkpad, gboolean active) -{ - GstAmrwbParse *amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (sinkpad)); - - if (active) { - amrwbparse->seek_handler = gst_amrwbparse_handle_push_seek; - } else { - amrwbparse->seek_handler = NULL; - } - - gst_object_unref (amrwbparse); - return TRUE; -} - -static gboolean -gst_amrwbparse_sink_activate_pull (GstPad * sinkpad, gboolean active) -{ - gboolean result; - GstAmrwbParse *amrwbparse; - - amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (sinkpad)); - if (active) { - amrwbparse->seek_handler = gst_amrwbparse_handle_pull_seek; - result = gst_pad_start_task (sinkpad, - (GstTaskFunction) gst_amrwbparse_loop, sinkpad); - } else { - amrwbparse->seek_handler = NULL; - result = gst_pad_stop_task (sinkpad); - } - - gst_object_unref (amrwbparse); - return result; -} - - -static GstStateChangeReturn -gst_amrwbparse_state_change (GstElement * element, GstStateChange transition) -{ - GstAmrwbParse *amrwbparse; - GstStateChangeReturn ret; - - amrwbparse = GST_AMRWBPARSE (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - amrwbparse->need_header = TRUE; - amrwbparse->ts = -1; - amrwbparse->block = 0; - gst_segment_init (&amrwbparse->segment, GST_FORMAT_TIME); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - - return ret; -} diff --git a/ext/amrwb/gstamrwbparse.h b/ext/amrwb/gstamrwbparse.h deleted file mode 100644 index 6cbe7b74..00000000 --- a/ext/amrwb/gstamrwbparse.h +++ /dev/null @@ -1,77 +0,0 @@ -/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin - * Copyright (C) 2006 Edgard Lima - * - * 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. - */ - -#ifndef __GST_AMRWBPARSE_H__ -#define __GST_AMRWBPARSE_H__ - -#include -#include -#include -#include -#include - -G_BEGIN_DECLS - -#define GST_TYPE_AMRWBPARSE \ - (gst_amrwbparse_get_type()) -#define GST_AMRWBPARSE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRWBPARSE, GstAmrwbParse)) -#define GST_AMRWBPARSE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRWBPARSE, GstAmrwbParseClass)) -#define GST_IS_AMRWBPARSE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRWBPARSE)) -#define GST_IS_AMRWBPARSE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRWBPARSE)) - -typedef struct _GstAmrwbParse GstAmrwbParse; -typedef struct _GstAmrwbParseClass GstAmrwbParseClass; - -typedef gboolean (*GstAmrwbSeekHandler) (GstAmrwbParse * amrwbparse, GstPad * pad, - GstEvent * event); - -struct _GstAmrwbParse { - GstElement element; - - /* pads */ - GstPad *sinkpad, *srcpad; - - GstAdapter *adapter; - - gboolean seekable; - gboolean need_header; - gint64 offset; - gint block; - - GstAmrwbSeekHandler seek_handler; - - guint64 ts; - - /* for seeking etc */ - GstSegment segment; -}; - -struct _GstAmrwbParseClass { - GstElementClass parent_class; -}; - -GType gst_amrwbparse_get_type (void); - -G_END_DECLS - -#endif /* __GST_AMRWBPARSE_H__ */ diff --git a/ext/amrwbenc/GstAmrwbEnc.prs b/ext/amrwbenc/GstAmrwbEnc.prs new file mode 100644 index 00000000..57658494 --- /dev/null +++ b/ext/amrwbenc/GstAmrwbEnc.prs @@ -0,0 +1,11 @@ +[_presets_] +version=0.10 +element-name=GstAmrwbEnc + +[enhance-size] +_meta/comment=Maximize compression, lowest bitrate +band-mode=0 + +[enhance-quality] +_meta/comment=Maximize quality, highest bitrate +band-mode=8 diff --git a/ext/amrwbenc/Makefile.am b/ext/amrwbenc/Makefile.am new file mode 100644 index 00000000..4c4ea3b3 --- /dev/null +++ b/ext/amrwbenc/Makefile.am @@ -0,0 +1,19 @@ +plugin_LTLIBRARIES = libgstamrwbenc.la + +libgstamrwbenc_la_SOURCES = \ + gstamrwb.c \ + gstamrwbenc.c + +libgstamrwbenc_la_CFLAGS = $(GST_CFLAGS) $(AMRWB_CFLAGS) +libgstamrwbenc_la_LIBADD = $(GST_BASE_LIBS) $(AMRWB_LIBS) +libgstamrwbenc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstamrwbenc_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = \ + gstamrwbenc.h + +presetdir = $(datadir)/gstreamer-$(GST_MAJORMINOR)/presets +preset_DATA = GstAmrwbEnc.prs + +EXTRA_DIST = $(preset_DATA) + diff --git a/ext/amrwbenc/README b/ext/amrwbenc/README new file mode 100644 index 00000000..9d158728 --- /dev/null +++ b/ext/amrwbenc/README @@ -0,0 +1,12 @@ +Compiling AMRWB encoder: +======================== + +To compile the amrwb encoder, you need to download the source code from +"http://www.3gpp.org/ftp/Specs/html-info/26204.htm" and uncompress the +files inside an amrwb-code directory; + +or execute this commands: +$cd amrwb-code +$sh ./run.sh + +and run the "autogen" script again. diff --git a/ext/amrwbenc/gstamrwb.c b/ext/amrwbenc/gstamrwb.c new file mode 100644 index 00000000..6cd734b1 --- /dev/null +++ b/ext/amrwbenc/gstamrwb.c @@ -0,0 +1,39 @@ +/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) Encoder plugin + * Copyright (C) 2006 Edgard Lima + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstamrwbenc.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "amrwbenc", + GST_RANK_NONE, GST_TYPE_AMRWBENC); +} + + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "amrwbenc", + "Adaptive Multi-Rate Wide-Band Encoder", + plugin_init, VERSION, GST_LICENSE_UNKNOWN, GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN); diff --git a/ext/amrwbenc/gstamrwbenc.c b/ext/amrwbenc/gstamrwbenc.c new file mode 100644 index 00000000..89659e53 --- /dev/null +++ b/ext/amrwbenc/gstamrwbenc.c @@ -0,0 +1,385 @@ +/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin + * Copyright (C) 2006 Edgard Lima + * + * 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. + */ + +/** + * SECTION:element-amrwbenc + * @see_also: #GstAmrwbDec, #GstAmrwbParse + * + * AMR wideband encoder based on the + * reference codec implementation. + * + * + * Example launch line + * |[ + * gst-launch filesrc location=abc.wav ! wavparse ! audioresample ! audioconvert ! amrwbenc ! filesink location=abc.amr + * ]| + * Please not that the above stream misses the header, that is needed to play + * the stream. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstamrwbenc.h" + +/* these defines are not in all .h files */ +#ifndef MR660 +#define MR660 0 +#define MR885 1 +#define MR1265 2 +#define MR1425 2 +#define MR1585 3 +#define MR1825 4 +#define MR1985 5 +#define MR2305 6 +#define MR2385 7 +#define MRDTX 8 +#endif + +static GType +gst_amrwbenc_bandmode_get_type () +{ + static GType gst_amrwbenc_bandmode_type = 0; + static GEnumValue gst_amrwbenc_bandmode[] = { + {MR660, "MR660", "MR660"}, + {MR885, "MR885", "MR885"}, + {MR1265, "MR1265", "MR1265"}, + {MR1425, "MR1425", "MR1425"}, + {MR1585, "MR1585", "MR1585"}, + {MR1825, "MR1825", "MR1825"}, + {MR1985, "MR1985", "MR1985"}, + {MR2305, "MR2305", "MR2305"}, + {MR2385, "MR2385", "MR2385"}, + {MRDTX, "MRDTX", "MRDTX"}, + {0, NULL, NULL}, + }; + if (!gst_amrwbenc_bandmode_type) { + gst_amrwbenc_bandmode_type = + g_enum_register_static ("GstAmrwbEncBandMode", gst_amrwbenc_bandmode); + } + return gst_amrwbenc_bandmode_type; +} + +#define GST_AMRWBENC_BANDMODE_TYPE (gst_amrwbenc_bandmode_get_type()) + +#define BANDMODE_DEFAULT MR660 + +enum +{ + PROP_0, + PROP_BANDMODE +}; + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "width = (int) 16, " + "depth = (int) 16, " + "signed = (boolean) TRUE, " + "endianness = (int) BYTE_ORDER, " + "rate = (int) 16000, " "channels = (int) 1") + ); + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/AMR-WB, " + "rate = (int) 16000, " "channels = (int) 1") + ); + +GST_DEBUG_CATEGORY_STATIC (gst_amrwbenc_debug); +#define GST_CAT_DEFAULT gst_amrwbenc_debug + +static void gst_amrwbenc_finalize (GObject * object); + +static GstFlowReturn gst_amrwbenc_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_amrwbenc_setcaps (GstPad * pad, GstCaps * caps); +static GstStateChangeReturn gst_amrwbenc_state_change (GstElement * element, + GstStateChange transition); + +static void +_do_init (GType object_type) +{ + const GInterfaceInfo preset_interface_info = { + NULL, /* interface init */ + NULL, /* interface finalize */ + NULL /* interface_data */ + }; + + g_type_add_interface_static (object_type, GST_TYPE_PRESET, + &preset_interface_info); + + GST_DEBUG_CATEGORY_INIT (gst_amrnbenc_debug, "amrwbenc", 0, + "AMR-WB audio encoder"); +} + +GST_BOILERPLATE_FULL (GstAmrwbEnc, gst_amrwbenc, GstElement, GST_TYPE_ELEMENT, + _do_init); + +static void +gst_amrwbenc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAmrwbEnc *self = GST_AMRWBENC (object); + + switch (prop_id) { + case PROP_BANDMODE: + self->bandmode = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + + return; +} + +static void +gst_amrwbenc_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstAmrwbEnc *self = GST_AMRWBENC (object); + + switch (prop_id) { + case PROP_BANDMODE: + g_value_set_enum (value, self->bandmode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + + return; +} + +static void +gst_amrwbenc_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstElementDetails details = GST_ELEMENT_DETAILS ("AMR-WB audio encoder", + "Codec/Encoder/Audio", + "Adaptive Multi-Rate Wideband audio encoder", + "Renato Araujo "); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_template)); + + gst_element_class_set_details (element_class, &details); +} + +static void +gst_amrwbenc_class_init (GstAmrwbEncClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + object_class->finalize = gst_amrwbenc_finalize; + object_class->set_property = gst_amrwbenc_set_property; + object_class->get_property = gst_amrwbenc_get_property; + + g_object_class_install_property (object_class, PROP_BANDMODE, + g_param_spec_enum ("band-mode", "Band Mode", + "Encoding Band Mode (Kbps)", GST_AMRWBENC_BANDMODE_TYPE, + BANDMODE_DEFAULT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + element_class->change_state = GST_DEBUG_FUNCPTR (gst_amrwbenc_state_change); +} + +static void +gst_amrwbenc_init (GstAmrwbEnc * amrwbenc, GstAmrwbEncClass * klass) +{ + /* create the sink pad */ + amrwbenc->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink"); + gst_pad_set_setcaps_function (amrwbenc->sinkpad, gst_amrwbenc_setcaps); + gst_pad_set_chain_function (amrwbenc->sinkpad, gst_amrwbenc_chain); + gst_element_add_pad (GST_ELEMENT (amrwbenc), amrwbenc->sinkpad); + + /* create the src pad */ + amrwbenc->srcpad = gst_pad_new_from_static_template (&src_template, "src"); + gst_pad_use_fixed_caps (amrwbenc->srcpad); + gst_element_add_pad (GST_ELEMENT (amrwbenc), amrwbenc->srcpad); + + amrwbenc->adapter = gst_adapter_new (); + + /* init rest */ + amrwbenc->handle = NULL; + amrwbenc->channels = 0; + amrwbenc->rate = 0; + amrwbenc->ts = 0; +} + +static void +gst_amrwbenc_finalize (GObject * object) +{ + GstAmrwbEnc *amrwbenc; + + amrwbenc = GST_AMRWBENC (object); + + g_object_unref (G_OBJECT (amrwbenc->adapter)); + amrwbenc->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_amrwbenc_setcaps (GstPad * pad, GstCaps * caps) +{ + GstStructure *structure; + GstAmrwbEnc *amrwbenc; + GstCaps *copy; + + amrwbenc = GST_AMRWBENC (GST_PAD_PARENT (pad)); + + structure = gst_caps_get_structure (caps, 0); + + /* get channel count */ + gst_structure_get_int (structure, "channels", &amrwbenc->channels); + gst_structure_get_int (structure, "rate", &amrwbenc->rate); + + /* this is not wrong but will sound bad */ + if (amrwbenc->channels != 1) { + GST_WARNING ("amrwbdec is only optimized for mono channels"); + } + if (amrwbenc->rate != 16000) { + GST_WARNING ("amrwbdec is only optimized for 16000 Hz samplerate"); + } + + /* create reverse caps */ + copy = gst_caps_new_simple ("audio/AMR-WB", + "channels", G_TYPE_INT, amrwbenc->channels, + "rate", G_TYPE_INT, amrwbenc->rate, NULL); + + gst_pad_set_caps (amrwbenc->srcpad, copy); + gst_caps_unref (copy); + + return TRUE; +} + +static GstFlowReturn +gst_amrwbenc_chain (GstPad * pad, GstBuffer * buffer) +{ + GstAmrwbEnc *amrwbenc; + GstFlowReturn ret = GST_FLOW_OK; + const int buffer_size = sizeof (Word16) * L_FRAME16k; + + amrwbenc = GST_AMRWBENC (gst_pad_get_parent (pad)); + + g_return_val_if_fail (amrwbenc->handle, GST_FLOW_WRONG_STATE); + + if (amrwbenc->rate == 0 || amrwbenc->channels == 0) { + ret = GST_FLOW_NOT_NEGOTIATED; + goto done; + } + + /* discontinuity clears adapter, FIXME, maybe we can set some + * encoder flag to mask the discont. */ + if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) { + gst_adapter_clear (amrwbenc->adapter); + amrwbenc->ts = 0; + amrwbenc->discont = TRUE; + } + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) + amrwbenc->ts = GST_BUFFER_TIMESTAMP (buffer); + + ret = GST_FLOW_OK; + gst_adapter_push (amrwbenc->adapter, buffer); + + /* Collect samples until we have enough for an output frame */ + while (gst_adapter_available (amrwbenc->adapter) >= buffer_size) { + GstBuffer *out; + guint8 *data; + gint outsize; + + out = gst_buffer_new_and_alloc (buffer_size); + GST_BUFFER_DURATION (out) = GST_SECOND * L_FRAME16k / + (amrwbenc->rate * amrwbenc->channels); + GST_BUFFER_TIMESTAMP (out) = amrwbenc->ts; + if (amrwbenc->ts != -1) { + amrwbenc->ts += GST_BUFFER_DURATION (out); + } + if (amrwbenc->discont) { + GST_BUFFER_FLAG_SET (out, GST_BUFFER_FLAG_DISCONT); + amrwbenc->discont = FALSE; + } + gst_buffer_set_caps (out, gst_pad_get_caps (amrwbenc->srcpad)); + + data = (guint8 *) gst_adapter_peek (amrwbenc->adapter, buffer_size); + + /* encode */ + outsize = + E_IF_encode (amrwbenc->handle, amrwbenc->bandmode, (Word16 *) data, + (UWord8 *) GST_BUFFER_DATA (out), 0); + + gst_adapter_flush (amrwbenc->adapter, buffer_size); + GST_BUFFER_SIZE (out) = outsize; + + /* play */ + if ((ret = gst_pad_push (amrwbenc->srcpad, out)) != GST_FLOW_OK) + break; + } + +done: + + gst_object_unref (amrwbenc); + return ret; + +} + +static GstStateChangeReturn +gst_amrwbenc_state_change (GstElement * element, GstStateChange transition) +{ + GstAmrwbEnc *amrwbenc; + GstStateChangeReturn ret; + + amrwbenc = GST_AMRWBENC (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!(amrwbenc->handle = E_IF_init ())) + return GST_STATE_CHANGE_FAILURE; + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + amrwbenc->rate = 0; + amrwbenc->channels = 0; + amrwbenc->ts = 0; + amrwbenc->discont = FALSE; + gst_adapter_clear (amrwbenc->adapter); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + E_IF_exit (amrwbenc->handle); + break; + default: + break; + } + + return ret; +} diff --git a/ext/amrwbenc/gstamrwbenc.h b/ext/amrwbenc/gstamrwbenc.h new file mode 100644 index 00000000..034a5bed --- /dev/null +++ b/ext/amrwbenc/gstamrwbenc.h @@ -0,0 +1,70 @@ +/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin + * Copyright (C) 2006 Edgard Lima + * + * 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. + */ + +#ifndef __GST_AMRWBENC_H__ +#define __GST_AMRWBENC_H__ + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_AMRWBENC \ + (gst_amrwbenc_get_type()) +#define GST_AMRWBENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRWBENC, GstAmrwbEnc)) +#define GST_AMRWBENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRWBENC, GstAmrwbEncClass)) +#define GST_IS_AMRWBENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRWBENC)) +#define GST_IS_AMRWBENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRWBENC)) + +typedef struct _GstAmrwbEnc GstAmrwbEnc; +typedef struct _GstAmrwbEncClass GstAmrwbEncClass; + +struct _GstAmrwbEnc { + GstElement element; + + /* pads */ + GstPad *sinkpad, *srcpad; + guint64 ts; + gboolean discont; + + GstAdapter *adapter; + + /* library handle */ + void *handle; + + /* input settings */ + gint bandmode; + gint channels, rate; +}; + +struct _GstAmrwbEncClass { + GstElementClass parent_class; +}; + +GType gst_amrwbenc_get_type (void); + +G_END_DECLS + +#endif /* __GST_AMRWBENC_H__ */ -- cgit v1.2.1 From d6bb14dade29ed36e4803e0ea4efff665da68dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 27 Jul 2009 19:59:32 +0200 Subject: amrwbenc: Fix compilation --- ext/amrwbenc/gstamrwbenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ext') diff --git a/ext/amrwbenc/gstamrwbenc.c b/ext/amrwbenc/gstamrwbenc.c index 89659e53..3b46d3e9 100644 --- a/ext/amrwbenc/gstamrwbenc.c +++ b/ext/amrwbenc/gstamrwbenc.c @@ -128,7 +128,7 @@ _do_init (GType object_type) g_type_add_interface_static (object_type, GST_TYPE_PRESET, &preset_interface_info); - GST_DEBUG_CATEGORY_INIT (gst_amrnbenc_debug, "amrwbenc", 0, + GST_DEBUG_CATEGORY_INIT (gst_amrwbenc_debug, "amrwbenc", 0, "AMR-WB audio encoder"); } -- cgit v1.2.1 From 4394b1a61c081163ec457d6b7924943c80aa29f4 Mon Sep 17 00:00:00 2001 From: Michael Pyne Date: Tue, 4 Aug 2009 10:06:54 +0200 Subject: spc: Use the portable libgme instead of x86-only OpenSPC library This will later allow us to play other gaming console files that are supported by libgme. Fixes bug #576800. --- ext/spc/gstspc.c | 80 +++++++++++++++++++++++++++++++++----------------------- ext/spc/gstspc.h | 12 ++++----- 2 files changed, 52 insertions(+), 40 deletions(-) (limited to 'ext') diff --git a/ext/spc/gstspc.c b/ext/spc/gstspc.c index c8ece379..3949686c 100644 --- a/ext/spc/gstspc.c +++ b/ext/spc/gstspc.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2004-2005 Michael Pyne +/* Copyright (C) 2004-2005,2009 Michael Pyne * Copyright (C) 2004-2006 Chris Lee * Copyright (C) 2007 Brian Koropoff * @@ -29,10 +29,10 @@ #include static const GstElementDetails gst_spc_dec_details = -GST_ELEMENT_DETAILS ("OpenSPC SPC decoder", +GST_ELEMENT_DETAILS ("SNES SPC 700 decoder", "Codec/Audio/Decoder", - "Uses OpenSPC to emulate an SPC processor", - "Chris Lee , Brian Koropoff "); + "Uses Blargg's libgme to emulate an SPC processor", + "Chris Lee , Brian Koropoff , Michael Pyne "); static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -54,7 +54,7 @@ static gboolean gst_spc_dec_src_event (GstPad * pad, GstEvent * event); static gboolean gst_spc_dec_src_query (GstPad * pad, GstQuery * query); static GstStateChangeReturn gst_spc_dec_change_state (GstElement * element, GstStateChange transition); -static void spc_play (GstPad * pad); +static void gst_spc_play (GstPad * pad); static void gst_spc_dec_dispose (GObject * object); static gboolean spc_setup (GstSpcDec * spc); @@ -157,6 +157,7 @@ gst_spc_dec_init (GstSpcDec * spc, GstSpcDecClass * klass) gst_element_add_pad (GST_ELEMENT (spc), spc->srcpad); spc->buf = NULL; + spc->player = NULL; spc->initialized = FALSE; spc_tag_clear (&spc->tag_info); } @@ -179,6 +180,7 @@ gst_spc_dec_chain (GstPad * pad, GstBuffer * buffer) { GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + /* Accumulate SPC data until end-of-stream, then commence playback. */ if (spc->buf) { spc->buf = gst_buffer_join (spc->buf, buffer); } else { @@ -286,8 +288,7 @@ gst_spc_dec_src_event (GstPad * pad, GstEvent * event) stop = GST_CLOCK_TIME_NONE; if (start_type == GST_SEEK_TYPE_SET) { - guint64 cur = - gst_util_uint64_scale (spc->byte_pos, GST_SECOND, 32000 * 2 * 2); + guint64 cur = gme_tell (spc->player) * GST_MSECOND; guint64 dest = (guint64) start; dest = CLAMP (dest, 0, gst_spc_duration (spc) + gst_spc_fadeout (spc)); @@ -320,12 +321,10 @@ gst_spc_dec_src_event (GstPad * pad, GstEvent * event) gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, rate, GST_FORMAT_TIME, dest, stop, dest)); - /* spc->byte_pos += OSPC_Run(-1, NULL, (unsigned int) (gst_util_uint64_scale(dest - cur, 32000*2*2, GST_SECOND))); */ - spc->seekpoint = - gst_util_uint64_scale (dest, 32000 * 2 * 2, GST_SECOND); + spc->seekpoint = dest / GST_MSECOND; /* nsecs to msecs */ spc->seeking = TRUE; - gst_pad_start_task (spc->srcpad, (GstTaskFunction) spc_play, + gst_pad_start_task (spc->srcpad, (GstTaskFunction) gst_spc_play, spc->srcpad); GST_PAD_STREAM_UNLOCK (spc->srcpad); @@ -373,8 +372,7 @@ gst_spc_dec_src_query (GstPad * pad, GstQuery * query) break; } gst_query_set_position (query, GST_FORMAT_TIME, - (gint64) gst_util_uint64_scale (spc->byte_pos, GST_SECOND, - 32000 * 2 * 2)); + (gint64) gme_tell (spc->player) * GST_MSECOND); break; } default: @@ -388,30 +386,35 @@ gst_spc_dec_src_query (GstPad * pad, GstQuery * query) } static void -spc_play (GstPad * pad) +gst_spc_play (GstPad * pad) { GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); GstFlowReturn flow_return; GstBuffer *out; gboolean seeking = spc->seeking; + gme_err_t gme_err = NULL; gint64 duration, fade, end, position; + const int NUM_SAMPLES = 1600; /* 4 bytes (stereo 16-bit) per sample */ if (!seeking) { - out = gst_buffer_new_and_alloc (1600 * 4); + out = gst_buffer_new_and_alloc (NUM_SAMPLES * 4); gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); - GST_BUFFER_TIMESTAMP (out) = - (gint64) gst_util_uint64_scale ((guint64) spc->byte_pos, GST_SECOND, - 32000 * 2 * 2); - spc->byte_pos += OSPC_Run (-1, (short *) GST_BUFFER_DATA (out), 1600 * 4); - } else { - if (spc->seekpoint < spc->byte_pos) { - OSPC_Init (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf)); - spc->byte_pos = 0; - } - spc->byte_pos += OSPC_Run (-1, NULL, 1600 * 4); - if (spc->byte_pos >= spc->seekpoint) { - spc->seeking = FALSE; + GST_BUFFER_TIMESTAMP (out) = gme_tell (spc->player) * GST_MSECOND; + + gme_err = + gme_play (spc->player, NUM_SAMPLES * 2, + (short *) GST_BUFFER_DATA (out)); + if (gme_err) { + GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); + gst_pad_pause_task (pad); + gst_pad_push_event (pad, gst_event_new_eos ()); + gst_object_unref (spc); + return; } + } else { + gme_seek (spc->player, spc->seekpoint); + spc->seeking = FALSE; + out = gst_buffer_new (); gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); } @@ -419,9 +422,7 @@ spc_play (GstPad * pad) duration = gst_spc_duration (spc); fade = gst_spc_fadeout (spc); end = duration + fade; - position = - (gint64) gst_util_uint64_scale ((guint64) spc->byte_pos, GST_SECOND, - 32000 * 2 * 2); + position = gme_tell (spc->player) * GST_MSECOND; if (position >= duration) { gint16 *data = (gint16 *) GST_BUFFER_DATA (out); @@ -461,6 +462,7 @@ static gboolean spc_setup (GstSpcDec * spc) { spc_tag_info *info; + gme_err_t gme_err = NULL; GstTagList *taglist; guint64 total_duration; @@ -526,15 +528,28 @@ spc_setup (GstSpcDec * spc) /* spc_tag_info_free(&info); */ + gme_err = + gme_open_data (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), + &spc->player, 32000); + if (gme_err || !spc->player || gme_type (spc->player) != gme_spc_type) { + if (spc->player) { + gme_delete (spc->player); + spc->player = NULL; + } + + GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); - if (OSPC_Init (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf)) != 0) { return FALSE; } + /* TODO: Is it worth it to make this optional? */ + gme_enable_accuracy (spc->player, 1); + gme_start_track (spc->player, 0); + gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0)); - gst_pad_start_task (spc->srcpad, (GstTaskFunction) spc_play, spc->srcpad); + gst_pad_start_task (spc->srcpad, (GstTaskFunction) gst_spc_play, spc->srcpad); /* We can't unreference this buffer because we might need to re-initialize * the emulator with the original data during a reverse seek @@ -544,7 +559,6 @@ spc_setup (GstSpcDec * spc) spc->initialized = TRUE; spc->seeking = FALSE; spc->seekpoint = 0; - spc->byte_pos = 0; return spc->initialized; } diff --git a/ext/spc/gstspc.h b/ext/spc/gstspc.h index e1740667..3d7e4899 100644 --- a/ext/spc/gstspc.h +++ b/ext/spc/gstspc.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2004-2005 Michael Pyne +/* Copyright (C) 2004-2005, 2009 Michael Pyne * Copyright (C) 2004-2006 Chris Lee * Copyright (C) 2007 Brian Koropoff * @@ -23,8 +23,7 @@ #include -#include - +#include #include "tag.h" G_BEGIN_DECLS @@ -51,13 +50,12 @@ struct _GstSpcDec GstPad *srcpad; GstBuffer *buf; + Music_Emu *player; gboolean initialized; gboolean seeking; - guint32 seekpoint; - + int seekpoint; + spc_tag_info tag_info; - - guint32 byte_pos; }; struct _GstSpcDecClass -- cgit v1.2.1 From de03453f6d3e284de1247ada864e45f010d126e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 4 Aug 2009 10:18:46 +0200 Subject: spc: Make the SPC plugin work with the latest libgme release gme_enable_accuracy() was added in SVN trunk and is not yet in any release. --- ext/spc/gstspc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'ext') diff --git a/ext/spc/gstspc.c b/ext/spc/gstspc.c index 3949686c..a66da7d5 100644 --- a/ext/spc/gstspc.c +++ b/ext/spc/gstspc.c @@ -541,9 +541,10 @@ spc_setup (GstSpcDec * spc) return FALSE; } - +#ifdef HAVE_LIBGME_ACCURACY /* TODO: Is it worth it to make this optional? */ gme_enable_accuracy (spc->player, 1); +#endif gme_start_track (spc->player, 0); gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, 1.0, -- cgit v1.2.1 From e268bd6e28c4ba7b1d8c86b0885a837b793c1f15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 6 Aug 2009 21:42:02 +0200 Subject: spcdec: Forward all unknown src events upstream and don't restrict to SPC --- ext/spc/gstspc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'ext') diff --git a/ext/spc/gstspc.c b/ext/spc/gstspc.c index a66da7d5..c5ca2457 100644 --- a/ext/spc/gstspc.c +++ b/ext/spc/gstspc.c @@ -274,6 +274,8 @@ gst_spc_dec_src_event (GstPad * pad, GstEvent * event) gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, &stop_type, &stop); + gst_event_unref (event); + if (format != GST_FORMAT_TIME) { GST_DEBUG_OBJECT (spc, "seeking is only supported in TIME format"); break; @@ -333,10 +335,10 @@ gst_spc_dec_src_event (GstPad * pad, GstEvent * event) break; } default: + result = gst_pad_push_event (spc->sinkpad, event); break; } - gst_event_unref (event); gst_object_unref (spc); return result; @@ -531,7 +533,7 @@ spc_setup (GstSpcDec * spc) gme_err = gme_open_data (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), &spc->player, 32000); - if (gme_err || !spc->player || gme_type (spc->player) != gme_spc_type) { + if (gme_err || !spc->player) { if (spc->player) { gme_delete (spc->player); spc->player = NULL; -- cgit v1.2.1 From c57eca3f0b254497649ac2474036ccbe406de8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 7 Aug 2009 06:41:43 +0200 Subject: spc: Rename SPC plugin to GME as preparation for enabling support for non-SPC gaming console sound files. --- ext/Makefile.am | 10 +- ext/gme/Makefile.am | 10 + ext/gme/gstgme.c | 612 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ext/gme/gstgme.h | 68 ++++++ ext/gme/tag.c | 353 ++++++++++++++++++++++++++++++ ext/gme/tag.h | 37 ++++ 6 files changed, 1085 insertions(+), 5 deletions(-) create mode 100644 ext/gme/Makefile.am create mode 100644 ext/gme/gstgme.c create mode 100644 ext/gme/gstgme.h create mode 100644 ext/gme/tag.c create mode 100644 ext/gme/tag.h (limited to 'ext') diff --git a/ext/Makefile.am b/ext/Makefile.am index e2e4cc52..d480fdc4 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -306,10 +306,10 @@ else SOUNDTOUCH_DIR= endif -if USE_SPC -SPC_DIR=spc +if USE_GME +GME_DIR=gme else -SPC_DIR= +GME_DIR= endif if USE_SWFDEC @@ -392,7 +392,7 @@ SUBDIRS=\ $(SMOOTHWAVE_DIR) \ $(SNDFILE_DIR) \ $(SOUNDTOUCH_DIR) \ - $(SPC_DIR) \ + $(GME_DIR) \ $(SWFDEC_DIR) \ $(TARKIN_DIR) \ $(THEORA_DIR) \ @@ -439,7 +439,7 @@ DIST_SUBDIRS = \ sdl \ sndfile \ soundtouch \ - spc \ + gme \ swfdec \ theora \ timidity \ diff --git a/ext/gme/Makefile.am b/ext/gme/Makefile.am new file mode 100644 index 00000000..c2bdbfea --- /dev/null +++ b/ext/gme/Makefile.am @@ -0,0 +1,10 @@ +plugin_LTLIBRARIES = libgstgme.la + +libgstgme_la_SOURCES = gstgme.c tag.c + +libgstgme_la_CFLAGS = $(GST_CFLAGS) $(GME_CFLAGS) +libgstgme_la_LIBADD = $(GST_LIBS) $(GME_LIBS) +libgstgme_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstgme_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = gstgme.h tag.h diff --git a/ext/gme/gstgme.c b/ext/gme/gstgme.c new file mode 100644 index 00000000..0315d6d0 --- /dev/null +++ b/ext/gme/gstgme.c @@ -0,0 +1,612 @@ +/* Copyright (C) 2004-2005,2009 Michael Pyne + * Copyright (C) 2004-2006 Chris Lee + * Copyright (C) 2007 Brian Koropoff + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstgme.h" + +#include +#include +#include + +static const GstElementDetails gst_spc_dec_details = +GST_ELEMENT_DETAILS ("SNES SPC 700 decoder", + "Codec/Audio/Decoder", + "Uses Blargg's libgme to emulate an SPC processor", + "Chris Lee , Brian Koropoff , Michael Pyne "); + +static GstStaticPadTemplate sink_factory = +GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-spc")); + +static GstStaticPadTemplate src_factory = +GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "endianness = (int) BYTE_ORDER, " + "signed = (boolean) TRUE, " + "width = (int) 16, " + "depth = (int) 16, " "rate = (int) 32000, " "channels = (int) 2")); + +GST_BOILERPLATE (GstSpcDec, gst_spc_dec, GstElement, GST_TYPE_ELEMENT); + +static GstFlowReturn gst_spc_dec_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_spc_dec_sink_event (GstPad * pad, GstEvent * event); +static gboolean gst_spc_dec_src_event (GstPad * pad, GstEvent * event); +static gboolean gst_spc_dec_src_query (GstPad * pad, GstQuery * query); +static GstStateChangeReturn gst_spc_dec_change_state (GstElement * element, + GstStateChange transition); +static void gst_spc_play (GstPad * pad); +static void gst_spc_dec_dispose (GObject * object); +static gboolean spc_setup (GstSpcDec * spc); + +static gboolean +spc_negotiate (GstSpcDec * spc) +{ + GstCaps *allowed, *caps; + GstStructure *structure; + gint width = 16, depth = 16; + gboolean sign; + int rate = 32000; + int channels = 2; + + allowed = gst_pad_get_allowed_caps (spc->srcpad); + if (!allowed) { + GST_DEBUG_OBJECT (spc, "couldn't get allowed caps"); + return FALSE; + } + + GST_DEBUG_OBJECT (spc, "allowed caps: %" GST_PTR_FORMAT, allowed); + + structure = gst_caps_get_structure (allowed, 0); + gst_structure_get_int (structure, "width", &width); + gst_structure_get_int (structure, "depth", &depth); + + if (width && depth && width != depth) { + GST_DEBUG_OBJECT (spc, "width %d and depth %d are different", width, depth); + gst_caps_unref (allowed); + return FALSE; + } + + gst_structure_get_boolean (structure, "signed", &sign); + gst_structure_get_int (structure, "rate", &rate); + gst_structure_get_int (structure, "channels", &channels); + + caps = gst_caps_new_simple ("audio/x-raw-int", + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "signed", G_TYPE_BOOLEAN, TRUE, + "width", G_TYPE_INT, width, + "depth", G_TYPE_INT, depth, + "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL); + gst_pad_set_caps (spc->srcpad, caps); + + gst_caps_unref (caps); + gst_caps_unref (allowed); + + return TRUE; +} + +static void +gst_spc_dec_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details (element_class, &gst_spc_dec_details); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_factory)); +} + +static void +gst_spc_dec_class_init (GstSpcDecClass * klass) +{ + GstElementClass *element_class = (GstElementClass *) klass; + GObjectClass *gobject_class = (GObjectClass *) klass; + + element_class->change_state = GST_DEBUG_FUNCPTR (gst_spc_dec_change_state); + gobject_class->dispose = gst_spc_dec_dispose; +} + +static const GstQueryType * +gst_spc_dec_src_query_type (GstPad * pad) +{ + static const GstQueryType query_types[] = { + GST_QUERY_DURATION, + GST_QUERY_POSITION, + (GstQueryType) 0 + }; + + return query_types; +} + + +static void +gst_spc_dec_init (GstSpcDec * spc, GstSpcDecClass * klass) +{ + spc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); + /* gst_pad_set_query_function (spc->sinkpad, NULL); */ + gst_pad_set_event_function (spc->sinkpad, gst_spc_dec_sink_event); + gst_pad_set_chain_function (spc->sinkpad, gst_spc_dec_chain); + gst_element_add_pad (GST_ELEMENT (spc), spc->sinkpad); + + spc->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); + gst_pad_set_event_function (spc->srcpad, gst_spc_dec_src_event); + gst_pad_set_query_function (spc->srcpad, gst_spc_dec_src_query); + gst_pad_set_query_type_function (spc->srcpad, gst_spc_dec_src_query_type); + gst_pad_use_fixed_caps (spc->srcpad); + gst_element_add_pad (GST_ELEMENT (spc), spc->srcpad); + + spc->buf = NULL; + spc->player = NULL; + spc->initialized = FALSE; + spc_tag_clear (&spc->tag_info); +} + +static void +gst_spc_dec_dispose (GObject * object) +{ + GstSpcDec *spc = GST_SPC_DEC (object); + + if (spc->buf) { + gst_buffer_unref (spc->buf); + spc->buf = NULL; + } + + spc_tag_free (&spc->tag_info); +} + +static GstFlowReturn +gst_spc_dec_chain (GstPad * pad, GstBuffer * buffer) +{ + GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + + /* Accumulate SPC data until end-of-stream, then commence playback. */ + if (spc->buf) { + spc->buf = gst_buffer_join (spc->buf, buffer); + } else { + spc->buf = buffer; + } + + gst_object_unref (spc); + + return GST_FLOW_OK; +} + +static gboolean +gst_spc_dec_sink_event (GstPad * pad, GstEvent * event) +{ + GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + gboolean result = TRUE; + gboolean forward = FALSE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* we get EOS when we loaded the complete file, now try to initialize the + * decoding */ + if (!(result = spc_setup (spc))) { + /* can't start, post an ERROR and push EOS downstream */ + GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), + ("can't start playback")); + forward = TRUE; + } + break; + default: + break; + } + if (forward) + result = gst_pad_push_event (spc->srcpad, event); + else + gst_event_unref (event); + + gst_object_unref (spc); + + return result; +} + +static gint64 +gst_spc_duration (GstSpcDec * spc) +{ + gint64 total_ticks = + spc->tag_info.time_intro + + spc->tag_info.time_loop * spc->tag_info.loop_count + + spc->tag_info.time_end; + if (total_ticks) { + return (gint64) gst_util_uint64_scale (total_ticks, GST_SECOND, 64000); + } else if (spc->tag_info.time_seconds) { + gint64 time = (gint64) spc->tag_info.time_seconds * GST_SECOND; + + return time; + } else { + return (gint64) (3 * 60) * GST_SECOND; + } +} + +static gint64 +gst_spc_fadeout (GstSpcDec * spc) +{ + if (spc->tag_info.time_fade) { + return (gint64) gst_util_uint64_scale ((guint64) spc->tag_info.time_fade, + GST_SECOND, 64000); + } else if (spc->tag_info.time_fade_milliseconds) { + return (gint64) (spc->tag_info.time_fade_milliseconds * GST_MSECOND); + } else { + return 10 * GST_SECOND; + } +} + + +static gboolean +gst_spc_dec_src_event (GstPad * pad, GstEvent * event) +{ + GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + gboolean result = FALSE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + { + gdouble rate; + GstFormat format; + GstSeekFlags flags; + GstSeekType start_type, stop_type; + gint64 start, stop; + gboolean flush; + + gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, + &stop_type, &stop); + + gst_event_unref (event); + + if (format != GST_FORMAT_TIME) { + GST_DEBUG_OBJECT (spc, "seeking is only supported in TIME format"); + break; + } + + if (start_type != GST_SEEK_TYPE_SET || stop_type != GST_SEEK_TYPE_NONE) { + GST_DEBUG_OBJECT (spc, "unsupported seek type"); + break; + } + + if (stop_type == GST_SEEK_TYPE_NONE) + stop = GST_CLOCK_TIME_NONE; + + if (start_type == GST_SEEK_TYPE_SET) { + guint64 cur = gme_tell (spc->player) * GST_MSECOND; + guint64 dest = (guint64) start; + + dest = CLAMP (dest, 0, gst_spc_duration (spc) + gst_spc_fadeout (spc)); + + if (dest == cur) + break; + + flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH; + + if (flush) { + gst_pad_push_event (spc->srcpad, gst_event_new_flush_start ()); + } else { + gst_pad_stop_task (spc->srcpad); + } + + GST_PAD_STREAM_LOCK (spc->srcpad); + + if (flags & GST_SEEK_FLAG_SEGMENT) { + gst_element_post_message (GST_ELEMENT (spc), + gst_message_new_segment_start (GST_OBJECT (spc), format, cur)); + } + + if (flush) { + gst_pad_push_event (spc->srcpad, gst_event_new_flush_stop ()); + } + + if (stop == GST_CLOCK_TIME_NONE) + stop = (guint64) (gst_spc_duration (spc) + gst_spc_fadeout (spc)); + + gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, rate, + GST_FORMAT_TIME, dest, stop, dest)); + + spc->seekpoint = dest / GST_MSECOND; /* nsecs to msecs */ + spc->seeking = TRUE; + + gst_pad_start_task (spc->srcpad, (GstTaskFunction) gst_spc_play, + spc->srcpad); + + GST_PAD_STREAM_UNLOCK (spc->srcpad); + result = TRUE; + } + break; + } + default: + result = gst_pad_push_event (spc->sinkpad, event); + break; + } + + gst_object_unref (spc); + + return result; +} + +static gboolean +gst_spc_dec_src_query (GstPad * pad, GstQuery * query) +{ + GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + gboolean result = TRUE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_DURATION: + { + GstFormat format; + + gst_query_parse_duration (query, &format, NULL); + if (!spc->initialized || format != GST_FORMAT_TIME) { + result = FALSE; + break; + } + gst_query_set_duration (query, GST_FORMAT_TIME, + gst_spc_duration (spc) + gst_spc_fadeout (spc)); + break; + } + case GST_QUERY_POSITION: + { + GstFormat format; + + gst_query_parse_position (query, &format, NULL); + if (!spc->initialized || format != GST_FORMAT_TIME) { + result = FALSE; + break; + } + gst_query_set_position (query, GST_FORMAT_TIME, + (gint64) gme_tell (spc->player) * GST_MSECOND); + break; + } + default: + result = gst_pad_query_default (pad, query); + break; + } + + gst_object_unref (spc); + + return result; +} + +static void +gst_spc_play (GstPad * pad) +{ + GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + GstFlowReturn flow_return; + GstBuffer *out; + gboolean seeking = spc->seeking; + gme_err_t gme_err = NULL; + gint64 duration, fade, end, position; + const int NUM_SAMPLES = 1600; /* 4 bytes (stereo 16-bit) per sample */ + + if (!seeking) { + out = gst_buffer_new_and_alloc (NUM_SAMPLES * 4); + gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); + GST_BUFFER_TIMESTAMP (out) = gme_tell (spc->player) * GST_MSECOND; + + gme_err = + gme_play (spc->player, NUM_SAMPLES * 2, + (short *) GST_BUFFER_DATA (out)); + if (gme_err) { + GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); + gst_pad_pause_task (pad); + gst_pad_push_event (pad, gst_event_new_eos ()); + gst_object_unref (spc); + return; + } + } else { + gme_seek (spc->player, spc->seekpoint); + spc->seeking = FALSE; + + out = gst_buffer_new (); + gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); + } + + duration = gst_spc_duration (spc); + fade = gst_spc_fadeout (spc); + end = duration + fade; + position = gme_tell (spc->player) * GST_MSECOND; + + if (position >= duration) { + gint16 *data = (gint16 *) GST_BUFFER_DATA (out); + guint32 size = GST_BUFFER_SIZE (out) / sizeof (gint16); + unsigned int i; + + gint64 num = (fade - (position - duration)); + + for (i = 0; i < size; i++) { + /* Apply a parabolic volume envelope */ + data[i] = (gint16) (data[i] * num / fade * num / fade); + } + } + + if ((flow_return = gst_pad_push (spc->srcpad, out)) != GST_FLOW_OK) { + GST_DEBUG_OBJECT (spc, "pausing task, reason %s", + gst_flow_get_name (flow_return)); + + gst_pad_pause_task (pad); + + if (GST_FLOW_IS_FATAL (flow_return) || flow_return == GST_FLOW_NOT_LINKED) { + gst_pad_push_event (pad, gst_event_new_eos ()); + } + } + + if (position >= end) { + gst_pad_pause_task (pad); + gst_pad_push_event (pad, gst_event_new_eos ()); + } + + gst_object_unref (spc); + + return; +} + +static gboolean +spc_setup (GstSpcDec * spc) +{ + spc_tag_info *info; + gme_err_t gme_err = NULL; + GstTagList *taglist; + guint64 total_duration; + + if (!spc->buf || !spc_negotiate (spc)) { + return FALSE; + } + + info = &(spc->tag_info); + + spc_tag_get_info (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), + info); + + taglist = gst_tag_list_new (); + + if (info->title) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, + info->title, NULL); + if (info->artist) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ARTIST, + info->artist, NULL); + /* Prefer the name of the official soundtrack over the name of the game (since this is + * how track numbers are derived) + */ + if (info->album) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, + info->album, NULL); + else if (info->game) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, info->game, + NULL); + if (info->year) { + GDate *date = g_date_new_dmy (1, 1, info->year); + + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date, NULL); + g_date_free (date); + } + if (info->track) { + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_NUMBER, + info->track, NULL); + } + if (info->comment) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COMMENT, + info->comment, NULL); + if (info->disc) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, + GST_TAG_ALBUM_VOLUME_NUMBER, info->disc, NULL); + if (info->publisher) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION, + info->publisher, NULL); + if (info->dumper) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_CONTACT, + info->dumper, NULL); + if (info->emulator) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, + info->emulator == EMU_ZSNES ? "ZSNES" : "Snes9x", NULL); + + total_duration = (guint64) (gst_spc_duration (spc) + gst_spc_fadeout (spc)); + + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, + GST_TAG_DURATION, total_duration, + GST_TAG_GENRE, "Game", GST_TAG_CODEC, "SPC700", NULL); + + gst_element_found_tags_for_pad (GST_ELEMENT (spc), spc->srcpad, taglist); + + /* spc_tag_info_free(&info); */ + + gme_err = + gme_open_data (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), + &spc->player, 32000); + if (gme_err || !spc->player) { + if (spc->player) { + gme_delete (spc->player); + spc->player = NULL; + } + + GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); + + return FALSE; + } +#ifdef HAVE_LIBGME_ACCURACY + /* TODO: Is it worth it to make this optional? */ + gme_enable_accuracy (spc->player, 1); +#endif + gme_start_track (spc->player, 0); + + gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, 1.0, + GST_FORMAT_TIME, 0, -1, 0)); + + gst_pad_start_task (spc->srcpad, (GstTaskFunction) gst_spc_play, spc->srcpad); + + /* We can't unreference this buffer because we might need to re-initialize + * the emulator with the original data during a reverse seek + * gst_buffer_unref (spc->buf); + * spc->buf = NULL; + */ + spc->initialized = TRUE; + spc->seeking = FALSE; + spc->seekpoint = 0; + return spc->initialized; +} + +static GstStateChangeReturn +gst_spc_dec_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn result; + GstSpcDec *dec; + + dec = GST_SPC_DEC (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + default: + break; + } + + result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (result == GST_STATE_CHANGE_FAILURE) + return result; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + if (dec->buf) { + gst_buffer_unref (dec->buf); + dec->buf = NULL; + } + break; + default: + break; + } + + return result; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "spcdec", GST_RANK_PRIMARY, + GST_TYPE_SPC_DEC); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "spcdec", + "OpenSPC Audio Decoder", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/ext/gme/gstgme.h b/ext/gme/gstgme.h new file mode 100644 index 00000000..3d7e4899 --- /dev/null +++ b/ext/gme/gstgme.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2004-2005, 2009 Michael Pyne + * Copyright (C) 2004-2006 Chris Lee + * Copyright (C) 2007 Brian Koropoff + * + * 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. + */ + +#ifndef __GST_SPC_DEC_H__ +#define __GST_SPC_DEC_H__ + +#include + +#include +#include "tag.h" + +G_BEGIN_DECLS + +#define GST_TYPE_SPC_DEC \ + (gst_spc_dec_get_type()) +#define GST_SPC_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPC_DEC,GstSpcDec)) +#define GST_SPC_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SPC_DEC,GstSpcDecClass)) +#define GST_IS_SPC_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SPC_DEC)) +#define GST_IS_SPC_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPC_DEC)) + +typedef struct _GstSpcDec GstSpcDec; +typedef struct _GstSpcDecClass GstSpcDecClass; + +struct _GstSpcDec +{ + GstElement element; + + GstPad *sinkpad; + GstPad *srcpad; + + GstBuffer *buf; + Music_Emu *player; + gboolean initialized; + gboolean seeking; + int seekpoint; + + spc_tag_info tag_info; +}; + +struct _GstSpcDecClass +{ + GstElementClass parent_class; +}; + +G_END_DECLS + +#endif /* __GST_SPC_DEC_H__ */ diff --git a/ext/gme/tag.c b/ext/gme/tag.c new file mode 100644 index 00000000..41ce812f --- /dev/null +++ b/ext/gme/tag.c @@ -0,0 +1,353 @@ +/* Copyright (C) 2007 Brian Koropoff + * + * 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. + */ + +#include +#include +#include +#include + +#include +#include +#include + + +#define EXTENDED_OFFSET 0x10200 +#define EXTENDED_MAGIC ((guint32) ('x' << 0 | 'i' << 8 | 'd' << 16 | '6' << 24)) + +#define TYPE_LENGTH 0x0 +#define TYPE_STRING 0x1 +#define TYPE_INTEGER 0x4 + +#define TAG_TITLE 0x01 +#define TAG_GAME 0x02 +#define TAG_ARTIST 0x03 +#define TAG_DUMPER 0x04 +#define TAG_DUMP_DATE 0x05 +#define TAG_EMULATOR 0x06 +#define TAG_COMMENT 0x07 +#define TAG_ALBUM 0x10 +#define TAG_DISC 0x11 +#define TAG_TRACK 0x12 +#define TAG_PUBLISHER 0x13 +#define TAG_YEAR 0x14 +#define TAG_INTRO 0x30 +#define TAG_LOOP 0x31 +#define TAG_END 0x32 +#define TAG_FADE 0x33 +#define TAG_MUTED 0x34 +#define TAG_COUNT 0x35 +#define TAG_AMP 0x36 + +#define READ_INT8(data, offset) (data[offset]) +#define READ_INT16(data, offset) ((data[offset] << 0) + (data[offset+1] << 8)) +#define READ_INT24(data, offset) ((data[offset] << 0) + (data[offset+1] << 8) + (data[offset+2] << 16)) +#define READ_INT32(data, offset) ((data[offset] << 0) + (data[offset+1] << 8) + (data[offset+2] << 16) + (data[offset+3] << 24)) + +static inline gboolean +spc_tag_is_extended (guchar * data, guint length) +{ + // Extended tags come at the end of the file (at a known offset) + // and start with "xid6" + return (length > EXTENDED_OFFSET + 4 + && READ_INT32 (data, EXTENDED_OFFSET) == EXTENDED_MAGIC); +} + +static inline gboolean +spc_tag_is_text_format (guchar * data, guint length) +{ + // Because the id666 format is brain dead, there's + // no definite way to decide if it is in text + // format. This function implements a set of + // heuristics to make a best-effort guess. + + // If the date field contains separators, it is probably text + if (data[0xA0] == '/' || data[0xA0] == '.') + return TRUE; + // If the first byte of the date field is small (but not 0, + // which could indicate an empty string), it's probably binary + if (data[0x9E] >= 1 && data[0x9E] <= 31) + return FALSE; + + // If all previous tests turned up nothing, assume it's text + return TRUE; +} + +static inline gboolean +spc_tag_is_present (guchar * data, guint length) +{ + return data[0x23] == 26; +} + +static inline GDate * +spc_tag_unpack_date (guint32 packed) +{ + guint dump_year = packed / 10000; + guint dump_month = (packed % 10000) / 100; + guint dump_day = packed % 100; + + if (dump_month == 0) + dump_month = 1; + if (dump_day == 0) + dump_day = 1; + + if (dump_year != 0) + return g_date_new_dmy (dump_day, dump_month, dump_year); + else + return NULL; +} + +void +spc_tag_clear (spc_tag_info * info) +{ + info->title = info->game = info->publisher = info->artist = info->album = + info->comment = info->dumper = NULL; + info->dump_date = NULL; + info->time_seconds = 0; + info->time_fade_milliseconds = 0; + info->time_intro = 0; + info->time_end = 0; + info->time_loop = 0; + info->time_fade = 0; + info->loop_count = 0; + info->muted = 0; + info->disc = 0; + info->amplification = 0; +} + +void +spc_tag_get_info (guchar * data, guint length, spc_tag_info * info) +{ + spc_tag_clear (info); + + if (spc_tag_is_present (data, length)) { + gboolean text_format = spc_tag_is_text_format (data, length); + + info->title = g_new0 (gchar, 0x21); + info->game = g_new0 (gchar, 0x21); + info->artist = g_new0 (gchar, 0x21); + info->dumper = g_new0 (gchar, 0x10); + info->comment = g_new0 (gchar, 0x32); + + strncpy (info->title, (gchar *) & data[0x2E], 32); + strncpy (info->artist, (gchar *) & data[(text_format ? 0xB1 : 0xB0)], 32); + strncpy (info->game, (gchar *) & data[0x4E], 32); + strncpy (info->dumper, (gchar *) & data[0x6E], 16); + strncpy (info->comment, (gchar *) & data[0x7E], 32); + + if (text_format) { + gchar time[4]; + gchar fade[6]; + guint dump_year, dump_month, dump_day; + + strncpy (time, (gchar *) data + 0xA9, 3); + strncpy (fade, (gchar *) data + 0xAC, 5); + + time[3] = fade[5] = 0; + + info->time_seconds = atoi (time); + info->time_fade_milliseconds = atoi (fade); + + dump_year = (guint) atoi ((gchar *) data + 0x9E); + dump_month = (guint) atoi ((gchar *) data + 0x9E + 3); + dump_day = (guint) atoi ((gchar *) data + 0x9E + 3 + 3); + + if (dump_month == 0) + dump_month = 1; + if (dump_day == 0) + dump_day = 1; + if (dump_year != 0) + info->dump_date = g_date_new_dmy (dump_day, dump_month, dump_year); + + info->muted = READ_INT8 (data, 0xD1); + info->emulator = READ_INT8 (data, 0xD2); + } else { + info->time_seconds = READ_INT24 (data, 0xA9); + info->time_fade_milliseconds = READ_INT32 (data, 0xAC); + info->dump_date = spc_tag_unpack_date (READ_INT32 (data, 0x9E)); + info->muted = READ_INT8 (data, 0xD0); + info->emulator = READ_INT8 (data, 0xD1); + } + } + + if (spc_tag_is_extended (data, length)) { + guchar *chunk = data + EXTENDED_OFFSET + 8; + guint32 chunk_size = *((guint32 *) (data + EXTENDED_OFFSET + 4)); + + guchar *subchunk, *subchunk_next; + + for (subchunk = chunk; subchunk < chunk + chunk_size; + subchunk = subchunk_next) { + guint8 tag = READ_INT8 (subchunk, 0); + guint8 type = READ_INT8 (subchunk, 1); + guint16 length = READ_INT16 (subchunk, 2); + guchar *value = subchunk + 4; + + switch (type) { + case TYPE_LENGTH: + { + switch (tag) { + case TAG_TRACK: + info->track = READ_INT8 (subchunk, 2 + 1); + break; + case TAG_YEAR: + info->year = READ_INT16 (subchunk, 2); + break; + case TAG_COUNT: + info->loop_count = READ_INT8 (subchunk, 2); + break; + case TAG_EMULATOR: + info->emulator = READ_INT8 (subchunk, 2); + break; + case TAG_DISC: + info->disc = READ_INT8 (subchunk, 2); + break; + case TAG_MUTED: + info->muted = READ_INT8 (subchunk, 2); + break; + default: + break; + } + + subchunk_next = subchunk + 4; + break; + } + case TYPE_STRING: + { + gchar *dest; + + if (length <= 1) + dest = NULL; + else + switch (tag) { + case TAG_TITLE: + dest = info->title = g_renew (gchar, info->title, length); + break; + case TAG_GAME: + dest = info->game = g_renew (gchar, info->game, length); + break; + case TAG_ARTIST: + dest = info->artist = g_renew (gchar, info->artist, length); + break; + case TAG_ALBUM: + dest = info->album = g_renew (gchar, info->album, length); + break; + case TAG_DUMPER: + dest = info->dumper = g_renew (gchar, info->dumper, length); + break; + case TAG_COMMENT: + dest = info->comment = g_renew (gchar, info->comment, length); + break; + case TAG_PUBLISHER: + dest = info->publisher = + g_renew (gchar, info->publisher, length); + break; + default: + dest = NULL; + break; + } + + if (dest) + strncpy (dest, (gchar *) value, length); + + subchunk_next = value + length; + break; + } + case TYPE_INTEGER: + { + switch (tag) { + case TAG_INTRO: + info->time_intro = READ_INT32 (value, 0); + break; + case TAG_END: + info->time_end = READ_INT32 (value, 0); + break; + case TAG_FADE: + info->time_fade = READ_INT32 (value, 0); + break; + case TAG_LOOP: + info->time_loop = READ_INT32 (value, 0); + break; + case TAG_DUMP_DATE: + info->dump_date = spc_tag_unpack_date (READ_INT32 (value, 0)); + break; + case TAG_AMP: + info->amplification = READ_INT32 (value, 0); + break; + default: + break; + } + subchunk_next = value + length; + break; + } + default: + subchunk_next = value + length; + break; + } + } + } + + if (info->title && !*info->title) { + g_free (info->title); + info->title = NULL; + } + if (info->game && !*info->game) { + g_free (info->game); + info->game = NULL; + } + if (info->artist && !*info->artist) { + g_free (info->artist); + info->artist = NULL; + } + if (info->album && !*info->album) { + g_free (info->album); + info->album = NULL; + } + if (info->publisher && !*info->publisher) { + g_free (info->publisher); + info->publisher = NULL; + } + if (info->comment && !*info->comment) { + g_free (info->comment); + info->comment = NULL; + } + if (info->dumper && !*info->dumper) { + g_free (info->dumper); + info->dumper = NULL; + } +} + +void +spc_tag_free (spc_tag_info * info) +{ + if (info->title) + g_free (info->title); + if (info->game) + g_free (info->game); + if (info->artist) + g_free (info->artist); + if (info->album) + g_free (info->album); + if (info->publisher) + g_free (info->publisher); + if (info->comment) + g_free (info->comment); + if (info->dumper) + g_free (info->dumper); + if (info->dump_date) + g_date_free (info->dump_date); +} diff --git a/ext/gme/tag.h b/ext/gme/tag.h new file mode 100644 index 00000000..5b795893 --- /dev/null +++ b/ext/gme/tag.h @@ -0,0 +1,37 @@ +/* Copyright (C) 2007 Brian Koropoff + * + * 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. + */ + +#include +#include + +typedef struct +{ + gchar *title, *game, *artist, *album, *publisher; + gchar *dumper, *comment; + enum { EMU_SNES9X = 2, EMU_ZSNES = 1, EMU_UNKNOWN = 0 } emulator; + guint8 track, disc, muted, loop_count; + guint16 year; + guint32 time_seconds, time_fade_milliseconds; + guint32 time_intro, time_loop, time_end, time_fade; + guint32 amplification; + GDate *dump_date; +} spc_tag_info; + +void spc_tag_clear(spc_tag_info* info); +void spc_tag_get_info(guchar* data, guint length, spc_tag_info* info); +void spc_tag_free(spc_tag_info* info); -- cgit v1.2.1 From e3cc12769288a4cdd4ba06c501451ebfec942ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 7 Aug 2009 06:56:54 +0200 Subject: gme: Use gme for tags and duration parsing --- ext/gme/Makefile.am | 4 +- ext/gme/gstgme.c | 149 +++++++--------------- ext/gme/gstgme.h | 3 +- ext/gme/tag.c | 353 ---------------------------------------------------- ext/gme/tag.h | 37 ------ 5 files changed, 45 insertions(+), 501 deletions(-) delete mode 100644 ext/gme/tag.c delete mode 100644 ext/gme/tag.h (limited to 'ext') diff --git a/ext/gme/Makefile.am b/ext/gme/Makefile.am index c2bdbfea..c84d340e 100644 --- a/ext/gme/Makefile.am +++ b/ext/gme/Makefile.am @@ -1,10 +1,10 @@ plugin_LTLIBRARIES = libgstgme.la -libgstgme_la_SOURCES = gstgme.c tag.c +libgstgme_la_SOURCES = gstgme.c libgstgme_la_CFLAGS = $(GST_CFLAGS) $(GME_CFLAGS) libgstgme_la_LIBADD = $(GST_LIBS) $(GME_LIBS) libgstgme_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstgme_la_LIBTOOLFLAGS = --tag=disable-static -noinst_HEADERS = gstgme.h tag.h +noinst_HEADERS = gstgme.h diff --git a/ext/gme/gstgme.c b/ext/gme/gstgme.c index 0315d6d0..0efb8d8a 100644 --- a/ext/gme/gstgme.c +++ b/ext/gme/gstgme.c @@ -158,8 +158,8 @@ gst_spc_dec_init (GstSpcDec * spc, GstSpcDecClass * klass) spc->buf = NULL; spc->player = NULL; + spc->total_duration = GST_CLOCK_TIME_NONE; spc->initialized = FALSE; - spc_tag_clear (&spc->tag_info); } static void @@ -171,8 +171,6 @@ gst_spc_dec_dispose (GObject * object) gst_buffer_unref (spc->buf); spc->buf = NULL; } - - spc_tag_free (&spc->tag_info); } static GstFlowReturn @@ -223,38 +221,6 @@ gst_spc_dec_sink_event (GstPad * pad, GstEvent * event) return result; } -static gint64 -gst_spc_duration (GstSpcDec * spc) -{ - gint64 total_ticks = - spc->tag_info.time_intro + - spc->tag_info.time_loop * spc->tag_info.loop_count + - spc->tag_info.time_end; - if (total_ticks) { - return (gint64) gst_util_uint64_scale (total_ticks, GST_SECOND, 64000); - } else if (spc->tag_info.time_seconds) { - gint64 time = (gint64) spc->tag_info.time_seconds * GST_SECOND; - - return time; - } else { - return (gint64) (3 * 60) * GST_SECOND; - } -} - -static gint64 -gst_spc_fadeout (GstSpcDec * spc) -{ - if (spc->tag_info.time_fade) { - return (gint64) gst_util_uint64_scale ((guint64) spc->tag_info.time_fade, - GST_SECOND, 64000); - } else if (spc->tag_info.time_fade_milliseconds) { - return (gint64) (spc->tag_info.time_fade_milliseconds * GST_MSECOND); - } else { - return 10 * GST_SECOND; - } -} - - static gboolean gst_spc_dec_src_event (GstPad * pad, GstEvent * event) { @@ -293,7 +259,10 @@ gst_spc_dec_src_event (GstPad * pad, GstEvent * event) guint64 cur = gme_tell (spc->player) * GST_MSECOND; guint64 dest = (guint64) start; - dest = CLAMP (dest, 0, gst_spc_duration (spc) + gst_spc_fadeout (spc)); + if (spc->total_duration != GST_CLOCK_TIME_NONE) + dest = CLAMP (dest, 0, spc->total_duration); + else + dest = MAX (0, dest); if (dest == cur) break; @@ -317,8 +286,9 @@ gst_spc_dec_src_event (GstPad * pad, GstEvent * event) gst_pad_push_event (spc->srcpad, gst_event_new_flush_stop ()); } - if (stop == GST_CLOCK_TIME_NONE) - stop = (guint64) (gst_spc_duration (spc) + gst_spc_fadeout (spc)); + if (stop == GST_CLOCK_TIME_NONE + && spc->total_duration != GST_CLOCK_TIME_NONE) + stop = spc->total_duration; gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, rate, GST_FORMAT_TIME, dest, stop, dest)); @@ -356,12 +326,12 @@ gst_spc_dec_src_query (GstPad * pad, GstQuery * query) GstFormat format; gst_query_parse_duration (query, &format, NULL); - if (!spc->initialized || format != GST_FORMAT_TIME) { + if (!spc->initialized || format != GST_FORMAT_TIME + || spc->total_duration == GST_CLOCK_TIME_NONE) { result = FALSE; break; } - gst_query_set_duration (query, GST_FORMAT_TIME, - gst_spc_duration (spc) + gst_spc_fadeout (spc)); + gst_query_set_duration (query, GST_FORMAT_TIME, spc->total_duration); break; } case GST_QUERY_POSITION: @@ -395,7 +365,6 @@ gst_spc_play (GstPad * pad) GstBuffer *out; gboolean seeking = spc->seeking; gme_err_t gme_err = NULL; - gint64 duration, fade, end, position; const int NUM_SAMPLES = 1600; /* 4 bytes (stereo 16-bit) per sample */ if (!seeking) { @@ -421,24 +390,6 @@ gst_spc_play (GstPad * pad) gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); } - duration = gst_spc_duration (spc); - fade = gst_spc_fadeout (spc); - end = duration + fade; - position = gme_tell (spc->player) * GST_MSECOND; - - if (position >= duration) { - gint16 *data = (gint16 *) GST_BUFFER_DATA (out); - guint32 size = GST_BUFFER_SIZE (out) / sizeof (gint16); - unsigned int i; - - gint64 num = (fade - (position - duration)); - - for (i = 0; i < size; i++) { - /* Apply a parabolic volume envelope */ - data[i] = (gint16) (data[i] * num / fade * num / fade); - } - } - if ((flow_return = gst_pad_push (spc->srcpad, out)) != GST_FLOW_OK) { GST_DEBUG_OBJECT (spc, "pausing task, reason %s", gst_flow_get_name (flow_return)); @@ -450,7 +401,7 @@ gst_spc_play (GstPad * pad) } } - if (position >= end) { + if (gme_track_ended (spc->player)) { gst_pad_pause_task (pad); gst_pad_push_event (pad, gst_event_new_eos ()); } @@ -463,7 +414,7 @@ gst_spc_play (GstPad * pad) static gboolean spc_setup (GstSpcDec * spc) { - spc_tag_info *info; + gme_info_t *info; gme_err_t gme_err = NULL; GstTagList *taglist; guint64 total_duration; @@ -472,77 +423,60 @@ spc_setup (GstSpcDec * spc) return FALSE; } - info = &(spc->tag_info); + gme_err = + gme_open_data (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), + &spc->player, 32000); + if (gme_err || !spc->player) { + if (spc->player) { + gme_delete (spc->player); + spc->player = NULL; + } + + GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); + + return FALSE; + } - spc_tag_get_info (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), - info); + gme_err = gme_track_info (spc->player, &info, 0); taglist = gst_tag_list_new (); - if (info->title) + if (info->song) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, - info->title, NULL); - if (info->artist) + info->song, NULL); + if (info->author) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ARTIST, - info->artist, NULL); + info->author, NULL); /* Prefer the name of the official soundtrack over the name of the game (since this is * how track numbers are derived) */ - if (info->album) - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, - info->album, NULL); - else if (info->game) + if (info->game) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, info->game, NULL); - if (info->year) { - GDate *date = g_date_new_dmy (1, 1, info->year); - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date, NULL); - g_date_free (date); - } - if (info->track) { - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_NUMBER, - info->track, NULL); - } if (info->comment) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COMMENT, info->comment, NULL); - if (info->disc) - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_ALBUM_VOLUME_NUMBER, info->disc, NULL); - if (info->publisher) - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION, - info->publisher, NULL); if (info->dumper) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_CONTACT, info->dumper, NULL); - if (info->emulator) + if (info->copyright) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COPYRIGHT, + info->copyright, NULL); + if (info->system) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, - info->emulator == EMU_ZSNES ? "ZSNES" : "Snes9x", NULL); + info->system, NULL); - total_duration = (guint64) (gst_spc_duration (spc) + gst_spc_fadeout (spc)); + spc->total_duration = total_duration = + gst_util_uint64_scale_int (info->play_length, GST_MSECOND, 1); gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_DURATION, total_duration, - GST_TAG_GENRE, "Game", GST_TAG_CODEC, "SPC700", NULL); + GST_TAG_DURATION, total_duration, NULL); gst_element_found_tags_for_pad (GST_ELEMENT (spc), spc->srcpad, taglist); - /* spc_tag_info_free(&info); */ + g_free (info); - gme_err = - gme_open_data (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), - &spc->player, 32000); - if (gme_err || !spc->player) { - if (spc->player) { - gme_delete (spc->player); - spc->player = NULL; - } - - GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); - - return FALSE; - } #ifdef HAVE_LIBGME_ACCURACY /* TODO: Is it worth it to make this optional? */ gme_enable_accuracy (spc->player, 1); @@ -575,6 +509,7 @@ gst_spc_dec_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: + dec->total_duration = GST_CLOCK_TIME_NONE; break; default: break; diff --git a/ext/gme/gstgme.h b/ext/gme/gstgme.h index 3d7e4899..b4ab17a5 100644 --- a/ext/gme/gstgme.h +++ b/ext/gme/gstgme.h @@ -24,7 +24,6 @@ #include #include -#include "tag.h" G_BEGIN_DECLS @@ -55,7 +54,7 @@ struct _GstSpcDec gboolean seeking; int seekpoint; - spc_tag_info tag_info; + GstClockTime total_duration; }; struct _GstSpcDecClass diff --git a/ext/gme/tag.c b/ext/gme/tag.c deleted file mode 100644 index 41ce812f..00000000 --- a/ext/gme/tag.c +++ /dev/null @@ -1,353 +0,0 @@ -/* Copyright (C) 2007 Brian Koropoff - * - * 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. - */ - -#include -#include -#include -#include - -#include -#include -#include - - -#define EXTENDED_OFFSET 0x10200 -#define EXTENDED_MAGIC ((guint32) ('x' << 0 | 'i' << 8 | 'd' << 16 | '6' << 24)) - -#define TYPE_LENGTH 0x0 -#define TYPE_STRING 0x1 -#define TYPE_INTEGER 0x4 - -#define TAG_TITLE 0x01 -#define TAG_GAME 0x02 -#define TAG_ARTIST 0x03 -#define TAG_DUMPER 0x04 -#define TAG_DUMP_DATE 0x05 -#define TAG_EMULATOR 0x06 -#define TAG_COMMENT 0x07 -#define TAG_ALBUM 0x10 -#define TAG_DISC 0x11 -#define TAG_TRACK 0x12 -#define TAG_PUBLISHER 0x13 -#define TAG_YEAR 0x14 -#define TAG_INTRO 0x30 -#define TAG_LOOP 0x31 -#define TAG_END 0x32 -#define TAG_FADE 0x33 -#define TAG_MUTED 0x34 -#define TAG_COUNT 0x35 -#define TAG_AMP 0x36 - -#define READ_INT8(data, offset) (data[offset]) -#define READ_INT16(data, offset) ((data[offset] << 0) + (data[offset+1] << 8)) -#define READ_INT24(data, offset) ((data[offset] << 0) + (data[offset+1] << 8) + (data[offset+2] << 16)) -#define READ_INT32(data, offset) ((data[offset] << 0) + (data[offset+1] << 8) + (data[offset+2] << 16) + (data[offset+3] << 24)) - -static inline gboolean -spc_tag_is_extended (guchar * data, guint length) -{ - // Extended tags come at the end of the file (at a known offset) - // and start with "xid6" - return (length > EXTENDED_OFFSET + 4 - && READ_INT32 (data, EXTENDED_OFFSET) == EXTENDED_MAGIC); -} - -static inline gboolean -spc_tag_is_text_format (guchar * data, guint length) -{ - // Because the id666 format is brain dead, there's - // no definite way to decide if it is in text - // format. This function implements a set of - // heuristics to make a best-effort guess. - - // If the date field contains separators, it is probably text - if (data[0xA0] == '/' || data[0xA0] == '.') - return TRUE; - // If the first byte of the date field is small (but not 0, - // which could indicate an empty string), it's probably binary - if (data[0x9E] >= 1 && data[0x9E] <= 31) - return FALSE; - - // If all previous tests turned up nothing, assume it's text - return TRUE; -} - -static inline gboolean -spc_tag_is_present (guchar * data, guint length) -{ - return data[0x23] == 26; -} - -static inline GDate * -spc_tag_unpack_date (guint32 packed) -{ - guint dump_year = packed / 10000; - guint dump_month = (packed % 10000) / 100; - guint dump_day = packed % 100; - - if (dump_month == 0) - dump_month = 1; - if (dump_day == 0) - dump_day = 1; - - if (dump_year != 0) - return g_date_new_dmy (dump_day, dump_month, dump_year); - else - return NULL; -} - -void -spc_tag_clear (spc_tag_info * info) -{ - info->title = info->game = info->publisher = info->artist = info->album = - info->comment = info->dumper = NULL; - info->dump_date = NULL; - info->time_seconds = 0; - info->time_fade_milliseconds = 0; - info->time_intro = 0; - info->time_end = 0; - info->time_loop = 0; - info->time_fade = 0; - info->loop_count = 0; - info->muted = 0; - info->disc = 0; - info->amplification = 0; -} - -void -spc_tag_get_info (guchar * data, guint length, spc_tag_info * info) -{ - spc_tag_clear (info); - - if (spc_tag_is_present (data, length)) { - gboolean text_format = spc_tag_is_text_format (data, length); - - info->title = g_new0 (gchar, 0x21); - info->game = g_new0 (gchar, 0x21); - info->artist = g_new0 (gchar, 0x21); - info->dumper = g_new0 (gchar, 0x10); - info->comment = g_new0 (gchar, 0x32); - - strncpy (info->title, (gchar *) & data[0x2E], 32); - strncpy (info->artist, (gchar *) & data[(text_format ? 0xB1 : 0xB0)], 32); - strncpy (info->game, (gchar *) & data[0x4E], 32); - strncpy (info->dumper, (gchar *) & data[0x6E], 16); - strncpy (info->comment, (gchar *) & data[0x7E], 32); - - if (text_format) { - gchar time[4]; - gchar fade[6]; - guint dump_year, dump_month, dump_day; - - strncpy (time, (gchar *) data + 0xA9, 3); - strncpy (fade, (gchar *) data + 0xAC, 5); - - time[3] = fade[5] = 0; - - info->time_seconds = atoi (time); - info->time_fade_milliseconds = atoi (fade); - - dump_year = (guint) atoi ((gchar *) data + 0x9E); - dump_month = (guint) atoi ((gchar *) data + 0x9E + 3); - dump_day = (guint) atoi ((gchar *) data + 0x9E + 3 + 3); - - if (dump_month == 0) - dump_month = 1; - if (dump_day == 0) - dump_day = 1; - if (dump_year != 0) - info->dump_date = g_date_new_dmy (dump_day, dump_month, dump_year); - - info->muted = READ_INT8 (data, 0xD1); - info->emulator = READ_INT8 (data, 0xD2); - } else { - info->time_seconds = READ_INT24 (data, 0xA9); - info->time_fade_milliseconds = READ_INT32 (data, 0xAC); - info->dump_date = spc_tag_unpack_date (READ_INT32 (data, 0x9E)); - info->muted = READ_INT8 (data, 0xD0); - info->emulator = READ_INT8 (data, 0xD1); - } - } - - if (spc_tag_is_extended (data, length)) { - guchar *chunk = data + EXTENDED_OFFSET + 8; - guint32 chunk_size = *((guint32 *) (data + EXTENDED_OFFSET + 4)); - - guchar *subchunk, *subchunk_next; - - for (subchunk = chunk; subchunk < chunk + chunk_size; - subchunk = subchunk_next) { - guint8 tag = READ_INT8 (subchunk, 0); - guint8 type = READ_INT8 (subchunk, 1); - guint16 length = READ_INT16 (subchunk, 2); - guchar *value = subchunk + 4; - - switch (type) { - case TYPE_LENGTH: - { - switch (tag) { - case TAG_TRACK: - info->track = READ_INT8 (subchunk, 2 + 1); - break; - case TAG_YEAR: - info->year = READ_INT16 (subchunk, 2); - break; - case TAG_COUNT: - info->loop_count = READ_INT8 (subchunk, 2); - break; - case TAG_EMULATOR: - info->emulator = READ_INT8 (subchunk, 2); - break; - case TAG_DISC: - info->disc = READ_INT8 (subchunk, 2); - break; - case TAG_MUTED: - info->muted = READ_INT8 (subchunk, 2); - break; - default: - break; - } - - subchunk_next = subchunk + 4; - break; - } - case TYPE_STRING: - { - gchar *dest; - - if (length <= 1) - dest = NULL; - else - switch (tag) { - case TAG_TITLE: - dest = info->title = g_renew (gchar, info->title, length); - break; - case TAG_GAME: - dest = info->game = g_renew (gchar, info->game, length); - break; - case TAG_ARTIST: - dest = info->artist = g_renew (gchar, info->artist, length); - break; - case TAG_ALBUM: - dest = info->album = g_renew (gchar, info->album, length); - break; - case TAG_DUMPER: - dest = info->dumper = g_renew (gchar, info->dumper, length); - break; - case TAG_COMMENT: - dest = info->comment = g_renew (gchar, info->comment, length); - break; - case TAG_PUBLISHER: - dest = info->publisher = - g_renew (gchar, info->publisher, length); - break; - default: - dest = NULL; - break; - } - - if (dest) - strncpy (dest, (gchar *) value, length); - - subchunk_next = value + length; - break; - } - case TYPE_INTEGER: - { - switch (tag) { - case TAG_INTRO: - info->time_intro = READ_INT32 (value, 0); - break; - case TAG_END: - info->time_end = READ_INT32 (value, 0); - break; - case TAG_FADE: - info->time_fade = READ_INT32 (value, 0); - break; - case TAG_LOOP: - info->time_loop = READ_INT32 (value, 0); - break; - case TAG_DUMP_DATE: - info->dump_date = spc_tag_unpack_date (READ_INT32 (value, 0)); - break; - case TAG_AMP: - info->amplification = READ_INT32 (value, 0); - break; - default: - break; - } - subchunk_next = value + length; - break; - } - default: - subchunk_next = value + length; - break; - } - } - } - - if (info->title && !*info->title) { - g_free (info->title); - info->title = NULL; - } - if (info->game && !*info->game) { - g_free (info->game); - info->game = NULL; - } - if (info->artist && !*info->artist) { - g_free (info->artist); - info->artist = NULL; - } - if (info->album && !*info->album) { - g_free (info->album); - info->album = NULL; - } - if (info->publisher && !*info->publisher) { - g_free (info->publisher); - info->publisher = NULL; - } - if (info->comment && !*info->comment) { - g_free (info->comment); - info->comment = NULL; - } - if (info->dumper && !*info->dumper) { - g_free (info->dumper); - info->dumper = NULL; - } -} - -void -spc_tag_free (spc_tag_info * info) -{ - if (info->title) - g_free (info->title); - if (info->game) - g_free (info->game); - if (info->artist) - g_free (info->artist); - if (info->album) - g_free (info->album); - if (info->publisher) - g_free (info->publisher); - if (info->comment) - g_free (info->comment); - if (info->dumper) - g_free (info->dumper); - if (info->dump_date) - g_date_free (info->dump_date); -} diff --git a/ext/gme/tag.h b/ext/gme/tag.h deleted file mode 100644 index 5b795893..00000000 --- a/ext/gme/tag.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright (C) 2007 Brian Koropoff - * - * 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. - */ - -#include -#include - -typedef struct -{ - gchar *title, *game, *artist, *album, *publisher; - gchar *dumper, *comment; - enum { EMU_SNES9X = 2, EMU_ZSNES = 1, EMU_UNKNOWN = 0 } emulator; - guint8 track, disc, muted, loop_count; - guint16 year; - guint32 time_seconds, time_fade_milliseconds; - guint32 time_intro, time_loop, time_end, time_fade; - guint32 amplification; - GDate *dump_date; -} spc_tag_info; - -void spc_tag_clear(spc_tag_info* info); -void spc_tag_get_info(guchar* data, guint length, spc_tag_info* info); -void spc_tag_free(spc_tag_info* info); -- cgit v1.2.1 From 7ddfae8a4a1e3c361b8c78c6bee6a02a6862e321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 7 Aug 2009 06:59:41 +0200 Subject: spc: Really remove old SPC code --- ext/spc/Makefile.am | 10 - ext/spc/gstspc.c | 612 ---------------------------------------------------- ext/spc/gstspc.h | 68 ------ ext/spc/tag.c | 353 ------------------------------ ext/spc/tag.h | 37 ---- 5 files changed, 1080 deletions(-) delete mode 100644 ext/spc/Makefile.am delete mode 100644 ext/spc/gstspc.c delete mode 100644 ext/spc/gstspc.h delete mode 100644 ext/spc/tag.c delete mode 100644 ext/spc/tag.h (limited to 'ext') diff --git a/ext/spc/Makefile.am b/ext/spc/Makefile.am deleted file mode 100644 index 89f0f721..00000000 --- a/ext/spc/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ -plugin_LTLIBRARIES = libgstspc.la - -libgstspc_la_SOURCES = gstspc.c tag.c - -libgstspc_la_CFLAGS = $(GST_CFLAGS) $(SPC_CFLAGS) -libgstspc_la_LIBADD = $(GST_LIBS) $(SPC_LIBS) -libgstspc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstspc_la_LIBTOOLFLAGS = --tag=disable-static - -noinst_HEADERS = gstspc.h tag.h diff --git a/ext/spc/gstspc.c b/ext/spc/gstspc.c deleted file mode 100644 index c5ca2457..00000000 --- a/ext/spc/gstspc.c +++ /dev/null @@ -1,612 +0,0 @@ -/* Copyright (C) 2004-2005,2009 Michael Pyne - * Copyright (C) 2004-2006 Chris Lee - * Copyright (C) 2007 Brian Koropoff - * - * 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstspc.h" - -#include -#include -#include - -static const GstElementDetails gst_spc_dec_details = -GST_ELEMENT_DETAILS ("SNES SPC 700 decoder", - "Codec/Audio/Decoder", - "Uses Blargg's libgme to emulate an SPC processor", - "Chris Lee , Brian Koropoff , Michael Pyne "); - -static GstStaticPadTemplate sink_factory = -GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-spc")); - -static GstStaticPadTemplate src_factory = -GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " - "endianness = (int) BYTE_ORDER, " - "signed = (boolean) TRUE, " - "width = (int) 16, " - "depth = (int) 16, " "rate = (int) 32000, " "channels = (int) 2")); - -GST_BOILERPLATE (GstSpcDec, gst_spc_dec, GstElement, GST_TYPE_ELEMENT); - -static GstFlowReturn gst_spc_dec_chain (GstPad * pad, GstBuffer * buffer); -static gboolean gst_spc_dec_sink_event (GstPad * pad, GstEvent * event); -static gboolean gst_spc_dec_src_event (GstPad * pad, GstEvent * event); -static gboolean gst_spc_dec_src_query (GstPad * pad, GstQuery * query); -static GstStateChangeReturn gst_spc_dec_change_state (GstElement * element, - GstStateChange transition); -static void gst_spc_play (GstPad * pad); -static void gst_spc_dec_dispose (GObject * object); -static gboolean spc_setup (GstSpcDec * spc); - -static gboolean -spc_negotiate (GstSpcDec * spc) -{ - GstCaps *allowed, *caps; - GstStructure *structure; - gint width = 16, depth = 16; - gboolean sign; - int rate = 32000; - int channels = 2; - - allowed = gst_pad_get_allowed_caps (spc->srcpad); - if (!allowed) { - GST_DEBUG_OBJECT (spc, "couldn't get allowed caps"); - return FALSE; - } - - GST_DEBUG_OBJECT (spc, "allowed caps: %" GST_PTR_FORMAT, allowed); - - structure = gst_caps_get_structure (allowed, 0); - gst_structure_get_int (structure, "width", &width); - gst_structure_get_int (structure, "depth", &depth); - - if (width && depth && width != depth) { - GST_DEBUG_OBJECT (spc, "width %d and depth %d are different", width, depth); - gst_caps_unref (allowed); - return FALSE; - } - - gst_structure_get_boolean (structure, "signed", &sign); - gst_structure_get_int (structure, "rate", &rate); - gst_structure_get_int (structure, "channels", &channels); - - caps = gst_caps_new_simple ("audio/x-raw-int", - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "signed", G_TYPE_BOOLEAN, TRUE, - "width", G_TYPE_INT, width, - "depth", G_TYPE_INT, depth, - "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL); - gst_pad_set_caps (spc->srcpad, caps); - - gst_caps_unref (caps); - gst_caps_unref (allowed); - - return TRUE; -} - -static void -gst_spc_dec_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details (element_class, &gst_spc_dec_details); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_factory)); -} - -static void -gst_spc_dec_class_init (GstSpcDecClass * klass) -{ - GstElementClass *element_class = (GstElementClass *) klass; - GObjectClass *gobject_class = (GObjectClass *) klass; - - element_class->change_state = GST_DEBUG_FUNCPTR (gst_spc_dec_change_state); - gobject_class->dispose = gst_spc_dec_dispose; -} - -static const GstQueryType * -gst_spc_dec_src_query_type (GstPad * pad) -{ - static const GstQueryType query_types[] = { - GST_QUERY_DURATION, - GST_QUERY_POSITION, - (GstQueryType) 0 - }; - - return query_types; -} - - -static void -gst_spc_dec_init (GstSpcDec * spc, GstSpcDecClass * klass) -{ - spc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); - /* gst_pad_set_query_function (spc->sinkpad, NULL); */ - gst_pad_set_event_function (spc->sinkpad, gst_spc_dec_sink_event); - gst_pad_set_chain_function (spc->sinkpad, gst_spc_dec_chain); - gst_element_add_pad (GST_ELEMENT (spc), spc->sinkpad); - - spc->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); - gst_pad_set_event_function (spc->srcpad, gst_spc_dec_src_event); - gst_pad_set_query_function (spc->srcpad, gst_spc_dec_src_query); - gst_pad_set_query_type_function (spc->srcpad, gst_spc_dec_src_query_type); - gst_pad_use_fixed_caps (spc->srcpad); - gst_element_add_pad (GST_ELEMENT (spc), spc->srcpad); - - spc->buf = NULL; - spc->player = NULL; - spc->initialized = FALSE; - spc_tag_clear (&spc->tag_info); -} - -static void -gst_spc_dec_dispose (GObject * object) -{ - GstSpcDec *spc = GST_SPC_DEC (object); - - if (spc->buf) { - gst_buffer_unref (spc->buf); - spc->buf = NULL; - } - - spc_tag_free (&spc->tag_info); -} - -static GstFlowReturn -gst_spc_dec_chain (GstPad * pad, GstBuffer * buffer) -{ - GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); - - /* Accumulate SPC data until end-of-stream, then commence playback. */ - if (spc->buf) { - spc->buf = gst_buffer_join (spc->buf, buffer); - } else { - spc->buf = buffer; - } - - gst_object_unref (spc); - - return GST_FLOW_OK; -} - -static gboolean -gst_spc_dec_sink_event (GstPad * pad, GstEvent * event) -{ - GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); - gboolean result = TRUE; - gboolean forward = FALSE; - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - /* we get EOS when we loaded the complete file, now try to initialize the - * decoding */ - if (!(result = spc_setup (spc))) { - /* can't start, post an ERROR and push EOS downstream */ - GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), - ("can't start playback")); - forward = TRUE; - } - break; - default: - break; - } - if (forward) - result = gst_pad_push_event (spc->srcpad, event); - else - gst_event_unref (event); - - gst_object_unref (spc); - - return result; -} - -static gint64 -gst_spc_duration (GstSpcDec * spc) -{ - gint64 total_ticks = - spc->tag_info.time_intro + - spc->tag_info.time_loop * spc->tag_info.loop_count + - spc->tag_info.time_end; - if (total_ticks) { - return (gint64) gst_util_uint64_scale (total_ticks, GST_SECOND, 64000); - } else if (spc->tag_info.time_seconds) { - gint64 time = (gint64) spc->tag_info.time_seconds * GST_SECOND; - - return time; - } else { - return (gint64) (3 * 60) * GST_SECOND; - } -} - -static gint64 -gst_spc_fadeout (GstSpcDec * spc) -{ - if (spc->tag_info.time_fade) { - return (gint64) gst_util_uint64_scale ((guint64) spc->tag_info.time_fade, - GST_SECOND, 64000); - } else if (spc->tag_info.time_fade_milliseconds) { - return (gint64) (spc->tag_info.time_fade_milliseconds * GST_MSECOND); - } else { - return 10 * GST_SECOND; - } -} - - -static gboolean -gst_spc_dec_src_event (GstPad * pad, GstEvent * event) -{ - GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); - gboolean result = FALSE; - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - { - gdouble rate; - GstFormat format; - GstSeekFlags flags; - GstSeekType start_type, stop_type; - gint64 start, stop; - gboolean flush; - - gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, - &stop_type, &stop); - - gst_event_unref (event); - - if (format != GST_FORMAT_TIME) { - GST_DEBUG_OBJECT (spc, "seeking is only supported in TIME format"); - break; - } - - if (start_type != GST_SEEK_TYPE_SET || stop_type != GST_SEEK_TYPE_NONE) { - GST_DEBUG_OBJECT (spc, "unsupported seek type"); - break; - } - - if (stop_type == GST_SEEK_TYPE_NONE) - stop = GST_CLOCK_TIME_NONE; - - if (start_type == GST_SEEK_TYPE_SET) { - guint64 cur = gme_tell (spc->player) * GST_MSECOND; - guint64 dest = (guint64) start; - - dest = CLAMP (dest, 0, gst_spc_duration (spc) + gst_spc_fadeout (spc)); - - if (dest == cur) - break; - - flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH; - - if (flush) { - gst_pad_push_event (spc->srcpad, gst_event_new_flush_start ()); - } else { - gst_pad_stop_task (spc->srcpad); - } - - GST_PAD_STREAM_LOCK (spc->srcpad); - - if (flags & GST_SEEK_FLAG_SEGMENT) { - gst_element_post_message (GST_ELEMENT (spc), - gst_message_new_segment_start (GST_OBJECT (spc), format, cur)); - } - - if (flush) { - gst_pad_push_event (spc->srcpad, gst_event_new_flush_stop ()); - } - - if (stop == GST_CLOCK_TIME_NONE) - stop = (guint64) (gst_spc_duration (spc) + gst_spc_fadeout (spc)); - - gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, rate, - GST_FORMAT_TIME, dest, stop, dest)); - - spc->seekpoint = dest / GST_MSECOND; /* nsecs to msecs */ - spc->seeking = TRUE; - - gst_pad_start_task (spc->srcpad, (GstTaskFunction) gst_spc_play, - spc->srcpad); - - GST_PAD_STREAM_UNLOCK (spc->srcpad); - result = TRUE; - } - break; - } - default: - result = gst_pad_push_event (spc->sinkpad, event); - break; - } - - gst_object_unref (spc); - - return result; -} - -static gboolean -gst_spc_dec_src_query (GstPad * pad, GstQuery * query) -{ - GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); - gboolean result = TRUE; - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_DURATION: - { - GstFormat format; - - gst_query_parse_duration (query, &format, NULL); - if (!spc->initialized || format != GST_FORMAT_TIME) { - result = FALSE; - break; - } - gst_query_set_duration (query, GST_FORMAT_TIME, - gst_spc_duration (spc) + gst_spc_fadeout (spc)); - break; - } - case GST_QUERY_POSITION: - { - GstFormat format; - - gst_query_parse_position (query, &format, NULL); - if (!spc->initialized || format != GST_FORMAT_TIME) { - result = FALSE; - break; - } - gst_query_set_position (query, GST_FORMAT_TIME, - (gint64) gme_tell (spc->player) * GST_MSECOND); - break; - } - default: - result = gst_pad_query_default (pad, query); - break; - } - - gst_object_unref (spc); - - return result; -} - -static void -gst_spc_play (GstPad * pad) -{ - GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); - GstFlowReturn flow_return; - GstBuffer *out; - gboolean seeking = spc->seeking; - gme_err_t gme_err = NULL; - gint64 duration, fade, end, position; - const int NUM_SAMPLES = 1600; /* 4 bytes (stereo 16-bit) per sample */ - - if (!seeking) { - out = gst_buffer_new_and_alloc (NUM_SAMPLES * 4); - gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); - GST_BUFFER_TIMESTAMP (out) = gme_tell (spc->player) * GST_MSECOND; - - gme_err = - gme_play (spc->player, NUM_SAMPLES * 2, - (short *) GST_BUFFER_DATA (out)); - if (gme_err) { - GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); - gst_pad_pause_task (pad); - gst_pad_push_event (pad, gst_event_new_eos ()); - gst_object_unref (spc); - return; - } - } else { - gme_seek (spc->player, spc->seekpoint); - spc->seeking = FALSE; - - out = gst_buffer_new (); - gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); - } - - duration = gst_spc_duration (spc); - fade = gst_spc_fadeout (spc); - end = duration + fade; - position = gme_tell (spc->player) * GST_MSECOND; - - if (position >= duration) { - gint16 *data = (gint16 *) GST_BUFFER_DATA (out); - guint32 size = GST_BUFFER_SIZE (out) / sizeof (gint16); - unsigned int i; - - gint64 num = (fade - (position - duration)); - - for (i = 0; i < size; i++) { - /* Apply a parabolic volume envelope */ - data[i] = (gint16) (data[i] * num / fade * num / fade); - } - } - - if ((flow_return = gst_pad_push (spc->srcpad, out)) != GST_FLOW_OK) { - GST_DEBUG_OBJECT (spc, "pausing task, reason %s", - gst_flow_get_name (flow_return)); - - gst_pad_pause_task (pad); - - if (GST_FLOW_IS_FATAL (flow_return) || flow_return == GST_FLOW_NOT_LINKED) { - gst_pad_push_event (pad, gst_event_new_eos ()); - } - } - - if (position >= end) { - gst_pad_pause_task (pad); - gst_pad_push_event (pad, gst_event_new_eos ()); - } - - gst_object_unref (spc); - - return; -} - -static gboolean -spc_setup (GstSpcDec * spc) -{ - spc_tag_info *info; - gme_err_t gme_err = NULL; - GstTagList *taglist; - guint64 total_duration; - - if (!spc->buf || !spc_negotiate (spc)) { - return FALSE; - } - - info = &(spc->tag_info); - - spc_tag_get_info (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), - info); - - taglist = gst_tag_list_new (); - - if (info->title) - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, - info->title, NULL); - if (info->artist) - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ARTIST, - info->artist, NULL); - /* Prefer the name of the official soundtrack over the name of the game (since this is - * how track numbers are derived) - */ - if (info->album) - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, - info->album, NULL); - else if (info->game) - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, info->game, - NULL); - if (info->year) { - GDate *date = g_date_new_dmy (1, 1, info->year); - - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date, NULL); - g_date_free (date); - } - if (info->track) { - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_NUMBER, - info->track, NULL); - } - if (info->comment) - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COMMENT, - info->comment, NULL); - if (info->disc) - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_ALBUM_VOLUME_NUMBER, info->disc, NULL); - if (info->publisher) - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION, - info->publisher, NULL); - if (info->dumper) - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_CONTACT, - info->dumper, NULL); - if (info->emulator) - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, - info->emulator == EMU_ZSNES ? "ZSNES" : "Snes9x", NULL); - - total_duration = (guint64) (gst_spc_duration (spc) + gst_spc_fadeout (spc)); - - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_DURATION, total_duration, - GST_TAG_GENRE, "Game", GST_TAG_CODEC, "SPC700", NULL); - - gst_element_found_tags_for_pad (GST_ELEMENT (spc), spc->srcpad, taglist); - - /* spc_tag_info_free(&info); */ - - gme_err = - gme_open_data (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), - &spc->player, 32000); - if (gme_err || !spc->player) { - if (spc->player) { - gme_delete (spc->player); - spc->player = NULL; - } - - GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); - - return FALSE; - } -#ifdef HAVE_LIBGME_ACCURACY - /* TODO: Is it worth it to make this optional? */ - gme_enable_accuracy (spc->player, 1); -#endif - gme_start_track (spc->player, 0); - - gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, 1.0, - GST_FORMAT_TIME, 0, -1, 0)); - - gst_pad_start_task (spc->srcpad, (GstTaskFunction) gst_spc_play, spc->srcpad); - - /* We can't unreference this buffer because we might need to re-initialize - * the emulator with the original data during a reverse seek - * gst_buffer_unref (spc->buf); - * spc->buf = NULL; - */ - spc->initialized = TRUE; - spc->seeking = FALSE; - spc->seekpoint = 0; - return spc->initialized; -} - -static GstStateChangeReturn -gst_spc_dec_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn result; - GstSpcDec *dec; - - dec = GST_SPC_DEC (element); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - break; - default: - break; - } - - result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (result == GST_STATE_CHANGE_FAILURE) - return result; - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - if (dec->buf) { - gst_buffer_unref (dec->buf); - dec->buf = NULL; - } - break; - default: - break; - } - - return result; -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - return gst_element_register (plugin, "spcdec", GST_RANK_PRIMARY, - GST_TYPE_SPC_DEC); -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "spcdec", - "OpenSPC Audio Decoder", - plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/ext/spc/gstspc.h b/ext/spc/gstspc.h deleted file mode 100644 index 3d7e4899..00000000 --- a/ext/spc/gstspc.h +++ /dev/null @@ -1,68 +0,0 @@ -/* Copyright (C) 2004-2005, 2009 Michael Pyne - * Copyright (C) 2004-2006 Chris Lee - * Copyright (C) 2007 Brian Koropoff - * - * 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. - */ - -#ifndef __GST_SPC_DEC_H__ -#define __GST_SPC_DEC_H__ - -#include - -#include -#include "tag.h" - -G_BEGIN_DECLS - -#define GST_TYPE_SPC_DEC \ - (gst_spc_dec_get_type()) -#define GST_SPC_DEC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPC_DEC,GstSpcDec)) -#define GST_SPC_DEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SPC_DEC,GstSpcDecClass)) -#define GST_IS_SPC_DEC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SPC_DEC)) -#define GST_IS_SPC_DEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPC_DEC)) - -typedef struct _GstSpcDec GstSpcDec; -typedef struct _GstSpcDecClass GstSpcDecClass; - -struct _GstSpcDec -{ - GstElement element; - - GstPad *sinkpad; - GstPad *srcpad; - - GstBuffer *buf; - Music_Emu *player; - gboolean initialized; - gboolean seeking; - int seekpoint; - - spc_tag_info tag_info; -}; - -struct _GstSpcDecClass -{ - GstElementClass parent_class; -}; - -G_END_DECLS - -#endif /* __GST_SPC_DEC_H__ */ diff --git a/ext/spc/tag.c b/ext/spc/tag.c deleted file mode 100644 index 41ce812f..00000000 --- a/ext/spc/tag.c +++ /dev/null @@ -1,353 +0,0 @@ -/* Copyright (C) 2007 Brian Koropoff - * - * 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. - */ - -#include -#include -#include -#include - -#include -#include -#include - - -#define EXTENDED_OFFSET 0x10200 -#define EXTENDED_MAGIC ((guint32) ('x' << 0 | 'i' << 8 | 'd' << 16 | '6' << 24)) - -#define TYPE_LENGTH 0x0 -#define TYPE_STRING 0x1 -#define TYPE_INTEGER 0x4 - -#define TAG_TITLE 0x01 -#define TAG_GAME 0x02 -#define TAG_ARTIST 0x03 -#define TAG_DUMPER 0x04 -#define TAG_DUMP_DATE 0x05 -#define TAG_EMULATOR 0x06 -#define TAG_COMMENT 0x07 -#define TAG_ALBUM 0x10 -#define TAG_DISC 0x11 -#define TAG_TRACK 0x12 -#define TAG_PUBLISHER 0x13 -#define TAG_YEAR 0x14 -#define TAG_INTRO 0x30 -#define TAG_LOOP 0x31 -#define TAG_END 0x32 -#define TAG_FADE 0x33 -#define TAG_MUTED 0x34 -#define TAG_COUNT 0x35 -#define TAG_AMP 0x36 - -#define READ_INT8(data, offset) (data[offset]) -#define READ_INT16(data, offset) ((data[offset] << 0) + (data[offset+1] << 8)) -#define READ_INT24(data, offset) ((data[offset] << 0) + (data[offset+1] << 8) + (data[offset+2] << 16)) -#define READ_INT32(data, offset) ((data[offset] << 0) + (data[offset+1] << 8) + (data[offset+2] << 16) + (data[offset+3] << 24)) - -static inline gboolean -spc_tag_is_extended (guchar * data, guint length) -{ - // Extended tags come at the end of the file (at a known offset) - // and start with "xid6" - return (length > EXTENDED_OFFSET + 4 - && READ_INT32 (data, EXTENDED_OFFSET) == EXTENDED_MAGIC); -} - -static inline gboolean -spc_tag_is_text_format (guchar * data, guint length) -{ - // Because the id666 format is brain dead, there's - // no definite way to decide if it is in text - // format. This function implements a set of - // heuristics to make a best-effort guess. - - // If the date field contains separators, it is probably text - if (data[0xA0] == '/' || data[0xA0] == '.') - return TRUE; - // If the first byte of the date field is small (but not 0, - // which could indicate an empty string), it's probably binary - if (data[0x9E] >= 1 && data[0x9E] <= 31) - return FALSE; - - // If all previous tests turned up nothing, assume it's text - return TRUE; -} - -static inline gboolean -spc_tag_is_present (guchar * data, guint length) -{ - return data[0x23] == 26; -} - -static inline GDate * -spc_tag_unpack_date (guint32 packed) -{ - guint dump_year = packed / 10000; - guint dump_month = (packed % 10000) / 100; - guint dump_day = packed % 100; - - if (dump_month == 0) - dump_month = 1; - if (dump_day == 0) - dump_day = 1; - - if (dump_year != 0) - return g_date_new_dmy (dump_day, dump_month, dump_year); - else - return NULL; -} - -void -spc_tag_clear (spc_tag_info * info) -{ - info->title = info->game = info->publisher = info->artist = info->album = - info->comment = info->dumper = NULL; - info->dump_date = NULL; - info->time_seconds = 0; - info->time_fade_milliseconds = 0; - info->time_intro = 0; - info->time_end = 0; - info->time_loop = 0; - info->time_fade = 0; - info->loop_count = 0; - info->muted = 0; - info->disc = 0; - info->amplification = 0; -} - -void -spc_tag_get_info (guchar * data, guint length, spc_tag_info * info) -{ - spc_tag_clear (info); - - if (spc_tag_is_present (data, length)) { - gboolean text_format = spc_tag_is_text_format (data, length); - - info->title = g_new0 (gchar, 0x21); - info->game = g_new0 (gchar, 0x21); - info->artist = g_new0 (gchar, 0x21); - info->dumper = g_new0 (gchar, 0x10); - info->comment = g_new0 (gchar, 0x32); - - strncpy (info->title, (gchar *) & data[0x2E], 32); - strncpy (info->artist, (gchar *) & data[(text_format ? 0xB1 : 0xB0)], 32); - strncpy (info->game, (gchar *) & data[0x4E], 32); - strncpy (info->dumper, (gchar *) & data[0x6E], 16); - strncpy (info->comment, (gchar *) & data[0x7E], 32); - - if (text_format) { - gchar time[4]; - gchar fade[6]; - guint dump_year, dump_month, dump_day; - - strncpy (time, (gchar *) data + 0xA9, 3); - strncpy (fade, (gchar *) data + 0xAC, 5); - - time[3] = fade[5] = 0; - - info->time_seconds = atoi (time); - info->time_fade_milliseconds = atoi (fade); - - dump_year = (guint) atoi ((gchar *) data + 0x9E); - dump_month = (guint) atoi ((gchar *) data + 0x9E + 3); - dump_day = (guint) atoi ((gchar *) data + 0x9E + 3 + 3); - - if (dump_month == 0) - dump_month = 1; - if (dump_day == 0) - dump_day = 1; - if (dump_year != 0) - info->dump_date = g_date_new_dmy (dump_day, dump_month, dump_year); - - info->muted = READ_INT8 (data, 0xD1); - info->emulator = READ_INT8 (data, 0xD2); - } else { - info->time_seconds = READ_INT24 (data, 0xA9); - info->time_fade_milliseconds = READ_INT32 (data, 0xAC); - info->dump_date = spc_tag_unpack_date (READ_INT32 (data, 0x9E)); - info->muted = READ_INT8 (data, 0xD0); - info->emulator = READ_INT8 (data, 0xD1); - } - } - - if (spc_tag_is_extended (data, length)) { - guchar *chunk = data + EXTENDED_OFFSET + 8; - guint32 chunk_size = *((guint32 *) (data + EXTENDED_OFFSET + 4)); - - guchar *subchunk, *subchunk_next; - - for (subchunk = chunk; subchunk < chunk + chunk_size; - subchunk = subchunk_next) { - guint8 tag = READ_INT8 (subchunk, 0); - guint8 type = READ_INT8 (subchunk, 1); - guint16 length = READ_INT16 (subchunk, 2); - guchar *value = subchunk + 4; - - switch (type) { - case TYPE_LENGTH: - { - switch (tag) { - case TAG_TRACK: - info->track = READ_INT8 (subchunk, 2 + 1); - break; - case TAG_YEAR: - info->year = READ_INT16 (subchunk, 2); - break; - case TAG_COUNT: - info->loop_count = READ_INT8 (subchunk, 2); - break; - case TAG_EMULATOR: - info->emulator = READ_INT8 (subchunk, 2); - break; - case TAG_DISC: - info->disc = READ_INT8 (subchunk, 2); - break; - case TAG_MUTED: - info->muted = READ_INT8 (subchunk, 2); - break; - default: - break; - } - - subchunk_next = subchunk + 4; - break; - } - case TYPE_STRING: - { - gchar *dest; - - if (length <= 1) - dest = NULL; - else - switch (tag) { - case TAG_TITLE: - dest = info->title = g_renew (gchar, info->title, length); - break; - case TAG_GAME: - dest = info->game = g_renew (gchar, info->game, length); - break; - case TAG_ARTIST: - dest = info->artist = g_renew (gchar, info->artist, length); - break; - case TAG_ALBUM: - dest = info->album = g_renew (gchar, info->album, length); - break; - case TAG_DUMPER: - dest = info->dumper = g_renew (gchar, info->dumper, length); - break; - case TAG_COMMENT: - dest = info->comment = g_renew (gchar, info->comment, length); - break; - case TAG_PUBLISHER: - dest = info->publisher = - g_renew (gchar, info->publisher, length); - break; - default: - dest = NULL; - break; - } - - if (dest) - strncpy (dest, (gchar *) value, length); - - subchunk_next = value + length; - break; - } - case TYPE_INTEGER: - { - switch (tag) { - case TAG_INTRO: - info->time_intro = READ_INT32 (value, 0); - break; - case TAG_END: - info->time_end = READ_INT32 (value, 0); - break; - case TAG_FADE: - info->time_fade = READ_INT32 (value, 0); - break; - case TAG_LOOP: - info->time_loop = READ_INT32 (value, 0); - break; - case TAG_DUMP_DATE: - info->dump_date = spc_tag_unpack_date (READ_INT32 (value, 0)); - break; - case TAG_AMP: - info->amplification = READ_INT32 (value, 0); - break; - default: - break; - } - subchunk_next = value + length; - break; - } - default: - subchunk_next = value + length; - break; - } - } - } - - if (info->title && !*info->title) { - g_free (info->title); - info->title = NULL; - } - if (info->game && !*info->game) { - g_free (info->game); - info->game = NULL; - } - if (info->artist && !*info->artist) { - g_free (info->artist); - info->artist = NULL; - } - if (info->album && !*info->album) { - g_free (info->album); - info->album = NULL; - } - if (info->publisher && !*info->publisher) { - g_free (info->publisher); - info->publisher = NULL; - } - if (info->comment && !*info->comment) { - g_free (info->comment); - info->comment = NULL; - } - if (info->dumper && !*info->dumper) { - g_free (info->dumper); - info->dumper = NULL; - } -} - -void -spc_tag_free (spc_tag_info * info) -{ - if (info->title) - g_free (info->title); - if (info->game) - g_free (info->game); - if (info->artist) - g_free (info->artist); - if (info->album) - g_free (info->album); - if (info->publisher) - g_free (info->publisher); - if (info->comment) - g_free (info->comment); - if (info->dumper) - g_free (info->dumper); - if (info->dump_date) - g_date_free (info->dump_date); -} diff --git a/ext/spc/tag.h b/ext/spc/tag.h deleted file mode 100644 index 5b795893..00000000 --- a/ext/spc/tag.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright (C) 2007 Brian Koropoff - * - * 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. - */ - -#include -#include - -typedef struct -{ - gchar *title, *game, *artist, *album, *publisher; - gchar *dumper, *comment; - enum { EMU_SNES9X = 2, EMU_ZSNES = 1, EMU_UNKNOWN = 0 } emulator; - guint8 track, disc, muted, loop_count; - guint16 year; - guint32 time_seconds, time_fade_milliseconds; - guint32 time_intro, time_loop, time_end, time_fade; - guint32 amplification; - GDate *dump_date; -} spc_tag_info; - -void spc_tag_clear(spc_tag_info* info); -void spc_tag_get_info(guchar* data, guint length, spc_tag_info* info); -void spc_tag_free(spc_tag_info* info); -- cgit v1.2.1 From 359c780830c048766534cd931a3b60f89fc823c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 7 Aug 2009 06:59:55 +0200 Subject: gme: Rename everything from SPC to GME --- ext/gme/gstgme.c | 272 +++++++++++++++++++++++++++---------------------------- ext/gme/gstgme.h | 34 +++---- 2 files changed, 152 insertions(+), 154 deletions(-) (limited to 'ext') diff --git a/ext/gme/gstgme.c b/ext/gme/gstgme.c index 0efb8d8a..45fa04db 100644 --- a/ext/gme/gstgme.c +++ b/ext/gme/gstgme.c @@ -28,15 +28,9 @@ #include #include -static const GstElementDetails gst_spc_dec_details = -GST_ELEMENT_DETAILS ("SNES SPC 700 decoder", - "Codec/Audio/Decoder", - "Uses Blargg's libgme to emulate an SPC processor", - "Chris Lee , Brian Koropoff , Michael Pyne "); - static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-spc")); + GST_STATIC_CAPS ("audio/x-gme")); static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, @@ -46,20 +40,20 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, "width = (int) 16, " "depth = (int) 16, " "rate = (int) 32000, " "channels = (int) 2")); -GST_BOILERPLATE (GstSpcDec, gst_spc_dec, GstElement, GST_TYPE_ELEMENT); +GST_BOILERPLATE (GstGmeDec, gst_gme_dec, GstElement, GST_TYPE_ELEMENT); -static GstFlowReturn gst_spc_dec_chain (GstPad * pad, GstBuffer * buffer); -static gboolean gst_spc_dec_sink_event (GstPad * pad, GstEvent * event); -static gboolean gst_spc_dec_src_event (GstPad * pad, GstEvent * event); -static gboolean gst_spc_dec_src_query (GstPad * pad, GstQuery * query); -static GstStateChangeReturn gst_spc_dec_change_state (GstElement * element, +static GstFlowReturn gst_gme_dec_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_gme_dec_sink_event (GstPad * pad, GstEvent * event); +static gboolean gst_gme_dec_src_event (GstPad * pad, GstEvent * event); +static gboolean gst_gme_dec_src_query (GstPad * pad, GstQuery * query); +static GstStateChangeReturn gst_gme_dec_change_state (GstElement * element, GstStateChange transition); -static void gst_spc_play (GstPad * pad); -static void gst_spc_dec_dispose (GObject * object); -static gboolean spc_setup (GstSpcDec * spc); +static void gst_gme_play (GstPad * pad); +static void gst_gme_dec_dispose (GObject * object); +static gboolean gme_setup (GstGmeDec * gme); static gboolean -spc_negotiate (GstSpcDec * spc) +gme_negotiate (GstGmeDec * gme) { GstCaps *allowed, *caps; GstStructure *structure; @@ -68,20 +62,20 @@ spc_negotiate (GstSpcDec * spc) int rate = 32000; int channels = 2; - allowed = gst_pad_get_allowed_caps (spc->srcpad); + allowed = gst_pad_get_allowed_caps (gme->srcpad); if (!allowed) { - GST_DEBUG_OBJECT (spc, "couldn't get allowed caps"); + GST_DEBUG_OBJECT (gme, "couldn't get allowed caps"); return FALSE; } - GST_DEBUG_OBJECT (spc, "allowed caps: %" GST_PTR_FORMAT, allowed); + GST_DEBUG_OBJECT (gme, "allowed caps: %" GST_PTR_FORMAT, allowed); structure = gst_caps_get_structure (allowed, 0); gst_structure_get_int (structure, "width", &width); gst_structure_get_int (structure, "depth", &depth); if (width && depth && width != depth) { - GST_DEBUG_OBJECT (spc, "width %d and depth %d are different", width, depth); + GST_DEBUG_OBJECT (gme, "width %d and depth %d are different", width, depth); gst_caps_unref (allowed); return FALSE; } @@ -96,7 +90,7 @@ spc_negotiate (GstSpcDec * spc) "width", G_TYPE_INT, width, "depth", G_TYPE_INT, depth, "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL); - gst_pad_set_caps (spc->srcpad, caps); + gst_pad_set_caps (gme->srcpad, caps); gst_caps_unref (caps); gst_caps_unref (allowed); @@ -105,11 +99,15 @@ spc_negotiate (GstSpcDec * spc) } static void -gst_spc_dec_base_init (gpointer g_class) +gst_gme_dec_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - gst_element_class_set_details (element_class, &gst_spc_dec_details); + gst_element_class_set_details_simple (element_class, + "Gaming console music file decoder", "Codec/Audio/Decoder", + "Uses libgme to emulate a gaming console sound processors", + "Chris Lee , Brian Koropoff , " + "Michael Pyne , Sebastian Dröge "); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&sink_factory)); @@ -118,17 +116,17 @@ gst_spc_dec_base_init (gpointer g_class) } static void -gst_spc_dec_class_init (GstSpcDecClass * klass) +gst_gme_dec_class_init (GstGmeDecClass * klass) { GstElementClass *element_class = (GstElementClass *) klass; GObjectClass *gobject_class = (GObjectClass *) klass; - element_class->change_state = GST_DEBUG_FUNCPTR (gst_spc_dec_change_state); - gobject_class->dispose = gst_spc_dec_dispose; + element_class->change_state = GST_DEBUG_FUNCPTR (gst_gme_dec_change_state); + gobject_class->dispose = gst_gme_dec_dispose; } static const GstQueryType * -gst_spc_dec_src_query_type (GstPad * pad) +gst_gme_dec_src_query_type (GstPad * pad) { static const GstQueryType query_types[] = { GST_QUERY_DURATION, @@ -141,59 +139,59 @@ gst_spc_dec_src_query_type (GstPad * pad) static void -gst_spc_dec_init (GstSpcDec * spc, GstSpcDecClass * klass) +gst_gme_dec_init (GstGmeDec * gme, GstGmeDecClass * klass) { - spc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); - /* gst_pad_set_query_function (spc->sinkpad, NULL); */ - gst_pad_set_event_function (spc->sinkpad, gst_spc_dec_sink_event); - gst_pad_set_chain_function (spc->sinkpad, gst_spc_dec_chain); - gst_element_add_pad (GST_ELEMENT (spc), spc->sinkpad); - - spc->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); - gst_pad_set_event_function (spc->srcpad, gst_spc_dec_src_event); - gst_pad_set_query_function (spc->srcpad, gst_spc_dec_src_query); - gst_pad_set_query_type_function (spc->srcpad, gst_spc_dec_src_query_type); - gst_pad_use_fixed_caps (spc->srcpad); - gst_element_add_pad (GST_ELEMENT (spc), spc->srcpad); - - spc->buf = NULL; - spc->player = NULL; - spc->total_duration = GST_CLOCK_TIME_NONE; - spc->initialized = FALSE; + gme->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); + /* gst_pad_set_query_function (gme->sinkpad, NULL); */ + gst_pad_set_event_function (gme->sinkpad, gst_gme_dec_sink_event); + gst_pad_set_chain_function (gme->sinkpad, gst_gme_dec_chain); + gst_element_add_pad (GST_ELEMENT (gme), gme->sinkpad); + + gme->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); + gst_pad_set_event_function (gme->srcpad, gst_gme_dec_src_event); + gst_pad_set_query_function (gme->srcpad, gst_gme_dec_src_query); + gst_pad_set_query_type_function (gme->srcpad, gst_gme_dec_src_query_type); + gst_pad_use_fixed_caps (gme->srcpad); + gst_element_add_pad (GST_ELEMENT (gme), gme->srcpad); + + gme->buf = NULL; + gme->player = NULL; + gme->total_duration = GST_CLOCK_TIME_NONE; + gme->initialized = FALSE; } static void -gst_spc_dec_dispose (GObject * object) +gst_gme_dec_dispose (GObject * object) { - GstSpcDec *spc = GST_SPC_DEC (object); + GstGmeDec *gme = GST_GME_DEC (object); - if (spc->buf) { - gst_buffer_unref (spc->buf); - spc->buf = NULL; + if (gme->buf) { + gst_buffer_unref (gme->buf); + gme->buf = NULL; } } static GstFlowReturn -gst_spc_dec_chain (GstPad * pad, GstBuffer * buffer) +gst_gme_dec_chain (GstPad * pad, GstBuffer * buffer) { - GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); - /* Accumulate SPC data until end-of-stream, then commence playback. */ - if (spc->buf) { - spc->buf = gst_buffer_join (spc->buf, buffer); + /* Accumulate GME data until end-of-stream, then commence playback. */ + if (gme->buf) { + gme->buf = gst_buffer_join (gme->buf, buffer); } else { - spc->buf = buffer; + gme->buf = buffer; } - gst_object_unref (spc); + gst_object_unref (gme); return GST_FLOW_OK; } static gboolean -gst_spc_dec_sink_event (GstPad * pad, GstEvent * event) +gst_gme_dec_sink_event (GstPad * pad, GstEvent * event) { - GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); gboolean result = TRUE; gboolean forward = FALSE; @@ -201,9 +199,9 @@ gst_spc_dec_sink_event (GstPad * pad, GstEvent * event) case GST_EVENT_EOS: /* we get EOS when we loaded the complete file, now try to initialize the * decoding */ - if (!(result = spc_setup (spc))) { + if (!(result = gme_setup (gme))) { /* can't start, post an ERROR and push EOS downstream */ - GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), + GST_ELEMENT_ERROR (gme, STREAM, DEMUX, (NULL), ("can't start playback")); forward = TRUE; } @@ -212,19 +210,19 @@ gst_spc_dec_sink_event (GstPad * pad, GstEvent * event) break; } if (forward) - result = gst_pad_push_event (spc->srcpad, event); + result = gst_pad_push_event (gme->srcpad, event); else gst_event_unref (event); - gst_object_unref (spc); + gst_object_unref (gme); return result; } static gboolean -gst_spc_dec_src_event (GstPad * pad, GstEvent * event) +gst_gme_dec_src_event (GstPad * pad, GstEvent * event) { - GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); gboolean result = FALSE; switch (GST_EVENT_TYPE (event)) { @@ -243,12 +241,12 @@ gst_spc_dec_src_event (GstPad * pad, GstEvent * event) gst_event_unref (event); if (format != GST_FORMAT_TIME) { - GST_DEBUG_OBJECT (spc, "seeking is only supported in TIME format"); + GST_DEBUG_OBJECT (gme, "seeking is only supported in TIME format"); break; } if (start_type != GST_SEEK_TYPE_SET || stop_type != GST_SEEK_TYPE_NONE) { - GST_DEBUG_OBJECT (spc, "unsupported seek type"); + GST_DEBUG_OBJECT (gme, "unsupported seek type"); break; } @@ -256,11 +254,11 @@ gst_spc_dec_src_event (GstPad * pad, GstEvent * event) stop = GST_CLOCK_TIME_NONE; if (start_type == GST_SEEK_TYPE_SET) { - guint64 cur = gme_tell (spc->player) * GST_MSECOND; + guint64 cur = gme_tell (gme->player) * GST_MSECOND; guint64 dest = (guint64) start; - if (spc->total_duration != GST_CLOCK_TIME_NONE) - dest = CLAMP (dest, 0, spc->total_duration); + if (gme->total_duration != GST_CLOCK_TIME_NONE) + dest = CLAMP (dest, 0, gme->total_duration); else dest = MAX (0, dest); @@ -270,54 +268,54 @@ gst_spc_dec_src_event (GstPad * pad, GstEvent * event) flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH; if (flush) { - gst_pad_push_event (spc->srcpad, gst_event_new_flush_start ()); + gst_pad_push_event (gme->srcpad, gst_event_new_flush_start ()); } else { - gst_pad_stop_task (spc->srcpad); + gst_pad_stop_task (gme->srcpad); } - GST_PAD_STREAM_LOCK (spc->srcpad); + GST_PAD_STREAM_LOCK (gme->srcpad); if (flags & GST_SEEK_FLAG_SEGMENT) { - gst_element_post_message (GST_ELEMENT (spc), - gst_message_new_segment_start (GST_OBJECT (spc), format, cur)); + gst_element_post_message (GST_ELEMENT (gme), + gst_message_new_segment_start (GST_OBJECT (gme), format, cur)); } if (flush) { - gst_pad_push_event (spc->srcpad, gst_event_new_flush_stop ()); + gst_pad_push_event (gme->srcpad, gst_event_new_flush_stop ()); } if (stop == GST_CLOCK_TIME_NONE - && spc->total_duration != GST_CLOCK_TIME_NONE) - stop = spc->total_duration; + && gme->total_duration != GST_CLOCK_TIME_NONE) + stop = gme->total_duration; - gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, rate, + gst_pad_push_event (gme->srcpad, gst_event_new_new_segment (FALSE, rate, GST_FORMAT_TIME, dest, stop, dest)); - spc->seekpoint = dest / GST_MSECOND; /* nsecs to msecs */ - spc->seeking = TRUE; + gme->seekpoint = dest / GST_MSECOND; /* nsecs to msecs */ + gme->seeking = TRUE; - gst_pad_start_task (spc->srcpad, (GstTaskFunction) gst_spc_play, - spc->srcpad); + gst_pad_start_task (gme->srcpad, (GstTaskFunction) gst_gme_play, + gme->srcpad); - GST_PAD_STREAM_UNLOCK (spc->srcpad); + GST_PAD_STREAM_UNLOCK (gme->srcpad); result = TRUE; } break; } default: - result = gst_pad_push_event (spc->sinkpad, event); + result = gst_pad_push_event (gme->sinkpad, event); break; } - gst_object_unref (spc); + gst_object_unref (gme); return result; } static gboolean -gst_spc_dec_src_query (GstPad * pad, GstQuery * query) +gst_gme_dec_src_query (GstPad * pad, GstQuery * query) { - GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); gboolean result = TRUE; switch (GST_QUERY_TYPE (query)) { @@ -326,12 +324,12 @@ gst_spc_dec_src_query (GstPad * pad, GstQuery * query) GstFormat format; gst_query_parse_duration (query, &format, NULL); - if (!spc->initialized || format != GST_FORMAT_TIME - || spc->total_duration == GST_CLOCK_TIME_NONE) { + if (!gme->initialized || format != GST_FORMAT_TIME + || gme->total_duration == GST_CLOCK_TIME_NONE) { result = FALSE; break; } - gst_query_set_duration (query, GST_FORMAT_TIME, spc->total_duration); + gst_query_set_duration (query, GST_FORMAT_TIME, gme->total_duration); break; } case GST_QUERY_POSITION: @@ -339,12 +337,12 @@ gst_spc_dec_src_query (GstPad * pad, GstQuery * query) GstFormat format; gst_query_parse_position (query, &format, NULL); - if (!spc->initialized || format != GST_FORMAT_TIME) { + if (!gme->initialized || format != GST_FORMAT_TIME) { result = FALSE; break; } gst_query_set_position (query, GST_FORMAT_TIME, - (gint64) gme_tell (spc->player) * GST_MSECOND); + (gint64) gme_tell (gme->player) * GST_MSECOND); break; } default: @@ -352,46 +350,46 @@ gst_spc_dec_src_query (GstPad * pad, GstQuery * query) break; } - gst_object_unref (spc); + gst_object_unref (gme); return result; } static void -gst_spc_play (GstPad * pad) +gst_gme_play (GstPad * pad) { - GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); GstFlowReturn flow_return; GstBuffer *out; - gboolean seeking = spc->seeking; + gboolean seeking = gme->seeking; gme_err_t gme_err = NULL; const int NUM_SAMPLES = 1600; /* 4 bytes (stereo 16-bit) per sample */ if (!seeking) { out = gst_buffer_new_and_alloc (NUM_SAMPLES * 4); gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); - GST_BUFFER_TIMESTAMP (out) = gme_tell (spc->player) * GST_MSECOND; + GST_BUFFER_TIMESTAMP (out) = gme_tell (gme->player) * GST_MSECOND; gme_err = - gme_play (spc->player, NUM_SAMPLES * 2, + gme_play (gme->player, NUM_SAMPLES * 2, (short *) GST_BUFFER_DATA (out)); if (gme_err) { - GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); + GST_ELEMENT_ERROR (gme, STREAM, DEMUX, (NULL), (gme_err)); gst_pad_pause_task (pad); gst_pad_push_event (pad, gst_event_new_eos ()); - gst_object_unref (spc); + gst_object_unref (gme); return; } } else { - gme_seek (spc->player, spc->seekpoint); - spc->seeking = FALSE; + gme_seek (gme->player, gme->seekpoint); + gme->seeking = FALSE; out = gst_buffer_new (); gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); } - if ((flow_return = gst_pad_push (spc->srcpad, out)) != GST_FLOW_OK) { - GST_DEBUG_OBJECT (spc, "pausing task, reason %s", + if ((flow_return = gst_pad_push (gme->srcpad, out)) != GST_FLOW_OK) { + GST_DEBUG_OBJECT (gme, "pausing task, reason %s", gst_flow_get_name (flow_return)); gst_pad_pause_task (pad); @@ -401,43 +399,43 @@ gst_spc_play (GstPad * pad) } } - if (gme_track_ended (spc->player)) { + if (gme_track_ended (gme->player)) { gst_pad_pause_task (pad); gst_pad_push_event (pad, gst_event_new_eos ()); } - gst_object_unref (spc); + gst_object_unref (gme); return; } static gboolean -spc_setup (GstSpcDec * spc) +gme_setup (GstGmeDec * gme) { gme_info_t *info; gme_err_t gme_err = NULL; GstTagList *taglist; guint64 total_duration; - if (!spc->buf || !spc_negotiate (spc)) { + if (!gme->buf || !gme_negotiate (gme)) { return FALSE; } gme_err = - gme_open_data (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), - &spc->player, 32000); - if (gme_err || !spc->player) { - if (spc->player) { - gme_delete (spc->player); - spc->player = NULL; + gme_open_data (GST_BUFFER_DATA (gme->buf), GST_BUFFER_SIZE (gme->buf), + &gme->player, 32000); + if (gme_err || !gme->player) { + if (gme->player) { + gme_delete (gme->player); + gme->player = NULL; } - GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); + GST_ELEMENT_ERROR (gme, STREAM, DEMUX, (NULL), (gme_err)); return FALSE; } - gme_err = gme_track_info (spc->player, &info, 0); + gme_err = gme_track_info (gme->player, &info, 0); taglist = gst_tag_list_new (); @@ -467,45 +465,45 @@ spc_setup (GstSpcDec * spc) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, info->system, NULL); - spc->total_duration = total_duration = + gme->total_duration = total_duration = gst_util_uint64_scale_int (info->play_length, GST_MSECOND, 1); gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DURATION, total_duration, NULL); - gst_element_found_tags_for_pad (GST_ELEMENT (spc), spc->srcpad, taglist); + gst_element_found_tags_for_pad (GST_ELEMENT (gme), gme->srcpad, taglist); g_free (info); #ifdef HAVE_LIBGME_ACCURACY /* TODO: Is it worth it to make this optional? */ - gme_enable_accuracy (spc->player, 1); + gme_enable_accuracy (gme->player, 1); #endif - gme_start_track (spc->player, 0); + gme_start_track (gme->player, 0); - gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, 1.0, + gst_pad_push_event (gme->srcpad, gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0)); - gst_pad_start_task (spc->srcpad, (GstTaskFunction) gst_spc_play, spc->srcpad); + gst_pad_start_task (gme->srcpad, (GstTaskFunction) gst_gme_play, gme->srcpad); /* We can't unreference this buffer because we might need to re-initialize * the emulator with the original data during a reverse seek - * gst_buffer_unref (spc->buf); - * spc->buf = NULL; + * gst_buffer_unref (gme->buf); + * gme->buf = NULL; */ - spc->initialized = TRUE; - spc->seeking = FALSE; - spc->seekpoint = 0; - return spc->initialized; + gme->initialized = TRUE; + gme->seeking = FALSE; + gme->seekpoint = 0; + return gme->initialized; } static GstStateChangeReturn -gst_spc_dec_change_state (GstElement * element, GstStateChange transition) +gst_gme_dec_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn result; - GstSpcDec *dec; + GstGmeDec *dec; - dec = GST_SPC_DEC (element); + dec = GST_GME_DEC (element); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: @@ -536,12 +534,12 @@ gst_spc_dec_change_state (GstElement * element, GstStateChange transition) static gboolean plugin_init (GstPlugin * plugin) { - return gst_element_register (plugin, "spcdec", GST_RANK_PRIMARY, - GST_TYPE_SPC_DEC); + return gst_element_register (plugin, "gmedec", GST_RANK_PRIMARY, + GST_TYPE_GME_DEC); } GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, - "spcdec", - "OpenSPC Audio Decoder", + "gmedec", + "GME Audio Decoder", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/ext/gme/gstgme.h b/ext/gme/gstgme.h index b4ab17a5..f1ba39d9 100644 --- a/ext/gme/gstgme.h +++ b/ext/gme/gstgme.h @@ -18,8 +18,8 @@ * Boston, MA 02111-1307, USA. */ -#ifndef __GST_SPC_DEC_H__ -#define __GST_SPC_DEC_H__ +#ifndef __GST_GME_DEC_H__ +#define __GST_GME_DEC_H__ #include @@ -27,21 +27,21 @@ G_BEGIN_DECLS -#define GST_TYPE_SPC_DEC \ - (gst_spc_dec_get_type()) -#define GST_SPC_DEC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPC_DEC,GstSpcDec)) -#define GST_SPC_DEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SPC_DEC,GstSpcDecClass)) -#define GST_IS_SPC_DEC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SPC_DEC)) -#define GST_IS_SPC_DEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPC_DEC)) +#define GST_TYPE_GME_DEC \ + (gst_gme_dec_get_type()) +#define GST_GME_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GME_DEC,GstGmeDec)) +#define GST_GME_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GME_DEC,GstGmeDecClass)) +#define GST_IS_GME_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GME_DEC)) +#define GST_IS_GME_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GME_DEC)) -typedef struct _GstSpcDec GstSpcDec; -typedef struct _GstSpcDecClass GstSpcDecClass; +typedef struct _GstGmeDec GstGmeDec; +typedef struct _GstGmeDecClass GstGmeDecClass; -struct _GstSpcDec +struct _GstGmeDec { GstElement element; @@ -57,11 +57,11 @@ struct _GstSpcDec GstClockTime total_duration; }; -struct _GstSpcDecClass +struct _GstGmeDecClass { GstElementClass parent_class; }; G_END_DECLS -#endif /* __GST_SPC_DEC_H__ */ +#endif /* __GST_GME_DEC_H__ */ -- cgit v1.2.1 From cf0c65b72cc7c4fb5a1db3d0d918e3321cfc01d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 7 Aug 2009 07:01:11 +0200 Subject: gme: Fix caps name again --- ext/gme/gstgme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ext') diff --git a/ext/gme/gstgme.c b/ext/gme/gstgme.c index 45fa04db..9da38028 100644 --- a/ext/gme/gstgme.c +++ b/ext/gme/gstgme.c @@ -30,7 +30,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-gme")); + GST_STATIC_CAPS ("audio/x-spc")); static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, -- cgit v1.2.1 From 9d9fa0a31d983530befe1ab9f3c701d5fa82c8be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 7 Aug 2009 07:23:07 +0200 Subject: gme: Don't add empty string tags --- ext/gme/gstgme.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'ext') diff --git a/ext/gme/gstgme.c b/ext/gme/gstgme.c index 9da38028..fb8485a8 100644 --- a/ext/gme/gstgme.c +++ b/ext/gme/gstgme.c @@ -439,29 +439,29 @@ gme_setup (GstGmeDec * gme) taglist = gst_tag_list_new (); - if (info->song) + if (info->song && *info->song) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, info->song, NULL); - if (info->author) + if (info->author && *info->author) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ARTIST, info->author, NULL); /* Prefer the name of the official soundtrack over the name of the game (since this is * how track numbers are derived) */ - if (info->game) + if (info->game && *info->game) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, info->game, NULL); - if (info->comment) + if (info->comment && *info->comment) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COMMENT, info->comment, NULL); - if (info->dumper) + if (info->dumper && *info->dumper) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_CONTACT, info->dumper, NULL); - if (info->copyright) + if (info->copyright && *info->copyright) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COPYRIGHT, info->copyright, NULL); - if (info->system) + if (info->system && *info->system) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, info->system, NULL); -- cgit v1.2.1 From 27efe0067c864baa57053bd7ce45ffb33aa6a6ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 7 Aug 2009 09:21:04 +0200 Subject: gmedec: Add caps for all supported file formats to the srcpad template --- ext/gme/gstgme.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'ext') diff --git a/ext/gme/gstgme.c b/ext/gme/gstgme.c index fb8485a8..ff4e41d6 100644 --- a/ext/gme/gstgme.c +++ b/ext/gme/gstgme.c @@ -29,8 +29,13 @@ #include static GstStaticPadTemplate sink_factory = -GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-spc")); + GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-ay; " + "audio/x-gbs; " + "audio/x-gym; " + "audio/x-hes; " + "audio/x-kss; " + "audio/x-nsf; " "audio/x-sap; " "audio/x-spc; " "audio/x-vgm")); static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, -- cgit v1.2.1 From 782965be9f6442155a3754def8dc05d8f4db0394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 7 Aug 2009 14:41:31 +0200 Subject: gmedec: Use GstAdapter instead of many buffer joins This reduces the number of reallocations and memcpys drastically. Also free the input data as soon as it's complete and passed to GME as it's not needed anymore. --- ext/gme/Makefile.am | 4 ++-- ext/gme/gstgme.c | 35 +++++++++++++++-------------------- ext/gme/gstgme.h | 3 ++- 3 files changed, 19 insertions(+), 23 deletions(-) (limited to 'ext') diff --git a/ext/gme/Makefile.am b/ext/gme/Makefile.am index c84d340e..f9a7b139 100644 --- a/ext/gme/Makefile.am +++ b/ext/gme/Makefile.am @@ -2,8 +2,8 @@ plugin_LTLIBRARIES = libgstgme.la libgstgme_la_SOURCES = gstgme.c -libgstgme_la_CFLAGS = $(GST_CFLAGS) $(GME_CFLAGS) -libgstgme_la_LIBADD = $(GST_LIBS) $(GME_LIBS) +libgstgme_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(GME_CFLAGS) +libgstgme_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(GME_LIBS) libgstgme_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstgme_la_LIBTOOLFLAGS = --tag=disable-static diff --git a/ext/gme/gstgme.c b/ext/gme/gstgme.c index ff4e41d6..e3f145c5 100644 --- a/ext/gme/gstgme.c +++ b/ext/gme/gstgme.c @@ -159,7 +159,7 @@ gst_gme_dec_init (GstGmeDec * gme, GstGmeDecClass * klass) gst_pad_use_fixed_caps (gme->srcpad); gst_element_add_pad (GST_ELEMENT (gme), gme->srcpad); - gme->buf = NULL; + gme->adapter = gst_adapter_new (); gme->player = NULL; gme->total_duration = GST_CLOCK_TIME_NONE; gme->initialized = FALSE; @@ -170,9 +170,9 @@ gst_gme_dec_dispose (GObject * object) { GstGmeDec *gme = GST_GME_DEC (object); - if (gme->buf) { - gst_buffer_unref (gme->buf); - gme->buf = NULL; + if (gme->adapter) { + gst_object_unref (gme->adapter); + gme->adapter = NULL; } } @@ -182,11 +182,7 @@ gst_gme_dec_chain (GstPad * pad, GstBuffer * buffer) GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); /* Accumulate GME data until end-of-stream, then commence playback. */ - if (gme->buf) { - gme->buf = gst_buffer_join (gme->buf, buffer); - } else { - gme->buf = buffer; - } + gst_adapter_push (gme->adapter, buffer); gst_object_unref (gme); @@ -421,14 +417,21 @@ gme_setup (GstGmeDec * gme) gme_err_t gme_err = NULL; GstTagList *taglist; guint64 total_duration; + GstBuffer *buffer; - if (!gme->buf || !gme_negotiate (gme)) { + if (!gst_adapter_available (gme->adapter) || !gme_negotiate (gme)) { return FALSE; } + buffer = + gst_adapter_take_buffer (gme->adapter, + gst_adapter_available (gme->adapter)); + gme_err = - gme_open_data (GST_BUFFER_DATA (gme->buf), GST_BUFFER_SIZE (gme->buf), + gme_open_data (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), &gme->player, 32000); + gst_buffer_unref (buffer); + if (gme_err || !gme->player) { if (gme->player) { gme_delete (gme->player); @@ -491,11 +494,6 @@ gme_setup (GstGmeDec * gme) gst_pad_start_task (gme->srcpad, (GstTaskFunction) gst_gme_play, gme->srcpad); - /* We can't unreference this buffer because we might need to re-initialize - * the emulator with the original data during a reverse seek - * gst_buffer_unref (gme->buf); - * gme->buf = NULL; - */ gme->initialized = TRUE; gme->seeking = FALSE; gme->seekpoint = 0; @@ -524,10 +522,7 @@ gst_gme_dec_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: - if (dec->buf) { - gst_buffer_unref (dec->buf); - dec->buf = NULL; - } + gst_adapter_clear (dec->adapter); break; default: break; diff --git a/ext/gme/gstgme.h b/ext/gme/gstgme.h index f1ba39d9..5fe969d3 100644 --- a/ext/gme/gstgme.h +++ b/ext/gme/gstgme.h @@ -22,6 +22,7 @@ #define __GST_GME_DEC_H__ #include +#include #include @@ -48,7 +49,7 @@ struct _GstGmeDec GstPad *sinkpad; GstPad *srcpad; - GstBuffer *buf; + GstAdapter *adapter; Music_Emu *player; gboolean initialized; gboolean seeking; -- cgit v1.2.1 From 24217ee31a5030f528e13a94233580e86d70baf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sat, 8 Aug 2009 12:20:55 +0100 Subject: kate: some minor clean-ups Print flow return as string in log message; if we check the return value of gst_buffer_new_and_alloc() we should use the _try() function that might actually return NULL. Post error message when returning GST_FLOW_ERROR. Use portable GLib macros to print 64-bit integers. Don't use 0LL, that's also not portable (and unneeded here). --- ext/kate/gstkateenc.c | 19 +++++++++++-------- ext/kate/gstkateparse.c | 4 ++-- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'ext') diff --git a/ext/kate/gstkateenc.c b/ext/kate/gstkateenc.c index fd412d34..42333509 100644 --- a/ext/kate/gstkateenc.c +++ b/ext/kate/gstkateenc.c @@ -397,7 +397,7 @@ gst_kate_enc_create_buffer (GstKateEnc * ke, kate_packet * kp, { GstBuffer *buffer; - buffer = gst_buffer_new_and_alloc (kp->nbytes); + buffer = gst_buffer_try_new_and_alloc (kp->nbytes); if (G_UNLIKELY (!buffer)) { GST_WARNING_OBJECT (ke, "Failed to allocate buffer for %u bytes", kp->nbytes); @@ -425,7 +425,7 @@ gst_kate_enc_create_buffer (GstKateEnc * ke, kate_packet * kp, static GstFlowReturn gst_kate_enc_push_buffer (GstKateEnc * ke, GstBuffer * buffer) { - GstFlowReturn rflow; + GstFlowReturn flow; ke->last_timestamp = GST_BUFFER_TIMESTAMP (buffer); if (GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) > @@ -437,12 +437,12 @@ gst_kate_enc_push_buffer (GstKateEnc * ke, GstBuffer * buffer) /* Hack to flush each packet on its own page - taken off the CMML encoder element */ GST_BUFFER_DURATION (buffer) = G_MAXINT64; - rflow = gst_pad_push (ke->srcpad, buffer); - if (G_UNLIKELY (rflow != GST_FLOW_OK)) { - GST_ERROR_OBJECT (ke, "Failed to push buffer: %d", rflow); + flow = gst_pad_push (ke->srcpad, buffer); + if (G_UNLIKELY (flow != GST_FLOW_OK)) { + GST_WARNING_OBJECT (ke->srcpad, "push flow: %s", gst_flow_get_name (flow)); } - return rflow; + return flow; } static GstFlowReturn @@ -548,9 +548,12 @@ gst_kate_enc_send_headers (GstKateEnc * ke) kate_packet kp; int ret = kate_encode_headers (&ke->k, &ke->kc, &kp); if (ret == 0) { - GstBuffer *buffer = - gst_kate_enc_create_buffer (ke, &kp, 0ll, 0ll, 0ll, TRUE); + GstBuffer *buffer; + + buffer = gst_kate_enc_create_buffer (ke, &kp, 0, 0, 0, TRUE); if (!buffer) { + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("Failed to create buffer, %u bytes", kp.nbytes)); rflow = GST_FLOW_ERROR; break; } diff --git a/ext/kate/gstkateparse.c b/ext/kate/gstkateparse.c index e9bcd5ae..e4a83477 100644 --- a/ext/kate/gstkateparse.c +++ b/ext/kate/gstkateparse.c @@ -242,7 +242,7 @@ static GstFlowReturn gst_kate_parse_push_buffer (GstKateParse * parse, GstBuffer * buf, gint64 granulepos) { - GST_LOG_OBJECT (parse, "granulepos %16llx", granulepos); + GST_LOG_OBJECT (parse, "granulepos %16" G_GINT64_MODIFIER "x", granulepos); if (granulepos < 0) { /* packets coming not from Ogg won't have a granpos in the offset end, so we have to synthesize one here - only problem is we don't know @@ -330,7 +330,7 @@ gst_kate_parse_queue_buffer (GstKateParse * parse, GstBuffer * buf) /* oggdemux stores the granule pos in the offset end */ granpos = GST_BUFFER_OFFSET_END (buf); - GST_LOG_OBJECT (parse, "granpos %16llx", granpos); + GST_LOG_OBJECT (parse, "granpos %16" G_GINT64_MODIFIER "x", granpos); g_queue_push_tail (parse->buffer_queue, buf); #if 1 -- cgit v1.2.1 From 8ad3f148734f6f5b89dcb84157f5cc083956f7eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 8 Aug 2009 21:17:37 +0200 Subject: spc: Add the OpenSPC spc plugin again The gme plugin obsoletes it but it might still be useful for users that don't have gme yet or prefer openspc for some reason. --- ext/Makefile.am | 8 + ext/spc/Makefile.am | 11 + ext/spc/gstspc.c | 595 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ext/spc/gstspc.h | 70 +++++++ ext/spc/tag.c | 353 +++++++++++++++++++++++++++++++ ext/spc/tag.h | 37 ++++ 6 files changed, 1074 insertions(+) create mode 100644 ext/spc/Makefile.am create mode 100644 ext/spc/gstspc.c create mode 100644 ext/spc/gstspc.h create mode 100644 ext/spc/tag.c create mode 100644 ext/spc/tag.h (limited to 'ext') diff --git a/ext/Makefile.am b/ext/Makefile.am index d480fdc4..31dd00a4 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -306,6 +306,12 @@ else SOUNDTOUCH_DIR= endif +if USE_SPC +SPC_DIR=gme +else +SPC_DIR= +endif + if USE_GME GME_DIR=gme else @@ -393,6 +399,7 @@ SUBDIRS=\ $(SNDFILE_DIR) \ $(SOUNDTOUCH_DIR) \ $(GME_DIR) \ + $(SPC_DIR) \ $(SWFDEC_DIR) \ $(TARKIN_DIR) \ $(THEORA_DIR) \ @@ -439,6 +446,7 @@ DIST_SUBDIRS = \ sdl \ sndfile \ soundtouch \ + spc \ gme \ swfdec \ theora \ diff --git a/ext/spc/Makefile.am b/ext/spc/Makefile.am new file mode 100644 index 00000000..067ea669 --- /dev/null +++ b/ext/spc/Makefile.am @@ -0,0 +1,11 @@ +plugin_LTLIBRARIES = libgstspc.la + +libgstspc_la_SOURCES = gstspc.c tag.c + +libgstspc_la_CFLAGS = $(GST_CFLAGS) $(SPC_CFLAGS) +libgstspc_la_LIBADD = $(GST_LIBS) $(SPC_LIBS) +libgstspc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstspc_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = gstspc.h tag.h + diff --git a/ext/spc/gstspc.c b/ext/spc/gstspc.c new file mode 100644 index 00000000..c8ece379 --- /dev/null +++ b/ext/spc/gstspc.c @@ -0,0 +1,595 @@ +/* Copyright (C) 2004-2005 Michael Pyne + * Copyright (C) 2004-2006 Chris Lee + * Copyright (C) 2007 Brian Koropoff + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstspc.h" + +#include +#include +#include + +static const GstElementDetails gst_spc_dec_details = +GST_ELEMENT_DETAILS ("OpenSPC SPC decoder", + "Codec/Audio/Decoder", + "Uses OpenSPC to emulate an SPC processor", + "Chris Lee , Brian Koropoff "); + +static GstStaticPadTemplate sink_factory = +GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-spc")); + +static GstStaticPadTemplate src_factory = +GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "endianness = (int) BYTE_ORDER, " + "signed = (boolean) TRUE, " + "width = (int) 16, " + "depth = (int) 16, " "rate = (int) 32000, " "channels = (int) 2")); + +GST_BOILERPLATE (GstSpcDec, gst_spc_dec, GstElement, GST_TYPE_ELEMENT); + +static GstFlowReturn gst_spc_dec_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_spc_dec_sink_event (GstPad * pad, GstEvent * event); +static gboolean gst_spc_dec_src_event (GstPad * pad, GstEvent * event); +static gboolean gst_spc_dec_src_query (GstPad * pad, GstQuery * query); +static GstStateChangeReturn gst_spc_dec_change_state (GstElement * element, + GstStateChange transition); +static void spc_play (GstPad * pad); +static void gst_spc_dec_dispose (GObject * object); +static gboolean spc_setup (GstSpcDec * spc); + +static gboolean +spc_negotiate (GstSpcDec * spc) +{ + GstCaps *allowed, *caps; + GstStructure *structure; + gint width = 16, depth = 16; + gboolean sign; + int rate = 32000; + int channels = 2; + + allowed = gst_pad_get_allowed_caps (spc->srcpad); + if (!allowed) { + GST_DEBUG_OBJECT (spc, "couldn't get allowed caps"); + return FALSE; + } + + GST_DEBUG_OBJECT (spc, "allowed caps: %" GST_PTR_FORMAT, allowed); + + structure = gst_caps_get_structure (allowed, 0); + gst_structure_get_int (structure, "width", &width); + gst_structure_get_int (structure, "depth", &depth); + + if (width && depth && width != depth) { + GST_DEBUG_OBJECT (spc, "width %d and depth %d are different", width, depth); + gst_caps_unref (allowed); + return FALSE; + } + + gst_structure_get_boolean (structure, "signed", &sign); + gst_structure_get_int (structure, "rate", &rate); + gst_structure_get_int (structure, "channels", &channels); + + caps = gst_caps_new_simple ("audio/x-raw-int", + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "signed", G_TYPE_BOOLEAN, TRUE, + "width", G_TYPE_INT, width, + "depth", G_TYPE_INT, depth, + "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL); + gst_pad_set_caps (spc->srcpad, caps); + + gst_caps_unref (caps); + gst_caps_unref (allowed); + + return TRUE; +} + +static void +gst_spc_dec_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details (element_class, &gst_spc_dec_details); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_factory)); +} + +static void +gst_spc_dec_class_init (GstSpcDecClass * klass) +{ + GstElementClass *element_class = (GstElementClass *) klass; + GObjectClass *gobject_class = (GObjectClass *) klass; + + element_class->change_state = GST_DEBUG_FUNCPTR (gst_spc_dec_change_state); + gobject_class->dispose = gst_spc_dec_dispose; +} + +static const GstQueryType * +gst_spc_dec_src_query_type (GstPad * pad) +{ + static const GstQueryType query_types[] = { + GST_QUERY_DURATION, + GST_QUERY_POSITION, + (GstQueryType) 0 + }; + + return query_types; +} + + +static void +gst_spc_dec_init (GstSpcDec * spc, GstSpcDecClass * klass) +{ + spc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); + /* gst_pad_set_query_function (spc->sinkpad, NULL); */ + gst_pad_set_event_function (spc->sinkpad, gst_spc_dec_sink_event); + gst_pad_set_chain_function (spc->sinkpad, gst_spc_dec_chain); + gst_element_add_pad (GST_ELEMENT (spc), spc->sinkpad); + + spc->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); + gst_pad_set_event_function (spc->srcpad, gst_spc_dec_src_event); + gst_pad_set_query_function (spc->srcpad, gst_spc_dec_src_query); + gst_pad_set_query_type_function (spc->srcpad, gst_spc_dec_src_query_type); + gst_pad_use_fixed_caps (spc->srcpad); + gst_element_add_pad (GST_ELEMENT (spc), spc->srcpad); + + spc->buf = NULL; + spc->initialized = FALSE; + spc_tag_clear (&spc->tag_info); +} + +static void +gst_spc_dec_dispose (GObject * object) +{ + GstSpcDec *spc = GST_SPC_DEC (object); + + if (spc->buf) { + gst_buffer_unref (spc->buf); + spc->buf = NULL; + } + + spc_tag_free (&spc->tag_info); +} + +static GstFlowReturn +gst_spc_dec_chain (GstPad * pad, GstBuffer * buffer) +{ + GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + + if (spc->buf) { + spc->buf = gst_buffer_join (spc->buf, buffer); + } else { + spc->buf = buffer; + } + + gst_object_unref (spc); + + return GST_FLOW_OK; +} + +static gboolean +gst_spc_dec_sink_event (GstPad * pad, GstEvent * event) +{ + GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + gboolean result = TRUE; + gboolean forward = FALSE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* we get EOS when we loaded the complete file, now try to initialize the + * decoding */ + if (!(result = spc_setup (spc))) { + /* can't start, post an ERROR and push EOS downstream */ + GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), + ("can't start playback")); + forward = TRUE; + } + break; + default: + break; + } + if (forward) + result = gst_pad_push_event (spc->srcpad, event); + else + gst_event_unref (event); + + gst_object_unref (spc); + + return result; +} + +static gint64 +gst_spc_duration (GstSpcDec * spc) +{ + gint64 total_ticks = + spc->tag_info.time_intro + + spc->tag_info.time_loop * spc->tag_info.loop_count + + spc->tag_info.time_end; + if (total_ticks) { + return (gint64) gst_util_uint64_scale (total_ticks, GST_SECOND, 64000); + } else if (spc->tag_info.time_seconds) { + gint64 time = (gint64) spc->tag_info.time_seconds * GST_SECOND; + + return time; + } else { + return (gint64) (3 * 60) * GST_SECOND; + } +} + +static gint64 +gst_spc_fadeout (GstSpcDec * spc) +{ + if (spc->tag_info.time_fade) { + return (gint64) gst_util_uint64_scale ((guint64) spc->tag_info.time_fade, + GST_SECOND, 64000); + } else if (spc->tag_info.time_fade_milliseconds) { + return (gint64) (spc->tag_info.time_fade_milliseconds * GST_MSECOND); + } else { + return 10 * GST_SECOND; + } +} + + +static gboolean +gst_spc_dec_src_event (GstPad * pad, GstEvent * event) +{ + GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + gboolean result = FALSE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + { + gdouble rate; + GstFormat format; + GstSeekFlags flags; + GstSeekType start_type, stop_type; + gint64 start, stop; + gboolean flush; + + gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, + &stop_type, &stop); + + if (format != GST_FORMAT_TIME) { + GST_DEBUG_OBJECT (spc, "seeking is only supported in TIME format"); + break; + } + + if (start_type != GST_SEEK_TYPE_SET || stop_type != GST_SEEK_TYPE_NONE) { + GST_DEBUG_OBJECT (spc, "unsupported seek type"); + break; + } + + if (stop_type == GST_SEEK_TYPE_NONE) + stop = GST_CLOCK_TIME_NONE; + + if (start_type == GST_SEEK_TYPE_SET) { + guint64 cur = + gst_util_uint64_scale (spc->byte_pos, GST_SECOND, 32000 * 2 * 2); + guint64 dest = (guint64) start; + + dest = CLAMP (dest, 0, gst_spc_duration (spc) + gst_spc_fadeout (spc)); + + if (dest == cur) + break; + + flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH; + + if (flush) { + gst_pad_push_event (spc->srcpad, gst_event_new_flush_start ()); + } else { + gst_pad_stop_task (spc->srcpad); + } + + GST_PAD_STREAM_LOCK (spc->srcpad); + + if (flags & GST_SEEK_FLAG_SEGMENT) { + gst_element_post_message (GST_ELEMENT (spc), + gst_message_new_segment_start (GST_OBJECT (spc), format, cur)); + } + + if (flush) { + gst_pad_push_event (spc->srcpad, gst_event_new_flush_stop ()); + } + + if (stop == GST_CLOCK_TIME_NONE) + stop = (guint64) (gst_spc_duration (spc) + gst_spc_fadeout (spc)); + + gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, rate, + GST_FORMAT_TIME, dest, stop, dest)); + + /* spc->byte_pos += OSPC_Run(-1, NULL, (unsigned int) (gst_util_uint64_scale(dest - cur, 32000*2*2, GST_SECOND))); */ + spc->seekpoint = + gst_util_uint64_scale (dest, 32000 * 2 * 2, GST_SECOND); + spc->seeking = TRUE; + + gst_pad_start_task (spc->srcpad, (GstTaskFunction) spc_play, + spc->srcpad); + + GST_PAD_STREAM_UNLOCK (spc->srcpad); + result = TRUE; + } + break; + } + default: + break; + } + + gst_event_unref (event); + gst_object_unref (spc); + + return result; +} + +static gboolean +gst_spc_dec_src_query (GstPad * pad, GstQuery * query) +{ + GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + gboolean result = TRUE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_DURATION: + { + GstFormat format; + + gst_query_parse_duration (query, &format, NULL); + if (!spc->initialized || format != GST_FORMAT_TIME) { + result = FALSE; + break; + } + gst_query_set_duration (query, GST_FORMAT_TIME, + gst_spc_duration (spc) + gst_spc_fadeout (spc)); + break; + } + case GST_QUERY_POSITION: + { + GstFormat format; + + gst_query_parse_position (query, &format, NULL); + if (!spc->initialized || format != GST_FORMAT_TIME) { + result = FALSE; + break; + } + gst_query_set_position (query, GST_FORMAT_TIME, + (gint64) gst_util_uint64_scale (spc->byte_pos, GST_SECOND, + 32000 * 2 * 2)); + break; + } + default: + result = gst_pad_query_default (pad, query); + break; + } + + gst_object_unref (spc); + + return result; +} + +static void +spc_play (GstPad * pad) +{ + GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + GstFlowReturn flow_return; + GstBuffer *out; + gboolean seeking = spc->seeking; + gint64 duration, fade, end, position; + + if (!seeking) { + out = gst_buffer_new_and_alloc (1600 * 4); + gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); + GST_BUFFER_TIMESTAMP (out) = + (gint64) gst_util_uint64_scale ((guint64) spc->byte_pos, GST_SECOND, + 32000 * 2 * 2); + spc->byte_pos += OSPC_Run (-1, (short *) GST_BUFFER_DATA (out), 1600 * 4); + } else { + if (spc->seekpoint < spc->byte_pos) { + OSPC_Init (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf)); + spc->byte_pos = 0; + } + spc->byte_pos += OSPC_Run (-1, NULL, 1600 * 4); + if (spc->byte_pos >= spc->seekpoint) { + spc->seeking = FALSE; + } + out = gst_buffer_new (); + gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); + } + + duration = gst_spc_duration (spc); + fade = gst_spc_fadeout (spc); + end = duration + fade; + position = + (gint64) gst_util_uint64_scale ((guint64) spc->byte_pos, GST_SECOND, + 32000 * 2 * 2); + + if (position >= duration) { + gint16 *data = (gint16 *) GST_BUFFER_DATA (out); + guint32 size = GST_BUFFER_SIZE (out) / sizeof (gint16); + unsigned int i; + + gint64 num = (fade - (position - duration)); + + for (i = 0; i < size; i++) { + /* Apply a parabolic volume envelope */ + data[i] = (gint16) (data[i] * num / fade * num / fade); + } + } + + if ((flow_return = gst_pad_push (spc->srcpad, out)) != GST_FLOW_OK) { + GST_DEBUG_OBJECT (spc, "pausing task, reason %s", + gst_flow_get_name (flow_return)); + + gst_pad_pause_task (pad); + + if (GST_FLOW_IS_FATAL (flow_return) || flow_return == GST_FLOW_NOT_LINKED) { + gst_pad_push_event (pad, gst_event_new_eos ()); + } + } + + if (position >= end) { + gst_pad_pause_task (pad); + gst_pad_push_event (pad, gst_event_new_eos ()); + } + + gst_object_unref (spc); + + return; +} + +static gboolean +spc_setup (GstSpcDec * spc) +{ + spc_tag_info *info; + GstTagList *taglist; + guint64 total_duration; + + if (!spc->buf || !spc_negotiate (spc)) { + return FALSE; + } + + info = &(spc->tag_info); + + spc_tag_get_info (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), + info); + + taglist = gst_tag_list_new (); + + if (info->title) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, + info->title, NULL); + if (info->artist) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ARTIST, + info->artist, NULL); + /* Prefer the name of the official soundtrack over the name of the game (since this is + * how track numbers are derived) + */ + if (info->album) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, + info->album, NULL); + else if (info->game) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, info->game, + NULL); + if (info->year) { + GDate *date = g_date_new_dmy (1, 1, info->year); + + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date, NULL); + g_date_free (date); + } + if (info->track) { + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_NUMBER, + info->track, NULL); + } + if (info->comment) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COMMENT, + info->comment, NULL); + if (info->disc) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, + GST_TAG_ALBUM_VOLUME_NUMBER, info->disc, NULL); + if (info->publisher) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION, + info->publisher, NULL); + if (info->dumper) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_CONTACT, + info->dumper, NULL); + if (info->emulator) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, + info->emulator == EMU_ZSNES ? "ZSNES" : "Snes9x", NULL); + + total_duration = (guint64) (gst_spc_duration (spc) + gst_spc_fadeout (spc)); + + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, + GST_TAG_DURATION, total_duration, + GST_TAG_GENRE, "Game", GST_TAG_CODEC, "SPC700", NULL); + + gst_element_found_tags_for_pad (GST_ELEMENT (spc), spc->srcpad, taglist); + + /* spc_tag_info_free(&info); */ + + + if (OSPC_Init (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf)) != 0) { + return FALSE; + } + + gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, 1.0, + GST_FORMAT_TIME, 0, -1, 0)); + + gst_pad_start_task (spc->srcpad, (GstTaskFunction) spc_play, spc->srcpad); + + /* We can't unreference this buffer because we might need to re-initialize + * the emulator with the original data during a reverse seek + * gst_buffer_unref (spc->buf); + * spc->buf = NULL; + */ + spc->initialized = TRUE; + spc->seeking = FALSE; + spc->seekpoint = 0; + spc->byte_pos = 0; + return spc->initialized; +} + +static GstStateChangeReturn +gst_spc_dec_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn result; + GstSpcDec *dec; + + dec = GST_SPC_DEC (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + default: + break; + } + + result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (result == GST_STATE_CHANGE_FAILURE) + return result; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + if (dec->buf) { + gst_buffer_unref (dec->buf); + dec->buf = NULL; + } + break; + default: + break; + } + + return result; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "spcdec", GST_RANK_PRIMARY, + GST_TYPE_SPC_DEC); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "spcdec", + "OpenSPC Audio Decoder", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/ext/spc/gstspc.h b/ext/spc/gstspc.h new file mode 100644 index 00000000..e1740667 --- /dev/null +++ b/ext/spc/gstspc.h @@ -0,0 +1,70 @@ +/* Copyright (C) 2004-2005 Michael Pyne + * Copyright (C) 2004-2006 Chris Lee + * Copyright (C) 2007 Brian Koropoff + * + * 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. + */ + +#ifndef __GST_SPC_DEC_H__ +#define __GST_SPC_DEC_H__ + +#include + +#include + +#include "tag.h" + +G_BEGIN_DECLS + +#define GST_TYPE_SPC_DEC \ + (gst_spc_dec_get_type()) +#define GST_SPC_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPC_DEC,GstSpcDec)) +#define GST_SPC_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SPC_DEC,GstSpcDecClass)) +#define GST_IS_SPC_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SPC_DEC)) +#define GST_IS_SPC_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPC_DEC)) + +typedef struct _GstSpcDec GstSpcDec; +typedef struct _GstSpcDecClass GstSpcDecClass; + +struct _GstSpcDec +{ + GstElement element; + + GstPad *sinkpad; + GstPad *srcpad; + + GstBuffer *buf; + gboolean initialized; + gboolean seeking; + guint32 seekpoint; + + spc_tag_info tag_info; + + guint32 byte_pos; +}; + +struct _GstSpcDecClass +{ + GstElementClass parent_class; +}; + +G_END_DECLS + +#endif /* __GST_SPC_DEC_H__ */ diff --git a/ext/spc/tag.c b/ext/spc/tag.c new file mode 100644 index 00000000..41ce812f --- /dev/null +++ b/ext/spc/tag.c @@ -0,0 +1,353 @@ +/* Copyright (C) 2007 Brian Koropoff + * + * 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. + */ + +#include +#include +#include +#include + +#include +#include +#include + + +#define EXTENDED_OFFSET 0x10200 +#define EXTENDED_MAGIC ((guint32) ('x' << 0 | 'i' << 8 | 'd' << 16 | '6' << 24)) + +#define TYPE_LENGTH 0x0 +#define TYPE_STRING 0x1 +#define TYPE_INTEGER 0x4 + +#define TAG_TITLE 0x01 +#define TAG_GAME 0x02 +#define TAG_ARTIST 0x03 +#define TAG_DUMPER 0x04 +#define TAG_DUMP_DATE 0x05 +#define TAG_EMULATOR 0x06 +#define TAG_COMMENT 0x07 +#define TAG_ALBUM 0x10 +#define TAG_DISC 0x11 +#define TAG_TRACK 0x12 +#define TAG_PUBLISHER 0x13 +#define TAG_YEAR 0x14 +#define TAG_INTRO 0x30 +#define TAG_LOOP 0x31 +#define TAG_END 0x32 +#define TAG_FADE 0x33 +#define TAG_MUTED 0x34 +#define TAG_COUNT 0x35 +#define TAG_AMP 0x36 + +#define READ_INT8(data, offset) (data[offset]) +#define READ_INT16(data, offset) ((data[offset] << 0) + (data[offset+1] << 8)) +#define READ_INT24(data, offset) ((data[offset] << 0) + (data[offset+1] << 8) + (data[offset+2] << 16)) +#define READ_INT32(data, offset) ((data[offset] << 0) + (data[offset+1] << 8) + (data[offset+2] << 16) + (data[offset+3] << 24)) + +static inline gboolean +spc_tag_is_extended (guchar * data, guint length) +{ + // Extended tags come at the end of the file (at a known offset) + // and start with "xid6" + return (length > EXTENDED_OFFSET + 4 + && READ_INT32 (data, EXTENDED_OFFSET) == EXTENDED_MAGIC); +} + +static inline gboolean +spc_tag_is_text_format (guchar * data, guint length) +{ + // Because the id666 format is brain dead, there's + // no definite way to decide if it is in text + // format. This function implements a set of + // heuristics to make a best-effort guess. + + // If the date field contains separators, it is probably text + if (data[0xA0] == '/' || data[0xA0] == '.') + return TRUE; + // If the first byte of the date field is small (but not 0, + // which could indicate an empty string), it's probably binary + if (data[0x9E] >= 1 && data[0x9E] <= 31) + return FALSE; + + // If all previous tests turned up nothing, assume it's text + return TRUE; +} + +static inline gboolean +spc_tag_is_present (guchar * data, guint length) +{ + return data[0x23] == 26; +} + +static inline GDate * +spc_tag_unpack_date (guint32 packed) +{ + guint dump_year = packed / 10000; + guint dump_month = (packed % 10000) / 100; + guint dump_day = packed % 100; + + if (dump_month == 0) + dump_month = 1; + if (dump_day == 0) + dump_day = 1; + + if (dump_year != 0) + return g_date_new_dmy (dump_day, dump_month, dump_year); + else + return NULL; +} + +void +spc_tag_clear (spc_tag_info * info) +{ + info->title = info->game = info->publisher = info->artist = info->album = + info->comment = info->dumper = NULL; + info->dump_date = NULL; + info->time_seconds = 0; + info->time_fade_milliseconds = 0; + info->time_intro = 0; + info->time_end = 0; + info->time_loop = 0; + info->time_fade = 0; + info->loop_count = 0; + info->muted = 0; + info->disc = 0; + info->amplification = 0; +} + +void +spc_tag_get_info (guchar * data, guint length, spc_tag_info * info) +{ + spc_tag_clear (info); + + if (spc_tag_is_present (data, length)) { + gboolean text_format = spc_tag_is_text_format (data, length); + + info->title = g_new0 (gchar, 0x21); + info->game = g_new0 (gchar, 0x21); + info->artist = g_new0 (gchar, 0x21); + info->dumper = g_new0 (gchar, 0x10); + info->comment = g_new0 (gchar, 0x32); + + strncpy (info->title, (gchar *) & data[0x2E], 32); + strncpy (info->artist, (gchar *) & data[(text_format ? 0xB1 : 0xB0)], 32); + strncpy (info->game, (gchar *) & data[0x4E], 32); + strncpy (info->dumper, (gchar *) & data[0x6E], 16); + strncpy (info->comment, (gchar *) & data[0x7E], 32); + + if (text_format) { + gchar time[4]; + gchar fade[6]; + guint dump_year, dump_month, dump_day; + + strncpy (time, (gchar *) data + 0xA9, 3); + strncpy (fade, (gchar *) data + 0xAC, 5); + + time[3] = fade[5] = 0; + + info->time_seconds = atoi (time); + info->time_fade_milliseconds = atoi (fade); + + dump_year = (guint) atoi ((gchar *) data + 0x9E); + dump_month = (guint) atoi ((gchar *) data + 0x9E + 3); + dump_day = (guint) atoi ((gchar *) data + 0x9E + 3 + 3); + + if (dump_month == 0) + dump_month = 1; + if (dump_day == 0) + dump_day = 1; + if (dump_year != 0) + info->dump_date = g_date_new_dmy (dump_day, dump_month, dump_year); + + info->muted = READ_INT8 (data, 0xD1); + info->emulator = READ_INT8 (data, 0xD2); + } else { + info->time_seconds = READ_INT24 (data, 0xA9); + info->time_fade_milliseconds = READ_INT32 (data, 0xAC); + info->dump_date = spc_tag_unpack_date (READ_INT32 (data, 0x9E)); + info->muted = READ_INT8 (data, 0xD0); + info->emulator = READ_INT8 (data, 0xD1); + } + } + + if (spc_tag_is_extended (data, length)) { + guchar *chunk = data + EXTENDED_OFFSET + 8; + guint32 chunk_size = *((guint32 *) (data + EXTENDED_OFFSET + 4)); + + guchar *subchunk, *subchunk_next; + + for (subchunk = chunk; subchunk < chunk + chunk_size; + subchunk = subchunk_next) { + guint8 tag = READ_INT8 (subchunk, 0); + guint8 type = READ_INT8 (subchunk, 1); + guint16 length = READ_INT16 (subchunk, 2); + guchar *value = subchunk + 4; + + switch (type) { + case TYPE_LENGTH: + { + switch (tag) { + case TAG_TRACK: + info->track = READ_INT8 (subchunk, 2 + 1); + break; + case TAG_YEAR: + info->year = READ_INT16 (subchunk, 2); + break; + case TAG_COUNT: + info->loop_count = READ_INT8 (subchunk, 2); + break; + case TAG_EMULATOR: + info->emulator = READ_INT8 (subchunk, 2); + break; + case TAG_DISC: + info->disc = READ_INT8 (subchunk, 2); + break; + case TAG_MUTED: + info->muted = READ_INT8 (subchunk, 2); + break; + default: + break; + } + + subchunk_next = subchunk + 4; + break; + } + case TYPE_STRING: + { + gchar *dest; + + if (length <= 1) + dest = NULL; + else + switch (tag) { + case TAG_TITLE: + dest = info->title = g_renew (gchar, info->title, length); + break; + case TAG_GAME: + dest = info->game = g_renew (gchar, info->game, length); + break; + case TAG_ARTIST: + dest = info->artist = g_renew (gchar, info->artist, length); + break; + case TAG_ALBUM: + dest = info->album = g_renew (gchar, info->album, length); + break; + case TAG_DUMPER: + dest = info->dumper = g_renew (gchar, info->dumper, length); + break; + case TAG_COMMENT: + dest = info->comment = g_renew (gchar, info->comment, length); + break; + case TAG_PUBLISHER: + dest = info->publisher = + g_renew (gchar, info->publisher, length); + break; + default: + dest = NULL; + break; + } + + if (dest) + strncpy (dest, (gchar *) value, length); + + subchunk_next = value + length; + break; + } + case TYPE_INTEGER: + { + switch (tag) { + case TAG_INTRO: + info->time_intro = READ_INT32 (value, 0); + break; + case TAG_END: + info->time_end = READ_INT32 (value, 0); + break; + case TAG_FADE: + info->time_fade = READ_INT32 (value, 0); + break; + case TAG_LOOP: + info->time_loop = READ_INT32 (value, 0); + break; + case TAG_DUMP_DATE: + info->dump_date = spc_tag_unpack_date (READ_INT32 (value, 0)); + break; + case TAG_AMP: + info->amplification = READ_INT32 (value, 0); + break; + default: + break; + } + subchunk_next = value + length; + break; + } + default: + subchunk_next = value + length; + break; + } + } + } + + if (info->title && !*info->title) { + g_free (info->title); + info->title = NULL; + } + if (info->game && !*info->game) { + g_free (info->game); + info->game = NULL; + } + if (info->artist && !*info->artist) { + g_free (info->artist); + info->artist = NULL; + } + if (info->album && !*info->album) { + g_free (info->album); + info->album = NULL; + } + if (info->publisher && !*info->publisher) { + g_free (info->publisher); + info->publisher = NULL; + } + if (info->comment && !*info->comment) { + g_free (info->comment); + info->comment = NULL; + } + if (info->dumper && !*info->dumper) { + g_free (info->dumper); + info->dumper = NULL; + } +} + +void +spc_tag_free (spc_tag_info * info) +{ + if (info->title) + g_free (info->title); + if (info->game) + g_free (info->game); + if (info->artist) + g_free (info->artist); + if (info->album) + g_free (info->album); + if (info->publisher) + g_free (info->publisher); + if (info->comment) + g_free (info->comment); + if (info->dumper) + g_free (info->dumper); + if (info->dump_date) + g_date_free (info->dump_date); +} diff --git a/ext/spc/tag.h b/ext/spc/tag.h new file mode 100644 index 00000000..5b795893 --- /dev/null +++ b/ext/spc/tag.h @@ -0,0 +1,37 @@ +/* Copyright (C) 2007 Brian Koropoff + * + * 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. + */ + +#include +#include + +typedef struct +{ + gchar *title, *game, *artist, *album, *publisher; + gchar *dumper, *comment; + enum { EMU_SNES9X = 2, EMU_ZSNES = 1, EMU_UNKNOWN = 0 } emulator; + guint8 track, disc, muted, loop_count; + guint16 year; + guint32 time_seconds, time_fade_milliseconds; + guint32 time_intro, time_loop, time_end, time_fade; + guint32 amplification; + GDate *dump_date; +} spc_tag_info; + +void spc_tag_clear(spc_tag_info* info); +void spc_tag_get_info(guchar* data, guint length, spc_tag_info* info); +void spc_tag_free(spc_tag_info* info); -- cgit v1.2.1 From 531ce4228d20af61f120ceceef28e9d6d29c49e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 8 Aug 2009 21:20:01 +0200 Subject: spc: Lower rank to SECONDARY to make the gme plugin the default --- ext/spc/gstspc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ext') diff --git a/ext/spc/gstspc.c b/ext/spc/gstspc.c index c8ece379..7032065a 100644 --- a/ext/spc/gstspc.c +++ b/ext/spc/gstspc.c @@ -584,7 +584,7 @@ gst_spc_dec_change_state (GstElement * element, GstStateChange transition) static gboolean plugin_init (GstPlugin * plugin) { - return gst_element_register (plugin, "spcdec", GST_RANK_PRIMARY, + return gst_element_register (plugin, "spcdec", GST_RANK_SECONDARY, GST_TYPE_SPC_DEC); } -- cgit v1.2.1