gstreamer/subprojects/gst-plugins-bad/sys/wic/gstwicpngdec.cpp
Seungha Yang f8016687a4 wic: Add support for JPEG and PNG decoding
Adding Windows Imaging Component (WIC) plugin with JPEG/PNG decoding
support.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1605>
2022-03-03 00:00:54 +00:00

316 lines
9.5 KiB
C++

/* GStreamer
* Copyright (C) 2022 Seungha Yang <seungha@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-wicpngdec
* @title: wicpngdec
*
* This element decodes PNG compressed data into RAW video data.
*
* Since: 1.22
*
*/
#include "gstwicpngdec.h"
#include <wrl.h>
#include <string.h>
/* *INDENT-OFF* */
using namespace Microsoft::WRL;
/* *INDENT-ON* */
GST_DEBUG_CATEGORY_STATIC (gst_wic_png_dec_debug);
#define GST_CAT_DEFAULT gst_wic_png_dec_debug
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("image/png")
);
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE
("{ RGBA64_LE, BGRA, RGBA, BGR, RGB, GRAY8, GRAY16_BE }"))
);
struct _GstWicPngDec
{
GstWicDecoder parent;
WICBitmapPlaneDescription plane_desc[GST_VIDEO_MAX_PLANES];
GstVideoInfo info;
};
static gboolean gst_wic_png_dec_set_format (GstWicDecoder * decoder,
GstVideoCodecState * state);
static GstFlowReturn gst_wic_png_dec_process_output (GstWicDecoder * decoder,
IWICImagingFactory * factory, IWICBitmapFrameDecode * decode_frame,
GstVideoCodecFrame * frame);
#define gst_wic_decoder_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstWicPngDec, gst_wic_png_dec, GST_TYPE_WIC_DECODER,
GST_DEBUG_CATEGORY_INIT (gst_wic_png_dec_debug,
"wicpngdec", 0, "wicpngdec"));
static void
gst_wic_png_dec_class_init (GstWicPngDecClass * klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstWicDecoderClass *decoder_class = GST_WIC_DECODER_CLASS (klass);
gst_element_class_add_static_pad_template (element_class, &sink_template);
gst_element_class_add_static_pad_template (element_class, &src_template);
gst_element_class_set_static_metadata (element_class,
"Windows Imaging Component PNG decoder", "Codec/Decoder/Image",
"Png image decoder using Windows Imaging Component API",
"Seungha Yang <seungha@centricular.com>");
decoder_class->codec_id = GUID_ContainerFormatPng;
decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_wic_png_dec_set_format);
decoder_class->process_output =
GST_DEBUG_FUNCPTR (gst_wic_png_dec_process_output);
}
static void
gst_wic_png_dec_init (GstWicPngDec * self)
{
gst_video_info_init (&self->info);
gst_video_decoder_set_packetized (GST_VIDEO_DECODER (self), TRUE);
}
static gboolean
gst_wic_png_dec_prepare_output (GstWicPngDec * self,
IWICImagingFactory * factory, IWICBitmapSource * input,
guint out_width, guint out_height, IWICBitmapSource ** source)
{
WICPixelFormatGUID native_pixel_format;
GstVideoFormat native_format = GST_VIDEO_FORMAT_UNKNOWN;
HRESULT hr;
ComPtr < IWICBitmapSource > output;
hr = input->GetPixelFormat (&native_pixel_format);
if (FAILED (hr)) {
GST_ERROR_OBJECT (self, "Failed to query pixel format, hr: 0x%x",
(guint) hr);
return FALSE;
}
if (!gst_wic_pixel_format_to_gst (native_pixel_format, &native_format)) {
ComPtr < IWICFormatConverter > conv;
GST_LOG_OBJECT (self,
"Native format is not supported for output, needs conversion");
native_format = GST_VIDEO_FORMAT_BGRA;
if (IsEqualGUID (native_pixel_format, GUID_WICPixelFormat1bppIndexed)
|| IsEqualGUID (native_pixel_format, GUID_WICPixelFormat2bppIndexed)
|| IsEqualGUID (native_pixel_format, GUID_WICPixelFormat4bppIndexed)
|| IsEqualGUID (native_pixel_format, GUID_WICPixelFormat8bppIndexed)) {
/* palette, convert to BGRA */
native_format = GST_VIDEO_FORMAT_BGRA;
} else if (IsEqualGUID (native_pixel_format, GUID_WICPixelFormatBlackWhite)
|| IsEqualGUID (native_pixel_format, GUID_WICPixelFormat2bppGray)
|| IsEqualGUID (native_pixel_format, GUID_WICPixelFormat4bppGray)) {
/* gray scale */
native_format = GST_VIDEO_FORMAT_GRAY8;
} else if (IsEqualGUID (native_pixel_format, GUID_WICPixelFormat48bppRGB)) {
/* 16bits per channel RGB, do we have defined format? */
native_format = GST_VIDEO_FORMAT_RGBA64_LE;
}
if (!gst_wic_pixel_format_from_gst (native_format, &native_pixel_format)) {
GST_ERROR_OBJECT (self, "Failed to convert format to WIC");
return FALSE;
}
hr = factory->CreateFormatConverter (&conv);
if (FAILED (hr)) {
GST_ERROR_OBJECT (self, "Failed to create converter");
return FALSE;
}
hr = conv->Initialize (input, native_pixel_format,
WICBitmapDitherTypeNone, nullptr, 0.0f, WICBitmapPaletteTypeCustom);
if (FAILED (hr)) {
GST_ERROR_OBJECT (self, "Failed to initialize converter, hr: 0x%x",
(guint) hr);
return FALSE;
}
conv.As (&output);
} else {
output = input;
}
gst_video_info_set_format (&self->info, native_format, out_width, out_height);
*source = output.Detach ();
return TRUE;
}
static gboolean
gst_wic_png_dec_fill_output (GstWicPngDec * self,
IWICImagingFactory * factory, IWICBitmapSource * source, GstBuffer * buffer)
{
ComPtr < IWICBitmap > bitmap;
ComPtr < IWICBitmapLock > bitmap_lock;
WICBitmapPlane plane;
HRESULT hr;
GstVideoFrame frame;
guint8 *src, *dst;
guint src_stride, dst_stride;
guint height, width_in_bytes;
hr = factory->CreateBitmapFromSource (source, WICBitmapCacheOnDemand,
&bitmap);
if (FAILED (hr)) {
GST_ERROR_OBJECT (self, "Failed to create bitmap from source, hr: 0x%x",
(guint) hr);
return FALSE;
}
hr = gst_wic_lock_bitmap (bitmap.Get (), nullptr,
WICBitmapLockRead, &bitmap_lock, &plane);
if (FAILED (hr)) {
GST_ERROR_OBJECT (self, "Failed to lock bitmap");
return FALSE;
}
if (!gst_video_frame_map (&frame, &self->info, buffer, GST_MAP_WRITE)) {
GST_ERROR_OBJECT (self, "Failed to map output buffer");
return FALSE;
}
src = plane.pbBuffer;
dst = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
src_stride = plane.cbStride;
dst_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame, 0);
width_in_bytes =
GST_VIDEO_FRAME_COMP_WIDTH (&frame, 0) *
GST_VIDEO_FRAME_COMP_PSTRIDE (&frame, 0);
height = GST_VIDEO_FRAME_COMP_HEIGHT (&frame, 0);
for (guint i = 0; i < height; i++) {
memcpy (dst, src, width_in_bytes);
src += src_stride;
dst += dst_stride;
}
gst_video_frame_unmap (&frame);
return TRUE;
}
static void
gst_wic_png_dec_update_output_state (GstWicPngDec * self)
{
GstWicDecoder *wic = GST_WIC_DECODER (self);
GstVideoDecoder *vdec = GST_VIDEO_DECODER (self);
GstVideoCodecState *output_state;
GstVideoInfo *info = &self->info;
GstVideoInfo *output_info;
output_state = gst_video_decoder_get_output_state (vdec);
if (output_state) {
output_info = &output_state->info;
if (GST_VIDEO_INFO_FORMAT (output_info) == GST_VIDEO_INFO_FORMAT (info) &&
GST_VIDEO_INFO_WIDTH (output_info) == GST_VIDEO_INFO_WIDTH (info) &&
GST_VIDEO_INFO_HEIGHT (output_info) == GST_VIDEO_INFO_HEIGHT (info)) {
gst_video_codec_state_unref (output_state);
return;
}
gst_video_codec_state_unref (output_state);
}
output_state = gst_video_decoder_set_output_state (vdec,
GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info),
GST_VIDEO_INFO_HEIGHT (info), wic->input_state);
gst_video_codec_state_unref (output_state);
gst_video_decoder_negotiate (vdec);
return;
}
static gboolean
gst_wic_png_dec_set_format (GstWicDecoder * decoder, GstVideoCodecState * state)
{
GstWicPngDec *self = GST_WIC_PNG_DEC (decoder);
gst_video_info_init (&self->info);
return TRUE;
}
static GstFlowReturn
gst_wic_png_dec_process_output (GstWicDecoder * decoder,
IWICImagingFactory * factory, IWICBitmapFrameDecode * decode_frame,
GstVideoCodecFrame * frame)
{
GstVideoDecoder *vdec = GST_VIDEO_DECODER (decoder);
GstWicPngDec *self = GST_WIC_PNG_DEC (decoder);
ComPtr < IWICBitmapSource > source;
ComPtr < IWICPlanarBitmapSourceTransform > transform;
UINT width, height;
HRESULT hr;
GstFlowReturn flow_ret;
gboolean rst;
hr = decode_frame->GetSize (&width, &height);
if (FAILED (hr)) {
GST_ERROR_OBJECT (decoder, "Failed to get size, hr: 0x%x", (guint) hr);
goto error;
}
rst = gst_wic_png_dec_prepare_output (self, factory, decode_frame,
width, height, &source);
if (!rst)
goto error;
gst_wic_png_dec_update_output_state (self);
flow_ret = gst_video_decoder_allocate_output_frame (vdec, frame);
if (flow_ret != GST_FLOW_OK) {
gst_video_decoder_release_frame (vdec, frame);
GST_INFO_OBJECT (self, "Unable to allocate output");
return flow_ret;
}
rst = gst_wic_png_dec_fill_output (self, factory, source.Get (),
frame->output_buffer);
if (!rst)
goto error;
return gst_video_decoder_finish_frame (vdec, frame);
error:
gst_video_decoder_release_frame (vdec, frame);
return GST_FLOW_ERROR;
}