diff --git a/common_py/src/videoag_common/media_process/basic_targets.py b/common_py/src/videoag_common/media_process/basic_targets.py index 5af86b9ea5b616369f4b0b072cab0db840adef8b..55252a816c6906e54b972f0f110013cbfc98034b 100644 --- a/common_py/src/videoag_common/media_process/basic_targets.py +++ b/common_py/src/videoag_common/media_process/basic_targets.py @@ -70,6 +70,7 @@ class SourceFileTargetProducer(SingleOutputTargetProducer["SourceMedium"]): class RescaleVideoTargetProducer(SingleInputTargetProducer, SingleOutputTargetProducer): target_vertical_resolution: int = json_field(min_value=1, max_value=10000) + crf: int | None = None @classmethod def get_type(cls) -> str: @@ -96,7 +97,8 @@ class RescaleVideoTargetProducer(SingleInputTargetProducer, SingleOutputTargetPr return "rescale_video", { "input_file": input_medium.file.file_path, "output_file": output_file.file_path, - "target_vertical_resolution": self.target_vertical_resolution + "target_vertical_resolution": self.target_vertical_resolution, + "crf": self.crf } diff --git a/common_py/src/videoag_common/media_process/jnode/file_jnode.py b/common_py/src/videoag_common/media_process/jnode/file_jnode.py index 92fa48550376ce4a323c5367d00926d2b135b9d7..29b9ac5e0cb50347d7cadf0d31860f8edaa8b27e 100644 --- a/common_py/src/videoag_common/media_process/jnode/file_jnode.py +++ b/common_py/src/videoag_common/media_process/jnode/file_jnode.py @@ -252,6 +252,7 @@ _AUDIO_CODECS = [OutputCodec.OPUS, OutputCodec.FLAC, OutputCodec.AAC] class OutputStream(JsonDataClass): graph_id: str framerate: int | None = json_field(default=None, note="If not specified, ffmpeg will guess") + crf: int | None = None codec: OutputCodec @@ -324,7 +325,12 @@ class OutputFileJNode(JGraphNode): if output_stream.framerate is not None: if not isinstance(metadata, FVideoStreamMetadata): raise ValueError(f"Cannot set output framerate for non-video stream {output_stream.graph_id}") - out_args["r"] = output_stream.framerate + out_args["r"] = output_stream.framerate# + + if output_stream.crf is not None: + if not isinstance(metadata, FVideoStreamMetadata): + raise ValueError(f"Cannot set output crf for non-video stream {output_stream.graph_id}") + out_args["crf"] = output_stream.crf streams.append((metadata.fid, out_args)) diff --git a/example_media_process.json b/example_media_process.json index 04ed13a85358932a3d34fff28048bf22d0f49759..4877667998e34b88a3dea69edebceb0baedf02a1 100644 --- a/example_media_process.json +++ b/example_media_process.json @@ -205,7 +205,8 @@ { "graph_id": "video_with_inoutro_logo", "codec": "av1", - "framerate": 50 + "framerate": 50, + "crf": 23 }, { "graph_id": "audio_with_inoutro", @@ -218,5 +219,5 @@ ] }, {"type": "sample_thumbnail", "timestamp_percent": 25, "input_id": "processed_video", "output_id": "thumbnail"}, - {"type": "rescale_video", "target_vertical_resolution": 720, "input_id": "processed_video", "output_id": "video_720"} + {"type": "rescale_video", "target_vertical_resolution": 720, "crf": 23, "input_id": "processed_video", "output_id": "video_720"} ], "publish_target_ids": ["processed_video", "thumbnail", "video_720"], "publish_wait_for_full_process": true} \ No newline at end of file diff --git a/job_controller/jobs/rescale_video/job.py b/job_controller/jobs/rescale_video/job.py index 39d32114ca5ccbc7d261cbde0edd6c8680acbcd4..ab0846f99026b72c0fb5576b7e30a12ab5ef3990 100644 --- a/job_controller/jobs/rescale_video/job.py +++ b/job_controller/jobs/rescale_video/job.py @@ -4,7 +4,7 @@ import subprocess from pathlib import Path from videoag_common.ffmpeg import get_file_extension, FFProbe, FFProbeVideoStream -from videoag_common.miscellaneous import CJsonObject, MAX_VALUE_SINT32 +from videoag_common.miscellaneous import CJsonObject, MAX_VALUE_SINT32, MIN_VALUE_SINT32 logger = logging.getLogger(__name__) @@ -17,6 +17,7 @@ def execute(database, own_job_id, input_data: CJsonObject): output_file = _DATA_DIR.joinpath(input_data.get_string("output_file", max_length=None)) tmp_output_file = Path("/tmp/job_output." + get_file_extension(output_file)) target_vertical_resolution = input_data.get_int("target_vertical_resolution", min_value=0, max_value=MAX_VALUE_SINT32) + crf = input_data.get_int("crf", min_value=MIN_VALUE_SINT32, max_value=MAX_VALUE_SINT32, optional=True) if not input_file.exists() or not input_file.is_file(): raise ValueError(f"Input file {input_file} doesn't exist") @@ -51,6 +52,8 @@ def execute(database, own_job_id, input_data: CJsonObject): raise ValueError(f"Unable to chose encoder for unknown codec '{codec}'") cmd.extend([f"-filter:{i}", f"scale=height={target_vertical_resolution}:width=-1"]) cmd.extend([f"-c:{i}", encoder]) + if crf is not None: + cmd.extend([f"-crf:{i}", str(crf)]) cmd.extend(["-c:a", "copy"])