Merge two videos in Java , video fade in from right side and fade out to left


    Two videos , one video fade out from right to left side , the other video fade in from right to left.

 We can use JavaCV FFmpeg package to create such video effective.

The steps:

1.Load two video , video one and video two

2.Get the video information like frame rate , frame number,duration

3.Get the start frame number that start to fade out/in for both videos

4.Get the end frame number that fade out/in complete for both videos

5.Merge above two frames with corresponding gradient

The code:

public void mergeVideo()
{
    String videoOne = "video-1.mp4"
    String videoTwo = "video-2.mp4"
    String mergedVideo="mergedVideo.mp4";
    try {
        //load video one
        FFmpegFrameGrabber grabberOne = FFmpegFrameGrabber.createDefault(videoOne);
        grabberOne.start();
        //load video two
        FFmpegFrameGrabber grabberTwo = FFmpegFrameGrabber.createDefault(videoTwo);
        grabberTwo.start();
        //get the width and height for merged video
        int maxWidth=grabberOne.getImageWidth()>grabberTwo.getImageWidth()?grabberOne.getImageWidth():grabberTwo.getImageWidth();
        int maxHeight=grabberOne.getImageHeight()>grabberTwo.getImageHeight()?grabberOne.getImageHeight():grabberTwo.getImageHeight();
        //create merged video
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(mergedVideo, maxWidth,
        maxHeight);
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_MPEG4);
        recorder.setFormat("mp4");
        recorder.setFrameRate(grabberOne.getFrameRate());
        recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
        recorder.start();
        
        //get amount of frame 
        int totalOne =grabberOne.getLengthInFrames();
        double frameRateOne=grabberOne.getFrameRate();
        int totalTwo=grabberTwo.getLengthInFrames();
        double frameRateTwo=grabberTwo.getFrameRate();
        
        //get duration in seconds
        long durationInSec = grabberOne.getFormatContext().duration() / 1000000;
        long durationTwoInSec = grabberTwo.getFormatContext().duration() / 1000000;
        //get start frame number for video one fade out
        int startFrameNum = getStartOrEndFrameNumber(3, durationInSec, totalOne);
        int endFrameNum = getStartOrEndFrameNumber(8, durationInSec, totalOne);
        //get start framte number for video two fade in 
        int grabeTwoEndFrameNum =getStartOrEndFrameNumber(5, durationTwoInSec, totalTwo);
        LOGGER.info("1st duration {}",durationInSec);
        LOGGER.info("1st total {}",totalOne);
        LOGGER.info("1st frameRate {}",frameRateOne);
        LOGGER.info("1st start frame num {}",startFrameNum);
        LOGGER.info("1st end frame num {}",endFrameNum);
        LOGGER.info("2nd duration {}",durationTwoInSec);
        LOGGER.info("2nd total {}",totalTwo);
        LOGGER.info("2nd frameRate {}",frameRateTwo);
        LOGGER.info("2nd end frame num {}",grabeTwoEndFrameNum);
        LOGGER.info("Uniform width {}, height {}",maxWidth,maxHeight);
        
        int i=0;
        double progress;
        Frame fTwo,fOne;
        Mat fadeInMat,fadeOutMat,resizedMat;
        OpenCVFrameConverter.ToMat conveter = new OpenCVFrameConverter.ToMat();
        DecimalFormat df = new DecimalFormat("#.00");
        while (i<=totalTwo) {
            i++;
            // record video one frame 
            if (i <= startFrameNum) {
            fOne = grabberOne.grabImage();
            recorder.record(fOne);
            }
          // video one fade out , video two fade in 
            else if (i > startFrameNum && i < endFrameNum) {
            resizedMat=new Mat();
            fOne = grabberOne.grabImage();
            fTwo = grabberTwo.grabImage();
            //org.bytedeco.opencv.global.opencv_imgproc.resize
            //resize frame
            resize(conveter.convertToMat(fOne), resizedMat, new Size(maxWidth, maxHeight));
            fadeOutMat = resizedMat;
            fadeInMat = conveter.convertToMat(fTwo);
            //calculate the picture weight
            progress = (i - startFrameNum) / (double) (endFrameNum - startFrameNum);
            //merge two frame and record it
            recorder.record(mergePhotoToFrame(fadeOutMat, fadeInMat, Double.valueOf(df.format(progress))));
            } else {
                fTwo = grabberTwo.grabImage();
                if (fTwo == null)
                    break;
                recorder.record(fTwo);
            }
        }
        recorder.close();
        grabberOne.close();
        grabberTwo.close();
    } catch (Exception e) {
        LOGGER.error("", e);
    }
}

public static int getStartOrEndFrameNumber(long startOrEndAtSecond, long durationInSecond, int totalFrame) {
    if (startOrEndAtSecond >= durationInSecond)
        return (int) durationInSecond;
    double percent = startOrEndAtSecond / (double) durationInSecond;
    return (int) Math.round(totalFrame * percent);
}

public static Frame mergePhotoToFrame(Mat srcFadeOut,Mat srcFadeIn,double progress)
{
    OpenCVFrameConverter.ToMat conveter = new OpenCVFrameConverter.ToMat();
    return conveter.convert(mergePhoto(srcFadeOut,srcFadeIn,progress));
}

public static Mat mergePhoto(Mat srcFadeOut,Mat srcFadeIn,double progress)
{
    if(progress <0||progress>1)
        return null;
    double srcAlpha=1-progress;
    Mat dest=new Mat();
    opencv_core.addWeighted(srcFadeOut,srcAlpha , srcFadeIn,progress, 0.0, dest);
    return dest;
}