Skip to content

Basic Audio Call

A complete example of one-to-one audio calling with connection management.

Try It Live

Run pnpm dev → Navigate to BasicCallDemo in the playground

Overview

The basic call demo shows how to:

  • Connect to a SIP server
  • Register your extension
  • Make outbound calls
  • Receive incoming calls
  • Track call state and duration

Quick Start

vue
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useSipClient, useCallSession } from 'vuesip'

// SIP connection
const { connect, disconnect, isConnected, isRegistered, error } = useSipClient()

// Call management
const { currentCall, makeCall, answer, hangup, callDuration } = useCallSession()

// Form state
const credentials = ref({
  uri: 'sip:1000@example.com',
  password: '',
  server: 'wss://sip.example.com:8089/ws',
})
const targetNumber = ref('')

// Computed
const callState = computed(() => currentCall.value?.state ?? 'idle')
const isInCall = computed(() => ['ringing', 'answered', 'connecting'].includes(callState.value))

// Actions
async function handleConnect() {
  await connect(credentials.value)
}

async function handleCall() {
  if (targetNumber.value && isRegistered.value) {
    await makeCall(targetNumber.value)
  }
}

function formatDuration(seconds: number): string {
  const mins = Math.floor(seconds / 60)
  const secs = seconds % 60
  return `${mins}:${secs.toString().padStart(2, '0')}`
}
</script>

<template>
  <div class="call-demo">
    <!-- Connection Form -->
    <section v-if="!isConnected" class="connection-form">
      <h3>Connect to SIP Server</h3>
      <input v-model="credentials.uri" placeholder="SIP URI (sip:1000@example.com)" />
      <input v-model="credentials.password" type="password" placeholder="Password" />
      <input v-model="credentials.server" placeholder="WebSocket Server" />
      <button @click="handleConnect">Connect</button>
      <p v-if="error" class="error">{{ error.message }}</p>
    </section>

    <!-- Connected State -->
    <section v-else class="call-interface">
      <div class="status">
        <span class="indicator" :class="{ active: isRegistered }"></span>
        {{ isRegistered ? 'Registered' : 'Connecting...' }}
      </div>

      <!-- Dialpad -->
      <div v-if="!isInCall" class="dialpad">
        <input
          v-model="targetNumber"
          placeholder="Enter number to call"
          @keyup.enter="handleCall"
        />
        <button @click="handleCall" :disabled="!targetNumber || !isRegistered">Call</button>
      </div>

      <!-- Active Call -->
      <div v-else class="active-call">
        <p class="call-status">{{ callState }}</p>
        <p class="duration">{{ formatDuration(callDuration) }}</p>

        <div class="call-actions">
          <button v-if="callState === 'ringing'" @click="answer" class="answer">Answer</button>
          <button @click="hangup" class="hangup">Hang Up</button>
        </div>
      </div>

      <button @click="disconnect" class="disconnect">Disconnect</button>
    </section>
  </div>
</template>

Features

  • SIP Client Connection: WebSocket-based SIP connection with TLS
  • Registration: Automatic SIP REGISTER with configurable expiry
  • Outbound Calls: Make calls to any SIP URI or phone number
  • Inbound Calls: Receive and answer incoming calls
  • Call State Tracking: Real-time call state updates
  • Duration Display: Live call timer with formatted output
  • Error Handling: Comprehensive error feedback

Key Composables

ComposablePurpose
useSipClientConnection, registration, and session management
useCallSessionCall lifecycle, state, and actions

Call States

StateDescription
idleNo active call
connectingOutbound call being established
ringingIncoming call waiting to be answered
answeredCall is active
endedCall has terminated

Error Handling

typescript
const { error } = useSipClient()

watch(error, (newError) => {
  if (newError) {
    console.error('SIP Error:', newError.code, newError.message)
    // Show user-friendly error message
  }
})

Released under the MIT License.