From d3eeb98f0ce298324f844c3678614afc27c3ce7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 16 Nov 2017 11:32:52 +0200 Subject: [PATCH] msdkenc: Add support for YV12, YUY2, UYVY and BGRA By doing conversion with VPP to NV12 before the actual encoding. https://bugzilla.gnome.org/show_bug.cgi?id=789847 --- sys/msdk/gstmsdkenc.c | 315 ++++++++++++++++++++++++++++++++++++------ sys/msdk/gstmsdkenc.h | 6 + sys/msdk/msdk.c | 145 +++++++++++++++---- 3 files changed, 398 insertions(+), 68 deletions(-) diff --git a/sys/msdk/gstmsdkenc.c b/sys/msdk/gstmsdkenc.c index 933eaf950e..d3eeedff6e 100644 --- a/sys/msdk/gstmsdkenc.c +++ b/sys/msdk/gstmsdkenc.c @@ -72,7 +72,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-raw, " - "format = (string) { NV12 }, " + "format = (string) { NV12, I420, YV12, YUY2, UYVY, BGRA }, " "framerate = (fraction) [0, MAX], " "width = (int) [ 16, MAX ], height = (int) [ 16, MAX ]," "interlace-mode = (string) progressive") @@ -140,6 +140,65 @@ gst_msdkenc_add_extra_param (GstMsdkEnc * thiz, mfxExtBuffer * param) } } +static void +gst_msdkenc_alloc_surfaces (GstMsdkEnc * thiz, GstVideoFormat format, + gint width, gint height, guint num_surfaces, mfxFrameSurface1 * surfaces) +{ + gsize Y_size = 0, U_size = 0; + gsize pitch; + gsize size; + gint i; + + width = GST_ROUND_UP_32 (width); + height = GST_ROUND_UP_32 (height); + + switch (format) { + case GST_VIDEO_FORMAT_NV12: + Y_size = width * height; + pitch = width; + size = Y_size + (Y_size >> 1); + break; + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_I420: + Y_size = width * height; + pitch = width; + U_size = (width / 2) * (height / 2); + size = Y_size + 2 * U_size; + break; + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + size = 2 * width * height; + pitch = 2 * width; + break; + case GST_VIDEO_FORMAT_BGRA: + size = 4 * width * height; + pitch = 4 * width; + break; + default: + g_assert_not_reached (); + break; + } + + for (i = 0; i < num_surfaces; i++) { + mfxFrameSurface1 *surface = &surfaces[i]; + mfxU8 *data = _aligned_alloc (32, size); + if (!data) { + GST_ERROR_OBJECT (thiz, "Memory allocation failed"); + return; + } + + surface->Data.MemId = (mfxMemId) data; + surface->Data.Pitch = pitch; + surface->Data.Y = data; + if (U_size) { + surface->Data.U = data + Y_size; + surface->Data.V = data + Y_size + U_size; + } else if (Y_size) { + surface->Data.UV = data + Y_size; + } + } +} + static gboolean gst_msdkenc_init_encoder (GstMsdkEnc * thiz) { @@ -147,7 +206,7 @@ gst_msdkenc_init_encoder (GstMsdkEnc * thiz) GstVideoInfo *info; mfxSession session; mfxStatus status; - mfxFrameAllocRequest request; + mfxFrameAllocRequest request[2]; guint i; if (!thiz->input_state) { @@ -166,6 +225,104 @@ gst_msdkenc_init_encoder (GstMsdkEnc * thiz) } GST_OBJECT_LOCK (thiz); + session = msdk_context_get_session (thiz->context); + + thiz->has_vpp = FALSE; + if (info->finfo->format != GST_VIDEO_FORMAT_NV12) { + thiz->vpp_param.IOPattern = + MFX_IOPATTERN_IN_SYSTEM_MEMORY | MFX_IOPATTERN_OUT_SYSTEM_MEMORY; + + thiz->vpp_param.vpp.In.Width = GST_ROUND_UP_32 (info->width); + thiz->vpp_param.vpp.In.Height = GST_ROUND_UP_32 (info->height); + thiz->vpp_param.vpp.In.CropW = info->width; + thiz->vpp_param.vpp.In.CropH = info->height; + thiz->vpp_param.vpp.In.FrameRateExtN = info->fps_n; + thiz->vpp_param.vpp.In.FrameRateExtD = info->fps_d; + thiz->vpp_param.vpp.In.AspectRatioW = info->par_n; + thiz->vpp_param.vpp.In.AspectRatioH = info->par_d; + thiz->vpp_param.vpp.In.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; + switch (info->finfo->format) { + case GST_VIDEO_FORMAT_NV12: + thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_NV12; + thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV420; + break; + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_I420: + thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_YV12; + thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV420; + break; + case GST_VIDEO_FORMAT_YUY2: + thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_YUY2; + thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV422; + break; + case GST_VIDEO_FORMAT_UYVY: + thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_UYVY; + thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV422; + break; + case GST_VIDEO_FORMAT_BGRA: + thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_RGB4; + thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV444; + break; + default: + g_assert_not_reached (); + break; + } + + thiz->vpp_param.vpp.Out = thiz->vpp_param.vpp.In; + thiz->vpp_param.vpp.Out.FourCC = MFX_FOURCC_NV12; + thiz->vpp_param.vpp.Out.ChromaFormat = MFX_CHROMAFORMAT_YUV420; + + /* validate parameters and allow the Media SDK to make adjustments */ + status = MFXVideoVPP_Query (session, &thiz->vpp_param, &thiz->vpp_param); + if (status < MFX_ERR_NONE) { + GST_ERROR_OBJECT (thiz, "Video VPP Query failed (%s)", + msdk_status_to_string (status)); + goto no_vpp; + } else if (status > MFX_ERR_NONE) { + GST_WARNING_OBJECT (thiz, "Video VPP Query returned: %s", + msdk_status_to_string (status)); + } + + status = MFXVideoVPP_QueryIOSurf (session, &thiz->vpp_param, request); + if (status < MFX_ERR_NONE) { + GST_ERROR_OBJECT (thiz, "VPP Query IO surfaces failed (%s)", + msdk_status_to_string (status)); + goto no_vpp; + } else if (status > MFX_ERR_NONE) { + GST_WARNING_OBJECT (thiz, "VPP Query IO surfaces returned: %s", + msdk_status_to_string (status)); + } + + thiz->num_vpp_surfaces = request[0].NumFrameSuggested; + thiz->vpp_surfaces = g_new0 (mfxFrameSurface1, thiz->num_vpp_surfaces); + for (i = 0; i < thiz->num_vpp_surfaces; i++) { + memcpy (&thiz->vpp_surfaces[i].Info, &thiz->vpp_param.vpp.In, + sizeof (mfxFrameInfo)); + } + + status = MFXVideoVPP_Init (session, &thiz->vpp_param); + if (status < MFX_ERR_NONE) { + GST_ERROR_OBJECT (thiz, "Init failed (%s)", + msdk_status_to_string (status)); + goto no_vpp; + } else if (status > MFX_ERR_NONE) { + GST_WARNING_OBJECT (thiz, "Init returned: %s", + msdk_status_to_string (status)); + } + + status = MFXVideoVPP_GetVideoParam (session, &thiz->vpp_param); + if (status < MFX_ERR_NONE) { + GST_ERROR_OBJECT (thiz, "Get VPP Parameters failed (%s)", + msdk_status_to_string (status)); + MFXVideoVPP_Close (session); + goto no_vpp; + } else if (status > MFX_ERR_NONE) { + GST_WARNING_OBJECT (thiz, "Get VPP Parameters returned: %s", + msdk_status_to_string (status)); + } + + thiz->has_vpp = TRUE; + } thiz->param.AsyncDepth = thiz->async_depth; thiz->param.IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY; @@ -196,7 +353,6 @@ gst_msdkenc_init_encoder (GstMsdkEnc * thiz) thiz->param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; thiz->param.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12; thiz->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420; - thiz->param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; /* allow subclass configure further */ if (klass->configure) { @@ -209,7 +365,6 @@ gst_msdkenc_init_encoder (GstMsdkEnc * thiz) thiz->param.ExtParam = thiz->extra_params; } - session = msdk_context_get_session (thiz->context); /* validate parameters and allow the Media SDK to make adjustments */ status = MFXVideoENCODE_Query (session, &thiz->param, &thiz->param); if (status < MFX_ERR_NONE) { @@ -221,54 +376,51 @@ gst_msdkenc_init_encoder (GstMsdkEnc * thiz) msdk_status_to_string (status)); } - status = MFXVideoENCODE_QueryIOSurf (session, &thiz->param, &request); + status = MFXVideoENCODE_QueryIOSurf (session, &thiz->param, request); if (status < MFX_ERR_NONE) { - GST_ERROR_OBJECT (thiz, "Query IO surfaces failed (%s)", + GST_ERROR_OBJECT (thiz, "Encode Query IO surfaces failed (%s)", msdk_status_to_string (status)); goto failed; } else if (status > MFX_ERR_NONE) { - GST_WARNING_OBJECT (thiz, "Query IO surfaces returned: %s", + GST_WARNING_OBJECT (thiz, "Encode Query IO surfaces returned: %s", msdk_status_to_string (status)); } - if (request.NumFrameSuggested < thiz->param.AsyncDepth) { + /* Maximum of VPP output and encoder input, if using VPP */ + if (thiz->has_vpp) + request[0].NumFrameSuggested = + MAX (request[0].NumFrameSuggested, request[1].NumFrameSuggested); + if (request[0].NumFrameSuggested < thiz->param.AsyncDepth) { GST_ERROR_OBJECT (thiz, "Required %d surfaces (%d suggested), async %d", - request.NumFrameMin, request.NumFrameSuggested, thiz->param.AsyncDepth); + request[0].NumFrameMin, request[0].NumFrameSuggested, + thiz->param.AsyncDepth); goto failed; } - thiz->num_surfaces = request.NumFrameSuggested; + /* These are VPP output (if any) and encoder input */ + thiz->num_surfaces = request[0].NumFrameSuggested; thiz->surfaces = g_new0 (mfxFrameSurface1, thiz->num_surfaces); for (i = 0; i < thiz->num_surfaces; i++) { memcpy (&thiz->surfaces[i].Info, &thiz->param.mfx.FrameInfo, sizeof (mfxFrameInfo)); } - if (GST_ROUND_UP_32 (info->width) != info->width - || GST_ROUND_UP_32 (info->height) != info->height) { - guint width = GST_ROUND_UP_32 (info->width); - guint height = GST_ROUND_UP_32 (info->height); - gsize Y_size = width * height; - gsize size = Y_size + (Y_size >> 1); - for (i = 0; i < thiz->num_surfaces; i++) { - mfxFrameSurface1 *surface = &thiz->surfaces[i]; - mfxU8 *data = _aligned_alloc (32, size); - if (!data) { - GST_ERROR_OBJECT (thiz, "Memory allocation failed"); - goto failed; - } - - surface->Data.MemId = (mfxMemId) data; - surface->Data.Pitch = width; - surface->Data.Y = data; - surface->Data.UV = data + Y_size; - } + if ((GST_ROUND_UP_32 (info->width) != info->width + || GST_ROUND_UP_32 (info->height) != info->height)) { + gst_msdkenc_alloc_surfaces (thiz, info->finfo->format, info->width, + info->height, + thiz->has_vpp ? thiz->num_vpp_surfaces : thiz->num_surfaces, + thiz->has_vpp ? thiz->vpp_surfaces : thiz->surfaces); GST_DEBUG_OBJECT (thiz, "Allocated aligned memory, pixel data will be copied"); } + if (thiz->has_vpp) { + gst_msdkenc_alloc_surfaces (thiz, GST_VIDEO_FORMAT_NV12, info->width, + info->height, thiz->num_surfaces, thiz->surfaces); + } GST_DEBUG_OBJECT (thiz, "Required %d surfaces (%d suggested), allocated %d", - request.NumFrameMin, request.NumFrameSuggested, thiz->num_surfaces); + request[0].NumFrameMin, request[0].NumFrameSuggested, thiz->num_surfaces); status = MFXVideoENCODE_Init (session, &thiz->param); if (status < MFX_ERR_NONE) { @@ -309,6 +461,7 @@ gst_msdkenc_init_encoder (GstMsdkEnc * thiz) return TRUE; +no_vpp: failed: GST_OBJECT_UNLOCK (thiz); msdk_close_context (thiz->context); @@ -344,6 +497,16 @@ gst_msdkenc_close_encoder (GstMsdkEnc * thiz) g_free (thiz->tasks); thiz->tasks = NULL; + /* Close VPP before freeing the surfaces. They are shared between encoder + * and VPP */ + if (thiz->has_vpp) { + status = MFXVideoVPP_Close (msdk_context_get_session (thiz->context)); + if (status != MFX_ERR_NONE && status != MFX_ERR_NOT_INITIALIZED) { + GST_WARNING_OBJECT (thiz, "VPP close failed (%s)", + msdk_status_to_string (status)); + } + } + for (i = 0; i < thiz->num_surfaces; i++) { mfxFrameSurface1 *surface = &thiz->surfaces[i]; if (surface->Data.MemId) @@ -352,6 +515,16 @@ gst_msdkenc_close_encoder (GstMsdkEnc * thiz) g_free (thiz->surfaces); thiz->surfaces = NULL; + if (thiz->has_vpp) { + for (i = 0; i < thiz->num_vpp_surfaces; i++) { + mfxFrameSurface1 *surface = &thiz->vpp_surfaces[i]; + if (surface->Data.MemId) + _aligned_free (surface->Data.MemId); + } + g_free (thiz->vpp_surfaces); + thiz->vpp_surfaces = NULL; + } + msdk_close_context (thiz->context); thiz->context = NULL; memset (&thiz->param, 0, sizeof (thiz->param)); @@ -394,7 +567,8 @@ gst_msdkenc_dequeue_frame (GstMsdkEnc * thiz, GstVideoCodecFrame * frame) if (fdata->frame != frame) continue; - gst_video_frame_unmap (&fdata->vframe); + if (fdata->vframe.buffer) + gst_video_frame_unmap (&fdata->vframe); gst_video_codec_frame_unref (fdata->frame); g_slice_free (FrameData, fdata); @@ -519,7 +693,7 @@ gst_msdkenc_encode_frame (GstMsdkEnc * thiz, mfxFrameSurface1 * surface, if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA) { GST_ELEMENT_ERROR (thiz, STREAM, ENCODE, ("Encode frame failed."), - ("MSDK encode return code=%d", status)); + ("MSDK encode error (%s)", msdk_status_to_string (status))); gst_msdkenc_dequeue_frame (thiz, input_frame); gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), input_frame); return GST_FLOW_ERROR; @@ -654,20 +828,75 @@ gst_msdkenc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame) if (G_UNLIKELY (thiz->context == NULL)) goto not_inited; - surface = msdk_get_free_surface (thiz->surfaces, thiz->num_surfaces); - if (!surface) - goto invalid_surface; + if (thiz->has_vpp) { + mfxFrameSurface1 *vpp_surface; + GstVideoFrame vframe; + mfxSession session; + mfxSyncPoint vpp_sync_point = NULL; + mfxStatus status; - fdata = gst_msdkenc_queue_frame (thiz, frame, info); - if (!fdata) - goto invalid_frame; + vpp_surface = + msdk_get_free_surface (thiz->vpp_surfaces, thiz->num_vpp_surfaces); + if (!vpp_surface) + goto invalid_surface; + surface = msdk_get_free_surface (thiz->surfaces, thiz->num_surfaces); + if (!surface) + goto invalid_surface; - msdk_frame_to_surface (&fdata->vframe, surface); - if (frame->pts != GST_CLOCK_TIME_NONE) { - surface->Data.TimeStamp = - gst_util_uint64_scale (frame->pts, 90000, GST_SECOND); + if (!gst_video_frame_map (&vframe, info, frame->input_buffer, GST_MAP_READ)) + goto invalid_frame; + + msdk_frame_to_surface (&vframe, vpp_surface); + if (frame->pts != GST_CLOCK_TIME_NONE) { + vpp_surface->Data.TimeStamp = + gst_util_uint64_scale (frame->pts, 90000, GST_SECOND); + surface->Data.TimeStamp = + gst_util_uint64_scale (frame->pts, 90000, GST_SECOND); + } else { + vpp_surface->Data.TimeStamp = MFX_TIMESTAMP_UNKNOWN; + surface->Data.TimeStamp = MFX_TIMESTAMP_UNKNOWN; + } + + session = msdk_context_get_session (thiz->context); + for (;;) { + status = + MFXVideoVPP_RunFrameVPPAsync (session, vpp_surface, surface, NULL, + &vpp_sync_point); + if (status != MFX_WRN_DEVICE_BUSY) + break; + /* If device is busy, wait 1ms and retry, as per MSDK's recomendation */ + g_usleep (1000); + }; + + gst_video_frame_unmap (&vframe); + + if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA) { + GST_ELEMENT_ERROR (thiz, STREAM, ENCODE, ("Converting frame failed."), + ("MSDK VPP error (%s)", msdk_status_to_string (status))); + gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), frame); + return GST_FLOW_ERROR; + } + + fdata = g_slice_new0 (FrameData); + fdata->frame = gst_video_codec_frame_ref (frame); + + thiz->pending_frames = g_list_prepend (thiz->pending_frames, fdata); } else { - surface->Data.TimeStamp = MFX_TIMESTAMP_UNKNOWN; + surface = msdk_get_free_surface (thiz->surfaces, thiz->num_surfaces); + if (!surface) + goto invalid_surface; + + fdata = gst_msdkenc_queue_frame (thiz, frame, info); + if (!fdata) + goto invalid_frame; + + msdk_frame_to_surface (&fdata->vframe, surface); + if (frame->pts != GST_CLOCK_TIME_NONE) { + surface->Data.TimeStamp = + gst_util_uint64_scale (frame->pts, 90000, GST_SECOND); + } else { + surface->Data.TimeStamp = MFX_TIMESTAMP_UNKNOWN; + } } return gst_msdkenc_encode_frame (thiz, surface, frame); diff --git a/sys/msdk/gstmsdkenc.h b/sys/msdk/gstmsdkenc.h index 1e316ec535..f3c99da9f2 100644 --- a/sys/msdk/gstmsdkenc.h +++ b/sys/msdk/gstmsdkenc.h @@ -77,6 +77,12 @@ struct _GstMsdkEnc MsdkEncTask *tasks; guint next_task; + gboolean has_vpp; + mfxVideoParam vpp_param; + guint num_vpp_surfaces; + /* Input interfaces, output above */ + mfxFrameSurface1 *vpp_surfaces; + mfxExtBuffer *extra_params[MAX_EXTRA_PARAMS]; guint num_extra_params; diff --git a/sys/msdk/msdk.c b/sys/msdk/msdk.c index ae2e129491..77f966c3eb 100644 --- a/sys/msdk/msdk.c +++ b/sys/msdk/msdk.c @@ -77,39 +77,134 @@ msdk_frame_to_surface (GstVideoFrame * frame, mfxFrameSurface1 * surface) guint8 *src, *dst; guint sstride, dstride; guint width, height; - guint i; + guint i, p; if (!surface->Data.MemId) { - surface->Data.Y = GST_VIDEO_FRAME_COMP_DATA (frame, 0); - surface->Data.UV = GST_VIDEO_FRAME_COMP_DATA (frame, 1); - surface->Data.Pitch = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); + switch (frame->info.finfo->format) { + case GST_VIDEO_FORMAT_NV12: + surface->Data.Y = GST_VIDEO_FRAME_COMP_DATA (frame, 0); + surface->Data.UV = GST_VIDEO_FRAME_COMP_DATA (frame, 1); + surface->Data.Pitch = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); + break; + + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_I420: + surface->Data.Y = GST_VIDEO_FRAME_COMP_DATA (frame, 0); + surface->Data.U = GST_VIDEO_FRAME_COMP_DATA (frame, 1); + surface->Data.V = GST_VIDEO_FRAME_COMP_DATA (frame, 2); + surface->Data.Pitch = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); + break; + + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + surface->Data.Y = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); + surface->Data.Pitch = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); + break; + + case GST_VIDEO_FORMAT_BGRA: + surface->Data.R = GST_VIDEO_FRAME_COMP_DATA (frame, 0); + surface->Data.Pitch = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); + break; + + default: + g_assert_not_reached (); + break; + } + return; } - /* Y Plane */ - width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); - height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); - src = GST_VIDEO_FRAME_COMP_DATA (frame, 0); - sstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); - dst = surface->Data.Y; - dstride = surface->Data.Pitch; - for (i = 0; i < height; i++) { - memcpy (dst, src, width); - src += sstride; - dst += dstride; - } + switch (frame->info.finfo->format) { + case GST_VIDEO_FORMAT_NV12: + width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); + for (p = 0; p < 2; p++) { + height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, p); + src = GST_VIDEO_FRAME_COMP_DATA (frame, p); + sstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, p); + dst = p == 0 ? surface->Data.Y : surface->Data.UV; + dstride = surface->Data.Pitch; - /* UV Plane */ - height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); - src = GST_VIDEO_FRAME_COMP_DATA (frame, 1); - sstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); - dst = surface->Data.UV; + for (i = 0; i < height; i++) { + memcpy (dst, src, width); + src += sstride; + dst += dstride; + } + } + break; - for (i = 0; i < height; i++) { - memcpy (dst, src, width); - src += sstride; - dst += dstride; + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_I420: + for (p = 0; p < 3; p++) { + width = GST_VIDEO_FRAME_COMP_WIDTH (frame, p); + height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, p); + src = GST_VIDEO_FRAME_COMP_DATA (frame, p); + sstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, p); + switch (p) { + case 0: + dst = surface->Data.Y; + break; + case 1: + dst = surface->Data.U; + break; + case 2: + dst = surface->Data.V; + break; + default: + g_assert_not_reached (); + break; + } + dstride = surface->Data.Pitch; + if (p > 0) + dstride = dstride / 2; + + for (i = 0; i < height; i++) { + memcpy (dst, src, width); + src += sstride; + dst += dstride; + } + } + break; + + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); + height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); + src = GST_VIDEO_FRAME_COMP_DATA (frame, 0); + sstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); + dst = surface->Data.Y; + dstride = surface->Data.Pitch; + + width *= 2; + width = MIN (sstride, width); + + for (i = 0; i < height; i++) { + memcpy (dst, src, width); + src += sstride; + dst += dstride; + } + break; + + case GST_VIDEO_FORMAT_BGRA: + width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); + height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); + src = GST_VIDEO_FRAME_COMP_DATA (frame, 0); + sstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); + dst = surface->Data.R; + dstride = surface->Data.Pitch; + + width *= 4; + + for (i = 0; i < height; i++) { + memcpy (dst, src, width); + src += sstride; + dst += dstride; + } + break; + + default: + g_assert_not_reached (); + break; } }