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
|
// Copyright (c) 2012 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.
//
// This file contains an implementation of a class that provides H264 decode
// support for use with VAAPI hardware video decode acceleration on Intel
// systems.
#ifndef CONTENT_COMMON_GPU_MEDIA_VAAPI_H264_DECODER_H_
#define CONTENT_COMMON_GPU_MEDIA_VAAPI_H264_DECODER_H_
#include <GL/glx.h>
#include <queue>
#include "base/callback_forward.h"
#include "base/lazy_instance.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
#include "content/common/gpu/media/h264_dpb.h"
#include "content/common/gpu/media/h264_parser.h"
#include "media/base/video_decoder_config.h"
#include "media/base/limits.h"
#include "third_party/libva/va/va.h"
namespace content {
// An H264 decoder for use for VA-API-specific decoding. Provides features not
// supported by libva, including stream parsing, reference picture management
// and other operations not supported by the HW codec.
//
// Provides functionality to allow plugging VAAPI HW acceleration into the
// VDA framework.
//
// Clients of this class are expected to pass H264 Annex-B byte stream and
// will receive decoded pictures via client-provided |OutputPicCB|.
//
// If used in multi-threaded environment, some of the functions have to be
// called on the child thread, i.e. the main thread of the GPU process
// (the one that has the GLX context passed to Initialize() set as current).
// This is essential so that the GLX calls can work properly.
// Decoder thread, on the other hand, does not require a GLX context and should
// be the same as the one on which Decode*() functions are called.
class VaapiH264Decoder {
public:
// Callback invoked on the client when a picture is to be displayed.
// Callee has to call PutPicToTexture() for the given picture before
// displaying it, to ensure the contents have been synced properly.
// Arguments: input buffer id, output buffer id (both provided by the client
// at the time of Decode() and AssignPictureBuffer() calls).
typedef base::Callback<void(int32, int32)> OutputPicCB;
// Decode result codes.
enum DecResult {
kDecodeError, // Error while decoding.
// TODO posciak: unsupported streams are currently treated as error
// in decoding; in future it could perhaps be possible to fall back
// to software decoding instead.
// kStreamError, // Error in stream.
kReadyToDecode, // Successfully initialized.
kDecodedFrame, // Successfully decoded a frame.
kNeedMoreStreamData, // Need more stream data to decode the next frame.
kNoOutputAvailable, // Waiting for the client to free up output surfaces.
};
VaapiH264Decoder();
// Should be called on the GLX thread, for the surface cleanup to work
// properly.
~VaapiH264Decoder();
// Initializes and sets up libva connection and GL/X11 resources.
// Must be called on the GLX thread with |glx_context| being current and
// with decoder thread not yet running.
// |output_pic_cb| will be called to notify when a picture can be displayed.
bool Initialize(media::VideoCodecProfile profile,
Display* x_display,
GLXContext glx_context,
const base::Callback<bool(void)>& make_context_current,
const OutputPicCB& output_pic_cb) WARN_UNUSED_RESULT;
void Destroy();
// Notify the decoder that this output buffer has been consumed and
// can be reused (overwritten).
// Must be run on the decoder thread.
void ReusePictureBuffer(int32 picture_buffer_id);
// Give a new picture buffer (texture) to decoder for use.
// Must be run on the GLX thread with decoder thread not yet running.
bool AssignPictureBuffer(int32 picture_buffer_id, uint32 texture_id)
WARN_UNUSED_RESULT;
// Sync the data so that the texture for given |picture_buffer_id| can
// be displayed.
// Must be run on the GLX thread.
bool PutPicToTexture(int32 picture_buffer_id) WARN_UNUSED_RESULT;
// Have the decoder flush its state and trigger output of all previously
// decoded pictures via OutputPicCB.
// Returns false if any of the resulting invocations of the callback fail.
bool Flush() WARN_UNUSED_RESULT;
// Called while decoding.
// Stop decoding, discarding all remaining input/output, but do not flush
// state, so the playback of the same stream can be resumed (possibly from
// another location).
void Reset();
// Set current stream data pointer to |ptr| and |size|.
// Must be run on decoder thread.
void SetStream(uint8* ptr, size_t size);
// Start parsing stream to detect picture sizes. Does not produce any
// decoded pictures and can be called without providing output textures.
// Also to be used after Reset() to find a suitable location in the
// stream to resume playback from.
DecResult DecodeInitial(int32 input_id) WARN_UNUSED_RESULT;
// Runs until a frame is decoded or end of provided stream data buffer
// is reached. Decoded pictures will be returned asynchronously via
// OutputPicCB.
DecResult DecodeOneFrame(int32 input_id) WARN_UNUSED_RESULT;
// Return dimensions for output buffer (texture) allocation.
// Valid only after a successful DecodeInitial().
int pic_height() { return pic_height_; }
int pic_width() { return pic_width_; }
// Return the number of output pictures required for decoding.
// Valid after a successful DecodeInitial().
static size_t GetRequiredNumOfPictures();
// Do any necessary initialization before the sandbox is enabled.
static void PreSandboxInitialization();
// Lazily initialize static data after sandbox is enabled. Return false on
// init failure.
static bool PostSandboxInitialization();
private:
// We need to keep at least kDPBMaxSize pictures in DPB for
// reference/to display later and an additional one for the one currently
// being decoded. We also ask for some additional ones since VDA needs
// to accumulate a few ready-to-output pictures before it actually starts
// displaying and giving them back. +2 instead of +1 because of subjective
// smoothness improvement during testing.
enum { kNumReqPictures = H264DPB::kDPBMaxSize +
media::limits::kMaxVideoFrames + 2 };
// Internal state of the decoder.
enum State {
kUninitialized, // Initialize() not yet called.
kInitialized, // Initialize() called, pictures requested.
kDecoding, // DecodeInitial() successful, output surfaces allocated.
kAfterReset, // After Reset() during decoding.
kError, // Error in kDecoding state.
};
// Get usable framebuffer configuration for use in binding textures
// or return false on failure.
bool InitializeFBConfig();
// Process H264 stream structures.
bool ProcessSPS(int sps_id);
bool ProcessPPS(int pps_id);
bool ProcessSlice(H264SliceHeader* slice_hdr);
// Initialize the current picture according to data in |slice_hdr|.
bool InitCurrPicture(H264SliceHeader* slice_hdr);
// Calculate picture order counts for the new picture
// on initialization of a new frame (see spec).
bool CalculatePicOrderCounts(H264SliceHeader* slice_hdr);
// Update PicNum values in pictures stored in DPB on creation of new
// frame (see spec).
void UpdatePicNums();
// Construct initial reference picture lists for use in decoding of
// P and B pictures (see 8.2.4 in spec).
void ConstructReferencePicListsP(H264SliceHeader* slice_hdr);
void ConstructReferencePicListsB(H264SliceHeader* slice_hdr);
// Helper functions for reference list construction, per spec.
int PicNumF(H264Picture *pic);
int LongTermPicNumF(H264Picture *pic);
// Perform the reference picture lists' modification (reordering), as
// specified in spec (8.2.4).
//
// |list| indicates list number and should be either 0 or 1.
bool ModifyReferencePicList(H264SliceHeader *slice_hdr, int list);
// Perform reference picture memory management operations (marking/unmarking
// of reference pictures, long term picture management, discarding, etc.).
// See 8.2.5 in spec.
bool HandleMemoryManagementOps();
void ReferencePictureMarking();
// Start processing a new frame.
bool StartNewFrame(H264SliceHeader* slice_hdr);
// All data for a frame received, process it and decode.
bool FinishPrevFrameIfPresent();
// Called after decoding, performs all operations to be done after decoding,
// including DPB management, reference picture marking and memory management
// operations.
// This will also output a picture if one is ready for output.
bool FinishPicture();
// Convert VideoCodecProfile to VAProfile and set it as active.
bool SetProfile(media::VideoCodecProfile profile);
// Vaapi-related functions.
// Allocates VASurfaces and creates a VAContext for them.
bool CreateVASurfaces();
// Destroys allocated VASurfaces and related VAContext.
void DestroyVASurfaces();
// Destroys all buffers in |pending_slice_bufs_| and |pending_va_bufs_|.
void DestroyPendingBuffers();
// Destroys a list of buffers.
void DestroyBuffers(size_t num_va_buffers, const VABufferID* va_buffers);
// These queue up data for HW decoder to be committed on running HW decode.
bool SendPPS();
bool SendIQMatrix();
bool SendVASliceParam(H264SliceHeader* slice_hdr);
bool SendSliceData(const uint8* ptr, size_t size);
bool QueueSlice(H264SliceHeader* slice_hdr);
// Helper methods for filling HW structures.
void FillVAPicture(VAPictureH264 *va_pic, H264Picture* pic);
int FillVARefFramesFromDPB(VAPictureH264 *va_pics, int num_pics);
// Commits all pending data for HW decoder and starts HW decoder.
bool DecodePicture();
// Notifies client that a picture is ready for output.
bool OutputPic(H264Picture* pic);
State state_;
// A frame has been sent to hardware as the result of the last
// DecodeOneFrame() call.
bool frame_ready_at_hw_;
// Parser in use.
H264Parser parser_;
// DPB in use.
H264DPB dpb_;
// Picture currently being processed/decoded.
scoped_ptr<H264Picture> curr_pic_;
// Reference picture lists, constructed for each picture before decoding.
// Those lists are not owners of the pointers (DPB is).
H264Picture::PtrVector ref_pic_list0_;
H264Picture::PtrVector ref_pic_list1_;
// Global state values, needed in decoding. See spec.
int max_pic_order_cnt_lsb_;
int max_frame_num_;
int max_pic_num_;
int max_long_term_frame_idx_;
int frame_num_;
int prev_frame_num_;
int prev_frame_num_offset_;
// Values related to previously decoded reference picture.
bool prev_ref_has_memmgmnt5_;
int prev_ref_top_field_order_cnt_;
int prev_ref_pic_order_cnt_msb_;
int prev_ref_pic_order_cnt_lsb_;
H264Picture::Field prev_ref_field_;
// Currently active SPS and PPS.
int curr_sps_id_;
int curr_pps_id_;
// Output picture size.
int pic_width_;
int pic_height_;
// Data queued up for HW decoder, to be committed on next HW decode.
std::queue<VABufferID> pending_slice_bufs_;
std::queue<VABufferID> pending_va_bufs_;
// Manages binding of a client-provided output buffer (texture) to VASurface.
class DecodeSurface;
// Maps output_buffer_id to a decode surface. Used to look up surfaces
// on requests from the client.
typedef std::map<int32, linked_ptr<DecodeSurface> > DecodeSurfaces;
DecodeSurfaces decode_surfaces_;
// Number of decode surface currently available for decoding.
int num_available_decode_surfaces_;
// Maps decode surfaces to PicOrderCount, used to look up output buffers
// when a decision to output a picture has been made.
typedef std::map<int, DecodeSurface*> POCToDecodeSurfaces;
POCToDecodeSurfaces poc_to_decode_surfaces_;
// Find an available surface and assign it to given PicOrderCnt |poc|,
// removing it from the available surfaces pool. Return true if a surface
// has been found, false otherwise.
bool AssignSurfaceToPoC(int poc);
// Unassign a surface from |poc| and return a pointer to it, or NULL if there
// is no surface associated with given |poc|. Note that this does not make
// the surface available for reuse - as this can only happen after client
// returns the surface via ReusePictureBuffer() - but only removes its
// association with given |poc|.
DecodeSurface* UnassignSurfaceFromPoC(int poc);
// The id of current input buffer, which will be associated with an
// output picture if a frame is decoded successfully.
int32 curr_input_id_;
// Any method that uses GL/VA routines probably wants to make sure
// make_context_current_.Run() is called at the top of the method.
// X/GLX handles.
Display* x_display_;
base::Callback<bool(void)> make_context_current_;
GLXFBConfig fb_config_;
// VA handles.
VADisplay va_display_;
VAConfigID va_config_id_;
VAContextID va_context_id_;
VAProfile profile_;
// Allocated VASurfaces.
VASurfaceID va_surface_ids_[kNumReqPictures];
// Called by decoder when a picture should be outputted.
OutputPicCB output_pic_cb_;
// Has static initialization of pre-sandbox components completed successfully?
static bool pre_sandbox_init_done_;
DISALLOW_COPY_AND_ASSIGN(VaapiH264Decoder);
};
} // namespace content
#endif // CONTENT_COMMON_GPU_MEDIA_VAAPI_H264_DECODER_H_
|