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