Index: content/common/gpu/media/vaapi_h264_decoder.h |
diff --git a/content/common/gpu/media/vaapi_h264_decoder.h b/content/common/gpu/media/vaapi_h264_decoder.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..abf67387ce2ce1d696d438694a22d471f88583a8 |
--- /dev/null |
+++ b/content/common/gpu/media/vaapi_h264_decoder.h |
@@ -0,0 +1,335 @@ |
+// 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 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(); |
+ |
+ 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(); |
+ |
+ // 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 commited 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_; |
+ |
+ // X/GLX handles. |
+ Display* x_display_; |
+ GLXContext parent_glx_context_; |
+ 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_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(VaapiH264Decoder); |
+}; |
+ |
+} // namespace content |
+ |
+#endif // CONTENT_COMMON_GPU_MEDIA_VAAPI_H264_DECODER_H_ |
+ |