Caprica Software

vlcj 4.x Tutorial

Media Info

Building on previous tutorials, we will now show how to get information about the current media.


Let's Get Started

You should now already have a basic template for how to create a vlcj application, so this tutorial will no longer duplicate all of the code each time - instead we'll just show the new code fragments.

There are different kinds of information available:

  • Meta data, like an ID3 tag for MP3 files, things like Title, Genre and so on;
  • Track descriptions, this might be something like a language name for an audio or sub-title track;
  • Chapter descriptions, for a DVD;
  • Track information, this is more detailed information about a track like the codec used, video width and height, audio sample rate and so on.

Some media information is available before you play media so long as you parse it. Parsing media will make meta data available.

Some media information is not available until some indeterminate time after you play it. Even if you play the media and wait for a playing event, the media information might still not be available.

When dealing with such media information it is usually better to wait for a mediaPlayerReady event rather than a playing event.

In some cases, specifically with regard to track information, if you query for that information at different times you may get progressively different results - i.e. initially you may get basic information which is later refreshed with the gaps filled in.


Parsing Media for Meta Data

Parsing media data makes meta data available, it does not make any track information available.

We first use prepare rather than play to ready the new media without actually playing (starting) it.

mediaPlayerComponent.mediaPlayer().media().prepare(mrl);

After we have prepared media, to parse the current media asynchronously:

mediaPlayerComponent.mediaPlayer().media().parsing().parse();

When the media has finished parsing, or some error occurred, a media event will be raised.

So, before parsing the media we should add an event listener:

mediaPlayerComponent.mediaPlayer().events().addMediaEventListener(new MediaEventAdapter(
@Override
public void mediaParsedChanged(Media media, MediaParsedStatus newStatus) {
}
));

You then check the parsed status and if parsing completed you can access the meta data (or signal your main application thread that it can access the meta data).

Parsing media may trigger an HTTP request to fetch meta data and e.g. perform a search for album art. This might be a privacy issue for your application.

You can specify whether or not this network request is made, or whether instead the search for meta data should only work locally by setting parse flags when you make your parse request.


Track Descriptions

Track descriptions are the most basic information about the video, audio, sub-title, and title descriptions. These descriptions are typically used as labels in menu items in a user interface. The idea is your user selects the menu item to activate the particular track.

MediaPlayer mediaPlayer = mediaPlayerComponent.mediaPlayer();
List<TrackDescription> videoTracks = mediaPlayer.video().trackDescriptions();
List<TrackDescription> audioTracks = mediaPlayer.audio().trackDescriptions();
List<TrackDescription> spuTracks = mediaPlayer.subpictures().trackDescriptions();

You get a unique track identifier and a simple textual description, nothing else.

The track identifiers are unique, but there is no guarantee that says they count from zero or that they increase contiguously. You can not assume anything about the track identifiers.

Querying and selecting tracks is straightforward:

MediaPlayer mediaPlayer = mediaPlayerComponent.mediaPlayer();

int videoTrackCount = mediaPlayer.video().trackCount();
int videoTrack = mediaPlayer.video().track();
mediaPlayer.video().setTrack(newVideoTrack);

int audioTrackCount = mediaPlayer.audio().trackCount();
int audioTrack = mediaPlayer.audio().track();
mediaPlayer.audio().setTrack(newAudioTrack);

int spuTrackCount = mediaPlayer.subpictures().trackCount();
int spuTrack = mediaPlayer.subpictures().track();
mediaPlayer.subpictures().setTrack(newSpuTrack);

There is usually a 'special' track identifier with an id of -1 and a description of "Disabled". You can use this to disable audio, video or sub-titles if you want.


Title Descriptions

Title descriptions, e.g. for DVD media, are slightly simpler - it is a list of strings, in pratical terms this is usually no more than "Title 1", "Title 2" and so on. With each title entry there is also a duration.

Get the descriptions for the current title:

List<TitleDescription> titles = mediaPlayerComponent.mediaPlayer().titles().descriptions();

You can navigate chapters (chapter numbers count from zero):

MediaPlayer mediaPlayer = mediaPlayerComponent.mediaPlayer();
int titleCount = mediaPlayer.titles().count();
int currentTitle = mediaPlayer.titles().title();
mediaPlayer.titles().setTitle(newTitle);

Chapter Descriptions

Chapter descriptions, e.g. for DVD media, are slightly simpler - it is a list of strings, in pratical terms this is usually no more than "Chapter 1", "Chapter 2" and so on. With each chapter entry there is also an offset from the start of the media and a duration.

