summaryrefslogtreecommitdiffstats
path: root/chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.h
blob: f31e1fa9d980bf4e8a44217fe0b7f6939bdede4a (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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROMECAST_MEDIA_CMA_BACKEND_ALSA_STREAM_MIXER_ALSA_INPUT_IMPL_H_
#define CHROMECAST_MEDIA_CMA_BACKEND_ALSA_STREAM_MIXER_ALSA_INPUT_IMPL_H_

#include <deque>

#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/synchronization/lock.h"
#include "chromecast/media/cma/backend/alsa/media_pipeline_backend_alsa.h"
#include "chromecast/media/cma/backend/alsa/stream_mixer_alsa.h"
#include "chromecast/media/cma/backend/alsa/stream_mixer_alsa_input.h"

namespace base {
class SingleThreadTaskRunner;
}  // namespace base

namespace media {
class AudioBus;
class MultiChannelResampler;
}  // namespace media

namespace chromecast {
namespace media {

// Input queue implementation for StreamMixerAlsa. Each input source pushes
// frames to an instance of StreamMixerAlsaInputImpl; this then signals the
// mixer to pull as much data as possible from all input queues, and mix it and
// write it out to the ALSA stack. The delegate's OnWritePcmCompletion() method
// is called (on the caller thread) whenever data has been successfully added to
// the queue (this may not happen immediately if the queue's maximum size limit
// has been exceeded).
//
// If an input is being resampled, it is conservative about how much data it can
// provide to be written. This is because the resampler buffers some data
// internally, and consumes data from the queue in large chunks. A resampled
// input ignores the data buffered inside the resampler, as well as any data
// that does not fit within a multiple of the resampler chunk size, for the
// purpose of calculating how much data it can provide. Note that this only
// holds true if the input is not in end-of-stream mode.
//
// When an input is paused, it rapidly fades out (to avoid any pops or clicks),
// and then begins feeding silence to the mixer. The "paused" state is
// transparent to the mixer (when paused, this class provides silence when the
// mixer asks for data). On unpause, the sound is rapidly faded in and then
// resumes normal playback. Similarly, the sound is rapidly faded in at the
// start of playback, and faded out when the input is being removed. This
// prevents sound distortions on track skip or seeking.
//
// The rendering delay is recalculated after every successful write to the ALSA
// stack. This delay is passed up to the input sources whenever some new data
// is sucessfully added to the input queue (which happens whenever the amount
// of data in the queue drops below the maximum limit, if data is pending). Note
// that the rendering delay is not accurate while the mixer is gathering frames
// to write, so pending data is not added to an input queue until after a write
// has completed (in AfterWriteFrames()).
//
// This class is constructed on the caller thread, and the StreamMixerAlsaInput
// methods (WritePcm(), SetPaused(), SetVolumeMultiplier(), and
// PreventDelegateCalls()) must be called on the caller thread. These methods
// must not be called after the input is being removed (ie, after
// mixer->RemoveInput() has been called for this input impl). All other methods
// (including the destructor) must be called on the mixer thread.
//
// When an input is removed, the mixer tells the impl that it is about to be
// removed (it is not deleted yet) by calling PrepareToRemove(). The impl then
// fades out any remaining audio data. Once that is done (or if it is not
// possible/necessary) then the impl calls the |delete_cb| to tell the mixer to
// actually delete it.
class StreamMixerAlsaInputImpl : public StreamMixerAlsa::InputQueue {
 public:
  enum State {
    kStateUninitialized,   // No data has been queued yet.
    kStateNormalPlayback,  // Normal playback.
    kStateFadingOut,       // Fading out to a paused state.
    kStatePaused,          // Currently paused.
    kStateGotEos,          // Got the end-of-stream buffer (normal playback).
    kStateFinalFade,       // Fading out to a deleted state.
    kStateDeleted,         // Told the mixer to delete this.
    kStateError,           // A mixer error occurred, this is unusable now.
  };

  StreamMixerAlsaInputImpl(StreamMixerAlsaInput::Delegate* delegate,
                           int input_samples_per_second,
                           bool primary,
                           StreamMixerAlsa* mixer);

  ~StreamMixerAlsaInputImpl() override;

  // Queues some PCM data to be mixed. |data| must be in planar float format.
  void WritePcm(const scoped_refptr<DecoderBufferBase>& data);

  // Sets the pause state of this stream.
  void SetPaused(bool paused);

  // Sets the volume multiplier for this stream. If |multiplier| < 0, sets the
  // volume multiplier to 0. If |multiplier| > 1, sets the volume multiplier
  // to 1.
  void SetVolumeMultiplier(float multiplier);

  // Prevents any further calls to the delegate (ie, called when the delegate
  // is being destroyed).
  void PreventDelegateCalls();

  State state() const { return state_; }

 private:
  // StreamMixerAlsa::InputQueue implementation:
  int input_samples_per_second() const override;
  float volume_multiplier() const override;
  bool primary() const override;
  bool IsDeleting() const override;
  void Initialize(const MediaPipelineBackendAlsa::RenderingDelay&
                      mixer_rendering_delay) override;
  int MaxReadSize() override;
  void GetResampledData(::media::AudioBus* dest, int frames) override;
  void AfterWriteFrames(const MediaPipelineBackendAlsa::RenderingDelay&
                            mixer_rendering_delay) override;
  void SignalError() override;
  void PrepareToDelete(const OnReadyToDeleteCb& delete_cb) override;

  // Tells the mixer to delete |this|. Makes sure not to call |delete_cb_| more
  // than once for |this|.
  void DeleteThis();
  MediaPipelineBackendAlsa::RenderingDelay QueueData(
      const scoped_refptr<DecoderBufferBase>& data);
  void PostPcmCallback(const MediaPipelineBackendAlsa::RenderingDelay& delay);
  void DidQueueData(bool end_of_stream);
  void ReadCB(int frame_delay, ::media::AudioBus* output);
  void FillFrames(int frame_delay, ::media::AudioBus* output, int frames);
  int NormalFadeFrames();
  void FadeIn(::media::AudioBus* dest, int frames);
  void FadeOut(::media::AudioBus* dest, int frames);
  void PostError();

  StreamMixerAlsaInput::Delegate* const delegate_;
  const int input_samples_per_second_;
  const bool primary_;
  StreamMixerAlsa* const mixer_;
  const scoped_refptr<base::SingleThreadTaskRunner> mixer_task_runner_;
  const scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;

  State state_;
  float volume_multiplier_;

  base::Lock queue_lock_;  // Lock for the following queue-related members.
  scoped_refptr<DecoderBufferBase> pending_data_;
  std::deque<scoped_refptr<DecoderBufferBase>> queue_;
  int queued_frames_;
  double queued_frames_including_resampler_;
  MediaPipelineBackendAlsa::RenderingDelay mixer_rendering_delay_;
  // End of members that queue_lock_ controls access for.

  int current_buffer_offset_;
  int max_queued_frames_;
  int fade_frames_remaining_;
  int fade_out_frames_total_;
  int zeroed_frames_;  // current count of consecutive 0-filled frames

  OnReadyToDeleteCb delete_cb_;

  scoped_ptr<::media::MultiChannelResampler> resampler_;

  base::WeakPtr<StreamMixerAlsaInputImpl> weak_this_;
  base::WeakPtrFactory<StreamMixerAlsaInputImpl> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(StreamMixerAlsaInputImpl);
};

}  // namespace media
}  // namespace chromecast

#endif  // CHROMECAST_MEDIA_CMA_BACKEND_ALSA_STREAM_MIXER_ALSA_INPUT_IMPL_H_