blob: 9c714a66f019ce33f72f30ffec138f88063a97e6 [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 ASH_SERVICES_RECORDING_RECORDING_ENCODER_MUXER_H_
#define ASH_SERVICES_RECORDING_RECORDING_ENCODER_MUXER_H_
#include <memory>
#include "base/callback_forward.h"
#include "base/containers/circular_deque.h"
#include "base/containers/queue.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "base/thread_annotations.h"
#include "base/threading/sequence_bound.h"
#include "media/audio/audio_opus_encoder.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_parameters.h"
#include "media/muxers/webm_muxer.h"
#include "media/video/vpx_video_encoder.h"
#include "ui/gfx/geometry/size.h"
namespace media {
class VideoFrame;
} // namespace media
namespace recording {
// The type of the failures that can happen while encoding.
enum class FailureType {
kEncoderInitialization,
kEncoding,
};
// Defines a callback type to notify the user of RecordingEncoderMuxer of a
// failure while encoding audio or video frames.
// TODO(afakhry): It's possible we don't need |type| or |for_video|.
using FailureCallback =
base::OnceCallback<void(FailureType type, bool for_video)>;
// Encapsulates encoding and muxing audio and video frame. An instance of this
// object can only be interacted with via a |base::SequenceBound| wrapper, which
// guarantees all encoding and muxing operations as well as destruction of the
// instance are done on the sequenced |blocking_task_runner| given to Create().
// This prevents expensive encoding and muxing operations from blocking the main
// thread of the recording service, on which the video frames are delivered.
//
// This object performs VP8 video encoding and Opus audio encoding, and mux the
// audio and video encoded frames into a Webm container.
class RecordingEncoderMuxer {
public:
RecordingEncoderMuxer(const RecordingEncoderMuxer&) = delete;
RecordingEncoderMuxer& operator=(const RecordingEncoderMuxer&) = delete;
// Creates an instance of this class that is bound to the given sequenced
// |blocking_task_runner| on which all operations as well as destruction will
// happen. |video_encoder_options| and |audio_input_params| will be used to
// initialize the video and audio encoders respectively.
// If |audio_input_params| is nullptr, then the service is not recording
// audio, and the muxer will be initialized accordingly.
// |muxer_output_callback| will be called on the same sequence of
// |blocking_task_runner| to provide the muxer output chunks ready to be sent
// to the recording service client.
// |on_failure_callback| will be called on the same sequence of
// |blocking_task_runner| to inform the owner of this object, after which
// all subsequent calls to EncodeVideo() and EncodeAudio() will be ignored.
static base::SequenceBound<RecordingEncoderMuxer> Create(
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
const media::VideoEncoder::Options& video_encoder_options,
const media::AudioParameters* audio_input_params,
media::WebmMuxer::WriteDataCB muxer_output_callback,
FailureCallback on_failure_callback);
bool did_failure_occur() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return !on_failure_callback_;
}
// Encodes and muxes the given video |frame|.
void EncodeVideo(scoped_refptr<media::VideoFrame> frame);
// Encodes and muxes the given audio frame in |audio_bus| captured at
// |capture_time|.
void EncodeAudio(std::unique_ptr<media::AudioBus> audio_bus,
base::TimeTicks capture_time);
// Audio and video encoders as well as the WebmMuxer may buffer several frames
// before they're processed. It is important to flush all those buffers before
// releasing this object so as not to drop the final portion of the recording.
// |on_done| will be called on the same sequence of |blocking_task_runner|
// when all remaining buffered frames have been processed and sent to
// |muxer_output_callback|.
void FlushAndFinalize(base::OnceClosure on_done);
private:
friend class base::SequenceBound<RecordingEncoderMuxer>;
RecordingEncoderMuxer(
const media::VideoEncoder::Options& video_encoder_options,
const media::AudioParameters* audio_input_params,
media::WebmMuxer::WriteDataCB muxer_output_callback,
FailureCallback on_failure_callback);
~RecordingEncoderMuxer();
// Called when the video encoder is initialized to provide the |status| of the
// initialization. If initialization failed, |on_failure_callback_| will
// be triggered.
void OnVideoEncoderInitialized(media::Status status);
// Performs the actual encoding of the given video |frame|. It should never be
// called before the video encoder is initialized. Video frames received
// before initialization should be added to |pending_video_frames_| and
// handled once initialization is complete.
void EncodeVideoImpl(scoped_refptr<media::VideoFrame> frame);
// Called by the video encoder to provided the encoded video frame |output|,
// which will then by sent to muxer.
void OnVideoEncoderOutput(
media::VideoEncoderOutput output,
base::Optional<media::VideoEncoder::CodecDescription> codec_description);
// Called by the audio encoder to provide the |encoded_audio|.
void OnAudioEncoded(media::EncodedAudioBuffer encoded_audio);
// Called when the video encoder flushes all its buffered frames, at which
// point we can flush the muxer. |on_done| will be called to signal that
// flushing is complete.
void OnVideoEncoderFlushed(base::OnceClosure on_done, media::Status status);
// Called by both the audio and video encoders to provide the |status| of
// encoding tasks.
void OnEncoderStatus(bool for_video, media::Status status);
// Notifies the owner of this object (via |on_failure_callback_|) that a
// failure of |type| has occurred during audio or video encoding depending on
// the value of |for_video|.
void NotifyFailure(FailureType type, bool for_video);
media::VpxVideoEncoder video_encoder_ GUARDED_BY_CONTEXT(sequence_checker_);
std::unique_ptr<media::AudioOpusEncoder> audio_encoder_
GUARDED_BY_CONTEXT(sequence_checker_);
media::WebmMuxer webm_muxer_ GUARDED_BY_CONTEXT(sequence_checker_);
// Holds video frames that were received before the video encoder is
// initialized, so that they can be processed once initialization is complete.
base::circular_deque<scoped_refptr<media::VideoFrame>> pending_video_frames_
GUARDED_BY_CONTEXT(sequence_checker_);
// The total number of frames that we dropped to keep the size of
// |pending_video_frames_| limited to |kMaxPendingFrames| to avoid consuming
// too much memory, or stalling the capturer since it has a maximum number of
// in-flight frames at a time.
size_t num_dropped_frames_ GUARDED_BY_CONTEXT(sequence_checker_) = 0;
// A queue containing the sizes of the visible region of the received video
// frame in the same order of their encoding. Note that the visible rect sizes
// may change from frame to frame (e.g. when recording a window, and the
// window gets resized).
base::queue<gfx::Size> video_visible_rect_sizes_
GUARDED_BY_CONTEXT(sequence_checker_);
// A callback triggered when a failure happens during encoding. Once
// triggered, this callback is null, and therefore indicates that a failure
// occurred (See did_failure_occur() above).
FailureCallback on_failure_callback_ GUARDED_BY_CONTEXT(sequence_checker_);
// True once video encoder is initialized successfully.
bool is_video_encoder_initialized_ GUARDED_BY_CONTEXT(sequence_checker_) =
false;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace recording
#endif // ASH_SERVICES_RECORDING_RECORDING_ENCODER_MUXER_H_