From 40f6ab5064a628dc11b79b5e571dc9444efac093 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Thu, 5 May 2011 21:34:17 +0300 Subject: 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. --- DOCS/man/en/mplayer.1 | 5 - libao2/ao_pcm.c | 249 +++++++++++++++++++++++--------------------------- libao2/audio_out.c | 13 ++- libao2/audio_out.h | 1 + mplayer.c | 11 ++- 5 files changed, 133 insertions(+), 146 deletions(-) diff --git a/DOCS/man/en/mplayer.1 b/DOCS/man/en/mplayer.1 index efd71a63c3..6469f51e10 100644 --- a/DOCS/man/en/mplayer.1 +++ b/DOCS/man/en/mplayer.1 @@ -3020,11 +3020,6 @@ When not included, raw PCM will be generated. Write the sound to 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 . diff --git a/libao2/ao_pcm.c b/libao2/ao_pcm.c index 64eda888f7..20399152c5 100644 --- a/libao2/ao_pcm.c +++ b/libao2/ao_pcm.c @@ -24,13 +24,14 @@ #include #include -#include "libavutil/common.h" -#include "mpbswap.h" +#include + +#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 #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; - - if (subopt_parse(ao_subdevice, subopts) != 0) { - return 0; - } - if (!ao_outputfilename){ - ao_outputfilename = - strdup(ao_pcm_waveheader?"audiodump.wav":"audiodump.pcm"); - } - - if (ao_pcm_waveheader) - { + priv->waveheader = 1; + + if (subopt_parse(params, subopts) != 0) + return -1; + + 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); - - 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"); - - fp = fopen(ao_outputfilename, "wb"); - if(fp) { - if(ao_pcm_waveheader){ /* Reserve space for wave header */ - write_wave_header(fp, 0x7ffff000); - } - return 1; + 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)\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"); + + 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) { -} - -// return: how many bytes can be played without blocking -static int get_space(void){ + struct priv *priv = ao->priv; - 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, +}; diff --git a/libao2/audio_out.c b/libao2/audio_out.c index da471f0cc7..699ae7071b 100644 --- a/libao2/audio_out.c +++ b/libao2/audio_out.c @@ -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); } diff --git a/libao2/audio_out.h b/libao2/audio_out.h index c60819e545..fb62923297 100644 --- a/libao2/audio_out.h +++ b/libao2/audio_out.h @@ -72,6 +72,7 @@ struct ao { int buffersize; int pts; bool initialized; + bool untimed; const struct ao_driver *driver; void *priv; }; diff --git a/mplayer.c b/mplayer.c index f988d92d1b..af3ad127eb 100644 --- a/mplayer.c +++ b/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; -- cgit v1.2.3