blob: 8d32085aa730d76c92591493b475d127a38a6f2e [file] [log] [blame]
// 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();
};
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_;
};
// 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_