Framerate fix (#246)

* Framerate fix

Keep original framerate up to 60fps, halve any framerate above 60fps. Because of "video_frame_rate": Fraction(video_info["r_frame_rate"]), it does not work, when float used, the video is encoded but framerate suffers from rounding error.

* Framerate fix

Keep original framerate up to 60fps, halve any framerate above 60fps. Because of "video_frame_rate": Fraction(video_info["r_frame_rate"]), it does not work, when float used, the video is encoded but framerate suffers from rounding error.

* Introduction of minimum bitrate modifier

A minimum bitrate modifier introduced as per https://developers.google.com/media/vp9/settings/vod

* Introduction of minimum bitrate modifier

A minimum bitrate modifier introduced as per https://developers.google.com/media/vp9/settings/vod
This commit is contained in:
multiflexi 2021-08-20 13:04:50 +02:00 committed by GitHub
parent 062e4be6c2
commit 48838ac406
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -23,6 +23,8 @@ CRF_ENCODING_NUM_SECONDS = 2 # 0 * 60 # videos with greater duration will get
# you should use CRF encoding. # you should use CRF encoding.
MAX_RATE_MULTIPLIER = 1.5 MAX_RATE_MULTIPLIER = 1.5
MIN_RATE_MULTIPLIER = 0.5
BUF_SIZE_MULTIPLIER = 1.5 BUF_SIZE_MULTIPLIER = 1.5
# in seconds, anything between 2 and 6 makes sense # in seconds, anything between 2 and 6 makes sense
@ -238,7 +240,8 @@ def media_file_info(input_file):
- `filename`: Filename - `filename`: Filename
- `file_size`: Size of the file in bytes - `file_size`: Size of the file in bytes
- `video_duration`: Duration of the video in `s.msec` - `video_duration`: Duration of the video in `s.msec`
- `video_frame_rate`: Framerate in Hz - `video_frame_rate_d`: Framerate franction denominator
- `video_frame_rate_n`: Framerate fraction nominator
- `video_bitrate`: Bitrate of the video stream in kBit/s - `video_bitrate`: Bitrate of the video stream in kBit/s
- `video_width`: Width in pixels - `video_width`: Width in pixels
- `video_height`: Height in pixels - `video_height`: Height in pixels
@ -367,17 +370,30 @@ def media_file_info(input_file):
stream_size = sum([int(line) for line in stdout.split("\n") if line != ""]) stream_size = sum([int(line) for line in stdout.split("\n") if line != ""])
video_bitrate = round((stream_size * 8 / 1024.0) / video_duration, 2) video_bitrate = round((stream_size * 8 / 1024.0) / video_duration, 2)
if "r_frame_rate" in video_info.keys():
video_frame_rate = video_info["r_frame_rate"].partition("/")
video_frame_rate_n = video_frame_rate[0]
video_frame_rate_d = video_frame_rate[2]
ret = { ret = {
"filename": input_file, "filename": input_file,
"file_size": file_size, "file_size": file_size,
"video_duration": video_duration, "video_duration": video_duration,
"video_frame_rate": float(Fraction(video_info["r_frame_rate"])), "video_frame_rate_n": video_frame_rate_n,
"video_frame_rate_d": video_frame_rate_d,
"video_bitrate": video_bitrate, "video_bitrate": video_bitrate,
"video_width": video_info["width"], "video_width": video_info["width"],
"video_height": video_info["height"], "video_height": video_info["height"],
"video_codec": video_info["codec_name"], "video_codec": video_info["codec_name"],
"has_video": has_video, "has_video": has_video,
"has_audio": has_audio, "has_audio": has_audio,
"color_range": video_info.get("color_range"),
"color_space": video_info.get("color_space"),
"color_transfer": video_info.get("color_space"),
"color_primaries": video_info.get("color_primaries"),
"field_order": video_info.get("field_order"),
"display_aspect_ratio": video_info.get("display_aspect_ratio"),
"sample_aspect_ratio": video_info.get("sample_aspect_ratio"),
} }
if has_audio: if has_audio:
@ -492,7 +508,7 @@ def get_base_ffmpeg_command(
codec {str} -- video codec codec {str} -- video codec
encoder {str} -- video encoder encoder {str} -- video encoder
audio_encoder {str} -- audio encoder audio_encoder {str} -- audio encoder
target_fps {int} -- target FPS target_fps {fractions.Fraction} -- target FPS
target_height {int} -- height target_height {int} -- height
target_rate {int} -- target bitrate in kbps target_rate {int} -- target bitrate in kbps
target_rate_audio {int} -- audio target bitrate target_rate_audio {int} -- audio target bitrate
@ -501,10 +517,12 @@ def get_base_ffmpeg_command(
enc_type {str} -- encoding type (twopass or crf) enc_type {str} -- encoding type (twopass or crf)
""" """
target_fps = int(target_fps) # avoid very high frame rates
# avoid Frame rate very high for a muxer not efficiently supporting it. while target_fps > 60:
if target_fps > 90: target_fps = target_fps/2
target_fps = 90
if target_fps < 1:
target_fps = 1
base_cmd = [ base_cmd = [
settings.FFMPEG_COMMAND, settings.FFMPEG_COMMAND,
@ -622,6 +640,8 @@ def get_base_ffmpeg_command(
str(keyframe_distance), str(keyframe_distance),
"-maxrate", "-maxrate",
str(int(int(target_rate) * MAX_RATE_MULTIPLIER)) + "k", str(int(int(target_rate) * MAX_RATE_MULTIPLIER)) + "k",
"-minrate",
str(int(int(target_rate) * MIN_RATE_MULTIPLIER)) + "k",
"-bufsize", "-bufsize",
str(int(int(target_rate) * BUF_SIZE_MULTIPLIER)) + "k", str(int(int(target_rate) * BUF_SIZE_MULTIPLIER)) + "k",
"-speed", "-speed",
@ -669,8 +689,8 @@ def produce_ffmpeg_commands(media_file, media_info, resolution, codec, output_fi
else: else:
return False return False
src_framerate = media_info.get("video_frame_rate", 30) target_fps = Fraction(int(media_info.get("video_frame_rate_n", 30)), int(media_info.get("video_frame_rate_d", 1)))
if src_framerate <= 30: if target_fps <= 30:
target_rate = VIDEO_BITRATES[codec][25].get(resolution) target_rate = VIDEO_BITRATES[codec][25].get(resolution)
else: else:
target_rate = VIDEO_BITRATES[codec][60].get(resolution) target_rate = VIDEO_BITRATES[codec][60].get(resolution)
@ -687,8 +707,6 @@ def produce_ffmpeg_commands(media_file, media_info, resolution, codec, output_fi
# target_fps = 25 # target_fps = 25
# else: # else:
# adjust the target frame rate if the input is fractional
target_fps = src_framerate if isinstance(src_framerate, int) else math.ceil(src_framerate)
if media_info.get("video_duration") > CRF_ENCODING_NUM_SECONDS: if media_info.get("video_duration") > CRF_ENCODING_NUM_SECONDS:
enc_type = "crf" enc_type = "crf"
@ -700,6 +718,7 @@ def produce_ffmpeg_commands(media_file, media_info, resolution, codec, output_fi
elif enc_type == "crf": elif enc_type == "crf":
passes = [2] passes = [2]
cmds = [] cmds = []
for pass_number in passes: for pass_number in passes:
cmds.append( cmds.append(