captionbob docs

Version 1.0.0
Contents ↓

Python: captionbob.render

captionbob.render.burn

The burn of a video with one ffmpeg.

ffmpeg draws the video in one pass. Each image layer, such as the emoji of an emoji effect, is one more input of ffmpeg: the image is read again and again for the time of its layer, so an animated image plays from its first frame at the start of the layer. The filter scale gives the image its size on each frame, and the filter overlay puts it on the video at its place for the time of the layer. The filter ass then draws the script over the images. The plugins act on the whole video after the script, each on the output of the one before it. The audio is copied.

ffmpeg writes its progress to its standard output, and run_burn gives the seconds of the video that are done to a function of the caller.

The layer, filter graph and script parts were moved here from the effect video tool, captionbob/devtools/effect_video.py, which draws with them.

Source file: captionbob/render/burn.py

ImageLayer

class ImageLayer(Protocol)

An image that ffmpeg draws on the video for a part of its time.

The expressions are ffmpeg expressions. They may use the time t of a frame in seconds of the video. The place may also use the width W of the video and the width w and the height h of the image frame.

ImageLayer.size_expression

def size_expression(self) -> str

Give the side of the square image in pixels.

ImageLayer.left_expression

def left_expression(self) -> str

Give the left column of the image.

ImageLayer.top_expression

def top_expression(self) -> str

Give the top row of the image.

PluginGraph

class PluginGraph(NamedTuple)

The filter graph of the plugins of a burn.

Args:

  • text: The filter graph text of the plugins, empty when there are none. It reads the label OUTPUT_LABEL.
  • output_label: The label of the last output, OUTPUT_LABEL when there are no plugins.

layer_input_arguments

def layer_input_arguments(
        layers: Sequence[ImageLayer], frame_rate: Fraction) -> list[str]

Give the ffmpeg input arguments of the image layers.

The image of a layer is read for one frame more than the time of the layer. Without that frame, a layer that ends between two frames of the video has no image on the last frame before its end, while an ASS event with the same times is still on that frame. The overlay of the layer decides on which frames the image is seen.

Args:

  • layers: The image layers.
  • frame_rate: The frames each second of the video.

Returns:

For each layer the arguments that read its image again and again for the time of the layer and one frame, from the first frame of the image.

Raises:

  • ValueError: When a layer does not end after its start.
  • FileNotFoundError: When an image is not a file.

layer_milliseconds

def layer_milliseconds(layer: ImageLayer) -> tuple[int, int]

Give the start and the end of a layer in whole milliseconds.

The overlay of a layer is on the frames whose time, cut to whole milliseconds, is from the start up to the end, without the end.

Args:

  • layer: The image layer.

Returns:

The start and the end, each rounded to the nearest millisecond.

effect_filter_graph

def effect_filter_graph(
        script_name: str, layers: Sequence[ImageLayer],
        frame_rate: Fraction) -> str

Write the ffmpeg filter graph that draws the layers and the script.

The images are drawn on the video in the order of the layers, and the script is drawn over them. The filter fps gives the frames of an image the frame times of the video, so the size of an image on a frame comes from the time of that frame. The overlay of a layer acts only on the frames of its time, because the overlay filter keeps the last frame of an image on the next frame of the video.

Args:

  • script_name: The file name of the script in the folder in which ffmpeg runs.
  • layers: The image layers, which are the inputs from FIRST_LAYER_INPUT on.
  • frame_rate: The frames each second of the video.

Returns:

The filter graph. Its output has the label OUTPUT_LABEL.

Raises:

  • ValueError: When the script name holds a mark that a filter reads.

plugin_filter_graph

def plugin_filter_graph(
        video: VideoInfo, plugins: Sequence[ChosenPlugin],
        has_audio: bool) -> PluginGraph

Write the filter graph of the plugins, after the script.

Each plugin reads the output of the one before it, and the first reads OUTPUT_LABEL. The plugins that read the sound each get a copy of the sound of the video from the filter asplit, because a filter output can be read only one time.

