]> git.sesse.net Git - vlc/blob - modules/demux/sid.cpp
decoder: do not wait for buffering when there is no data
[vlc] / modules / demux / sid.cpp
1 /**
2  * @file sid.cpp
3  * @brief Sidplay demux module for VLC media player
4  */
5 /*****************************************************************************
6  * Copyright © 2010 Rémi Denis-Courmont
7  * Copyright © 2010 Alan Fischer <alan@lightningtoads.com>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * NOTA BENE: this module requires the linking against a library which is
26  * known to require licensing under the GNU General Public License version 2
27  * (or later). Therefore, the result of compiling this module will normally
28  * be subject to the terms of that later license.
29  *****************************************************************************/
30
31
32 #ifdef HAVE_CONFIG_H
33 # include <config.h>
34 #endif
35
36 /* INT64_C and UINT64_C are only exposed to c++ if this is defined */
37 #ifndef __STDC_CONSTANT_MACROS
38 # define __STDC_CONSTANT_MACROS
39 #endif
40
41 #include <vlc_common.h>
42 #include <vlc_input.h>
43 #include <vlc_demux.h>
44 #include <vlc_plugin.h>
45
46 #include <limits.h>
47
48 #include <sidplay/sidplay2.h>
49 #include <sidplay/builders/resid.h>
50
51 static int  Open (vlc_object_t *);
52 static void Close (vlc_object_t *);
53
54 vlc_module_begin ()
55     set_shortname ("sid")
56     set_description ( N_("C64 sid demuxer") )
57     set_category (CAT_INPUT)
58     set_subcategory (SUBCAT_INPUT_DEMUX)
59     set_capability ("demux", 100)
60     set_callbacks (Open, Close)
61 vlc_module_end ()
62
63 struct demux_sys_t
64 {
65     sidplay2 *player;
66     sid2_config_t config;
67     sid2_info_t info;
68     SidTune *tune;
69     SidTuneInfo tuneInfo;
70
71     int bytes_per_frame;
72     int block_size;
73     es_out_id_t *es;
74     date_t pts;
75 };
76
77
78 static int Demux (demux_t *);
79 static int Control (demux_t *, int, va_list);
80
81 static int Open (vlc_object_t *obj)
82 {
83     demux_t *demux = (demux_t *)obj;
84     demux_sys_t *sys = NULL;
85     es_format_t fmt;
86     bool result = false;
87     SidTune *tune = NULL;
88     sidplay2 *player = NULL;
89     ReSIDBuilder *builder = NULL;
90
91     int64_t size = stream_Size (demux->s);
92     if (size < 4 || size > LONG_MAX) /* We need to load the whole file for sidplay */
93         return VLC_EGENERIC;
94
95     const uint8_t *peek;
96     if (stream_Peek (demux->s, &peek, 4) < 4)
97         return VLC_EGENERIC;
98
99     /* sidplay2 can read PSID and the newer RSID formats */
100     if(memcmp(peek,"PSID",4)!=0 && memcmp(peek,"RSID",4)!=0)
101         return VLC_EGENERIC;
102
103     uint8_t *data = (uint8_t*) malloc(size);
104     if (unlikely (data==NULL))
105         goto error;
106
107     if (stream_Read (demux->s,data,size) < size) {
108         free (data);
109         goto error;
110     }
111
112     tune = new SidTune(0);
113     if (unlikely (tune==NULL)) {
114         free (data);
115         goto error;
116     }
117
118     result = tune->read (data, size);
119     free (data);
120     if (!result)
121         goto error;
122
123     player = new sidplay2();
124     if (unlikely(player==NULL))
125         goto error;
126
127     sys = (demux_sys_t*) calloc (1, sizeof(demux_sys_t));
128     if (unlikely(sys==NULL))
129         goto error;
130
131     sys->player = player;
132     sys->tune = tune;
133
134     tune->getInfo (sys->tuneInfo);
135
136     sys->info = player->info();
137     sys->config = player->config();
138
139     builder = new ReSIDBuilder ("ReSID");
140     if (unlikely(builder==NULL))
141         goto error;
142
143     builder->create (sys->info.maxsids);
144     builder->sampling (sys->config.frequency);
145
146     sys->config.sidEmulation = builder;
147     sys->config.precision    = 16;
148     sys->config.playback     = (sys->info.channels == 2 ? sid2_stereo : sid2_mono);
149
150     player->config (sys->config);
151
152     sys->bytes_per_frame = sys->info.channels * sys->config.precision / 8;
153     sys->block_size = sys->config.frequency / 10 * sys->bytes_per_frame;
154
155     es_format_Init (&fmt, AUDIO_ES, VLC_CODEC_S16N);
156
157     fmt.audio.i_channels        = sys->info.channels;
158     fmt.audio.i_bitspersample   = sys->config.precision;
159     fmt.audio.i_rate            = sys->config.frequency;
160     fmt.audio.i_bytes_per_frame = sys->bytes_per_frame;
161     fmt.audio.i_frame_length    = fmt.audio.i_bytes_per_frame;
162     fmt.audio.i_blockalign      = fmt.audio.i_bytes_per_frame;
163
164     fmt.i_bitrate = fmt.audio.i_rate * fmt.audio.i_bytes_per_frame;
165
166     sys->es = es_out_Add (demux->out, &fmt);
167
168     date_Init (&sys->pts, fmt.audio.i_rate, 1);
169     date_Set (&sys->pts, 0);
170
171     sys->tune->selectSong (0);
172     result = (sys->player->load (sys->tune) >=0 );
173     sys->player->fastForward (100);
174     if (!result)
175         goto error;
176
177     /* Callbacks */
178     demux->pf_demux = Demux;
179     demux->pf_control = Control;
180     demux->p_sys = sys;
181
182     return VLC_SUCCESS;
183
184 error:
185     msg_Err (demux, "An error occurred during sid demuxing" );
186     delete player;
187     delete builder;
188     delete tune;
189     free (sys);
190     return VLC_EGENERIC;
191 }
192
193
194 static void Close (vlc_object_t *obj)
195 {
196     demux_t *demux = (demux_t *)obj;
197     demux_sys_t *sys = demux->p_sys;
198
199     delete sys->player;
200     delete sys->config.sidEmulation;
201     delete sys->tune;
202     free (sys);
203 }
204
205 static int Demux (demux_t *demux)
206 {
207     demux_sys_t *sys = demux->p_sys;
208
209     block_t *block = block_Alloc( sys->block_size);
210     if (unlikely(block==NULL))
211         return 0;
212
213     if (!sys->tune->getStatus()) {
214         block_Release (block);
215         return 0;
216     }
217
218     int i_read = sys->player->play ((void*)block->p_buffer, block->i_buffer);
219     if (i_read <= 0) {
220         block_Release (block);
221         return 0;
222     }
223     block->i_buffer = i_read;
224     block->i_pts = block->i_dts = VLC_TS_0 + date_Get (&sys->pts);
225
226     es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts);
227
228     es_out_Send (demux->out, sys->es, block);
229
230     date_Increment (&sys->pts, i_read / sys->bytes_per_frame);
231
232     return 1;
233 }
234
235
236 static int Control (demux_t *demux, int query, va_list args)
237 {
238     demux_sys_t *sys = demux->p_sys;
239
240     switch (query)
241     {
242         case DEMUX_GET_TIME : {
243             int64_t *v = va_arg (args, int64_t*);
244             *v = sys->player->time() * sys->player->timebase() * (CLOCK_FREQ / 100);
245             return VLC_SUCCESS;
246         }
247
248         case DEMUX_GET_META : {
249             vlc_meta_t *p_meta = (vlc_meta_t *) va_arg (args, vlc_meta_t*);
250
251             /* These are specified in the sid tune class as 0 = Title, 1 = Artist, 2 = Copyright/Publisher */
252             vlc_meta_SetTitle( p_meta, sys->tuneInfo.infoString[0] );
253             vlc_meta_SetArtist( p_meta, sys->tuneInfo.infoString[1] );
254             vlc_meta_SetCopyright( p_meta, sys->tuneInfo.infoString[2] );
255
256             return VLC_SUCCESS;
257         }
258
259         case DEMUX_GET_TITLE_INFO :
260             if ( sys->tuneInfo.songs > 1 ) {
261                 input_title_t ***ppp_title = (input_title_t***) va_arg (args, input_title_t***);
262                 int *pi_int    = (int*)va_arg( args, int* );
263
264                 *pi_int = sys->tuneInfo.songs;
265                 *ppp_title = (input_title_t**) malloc( sizeof (input_title_t*) * sys->tuneInfo.songs);
266
267                 for( int i = 0; i < sys->tuneInfo.songs; i++ ) {
268                     (*ppp_title)[i] = vlc_input_title_New();
269                 }
270
271                 return VLC_SUCCESS;
272             }
273             return VLC_EGENERIC;
274
275         case DEMUX_SET_TITLE : {
276             int i_idx = (int) va_arg (args, int);
277             sys->tune->selectSong (i_idx+1);
278             bool result = (sys->player->load (sys->tune) >=0 );
279             if (!result)
280                 return  VLC_EGENERIC;
281
282             demux->info.i_title = i_idx;
283             demux->info.i_update = INPUT_UPDATE_TITLE;
284             msg_Dbg( demux, "set song %i", i_idx);
285
286             return VLC_SUCCESS;
287         }
288     }
289
290     return VLC_EGENERIC;
291 }