xref: /aosp_15_r20/external/openscreen/cast/standalone_receiver/sdl_player_base.h (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef CAST_STANDALONE_RECEIVER_SDL_PLAYER_BASE_H_
6 #define CAST_STANDALONE_RECEIVER_SDL_PLAYER_BASE_H_
7 
8 #include <stdint.h>
9 
10 #include <functional>
11 #include <map>
12 #include <string>
13 
14 #include "cast/standalone_receiver/decoder.h"
15 #include "cast/standalone_receiver/sdl_glue.h"
16 #include "cast/streaming/message_fields.h"
17 #include "cast/streaming/receiver.h"
18 #include "platform/api/task_runner.h"
19 #include "platform/api/time.h"
20 #include "platform/base/error.h"
21 
22 namespace openscreen {
23 namespace cast {
24 
25 // Common base class that consumes frames from a Receiver, decodes them, and
26 // plays them out via the appropriate SDL subsystem. Subclasses implement the
27 // specifics, based on the type of media (audio or video).
28 class SDLPlayerBase : public Receiver::Consumer, public Decoder::Client {
29  public:
30   ~SDLPlayerBase() override;
31 
32   // Returns OK unless a fatal error has occurred.
error_status()33   const Error& error_status() const { return error_status_; }
34 
35  protected:
36   // Current player state, which is used to determine what to render/present,
37   // and how frequently.
38   enum PlayerState {
39     kWaitingForFirstFrame,  // Render silent "blue splash" screen at idle FPS.
40     kScheduledToPresent,    // Present new content at an exact time point.
41     kPresented,             // Continue presenting same content at idle FPS.
42     kError,                 // Render silent "red splash" screen at idle FPS.
43   };
44 
45   // A decoded frame and its target presentation time.
46   struct PresentableFrame {
47     Clock::time_point presentation_time;
48     AVFrameUniquePtr decoded_frame;
49 
50     PresentableFrame();
51     ~PresentableFrame();
52     PresentableFrame(PresentableFrame&& other) noexcept;
53     PresentableFrame& operator=(PresentableFrame&& other) noexcept;
54   };
55 
56   // |error_callback| is run only if a fatal error occurs, at which point the
57   // player has halted and set |error_status()|. |media_type| should be "audio"
58   // or "video" (only used when logging).
59   SDLPlayerBase(ClockNowFunctionPtr now_function,
60                 TaskRunner* task_runner,
61                 Receiver* receiver,
62                 const std::string& codec_name,
63                 std::function<void()> error_callback,
64                 const char* media_type);
65 
state()66   PlayerState state() const { return state_; }
67 
68   // Called back from either |decoder_| or a player subclass to handle a fatal
69   // error event.
70   void OnFatalError(std::string message) final;
71 
72   // Renders the |frame| and returns its [possibly adjusted] presentation time.
73   virtual ErrorOr<Clock::time_point> RenderNextFrame(
74       const PresentableFrame& frame) = 0;
75 
76   // Called to render when the player has no new content, and returns true if a
77   // Present() is necessary. |frame| may be null, if it is not available. This
78   // method can be called before the first frame, after any frame, or after a
79   // fatal error has occurred.
80   virtual bool RenderWhileIdle(const PresentableFrame* frame) = 0;
81 
82   // Presents the rendering from the last call to RenderNextFrame() or
83   // RenderWhileIdle().
84   virtual void Present() = 0;
85 
86  private:
87   struct PendingFrame : public PresentableFrame {
88     Clock::time_point start_time;
89 
90     PendingFrame();
91     ~PendingFrame();
92     PendingFrame(PendingFrame&& other) noexcept;
93     PendingFrame& operator=(PendingFrame&& other) noexcept;
94   };
95 
96   // Receiver::Consumer implementation.
97   void OnFramesReady(int next_frame_buffer_size) final;
98 
99   // Determine the presentation time of the frame. Ideally, this will occur
100   // based on the time progression of the media, given by the RTP timestamps.
101   // However, if this falls too far out-of-sync with the system reference clock,
102   // re-synchronize, possibly causing user-visible "jank."
103   Clock::time_point ResyncAndDeterminePresentationTime(
104       const EncodedFrame& frame);
105 
106   // AVCodecDecoder::Client implementation. These are called-back from
107   // |decoder_| to provide results.
108   void OnFrameDecoded(FrameId frame_id, const AVFrame& frame) final;
109   void OnDecodeError(FrameId frame_id, std::string message) final;
110 
111   // Calls RenderNextFrame() on the next available decoded frame, and schedules
112   // its presentation. If no decoded frame is available, RenderWhileIdle() is
113   // called instead.
114   void RenderAndSchedulePresentation();
115 
116   // Schedules an explicit check to see if more frames are ready for
117   // consumption. Normally, the Receiver will notify this Consumer when more
118   // frames are ready. However, there are cases where prior notifications were
119   // ignored because there were too many frames in the player's pipeline. Thus,
120   // whenever frames are removed from the pipeline, this method should be
121   // called.
122   void ResumeDecoding();
123 
124   // Called whenever a frame has been decoded, presentation of a prior frame has
125   // completed, and/or the player has encountered a state change that might
126   // require rendering/presenting a different output.
127   void ResumeRendering();
128 
129   const ClockNowFunctionPtr now_;
130   Receiver* const receiver_;
131   std::function<void()> error_callback_;  // Run once by OnFatalError().
132   const char* const media_type_;          // For logging only.
133 
134   // Set to the error code that placed the player in a fatal error state.
135   Error error_status_;
136 
137   // Current player state, which is used to determine what to render/present,
138   // and how frequently.
139   PlayerState state_ = kWaitingForFirstFrame;
140 
141   // Queue of frames currently being decoded and decoded frames awaiting
142   // rendering.
143 
144   std::map<FrameId, PendingFrame> frames_to_render_;
145 
146   // Buffer for holding EncodedFrame::data.
147   Decoder::Buffer buffer_;
148 
149   // Associates a RTP timestamp with a local clock time point. This is updated
150   // whenever the media (RTP) timestamps drift too much away from the rate at
151   // which the local clock ticks. This is important for A/V synchronization.
152   RtpTimeTicks last_sync_rtp_timestamp_{};
153   Clock::time_point last_sync_reference_time_{};
154 
155   Decoder decoder_;
156 
157   // The decoded frame to be rendered/presented.
158   PendingFrame current_frame_;
159 
160   // A cumulative moving average of recent single-frame processing times
161   // (consume + decode + render). This is passed to the Cast Receiver so that it
162   // can determine when to drop late frames.
163   Clock::duration recent_processing_time_{};
164 
165   // Alarms that execute the various stages of the player pipeline at certain
166   // times.
167   Alarm decode_alarm_;
168   Alarm render_alarm_;
169   Alarm presentation_alarm_;
170 
171   // Maximum number of frames in the decode/render pipeline. This limit is about
172   // making sure the player uses resources efficiently: It is better for frames
173   // to remain in the Receiver's queue until this player is ready to process
174   // them.
175   static constexpr int kMaxFramesInPipeline = 8;
176 };
177 
178 }  // namespace cast
179 }  // namespace openscreen
180 
181 #endif  // CAST_STANDALONE_RECEIVER_SDL_PLAYER_BASE_H_
182