GPU-accelerated media processing via EXCubeCL.
Provides real-time video and image processing pipelines using GPU compute kernels. Designed for:
- Camera frame processing (blur, beauty, filters)
- Video effects (transitions, overlays, color grading)
- Livestream effects (virtual background, AR)
- Image preprocessing for ML inference
- Video encoding/transcoding via ExCubecl.Transcode
EXCubeCL Compatibility
This module is compatible with EXCubeCL 0.3.0+ and 0.4.0+ APIs:
- All EXCubeCL functions return
{:ok, result}tuples (not bare values) ExCubecl.read/1returns{:ok, binary()}(not a list)ExCubecl.run_kernel/4returns{:ok, cmd_id}(not:ok)ExCubecl.buffer/3andExCubecl.buffer_zeros/2return{:ok, ref}ExCubecl.pipeline/0returns{:ok, pipeline_id}ExCubecl.submit/1takes a string job specExCubecl.device_info/0returns{:ok, map()}
EXCubeCL 0.4.0 Media Modules
ExCubecl.Media— Media I/O: open sources, query streams, read framesExCubecl.Video— Video ops: overlay, mix, scale, crop, convertExCubecl.Audio— Audio ops: mix, overlay, resample, channelsExCubecl.Filter— GPU-accelerated filters: apply, chainExCubecl.Transcode— Encoding/muxing: start, write_frame, write_samples, finishExCubecl.VideoFrame— Video frame struct (width, height, format, data, pts)ExCubecl.AudioSamples— Audio samples struct (sample_rate, channels, data, pts)
Architecture
Camera Frame → GPU Buffer → CubeCL Kernels → GPU Buffer → Display/EncoderExample: Real-time blur filter
# Create processing context
{:ok, ctx} = Dala.Media.Gpu.Processor.start_pipeline(640, 480)
# Process frames in a loop
receive do
{:camera_frame, rgba_data} ->
{:ok, output} = Dala.Media.Gpu.Processor.process_frame(ctx, rgba_data, [
{:blur, %{radius: 3, sigma: 1.5}},
{:sharpen, %{amount: 0.3}}
])
# Display or encode output
endExample: Virtual background
{:ok, ctx} = Dala.Media.Gpu.Processor.start_pipeline(1280, 720)
{:ok, output} = Dala.Media.Gpu.Processor.process_frame(ctx, camera_frame, [
{:segmentation, %{model: :deeplab}},
{:background_replace, %{background: bg_image}},
{:blend, %{alpha: 0.9}}
])Example: Media source → filter → encode
{:ok, source} = Dala.Media.Gpu.Processor.open_source("video.mp4")
{:ok, streams} = Dala.Media.Gpu.Processor.streams(source)
{:ok, encoder} = Dala.Media.Gpu.Processor.start_encoder("output.mp4", %{codec: :h264})
# Read, filter, encode loop
{:ok, frame} = Dala.Media.Gpu.Processor.read_video_frame(source)
{:ok, filtered} = Dala.Media.Gpu.Processor.apply_filter(frame, :blur, %{radius: 2})
:ok = Dala.Media.Gpu.Processor.write_video_frame(encoder, filtered)
:ok = Dala.Media.Gpu.Processor.finish_encoder(encoder)Available Filters
| Filter | Description | Params |
|---|---|---|
:blur | Gaussian blur | %{radius: 3, sigma: 1.5} |
:sharpen | Unsharp mask | %{amount: 0.5} |
:denoise | Bilateral filter | %{strength: 0.5} |
:beauty | Skin smoothing | %{strength: 0.3} |
:grayscale | RGB to grayscale | %{} |
:sepia | Sepia tone | %{intensity: 0.8} |
:vignette | Vignette effect | %{intensity: 0.5} |
:lut | Color LUT transform | %{lut: lut_data} |
:brightness | Brightness adjustment | %{value: 0.1} |
:contrast | Contrast adjustment | %{value: 0.2} |
:saturation | Saturation adjustment | %{value: 0.3} |
:white_balance | White balance correction | %{temperature: 6500} |
:hdr | HDR tone mapping | %{exposure: 1.0} |
:segmentation | Semantic segmentation | %{model: :deeplab} |
:background_replace | Background replacement | %{background: buf} |
:blend | Alpha blending | %{alpha: 0.5} |
Summary
Functions
Apply a named GPU-accelerated filter to a video frame.
Apply a single filter to an RGBA binary.
Blur filter.
Brightness adjustment.
Close a media source and release all associated resources.
Contrast adjustment.
Convert a video frame to a different pixel format via ExCubecl.Video.
Convert audio channel layout via ExCubecl.Audio.
Crop a video frame via ExCubecl.Video.
Apply a chain of GPU-accelerated filters to a video frame.
Finalize encoding and close the output file.
Grayscale filter.
Mix two video frames with equal weight via ExCubecl.Video.
Mix two audio sample buffers via ExCubecl.Audio.
Open a media source via ExCubecl.Media.
Overlay one video frame onto another via ExCubecl.Video.
Overlay audio samples onto a base via ExCubecl.Audio.
Process a frame through a chain of GPU filters.
Process a frame asynchronously. Returns {:ok, cmd_id}.
Read audio samples from a media source.
Read a video frame from a media source.
Resample audio to a different sample rate via ExCubecl.Audio.
Saturation adjustment.
Scale a video frame to new dimensions via ExCubecl.Video.
Sharpen filter.
Start a transcoder/encoder via ExCubecl.Transcode.
Start a new GPU processing pipeline for the given dimensions.
Stop a processing pipeline and free all GPU resources.
Get stream information from an opened media source.
Transcode a media file to another format via ExCubecl.Transcode.
Write audio samples to an encoder.
Write a video frame to an encoder.
Types
@type context() :: %Dala.Media.Gpu.Processor{ height: non_neg_integer(), input_buf: Dala.Gpu.Compute.Buffer.t() | nil, output_buf: Dala.Gpu.Compute.Buffer.t() | nil, pipeline: reference() | nil, temp_buf: Dala.Gpu.Compute.Buffer.t() | nil, width: non_neg_integer() }
@type encoder() :: reference()
@type source() :: reference()
Functions
@spec apply_filter(ExCubecl.VideoFrame.t(), atom(), map()) :: {:ok, ExCubecl.VideoFrame.t()} | {:error, term()}
Apply a named GPU-accelerated filter to a video frame.
Parameters
frame—%ExCubecl.VideoFrame{}structfilter— filter atom (e.g.:blur,:sharpen,:denoise,:beauty)params— map of filter-specific parameters
Returns {:ok, %ExCubecl.VideoFrame{}}.
@spec apply_filter(binary(), non_neg_integer(), non_neg_integer(), atom(), map()) :: {:ok, binary()} | {:error, term()}
Apply a single filter to an RGBA binary.
@spec blur(binary(), non_neg_integer(), non_neg_integer(), keyword()) :: {:ok, binary()} | {:error, term()}
Blur filter.
@spec brightness(binary(), non_neg_integer(), non_neg_integer(), float()) :: {:ok, binary()} | {:error, term()}
Brightness adjustment.
Close a media source and release all associated resources.
@spec contrast(binary(), non_neg_integer(), non_neg_integer(), float()) :: {:ok, binary()} | {:error, term()}
Contrast adjustment.
@spec convert(ExCubecl.VideoFrame.t(), atom(), map()) :: {:ok, ExCubecl.VideoFrame.t()} | {:error, term()}
Convert a video frame to a different pixel format via ExCubecl.Video.
Parameters
frame—%ExCubecl.VideoFrame{}format— target format atom (e.g.:rgba,:bgra,:yuv420p,:nv12)opts— additional options map
Returns {:ok, %ExCubecl.VideoFrame{}}.
@spec convert_audio_channels(ExCubecl.AudioSamples.t(), atom(), map()) :: {:ok, ExCubecl.AudioSamples.t()} | {:error, term()}
Convert audio channel layout via ExCubecl.Audio.
Returns {:ok, %ExCubecl.AudioSamples{}}.
@spec crop(ExCubecl.VideoFrame.t(), map()) :: {:ok, ExCubecl.VideoFrame.t()} | {:error, term()}
Crop a video frame via ExCubecl.Video.
Parameters
frame—%ExCubecl.VideoFrame{}opts— options map::x— left edge:y— top edge:width— crop width:height— crop height
Returns {:ok, %ExCubecl.VideoFrame{}}.
@spec filter_chain(ExCubecl.VideoFrame.t(), [filter_spec()], map()) :: {:ok, ExCubecl.VideoFrame.t()} | {:error, term()}
Apply a chain of GPU-accelerated filters to a video frame.
filters is a list of {filter_name, params} tuples, applied in order.
Returns {:ok, %ExCubecl.VideoFrame{}}.
Finalize encoding and close the output file.
Returns :ok or {:error, term()}.
@spec grayscale(binary(), non_neg_integer(), non_neg_integer()) :: {:ok, binary()} | {:error, term()}
Grayscale filter.
@spec mix(ExCubecl.VideoFrame.t(), ExCubecl.VideoFrame.t(), map()) :: {:ok, ExCubecl.VideoFrame.t()} | {:error, term()}
Mix two video frames with equal weight via ExCubecl.Video.
Returns {:ok, %ExCubecl.VideoFrame{}}.
@spec mix_audio(ExCubecl.AudioSamples.t(), ExCubecl.AudioSamples.t(), map()) :: {:ok, ExCubecl.AudioSamples.t()} | {:error, term()}
Mix two audio sample buffers via ExCubecl.Audio.
Returns {:ok, %ExCubecl.AudioSamples{}}.
Open a media source via ExCubecl.Media.
path can be a file path, URL, or device identifier.
Returns {:ok, source} where source is a reference to the opened media.
@spec overlay(ExCubecl.VideoFrame.t(), ExCubecl.VideoFrame.t(), map()) :: {:ok, ExCubecl.VideoFrame.t()} | {:error, term()}
Overlay one video frame onto another via ExCubecl.Video.
Parameters
base— base%ExCubecl.VideoFrame{}overlay— overlay%ExCubecl.VideoFrame{}opts— options map::x— horizontal position (default: 0):y— vertical position (default: 0):alpha— blend alpha 0.0..1.0 (default: 1.0)
Returns {:ok, %ExCubecl.VideoFrame{}}.
@spec overlay_audio(ExCubecl.AudioSamples.t(), ExCubecl.AudioSamples.t(), map()) :: {:ok, ExCubecl.AudioSamples.t()} | {:error, term()}
Overlay audio samples onto a base via ExCubecl.Audio.
Returns {:ok, %ExCubecl.AudioSamples{}}.
@spec process_frame(context(), binary(), [filter_spec()]) :: {:ok, binary()} | {:error, term()}
Process a frame through a chain of GPU filters.
rgba_data is a raw RGBA8888 binary of size width * height * 4.
filters is a list of {filter_name, params} tuples.
Returns {:ok, binary()} with the processed RGBA8888 data.
@spec process_frame_async(context(), binary(), [filter_spec()]) :: {:ok, non_neg_integer()} | {:error, term()}
Process a frame asynchronously. Returns {:ok, cmd_id}.
@spec read_audio_samples(source()) :: {:ok, ExCubecl.AudioSamples.t()} | {:error, term()}
Read audio samples from a media source.
Returns {:ok, %ExCubecl.AudioSamples{}} or {:error, :eof} when done.
@spec read_video_frame(source()) :: {:ok, ExCubecl.VideoFrame.t()} | {:error, term()}
Read a video frame from a media source.
Returns {:ok, %ExCubecl.VideoFrame{}} or {:error, :eof} when done.
@spec resample_audio(ExCubecl.AudioSamples.t(), non_neg_integer(), map()) :: {:ok, ExCubecl.AudioSamples.t()} | {:error, term()}
Resample audio to a different sample rate via ExCubecl.Audio.
Returns {:ok, %ExCubecl.AudioSamples{}}.
@spec saturation(binary(), non_neg_integer(), non_neg_integer(), float()) :: {:ok, binary()} | {:error, term()}
Saturation adjustment.
@spec scale(ExCubecl.VideoFrame.t(), map()) :: {:ok, ExCubecl.VideoFrame.t()} | {:error, term()}
Scale a video frame to new dimensions via ExCubecl.Video.
Parameters
frame—%ExCubecl.VideoFrame{}opts— options map::width— target width:height— target height:interpolation—:nearest|:bilinear|:bicubic(default::bilinear)
Returns {:ok, %ExCubecl.VideoFrame{}}.
@spec sharpen(binary(), non_neg_integer(), non_neg_integer(), keyword()) :: {:ok, binary()} | {:error, term()}
Sharpen filter.
Start a transcoder/encoder via ExCubecl.Transcode.
Parameters
output_path— destination file pathopts— encoder options map, e.g.::codec—:h264|:h265|:vp9|:av1:bitrate— target bitrate in bits/sec:width/:height— output dimensions:fps— output frame rate:sample_rate— audio sample rate:audio_codec—:aac|:opus|:mp3
Returns {:ok, encoder}.
@spec start_pipeline(non_neg_integer(), non_neg_integer()) :: {:ok, context()} | {:error, term()}
Start a new GPU processing pipeline for the given dimensions.
@spec stop_pipeline(context()) :: :ok
Stop a processing pipeline and free all GPU resources.
Get stream information from an opened media source.
Returns {:ok, streams} where streams is a list of stream info maps.
Each map contains keys like :type (:video | :audio), :codec,
:width, :height, :sample_rate, etc.
Transcode a media file to another format via ExCubecl.Transcode.
Convenience wrapper for file-to-file transcode.
Options
:video— keyword list with:codec,:bitrate,:fps,:width,:height:audio— keyword list with:codec,:bitrate,:sample_rate
Example
Dala.Media.Gpu.Processor.transcode_file("input.mp4", "output.mkv",
video: [codec: :h265, bitrate: "4M"],
audio: [codec: :opus, bitrate: "128k"]
)
@spec write_audio_samples(encoder(), ExCubecl.AudioSamples.t()) :: :ok | {:error, term()}
Write audio samples to an encoder.
samples is an %ExCubecl.AudioSamples{} struct.
encoder is a reference returned by start_encoder/2.
Returns :ok or {:error, term()}.
@spec write_video_frame(encoder(), ExCubecl.VideoFrame.t()) :: :ok | {:error, term()}
Write a video frame to an encoder.
frame is an %ExCubecl.VideoFrame{} struct.
encoder is a reference returned by start_encoder/2.
Returns :ok or {:error, term()}.