게이머들을 위한 공간, 도르의 영상 편집기 개발 과정

Sangwoo Jeon·2024년 11월 4일

안녕하세요. 도르팀의 프론트엔드 개발자 전상우입니다.

도르팀이 웹 비디오 편집기를 개발하면서 있었던 고민과 그 해결과정을 함께 나누고 싶어서 짧게 글을 적어봅니다.

배경 지식을 위한 간단한 서비스 소개

먼저, 이해를 돕기 위해서 간단하게 저희 서비스를 소개하겠습니다.

도르는 전세계 게이머들이 자신의 게임 플레이를 공유할 수 있는 공간을 만들고 있습니다.

게이머들이 자신의 플레이를 쉽고 편리하게 나눌 수 있는 방법에 대해 고민하였고, 이를 위해서는 누구나 자신의 게임 플레이를 영상으로 만들 수 있게 즉, 누구나 크리에이터가 될 수 있게 도와주어야 한다는 결론에 이르렀습니다.

이를 위해 가장 우선적으로 2가지 필수적인 툴의 개발에 집중했습니다.

  1. 자동 녹화: 유저의 게임 플레이 중 특별한 이벤트를 감지하여, 자동으로 녹화
  2. 영상 편집: 녹화된 영상을 편집하고 싶은 유저들을 위해 다양한 효과를 제공

MVP를 통해 자신의 게임 플레이를 남기고 싶은 유저들의 니즈를 확인하였고, 2023년 9월 정식 서비스를 시작하여 MAU 40만명까지 서비스를 성장을 이룰 수 있었습니다.

웹 편집기의 구조

많은 분들이 이미 프리미어 프로 등의 전문적인 편집기를 통해 영상 편집 프로그램의 형태에 익숙하시리라고 생각이 듭니다.

저희는 기본적인 영상 편집기의 구조를 차용하되, 영상 편집에 익숙하지 않은 유저들도 쉽게 접근할 수 있도록 최대한 직관적이게 구조를 설계하고자 하였습니다.

유저가 게임플레이를 하면, 영상을 불러오고. 도르팀이 제공해주는 다양한 VFX들을 사용하여 원하는대로 편집을 할 수 있습니다. 직접 편집기를 사용해보실 수 있도록 링크를 첨부합니다.

(모바일은 아쉽게도 지원하지 않고 있습니다.)
https://dor.gg/studio

대략적인 화면 구조는 크게 4가지로 구분할 수 있습니다.

  • 좌측 Nav
  • 좌측 Panel (메뉴와 History)
  • 우측 상단 Canvas
  • 우측 하단 Timeline

오늘 글에서는 Timeline과 Canvas에 보여줄 영상의 흐름을 어떻게 제어하는지에 대해 적어보려고 합니다.

유저에게 영상 보여주기

유저가 영상을 편집하기 위해서 제일 먼저 확인해야하는 것은 무엇일까요?

바로 편집할 영상이 눈에 보이는 것입니다. 현재 재생 중인 비디오를 확인할 수 있어야 시간을 조정하고 편집을 시작할 수 있으니까요.

이를 위해 WebGL을 활용한 렌더링 시스템인 Pixi.js를 도입하여 성능과 개발 효율성을 극대화할 수 있었습니다.

유저가 영상의 특정 프레임에 편집을 적용할 때, Video Element를 참조하고 있는 Pixi js의 Sprite를 조작함으로써 영상의 확대/축소, 회전, 필터 적용 등 다양하고 인터랙티브한 조작을 구현할 수 있었습니다.

가장 먼저, 영상을 불러올 수 있는 Application을 initialize해줍니다.

이 Application은 하나의 stage를 가지고 있습니다. 편집기 상에서 stage는 가장 최상위에 있는 흰 도화지와 같습니다. 여기에 4개의 Layer를 추가하였습니다.

여러개의 Layer를 넣은 이유는 영상 효과(VFX)를 제어하기 위함인데, Video Container, Overlay Container와 Application Stage에 상황에 맞추어 VFX가 들어가게 됩니다.

도화지를 만들었으면, 이제 그림을 그려야겠죠?

