/src/vlc/modules/demux/xa.c
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | * xa.c : xa file demux module for vlc |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2005 Rémi Denis-Courmont |
5 | | * |
6 | | * Authors: 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 | | /***************************************************************************** |
24 | | * Preamble |
25 | | *****************************************************************************/ |
26 | | |
27 | | #ifdef HAVE_CONFIG_H |
28 | | # include "config.h" |
29 | | #endif |
30 | | |
31 | | #include <assert.h> |
32 | | #include <vlc_common.h> |
33 | | #include <vlc_plugin.h> |
34 | | #include <vlc_demux.h> |
35 | | |
36 | | /***************************************************************************** |
37 | | * Module descriptor |
38 | | *****************************************************************************/ |
39 | | static int Open ( vlc_object_t * ); |
40 | | |
41 | 4 | vlc_module_begin () |
42 | 2 | set_description( N_("XA demuxer") ) |
43 | 2 | set_subcategory( SUBCAT_INPUT_DEMUX ) |
44 | 2 | set_capability( "demux", 10 ) |
45 | 2 | set_callback( Open ) |
46 | 2 | vlc_module_end () |
47 | | |
48 | | /***************************************************************************** |
49 | | * Local prototypes |
50 | | *****************************************************************************/ |
51 | | static int Demux ( demux_t * ); |
52 | | static int Control( demux_t *, int i_query, va_list args ); |
53 | | |
54 | | typedef struct |
55 | | { |
56 | | es_out_id_t *p_es; |
57 | | |
58 | | unsigned int i_data_size; |
59 | | unsigned int i_block_frames; |
60 | | unsigned int i_frame_size; |
61 | | unsigned int i_bitrate; |
62 | | |
63 | | date_t pts; |
64 | | } demux_sys_t; |
65 | | |
66 | | typedef struct xa_header_t |
67 | | { |
68 | | char xa_id[4]; |
69 | | uint32_t iSize; |
70 | | |
71 | | uint16_t wFormatTag; |
72 | | uint16_t nChannels; |
73 | | uint32_t nSamplesPerSec; |
74 | | uint32_t nAvgBytesPerSec; |
75 | | uint16_t nBlockAlign; |
76 | | uint16_t wBitsPerSample; |
77 | | } xa_header_t; |
78 | | |
79 | 0 | #define HEADER_LENGTH 24 |
80 | | |
81 | | static_assert(offsetof(xa_header_t, wBitsPerSample) == 22, "Bad padding"); |
82 | | |
83 | 0 | #define FRAME_LENGTH 28 /* samples per frame */ |
84 | | |
85 | | /***************************************************************************** |
86 | | * Open: check file and initializes structures |
87 | | *****************************************************************************/ |
88 | | static int Open( vlc_object_t * p_this ) |
89 | 0 | { |
90 | 0 | demux_t *p_demux = (demux_t*)p_this; |
91 | 0 | const uint8_t *peek; |
92 | | |
93 | | /* XA file heuristic */ |
94 | 0 | if( vlc_stream_Peek( p_demux->s, &peek, 10 ) < 10 ) |
95 | 0 | return VLC_EGENERIC; |
96 | 0 | if( memcmp( peek, "XAI", 4 ) && memcmp( peek, "XAJ", 4 ) && |
97 | 0 | memcmp( peek, "XA\0", 4 ) ) |
98 | 0 | return VLC_EGENERIC; |
99 | 0 | if( GetWLE( peek + 8 ) != 1 ) /* format tag */ |
100 | 0 | return VLC_EGENERIC; |
101 | | |
102 | 0 | demux_sys_t *p_sys = vlc_obj_malloc( p_this, sizeof (*p_sys) ); |
103 | 0 | if( unlikely( p_sys == NULL ) ) |
104 | 0 | return VLC_ENOMEM; |
105 | | |
106 | | /* read XA header*/ |
107 | 0 | xa_header_t xa; |
108 | |
|
109 | 0 | if( vlc_stream_Read( p_demux->s, &xa, HEADER_LENGTH ) < HEADER_LENGTH ) |
110 | 0 | return VLC_EGENERIC; |
111 | | |
112 | 0 | es_format_t fmt; |
113 | 0 | es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_ADPCM_XA_EA ); |
114 | |
|
115 | 0 | msg_Dbg( p_demux, "assuming EA ADPCM audio codec" ); |
116 | 0 | fmt.audio.i_rate = GetDWLE( &xa.nSamplesPerSec ); |
117 | 0 | fmt.audio.i_bytes_per_frame = 15 * GetWLE( &xa.nChannels ); |
118 | 0 | fmt.audio.i_frame_length = FRAME_LENGTH; |
119 | |
|
120 | 0 | fmt.audio.i_channels = GetWLE ( &xa.nChannels ); |
121 | 0 | fmt.audio.i_blockalign = fmt.audio.i_bytes_per_frame; |
122 | 0 | fmt.audio.i_bitspersample = GetWLE( &xa.wBitsPerSample ); |
123 | 0 | fmt.i_bitrate = (fmt.audio.i_rate * fmt.audio.i_bytes_per_frame * 8) |
124 | 0 | / fmt.audio.i_frame_length; |
125 | | |
126 | | /* FIXME: better computation */ |
127 | 0 | p_sys->i_data_size = xa.iSize * 15 / 56; |
128 | | /* How many frames per block (1:1 is too CPU intensive) */ |
129 | 0 | p_sys->i_block_frames = fmt.audio.i_rate / (FRAME_LENGTH * 20) + 1; |
130 | 0 | p_sys->i_frame_size = fmt.audio.i_bytes_per_frame; |
131 | 0 | p_sys->i_bitrate = fmt.i_bitrate; |
132 | |
|
133 | 0 | msg_Dbg( p_demux, "fourcc: %4.4s, channels: %d, " |
134 | 0 | "freq: %d Hz, bitrate: %dKo/s, blockalign: %d", |
135 | 0 | (char *)&fmt.i_codec, fmt.audio.i_channels, fmt.audio.i_rate, |
136 | 0 | fmt.i_bitrate / 8192, fmt.audio.i_blockalign ); |
137 | |
|
138 | 0 | if( fmt.audio.i_rate == 0 || fmt.audio.i_channels == 0 |
139 | 0 | || fmt.audio.i_bitspersample != 16 ) |
140 | 0 | return VLC_EGENERIC; |
141 | | |
142 | 0 | p_sys->p_es = es_out_Add( p_demux->out, &fmt ); |
143 | 0 | if( unlikely(p_sys->p_es == NULL) ) |
144 | 0 | return VLC_ENOMEM; |
145 | | |
146 | 0 | date_Init( &p_sys->pts, fmt.audio.i_rate, 1 ); |
147 | 0 | date_Set( &p_sys->pts, VLC_TICK_0 ); |
148 | |
|
149 | 0 | p_demux->pf_demux = Demux; |
150 | 0 | p_demux->pf_control = Control; |
151 | 0 | p_demux->p_sys = p_sys; |
152 | 0 | return VLC_SUCCESS; |
153 | 0 | } |
154 | | |
155 | | /***************************************************************************** |
156 | | * Demux: read packet and send them to decoders |
157 | | ***************************************************************************** |
158 | | * Returns -1 in case of error, 0 in case of EOF, 1 otherwise |
159 | | *****************************************************************************/ |
160 | | static int Demux( demux_t *p_demux ) |
161 | 0 | { |
162 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
163 | 0 | block_t *p_block; |
164 | 0 | int64_t i_offset = vlc_stream_Tell( p_demux->s ); |
165 | 0 | unsigned i_frames = p_sys->i_block_frames; |
166 | |
|
167 | 0 | if( p_sys->i_data_size > 0 && |
168 | 0 | (i_offset - HEADER_LENGTH) >= p_sys->i_data_size ) |
169 | 0 | { |
170 | 0 | return VLC_DEMUXER_EOF; |
171 | 0 | } |
172 | | |
173 | 0 | p_block = vlc_stream_Block( p_demux->s, p_sys->i_frame_size * i_frames ); |
174 | 0 | if( p_block == NULL ) |
175 | 0 | { |
176 | 0 | msg_Warn( p_demux, "cannot read data" ); |
177 | 0 | return VLC_DEMUXER_EOF; |
178 | 0 | } |
179 | | |
180 | 0 | i_frames = p_block->i_buffer / p_sys->i_frame_size; |
181 | 0 | p_block->i_dts = p_block->i_pts = date_Get( &p_sys->pts ); |
182 | 0 | es_out_SetPCR( p_demux->out, p_block->i_pts ); |
183 | 0 | es_out_Send( p_demux->out, p_sys->p_es, p_block ); |
184 | |
|
185 | 0 | date_Increment( &p_sys->pts, i_frames * FRAME_LENGTH ); |
186 | |
|
187 | 0 | return VLC_DEMUXER_SUCCESS; |
188 | 0 | } |
189 | | |
190 | | /***************************************************************************** |
191 | | * Control: |
192 | | *****************************************************************************/ |
193 | | static int Control( demux_t *p_demux, int i_query, va_list args ) |
194 | 0 | { |
195 | 0 | demux_sys_t *p_sys = p_demux->p_sys; |
196 | |
|
197 | 0 | return demux_vaControlHelper( p_demux->s, HEADER_LENGTH, |
198 | 0 | p_sys->i_data_size ? (int64_t)HEADER_LENGTH + p_sys->i_data_size : -1, |
199 | 0 | p_sys->i_bitrate, p_sys->i_frame_size, |
200 | 0 | i_query, args ); |
201 | 0 | } |