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