| // Copyright 2017 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 REMOTING_CODEC_WEBRTC_VIDEO_ENCODER_GPU_H_ |
| #define REMOTING_CODEC_WEBRTC_VIDEO_ENCODER_GPU_H_ |
| |
| #include "base/containers/flat_map.h" |
| #include "base/memory/shared_memory_mapping.h" |
| #include "base/memory/unsafe_shared_memory_region.h" |
| #include "build/build_config.h" |
| #include "media/video/video_encode_accelerator.h" |
| #include "remoting/codec/encoder_bitrate_filter.h" |
| #include "remoting/codec/webrtc_video_encoder.h" |
| #include "remoting/codec/webrtc_video_encoder_selector.h" |
| |
| #if defined(OS_WIN) |
| #include "base/win/scoped_com_initializer.h" |
| #endif |
| |
| namespace remoting { |
| |
| // A WebrtcVideoEncoder implementation utilizing the VideoEncodeAccelerator |
| // framework to do hardware-accelerated encoding. |
| // A brief explanation of how this class is initialized: |
| // 1. An instance of WebrtcVideoEncoderGpu is created using a static method, for |
| // example CreateForH264(). The state at this point is UNINITIALIZED. |
| // 2. On the first encode call, the WebrtcVideoEncoder saves the incoming |
| // DesktopFrame's dimensions and thunks the encode. Before returning, it |
| // calls BeginInitialization(). |
| // 3. In BeginInitialization(), the WebrtcVideoEncoderGpu constructs the |
| // VideoEncodeAccelerator using the saved dimensions from the DesktopFrame. |
| // If the VideoEncodeAccelerator is constructed |
| // successfully, the state is then INITIALIZING. If not, the state is |
| // INIITALIZATION_ERROR. |
| // 4. Some time later, the VideoEncodeAccelerator sets itself up and is ready |
| // to encode. At this point, it calls our WebrtcVideoEncoderGpu's |
| // RequireBitstreamBuffers() method. Once bitstream buffers are allocated, |
| // the state is INITIALIZED. |
| class WebrtcVideoEncoderGpu : public WebrtcVideoEncoder, |
| public media::VideoEncodeAccelerator::Client { |
| public: |
| static std::unique_ptr<WebrtcVideoEncoder> CreateForH264(); |
| static bool IsSupportedByH264( |
| const WebrtcVideoEncoderSelector::Profile& profile); |
| |
| ~WebrtcVideoEncoderGpu() override; |
| |
| // WebrtcVideoEncoder interface. |
| void Encode(std::unique_ptr<webrtc::DesktopFrame> frame, |
| const FrameParams& params, |
| WebrtcVideoEncoder::EncodeCallback done) override; |
| |
| // VideoEncodeAccelerator::Client interface. |
| void RequireBitstreamBuffers(unsigned int input_count, |
| const gfx::Size& input_coded_size, |
| size_t output_buffer_size) override; |
| void BitstreamBufferReady( |
| int32_t bitstream_buffer_id, |
| const media::BitstreamBufferMetadata& metadata) override; |
| void NotifyError(media::VideoEncodeAccelerator::Error error) override; |
| |
| private: |
| enum State { UNINITIALIZED, INITIALIZING, INITIALIZED, INITIALIZATION_ERROR }; |
| |
| struct OutputBuffer { |
| base::UnsafeSharedMemoryRegion region; |
| base::WritableSharedMemoryMapping mapping; |
| |
| bool IsValid(); |
| }; |
| |
| explicit WebrtcVideoEncoderGpu(media::VideoCodecProfile codec_profile); |
| |
| void BeginInitialization(); |
| |
| void UseOutputBitstreamBufferId(int32_t bitstream_buffer_id); |
| |
| void RunAnyPendingEncode(); |
| |
| #if defined(OS_WIN) |
| // This object is required by Chromium to ensure proper init/uninit of COM on |
| // this thread. The guidance is to match the lifetime of this object to the |
| // lifetime of the thread if possible. Since we are still experimenting with |
| // H.264 and run the encoder on a different thread, we use an object-lifetime |
| // scoped instance for now. |
| // TODO(joedow): Use a COMscoped Autothread (or run in a separate process) if |
| // H.264 becomes a common use case for us. |
| base::win::ScopedCOMInitializer scoped_com_initializer_; |
| #endif |
| |
| State state_; |
| |
| // Only after the first encode request do we know how large the incoming |
| // frames will be. Thus, we initialize after the first encode request, |
| // postponing the encode until the encoder has been initialized. |
| base::OnceClosure pending_encode_; |
| |
| std::unique_ptr<media::VideoEncodeAccelerator> video_encode_accelerator_; |
| |
| base::TimeDelta previous_timestamp_; |
| |
| media::VideoCodecProfile codec_profile_; |
| |
| // Shared memory with which the VEA transfers output to WebrtcVideoEncoderGpu |
| std::vector<std::unique_ptr<OutputBuffer>> output_buffers_; |
| |
| // TODO(gusss): required_input_frame_count_ is currently unused; evaluate |
| // whether or not it's actually needed. This variable represents the number of |
| // frames needed by the VEA before it can start producing output. |
| // This may be important in the future, as the current frame scheduler for CRD |
| // encodes one frame at a time - it will not send the next frame in until the |
| // previous frame has been returned. It may need to be "tricked" into sending |
| // in a number of start frames. |
| // However, initial tests showed that, even if the VEA requested a number of |
| // initial frames, it still encoded and returned the first frame before |
| // getting the second frame. This may be platform-dependent - these tests were |
| // done with the MediaFoundationVideoEncodeAccelerator for Windows. |
| unsigned int required_input_frame_count_; |
| |
| gfx::Size input_coded_size_; |
| gfx::Size input_visible_size_; |
| |
| size_t output_buffer_size_; |
| |
| base::flat_map<base::TimeDelta, WebrtcVideoEncoder::EncodeCallback> |
| callbacks_; |
| |
| EncoderBitrateFilter bitrate_filter_; |
| |
| base::WeakPtrFactory<WebrtcVideoEncoderGpu> weak_factory_{this}; |
| |
| DISALLOW_COPY_AND_ASSIGN(WebrtcVideoEncoderGpu); |
| }; |
| |
| } // namespace remoting |
| |
| #endif // REMOTING_CODEC_WEBRTC_VIDEO_ENCODER_GPU_H_ |