Browse Source

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
multiflexi 3 years ago
parent
commit
48838ac406
1 changed files with 32 additions and 13 deletions
  1. 32 13
      files/helpers.py

+ 32 - 13
files/helpers.py

@@ -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
@@ -366,18 +369,31 @@ def media_file_info(input_file):
         stdout = run_command(cmd).get("out")
         stdout = run_command(cmd).get("out")
         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_frame_rate": float(Fraction(video_info["r_frame_rate"])),
+        "video_duration": video_duration, 
+        "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 Frame rate very high for a muxer not efficiently supporting it.
-    if target_fps > 90:
-        target_fps = 90
+    # avoid very high frame rates
+    while target_fps > 60:
+        target_fps = target_fps/2
+
+    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)
-    if src_framerate <= 30:
+    target_fps = Fraction(int(media_info.get("video_frame_rate_n", 30)), int(media_info.get("video_frame_rate_d", 1)))
+    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(
@@ -738,4 +757,4 @@ def clean_query(query):
     for char in chars:
     for char in chars:
         query = query.replace(char, "")
         query = query.replace(char, "")
 
 
-    return query.lower()
+    return query.lower()