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.