Weitere aktuelle Java-Titel finden Sie bei dpunkt.
 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index


12.3.2

Videoplayer


Mit dem Videoplayer, der in diesem Abschnitt vorgestellt wird, kann man keine Videoformate darstellen (z. B. mpeg). Das Abspielen von mpeg- oder mov-Dateien kann man jedoch mit dem Java Media Framework bewerkstelligen. Das Java Media Framework ist eine Erweiterung zum JDK, die ebenfalls von Sun spezifiziert wurde. Bei Applets besteht eine andere Möglichkeit zum Abspielen von Videos über die MIME-Types des Browsers mit dem Aufruf von externen Programmen. Diese Vorgehensweise hat jedoch den Nachteil, dass man den Player nicht aus der Java-Anwendung steuern kann (falls der nicht mit einem Plug-in verknüpft wurde).

Dieses Applet zeichnet einfach mehrere Einzelbilder nacheinander in der richtigen Reihenfolge an dieselbe Stelle. Dadurch entsteht beim Betrachter der Eindruck, dass sich das Bild bewegt.

Was bei dieser Methode Probleme bereitet, ist das Laden der Bilder. Um die Animation gleich von Beginn an flüssig zu gestalten, muss gewährleistet sein, dass bei Beginn der Animation alle Bilder geladen sind. Ist dies nicht der Fall, wird das entsprechende Bild beim ersten Aufruf von drawImage() geladen und verzögert so den Zeichenvorgang. Dadurch kann man regelrecht mitverfolgen, wie das Bild gezeichnet wird.

Also sollte das Programm so lange mit dem Abspielen der Animation warten, bis die Bilder vollständig geladen sind. Das kann man z. B. über die Methoden prepareImage() und checkImage() bewerkstelligen, wie es bereits in Abschnitt beschrieben wurde. Eine einfachere Möglichkeit besteht allerdings in der Verwendung der Klasse MediaTracker.

MediaTracker wurde bereits bei den Grafik-Buttons im Abschnitt über Ereignisbehandlung und beim Einbinden von Bildern benutzt.

Dort sollten die Buttons genau die gleiche Größe wie die Bilder besitzen. getHeight() und getWidth() liefern aber erst die richtige Größe, wenn die Bilder geladen sind. Deshalb ist die Überwachung des Ladevorgangs mit MediaTracker erforderlich.

Die Bilder, die der Videoplayer verwendet, müssen alle dieselbe Größe haben. Der Name einer Bilddatei besteht aus einem Basisnamen, der für alle Bilder gleich ist, einer Zahl, welche die Position des Bildes innerhalb eines Animationsablaufs angibt, und der Endung, die das Bildformat angibt. Die Bilder müssen durchgehend nummeriert sein, und die Zahl des ersten Bildes muss 1 sein. Ein Beispiel für eine solche Bildnamen wären:
  bild1.jpg, bild2.jpg, bild3.jpg, ...
Der Basisname der Bilder, die Anzahl der Bilder und das Bildformat werden dem Applet als Parameter übergeben:
  String imagebase = getParameter("ImageBase");
  String imagetype = getParameter("ImageType");
  int number = Integer.parseInt(getParameter("Number"));
Mit dieser Information kann sich das Applet die Namen aller Bilddateien zusammensetzen.

Der nächste Schritt, der vorgenommen werden muss, ist das Laden der Bilder. Hierzu wird die Klasse MediaTracker benutzt:
  Image images[] = new Image[number];
  MediaTracker tracker = new MediaTracker(this);
  for (int i= 0; i<number;i++) {
    images[i] = getImage(getCodeBase(),
                         imagebase+(i+1)+"."+imagetype);
    tracker.addImage(images[i], i);
  }
Der Konstruktor von MediaTracker besitzt einen Parameter vom Typ Component. Mit dem so erzeugten Exemplar tracker kann man den Ladevorgang von Bildern steuern und überwachen. Hierzu muss man aber zuerst festlegen, welche Bilder vom MediaTracker überwacht werden sollen.

Dies macht man mit der Methode addImage(). addImage(Image, int) erhält als Parameter das zu überwachende Bild und eine Zahl vom Typ int. Die Zahl legt die ID fest, der das Bild zugeordnet wird. Die ID ist nicht eindeutig, d. h. einer ID können mehrere Bilder zugeordnet werden. Auf diese Weise kann man Bilder gruppieren. Mit der Methode checkID(int ID) kann der Status der Bilder, die einer ID zugeordnet sind, abgefragt werden. Dies zeigt, dass die Klasse MediaTracker Bilder nur nach ihren IDs verwaltet. Alle Bilder mit derselben ID werden gleich behandelt.

Im obigen Programmauszug wird jedem Bild eine eigene ID zugeordnet. Die Erklärung, warum dies so gemacht wird, folgt später. checkID() liefert einen booleschen Wert, der anzeigt, ob schon alle Bilder der übergebenen ID geladen wurden. checkID() startet den Ladevorgang jedoch nicht.

checkID() gibt es allerdings noch in einer anderen Variante, die es ermöglicht, den Ladevorgang auch zu starten:
public synchronized boolean checkID(int id, boolean load)
Erhält load den Wert true, wird der Ladevorgang gestartet.

