182 lines
6.8 KiB
C++
182 lines
6.8 KiB
C++
// 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_
|