transcoder¶
/usr/local/m2/setting.json
다음 영역에 대해 기술한다.
{
"functions": {
"contents": {
"transcoder": {
...
}
}
}
}
Important
트랜스코딩 시스템 권장사항은 다음과 같다.
용도 |
Core |
메모리(GiB) |
비고 |
---|---|---|---|
test |
4 |
4 |
동작을 위해 트랜스코딩 성능 제약 |
stage |
4 |
8 |
다른 함수와 동시 구동이 가능 |
production (SMB) |
8 |
16 |
gif2video 서비스 가능 |
production (enterprise) |
16 |
32 |
권장 |
How to use¶
명령어 리스트¶
# 비디오 분석정보를 JSON으로 제공한다.
https://example.com/video.mp4/xcdr/analyze/src
https://example.com/video.mp4/xcdr/analyze/srcd
# 원본-변환된 비디오 간 SSIM 값을 측정한다.
https://example.com/video.mp4/xcdr/preset/_720p/analyze/ssim
# /video.mp4를 사전 정의한 low 프리셋으로 트랜스코딩
https://example.com/video.mp4/xcdr/preset/low
# /video.mp4를 _1080p 프리셋 (built-in) 로 트랜스코딩
https://example.com/video.mp4/xcdr/preset/_1080p
# /video.mp4를 _1080p 프리셋 기반, 세부옵션만 조정하여 트랜스코딩
https://example.com/video.mp4/xcdr/preset/_1080p/options/audio.channels=1;video.frameRate=10
# /video.mp4를 구간/다구간 추출
https://example.com/video.mp4/xcdr/trim/01:20-01:30
https://example.com/video.mp4/xcdr/trim/01:20-01:30,03:57-04:28
https://example.com/video.mp4/xcdr/trim/45-50,11-13,36-43/faststart/true
# /video.mp4를 헤더만 앞으로 옮겨 전달
https://example.com/video.mp4/xcdr/faststart/true
# 화면을 캡쳐한다.
https://example.com/video.mp4/xcdr/capture/ts=10.3
# 화면을 캡쳐하여 avif로 추출한다.
https://example.com/video.mp4/xcdr/capture/ts=10.3;format=avif
# 화면을 캡쳐하여 animated 이미지로 제공한다.
https://example.com/video.mp4/xcdr/capture/ts=1,2,3,4,5
# 화면을 캡쳐하여 1s간격의 mp4 비디오로 제공한다.
https://example.com/video.mp4/xcdr/capture/ts=1,2,3,4,5;format=mp4
여러 명령어를 결합하거나 다른 함수와 연계되면 보다 확장성있는 서비스를 구현할 수 있다.
# /video.mp4의 10초~20초 구간추출하여 low 프리셋으로 트랜스코딩
https://example.com/video.mp4/xcdr/trim/00:00:10-00:00:20/preset/low
https://example.com/video.mp4/xcdr/trim/10-20/preset/low
# /video.mp4의 1시간 15분 30초 이후 구간추출하여 low 프리셋으로 트랜스코딩 후 헤더를 앞으로 이동
https://example.com/video.mp4/xcdr/trim/1:15:30-/preset/low/faststart/true
# /video.mp4를 시작부터 1분 30초 구간만 추출하여 low 프리셋으로 트랜스코딩 후 헤더를 앞으로 이동
https://example.com/video.mp4/xcdr/trim/-01:30/preset/low/faststart/true
# mp4hls 연계 - /video.mp4를 _1080p 프리셋으로 트랜스코딩한 다음 해당영상을 HLS로 전송
https://example.com/video.mp4/xcdr/preset/_1080p/mp4hls/index.m3u8
# mp4hls 연계 - /video.mp4 원본을 HLS로 분할한 뒤 전송과정에서 _1080p 프리셋으로 트랜스코딩
https://example.com/video.mp4/mp4hls/index.m3u8/xcdr/preset/_1080p
# mp4hls 연계 - /video.mp4 원본을 구간추출한 뒤 _1080p 프리셋으로 트랜스코딩, 이후 HLS로 전송
https://example.com/video.mp4/xcdr/trim/00:01:40-00:03:00/preset/_1080p/mp4hls/index.m3u8
# dims 연계 - /video.mp4 원본의 10.3초 구간을 이미지로 추출하여 800x600으로 리사이즈한다.
https://example.com/video.mp4/xcdr/capture/ts=10.3/dims/resize/800x600
명령어 |
파라미터 |
동작 |
---|---|---|
|
|
비디오 분석정보를 |
|
|
고~급스럽게 영국식 명령어를 제공한다. |
|
|
원본-변환된 비디오 간 SSIM 값을 측정한다. |
|
{이름} |
트랜스코딩할 프리셋을 지정한다. |
|
|
|
|
|
트랜스코딩할 프리셋을 지정한다. |
|
|
동영상 헤더정보를 파일의 앞으로 이동시킨다. |
|
|
화면을 캡쳐 지점. 단일 또는 콤마로 구분된 연속 |
비디오 분석정보¶
/analyze/src
명령어는 비디오 분석정보를 간략히 제공한다.
{
"enable": true,
"url": "/video.mp4",
"result": {
"source": {
"size": 6238713,
"format": "mp4",
"duration": 15.062,
"video": {
"codec_name": "h264",
"width": 1920,
"height": 1080
},
"audio": {
"codec_name": "aac"
}
},
"elapsed": {
"init": 2,
"complete": 14
},
"function": {
"keyword": "xcdr"
}
}
}
enable
트랜스코더 모듈에 적재/처리 가능하다면
true
, 불가하다면false
url
원본 URL
result
분석결과
source
미디어 정보
size (단위: bytes)
용량
format
포맷 -
flac
,flv
,fmp4
,gif
,mp2
,mp3
,mp4
,mpg
,mxf
,oga
,ogg
,ts
,wav
,webm
duration (단위: 초)
재생시간 (소수점 3째자리까지 표시)
video
비디오 정보. 비디오가 없다면
null
이다.codec_name
비디오 코덱
width (단위: px)
가로길이
height (단위: px)
세로길이
audio
오디오 정보. 오디오가 없다면
null
이다.codec_name
비디오 코덱
elapsed
경과시간
init (단위: ms)
호출시점 ~ 원본이미지 초기화
complete (단위: ms)
호출시점 ~ 완료
function
트랜스코더 설정
keyword
호출 키워드
/analyze/srcd
명령어는 보다 상세한 분석정보를 제공한다.
{
"enable": true,
"url": "/video.mp4",
"result": {
"source": {
"streams": [
{
"index": 0,
"codec_name": "aac",
"codec_long_name": "AAC (Advanced Audio Coding)",
"profile": "LC",
"codec_type": "audio",
"codec_time_base": "1/48000",
"codec_tag_string": "mp4a",
"codec_tag": "0x6134706d",
"sample_fmt": "fltp",
"sample_rate": "48000",
"channels": 2,
"channel_layout": "stereo",
"bits_per_sample": 0,
"r_frame_rate": "0/0",
"avg_frame_rate": "0/0",
"time_base": "1/48000",
"start_pts": -2048,
"start_time": "-0.042667",
"duration_ts": 722944,
"duration": "15.061333",
"bit_rate": "160002",
"max_bit_rate": "160002",
"nb_frames": "706",
"disposition": {
"default": 1,
"dub": 0,
"original": 0,
"comment": 0,
"lyrics": 0,
"karaoke": 0,
"forced": 0,
"hearing_impaired": 0,
"visual_impaired": 0,
"clean_effects": 0,
"attached_pic": 0
},
"tags": {
"language": "kor",
"handler_name": "SoundHandler"
}
},
{
"index": 1,
"codec_name": "h264",
"codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
"profile": "High",
"codec_type": "video",
"codec_time_base": "1/60",
"codec_tag_string": "avc1",
"codec_tag": "0x31637661",
"width": 1920,
"height": 1080,
"coded_width": 1920,
"coded_height": 1088,
"has_b_frames": 2,
"sample_aspect_ratio": "1:1",
"display_aspect_ratio": "16:9",
"pix_fmt": "yuv420p",
"level": 41,
"color_range": "tv",
"color_space": "bt709",
"color_transfer": "bt709",
"color_primaries": "bt709",
"chroma_location": "left",
"refs": 4,
"is_avc": "1",
"nal_length_size": "4",
"r_frame_rate": "30/1",
"avg_frame_rate": "30/1",
"time_base": "1/15360",
"start_pts": 0,
"start_time": "0.000000",
"duration_ts": 230400,
"duration": "15.000000",
"bit_rate": "3157810",
"bits_per_raw_sample": "8",
"nb_frames": "450",
"disposition": {
"default": 1,
"dub": 0,
"original": 0,
"comment": 0,
"lyrics": 0,
"karaoke": 0,
"forced": 0,
"hearing_impaired": 0,
"visual_impaired": 0,
"clean_effects": 0,
"attached_pic": 0
},
"tags": {
"language": "und",
"handler_name": "VideoHandler"
}
}
],
"format": {
"filename": "http://rproxy.video.m2/http://127.0.0.1/mmv/media/monopoly.mp4",
"nb_streams": 2,
"nb_programs": 0,
"format_name": "mov,mp4,m4a,3gp,3g2,mj2",
"format_long_name": "QuickTime / MOV",
"start_time": "-0.042667",
"duration": "15.062000",
"size": "6238713",
"bit_rate": "3313617",
"probe_score": 100,
"tags": {
"major_brand": "isom",
"minor_version": "512",
"compatible_brands": "isomiso2avc1mp41",
"encoder": "Lavf57.71.100"
}
}
},
"elapsed": {
"init": 2,
"complete": 14
},
"function": {
"keyword": "xcdr"
}
}
}
유사성 비교¶
/analyze/ssim
명령어는 원본과 변환된 비디오의 유사성을 SSIM(Structural Similarity Index Measure) 방식으로 비교한다.
{
"enable": true,
"url": "/example.mp4",
"result": {
"ssim": "0.971172",
"elapsed": {
"init": 5217,
"complete": 9559
}
}
}
enable
SSIM 비교가 정상처리되었다. 실패라면
false
로 응답하다.url
원본 URL
result
분석결과
ssim (범위: 0~1)
원본과 변환된 비디오의 유사성으로 완전히 동일하다면
1
이다.
예외처리¶
프리셋 기반 트랜스코딩 처리 중 예외상황/오류가 발생했을 때 정책을 지정한다.
Note
기본정책은 오류 발생시
510
응답한다.- 트랜스코딩 명령어
preset
과 함께 동작한다. faststart
options
명령어와 함께 사용할 수 있다.trim
capture
mp4hls
analyze
와 같이 원본 대체불가 명령과 함께 사용시510
응답한다.
- 트랜스코딩 명령어
# 오류 발생시 510 Not Extended를 응답한다. 이 때 Body는 디버깅을 위한 분석정보가 노출된다. (기본)
https://example.com/sample.mp4/xcdr/preset/_gif2avif/exception/debug
# 오류 발생시 원본으로 리디렉션한다.
https://example.com/sample.mp4/xcdr/preset/_gif2avif/exception/redirect
# 오류 발생시 원본을 전달한다.
https://example.com/sample.mp4/xcdr/preset/_gif2avif/exception/origin
# debug 응답
{
# 원본이 200 OK라면 분석 정보를 제공한다.
"origin": {
...
},
# error - 표준 에러리스트 정보
"error": {
"code": 30500012,
"x-sc-chain": "failpresetformat",
"vhost": "cdn.example.com",
"url": "/sample.mp4",
"x-ctx-id": "454ecfb6-13d8-496d-b137-8b0d411caa21",
"command": "/preset/_gif2avif",
"content-length": 6686799,
"reason": "not gif format"
}
}
meta¶
"meta" : {
"enable" : false,
"keyword": "xcdr",
"tempdir": "xcdrtmp",
"maxDuration": "3:00",
"capacityPerCore": 52428800
}
enable (기본: false)
트랜스코딩 활성화
keyword (기본: xcdr)
트랜스코딩 키워드
tempdir (기본: xcdrtmp)
임시파일 I/O 디렉토리 경로 (상대경로인 경우 설치경로 하위에 생성된다.)
maxDuration (기본: 3:00)
트랜스코딩 가능한 소스 최대 시간으로
hh:mm:ss
표기를 사용한다. 소스가trim
되었다면trim
된 시간을 기준으로 한다.Warning
이 값은 지나치게 무거운 워크로드로 인해 의도치 않은 서비스 지연을 방지하는 안전장치이다. 설정을 조정할 경우 서비스 흐름상에서 클라이언트가 응답에 대해 충분히 대기하여 처리 가능한지 검증되어야 한다.
capacityPerCore (기본: 50MB)
코어당 트랜스코딩 대기 가능한 최대 용량. 8코어 머신이라면 400MB까지 큐잉이 가능하다.
Note
단, 현재 트랜스코딩되는 크기는 포함되지 않는다. 최대 큐잉크기를 넘어선다면
503 Service Unavailable
을 응답한다.
presets¶
AWS Elastic Transcoder 와 동일한 인터페이스와 기능을 제공한다.
"presets" : {
"list": [
{
"name": "low",
"video":{
"bitRate":"900",
"maxWidth":"640",
"maxHeight":"480"
},
"watermarks":[
{
"maxWidth": "20%",
"maxHeight": "20%",
"sizingPolicy": "ShrinkToFit",
"horizontalAlign": "Right",
"horizontalOffset": "10px",
"verticalAlign": "Bottom",
"verticalOffset": "10px",
"opacity": "55.5",
"target": "Content"
}
]
}
]
}
list
프리셋 정의
preset 세부옵션¶
설정옵션 |
값 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
출력 파일의 오디오 비트 전송률(KB/초) |
|
|
|
|
|
|
|
|
|
|
|
|
|
최대 참조 프레임 수 |
|
최대 비트 전송률 |
|
|
|
|
|
|
|
키 프레임 사이의 최대 프레임 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
최소 |
|
최소 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
built-in¶
별도 설정없이도 동작하는 프리셋을 지원한다.
빌트인 프리셋은 _
로 시작하는 규칙을 가진다.
name |
container |
video |
audio |
width |
height |
bitrate |
---|---|---|---|---|---|---|
|
mp4 |
H.264 |
AAC |
1920 |
1080 |
5400 kbps |
|
mp4 |
H.264 |
AAC |
1280 |
720 |
2400 kbps |
|
mp4 |
H.264 |
AAC |
854 |
480 |
1200 kbps |
|
mp4 |
H.264 |
AAC |
640 |
360 |
720 kbps |
|
mp4 |
H.264 |
|
|
|
3000 kbps |
|
webp |
webp |
|
|
|
360 kbps |
|
avif |
AV1 |
|
|
|
360 kbps |
|
webp |
webp |
|
|
|
360 kbps |
|
avif |
AV1 |
|
|
|
360 kbps |
|
webm |
vp9 |
opus |
|
|
|
|
webm |
AV1 |
opus |
|
|
|
Warning
원본 동영상이 container 포맷과 다르다면, 원본으로 리디렉션한다.
presets.list[].name
이름을_1080p
로 build-in과 동일하게 지정하여 오버라이딩 가능하다. 하지만 혼란스러움을 감당할만큼 가치있는 일이 아니다.
{
"list": [
{
"name":"_1080p",
"container":"mp4",
"audio":{
"enable": true,
"codec":"AAC",
"codecOptions":{
"profile":"AAC-LC",
"bitDepth":"8"
},
"sampleRate":"44100",
"bitRate":"128",
"channels":"2"
},
"video":{
"enable": true,
"codec":"H.264",
"speed": "medium",
"codecOptions":{
"profile":"high",
"level":"4",
"maxReferenceFrames": "3",
"interlacedMode":"progressive",
"colorSpaceConversionMode":"none",
"chromaSubsampling":"yuv420p"
},
"keyframesMaxDist": "24",
"fixedGOP": false,
"bitRate": "5400",
"frameRate":"23.97",
"maxFrameRate":"30",
"maxWidth":"1920",
"maxHeight":"1080",
"sizingPolicy":"ShrinkToFit",
"paddingPolicy":"NoPad",
"displayAspectRatio":"auto"
}
},
{
"name":"_720p",
"container":"mp4",
"audio":{
"enable": true,
"codec":"AAC",
"codecOptions":{
"profile":"AAC-LC",
"bitDepth":"8"
},
"sampleRate":"44100",
"bitRate":"128",
"channels":"2"
},
"video":{
"enable": true,
"codec":"H.264",
"speed": "medium",
"codecOptions":{
"profile":"high",
"level":"3.1",
"maxReferenceFrames": "3",
"interlacedMode":"progressive",
"colorSpaceConversionMode":"none",
"chromaSubsampling":"yuv420p"
},
"keyframesMaxDist": "24",
"fixedGOP": false,
"bitRate": "2400",
"frameRate":"23.97",
"maxFrameRate":"30",
"maxWidth":"1280",
"maxHeight":"720",
"sizingPolicy":"ShrinkToFit",
"paddingPolicy":"NoPad",
"displayAspectRatio":"auto"
}
},
{
"name":"_480p",
"container":"mp4",
"audio":{
"enable": true,
"codec":"AAC",
"codecOptions":{
"profile":"AAC-LC",
"bitDepth":"8"
},
"sampleRate":"44100",
"bitRate":"128",
"channels":"2"
},
"video":{
"enable": true,
"codec":"H.264",
"speed": "medium",
"codecOptions":{
"profile":"main",
"level":"3",
"maxReferenceFrames": "3",
"interlacedMode":"progressive",
"colorSpaceConversionMode":"none",
"chromaSubsampling":"yuv420p"
},
"keyframesMaxDist": "240",
"fixedGOP": false,
"bitRate": "1200",
"frameRate":"23.97",
"maxFrameRate":"30",
"maxWidth":"854",
"maxHeight":"480",
"sizingPolicy":"ShrinkToFit",
"paddingPolicy":"NoPad",
"displayAspectRatio":"auto"
}
},
{
"name":"_360p",
"container":"mp4",
"audio":{
"enable": true,
"codec":"AAC",
"codecOptions":{
"profile":"AAC-LC",
"bitDepth":"8"
},
"sampleRate":"44100",
"bitRate":"128",
"channels":"2"
},
"video":{
"enable": true,
"codec":"H.264",
"speed": "medium",
"codecOptions":{
"profile":"Baseline",
"level":"3",
"maxReferenceFrames": "1",
"interlacedMode":"progressive",
"colorSpaceConversionMode":"none",
"chromaSubsampling":"yuv420p"
},
"keyframesMaxDist": "24",
"fixedGOP": false,
"bitRate": "720",
"frameRate":"23.97",
"maxFrameRate":"30",
"maxWidth":"640",
"maxHeight":"360",
"sizingPolicy":"ShrinkToFit",
"paddingPolicy":"NoPad",
"displayAspectRatio":"auto"
}
},
{
"name": "_gif2mp4",
"container": "mp4",
"audio": {
"enable": false
},
"video": {
"enable": true,
"codec": "H.264",
"codecOptions": {
"profile": "main",
"chromaSubsampling": "yuv420p"
},
"fixedGOP": false,
"sizingPolicy": "ShrinkToFit",
"paddingPolicy": "NoPad"
}
},
{
"name": "_mp42webp",
"container": "webp",
"audio": {
"enable": false
},
"video": {
"enable": true,
"codec": "webp",
"codecOptions": {
"profile": "main",
"chromaSubsampling": "yuv420p"
},
"fixedGOP": false,
"sizingPolicy": "ShrinkToFit",
"paddingPolicy": "NoPad"
}
},
{
"name": "_mp42avif",
"container": "avif",
"audio": {
"enable": false
},
"video": {
"enable": true,
"codec": "AV1",
"codecOptions": {
"profile": "main",
"chromaSubsampling": "yuv420p"
},
"fixedGOP": false,
"sizingPolicy": "ShrinkToFit",
"paddingPolicy": "NoPad"
}
},
{
"name": "_gif2webp",
"container": "webp",
"audio": {
"enable": false
},
"video": {
"enable": true,
"codec": "webp",
"codecOptions": {
"profile": "main",
"chromaSubsampling": "yuv420p"
},
"fixedGOP": false,
"sizingPolicy": "ShrinkToFit",
"paddingPolicy": "NoPad"
}
},
{
"name": "_gif2avif",
"container": "avif",
"audio": {
"enable": false
},
"video": {
"enable": true,
"codec": "AV1",
"codecOptions": {
"profile": "main",
"chromaSubsampling": "yuv420p"
},
"fixedGOP": false,
"sizingPolicy": "ShrinkToFit",
"paddingPolicy": "NoPad"
}
},
{
"name": "_gif2webm",
"container": "webm",
"audio": {
"enable": true,
"codec": "opus",
"bitRateForce": false
},
"video": {
"enable": true,
"codec": "vp9",
"codecOptions": {
"profile": "main",
"chromaSubsampling": "yuva420p"
},
"bitRateForce": true,
"sizingPolicy": "ShrinkToFit",
"paddingPolicy": "NoPad"
}
},
{
"name": "_mp42webm",
"container": "webm",
"audio": {
"enable": true,
"codec": "opus",
"bitRateForce": false
},
"video": {
"enable": true,
"codec": "SVT_AV1",
"codecOptions": {
"profile": "main",
"chromaSubsampling": "yuva420p"
},
"bitRateForce": true,
"sizingPolicy": "ShrinkToFit",
"paddingPolicy": "NoPad"
}
}
]
}
capture¶
capture
명령어로 추출되는 이미지 출력형태를 설정한다.
"capture" : {
"format" : "webp",
"interval" : "1s"
}
format (기본: webp)
출력 포맷
webp
,jpg
,png
,gif
,avif
,mp4
를 지원한다.Note
jpg
,png
와 같이 animated를 지원하지 않는 포맷에서 다구간을 추출하는 경우라면 첫 장만 추출된다.interval (기본: 1s)
다구간 animated 이미지 추출시 화면전환 간격
Warning
비디오 가로, 세로 길이가 고정되면 비율을 유지하지 않는다. 추출된 이미지에 대한 가공은 hyperdims 를 연계하는 것을 권장한다.
/video.mp4/xcdr/capture/ts=10/hdims/resize/640