• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (C) 2004-2005,2009 Michael Pyne <mpyne at kde org>
2  * Copyright (C) 2004-2006 Chris Lee <clee at kde org>
3  * Copyright (C) 2007 Brian Koropoff <bkoropoff at gmail com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
22  * with newer GLib versions (>= 2.31.0) */
23 #define GLIB_DISABLE_DEPRECATION_WARNINGS
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include "gstgme.h"
30 #include <gst/audio/audio.h>
31 
32 #include <string.h>
33 #include <glib/gprintf.h>
34 #include <glib.h>
35 
36 static GstStaticPadTemplate sink_factory =
37     GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
38     GST_STATIC_CAPS ("audio/x-ay; "
39         "audio/x-gbs; "
40         "audio/x-gym; "
41         "audio/x-hes; "
42         "audio/x-kss; "
43         "audio/x-nsf; " "audio/x-sap; " "audio/x-spc; " "audio/x-vgm"));
44 
45 static GstStaticPadTemplate src_factory =
46 GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
47     GST_STATIC_CAPS ("audio/x-raw, "
48         "format = (string) " GST_AUDIO_NE (S16) ", "
49         "layout = (string) interleaved, "
50         "rate = (int) 32000, " "channels = (int) 2"));
51 
52 #define gst_gme_dec_parent_class parent_class
53 G_DEFINE_TYPE (GstGmeDec, gst_gme_dec, GST_TYPE_ELEMENT);
54 GST_ELEMENT_REGISTER_DEFINE (gmedec, "gmedec", GST_RANK_PRIMARY,
55     GST_TYPE_GME_DEC);
56 
57 static GstFlowReturn gst_gme_dec_chain (GstPad * pad, GstObject * parent,
58     GstBuffer * buffer);
59 static gboolean gst_gme_dec_sink_event (GstPad * pad, GstObject * parent,
60     GstEvent * event);
61 static gboolean gst_gme_dec_src_event (GstPad * pad, GstObject * parent,
62     GstEvent * event);
63 static gboolean gst_gme_dec_src_query (GstPad * pad, GstObject * parent,
64     GstQuery * query);
65 static GstStateChangeReturn gst_gme_dec_change_state (GstElement * element,
66     GstStateChange transition);
67 static void gst_gme_play (GstPad * pad);
68 static void gst_gme_dec_dispose (GObject * object);
69 static gboolean gme_setup (GstGmeDec * gme);
70 
71 static gboolean
gme_negotiate(GstGmeDec * gme)72 gme_negotiate (GstGmeDec * gme)
73 {
74   GstCaps *caps;
75 
76   caps = gst_pad_get_pad_template_caps (gme->srcpad);
77   gst_pad_set_caps (gme->srcpad, caps);
78   gst_caps_unref (caps);
79 
80   return TRUE;
81 }
82 
83 static void
gst_gme_dec_class_init(GstGmeDecClass * klass)84 gst_gme_dec_class_init (GstGmeDecClass * klass)
85 {
86   GObjectClass *gobject_class = (GObjectClass *) klass;
87   GstElementClass *element_class = (GstElementClass *) klass;
88 
89   gobject_class->dispose = gst_gme_dec_dispose;
90 
91   gst_element_class_set_static_metadata (element_class,
92       "Gaming console music file decoder", "Codec/Audio/Decoder",
93       "Uses libgme to emulate a gaming console sound processors",
94       "Chris Lee <clee@kde.org>, Brian Koropoff <bkoropoff@gmail.com>, "
95       "Michael Pyne <mpyne@kde.org>, Sebastian Dröge <sebastian.droege@collabora.co.uk>");
96 
97   gst_element_class_add_static_pad_template (element_class, &sink_factory);
98   gst_element_class_add_static_pad_template (element_class, &src_factory);
99 
100   element_class->change_state = GST_DEBUG_FUNCPTR (gst_gme_dec_change_state);
101 }
102 
103 
104 static void
gst_gme_dec_init(GstGmeDec * gme)105 gst_gme_dec_init (GstGmeDec * gme)
106 {
107   gme->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
108   /* gst_pad_set_query_function (gme->sinkpad, NULL); */
109   gst_pad_set_event_function (gme->sinkpad, gst_gme_dec_sink_event);
110   gst_pad_set_chain_function (gme->sinkpad, gst_gme_dec_chain);
111   gst_element_add_pad (GST_ELEMENT (gme), gme->sinkpad);
112 
113   gme->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
114   gst_pad_set_event_function (gme->srcpad, gst_gme_dec_src_event);
115   gst_pad_set_query_function (gme->srcpad, gst_gme_dec_src_query);
116   gst_pad_use_fixed_caps (gme->srcpad);
117   gst_element_add_pad (GST_ELEMENT (gme), gme->srcpad);
118 
119   gme->adapter = gst_adapter_new ();
120   gme->player = NULL;
121   gme->total_duration = GST_CLOCK_TIME_NONE;
122   gme->initialized = FALSE;
123 }
124 
125 static void
gst_gme_dec_dispose(GObject * object)126 gst_gme_dec_dispose (GObject * object)
127 {
128   GstGmeDec *gme = GST_GME_DEC (object);
129 
130   if (gme->adapter) {
131     g_object_unref (gme->adapter);
132     gme->adapter = NULL;
133   }
134 
135   GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
136 }
137 
138 static GstFlowReturn
gst_gme_dec_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)139 gst_gme_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
140 {
141   GstGmeDec *gme = GST_GME_DEC (parent);
142 
143   /* Accumulate GME data until end-of-stream, then commence playback. */
144   gst_adapter_push (gme->adapter, buffer);
145 
146   return GST_FLOW_OK;
147 }
148 
149 static gboolean
gst_gme_dec_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)150 gst_gme_dec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
151 {
152   GstGmeDec *gme = GST_GME_DEC (parent);
153   gboolean result = TRUE;
154   gboolean forward = FALSE;
155 
156   switch (GST_EVENT_TYPE (event)) {
157     case GST_EVENT_EOS:
158       /* we get EOS when we loaded the complete file, now try to initialize the
159        * decoding */
160       if (!(result = gme_setup (gme))) {
161         /* can't start, post an ERROR and push EOS downstream */
162         GST_ELEMENT_ERROR (gme, STREAM, DEMUX, (NULL),
163             ("can't start playback"));
164         forward = TRUE;
165       }
166       break;
167     case GST_EVENT_CAPS:
168     case GST_EVENT_SEGMENT:
169       break;
170     default:
171       forward = TRUE;
172       break;
173   }
174   if (forward)
175     result = gst_pad_push_event (gme->srcpad, event);
176   else
177     gst_event_unref (event);
178 
179   return result;
180 }
181 
182 static gboolean
gst_gme_dec_src_event(GstPad * pad,GstObject * parent,GstEvent * event)183 gst_gme_dec_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
184 {
185   GstGmeDec *gme = GST_GME_DEC (parent);
186   gboolean result = FALSE;
187 
188   switch (GST_EVENT_TYPE (event)) {
189     case GST_EVENT_SEEK:
190     {
191       gdouble rate;
192       GstFormat format;
193       GstSeekFlags flags;
194       GstSeekType start_type, stop_type;
195       gint64 start, stop;
196       gboolean flush;
197 
198       gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
199           &stop_type, &stop);
200 
201       gst_event_unref (event);
202 
203       if (format != GST_FORMAT_TIME) {
204         GST_DEBUG_OBJECT (gme, "seeking is only supported in TIME format");
205         break;
206       }
207 
208       if (start_type != GST_SEEK_TYPE_SET || stop_type != GST_SEEK_TYPE_NONE) {
209         GST_DEBUG_OBJECT (gme, "unsupported seek type");
210         break;
211       }
212 
213       if (stop_type == GST_SEEK_TYPE_NONE)
214         stop = GST_CLOCK_TIME_NONE;
215 
216       if (start_type == GST_SEEK_TYPE_SET) {
217         GstSegment seg;
218         guint64 cur = gme_tell (gme->player) * GST_MSECOND;
219         guint64 dest = (guint64) start;
220 
221         if (gme->total_duration != GST_CLOCK_TIME_NONE)
222           dest = MIN (dest, gme->total_duration);
223 
224         if (dest == cur)
225           break;
226 
227         flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH;
228 
229         if (flush) {
230           gst_pad_push_event (gme->srcpad, gst_event_new_flush_start ());
231         } else {
232           gst_pad_stop_task (gme->srcpad);
233         }
234 
235         GST_PAD_STREAM_LOCK (gme->srcpad);
236 
237         if (flags & GST_SEEK_FLAG_SEGMENT) {
238           gst_element_post_message (GST_ELEMENT (gme),
239               gst_message_new_segment_start (GST_OBJECT (gme), format, cur));
240         }
241 
242         if (flush) {
243           gst_pad_push_event (gme->srcpad, gst_event_new_flush_stop (TRUE));
244         }
245 
246         if (stop == GST_CLOCK_TIME_NONE
247             && gme->total_duration != GST_CLOCK_TIME_NONE)
248           stop = gme->total_duration;
249 
250         gst_segment_init (&seg, GST_FORMAT_TIME);
251         seg.rate = rate;
252         seg.start = dest;
253         seg.stop = stop;
254         seg.time = dest;
255         gst_pad_push_event (gme->srcpad, gst_event_new_segment (&seg));
256 
257         gme->seekpoint = dest / GST_MSECOND;    /* nsecs to msecs */
258         gme->seeking = TRUE;
259 
260         gst_pad_start_task (gme->srcpad, (GstTaskFunction) gst_gme_play,
261             gme->srcpad, NULL);
262 
263         GST_PAD_STREAM_UNLOCK (gme->srcpad);
264         result = TRUE;
265       }
266       break;
267     }
268     default:
269       result = gst_pad_push_event (gme->sinkpad, event);
270       break;
271   }
272 
273   return result;
274 }
275 
276 static gboolean
gst_gme_dec_src_query(GstPad * pad,GstObject * parent,GstQuery * query)277 gst_gme_dec_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
278 {
279   GstGmeDec *gme = GST_GME_DEC (parent);
280   gboolean result = TRUE;
281 
282   switch (GST_QUERY_TYPE (query)) {
283     case GST_QUERY_DURATION:
284     {
285       GstFormat format;
286 
287       gst_query_parse_duration (query, &format, NULL);
288       if (!gme->initialized || format != GST_FORMAT_TIME
289           || gme->total_duration == GST_CLOCK_TIME_NONE) {
290         result = FALSE;
291         break;
292       }
293       gst_query_set_duration (query, GST_FORMAT_TIME, gme->total_duration);
294       break;
295     }
296     case GST_QUERY_POSITION:
297     {
298       GstFormat format;
299 
300       gst_query_parse_position (query, &format, NULL);
301       if (!gme->initialized || format != GST_FORMAT_TIME) {
302         result = FALSE;
303         break;
304       }
305       gst_query_set_position (query, GST_FORMAT_TIME,
306           (gint64) gme_tell (gme->player) * GST_MSECOND);
307       break;
308     }
309     default:
310       result = gst_pad_query_default (pad, parent, query);
311       break;
312   }
313 
314   return result;
315 }
316 
317 static void
gst_gme_play(GstPad * pad)318 gst_gme_play (GstPad * pad)
319 {
320   GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad));
321   GstFlowReturn flow_return;
322   GstBuffer *out;
323   gboolean seeking = gme->seeking;
324   gme_err_t gme_err = NULL;
325   const int NUM_SAMPLES = 1600; /* 4 bytes (stereo 16-bit) per sample */
326 
327   if (!seeking) {
328     GstMapInfo map;
329 
330     out = gst_buffer_new_and_alloc (NUM_SAMPLES * 4);
331     GST_BUFFER_TIMESTAMP (out) = gme_tell (gme->player) * GST_MSECOND;
332 
333     gst_buffer_map (out, &map, GST_MAP_WRITE);
334     gme_err = gme_play (gme->player, NUM_SAMPLES * 2, (short *) map.data);
335     gst_buffer_unmap (out, &map);
336 
337     if (gme_err) {
338       GST_ELEMENT_ERROR (gme, STREAM, DEMUX, (NULL), ("%s", gme_err));
339       gst_pad_pause_task (pad);
340       gst_pad_push_event (pad, gst_event_new_eos ());
341       gst_object_unref (gme);
342       return;
343     }
344   } else {
345     gme_seek (gme->player, gme->seekpoint);
346     gme->seeking = FALSE;
347 
348     out = gst_buffer_new ();
349   }
350 
351   if ((flow_return = gst_pad_push (gme->srcpad, out)) != GST_FLOW_OK) {
352     GST_DEBUG_OBJECT (gme, "pausing task, reason %s",
353         gst_flow_get_name (flow_return));
354 
355     gst_pad_pause_task (pad);
356 
357     if (flow_return == GST_FLOW_EOS) {
358       gst_pad_push_event (pad, gst_event_new_eos ());
359     } else if (flow_return < GST_FLOW_EOS || flow_return == GST_FLOW_NOT_LINKED) {
360       GST_ELEMENT_FLOW_ERROR (gme, flow_return);
361       gst_pad_push_event (pad, gst_event_new_eos ());
362     }
363   }
364 
365   if (gme_tell (gme->player) * GST_MSECOND > gme->total_duration) {
366     gst_pad_pause_task (pad);
367     gst_pad_push_event (pad, gst_event_new_eos ());
368   }
369 
370   gst_object_unref (gme);
371 
372   return;
373 }
374 
375 static gboolean
gme_setup(GstGmeDec * gme)376 gme_setup (GstGmeDec * gme)
377 {
378   gme_info_t *info;
379   gme_err_t gme_err = NULL;
380   GstTagList *taglist;
381   guint64 total_duration;
382   guint64 fade_time;
383   GstBuffer *buffer;
384   GstSegment seg;
385   GstMapInfo map;
386 
387   if (!gst_adapter_available (gme->adapter) || !gme_negotiate (gme)) {
388     return FALSE;
389   }
390 
391   buffer =
392       gst_adapter_take_buffer (gme->adapter,
393       gst_adapter_available (gme->adapter));
394 
395   gst_buffer_map (buffer, &map, GST_MAP_READ);
396   gme_err = gme_open_data (map.data, map.size, &gme->player, 32000);
397   gst_buffer_unmap (buffer, &map);
398   gst_buffer_unref (buffer);
399 
400   if (gme_err || !gme->player) {
401     if (gme->player) {
402       gme_delete (gme->player);
403       gme->player = NULL;
404     }
405 
406     GST_ELEMENT_ERROR (gme, STREAM, DEMUX, (NULL), ("%s", gme_err));
407 
408     return FALSE;
409   }
410 
411   gme_err = gme_track_info (gme->player, &info, 0);
412 
413   taglist = gst_tag_list_new_empty ();
414 
415   if (info->song && *info->song)
416     gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE,
417         info->song, NULL);
418   if (info->author && *info->author)
419     gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ARTIST,
420         info->author, NULL);
421   /* Prefer the name of the official soundtrack over the name of the game (since this is
422    * how track numbers are derived)
423    */
424   if (info->game && *info->game)
425     gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, info->game,
426         NULL);
427 
428   if (info->comment && *info->comment)
429     gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COMMENT,
430         info->comment, NULL);
431   if (info->dumper && *info->dumper)
432     gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_CONTACT,
433         info->dumper, NULL);
434   if (info->copyright && *info->copyright)
435     gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COPYRIGHT,
436         info->copyright, NULL);
437   if (info->system && *info->system)
438     gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER,
439         info->system, NULL);
440 
441   gme->total_duration = total_duration =
442       gst_util_uint64_scale_int (info->play_length + (info->loop_length >
443           0 ? 8000 : 0), GST_MSECOND, 1);
444   fade_time = info->loop_length > 0 ? info->play_length : 0;
445 
446   gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
447       GST_TAG_DURATION, total_duration, NULL);
448 
449   gst_pad_push_event (gme->srcpad, gst_event_new_tag (taglist));
450 
451   g_free (info);
452 
453 #ifdef HAVE_LIBGME_ACCURACY
454   /* TODO: Is it worth it to make this optional? */
455   gme_enable_accuracy (gme->player, 1);
456 #endif
457   gme_start_track (gme->player, 0);
458   if (fade_time)
459     gme_set_fade (gme->player, fade_time);
460 
461   gst_segment_init (&seg, GST_FORMAT_TIME);
462   gst_pad_push_event (gme->srcpad, gst_event_new_segment (&seg));
463 
464   gst_pad_start_task (gme->srcpad, (GstTaskFunction) gst_gme_play, gme->srcpad,
465       NULL);
466 
467   gme->initialized = TRUE;
468   gme->seeking = FALSE;
469   gme->seekpoint = 0;
470   return gme->initialized;
471 }
472 
473 static GstStateChangeReturn
gst_gme_dec_change_state(GstElement * element,GstStateChange transition)474 gst_gme_dec_change_state (GstElement * element, GstStateChange transition)
475 {
476   GstStateChangeReturn result;
477   GstGmeDec *dec;
478 
479   dec = GST_GME_DEC (element);
480 
481   switch (transition) {
482     case GST_STATE_CHANGE_READY_TO_PAUSED:
483       dec->total_duration = GST_CLOCK_TIME_NONE;
484       break;
485     default:
486       break;
487   }
488 
489   result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
490   if (result == GST_STATE_CHANGE_FAILURE)
491     return result;
492 
493   switch (transition) {
494     case GST_STATE_CHANGE_PAUSED_TO_READY:
495       gst_adapter_clear (dec->adapter);
496       if (dec->player) {
497         gme_delete (dec->player);
498         dec->player = NULL;
499       }
500       break;
501     default:
502       break;
503   }
504 
505   return result;
506 }
507 
508 static gboolean
plugin_init(GstPlugin * plugin)509 plugin_init (GstPlugin * plugin)
510 {
511   return GST_ELEMENT_REGISTER (gmedec, plugin);
512 }
513 
514 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
515     GST_VERSION_MINOR,
516     gme,
517     "GME Audio Decoder",
518     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
519