Hello all!
I'm a new xuggle user so I would appreciate any help that I could
receive from you. :)
My objective is to generate a video from images so that the output
video would be encoded in H.264 format.
I have a working Java App that's similar to the Desktop Snapshot
tutorial. Actually, it is a modified version of the code made by Java
Code Geeks and published at the end of the following link:
http://www.javacodegeeks.com/2011/02/xuggler-tutorial-frames-capture-video.html
The main difference is that I am reading png files from a desktop
folder instead of capturing the screen.
I include my code at the end of this post. With that code, I haven't
been able to get an H.264 video. I understand I have to configure the
format using a ffmpeg presets file, which I've tried with the
following methods:
1.- Adding the following lines after line number 34.
isc = imw.getContainer().getStream(0).getStreamCoder();
int retval =Configuration.configure("/usr/local/xuggler/share/ffmpeg/
libx264-lossless_ultrafast.ffpreset",isc);
if (retval < 0)
{
isc.setProperty("crf", 22);
isc.setProperty("threads", 0);
throw new RuntimeException("could not configure coder from preset
file");
}
This approach throws the runtime exception (or a warning about an
invalid property in the preset file) and doesn't generate the desired
video.
This idea was taken from the comments on this:
http://forum.processing.org/topic/sketch-video-catpure-with-ffmpeg-and-xuggle
2.- The second method was the one suggested here:
http://tikiroomtiki.blogspot.com/2011/08/transcoding-video-to-h264-using-xuggler.html
Which didn't work either. Throws the warnings and doesn't generate an
H.264 video.
3.- Finally, I've implemented a method in the same
ScreenRecorderExample class that would take the generated video and
encode it in H.264, like this:
public static void TranscodeH264() {
String inputStream = "/home/pame/Desktop/mydesktop1.mp4";
String outputStream = "/home/pame/Desktop/mydesktop264.mp4";
String[] parameters = new String[] { "--acodec", "libfaac", "--
vcodec",
"libx264", "--vpreset",
"/usr/local/xuggler/share/ffmpeg/libx264-
lossless_ultrafast.ffpreset",
inputStream, outputStream };
Converter converter = new Converter();
Options options = converter.defineOptions();
CommandLine cmdLine;
try {
cmdLine = converter.parseOptions(options, parameters);
converter.run(cmdLine);
System.out.println("Finish!!!");
} catch (ParseException e) {
e.printStackTrace();
}
}
Inspired from this:
http://www.javacodegeeks.com/2010/05/rtmp-to-rtsp-re-stream-using-wowza-and.html#more
And *that* does the trick. It still shows the warnings, but the output
video (mydesktop264.mp4) is actually in H264 format and also its size
is 2.7 MB compared to the 6.4 MB from mydesktop1.mp4.
Basically, what happens (as I understand it) is that the App takes the
images, generates a mpeg4-2 (I think) video and then transcodes it to
H264 (or mpeg4-10). Even though I get what I wanted, I find it a
rather "dirty" solution, and I was wondering if there was a cleaner
way to obtain what I want: in a nutshell, generate (from images) a
video encoded in H264.
Also, if you could help me discover why the warnings (which are
something like "WARNING: com.xuggle.xuggler.Configuration - Invalid
property on object com.xuggle.xuggler.IStreamCoder@...")?
I hope these are not silly questions, and that you can give me some
light, specially, on the H264 issue. :)
Thank you very much!
Pamela.
1-13 // imports...
14
15 public class ScreenRecordingExample {
16
17 private static final double FRAME_RATE = 50;
18
19 private static final int SECONDS_TO_RUN_FOR = 20;
20
21 private static final String outputFilename = "/home/pame/
Desktop/mydesktop1.mp4";
22
23 private static Dimension screenBounds;
24
25
26 public static void main(String[] args) {
27
28 final IMediaWriter writer =
ToolFactory.makeWriter(outputFilename);
29
30 screenBounds = Toolkit.getDefaultToolkit().getScreenSize();
31
32 // We tell it we're going to add one video stream, with id
0,
33 // at position 0, and that it will have a fixed frame rate
of FRAME_RATE.
34 writer.addVideoStream(0, 0, ICodec.ID.CODEC_ID_MPEG4,
35 150, 200);
36
37 long startTime = System.nanoTime();
38
39 for (int index = 0; index < SECONDS_TO_RUN_FOR *
FRAME_RATE; index++) {
40
41
42 BufferedImage screen = getImg(index);
43
44 // convert to the right image type
45 BufferedImage bgrScreen = convertToType(screen,
46 BufferedImage.TYPE_3BYTE_BGR);
47
48 // encode the image to stream #0
49 writer.encodeVideo(0, bgrScreen, System.nanoTime() -
startTime,
50 TimeUnit.NANOSECONDS);
51
52 // sleep for frame rate milliseconds
53 try {
54 Thread.sleep((long) (1000 / FRAME_RATE));
55 }
56 catch (InterruptedException e) {
57 // ignore
58 }
59
60 }
61
62 // tell the writer to close and write the trailer if
needed
63 writer.close();
64
65 }
66
67 public static BufferedImage convertToType(BufferedImage
sourceImage, int targetType) {
68
69 BufferedImage image;
70
71 // if the source image is already the target type, return
the source image
72 if (sourceImage.getType() == targetType) {
73 image = sourceImage;
74 }
75 // otherwise create a new image of the target type and draw
the new image
76 else {
77 image = new BufferedImage(sourceImage.getWidth(),
78 sourceImage.getHeight(), targetType);
79 image.getGraphics().drawImage(sourceImage, 0, 0, null);
80 }
81
82 return image;
83
84 }
85
86 private static BufferedImage getImg(int index) {
87 String name="img"+index+".png";
88 try {
89 BufferedImage image = ImageIO.read( new File( "/home/
pame/Desktop/images/"+name ) );
90 }
91 catch (AWTException e) {
92 e.printStackTrace();
93 return null;
94 }
95
96 }
97
98 }