Wav audio file compression not working - ffmpeg

Is it possible to compress a wav audio file without reducing the sampling rate?
I have an audio file with 256 bit rate and sampling rate - 8000Hz. I would just like to reduce the bit rate to 128/64 kbs
I tried converting to mp3 and back to wav,
ffmpeg -i input.wav 1.mp3
ffmpeg -i "1.mp3" -acodec pcm_s16le -ar 4000 out.wav
but this reduced sampling rate as well.
ffmpeg -i "1.mp3" -acodec pcm_s16le -ab 128 out.wav has default 256 bit rate

PCM (WAV) is uncompressed, so -b:a/-ab is ignored.
The bitrate of WAV is directly affected by the sample rate, channel layout, and bits per sample.
Calculating PCM/WAV bitrate
Assuming 8000 samples per second, stereo channel layout, 16 bits per sample:
sample rate × number of channels × bits per sample = bitrate
8000 × 2 × 16 = 256000 bits/s, or 256 kb/s
Getting channels, sample rate, bit depth
You can just view the output of ffmpeg -i input.wav or use ffprobe for a more concise output:
$ ffprobe -loglevel error -select_streams a -show_entries stream=sample_rate,channels,bits_per_sample -of default=nw=1 input.wav
sample_rate=8000
channels=2
bits_per_sample=16
Changing the bitrate
Bitrate should not be a consideration when using WAV. If bitrate is a problem then WAV is the wrong choice for you. That being said, you can change the bitrate by changing:
The sample rate (-ar)
The number of channels (-ac)
The bit depth. For PCM/WAV the bit depth is the number listed in the encoder name: -c:a pcm_s24le, -c:a pcm_s16le, -c:a pcm_u8, etc. See ffmpeg -encoders.
Examples for 128 kb/s (this will probably sound bad):
ffmpeg -i input.wav -ar 8000 -ac 1 -c:a pcm_s16le output.wav
ffmpeg -i input.wav -ar 8000 -ac 2 -c:a pcm_s8 output.wav
Another option is to use a lossless compressed format. The quality will be the same as WAV but the file size can be significantly smaller. Example for FLAC:
$ ffmpeg -i audio.wav audio.flac
$ ls -alh audio.wav audio.flac
6.1M audio.flac
11M audio.wac

I usually do this using Audacity
1) import the wav file to audacity
2) Then File>Export
3) Choose "Constant" and then from the Quality drop-down select your required bit-rate
I haven't tried that with ffmpeg, but the command should be:
ffmpeg -i input.wav -ab 64000 output.wav

Related

ffmpeg 2 pass encoding- understand the statistics in the output .log file

I am using 2 pass encoder to encode my input video with a target bitrate.
I want to get the encoder (x264) statistics from the first pass- these statistics are written to ffmpeg2pass.log
the output for each frame looks like that:
in:2 out:2 type:B dur:2 cpbdur:2 q:28.41 aq:25.07 tex:4721 mv:2357 misc:2266 imb:24 pmb:527 smb:3049 d:- ref:0 ;
How can I found is the meaning of each value? (for example, mv is motion vectors, but what is the meaning of misc, imb, pmb etc.?)
ffmpeg command used:
ffmpeg -f rawvideo -pix_fmt yuv422p16le -s:v 1280x720 -i "input_file.yuv" -c:v libx264 -pass 1 -stats -vstats -passlogfile "log_file_prefix" -f mp4 -y "output.mp4" &&
ffmpeg -f rawvideo -pix_fmt yuv422p16le -s:v 1280x720 -i "input_file.yuv" -c:v libx264 -pass 2 -passlogfile "log_file_prefix" -y "output.mp4"
In: display/input frame number
Out: coded frame number
Type: frame type
q: frame quantization value
Tex: the bit size used for residual
Mv: bit size for mvs
Misc: for other bit sizes
Imb: number of internal macroblocks
Pmb: number of inter macroblocks
Smb: skip the number of skip macroblocks
d: the best mode for this frame
Ref: the number of times each ref is used in the list
w: the optimal weight of this frame (if the weight is turned on)

ffmpeg recording 16 bits even if I want only 8 bits

