JOGL v2.6.0-rc-20250706
JOGL, High-Performance Graphics Binding for Java™ (public API).
MediaPlayer.java
Go to the documentation of this file.
1/**
2 * Copyright 2010-2024 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28package com.jogamp.graph.ui.widgets;
29
30import java.util.Arrays;
31import java.util.List;
32import java.util.Locale;
33import java.util.concurrent.atomic.AtomicReference;
34
35import com.jogamp.common.av.PTS;
36import com.jogamp.common.net.Uri;
37import com.jogamp.common.os.Clock;
38import com.jogamp.common.util.InterruptSource;
39import com.jogamp.graph.curve.Region;
40import com.jogamp.graph.curve.opengl.GLRegion;
41import com.jogamp.graph.font.Font;
42import com.jogamp.graph.font.FontFactory;
43import com.jogamp.graph.ui.Group;
44import com.jogamp.graph.ui.Scene;
45import com.jogamp.graph.ui.Shape;
46import com.jogamp.graph.ui.TooltipText;
47import com.jogamp.graph.ui.layout.Alignment;
48import com.jogamp.graph.ui.layout.BoxLayout;
49import com.jogamp.graph.ui.layout.Gap;
50import com.jogamp.graph.ui.layout.GridLayout;
51import com.jogamp.graph.ui.layout.Padding;
52import com.jogamp.graph.ui.shapes.Button;
53import com.jogamp.graph.ui.shapes.HUDShape;
54import com.jogamp.graph.ui.shapes.Label;
55import com.jogamp.graph.ui.shapes.MediaButton;
56import com.jogamp.graph.ui.shapes.Rectangle;
57import com.jogamp.math.FloatUtil;
58import com.jogamp.math.Vec2f;
59import com.jogamp.math.Vec3f;
60import com.jogamp.math.Vec4f;
61import com.jogamp.math.geom.AABBox;
62import com.jogamp.newt.event.MouseEvent;
63import com.jogamp.opengl.GL;
64import com.jogamp.opengl.GL2ES2;
65import com.jogamp.opengl.GLAnimatorControl;
66import com.jogamp.opengl.GLAutoDrawable;
67import com.jogamp.opengl.GLEventAdapter;
68import com.jogamp.opengl.GLProfile;
69import com.jogamp.opengl.util.av.GLMediaPlayer;
70import com.jogamp.opengl.util.av.GLMediaPlayerFactory;
71import com.jogamp.opengl.util.av.GLMediaPlayer.EventMask;
72import com.jogamp.opengl.util.av.GLMediaPlayer.StreamException;
73import com.jogamp.opengl.util.texture.TextureSequence;
74
75import jogamp.graph.ui.TreeTool;
76
77/**
78 * Media player {@link Widget}, embedding a {@link MediaButton} and its controls.
79 * @see #MediaPlayer(int, Scene, GLMediaPlayer, Uri, int, float, boolean, float, List)
80 */
81public class MediaPlayer extends Widget {
82 private static final boolean DEBUG = false;
83 public static final Vec2f FixedSymSize = new Vec2f(0.0f, 1.0f);
84 public static final Vec2f SymSpacing = new Vec2f(0f, 0.2f);
85 public static final float CtrlButtonWidth = 1f;
86 public static final float CtrlButtonHeight = 1f;
87 public static final Vec4f CtrlCellCol = new Vec4f(0, 0, 0, 0);
88 private static final float BorderSz = 0.01f;
89 private static final float BorderSzS = 0.03f;
90 private static final Vec4f BorderColor = new Vec4f(0, 0, 0, 0.5f);
91 private static final Vec4f BorderColorA = new Vec4f(0, 0, 0.5f, 0.5f);
92 private static final float AlphaBlend = 0.3f;
93 private static final float KnobScale = 2f;
94 private static final float StillPlayerScale = 1/3f;
95 private static final float ChapterTipScaleY = 5f;
96 private static final float ToolTipScaleY = 0.6f;
97
98 private final MediaButton mButton;
99
100 /**
101 * Constructs a {@link MediaPlayer}, i.e. its shapes and controls.
102 * @param renderModes Graph's {@link Region} render modes, see {@link GLRegion#create(GLProfile, int, TextureSequence) create(..)}.
103 * @param scene the used {@link Scene} to query parameter and access rendering loop
104 * @param mPlayer fresh {@link GLMediaPlayer} instance owned by this {@link MediaPlayer}, may be customized via e.g. {@link GLMediaPlayer#setTextureMinMagFilter(int[])}.
105 * @param medium {@link Uri} stream source, either a file or network source
106 * @param aratio aspect ratio of the resulting {@link Shape}, usually 16.0f/9.0f or 4.0f/3.0f, which also denotes the width of this shape while using height 1.0.
107 * @param letterBox toggles {@link GLMediaPlayer#setARatioLetterbox(boolean, Vec4f)} on or off
108 * @param zoomSize zoom-size (0..1] for zoom-out control
109 * @param enableStills pass {@code true} to enable still images on the time slider on mouse-over, involves a 2nd internal {@link GLMediaPlayer} instance
110 * @param customCtrls optional custom controls, maybe an empty list
111 */
112 public MediaPlayer(final int renderModes, final Scene scene, final GLMediaPlayer mPlayer,
113 final Uri medium, final float aratio, final boolean letterBox, final float zoomSize,
114 final boolean enableStills, final List<Shape> customCtrls)
115 {
116 super( new BoxLayout(aratio, 1, Alignment.None) );
117
118 final Font fontInfo = FontFactory.getDefaultFont(), fontSymbols = FontFactory.getSymbolsFont();
119 if( null == fontInfo || null == fontSymbols ) {
120 mButton = null;
121 return;
122 }
123 final float zEpsilon = scene.getZEpsilon(16);
124 final float superZOffset = zEpsilon * 20f;
125
126 final int ctrlCellsInt = 11+3;
127 final int ctrlCells = Math.max(customCtrls.size() + ctrlCellsInt, 20);
128
129 final float ctrlCellWidth = (aratio-2*BorderSzS)/ctrlCells;
130 final float ctrlCellHeight = ctrlCellWidth;
131 final float ctrlSliderHeightMin = ctrlCellHeight/6f; // bar-height
132 final float ctrlSliderHeightMax = KnobScale * ctrlSliderHeightMin; // knob-height
133
134 final AtomicReference<Shape> zoomReplacement = new AtomicReference<Shape>();
135 final AtomicReference<Vec3f> zoomOrigScale = new AtomicReference<Vec3f>();
136 final AtomicReference<Vec3f> zoomOrigPos = new AtomicReference<Vec3f>();
137
138 this.setName("mp.container");
139 this.setBorderColor(BorderColor).setBorder(BorderSz);
140 this.setInteractive(true).setFixedARatioResize(true);
141
142 mButton = new MediaButton(renderModes, aratio, 1, mPlayer);
143 mButton.setName("mp.mButton").setInteractive(false);
144 mButton.setPerp().setPressedColorMod(1f, 1f, 1f, 0.85f);
145 mButton.setVerbose(false).addDefaultEventListener().setARatioLetterbox(letterBox, new Vec4f(1, 1, 1, 1));
146 mPlayer.setAudioVolume( 0f );
147
148 final RangeSlider ctrlSlider;
149 {
150 final float knobScale = ctrlSliderHeightMax / ctrlSliderHeightMin;
151 ctrlSlider = new RangeSlider(renderModes, new Vec2f(aratio - ctrlSliderHeightMax, ctrlSliderHeightMin), knobScale, new Vec2f(0, 100), 1000, 0);
152 final float dx = ctrlSliderHeightMax / 2f;
153 final float dy = ( ctrlSliderHeightMax - ctrlSliderHeightMin ) * 0.5f;
154 ctrlSlider.setPaddding(new Padding(0, dx, ctrlCellHeight-dy, dx));
155 ctrlSlider.getBar().setColor(0.3f, 0.3f, 0.3f, 0.7f);
156 ctrlSlider.getKnob().setColor(0.6f, 0.6f, 1f, 1f);
157 ctrlSlider.setActiveKnobColorMod(new Vec4f(0.1f, 0.1f, 1, 1));
158 ctrlSlider.move(0, 0, zEpsilon);
159 }
160 ctrlSlider.setName("mp.slider");
161
162 final GLMediaPlayer stillPlayer;
163 final Button stillTime;
164 final HUDShape stillHUD;
165 final Runnable reshapeStillHUD;
166 {
167 final Group stillGroup = new Group();
168 final float labelW = aratio/4f;
169 final float labelH = 1f/10f;
170 stillTime = new Button(renderModes, fontInfo, PTS.toTimeStr(0), labelW, labelH, 0);
171 stillTime.setName("mp.stillTime").setInteractive(false);
172 stillTime.setLabelColor(0.2f, 0.2f, 0.2f, 1f);
173 stillTime.setColor(1f, 1f, 1f, 1f);
174 stillTime.setSpacing(0.1f, 0.3f);
175 stillTime.setCorner(0.75f);
176 stillTime.moveTo(aratio/2f-labelW/2f, 0, 0); // center to stillMedia
177 stillGroup.addShape(stillTime);
178
179 final MediaButton stillMedia;
180 if( enableStills ) {
181 stillPlayer = GLMediaPlayerFactory.createDefault();
182 // stillPlayer.setTextureMinMagFilter( new int[] { GL.GL_NEAREST, GL.GL_NEAREST } );
183 stillPlayer.setTextureMinMagFilter( new int[] { GL.GL_LINEAR, GL.GL_LINEAR } );
184 stillPlayer.setTextureUnit(2);
185 stillPlayer.addEventListener((final GLMediaPlayer mp, final EventMask eventMask, final long when) -> {
186 if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Play) ) {
187 mp.pause(true);
188 }
189 });
190 stillMedia = new MediaButton(renderModes, aratio, 1.0f, stillPlayer);
191 stillMedia.setName("mp.stillMedia").setInteractive(false);
192 stillMedia.setPerp().setPressedColorMod(1f, 1f, 1f, 0.85f);
194 stillMedia.moveTo(0, labelH*1.2f, 0); // above stillTime
195 stillGroup.addShape(stillMedia);
196 } else {
197 stillPlayer = null;
198 stillMedia = null;
199 }
200 stillHUD = new HUDShape(scene,
201 enableStills ? aratio*StillPlayerScale : labelW*StillPlayerScale,
202 enableStills ? 1f*StillPlayerScale : labelH*StillPlayerScale,
203 renderModes, ctrlSlider, stillGroup);
204 stillHUD.setVisible(false);
205 scene.addShape(stillHUD);
206 reshapeStillHUD = () -> {
207 final float ar = (float)mPlayer.getWidth()/(float)mPlayer.getHeight();
208 final float labelW2 = ar/4f;
209 final float labelH2 = 1f/10f;
210 stillMedia.setSize(ar, 1f);
211 stillMedia.moveTo(0, labelH2*1.2f, 0); // above stillTime
212 stillTime.moveTo(ar/2f-labelW2/2f, 0, 0); // center to stillMedia
213 stillHUD.setClientSize(ar*StillPlayerScale, 1f*StillPlayerScale);
214 };
215 }
216
217 final Button playButton = new Button(renderModes, fontSymbols,
218 fontSymbols.getUTF16String("play_arrow"), fontSymbols.getUTF16String("pause"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
219 playButton.setName("mp.play");
221
222 final Button audioLabel = new Button(renderModes, fontInfo, "audio\nund", CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
223 audioLabel.setName("mp.audio_lang").setToggleable(false);
224 audioLabel.setPerp().setColor(CtrlCellCol);
225 final Button subLabel = new Button(renderModes, fontInfo, "sub\nund", CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
226 subLabel.setName("mp.sub_lang").setToggleable(false);
227 subLabel.setPerp().setColor(CtrlCellCol);
228 final Button cropButton = new Button(renderModes, fontSymbols,
229 fontSymbols.getUTF16String("crop"), fontSymbols.getUTF16String("crop_free"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
231
232 mPlayer.addEventListener((final GLMediaPlayer mp, final EventMask eventMask, final long when) -> {
233 if( DEBUG ) {
234 System.err.println("MediaButton AttributesChanges: "+eventMask+", when "+when);
235 System.err.println("MediaButton State: "+mp);
236 }
237 if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Init) ) {
238 audioLabel.setText("audio\n"+mp.getLang(mp.getAID()));
239 subLabel.setText("sub\n"+mp.getLang(mp.getSID()));
240 ctrlSlider.setMinMax(new Vec2f(0, mp.getDuration()), 0);
241 System.err.println("Init "+mp.toString());
242
243 for(final GLMediaPlayer.Chapter c : mp.getChapters()) {
244 if( DEBUG ) {
245 System.err.println(c);
246 }
247 final Shape mark = ctrlSlider.addMark(c.start, new Vec4f(0.9f, 0.9f, 0.9f, 0.5f));
248 mark.setToolTip(new TooltipText(c.title+"\n@ "+PTS.toTimeStr(c.start, false)+", duration "+PTS.toTimeStr(c.duration(), false), fontInfo, ChapterTipScaleY));
249 }
250 final float aratioVideo = (float)mPlayer.getWidth() / (float)mPlayer.getHeight();
251 if( FloatUtil.isZero(Math.abs(aratio - aratioVideo), 0.1f) ) {
252 cropButton.setVisible(false);
253 System.err.println("AR Crop disabled: aratioPlayer "+aratio+", aratioVideo "+aratioVideo+": "+mPlayer.getTitle());
254 } else {
255 System.err.println("AR Crop enabled: aratioPlayer "+aratio+", aratioVideo "+aratioVideo+": "+mPlayer.getTitle());
256 }
257 if( enableStills ) {
258 scene.invoke(false, (final GLAutoDrawable d) -> {
259 stillPlayer.stop();
260 stillPlayer.playStream(mPlayer.getUri(), mPlayer.getVID(), GLMediaPlayer.STREAM_ID_NONE, GLMediaPlayer.STREAM_ID_NONE, 1);
261 reshapeStillHUD.run();
262 return true;
263 });
264 }
265 } else if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Play) ) {
266 playButton.setToggle(true);
267 } else if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Pause) ) {
268 playButton.setToggle(false);
269 }
270 if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.EOS) ) {
271 final StreamException err = mp.getStreamException();
272 if( null != err ) {
273 System.err.println("MovieSimple State: Exception: "+err.getMessage());
274 } else {
275 new InterruptSource.Thread() {
276 @Override
277 public void run() {
278 mp.setPlaySpeed(1f);
279 mp.seek(0);
280 mp.resume();
281 }
282 }.start();
283 }
284 }
285 } );
286 this.addShape(mButton);
287
288 Group ctrlGroup, infoGroup;
289 Shape ctrlBlend;
290 final Label muteLabel, infoLabel;
291 final Button timeLabel;
292 final float infoGroupHeight;
293 final boolean[] hud_sticky = { false };
294 final boolean[] info_full = { false };
295 {
296 muteLabel = new Label(renderModes, fontSymbols, aratio/6f, fontSymbols.getUTF16String("music_off")); // volume_mute, headset_off
297 muteLabel.setName("mp.mute");
298 {
299 final float sz = aratio/6f;
300 muteLabel.setColor(1, 0, 0, 1);
301 muteLabel.setPaddding(new Padding(0, 0, 1f-sz, aratio-sz));
302
303 muteLabel.setInteractive(false);
304 muteLabel.setVisible( mPlayer.isAudioMuted() );
305 this.addShape(muteLabel);
306 }
307
308 infoGroup = new Group(new BoxLayout());
309 infoGroup.setName("mp.info").setInteractive(false);
310 this.addShape( infoGroup.setVisible(false) );
311 {
312 final String text = "88:88 / 88:88:88 (88 %), playing (8.88x, vol 8.88), A/R 8.88, vid 8 (H264), aid 8 (eng, AC3), sid 8 (eng, HDMV_PGS)\n"+
313 "JogAmp's GraphUI Full-Feature Media Player Widget Rocks!";
314 final AABBox textBounds = fontInfo.getGlyphBounds(text);
315 final float lineHeight = textBounds.getHeight()/2f; // fontInfo.getLineHeight();
316 final float sxy = aratio/(1.1f*textBounds.getWidth()); // add 10%
317 infoLabel = new Label(renderModes, fontInfo, text);
318 infoLabel.setName("mp.info.label");
319 infoLabel.setInteractive(false);
320 infoLabel.setColor(1, 1, 1, 0.9f);
321 infoLabel.scale(sxy, sxy, 1f);
322
323 final float dy = 0.5f*lineHeight*sxy;
324 infoGroupHeight = 2.5f*dy + textBounds.getHeight()*sxy;
325 if( DEBUG ) {
326 System.err.println("XXX: sxy "+sxy+", b "+textBounds);
327 System.err.println("XXX: GroupHeight "+infoGroupHeight+", dy "+dy+", lineHeight*sxy "+(lineHeight*sxy));
328 System.err.println("XXX: b.getHeight() * sxy "+(textBounds.getHeight() * sxy));
329 System.err.println("XXX: (1f-GroupHeight+dy)/sxy "+((1f-infoGroupHeight+dy)/sxy));
330 }
331 infoLabel.setPaddding(new Padding(0, 0, (1f-infoGroupHeight+dy)/sxy, 0.5f));
332
333 final Rectangle rect = new Rectangle(renderModes & ~Region.AA_RENDERING_MASK, aratio, infoGroupHeight, 0);
334 rect.setName("mp.info.blend").setInteractive(false);
335 rect.setColor(0, 0, 0, AlphaBlend);
336 rect.setPaddding(new Padding(0, 0, 1f-infoGroupHeight, 0));
337
338 infoGroup.addShape(rect);
339 infoGroup.addShape(infoLabel);
340 }
341 {
342 timeLabel = new Button(renderModes, fontInfo,
343 getMultilineTime(Clock.currentMillis(), mPlayer), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
344 timeLabel.setName("mp.time");
345 timeLabel.setPerp().setColor(CtrlCellCol);
346 timeLabel.setLabelColor(1, 1, 1, 1.0f);
347 }
348 scene.addGLEventListener(new GLEventAdapter() {
349 long t0 = 0;
350 @Override
351 public void display(final GLAutoDrawable drawable) {
352 final GLAnimatorControl anim = drawable.getAnimator();
353 if( ( timeLabel.isVisible() || infoLabel.isVisible() ) &&
354 ( mPlayer.getState() == GLMediaPlayer.State.Playing ||
355 mPlayer.getState() == GLMediaPlayer.State.Paused ) &&
356 null != anim )
357 {
358 final long t1 = anim.getTotalFPSDuration();
359 if( t1 - t0 >= 333) {
360 t0 = t1;
361 final int ptsMS = mPlayer.getPTS().getCurrent();
362 final int durationMS = mPlayer.getDuration();
363 infoLabel.setText(getInfo(ptsMS, durationMS, mPlayer, info_full[0]));
364 timeLabel.setText(getMultilineTime(ptsMS, durationMS));
365 ctrlSlider.setValue(ptsMS);
366 }
367 }
368 }
369 } );
370 ctrlSlider.addChangeListener((final RangeSlider w, final float old_val, final float val, final float old_val_pct, final float val_pct, final Vec3f pos, final MouseEvent e) -> {
371 if( DEBUG ) {
372 System.err.println("Dragged "+w.getName()+": "+PTS.toTimeStr(Math.round(val), true)+"ms, "+(val_pct*100f)+"%");
373 System.err.println("Slider.D "+ctrlSlider.getDescription());
374 }
375 final int dir_val = (int)Math.signum(val - old_val);
376 final int currentPTS = mPlayer.getPTS().getCurrent();
377 final int nextPTS = Math.round( val );
378 final int dir_pts = (int)Math.signum(nextPTS - currentPTS);
379 if( dir_val == dir_pts ) {
380 scene.invoke(false, (final GLAutoDrawable d) -> {
381 final int durationMS = mPlayer.getDuration();
382 timeLabel.setText(getMultilineTime(nextPTS, durationMS));
383 mPlayer.seek(nextPTS);
384 return true;
385 } );
386 }
387 });
388
389 final int[] lastPeekPTS = { 0 };
390 ctrlSlider.addPeekListener((final RangeSlider w, final float val, final float val_pct, final Vec3f pos, final MouseEvent e) -> {
391 final float res = Math.max(1000, w.getRange() / 300f); // ~300dpi alike less jittery
392 final int nextPTS = Math.round( val/1000f ) * 1000;
393 final Vec3f pos2 = new Vec3f(pos.x()-stillHUD.getClientSize().x()/2f, ctrlSliderHeightMax, pos.z() + ctrlSlider.getPosition().z());
394 stillHUD.moveToHUDPos(pos2);
395 // stillMedia.setARatioLetterbox(mButton.useARatioAdjustment(), mButton.getARatioLetterboxBackColor());
396 stillTime.setText(PTS.toTimeStr(nextPTS, false));
397 stillHUD.setVisible(true);
398 if( enableStills && Math.abs(lastPeekPTS[0] - nextPTS ) >= res ) {
399 scene.invoke(false, (final GLAutoDrawable d) -> {
400 stillPlayer.seek(nextPTS);
401 return true;
402 } );
403 lastPeekPTS[0] = nextPTS;
404 }
405 });
406 ctrlSlider.addActivationListener((final Shape s) -> {
407 if( s.isActive() ) {
408 // stillMedia.setARatioLetterbox(mButton.useARatioAdjustment(), mButton.getARatioLetterboxBackColor());
409 stillTime.setText(PTS.toTimeStr(mPlayer.getPTS().getCurrent(), false));
410 stillHUD.setVisible(true);
411 } else {
412 stillHUD.setVisible(false);
413 }
414 });
415
416 ctrlBlend = new Rectangle(renderModes & ~Region.AA_RENDERING_MASK, aratio, ctrlCellHeight, 0);
417 ctrlBlend.setName("ctrl.blend").setInteractive(false);
418 ctrlBlend.setColor(0, 0, 0, AlphaBlend);
419 this.addShape( ctrlBlend.setVisible(false) );
420
421 this.addShape( ctrlSlider.setVisible(false) );
422
423 ctrlGroup = new Group(new GridLayout(ctrlCellWidth, ctrlCellHeight, Alignment.FillCenter, Gap.None, 1));
424 ctrlGroup.setName("ctrlGroup").setInteractive(false);
425 ctrlGroup.setPaddding(new Padding(0, BorderSzS, 0, BorderSzS));
426 this.addShape( ctrlGroup.setVisible(false) );
427 { // 1
428 playButton.onToggle((final Shape s) -> {
429 if( s.isToggleOn() ) {
430 mPlayer.setPlaySpeed(1);
431 mPlayer.resume();
432 } else {
433 mPlayer.pause(false);
434 }
435 });
436 playButton.setToggle(true); // on == play
437 ctrlGroup.addShape(playButton);
438 playButton.setToolTip(new TooltipText("Play/Pause", fontInfo, ToolTipScaleY));
439 }
440 { // 2
441 final Button button = new Button(renderModes, fontSymbols,
442 fontSymbols.getUTF16String("skip_previous"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
443 button.setName("back");
444 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
445 button.onClicked((final Shape s, final Vec3f pos, final MouseEvent e) -> {
446 final int pts = mPlayer.getPTS().getCurrent();
447 int targetMS = 0;
448 final GLMediaPlayer.Chapter c0 = mPlayer.getChapter(mPlayer.getPTS().getCurrent());
449 if( null != c0 ) {
450 if( pts > c0.start + 5000 ) {
451 targetMS = c0.start;
452 } else {
453 final GLMediaPlayer.Chapter c1 = mPlayer.getChapter(c0.start - 1);
454 if( null != c1 ) {
455 targetMS = c1.start;
456 } else {
457 targetMS = 0;
458 }
459 }
460 }
461 mPlayer.seek(targetMS);
462 });
463 ctrlGroup.addShape(button);
464 button.setToolTip(new TooltipText("Prev Chapter", fontInfo, ToolTipScaleY));
465 }
466 { // 3
467 final Button button = new Button(renderModes, fontSymbols,
468 fontSymbols.getUTF16String("skip_next"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
469 button.setName("next");
470 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
471 button.onClicked((final Shape s, final Vec3f pos, final MouseEvent e) -> {
472 int targetMS = 0;
473 final GLMediaPlayer.Chapter c0 = mPlayer.getChapter(mPlayer.getPTS().getCurrent());
474 if( null != c0 ) {
475 final GLMediaPlayer.Chapter c1 = mPlayer.getChapter(c0.end + 1);
476 if( null != c1 ) {
477 targetMS = c1.start;
478 } else {
479 targetMS = c0.end;
480 }
481 } else if( mPlayer.getChapters().length > 0 ) {
482 targetMS = 0;
483 } else {
484 targetMS = mPlayer.getPTS().getCurrent() * ( 1 + 1 / ( 60000 * 10 ) );
485 }
486 mPlayer.seek(targetMS);
487 });
488 ctrlGroup.addShape(button);
489 button.setToolTip(new TooltipText("Next Chapter", fontInfo, ToolTipScaleY));
490 }
491 { // 4
492 final Button button = new Button(renderModes, fontSymbols,
493 fontSymbols.getUTF16String("fast_rewind"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
494 button.setName("rv-");
495 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
496 button.onClicked((final Shape s, final Vec3f pos, final MouseEvent e) -> {
497 final float v = mPlayer.getPlaySpeed();
498 if( v <= 1.0f ) {
499 mPlayer.setPlaySpeed(v / 2.0f);
500 } else {
501 mPlayer.setPlaySpeed(v - 0.5f);
502 }
503 });
504 ctrlGroup.addShape(button);
505 button.setToolTip(new TooltipText("replay speed: v <= 1 ? v/2 : v-0.5", fontInfo, ToolTipScaleY));
506 }
507 { // 5
508 final Button button = new Button(renderModes, fontSymbols,
509 fontSymbols.getUTF16String("fast_forward"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
510 button.setName("rv+");
511 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
512 button.onClicked((final Shape s, final Vec3f pos, final MouseEvent e) -> {
513 final float v = mPlayer.getPlaySpeed();
514 if( 1f > v && v + 0.5f > 1f ) {
515 mPlayer.setPlaySpeed(1); // reset while crossing over 1
516 } else {
517 mPlayer.setPlaySpeed(v + 0.5f);
518 }
519 });
520 ctrlGroup.addShape(button);
521 button.setToolTip(new TooltipText("replay speed: v+0.5", fontInfo, ToolTipScaleY));
522 }
523 { // 6
524 final Button button = new Button(renderModes, fontSymbols,
525 fontSymbols.getUTF16String("replay_10"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
526 button.setName("rew5");
527 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
528 button.onClicked((final Shape s, final Vec3f pos, final MouseEvent e) -> {
529 mPlayer.seek(mPlayer.getPTS().getCurrent() - 10000);
530 });
531 button.addMouseListener(new Shape.MouseGestureAdapter() {
532 @Override
533 public void mouseWheelMoved(final MouseEvent e) {
534 final int pts0 = mPlayer.getPTS().getCurrent();
535 final int pts1 = pts0 + (int)(e.getRotation()[1]*10000f);
536 if( DEBUG ) {
537 System.err.println("Seek: "+pts0+" -> "+pts1);
538 }
539 mPlayer.seek(pts1);
540 } } );
541 ctrlGroup.addShape(button);
542 button.setToolTip(new TooltipText("Replay 10s (+scroll)", fontInfo, ToolTipScaleY));
543 }
544 { // 7
545 final Button button = new Button(renderModes, fontSymbols,
546 fontSymbols.getUTF16String("forward_10"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
547 button.setName("fwd5");
548 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
549 button.onClicked((final Shape s, final Vec3f pos, final MouseEvent e) -> {
550 mPlayer.seek(mPlayer.getPTS().getCurrent() + 10000);
551 });
552 button.addMouseListener(new Shape.MouseGestureAdapter() {
553 @Override
554 public void mouseWheelMoved(final MouseEvent e) {
555 final int pts0 = mPlayer.getPTS().getCurrent();
556 final int pts1 = pts0 + (int)(e.getRotation()[1]*10000f);
557 if( DEBUG ) {
558 System.err.println("Seek: "+pts0+" -> "+pts1);
559 }
560 mPlayer.seek(pts1);
561 } } );
562 ctrlGroup.addShape(button);
563 button.setToolTip(new TooltipText("Forward 10s (+scroll)", fontInfo, ToolTipScaleY));
564 }
565 { // 8
566 final Button button = new Button(renderModes, fontSymbols,
567 fontSymbols.getUTF16String("volume_up"), fontSymbols.getUTF16String("volume_mute"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
568 button.setName("mute");
569 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
570 final float[] volume = { 1.0f };
571 button.onToggle( (final Shape s) -> {
572 if( s.isToggleOn() ) {
573 mPlayer.setAudioVolume( volume[0] );
574 } else {
575 mPlayer.setAudioVolume( 0f );
576 }
577 muteLabel.setVisible( !s.isToggleOn() );
578 });
579 button.addMouseListener(new Shape.MouseGestureAdapter() {
580 @Override
581 public void mouseWheelMoved(final MouseEvent e) {
582 mPlayer.setAudioVolume( mPlayer.getAudioVolume() + e.getRotation()[1]/20f );
583 volume[0] = mPlayer.getAudioVolume();
584 } } );
585 button.setToggle( !mPlayer.isAudioMuted() ); // on == volume
586 ctrlGroup.addShape(button);
587 button.setToolTip(new TooltipText("Volume (+scroll)", fontInfo, ToolTipScaleY));
588 }
589 { // 9
590 audioLabel.onClicked((final Shape s, final Vec3f pos, final MouseEvent e) -> {
591 final int next_aid = mPlayer.getNextAID();
592 if( mPlayer.getAID() != next_aid ) {
593 mPlayer.switchStream(mPlayer.getVID(), next_aid, mPlayer.getSID());
594 }
595 });
596 ctrlGroup.addShape(audioLabel);
597 audioLabel.setToolTip(new TooltipText("Audio Language", fontInfo, ToolTipScaleY));
598 }
599 { // 10
600 subLabel.onClicked((final Shape s, final Vec3f pos, final MouseEvent e) -> {
601 final int next_sid = mPlayer.getNextSID();
602 if( mPlayer.getSID() != next_sid ) {
603 mPlayer.switchStream(mPlayer.getVID(), mPlayer.getAID(), next_sid);
604 }
605 });
606 ctrlGroup.addShape(subLabel);
607 subLabel.setToolTip(new TooltipText("Subtitle Language", fontInfo, ToolTipScaleY));
608 }
609 { // 11
610 ctrlGroup.addShape(timeLabel);
611 }
612 for(int i=11; i<ctrlCells-3-customCtrls.size(); ++i) {
613 final Button button = new Button(renderModes, fontInfo, " ", CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
614 button.setName("ctrl_"+i);
615 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
616 ctrlGroup.addShape(button);
617 }
618 { // -1
619 final Button button = new Button(renderModes, fontSymbols,
620 fontSymbols.getUTF16String("visibility"), fontSymbols.getUTF16String("visibility_off"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
621 button.setName("hud");
622 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
623 button.onToggle( (final Shape s) -> {
624 hud_sticky[0] = s.isToggleOn();
625 });
626 button.setToggle( false );
627 ctrlGroup.addShape(button);
628 button.setToolTip(new TooltipText("Sticky HUD", fontInfo, ToolTipScaleY));
629 }
630 { // -2
631 final boolean[] value = { !letterBox };
632 cropButton.onToggle( (final Shape s) -> {
633 value[0] = !value[0];
634 mButton.setARatioLetterbox(!value[0], mButton.getARatioLetterboxBackColor());
635 });
636 ctrlGroup.addShape(cropButton);
637 cropButton.setToolTip(new TooltipText("Letterbox Crop", fontInfo, ToolTipScaleY));
638 }
639 { // -3
640 final Button button = new Button(renderModes, fontSymbols,
641 fontSymbols.getUTF16String("zoom_out_map"), fontSymbols.getUTF16String("zoom_in_map"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
642 button.setName("zoom");
643 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
644 final boolean fullScene = FloatUtil.isEqual(1f, zoomSize);
645 final boolean wasDraggable = isDraggable();
646 button.onToggle( (final Shape s) -> {
647 if( s.isToggleOn() ) {
648 // Zoom in
649 if( fullScene ) {
650 MediaPlayer.this.setBorder(0f);
651 MediaPlayer.this.setDraggable(false);
652 }
653 final AABBox sbox = scene.getBounds();
654 final Group parent = this.getParent();
655 final float sx = sbox.getWidth() * zoomSize / this.getScaledWidth();
656 final float sy = sbox.getHeight() * zoomSize / this.getScaledHeight();
657 final float sxy = Math.min(sx, sy);
658 Shape _zoomReplacement = null;
659 zoomOrigScale.set( this.getScale().copy() );
660 zoomOrigPos.set( this.getPosition().copy() );
661 if( null != parent ) {
662 // System.err.println("Zoom1: rep, sxy "+sx+" x "+sy+" = "+sxy);
663 _zoomReplacement = new Label(renderModes, fontInfo, aratio/40f, "zoomed");
664 final boolean r = parent.replaceShape(this, _zoomReplacement);
665 if( r ) {
666 // System.err.println("Zoom1: p "+parent);
667 // System.err.println("Zoom1: t "+this);
668 this.scale(sxy, sxy, 1f);
669 this.moveTo(sbox.getLow()).move(sbox.getWidth() * ( 1f - zoomSize )/2.0f, sbox.getHeight() * ( 1f - zoomSize )/2.0f, superZOffset);
670 scene.addShape(this);
671 } else {
672 // System.err.println("Zoom1: failed "+this);
673 }
674 } else {
675 // System.err.println("Zoom2: top, sxy "+sx+" x "+sy+" = "+sxy);
676 // System.err.println("Zoom2: t "+this);
677 this.scale(sxy, sxy, 1f);
678 this.moveTo(sbox.getLow()).move(sbox.getWidth() * ( 1f - zoomSize )/2.0f, sbox.getHeight() * ( 1f - zoomSize )/2.0f, superZOffset);
679 }
680 // System.err.println("Zoom: R "+_zoomReplacement);
681 zoomReplacement.set( _zoomReplacement );
682 } else {
683 // Zoom out
684 if( fullScene ) {
685 MediaPlayer.this.setBorder(BorderSz);
686 MediaPlayer.this.setDraggable(wasDraggable);
687 }
688 final Vec3f _zoomOrigScale = zoomOrigScale.getAndSet(null);
689 final Vec3f _zoomOrigPos = zoomOrigPos.getAndSet(null);
690 final Shape _zoomReplacement = zoomReplacement.getAndSet(null);
691 if( null != _zoomReplacement ) {
692 final Group parent = _zoomReplacement.getParent();
693 scene.removeShape(this);
694 if( null != _zoomOrigScale ) {
695 this.setScale(_zoomOrigScale);
696 }
697 if( null != _zoomOrigPos ) {
698 this.moveTo(_zoomOrigPos);
699 }
700 parent.replaceShape(_zoomReplacement, this);
701 // System.err.println("Reset1: "+parent);
702 } else {
703 if( null != _zoomOrigScale ) {
704 this.setScale(_zoomOrigScale);
705 }
706 if( null != _zoomOrigPos ) {
707 this.moveTo(_zoomOrigPos);
708 }
709 // System.err.println("Reset2: top");
710 }
711 if( null != _zoomReplacement ) {
712 scene.invoke(true, (drawable) -> {
713 final GL2ES2 gl = drawable.getGL().getGL2ES2();
714 _zoomReplacement .destroy(gl, scene.getRenderer());
715 return true;
716 });
717 }
718 }
719 });
720 button.setToggle( false ); // on == zoom
721 ctrlGroup.addShape(button);
722 button.setToolTip(new TooltipText("Zoom", fontInfo, ToolTipScaleY));
723 }
724 for(final Shape cs : customCtrls ) {
725 ctrlGroup.addShape(cs);
726 }
727 }
728 this.enableTopLevelWidget(scene);
729
730 this.addActivationListener( (final Shape s) -> {
731 if( this.isActive() ) {
732 this.setBorderColor(BorderColorA);
733 } else {
734 final boolean hud_on = hud_sticky[0];
735 this.setBorderColor(BorderColor);
736 ctrlSlider.setVisible(hud_on);
737 ctrlBlend.setVisible(hud_on);
738 ctrlGroup.setVisible(hud_on);
739 infoGroup.setVisible(hud_on);
740 }
741 });
742 this.addMouseListener(new Shape.MouseGestureAdapter() {
743 @Override
744 public void mouseClicked(final MouseEvent e) {
745 final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
746 final Vec3f p = shapeEvent.objPos;
747 if( p.y() > ( 1f - infoGroupHeight ) ) {
748 info_full[0] = !info_full[0];
749 final float sxy = infoLabel.getScale().x();
750 final float p_bottom_s = infoLabel.getPadding().bottom * sxy;
751 final float sxy2;
752 if( info_full[0] ) {
753 sxy2 = sxy * 0.5f;
754 } else {
755 sxy2 = sxy * 2f;
756 }
757 infoLabel.setScale(sxy2, sxy2, 1f);
758 infoLabel.setPaddding(new Padding(0, 0, p_bottom_s/sxy2, 0.5f));
759 }
760 }
761 @Override
762 public void mouseMoved(final MouseEvent e) {
763 final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
764 final Vec3f p = shapeEvent.objPos;
765 final boolean c = hud_sticky[0] || ( ctrlCellHeight + ctrlSliderHeightMax ) > p.y() || p.y() > ( 1f - infoGroupHeight );
766 ctrlSlider.setVisible(c);
767 ctrlBlend.setVisible(c);
768 ctrlGroup.setVisible(c);
769 infoGroup.setVisible(c);
770 }
771 @Override
772 public void mouseReleased(final MouseEvent e) {
773 mButton.setPressedColorMod(1f, 1f, 1f, 1f);
774 }
775 @Override
776 public void mouseDragged(final MouseEvent e) {
777 mButton.setPressedColorMod(1f, 1f, 1f, 0.85f);
778 }
779 } );
780 TreeTool.forAll(this, (final Shape s) -> { s.setDraggable(false).setResizable(false); return false; });
781 ctrlSlider.getKnob().setDraggable(true);
782 }
783
784 /** Toggle enabling subtitle rendering */
785 public void setSubtitlesEnabled(final boolean v) {
786 if( null != mButton ) {
787 mButton.setSubtitlesEnabled(v);
788 }
789 }
790
791 /**
792 * Sets text/ASS subtitle parameter, enabling subtitle rendering
793 * @param subFont text/ASS subtitle font, pass {@code null} for {@link FontFactory#getDefaultFont()}
794 * {@link FontFactory#getFallbackFont()} is used {@link Font#getBestCoverage(Font, Font, CharSequence) if providing a better coverage} of a Text/ASS subtitle line.
795 * @param subLineHeightPct text/ASS subtitle line height percentage, defaults to {@link MediaButton#DEFAULT_ASS_SUB_HEIGHT}
796 * @param subLineDY text/ASS y-axis offset to bottom in line-height, defaults to {@link MediaButton#DEFAULT_ASS_SUB_POS}
797 * @param subAlignment text/ASS subtitle alignment, defaults to {@link #DEFAULT_ASS_SUB_ALIGNMENT}
798 */
799 public void setSubtitleParams(final Font subFont, final float subLineHeightPct, final float subLineDY, final Alignment subAlignment) {
800 if( null != mButton ) {
801 mButton.setSubtitleParams(subFont, subLineHeightPct, subLineDY, subAlignment);
802 }
803 }
804 /**
805 * Sets text/ASS subtitle colors
806 * @param color color for the text/ASS, defaults to {@link MediaButton#DEFAULT_ASS_SUB_COLOR}
807 * @param blend blending alpha (darkness), defaults to {@link MediaButton#DEFAULT_ASS_SUB_BLEND}
808 */
809 public void setSubtitleColor(final Vec4f color, final float blend) {
810 if( null != mButton ) {
811 mButton.setSubtitleColor(color, blend);
812 }
813 }
814
815 public static String getInfo(final long currentMillis, final GLMediaPlayer mPlayer, final boolean full) {
816 return getInfo(mPlayer.getPTS().get(currentMillis), mPlayer.getDuration(), mPlayer, full);
817 }
818 public static String getInfo(final int ptsMS, final int durationMS, final GLMediaPlayer mPlayer, final boolean full) {
819 final String chapter;
820 {
821 final GLMediaPlayer.Chapter c = mPlayer.getChapter(ptsMS);
822 if( null != c ) {
823 chapter = " - "+c.title;
824 } else {
825 chapter = "";
826 }
827 }
828 final float aspect = (float)mPlayer.getWidth() / (float)mPlayer.getHeight();
829 final float pct = (float)ptsMS / (float)durationMS;
830 if( full ) {
831 final String text1 = String.format("%s / %s (%.0f %%), %s (%01.2fx, vol %1.2f), A/R %.2f, fps %02.1f, kbps %.2f",
832 PTS.toTimeStr(ptsMS, false), PTS.toTimeStr(durationMS, false), pct*100,
833 mPlayer.getState().toString().toLowerCase(), mPlayer.getPlaySpeed(), mPlayer.getAudioVolume(), aspect,
834 mPlayer.getFramerate(), mPlayer.getStreamBitrate()/1000.0f);
835 final String text2 = String.format("video: id %d (%s), kbps %.2f, codec %s/'%s'",
836 mPlayer.getVID(), mPlayer.getLang(mPlayer.getVID()), mPlayer.getVideoBitrate()/1000.0f, mPlayer.getVideoCodecID(), mPlayer.getVideoCodec());
837 final String text3 = String.format("audio: id %d/%s (%s/%s), kbps %.2f, codec %s/'%s'",
838 mPlayer.getAID(), Arrays.toString(mPlayer.getAStreams()),
839 mPlayer.getLang(mPlayer.getAID()), Arrays.toString(mPlayer.getALangs()),
840 mPlayer.getAudioBitrate()/1000.0f, mPlayer.getAudioCodecID(), mPlayer.getAudioCodec());
841 final String text4 = String.format("sub : id %d/%s (%s/%s), codec %s/'%s'",
842 mPlayer.getSID(), Arrays.toString(mPlayer.getSStreams()),
843 mPlayer.getLang(mPlayer.getSID()), Arrays.toString(mPlayer.getSLangs()),
844 mPlayer.getSubtitleCodecID(), mPlayer.getSubtitleCodec());
845 return text1+"\n"+text2+"\n"+text3+"\n"+text4+"\n"+mPlayer.getTitle()+chapter;
846 } else {
847 final String vinfo, ainfo, sinfo;
848 if( mPlayer.getVID() != GLMediaPlayer.STREAM_ID_NONE ) {
849 vinfo = String.format((Locale)null, ", vid %d (%s)", mPlayer.getVID(), mPlayer.getVideoCodecID());
850 } else {
851 vinfo = "";
852 }
853 if( mPlayer.getAID() != GLMediaPlayer.STREAM_ID_NONE ) {
854 ainfo = String.format((Locale)null, ", aid %d (%s, %s)", mPlayer.getAID(), mPlayer.getLang(mPlayer.getAID()), mPlayer.getAudioCodecID());
855 } else {
856 ainfo = "";
857 }
858 if( mPlayer.getSID() != GLMediaPlayer.STREAM_ID_NONE ) {
859 sinfo = String.format((Locale)null, ", sid %d (%s, %s)", mPlayer.getSID(), mPlayer.getLang(mPlayer.getSID()), mPlayer.getSubtitleCodecID());
860 } else {
861 sinfo = "";
862 }
863 final String text1 = String.format("%s / %s (%.0f %%), %s (%01.2fx, vol %1.2f), A/R %.2f%s%s%s",
864 PTS.toTimeStr(ptsMS, false), PTS.toTimeStr(durationMS, false), pct*100,
865 mPlayer.getState().toString().toLowerCase(), mPlayer.getPlaySpeed(), mPlayer.getAudioVolume(), aspect, vinfo, ainfo, sinfo);
866 return text1+"\n"+mPlayer.getTitle()+chapter;
867 }
868 }
869 public static String getMultilineTime(final long currentMillis, final GLMediaPlayer mPlayer) {
870 return getMultilineTime(mPlayer.getPTS().get(currentMillis), mPlayer.getDuration());
871 }
872 public static String getMultilineTime(final int ptsMS, final int durationMS) {
873 final float pct = (float)ptsMS / (float)durationMS;
874 return String.format("%.0f %%%n%s%n%s",
875 pct*100, PTS.toTimeStr(ptsMS, false), PTS.toTimeStr(durationMS, false));
876 }
877}
The optional property jogamp.graph.font.ctor allows user to specify the FontConstructor implementatio...
static Font getDefaultFont()
Returns default Font of default FontSet or null if n/a.
static Font getSymbolsFont()
Returns the default symbols Font or null if n/a.
Group of Shapes, optionally utilizing a Group.Layout.
Definition: Group.java:61
Group()
Create a group of Shapes w/o Group.Layout.
Definition: Group.java:106
void addShape(final Shape s)
Adds a Shape.
Definition: Group.java:225
GraphUI Scene.
Definition: Scene.java:102
void addShape(final Shape s)
Adds a Shape.
Definition: Scene.java:287
static float getZEpsilon(final int zBits, final PMVMatrixSetup setup)
Default Z precision on 16-bit depth buffer using -1 z-position and DEFAULT_ZNEAR.
Definition: Scene.java:126
Shape setColor(final float r, final float g, final float b, final float a)
Set base color.
Definition: Shape.java:1389
Shape setName(final String name)
Set a symbolic name for this shape for identification.
Definition: Shape.java:339
final Shape move(final float dtx, final float dty, final float dtz)
Move about scaled distance.
Definition: Shape.java:557
final Shape setInteractive(final boolean v)
Set whether this shape is interactive in general, i.e.
Definition: Shape.java:1711
final Shape setFixedARatioResize(final boolean v)
Sets whether aspect-ratio shall be kept at resize, if isResizable().
Definition: Shape.java:1788
final Shape moveTo(final float tx, final float ty, final float tz)
Move to scaled position.
Definition: Shape.java:543
Shape setPressedColorMod(final float r, final float g, final float b, final float a)
Set pressed color, modulating getColor() if isPressed().
Definition: Shape.java:1423
final Shape setPaddding(final Padding padding)
Sets the unscaled padding for this shape, which is included in unscaled getBounds() and also includes...
Definition: Shape.java:376
final Shape setVisible(final boolean v)
Enable (default) or disable this shape's visibility.
Definition: Shape.java:363
final Shape setBorderColor(final float r, final float g, final float b, final float a)
Set border color.
Definition: Shape.java:1489
final Shape setBorder(final float thickness)
Sets the thickness of the border, which is included in getBounds() and is outside of getPadding().
Definition: Shape.java:402
final Shape setToggleable(final boolean toggleable)
Set this shape toggleable, default is off.
Definition: Shape.java:1573
Immutable layout alignment options, including Bit#Fill.
Definition: Alignment.java:35
static final Alignment None
No alignment constant.
Definition: Alignment.java:37
GraphUI Stack Group.Layout.
Definition: BoxLayout.java:53
GraphUI CSS property Padding, unscaled space belonging to the element and included in the element's s...
Definition: Padding.java:38
BaseButton setPerp()
Sets a perpendicular corner.
BaseButton setSize(final float width, final float height)
BaseButton setCorner(final float corner)
Set corner size with range [0.01 .
Definition: BaseButton.java:95
A GraphUI text labeled BaseButton GraphShape.
Definition: Button.java:61
final Button setLabelColor(final Vec4f c)
Sets the label color, consider using alpha 1.
Definition: Button.java:333
final Button setSpacing(final float spacingX, final float spacingY)
Sets spacing in percent of text label, clipped to range [0 .
Definition: Button.java:300
A Head Up Display (HUD) Shape for a client Shape using inner size and Mv position to be displayed on ...
Definition: HUDShape.java:62
HUDShape setClientSize(final float clientWidth, final float clientHeight)
Sets the client Shape size of this HUD in given targetShape object Mv-space, not Scene.
Definition: HUDShape.java:224
A GraphUI GLMediaPlayer based TexSeqButton GraphShape.
void setSubtitlesEnabled(final boolean v)
Toggle enabling subtitle rendering.
void setSubtitleColor(final Vec4f color, final float blend)
Sets text/ASS subtitle colors.
MediaButton addDefaultEventListener()
Add the default GLMediaEventListener to this class's GLMediaPlayer.
MediaButton setVerbose(final boolean v)
void setSubtitleParams(final Font subFont, final float subLineHeightPct, final float subLineDY, final Alignment subAlignment)
Sets text/ASS subtitle parameter, enabling subtitle rendering.
Vec4f getARatioLetterboxBackColor()
See TextureSequence#getARatioLetterboxBackColor().
TexSeqButton setARatioLetterbox(final boolean v, final Vec4f backColor)
See TextureSequence#setARatioLetterbox(boolean, Vec4f).
Media player Widget, embedding a MediaButton and its controls.
static String getInfo(final int ptsMS, final int durationMS, final GLMediaPlayer mPlayer, final boolean full)
void setSubtitlesEnabled(final boolean v)
Toggle enabling subtitle rendering.
static String getInfo(final long currentMillis, final GLMediaPlayer mPlayer, final boolean full)
MediaPlayer(final int renderModes, final Scene scene, final GLMediaPlayer mPlayer, final Uri medium, final float aratio, final boolean letterBox, final float zoomSize, final boolean enableStills, final List< Shape > customCtrls)
Constructs a MediaPlayer, i.e.
static String getMultilineTime(final long currentMillis, final GLMediaPlayer mPlayer)
static String getMultilineTime(final int ptsMS, final int durationMS)
void setSubtitleParams(final Font subFont, final float subLineHeightPct, final float subLineDY, final Alignment subAlignment)
Sets text/ASS subtitle parameter, enabling subtitle rendering.
void setSubtitleColor(final Vec4f color, final float blend)
Sets text/ASS subtitle colors.
RangeSlider Widget either utilizing a simple positional round knob or a rectangular page-sized knob.
Shape setActiveKnobColorMod(final Vec4f c)
Sets the knob active modulation color.
A widget specifies specific UI semantics including individual controls.
Definition: Widget.java:44
Basic Float math utility functions.
Definition: FloatUtil.java:83
static boolean isZero(final float a, final float epsilon)
Returns true if value is zero, i.e.
2D Vector based upon two float components.
Definition: Vec2f.java:37
4D Vector based upon four float components.
Definition: Vec4f.java:37
Interface wrapper for font implementation.
Definition: Font.java:60
static final int GL_LINEAR
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_LINEAR" with expressi...
Definition: GL.java:323
GLMediaPlayer interface specifies a TextureSequence state machine using a multiplexed audio/video str...
int getVID()
Return the video stream id, see audio and video Stream IDs.
float getAudioVolume()
Returns the audio volume.
int getDuration()
Return total duration of stream in msec.
int getWidth()
Returns the width of the video.
boolean setAudioVolume(float v)
Sets the audio volume, [0f..1f].
CodecID getAudioCodecID()
Warning: Optional information, may not be supported by implementation.
float getPlaySpeed()
Returns the playback speed.
String getTitle()
Returns title meta-data from stream, available after State#Initialized is reached after issuing playS...
void setTextureUnit(int u)
Sets the texture unit.
CodecID getSubtitleCodecID()
Warning: Optional information, may not be supported by implementation.
void setTextureMinMagFilter(int[] minMagFilter)
Sets the texture min-mag filter, defaults to GL#GL_NEAREST.
String getLang(int id)
Return the matching language code of given stream ID, matching one of the stream IDs in getVStreams()...
int getSID()
Return the subtitle stream id, see audio and video Stream IDs.
int getHeight()
Returns the height of the video.
void addEventListener(GLMediaEventListener l)
Adds a GLMediaEventListener to this player.
int getAID()
Return the audio stream id, see audio and video Stream IDs.
String[] getSLangs()
Return an array of detected subtitle stream language codes, matching getSStreams() array and its indi...
int getAudioBitrate()
Warning: Optional information, may not be supported by implementation.
long getStreamBitrate()
Warning: Optional information, may not be supported by implementation.
float getFramerate()
Warning: Optional information, may not be supported by implementation.
int[] getAStreams()
Return an array of detected audio stream IDs.
CodecID getVideoCodecID()
Warning: Optional information, may not be supported by implementation.
String getVideoCodec()
Warning: Optional information, may not be supported by implementation.
String getSubtitleCodec()
Warning: Optional information, may not be supported by implementation.
String[] getALangs()
Return an array of detected audio stream language codes, matching getAStreams() array and its indices...
Chapter getChapter(int msec)
Returns Chapter covering given time position in milliseconds or null if none covers given time.
int[] getSStreams()
Return an array of detected subtitle stream IDs.
int getVideoBitrate()
Warning: Optional information, may not be supported by implementation.
PTS getPTS()
Returns current System Clock Reference (SCR) presentation timestamp (PTS).
String getAudioCodec()
Warning: Optional information, may not be supported by implementation.