• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

EsupPortail / Esup-Pod / 22315024542

23 Feb 2026 04:26PM UTC coverage: 68.113%. First build
22315024542

Pull #1403

github

web-flow
Merge 147b047e9 into 7506dd15c
Pull Request #1403: Runner Manager support

632 of 1449 new or added lines in 24 files covered. (43.62%)

12812 of 18810 relevant lines covered (68.11%)

0.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

25.71
/pod/video_encode_transcript/encoding_studio.py
1
"""This module handles studio encoding with CPU."""
2

3
import json
1✔
4
import subprocess
1✔
5
import time
1✔
6

7
if __name__ == "__main__":
1✔
NEW
8
    from encoding_settings import (FFMPEG_CMD, FFMPEG_CRF, FFMPEG_NB_THREADS,
×
9
                                   FFMPEG_STUDIO_COMMAND, FFPROBE_CMD,
10
                                   FFPROBE_GET_INFO)
11
else:
12
    from .encoding_settings import (FFMPEG_CMD, FFMPEG_CRF, FFMPEG_NB_THREADS,
1✔
13
                                    FFMPEG_STUDIO_COMMAND, FFPROBE_CMD,
14
                                    FFPROBE_GET_INFO)
15

16
try:
1✔
17
    from django.conf import settings
1✔
18

19
    FFMPEG_CMD = getattr(settings, "FFMPEG_CMD", FFMPEG_CMD)
1✔
20
    FFPROBE_CMD = getattr(settings, "FFPROBE_CMD", FFPROBE_CMD)
1✔
21
    FFMPEG_CRF = getattr(settings, "FFMPEG_CRF", FFMPEG_CRF)
1✔
22
    FFMPEG_NB_THREADS = getattr(settings, "FFMPEG_NB_THREADS", FFMPEG_NB_THREADS)
1✔
23
    FFPROBE_GET_INFO = getattr(settings, "FFPROBE_GET_INFO", FFPROBE_GET_INFO)
1✔
24
    FFMPEG_STUDIO_COMMAND = getattr(
1✔
25
        settings, "FFMPEG_STUDIO_COMMAND", FFMPEG_STUDIO_COMMAND
26
    )
27
except ImportError:  # pragma: no cover
28
    pass
29

30

31
# ##########################################################################
32
# ENCODE VIDEO STUDIO: MAIN ENCODE
33
# ##########################################################################
34

35

36
def get_video_info(command):
1✔
37
    """Get ffprobe video info."""
38
    ffproberesult = subprocess.run(
×
39
        command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
40
    )
41
    return json.loads(ffproberesult.stdout.decode("utf-8"))
×
42

43

44
def start_encode_video_studio(video_output, videos, subtime, presenter):
1✔
45
    """Encode video from studio."""
46
    presenter_source = None
×
47
    presentation_source = None
×
48
    input_video = ""
×
49
    for video in videos:
×
50
        if video.get("type") == "presenter/source":
×
51
            presenter_source = video.get("src")
×
52
            input_video = '-i "' + presenter_source + '" '
×
53
        if video.get("type") == "presentation/source":
×
54
            presentation_source = video.get("src")
×
55
            input_video = '-i "' + presentation_source + '" '
×
56
    info_presenter_video = {}
×
57
    info_presentation_video = {}
×
58
    if presenter_source and presentation_source:
×
59
        # to put it in the right order
60
        input_video = '-i "' + presentation_source + '" -i "' + presenter_source + '" '
×
61
        command = FFPROBE_GET_INFO % {
×
62
            "ffprobe": FFPROBE_CMD,
63
            "select_streams": "-select_streams v:0 ",
64
            "source": '"' + presentation_source + '" ',
65
        }
66
        info_presentation_video = get_video_info(command)
×
67
        command = FFPROBE_GET_INFO % {
×
68
            "ffprobe": FFPROBE_CMD,
69
            "select_streams": "-select_streams v:0 ",
70
            "source": '"' + presenter_source + '" ',
71
        }
72
        info_presenter_video = get_video_info(command)
×
73
        subcmd = get_sub_cmd(
×
74
            get_height(info_presentation_video),
75
            get_height(info_presenter_video),
76
            presenter,
77
        )
78

79
    else:
80
        subcmd = " -vsync 0 "
×
81
    subcmd += " -movflags +faststart -f mp4 "
×
82

83
    return launch_encode_video_studio(input_video, subtime, subcmd, video_output)
