1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "FFmpegVideoFramePool.h"
8 #include "FFmpegLog.h"
9 #include "mozilla/widget/DMABufLibWrapper.h"
10 #include "libavutil/pixfmt.h"
11 
12 namespace mozilla {
13 
GetAsImage()14 RefPtr<layers::Image> VideoFrameSurfaceDMABuf::GetAsImage() {
15   return new layers::DMABUFSurfaceImage(mSurface);
16 }
17 
VideoFrameSurfaceDMABuf(DMABufSurface * aSurface)18 VideoFrameSurfaceDMABuf::VideoFrameSurfaceDMABuf(DMABufSurface* aSurface)
19     : mSurface(aSurface) {
20   // Create global refcount object to track mSurface usage over
21   // gects rendering engine. We can't release it until it's used
22   // by GL compositor / WebRender.
23   MOZ_ASSERT(mSurface);
24   MOZ_RELEASE_ASSERT(mSurface->GetAsDMABufSurfaceYUV());
25   mSurface->GlobalRefCountCreate();
26   FFMPEG_LOG("VideoFrameSurfaceDMABuf: creating surface UID = %d",
27              mSurface->GetUID());
28 }
29 
VideoFrameSurfaceVAAPI(DMABufSurface * aSurface)30 VideoFrameSurfaceVAAPI::VideoFrameSurfaceVAAPI(DMABufSurface* aSurface)
31     : VideoFrameSurfaceDMABuf(aSurface) {}
32 
LockVAAPIData(AVCodecContext * aAVCodecContext,AVFrame * aAVFrame,FFmpegLibWrapper * aLib)33 void VideoFrameSurfaceVAAPI::LockVAAPIData(AVCodecContext* aAVCodecContext,
34                                            AVFrame* aAVFrame,
35                                            FFmpegLibWrapper* aLib) {
36   FFMPEG_LOG("VideoFrameSurfaceVAAPI: VAAPI locking dmabuf surface UID = %d",
37              mSurface->GetUID());
38   mLib = aLib;
39   mAVHWFramesContext = aLib->av_buffer_ref(aAVCodecContext->hw_frames_ctx);
40   mHWAVBuffer = aLib->av_buffer_ref(aAVFrame->buf[0]);
41 }
42 
ReleaseVAAPIData(bool aForFrameRecycle)43 void VideoFrameSurfaceVAAPI::ReleaseVAAPIData(bool aForFrameRecycle) {
44   FFMPEG_LOG("VideoFrameSurfaceVAAPI: VAAPI releasing dmabuf surface UID = %d",
45              mSurface->GetUID());
46 
47   // It's possible to unref GPU data while IsUsed() is still set.
48   // It can happens when VideoFramePool is deleted while decoder shutdown
49   // but related dmabuf surfaces are still used in another process.
50   // In such case we don't care as the dmabuf surface will not be
51   // recycled for another frame and stays here untill last fd of it
52   // is closed.
53   mLib->av_buffer_unref(&mHWAVBuffer);
54   mLib->av_buffer_unref(&mAVHWFramesContext);
55 
56   if (aForFrameRecycle) {
57     // If we want to recycle the frame, make sure it's not used
58     // by gecko rendering pipeline.
59     MOZ_DIAGNOSTIC_ASSERT(!IsUsed());
60     mSurface->ReleaseSurface();
61   }
62 }
63 
~VideoFrameSurfaceVAAPI()64 VideoFrameSurfaceVAAPI::~VideoFrameSurfaceVAAPI() {
65   FFMPEG_LOG("VideoFrameSurfaceVAAPI: deleting dmabuf surface UID = %d",
66              mSurface->GetUID());
67   // We're about to quit, no need to recycle the frames.
68   ReleaseVAAPIData(/* aForFrameRecycle */ false);
69 }
70 
VideoFramePool(bool aUseVAAPI)71 VideoFramePool::VideoFramePool(bool aUseVAAPI) : mUseVAAPI(aUseVAAPI) {}
72 
~VideoFramePool()73 VideoFramePool::~VideoFramePool() { mDMABufSurfaces.Clear(); }
74 
ReleaseUnusedVAAPIFrames()75 void VideoFramePool::ReleaseUnusedVAAPIFrames() {
76   if (!mUseVAAPI) {
77     return;
78   }
79   for (const auto& surface : mDMABufSurfaces) {
80     if (!surface->IsUsed()) {
81       surface->ReleaseVAAPIData();
82     }
83   }
84 }
85 
GetFreeVideoFrameSurface()86 RefPtr<VideoFrameSurface> VideoFramePool::GetFreeVideoFrameSurface() {
87   int len = mDMABufSurfaces.Length();
88   for (int i = 0; i < len; i++) {
89     if (!mDMABufSurfaces[i]->IsUsed()) {
90       return mDMABufSurfaces[i];
91     }
92   }
93   return nullptr;
94 }
95 
GetVideoFrameSurface(VADRMPRIMESurfaceDescriptor & aVaDesc)96 RefPtr<VideoFrameSurface> VideoFramePool::GetVideoFrameSurface(
97     VADRMPRIMESurfaceDescriptor& aVaDesc) {
98   // VADRMPRIMESurfaceDescriptor can be used with VA-API only.
99   MOZ_ASSERT(mUseVAAPI);
100 
101   auto videoSurface = GetFreeVideoFrameSurface();
102   if (!videoSurface) {
103     RefPtr<DMABufSurfaceYUV> surface =
104         DMABufSurfaceYUV::CreateYUVSurface(aVaDesc);
105     if (!surface) {
106       return nullptr;
107     }
108     FFMPEG_LOG("Created new VA-API DMABufSurface UID = %d", surface->GetUID());
109     videoSurface = new VideoFrameSurfaceVAAPI(surface);
110     mDMABufSurfaces.AppendElement(videoSurface);
111     return videoSurface;
112   }
113 
114   // Release VAAPI surface data before we reuse it.
115   videoSurface->ReleaseVAAPIData();
116 
117   RefPtr<DMABufSurfaceYUV> surface = videoSurface->GetDMABufSurface();
118   if (!surface->UpdateYUVData(aVaDesc)) {
119     return nullptr;
120   }
121   FFMPEG_LOG("Reusing VA-API DMABufSurface UID = %d", surface->GetUID());
122   return videoSurface;
123 }
124 
GetVideoFrameSurface(AVPixelFormat aPixelFormat,AVFrame * aFrame)125 RefPtr<VideoFrameSurface> VideoFramePool::GetVideoFrameSurface(
126     AVPixelFormat aPixelFormat, AVFrame* aFrame) {
127   // We should not use SW surfaces when VA-API is enabled.
128   MOZ_ASSERT(!mUseVAAPI);
129   MOZ_ASSERT(aFrame);
130 
131   // With SW decode we support only YUV420P format with DMABuf surfaces.
132   if (aPixelFormat != AV_PIX_FMT_YUV420P) {
133     return nullptr;
134   }
135 
136   auto videoSurface = GetFreeVideoFrameSurface();
137   if (!videoSurface) {
138     RefPtr<DMABufSurfaceYUV> surface = DMABufSurfaceYUV::CreateYUVSurface(
139         aFrame->width, aFrame->height, (void**)aFrame->data, aFrame->linesize);
140     if (!surface) {
141       return nullptr;
142     }
143     FFMPEG_LOG("Created new SW DMABufSurface UID = %d", surface->GetUID());
144     videoSurface = new VideoFrameSurfaceDMABuf(surface);
145     mDMABufSurfaces.AppendElement(videoSurface);
146     return videoSurface;
147   }
148 
149   RefPtr<DMABufSurfaceYUV> surface = videoSurface->GetDMABufSurface();
150   if (!surface->UpdateYUVData((void**)aFrame->data, aFrame->linesize)) {
151     return nullptr;
152   }
153   FFMPEG_LOG("Reusing SW DMABufSurface UID = %d", surface->GetUID());
154   return videoSurface;
155 }
156 
157 }  // namespace mozilla
158