2019년 1월 24일 목요일

DRM 동영상 스트리밍 서비스를 만들기 위한 모험

페이월 뒤에 일련의 동영상을 제공하는 서비스를 내가 만들 수 있는지 확인할 필요가 있었다. DRM도 포함해서.

그래서 몇 단계의 과정을 거쳐서 관련 정보를 찾아보았고, 알아가는 재미가 있었기에 브라우저에 열려있는 수많은 탭을 정리하며 여기에 그 과정을 남겨본다.


1단계 - 검색어 수집과 탐색

맨 처음이 뭐였는지는 기억나지 않는다. ffmpeg mp4 streaming 정도의 검색어로 시작했던 것 같다. ffmpeg가 이것저것 만능이라는 건 알고 있었는데, 실제로도 이런저런 검색결과가 나왔다.

2단계 - HLS라는 게 있다

검색결과에서 눈에 걸린 건 hls, rtmp 같은 용어였다.
https://www.keycdn.com/support/how-to-convert-mp4-to-hls 페이지에서는 hls 형태로 전환하는 명령이 나와 있다.
ffmpeg -i input.mp4 -profile:v baseline -level 3.0 -s 640x360 -start_number 0 -hls_time 10 -hls_list_size 0 -f hls index.m3u8
 
적당한 동영상 파일을 index.mp4 파일명으로 맞추고 명령을 실행해보았다. index.m3u8 파일 하나와 indexNNN.ts 파일 272개가 생겼다.
m3u 확장자라면 재생목록인 걸 이미 알고 있고 m3u8 파일의 내용을 보아도 약간의 주석과 ts 파일들만 쭉 있었다. mpv index.m3u8 명령으로 실행해봐도 끊기는 마디 없이 하나의 긴 영상으로 뜬다.
이 정도면 파일들을 웹에서 접근할 수 있게 놔도면 그냥 재생이 될 것처럼 보인다.
 

3단계 - DRM 적용

다음으로는 ffmpeg create drm enabled content 정도의 검색어를 찾아보았다.
 
http://hlsbook.net/how-to-encrypt-hls-video-with-ffmpeg/ 페이지에는 hls를 만드는 ffmpeg 실행에 키 파일을 같이 넘기는 설명이 있다.
그럼 m3u8 파일에 키에 대한 언급이 들어가고, 해보진 않았지만 아마 결과물이 되는 파일도 키로 감싸져 있겠지.
 
https://stackoverflow.com/a/42721974/6629746 에서는 DRM이 보안 걸린 파일을 생성하는 것과, 서버와 사용자 간에 그 보안키를 공유하는 것으로 나뉜다고 전제하고 흔히 말하는 DRM이란 후자인 재생 쪽을 뜻한다고 설명한다.
https://shaka-player-demo.appspot.com/docs/api/tutorial-drm-config.html 페이지를 보라고 하는데 여기서는 재생기에 drm 옵션을 어떻게 주는지 설명하고 있다.
 
DRM 자체는 아직 정확히는 모르겠고 재생기를 붙이는 단계까지 가봐야 체감이 될 것 같다.
 

2.1 단계 - DASH라는 게 있다

DRM을 찾다 보니 걸려나오는 문서 중에 다른 것도 있었다.
 
https://www.axinom.com/creating-multi-drm-protected-videos-with-free-tools/ 페이지에서는 ffmpeg와 MP4Box라는 명령으로 dash라는 형식의 파일을 만들어낸다. 
MP4Box는 우분투에서 gpac 패키지가 제공하고 있어서 바로 설치하고 실행해봤다. 
생성되는 파일 중에 mpd 파일이 뭔지는 dash mpd 검색어로 검색해서 나온 https://stackoverflow.com/a/50060214/6629746 여기에 설명이 있다.
https://developer.mozilla.org/en-US/docs/Web/Apps/Fundamentals/Audio_and_video_delivery/Setting_up_adaptive_streaming_media_sources 페이지도 설명이 있는데 한눈에 들어오지 않는다.
  
dash hls라고 찾아보았다.
https://blog.wisen.co.kr/?p=2813 페이지는 CDN 업체의 홍보 페이지인데 DRM 적용된 인코딩 기능을 서비스로 제공하면서 그 일환으로 전반적인 구성을 설명한다. 특출하게 기술적인 내용을 담은 건 아니지만 길잡이로 삼을만했다. 
 
https://meetup.toast.com/posts/131 페이지에 한국어로 친절하게 정리가 되어 있었다. 
요약하면 DASH는 표준 규격, HLS는 애플 규격이었다.
 
모르는 말들이 나와서 caniuse.com에 물어봤다. 
  • https://caniuse.com/#search=hls - Edge와 웹킷 기반의 브라우저가 대체로 지원하는 걸로 보인다.
  • https://caniuse.com/#search=dash - Edge 말고 지원하는 브라우저가 없는 걸로 나온다. 
  • https://caniuse.com/#search=mse - DASH가 지원 브라우저가 거의 없는 건 실제로는 MSE를 통해 구현되기 때문이다.
  • https://caniuse.com/#search=eme - DRM을 처리하는 구현체의 이름. 당당하게 사파리가 빠져 있는데, https://github.com/Fyrd/caniuse/issues/4147 를 보면 실제로는 iOS 11.2부터 지원을 하는 걸로 되어 있다. 벌써 2018년 초반의 일인데 왜 caniuse 사이트에는 아직 미지원이라고 표시가 되어 있는지 모르겠다.

4단계 - 재생기

이쯤에서 내가 아는 DRM 동영상 서비스와 비교해봤다. 왓챠플레이.
왓챠플레이를 열고 브라우저 개발자 도구에서 웹페이지와 Network 탭을 뒤적거려보니 dash 기반이라는 것을 알 수 있었다.
마크업에 붙은 class 값을 보면 재생기는 DASH Everywhere인 걸로 보이는데 이게 그 사이에 이름이 바뀌었는지 검색해서 찾아간 공식 사이트처럼 보이는 곳에는 PRESTOplay라는 이름으로 나온다.
왓챠플레이를 쓰다보면 종종 알 수 없는 오류라면서 죽고 재생이 멈추는 경우가 있어서 왓챠에서 쓰는 건 택하고 싶지 않다.
 
https://blog.streamroot.io/how-to-choose-your-media-engine/ 페이지는 hls와 dash 형식마다 어떤 재생기를 쓸 수 있는지 표로 나열하고 있다.
iOS에서 DASH를 돌리는 것 말고는 다 방법이 있긴 한가보다. 
https://www.npmtrends.com/dash.js-vs-hls.js-vs-rx-player-vs-shaka-player-vs-videojs-contrib-hls 페이지를 보면 hls를 js로 구현하는 게 인기가 많다. 그렇다는 건 hls 포맷을 많이 쓴다는 걸까?
https://blog.videojs.com/dash-everywhere-ish-hack-project/ 페이지를 보면 iOS 때문에라도 HLS와 DASH를 모두 유지하도록 권하고 있다.
 
https://medium.com/@stepashka69/making-web-browser-play-streaming-video-mpeg-dash-smooth-streaming-hls-with-one-player-c5f4dd445b91 페이지에서는 아예 adapter를 하나 입혀서 구분없이 쓸 수 있게 하자는데 이게 실무에서 의미가 있을지 어떨지 모르겠다.
 

5단계 - 서버

이미 인코딩된 파일을 제공하는 것이니 파일을 제공하는 웹서버만 있으면 될 것 같다.

rtmp는 직접 인코딩을 실시간으로 할 수도 있는 것 같고 ffmpeg 인코딩을 받아서 내보내는 역할만 할 수도 있는 것 같다.
하지만 스트리밍을 대량으로 하려면 실시간 인코딩은 자원 소모가 심하고 지연도 발생할 것이니 선택할만한 방법이 아니다.

nginx를 그냥 쓰고 rtmp 플러그인을 추가하는 것도 보인다. https://hermantorjussen.no/how-to-livestream.html
apt search 명령으로 찾아본 우분투 패키지 중에는 crtmpserver라는 것도 있는데 자세히 찾아보진 않았다.

http://elelinux.blogspot.com/2015/11/dash-streaming-using-nginx.html 페이지에서는 nginx 설정에 rtmp 블럭을 두고 그 안에 dash 설정을 붙이는 방식이 나온다.
  

5.1단계 - 인증된 사용자에게만 서빙

paywall을 거쳐야 하기 때문에 접근을 제한할 방법이 필요하다. 

signed cookie, signed url이라는 용어를 초반 검색 어딘가에서 봤다. https://blog.embian.com/123 페이지였던 것 같다.
이건 탐색이 더 필요하다.
https://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html

encryption key rotation이라는 게 재생기 옵션에 대체로 있는 것 같으니 지원을 다들 하는 것 같다.

http://blog.leedoing.com/87 여기는 샘플로 할만한 코드가 있다.

6단계 - AWS 사용

AWS에도 스트리밍 기능은 있으니 AWS의 문서를 읽어보면 위에서 검토한 내용들을 맞게 이해했는지, AWS를 써먹을 수 있는지를 알 수 있다.