×
84

85

86
def get_sub_cmd(height_presentation_video, height_presenter_video, presenter):
1✔
87
    min_height = min([height_presentation_video, height_presenter_video])
×
88
    subcmd = ""
×
89
    if presenter == "pipb":
×
90
        # trouver la bonne hauteur en fonction de la video de presentation
91
        height = (
×
92
            height_presentation_video
93
            if (height_presentation_video % 2) == 0
94
            else height_presentation_video + 1
95
        )
96
        # ffmpeg -y -i presentation_source.webm -i presenter_source.webm \
97
        # -c:v libx264 -filter_complex "[0:v]scale=-2:720[pres];[1:v]scale=-2:180[pip];\
98
        # [pres][pip]overlay=W-w-10:H-h-10:shortest=1" \
99
        # -vsync 0 outputVideo.mp4
100
        subcmd = (
×
101
            " -filter_complex "
102
            + '"[0:v]scale=-2:%(height)s[pres];[1:v]scale=-2:%(sh)s[pip];'
103
            % {"height": height, "sh": height / 4}
104
            + '[pres][pip]overlay=W-w-10:H-h-10:shortest=1" -vsync 0 '
105
        )
106
    if presenter == "piph":
×
107
        # trouver la bonne hauteur en fonction de la video de presentation
108
        height = (
×
109
            height_presentation_video
110
            if (height_presentation_video % 2) == 0
111
            else height_presentation_video + 1
112
        )
113
        # ffmpeg -y -i presentation_source.webm -i presenter_source.webm \
114
        # -c:v libx264 -filter_complex "[0:v]scale=-2:720[pres];[1:v]scale=-2:180[pip];\
115
        # [pres][pip]overlay=W-w-10:H-h-10:shortest=1" \
116
        # -vsync 0 outputVideo.mp4
117
        subcmd = (
×
118
            " -filter_complex "
119
            + '"[0:v]scale=-2:%(height)s[pres];[1:v]scale=-2:%(sh)s[pip];'
120
            % {"height": height, "sh": height / 4}
121
            + '[pres][pip]overlay=W-w-10:10:shortest=1" -vsync 0 '
122
        )
123
    if presenter == "mid":
×
124
        height = min_height if (min_height % 2) == 0 else min_height + 1
×
125
        # ffmpeg -i presentation.webm -i presenter.webm \
126
        # -c:v libx264 -filter_complex "[0:v]scale=-2:720[left];[left][1:v]hstack" \
127
        # outputVideo.mp4
128
        subcmd = (
×
129
            " -filter_complex "
130
            + '"[0:v]scale=-2:%(height)s[left];[1:v]scale=-2:%(height)s[right];'
131
            % {"height": height}
132
            + '[left][right]hstack" -vsync 0 '
133
        )
134

135
    return subcmd
×
136

137

138
def get_height(info):
1✔
139
    """Get the height value from the given video information dictionary."""
140
    in_height = 0
×
141
    if len(info["streams"]) > 0 and info["streams"][0].get("height"):
×
142
        in_height = info["streams"][0]["height"]
×
143
    return in_height
×
144

145

146
def launch_encode_video_studio(input_video, subtime, subcmd, video_output):
1✔
147
    """Encode video for studio."""
148
    msg = ""
×
149
    partial_command = FFMPEG_STUDIO_COMMAND % {
×
150
        "nb_threads": FFMPEG_NB_THREADS,
151
        "input": input_video,
152
        "subtime": subtime,
153
        "crf": FFMPEG_CRF,
154
    }
155
    ffmpegStudioCommand = "%s %s %s %s" % (
×
156
        FFMPEG_CMD,
157
        partial_command,
158
        subcmd,
159
        video_output,
160
    )
161
    msg += "- %s\n" % ffmpegStudioCommand
×
162
    logfile = video_output.replace(".mp4", ".log")
×
163
    ffmpegstudio = subprocess.run(
×
164
        ffmpegStudioCommand,
165
        shell=True,
166
        stdout=subprocess.PIPE,
167
        stderr=subprocess.STDOUT,
168
    )
169
    with open(logfile, "ab") as f:
×
170
        f.write(b"\n\ffmpegstudio:\n\n")
×
171
        f.write(ffmpegstudio.stdout)
×
172
    msg += "\n- Encoding Mp4: %s" % time.ctime()
×
173
    return msg
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc