주요글: 도커 시작하기

앞서 작업까지의 결과물인 FfmpegTranscoder 클래스의 코드를 보자.


public class FfmpegTranscoder implements Transcoder {


    @Override

    public List<File> transcode(File multimediaFile,

            List<OutputFormat> outputFormats) {

        List<File> results = new ArrayList<File>();

        for (OutputFormat format : outputFormats) {

            results.add(transcode(multimediaFile, format));

        }

        return results;

    }


    private File transcode(File sourceFile, OutputFormat format) {

        IMediaReader reader = ToolFactory.makeReader(sourceFile

                .getAbsolutePath());


        String outputFile = "outputFile.mp4"; // 항상 확장자, 파일 명이 같음

        VideoConverter converter = new VideoConverter(outputFile, reader,

                format);

        ...

    }


}


원하는 파일이 MP4인지 AVI인지에 상관없이 항상 파일명은 outputFile.mp4 이다. 이제 이 부분을 건드려볼 차례이다.


우선, 확장자는 멀티미디어 컨테이너에 따라 다르다. 예를 들어, AVI, MP4, MOV, FLV 등이 컨테이너이며, 보통 이 이름을 파일의 확장자로 사용한다. 또한, 각 컨테이너의 특징에 따라 사용가능한 코덱에 제한이 있다고도 한다. 하튼, 필자는 이 부분의 전문가는 아니기 때문에 컨테이너 포맷이나 코덱 등의 정보가 궁금한 사람은 따로 정보를 검색해보시라.


컨테이너 모델 추가


확장자를 검사하도록 테스트를 먼저 수정하자. 확장자를 검사하기 적당한 위치는 VideoFormatVerifier 클래스이다. 이 클래스에 확장자를 확인하는 기능을 추가해 넣도록 하자.


public class VideoFormatVerifier {

    ...

    public VideoFormatVerifier(OutputFormat expectedFormat, File videoFile) {

        this.expectedFormat = expectedFormat;

        this.videoFile = videoFile;

    }


    public void verify() {

        try {

            assertExtension();

            makeContainer();

            extractMetaInfoOfVideo();

            assertVideoFile();

        } finally {

            closeContainer();

        }

    }


    private void assertExtension() {

        assertEquals(expectedFormat.getFileExtenstion()fileExtenstion());

    }


    private String fileExtenstion() {

        String filePath = videoFile.getAbsolutePath();

        int lastDotIdx = filePath.lastIndexOf(".");

        String extension = filePath.substring(lastDotIdx + 1);

        return extension;

    }


OutputFormat의 getFileExtension() 메서드가 파일 확장자를 제공한다고 했다. 이 메서드가 없으니 아래와 같이 추가해서 기존의 테스트가 통과되도록 하자.


public class OutputFormat {


    private int width;

    private int height;

    private int bitrate;

    ...

    public String getFileExtension() {

        return "mp4"; //일단 테스트가 통과하도록

    }


    public VideoCodec getVideoCodec() {

        return videoCodec;

    }


    public AudioCodec getAudioCodec() {

        return audioCodec;

    }


}


FfmpegTranscoderTest를 실행해 보자. 녹색! 통과다.


이제 본격적으로 컨테이너 정보를 추가해 보자. 파일의 확장자를 결졍짓는 것은 컨테이너이므로, 코덱과 별개로 컨테이너 정보를 추가하자.


public enum Container {


    MP4(VideoCodec.H264, AudioCodec.AAC, "mp4"),

    AVI(VideoCodec.MPEG4,AudioCodec.MP3, "avi");


    private VideoCodec defaultVideoCodec;

    private AudioCodec defaultAudioCodec;

    private String fileExtension;


    private Container(VideoCodec defaultVideoCodec,

            AudioCodec defaultAudioCodec, String fileExtenstion) {

        this.defaultVideoCodec = defaultVideoCodec;

        this.defaultAudioCodec = defaultAudioCodec;

        this.fileExtension = fileExtenstion;

    }


