[FFmpeg-devel] [PATCHv2 1/2] ffplay: add support for interactive volume control

Marton Balint cus at passwd.hu
Sun Sep 27 00:29:41 CEST 2015


On Sat, 26 Sep 2015, Ganesh Ajjanagadde wrote:

> This is a feature heavily inspired by the mpv player. At the moment, methods
> for adjusting volume in ffplay are rather clumsy: either one needs to set it
> system-wide, or one needs to set it via the volume filter.
>
> This patch adds key bindings identical to the mpv defaults for muting/unmuting
> and increasing/decreasing the volume interactively without any introduction of
> external dependencies.
>
> TODO: doc update, possible mouse button bindings (mpv has this).
>
> Signed-off-by: Ganesh Ajjanagadde <gajjanagadde at gmail.com>
> ---
> ffplay.c | 34 +++++++++++++++++++++++++++++++++-
> 1 file changed, 33 insertions(+), 1 deletion(-)
>
> diff --git a/ffplay.c b/ffplay.c
> index d302793..eada863 100644
> --- a/ffplay.c
> +++ b/ffplay.c
> @@ -73,6 +73,9 @@ const int program_birth_year = 2003;
> /* Calculate actual buffer size keeping in mind not cause too frequent audio callbacks */
> #define SDL_AUDIO_MAX_CALLBACKS_PER_SEC 30
> 
> +/* Step size for volume control */
> +#define SDL_VOLUME_STEP 2

Maybe (SDL_MIX_MAXVOLUME/40) or something like that would be more 
readable/future proof.

> +
> /* no AV sync correction is done if below the minimum AV sync threshold */
> #define AV_SYNC_THRESHOLD_MIN 0.04
> /* AV sync correction is done if above the maximum AV sync threshold */
> @@ -246,6 +249,8 @@ typedef struct VideoState {
>     unsigned int audio_buf1_size;
>     int audio_buf_index; /* in bytes */
>     int audio_write_buf_size;
> +    int audio_volume;
> +    int muted;
>     struct AudioParams audio_src;
> #if CONFIG_AVFILTER
>     struct AudioParams audio_filter_src;
> @@ -1348,6 +1353,17 @@ static void toggle_pause(VideoState *is)
>     is->step = 0;
> }
> 
> +static void toggle_mute(VideoState *is)
> +{
> +    is->muted = !is->muted;
> +}
> +
> +static void update_volume(VideoState *is, int sign, int step)
> +{
> +    is->audio_volume += sign * step;
> +    is->audio_volume = av_clip(is->audio_volume, 0, SDL_MIX_MAXVOLUME);

Update audio_volume in one step to avoid the race condition reading the 
unclipped value from it in the audio thread.

> +}
> +
> static void step_to_next_frame(VideoState *is)
> {
>     /* if the stream is paused unpause it, then step */
> @@ -2447,7 +2463,10 @@ static void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
>         len1 = is->audio_buf_size - is->audio_buf_index;
>         if (len1 > len)
>             len1 = len;
> -        memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);
> +        if (is->muted)
> +            memset(stream, 0, len1);
> +        else
> +            SDL_MixAudio(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1, is->audio_volume);

I guess this only works because most SDL audio drivers reset the audio 
buffer before calling the callback. I am not sure this reliably works 
on all platforms, so probably it is better if you always initalize the 
buffer with zeroes before mixing into it. SDL2 explicitly needs this 
anyway.

Also you may want add a special case for max volume to eliminate the 
performance penalty of the extra memset/memcpy. (Yes, not really 
significant).

Something like:

if (!muted and maxvolume)
   memcpy
else {
   memset
   if (!muted)
     Mix
}

>         len -= len1;
>         stream += len1;
>         is->audio_buf_index += len1;
> @@ -2634,6 +2653,8 @@ static int stream_component_open(VideoState *is, int stream_index)
>         is->audio_src = is->audio_tgt;
>         is->audio_buf_size  = 0;
>         is->audio_buf_index = 0;
> +        is->audio_volume = SDL_MIX_MAXVOLUME;
> +        is->muted = 0;

Are you sure you want to reset the settings here? This get called when the 
user changes the audio stream as well. I think it is better if you only 
set these in stream_open.

>
>         /* init averaging filter */
>         is->audio_diff_avg_coef  = exp(log(0.01) / AUDIO_DIFF_AVG_NB);
> @@ -3311,6 +3332,17 @@ static void event_loop(VideoState *cur_stream)
>             case SDLK_SPACE:
>                 toggle_pause(cur_stream);
>                 break;
> +            case SDLK_m:
> +                toggle_mute(cur_stream);
> +                break;
> +            case SDLK_ASTERISK:

SDLK_KP_MULTIPLY is the numeric keypad key.

> +            case SDLK_0:
> +                update_volume(cur_stream, 1, SDL_VOLUME_STEP);
> +                break;
> +            case SDLK_SLASH:

SDLK_KP_DIVIDE

> +            case SDLK_9:
> +                update_volume(cur_stream, -1, SDL_VOLUME_STEP);
> +                break;
>             case SDLK_s: // S: Step to next frame
>                 step_to_next_frame(cur_stream);
>                 break;
> -- 
> 2.5.3

Regards,
Marton


More information about the ffmpeg-devel mailing list