Source code for cutcutcodec.core.compilation.export.rate

"""Allow to suggest an appropriate rate."""

import logging
import typing
from fractions import Fraction

from cutcutcodec.core.analysis.stream.rate_audio import optimal_rate_audio
from cutcutcodec.core.analysis.stream.rate_video import optimal_rate_video
from cutcutcodec.core.classes.encoder import Encoder
from cutcutcodec.core.classes.stream_audio import StreamAudio
from cutcutcodec.core.classes.stream_video import StreamVideo


[docs] def available_audio_rates(encoders: typing.Iterable[str]) -> None | set[int]: """Search the different sampling frequencies available by this encoder. Parameters ---------- encoders : list[str] The encoder name. Returns ------- rates : set[int] or None The set of the available rates. The value None means their is no constraints. An empty set means there is not availaible or common rates. Examples -------- >>> from cutcutcodec.core.compilation.export.rate import available_audio_rates >>> available_audio_rates([]) set() >>> sorted(available_audio_rates(["libmp3lame"])) [8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000] >>> sorted(available_audio_rates(["libopus"])) [8000, 12000, 16000, 24000, 48000] >>> print(available_audio_rates(["flac"])) None >>> sorted(available_audio_rates(["libmp3lame", "libopus"])) [8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000] >>> """ assert isinstance(encoders, typing.Iterable), encoders.__class__.__name__ common_rates = set() for encoder in encoders: if (rates := Encoder(encoder).audio_rates) is None: return None common_rates |= set(rates) return common_rates
[docs] def suggest_audio_rate( stream: StreamAudio, choices: typing.Iterable[int] | None = None, ) -> int: """Return the best compatible audio samplerate. Parameters ---------- stream : cutcutcodec.core.classes.stream_audio.StreamAudio The stream that we want to encode. choices : set[int], optional The possible rates. If provide, returns the most appropriate rate of this set. The value None means all rates are allowed. This selection could be generated from ``cutcutcodec.core.compilation.export.rate.available_audio_rates``. Returns ------- rate : int A suitable sampling rate compatible with the specified options. Examples -------- >>> from cutcutcodec.core.compilation.export.rate import suggest_audio_rate >>> from cutcutcodec.core.io.read_ffmpeg import ContainerInputFFMPEG >>> from cutcutcodec.utils import get_project_root >>> audio = get_project_root() / "media" / "audio" / "narration_5_1.oga" >>> (stream,) = ContainerInputFFMPEG(audio).out_streams >>> suggest_audio_rate(stream) # no constraint 16000 >>> suggest_audio_rate(stream, [8000, 12000, 24000, 48000]) # constraint 24000 >>> """ assert isinstance(stream, StreamAudio), stream.__class__.__name__ if choices is not None: assert isinstance(choices, typing.Iterable), choices.__class__.__name__ choices = set(choices) assert all(isinstance(r, int) for r in choices), choices assert all(r > 0 for r in choices), choices # estimation of the best rate optimal: int = optimal_rate_audio(stream) # can be 0, not None # select the most appropriate rate among the choices if not optimal: # if the optimal rate is not found choices = choices or [48000] # default value return min(choices, key=lambda r: (r < 48000, abs(r-48000))) if choices is None: return optimal choice = min(choices, key=lambda r: (r < optimal, abs(r-optimal))) if choice < optimal: logging.warning( "spectral aliasing, append low pass filter because max rate is %d and best is %d", max(choices), optimal, ) return choice
[docs] def suggest_video_rate(stream: StreamVideo) -> Fraction: """Return the best compatible video framerate. Parameters ---------- stream : cutcutcodec.core.classes.stream_video.StreamVideo The stream that we want to encode. Returns ------- rate : Fraction An optimal frame rate. Examples -------- >>> from cutcutcodec.core.compilation.export.rate import suggest_video_rate >>> from cutcutcodec.core.io.read_ffmpeg import ContainerInputFFMPEG >>> from cutcutcodec.utils import get_project_root >>> video = get_project_root() / "media" / "video" / "intro.webm" >>> stream = ContainerInputFFMPEG(video).out_select("video")[0] >>> suggest_video_rate(stream) Fraction(30000, 1001) >>> """ return optimal_rate_video(stream) or Fraction(30000, 1001)