Get the chapter descriptions for the current title:

List<ChapterDescription> chapters = mediaPlayerComponent.mediaPlayer().chapters().descriptions();

Get the chapter descriptions for all titles (e.g. DVD media might have many 'titles' for the main feature, special features and so on):

List<List<ChapterDescription>> allChapters = mediaPlayerComponent.mediaPlayer().chapters().allDescriptions();

You can navigate chapters (chapter numbers count from zero):

MediaPlayer mediaPlayer = mediaPlayerComponent.mediaPlayer();
int chapterCount = mediaPlayer.chapters().count();
int currentChapter = mediaPlayer.chapters().chapter();
mediaPlayer.chapters().setChapter(newChapter);
mediaPlayer.chapters().next();
mediaPlayer.chapters().previous();

Track Information

Track information contains technical details about the audio, video and sub-title tracks. This is things like the codec, bit-rate, for video tracks it is things like width and height, for audio tracks it is things like sample-rate and so on and so on.

You can query all track information in one call, or you can just query for one or more specific types of track information that you are interested in.

All tracks:

MediaPlayer mediaPlayer = mediaPlayerComponent.mediaPlayer();
List<? extends TrackInfo> trackInfo = mediaPlayer.media().info().tracks();

Specific track types, here we just pick audio and video:

MediaPlayer mediaPlayer = mediaPlayerComponent.mediaPlayer();
List<? extends TrackInfo> trackInfo = mediaPlayer.media().info().tracks(TrackType.AUDIO, TrackType.VIDEO);

Note that TrackType.TEXT is used for sub-title tracks.

The track information forms a sub-class hierarchy for the different types available: base-class TrackInfo and sub-classes AudioTrackInfo, VideoTrackInfo and TextTrackInfo.

When you iterate the returned track information list, if you are interested in the attributes from the sub-classes you must cast the track information reference to the appropriate type.

If you requested a particular type:

MediaPlayer mediaPlayer = mediaPlayerComponent.mediaPlayer();
List<? extends TrackInfo> trackInfo = mediaPlayer.getTrackInfo(TrackType.VIDEO);
for (TrackInfo track : trackInfo) {
VideoTrackInfo videoTrack = (VideoTrackInfo) track;
// ... use videoTrack ...
}

If you requested multiple types:

MediaPlayer mediaPlayer = mediaPlayerComponent.mediaPlayer();
List<? extends TrackInfo> trackInfo = mediaPlayer.getTrackInfo();
for (TrackInfo track : trackInfo) {
if (track instanceof AudioTrackInfo) {
AudioTrackInfo audioTrack = (AudioTrackInfo) track;
// ... use audioTrack ...
}
else if (track instanceof VideoTrackInfo) {
VideoTrackInfo videoTrack = (VideoTrackInfo) track;
// ... use videoTrack ...
}
else if (track instanceof TextTrackInfo) {
TextTrackInfo textTrack = (TextTrackInfo) track;
// ... use textTrack ...
}
}

There are additional methods for each track type that will not require you to use type-casts:

MediaPlayer mediaPlayer = mediaPlayerComponent.mediaPlayer();
List<AudioTrackInfo> audioTrackInfo = mediaPlayer.media().info().audioTracks();
List<VideoTrackInfo> videoTrackInfo = mediaPlayer.media().info().videoTracks();
List<TextTrackInfo> textTrackInfo = mediaPlayer.media().info().textTracks();

Events

Media player events are raised when the various tracks are discovered. You could use this e.g. to dynamically build track menus.

As has been explained in an earlier tutorial, you can add a listener or you can override template methods in the media player component. We will use template methods:

mediaPlayerComponent = new EmbeddedMediaPlayerComponent() {
@Override
public void elementaryStreamAdded(MediaPlayer mediaPlayer, TrackType type, int id) {
}
@Override
public void elementaryStreamDeleted(MediaPlayer mediaPlayer, TrackType type, int id) {
}
@Override
public void elementaryStreamSelected(MediaPlayer mediaPlayer, TrackType type, int id) {
}
};

Media Information Availability

As stated right at the start of this tutorial, the media information is not always immediately available.

If you are building a media player with a user interface, the best strategy really is to query for media information on demand and each time you demand it.

What this means in practice is that if you are building up a menu of tracks then query for the track information just before you are about to show the menu. For this specific case you can add a standard MenuListener to your menu and the menuSelected event will be raised, and this is where you can query the track information.

This same on-demand strategy is a good strategy for all of the media information we've covered in this tutorial.