]> git.sesse.net Git - vlc/blob - modules/demux/gme.c
mediacodec: don't loop in GetOutput
[vlc] / modules / demux / gme.c
1 /**
2  * @file gme.c
3  * @brief Game Music Emu demux module for VLC media player
4  */
5 /*****************************************************************************
6  * Copyright © 2010 Rémi Denis-Courmont
7  *
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.
12  *
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.
17  *
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  *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <stdarg.h>
28 #include <limits.h>
29 #include <assert.h>
30
31 #include <vlc_common.h>
32 #include <vlc_input.h>
33 #include <vlc_demux.h>
34 #include <vlc_plugin.h>
35
36 #include <gme/gme.h>
37
38 static int  Open (vlc_object_t *);
39 static void Close (vlc_object_t *);
40
41 vlc_module_begin ()
42     set_shortname ("GME")
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)
48 vlc_module_end ()
49
50 #define RATE 48000
51
52 struct demux_sys_t
53 {
54     Music_Emu   *emu;
55     unsigned     track_id;
56
57     es_out_id_t *es;
58     date_t       pts;
59
60     input_title_t **titlev;
61     unsigned        titlec;
62 };
63
64
65 static int Demux (demux_t *);
66 static int Control (demux_t *, int, va_list);
67 static gme_err_t ReaderStream (void *, void *, int);
68 static gme_err_t ReaderBlock (void *, void *, int);
69
70 static int Open (vlc_object_t *obj)
71 {
72     demux_t *demux = (demux_t *)obj;
73
74     int64_t size = stream_Size (demux->s);
75     if (size > LONG_MAX /* too big for GME */)
76         return VLC_EGENERIC;
77
78     /* Auto detection */
79     const uint8_t *peek;
80     if (stream_Peek (demux->s, &peek, 4) < 4)
81         return VLC_EGENERIC;
82
83     const char *type = gme_identify_header (peek);
84     if (!*type)
85         return VLC_EGENERIC;
86     msg_Dbg (obj, "detected file type %s", type);
87
88     block_t *data = NULL;
89     if (size <= 0)
90     {
91         data = stream_BlockRemaining (demux->s, 100000000);
92         if (!data )
93             return VLC_EGENERIC;
94     }
95
96     /* Initialization */
97     demux_sys_t *sys = malloc (sizeof (*sys));
98     if (unlikely(sys == NULL))
99         return VLC_ENOMEM;
100
101     sys->emu = gme_new_emu (gme_identify_extension (type), RATE);
102     if (sys->emu == NULL)
103     {
104         free (sys);
105         return VLC_ENOMEM;
106     }
107     if (data)
108     {
109         gme_load_custom (sys->emu, ReaderBlock, data->i_buffer, data);
110         block_Release(data);
111     }
112     else
113     {
114         gme_load_custom (sys->emu, ReaderStream, size, demux->s);
115     }
116     gme_start_track (sys->emu, sys->track_id = 0);
117
118     es_format_t fmt;
119     es_format_Init (&fmt, AUDIO_ES, VLC_CODEC_S16N);
120     fmt.audio.i_rate = RATE;
121     fmt.audio.i_bytes_per_frame = 4;
122     fmt.audio.i_frame_length = 4;
123     fmt.audio.i_channels = 2;
124     fmt.audio.i_blockalign = 4;
125     fmt.audio.i_bitspersample = 16;
126     fmt.i_bitrate = RATE * 4;
127
128     sys->es = es_out_Add (demux->out, &fmt);
129     date_Init (&sys->pts, RATE, 1);
130     date_Set (&sys->pts, 0);
131
132     /* Titles */
133     unsigned n = gme_track_count (sys->emu);
134     sys->titlev = malloc (n * sizeof (*sys->titlev));
135     if (unlikely(sys->titlev == NULL))
136         n = 0;
137     sys->titlec = n;
138     for (unsigned i = 0; i < n; i++)
139     {
140          input_title_t *title = vlc_input_title_New ();
141          sys->titlev[i] = title;
142          if (unlikely(title == NULL))
143              continue;
144
145          gme_info_t *infos;
146          if (gme_track_info (sys->emu, &infos, i))
147              continue;
148          msg_Dbg (obj, "track %u: %s %d ms", i, infos->song, infos->length);
149          if (infos->length != -1)
150              title->i_length = infos->length * INT64_C(1000);
151          if (infos->song[0])
152              title->psz_name = strdup (infos->song);
153          gme_free_info (infos);
154     }
155
156     /* Callbacks */
157     demux->pf_demux = Demux;
158     demux->pf_control = Control;
159     demux->p_sys = sys;
160     return VLC_SUCCESS;
161 }
162
163
164 static void Close (vlc_object_t *obj)
165 {
166     demux_t *demux = (demux_t *)obj;
167     demux_sys_t *sys = demux->p_sys;
168
169     for (unsigned i = 0, n = sys->titlec; i < n; i++)
170         vlc_input_title_Delete (sys->titlev[i]);
171     free (sys->titlev);
172     gme_delete (sys->emu);
173     free (sys);
174 }
175
176
177 static gme_err_t ReaderStream (void *data, void *buf, int length)
178 {
179     stream_t *s = data;
180
181     if (stream_Read (s, buf, length) < length)
182         return "short read";
183     return NULL;
184 }
185 static gme_err_t ReaderBlock (void *data, void *buf, int length)
186 {
187     block_t *block = data;
188
189     int max = __MIN (length, (int)block->i_buffer);
190     memcpy (buf, block->p_buffer, max);
191     block->i_buffer -= max;
192     block->p_buffer += max;
193     if (max != length)
194         return "short read";
195     return NULL;
196 }
197
198 #define SAMPLES (RATE / 10)
199
200 static int Demux (demux_t *demux)
201 {
202     demux_sys_t *sys = demux->p_sys;
203
204     /* Next track */
205     if (gme_track_ended (sys->emu))
206     {
207         msg_Dbg (demux, "track %u ended", sys->track_id);
208         if (++sys->track_id >= (unsigned)gme_track_count (sys->emu))
209             return 0;
210
211         demux->info.i_update |= INPUT_UPDATE_TITLE;
212         demux->info.i_title = sys->track_id;
213         gme_start_track (sys->emu, sys->track_id);
214     }
215
216
217     block_t *block = block_Alloc (2 * 2 * SAMPLES);
218     if (unlikely(block == NULL))
219         return 0;
220
221     gme_err_t ret = gme_play (sys->emu, 2 * SAMPLES, (void *)block->p_buffer);
222     if (ret != NULL)
223     {
224         block_Release (block);
225         msg_Err (demux, "%s", ret);
226         return 0;
227     }
228
229     block->i_pts = block->i_dts = VLC_TS_0 + date_Get (&sys->pts);
230     es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts);
231     es_out_Send (demux->out, sys->es, block);
232     date_Increment (&sys->pts, SAMPLES);
233     return 1;
234 }
235
236
237 static int Control (demux_t *demux, int query, va_list args)
238 {
239     demux_sys_t *sys = demux->p_sys;
240
241     switch (query)
242     {
243         case DEMUX_GET_POSITION:
244         {
245             double *pos = va_arg (args, double *);
246
247             if (unlikely(sys->track_id >= sys->titlec)
248              || (sys->titlev[sys->track_id]->i_length == 0))
249                 *pos = 0.;
250             else
251             {
252                 int offset = gme_tell (sys->emu);
253
254                 *pos = (double)offset
255                     / (double)(sys->titlev[sys->track_id]->i_length / 1000);
256             }
257             return VLC_SUCCESS;
258         }
259
260         case DEMUX_SET_POSITION:
261         {
262             double pos = va_arg (args, double);
263
264             if (unlikely(sys->track_id >= sys->titlec)
265              || (sys->titlev[sys->track_id]->i_length == 0))
266                 break;
267
268             int seek = (sys->titlev[sys->track_id]->i_length / 1000) * pos;
269             if (gme_seek (sys->emu, seek))
270                 break;
271             return VLC_SUCCESS;
272         }
273
274         case DEMUX_GET_LENGTH:
275         {
276             int64_t *v = va_arg (args, int64_t *);
277
278             if (unlikely(sys->track_id >= sys->titlec)
279              || (sys->titlev[sys->track_id]->i_length == 0))
280                 break;
281             *v = sys->titlev[sys->track_id]->i_length;
282             return VLC_SUCCESS;
283         }
284
285         case DEMUX_GET_TIME:
286         {
287             int64_t *v = va_arg (args, int64_t *);
288             *v = gme_tell (sys->emu) * INT64_C(1000);
289             return VLC_SUCCESS;
290         }
291
292         case DEMUX_SET_TIME:
293         {
294             int64_t v = va_arg (args, int64_t) / 1000;
295             if (v > INT_MAX || gme_seek (sys->emu, v))
296                 break;
297             return VLC_SUCCESS;
298         }
299
300         case DEMUX_GET_TITLE_INFO:
301         {
302             input_title_t ***titlev = va_arg (args, input_title_t ***);
303             int *titlec = va_arg (args, int *);
304             *(va_arg (args, int *)) = 0; /* Title offset */
305             *(va_arg (args, int *)) = 0; /* Chapter offset */
306
307             unsigned n = sys->titlec;
308             *titlev = malloc (sizeof (**titlev) * n);
309             if (unlikely(*titlev == NULL))
310                 n = 0;
311             *titlec = n;
312             for (unsigned i = 0; i < n; i++)
313                 (*titlev)[i] = vlc_input_title_Duplicate (sys->titlev[i]);
314             return VLC_SUCCESS;
315         }
316
317         case DEMUX_SET_TITLE:
318         {
319             int track_id = va_arg (args, int);
320             if (track_id >= gme_track_count (sys->emu))
321                 break;
322             gme_start_track (sys->emu, track_id);
323             demux->info.i_update |= INPUT_UPDATE_TITLE;
324             demux->info.i_title = track_id;
325             sys->track_id = track_id;
326             return VLC_SUCCESS;
327         }
328     }
329
330     return VLC_EGENERIC;
331 }