I tried following commands:
ffmpeg -f dshow -sample_fmt u8 -ac 1 -ar 11025 -i audio="mic" output.wav
ffmpeg -f dshow -sample_fmt u8p -ac 1 -ar 11025 -i audio="mic" output.wav
Every time I try to get 8 bits output ffmpeg returns 16 bits per sample.
Is there any way to record with 8 bits per sample?
Default encoder for WAV output is pcm_s16le (PCM signed 16-bit little-endian). Use one of these encoders:
encoder
description
pcm_s8
PCM signed 8-bit
pcm_s8_planar
PCM signed 8-bit planar
pcm_u8
PCM unsigned 8-bit
Example:
ffmpeg -f dshow -channels 1 -sample_rate 11025 -i audio="mic" -c:a pcm_u8 output.wav
I changed the input options to use the documented dshow options.
See ffmpeg -encoders for a list of encoders.

ffmpeg from .mp3 to .al appears to slow and deepen the audio

I have an mp3 input file that is 22050Hz and 48kb/s. I need to convert it to a .al file in order to stream it to a camera on my network for output.
ffmpeg -i file.mp3 file.al
creates a file that does play but the bitrate of the output is 176kb/s and it clearly slower/deeper sounding. I have tried -b:a 48k and -filter:a without success but I could be doing it wrong.
Documentation states that this is the format I need: PCM ADPCM G.711A G.711.Mu G.726 G.729 MPEG2 AMR AAC
ffmpeg -i input.mp3 -ar 8000 -ac 1 -ab 64 output.al -ar 8000 -ac 1 -ab 64 -f mulaw
This has produced the correct result. Conversion is for playing audio over an Amcrest camera speaker.

Recording streams audio with ffmpeg for Cloud Speech-to-Text

Goodnight
I am trying to record audio with the following features:
codec: flac
sampling rate: 16000hz
I am testing with the following line of code:
ffmpeg -t 15 -i http://198.15.86.218:9436/stream -codec:a flac -b:a 16k example.flac
But when reviewing the output file, I get the following:
codec: flac
sampling rate: 44000hz
I could guide the correct use of ffmpeg options.
-b:a is to set the bitrate*. For sampling rate, you have to use -ar.
Use
ffmpeg -t 15 -i http://198.15.86.218:9436/stream -codec:a flac -ar 16k example.flac
*for a lossless codec, bitrate setting is irrelevant.
"Free Lossless Audio Codec" Flac is lossless and hence output bit-rate cannot be controlled precisely. -b:a 16k is actually trying to set output bit-rate of audio to 16k bits per second.
While in your case you need it to be sampled at 16000 Hz. So the correct option would be to use -ar [audio rate]
ffmpeg -t 15 -i http://198.15.86.218:9436/stream -c:a flac -ar 16000 example.flac
If you want to control the output bit-rate with FLAC encoder then you can use the option -compression_level 0-15 with 5 being default. You can get mode details on controlling other parameters of FLAC ffmpeg encoder here.

How can I place a still image before the first frame of a video?

