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

feat: Expand binary platforms and hardware encoding #161

Merged
merged 4 commits into from
Oct 22, 2024
Merged
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
74 changes: 52 additions & 22 deletions binaries/build_wheels.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,33 +26,43 @@

# Version constants.
# Change to download different versions.
FFMPEG_VERSION = 'n4.4-2'
PACKAGER_VERSION = 'v2.6.1'
FFMPEG_VERSION = 'n7.1-1'
PACKAGER_VERSION = 'v3.2.0'

# A map of suffixes that will be combined with the binary download links
# to achieve a full download link. Different suffix for each platform.
# Extend this dictionary to add more platforms.
PLATFORM_SUFFIXES = {
# 64-bit Windows
'win_amd64': '-win-x64.exe',
# 64-bit Linux
'manylinux1_x86_64': '-linux-x64',
# Linux on ARM
# Linux x64
'manylinux2014_x86_64': '-linux-x64',
# Linux arm64
'manylinux2014_aarch64': '-linux-arm64',
# 64-bit with 10.9 SDK
# macOS x64 with 10.9 SDK
'macosx_10_9_x86_64': '-osx-x64',
# macOS arm64 with 10.9 SDK
'macosx_10_9_arm64': '-osx-arm64',
# Windows x64
'win_amd64': '-win-x64.exe',
}

FFMPEG_DL_PREFIX = 'https://github.com/shaka-project/static-ffmpeg-binaries/releases/download/' + FFMPEG_VERSION
PACKAGER_DL_PREFIX = 'https://github.com/shaka-project/shaka-packager/releases/download/' + PACKAGER_VERSION

# The download links to each binary. These download links
# aren't complete, they miss the platfrom-specific suffix.
BINARIES_DL = [
# The download links to each binary. These download links aren't complete.
# They are missing the platfrom-specific suffix and optional distro-specific
# suffix (Linux only).
FFMPEG_BINARIES_DL = [
FFMPEG_DL_PREFIX + '/ffmpeg',
FFMPEG_DL_PREFIX + '/ffprobe',
]
PACKAGER_BINARIES_DL = [
PACKAGER_DL_PREFIX + '/packager',
]
# Important: wrap map() in list(), because map returns an iterator, and we need
# a real list.
UBUNTU_SUFFIXES = list(map(
lambda version: '-ubuntu-{}'.format(version),
mariocynicys marked this conversation as resolved.
Show resolved Hide resolved
streamer_binaries._ubuntu_versions_with_hw_encoders))

BINARIES_ROOT_DIR = os.path.abspath(os.path.dirname(__file__))

Expand Down Expand Up @@ -88,34 +98,54 @@ def download_binary(download_url: str, download_dir: str) -> str:
"""Downloads a file and writes it to the file system.
Returns the file name.
"""

binary_name = download_url.split('/')[-1]
binary_path = os.path.join(download_dir, binary_name)

print('downloading', binary_name, flush=True, end=' ')
urllib.request.urlretrieve(download_url, binary_path)
print('(finished)')

# Set executable permissions for the downloaded binaries.
default_permissions = 0o755
os.chmod(binary_path, default_permissions)
executable_permissions = 0o755
os.chmod(binary_path, executable_permissions)

return binary_name


def main():
# For each platform(OS+CPU), we download the its binaries and
# create a binary wheel distribution that contains the executable
# binaries specific to this platform.
# For each platform(OS+CPU), we download the its binaries and create a binary
# wheel distribution that contains the executable binaries specific to this
# platform.
download_dir = os.path.join(BINARIES_ROOT_DIR, streamer_binaries.__name__)

for platform_name, suffix in PLATFORM_SUFFIXES.items():
binaries_to_include = []
# Use the `suffix` specific to this platfrom to achieve
# the full download link for each binary.
for binary_dl in BINARIES_DL:

# Use the suffix specific to this platfrom to construct the full download
# link for each binary.
for binary_dl in PACKAGER_BINARIES_DL:
download_link = binary_dl + suffix
binary_name = download_binary(download_url=download_link,
download_dir=download_dir)
binaries_to_include.append(binary_name)

# FFmpeg binaries are like packager binaries, except we have extra variants
# for Ubuntu Linux to support hardware encoding.
for binary_dl in FFMPEG_BINARIES_DL:
download_link = binary_dl + suffix
binary_name = download_binary(download_url=download_link,
download_dir=download_dir)
binaries_to_include.append(binary_name)
# Build a wheel distribution for this platform
# and include the binaries we have just downloaded.

if 'linux' in suffix:
for ubuntu_suffix in UBUNTU_SUFFIXES:
download_link = binary_dl + suffix + ubuntu_suffix
binary_name = download_binary(download_url=download_link,
download_dir=download_dir)
binaries_to_include.append(binary_name)

# Build a wheel distribution for this platform and include the binaries we
# have just downloaded.
build_bdist_wheel(platform_name, binaries_to_include)


Expand Down
6 changes: 5 additions & 1 deletion binaries/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,9 @@
package_data={
# Only add the corresponding platform specific binaries to the wheel.
streamer_binaries.__name__: platform_binaries,
}
},
install_requires=[
# This is only used for Linux, and only supports Linux.
'distro;platform_system=="Linux"',
],
)
17 changes: 16 additions & 1 deletion binaries/streamer_binaries/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import distro
import os
import platform