    public VideoCodec getDefaultVideoCodec() {

        return defaultVideoCodec;

    }


    public AudioCodec getDefaultAudioCodec() {

        return defaultAudioCodec;

    }


    public String getFileExtension() {

        return fileExtension;

    }


}


Container는 enum 타입으로 각 값은 해당 컨테이너의 기본 코덱 정보와 확장자 정보를 갖도록 했다.


Container를 추가했으므로, 변환 결과물 정보를 담는 OutputFormat에 추가할 차례이다. OutputFormat이 Container를 갖도록 필드를 추가혹, 생성자를 통해서 Container를 전달받도록 수정하자.


public class OutputFormat {


    private int width;

    private int height;

    private int bitrate;

    private Container container;

    private VideoCodec videoCodec;

    private AudioCodec audioCodec;


    public OutputFormat(int width, int height, int bitrate,

            Container container, VideoCodec videoCodec, AudioCodec audioCodec) {

        this.width = width;

        this.height = height;

        this.bitrate = bitrate;

        this.container = container;

        this.videoCodec = videoCodec;

        this.audioCodec = audioCodec;

    }

    ...

    public String getFileExtension() {

        return container.getFileExtension();

    }

    ...

}


OutputFormat에 getFileExtension() 메서드가 추가되었다. 이제 VideoFormatVerifier 테스트에 추가했던 확장자 확인 코드가 정상적으로 컴파일된다.


public class VideoFormatVerifier {

    ...

    ...

    private void assertExtension() {

        assertEquals(expectedFormat.getFileExtenstion(), fileExtenstion()); // 컴파일 됨

    }



위 코드는 정상적으로 컴파일되지만, OutputFormat 생성자에 새로운 파라미터가 추가되었기 때문에, OutputFormat 객체를 생성하는 테스트 코드에서는 컴파일 에러가 발생한다. 


public class VideoConverterTest {

    ...

    @Test

    public void transcode() {

        IMediaReader reader = ToolFactory.makeReader(SOURCE_FILE);


        OutputFormat outputFormat = new OutputFormat(WIDTH, HEIGHT, BITRATE,

                VideoCodec.H264, AudioCodec.AAC);

        VideoConverter writer = new VideoConverter(TRANSCODED_FILE, reader,

                outputFormat);

        ...

    }

}



public class FfmpegTranscoderTest {

    ...

    @Test

    public void transcodeWithOneOutputFormat() {

        File multimediaFile = new File("src/test/resources/sample.avi");

        List<OutputFormat> outputFormats = new ArrayList<OutputFormat>();

        outputFormats.add(new OutputFormat(160, 120, 150, VideoCodec.H264,

                AudioCodec.AAC));

        List<File> transcodedFiles = transcoder.transcode(multimediaFile,

                outputFormats);

        ...

    }

}


컴파일 에러가 나지 않도록 OutputFormat 생성자에 Container를 값으로 전달해 준다.


public class VideoConverterTest {


    @Test

    public void transcode() {

        IMediaReader reader = ToolFactory.makeReader(SOURCE_FILE);


        OutputFormat outputFormat = new OutputFormat(WIDTH, HEIGHT, BITRATE,

                Container.MP4, VideoCodec.H264, AudioCodec.AAC);

        VideoConverter writer = new VideoConverter(TRANSCODED_FILE, reader,

                outputFormat);

        ...

    }

}



public class FfmpegTranscoderTest {

    ...

    @Test

    public void transcodeWithOneOutputFormat() {

        File multimediaFile = new File("src/test/resources/sample.avi");

        List<OutputFormat> outputFormats = new ArrayList<OutputFormat>();

        outputFormats.add(new OutputFormat(160, 120, 150, Container.MP4

       VideoCodec.H264, AudioCodec.AAC));

        ...

    }

}


위와 같이 변경했으면, 두 테스트가 정상적으로 동작하는지 다시 테스트 해 본다. 녹색! 통과다.


AVI 변환 추가


이제 AVI 파일로 변환해주는 기능을 추가해 넣고 테스트를 해 보자. 이를 위해 FfmpegTranscoderTest를 다음과 같이 작성하였다. (음,, 바로 이전 코드하고 좀 많이 변경되었는데, 하나 하나 설명하지 않고 한 번에 넘어간 점 양해 바란다. 중복된 부분을 별도 메서드로 분리하고 setup에서 초기화를 진행하도록 수정했다.)


public class FfmpegTranscoderTest {


