explorerframe: Remove DECLSPEC_HIDDEN usage.
[wine.git] / dlls / mf / session.c
blob555a71dcdf76aece5d1cc9df8fd37ed948a8cccb
1 /*
2 * Copyright 2017 Nikolay Sivov
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <stdarg.h>
20 #include <math.h>
21 #include <float.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "mfidl.h"
28 #include "evr.h"
30 #include "wine/debug.h"
31 #include "wine/list.h"
33 #include "mf_private.h"
35 #include "initguid.h"
37 DEFINE_GUID(_MF_TOPONODE_IMFActivate, 0x33706f4a, 0x309a, 0x49be, 0xa8, 0xdd, 0xe7, 0xc0, 0x87, 0x5e, 0xb6, 0x79);
39 WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
41 enum session_command
43 SESSION_CMD_CLEAR_TOPOLOGIES,
44 SESSION_CMD_CLOSE,
45 SESSION_CMD_SET_TOPOLOGY,
46 SESSION_CMD_START,
47 SESSION_CMD_PAUSE,
48 SESSION_CMD_STOP,
49 SESSION_CMD_SET_RATE,
50 SESSION_CMD_SHUTDOWN,
53 struct session_op
55 IUnknown IUnknown_iface;
56 LONG refcount;
57 enum session_command command;
58 union
60 struct
62 DWORD flags;
63 IMFTopology *topology;
64 } set_topology;
65 struct
67 GUID time_format;
68 PROPVARIANT start_position;
69 } start;
70 struct
72 BOOL thin;
73 float rate;
74 } set_rate;
75 struct
77 IMFTopology *topology;
78 } notify_topology;
80 struct list entry;
83 struct queued_topology
85 struct list entry;
86 IMFTopology *topology;
87 MF_TOPOSTATUS status;
90 enum session_state
92 SESSION_STATE_STOPPED = 0,
93 SESSION_STATE_STARTING_SOURCES,
94 SESSION_STATE_PREROLLING_SINKS,
95 SESSION_STATE_STARTING_SINKS,
96 SESSION_STATE_STARTED,
97 SESSION_STATE_PAUSING_SINKS,
98 SESSION_STATE_PAUSING_SOURCES,
99 SESSION_STATE_PAUSED,
100 SESSION_STATE_STOPPING_SINKS,
101 SESSION_STATE_STOPPING_SOURCES,
102 SESSION_STATE_FINALIZING_SINKS,
103 SESSION_STATE_CLOSED,
104 SESSION_STATE_SHUT_DOWN,
107 enum object_state
109 OBJ_STATE_STOPPED = 0,
110 OBJ_STATE_STARTED,
111 OBJ_STATE_PAUSED,
112 OBJ_STATE_PREROLLED,
113 OBJ_STATE_INVALID,
116 enum media_source_flags
118 SOURCE_FLAG_END_OF_PRESENTATION = 0x1,
121 struct media_source
123 struct list entry;
124 union
126 IMFMediaSource *source;
127 IUnknown *object;
129 IMFPresentationDescriptor *pd;
130 enum object_state state;
131 unsigned int flags;
134 struct media_sink
136 struct list entry;
137 union
139 IMFMediaSink *sink;
140 IUnknown *object;
142 IMFMediaSinkPreroll *preroll;
143 IMFMediaEventGenerator *event_generator;
144 BOOL finalized;
147 struct sample
149 struct list entry;
150 IMFSample *sample;
153 struct transform_stream
155 struct list samples;
156 unsigned int requests;
157 unsigned int min_buffer_size;
160 enum topo_node_flags
162 TOPO_NODE_END_OF_STREAM = 0x1,
163 TOPO_NODE_SCRUB_SAMPLE_COMPLETE = 0x2,
166 struct topo_node
168 struct list entry;
169 struct media_session *session;
170 MF_TOPOLOGY_TYPE type;
171 TOPOID node_id;
172 IMFTopologyNode *node;
173 enum object_state state;
174 unsigned int flags;
175 union
177 IMFMediaStream *source_stream;
178 IMFStreamSink *sink_stream;
179 IMFTransform *transform;
180 IUnknown *object;
181 } object;
183 union
185 struct
187 IMFMediaSource *source;
188 DWORD stream_id;
189 } source;
190 struct
192 unsigned int requests;
193 IMFVideoSampleAllocatorNotify notify_cb;
194 IMFVideoSampleAllocator *allocator;
195 IMFVideoSampleAllocatorCallback *allocator_cb;
196 } sink;
197 struct
199 struct transform_stream *inputs;
200 DWORD *input_map;
201 unsigned int input_count;
203 struct transform_stream *outputs;
204 DWORD *output_map;
205 unsigned int output_count;
206 } transform;
207 } u;
210 enum presentation_flags
212 SESSION_FLAG_SOURCES_SUBSCRIBED = 0x1,
213 SESSION_FLAG_PRESENTATION_CLOCK_SET = 0x2,
214 SESSION_FLAG_FINALIZE_SINKS = 0x4,
215 SESSION_FLAG_NEEDS_PREROLL = 0x8,
216 SESSION_FLAG_END_OF_PRESENTATION = 0x10,
217 SESSION_FLAG_PENDING_RATE_CHANGE = 0x20,
218 SESSION_FLAG_PENDING_COMMAND = 0x40,
221 struct media_session
223 IMFMediaSession IMFMediaSession_iface;
224 IMFGetService IMFGetService_iface;
225 IMFRateSupport IMFRateSupport_iface;
226 IMFRateControl IMFRateControl_iface;
227 IMFTopologyNodeAttributeEditor IMFTopologyNodeAttributeEditor_iface;
228 IMFAsyncCallback commands_callback;
229 IMFAsyncCallback sa_ready_callback;
230 IMFAsyncCallback events_callback;
231 IMFAsyncCallback sink_finalizer_callback;
232 LONG refcount;
233 IMFMediaEventQueue *event_queue;
234 IMFPresentationClock *clock;
235 IMFPresentationTimeSource *system_time_source;
236 IMFRateControl *clock_rate_control;
237 IMFTopoLoader *topo_loader;
238 IMFQualityManager *quality_manager;
239 struct
241 IMFTopology *current_topology;
242 MF_TOPOSTATUS topo_status;
243 MFTIME clock_stop_time;
244 unsigned int flags;
245 struct list sources;
246 struct list sinks;
247 struct list nodes;
249 /* Latest Start() arguments. */
250 GUID time_format;
251 PROPVARIANT start_position;
252 /* Latest SetRate() arguments. */
253 BOOL thin;
254 float rate;
255 } presentation;
256 struct list topologies;
257 struct list commands;
258 enum session_state state;
259 DWORD caps;
260 CRITICAL_SECTION cs;
263 static inline struct media_session *impl_from_IMFMediaSession(IMFMediaSession *iface)
265 return CONTAINING_RECORD(iface, struct media_session, IMFMediaSession_iface);
268 static struct media_session *impl_from_commands_callback_IMFAsyncCallback(IMFAsyncCallback *iface)
270 return CONTAINING_RECORD(iface, struct media_session, commands_callback);
273 static struct media_session *impl_from_sa_ready_callback_IMFAsyncCallback(IMFAsyncCallback *iface)
275 return CONTAINING_RECORD(iface, struct media_session, sa_ready_callback);
278 static struct media_session *impl_from_events_callback_IMFAsyncCallback(IMFAsyncCallback *iface)
280 return CONTAINING_RECORD(iface, struct media_session, events_callback);
283 static struct media_session *impl_from_sink_finalizer_callback_IMFAsyncCallback(IMFAsyncCallback *iface)
285 return CONTAINING_RECORD(iface, struct media_session, sink_finalizer_callback);
288 static struct media_session *impl_from_IMFGetService(IMFGetService *iface)
290 return CONTAINING_RECORD(iface, struct media_session, IMFGetService_iface);
293 static struct media_session *impl_session_from_IMFRateSupport(IMFRateSupport *iface)
295 return CONTAINING_RECORD(iface, struct media_session, IMFRateSupport_iface);
298 static struct media_session *impl_session_from_IMFRateControl(IMFRateControl *iface)
300 return CONTAINING_RECORD(iface, struct media_session, IMFRateControl_iface);
303 static struct media_session *impl_session_from_IMFTopologyNodeAttributeEditor(IMFTopologyNodeAttributeEditor *iface)
305 return CONTAINING_RECORD(iface, struct media_session, IMFTopologyNodeAttributeEditor_iface);
308 static struct session_op *impl_op_from_IUnknown(IUnknown *iface)
310 return CONTAINING_RECORD(iface, struct session_op, IUnknown_iface);
313 static struct topo_node *impl_node_from_IMFVideoSampleAllocatorNotify(IMFVideoSampleAllocatorNotify *iface)
315 return CONTAINING_RECORD(iface, struct topo_node, u.sink.notify_cb);
318 /* IMFLocalMFTRegistration */
319 static HRESULT WINAPI local_mft_registration_QueryInterface(IMFLocalMFTRegistration *iface, REFIID riid, void **obj)
321 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
323 if (IsEqualIID(riid, &IID_IMFLocalMFTRegistration) ||
324 IsEqualIID(riid, &IID_IUnknown))
326 *obj = iface;
327 IMFLocalMFTRegistration_AddRef(iface);
328 return S_OK;
331 WARN("Unexpected %s.\n", debugstr_guid(riid));
332 *obj = NULL;
333 return E_NOINTERFACE;
336 static ULONG WINAPI local_mft_registration_AddRef(IMFLocalMFTRegistration *iface)
338 return 2;
341 static ULONG WINAPI local_mft_registration_Release(IMFLocalMFTRegistration *iface)
343 return 1;
346 static HRESULT WINAPI local_mft_registration_RegisterMFTs(IMFLocalMFTRegistration *iface, MFT_REGISTRATION_INFO *info,
347 DWORD count)
349 HRESULT hr = S_OK;
350 DWORD i;
352 TRACE("%p, %p, %lu.\n", iface, info, count);
354 for (i = 0; i < count; ++i)
356 if (FAILED(hr = MFTRegisterLocalByCLSID(&info[i].clsid, &info[i].guidCategory, info[i].pszName,
357 info[i].uiFlags, info[i].cInTypes, info[i].pInTypes, info[i].cOutTypes, info[i].pOutTypes)))
359 break;
363 return hr;
366 static const IMFLocalMFTRegistrationVtbl local_mft_registration_vtbl =
368 local_mft_registration_QueryInterface,
369 local_mft_registration_AddRef,
370 local_mft_registration_Release,
371 local_mft_registration_RegisterMFTs,
374 static IMFLocalMFTRegistration local_mft_registration = { &local_mft_registration_vtbl };
376 static HRESULT WINAPI session_op_QueryInterface(IUnknown *iface, REFIID riid, void **obj)
378 if (IsEqualIID(riid, &IID_IUnknown))
380 *obj = iface;
381 IUnknown_AddRef(iface);
382 return S_OK;
385 *obj = NULL;
386 return E_NOINTERFACE;
389 static ULONG WINAPI session_op_AddRef(IUnknown *iface)
391 struct session_op *op = impl_op_from_IUnknown(iface);
392 ULONG refcount = InterlockedIncrement(&op->refcount);
394 TRACE("%p, refcount %lu.\n", iface, refcount);
396 return refcount;
399 static ULONG WINAPI session_op_Release(IUnknown *iface)
401 struct session_op *op = impl_op_from_IUnknown(iface);
402 ULONG refcount = InterlockedDecrement(&op->refcount);
404 TRACE("%p, refcount %lu.\n", iface, refcount);
406 if (!refcount)
408 switch (op->command)
410 case SESSION_CMD_SET_TOPOLOGY:
411 if (op->set_topology.topology)
412 IMFTopology_Release(op->set_topology.topology);
413 break;
414 case SESSION_CMD_START:
415 PropVariantClear(&op->start.start_position);
416 break;
417 default:
420 free(op);
423 return refcount;
426 static const IUnknownVtbl session_op_vtbl =
428 session_op_QueryInterface,
429 session_op_AddRef,
430 session_op_Release,
433 static HRESULT create_session_op(enum session_command command, struct session_op **ret)
435 struct session_op *op;
437 if (!(op = calloc(1, sizeof(*op))))
438 return E_OUTOFMEMORY;
440 op->IUnknown_iface.lpVtbl = &session_op_vtbl;
441 op->refcount = 1;
442 op->command = command;
444 *ret = op;
446 return S_OK;
449 static HRESULT session_is_shut_down(struct media_session *session)
451 return session->state == SESSION_STATE_SHUT_DOWN ? MF_E_SHUTDOWN : S_OK;
454 static HRESULT session_submit_command(struct media_session *session, struct session_op *op)
456 HRESULT hr;
458 TRACE("session %p, op %p, command %u.\n", session, op, op->command);
460 EnterCriticalSection(&session->cs);
461 if (SUCCEEDED(hr = session_is_shut_down(session)))
463 if (list_empty(&session->commands) && !(session->presentation.flags & SESSION_FLAG_PENDING_COMMAND))
464 hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &session->commands_callback, &op->IUnknown_iface);
465 if (op->command == SESSION_CMD_SHUTDOWN)
466 list_add_head(&session->commands, &op->entry);
467 else
468 list_add_tail(&session->commands, &op->entry);
469 IUnknown_AddRef(&op->IUnknown_iface);
471 LeaveCriticalSection(&session->cs);
473 return hr;
476 static HRESULT session_submit_simple_command(struct media_session *session, enum session_command command)
478 struct session_op *op;
479 HRESULT hr;
481 if (FAILED(hr = create_session_op(command, &op)))
482 return hr;
484 hr = session_submit_command(session, op);
485 IUnknown_Release(&op->IUnknown_iface);
486 return hr;
489 static void session_clear_queued_topologies(struct media_session *session)
491 struct queued_topology *ptr, *next;
493 LIST_FOR_EACH_ENTRY_SAFE(ptr, next, &session->topologies, struct queued_topology, entry)
495 list_remove(&ptr->entry);
496 IMFTopology_Release(ptr->topology);
497 free(ptr);
501 static void session_set_topo_status(struct media_session *session, HRESULT status,
502 MF_TOPOSTATUS topo_status)
504 IMFMediaEvent *event;
505 PROPVARIANT param;
507 if (topo_status == MF_TOPOSTATUS_INVALID)
508 return;
510 if (list_empty(&session->topologies))
512 FIXME("Unexpectedly empty topology queue.\n");
513 return;
516 if (topo_status > session->presentation.topo_status)
518 struct queued_topology *topology = LIST_ENTRY(list_head(&session->topologies), struct queued_topology, entry);
520 param.vt = VT_UNKNOWN;
521 param.punkVal = (IUnknown *)topology->topology;
523 if (FAILED(MFCreateMediaEvent(MESessionTopologyStatus, &GUID_NULL, status, &param, &event)))
524 return;
526 session->presentation.topo_status = topo_status;
528 IMFMediaEvent_SetUINT32(event, &MF_EVENT_TOPOLOGY_STATUS, topo_status);
529 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
530 IMFMediaEvent_Release(event);
534 static HRESULT session_bind_output_nodes(IMFTopology *topology)
536 MF_TOPOLOGY_TYPE node_type;
537 IMFStreamSink *stream_sink;
538 IMFMediaSink *media_sink;
539 WORD node_count = 0, i;
540 IMFTopologyNode *node;
541 IMFActivate *activate;
542 UINT32 stream_id;
543 IUnknown *object;
544 HRESULT hr;
546 hr = IMFTopology_GetNodeCount(topology, &node_count);
548 for (i = 0; i < node_count; ++i)
550 if (FAILED(hr = IMFTopology_GetNode(topology, i, &node)))
551 break;
553 if (FAILED(hr = IMFTopologyNode_GetNodeType(node, &node_type)) || node_type != MF_TOPOLOGY_OUTPUT_NODE)
555 IMFTopologyNode_Release(node);
556 continue;
559 if (SUCCEEDED(hr = IMFTopologyNode_GetObject(node, &object)))
561 stream_sink = NULL;
562 if (FAILED(IUnknown_QueryInterface(object, &IID_IMFStreamSink, (void **)&stream_sink)))
564 if (SUCCEEDED(hr = IUnknown_QueryInterface(object, &IID_IMFActivate, (void **)&activate)))
566 if (SUCCEEDED(hr = IMFActivate_ActivateObject(activate, &IID_IMFMediaSink, (void **)&media_sink)))
568 if (FAILED(IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_STREAMID, &stream_id)))
569 stream_id = 0;
571 stream_sink = NULL;
572 if (FAILED(IMFMediaSink_GetStreamSinkById(media_sink, stream_id, &stream_sink)))
573 hr = IMFMediaSink_AddStreamSink(media_sink, stream_id, NULL, &stream_sink);
575 if (stream_sink)
576 hr = IMFTopologyNode_SetObject(node, (IUnknown *)stream_sink);
578 IMFMediaSink_Release(media_sink);
581 if (SUCCEEDED(hr))
582 IMFTopologyNode_SetUnknown(node, &_MF_TOPONODE_IMFActivate, (IUnknown *)activate);
584 IMFActivate_Release(activate);
588 if (stream_sink)
589 IMFStreamSink_Release(stream_sink);
590 IUnknown_Release(object);
593 IMFTopologyNode_Release(node);
596 return hr;
599 static HRESULT session_init_media_types(IMFTopology *topology)
601 MF_TOPOLOGY_TYPE node_type;
602 WORD node_count, i, j;
603 IMFTopologyNode *node;
604 IMFMediaType *type;
605 DWORD input_count;
606 HRESULT hr;
608 if (FAILED(hr = IMFTopology_GetNodeCount(topology, &node_count)))
609 return hr;
611 for (i = 0; i < node_count; ++i)
613 if (FAILED(hr = IMFTopology_GetNode(topology, i, &node)))
614 break;
616 if (FAILED(hr = IMFTopologyNode_GetInputCount(node, &input_count))
617 || FAILED(hr = IMFTopologyNode_GetNodeType(node, &node_type))
618 || node_type != MF_TOPOLOGY_OUTPUT_NODE)
620 IMFTopologyNode_Release(node);
621 continue;
624 for (j = 0; j < input_count; ++j)
626 IMFMediaTypeHandler *handler;
627 IMFTopologyNode *up_node;
628 DWORD up_output;
630 if (SUCCEEDED(hr = IMFTopologyNode_GetInput(node, j, &up_node, &up_output)))
632 hr = topology_node_init_media_type(up_node, up_output, TRUE, &type);
633 IMFTopologyNode_Release(up_node);
635 if (FAILED(hr))
636 break;
638 if (SUCCEEDED(hr = topology_node_get_type_handler(node, j, FALSE, &handler)))
640 hr = IMFMediaTypeHandler_SetCurrentMediaType(handler, type);
641 IMFMediaTypeHandler_Release(handler);
644 IMFMediaType_Release(type);
647 IMFTopologyNode_Release(node);
650 return hr;
653 static void session_set_caps(struct media_session *session, DWORD caps)
655 DWORD delta = session->caps ^ caps;
656 IMFMediaEvent *event;
658 /* Delta is documented to reflect rate value changes as well, but it's not clear what to compare
659 them to, since session always queries for current object rates. */
660 if (!delta)
661 return;
663 session->caps = caps;
665 if (FAILED(MFCreateMediaEvent(MESessionCapabilitiesChanged, &GUID_NULL, S_OK, NULL, &event)))
666 return;
668 IMFMediaEvent_SetUINT32(event, &MF_EVENT_SESSIONCAPS, caps);
669 IMFMediaEvent_SetUINT32(event, &MF_EVENT_SESSIONCAPS_DELTA, delta);
671 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
672 IMFMediaEvent_Release(event);
675 static void transform_release_sample(struct sample *sample)
677 list_remove(&sample->entry);
678 if (sample->sample)
679 IMFSample_Release(sample->sample);
680 free(sample);
683 static void transform_stream_drop_samples(struct transform_stream *stream)
685 struct sample *sample, *sample2;
687 LIST_FOR_EACH_ENTRY_SAFE(sample, sample2, &stream->samples, struct sample, entry)
688 transform_release_sample(sample);
691 static void release_topo_node(struct topo_node *node)
693 unsigned int i;
695 switch (node->type)
697 case MF_TOPOLOGY_SOURCESTREAM_NODE:
698 if (node->u.source.source)
699 IMFMediaSource_Release(node->u.source.source);
700 break;
701 case MF_TOPOLOGY_TRANSFORM_NODE:
702 for (i = 0; i < node->u.transform.input_count; ++i)
703 transform_stream_drop_samples(&node->u.transform.inputs[i]);
704 for (i = 0; i < node->u.transform.output_count; ++i)
705 transform_stream_drop_samples(&node->u.transform.outputs[i]);
706 free(node->u.transform.inputs);
707 free(node->u.transform.outputs);
708 free(node->u.transform.input_map);
709 free(node->u.transform.output_map);
710 break;
711 case MF_TOPOLOGY_OUTPUT_NODE:
712 if (node->u.sink.allocator)
713 IMFVideoSampleAllocator_Release(node->u.sink.allocator);
714 if (node->u.sink.allocator_cb)
716 IMFVideoSampleAllocatorCallback_SetCallback(node->u.sink.allocator_cb, NULL);
717 IMFVideoSampleAllocatorCallback_Release(node->u.sink.allocator_cb);
719 break;
720 default:
724 if (node->object.object)
725 IUnknown_Release(node->object.object);
726 if (node->node)
727 IMFTopologyNode_Release(node->node);
728 free(node);
731 static void session_shutdown_current_topology(struct media_session *session)
733 unsigned int shutdown, force_shutdown;
734 MF_TOPOLOGY_TYPE node_type;
735 IMFStreamSink *stream_sink;
736 IMFTopology *topology;
737 IMFTopologyNode *node;
738 IMFActivate *activate;
739 IMFMediaSink *sink;
740 WORD idx = 0;
741 HRESULT hr;
743 topology = session->presentation.current_topology;
744 force_shutdown = session->state == SESSION_STATE_SHUT_DOWN;
746 /* FIXME: should handle async MFTs, but these are not supported by the rest of the pipeline currently. */
748 while (SUCCEEDED(IMFTopology_GetNode(topology, idx++, &node)))
750 if (SUCCEEDED(IMFTopologyNode_GetNodeType(node, &node_type)) &&
751 node_type == MF_TOPOLOGY_OUTPUT_NODE)
753 shutdown = 1;
754 IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, &shutdown);
756 if (force_shutdown || shutdown)
758 if (SUCCEEDED(IMFTopologyNode_GetUnknown(node, &_MF_TOPONODE_IMFActivate, &IID_IMFActivate,
759 (void **)&activate)))
761 if (FAILED(hr = IMFActivate_ShutdownObject(activate)))
762 WARN("Failed to shut down activation object for the sink, hr %#lx.\n", hr);
763 IMFActivate_Release(activate);
765 else if (SUCCEEDED(topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink)))
767 if (SUCCEEDED(IMFStreamSink_GetMediaSink(stream_sink, &sink)))
769 IMFMediaSink_Shutdown(sink);
770 IMFMediaSink_Release(sink);
773 IMFStreamSink_Release(stream_sink);
778 IMFTopologyNode_Release(node);
782 static void session_clear_command_list(struct media_session *session)
784 struct session_op *op, *op2;
786 LIST_FOR_EACH_ENTRY_SAFE(op, op2, &session->commands, struct session_op, entry)
788 list_remove(&op->entry);
789 IUnknown_Release(&op->IUnknown_iface);
793 static void session_clear_presentation(struct media_session *session)
795 struct media_source *source, *source2;
796 struct media_sink *sink, *sink2;
797 struct topo_node *node, *node2;
799 session_shutdown_current_topology(session);
801 IMFTopology_Clear(session->presentation.current_topology);
802 session->presentation.topo_status = MF_TOPOSTATUS_INVALID;
803 session->presentation.flags = 0;
805 LIST_FOR_EACH_ENTRY_SAFE(source, source2, &session->presentation.sources, struct media_source, entry)
807 list_remove(&source->entry);
808 if (source->source)
809 IMFMediaSource_Release(source->source);
810 if (source->pd)
811 IMFPresentationDescriptor_Release(source->pd);
812 free(source);
815 LIST_FOR_EACH_ENTRY_SAFE(node, node2, &session->presentation.nodes, struct topo_node, entry)
817 list_remove(&node->entry);
818 release_topo_node(node);
821 LIST_FOR_EACH_ENTRY_SAFE(sink, sink2, &session->presentation.sinks, struct media_sink, entry)
823 list_remove(&sink->entry);
825 if (sink->sink)
826 IMFMediaSink_Release(sink->sink);
827 if (sink->preroll)
828 IMFMediaSinkPreroll_Release(sink->preroll);
829 if (sink->event_generator)
830 IMFMediaEventGenerator_Release(sink->event_generator);
831 free(sink);
835 static struct topo_node *session_get_node_by_id(const struct media_session *session, TOPOID id)
837 struct topo_node *node;
839 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
841 if (node->node_id == id)
842 return node;
845 return NULL;
848 static void session_command_complete(struct media_session *session)
850 struct session_op *op;
851 struct list *e;
853 session->presentation.flags &= ~SESSION_FLAG_PENDING_COMMAND;
855 /* Submit next command. */
856 if ((e = list_head(&session->commands)))
858 op = LIST_ENTRY(e, struct session_op, entry);
859 MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &session->commands_callback, &op->IUnknown_iface);
863 static void session_command_complete_with_event(struct media_session *session, MediaEventType event,
864 HRESULT status, const PROPVARIANT *param)
866 IMFMediaEventQueue_QueueEventParamVar(session->event_queue, event, &GUID_NULL, status, param);
867 session_command_complete(session);
870 static HRESULT session_subscribe_sources(struct media_session *session)
872 struct media_source *source;
873 HRESULT hr = S_OK;
875 if (session->presentation.flags & SESSION_FLAG_SOURCES_SUBSCRIBED)
876 return hr;
878 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
880 if (FAILED(hr = IMFMediaSource_BeginGetEvent(source->source, &session->events_callback,
881 source->object)))
883 WARN("Failed to subscribe to source events, hr %#lx.\n", hr);
884 return hr;
888 session->presentation.flags |= SESSION_FLAG_SOURCES_SUBSCRIBED;
889 return hr;
892 static void session_start(struct media_session *session, const GUID *time_format, const PROPVARIANT *start_position)
894 struct media_source *source;
895 HRESULT hr;
897 switch (session->state)
899 case SESSION_STATE_STOPPED:
901 /* Start request with no current topology. */
902 if (session->presentation.topo_status == MF_TOPOSTATUS_INVALID)
904 session_command_complete_with_event(session, MESessionStarted, MF_E_INVALIDREQUEST, NULL);
905 break;
908 /* fallthrough */
909 case SESSION_STATE_PAUSED:
911 session->presentation.time_format = *time_format;
912 session->presentation.start_position.vt = VT_EMPTY;
913 PropVariantCopy(&session->presentation.start_position, start_position);
915 if (FAILED(hr = session_subscribe_sources(session)))
917 session_command_complete_with_event(session, MESessionStarted, hr, NULL);
918 return;
921 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
923 if (FAILED(hr = IMFMediaSource_Start(source->source, source->pd, &GUID_NULL, start_position)))
925 WARN("Failed to start media source %p, hr %#lx.\n", source->source, hr);
926 session_command_complete_with_event(session, MESessionStarted, hr, NULL);
927 return;
931 session->state = SESSION_STATE_STARTING_SOURCES;
932 break;
933 case SESSION_STATE_STARTED:
934 FIXME("Seeking is not implemented.\n");
935 session_command_complete(session);
936 break;
937 default:
938 session_command_complete_with_event(session, MESessionStarted, MF_E_INVALIDREQUEST, NULL);
939 break;
943 static void session_set_started(struct media_session *session)
945 struct media_source *source;
946 IMFMediaEvent *event;
947 unsigned int caps;
948 DWORD flags;
950 session->state = SESSION_STATE_STARTED;
952 caps = session->caps | MFSESSIONCAP_PAUSE;
954 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
956 if (SUCCEEDED(IMFMediaSource_GetCharacteristics(source->source, &flags)))
958 if (!(flags & MFMEDIASOURCE_CAN_PAUSE))
960 caps &= ~MFSESSIONCAP_PAUSE;
961 break;
966 session_set_caps(session, caps);
968 if (SUCCEEDED(MFCreateMediaEvent(MESessionStarted, &GUID_NULL, S_OK, NULL, &event)))
970 IMFMediaEvent_SetUINT64(event, &MF_EVENT_PRESENTATION_TIME_OFFSET, 0);
971 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
972 IMFMediaEvent_Release(event);
974 session_command_complete(session);
977 static void session_set_paused(struct media_session *session, unsigned int state, HRESULT status)
979 /* Failed event status could indicate a failure during normal transition to paused state,
980 or an attempt to pause from invalid initial state. To finalize failed transition in the former case,
981 state is still forced to PAUSED, otherwise previous state is retained. */
982 if (state != ~0u) session->state = state;
983 if (SUCCEEDED(status))
984 session_set_caps(session, session->caps & ~MFSESSIONCAP_PAUSE);
985 session_command_complete_with_event(session, MESessionPaused, status, NULL);
988 static void session_set_closed(struct media_session *session, HRESULT status)
990 session->state = SESSION_STATE_CLOSED;
991 if (SUCCEEDED(status))
992 session_set_caps(session, session->caps & ~(MFSESSIONCAP_START | MFSESSIONCAP_SEEK));
993 session_command_complete_with_event(session, MESessionClosed, status, NULL);
996 static void session_pause(struct media_session *session)
998 unsigned int state = ~0u;
999 HRESULT hr;
1001 switch (session->state)
1003 case SESSION_STATE_STARTED:
1005 /* Transition in two steps - pause the clock, wait for sinks, then pause sources. */
1006 if (SUCCEEDED(hr = IMFPresentationClock_Pause(session->clock)))
1007 session->state = SESSION_STATE_PAUSING_SINKS;
1008 state = SESSION_STATE_PAUSED;
1010 break;
1012 case SESSION_STATE_STOPPED:
1013 hr = MF_E_SESSION_PAUSEWHILESTOPPED;
1014 break;
1015 default:
1016 hr = MF_E_INVALIDREQUEST;
1019 if (FAILED(hr))
1020 session_set_paused(session, state, hr);
1023 static void session_clear_end_of_presentation(struct media_session *session)
1025 struct media_source *source;
1026 struct topo_node *node;
1028 session->presentation.flags &= ~SESSION_FLAG_END_OF_PRESENTATION;
1029 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
1031 source->flags &= ~SOURCE_FLAG_END_OF_PRESENTATION;
1033 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
1035 node->flags &= ~TOPO_NODE_END_OF_STREAM;
1037 session->presentation.topo_status = MF_TOPOSTATUS_READY;
1040 static void session_set_stopped(struct media_session *session, HRESULT status)
1042 MediaEventType event_type;
1043 IMFMediaEvent *event;
1045 session->state = SESSION_STATE_STOPPED;
1046 event_type = session->presentation.flags & SESSION_FLAG_END_OF_PRESENTATION ? MESessionEnded : MESessionStopped;
1048 if (SUCCEEDED(MFCreateMediaEvent(event_type, &GUID_NULL, status, NULL, &event)))
1050 IMFMediaEvent_SetUINT64(event, &MF_SESSION_APPROX_EVENT_OCCURRENCE_TIME, session->presentation.clock_stop_time);
1051 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
1052 IMFMediaEvent_Release(event);
1054 session_clear_end_of_presentation(session);
1055 session_command_complete(session);
1058 static void session_stop(struct media_session *session)
1060 HRESULT hr = MF_E_INVALIDREQUEST;
1062 switch (session->state)
1064 case SESSION_STATE_STARTED:
1065 case SESSION_STATE_PAUSED:
1067 /* Transition in two steps - stop the clock, wait for sinks, then stop sources. */
1068 IMFPresentationClock_GetTime(session->clock, &session->presentation.clock_stop_time);
1069 if (SUCCEEDED(hr = IMFPresentationClock_Stop(session->clock)))
1070 session->state = SESSION_STATE_STOPPING_SINKS;
1071 else
1072 session_set_stopped(session, hr);
1074 break;
1075 case SESSION_STATE_STOPPED:
1076 hr = S_OK;
1077 /* fallthrough */
1078 default:
1079 session_command_complete_with_event(session, MESessionStopped, hr, NULL);
1080 break;
1084 static HRESULT session_finalize_sinks(struct media_session *session)
1086 IMFFinalizableMediaSink *fin_sink;
1087 BOOL sinks_finalized = TRUE;
1088 struct media_sink *sink;
1089 HRESULT hr = S_OK;
1091 session->presentation.flags &= ~SESSION_FLAG_FINALIZE_SINKS;
1092 session->state = SESSION_STATE_FINALIZING_SINKS;
1094 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
1096 if (SUCCEEDED(IMFMediaSink_QueryInterface(sink->sink, &IID_IMFFinalizableMediaSink, (void **)&fin_sink)))
1098 hr = IMFFinalizableMediaSink_BeginFinalize(fin_sink, &session->sink_finalizer_callback,
1099 (IUnknown *)fin_sink);
1100 IMFFinalizableMediaSink_Release(fin_sink);
1101 if (FAILED(hr))
1102 break;
1103 sinks_finalized = FALSE;
1105 else
1106 sink->finalized = TRUE;
1109 if (sinks_finalized)
1110 session_set_closed(session, hr);
1112 return hr;
1115 static void session_close(struct media_session *session)
1117 HRESULT hr = S_OK;
1119 switch (session->state)
1121 case SESSION_STATE_STOPPED:
1122 hr = session_finalize_sinks(session);
1123 break;
1124 case SESSION_STATE_STARTED:
1125 case SESSION_STATE_PAUSED:
1126 session->presentation.flags |= SESSION_FLAG_FINALIZE_SINKS;
1127 if (SUCCEEDED(hr = IMFPresentationClock_Stop(session->clock)))
1128 session->state = SESSION_STATE_STOPPING_SINKS;
1129 break;
1130 default:
1131 hr = MF_E_INVALIDREQUEST;
1132 break;
1135 session_clear_queued_topologies(session);
1136 if (FAILED(hr))
1137 session_set_closed(session, hr);
1140 static void session_clear_topologies(struct media_session *session)
1142 HRESULT hr = S_OK;
1144 if (session->state == SESSION_STATE_CLOSED)
1145 hr = MF_E_INVALIDREQUEST;
1146 else
1147 session_clear_queued_topologies(session);
1148 session_command_complete_with_event(session, MESessionTopologiesCleared, hr, NULL);
1151 static HRESULT session_is_presentation_rate_supported(struct media_session *session, BOOL thin, float rate,
1152 float *nearest_rate)
1154 IMFRateSupport *rate_support;
1155 struct media_source *source;
1156 struct media_sink *sink;
1157 float value = 0.0f, tmp;
1158 HRESULT hr = S_OK;
1159 DWORD flags;
1161 if (!nearest_rate) nearest_rate = &tmp;
1163 if (rate == 0.0f)
1165 *nearest_rate = 1.0f;
1166 return S_OK;
1169 if (session->presentation.topo_status != MF_TOPOSTATUS_INVALID)
1171 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
1173 if (FAILED(hr = MFGetService(source->object, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport,
1174 (void **)&rate_support)))
1176 value = 1.0f;
1177 break;
1180 value = rate;
1181 if (FAILED(hr = IMFRateSupport_IsRateSupported(rate_support, thin, rate, &value)))
1182 WARN("Source does not support rate %f, hr %#lx.\n", rate, hr);
1183 IMFRateSupport_Release(rate_support);
1185 /* Only "first" source is considered. */
1186 break;
1189 if (SUCCEEDED(hr))
1191 /* For sinks only check if rate is supported, ignoring nearest values. */
1192 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
1194 flags = 0;
1195 if (FAILED(hr = IMFMediaSink_GetCharacteristics(sink->sink, &flags)))
1196 break;
1198 if (flags & MEDIASINK_RATELESS)
1199 continue;
1201 if (FAILED(MFGetService(sink->object, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport,
1202 (void **)&rate_support)))
1203 continue;
1205 hr = IMFRateSupport_IsRateSupported(rate_support, thin, rate, NULL);
1206 IMFRateSupport_Release(rate_support);
1207 if (FAILED(hr))
1209 WARN("Sink %p does not support rate %f, hr %#lx.\n", sink->sink, rate, hr);
1210 break;
1216 *nearest_rate = value;
1218 return hr;
1221 static void session_set_consumed_clock(IUnknown *object, IMFPresentationClock *clock)
1223 IMFClockConsumer *consumer;
1225 if (SUCCEEDED(IUnknown_QueryInterface(object, &IID_IMFClockConsumer, (void **)&consumer)))
1227 IMFClockConsumer_SetPresentationClock(consumer, clock);
1228 IMFClockConsumer_Release(consumer);
1232 static void session_set_presentation_clock(struct media_session *session)
1234 IMFPresentationTimeSource *time_source = NULL;
1235 struct media_source *source;
1236 struct media_sink *sink;
1237 struct topo_node *node;
1238 HRESULT hr;
1240 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
1242 if (node->type == MF_TOPOLOGY_TRANSFORM_NODE)
1243 IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
1246 if (!(session->presentation.flags & SESSION_FLAG_PRESENTATION_CLOCK_SET))
1248 /* Attempt to get time source from the sinks. */
1249 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
1251 if (SUCCEEDED(IMFMediaSink_QueryInterface(sink->sink, &IID_IMFPresentationTimeSource,
1252 (void **)&time_source)))
1253 break;
1256 if (time_source)
1258 hr = IMFPresentationClock_SetTimeSource(session->clock, time_source);
1259 IMFPresentationTimeSource_Release(time_source);
1261 else
1262 hr = IMFPresentationClock_SetTimeSource(session->clock, session->system_time_source);
1264 if (FAILED(hr))
1265 WARN("Failed to set time source, hr %#lx.\n", hr);
1267 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
1269 if (node->type != MF_TOPOLOGY_OUTPUT_NODE)
1270 continue;
1272 if (FAILED(hr = IMFStreamSink_BeginGetEvent(node->object.sink_stream, &session->events_callback,
1273 node->object.object)))
1275 WARN("Failed to subscribe to stream sink events, hr %#lx.\n", hr);
1279 /* Set clock for all topology nodes. */
1280 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
1282 session_set_consumed_clock(source->object, session->clock);
1285 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
1287 if (sink->event_generator && FAILED(hr = IMFMediaEventGenerator_BeginGetEvent(sink->event_generator,
1288 &session->events_callback, (IUnknown *)sink->event_generator)))
1290 WARN("Failed to subscribe to sink events, hr %#lx.\n", hr);
1293 if (FAILED(hr = IMFMediaSink_SetPresentationClock(sink->sink, session->clock)))
1294 WARN("Failed to set presentation clock for the sink, hr %#lx.\n", hr);
1297 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
1299 if (node->type != MF_TOPOLOGY_TRANSFORM_NODE)
1300 continue;
1302 session_set_consumed_clock(node->object.object, session->clock);
1305 session->presentation.flags |= SESSION_FLAG_PRESENTATION_CLOCK_SET;
1309 static void session_set_rate(struct media_session *session, BOOL thin, float rate)
1311 IMFRateControl *rate_control;
1312 struct media_source *source;
1313 float clock_rate = 0.0f;
1314 PROPVARIANT param;
1315 HRESULT hr;
1317 hr = session_is_presentation_rate_supported(session, thin, rate, NULL);
1319 if (SUCCEEDED(hr))
1320 hr = IMFRateControl_GetRate(session->clock_rate_control, NULL, &clock_rate);
1322 if (SUCCEEDED(hr) && (rate != clock_rate) && SUCCEEDED(hr = session_subscribe_sources(session)))
1324 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
1326 if (SUCCEEDED(hr = MFGetService(source->object, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateControl,
1327 (void **)&rate_control)))
1329 hr = IMFRateControl_SetRate(rate_control, thin, rate);
1330 IMFRateControl_Release(rate_control);
1331 if (SUCCEEDED(hr))
1333 session->presentation.flags |= SESSION_FLAG_PENDING_RATE_CHANGE;
1334 session->presentation.rate = rate;
1335 return;
1339 break;
1343 param.vt = VT_R4;
1344 param.fltVal = rate;
1345 session_command_complete_with_event(session, MESessionRateChanged, hr, SUCCEEDED(hr) ? &param : NULL);
1348 static void session_complete_rate_change(struct media_session *session)
1350 PROPVARIANT param;
1351 HRESULT hr;
1353 if (!(session->presentation.flags & SESSION_FLAG_PENDING_RATE_CHANGE))
1354 return;
1356 session->presentation.flags &= ~SESSION_FLAG_PENDING_RATE_CHANGE;
1357 session_set_presentation_clock(session);
1359 hr = IMFRateControl_SetRate(session->clock_rate_control, session->presentation.thin,
1360 session->presentation.rate);
1362 param.vt = VT_R4;
1363 param.fltVal = session->presentation.rate;
1365 IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionRateChanged, &GUID_NULL, hr,
1366 SUCCEEDED(hr) ? &param : NULL);
1367 session_command_complete(session);
1370 static struct media_source *session_get_media_source(struct media_session *session, IMFMediaSource *source)
1372 struct media_source *cur;
1374 LIST_FOR_EACH_ENTRY(cur, &session->presentation.sources, struct media_source, entry)
1376 if (source == cur->source)
1377 return cur;
1380 return NULL;
1383 static void session_release_media_source(struct media_source *source)
1385 IMFMediaSource_Release(source->source);
1386 if (source->pd)
1387 IMFPresentationDescriptor_Release(source->pd);
1388 free(source);
1391 static HRESULT session_add_media_source(struct media_session *session, IMFTopologyNode *node, IMFMediaSource *source)
1393 struct media_source *media_source;
1394 HRESULT hr;
1396 if (session_get_media_source(session, source))
1397 return S_FALSE;
1399 if (!(media_source = calloc(1, sizeof(*media_source))))
1400 return E_OUTOFMEMORY;
1402 media_source->source = source;
1403 IMFMediaSource_AddRef(media_source->source);
1405 hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, &IID_IMFPresentationDescriptor,
1406 (void **)&media_source->pd);
1408 if (SUCCEEDED(hr))
1409 list_add_tail(&session->presentation.sources, &media_source->entry);
1410 else
1411 session_release_media_source(media_source);
1413 return hr;
1416 static void session_raise_topology_set(struct media_session *session, IMFTopology *topology, HRESULT status)
1418 PROPVARIANT param;
1420 param.vt = topology ? VT_UNKNOWN : VT_EMPTY;
1421 param.punkVal = (IUnknown *)topology;
1423 IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionTopologySet, &GUID_NULL, status, &param);
1426 static DWORD session_get_object_rate_caps(IUnknown *object)
1428 IMFRateSupport *rate_support;
1429 DWORD caps = 0;
1430 float rate;
1432 if (SUCCEEDED(MFGetService(object, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, (void **)&rate_support)))
1434 rate = 0.0f;
1435 if (SUCCEEDED(IMFRateSupport_GetFastestRate(rate_support, MFRATE_FORWARD, TRUE, &rate)) && rate != 0.0f)
1436 caps |= MFSESSIONCAP_RATE_FORWARD;
1438 rate = 0.0f;
1439 if (SUCCEEDED(IMFRateSupport_GetFastestRate(rate_support, MFRATE_REVERSE, TRUE, &rate)) && rate != 0.0f)
1440 caps |= MFSESSIONCAP_RATE_REVERSE;
1442 IMFRateSupport_Release(rate_support);
1445 return caps;
1448 static HRESULT session_add_media_sink(struct media_session *session, IMFTopologyNode *node, IMFMediaSink *sink)
1450 struct media_sink *media_sink;
1451 unsigned int disable_preroll = 0;
1452 DWORD flags;
1454 LIST_FOR_EACH_ENTRY(media_sink, &session->presentation.sinks, struct media_sink, entry)
1456 if (sink == media_sink->sink)
1457 return S_FALSE;
1460 if (!(media_sink = calloc(1, sizeof(*media_sink))))
1461 return E_OUTOFMEMORY;
1463 media_sink->sink = sink;
1464 IMFMediaSink_AddRef(media_sink->sink);
1466 IMFMediaSink_QueryInterface(media_sink->sink, &IID_IMFMediaEventGenerator, (void **)&media_sink->event_generator);
1468 IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_DISABLE_PREROLL, &disable_preroll);
1469 if (SUCCEEDED(IMFMediaSink_GetCharacteristics(sink, &flags)) && flags & MEDIASINK_CAN_PREROLL && !disable_preroll)
1471 if (SUCCEEDED(IMFMediaSink_QueryInterface(media_sink->sink, &IID_IMFMediaSinkPreroll, (void **)&media_sink->preroll)))
1472 session->presentation.flags |= SESSION_FLAG_NEEDS_PREROLL;
1475 list_add_tail(&session->presentation.sinks, &media_sink->entry);
1477 return S_OK;
1480 static DWORD transform_node_get_stream_id(struct topo_node *node, BOOL output, unsigned int index)
1482 DWORD *map = output ? node->u.transform.output_map : node->u.transform.input_map;
1483 return map ? map[index] : index;
1486 static HRESULT session_set_transform_stream_info(struct topo_node *node)
1488 DWORD *input_map = NULL, *output_map = NULL;
1489 DWORD i, input_count, output_count;
1490 struct transform_stream *streams;
1491 unsigned int block_alignment;
1492 IMFMediaType *media_type;
1493 UINT32 bytes_per_second;
1494 GUID major = { 0 };
1495 HRESULT hr;
1497 hr = IMFTransform_GetStreamCount(node->object.transform, &input_count, &output_count);
1498 if (SUCCEEDED(hr) && (input_count > 1 || output_count > 1))
1500 input_map = calloc(input_count, sizeof(*input_map));
1501 output_map = calloc(output_count, sizeof(*output_map));
1502 if (FAILED(IMFTransform_GetStreamIDs(node->object.transform, input_count, input_map,
1503 output_count, output_map)))
1505 /* Assume sequential identifiers. */
1506 free(input_map);
1507 free(output_map);
1508 input_map = output_map = NULL;
1512 if (SUCCEEDED(hr))
1514 node->u.transform.input_map = input_map;
1515 node->u.transform.output_map = output_map;
1517 streams = calloc(input_count, sizeof(*streams));
1518 for (i = 0; i < input_count; ++i)
1519 list_init(&streams[i].samples);
1520 node->u.transform.inputs = streams;
1521 node->u.transform.input_count = input_count;
1523 streams = calloc(output_count, sizeof(*streams));
1524 for (i = 0; i < output_count; ++i)
1526 list_init(&streams[i].samples);
1528 if (SUCCEEDED(IMFTransform_GetOutputCurrentType(node->object.transform,
1529 transform_node_get_stream_id(node, TRUE, i), &media_type)))
1531 if (SUCCEEDED(IMFMediaType_GetMajorType(media_type, &major)) && IsEqualGUID(&major, &MFMediaType_Audio)
1532 && SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &block_alignment)))
1534 streams[i].min_buffer_size = block_alignment;
1535 if (SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &bytes_per_second)))
1536 streams[i].min_buffer_size = max(streams[i].min_buffer_size, bytes_per_second);
1538 IMFMediaType_Release(media_type);
1541 node->u.transform.outputs = streams;
1542 node->u.transform.output_count = output_count;
1545 return hr;
1548 static HRESULT session_get_stream_sink_type(IMFStreamSink *sink, IMFMediaType **media_type)
1550 IMFMediaTypeHandler *handler;
1551 HRESULT hr;
1553 if (SUCCEEDED(hr = IMFStreamSink_GetMediaTypeHandler(sink, &handler)))
1555 hr = IMFMediaTypeHandler_GetCurrentMediaType(handler, media_type);
1556 IMFMediaTypeHandler_Release(handler);
1559 return hr;
1562 static HRESULT WINAPI node_sample_allocator_cb_QueryInterface(IMFVideoSampleAllocatorNotify *iface,
1563 REFIID riid, void **obj)
1565 if (IsEqualIID(riid, &IID_IMFVideoSampleAllocatorNotify) ||
1566 IsEqualIID(riid, &IID_IUnknown))
1568 *obj = iface;
1569 IMFVideoSampleAllocatorNotify_AddRef(iface);
1570 return S_OK;
1573 *obj = NULL;
1574 return E_NOINTERFACE;
1577 static ULONG WINAPI node_sample_allocator_cb_AddRef(IMFVideoSampleAllocatorNotify *iface)
1579 return 2;
1582 static ULONG WINAPI node_sample_allocator_cb_Release(IMFVideoSampleAllocatorNotify *iface)
1584 return 1;
1587 static HRESULT session_request_sample_from_node(struct media_session *session, IMFTopologyNode *node, DWORD output);
1589 static HRESULT WINAPI node_sample_allocator_cb_NotifyRelease(IMFVideoSampleAllocatorNotify *iface)
1591 struct topo_node *topo_node = impl_node_from_IMFVideoSampleAllocatorNotify(iface);
1592 MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &topo_node->session->sa_ready_callback, (IUnknown *)iface);
1593 return S_OK;
1596 static const IMFVideoSampleAllocatorNotifyVtbl node_sample_allocator_cb_vtbl =
1598 node_sample_allocator_cb_QueryInterface,
1599 node_sample_allocator_cb_AddRef,
1600 node_sample_allocator_cb_Release,
1601 node_sample_allocator_cb_NotifyRelease,
1604 static HRESULT session_append_node(struct media_session *session, IMFTopologyNode *node)
1606 struct topo_node *topo_node;
1607 IMFMediaSink *media_sink;
1608 IMFMediaType *media_type;
1609 IMFStreamDescriptor *sd;
1610 HRESULT hr = S_OK;
1612 if (!(topo_node = calloc(1, sizeof(*topo_node))))
1613 return E_OUTOFMEMORY;
1615 IMFTopologyNode_GetNodeType(node, &topo_node->type);
1616 IMFTopologyNode_GetTopoNodeID(node, &topo_node->node_id);
1617 topo_node->node = node;
1618 IMFTopologyNode_AddRef(topo_node->node);
1619 topo_node->session = session;
1621 switch (topo_node->type)
1623 case MF_TOPOLOGY_OUTPUT_NODE:
1624 topo_node->u.sink.notify_cb.lpVtbl = &node_sample_allocator_cb_vtbl;
1626 if (FAILED(hr = topology_node_get_object(node, &IID_IMFStreamSink, (void **)&topo_node->object.object)))
1628 WARN("Failed to get stream sink interface, hr %#lx.\n", hr);
1629 break;
1632 if (FAILED(hr = IMFStreamSink_GetMediaSink(topo_node->object.sink_stream, &media_sink)))
1633 break;
1635 if (SUCCEEDED(hr = session_add_media_sink(session, node, media_sink)))
1637 if (SUCCEEDED(session_get_stream_sink_type(topo_node->object.sink_stream, &media_type)))
1639 if (SUCCEEDED(MFGetService(topo_node->object.object, &MR_VIDEO_ACCELERATION_SERVICE,
1640 &IID_IMFVideoSampleAllocator, (void **)&topo_node->u.sink.allocator)))
1642 if (FAILED(hr = IMFVideoSampleAllocator_InitializeSampleAllocator(topo_node->u.sink.allocator,
1643 2, media_type)))
1645 WARN("Failed to initialize sample allocator for the stream, hr %#lx.\n", hr);
1647 IMFVideoSampleAllocator_QueryInterface(topo_node->u.sink.allocator,
1648 &IID_IMFVideoSampleAllocatorCallback, (void **)&topo_node->u.sink.allocator_cb);
1649 IMFVideoSampleAllocatorCallback_SetCallback(topo_node->u.sink.allocator_cb,
1650 &topo_node->u.sink.notify_cb);
1652 IMFMediaType_Release(media_type);
1655 IMFMediaSink_Release(media_sink);
1657 break;
1658 case MF_TOPOLOGY_SOURCESTREAM_NODE:
1659 if (FAILED(IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_SOURCE, &IID_IMFMediaSource,
1660 (void **)&topo_node->u.source.source)))
1662 WARN("Missing MF_TOPONODE_SOURCE, hr %#lx.\n", hr);
1663 break;
1666 if (FAILED(hr = session_add_media_source(session, node, topo_node->u.source.source)))
1667 break;
1669 if (FAILED(hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_STREAM_DESCRIPTOR,
1670 &IID_IMFStreamDescriptor, (void **)&sd)))
1672 WARN("Missing MF_TOPONODE_STREAM_DESCRIPTOR, hr %#lx.\n", hr);
1673 break;
1676 hr = IMFStreamDescriptor_GetStreamIdentifier(sd, &topo_node->u.source.stream_id);
1677 IMFStreamDescriptor_Release(sd);
1679 break;
1680 case MF_TOPOLOGY_TRANSFORM_NODE:
1682 if (SUCCEEDED(hr = topology_node_get_object(node, &IID_IMFTransform, (void **)&topo_node->object.transform)))
1684 hr = session_set_transform_stream_info(topo_node);
1686 else
1687 WARN("Failed to get IMFTransform for MFT node, hr %#lx.\n", hr);
1689 break;
1690 case MF_TOPOLOGY_TEE_NODE:
1691 FIXME("Unsupported node type %d.\n", topo_node->type);
1693 break;
1694 default:
1698 if (SUCCEEDED(hr))
1699 list_add_tail(&session->presentation.nodes, &topo_node->entry);
1700 else
1701 release_topo_node(topo_node);
1703 return hr;
1706 static HRESULT session_collect_nodes(struct media_session *session)
1708 IMFTopology *topology = session->presentation.current_topology;
1709 IMFTopologyNode *node;
1710 WORD i, count = 0;
1711 HRESULT hr;
1713 if (!list_empty(&session->presentation.nodes))
1714 return S_OK;
1716 if (FAILED(hr = IMFTopology_GetNodeCount(topology, &count)))
1717 return hr;
1719 for (i = 0; i < count; ++i)
1721 if (FAILED(hr = IMFTopology_GetNode(topology, i, &node)))
1723 WARN("Failed to get node %u.\n", i);
1724 break;
1727 hr = session_append_node(session, node);
1728 IMFTopologyNode_Release(node);
1729 if (FAILED(hr))
1731 WARN("Failed to add node %u.\n", i);
1732 break;
1736 return hr;
1739 static HRESULT session_set_current_topology(struct media_session *session, IMFTopology *topology)
1741 struct media_source *source;
1742 DWORD caps, object_flags;
1743 struct media_sink *sink;
1744 struct topo_node *node;
1745 IMFMediaEvent *event;
1746 HRESULT hr;
1748 if (FAILED(hr = IMFTopology_CloneFrom(session->presentation.current_topology, topology)))
1750 WARN("Failed to clone topology, hr %#lx.\n", hr);
1751 return hr;
1754 session_collect_nodes(session);
1756 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
1758 if (node->type == MF_TOPOLOGY_TRANSFORM_NODE)
1760 if (FAILED(hr = IMFTransform_ProcessMessage(node->object.transform,
1761 MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0)))
1762 return hr;
1766 /* FIXME: attributes are all zero for now */
1767 if (SUCCEEDED(MFCreateMediaEvent(MESessionNotifyPresentationTime, &GUID_NULL, S_OK, NULL, &event)))
1769 IMFMediaEvent_SetUINT64(event, &MF_EVENT_START_PRESENTATION_TIME, 0);
1770 IMFMediaEvent_SetUINT64(event, &MF_EVENT_PRESENTATION_TIME_OFFSET, 0);
1771 IMFMediaEvent_SetUINT64(event, &MF_EVENT_START_PRESENTATION_TIME_AT_OUTPUT, 0);
1773 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
1774 IMFMediaEvent_Release(event);
1777 /* Update session caps. */
1778 caps = MFSESSIONCAP_START | MFSESSIONCAP_SEEK | MFSESSIONCAP_RATE_FORWARD | MFSESSIONCAP_RATE_REVERSE |
1779 MFSESSIONCAP_DOES_NOT_USE_NETWORK;
1781 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
1783 if (!caps)
1784 break;
1786 object_flags = 0;
1787 if (SUCCEEDED(IMFMediaSource_GetCharacteristics(source->source, &object_flags)))
1789 if (!(object_flags & MFMEDIASOURCE_DOES_NOT_USE_NETWORK))
1790 caps &= ~MFSESSIONCAP_DOES_NOT_USE_NETWORK;
1791 if (!(object_flags & MFMEDIASOURCE_CAN_SEEK))
1792 caps &= ~MFSESSIONCAP_SEEK;
1795 /* Mask unsupported rate caps. */
1797 caps &= session_get_object_rate_caps(source->object)
1798 | ~(MFSESSIONCAP_RATE_FORWARD | MFSESSIONCAP_RATE_REVERSE);
1801 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
1803 if (!caps)
1804 break;
1806 object_flags = 0;
1807 if (SUCCEEDED(IMFMediaSink_GetCharacteristics(sink->sink, &object_flags)))
1809 if (!(object_flags & MEDIASINK_RATELESS))
1810 caps &= session_get_object_rate_caps(sink->object)
1811 | ~(MFSESSIONCAP_RATE_FORWARD | MFSESSIONCAP_RATE_REVERSE);
1815 session_set_caps(session, caps);
1817 return S_OK;
1820 static void session_set_topology(struct media_session *session, DWORD flags, IMFTopology *topology)
1822 IMFTopology *resolved_topology = NULL;
1823 HRESULT hr = S_OK;
1825 /* Resolve unless claimed to be full. */
1826 if (!(flags & MFSESSION_SETTOPOLOGY_CLEAR_CURRENT) && topology)
1828 if (!(flags & MFSESSION_SETTOPOLOGY_NORESOLUTION))
1830 hr = session_bind_output_nodes(topology);
1832 if (SUCCEEDED(hr))
1833 hr = IMFTopoLoader_Load(session->topo_loader, topology, &resolved_topology, NULL /* FIXME? */);
1834 if (SUCCEEDED(hr))
1835 hr = session_init_media_types(resolved_topology);
1837 if (SUCCEEDED(hr))
1839 topology = resolved_topology;
1844 if (flags & MFSESSION_SETTOPOLOGY_CLEAR_CURRENT)
1846 if ((topology && topology == session->presentation.current_topology) || !topology)
1848 /* FIXME: stop current topology, queue next one. */
1849 session_clear_presentation(session);
1851 else
1852 hr = S_FALSE;
1854 topology = NULL;
1856 else if (topology && flags & MFSESSION_SETTOPOLOGY_IMMEDIATE)
1858 session_clear_queued_topologies(session);
1859 session_clear_presentation(session);
1862 session_raise_topology_set(session, topology, hr);
1864 /* With no current topology set it right away, otherwise queue. */
1865 if (topology)
1867 struct queued_topology *queued_topology;
1869 if ((queued_topology = calloc(1, sizeof(*queued_topology))))
1871 queued_topology->topology = topology;
1872 IMFTopology_AddRef(queued_topology->topology);
1874 list_add_tail(&session->topologies, &queued_topology->entry);
1877 if (session->presentation.topo_status == MF_TOPOSTATUS_INVALID)
1879 hr = session_set_current_topology(session, topology);
1880 session_set_topo_status(session, hr, MF_TOPOSTATUS_READY);
1881 if (session->quality_manager)
1882 IMFQualityManager_NotifyTopology(session->quality_manager, topology);
1886 if (resolved_topology)
1887 IMFTopology_Release(resolved_topology);
1890 static HRESULT WINAPI mfsession_QueryInterface(IMFMediaSession *iface, REFIID riid, void **out)
1892 struct media_session *session = impl_from_IMFMediaSession(iface);
1894 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out);
1896 *out = NULL;
1898 if (IsEqualIID(riid, &IID_IMFMediaSession) ||
1899 IsEqualIID(riid, &IID_IMFMediaEventGenerator) ||
1900 IsEqualIID(riid, &IID_IUnknown))
1902 *out = &session->IMFMediaSession_iface;
1904 else if (IsEqualIID(riid, &IID_IMFGetService))
1906 *out = &session->IMFGetService_iface;
1908 else if (IsEqualIID(riid, &IID_IMFRateSupport))
1910 *out = &session->IMFRateSupport_iface;
1912 else if (IsEqualIID(riid, &IID_IMFRateControl))
1914 *out = &session->IMFRateControl_iface;
1917 if (*out)
1919 IMFMediaSession_AddRef(iface);
1920 return S_OK;
1923 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
1924 return E_NOINTERFACE;
1927 static ULONG WINAPI mfsession_AddRef(IMFMediaSession *iface)
1929 struct media_session *session = impl_from_IMFMediaSession(iface);
1930 ULONG refcount = InterlockedIncrement(&session->refcount);
1932 TRACE("%p, refcount %lu.\n", iface, refcount);
1934 return refcount;
1937 static ULONG WINAPI mfsession_Release(IMFMediaSession *iface)
1939 struct media_session *session = impl_from_IMFMediaSession(iface);
1940 ULONG refcount = InterlockedDecrement(&session->refcount);
1942 TRACE("%p, refcount %lu.\n", iface, refcount);
1944 if (!refcount)
1946 session_clear_queued_topologies(session);
1947 session_clear_presentation(session);
1948 session_clear_command_list(session);
1949 if (session->presentation.current_topology)
1950 IMFTopology_Release(session->presentation.current_topology);
1951 if (session->event_queue)
1952 IMFMediaEventQueue_Release(session->event_queue);
1953 if (session->clock)
1954 IMFPresentationClock_Release(session->clock);
1955 if (session->system_time_source)
1956 IMFPresentationTimeSource_Release(session->system_time_source);
1957 if (session->clock_rate_control)
1958 IMFRateControl_Release(session->clock_rate_control);
1959 if (session->topo_loader)
1960 IMFTopoLoader_Release(session->topo_loader);
1961 if (session->quality_manager)
1962 IMFQualityManager_Release(session->quality_manager);
1963 DeleteCriticalSection(&session->cs);
1964 free(session);
1967 return refcount;
1970 static HRESULT WINAPI mfsession_GetEvent(IMFMediaSession *iface, DWORD flags, IMFMediaEvent **event)
1972 struct media_session *session = impl_from_IMFMediaSession(iface);
1974 TRACE("%p, %#lx, %p.\n", iface, flags, event);
1976 return IMFMediaEventQueue_GetEvent(session->event_queue, flags, event);
1979 static HRESULT WINAPI mfsession_BeginGetEvent(IMFMediaSession *iface, IMFAsyncCallback *callback, IUnknown *state)
1981 struct media_session *session = impl_from_IMFMediaSession(iface);
1983 TRACE("%p, %p, %p.\n", iface, callback, state);
1985 return IMFMediaEventQueue_BeginGetEvent(session->event_queue, callback, state);
1988 static HRESULT WINAPI mfsession_EndGetEvent(IMFMediaSession *iface, IMFAsyncResult *result, IMFMediaEvent **event)
1990 struct media_session *session = impl_from_IMFMediaSession(iface);
1992 TRACE("%p, %p, %p.\n", iface, result, event);
1994 return IMFMediaEventQueue_EndGetEvent(session->event_queue, result, event);
1997 static HRESULT WINAPI mfsession_QueueEvent(IMFMediaSession *iface, MediaEventType event_type, REFGUID ext_type,
1998 HRESULT hr, const PROPVARIANT *value)
2000 struct media_session *session = impl_from_IMFMediaSession(iface);
2002 TRACE("%p, %ld, %s, %#lx, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value);
2004 return IMFMediaEventQueue_QueueEventParamVar(session->event_queue, event_type, ext_type, hr, value);
2007 static HRESULT session_check_stream_descriptor(IMFPresentationDescriptor *pd, IMFStreamDescriptor *sd)
2009 IMFStreamDescriptor *selected_sd;
2010 DWORD i, count;
2011 BOOL selected;
2012 HRESULT hr;
2014 if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorCount(pd, &count)))
2016 WARN("Failed to get stream descriptor count, hr %#lx.\n", hr);
2017 return MF_E_TOPO_STREAM_DESCRIPTOR_NOT_SELECTED;
2020 for (i = 0; i < count; ++i)
2022 if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, i,
2023 &selected, &selected_sd)))
2025 WARN("Failed to get stream descriptor %lu, hr %#lx.\n", i, hr);
2026 return MF_E_TOPO_STREAM_DESCRIPTOR_NOT_SELECTED;
2028 IMFStreamDescriptor_Release(selected_sd);
2030 if (selected_sd == sd)
2032 if (selected)
2033 return S_OK;
2035 WARN("Presentation descriptor %p stream %p is not selected.\n", pd, sd);
2036 return MF_E_TOPO_STREAM_DESCRIPTOR_NOT_SELECTED;
2040 WARN("Failed to find stream descriptor %lu, hr %#lx.\n", i, hr);
2041 return MF_E_TOPO_STREAM_DESCRIPTOR_NOT_SELECTED;
2044 static HRESULT session_check_topology(IMFTopology *topology)
2046 MF_TOPOLOGY_TYPE node_type;
2047 IMFTopologyNode *node;
2048 WORD node_count, i;
2049 HRESULT hr;
2051 if (!topology)
2052 return S_OK;
2054 if (FAILED(IMFTopology_GetNodeCount(topology, &node_count))
2055 || node_count == 0)
2056 return E_INVALIDARG;
2058 for (i = 0; i < node_count; ++i)
2060 if (FAILED(hr = IMFTopology_GetNode(topology, i, &node)))
2061 break;
2063 if (FAILED(hr = IMFTopologyNode_GetNodeType(node, &node_type)))
2065 IMFTopologyNode_Release(node);
2066 break;
2069 switch (node_type)
2071 case MF_TOPOLOGY_SOURCESTREAM_NODE:
2073 IMFPresentationDescriptor *pd;
2074 IMFStreamDescriptor *sd;
2075 IMFMediaSource *source;
2077 if (FAILED(hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_SOURCE, &IID_IMFMediaSource,
2078 (void **)&source)))
2080 WARN("Missing MF_TOPONODE_SOURCE, hr %#lx.\n", hr);
2081 IMFTopologyNode_Release(node);
2082 return MF_E_TOPO_MISSING_SOURCE;
2084 IMFMediaSource_Release(source);
2086 if (FAILED(hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR,
2087 &IID_IMFPresentationDescriptor, (void **)&pd)))
2089 WARN("Missing MF_TOPONODE_PRESENTATION_DESCRIPTOR, hr %#lx.\n", hr);
2090 IMFTopologyNode_Release(node);
2091 return MF_E_TOPO_MISSING_PRESENTATION_DESCRIPTOR;
2094 if (FAILED(hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_STREAM_DESCRIPTOR,
2095 &IID_IMFStreamDescriptor, (void **)&sd)))
2097 WARN("Missing MF_TOPONODE_STREAM_DESCRIPTOR, hr %#lx.\n", hr);
2098 IMFPresentationDescriptor_Release(pd);
2099 IMFTopologyNode_Release(node);
2100 return MF_E_TOPO_MISSING_STREAM_DESCRIPTOR;
2103 hr = session_check_stream_descriptor(pd, sd);
2104 IMFPresentationDescriptor_Release(pd);
2105 IMFStreamDescriptor_Release(sd);
2106 if (FAILED(hr))
2108 IMFTopologyNode_Release(node);
2109 return hr;
2112 break;
2115 default:
2116 break;
2119 IMFTopologyNode_Release(node);
2122 return hr;
2125 static HRESULT WINAPI mfsession_SetTopology(IMFMediaSession *iface, DWORD flags, IMFTopology *topology)
2127 struct media_session *session = impl_from_IMFMediaSession(iface);
2128 struct session_op *op;
2129 HRESULT hr;
2131 TRACE("%p, %#lx, %p.\n", iface, flags, topology);
2133 if (FAILED(hr = session_check_topology(topology)))
2134 return hr;
2136 if (FAILED(hr = create_session_op(SESSION_CMD_SET_TOPOLOGY, &op)))
2137 return hr;
2139 op->set_topology.flags = flags;
2140 op->set_topology.topology = topology;
2141 if (op->set_topology.topology)
2142 IMFTopology_AddRef(op->set_topology.topology);
2144 hr = session_submit_command(session, op);
2145 IUnknown_Release(&op->IUnknown_iface);
2147 return hr;
2150 static HRESULT WINAPI mfsession_ClearTopologies(IMFMediaSession *iface)
2152 struct media_session *session = impl_from_IMFMediaSession(iface);
2154 TRACE("%p.\n", iface);
2156 return session_submit_simple_command(session, SESSION_CMD_CLEAR_TOPOLOGIES);
2159 static HRESULT WINAPI mfsession_Start(IMFMediaSession *iface, const GUID *format, const PROPVARIANT *start_position)
2161 struct media_session *session = impl_from_IMFMediaSession(iface);
2162 struct session_op *op;
2163 HRESULT hr;
2165 TRACE("%p, %s, %s.\n", iface, debugstr_guid(format), debugstr_propvar(start_position));
2167 if (!start_position)
2168 return E_POINTER;
2170 if (FAILED(hr = create_session_op(SESSION_CMD_START, &op)))
2171 return hr;
2173 op->start.time_format = format ? *format : GUID_NULL;
2174 hr = PropVariantCopy(&op->start.start_position, start_position);
2176 if (SUCCEEDED(hr))
2177 hr = session_submit_command(session, op);
2179 IUnknown_Release(&op->IUnknown_iface);
2180 return hr;
2183 static HRESULT WINAPI mfsession_Pause(IMFMediaSession *iface)
2185 struct media_session *session = impl_from_IMFMediaSession(iface);
2187 TRACE("%p.\n", iface);
2189 return session_submit_simple_command(session, SESSION_CMD_PAUSE);
2192 static HRESULT WINAPI mfsession_Stop(IMFMediaSession *iface)
2194 struct media_session *session = impl_from_IMFMediaSession(iface);
2196 TRACE("%p.\n", iface);
2198 return session_submit_simple_command(session, SESSION_CMD_STOP);
2201 static HRESULT WINAPI mfsession_Close(IMFMediaSession *iface)
2203 struct media_session *session = impl_from_IMFMediaSession(iface);
2205 TRACE("%p.\n", iface);
2207 return session_submit_simple_command(session, SESSION_CMD_CLOSE);
2210 static HRESULT WINAPI mfsession_Shutdown(IMFMediaSession *iface)
2212 struct media_session *session = impl_from_IMFMediaSession(iface);
2213 HRESULT hr = S_OK;
2215 TRACE("%p.\n", iface);
2217 EnterCriticalSection(&session->cs);
2218 if (SUCCEEDED(hr = session_is_shut_down(session)))
2220 session->state = SESSION_STATE_SHUT_DOWN;
2221 IMFMediaEventQueue_Shutdown(session->event_queue);
2222 if (session->quality_manager)
2223 IMFQualityManager_Shutdown(session->quality_manager);
2224 MFShutdownObject((IUnknown *)session->clock);
2225 IMFPresentationClock_Release(session->clock);
2226 session->clock = NULL;
2227 session_clear_presentation(session);
2228 session_clear_queued_topologies(session);
2229 session_submit_simple_command(session, SESSION_CMD_SHUTDOWN);
2231 LeaveCriticalSection(&session->cs);
2233 return hr;
2236 static HRESULT WINAPI mfsession_GetClock(IMFMediaSession *iface, IMFClock **clock)
2238 struct media_session *session = impl_from_IMFMediaSession(iface);
2239 HRESULT hr;
2241 TRACE("%p, %p.\n", iface, clock);
2243 EnterCriticalSection(&session->cs);
2244 if (SUCCEEDED(hr = session_is_shut_down(session)))
2246 *clock = (IMFClock *)session->clock;
2247 IMFClock_AddRef(*clock);
2249 LeaveCriticalSection(&session->cs);
2251 return hr;
2254 static HRESULT WINAPI mfsession_GetSessionCapabilities(IMFMediaSession *iface, DWORD *caps)
2256 struct media_session *session = impl_from_IMFMediaSession(iface);
2257 HRESULT hr = S_OK;
2259 TRACE("%p, %p.\n", iface, caps);
2261 if (!caps)
2262 return E_POINTER;
2264 EnterCriticalSection(&session->cs);
2265 if (SUCCEEDED(hr = session_is_shut_down(session)))
2266 *caps = session->caps;
2267 LeaveCriticalSection(&session->cs);
2269 return hr;
2272 static HRESULT WINAPI mfsession_GetFullTopology(IMFMediaSession *iface, DWORD flags, TOPOID id, IMFTopology **topology)
2274 struct media_session *session = impl_from_IMFMediaSession(iface);
2275 struct queued_topology *queued;
2276 TOPOID topo_id;
2277 HRESULT hr;
2279 TRACE("%p, %#lx, %s, %p.\n", iface, flags, wine_dbgstr_longlong(id), topology);
2281 *topology = NULL;
2283 EnterCriticalSection(&session->cs);
2285 if (SUCCEEDED(hr = session_is_shut_down(session)))
2287 if (flags & MFSESSION_GETFULLTOPOLOGY_CURRENT)
2289 if (session->presentation.topo_status != MF_TOPOSTATUS_INVALID)
2290 *topology = session->presentation.current_topology;
2291 else
2292 hr = MF_E_INVALIDREQUEST;
2294 else
2296 LIST_FOR_EACH_ENTRY(queued, &session->topologies, struct queued_topology, entry)
2298 if (SUCCEEDED(IMFTopology_GetTopologyID(queued->topology, &topo_id)) && topo_id == id)
2300 *topology = queued->topology;
2301 break;
2306 if (*topology)
2307 IMFTopology_AddRef(*topology);
2310 LeaveCriticalSection(&session->cs);
2312 return hr;
2315 static const IMFMediaSessionVtbl mfmediasessionvtbl =
2317 mfsession_QueryInterface,
2318 mfsession_AddRef,
2319 mfsession_Release,
2320 mfsession_GetEvent,
2321 mfsession_BeginGetEvent,
2322 mfsession_EndGetEvent,
2323 mfsession_QueueEvent,
2324 mfsession_SetTopology,
2325 mfsession_ClearTopologies,
2326 mfsession_Start,
2327 mfsession_Pause,
2328 mfsession_Stop,
2329 mfsession_Close,
2330 mfsession_Shutdown,
2331 mfsession_GetClock,
2332 mfsession_GetSessionCapabilities,
2333 mfsession_GetFullTopology,
2336 static HRESULT WINAPI session_get_service_QueryInterface(IMFGetService *iface, REFIID riid, void **obj)
2338 struct media_session *session = impl_from_IMFGetService(iface);
2339 return IMFMediaSession_QueryInterface(&session->IMFMediaSession_iface, riid, obj);
2342 static ULONG WINAPI session_get_service_AddRef(IMFGetService *iface)
2344 struct media_session *session = impl_from_IMFGetService(iface);
2345 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
2348 static ULONG WINAPI session_get_service_Release(IMFGetService *iface)
2350 struct media_session *session = impl_from_IMFGetService(iface);
2351 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
2354 typedef BOOL (*p_renderer_node_test_func)(IMFMediaSink *sink);
2356 static BOOL session_video_renderer_test_func(IMFMediaSink *sink)
2358 IUnknown *obj;
2359 HRESULT hr;
2361 /* Use first sink to support IMFVideoRenderer. */
2362 hr = IMFMediaSink_QueryInterface(sink, &IID_IMFVideoRenderer, (void **)&obj);
2363 if (obj)
2364 IUnknown_Release(obj);
2366 return hr == S_OK;
2369 static BOOL session_audio_renderer_test_func(IMFMediaSink *sink)
2371 return mf_is_sar_sink(sink);
2374 static HRESULT session_get_renderer_node_service(struct media_session *session,
2375 p_renderer_node_test_func node_test_func, REFGUID service, REFIID riid, void **obj)
2377 HRESULT hr = E_NOINTERFACE;
2378 IMFStreamSink *stream_sink;
2379 IMFTopologyNode *node;
2380 IMFCollection *nodes;
2381 IMFMediaSink *sink;
2382 unsigned int i = 0;
2384 if (session->presentation.current_topology)
2386 if (SUCCEEDED(IMFTopology_GetOutputNodeCollection(session->presentation.current_topology,
2387 &nodes)))
2389 while (IMFCollection_GetElement(nodes, i++, (IUnknown **)&node) == S_OK)
2391 if (SUCCEEDED(topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink)))
2393 if (SUCCEEDED(IMFStreamSink_GetMediaSink(stream_sink, &sink)))
2395 if (node_test_func(sink))
2397 if (FAILED(hr = MFGetService((IUnknown *)sink, service, riid, obj)))
2398 WARN("Failed to get service from renderer node, %#lx.\n", hr);
2400 IMFMediaSink_Release(sink);
2402 IMFStreamSink_Release(stream_sink);
2405 IMFTopologyNode_Release(node);
2407 if (*obj)
2408 break;
2411 IMFCollection_Release(nodes);
2415 return hr;
2418 static HRESULT session_get_audio_render_service(struct media_session *session, REFGUID service,
2419 REFIID riid, void **obj)
2421 return session_get_renderer_node_service(session, session_audio_renderer_test_func,
2422 service, riid, obj);
2425 static HRESULT session_get_video_render_service(struct media_session *session, REFGUID service,
2426 REFIID riid, void **obj)
2428 return session_get_renderer_node_service(session, session_video_renderer_test_func,
2429 service, riid, obj);
2432 static HRESULT WINAPI session_get_service_GetService(IMFGetService *iface, REFGUID service, REFIID riid, void **obj)
2434 struct media_session *session = impl_from_IMFGetService(iface);
2435 HRESULT hr = S_OK;
2437 TRACE("%p, %s, %s, %p.\n", iface, debugstr_guid(service), debugstr_guid(riid), obj);
2439 *obj = NULL;
2441 EnterCriticalSection(&session->cs);
2442 if (FAILED(hr = session_is_shut_down(session)))
2445 else if (IsEqualGUID(service, &MF_RATE_CONTROL_SERVICE))
2447 if (IsEqualIID(riid, &IID_IMFRateSupport))
2449 *obj = &session->IMFRateSupport_iface;
2451 else if (IsEqualIID(riid, &IID_IMFRateControl))
2453 *obj = &session->IMFRateControl_iface;
2455 else
2456 hr = E_NOINTERFACE;
2458 if (*obj)
2459 IUnknown_AddRef((IUnknown *)*obj);
2461 else if (IsEqualGUID(service, &MF_LOCAL_MFT_REGISTRATION_SERVICE))
2463 hr = IMFLocalMFTRegistration_QueryInterface(&local_mft_registration, riid, obj);
2465 else if (IsEqualGUID(service, &MF_TOPONODE_ATTRIBUTE_EDITOR_SERVICE))
2467 *obj = &session->IMFTopologyNodeAttributeEditor_iface;
2468 IUnknown_AddRef((IUnknown *)*obj);
2470 else if (IsEqualGUID(service, &MR_VIDEO_RENDER_SERVICE))
2472 hr = session_get_video_render_service(session, service, riid, obj);
2474 else if (IsEqualGUID(service, &MR_POLICY_VOLUME_SERVICE) ||
2475 IsEqualGUID(service, &MR_STREAM_VOLUME_SERVICE))
2477 hr = session_get_audio_render_service(session, service, riid, obj);
2479 else
2480 FIXME("Unsupported service %s.\n", debugstr_guid(service));
2482 LeaveCriticalSection(&session->cs);
2484 return hr;
2487 static const IMFGetServiceVtbl session_get_service_vtbl =
2489 session_get_service_QueryInterface,
2490 session_get_service_AddRef,
2491 session_get_service_Release,
2492 session_get_service_GetService,
2495 static HRESULT WINAPI session_commands_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
2497 if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
2498 IsEqualIID(riid, &IID_IUnknown))
2500 *obj = iface;
2501 IMFAsyncCallback_AddRef(iface);
2502 return S_OK;
2505 WARN("Unsupported %s.\n", debugstr_guid(riid));
2506 *obj = NULL;
2507 return E_NOINTERFACE;
2510 static ULONG WINAPI session_commands_callback_AddRef(IMFAsyncCallback *iface)
2512 struct media_session *session = impl_from_commands_callback_IMFAsyncCallback(iface);
2513 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
2516 static ULONG WINAPI session_commands_callback_Release(IMFAsyncCallback *iface)
2518 struct media_session *session = impl_from_commands_callback_IMFAsyncCallback(iface);
2519 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
2522 static HRESULT WINAPI session_commands_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
2524 return E_NOTIMPL;
2527 static void session_deliver_pending_samples(struct media_session *session, IMFTopologyNode *node);
2529 static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
2531 struct session_op *op = impl_op_from_IUnknown(IMFAsyncResult_GetStateNoAddRef(result));
2532 struct media_session *session = impl_from_commands_callback_IMFAsyncCallback(iface);
2534 TRACE("session %p, op %p, command %u.\n", session, op, op->command);
2536 EnterCriticalSection(&session->cs);
2538 if (session->presentation.flags & SESSION_FLAG_PENDING_COMMAND)
2540 WARN("session %p command is in progress, waiting for it to complete.\n", session);
2541 LeaveCriticalSection(&session->cs);
2542 return S_OK;
2545 list_remove(&op->entry);
2546 session->presentation.flags |= SESSION_FLAG_PENDING_COMMAND;
2548 switch (op->command)
2550 case SESSION_CMD_CLEAR_TOPOLOGIES:
2551 session_clear_topologies(session);
2552 break;
2553 case SESSION_CMD_SET_TOPOLOGY:
2554 session_set_topology(session, op->set_topology.flags, op->set_topology.topology);
2555 session_command_complete(session);
2556 break;
2557 case SESSION_CMD_START:
2558 session_start(session, &op->start.time_format, &op->start.start_position);
2559 break;
2560 case SESSION_CMD_PAUSE:
2561 session_pause(session);
2562 break;
2563 case SESSION_CMD_STOP:
2564 session_stop(session);
2565 break;
2566 case SESSION_CMD_CLOSE:
2567 session_close(session);
2568 break;
2569 case SESSION_CMD_SET_RATE:
2570 session_set_rate(session, op->set_rate.thin, op->set_rate.rate);
2571 break;
2572 case SESSION_CMD_SHUTDOWN:
2573 session_clear_command_list(session);
2574 session_command_complete(session);
2575 break;
2576 default:
2580 LeaveCriticalSection(&session->cs);
2582 IUnknown_Release(&op->IUnknown_iface);
2584 return S_OK;
2587 static const IMFAsyncCallbackVtbl session_commands_callback_vtbl =
2589 session_commands_callback_QueryInterface,
2590 session_commands_callback_AddRef,
2591 session_commands_callback_Release,
2592 session_commands_callback_GetParameters,
2593 session_commands_callback_Invoke,
2596 static HRESULT WINAPI session_sa_ready_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
2598 if (IsEqualIID(riid, &IID_IMFAsyncCallback)
2599 || IsEqualIID(riid, &IID_IUnknown))
2601 *obj = iface;
2602 IMFAsyncCallback_AddRef(iface);
2603 return S_OK;
2606 WARN("Unsupported %s.\n", debugstr_guid(riid));
2607 *obj = NULL;
2608 return E_NOINTERFACE;
2611 static ULONG WINAPI session_sa_ready_callback_AddRef(IMFAsyncCallback *iface)
2613 struct media_session *session = impl_from_sa_ready_callback_IMFAsyncCallback(iface);
2614 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
2617 static ULONG WINAPI session_sa_ready_callback_Release(IMFAsyncCallback *iface)
2619 struct media_session *session = impl_from_sa_ready_callback_IMFAsyncCallback(iface);
2620 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
2623 static HRESULT WINAPI session_sa_ready_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
2625 return E_NOTIMPL;
2628 static HRESULT WINAPI session_sa_ready_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
2630 IMFVideoSampleAllocatorNotify *notify = (IMFVideoSampleAllocatorNotify *)IMFAsyncResult_GetStateNoAddRef(result);
2631 struct topo_node *topo_node = impl_node_from_IMFVideoSampleAllocatorNotify(notify);
2632 struct media_session *session = impl_from_sa_ready_callback_IMFAsyncCallback(iface);
2633 IMFTopologyNode *upstream_node;
2634 DWORD upstream_output;
2636 EnterCriticalSection(&session->cs);
2638 if (topo_node->u.sink.requests)
2640 if (SUCCEEDED(IMFTopologyNode_GetInput(topo_node->node, 0, &upstream_node, &upstream_output)))
2642 session_deliver_pending_samples(session, upstream_node);
2643 IMFTopologyNode_Release(upstream_node);
2647 LeaveCriticalSection(&session->cs);
2649 return S_OK;
2652 static const IMFAsyncCallbackVtbl session_sa_ready_callback_vtbl =
2654 session_sa_ready_callback_QueryInterface,
2655 session_sa_ready_callback_AddRef,
2656 session_sa_ready_callback_Release,
2657 session_sa_ready_callback_GetParameters,
2658 session_sa_ready_callback_Invoke,
2661 static HRESULT WINAPI session_events_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
2663 if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
2664 IsEqualIID(riid, &IID_IUnknown))
2666 *obj = iface;
2667 IMFAsyncCallback_AddRef(iface);
2668 return S_OK;
2671 WARN("Unsupported %s.\n", debugstr_guid(riid));
2672 *obj = NULL;
2673 return E_NOINTERFACE;
2676 static ULONG WINAPI session_events_callback_AddRef(IMFAsyncCallback *iface)
2678 struct media_session *session = impl_from_events_callback_IMFAsyncCallback(iface);
2679 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
2682 static ULONG WINAPI session_events_callback_Release(IMFAsyncCallback *iface)
2684 struct media_session *session = impl_from_events_callback_IMFAsyncCallback(iface);
2685 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
2688 static HRESULT WINAPI session_events_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
2690 return E_NOTIMPL;
2693 static HRESULT session_add_media_stream(struct media_session *session, IMFMediaSource *source, IMFMediaStream *stream)
2695 struct topo_node *node;
2696 IMFStreamDescriptor *sd;
2697 DWORD stream_id = 0;
2698 HRESULT hr;
2700 if (FAILED(hr = IMFMediaStream_GetStreamDescriptor(stream, &sd)))
2701 return hr;
2703 hr = IMFStreamDescriptor_GetStreamIdentifier(sd, &stream_id);
2704 IMFStreamDescriptor_Release(sd);
2705 if (FAILED(hr))
2706 return hr;
2708 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
2710 if (node->type == MF_TOPOLOGY_SOURCESTREAM_NODE && node->u.source.source == source
2711 && node->u.source.stream_id == stream_id)
2713 if (node->object.source_stream)
2715 WARN("Node already has stream set.\n");
2716 return S_FALSE;
2719 node->object.source_stream = stream;
2720 IMFMediaStream_AddRef(node->object.source_stream);
2721 break;
2725 return S_OK;
2728 static BOOL session_is_source_nodes_state(struct media_session *session, enum object_state state)
2730 struct media_source *source;
2731 struct topo_node *node;
2733 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
2735 if (source->state != state)
2736 return FALSE;
2739 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
2741 if (node->type == MF_TOPOLOGY_SOURCESTREAM_NODE && node->state != state)
2742 return FALSE;
2745 return TRUE;
2748 static BOOL session_is_output_nodes_state(struct media_session *session, enum object_state state)
2750 struct topo_node *node;
2752 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
2754 if (node->type == MF_TOPOLOGY_OUTPUT_NODE && node->state != state)
2755 return FALSE;
2758 return TRUE;
2761 static enum object_state session_get_object_state_for_event(MediaEventType event)
2763 switch (event)
2765 case MESourceSeeked:
2766 case MEStreamSeeked:
2767 case MESourceStarted:
2768 case MEStreamStarted:
2769 case MEStreamSinkStarted:
2770 return OBJ_STATE_STARTED;
2771 case MESourcePaused:
2772 case MEStreamPaused:
2773 case MEStreamSinkPaused:
2774 return OBJ_STATE_PAUSED;
2775 case MESourceStopped:
2776 case MEStreamStopped:
2777 case MEStreamSinkStopped:
2778 return OBJ_STATE_STOPPED;
2779 case MEStreamSinkPrerolled:
2780 return OBJ_STATE_PREROLLED;
2781 default:
2782 return OBJ_STATE_INVALID;
2786 static HRESULT session_start_clock(struct media_session *session)
2788 LONGLONG start_offset = 0;
2789 HRESULT hr;
2791 if (IsEqualGUID(&session->presentation.time_format, &GUID_NULL))
2793 if (session->presentation.start_position.vt == VT_EMPTY)
2794 start_offset = PRESENTATION_CURRENT_POSITION;
2795 else if (session->presentation.start_position.vt == VT_I8)
2796 start_offset = session->presentation.start_position.hVal.QuadPart;
2797 else
2798 FIXME("Unhandled position type %d.\n", session->presentation.start_position.vt);
2800 else
2801 FIXME("Unhandled time format %s.\n", debugstr_guid(&session->presentation.time_format));
2803 if (FAILED(hr = IMFPresentationClock_Start(session->clock, start_offset)))
2804 WARN("Failed to start session clock, hr %#lx.\n", hr);
2806 return hr;
2809 static struct topo_node *session_get_node_object(struct media_session *session, IUnknown *object,
2810 MF_TOPOLOGY_TYPE node_type)
2812 struct topo_node *node = NULL;
2814 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
2816 if (node->type == node_type && object == node->object.object)
2817 break;
2820 return node;
2823 static BOOL session_set_node_object_state(struct media_session *session, IUnknown *object,
2824 MF_TOPOLOGY_TYPE node_type, enum object_state state)
2826 struct topo_node *node;
2827 BOOL changed = FALSE;
2829 if ((node = session_get_node_object(session, object, node_type)))
2831 changed = node->state != state;
2832 node->state = state;
2835 return changed;
2838 static void session_set_source_object_state(struct media_session *session, IUnknown *object,
2839 MediaEventType event_type)
2841 IMFStreamSink *stream_sink;
2842 struct media_source *src;
2843 struct media_sink *sink;
2844 enum object_state state;
2845 struct topo_node *node;
2846 BOOL changed = FALSE;
2847 DWORD i, count;
2848 HRESULT hr;
2850 if ((state = session_get_object_state_for_event(event_type)) == OBJ_STATE_INVALID)
2851 return;
2853 switch (event_type)
2855 case MESourceSeeked:
2856 case MESourceStarted:
2857 case MESourcePaused:
2858 case MESourceStopped:
2860 LIST_FOR_EACH_ENTRY(src, &session->presentation.sources, struct media_source, entry)
2862 if (object == src->object)
2864 changed = src->state != state;
2865 src->state = state;
2866 break;
2869 break;
2870 case MEStreamSeeked:
2871 case MEStreamStarted:
2872 case MEStreamPaused:
2873 case MEStreamStopped:
2875 changed = session_set_node_object_state(session, object, MF_TOPOLOGY_SOURCESTREAM_NODE, state);
2876 default:
2880 if (!changed)
2881 return;
2883 switch (session->state)
2885 case SESSION_STATE_STARTING_SOURCES:
2886 if (!session_is_source_nodes_state(session, OBJ_STATE_STARTED))
2887 break;
2889 session_set_topo_status(session, S_OK, MF_TOPOSTATUS_STARTED_SOURCE);
2891 session_set_presentation_clock(session);
2893 if (session->presentation.flags & SESSION_FLAG_NEEDS_PREROLL)
2895 MFTIME preroll_time = 0;
2897 if (session->presentation.start_position.vt == VT_I8)
2898 preroll_time = session->presentation.start_position.hVal.QuadPart;
2900 /* Mark stream sinks without prerolling support as PREROLLED to keep state test logic generic. */
2901 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
2903 if (sink->preroll)
2905 /* FIXME: abort and enter error state on failure. */
2906 if (FAILED(hr = IMFMediaSinkPreroll_NotifyPreroll(sink->preroll, preroll_time)))
2907 WARN("Preroll notification failed, hr %#lx.\n", hr);
2909 else
2911 if (SUCCEEDED(IMFMediaSink_GetStreamSinkCount(sink->sink, &count)))
2913 for (i = 0; i < count; ++i)
2915 if (SUCCEEDED(IMFMediaSink_GetStreamSinkByIndex(sink->sink, i, &stream_sink)))
2917 session_set_node_object_state(session, (IUnknown *)stream_sink, MF_TOPOLOGY_OUTPUT_NODE,
2918 OBJ_STATE_PREROLLED);
2919 IMFStreamSink_Release(stream_sink);
2925 session->state = SESSION_STATE_PREROLLING_SINKS;
2927 else if (SUCCEEDED(session_start_clock(session)))
2928 session->state = SESSION_STATE_STARTING_SINKS;
2930 break;
2931 case SESSION_STATE_PAUSING_SOURCES:
2932 if (!session_is_source_nodes_state(session, OBJ_STATE_PAUSED))
2933 break;
2935 session_set_paused(session, SESSION_STATE_PAUSED, S_OK);
2936 break;
2937 case SESSION_STATE_STOPPING_SOURCES:
2938 if (!session_is_source_nodes_state(session, OBJ_STATE_STOPPED))
2939 break;
2941 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
2943 switch (node->type)
2945 case MF_TOPOLOGY_OUTPUT_NODE:
2946 IMFStreamSink_Flush(node->object.sink_stream);
2947 break;
2948 case MF_TOPOLOGY_TRANSFORM_NODE:
2949 IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_COMMAND_FLUSH, 0);
2950 break;
2951 default:
2956 session_set_caps(session, session->caps & ~MFSESSIONCAP_PAUSE);
2958 if (session->presentation.flags & SESSION_FLAG_FINALIZE_SINKS)
2959 session_finalize_sinks(session);
2960 else
2961 session_set_stopped(session, S_OK);
2963 break;
2964 default:
2969 static void session_set_sink_stream_state(struct media_session *session, IMFStreamSink *stream,
2970 MediaEventType event_type)
2972 struct media_source *source;
2973 enum object_state state;
2974 HRESULT hr = S_OK;
2975 BOOL changed;
2977 if ((state = session_get_object_state_for_event(event_type)) == OBJ_STATE_INVALID)
2978 return;
2980 if (!(changed = session_set_node_object_state(session, (IUnknown *)stream, MF_TOPOLOGY_OUTPUT_NODE, state)))
2981 return;
2983 switch (session->state)
2985 case SESSION_STATE_PREROLLING_SINKS:
2986 if (!session_is_output_nodes_state(session, OBJ_STATE_PREROLLED))
2987 break;
2989 if (SUCCEEDED(session_start_clock(session)))
2990 session->state = SESSION_STATE_STARTING_SINKS;
2991 break;
2992 case SESSION_STATE_STARTING_SINKS:
2993 if (!session_is_output_nodes_state(session, OBJ_STATE_STARTED))
2994 break;
2996 session_set_started(session);
2997 break;
2998 case SESSION_STATE_PAUSING_SINKS:
2999 if (!session_is_output_nodes_state(session, OBJ_STATE_PAUSED))
3000 break;
3002 session->state = SESSION_STATE_PAUSING_SOURCES;
3004 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
3006 if (FAILED(hr = IMFMediaSource_Pause(source->source)))
3007 break;
3010 if (FAILED(hr))
3011 session_set_paused(session, SESSION_STATE_PAUSED, hr);
3013 break;
3014 case SESSION_STATE_STOPPING_SINKS:
3015 if (!session_is_output_nodes_state(session, OBJ_STATE_STOPPED))
3016 break;
3018 session->state = SESSION_STATE_STOPPING_SOURCES;
3020 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
3022 if (session->presentation.flags & SESSION_FLAG_END_OF_PRESENTATION)
3023 IMFMediaSource_Stop(source->source);
3024 else if (FAILED(hr = IMFMediaSource_Stop(source->source)))
3025 break;
3028 if (session->presentation.flags & SESSION_FLAG_END_OF_PRESENTATION || FAILED(hr))
3029 session_set_stopped(session, hr);
3031 break;
3032 default:
3037 static struct sample *transform_create_sample(IMFSample *sample)
3039 struct sample *sample_entry = calloc(1, sizeof(*sample_entry));
3041 if (sample_entry)
3043 sample_entry->sample = sample;
3044 if (sample_entry->sample)
3045 IMFSample_AddRef(sample_entry->sample);
3048 return sample_entry;
3051 static HRESULT transform_get_external_output_sample(const struct media_session *session, struct topo_node *transform,
3052 unsigned int output_index, const MFT_OUTPUT_STREAM_INFO *stream_info, IMFSample **sample)
3054 IMFTopologyNode *downstream_node;
3055 IMFMediaBuffer *buffer = NULL;
3056 struct topo_node *topo_node;
3057 unsigned int buffer_size;
3058 DWORD downstream_input;
3059 TOPOID node_id;
3060 HRESULT hr;
3062 if (FAILED(IMFTopologyNode_GetOutput(transform->node, output_index, &downstream_node, &downstream_input)))
3064 WARN("Failed to get connected node for output %u.\n", output_index);
3065 return MF_E_UNEXPECTED;
3068 IMFTopologyNode_GetTopoNodeID(downstream_node, &node_id);
3069 IMFTopologyNode_Release(downstream_node);
3071 topo_node = session_get_node_by_id(session, node_id);
3073 if (topo_node->type == MF_TOPOLOGY_OUTPUT_NODE && topo_node->u.sink.allocator)
3075 hr = IMFVideoSampleAllocator_AllocateSample(topo_node->u.sink.allocator, sample);
3077 else
3079 buffer_size = max(stream_info->cbSize, transform->u.transform.outputs[output_index].min_buffer_size);
3081 hr = MFCreateAlignedMemoryBuffer(buffer_size, stream_info->cbAlignment, &buffer);
3082 if (SUCCEEDED(hr))
3083 hr = MFCreateSample(sample);
3085 if (SUCCEEDED(hr))
3086 hr = IMFSample_AddBuffer(*sample, buffer);
3088 if (buffer)
3089 IMFMediaBuffer_Release(buffer);
3092 return hr;
3095 static HRESULT transform_node_pull_samples(const struct media_session *session, struct topo_node *node)
3097 MFT_OUTPUT_STREAM_INFO stream_info;
3098 MFT_OUTPUT_DATA_BUFFER *buffers;
3099 struct sample *queued_sample;
3100 HRESULT hr = E_UNEXPECTED;
3101 DWORD status = 0;
3102 unsigned int i;
3104 if (!(buffers = calloc(node->u.transform.output_count, sizeof(*buffers))))
3105 return E_OUTOFMEMORY;
3107 for (i = 0; i < node->u.transform.output_count; ++i)
3109 buffers[i].dwStreamID = transform_node_get_stream_id(node, TRUE, i);
3110 buffers[i].pSample = NULL;
3111 buffers[i].dwStatus = 0;
3112 buffers[i].pEvents = NULL;
3114 memset(&stream_info, 0, sizeof(stream_info));
3115 if (FAILED(hr = IMFTransform_GetOutputStreamInfo(node->object.transform, buffers[i].dwStreamID, &stream_info)))
3116 break;
3118 if (!(stream_info.dwFlags & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)))
3120 if (FAILED(hr = transform_get_external_output_sample(session, node, i, &stream_info, &buffers[i].pSample)))
3121 break;
3125 if (SUCCEEDED(hr))
3126 hr = IMFTransform_ProcessOutput(node->object.transform, 0, node->u.transform.output_count, buffers, &status);
3128 /* Collect returned samples for all streams. */
3129 for (i = 0; i < node->u.transform.output_count; ++i)
3131 if (buffers[i].pEvents)
3132 IMFCollection_Release(buffers[i].pEvents);
3134 if (SUCCEEDED(hr) && !(buffers[i].dwStatus & MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE))
3136 if (session->quality_manager)
3137 IMFQualityManager_NotifyProcessOutput(session->quality_manager, node->node, i, buffers[i].pSample);
3139 queued_sample = transform_create_sample(buffers[i].pSample);
3140 list_add_tail(&node->u.transform.outputs[i].samples, &queued_sample->entry);
3143 if (buffers[i].pSample)
3144 IMFSample_Release(buffers[i].pSample);
3147 free(buffers);
3149 return hr;
3152 static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input,
3153 IMFSample *sample)
3155 struct sample *sample_entry, *sample_entry2;
3156 DWORD stream_id, downstream_input;
3157 IMFTopologyNode *downstream_node;
3158 struct topo_node *topo_node;
3159 MF_TOPOLOGY_TYPE node_type;
3160 BOOL drain = FALSE;
3161 TOPOID node_id;
3162 unsigned int i;
3163 HRESULT hr;
3165 if (session->quality_manager)
3166 IMFQualityManager_NotifyProcessInput(session->quality_manager, node, input, sample);
3168 IMFTopologyNode_GetNodeType(node, &node_type);
3169 IMFTopologyNode_GetTopoNodeID(node, &node_id);
3171 topo_node = session_get_node_by_id(session, node_id);
3173 switch (node_type)
3175 case MF_TOPOLOGY_OUTPUT_NODE:
3176 if (topo_node->u.sink.requests)
3178 if (sample)
3180 if (FAILED(hr = IMFStreamSink_ProcessSample(topo_node->object.sink_stream, sample)))
3181 WARN("Stream sink failed to process sample, hr %#lx.\n", hr);
3183 else if (FAILED(hr = IMFStreamSink_PlaceMarker(topo_node->object.sink_stream, MFSTREAMSINK_MARKER_ENDOFSEGMENT,
3184 NULL, NULL)))
3186 WARN("Failed to place sink marker, hr %#lx.\n", hr);
3188 topo_node->u.sink.requests--;
3190 break;
3191 case MF_TOPOLOGY_TRANSFORM_NODE:
3193 transform_node_pull_samples(session, topo_node);
3195 sample_entry = transform_create_sample(sample);
3196 list_add_tail(&topo_node->u.transform.inputs[input].samples, &sample_entry->entry);
3198 for (i = 0; i < topo_node->u.transform.input_count; ++i)
3200 stream_id = transform_node_get_stream_id(topo_node, FALSE, i);
3201 LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &topo_node->u.transform.inputs[i].samples,
3202 struct sample, entry)
3204 if (sample_entry->sample)
3206 if ((hr = IMFTransform_ProcessInput(topo_node->object.transform, stream_id,
3207 sample_entry->sample, 0)) == MF_E_NOTACCEPTING)
3208 break;
3209 if (FAILED(hr))
3210 WARN("Failed to process input for stream %u/%lu, hr %#lx.\n", i, stream_id, hr);
3211 transform_release_sample(sample_entry);
3213 else
3215 transform_stream_drop_samples(&topo_node->u.transform.inputs[i]);
3216 drain = TRUE;
3221 if (drain)
3223 if (FAILED(hr = IMFTransform_ProcessMessage(topo_node->object.transform, MFT_MESSAGE_COMMAND_DRAIN, 0)))
3224 WARN("Drain command failed for transform, hr %#lx.\n", hr);
3227 transform_node_pull_samples(session, topo_node);
3229 /* Remaining unprocessed input has been discarded, now queue markers for every output. */
3230 if (drain)
3232 for (i = 0; i < topo_node->u.transform.output_count; ++i)
3234 if ((sample_entry = transform_create_sample(NULL)))
3235 list_add_tail(&topo_node->u.transform.outputs[i].samples, &sample_entry->entry);
3239 /* Push down all available output. */
3240 for (i = 0; i < topo_node->u.transform.output_count; ++i)
3242 if (FAILED(IMFTopologyNode_GetOutput(node, i, &downstream_node, &downstream_input)))
3244 WARN("Failed to get connected node for output %u.\n", i);
3245 continue;
3248 LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &topo_node->u.transform.outputs[i].samples,
3249 struct sample, entry)
3251 if (!topo_node->u.transform.outputs[i].requests)
3252 break;
3254 session_deliver_sample_to_node(session, downstream_node, downstream_input, sample_entry->sample);
3255 topo_node->u.transform.outputs[i].requests--;
3257 transform_release_sample(sample_entry);
3260 IMFTopologyNode_Release(downstream_node);
3263 break;
3264 case MF_TOPOLOGY_TEE_NODE:
3265 FIXME("Unhandled downstream node type %d.\n", node_type);
3266 break;
3267 default:
3272 static void session_deliver_pending_samples(struct media_session *session, IMFTopologyNode *node)
3274 struct sample *sample_entry, *sample_entry2;
3275 IMFTopologyNode *downstream_node;
3276 struct topo_node *topo_node;
3277 MF_TOPOLOGY_TYPE node_type;
3278 DWORD downstream_input;
3279 TOPOID node_id;
3280 unsigned int i;
3282 IMFTopologyNode_GetNodeType(node, &node_type);
3283 IMFTopologyNode_GetTopoNodeID(node, &node_id);
3285 topo_node = session_get_node_by_id(session, node_id);
3287 switch (node_type)
3289 case MF_TOPOLOGY_TRANSFORM_NODE:
3291 transform_node_pull_samples(session, topo_node);
3293 /* Push down all available output. */
3294 for (i = 0; i < topo_node->u.transform.output_count; ++i)
3296 if (FAILED(IMFTopologyNode_GetOutput(node, i, &downstream_node, &downstream_input)))
3298 WARN("Failed to get connected node for output %u.\n", i);
3299 continue;
3302 LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &topo_node->u.transform.outputs[i].samples,
3303 struct sample, entry)
3305 if (!topo_node->u.transform.outputs[i].requests)
3306 break;
3308 session_deliver_sample_to_node(session, downstream_node, downstream_input, sample_entry->sample);
3309 topo_node->u.transform.outputs[i].requests--;
3311 transform_release_sample(sample_entry);
3314 IMFTopologyNode_Release(downstream_node);
3316 break;
3317 default:
3318 FIXME("Unexpected node type %u.\n", node_type);
3323 static HRESULT session_request_sample_from_node(struct media_session *session, IMFTopologyNode *node, DWORD output)
3325 IMFTopologyNode *downstream_node, *upstream_node;
3326 DWORD downstream_input, upstream_output;
3327 struct topo_node *topo_node;
3328 MF_TOPOLOGY_TYPE node_type;
3329 struct sample *sample;
3330 TOPOID node_id;
3331 HRESULT hr;
3333 IMFTopologyNode_GetNodeType(node, &node_type);
3334 IMFTopologyNode_GetTopoNodeID(node, &node_id);
3336 topo_node = session_get_node_by_id(session, node_id);
3338 switch (node_type)
3340 case MF_TOPOLOGY_SOURCESTREAM_NODE:
3341 if (FAILED(hr = IMFMediaStream_RequestSample(topo_node->object.source_stream, NULL)))
3342 WARN("Sample request failed, hr %#lx.\n", hr);
3343 break;
3344 case MF_TOPOLOGY_TRANSFORM_NODE:
3346 if (list_empty(&topo_node->u.transform.outputs[output].samples))
3348 /* Forward request to upstream node. */
3349 if (SUCCEEDED(hr = IMFTopologyNode_GetInput(node, 0 /* FIXME */, &upstream_node, &upstream_output)))
3351 if (SUCCEEDED(hr = session_request_sample_from_node(session, upstream_node, upstream_output)))
3352 topo_node->u.transform.outputs[output].requests++;
3353 IMFTopologyNode_Release(upstream_node);
3356 else
3358 if (SUCCEEDED(hr = IMFTopologyNode_GetOutput(node, output, &downstream_node, &downstream_input)))
3360 sample = LIST_ENTRY(list_head(&topo_node->u.transform.outputs[output].samples), struct sample, entry);
3361 session_deliver_sample_to_node(session, downstream_node, downstream_input, sample->sample);
3362 transform_release_sample(sample);
3363 IMFTopologyNode_Release(downstream_node);
3367 break;
3368 case MF_TOPOLOGY_TEE_NODE:
3369 FIXME("Unhandled upstream node type %d.\n", node_type);
3370 default:
3371 hr = E_UNEXPECTED;
3374 return hr;
3377 static void session_request_sample(struct media_session *session, IMFStreamSink *sink_stream)
3379 struct topo_node *sink_node = NULL, *node;
3380 IMFTopologyNode *upstream_node;
3381 DWORD upstream_output;
3382 HRESULT hr;
3384 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
3386 if (node->type == MF_TOPOLOGY_OUTPUT_NODE && node->object.sink_stream == sink_stream)
3388 sink_node = node;
3389 break;
3393 if (!sink_node)
3394 return;
3396 if (FAILED(hr = IMFTopologyNode_GetInput(sink_node->node, 0, &upstream_node, &upstream_output)))
3398 WARN("Failed to get upstream node connection, hr %#lx.\n", hr);
3399 return;
3402 sink_node->u.sink.requests++;
3403 if (FAILED(session_request_sample_from_node(session, upstream_node, upstream_output)))
3404 sink_node->u.sink.requests--;
3405 IMFTopologyNode_Release(upstream_node);
3408 static void session_deliver_sample(struct media_session *session, IMFMediaStream *stream, const PROPVARIANT *value)
3410 struct topo_node *source_node = NULL, *node;
3411 IMFTopologyNode *downstream_node;
3412 DWORD downstream_input;
3413 HRESULT hr;
3415 if (value && (value->vt != VT_UNKNOWN || !value->punkVal))
3417 WARN("Unexpected value type %d.\n", value->vt);
3418 return;
3421 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
3423 if (node->type == MF_TOPOLOGY_SOURCESTREAM_NODE && node->object.source_stream == stream)
3425 source_node = node;
3426 break;
3430 if (!source_node)
3431 return;
3433 if (!value)
3434 source_node->flags |= TOPO_NODE_END_OF_STREAM;
3436 if (FAILED(hr = IMFTopologyNode_GetOutput(source_node->node, 0, &downstream_node, &downstream_input)))
3438 WARN("Failed to get downstream node connection, hr %#lx.\n", hr);
3439 return;
3442 session_deliver_sample_to_node(session, downstream_node, downstream_input, value ? (IMFSample *)value->punkVal : NULL);
3443 IMFTopologyNode_Release(downstream_node);
3446 static void session_sink_invalidated(struct media_session *session, IMFMediaEvent *event, IMFStreamSink *sink)
3448 struct topo_node *node, *sink_node = NULL;
3449 HRESULT hr;
3451 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
3453 if (node->type == MF_TOPOLOGY_OUTPUT_NODE && node->object.sink_stream == sink)
3455 sink_node = node;
3456 break;
3460 if (!sink_node)
3461 return;
3463 if (!event)
3465 if (FAILED(hr = MFCreateMediaEvent(MESinkInvalidated, &GUID_NULL, S_OK, NULL, &event)))
3466 WARN("Failed to create event, hr %#lx.\n", hr);
3469 if (!event)
3470 return;
3472 IMFMediaEvent_SetUINT64(event, &MF_EVENT_OUTPUT_NODE, sink_node->node_id);
3473 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
3475 session_set_topo_status(session, S_OK, MF_TOPOSTATUS_ENDED);
3478 static BOOL session_nodes_is_mask_set(struct media_session *session, MF_TOPOLOGY_TYPE node_type, unsigned int flags)
3480 struct media_source *source;
3481 struct topo_node *node;
3483 if (node_type == MF_TOPOLOGY_MAX)
3485 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
3487 if ((source->flags & flags) != flags)
3488 return FALSE;
3491 else
3493 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
3495 if (node->type == node_type && (node->flags & flags) != flags)
3496 return FALSE;
3500 return TRUE;
3503 static void session_nodes_unset_mask(struct media_session *session, MF_TOPOLOGY_TYPE node_type, unsigned int flags)
3505 struct media_source *source;
3506 struct topo_node *node;
3508 if (node_type == MF_TOPOLOGY_MAX)
3510 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
3512 source->flags &= ~flags;
3515 else
3517 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
3519 if (node->type == node_type)
3521 node->flags &= ~flags;
3527 static void session_raise_end_of_presentation(struct media_session *session)
3529 if (!(session_nodes_is_mask_set(session, MF_TOPOLOGY_SOURCESTREAM_NODE, TOPO_NODE_END_OF_STREAM)))
3530 return;
3532 if (!(session->presentation.flags & SESSION_FLAG_END_OF_PRESENTATION))
3534 if (session_nodes_is_mask_set(session, MF_TOPOLOGY_MAX, SOURCE_FLAG_END_OF_PRESENTATION))
3536 session->presentation.flags |= SESSION_FLAG_END_OF_PRESENTATION | SESSION_FLAG_PENDING_COMMAND;
3537 IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, NULL);
3542 static void session_handle_end_of_stream(struct media_session *session, IMFMediaStream *stream)
3544 struct topo_node *node;
3546 if (!(node = session_get_node_object(session, (IUnknown *)stream, MF_TOPOLOGY_SOURCESTREAM_NODE))
3547 || node->flags & TOPO_NODE_END_OF_STREAM)
3549 return;
3552 session_deliver_sample(session, stream, NULL);
3554 session_raise_end_of_presentation(session);
3557 static void session_handle_end_of_presentation(struct media_session *session, IMFMediaSource *object)
3559 struct media_source *source;
3561 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
3563 if (source->source == object)
3565 if (!(source->flags & SOURCE_FLAG_END_OF_PRESENTATION))
3567 source->flags |= SOURCE_FLAG_END_OF_PRESENTATION;
3568 session_raise_end_of_presentation(session);
3571 break;
3576 static void session_sink_stream_marker(struct media_session *session, IMFStreamSink *stream_sink)
3578 struct topo_node *node;
3580 if (!(node = session_get_node_object(session, (IUnknown *)stream_sink, MF_TOPOLOGY_OUTPUT_NODE))
3581 || node->flags & TOPO_NODE_END_OF_STREAM)
3583 return;
3586 node->flags |= TOPO_NODE_END_OF_STREAM;
3588 if (session->presentation.flags & SESSION_FLAG_END_OF_PRESENTATION &&
3589 session_nodes_is_mask_set(session, MF_TOPOLOGY_OUTPUT_NODE, TOPO_NODE_END_OF_STREAM))
3591 session_set_topo_status(session, S_OK, MF_TOPOSTATUS_ENDED);
3592 session_set_caps(session, session->caps & ~MFSESSIONCAP_PAUSE);
3593 session_stop(session);
3597 static void session_sink_stream_scrub_complete(struct media_session *session, IMFStreamSink *stream_sink)
3599 struct topo_node *node;
3601 if (!(node = session_get_node_object(session, (IUnknown *)stream_sink, MF_TOPOLOGY_OUTPUT_NODE))
3602 || node->flags & TOPO_NODE_SCRUB_SAMPLE_COMPLETE)
3604 return;
3607 node->flags |= TOPO_NODE_SCRUB_SAMPLE_COMPLETE;
3609 /* Scrubbing event is not limited to the started state transition, or even the started state.
3610 Events are processed and forwarded at any point after transition from initial idle state. */
3611 if (session->presentation.flags & SESSION_FLAG_SOURCES_SUBSCRIBED &&
3612 session_nodes_is_mask_set(session, MF_TOPOLOGY_OUTPUT_NODE, TOPO_NODE_SCRUB_SAMPLE_COMPLETE))
3614 IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionScrubSampleComplete, &GUID_NULL, S_OK, NULL);
3615 session_nodes_unset_mask(session, MF_TOPOLOGY_OUTPUT_NODE, TOPO_NODE_SCRUB_SAMPLE_COMPLETE);
3619 static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
3621 struct media_session *session = impl_from_events_callback_IMFAsyncCallback(iface);
3622 IMFMediaEventGenerator *event_source;
3623 IMFMediaEvent *event = NULL;
3624 MediaEventType event_type;
3625 IUnknown *object = NULL;
3626 IMFMediaSource *source;
3627 IMFMediaStream *stream;
3628 PROPVARIANT value;
3629 HRESULT hr;
3631 if (FAILED(hr = IMFAsyncResult_GetState(result, (IUnknown **)&event_source)))
3632 return hr;
3634 if (FAILED(hr = IMFMediaEventGenerator_EndGetEvent(event_source, result, &event)))
3636 WARN("Failed to get event from %p, hr %#lx.\n", event_source, hr);
3637 goto failed;
3640 if (FAILED(hr = IMFMediaEvent_GetType(event, &event_type)))
3642 WARN("Failed to get event type, hr %#lx.\n", hr);
3643 goto failed;
3646 value.vt = VT_EMPTY;
3647 if (FAILED(hr = IMFMediaEvent_GetValue(event, &value)))
3649 WARN("Failed to get event value, hr %#lx.\n", hr);
3650 goto failed;
3653 switch (event_type)
3655 case MESourceSeeked:
3656 case MEStreamSeeked:
3657 FIXME("Source/stream seeking, semi-stub!\n");
3658 /* fallthrough */
3659 case MESourceStarted:
3660 case MESourcePaused:
3661 case MESourceStopped:
3662 case MEStreamStarted:
3663 case MEStreamPaused:
3664 case MEStreamStopped:
3666 EnterCriticalSection(&session->cs);
3667 session_set_source_object_state(session, (IUnknown *)event_source, event_type);
3668 LeaveCriticalSection(&session->cs);
3670 break;
3672 case MESourceRateChanged:
3674 EnterCriticalSection(&session->cs);
3675 session_complete_rate_change(session);
3676 LeaveCriticalSection(&session->cs);
3678 break;
3680 case MEBufferingStarted:
3681 case MEBufferingStopped:
3683 EnterCriticalSection(&session->cs);
3684 if (session_get_media_source(session, (IMFMediaSource *)event_source))
3686 if (event_type == MEBufferingStarted)
3687 IMFPresentationClock_Pause(session->clock);
3688 else
3689 IMFPresentationClock_Start(session->clock, PRESENTATION_CURRENT_POSITION);
3691 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
3693 LeaveCriticalSection(&session->cs);
3694 break;
3696 case MEReconnectStart:
3697 case MEReconnectEnd:
3699 EnterCriticalSection(&session->cs);
3700 if (session_get_media_source(session, (IMFMediaSource *)event_source))
3701 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
3702 LeaveCriticalSection(&session->cs);
3703 break;
3705 case MEExtendedType:
3706 case MERendererEvent:
3707 case MEStreamSinkFormatChanged:
3709 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
3710 break;
3712 case MENewStream:
3713 stream = (IMFMediaStream *)value.punkVal;
3715 if (value.vt != VT_UNKNOWN || !stream)
3717 WARN("Unexpected event value.\n");
3718 break;
3721 if (FAILED(hr = IMFMediaStream_GetMediaSource(stream, &source)))
3722 break;
3724 EnterCriticalSection(&session->cs);
3725 if (SUCCEEDED(hr = session_add_media_stream(session, source, stream)))
3726 hr = IMFMediaStream_BeginGetEvent(stream, &session->events_callback, (IUnknown *)stream);
3727 LeaveCriticalSection(&session->cs);
3729 IMFMediaSource_Release(source);
3731 break;
3732 case MEStreamSinkStarted:
3733 case MEStreamSinkPaused:
3734 case MEStreamSinkStopped:
3735 case MEStreamSinkPrerolled:
3737 EnterCriticalSection(&session->cs);
3738 session_set_sink_stream_state(session, (IMFStreamSink *)event_source, event_type);
3739 LeaveCriticalSection(&session->cs);
3741 break;
3742 case MEStreamSinkMarker:
3744 EnterCriticalSection(&session->cs);
3745 session_sink_stream_marker(session, (IMFStreamSink *)event_source);
3746 LeaveCriticalSection(&session->cs);
3748 break;
3749 case MEStreamSinkRequestSample:
3751 EnterCriticalSection(&session->cs);
3752 session_request_sample(session, (IMFStreamSink *)event_source);
3753 LeaveCriticalSection(&session->cs);
3755 break;
3756 case MEStreamSinkScrubSampleComplete:
3758 EnterCriticalSection(&session->cs);
3759 session_sink_stream_scrub_complete(session, (IMFStreamSink *)event_source);
3760 LeaveCriticalSection(&session->cs);
3761 break;
3762 case MEMediaSample:
3764 EnterCriticalSection(&session->cs);
3765 session_deliver_sample(session, (IMFMediaStream *)event_source, &value);
3766 LeaveCriticalSection(&session->cs);
3768 break;
3769 case MEEndOfStream:
3771 EnterCriticalSection(&session->cs);
3772 session_handle_end_of_stream(session, (IMFMediaStream *)event_source);
3773 LeaveCriticalSection(&session->cs);
3775 break;
3777 case MEEndOfPresentation:
3779 EnterCriticalSection(&session->cs);
3780 session_handle_end_of_presentation(session, (IMFMediaSource *)event_source);
3781 LeaveCriticalSection(&session->cs);
3783 break;
3784 case MEAudioSessionGroupingParamChanged:
3785 case MEAudioSessionIconChanged:
3786 case MEAudioSessionNameChanged:
3787 case MEAudioSessionVolumeChanged:
3789 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
3791 break;
3792 case MEAudioSessionDeviceRemoved:
3793 case MEAudioSessionDisconnected:
3794 case MEAudioSessionExclusiveModeOverride:
3795 case MEAudioSessionFormatChanged:
3796 case MEAudioSessionServerShutdown:
3798 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
3799 /* fallthrough */
3800 case MESinkInvalidated:
3802 EnterCriticalSection(&session->cs);
3803 session_sink_invalidated(session, event_type == MESinkInvalidated ? event : NULL,
3804 (IMFStreamSink *)event_source);
3805 LeaveCriticalSection(&session->cs);
3807 break;
3808 case MEQualityNotify:
3810 if (session->quality_manager)
3812 if (FAILED(IMFMediaEventGenerator_QueryInterface(event_source, &IID_IMFStreamSink, (void **)&object)))
3813 IMFMediaEventGenerator_QueryInterface(event_source, &IID_IMFTransform, (void **)&object);
3815 if (object)
3817 IMFQualityManager_NotifyQualityEvent(session->quality_manager, object, event);
3818 IUnknown_Release(object);
3822 break;
3823 default:
3827 PropVariantClear(&value);
3829 failed:
3830 if (event)
3831 IMFMediaEvent_Release(event);
3833 if (FAILED(hr = IMFMediaEventGenerator_BeginGetEvent(event_source, iface, (IUnknown *)event_source)))
3834 WARN("Failed to re-subscribe, hr %#lx.\n", hr);
3836 IMFMediaEventGenerator_Release(event_source);
3838 return hr;
3841 static const IMFAsyncCallbackVtbl session_events_callback_vtbl =
3843 session_events_callback_QueryInterface,
3844 session_events_callback_AddRef,
3845 session_events_callback_Release,
3846 session_events_callback_GetParameters,
3847 session_events_callback_Invoke,
3850 static HRESULT WINAPI session_sink_finalizer_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
3852 if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
3853 IsEqualIID(riid, &IID_IUnknown))
3855 *obj = iface;
3856 IMFAsyncCallback_AddRef(iface);
3857 return S_OK;
3860 WARN("Unsupported %s.\n", debugstr_guid(riid));
3861 *obj = NULL;
3862 return E_NOINTERFACE;
3865 static ULONG WINAPI session_sink_finalizer_callback_AddRef(IMFAsyncCallback *iface)
3867 struct media_session *session = impl_from_sink_finalizer_callback_IMFAsyncCallback(iface);
3868 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
3871 static ULONG WINAPI session_sink_finalizer_callback_Release(IMFAsyncCallback *iface)
3873 struct media_session *session = impl_from_sink_finalizer_callback_IMFAsyncCallback(iface);
3874 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
3877 static HRESULT WINAPI session_sink_finalizer_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
3879 return E_NOTIMPL;
3882 static HRESULT WINAPI session_sink_finalizer_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
3884 struct media_session *session = impl_from_sink_finalizer_callback_IMFAsyncCallback(iface);
3885 IMFFinalizableMediaSink *fin_sink = NULL;
3886 BOOL sinks_finalized = TRUE;
3887 struct media_sink *sink;
3888 IUnknown *state;
3889 HRESULT hr;
3891 if (FAILED(hr = IMFAsyncResult_GetState(result, &state)))
3892 return hr;
3894 EnterCriticalSection(&session->cs);
3896 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
3898 if (state == sink->object)
3900 if (FAILED(hr = IMFMediaSink_QueryInterface(sink->sink, &IID_IMFFinalizableMediaSink, (void **)&fin_sink)))
3901 WARN("Unexpected, missing IMFFinalizableMediaSink, hr %#lx.\n", hr);
3903 else
3905 sinks_finalized &= sink->finalized;
3906 if (!sinks_finalized)
3907 break;
3911 IUnknown_Release(state);
3913 if (fin_sink)
3915 /* Complete session transition, or close prematurely on error. */
3916 if (SUCCEEDED(hr = IMFFinalizableMediaSink_EndFinalize(fin_sink, result)))
3918 sink->finalized = TRUE;
3919 if (sinks_finalized)
3920 session_set_closed(session, hr);
3922 IMFFinalizableMediaSink_Release(fin_sink);
3925 LeaveCriticalSection(&session->cs);
3927 return S_OK;
3930 static const IMFAsyncCallbackVtbl session_sink_finalizer_callback_vtbl =
3932 session_sink_finalizer_callback_QueryInterface,
3933 session_sink_finalizer_callback_AddRef,
3934 session_sink_finalizer_callback_Release,
3935 session_sink_finalizer_callback_GetParameters,
3936 session_sink_finalizer_callback_Invoke,
3939 static HRESULT WINAPI session_rate_support_QueryInterface(IMFRateSupport *iface, REFIID riid, void **obj)
3941 struct media_session *session = impl_session_from_IMFRateSupport(iface);
3942 return IMFMediaSession_QueryInterface(&session->IMFMediaSession_iface, riid, obj);
3945 static ULONG WINAPI session_rate_support_AddRef(IMFRateSupport *iface)
3947 struct media_session *session = impl_session_from_IMFRateSupport(iface);
3948 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
3951 static ULONG WINAPI session_rate_support_Release(IMFRateSupport *iface)
3953 struct media_session *session = impl_session_from_IMFRateSupport(iface);
3954 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
3957 static HRESULT session_presentation_object_get_rate(IUnknown *object, MFRATE_DIRECTION direction,
3958 BOOL thin, BOOL fastest, float *result)
3960 IMFRateSupport *rate_support;
3961 float rate;
3962 HRESULT hr;
3964 /* For objects that don't support rate control, it's assumed that only forward direction is allowed, at 1.0f. */
3966 if (FAILED(hr = MFGetService(object, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, (void **)&rate_support)))
3968 if (direction == MFRATE_FORWARD)
3970 *result = 1.0f;
3971 return S_OK;
3973 else
3974 return MF_E_REVERSE_UNSUPPORTED;
3977 rate = 0.0f;
3978 if (fastest)
3980 if (SUCCEEDED(hr = IMFRateSupport_GetFastestRate(rate_support, direction, thin, &rate)))
3981 *result = min(fabsf(rate), *result);
3983 else
3985 if (SUCCEEDED(hr = IMFRateSupport_GetSlowestRate(rate_support, direction, thin, &rate)))
3986 *result = max(fabsf(rate), *result);
3989 IMFRateSupport_Release(rate_support);
3991 return hr;
3994 static HRESULT session_get_presentation_rate(struct media_session *session, MFRATE_DIRECTION direction,
3995 BOOL thin, BOOL fastest, float *result)
3997 struct media_source *source;
3998 struct media_sink *sink;
3999 HRESULT hr = E_POINTER;
4000 float rate;
4002 rate = fastest ? FLT_MAX : 0.0f;
4004 EnterCriticalSection(&session->cs);
4006 if (session->presentation.topo_status != MF_TOPOSTATUS_INVALID)
4008 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
4010 if (FAILED(hr = session_presentation_object_get_rate(source->object, direction, thin, fastest, &rate)))
4011 break;
4014 if (SUCCEEDED(hr))
4016 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
4018 if (FAILED(hr = session_presentation_object_get_rate(sink->object, direction, thin, fastest, &rate)))
4019 break;
4024 LeaveCriticalSection(&session->cs);
4026 if (SUCCEEDED(hr))
4027 *result = direction == MFRATE_FORWARD ? rate : -rate;
4029 return hr;
4032 static HRESULT WINAPI session_rate_support_GetSlowestRate(IMFRateSupport *iface, MFRATE_DIRECTION direction,
4033 BOOL thin, float *rate)
4035 struct media_session *session = impl_session_from_IMFRateSupport(iface);
4037 TRACE("%p, %d, %d, %p.\n", iface, direction, thin, rate);
4039 return session_get_presentation_rate(session, direction, thin, FALSE, rate);
4042 static HRESULT WINAPI session_rate_support_GetFastestRate(IMFRateSupport *iface, MFRATE_DIRECTION direction,
4043 BOOL thin, float *rate)
4045 struct media_session *session = impl_session_from_IMFRateSupport(iface);
4047 TRACE("%p, %d, %d, %p.\n", iface, direction, thin, rate);
4049 return session_get_presentation_rate(session, direction, thin, TRUE, rate);
4052 static HRESULT WINAPI session_rate_support_IsRateSupported(IMFRateSupport *iface, BOOL thin, float rate,
4053 float *nearest_supported_rate)
4055 struct media_session *session = impl_session_from_IMFRateSupport(iface);
4056 HRESULT hr;
4058 TRACE("%p, %d, %f, %p.\n", iface, thin, rate, nearest_supported_rate);
4060 EnterCriticalSection(&session->cs);
4061 hr = session_is_presentation_rate_supported(session, thin, rate, nearest_supported_rate);
4062 LeaveCriticalSection(&session->cs);
4064 return hr;
4067 static const IMFRateSupportVtbl session_rate_support_vtbl =
4069 session_rate_support_QueryInterface,
4070 session_rate_support_AddRef,
4071 session_rate_support_Release,
4072 session_rate_support_GetSlowestRate,
4073 session_rate_support_GetFastestRate,
4074 session_rate_support_IsRateSupported,
4077 static HRESULT WINAPI session_rate_control_QueryInterface(IMFRateControl *iface, REFIID riid, void **obj)
4079 struct media_session *session = impl_session_from_IMFRateControl(iface);
4080 return IMFMediaSession_QueryInterface(&session->IMFMediaSession_iface, riid, obj);
4083 static ULONG WINAPI session_rate_control_AddRef(IMFRateControl *iface)
4085 struct media_session *session = impl_session_from_IMFRateControl(iface);
4086 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
4089 static ULONG WINAPI session_rate_control_Release(IMFRateControl *iface)
4091 struct media_session *session = impl_session_from_IMFRateControl(iface);
4092 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
4095 static HRESULT WINAPI session_rate_control_SetRate(IMFRateControl *iface, BOOL thin, float rate)
4097 struct media_session *session = impl_session_from_IMFRateControl(iface);
4098 struct session_op *op;
4099 HRESULT hr;
4101 TRACE("%p, %d, %f.\n", iface, thin, rate);
4103 if (FAILED(hr = create_session_op(SESSION_CMD_SET_RATE, &op)))
4104 return hr;
4106 op->set_rate.thin = thin;
4107 op->set_rate.rate = rate;
4108 hr = session_submit_command(session, op);
4109 IUnknown_Release(&op->IUnknown_iface);
4110 return hr;
4113 static HRESULT WINAPI session_rate_control_GetRate(IMFRateControl *iface, BOOL *thin, float *rate)
4115 struct media_session *session = impl_session_from_IMFRateControl(iface);
4117 TRACE("%p, %p, %p.\n", iface, thin, rate);
4119 return IMFRateControl_GetRate(session->clock_rate_control, thin, rate);
4122 static const IMFRateControlVtbl session_rate_control_vtbl =
4124 session_rate_control_QueryInterface,
4125 session_rate_control_AddRef,
4126 session_rate_control_Release,
4127 session_rate_control_SetRate,
4128 session_rate_control_GetRate,
4131 static HRESULT WINAPI node_attribute_editor_QueryInterface(IMFTopologyNodeAttributeEditor *iface,
4132 REFIID riid, void **obj)
4134 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
4136 if (IsEqualIID(riid, &IID_IMFTopologyNodeAttributeEditor) ||
4137 IsEqualIID(riid, &IID_IUnknown))
4139 *obj = iface;
4140 IMFTopologyNodeAttributeEditor_AddRef(iface);
4141 return S_OK;
4144 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
4145 *obj = NULL;
4146 return E_NOINTERFACE;
4149 static ULONG WINAPI node_attribute_editor_AddRef(IMFTopologyNodeAttributeEditor *iface)
4151 struct media_session *session = impl_session_from_IMFTopologyNodeAttributeEditor(iface);
4152 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
4155 static ULONG WINAPI node_attribute_editor_Release(IMFTopologyNodeAttributeEditor *iface)
4157 struct media_session *session = impl_session_from_IMFTopologyNodeAttributeEditor(iface);
4158 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
4161 static HRESULT WINAPI node_attribute_editor_UpdateNodeAttributes(IMFTopologyNodeAttributeEditor *iface,
4162 TOPOID id, DWORD count, MFTOPONODE_ATTRIBUTE_UPDATE *updates)
4164 FIXME("%p, %s, %lu, %p.\n", iface, wine_dbgstr_longlong(id), count, updates);
4166 return E_NOTIMPL;
4169 static const IMFTopologyNodeAttributeEditorVtbl node_attribute_editor_vtbl =
4171 node_attribute_editor_QueryInterface,
4172 node_attribute_editor_AddRef,
4173 node_attribute_editor_Release,
4174 node_attribute_editor_UpdateNodeAttributes,
4177 /***********************************************************************
4178 * MFCreateMediaSession (mf.@)
4180 HRESULT WINAPI MFCreateMediaSession(IMFAttributes *config, IMFMediaSession **session)
4182 BOOL without_quality_manager = FALSE;
4183 struct media_session *object;
4184 HRESULT hr;
4186 TRACE("%p, %p.\n", config, session);
4188 if (!(object = calloc(1, sizeof(*object))))
4189 return E_OUTOFMEMORY;
4191 object->IMFMediaSession_iface.lpVtbl = &mfmediasessionvtbl;
4192 object->IMFGetService_iface.lpVtbl = &session_get_service_vtbl;
4193 object->IMFRateSupport_iface.lpVtbl = &session_rate_support_vtbl;
4194 object->IMFRateControl_iface.lpVtbl = &session_rate_control_vtbl;
4195 object->IMFTopologyNodeAttributeEditor_iface.lpVtbl = &node_attribute_editor_vtbl;
4196 object->commands_callback.lpVtbl = &session_commands_callback_vtbl;
4197 object->sa_ready_callback.lpVtbl = &session_sa_ready_callback_vtbl;
4198 object->events_callback.lpVtbl = &session_events_callback_vtbl;
4199 object->sink_finalizer_callback.lpVtbl = &session_sink_finalizer_callback_vtbl;
4200 object->refcount = 1;
4201 list_init(&object->topologies);
4202 list_init(&object->commands);
4203 list_init(&object->presentation.sources);
4204 list_init(&object->presentation.sinks);
4205 list_init(&object->presentation.nodes);
4206 InitializeCriticalSection(&object->cs);
4208 if (FAILED(hr = MFCreateTopology(&object->presentation.current_topology)))
4209 goto failed;
4211 if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
4212 goto failed;
4214 if (FAILED(hr = MFCreatePresentationClock(&object->clock)))
4215 goto failed;
4217 if (FAILED(hr = MFCreateSystemTimeSource(&object->system_time_source)))
4218 goto failed;
4220 if (FAILED(hr = IMFPresentationClock_QueryInterface(object->clock, &IID_IMFRateControl,
4221 (void **)&object->clock_rate_control)))
4223 goto failed;
4226 if (config)
4228 GUID clsid;
4230 if (SUCCEEDED(IMFAttributes_GetGUID(config, &MF_SESSION_TOPOLOADER, &clsid)))
4232 if (FAILED(hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTopoLoader,
4233 (void **)&object->topo_loader)))
4235 WARN("Failed to create custom topology loader, hr %#lx.\n", hr);
4239 if (SUCCEEDED(IMFAttributes_GetGUID(config, &MF_SESSION_QUALITY_MANAGER, &clsid)))
4241 if (!(without_quality_manager = IsEqualGUID(&clsid, &GUID_NULL)))
4243 if (FAILED(hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IMFQualityManager,
4244 (void **)&object->quality_manager)))
4246 WARN("Failed to create custom quality manager, hr %#lx.\n", hr);
4252 if (!object->topo_loader && FAILED(hr = MFCreateTopoLoader(&object->topo_loader)))
4253 goto failed;
4255 if (!object->quality_manager && !without_quality_manager &&
4256 FAILED(hr = MFCreateStandardQualityManager(&object->quality_manager)))
4258 goto failed;
4261 if (object->quality_manager && FAILED(hr = IMFQualityManager_NotifyPresentationClock(object->quality_manager,
4262 object->clock)))
4264 goto failed;
4267 *session = &object->IMFMediaSession_iface;
4269 return S_OK;
4271 failed:
4272 IMFMediaSession_Release(&object->IMFMediaSession_iface);
4273 return hr;