aboutsummaryrefslogtreecommitdiff
path: root/cast/standalone_sender/simulated_capturer.h
blob: 61738e1f803d8e3fd4ac2f3433af0b87e5db39d6 (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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// Copyright 2020 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 CAST_STANDALONE_SENDER_SIMULATED_CAPTURER_H_
#define CAST_STANDALONE_SENDER_SIMULATED_CAPTURER_H_

#include <stdint.h>

#include <string>
#include <vector>

#include "absl/types/optional.h"
#include "cast/standalone_sender/ffmpeg_glue.h"
#include "platform/api/time.h"
#include "util/alarm.h"

namespace openscreen {
namespace cast {

class Environment;

// Simulates live media capture by demuxing, decoding, and emitting a stream of
// frames from a file at normal (1X) speed. This is a base class containing
// common functionality. Typical usage: Instantiate one SimulatedAudioCapturer
// and one FileVideoStreamCapturer.
class SimulatedCapturer {
 public:
  // Interface for receiving end-of-stream and fatal error notifications.
  class Observer {
   public:
    // Called once the end of the file has been reached and the |capturer| has
    // halted.
    virtual void OnEndOfFile(SimulatedCapturer* capturer) = 0;

    // Called if a non-recoverable error occurs and the |capturer| has halted.
    virtual void OnError(SimulatedCapturer* capturer, std::string message) = 0;

   protected:
    virtual ~Observer();
  };

  void SetPlaybackRate(double rate);

 protected:
  SimulatedCapturer(Environment* environment,
                    const char* path,
                    AVMediaType media_type,
                    Clock::time_point start_time,
                    Observer* observer);

  virtual ~SimulatedCapturer();

  // Optionally overridden, to apply additional decoder context settings before
  // avcodec_open2() is called.
  virtual void SetAdditionalDecoderParameters(AVCodecContext* decoder_context);

  // Performs any additional processing on the decoded frame (e.g., audio
  // resampling), and returns any adjustments to the frame's capture time (e.g.,
  // to account for any buffering). If a fatal error occurs, absl::nullopt is
  // returned. The default implementation does nothing.
  //
  // Mutating the |decoded_frame| is not allowed. If a subclass implementation
  // wants to deliver different data (e.g., resampled audio), it must stash the
  // data itself for the next DeliverDataToClient() call.
  virtual absl::optional<Clock::duration> ProcessDecodedFrame(
      const AVFrame& decoded_frame);

  // Delivers the decoded frame data to the client.
  virtual void DeliverDataToClient(const AVFrame& decoded_frame,
                                   Clock::time_point capture_time) = 0;

  // Called when any transient or fatal error occurs, generating an Error and
  // scheduling a task to notify the Observer of it soon.
  void OnError(const char* what, int av_errnum);

  // Converts the given FFMPEG tick count into an approximate Clock::duration.
  static Clock::duration ToApproximateClockDuration(
      int64_t ticks,
      const AVRational& time_base);

 private:
  // Reads the next frame from the file, sends it to the decoder, and schedules
  // a future ConsumeNextDecodedFrame() call to continue processing.
  void StartDecodingNextFrame();

  // Receives the next decoded frame and schedules media delivery to the client,
  // and/or calls Observer::OnEndOfFile() if there are no more frames in the
  // file.
  void ConsumeNextDecodedFrame();

  const AVFormatContextUniquePtr format_context_;
  const AVMediaType media_type_;  // Audio or Video.
  const Clock::time_point start_time_;
  Observer* const observer_;
  const AVPacketUniquePtr packet_;        // Decoder input buffer.
  const AVFrameUniquePtr decoded_frame_;  // Decoder output frame.
  int stream_index_ = -1;                 // Selected stream from the file.
  AVCodecContextUniquePtr decoder_context_;

  // The last frame's stream timestamp. This is used to detect bad stream
  // timestamps in the file.
  absl::optional<Clock::duration> last_frame_timestamp_;

  // Used to schedule the next task to execute and when it should execute. There
  // is only ever one task scheduled/running at any time.
  Alarm next_task_;

  // Used to determine playback rate. Currently, we only support "playing"
  // at 1x speed, or "pausing" at 0x speed.
  bool playback_rate_is_non_zero_ = true;
};

// Emits the primary audio stream from a file.
class SimulatedAudioCapturer final : public SimulatedCapturer {
 public:
  class Client : public SimulatedCapturer::Observer {
   public:
    // Called to deliver more audio data as |interleaved_samples|, which
    // contains |num_samples| tuples (i.e., multiply by the number of channels
    // to determine the number of array elements). |capture_time| is used to
    // synchronize the play-out of the first audio sample with respect to video
    // frames.
    virtual void OnAudioData(const float* interleaved_samples,
                             int num_samples,
                             Clock::time_point capture_time) = 0;

   protected:
    ~Client() override;
  };

  // Constructor: |num_channels| and |sample_rate| specify the required audio
  // format. If necessary, audio from the file will be resampled to match the
  // required format.
  SimulatedAudioCapturer(Environment* environment,
                         const char* path,
                         int num_channels,
                         int sample_rate,
                         Clock::time_point start_time,
                         Client* client);

  ~SimulatedAudioCapturer() final;

 private:
  // Examines the audio format of the given |frame|, and ensures the resampler
  // is initialized to take that as input.
  bool EnsureResamplerIsInitializedFor(const AVFrame& frame);

  // Resamples the current |SimulatedCapturer::decoded_frame()| into the
  // required output format/channels/rate. The result is stored in
  // |resampled_audio_| for the next DeliverDataToClient() call.
  absl::optional<Clock::duration> ProcessDecodedFrame(
      const AVFrame& decoded_frame) final;

  // Called at the moment Client::OnAudioData() should be called to pass the
  // |resampled_audio_|.
  void DeliverDataToClient(const AVFrame& decoded_frame,
                           Clock::time_point capture_time) final;

  const int num_channels_;  // Output number of channels.
  const int sample_rate_;   // Output sample rate.
  Client* const client_;

  const SwrContextUniquePtr resampler_;

  // Current resampler input audio parameters.
  AVSampleFormat input_sample_format_ = AV_SAMPLE_FMT_NONE;
  int input_sample_rate_;
  uint64_t input_channel_layout_;  // Opaque value used by resampler library.

  std::vector<float> resampled_audio_;
};

// Emits the primary video stream from a file.
class SimulatedVideoCapturer final : public SimulatedCapturer {
 public:
  class Client : public SimulatedCapturer::Observer {
   public:
    // Called to deliver the next video |frame|, which is always in I420 format.
    // |capture_time| is used to synchronize the play-out of the video frame
    // with respect to the audio track.
    virtual void OnVideoFrame(const AVFrame& frame,
                              Clock::time_point capture_time) = 0;

   protected:
    ~Client() override;
  };

  SimulatedVideoCapturer(Environment* environment,
                         const char* path,
                         Clock::time_point start_time,
                         Client* client);

  ~SimulatedVideoCapturer() final;

 private:
  Client* const client_;

  // Sets up the decoder to produce I420 format output.
  void SetAdditionalDecoderParameters(AVCodecContext* decoder_context) final;

  // Called at the moment Client::OnVideoFrame() should be called to provide the
  // next video frame.
  void DeliverDataToClient(const AVFrame& decoded_frame,
                           Clock::time_point capture_time) final;
};

}  // namespace cast
}  // namespace openscreen

#endif  // CAST_STANDALONE_SENDER_SIMULATED_CAPTURER_H_