phpffmpeg

Generating a waveform using ffmpeg


I am trying to generate a waveform image using ffmpeg.

I have successfully made a waveform image, however it doesn't look very nice...

I have been looking around to try and style the image to make it look nicer, however I have been unable to find any information on this or any tutorials on this.

I am using PHP and shell_exec to create the waveform.

I am aware that there are php library that can do this but due to file format this is a lengthy process.

The code I am using is as follows:

$command = 'convertvid\bin\ffmpeg -i Temp\\'.$file.' -y -lavfi showwavespic=split_channels=0:s='.$width.'x50 Temp\\'.$PNGFileName;
shell_exec($command);

Basically I would like to add a line through the middle as there are blank spots at the moment and would like to be able to set the background and wave colour.


Solution

  • Default waveform

    Default waveform

    ffmpeg -i input.wav -filter_complex showwavespic -frames:v 1 output.png
    

    Notes

    Fancy waveform

    Fancy waveform

    ffmpeg -i input.mp4 -filter_complex \
    "[0:a]aformat=channel_layouts=mono, \
     compand=gain=-6, \
     showwavespic=s=600x120:colors=#9cf42f[fg]; \
     color=s=600x120:color=#44582c, \
     drawgrid=width=iw/10:height=ih/5:color=#9cf42f@0.1[bg]; \
     [bg][fg]overlay=format=auto,drawbox=x=(iw-w)/2:y=(ih-h)/2:w=iw:h=1:color=#9cf42f" \
    -frames:v 1 output.png
    

    Explanation of options

    1. aformat downsamples the audio to mono. Otherwise, by default, a stereo input would result in a waveform with a different color for each channel (see Default waveform example above).

    2. compand modifies the dynamic range of the audio to make the waveform look less flat. It makes a less accurate representation of the actual audio, but can be more visually appealing for some inputs.

    3. showwavespic makes the actual waveform.

    4. color source filter is used to make a colored background that is the same size as the waveform.

    5. drawgrid adds a grid over the background. The grid does not represent anything, but is just for looks. The grid color is the same as the waveform color (#9cf42f), but opacity is set to 10% (@0.1).

    6. overlay will place [bg] (what I named the filtergraph for the background) behind [fg] (the waveform).

    7. Finally, drawbox will make the horizontal line so any silent areas are not blank.

    Gradient example

    Gradient example

    Using gradients filter:

    ffmpeg -i input.mp3 -filter_complex "gradients=s=1920x1080:c0=000000:c1=434343:x0=0:x1=0:y0=0:y1=1080,drawbox=x=(iw-w)/2:y=(ih-h)/2:w=iw:h=1:color=#0000ff[bg];[0:a]aformat=channel_layouts=mono,showwavespic=s=1920x1080:colors=#0068ff[fg];[bg][fg]overlay=format=auto" -vframes:v 1 output.png
    

    Color background

    waveform with simple color background

    ffmpeg -i input.opus -filter_complex "color=c=blue[color];aformat=channel_layouts=mono,showwavespic=s=1280x720:colors=white[wave];[color][wave]scale=rw:rh[bg];[bg][wave]overlay=format=auto" -frames:v 1 output.png
    

    The scale filter automatically makes the background the same size as the waveform.

    Image background

    Of course you can use an image or video instead for the background:

    Image background example

    ffmpeg -i audio.flac -i background.jpg -filter_complex \
    "[1:v]scale=600:-1,crop=iw:120[bg]; \
     [0:a]showwavespic=s=600x120:colors=cyan|aqua[fg]; \
     [bg][fg]overlay=format=auto" \
    -q:v 3 showwavespic_bg.jpg
    

    Getting waveform stats and data

    Use the astats filter. Many stats are available: RMS, peak, min, max, difference, etc.

    RMS level per audio frame

    Example to get standard RMS level measured in dBFS per audio frame:

    ffprobe -v error -f lavfi -i "amovie=input.wav,astats=metadata=1:reset=1" -show_entries frame_tags=lavfi.astats.Overall.RMS_level -of csv=p=0 > rms.log
    

    Peak level per second

    Add the asetnsamples filter.

    ffprobe -v error -f lavfi -i "amovie=input.wav,asetnsamples=44100,astats=metadata=1:reset=1" -show_entries frame_tags=lavfi.astats.Overall.Peak_level -of csv=p=0
    

    Same as above but with timestamps

    ffprobe -v error -f lavfi -i "amovie=input.wav,asetnsamples=44100,astats=metadata=1:reset=1" -show_entries frame=pkt_pts_time:frame_tags=lavfi.astats.Overall.Peak_level -of csv=p=0
    

    Output to file

    Just append > output.log to the end of your command:

    ffprobe -v error -f lavfi -i "amovie=input.wav,asetnsamples=44100,astats=metadata=1:reset=1" -show_entries frame_tags=lavfi.astats.Overall.RMS_level -of csv=p=0 > output.log
    

    JSON

    ffprobe -v error -f lavfi -i "amovie=input.wav,asetnsamples=44100,astats=metadata=1:reset=1" -show_entries frame_tags=lavfi.astats.Overall.RMS_level -of json > output.json