Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DONE] - FFMPEG use GPU #1168

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 100 additions & 35 deletions pod/video_encode_transcript/Encoding_video.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@
FFMPEG_DRESSING_SCALE,
FFMPEG_DRESSING_CONCAT,
)
from encoding_gpu_settings import (
FFMPEG_USE_GPU,
FFMPEG_CMD_GPU,
FFMPEG_PRESET_GPU,
FFMPEG_LEVEL_GPU,
FFMPEG_INPUT_GPU,
FFMPEG_LIBX_GPU,
FFMPEG_MP4_ENCODE_GPU,
FFMPEG_HLS_COMMON_PARAMS_GPU,
FFMPEG_HLS_ENCODE_PARAMS_GPU,
FFMPEG_CREATE_THUMBNAIL_GPU
)
else:
from .encoding_utils import (
get_dressing_position_value,
Expand Down Expand Up @@ -83,6 +95,18 @@
FFMPEG_DRESSING_SCALE,
FFMPEG_DRESSING_CONCAT,
)
from .encoding_gpu_settings import (
FFMPEG_USE_GPU,
FFMPEG_CMD_GPU,
FFMPEG_PRESET_GPU,
FFMPEG_LEVEL_GPU,
FFMPEG_INPUT_GPU,
FFMPEG_LIBX_GPU,
FFMPEG_MP4_ENCODE_GPU,
FFMPEG_HLS_COMMON_PARAMS_GPU,
FFMPEG_HLS_ENCODE_PARAMS_GPU,
FFMPEG_CREATE_THUMBNAIL_GPU
)


__author__ = "Nicolas CAN <[email protected]>"
Expand All @@ -100,23 +124,36 @@
try:
from django.conf import settings

FFMPEG_USE_GPU = getattr(settings, "FFMPEG_USE_GPU", FFMPEG_USE_GPU)
FFMPEG_CMD = getattr(settings, "FFMPEG_CMD", FFMPEG_CMD)
FFMPEG_CMD_GPU = getattr(settings, "FFMPEG_CMD_GPU", FFMPEG_CMD_GPU)
FFPROBE_CMD = getattr(settings, "FFPROBE_CMD", FFPROBE_CMD)
FFPROBE_GET_INFO = getattr(settings, "FFPROBE_GET_INFO", FFPROBE_GET_INFO)
FFMPEG_CRF = getattr(settings, "FFMPEG_CRF", FFMPEG_CRF)
FFMPEG_PRESET = getattr(settings, "FFMPEG_PRESET", FFMPEG_PRESET)
FFMPEG_PRESET_GPU = getattr(settings, "FFMPEG_PRESET_GPU", FFMPEG_PRESET_GPU)
FFMPEG_PROFILE = getattr(settings, "FFMPEG_PROFILE", FFMPEG_PROFILE)
FFMPEG_LEVEL = getattr(settings, "FFMPEG_LEVEL", FFMPEG_LEVEL)
FFMPEG_LEVEL_GPU = getattr(settings, "FFMPEG_LEVEL_GPU", FFMPEG_LEVEL_GPU)
FFMPEG_HLS_TIME = getattr(settings, "FFMPEG_HLS_TIME", FFMPEG_HLS_TIME)
FFMPEG_INPUT = getattr(settings, "FFMPEG_INPUT", FFMPEG_INPUT)
FFMPEG_INPUT_GPU = getattr(settings, "FFMPEG_INPUT_GPU", FFMPEG_INPUT_GPU)
FFMPEG_LIBX = getattr(settings, "FFMPEG_LIBX", FFMPEG_LIBX)
FFMPEG_LIBX_GPU = getattr(settings, "FFMPEG_LIBX_GPU", FFMPEG_LIBX_GPU)
FFMPEG_MP4_ENCODE = getattr(settings, "FFMPEG_MP4_ENCODE", FFMPEG_MP4_ENCODE)
FFMPEG_MP4_ENCODE_GPU = getattr(settings, "FFMPEG_MP4_ENCODE_GPU", FFMPEG_MP4_ENCODE_GPU)
FFMPEG_HLS_COMMON_PARAMS = getattr(
settings, "FFMPEG_HLS_COMMON_PARAMS", FFMPEG_HLS_COMMON_PARAMS
)
FFMPEG_HLS_COMMON_PARAMS_GPU = getattr(
settings, "FFMPEG_HLS_COMMON_PARAMS_GPU", FFMPEG_HLS_COMMON_PARAMS_GPU
)
FFMPEG_HLS_ENCODE_PARAMS = getattr(
settings, "FFMPEG_HLS_ENCODE_PARAMS", FFMPEG_HLS_ENCODE_PARAMS
)
FFMPEG_HLS_ENCODE_PARAMS_GPU = getattr(
settings, "FFMPEG_HLS_ENCODE_PARAMS_GPU", FFMPEG_HLS_ENCODE_PARAMS_GPU
)
FFMPEG_MP3_ENCODE = getattr(settings, "FFMPEG_MP3_ENCODE", FFMPEG_MP3_ENCODE)
FFMPEG_M4A_ENCODE = getattr(settings, "FFMPEG_M4A_ENCODE", FFMPEG_M4A_ENCODE)
FFMPEG_NB_THREADS = getattr(settings, "FFMPEG_NB_THREADS", FFMPEG_NB_THREADS)
Expand All @@ -128,6 +165,9 @@
FFMPEG_CREATE_THUMBNAIL = getattr(
settings, "FFMPEG_CREATE_THUMBNAIL", FFMPEG_CREATE_THUMBNAIL
)
FFMPEG_CREATE_THUMBNAIL_GPU = getattr(
settings, "FFMPEG_CREATE_THUMBNAIL_GPU", FFMPEG_CREATE_THUMBNAIL_GPU
)
FFMPEG_EXTRACT_SUBTITLE = getattr(
settings, "FFMPEG_EXTRACT_SUBTITLE", FFMPEG_EXTRACT_SUBTITLE
)
Expand Down Expand Up @@ -304,8 +344,8 @@ def create_output_dir(self):
os.makedirs(output_dir)
self.output_dir = output_dir

