/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef __FFmpegVideoFramePool_h__ #define __FFmpegVideoFramePool_h__ #include "FFmpegLibWrapper.h" #include "FFmpegLibs.h" #include "FFmpegLog.h" #include "mozilla/layers/DMABUFSurfaceImage.h" #include "mozilla/widget/DMABufLibWrapper.h" #include "mozilla/widget/DMABufSurface.h" namespace mozilla { // VideoFrameSurface holds a reference to GPU data with a video frame. // // Actual GPU pixel data are stored at DMABufSurface and // DMABufSurface is passed to gecko GL rendering pipeline via. // DMABUFSurfaceImage. // // VideoFrameSurface can optionally hold VA-API ffmpeg related data to keep // GPU data locked untill we need them. // // VideoFrameSurface is used for both HW accelerated video decoding // (VA-API) and ffmpeg SW decoding. // // VA-API scenario // // When VA-API decoding is running, ffmpeg allocates AVHWFramesContext - a pool // of "hardware" frames. Every "hardware" frame (VASurface) is backed // by actual piece of GPU memory which holds the decoded image data. // // The VASurface is wrapped by DMABufSurface and transferred to // rendering queue by DMABUFSurfaceImage, where TextureClient is // created and VASurface is used as a texture there. // // As there's a limited number of VASurfaces, ffmpeg reuses them to decode // next frames ASAP even if they are still attached to DMABufSurface // and used as a texture in our rendering engine. // // Unfortunately there isn't any obvious way how to mark particular VASurface // as used. The best we can do is to hold a reference to particular AVBuffer // from decoded AVFrame and AVHWFramesContext which owns the AVBuffer. template class VideoFrameSurface {}; template <> class VideoFrameSurface; template class VideoFramePool {}; template <> class VideoFramePool; template <> class VideoFrameSurface { friend class VideoFramePool; public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoFrameSurface) explicit VideoFrameSurface(DMABufSurface* aSurface); void SetYUVColorSpace(mozilla::gfx::YUVColorSpace aColorSpace) { mSurface->GetAsDMABufSurfaceYUV()->SetYUVColorSpace(aColorSpace); } void SetColorRange(mozilla::gfx::ColorRange aColorRange) { mSurface->GetAsDMABufSurfaceYUV()->SetColorRange(aColorRange); } RefPtr GetDMABufSurface() { return mSurface->GetAsDMABufSurfaceYUV(); }; RefPtr GetAsImage(); // Don't allow VideoFrameSurface plain copy as it leads to // unexpected DMABufSurface/HW buffer releases and we don't want to // deep copy them. VideoFrameSurface(const VideoFrameSurface&) = delete; const VideoFrameSurface& operator=(VideoFrameSurface const&) = delete; protected: // Lock VAAPI related data void LockVAAPIData(AVCodecContext* aAVCodecContext, AVFrame* aAVFrame, FFmpegLibWrapper* aLib); // Release VAAPI related data, DMABufSurface can be reused // for another frame. void ReleaseVAAPIData(bool aForFrameRecycle = true); // Check if DMABufSurface is used by any gecko rendering process // (WebRender or GL compositor) or by DMABUFSurfaceImage/VideoData. bool IsUsed() const { return mSurface->IsGlobalRefSet(); } // Surface points to dmabuf memmory owned by ffmpeg. bool IsFFMPEGSurface() const { return !!mLib; } void MarkAsUsed(VASurfaceID aFFMPEGSurfaceID) { mFFMPEGSurfaceID = Some(aFFMPEGSurfaceID); } private: virtual ~VideoFrameSurface(); const RefPtr mSurface; const FFmpegLibWrapper* mLib; AVBufferRef* mAVHWFrameContext; AVBufferRef* mHWAVBuffer; Maybe mFFMPEGSurfaceID; }; // VideoFramePool class is thread-safe. template <> class VideoFramePool { public: explicit VideoFramePool(int aFFMPEGPoolSize); ~VideoFramePool(); RefPtr> GetVideoFrameSurface( VADRMPRIMESurfaceDescriptor& aVaDesc, int aWidth, int aHeight, AVCodecContext* aAVCodecContext, AVFrame* aAVFrame, FFmpegLibWrapper* aLib); RefPtr> GetVideoFrameSurface( AVDRMFrameDescriptor& aDesc, int aWidth, int aHeight, AVCodecContext* aAVCodecContext, AVFrame* aAVFrame, FFmpegLibWrapper* aLib); void ReleaseUnusedVAAPIFrames(); private: RefPtr> GetFreeVideoFrameSurface(); bool ShouldCopySurface(); void CheckNewFFMPEGSurface(VASurfaceID aNewSurfaceID); private: // Protect mDMABufSurfaces pool access Mutex mSurfaceLock MOZ_UNANNOTATED; nsTArray>> mDMABufSurfaces; // Number of dmabuf surfaces allocated by ffmpeg for decoded video frames. // Can be adjusted by extra_hw_frames at InitVAAPICodecContext(). int mFFMPEGPoolSize; // We may fail to create texture over DMABuf memory due to driver bugs so // check that before we export first DMABuf video frame. Maybe mTextureCreationWorks; // We may fail to copy DMABuf memory on NVIDIA drivers. bool mTextureCopyWorks = true; }; } // namespace mozilla #endif // __FFmpegVideoFramePool_h__