/*
* @(#)FrameRateConverter.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.converter;
import org.monte.media.AbstractVideoCodec;
import org.monte.media.Buffer;
import org.monte.media.Format;
import java.util.ArrayList;
import org.monte.media.math.Rational;
import java.awt.image.BufferedImage;
import static java.lang.Math.*;
import static org.monte.media.VideoFormatKeys.*;
import static org.monte.media.BufferFlag.*;
/**
* This codec converts frames from one frame rate into another.
* <p>
* Makes frames longer if the output time is behind the input time.
* Drops frames if the output time runs away from the input time.
* <p>
* The output of the converter has a variable frame rate but it may still
* contain identical frames.
* Thus an additional conversion step with {@link FFRtoVFRConverter} is needed.
* <p>
* This codec is needed when the input source has a different frame rate than
* the output sink.
*
* @author Werner Randelshofer
* @version $Id: FrameRateConverter.java 299 2013-01-03 07:40:18Z werner $
*/
public class FrameRateConverter extends AbstractVideoCodec {
private Rational inputTime;
private Rational outputTime;
public FrameRateConverter() {
super(new Format[]{
new Format(MediaTypeKey, MediaType.VIDEO,//
EncodingKey,ENCODING_BUFFERED_IMAGE,
DataClassKey, BufferedImage.class), //
},
new Format[]{
new Format(MediaTypeKey, MediaType.VIDEO,//
EncodingKey,ENCODING_BUFFERED_IMAGE,
DataClassKey, BufferedImage.class,
FixedFrameRateKey,false), //
});
name = "Frame Rate Converter";
}
@Override
public Format[] getOutputFormats(Format input) {
Format forceVFR = new Format(MediaTypeKey, MediaType.VIDEO,ENCODING_BUFFERED_IMAGE,
DataClassKey, BufferedImage.class, FixedFrameRateKey,false);
ArrayList<Format> of = new ArrayList<Format>(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(MediaTypeKey, MediaType.VIDEO,ENCODING_BUFFERED_IMAGE,
DataClassKey, BufferedImage.class, FixedFrameRateKey,true);
Format forceVFR = new Format(MediaTypeKey, MediaType.VIDEO,ENCODING_BUFFERED_IMAGE,
DataClassKey, BufferedImage.class, 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() {
inputTime = null;
outputTime = null;
}
@Override
public int process(Buffer in, Buffer out) {
// Pass discarded buffers.
out.setMetaTo(in);
if (in.isFlag(DISCARD)) {
return CODEC_OK;
}
// Initialize codec.
if (inputTime == null) {
inputTime = new Rational(0, 1);
outputTime = new Rational(0, 1);
}
// Convert from input frame rate to output frame rate.
Format vf = (Format) outputFormat;
inputTime = inputTime.add(in.sampleDuration);
Rational outputDuration = inputTime.subtract(outputTime);
long jiffies = vf.get(FrameRateKey).getNumerator();
outputDuration = outputDuration.round(jiffies);
long outputMediaDuration = (int) (outputDuration.getNumerator() * jiffies / outputDuration.getDenominator());
long remainder = outputMediaDuration % vf.get(FrameRateKey).getDenominator();
outputDuration = new Rational(outputMediaDuration, jiffies);
// Drop frame if outputTime is too far ahead of inputTime.
if (outputDuration.isLessOrEqualZero()) {
out.setFlag(DISCARD, true);
out.sampleDuration = outputDuration;
//System.out.println("FrameRateConverter inTS=" + in.timeStamp + " outTS=" + out.timeStamp + " inDur=" + in.sampleDuration + " outDur=" + out.sampleDuration.toDescriptiveString()+" DISCARD");
return CODEC_OK;
}
// Produce time converted frame.
out.format = outputFormat;
out.setDataTo(in);
out.timeStamp = outputTime;
out.sampleDuration = outputDuration;
outputTime = outputTime.add(outputDuration);
// System.out.println("FrameRateConverter inTS=" + in.timeStamp + " outTS=" + out.timeStamp + " inDur=" + in.sampleDuration + " outDur=" + out.sampleDuration.toDescriptiveString());
return CODEC_OK;
}
}