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