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