Module: HLS::Testing
- Defined in:
- lib/hls/testing.rb
Overview
Public test helpers for verifying that an HLS profile actually produces a correct bundle when run against ffmpeg. Users of the gem can include this module in their own spec suites to write integration tests against their ‘app/videos/*.rb` profile classes:
require "hls/testing"
RSpec.describe CourseVideo do
include HLS::Testing
it "encodes a valid HLS bundle" do
video = generate_test_video(duration: 12)
output = Pathname.new(Dir.mktmpdir)
profile = CourseVideo.new(input: HLS::Input.new(video), output: output)
silence_ffmpeg do
profile.encode!
profile.poster!
end
expect(output).to be_a_valid_hls_bundle.with_variants(3)
end
end
All helpers shell out to ffmpeg/ffprobe (already a hard dep of the gem), so any environment that can run the gem can run these helpers.
Instance Method Summary collapse
-
#generate_test_video(path: nil, duration: 12, width: 640, height: 360, framerate: 30, frequency: 440) ⇒ Object
Generates a deterministic test video at ‘path` (or a tmpdir-backed path if not given).
-
#parse_playlist(path) ⇒ Object
Parses an m3u8 file and returns the M3u8::Playlist.
-
#probe(path) ⇒ Object
Probes a media file with ffprobe and returns its parsed metadata.
-
#probe_dimensions(path) ⇒ Object
Returns [width, height] for an image or video file.
-
#silence_ffmpeg ⇒ Object
Silences stdout/stderr inside the block.
Instance Method Details
#generate_test_video(path: nil, duration: 12, width: 640, height: 360, framerate: 30, frequency: 440) ⇒ Object
Generates a deterministic test video at ‘path` (or a tmpdir-backed path if not given). Returns the path. The video uses ffmpeg’s ‘testsrc` filter for video and `sine` for audio — fully self-contained, no external assets.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/hls/testing.rb', line 42 def generate_test_video(path: nil, duration: 12, width: 640, height: 360, framerate: 30, frequency: 440) path = Pathname.new(path || Dir::Tmpname.create(["hls-fixture", ".mp4"]) {}) cmd = [ "ffmpeg", "-y", "-loglevel", "error", "-f", "lavfi", "-i", "testsrc=duration=#{duration}:size=#{width}x#{height}:rate=#{framerate}", "-f", "lavfi", "-i", "sine=frequency=#{frequency}:duration=#{duration}", "-c:v", "libx264", "-preset", "ultrafast", "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "64k", "-shortest", path.to_s ] unless system(*cmd, out: File::NULL, err: File::NULL) raise HLS::Error, "ffmpeg failed to generate test fixture at #{path}" end path end |
#parse_playlist(path) ⇒ Object
Parses an m3u8 file and returns the M3u8::Playlist.
82 83 84 |
# File 'lib/hls/testing.rb', line 82 def parse_playlist(path) M3u8::Reader.new.read(File.read(path)) end |
#probe(path) ⇒ Object
Probes a media file with ffprobe and returns its parsed metadata.
62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/hls/testing.rb', line 62 def probe(path) stdout, _stderr, status = Open3.capture3( "ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=width,height,codec_name", "-of", "json", path.to_s ) raise HLS::Error, "ffprobe failed for #{path}" unless status.success? JSON.parse(stdout) end |
#probe_dimensions(path) ⇒ Object
Returns [width, height] for an image or video file.
75 76 77 78 79 |
# File 'lib/hls/testing.rb', line 75 def probe_dimensions(path) stream = probe(path).fetch("streams").first or raise HLS::Error, "no video stream in #{path}" [stream["width"], stream["height"]] end |
#silence_ffmpeg ⇒ Object
Silences stdout/stderr inside the block. Useful for hiding ffmpeg’s noisy progress output during test runs. Restores streams even on exception.
89 90 91 92 93 94 95 96 97 98 |
# File 'lib/hls/testing.rb', line 89 def silence_ffmpeg original_stdout = $stdout.dup original_stderr = $stderr.dup $stdout.reopen(File::NULL, "w") $stderr.reopen(File::NULL, "w") yield ensure $stdout.reopen(original_stdout) if original_stdout $stderr.reopen(original_stderr) if original_stderr end |