Skip to main content

inset_embedder/
media.rs

1//! Players and cameras: the system's own media stack, as a host hands it to the app. Not a
2//! dart:ui file: Flutter reaches media through its `video_player` and `camera` plugins, whose
3//! controllers come from their method channels. These follow the systems' own model instead,
4//! which they share: a player is one object made from a URL, loading by itself, which the app
5//! controls and whose state it observes (`AVPlayer`, Media3's `ExoPlayer`, Windows'
6//! `MediaPlayer`, the web's `<video>`); a camera is a session on one device with outputs, each for
7//! one use (`AVCaptureSession` with its preview layer and its photo, video data and movie file
8//! outputs, CameraX's use cases).
9//!
10//! Every system has two levels: a view that takes a player (`AVPlayerLayer(player:)`, Media3's
11//! `PlayerView.player`), which is the framework's, and a player that puts its pictures where it
12//! is given (Media3's `setVideoSurface`), which is the host's and this module's. A player's
13//! picture goes to one destination at a time, which the app makes and gives it
14//! ([`MediaPlayer::set_video_output`]): a [`CompositionLayer`], whose content becomes a layer the
15//! system draws the picture into, which is the only way protected and HDR video show at all, with
16//! no copy; or a [`VideoFrameReader`], made on its own ([`VideoFrameReaders::make_reader`]), which
17//! hands the frames to the app's GPU, as an Android app makes an `ImageReader` and an Apple app an
18//! `AVPlayerItemVideoOutput` and gives it to its player. A camera, once opened, lists the formats
19//! it runs in, and starts in one of them with its outputs given whole ([`CameraOutputs`]): a layer
20//! for its preview and a reader for its frames, made the same way, beside its photos and
21//! recordings, all of which the system configures together, as Apple configures an
22//! `AVCaptureSession` between `beginConfiguration` and `commitConfiguration` and Android binds
23//! CameraX's use cases at once. A camera's photos are encoded by the system and its recordings
24//! encoded and written to a file by the system: the app encodes nothing.
25//!
26//! Listing the cameras, opening one and starting it answer futures, as systems that answer them
27//! later do: the web's `enumerateDevices` and `getUserMedia` answer promises, and Apple's
28//! documentation of `AVCaptureSession` has a session started on a serial queue of its own, since
29//! `startRunning` blocks until the session runs or fails.
30//!
31//! A player tells its media's tracks, plays one audio track and shows one text track or none, as
32//! every system's player does (Apple's media selection groups, Media3's track overrides); it tells
33//! what the media's tags say ([`MediaMetadata`]); and it hands the app the [`Cue`]s of its text
34//! track to draw, the system drawing none, as most players draw their own (Media3's
35//! `SubtitleView`, Safari over AVFoundation's legible output). A cue carries the media's own
36//! styling, and the user's caption style is a system setting beside it, which the platform's
37//! caption settings answer ([`Platform::caption_settings`](crate::Platform::caption_settings))
38//! and the app merges in as it draws, as Media3's `SubtitleView` and WebKit's cue style sheet do.
39//!
40//! A player's state tells what the system's player does, not what the app asked of it: whether
41//! it is paused, waiting or playing, and why, as Apple's `timeControlStatus` and Windows'
42//! `PlaybackState` tell it, since the system pauses a player itself (a call takes the sound,
43//! headphones are unplugged); a seek under way; what is loaded and where it can seek; the rate,
44//! volume and mute it plays with, which the system can change; and a failure with its kind, at
45//! any time. A player's position is read when the app needs it ([`MediaPlayer::position`]), as
46//! every system lets an app read it (AVPlayer's `currentTime()`, Media3's
47//! `getCurrentPosition()`, Windows' `PlaybackSession.Position`, the web's `currentTime`), and told
48//! only when it jumps (a seek landing, a loop), as the systems tell it (Apple's
49//! `AVPlayerItemTimeJumped`, Media3's `onPositionDiscontinuity`, the web's `seeked`): while it
50//! plays it moves on by itself, and an app that shows it reads it at each frame it draws.
51//!
52//! The platform answers `None` where its system has no players, no cameras or no frames. A player
53//! or a session reports its state through [`MediaPlayer::state`] and [`CaptureSession::state`],
54//! and the cameras their list through [`Cameras::cameras`], each read when the future from its
55//! `changed` resolves: the invariant a host keeps is that such a future resolves at every change
56//! of what it reads, a player's metadata and cues included, at every jump of a player's position,
57//! and when the player or session goes, so the app never waits on one for ever.
58
59use std::any::Any;
60use std::fmt;
61use std::future::Future;
62use std::path::PathBuf;
63use std::pin::Pin;
64use std::sync::Arc;
65use std::time::{Duration, Instant};
66
67use crate::{CompositionLayer, Cue, Size};
68
69/// The system's players, or `None` from [`Platform::players`](crate::Platform::players) where it
70/// has none.
71pub trait Players {
72    /// A player of the media at `url`, which loads by itself, its state saying how far it got: a
73    /// link the system fetches (`https:`, a streaming playlist), or a picked file's
74    /// [`PickedFile::url`](crate::PickedFile::url), as every system's player takes a URL. A URL
75    /// the system cannot play gives a player that failed, with the system's reason. A player of
76    /// bytes the app makes itself is for later.
77    fn open(&self, url: &str) -> Box<dyn MediaPlayer>;
78}
79
80/// One player, controlled on the app's main thread. Dropped, it stops, and its picture goes
81/// nowhere: it leaves the layer it showed on, and its reader reads nothing more.
82pub trait MediaPlayer {
83    /// Plays from where it is, at the rate last set: from the start once it has ended.
84    fn play(&self);
85
86    fn pause(&self);
87
88    /// Moves to `position`, or as near it as `tolerance` lets the system land for speed,
89    /// playing on from there if it was playing. The state tells the seek under way until it
90    /// lands; a seek outside the seekable ranges moves nothing.
91    fn seek(&self, position: Duration, tolerance: SeekTolerance);
92
93    /// How fast it plays, `1.0` the media's own speed; kept while paused, for the next play.
94    fn set_rate(&self, rate: f64);
95
96    /// How loud it plays, from `0.0`, silent, to `1.0`, the media's own volume.
97    fn set_volume(&self, volume: f64);
98
99    /// Whether its sound is off, apart from its volume.
100    fn set_muted(&self, muted: bool);
101
102    /// Whether it starts again from the beginning when it reaches the end, rather than ending.
103    fn set_looping(&self, looping: bool);
104
105    /// The player's state now.
106    fn state(&self) -> PlayerState;
107
108    /// Where it is in the media now, read from the system at each call; it moves on by itself
109    /// while the player plays, and is the target while a seek is under way, as the web's and
110    /// Media3's are.
111    fn position(&self) -> Duration;
112
113    /// Resolves once [`state`](Self::state), [`metadata`](Self::metadata) or
114    /// [`cues`](Self::cues) may answer something else than when this was called, once the
115    /// [`position`](Self::position) jumped, as a seek landing or a loop makes it, or once the
116    /// player is gone; not as the position moves on while the player plays.
117    fn changed(&self) -> MediaChangeFuture;
118
119    /// Sends the picture to `output` from now on, and nowhere it went before; `None` sends it
120    /// nowhere, as Media3's `setVideoSurface` does. The player holds the output while it sends
121    /// the picture there, as a producer holds its layer. A layer no compositor of this host made
122    /// shows nothing, and a reader no [`VideoFrameReaders`] of this host made reads nothing.
123    fn set_video_output(&self, output: Option<VideoOutput>);
124
125    /// Plays the audio track `id`, one of the state's [`tracks`](PlayerState::tracks), in place
126    /// of the one playing.
127    fn select_audio_track(&self, id: &str);
128
129    /// Shows the text track `id`, one of the state's [`tracks`](PlayerState::tracks), or none:
130    /// subtitles off. Its cues are what [`cues`](Self::cues) answers.
131    fn select_text_track(&self, id: Option<&str>);
132
133    /// What the media's tags say about it; empty until the player has read them.
134    fn metadata(&self) -> MediaMetadata;
135
136    /// The cues of the text track shown, to show now, as the media styles them: the user's
137    /// caption style is not merged in. Empty while no text track is shown.
138    fn cues(&self) -> Vec<Cue>;
139}
140
141/// Where a player sends its picture, one destination at a time.
142#[derive(Clone)]
143pub enum VideoOutput {
144    /// The system draws the picture into a layer of its own that is this layer's content.
145    Layer(Arc<dyn CompositionLayer>),
146    /// The app draws the frames this reader hands out.
147    Frames(Arc<dyn VideoFrameReader>),
148}
149
150/// What a player's media is, and what the system's player does with it.
151#[derive(Clone, Debug, PartialEq)]
152pub struct PlayerState {
153    pub status: PlayerStatus,
154    /// Whether it is paused, waiting or playing, as the system reports it.
155    pub activity: PlayerActivity,
156    /// Where a seek is taking it, until it lands; `None` while no seek is under way. A later
157    /// seek replaces the target.
158    pub seeking: Option<Duration>,
159    /// The rate it plays at while it plays, `1.0` the media's own speed, kept while paused for
160    /// the next play; as the system reports it, which can differ from the rate asked for.
161    pub rate: f64,
162    /// How loud it plays, from `0.0`, silent, to `1.0`, the media's own volume.
163    pub volume: f64,
164    /// Whether its sound is off, apart from its volume.
165    pub muted: bool,
166    /// How long the media is; `None` until it is known, and for a live stream.
167    pub duration: Option<Duration>,
168    /// The parts of the media loaded now, in order.
169    pub loaded: Vec<TimeRange>,
170    /// The parts it can seek into, in order: the whole media for a file, a moving window for a
171    /// live stream, none where it cannot seek.
172    pub seekable: Vec<TimeRange>,
173    /// The picture's size, in its own pixels, as it is presented: its aspect corrected; `None`
174    /// until it is known, and for media without a picture.
175    pub size: Option<Size>,
176    /// The media's tracks; empty until it is ready.
177    pub tracks: Vec<MediaTrack>,
178}
179
180impl PlayerState {
181    /// A player that is loading, paused as the app has not played it: nothing known yet.
182    pub fn loading() -> PlayerState {
183        PlayerState {
184            status: PlayerStatus::Loading,
185            activity: PlayerActivity::Paused(Some(PauseReason::Requested)),
186            seeking: None,
187            rate: 1.0,
188            volume: 1.0,
189            muted: false,
190            duration: None,
191            loaded: Vec::new(),
192            seekable: Vec::new(),
193            size: None,
194            tracks: Vec::new(),
195        }
196    }
197
198    /// A player that failed with `error`, and does nothing more.
199    pub fn failed(error: PlayerError) -> PlayerState {
200        PlayerState {
201            status: PlayerStatus::Failed(error),
202            activity: PlayerActivity::Paused(None),
203            ..PlayerState::loading()
204        }
205    }
206}
207
208/// How far a player got with its media.
209#[derive(Clone, Debug, PartialEq, Eq)]
210pub enum PlayerStatus {
211    Loading,
212    /// It can play.
213    Ready,
214    /// It cannot play, from the start or partway through, and does nothing more: Apple's failed
215    /// item and its failure to play to the end, Media3's player error, the web's `error`. It is
216    /// opened again to try again.
217    Failed(PlayerError),
218}
219
220/// What a player is doing: Apple's `timeControlStatus`, Windows' `PlaybackState`.
221#[derive(Clone, Copy, Debug, PartialEq, Eq)]
222pub enum PlayerActivity {
223    /// Not playing, and not trying to; the reason is `None` where the system does not say.
224    Paused(Option<PauseReason>),
225    /// Trying to play, and waiting; the reason is `None` where the system does not say.
226    Waiting(Option<WaitReason>),
227    Playing,
228}
229
230/// Why a player is paused.
231#[derive(Clone, Copy, Debug, PartialEq, Eq)]
232pub enum PauseReason {
233    /// The app paused it, or has not played it yet.
234    Requested,
235    /// It reached the end of media it does not loop: Apple's played-to-end, Media3's end of the
236    /// media item, the web's `ended`.
237    End,
238    /// The system took the sound for something else, a call or another app: Apple's audio
239    /// session interruption, Android's audio focus loss.
240    Interrupted,
241    /// The sound's output went, such as headphones unplugged: Android's audio becoming noisy.
242    OutputRemoved,
243    /// The app went to the background, where the system pauses its video: Apple's app
244    /// backgrounded.
245    Backgrounded,
246}
247
248/// Why a player waits to play.
249#[derive(Clone, Copy, Debug, PartialEq, Eq)]
250pub enum WaitReason {
251    /// For media data: Apple's waiting to minimize stalls, Media3's buffering, the web's
252    /// `waiting`, Windows' buffering.
253    Data,
254    /// The system judges whether data comes fast enough to play on, and Apple advises showing
255    /// no waiting yet: Apple's waiting while evaluating the buffering rate.
256    EvaluatingData,
257    /// The system holds it back for a moment: Android's playback suppressed by a short loss of
258    /// audio focus.
259    HeldBySystem,
260}
261
262/// Why a player failed.
263#[derive(Clone, Debug, PartialEq, Eq)]
264pub struct PlayerError {
265    pub kind: PlayerErrorKind,
266    /// The system's own words, for logs: not for showing as they are.
267    pub message: String,
268}
269
270/// The kind of a player's failure, which an app words for its user and acts on: the web's
271/// `MediaError` and Windows' `MediaPlayerError` share the first three, and Apple's `AVError` and
272/// Media3's error codes fall into them.
273#[derive(Clone, Copy, Debug, PartialEq, Eq)]
274pub enum PlayerErrorKind {
275    /// Reaching the media failed: a connection, a timeout, an HTTP error, a file that is not
276    /// there.
277    Network,
278    /// The media is damaged, or decoding it failed.
279    Decode,
280    /// A format or codec the system cannot play.
281    Unsupported,
282    /// The media is protected, and the system may not play it here.
283    Protected,
284    /// The system or the host does not let the player reach the source: a sandbox's rules, a
285    /// file it may not read, a link it may not fetch.
286    Refused,
287    /// Anything else the system reports.
288    Other,
289}
290
291/// A span of a player's media, from `start` to `end`.
292#[derive(Clone, Copy, Debug, PartialEq, Eq)]
293pub struct TimeRange {
294    pub start: Duration,
295    pub end: Duration,
296}
297
298/// How far from its target a seek may land, before and after it, for the system to land
299/// sooner: Apple's tolerances, Media3's `SeekParameters`. A system that seeks only exactly keeps
300/// any tolerance.
301#[derive(Clone, Copy, Debug, PartialEq, Eq)]
302pub struct SeekTolerance {
303    pub before: Duration,
304    pub after: Duration,
305}
306
307impl SeekTolerance {
308    /// Exactly at the target, which may take longer to decode to.
309    pub const EXACT: SeekTolerance = SeekTolerance {
310        before: Duration::ZERO,
311        after: Duration::ZERO,
312    };
313
314    /// Wherever the system lands soonest, such as the nearest keyframe.
315    pub const ANY: SeekTolerance = SeekTolerance {
316        before: Duration::MAX,
317        after: Duration::MAX,
318    };
319}
320
321/// One of a player's media's tracks.
322#[derive(Clone, Debug, PartialEq, Eq)]
323pub struct MediaTrack {
324    /// Names the track for this player's run.
325    pub id: String,
326    pub kind: MediaTrackKind,
327    /// A BCP 47 language tag; empty where the media gives none.
328    pub language: String,
329    /// Its name as the media gives it; empty where it gives none.
330    pub label: String,
331    /// Whether it plays now: one audio track at a time, and one text track or none.
332    pub selected: bool,
333}
334
335/// What a track carries.
336#[derive(Clone, Copy, Debug, PartialEq, Eq)]
337pub enum MediaTrackKind {
338    Audio,
339    Video,
340    /// Subtitles or captions.
341    Text,
342}
343
344/// What a player's media says about itself, from its tags; a field is `None` where it does not
345/// say.
346#[derive(Clone, Debug, Default, PartialEq, Eq)]
347pub struct MediaMetadata {
348    pub title: Option<String>,
349    pub artist: Option<String>,
350    pub album: Option<String>,
351    /// The cover image, encoded as the media holds it.
352    pub artwork: Option<EncodedImage>,
353}
354
355/// An image as its file format encodes it.
356#[derive(Clone, Debug, PartialEq, Eq)]
357pub struct EncodedImage {
358    /// Its format's MIME type, such as `image/jpeg`.
359    pub mime_type: String,
360    /// Shared, so that a player hands the same image out again without copying it.
361    pub data: Arc<[u8]>,
362}
363
364/// The system's cameras, or `None` from [`Platform::cameras`](crate::Platform::cameras) where it
365/// has none.
366pub trait Cameras {
367    /// The cameras attached now, in the system's order. Listing asks the user nothing.
368    fn cameras(&self) -> CameraListFuture;
369
370    /// Resolves once [`cameras`](Self::cameras) may answer something else than when this was
371    /// called: a camera plugged in or out, as Apple's discovery session, Android's
372    /// `CameraManager.AvailabilityCallback`, the web's `devicechange` event and Windows'
373    /// `DeviceWatcher` tell it.
374    fn changed(&self) -> MediaChangeFuture;
375
376    /// Opens `camera`, asking the user for the cameras first where the system asks and the user
377    /// has not answered, as an Android app opens Camera2's `CameraDevice` once it holds the
378    /// camera permission: [`CameraError::NotAllowed`] where the user or the system's policy does
379    /// not let the app use them, and [`CameraError::Unplugged`] for a camera no longer attached.
380    fn open(&self, camera: &Camera) -> CameraOpenFuture;
381}
382
383/// A camera, as the system lists it.
384#[derive(Clone, Debug, PartialEq, Eq)]
385pub struct Camera {
386    pub id: CameraId,
387    /// What the system calls it, which the user knows it by.
388    pub name: String,
389    pub facing: CameraFacing,
390}
391
392/// The system's name for a camera, kept while it stays attached.
393#[derive(Clone, Debug, PartialEq, Eq, Hash)]
394pub struct CameraId(pub String);
395
396/// Which way a camera looks.
397#[derive(Clone, Copy, Debug, PartialEq, Eq)]
398pub enum CameraFacing {
399    /// At the user: a phone's selfie camera, a laptop's.
400    Front,
401    /// Away from the user: a phone's main camera.
402    Back,
403    /// A camera the system cannot say of: one plugged in, or on another device.
404    External,
405}
406
407/// A camera the app opened, controlled on the app's main thread: Camera2's `CameraDevice`. A
408/// session started on it runs on after it is dropped.
409pub trait CameraDevice {
410    /// The formats the camera runs in, in the system's order, each with all its outputs: Apple's
411    /// `AVCaptureDevice.formats`, Android's stream configuration map.
412    fn formats(&self) -> Vec<CameraFormat>;
413
414    /// Starts the camera in `format`, one of [`formats`](Self::formats), feeding `outputs`, which
415    /// the system configures together, and answers the session once it runs, or is interrupted,
416    /// from which it resumes by itself. Where the camera already runs for another session and the
417    /// system runs a camera in one format at a time, the session joins that format instead, and
418    /// its [`format`](CaptureSession::format) says so. It fails for what can happen at any time:
419    /// the camera in use, unplugged, or not allowed; and for a format the camera does not list.
420    fn start(&self, format: &CameraFormat, outputs: CameraOutputs) -> CameraStartFuture;
421}
422
423/// One way a camera runs, with all its outputs.
424#[derive(Clone, Debug, PartialEq)]
425pub struct CameraFormat {
426    /// The size of its frames and its preview, in the camera's own pixels.
427    pub size: Size,
428    /// The slowest frame rate it keeps at that size, in frames a second.
429    pub min_frame_rate: f64,
430    /// The fastest frame rate it keeps at that size, in frames a second.
431    pub max_frame_rate: f64,
432    /// The sizes it takes photos at while it runs so, in pixels.
433    pub photo_sizes: Vec<Size>,
434}
435
436/// What a camera feeds, given whole to [`CameraDevice::start`]: each output the system's own, for
437/// one use. An output left out is not fed.
438#[derive(Clone, Default)]
439pub struct CameraOutputs {
440    /// The layer the preview shows on, drawn by the system into a layer of its own that is this
441    /// layer's content. The session holds it while it runs. A layer no compositor of this host
442    /// made shows nothing.
443    pub preview: Option<Arc<dyn CompositionLayer>>,
444    /// The reader the frames go to, for the app to draw. A reader no [`VideoFrameReaders`] of this
445    /// host made reads nothing.
446    pub frames: Option<Arc<dyn VideoFrameReader>>,
447    /// The size photos are taken at, one of the format's
448    /// [`photo_sizes`](CameraFormat::photo_sizes), each captured, processed and encoded by the
449    /// system. In a joined format that does not list it, photos come at the system's own size
450    /// for that format.
451    pub photos: Option<Size>,
452    /// Recordings, each encoded and written to a file by the system, with sound where `options`
453    /// ask for it, which asks the user for the microphone as the session starts.
454    pub movies: Option<MovieOptions>,
455}
456
457impl CameraOutputs {
458    /// Outputs that feed nothing.
459    pub fn new() -> CameraOutputs {
460        CameraOutputs::default()
461    }
462
463    pub fn preview(mut self, layer: Arc<dyn CompositionLayer>) -> CameraOutputs {
464        self.preview = Some(layer);
465        self
466    }
467
468    pub fn frames(mut self, reader: Arc<dyn VideoFrameReader>) -> CameraOutputs {
469        self.frames = Some(reader);
470        self
471    }
472
473    pub fn photos(mut self, size: Size) -> CameraOutputs {
474        self.photos = Some(size);
475        self
476    }
477
478    pub fn movies(mut self, options: MovieOptions) -> CameraOutputs {
479        self.movies = Some(options);
480        self
481    }
482}
483
484/// A camera running for the app, controlled on the app's main thread. Dropped, it stops for the
485/// app: its preview leaves the layer it showed on, and its reader reads nothing more.
486pub trait CaptureSession {
487    /// The format it runs in: the one picked, unless it joined the format the camera ran in for
488    /// another session.
489    fn format(&self) -> CameraFormat;
490
491    /// The session's state now.
492    fn state(&self) -> CaptureState;
493
494    /// Resolves once [`state`](Self::state) may answer something else than when this was
495    /// called, or once the session is gone.
496    fn changed(&self) -> MediaChangeFuture;
497
498    /// Takes a photo while the session runs with photos among its outputs, answering it once the
499    /// system has encoded it.
500    fn take_photo(&self) -> PhotoFuture;
501
502    /// Records to a new file at `path`, as a QuickTime movie, while the session runs with movies
503    /// among its outputs, until [`stop_recording`](Self::stop_recording); answers once the file is
504    /// written whole, or why it was not.
505    fn start_recording(&self, path: PathBuf) -> RecordingFuture;
506
507    /// Ends the recording, whose future then answers.
508    fn stop_recording(&self);
509}
510
511/// What a camera session is doing.
512#[derive(Clone, Debug, PartialEq, Eq)]
513pub enum CaptureState {
514    Running,
515    /// No frames for now, for this reason where the system gives one; it resumes by itself.
516    /// macOS gives none: Apple marks the interruption's reason unavailable there.
517    Interrupted(Option<CameraInterruption>),
518    /// For good, for this reason.
519    Ended(CameraError),
520}
521
522/// Why a camera session gives no frames for now.
523#[derive(Clone, Copy, Debug, PartialEq, Eq)]
524pub enum CameraInterruption {
525    /// Another app has the camera, as a phone allows one at a time.
526    InUseByAnotherApp,
527    /// The app is in the background.
528    Background,
529    /// The system is too hot or too busy.
530    SystemPressure,
531}
532
533/// Why a camera did not open, start or run.
534#[derive(Clone, Debug, PartialEq, Eq)]
535pub enum CameraError {
536    /// The user did not let the app use the cameras, or the system's policy does not.
537    NotAllowed,
538    /// Another app has the camera, and the system does not share it.
539    InUse,
540    /// The camera is no longer attached.
541    Unplugged,
542    /// The system failed, for its reason.
543    Failed(String),
544}
545
546impl fmt::Display for CameraError {
547    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
548        match self {
549            CameraError::NotAllowed => f.write_str("the app may not use the camera"),
550            CameraError::InUse => f.write_str("another app has the camera"),
551            CameraError::Unplugged => f.write_str("the camera is no longer attached"),
552            CameraError::Failed(reason) => write!(f, "the camera failed: {reason}"),
553        }
554    }
555}
556
557impl std::error::Error for CameraError {}
558
559/// A photo, as the system encoded it.
560#[derive(Clone, Debug, PartialEq, Eq)]
561pub struct Photo {
562    pub bytes: Vec<u8>,
563    /// The encoding's MIME type: `image/jpeg`.
564    pub mime_type: String,
565}
566
567/// What a recording asks for beyond the picture.
568#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
569pub struct MovieOptions {
570    /// Whether it records sound from the system's microphone.
571    pub sound: bool,
572}
573
574/// The system's frame readers, or `None` from
575/// [`Platform::video_frame_readers`](crate::Platform::video_frame_readers) where it hands out no
576/// frames.
577pub trait VideoFrameReaders {
578    /// A reader fed by nothing yet, which reads the frames of the source it is given to: a
579    /// player's [`set_video_output`](MediaPlayer::set_video_output), a camera's outputs
580    /// ([`CameraOutputs::frames`]). The reader goes when its last handle does: the app's, and its
581    /// source's.
582    fn make_reader(&self) -> Arc<dyn VideoFrameReader>;
583}
584
585/// The pictures of the one source a reader is given to, read from any thread as they are drawn.
586/// It takes one source at a time, as Android's `ImageReader` takes one producer: given to a second
587/// while the first still feeds it, it is refused, and the second sends its frames nowhere. A
588/// reader keeps no frame for later: one not read before the next arrives is dropped.
589///
590/// A host gets its own type back from a reader it is given by downcasting through `Any`.
591pub trait VideoFrameReader: Any + Send + Sync {
592    /// The frame to show at `display_time`, the moment the picture being drawn reaches the
593    /// screen: a player's frame for that moment of the video, as a display link asks
594    /// AVFoundation's video output for each refresh's target time, and a camera's newest. `None`
595    /// when it is the frame handed out last, or none came.
596    fn frame_for(&self, display_time: Instant) -> Option<Arc<dyn VideoFrame>>;
597}
598
599/// One picture of a video as the system hands it out, as WebCodecs' `VideoFrame`. It holds its
600/// slot in the decoder's pool until it, and every texture made of it, are dropped.
601///
602/// Each host implements it over the frames its system hands out. Platform code reaches the
603/// system's own frame by downcasting through `Any` to the type of the host that handed it out:
604/// on Apple's systems `inset-embedder-winit`'s `CoreVideoFrame`, whose `pixel_buffer` is the
605/// `CVPixelBuffer` AVFoundation handed out.
606pub trait VideoFrame: Any + Send + Sync {
607    /// The picture's width, in its own pixels.
608    fn width(&self) -> u32;
609
610    /// The picture's height, in its own pixels.
611    fn height(&self) -> u32;
612
613    /// A texture of the frame on `device`, for sampling, sharing its memory where the system can.
614    /// The texture holds the frame. `None` for a device that cannot take the frame's memory: on
615    /// macOS a wgpu device not on Metal, or one Core Video makes no texture of the frame on.
616    fn texture(&self, device: &wgpu::Device) -> Option<wgpu::Texture>;
617}
618
619/// Why a camera session's photo or recording answered nothing.
620#[derive(Clone, Debug, PartialEq, Eq)]
621pub enum MediaError {
622    /// The session is interrupted or ended.
623    NotRunning,
624    /// The system failed, for its reason.
625    Failed(String),
626}
627
628impl fmt::Display for MediaError {
629    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
630        match self {
631            MediaError::NotRunning => f.write_str("the camera session does not run"),
632            MediaError::Failed(reason) => write!(f, "the camera failed: {reason}"),
633        }
634    }
635}
636
637impl std::error::Error for MediaError {}
638
639/// What a player's, a session's, the cameras' or the caption settings' `changed` answers.
640pub type MediaChangeFuture = Pin<Box<dyn Future<Output = ()>>>;
641/// What listing the cameras answers.
642pub type CameraListFuture = Pin<Box<dyn Future<Output = Vec<Camera>>>>;
643/// What opening a camera answers.
644pub type CameraOpenFuture =
645    Pin<Box<dyn Future<Output = Result<Box<dyn CameraDevice>, CameraError>>>>;
646/// What starting a camera answers.
647pub type CameraStartFuture =
648    Pin<Box<dyn Future<Output = Result<Box<dyn CaptureSession>, CameraError>>>>;
649/// What taking a photo answers.
650pub type PhotoFuture = Pin<Box<dyn Future<Output = Result<Photo, MediaError>>>>;
651/// What a recording answers once it ends.
652pub type RecordingFuture = Pin<Box<dyn Future<Output = Result<(), MediaError>>>>;