Thumbnail previews

The SDK can display thumbnails to show the user a preview of where in the stream a seek using the scrubber bar will take them.

Prerequisites

The sample code for this feature is in the preview-thumbnails folder in the example code zip file that is included in the SDK download.

You also need a test stream with an accompanying set of image file(s) containing a mosaic of thumbnails and the associated WebVTT file describing the timing and placement of the thumbnails within the mosaic.

This procedure assumes that you have an existing Player the Android SDK for which you want to enable thumbnail based preview on seeking.

Procedure

Design/layout

The layout of your application needs to accommodate the placement of a thumbnail image in the desired location. For example an ImageView whose horizontal position can be adjusted to reflect the destination of the potential seek position:

<ImageView
android:id="@+id/thumbnail_preview"
android:layout_width="200dp"
android:layout_height="130dp"
android:layout_marginBottom="10dp"
android:adjustViewBounds="true"
android:background="@android:color/black"
android:contentDescription="preview"
android:scaleType="fitCenter"
android:visibility="invisible"/>

Preparation

The API footprint for this feature is very simple, the application simply needs to implement the ThumbnailsListener interface and instantiate the AssetThumbnails class.

For example:

private AssetThumbnails mThumbnailHandler = new AssetThumbnails();

private ThumbnailsListener mThumbnailListener = new ThumbnailsListener() {
  @Override
  public void prepared() {
    mThumbnailMap = mThumbnailHandler.thumbnails();
    // Calling this method absolves the SDK of responsibility for the thumbnail bitmaps,
    // any further calls to .thumbnails() without calling .prepareThumbs() will return null.
    mProgress.setOnSeekBarChangeListener(mThumbUpdater);
  }

  @Override
  public void failed(ThumbnailError thumbnailError, String s) {
    mThumbnailMap = null;
  }
};

Then it is simply a case of calling the AssetThumbnails class prepareThumbnails() method supplying the URL of the WebVTT file and a reference to the instance of the listener, for example:

mThumbnailHandler.prepareThumbnails(url, mThumbnailListener);

Determining the thumbnail to paint

The prepared map of thumbnails stored above can be queried using the following example method which returns the relevant Drawable for the provided progress in milliseconds.

private Drawable getThumb(long progress) {
  ColorDrawable placeholder = new ColorDrawable(Color.BLACK);
  if (mThumbnailMap == null) {
    return placeholder;
  }

  Long[] startTimes = mThumbnailMap.first;
  for(long key : startTimes){
    if(key >= progress) {
      return new BitmapDrawable(this.getResources(), mThumbnailMap.second.get(key));
    }
  }

  return placeholder;
}

Event handling (handheld)

In order to react to touch related seeking events, an OnSeekBarChangeListener needs to be declared with overrides for the onStartTrackingTouch(), onProgressChanged() and onStopTrackingTouch() methods to react to the seeking start, changes and seeking stop respectively. These methods allow your application to be able to hide, show and update the position of the thumbnails as appropriate.

private OnSeekBarChangeListener mThumbUpdater = new OnSeekBarChangeListener() {
  @Override
  public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
    mSeekedTime = progress;
    if (!fromuser || !mThumbnailsVisible) {
      // We're not interested in programmatically generated changes to
      // the progress bar's position.
      return;
    }

    int duration = mPlayer.getDuration();

    if (duration > 0) {
      int width = bar.getWidth() - bar.getPaddingLeft() - bar.getPaddingRight();
      int thumbCenter =  (width * bar.getProgress()) / bar.getMax() + bar.getThumb().getBounds().width()/2;

      mThumbnailPreview.setX(thumbCenter);
      mThumbnailPreview.setImageDrawable(getThumb(mSeekedTime));
      mThumbnailPreview.setVisibility(VISIBLE);
    }
  }

  @Override
  public void onStartTrackingTouch(SeekBar bar) {
    setSeeking(true);
  }

  @Override
  public void onStopTrackingTouch(SeekBar bar) {
    if (mSeeking) {
      mPlayer.seekTo(mSeekedTime);
    }
    hideThumbnail();
    setSeeking(false);
  }
};

Teardown

The AssetThumbnails class has a reset() method which must be called on destruction of the view.

@Override
public void onDestroyView() {
  mThumbnailHandler.reset();
}

Limitations

It is suggested there should be no more than 300 Standard Definition thumbnails for any asset.

See also

To understand more about the thumbnail mosaic structure and how it is described in WebVTT visit https://support.jwplayer.com/articles/how-to-add-preview-thumbnails