Skip to content

Media ​

Universal media component for video playback, video recording, audio playback, and audio recording with device management and snapshot capture.

Overview ​

VOvMedia provides a flexible media component that supports both playback and recording of video and audio streams. It can play remote media files via src prop or record from device cameras and microphones. The component includes features for device selection, snapshot capture, customizable button positioning, and comprehensive event handling.

Features:

  • Video playback and recording
  • Audio playback and recording
  • Multiple media device support with device enumeration
  • Snapshot capture (base64 or blob format)
  • Customizable button positioning and styling
  • Recording time display
  • Compact and full UI modes
  • Browser-based MediaStream API integration
  • Full TypeScript support

Usage ​

Examples
vue
<template>
  <v-container fluid>
    <v-defaults-provider :defaults="{ VCol: { class: 'pa-6' } }">
      <v-row>
        <v-col cols="12" md="6" lg="4">
          <h3>PLAYBACK VIDEO</h3>
          <v-ov-media
            video
            :autoplay="false"
            format="blob"
            src="https://lachy.id.au/dev/markup/examples/video/bus.mp4"
          />
        </v-col>
        <v-col cols="12" md="6" lg="4">
          <h3>RECORD VIDEO</h3>
          <v-ov-media
            video
            @loading="cameraLoading"
            @device="cameraDevice"
            @started="cameraStarted"
            @paused="cameraPaused"
            @resumed="cameraResumed"
            @stopped="cameraStopped"
            @snapped="cameraSnapped"
            @recorded="cameraRecorded"
            @error="cameraError"
          />
        </v-col>
        <v-col cols="12" md="6" lg="4">
          <h3>TAKE A SNAP</h3>
          <v-ov-media
            video
            snap
            format="blob"
            @loading="cameraLoading"
            @device="cameraDevice"
            @started="cameraStarted"
            @paused="cameraPaused"
            @resumed="cameraResumed"
            @stopped="cameraStopped"
            @snapped="cameraSnapped"
            @recorded="cameraRecorded"
            @error="cameraError"
          />
        </v-col>
      </v-row>
      <v-row>
        <v-col cols="12" md="6" lg="4">
          <h3>PLAYBACK AUDIO FULL</h3>
          <v-ov-media
            audio
            :autoplay="false"
            src="https://onlinetestcase.com/wp-content/uploads/2023/06/100-KB-MP3.mp3"
            @loading="cameraLoading"
            @device="cameraDevice"
            @started="cameraStarted"
            @paused="cameraPaused"
            @resumed="cameraResumed"
            @stopped="cameraStopped"
            @snapped="cameraSnapped"
            @recorded="cameraRecorded"
            @error="cameraError"
          />
        </v-col>
        <v-col cols="12" md="6" lg="4">
          <h3>PLAYBACK AUDIO COMPACT</h3>
          <v-ov-media
            class="mt-4"
            audio
            compact
            :autoplay="false"
            variant="outlined"
            density="compact"
            src="https://onlinetestcase.com/wp-content/uploads/2023/06/100-KB-MP3.mp3"
            @loading="cameraLoading"
            @device="cameraDevice"
            @started="cameraStarted"
            @paused="cameraPaused"
            @resumed="cameraResumed"
            @stopped="cameraStopped"
            @snapped="cameraSnapped"
            @recorded="cameraRecorded"
            @error="cameraError"
          />
        </v-col>
        <v-col cols="12" md="6" lg="4">
          <h3>RECORD AUDIO FULL</h3>
          <v-ov-media
            audio
            @loading="cameraLoading"
            @device="cameraDevice"
            @started="cameraStarted"
            @paused="cameraPaused"
            @resumed="cameraResumed"
            @stopped="cameraStopped"
            @snapped="cameraSnapped"
            @recorded="cameraRecorded"
            @error="cameraError"
          />
        </v-col>
        <v-col cols="12" md="6" lg="4">
          <h3>RECORD AUDIO COMPACT</h3>
          <v-ov-media
            audio
            compact
            @loading="cameraLoading"
            @device="cameraDevice"
            @started="cameraStarted"
            @paused="cameraPaused"
            @resumed="cameraResumed"
            @stopped="cameraStopped"
            @snapped="cameraSnapped"
            @recorded="cameraRecorded"
            @error="cameraError"
          />
        </v-col>
      </v-row>
    </v-defaults-provider>
  </v-container>
