> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/creator/content-creator-ko/sdk7/media/video-playing.md).

# 비디오 재생

씬으로 비디오 스트리밍

장면에서 비디오를 표시하는 방법은 세 가지가 있습니다:

* 장면 콘텐츠의 일부로 비디오 파일을 업로드하세요
* 외부 소스에서 비디오를 스트리밍하세요
* Decentraland Cast를 통해 라이브 스트리밍하세요

{% hint style="info" %}
**💡 팁**: In the [Creator Hub의 씬 편집기](/creator/content-creator-ko/scene-editor/get-started/about-editor.md), 다음을 사용할 수 있습니다 **비디오 플레이어** [스마트 아이템](/creator/content-creator-ko/scene-editor/interactivity/smart-items.md) 이를 구현하는 노코드 방식입니다.
{% endhint %}

모든 경우에 다음이 필요합니다:

* 다음이 적용된 엔티티: [기본 도형](/creator/content-creator-ko/sdk7/3d/shape-components.md) 플레인, 큐브, 심지어 원뿔과 같은 것.
* 하나의 [material](/creator/content-creator-ko/sdk7/3d/materials.md) 와 함께 `VideoTexture` 그 텍스처에 할당된
* 하나의 `VideoPlayer` 비디오 상태를 제어하는 컴포넌트.

## 성능 고려 사항

스트리밍 비디오는 플레이어의 기기에 상당한 부담을 준다는 점을 기억하세요. 동시에 하나 이상의 비디오를 재생하지 않는 것이 좋습니다.

장면에서 너무 많은 비디오가 동시에 재생되면 일부는 엔진에 의해 일시 중지됩니다. 화면 일시 중지 우선순위는 플레이어와의 거리, 크기, 화면이 플레이어의 시야 안에 있는지 여부 등 여러 요소를 기준으로 결정됩니다. 동시에 재생할 수 있는 비디오 최대 수는 플레이어의 품질 설정에 따라 달라집니다.

* 낮음: 1
* 중간: 5
* 높음: 10

또한 플레이어가 가까이 있거나 이를 위해 동작을 수행할 때 동영상 재생을 시작하는 것이 좋습니다. 씬이 먼 지평선에 로드될 때 동영상 재생을 시작하면 플레이어가 인접한 씬을 방문하는 동안 불필요하게 성능에 영향을 줍니다.

또한 매우 높은 해상도의 비디오 스트리밍은 피하세요. 다음보다 높은 것은 사용하지 마세요 *HD*.

성능 부하를 줄이기 위해, 아래의 모든 예제 스니펫과 마찬가지로 비디오는 비조명 기본(Basic) 머티리얼에서 재생하는 것이 가장 좋습니다.

## 비디오 표시

다음 지침은 세 가지 비디오 표시 옵션 모두에 적용됩니다:

1. 비디오 화면 역할을 할 엔티티를 만드세요. 이 엔티티에 다음을 부여하세요: `MeshRenderer` 컴포넌트를 추가하여 보이는 형태를 갖게 하세요.
2. 생성하세요 `VideoPlayer` 컴포넌트를 추가하고, 스트리밍 URL이나 비디오 파일 경로를 참조하도록 하세요. 여기서 비디오의 `playing` 상태와 볼륨도 설정할 수 있습니다. 이 컴포넌트는 비디오 화면 엔티티나 장면의 다른 어떤 엔티티에도 할당할 수 있습니다.
3. 생성하세요 `VideoTexture` 객체와 그 안의 `videoPlayerEntity` 속성에 다음을 소유한 엔티티를 할당하세요 `VideoPlayer` 컴포넌트를 부여해야 합니다.
4. 생성하세요 `재질`, 이를 화면 엔티티에 할당하고 그 `텍스처` 가 `VideoTexture` 방금 생성한 것입니다.

이 예제는 다음에 로컬로 저장된 비디오를 사용합니다. `/videos` 폴더입니다:

```ts
// #1
const screen = engine.addEntity()
MeshRenderer.setPlane(screen)
Transform.create(screen, { position: { x: 4, y: 1, z: 4 } })

// #2
VideoPlayer.create(screen, {
	src: 'videos/myVideo.mp4',
	playing: true,
})

// #3
const videoTexture = Material.Texture.Video({ videoPlayerEntity: screen })

// #4
Material.setBasicMaterial(screen, {
	texture: videoTexture,
})
```

