tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FFmpegVideoFramePool.cpp (19972B)


      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 
      9 #include "FFmpegLog.h"
     10 #include "PlatformDecoderModule.h"
     11 #include "libavutil/pixfmt.h"
     12 #include "mozilla/StaticPrefs_media.h"
     13 #include "mozilla/gfx/gfxVars.h"
     14 #include "mozilla/widget/DMABufDevice.h"
     15 #include "mozilla/widget/va_drmcommon.h"
     16 
     17 // DMABufLibWrapper defines its own version of this which collides with the
     18 // official version in drm_fourcc.h
     19 #ifdef DRM_FORMAT_MOD_INVALID
     20 #  undef DRM_FORMAT_MOD_INVALID
     21 #endif
     22 #include <libdrm/drm_fourcc.h>
     23 
     24 #ifdef MOZ_LOGGING
     25 #  undef DMABUF_LOG
     26 extern mozilla::LazyLogModule gDmabufLog;
     27 #  define DMABUF_LOG(str, ...) \
     28    MOZ_LOG(gDmabufLog, mozilla::LogLevel::Debug, (str, ##__VA_ARGS__))
     29 #else
     30 #  define DMABUF_LOG(args)
     31 #endif /* MOZ_LOGGING */
     32 
     33 // Start copying surfaces when free ffmpeg surface count is below 1/4 of all
     34 // available surfaces.
     35 #define SURFACE_COPY_THRESHOLD (1.0f / 4.0f)
     36 
     37 constexpr static VASurfaceID sInvalidFFMPEGSurfaceID = -1;
     38 
     39 namespace mozilla {
     40 
     41 RefPtr<layers::Image> VideoFrameSurface<LIBAV_VER>::GetAsImage() {
     42  return new layers::DMABUFSurfaceImage(mSurface);
     43 }
     44 
     45 VideoFrameSurface<LIBAV_VER>::VideoFrameSurface(DMABufSurface* aSurface,
     46                                                VASurfaceID aFFMPEGSurfaceID)
     47    : mSurface(aSurface),
     48      mLib(nullptr),
     49      mAVHWFrameContext(nullptr),
     50      mHWAVBuffer(nullptr),
     51      mFFMPEGSurfaceID(aFFMPEGSurfaceID),
     52      mHoldByFFmpeg(false) {
     53  // Create global refcount object to track mSurface usage over
     54  // gects rendering engine. We can't release it until it's used
     55  // by GL compositor / WebRender.
     56  MOZ_ASSERT(mSurface);
     57  MOZ_RELEASE_ASSERT(mSurface->GetAsDMABufSurfaceYUV());
     58  mSurface->GlobalRefCountCreate();
     59  DMABUF_LOG("VideoFrameSurface: creating surface UID %d FFmpeg ID %x",
     60             mSurface->GetUID(), aFFMPEGSurfaceID);
     61 }
     62 
     63 VideoFrameSurface<LIBAV_VER>::~VideoFrameSurface() {
     64  DMABUF_LOG("~VideoFrameSurface: deleting dmabuf surface UID %d",
     65             mSurface->GetUID());
     66  mSurface->GlobalRefCountDelete();
     67  // We're about to quit, no need to recycle the frames.
     68  if (mHoldByFFmpeg) {
     69    ReleaseVAAPIData(/* aForFrameRecycle */ false);
     70  }
     71 }
     72 
     73 void VideoFrameSurface<LIBAV_VER>::DisableRecycle() {
     74  MOZ_DIAGNOSTIC_ASSERT(mFFMPEGSurfaceID == sInvalidFFMPEGSurfaceID,
     75                        "VideoFrameSurface::DisableRecycle(): can't disable "
     76                        "recycle for FFmpeg surfaces!");
     77  mSurface->DisableRecycle();
     78 }
     79 
     80 void VideoFrameSurface<LIBAV_VER>::LockVAAPIData(
     81    AVCodecContext* aAVCodecContext, AVFrame* aAVFrame,
     82    const FFmpegLibWrapper* aLib) {
     83  mLib = aLib;
     84  mHoldByFFmpeg = true;
     85 
     86  // V4L2 frames don't have hw_frames_ctx because the v4l2-wrapper codecs
     87  // don't actually use hwaccel.  In this case we don't need to add a
     88  // HW frame context reference
     89  if (aAVCodecContext->hw_frames_ctx) {
     90    mAVHWFrameContext = aLib->av_buffer_ref(aAVCodecContext->hw_frames_ctx);
     91    mHWAVBuffer = aLib->av_buffer_ref(aAVFrame->buf[0]);
     92    DMABUF_LOG(
     93        "VideoFrameSurface: VAAPI locking dmabuf surface UID %d FFMPEG ID 0x%x "
     94        "mAVHWFrameContext %p mHWAVBuffer %p",
     95        mSurface->GetUID(), mFFMPEGSurfaceID, mAVHWFrameContext, mHWAVBuffer);
     96  } else {
     97    mAVHWFrameContext = nullptr;
     98    mHWAVBuffer = aLib->av_buffer_ref(aAVFrame->buf[0]);
     99    DMABUF_LOG(
    100        "VideoFrameSurface: V4L2 locking dmabuf surface UID %d FFMPEG ID 0x%x "
    101        "mHWAVBuffer %p",
    102        mSurface->GetUID(), mFFMPEGSurfaceID, mHWAVBuffer);
    103  }
    104 }
    105 
    106 void VideoFrameSurface<LIBAV_VER>::ReleaseVAAPIData(bool aForFrameRecycle) {
    107  DMABUF_LOG(
    108      "VideoFrameSurface: Releasing dmabuf surface UID %d FFMPEG ID 0x%x "
    109      "aForFrameRecycle %d mLib %p mAVHWFrameContext %p mHWAVBuffer %p",
    110      mSurface->GetUID(), mFFMPEGSurfaceID, aForFrameRecycle, mLib,
    111      mAVHWFrameContext, mHWAVBuffer);
    112  // It's possible to unref GPU data while IsUsedByRenderer() is still set.
    113  // It can happen when VideoFramePool is deleted while decoder shutdown
    114  // but related dmabuf surfaces are still used in another process.
    115  // In such case we don't care as the dmabuf surface will not be
    116  // recycled for another frame and stays here untill last fd of it
    117  // is closed.
    118  if (mLib) {
    119    mLib->av_buffer_unref(&mHWAVBuffer);
    120    if (mAVHWFrameContext) {
    121      mLib->av_buffer_unref(&mAVHWFrameContext);
    122    }
    123    mLib = nullptr;
    124  }
    125 
    126  mHoldByFFmpeg = false;
    127 
    128  // Release dmabuf surface now as we're going to replace it.
    129  if (aForFrameRecycle) {
    130    mSurface->ReleaseSurface();
    131  }
    132 
    133  if (aForFrameRecycle && IsUsedByRenderer()) {
    134    NS_WARNING("Reusing live dmabuf surface, visual glitches ahead");
    135  }
    136 }
    137 
    138 VideoFramePool<LIBAV_VER>::VideoFramePool(int aFFMPEGPoolSize)
    139    : mSurfaceLock("VideoFramePoolSurfaceLock"),
    140      mMaxFFMPEGPoolSize(aFFMPEGPoolSize) {
    141  DMABUF_LOG("VideoFramePool::VideoFramePool() pool size %d",
    142             mMaxFFMPEGPoolSize);
    143 }
    144 
    145 VideoFramePool<LIBAV_VER>::~VideoFramePool() {
    146  DMABUF_LOG("VideoFramePool::~VideoFramePool()");
    147  MutexAutoLock lock(mSurfaceLock);
    148  mDMABufSurfaces.Clear();
    149 }
    150 
    151 void VideoFramePool<LIBAV_VER>::ReleaseUnusedVAAPIFrames() {
    152  MutexAutoLock lock(mSurfaceLock);
    153  for (const auto& surface : mDMABufSurfaces) {
    154    if (!surface->mHoldByFFmpeg && surface->IsUsedByRenderer()) {
    155      DMABUF_LOG("Copied and used surface UID %d",
    156                 surface->GetDMABufSurface()->GetUID());
    157    }
    158    if (surface->mHoldByFFmpeg && !surface->IsUsedByRenderer()) {
    159      surface->ReleaseVAAPIData();
    160    }
    161  }
    162 }
    163 
    164 // Unlink all FFmpeg frames from ID. That ensures we'll allocate new
    165 // DMABuf surfaces with fresh UID and we won't recycle old ones.
    166 // It's used when FFmpeg invalides frames after avcodec_flush_buffers() call,
    167 // before seek for instance.
    168 void VideoFramePool<LIBAV_VER>::FlushFFmpegFrames() {
    169  MutexAutoLock lock(mSurfaceLock);
    170  for (const auto& surface : mDMABufSurfaces) {
    171    surface->mFFMPEGSurfaceID = sInvalidFFMPEGSurfaceID;
    172  }
    173 }
    174 
    175 RefPtr<VideoFrameSurface<LIBAV_VER>>
    176 VideoFramePool<LIBAV_VER>::GetFFmpegVideoFrameSurfaceLocked(
    177    const MutexAutoLock& aProofOfLock, VASurfaceID aFFMPEGSurfaceID) {
    178  MOZ_DIAGNOSTIC_ASSERT(
    179      aFFMPEGSurfaceID != sInvalidFFMPEGSurfaceID,
    180      "GetFFmpegVideoFrameSurfaceLocked(): expects valid aFFMPEGSurfaceID");
    181 
    182  // Try to find existing surface by ffmpeg ID. We want to re-use it
    183  // to keep matched surface UID / FFmpeg ID.
    184  for (auto& surface : mDMABufSurfaces) {
    185    if (surface->mFFMPEGSurfaceID == aFFMPEGSurfaceID) {
    186      // This should not happen as we reference FFmpeg surfaces from
    187      // renderer process.
    188      if (surface->IsUsedByRenderer()) {
    189        NS_WARNING("Using live surfaces, visual glitches ahead!");
    190      }
    191      return surface;
    192    }
    193  }
    194  return nullptr;
    195 }
    196 
    197 RefPtr<VideoFrameSurface<LIBAV_VER>>
    198 VideoFramePool<LIBAV_VER>::GetFreeVideoFrameSurfaceLocked(
    199    const MutexAutoLock& aProofOfLock) {
    200  for (auto& surface : mDMABufSurfaces) {
    201    if (surface->mFFMPEGSurfaceID != sInvalidFFMPEGSurfaceID) {
    202      continue;
    203    }
    204    if (surface->mHoldByFFmpeg) {
    205      continue;
    206    }
    207    if (surface->IsUsedByRenderer()) {
    208      continue;
    209    }
    210    surface->ReleaseVAAPIData();
    211    return surface;
    212  }
    213  return nullptr;
    214 }
    215 
    216 bool VideoFramePool<LIBAV_VER>::ShouldCopySurface() {
    217  // Number of used HW surfaces.
    218  int surfacesUsed = 0;
    219  int surfacesUsedFFmpeg = 0;
    220  for (const auto& surface : mDMABufSurfaces) {
    221    if (surface->IsUsedByRenderer()) {
    222      surfacesUsed++;
    223      if (surface->IsFFMPEGSurface()) {
    224        DMABUF_LOG("Used HW surface UID %d FFMPEG ID 0x%x\n",
    225                   surface->mSurface->GetUID(), surface->mFFMPEGSurfaceID);
    226        surfacesUsedFFmpeg++;
    227      }
    228    } else {
    229      if (surface->IsFFMPEGSurface()) {
    230        DMABUF_LOG("Free HW surface UID %d FFMPEG ID 0x%x\n",
    231                   surface->mSurface->GetUID(), surface->mFFMPEGSurfaceID);
    232      }
    233    }
    234  }
    235 
    236  // mMaxFFMPEGPoolSize can be zero for dynamic pools,
    237  // we don't do copy in that case unless it's requested by HW setup.
    238  float freeRatio =
    239      mMaxFFMPEGPoolSize
    240          ? 1.0f - (surfacesUsedFFmpeg / (float)mMaxFFMPEGPoolSize)
    241          : 1.0;
    242  DMABUF_LOG(
    243      "Surface pool size %d used copied %d used ffmpeg %d (max %d) free ratio "
    244      "%f",
    245      (int)mDMABufSurfaces.Length(), surfacesUsed - surfacesUsedFFmpeg,
    246      surfacesUsedFFmpeg, mMaxFFMPEGPoolSize, freeRatio);
    247  if (!gfx::gfxVars::HwDecodedVideoZeroCopy()) {
    248    return true;
    249  }
    250  return freeRatio < SURFACE_COPY_THRESHOLD;
    251 }
    252 
    253 RefPtr<VideoFrameSurface<LIBAV_VER>>
    254 VideoFramePool<LIBAV_VER>::GetTargetVideoFrameSurfaceLocked(
    255    const MutexAutoLock& aProofOfLock, VASurfaceID aFFmpegSurfaceID,
    256    bool aRecycleSurface) {
    257  RefPtr<DMABufSurfaceYUV> surface;
    258  RefPtr<VideoFrameSurface<LIBAV_VER>> videoSurface;
    259 
    260  // Look for surface pool to select existing or unused surface
    261  if (!aRecycleSurface) {
    262    // Copied surfaces are not recycled.
    263    videoSurface = GetFreeVideoFrameSurfaceLocked(aProofOfLock);
    264  } else {
    265    // Use FFmpeg ID to find appropriate dmabuf surface. We want to use
    266    // the same DMABuf surface for FFmpeg decoded frame (FFmpeg ID).
    267    // It allows us to recycle buffers in rendering process.
    268    MOZ_DIAGNOSTIC_ASSERT(aFFmpegSurfaceID != sInvalidFFMPEGSurfaceID,
    269                          "Wrong FFMPEGSurfaceID to recycle!");
    270    videoSurface =
    271        GetFFmpegVideoFrameSurfaceLocked(aProofOfLock, aFFmpegSurfaceID);
    272  }
    273 
    274  // Okay, create a new one
    275  if (!videoSurface) {
    276    surface = new DMABufSurfaceYUV();
    277    videoSurface = new VideoFrameSurface<LIBAV_VER>(
    278        surface, aRecycleSurface ? aFFmpegSurfaceID : sInvalidFFMPEGSurfaceID);
    279    mDMABufSurfaces.AppendElement(videoSurface);
    280    DMABUF_LOG("Added new DMABufSurface UID %d", surface->GetUID());
    281  } else {
    282    surface = videoSurface->GetDMABufSurface();
    283    DMABUF_LOG("Matched DMABufSurface UID %d", surface->GetUID());
    284  }
    285 
    286  return videoSurface;
    287 }
    288 
    289 RefPtr<VideoFrameSurface<LIBAV_VER>>
    290 VideoFramePool<LIBAV_VER>::GetVideoFrameSurface(
    291    VADRMPRIMESurfaceDescriptor& aVaDesc, int aWidth, int aHeight,
    292    AVCodecContext* aAVCodecContext, AVFrame* aAVFrame,
    293    const FFmpegLibWrapper* aLib) {
    294  if (aVaDesc.fourcc != VA_FOURCC_NV12 && aVaDesc.fourcc != VA_FOURCC_YV12 &&
    295      aVaDesc.fourcc != VA_FOURCC_P010 && aVaDesc.fourcc != VA_FOURCC_P016) {
    296    DMABUF_LOG("Unsupported VA-API surface format %d", aVaDesc.fourcc);
    297    return nullptr;
    298  }
    299 
    300  MutexAutoLock lock(mSurfaceLock);
    301 
    302  bool copySurface = mTextureCopyWorks && ShouldCopySurface();
    303 
    304  VASurfaceID ffmpegSurfaceID = (uintptr_t)aAVFrame->data[3];
    305  MOZ_DIAGNOSTIC_ASSERT(ffmpegSurfaceID != sInvalidFFMPEGSurfaceID,
    306                        "Exported invalid FFmpeg surface ID");
    307  DMABUF_LOG("Got VA-API DMABufSurface FFMPEG ID 0x%x", ffmpegSurfaceID);
    308 
    309  RefPtr<VideoFrameSurface<LIBAV_VER>> videoSurface =
    310      GetTargetVideoFrameSurfaceLocked(lock, ffmpegSurfaceID,
    311                                       /* aRecycleSurface */ !copySurface);
    312  RefPtr<DMABufSurfaceYUV> surface = videoSurface->GetDMABufSurface();
    313 
    314  if (!surface->UpdateYUVData(aVaDesc, aWidth, aHeight, copySurface)) {
    315    if (!copySurface) {
    316      // We failed to move data to DMABuf, so quit now.
    317      return nullptr;
    318    }
    319 
    320    // We failed to copy data, try again as move.
    321    DMABUF_LOG("  DMABuf texture copy is broken");
    322    copySurface = mTextureCopyWorks = false;
    323 
    324    videoSurface = GetTargetVideoFrameSurfaceLocked(lock, ffmpegSurfaceID,
    325                                                    /* aRecycleSurface */ true);
    326    surface = videoSurface->GetDMABufSurface();
    327    if (!surface->UpdateYUVData(aVaDesc, aWidth, aHeight,
    328                                /* copySurface */ false)) {
    329      return nullptr;
    330    }
    331  }
    332 
    333  if (MOZ_UNLIKELY(!mTextureCreationWorks)) {
    334    mTextureCreationWorks = Some(surface->VerifyTextureCreation());
    335    if (!*mTextureCreationWorks) {
    336      DMABUF_LOG("  failed to create texture over DMABuf memory!");
    337      return nullptr;
    338    }
    339  }
    340 
    341  if (copySurface) {
    342    // Disable recycling for copied DMABuf surfaces as we can't ensure
    343    // match between FFmpeg frame with DMABufSurface.
    344    // It doesn't matter much as surface copy uses extra GPU resources
    345    // anyway.
    346    videoSurface->DisableRecycle();
    347  } else {
    348    videoSurface->LockVAAPIData(aAVCodecContext, aAVFrame, aLib);
    349  }
    350 
    351  return videoSurface;
    352 }
    353 
    354 static gfx::SurfaceFormat GetSurfaceFormat(enum AVPixelFormat aPixFmt) {
    355  switch (aPixFmt) {
    356    case AV_PIX_FMT_YUV420P10LE:
    357      return gfx::SurfaceFormat::YUV420P10;
    358    case AV_PIX_FMT_YUV420P:
    359      return gfx::SurfaceFormat::YUV420;
    360    default:
    361      return gfx::SurfaceFormat::UNKNOWN;
    362  }
    363 }
    364 
    365 // TODO: Add support for AV_PIX_FMT_YUV444P / AV_PIX_FMT_GBRP
    366 RefPtr<VideoFrameSurface<LIBAV_VER>>
    367 VideoFramePool<LIBAV_VER>::GetVideoFrameSurface(
    368    const layers::PlanarYCbCrData& aData, AVCodecContext* aAVCodecContext) {
    369  static gfx::SurfaceFormat format = GetSurfaceFormat(aAVCodecContext->pix_fmt);
    370  if (format == gfx::SurfaceFormat::UNKNOWN) {
    371    DMABUF_LOG("Unsupported FFmpeg DMABuf format %x", aAVCodecContext->pix_fmt);
    372    return nullptr;
    373  }
    374 
    375  MutexAutoLock lock(mSurfaceLock);
    376 
    377  RefPtr<VideoFrameSurface<LIBAV_VER>> videoSurface =
    378      GetTargetVideoFrameSurfaceLocked(lock, sInvalidFFMPEGSurfaceID,
    379                                       /* aRecycleSurface */ false);
    380  RefPtr<DMABufSurfaceYUV> surface = videoSurface->GetDMABufSurface();
    381 
    382  DMABUF_LOG("Using SW DMABufSurface UID %d", surface->GetUID());
    383 
    384  if (!surface->UpdateYUVData(aData, format)) {
    385    DMABUF_LOG("  failed to convert YUV data to DMABuf memory!");
    386    return nullptr;
    387  }
    388 
    389  if (MOZ_UNLIKELY(!mTextureCreationWorks)) {
    390    mTextureCreationWorks = Some(surface->VerifyTextureCreation());
    391    if (!*mTextureCreationWorks) {
    392      DMABUF_LOG("  failed to create texture over DMABuf memory!");
    393      return nullptr;
    394    }
    395  }
    396 
    397  // Disable recycling for copied DMABuf surfaces as we can't ensure
    398  // match between FFmpeg frame with DMABufSurface.
    399  // It doesn't matter much as surface copy/texture upload uses extra
    400  // GPU resources anyway.
    401  videoSurface->DisableRecycle();
    402  return videoSurface;
    403 }
    404 
    405 // Convert an FFmpeg-specific DRM descriptor into a
    406 // VADRMPRIMESurfaceDescriptor.  There is no fundamental difference between
    407 // the descriptor structs and using the latter means this can use all the
    408 // existing machinery in DMABufSurfaceYUV.
    409 static Maybe<VADRMPRIMESurfaceDescriptor> FFmpegDescToVA(
    410    AVDRMFrameDescriptor& aDesc, AVFrame* aAVFrame) {
    411  VADRMPRIMESurfaceDescriptor vaDesc{};
    412 
    413  if (aAVFrame->format != AV_PIX_FMT_DRM_PRIME) {
    414    DMABUF_LOG("Got non-DRM-PRIME frame from FFmpeg V4L2");
    415    return Nothing();
    416  }
    417 
    418  if (aAVFrame->crop_top != 0 || aAVFrame->crop_left != 0) {
    419    DMABUF_LOG("Top and left-side cropping are not supported");
    420    return Nothing();
    421  }
    422 
    423  // Width and height after crop
    424  vaDesc.width = aAVFrame->width;
    425  vaDesc.height = aAVFrame->height - aAVFrame->crop_bottom;
    426 
    427  // Native width and height before crop is applied
    428  unsigned int uncrop_width = aDesc.layers[0].planes[0].pitch;
    429  unsigned int uncrop_height = aAVFrame->height;
    430 
    431  unsigned int offset = aDesc.layers[0].planes[0].offset;
    432 
    433  if (aDesc.layers[0].format == DRM_FORMAT_YUV420) {
    434    vaDesc.fourcc = VA_FOURCC_I420;
    435 
    436    // V4L2 expresses YUV420 as a single contiguous buffer containing
    437    // all three planes.  DMABufSurfaceYUV expects the three planes
    438    // separately, so we have to split them out
    439    MOZ_ASSERT(aDesc.nb_objects == 1);
    440    MOZ_ASSERT(aDesc.nb_layers == 1);
    441 
    442    vaDesc.num_objects = 1;
    443    vaDesc.objects[0].drm_format_modifier = aDesc.objects[0].format_modifier;
    444    vaDesc.objects[0].size = aDesc.objects[0].size;
    445    vaDesc.objects[0].fd = aDesc.objects[0].fd;
    446 
    447    vaDesc.num_layers = 3;
    448    for (int i = 0; i < 3; i++) {
    449      vaDesc.layers[i].drm_format = DRM_FORMAT_R8;
    450      vaDesc.layers[i].num_planes = 1;
    451      vaDesc.layers[i].object_index[0] = 0;
    452    }
    453    vaDesc.layers[0].offset[0] = offset;
    454    vaDesc.layers[0].pitch[0] = uncrop_width;
    455    vaDesc.layers[1].offset[0] = offset + uncrop_width * uncrop_height;
    456    vaDesc.layers[1].pitch[0] = uncrop_width / 2;
    457    vaDesc.layers[2].offset[0] = offset + uncrop_width * uncrop_height * 5 / 4;
    458    vaDesc.layers[2].pitch[0] = uncrop_width / 2;
    459  } else if (aDesc.layers[0].format == DRM_FORMAT_NV12) {
    460    vaDesc.fourcc = VA_FOURCC_NV12;
    461 
    462    // V4L2 expresses NV12 as a single contiguous buffer containing both
    463    // planes.  DMABufSurfaceYUV expects the two planes separately, so we have
    464    // to split them out
    465    MOZ_ASSERT(aDesc.nb_objects == 1);
    466    MOZ_ASSERT(aDesc.nb_layers == 1);
    467 
    468    vaDesc.num_objects = 1;
    469    vaDesc.objects[0].drm_format_modifier = aDesc.objects[0].format_modifier;
    470    vaDesc.objects[0].size = aDesc.objects[0].size;
    471    vaDesc.objects[0].fd = aDesc.objects[0].fd;
    472 
    473    vaDesc.num_layers = 2;
    474    for (int i = 0; i < 2; i++) {
    475      vaDesc.layers[i].num_planes = 1;
    476      vaDesc.layers[i].object_index[0] = 0;
    477      vaDesc.layers[i].pitch[0] = uncrop_width;
    478    }
    479    vaDesc.layers[0].drm_format = DRM_FORMAT_R8;  // Y plane
    480    vaDesc.layers[0].offset[0] = offset;
    481    vaDesc.layers[1].drm_format = DRM_FORMAT_GR88;  // UV plane
    482    vaDesc.layers[1].offset[0] = offset + uncrop_width * uncrop_height;
    483  } else {
    484    DMABUF_LOG("Don't know how to deal with FOURCC 0x%x",
    485               aDesc.layers[0].format);
    486    return Nothing();
    487  }
    488 
    489  return Some(vaDesc);
    490 }
    491 
    492 RefPtr<VideoFrameSurface<LIBAV_VER>>
    493 VideoFramePool<LIBAV_VER>::GetVideoFrameSurface(AVDRMFrameDescriptor& aDesc,
    494                                                int aWidth, int aHeight,
    495                                                AVCodecContext* aAVCodecContext,
    496                                                AVFrame* aAVFrame,
    497                                                const FFmpegLibWrapper* aLib) {
    498  MOZ_ASSERT(aDesc.nb_layers > 0);
    499 
    500  auto layerDesc = FFmpegDescToVA(aDesc, aAVFrame);
    501  if (layerDesc.isNothing()) {
    502    return nullptr;
    503  }
    504 
    505  // Width and height, after cropping
    506  int crop_width = (int)layerDesc->width;
    507  int crop_height = (int)layerDesc->height;
    508 
    509  MutexAutoLock lock(mSurfaceLock);
    510 
    511  RefPtr<VideoFrameSurface<LIBAV_VER>> videoSurface =
    512      GetTargetVideoFrameSurfaceLocked(lock, sInvalidFFMPEGSurfaceID,
    513                                       /* aRecycleSurface */ false);
    514  RefPtr<DMABufSurfaceYUV> surface = videoSurface->GetDMABufSurface();
    515 
    516  DMABUF_LOG("Using V4L2 DMABufSurface UID %d", surface->GetUID());
    517 
    518  bool copySurface = mTextureCopyWorks && ShouldCopySurface();
    519  if (!surface->UpdateYUVData(layerDesc.value(), crop_width, crop_height,
    520                              copySurface)) {
    521    if (!copySurface) {
    522      // Failed without texture copy. We can't do more here.
    523      return nullptr;
    524    }
    525    // Try again without texture copy
    526    DMABUF_LOG("  DMABuf texture copy is broken");
    527    copySurface = mTextureCopyWorks = false;
    528    if (!surface->UpdateYUVData(layerDesc.value(), crop_width, crop_height,
    529                                copySurface)) {
    530      return nullptr;
    531    }
    532  }
    533 
    534  if (MOZ_UNLIKELY(!mTextureCreationWorks)) {
    535    mTextureCreationWorks = Some(surface->VerifyTextureCreation());
    536    if (!*mTextureCreationWorks) {
    537      DMABUF_LOG("  failed to create texture over DMABuf memory!");
    538      return nullptr;
    539    }
    540  }
    541 
    542  // Don't recycle v4l surfaces, we don't have FFmpegID and we can't ensure
    543  // match between FFmpeg frame with DMABufSurface.
    544  videoSurface->DisableRecycle();
    545 
    546  if (!copySurface) {
    547    videoSurface->LockVAAPIData(aAVCodecContext, aAVFrame, aLib);
    548  }
    549 
    550  return videoSurface;
    551 }
    552 
    553 }  // namespace mozilla