2 * GStreamer transform backend
4 * Copyright 2022 RĂ©mi Bernon for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #include <gst/video/video.h>
33 #include <gst/audio/audio.h>
36 #define WIN32_NO_STATUS
40 #include "unix_private.h"
42 #define GST_SAMPLE_FLAG_WG_CAPS_CHANGED (GST_MINI_OBJECT_FLAG_LAST << 0)
46 struct wg_transform_attrs attrs
;
48 GstElement
*container
;
49 GstAllocator
*allocator
;
50 GstPad
*my_src
, *my_sink
;
52 GstQuery
*drain_query
;
54 GstAtomicQueue
*input_queue
;
56 bool input_is_flipped
;
57 GstElement
*video_flip
;
59 struct wg_format output_format
;
60 GstAtomicQueue
*output_queue
;
61 GstSample
*output_sample
;
62 bool output_caps_changed
;
66 static struct wg_transform
*get_transform(wg_transform_t trans
)
68 return (struct wg_transform
*)(ULONG_PTR
)trans
;
71 static void align_video_info_planes(gsize plane_align
, GstVideoInfo
*info
, GstVideoAlignment
*align
)
73 gst_video_alignment_reset(align
);
75 align
->padding_right
= ((plane_align
+ 1) - (info
->width
& plane_align
)) & plane_align
;
76 align
->padding_bottom
= ((plane_align
+ 1) - (info
->height
& plane_align
)) & plane_align
;
77 align
->stride_align
[0] = plane_align
;
78 align
->stride_align
[1] = plane_align
;
79 align
->stride_align
[2] = plane_align
;
80 align
->stride_align
[3] = plane_align
;
82 gst_video_info_align(info
, align
);
85 static GstFlowReturn
transform_sink_chain_cb(GstPad
*pad
, GstObject
*parent
, GstBuffer
*buffer
)
87 struct wg_transform
*transform
= gst_pad_get_element_private(pad
);
90 GST_LOG("transform %p, buffer %p.", transform
, buffer
);
92 if (!(sample
= gst_sample_new(buffer
, transform
->output_caps
, NULL
, NULL
)))
94 GST_ERROR("Failed to allocate transform %p output sample.", transform
);
95 gst_buffer_unref(buffer
);
96 return GST_FLOW_ERROR
;
99 if (transform
->output_caps_changed
)
100 GST_MINI_OBJECT_FLAG_SET(sample
, GST_SAMPLE_FLAG_WG_CAPS_CHANGED
);
101 transform
->output_caps_changed
= false;
103 gst_atomic_queue_push(transform
->output_queue
, sample
);
104 gst_buffer_unref(buffer
);
108 static gboolean
transform_src_query_latency(struct wg_transform
*transform
, GstQuery
*query
)
110 GST_LOG("transform %p, query %p", transform
, query
);
111 gst_query_set_latency(query
, transform
->attrs
.low_latency
, 0, 0);
115 static gboolean
transform_src_query_cb(GstPad
*pad
, GstObject
*parent
, GstQuery
*query
)
117 struct wg_transform
*transform
= gst_pad_get_element_private(pad
);
121 case GST_QUERY_LATENCY
:
122 return transform_src_query_latency(transform
, query
);
124 return gst_pad_query_default(pad
, parent
, query
);
128 static gboolean
transform_sink_query_cb(GstPad
*pad
, GstObject
*parent
, GstQuery
*query
)
130 struct wg_transform
*transform
= gst_pad_get_element_private(pad
);
132 GST_LOG("transform %p, type \"%s\".", transform
, gst_query_type_get_name(query
->type
));
136 case GST_QUERY_ALLOCATION
:
138 gsize plane_align
= transform
->attrs
.output_plane_align
;
139 GstStructure
*config
, *params
;
140 GstVideoAlignment align
;
146 gst_query_parse_allocation(query
, &caps
, &needs_pool
);
147 if (stream_type_from_caps(caps
) != GST_STREAM_TYPE_VIDEO
|| !needs_pool
)
150 if (!gst_video_info_from_caps(&info
, caps
)
151 || !(pool
= gst_video_buffer_pool_new()))
154 align_video_info_planes(plane_align
, &info
, &align
);
156 if ((params
= gst_structure_new("video-meta",
157 "padding-top", G_TYPE_UINT
, align
.padding_top
,
158 "padding-bottom", G_TYPE_UINT
, align
.padding_bottom
,
159 "padding-left", G_TYPE_UINT
, align
.padding_left
,
160 "padding-right", G_TYPE_UINT
, align
.padding_right
,
163 gst_query_add_allocation_meta(query
, GST_VIDEO_META_API_TYPE
, params
);
164 gst_structure_free(params
);
167 if (!(config
= gst_buffer_pool_get_config(pool
)))
168 GST_ERROR("Failed to get pool %p config.", pool
);
171 gst_buffer_pool_config_add_option(config
, GST_BUFFER_POOL_OPTION_VIDEO_META
);
172 gst_buffer_pool_config_add_option(config
, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT
);
173 gst_buffer_pool_config_set_video_alignment(config
, &align
);
175 gst_buffer_pool_config_set_params(config
, caps
,
177 gst_buffer_pool_config_set_allocator(config
, transform
->allocator
, NULL
);
178 if (!gst_buffer_pool_set_config(pool
, config
))
179 GST_ERROR("Failed to set pool %p config.", pool
);
182 /* Prevent pool reconfiguration, we don't want another alignment. */
183 if (!gst_buffer_pool_set_active(pool
, true))
184 GST_ERROR("Pool %p failed to activate.", pool
);
186 gst_query_add_allocation_pool(query
, pool
, info
.size
, 0, 0);
187 gst_query_add_allocation_param(query
, transform
->allocator
, NULL
);
189 GST_INFO("Proposing pool %p, buffer size %#zx, allocator %p, for query %p.",
190 pool
, info
.size
, transform
->allocator
, query
);
192 g_object_unref(pool
);
198 GstCaps
*caps
, *filter
, *temp
;
200 gst_query_parse_caps(query
, &filter
);
201 if (!(caps
= wg_format_to_caps(&transform
->output_format
)))
206 temp
= gst_caps_intersect(caps
, filter
);
207 gst_caps_unref(caps
);
211 GST_INFO("Returning caps %" GST_PTR_FORMAT
, caps
);
213 gst_query_set_caps_result(query
, caps
);
214 gst_caps_unref(caps
);
219 GST_WARNING("Ignoring \"%s\" query.", gst_query_type_get_name(query
->type
));
223 return gst_pad_query_default(pad
, parent
, query
);
226 static gboolean
transform_sink_event_cb(GstPad
*pad
, GstObject
*parent
, GstEvent
*event
)
228 struct wg_transform
*transform
= gst_pad_get_element_private(pad
);
230 GST_LOG("transform %p, type \"%s\".", transform
, GST_EVENT_TYPE_NAME(event
));
238 gst_event_parse_caps(event
, &caps
);
240 transform
->output_caps_changed
= transform
->output_caps_changed
241 || !gst_caps_is_always_compatible(transform
->output_caps
, caps
);
243 gst_caps_unref(transform
->output_caps
);
244 transform
->output_caps
= gst_caps_ref(caps
);
248 GST_WARNING("Ignoring \"%s\" event.", GST_EVENT_TYPE_NAME(event
));
252 gst_event_unref(event
);
256 NTSTATUS
wg_transform_destroy(void *args
)
258 struct wg_transform
*transform
= get_transform(*(wg_transform_t
*)args
);
262 while ((buffer
= gst_atomic_queue_pop(transform
->input_queue
)))
263 gst_buffer_unref(buffer
);
264 gst_atomic_queue_unref(transform
->input_queue
);
266 gst_element_set_state(transform
->container
, GST_STATE_NULL
);
268 if (transform
->output_sample
)
269 gst_sample_unref(transform
->output_sample
);
270 while ((sample
= gst_atomic_queue_pop(transform
->output_queue
)))
271 gst_sample_unref(sample
);
273 wg_allocator_destroy(transform
->allocator
);
274 g_object_unref(transform
->container
);
275 g_object_unref(transform
->my_sink
);
276 g_object_unref(transform
->my_src
);
277 gst_query_unref(transform
->drain_query
);
278 gst_caps_unref(transform
->output_caps
);
279 gst_atomic_queue_unref(transform
->output_queue
);
282 return STATUS_SUCCESS
;
285 static bool wg_format_video_is_flipped(const struct wg_format
*format
)
287 return format
->major_type
== WG_MAJOR_TYPE_VIDEO
&& (format
->u
.video
.height
< 0);
290 NTSTATUS
wg_transform_create(void *args
)
292 struct wg_transform_create_params
*params
= args
;
293 struct wg_format output_format
= *params
->output_format
;
294 struct wg_format input_format
= *params
->input_format
;
295 GstElement
*first
= NULL
, *last
= NULL
, *element
;
296 GstCaps
*raw_caps
= NULL
, *src_caps
= NULL
;
297 NTSTATUS status
= STATUS_UNSUCCESSFUL
;
298 GstPadTemplate
*template = NULL
;
299 struct wg_transform
*transform
;
300 const gchar
*media_type
;
303 if (!(transform
= calloc(1, sizeof(*transform
))))
304 return STATUS_NO_MEMORY
;
305 if (!(transform
->container
= gst_bin_new("wg_transform")))
307 if (!(transform
->input_queue
= gst_atomic_queue_new(8)))
309 if (!(transform
->output_queue
= gst_atomic_queue_new(8)))
311 if (!(transform
->drain_query
= gst_query_new_drain()))
313 if (!(transform
->allocator
= wg_allocator_create()))
315 transform
->attrs
= *params
->attrs
;
316 transform
->output_format
= output_format
;
318 if (!(src_caps
= wg_format_to_caps(&input_format
)))
320 if (!(template = gst_pad_template_new("src", GST_PAD_SRC
, GST_PAD_ALWAYS
, src_caps
)))
322 transform
->my_src
= gst_pad_new_from_template(template, "src");
323 g_object_unref(template);
324 if (!transform
->my_src
)
327 gst_pad_set_element_private(transform
->my_src
, transform
);
328 gst_pad_set_query_function(transform
->my_src
, transform_src_query_cb
);
330 if (!(transform
->output_caps
= wg_format_to_caps(&output_format
)))
332 if (!(template = gst_pad_template_new("sink", GST_PAD_SINK
, GST_PAD_ALWAYS
, transform
->output_caps
)))
334 transform
->my_sink
= gst_pad_new_from_template(template, "sink");
335 g_object_unref(template);
336 if (!transform
->my_sink
)
339 gst_pad_set_element_private(transform
->my_sink
, transform
);
340 gst_pad_set_event_function(transform
->my_sink
, transform_sink_event_cb
);
341 gst_pad_set_query_function(transform
->my_sink
, transform_sink_query_cb
);
342 gst_pad_set_chain_function(transform
->my_sink
, transform_sink_chain_cb
);
344 /* Since we append conversion elements, we don't want to filter decoders
345 * based on the actual output caps now. Matching decoders with the
346 * raw output media type should be enough.
348 media_type
= gst_structure_get_name(gst_caps_get_structure(transform
->output_caps
, 0));
349 if (!(raw_caps
= gst_caps_new_empty_simple(media_type
)))
352 switch (input_format
.major_type
)
354 case WG_MAJOR_TYPE_VIDEO_H264
:
355 case WG_MAJOR_TYPE_AUDIO_MPEG1
:
356 case WG_MAJOR_TYPE_AUDIO_MPEG4
:
357 case WG_MAJOR_TYPE_AUDIO_WMA
:
358 case WG_MAJOR_TYPE_VIDEO_CINEPAK
:
359 case WG_MAJOR_TYPE_VIDEO_INDEO
:
360 case WG_MAJOR_TYPE_VIDEO_WMV
:
361 case WG_MAJOR_TYPE_VIDEO_MPEG1
:
362 if (!(element
= find_element(GST_ELEMENT_FACTORY_TYPE_DECODER
, src_caps
, raw_caps
))
363 || !append_element(transform
->container
, element
, &first
, &last
))
365 gst_caps_unref(raw_caps
);
370 case WG_MAJOR_TYPE_AUDIO
:
371 case WG_MAJOR_TYPE_VIDEO
:
373 case WG_MAJOR_TYPE_UNKNOWN
:
374 GST_FIXME("Format %u not implemented!", input_format
.major_type
);
375 gst_caps_unref(raw_caps
);
379 gst_caps_unref(raw_caps
);
381 switch (output_format
.major_type
)
383 case WG_MAJOR_TYPE_AUDIO
:
384 /* The MF audio decoder transforms allow decoding to various formats
385 * as well as resampling the audio at the same time, whereas
386 * GStreamer decoder plugins usually only support decoding to a
387 * single format and at the original rate.
389 * The WMA decoder transform also has output samples interleaved on
390 * Windows, whereas GStreamer avdec_wmav2 output uses
391 * non-interleaved format.
393 if (!(element
= create_element("audioconvert", "base"))
394 || !append_element(transform
->container
, element
, &first
, &last
))
396 if (!(element
= create_element("audioresample", "base"))
397 || !append_element(transform
->container
, element
, &first
, &last
))
401 case WG_MAJOR_TYPE_VIDEO
:
402 if (!(element
= create_element("videoconvert", "base"))
403 || !append_element(transform
->container
, element
, &first
, &last
))
405 if (!(transform
->video_flip
= create_element("videoflip", "base"))
406 || !append_element(transform
->container
, transform
->video_flip
, &first
, &last
))
408 transform
->input_is_flipped
= wg_format_video_is_flipped(&input_format
);
409 if (transform
->input_is_flipped
!= wg_format_video_is_flipped(&output_format
))
410 gst_util_set_object_arg(G_OBJECT(transform
->video_flip
), "method", "vertical-flip");
411 if (!(element
= create_element("videoconvert", "base"))
412 || !append_element(transform
->container
, element
, &first
, &last
))
414 /* Let GStreamer choose a default number of threads. */
415 gst_util_set_object_arg(G_OBJECT(element
), "n-threads", "0");
418 case WG_MAJOR_TYPE_UNKNOWN
:
419 case WG_MAJOR_TYPE_AUDIO_MPEG1
:
420 case WG_MAJOR_TYPE_AUDIO_MPEG4
:
421 case WG_MAJOR_TYPE_AUDIO_WMA
:
422 case WG_MAJOR_TYPE_VIDEO_CINEPAK
:
423 case WG_MAJOR_TYPE_VIDEO_H264
:
424 case WG_MAJOR_TYPE_VIDEO_INDEO
:
425 case WG_MAJOR_TYPE_VIDEO_WMV
:
426 case WG_MAJOR_TYPE_VIDEO_MPEG1
:
427 GST_FIXME("Format %u not implemented!", output_format
.major_type
);
431 if (!link_src_to_element(transform
->my_src
, first
))
433 if (!link_element_to_sink(last
, transform
->my_sink
))
435 if (!gst_pad_set_active(transform
->my_sink
, 1))
437 if (!gst_pad_set_active(transform
->my_src
, 1))
440 gst_element_set_state(transform
->container
, GST_STATE_PAUSED
);
441 if (!gst_element_get_state(transform
->container
, NULL
, NULL
, -1))
444 if (!(event
= gst_event_new_stream_start("stream"))
445 || !push_event(transform
->my_src
, event
))
447 if (!(event
= gst_event_new_caps(src_caps
))
448 || !push_event(transform
->my_src
, event
))
451 /* We need to use GST_FORMAT_TIME here because it's the only format
452 * some elements such avdec_wmav2 correctly support. */
453 gst_segment_init(&transform
->segment
, GST_FORMAT_TIME
);
454 transform
->segment
.start
= 0;
455 transform
->segment
.stop
= -1;
456 if (!(event
= gst_event_new_segment(&transform
->segment
))
457 || !push_event(transform
->my_src
, event
))
460 gst_caps_unref(src_caps
);
462 GST_INFO("Created winegstreamer transform %p.", transform
);
463 params
->transform
= (wg_transform_t
)(ULONG_PTR
)transform
;
464 return STATUS_SUCCESS
;
467 if (transform
->my_sink
)
468 gst_object_unref(transform
->my_sink
);
469 if (transform
->output_caps
)
470 gst_caps_unref(transform
->output_caps
);
471 if (transform
->my_src
)
472 gst_object_unref(transform
->my_src
);
474 gst_caps_unref(src_caps
);
475 if (transform
->allocator
)
476 wg_allocator_destroy(transform
->allocator
);
477 if (transform
->drain_query
)
478 gst_query_unref(transform
->drain_query
);
479 if (transform
->output_queue
)
480 gst_atomic_queue_unref(transform
->output_queue
);
481 if (transform
->input_queue
)
482 gst_atomic_queue_unref(transform
->input_queue
);
483 if (transform
->container
)
485 gst_element_set_state(transform
->container
, GST_STATE_NULL
);
486 gst_object_unref(transform
->container
);
489 GST_ERROR("Failed to create winegstreamer transform.");
493 NTSTATUS
wg_transform_set_output_format(void *args
)
495 struct wg_transform_set_output_format_params
*params
= args
;
496 struct wg_transform
*transform
= get_transform(params
->transform
);
497 const struct wg_format
*format
= params
->format
;
501 if (!(caps
= wg_format_to_caps(format
)))
503 GST_ERROR("Failed to convert format %p to caps.", format
);
504 return STATUS_UNSUCCESSFUL
;
506 transform
->output_format
= *format
;
508 if (gst_caps_is_always_compatible(transform
->output_caps
, caps
))
510 gst_caps_unref(caps
);
511 return STATUS_SUCCESS
;
514 if (!gst_pad_peer_query(transform
->my_src
, transform
->drain_query
))
516 GST_ERROR("Failed to drain transform %p.", transform
);
517 return STATUS_UNSUCCESSFUL
;
520 gst_caps_unref(transform
->output_caps
);
521 transform
->output_caps
= caps
;
523 if (transform
->video_flip
)
526 if (transform
->input_is_flipped
!= wg_format_video_is_flipped(format
))
527 value
= "vertical-flip";
530 gst_util_set_object_arg(G_OBJECT(transform
->video_flip
), "method", value
);
532 if (!push_event(transform
->my_sink
, gst_event_new_reconfigure()))
534 GST_ERROR("Failed to reconfigure transform %p.", transform
);
535 return STATUS_UNSUCCESSFUL
;
538 GST_INFO("Configured new caps %" GST_PTR_FORMAT
".", caps
);
540 /* Ideally and to be fully compatible with native transform, the queued
541 * output buffers will need to be converted to the new output format and
544 if (transform
->output_sample
)
545 gst_sample_unref(transform
->output_sample
);
546 while ((sample
= gst_atomic_queue_pop(transform
->output_queue
)))
547 gst_sample_unref(sample
);
548 transform
->output_sample
= NULL
;
550 return STATUS_SUCCESS
;
553 static void wg_sample_free_notify(void *arg
)
555 struct wg_sample
*sample
= arg
;
556 GST_DEBUG("Releasing wg_sample %p", sample
);
557 InterlockedDecrement(&sample
->refcount
);
560 NTSTATUS
wg_transform_push_data(void *args
)
562 struct wg_transform_push_data_params
*params
= args
;
563 struct wg_transform
*transform
= get_transform(params
->transform
);
564 struct wg_sample
*sample
= params
->sample
;
568 length
= gst_atomic_queue_length(transform
->input_queue
);
569 if (length
>= transform
->attrs
.input_queue_length
+ 1)
571 GST_INFO("Refusing %u bytes, %u buffers already queued", sample
->size
, length
);
572 params
->result
= MF_E_NOTACCEPTING
;
573 return STATUS_SUCCESS
;
576 if (!(buffer
= gst_buffer_new_wrapped_full(GST_MEMORY_FLAG_READONLY
, wg_sample_data(sample
), sample
->max_size
,
577 0, sample
->size
, sample
, wg_sample_free_notify
)))
579 GST_ERROR("Failed to allocate input buffer");
580 return STATUS_NO_MEMORY
;
584 InterlockedIncrement(&sample
->refcount
);
585 GST_INFO("Wrapped %u/%u bytes from sample %p to buffer %p", sample
->size
, sample
->max_size
, sample
, buffer
);
588 if (sample
->flags
& WG_SAMPLE_FLAG_HAS_PTS
)
589 GST_BUFFER_PTS(buffer
) = sample
->pts
* 100;
590 if (sample
->flags
& WG_SAMPLE_FLAG_HAS_DURATION
)
591 GST_BUFFER_DURATION(buffer
) = sample
->duration
* 100;
592 if (!(sample
->flags
& WG_SAMPLE_FLAG_SYNC_POINT
))
593 GST_BUFFER_FLAG_SET(buffer
, GST_BUFFER_FLAG_DELTA_UNIT
);
594 if (sample
->flags
& WG_SAMPLE_FLAG_DISCONTINUITY
)
595 GST_BUFFER_FLAG_SET(buffer
, GST_BUFFER_FLAG_DISCONT
);
596 gst_atomic_queue_push(transform
->input_queue
, buffer
);
598 params
->result
= S_OK
;
599 return STATUS_SUCCESS
;
602 static NTSTATUS
copy_video_buffer(GstBuffer
*buffer
, GstCaps
*caps
, gsize plane_align
,
603 struct wg_sample
*sample
, gsize
*total_size
)
605 NTSTATUS status
= STATUS_UNSUCCESSFUL
;
606 GstVideoFrame src_frame
, dst_frame
;
607 GstVideoInfo src_info
, dst_info
;
608 GstVideoAlignment align
;
609 GstBuffer
*dst_buffer
;
611 if (!gst_video_info_from_caps(&src_info
, caps
))
613 GST_ERROR("Failed to get video info from caps.");
614 return STATUS_UNSUCCESSFUL
;
618 align_video_info_planes(plane_align
, &dst_info
, &align
);
620 if (sample
->max_size
< dst_info
.size
)
622 GST_ERROR("Output buffer is too small.");
623 return STATUS_BUFFER_TOO_SMALL
;
626 if (!(dst_buffer
= gst_buffer_new_wrapped_full(0, wg_sample_data(sample
), sample
->max_size
,
627 0, sample
->max_size
, 0, NULL
)))
629 GST_ERROR("Failed to wrap wg_sample into GstBuffer");
630 return STATUS_UNSUCCESSFUL
;
632 gst_buffer_set_size(dst_buffer
, dst_info
.size
);
633 *total_size
= sample
->size
= dst_info
.size
;
635 if (!gst_video_frame_map(&src_frame
, &src_info
, buffer
, GST_MAP_READ
))
636 GST_ERROR("Failed to map source frame.");
639 if (!gst_video_frame_map(&dst_frame
, &dst_info
, dst_buffer
, GST_MAP_WRITE
))
640 GST_ERROR("Failed to map destination frame.");
643 if (gst_video_frame_copy(&dst_frame
, &src_frame
))
644 status
= STATUS_SUCCESS
;
646 GST_ERROR("Failed to copy video frame.");
647 gst_video_frame_unmap(&dst_frame
);
649 gst_video_frame_unmap(&src_frame
);
652 gst_buffer_unref(dst_buffer
);
656 static NTSTATUS
copy_buffer(GstBuffer
*buffer
, GstCaps
*caps
, struct wg_sample
*sample
,
661 if (!gst_buffer_map(buffer
, &info
, GST_MAP_READ
))
662 return STATUS_UNSUCCESSFUL
;
664 if (sample
->max_size
>= info
.size
)
665 sample
->size
= info
.size
;
668 sample
->flags
|= WG_SAMPLE_FLAG_INCOMPLETE
;
669 sample
->size
= sample
->max_size
;
672 memcpy(wg_sample_data(sample
), info
.data
, sample
->size
);
673 gst_buffer_unmap(buffer
, &info
);
675 if (sample
->flags
& WG_SAMPLE_FLAG_INCOMPLETE
)
676 gst_buffer_resize(buffer
, sample
->size
, -1);
678 *total_size
= info
.size
;
679 return STATUS_SUCCESS
;
682 static NTSTATUS
read_transform_output_data(GstBuffer
*buffer
, GstCaps
*caps
, gsize plane_align
,
683 struct wg_sample
*sample
)
690 if (!gst_buffer_map(buffer
, &info
, GST_MAP_READ
))
692 GST_ERROR("Failed to map buffer %p", buffer
);
694 return STATUS_UNSUCCESSFUL
;
696 needs_copy
= info
.data
!= wg_sample_data(sample
);
697 total_size
= sample
->size
= info
.size
;
698 gst_buffer_unmap(buffer
, &info
);
701 status
= STATUS_SUCCESS
;
702 else if (stream_type_from_caps(caps
) == GST_STREAM_TYPE_VIDEO
)
703 status
= copy_video_buffer(buffer
, caps
, plane_align
, sample
, &total_size
);
705 status
= copy_buffer(buffer
, caps
, sample
, &total_size
);
709 GST_ERROR("Failed to copy buffer %p", buffer
);
714 if (GST_BUFFER_PTS_IS_VALID(buffer
))
716 sample
->flags
|= WG_SAMPLE_FLAG_HAS_PTS
;
717 sample
->pts
= GST_BUFFER_PTS(buffer
) / 100;
719 if (GST_BUFFER_DURATION_IS_VALID(buffer
))
721 GstClockTime duration
= GST_BUFFER_DURATION(buffer
) / 100;
723 duration
= (duration
* sample
->size
) / total_size
;
724 GST_BUFFER_DURATION(buffer
) -= duration
* 100;
725 if (GST_BUFFER_PTS_IS_VALID(buffer
))
726 GST_BUFFER_PTS(buffer
) += duration
* 100;
728 sample
->flags
|= WG_SAMPLE_FLAG_HAS_DURATION
;
729 sample
->duration
= duration
;
731 if (!GST_BUFFER_FLAG_IS_SET(buffer
, GST_BUFFER_FLAG_DELTA_UNIT
))
732 sample
->flags
|= WG_SAMPLE_FLAG_SYNC_POINT
;
733 if (GST_BUFFER_FLAG_IS_SET(buffer
, GST_BUFFER_FLAG_DISCONT
))
734 sample
->flags
|= WG_SAMPLE_FLAG_DISCONTINUITY
;
738 if (stream_type_from_caps(caps
) == GST_STREAM_TYPE_VIDEO
)
739 GST_WARNING("Copied %u bytes, sample %p, flags %#x", sample
->size
, sample
, sample
->flags
);
741 GST_INFO("Copied %u bytes, sample %p, flags %#x", sample
->size
, sample
, sample
->flags
);
743 else if (sample
->flags
& WG_SAMPLE_FLAG_INCOMPLETE
)
744 GST_ERROR("Partial read %u bytes, sample %p, flags %#x", sample
->size
, sample
, sample
->flags
);
746 GST_INFO("Read %u bytes, sample %p, flags %#x", sample
->size
, sample
, sample
->flags
);
748 return STATUS_SUCCESS
;
751 static bool get_transform_output(struct wg_transform
*transform
, struct wg_sample
*sample
)
753 GstBuffer
*input_buffer
;
756 wg_allocator_provide_sample(transform
->allocator
, sample
);
758 while (!(transform
->output_sample
= gst_atomic_queue_pop(transform
->output_queue
))
759 && (input_buffer
= gst_atomic_queue_pop(transform
->input_queue
)))
761 if ((ret
= gst_pad_push(transform
->my_src
, input_buffer
)))
762 GST_WARNING("Failed to push transform input, error %d", ret
);
765 /* Remove the sample so the allocator cannot use it */
766 wg_allocator_provide_sample(transform
->allocator
, NULL
);
768 return !!transform
->output_sample
;
771 NTSTATUS
wg_transform_read_data(void *args
)
773 struct wg_transform_read_data_params
*params
= args
;
774 struct wg_transform
*transform
= get_transform(params
->transform
);
775 struct wg_sample
*sample
= params
->sample
;
776 struct wg_format
*format
= params
->format
;
777 GstBuffer
*output_buffer
;
778 GstCaps
*output_caps
;
782 if (!transform
->output_sample
&& !get_transform_output(transform
, sample
))
785 params
->result
= MF_E_TRANSFORM_NEED_MORE_INPUT
;
786 GST_INFO("Cannot read %u bytes, no output available", sample
->max_size
);
787 wg_allocator_release_sample(transform
->allocator
, sample
, false);
788 return STATUS_SUCCESS
;
791 output_buffer
= gst_sample_get_buffer(transform
->output_sample
);
792 output_caps
= gst_sample_get_caps(transform
->output_sample
);
794 if (GST_MINI_OBJECT_FLAG_IS_SET(transform
->output_sample
, GST_SAMPLE_FLAG_WG_CAPS_CHANGED
))
796 GST_MINI_OBJECT_FLAG_UNSET(transform
->output_sample
, GST_SAMPLE_FLAG_WG_CAPS_CHANGED
);
800 gsize plane_align
= transform
->attrs
.output_plane_align
;
801 GstVideoAlignment align
;
804 wg_format_from_caps(format
, output_caps
);
806 if (format
->major_type
== WG_MAJOR_TYPE_VIDEO
807 && gst_video_info_from_caps(&info
, output_caps
))
809 align_video_info_planes(plane_align
, &info
, &align
);
811 GST_INFO("Returning video alignment left %u, top %u, right %u, bottom %u.", align
.padding_left
,
812 align
.padding_top
, align
.padding_right
, align
.padding_bottom
);
814 format
->u
.video
.padding
.left
= align
.padding_left
;
815 format
->u
.video
.width
+= format
->u
.video
.padding
.left
;
816 format
->u
.video
.padding
.right
= align
.padding_right
;
817 format
->u
.video
.width
+= format
->u
.video
.padding
.right
;
818 format
->u
.video
.padding
.top
= align
.padding_top
;
819 format
->u
.video
.height
+= format
->u
.video
.padding
.top
;
820 format
->u
.video
.padding
.bottom
= align
.padding_bottom
;
821 format
->u
.video
.height
+= format
->u
.video
.padding
.bottom
;
825 params
->result
= MF_E_TRANSFORM_STREAM_CHANGE
;
826 GST_INFO("Format changed detected, returning no output");
827 wg_allocator_release_sample(transform
->allocator
, sample
, false);
828 return STATUS_SUCCESS
;
831 if ((status
= read_transform_output_data(output_buffer
, output_caps
,
832 transform
->attrs
.output_plane_align
, sample
)))
834 wg_allocator_release_sample(transform
->allocator
, sample
, false);
838 if (sample
->flags
& WG_SAMPLE_FLAG_INCOMPLETE
)
839 discard_data
= false;
842 /* Taint the buffer memory to make sure it cannot be reused by the buffer pool,
843 * for the pool to always requests new memory from the allocator, and so we can
844 * then always provide output sample memory to achieve zero-copy.
846 * However, some decoder keep a reference on the buffer they passed downstream,
847 * to re-use it later. In this case, it will not be possible to do zero-copy,
848 * and we should copy the data back to the buffer and leave it unchanged.
850 * Some other plugins make assumptions that the returned buffer will always have
851 * at least one memory attached, we cannot just remove it and need to replace the
854 if ((discard_data
= gst_buffer_is_writable(output_buffer
)))
855 gst_buffer_replace_all_memory(output_buffer
, gst_allocator_alloc(NULL
, 0, NULL
));
857 gst_sample_unref(transform
->output_sample
);
858 transform
->output_sample
= NULL
;
861 params
->result
= S_OK
;
862 wg_allocator_release_sample(transform
->allocator
, sample
, discard_data
);
863 return STATUS_SUCCESS
;
866 NTSTATUS
wg_transform_get_status(void *args
)
868 struct wg_transform_get_status_params
*params
= args
;
869 struct wg_transform
*transform
= get_transform(params
->transform
);
871 params
->accepts_input
= gst_atomic_queue_length(transform
->input_queue
) < transform
->attrs
.input_queue_length
+ 1;
872 return STATUS_SUCCESS
;
875 NTSTATUS
wg_transform_drain(void *args
)
877 struct wg_transform
*transform
= get_transform(*(wg_transform_t
*)args
);
878 GstBuffer
*input_buffer
;
882 GST_LOG("transform %p", transform
);
884 while ((input_buffer
= gst_atomic_queue_pop(transform
->input_queue
)))
886 if ((ret
= gst_pad_push(transform
->my_src
, input_buffer
)))
887 GST_WARNING("Failed to push transform input, error %d", ret
);
890 if (!(event
= gst_event_new_segment_done(GST_FORMAT_TIME
, -1))
891 || !push_event(transform
->my_src
, event
))
893 if (!(event
= gst_event_new_eos())
894 || !push_event(transform
->my_src
, event
))
896 if (!(event
= gst_event_new_stream_start("stream"))
897 || !push_event(transform
->my_src
, event
))
899 if (!(event
= gst_event_new_segment(&transform
->segment
))
900 || !push_event(transform
->my_src
, event
))
903 return STATUS_SUCCESS
;
906 GST_ERROR("Failed to drain transform %p.", transform
);
907 return STATUS_UNSUCCESSFUL
;
910 NTSTATUS
wg_transform_flush(void *args
)
912 struct wg_transform
*transform
= get_transform(*(wg_transform_t
*)args
);
913 GstBuffer
*input_buffer
;
917 GST_LOG("transform %p", transform
);
919 while ((input_buffer
= gst_atomic_queue_pop(transform
->input_queue
)))
920 gst_buffer_unref(input_buffer
);
922 if ((status
= wg_transform_drain(args
)))
925 while ((sample
= gst_atomic_queue_pop(transform
->output_queue
)))
926 gst_sample_unref(sample
);
927 if ((sample
= transform
->output_sample
))
928 gst_sample_unref(sample
);
929 transform
->output_sample
= NULL
;
931 return STATUS_SUCCESS
;