1 // This may look like C code, but it's really -*- C++ -*- 2 /* 3 * Copyright (C) 2011 Emweb bv, Herent, Belgium. 4 * 5 * See the LICENSE file for terms of use. 6 */ 7 #ifndef WMEDIA_PLAYER_H_ 8 #define WMEDIA_PLAYER_H_ 9 10 #include <Wt/WCompositeWidget.h> 11 #include <Wt/WJavaScript.h> 12 #include <Wt/WLink.h> 13 #include <Wt/WString.h> 14 15 namespace Wt { 16 17 /*! \brief An enumeration for a media encoding. 18 * 19 * \sa addSource() 20 * \sa http://www.jplayer.org/latest/developer-guide/#jPlayer-media-encoding 21 */ 22 enum class MediaEncoding { 23 PosterImage, //!< The poster image (e.g. JPG/PNG) for a video 24 25 MP3, //!< Audio: MP3 encoding (<b>essential audio</b> format) 26 M4A, //!< Audio: MP4 encoding (<b>essential audio</b> format) 27 OGA, //!< Audio: OGG encoding 28 WAV, //!< Audio: WAV (uncompressed) format 29 WEBMA, //!< Audio: WebM encoding 30 FLA, //!< Audio: Flash format 31 32 M4V, //!< Video: MP4 encoding (<b>essential video</b> format) 33 OGV, //!< Video: OGG encoding 34 WEBMV, //!< Video: WebM encoding 35 FLV //!< Video: Flash format 36 }; 37 38 /*! \brief An enumeration for a media type. 39 */ 40 enum class MediaType { 41 Audio, //!< Defines an audio player 42 Video //!< Defines a video player 43 }; 44 45 /*! \brief An enumeration for a button function. 46 * 47 * \sa setButton(), button() 48 */ 49 enum class MediaPlayerButtonId { 50 VideoPlay, //!< Play button which overlays the video (for Video only) 51 Play, //!< Play button, is hidden while playing 52 Pause, //!< Pause button, is hidden while paused 53 Stop, //!< Stop button 54 VolumeMute, //!< Volume mute button 55 VolumeUnmute, //!< Volume unmute button 56 VolumeMax, //!< Volume max button 57 58 /*! Toggle button for full screen, is hidden while full screen 59 * (for Video only) */ 60 FullScreen, 61 /*! Toggle button to restore the screen, is shown only in full screen 62 * (for Video only) */ 63 RestoreScreen, 64 65 /*! Toggle button to enable looping, is hidden while repeating is on */ 66 RepeatOn, 67 /*! Toggle button to disable looping, is hidden while repeat is off */ 68 RepeatOff 69 }; 70 71 /*! \brief An enumeration for a progressbar function. 72 * 73 * \sa setProgressBar(), progressBar() 74 */ 75 enum class MediaPlayerProgressBarId { 76 Time, //!< The time bar 77 Volume //!< The volume bar 78 }; 79 80 /*! \brief An enumeration for a text. 81 * 82 * \sa setText(), text() 83 */ 84 enum class MediaPlayerTextId { 85 CurrentTime, //!< Displays the current time 86 Duration, //!< Displays the total duration 87 Title //!< Displays the title set in setTitle() 88 }; 89 90 /*! \class WMediaPlayer Wt/WMediaPlayer.h Wt/WMediaPlayer.h 91 * \brief A media player 92 * 93 * This widget implements a media player, suitable to play video or 94 * audio, and with a customizable user-interface. 95 * 96 * To support cross-browser playing of video or audio content, you may 97 * need to provide the contents appropriately encoded. For audio, at 98 * least an MP3 or MP4 audio (M4A) encoding should be supplied, while 99 * for video the M4V encoding should be provided. Additional encodings 100 * are beneficial since they increase the chance that native HTML 101 * <tt><video></tt> or <tt><audio></tt> elements can be 102 * used (which may be hardware accelerated), instead of the flash 103 * player. See <a 104 * href="http://www.jplayer.org/latest/developer-guide/#reference-html5-media"> 105 * HTML5 browser media support</a>. 106 * 107 * You need to specify the encoding types you are going to use when 108 * instantiating the media player, since based on the chosen 109 * encodings, a particular suitable implementation will be used. Thus, 110 * you need to call addSource() immediately, but you may pass empty 111 * URLs if you do not yet want to load media. 112 * 113 * The player provides a user-interface to control the playback which 114 * may be freely customized, and which is independent of the 115 * underlying media technology (HTML video or Flash player). The 116 * controls user-interface may be implemented as a %Wt widget, where 117 * the controls (buttons, progress bars, and text widgets) are bound 118 * directly to the video player component (client-side). 119 * 120 * This widget relies on a third-party JavaScript component <a 121 * href="http://www.jplayer.org/">jPlayer</a>, which is 122 * distributed together with %Wt. 123 * 124 * The default user-interface can be themed using jPlayer themes. The 125 * theme is global (it applies to all media player instances), and is 126 * configured by loading a CSS stylesheet. 127 * 128 * The following code creates a video using the default controls: 129 * \if cpp 130 * \code 131 * useStyleSheet(WApplication::relativeResourcesUrl() + "jPlayer/skin/jplayer.blue.monday.css"); 132 * 133 * auto player = std::make_unique<WMediaPlayer>(MediaType::Video); 134 * 135 * player->addSource(MediaEncoding::M4V, "video.m4v"); 136 * player->addSource(MediaEncoding::OGV, "video.ogv"); 137 * player->addSource(MediaEncoding::PosterImage, "poster.png"); 138 * 139 * \endcode 140 * \elseif java 141 * \code 142 * ... 143 * \endcode 144 * \endif 145 * 146 * Alternatively, a custom widget may be set which implements the 147 * controls, using setControlsWidget(). In this case, you should add 148 * to this widget the buttons, text place holders, and progress bars 149 * and bind them to the media player using the setButton(), setText() 150 * and setProgressBar() methods. The controls widget is integrated in 151 * the media player, and this has as unique benefit (for a video 152 * player) that they may also be shown when the video player is 153 * maximized. 154 * 155 * Finally, you may want to control the media player only through 156 * widgets external to the media player. This may be configured by 157 * setting \c 0 as controlsWidget. In this case however, full screen 158 * mode should not be used since there is no way to restore the 159 * original size. 160 */ 161 class WT_API WMediaPlayer : public WCompositeWidget 162 { 163 public: 164 /*! \brief Typedef for enum Wt::MediaEncoding */ 165 typedef MediaEncoding Encoding; 166 /*! \brief Typedef for enum Wt::MediaType */ 167 typedef MediaType Type; 168 /*! \brief Typedef for enum Wt::MediaPlayerButtonId */ 169 typedef MediaPlayerButtonId ButtonId; 170 /*! \brief Typedef for enum Wt::MediaPlayerProgressBarId */ 171 typedef MediaPlayerProgressBarId ProgressBarId; 172 /*! \brief Typedef for enum Wt::MediaPlayerTextId */ 173 typedef MediaPlayerTextId TextId; 174 175 /*! \brief Creates a new media player. 176 * 177 * The player is instantiated with default controls. 178 * 179 * \sa setControlsWidget() 180 */ 181 WMediaPlayer(MediaType mediaType); 182 183 /*! \brief Destructor. 184 */ 185 ~WMediaPlayer(); 186 187 /*! \brief Sets the video size. 188 * 189 * This sets the size for the video. The actual size of the media 190 * player may be slightly larger, if the controlWidget take 191 * additional space (i.e. is not overlayed on top of the video). 192 * 193 * CSS Themes for the default jPlayer controls support two formats 194 * (480 x 270 and 640 x 360). 195 * 196 * The default video size is 480 x 270. 197 */ 198 void setVideoSize(int width, int height); 199 200 /*! \brief Returns the video width. 201 * 202 * \sa setVideoSize() 203 */ videoWidth()204 int videoWidth() const { return videoWidth_; } 205 206 /*! \brief Returns the video height. 207 * 208 * \sa setVideoSize() 209 */ videoHeight()210 int videoHeight() const { return videoHeight_; } 211 212 /*! \brief Sets the user-interface controls widget. 213 * 214 * This sets a widget that contains the controls (buttons, text 215 * widgets, etc...) to allow the user to control the player. 216 * 217 * Widgets that implement the buttons, bars, and text holders should 218 * be bound to the player using setButton(), setText() and 219 * setProgressBar() calls. 220 * 221 * Setting a \c 0 widget will result in a player without 222 * controls. For an audio player this has the effect of being 223 * entirely invisible. 224 * 225 * The default controls widget is a widget that can be styled using 226 * a jPlayer CSS theme. 227 */ 228 void setControlsWidget(std::unique_ptr<WWidget> controls); 229 230 /*! \brief Returns the user-interface controls widget. 231 * 232 * \sa setControlsWidget() 233 */ 234 WWidget *controlsWidget() const; 235 236 /*! \brief Sets the media title. 237 * 238 * \sa MediaPlayerTextId::Title 239 */ 240 void setTitle(const WString& title); 241 242 /*! \brief Adds a source. 243 * 244 * Adds a media source. The source may be specified as a URL or as a 245 * dynamic resource. 246 * 247 * You may pass a null \p link if you want to indicate the media types 248 * you will use (later) without already loading data. 249 */ 250 void addSource(MediaEncoding encoding, const WLink& link); 251 252 /*! \brief Returns a source. 253 * 254 * Returns the media source for the given \p encoding, which must have 255 * previously been added using addSource(). 256 */ 257 WLink getSource(MediaEncoding encoding) const; 258 259 /*! \brief Clears all sources. 260 * 261 * \sa addSource() 262 */ 263 void clearSources(); 264 265 /*! \brief Binds a control button. 266 * 267 * A control button is typically implemented as a WAnchor or a 268 * WPushButton (although any WInteractWidget can work). 269 * 270 * You should use this method in conjunction with 271 * setControlsWidget() to bind buttons in a custom control interface 272 * to media player functions. 273 * 274 * The default control widget implements all buttons using a 275 * WAnchor. 276 */ 277 void setButton(MediaPlayerButtonId id, WInteractWidget *btn); 278 279 /*! \brief Returns a control button. 280 * 281 * \sa setButton() 282 */ 283 WInteractWidget *button(MediaPlayerButtonId id) const; 284 285 /*! \brief Binds a control progress bar. 286 * 287 * The progress bar for the MediaPlayerProgressBarId::Time 288 * indication should be contained in a WContainerWidget which bounds 289 * the width of the progress bar, rather than setting a width on the 290 * progress bar. This is because the progress bar may, in some 291 * cases, also be used to indicate which part of the media can be 292 * seeked, and for this its width is being manipulated. 293 * 294 * You should use this method in conjunction with 295 * setControlsWidget() to bind progress bars in a custom control 296 * interface to media player functions. 297 */ 298 void setProgressBar(MediaPlayerProgressBarId id, WProgressBar *progressBar); 299 300 /*! \brief Returns a control progress bar. 301 * 302 * \sa setProgressBar() 303 */ 304 WProgressBar *progressBar(MediaPlayerProgressBarId id) const; 305 306 /*! \brief Sets a text place-holder widget. 307 * 308 * This binds the widget that displays text such as current time and 309 * total duration of the loaded media. 310 * 311 * You should use this method in conjunction with 312 * setControlsWidget() to bind progress bars in a custom control 313 * interface to media player functions. 314 */ 315 void setText(MediaPlayerTextId id, WText *text); 316 317 /*! \brief Returns a text place-holder widget. 318 * 319 * \sa setText() 320 */ 321 WText *text(MediaPlayerTextId id) const; 322 323 /*! \brief Pauses the player. 324 * 325 * \sa play() 326 */ 327 void pause(); 328 329 /*! \brief Start or resume playing. 330 * 331 * The player starts or resumes playing at the current time. 332 * 333 * \sa seek() 334 */ 335 void play(); 336 337 /*! \brief Stops the player. 338 * 339 * \sa play() 340 */ 341 void stop(); 342 343 /*! \brief Seeks to a time. 344 * 345 * If possible, the player sets the current time to the indicated \p time 346 * (expressed in seconds). 347 * 348 * \note It may be the case that this only works after the player has 349 * already loaded the media. 350 */ 351 void seek(double time); 352 353 /*! \brief Sets the playback rate. 354 * 355 * This modifies the playback rate, expressed as a ratio of the 356 * normal (natural) playback rate. 357 * 358 * The default value is 1.0 359 * 360 * \note Not all browsers support this function. 361 */ 362 void setPlaybackRate(double rate); 363 364 /*! \brief Sets the volume. 365 * 366 * This modifies the volume, which must be a number between 0 and 1.0. 367 * 368 * The default value is 0.8 369 */ 370 void setVolume(double volume); 371 372 /*! \brief Returns the volume. 373 * 374 * \sa setVolume() 375 */ 376 double volume() const; 377 378 /*! \brief Mutes or unmutes the playback volume. 379 * 380 * \sa setVolume() 381 */ 382 void mute(bool mute); 383 384 /*! \brief Returns whether the media is currently playing. 385 * 386 * \sa play() 387 */ playing()388 bool playing() const { return status_.playing; } 389 390 /*! \brief Returns the current player state. 391 * 392 * The state reflects in how far the media player has loaded the 393 * media, and has determined its characteristics. 394 * 395 */ readyState()396 MediaReadyState readyState() const { return status_.readyState; } 397 398 /*! \brief Returns the duration. 399 * 400 * The duration may be reported as 0 if the player has not yet loaded 401 * the media to determine the duration. Otherwise the duration is the 402 * duration of the loaded media, expressed in seconds. 403 * 404 * \sa readyState(), currentTime() 405 */ duration()406 double duration() const { return status_.duration; } 407 408 /*! \brief Returns the current playback time. 409 * 410 * Returns the current playback time, expressed in seconds. 411 * 412 * \sa seek() 413 */ currentTime()414 double currentTime() const { return status_.currentTime; } 415 416 /*! \brief Returns the current playback rate. 417 * 418 * \sa setPlaybackRate() 419 */ playbackRate()420 double playbackRate() const { return status_.playbackRate; } 421 422 // This even seems kind of useless ? 423 // JSignal<>& loadStarted(); 424 425 /*! \brief Event that indicates a time update. 426 * 427 * The event indicates that the currentTime() has changed. 428 */ 429 JSignal<double>& timeUpdated(); 430 431 /*! \brief Event that indicates that playback started. 432 * 433 * The event is fired when playback has started (or is being 434 * continued). 435 */ 436 JSignal<>& playbackStarted(); 437 438 /*! \brief Event that indicates that playback paused. 439 * 440 * The event is fired when playback has been paused. 441 */ 442 JSignal<>& playbackPaused(); 443 444 /*! \brief Event that indicates that the video or audio has ended. 445 */ 446 JSignal<>& ended(); 447 448 /*! \brief Event that indicates that the volume has changed. 449 */ 450 JSignal<double>& volumeChanged(); 451 452 std::string jsPlayerRef() const; 453 454 virtual void refresh() override; 455 456 protected: 457 virtual void setFormData(const FormData& formData) override; 458 virtual void render(WFlags<RenderFlag> flags) override; 459 460 private: 461 struct Source { 462 MediaEncoding encoding; 463 WLink link; 464 }; 465 466 struct SignalDouble { 467 JSignal<double> *signal; 468 std::string jsExprA1; 469 }; 470 471 std::vector<JSignal<> *> signals_; 472 std::vector<SignalDouble> signalsDouble_; 473 474 MediaType mediaType_; 475 int videoWidth_, videoHeight_; 476 477 WString title_; 478 std::vector<Source> media_; 479 std::string initialJs_; 480 481 observing_ptr<WInteractWidget> control_[11]; 482 WText *display_[3]; 483 WProgressBar *progressBar_[2]; 484 485 observing_ptr<WWidget> gui_; 486 unsigned boundSignals_, boundSignalsDouble_; 487 488 bool mediaUpdated_; 489 490 struct State { 491 bool playing, ended; 492 MediaReadyState readyState; 493 double seekPercent, volume, duration, currentTime, playbackRate; 494 495 State(); 496 }; 497 498 State status_; 499 500 void createDefaultGui(); 501 502 void addAnchor(WTemplate *t, MediaPlayerButtonId id, const char *bindId, 503 const std::string& styleClass, 504 const std::string& altText = std::string()); 505 void addText(WTemplate *t, MediaPlayerTextId id, const char *bindId, 506 const std::string& styleClass); 507 void addProgressBar(WTemplate *t, MediaPlayerProgressBarId id, 508 const char *bindId, 509 const std::string& styleClass, 510 const std::string& valueStyleClass); 511 JSignal<>& signal(const char *name); 512 JSignal<double>& signalDouble(const char *name, const std::string& expr); 513 514 void updateProgressBarState(MediaPlayerProgressBarId id); 515 void updateFromProgressBar(MediaPlayerProgressBarId id, double value); 516 517 void playerDo(const std::string& method, 518 const std::string& args = std::string()); 519 void playerDoData(const std::string& method, const std::string& args); 520 void playerDoRaw(const std::string& jqueryMethod); 521 522 static const char *LOAD_STARTED_SIGNAL; 523 static const char *TIME_UPDATED_SIGNAL; 524 static const char *PLAYBACK_STARTED_SIGNAL; 525 static const char *PLAYBACK_PAUSED_SIGNAL; 526 static const char *ENDED_SIGNAL; 527 static const char *VOLUME_CHANGED_SIGNAL; 528 529 friend class WMediaPlayerImpl; 530 }; 531 532 } 533 534 #endif // WMEDIA_PLAYER_H_ 535 536