Args:

  • video: The frame size, the frame rate and the duration of the video.
  • plugins: The plugins in the order in which they are drawn.
  • has_audio: True when the video has a sound stream.

Returns:

The filter graph of the plugins and the label of its last output. NO_PLUGINS when there are no plugins.

Raises:

  • ValueError: When a plugin reads the sound and the video has no sound.

burn_command

def burn_command(
        ffmpeg_path: Path, video_path: Path, script_path: Path,
        layers: Sequence[ImageLayer], frame_rate: Fraction,
        plugin_graph: PluginGraph, output_path: Path) -> list[str]

Give the ffmpeg command that burns the layers, the script and the plugins on a video.

ffmpeg must run in the folder of the script, because the filter ass names the script with no path. ffmpeg writes its progress to its standard output.

Args:

  • ffmpeg_path: The ffmpeg of the environment.
  • video_path: The video. Its audio, when it has one, is copied.
  • script_path: The ASS script file.
  • layers: The image layers.
  • frame_rate: The frames each second of the video.
  • plugin_graph: The filter graph of the plugins, NO_PLUGINS for none.
  • output_path: The new video. A file that is already there is replaced.

Returns:

The command and its arguments.

Raises:

  • FileNotFoundError: When the video, the script or an image is not a file.
  • ValueError: When a layer does not end after its start, or when the script name holds a mark that a filter reads.

progress_seconds

def progress_seconds(line: str) -> float | None

Read the seconds that are done from one progress line of ffmpeg.

Args:

  • line: One line of the standard output of ffmpeg.

Returns:

The seconds of the video that are done, or None when the line does not give them. ffmpeg gives N/A before the first frame.

exit_code_text

def exit_code_text(exit_code: int) -> str

Write an exit code in decimal and in hex, as Windows shows it.

Args:

  • exit_code: The exit code of a program, such as 3221225477. A negative code is a signal on Linux.

Returns:

The text, such as "3221225477 = 0xC0000005".

run_burn

def run_burn(
        command: Sequence[str], run_folder: Path, error_log_path: Path,
        report_progress: Callable[[float], None]) -> None

Run the burn command and report its progress.

Args:

  • command: The command of burn_command.
  • run_folder: The folder of the script, in which ffmpeg runs.
  • error_log_path: The file that keeps the error text of ffmpeg. A file that is already there is replaced.
  • report_progress: Gets the seconds of the video that are done, each time ffmpeg gives them.

Raises:

  • RuntimeError: When ffmpeg fails. The text holds the exit code and the error text of ffmpeg, or says that there is no error text.

keyword_word_emoji

def keyword_word_emoji(
        matcher: EmojiMatcher,
        keyword_words: frozenset[Word]) -> Callable[[Sequence[Word]],
                                                     Path | None]

Make the emoji choice of a caption that compares only its keyword words.

Args:

  • matcher: The emoji matcher of an emoji index folder.
  • keyword_words: The words of the transcript that may be keywords, such as its nouns and its adjectives.

Returns:

A function that gives the emoji of the keyword words of a caption, or None when the caption has no keyword word or the matcher finds no keyword in them.

keyword_layers

def keyword_layers(
        effect: Any, words: Sequence[Word], video: VideoInfo,
        matcher: EmojiMatcher,
        keyword_words: frozenset[Word] | None = None
        ) -> tuple[str, tuple[Any, ...]]

Build the script and the keyword emoji layers of an emoji effect.

Each caption shows the emoji whose meaning is closest to its best keyword. A caption with no keyword shows no emoji.

Args:

  • effect: An effect with the method build_layers.
  • words: The words of the video in time order.
  • video: The frame size, the frame rate and the duration of the video.
  • matcher: The emoji matcher of an emoji index folder.
  • keyword_words: The words that the matcher compares, such as the nouns and the adjectives of the transcript. None compares every word.

Returns:

The text of the ASS script and the emoji layers.