</template>

<script setup lang="ts">
definePage({
  meta: {
    visibility: 'with-role',
    access: 'with-role',
    roles: ['developer'],
  },
})

function cameraLoading(loading: boolean) {
  console.log('camera loading', loading)
}

async function cameraDevice(deviceInfo: unknown) {
  console.log('Camera change of device', deviceInfo)
}

function cameraStarted(deviceInfo: unknown) {
  console.log('camera started', deviceInfo)
}

function cameraPaused() {
  console.log('camera paused')
}

function cameraResumed() {
  console.log('camera resumed')
}

function cameraStopped() {
  console.log('camera stopped')
}

async function cameraSnapped(snap: Blob) {
  console.log('Camera sanp', snap)
  const url = URL.createObjectURL(snap)
  const a = document.createElement('a')
  a.href = url
  a.download = 'image/png'
  a.click()
  URL.revokeObjectURL(url)
}

async function cameraRecorded(video: string | Blob) {
  if (typeof video === 'string') {
    console.log('Camera recorded base64', video)
  } else {
    console.log('Camera recorded blob', video)
    const url = URL.createObjectURL(video)
    const a = document.createElement('a')
    a.href = url
    a.download = 'recording.webm' // You can download the video as a WebM file
    a.click()
    URL.revokeObjectURL(url)
  }
}

function cameraError(error: Error) {
  console.log('camera error', error)
}
</script>

API ​

Props ​

PropTypeDefaultDescription
srcstring | nullnullMedia source URL (video or audio file)
autoplaybooleantrueAuto-play media when ready
loopbooleanfalseLoop media playback
videobooleanfalseEnable video (playback or recording)
audiobooleanfalseEnable audio (playback or recording)
recorderPosition'top-left' | 'top-right' | 'bottom-left' | 'bottom-right''bottom-left'Position of record button
snapbooleanfalseShow snapshot button (video only)
snapPosition'top-left' | 'top-right' | 'bottom-left' | 'bottom-right''top-right'Position of snapshot button
snapIconstring'$mdiCamera'Material Design icon for snapshot button
format'base64' | 'blob''base64'Format for snapshots and recordings
videoConstraintsMediaTrackConstraints{}Browser MediaStream video constraints
audioConstraintsMediaTrackConstraints{}Browser MediaStream audio constraints
compactbooleanfalseCompact UI mode (icon-only buttons, no controls text)
variant'outlined' | 'flat' | 'text' | 'elevated' | 'tonal' | 'plain''flat'Vuetify button variant
density'default' | 'comfortable' | 'compact''default'Vuetify button density

Events ​

EventPayloadDescription
loadingbooleanEmitted when starting/stopping media device access
device{ devices: MediaDeviceInfo[]; device: string }Emitted when device list is available
startedstring | { video: string; audio: string }Emitted when recording starts
paused-Emitted when recording is paused
resumed-Emitted when recording is resumed
stopped-Emitted when recording stops
snappedstring | BlobEmitted with snapshot (base64 string or Blob)
recordedstring | BlobEmitted with recording (base64 string or Blob)
errorErrorEmitted when an error occurs

Exposed Methods ​

MethodParametersReturnsDescription
listDevices()-MediaDeviceInfo[]Get list of available media devices
setDevice(deviceId: string)deviceIdvoidSwitch to specific media device
audioPlayback()-Promise<void>Toggle audio playback
videoRecording()-Promise<void>Toggle video recording

Exposed Reactive State ​

PropertyTypeDescription
isPlayingRef<boolean>Current audio playback state