상세 컨텐츠

본문 제목

웹 설문 영상 미리보기 (video src에 사용되는 영상의 코덱)

Programming

by 쌩우 2020. 7. 21. 18:37

본문

포켓서베이 서비스가 지나온 길

포켓서베이는 대한민국 사람이라면 누구나 한번은 이용해보았고, 또 이용하고 있는 카카오톡이라는 앱을 이용하여 설문 진행이 가능하다.

덕분에 포켓서베이 이용자는 익숙한 인터페이스를 제공하면서 많은 사람들을 설문에 참여하도록 유도할 수 있다. 챗봇과 대화를 하듯이 메시지를 주고 받다 보면 어느 새 설문이 완료됐다는 메시지를 볼 수 있으니 말이다.

더불어 카카오톡은 이미 한국 사회에서 범용적이고 대중적인 이미지를 가지고 있어, 대부분의 기업에서도 큰 거부감 없이 서비스를 이용할 수 있다.

하지만, 설문 조사라는 서비스 본연의 목적에 맞추어 다양한 인터페이스를 구축하고 제공하기에는 카카오톡 챗봇이라는 시스템이 주는 한계가 분명 존재한다. 자유도가 높지 않다는 것은 앞으로의 서비스 편의 다양화에도 큰 걸림돌이 될 것임이 분명했다.

많은 사람들이 업무 시간에는 스마트폰을 가까이 하기 보다는, pc와 함께 업무를 한다. 포켓서베이는 여러가지 방법으로 설문을 배포할 수 있는데, 그 중 하나가 링크를 공유하는 것이다.

링크 주소와 카카오톡만 있으면 설문 참여가 가능한 것이다. 하지만 못내 아쉬운 점은, 업무 중 pc 환경을 떠나 스마트폰 환경으로 이동해야만 하는 불편함이 있다는 것이었다.

여기에서

"pc 환경을 유지하면서 설문에 참여할 수 있도록 한다면 설문 참여율을 한층 더 끌어올릴 수 있겠구나"

라는 발전 가능성을 찾을 수 있었다.

여기서 탄생한 서비스가 바로 "웹 설문" 기능이다.

포켓서베이 진화시키기

포켓서베이가 제공하는 설문 제작 도구에는 다양한 문항 타입이 존재하는데, 그 중 최근에 추가된 것이 있다.

바로 이미지 혹은 영상으로 설문에 응답하는 타입이다. 설문 제작자가 필요하다고 생각되는 데이터가 이미지나 영상인 경우에 이 타입으로 문항을 제작하면 된다.

모바일에서 이미지/영상 응답하기

모바일에서 해당 문항으로 제작한 설문에 참여하는 경우, 아래의 단계들을 거쳐 설문을 진행할 수 있게 된다.

  • 이미지 응답 문항 안내
"셀카를 올려주세요"라는 문항 제목과 함께 사진을 전송하라는 안내 문구가 나타난다.
  • 이미지 선택
앨범에 있던 이미지를 선택하는 경우
  • 영상 응답 문항 안내
"vlog를 올려주세요"라는 문항 제목과 함께 동영상을 전송하라는 안내 문구가 나타난다.
  • 영상 선택

이미지나 영상 파일로 유의미한 분석을 하는 이용자에게는 아주 유용한 기능일 것이다.

그럼, 웹 설문에서는?

브라우저의 파일 업로드 기능(input type file)을 사용하여 선택할 수 있도록 되어 있다.

영상 응답 문항의 모습 (이미지 응답 문항도 동일하다)

파일 업로드 버튼을 클릭하면,
아래와 같이 파일을 선택할 수 있다.

영상 응답 문항이므로, 지정된 확장자의 영상 파일만 선택 가능한 상태

정상적으로 파일을 선택한 후에는, 아래와 같이 업로드한 파일이 나타나게 된다.

선택한 파일을 파일명이 아닌 미리보기로 확인이 가능하다

이와 같이 혹시나 잘못된 파일을 올리지는 않았는지 한번 더 눈으로 직접 확인하고 설문 제출을 할 수 있어서 설문 참여자는 부담을 덜고 설문에 응할 수 있게 된다.

다양한 영상을 업로드해보며 테스트를 진행하던 중, 아래와 같은 경우가 발생했다.

썸네일이 나타나지 않고, 플레이 시에 음성만 들린다

최근에 다양한 고효율 코덱들이 등장하면서, 브라우저가 지원하지 않는 코덱의 영상을 업로드할 경우에 영상이 제대로 나타나지 않는 것이다..!
재생 버튼을 누르면 음성은 나오지만 영상은 나타나지 않는 것이다.

테스트로 사용된 영상의 경우는, 비교적 최신 코덱인 H.265였다. 검색을 해보니 H.265 코덱의 경우 지원하는 브라우저가 거의 없었다.

출처: https://caniuse.com/#search=hevc

"그럼 어떡해야 선택한 영상 파일의 미리보기를 제공할 수 있을까?"

해결 방법

1. 썸네일만 보여주기

어차피 카카오톡에서도 영상을 업로드한 뒤 직접 재생하지 않으면 썸네일만으로 영상을 판별하게 돼 있다. 본인이 선택한 영상이 무엇인지를 확인하기 위하여 굳이 재생까지 하지 않아도 썸네일만 있으면 알 수 있을 것으로 판단됐다.

영상 파일을 업로드한 후, 썸네일 이미지를 임의의 canvas로 생성하여 video 요소 대신에 보여주고자 하는 것이 목표였다.

생각한대로 구현이 가능할지 테스트를 하기 위하여 별도의 파일로 작성을 해보았다.

https://usefulangle.com/post/46/javascript-get-video-thumbnail-image-jpeg-png

<div id="container">
    <button id="upload">파일 업로드</button> 
    <input type="file" id="file-input" accept="video/mp4" />
    <video id="video-element" controls>
        <source type="video/mp4">
    </video>
    <canvas id="canvas-element"></canvas>
    <div id="thumbnail"> 
      <a id="download" href="#">썸네일 다운로드</a>
    </div>
  <div>
      <img id="thumbnail-img"/>
  </div>
</div>

파일 업로드 버튼을 클릭하여 input에 삽입할 적절한 파일을 선택한다.

이 때에, onChange 이벤트 리스너로 영상 데이터를 읽어들일 함수(URL.createObjectURL())를 실행시킨다.
https://developer.mozilla.org/ko/docs/Web/API/URL/createObjectURL

document.querySelector("#file-input").addEventListener('change', function() {
    document.querySelector("#video-element source").setAttribute('src', URL.createObjectURL(document.querySelector("#file-input").files[0]));
  //업로드한 영상 파일의 데이터를 DOMString으로 반환하여 video src로 지정해준다
});

이 때, video 태그가 필요로 하는 src 데이터를 읽어들인 뒤에 canvas를 생성하도록 했다.

let VIDEO = document.querySelector("#video-element"),
    CANVAS = document.querySelector("#canvas-element"),
      CTX = CANVAS.getContext('2d'); //

// 영상 데이터를 읽어들인 경우에 실행할 함수 선언
VIDEO.addEventListener('loadedmetadata', function() {
// 영상의 비율(너비와 높이)을 기준으로 썸네일 이미지를 만들어준다
CANVAS.width = VIDEO.videoWidth;
CANVAS.height = VIDEO.videoHeight;
});

여기까지 해주면 canvas에 담을 썸네일 이미지 크기를 설정할 수 있게 된다.

이미지 정보를 기반으로 canvas를 그려주기만 하면 처음에 목표로 했던 썸네일 이미지를 보여줄 수 있게 된다.
CANVAS 데이터를 img의 src로 지정하여 나타내도록 한다. 브라우저에 나타냄과 동시에 썸네일을 다운로드 받는 기능도 활성화해서 두 가지 방법으로 확인해보았다.

CTX.drawImage(VIDEO, 0, 0, VIDEO.videoWidth, VIDEO.videoHeight)
documnet.querySelector("#thumbnail-img").setAttribute('src', CANVAS.toDATAURL())
document.querySelector("#download").setAttribute('href', CANVAS.toDataURL());
document.querySelector("#download").setAttribute('download', 'thumbnail.png');

그런데...

썸네일 이미지가 생성되지 않는다.

샘플 영상 파일이 잘못됐나 해서 다른 파일로도 시도를 해보았다.

정상적으로 썸네일이 생성됐다...

브라우저가 인식하지 못하는 영상 파일의 경우는 썸네일을 만들기 위한 전단계에서 아예 막히는 것이었다.
영상 데이터부터 제대로 처리하지 못하니, 썸네일 이미지를 만들기 위해서 읽어들이는 과정조차 제대로 진행하지 못했다...