__version__ = '0.5.2'
__version__ = '0.6.0'


# Get the directory path where this __init__.py file resides.
Expand All @@ -21,6 +22,12 @@
'aarch64': 'arm64',
}[platform.machine()]

# Specific versions of Ubuntu with special builds for hardware-encoding.
_ubuntu_versions_with_hw_encoders = (
'22.04',
'24.04',
)

# Module level variables.
ffmpeg = os.path.join(_dir_path, 'ffmpeg-{}-{}'.format(_os, _cpu))
"""The path to the installed FFmpeg binary."""
Expand All @@ -31,3 +38,11 @@
packager = os.path.join(_dir_path, 'packager-{}-{}'.format(_os, _cpu))
"""The path to the installed Shaka Packager binary."""

# Special overrides for Ubuntu builds with hardware encoding support.
# These are not static binaries, and so they must be matched to the distro.
if _os == 'linux':
if distro.id() == 'ubuntu':
if distro.version() in _ubuntu_versions_with_hw_encoders:
suffix = '-ubuntu-' + distro.version()
ffmpeg += suffix
ffprobe += suffix
2 changes: 1 addition & 1 deletion streamer/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__version__ = '0.5.1'
__version__ = '0.6.0'

from . import controller_node
18 changes: 10 additions & 8 deletions streamer/controller_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@

from typing import Any, Dict, List, Optional, Tuple, Union
from streamer import __version__
from streamer import autodetect
from streamer import min_versions
from streamer.cloud_node import CloudNode
from streamer.bitrate_configuration import BitrateConfig, AudioChannelLayout, VideoResolution
from streamer.external_command_node import ExternalCommandNode
from streamer import autodetect
from streamer.input_configuration import InputConfig, InputType, MediaType, Input
from streamer.node_base import NodeBase, ProcessStatus
from streamer.output_stream import AudioOutputStream, OutputStream, TextOutputStream, VideoOutputStream
Expand Down Expand Up @@ -134,16 +135,17 @@ def next_short_version(version: str) -> str:
exact_match=True,
addendum='Install with: {}'.format(pip_command))
else:
# Check that ffmpeg version is 4.1 or above.
_check_command_version('FFmpeg', ['ffmpeg', '-version'], (4, 1))
# Check the ffmpeg version.
_check_command_version('FFmpeg', ['ffmpeg', '-version'],
min_versions.FFMPEG)

# Check that ffprobe version (used for autodetect features) is 4.1 or
# above.
_check_command_version('ffprobe', ['ffprobe', '-version'], (4, 1))
# Check the ffprobe version (used for autodetect features).
_check_command_version('ffprobe', ['ffprobe', '-version'],
min_versions.FFMPEG)

# Check that Shaka Packager version is 2.6.0 or above.
# Check the Shaka Packager version.
_check_command_version('Shaka Packager', ['packager', '-version'],
(2, 6, 1))
min_versions.PACKAGER)

if bucket_url:
# Check that the Google Cloud SDK is at least v212, which introduced
Expand Down
19 changes: 19 additions & 0 deletions streamer/min_versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Minimum versions of tools we depend on."""

# These are minimum semantic versions expressed as tuples of ints.
FFMPEG = (7, 1)
PACKAGER = (3, 2, 0)
10 changes: 1 addition & 9 deletions streamer/transcoder_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,16 +292,8 @@ def _encode_video(self, stream: VideoOutputStream, input: Input) -> List[str]:
'-cpu-used', '8',
# According to the wiki (https://trac.ffmpeg.org/wiki/Encode/AV1),
# this allows threaded encoding in AV1, which makes better use of CPU
# resources and speeds up encoding. This will be ignored by libaom
# before version 1.0.0-759-g90a15f4f2, and so there may be no benefit
# unless libaom and ffmpeg are built from source (as of Oct 2019).
# resources and speeds up encoding.
'-row-mt', '1',
# According to the wiki (https://trac.ffmpeg.org/wiki/Encode/AV1),
# this allows for threaded _decoding_ in AV1, which will provide a
# smoother playback experience for the end user.
'-tiles', '2x2',
# AV1 is considered "experimental".
'-strict', 'experimental',
]

keyframe_interval = int(self._pipeline_config.segment_size *
Expand Down