ビルのビデオチャットアプリケーションをVuejsと
30875 ワード
仕事の将来が完全に遠いかハイブリッドであることは、非常に明らかになりました.多くの企業は、彼らのコミュニケーションを強化して、彼らの顧客にサービスを遠隔提供するために、ツールを使うか、開発しなければなりません.
この内容はもともと公開されていた.HERE
この記事では、それはどのように簡単にVuEJS 3(TypeScriptを使用して)で100 msのSDKとGolangでNetlify機能を使用してビデオチャットアプリケーションを構築する方法を示します.TruwindCSSはスタイルに使用されます.
チュートリアルの最後に、アプリケーションのようになります.
機能 会話ができる新しい部屋をつくること 認証トークンを生成した後に部屋に加わる ミュートとオーディオとビデオの両方のローカルとリモートのピアを解除します. オーディオとビデオのオンとオフの状態に適切なユーザーインターフェイスを表示する.
必要条件
100ms.live アカウント.あなたが得る必要があります 我々は新しい部屋を作成し、Authトークンを生成するために使用されるゴランと親しみ. VUEJS 3とそのITSの公正な理解composition API . Serverless関数.私たちはNetlify functions 我々のゴングバックエンドを主催するこのブログで.必ずインストールするNetlify CLI .
プロジェクト設定 アプリケーションを作成する
アプリケーション内の新しいNetlifyアプリを初期化します.次のコマンドを実行した後にプロンプトに従ってください.
JavaScript SDKとプロジェクト依存関係をインストールします.
これに続いてinstallation guide .
加える
2つのnetlify関数を作成します.
プロジェクトのルートディレクトリの中で
部屋とトークンのための残りのAPI
私たちがAPIを持っている2つのものがあります.最初は、ユーザーが新しい部屋を作成したいときに呼び出される作成室です.番目は、ユーザーが部屋に参加したがって起動される認証トークンです.Authトークンは100 msに結合を許すように必要です.
部屋作成終点から始めましょう
CreateNomディレクトリに移動し、次のライブラリをインストールします.
終了点は次のようになります. 生成するmanagement token に
Creates a room 管理トークンと渡された部屋名を使用します. 以下を加える
今、私たちは部屋を作成するAPIを持っている、我々はまた、ユーザーがそれらに参加できるようにする必要があります.100 msが必要app token 有効な結合を許可する.GenerateAppTokenディレクトリに移動し、次のライブラリをインストールします.
次のコードは、上記のParamsを受け入れ、ビデオコールに参加するときに使用される1日の満了期間を使用してJWTトークンを返します.
次のコードを追加します
UI
UIは、ユーザーが部屋に参加するいくつかの詳細を入力し、彼らのビデオとオーディオストリームが正常にビデオチャットのための同じ部屋に参加するときに表示されるフォームで構成されています.
APIの要求をするユーティリティ機能. クリエイト
クリエイト 私たちは The
これは、ユーザーが自分のユーザー名と彼らがビデオ通話のために参加したい部屋を入力する簡単なフォームです.
The
The
我々は、ローカルおよびリモートのピアのオーディオとビデオの状態を決定するセレクタを使用します.
使用方法の説明 OnAudioChanges:ローカルピアミュート/アンミュートオーディオ時のハンドラ OnVideoChange :ローカルピアミュート/アンミュテイトビデオのときのハンドラ OnepeAuAudioChanges:リモートピアミュート/アンミュイトオーディオ時のハンドラ onPeerVideoChange :リモートピアミュート/アンミュテイトビデオのハンドラ ローカルオーディオとビデオをミュート/ミュートする機能 RenderPeers :これはセレクタセレクタを介してピアの追加と削除を検出するハンドラです.接続するすべてのピアについては、ビデオストリームは RemotePeerのために、我々は彼らとオーディオとビデオのミュート状態を購読します
私たちは
環境変数の追加
次の環境変数を
私がビデオチャットに参加しようとするたびに部屋の作成を防ぐためにデフォルトの部屋名を設定していることに注意してください.
他の人がビデオチャットに参加するには、同じ部屋の名前を使用する必要があります.
アプリケーションのテスト ローカルでアプリケーションを実行します.アプリケーションは次のポートで開きます.http://localhost:8888/
つのブラウザを開きます.つは、正規モードと他の忍者で、アプリケーションを実行するリンクを開く必要があります. あなたのユーザー名を入力し、ビデオチャットに参加.
結論
あなたはcomplete project repository はい.
私にとって、能力は単に特定の状態を購読するために100 msのSDKを非常に使いやすいです.タイプ定義は素晴らしいです、ドキュメントは単純で、非常に良い開発者経験を提供します.
私は、このチュートリアルが100 msに非常に歓迎している導入であることを望みます.ライブプラットフォームと私はあなたが構築しようとしている素晴らしいアプリケーションを楽しみにしています.
この内容はもともと公開されていた.HERE
この記事では、それはどのように簡単にVuEJS 3(TypeScriptを使用して)で100 msのSDKとGolangでNetlify機能を使用してビデオチャットアプリケーションを構築する方法を示します.TruwindCSSはスタイルに使用されます.
チュートリアルの最後に、アプリケーションのようになります.
機能
必要条件
100ms.live アカウント.あなたが得る必要があります
APP_ACCESS_KEY
and APP_SECRET
からdeveloper section ダッシュボードで.プロジェクト設定
npm init vite@latest vue-video-chat --template vue-ts
cd vue-video-chat
npm install
ntl init
これに続いてinstallation guide .
# 100ms SDKs for conferencing
npm install @100mslive/hms-video-store
# Axios for making API calls
npm install axios
# Setup tailwindcss for styling.(https://tailwindcss.com/docs/guides/vite)
# A tailwind plugin for forms
npm install @tailwindcss/forms
netlify.toml
ファイルと関数ディレクトリへのパスを追加します.# Let's tell Netlify about the directory where we'll
# keep the serverless functions
[functions]
directory = "hms-functions/"
createRoom
and generateAppToken
ディレクトリ内の名前hms-functions
. プロジェクトのルートディレクトリの中で
mkdir hms-functions
cd hms-functions
ntl functions:create --name="createRoom"
ntl functions:create --name="generateAppToken"
部屋とトークンのための残りのAPI
私たちがAPIを持っている2つのものがあります.最初は、ユーザーが新しい部屋を作成したいときに呼び出される作成室です.番目は、ユーザーが部屋に参加したがって起動される認証トークンです.Authトークンは100 msに結合を許すように必要です.
部屋作成終点から始めましょう
CreateNomディレクトリに移動し、次のライブラリをインストールします.
cd hms-functions/createRoom
go get github.com/golang-jwt/jwt/v4 v4.2.0
go get github.com/google/uuid v1.3.0
go mod tidy
このエンドポイントはroom name
部屋を作成しながら使用される入力として.100 msは、我々が名前で1つの部屋をつくることができることを確実とします.それで、私たちが次の時間それを作成しようとするならば、我々は以前と同じ部屋を得ます.我々は、両方の部屋を作成し、既存のいずれかに参加しながら私たちのUIから同じ作成終点を呼び出すことによって、この機能を使用します.終了点は次のようになります.
generateManagementToken
部屋を作成している間、認可のために使われる機能.Creates a room 管理トークンと渡された部屋名を使用します.
hms-functions/createRoom/main.go
package main
import (
"bytes"
"context"
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"strings"
"time"
"os"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/golang-jwt/jwt/v4"
"github.com/google/uuid"
)
type RequestBody struct {
Room string `json:"room"`
}
// https://docs.100ms.live/server-side/v2/foundation/authentication-and-tokens#management-token
func generateManagementToken() string {
appAccessKey := os.Getenv("APP_ACCESS_KEY")
appSecret := os.Getenv("APP_SECRET")
mySigningKey := []byte(appSecret)
expiresIn := uint32(24 * 3600)
now := uint32(time.Now().UTC().Unix())
exp := now + expiresIn
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"access_key": appAccessKey,
"type": "management",
"version": 2,
"jti": uuid.New().String(),
"iat": now,
"exp": exp,
"nbf": now,
})
// Sign and get the complete encoded token as a string using the secret
signedToken, _ := token.SignedString(mySigningKey)
return signedToken
}
func handleInternalServerError(errMessage string) (*events.APIGatewayProxyResponse, error) {
err := errors.New(errMessage)
return &events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Headers: map[string]string{"Content-Type": "application/json"},
Body: "Internal server error",
}, err
}
func handler(ctx context.Context, request events.APIGatewayProxyRequest) (*events.APIGatewayProxyResponse, error) {
var f RequestBody
managementToken := generateManagementToken()
b := []byte(request.Body)
err1 := json.Unmarshal(b, &f)
if err1 != nil {
return &events.APIGatewayProxyResponse{
StatusCode: http.StatusUnprocessableEntity,
}, errors.New("Provide room name in the request body")
}
postBody, _ := json.Marshal(map[string]interface{}{
"name": strings.ToLower(f.Room),
"active": true,
})
payload := bytes.NewBuffer(postBody)
roomUrl := os.Getenv("ROOM_URL")
method := "POST"
client := &http.Client{}
req, err := http.NewRequest(method, roomUrl, payload)
if err != nil {
return handleInternalServerError(err.Error())
}
// Add Authorization header
req.Header.Add("Authorization", "Bearer "+managementToken)
req.Header.Add("Content-Type", "application/json")
// Send HTTP request
res, err := client.Do(req)
if err != nil {
return handleInternalServerError(err.Error())
}
defer res.Body.Close()
resp, err := ioutil.ReadAll(res.Body)
if err != nil {
return handleInternalServerError(err.Error())
}
return &events.APIGatewayProxyResponse{
StatusCode: res.StatusCode,
Headers: map[string]string{"Content-Type": "application/json"},
Body: string(resp),
IsBase64Encoded: false,
}, nil
}
func main() {
// start the serverless lambda function for the API calls
lambda.Start(handler)
}
トークン生成終点今、私たちは部屋を作成するAPIを持っている、我々はまた、ユーザーがそれらに参加できるようにする必要があります.100 msが必要app token 有効な結合を許可する.GenerateAppTokenディレクトリに移動し、次のライブラリをインストールします.
cd hms-functions/generateAppToken
go get github.com/golang-jwt/jwt/v4 v4.2.0
go get github.com/google/uuid v1.3.0
go mod tidy
このエンドポイントは以下のparamsを受け入れます:user_id
: これは私たちのシステムからリファレンスユーザIDを格納するために使用されるものです.room_id
: ユーザーが参加したい部屋ID.role
: ビデオチャットに参加しながらユーザーに割り当てる役割.例えば、ホストまたはゲスト.これは、すべての権限がポストに加わることを決定します.次のコードを追加します
hms-functions/generateAppToken/main.go
:package main
import (
"context"
"encoding/json"
"errors"
"net/http"
"os"
"time"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/golang-jwt/jwt/v4"
"github.com/google/uuid"
)
type RequestBody struct {
UserId string `json:"user_id"`
RoomId string `json:"room_id"`
Role string `json:"role"`
}
func handler(ctx context.Context, request events.APIGatewayProxyRequest) (*events.APIGatewayProxyResponse, error) {
var f RequestBody
b := []byte(request.Body)
err1 := json.Unmarshal(b, &f)
if err1 != nil {
return &events.APIGatewayProxyResponse{
StatusCode: http.StatusUnprocessableEntity,
}, errors.New("Provide user_id, room_id and room in the request body")
}
appAccessKey := os.Getenv("APP_ACCESS_KEY")
appSecret := os.Getenv("APP_SECRET")
mySigningKey := []byte(appSecret)
expiresIn := uint32(24 * 3600)
now := uint32(time.Now().UTC().Unix())
exp := now + expiresIn
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"access_key": appAccessKey,
"type": "app",
"version": 2,
"room_id": f.RoomId,
"user_id": f.UserId,
"role": f.Role,
"jti": uuid.New().String(),
"iat": now,
"exp": exp,
"nbf": now,
})
// Sign and get the complete encoded token as a string using the secret
signedToken, err := token.SignedString(mySigningKey)
if err != nil {
return &events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Headers: map[string]string{"Content-Type": "application/json"},
Body: "Internal server error",
}, err
}
// return the app token so the UI can join
return &events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Headers: map[string]string{"Content-Type": "application/json"},
Body: signedToken,
IsBase64Encoded: false,
}, nil
}
func main() {
lambda.Start(handler)
}
UI
UIは、ユーザーが部屋に参加するいくつかの詳細を入力し、彼らのビデオとオーディオストリームが正常にビデオチャットのための同じ部屋に参加するときに表示されるフォームで構成されています.
APIの要求をするユーティリティ機能.
types.ts
型定義を含める// Inside the project's root directory
touch src/types.ts
// Add the following code to types.ts
export type HmsTokenResponse = {
user_id?: String;
room_id?: String;
token: String;
};
hms.ts
ユーティリティ関数を含み、100 msのSDKインスタンスを開始します.HMSReactiveStore
インスタンスを作成し、次のように作成します.hmsStore
: 現在の部屋の状態にアクセスするには、すべての部屋にある場合、そのオーディオ/ビデオがオンになっている.hmsActions
: ミュートやアンミューティングのような部屋でのアクションを行うため.FUNCTION_BASE_URL
netlify関数を打つためのベースURLです.fetchToken
: この関数は、ビデオチャットに参加するときに使用されるAuthTokenを生成した後に、部屋を作成するために使用されます.すべてのケースで簡単にするためにロールを“host”に設定します.ロールを使用すると、必要に応じてユーザーがアクセス許可を設定できます.
// this code will be in src/hms.ts
import axios from "axios";
import { HMSReactiveStore } from "@100mslive/hms-video-store";
import { HmsTokenResponse } from "./types";
const FUNCTION_BASE_URL = "/.netlify/functions";
const hmsManager = new HMSReactiveStore();
// store will be used to get any state of the room
// actions will be used to perform an action in the room
export const hmsStore = hmsManager.getStore();
export const hmsActions = hmsManager.getActions();
export const fetchToken = async (
userName: string,
roomName: string
): Promise<HmsTokenResponse | any> => {
try {
// create or fetch the room_id for the passed in room
const { data: room } = await axios.post(
`${FUNCTION_BASE_URL}/createRoom`,
{ room: roomName },
{
headers: {
"Content-Type": "application/json",
},
}
);
// Generate the app/authToken
const { data:token } = await axios.post(
`${FUNCTION_BASE_URL}/generateAppToken`,
{
user_id: userName,
room_id: room.id,
role: "host",
},
{
headers: {
"Content-Type": "application/json",
},
}
);
return token;
} catch (error: any) {
throw error;
}
};
ユーザーが詳細を入力して、ファイル内のビデオチャットに参加するフォームを追加します.join.vue
これは、ユーザーが自分のユーザー名と彼らがビデオ通話のために参加したい部屋を入力する簡単なフォームです.
joinHmsRoom
: この関数はfetchToken
方法と応答を使用して部屋に参加するhmsActions.join
メソッド.参加しているすべてのユーザは、デフォルトでミュートされたオーディオを設定しますisAudioMuted: true
.
// Add the following to src/components/Join.vue
<script setup lang="ts">
import { reactive, ref } from "vue";
import { fetchTokens, hmsActions } from "../hms";
const defaultRoomName = import.meta.env.VITE_APP_DEFAULT_ROOM;
const isLoading = ref(false);
const formData = reactive({
name: "",
room: `${defaultRoomName}`,
});
const joinHmsRoom = async () => {
try {
isLoading.value = true;
const authToken = await fetchToken(formData.name, formData.room);
hmsActions.join({
userName: formData.name,
authToken: authToken,
settings: {
isAudioMuted: true, // Join with audio muted
},
});
} catch (error) {
alert(error);
}
isLoading.value = false;
};
</script>
<template>
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div class="bg-white py-10 px-5 shadow sm:rounded-lg sm:px-10">
<form class="space-y-6" @submit.prevent="joinHmsRoom">
<div>
<label for="name" class="block text-sm font-2xl text-gray-700">
Name
</label>
<div class="mt-1">
<input
id="name"
name="name"
type="text"
autocomplete="username"
required
v-model="formData.name"
class="
appearance-none
block
w-full
px-3
py-2
border border-gray-300
rounded-md
shadow-sm
placeholder-gray-400
focus:outline-none focus:ring-indigo-500 focus:border-indigo-500
sm:text-sm
"
/>
</div>
</div>
<div>
<label for="room" class="block text-sm font-medium text-gray-700">
Room
</label>
<div class="mt-1">
<input
id="room"
name="room"
type="text"
required
disabled
v-model="formData.room"
class="
appearance-none
block
w-full
px-3
py-2
border border-gray-300
rounded-md
shadow-sm
placeholder-gray-400
focus:outline-none focus:ring-indigo-500 focus:border-indigo-500
sm:text-sm
disabled:cursor-not-allowed
"
/>
</div>
</div>
<div>
<button
type="submit"
:disabled="formData.name === '' || isLoading"
:class="{ 'cursor-not-allowed': isLoading }"
class="
w-full
flex
justify-center
py-2
px-4
border border-transparent
rounded-md
shadow-sm
text-sm
font-medium
text-white
bg-indigo-600
hover:bg-indigo-700
focus:outline-none
focus:ring-2
focus:ring-offset-2
focus:ring-indigo-500
"
>
<svg
class="animate-spin mr-3 h-5 w-5 text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
v-if="isLoading"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
></circle>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
{{ isLoading ? "Joining..." : "Join" }}
</button>
</div>
</form>
</div>
</div>
</template>
ビデオストリームが表示されるコンポーネントを作成します.conference.vue
The
hmsStore
私が以前に述べたように、ビデオチャットのために100 msによって提供されるいろいろな州が含まれています.The
subscribe
メソッドは、さまざまな状態の値を取得する非常に簡単な方法を提供します.あなたがする必要があるのは、状態を購読して、指定されたセレクタから状態変化を処理するためにハンドラー機能を付けます.hmsStore.getState
また、時間内に値を取得する状態セレクタを受け入れます.我々は、反応性が必要でない所でそれを使用しています.我々は、ローカルおよびリモートのピアのオーディオとビデオの状態を決定するセレクタを使用します.
使用方法の説明
hmsActions.attachVideo
メソッド.selectIsPeerAudioEnabled
and selectIsPeerVideoEnabled
セレクタ.検出された変更は、それぞれのUIの変更をトリガします.// Add the following to src/components/Conference.vue
<script setup lang="ts">
import { ref, reactive, onUnmounted } from "vue";
import {
selectPeers,
HMSPeer,
HMSTrackID,
selectIsLocalAudioEnabled,
selectIsLocalVideoEnabled,
selectIsPeerAudioEnabled,
selectIsPeerVideoEnabled,
} from "@100mslive/hms-video-store";
import { hmsStore, hmsActions } from "../hms";
const videoRefs: any = reactive({});
const remotePeerProps: any = reactive({});
const allPeers = ref<HMSPeer[]>([]);
const isAudioEnabled = ref(hmsStore.getState(selectIsLocalAudioEnabled));
const isVideoEnabled = ref(hmsStore.getState(selectIsLocalVideoEnabled));
enum MediaState {
isAudioEnabled = "isAudioEnabled",
isVideoEnabled = "isVideoEnabled",
}
onUnmounted(() => {
if (allPeers.value.length) leaveMeeting();
});
const leaveMeeting = () => {
hmsActions.leave();
};
const onAudioChange = (newAudioState: boolean) => {
isAudioEnabled.value = newAudioState;
};
const onVideoChange = (newVideoState: boolean) => {
isVideoEnabled.value = newVideoState;
};
const onPeerAudioChange = (isEnabled: boolean, peerId: string) => {
if (videoRefs[peerId]) {
remotePeerProps[peerId][MediaState.isAudioEnabled] = isEnabled;
}
};
const onPeerVideoChange = (isEnabled: boolean, peerId: string) => {
if (videoRefs[peerId]) {
remotePeerProps[peerId][MediaState.isVideoEnabled] = isEnabled;
}
};
const renderPeers = (peers: HMSPeer[]) => {
allPeers.value = peers;
peers.forEach((peer: HMSPeer) => {
if (videoRefs[peer.id]) {
hmsActions.attachVideo(peer.videoTrack as HMSTrackID, videoRefs[peer.id]);
// If the peer is a remote peer, attach a listener to get video and audio states
if (!peer.isLocal) {
// Set up a property to track the audio and video states of remote peer so that
if (!remotePeerProps[peer.id]) {
remotePeerProps[peer.id] = {};
}
remotePeerProps[peer.id][MediaState.isAudioEnabled] = hmsStore.getState(
selectIsPeerAudioEnabled(peer.id)
);
remotePeerProps[peer.id][MediaState.isVideoEnabled] = hmsStore.getState(
selectIsPeerVideoEnabled(peer.id)
);
// Subscribe to the audio and video changes of the remote peer
hmsStore.subscribe(
(isEnabled) => onPeerAudioChange(isEnabled, peer.id),
selectIsPeerAudioEnabled(peer.id)
);
hmsStore.subscribe(
(isEnabled) => onPeerVideoChange(isEnabled, peer.id),
selectIsPeerVideoEnabled(peer.id)
);
}
}
});
};
const toggleAudio = async () => {
const enabled = hmsStore.getState(selectIsLocalAudioEnabled);
await hmsActions.setLocalAudioEnabled(!enabled);
};
const toggleVideo = async () => {
const enabled = hmsStore.getState(selectIsLocalVideoEnabled);
await hmsActions.setLocalVideoEnabled(!enabled);
// rendering again is required for the local video to show after turning off
renderPeers(hmsStore.getState(selectPeers));
};
// HMS Listeners
hmsStore.subscribe(renderPeers, selectPeers);
hmsStore.subscribe(onAudioChange, selectIsLocalAudioEnabled);
hmsStore.subscribe(onVideoChange, selectIsLocalVideoEnabled);
</script>
<template>
<main class="mx-10 min-h-[80vh]">
<div class="grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-3 my-6">
<div v-for="peer in allPeers" :key="peer.id" class="relative">
<video
autoplay
:muted="peer.isLocal"
playsinline
class="h-full w-full object-cover"
:ref="
(el) => {
if (el) videoRefs[peer.id] = el;
}
"
></video>
<p
class="
flex
justify-center
items-center
py-1
px-2
text-sm
font-medium
bg-black bg-opacity-80
text-white
pointer-events-none
absolute
bottom-0
left-0
"
>
<span
class="inline-block w-6"
v-show="
(peer.isLocal && isAudioEnabled) ||
(!peer.isLocal &&
remotePeerProps?.[peer.id]?.[MediaState.isAudioEnabled])
"
>
<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<path
stroke="#FFF"
fill="#FFF"
d="m23 14v3a7 7 0 0 1 -14 0v-3h-2v3a9 9 0 0 0 8 8.94v2.06h-4v2h10v-2h-4v-2.06a9 9 0 0 0 8-8.94v-3z"
/>
<path
stroke="#FFF"
fill="#FFF"
d="m16 22a5 5 0 0 0 5-5v-10a5 5 0 0 0 -10 0v10a5 5 0 0 0 5 5z"
/>
<path d="m0 0h32v32h-32z" fill="none" />
</svg>
</span>
<span
class="inline-block w-6"
v-show="
(peer.isLocal && !isAudioEnabled) ||
(!peer.isLocal &&
!remotePeerProps?.[peer.id]?.[MediaState.isAudioEnabled])
"
>
<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<path
fill="#FFF"
d="m23 17a7 7 0 0 1 -11.73 5.14l1.42-1.41a5 5 0 0 0 8.31-3.73v-4.58l9-9-1.41-1.42-26.59 26.59 1.41 1.41 6.44-6.44a8.91 8.91 0 0 0 5.15 2.38v2.06h-4v2h10v-2h-4v-2.06a9 9 0 0 0 8-8.94v-3h-2z"
/>
<path
fill="#FFF"
d="m9 17.32c0-.11 0-.21 0-.32v-3h-2v3a9 9 0 0 0 .25 2.09z"
/>
<path fill="#FFF" d="m20.76 5.58a5 5 0 0 0 -9.76 1.42v8.34z" />
<path d="m0 0h32v32h-32z" fill="none" />
</svg>
</span>
<span class="inline-block">
{{ peer.isLocal ? `You (${peer.name})` : peer.name }}</span
>
</p>
<p
class="text-white text-center absolute top-1/2 right-0 left-0"
v-show="
(peer.isLocal && !isVideoEnabled) ||
(!peer.isLocal &&
!remotePeerProps?.[peer.id]?.[MediaState.isVideoEnabled])
"
>
Camera Off
</p>
</div>
</div>
<div
class="mx-auto mt-10 flex items-center justify-center"
v-if="allPeers.length"
>
<button
class="bg-teal-800 text-white rounded-md p-3 block"
@click="toggleAudio"
>
{{ isAudioEnabled ? "Mute" : "Unmute" }} Microphone
</button>
<button
class="bg-indigo-400 text-white rounded-md p-3 block mx-5"
@click="toggleVideo"
>
{{ isVideoEnabled ? "Mute" : "Unmute" }} Camera
</button>
<button
class="bg-rose-800 text-white rounded-md p-3 block"
@click="leaveMeeting"
>
Leave Meeting
</button>
</div>
<div v-else>
<p class="text-white text-center font-bold text-2xl">
Hold On!, Loading Video Tiles...
</p>
</div>
</main>
</template>
アプリケーションにコンポーネントを追加します.Vue私たちは
selectRoomStarted
結合が完了したときに知っているConference
コンポーネント.部屋が始まっていないならば、我々はJoin
コンポーネント.<script setup lang="ts">
import { ref } from "vue";
import { selectRoomStarted } from "@100mslive/hms-video-store";
import { hmsStore } from "./hms";
import Join from "./components/Join.vue";
import Conference from "./components/Conference.vue";
const isConnected = ref(false);
const onConnection = (connectionState: boolean | undefined) => {
isConnected.value = Boolean(connectionState);
};
hmsStore.subscribe(onConnection, selectRoomStarted);
</script>
<template>
<div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8">
<div class="sm:mx-auto sm:w-full sm:max-w-md">
<img
class="mx-auto block h-20 w-auto"
src="https://www.100ms.live/assets/logo.svg"
alt="100ms"
/>
<h2 class="mt-6 text-center text-3xl font-extrabold text-white">
Kofi Mupati Video Call Meeting
</h2>
</div>
<Conference v-if="isConnected" />
<Join v-else />
</div>
</template>
環境変数の追加
次の環境変数を
.env
ファイル.私がビデオチャットに参加しようとするたびに部屋の作成を防ぐためにデフォルトの部屋名を設定していることに注意してください.
他の人がビデオチャットに参加するには、同じ部屋の名前を使用する必要があります.
ROOM_URL=https://prod-in2.100ms.live/api/v2/rooms
APP_ACCESS_KEY=your_hms_app_access_key_from_dashboard
APP_SECRET=your_hms_app_secret_from_dashboard
VITE_APP_DEFAULT_ROOM=kofi_mupati_secret_room
アプリケーションのテスト
ntl dev
Visual learners can watch the application demonstration on
結論
あなたはcomplete project repository はい.
私にとって、能力は単に特定の状態を購読するために100 msのSDKを非常に使いやすいです.タイプ定義は素晴らしいです、ドキュメントは単純で、非常に良い開発者経験を提供します.
私は、このチュートリアルが100 msに非常に歓迎している導入であることを望みます.ライブプラットフォームと私はあなたが構築しようとしている素晴らしいアプリケーションを楽しみにしています.
Reference
この問題について(ビルのビデオチャットアプリケーションをVuejsと), 我々は、より多くの情報をここで見つけました https://dev.to/100mslive/building-video-chat-app-with-vuejs-and-golang-2367テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol