이전 포스팅에서는 MediaConvert 를 사용하기 위한 role 을 생성하는 방법에 대해서 알아 보았습니다. 이번 포스팅에서는 Spring Boot 프로젝트에 MediaConvert SDK 를 사용하여 Job 을 생성하는 샘플코드를 작성해 보겠습니다.
AWS MediaConvert 디펜던시
AWS MediaConvert SDK 를 사용하기 위해서는 software.amazon.awssdk:mediaconvert 디펜던시가 필요합니다.
샘플코드를 작성하기 이전에 트랜스코딩 대상 미디어 파일은 S3 에 업로드 되어 있다고 가정하겠습니다.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>2.20.87</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-dependencies</artifactId>
<version>3.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
...
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>mediaconvert</artifactId>
</dependency>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-starter-sns</artifactId>
</dependency>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-autoconfigure</artifactId>
</dependency>
...
</dependencies>
software.amazon.awssdk (AWS java SDK) 와 io.awspring.cloud 에 정의된 패키지들을 사용할 것입니다.
io.awspring.cloud (spring cloud AWS) 는 AWS 관련 서비스를 사용하기 위한 인프라 관련 코드를 최대한 피할 수 있도록 Spring 에서 제공하는 Spring 기반 통합 모듈입니다. 즉, AWS 서비스를 사용하기 위한 코드를 보다 간단한 설정으로 사용할 수 있게 해줍니다.
io.awspring.cloud 관련 내용은 아래 링크를 참고하시면 도움이 됩니다.
참고로 mediaconvert artifact 가 어떤 버전을 사용하는지 확인하려면
software.amazon.awssdk:bom 파일과 software.amazon.aswsdk:aws-sdk-java-pom 파일을 살펴보면 됩니다.
software.amazon.awssdk:bom 파일을 보면 아래와 같이 정의 되어 있습니다.
<parent>
<groupId>software.amazon.awssdk</groupId>
<artifactId>aws-sdk-java-pom</artifactId>
<version>2.20.87</version>
<relativePath>../pom.xml</relativePath>
</parent>
....
<dependencyManagement>
<dependencies>
...
...
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>mediaconvert</artifactId>
<version>${awsjavasdk.version}</version>
</dependency>
...
...
</dependencies>
</dependencyManagement>
software.amazon.awssdk:aws-sdk-java-pom 파일에 보면 awsjavasdk.version 을 확인할 수 있습니다.
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>software.amazon.awssdk</groupId>
<artifactId>aws-sdk-java-pom</artifactId>
<version>2.20.87</version>
<packaging>pom</packaging>
<name>AWS Java SDK :: Parent</name>
...
...
<properties>
...
<awsjavasdk.version>${project.version}</awsjavasdk.version>
...
</properties>
...
...
</project>
예시에서 사용된 software.amazon.awssdk 의 project.version 은 2.20.87 임을 알 수 있습니다.
application.yml 설정
...
spring:
cloud:
aws:
credentials:
access-key: ${AWS_ACCESS_KEY}
secret-key: ${AWS_SECRET_KEY}
region:
static: ${AWS_REGION}
media-convert:
role-arn: <MediaConvert role arn 정보를 입력합니다>
...
spring.cloud.aws.credentials 는 spring cloud AWS 모듈에서 참조하는 정보로써 AWS 계정 인증 정보를 입력합니다.
예시에서는 AWS_ACCESS_KEY, AWS_SECRET_KEY, AWS_REGION 을 시스템 환경변수로 등록하여 설정한 것입니다. 환경 변수로 등록하지 않고 직접 값을 입력해도 됩니다.
media-convert.role-arn 에는 앞선 AWS MediaConvert SDK 사용 방법 (1) - role 생성하기 에서 생성한 role 의 arn 정보를 입력합니다.
MediaConvertClient Bean 생성
package global.voda.operator.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.regions.providers.AwsRegionProvider;
import software.amazon.awssdk.services.mediaconvert.MediaConvertClient;
import software.amazon.awssdk.services.mediaconvert.model.DescribeEndpointsRequest;
import software.amazon.awssdk.services.mediaconvert.model.DescribeEndpointsResponse;
import java.net.URI;
@Slf4j
@Configuration
public class AwsMediaConvertConfig {
@Bean
public MediaConvertClient mediaConvertClient(AwsCredentialsProvider credentialsProvider,
AwsRegionProvider regionProvider) {
DescribeEndpointsResponse describeEndpointsResponse = MediaConvertClient.builder()
.credentialsProvider(credentialsProvider)
.region(regionProvider.getRegion())
.build()
.describeEndpoints(
//한번에 반환할 MediaConvert 서비스 endpoint 최대 개수 지정
DescribeEndpointsRequest.builder().maxResults(20).build()
);
if (describeEndpointsResponse.endpoints().size() == 0) {
log.error("cannot find MediaConvert service endpoint URL");
throw new RuntimeException("cannot find MediaConvert service endpoint URL");
}
String endPointUrl = describeEndpointsResponse.endpoints().get(0).url();
if (log.isDebugEnabled()) {
log.debug("MediaConvert endpoint URL: {}", endPointUrl);
}
//가져온 MediaConvert 서비스 endpoint URL 을 MediaConvertClient 에 셋팅하여
//MediaConvertClient Bean 을 생성
return MediaConvertClient.builder()
.region(regionProvider.getRegion())
.endpointOverride(URI.create(endPointUrl))
.build();
}
}
AwsCredentialsProvider, AwsRegionProvider 는 spring.cloud.aws.credentials 설정을 기반으로 자동 주입됩니다.
MediaConvert Job 생성 코드
package com.example.mediaconvert.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import software.amazon.awssdk.services.mediaconvert.MediaConvertClient;
import software.amazon.awssdk.services.mediaconvert.model.*;
@Service
@Slf4j
@RequiredArgsConstructor
public class MediaConvertServiceEx {
@Value("${media-convert.role-arn:}")
private String roleArn;
private final MediaConvertClient mediaConvertClient;
/**
* 동영상을 트랜스 코딩하기 위해서 media convert job 을 생성하는 메소드
*
* @param s3InputPath 트랜스코딩 대상 영상 파일 S3 object 경로
* @param s3OutputPath 트랜스코딩 결과 파일 저장하기 위한 S3 object 경로
* @throws MediaConvertException MediaConvert 예외
*/
public void videoConvert(String s3InputPath,
String s3OutputPath) throws MediaConvertException {
Output.Builder outputBuilder = commonOutputBuilder();
Output output = outputBuilder.videoDescription(
VideoDescription.builder()
.codecSettings(VideoCodecSettings.builder()
//mp4 형식 출력
.codec(VideoCodec.H_264)
.h264Settings(H264Settings.builder()
.maxBitrate(1000000)
.rateControlMode(H264RateControlMode.QVBR)
.sceneChangeDetect(H264SceneChangeDetect.TRANSITION_DETECTION)
.build())
.build())
.build()
).build();
OutputGroup.Builder outputGroupBuilder =
commonOutputGroupBuilder(s3OutputPath);
OutputGroup outputGroup = outputGroupBuilder
.outputs(output)
.build();
Input input = Input.builder()
.timecodeSource(InputTimecodeSource.ZEROBASED)
.fileInput(s3InputPath)
.build();
executeMediaConvert(outputGroup, input);
}
/**
* 이미지를 정지 영상으로 생성하기 위해서 media convert job 을 생성하는 메소드
*
* @param s3InputPath 이미지 파일 S3 object 경로
* @param s3OutputPath 트랜스코딩 결과 파일 저장하기 위한 S3 object 경로
* @param width 이미지 파일 가로 해상도
* @param height 이미지 파일 세로 해상도
* @param timeDurationMs 트랜스 코딩된 영상 재생시간 (MS)
* @throws MediaConvertException MediaConvert 예외
*/
public void imageConvert(String s3InputPath,
String s3OutputPath,
int width,
int height,
int timeDurationMs) throws MediaConvertException {
int outputWidth = (int) (Math.ceil((double) width / 2) * 2);
int outputHeight = (int) (Math.ceil((double) height / 2) * 2);
int framerateNumerator = 30;
int framerateDenominator = 1;
Output.Builder outputBuilder = commonOutputBuilder();
Output output = outputBuilder.videoDescription(
VideoDescription.builder()
.width(outputWidth)
.height(outputHeight)
.videoPreprocessors(VideoPreprocessor.builder()
.imageInserter(ImageInserter.builder()
.insertableImages(InsertableImage.builder()
.imageX(0)
.imageY(0)
.layer(1)
.imageInserterInput(s3InputPath)
.opacity(100)
.build())
.build())
.build())
.codecSettings(VideoCodecSettings.builder()
.codec(VideoCodec.H_264)
.h264Settings(H264Settings.builder()
.maxBitrate(1000000)
.framerateDenominator(framerateDenominator)
.framerateControl(H264FramerateControl.SPECIFIED)
.framerateNumerator(framerateNumerator)
.rateControlMode(H264RateControlMode.QVBR)
.sceneChangeDetect(H264SceneChangeDetect.TRANSITION_DETECTION)
.build())
.build())
.build()
).build();
OutputGroup.Builder outputGroupBuilder =
commonOutputGroupBuilder(s3OutputPath);
OutputGroup outputGroup = outputGroupBuilder
.outputs(output)
.build();
double oneFrameTime = (double) framerateDenominator / framerateNumerator;
int oneFrameTimeMs = (int)(oneFrameTime * 1000);
//마지막에 1 frame 시간이 재생시간에 추가되기 때문에 재생시간에서 1 frame 만큼의 시간을 빼준다.
Input input = Input.builder()
.videoGenerator(InputVideoGenerator.builder()
.duration(timeDurationMs - oneFrameTimeMs)
.build())
.build();
executeMediaConvert(outputGroup, input);
}
private void executeMediaConvert(OutputGroup outputGroup, Input input) {
try {
JobSettings jobSettings = JobSettings.builder()
.timecodeConfig(TimecodeConfig.builder()
.source(TimecodeSource.ZEROBASED)
.build())
.inputs(input)
.outputGroups(outputGroup)
.build();
CreateJobRequest createJobRequest = CreateJobRequest.builder()
.role(roleArn)
.settings(jobSettings)
.build();
CreateJobResponse jobResponse = mediaConvertClient.createJob(createJobRequest);
log.info("success to create image media convert job, id={}", jobResponse.job().id());
} catch (MediaConvertException e) {
log.error("failure create image media convert job, {}", e.getMessage());
throw e;
}
}
private OutputGroup.Builder commonOutputGroupBuilder(String s3OutputPath) {
return OutputGroup.builder()
.name("File Group")
.outputGroupSettings(OutputGroupSettings.builder()
.type(OutputGroupType.FILE_GROUP_SETTINGS)
.fileGroupSettings(FileGroupSettings.builder()
.destination(s3OutputPath)
.build())
.build());
}
private Output.Builder commonOutputBuilder() {
return Output.builder()
.containerSettings(ContainerSettings.builder()
.container(ContainerType.MP4)
.build());
}
}
샘플 코드에는 동영상 파일을 트랜스코딩 하는 메소드와 이미지 파일을 정지된 동영상 파일로 트랜스코딩 하는 메소드가 있습니다.
이미지 파일 하나를 정지된 동영상 파일로 만드는 경우에는 MediaConvert Job Input 에 재생하고자 하는 시간 길이의 black video 를 생성하고 Output 에는 Encoding settings 에서 Image inserter 에 input 이미지를 지정해서 만듭니다.
MediaConvert Job 생성 샘플 코드
package com.example.mediaconvert;
import com.example.mediaconvert.service.MediaConvertServiceEx;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@RequiredArgsConstructor
public class MediaconvertApplication implements CommandLineRunner {
private final MediaConvertServiceEx mediaConvertServiceEx;
public static void main(String[] args) {
SpringApplication.run(MediaconvertApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
//이미지 해상도 600 * 120
String input = "s3://my-media-convert-source/600x120.png";
String output = "s3://my-media-convert-target/600x120";
//출력 영상 재생시간은 15초
mediaConvertServiceEx.imageConvert(input, output, 600, 120, 15000);
}
}
참고로 이미지의 경우 해상도 정보는 tika 라이브러리를 통해서 이미지 해상도 정보를 얻어 올 수 있습니다.
위와 같이 실행해 봤을 때 생성된 MediaConvert Job id 는 아래와 같습니다.
AWS 관리 콘솔 상에서 Job 이 정상적으로 생성이 되었는지 확인해 봅니다.
MediaConvert Job 생성을 위한 셋팅 요소들이 많기 때문에 JOB 생성을 위한 SDK 를 사용하기 전에 AWS 관리 콘솔에서 JOB 을 직접 생성한 후에 Job 목록에서 해당 Job ID 의 상세 정보 페이지에서 [View JSON] 으로 Job 에 대한 명세를 보고 SDK 를 사용하면 많은 도움이 됩니다.
지금까지 MediaConvert SDK 를 이용하여 Job 을 생성하는 샘플코드를 작성해 봤습니다.
다음 포스팅에서는 AWS SNS 를 통해 요청된 Job 의 완료 이벤트를 REST API 기반으로 수신하는 방법에 대해서 알아 보겠습니다.
'AWS' 카테고리의 다른 글
bitbucket pipeline을 이용한 AWS ECS 애플리케이션 배포 (0) | 2023.10.23 |
---|---|
AWS MediaConvert SDK 사용 방법 (3) - SNS 연결하기 (0) | 2023.08.18 |
AWS MediaConvert SDK 사용 방법 (1) - role 생성하기 (0) | 2023.08.17 |
AWS SES 사용하기 (0) | 2023.07.31 |
댓글