/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under GNU GENERAL PUBLIC LICENSE Version 3.
*/
package com.ttProject.xuggle.test;
import java.nio.ByteBuffer;
import org.apache.log4j.Logger;
import org.junit.Test;
import com.ttProject.util.BufferUtil;
import com.ttProject.util.HexUtil;
import com.xuggle.ferry.IBuffer;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.IPixelFormat;
import com.xuggle.xuggler.IRational;
import com.xuggle.xuggler.IStream;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.IVideoResampler;
import com.xuggle.xuggler.IPixelFormat.Type;
import com.xuggle.xuggler.IStreamCoder.Direction;
import com.xuggle.xuggler.IStreamCoder.Flags;
/**
* sample to make mp4 from raw rgb data.
* @see https://groups.google.com/forum/#!topic/xuggler-users/Qn3lTdUH-y0
* @author taktod
*/
public class MakePictureFromRGB24Test {
/** logger */
private Logger logger = Logger.getLogger(MakePictureFromRGB24Test.class);
@Test
public void makeMp4() {
IStreamCoder decoder = null;
IStreamCoder encoder = null;
IContainer container = null;
IVideoResampler resampler = null;
IPacket sourcePacket = IPacket.make();
IPacket targetPacket = IPacket.make();
try {
container = createContainer();
encoder = createEncoder(container);
decoder = createDecoder();
resampler = createResampler(encoder, decoder);
container.writeHeader();
writeBody(decoder, encoder, container, resampler, sourcePacket, targetPacket);
container.writeTrailer();
}
catch(Exception e) {
logger.fatal("error end", e);
}
finally {
if(decoder != null) {
decoder.close();
decoder = null;
}
if(encoder != null) {
encoder.close();
encoder = null;
}
if(container != null) {
container.close();
container = null;
}
}
}
private void writeBody(IStreamCoder decoder, IStreamCoder encoder, IContainer container, IVideoResampler resampler, IPacket sourcePacket, IPacket targetPacket)
throws Exception {
ByteBuffer bmpHeader = HexUtil.makeBuffer("424D36090000000000003600000028000000200000001800000001001800000000000009000000000000000000000000000000000000");
int dataSize = 3 * 32 * 24; // 24bit * width x height
for(int i = 0;i < 255;i ++) {
ByteBuffer rgb = ByteBuffer.allocate(dataSize);
for(int j = 0;j < dataSize / 3;j ++) {
rgb.put((byte)0); // b
rgb.put((byte)0); // g
rgb.put((byte)i); // r
}
rgb.flip();
ByteBuffer targetBuffer = BufferUtil.connect(bmpHeader, rgb);
int size = targetBuffer.remaining();
// make source Packet
IBuffer bufData = IBuffer.make(null, targetBuffer.array(), 0, size);
sourcePacket.setData(bufData);
sourcePacket.setFlags(0);
sourcePacket.setDts(i * 10);
sourcePacket.setPts(i * 10);
sourcePacket.setTimeBase(IRational.make(1, 1000));
sourcePacket.setComplete(true, size);
sourcePacket.setKeyPacket(true);
logger.info(sourcePacket);
decode(decoder, encoder, container, resampler, sourcePacket, targetPacket);
}
}
private void decode(IStreamCoder decoder, IStreamCoder encoder, IContainer container, IVideoResampler resampler, IPacket sourcePacket, IPacket targetPacket)
throws Exception {
// make videoPicture
IVideoPicture picture = IVideoPicture.make(decoder.getPixelType(), decoder.getWidth(), decoder.getHeight());
int offset = 0;
while(offset < sourcePacket.getSize()) {
int bytesDecoded = decoder.decodeVideo(picture, sourcePacket, offset);
if(bytesDecoded <= 0) {
throw new Exception("fail to decode");
}
offset += bytesDecoded;
if(picture.isComplete()) {
picture = resamplePicture(encoder, resampler, picture);
logger.info(picture);
encode(encoder, container, picture, targetPacket);
}
}
}
private IVideoPicture resamplePicture(IStreamCoder encoder, IVideoResampler resampler, IVideoPicture picture) throws Exception {
if(resampler == null) {
return picture;
}
IVideoPicture resampledPicture = IVideoPicture.make(encoder.getPixelType(), encoder.getWidth(), encoder.getHeight());
int retval = resampler.resample(resampledPicture, picture);
if(retval <= 0) {
throw new Exception("failed to resampled picture");
}
return resampledPicture;
}
private void encode(IStreamCoder encoder, IContainer container, IVideoPicture picture, IPacket targetPacket) throws Exception {
if(encoder.encodeVideo(targetPacket, picture, 0) < 0) {
throw new Exception("failed to encode");
}
if(targetPacket.isComplete()) {
logger.info(targetPacket);
if(container.writePacket(targetPacket) < 0) {
throw new Exception("failed to write to container");
}
}
}
private IVideoResampler createResampler(IStreamCoder encoder, IStreamCoder decoder) throws Exception {
if(encoder.getWidth() != decoder.getWidth()
|| encoder.getHeight() != decoder.getHeight()
|| encoder.getPixelType() != decoder.getPixelType()) {
IVideoResampler resampler = IVideoResampler.make(
encoder.getWidth(), encoder.getHeight(), encoder.getPixelType(),
decoder.getWidth(), decoder.getHeight(), decoder.getPixelType());
return resampler;
}
else {
return null;
}
}
private IContainer createContainer() throws Exception {
IContainer container = IContainer.make();
if(container.open("output.mp4", IContainer.Type.WRITE, null) < 0) {
throw new Exception("failed to open output container.");
}
return container;
}
private IStreamCoder createDecoder() throws Exception {
IStreamCoder videoDecoder = IStreamCoder.make(Direction.DECODING, ICodec.ID.CODEC_ID_RAWVIDEO);
videoDecoder.setTimeBase(IRational.make(1, 1000));
videoDecoder.setPixelType(Type.BGR24);
videoDecoder.setWidth(32);
videoDecoder.setHeight(24);
if(videoDecoder.open(null, null) < 0) {
throw new Exception("failed to open decoder");
}
return videoDecoder;
}
private IStreamCoder createEncoder(IContainer container) throws Exception {
IStream stream = container.addNewStream(ICodec.ID.CODEC_ID_H264);
IStreamCoder videoEncoder = stream.getStreamCoder();
IRational frameRate = IRational.make(15, 1); // 15fps
videoEncoder.setNumPicturesInGroupOfPictures(5); // gop 5
videoEncoder.setBitRate(650000); // 650kbps
videoEncoder.setBitRateTolerance(9000);
videoEncoder.setWidth(320);
videoEncoder.setHeight(240);
videoEncoder.setGlobalQuality(10);
videoEncoder.setFrameRate(frameRate);
videoEncoder.setTimeBase(IRational.make(1, 1000));
videoEncoder.setProperty("level", "30");
videoEncoder.setProperty("coder", "0");
videoEncoder.setProperty("qmin", "10");
videoEncoder.setProperty("bf", "0");
videoEncoder.setProperty("wprefp", "0");
videoEncoder.setProperty("cmp", "+chroma");
videoEncoder.setProperty("partitions", "-parti8x8+parti4x4+partp8x8+partp4x4-partb8x8");
videoEncoder.setProperty("me_method", "hex");
videoEncoder.setProperty("subq", "5");
videoEncoder.setProperty("me_range", "16");
videoEncoder.setProperty("keyint_min", "25");
videoEncoder.setProperty("sc_threshold", "40");
videoEncoder.setProperty("i_qfactor", "0.71");
videoEncoder.setProperty("b_strategy", "0");
videoEncoder.setProperty("qcomp", "0.6");
videoEncoder.setProperty("qmax", "30");
videoEncoder.setProperty("qdiff", "4");
videoEncoder.setProperty("directpred", "0");
videoEncoder.setProperty("profile", "main");
videoEncoder.setProperty("cqp", "0");
videoEncoder.setFlag(Flags.FLAG_LOOP_FILTER, true);
videoEncoder.setFlag(Flags.FLAG_CLOSED_GOP, true);
ICodec codec = videoEncoder.getCodec();
IPixelFormat.Type findType = null;
for(IPixelFormat.Type type : codec.getSupportedVideoPixelFormats()) {
if(findType == null) {
findType = type;
}
if(type == IPixelFormat.Type.YUV420P) {
findType = type;
break;
}
}
if(findType == null) {
throw new Exception("pixelFormat is unknown");
}
videoEncoder.setPixelType(findType);
if(videoEncoder.open(null, null) < 0) {
throw new Exception("failed to open encoder");
}
return videoEncoder;
}
}