[유니티 C#] 유튜브 영상 재생하기 (에셋 구매 없이)
https://tech5g.tistory.com/m/523
해당 영상과 포스트를 참조했습니다.
2023.06.05 - [유니티] - [유니티 C#] 한컴타자연습의 짧은 글 연습 만들기 (랜덤 문제 뽑기)
[유니티 C#] 한컴타자연습의 짧은 글 연습 만들기 (랜덤 문제 뽑기)
#랜덤 #랜덤문제뽑기 2023.06.05 - [유니티] - [유니티 C#] 다음에 올 문장 미리 보여주기 [유니티 C#] 다음에 올 문장 미리 보여주기 2023.06.02 - [유니티] - [유니티 C#] 문자열 종류 구분, 긴 글에서 현재
redasol.tistory.com
이전 포스트에서 이어집니다.
타자 연습 게임에서 배경을 흰색으로 처리했더니 너무 밍밍한 느낌이 들었다.
배경 이미지를 넣어도 좋지만 움직이는 영상을 넣는다면 더 멋진 느낌이 날 것 같았다.
문제는 영상을 에셋에 포함하면 경우 영상 데이터로 인해 용량이 무거워진다는 것.....
유튜브 영상을 실시간으로 로딩해서 플레이하는 방법이 있을까 구글링했다.
대부분은 에셋을 구매하여 사용했지만 다행히 영상을 발견해서 따라해보니 스트리밍이 잘 되었다.
먼저 유니티 프로젝트에 유튜브플레이어 패키지를 설치해야한다.
관련 내용은
https://github.com/iBicha/UnityYoutubePlayer
GitHub - iBicha/UnityYoutubePlayer: Play youtube videos in Unity using youtube-dl and Unity's VideoPlayer
Play youtube videos in Unity using youtube-dl and Unity's VideoPlayer - GitHub - iBicha/UnityYoutubePlayer: Play youtube videos in Unity using youtube-dl and Unity's VideoPlayer
github.com
이 링크에 나와있지만 영어라서 어려울수도 있으므로 방법을 하나씩 설명하겠다.
1. 유니티 프로젝트에 패키지를 설치한다.
프로젝트파일 - Packages 폴더내의 manifest.json를 연다.
매니페스트 상단에 사진처럼 넣는다.
{
"scopedRegistries": [
{
"name": "iBicha",
"url": "https://registry.npmjs.com",
"scopes": [ "com.ibicha" ]
}
],
"dependencies": {
"com.ibicha.youtube-player": "2.0.2",
...
이렇게 저장하고 프로젝트로 돌아오면 패키지가 설치될 것이다.
설치 후 패키지 매니저를 보면 사진처럼 추가 된 것을 확인할 수 있다.
캔버스의 인스펙터를 사진과 같이 바꾸고 변경된 캔버스 내부를 다시 조정하자.
해당 해상도의 높이를 기준으로 사람마다 다른 해상도에 대해 자동으로 대응하게 해주는 캔버스 기능이다.
높이를 기준으로 삼은 것은 대부분의 사람들이 가로 모니터를 사용하기 때문.
높이를 기준으로 삼으므로 1080 높이가 아닌 해상도는 양쪽에 레터박스가 생기겠지만 UI가 짤리게 하지 않는다.
조정후에 비디오 플레이어와 RawImage를 추가하고
프로젝트에서 Render Texture 두 개를 생성한다.
VideoTexture, NonVideoTexture 라고 이름을 정했다.
렌더 텍스처 하나로도 가능하나 동영상이 마지막으로 재생되던 프레임의 이미지가 입력되어서 잔상이 남아 동영상이 재생되기전에 잔상이 보이는 것을 보이지 않게하려 함이다.
RawImage에 Texture에 VideoTexture라고 명명한 렌더 텍스처를 넣는다.
비디오 플레이어의 Target Texture에 방금 사용한 렌더 텍스처를 넣는다.
Aspect Ratio는 가로 화면으로 볼 것이므로 버티컬로 변경하다.
여기까지하면 일반적으로 비디오클립이나 파일의 영상을 재생할 수 있는 비디오 플레이어가 된다.
Video Player 인스펙터에서 add Component를 눌러 YoutubePlayer 스크립트를 붙이자.
이제 YoutubePlayer 기능을 사용할 준비가 되었다.
Video를 컨트롤할 스크립트를 생성하자.
작성자는 VideoCtr이라고 붙였다.
사진처럼 네임스페이스를 붙여야 YoutubePlayer 기능이 활성화된다.
// 비디오 객체에 붙인 유튜브 플레이어
public YoutubePlayer youtubePlayer;
// 비디오 객체의 비디오 플레이어
public VideoPlayer videoPlayer;
// 유튜브 동영상이 플레이 가능한지 확인하기 위한 버튼
// 위치는 화면 밖으로 날려버려 보이지않게 하자.
public Button playBtn;
// 실제 비디오가 재생될 렌더 텍스처
public RenderTexture renderTexture;
// 비디오 재생전에 아무것도 보이지 않을 임시 렌더 텍스처
public RenderTexture nonRenderTexture;
// 렌더 텍스처를 관리하는 RawImage 객체
public RawImage rawImage;
변수를 선언하고
// Start와 Update는 사용하지 않는다.
private void Awake() {
rawImage.texture = nonRenderTexture;
playBtn.interactable = false;
}
Awake 함수에서 rawImage는 임시 렌더 텍스처를 붙이고
버튼에 interactable 이라는 것이 있는데 해당 bool을 false로
public void SetYoutubeVideo(string url)
{
try
{
videoPlayer.prepareCompleted -= VideoReadyAndCompleted;
}
catch (System.Exception ex)
{
// TODO
}
try
{
videoPlayer.prepareCompleted += VideoReadyAndCompleted;
}
catch (System.Exception ex)
{
// TODO
}
playBtn.interactable = false;
youtubePlayer.youtubeUrl = url;
Prepare(youtubePlayer.youtubeUrl);
}
그리고 외부의 MainManager 또는 다른 매니저로 유튜브 링크를 넣어 부를 함수를 작성하자.
try catch 문은 비디오 플레이어의 이벤트로 사용하기 위해 사용.
비디오의 소스가 재생가능한지 아닌지 판별하게 하는 이벤트를 작성할 것이다, 등록해놓으면 자동으로 함수가 실행된다.
그리고 영상 여러개가 차례대로 재생될 것을 생각하여 이벤트를 빼고 추가하는 방법을 사용했다.
void VideoReadyAndCompleted(VideoPlayer source)
{
playBtn.interactable = source.isPrepared;
}
이벤트로 등록한 함수
비디오 플레이어에 등록된 소스(유튜브 링크로 스트리밍되는 영상)가 준비가 되는지 확인한다.
playBtn.interactable = source.isPrepared; 이 구문에서 인터렉터블 bool 변수를 이용한다.
public async void Prepare(string url = null)
{
try
{
await youtubePlayer.PrepareVideoAsync();
StartCoroutine(Play());
}
catch (System.Exception e)
{
Debug.LogWarning(e.Message);
}
}
public IEnumerator Play()
{
while(true)
{
yield return null;
if(playBtn.interactable == true)
{
rawImage.texture = renderTexture;
videoPlayer.Play();
break;
}
}
}
이벤트로 등록후 Prepare 함수에서 url을 받아 비디오를 준비한다.
await youtubePlayer.PrepareVideoAsync(); (비동기 로드) 를 이용해 영상을 받기 시작.
Play 코루틴을 실행시켜 영상이 준비가 되면 Play하도록 한다.
▶ 함수 void에 async 접두사를 붙이면 await 를 사용해야한다. ( 사용 안하면 비동기적으로 실행되지 않는다는 메세지가 나타난다. )
youtubePlayer.PrepareVideoAsync() 자체가 비동기 로드를 사용한다.
Play함수의 if문을 보면
playBtn.interactable == true 로 버튼의 인터렉터블이 변한 것을 보고 영상이 준비가 됐는지 확인하고
rawImage.texture = renderTexture; 로 rawImage의 렌더 텍스처를 변경하고 영상을 플레이한다.
public void OnDestroy() {
videoPlayer.Stop();
rawImage.texture = nonRenderTexture;
videoPlayer.prepareCompleted -= VideoReadyAndCompleted;
}
그리고 OnDestroy 를 구현해서 해당 씬 또는 비디오 플레이어가 파괴될 때를 대비했다.
만약 rawImage의 렌더텍스처를 변경하는 것 없이 코드를 작성한다면
에디터의 플레이 버튼을 눌러 영상이 재생되는 것을 확인하고 플레이 버튼을 다시 눌러 중지했을 때
사진처럼 잔상이 남아있다. (다시 플레이해도 영상이 재생되기 전에도 잔상이 남음)
렌더 텍스처를 변경했기에 이 상태에서 다시 플레이 버튼을 누르면 빈 화면만 보인다.
영상이 플레이되기 전에 잔상이 보이지 않는 원리는 다음과 같다.
프로그램이 실행될 때를 보면
비디오 플레이어와 RawImage를 이용해 UI Canvas에서 보여주는 것인데.
1. 비디오 플레이어에서 Target Texture에 붙은 렌더 텍스처에 영상의 이미지를 실시간으로 전송한다.
2. 하지만 비디오 플레이어는 UI가 아니라 화면에 보이지 않는다.
3. UI인 RawImage는 Texture에 붙어있는 텍스처 이미지를 Canvas 화면에 보여준다.
4. 비디오 플레이어가 실시간으로 그려내는 Video Texture를 RawImage에 넣으면 화면에 보여주는 원리.
5. 프로그램의 플레이가 종료할 때 마지막으로 그린 이미지가 Video Texture에 남는다.
6. 에디터든 실제 프로그램이든 다시 실행할 때 마지막 이미지가 화면에 보일 수 있다.
7. 이때 프로그램 시작할 때 비디오 플레이어가 관여하지 않는 NonVideoTexture를 RawImage에 넣으면 빈 화면으로 보여진다.
아무 그림도 안 들어간 RenderTexture는 투명한 이미지가 기본값이다.
렌더 텍스처의 size는 캔버스 크기랑 맞추자.
작성한 VideoCtr 스크립트를 VideoPlayer에 붙이고 public 인자를 설정하자.
이제 매니저에서 VideoCtr 스크립트를 불러보자
public 인자로 불렀으므로 매니저 스크립트 인스펙터에 VideoPlayer를 넣자
긴 글 연습 씬에서 테스트 했기에 해당 매니저에서 불렀다.
유튜브 링크는 주소창 또는 영상에서 마우스 오른쪽 버튼 클릭의 동영상 URL 복사 둘 다 된다
테스트해보니 잘 된다.
비디오 플레이어에 붙은 YoutubePlayer에 url이 잘 들어가고 비디오 플레이어 컴포넌트도 변한 것을 볼 수 있다.
영상을 넣고보니 화면의 문장들이 잘 보이지 않는다.
텍스트에 반투명 패널을 넣으면 잘 보일 것 같다.