다른 방법을 찾아야했다.

2. 인코딩

업로드한 파일의 코덱을 판별해서 해당 브라우저가 지원하지 않는 형식이라면 업로드 자체를 중단시키거나, 인코딩으로 코덱을 변환하는 방법이다.
이번 방법은 FFmpeg를 통하여 시도해보았다.

https://ko.wikipedia.org/wiki/FFmpeg


FFmpeg가 영상 파일의 정보를 읽고 알려주기 때문에 판별을 할 수 있게 된다.

import { createFFmpef } from '@ffmpeg/ffmepg' //ffmpeg를 사용하기 위해서 먼저 import

const ffmpeg = createFFmpeg({log: true}) //log 속성을 true로 지정시 ffmpeg가 작업하는 log를 볼 수 있게 함

console.log(ffmpeg.run('ffmpeg -show_streams -i sample.mp4') //sample.mp4의 정보를 읽어들이고 아래와 같은 형태로 보여준다
/*
[STREAM]
index=0
codec_name=h264
codec_long_name=H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10
profile=High
codec_type=video
codec_time_base=1001/48000
codec_tag_string=avc1
codec_tag=0x31637661
width=1920
height=1080
has_b_frames=0
sample_aspect_ratio=0:1
display_aspect_ratio=0:1
pix_fmt=yuv420p
level=40
timecode=N/A
is_avc=1
nal_length_size=4
id=N/A
r_frame_rate=24000/1001
avg_frame_rate=24000/1001
time_base=1/48000
start_time=0.000000
duration=252.168583
bit_rate=5617233
nb_frames=6046
nb_read_frames=N/A
nb_read_packets=N/A
TAG:creation_time=1970-01-01 00:00:00
TAG:language=und
TAG:handler_name=VideoHandler
[/STREAM]
*/

여기서 h264라는 codec_name 값을 확인할 수 있다.
만약 이 codec_name 값이 일반적으로 지원하지 않는 코덱인 경우에 추가적으로 인코딩 작업을 진행해주면 되는 것이다.

이 때에는 가장 일반적인 코덱인 h264로 인코딩을 해주었다.

import React, { useState, useEffect } from 'react'
import { createFFmpeg } from '@ffmpeg/ffmpeg'

export const VideoPlayer = props => {
const [videoSrc, setVideoSrc] = useState('')
const ffmpeg = createFFmpeg({ log: true })
const doTranscode = async () => {
await ffmpeg.load()
await ffmpeg.write(props.videoSrc.name, props.videoSrc)
await ffmpeg.run(
`-y -i ${props.videoSrc.name} -vcodec libx264 -s vga -threads 2 new${props.videoSrc.name}`
)
const data = ffmpeg.read(`new${props.videoSrc.name}`)
setVideoSrc(URL.createObjectURL(new Blob([data])))
}
useEffect(() => {
doTranscode()
}, [])
return (
<div>
<video controls src={videoSrc} />
</div>
)
}

테스트로 h265 코덱으로 촬영된 1초 정도의 10MB짜리 영상을 업로드해보았다.

인코딩까지 약 1~2분이 소요됐다. 시간도 시간이지만 또다른 문제점이 있었다.

1. 문항별 응답시간 판별
설문 참여 시 각 문항당 응답에 소요된 시간을 기록하게 되는데, 영상 인코딩에 걸리는 시간이 포함되게 된다. 물론 아주 짧은 영상과 좋은 성능의 pc로 참여하는 경우라면 응답시간에 큰 영향을 끼치지는 않을 것이다. 하지만, 대다수의 경우로 따질 경우에 그렇지 못한 쪽이 많을 것이다.

2. 다른 작업이 불가능할 정도로 리소스 잡아먹음
인코딩 중에는 pc의 성능을 모두 끌어모아 작업이 진행된다. 만약 다른 작업과 병행하여 설문에 참여하던 중이었다면, 기존 주 업무에 대한 불안정성이 존재하게 되어 위험을 감수해야 할 수도 있다.

그래서 클라이언트쪽에서 인코딩을 하도록 만드는 것이 설문이라는 서비스에 사용하기는 애매한 것으로 판단했다.

그럼 이제 또 다른 방법으로 영상을 업로드하도록 개선해야겠네..?

관련글 더보기

댓글 영역