How to play videos using FFplay

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-with-ffplay-compressor.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-with-ffplay-compressor.txt script is as follows:

#! /bin/bash
# play-with-ffplay-compressor.txt

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})

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
        notify-send "FFplay compressor" "Duration stream information not found"
        echo "FFplay compressor: Duration stream information not found"
        exit
  else
    sDur=${sDur%.*}
    iMin=$(echo $sDur | cut -d ":" -f2)
    iHour=$(echo $sDur | cut -d ":" -f1)
    iSec=$(echo $sDur | cut -d ":" -f3)
    let iHour=iHour*60*60
    let iMin=iMin*60
    let iDur=iHour+iMin+iSec
    #echo $iDur $iHour $iMin $iSec
  fi
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
  sSubtitlesFilter=",subtitles=filename=${sFile}"
else
  sSubtitlesFilter=""
fi

ffplay \
  -x 600 -y 337 -left 200 -top 200 -hide_banner -autoexit \
  -window_title "$sTitle" \
  -vf "eq=saturation=1.6,
       drawtext=x=round((W-tw)/2):y=H-th-20:
         fontfile='${HOME}/.fonts/Time-Normal.ttf':
         fontsize=round(H/30):fontcolor=FFFFFF77:
         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\,15),
       drawtext=x=round(W*t/${iDur}):y=H-th-1:
         fontfile='${HOME}/.fonts/Inter-Regular.ttf':
         fontsize=round(H/20):fontcolor=FFAABBDD:
         text='□',
       drawtext=x=W-tw-20:y=H-th-20:
         fontfile='${HOME}/.fonts/Time-Normal.ttf':
         fontsize=round(H/30):fontcolor=FFFFFF77:
         text='%{eif\:mod(t/3600\,60)\:d\:2}\:%{eif\:mod(t/60\,60)\:d\:2}\:%{eif\:mod(t\,60)\:d\:2}'
       ${sSubtitlesFilter}" \
  -af "dynaudnorm=gausssize=3" \
  "${sFile}"

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

bash play-with-ffplay-compressor.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 using ffprobe in another bash command.

This kind of fix is okay for amateur videos (that you find on TheyTube and Rumble) and bootleg videos. Do not use it for original movies or music videos.

Video player screenshot

Video demo

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

Rumble

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/ttf-misc/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 post was updated to change the scrollbar text and to add the timer and subtitles.

Link: | Magic Link:

Comments are not enabled yet.

For older posts, check the archives.