성능 개선

영상 인코딩 및 최적화를 위한 FFmpeg 사용법과 Spring Boot 연동 예제

burgerkim 2023. 4. 28. 10:51
반응형

영상 인코딩

영상 데이터를 압축하고 표준화된 형식으로 변환하는 과정

인코딩을 할땐 코덱이 사용되며, 원본 영상 데이터의 크기를 줄이고 호환성을 높이는 역할을 한다.
이로 인해 네트워크 전송 시 빠르게 전송되고 저장곤간을 절약할 수 있다.

코덱

코덱은 영상 데이터를 압축하고 해석하는데 사용되는 소프트웨어이다.
압축률, 품질, 호환성 등 다양한 측면에서 차이가 있으며 H.264, H.265, VP8, VP9 등이 대표적인 예이다.

컨테이너

컨테이너는 영상, 오디오, 메타데이터 등 다양한 데이터를 하나의 파일로 묶어 호환성과 재생을 보장하는 역할
MP4, AVI, MKV, MOV 등의 포멧이 있다.

 


 

영상 인코딩 및 최적화를 하는 이유

  • 품질 유지 - 원본 영상의 품질을 최대한 보존하면서 효율적인 포맥으로 변환이 가능하여 사용자들의 사용에 편의성을 줄 수 있다.
  • 전송 속도 - 영상 파일 크기를 줄여, 데이터 전송 속도가 향상된다.
  • 저장 공간 절약 - 영상 파일의 크기를 최소화하여 저장 공간을 절약하여 서버 운영 비용을 줄일 수 있다.
  • 호환성 향상 - 영상을 다양한 기기 및 브라우저에서 재생 가능하도록 코덱과 컨테이너 포멧을 통해 인코딩할 수 있다.

 


 

FFmpeg 

오픈소스 프로젝트로 다양한 기능을 제공하는 미디어 처리 라이브러리이다.

주로 비디오, 오디오, 스트림 데이터를 처리하는데 사용되며, 다양한 포맷간의 변환, 인코딩, 디코딩, 필터링, 스트리밍 등 여러 작업을 지원한다.

크로스 플랫폼을 지원하여 대부분의 운영체제에서 사용가능하다.

 

FFmpeg 주요 구성요소

  • libavcode - 다양한 코덱을 제공하여 비디오 및 오디오 데이터의 인코딩과 디코딩을 처리한다. (H.264, H.265, VP8, VP9, AAC, MP3)
  • libavformat - 다양한 미디어 컨테이너 포맷을 지원하며, 비디오와 오디오 데이터를 저장하고 전송할 때 사용되는 파일 포맷을 처리한다. (MP4, AVI, MKV, MOV, FLV)
  • libavfilter - 비디오 및 오디오 데이터에 다양한 필터링 기능을 적용할 수 있게 해준다. 예를 들어 영상의 크기를 변경, 회전, 자르기, 노이즈 제거, 오디오 볼륨조절 등
  • libavutil - FFmpeg 프로젝트에서 공통으로 사용되는 기능을 제공. 예를 들어 수학 계산, 메모리 관리, 데이터 구조 등
  • libswscale - 비디오 데이터의 크기를 변경하거나 색상 형식을 변환하는 작업을 처리
  • ffmpeg - 위의 요소들을 이용하여 다양한 미디어 처리작업을 수행하는 CLI 도구. 사용자는 이 도구를 통해 영상 인코딩, 변환, 필터링 등의 작업을 실행할 수 있다.

 


 

Spring boot에서 FFmpeg를 사용해 영상 인코딩 작업 하는 방법

 

1. FFmpeg 설치 (Ubuntu)

sudo apt-get update
sudo apt-get install ffmpeg

먼저 FFmpeg가 설치되어있어야 하므로 명령어를 통해 설치

 

2. FFmpeg 래퍼 라이브러리 종속성 추가

implementation("ws.schild:jave-core:3.3.1")

FFmpeg를 사용하기위해 java용 FFmpeg 래퍼 라이브러리 종속성을 추가

* 최신버전 확인
https://mvnrepository.com/artifact/ws.schild/jave-core

 

 

3. 영상 인코딩 로직 구현

@RestController
@RequestMapping("/api/video")
class VideoTestController {