Video Element → Resource → Texture → Sprite

위 과정을 거쳐서 영상이 화면에 랜더링되며,

이 중 Texture를 통하여 우리가 유저에게 보여주고 싶은 영상을 GPU에 올려 랜더링할 수 있습니다.

좀 더 상세한 설명은 공식 문서를 확인해주세요. (https://pixijs.com/8.x/guides/components/textures)

아래 코드는 영상을 Pixi Application에 load하는 매서드입니다.

class DORSprite extends Sprite {
	constructor(texture?: Texture<Resource> | undefined) {
    super(texture);
  }
  
  // ...
  createVideoTexture(videoElement: HTMLVideoElement) {
    let videoResource = new VideoResource(videoElement);
    videoResource.updateFPS = FPS;

    let texture = RenderTexture.from(videoResource as unknown as TextureSource);
    // https://pixijs.download/release/docs/PIXI.html#ALPHA_MODES
    texture.baseTexture.alphaMode = ALPHA_MODES.PREMULTIPLIED_ALPHA;

    return texture;
  }
  
  // ...
}

// ...

class DORVideoSprite extends DORSprite {
	constructor(texture?: Texture<Resource> | undefined) {
    super(texture);
    this.type = 'video';
    this.fitMultiplier = 1.75;
  }
  
  // ...
   /**
   * create video sprite for videos
   */
  create({
    videoElement,
    id,
    options,
  }: {
    videoElement: HTMLVideoElement;
    id: string;
    options: SpriteOptions;
  }) {
    const texture = super.createVideoTexture(videoElement);

    let videoSprite = new DorVideoSprite(texture);
    videoSprite.getVideoElement().pause(); // 자동재생 x

    // canvasSize height 쓰는 이유 : 아래 timeline이 열렸다 닫혔다 하면서, 열린 상황에서 pixiApp의 크기가 기대보다 작게 측정
    videoSprite.width = options.width;
    videoSprite.height = options.height;

    if (typeof options.scale === 'number') {
      videoSprite.scale.x = options.scale;
      videoSprite.scale.y = options.scale;
    } else {
      videoSprite.scale.x = options.scale.x;
      videoSprite.scale.y = options.scale.y;
    }

    if (options.position?.x) videoSprite.x = options.position.x;
    if (options.position?.y) videoSprite.y = options.position.y;

    videoSprite.name = id;
    videoSprite.anchor.set(0, 0);
    videoSprite.interactive = true;

    return videoSprite;
  }
  // ...
}

이제 영상을 유저에게 보여줄 수 있게 되었습니다. 다음 단계로 유저가 영상의 시간을 조작할 수 있게 해야합니다.

시간의 흐름에 따른 화면 제어하기

서비스 개발을 시작하고 이 부분이 너무 막막하였는데요.

우선, 영상 하나부터 제대로 컨트롤하는 것에서부터 시작해보기로 하였습니다.

영상을 컨트롤한다는 것은 어떤 것을 처리한다는 말일까요?

  • 재생/정지
  • time seek
  • time update

등이 있을 겁니다.

영상 하나와 Timeline(시간의 흐름) 하나를 1:1로 연결하는 것은 크게 어렵지 않았습니다. 단순히, Video Element의 event listener에 관련 로직을 담아두면 되었으니깐요.

여기서부터 본격적인 고민이 시작되었습니다.

어떻게 여러개의 비디오가 화면에 순차적으로 제 시간에 나오게하지?

저희가 생각한 답은 하나의 Timeline Controller가 시간의 흐름과 다른 모든 비디오를 “컨트롤”하는 것이었습니다.

여기서의 컨트롤은 위의 요소들에 한 가지 핵심 요구 사항이 새롭게 추가가 되어야 했습니다.

“제 때에 화면에 비디오가 노출되어야 한다.”

이를 위해 두가지 Timeline을 만들었습니다.

  1. 시간의 흐름과 비디오의 show/hide를 관리하는 RootTimeline
  2. 각 비디오의 시간 흐름을 관리하는 VideoTimeline

그러고서는 RootTimeline에 영상이 재생되기 시작해야하는 시간대 별로 VideoTimeline을 배치하였습니다. RootTimeline은 시간의 흐름을 관찰하고 있다가 시간에 맞추어 VideoSprite의 visible property를 조작해주었습니다. 동시에 VideoTimeline은 재생하기 시작해야하는 영상의 시간에 맞추어 해당 영상의 time을 seek하고, 재생합니다.

좀 더 직관적으로 시간의 흐름에 따른 Timeline Control을 보여드리면 아래 그림과 같은 구조가 됩니다.

영상 효과(VFX) 컨트롤하기

비디오를 순차적으로 화면에 표시하는데에 성공하였으니, 이제 도르가 제공하는 특별한 영상 효과들을 추가할 차례입니다.

이를 위해서 효과를 제어하는 별도의 Timeline을 구성하였습니다.

이전의 영상 컨트롤 Timeline과 비교하여 차이점이 하나 있는데, 하나의 Effect Preset은 여러개의 개별 Effect로 구성이 되어 있다는 것입니다.

예를 들어서 설명해드리겠습니다.

위에서 볼 수 있는 영상의 앞부분에 보면 위 아래로 Letter Box가 나오면서 화면이 극적으로 포커스되는 효과가 등장합니다. 이 하나의 효과는 3개의 하위 효과들로 구성이 되어 있습니다.

  • 시네마틱 인트로
    • Letter Box In
    • Letter Box Maintain
    • Action Zoom Effect

그림으로 나타내보면 다음과 같습니다. 아래 쪽 Effect Preset A Timeline을 주목해주세요.

실제 구현한 코드는 대략적으로 다음과 같습니다.

Effect Timeline은 메서드 체이닝 패턴을 활용하여 파이프라인을 구성했습니다.

class HighlightPreset extends Preset {
	// ..
	applyCinemaFocusHighlight () {	
		this.createPresetTimeline({ id, name })
	      .controlActive()
	      .applyActionZoom()
	      .toggleOverlayShow(letterboxIn)
	      .maintainOverlayShow(letterboxIdle)
	      .addTo(this.timeline, startAt);
}
	
	// ..
}

그 외의 기능들

이렇게 가장 핵심적인 도르 영상 편집기의 구조에 대해 설명을 드렸습니다.

물론 지금 설명드린 내용은 가장 기본이 되는 구조입니다. 여기에 몇 가지 필수 기능이 더 추가가 되어 있습니다.

  • BaseTimeline에 배경음악을 조작하는 MusicTimeline
  • Canvas 상에 올라간 영상의 크기와 위치 등을 조작
  • 자막 등등

이렇게 해도 끝이 아닙니다. 유저가 편집을 마쳤다고 생각한 영상은 유저에게 보여지는 웹에 표시되는 미리보기 일뿐, 실질적인 영상 편집은 저희 영상 편집 서버를 통해 이루어져야 합니다.

마치며

아직 갈 길이 멀다고 많다고 느끼고 있습니다.

되돌리기 같은 기본적인 구현도 없고. 최적화의 문제도 남아 있어서 컴퓨터 환경에 따라 퍼포먼스 이슈가 발생하기도 합니다.

그럼에도 많은 유저분들이 불편한 사용 경험에도 불구하고 사용해주시고, 지속적인 피드백을 주고 계십니다.

그래서 더 빠르게 제품 개선을 하기 위해 팀원분들을 찾고 있습니다.

글을 읽고 조금이나마 관심이 생겼으면, 아래 링크드인을 통해 언제든지 커피챗 요청 남겨주세요.

팀원 합류가 아니더라도 웹 편집기 구현에 대한 좀 더 상세한 이야기를 듣고 싶거나, 함께 이야기해보고 싶은 내용이 있으시다면 언제든 커피챗 환영입니다!

감사합니다.

링크드인

profile
게이머들을 위한 공간, DOR를 서비스 중인 프론트엔드 개발자입니다.

1개의 댓글

comment-user-thumbnail
2024년 11월 4일

좋은 경험 공유해주셔서 감사합니다. ^^

답글 달기