Beim Videoplayer soll nun so lange gewartet werden, bis alle Bilder geladen wurden. Hierfür definiert die Klasse MediaTracker die Methode waitForID(int id):
  for (int i=0; i < number; i++) {
    getAppletContext().showStatus(
                  "Loading Image "+(i+1)+" of "+number);
    try {
      tracker.waitForID(i);
    }
    catch (InterruptedException e) {
      System.err.println("Error: "+e);
    }
    // Fehlerüberprüfung
    if (tracker.isErrorID(i)) {
      Object[] errors = tracker.getErrorsID(i);
      for(int n=0;n<errors.length;n++)
        System.err.println(errors[n]);
    }
  }
In diesem Beispiel wird in einer Schleife ein Bild nach dem anderen geladen. Damit der Betrachter des Applets über den aktuellen Stand des Ladevorgangs der Bilder informiert ist, wird in der Statuszeile des Browsers bzw. des Appletviewers die Anzahl der bereits geladenen und die Gesamtzahl der Bilder ausgegeben.

waitForID() lädt alle Bilder, die einer ID zugeordet sind. Dies erklärt auch, warum oben jedem Bild eine eigene ID zugewiesen wurde. Wenn alle Bilder die gleiche ID besitzen würden, dann könnte man durch einen Aufruf von waitForID() zwar das Laden aller Bilder veranlassen, nicht jedoch den Text in der Statuszeile erneuern, da waitForID() den Programmablauf so lange blockiert, bis auch wirklich alle Bilder geladen sind.

Wenn waitForID() von einem anderen Thread unterbrochen wird, löst dies eine InterruptedException aus. Will man die Bilder von allen IDs laden, kann man die Methode waitForAll() der Klasse MediaTracker verwenden.

Fehler beim Laden werden weder von checkID() noch von waitForID() erfasst. Tritt beim Laden ein Fehler auf, so wird der Ladevorgang abgebrochen und das Bild als geladen angesehen, d. h. ein Aufruf von checkID() liefert true. Deshalb sollte man zusätzlich Fehler abfragen. Hierzu kann man eine der Methoden verwenden. Informationen über die aufgetretenen Fehler können mit den Methoden erfragt werden.

Konnte ein Bild nicht geladen werden, weil der Name falsch angegeben wurde, liefern obige Methoden die Exemplare der Klasse Image, bei denen die Fehler aufgetreten sind. Hiermit kann die Fehlerquelle eindeutig bestimmt werden.

Sind die Bilder einmal geladen, müssen sie noch in der richtigen Reihenfolge angezeigt werden. Dies wird fast genauso gemacht, wie bei der Laufschrift im letzten Abschnitt.

Der Videoplayer dieses Beispiels stellt dem Benutzer zusätzlich Kontrollfunktionen zur Verfügung, die es ihm ermöglichen, den Animationsablauf

Hierzu besitzt das Applet ein Panel, das die Steuerelemente enthält, und eine von Canvas abgeleitete Komponente ImageCanvas, in der die Bilder angezeigt werden. Die wichtigste Komponente für die Darstellung der Animation ist das ImageCanvas.

Durch Betätigung der Steuerelemente werden die einzelnen Methoden des ImageCanvas aufgerufen. Dem Konstruktor des ImageCanvas werden die Bilder, aus denen sich die Animation zusammensetzt, übergeben:
  public ImageCanvas(Image images[]) {
    this.images = images;    // Speichern der Bilder
  }

ImageCanvas implementiert das Interface Runnable. Der damit verbundene Thread kann durch zwei Methoden beeinflusst werden:
  public void turnOn() {
    if (t == null) {     // Wenn kein Thread angelegt ist
      t = new Thread(this); // Erzeugen...
      t.start();            // und Starten des Threads
    }
  }

Diese Methoden sind analog zu den Methoden start() und stop(), die bei der Laufschrift verwendet wurden. Sie werden bei Betätigung der Steuerelemente »start« und »stop« aufgerufen und starten bzw. beenden die Animation.

In der run()-Methode ist der Ablauf der Animation festgelegt:
  public void run() {
    Thread me = Thread.currentThread();
    while(t == me) {       // Endlosschleife
      repaint();           // Bild zeichnen
      try {
        t.sleep(delay);    // Verzögerung
      }
      catch (InterruptedException e) {
        System.err.println("Error: "+e);
      }
      currentimage = (currentimage + 1) % images.length;
    }
  }

run() enthält eine Endlosschleife, in der bei jedem Durchlauf die Komponente neu gezeichnet und das Feld currentimage hochgezählt wird. Ist currentimage gleich der maximalen Bilderzahl minus eins, wird currentimage wieder auf null gesetzt. Im Beispiel wird hierfür der modulo-Operator (%) verwendet:
  currentimage = (currentimage + 1) % images.length;
Dadurch enthält currentimage zu jedem Zeitpunkt den Index in dem Array mit den Bildern. Bei jedem Aufruf von repaint() wird das mit currentimage indexierte Bild in die Komponente gemalt:
  public void update(Graphics g) {
    // Zeichnen des aktuellen Bildes
    g.drawImage(images[currentimage], 0, 0, this);
  }

Bei diesem Applet erfolgt das Zeichnen des Bildes in der Methode update(). Ihre Ausführung wird durch einen Aufruf von repaint() ausgelöst. Der Zusammenhang zwischen update(), paint() und repaint() wird im folgenden Abschnitt erläutert.

Abbildung 12.3: Ein Videoplayer in Java
Abbildung 12.3


Material zum Beispiel


 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index

Copyright © 2002 dpunkt.Verlag, Heidelberg. Alle Rechte vorbehalten.