3 * @brief Game Music Emu demux module for VLC media player
5 /*****************************************************************************
6 * Copyright © 2010 Rémi Denis-Courmont
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_input.h>
33 #include <vlc_demux.h>
34 #include <vlc_plugin.h>
38 static int Open (vlc_object_t
*);
39 static void Close (vlc_object_t
*);
43 set_description ("Game Music Emu")
44 set_category (CAT_INPUT
)
45 set_subcategory (SUBCAT_INPUT_DEMUX
)
46 set_capability ("demux", 10)
47 set_callbacks (Open
, Close
)
60 input_title_t
**titlev
;
65 static int Demux (demux_t
*);
66 static int Control (demux_t
*, int, va_list);
67 static gme_err_t
Reader (void *, void *, int);
69 static int Open (vlc_object_t
*obj
)
71 demux_t
*demux
= (demux_t
*)obj
;
73 int64_t size
= stream_Size (demux
->s
);
74 if (size
< 4 /* GME needs to know the file size */
75 || size
> LONG_MAX
/* too big for GME */)
80 if (stream_Peek (demux
->s
, &peek
, 4) < 4)
83 const char *type
= gme_identify_header (peek
);
86 msg_Dbg (obj
, "detected file type %s", type
);
89 demux_sys_t
*sys
= malloc (sizeof (*sys
));
90 if (unlikely(sys
== NULL
))
93 sys
->emu
= gme_new_emu (gme_identify_extension (type
), RATE
);
99 gme_load_custom (sys
->emu
, Reader
, size
, demux
->s
);
100 gme_start_track (sys
->emu
, sys
->track_id
= 0);
103 es_format_Init (&fmt
, AUDIO_ES
, VLC_CODEC_S16N
);
104 fmt
.audio
.i_rate
= RATE
;
105 fmt
.audio
.i_bytes_per_frame
= 4;
106 fmt
.audio
.i_frame_length
= 4;
107 fmt
.audio
.i_channels
= 2;
108 fmt
.audio
.i_blockalign
= 4;
109 fmt
.audio
.i_bitspersample
= 16;
110 fmt
.i_bitrate
= RATE
* 4;
112 sys
->es
= es_out_Add (demux
->out
, &fmt
);
113 date_Init (&sys
->pts
, RATE
, 1);
114 date_Set (&sys
->pts
, 0);
117 unsigned n
= gme_track_count (sys
->emu
);
118 sys
->titlev
= malloc (n
* sizeof (*sys
->titlev
));
119 if (unlikely(sys
->titlev
== NULL
))
122 for (unsigned i
= 0; i
< n
; i
++)
124 input_title_t
*title
= vlc_input_title_New ();
125 sys
->titlev
[i
] = title
;
126 if (unlikely(title
== NULL
))
130 if (gme_track_info (sys
->emu
, &infos
, i
))
132 msg_Dbg (obj
, "track %u: %s %d ms", i
, infos
->song
, infos
->length
);
133 if (infos
->length
!= -1)
134 title
->i_length
= infos
->length
* INT64_C(1000);
136 title
->psz_name
= strdup (infos
->song
);
137 gme_free_info (infos
);
141 demux
->pf_demux
= Demux
;
142 demux
->pf_control
= Control
;
148 static void Close (vlc_object_t
*obj
)
150 demux_t
*demux
= (demux_t
*)obj
;
151 demux_sys_t
*sys
= demux
->p_sys
;
153 for (unsigned i
= 0, n
= sys
->titlec
; i
< n
; i
++)
154 vlc_input_title_Delete (sys
->titlev
[i
]);
156 gme_delete (sys
->emu
);
161 static gme_err_t
Reader (void *data
, void *buf
, int length
)
165 if (stream_Read (s
, buf
, length
) < length
)
171 #define SAMPLES (RATE / 10)
173 static int Demux (demux_t
*demux
)
175 demux_sys_t
*sys
= demux
->p_sys
;
178 if (gme_track_ended (sys
->emu
))
180 msg_Dbg (demux
, "track %u ended", sys
->track_id
);
181 if (++sys
->track_id
>= (unsigned)gme_track_count (sys
->emu
))
184 demux
->info
.i_update
|= INPUT_UPDATE_TITLE
;
185 demux
->info
.i_title
= sys
->track_id
;
186 gme_start_track (sys
->emu
, sys
->track_id
);
190 block_t
*block
= block_Alloc (2 * 2 * SAMPLES
);
191 if (unlikely(block
== NULL
))
194 gme_err_t ret
= gme_play (sys
->emu
, 2 * SAMPLES
, (void *)block
->p_buffer
);
197 block_Release (block
);
198 msg_Err (demux
, "%s", ret
);
202 block
->i_pts
= block
->i_dts
= VLC_TS_0
+ date_Get (&sys
->pts
);
203 es_out_Control (demux
->out
, ES_OUT_SET_PCR
, block
->i_pts
);
204 es_out_Send (demux
->out
, sys
->es
, block
);
205 date_Increment (&sys
->pts
, SAMPLES
);
210 static int Control (demux_t
*demux
, int query
, va_list args
)
212 demux_sys_t
*sys
= demux
->p_sys
;
216 case DEMUX_GET_POSITION
:
218 double *pos
= va_arg (args
, double *);
220 if (unlikely(sys
->track_id
>= sys
->titlec
)
221 || (sys
->titlev
[sys
->track_id
]->i_length
== 0))
224 *pos
= (double)(gme_tell (sys
->emu
))
225 / (double)(sys
->titlev
[sys
->track_id
]->i_length
/ 1000);
229 case DEMUX_SET_POSITION
:
231 double pos
= va_arg (args
, double);
233 if (unlikely(sys
->track_id
>= sys
->titlec
)
234 || (sys
->titlev
[sys
->track_id
]->i_length
== 0))
237 int seek
= (sys
->titlev
[sys
->track_id
]->i_length
/ 1000) * pos
;
238 if (seek
> INT_MAX
|| gme_seek (sys
->emu
, seek
))
243 case DEMUX_GET_LENGTH
:
245 int64_t *v
= va_arg (args
, int64_t *);
247 if (unlikely(sys
->track_id
>= sys
->titlec
)
248 || (sys
->titlev
[sys
->track_id
]->i_length
== 0))
250 *v
= sys
->titlev
[sys
->track_id
]->i_length
;
256 int64_t *v
= va_arg (args
, int64_t *);
257 *v
= gme_tell (sys
->emu
) * INT64_C(1000);
263 int64_t v
= va_arg (args
, int64_t) / 1000;
264 if (v
> INT_MAX
|| gme_seek (sys
->emu
, v
))
269 case DEMUX_GET_TITLE_INFO
:
271 input_title_t
***titlev
= va_arg (args
, input_title_t
***);
272 int *titlec
= va_arg (args
, int *);
273 *(va_arg (args
, int *)) = 0; /* Title offset */
274 *(va_arg (args
, int *)) = 0; /* Chapter offset */
276 unsigned n
= sys
->titlec
;
277 *titlev
= malloc (sizeof (**titlev
) * n
);
278 if (unlikely(titlev
== NULL
))
281 for (unsigned i
= 0; i
< n
; i
++)
282 (*titlev
)[i
] = vlc_input_title_Duplicate (sys
->titlev
[i
]);
286 case DEMUX_SET_TITLE
:
288 int track_id
= va_arg (args
, int);
289 if (track_id
>= gme_track_count (sys
->emu
))
291 gme_start_track (sys
->emu
, track_id
);
292 demux
->info
.i_update
|= INPUT_UPDATE_TITLE
;
293 demux
->info
.i_title
= track_id
;
294 sys
->track_id
= track_id
;