외부 스트리밍 URL의 비디오를 사용하려면 2단계를 변경하여 다음이 되도록 하세요: `src` 속성 `VideoPlayer` 컴포넌트가 스트리밍 URL을 참조하도록 합니다.

```ts
// #2
VideoPlayer.create(screen, {
	src: 'https://vz-7c61c1b5-d59.b-cdn.net/ccea595a-b910-4de6-b160-092819db021d/playlist.m3u8',
	playing: true,
})
```

참고 [Decentraland Cast를 사용한 스트리밍](#streaming-using-decentraland-cast) 이 세 번째 대안 방식의 사용 방법에 대한 자세한 내용은

## 외부 스트리밍 정보

스트리밍 소스는 다음이어야 합니다 *https* URL (*http* URL은 지원되지 않습니다).

가장 인기 있는 비디오 스트리밍 사이트의 비디오를 가리키는 URL을 붙여 넣을 수 있어야 합니다. 해당 플랫폼의 서비스 약관에 유의하세요.

로컬 컴퓨터에 있는 비디오 파일에서 스트리밍하려면, 이 비디오를 공개 Google Drive에 업로드하고 링크를 붙여 넣는 것이 가장 쉽습니다.

또 다른 옵션은 다음과 같은 관리형 호스팅 제공업체를 사용하는 것입니다. [Vimeo](https://vimeo.com/) , [Livepeer Studio](https://livepeer.studio/) 또는 [Serraform](https://serraform.gitbook.io/streaming-docs/guides/decentraland-playback) 여기서는 모든 스트리밍 인프라 관리를 위해 제공업체에 비용을 지불합니다.

가장 강력한 방법은 무료 소프트웨어를 사용해 자체 서버를 구축하되, 다음과 같은 플랫폼에서 호스팅 비용을 지불하는 것입니다. [Digital Ocean](https://try.digitalocean.com/developerbrand/?_campaign=emea_brand_kw_en_cpc&_adgroup=digitalocean_exact_exact&_keyword=digitalocean&_device=c&_adposition=&_content=conversion&_medium=cpc&_source=bing\&msclkid=160bfc160a2a1bab9bbf9933594bd9c5\&utm_source=bing\&utm_medium=cpc\&utm_campaign=emea_brand_kw_en_cpc\&utm_term=digitalocean\&utm_content=DigitalOcean%20Exact_Exact) 또는 [Cloudflare](https://www.cloudflare.com/products/cloudflare-stream/). 다음과 같은 것을 배포할 수 있습니다. [Node Media Server](https://github.com/illuspas/Node-Media-Server), 기본적으로 필요한 대부분을 제공합니다.

이 모든 옵션은 각기 다른 상황에서 장단점이 있습니다. 필요 사항, 기술 수준, 예산을 고려하여 자신에게 가장 적합한 것을 평가해야 합니다.

## 성공적인 스트리밍을 위한 OBS 설정

[OBS](https://obsproject.com/) 은 스트림을 관리하는 데 널리 사용되는 무료 도구입니다.

장소의 스트림 키를 사용하든 직접 RTMP 서버를 사용하든, OBS 설정은 스트림 성공에 중요합니다. 안정적이고 일관된 연결을 목표로 해야 합니다.

### 간단한 OBS 설정

다음과 같은 간단한 설정을 권장합니다:

* 비트레이트 2500kbps(모든 Decentraland 장소에서 작동함)
* 오디오 비트레이트 160kbps
* 비디오 인코더 프리셋: Hardware NVENC
* 오디오 인코더 AAC
* 최대 해상도: 720(그보다 높으면 DCL에서 문제가 발생함)
* 프레임 속도 30fps

{% hint style="warning" %}
**📔 참고**: OBS에서 멀티트랙 스트리밍을 비활성화하세요. 이 옵션은 설정 > 스트림에서 찾을 수 있습니다.
{% endhint %}

### 새 스트리머를 위한 조언

* 사전에 소리 점검을 하는 것은 장소와의 설정을 테스트하는 데 필수적입니다.
* 스트림 키의 숫자 하나를 잘못 입력하는 것 같은 작은 오류가 스트림을 망칠 가능성이 가장 큽니다.
* 해상도 720 또는 비트레이트 2500kbps를 넘기지 마세요.

## 라이브 스트리밍

카메라에서 라이브 스트리밍하거나 화면을 공유할 수 있습니다. 다음을 사용하여 [라이브 스트리밍](/creator/content-creator-ko/scene-editor/operate-live/live-streaming.md) 의 기능 [관리 도구](/creator/content-creator-ko/scene-editor/operate-live/scene-admin.md) 스마트 아이템.

이 스트리밍 방식은 플레이어 간 라이브 통신에 사용되는 것과 동일한 통신 아키텍처를 사용합니다. 설정이 쉽고 외부 소스에서 스트리밍하는 것보다 지연이 훨씬 적습니다.

1. 스마트 아이템을 추가하고 [관리 도구](/creator/content-creator-ko/scene-editor/operate-live/scene-admin.md) 장면에 스마트 아이템을 추가하고, 또한 [비디오 플레이어](/creator/content-creator-ko/scene-editor/interactivity/video-screen.md) 스마트 아이템.
2. 장면을 World 또는 Genesis City에 게시하세요.
3. Admin 도구를 사용할 권한이 있는 플레이어로 장면에 입장하세요.
4. Admin 콘솔을 열고 다음을 선택하세요: **비디오** 탭을 선택한 다음 **라이브** 기능을 선택한 다음 **스트림 키 받기** 버튼.
5. 다음을 복사하여 **서버 URL** 및 *스트리밍 키*\*를 스트리밍 소프트웨어(예: OBS)에 입력하세요.
6. 다음을 누르세요 **활성화** 버튼을 클릭해 스트리밍을 시작할 수 있습니다.

장면에 비디오 플레이어 스마트 아이템을 추가하는 대신 URL `livekit-video://current-stream` 을 비디오 소스로 사용하여 장면에서 스트림을 재생할 수도 있습니다. 스트림 키를 얻으려면 여전히 Admin 도구가 필요합니다.

```ts
// #1
const screen = engine.addEntity()
MeshRenderer.setPlane(screen)
Transform.create(screen, { position: { x: 4, y: 1, z: 4 } })

// #2
VideoPlayer.create(screen, {
	src: `livekit-video://current-stream`,
	playing: true,
})

// #3
const videoTexture = Material.Texture.Video({ videoPlayerEntity: screen })

// #4
Material.setBasicMaterial(screen, {
	texture: videoTexture,
})
```

## 비디오 머티리얼

대부분의 경우, 비디오는 비조명 [기본 머티리얼](/creator/content-creator-ko/sdk7/3d/materials.md#unlit-materials)보다 PBR 머티리얼에서 재생하는 것보다 좋습니다. 이렇게 하면 훨씬 더 밝고 선명한 이미지가 되며 성능에도 더 좋습니다.

```ts
Material.setBasicMaterial(screen, {
	texture: videoTexture,
})
```

보통은 성능상 더 유리하므로 비디오는 비조명 기본 머티리얼에서 재생하는 것이 권장됩니다. 그러나 PBR 머티리얼 위에 비디오를 투영하려는 경우, 기본 속성만으로는 비디오가 다소 불투명하게 보일 수 있다는 점을 기억하세요. 머티리얼의 다른 속성을 조정하면 이를 개선할 수 있습니다. 비디오를 더 돋보이게 만드는 데 권장되는 설정은 다음과 같습니다:

```ts
Material.setPbrMaterial(screen, {
	texture: videoTexture,
	roughness: 1.0,
	specularIntensity: 0,
	metallic: 0,
	emissiveTexture: videoTexture,
	emissiveIntensity: 0.6,
	emissiveColor: Color3.White(),
})
```

{% hint style="info" %}
**💡 팁**: 비디오는 머티리얼에 추가되는 텍스처이므로, 색상으로 틴팅하거나 다른 텍스처 레이어를 추가하는 등 머티리얼의 다른 속성도 실험해 볼 수 있습니다. 예를 들어 더러운 화면 효과를 만들 수 있습니다.

참고 [재질](/creator/content-creator-ko/sdk7/3d/materials.md) 자세한 내용은 다음을 참조하세요.
{% endhint %}

## 동영상 파일 정보

다음 파일 형식이 지원됩니다:

* *.mp4*
* *.ogg*
* *.webm*

동영상 파일은 씬의 전체 크기에 추가되므로 플레이어가 씬에 들어올 때 씬 다운로드 시간이 더 길어진다는 점을 유의하세요. 동영상 크기로 인해 다음을 초과할 수도 있습니다 [씬 제한 사항](/creator/content-creator-ko/sdk7/optimizing/scene-limitations.md), 구획당 사용할 수 있는 최대 용량이 15MB이기 때문입니다. 문제가 줄어들도록 동영상을 가능한 한 많이 압축하는 것을 권장합니다.

플레이어가 가까이 있거나 이를 트리거하는 동작을 할 때 비디오 재생을 시작하는 것도 권장됩니다. 장면이 멀리 있는 지평선에 로드될 때 비디오를 시작하면, 플레이어가 인접한 장면을 방문하는 동안 성능에 불필요한 영향을 줄 수 있습니다.

## 비디오 시작, 일시 중지 및 중지

비디오를 재생하거나 일시 중지하려면 다음을 설정하세요: `playing` 속성을 *true* 또는 *false*. 만약 `playing` 가 false로 설정되면 비디오는 마지막으로 표시된 프레임에서 일시 중지됩니다.

아래와 같이 포인터 이벤트를 추가하여 화면을 토글 가능하게 만들 수 있습니다:

```ts
pointerEventsSystem.onPointerDown(
	{
		entity: screen,
		opts: { button: InputAction.IA_POINTER, hoverText: '재생/일시중지' },
	},
	function () {
		const videoPlayer = VideoPlayer.getMutable(screen)
		videoPlayer.playing = !videoPlayer.playing
	}
)
```

비디오를 중지하고 첫 프레임으로 되돌리려면 다음을 설정하세요: `위치` 속성을 0으로 설정합니다. 다음 예제에서는 비디오를 클릭하면 중지됩니다.

```ts
pointerEventsSystem.onPointerDown(
	{
		entity: screen,
		opts: { button: InputAction.IA_POINTER, hoverText: '정지' },
	},
	function () {
		const videoPlayer = VideoPlayer.getMutable(screen)
		videoPlayer.playing = false
		videoPlayer.position = 0
	}
)
```

## 비디오 플레이어 구성

다음 선택적 속성을 다음에 설정할 수 있습니다: `VideoPlayer` 컴포넌트:

* `playing`: 비디오가 현재 재생 중인지 여부를 결정합니다. false이면 비디오는 일시 중지됩니다.

{% hint style="warning" %}
**📔 참고**: 플레이어의 품질 설정에 따라 동시에 재생할 수 있는 비디오 수는 제한됩니다. 장면에서 그보다 더 많이 재생되면 엔진이 우선순위가 가장 낮은 비디오를 일시 중지합니다. 다음을 참조하세요: [성능 고려 사항](#performance-considerations).
{% endhint %}

* `playbackRate`: 비디오 재생 속도를 변경합니다. *1* 항목만 내보냅니다.
* `volume`: 오디오 볼륨을 변경할 수 있습니다. *1* 항목만 내보냅니다.
* `위치`: 비디오의 시작 위치를 다르게 설정할 수 있습니다. 비디오의 원래 시작 시점 이후의 초 단위로 표시됩니다. *0* 기본적으로, 이에 따라 비디오의 실제 시작 지점에서 시작합니다.
* `loop`: 비디오를 계속 반복 재생할지, 아니면 한 번 재생한 뒤 멈출지를 결정하는 불리언 값입니다. *false* 항목만 내보냅니다.

## 여러 비디오 재생

성능 문제를 피하려면 동시에 재생되는 서로 다른 비디오 수를 낮게 유지하세요. 엔진은 플레이어의 품질 설정에 따라 동시에 재생할 수 있는 비디오 수를 제한합니다(다음을 참조하세요: [성능 고려 사항](#performance-considerations)). 그러나 장면은 여러 개의 서로 다른 화면에서 같은 비디오 텍스처의 여러 복사본을 재생할 수 있습니다. 이는 별도의 비디오를 재생하는 것보다 성능에 훨씬 덜 영향을 주므로 제한되지 않습니다. 같은 비디오를 여러 엔티티에서 재생하려면, 비디오 텍스처 객체의 동일한 인스턴스를 다음에 할당하기만 하면 됩니다: `재질` 각 화면 엔티티의 컴포넌트.

```ts
// #1
const screen1 = engine.addEntity()
MeshRenderer.setPlane(screen1)
Transform.create(screen1, { position: { x: 4, y: 1, z: 4 } })

const screen2 = engine.addEntity()
MeshRenderer.setPlane(screen2)
Transform.create(screen2, { position: { x: 6, y: 1, z: 4 } })

// #2
VideoPlayer.create(screen1, {
	src: 'https://vz-7c61c1b5-d59.b-cdn.net/ccea595a-b910-4de6-b160-092819db021d/playlist.m3u8',
	playing: true,
})

// #3
const videoTexture = Material.Texture.Video({ videoPlayerEntity: screen1 })

// #4
Material.setBasicMaterial(screen1, {
	texture: videoTexture,
})

Material.setBasicMaterial(screen2, {
	texture: videoTexture,
})
```

위 예제에서는 하나만 생성하면 된다는 점에 유의하세요 `VideoPlayer` 컴포넌트가 두 비디오 화면의 상태를 모두 제어합니다. 이 경우 컴포넌트는 다음에 할당됩니다: `screen1` 엔티티입니다. 하지만 꼭 화면 중 하나일 필요는 없고, 장면의 다른 어떤 엔티티에도 할당할 수 있습니다.

## 비디오 이벤트

비디오가 재생을 시작하거나, 일시 중지되거나 하는 등의 상태 변경에 쉽게 대응할 수 있습니다. 예를 들어 비디오와 완벽하게 동기화된 애니메이션을 재생하여 비디오와 동시에 시작되도록 할 수 있습니다.

사용: `videoEventsSystem.registerVideoEventsEntity` 엔티티에 할당된 비디오의 상태가 변경될 때마다 실행되는 함수를 정의합니다. 상태가 바뀔 때마다 함수가 새 상태를 확인하고 그에 맞게 응답할 수 있습니다.

```ts
import {
	engine,
	Entity,
	VideoPlayer,
	videoEventsSystem,
	VideoState,
} from '@dcl/sdk/ecs'

// ... VideoPlayer 컴포넌트, Transform, MeshRenderer.setPlane() 등을 사용하여 videoPlayerEntity를 생성합니다. ...

videoEventsSystem.registerVideoEventsEntity(
	videoPlayerEntity,
	function (videoEvent) {
		console.log(
			'비디오 이벤트 - 상태: ' +
				videoEvent.state +
				'\n현재 오프셋:' +
				videoEvent.currentOffset +
				'\n비디오 길이:' +
				videoEvent.videoLength
		)

		switch (videoEvent.state) {
			case VideoState.VS_READY:
				console.log('비디오 이벤트 - 비디오가 준비됨')
				break
			case VideoState.VS_NONE:
				console.log('비디오 이벤트 - 비디오가 상태 없음')
				break
			case VideoState.VS_ERROR:
				console.log('비디오 이벤트 - 비디오 오류')
				break
			case VideoState.VS_SEEKING:
				console.log('비디오 이벤트 - 비디오를 탐색 중')
				break
			case VideoState.VS_LOADING:
				console.log('비디오 이벤트 - 비디오를 로드 중')
				break
			case VideoState.VS_BUFFERING:
				console.log('비디오 이벤트 - 비디오를 버퍼링 중')
				break
			case VideoState.VS_PLAYING:
				console.log('비디오 이벤트 - 비디오 재생 시작')
				break
			case VideoState.VS_PAUSED:
				console.log('비디오 이벤트 - 비디오가 일시 중지됨')
				break
		}
	}
)
```

함수의 입력으로 전달되는 videoEvent 객체에는 다음 속성이 포함됩니다:

* `currentOffset` (*number*): 비디오의 `위치` 속성의 현재 값입니다. 이 값은 비디오의 원래 시작 시점 이후의 초를 나타냅니다. *-1* 비디오가 아직 재생을 시작하지 않은 경우.
* `state`: 새 비디오 상태로, 다음 값 중 하나로 표시됩니다: `VideoState` 열거형의 값으로 표현됩니다. 이 열거형은 다음 가능한 값을 가질 수 있습니다:
  * `VideoState.VS_READY`
  * `VideoState.VS_NONE`
  * `VideoState.VS_ERROR`
  * `VideoState.VS_SEEKING`
  * `VideoState.VS_LOADING`
  * `VideoState.VS_BUFFERING`
  * `VideoState.VS_PLAYING`
  * `VideoState.VS_PAUSED`
* `videoLength` (*number* ): 전체 비디오의 길이(초)입니다. *-1* 길이를 알 수 없는 경우.
* `타임스탬프` ( *number*): *람포트* 비디오가 상태를 변경할 때마다 증가하는 타임스탬프입니다.
* `tickNumber` (*number*이벤트가 발생한 시간으로, 장면이 실행을 시작한 이후의 틱 수로 표시됩니다.

이미 콜백이 있는 엔티티에 콜백을 다시 등록하면(예: 상태 변경 시 발생하는 동작을 바꾸기 위해), 새 콜백이 이전 콜백을 대체하고 마지막으로 보고된 상태는 보존됩니다. 즉, 새 콜백은 이전 콜백에 이미 보고된 상태에 대해 다시 호출되지 않습니다.

### 엔티티가 등록되어 있는지 확인

사용: `videoEventsSystem.hasVideoEventsEntity()` 를 사용하여 엔티티에 이미 등록된 비디오 이벤트 콜백이 있는지 확인하세요.

```ts
if (videoEventsSystem.hasVideoEventsEntity(videoPlayerEntity)) {
	console.log('이 엔티티에는 이미 비디오 이벤트 리스너가 있습니다')
}
```

### 최신 비디오 이벤트

다음을 사용하여 비디오의 마지막 상태 변경을 조회하세요 `videoEventsSystem.getVideoState()`. 이를 통해 최신 `VideoEvent` 엔티티에 대한 값 또는 `undefined` 아직 이벤트가 보고되지 않은 경우. 콜백을 등록하지 않고도 시스템에서 이를 호출하여 매 프레임마다 비디오 상태를 폴링할 수 있습니다.

```ts
function mySystem() {
	const latestVideoEvent = videoEventsSystem.getVideoState(videoPlayerEntity)
	if (!latestVideoEvent) return

	console.log(`상태: ${latestVideoEvent.state}
    \n현재 오프셋: ${latestVideoEvent.currentOffset}
    \n비디오 길이: ${latestVideoEvent.videoLength}`)
}
```

## 비디오의 알파 마스크

직사각형이 아닌 비디오 화면을 만드는 깔끔한 방법은 평면 위에 알파 텍스처를 적용하는 것입니다. 평면의 일부를 원하는 어떤 모양으로든 잘라낼 수 있습니다.

다음 이미지를 사용하여 투명한 모서리가 있는 원형 모양으로 비디오를 잘라내세요.

![](https://3980763956-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-4a71dbd3206e2ccafdba8990cb5099b48c9a97cf%2Fcircle_mask.png?alt=media)

```ts
const videoTexture = Material.Texture.Video({
	videoPlayerEntity: screen,
})
const alphaMask = Material.Texture.Common({
	src: 'assets/scene/circle_mask.png',
	wrapMode: TextureWrapMode.TWM_MIRROR,
})

Material.setBasicMaterial(screen, {
	texture: videoTexture,
	alphaTexture: alphaMask,
})
```

![](https://3980763956-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-29eeafc704d19336cc3b0dc023c91e278b4bcd94%2Fcircular-video-screen.png?alt=media)

{% hint style="warning" %}
**📔 참고**: 이전 버전에서는 `alphaTexture` 속성은 PBR 머티리얼에만 있었습니다. 현재는 기본 머티리얼에서만 작동합니다.
{% endhint %}

## glTF 모델에서 비디오 재생

다음에서 비디오를 재생할 수 있습니다: *glTF* 모델에서 다음을 사용하여 [GltfNodeModifiers](/creator/content-creator-ko/sdk7/3d/materials.md#modify-gltf-materials) 컴포넌트를 사용하여 모든 플레이어를 순회하세요. 자세한 내용은 [glTF 머티리얼 수정](/creator/content-creator-ko/sdk7/3d/materials.md#modify-gltf-materials) 자세한 내용은 다음을 참조하세요.

이를 통해 평면뿐 아니라 어떤 모양에서도 비디오를 재생할 수 있습니다. 예를 들어 곡면 스크린이나 NPC의 전체 몸체에서도 비디오를 재생할 수 있습니다.

```ts
const myEntity = engine.addEntity()

GltfContainer.create(myEntity, {
	src: 'models/myModel.glb',
})

Transform.create(myEntity, {
	position: Vector3.create(4, 0, 4),
})

VideoPlayer.create(myEntity, {
	src: 'https://vz-7c61c1b5-d59.b-cdn.net/ccea595a-b910-4de6-b160-092819db021d/playlist.m3u8',
	playing: true,
})

GltfNodeModifiers.create(myEntity, {
	modifiers: [
		{
			path: '',
			material: {
				material: {
					$case: 'pbr',
					pbr: {
						texture: Material.Texture.Video({
							videoPlayerEntity: myEntity,
						}),
					},
				},
			},
		},
	],
})
```

비디오의 매핑은 모델이 사용하는 원래 UV 매핑을 따릅니다. 즉, 모델에 특정 부분에 매핑된 텍스처가 있다면 비디오도 그 동일한 부분에 매핑됩니다.

또한 다음을 사용할 수도 있습니다. `GltfNodeModifiers` 모델 내부의 특정 메시에서만 비디오를 재생하는 컴포넌트입니다. 예를 들어 모델이 건물 전체를 차지하더라도 건물의 특정 벽에서 재생할 수 있습니다. 다음을 참조하세요: [glTF 머티리얼 수정](/creator/content-creator-ko/sdk7/3d/materials.md#modify-gltf-materials) 자세한 내용은 다음을 참조하세요.

## 공간 오디오

기본적으로, 다음의 비디오는 `VideoPlayer` 컴포넌트는 전역적이어서, 장면 전체에서 일정한 볼륨으로 들립니다. 플레이어가 장면 밖으로 나가면 스트리밍 소리는 전혀 들리지 않습니다.

오디오를 공간화하려면 다음을 설정하세요: `spatial` 속성을 *true*.

```ts
VideoPlayer.create(entity, {
	src: 'https://player.vimeo.com/progressive_redirect/playback/1145666916/rendition/540p/file.mp4%20%28540p%29.mp4?loc=external&signature=db1cd6946851313cb8f7be60d1f6c30af0902bcc46fdae0ba2a06e5fdf44c329',
	playing: true,
	spatial: true,
})
```

이제 비디오는 다음을 소유한 엔티티의 위치에서 들립니다: `VideoPlayer` 컴포넌트이며, 플레이어가 가까워질수록 더 크게 들립니다.

다음 속성으로 공간 오디오를 제어할 수 있습니다:

* `spatialMinDistance`: 오디오가 공간적으로 들리기 시작하는 최소 거리입니다. 플레이어가 더 가까우면 오디오는 전체 볼륨으로 들립니다. *0* 항목만 내보냅니다.
* `spatialMaxDistance`: 오디오가 들리는 최대 거리입니다. 플레이어가 더 멀리 있으면 오디오는 0 볼륨으로 들립니다. *60* 기본적으로

```ts
const videoPlayerEntity = engine.addEntity()

Transform.create(videoPlayerEntity, {
	position: Vector3.create(8, 2, 8),
})

VideoPlayer.create(videoPlayerEntity, {
	src: 'https://player.vimeo.com/progressive_redirect/playback/1145666916/rendition/540p/file.mp4%20%28540p%29.mp4?loc=external&signature=db1cd6946851313cb8f7be60d1f6c30af0902bcc46fdae0ba2a06e5fdf44c329',
	playing: true,
	spatial: true,
	spatialMinDistance: 5,
	spatialMaxDistance: 10,
})

MeshRenderer.setPlane(videoPlayerEntity)

Material.setBasicMaterial(videoPlayerEntity, {
	texture: Material.Texture.Video({ videoPlayerEntity: videoPlayerEntity }),
})
```

{% hint style="warning" %}
**📔 참고**: 일부 비디오 형식은 공간 오디오를 지원하지 않습니다. 스트림이 다음 형식으로 인코딩되었는지 확인하세요: *mp4*, *m4a*, 또는 *mov*.
{% endhint %}

## 오디오 분석

다음의 오디오 트랙에서 실시간 진폭 및 주파수 데이터를 읽을 수 있습니다: `VideoPlayer` 엔티티를 사용해 비디오 사운드트랙과 동기화되는 반응형 비주얼을 구현할 수 있습니다. 다음을 참조하세요: [오디오 분석](/creator/content-creator-ko/sdk7/media/audio-analysis.md).

{% hint style="info" %}
**💡 팁**: 프리미티브가 아니라 GLTF 모델에서 재생되는 비디오를 보려면 다음을 참조하세요: [`74,-8-gltfnodemodifier`](https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/74,-8-gltfnodemodifier) 테스트 장면을 참조하세요. 이 장면은 다음을 사용하여 특정 GLTF 노드에 HLS 스트림을 적용합니다: `GltfNodeModifiers` 비디오 텍스처.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.decentraland.org/creator/content-creator-ko/sdk7/media/video-playing.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
