package streamExample.handler;
import com.xuggle.xuggler.*;
import com.xuggle.xuggler.IPixelFormat.Type;
import com.xuggle.xuggler.IStreamCoder.Direction;
import com.xuggle.xuggler.video.ConverterFactory;
import com.xuggle.xuggler.video.IConverter;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import streamExample.handler.frame.FrameEncoder;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class H264StreamEncoder extends OneToOneEncoder {
protected final static Logger logger = LoggerFactory.getLogger(Logger.class);
protected final IStreamCoder iStreamCoder = IStreamCoder.make(Direction.ENCODING, ICodec.ID.CODEC_ID_H264);
protected final IPacket iPacket = IPacket.make();
protected long startTime;
protected final Dimension dimension;
protected final FrameEncoder frameEncoder;
public H264StreamEncoder(Dimension dimension, boolean usingInternalFrameEncoder) {
super();
this.dimension = dimension;
if (usingInternalFrameEncoder) {
frameEncoder = new FrameEncoder(4);
} else {
frameEncoder = null;
}
initialize();
}
private void initialize() {
// todo: fps can change!!!
iStreamCoder.setNumPicturesInGroupOfPictures(25);
// todo: settings in gui
iStreamCoder.setBitRate(200000);
iStreamCoder.setBitRateTolerance(10000);
iStreamCoder.setPixelType(Type.YUV420P);
iStreamCoder.setHeight(dimension.height);
iStreamCoder.setWidth(dimension.width);
iStreamCoder.setFlag(IStreamCoder.Flags.FLAG_QSCALE, true);
iStreamCoder.setGlobalQuality(0);
// rate
// todo: fps can change!!!
IRational rate = IRational.make(25, 1);
iStreamCoder.setFrameRate(rate);
// time base
//iStreamCoder.setAutomaticallyStampPacketsForStream(true);
iStreamCoder.setTimeBase(IRational.make(rate.getDenominator(), rate.getNumerator()));
IMetaData codecOptions = IMetaData.make();
codecOptions.setValue("tune", "zerolatency"); // equals -tune zerolatency in ffmpeg
//open it
int revl = iStreamCoder.open(codecOptions, null);
if (revl < 0) {
throw new RuntimeException("could not open the coder");
}
}
@Override
protected Object encode(ChannelHandlerContext ctx, Channel channel,
Object msg) throws Exception {
return encode(msg);
}
public Object encode(Object msg) throws Exception {
if (msg == null) {
return null;
}
if (!(msg instanceof BufferedImage)) {
throw new IllegalArgumentException("your need to pass into an bufferedimage");
}
logger.info("encode the frame");
BufferedImage bufferedImage = (BufferedImage) msg;
//here is the encode
//convert the image
BufferedImage convertedImage = ImageUtils.convertToType(bufferedImage, BufferedImage.TYPE_3BYTE_BGR);
IConverter converter = ConverterFactory.createConverter(convertedImage, Type.YUV420P);
//to frame
long now = System.currentTimeMillis();
if (startTime == 0) {
startTime = now;
}
IVideoPicture pFrame = converter.toPicture(convertedImage, (now - startTime) * 1000);
//pFrame.setQuality(0);
iStreamCoder.encodeVideo(iPacket, pFrame, 0);
//free the MEM
pFrame.delete();
converter.delete();
//write to the container
if (iPacket.isComplete()) {
//iPacket.delete();
//here we send the package to the remote peer
try {
ByteBuffer byteBuffer = iPacket.getByteBuffer();
if (iPacket.isKeyPacket()) {
logger.info("key frame");
}
ChannelBuffer channelBuffe = ChannelBuffers.copiedBuffer(byteBuffer.order(ByteOrder.BIG_ENDIAN));
if (frameEncoder != null) {
return frameEncoder.encode(channelBuffe);
}
return channelBuffe;
} finally {
iPacket.reset();
}
} else {
return null;
}
}
}