package org.jcodec.movtool.streaming.tracks; import java.lang.IllegalStateException; import java.lang.System; import java.lang.IllegalArgumentException; import org.jcodec.codecs.h264.H264Encoder; import org.jcodec.codecs.h264.H264Utils; import org.jcodec.codecs.h264.encode.H264FixedRateControl; import org.jcodec.codecs.h264.io.model.SliceType; import org.jcodec.codecs.mpeg12.MPEGDecoder; import org.jcodec.codecs.mpeg12.Mpeg2Thumb2x2; import org.jcodec.codecs.mpeg12.Mpeg2Thumb4x4; import org.jcodec.common.VideoDecoder; import org.jcodec.common.logging.Logger; import org.jcodec.common.model.ColorSpace; import org.jcodec.common.model.Picture8Bit; import org.jcodec.common.model.Rect; import org.jcodec.common.model.Size; import org.jcodec.scale.ColorUtil; import org.jcodec.scale.Transform8Bit; import java.io.IOException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * An MPEG thumbnail to AVC transcoder implemented fully in java ( using jcodec * codecs ). * * @author The JCodec project * */ public class MPEGToAVCTranscoder { public static final int TARGET_RATE = 1024; private VideoDecoder decoder; private H264Encoder encoder; private Picture8Bit pic0; private Picture8Bit pic1; private Transform8Bit transform; private H264FixedRateControl rc; private int scaleFactor; private int thumbWidth; private int thumbHeight; public MPEGToAVCTranscoder(int scaleFactor) { this.scaleFactor = scaleFactor; rc = new H264FixedRateControl(MPEGToAVCTranscoder.TARGET_RATE); this.decoder = getDecoder(scaleFactor); this.encoder = new H264Encoder(rc); } protected VideoDecoder getDecoder(int scaleFactor) { switch (scaleFactor) { case 2: return new Mpeg2Thumb2x2(); case 1: return new Mpeg2Thumb4x4(); case 0: return new MPEGDecoder(); default: throw new IllegalArgumentException("Unsupported scale factor: " + scaleFactor); } } public ByteBuffer transcodeFrame(ByteBuffer src, ByteBuffer dst, boolean iframe, int poc) throws IOException { if (src == null) return null; if (pic0 == null) { Size size = new MPEGDecoder().getCodecMeta(src.duplicate()).getSize(); thumbWidth = size.getWidth() >> this.scaleFactor; thumbHeight = size.getHeight() >> this.scaleFactor; int mbW = (thumbWidth + 8) >> 4; int mbH = (thumbHeight + 8) >> 4; pic0 = Picture8Bit.create(mbW << 4, (mbH + 1) << 4, ColorSpace.YUV444); } Picture8Bit decoded = decoder.decodeFrame8Bit(src, pic0.getData()); if (pic1 == null) { pic1 = Picture8Bit.create(decoded.getWidth(), decoded.getHeight(), encoder.getSupportedColorSpaces()[0]); transform = ColorUtil.getTransform8Bit(decoded.getColor(), encoder.getSupportedColorSpaces()[0]); } Picture8Bit toEnc; if (transform != null) { transform.transform(decoded, pic1); toEnc = pic1; } else { toEnc = decoded; } pic1.setCrop(new Rect(0, 0, thumbWidth, thumbHeight)); int rate = MPEGToAVCTranscoder.TARGET_RATE; do { try { encoder.doEncodeFrame8Bit(toEnc, dst, iframe, poc, SliceType.I); break; } catch (BufferOverflowException ex) { Logger.warn("Abandon frame, buffer too small: " + dst.capacity()); rate -= 10; rc.setRate(rate); } } while (rate > 10); rc.setRate(MPEGToAVCTranscoder.TARGET_RATE); H264Utils.encodeMOVPacketInplace(dst); return dst; } protected static MPEGToAVCTranscoder createTranscoder(int scaleFactor) { return new MPEGToAVCTranscoder(scaleFactor); } }