	// 로컬에 ffmpeg 경로 지정, which ffmpeg 명령어를 통해 경로를 찾을 수 있다.
    private val ffmpegExecutablePath = "/usr/local/bin/ffmpeg" 

    @PostMapping("/upload")
    fun uploadVideo(@RequestParam("file") file: MultipartFile): ResponseEntity<String?>? {
        return try {
            // multipart file 로 입력받은 파일을 임시파일로 로컬에 저장       
            val inputFile = File.createTempFile("input", file.originalFilename)
            file.transferTo(inputFile)
            // 출력 받을 파일 객체 생성
            val outputFile = File.createTempFile("output", ".mp4")
            // 영상 파일 인코딩
            encodeVideo(inputFile, outputFile)

            // 인코딩 된 파일 경로 반환
            ResponseEntity.ok(outputFile.absolutePath)
        } catch (e: Exception) {
            e.printStackTrace()
            ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Failed to process video");
        }
    }
    
    fun encodeVideo(input: File, output: File) {
        // FFmpeg 실행 파일의 경로를 지정하고, 라이브러리가 FFmpeg을 찾을 수 있게 해줌
        val locator = FFMPEGLocatorCustom(ffmpegExecutablePath)
        // 비디오 파일의 메타데이터와 인코딩 작업을 수행하는 데 필요한 정보를 제공
        val multimediaObject = MultimediaObject(input, locator)


        val audio = AudioAttributes()
        audio.setCodec("aac") // 오디오 코덱 설정
        audio.setBitRate(128000) // 비트레이트 설정
        audio.setChannels(2) // 채널 수
        audio.setSamplingRate(44100) // 샘플링 레이트

        // 비디오의 사이즈를 가져와 1000픽셀로 이하로 비율을 조절
        val videoInfo = multimediaObject.info.video
        val originalWidth = videoInfo.size.width ?: 0
        val originalHeight = videoInfo.size.height ?: 0
        val scaleFactor = 1000.0 / max(originalWidth, originalHeight)
        // 조절된 width, height 값
        val newWidth = (originalWidth * scaleFactor).toInt()
        val newHeight = (originalHeight * scaleFactor).toInt()
        
        
        val video = VideoAttributes()
        video.setCodec("libx264") // 비디오 코덱 설정
        video.setBitRate(320000) // 비트레이트 설정
        video.setFrameRate(30) // 프레임 레이트 설정
        video.setSize(VideoSize(newWidth, newHeight)) // 비디오 크기 설정

		
        val attrs = EncodingAttributes()
        attrs.setOutputFormat("mp4") // 출력 형식
        attrs.setAudioAttributes(audio) // 오디오 속성 설정
        attrs.setVideoAttributes(video) // 비디오 속성 설정

        // 인코딩 작업을 수행 및 예외 처리
        val encoder = Encoder(locator)
        try {
            encoder.encode(multimediaObject, output, attrs)
        } catch (e: IllegalArgumentException) {
            e.printStackTrace()
        } catch (e: EncoderException) {
            e.printStackTrace()
        }
    }
}

class FFMPEGLocatorCustom(private val ffmpegExecutablePath: String) : DefaultFFMPEGLocator() {
    override fun getExecutablePath(): String {
        return ffmpegExecutablePath
    }

    override fun createExecutor(): ProcessWrapper {
        return FFMPEGProcess(executablePath)
    }
}

 


 

Spring boot에서 FFmpeg를 사용해 영상 인코딩 결과

 

인코딩을 수행할 영상 파일 정보

재생시간이 43초밖에 되지 않지만 용량이 391MB 로 상당히 큰 영상 파일이다.

 

 

인코딩 수행 시간 및 결과

영상 파일의 용량이 커서 그런지 총 인코딩 시간은 15.5초가 걸렸다.

 

 

인코딩 수행된 영상 파일 정보

인용량은 99% 이상 줄어들었다.
수행 시간이 길었던 만큼 굉장히 많이 줄어들었고 그만큼 화질이 안 좋아진거는 어쩔 수 없는것같다.

 

 

원본 영상 정보와는 달리 추가정보란에 데이터가 없는데 이 부분은 메타데이터 입력을 통해 추가살 수 있다.

attrs.isMapMetaData = true

 

 

반응형