def get_mp4_command(self) -> str:
mp4_command = "%s " % FFMPEG_CMD
def get_mp4_command(self, use_gpu: bool = False) -> str:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Il manque la documentation

mp4_command = "%s " % (FFMPEG_CMD_GPU if use_gpu else FFMPEG_CMD)
list_rendition = get_list_rendition()
# remove rendition if encode_mp4 == False
for rend in list_rendition.copy():
Expand All @@ -319,14 +359,14 @@ def get_mp4_command(self) -> str:
"nb_threads": FFMPEG_NB_THREADS,
}
output_file = os.path.join(self.output_dir, "%sp.mp4" % first_item[0])
mp4_command += FFMPEG_MP4_ENCODE % {
mp4_command += (FFMPEG_MP4_ENCODE_GPU if use_gpu else FFMPEG_MP4_ENCODE) % {
"cut": self.get_subtime(self.cutting_start, self.cutting_stop),
"map_audio": "-map 0:a:0" if len(self.list_audio_track) > 0 else "",
"libx": FFMPEG_LIBX,
"libx": (FFMPEG_LIBX_GPU if use_gpu else FFMPEG_LIBX),
"height": first_item[0],
"preset": FFMPEG_PRESET,
"preset": (FFMPEG_PRESET_GPU if use_gpu else FFMPEG_PRESET),
"profile": FFMPEG_PROFILE,
"level": FFMPEG_LEVEL,
"level": (FFMPEG_LEVEL_GPU if use_gpu else FFMPEG_LEVEL),
"crf": FFMPEG_CRF,
"maxrate": first_item[1]["maxrate"],
"bufsize": first_item[1]["maxrate"],
Expand All @@ -348,14 +388,14 @@ def get_mp4_command(self) -> str:
)
if in_height >= resolution_threshold:
output_file = os.path.join(self.output_dir, "%sp.mp4" % rend)
mp4_command += FFMPEG_MP4_ENCODE % {
mp4_command += (FFMPEG_MP4_ENCODE_GPU if use_gpu else FFMPEG_MP4_ENCODE) % {
"cut": self.get_subtime(self.cutting_start, self.cutting_stop),
"map_audio": "-map 0:a:0" if len(self.list_audio_track) > 0 else "",
"libx": FFMPEG_LIBX,
"libx": (FFMPEG_LIB_GPU if use_gpu else FFMPEG_LIBX),
"height": min(rend, in_height),
"preset": FFMPEG_PRESET,
"preset": (FFMPEG_PRESET_GPU if use_gpu else FFMPEG_PRESET),
"profile": FFMPEG_PROFILE,
"level": FFMPEG_LEVEL,
"level": (FFMPEG_LEVEL_GPU if use_gpu else FFMPEG_LEVEL),
"crf": FFMPEG_CRF,
"maxrate": list_rendition[rend]["maxrate"],
"bufsize": list_rendition[rend]["maxrate"],
Expand All @@ -365,19 +405,19 @@ def get_mp4_command(self) -> str:
self.list_mp4_files[rend] = output_file
return mp4_command

def get_hls_command(self) -> str:
hls_command = "%s " % FFMPEG_CMD
def get_hls_command(self, use_gpu: bool = False) -> str:
hls_command = "%s " % (FFMPEG_CMD_GPU if use_gpu else FFMPEG_CMD)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Il manque également la documentation

list_rendition = get_list_rendition()
hls_command += FFMPEG_INPUT % {
"input": self.video_file,
"nb_threads": FFMPEG_NB_THREADS,
}
hls_common_params = FFMPEG_HLS_COMMON_PARAMS % {
hls_common_params = (FFMPEG_HLS_COMMON_PARAMS_GPU if use_gpu else FFMPEG_HLS_COMMON_PARAMS) % {
"cut": self.get_subtime(self.cutting_start, self.cutting_stop),
"libx": FFMPEG_LIBX,
"preset": FFMPEG_PRESET,
"libx": (FFMPEG_LIBX_GPU if use_gpu else FFMPEG_LIBX),
"preset": (FFMPEG_PRESET_GPU if use_gpu else FFMPEG_PRESET),
"profile": FFMPEG_PROFILE,
"level": FFMPEG_LEVEL,
"level": (FFMPEG_LEVEL_GPU if use_gpu else FFMPEG_LEVEL),
"crf": FFMPEG_CRF,
}
hls_command += hls_common_params
Expand All @@ -389,7 +429,7 @@ def get_hls_command(self) -> str:
if in_height >= resolution_threshold or index == 0:
output_file = os.path.join(self.output_dir, "%sp.m3u8" % rend)
hls_command += hls_common_params
hls_command += FFMPEG_HLS_ENCODE_PARAMS % {
hls_command += (FFMPEG_HLS_ENCODE_PARAMS_GPU if use_gpu else FFMPEG_HLS_ENCODE_PARAMS) % {
"height": min(rend, in_height),
"maxrate": list_rendition[rend]["maxrate"],
"bufsize": list_rendition[rend]["maxrate"],
Expand Down Expand Up @@ -511,20 +551,38 @@ def encode_video_dressing(self):

def encode_video_part(self):
"""Encode the video part of a file."""
mp4_command = self.get_mp4_command()
return_value, return_msg = launch_cmd(mp4_command)
self.add_encoding_log("mp4_command", mp4_command, return_value, return_msg)
if not return_value:
self.error_encoding = True
def _run_mp4_command(use_gpu: bool = False) -> bool:
mp4_command = self.get_mp4_command(use_gpu=use_gpu)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

manque une petite Pydoc à chaque nouvelle fonction, au format
"""Describe briefly the function."""
(Majuscule au début et point à la fin)

return_value, return_msg = launch_cmd(mp4_command)
self.add_encoding_log(f"mp4_command{'_gpu' if use_gpu else ''}", mp4_command, return_value, return_msg)
return return_value
return_value = _run_mp4_command(use_gpu=FFMPEG_USE_GPU)
if FFMPEG_USE_GPU:
if not return_value:
if not _run_mp4_command(use_gpu=False):
self.error_encoding = True
else:
if not return_value:
self.error_encoding = True

if self.duration == 0:
list_rendition = get_list_rendition()
first_item = list_rendition.popitem(last=False)
self.fix_duration(self.list_mp4_files[first_item[0]])
hls_command = self.get_hls_command()
return_value, return_msg = launch_cmd(hls_command)
if return_value:
self.create_main_livestream()
self.add_encoding_log("hls_command", hls_command, return_value, return_msg)

def _run_hls_command(use_gpu: bool = False) -> bool:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation manquante

hls_command = self.get_hls_command(use_gpu=use_gpu)
return_value, return_msg = launch_cmd(hls_command)
self.add_encoding_log(f"hls_command{'_gpu' if use_gpu else ''}", hls_command, return_value, return_msg)
return return_value
return_value = _run_hls_command(use_gpu=FFMPEG_USE_GPU)
if FFMPEG_USE_GPU:
if not return_value:
if _run_hls_command(use_gpu=False):
self.create_main_livestream()
else:
if return_value:
self.create_main_livestream()

def create_main_livestream(self):
list_rendition = get_list_rendition()
Expand Down Expand Up @@ -605,16 +663,16 @@ def get_extract_thumbnail_command(self) -> str:
self.list_thumbnail_files[img] = output_file
return thumbnail_command

def get_create_thumbnail_command(self) -> str:
thumbnail_command = "%s " % FFMPEG_CMD
def get_create_thumbnail_command(self, use_gpu: bool = False) -> str:
thumbnail_command = "%s " % (FFMPEG_CMD_GPU if use_gpu else FFMPEG_CMD)
first_item = self.get_first_item()
input_file = self.list_mp4_files[first_item[0]]
thumbnail_command += FFMPEG_INPUT % {
"input": input_file,
"nb_threads": FFMPEG_NB_THREADS,
}
output_file = os.path.join(self.output_dir, "thumbnail")
thumbnail_command += FFMPEG_CREATE_THUMBNAIL % {
thumbnail_command += (FFMPEG_CREATE_THUMBNAIL_GPU if use_gpu else FFMPEG_CREATE_THUMBNAIL) % {
"duration": self.duration,
"nb_thumbnail": FFMPEG_NB_THUMBNAIL,
"output": output_file,
Expand Down Expand Up @@ -707,11 +765,18 @@ def encode_image_part(self):
"extract_thumbnail_command", thumbnail_command, return_value, return_msg
)
elif self.is_video():
thumbnail_command = self.get_create_thumbnail_command()
return_value, return_msg = launch_cmd(thumbnail_command)
self.add_encoding_log(
"create_thumbnail_command", thumbnail_command, return_value, return_msg
)
def _run_thumbnail_command(use_gpu: bool = False) -> bool:
thumbnail_command = self.get_create_thumbnail_command(use_gpu=use_gpu)
return_value, return_msg = launch_cmd(thumbnail_command)
self.add_encoding_log(
f"create_thumbnail_command{'_gpu' if use_gpu else ''}", thumbnail_command, return_value, return_msg
)
return return_value
return_value = _run_thumbnail_command(use_gpu=FFMPEG_USE_GPU)
if FFMPEG_USE_GPU:
if not return_value:
_run_thumbnail_command(use_gpu=False)

# on ne fait pas d'overview pour les videos de moins de 10 secondes
# (laisser les 10sec inclus pour laisser les tests passer) --> OK
if self.is_video() and self.duration >= 10:
Expand Down
40 changes: 40 additions & 0 deletions pod/video_encode_transcript/encoding_gpu_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# manage setting to encode video with gpu
FFMPEG_USE_GPU = False
FFMPEG_CMD_GPU = "ffmpeg -hwaccel_device 0 -hwaccel_output_format cuda -hwaccel cuda"
FFMPEG_PRESET_GPU = "p6"
FFMPEG_LEVEL_GPU = 0

FFMPEG_INPUT_GPU = '-hide_banner -threads %(nb_threads)s -i "%(input)s" '

FFMPEG_LIBX_GPU = "h264_nvenc"
FFMPEG_MP4_ENCODE_GPU = (
'%(cut)s -map 0:v:0 %(map_audio)s -c:v %(libx)s -vf "scale_cuda=-2:%(height)s:interp_algo=bicubic:format=yuv420p" '
+ "-preset %(preset)s -profile:v %(profile)s "
+ "-level %(level)s "
+ "-forced-idr 1 "
+ "-b:v %(maxrate)s -maxrate %(maxrate)s -bufsize %(bufsize)s -rc vbr -rc-lookahead 20 -bf 1 "
+ '-force_key_frames "expr:gte(t,n_forced*1)" '
+ '-c:a aac -ar 48000 -b:a %(ba)s -movflags faststart -y -fps_mode passthrough "%(output)s" '
)
FFMPEG_HLS_COMMON_PARAMS_GPU = (
"%(cut)s "
+ "-c:v %(libx)s -preset %(preset)s -profile:v %(profile)s "
+ "-level %(level)s "
+ "-forced-idr 1 "
+ '-force_key_frames "expr:gte(t,n_forced*1)" '
+ "-c:a aac -ar 48000 "
)
FFMPEG_HLS_ENCODE_PARAMS_GPU = (
'-vf "scale_cuda=-2:%(height)s:interp_algo=bicubic:format=yuv420p" -b:v %(maxrate)s -maxrate %(maxrate)s -bufsize %(bufsize)s -b:a:0 %(ba)s -rc vbr -rc-lookahead 20 -bf 1 '
+ "-hls_playlist_type vod -hls_time %(hls_time)s -hls_flags single_file "
+ '-master_pl_name "livestream%(height)s.m3u8" '
+ '-y "%(output)s" '
)

FFMPEG_CREATE_THUMBNAIL_GPU = (
'-vf "select=between(t\,0\,%(duration)s)*eq(pict_type\,PICT_TYPE_I),thumbnail_cuda=2,scale_cuda=-2:720:interp_algo=bicubic:format=yuv420p,hwdownload,format=yuv420p" -frames:v %(nb_thumbnail)s -vsync vfr "%(output)s_%%04d.png"'
)

#FFMPEG_CREATE_OVERVIEW_GPU = (
# " -vsync vfr -vf fps=(%(image_count)s/%(duration)s),scale_cuda=%(width)s:%(height)s:interp_algo=bicubic:format=yuv420p,hwdownload,tile=%(image_count)sx1,format=yuv420p -frames:v 1 '%(output)s' "
#)
Loading