JavaFX多媒體(3)Metadata與Marker by 黃嘉輝 | CodeData
top

JavaFX多媒體(3)Metadata與Marker

分享:

JavaFX多媒體(2)使用Media播放音訊與影像 << 前情

接著上一篇JavaFX多媒體(2)介紹基本的JavaFX Media概念,本篇將說明多媒體的Metadata與Marker。

Metadata原意為Data about Data,中文直譯為有關資料的資料或描述資料的資料,國內學者對於Metadata有不同的譯名,常見的有元資料、詮釋資料、超資料等,中央研究院數位典藏與數位學習國家型科技計畫則譯為後設資料,特此說明。請參考以下網址之說明:Metadata

在多媒體中,Metadata是指內嵌於音訊與影像檔案中的標籤 (Tag) 資料,常見的標籤與JavaFX所支援的媒體格式如下所示。

音訊類型,適用於MP3媒體格式:

  • album artist:專輯演出者。
  • album:專輯名稱。
  • artist:演出者。
  • composer:作曲者。
  • comment-N:專輯第N首音軌的註解。
  • disc count:專輯光碟數目。
  • disc number:專輯光碟編號。
  • duration:音軌時間長度。
  • genre:音軌類型如流行樂、搖滾樂等。
  • image:專輯封面。
  • title:音軌名稱。
  • track count:音軌總數。
  • track number:音軌編號。
  • year:專輯年份。

影像類型,適用於FLV與FXM媒體格式:

  • audio codec:音訊編碼器。
  • creationdate:媒體建立日期。
  • duration:音訊時間長度。
  • framerate:影像畫格 (Frame) 速率。
  • height:影像高度。
  • width:影像寬度。
  • video codec:影像編碼器。

欲處理Metadata,可使用Media類別的getMetadata()方法取得。由於媒體常會改變,特別是專輯中各音軌的Metadata資料均不同,因此在實作上,會將上述以getMetadata()方法所取得的Metadata資料,再以addListener()方法處理其改變事件,一旦資料改變時,再重新取得相關Metadata資料。

此外,須注意的是JavaFX目前支援取得Metadata資料的媒體格式分別為MP3音訊、FLV影像與FXM影像等,其餘尚未支援。

請參考以下範例,首先以Media類別的getMetadata()方法取得音訊檔案中的Metadata資料,接著以addListener()方法處理其改變事件,在事件中分別判斷各Metadata標籤資料是否改變,藉此更新其內容:

media.getMetadata().addListener(
  new MapChangeListener<String, Object>() {
  @Override public void onChanged(
    MapChangeListener.Change<? extends String, 
    ? extends Object> change) {

    if (change.wasAdded()) {
      String key = change.getKey();
      Object value = change.getValueAdded();

      if (key.equals("album")) {
        lblAlbum.setText("Album: " + value.toString());
      } 
      else if (key.equals("artist")) {
        lblArtist.setText("Artist: " + value.toString());
      } 
      else if (key.equals("composer")) {
        lblComposer.setText("Composer: " + value.toString());
      }
      else if (key.equals("duration")) {
        lblDurations.setText("Duration: " + value.toString());
      } 
      else if (key.equals("genre")) {
        lblGenre.setText("Genre: " + value.toString());
      }
      else if (key.equals("title")) {
        lblTitle.setText("Title: " + value.toString());
      } 
      else if (key.equals("year")) {
        lblYear.setText("Year: " + value.toString());
      } 
      else if (key.equals("image")) {
        imgCover.setImage((Image)value);
      }
    }
  }
});

此外,範例建立一分頁窗格顯示音訊檔案的Metadata資料:

