All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ru.sbtqa.monte.media.converter.FFRtoVFRConverter Maven / Gradle / Ivy

There is a newer version: 1.1.0-JAVA7
Show newest version
/* @(#)FFRtoVFRConverter.java
 * Copyright © 2011 Werner Randelshofer, Switzerland. 
 * You may only use this software in accordance with the license terms.
 */
package ru.sbtqa.monte.media.converter;

import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.util.ArrayList;
import java.util.Arrays;
import ru.sbtqa.monte.media.AbstractVideoCodec;
import ru.sbtqa.monte.media.Buffer;
import static ru.sbtqa.monte.media.BufferFlag.*;
import ru.sbtqa.monte.media.Format;
import static ru.sbtqa.monte.media.VideoFormatKeys.*;
import ru.sbtqa.monte.media.math.Rational;

/**
 * This codec converts frames from a fixed frame rate into a variable frame rate
 * by coalescing identical frames.
 * 
 * This codec can be used when the input source has a fixed frame rate and the
 * output sink supports a variable frame rate.
 *
 * @author Werner Randelshofer
 * @version $Id: FFRtoVFRConverter.java 364 2016-11-09 19:54:25Z werner $
 */
public class FFRtoVFRConverter extends AbstractVideoCodec {

    private Rational timeStamp;
    private Rational duration;
    private int[] inputColors;
    private int[] previousColors;
    private Object previousPixels;

    public FFRtoVFRConverter() {
        super(new Format[]{
            new Format(DataClassKey, BufferedImage.class), //
        },
              new Format[]{
                  new Format(DataClassKey, BufferedImage.class, FixedFrameRateKey, false), //
              });
        name = "FFR to VFR";
    }

    @Override
    public Format[] getOutputFormats(Format input) {
        Format forceVFR = new Format(FixedFrameRateKey, false);

        ArrayList of = new ArrayList(outputFormats.length);
        for (Format f : outputFormats) {
            of.add(forceVFR.append(f.append(input)));
        }
        return of.toArray(new Format[of.size()]);
    }

    @Override
    public Format setOutputFormat(Format f) {
        Format forceFFR = new Format(FixedFrameRateKey, true);
        Format forceVFR = new Format(FixedFrameRateKey, false);

        for (Format sf : getOutputFormats(f)) {
            if (sf.matches(f)
                  || forceFFR.append(sf).matches(f)
                  || forceVFR.append(sf).matches(f)) {
                this.outputFormat = forceVFR.append(f);
                return sf;
            }
        }
        this.outputFormat = null;
        return null;
    }

    @Override
    public void reset() {
        this.timeStamp = null;
    }

    @Override
    public int process(Buffer in, Buffer out) {
        out.setMetaTo(in);
        Format vf = inputFormat;

        if (!in.isFlag(KEYFRAME)) {
            // This codec can only process keyframes.
            return CODEC_FAILED;
        }

        if (in.isFlag(END_OF_MEDIA) && in.isFlag(DISCARD)) {
            // => End of media reached. Flush buffer.
            out.setFlag(END_OF_MEDIA, true);
            if (duration.isZero()) {
                out.setFlag(DISCARD, true);
            } else {
                out.setFlag(END_OF_MEDIA, false);
                out.setFlag(DISCARD, false);
                IndexColorModel newColorModel = new IndexColorModel(8, 256, previousColors, 0, false, -1, DataBuffer.TYPE_BYTE);

                WritableRaster newRaster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
                      vf.get(WidthKey), vf.get(HeightKey), 1, null);
                byte[] newPixels = ((DataBufferByte) newRaster.getDataBuffer()).getData();
                System.arraycopy(previousPixels, 0, newPixels, 0, newPixels.length);

                out.data = new BufferedImage(newColorModel, newRaster, false, null);
                out.sampleDuration = duration;
                out.timeStamp = timeStamp;
                duration = new Rational(0, 1);
            }
            return CODEC_OK;
        }
        if (in.isFlag(DISCARD)) {
            // => Discard discarded buffer.
            out.setFlag(DISCARD, true);
            return CODEC_OK;
        }

        BufferedImage inputImage = (BufferedImage) in.data;
        byte[] inputPixels = ((DataBufferByte) inputImage.getRaster().getDataBuffer()).getData();
        IndexColorModel inputColorModel = (IndexColorModel) inputImage.getColorModel();
        if (inputColors == null) {
            inputColors = new int[256];
        }
        inputColorModel.getRGBs(inputColors);

        if (previousPixels == null) {
            // => First image. Copy data and discard output buffer.
            previousPixels = inputPixels.clone();
            previousColors = inputColors.clone();
            duration = in.sampleDuration;
            timeStamp = in.timeStamp;
            out.setFlag(DISCARD, true);
        } else // => Not the first image. Convert fixed rate to variable rate if images are the same.
        if (Arrays.equals((byte[]) previousPixels, inputPixels)
              && Arrays.equals(previousColors, inputColors)) {
            duration = duration.add(in.sampleDuration);
            out.setFlag(DISCARD, true);
        } else {
            IndexColorModel newColorModel = new IndexColorModel(8, 256, previousColors, 0, false, -1, DataBuffer.TYPE_BYTE);

            WritableRaster newRaster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
                  vf.get(WidthKey), vf.get(HeightKey), 1, null);
            byte[] newPixels = ((DataBufferByte) newRaster.getDataBuffer()).getData();
            System.arraycopy(previousPixels, 0, newPixels, 0, newPixels.length);

            out.data = new BufferedImage(newColorModel, newRaster, false, null);
            out.sampleDuration = duration;
            out.timeStamp = timeStamp;
            duration = in.sampleDuration;
            timeStamp = in.timeStamp;
            System.arraycopy(inputPixels, 0, previousPixels, 0, inputPixels.length);
            System.arraycopy(inputColors, 0, previousColors, 0, inputColors.length);

        }
        return CODEC_OK;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy