errno

The ffmpeg on your NAS is a different binary: no AAC decoding, no encoding worth using

· in Servers and infrastructure · tested on Synology DSM, the vendor-supplied ffmpeg, a Linux workstation over SSH

Symptom

I had a batch of audio files sitting on a NAS and a one-line ffmpeg command that converted them perfectly on my workstation. The obvious optimisation was to run the conversion on the NAS itself: the files are already there, the box has ffmpeg on its PATH, and pushing gigabytes across the LAN twice to do the same work seemed silly.

It did not run. The job died almost immediately, and the binary’s complaint was not about memory, disk or CPU — it reported that the codec it needed was not available in this build. Not a corrupt input, not a container problem, not a permissions issue. The decoder for the input stream simply did not exist, and when I worked around that by feeding it something else, the encoder I wanted did not exist either.

That is the whole shape of the problem, and it is worth stating plainly because the name of the command misled me for a good twenty minutes: the ffmpeg on the appliance is a different binary from the ffmpeg on your workstation. Same name, same rough command line, a much smaller feature set.

What it is not

The instinct, when a media job fails on a small box and succeeds on a big one, is to blame the small box’s resources. That is the wrong tree and it is an expensive one to climb, because “transcoding is heavy” is true often enough to sound convincing.

It is not RAM. An out-of-memory kill looks nothing like this: the process is terminated by the kernel, you get a signal, not a message from ffmpeg about a codec.

It is not CPU or thermals. A slow build is still a build. Slow means it takes twenty minutes; it does not mean it fails in the first second.

It is not the file. The same input transcoded fine on the workstation, from the same bytes — the cheapest check available, and the one that immediately rules out corruption, an unusual container or a broken mount.

It is not PATH weirdness or a stale package either. There is only one ffmpeg, it is the vendor’s, and it is doing exactly what it was compiled to do.

What I checked

Stop guessing and ask the binary. Three commands settle the question in about two seconds, and they are worth running on any machine before you design a pipeline around it:

ssh nasuser@nas.example.lan 'ffmpeg -version'
ssh nasuser@nas.example.lan 'ffmpeg -codecs'
ssh nasuser@nas.example.lan 'ffmpeg -encoders'

ffmpeg -version prints the configuration: line — the full list of --enable-* and --disable-* flags the build was configured with. That line is the ground truth about what the binary can do. If the external library you are counting on is not enabled there, no amount of command-line argument fiddling will conjure it up.

ffmpeg -codecs and ffmpeg -encoders are the same answer from the other side: what is actually registered and usable at runtime, with the decode/encode capability letters in the left column. Run the same three commands on your workstation and diff the output. The gap between the two lists is the set of pipelines that will work in one place and fail in the other.

On the appliance, the gap was large. The build could not decode AAC, and it could not encode to the formats I would have reached for. It is not an ffmpeg capable of general media work; it is an ffmpeg that covers the specific formats the vendor’s own indexing and streaming features need.

Why it is built that way

This is not vendor incompetence and it is not worth being annoyed about. Codecs carry patent and licensing obligations, and those obligations attach to the party that ships the binary. An appliance vendor ships what it is licensed to ship for the features it advertises — thumbnailing, library indexing, the streaming its own media apps perform. The binary exists to serve those features. That it is also on your PATH and answers to ffmpeg is a side effect of how the firmware is assembled, not a promise.

The generalisation is the useful part, because this is not a NAS-specific trap:

A command’s name tells you which interface it implements. It tells you nothing about which features were compiled in.

The fix: compute where the toolchain is

The working pattern is unglamorous and correct: do the media work on a general-purpose machine and use the appliance as storage. Pull the file to the workstation, transcode there with a full ffmpeg, push the result back. The extra LAN round trip costs seconds on a gigabit link; debugging a pipeline against a binary that structurally cannot run it costs an afternoon and ends in the same place anyway.

Which leads directly to the second half of this incident, because the two always appear together: on this appliance, moving the files is also not what you expect. scp and sftp both fail, because there is no SFTP subsystem for them to talk to — I wrote up the scp failure itself separately, since it has its own misleading error.

The way through needs nothing but a shell and cat on the far end:

# NAS -> workstation
ssh nasuser@nas.example.lan 'cat "/volume1/media/clip.m4a"' > ./clip.m4a

# transcode locally, with a full ffmpeg

# workstation -> NAS
cat ./clip.out.mp3 | ssh nasuser@nas.example.lan 'cat > "/volume1/media/clip.mp3"'

scp and sftp are protocols: the client asks the server to start a named subsystem, and if the server has no such subsystem the session ends before a single byte of your file moves. A pipe over ssh makes no such request. It runs a command in a shell and connects your local stdin/stdout to it — the lowest common denominator of SSH, available anywhere a login shell exists.

The caveats are real and you should know them before you rely on this:

Verify after the move

That last point is why a byte count is not verification. Compare digests on both ends:

sha256sum ./clip.out.mp3
ssh nasuser@nas.example.lan 'sha256sum "/volume1/media/clip.mp3"'

And because a truncated media file very often still opens, probe the result rather than trusting that a player accepted it:

ffprobe -v error -show_format -show_streams ./clip.out.mp3

Check that the duration matches the source and that the stream is the codec you asked for. A file that opens, plays the first thirty seconds and stops is the classic outcome of a transfer nobody checked.

What to remember

On an appliance, check capability rather than inferring it from a command’s name. ffmpeg -codecs, ffmpeg -encoders and the configuration: line from ffmpeg -version cost two seconds and they tell you whether the pipeline you are about to design can exist on that machine at all. Doing it first turns an afternoon of plausible wrong theories into a decision.

The wider rule is the same one in different clothing: same binary name, different build. It applies to slim container images, BusyBox environments and vendor SDK images exactly as much as it does to a NAS. When a familiar tool behaves unfamiliarly on an unfamiliar box, the first question is not what you typed — it is which build you are typing at.

synology ffmpeg nas media