Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/media/platforms/ffmpeg/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 24 kB image not shown  

Quelle  FFmpegVideoFramePool.cpp

  Sprache: C
 

/* 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 "PlatformDecoderModule.h"
#include "libavutil/pixfmt.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/widget/DMABufDevice.h"
#include "mozilla/widget/va_drmcommon.h"

// DMABufLibWrapper defines its own version of this which collides with the
// official version in drm_fourcc.h
#ifdef DRM_FORMAT_MOD_INVALID
#  undef DRM_FORMAT_MOD_INVALID
#endif
#include <libdrm/drm_fourcc.h>

#ifdef MOZ_LOGGING
#  undef DMABUF_LOG
extern mozilla::LazyLogModule gDmabufLog;
#  define DMABUF_LOG(str, ...) \
    MOZ_LOG_FMT(gDmabufLog, mozilla::LogLevel::Debug, str, ##__VA_ARGS__)
#else
#  define DMABUF_LOG(args)
#endif /* MOZ_LOGGING */

// Start copying surfaces when free ffmpeg surface count is below 1/4 of all
// available surfaces.
#define SURFACE_COPY_THRESHOLD (1.0f / 4.0f)

constexpr static VASurfaceID sInvalidFFMPEGSurfaceID = -1;

namespace mozilla {

RefPtr<layers::Image> VideoFrameSurface<LIBAV_VER>::GetAsImage() {
  return new layers::DMABUFSurfaceImage(mSurface);
}

VideoFrameSurface<LIBAV_VER>::VideoFrameSurface(DMABufSurface* aSurface,
                                                VASurfaceID aFFMPEGSurfaceID)
    : mSurface(aSurface),
      mLib(nullptr),
      mAVHWFrameContext(nullptr),
      mHWAVBuffer(nullptr),
      mFFMPEGSurfaceID(aFFMPEGSurfaceID),
      mHoldByFFmpeg(false) {
  // 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();
  DMABUF_LOG("VideoFrameSurface: creating surface UID {} FFmpeg ID {:x}",
             mSurface->GetUID(), aFFMPEGSurfaceID);
}

VideoFrameSurface<LIBAV_VER>::~VideoFrameSurface() {
  DMABUF_LOG("~VideoFrameSurface: deleting dmabuf surface UID {}",
             mSurface->GetUID());
  mSurface->GlobalRefCountDelete();
  // We're about to quit, no need to recycle the frames.
  if (mHoldByFFmpeg) {
    ReleaseVAAPIData(/* aForFrameRecycle */ false);
  }
}

void VideoFrameSurface<LIBAV_VER>::DisableRecycle() {
  MOZ_DIAGNOSTIC_ASSERT(mFFMPEGSurfaceID == sInvalidFFMPEGSurfaceID,
                        "VideoFrameSurface::DisableRecycle(): can't disable "
                        "recycle for FFmpeg surfaces!");
  mSurface->DisableRecycle();
}

void VideoFrameSurface<LIBAV_VER>::LockVAAPIData(
    AVCodecContext* aAVCodecContext, AVFrame* aAVFrame,
    const FFmpegLibWrapper* aLib) {
  mLib = aLib;
  mHoldByFFmpeg = true;

  // V4L2 frames don't have hw_frames_ctx because the v4l2-wrapper codecs
  // don't actually use hwaccel.  In this case we don't need to add a
  // HW frame context reference
  if (aAVCodecContext->hw_frames_ctx) {
    mAVHWFrameContext = aLib->av_buffer_ref(aAVCodecContext->hw_frames_ctx);
    mHWAVBuffer = aLib->av_buffer_ref(aAVFrame->buf[0]);
    DMABUF_LOG(
        "VideoFrameSurface: VAAPI locking dmabuf surface UID {} FFMPEG ID "
        "0x{:x} "
        "mAVHWFrameContext {} mHWAVBuffer {}",
        mSurface->GetUID(), mFFMPEGSurfaceID, fmt::ptr(mAVHWFrameContext),
        fmt::ptr(mHWAVBuffer));
  } else {
    mAVHWFrameContext = nullptr;
    mHWAVBuffer = aLib->av_buffer_ref(aAVFrame->buf[0]);
    DMABUF_LOG(
        "VideoFrameSurface: V4L2 locking dmabuf surface UID {} FFMPEG ID "
        "0x{:x} "
        "mHWAVBuffer {}",
        mSurface->GetUID(), mFFMPEGSurfaceID, fmt::ptr(mHWAVBuffer));
  }
}

void VideoFrameSurface<LIBAV_VER>::ReleaseVAAPIData(bool aForFrameRecycle) {
  DMABUF_LOG(
      "VideoFrameSurface: Releasing dmabuf surface UID {} FFMPEG ID 0x{:x} "
      "aForFrameRecycle {} mLib {} mAVHWFrameContext {} mHWAVBuffer {}",
      mSurface->GetUID(), mFFMPEGSurfaceID, aForFrameRecycle, fmt::ptr(mLib),
      fmt::ptr(mAVHWFrameContext), fmt::ptr(mHWAVBuffer));
  // It's possible to unref GPU data while IsUsedByRenderer() is still set.
  // It can happen 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.
  if (mLib) {
    mLib->av_buffer_unref(&mHWAVBuffer);
    if (mAVHWFrameContext) {
      mLib->av_buffer_unref(&mAVHWFrameContext);
    }
    mLib = nullptr;
  }

  mHoldByFFmpeg = false;
  mVulkanCopySlotIndex = -1;

  // Release dmabuf surface now as we're going to replace it.
  if (aForFrameRecycle) {
    mSurface->ReleaseSurface();
  }

  if (aForFrameRecycle && IsUsedByRenderer()) {
    NS_WARNING("Reusing live dmabuf surface, visual glitches ahead");
  }
}

VideoFramePool<LIBAV_VER>::VideoFramePool(int aFFMPEGPoolSize)
    : mSurfaceLock("VideoFramePoolSurfaceLock"),
      mMaxFFMPEGPoolSize(aFFMPEGPoolSize) {
  DMABUF_LOG("VideoFramePool::VideoFramePool() pool size {}",
             mMaxFFMPEGPoolSize);
}

VideoFramePool<LIBAV_VER>::~VideoFramePool() {
  DMABUF_LOG("VideoFramePool::~VideoFramePool()");
  MutexAutoLock lock(mSurfaceLock);
  mDMABufSurfaces.Clear();
}

bool VideoFramePool<LIBAV_VER>::IsVulkanFrameSlotInUseByRenderer(
    int32_t aSlotIndex) {
  if (aSlotIndex < 0) {
    return false;
  }
  MutexAutoLock lock(mSurfaceLock);
  for (const auto& surface : mDMABufSurfaces) {
    if (surface->mVulkanCopySlotIndex == aSlotIndex) {
      return surface->IsUsedByRenderer();
    }
  }
  return false;
}

void VideoFramePool<LIBAV_VER>::ReleaseUnusedVAAPIFrames() {
  MutexAutoLock lock(mSurfaceLock);
  for (const auto& surface : mDMABufSurfaces) {
    if (!surface->mHoldByFFmpeg && surface->IsUsedByRenderer()) {
      DMABUF_LOG("Copied and used surface UID {}",
                 surface->GetDMABufSurface()->GetUID());
    }
    if (surface->mHoldByFFmpeg && !surface->IsUsedByRenderer()) {
      surface->ReleaseVAAPIData();
    }
  }
}

// Unlink all FFmpeg frames from ID. That ensures we'll allocate new
// DMABuf surfaces with fresh UID and we won't recycle old ones.
// It's used when FFmpeg invalides frames after avcodec_flush_buffers() call,
// before seek for instance.
void VideoFramePool<LIBAV_VER>::FlushFFmpegFrames() {
  MutexAutoLock lock(mSurfaceLock);
  for (const auto& surface : mDMABufSurfaces) {
    surface->mFFMPEGSurfaceID = sInvalidFFMPEGSurfaceID;
  }
}

RefPtr<VideoFrameSurface<LIBAV_VER>>
VideoFramePool<LIBAV_VER>::GetFFmpegVideoFrameSurfaceLocked(
    const MutexAutoLock& aProofOfLock, VASurfaceID aFFMPEGSurfaceID) {
  MOZ_DIAGNOSTIC_ASSERT(
      aFFMPEGSurfaceID != sInvalidFFMPEGSurfaceID,
      "GetFFmpegVideoFrameSurfaceLocked(): expects valid aFFMPEGSurfaceID");

  // Try to find existing surface by ffmpeg ID. We want to re-use it
  // to keep matched surface UID / FFmpeg ID.
  for (auto& surface : mDMABufSurfaces) {
    if (surface->mFFMPEGSurfaceID == aFFMPEGSurfaceID) {
      if (surface->IsUsedByRenderer()) {
        // A re-output frame (e.g. VP9/AV1 show_existing_frame) re-displays an
        // already-decoded frame from the reference buffer, so it can carry the
        // same surface id as a frame the renderer is still using. When the
        // matched surface is still in use, detach it from this FFmpeg id and
        // let the caller allocate a fresh surface instead (as
        // GetFreeVideoFrameSurfaceLocked does).
        surface->mFFMPEGSurfaceID = sInvalidFFMPEGSurfaceID;
        return nullptr;
      }
      return surface;
    }
  }
  return nullptr;
}

RefPtr<VideoFrameSurface<LIBAV_VER>>
VideoFramePool<LIBAV_VER>::GetFreeVideoFrameSurfaceLocked(
    const MutexAutoLock& aProofOfLock) {
  for (auto& surface : mDMABufSurfaces) {
    if (surface->mFFMPEGSurfaceID != sInvalidFFMPEGSurfaceID) {
      continue;
    }
    if (surface->mHoldByFFmpeg) {
      continue;
    }
    if (surface->IsUsedByRenderer()) {
      continue;
    }
    surface->ReleaseVAAPIData();
    return surface;
  }
  return nullptr;
}

bool VideoFramePool<LIBAV_VER>::ShouldCopySurface() {
  // Number of used HW surfaces.
  int surfacesUsed = 0;
  int surfacesUsedFFmpeg = 0;
  for (const auto& surface : mDMABufSurfaces) {
    if (surface->IsUsedByRenderer()) {
      surfacesUsed++;
      if (surface->IsFFMPEGSurface()) {
        DMABUF_LOG("Used HW surface UID {} FFMPEG ID 0x{:x}\n",
                   surface->mSurface->GetUID(), surface->mFFMPEGSurfaceID);
        surfacesUsedFFmpeg++;
      }
    } else {
      if (surface->IsFFMPEGSurface()) {
        DMABUF_LOG("Free HW surface UID {} FFMPEG ID 0x{:x}\n",
                   surface->mSurface->GetUID(), surface->mFFMPEGSurfaceID);
      }
    }
  }

  // mMaxFFMPEGPoolSize can be zero for dynamic pools,
  // we don't do copy in that case unless it's requested by HW setup.
  float freeRatio =
      mMaxFFMPEGPoolSize
          ? 1.0f - (surfacesUsedFFmpeg / (float)mMaxFFMPEGPoolSize)
          : 1.0;
  DMABUF_LOG(
      "Surface pool size {} used copied {} used ffmpeg {} (max {}) free ratio "
      "{}",
      static_cast<int>(mDMABufSurfaces.Length()),
      surfacesUsed - surfacesUsedFFmpeg, surfacesUsedFFmpeg, mMaxFFMPEGPoolSize,
      freeRatio);
  if (!gfx::gfxVars::HwDecodedVideoZeroCopy()) {
    return true;
  }
  return freeRatio < SURFACE_COPY_THRESHOLD;
}

RefPtr<VideoFrameSurface<LIBAV_VER>>
VideoFramePool<LIBAV_VER>::GetTargetVideoFrameSurfaceLocked(
    const MutexAutoLock& aProofOfLock, VASurfaceID aFFmpegSurfaceID,
    bool aRecycleSurface) {
  RefPtr<DMABufSurfaceYUV> surface;
  RefPtr<VideoFrameSurface<LIBAV_VER>> videoSurface;

  // Look for surface pool to select existing or unused surface
  if (!aRecycleSurface) {
    // Copied surfaces are not recycled.
    videoSurface = GetFreeVideoFrameSurfaceLocked(aProofOfLock);
  } else {
    // Use FFmpeg ID to find appropriate dmabuf surface. We want to use
    // the same DMABuf surface for FFmpeg decoded frame (FFmpeg ID).
    // It allows us to recycle buffers in rendering process.
    MOZ_DIAGNOSTIC_ASSERT(aFFmpegSurfaceID != sInvalidFFMPEGSurfaceID,
                          "Wrong FFMPEGSurfaceID to recycle!");
    videoSurface =
        GetFFmpegVideoFrameSurfaceLocked(aProofOfLock, aFFmpegSurfaceID);
  }

  // Okay, create a new one
  if (!videoSurface) {
    surface = new DMABufSurfaceYUV();
    videoSurface = new VideoFrameSurface<LIBAV_VER>(
        surface, aRecycleSurface ? aFFmpegSurfaceID : sInvalidFFMPEGSurfaceID);
    mDMABufSurfaces.AppendElement(videoSurface);
    DMABUF_LOG("Added new DMABufSurface UID {}", surface->GetUID());
  } else {
    surface = videoSurface->GetDMABufSurface();
    DMABUF_LOG("Matched DMABufSurface UID {}", surface->GetUID());
  }

  return videoSurface;
}

RefPtr<VideoFrameSurface<LIBAV_VER>>
VideoFramePool<LIBAV_VER>::GetVideoFrameSurface(
    VADRMPRIMESurfaceDescriptor& aVaDesc, int aWidth, int aHeight,
    AVCodecContext* aAVCodecContext, AVFrame* aAVFrame,
    const FFmpegLibWrapper* aLib) {
  if (aVaDesc.fourcc != VA_FOURCC_NV12 && aVaDesc.fourcc != VA_FOURCC_YV12 &&
      aVaDesc.fourcc != VA_FOURCC_P010 && aVaDesc.fourcc != VA_FOURCC_P016) {
    DMABUF_LOG("Unsupported VA-API surface format {}", aVaDesc.fourcc);
    return nullptr;
  }

  MutexAutoLock lock(mSurfaceLock);

  bool copySurface = mTextureCopyWorks && ShouldCopySurface();

  VASurfaceID ffmpegSurfaceID = (uintptr_t)aAVFrame->data[3];
  MOZ_DIAGNOSTIC_ASSERT(ffmpegSurfaceID != sInvalidFFMPEGSurfaceID,
                        "Exported invalid FFmpeg surface ID");
  DMABUF_LOG("Got VA-API DMABufSurface FFMPEG ID 0x{:x}", ffmpegSurfaceID);

  RefPtr<VideoFrameSurface<LIBAV_VER>> videoSurface =
      GetTargetVideoFrameSurfaceLocked(lock, ffmpegSurfaceID,
                                       /* aRecycleSurface */ !copySurface);
  RefPtr<DMABufSurfaceYUV> surface = videoSurface->GetDMABufSurface();

  if (!surface->UpdateYUVData(aVaDesc, aWidth, aHeight, copySurface)) {
    if (!copySurface) {
      // We failed to move data to DMABuf, so quit now.
      return nullptr;
    }

    // We failed to copy data, try again as move.
    DMABUF_LOG("  DMABuf texture copy is broken");
    copySurface = mTextureCopyWorks = false;

    videoSurface = GetTargetVideoFrameSurfaceLocked(lock, ffmpegSurfaceID,
                                                    /* aRecycleSurface */ true);
    surface = videoSurface->GetDMABufSurface();
    if (!surface->UpdateYUVData(aVaDesc, aWidth, aHeight,
                                /* copySurface */ false)) {
      return nullptr;
    }
  }

  if (MOZ_UNLIKELY(!mTextureCreationWorks)) {
    mTextureCreationWorks = Some(surface->VerifyTextureCreation());
    if (!*mTextureCreationWorks) {
      DMABUF_LOG("  failed to create texture over DMABuf memory!");
      return nullptr;
    }
  }

  if (copySurface) {
    // Disable recycling for copied DMABuf surfaces as we can't ensure
    // match between FFmpeg frame with DMABufSurface.
    // It doesn't matter much as surface copy uses extra GPU resources
    // anyway.
    videoSurface->DisableRecycle();
  } else {
    videoSurface->LockVAAPIData(aAVCodecContext, aAVFrame, aLib);
  }

  return videoSurface;
}

static gfx::SurfaceFormat GetSurfaceFormat(enum AVPixelFormat aPixFmt) {
  switch (aPixFmt) {
    case AV_PIX_FMT_YUV420P10LE:
      return gfx::SurfaceFormat::YUV420P10;
    case AV_PIX_FMT_YUV420P:
      return gfx::SurfaceFormat::YUV420;
    default:
      return gfx::SurfaceFormat::UNKNOWN;
  }
}

// TODO: Add support for AV_PIX_FMT_YUV444P / AV_PIX_FMT_GBRP
RefPtr<VideoFrameSurface<LIBAV_VER>>
VideoFramePool<LIBAV_VER>::GetVideoFrameSurface(
    const layers::PlanarYCbCrData& aData, AVCodecContext* aAVCodecContext) {
  static gfx::SurfaceFormat format = GetSurfaceFormat(aAVCodecContext->pix_fmt);
  if (format == gfx::SurfaceFormat::UNKNOWN) {
    DMABUF_LOG("Unsupported FFmpeg DMABuf format {:x}",
               static_cast<int>(aAVCodecContext->pix_fmt));
    return nullptr;
  }

  MutexAutoLock lock(mSurfaceLock);

  RefPtr<VideoFrameSurface<LIBAV_VER>> videoSurface =
      GetTargetVideoFrameSurfaceLocked(lock, sInvalidFFMPEGSurfaceID,
                                       /* aRecycleSurface */ false);
  RefPtr<DMABufSurfaceYUV> surface = videoSurface->GetDMABufSurface();

  DMABUF_LOG("Using SW DMABufSurface UID {}", surface->GetUID());

  if (!surface->UpdateYUVData(aData, format)) {
    DMABUF_LOG("  failed to convert YUV data to DMABuf memory!");
    return nullptr;
  }

  if (MOZ_UNLIKELY(!mTextureCreationWorks)) {
    mTextureCreationWorks = Some(surface->VerifyTextureCreation());
    if (!*mTextureCreationWorks) {
      DMABUF_LOG("  failed to create texture over DMABuf memory!");
      return nullptr;
    }
  }

  // Disable recycling for copied DMABuf surfaces as we can't ensure
  // match between FFmpeg frame with DMABufSurface.
  // It doesn't matter much as surface copy/texture upload uses extra
  // GPU resources anyway.
  videoSurface->DisableRecycle();
  return videoSurface;
}

// Convert an FFmpeg-specific DRM descriptor into a
// VADRMPRIMESurfaceDescriptor.  There is no fundamental difference between
// the descriptor structs and using the latter means this can use all the
// existing machinery in DMABufSurfaceYUV.
static Maybe<VADRMPRIMESurfaceDescriptor> FFmpegDescToVA(
    AVDRMFrameDescriptor& aDesc, AVFrame* aAVFrame) {
  VADRMPRIMESurfaceDescriptor vaDesc{};

  if (aAVFrame->format != AV_PIX_FMT_DRM_PRIME) {
    if (aAVFrame->hw_frames_ctx) {
      AVHWDeviceType hwdeviceType =
          ((AVHWDeviceContext*)((AVHWFramesContext*)
                                    aAVFrame->hw_frames_ctx->data)
               ->device_ref->data)
              ->type;
      DMABUF_LOG("Got non-DRM-PRIME frame from FFmpeg AVHWDeviceType {}",
                 static_cast<int>(hwdeviceType));
    }
    return Nothing();
  }

  if (aAVFrame->crop_top != 0 || aAVFrame->crop_left != 0) {
    DMABUF_LOG("Top and left-side cropping are not supported");
    return Nothing();
  }

  // Width and height after crop
  vaDesc.width = aAVFrame->width;
  vaDesc.height = aAVFrame->height - aAVFrame->crop_bottom;

  // Native width and height before crop is applied
  unsigned int uncrop_width = aDesc.layers[0].planes[0].pitch;
  unsigned int uncrop_height = aAVFrame->height;

  const unsigned int yPitch = uncrop_width;
  unsigned int uvPitch = yPitch;
#if defined(MOZ_ENABLE_VULKAN_VIDEO) && LIBAVCODEC_VERSION_MAJOR >= 59
  // Vulkan can expose a distinct UV row stride (GPU drivers may require the
  // correct UV pitch for EGL DMA-BUF import); VA-API/V4L2 paths keep both
  // pitches equal to the historical behavior.
  if (aAVFrame->hw_frames_ctx) {
    AVHWDeviceType hwdeviceType =
        ((AVHWDeviceContext*)((AVHWFramesContext*)aAVFrame->hw_frames_ctx->data)
             ->device_ref->data)
            ->type;
    if ((hwdeviceType == AV_HWDEVICE_TYPE_VULKAN) &&
        (aDesc.layers[0].nb_planes >= 2) &&
        (aDesc.layers[0].planes[1].pitch > 0)) {
      uvPitch = static_cast<unsigned int>(aDesc.layers[0].planes[1].pitch);
    }
  }
#endif

  unsigned int offset = aDesc.layers[0].planes[0].offset;

  if (aDesc.layers[0].format == DRM_FORMAT_YUV420) {
    vaDesc.fourcc = VA_FOURCC_I420;

    // V4L2 expresses YUV420 as a single contiguous buffer containing
    // all three planes.  DMABufSurfaceYUV expects the three planes
    // separately, so we have to split them out
    MOZ_ASSERT(aDesc.nb_objects == 1);
    MOZ_ASSERT(aDesc.nb_layers == 1);

    vaDesc.num_objects = 1;
    vaDesc.objects[0].drm_format_modifier = aDesc.objects[0].format_modifier;
    vaDesc.objects[0].size = aDesc.objects[0].size;
    vaDesc.objects[0].fd = aDesc.objects[0].fd;

    vaDesc.num_layers = 3;
    for (int i = 0; i < 3; i++) {
      vaDesc.layers[i].drm_format = DRM_FORMAT_R8;
      vaDesc.layers[i].num_planes = 1;
      vaDesc.layers[i].object_index[0] = 0;
    }
    vaDesc.layers[0].offset[0] = offset;
    vaDesc.layers[0].pitch[0] = uncrop_width;
    vaDesc.layers[1].offset[0] = offset + uncrop_width * uncrop_height;
    vaDesc.layers[1].pitch[0] = uncrop_width / 2;
    vaDesc.layers[2].offset[0] = offset + uncrop_width * uncrop_height * 5 / 4;
    vaDesc.layers[2].pitch[0] = uncrop_width / 2;
  } else if (aDesc.layers[0].format == DRM_FORMAT_NV12 &&
             aDesc.nb_layers == 1) {
    vaDesc.fourcc = VA_FOURCC_NV12;

    // V4L2 and Vulkan express NV12 as a single contiguous buffer containing
    // both planes.  DMABufSurfaceYUV expects the two planes separately, so we
    // have to split them out
    MOZ_ASSERT(aDesc.nb_objects == 1);
    MOZ_ASSERT(aDesc.nb_layers == 1);

    vaDesc.num_objects = 1;
    vaDesc.objects[0].drm_format_modifier = aDesc.objects[0].format_modifier;
    vaDesc.objects[0].size = aDesc.objects[0].size;
    vaDesc.objects[0].fd = aDesc.objects[0].fd;

    vaDesc.num_layers = 2;
    for (int i = 0; i < 2; i++) {
      vaDesc.layers[i].num_planes = 1;
      vaDesc.layers[i].object_index[0] = 0;
    }
    vaDesc.layers[0].pitch[0] = yPitch;
    vaDesc.layers[1].pitch[0] = uvPitch;
    vaDesc.layers[0].drm_format = DRM_FORMAT_R8;  // Y plane
    vaDesc.layers[0].offset[0] = offset;
    vaDesc.layers[1].drm_format = DRM_FORMAT_GR88;  // UV plane
    // Use actual UV offset if available (Vulkan), otherwise compute (V4L2)
    vaDesc.layers[1].offset[0] = aDesc.layers[0].nb_planes >= 2
                                     ? aDesc.layers[0].planes[1].offset
                                     : offset + yPitch * uncrop_height;
  } else if (aDesc.layers[0].format == DRM_FORMAT_P010 &&
             aDesc.nb_layers == 1) {
    vaDesc.fourcc = VA_FOURCC_P010;

    MOZ_ASSERT(aDesc.nb_objects == 1);
    MOZ_ASSERT(aDesc.nb_layers == 1);

    vaDesc.num_objects = 1;
    vaDesc.objects[0].drm_format_modifier = aDesc.objects[0].format_modifier;
    vaDesc.objects[0].size = aDesc.objects[0].size;
    vaDesc.objects[0].fd = aDesc.objects[0].fd;

    vaDesc.num_layers = 2;
    for (int i = 0; i < 2; i++) {
      vaDesc.layers[i].num_planes = 1;
      vaDesc.layers[i].object_index[0] = 0;
    }
    vaDesc.layers[0].pitch[0] = yPitch;
    vaDesc.layers[1].pitch[0] = uvPitch;
    vaDesc.layers[0].drm_format = DRM_FORMAT_R16;  // Y plane
    vaDesc.layers[0].offset[0] = offset;
    vaDesc.layers[1].drm_format = DRM_FORMAT_GR1616;  // UV plane
    vaDesc.layers[1].offset[0] = aDesc.layers[0].nb_planes >= 2
                                     ? aDesc.layers[0].planes[1].offset
                                     : offset + yPitch * uncrop_height;
  } else if (aDesc.nb_layers == 2 && aDesc.layers[0].format == DRM_FORMAT_R8 &&
             aDesc.layers[1].format == DRM_FORMAT_GR88 &&
             aDesc.nb_objects == 1) {
    vaDesc.fourcc = VA_FOURCC_NV12;
    vaDesc.num_objects = 1;
    vaDesc.objects[0].drm_format_modifier = aDesc.objects[0].format_modifier;
    vaDesc.objects[0].size = aDesc.objects[0].size;
    vaDesc.objects[0].fd = aDesc.objects[0].fd;
    vaDesc.num_layers = 2;
    for (int i = 0; i < 2; i++) {
      vaDesc.layers[i].num_planes = 1;
      vaDesc.layers[i].object_index[0] = 0;
      vaDesc.layers[i].drm_format = aDesc.layers[i].format;
      vaDesc.layers[i].pitch[0] = aDesc.layers[i].planes[0].pitch;
      vaDesc.layers[i].offset[0] = aDesc.layers[i].planes[0].offset;
    }
  } else if (aDesc.nb_layers == 2 && aDesc.layers[0].format == DRM_FORMAT_R16 &&
             aDesc.layers[1].format == DRM_FORMAT_GR1616 &&
             aDesc.nb_objects == 1) {
    vaDesc.fourcc = VA_FOURCC_P010;
    vaDesc.num_objects = 1;
    vaDesc.objects[0].drm_format_modifier = aDesc.objects[0].format_modifier;
    vaDesc.objects[0].size = aDesc.objects[0].size;
    vaDesc.objects[0].fd = aDesc.objects[0].fd;
    vaDesc.num_layers = 2;
    for (int i = 0; i < 2; i++) {
      vaDesc.layers[i].num_planes = 1;
      vaDesc.layers[i].object_index[0] = 0;
      vaDesc.layers[i].drm_format = aDesc.layers[i].format;
      vaDesc.layers[i].pitch[0] = aDesc.layers[i].planes[0].pitch;
      vaDesc.layers[i].offset[0] = aDesc.layers[i].planes[0].offset;
    }
  } else {
    DMABUF_LOG("Don't know how to deal with FOURCC 0x{:x}",
               aDesc.layers[0].format);
    return Nothing();
  }

  return Some(vaDesc);
}

RefPtr<VideoFrameSurface<LIBAV_VER>>
VideoFramePool<LIBAV_VER>::GetVideoFrameSurface(AVDRMFrameDescriptor& aDesc,
                                                int aWidth, int aHeight,
                                                AVCodecContext* aAVCodecContext,
                                                AVFrame* aAVFrame,
                                                const FFmpegLibWrapper* aLib) {
  MOZ_ASSERT(aDesc.nb_layers > 0);

  auto layerDesc = FFmpegDescToVA(aDesc, aAVFrame);
  if (layerDesc.isNothing()) {
    return nullptr;
  }

  // Width and height, after cropping
  int crop_width = (int)layerDesc->width;
  int crop_height = (int)layerDesc->height;

  MutexAutoLock lock(mSurfaceLock);

  RefPtr<VideoFrameSurface<LIBAV_VER>> videoSurface =
      GetTargetVideoFrameSurfaceLocked(lock, sInvalidFFMPEGSurfaceID,
                                       /* aRecycleSurface */ false);
  RefPtr<DMABufSurfaceYUV> surface = videoSurface->GetDMABufSurface();

  if (aAVFrame->hw_frames_ctx) {
    AVHWDeviceType hwdeviceType =
        ((AVHWDeviceContext*)((AVHWFramesContext*)aAVFrame->hw_frames_ctx->data)
             ->device_ref->data)
            ->type;
    DMABUF_LOG("Using {} DMABufSurface UID {}",
               aLib->av_hwdevice_get_type_name(hwdeviceType),
               surface->GetUID());
  }

  bool copySurface = mTextureCopyWorks && ShouldCopySurface();
  if (!surface->UpdateYUVData(layerDesc.value(), crop_width, crop_height,
                              copySurface)) {
    if (!copySurface) {
      // Failed without texture copy. We can't do more here.
      return nullptr;
    }
    // Try again without texture copy
    DMABUF_LOG("  DMABuf texture copy is broken");
    copySurface = mTextureCopyWorks = false;
    if (!surface->UpdateYUVData(layerDesc.value(), crop_width, crop_height,
                                copySurface)) {
      return nullptr;
    }
  }

  if (MOZ_UNLIKELY(!mTextureCreationWorks)) {
    mTextureCreationWorks = Some(surface->VerifyTextureCreation());
    if (!*mTextureCreationWorks) {
      DMABUF_LOG("  failed to create texture over DMABuf memory!");
      return nullptr;
    }
  }

  // Don't recycle v4l surfaces, we don't have FFmpegID and we can't ensure
  // match between FFmpeg frame with DMABufSurface.
  videoSurface->DisableRecycle();

  if (!copySurface) {
    videoSurface->LockVAAPIData(aAVCodecContext, aAVFrame, aLib);
  }

  return videoSurface;
}

}  // namespace mozilla

Messung V0.5 in Prozent
C=88 H=89 G=88

¤ Dauer der Verarbeitung: 0.19 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.