| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE examples.
 -    Copyright (c) 2022 - Raw Material Software Limited
 - 
 -    The code included in this file is provided under the terms of the ISC license
 -    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 -    To use, copy, modify, and/or distribute this software for any purpose with or
 -    without fee is hereby granted provided that the above copyright notice and
 -    this permission notice appear in all copies.
 - 
 -    THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 -    WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 -    PURPOSE, ARE DISCLAIMED.
 - 
 -   ==============================================================================
 - */
 - 
 - /*******************************************************************************
 -  The block below describes the properties of this PIP. A PIP is a short snippet
 -  of code that can be read by the Projucer and used to generate a JUCE project.
 - 
 -  BEGIN_JUCE_PIP_METADATA
 - 
 -  name:             VideoDemo
 -  version:          1.0.0
 -  vendor:           JUCE
 -  website:          http://juce.com
 -  description:      Plays video files.
 - 
 -  dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 -                    juce_gui_basics, juce_gui_extra, juce_video
 -  exporters:        xcode_mac, vs2022, androidstudio, xcode_iphone
 - 
 -  moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 - 
 -  type:             Component
 -  mainClass:        VideoDemo
 - 
 -  useLocalCopy:     1
 - 
 -  END_JUCE_PIP_METADATA
 - 
 - *******************************************************************************/
 - 
 - #pragma once
 - 
 - #include "../Assets/DemoUtilities.h"
 - 
 - #if JUCE_MAC || JUCE_WINDOWS
 - //==============================================================================
 - // so that we can easily have two video windows each with a file browser, wrap this up as a class..
 - class MovieComponentWithFileBrowser final : public Component,
 -                                             public DragAndDropTarget,
 -                                             private FilenameComponentListener
 - {
 - public:
 -     MovieComponentWithFileBrowser()
 -         : videoComp (true)
 -     {
 -         addAndMakeVisible (videoComp);
 - 
 -         addAndMakeVisible (fileChooser);
 -         fileChooser.addListener (this);
 -         fileChooser.setBrowseButtonText ("browse");
 -     }
 - 
 -     void setFile (const File& file)
 -     {
 -         fileChooser.setCurrentFile (file, true);
 -     }
 - 
 -     void paintOverChildren (Graphics& g) override
 -     {
 -         if (isDragOver)
 -         {
 -             g.setColour (Colours::red);
 -             g.drawRect (fileChooser.getBounds(), 2);
 -         }
 -     }
 - 
 -     void resized() override
 -     {
 -         videoComp.setBounds (getLocalBounds().reduced (10));
 -     }
 - 
 -     bool isInterestedInDragSource (const SourceDetails&) override   { return true; }
 - 
 -     void itemDragEnter (const SourceDetails&) override
 -     {
 -         isDragOver = true;
 -         repaint();
 -     }
 - 
 -     void itemDragExit (const SourceDetails&) override
 -     {
 -         isDragOver = false;
 -         repaint();
 -     }
 - 
 -     void itemDropped (const SourceDetails& dragSourceDetails) override
 -     {
 -         setFile (dragSourceDetails.description.toString());
 -         isDragOver = false;
 -         repaint();
 -     }
 - 
 - private:
 -     VideoComponent videoComp;
 - 
 -     bool isDragOver = false;
 -     FilenameComponent fileChooser  { "movie", {}, true, false, false, "*", {}, "(choose a video file to play)"};
 - 
 -     void filenameComponentChanged (FilenameComponent*) override
 -     {
 -         auto url = URL (fileChooser.getCurrentFile());
 - 
 -         // this is called when the user changes the filename in the file chooser box
 -         auto result = videoComp.load (url);
 -         videoLoadingFinished (url, result);
 -     }
 - 
 -     void videoLoadingFinished (const URL& url, Result result)
 -     {
 -         ignoreUnused (url);
 - 
 -         if (result.wasOk())
 -         {
 -             // loaded the file ok, so let's start it playing..
 - 
 -             videoComp.play();
 -             resized(); // update to reflect the video's aspect ratio
 -         }
 -         else
 -         {
 -             auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon,
 -                                                              "Couldn't load the file!",
 -                                                              result.getErrorMessage());
 -             messageBox = AlertWindow::showScopedAsync (options, nullptr);
 -         }
 -     }
 - 
 -     ScopedMessageBox messageBox;
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MovieComponentWithFileBrowser)
 - };
 - 
 - //==============================================================================
 - class VideoDemo final : public Component,
 -                         public DragAndDropContainer,
 -                         private FileBrowserListener
 - {
 - public:
 -     VideoDemo()
 -     {
 -         setOpaque (true);
 - 
 -         movieList.setDirectory (File::getSpecialLocation (File::userMoviesDirectory), true, true);
 -         directoryThread.startThread (Thread::Priority::background);
 - 
 -         fileTree.setTitle ("Files");
 -         fileTree.addListener (this);
 -         fileTree.setColour (FileTreeComponent::backgroundColourId, Colours::lightgrey.withAlpha (0.6f));
 -         addAndMakeVisible (fileTree);
 - 
 -         addAndMakeVisible (resizerBar);
 - 
 -         loadLeftButton .onClick = [this] { movieCompLeft .setFile (fileTree.getSelectedFile (0)); };
 -         loadRightButton.onClick = [this] { movieCompRight.setFile (fileTree.getSelectedFile (0)); };
 - 
 -         addAndMakeVisible (loadLeftButton);
 -         addAndMakeVisible (loadRightButton);
 - 
 -         addAndMakeVisible (movieCompLeft);
 -         addAndMakeVisible (movieCompRight);
 - 
 -         // we have to set up our StretchableLayoutManager so it know the limits and preferred sizes of it's contents
 -         stretchableManager.setItemLayout (0,            // for the fileTree
 -                                           -0.1, -0.9,   // must be between 50 pixels and 90% of the available space
 -                                           -0.3);        // and its preferred size is 30% of the total available space
 - 
 -         stretchableManager.setItemLayout (1,            // for the resize bar
 -                                           5, 5, 5);     // hard limit to 5 pixels
 - 
 -         stretchableManager.setItemLayout (2,            // for the movie components
 -                                           -0.1, -0.9,   // size must be between 50 pixels and 90% of the available space
 -                                           -0.7);        // and its preferred size is 70% of the total available space
 - 
 -         setSize (500, 500);
 -     }
 - 
 -     ~VideoDemo() override
 -     {
 -         fileTree.removeListener (this);
 -     }
 - 
 -     void paint (Graphics& g) override
 -     {
 -         g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 -     }
 - 
 -     void resized() override
 -     {
 -         // make a list of two of our child components that we want to reposition
 -         Component* comps[] = { &fileTree, &resizerBar, nullptr };
 - 
 -         // this will position the 3 components, one above the other, to fit
 -         // vertically into the rectangle provided.
 -         stretchableManager.layOutComponents (comps, 3,
 -                                              0, 0, getWidth(), getHeight(),
 -                                              true, true);
 - 
 -         // now position out two video components in the space that's left
 -         auto area = getLocalBounds().removeFromBottom (getHeight() - resizerBar.getBottom());
 - 
 -         {
 -             auto buttonArea = area.removeFromTop (30);
 -             loadLeftButton .setBounds (buttonArea.removeFromLeft (buttonArea.getWidth() / 2).reduced (5));
 -             loadRightButton.setBounds (buttonArea.reduced (5));
 -         }
 - 
 -         movieCompLeft .setBounds (area.removeFromLeft (area.getWidth() / 2).reduced (5));
 -         movieCompRight.setBounds (area.reduced (5));
 -     }
 - 
 - private:
 -     std::unique_ptr<FileChooser> fileChooser;
 -     WildcardFileFilter moviesWildcardFilter  { "*", "*", "Movies File Filter" };
 -     TimeSliceThread directoryThread          { "Movie File Scanner Thread" };
 -     DirectoryContentsList movieList          { &moviesWildcardFilter, directoryThread };
 -     FileTreeComponent fileTree               { movieList };
 - 
 -     StretchableLayoutManager stretchableManager;
 -     StretchableLayoutResizerBar resizerBar   { &stretchableManager, 1, false };
 - 
 -     TextButton loadLeftButton   { "Load Left" },
 -                loadRightButton  { "Load Right" };
 -     MovieComponentWithFileBrowser movieCompLeft, movieCompRight;
 - 
 -     void selectionChanged() override
 -     {
 -         // we're just going to update the drag description of out tree so that rows can be dragged onto the file players
 -         fileTree.setDragAndDropDescription (fileTree.getSelectedFile().getFullPathName());
 -     }
 - 
 -     void fileClicked (const File&, const MouseEvent&) override {}
 -     void fileDoubleClicked (const File&)              override {}
 -     void browserRootChanged (const File&)             override {}
 - 
 -     void selectVideoFile()
 -     {
 -         fileChooser.reset (new FileChooser ("Choose a file to open...", File::getCurrentWorkingDirectory(),
 -                                             "*", false));
 - 
 -         fileChooser->launchAsync (FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles,
 -                                   [this] (const FileChooser& chooser)
 -                                   {
 -                                       String chosen;
 -                                       auto results = chooser.getURLResults();
 - 
 -                                       // TODO: support non local files too
 -                                       if (results.size() > 0)
 -                                           movieCompLeft.setFile (results[0].getLocalFile());
 -                                   });
 -     }
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VideoDemo)
 - };
 - #elif JUCE_IOS || JUCE_ANDROID
 - //==============================================================================
 - class VideoDemo final : public Component,
 -                         private Timer
 - {
 - public:
 -     VideoDemo()
 -         : videoCompWithNativeControls (true),
 -           videoCompNoNativeControls (false)
 -     {
 -         loadLocalButton  .onClick = [this] { selectVideoFile(); };
 -         loadUrlButton    .onClick = [this] { showVideoUrlPrompt(); };
 -         seekToStartButton.onClick = [this] { seekVideoToStart(); };
 -         playButton       .onClick = [this] { playVideo(); };
 -         pauseButton      .onClick = [this] { pauseVideo(); };
 -         unloadButton     .onClick = [this] { unloadVideoFile(); };
 - 
 -         volumeLabel         .setColour (Label::textColourId, Colours::white);
 -         currentPositionLabel.setColour (Label::textColourId, Colours::white);
 - 
 -         volumeLabel         .setJustificationType (Justification::right);
 -         currentPositionLabel.setJustificationType (Justification::right);
 - 
 -         volumeSlider  .setRange (0.0, 1.0);
 -         positionSlider.setRange (0.0, 1.0);
 - 
 -         volumeSlider  .setSliderSnapsToMousePosition (false);
 -         positionSlider.setSliderSnapsToMousePosition (false);
 - 
 -         volumeSlider.setSkewFactor (1.5);
 -         volumeSlider.setValue (1.0, dontSendNotification);
 -        #if JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
 -         curVideoComp->onGlobalMediaVolumeChanged = [this]() { volumeSlider.setValue (curVideoComp->getAudioVolume(), dontSendNotification); };
 -        #endif
 - 
 -         volumeSlider  .onValueChange = [this]() { curVideoComp->setAudioVolume ((float) volumeSlider.getValue()); };
 -         positionSlider.onValueChange = [this]() { seekVideoToNormalisedPosition (positionSlider.getValue()); };
 - 
 -         positionSlider.onDragStart = [this]()
 -                                      {
 -                                          positionSliderDragging = true;
 -                                          wasPlayingBeforeDragStart = curVideoComp->isPlaying();
 - 
 -                                          if (wasPlayingBeforeDragStart)
 -                                              curVideoComp->stop();
 -                                      };
 - 
 -         positionSlider.onDragEnd   = [this]()
 -                                      {
 -                                          if (wasPlayingBeforeDragStart)
 -                                              curVideoComp->play();
 - 
 -                                          wasPlayingBeforeDragStart = false;
 - 
 -                                          // Ensure the slider does not temporarily jump back on consecutive timer callback.
 -                                          Timer::callAfterDelay (500, [this]() { positionSliderDragging = false; });
 -                                      };
 - 
 -         playSpeedComboBox.addItem ("25%", 25);
 -         playSpeedComboBox.addItem ("50%", 50);
 -         playSpeedComboBox.addItem ("100%", 100);
 -         playSpeedComboBox.addItem ("200%", 200);
 -         playSpeedComboBox.addItem ("400%", 400);
 -         playSpeedComboBox.setSelectedId (100, dontSendNotification);
 -         playSpeedComboBox.onChange = [this]() { curVideoComp->setPlaySpeed (playSpeedComboBox.getSelectedId() / 100.0); };
 - 
 -         setTransportControlsEnabled (false);
 - 
 -         addAndMakeVisible (loadLocalButton);
 -         addAndMakeVisible (loadUrlButton);
 -         addAndMakeVisible (volumeLabel);
 -         addAndMakeVisible (volumeSlider);
 -         addChildComponent (videoCompWithNativeControls);
 -         addChildComponent (videoCompNoNativeControls);
 -         addAndMakeVisible (positionSlider);
 -         addAndMakeVisible (currentPositionLabel);
 - 
 -         addAndMakeVisible (playSpeedComboBox);
 -         addAndMakeVisible (seekToStartButton);
 -         addAndMakeVisible (playButton);
 -         addAndMakeVisible (unloadButton);
 -         addChildComponent (pauseButton);
 - 
 -         setSize (500, 500);
 - 
 -         RuntimePermissions::request (RuntimePermissions::readExternalStorage,
 -                                      [] (bool granted)
 -                                      {
 -                                          if (! granted)
 -                                          {
 -                                              AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
 -                                                                                "Permissions warning",
 -                                                                                "External storage access permission not granted, some files"
 -                                                                                " may be inaccessible.");
 -                                          }
 -                                      });
 - 
 -         setPortraitOrientationEnabled (true);
 -     }
 - 
 -     ~VideoDemo() override
 -     {
 -         curVideoComp->onPlaybackStarted = nullptr;
 -         curVideoComp->onPlaybackStopped = nullptr;
 -         curVideoComp->onErrorOccurred   = nullptr;
 -         curVideoComp->onGlobalMediaVolumeChanged = nullptr;
 - 
 -         setPortraitOrientationEnabled (false);
 -     }
 - 
 -     void paint (Graphics& g) override
 -     {
 -         g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 -     }
 - 
 -     void resized() override
 -     {
 -         auto area = getLocalBounds();
 - 
 -         int marginSize = 5;
 -         int buttonHeight = 20;
 - 
 -         area.reduce (0, marginSize);
 - 
 -         auto topArea = area.removeFromTop (buttonHeight);
 -         loadLocalButton.setBounds (topArea.removeFromLeft (topArea.getWidth() / 6));
 -         loadUrlButton.setBounds (topArea.removeFromLeft (loadLocalButton.getWidth()));
 -         volumeLabel.setBounds (topArea.removeFromLeft (loadLocalButton.getWidth()));
 -         volumeSlider.setBounds (topArea.reduced (10, 0));
 - 
 -         auto transportArea = area.removeFromBottom (buttonHeight);
 -         auto positionArea  = area.removeFromBottom (buttonHeight).reduced (marginSize, 0);
 - 
 -         playSpeedComboBox.setBounds (transportArea.removeFromLeft (jmax (50, transportArea.getWidth() / 5)));
 - 
 -         auto controlWidth = transportArea.getWidth() / 3;
 - 
 -         currentPositionLabel.setBounds (positionArea.removeFromRight (jmax (150, controlWidth)));
 -         positionSlider.setBounds (positionArea);
 - 
 -         seekToStartButton.setBounds (transportArea.removeFromLeft (controlWidth));
 -         playButton       .setBounds (transportArea.removeFromLeft (controlWidth));
 -         unloadButton     .setBounds (transportArea.removeFromLeft (controlWidth));
 -         pauseButton.setBounds (playButton.getBounds());
 - 
 -         area.removeFromTop (marginSize);
 -         area.removeFromBottom (marginSize);
 - 
 -         videoCompWithNativeControls.setBounds (area);
 -         videoCompNoNativeControls.setBounds (area);
 - 
 -         if (positionSlider.getWidth() > 0)
 -             positionSlider.setMouseDragSensitivity (positionSlider.getWidth());
 -     }
 - 
 - private:
 -     TextButton loadLocalButton { "Load Local" };
 -     TextButton loadUrlButton { "Load URL" };
 -     Label volumeLabel { "volumeLabel", "Vol:" };
 -     Slider volumeSlider { Slider::LinearHorizontal, Slider::NoTextBox };
 - 
 -     VideoComponent videoCompWithNativeControls;
 -     VideoComponent videoCompNoNativeControls;
 -    #if JUCE_IOS || JUCE_MAC
 -     VideoComponent* curVideoComp = &videoCompWithNativeControls;
 -    #else
 -     VideoComponent* curVideoComp = &videoCompNoNativeControls;
 -    #endif
 -     bool isFirstSetup = true;
 - 
 -     Slider positionSlider { Slider::LinearHorizontal, Slider::NoTextBox };
 -     bool positionSliderDragging = false;
 -     bool wasPlayingBeforeDragStart = false;
 - 
 -     Label currentPositionLabel { "currentPositionLabel", "-:- / -:-" };
 - 
 -     ComboBox playSpeedComboBox { "playSpeedComboBox" };
 -     TextButton seekToStartButton { "|<" };
 -     TextButton playButton { "Play" };
 -     TextButton pauseButton { "Pause" };
 -     TextButton unloadButton { "Unload" };
 - 
 -     std::unique_ptr<FileChooser> fileChooser;
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VideoDemo)
 -     JUCE_DECLARE_WEAK_REFERENCEABLE (VideoDemo)
 - 
 -     //==============================================================================
 -     void setPortraitOrientationEnabled (bool shouldBeEnabled)
 -     {
 -         auto allowedOrientations = Desktop::getInstance().getOrientationsEnabled();
 - 
 -         if (shouldBeEnabled)
 -             allowedOrientations |= Desktop::upright;
 -         else
 -             allowedOrientations &= ~Desktop::upright;
 - 
 -         Desktop::getInstance().setOrientationsEnabled (allowedOrientations);
 -     }
 - 
 -     void setTransportControlsEnabled (bool shouldBeEnabled)
 -     {
 -         positionSlider   .setEnabled (shouldBeEnabled);
 -         playSpeedComboBox.setEnabled (shouldBeEnabled);
 -         seekToStartButton.setEnabled (shouldBeEnabled);
 -         playButton       .setEnabled (shouldBeEnabled);
 -         unloadButton     .setEnabled (shouldBeEnabled);
 -         pauseButton      .setEnabled (shouldBeEnabled);
 -     }
 - 
 -     void selectVideoFile()
 -     {
 -         fileChooser.reset (new FileChooser ("Choose a video file to open...", File::getCurrentWorkingDirectory(),
 -                                             "*", true));
 - 
 -         fileChooser->launchAsync (FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles,
 -                                   [this] (const FileChooser& chooser)
 -                                   {
 -                                       auto results = chooser.getURLResults();
 - 
 -                                       if (results.size() > 0)
 -                                           loadVideo (results[0]);
 -                                   });
 -     }
 - 
 -     void loadVideo (const URL& url)
 -     {
 -         unloadVideoFile();
 - 
 -        #if JUCE_IOS || JUCE_MAC
 -         askIfUseNativeControls (url);
 -        #else
 -         loadUrl (url);
 -         setupVideoComp (false);
 -        #endif
 -     }
 - 
 -     void askIfUseNativeControls (const URL& url)
 -     {
 -         auto* aw = new AlertWindow ("Choose viewer type", {}, MessageBoxIconType::NoIcon);
 - 
 -         aw->addButton ("Yes", 1, KeyPress (KeyPress::returnKey));
 -         aw->addButton ("No", 0, KeyPress (KeyPress::escapeKey));
 -         aw->addTextBlock ("Do you want to use the viewer with native controls?");
 - 
 -         auto callback = ModalCallbackFunction::forComponent (videoViewerTypeChosen, this, url);
 -         aw->enterModalState (true, callback, true);
 -     }
 - 
 -     static void videoViewerTypeChosen (int result, VideoDemo* owner, URL url)
 -     {
 -         if (owner != nullptr)
 -         {
 -             owner->setupVideoComp (result != 0);
 -             owner->loadUrl (url);
 -         }
 -     }
 - 
 -     void setupVideoComp (bool useNativeViewerWithNativeControls)
 -     {
 -         auto* oldVideoComp = curVideoComp;
 - 
 -         if (useNativeViewerWithNativeControls)
 -             curVideoComp = &videoCompWithNativeControls;
 -         else
 -             curVideoComp = &videoCompNoNativeControls;
 - 
 -         if (isFirstSetup || oldVideoComp != curVideoComp)
 -         {
 -             oldVideoComp->onPlaybackStarted = nullptr;
 -             oldVideoComp->onPlaybackStopped = nullptr;
 -             oldVideoComp->onErrorOccurred   = nullptr;
 -             oldVideoComp->setVisible (false);
 - 
 -             curVideoComp->onPlaybackStarted = [this]() { processPlaybackStarted(); };
 -             curVideoComp->onPlaybackStopped = [this]() { processPlaybackPaused(); };
 -             curVideoComp->onErrorOccurred   = [this] (const String& errorMessage) { errorOccurred (errorMessage); };
 -             curVideoComp->setVisible (true);
 - 
 -            #if JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
 -             oldVideoComp->onGlobalMediaVolumeChanged = nullptr;
 -             curVideoComp->onGlobalMediaVolumeChanged = [this]() { volumeSlider.setValue (curVideoComp->getAudioVolume(), dontSendNotification); };
 -            #endif
 -         }
 - 
 -         isFirstSetup = false;
 -     }
 - 
 -     void loadUrl (const URL& url)
 -     {
 -         curVideoComp->loadAsync (url, [this] (const URL& u, Result r) { videoLoadingFinished (u, r); });
 -     }
 - 
 -     void showVideoUrlPrompt()
 -     {
 -         auto* aw = new AlertWindow ("Enter URL for video to load", {}, MessageBoxIconType::NoIcon);
 - 
 -         aw->addButton ("OK", 1, KeyPress (KeyPress::returnKey));
 -         aw->addButton ("Cancel", 0, KeyPress (KeyPress::escapeKey));
 -         aw->addTextEditor ("videoUrlTextEditor", "https://www.rmp-streaming.com/media/bbb-360p.mp4");
 - 
 -         auto callback = ModalCallbackFunction::forComponent (videoUrlPromptClosed, this, Component::SafePointer<AlertWindow> (aw));
 -         aw->enterModalState (true, callback, true);
 -     }
 - 
 -     static void videoUrlPromptClosed (int result, VideoDemo* owner, Component::SafePointer<AlertWindow> aw)
 -     {
 -         if (result != 0 && owner != nullptr && aw != nullptr)
 -         {
 -             auto url = aw->getTextEditorContents ("videoUrlTextEditor");
 - 
 -             if (url.isNotEmpty())
 -                 owner->loadVideo (url);
 -         }
 -     }
 - 
 -     void videoLoadingFinished (const URL& url, Result result)
 -     {
 -         ignoreUnused (url);
 - 
 -         if (result.wasOk())
 -         {
 -             resized(); // update to reflect the video's aspect ratio
 - 
 -             setTransportControlsEnabled (true);
 - 
 -             currentPositionLabel.setText (getPositionString (0.0, curVideoComp->getVideoDuration()), sendNotification);
 -             positionSlider.setValue (0.0, dontSendNotification);
 -             playSpeedComboBox.setSelectedId (100, dontSendNotification);
 -         }
 -         else
 -         {
 -             AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
 -                                               "Couldn't load the file!",
 -                                               result.getErrorMessage());
 -         }
 -     }
 - 
 -     static String getPositionString (double playPositionSeconds, double durationSeconds)
 -     {
 -         auto positionMs = static_cast<int> (1000 * playPositionSeconds);
 -         int posMinutes = positionMs / 60000;
 -         int posSeconds = (positionMs % 60000) / 1000;
 -         int posMillis = positionMs % 1000;
 - 
 -         auto totalMs = static_cast<int> (1000 * durationSeconds);
 -         int totMinutes = totalMs / 60000;
 -         int totSeconds = (totalMs % 60000) / 1000;
 -         int totMillis = totalMs % 1000;
 - 
 -         return String::formatted ("%02d:%02d:%03d / %02d:%02d:%03d",
 -                                   posMinutes, posSeconds, posMillis,
 -                                   totMinutes, totSeconds, totMillis);
 -     }
 - 
 -     void updatePositionSliderAndLabel()
 -     {
 -         auto position = curVideoComp->getPlayPosition();
 -         auto duration = curVideoComp->getVideoDuration();
 - 
 -         currentPositionLabel.setText (getPositionString (position, duration), sendNotification);
 - 
 -         if (! positionSliderDragging)
 -             positionSlider.setValue (approximatelyEqual (duration, 0.0) ? 0.0 : (position / duration), dontSendNotification);
 -     }
 - 
 -     void seekVideoToStart()
 -     {
 -         seekVideoToNormalisedPosition (0.0);
 -     }
 - 
 -     void seekVideoToNormalisedPosition (double normalisedPos)
 -     {
 -         normalisedPos = jlimit (0.0, 1.0, normalisedPos);
 - 
 -         auto duration = curVideoComp->getVideoDuration();
 -         auto newPos = jlimit (0.0, duration, duration * normalisedPos);
 - 
 -         curVideoComp->setPlayPosition (newPos);
 -         currentPositionLabel.setText (getPositionString (newPos, curVideoComp->getVideoDuration()), sendNotification);
 -         positionSlider.setValue (normalisedPos, dontSendNotification);
 -     }
 - 
 -     void playVideo()
 -     {
 -         curVideoComp->play();
 -     }
 - 
 -     void processPlaybackStarted()
 -     {
 -         playButton.setVisible (false);
 -         pauseButton.setVisible (true);
 - 
 -         startTimer (20);
 -     }
 - 
 -     void pauseVideo()
 -     {
 -         curVideoComp->stop();
 -     }
 - 
 -     void processPlaybackPaused()
 -     {
 -         // On seeking to a new pos, the playback may be temporarily paused.
 -         if (positionSliderDragging)
 -             return;
 - 
 -         pauseButton.setVisible (false);
 -         playButton.setVisible (true);
 -     }
 - 
 -     void errorOccurred (const String& errorMessage)
 -     {
 -         AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon,
 -                                           "An error has occurred",
 -                                           errorMessage + ", video will be unloaded.");
 - 
 -         unloadVideoFile();
 -     }
 - 
 -     void unloadVideoFile()
 -     {
 -         curVideoComp->closeVideo();
 - 
 -         setTransportControlsEnabled (false);
 -         stopTimer();
 - 
 -         pauseButton.setVisible (false);
 -         playButton.setVisible (true);
 - 
 -         currentPositionLabel.setText ("-:- / -:-", sendNotification);
 -         positionSlider.setValue (0.0, dontSendNotification);
 -     }
 - 
 -     void timerCallback() override
 -     {
 -         updatePositionSliderAndLabel();
 -     }
 - };
 - #elif JUCE_LINUX || JUCE_BSD
 -  #error "This demo is not supported on Linux!"
 - #endif
 
 
  |