fune/dom/media/platforms/ffmpeg/FFmpegVideoFramePool.cpp
stransky a5eed5ff52 Bug 1743638 [Linux] Make VideoFramePool thread safe r=alwu,media-playback-reviewers
Make VideoFramePool thread safe to avoid multiple access during software decode to DMABuf:

- Create Mutex for VideoFramePool access
- Mark surface as used when it's provided by VideoFramePool to avoid race conditions.

Differential Revision: https://phabricator.services.mozilla.com/D135557
2022-01-24 11:59:42 +00:00

180 lines
6.2 KiB
C++

/* -*- 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/. */
#include "FFmpegVideoFramePool.h"
#include "FFmpegLog.h"
#include "mozilla/widget/DMABufLibWrapper.h"
#include "libavutil/pixfmt.h"
namespace mozilla {
RefPtr<layers::Image> VideoFrameSurfaceDMABuf::GetAsImage() {
return new layers::DMABUFSurfaceImage(mSurface);
}
VideoFrameSurfaceDMABuf::VideoFrameSurfaceDMABuf(DMABufSurface* aSurface)
: mSurface(aSurface) {
// Create global refcount object to track mSurface usage over
// gects rendering engine. We can't release it until it's used
// by GL compositor / WebRender.
MOZ_ASSERT(mSurface);
MOZ_RELEASE_ASSERT(mSurface->GetAsDMABufSurfaceYUV());
mSurface->GlobalRefCountCreate();
mSurface->GlobalRefAdd();
FFMPEG_LOG("VideoFrameSurfaceDMABuf: creating surface UID = %d",
mSurface->GetUID());
}
VideoFrameSurfaceVAAPI::VideoFrameSurfaceVAAPI(DMABufSurface* aSurface)
: VideoFrameSurfaceDMABuf(aSurface) {}
void VideoFrameSurfaceVAAPI::LockVAAPIData(AVCodecContext* aAVCodecContext,
AVFrame* aAVFrame,
FFmpegLibWrapper* aLib) {
FFMPEG_LOG("VideoFrameSurfaceVAAPI: VAAPI locking dmabuf surface UID = %d",
mSurface->GetUID());
mLib = aLib;
mAVHWFramesContext = aLib->av_buffer_ref(aAVCodecContext->hw_frames_ctx);
mHWAVBuffer = aLib->av_buffer_ref(aAVFrame->buf[0]);
}
void VideoFrameSurfaceVAAPI::ReleaseVAAPIData(bool aForFrameRecycle) {
FFMPEG_LOG("VideoFrameSurfaceVAAPI: VAAPI releasing dmabuf surface UID = %d",
mSurface->GetUID());
// It's possible to unref GPU data while IsUsed() is still set.
// It can happens when VideoFramePool is deleted while decoder shutdown
// but related dmabuf surfaces are still used in another process.
// In such case we don't care as the dmabuf surface will not be
// recycled for another frame and stays here untill last fd of it
// is closed.
mLib->av_buffer_unref(&mHWAVBuffer);
mLib->av_buffer_unref(&mAVHWFramesContext);
if (aForFrameRecycle) {
// If we want to recycle the frame, make sure it's not used
// by gecko rendering pipeline.
MOZ_DIAGNOSTIC_ASSERT(!IsUsed());
mSurface->ReleaseSurface();
}
}
VideoFrameSurfaceVAAPI::~VideoFrameSurfaceVAAPI() {
FFMPEG_LOG("VideoFrameSurfaceVAAPI: deleting dmabuf surface UID = %d",
mSurface->GetUID());
// We're about to quit, no need to recycle the frames.
ReleaseVAAPIData(/* aForFrameRecycle */ false);
}
VideoFramePool::VideoFramePool(bool aUseVAAPI)
: mUseVAAPI(aUseVAAPI), mSurfaceLock("VideoFramePoolSurfaceLock") {}
VideoFramePool::~VideoFramePool() {
MutexAutoLock lock(mSurfaceLock);
mDMABufSurfaces.Clear();
}
void VideoFramePool::ReleaseUnusedVAAPIFrames() {
if (!mUseVAAPI) {
return;
}
MutexAutoLock lock(mSurfaceLock);
for (const auto& surface : mDMABufSurfaces) {
auto* dmabufSurface = surface->AsVideoFrameSurfaceVAAPI();
if (!dmabufSurface->IsUsed()) {
dmabufSurface->ReleaseVAAPIData();
}
}
}
RefPtr<VideoFrameSurface> VideoFramePool::GetFreeVideoFrameSurface() {
int len = mDMABufSurfaces.Length();
for (int i = 0; i < len; i++) {
auto* dmabufSurface = mDMABufSurfaces[i]->AsVideoFrameSurfaceDMABuf();
if (!dmabufSurface->IsUsed()) {
auto* vaapiSurface = dmabufSurface->AsVideoFrameSurfaceVAAPI();
if (vaapiSurface) {
vaapiSurface->ReleaseVAAPIData();
}
dmabufSurface->MarkAsUsed();
return mDMABufSurfaces[i];
}
}
return nullptr;
}
RefPtr<VideoFrameSurface> VideoFramePool::GetVideoFrameSurface(
VADRMPRIMESurfaceDescriptor& aVaDesc, AVCodecContext* aAVCodecContext,
AVFrame* aAVFrame, FFmpegLibWrapper* aLib) {
// VADRMPRIMESurfaceDescriptor can be used with VA-API only.
MOZ_ASSERT(mUseVAAPI);
if (aVaDesc.fourcc != VA_FOURCC_NV12 && aVaDesc.fourcc != VA_FOURCC_YV12 &&
aVaDesc.fourcc != VA_FOURCC_P010) {
FFMPEG_LOG("Unsupported VA-API surface format %d", aVaDesc.fourcc);
return nullptr;
}
MutexAutoLock lock(mSurfaceLock);
auto videoSurface = GetFreeVideoFrameSurface();
if (!videoSurface) {
RefPtr<DMABufSurfaceYUV> surface =
DMABufSurfaceYUV::CreateYUVSurface(aVaDesc);
if (!surface) {
return nullptr;
}
FFMPEG_LOG("Created new VA-API DMABufSurface UID = %d", surface->GetUID());
videoSurface = new VideoFrameSurfaceVAAPI(surface);
mDMABufSurfaces.AppendElement(videoSurface);
} else {
RefPtr<DMABufSurfaceYUV> surface = videoSurface->GetDMABufSurface();
if (!surface->UpdateYUVData(aVaDesc)) {
return nullptr;
}
FFMPEG_LOG("Reusing VA-API DMABufSurface UID = %d", surface->GetUID());
}
auto vaapiSurface = videoSurface->AsVideoFrameSurfaceVAAPI();
if (vaapiSurface) {
vaapiSurface->LockVAAPIData(aAVCodecContext, aAVFrame, aLib);
}
return videoSurface;
}
RefPtr<VideoFrameSurface> VideoFramePool::GetVideoFrameSurface(
AVPixelFormat aPixelFormat, AVFrame* aFrame) {
// We should not use SW surfaces when VA-API is enabled.
MOZ_ASSERT(!mUseVAAPI);
MOZ_ASSERT(aFrame);
// With SW decode we support only YUV420P format with DMABuf surfaces.
if (aPixelFormat != AV_PIX_FMT_YUV420P) {
return nullptr;
}
MutexAutoLock lock(mSurfaceLock);
auto videoSurface = GetFreeVideoFrameSurface();
if (!videoSurface) {
RefPtr<DMABufSurfaceYUV> surface = DMABufSurfaceYUV::CreateYUVSurface(
aFrame->width, aFrame->height, (void**)aFrame->data, aFrame->linesize);
if (!surface) {
return nullptr;
}
FFMPEG_LOG("Created new SW DMABufSurface UID = %d", surface->GetUID());
videoSurface = new VideoFrameSurfaceDMABuf(surface);
mDMABufSurfaces.AppendElement(videoSurface);
return videoSurface;
}
RefPtr<DMABufSurfaceYUV> surface = videoSurface->GetDMABufSurface();
if (!surface->UpdateYUVData((void**)aFrame->data, aFrame->linesize)) {
return nullptr;
}
FFMPEG_LOG("Reusing SW DMABufSurface UID = %d", surface->GetUID());
return videoSurface;
}
} // namespace mozilla