summaryrefslogtreecommitdiffstats
path: root/dom/media/mediasink/AudioSinkWrapper.h
blob: 411983c526ea9c9deb3ea09e047a10d840d7a722 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef AudioSinkWrapper_h_
#define AudioSinkWrapper_h_

#include "mozilla/AbstractThread.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"

#include "AudioSink.h"
#include "MediaSink.h"

namespace mozilla {
class MediaData;
template <class T>
class MediaQueue;

/**
 * A wrapper around AudioSink to provide the interface of MediaSink.
 */
class AudioSinkWrapper : public MediaSink {
  using PlaybackParams = AudioSink::PlaybackParams;

  // An AudioSink factory.
  class Creator {
   public:
    virtual ~Creator() = default;
    virtual AudioSink* Create() = 0;
  };

  // Wrap around a function object which creates AudioSinks.
  template <typename Function>
  class CreatorImpl : public Creator {
   public:
    explicit CreatorImpl(const Function& aFunc) : mFunction(aFunc) {}
    AudioSink* Create() override { return mFunction(); }

   private:
    Function mFunction;
  };

 public:
  template <typename Function>
  AudioSinkWrapper(AbstractThread* aOwnerThread,
                   MediaQueue<AudioData>& aAudioQueue, const Function& aFunc,
                   double aVolume, double aPlaybackRate, bool aPreservesPitch,
                   RefPtr<AudioDeviceInfo> aAudioDevice)
      : mOwnerThread(aOwnerThread),
        mCreator(new CreatorImpl<Function>(aFunc)),
        mAudioDevice(std::move(aAudioDevice)),
        mIsStarted(false),
        mParams(aVolume, aPlaybackRate, aPreservesPitch),
        // Give an invalid value to facilitate debug if used before playback
        // starts.
        mPlayDuration(media::TimeUnit::Invalid()),
        mAudioEnded(true),
        mAudioQueue(aAudioQueue) {}

  RefPtr<EndedPromise> OnEnded(TrackType aType) override;
  media::TimeUnit GetEndTime(TrackType aType) const override;
  media::TimeUnit GetPosition(TimeStamp* aTimeStamp = nullptr) override;
  bool HasUnplayedFrames(TrackType aType) const override;
  media::TimeUnit UnplayedDuration(TrackType aType) const override;
  void DropAudioPacketsIfNeeded(const media::TimeUnit& aMediaPosition);

  void SetVolume(double aVolume) override;
  void SetStreamName(const nsAString& aStreamName) override;
  void SetPlaybackRate(double aPlaybackRate) override;
  void SetPreservesPitch(bool aPreservesPitch) override;
  void SetPlaying(bool aPlaying) override;

  double PlaybackRate() const override;

  nsresult Start(const media::TimeUnit& aStartTime,
                 const MediaInfo& aInfo) override;
  void Stop() override;
  bool IsStarted() const override;
  bool IsPlaying() const override;

  const AudioDeviceInfo* AudioDevice() const override { return mAudioDevice; }

  void Shutdown() override;

  void GetDebugInfo(dom::MediaSinkDebugInfo& aInfo) override;

  void EnableTreatAudioUnderrunAsSilence(bool aEnabled) override;

 private:
  // The clock that was in use for the previous position query, allowing to
  // detect clock switches.
  enum class ClockSource {
    // The clock comes from an underlying system-level audio stream.
    AudioStream,
    // The clock comes from the system clock.
    SystemClock,
    // The stream is paused, a constant time is reported.
    Paused
  } mLastClockSource = ClockSource::Paused;
  bool IsMuted() const;
  void OnMuted(bool aMuted);
  virtual ~AudioSinkWrapper();

  void AssertOwnerThread() const {
    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
  }

  // An AudioSink can be started synchronously from the MDSM thread, or
  // asynchronously.
  // In synchronous mode, the clock doesn't advance until the sink has been
  // created, initialized and started. This is useful for the initial startup,
  // and when seeking.
  // In asynchronous mode, the clock will keep going forward (using the system
  // clock) until the AudioSink is started, at which point the clock will use
  // the AudioSink clock. This is used when unmuting a media element.
  enum class AudioSinkStartPolicy { SYNC, ASYNC };
  nsresult StartAudioSink(const media::TimeUnit& aStartTime,
                          AudioSinkStartPolicy aPolicy);

  // Get the current media position using the system clock. This is used when
  // the audio is muted, or when the media has no audio track. Otherwise, the
  // media's position is based on the clock of the AudioStream.
  media::TimeUnit GetSystemClockPosition(TimeStamp aNow) const;
  bool CheckIfEnded() const;

  void OnAudioEnded();

  bool IsAudioSourceEnded(const MediaInfo& aInfo) const;

  const RefPtr<AbstractThread> mOwnerThread;
  UniquePtr<Creator> mCreator;
  UniquePtr<AudioSink> mAudioSink;
  // The output device this AudioSink is playing data to. The system's default
  // device is used if this is null.
  const RefPtr<AudioDeviceInfo> mAudioDevice;
  // Will only exist when media has an audio track.
  RefPtr<EndedPromise> mEndedPromise;
  MozPromiseHolder<EndedPromise> mEndedPromiseHolder;

  bool mIsStarted;
  PlaybackParams mParams;

  TimeStamp mPlayStartTime;
  media::TimeUnit mPlayDuration;

  bool mAudioEnded;
  MozPromiseRequestHolder<EndedPromise> mAudioSinkEndedPromise;
  MediaQueue<AudioData>& mAudioQueue;

  // True if we'd like to treat underrun as silent frames. But that can only be
  // applied in the special situation for seamless looping.
  bool mTreatUnderrunAsSilence = false;
};

}  // namespace mozilla

#endif  // AudioSinkWrapper_h_