package us.sosia.video.stream.handler;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
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 us.sosia.video.stream.handler.frame.FrameEncoder;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IMetaData;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.IPixelFormat.Type;
import com.xuggle.xuggler.IRational;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IStreamCoder.Direction;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.video.ConverterFactory;
import com.xuggle.xuggler.video.IConverter;
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(){
//setup
iStreamCoder.setNumPicturesInGroupOfPictures(25);
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
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 convetedImage = ImageUtils.convertToType(bufferedImage, BufferedImage.TYPE_3BYTE_BGR);
IConverter converter = ConverterFactory.createConverter(convetedImage, Type.YUV420P);
//to frame
long now = System.currentTimeMillis();
if (startTime == 0) {
startTime = now;
}
IVideoPicture pFrame = converter.toPicture(convetedImage, (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;
}
}
}