    private Transcoder transcoder;

    private File multimediaFile;

    private List<OutputFormat> outputFormats;


    private OutputFormat mp4Format;

    private OutputFormat aviFormat;


    @Before

    public void setup() {

        outputFormats = new ArrayList<OutputFormat>();

        mp4Format = new OutputFormat(160, 120, 150, Container.MP4,

                VideoCodec.H264, AudioCodec.AAC);

        aviFormat = new OutputFormat(160, 120, 150, Container.AVI,

                VideoCodec.MPEG4, AudioCodec.MP3);

        multimediaFile = new File("src/test/resources/sample.avi");


        transcoder = new FfmpegTranscoder(namingRule);

    }


    @Test

    public void transcodeWithOneMp4OutputFormat() {

        outputFormats.add(mp4Format);

        executeTranscoderAndAssert();

    }


    private void executeTranscoderAndAssert() {

        List<File> transcodedFiles = transcoder.transcode(multimediaFile,

                outputFormats);

        assertEquals(1, transcodedFiles.size());

        assertTrue(transcodedFiles.get(0).exists());

        VideoFormatVerifier.verifyVideoFormat(outputFormats.get(0),

                transcodedFiles.get(0));

    }


    @Test

    public void transcodeWithOneAviOutputFormat() {

        outputFormats.add(aviFormat);

        executeTranscoderAndAssert();

    }

}


AVI 변환에 대한 테스트 코드를 넣었다. 테스트 실행! 실패다. 실패 이유는 다음과 같다.


실패 메시지:

org.junit.ComparisonFailure: expected:<[avi]> but was:<[mp4]>

    at org.junit.Assert.assertEquals(Assert.java:123)

    at org.junit.Assert.assertEquals(Assert.java:145)

    at org.chimi.s4t.infra.ffmpeg.VideoFormatVerifier.assertExtension(VideoFormatVerifier.java:47)

    at org.chimi.s4t.infra.ffmpeg.VideoFormatVerifier.verify(VideoFormatVerifier.java:37)

    ...


실재 난 테스트 메서드:

    @Test

    public void transcodeWithOneAviOutputFormat() {

        outputFormats.add(aviFormat);

        executeTranscoderAndAssert();

    }

}


파일명이 AVI이길 기대했는데 실제 파일 이름은 MP4 여서 테스트를 통과하지 못했다. 이제 테스트를 통과시켜 보자. FfmpegTranscoder가 OutputFormat을 이용해서 파일의 확장자를 결정하도록 수정했다.


public class FfmpegTranscoder implements Transcoder {


    @Override

    public List<File> transcode(File multimediaFile,

            List<OutputFormat> outputFormats) {

        List<File> results = new ArrayList<File>();

        for (OutputFormat format : outputFormats) {

            results.add(transcode(multimediaFile, format));

        }

        return results;

    }


    private File transcode(File sourceFile, OutputFormat format) {

        IMediaReader reader = ToolFactory.makeReader(sourceFile

                .getAbsolutePath());


        String outputFile = getFileName(format);

        VideoConverter converter = new VideoConverter(outputFile, reader,

                format);

        reader.addListener(converter);

        while (reader.readPacket() == null)

            do {

            } while (false);

        return new File(outputFile);

    }


    private String getFileName(OutputFormat format) {

        return "outputFile." + format.getFileExtension(); // 기존 "outputFile.mp4"

    }


}


테스트를 실행해보자. 녹색! 통과다!


지금까지 한 번에 1개의 변환을 처리하는 부분에 집중했는데, 다음 연습 8에서는 여러 형식으로 변환하는 기능을 추가해 보도록 하자.








+ Recent posts