From 4b7944e95a3b362cd102a7cba653873254e3ba8f Mon Sep 17 00:00:00 2001 From: Billal Ghilas <84322223+gBillal@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:23:09 +0100 Subject: [PATCH] Add YUV 10 bits formats (#15) * Add YUV 10 bits formats * bump to v0.4.2 --- README.md | 2 +- lib/membrane_raw_video.ex | 55 ++++++++++++++++++++++++++++++-- mix.exs | 2 +- test/membrane_raw_video_test.exs | 36 +++++++++++++++++++++ 4 files changed, 90 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f68f541..bc212ea 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ reason, just add the following line to your `deps` in the `mix.exs` and run `mix deps.get`. ```elixir -{:membrane_raw_video_format, "~> 0.4.1"} +{:membrane_raw_video_format, "~> 0.4.2"} ``` ## Copyright and License diff --git a/lib/membrane_raw_video.ex b/lib/membrane_raw_video.ex index e677102..2ee5637 100644 --- a/lib/membrane_raw_video.ex +++ b/lib/membrane_raw_video.ex @@ -28,7 +28,24 @@ defmodule Membrane.RawVideo do Format used to encode the color of every pixel in each video frame. """ @type pixel_format :: - :I420 | :I422 | :I444 | :RGB | :BGRA | :RGBA | :NV12 | :NV21 | :YV12 | :AYUV | :YUY2 + :I420 + | :I422 + | :I444 + | :RGB + | :BGR + | :BGRA + | :RGBA + | :NV12 + | :NV21 + | :YV12 + | :AYUV + | :YUY2 + | :I420_10LE + | :I420_10BE + | :I422_10LE + | :I422_10BE + | :I444_10LE + | :I444_10BE @typedoc """ Determines, whether buffers are aligned i.e. each buffer contains one frame. @@ -51,13 +68,20 @@ defmodule Membrane.RawVideo do :I422, :I444, :RGB, + :BGR, :BGRA, :RGBA, :NV12, :NV21, :YV12, :AYUV, - :YUY2 + :YUY2, + :I420_10LE, + :I420_10BE, + :I422_10LE, + :I422_10BE, + :I444_10LE, + :I444_10BE ] @doc """ @@ -96,7 +120,7 @@ defmodule Membrane.RawVideo do {:ok, width * height * 2} end - def frame_size(format, width, height) when format in [:I444, :RGB] do + def frame_size(format, width, height) when format in [:I444, :RGB, :BGR] do # No subsampling {:ok, width * height * 3} end @@ -106,6 +130,31 @@ defmodule Membrane.RawVideo do {:ok, width * height * 4} end + def frame_size(format, width, height) + when format in [:I420_10LE, :I420_10BE] and Integer.is_even(width) and + Integer.is_even(height) do + # Subsampling by 2 in both dimensions + # Each pixel requires 2 bytes. + # Y = 2 * width * height + # V = U = 2 * (width / 2) * (height / 2) + {:ok, 3 * width * height} + end + + def frame_size(format, width, height) + when format in [:I422_10LE, :I422_10BE] and Integer.is_even(width) do + # Subsampling by 2 in horizontal dimension + # Each pixel requires 2 bytes. + # Y = 2 * width * height + # V = U = 2 * (width / 2) * height + {:ok, 4 * width * height} + end + + def frame_size(format, width, height) when format in [:I444_10LE, :I444_10BE] do + # No subsampling + # Each pixel requires 2 bytes. + {:ok, 6 * width * height} + end + def frame_size(format, _width, _height) when format in @supported_pixel_formats do {:error, :invalid_dimensions} end diff --git a/mix.exs b/mix.exs index 80cbcf1..ab3ce93 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Membrane.RawVideo.Mixfile do use Mix.Project - @version "0.4.1" + @version "0.4.2" @github_url "https://github.com/membraneframework/membrane_raw_video_format" def project do diff --git a/test/membrane_raw_video_test.exs b/test/membrane_raw_video_test.exs index c4a77e1..63dba01 100644 --- a/test/membrane_raw_video_test.exs +++ b/test/membrane_raw_video_test.exs @@ -133,6 +133,42 @@ defmodule Membrane.RawVideo.Test do assert @module.frame_size(format_struct(format, 10, 19)) == {:ok, 760} end + test "frame_size for :I420_10xx format" do + format = :I420_10LE + + assert @module.frame_size(format, 10, 20) == {:ok, 600} + assert @module.frame_size(format, 9, 20) == {:error, :invalid_dimensions} + assert @module.frame_size(format, 10, 19) == {:error, :invalid_dimensions} + + assert @module.frame_size(format_struct(format, 10, 20)) == {:ok, 600} + assert @module.frame_size(format_struct(format, 9, 20)) == {:error, :invalid_dimensions} + assert @module.frame_size(format_struct(format, 10, 19)) == {:error, :invalid_dimensions} + end + + test "frame_size for :I422_10xx format" do + format = :I422_10LE + + assert @module.frame_size(format, 10, 20) == {:ok, 800} + assert @module.frame_size(format, 9, 20) == {:error, :invalid_dimensions} + assert @module.frame_size(format, 10, 19) == {:ok, 760} + + assert @module.frame_size(format_struct(format, 10, 20)) == {:ok, 800} + assert @module.frame_size(format_struct(format, 9, 20)) == {:error, :invalid_dimensions} + assert @module.frame_size(format_struct(format, 10, 19)) == {:ok, 760} + end + + test "frame_size for :I44_10xx format" do + format = :I444_10LE + + assert @module.frame_size(format, 10, 20) == {:ok, 1200} + assert @module.frame_size(format, 9, 20) == {:ok, 1080} + assert @module.frame_size(format, 10, 19) == {:ok, 1140} + + assert @module.frame_size(format_struct(format, 10, 20)) == {:ok, 1200} + assert @module.frame_size(format_struct(format, 9, 20)) == {:ok, 1080} + assert @module.frame_size(format_struct(format, 10, 19)) == {:ok, 1140} + end + test "frame_size error on unknown pixel format" do assert @module.frame_size(:yuv_240p, 10, 20) == {:error, :invalid_pixel_format} end