2 * PCM audio output driver
4 * This file is part of MPlayer.
6 * MPlayer 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.
11 * MPlayer 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.
16 * You should have received a copy of the GNU General Public License along
17 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include <libavutil/common.h>
31 #include "subopt-helper.h"
32 #include "libaf/af_format.h"
33 #include "libaf/reorder_ch.h"
34 #include "audio_out.h"
38 // for GetFileType to detect pipes
50 #define WAV_ID_RIFF 0x46464952 /* "RIFF" */
51 #define WAV_ID_WAVE 0x45564157 /* "WAVE" */
52 #define WAV_ID_FMT 0x20746d66 /* "fmt " */
53 #define WAV_ID_DATA 0x61746164 /* "data" */
54 #define WAV_ID_PCM 0x0001
55 #define WAV_ID_FLOAT_PCM 0x0003
56 #define WAV_ID_FORMAT_EXTENSIBLE 0xfffe
58 static void fput16le(uint16_t val
, FILE *fp
)
60 uint8_t bytes
[2] = {val
, val
>> 8};
61 fwrite(bytes
, 1, 2, fp
);
64 static void fput32le(uint32_t val
, FILE *fp
)
66 uint8_t bytes
[4] = {val
, val
>> 8, val
>> 16, val
>> 24};
67 fwrite(bytes
, 1, 4, fp
);
70 static void write_wave_header(struct ao
*ao
, FILE *fp
, uint64_t data_length
)
72 bool use_waveex
= ao
->channels
>= 5 && ao
->channels
<= 8;
73 uint16_t fmt
= ao
->format
== AF_FORMAT_FLOAT_LE
?
74 WAV_ID_FLOAT_PCM
: WAV_ID_PCM
;
75 uint32_t fmt_chunk_size
= use_waveex
? 40 : 16;
76 int bits
= af_fmt2bits(ao
->format
);
79 fput32le(WAV_ID_RIFF
, fp
);
80 // RIFF chunk size: 'WAVE' + 'fmt ' + 4 + fmt_chunk_size +
81 // data chunk hdr (8) + data length
82 fput32le(12 + fmt_chunk_size
+ 8 + data_length
, fp
);
83 fput32le(WAV_ID_WAVE
, fp
);
86 fput32le(WAV_ID_FMT
, fp
);
87 fput32le(fmt_chunk_size
, fp
);
88 fput16le(use_waveex
? WAV_ID_FORMAT_EXTENSIBLE
: fmt
, fp
);
89 fput16le(ao
->channels
, fp
);
90 fput32le(ao
->samplerate
, fp
);
91 fput32le(ao
->bps
, fp
);
92 fput16le(ao
->channels
* (bits
/ 8), fp
);
99 switch (ao
->channels
) {
101 fput32le(0x0607, fp
); // L R C Lb Rb
104 fput32le(0x060f, fp
); // L R C Lb Rb LFE
107 fput32le(0x0727, fp
); // L R C Cb Ls Rs LFE
110 fput32le(0x063f, fp
); // L R C Lb Rb Ls Rs LFE
113 // 2 bytes format + 14 bytes guid
115 fput32le(0x00100000, fp
);
116 fput32le(0xAA000080, fp
);
117 fput32le(0x719B3800, fp
);
121 fput32le(WAV_ID_DATA
, fp
);
122 fput32le(data_length
, fp
);
125 static int init(struct ao
*ao
, char *params
)
127 struct priv
*priv
= talloc_zero(ao
, struct priv
);
131 const opt_t subopts
[] = {
132 {"waveheader", OPT_ARG_BOOL
, &priv
->waveheader
, NULL
},
133 {"file", OPT_ARG_MSTRZ
, &priv
->outputfilename
, NULL
},
134 {"fast", OPT_ARG_BOOL
, &fast
, NULL
},
138 priv
->waveheader
= 1;
140 if (subopt_parse(params
, subopts
) != 0)
144 mp_msg(MSGT_AO
, MSGL_WARN
,
145 "[AO PCM] Suboption \"fast\" is deprecated.\n"
146 "[AO PCM] Use -novideo, or -benchmark if you want "
147 "faster playback with video.\n");
148 if (!priv
->outputfilename
)
149 priv
->outputfilename
=
150 strdup(priv
->waveheader
? "audiodump.wav" : "audiodump.pcm");
151 if (priv
->waveheader
) {
152 // WAV files must have one of the following formats
154 switch (ao
->format
) {
156 case AF_FORMAT_S16_LE
:
157 case AF_FORMAT_S24_LE
:
158 case AF_FORMAT_S32_LE
:
159 case AF_FORMAT_FLOAT_LE
:
160 case AF_FORMAT_AC3_BE
:
161 case AF_FORMAT_AC3_LE
:
164 ao
->format
= AF_FORMAT_S16_LE
;
169 ao
->outburst
= 65536;
170 ao
->bps
= ao
->channels
* ao
->samplerate
* (af_fmt2bits(ao
->format
) / 8);
172 mp_tmsg(MSGT_AO
, MSGL_INFO
, "[AO PCM] File: %s (%s)\n"
173 "PCM: Samplerate: %d Hz Channels: %d Format: %s\n",
174 priv
->outputfilename
,
175 priv
->waveheader
? "WAVE" : "RAW PCM", ao
->samplerate
,
176 ao
->channels
, af_fmt2str_short(ao
->format
));
177 mp_tmsg(MSGT_AO
, MSGL_INFO
,
178 "[AO PCM] Info: Faster dumping is achieved with -novideo\n"
179 "[AO PCM] Info: To write WAVE files use -ao pcm:waveheader (default).\n");
181 priv
->fp
= fopen(priv
->outputfilename
, "wb");
183 mp_tmsg(MSGT_AO
, MSGL_ERR
, "[AO PCM] Failed to open %s for writing!\n",
184 priv
->outputfilename
);
187 if (priv
->waveheader
) // Reserve space for wave header
188 write_wave_header(ao
, priv
->fp
, 0x7ffff000);
194 // close audio device
195 static void uninit(struct ao
*ao
, bool cut_audio
)
197 struct priv
*priv
= ao
->priv
;
199 if (priv
->waveheader
) { // Rewrite wave header
200 bool broken_seek
= false;
202 // Windows, in its usual idiocy "emulates" seeks on pipes so it always
203 // looks like they work. So we have to detect them brute-force.
204 broken_seek
= FILE_TYPE_DISK
!=
205 GetFileType((HANDLE
)_get_osfhandle(_fileno(priv
->fp
)));
207 if (broken_seek
|| fseek(priv
->fp
, 0, SEEK_SET
) != 0)
208 mp_msg(MSGT_AO
, MSGL_ERR
, "Could not seek to start, "
209 "WAV size headers not updated!\n");
211 if (priv
->data_length
> 0xfffff000) {
212 mp_msg(MSGT_AO
, MSGL_ERR
, "File larger than allowed for "
213 "WAV files, may play truncated!\n");
214 priv
->data_length
= 0xfffff000;
216 write_wave_header(ao
, priv
->fp
, priv
->data_length
);
220 free(priv
->outputfilename
);
223 static int get_space(struct ao
*ao
)
228 static int play(struct ao
*ao
, void *data
, int len
, int flags
)
230 struct priv
*priv
= ao
->priv
;
232 if (ao
->channels
== 5 || ao
->channels
== 6 || ao
->channels
== 8) {
233 int frame_size
= af_fmt2bits(ao
->format
) / 8;
234 len
-= len
% (frame_size
* ao
->channels
);
235 reorder_channel_nch(data
, AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT
,
236 AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT
,
237 ao
->channels
, len
/ frame_size
, frame_size
);
239 fwrite(data
, len
, 1, priv
->fp
);
240 priv
->data_length
+= len
;
244 const struct ao_driver audio_out_pcm
= {
246 .info
= &(const struct ao_info
) {
247 "RAW PCM/WAVE file writer audio output",
254 .get_space
= get_space
,