# camera and microphone

> Take a photograph, record a video, or record audio. `Camera.Start()` asks for the device and `Camera()` shows the viewfinder; `Camera.Capture()` answers an `UploadedFile` — the same value the `Upload` control hands over, so a photograph is stored exactly like a chosen file. Every refusal is one catchable `DeviceUnavailableException`.

<!-- id: ui-capture · area: ui · stability: preview · html: https://osysharp.com/reference/ui/capture/ -->

## Summary        {#summary}
Two devices, one shape. `Camera` and `Mic` each have a **device** pair and a **recording** pair:

| | acquire / release | record |
|---|---|---|
| **camera** | `Camera.Start(facing: Back)` · `Camera.Stop()` | `Camera.Record()` · `Camera.StopRecording()` |
| **microphone** | `Mic.Start()` · `Mic.Stop()` | `Mic.Record()` · `Mic.StopRecording()` |

The camera also takes still photographs with `Camera.Capture()`, and shows what it sees through the `Camera()`
atom — the viewfinder.

Everything that produces a file — `Capture`, and both `StopRecording`s — answers an **`UploadedFile`**, which is the
same value the [upload](https://osysharp.com/reference/ui/upload/) control hands an `onUploaded` action. So a photograph and a chosen file are stored the
same way, by the same code.

## Signature      {#signature}
```osy syntax
Camera.Start(facing: CameraFacing.Back)        // ask for the camera; CameraFacing is Any (default), Front or Back
Camera.Start(withSound: true)                  // …and the microphone, if this app will record video WITH sound
Camera.Stop()                                  // release it

UploadedFile photo = Camera.Capture()          // photograph the viewfinder
Camera.Record()                                // start recording
UploadedFile clip = Camera.StopRecording()     // stop, and answer the whole clip

Mic.Start()                                    // ask for the microphone
Mic.Record()
UploadedFile note = Mic.StopRecording()
Mic.Stop()
```

## Description    {#description}

### A whole photo booth   {#example}
```osy title="ask, look, photograph, store" test app=file-manager
using Osysharp.Storage;

[Page("/booth")] [AllowAnonymous]
component Booth() {
  bool live = false;
  string problem = "";
  string shot = "";

  action Begin() {
    try {
      Camera.Start(facing: CameraFacing.Front);
      live = true;
      problem = "";
    } catch (DeviceUnavailableException ex) {
      problem = ex.Message;
    }
  }

  action Snap() {
    try {
      UploadedFile photo = Camera.Capture();
      shot = photo.Path;
    } catch (DeviceUnavailableException ex) {
      problem = ex.Message;
    }
  }

  action Done() { Camera.Stop(); live = false; }

  render {
    Stack(gap: 3) {
      if (!live) { Pressable("Turn the camera on", onClick: Begin); }
      if (live) {
        Camera();
        Row(gap: 2) {
          Pressable("Take a photo", onClick: Snap);
          Pressable("Done", onClick: Done);
        }
      }
      if (shot != "") { Image(shot); }
      if (problem != "") { Text(problem); }
    }
  }
}
```

### Being refused is normal, and it is one exception   {#refusal}
A person saying no to a permission prompt is the system working, not a failure. So every capture verb refuses the
same way — `DeviceUnavailableException` — and an app that uses both devices writes **one** `catch`.

`ex.Message` is a sentence you can show, and it says which of four things happened:

| what happened | what the person can do |
|---|---|
| they refused the prompt | change it from the padlock in the address bar |
| the machine has no camera or microphone | nothing — hide the feature |
| another application is holding the device | close the other application |
| **the page is not on https or localhost** | nothing they can do — see below |

⛔ **The last one is a deployment problem, not an answer.** A browser hands out a camera or microphone **only** on
`https://` or `localhost`, and there is no fallback of any kind — unlike [Clipboard](https://osysharp.com/reference/ui/clipboard/), which has a second path
for exactly this case. An app served over plain `http://` on a LAN address **cannot** have a camera. Nothing at
compile time can see that, which is why it arrives as this exception rather than as a refusal to build.

### The device and the recording are separate   {#two-pairs}
`Start`/`Stop` hold the **device**. `Record`/`StopRecording` are one **clip**. They are separate because holding the
microphone across several recordings is the normal case, and re-acquiring it per clip would prompt every time.

```osy syntax
Mic.Start();                          // one prompt
Mic.Record();  UploadedFile a = Mic.StopRecording();
Mic.Record();  UploadedFile b = Mic.StopRecording();
Mic.Stop();                           // the indicator goes out here
```

`Stop()` always means the device and `StopRecording()` always means the clip, on **both** ambients — even though
`Mic.Stop()` could have meant "stop recording", since a microphone has nothing to show. One vocabulary is worth more
than a locally shorter name.

### Sound is asked for when you start, not when you record   {#with-sound}
`Camera.Start()` acquires video only. To record video **with** sound, say so up front:

```osy syntax
Camera.Start(facing: Back, withSound: true);
```

Acquiring the microphone later, at `Camera.Record()`, would show a second permission prompt at the moment somebody
presses record — the worst possible time. And an app that only takes photographs should not be asking for a
microphone at all. One prompt, for exactly what you declared.

### The viewfinder   {#viewfinder}
`Camera()` is a live `<video>`. It takes no props of its own — which camera, and when, is `Camera.Start`'s business,
because that is the call that can be refused. Style it like any other element.

A component's verbs act on **its own** `Camera()`, the same rule [Canvas](https://osysharp.com/reference/ui/canvas/) follows: a viewfinder inside a nested
component belongs to that component, and the one that starts the camera must be the one that declares it.

### Playing it back   {#playback}
`Video` and `Audio` play a file the app has stored — a recording you just made, or anything else in its file store.
Both always show controls.

```osy syntax
Video(clip.Path);
Video(clip.Path, poster: still.Path, loop: true);
Audio(note.Path);
```

### What comes out   {#formats}
A photograph is a **JPEG** at the camera's own resolution — not the size of the element on screen, so a photo does
not change with the page layout.

A recording is whatever the browser records: **WebM** on Chrome and Firefox, **MP4** on Safari. There is no single
format every browser produces, so the platform asks for the best each one supports rather than insisting on one and
failing on the others. Whatever arrives, the server derives the stored file's real type from its bytes.

### Trying it before it exists   {#testing}
`Camera.Capture()` refuses if the camera has not produced its first frame yet, rather than storing a blank image. If
you photograph immediately after `Camera.Start()`, expect that refusal — its message says the camera is still
starting up.

## See also   {#see-also}
- [upload](https://osysharp.com/reference/ui/upload/) — the `Upload` control, which hands over the same `UploadedFile`
- [sound](https://osysharp.com/reference/ui/sound/) — playing audio the app ships, which is a different thing from recording it
- [Canvas](https://osysharp.com/reference/ui/canvas/) — the drawing surface, and the "a component acts on its own element" rule
- [component](https://osysharp.com/reference/ui/component/) — actions, state, and where these verbs are called from