aws hls dash라고 검색하면 Elastic Transcoder나 Elemental MediaConverter가 나온다. AWS Elemental 시리즈가 신형으로 Elastic Transcoder를 대체하는 것 같다.
aws hls dash drm라고 하나 더 붙여도 특별히 달라지는 건 없지만 DRM 관련 정보를 더 직접적으로 찾을 수 있다.

https://medium.com/@whatauseless/aws-media-services-%EC%A0%95%EB%A6%AC-232484cbdbd6 페이지는 Elemental 시리즈의 대강을 설명하는데 비슷비슷하게 보여서 잘 모르겠다.

https://www.slideshare.net/awskorea/pooq-ott-case-for-live-vod-build-on-aws 페이지는 마침 AWS로 POOQ 서비스를 구축한 사례인데 대체로 알아들을 수 있다.
"서비스 운영" 장에서 "룰 베이스 미디어 파일명 사용으로 Manifest와 Media 의존성 제거" 부분은 감이 안 온다.
"서명된 쿠키 사용"은 JSON이 적혀 있는데 당장은 어떻게 사용되는지 다 이해가 되지는 않는다.

5.2단계 - 인증된 사용자의 유출을 추적

forensic watermark라는 용어가 이건 것 같다. cloudfront forensic watermark라고 찾아보면 이런저런 단어들이 나온다. ffmpeg forensic watermark는 자동완성으로도 뜨는데 검색결과는 신통치 않다.

AWS 관련으로 검색하던 중에 https://docs.pallycon.com/ko/multidrm/packaging/aws-elemental/ 페이지를 보았는데 메뉴에 포렌식 워터마킹이라는 항목이 있었다. 전자책 업체 등에서 사용자가 다운로드 받은 파일에 누구인지 구분하는 식별자를 붙여서 복제가 발생했을 때 누가 유출한 건지 밝힌다는 얘기가 이건가 싶었다.

https://docs.pallycon.com/ko/watermarking/mixing/ 페이지의 설명을 읽고서야 동작 방식이 이해가 되었다. 실시간으로 영상 인코딩을 하는 건 아니고, HLS나 DASH를 만들 때 각 세그먼트(파일 조각)을 아예 0,1 두 벌로 만들고, 둘 중에 어떤 걸 송출할지를 세션 정보에 따라 구분해서 결과적으로 완성된 스트림을 녹화한다면 어떤 세션 기준으로 조합된 것인지 알 수 있게 한다.

https://docs.pallycon.com/ko/watermarking/mixing/cloudfront-mixer/ 페이지를 더 읽어보면 조합 절차는 lambda edge로 처리하는데 이 부분은 부하가 걸리지 않으려나? https://www.tvbeurope.com/technology/nagras-watermarking-solution-integrated-into-amazon-cloudfront-cdn 페이지를 보면 NexGuard 제품도 같은 방식인 것 같다. 결국 실시간으로 처리하는 구간이 어딘가는 존재해야 하고 그게 CDN과 붙어있는 람다 엣지라는 거겠지.

https://docs.pallycon.com/ko/watermarking/detecting/ 페이지에서는 유출 의심이 생겼을 때 의뢰도 자기들한테 하라고 되어 있다. 직접 검출하려면 어떤 방법이 있을지 모르겠다.

7단계 - 웹 푸시

네이티브 앱이 당분간 없을 예정인데, 가급적 사용자의 주목을 끌고 싶은 마음은 있어서, web push를 같이 붙이고 싶다.
https://www.binpress.com/building-useful-notifications-html5-apis/ 페이지에는 푸시 말고도 유용한 방법을 더 안내하고 있다.

https://developer.mozilla.org/ko/docs/Apps/Progressive/Re-engageable_Notifications_Push 페이지에서는 service worker가 필요하다고 한다. 그렇다는 건 PWA 구현체가 되어야 한다는 거지.
https://github.com/GoogleChromeLabs/sample-media-pwa 이런 구현 샘플을 뜯어보면 이해가 되려나.

하지만 https://caniuse.com/#feat=push-api 표를 보면 지원 현황이 좋지 않다.
https://medium.com/@firt/progressive-web-apps-on-ios-are-here-d00430dee3a7 페이지를 보면 iOS와 안드로이드에서 지원이 꽤 다르기도 하다.

7.1단계 - TypeScript 도입?

가급적 ES6 내지는 TS 정도를 도입하고 싶은데, PWA나 service worker도 모르는 판국이라 엄두가 나지 않는다.