Choosing the right media tools for web workflows

Preparing media for the web usually means changing file characteristics like bitrate or resolution, and knowing which tool handles which task can be intimidating. Two command-line utilities cover most needs: Shaka Packager and FFmpeg. They complement each other well—neither alone can perform every operation required to get media ready for web playback.

Shaka Packager functions as both a library and a command-line tool. It handles packaging for the two dominant streaming protocols, DASH and HLS, and supports common encryption and Widevine DRM for live streams and video-on-demand. FFmpeg, meanwhile, excels at recording, converting, and streaming. Alternatives exist—GUI apps like Miro, HandBrake, and VLC, plus cloud services such as Zencoder, Amazon Elastic Encoder, and Google Transcoder API—but these two remain the most common and powerful choices for scripted workflows.

Using Shaka Packager's command pattern

A typical Shaka Packager invocation follows this structure:

packager stream_descriptor [stream_descriptor-2 [stream_descriptor-n]] [flags]

This simplified pattern differs from what packager -help returns, but it's easier to reason about and matches the official documentation. Note the multiple stream_descriptor entries: you can manipulate audio and video streams independently in a single command.

Consider a basic use case that inspects file properties:

packager stream_descriptor [stream_descriptor-n] [flags]

packager input=glocken.mp4                       --dump_stream_info

That yields output like this:

File "glocken.mp4":
Found 2 stream(s).
Stream [0] type: Video
 codec_string: avc1.640028
 time_scale: 30000
 duration: 300300 (10.0 seconds)
 is_encrypted: false
 codec: H264
 width: 1920
 height: 1080
 pixel_aspect_ratio: 1:1
 trick_play_factor: 0
 nalu_length_size: 4

Stream [1] type: Audio
 codec_string: mp4a.40.2
 time_scale: 48000
 duration: 481280 (10.0 seconds)
 is_encrypted: false
 codec: AAC
 sample_bits: 16
 num_channels: 2
 sampling_frequency: 48000
 language: eng
 seek_preroll_ns: 20833

Watch for the characteristics discussed in Media file basics. Full HD dimensions show up correctly here, and the codecs—AAC audio and H264 video—are the preferred pair for their containers. Also note the numeric stream identifiers, which become important when you need to target audio or video separately.

Bitrate is conspicuously absent from this output. When that value is required, FFmpeg is the better instrument.

FFmpeg's option positioning matters

The core FFmpeg command takes this shape:

ffmpeg [GeneralOptions] [InputFileOptions] -i input [OutputFileOptions] output

Like Shaka Packager, FFmpeg handles multiple streams. Its options carry context-sensitive meanings: placement within the command changes how they affect the output. Keep that in mind when reviewing FFmpeg answers on Stack Overflow.

Here's the parallel example that reports file details:

    ffmpeg [GeneralOptions] [InputFileOptions] -i input        [OutputFileOptions] output

    ffmpeg                                     -i glocken.mp4

Beyond the requested information, this command prints an error message, shown below. It's technically incorrect usage, but deliberate—we use it specifically because it surfaces the data we want.

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'glocken.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf58.17.100
  Duration: 00:01:47.53, start: 0.000000, bitrate: 10715 kb/s
    Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuvj420p(pc), 1920x1080, 10579 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 59.94 tbc (default)
    Metadata:
      handler_name    : VideoHandler
    Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s (default)
    Metadata:
      handler_name    : SoundHandler
At least one output file must be specified

Standing up both tools with Docker

Installing both utilities manually is possible, but Docker offers a faster path. Set up a directory structure that keeps large media files out of the Docker build context, since oversized files would slow down image rebuilds later.

  1. Create a project directory, e.g., media-tools.
  2. Inside it, create docker/ and media/. The media directory holds the files you'll operate on; the Dockerfile goes in docker/.
  3. Write the Dockerfile at media-tools/docker/Dockerfile:

FROM google/shaka-packager:release-v2.4.3 as packager
FROM jrottenberg/ffmpeg:4.3.2-alpine38
COPY --from=packager /usr/bin /usr/bin
ENTRYPOINT  ["sh"]

  1. Build the image:

docker build -t media-tools ./docker

  1. Launch an interactive shell; the command differs per OS:
    Linux:
    docker run -w /media -v ${PWD}/media:/media -it --rm media-tools
    /media #
    

    Windows:
    docker run -w /media -v %cd%/media:/media -it --rm media-tools
    /media #
    

Inside the running container, verify the installation with ffmpeg -version and packager --version:

/media # ffmpeg -version
ffmpeg version 4.3.2 Copyright (c) 2000-2021 the FFmpeg developers
built with gcc 6.4.0 (Alpine 6.4.0)
configuration: --disable-debug --disable-doc --disable-ffplay --enable-shared --enable-avresample --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-gpl --enable-libass --enable-fontconfig --enable-libfreetype --enable-libvidstab --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libxcb --enable-libx265 --enable-libxvid --enable-libx264 --enable-nonfree --enable-openssl --enable-libfdk_aac --enable-postproc --enable-small --enable-version3 --enable-libbluray --enable-libzmq --extra-libs=-ldl --prefix=/opt/ffmpeg --enable-libopenjpeg --enable-libkvazaar --enable-libaom --extra-libs=-lpthread --enable-libsrt --enable-libaribb24 --extra-cflags=-I/opt/ffmpeg/include --extra-ldflags=-L/opt/ffmpeg/lib
libavutil      56. 51.100 / 56. 51.100
libavcodec     58. 91.100 / 58. 91.100
libavformat    58. 45.100 / 58. 45.100
libavdevice    58. 10.100 / 58. 10.100
libavfilter     7. 85.100 /  7. 85.100
libavresample   4.  0.  0 /  4.  0.  0
libswscale      5.  7.100 /  5.  7.100
libswresample   3.  7.100 /  3.  7.100
libpostproc    55.  7.100 / 55.  7.100

/media # packager --version
packager version v2.4.3-dd9870075f-release

These two tools give you a solid foundation for web media work. From here, the next step is understanding Media streaming basics.