1 /*
2 * Copyright (C) 2001-2018 the xine project
3 *
4 * This file is part of xine, a free video player.
5 *
6 * xine is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * xine is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19 */
20
21 /*
22 * Creative Voice File Demuxer by Mike Melanson (melanson@pcisys.net)
23 * Note that this demuxer does not yet support very many things that can
24 * possibly be seen in a VOC file. It only plays the first block in a file.
25 * It will only play that block if it is PCM data. More variations will be
26 * supported as they are encountered.
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <stdio.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39
40 #include <xine/xine_internal.h>
41 #include <xine/xineutils.h>
42 #include <xine/demux.h>
43 #include <xine/buffer.h>
44 #include "bswap.h"
45 #include "group_audio.h"
46
47 #define PCM_BLOCK_ALIGN 1024
48 #define VOC_HEADER_SIZE 0x1A
49 #define VOC_SIGNATURE "Creative Voice File\x1A"
50 #define BLOCK_PREAMBLE_SIZE 4
51
52 typedef struct {
53 demux_plugin_t demux_plugin;
54
55 xine_stream_t *stream;
56 fifo_buffer_t *audio_fifo;
57 input_plugin_t *input;
58 int status;
59
60 unsigned int audio_type;
61 unsigned int audio_sample_rate;
62 unsigned int audio_bits;
63 unsigned int audio_channels;
64
65 off_t data_start;
66 off_t data_size;
67 unsigned int running_time;
68
69 int seek_flag; /* this is set when a seek just occurred */
70 } demux_voc_t;
71
72
probe_voc_file(input_plugin_t * input,int * first_block_offset)73 static int probe_voc_file(input_plugin_t *input, int *first_block_offset) {
74 unsigned char header[VOC_HEADER_SIZE];
75
76 if (_x_demux_read_header(input, header, VOC_HEADER_SIZE) != VOC_HEADER_SIZE)
77 return 0;
78
79 /* check the signature */
80 if (memcmp(header, VOC_SIGNATURE, sizeof(VOC_SIGNATURE)-1) != 0)
81 return 0;
82
83 *first_block_offset = _X_LE_16(&header[0x14]);
84
85 return 1;
86 }
87
88 /* returns 1 if the VOC file was opened successfully, 0 otherwise */
open_voc_file(demux_voc_t * this,int first_block_offset)89 static int open_voc_file(demux_voc_t *this, int first_block_offset) {
90 unsigned char preamble[BLOCK_PREAMBLE_SIZE];
91 unsigned char sample_rate_divisor;
92
93 if (this->input->seek(this->input, first_block_offset, SEEK_SET) != first_block_offset)
94 return 0;
95
96 /* load the block preamble */
97 if (this->input->read(this->input, preamble, BLOCK_PREAMBLE_SIZE) !=
98 BLOCK_PREAMBLE_SIZE)
99 return 0;
100
101 /* so far, this demuxer only cares about type 1 blocks */
102 if (preamble[0] != 1) {
103 xine_log(this->stream->xine, XINE_LOG_MSG,
104 _("unknown VOC block type (0x%02X); please report to xine developers\n"),
105 preamble[0]);
106 return 0;
107 }
108
109 /* assemble 24-bit, little endian length */
110 this->data_size = _X_LE_24(&preamble[1]);
111
112 /* get the next 2 bytes (re-use preamble bytes) */
113 if (this->input->read(this->input, preamble, 2) != 2)
114 return 0;
115
116 /* this app only knows how to deal with format 0 data (raw PCM) */
117 if (preamble[1] != 0) {
118 xine_log(this->stream->xine, XINE_LOG_MSG,
119 _("unknown VOC compression type (0x%02X); please report to xine developers\n"),
120 preamble[1]);
121 return 0;
122 }
123
124 this->audio_type = BUF_AUDIO_LPCM_BE;
125 sample_rate_divisor = preamble[0];
126 this->audio_sample_rate = 1000000 / (256 - sample_rate_divisor);
127 this->data_start = this->input->get_current_pos(this->input);
128 this->audio_bits = 8;
129 this->audio_channels = 1;
130 this->running_time = this->data_size / this->audio_sample_rate;
131
132 return 1;
133 }
134
demux_voc_send_chunk(demux_plugin_t * this_gen)135 static int demux_voc_send_chunk(demux_plugin_t *this_gen) {
136 demux_voc_t *this = (demux_voc_t *) this_gen;
137
138 buf_element_t *buf = NULL;
139 unsigned int remaining_sample_bytes;
140 off_t current_file_pos;
141 int64_t current_pts;
142
143 /* just load data chunks from wherever the stream happens to be
144 * pointing; issue a DEMUX_FINISHED status if EOF is reached */
145 remaining_sample_bytes = PCM_BLOCK_ALIGN;
146 current_file_pos =
147 this->input->get_current_pos(this->input) - this->data_start;
148
149 current_pts = current_file_pos;
150 current_pts *= 90000;
151 current_pts /= this->audio_sample_rate;
152
153 if (this->seek_flag) {
154 _x_demux_control_newpts(this->stream, current_pts, BUF_FLAG_SEEK);
155 this->seek_flag = 0;
156 }
157
158 while (remaining_sample_bytes) {
159 /* abort if no audio fifo */
160 if(!this->audio_fifo) {
161 this->status = DEMUX_FINISHED;
162 break;
163 }
164
165 buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
166 buf->type = this->audio_type;
167 if( this->data_size )
168 buf->extra_info->input_normpos = (int)( (double) current_file_pos * 65535 / this->data_size);
169 buf->extra_info->input_time = current_pts / 90;
170 buf->pts = current_pts;
171
172 if ((int)remaining_sample_bytes > buf->max_size)
173 buf->size = buf->max_size;
174 else
175 buf->size = remaining_sample_bytes;
176 remaining_sample_bytes -= buf->size;
177
178 if (this->input->read(this->input, buf->content, buf->size) !=
179 buf->size) {
180 buf->free_buffer(buf);
181 this->status = DEMUX_FINISHED;
182 break;
183 }
184
185 if (!remaining_sample_bytes)
186 buf->decoder_flags |= BUF_FLAG_FRAME_END;
187
188 this->audio_fifo->put (this->audio_fifo, buf);
189 }
190
191 return this->status;
192 }
193
demux_voc_send_headers(demux_plugin_t * this_gen)194 static void demux_voc_send_headers(demux_plugin_t *this_gen) {
195 demux_voc_t *this = (demux_voc_t *) this_gen;
196 buf_element_t *buf;
197
198 this->audio_fifo = this->stream->audio_fifo;
199
200 this->status = DEMUX_OK;
201
202 /* load stream information */
203 _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_VIDEO, 0);
204 _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_AUDIO, 1);
205 _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_CHANNELS,
206 this->audio_channels);
207 _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE,
208 this->audio_sample_rate);
209 _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_BITS,
210 this->audio_bits);
211
212 /* send start buffers */
213 _x_demux_control_start(this->stream);
214
215 /* send init info to decoders */
216 if (this->audio_fifo && this->audio_type) {
217 buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
218 buf->type = this->audio_type;
219 buf->decoder_flags = BUF_FLAG_HEADER|BUF_FLAG_STDHEADER|BUF_FLAG_FRAME_END;
220 buf->decoder_info[0] = 0;
221 buf->decoder_info[1] = this->audio_sample_rate;
222 buf->decoder_info[2] = this->audio_bits;
223 buf->decoder_info[3] = this->audio_channels;
224 buf->size = 0;
225 this->audio_fifo->put (this->audio_fifo, buf);
226 }
227 }
228
demux_voc_seek(demux_plugin_t * this_gen,off_t start_pos,int start_time,int playing)229 static int demux_voc_seek (demux_plugin_t *this_gen, off_t start_pos, int start_time, int playing) {
230 demux_voc_t *this = (demux_voc_t *) this_gen;
231
232 start_pos = (off_t) ( (double) start_pos / 65535 *
233 this->data_size );
234
235 (void)start_time;
236 (void)playing;
237
238 this->seek_flag = 1;
239 this->status = DEMUX_OK;
240 _x_demux_flush_engine (this->stream);
241
242 /* if input is non-seekable, do not proceed with the rest of this
243 * seek function */
244 if (!INPUT_IS_SEEKABLE(this->input))
245 return this->status;
246
247 /* check the boundary offsets */
248 if (start_pos < 0)
249 this->input->seek(this->input, this->data_start, SEEK_SET);
250 else if (start_pos >= this->data_size) {
251 this->status = DEMUX_FINISHED;
252 return this->status;
253 } else {
254 /* This function must seek along the block alignment. The start_pos
255 * is in reference to the start of the data. Divide the start_pos by
256 * the block alignment integer-wise, and multiply the quotient by the
257 * block alignment to get the new aligned offset. Add the data start
258 * offset and seek to the new position. */
259 start_pos /= PCM_BLOCK_ALIGN;
260 start_pos *= PCM_BLOCK_ALIGN;
261 start_pos += this->data_start;
262
263 this->input->seek(this->input, start_pos, SEEK_SET);
264 }
265
266 return this->status;
267 }
268
demux_voc_get_status(demux_plugin_t * this_gen)269 static int demux_voc_get_status (demux_plugin_t *this_gen) {
270 demux_voc_t *this = (demux_voc_t *) this_gen;
271
272 return this->status;
273 }
274
275 /* return the approximate length in miliseconds */
demux_voc_get_stream_length(demux_plugin_t * this_gen)276 static int demux_voc_get_stream_length (demux_plugin_t *this_gen) {
277 demux_voc_t *this = (demux_voc_t *) this_gen;
278
279 return this->running_time * 1000;
280 }
281
demux_voc_get_capabilities(demux_plugin_t * this_gen)282 static uint32_t demux_voc_get_capabilities(demux_plugin_t *this_gen) {
283 (void)this_gen;
284 return DEMUX_CAP_NOCAP;
285 }
286
demux_voc_get_optional_data(demux_plugin_t * this_gen,void * data,int data_type)287 static int demux_voc_get_optional_data(demux_plugin_t *this_gen,
288 void *data, int data_type) {
289 (void)this_gen;
290 (void)data;
291 (void)data_type;
292 return DEMUX_OPTIONAL_UNSUPPORTED;
293 }
294
open_plugin(demux_class_t * class_gen,xine_stream_t * stream,input_plugin_t * input)295 static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *stream,
296 input_plugin_t *input) {
297
298 demux_voc_t *this;
299 int first_block_offset;
300
301 switch (stream->content_detection_method) {
302 case METHOD_BY_MRL:
303 case METHOD_BY_CONTENT:
304 case METHOD_EXPLICIT:
305 if (!probe_voc_file(input, &first_block_offset))
306 return NULL;
307 break;
308 default:
309 return NULL;
310 }
311
312 this = calloc(1, sizeof(demux_voc_t));
313 if (!this)
314 return NULL;
315
316 this->stream = stream;
317 this->input = input;
318
319 this->demux_plugin.send_headers = demux_voc_send_headers;
320 this->demux_plugin.send_chunk = demux_voc_send_chunk;
321 this->demux_plugin.seek = demux_voc_seek;
322 this->demux_plugin.dispose = default_demux_plugin_dispose;
323 this->demux_plugin.get_status = demux_voc_get_status;
324 this->demux_plugin.get_stream_length = demux_voc_get_stream_length;
325 this->demux_plugin.get_capabilities = demux_voc_get_capabilities;
326 this->demux_plugin.get_optional_data = demux_voc_get_optional_data;
327 this->demux_plugin.demux_class = class_gen;
328
329 this->status = DEMUX_FINISHED;
330
331 if (!open_voc_file(this, first_block_offset)) {
332 free (this);
333 return NULL;
334 }
335
336 return &this->demux_plugin;
337 }
338
demux_voc_init_plugin(xine_t * xine,const void * data)339 void *demux_voc_init_plugin (xine_t *xine, const void *data) {
340
341 (void)xine;
342 (void)data;
343
344 static const demux_class_t demux_voc_class = {
345 .open_plugin = open_plugin,
346 .description = N_("VOC file demux plugin"),
347 .identifier = "VOC",
348 .mimetypes = NULL,
349 .extensions = "voc",
350 .dispose = NULL,
351 };
352
353 return (void *)&demux_voc_class;
354 }
355