본문 바로가기

성능 개선

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

반응형

영상 인코딩

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

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

코덱

코덱은 영상 데이터를 압축하고 해석하는데 사용되는 소프트웨어이다.
압축률, 품질, 호환성 등 다양한 측면에서 차이가 있으며 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

 

 

반응형