When I encode videos by FFMpeg I would like to put a jpg image before the very first video frame, because when I embed the video on a webpage with "video" html5 tag, it shows the very first picture as a splash image. Alternatively I want to encode an image to an 1 frame video and concatenate it to my encoded video. I don't want to use the "poster" property of the "video" html5 element.
You can use the concat filter to do that. The exact command depends on how long you want your splash screen to be. I am pretty sure you don't want an 1-frame splash screen, which is about 1/25 to 1/30 seconds, depending on the video ;)
The Answer
First, you need to get the frame rate of the video. Try ffmpeg -i INPUT and find the tbr value. E.g.
$ ffmpeg -i a.mkv
ffmpeg version N-62860-g9173602 Copyright (c) 2000-2014 the FFmpeg developers
built on Apr 30 2014 21:42:15 with gcc 4.8 (Ubuntu 4.8.2-19ubuntu1)
[...]
Input #0, matroska,webm, from 'a.mkv':
Metadata:
ENCODER : Lavf55.37.101
Duration: 00:00:10.08, start: 0.080000, bitrate: 23 kb/s
Stream #0:0: Video: h264 (High 4:4:4 Predictive), yuv444p, 320x240 [SAR 1:1 DAR 4:3], 25 fps, 25 tbr, 1k tbn, 50 tbc (default)
At least one output file must be specified
In the above example, it shows 25 tbr. Remember this number.
Second, you need to concatenate the image with the video. Try this command:
ffmpeg -loop 1 -framerate FPS -t SECONDS -i IMAGE \
-t SECONDS -f lavfi -i aevalsrc=0 \
-i INPUTVIDEO \
-filter_complex '[0:0] [1:0] [2:0] [2:1] concat=n=2:v=1:a=1' \
[OPTIONS] OUTPUT
If your video doesn't have audio, try this:
ffmpeg -loop 1 -framerate FPS -t SECONDS -i IMAGE \
-i INPUTVIDEO \
-filter_complex '[0:0] [1:0] concat=n=2:v=1:a=0' \
[OPTIONS] OUTPUT
FPS = tbr value got from step 1
SECONDS = duration you want the image to be shown.
IMAGE = the image name
INPUTVIDEO = the original video name
[OPTIONS] = optional encoding parameters (such as -vcodec libx264 or -b:a 160k)
OUTPUT = the output video file name
How Does This Work?
Let's split the command line I used:
-loop 1 -framerate FPS -t SECONDS -i IMAGE: this basically means: open the image, and loop over it to make it a video with SECONDS seconds with FPS frames per second. The reason you need it to have the same FPS as the input video is because the concat filter we will use later has a restriction on it.
-t SECONDS -f lavfi -i aevalsrc=0: this means: generate silence for SECONDS (0 means silence). You need silence to fill up the time for the splash image. This isn't needed if the original video doesn't have audio.
-i INPUTVIDEO: open the video itself.
-filter_complex '[0:0] [1:0] [2:0] [2:1] concat=n=2:v=1:a=1': this is the best part. You open file 0 stream 0 (the image-video), file 1 stream 0 (the silence audio), file 2 streams 0 and 1 (the real input audio and video), and concatenate them together. The options n, v, and a mean that there are 2 segments, 1 output video, and 1 output audio.
[OPTIONS] OUTPUT: this just means to encode the video to the output file name. If you are using HTML5 streaming, you'd probably want to use -c:v libx264 -crf 23 -c:a libfdk_aac (or -c:a libfaac) -b:a 128k for H.264 video and AAC audio.
Further information
You can check out the documentation for the image2 demuxer which is the core of the magic behind -loop 1.
Documentation for concat filter is also helpful.
Another good source of information is the FFmpeg wiki on concatenation.
The answer above works for me but in my case it took too much time to execute (perhaps because it re-encodes the entire video). I found another solution that's much faster. The basic idea is:
Create a "video" that only has the image.
Concatenate the above video with the original one, without re-encoding.
Create a video that only has the image:
ffmpeg -loop 1 -framerate 30 -i image.jpg -c:v libx264 -t 3 -pix_fmt yuv420p image.mp4
Note the -framerate 30 option. It has to be the same with the main video. Also, the image should have the same dimension with the main video. The -t 3 specifies the length of the video in seconds.
Convert the videos to MPEG-2 transport stream
According to the ffmpeg official documentation, only certain files can be concatenated using the concat protocal, this includes the MPEG-2 transport streams. And since we have 2 MP4 videos, they can be losslessly converted to MPEG-2 TS:
ffmpeg -i image.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts image.ts
and for the main video:
ffmpeg -i video.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts video.ts
Concatenate the MPEG-2 TS files
Now use the following command to concatenate the above intermediate files:
ffmpeg -i "concat:image.ts|video.ts" -c copy -bsf:a aac_adtstoasc output.mp4
Although there are 4 commands to run, combined they're still much faster then re-encoding the entire video.
My solution. It sets an image with duration of 5 sec before the video along with aligning video to be 1280x720. Image should have 16/9 aspect ratio.
ffmpeg -i video.mp4 -i image.png -filter_complex '
color=c=black:size=1280x720 [temp]; \
[temp][1:v] overlay=x=0:y=0:enable='between(t,0,5)' [temp]; \
[0:v] setpts=PTS+5/TB, scale=1280x720:force_original_aspect_ratio=decrease, pad=1280:720:-1:-1:color=black [v:0]; \
[temp][v:0] overlay=x=0:y=0:shortest=1:enable='gt(t,5)' [v]; \
[0:a] asetpts=PTS+5/TB [a]'
-map [v] -map [a] -preset veryfast output.mp4

Resources