Search code examples
videoffmpegvideo-processingbitrate

Manipulate video with ffmpeg without losing quality


I am rotating a video with ffmpeg with the following code:

ffmpeg -i nd750_a0040.MOV -vf "transpose=dir=clock, transpose=dir=clock" out.mkv

The resulting file is almost 10x smaller than the input. I have found this question which addresses a related question and suggsts passing to ffmpeg the codec and bitrate obtained from the following:

bitratev="$(ffmpeg -i "$1" -f null - |& grep video: | awk -F'[:|kB]' '{print $2}')"
codecv="$(ffprobe -loglevel error -select_streams v:0 -show_entries stream=codec_name -of default=nk=1:nw=1 "$1")"

however, both of these commands give me the same output for both files: 2643 and h264 respectively. Am I correct in assuming that ffmpeg keeps these values the same for the output - by default?

However, if I inspect the files with ffmpeg -i I get different bitrate values:

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'nd750_a0040.MOV':
  Metadata:
    major_brand     : qt  
    minor_version   : 537331968
    compatible_brands: qt  niko
    creation_time   : 2016-06-18 04:28:03
  Duration: 00:15:40.74, start: 0.000000, bitrate: 11569 kb/s
    Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuvj420p(pc, smpte170m/bt709/bt470m), 1920x1080, 10029 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 59.94 tbc (default)
    Metadata:
      creation_time   : 2016-06-18 04:28:03
    Stream #0:1(eng): Audio: pcm_s16le (sowt / 0x74776F73), 48000 Hz, 2 channels, s16, 1536 kb/s (default)
    Metadata:
      creation_time   : 2016-06-18 04:28:03

and

Input #0, matroska,webm, from 'out.mkv':
  Metadata:
    COMPATIBLE_BRANDS: qt  niko
    MAJOR_BRAND     : qt  
    MINOR_VERSION   : 537331968
    ENCODER         : Lavf56.40.101
  Duration: 00:15:40.74, start: 0.000000, bitrate: 1445 kb/s
    Stream #0:0(eng): Video: h264 (High), yuvj420p(pc), 1920x1080, SAR 1:1 DAR 16:9, 29.97 fps, 29.97 tbr, 1k tbn, 59.94 tbc (default)
    Metadata:
      CREATION_TIME   : 2016-06-18 04:28:03
      LANGUAGE        : eng
      ENCODER         : Lavc56.60.100 libx264
      DURATION        : 00:15:40.742000000
    Stream #0:1(eng): Audio: vorbis, 48000 Hz, stereo, fltp (default)
    Metadata:
      CREATION_TIME   : 2016-06-18 04:28:03
      LANGUAGE        : eng
      ENCODER         : Lavc56.60.100 libvorbis
      DURATION        : 00:15:40.743000000

So I have a few questions:

  • Which bitrate is the correct one for each video?
  • Is all of the information loss between these two files covered by the bitrate (or does ffmpeg by default change other things that lead to a lower file size as well - if so, what?)?
  • How do I make sure nothing else changes but the container format and the rotation?

Solution

  • If you manipulate the video using a filter, such as the transpose filter, the video will be re-encoded.

    The command below will get you the bitrate for the video stream, if available.

    ffprobe video.mov -select_streams v -show_entries stream=bit_rate -of compact=p=0:nk=1
    

    See my answer here, if this doesn't produce a value.


    Your present command re-encodes the audio as well. You can skip that, and specify a bitrate:

    ffmpeg -i nd750_a0040.MOV -vf "transpose=clock,transpose=clock" -b:v 10M -c:a copy out.mkv
    

    The x264 encode is smart, so it will only use as much upto the bitrate as needed.

    Instead of sticking to the bitrate, you can use the CRF method, which aims to maintain a constant quality throughout. 18 is a good number. Lower is better, but visually not usually noticeable.

    ffmpeg -i nd750_a0040.MOV -vf "transpose=clock,transpose=clock" -crf 18 -c:a copy out.mkv
    

    Of course, if your player supports it, you can just add a rotation tag and not re-encode at all.

    ffmpeg -i nd750_a0040.MOV -c copy -metadata:s:v:0 rotate=180 out.mkv