1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
media / gpu / vaapi / vaapi_video_encode_accelerator.h [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_GPU_VAAPI_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_
#define MEDIA_GPU_VAAPI_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_
#include <stddef.h>
#include <stdint.h>
#include <map>
#include <memory>
#include "base/containers/queue.h"
#include "base/containers/small_map.h"
#include "base/memory/ref_counted_memory.h"
#include "base/sequence_checker.h"
#include "base/task/single_thread_task_runner.h"
#include "base/trace_event/memory_dump_provider.h"
#include "media/base/bitrate.h"
#include "media/gpu/media_gpu_export.h"
#include "media/gpu/vaapi/vaapi_utils.h"
#include "media/gpu/vaapi/vaapi_video_encoder_delegate.h"
#include "media/gpu/vaapi/vaapi_wrapper.h"
#include "media/video/video_encode_accelerator.h"
namespace base {
class SequencedTaskRunner;
class SingleThreadTaskRunner;
} // namespace base
namespace media {
// A VideoEncodeAccelerator implementation that uses VA-API
// (https://01.org/vaapi) for HW-accelerated video encode.
class MEDIA_GPU_EXPORT VaapiVideoEncodeAccelerator
: public VideoEncodeAccelerator,
public base::trace_event::MemoryDumpProvider {
public:
VaapiVideoEncodeAccelerator();
VaapiVideoEncodeAccelerator(const VaapiVideoEncodeAccelerator&) = delete;
VaapiVideoEncodeAccelerator& operator=(const VaapiVideoEncodeAccelerator&) =
delete;
~VaapiVideoEncodeAccelerator() override;
// VideoEncodeAccelerator implementation.
SupportedProfiles GetSupportedProfiles() override;
bool Initialize(const Config& config,
Client* client,
std::unique_ptr<MediaLog> media_log) override;
void Encode(scoped_refptr<VideoFrame> frame, bool force_keyframe) override;
void UseOutputBitstreamBuffer(BitstreamBuffer buffer) override;
void RequestEncodingParametersChange(
const Bitrate& bitrate,
uint32_t framerate,
const std::optional<gfx::Size>& size) override;
void RequestEncodingParametersChange(
const VideoBitrateAllocation& bitrate_allocation,
uint32_t framerate,
const std::optional<gfx::Size>& size) override;
void Destroy() override;
void Flush(FlushCallback flush_callback) override;
bool IsFlushSupported() override;
// base::trace_event::MemoryDumpProvider implementation.
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override;
private:
friend class VaapiVideoEncodeAcceleratorTest;
using EncodeJob = VaapiVideoEncoderDelegate::EncodeJob;
using EncodeResult = VaapiVideoEncoderDelegate::EncodeResult;
// Encoder state.
enum State {
kUninitialized,
kEncoding,
kError,
};
struct SizeComparator {
constexpr bool operator()(const gfx::Size& lhs,
const gfx::Size& rhs) const {
return std::forward_as_tuple(lhs.width(), lhs.height()) <
std::forward_as_tuple(rhs.width(), rhs.height());
}
};
// Maximum size is four to support the worst case of a given input of a
// different resolution than the maximum number of spatial layers (3).
static constexpr size_t kMaxNumSpatialLayersPlusOne = 3 + 1;
using InputSurfaceMap = base::small_map<
std::map<gfx::Size, std::unique_ptr<ScopedVASurface>, SizeComparator>,
kMaxNumSpatialLayersPlusOne>;
using EncodeSurfacesMap =
base::small_map<std::map<gfx::Size,
std::vector<std::unique_ptr<ScopedVASurface>>,
SizeComparator>,
kMaxNumSpatialLayersPlusOne>;
using EncodeSurfacesCountMap =
base::small_map<std::map<gfx::Size, size_t, SizeComparator>,
kMaxNumSpatialLayersPlusOne>;
// Holds input frames coming from the client ready to be encoded.
struct InputFrameRef;
// Thin wrapper around a ScopedVASurface to furnish it with a ReleaseCB to do
// something with it upon destruction, e.g. return it to a vector or pool.
class ScopedVASurfaceWrapper;
//
// Tasks for each of the VEA interface calls to be executed on
// |encoder_task_runner_|.
//
void InitializeTask(const Config& config);
bool AttemptedInitialization() const { return !!client_ptr_factory_; }
// Enqueues |frame| onto the queue of pending inputs and attempts to continue
// encoding.
void EncodeTask(scoped_refptr<VideoFrame> frame, bool force_keyframe);
// Push |buffer| into |available_bitstream_buffers_|, and attempts to return
// any pending encoded data in it, if any.
void UseOutputBitstreamBufferTask(BitstreamBuffer buffer);
void RequestEncodingParametersChangeTask(
VideoBitrateAllocation bitrate_allocation,
uint32_t framerate,
const std::optional<gfx::Size>& size);
void DestroyTask();
void FlushTask(FlushCallback flush_callback);
// Create input and reconstructed surfaces used in encoding whose sizes are
// |spatial_layer_resolutions| from GpuMemoryBuffer-based VideoFrame |frame|.
// The created surfaces for input to an encoder driver are filled into
// |input_surfaces| and, ones used as reconstructed surfaces by the driver are
// filled to |reconstructed_surfaces|. This must be called only in native
// input mode.
bool CreateSurfacesForGpuMemoryBufferEncoding(
const VideoFrame& frame,
const std::vector<gfx::Size>& spatial_layer_resolutions,
std::vector<std::unique_ptr<ScopedVASurfaceWrapper>>* input_surfaces,
std::vector<std::unique_ptr<ScopedVASurfaceWrapper>>*
reconstructed_surfaces);
// Create input and reconstructed surfaces used in encoding from SharedMemory
// VideoFrame |frame|. This must be called only in non native input mode.
bool CreateSurfacesForShmemEncoding(
const VideoFrame& frame,
std::unique_ptr<ScopedVASurfaceWrapper>* input_surface,
std::unique_ptr<ScopedVASurfaceWrapper>* reconstructed_surface);
// Creates or retrieves a ScopedVASurface compatible with |encode_size|. If
// there is no available surface and the number of previously allocated
// surfaces is less than threshold, then it returns a reference to a newly
// created surface. Returns nullptr if too many surfaces have already been
// allocated, or if creation fails.
std::unique_ptr<ScopedVASurfaceWrapper> GetOrCreateReconstructedSurface(
const gfx::Size& encode_size);
// Creates or retrieves a ScopedVASurface compatible with |encode_size| and
// |surface_usage_hints|. Returns nullptr if the surfaces fail to be created
// successfully.
std::unique_ptr<ScopedVASurfaceWrapper> GetOrCreateInputSurface(
VaapiWrapper& vaapi_wrapper,
const gfx::Size& encode_size,
const std::vector<VaapiWrapper::SurfaceUsageHint>& surface_usage_hints);
// Creates |vpp_vaapi_wrapper_| if it hasn't been created.
scoped_refptr<VaapiWrapper> CreateVppVaapiWrapper();
// Executes BlitSurface() using |vpp_vaapi_wrapper_| with |source_surface|,
// |source_visible_rect|. Returns the destination VASurface in BlitSurface()
// whose size is |encode_size| on success, otherwise nullptr.
std::unique_ptr<ScopedVASurfaceWrapper> ExecuteBlitSurface(
const ScopedVASurface* source_surface,
const gfx::Rect source_visible_rect,
const gfx::Size& encode_size);
// Checks if sufficient resources for a new encode job with |frame| as input
// are available, and if so, claims them by associating them with
// a EncodeJob, and returns the newly-created job, nullptr otherwise.
std::unique_ptr<EncodeJob> CreateEncodeJob(
bool force_keyframe,
base::TimeDelta frame_timestamp,
uint8_t spatial_index,
bool end_of_picture,
VASurfaceID input_surface_id,
std::unique_ptr<ScopedVASurfaceWrapper> reconstructed_surface);
// Continues encoding frames as long as input_queue_ is not empty, and we are
// able to create new EncodeJobs.
void EncodePendingInputs();
// Callback that returns a no longer used ScopedVASurface |va_surface| to
// |input_surfaces_[encode_size]| or |available_encode_surfaces_[encode_size]|
// respectively, for reuse.
void RecycleInputScopedVASurface(const gfx::Size& encode_size,
std::unique_ptr<ScopedVASurface> va_surface);
void RecycleEncodeScopedVASurface(
const gfx::Size& encode_size,
std::unique_ptr<ScopedVASurface> va_surface);
// Returns pending bitstream buffers to the client if we have both pending
// encoded data to be completed and bitstream buffers available to download
// the encoded data into.
void TryToReturnBitstreamBuffers();
// Downloads encoded data produced as a result of running |encode_result| into
// |buffer|, and returns it to the client.
void ReturnBitstreamBuffer(const EncodeResult& encode_result,
const BitstreamBuffer& buffer);
// Puts the encoder into en error state and notifies the client
// about the error.
void NotifyError(EncoderStatus status);
// Sets the encoder state to |state| on the correct thread.
void SetState(State state);
bool IsConfiguredForTesting() const {
return !supported_profiles_for_testing_.empty();
}
// Having too many encoder instances at once may cause us to run out of FDs
// and subsequently crash (crbug.com/1289465). To avoid that, we limit the
// maximum number of encoder instances that can exist at once.
// |num_instances_| tracks that number.
static constexpr int kMaxNumOfInstances = 10;
static base::AtomicRefCount num_instances_;
const bool can_use_encoder_ GUARDED_BY_CONTEXT(child_sequence_checker_);
// All of the members below must be accessed on the encoder_task_runner_,
// while it is running.
// The unchanged values are filled upon the construction. The varied values
// are filled properly during encoding.
VideoEncoderInfo encoder_info_ GUARDED_BY_CONTEXT(encoder_sequence_checker_);
// VaapiWrapper is the owner of all HW resources (surfaces and buffers)
// and will free them on destruction.
scoped_refptr<VaapiWrapper> vaapi_wrapper_
GUARDED_BY_CONTEXT(encoder_sequence_checker_);
// The expected coded size of incoming video frames when |native_input_mode_|
// is false.
gfx::Size expected_input_coded_size_
GUARDED_BY_CONTEXT(encoder_sequence_checker_);
// The codec of the stream to be produced. Set during initialization.
VideoCodec output_codec_ GUARDED_BY_CONTEXT(encoder_sequence_checker_) =
VideoCodec::kUnknown;
// The visible rect to be encoded.
gfx::Rect visible_rect_ GUARDED_BY_CONTEXT(encoder_sequence_checker_);
// Size in bytes required for output bitstream buffers.
size_t output_buffer_byte_size_
GUARDED_BY_CONTEXT(encoder_sequence_checker_) = 0;
// Size of the max size of |pending_encode_results_|.
size_t max_pending_results_size_
GUARDED_BY_CONTEXT(encoder_sequence_checker_) = 0;
// This flag signals when the client is sending NV12 + DmaBuf-backed
// VideoFrames to encode, which allows for skipping a copy-adaptation on
// input.
bool native_input_mode_ GUARDED_BY_CONTEXT(encoder_sequence_checker_) = false;
// The number of frames that needs to be held on encoding.
size_t num_frames_in_flight_ GUARDED_BY_CONTEXT(encoder_sequence_checker_) =
0;
// Encoder state. Encode tasks will only run in kEncoding state.
State state_ GUARDED_BY_CONTEXT(encoder_sequence_checker_) =
State::kUninitialized;
// Encoder instance managing video codec state and preparing encode jobs.
// Should only be used on |encoder_task_runner_|.
std::unique_ptr<VaapiVideoEncoderDelegate> encoder_
GUARDED_BY_CONTEXT(encoder_sequence_checker_);
// Map of input surfaces. In non |native_input_mode_|, this is always created
// and memory-based encode input VideoFrame is written into this.
// In |native_input_mode_|, this is created only if scaling or cropping is
// required and used as a VPP destination.
InputSurfaceMap input_surfaces_ GUARDED_BY_CONTEXT(encoder_sequence_checker_);
// Map of available reconstructed surfaces for encoding index by a layer
// resolution. These are stored as reference frames in
// VaapiVideoEncoderDelegate if necessary.
EncodeSurfacesMap available_encode_surfaces_
GUARDED_BY_CONTEXT(encoder_sequence_checker_);
// Map of the number of allocated reconstructed surfaces for encoding
// indexed by a layer resolution.
EncodeSurfacesCountMap encode_surfaces_count_
GUARDED_BY_CONTEXT(encoder_sequence_checker_);
// Queue of input frames to be encoded.
base::queue<InputFrameRef> input_queue_
GUARDED_BY_CONTEXT(encoder_sequence_checker_);
// BitstreamBuffers mapped, ready to be filled with encoded stream data.
base::queue<BitstreamBuffer> available_bitstream_buffers_
GUARDED_BY_CONTEXT(encoder_sequence_checker_);
// VASurfaces already encoded and waiting for the bitstream buffer to
// be downloaded.
base::queue<std::optional<EncodeResult>> pending_encode_results_
GUARDED_BY_CONTEXT(encoder_sequence_checker_);
// Task runner for interacting with the client, and its checker.
const scoped_refptr<base::SequencedTaskRunner> child_task_runner_;
SEQUENCE_CHECKER(child_sequence_checker_);
// Encoder sequence and its checker. All tasks are executed on it.
const scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner_;
SEQUENCE_CHECKER(encoder_sequence_checker_);
// To expose client callbacks from VideoEncodeAccelerator.
// NOTE: all calls to these objects *MUST* be executed on
// child_task_runner_.
std::unique_ptr<base::WeakPtrFactory<Client>> client_ptr_factory_;
base::WeakPtr<Client> client_;
// VaapiWrapper for VPP (Video Pre Processing). This is used for scale down
// for the picture send to vaapi encoder.
scoped_refptr<VaapiWrapper> vpp_vaapi_wrapper_
GUARDED_BY_CONTEXT(encoder_sequence_checker_);
// The completion callback of the Flush() function.
FlushCallback flush_callback_ GUARDED_BY_CONTEXT(encoder_sequence_checker_);
// Supported profiles that are filled if and only if in a unit test.
SupportedProfiles supported_profiles_for_testing_;
// WeakPtr of this, bound to |child_task_runner_|.
base::WeakPtr<VaapiVideoEncodeAccelerator> child_weak_this_;
// WeakPtr of this, bound to |encoder_task_runner_|.
base::WeakPtr<VaapiVideoEncodeAccelerator> encoder_weak_this_;
base::WeakPtrFactory<VaapiVideoEncodeAccelerator> child_weak_this_factory_{
this};
base::WeakPtrFactory<VaapiVideoEncodeAccelerator> encoder_weak_this_factory_{
this};
};
} // namespace media
#endif // MEDIA_GPU_VAAPI_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_