001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.markup.html.media;
018
019import org.apache.wicket.markup.ComponentTag;
020import org.apache.wicket.markup.html.WebMarkupContainer;
021import org.apache.wicket.model.IModel;
022import org.apache.wicket.request.Url;
023import org.apache.wicket.request.cycle.RequestCycle;
024import org.apache.wicket.request.mapper.parameter.PageParameters;
025import org.apache.wicket.request.mapper.parameter.PageParametersEncoder;
026import org.apache.wicket.request.resource.ResourceReference;
027
028/**
029 * The media component is used to provide basic functionality to the video and audio component.
030 *
031 * @author Tobias Soloschenko
032 * @author Andrew Lombardi
033 * @since 7.0.0
034 */
035public abstract class MediaComponent extends WebMarkupContainer
036{
037        private static final long serialVersionUID = 1L;
038
039        /**
040         * To be used for the <em>crossorigin</em> attribute
041         *
042         * @see #setCrossOrigin(Cors)
043         */
044        public enum Cors {
045                /**
046                 * no authentication required
047                 */
048                ANONYMOUS("anonymous"),
049                /**
050                 * user credentials required
051                 */
052                USER_CREDENTIALS("user-credentials"),
053                /**
054                 * no cross origin
055                 */
056                NO_CORS("");
057
058                private final String realName;
059
060                private Cors(String realName)
061                {
062                        this.realName = realName;
063                }
064
065                /**
066                 * Gets the real name for the cors option
067                 * 
068                 * @return the real name
069                 */
070                public String getRealName()
071                {
072                        return realName;
073                }
074        }
075
076        /**
077         * To be used for the <em>preload</em> attribute
078         *
079         * @see #setPreload(Preload)
080         */
081        public enum Preload {
082                /**
083                 * preloads nothing
084                 */
085                NONE("none"),
086                /**
087                 * preloads only meta data like first picture, etc.
088                 */
089                METADATA("metadata"),
090                /**
091                 * auto detection what is going to be preload
092                 */
093                AUTO("auto");
094
095                private final String realName;
096
097                Preload(String realname)
098                {
099                        realName = realname;
100                }
101
102                /**
103                 * Gets the real name for the preload option
104                 * 
105                 * @return the real name
106                 */
107                public String getRealName()
108                {
109                        return realName;
110                }
111        }
112
113        private boolean autoplay;
114
115        private boolean loop;
116
117        private boolean muted;
118
119        private boolean controls = true;
120
121        private Preload preload;
122
123        private String startTime;
124
125        private String endTime;
126
127        private String mediaGroup;
128
129        private Cors crossOrigin;
130
131        private String type;
132
133        private PageParameters pageParameters;
134
135        private final ResourceReference resourceReference;
136
137        private final String url;
138
139        /**
140         * Constructor.
141         *
142         * @param id
143         *            The component id
144         */
145        public MediaComponent(String id)
146        {
147                this(id, null, null, null, null);
148        }
149
150        /**
151         * Constructor.
152         *
153         * @param id
154         *            The component id
155         * @param model
156         *            The component model
157         */
158        public MediaComponent(String id, IModel<?> model)
159        {
160                this(id, model, null, null, null);
161        }
162
163        /**
164         * Creates a media component
165         *
166         * @param id
167         *            The component id
168         * @param resourceReference
169         *            the resource reference of the media file
170         */
171        public MediaComponent(String id, ResourceReference resourceReference)
172        {
173                this(id, null, null, null, resourceReference);
174        }
175
176        /**
177         * Creates a media component
178         *
179         * @param id
180         *            The component id
181         * @param model
182         *            the internally used model
183         * @param resourceReference
184         *            the resource reference of the media file
185         */
186        public MediaComponent(String id, IModel<?> model, ResourceReference resourceReference)
187        {
188                this(id, model, null, null, resourceReference);
189        }
190
191        /**
192         * Creates a media component
193         *
194         * @param id
195         *            The component id
196         * @param resourceReference
197         *            the resource reference of the media file
198         * @param pageParameters
199         *            the page parameters to be used to be prepended to the media URL
200         */
201        public MediaComponent(String id, ResourceReference resourceReference,
202                PageParameters pageParameters)
203        {
204                this(id, null, null, pageParameters, resourceReference);
205        }
206
207        /**
208         * Creates a media component
209         *
210         * @param id
211         *            The component id
212         * @param model
213         *            the internally used model
214         * @param resourceReference
215         *            the resource reference of the media file
216         * @param pageParameters
217         *            the page parameters to be used to be prepended to the media URL
218         */
219        public MediaComponent(String id, IModel<?> model, ResourceReference resourceReference,
220                PageParameters pageParameters)
221        {
222                this(id, model, null, pageParameters, resourceReference);
223        }
224
225        /**
226         * Creates a media component
227         *
228         * @param id
229         *            The component id
230         * @param url
231         *            an external URL to be used for the media component
232         */
233        public MediaComponent(String id, String url)
234        {
235                this(id, null, url, null, null);
236        }
237
238        /**
239         * Creates a media component
240         *
241         * @param id
242         *            The component id
243         * @param model
244         *            the internally used model
245         * @param url
246         *            an external URL to be used for the media component
247         */
248        public MediaComponent(String id, IModel<?> model, String url)
249        {
250                this(id, model, url, null, null);
251        }
252
253        /**
254         * Creates a media component
255         *
256         * @param id
257         *            The component id
258         * @param model
259         *            the internally used model
260         * @param url
261         *            an external URL to be used for the media component
262         * @param pageParameters
263         *            the page parameters to be used to be prepended to the media URL
264         */
265        public MediaComponent(String id, IModel<?> model, String url, PageParameters pageParameters)
266        {
267                this(id, model, url, pageParameters, null);
268        }
269
270        private MediaComponent(String id, IModel<?> model, String url, PageParameters pageParameters,
271                ResourceReference resourceReference)
272        {
273                super(id, model);
274                this.url = url;
275                this.pageParameters = pageParameters;
276                this.resourceReference = resourceReference;
277        }
278
279        @Override
280        protected void onComponentTag(ComponentTag tag)
281        {
282                super.onComponentTag(tag);
283
284                // The time management is used to set the start / stop
285                // time in seconds of the movie to be played back
286                String timeManagement = "";
287                if (startTime != null)
288                {
289                        timeManagement += "#t=" + startTime + (endTime != null ? "," + endTime : "");
290                }
291
292                if (resourceReference != null)
293                {
294                        CharSequence urlToMediaReference = RequestCycle.get().urlFor(resourceReference,
295                                pageParameters);
296                        tag.put("src", urlToMediaReference + timeManagement);
297                }
298                else if (url != null)
299                {
300                        Url encoded = new PageParametersEncoder().encodePageParameters(pageParameters);
301                        String queryString = encoded.getQueryString();
302                        tag.put("src", url + (queryString != null ? "?" + queryString : "") + timeManagement);
303                }
304
305                String mg = getMediaGroup();
306                if (mg != null)
307                {
308                        tag.put("mediagroup", mg);
309                }
310
311                if (isAutoplay())
312                {
313                        tag.put("autoplay", "autoplay");
314                }
315
316                if (isLooping())
317                {
318                        tag.put("loop", "loop");
319                }
320
321                if (isMuted())
322                {
323                        tag.put("muted", "muted");
324                }
325
326                if (hasControls())
327                {
328                        tag.put("controls", "controls");
329                }
330
331                Preload _preload = getPreload();
332                if (_preload != null)
333                {
334                        tag.put("preload", _preload.getRealName());
335                }
336
337                Cors cors = getCrossOrigin();
338                if (cors != null)
339                {
340                        tag.put("crossorigin", cors.getRealName());
341                }
342
343                String type = getType();
344                if (type != null)
345                {
346                        tag.put("type", type);
347                }
348        }
349
350        /**
351         * If the playback is autoplayed on load
352         *
353         * @return If the playback is autoplayed on load
354         */
355        public boolean isAutoplay()
356        {
357                return autoplay;
358        }
359
360        /**
361         * Sets the playback to be autoplayed on load
362         *
363         * @param autoplay
364         *            If the playback is autoplayed on load
365         */
366        public void setAutoplay(boolean autoplay)
367        {
368                this.autoplay = autoplay;
369        }
370
371        /**
372         * If the playback is looped
373         *
374         * @return If the playback is looped
375         */
376        public boolean isLooping()
377        {
378                return loop;
379        }
380
381        /**
382         * Sets the playback to be looped
383         *
384         * @param loop
385         *            If the playback is looped
386         */
387        public void setLooping(boolean loop)
388        {
389                this.loop = loop;
390        }
391
392        /**
393         * Gets the page parameter applied to the URL of the media component
394         * 
395         * @return the page parameter applied to the URL of the media component
396         */
397        public PageParameters getPageParameters()
398        {
399                return pageParameters;
400        }
401
402        /**
403         * Sets the page parameter applied to the URL of the media component
404         * 
405         * @param pageParameters
406         *            the page parameter which are going to be applied to the URL of the media component
407         */
408        public void setPageParameters(PageParameters pageParameters)
409        {
410                this.pageParameters = pageParameters;
411        }
412
413        /**
414         * If the playback is muted initially
415         *
416         * @return If the playback is muted initially
417         */
418        public boolean isMuted()
419        {
420                return muted;
421        }
422
423        /**
424         * Sets the playback muted initially
425         *
426         * @param muted
427         *            If the playback is muted initially
428         */
429        public void setMuted(boolean muted)
430        {
431                this.muted = muted;
432        }
433
434        /**
435         * If the controls are going to be displayed
436         *
437         * @return if the controls are going to displayed
438         */
439        public boolean hasControls()
440        {
441                return controls;
442        }
443
444        /**
445         * Sets if the controls are going to be displayed
446         *
447         * @param controls
448         *            if the controls are going to displayed
449         */
450        public void setControls(Boolean controls)
451        {
452                this.controls = controls;
453        }
454
455        /**
456         * The type of preload
457         *
458         * @see #setPreload(Preload)
459         *
460         * @return the preload
461         */
462        public Preload getPreload()
463        {
464                return preload;
465        }
466
467        /**
468         * Sets the type of preload.
469         * <ul>
470         * <li><b>none</b>: Hints to the user agent that either the author does not expect the user to
471         * need the media resource, or that the server wants to minimise unnecessary traffic.</li>
472         *
473         * <li><b>metadata</b>: Hints to the user agent that the author does not expect the user to need
474         * the media resource, but that fetching the resource metadata (dimensions, first frame, track
475         * list, duration, etc) is reasonable.</li>
476         *
477         * <li><b>auto</b>: Hints to the user agent that the user agent can put the user's needs first
478         * without risk to the server, up to and including optimistically downloading the entire
479         * resource.</li>
480         * </ul>
481         * </p>
482         *
483         * @param preload
484         *            the type of the preload
485         */
486        public void setPreload(Preload preload)
487        {
488                this.preload = preload;
489        }
490
491        /**
492         * Gets the position at which the media component starts the playback
493         *
494         * @see #setStartTime(String)
495         *
496         * @return the time at which position the media component starts the playback
497         */
498        public String getStartTime()
499        {
500                return startTime;
501        }
502
503        /**
504         * Sets the position at which the media component starts the playback<br>
505         * <br>
506         * t=<b>10</b>,20<br>
507         * t=<b>npt:10</b>,20<br>
508         * <br>
509         *
510         * t=<b>120s</b>,121.5s<br>
511         * t=<b>npt:120</b>,0:02:01.5<br>
512         * <br>
513         *
514         * t=<b>smpte-30:0:02:00</b>,0:02:01:15<br>
515         * t=<b>smpte-25:0:02:00:00</b>,0:02:01:12.1<br>
516         * <br>
517         *
518         * t=<b>clock:20090726T111901Z</b>,20090726T121901Z
519         *
520         * @param startTime
521         *            the time at which position the media component starts the playback
522         */
523        public void setStartTime(String startTime)
524        {
525                this.startTime = startTime;
526        }
527
528        /**
529         * Gets the position at which the media component stops the playback
530         *
531         * @see #setEndTime(String)
532         *
533         * @return the time at which position the media component stops the playback
534         */
535        public String getEndTime()
536        {
537                return endTime;
538        }
539
540        /**
541         * Sets the position at which the media component stops the playback<br>
542         * <br>
543         * t=10,<b>20</b><br>
544         * t=npt:10,<b>20</b><br>
545         * <br>
546         *
547         * t=120s,<b>121.5s</b><br>
548         * t=npt:120,<b>0:02:01.5</b><br>
549         * <br>
550         *
551         * t=smpte-30:0:02:00,<b>0:02:01:15</b><br>
552         * t=smpte-25:0:02:00:00,<b>0:02:01:12.1</b><br>
553         * <br>
554         *
555         * t=clock:20090726T111901Z,<b>20090726T121901Z</b>
556         *
557         * @param endTime
558         *            the time at which position the media component stops the playback
559         */
560        public void setEndTime(String endTime)
561        {
562                this.endTime = endTime;
563        }
564
565        /**
566         * Gets the media group.
567         *
568         * @return the media group
569         */
570        public String getMediaGroup()
571        {
572                return mediaGroup;
573        }
574
575        /**
576         * Sets the media group
577         *
578         * @param mediaGroup
579         *            to be set
580         */
581        public void setMediaGroup(String mediaGroup)
582        {
583                this.mediaGroup = mediaGroup;
584        }
585
586        /**
587         * Gets the cross origin settings
588         *
589         * @see #setCrossOrigin(Cors)
590         *
591         * @return the cross origins settings
592         */
593        public Cors getCrossOrigin()
594        {
595                return crossOrigin;
596        }
597
598        /**
599         * Sets the cross origin settings<br>
600         * <br>
601         *
602         * <b>ANONYMOUS</b>: Cross-origin CORS requests for the element will not have the credentials
603         * flag set.<br>
604         * <br>
605         * <b>USER_CREDENTIALS</b>: Cross-origin CORS requests for the element will have the credentials
606         * flag set.<br>
607         * <br>
608         * <b>NO_CORS</b>: The empty string is also a valid keyword, and maps to the Anonymous state.
609         * The attribute's invalid value default is the Anonymous state. The missing value default, used
610         * when the attribute is omitted, is the No CORS state
611         *
612         * @param crossOrigin
613         *            the cross origins settings to set
614         */
615        public void setCrossOrigin(Cors crossOrigin)
616        {
617                this.crossOrigin = crossOrigin;
618        }
619
620        /**
621         * Gets the type
622         *
623         * @see #setType(String)
624         *
625         * @return the type of this media element
626         */
627        public String getType()
628        {
629                return type;
630        }
631
632        /**
633         * Sets the type<br>
634         * <br>
635         *
636         * * The following list shows some examples of how to use the codecs= MIME parameter in the type
637         * attribute.<br>
638         * <br>
639         *
640         * H.264 Constrained baseline profile video (main and extended video compatible) level 3 and
641         * Low-Complexity AAC audio in MP4 container<br>
642         * &lt;source src='video.mp4' <b>type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'</b>&gt;<br>
643         * H.264 Extended profile video (baseline-compatible) level 3 and Low-Complexity AAC audio in
644         * MP4 container<br>
645         * &lt;source src='video.mp4' <b>type='video/mp4; codecs="avc1.58A01E, mp4a.40.2"'</b>&gt;<br>
646         * H.264 Main profile video level 3 and Low-Complexity AAC audio in MP4 container<br>
647         * &lt;source src='video.mp4' <b>type='video/mp4; codecs="avc1.4D401E, mp4a.40.2"'</b>&gt;<br>
648         * H.264 'High' profile video (incompatible with main, baseline, or extended profiles) level 3
649         * and Low-Complexity AAC audio in MP4 container<br>
650         * &lt;source src='video.mp4' <b>type='video/mp4; codecs="avc1.64001E, mp4a.40.2"'</b>&gt;<br>
651         * MPEG-4 Visual Simple Profile Level 0 video and Low-Complexity AAC audio in MP4 container<br>
652         * &lt;source src='video.mp4' <b>type='video/mp4; codecs="mp4v.20.8, mp4a.40.2"'</b>&gt;<br>
653         * MPEG-4 Advanced Simple Profile Level 0 video and Low-Complexity AAC audio in MP4 container
654         * <br>
655         * &lt;source src='video.mp4' <b>type='video/mp4; codecs="mp4v.20.240, mp4a.40.2"'</b>&gt;<br>
656         * MPEG-4 Visual Simple Profile Level 0 video and AMR audio in 3GPP container<br>
657         * &lt;source src='video.3gp' <b>type='video/3gpp; codecs="mp4v.20.8, samr"'</b>&gt;<br>
658         * Theora video and Vorbis audio in Ogg container<br>
659         * &lt;source src='video.ogv' <b>type='video/ogg; codecs="theora, vorbis"'</b>&gt;<br>
660         * Theora video and Speex audio in Ogg container<br>
661         * &lt;source src='video.ogv' <b>type='video/ogg; codecs="theora, speex"'</b>&gt;<br>
662         * Vorbis audio alone in Ogg container<br>
663         * &lt;source src='audio.ogg' <b>type='audio/ogg; codecs=vorbis'</b>&gt;<br>
664         * Speex audio alone in Ogg container<br>
665         * &lt;source src='audio.spx' <b>type='audio/ogg; codecs=speex'</b>&gt;<br>
666         * FLAC audio alone in Ogg container<br>
667         * &lt;source src='audio.oga' <b>type='audio/ogg; codecs=flac'</b>&gt;<br>
668         * Dirac video and Vorbis audio in Ogg container<br>
669         * &lt;source src='video.ogv' <b>type='video/ogg; codecs="dirac, vorbis"'</b>&gt;<br>
670         * Theora video and Vorbis audio in Matroska container<br>
671         * &lt;source src='video.mkv' <b>type='video/x-matroska; codecs="theora, vorbis"'</b>&gt;<br>
672         *
673         * @param type
674         *            the type of this media element
675         */
676        public void setType(String type)
677        {
678                this.type = type;
679        }
680}