/*
* @(#)Main.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.jmfavidemo;
import com.sun.media.format.AviVideoFormat;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import java.awt.image.IndexColorModel;
import java.io.*;
import java.util.Random;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.DataSink;
import javax.media.EndOfMediaEvent;
import javax.media.Format;
import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.NoDataSinkException;
import javax.media.NoProcessorException;
import javax.media.Processor;
import javax.media.Time;
import javax.media.control.TrackControl;
import javax.media.datasink.DataSinkErrorEvent;
import javax.media.datasink.DataSinkEvent;
import javax.media.datasink.DataSinkListener;
import javax.media.datasink.EndOfStreamEvent;
import javax.media.format.IndexedColorFormat;
import javax.media.format.RGBFormat;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.DataSource;
import javax.media.protocol.FileTypeDescriptor;
import javax.media.protocol.PullBufferDataSource;
import javax.media.protocol.PullBufferStream;
/**
* {@code Main}.
*
* @author Werner Randelshofer
* @version $Id: Main.java 304 2013-01-03 07:45:40Z werner $
*/
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
String folder;
if (args.length > 0) {
folder = args[0];
} else {
folder = System.getProperty("user.home");
}
try {
test(new File(folder, "avidemo-tscc8.avi"), new AviVideoFormat("tscc", null, Format.NOT_SPECIFIED, null, 30f, Format.NOT_SPECIFIED, 8, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, null));
//test(new File(folder, "avidemo-tscc16.avi"), new AviVideoFormat("tscc", null, Format.NOT_SPECIFIED, null, 30f, Format.NOT_SPECIFIED, 16, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, null));
test(new File(folder, "avidemo-tscc24.avi"), new AviVideoFormat("tscc", null, Format.NOT_SPECIFIED, null, 30f, Format.NOT_SPECIFIED, 24, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, null));
} catch (Throwable ex) {
ex.printStackTrace();
}
}
private static void test(File file, AviVideoFormat format) throws IOException, NoProcessorException, NoDataSinkException {
System.out.println("* Writing " + file);
DataSource source = new ImageDataSource(format);
Processor p = Manager.createProcessor(source);
Handler h = new Handler();
p.addControllerListener(h);
p.configure();
if (!h.waitForState(p, Processor.Configured)) {
throw new IOException("Could not configure processor.");
}
p.setContentDescriptor(new ContentDescriptor(FileTypeDescriptor.MSVIDEO));
TrackControl trackControls[] = p.getTrackControls();
javax.media.Format formats[] = trackControls[0].getSupportedFormats();
if (formats == null || formats.length <= 0) {
throw new UnsupportedOperationException("No output formats available.");
}
String encoding = format.getEncoding();
javax.media.Format selectedFormat = null;
for (javax.media.Format f : formats) {
if (f.getEncoding().equals(encoding)) {
selectedFormat = f;
break;
}
}
if (selectedFormat == null) {
throw new UnsupportedOperationException("No output format selected.");
}
trackControls[0].setFormat(selectedFormat);
p.realize();
if (!h.waitForState(p, Processor.Realized)) {
throw new IOException("Could not realize processor.");
}
MediaLocator ml = new MediaLocator(file.toURI().toURL());
DataSink sink = Manager.createDataSink(p.getDataOutput(), ml);
sink.addDataSinkListener(h);
sink.open();
try {
sink.start();
p.start();
if (!h.waitForEndOfMedia()) {
throw new IOException("Processor reported an error.");
}
p.stop();
sink.stop();
/*
if (!h.waitForFileDone()) {
throw new IOException("DataSink reported an error.");
}*/
} finally {
p.close();
sink.close();
}
}
private static class Handler implements ControllerListener, DataSinkListener {
private boolean fileDone, fileSuccess;
private final Object waitSync = new Object();
private boolean endOfMedia;
@Override
public void controllerUpdate(ControllerEvent evt) {
// System.out.println(evt);
if (evt instanceof EndOfMediaEvent) {
endOfMedia = true;
}
synchronized (waitSync) {
waitSync.notifyAll();
}
}
@Override
public void dataSinkUpdate(DataSinkEvent evt) {
// System.out.println(evt);
if (evt instanceof EndOfStreamEvent) {
fileDone = true;
} else if (evt instanceof DataSinkErrorEvent) {
fileDone = true;
fileSuccess = false;
endOfMedia=true;
}
synchronized (waitSync) {
waitSync.notifyAll();
}
}
public boolean waitForState(Processor p, int state) {
synchronized (waitSync) {
try {
while (p.getState() < state) {
waitSync.wait();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return p.getState() == state;
}
public boolean waitForEndOfMedia() {
synchronized (waitSync) {
try {
while (!endOfMedia) {
waitSync.wait();
}
} catch (Exception e) {
}
}
return endOfMedia;
}
public boolean waitForFileDone() {
synchronized (waitSync) {
try {
while (!fileDone) {
waitSync.wait();
}
} catch (Exception e) {
}
}
return fileSuccess;
}
}
private static class ImageStream implements PullBufferStream {
private int index;
private int n;
private Random rnd;
private BufferedImage img;
private IndexColorModel palette;
private Graphics2D g;
private javax.media.format.VideoFormat mediaFormat;
private Format format;
private Object data;
public ImageStream(AviVideoFormat format) {
index = 0;
n = 100;
rnd = new Random(0); // use seed 0 to get reproducable output
this.format = format;
int w = 320;
int h = 160;
int depth = format.getBitsPerPixel();
switch (depth) {
case 24:
default: {
img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
mediaFormat = new RGBFormat(new Dimension(w, h), w * h, int[].class,
30.0f, 24, 0xff0000, 0xff00, 0xff);
data = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();
break;
}
case 16:{
img = new BufferedImage(w, h, BufferedImage.TYPE_USHORT_555_RGB);
mediaFormat = new RGBFormat(new Dimension(w, h), w * h, int[].class,
30.0f, 16, 0xff0000, 0xff00, 0xff);
data = ((DataBufferUShort) img.getRaster().getDataBuffer()).getData();
break;
}
case 8: {
byte[] red = new byte[256];
byte[] green = new byte[256];
byte[] blue = new byte[256];
for (int i = 0; i < 255; i++) {
red[i] = (byte) rnd.nextInt(256);
green[i] = (byte) rnd.nextInt(256);
blue[i] = (byte) rnd.nextInt(256);
}
rnd.setSeed(0); // set back to 0 for reproducable output
palette = new IndexColorModel(8, 256, red, green, blue);
img = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_INDEXED, palette);
data = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
mediaFormat = new IndexedColorFormat(new Dimension(w, h),
w * h, byte[].class,
30.0f, w, 256, red, green, blue);
break;
}
case 4: {
byte[] red = new byte[16];
byte[] green = new byte[16];
byte[] blue = new byte[16];
for (int i = 0; i < 15; i++) {
red[i] = (byte) rnd.nextInt(16);
green[i] = (byte) rnd.nextInt(16);
blue[i] = (byte) rnd.nextInt(16);
}
rnd.setSeed(0); // set back to 0 for reproducable output
IndexColorModel palette = new IndexColorModel(4, 16, red, green, blue);
img = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_INDEXED, palette);
data = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
mediaFormat = new IndexedColorFormat(new Dimension(w, h),
w * h, byte[].class,
30.0f, w, 16, red, green, blue);
break;
}
}
g = img.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setBackground(Color.WHITE);
g.clearRect(0, 0, img.getWidth(), img.getHeight());
}
@Override
public boolean willReadBlock() {
return false;
}
@Override
public void read(javax.media.Buffer buffer) throws IOException {
buffer.setFlags(0);
if (index >= n - 1) {
// mark last frame
buffer.setEOM(true);
}
if (index >= n) {
// handle attempt to read past of stream
buffer.setDiscard(true);
return;
}
g.setColor(new Color(rnd.nextInt()));
g.fillOval(rnd.nextInt(img.getWidth() - 30), rnd.nextInt(img.getHeight() - 30), 30, 30);
buffer.setData(data);
buffer.setOffset(0);
buffer.setLength(img.getWidth() * img.getHeight());
buffer.setSequenceNumber(index);
buffer.setTimeStamp((long) (1e9 * index / mediaFormat.getFrameRate()));
buffer.setDuration((long)(1e9/mediaFormat.getFrameRate()));
index++;
}
@Override
public javax.media.Format getFormat() {
return mediaFormat;
}
@Override
public ContentDescriptor getContentDescriptor() {
return new ContentDescriptor(ContentDescriptor.RAW);
}
@Override
public long getContentLength() {
return 0;
}
@Override
public boolean endOfStream() {
return index >= n;
}
@Override
public Object[] getControls() {
return new Object[0];
}
@Override
public Object getControl(String controlType) {
return null;
}
}
private static class ImageDataSource extends PullBufferDataSource {
private PullBufferStream streams[];
public ImageDataSource(AviVideoFormat format) {
streams = new PullBufferStream[]{new ImageStream(format)};
}
@Override
public PullBufferStream[] getStreams() {
return streams.clone();
}
@Override
public String getContentType() {
return ContentDescriptor.RAW;
}
@Override
public void connect() throws IOException {
// nothing to do
}
@Override
public void disconnect() {
// nothing to do
}
@Override
public void start() throws IOException {
// nothing to do
}
@Override
public void stop() throws IOException {
// nothing to do
}
@Override
public Object[] getControls() {
return new Object[0];
}
@Override
public Object getControl(String controlType) {
return null;
}
@Override
public Time getDuration() {
return DURATION_UNKNOWN;
}
}
}