Skip to content

Commit

Permalink
feat: generalise spec and ready release
Browse files Browse the repository at this point in the history
  • Loading branch information
stakach committed Aug 25, 2023
1 parent 08002c2 commit acc4cfe
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
*.dwarf
captured_frame.png
shard.lock
test.mp4
2 changes: 1 addition & 1 deletion shard.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: v4l2
version: 0.1.0
version: 1.0.0

development_dependencies:
ameba:
Expand Down
34 changes: 30 additions & 4 deletions spec/spec_helper.cr
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,39 @@ require "ffmpeg"
require "stumpy_png"
require "../src/v4l2"

unless File.exists? "./test.mp4"
# the video file we'll use for testing
video_file = "./test.mp4"
unless File.exists? video_file
puts "downloading video file..."
HTTP::Client.get("https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/360/Big_Buck_Bunny_360_10s_5MB.mp4") do |response|
raise "could not found video file" unless response.success?
File.write("./test.mp4", response.body_io)
File.write(video_file, response.body_io)
end
end

loopback_device = V4L2::Video.find_loopback_device
raise "no available loopback device" unless loopback_device
# ensure we have a loopback device
loopback = V4L2::Video.find_loopback_device
raise "no loopback running. run 'sudo modprobe v4l2loopback'" unless loopback
LOOPBACK_DEVICE = V4L2::Video.find_loopback_device.as(Path)

# push a video to the loopback device for
wait_running = Channel(Process).new
spawn do
Process.run("ffmpeg", {
"-stream_loop", "-1", "-re", "-i", video_file,
"-f", "v4l2", "-vcodec", "rawvideo",
"-pix_fmt", "yuyv422", LOOPBACK_DEVICE.to_s,
}) do |process|
wait_running.send process
end
end

# terminate ffmpeg once the spec has finished
select
when process = wait_running.receive
# ensure the process exits once the spec has run
Spec.after_suite { process.terminate }
sleep 1
when timeout(5.seconds)
raise "timeout waiting for ffmpeg to launch"
end
43 changes: 24 additions & 19 deletions spec/v4l2_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,39 @@ require "./spec_helper"

module V4L2
describe V4L2 do
it "loads the library and runs some functions" do
LibV4l2::CreateBuffers.new
vid = Video.new(Path["/dev/video0"])
pixels = vid.supported_formats
pixels.each(&.frame_sizes.each(&.frame_rate))
pp! pixels
it "can inspect the device name" do
vid = Video.new(LOOPBACK_DEVICE)
details = vid.device_details
(details.name.size > 0).should be_true
details.name.should eq "Dummy video device (0x0000)"
end

it "can inspect the stream details" do
vid = Video.new(LOOPBACK_DEVICE)
format = vid.supported_formats[0]
format.code.should eq "YUYV"
format.description.should eq "YUYV 4:2:2"
size = format.frame_sizes[0]
size.width.should eq 640
size.height.should eq 360
size.frame_rate.fps.should eq 30
end

it "resets the buffer properly" do
vid = Video.new(Path["/dev/video0"])
vid = Video.new(LOOPBACK_DEVICE)
vid.buffer.index = 3_u32
vid.buffer.index.should eq 3_u32
vid.reset_buffer
vid.buffer.index.should eq 0_u32
end

it "gets the device name" do
vid = Video.new(Path["/dev/video0"])
details = vid.device_details
(details.name.size > 0).should be_true
end

it "can stream and scale" do
vid = Video.new(Path["/dev/video0"])
vid = Video.new(LOOPBACK_DEVICE)
pixels = vid.supported_formats
pixels.each(&.frame_sizes.each(&.frame_rate))

# highest resolution + framerate combo
format = pixels[1].frame_sizes[1].frame_rate
vid.set_format(format).request_buffers(1)
# grab the first available
format = pixels[0].frame_sizes[0].frame_rate
vid.set_format(format).request_buffers(2)

# setup the ffmpeg image format conversion
# v4l2_frame = FFmpeg::Frame.new(format.width, format.height, :yuyv422)
Expand All @@ -56,7 +59,7 @@ module V4L2
convert.scale(v4l2_frame, rgb_frame)
end

(count > 0).should eq true
(count > 1).should eq true

# save the last frame using stumpy png
pixel_components = format.width * format.height * 3
Expand All @@ -72,6 +75,8 @@ module V4L2
canvas.pixels[index] = StumpyCore::RGBA.new(r, g, b)
end
StumpyPNG.write(canvas, "./captured_frame.png")

File.exists?("./captured_frame.png").should eq true
end
end
end
7 changes: 4 additions & 3 deletions src/v4l2/video.cr
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,13 @@ class V4L2::Video
close
end

def self.find_loopback_device : String?
def self.find_loopback_device : Path?
Dir.glob("/dev/video*").each do |dev_path|
begin
video = V4L2::Video.new(dev_path)
path = Path[dev_path]
video = V4L2::Video.new(path)
begin
return dev_path if video.device_details.card.downcase.includes?("dummy")
return path if video.device_details.card.downcase.includes?("dummy")
ensure
video.close
end
Expand Down

0 comments on commit acc4cfe

Please sign in to comment.