1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 package com.example.android.leanback; 15 16 import android.net.Uri; 17 import android.os.Bundle; 18 import android.support.v4.media.session.MediaSessionCompat; 19 20 import androidx.leanback.app.VideoFragmentGlueHost; 21 import androidx.leanback.media.MediaPlayerAdapter; 22 import androidx.leanback.media.PlaybackGlue; 23 import androidx.leanback.media.PlaybackTransportControlGlue; 24 import androidx.leanback.widget.PlaybackControlsRow; 25 26 /** 27 * Fragment demonstrating the use of {@link androidx.leanback.app.VideoFragment} to 28 * render video with playback controls. And demonstrates video seeking with thumbnails. 29 * 30 * Generate 1 frame per second thumbnail bitmaps and put on sdcard: 31 * <pre> 32 * sudo apt-get install libav-tools 33 * avconv -i input.mp4 -s 240x135 -vsync 1 -r 1 -an -y -qscale 8 frame_%04d.jpg 34 * adb shell mkdir /sdcard/seek 35 * adb push frame_*.jpg /sdcard/seek/ 36 * </pre> 37 * Change to 1 frame per minute: use "-r 1/60". 38 * For more options, see https://wiki.libav.org/Snippets/avconv 39 * 40 * <p> 41 * Showcase: 42 * </p> 43 * <li>Auto play when ready</li> 44 * <li>Set seek provider</li> 45 * <li>switch MediaSource</li> 46 * <li>switch PlaybackGlue</li> 47 */ 48 public class SampleVideoFragment extends androidx.leanback.app.VideoFragment { 49 50 // Media Session Token 51 private static final String MEDIA_SESSION_COMPAT_TOKEN = "media session support video"; 52 53 private PlaybackTransportControlGlueSample<MediaPlayerAdapter> mMediaPlayerGlue; 54 55 private MediaSessionCompat mMediaSessionCompat; 56 57 final VideoFragmentGlueHost mHost = new VideoFragmentGlueHost(SampleVideoFragment.this); 58 59 static void playWhenReady(PlaybackGlue glue) { 60 if (glue.isPrepared()) { 61 glue.play(); 62 } else { 63 glue.addPlayerCallback(new PlaybackGlue.PlayerCallback() { 64 @Override 65 public void onPreparedStateChanged(PlaybackGlue glue) { 66 if (glue.isPrepared()) { 67 glue.removePlayerCallback(this); 68 glue.play(); 69 } 70 } 71 }); 72 } 73 } 74 75 static void loadSeekData(final PlaybackTransportControlGlue glue) { 76 if (glue.isPrepared()) { 77 glue.setSeekProvider(new PlaybackSeekDiskDataProvider( 78 glue.getDuration(), 79 1000, 80 "/sdcard/seek/frame_%04d.jpg")); 81 } else { 82 glue.addPlayerCallback(new PlaybackGlue.PlayerCallback() { 83 @Override 84 public void onPreparedStateChanged(PlaybackGlue glue) { 85 if (glue.isPrepared()) { 86 glue.removePlayerCallback(this); 87 PlaybackTransportControlGlue transportControlGlue = 88 (PlaybackTransportControlGlue) glue; 89 transportControlGlue.setSeekProvider(new PlaybackSeekDiskDataProvider( 90 transportControlGlue.getDuration(), 91 1000, 92 "/sdcard/seek/frame_%04d.jpg")); 93 } 94 } 95 }); 96 } 97 } 98 99 @Override 100 public void onCreate(Bundle savedInstanceState) { 101 super.onCreate(savedInstanceState); 102 mMediaPlayerGlue = new PlaybackTransportControlGlueSample(getActivity(), 103 new MediaPlayerAdapter(getActivity())); 104 105 // create a media session inside of a fragment, and app developer can determine if connect 106 // this media session to glue or not 107 // as requested in b/64935838 108 mMediaSessionCompat = new MediaSessionCompat(getActivity(), MEDIA_SESSION_COMPAT_TOKEN); 109 mMediaPlayerGlue.connectToMediaSession(mMediaSessionCompat); 110 111 mMediaPlayerGlue.setHost(mHost); 112 mMediaPlayerGlue.setMode(PlaybackControlsRow.RepeatAction.INDEX_NONE); 113 mMediaPlayerGlue.addPlayerCallback(new PlaybackGlue.PlayerCallback() { 114 boolean mSecondCompleted = false; 115 @Override 116 public void onPlayCompleted(PlaybackGlue glue) { 117 if (!mSecondCompleted) { 118 mSecondCompleted = true; 119 mMediaPlayerGlue.setSubtitle("Leanback artist Changed!"); 120 mMediaPlayerGlue.setTitle("Leanback team at work"); 121 String uriPath = "https://storage.googleapis.com/android-tv/Sample videos/" 122 + "April Fool's 2013/Explore Treasure Mode with Google Maps.mp4"; 123 mMediaPlayerGlue.getPlayerAdapter().setDataSource(Uri.parse(uriPath)); 124 loadSeekData(mMediaPlayerGlue); 125 playWhenReady(mMediaPlayerGlue); 126 } else { 127 mMediaPlayerGlue.removePlayerCallback(this); 128 switchAnotherGlue(); 129 } 130 } 131 }); 132 mMediaPlayerGlue.setSubtitle("Leanback artist"); 133 mMediaPlayerGlue.setTitle("Leanback team at work"); 134 String uriPath = "https://storage.googleapis.com/android-tv/Sample videos/" 135 + "April Fool's 2013/Explore Treasure Mode with Google Maps.mp4"; 136 mMediaPlayerGlue.getPlayerAdapter().setDataSource(Uri.parse(uriPath)); 137 loadSeekData(mMediaPlayerGlue); 138 playWhenReady(mMediaPlayerGlue); 139 } 140 141 @Override 142 public void onPause() { 143 if (mMediaPlayerGlue != null) { 144 mMediaPlayerGlue.pause(); 145 } 146 super.onPause(); 147 } 148 149 @Override 150 public void onDestroy() { 151 super.onDestroy(); 152 mMediaPlayerGlue.disconnectToMediaSession(); 153 } 154 155 void switchAnotherGlue() { 156 mMediaPlayerGlue = new PlaybackTransportControlGlueSample(getActivity(), 157 new MediaPlayerAdapter(getActivity())); 158 159 // If the glue is switched, re-register the media session 160 mMediaPlayerGlue.connectToMediaSession(mMediaSessionCompat); 161 162 mMediaPlayerGlue.setMode(PlaybackControlsRow.RepeatAction.INDEX_ONE); 163 mMediaPlayerGlue.setSubtitle("A Googler"); 164 mMediaPlayerGlue.setTitle("Swimming with the fishes"); 165 mMediaPlayerGlue.getPlayerAdapter().setDataSource( 166 Uri.parse("http://techslides.com/demos/sample-videos/small.mp4")); 167 mMediaPlayerGlue.setHost(mHost); 168 loadSeekData(mMediaPlayerGlue); 169 playWhenReady(mMediaPlayerGlue); 170 } 171 } 172