private BorderPane createMetadataPane() {
  lblAlbum = new Label();
  lblAlbum.setText("N/A");
  lblArtist = new Label();
  lblArtist.setText("N/A");
  lblComposer = new Label();
  lblComposer.setText("N/A");
  lblDurations = new Label();
  lblDurations.setText("N/A");
  lblGenre = new Label();
  lblGenre.setText("N/A");
  lblTitle = new Label();
  lblTitle.setText("N/A");
  lblYear = new Label();
  lblYear.setText("N/A");
  imgCover = new ImageView();
  imgCover.setImage(new Image(
    getClass().getResourceAsStream("images/album.png")));

  Reflection reflection = new Reflection();
  reflection.setFraction(0.6);
  imgCover.setFitWidth(150);
  imgCover.setPreserveRatio(true);
  imgCover.setSmooth(true);
  imgCover.setEffect(reflection);

  GridPane gridpane = new GridPane();
  gridpane.setPadding(new Insets(10));
  gridpane.setHgap(20);
  gridpane.add(imgCover, 0, 0, 1, GridPane.REMAINING);
  gridpane.add(lblTitle, 1, 0);
  gridpane.add(lblAlbum, 1, 1);
  gridpane.add(lblArtist, 1, 2);
  gridpane.add(lblComposer, 1, 3);
  gridpane.add(lblGenre, 1, 4);
  gridpane.add(lblYear, 1, 5);
  gridpane.add(lblDurations, 1, 6);

  ColumnConstraints column1 = new ColumnConstraints();
  ColumnConstraints column2 = new ColumnConstraints();
  column2.setHgrow(Priority.ALWAYS);
  gridpane.getColumnConstraints().addAll(column1, column2);

  RowConstraints row1 = new RowConstraints();
  row1.setValignment(VPos.TOP);
  gridpane.getRowConstraints().addAll(
    row1, row1, row1, row1, row1, row1, row1);

  BorderPane borderpane = new BorderPane();
  borderpane.setLeft(gridpane);

  return borderpane;
}

【執行結果】

18-7

請參考以下範例處理影像媒體的Metadata資料,程式與上述範例幾乎一樣,只是兩者所處理的Metadata標籤資料不同而已。此外,影像媒體並沒有image的Metadata標籤,但為與上述範例一致,因此亦加入圖像。須注意的是JavaFX僅支援FLV與FXM影像。

【執行結果】

18-8

接著說明Marker。

標記 (Marker) 是針對媒體額外附加文字說明,類似於字幕,雖然音訊與影像均可以加入標記,但由於音訊並無影像,因此在實作上僅針對影像處理。

欲處理標記,可使用Media類別的getMarkers()方法,getMarkers()方法回傳ObservableMap介面物件,其內容由字串與時間所組成,代表在特定「時間」處理指定的「標記字串」。其內容可由ObservableMap介面的put()方法依序加入。待標記處理完成之後,再以MediaPlayer類別的setOnMarker()方法處理。

請參考以下範例,示範在影像中附加標記字串。首先以Media類別的getMarkers()方法回傳ObservableMap介面物件,並以put()方法依序加入標記字串與時間:

final ObservableMap<String, Duration> markers = media.getMarkers();
markers.put("Marker 1", Duration.millis(3000));
markers.put("Marker 2", Duration.millis(6000));
markers.put("Marker 3", Duration.millis(9000));
markers.put("Marker 4", Duration.millis(12000));
markers.put("Marker 5", Duration.millis(15000));

接著以MediaPlayer類別的setOnMarker()方法處理標記字串:

final Label lblMarker = new Label();
lblMarker.setFont(Font.font("Verdana", 12));
lblMarker.setTextFill(Color.WHITE);
StackPane.setAlignment(lblMarker, Pos.CENTER);

mediaplayer.setOnMarker(new EventHandler() {
  @Override public void handle(final MediaMarkerEvent e) {
    Platform.runLater(new Runnable() {
      @Override public void run() {

        lblMarker.setText(e.getMarker().getKey());
      }
    });
  }
});
...

StackPane stackpane = new StackPane();
stackpane.getChildren().addAll(borderpane, lblMarker);
// Set the Layout Pane of Scene
Scene scene = new Scene(stackpane);

【執行結果】

18-9

【參考資料】

[1] Java Official Web Site
[2] JavaFX
[3] NetBeans
[4] JavaFX 2.2 API Specification.
[5] Java Platform, Standard Edition 7 API Specification.

 

分享:
按讚!加入 CodeData Facebook 粉絲群

留言

留言請先。還沒帳號註冊也可以使用FacebookGoogle+登錄留言

關於作者

黃嘉輝副教授,目前任職於國立臺北商業大學企業管理學系,喜歡寫程式,特別愛Java,範例可參考教學網站

熱門論壇文章

熱門技術文章