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