#include "livekit/dmabuf_video_frame_buffer.h"
#include <cstdio>
#include <cstring>
#include <mutex>
#include <unordered_map>
#include "api/make_ref_counted.h"
#include "rtc_base/logging.h"
#ifdef USE_JETSON_VIDEO_CODEC
#include "NvBufSurface.h"
#endif
#include "third_party/libyuv/include/libyuv/convert.h"
namespace livekit {
DmaBufVideoFrameBuffer::DmaBufVideoFrameBuffer(int dmabuf_fd,
int width,
int height,
DmaBufPixelFormat pixel_format)
: dmabuf_fd_(dmabuf_fd),
width_(width),
height_(height),
pixel_format_(pixel_format) {}
webrtc::VideoFrameBuffer::Type DmaBufVideoFrameBuffer::type() const {
return Type::kNative;
}
int DmaBufVideoFrameBuffer::width() const {
return width_;
}
int DmaBufVideoFrameBuffer::height() const {
return height_;
}
webrtc::scoped_refptr<webrtc::I420BufferInterface>
DmaBufVideoFrameBuffer::ToI420() {
#ifdef USE_JETSON_VIDEO_CODEC
static std::mutex cache_mutex;
static std::unordered_map<int, NvBufSurface*> surface_cache;
NvBufSurface* surface = nullptr;
{
std::lock_guard<std::mutex> lock(cache_mutex);
auto cache_it = surface_cache.find(dmabuf_fd_);
if (cache_it != surface_cache.end()) {
surface = cache_it->second;
} else {
int ret = NvBufSurfaceFromFd(dmabuf_fd_, reinterpret_cast<void**>(&surface));
if (ret != 0 || !surface || surface->batchSize < 1) {
RTC_LOG(LS_ERROR) << "DmaBufVideoFrameBuffer::ToI420: "
"NvBufSurfaceFromFd failed (fd=" << dmabuf_fd_
<< ", ret=" << ret << ")";
return nullptr;
}
surface_cache[dmabuf_fd_] = surface;
}
}
int ret = NvBufSurfaceMap(surface, 0, -1, NVBUF_MAP_READ);
if (ret != 0) {
RTC_LOG(LS_ERROR) << "DmaBufVideoFrameBuffer::ToI420: "
"NvBufSurfaceMap failed (ret=" << ret << ")";
return nullptr;
}
NvBufSurfaceSyncForCpu(surface, 0, -1);
const NvBufSurfaceParams& params = surface->surfaceList[0];
webrtc::scoped_refptr<webrtc::I420Buffer> i420 =
webrtc::I420Buffer::Create(width_, height_);
if (pixel_format_ == DmaBufPixelFormat::kNV12) {
const uint8_t* src_y =
static_cast<const uint8_t*>(params.mappedAddr.addr[0]);
const uint8_t* src_uv =
static_cast<const uint8_t*>(params.mappedAddr.addr[1]);
int src_stride_y = static_cast<int>(params.planeParams.pitch[0]);
int src_stride_uv = static_cast<int>(params.planeParams.pitch[1]);
libyuv::NV12ToI420(src_y, src_stride_y,
src_uv, src_stride_uv,
i420->MutableDataY(), i420->StrideY(),
i420->MutableDataU(), i420->StrideU(),
i420->MutableDataV(), i420->StrideV(),
width_, height_);
} else {
const uint8_t* src_y =
static_cast<const uint8_t*>(params.mappedAddr.addr[0]);
const uint8_t* src_u =
static_cast<const uint8_t*>(params.mappedAddr.addr[1]);
const uint8_t* src_v =
static_cast<const uint8_t*>(params.mappedAddr.addr[2]);
int src_stride_y = static_cast<int>(params.planeParams.pitch[0]);
int src_stride_u = static_cast<int>(params.planeParams.pitch[1]);
int src_stride_v = static_cast<int>(params.planeParams.pitch[2]);
libyuv::I420Copy(src_y, src_stride_y,
src_u, src_stride_u,
src_v, src_stride_v,
i420->MutableDataY(), i420->StrideY(),
i420->MutableDataU(), i420->StrideU(),
i420->MutableDataV(), i420->StrideV(),
width_, height_);
}
NvBufSurfaceUnMap(surface, 0, -1);
return i420;
#else
RTC_LOG(LS_ERROR) << "DmaBufVideoFrameBuffer::ToI420: "
"not supported without Jetson MMAPI";
return nullptr;
#endif
}
webrtc::scoped_refptr<webrtc::VideoFrameBuffer>
DmaBufVideoFrameBuffer::CropAndScale(int offset_x,
int offset_y,
int crop_width,
int crop_height,
int scaled_width,
int scaled_height) {
return webrtc::scoped_refptr<webrtc::VideoFrameBuffer>(this);
}
DmaBufVideoFrameBuffer* DmaBufVideoFrameBuffer::FromNative(
webrtc::VideoFrameBuffer* buffer) {
if (!buffer || buffer->type() != webrtc::VideoFrameBuffer::Type::kNative) {
return nullptr;
}
return dynamic_cast<DmaBufVideoFrameBuffer*>(buffer);
}
}