| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CHROMEOS_ASH_SERVICES_RECORDING_WEBM_ENCODER_MUXER_H_ |
| #define CHROMEOS_ASH_SERVICES_RECORDING_WEBM_ENCODER_MUXER_H_ |
| |
| #include <memory> |
| #include <optional> |
| |
| #include "base/containers/circular_deque.h" |
| #include "base/containers/queue.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/thread_annotations.h" |
| #include "base/threading/sequence_bound.h" |
| #include "base/time/time.h" |
| #include "base/types/pass_key.h" |
| #include "chromeos/ash/services/recording/recording_encoder.h" |
| #include "media/audio/audio_opus_encoder.h" |
| #include "media/base/audio_bus.h" |
| #include "media/base/audio_parameters.h" |
| #include "media/muxers/muxer_timestamp_adapter.h" |
| #include "media/video/vpx_video_encoder.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "ui/gfx/geometry/size.h" |
| |
| namespace base { |
| class FilePath; |
| } |
| |
| namespace recording { |
| |
| namespace mojom { |
| class DriveFsQuotaDelegate; |
| } // namespace mojom |
| |
| // 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 WebmEncoderMuxer : public RecordingEncoder { |
| private: |
| using PassKey = base::PassKey<WebmEncoderMuxer>; |
| |
| public: |
| // 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. |
| // the webm muxer chunks will be written directly to a file at the given |
| // |webm_file_path|. |
| // |on_failure_callback| will be called to inform the owner of this object of |
| // a failure, after which all subsequent calls to EncodeVideo() and |
| // EncodeAudio() will be ignored. |
| // |
| // By default, |on_failure_callback| will be called on the same sequence of |
| // |blocking_task_runner| (unless the caller binds the given callback to a |
| // different sequence by means of base::BindPostTask()). |
| static base::SequenceBound<RecordingEncoder> Create( |
| scoped_refptr<base::SequencedTaskRunner> blocking_task_runner, |
| const media::VideoEncoder::Options& video_encoder_options, |
| const media::AudioParameters* audio_input_params, |
| mojo::PendingRemote<mojom::DriveFsQuotaDelegate> drive_fs_quota_delegate, |
| const base::FilePath& webm_file_path, |
| OnFailureCallback on_failure_callback); |
| |
| // Creates and returns an object that specifies the capabilities of this |
| // encoder. |
| static std::unique_ptr<Capabilities> CreateCapabilities(); |
| |
| WebmEncoderMuxer( |
| PassKey, |
| const media::VideoEncoder::Options& video_encoder_options, |
| const media::AudioParameters* audio_input_params, |
| mojo::PendingRemote<mojom::DriveFsQuotaDelegate> drive_fs_quota_delegate, |
| const base::FilePath& webm_file_path, |
| OnFailureCallback on_failure_callback); |
| WebmEncoderMuxer(const WebmEncoderMuxer&) = delete; |
| WebmEncoderMuxer& operator=(const WebmEncoderMuxer&) = delete; |
| ~WebmEncoderMuxer() override; |
| |
| // RecordingEncoder: |
| void InitializeVideoEncoder( |
| const media::VideoEncoder::Options& video_encoder_options) override; |
| void EncodeVideo(scoped_refptr<media::VideoFrame> frame) override; |
| void EncodeRgbVideo(RgbVideoFrame rgb_video_frame) override; |
| EncodeAudioCallback GetEncodeAudioCallback() override; |
| void FlushAndFinalize(base::OnceClosure on_done) override; |
| |
| private: |
| struct AudioFrame { |
| AudioFrame(std::unique_ptr<media::AudioBus>, base::TimeTicks); |
| AudioFrame(AudioFrame&&); |
| ~AudioFrame(); |
| |
| std::unique_ptr<media::AudioBus> bus; |
| base::TimeTicks capture_time; |
| }; |
| |
| // Gathers the video frame data needed when the frame is encoded and ready to |
| // be submitted to the muxer. |
| struct EncodedVideoFrameParams { |
| // The received video frame's reference time. See |
| // `media::VideoFrameMetadata::reference_time`. |
| base::TimeTicks frame_reference_time; |
| |
| // The size of the visible region of the received video frame. Note that the |
| // visible rect sizes may change from frame to frame (e.g. when recording a |
| // window, and the window gets resized). |
| gfx::Size visible_rect_size; |
| }; |
| |
| // Creates and initializes the audio encoder. |
| void InitializeAudioEncoder(const media::AudioEncoder::Options& options); |
| |
| // Called when the audio encoder is initialized to provide the |status| of |
| // the initialization. |
| void OnAudioEncoderInitialized(media::EncoderStatus status); |
| |
| // 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::VpxVideoEncoder* encoder, |
| media::EncoderStatus status); |
| |
| // Encodes and muxes the given audio frames in `audio_bus` captured at |
| // `capture_time`. If `did_failure_occur()` is true, all `audio_bus`s will be |
| // ignored. |
| // It is bound to the callback returned by `GetEncodeAudioCallback()`. |
| void EncodeAudio(std::unique_ptr<media::AudioBus> audio_bus, |
| base::TimeTicks capture_time); |
| |
| // Performs the actual encoding of the given audio |frame|. It should never be |
| // called before the audio encoder is initialized. Audio frames received |
| // before initialization should be added to |pending_audio_frames_| and |
| // handled once initialization is complete. |
| void EncodeAudioImpl(AudioFrame frame); |
| |
| // 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 provide the encoded video frame `output`, |
| // which will then be sent to the muxer. |
| void OnVideoEncoderOutput( |
| media::VideoEncoderOutput output, |
| std::optional<media::VideoEncoder::CodecDescription> codec_description); |
| |
| // Called by the audio encoder to provide the |encoded_audio|. |
| void OnAudioEncoded( |
| media::EncodedAudioBuffer encoded_audio, |
| std::optional<media::AudioEncoder::CodecDescription> codec_description); |
| |
| // Called when the audio encoder flushes all its buffered frames, at which |
| // point we can flush the video encoder. |on_done| will be passed to |
| // OnVideoEncoderFlushed() |
| void OnAudioEncoderFlushed(base::OnceClosure on_done, |
| media::EncoderStatus status); |
| |
| // 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::EncoderStatus status); |
| |
| std::unique_ptr<media::VpxVideoEncoder> video_encoder_ |
| GUARDED_BY_CONTEXT(sequence_checker_); |
| |
| std::unique_ptr<media::AudioOpusEncoder> audio_encoder_ |
| GUARDED_BY_CONTEXT(sequence_checker_); |
| |
| media::MuxerTimestampAdapter muxer_adapter_ |
| 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_); |
| |
| // Holds audio frames that were received before the audio encoder is |
| // initialized, so that they can be processed once initialization is complete. |
| base::circular_deque<AudioFrame> pending_audio_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 parameters of the encoded video frames in the same |
| // order of their encoding. These parameters are used when submitting the |
| // video encoder output to the muxer. See `OnVideoEncoderOutput()`. |
| base::queue<EncodedVideoFrameParams> encoded_video_params_ |
| GUARDED_BY_CONTEXT(sequence_checker_); |
| |
| // True once video encoder is initialized successfully. |
| bool is_video_encoder_initialized_ GUARDED_BY_CONTEXT(sequence_checker_) = |
| false; |
| |
| // True once audio encoder is initialized successfully. |
| bool is_audio_encoder_initialized_ GUARDED_BY_CONTEXT(sequence_checker_) = |
| false; |
| |
| base::WeakPtrFactory<WebmEncoderMuxer> weak_ptr_factory_ |
| GUARDED_BY_CONTEXT(sequence_checker_){this}; |
| }; |
| |
| } // namespace recording |
| |
| #endif // CHROMEOS_ASH_SERVICES_RECORDING_WEBM_ENCODER_MUXER_H_ |