001: /*
002: * Apollo - Motion capture and animation system
003: * Copyright (c) 2005 Apollo
004: *
005: * This program is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU General Public License
007: * as published by the Free Software Foundation; either version 2
008: * of the License, or (at your option) any later version.
009: *
010: * This program is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013: * GNU General Public License for more details.
014: *
015: * You should have received a copy of the GNU General Public License
016: * along with this program; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
018: *
019: * http://www.gnu.org/copyleft/gpl.html
020: *
021: * @author Giovane.Kuhn - brain@netuno.com.br
022: *
023: */
024: package org.apollo.view;
025:
026: import java.awt.Component;
027: import java.awt.Dimension;
028: import java.awt.event.ActionEvent;
029: import java.awt.event.ActionListener;
030: import java.io.File;
031: import java.io.IOException;
032: import java.net.MalformedURLException;
033: import java.net.URL;
034: import java.util.ArrayList;
035: import java.util.Arrays;
036: import java.util.List;
037: import java.util.Observable;
038: import java.util.Observer;
039:
040: import javax.media.CannotRealizeException;
041: import javax.media.ControllerEvent;
042: import javax.media.ControllerListener;
043: import javax.media.Effect;
044: import javax.media.EndOfMediaEvent;
045: import javax.media.IncompatibleTimeBaseException;
046: import javax.media.Manager;
047: import javax.media.MediaLocator;
048: import javax.media.NoProcessorException;
049: import javax.media.NotConfiguredError;
050: import javax.media.PrefetchCompleteEvent;
051: import javax.media.Processor;
052: import javax.media.ProcessorModel;
053: import javax.media.Time;
054: import javax.media.UnsupportedPlugInException;
055: import javax.media.control.TrackControl;
056: import javax.media.format.VideoFormat;
057: import javax.swing.JButton;
058: import javax.swing.JLabel;
059: import javax.swing.JList;
060: import javax.swing.JScrollPane;
061: import javax.swing.JSplitPane;
062: import javax.swing.JTable;
063: import javax.swing.SwingConstants;
064: import javax.swing.event.ListSelectionEvent;
065: import javax.swing.event.ListSelectionListener;
066: import javax.swing.table.AbstractTableModel;
067:
068: import jmapps.util.StateHelper;
069:
070: import org.apollo.ApolloMediaCore;
071: import org.apollo.ApolloUtil;
072: import org.apollo.MediaUtil;
073: import org.apollo.datamodel.EffectingVideo;
074: import org.apollo.datamodel.Project;
075: import org.apollo.datamodel.Video;
076: import org.apollo.datamodel.VideoEffect;
077: import org.apollo.effect.EffectingVideoCreator;
078: import org.apollo.effect.IVideoEffect;
079: import org.apollo.i18n.ApolloResource;
080:
081: /**
082: * Panel to show effects that will be applyed in a video,
083: * allowing user to configure them.
084: *
085: * @author Giovane.Kuhn on 28/04/2005
086: */
087: public final class EffectPanel extends PropertyPanel implements
088: IVideoHandler, Observer {
089:
090: private static final long serialVersionUID = 1L;
091:
092: /** Original video panel */
093: private final OriginalVideoPanel originalPanel;
094:
095: /** effecting video panel */
096: private final EffectingVideoPanel effectingPanel;
097:
098: /** Effect properties panel */
099: private final EffectPropertiesPanel propertiesPanel;
100:
101: /** Video that was be showed */
102: private Video video;
103:
104: /** List with JMF effect applyed */
105: private List<Effect> codecs;
106:
107: public EffectPanel(Video video) {
108:
109: // panel video
110: PropertyPanel videoPanel = new PropertyPanel();
111: videoPanel.addNewLine(originalPanel = new OriginalVideoPanel(),
112: BOTH_RESIZABLE);
113: JButton item;
114: videoPanel.add(item = new JButton(ApolloResource
115: .getText("panel.effect.button.create")),
116: REMAINDER_POSITION, 1, NONE_RESIZABLE);
117: item.addActionListener(new ActionListener() {
118:
119: public void actionPerformed(ActionEvent e) {
120: create();
121: }
122:
123: });
124: videoPanel.addNewLine(
125: effectingPanel = new EffectingVideoPanel(),
126: BOTH_RESIZABLE);
127:
128: // split effect
129: JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
130: split
131: .setRightComponent(propertiesPanel = new EffectPropertiesPanel());
132: split.setLeftComponent(videoPanel);
133: split.setOneTouchExpandable(true);
134: this .add(split, BOTH_RESIZABLE);
135:
136: // configure for this video
137: changeVideo(video);
138: }
139:
140: /**
141: * Create the effecting video
142: */
143: public boolean create() {
144: if (video == null || video.getEffectingVideo() == null) {
145: return false;
146: }
147:
148: // wait sinker finish
149: String projectPath = ApolloUtil.getPathName(video.getProject()
150: .getProjectFile());
151: String output = projectPath
152: + File.separatorChar
153: + ApolloUtil.getFileName(video.getRelativeVideoFile(),
154: false) + "_"
155: + video.getProject().getProjectName() + ".avi";
156: EffectingVideoCreator ds;
157: try {
158: ds = new EffectingVideoCreator(video, output);
159: ds.start();
160: } catch (MalformedURLException e) {
161: throw new RuntimeException(e);
162: }
163:
164: // update effecting video
165: EffectingVideo ev = video.getEffectingVideo();
166: ev.setRelativeVideoFile(ApolloUtil.getRelativeFileName(
167: projectPath, output));
168: video.setTrackingVideo(null);
169: ApolloFrame.getInstance().save(ev.getProject());
170: return true;
171:
172: }
173:
174: public synchronized void close() {
175: // stop and close player
176: changeVideo(null);
177: }
178:
179: public synchronized void refresh() {
180: // TODO
181: }
182:
183: public synchronized void changeVideo(Video v) {
184: if (v == video) {
185: // video hasn't been changed
186: return;
187: }
188: this .video = v;
189: if (v == null) {
190: // don't show any video
191: propertiesPanel.setVideo(null);
192: originalPanel.setPlayer(null);
193: effectingPanel.setPlayer(null);
194: return;
195: }
196: v.getProject().deleteObserver(this );
197: propertiesPanel.setVideo(v);
198: v.getProject().addObserver(this );
199: initPlayers();
200: }
201:
202: private synchronized void initPlayers() {
203: try {
204: Manager.setHint(Manager.LIGHTWEIGHT_RENDERER, new Boolean(
205: true));
206: MediaLocator locator = new MediaLocator(new URL("file",
207: null, video.getCanonicalVideoFile()));
208: Processor originalPlayer = Manager
209: .createRealizedProcessor(new ProcessorModel(
210: locator, ApolloMediaCore.SUPPORTED_FORMATS,
211: null));
212: originalPanel.setPlayer(originalPlayer);
213: Processor effectingPlayer = Manager
214: .createProcessor(locator);
215: effectingPanel.setPlayer(effectingPlayer);
216:
217: // configure processor with effects
218: StateHelper effectHelper = new StateHelper(effectingPlayer);
219: if (!effectHelper.configure()) {
220: throw new RuntimeException(
221: "Failed to configure the effecting player");
222: }
223:
224: // can be uses as a player.
225: effectingPlayer.setContentDescriptor(null);
226:
227: // search the track control for the video track.
228: TrackControl videoTrack = MediaUtil
229: .getVideoTrack(effectingPlayer);
230: if (videoTrack == null) {
231: throw new RuntimeException(
232: "The input media does not contain a video track.");
233: }
234:
235: // obtain video info
236: assert videoTrack.getFormat() instanceof VideoFormat;
237: VideoFormat format = (VideoFormat) videoTrack.getFormat();
238: video.setSize(format.getSize());
239: video.setFrameRate(format.getFrameRate());
240:
241: // configure effects
242: codecs = new ArrayList<Effect>();
243: for (VideoEffect effect : video.getEffectingVideo()
244: .getEffects()) {
245: try {
246: // set instance properties
247: Effect e = (Effect) ApolloUtil.newInstance(effect
248: .getEffectClass());
249: MediaUtil.setEffectProperties(effect, e);
250: codecs.add(e);
251: } catch (ClassNotFoundException e) {
252: // nop
253: } catch (InstantiationException e) {
254: throw new RuntimeException(e);
255: } catch (IllegalAccessException e) {
256: throw new RuntimeException(e);
257: } catch (SecurityException e) {
258: throw new RuntimeException(e);
259: } catch (IllegalArgumentException e) {
260: throw new RuntimeException(e);
261: }
262: }
263: videoTrack.setCodecChain(codecs.toArray(new Effect[codecs
264: .size()]));
265:
266: try {
267: // realize effecting player to be a slave
268: if (!effectHelper.realize()) {
269: throw new RuntimeException(
270: "Failed to realize the effecting player");
271: }
272: originalPlayer.addController(effectingPlayer);
273: } catch (IncompatibleTimeBaseException e) {
274: // nop
275: }
276: StateHelper origHelper = new StateHelper(originalPlayer);
277: if (!origHelper.prefetch(30000)) {
278: throw new RuntimeException(
279: "Failed to prefetch the original player");
280: }
281: } catch (NoProcessorException e) {
282: throw new RuntimeException(e);
283: } catch (IOException e) {
284: throw new RuntimeException(e);
285: } catch (CannotRealizeException e) {
286: throw new RuntimeException(e);
287: } catch (NotConfiguredError e) {
288: throw new RuntimeException(e);
289: } catch (UnsupportedPlugInException e) {
290: throw new RuntimeException(e);
291: }
292:
293: }
294:
295: public void update(Observable o, Object arg) {
296: if (o instanceof Project) {
297: if (arg == Video.class) {
298: Video old = video;
299: changeVideo(null);
300: changeVideo(old);
301: return;
302: }
303: if (arg == EffectingVideo.class) {
304: // effecting change, restart player
305: originalPanel.setPlayer(null);
306: effectingPanel.setPlayer(null);
307: initPlayers();
308: propertiesPanel.refreshList();
309: propertiesPanel.refreshTable();
310: return;
311: }
312: if (arg == VideoEffect.class) {
313: // effect changed, set jmf effect property
314: assert video.getEffectingVideo().getEffects().size() == codecs
315: .size();
316: int i = 0;
317: for (VideoEffect effect : video.getEffectingVideo()
318: .getEffects()) {
319: MediaUtil.setEffectProperties(effect, codecs
320: .get(i++));
321: }
322: return;
323: }
324: }
325: }
326:
327: public Object getSelectedObject() {
328: return null;
329: }
330:
331: /** Panel to maintain video effects and their properties */
332: private static final class EffectPropertiesPanel extends
333: PropertyPanel {
334:
335: private static final long serialVersionUID = 1L;
336:
337: /** Video to be effecting */
338: private Video video;
339:
340: /** Model for properties table */
341: protected PropertyTableModel tableModel;
342:
343: /** Propreties table */
344: protected JTable table;
345:
346: /** Effects list */
347: protected JList list;
348:
349: public EffectPropertiesPanel() {
350:
351: JLabel label = new JLabel(ApolloResource
352: .getText("panel.effect.property.label.effect"));
353: label.setHorizontalTextPosition(SwingConstants.CENTER);
354: this .add(label, REMAINDER_POSITION, 1);
355:
356: // buttons to manipulate effects
357: JButton item;
358: this .add(item = new JButton(ApolloResource
359: .getText("panel.effect.property.button.reset")), 1,
360: 1, WIDTH_RESIZABLE);
361: item.addActionListener(new ActionListener() {
362:
363: public void actionPerformed(ActionEvent e) {
364: reset();
365: }
366: });
367: this .add(item = new JButton(ApolloResource
368: .getText("panel.effect.property.button.remove")),
369: 1, 1, WIDTH_RESIZABLE);
370: item.addActionListener(new ActionListener() {
371:
372: public void actionPerformed(ActionEvent e) {
373: remove();
374: }
375:
376: });
377: this .add(item = new JButton(ApolloResource
378: .getText("panel.effect.property.button.up")), 1, 1,
379: WIDTH_RESIZABLE);
380: item.addActionListener(new ActionListener() {
381:
382: public void actionPerformed(ActionEvent e) {
383: up();
384: }
385:
386: });
387: this .add(item = new JButton(ApolloResource
388: .getText("panel.effect.property.button.down")),
389: REMAINDER_POSITION, 1, WIDTH_RESIZABLE);
390: item.addActionListener(new ActionListener() {
391:
392: public void actionPerformed(ActionEvent e) {
393: down();
394: }
395:
396: });
397:
398: // effect list
399: this .addNewLine(new JScrollPane(list = new JList()),
400: BOTH_RESIZABLE);
401: this .add("", REMAINDER_POSITION, 1, WIDTH_RESIZABLE);
402:
403: // effect properties table
404: label = new JLabel(ApolloResource
405: .getText("panel.effect.property.label.property"));
406: label.setHorizontalTextPosition(SwingConstants.CENTER);
407: this .add(label, REMAINDER_POSITION, 1);
408: this .addNewLine(new JScrollPane(table = new JTable(
409: tableModel = new PropertyTableModel())),
410: BOTH_RESIZABLE);
411: list.addListSelectionListener(new ListSelectionListener() {
412:
413: public void valueChanged(ListSelectionEvent e) {
414: refreshTable();
415: }
416:
417: });
418: }
419:
420: public boolean remove() {
421: Object[] selecteds = list.getSelectedValues();
422: if (video == null || selecteds == null
423: || list.getModel().getSize() == 1) {
424: return false;
425: }
426: VideoEffect[] effects = null;
427: if (selecteds.length == list.getModel().getSize()) {
428: effects = new VideoEffect[selecteds.length - 1];
429: } else {
430: effects = new VideoEffect[selecteds.length];
431: }
432: System.arraycopy(selecteds, 0, effects, 0, effects.length);
433: boolean ret = video.getEffectingVideo().removeEffects(
434: Arrays.asList(effects));
435: ApolloFrame.getInstance().save(video.getProject());
436: return ret;
437: }
438:
439: public boolean reset() {
440: if (video == null || video.getEffectingVideo() == null) {
441: return false;
442: }
443: video.getEffectingVideo().replaceAllEffects(
444: getRegisteredEffects());
445: ApolloFrame.getInstance().save(video.getProject());
446: return true;
447: }
448:
449: public boolean up() {
450: VideoEffect effect = (VideoEffect) list.getSelectedValue();
451: if (video == null || effect == null
452: || list.getModel().getSize() == 1) {
453: return false;
454: }
455: boolean ret = video.getEffectingVideo().changeEffectOrder(
456: effect, -1);
457: ApolloFrame.getInstance().save(video.getProject());
458: return ret;
459: }
460:
461: public boolean down() {
462: VideoEffect effect = (VideoEffect) list.getSelectedValue();
463: if (video == null || effect == null
464: || list.getModel().getSize() == 1) {
465: return false;
466: }
467: boolean ret = video.getEffectingVideo().changeEffectOrder(
468: effect, 1);
469: ApolloFrame.getInstance().save(video.getProject());
470: return ret;
471: }
472:
473: public void setVideo(Video video) {
474: this .video = video;
475: if (video != null && video.getEffectingVideo() == null) {
476: // create new effecting video
477: EffectingVideo ev = new EffectingVideo();
478: ev.replaceAllEffects(getRegisteredEffects());
479: video.setEffectingVideo(ev);
480: ApolloFrame.getInstance().save(video.getProject());
481: }
482: refreshList();
483: refreshTable();
484: }
485:
486: public List<VideoEffect> getRegisteredEffects() {
487: List<VideoEffect> ret = new ArrayList<VideoEffect>();
488: for (Class clazz : IVideoEffect.REGISTERED) {
489: // create effect for each class
490: VideoEffect effect = new VideoEffect();
491: effect.setEffectClass(clazz.getName());
492: IVideoEffect obj;
493: try {
494: obj = (IVideoEffect) clazz.newInstance();
495: } catch (InstantiationException e) {
496: throw new RuntimeException(e);
497: } catch (IllegalAccessException e) {
498: throw new RuntimeException(e);
499: }
500: // create all properties
501: MediaUtil.getEffectProperties(effect, obj);
502: ret.add(effect);
503: }
504: return ret;
505: }
506:
507: public void refreshList() {
508: if (video == null) {
509: list.setListData(new VideoEffect[0]);
510: } else {
511: list.setListData(video.getEffectingVideo().getEffects()
512: .toArray());
513: }
514: }
515:
516: public void refreshTable() {
517: tableModel.setValues((VideoEffect) list.getSelectedValue());
518: }
519: }
520:
521: /** Data model for properties table */
522: private final static class PropertyTableModel extends
523: AbstractTableModel {
524:
525: private static final long serialVersionUID = 1L;
526:
527: /** Participants columns */
528: private static final String[] COLUMNS = {
529: ApolloResource.getText("table.effect.property.name"),
530: ApolloResource.getText("table.effect.property.value") };
531:
532: /** Column types */
533: private static final Class[] TYPES = { String.class,
534: Object.class };
535:
536: /** Video Effect */
537: private VideoEffect effect;
538:
539: /** Property names */
540: private String[] names;
541:
542: public void setValues(VideoEffect effect) {
543: this .effect = effect;
544: if (effect == null) {
545: this .names = null;
546: } else {
547: this .names = effect.getProperties().keySet().toArray(
548: new String[0]);
549: }
550: fireTableStructureChanged();
551: }
552:
553: public int getColumnCount() {
554: return COLUMNS.length;
555: }
556:
557: public int getRowCount() {
558: if (effect == null) {
559: return 0;
560: }
561: return effect.getProperties().size();
562: }
563:
564: public Object getValueAt(int rowIndex, int columnIndex) {
565: if (effect == null) {
566: return null;
567: }
568: String key = names[rowIndex];
569: if (columnIndex == 0) {
570: return key;
571: }
572: return effect.getProperties().get(key);
573: }
574:
575: public void setValueAt(Object aValue, int rowIndex,
576: int columnIndex) {
577: if (columnIndex == 0) {
578: return;
579: }
580: String key = names[rowIndex];
581: effect.setProperty(key, aValue);
582: ApolloFrame.getInstance().save(effect.getProject());
583: }
584:
585: public String getColumnName(int column) {
586: return COLUMNS[column];
587: }
588:
589: public Class<?> getColumnClass(int columnIndex) {
590: return TYPES[columnIndex];
591: }
592:
593: public boolean isCellEditable(int rowIndex, int columnIndex) {
594: return columnIndex > 0;
595: }
596:
597: }
598:
599: /** Panel to show the original video */
600: private static class OriginalVideoPanel extends PropertyPanel
601: implements ControllerListener {
602:
603: private static final long serialVersionUID = 1L;
604:
605: /** Visual component of the video */
606: protected Component visual;
607:
608: /** Control component of the video */
609: protected Component control;
610:
611: /** Video player */
612: protected Processor player;
613:
614: protected void createFixedComponents() {
615: this .add(ApolloResource
616: .getText("panel.effect.original.label.original"),
617: REMAINDER_POSITION, 1, WIDTH_RESIZABLE);
618: }
619:
620: private void init() {
621: this .visual = this .control = null;
622: this .removeAll();
623: createFixedComponents();
624: }
625:
626: public void setPlayer(Processor p) {
627: this .init();
628: // deallocate actual players
629: if (player != null) {
630: player.removeControllerListener(this );
631: player.close();
632: player = null;
633: ApolloUtil.gc();
634: }
635: player = p;
636: if (player != null) {
637: player.addControllerListener(this );
638: }
639: }
640:
641: public void controllerUpdate(ControllerEvent ce) {
642: if (ce instanceof PrefetchCompleteEvent) {
643: if (visual != null) {
644: return;
645: }
646: visual = player.getVisualComponent();
647: if (visual != null) {
648: visual.setSize(200, 200);
649: this .add(visual, REMAINDER_POSITION, 1,
650: BOTH_RESIZABLE);
651: }
652: control = player.getControlPanelComponent();
653: if (control != null) {
654: control.setSize(200, 10);
655: this .add(control, REMAINDER_POSITION, 1,
656: BOTH_RESIZABLE);
657: }
658: validate();
659: } else if (ce instanceof EndOfMediaEvent) {
660: player.setMediaTime(new Time(0));
661: player.start();
662: }
663: }
664:
665: public Dimension getPreferredSize() {
666: return new Dimension(200, 400);
667: }
668: }
669:
670: /** Panel to show the manipulated video */
671: private static final class EffectingVideoPanel extends
672: OriginalVideoPanel {
673:
674: private static final long serialVersionUID = 1L;
675:
676: protected void createFixedComponents() {
677: this .add(ApolloResource
678: .getText("panel.effect.effecting.label.effecting"),
679: REMAINDER_POSITION, 1, WIDTH_RESIZABLE);
680: }
681:
682: public void controllerUpdate(ControllerEvent ce) {
683: if (ce instanceof PrefetchCompleteEvent) {
684: if (visual != null) {
685: return;
686: }
687: visual = player.getVisualComponent();
688: if (visual != null) {
689: visual.setSize(200, 200);
690: this .add(visual, REMAINDER_POSITION, 1,
691: BOTH_RESIZABLE);
692: }
693: //control = player.getControlPanelComponent();
694: if (control != null) {
695: control.setSize(200, 10);
696: this .add(control, REMAINDER_POSITION, 1,
697: BOTH_RESIZABLE);
698: }
699: validate();
700: }
701: }
702:
703: }
704:
705: }
|