From e1879aa0bca46bb630e738208521a978eceeed72 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Sat, 27 Jun 2015 02:15:46 +0800 Subject: [PATCH] a convenience func to make host video frame from gpu --- src/QtAV/VideoFrame.h | 23 ++++++++++++++- src/VideoFrame.cpp | 55 ++++++++++++++++++++++++++++++++++++ src/utils/CopyFrame_SSE2.cpp | 1 + 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/QtAV/VideoFrame.h b/src/QtAV/VideoFrame.h index bfbf1b5d..37d2f1e7 100644 --- a/src/QtAV/VideoFrame.h +++ b/src/QtAV/VideoFrame.h @@ -35,6 +35,20 @@ class Q_AV_EXPORT VideoFrame : public Frame { Q_DECLARE_PRIVATE(VideoFrame) public: + /*! + * \brief fromGPU + * Make a VideoFrame with data on host memory from GPU resource + * \param fmt video format of GPU resource + * \param width frame width + * \param height frame height + * \param surface_h surface height. Can be greater than visual frame height because of alignment + * \param src CPU accessible address of frame planes on GPU. src[0] must be valid. src[i>0] will be filled depending on pixel format, pitch and surface_h if it's NULL. + * \param pitch plane pitch on GPU. pitch[0] must be valid. pitch[i>0] will be filled depending on pixel format, pitch[0] and surface_h if it's NULL. + * \param optimized try to use SIMD to copy from GPU. otherwise use memcpy + * \param swapUV + */ + static VideoFrame fromGPU(const VideoFormat& fmt, int width, int height, int surface_h, quint8 *src[], int pitch[], bool optimized = true, bool swapUV = false); + VideoFrame(); //must set planes and linesize manually VideoFrame(int width, int height, const VideoFormat& format); @@ -90,6 +104,13 @@ public: * \param roi NOT implemented! */ QImage toImage(QImage::Format fmt = QImage::Format_ARGB32, const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const; + /*! + * \brief to + * The result frame data is always on host memory. + * \param pixfmt target pixel format + * \param dstSize target frame size + * \param roi interested region of source frame + */ VideoFrame to(VideoFormat::PixelFormat pixfmt, const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const; VideoFrame to(const VideoFormat& fmt, const QSize& dstSize = QSize(), const QRectF& roi = QRect()) const; /*! @@ -129,7 +150,7 @@ public: void setEq(int brightness, int contrast, int saturation); /*! * \brief convert - * return a frame with a given format from a given source frame + * return a frame with a given format from a given source frame. The result frame data is always on host memory. */ VideoFrame convert(const VideoFrame& frame, const VideoFormat& fmt) const; VideoFrame convert(const VideoFrame& frame, VideoFormat::PixelFormat fmt) const; diff --git a/src/VideoFrame.cpp b/src/VideoFrame.cpp index 7116916b..82f9a0e8 100644 --- a/src/VideoFrame.cpp +++ b/src/VideoFrame.cpp @@ -26,6 +26,7 @@ #include #include #include "QtAV/private/AVCompat.h" +#include "utils/GPUMemCopy.h" #include "utils/Logger.h" // FF_API_PIX_FMT @@ -44,6 +45,60 @@ static const struct RegisterMetaTypes } _registerMetaTypes; } +VideoFrame VideoFrame::fromGPU(const VideoFormat& fmt, int width, int height, int surface_h, quint8 *src[], int pitch[], bool optimized, bool swapUV) +{ + Q_ASSERT(src[0] && pitch[0] > 0 && "VideoFrame::fromGPU: src[0] and pitch[0] must be set"); + const int nb_planes = fmt.planeCount(); + const int chroma_pitch = nb_planes > 1 ? fmt.bytesPerLine(pitch[0], 1) : 0; + const int chroma_h = fmt.chromaHeight(surface_h); + int h[] = { surface_h, 0, 0}; + for (int i = 1; i < nb_planes; ++i) { + h[i] = chroma_h; + // set chroma address and pitch if not set + if (pitch[i] <= 0) + pitch[i] = chroma_pitch; + if (!src[i]) + src[i] = src[i-1] + pitch[i-1]*h[i-1]; + } + if (swapUV) { + std::swap(src[1], src[2]); + std::swap(pitch[1], pitch[2]); + } + VideoFrame frame; + if (optimized) { + qDebug("simd"); + int yuv_size = 0; + for (int i = 0; i < nb_planes; ++i) { + yuv_size += pitch[i]*h[i]; + } + // additional 15 bytes to ensure 16 bytes aligned + QByteArray buf(15 + yuv_size, 0); + const int offset_16 = (16 - ((uintptr_t)buf.data() & 0x0f)) & 0x0f; + // plane 1, 2... is aligned? + uchar* plane_ptr = (uchar*)buf.data() + offset_16; + QVector dst(nb_planes, 0); + for (int i = 0; i < nb_planes; ++i) { + dst[i] = plane_ptr; + // TODO: add VideoFormat::planeWidth/Height() ? + // pitch instead of surface_width + plane_ptr += pitch[i] * h[i]; + gpu_memcpy(dst[i], src[i], pitch[i]*h[i]); + + } + frame = VideoFrame(buf, width, height, fmt); + frame.setBits(dst); + frame.setBytesPerLine(pitch); + } else { + frame = VideoFrame(width, height, fmt); + frame.setBits(src); + frame.setBytesPerLine(pitch); + // TODO: why clone is faster()? + // TODO: buffer pool and create VideoFrame when needed to avoid copy? also for other va + frame = frame.clone(); + } + return frame; +} + class VideoFramePrivate : public FramePrivate { Q_DISABLE_COPY(VideoFramePrivate) diff --git a/src/utils/CopyFrame_SSE2.cpp b/src/utils/CopyFrame_SSE2.cpp index 294379fa..fe00a10f 100644 --- a/src/utils/CopyFrame_SSE2.cpp +++ b/src/utils/CopyFrame_SSE2.cpp @@ -19,6 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include //intptr_t +#include #include