Use FFplay as your default media player 👈 🌶 🌞

A guide to the barebones media player bundled with the FFmpeg

I cannot believe I spent the better part of one day to fix a problem in many difficult ways when the fix was very simple!

What is ffplay

The FFmpeg project also provides a barebones media player known as ffplay. It does not have any menus — main or context. You have to use certain keys and mouse actions for controlling the playback.

Keypress/
Mouse action
Function
SPACE barplay or pause playback
LEFT and RIGHTfast-forward or rewind by 10 seconds
UP and DOWNfast-forward or rewind by 1 minute
PAGE UP and PAGE DOWNswitch chapter (if present)

if there are no chapters, behave like LEFT and RIGHT
double-clicktoggle between windowed and fullscreen view
right-clickseek forward or backward using an invisible playback slider
aswitch (audio) language
tswitch subtitle (language)
mmute on/off
/decrease sound volume
*increase sound volume
wswitch filters

Like ffmpeg, ffplay displays a huge banner and writes a lot of text messages on the console. You can hide them using some switches.

ffplay -hide_banner -nodisp -autoexit \
       Nightfall-Stone-Ship.mp3

Even with this arrangement, ffplay is not entirely quiet because it also writes to standard error. If you want a clean console, try

ffplay -loglevel quiet \
       Nightfall-Stone-Ship.mp3

How to play videos with ffplay

As mentioned in another blog post, I now I use ffplay command to fix two prominent problems with online videos.

  1. Extremely pale videos.
  2. Extremely loud or low-volume videos.

I use a Caja Actions Configuration to right-click videos and launch them with ffplay. It has the effect of running this script manually.

bash Play-Video-With-ffplay.txt \
     hobonichi-no-kaidan.mp4

Of course, to use Caja Actions, you should have chosen the Mate desktop. If you had meekly accepted Gnome 3 like a moron when it was imposed on you, then you deserve it. Free software is about freedom, is it not, slave?

The definitive ffplay command for offline videos

The code in the Play-Video-With-ffplay.txt script is as follows:

#! /bin/bash
set -ux

### Caja Actions Configuration ###

# Context label:    Play with FFplay compressor
# Tooltip:              Play this video with FFmpeg video player with realtime compressor
# Command:              terminator
# Parameters:           -x bash ${HOME}/Scripts/CajaActionsConfiguration/Play-Video-With-ffplay.txt %f
# Mimetype:             video/*
# Environment:      Count = 1
# Updated:              8 October 2025

sFile=$*
sTitle=$(basename "${sFile}")

sSdata=$(ffprobe -loglevel quiet -print_format "default=nw=1:nk=1" \
                -select_streams v:0 \
                -show_entries "stream=width,height,duration" \
                -i "${sFile}")
#echo $sSdata
#exit

sDur=$(echo $sSdata | cut -d " " -f3)
sWidth=$(echo $sSdata | cut -d " " -f1)
sHeight=$(echo $sSdata | cut -d " " -f2)

