그룹 통화 미디어 구독
Planet Cloud 플랫폼에 그룹 통화 미디어 구독을 요청하고 미디어 데이터를 수신하기 위한 연동 방법을 설명합니다.
개요
그룹 통화 미디어 구독을 요청하려면 구독 클라이언트(WebSocket 클라이언트)를 구현해야 합니다. 그룹 통화 미디어 구독은 다음과 같이 작동합니다.
- 구독 클라이언트가 그룹 통화 미디어 구독을 요청하기 위해 Planet Cloud의 게이트웨이 서버에 인증을 요청합니다.
- 게이트웨이 서버는 인증 후 구독 토큰을 생성하여 구독 클라이언트에게 보냅니다.
- 구독 클라이언트가 Planet Cloud의 구독 서버에 그룹 통화 미디어 구독을 요청합니다.
- 구독 서버는 구독 클라이언트의 요청에 따라 설정된 WebSocket 연결을 사용하여 그룹 통화 미디어를 구독 클라이언트에게 보냅니다.
다음 그림은 그룹 통화 미디어 구독의 연동 구조를 보여줍니다.
앱 서버가 구독 클라이언트 기능을 포함하도록 구현할 수도 있습니다. 이 경우 앱 서버가 게이트웨이 서버 및 구독 서버와 직접 상호 작용하게 됩니다.
이 문서에서는 앱 서버와 구독 클라이언트가 별도로 구현되어 있다고 가정하고 연동 방법을 설명합니다.
API 호출 순서
아래 다이어그램은 그룹 통화 미디어 구독을 요청하고 미디어 데이터를 수신하기 위한 API 호출 순서를 보여줍니다.
게이트웨이 서버 API 사양
여기서는 인증을 위해 게이트웨이 서버에서 구독 토큰을 가져오는 API에 대해 설명합니다.
접근 정보
환경 | 기준 URL |
---|---|
Evaluation | https://vpnx-stn-api.line-apps-rc.com |
인증과 관련된 상세 정보는 기본 프로토콜을 참고하세요.
API 설명
구독 서버에 연결하기 위한 구독 토큰을 가져옵니다.
토큰 유효 기간은 다음과 같습니다.
환경 | 토큰 유효 기간 |
---|---|
Evaluation | 1일 |
Real | 1일 |
메서드와 엔드포인트
-
메서드: GET
-
엔드포인트
/tas/v1/gcall/{serviceId}/{roomId}/subs/token
헤더
- Authorization:
Basic {your credential}
- Content-Type: text/plain
패스 파라미터
파라미터 | 설명 |
---|---|
serviceId | 서비스 ID |
roomId | 방 ID |
쿼리
없음
응답
구독 토큰
예제
curl --location --request GET 'https://vpnx-stn-api.line-apps-rc.com/tas/v1/gcall/planetkit-inhouse/1024/subs/token' \
--header 'Authorization: Basic em45eEkxRnROYlZwYlNXWFo1U2ZCWmhucF85dHFGaXBod2h0TjdNLTdTWHlFbllQalB2dUVKRXBDdkx5OU1tRWlHUEI2clpITjdvSVA3UXVWSE5sN3BmeHlObkxlRld0dkFCT2p4MkpCbXBROFZPVXZmMU11dVZ3QWtfdGU2cUh0OFVpV250TzZaZzZqdWIteWdtRnNBOjc4YTQwN2JjOGM0NjljZjE3MjI2MWVmY2E0MWJmODBlYzY5ODM1ZTY1MDk0YTJjZjc2YzAxYWE5N2U5Y2Q1MWU=' \
--header 'Content-Type: text/plain'
eea3091e49674a13a3091e49671a13c7
발생할 수 있는 HTTP 상태 코드
발생 가능한 HTTP 상태 코드는 다음과 같습니다. 자세한 내용은 오류 처리를 참조하세요.
- 400, 401, 403, 404, 429, 500, 503
구독 서버 API 사양
여기서는 그룹 통화의 미디어 데이터 구독을 처리하는 API에 대해 설명합니다.
접근 정보
환경 | 기준 URL |
---|---|
Evaluation | wss://voipnx-rhea.line-apps-rc.com |
API 설명
session_id
를 경로 파라미터로 사용하여 WebSocket 연결을 요청합니다.
- WebSocket 연결 후 1초 이내에 유효한 메시지가 수신되지 않으면 연결이 종료됩니다.
- 구독 클라이언트는 서버로부터 PING(opcode 0x09) 메시지를 수신하면 연결을 유지하기 위해 즉시 PONG(opcode 0x0a) 메시지로 응답해야 합니다.
구독 서버는 고가용성을 지원하지 않습니다. 구독 중에 네트워크나 서버 문제로 인해 WebSocket 연결이 끊어진 경우 구독 클라이언트는 WebSocket 연결을 다시 설정하고 구독을 다시 요청해야 합니다.
메서드와 엔드포인트
-
메서드: GET
-
엔드포인트
/subs_gcall/{session-id}
제어 메시지
제어 메시지는 WebSocket의 텍스트 opcode를 사용하여 전달됩니다. 제어 메시지에는 다음과 같은 특징이 있습니다.
- JSON 형식을 사용합니다.
- 헤더와 본문으로 구성됩니다.
헤더
필드 | 데이터 유형 | 설명 |
---|---|---|
msg_id | Number | - SubscribeGroupcallMediaReq = 1- SubscribeGroupcallMediaRsp = 2- UnsubscribeGroupcallMediaReq = 3- UnsubscribeGroupcallMediaRsp = 4- AbortGroupcallMediaSubscriptionRpt = 5- ChangeGroupcallMediaSubscriptionReq = 6- ChangeGroupcallMediaSubscriptionRsp = 7- UpdateGroupcallMediaContributorsRpt = 8- NotifyGroupcallUserStatusRpt = 9 |
sess_id | String | 구독 클라이언트가 생성한 세션 ID |
SubscribeGroupcallMediaReq
(msg_id
: 1)
이 메시지는 Planet Cloud에 그룹 통화의 미디어 데이터를 구독하도록 요청합니다.
이 메시지로 요청하기 전에 Planet Cloud 게이트웨이 서버에 인증을 요청하여 subsToken
을 얻어야 합니다.
메시지 본문
필드 | 데이터 유형 | 설명 | 비고 |
---|---|---|---|
service_id | String | 서비스 ID | |
room_id | String | 방 ID | |
credential | String | Base64 인코딩된 문자열 | |
media_type | Array (String 요소) | 미디어 유형 목록, A 는 오디오를, V 는 비디오를 나타냄 | 지원 가능한 값은 다음과 같습니다. - A - A, V 비디오만 있는 모드는 지원하지 않습니다. |
stream_type | String | 수신하려는 스트림의 유형 - MIX : 요청한 대상에 대한 혼합 스트림 제공- MULTI : 요청한 대상에 대해 ID별로 별도의 스트림 제공 | media_type 에 따른 지원 여부는 다음과 같습니다.- A : MIX , MULTI - A, V : MIX |
target_type | String | 구독 대상 유형 - MAIN_ROOM : 기본 방 구독- SUBGROUP : 서브그룹 구독- TRANSLATE : 번역 방 및 서브그룹 구독 | 이 필드는 stream_type 이 MIX 일 때만 유효합니다. |
subgroup_name | String | 구독하고자 하는 서브그룹의 이름 | 이 필드는 target_type 이 SUBGROUP 일 때만 유효합니다. |
target_user_ids | Array (String 요소) | 구독하고자 하는 사용자 ID 목록 | target_user_ids 필드가 없거나 해당 필드에 항목이 없는 경우, 모든 참가자가 구독 대상입니다.target_type 이 MAIN_ROOM 인 경우 기본 방의 참가자가 선택되고, SUBGROUP 인 경우 서브그룹의 참가자가 선택됩니다.target_type 이 TRANSLATE 인 경우 target_user_ids 는 허용되지 않습니다.target_user_ids 에 설정할 수 있는 최대 요소 수는 16입니다. |
layout_type | String | 비디오 믹싱을 위한 레이아웃 유형. 자세한 정보는 레이아웃 유형을 참조하세요. | 이 필드는 media_type 에 V (비디오)가 포함될 때만 유효합니다. |
max_display_num | Number | - 그리드 뷰: 1~16 - 포커스 뷰: 1~8 - 프레젠테이션 뷰: 1~8 | 비디오에 표시할 수 있는 참가자의 최대 수는 레이아웃 유형에 따라 다르며, 그리드 뷰의 경우 최대 16입니다. |
auto_screenshare | Bool | 화면 공유 시작 시 자동으로 프레젠테이션 뷰로 전환할지 여부 | |
no_target_users_timeout | Number | target_user_ids 파라미터가 설정된 경우, 방에 대상 사용자가 없을 때 구독 세션을 유지하는 최대 시간(초 단위) | 구독 클라이언트가 이 값을 설정하지 않으면 구독 세션은 시스템 설정에 따라 종료됩니다. |
target_type
이 SUBGROUP
인 경우의 주의사항하나의 구독 세션에서는 하나의 서브그룹만 구독할 수 있습니다.
기본 방에 생성된 다른 서브그룹을 구독하려면, 새로운 구독 세션을 설정하고 해당 서브그룹에 대한 구독을 요청해야 합니다.
레이아웃 유형
layout_type
필드에 사용 가능한 값은 다음과 같습니다.
layout_type 의 값 | target_user_ids 값 존재 여부 | 설명 |
---|---|---|
TALKER_GRID_VIEW | N | 새로운 발언자가 우선인 그리드 뷰입니다. 새로운 발언자가 레이아웃에 들어가야 할 필요가 있지만 공간이 없는 경우, 가장 오랫동안 발언하지 않은 참가자가 제외됩니다. |
Y | target_user_ids 가 지정된 경우, 해당 사용자만 레이아웃에 포함됩니다. | |
ORDERED_GRID_VIEW | N | 사용 불가 |
Y | target_user_ids 요소의 순서에 따라 레이아웃 순서가 결정되는 그리드 뷰입니다. | |
TALKER_FOCUS_VIEW | N | 새로운 발언자가 우선인 포커스 뷰입니다. 새로운 발언자는 전체화면 영역에 표시되고, 이전 발언자는 첫 번째 썸네일 영역으로 이동합니다. 썸네일 뷰가 꽉 찬 경우, 가장 오랫동안 발언하지 않은 참가자가 제외됩니다. |
Y | target_user_ids 가 지정된 경우, 해당 사용자만 레이아웃에 포함됩니다.새로운 발언자는 전체화면 영역에 표시되고, 이전 발언자는 첫 번째 썸네일 영역으로 이동합니다. | |
ORDERED_FOCUS_VIEW | N | 사용 불가 |
Y | target_user_ids 요소의 순서에 따라 레이아웃 순서가 결정되는 포커스 뷰입니다.target_user_ids 의 첫 번째 사용자가 전체화면 영역에 표시되고, 이전 발언자는 첫 번째 썸네일 영역으로 이동합니다. |
예제
{
"header": {
"msg_id": 1,
"sess_id": "sid343213"
},
"body": {
"service_id": "planet",
"room_id": "RID_xlkjds",
"credential": "YW55IGNhcm5hbCBwbGVhcw==",
"media_type": ["A"],
"stream_type": "MULTI",
"target_type": "MAIN_ROOM"
}
}
{
"header": {
"msg_id": 1,
"sess_id": "sid343213"
},
"body": {
"service_id": "planet",
"room_id": "RID_xlkjds",
"credential": "YW55IGNhcm5hbCBwbGVhcw==",
"media_type": ["A", "V"],
"stream_type": "MIX",
"target_type": "MAIN_ROOM",
"layout_type": "TALKER_GRID_VIEW"
}
}
구독 요청의 인증 정보 계산
그룹 통화 미디어 구독을 요청할 때, 요청 본문에 credential
필드의 값으로 다음과 같이 계산된 값을 포함해야 합니다.
credential
값: Planet 게이트웨이 서버에서 생성된subsToken
값과 그룹 통화 관련 정보를 입력 값으로 사용하여 SHA-256 해시 알고리즘으로 계산한 값
구독 서버는 요청 본문의 credential
값을 사용하여 구독 요청이 인증되었는지 확인합니다.
입력 값
serviceID
roomID
subsToken
구현 예제 의사 코드
credential calculation
#define AUTH_DELIM "::"
#define SHA256_HASHLEN 32
typedef unsigned char SHA256_HASH[SHA256_HASHLEN];
SHA256_CTX ctx;
SHA256_HASH SHA256_HA;
SHA256_Init(&ctx);
SHA256_Update(&ctx, svc_id, strlen(svc_id));
SHA256_Update(&ctx, AUTH_DELIM, strlen(AUTH_DELIM));
SHA256_Update(&ctx, room_id, strlen(room_id));
SHA256_Update(&ctx, AUTH_DELIM, strlen(AUTH_DELIM));
SHA256_Update(&ctx, subs_token, strlen(subs_token));
SHA256_Final(SHA256_HA, &ctx)
Java 예제 코드
import java.nio.charset.StandardCharsets;
import org.apache.commons.codec.binary.Base64;
import com.google.common.hash.Hashing;
public class SubsUtils {
public static final String del = "::";
public static String makeCredential(String svcId, String roomId, String subsToken) {
byte[] hash = Hashing.sha256()
.newHasher()
.putString(svcId, StandardCharsets.UTF_8)
.putString(del, StandardCharsets.UTF_8)
.putString(roomId, StandardCharsets.UTF_8)
.putString(del, StandardCharsets.UTF_8)
.putString(subsToken, StandardCharsets.UTF_8)
.hash()
.asBytes();
String base64EncodedCredential = new String(Base64.encodeBase64(hash));
return base64EncodedCredential;
}
}
SubscribeGroupcallMediaRsp
(msg_id
: 2)
그룹 통화 미디어 구독 요청에 대한 응답 메시지입니다. 구독 서버에서 생성된 subs_id
와 전송 가능한 미디어 포맷의 세부 정보를 포함하고 있습니다. 미디어 데이터가 WebSocket의 바이너리 opcode을 통해 전송될 때는 미디어 데이터 메시지 형식으로 전달됩니다.
메시지 본문
필드 | 데이터 유형 | 설명 | 비고 |
---|---|---|---|
result | String | API의 결과 - success - fail | |
subs_id | Number | 구독 서버에 의해 생성된 구독 ID | result 가 success 일 때 값이 존재합니다. |
audio_payload_info | Array (Audio PayloadInfo Object 요소) | 오디오 패킷을 나타내는 pt (페이로드 타입) 값에 대한 세부 정보 | result 가 success 일 때 값이 존재합니다. |
video_payload_info | Array (Video PayloadInfo Object 요소) | 비디오 패킷을 나타내는 pt 값에 대한 세부 정보 | result 가 success 일 때 값이 존재합니다. |
error_code | Number | 오류 코드. 자세한 정보는 오류 코드를 참고하세요. | result 가 fail 일 때 값이 존재합니다. |
error_desc | String | 오류에 대한 세부 설명 | result 가 fail 일 때 값이 존재합니다. |
Audio PayloadInfo Object
이름 | 유형 | 설명 | 예시 |
---|---|---|---|
pt | Number | 페이로드 타입 ID | 120 |
codec | String | 오디오 코덱 이름 | "opus" |
clock_rate | Number | 클럭 레이트 | 48000 |
channels | Number | 오디오 채널 수 | 1 |
ptime | Number | 패킷화 간격 시간(밀리초) | 20 |
Video PayloadInfo Object
이름 | 유형 | 설명 | 예시 |
---|---|---|---|
pt | Number | 페이로드 타입 ID | 121 |
codec | String | 비디오 코덱 이름 | "H264" |
clock_rate | Number | 클럭 레이트 | 90000 |
예제
{
"header": {
"msg_id": 2,
"sess_id": "sid343213"
},
"body": {
"result": "success",
"subs_id": 2394872139847981,
"audio_payload_info": [
{
"pt": 120,
"codec": "opus",
"clock_rate": 48000,
"channels": 1,
"ptime": 20
}
]
}
}
{
"header": {
"msg_id": 2,
"sess_id": "sid343213"
},
"body": {
"result": "success",
"subs_id": 2394872139847981,
"audio_payload_info": [
{
"pt": 120,
"codec": "opus",
"clock_rate": 48000,
"channels": 1,
"ptime": 20
}
],
"video_payload_info": [
{
"pt": 121,
"codec": "H264",
"clock_rate": 90000
}
]
}
}
{
"header": {
"msg_id": 2,
"sess_id": "sid343213"
},
"body": {
"result": "fail",
"error_code": 100,
"error_desc": "Authentication failed"
}
}
UnsubscribeGroupcallMediaReq
(msg_id
: 3)
그룹 통화 미디어 구독 중단을 요청하는 메시지입니다.
메시지 본문
필드 | 데이터 유형 | 설명 |
---|---|---|
service_id | String | 서비스 ID |
room_id | String | 방 ID |
subs_id | Number | 구독 서버에서 생성된 구독 ID |
예제
{
"header": {
"msg_id": 3,
"sess_id": "sid343213"
},
"body": {
"service_id": "planet",
"room_id": "RID_xlkjds",
"subs_id": 2394872139847981
}
}
UnsubscribeGroupcallMediaRsp
(msg_id
: 4)
그룹 통화 미디어 구독 중단 요청에 대한 응답 메시지입니다.
메시지 본문
필드 | 데이터 유형 | 설명 | 비고 |
---|---|---|---|
result | String | API의 결과 - success - fail | |
error_code | Number | 오류 코드. 자세한 정보는 오류 코드를 참고하세요. | result 가 fail 일 때 값이 존재합니다. |
error_desc | String | 오류에 대한 세부 설명 | result 가 fail 일 때 값이 존재합니다. |
예제
{
"header": {
"msg_id": 4,
"sess_id": "sid343213"
},
"body": {
"result": "success",
}
}
AbortGroupcallMediaSubscriptionRpt
(msg_id
: 5)
Planet Cloud GroupCall 서버에서 발생한 이벤트로 인해 구독이 중단된 경우 구독 클라이언트에게 이를 알리기 위한 메시지입니다.
메시지 본문
필드 | 데이터 유형 | 설명 |
---|---|---|
error_code | Number | 오류 코드. 자세한 정보는 오류 코드를 참고하세요. |
error_desc | String | 오류에 대한 세부 설명 |
예제
{
"header": {
"msg_id": 5,
"sess_id": "sid343213"
},
"body": {
"error_code": 2,
"error_desc": "Group call has been ended"
}
}
ChangeGroupcallMediaSubscriptionReq
(msg_id
: 6)
이 메시지는 미디어 구독 정보를 변경하는 데 사용됩니다. 다음 항목을 업데이트할 수 있습니다.
- 비디오 믹싱 레이아웃 유형
메시지 본문
필드 | 데이터 유형 | 설명 | 비고 |
---|---|---|---|
service_id | String | 서비스 ID | |
room_id | String | 방 ID | |
subs_id | Number | 구독 서버에서 생성된 구독 ID | |
layout_type | String | 비디오 믹싱을 위한 레이아웃 유형 | 이 필드는 media_type 에 V (비디오)가 포함된 경우에만 유효합니다. |
예제
{
"header": {
"msg_id": 6,
"sess_id": "sid343213"
},
"body": {
"service_id": "planet",
"room_id": "RID_xlkjds",
"subs_id": 2394872139847981,
"layout_type": "TALKER_FOCUS_VIEW"
}
}
ChangeGroupcallMediaSubscriptionRsp
(msg_id
: 7)
구독 정보 변경 결과를 반환하는 메시지입니다.
메시지 본문
필드 | 데이터 유형 | 설명 | 비고 |
---|---|---|---|
result | String | API의 결과 - success - fail | |
error_code | Number | 오류 코드. 자세한 정보는 오류 코드를 참고하세요. | result 가 fail 일 때 값이 존재합니다. |
error_desc | String | 오류에 대한 세부 설명 | result 가 fail 일 때 값이 존재합니다. |
예제
{
"header": {
"msg_id": 7,
"sess_id": "sid343213"
},
"body": {
"result": "success",
}
}
UpdateGroupcallMediaContributorsRpt
(msg_id
: 8)
stream_type
이 MIX
이고 혼합 미디어에 포함된 user_id
목록에 변경이 있는 경우 업데이트된 user_id
목록이 이 메시지를 통해 구독 클라이언트에게 전달됩니다.
메시지 본문
필드 | 데이터 유형 | 설명 | 예시 |
---|---|---|---|
pt | Number | 페이로드 유형 ID | 120 (오디오) 또는 121 (비디오) |
ts | Number | RTP 타임스탬프 | |
seq | Number | RTP 시퀀스 번호 | |
user_ids | Array (String 요소) | user_id 리스트 |
예제
{
"header": {
"msg_id": 8,
"sess_id": "sid343213"
},
"body": {
"pt": 120,
"ts": 3475364964,
"seq": 30968,
"user_ids": ["user1", "user2", "user3"]
}
}
NotifyGroupcallUserStatusRpt
(msg_id
: 9)
구독 요청(SubscribeGroupcallMediaReq
) 시 target_user_ids
파라미터가 설정된 경우, 이 파라미터에 포함된 사용자가 방에 들어오거나 나갈 때 사용자의 상태 정보가 이 메시지를 통해 구독 클라이언트에게 전달됩니다.
메시지 본문
필드 | 데이터 유형 | 설명 |
---|---|---|
enter | Array (String 요소) | 방에 들어온 사용자의 ID 목록 |
leave | Array (String 요소) | 방에서 나간 사용자의 ID 목록 |
예제
{
"header": {
"msg_id": 9,
"sess_id": "sid343213"
},
"body": {
"enter": ["user1", "user2", "user3"],
"leave": ["user4", "user5"]
}
}
오류 코드
오류 코드 | 이유 | 비고 |
---|---|---|
0 | UNDEFINED | |
1 | NO_ERROR | |
2 | GROUPCALL_END | |
100 | AUTHENTICATION_FAIL | |
101 | ROOM_NOT_FOUND | |
102 | SUBSCRIPTION_NOT_SUPPORT | |
103 | INVALID_PARAMETER | |
104 | SUBSCRIPTION_NOT_FOUND | |
105 | INVALID_STATE | |
106 | INVALID_MSG_FORMAT | |
107 | SUBS_CAPA_FULL | |
200 | INTERNAL_SERVER_ERROR | |
201 | WS_DISCONNECTED_BY_PEER | |
202 | WS_PING_PONG_FAIL | |
203 | WS_INITIAL_REQUEST_TIMEOUT | |
1000 | UNKNOWN |
미디어 데이터 메시지
미디어 데이터 메시지는 WebSocket의 바이너리 opcode를 사용하여 전달되며, 요청 받은 stream_type
에 따라 아래와 같은 두 가지 유형으로 전달됩니다.
미디어 데이터 메시지 형식
미디어 데이터 메시지의 형식은 다음과 같습니다.
순서 | 속성 | 크기 | 설명 |
---|---|---|---|
1 | type | 1 바이트 | 데이터 유형 1: RTP (혼합 오디오 또는 비디오) 2: user_id 가 포함된 RTP |
2 | data | 가변 길이 |
유형 1: 혼합 오디오 또는 비디오 RTP
- 대상
stream_type
:MIX
- 대상
media_type
:A
(오디오) 또는A, V
(오디오 및 비디오) - 혼합된 오디오와 비디오는 연결된 WebSocket 세션을 통해 RTP 형식의 데이터로 전송됩니다.
- 오디오와 비디오는 전송된 RTP 헤더의
pt
(페이로드 타입)을 통해 구분됩니다.
유형 1의 미디어 데이터 메시지의 형식은 다음과 같습니다.
순서 | 속성 | 크기 |
---|---|---|
1 | 오디오 또는 비디오 RTP | 가변 길이 |
유형 2: 특정 사용자의 오디오 RTP
- 대상
stream_type
:MULTI
- 대상
media_type
:A
(오디오) - 그룹 통화에 참여하는 각 사용자의 오디오 데이터는
user_id
정보(헤더)와 RTP의 형태로 구독 클라이언트에게 전달됩니다.
유형 2의 미디어 데이터 메시지의 형식은 다음과 같습니다.
순서 | 속성 | 크기 |
---|---|---|
1 | user_id_length | 1 바이트 |
2 | user_id | 가변 길이 |
3 | 오디오 RTP | 가변 길이 |
특정 사용자(그룹 통화 참가자)의 RTP 컨텍스트(타임스탬프, 시퀀스)는 다음 경우에 재시작될 수 있습니다.
- 사용자가 일정 시간 동안 말을 멈춘 후 다시 말을 시작할 때
- 사용자가 통화 방에 들어오거나 나갔다가 다시 들어와서 말을 시작할 때