Converting m4a (alac) to flac using ffmpeg, output is lower quality?

1

I'm trying to convert my imported music collection to flac for compatibility reasons. Anyway, I'm converting using this command:
ffmpeg -i file.m4a file.flac
Now technically, this works fine, and converts the file to flac, but studying the output I notice that the original file (in this one example, the actual number varies between tracks) shows this for the audio stream:
Audio: alac (alac / 0x63616C61), 44100 Hz, stereo, s16p, 968 kb/s (default)
and the new flac version shows this for the audio stream:
Audio: flac, 44100 Hz, stereo, s16, 128 kb/s (default)
I notice two things: the first is that "s16p" was changed to "s16" though I don't know what that means, and don't care too much, but what bothers me, is the massive lowering of the bitrate! From 968 down to 128. Now, each track has a slightly different bitrate, but they all end up outputting to 128. Is this a limitation of flac? And if not, then how do I instruct ffmpeg to output at the original bitrate?

Also, if it affects the quality, then what are s16 and s16p referring to?

EDIT: I technically got what I was looking for, though I still don't know what s16 means, and I'm not 100% sure that what I did worked properly, check my answer for an explanation.

Matthew

Posted 2019-07-06T23:58:29.317

Reputation: 13

Answers

1

Don't pay heed to the bitrate shown for the output during conversion - that is germane only when the encoder's rate control method targets a bitrate. The FLAC encoder doesn't. Run ffprobe on the output file after it has been generated. It will show the actual encoded bitrate.

FLAC is a lossless codec, so irrespective of the bitrate obtained, it will be lossless i.e. of identical quality, relative to the source.

s16 and s16p refer to the sample formats generated by ffmpeg's decoder for these formats. s16p means the decoded samples are signed 16-bit integers, with each channel in a separate plane i.e. C1 C1 C1 C1 and C2 C2 C2 C2 whereas in s16, they are stored interleaved as C1 C2 C1 C2 C1 C2 C1 C2. To clarify, this is the layout and format of the uncompressed output produced by the decoder. The actual data in the files is coded (hence the term encoded).

Gyan

Posted 2019-07-06T23:58:29.317

Reputation: 21 016

Thank you for informing me the about the output quirk. In the answer I provided, do you see any problems arising? Essentially what I'm doing is ffmpeg -i song.m4a -ab 968k song.flac. Which makes the output show what I want, but are there any unintended side effects from running ffmpeg like this? – Matthew – 2019-07-07T21:20:00.927

The bitrate specification has no effect. The FLAC encoder does not look at it, so it's unnecessary. – Gyan – 2019-07-08T04:39:17.577

0

I dug around for a couple hours, and found out about ffprobe, and at first I ignored it, because I knew the command I wrote would end up being long, but with no other choice, I present to you my final, working, powershell command. Granted if anyone see's a way in which this may be problematic, please tell me, and I make no guarantee's, but here is what I came up with (this recursively converts all the m4a alac files in the current directory):
Get-ChildItem -Path . -filter *.m4a | ForEach-Object -Process {[System.IO.Path]::GetFileNameWithoutExtension($_)} | ForEach-Object -Process {ffmpeg.exe -i "$($_).m4a" -ab $(ffprobe -v error -i "$($_).m4a" -select_streams a:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1).replace("bit_rate=","") "$($_).flac"}
An explanation of what this garbled mess it, to try and help anyone who reads it:

  • Get-ChildItem -Path . -filter *.m4a this is essentially like a dir command, but it only shows files ending in .m4a. This gets piped into:
  • ForEach-Object -Process {[System.IO.Path]::GetFileNameWithoutExtensions($_)} this takes all those filenames and removes the .m4a. Which is then piped into:
  • ForEach-Object -Process {ffmpeg -i "$($_).m4a" this is just the start of the ffmpeg command, where the filename is passed, and a .m4a extension is added
  • -ab $(ffprobe -v error -i "$($_).m4a" -select_streams a:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1).replace("bit_rate=","") this sends the m4a file to ffprobe, which outputs bit_rate=aNumber, and then "bit_rate=" is removed from the string
  • "$($_).flac"} and finally, the filename is given to ffmpeg again, but this time with the flac extension as the output file.
    One last time: I can't guarantee that this command will do exactly as intended, all I know is that now the output flac files bitrate matches the original, the s16p thing still changes to s16, and afaik some metadata may be lost, among other possibilities.

Matthew

Posted 2019-07-06T23:58:29.317

Reputation: 13