if [ "$sDur" = "N/A" ]; then
  sDur=$(ffprobe -loglevel quiet -print_format "default=nw=1:nk=1" \
                 -select_streams v:0 \
                 -show_entries "stream_tags=duration" \
                 -i "${sFile}")
  if [ -z "${sDur}" ]; then
    sDur1=$(ffprobe "${sFile}" 2>&1 | grep "Duration: ")
    if [ -z "${sDur1}" ]; then
            notify-send "FFplay compressor" "Duration stream information not found"
            echo "FFplay compressor: Duration stream information not found"
            exit
    else
          sDur2=${sDur1%%,*}
          sDur=${sDur2:11}
    fi
  fi

  sDur=${sDur%.*}
  iMin=$(echo $sDur | cut -d ":" -f2)
  iHour=$(echo $sDur | cut -d ":" -f1)
  iSec=$(echo $sDur | cut -d ":" -f3)
  iHour=${iHour#0}
  iMin=${iMin#0}
  iSec=${iSec#0}
  let iHour=iHour*60*60
  let iMin=iMin*60
  #echo $iHour $iMin $iSec
  let iDur=iHour+iMin+iSec
  #echo $iDur $iHour $iMin $iSec

else
  iDur=${sDur%.*}
fi

iSubtitleCount=$(ffprobe -loglevel 0 -print_format "default=nw=1:nk=1" -show_entries stream=codec_type "${sFile}" | grep subtitle | wc -l)
if [ $iSubtitleCount -gt 0 ]; then
  sSubtitleFilter=",subtitles=filename=${sFile}"
else
  sSubtitleFilter=""
fi

ffplay -analyzeduration 9*999*999*999 -probesize 9*999*999*999 \
  -hide_banner -autoexit \
  -window_title "$sTitle" \
  -vf "eq=saturation=1.6,
       drawtext=x=round((W-tw)/2):y=H-th-5:
         fontfile='${HOME}/.fonts/Time-Normal.ttf':
         fontsize=round(H/25):fontcolor=F3B75FFF:
         text=\'%{eif\:mod(${iDur}/3600\,60)\:d\:2}\:%{eif\:mod(${iDur}/60\,60)\:d\:2}\:%{eif\:mod(${iDur}\,60)\:d\:2} ${sWidth}×${sHeight}\':
         enable=lt(t\,25),
       drawtext=x=round(W*t/${iDur}):y=H-th-1:
         fontfile='${HOME}/.fonts/Inter-Regular.ttf':
         fontsize=round(H/20):fontcolor=purple:
         text='□':enable=lt(mod(t\,4)\,2),
       drawtext=x=round(W*t/${iDur}):y=H-th-1:
         fontfile='${HOME}/.fonts/Inter-Regular.ttf':
         fontsize=round(H/20):fontcolor=orange:
         text='□':enable=gt(mod(t\,4)\,2),
       drawtext=x=W-tw-20:y=H-th-5:
         fontfile='${HOME}/.fonts/Time-Normal.ttf':
         fontsize=round(H/25):fontcolor=FFA7FDFF:
         text='%{eif\:mod(t/3600\,60)\:d\:2}\:%{eif\:mod(t/60\,60)\:d\:2}\:%{eif\:mod(t\,60)\:d\:2}'
       ${sSubtitleFilter}" \
  -af "dynaudnorm=gausssize=3,extrastereo=c=0" \
  "${sFile}"

You can play a video with the above script like this:

bash Play-Video-With-ffplay.txt hobonichi-no-kaidan.mp4

The script writes some text, a square box, which moves to the bottom-left corner of the video which then moves to the bottom-right corner as the playback progress towards the end. Another piece of text is drawn for the timer in the bottom-right corner. In the middle, also near the bottom edge, some text is written for the total duration and dimensions of the video. This disappears after a few seconds.

The timer can also be set using the filter options timecode=\'00\:00\:00\:00\':timecode_rate=24. This however includes the microseconds. That is overkill. For less distraction and more flexibility, I used three expressions (involving the t variable, which stands for playback position in seconds) expanded in the text filter option.

All day, I was trying to find a way to get the total duration of the playback in various filters to no avail. Eventually, it fixed it by capturing the output of ffprobe in another bash command.

The colours are enhanced. The audio is compressed so there are no low-volume or sudden high-volume segments. These fix are needed as I mostly see poorly produced videos on TheyTube and Rumble. Do not use it for original movies or music videos.

Video player screenshot

Customization

Rather than type this long command every time, you could place the script in your /opt directory and have this alias in your .bashrc file.

alias play='bash /opt/Play-Video-With-ffplay.txt '

This way, you can simply type play some-video-file.mkv to accomplish the same.

Video demo

In ffplay, you can right-click anywhere on the video as the entire frame acts as a giant progress bar.

Rumble

The progress bar does not work well with small files.

Make the scrollbar permanent

If you want to permanently add the slider to your video, you need to make some modifications to the script.

#! /bin/bash
# add-scrollbar-to-video.txt

sFile=$*
sDuration=$(ffprobe -loglevel quiet -print_format "default=nw=1:nk=1" \
                -select_streams v:0 \
                -show_entries "stream=duration" \
                -i ${sFile})
iDuration=${sDuration%.*}

ffmpeg -i "$sFile" \
        -vf "drawtext=x=round(W*t/${iDuration}):y=H-th-1:
               fontfile='${HOME}/.fonts/Inter-Regular.ttf':
               fontsize=round(H/20):fontcolor=FFAAEE:
               text='□'" \
        -af "extrastereo=c=0" \
        -c:v libx264 -profile:v high -crf 21 -preset medium -tune film -r 25  -pix_fmt yuv420p \
        -c:a libfdk_aac -vbr 5 \
        "${sFile%.*}-SCROLLS.mp4"

When I play this ‘SCROLLS’ video with my ffplay compressor script, the two squares (progress indicators) coincide!

How to play livestreamed videos using ffplay

Youtube-DL is under DMCA takedown. In its place, you can use its fork yt-dlp.

To play livestreamed videos, you can use this script:

#! /bin/bash
# play-livestream-video.txt

sURL="$*"
sTitle=$(yt-dlp_linux --quiet --skip-download --print title $sURL)

yt-dlp_linux -o - "$sURL" | \
    ffplay -window_title "${sTitle}" \
           -af "dynaudnorm=gausssize=3" \
           -

Run it like this:

bash play-livestream-video.txt https://www.youtube.com/watch?v=nvXLt3ET9mE

Livestream screenshot

Do note that the ytp-dlp command writes to the standard input and the ffplay reads from the standard input. So, do not omit the hyphens on either side of the pipe.

How to play Youtube videos offline using ffplay

For other videos that have been uploaded to the Web, the above script will be annoying because you cannot fast-forward or rewind. It would be better to download and then play the videos offline.

yt-dlp_linux \
    -f "hasvid,hasaud,[height>=360]" -S "+height" \
    -o '%(title)s.%(ext)s' \
    --exec 'ffplay -autoexit -af "dynaudnorm=gausssize=3" {}' \
    https://www.youtube.com/watch?v=rf4cz_xjgIg
  • Streaming sites offer the same video in multiple formats, dimensions and quality. The -f option is for specifying the name for one of these download options. The names differ from video to video and platform to platform. Instead of specifying an explicit name, I can use some special keywords instead. The keywords hasvid,hasaud ensures that yt-dlp chooses download options that have both video and audio streams. The value [height>=360] ensures that yt-dlp downloads videos whose height equal or greater than 360 pixels. With the -S option, I have ensured that the smallest video among those available with the -f option is chosen for download first. yt-dlp ignores other qualifying videos. Without the plus (+) prefix in its value, yt-dlp would have chosen the biggest available video.
  • The -o option is for naming the download file. It uses some metadata placeholders.
  • The --exec option is for launching the downloaded video with some application. Obviously, I have chosen ffplay. The '{}' in the exec option will be replaced by the downloaded video file name (including the extension).

Desktop screenshot


Become an FFmpeg PRO by reading my book Quick Start Guide to FFmpeg.

Book photo


This blog post was updated several times to change the scrollbar text and to add the timer and subtitles. Now, it looks like this:

Video player screenshot

Link: | Magic Link:

Comments are not enabled yet.

For older posts, check the archives.