aboutsummaryrefslogtreecommitdiff
path: root/cast/standalone_receiver/sdl_player_base.h
blob: 4e268e8ade3598476a21877d98a916cb93230485 (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
// Copyright 2019 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_RECEIVER_SDL_PLAYER_BASE_H_
#define CAST_STANDALONE_RECEIVER_SDL_PLAYER_BASE_H_

#include <stdint.h>

#include <functional>
#include <map>
#include <string>

#include "cast/standalone_receiver/decoder.h"
#include "cast/standalone_receiver/sdl_glue.h"
#include "cast/streaming/message_fields.h"
#include "cast/streaming/receiver.h"
#include "platform/api/task_runner.h"
#include "platform/api/time.h"
#include "platform/base/error.h"

namespace openscreen {
namespace cast {

// Common base class that consumes frames from a Receiver, decodes them, and
// plays them out via the appropriate SDL subsystem. Subclasses implement the
// specifics, based on the type of media (audio or video).
class SDLPlayerBase : public Receiver::Consumer, public Decoder::Client {
 public:
  ~SDLPlayerBase() override;

  // Returns OK unless a fatal error has occurred.
  const Error& error_status() const { return error_status_; }

 protected:
  // Current player state, which is used to determine what to render/present,
  // and how frequently.
  enum PlayerState {
    kWaitingForFirstFrame,  // Render silent "blue splash" screen at idle FPS.
    kScheduledToPresent,    // Present new content at an exact time point.
    kPresented,             // Continue presenting same content at idle FPS.
    kError,                 // Render silent "red splash" screen at idle FPS.
  };

  // A decoded frame and its target presentation time.
  struct PresentableFrame {
    Clock::time_point presentation_time;
    AVFrameUniquePtr decoded_frame;

    PresentableFrame();
    ~PresentableFrame();
    PresentableFrame(PresentableFrame&& other) noexcept;
    PresentableFrame& operator=(PresentableFrame&& other) noexcept;
  };

  // |error_callback| is run only if a fatal error occurs, at which point the
  // player has halted and set |error_status()|. |media_type| should be "audio"
  // or "video" (only used when logging).
  SDLPlayerBase(ClockNowFunctionPtr now_function,
                TaskRunner* task_runner,
                Receiver* receiver,
                const std::string& codec_name,
                std::function<void()> error_callback,
                const char* media_type);

  PlayerState state() const { return state_; }

  // Called back from either |decoder_| or a player subclass to handle a fatal
  // error event.
  void OnFatalError(std::string message) final;

  // Renders the |frame| and returns its [possibly adjusted] presentation time.
  virtual ErrorOr<Clock::time_point> RenderNextFrame(
      const PresentableFrame& frame) = 0;

  // Called to render when the player has no new content, and returns true if a
  // Present() is necessary. |frame| may be null, if it is not available. This
  // method can be called before the first frame, after any frame, or after a
  // fatal error has occurred.
  virtual bool RenderWhileIdle(const PresentableFrame* frame) = 0;

  // Presents the rendering from the last call to RenderNextFrame() or
  // RenderWhileIdle().
  virtual void Present() = 0;

 private:
  struct PendingFrame : public PresentableFrame {
    Clock::time_point start_time;

    PendingFrame();
    ~PendingFrame();
    PendingFrame(PendingFrame&& other) noexcept;
    PendingFrame& operator=(PendingFrame&& other) noexcept;
  };

  // Receiver::Consumer implementation.
  void OnFramesReady(int next_frame_buffer_size) final;

  // Determine the presentation time of the frame. Ideally, this will occur
  // based on the time progression of the media, given by the RTP timestamps.
  // However, if this falls too far out-of-sync with the system reference clock,
  // re-synchronize, possibly causing user-visible "jank."
  Clock::time_point ResyncAndDeterminePresentationTime(
      const EncodedFrame& frame);

  // AVCodecDecoder::Client implementation. These are called-back from
  // |decoder_| to provide results.
  void OnFrameDecoded(FrameId frame_id, const AVFrame& frame) final;
  void OnDecodeError(FrameId frame_id, std::string message) final;

  // Calls RenderNextFrame() on the next available decoded frame, and schedules
  // its presentation. If no decoded frame is available, RenderWhileIdle() is
  // called instead.
  void RenderAndSchedulePresentation();

  // Schedules an explicit check to see if more frames are ready for
  // consumption. Normally, the Receiver will notify this Consumer when more
  // frames are ready. However, there are cases where prior notifications were
  // ignored because there were too many frames in the player's pipeline. Thus,
  // whenever frames are removed from the pipeline, this method should be
  // called.
  void ResumeDecoding();

  // Called whenever a frame has been decoded, presentation of a prior frame has
  // completed, and/or the player has encountered a state change that might
  // require rendering/presenting a different output.
  void ResumeRendering();

  const ClockNowFunctionPtr now_;
  Receiver* const receiver_;
  std::function<void()> error_callback_;  // Run once by OnFatalError().
  const char* const media_type_;          // For logging only.

  // Set to the error code that placed the player in a fatal error state.
  Error error_status_;

  // Current player state, which is used to determine what to render/present,
  // and how frequently.
  PlayerState state_ = kWaitingForFirstFrame;

  // Queue of frames currently being decoded and decoded frames awaiting
  // rendering.

  std::map<FrameId, PendingFrame> frames_to_render_;

  // Buffer for holding EncodedFrame::data.
  Decoder::Buffer buffer_;

  // Associates a RTP timestamp with a local clock time point. This is updated
  // whenever the media (RTP) timestamps drift too much away from the rate at
  // which the local clock ticks. This is important for A/V synchronization.
  RtpTimeTicks last_sync_rtp_timestamp_{};
  Clock::time_point last_sync_reference_time_{};

  Decoder decoder_;

  // The decoded frame to be rendered/presented.
  PendingFrame current_frame_;

  // A cumulative moving average of recent single-frame processing times
  // (consume + decode + render). This is passed to the Cast Receiver so that it
  // can determine when to drop late frames.
  Clock::duration recent_processing_time_{};

  // Alarms that execute the various stages of the player pipeline at certain
  // times.
  Alarm decode_alarm_;
  Alarm render_alarm_;
  Alarm presentation_alarm_;

  // Maximum number of frames in the decode/render pipeline. This limit is about
  // making sure the player uses resources efficiently: It is better for frames
  // to remain in the Receiver's queue until this player is ready to process
  // them.
  static constexpr int kMaxFramesInPipeline = 8;
};

}  // namespace cast
}  // namespace openscreen

#endif  // CAST_STANDALONE_RECEIVER_SDL_PLAYER_BASE_H_