mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
mediafoundation: Add h264 encoder
Add Media Foundation h264 encoder. If hardware encoders are available on system, they will have higher rank than software encoder. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/760>
This commit is contained in:
parent
eece89042a
commit
9625d19279
9 changed files with 3437 additions and 11 deletions
1395
sys/mediafoundation/gstmfh264enc.cpp
Normal file
1395
sys/mediafoundation/gstmfh264enc.cpp
Normal file
File diff suppressed because it is too large
Load diff
33
sys/mediafoundation/gstmfh264enc.h
Normal file
33
sys/mediafoundation/gstmfh264enc.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#ifndef __GST_MF_H264_ENC_H__
|
||||
#define __GST_MF_H264_ENC_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gst_mf_h264_enc_plugin_init (GstPlugin * plugin,
|
||||
guint rank);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_MF_H264_ENC_H__ */
|
929
sys/mediafoundation/gstmftransform.cpp
Normal file
929
sys/mediafoundation/gstmftransform.cpp
Normal file
|
@ -0,0 +1,929 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2020 Seungha Yang <seungha.yang@navercorp.com>
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmftransform.h"
|
||||
#include "gstmfutils.h"
|
||||
#include <string.h>
|
||||
#include <wrl.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
extern "C" {
|
||||
GST_DEBUG_CATEGORY_EXTERN (gst_mf_transform_debug);
|
||||
#define GST_CAT_DEFAULT gst_mf_transform_debug
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_DEVICE_NAME,
|
||||
PROP_HARDWARE,
|
||||
PROP_ENUM_PARAMS,
|
||||
};
|
||||
|
||||
struct _GstMFTransform
|
||||
{
|
||||
GstObject object;
|
||||
gboolean initialized;
|
||||
|
||||
GstMFTransformEnumParams enum_params;
|
||||
|
||||
gchar *device_name;
|
||||
gboolean hardware;
|
||||
|
||||
IMFActivate *activate;
|
||||
IMFTransform *transform;
|
||||
ICodecAPI * codec_api;
|
||||
IMFMediaEventGenerator *event_gen;
|
||||
|
||||
GQueue *output_queue;
|
||||
|
||||
DWORD input_id;
|
||||
DWORD output_id;
|
||||
|
||||
gboolean need_start;
|
||||
|
||||
gint pending_need_input;
|
||||
gint pending_have_output;
|
||||
};
|
||||
|
||||
#define gst_mf_transform_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstMFTransform, gst_mf_transform, GST_TYPE_OBJECT);
|
||||
|
||||
static void gst_mf_transform_constructed (GObject * object);
|
||||
static void gst_mf_transform_finalize (GObject * object);
|
||||
static void gst_mf_transform_get_property (GObject * object,
|
||||
guint prop_id, GValue * value, GParamSpec * pspec);
|
||||
static void gst_mf_transform_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec);
|
||||
|
||||
static void
|
||||
gst_mf_transform_class_init (GstMFTransformClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->constructed = gst_mf_transform_constructed;
|
||||
gobject_class->finalize = gst_mf_transform_finalize;
|
||||
gobject_class->get_property = gst_mf_transform_get_property;
|
||||
gobject_class->set_property = gst_mf_transform_set_property;
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
|
||||
g_param_spec_string ("device-name", "device-name",
|
||||
"Device name", NULL,
|
||||
(GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
|
||||
g_object_class_install_property (gobject_class, PROP_HARDWARE,
|
||||
g_param_spec_boolean ("hardware", "Hardware",
|
||||
"Whether hardware device or not", FALSE,
|
||||
(GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
|
||||
g_object_class_install_property (gobject_class, PROP_ENUM_PARAMS,
|
||||
g_param_spec_pointer ("enum-params", "Enum Params",
|
||||
"GstMFTransformEnumParams for MFTEnumEx",
|
||||
(GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mf_transform_init (GstMFTransform * self)
|
||||
{
|
||||
self->output_queue = g_queue_new ();
|
||||
|
||||
CoInitializeEx (NULL, COINIT_MULTITHREADED);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mf_transform_clear_enum_params (GstMFTransformEnumParams *params)
|
||||
{
|
||||
g_free (params->input_typeinfo);
|
||||
params->input_typeinfo = NULL;
|
||||
|
||||
g_free (params->output_typeinfo);
|
||||
params->output_typeinfo = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
release_mf_sample (IMFSample * sample)
|
||||
{
|
||||
if (sample)
|
||||
sample->Release ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mf_transform_finalize (GObject * object)
|
||||
{
|
||||
GstMFTransform *self = GST_MF_TRANSFORM (object);
|
||||
|
||||
gst_mf_transform_close (self);
|
||||
|
||||
if (self->activate)
|
||||
self->activate->Release ();
|
||||
|
||||
gst_mf_transform_clear_enum_params (&self->enum_params);
|
||||
g_free (self->device_name);
|
||||
|
||||
g_queue_free_full (self->output_queue, (GDestroyNotify) release_mf_sample);
|
||||
|
||||
CoUninitialize ();
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mf_transform_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMFTransform *self = GST_MF_TRANSFORM (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DEVICE_NAME:
|
||||
g_value_set_string (value, self->device_name);
|
||||
break;
|
||||
case PROP_HARDWARE:
|
||||
g_value_set_boolean (value, self->hardware);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mf_transform_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMFTransform *self = GST_MF_TRANSFORM (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_ENUM_PARAMS:
|
||||
{
|
||||
GstMFTransformEnumParams *params;
|
||||
params = (GstMFTransformEnumParams *) g_value_get_pointer (value);
|
||||
|
||||
gst_mf_transform_clear_enum_params (&self->enum_params);
|
||||
self->enum_params.category = params->category;
|
||||
self->enum_params.enum_flags = params->enum_flags;
|
||||
self->enum_params.device_index = params->device_index;
|
||||
if (params->input_typeinfo) {
|
||||
self->enum_params.input_typeinfo = g_new0 (MFT_REGISTER_TYPE_INFO, 1);
|
||||
memcpy (self->enum_params.input_typeinfo, params->input_typeinfo,
|
||||
sizeof (MFT_REGISTER_TYPE_INFO));
|
||||
}
|
||||
|
||||
if (params->output_typeinfo) {
|
||||
self->enum_params.output_typeinfo = g_new0 (MFT_REGISTER_TYPE_INFO, 1);
|
||||
memcpy (self->enum_params.output_typeinfo, params->output_typeinfo,
|
||||
sizeof (MFT_REGISTER_TYPE_INFO));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mf_transform_constructed (GObject * object)
|
||||
{
|
||||
GstMFTransform *self = GST_MF_TRANSFORM (object);
|
||||
HRESULT hr;
|
||||
IMFActivate **devices = NULL;
|
||||
UINT32 num_devices, i;
|
||||
LPWSTR name = NULL;
|
||||
|
||||
hr = MFTEnumEx (self->enum_params.category, self->enum_params.enum_flags,
|
||||
self->enum_params.input_typeinfo, self->enum_params.output_typeinfo,
|
||||
&devices, &num_devices);
|
||||
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_WARNING_OBJECT (self, "MFTEnumEx failure");
|
||||
return;
|
||||
}
|
||||
|
||||
if (num_devices == 0 || self->enum_params.device_index >= num_devices) {
|
||||
GST_WARNING_OBJECT (self, "No available device at index %d",
|
||||
self->enum_params.device_index);
|
||||
for (i = 0; i < num_devices; i++) {
|
||||
devices[i]->Release ();
|
||||
}
|
||||
|
||||
CoTaskMemFree (devices);
|
||||
return;
|
||||
}
|
||||
|
||||
self->activate = devices[self->enum_params.device_index];
|
||||
self->activate->AddRef ();
|
||||
|
||||
for (i = 0; i < num_devices; i++)
|
||||
devices[i]->Release ();
|
||||
|
||||
hr = self->activate->GetAllocatedString (MFT_FRIENDLY_NAME_Attribute,
|
||||
&name, NULL);
|
||||
|
||||
if (gst_mf_result (hr)) {
|
||||
self->device_name = g_utf16_to_utf8 ((const gunichar2 *) name,
|
||||
-1, NULL, NULL, NULL);
|
||||
|
||||
CoTaskMemFree (name);
|
||||
|
||||
GST_INFO_OBJECT (self, "Open device %s", self->device_name);
|
||||
}
|
||||
|
||||
done:
|
||||
CoTaskMemFree (devices);
|
||||
|
||||
self->hardware = ! !(self->enum_params.enum_flags & MFT_ENUM_FLAG_HARDWARE);
|
||||
self->initialized = TRUE;
|
||||
}
|
||||
|
||||
static HRESULT
|
||||
gst_mf_transform_pop_event (GstMFTransform * self,
|
||||
gboolean no_wait, MediaEventType * event_type)
|
||||
{
|
||||
ComPtr<IMFMediaEvent> event;
|
||||
MediaEventType type;
|
||||
HRESULT hr;
|
||||
DWORD flags = 0;
|
||||
|
||||
if (!self->hardware || !self->event_gen)
|
||||
return MF_E_NO_EVENTS_AVAILABLE;
|
||||
|
||||
if (no_wait)
|
||||
flags = MF_EVENT_FLAG_NO_WAIT;
|
||||
|
||||
hr = self->event_gen->GetEvent (flags, event.GetAddressOf ());
|
||||
|
||||
if (hr == MF_E_NO_EVENTS_AVAILABLE)
|
||||
return hr;
|
||||
else if (!gst_mf_result (hr))
|
||||
return hr;
|
||||
|
||||
hr = event->GetType (&type);
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self, "Failed to get event, hr: 0x%x", (guint) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
*event_type = type;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mf_transform_drain_all_events (GstMFTransform * self)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (!self->hardware)
|
||||
return;
|
||||
|
||||
do {
|
||||
MediaEventType type;
|
||||
|
||||
hr = gst_mf_transform_pop_event (self, TRUE, &type);
|
||||
if (hr == MF_E_NO_EVENTS_AVAILABLE || !gst_mf_result (hr))
|
||||
return;
|
||||
|
||||
switch (type) {
|
||||
case METransformNeedInput:
|
||||
self->pending_need_input++;
|
||||
break;
|
||||
case METransformHaveOutput:
|
||||
self->pending_have_output++;
|
||||
break;
|
||||
default:
|
||||
GST_DEBUG_OBJECT (self, "Unhandled event %d", type);
|
||||
break;
|
||||
}
|
||||
} while (SUCCEEDED (hr));
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_mf_transform_process_output (GstMFTransform * self)
|
||||
{
|
||||
DWORD status;
|
||||
HRESULT hr;
|
||||
IMFTransform *transform = self->transform;
|
||||
DWORD stream_id = self->output_id;
|
||||
MFT_OUTPUT_STREAM_INFO out_stream_info = { 0 };
|
||||
MFT_OUTPUT_DATA_BUFFER out_data = { 0 };
|
||||
|
||||
hr = transform->GetOutputStreamInfo (stream_id, &out_stream_info);
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't get output stream info");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
if ((out_stream_info.dwFlags & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES |
|
||||
MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)) == 0) {
|
||||
ComPtr<IMFMediaBuffer> buffer;
|
||||
ComPtr<IMFSample> new_sample;
|
||||
|
||||
hr = MFCreateMemoryBuffer (out_stream_info.cbSize,
|
||||
buffer.GetAddressOf ());
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't create memory buffer");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
hr = MFCreateSample (new_sample.GetAddressOf ());
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't create sample");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
hr = new_sample->AddBuffer (buffer.Get ());
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't add buffer to sample");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
out_data.pSample = new_sample.Detach ();
|
||||
}
|
||||
|
||||
out_data.dwStreamID = stream_id;
|
||||
|
||||
hr = transform->ProcessOutput (0, 1, &out_data, &status);
|
||||
|
||||
if (self->hardware)
|
||||
self->pending_have_output--;
|
||||
|
||||
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
|
||||
GST_LOG_OBJECT (self, "Need more input data");
|
||||
return GST_MF_TRANSFORM_FLOW_NEED_DATA;
|
||||
} else if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
|
||||
ComPtr<IMFMediaType> output_type;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Stream change, set output type again");
|
||||
|
||||
hr = transform->GetOutputAvailableType (stream_id,
|
||||
0, output_type.GetAddressOf ());
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't get available output type");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
hr = transform->SetOutputType (stream_id, output_type.Get (), 0);
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't set output type");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
return GST_MF_TRANSFORM_FLOW_NEED_DATA;
|
||||
} else if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self, "ProcessOutput error");
|
||||
if (out_data.pSample)
|
||||
out_data.pSample->Release ();
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
if (!out_data.pSample) {
|
||||
GST_WARNING_OBJECT (self, "No output sample");
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
g_queue_push_tail (self->output_queue, out_data.pSample);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_mf_transform_process_input_sync (GstMFTransform * self,
|
||||
IMFSample * sample)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
hr = self->transform->ProcessInput (self->output_id, sample, 0);
|
||||
|
||||
if (self->hardware)
|
||||
self->pending_need_input--;
|
||||
|
||||
return gst_mf_result (hr);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mf_transform_process_input (GstMFTransform * object,
|
||||
IMFSample * sample)
|
||||
{
|
||||
HRESULT hr;
|
||||
GstFlowReturn ret;
|
||||
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
||||
g_return_val_if_fail (sample != NULL, FALSE);
|
||||
|
||||
if (!object->transform)
|
||||
return FALSE;
|
||||
|
||||
if (object->need_start) {
|
||||
hr = object->transform->ProcessMessage (MFT_MESSAGE_NOTIFY_START_OF_STREAM,
|
||||
0);
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (object, "Cannot post start-of-stream message");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hr = object->transform->ProcessMessage (MFT_MESSAGE_NOTIFY_BEGIN_STREAMING,
|
||||
0);
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (object, "Cannot post begin-stream message");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
object->need_start = FALSE;
|
||||
}
|
||||
|
||||
gst_mf_transform_drain_all_events (object);
|
||||
|
||||
if (object->hardware) {
|
||||
while (object->pending_have_output > 0) {
|
||||
ret = gst_mf_transform_process_output (object);
|
||||
if (ret != GST_FLOW_OK) {
|
||||
if (ret == GST_VIDEO_ENCODER_FLOW_NEED_DATA) {
|
||||
ret = GST_FLOW_OK;
|
||||
break;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (object->pending_need_input == 0) {
|
||||
MediaEventType type;
|
||||
HRESULT hr;
|
||||
|
||||
hr = gst_mf_transform_pop_event (object, FALSE, &type);
|
||||
if (hr != MF_E_NO_EVENTS_AVAILABLE && !gst_mf_result (hr)) {
|
||||
GST_DEBUG_OBJECT (object, "failed to pop event, hr: 0x%x", (guint) hr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case METransformNeedInput:
|
||||
object->pending_need_input++;
|
||||
break;
|
||||
case METransformHaveOutput:
|
||||
object->pending_have_output++;
|
||||
break;
|
||||
default:
|
||||
GST_DEBUG_OBJECT (object, "Unhandled event %d", type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return gst_mf_transform_process_input_sync (object, sample);
|
||||
}
|
||||
|
||||
GstFlowReturn
|
||||
gst_mf_transform_get_output (GstMFTransform * object,
|
||||
IMFSample ** sample)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), GST_FLOW_ERROR);
|
||||
g_return_val_if_fail (sample != NULL, GST_FLOW_ERROR);
|
||||
|
||||
if (!object->transform)
|
||||
return GST_FLOW_ERROR;
|
||||
|
||||
gst_mf_transform_drain_all_events (object);
|
||||
|
||||
if (!object->hardware || object->pending_have_output)
|
||||
gst_mf_transform_process_output (object);
|
||||
|
||||
if (g_queue_is_empty (object->output_queue))
|
||||
return GST_MF_TRANSFORM_FLOW_NEED_DATA;
|
||||
|
||||
*sample = (IMFSample *) g_queue_pop_head (object->output_queue);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mf_transform_flush (GstMFTransform * object)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
||||
|
||||
if (object->transform) {
|
||||
if (!object->need_start)
|
||||
object->transform->ProcessMessage (MFT_MESSAGE_COMMAND_FLUSH, 0);
|
||||
|
||||
object->pending_have_output = 0;
|
||||
object->pending_need_input = 0;
|
||||
}
|
||||
|
||||
object->need_start = TRUE;
|
||||
|
||||
while (!g_queue_is_empty (object->output_queue)) {
|
||||
IMFSample *sample = (IMFSample *) g_queue_pop_head (object->output_queue);
|
||||
sample->Release ();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mf_transform_drain (GstMFTransform * object)
|
||||
{
|
||||
GstFlowReturn ret;
|
||||
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
||||
|
||||
if (!object->transform)
|
||||
return TRUE;
|
||||
|
||||
object->need_start = TRUE;
|
||||
object->transform->ProcessMessage (MFT_MESSAGE_COMMAND_DRAIN, 0);
|
||||
|
||||
if (object->hardware) {
|
||||
MediaEventType type;
|
||||
HRESULT hr;
|
||||
|
||||
do {
|
||||
hr = gst_mf_transform_pop_event (object, FALSE, &type);
|
||||
if (hr != MF_E_NO_EVENTS_AVAILABLE && FAILED (hr)) {
|
||||
GST_DEBUG_OBJECT (object, "failed to pop event, hr: 0x%x", (guint) hr);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case METransformNeedInput:
|
||||
GST_DEBUG_OBJECT (object, "Ignore need input during finish");
|
||||
break;
|
||||
case METransformHaveOutput:
|
||||
object->pending_have_output++;
|
||||
gst_mf_transform_process_output (object);
|
||||
break;
|
||||
case METransformDrainComplete:
|
||||
GST_DEBUG_OBJECT (object, "Drain complete");
|
||||
return TRUE;
|
||||
default:
|
||||
GST_DEBUG_OBJECT (object, "Unhandled event %d", type);
|
||||
break;
|
||||
}
|
||||
} while (SUCCEEDED (hr));
|
||||
|
||||
/* and drain all the other events if any */
|
||||
gst_mf_transform_drain_all_events (object);
|
||||
|
||||
object->pending_have_output = 0;
|
||||
object->pending_need_input = 0;
|
||||
} else {
|
||||
do {
|
||||
ret = gst_mf_transform_process_output (object);
|
||||
} while (ret == GST_FLOW_OK);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mf_transform_open (GstMFTransform * object)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
||||
|
||||
gst_mf_transform_close (object);
|
||||
|
||||
hr = object->activate->ActivateObject (IID_IMFTransform,
|
||||
(void **) &object->transform);
|
||||
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_WARNING_OBJECT (object, "Couldn't open MFT");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (object->hardware) {
|
||||
ComPtr<IMFAttributes> attr;
|
||||
|
||||
hr = object->transform->GetAttributes (attr.GetAddressOf ());
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (object, "Couldn't get attribute object");
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = attr->SetUINT32 (MF_TRANSFORM_ASYNC_UNLOCK, TRUE);
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (object, "MF_TRANSFORM_ASYNC_UNLOCK error");
|
||||
goto error;
|
||||
}
|
||||
|
||||
hr = object->transform->QueryInterface (IID_IMFMediaEventGenerator,
|
||||
(void **) &object->event_gen);
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (object, "IMFMediaEventGenerator unavailable");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
hr = object->transform->GetStreamIDs (1, &object->input_id, 1,
|
||||
&object->output_id);
|
||||
if (hr == E_NOTIMPL) {
|
||||
object->input_id = 0;
|
||||
object->output_id = 0;
|
||||
}
|
||||
|
||||
hr = object->transform->QueryInterface (IID_ICodecAPI,
|
||||
(void **) &object->codec_api);
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_WARNING_OBJECT (object, "ICodecAPI is unavailable");
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
gst_mf_transform_close (object);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mf_transform_close (GstMFTransform * object)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
||||
|
||||
gst_mf_transform_flush (object);
|
||||
|
||||
if (object->event_gen) {
|
||||
object->event_gen->Release ();
|
||||
object->event_gen = NULL;
|
||||
}
|
||||
|
||||
if (object->codec_api) {
|
||||
object->codec_api->Release ();
|
||||
object->codec_api = NULL;
|
||||
}
|
||||
|
||||
if (object->transform) {
|
||||
object->transform->Release ();
|
||||
object->transform = NULL;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
IMFActivate *
|
||||
gst_mf_transform_get_activate_handle (GstMFTransform * object)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), NULL);
|
||||
|
||||
return object->activate;
|
||||
}
|
||||
|
||||
IMFTransform *
|
||||
gst_mf_transform_get_transform_handle (GstMFTransform * object)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), NULL);
|
||||
|
||||
if (!object->transform) {
|
||||
GST_WARNING_OBJECT (object,
|
||||
"IMFTransform is not configured, open MFT first");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return object->transform;
|
||||
}
|
||||
|
||||
ICodecAPI *
|
||||
gst_mf_transform_get_codec_api_handle (GstMFTransform * object)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), NULL);
|
||||
|
||||
if (!object->codec_api) {
|
||||
GST_WARNING_OBJECT (object,
|
||||
"ICodecAPI is not configured, open MFT first");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return object->codec_api;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mf_transform_get_input_available_types (GstMFTransform * object,
|
||||
GList ** input_types)
|
||||
{
|
||||
IMFTransform *transform;
|
||||
HRESULT hr;
|
||||
DWORD index = 0;
|
||||
GList *list = NULL;
|
||||
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
||||
g_return_val_if_fail (input_types != NULL, FALSE);
|
||||
|
||||
transform = object->transform;
|
||||
|
||||
if (!transform) {
|
||||
GST_ERROR_OBJECT (object, "Should open first");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
do {
|
||||
IMFMediaType *type = NULL;
|
||||
|
||||
hr = transform->GetInputAvailableType (object->input_id, index, &type);
|
||||
if (SUCCEEDED (hr))
|
||||
list = g_list_append (list, type);
|
||||
|
||||
index++;
|
||||
} while (SUCCEEDED (hr));
|
||||
|
||||
*input_types = list;
|
||||
|
||||
return !!list;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mf_transform_get_output_available_types (GstMFTransform * object,
|
||||
GList ** output_types)
|
||||
{
|
||||
IMFTransform *transform;
|
||||
HRESULT hr;
|
||||
DWORD index = 0;
|
||||
GList *list = NULL;
|
||||
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
||||
g_return_val_if_fail (output_types != NULL, FALSE);
|
||||
|
||||
transform = object->transform;
|
||||
|
||||
if (!transform) {
|
||||
GST_ERROR_OBJECT (object, "Should open first");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
do {
|
||||
IMFMediaType *type;
|
||||
|
||||
hr = transform->GetOutputAvailableType (object->input_id, index, &type);
|
||||
if (SUCCEEDED (hr))
|
||||
list = g_list_append (list, type);
|
||||
|
||||
index++;
|
||||
} while (SUCCEEDED (hr));
|
||||
|
||||
*output_types = list;
|
||||
|
||||
return !!list;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mf_transform_set_input_type (GstMFTransform * object,
|
||||
IMFMediaType * input_type)
|
||||
{
|
||||
IMFTransform *transform;
|
||||
HRESULT hr;
|
||||
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
||||
|
||||
transform = object->transform;
|
||||
|
||||
if (!transform) {
|
||||
GST_ERROR_OBJECT (object, "Should open first");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hr = transform->SetInputType (object->input_id, input_type, 0);
|
||||
if (!gst_mf_result (hr))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mf_transform_set_output_type (GstMFTransform * object,
|
||||
IMFMediaType * output_type)
|
||||
{
|
||||
IMFTransform *transform;
|
||||
HRESULT hr;
|
||||
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
||||
|
||||
transform = object->transform;
|
||||
|
||||
if (!transform) {
|
||||
GST_ERROR_OBJECT (object, "Should open first");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hr = transform->SetOutputType (object->output_id, output_type, 0);
|
||||
if (!gst_mf_result (hr)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GstMFTransform *
|
||||
gst_mf_transform_new (GstMFTransformEnumParams * params)
|
||||
{
|
||||
GstMFTransform *self;
|
||||
|
||||
g_return_val_if_fail (params != NULL, NULL);
|
||||
|
||||
self = (GstMFTransform *) g_object_new (GST_TYPE_MF_TRANSFORM_OBJECT,
|
||||
"enum-params", params, NULL);
|
||||
|
||||
if (!self->initialized) {
|
||||
gst_object_unref (self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gst_object_ref_sink (self);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mf_transform_set_codec_api_uint32 (GstMFTransform * object,
|
||||
const GUID * api, guint32 value)
|
||||
{
|
||||
HRESULT hr;
|
||||
VARIANT var;
|
||||
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
||||
g_return_val_if_fail (api != NULL, FALSE);
|
||||
|
||||
if (!object->codec_api) {
|
||||
GST_WARNING_OBJECT (object, "codec api unavailable");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
VariantInit (&var);
|
||||
var.vt = VT_UI4;
|
||||
var.ulVal = value;
|
||||
|
||||
hr = object->codec_api->SetValue (api, &var);
|
||||
VariantClear (&var);
|
||||
|
||||
return gst_mf_result (hr);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mf_transform_set_codec_api_uint64 (GstMFTransform * object,
|
||||
const GUID * api, guint64 value)
|
||||
{
|
||||
HRESULT hr;
|
||||
VARIANT var;
|
||||
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
||||
g_return_val_if_fail (api != NULL, FALSE);
|
||||
|
||||
if (!object->codec_api) {
|
||||
GST_WARNING_OBJECT (object, "codec api unavailable");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
VariantInit (&var);
|
||||
var.vt = VT_UI8;
|
||||
var.ullVal = value;
|
||||
|
||||
hr = object->codec_api->SetValue (api, &var);
|
||||
VariantClear (&var);
|
||||
|
||||
return gst_mf_result (hr);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mf_transform_set_codec_api_boolean (GstMFTransform * object,
|
||||
const GUID * api, gboolean value)
|
||||
{
|
||||
HRESULT hr;
|
||||
VARIANT var;
|
||||
|
||||
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
||||
g_return_val_if_fail (api != NULL, FALSE);
|
||||
|
||||
if (!object->codec_api) {
|
||||
GST_WARNING_OBJECT (object, "codec api unavailable");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
VariantInit (&var);
|
||||
var.vt = VT_BOOL;
|
||||
var.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE;
|
||||
|
||||
hr = object->codec_api->SetValue (api, &var);
|
||||
VariantClear (&var);
|
||||
|
||||
return gst_mf_result (hr);
|
||||
}
|
||||
|
95
sys/mediafoundation/gstmftransform.h
Normal file
95
sys/mediafoundation/gstmftransform.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2020 Seungha Yang <seungha.yang@navercorp.com>
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#ifndef __GST_MF_TRANSFORM_OBJECT_H__
|
||||
#define __GST_MF_TRANSFORM_OBJECT_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmfutils.h"
|
||||
#include <codecapi.h>
|
||||
#include "gststrmif.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MF_TRANSFORM_OBJECT (gst_mf_transform_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstMFTransform, gst_mf_transform,
|
||||
GST, MF_TRANSFORM, GstObject);
|
||||
|
||||
#define GST_MF_TRANSFORM_FLOW_NEED_DATA GST_FLOW_CUSTOM_SUCCESS
|
||||
|
||||
typedef struct _GstMFTransformEnumParams
|
||||
{
|
||||
GUID category;
|
||||
guint32 enum_flags;
|
||||
MFT_REGISTER_TYPE_INFO *input_typeinfo;
|
||||
MFT_REGISTER_TYPE_INFO *output_typeinfo;
|
||||
|
||||
guint device_index;
|
||||
} GstMFTransformEnumParams;
|
||||
|
||||
GstMFTransform * gst_mf_transform_new (GstMFTransformEnumParams * params);
|
||||
|
||||
gboolean gst_mf_transform_open (GstMFTransform * object);
|
||||
|
||||
gboolean gst_mf_transform_close (GstMFTransform * object);
|
||||
|
||||
IMFActivate * gst_mf_transform_get_activate_handle (GstMFTransform * object);
|
||||
|
||||
IMFTransform * gst_mf_transform_get_transform_handle (GstMFTransform * object);
|
||||
|
||||
ICodecAPI * gst_mf_transform_get_codec_api_handle (GstMFTransform * object);
|
||||
|
||||
gboolean gst_mf_transform_process_input (GstMFTransform * object,
|
||||
IMFSample * sample);
|
||||
|
||||
GstFlowReturn gst_mf_transform_get_output (GstMFTransform * object,
|
||||
IMFSample ** sample);
|
||||
|
||||
gboolean gst_mf_transform_flush (GstMFTransform * object);
|
||||
|
||||
gboolean gst_mf_transform_drain (GstMFTransform * object);
|
||||
|
||||
gboolean gst_mf_transform_get_input_available_types (GstMFTransform * object,
|
||||
GList ** input_types);
|
||||
|
||||
gboolean gst_mf_transform_get_output_available_types (GstMFTransform * object,
|
||||
GList ** output_types);
|
||||
|
||||
gboolean gst_mf_transform_set_input_type (GstMFTransform * object,
|
||||
IMFMediaType * input_type);
|
||||
|
||||
gboolean gst_mf_transform_set_output_type (GstMFTransform * object,
|
||||
IMFMediaType * output_type);
|
||||
|
||||
gboolean gst_mf_transform_set_codec_api_uint32 (GstMFTransform * object,
|
||||
const GUID * api,
|
||||
guint32 value);
|
||||
|
||||
gboolean gst_mf_transform_set_codec_api_uint64 (GstMFTransform * object,
|
||||
const GUID * api,
|
||||
guint64 value);
|
||||
|
||||
gboolean gst_mf_transform_set_codec_api_boolean (GstMFTransform * object,
|
||||
const GUID * api,
|
||||
gboolean value);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_MF_TRANSFORM_OBJECT_H__ */
|
574
sys/mediafoundation/gstmfvideoenc.cpp
Normal file
574
sys/mediafoundation/gstmfvideoenc.cpp
Normal file
|
@ -0,0 +1,574 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2020 Seungha Yang <seungha.yang@navercorp.com>
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmfvideoenc.h"
|
||||
#include <wrl.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
GST_DEBUG_CATEGORY (gst_mf_video_enc_debug);
|
||||
#define GST_CAT_DEFAULT gst_mf_video_enc_debug
|
||||
|
||||
#define gst_mf_video_enc_parent_class parent_class
|
||||
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstMFVideoEnc, gst_mf_video_enc,
|
||||
GST_TYPE_VIDEO_ENCODER,
|
||||
GST_DEBUG_CATEGORY_INIT (gst_mf_video_enc_debug, "mfvideoenc", 0,
|
||||
"mfvideoenc"));
|
||||
|
||||
static gboolean gst_mf_video_enc_open (GstVideoEncoder * enc);
|
||||
static gboolean gst_mf_video_enc_close (GstVideoEncoder * enc);
|
||||
static gboolean gst_mf_video_enc_set_format (GstVideoEncoder * enc,
|
||||
GstVideoCodecState * state);
|
||||
static GstFlowReturn gst_mf_video_enc_handle_frame (GstVideoEncoder * enc,
|
||||
GstVideoCodecFrame * frame);
|
||||
static GstFlowReturn gst_mf_video_enc_finish (GstVideoEncoder * enc);
|
||||
static gboolean gst_mf_video_enc_flush (GstVideoEncoder * enc);
|
||||
|
||||
static void
|
||||
gst_mf_video_enc_class_init (GstMFVideoEncClass * klass)
|
||||
{
|
||||
GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
|
||||
|
||||
videoenc_class->open = GST_DEBUG_FUNCPTR (gst_mf_video_enc_open);
|
||||
videoenc_class->close = GST_DEBUG_FUNCPTR (gst_mf_video_enc_close);
|
||||
videoenc_class->set_format = GST_DEBUG_FUNCPTR (gst_mf_video_enc_set_format);
|
||||
videoenc_class->handle_frame =
|
||||
GST_DEBUG_FUNCPTR (gst_mf_video_enc_handle_frame);
|
||||
videoenc_class->finish = GST_DEBUG_FUNCPTR (gst_mf_video_enc_finish);
|
||||
videoenc_class->flush = GST_DEBUG_FUNCPTR (gst_mf_video_enc_flush);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mf_video_enc_init (GstMFVideoEnc * self)
|
||||
{
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_mf_video_enc_open (GstVideoEncoder * enc)
|
||||
{
|
||||
GstMFVideoEnc *self = GST_MF_VIDEO_ENC (enc);
|
||||
GstMFVideoEncClass *klass = GST_MF_VIDEO_ENC_GET_CLASS (enc);
|
||||
GstMFTransformEnumParams enum_params = { 0, };
|
||||
MFT_REGISTER_TYPE_INFO output_type;
|
||||
gboolean ret;
|
||||
|
||||
output_type.guidMajorType = MFMediaType_Video;
|
||||
output_type.guidSubtype = klass->codec_id;
|
||||
|
||||
enum_params.category = MFT_CATEGORY_VIDEO_ENCODER;
|
||||
enum_params.enum_flags = klass->enum_flags;
|
||||
enum_params.output_typeinfo = &output_type;
|
||||
enum_params.device_index = klass->device_index;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Create MFT with enum flags 0x%x, device index %d",
|
||||
klass->enum_flags, klass->device_index);
|
||||
|
||||
self->transform = gst_mf_transform_new (&enum_params);
|
||||
ret = !!self->transform;
|
||||
|
||||
if (!ret)
|
||||
GST_ERROR_OBJECT (self, "Cannot create MFT object");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_mf_video_enc_close (GstVideoEncoder * enc)
|
||||
{
|
||||
GstMFVideoEnc *self = GST_MF_VIDEO_ENC (enc);
|
||||
|
||||
gst_clear_object (&self->transform);
|
||||
|
||||
if (self->input_state) {
|
||||
gst_video_codec_state_unref (self->input_state);
|
||||
self->input_state = NULL;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mf_media_type_release (IMFMediaType * type)
|
||||
{
|
||||
if (type)
|
||||
type->Release ();
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_mf_video_enc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state)
|
||||
{
|
||||
GstMFVideoEnc *self = GST_MF_VIDEO_ENC (enc);
|
||||
GstMFVideoEncClass *klass = GST_MF_VIDEO_ENC_GET_CLASS (enc);
|
||||
GstVideoInfo *info = &state->info;
|
||||
ComPtr<IMFMediaType> in_type;
|
||||
ComPtr<IMFMediaType> out_type;
|
||||
GList *input_types = NULL;
|
||||
GList *iter;
|
||||
HRESULT hr;
|
||||
gint fps_n, fps_d;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Set format");
|
||||
|
||||
gst_mf_video_enc_finish (enc);
|
||||
|
||||
if (self->input_state)
|
||||
gst_video_codec_state_unref (self->input_state);
|
||||
self->input_state = gst_video_codec_state_ref (state);
|
||||
|
||||
if (!gst_mf_transform_open (self->transform)) {
|
||||
GST_ERROR_OBJECT (self, "Failed to open MFT");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hr = MFCreateMediaType (out_type.GetAddressOf ());
|
||||
if (!gst_mf_result (hr))
|
||||
return FALSE;
|
||||
|
||||
hr = out_type->SetGUID (MF_MT_MAJOR_TYPE, MFMediaType_Video);
|
||||
if (!gst_mf_result (hr))
|
||||
return FALSE;
|
||||
|
||||
if (klass->set_option) {
|
||||
if (!klass->set_option (self, out_type.Get ())) {
|
||||
GST_ERROR_OBJECT (self, "subclass failed to set option");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
fps_n = GST_VIDEO_INFO_FPS_N (info);
|
||||
fps_d = GST_VIDEO_INFO_FPS_D (info);
|
||||
if (fps_n == 0 || fps_d == 0) {
|
||||
fps_n = 0;
|
||||
fps_d = 1;
|
||||
}
|
||||
|
||||
hr = MFSetAttributeRatio (out_type.Get (), MF_MT_FRAME_RATE, fps_n, fps_d);
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self,
|
||||
"Couldn't set framerate %d/%d, hr: 0x%x", (guint) hr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hr = MFSetAttributeSize (out_type.Get (), MF_MT_FRAME_SIZE,
|
||||
GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info));
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self,
|
||||
"Couldn't set resolution %dx%d, hr: 0x%x", GST_VIDEO_INFO_WIDTH (info),
|
||||
GST_VIDEO_INFO_HEIGHT (info), (guint) hr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hr = out_type->SetUINT32 (MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self,
|
||||
"Couldn't set interlace mode, hr: 0x%x", (guint) hr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_mf_transform_set_output_type (self->transform, out_type.Get ())) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't set output type");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_mf_transform_get_input_available_types (self->transform,
|
||||
&input_types)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't get available input types");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (iter = input_types; iter; iter = g_list_next (iter)) {
|
||||
GstVideoFormat format;
|
||||
GUID subtype;
|
||||
IMFMediaType *type = (IMFMediaType *) iter->data;
|
||||
|
||||
hr = type->GetGUID (MF_MT_SUBTYPE, &subtype);
|
||||
if (!gst_mf_result (hr))
|
||||
continue;
|
||||
|
||||
format = gst_mf_video_subtype_to_video_format (&subtype);
|
||||
if (format != GST_VIDEO_INFO_FORMAT (info))
|
||||
continue;
|
||||
|
||||
in_type = type;
|
||||
}
|
||||
|
||||
g_list_free_full (input_types, (GDestroyNotify) gst_mf_media_type_release);
|
||||
|
||||
if (!in_type) {
|
||||
GST_ERROR_OBJECT (self,
|
||||
"Couldn't convert input caps %" GST_PTR_FORMAT " to media type",
|
||||
state->caps);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hr = MFSetAttributeSize (in_type.Get (), MF_MT_FRAME_SIZE,
|
||||
GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info));
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't set frame size %dx%d",
|
||||
GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hr = in_type->SetUINT32 (MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self,
|
||||
"Couldn't set interlace mode, hr: 0x%x", (guint) hr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hr = MFSetAttributeRatio (in_type.Get (), MF_MT_PIXEL_ASPECT_RATIO,
|
||||
GST_VIDEO_INFO_PAR_N (info), GST_VIDEO_INFO_PAR_D (info));
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't set par %d/%d",
|
||||
GST_VIDEO_INFO_PAR_N (info), GST_VIDEO_INFO_PAR_D (info));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hr = MFSetAttributeRatio (in_type.Get (), MF_MT_FRAME_RATE, fps_n, fps_d);
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't set framerate ratio %d/%d", fps_n, fps_d);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hr = in_type->SetUINT32 (MF_MT_DEFAULT_STRIDE,
|
||||
GST_VIDEO_INFO_PLANE_STRIDE (info, 0));
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't set default stride");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_mf_transform_set_input_type (self->transform, in_type.Get ())) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't set input media type");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_assert (klass->set_src_caps != NULL);
|
||||
if (!klass->set_src_caps (self, self->input_state, out_type.Get ())) {
|
||||
GST_ERROR_OBJECT (self, "subclass couldn't set src caps");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstClockTime mf_pts;
|
||||
} GstMFVideoEncFrameData;
|
||||
|
||||
static gboolean
|
||||
gst_mf_video_enc_process_input (GstMFVideoEnc * self,
|
||||
GstVideoCodecFrame * frame)
|
||||
{
|
||||
GstMFVideoEncClass *klass = GST_MF_VIDEO_ENC_GET_CLASS (self);
|
||||
HRESULT hr;
|
||||
ComPtr<IMFSample> sample;
|
||||
ComPtr<IMFMediaBuffer> media_buffer;
|
||||
GstVideoInfo *info = &self->input_state->info;
|
||||
gint i, j;
|
||||
BYTE *data;
|
||||
GstVideoFrame vframe;
|
||||
gboolean res = FALSE;
|
||||
gboolean unset_force_keyframe = FALSE;
|
||||
GstMFVideoEncFrameData *frame_data = NULL;
|
||||
|
||||
if (!gst_video_frame_map (&vframe, info, frame->input_buffer, GST_MAP_READ)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't map input frame");
|
||||
gst_video_codec_frame_unref (frame);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hr = MFCreateSample (sample.GetAddressOf ());
|
||||
if (!gst_mf_result (hr))
|
||||
goto done;
|
||||
|
||||
hr = MFCreateMemoryBuffer (GST_VIDEO_INFO_SIZE (info),
|
||||
media_buffer.GetAddressOf ());
|
||||
if (!gst_mf_result (hr))
|
||||
goto done;
|
||||
|
||||
hr = media_buffer->Lock (&data, NULL, NULL);
|
||||
if (!gst_mf_result (hr))
|
||||
goto done;
|
||||
|
||||
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
|
||||
guint8 *src, *dst;
|
||||
gint src_stride, dst_stride;
|
||||
gint width;
|
||||
|
||||
src = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&vframe, i);
|
||||
dst = data + GST_VIDEO_INFO_PLANE_OFFSET (info, i);
|
||||
|
||||
src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, i);
|
||||
dst_stride = GST_VIDEO_INFO_PLANE_STRIDE (info, i);
|
||||
|
||||
width = GST_VIDEO_INFO_COMP_WIDTH (info, i)
|
||||
* GST_VIDEO_INFO_COMP_PSTRIDE (info, i);
|
||||
|
||||
for (j = 0; j < GST_VIDEO_INFO_COMP_HEIGHT (info, i); j++) {
|
||||
memcpy (dst, src, width);
|
||||
src += src_stride;
|
||||
dst += dst_stride;
|
||||
}
|
||||
}
|
||||
|
||||
media_buffer->Unlock ();
|
||||
|
||||
hr = media_buffer->SetCurrentLength (GST_VIDEO_INFO_SIZE (info));
|
||||
if (!gst_mf_result (hr))
|
||||
goto done;
|
||||
|
||||
hr = sample->AddBuffer (media_buffer.Get ());
|
||||
if (!gst_mf_result (hr))
|
||||
goto done;
|
||||
|
||||
frame_data = g_new0 (GstMFVideoEncFrameData, 1);
|
||||
frame_data->mf_pts = frame->pts / 100;
|
||||
|
||||
gst_video_codec_frame_set_user_data (frame,
|
||||
frame_data, (GDestroyNotify) g_free);
|
||||
|
||||
hr = sample->SetSampleTime (frame_data->mf_pts);
|
||||
if (!gst_mf_result (hr))
|
||||
goto done;
|
||||
|
||||
hr = sample->SetSampleDuration (
|
||||
GST_CLOCK_TIME_IS_VALID (frame->duration) ? frame->duration / 100 : 0);
|
||||
if (!gst_mf_result (hr))
|
||||
goto done;
|
||||
|
||||
if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) {
|
||||
if (klass->can_force_keyframe) {
|
||||
unset_force_keyframe =
|
||||
gst_mf_transform_set_codec_api_uint32 (self->transform,
|
||||
&CODECAPI_AVEncVideoForceKeyFrame, TRUE);
|
||||
} else {
|
||||
GST_WARNING_OBJECT (self, "encoder does not support force keyframe");
|
||||
}
|
||||
}
|
||||
|
||||
if (!gst_mf_transform_process_input (self->transform, sample.Get ())) {
|
||||
GST_ERROR_OBJECT (self, "Failed to process input");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (unset_force_keyframe) {
|
||||
gst_mf_transform_set_codec_api_uint32 (self->transform,
|
||||
&CODECAPI_AVEncVideoForceKeyFrame, FALSE);
|
||||
}
|
||||
|
||||
res = TRUE;
|
||||
|
||||
done:
|
||||
gst_video_frame_unmap (&vframe);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static GstVideoCodecFrame *
|
||||
gst_mf_video_enc_find_output_frame (GstMFVideoEnc * self, UINT64 mf_dts,
|
||||
UINT64 mf_pts)
|
||||
{
|
||||
GList *l, *walk = gst_video_encoder_get_frames (GST_VIDEO_ENCODER (self));
|
||||
GstVideoCodecFrame *ret = NULL;
|
||||
|
||||
for (l = walk; l; l = l->next) {
|
||||
GstVideoCodecFrame *frame = (GstVideoCodecFrame *) l->data;
|
||||
GstMFVideoEncFrameData *data = (GstMFVideoEncFrameData *)
|
||||
gst_video_codec_frame_get_user_data (frame);
|
||||
|
||||
if (!data)
|
||||
continue;
|
||||
|
||||
if (mf_dts == data->mf_pts) {
|
||||
ret = frame;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* find target with pts */
|
||||
if (!ret) {
|
||||
for (l = walk; l; l = l->next) {
|
||||
GstVideoCodecFrame *frame = (GstVideoCodecFrame *) l->data;
|
||||
GstMFVideoEncFrameData *data = (GstMFVideoEncFrameData *)
|
||||
gst_video_codec_frame_get_user_data (frame);
|
||||
|
||||
if (!data)
|
||||
continue;
|
||||
|
||||
if (mf_pts == data->mf_pts) {
|
||||
ret = frame;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
gst_video_codec_frame_ref (ret);
|
||||
} else {
|
||||
/* just return the oldest one */
|
||||
ret = gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (self));
|
||||
}
|
||||
|
||||
if (walk)
|
||||
g_list_free_full (walk, (GDestroyNotify) gst_video_codec_frame_unref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_mf_video_enc_process_output (GstMFVideoEnc * self)
|
||||
{
|
||||
HRESULT hr;
|
||||
BYTE *data;
|
||||
ComPtr<IMFMediaBuffer> media_buffer;
|
||||
ComPtr<IMFSample> sample;
|
||||
GstBuffer *buffer;
|
||||
GstFlowReturn res = GST_FLOW_ERROR;
|
||||
GstVideoCodecFrame *frame;
|
||||
LONGLONG sample_timestamp;
|
||||
LONGLONG sample_duration;
|
||||
UINT32 keyframe = FALSE;
|
||||
UINT64 mf_dts = GST_CLOCK_TIME_NONE;
|
||||
DWORD buffer_len;
|
||||
|
||||
res = gst_mf_transform_get_output (self->transform, sample.GetAddressOf ());
|
||||
|
||||
if (res != GST_FLOW_OK)
|
||||
return res;
|
||||
|
||||
hr = sample->GetBufferByIndex (0, media_buffer.GetAddressOf ());
|
||||
if (!gst_mf_result (hr))
|
||||
return GST_FLOW_ERROR;
|
||||
|
||||
hr = media_buffer->Lock (&data, NULL, &buffer_len);
|
||||
if (!gst_mf_result (hr))
|
||||
return GST_FLOW_ERROR;
|
||||
|
||||
buffer = gst_buffer_new_allocate (NULL, buffer_len, NULL);
|
||||
gst_buffer_fill (buffer, 0, data, buffer_len);
|
||||
media_buffer->Unlock ();
|
||||
|
||||
sample->GetSampleTime (&sample_timestamp);
|
||||
sample->GetSampleDuration (&sample_duration);
|
||||
sample->GetUINT32 (MFSampleExtension_CleanPoint, &keyframe);
|
||||
|
||||
hr = sample->GetUINT64 (MFSampleExtension_DecodeTimestamp, &mf_dts);
|
||||
if (FAILED (hr))
|
||||
mf_dts = sample_timestamp;
|
||||
|
||||
frame = gst_mf_video_enc_find_output_frame (self,
|
||||
mf_dts, (UINT64) sample_timestamp);
|
||||
|
||||
if (frame) {
|
||||
if (keyframe) {
|
||||
GST_DEBUG_OBJECT (self, "Keyframe pts %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (frame->pts));
|
||||
GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
|
||||
GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
|
||||
} else {
|
||||
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
|
||||
}
|
||||
|
||||
frame->pts = sample_timestamp * 100;
|
||||
frame->dts = mf_dts * 100;
|
||||
frame->duration = sample_duration * 100;
|
||||
frame->output_buffer = buffer;
|
||||
|
||||
res = gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (self), frame);
|
||||
} else {
|
||||
GST_BUFFER_DTS (buffer) = mf_dts * 100;
|
||||
GST_BUFFER_PTS (buffer) = sample_timestamp * 100;
|
||||
GST_BUFFER_DURATION (buffer) = sample_duration * 100;
|
||||
|
||||
if (keyframe) {
|
||||
GST_DEBUG_OBJECT (self, "Keyframe pts %" GST_TIME_FORMAT,
|
||||
GST_BUFFER_PTS (buffer));
|
||||
GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
|
||||
} else {
|
||||
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
|
||||
}
|
||||
|
||||
res = gst_pad_push (GST_VIDEO_ENCODER_SRC_PAD (self), buffer);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_mf_video_enc_handle_frame (GstVideoEncoder * enc,
|
||||
GstVideoCodecFrame * frame)
|
||||
{
|
||||
GstMFVideoEnc *self = GST_MF_VIDEO_ENC (enc);
|
||||
GstFlowReturn ret;
|
||||
|
||||
if (!gst_mf_video_enc_process_input (self, frame)) {
|
||||
GST_ERROR_OBJECT (self, "Failed to process input");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = gst_mf_video_enc_process_output (self);
|
||||
} while (ret == GST_FLOW_OK);
|
||||
|
||||
if (ret == GST_MF_TRANSFORM_FLOW_NEED_DATA)
|
||||
ret = GST_FLOW_OK;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_mf_video_enc_finish (GstVideoEncoder * enc)
|
||||
{
|
||||
GstMFVideoEnc *self = GST_MF_VIDEO_ENC (enc);
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
|
||||
if (!self->transform)
|
||||
return GST_FLOW_OK;
|
||||
|
||||
gst_mf_transform_drain (self->transform);
|
||||
|
||||
do {
|
||||
ret = gst_mf_video_enc_process_output (self);
|
||||
} while (ret == GST_FLOW_OK);
|
||||
|
||||
if (ret == GST_MF_TRANSFORM_FLOW_NEED_DATA)
|
||||
ret = GST_FLOW_OK;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_mf_video_enc_flush (GstVideoEncoder * enc)
|
||||
{
|
||||
GstMFVideoEnc *self = GST_MF_VIDEO_ENC (enc);
|
||||
|
||||
if (!self->transform)
|
||||
return TRUE;
|
||||
|
||||
gst_mf_transform_flush (self->transform);
|
||||
|
||||
return TRUE;
|
||||
}
|
71
sys/mediafoundation/gstmfvideoenc.h
Normal file
71
sys/mediafoundation/gstmfvideoenc.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2020 Seungha Yang <seungha.yang@navercorp.com>
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#ifndef __GST_MF_VIDEO_ENC_H__
|
||||
#define __GST_MF_VIDEO_ENC_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
#include "gstmfutils.h"
|
||||
#include "gstmftransform.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MF_VIDEO_ENC (gst_mf_video_enc_get_type())
|
||||
#define GST_MF_VIDEO_ENC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MF_VIDEO_ENC,GstMFVideoEnc))
|
||||
#define GST_MF_VIDEO_ENC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_MF_VIDEO_ENC,GstMFVideoEncClass))
|
||||
#define GST_MF_VIDEO_ENC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_MF_VIDEO_ENC,GstMFVideoEncClass))
|
||||
#define GST_IS_MF_VIDEO_ENC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MF_VIDEO_ENC))
|
||||
#define GST_IS_MF_VIDEO_ENC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_MF_VIDEO_ENC))
|
||||
|
||||
typedef struct _GstMFVideoEnc GstMFVideoEnc;
|
||||
typedef struct _GstMFVideoEncClass GstMFVideoEncClass;
|
||||
|
||||
struct _GstMFVideoEnc
|
||||
{
|
||||
GstVideoEncoder parent;
|
||||
|
||||
GstMFTransform *transform;
|
||||
|
||||
GstVideoCodecState *input_state;
|
||||
};
|
||||
|
||||
struct _GstMFVideoEncClass
|
||||
{
|
||||
GstVideoEncoderClass parent_class;
|
||||
|
||||
GUID codec_id;
|
||||
guint32 enum_flags;
|
||||
guint device_index;
|
||||
gboolean can_force_keyframe;
|
||||
|
||||
gboolean (*set_option) (GstMFVideoEnc * mfenc,
|
||||
IMFMediaType * output_type);
|
||||
|
||||
gboolean (*set_src_caps) (GstMFVideoEnc * mfenc,
|
||||
GstVideoCodecState * state,
|
||||
IMFMediaType * output_type);
|
||||
};
|
||||
|
||||
GType gst_mf_video_enc_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_MF_VIDEO_ENC_H__ */
|
305
sys/mediafoundation/gststrmif.h
Normal file
305
sys/mediafoundation/gststrmif.h
Normal file
|
@ -0,0 +1,305 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2020 Seungha Yang <seungha.yang@navercorp.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.
|
||||
*/
|
||||
|
||||
#ifndef __GST_STRMIF_H__
|
||||
#define __GST_STRMIF_H__
|
||||
|
||||
#include <strmif.h>
|
||||
|
||||
/* From strmif.h.
|
||||
* ICodecAPI interface will not be exposed
|
||||
* for the !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) case
|
||||
* but MSDN said the interface should be available on both
|
||||
* desktop and UWP cases */
|
||||
#ifndef __ICodecAPI_INTERFACE_DEFINED__
|
||||
#define __ICodecAPI_INTERFACE_DEFINED__
|
||||
|
||||
/* interface ICodecAPI */
|
||||
/* [unique][uuid][object][local] */
|
||||
|
||||
|
||||
EXTERN_C const IID IID_ICodecAPI;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
|
||||
MIDL_INTERFACE("901db4c7-31ce-41a2-85dc-8fa0bf41b8da")
|
||||
ICodecAPI : public IUnknown
|
||||
{
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE IsSupported(
|
||||
/* [in] */ const GUID *Api) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE IsModifiable(
|
||||
/* [in] */ const GUID *Api) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetParameterRange(
|
||||
/* [in] */ const GUID *Api,
|
||||
/* [annotation][out] */
|
||||
_Out_ VARIANT *ValueMin,
|
||||
/* [annotation][out] */
|
||||
_Out_ VARIANT *ValueMax,
|
||||
/* [annotation][out] */
|
||||
_Out_ VARIANT *SteppingDelta) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetParameterValues(
|
||||
/* [in] */ const GUID *Api,
|
||||
/* [annotation][size_is][size_is][out] */
|
||||
_Outptr_result_buffer_all_(*ValuesCount) VARIANT **Values,
|
||||
/* [annotation][out] */
|
||||
_Out_ ULONG *ValuesCount) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetDefaultValue(
|
||||
/* [in] */ const GUID *Api,
|
||||
/* [annotation][out] */
|
||||
_Out_ VARIANT *Value) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetValue(
|
||||
/* [in] */ const GUID *Api,
|
||||
/* [annotation][out] */
|
||||
_Out_ VARIANT *Value) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetValue(
|
||||
/* [in] */ const GUID *Api,
|
||||
/* [annotation][in] */
|
||||
_In_ VARIANT *Value) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE RegisterForEvent(
|
||||
/* [in] */ const GUID *Api,
|
||||
/* [in] */ LONG_PTR userData) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE UnregisterForEvent(
|
||||
/* [in] */ const GUID *Api) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetAllDefaults( void) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetValueWithNotify(
|
||||
/* [in] */ const GUID *Api,
|
||||
/* [in] */ VARIANT *Value,
|
||||
/* [annotation][size_is][size_is][out] */
|
||||
_Outptr_result_buffer_all_(*ChangedParamCount) GUID **ChangedParam,
|
||||
/* [annotation][out] */
|
||||
_Out_ ULONG *ChangedParamCount) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetAllDefaultsWithNotify(
|
||||
/* [annotation][size_is][size_is][out] */
|
||||
_Outptr_result_buffer_all_(*ChangedParamCount) GUID **ChangedParam,
|
||||
/* [annotation][out] */
|
||||
_Out_ ULONG *ChangedParamCount) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetAllSettings(
|
||||
/* [in] */ IStream *__MIDL__ICodecAPI0000) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetAllSettings(
|
||||
/* [in] */ IStream *__MIDL__ICodecAPI0001) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetAllSettingsWithNotify(
|
||||
IStream *__MIDL__ICodecAPI0002,
|
||||
/* [annotation][size_is][size_is][out] */
|
||||
_Outptr_result_buffer_all_(*ChangedParamCount) GUID **ChangedParam,
|
||||
/* [annotation][out] */
|
||||
_Out_ ULONG *ChangedParamCount) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#else /* C style interface */
|
||||
|
||||
typedef struct ICodecAPIVtbl
|
||||
{
|
||||
BEGIN_INTERFACE
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
|
||||
ICodecAPI * This,
|
||||
/* [in] */ REFIID riid,
|
||||
/* [annotation][iid_is][out] */
|
||||
_COM_Outptr_ void **ppvObject);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE *AddRef )(
|
||||
ICodecAPI * This);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE *Release )(
|
||||
ICodecAPI * This);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *IsSupported )(
|
||||
ICodecAPI * This,
|
||||
/* [in] */ const GUID *Api);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *IsModifiable )(
|
||||
ICodecAPI * This,
|
||||
/* [in] */ const GUID *Api);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetParameterRange )(
|
||||
ICodecAPI * This,
|
||||
/* [in] */ const GUID *Api,
|
||||
/* [annotation][out] */
|
||||
_Out_ VARIANT *ValueMin,
|
||||
/* [annotation][out] */
|
||||
_Out_ VARIANT *ValueMax,
|
||||
/* [annotation][out] */
|
||||
_Out_ VARIANT *SteppingDelta);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetParameterValues )(
|
||||
ICodecAPI * This,
|
||||
/* [in] */ const GUID *Api,
|
||||
/* [annotation][size_is][size_is][out] */
|
||||
_Outptr_result_buffer_all_(*ValuesCount) VARIANT **Values,
|
||||
/* [annotation][out] */
|
||||
_Out_ ULONG *ValuesCount);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetDefaultValue )(
|
||||
ICodecAPI * This,
|
||||
/* [in] */ const GUID *Api,
|
||||
/* [annotation][out] */
|
||||
_Out_ VARIANT *Value);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetValue )(
|
||||
ICodecAPI * This,
|
||||
/* [in] */ const GUID *Api,
|
||||
/* [annotation][out] */
|
||||
_Out_ VARIANT *Value);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *SetValue )(
|
||||
ICodecAPI * This,
|
||||
/* [in] */ const GUID *Api,
|
||||
/* [annotation][in] */
|
||||
_In_ VARIANT *Value);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *RegisterForEvent )(
|
||||
ICodecAPI * This,
|
||||
/* [in] */ const GUID *Api,
|
||||
/* [in] */ LONG_PTR userData);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *UnregisterForEvent )(
|
||||
ICodecAPI * This,
|
||||
/* [in] */ const GUID *Api);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *SetAllDefaults )(
|
||||
ICodecAPI * This);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *SetValueWithNotify )(
|
||||
ICodecAPI * This,
|
||||
/* [in] */ const GUID *Api,
|
||||
/* [in] */ VARIANT *Value,
|
||||
/* [annotation][size_is][size_is][out] */
|
||||
_Outptr_result_buffer_all_(*ChangedParamCount) GUID **ChangedParam,
|
||||
/* [annotation][out] */
|
||||
_Out_ ULONG *ChangedParamCount);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *SetAllDefaultsWithNotify )(
|
||||
ICodecAPI * This,
|
||||
/* [annotation][size_is][size_is][out] */
|
||||
_Outptr_result_buffer_all_(*ChangedParamCount) GUID **ChangedParam,
|
||||
/* [annotation][out] */
|
||||
_Out_ ULONG *ChangedParamCount);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetAllSettings )(
|
||||
ICodecAPI * This,
|
||||
/* [in] */ IStream *__MIDL__ICodecAPI0000);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *SetAllSettings )(
|
||||
ICodecAPI * This,
|
||||
/* [in] */ IStream *__MIDL__ICodecAPI0001);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *SetAllSettingsWithNotify )(
|
||||
ICodecAPI * This,
|
||||
IStream *__MIDL__ICodecAPI0002,
|
||||
/* [annotation][size_is][size_is][out] */
|
||||
_Outptr_result_buffer_all_(*ChangedParamCount) GUID **ChangedParam,
|
||||
/* [annotation][out] */
|
||||
_Out_ ULONG *ChangedParamCount);
|
||||
|
||||
END_INTERFACE
|
||||
} ICodecAPIVtbl;
|
||||
|
||||
interface ICodecAPI
|
||||
{
|
||||
CONST_VTBL struct ICodecAPIVtbl *lpVtbl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifdef COBJMACROS
|
||||
|
||||
|
||||
#define ICodecAPI_QueryInterface(This,riid,ppvObject) \
|
||||
( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
|
||||
|
||||
#define ICodecAPI_AddRef(This) \
|
||||
( (This)->lpVtbl -> AddRef(This) )
|
||||
|
||||
#define ICodecAPI_Release(This) \
|
||||
( (This)->lpVtbl -> Release(This) )
|
||||
|
||||
|
||||
#define ICodecAPI_IsSupported(This,Api) \
|
||||
( (This)->lpVtbl -> IsSupported(This,Api) )
|
||||
|
||||
#define ICodecAPI_IsModifiable(This,Api) \
|
||||
( (This)->lpVtbl -> IsModifiable(This,Api) )
|
||||
|
||||
#define ICodecAPI_GetParameterRange(This,Api,ValueMin,ValueMax,SteppingDelta) \
|
||||
( (This)->lpVtbl -> GetParameterRange(This,Api,ValueMin,ValueMax,SteppingDelta) )
|
||||
|
||||
#define ICodecAPI_GetParameterValues(This,Api,Values,ValuesCount) \
|
||||
( (This)->lpVtbl -> GetParameterValues(This,Api,Values,ValuesCount) )
|
||||
|
||||
#define ICodecAPI_GetDefaultValue(This,Api,Value) \
|
||||
( (This)->lpVtbl -> GetDefaultValue(This,Api,Value) )
|
||||
|
||||
#define ICodecAPI_GetValue(This,Api,Value) \
|
||||
( (This)->lpVtbl -> GetValue(This,Api,Value) )
|
||||
|
||||
#define ICodecAPI_SetValue(This,Api,Value) \
|
||||
( (This)->lpVtbl -> SetValue(This,Api,Value) )
|
||||
|
||||
#define ICodecAPI_RegisterForEvent(This,Api,userData) \
|
||||
( (This)->lpVtbl -> RegisterForEvent(This,Api,userData) )
|
||||
|
||||
#define ICodecAPI_UnregisterForEvent(This,Api) \
|
||||
( (This)->lpVtbl -> UnregisterForEvent(This,Api) )
|
||||
|
||||
#define ICodecAPI_SetAllDefaults(This) \
|
||||
( (This)->lpVtbl -> SetAllDefaults(This) )
|
||||
|
||||
#define ICodecAPI_SetValueWithNotify(This,Api,Value,ChangedParam,ChangedParamCount) \
|
||||
( (This)->lpVtbl -> SetValueWithNotify(This,Api,Value,ChangedParam,ChangedParamCount) )
|
||||
|
||||
#define ICodecAPI_SetAllDefaultsWithNotify(This,ChangedParam,ChangedParamCount) \
|
||||
( (This)->lpVtbl -> SetAllDefaultsWithNotify(This,ChangedParam,ChangedParamCount) )
|
||||
|
||||
#define ICodecAPI_GetAllSettings(This,__MIDL__ICodecAPI0000) \
|
||||
( (This)->lpVtbl -> GetAllSettings(This,__MIDL__ICodecAPI0000) )
|
||||
|
||||
#define ICodecAPI_SetAllSettings(This,__MIDL__ICodecAPI0001) \
|
||||
( (This)->lpVtbl -> SetAllSettings(This,__MIDL__ICodecAPI0001) )
|
||||
|
||||
#define ICodecAPI_SetAllSettingsWithNotify(This,__MIDL__ICodecAPI0002,ChangedParam,ChangedParamCount) \
|
||||
( (This)->lpVtbl -> SetAllSettingsWithNotify(This,__MIDL__ICodecAPI0002,ChangedParam,ChangedParamCount) )
|
||||
|
||||
#endif /* COBJMACROS */
|
||||
|
||||
|
||||
#endif /* C style interface */
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* __ICodecAPI_INTERFACE_DEFINED__ */
|
||||
|
||||
#endif /* __GST_STRMIF_H__ */
|
|
@ -1,6 +1,12 @@
|
|||
mf_sources = [
|
||||
'plugin.c',
|
||||
'gstmfutils.cpp',
|
||||
'gstmftransform.cpp',
|
||||
'gstmfvideoenc.cpp',
|
||||
'gstmfh264enc.cpp',
|
||||
]
|
||||
|
||||
mf_desktop_sources = [
|
||||
'gstmfvideosrc.c',
|
||||
'gstmfsourceobject.c',
|
||||
'gstmfsourcereader.cpp',
|
||||
|
@ -13,6 +19,7 @@ mf_header_deps = [
|
|||
'mferror.h',
|
||||
'strmif.h',
|
||||
'mfobjects.h',
|
||||
'codecapi.h',
|
||||
]
|
||||
|
||||
winapi_desktop = false
|
||||
|
@ -70,16 +77,13 @@ winapi_desktop = cxx.compiles('''#include <winapifamily.h>
|
|||
#endif''',
|
||||
dependencies: mf_lib_deps,
|
||||
name: 'checking if building for Win32')
|
||||
if not winapi_desktop
|
||||
if mf_option.enabled()
|
||||
error('The mediafoundation plugin was enabled explicitly, build target is not desktop app.')
|
||||
endif
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
have_capture_engine = cc.has_header('mfcaptureengine.h')
|
||||
if have_capture_engine
|
||||
mf_sources += ['gstmfcaptureengine.cpp']
|
||||
if winapi_desktop
|
||||
mf_sources += mf_desktop_sources
|
||||
have_capture_engine = cc.has_header('mfcaptureengine.h')
|
||||
if have_capture_engine
|
||||
mf_sources += ['gstmfcaptureengine.cpp']
|
||||
endif
|
||||
endif
|
||||
|
||||
mf_config.set10('HAVE_CAPTURE_ENGINE', have_capture_engine)
|
||||
|
@ -94,7 +98,7 @@ gstmediafoundation = library('gstmediafoundation',
|
|||
c_args : gst_plugins_bad_args + extra_c_args,
|
||||
cpp_args : gst_plugins_bad_args,
|
||||
include_directories : [configinc],
|
||||
dependencies : [gstbase_dep, gstvideo_dep] + mf_lib_deps,
|
||||
dependencies : [gstbase_dep, gstvideo_dep, gstpbutils_dep] + mf_lib_deps,
|
||||
install : true,
|
||||
install_dir : plugins_install_dir,
|
||||
)
|
||||
|
|
|
@ -22,16 +22,29 @@
|
|||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <winapifamily.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
#include "gstmfvideosrc.h"
|
||||
#endif
|
||||
#include "gstmfutils.h"
|
||||
#include "gstmfh264enc.h"
|
||||
|
||||
GST_DEBUG_CATEGORY (gst_mf_debug);
|
||||
GST_DEBUG_CATEGORY (gst_mf_utils_debug);
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
GST_DEBUG_CATEGORY (gst_mf_source_object_debug);
|
||||
#endif
|
||||
GST_DEBUG_CATEGORY (gst_mf_transform_debug);
|
||||
|
||||
#define GST_CAT_DEFAULT gst_mf_debug
|
||||
|
||||
/* NOTE: If you want to use this plugin in UWP app, don't try to load/initialize
|
||||
* this plugin on UI thread, since the UI thread would be STA Thread
|
||||
* but this plugin will be initialized with COINIT_MULTITHREADED parameter.
|
||||
* This rule can be applied over all GStreamer plugins which are involved with
|
||||
* COM libraries */
|
||||
static gboolean
|
||||
plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
|
@ -40,17 +53,24 @@ plugin_init (GstPlugin * plugin)
|
|||
GST_DEBUG_CATEGORY_INIT (gst_mf_debug, "mf", 0, "media foundation");
|
||||
GST_DEBUG_CATEGORY_INIT (gst_mf_utils_debug,
|
||||
"mfutils", 0, "media foundation utility functions");
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
GST_DEBUG_CATEGORY_INIT (gst_mf_source_object_debug,
|
||||
"mfsourceobject", 0, "mfsourceobject");
|
||||
#endif
|
||||
GST_DEBUG_CATEGORY_INIT (gst_mf_transform_debug,
|
||||
"mftransform", 0, "mftransform");
|
||||
|
||||
hr = MFStartup (MF_VERSION, MFSTARTUP_NOSOCKET);
|
||||
if (!gst_mf_result (hr)) {
|
||||
GST_WARNING ("MFStartup failure, hr: 0x%x", hr);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
gst_element_register (plugin,
|
||||
"mfvideosrc", GST_RANK_SECONDARY, GST_TYPE_MF_VIDEO_SRC);
|
||||
#endif
|
||||
|
||||
gst_mf_h264_enc_plugin_init (plugin, GST_RANK_SECONDARY);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue