mirror of
https://github.com/mpv-player/mpv
synced 2025-02-16 20:27:23 +00:00
ao_pcm, core: use new API in ao_pcm, change timing with it
Change ao_pcm to use the new audio output driver API and clean up some of the code. Rewrite the logic controlling how playback timing works when using -ao pcm. Deprecate the "fast" suboption; its only effect now is to print a warning, but it's still accepted so that specifying it is not an error. Before, timing with -ao pcm and video enabled had two possible modes. In the default mode playback speed was rather arbitrary - not realtime, but not particularly fast. -ao pcm:fast tried to play back at maximum video playback speed - mostly succeeding, but not quite guaranteed to work in all cases. Now the default is to play at realtime speed. The -benchmark option can now be used to get faster playback (same as the video-only case). In the audio-only case playback is always maximum speed.
This commit is contained in:
parent
2fae42d00e
commit
40f6ab5064
@ -3020,11 +3020,6 @@ When not included, raw PCM will be generated.
|
||||
Write the sound to <filename> instead of the default
|
||||
audiodump.wav.
|
||||
If nowaveheader is specified, the default is audiodump.pcm.
|
||||
.IPs "fast\ "
|
||||
Try to dump faster than realtime.
|
||||
Make sure the output does not get truncated (usually with
|
||||
"Too many video packets in buffer" message).
|
||||
It is normal that you get a "Your system is too SLOW to play this!" message.
|
||||
.RE
|
||||
.PD 1
|
||||
.
|
||||
|
241
libao2/ao_pcm.c
241
libao2/ao_pcm.c
@ -24,13 +24,14 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libavutil/common.h"
|
||||
#include "mpbswap.h"
|
||||
#include <libavutil/common.h>
|
||||
|
||||
#include "talloc.h"
|
||||
|
||||
#include "subopt-helper.h"
|
||||
#include "libaf/af_format.h"
|
||||
#include "libaf/reorder_ch.h"
|
||||
#include "audio_out.h"
|
||||
#include "audio_out_internal.h"
|
||||
#include "mp_msg.h"
|
||||
|
||||
#ifdef __MINGW32__
|
||||
@ -38,22 +39,13 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
static const ao_info_t info =
|
||||
{
|
||||
"RAW PCM/WAVE file writer audio output",
|
||||
"pcm",
|
||||
"Atmosfear",
|
||||
""
|
||||
struct priv {
|
||||
char *outputfilename;
|
||||
int waveheader;
|
||||
uint64_t data_length;
|
||||
FILE *fp;
|
||||
};
|
||||
|
||||
LIBAO_EXTERN(pcm)
|
||||
|
||||
extern int vo_pts;
|
||||
|
||||
static char *ao_outputfilename = NULL;
|
||||
static int ao_pcm_waveheader = 1;
|
||||
static int fast = 0;
|
||||
|
||||
#define WAV_ID_RIFF 0x46464952 /* "RIFF" */
|
||||
#define WAV_ID_WAVE 0x45564157 /* "WAVE" */
|
||||
#define WAV_ID_FMT 0x20746d66 /* "fmt " */
|
||||
@ -62,30 +54,30 @@ static int fast = 0;
|
||||
#define WAV_ID_FLOAT_PCM 0x0003
|
||||
#define WAV_ID_FORMAT_EXTENSIBLE 0xfffe
|
||||
|
||||
/* init with default values */
|
||||
static uint64_t data_length;
|
||||
static FILE *fp = NULL;
|
||||
|
||||
|
||||
static void fput16le(uint16_t val, FILE *fp) {
|
||||
static void fput16le(uint16_t val, FILE *fp)
|
||||
{
|
||||
uint8_t bytes[2] = {val, val >> 8};
|
||||
fwrite(bytes, 1, 2, fp);
|
||||
}
|
||||
|
||||
static void fput32le(uint32_t val, FILE *fp) {
|
||||
static void fput32le(uint32_t val, FILE *fp)
|
||||
{
|
||||
uint8_t bytes[4] = {val, val >> 8, val >> 16, val >> 24};
|
||||
fwrite(bytes, 1, 4, fp);
|
||||
}
|
||||
|
||||
static void write_wave_header(FILE *fp, uint64_t data_length) {
|
||||
int use_waveex = (ao_data.channels >= 5 && ao_data.channels <= 8);
|
||||
uint16_t fmt = (ao_data.format == AF_FORMAT_FLOAT_LE) ? WAV_ID_FLOAT_PCM : WAV_ID_PCM;
|
||||
static void write_wave_header(struct ao *ao, FILE *fp, uint64_t data_length)
|
||||
{
|
||||
bool use_waveex = ao->channels >= 5 && ao->channels <= 8;
|
||||
uint16_t fmt = ao->format == AF_FORMAT_FLOAT_LE ?
|
||||
WAV_ID_FLOAT_PCM : WAV_ID_PCM;
|
||||
uint32_t fmt_chunk_size = use_waveex ? 40 : 16;
|
||||
int bits = af_fmt2bits(ao_data.format);
|
||||
int bits = af_fmt2bits(ao->format);
|
||||
|
||||
// Master RIFF chunk
|
||||
fput32le(WAV_ID_RIFF, fp);
|
||||
// RIFF chunk size: 'WAVE' + 'fmt ' + 4 + fmt_chunk_size + data chunk hdr (8) + data length
|
||||
// RIFF chunk size: 'WAVE' + 'fmt ' + 4 + fmt_chunk_size +
|
||||
// data chunk hdr (8) + data length
|
||||
fput32le(12 + fmt_chunk_size + 8 + data_length, fp);
|
||||
fput32le(WAV_ID_WAVE, fp);
|
||||
|
||||
@ -93,17 +85,17 @@ static void write_wave_header(FILE *fp, uint64_t data_length) {
|
||||
fput32le(WAV_ID_FMT, fp);
|
||||
fput32le(fmt_chunk_size, fp);
|
||||
fput16le(use_waveex ? WAV_ID_FORMAT_EXTENSIBLE : fmt, fp);
|
||||
fput16le(ao_data.channels, fp);
|
||||
fput32le(ao_data.samplerate, fp);
|
||||
fput32le(ao_data.bps, fp);
|
||||
fput16le(ao_data.channels * (bits / 8), fp);
|
||||
fput16le(ao->channels, fp);
|
||||
fput32le(ao->samplerate, fp);
|
||||
fput32le(ao->bps, fp);
|
||||
fput16le(ao->channels * (bits / 8), fp);
|
||||
fput16le(bits, fp);
|
||||
|
||||
if (use_waveex) {
|
||||
// Extension chunk
|
||||
fput16le(22, fp);
|
||||
fput16le(bits, fp);
|
||||
switch (ao_data.channels) {
|
||||
switch (ao->channels) {
|
||||
case 5:
|
||||
fput32le(0x0607, fp); // L R C Lb Rb
|
||||
break;
|
||||
@ -129,36 +121,36 @@ static void write_wave_header(FILE *fp, uint64_t data_length) {
|
||||
fput32le(data_length, fp);
|
||||
}
|
||||
|
||||
// to set/get/query special features/parameters
|
||||
static int control(int cmd,void *arg){
|
||||
return -1;
|
||||
}
|
||||
static int init(struct ao *ao, char *params)
|
||||
{
|
||||
struct priv *priv = talloc_zero(ao, struct priv);
|
||||
ao->priv = priv;
|
||||
|
||||
// open & setup audio device
|
||||
// return: 1=success 0=fail
|
||||
static int init(int rate,int channels,int format,int flags){
|
||||
int fast;
|
||||
const opt_t subopts[] = {
|
||||
{"waveheader", OPT_ARG_BOOL, &ao_pcm_waveheader, NULL},
|
||||
{"file", OPT_ARG_MSTRZ, &ao_outputfilename, NULL},
|
||||
{"fast", OPT_ARG_BOOL, &fast, NULL},
|
||||
{"waveheader", OPT_ARG_BOOL, &priv->waveheader, NULL},
|
||||
{"file", OPT_ARG_MSTRZ, &priv->outputfilename, NULL},
|
||||
{"fast", OPT_ARG_BOOL, &fast, NULL},
|
||||
{NULL}
|
||||
};
|
||||
// set defaults
|
||||
ao_pcm_waveheader = 1;
|
||||
priv->waveheader = 1;
|
||||
|
||||
if (subopt_parse(ao_subdevice, subopts) != 0) {
|
||||
return 0;
|
||||
}
|
||||
if (!ao_outputfilename){
|
||||
ao_outputfilename =
|
||||
strdup(ao_pcm_waveheader?"audiodump.wav":"audiodump.pcm");
|
||||
}
|
||||
if (subopt_parse(params, subopts) != 0)
|
||||
return -1;
|
||||
|
||||
if (ao_pcm_waveheader)
|
||||
{
|
||||
if (fast)
|
||||
mp_msg(MSGT_AO, MSGL_WARN,
|
||||
"[AO PCM] Suboption \"fast\" is deprecated.\n"
|
||||
"[AO PCM] Use -novideo, or -benchmark if you want "
|
||||
"faster playback with video.\n");
|
||||
if (!priv->outputfilename)
|
||||
priv->outputfilename =
|
||||
strdup(priv->waveheader ? "audiodump.wav" : "audiodump.pcm");
|
||||
if (priv->waveheader) {
|
||||
// WAV files must have one of the following formats
|
||||
|
||||
switch(format){
|
||||
switch (ao->format) {
|
||||
case AF_FORMAT_U8:
|
||||
case AF_FORMAT_S16_LE:
|
||||
case AF_FORMAT_S24_LE:
|
||||
@ -168,110 +160,97 @@ static int init(int rate,int channels,int format,int flags){
|
||||
case AF_FORMAT_AC3_LE:
|
||||
break;
|
||||
default:
|
||||
format = AF_FORMAT_S16_LE;
|
||||
ao->format = AF_FORMAT_S16_LE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ao_data.outburst = 65536;
|
||||
ao_data.buffersize= 2*65536;
|
||||
ao_data.channels=channels;
|
||||
ao_data.samplerate=rate;
|
||||
ao_data.format=format;
|
||||
ao_data.bps=channels*rate*(af_fmt2bits(format)/8);
|
||||
ao->outburst = 65536;
|
||||
ao->buffersize = 2 * 65536;
|
||||
ao->bps = ao->channels * ao->samplerate * (af_fmt2bits(ao->format) / 8);
|
||||
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO PCM] File: %s (%s)\nPCM: Samplerate: %iHz Channels: %s Format %s\n", ao_outputfilename,
|
||||
(ao_pcm_waveheader?"WAVE":"RAW PCM"), rate,
|
||||
(channels > 1) ? "Stereo" : "Mono", af_fmt2str_short(format));
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO PCM] Info: Faster dumping is achieved with -novideo -ao pcm:fast\n[AO PCM] Info: To write WAVE files use -ao pcm:waveheader (default).\n");
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO PCM] File: %s (%s)\n"
|
||||
"PCM: Samplerate: %d Hz Channels: %d Format: %s\n",
|
||||
priv->outputfilename,
|
||||
priv->waveheader ? "WAVE" : "RAW PCM", ao->samplerate,
|
||||
ao->channels, af_fmt2str_short(ao->format));
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO,
|
||||
"[AO PCM] Info: Faster dumping is achieved with -novideo\n"
|
||||
"[AO PCM] Info: To write WAVE files use -ao pcm:waveheader (default).\n");
|
||||
|
||||
fp = fopen(ao_outputfilename, "wb");
|
||||
if(fp) {
|
||||
if(ao_pcm_waveheader){ /* Reserve space for wave header */
|
||||
write_wave_header(fp, 0x7ffff000);
|
||||
}
|
||||
return 1;
|
||||
priv->fp = fopen(priv->outputfilename, "wb");
|
||||
if (!priv->fp) {
|
||||
mp_tmsg(MSGT_AO, MSGL_ERR, "[AO PCM] Failed to open %s for writing!\n",
|
||||
priv->outputfilename);
|
||||
return -1;
|
||||
}
|
||||
mp_tmsg(MSGT_AO, MSGL_ERR, "[AO PCM] Failed to open %s for writing!\n",
|
||||
ao_outputfilename);
|
||||
if (priv->waveheader) // Reserve space for wave header
|
||||
write_wave_header(ao, priv->fp, 0x7ffff000);
|
||||
ao->untimed = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// close audio device
|
||||
static void uninit(int immed){
|
||||
static void uninit(struct ao *ao, bool cut_audio)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
|
||||
if(ao_pcm_waveheader){ /* Rewrite wave header */
|
||||
int broken_seek = 0;
|
||||
if (priv->waveheader) { // Rewrite wave header
|
||||
bool broken_seek = false;
|
||||
#ifdef __MINGW32__
|
||||
// Windows, in its usual idiocy "emulates" seeks on pipes so it always looks
|
||||
// like they work. So we have to detect them brute-force.
|
||||
broken_seek = GetFileType((HANDLE)_get_osfhandle(_fileno(fp))) != FILE_TYPE_DISK;
|
||||
// Windows, in its usual idiocy "emulates" seeks on pipes so it always
|
||||
// looks like they work. So we have to detect them brute-force.
|
||||
broken_seek = FILE_TYPE_DISK !=
|
||||
GetFileType((HANDLE)_get_osfhandle(_fileno(priv->fp)));
|
||||
#endif
|
||||
if (broken_seek || fseek(fp, 0, SEEK_SET) != 0)
|
||||
mp_msg(MSGT_AO, MSGL_ERR, "Could not seek to start, WAV size headers not updated!\n");
|
||||
if (broken_seek || fseek(priv->fp, 0, SEEK_SET) != 0)
|
||||
mp_msg(MSGT_AO, MSGL_ERR, "Could not seek to start, "
|
||||
"WAV size headers not updated!\n");
|
||||
else {
|
||||
if (data_length > 0xfffff000) {
|
||||
mp_msg(MSGT_AO, MSGL_ERR, "File larger than allowed for WAV files, may play truncated!\n");
|
||||
data_length = 0xfffff000;
|
||||
if (priv->data_length > 0xfffff000) {
|
||||
mp_msg(MSGT_AO, MSGL_ERR, "File larger than allowed for "
|
||||
"WAV files, may play truncated!\n");
|
||||
priv->data_length = 0xfffff000;
|
||||
}
|
||||
write_wave_header(fp, data_length);
|
||||
write_wave_header(ao, priv->fp, priv->data_length);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
free(ao_outputfilename);
|
||||
ao_outputfilename = NULL;
|
||||
fclose(priv->fp);
|
||||
free(priv->outputfilename);
|
||||
}
|
||||
|
||||
// stop playing and empty buffers (for seeking/pause)
|
||||
static void reset(void){
|
||||
|
||||
}
|
||||
|
||||
// stop playing, keep buffers (for pause)
|
||||
static void audio_pause(void)
|
||||
static int get_space(struct ao *ao)
|
||||
{
|
||||
// for now, just call reset();
|
||||
reset();
|
||||
return ao->outburst;
|
||||
}
|
||||
|
||||
// resume playing, after audio_pause()
|
||||
static void audio_resume(void)
|
||||
static int play(struct ao *ao, void *data, int len, int flags)
|
||||
{
|
||||
}
|
||||
struct priv *priv = ao->priv;
|
||||
|
||||
// return: how many bytes can be played without blocking
|
||||
static int get_space(void){
|
||||
|
||||
if(vo_pts)
|
||||
return ao_data.pts < vo_pts + fast * 30000 ? ao_data.outburst : 0;
|
||||
return ao_data.outburst;
|
||||
}
|
||||
|
||||
// plays 'len' bytes of 'data'
|
||||
// it should round it down to outburst*n
|
||||
// return: number of bytes played
|
||||
static int play(void* data,int len,int flags){
|
||||
|
||||
if (ao_data.channels == 5 || ao_data.channels == 6 || ao_data.channels == 8) {
|
||||
int frame_size = af_fmt2bits(ao_data.format) / 8;
|
||||
len -= len % (frame_size * ao_data.channels);
|
||||
if (ao->channels == 5 || ao->channels == 6 || ao->channels == 8) {
|
||||
int frame_size = af_fmt2bits(ao->format) / 8;
|
||||
len -= len % (frame_size * ao->channels);
|
||||
reorder_channel_nch(data, AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
|
||||
AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT,
|
||||
ao_data.channels,
|
||||
len / frame_size, frame_size);
|
||||
ao->channels, len / frame_size, frame_size);
|
||||
}
|
||||
|
||||
//printf("PCM: Writing chunk!\n");
|
||||
fwrite(data,len,1,fp);
|
||||
|
||||
if(ao_pcm_waveheader)
|
||||
data_length += len;
|
||||
|
||||
fwrite(data, len, 1, priv->fp);
|
||||
priv->data_length += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
// return: delay in seconds between first and last sample in buffer
|
||||
static float get_delay(void){
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
const struct ao_driver audio_out_pcm = {
|
||||
.is_new = true,
|
||||
.info = &(const struct ao_info) {
|
||||
"RAW PCM/WAVE file writer audio output",
|
||||
"pcm",
|
||||
"Atmosfear",
|
||||
"",
|
||||
},
|
||||
.init = init,
|
||||
.uninit = uninit,
|
||||
.get_space = get_space,
|
||||
.play = play,
|
||||
};
|
||||
|
@ -236,6 +236,10 @@ int ao_control(struct ao *ao, int cmd, void *arg)
|
||||
|
||||
double ao_get_delay(struct ao *ao)
|
||||
{
|
||||
if (!ao->driver->get_delay) {
|
||||
assert(ao->untimed);
|
||||
return 0;
|
||||
}
|
||||
return ao->driver->get_delay(ao);
|
||||
}
|
||||
|
||||
@ -246,17 +250,20 @@ int ao_get_space(struct ao *ao)
|
||||
|
||||
void ao_reset(struct ao *ao)
|
||||
{
|
||||
ao->driver->reset(ao);
|
||||
if (ao->driver->reset)
|
||||
ao->driver->reset(ao);
|
||||
}
|
||||
|
||||
void ao_pause(struct ao *ao)
|
||||
{
|
||||
ao->driver->pause(ao);
|
||||
if (ao->driver->pause)
|
||||
ao->driver->pause(ao);
|
||||
}
|
||||
|
||||
void ao_resume(struct ao *ao)
|
||||
{
|
||||
ao->driver->resume(ao);
|
||||
if (ao->driver->resume)
|
||||
ao->driver->resume(ao);
|
||||
}
|
||||
|
||||
|
||||
|
@ -72,6 +72,7 @@ struct ao {
|
||||
int buffersize;
|
||||
int pts;
|
||||
bool initialized;
|
||||
bool untimed;
|
||||
const struct ao_driver *driver;
|
||||
void *priv;
|
||||
};
|
||||
|
11
mplayer.c
11
mplayer.c
@ -2048,7 +2048,7 @@ static int check_framedrop(struct MPContext *mpctx, double frame_time) {
|
||||
struct MPOpts *opts = &mpctx->opts;
|
||||
// check for frame-drop:
|
||||
current_module = "check_framedrop";
|
||||
if (mpctx->sh_audio && !mpctx->d_audio->eof) {
|
||||
if (mpctx->sh_audio && !mpctx->ao->untimed && !mpctx->d_audio->eof) {
|
||||
static int dropped_frames;
|
||||
float delay = opts->playback_speed * ao_get_delay(mpctx->ao);
|
||||
float d = delay-mpctx->delay;
|
||||
@ -2411,6 +2411,9 @@ static int fill_audio_out_buffers(struct MPContext *mpctx)
|
||||
|
||||
current_module="play_audio";
|
||||
|
||||
if (ao->untimed && mpctx->sh_video && mpctx->delay > 0)
|
||||
return 0;
|
||||
|
||||
// all the current uses of ao->pts seem to be in aos that handle
|
||||
// sync completely wrong; there should be no need to use ao->pts
|
||||
// in get_space()
|
||||
@ -3366,7 +3369,7 @@ static void run_playloop(struct MPContext *mpctx)
|
||||
if (mpctx->sh_audio && !mpctx->paused
|
||||
&& (!mpctx->restart_playback || !mpctx->sh_video)) {
|
||||
int status = fill_audio_out_buffers(mpctx);
|
||||
full_audio_buffers = status >= 0;
|
||||
full_audio_buffers = status >= 0 && !mpctx->ao->untimed;
|
||||
if (status == -2)
|
||||
// at eof, all audio at least written to ao
|
||||
if (!mpctx->sh_video)
|
||||
@ -3412,7 +3415,9 @@ static void run_playloop(struct MPContext *mpctx)
|
||||
} else if (!mpctx->stop_play) {
|
||||
int sleep_time = 100;
|
||||
if (mpctx->sh_audio) {
|
||||
if (full_audio_buffers)
|
||||
if (mpctx->ao->untimed)
|
||||
sleep_time = 0;
|
||||
else if (full_audio_buffers)
|
||||
sleep_time = FFMAX(20, a_buf * 1000 - 50);
|
||||
else
|
||||
sleep_time = 20;
|
||||
|
Loading…
Reference in New Issue
Block a user