/*
 * Copyright (C) 2009 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_MEDIA_PLAYER_H_
#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_MEDIA_PLAYER_H_

#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "media/base/video_frame_metadata.h"
#include "third_party/blink/public/common/media/display_type.h"
#include "third_party/blink/public/platform/web_content_decryption_module.h"
#include "third_party/blink/public/platform/web_media_source.h"
#include "third_party/blink/public/platform/web_set_sink_id_callbacks.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/webaudiosourceprovider_impl.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"

namespace cc {
class PaintCanvas;
class PaintFlags;
}  // namespace cc

namespace media {
class VideoFrame;
class PaintCanvasVideoRenderer;
}

namespace blink {

class WebContentDecryptionModule;
class WebMediaPlayerSource;
class WebString;
class WebURL;
enum class WebFullscreenVideoStatus;

class WebMediaPlayer {
 public:
  enum NetworkState {
    kNetworkStateEmpty,
    kNetworkStateIdle,
    kNetworkStateLoading,
    kNetworkStateLoaded,
    kNetworkStateFormatError,
    kNetworkStateNetworkError,
    kNetworkStateDecodeError,
  };

  enum ReadyState {
    kReadyStateHaveNothing,
    kReadyStateHaveMetadata,
    kReadyStateHaveCurrentData,
    kReadyStateHaveFutureData,
    kReadyStateHaveEnoughData,
  };

  enum Preload {
    kPreloadNone,
    kPreloadMetaData,
    kPreloadAuto,
  };

  enum CorsMode {
    kCorsModeUnspecified,
    kCorsModeAnonymous,
    kCorsModeUseCredentials,
  };

  // Reported to UMA. Do not change existing values.
  enum LoadType {
    kLoadTypeURL = 0,
    kLoadTypeMediaSource = 1,
    kLoadTypeMediaStream = 2,
    kLoadTypeMax = kLoadTypeMediaStream,
  };

  typedef WebString TrackId;
  enum TrackType { kTextTrack, kAudioTrack, kVideoTrack };

  // This must stay in sync with WebGLRenderingContextBase::TexImageFunctionID.
  enum TexImageFunctionID {
    kTexImage2D,
    kTexSubImage2D,
    kTexImage3D,
    kTexSubImage3D
  };

  // Returned by Load() to signal when a players choose to defer (e.g. as part
  // of pre-rendering)
  enum LoadTiming { kImmediate, kDeferred };

  // For video.requestVideoFrameCallback(). https://wicg.github.io/video-rvfc/
  struct VideoFramePresentationMetadata {
    uint32_t presented_frames;
    base::TimeTicks presentation_time;
    base::TimeTicks expected_display_time;
    int width;
    int height;
    base::TimeDelta media_time;
    media::VideoFrameMetadata metadata;
    base::TimeDelta rendering_interval;
    base::TimeDelta average_frame_duration;
  };

  // Describes when we use SurfaceLayer for video instead of VideoLayer.
  enum class SurfaceLayerMode {
    // Always use VideoLayer
    kNever,

    // Always use SurfaceLayer for video.
    kAlways,
  };

  virtual ~WebMediaPlayer() = default;

  virtual LoadTiming Load(LoadType,
                          const WebMediaPlayerSource&,
                          CorsMode,
                          bool is_cache_disabled) = 0;

  // Playback controls.
  virtual void Play() = 0;
  virtual void Pause() = 0;
  virtual void Seek(double seconds) = 0;
  virtual void SetRate(double) = 0;
  virtual void SetVolume(double) = 0;

  // Set a target value for media pipeline latency for post-decode buffering.
  // |seconds| is a target value for post-decode buffering latency. As a default
  // |seconds| may also be NaN, indicating no preference. NaN will also be the
  // value if the hint is cleared.
  virtual void SetLatencyHint(double seconds) = 0;

  // Sets a flag indicating that the WebMediaPlayer should apply pitch
  // adjustments when using a playback rate other than 1.0.
  virtual void SetPreservesPitch(bool preserves_pitch) = 0;

  // Sets a flag indicating whether the audio stream was initiated by autoplay.
  virtual void SetAutoplayInitiated(bool autoplay_initiated) = 0;

  // The associated media element is going to enter Picture-in-Picture. This
  // method should make sure the player is set up for this and has a SurfaceId
  // as it will be needed.
  virtual void OnRequestPictureInPicture() = 0;

  // Called to notify about changes of the associated media element's media
  // time, playback rate, and duration. During uninterrupted playback, the
  // calls are still made periodically.
  virtual void OnTimeUpdate() {}

  virtual void RequestRemotePlayback() {}
  virtual void RequestRemotePlaybackControl() {}
  virtual void RequestRemotePlaybackStop() {}
  virtual void RequestRemotePlaybackDisabled(bool disabled) {}
  virtual void FlingingStarted() {}
  virtual void FlingingStopped() {}
  virtual void SetPreload(Preload) {}
  virtual WebTimeRanges Buffered() const = 0;
  virtual WebTimeRanges Seekable() const = 0;

  // Attempts to switch the audio output device.
  virtual bool SetSinkId(const WebString& sing_id,
                         WebSetSinkIdCompleteCallback) = 0;

  // True if the loaded media has a playable video/audio track.
  virtual bool HasVideo() const = 0;
  virtual bool HasAudio() const = 0;

  // True if the media is being played on a remote device.
  virtual bool IsRemote() const { return false; }

  // Dimension of the video.
  virtual gfx::Size NaturalSize() const = 0;

  virtual gfx::Size VisibleSize() const = 0;

  // Getters of playback state.
  virtual bool Paused() const = 0;
  virtual bool Seeking() const = 0;
  virtual double Duration() const = 0;
  virtual double CurrentTime() const = 0;
  virtual bool IsEnded() const = 0;

  virtual bool PausedWhenHidden() const { return false; }

  // Internal states of loading and network.
  virtual NetworkState GetNetworkState() const = 0;
  virtual ReadyState GetReadyState() const = 0;

  virtual SurfaceLayerMode GetVideoSurfaceLayerMode() const = 0;

  // Returns an implementation-specific human readable error message, or an
  // empty string if no message is available. The message should begin with a
  // UA-specific-error-code (without any ':'), optionally followed by ': ' and
  // further description of the error.
  virtual WebString GetErrorMessage() const = 0;

  virtual bool DidLoadingProgress() = 0;

  // Returns true if the response is CORS-cross-origin and so we shouldn't be
  // allowing media to play through webaudio.
  // This should be called after the response has arrived.
  virtual bool WouldTaintOrigin() const = 0;

  virtual double MediaTimeForTimeValue(double time_value) const = 0;

  virtual unsigned DecodedFrameCount() const = 0;
  virtual unsigned DroppedFrameCount() const = 0;
  virtual unsigned CorruptedFrameCount() const { return 0; }
  virtual uint64_t AudioDecodedByteCount() const = 0;
  virtual uint64_t VideoDecodedByteCount() const = 0;

  // Suspends the player for the host frame closed.
  virtual void SuspendForFrameClosed() = 0;

  // Returns true if the player has a frame available for presentation. Usually
  // this just means the first frame has been delivered.
  virtual bool HasAvailableVideoFrame() const = 0;

  // Renders the current frame into the provided cc::PaintCanvas.
  virtual void Paint(cc::PaintCanvas*, const gfx::Rect&, cc::PaintFlags&) = 0;

  // Similar to Paint(), but just returns the frame directly instead of trying
  // to upload or convert it. Note: This may kick off a process to update the
  // current frame for a future call in some cases. Returns nullptr if no frame
  // is available.
  virtual scoped_refptr<media::VideoFrame> GetCurrentFrame() = 0;

  // Provides a PaintCanvasVideoRenderer instance owned by this WebMediaPlayer.
  // Useful for ensuring that the paint/texturing operation for current frame is
  // cached in cases of repainting/retexturing (since clients may not know that
  // the underlying frame is unchanged). May only be used on the main thread and
  // should not be held outside the scope of a single call site.
  virtual media::PaintCanvasVideoRenderer* GetPaintCanvasVideoRenderer() {
    return nullptr;
  }

  virtual scoped_refptr<WebAudioSourceProviderImpl> GetAudioSourceProvider() {
    return nullptr;
  }

  virtual void SetContentDecryptionModule(
      WebContentDecryptionModule* cdm,
      WebContentDecryptionModuleResult result) {
    result.CompleteWithError(
        kWebContentDecryptionModuleExceptionNotSupportedError, 0, "ERROR");
  }

  // Sets the poster image URL.
  virtual void SetPoster(const WebURL& poster) {}

  // Whether the WebMediaPlayer supports overlay fullscreen video mode. When
  // this is true, the video layer will be removed from the layer tree when
  // entering fullscreen, and the WebMediaPlayer is responsible for displaying
  // the video in enteredFullscreen().
  virtual bool SupportsOverlayFullscreenVideo() { return false; }
  // Inform WebMediaPlayer when the element has entered/exited fullscreen.
  virtual void EnteredFullscreen() {}
  virtual void ExitedFullscreen() {}

  // Inform WebMediaPlayer when the element starts/stops being the dominant
  // visible content. This will only be called after the monitoring of the
  // intersection with viewport is activated by calling
  // WebMediaPlayerClient::ActivateViewportIntersectionMonitoring().
  virtual void BecameDominantVisibleContent(bool is_dominant) {}

  // Inform WebMediaPlayer when the element starts/stops being the effectively
  // fullscreen video, i.e. being the fullscreen element or child of the
  // fullscreen element, and being dominant in the viewport.
  //
  // TODO(zqzhang): merge with BecameDominantVisibleContent(). See
  // https://crbug.com/696211
  virtual void SetIsEffectivelyFullscreen(WebFullscreenVideoStatus) {}

  virtual void EnabledAudioTracksChanged(
      const WebVector<TrackId>& enabled_track_ids) {}
  // |selected_track_id| is null if no track is selected.
  virtual void SelectedVideoTrackChanged(TrackId* selected_track_id) {}

  // Callback called whenever the media element may have received or last native
  // controls. It might be called twice with the same value: the caller has to
  // check if the value have changed if it only wants to handle this case.
  // This method is not used to say express if the native controls are visible
  // but if the element is using them.
  virtual void OnHasNativeControlsChanged(bool) {}

  // Callback called whenever the media element display type changes. By
  // default, the display type is `kInline`.
  virtual void OnDisplayTypeChanged(DisplayType) {}

  // Test helper methods for exercising media suspension.
  virtual void ForceStaleStateForTesting(ReadyState target_state) {}
  virtual bool IsSuspendedForTesting() { return false; }

  virtual bool DidLazyLoad() const { return false; }
  virtual void OnBecameVisible() {}

  virtual bool IsOpaque() const { return false; }

  // Returns the id given by the WebMediaPlayerDelegate. This is used by the
  // Blink code to pass a player id to mojo services.
  // TODO(mlamouri): remove this and move the id handling to Blink.
  virtual int GetDelegateId() { return -1; }

  // Returns the SurfaceId the video element is currently using.
  // Returns base::nullopt if the element isn't a video or doesn't have a
  // SurfaceId associated to it.
  virtual base::Optional<viz::SurfaceId> GetSurfaceId() {
    return base::nullopt;
  }

  // Provide the media URL, after any redirects are applied.  May return an
  // empty GURL, which will be interpreted as "use the original URL".
  virtual GURL GetSrcAfterRedirects() { return GURL(); }

  // Register a request to be notified the next time a video frame is presented
  // to the compositor. The request will be completed via
  // WebMediaPlayerClient::OnRequestVideoFrameCallback(). The frame info can be
  // retrieved via GetVideoFramePresentationMetadata().
  // See https://wicg.github.io/video-rvfc/.
  virtual void RequestVideoFrameCallback() {}
  virtual std::unique_ptr<VideoFramePresentationMetadata>
  GetVideoFramePresentationMetadata() {
    return nullptr;
  }

  // Forces the WebMediaPlayer to update its frame if it is stale. This is used
  // during immersive WebXR sessions with the RequestVideoFrameCallback() API,
  // when compositors aren't driving frame updates.
  virtual void UpdateFrameIfStale() {}

  virtual base::WeakPtr<WebMediaPlayer> AsWeakPtr() = 0;
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_MEDIA_PLAYER_H_
