A friendly interface for built-in video sources, such as cameras.
Camera lets you open a camera, change its settings, take snapshots, and show a
live preview in an HTML element.
The current API covers the common case of opening and using a single camera. It does not yet expose the full range of browser media features.
Commands and information pass between Elm and the browser through ports. Large data items are not passed to Elm, but are addressed through opaque handles instead.
Using a camera follows a small request-and-result loop:
open, or use openWithOptions to choose a device and send initial
Configuration.Constraints. The regular open function starts with
Configuration.defaultConstraints, where every field is Nothing.OpenCompleted. A successful result contains an OpenedCamera
with its ID, capabilities, and active settings.configure.ConfigureCompleted. A successful result contains the active
settings the browser actually applied.Constraints express what your application wants, while settings describe what
the browser is using. The browser may choose compatible values that differ from
the request, so use the returned settings as the current camera state. Both
completion events return Err CameraError when their operation fails.
Install the Elm package:
elm install mbr/elm-cameraElm packages cannot declare ports. Make Main.elm a port module, or add an
application-owned port module containing these declarations:
import Camera exposing (CommandPort, EventPort)
port cameraCmd : CommandPort msg
port cameraMsg : EventPort msgThe JavaScript runtime expects the exact port names cameraCmd and
cameraMsg.
Copy
js/elm-camera.js
into the browser assets. Load the runtime and compiled Elm application,
initialize Elm, and then initialize the camera runtime:
<script src="elm-camera.js"></script>
<script src="app.js"></script>
<script>
var app = Elm.Main.init({
node: document.getElementById("elm")
});
var cameraRuntime = ElmCamera.initApp(app);
</script>initApp returns a small runtime object. Most applications can leave it running
for the lifetime of the page. If your Elm application is removed or replaced,
call shutdown() to release its cameras, snapshots, and any URLs created for
those snapshots.
If the cameraCmd port is not present, initApp simply returns null.
Application-owned browser integrations can resolve a retained snapshot handle
with cameraRuntime.snapshotBlob(handle), or read an open camera's current
frame with await cameraRuntime.currentFrame(cameraId). A current frame is a
full-size ImageBitmap owned by the caller and must be closed after use. Use
the corresponding encoders and decoders at application port boundaries.
Camera access requires a secure browser context, so use HTTPS in production.
Browsers normally treat http://localhost, http://127.0.0.1, and
http://[::1] as trustworthy for local development. A phone visiting a plain
HTTP address on the local network is not using localhost and generally needs
HTTPS.
Call openRear from a user action, subscribe to camera events, and track the
pending operation in your application's model until OpenCompleted arrives:
import Camera
import CameraPorts as Ports
type Model
= Opening
| Ready Camera.OpenedCamera
| Failed Camera.CameraError
type Msg
= CameraEvent Camera.Event
init : () -> ( Model, Cmd Msg )
init _ =
( Opening, Camera.openRear Ports.cameraCmd )
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
CameraEvent (Camera.OpenCompleted result) ->
case result of
Ok camera ->
( Ready camera, Cmd.none )
Err error ->
( Failed error, Cmd.none )
CameraEvent _ ->
( model, Cmd.none )
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.map CameraEvent (Camera.subscribe Ports.cameraMsg)A successful open returns the camera ID together with its capabilities and
active settings. Camera acquisition and permission failures arrive as
Err CameraError.
CommandPort and EventPort match the two application-owned ports shown above.
Pass those ports to camera commands and subscriptions.
A port provided by the application for receiving camera events from the browser.
Only one camera can be opening at a time. Wait for a command's completion event before sending another command that changes the same camera.
The named functions below cover the usual commands. send is available when it
is more convenient to work with Command values directly. subscribe receives
both replies to commands and events generated by an open camera.
A request sent to the browser runtime.
Most applications can use the named command functions. send is useful when
keeping commands as values is more convenient.
A reply or camera event received from the browser runtime.
Each command produces a matching completion event containing a Result.
Device and camera changes can arrive on their own. RuntimeError contains a
failure that could not be tied to a completion event. An ended camera is already
closed and does not need to be passed to close.
Send a Command through the application's command port.
The named command functions are usually more convenient.
Listen for camera events through the application's event port.
Use Sub.map to turn these events into application messages. Keep the
subscription active while a command is pending or a camera is open.
open uses the browser's default camera, which is usually the right choice for
desktop computers and USB webcams. openFront and openRear ask for a camera
facing in a particular direction, but still allow the browser to choose another
camera when needed.
Use openWithOptions to choose a device or ask for particular initial settings.
devices can help with device pickers, but opening a camera is the only way to
know whether it is available. If the browser lists a camera without an ID,
devices returns Err DeviceInformationUnavailable. Opening a camera and then
calling devices again will usually reveal IDs and labels.
Camera settings and facing modes are defined in
Camera.Configuration.
A camera listed by the browser.
The label is Nothing when the browser does not provide one.
A camera device known to the browser.
The device and initial settings to ask for when opening a camera.
Options that let the browser choose the camera and its settings.
The handle and initial information returned for an opened camera.
Ask the browser for its current list of cameras.
Browsers often hide device IDs until the page has opened a camera. In that
case, the result is Err DeviceInformationUnavailable. Call devices again
after a successful OpenCompleted to get the selectable cameras.
Open the browser's default camera.
This is usually the right choice on desktop computers. The result arrives in
OpenCompleted.
Open a camera while preferring one that faces the user.
The browser may choose another camera when needed. The result arrives in
OpenCompleted.
Open a camera while preferring one that faces away from the user.
The browser may choose another camera when needed. The result arrives in
OpenCompleted.
Open a camera with a chosen device or initial settings.
The result arrives in OpenCompleted.
Camera configuration has four parts:
supportedConstraints tells you which setting names the browser knows.capabilities tells you what an open camera can do.Configuration.Constraints contains the values your application asks for.settings tells you what the browser is actually using.Keep your constraints in your application model when making several changes.
A property the browser does not report is Nothing. Even when a property is
supported, a particular value or combination may still fail. After calling
configure, use the Configuration.Settings from ConfigureCompleted as the
camera's current settings.
For example, request continuous autofocus only when the camera advertises it:
import Camera.Configuration as Configuration
enableContinuousFocus :
CommandPort msg
-> OpenedCamera
-> Configuration.Constraints
-> Cmd msg
enableContinuousFocus commandPort camera constraints =
if
camera.capabilities.focusMode
|> Maybe.withDefault []
|> List.member Configuration.Continuous
then
configure commandPort
camera.id
{ constraints
| focusMode = Just Configuration.Continuous
}
else
Cmd.noneSee Camera.Configuration for the control types and a
detailed explanation of capabilities, constraints, and settings.
Ask which camera setting names the browser recognizes.
Ask what an open camera can do.
Ask which settings a camera is currently using.
Ask a camera to use new settings.
The result arrives in ConfigureCompleted with the settings the browser
actually applied.
preview creates an HTML element showing the camera's live video. It fills its
element by default and may crop video edges. Use Camera.Preview
to show the complete frame instead, and add any other HTML attributes the
application needs:
import Camera.Preview as Preview
Camera.preview camera.id
[ Preview.fit Preview.Contain
, Html.Attributes.class "my-viewfinder"
]Removing the element does not close the camera, and adding it again reconnects the preview. The same camera can appear in more than one preview, and regular HTML or SVG can be placed over it.
A handle for an open camera.
The camera itself stays in the browser. IDs are never reused, and using one
after its camera has closed or ended produces StaleCamera.
Show a camera's live video in a camera-preview element.
The given attributes are added to the element. Adding, removing, or changing a preview does not open or close its camera.
Application-owned browser integrations can address an open camera without
exposing its ID representation. encodeId sends the ID through a port, and
idDecoder reads it when the integration responds.
Encode a camera ID for an application-owned browser integration.
Decode a camera ID returned by an application-owned browser integration.
capturePng takes a snapshot of the current frame. The PNG stays in the browser
rather than passing through Elm, and SnapshotCaptured returns its metadata and
an opaque handle.
A successful snapshot is kept until you call discardSnapshot for its handle,
or until the camera runtime shuts down. Removing the handle from your Elm model,
removing an image from the page, or closing the camera does not release it. Use
snapshotObjectUrl to show or download the PNG, then call discardSnapshot
when it is no longer needed.
import Camera.Snapshot as Snapshot
capture : CommandPort msg -> OpenedCamera -> Cmd msg
capture commandPort camera =
capturePng commandPort camera.id Snapshot.defaultOptionsSee Camera.Snapshot for snapshot options, metadata, and URL
cleanup.
Take a PNG snapshot of the camera's current frame.
The result arrives in SnapshotCaptured. The PNG stays in the browser until
its handle is passed to discardSnapshot or the runtime shuts down.
Create a browser URL for showing or downloading a snapshot.
Release a snapshot and any browser URLs created for it.
The result arrives in SnapshotDiscarded. A snapshot that has already been
released produces StaleSnapshot.
setEnabled pauses or resumes camera output without releasing the camera.
close releases the camera and makes its ID stale. Removing every preview does
not close the camera.
A camera can also be muted, unmuted, or ended by the browser or device. These
changes arrive through CameraMuted, CameraUnmuted, and CameraEnded. Camera
and snapshot IDs are never reused, so an old ID cannot accidentally refer to a
new resource.
Pause or resume camera output without releasing the camera.
Close and release a camera.
Command failures arrive in completion events as Err values. If a message from
the runtime cannot be read, subscribe produces RuntimeError containing a
ProtocolFailure instead of crashing the Elm application.
Something that went wrong while using Camera.
UnsupportedBrowser means the required browser camera API is absent.InsecureContext means the page is not allowed to request a camera from
its current origin.PermissionDenied means camera access was refused.DeviceNotFound means no camera matched the request.DeviceInformationUnavailable means the browser listed a camera without
enough information to select it. Opening a camera and trying again may help.DeviceUnavailable means a matching camera could not be read, for example
because another application owns it or the operating system rejected it.UnsupportedConstraint means the browser does not recognize a requested
constraint.Overconstrained contains the reported constraint name, when available,
and the browser's message.OpenAlreadyPending means another open operation has not completed.OperationCanceled means the operation was canceled before completion.StaleCamera and StaleSnapshot identify resources that no longer exist.PreviewFailed, CaptureFailed, and EncodingFailed describe failures in
their respective operations.UnknownCameraError preserves an otherwise unclassified browser failure.ProtocolFailure contains a message from the runtime that Elm could not
read.Details about a message from the browser runtime that Elm could not read.
Describe a camera error in friendly language.
Browser-provided details are included when they help explain the failure.
A port provided by the application for sending camera commands to the browser.