Streaming leicht gemacht: Videos mit Ruby in HLS und MPEG-DASH umwandeln
Die effiziente Auslieferung von Videoinhalten erfordert adaptive Streaming-Formate wie HLS (HTTP Live Streaming) und MPEG-DASH. In diesem Leitfaden zeigen wir, wie Sie Videos mit Ruby, FFmpeg und dem Open-Source-Gem Streamio FFMPEG in diese Formate umwandeln. Indem Sie Aufgaben der Videokonvertierung automatisieren, steigern Sie die Streaming-Qualität auf unterschiedlichen Geräten und bei verschiedenen Netzwerkbedingungen.
Einführung in HLS und MPEG-DASH
HLS ist ein von Apple entwickeltes Streaming-Protokoll, das Videoinhalte in kleine, HTTP-basierte Dateien segmentiert und so adaptives Bitraten-Streaming ermöglicht. MPEG-DASH (Dynamic Adaptive Streaming over HTTP) ist ein ähnlicher Standard, der die Videoqualität dynamisch an die Netzwerkbedingungen der Nutzerinnen und Nutzer anpasst.
Die Implementierung dieser Formate verbessert das Erlebnis der Zuschauer, da sie Pufferzeiten reduziert und die Videoqualität optimiert. Reibungsloses Streaming auf verschiedenen Geräten und in unterschiedlichen Netzwerken sicherzustellen, ist für moderne Anwendungen unverzichtbar.
Ruby-Umgebung für die Videokonvertierung einrichten
Verwenden Sie Ruby 3.2 oder neuer, FFmpeg mit den Encodern libx264 und aac sowie streamio-ffmpeg 3.0.2.
Die folgenden Beispiele verwenden eine 16:9-Eingabe mit Video und Audio.
Ruby 4 hat logger von einem Standard-Gem zu einem gebündelten Gem gemacht.
Führen Sie es explizit in Ihrem Gemfile auf, damit streamio-ffmpeg es beim Ausführen unter
Bundler einbinden kann.
Überprüfen Sie Ihre Ruby-Installation:
ruby -v
Wenn Sie Ruby installieren oder aktualisieren müssen, verwenden Sie einen Versionsmanager wie RVM oder rbenv.
Installieren Sie als Nächstes FFmpeg, das für die Verarbeitung von Videodateien unverzichtbar ist.
Für Nutzer von macOS:
brew install ffmpeg
Für Nutzer von Ubuntu/Debian:
sudo apt-get update
sudo apt-get install ffmpeg
Erstellen Sie ein neues Projektverzeichnis und initialisieren Sie es:
mkdir video_conversion
cd video_conversion
bundle init
Fügen Sie das erforderliche Gem zu Ihrer Datei Gemfile hinzu:
source 'https://rubygems.org'
gem 'streamio-ffmpeg', '~> 3.0.2'
gem 'logger'
Alternativ installieren Sie das Gem direkt über die Kommandozeile:
gem install streamio-ffmpeg -v 3.0.2
gem install logger
Installieren Sie die Abhängigkeiten:
bundle install
Einführung in FFmpeg und das Streamio FFmpeg Gem
FFmpeg ist ein robustes Open-Source-Werkzeug für die Multimedia-Bearbeitung, mit dem Sie Audio- und Videodateien aufnehmen, konvertieren und streamen können. Das Streamio FFMPEG Gem bietet eine Ruby-Schnittstelle zu FFmpeg und vereinfacht die Integration der Videoverarbeitung in Ihre Anwendungen.
Videos in Ruby in das HLS-Format umwandeln
Nachfolgend finden Sie eine Ruby-Klasse, die die Videokonvertierung nach HLS und DASH übernimmt. Sie nutzt das Gem für die Prüfung der Eingabe und die Encoder-Optionen, führt anschließend FFmpeg mit einem Argument-Array aus und prüft dessen Exit-Status. Jede Konvertierung legt ein neues Ausgabeverzeichnis an; ein bereits vorhandenes Verzeichnis wird abgelehnt, um frühere Playlists und Segmente zu schützen.
Erstellen Sie eine neue Datei namens video_converter.rb:
require 'streamio-ffmpeg'
require 'fileutils'
class VideoConverter
def initialize(input_file)
begin
@movie = FFMPEG::Movie.new(input_file)
raise "Invalid file" unless @movie.valid?
@input_file = input_file
rescue FFMPEG::Error => e
raise "FFmpeg error: #{e.message}"
rescue StandardError => e
raise "Error initializing converter: #{e.message}"
end
end
def to_hls(output_dir)
FileUtils.mkdir_p(File.dirname(output_dir))
Dir.mkdir(output_dir)
variants = [
{ resolution: '1280x720', video_bitrate: '2500k', audio_bitrate: '128k' },
{ resolution: '854x480', video_bitrate: '1500k', audio_bitrate: '96k' },
{ resolution: '640x360', video_bitrate: '800k', audio_bitrate: '64k' }
]
begin
variants.each do |variant|
options = {
video_codec: 'libx264',
audio_codec: 'aac',
frame_rate: 30,
resolution: variant[:resolution],
video_bitrate: variant[:video_bitrate],
audio_bitrate: variant[:audio_bitrate],
custom: [
'-map', '0:v:0', '-map', '0:a:0',
'-g', '300', '-keyint_min', '300', '-sc_threshold', '0',
'-force_key_frames', 'expr:gte(t,n_forced*10)',
'-hls_time', '10',
'-hls_playlist_type', 'vod',
'-hls_segment_filename', "#{output_dir}/#{variant[:resolution]}_%03d.ts"
]
}
transcode("#{output_dir}/#{variant[:resolution]}.m3u8", options)
end
generate_master_playlist(output_dir, variants)
rescue FFMPEG::Error => e
raise "Transcoding error: #{e.message}"
rescue StandardError => e
raise "General error: #{e.message}"
end
end
def to_dash(output_dir)
FileUtils.mkdir_p(File.dirname(output_dir))
Dir.mkdir(output_dir)
begin
options = {
video_codec: 'libx264',
audio_codec: 'aac',
frame_rate: 30,
custom: [
'-map', '0:v:0', '-map', '0:v:0', '-map', '0:a:0',
'-s:v:0', '1280x720', '-b:v:0', '2500k',
'-s:v:1', '640x360', '-b:v:1', '800k',
'-g', '300', '-keyint_min', '300', '-sc_threshold', '0',
'-force_key_frames', 'expr:gte(t,n_forced*10)',
'-use_template', '1',
'-use_timeline', '1',
'-seg_duration', '10',
'-adaptation_sets', 'id=0,streams=v id=1,streams=a',
'-f', 'dash'
]
}
transcode("#{output_dir}/manifest.mpd", options)
rescue FFMPEG::Error => e
raise "DASH conversion error: #{e.message}"
rescue StandardError => e
raise "General error: #{e.message}"
end
end
private
def transcode(output_file, options)
arguments = FFMPEG::EncodingOptions.new(options).to_a
system(FFMPEG.ffmpeg_binary, '-nostdin', '-n', '-i', @input_file,
*arguments, output_file, exception: true)
end
def generate_master_playlist(output_dir, variants)
master_playlist = "#EXTM3U\n#EXT-X-VERSION:3\n"
variants.each do |variant|
# Measure the multiplexed segments, including audio and container overhead.
duration = nil
rates = []
File.foreach("#{output_dir}/#{variant[:resolution]}.m3u8") do |line|
if line.start_with?('#EXTINF:')
duration = Float(line.delete_prefix('#EXTINF:').split(',').first)
elsif !line.start_with?('#') && !line.strip.empty?
raise 'Missing segment duration' unless duration && duration.positive?
rates << File.size(File.join(output_dir, line.strip)) * 8 / duration
duration = nil
end
end
raise 'No HLS segments generated' if rates.empty?
bandwidth = rates.max.ceil
master_playlist += <<~PLAYLIST
#EXT-X-STREAM-INF:BANDWIDTH=#{bandwidth},RESOLUTION=#{variant[:resolution]}
#{variant[:resolution]}.m3u8
PLAYLIST
end
File.write("#{output_dir}/master.m3u8", master_playlist)
end
end
Videos in Ruby in MPEG-DASH umwandeln
Die oben gezeigte Methode to_dash mappt das Eingabevideo zweimal und erzeugt so Repräsentationen in
720p und 360p sowie eine gemeinsam genutzte Audio-Repräsentation. Beide Video-Encodings verwenden
dieselbe Bildrate und erzwingen alle zehn Sekunden Keyframes, sodass die Segmentgrenzen
übereinstimmen. HLS nutzt denselben Keyframe-Rhythmus für seine drei Varianten.
Den Konverter verwenden
Erstellen Sie ein Skript, das die Klasse VideoConverter nutzt. Dieses Beispiel zeigt, wie Sie eine
Videodatei sowohl in das HLS- als auch in das MPEG-DASH-Format umwandeln:
# Convert.rb
require_relative 'video_converter'
begin
converter = VideoConverter.new('input.mp4')
puts 'Converting to HLS...'
converter.to_hls('output/hls')
puts 'Converting to MPEG-DASH...'
converter.to_dash('output/dash')
puts 'Conversion completed successfully'
rescue StandardError => e
puts "Error: #{e.message}"
exit 1
end
Die Streams testen
Um die erzeugten Streams zu testen, speichern Sie die folgende Seite als player.html neben dem
Verzeichnis output und liefern Sie dieses Verzeichnis über HTTP aus. Video.js 8 enthält
HTTP-Streaming-Unterstützung für HLS und DASH. Die erste unterstützte Quelle wird ausgewählt;
entfernen Sie die HLS-Quelle vorübergehend, um DASH zu testen.
<!DOCTYPE html>
<html>
<head>
<title>Video Player</title>
<link href="https://vjs.zencdn.net/8.10.0/video-js.css" rel="stylesheet" />
<script src="https://vjs.zencdn.net/8.10.0/video.min.js"></script>
</head>
<body>
<video
id="player"
class="video-js vjs-default-skin"
controls
preload="auto"
width="640"
height="360"
>
<source src="output/hls/master.m3u8" type="application/x-mpegURL" />
<source src="output/dash/manifest.mpd" type="application/dash+xml" />
</video>
<script>
var player = videojs('player')
</script>
</body>
</html>
Fortgeschrittene Fehlerbehandlung
Geben Sie in Produktionsumgebungen Konvertierungsfehler an den aufrufenden Job weiter. Zum Beispiel:
begin
VideoConverter.new('input.mp4').to_hls('output/new-hls')
rescue StandardError => e
warn "Conversion failed: #{e.message}"
exit 1
end
Zusätzlich zur Fehlerprotokollierung sollten Sie die Systemressourcen überwachen und Retry-Mechanismen für fehlgeschlagene Konvertierungen implementieren. Ein fehlgeschlagener Durchlauf kann unvollständige Dateien in seinem neu angelegten Verzeichnis hinterlassen. Verwenden Sie bei einer Wiederholung ein frisches Verzeichnis und veröffentlichen Sie Playlists erst, nachdem die gesamte Konvertierung erfolgreich war.
Häufige Herausforderungen und Lösungen
Unterstützung von Containerformaten
Achten Sie darauf, dass Ihre Eingabevideos weit verbreitete Containerformate wie MP4 (H.264) verwenden, um optimale Kompatibilität zu erreichen. FFmpeg unterstützt zwar viele verschiedene Formate, doch H.264 in einem MP4-Container bietet die beste Streaming-Performance.
Performance-Optimierung
Für die Videoverarbeitung in großem Maßstab:
- Verarbeiten Sie Videos asynchron mithilfe von Background-Jobs.
- Nutzen Sie niedrigere Auflösungen für die Erzeugung von Vorschauen.
- Implementieren Sie Caching für häufig abgerufene Segmente.
- Überwachen Sie die Systemressourcen und passen Sie die Batch-Größen entsprechend an.
Robuste Fehlerbehandlung
Bauen Sie in Ihren Produktionscode eine umfassende Fehlerbehandlung ein. Validieren Sie Eingabedateien, überwachen Sie den Speicherplatz der Ausgabeverzeichnisse und protokollieren Sie detaillierte Fehlermeldungen, um das Debugging zu erleichtern.
Fazit
Die Automatisierung der Videokonvertierung mit Ruby, FFmpeg und dem Streamio FFMPEG Gem vereinfacht adaptives Streaming mit HLS und MPEG-DASH. Wenn Sie diese aktualisierten bewährten Verfahren befolgen und eine robuste Fehlerbehandlung einbauen, verbessern Sie die Video-Auslieferung auf unterschiedlichen Geräten und bei verschiedenen Netzwerkbedingungen.
Wenn Sie eine skalierbare und robuste Lösung für Video-Encoding und Streaming suchen, sollten Sie Transloadits Video-Encoding-Service in Betracht ziehen, der diese Konvertierungen vereinfacht und Ihren Workflow verbessert.
