| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CAST_STREAMING_PUBLIC_RECEIVER_H_ |
| #define CAST_STREAMING_PUBLIC_RECEIVER_H_ |
| |
| #include <chrono> |
| |
| #include "cast/streaming/public/encoded_frame.h" |
| #include "cast/streaming/public/session_config.h" |
| #include "cast/streaming/ssrc.h" |
| #include "platform/api/time.h" |
| #include "platform/base/error.h" |
| #include "platform/base/span.h" |
| |
| namespace openscreen::cast { |
| |
| // The Cast Streaming Receiver, a peer corresponding to some Cast Streaming |
| // Sender at the other end of a network link. |
| // |
| // Cast Streaming is a transport protocol which divides up the frames for one |
| // media stream (e.g., audio or video) into multiple RTP packets containing an |
| // encrypted payload. The Receiver is the peer responsible for collecting the |
| // RTP packets, decrypting the payload, and re-assembling a frame that can be |
| // passed to a decoder and played out. |
| // |
| // A Sender ↔ Receiver pair is used to transport each media stream. Typically, |
| // there are two pairs in a normal system, one for the audio stream and one for |
| // video stream. A local player is responsible for synchronizing the playout of |
| // the frames of each stream to achieve lip-sync. See the discussion in |
| // encoded_frame.h for how the `reference_time` and `rtp_timestamp` of the |
| // EncodedFrames are used to achieve this. |
| // |
| // See the Receiver Demo app for a reference implementation that both shows and |
| // explains how Receivers are properly configured and started, integrated with a |
| // decoder, and the resulting decoded media is played out. The |
| // Receiver::Consumer is implemented by the SDLPlayerBase class in |
| // //cast/standalone_receiver/sdl_player_base.h. |
| // |
| // Internally, a queue of complete and partially-received frames is maintained. |
| // The queue is a circular queue of FrameCollectors that each maintain the |
| // individual receive state of each in-flight frame. There are three conceptual |
| // "pointers" that indicate what assumptions and operations are made on certain |
| // ranges of frames in the queue: |
| // |
| // 1. Latest Frame Expected: The FrameId of the latest frame whose existence |
| // is known to this Receiver. This is the highest FrameId seen in any |
| // successfully-parsed RTP packet. |
| // 2. Checkpoint Frame: Indicates that all of the RTP packets for all frames |
| // up to and including the one having this FrameId have been successfully |
| // received and processed. |
| // 3. Last Frame Consumed: The FrameId of last frame consumed (see |
| // ConsumeNextFrame()). Once a frame is consumed, all internal resources |
| // related to the frame can be freed and/or reused for later frames. |
| class Receiver { |
| public: |
| class Consumer { |
| public: |
| virtual ~Consumer(); |
| |
| // Called whenever one or more frames have become ready for consumption. The |
| // `next_frame_buffer_size` argument is identical to the result of calling |
| // AdvanceToNextFrame(), and so the Consumer only needs to prepare a buffer |
| // and call ConsumeNextFrame(). It may then call AdvanceToNextFrame() to |
| // check whether there are any more frames ready, but this is not mandatory. |
| // See usage example in SDLPlayerBase::OnFramesReady. |
| virtual void OnFramesReady(size_t next_frame_buffer_size) = 0; |
| }; |
| |
| Receiver(); |
| virtual ~Receiver(); |
| |
| // The session configuration for this sender. The configuration is generated |
| // from the offer/answer exchange, and includes critical information like the |
| // RTP timebase, SSRCs for sending and receiving, and the AES configuration. |
| virtual const SessionConfig& config() const = 0; |
| |
| // Set the Consumer receiving notifications when new frames are ready for |
| // consumption. Frames received before this method is called will remain in |
| // the queue indefinitely. The `consumer` pointer is expected to remain valid |
| // for the lifetime of the Receiver (or until SetConsumer() is called again |
| // with a new value, either a new consumer or nullptr). |
| virtual void SetConsumer(Consumer* consumer) = 0; |
| |
| // Sets how much time the consumer will need to decode/buffer/render/etc., and |
| // otherwise fully process a frame for on-time playback. This information is |
| // used by the Receiver to decide whether to skip past frames that have |
| // arrived too late, as well as adjust the reference time of the frame to |
| // factor in the player processing time -- resulting in the frame being |
| // scheduled for playback earlier and decreasing total playout delay. This |
| // method can be called repeatedly to make adjustments based on changing |
| // environmental conditions. It is HIGHLY recommended that consumers of this |
| // API provide a proper processing time, otherwise there may be significantly |
| // larger playout delays. |
| // |
| // Default setting: kDefaultPlayerProcessingTime |
| virtual void SetPlayerProcessingTime(Clock::duration needed_time) = 0; |
| |
| // Called by the consumer to report that a frame has been played out. This is |
| // used to report playback statistics to the sender. |
| // |
| // NOTE: the consumer has until `kMaxUnackedFrames` additional frames have |
| // been consumed *after* `frame_id` to report the playout event, otherwise a |
| // kParameterOutOfRange error will be returned. |
| virtual Error ReportPlayoutEvent(FrameId frame_id, |
| RtpTimeTicks rtp_timestamp, |
| Clock::time_point playout_time) = 0; |
| |
| // Propagates a "picture loss indicator" notification to the Sender, |
| // requesting a key frame so that decode/playout can recover. It is safe to |
| // call this redundantly. The Receiver will clear the picture loss condition |
| // automatically, once a key frame is received (i.e., before |
| // ConsumeNextFrame() is called to access it). |
| virtual void RequestKeyFrame() = 0; |
| |
| // Advances to the next frame ready for consumption. This may skip-over |
| // incomplete frames that will not play out on-time; but only if there are |
| // completed frames further down the queue that have no dependency |
| // relationship with them (e.g., key frames). |
| // |
| // This method returns std::nullopt if there is not currently a frame ready |
| // for consumption. The caller should wait for a Consumer::OnFramesReady() |
| // notification before trying again. Otherwise, the number of bytes of encoded |
| // data is returned, and the caller should use this to ensure the buffer it |
| // passes to ConsumeNextFrame() is large enough. |
| virtual std::optional<size_t> AdvanceToNextFrame() = 0; |
| |
| // Returns the next frame, both metadata and payload data. The Consumer calls |
| // this method after being notified via OnFramesReady(), and it can also call |
| // this whenever AdvanceToNextFrame() indicates another frame is ready. |
| // `buffer` must point to a sufficiently-sized buffer (enforced by CHECK) that |
| // will be populated with the frame's payload data. The returned frame's |
| // `data` will be set to the portion of the buffer that was populated. |
| virtual EncodedFrame ConsumeNextFrame(ByteBuffer buffer) = 0; |
| |
| // The default "player processing time" amount. See SetPlayerProcessingTime(). |
| // This value is based on real world experimentation, however may vary |
| // widely depending on the platform of the receiver and what type of |
| // decoder is available. |
| static constexpr std::chrono::milliseconds kDefaultPlayerProcessingTime{50}; |
| }; |
| |
| } // namespace openscreen::cast |
| |
| #endif // CAST_STREAMING_PUBLIC_RECEIVER_H_ |