1 /*****************************************************************************
2 * wav.c: wav muxer module for vlc
3 *****************************************************************************
4 * Copyright (C) 2004, 2006 VLC authors and VideoLAN
7 * Authors: Gildas Bazin <gbazin@videolan.org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
36 #include <vlc_block.h>
37 #include <vlc_codecs.h>
39 /*****************************************************************************
41 *****************************************************************************/
42 static int Open ( vlc_object_t
* );
43 static void Close ( vlc_object_t
* );
46 set_description( N_("WAV muxer") )
47 set_capability( "sout mux", 5 )
48 set_category( CAT_SOUT
)
49 set_subcategory( SUBCAT_SOUT_MUX
)
50 set_callbacks( Open
, Close
)
54 /*****************************************************************************
56 *****************************************************************************/
57 static int Control ( sout_mux_t
*, int, va_list );
58 static int AddStream( sout_mux_t
*, sout_input_t
* );
59 static void DelStream( sout_mux_t
*, sout_input_t
* );
60 static int Mux ( sout_mux_t
* );
62 #define MAX_CHANNELS 6
72 /* Wave header for the output data */
73 uint32_t waveheader
[5];
74 WAVEFORMATEXTENSIBLE waveformat
;
75 uint32_t waveheader2
[2];
77 uint32_t i_channel_mask
;
78 uint8_t i_chans_to_reorder
; /* do we need channel reordering */
79 uint8_t pi_chan_table
[AOUT_CHAN_MAX
];
82 static const uint32_t pi_channels_in
[] =
83 { WAVE_SPEAKER_FRONT_LEFT
, WAVE_SPEAKER_FRONT_RIGHT
,
84 WAVE_SPEAKER_SIDE_LEFT
, WAVE_SPEAKER_SIDE_RIGHT
,
85 WAVE_SPEAKER_BACK_LEFT
, WAVE_SPEAKER_BACK_RIGHT
, WAVE_SPEAKER_BACK_CENTER
,
86 WAVE_SPEAKER_FRONT_CENTER
, WAVE_SPEAKER_LOW_FREQUENCY
, 0 };
87 static const uint32_t pi_channels_out
[] =
88 { WAVE_SPEAKER_FRONT_LEFT
, WAVE_SPEAKER_FRONT_RIGHT
,
89 WAVE_SPEAKER_FRONT_CENTER
, WAVE_SPEAKER_LOW_FREQUENCY
,
90 WAVE_SPEAKER_BACK_LEFT
, WAVE_SPEAKER_BACK_RIGHT
,
91 WAVE_SPEAKER_BACK_CENTER
,
92 WAVE_SPEAKER_SIDE_LEFT
, WAVE_SPEAKER_SIDE_RIGHT
, 0 };
94 /*****************************************************************************
96 *****************************************************************************/
97 static int Open( vlc_object_t
*p_this
)
99 sout_mux_t
*p_mux
= (sout_mux_t
*)p_this
;
100 sout_mux_sys_t
*p_sys
;
102 p_mux
->pf_control
= Control
;
103 p_mux
->pf_addstream
= AddStream
;
104 p_mux
->pf_delstream
= DelStream
;
107 p_mux
->p_sys
= p_sys
= malloc( sizeof( sout_mux_sys_t
) );
110 p_sys
->b_used
= false;
111 p_sys
->b_header
= true;
114 p_sys
->i_chans_to_reorder
= 0;
119 /*****************************************************************************
121 *****************************************************************************/
122 static void Close( vlc_object_t
* p_this
)
124 sout_mux_t
*p_mux
= (sout_mux_t
*)p_this
;
125 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
129 static int Control( sout_mux_t
*p_mux
, int i_query
, va_list args
)
137 case MUX_CAN_ADD_STREAM_WHILE_MUXING
:
138 pb_bool
= va_arg( args
, bool * );
142 case MUX_GET_ADD_STREAM_WAIT
:
143 pb_bool
= va_arg( args
, bool * );
148 ppsz
= va_arg( args
, char ** );
149 *ppsz
= strdup( "audio/wav" );
156 static int AddStream( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
158 GUID subformat_guid
= {0, 0, 0x10,{0x80, 0, 0, 0xaa, 0, 0x38, 0x9b, 0x71}};
159 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
160 WAVEFORMATEX
*p_waveformat
= &p_sys
->waveformat
.Format
;
161 int i_bytes_per_sample
;
165 if( p_input
->p_fmt
->i_cat
!= AUDIO_ES
)
167 msg_Dbg( p_mux
, "not an audio stream" );
173 msg_Dbg( p_mux
, "can't add more than 1 stream" );
177 msg_Dbg( p_mux
, "adding %i input channels, %iHz",
178 p_input
->p_fmt
->audio
.i_channels
,
179 p_input
->p_fmt
->audio
.i_rate
);
181 p_sys
->i_channel_mask
= 0;
182 if( p_input
->p_fmt
->audio
.i_physical_channels
)
184 for( unsigned i
= 0; i
< pi_vlc_chan_order_wg4
[i
]; i
++ )
185 if( p_input
->p_fmt
->audio
.i_physical_channels
& pi_vlc_chan_order_wg4
[i
])
186 p_sys
->i_channel_mask
|= pi_channels_in
[i
];
188 p_sys
->i_chans_to_reorder
=
189 aout_CheckChannelReorder( pi_channels_in
, pi_channels_out
,
190 p_sys
->i_channel_mask
,
191 p_sys
->pi_chan_table
);
193 msg_Dbg( p_mux
, "channel mask: %x, reordering: %u",
194 p_sys
->i_channel_mask
, p_sys
->i_chans_to_reorder
);
197 fourcc_to_wf_tag( p_input
->p_fmt
->i_codec
, &i_format
);
198 b_ext
= p_sys
->b_ext
= p_input
->p_fmt
->audio
.i_channels
> 2;
200 /* Build a WAV header for the output data */
201 p_sys
->waveheader
[0] = VLC_FOURCC('R', 'I', 'F', 'F'); /* MainChunkID */
202 SetDWLE( &p_sys
->waveheader
[1], 0 ); /* Length */
203 p_sys
->waveheader
[2] = VLC_FOURCC('W', 'A', 'V', 'E'); /* ChunkTypeID */
204 p_sys
->waveheader
[3] = VLC_FOURCC('f', 'm', 't', ' '); /* SubChunkID */
205 SetDWLE( &p_sys
->waveheader
[4], b_ext
? 40 : 16 ); /* SubChunkLength */
207 p_sys
->waveheader2
[0] = VLC_FOURCC('d', 'a', 't', 'a'); /* DataChunkID */
208 SetDWLE( &p_sys
->waveheader2
[1], 0 ); /* DataLength */
210 /* Build a WAVEVFORMAT header for the output data */
211 memset( &p_sys
->waveformat
, 0, sizeof(WAVEFORMATEXTENSIBLE
) );
212 SetWLE( &p_waveformat
->wFormatTag
,
213 b_ext
? WAVE_FORMAT_EXTENSIBLE
: i_format
);
214 SetWLE( &p_waveformat
->nChannels
,
215 p_input
->p_fmt
->audio
.i_channels
);
216 SetDWLE( &p_waveformat
->nSamplesPerSec
, p_input
->p_fmt
->audio
.i_rate
);
217 i_bytes_per_sample
= p_input
->p_fmt
->audio
.i_channels
*
218 p_input
->p_fmt
->audio
.i_bitspersample
/ 8;
219 SetDWLE( &p_waveformat
->nAvgBytesPerSec
,
220 i_bytes_per_sample
* p_input
->p_fmt
->audio
.i_rate
);
221 SetWLE( &p_waveformat
->nBlockAlign
, i_bytes_per_sample
);
222 SetWLE( &p_waveformat
->wBitsPerSample
,
223 p_input
->p_fmt
->audio
.i_bitspersample
);
224 SetWLE( &p_waveformat
->cbSize
, 22 );
225 SetWLE( &p_sys
->waveformat
.Samples
.wValidBitsPerSample
,
226 p_input
->p_fmt
->audio
.i_bitspersample
);
227 SetDWLE( &p_sys
->waveformat
.dwChannelMask
,
228 p_sys
->i_channel_mask
);
229 p_sys
->waveformat
.SubFormat
= subformat_guid
;
230 p_sys
->waveformat
.SubFormat
.Data1
= i_format
;
233 p_sys
->b_used
= true;
238 static block_t
*GetHeader( sout_mux_t
*p_mux
)
240 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
242 block_Alloc( sizeof( WAVEFORMATEXTENSIBLE
) + 7 * 4 );
244 SetDWLE( &p_sys
->waveheader
[1],
245 20 + (p_sys
->b_ext
? 40 : 16) + p_sys
->i_data
); /* Length */
246 SetDWLE( &p_sys
->waveheader2
[1], p_sys
->i_data
); /* DataLength */
248 memcpy( p_block
->p_buffer
, &p_sys
->waveheader
, 5 * 4 );
249 memcpy( p_block
->p_buffer
+ 5 * 4, &p_sys
->waveformat
,
250 sizeof( WAVEFORMATEXTENSIBLE
) );
251 memcpy( p_block
->p_buffer
+ 5 * 4 +
252 (p_sys
->b_ext
? sizeof( WAVEFORMATEXTENSIBLE
) : 16),
253 &p_sys
->waveheader2
, 2 * 4 );
254 if( !p_sys
->b_ext
) p_block
->i_buffer
-= 24;
258 static void DelStream( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
261 msg_Dbg( p_mux
, "removing input" );
263 msg_Dbg( p_mux
, "writing header data" );
264 if( sout_AccessOutSeek( p_mux
->p_access
, 0 ) == VLC_SUCCESS
)
266 sout_AccessOutWrite( p_mux
->p_access
, GetHeader( p_mux
) );
270 static int Mux( sout_mux_t
*p_mux
)
272 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
273 sout_input_t
*p_input
;
275 if( !p_mux
->i_nb_inputs
) return VLC_SUCCESS
;
277 if( p_sys
->b_header
)
279 msg_Dbg( p_mux
, "writing header data" );
280 sout_AccessOutWrite( p_mux
->p_access
, GetHeader( p_mux
) );
282 p_sys
->b_header
= false;
284 p_input
= p_mux
->pp_inputs
[0];
285 while( block_FifoCount( p_input
->p_fifo
) > 0 )
287 block_t
*p_block
= block_FifoGet( p_input
->p_fifo
);
288 p_sys
->i_data
+= p_block
->i_buffer
;
290 /* Do the channel reordering */
291 if( p_sys
->i_chans_to_reorder
)
292 aout_ChannelReorder( p_block
->p_buffer
, p_block
->i_buffer
,
293 p_sys
->i_chans_to_reorder
,
294 p_sys
->pi_chan_table
, p_input
->p_fmt
->i_codec
);
296 sout_AccessOutWrite( p_mux
->p_access
, p_block
);