mf/session: Handle errors when subscribing to source's events.
[wine.git] / dlls / mf / session.c
blobbac4f23b9c68119d68e704b935749f84f1cf617b
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;
804 LIST_FOR_EACH_ENTRY_SAFE(source, source2, &session->presentation.sources, struct media_source, entry)
806 list_remove(&source->entry);
807 if (source->source)
808 IMFMediaSource_Release(source->source);
809 if (source->pd)
810 IMFPresentationDescriptor_Release(source->pd);
811 free(source);
814 LIST_FOR_EACH_ENTRY_SAFE(node, node2, &session->presentation.nodes, struct topo_node, entry)
816 list_remove(&node->entry);
817 release_topo_node(node);
820 LIST_FOR_EACH_ENTRY_SAFE(sink, sink2, &session->presentation.sinks, struct media_sink, entry)
822 list_remove(&sink->entry);
824 if (sink->sink)
825 IMFMediaSink_Release(sink->sink);
826 if (sink->preroll)
827 IMFMediaSinkPreroll_Release(sink->preroll);
828 if (sink->event_generator)
829 IMFMediaEventGenerator_Release(sink->event_generator);
830 free(sink);
834 static struct topo_node *session_get_node_by_id(const struct media_session *session, TOPOID id)
836 struct topo_node *node;
838 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
840 if (node->node_id == id)
841 return node;
844 return NULL;
847 static void session_command_complete(struct media_session *session)
849 struct session_op *op;
850 struct list *e;
852 session->presentation.flags &= ~SESSION_FLAG_PENDING_COMMAND;
854 /* Submit next command. */
855 if ((e = list_head(&session->commands)))
857 op = LIST_ENTRY(e, struct session_op, entry);
858 MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &session->commands_callback, &op->IUnknown_iface);
862 static void session_command_complete_with_event(struct media_session *session, MediaEventType event,
863 HRESULT status, const PROPVARIANT *param)
865 IMFMediaEventQueue_QueueEventParamVar(session->event_queue, event, &GUID_NULL, status, param);
866 session_command_complete(session);
869 static HRESULT session_subscribe_sources(struct media_session *session)
871 struct media_source *source;
872 HRESULT hr = S_OK;
874 if (session->presentation.flags & SESSION_FLAG_SOURCES_SUBSCRIBED)
875 return hr;
877 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
879 if (FAILED(hr = IMFMediaSource_BeginGetEvent(source->source, &session->events_callback,
880 source->object)))
882 WARN("Failed to subscribe to source events, hr %#lx.\n", hr);
883 return hr;
887 session->presentation.flags |= SESSION_FLAG_SOURCES_SUBSCRIBED;
888 return hr;
891 static void session_start(struct media_session *session, const GUID *time_format, const PROPVARIANT *start_position)
893 struct media_source *source;
894 HRESULT hr;
896 switch (session->state)
898 case SESSION_STATE_STOPPED:
900 /* Start request with no current topology. */
901 if (session->presentation.topo_status == MF_TOPOSTATUS_INVALID)
903 session_command_complete_with_event(session, MESessionStarted, MF_E_INVALIDREQUEST, NULL);
904 break;
907 /* fallthrough */
908 case SESSION_STATE_PAUSED:
910 session->presentation.time_format = *time_format;
911 session->presentation.start_position.vt = VT_EMPTY;
912 PropVariantCopy(&session->presentation.start_position, start_position);
914 if (FAILED(hr = session_subscribe_sources(session)))
916 session_command_complete_with_event(session, MESessionStarted, hr, NULL);
917 return;
920 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
922 if (FAILED(hr = IMFMediaSource_Start(source->source, source->pd, &GUID_NULL, start_position)))
923 WARN("Failed to start media source %p, hr %#lx.\n", source->source, hr);
926 session->state = SESSION_STATE_STARTING_SOURCES;
927 break;
928 case SESSION_STATE_STARTED:
929 FIXME("Seeking is not implemented.\n");
930 session_command_complete(session);
931 break;
932 default:
933 session_command_complete_with_event(session, MESessionStarted, MF_E_INVALIDREQUEST, NULL);
934 break;
938 static void session_set_started(struct media_session *session)
940 struct media_source *source;
941 IMFMediaEvent *event;
942 unsigned int caps;
943 DWORD flags;
945 session->state = SESSION_STATE_STARTED;
947 caps = session->caps | MFSESSIONCAP_PAUSE;
949 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
951 if (SUCCEEDED(IMFMediaSource_GetCharacteristics(source->source, &flags)))
953 if (!(flags & MFMEDIASOURCE_CAN_PAUSE))
955 caps &= ~MFSESSIONCAP_PAUSE;
956 break;
961 session_set_caps(session, caps);
963 if (SUCCEEDED(MFCreateMediaEvent(MESessionStarted, &GUID_NULL, S_OK, NULL, &event)))
965 IMFMediaEvent_SetUINT64(event, &MF_EVENT_PRESENTATION_TIME_OFFSET, 0);
966 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
967 IMFMediaEvent_Release(event);
969 session_command_complete(session);
972 static void session_set_paused(struct media_session *session, unsigned int state, HRESULT status)
974 /* Failed event status could indicate a failure during normal transition to paused state,
975 or an attempt to pause from invalid initial state. To finalize failed transition in the former case,
976 state is still forced to PAUSED, otherwise previous state is retained. */
977 if (state != ~0u) session->state = state;
978 if (SUCCEEDED(status))
979 session_set_caps(session, session->caps & ~MFSESSIONCAP_PAUSE);
980 session_command_complete_with_event(session, MESessionPaused, status, NULL);
983 static void session_set_closed(struct media_session *session, HRESULT status)
985 session->state = SESSION_STATE_CLOSED;
986 if (SUCCEEDED(status))
987 session_set_caps(session, session->caps & ~(MFSESSIONCAP_START | MFSESSIONCAP_SEEK));
988 session_command_complete_with_event(session, MESessionClosed, status, NULL);
991 static void session_pause(struct media_session *session)
993 unsigned int state = ~0u;
994 HRESULT hr;
996 switch (session->state)
998 case SESSION_STATE_STARTED:
1000 /* Transition in two steps - pause the clock, wait for sinks, then pause sources. */
1001 if (SUCCEEDED(hr = IMFPresentationClock_Pause(session->clock)))
1002 session->state = SESSION_STATE_PAUSING_SINKS;
1003 state = SESSION_STATE_PAUSED;
1005 break;
1007 case SESSION_STATE_STOPPED:
1008 hr = MF_E_SESSION_PAUSEWHILESTOPPED;
1009 break;
1010 default:
1011 hr = MF_E_INVALIDREQUEST;
1014 if (FAILED(hr))
1015 session_set_paused(session, state, hr);
1018 static void session_clear_end_of_presentation(struct media_session *session)
1020 struct media_source *source;
1021 struct topo_node *node;
1023 session->presentation.flags &= ~SESSION_FLAG_END_OF_PRESENTATION;
1024 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
1026 source->flags &= ~SOURCE_FLAG_END_OF_PRESENTATION;
1028 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
1030 node->flags &= ~TOPO_NODE_END_OF_STREAM;
1032 session->presentation.topo_status = MF_TOPOSTATUS_READY;
1035 static void session_set_stopped(struct media_session *session, HRESULT status)
1037 MediaEventType event_type;
1038 IMFMediaEvent *event;
1040 session->state = SESSION_STATE_STOPPED;
1041 event_type = session->presentation.flags & SESSION_FLAG_END_OF_PRESENTATION ? MESessionEnded : MESessionStopped;
1043 if (SUCCEEDED(MFCreateMediaEvent(event_type, &GUID_NULL, status, NULL, &event)))
1045 IMFMediaEvent_SetUINT64(event, &MF_SESSION_APPROX_EVENT_OCCURRENCE_TIME, session->presentation.clock_stop_time);
1046 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
1047 IMFMediaEvent_Release(event);
1049 session_clear_end_of_presentation(session);
1050 session_command_complete(session);
1053 static void session_stop(struct media_session *session)
1055 HRESULT hr = MF_E_INVALIDREQUEST;
1057 switch (session->state)
1059 case SESSION_STATE_STARTED:
1060 case SESSION_STATE_PAUSED:
1062 /* Transition in two steps - stop the clock, wait for sinks, then stop sources. */
1063 IMFPresentationClock_GetTime(session->clock, &session->presentation.clock_stop_time);
1064 if (SUCCEEDED(hr = IMFPresentationClock_Stop(session->clock)))
1065 session->state = SESSION_STATE_STOPPING_SINKS;
1066 else
1067 session_set_stopped(session, hr);
1069 break;
1070 case SESSION_STATE_STOPPED:
1071 hr = S_OK;
1072 /* fallthrough */
1073 default:
1074 session_command_complete_with_event(session, MESessionStopped, hr, NULL);
1075 break;
1079 static HRESULT session_finalize_sinks(struct media_session *session)
1081 IMFFinalizableMediaSink *fin_sink;
1082 BOOL sinks_finalized = TRUE;
1083 struct media_sink *sink;
1084 HRESULT hr = S_OK;
1086 session->presentation.flags &= ~SESSION_FLAG_FINALIZE_SINKS;
1087 session->state = SESSION_STATE_FINALIZING_SINKS;
1089 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
1091 if (SUCCEEDED(IMFMediaSink_QueryInterface(sink->sink, &IID_IMFFinalizableMediaSink, (void **)&fin_sink)))
1093 hr = IMFFinalizableMediaSink_BeginFinalize(fin_sink, &session->sink_finalizer_callback,
1094 (IUnknown *)fin_sink);
1095 IMFFinalizableMediaSink_Release(fin_sink);
1096 if (FAILED(hr))
1097 break;
1098 sinks_finalized = FALSE;
1100 else
1101 sink->finalized = TRUE;
1104 if (sinks_finalized)
1105 session_set_closed(session, hr);
1107 return hr;
1110 static void session_close(struct media_session *session)
1112 HRESULT hr = S_OK;
1114 switch (session->state)
1116 case SESSION_STATE_STOPPED:
1117 hr = session_finalize_sinks(session);
1118 break;
1119 case SESSION_STATE_STARTED:
1120 case SESSION_STATE_PAUSED:
1121 session->presentation.flags |= SESSION_FLAG_FINALIZE_SINKS;
1122 if (SUCCEEDED(hr = IMFPresentationClock_Stop(session->clock)))
1123 session->state = SESSION_STATE_STOPPING_SINKS;
1124 break;
1125 default:
1126 hr = MF_E_INVALIDREQUEST;
1127 break;
1130 session_clear_queued_topologies(session);
1131 if (FAILED(hr))
1132 session_set_closed(session, hr);
1135 static void session_clear_topologies(struct media_session *session)
1137 HRESULT hr = S_OK;
1139 if (session->state == SESSION_STATE_CLOSED)
1140 hr = MF_E_INVALIDREQUEST;
1141 else
1142 session_clear_queued_topologies(session);
1143 session_command_complete_with_event(session, MESessionTopologiesCleared, hr, NULL);
1146 static HRESULT session_is_presentation_rate_supported(struct media_session *session, BOOL thin, float rate,
1147 float *nearest_rate)
1149 IMFRateSupport *rate_support;
1150 struct media_source *source;
1151 struct media_sink *sink;
1152 float value = 0.0f, tmp;
1153 HRESULT hr = S_OK;
1154 DWORD flags;
1156 if (!nearest_rate) nearest_rate = &tmp;
1158 if (rate == 0.0f)
1160 *nearest_rate = 1.0f;
1161 return S_OK;
1164 if (session->presentation.topo_status != MF_TOPOSTATUS_INVALID)
1166 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
1168 if (FAILED(hr = MFGetService(source->object, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport,
1169 (void **)&rate_support)))
1171 value = 1.0f;
1172 break;
1175 value = rate;
1176 if (FAILED(hr = IMFRateSupport_IsRateSupported(rate_support, thin, rate, &value)))
1177 WARN("Source does not support rate %f, hr %#lx.\n", rate, hr);
1178 IMFRateSupport_Release(rate_support);
1180 /* Only "first" source is considered. */
1181 break;
1184 if (SUCCEEDED(hr))
1186 /* For sinks only check if rate is supported, ignoring nearest values. */
1187 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
1189 flags = 0;
1190 if (FAILED(hr = IMFMediaSink_GetCharacteristics(sink->sink, &flags)))
1191 break;
1193 if (flags & MEDIASINK_RATELESS)
1194 continue;
1196 if (FAILED(MFGetService(sink->object, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport,
1197 (void **)&rate_support)))
1198 continue;
1200 hr = IMFRateSupport_IsRateSupported(rate_support, thin, rate, NULL);
1201 IMFRateSupport_Release(rate_support);
1202 if (FAILED(hr))
1204 WARN("Sink %p does not support rate %f, hr %#lx.\n", sink->sink, rate, hr);
1205 break;
1211 *nearest_rate = value;
1213 return hr;
1216 static void session_set_consumed_clock(IUnknown *object, IMFPresentationClock *clock)
1218 IMFClockConsumer *consumer;
1220 if (SUCCEEDED(IUnknown_QueryInterface(object, &IID_IMFClockConsumer, (void **)&consumer)))
1222 IMFClockConsumer_SetPresentationClock(consumer, clock);
1223 IMFClockConsumer_Release(consumer);
1227 static void session_set_presentation_clock(struct media_session *session)
1229 IMFPresentationTimeSource *time_source = NULL;
1230 struct media_source *source;
1231 struct media_sink *sink;
1232 struct topo_node *node;
1233 HRESULT hr;
1235 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
1237 if (node->type == MF_TOPOLOGY_TRANSFORM_NODE)
1238 IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
1241 if (!(session->presentation.flags & SESSION_FLAG_PRESENTATION_CLOCK_SET))
1243 /* Attempt to get time source from the sinks. */
1244 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
1246 if (SUCCEEDED(IMFMediaSink_QueryInterface(sink->sink, &IID_IMFPresentationTimeSource,
1247 (void **)&time_source)))
1248 break;
1251 if (time_source)
1253 hr = IMFPresentationClock_SetTimeSource(session->clock, time_source);
1254 IMFPresentationTimeSource_Release(time_source);
1256 else
1257 hr = IMFPresentationClock_SetTimeSource(session->clock, session->system_time_source);
1259 if (FAILED(hr))
1260 WARN("Failed to set time source, hr %#lx.\n", hr);
1262 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
1264 if (node->type != MF_TOPOLOGY_OUTPUT_NODE)
1265 continue;
1267 if (FAILED(hr = IMFStreamSink_BeginGetEvent(node->object.sink_stream, &session->events_callback,
1268 node->object.object)))
1270 WARN("Failed to subscribe to stream sink events, hr %#lx.\n", hr);
1274 /* Set clock for all topology nodes. */
1275 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
1277 session_set_consumed_clock(source->object, session->clock);
1280 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
1282 if (sink->event_generator && FAILED(hr = IMFMediaEventGenerator_BeginGetEvent(sink->event_generator,
1283 &session->events_callback, (IUnknown *)sink->event_generator)))
1285 WARN("Failed to subscribe to sink events, hr %#lx.\n", hr);
1288 if (FAILED(hr = IMFMediaSink_SetPresentationClock(sink->sink, session->clock)))
1289 WARN("Failed to set presentation clock for the sink, hr %#lx.\n", hr);
1292 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
1294 if (node->type != MF_TOPOLOGY_TRANSFORM_NODE)
1295 continue;
1297 session_set_consumed_clock(node->object.object, session->clock);
1300 session->presentation.flags |= SESSION_FLAG_PRESENTATION_CLOCK_SET;
1304 static void session_set_rate(struct media_session *session, BOOL thin, float rate)
1306 IMFRateControl *rate_control;
1307 struct media_source *source;
1308 float clock_rate = 0.0f;
1309 PROPVARIANT param;
1310 HRESULT hr;
1312 hr = session_is_presentation_rate_supported(session, thin, rate, NULL);
1314 if (SUCCEEDED(hr))
1315 hr = IMFRateControl_GetRate(session->clock_rate_control, NULL, &clock_rate);
1317 if (SUCCEEDED(hr) && (rate != clock_rate) && SUCCEEDED(hr = session_subscribe_sources(session)))
1319 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
1321 if (SUCCEEDED(hr = MFGetService(source->object, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateControl,
1322 (void **)&rate_control)))
1324 hr = IMFRateControl_SetRate(rate_control, thin, rate);
1325 IMFRateControl_Release(rate_control);
1326 if (SUCCEEDED(hr))
1328 session->presentation.flags |= SESSION_FLAG_PENDING_RATE_CHANGE;
1329 session->presentation.rate = rate;
1330 return;
1334 break;
1338 param.vt = VT_R4;
1339 param.fltVal = rate;
1340 session_command_complete_with_event(session, MESessionRateChanged, hr, SUCCEEDED(hr) ? &param : NULL);
1343 static void session_complete_rate_change(struct media_session *session)
1345 PROPVARIANT param;
1346 HRESULT hr;
1348 if (!(session->presentation.flags & SESSION_FLAG_PENDING_RATE_CHANGE))
1349 return;
1351 session->presentation.flags &= ~SESSION_FLAG_PENDING_RATE_CHANGE;
1352 session_set_presentation_clock(session);
1354 hr = IMFRateControl_SetRate(session->clock_rate_control, session->presentation.thin,
1355 session->presentation.rate);
1357 param.vt = VT_R4;
1358 param.fltVal = session->presentation.rate;
1360 IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionRateChanged, &GUID_NULL, hr,
1361 SUCCEEDED(hr) ? &param : NULL);
1362 session_command_complete(session);
1365 static struct media_source *session_get_media_source(struct media_session *session, IMFMediaSource *source)
1367 struct media_source *cur;
1369 LIST_FOR_EACH_ENTRY(cur, &session->presentation.sources, struct media_source, entry)
1371 if (source == cur->source)
1372 return cur;
1375 return NULL;
1378 static void session_release_media_source(struct media_source *source)
1380 IMFMediaSource_Release(source->source);
1381 if (source->pd)
1382 IMFPresentationDescriptor_Release(source->pd);
1383 free(source);
1386 static HRESULT session_add_media_source(struct media_session *session, IMFTopologyNode *node, IMFMediaSource *source)
1388 struct media_source *media_source;
1389 HRESULT hr;
1391 if (session_get_media_source(session, source))
1392 return S_FALSE;
1394 if (!(media_source = calloc(1, sizeof(*media_source))))
1395 return E_OUTOFMEMORY;
1397 media_source->source = source;
1398 IMFMediaSource_AddRef(media_source->source);
1400 hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, &IID_IMFPresentationDescriptor,
1401 (void **)&media_source->pd);
1403 if (SUCCEEDED(hr))
1404 list_add_tail(&session->presentation.sources, &media_source->entry);
1405 else
1406 session_release_media_source(media_source);
1408 return hr;
1411 static void session_raise_topology_set(struct media_session *session, IMFTopology *topology, HRESULT status)
1413 PROPVARIANT param;
1415 param.vt = topology ? VT_UNKNOWN : VT_EMPTY;
1416 param.punkVal = (IUnknown *)topology;
1418 IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionTopologySet, &GUID_NULL, status, &param);
1421 static DWORD session_get_object_rate_caps(IUnknown *object)
1423 IMFRateSupport *rate_support;
1424 DWORD caps = 0;
1425 float rate;
1427 if (SUCCEEDED(MFGetService(object, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, (void **)&rate_support)))
1429 rate = 0.0f;
1430 if (SUCCEEDED(IMFRateSupport_GetFastestRate(rate_support, MFRATE_FORWARD, TRUE, &rate)) && rate != 0.0f)
1431 caps |= MFSESSIONCAP_RATE_FORWARD;
1433 rate = 0.0f;
1434 if (SUCCEEDED(IMFRateSupport_GetFastestRate(rate_support, MFRATE_REVERSE, TRUE, &rate)) && rate != 0.0f)
1435 caps |= MFSESSIONCAP_RATE_REVERSE;
1437 IMFRateSupport_Release(rate_support);
1440 return caps;
1443 static HRESULT session_add_media_sink(struct media_session *session, IMFTopologyNode *node, IMFMediaSink *sink)
1445 struct media_sink *media_sink;
1446 unsigned int disable_preroll = 0;
1447 DWORD flags;
1449 LIST_FOR_EACH_ENTRY(media_sink, &session->presentation.sinks, struct media_sink, entry)
1451 if (sink == media_sink->sink)
1452 return S_FALSE;
1455 if (!(media_sink = calloc(1, sizeof(*media_sink))))
1456 return E_OUTOFMEMORY;
1458 media_sink->sink = sink;
1459 IMFMediaSink_AddRef(media_sink->sink);
1461 IMFMediaSink_QueryInterface(media_sink->sink, &IID_IMFMediaEventGenerator, (void **)&media_sink->event_generator);
1463 IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_DISABLE_PREROLL, &disable_preroll);
1464 if (SUCCEEDED(IMFMediaSink_GetCharacteristics(sink, &flags)) && flags & MEDIASINK_CAN_PREROLL && !disable_preroll)
1466 if (SUCCEEDED(IMFMediaSink_QueryInterface(media_sink->sink, &IID_IMFMediaSinkPreroll, (void **)&media_sink->preroll)))
1467 session->presentation.flags |= SESSION_FLAG_NEEDS_PREROLL;
1470 list_add_tail(&session->presentation.sinks, &media_sink->entry);
1472 return S_OK;
1475 static DWORD transform_node_get_stream_id(struct topo_node *node, BOOL output, unsigned int index)
1477 DWORD *map = output ? node->u.transform.output_map : node->u.transform.input_map;
1478 return map ? map[index] : index;
1481 static HRESULT session_set_transform_stream_info(struct topo_node *node)
1483 DWORD *input_map = NULL, *output_map = NULL;
1484 DWORD i, input_count, output_count;
1485 struct transform_stream *streams;
1486 unsigned int block_alignment;
1487 IMFMediaType *media_type;
1488 UINT32 bytes_per_second;
1489 GUID major = { 0 };
1490 HRESULT hr;
1492 hr = IMFTransform_GetStreamCount(node->object.transform, &input_count, &output_count);
1493 if (SUCCEEDED(hr) && (input_count > 1 || output_count > 1))
1495 input_map = calloc(input_count, sizeof(*input_map));
1496 output_map = calloc(output_count, sizeof(*output_map));
1497 if (FAILED(IMFTransform_GetStreamIDs(node->object.transform, input_count, input_map,
1498 output_count, output_map)))
1500 /* Assume sequential identifiers. */
1501 free(input_map);
1502 free(output_map);
1503 input_map = output_map = NULL;
1507 if (SUCCEEDED(hr))
1509 node->u.transform.input_map = input_map;
1510 node->u.transform.output_map = output_map;
1512 streams = calloc(input_count, sizeof(*streams));
1513 for (i = 0; i < input_count; ++i)
1514 list_init(&streams[i].samples);
1515 node->u.transform.inputs = streams;
1516 node->u.transform.input_count = input_count;
1518 streams = calloc(output_count, sizeof(*streams));
1519 for (i = 0; i < output_count; ++i)
1521 list_init(&streams[i].samples);
1523 if (SUCCEEDED(IMFTransform_GetOutputCurrentType(node->object.transform,
1524 transform_node_get_stream_id(node, TRUE, i), &media_type)))
1526 if (SUCCEEDED(IMFMediaType_GetMajorType(media_type, &major)) && IsEqualGUID(&major, &MFMediaType_Audio)
1527 && SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &block_alignment)))
1529 streams[i].min_buffer_size = block_alignment;
1530 if (SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &bytes_per_second)))
1531 streams[i].min_buffer_size = max(streams[i].min_buffer_size, bytes_per_second);
1533 IMFMediaType_Release(media_type);
1536 node->u.transform.outputs = streams;
1537 node->u.transform.output_count = output_count;
1540 return hr;
1543 static HRESULT session_get_stream_sink_type(IMFStreamSink *sink, IMFMediaType **media_type)
1545 IMFMediaTypeHandler *handler;
1546 HRESULT hr;
1548 if (SUCCEEDED(hr = IMFStreamSink_GetMediaTypeHandler(sink, &handler)))
1550 hr = IMFMediaTypeHandler_GetCurrentMediaType(handler, media_type);
1551 IMFMediaTypeHandler_Release(handler);
1554 return hr;
1557 static HRESULT WINAPI node_sample_allocator_cb_QueryInterface(IMFVideoSampleAllocatorNotify *iface,
1558 REFIID riid, void **obj)
1560 if (IsEqualIID(riid, &IID_IMFVideoSampleAllocatorNotify) ||
1561 IsEqualIID(riid, &IID_IUnknown))
1563 *obj = iface;
1564 IMFVideoSampleAllocatorNotify_AddRef(iface);
1565 return S_OK;
1568 *obj = NULL;
1569 return E_NOINTERFACE;
1572 static ULONG WINAPI node_sample_allocator_cb_AddRef(IMFVideoSampleAllocatorNotify *iface)
1574 return 2;
1577 static ULONG WINAPI node_sample_allocator_cb_Release(IMFVideoSampleAllocatorNotify *iface)
1579 return 1;
1582 static HRESULT session_request_sample_from_node(struct media_session *session, IMFTopologyNode *node, DWORD output);
1584 static HRESULT WINAPI node_sample_allocator_cb_NotifyRelease(IMFVideoSampleAllocatorNotify *iface)
1586 struct topo_node *topo_node = impl_node_from_IMFVideoSampleAllocatorNotify(iface);
1587 MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &topo_node->session->sa_ready_callback, (IUnknown *)iface);
1588 return S_OK;
1591 static const IMFVideoSampleAllocatorNotifyVtbl node_sample_allocator_cb_vtbl =
1593 node_sample_allocator_cb_QueryInterface,
1594 node_sample_allocator_cb_AddRef,
1595 node_sample_allocator_cb_Release,
1596 node_sample_allocator_cb_NotifyRelease,
1599 static HRESULT session_append_node(struct media_session *session, IMFTopologyNode *node)
1601 struct topo_node *topo_node;
1602 IMFMediaSink *media_sink;
1603 IMFMediaType *media_type;
1604 IMFStreamDescriptor *sd;
1605 HRESULT hr = S_OK;
1607 if (!(topo_node = calloc(1, sizeof(*topo_node))))
1608 return E_OUTOFMEMORY;
1610 IMFTopologyNode_GetNodeType(node, &topo_node->type);
1611 IMFTopologyNode_GetTopoNodeID(node, &topo_node->node_id);
1612 topo_node->node = node;
1613 IMFTopologyNode_AddRef(topo_node->node);
1614 topo_node->session = session;
1616 switch (topo_node->type)
1618 case MF_TOPOLOGY_OUTPUT_NODE:
1619 topo_node->u.sink.notify_cb.lpVtbl = &node_sample_allocator_cb_vtbl;
1621 if (FAILED(hr = topology_node_get_object(node, &IID_IMFStreamSink, (void **)&topo_node->object.object)))
1623 WARN("Failed to get stream sink interface, hr %#lx.\n", hr);
1624 break;
1627 if (FAILED(hr = IMFStreamSink_GetMediaSink(topo_node->object.sink_stream, &media_sink)))
1628 break;
1630 if (SUCCEEDED(hr = session_add_media_sink(session, node, media_sink)))
1632 if (SUCCEEDED(session_get_stream_sink_type(topo_node->object.sink_stream, &media_type)))
1634 if (SUCCEEDED(MFGetService(topo_node->object.object, &MR_VIDEO_ACCELERATION_SERVICE,
1635 &IID_IMFVideoSampleAllocator, (void **)&topo_node->u.sink.allocator)))
1637 if (FAILED(hr = IMFVideoSampleAllocator_InitializeSampleAllocator(topo_node->u.sink.allocator,
1638 2, media_type)))
1640 WARN("Failed to initialize sample allocator for the stream, hr %#lx.\n", hr);
1642 IMFVideoSampleAllocator_QueryInterface(topo_node->u.sink.allocator,
1643 &IID_IMFVideoSampleAllocatorCallback, (void **)&topo_node->u.sink.allocator_cb);
1644 IMFVideoSampleAllocatorCallback_SetCallback(topo_node->u.sink.allocator_cb,
1645 &topo_node->u.sink.notify_cb);
1647 IMFMediaType_Release(media_type);
1650 IMFMediaSink_Release(media_sink);
1652 break;
1653 case MF_TOPOLOGY_SOURCESTREAM_NODE:
1654 if (FAILED(IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_SOURCE, &IID_IMFMediaSource,
1655 (void **)&topo_node->u.source.source)))
1657 WARN("Missing MF_TOPONODE_SOURCE, hr %#lx.\n", hr);
1658 break;
1661 if (FAILED(hr = session_add_media_source(session, node, topo_node->u.source.source)))
1662 break;
1664 if (FAILED(hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_STREAM_DESCRIPTOR,
1665 &IID_IMFStreamDescriptor, (void **)&sd)))
1667 WARN("Missing MF_TOPONODE_STREAM_DESCRIPTOR, hr %#lx.\n", hr);
1668 break;
1671 hr = IMFStreamDescriptor_GetStreamIdentifier(sd, &topo_node->u.source.stream_id);
1672 IMFStreamDescriptor_Release(sd);
1674 break;
1675 case MF_TOPOLOGY_TRANSFORM_NODE:
1677 if (SUCCEEDED(hr = topology_node_get_object(node, &IID_IMFTransform, (void **)&topo_node->object.transform)))
1679 hr = session_set_transform_stream_info(topo_node);
1681 else
1682 WARN("Failed to get IMFTransform for MFT node, hr %#lx.\n", hr);
1684 break;
1685 case MF_TOPOLOGY_TEE_NODE:
1686 FIXME("Unsupported node type %d.\n", topo_node->type);
1688 break;
1689 default:
1693 if (SUCCEEDED(hr))
1694 list_add_tail(&session->presentation.nodes, &topo_node->entry);
1695 else
1696 release_topo_node(topo_node);
1698 return hr;
1701 static HRESULT session_collect_nodes(struct media_session *session)
1703 IMFTopology *topology = session->presentation.current_topology;
1704 IMFTopologyNode *node;
1705 WORD i, count = 0;
1706 HRESULT hr;
1708 if (!list_empty(&session->presentation.nodes))
1709 return S_OK;
1711 if (FAILED(hr = IMFTopology_GetNodeCount(topology, &count)))
1712 return hr;
1714 for (i = 0; i < count; ++i)
1716 if (FAILED(hr = IMFTopology_GetNode(topology, i, &node)))
1718 WARN("Failed to get node %u.\n", i);
1719 break;
1722 hr = session_append_node(session, node);
1723 IMFTopologyNode_Release(node);
1724 if (FAILED(hr))
1726 WARN("Failed to add node %u.\n", i);
1727 break;
1731 return hr;
1734 static HRESULT session_set_current_topology(struct media_session *session, IMFTopology *topology)
1736 struct media_source *source;
1737 DWORD caps, object_flags;
1738 struct media_sink *sink;
1739 struct topo_node *node;
1740 IMFMediaEvent *event;
1741 HRESULT hr;
1743 if (FAILED(hr = IMFTopology_CloneFrom(session->presentation.current_topology, topology)))
1745 WARN("Failed to clone topology, hr %#lx.\n", hr);
1746 return hr;
1749 session_collect_nodes(session);
1751 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
1753 if (node->type == MF_TOPOLOGY_TRANSFORM_NODE)
1755 if (FAILED(hr = IMFTransform_ProcessMessage(node->object.transform,
1756 MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0)))
1757 return hr;
1761 /* FIXME: attributes are all zero for now */
1762 if (SUCCEEDED(MFCreateMediaEvent(MESessionNotifyPresentationTime, &GUID_NULL, S_OK, NULL, &event)))
1764 IMFMediaEvent_SetUINT64(event, &MF_EVENT_START_PRESENTATION_TIME, 0);
1765 IMFMediaEvent_SetUINT64(event, &MF_EVENT_PRESENTATION_TIME_OFFSET, 0);
1766 IMFMediaEvent_SetUINT64(event, &MF_EVENT_START_PRESENTATION_TIME_AT_OUTPUT, 0);
1768 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
1769 IMFMediaEvent_Release(event);
1772 /* Update session caps. */
1773 caps = MFSESSIONCAP_START | MFSESSIONCAP_SEEK | MFSESSIONCAP_RATE_FORWARD | MFSESSIONCAP_RATE_REVERSE |
1774 MFSESSIONCAP_DOES_NOT_USE_NETWORK;
1776 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
1778 if (!caps)
1779 break;
1781 object_flags = 0;
1782 if (SUCCEEDED(IMFMediaSource_GetCharacteristics(source->source, &object_flags)))
1784 if (!(object_flags & MFMEDIASOURCE_DOES_NOT_USE_NETWORK))
1785 caps &= ~MFSESSIONCAP_DOES_NOT_USE_NETWORK;
1786 if (!(object_flags & MFMEDIASOURCE_CAN_SEEK))
1787 caps &= ~MFSESSIONCAP_SEEK;
1790 /* Mask unsupported rate caps. */
1792 caps &= session_get_object_rate_caps(source->object)
1793 | ~(MFSESSIONCAP_RATE_FORWARD | MFSESSIONCAP_RATE_REVERSE);
1796 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
1798 if (!caps)
1799 break;
1801 object_flags = 0;
1802 if (SUCCEEDED(IMFMediaSink_GetCharacteristics(sink->sink, &object_flags)))
1804 if (!(object_flags & MEDIASINK_RATELESS))
1805 caps &= session_get_object_rate_caps(sink->object)
1806 | ~(MFSESSIONCAP_RATE_FORWARD | MFSESSIONCAP_RATE_REVERSE);
1810 session_set_caps(session, caps);
1812 return S_OK;
1815 static void session_set_topology(struct media_session *session, DWORD flags, IMFTopology *topology)
1817 IMFTopology *resolved_topology = NULL;
1818 HRESULT hr = S_OK;
1820 /* Resolve unless claimed to be full. */
1821 if (!(flags & MFSESSION_SETTOPOLOGY_CLEAR_CURRENT) && topology)
1823 if (!(flags & MFSESSION_SETTOPOLOGY_NORESOLUTION))
1825 hr = session_bind_output_nodes(topology);
1827 if (SUCCEEDED(hr))
1828 hr = IMFTopoLoader_Load(session->topo_loader, topology, &resolved_topology, NULL /* FIXME? */);
1829 if (SUCCEEDED(hr))
1830 hr = session_init_media_types(resolved_topology);
1832 if (SUCCEEDED(hr))
1834 topology = resolved_topology;
1839 if (flags & MFSESSION_SETTOPOLOGY_CLEAR_CURRENT)
1841 if ((topology && topology == session->presentation.current_topology) || !topology)
1843 /* FIXME: stop current topology, queue next one. */
1844 session_clear_presentation(session);
1846 else
1847 hr = S_FALSE;
1849 topology = NULL;
1851 else if (topology && flags & MFSESSION_SETTOPOLOGY_IMMEDIATE)
1853 session_clear_queued_topologies(session);
1854 session_clear_presentation(session);
1857 session_raise_topology_set(session, topology, hr);
1859 /* With no current topology set it right away, otherwise queue. */
1860 if (topology)
1862 struct queued_topology *queued_topology;
1864 if ((queued_topology = calloc(1, sizeof(*queued_topology))))
1866 queued_topology->topology = topology;
1867 IMFTopology_AddRef(queued_topology->topology);
1869 list_add_tail(&session->topologies, &queued_topology->entry);
1872 if (session->presentation.topo_status == MF_TOPOSTATUS_INVALID)
1874 hr = session_set_current_topology(session, topology);
1875 session_set_topo_status(session, hr, MF_TOPOSTATUS_READY);
1876 if (session->quality_manager)
1877 IMFQualityManager_NotifyTopology(session->quality_manager, topology);
1881 if (resolved_topology)
1882 IMFTopology_Release(resolved_topology);
1885 static HRESULT WINAPI mfsession_QueryInterface(IMFMediaSession *iface, REFIID riid, void **out)
1887 struct media_session *session = impl_from_IMFMediaSession(iface);
1889 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out);
1891 *out = NULL;
1893 if (IsEqualIID(riid, &IID_IMFMediaSession) ||
1894 IsEqualIID(riid, &IID_IMFMediaEventGenerator) ||
1895 IsEqualIID(riid, &IID_IUnknown))
1897 *out = &session->IMFMediaSession_iface;
1899 else if (IsEqualIID(riid, &IID_IMFGetService))
1901 *out = &session->IMFGetService_iface;
1903 else if (IsEqualIID(riid, &IID_IMFRateSupport))
1905 *out = &session->IMFRateSupport_iface;
1907 else if (IsEqualIID(riid, &IID_IMFRateControl))
1909 *out = &session->IMFRateControl_iface;
1912 if (*out)
1914 IMFMediaSession_AddRef(iface);
1915 return S_OK;
1918 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
1919 return E_NOINTERFACE;
1922 static ULONG WINAPI mfsession_AddRef(IMFMediaSession *iface)
1924 struct media_session *session = impl_from_IMFMediaSession(iface);
1925 ULONG refcount = InterlockedIncrement(&session->refcount);
1927 TRACE("%p, refcount %lu.\n", iface, refcount);
1929 return refcount;
1932 static ULONG WINAPI mfsession_Release(IMFMediaSession *iface)
1934 struct media_session *session = impl_from_IMFMediaSession(iface);
1935 ULONG refcount = InterlockedDecrement(&session->refcount);
1937 TRACE("%p, refcount %lu.\n", iface, refcount);
1939 if (!refcount)
1941 session_clear_queued_topologies(session);
1942 session_clear_presentation(session);
1943 session_clear_command_list(session);
1944 if (session->presentation.current_topology)
1945 IMFTopology_Release(session->presentation.current_topology);
1946 if (session->event_queue)
1947 IMFMediaEventQueue_Release(session->event_queue);
1948 if (session->clock)
1949 IMFPresentationClock_Release(session->clock);
1950 if (session->system_time_source)
1951 IMFPresentationTimeSource_Release(session->system_time_source);
1952 if (session->clock_rate_control)
1953 IMFRateControl_Release(session->clock_rate_control);
1954 if (session->topo_loader)
1955 IMFTopoLoader_Release(session->topo_loader);
1956 if (session->quality_manager)
1957 IMFQualityManager_Release(session->quality_manager);
1958 DeleteCriticalSection(&session->cs);
1959 free(session);
1962 return refcount;
1965 static HRESULT WINAPI mfsession_GetEvent(IMFMediaSession *iface, DWORD flags, IMFMediaEvent **event)
1967 struct media_session *session = impl_from_IMFMediaSession(iface);
1969 TRACE("%p, %#lx, %p.\n", iface, flags, event);
1971 return IMFMediaEventQueue_GetEvent(session->event_queue, flags, event);
1974 static HRESULT WINAPI mfsession_BeginGetEvent(IMFMediaSession *iface, IMFAsyncCallback *callback, IUnknown *state)
1976 struct media_session *session = impl_from_IMFMediaSession(iface);
1978 TRACE("%p, %p, %p.\n", iface, callback, state);
1980 return IMFMediaEventQueue_BeginGetEvent(session->event_queue, callback, state);
1983 static HRESULT WINAPI mfsession_EndGetEvent(IMFMediaSession *iface, IMFAsyncResult *result, IMFMediaEvent **event)
1985 struct media_session *session = impl_from_IMFMediaSession(iface);
1987 TRACE("%p, %p, %p.\n", iface, result, event);
1989 return IMFMediaEventQueue_EndGetEvent(session->event_queue, result, event);
1992 static HRESULT WINAPI mfsession_QueueEvent(IMFMediaSession *iface, MediaEventType event_type, REFGUID ext_type,
1993 HRESULT hr, const PROPVARIANT *value)
1995 struct media_session *session = impl_from_IMFMediaSession(iface);
1997 TRACE("%p, %ld, %s, %#lx, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value);
1999 return IMFMediaEventQueue_QueueEventParamVar(session->event_queue, event_type, ext_type, hr, value);
2002 static HRESULT session_check_stream_descriptor(IMFPresentationDescriptor *pd, IMFStreamDescriptor *sd)
2004 IMFStreamDescriptor *selected_sd;
2005 DWORD i, count;
2006 BOOL selected;
2007 HRESULT hr;
2009 if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorCount(pd, &count)))
2011 WARN("Failed to get stream descriptor count, hr %#lx.\n", hr);
2012 return MF_E_TOPO_STREAM_DESCRIPTOR_NOT_SELECTED;
2015 for (i = 0; i < count; ++i)
2017 if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, i,
2018 &selected, &selected_sd)))
2020 WARN("Failed to get stream descriptor %lu, hr %#lx.\n", i, hr);
2021 return MF_E_TOPO_STREAM_DESCRIPTOR_NOT_SELECTED;
2023 IMFStreamDescriptor_Release(selected_sd);
2025 if (selected_sd == sd)
2027 if (selected)
2028 return S_OK;
2030 WARN("Presentation descriptor %p stream %p is not selected.\n", pd, sd);
2031 return MF_E_TOPO_STREAM_DESCRIPTOR_NOT_SELECTED;
2035 WARN("Failed to find stream descriptor %lu, hr %#lx.\n", i, hr);
2036 return MF_E_TOPO_STREAM_DESCRIPTOR_NOT_SELECTED;
2039 static HRESULT session_check_topology(IMFTopology *topology)
2041 MF_TOPOLOGY_TYPE node_type;
2042 IMFTopologyNode *node;
2043 WORD node_count, i;
2044 HRESULT hr;
2046 if (!topology)
2047 return S_OK;
2049 if (FAILED(IMFTopology_GetNodeCount(topology, &node_count))
2050 || node_count == 0)
2051 return E_INVALIDARG;
2053 for (i = 0; i < node_count; ++i)
2055 if (FAILED(hr = IMFTopology_GetNode(topology, i, &node)))
2056 break;
2058 if (FAILED(hr = IMFTopologyNode_GetNodeType(node, &node_type)))
2060 IMFTopologyNode_Release(node);
2061 break;
2064 switch (node_type)
2066 case MF_TOPOLOGY_SOURCESTREAM_NODE:
2068 IMFPresentationDescriptor *pd;
2069 IMFStreamDescriptor *sd;
2070 IMFMediaSource *source;
2072 if (FAILED(hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_SOURCE, &IID_IMFMediaSource,
2073 (void **)&source)))
2075 WARN("Missing MF_TOPONODE_SOURCE, hr %#lx.\n", hr);
2076 IMFTopologyNode_Release(node);
2077 return MF_E_TOPO_MISSING_SOURCE;
2079 IMFMediaSource_Release(source);
2081 if (FAILED(hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR,
2082 &IID_IMFPresentationDescriptor, (void **)&pd)))
2084 WARN("Missing MF_TOPONODE_PRESENTATION_DESCRIPTOR, hr %#lx.\n", hr);
2085 IMFTopologyNode_Release(node);
2086 return MF_E_TOPO_MISSING_PRESENTATION_DESCRIPTOR;
2089 if (FAILED(hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_STREAM_DESCRIPTOR,
2090 &IID_IMFStreamDescriptor, (void **)&sd)))
2092 WARN("Missing MF_TOPONODE_STREAM_DESCRIPTOR, hr %#lx.\n", hr);
2093 IMFPresentationDescriptor_Release(pd);
2094 IMFTopologyNode_Release(node);
2095 return MF_E_TOPO_MISSING_STREAM_DESCRIPTOR;
2098 hr = session_check_stream_descriptor(pd, sd);
2099 IMFPresentationDescriptor_Release(pd);
2100 IMFStreamDescriptor_Release(sd);
2101 if (FAILED(hr))
2103 IMFTopologyNode_Release(node);
2104 return hr;
2107 break;
2110 default:
2111 break;
2114 IMFTopologyNode_Release(node);
2117 return hr;
2120 static HRESULT WINAPI mfsession_SetTopology(IMFMediaSession *iface, DWORD flags, IMFTopology *topology)
2122 struct media_session *session = impl_from_IMFMediaSession(iface);
2123 struct session_op *op;
2124 HRESULT hr;
2126 TRACE("%p, %#lx, %p.\n", iface, flags, topology);
2128 if (FAILED(hr = session_check_topology(topology)))
2129 return hr;
2131 if (FAILED(hr = create_session_op(SESSION_CMD_SET_TOPOLOGY, &op)))
2132 return hr;
2134 op->set_topology.flags = flags;
2135 op->set_topology.topology = topology;
2136 if (op->set_topology.topology)
2137 IMFTopology_AddRef(op->set_topology.topology);
2139 hr = session_submit_command(session, op);
2140 IUnknown_Release(&op->IUnknown_iface);
2142 return hr;
2145 static HRESULT WINAPI mfsession_ClearTopologies(IMFMediaSession *iface)
2147 struct media_session *session = impl_from_IMFMediaSession(iface);
2149 TRACE("%p.\n", iface);
2151 return session_submit_simple_command(session, SESSION_CMD_CLEAR_TOPOLOGIES);
2154 static HRESULT WINAPI mfsession_Start(IMFMediaSession *iface, const GUID *format, const PROPVARIANT *start_position)
2156 struct media_session *session = impl_from_IMFMediaSession(iface);
2157 struct session_op *op;
2158 HRESULT hr;
2160 TRACE("%p, %s, %s.\n", iface, debugstr_guid(format), debugstr_propvar(start_position));
2162 if (!start_position)
2163 return E_POINTER;
2165 if (FAILED(hr = create_session_op(SESSION_CMD_START, &op)))
2166 return hr;
2168 op->start.time_format = format ? *format : GUID_NULL;
2169 hr = PropVariantCopy(&op->start.start_position, start_position);
2171 if (SUCCEEDED(hr))
2172 hr = session_submit_command(session, op);
2174 IUnknown_Release(&op->IUnknown_iface);
2175 return hr;
2178 static HRESULT WINAPI mfsession_Pause(IMFMediaSession *iface)
2180 struct media_session *session = impl_from_IMFMediaSession(iface);
2182 TRACE("%p.\n", iface);
2184 return session_submit_simple_command(session, SESSION_CMD_PAUSE);
2187 static HRESULT WINAPI mfsession_Stop(IMFMediaSession *iface)
2189 struct media_session *session = impl_from_IMFMediaSession(iface);
2191 TRACE("%p.\n", iface);
2193 return session_submit_simple_command(session, SESSION_CMD_STOP);
2196 static HRESULT WINAPI mfsession_Close(IMFMediaSession *iface)
2198 struct media_session *session = impl_from_IMFMediaSession(iface);
2200 TRACE("%p.\n", iface);
2202 return session_submit_simple_command(session, SESSION_CMD_CLOSE);
2205 static HRESULT WINAPI mfsession_Shutdown(IMFMediaSession *iface)
2207 struct media_session *session = impl_from_IMFMediaSession(iface);
2208 HRESULT hr = S_OK;
2210 TRACE("%p.\n", iface);
2212 EnterCriticalSection(&session->cs);
2213 if (SUCCEEDED(hr = session_is_shut_down(session)))
2215 session->state = SESSION_STATE_SHUT_DOWN;
2216 IMFMediaEventQueue_Shutdown(session->event_queue);
2217 if (session->quality_manager)
2218 IMFQualityManager_Shutdown(session->quality_manager);
2219 MFShutdownObject((IUnknown *)session->clock);
2220 IMFPresentationClock_Release(session->clock);
2221 session->clock = NULL;
2222 session_clear_presentation(session);
2223 session_submit_simple_command(session, SESSION_CMD_SHUTDOWN);
2225 LeaveCriticalSection(&session->cs);
2227 return hr;
2230 static HRESULT WINAPI mfsession_GetClock(IMFMediaSession *iface, IMFClock **clock)
2232 struct media_session *session = impl_from_IMFMediaSession(iface);
2233 HRESULT hr;
2235 TRACE("%p, %p.\n", iface, clock);
2237 EnterCriticalSection(&session->cs);
2238 if (SUCCEEDED(hr = session_is_shut_down(session)))
2240 *clock = (IMFClock *)session->clock;
2241 IMFClock_AddRef(*clock);
2243 LeaveCriticalSection(&session->cs);
2245 return hr;
2248 static HRESULT WINAPI mfsession_GetSessionCapabilities(IMFMediaSession *iface, DWORD *caps)
2250 struct media_session *session = impl_from_IMFMediaSession(iface);
2251 HRESULT hr = S_OK;
2253 TRACE("%p, %p.\n", iface, caps);
2255 if (!caps)
2256 return E_POINTER;
2258 EnterCriticalSection(&session->cs);
2259 if (SUCCEEDED(hr = session_is_shut_down(session)))
2260 *caps = session->caps;
2261 LeaveCriticalSection(&session->cs);
2263 return hr;
2266 static HRESULT WINAPI mfsession_GetFullTopology(IMFMediaSession *iface, DWORD flags, TOPOID id, IMFTopology **topology)
2268 struct media_session *session = impl_from_IMFMediaSession(iface);
2269 struct queued_topology *queued;
2270 TOPOID topo_id;
2271 HRESULT hr;
2273 TRACE("%p, %#lx, %s, %p.\n", iface, flags, wine_dbgstr_longlong(id), topology);
2275 *topology = NULL;
2277 EnterCriticalSection(&session->cs);
2279 if (SUCCEEDED(hr = session_is_shut_down(session)))
2281 if (flags & MFSESSION_GETFULLTOPOLOGY_CURRENT)
2283 if (session->presentation.topo_status != MF_TOPOSTATUS_INVALID)
2284 *topology = session->presentation.current_topology;
2285 else
2286 hr = MF_E_INVALIDREQUEST;
2288 else
2290 LIST_FOR_EACH_ENTRY(queued, &session->topologies, struct queued_topology, entry)
2292 if (SUCCEEDED(IMFTopology_GetTopologyID(queued->topology, &topo_id)) && topo_id == id)
2294 *topology = queued->topology;
2295 break;
2300 if (*topology)
2301 IMFTopology_AddRef(*topology);
2304 LeaveCriticalSection(&session->cs);
2306 return hr;
2309 static const IMFMediaSessionVtbl mfmediasessionvtbl =
2311 mfsession_QueryInterface,
2312 mfsession_AddRef,
2313 mfsession_Release,
2314 mfsession_GetEvent,
2315 mfsession_BeginGetEvent,
2316 mfsession_EndGetEvent,
2317 mfsession_QueueEvent,
2318 mfsession_SetTopology,
2319 mfsession_ClearTopologies,
2320 mfsession_Start,
2321 mfsession_Pause,
2322 mfsession_Stop,
2323 mfsession_Close,
2324 mfsession_Shutdown,
2325 mfsession_GetClock,
2326 mfsession_GetSessionCapabilities,
2327 mfsession_GetFullTopology,
2330 static HRESULT WINAPI session_get_service_QueryInterface(IMFGetService *iface, REFIID riid, void **obj)
2332 struct media_session *session = impl_from_IMFGetService(iface);
2333 return IMFMediaSession_QueryInterface(&session->IMFMediaSession_iface, riid, obj);
2336 static ULONG WINAPI session_get_service_AddRef(IMFGetService *iface)
2338 struct media_session *session = impl_from_IMFGetService(iface);
2339 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
2342 static ULONG WINAPI session_get_service_Release(IMFGetService *iface)
2344 struct media_session *session = impl_from_IMFGetService(iface);
2345 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
2348 typedef BOOL (*p_renderer_node_test_func)(IMFMediaSink *sink);
2350 static BOOL session_video_renderer_test_func(IMFMediaSink *sink)
2352 IUnknown *obj;
2353 HRESULT hr;
2355 /* Use first sink to support IMFVideoRenderer. */
2356 hr = IMFMediaSink_QueryInterface(sink, &IID_IMFVideoRenderer, (void **)&obj);
2357 if (obj)
2358 IUnknown_Release(obj);
2360 return hr == S_OK;
2363 static BOOL session_audio_renderer_test_func(IMFMediaSink *sink)
2365 return mf_is_sar_sink(sink);
2368 static HRESULT session_get_renderer_node_service(struct media_session *session,
2369 p_renderer_node_test_func node_test_func, REFGUID service, REFIID riid, void **obj)
2371 HRESULT hr = E_NOINTERFACE;
2372 IMFStreamSink *stream_sink;
2373 IMFTopologyNode *node;
2374 IMFCollection *nodes;
2375 IMFMediaSink *sink;
2376 unsigned int i = 0;
2378 if (session->presentation.current_topology)
2380 if (SUCCEEDED(IMFTopology_GetOutputNodeCollection(session->presentation.current_topology,
2381 &nodes)))
2383 while (IMFCollection_GetElement(nodes, i++, (IUnknown **)&node) == S_OK)
2385 if (SUCCEEDED(topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink)))
2387 if (SUCCEEDED(IMFStreamSink_GetMediaSink(stream_sink, &sink)))
2389 if (node_test_func(sink))
2391 if (FAILED(hr = MFGetService((IUnknown *)sink, service, riid, obj)))
2392 WARN("Failed to get service from renderer node, %#lx.\n", hr);
2395 IMFStreamSink_Release(stream_sink);
2398 IMFTopologyNode_Release(node);
2400 if (*obj)
2401 break;
2404 IMFCollection_Release(nodes);
2408 return hr;
2411 static HRESULT session_get_audio_render_service(struct media_session *session, REFGUID service,
2412 REFIID riid, void **obj)
2414 return session_get_renderer_node_service(session, session_audio_renderer_test_func,
2415 service, riid, obj);
2418 static HRESULT session_get_video_render_service(struct media_session *session, REFGUID service,
2419 REFIID riid, void **obj)
2421 return session_get_renderer_node_service(session, session_video_renderer_test_func,
2422 service, riid, obj);
2425 static HRESULT WINAPI session_get_service_GetService(IMFGetService *iface, REFGUID service, REFIID riid, void **obj)
2427 struct media_session *session = impl_from_IMFGetService(iface);
2428 HRESULT hr = S_OK;
2430 TRACE("%p, %s, %s, %p.\n", iface, debugstr_guid(service), debugstr_guid(riid), obj);
2432 *obj = NULL;
2434 EnterCriticalSection(&session->cs);
2435 if (FAILED(hr = session_is_shut_down(session)))
2438 else if (IsEqualGUID(service, &MF_RATE_CONTROL_SERVICE))
2440 if (IsEqualIID(riid, &IID_IMFRateSupport))
2442 *obj = &session->IMFRateSupport_iface;
2444 else if (IsEqualIID(riid, &IID_IMFRateControl))
2446 *obj = &session->IMFRateControl_iface;
2448 else
2449 hr = E_NOINTERFACE;
2451 if (*obj)
2452 IUnknown_AddRef((IUnknown *)*obj);
2454 else if (IsEqualGUID(service, &MF_LOCAL_MFT_REGISTRATION_SERVICE))
2456 hr = IMFLocalMFTRegistration_QueryInterface(&local_mft_registration, riid, obj);
2458 else if (IsEqualGUID(service, &MF_TOPONODE_ATTRIBUTE_EDITOR_SERVICE))
2460 *obj = &session->IMFTopologyNodeAttributeEditor_iface;
2461 IUnknown_AddRef((IUnknown *)*obj);
2463 else if (IsEqualGUID(service, &MR_VIDEO_RENDER_SERVICE))
2465 hr = session_get_video_render_service(session, service, riid, obj);
2467 else if (IsEqualGUID(service, &MR_POLICY_VOLUME_SERVICE) ||
2468 IsEqualGUID(service, &MR_STREAM_VOLUME_SERVICE))
2470 hr = session_get_audio_render_service(session, service, riid, obj);
2472 else
2473 FIXME("Unsupported service %s.\n", debugstr_guid(service));
2475 LeaveCriticalSection(&session->cs);
2477 return hr;
2480 static const IMFGetServiceVtbl session_get_service_vtbl =
2482 session_get_service_QueryInterface,
2483 session_get_service_AddRef,
2484 session_get_service_Release,
2485 session_get_service_GetService,
2488 static HRESULT WINAPI session_commands_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
2490 if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
2491 IsEqualIID(riid, &IID_IUnknown))
2493 *obj = iface;
2494 IMFAsyncCallback_AddRef(iface);
2495 return S_OK;
2498 WARN("Unsupported %s.\n", debugstr_guid(riid));
2499 *obj = NULL;
2500 return E_NOINTERFACE;
2503 static ULONG WINAPI session_commands_callback_AddRef(IMFAsyncCallback *iface)
2505 struct media_session *session = impl_from_commands_callback_IMFAsyncCallback(iface);
2506 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
2509 static ULONG WINAPI session_commands_callback_Release(IMFAsyncCallback *iface)
2511 struct media_session *session = impl_from_commands_callback_IMFAsyncCallback(iface);
2512 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
2515 static HRESULT WINAPI session_commands_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
2517 return E_NOTIMPL;
2520 static void session_deliver_pending_samples(struct media_session *session, IMFTopologyNode *node);
2522 static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
2524 struct session_op *op = impl_op_from_IUnknown(IMFAsyncResult_GetStateNoAddRef(result));
2525 struct media_session *session = impl_from_commands_callback_IMFAsyncCallback(iface);
2527 TRACE("session %p, op %p, command %u.\n", session, op, op->command);
2529 EnterCriticalSection(&session->cs);
2531 if (session->presentation.flags & SESSION_FLAG_PENDING_COMMAND)
2533 WARN("session %p command is in progress, waiting for it to complete.\n", session);
2534 LeaveCriticalSection(&session->cs);
2535 return S_OK;
2538 list_remove(&op->entry);
2539 session->presentation.flags |= SESSION_FLAG_PENDING_COMMAND;
2541 switch (op->command)
2543 case SESSION_CMD_CLEAR_TOPOLOGIES:
2544 session_clear_topologies(session);
2545 break;
2546 case SESSION_CMD_SET_TOPOLOGY:
2547 session_set_topology(session, op->set_topology.flags, op->set_topology.topology);
2548 session_command_complete(session);
2549 break;
2550 case SESSION_CMD_START:
2551 session_start(session, &op->start.time_format, &op->start.start_position);
2552 break;
2553 case SESSION_CMD_PAUSE:
2554 session_pause(session);
2555 break;
2556 case SESSION_CMD_STOP:
2557 session_stop(session);
2558 break;
2559 case SESSION_CMD_CLOSE:
2560 session_close(session);
2561 break;
2562 case SESSION_CMD_SET_RATE:
2563 session_set_rate(session, op->set_rate.thin, op->set_rate.rate);
2564 break;
2565 case SESSION_CMD_SHUTDOWN:
2566 session_clear_command_list(session);
2567 session_command_complete(session);
2568 break;
2569 default:
2573 LeaveCriticalSection(&session->cs);
2575 IUnknown_Release(&op->IUnknown_iface);
2577 return S_OK;
2580 static const IMFAsyncCallbackVtbl session_commands_callback_vtbl =
2582 session_commands_callback_QueryInterface,
2583 session_commands_callback_AddRef,
2584 session_commands_callback_Release,
2585 session_commands_callback_GetParameters,
2586 session_commands_callback_Invoke,
2589 static HRESULT WINAPI session_sa_ready_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
2591 if (IsEqualIID(riid, &IID_IMFAsyncCallback)
2592 || IsEqualIID(riid, &IID_IUnknown))
2594 *obj = iface;
2595 IMFAsyncCallback_AddRef(iface);
2596 return S_OK;
2599 WARN("Unsupported %s.\n", debugstr_guid(riid));
2600 *obj = NULL;
2601 return E_NOINTERFACE;
2604 static ULONG WINAPI session_sa_ready_callback_AddRef(IMFAsyncCallback *iface)
2606 struct media_session *session = impl_from_sa_ready_callback_IMFAsyncCallback(iface);
2607 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
2610 static ULONG WINAPI session_sa_ready_callback_Release(IMFAsyncCallback *iface)
2612 struct media_session *session = impl_from_sa_ready_callback_IMFAsyncCallback(iface);
2613 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
2616 static HRESULT WINAPI session_sa_ready_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
2618 return E_NOTIMPL;
2621 static HRESULT WINAPI session_sa_ready_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
2623 IMFVideoSampleAllocatorNotify *notify = (IMFVideoSampleAllocatorNotify *)IMFAsyncResult_GetStateNoAddRef(result);
2624 struct topo_node *topo_node = impl_node_from_IMFVideoSampleAllocatorNotify(notify);
2625 struct media_session *session = impl_from_sa_ready_callback_IMFAsyncCallback(iface);
2626 IMFTopologyNode *upstream_node;
2627 DWORD upstream_output;
2629 EnterCriticalSection(&session->cs);
2631 if (topo_node->u.sink.requests)
2633 if (SUCCEEDED(IMFTopologyNode_GetInput(topo_node->node, 0, &upstream_node, &upstream_output)))
2635 session_deliver_pending_samples(session, upstream_node);
2636 IMFTopologyNode_Release(upstream_node);
2640 LeaveCriticalSection(&session->cs);
2642 return S_OK;
2645 static const IMFAsyncCallbackVtbl session_sa_ready_callback_vtbl =
2647 session_sa_ready_callback_QueryInterface,
2648 session_sa_ready_callback_AddRef,
2649 session_sa_ready_callback_Release,
2650 session_sa_ready_callback_GetParameters,
2651 session_sa_ready_callback_Invoke,
2654 static HRESULT WINAPI session_events_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
2656 if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
2657 IsEqualIID(riid, &IID_IUnknown))
2659 *obj = iface;
2660 IMFAsyncCallback_AddRef(iface);
2661 return S_OK;
2664 WARN("Unsupported %s.\n", debugstr_guid(riid));
2665 *obj = NULL;
2666 return E_NOINTERFACE;
2669 static ULONG WINAPI session_events_callback_AddRef(IMFAsyncCallback *iface)
2671 struct media_session *session = impl_from_events_callback_IMFAsyncCallback(iface);
2672 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
2675 static ULONG WINAPI session_events_callback_Release(IMFAsyncCallback *iface)
2677 struct media_session *session = impl_from_events_callback_IMFAsyncCallback(iface);
2678 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
2681 static HRESULT WINAPI session_events_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
2683 return E_NOTIMPL;
2686 static HRESULT session_add_media_stream(struct media_session *session, IMFMediaSource *source, IMFMediaStream *stream)
2688 struct topo_node *node;
2689 IMFStreamDescriptor *sd;
2690 DWORD stream_id = 0;
2691 HRESULT hr;
2693 if (FAILED(hr = IMFMediaStream_GetStreamDescriptor(stream, &sd)))
2694 return hr;
2696 hr = IMFStreamDescriptor_GetStreamIdentifier(sd, &stream_id);
2697 IMFStreamDescriptor_Release(sd);
2698 if (FAILED(hr))
2699 return hr;
2701 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
2703 if (node->type == MF_TOPOLOGY_SOURCESTREAM_NODE && node->u.source.source == source
2704 && node->u.source.stream_id == stream_id)
2706 if (node->object.source_stream)
2708 WARN("Node already has stream set.\n");
2709 return S_FALSE;
2712 node->object.source_stream = stream;
2713 IMFMediaStream_AddRef(node->object.source_stream);
2714 break;
2718 return S_OK;
2721 static BOOL session_is_source_nodes_state(struct media_session *session, enum object_state state)
2723 struct media_source *source;
2724 struct topo_node *node;
2726 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
2728 if (source->state != state)
2729 return FALSE;
2732 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
2734 if (node->type == MF_TOPOLOGY_SOURCESTREAM_NODE && node->state != state)
2735 return FALSE;
2738 return TRUE;
2741 static BOOL session_is_output_nodes_state(struct media_session *session, enum object_state state)
2743 struct topo_node *node;
2745 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
2747 if (node->type == MF_TOPOLOGY_OUTPUT_NODE && node->state != state)
2748 return FALSE;
2751 return TRUE;
2754 static enum object_state session_get_object_state_for_event(MediaEventType event)
2756 switch (event)
2758 case MESourceSeeked:
2759 case MEStreamSeeked:
2760 case MESourceStarted:
2761 case MEStreamStarted:
2762 case MEStreamSinkStarted:
2763 return OBJ_STATE_STARTED;
2764 case MESourcePaused:
2765 case MEStreamPaused:
2766 case MEStreamSinkPaused:
2767 return OBJ_STATE_PAUSED;
2768 case MESourceStopped:
2769 case MEStreamStopped:
2770 case MEStreamSinkStopped:
2771 return OBJ_STATE_STOPPED;
2772 case MEStreamSinkPrerolled:
2773 return OBJ_STATE_PREROLLED;
2774 default:
2775 return OBJ_STATE_INVALID;
2779 static HRESULT session_start_clock(struct media_session *session)
2781 LONGLONG start_offset = 0;
2782 HRESULT hr;
2784 if (IsEqualGUID(&session->presentation.time_format, &GUID_NULL))
2786 if (session->presentation.start_position.vt == VT_EMPTY)
2787 start_offset = PRESENTATION_CURRENT_POSITION;
2788 else if (session->presentation.start_position.vt == VT_I8)
2789 start_offset = session->presentation.start_position.hVal.QuadPart;
2790 else
2791 FIXME("Unhandled position type %d.\n", session->presentation.start_position.vt);
2793 else
2794 FIXME("Unhandled time format %s.\n", debugstr_guid(&session->presentation.time_format));
2796 if (FAILED(hr = IMFPresentationClock_Start(session->clock, start_offset)))
2797 WARN("Failed to start session clock, hr %#lx.\n", hr);
2799 return hr;
2802 static struct topo_node *session_get_node_object(struct media_session *session, IUnknown *object,
2803 MF_TOPOLOGY_TYPE node_type)
2805 struct topo_node *node = NULL;
2807 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
2809 if (node->type == node_type && object == node->object.object)
2810 break;
2813 return node;
2816 static BOOL session_set_node_object_state(struct media_session *session, IUnknown *object,
2817 MF_TOPOLOGY_TYPE node_type, enum object_state state)
2819 struct topo_node *node;
2820 BOOL changed = FALSE;
2822 if ((node = session_get_node_object(session, object, node_type)))
2824 changed = node->state != state;
2825 node->state = state;
2828 return changed;
2831 static void session_set_source_object_state(struct media_session *session, IUnknown *object,
2832 MediaEventType event_type)
2834 IMFStreamSink *stream_sink;
2835 struct media_source *src;
2836 struct media_sink *sink;
2837 enum object_state state;
2838 struct topo_node *node;
2839 BOOL changed = FALSE;
2840 DWORD i, count;
2841 HRESULT hr;
2843 if ((state = session_get_object_state_for_event(event_type)) == OBJ_STATE_INVALID)
2844 return;
2846 switch (event_type)
2848 case MESourceSeeked:
2849 case MESourceStarted:
2850 case MESourcePaused:
2851 case MESourceStopped:
2853 LIST_FOR_EACH_ENTRY(src, &session->presentation.sources, struct media_source, entry)
2855 if (object == src->object)
2857 changed = src->state != state;
2858 src->state = state;
2859 break;
2862 break;
2863 case MEStreamSeeked:
2864 case MEStreamStarted:
2865 case MEStreamPaused:
2866 case MEStreamStopped:
2868 changed = session_set_node_object_state(session, object, MF_TOPOLOGY_SOURCESTREAM_NODE, state);
2869 default:
2873 if (!changed)
2874 return;
2876 switch (session->state)
2878 case SESSION_STATE_STARTING_SOURCES:
2879 if (!session_is_source_nodes_state(session, OBJ_STATE_STARTED))
2880 break;
2882 session_set_topo_status(session, S_OK, MF_TOPOSTATUS_STARTED_SOURCE);
2884 session_set_presentation_clock(session);
2886 if (session->presentation.flags & SESSION_FLAG_NEEDS_PREROLL)
2888 MFTIME preroll_time = 0;
2890 if (session->presentation.start_position.vt == VT_I8)
2891 preroll_time = session->presentation.start_position.hVal.QuadPart;
2893 /* Mark stream sinks without prerolling support as PREROLLED to keep state test logic generic. */
2894 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
2896 if (sink->preroll)
2898 /* FIXME: abort and enter error state on failure. */
2899 if (FAILED(hr = IMFMediaSinkPreroll_NotifyPreroll(sink->preroll, preroll_time)))
2900 WARN("Preroll notification failed, hr %#lx.\n", hr);
2902 else
2904 if (SUCCEEDED(IMFMediaSink_GetStreamSinkCount(sink->sink, &count)))
2906 for (i = 0; i < count; ++i)
2908 if (SUCCEEDED(IMFMediaSink_GetStreamSinkByIndex(sink->sink, i, &stream_sink)))
2910 session_set_node_object_state(session, (IUnknown *)stream_sink, MF_TOPOLOGY_OUTPUT_NODE,
2911 OBJ_STATE_PREROLLED);
2912 IMFStreamSink_Release(stream_sink);
2918 session->state = SESSION_STATE_PREROLLING_SINKS;
2920 else if (SUCCEEDED(session_start_clock(session)))
2921 session->state = SESSION_STATE_STARTING_SINKS;
2923 break;
2924 case SESSION_STATE_PAUSING_SOURCES:
2925 if (!session_is_source_nodes_state(session, OBJ_STATE_PAUSED))
2926 break;
2928 session_set_paused(session, SESSION_STATE_PAUSED, S_OK);
2929 break;
2930 case SESSION_STATE_STOPPING_SOURCES:
2931 if (!session_is_source_nodes_state(session, OBJ_STATE_STOPPED))
2932 break;
2934 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
2936 switch (node->type)
2938 case MF_TOPOLOGY_OUTPUT_NODE:
2939 IMFStreamSink_Flush(node->object.sink_stream);
2940 break;
2941 case MF_TOPOLOGY_TRANSFORM_NODE:
2942 IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_COMMAND_FLUSH, 0);
2943 break;
2944 default:
2949 session_set_caps(session, session->caps & ~MFSESSIONCAP_PAUSE);
2951 if (session->presentation.flags & SESSION_FLAG_FINALIZE_SINKS)
2952 session_finalize_sinks(session);
2953 else
2954 session_set_stopped(session, S_OK);
2956 break;
2957 default:
2962 static void session_set_sink_stream_state(struct media_session *session, IMFStreamSink *stream,
2963 MediaEventType event_type)
2965 struct media_source *source;
2966 enum object_state state;
2967 HRESULT hr = S_OK;
2968 BOOL changed;
2970 if ((state = session_get_object_state_for_event(event_type)) == OBJ_STATE_INVALID)
2971 return;
2973 if (!(changed = session_set_node_object_state(session, (IUnknown *)stream, MF_TOPOLOGY_OUTPUT_NODE, state)))
2974 return;
2976 switch (session->state)
2978 case SESSION_STATE_PREROLLING_SINKS:
2979 if (!session_is_output_nodes_state(session, OBJ_STATE_PREROLLED))
2980 break;
2982 if (SUCCEEDED(session_start_clock(session)))
2983 session->state = SESSION_STATE_STARTING_SINKS;
2984 break;
2985 case SESSION_STATE_STARTING_SINKS:
2986 if (!session_is_output_nodes_state(session, OBJ_STATE_STARTED))
2987 break;
2989 session_set_started(session);
2990 break;
2991 case SESSION_STATE_PAUSING_SINKS:
2992 if (!session_is_output_nodes_state(session, OBJ_STATE_PAUSED))
2993 break;
2995 session->state = SESSION_STATE_PAUSING_SOURCES;
2997 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
2999 if (FAILED(hr = IMFMediaSource_Pause(source->source)))
3000 break;
3003 if (FAILED(hr))
3004 session_set_paused(session, SESSION_STATE_PAUSED, hr);
3006 break;
3007 case SESSION_STATE_STOPPING_SINKS:
3008 if (!session_is_output_nodes_state(session, OBJ_STATE_STOPPED))
3009 break;
3011 session->state = SESSION_STATE_STOPPING_SOURCES;
3013 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
3015 if (session->presentation.flags & SESSION_FLAG_END_OF_PRESENTATION)
3016 IMFMediaSource_Stop(source->source);
3017 else if (FAILED(hr = IMFMediaSource_Stop(source->source)))
3018 break;
3021 if (session->presentation.flags & SESSION_FLAG_END_OF_PRESENTATION || FAILED(hr))
3022 session_set_stopped(session, hr);
3024 break;
3025 default:
3030 static struct sample *transform_create_sample(IMFSample *sample)
3032 struct sample *sample_entry = calloc(1, sizeof(*sample_entry));
3034 if (sample_entry)
3036 sample_entry->sample = sample;
3037 if (sample_entry->sample)
3038 IMFSample_AddRef(sample_entry->sample);
3041 return sample_entry;
3044 static HRESULT transform_get_external_output_sample(const struct media_session *session, struct topo_node *transform,
3045 unsigned int output_index, const MFT_OUTPUT_STREAM_INFO *stream_info, IMFSample **sample)
3047 IMFTopologyNode *downstream_node;
3048 IMFMediaBuffer *buffer = NULL;
3049 struct topo_node *topo_node;
3050 unsigned int buffer_size;
3051 DWORD downstream_input;
3052 TOPOID node_id;
3053 HRESULT hr;
3055 if (FAILED(IMFTopologyNode_GetOutput(transform->node, output_index, &downstream_node, &downstream_input)))
3057 WARN("Failed to get connected node for output %u.\n", output_index);
3058 return MF_E_UNEXPECTED;
3061 IMFTopologyNode_GetTopoNodeID(downstream_node, &node_id);
3062 IMFTopologyNode_Release(downstream_node);
3064 topo_node = session_get_node_by_id(session, node_id);
3066 if (topo_node->type == MF_TOPOLOGY_OUTPUT_NODE && topo_node->u.sink.allocator)
3068 hr = IMFVideoSampleAllocator_AllocateSample(topo_node->u.sink.allocator, sample);
3070 else
3072 buffer_size = max(stream_info->cbSize, transform->u.transform.outputs[output_index].min_buffer_size);
3074 hr = MFCreateAlignedMemoryBuffer(buffer_size, stream_info->cbAlignment, &buffer);
3075 if (SUCCEEDED(hr))
3076 hr = MFCreateSample(sample);
3078 if (SUCCEEDED(hr))
3079 hr = IMFSample_AddBuffer(*sample, buffer);
3081 if (buffer)
3082 IMFMediaBuffer_Release(buffer);
3085 return hr;
3088 static HRESULT transform_node_pull_samples(const struct media_session *session, struct topo_node *node)
3090 MFT_OUTPUT_STREAM_INFO stream_info;
3091 MFT_OUTPUT_DATA_BUFFER *buffers;
3092 struct sample *queued_sample;
3093 HRESULT hr = E_UNEXPECTED;
3094 DWORD status = 0;
3095 unsigned int i;
3097 if (!(buffers = calloc(node->u.transform.output_count, sizeof(*buffers))))
3098 return E_OUTOFMEMORY;
3100 for (i = 0; i < node->u.transform.output_count; ++i)
3102 buffers[i].dwStreamID = transform_node_get_stream_id(node, TRUE, i);
3103 buffers[i].pSample = NULL;
3104 buffers[i].dwStatus = 0;
3105 buffers[i].pEvents = NULL;
3107 memset(&stream_info, 0, sizeof(stream_info));
3108 if (FAILED(hr = IMFTransform_GetOutputStreamInfo(node->object.transform, buffers[i].dwStreamID, &stream_info)))
3109 break;
3111 if (!(stream_info.dwFlags & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)))
3113 if (FAILED(hr = transform_get_external_output_sample(session, node, i, &stream_info, &buffers[i].pSample)))
3114 break;
3118 if (SUCCEEDED(hr))
3119 hr = IMFTransform_ProcessOutput(node->object.transform, 0, node->u.transform.output_count, buffers, &status);
3121 /* Collect returned samples for all streams. */
3122 for (i = 0; i < node->u.transform.output_count; ++i)
3124 if (buffers[i].pEvents)
3125 IMFCollection_Release(buffers[i].pEvents);
3127 if (SUCCEEDED(hr) && !(buffers[i].dwStatus & MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE))
3129 if (session->quality_manager)
3130 IMFQualityManager_NotifyProcessOutput(session->quality_manager, node->node, i, buffers[i].pSample);
3132 queued_sample = transform_create_sample(buffers[i].pSample);
3133 list_add_tail(&node->u.transform.outputs[i].samples, &queued_sample->entry);
3136 if (buffers[i].pSample)
3137 IMFSample_Release(buffers[i].pSample);
3140 free(buffers);
3142 return hr;
3145 static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input,
3146 IMFSample *sample)
3148 struct sample *sample_entry, *sample_entry2;
3149 DWORD stream_id, downstream_input;
3150 IMFTopologyNode *downstream_node;
3151 struct topo_node *topo_node;
3152 MF_TOPOLOGY_TYPE node_type;
3153 BOOL drain = FALSE;
3154 TOPOID node_id;
3155 unsigned int i;
3156 HRESULT hr;
3158 if (session->quality_manager)
3159 IMFQualityManager_NotifyProcessInput(session->quality_manager, node, input, sample);
3161 IMFTopologyNode_GetNodeType(node, &node_type);
3162 IMFTopologyNode_GetTopoNodeID(node, &node_id);
3164 topo_node = session_get_node_by_id(session, node_id);
3166 switch (node_type)
3168 case MF_TOPOLOGY_OUTPUT_NODE:
3169 if (topo_node->u.sink.requests)
3171 if (sample)
3173 if (FAILED(hr = IMFStreamSink_ProcessSample(topo_node->object.sink_stream, sample)))
3174 WARN("Stream sink failed to process sample, hr %#lx.\n", hr);
3176 else if (FAILED(hr = IMFStreamSink_PlaceMarker(topo_node->object.sink_stream, MFSTREAMSINK_MARKER_ENDOFSEGMENT,
3177 NULL, NULL)))
3179 WARN("Failed to place sink marker, hr %#lx.\n", hr);
3181 topo_node->u.sink.requests--;
3183 break;
3184 case MF_TOPOLOGY_TRANSFORM_NODE:
3186 transform_node_pull_samples(session, topo_node);
3188 sample_entry = transform_create_sample(sample);
3189 list_add_tail(&topo_node->u.transform.inputs[input].samples, &sample_entry->entry);
3191 for (i = 0; i < topo_node->u.transform.input_count; ++i)
3193 stream_id = transform_node_get_stream_id(topo_node, FALSE, i);
3194 LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &topo_node->u.transform.inputs[i].samples,
3195 struct sample, entry)
3197 if (sample_entry->sample)
3199 if ((hr = IMFTransform_ProcessInput(topo_node->object.transform, stream_id,
3200 sample_entry->sample, 0)) == MF_E_NOTACCEPTING)
3201 break;
3202 if (FAILED(hr))
3203 WARN("Failed to process input for stream %u/%lu, hr %#lx.\n", i, stream_id, hr);
3204 transform_release_sample(sample_entry);
3206 else
3208 transform_stream_drop_samples(&topo_node->u.transform.inputs[i]);
3209 drain = TRUE;
3214 if (drain)
3216 if (FAILED(hr = IMFTransform_ProcessMessage(topo_node->object.transform, MFT_MESSAGE_COMMAND_DRAIN, 0)))
3217 WARN("Drain command failed for transform, hr %#lx.\n", hr);
3220 transform_node_pull_samples(session, topo_node);
3222 /* Remaining unprocessed input has been discarded, now queue markers for every output. */
3223 if (drain)
3225 for (i = 0; i < topo_node->u.transform.output_count; ++i)
3227 if ((sample_entry = transform_create_sample(NULL)))
3228 list_add_tail(&topo_node->u.transform.outputs[i].samples, &sample_entry->entry);
3232 /* Push down all available output. */
3233 for (i = 0; i < topo_node->u.transform.output_count; ++i)
3235 if (FAILED(IMFTopologyNode_GetOutput(node, i, &downstream_node, &downstream_input)))
3237 WARN("Failed to get connected node for output %u.\n", i);
3238 continue;
3241 LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &topo_node->u.transform.outputs[i].samples,
3242 struct sample, entry)
3244 if (!topo_node->u.transform.outputs[i].requests)
3245 break;
3247 session_deliver_sample_to_node(session, downstream_node, downstream_input, sample_entry->sample);
3248 topo_node->u.transform.outputs[i].requests--;
3250 transform_release_sample(sample_entry);
3253 IMFTopologyNode_Release(downstream_node);
3256 break;
3257 case MF_TOPOLOGY_TEE_NODE:
3258 FIXME("Unhandled downstream node type %d.\n", node_type);
3259 break;
3260 default:
3265 static void session_deliver_pending_samples(struct media_session *session, IMFTopologyNode *node)
3267 struct sample *sample_entry, *sample_entry2;
3268 IMFTopologyNode *downstream_node;
3269 struct topo_node *topo_node;
3270 MF_TOPOLOGY_TYPE node_type;
3271 DWORD downstream_input;
3272 TOPOID node_id;
3273 unsigned int i;
3275 IMFTopologyNode_GetNodeType(node, &node_type);
3276 IMFTopologyNode_GetTopoNodeID(node, &node_id);
3278 topo_node = session_get_node_by_id(session, node_id);
3280 switch (node_type)
3282 case MF_TOPOLOGY_TRANSFORM_NODE:
3284 transform_node_pull_samples(session, topo_node);
3286 /* Push down all available output. */
3287 for (i = 0; i < topo_node->u.transform.output_count; ++i)
3289 if (FAILED(IMFTopologyNode_GetOutput(node, i, &downstream_node, &downstream_input)))
3291 WARN("Failed to get connected node for output %u.\n", i);
3292 continue;
3295 LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &topo_node->u.transform.outputs[i].samples,
3296 struct sample, entry)
3298 if (!topo_node->u.transform.outputs[i].requests)
3299 break;
3301 session_deliver_sample_to_node(session, downstream_node, downstream_input, sample_entry->sample);
3302 topo_node->u.transform.outputs[i].requests--;
3304 transform_release_sample(sample_entry);
3307 IMFTopologyNode_Release(downstream_node);
3309 break;
3310 default:
3311 FIXME("Unexpected node type %u.\n", node_type);
3316 static HRESULT session_request_sample_from_node(struct media_session *session, IMFTopologyNode *node, DWORD output)
3318 IMFTopologyNode *downstream_node, *upstream_node;
3319 DWORD downstream_input, upstream_output;
3320 struct topo_node *topo_node;
3321 MF_TOPOLOGY_TYPE node_type;
3322 struct sample *sample;
3323 TOPOID node_id;
3324 HRESULT hr;
3326 IMFTopologyNode_GetNodeType(node, &node_type);
3327 IMFTopologyNode_GetTopoNodeID(node, &node_id);
3329 topo_node = session_get_node_by_id(session, node_id);
3331 switch (node_type)
3333 case MF_TOPOLOGY_SOURCESTREAM_NODE:
3334 if (FAILED(hr = IMFMediaStream_RequestSample(topo_node->object.source_stream, NULL)))
3335 WARN("Sample request failed, hr %#lx.\n", hr);
3336 break;
3337 case MF_TOPOLOGY_TRANSFORM_NODE:
3339 if (list_empty(&topo_node->u.transform.outputs[output].samples))
3341 /* Forward request to upstream node. */
3342 if (SUCCEEDED(hr = IMFTopologyNode_GetInput(node, 0 /* FIXME */, &upstream_node, &upstream_output)))
3344 if (SUCCEEDED(hr = session_request_sample_from_node(session, upstream_node, upstream_output)))
3345 topo_node->u.transform.outputs[output].requests++;
3346 IMFTopologyNode_Release(upstream_node);
3349 else
3351 if (SUCCEEDED(hr = IMFTopologyNode_GetOutput(node, output, &downstream_node, &downstream_input)))
3353 sample = LIST_ENTRY(list_head(&topo_node->u.transform.outputs[output].samples), struct sample, entry);
3354 session_deliver_sample_to_node(session, downstream_node, downstream_input, sample->sample);
3355 transform_release_sample(sample);
3356 IMFTopologyNode_Release(downstream_node);
3360 break;
3361 case MF_TOPOLOGY_TEE_NODE:
3362 FIXME("Unhandled upstream node type %d.\n", node_type);
3363 default:
3364 hr = E_UNEXPECTED;
3367 return hr;
3370 static void session_request_sample(struct media_session *session, IMFStreamSink *sink_stream)
3372 struct topo_node *sink_node = NULL, *node;
3373 IMFTopologyNode *upstream_node;
3374 DWORD upstream_output;
3375 HRESULT hr;
3377 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
3379 if (node->type == MF_TOPOLOGY_OUTPUT_NODE && node->object.sink_stream == sink_stream)
3381 sink_node = node;
3382 break;
3386 if (!sink_node)
3387 return;
3389 if (FAILED(hr = IMFTopologyNode_GetInput(sink_node->node, 0, &upstream_node, &upstream_output)))
3391 WARN("Failed to get upstream node connection, hr %#lx.\n", hr);
3392 return;
3395 sink_node->u.sink.requests++;
3396 if (FAILED(session_request_sample_from_node(session, upstream_node, upstream_output)))
3397 sink_node->u.sink.requests--;
3398 IMFTopologyNode_Release(upstream_node);
3401 static void session_deliver_sample(struct media_session *session, IMFMediaStream *stream, const PROPVARIANT *value)
3403 struct topo_node *source_node = NULL, *node;
3404 IMFTopologyNode *downstream_node;
3405 DWORD downstream_input;
3406 HRESULT hr;
3408 if (value && (value->vt != VT_UNKNOWN || !value->punkVal))
3410 WARN("Unexpected value type %d.\n", value->vt);
3411 return;
3414 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
3416 if (node->type == MF_TOPOLOGY_SOURCESTREAM_NODE && node->object.source_stream == stream)
3418 source_node = node;
3419 break;
3423 if (!source_node)
3424 return;
3426 if (!value)
3427 source_node->flags |= TOPO_NODE_END_OF_STREAM;
3429 if (FAILED(hr = IMFTopologyNode_GetOutput(source_node->node, 0, &downstream_node, &downstream_input)))
3431 WARN("Failed to get downstream node connection, hr %#lx.\n", hr);
3432 return;
3435 session_deliver_sample_to_node(session, downstream_node, downstream_input, value ? (IMFSample *)value->punkVal : NULL);
3436 IMFTopologyNode_Release(downstream_node);
3439 static void session_sink_invalidated(struct media_session *session, IMFMediaEvent *event, IMFStreamSink *sink)
3441 struct topo_node *node, *sink_node = NULL;
3442 HRESULT hr;
3444 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
3446 if (node->type == MF_TOPOLOGY_OUTPUT_NODE && node->object.sink_stream == sink)
3448 sink_node = node;
3449 break;
3453 if (!sink_node)
3454 return;
3456 if (!event)
3458 if (FAILED(hr = MFCreateMediaEvent(MESinkInvalidated, &GUID_NULL, S_OK, NULL, &event)))
3459 WARN("Failed to create event, hr %#lx.\n", hr);
3462 if (!event)
3463 return;
3465 IMFMediaEvent_SetUINT64(event, &MF_EVENT_OUTPUT_NODE, sink_node->node_id);
3466 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
3468 session_set_topo_status(session, S_OK, MF_TOPOSTATUS_ENDED);
3471 static BOOL session_nodes_is_mask_set(struct media_session *session, MF_TOPOLOGY_TYPE node_type, unsigned int flags)
3473 struct media_source *source;
3474 struct topo_node *node;
3476 if (node_type == MF_TOPOLOGY_MAX)
3478 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
3480 if ((source->flags & flags) != flags)
3481 return FALSE;
3484 else
3486 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
3488 if (node->type == node_type && (node->flags & flags) != flags)
3489 return FALSE;
3493 return TRUE;
3496 static void session_nodes_unset_mask(struct media_session *session, MF_TOPOLOGY_TYPE node_type, unsigned int flags)
3498 struct media_source *source;
3499 struct topo_node *node;
3501 if (node_type == MF_TOPOLOGY_MAX)
3503 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
3505 source->flags &= ~flags;
3508 else
3510 LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
3512 if (node->type == node_type)
3514 node->flags &= ~flags;
3520 static void session_raise_end_of_presentation(struct media_session *session)
3522 if (!(session_nodes_is_mask_set(session, MF_TOPOLOGY_SOURCESTREAM_NODE, TOPO_NODE_END_OF_STREAM)))
3523 return;
3525 if (!(session->presentation.flags & SESSION_FLAG_END_OF_PRESENTATION))
3527 if (session_nodes_is_mask_set(session, MF_TOPOLOGY_MAX, SOURCE_FLAG_END_OF_PRESENTATION))
3529 session->presentation.flags |= SESSION_FLAG_END_OF_PRESENTATION | SESSION_FLAG_PENDING_COMMAND;
3530 IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, NULL);
3535 static void session_handle_end_of_stream(struct media_session *session, IMFMediaStream *stream)
3537 struct topo_node *node;
3539 if (!(node = session_get_node_object(session, (IUnknown *)stream, MF_TOPOLOGY_SOURCESTREAM_NODE))
3540 || node->flags & TOPO_NODE_END_OF_STREAM)
3542 return;
3545 session_deliver_sample(session, stream, NULL);
3547 session_raise_end_of_presentation(session);
3550 static void session_handle_end_of_presentation(struct media_session *session, IMFMediaSource *object)
3552 struct media_source *source;
3554 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
3556 if (source->source == object)
3558 if (!(source->flags & SOURCE_FLAG_END_OF_PRESENTATION))
3560 source->flags |= SOURCE_FLAG_END_OF_PRESENTATION;
3561 session_raise_end_of_presentation(session);
3564 break;
3569 static void session_sink_stream_marker(struct media_session *session, IMFStreamSink *stream_sink)
3571 struct topo_node *node;
3573 if (!(node = session_get_node_object(session, (IUnknown *)stream_sink, MF_TOPOLOGY_OUTPUT_NODE))
3574 || node->flags & TOPO_NODE_END_OF_STREAM)
3576 return;
3579 node->flags |= TOPO_NODE_END_OF_STREAM;
3581 if (session->presentation.flags & SESSION_FLAG_END_OF_PRESENTATION &&
3582 session_nodes_is_mask_set(session, MF_TOPOLOGY_OUTPUT_NODE, TOPO_NODE_END_OF_STREAM))
3584 session_set_topo_status(session, S_OK, MF_TOPOSTATUS_ENDED);
3585 session_set_caps(session, session->caps & ~MFSESSIONCAP_PAUSE);
3586 session_stop(session);
3590 static void session_sink_stream_scrub_complete(struct media_session *session, IMFStreamSink *stream_sink)
3592 struct topo_node *node;
3594 if (!(node = session_get_node_object(session, (IUnknown *)stream_sink, MF_TOPOLOGY_OUTPUT_NODE))
3595 || node->flags & TOPO_NODE_SCRUB_SAMPLE_COMPLETE)
3597 return;
3600 node->flags |= TOPO_NODE_SCRUB_SAMPLE_COMPLETE;
3602 /* Scrubbing event is not limited to the started state transition, or even the started state.
3603 Events are processed and forwarded at any point after transition from initial idle state. */
3604 if (session->presentation.flags & SESSION_FLAG_SOURCES_SUBSCRIBED &&
3605 session_nodes_is_mask_set(session, MF_TOPOLOGY_OUTPUT_NODE, TOPO_NODE_SCRUB_SAMPLE_COMPLETE))
3607 IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionScrubSampleComplete, &GUID_NULL, S_OK, NULL);
3608 session_nodes_unset_mask(session, MF_TOPOLOGY_OUTPUT_NODE, TOPO_NODE_SCRUB_SAMPLE_COMPLETE);
3612 static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
3614 struct media_session *session = impl_from_events_callback_IMFAsyncCallback(iface);
3615 IMFMediaEventGenerator *event_source;
3616 IMFMediaEvent *event = NULL;
3617 MediaEventType event_type;
3618 IUnknown *object = NULL;
3619 IMFMediaSource *source;
3620 IMFMediaStream *stream;
3621 PROPVARIANT value;
3622 HRESULT hr;
3624 if (FAILED(hr = IMFAsyncResult_GetState(result, (IUnknown **)&event_source)))
3625 return hr;
3627 if (FAILED(hr = IMFMediaEventGenerator_EndGetEvent(event_source, result, &event)))
3629 WARN("Failed to get event from %p, hr %#lx.\n", event_source, hr);
3630 goto failed;
3633 if (FAILED(hr = IMFMediaEvent_GetType(event, &event_type)))
3635 WARN("Failed to get event type, hr %#lx.\n", hr);
3636 goto failed;
3639 value.vt = VT_EMPTY;
3640 if (FAILED(hr = IMFMediaEvent_GetValue(event, &value)))
3642 WARN("Failed to get event value, hr %#lx.\n", hr);
3643 goto failed;
3646 switch (event_type)
3648 case MESourceSeeked:
3649 case MEStreamSeeked:
3650 FIXME("Source/stream seeking, semi-stub!\n");
3651 /* fallthrough */
3652 case MESourceStarted:
3653 case MESourcePaused:
3654 case MESourceStopped:
3655 case MEStreamStarted:
3656 case MEStreamPaused:
3657 case MEStreamStopped:
3659 EnterCriticalSection(&session->cs);
3660 session_set_source_object_state(session, (IUnknown *)event_source, event_type);
3661 LeaveCriticalSection(&session->cs);
3663 break;
3665 case MESourceRateChanged:
3667 EnterCriticalSection(&session->cs);
3668 session_complete_rate_change(session);
3669 LeaveCriticalSection(&session->cs);
3671 break;
3673 case MEBufferingStarted:
3674 case MEBufferingStopped:
3676 EnterCriticalSection(&session->cs);
3677 if (session_get_media_source(session, (IMFMediaSource *)event_source))
3679 if (event_type == MEBufferingStarted)
3680 IMFPresentationClock_Pause(session->clock);
3681 else
3682 IMFPresentationClock_Start(session->clock, PRESENTATION_CURRENT_POSITION);
3684 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
3686 LeaveCriticalSection(&session->cs);
3687 break;
3689 case MEReconnectStart:
3690 case MEReconnectEnd:
3692 EnterCriticalSection(&session->cs);
3693 if (session_get_media_source(session, (IMFMediaSource *)event_source))
3694 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
3695 LeaveCriticalSection(&session->cs);
3696 break;
3698 case MEExtendedType:
3699 case MERendererEvent:
3700 case MEStreamSinkFormatChanged:
3702 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
3703 break;
3705 case MENewStream:
3706 stream = (IMFMediaStream *)value.punkVal;
3708 if (value.vt != VT_UNKNOWN || !stream)
3710 WARN("Unexpected event value.\n");
3711 break;
3714 if (FAILED(hr = IMFMediaStream_GetMediaSource(stream, &source)))
3715 break;
3717 EnterCriticalSection(&session->cs);
3718 if (SUCCEEDED(hr = session_add_media_stream(session, source, stream)))
3719 hr = IMFMediaStream_BeginGetEvent(stream, &session->events_callback, (IUnknown *)stream);
3720 LeaveCriticalSection(&session->cs);
3722 IMFMediaSource_Release(source);
3724 break;
3725 case MEStreamSinkStarted:
3726 case MEStreamSinkPaused:
3727 case MEStreamSinkStopped:
3728 case MEStreamSinkPrerolled:
3730 EnterCriticalSection(&session->cs);
3731 session_set_sink_stream_state(session, (IMFStreamSink *)event_source, event_type);
3732 LeaveCriticalSection(&session->cs);
3734 break;
3735 case MEStreamSinkMarker:
3737 EnterCriticalSection(&session->cs);
3738 session_sink_stream_marker(session, (IMFStreamSink *)event_source);
3739 LeaveCriticalSection(&session->cs);
3741 break;
3742 case MEStreamSinkRequestSample:
3744 EnterCriticalSection(&session->cs);
3745 session_request_sample(session, (IMFStreamSink *)event_source);
3746 LeaveCriticalSection(&session->cs);
3748 break;
3749 case MEStreamSinkScrubSampleComplete:
3751 EnterCriticalSection(&session->cs);
3752 session_sink_stream_scrub_complete(session, (IMFStreamSink *)event_source);
3753 LeaveCriticalSection(&session->cs);
3754 break;
3755 case MEMediaSample:
3757 EnterCriticalSection(&session->cs);
3758 session_deliver_sample(session, (IMFMediaStream *)event_source, &value);
3759 LeaveCriticalSection(&session->cs);
3761 break;
3762 case MEEndOfStream:
3764 EnterCriticalSection(&session->cs);
3765 session_handle_end_of_stream(session, (IMFMediaStream *)event_source);
3766 LeaveCriticalSection(&session->cs);
3768 break;
3770 case MEEndOfPresentation:
3772 EnterCriticalSection(&session->cs);
3773 session_handle_end_of_presentation(session, (IMFMediaSource *)event_source);
3774 LeaveCriticalSection(&session->cs);
3776 break;
3777 case MEAudioSessionGroupingParamChanged:
3778 case MEAudioSessionIconChanged:
3779 case MEAudioSessionNameChanged:
3780 case MEAudioSessionVolumeChanged:
3782 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
3784 break;
3785 case MEAudioSessionDeviceRemoved:
3786 case MEAudioSessionDisconnected:
3787 case MEAudioSessionExclusiveModeOverride:
3788 case MEAudioSessionFormatChanged:
3789 case MEAudioSessionServerShutdown:
3791 IMFMediaEventQueue_QueueEvent(session->event_queue, event);
3792 /* fallthrough */
3793 case MESinkInvalidated:
3795 EnterCriticalSection(&session->cs);
3796 session_sink_invalidated(session, event_type == MESinkInvalidated ? event : NULL,
3797 (IMFStreamSink *)event_source);
3798 LeaveCriticalSection(&session->cs);
3800 break;
3801 case MEQualityNotify:
3803 if (session->quality_manager)
3805 if (FAILED(IMFMediaEventGenerator_QueryInterface(event_source, &IID_IMFStreamSink, (void **)&object)))
3806 IMFMediaEventGenerator_QueryInterface(event_source, &IID_IMFTransform, (void **)&object);
3808 if (object)
3810 IMFQualityManager_NotifyQualityEvent(session->quality_manager, object, event);
3811 IUnknown_Release(object);
3815 break;
3816 default:
3820 PropVariantClear(&value);
3822 failed:
3823 if (event)
3824 IMFMediaEvent_Release(event);
3826 if (FAILED(hr = IMFMediaEventGenerator_BeginGetEvent(event_source, iface, (IUnknown *)event_source)))
3827 WARN("Failed to re-subscribe, hr %#lx.\n", hr);
3829 IMFMediaEventGenerator_Release(event_source);
3831 return hr;
3834 static const IMFAsyncCallbackVtbl session_events_callback_vtbl =
3836 session_events_callback_QueryInterface,
3837 session_events_callback_AddRef,
3838 session_events_callback_Release,
3839 session_events_callback_GetParameters,
3840 session_events_callback_Invoke,
3843 static HRESULT WINAPI session_sink_finalizer_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
3845 if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
3846 IsEqualIID(riid, &IID_IUnknown))
3848 *obj = iface;
3849 IMFAsyncCallback_AddRef(iface);
3850 return S_OK;
3853 WARN("Unsupported %s.\n", debugstr_guid(riid));
3854 *obj = NULL;
3855 return E_NOINTERFACE;
3858 static ULONG WINAPI session_sink_finalizer_callback_AddRef(IMFAsyncCallback *iface)
3860 struct media_session *session = impl_from_sink_finalizer_callback_IMFAsyncCallback(iface);
3861 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
3864 static ULONG WINAPI session_sink_finalizer_callback_Release(IMFAsyncCallback *iface)
3866 struct media_session *session = impl_from_sink_finalizer_callback_IMFAsyncCallback(iface);
3867 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
3870 static HRESULT WINAPI session_sink_finalizer_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
3872 return E_NOTIMPL;
3875 static HRESULT WINAPI session_sink_finalizer_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
3877 struct media_session *session = impl_from_sink_finalizer_callback_IMFAsyncCallback(iface);
3878 IMFFinalizableMediaSink *fin_sink = NULL;
3879 BOOL sinks_finalized = TRUE;
3880 struct media_sink *sink;
3881 IUnknown *state;
3882 HRESULT hr;
3884 if (FAILED(hr = IMFAsyncResult_GetState(result, &state)))
3885 return hr;
3887 EnterCriticalSection(&session->cs);
3889 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
3891 if (state == sink->object)
3893 if (FAILED(hr = IMFMediaSink_QueryInterface(sink->sink, &IID_IMFFinalizableMediaSink, (void **)&fin_sink)))
3894 WARN("Unexpected, missing IMFFinalizableMediaSink, hr %#lx.\n", hr);
3896 else
3898 sinks_finalized &= sink->finalized;
3899 if (!sinks_finalized)
3900 break;
3904 IUnknown_Release(state);
3906 if (fin_sink)
3908 /* Complete session transition, or close prematurely on error. */
3909 if (SUCCEEDED(hr = IMFFinalizableMediaSink_EndFinalize(fin_sink, result)))
3911 sink->finalized = TRUE;
3912 if (sinks_finalized)
3913 session_set_closed(session, hr);
3915 IMFFinalizableMediaSink_Release(fin_sink);
3918 LeaveCriticalSection(&session->cs);
3920 return S_OK;
3923 static const IMFAsyncCallbackVtbl session_sink_finalizer_callback_vtbl =
3925 session_sink_finalizer_callback_QueryInterface,
3926 session_sink_finalizer_callback_AddRef,
3927 session_sink_finalizer_callback_Release,
3928 session_sink_finalizer_callback_GetParameters,
3929 session_sink_finalizer_callback_Invoke,
3932 static HRESULT WINAPI session_rate_support_QueryInterface(IMFRateSupport *iface, REFIID riid, void **obj)
3934 struct media_session *session = impl_session_from_IMFRateSupport(iface);
3935 return IMFMediaSession_QueryInterface(&session->IMFMediaSession_iface, riid, obj);
3938 static ULONG WINAPI session_rate_support_AddRef(IMFRateSupport *iface)
3940 struct media_session *session = impl_session_from_IMFRateSupport(iface);
3941 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
3944 static ULONG WINAPI session_rate_support_Release(IMFRateSupport *iface)
3946 struct media_session *session = impl_session_from_IMFRateSupport(iface);
3947 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
3950 static HRESULT session_presentation_object_get_rate(IUnknown *object, MFRATE_DIRECTION direction,
3951 BOOL thin, BOOL fastest, float *result)
3953 IMFRateSupport *rate_support;
3954 float rate;
3955 HRESULT hr;
3957 /* For objects that don't support rate control, it's assumed that only forward direction is allowed, at 1.0f. */
3959 if (FAILED(hr = MFGetService(object, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, (void **)&rate_support)))
3961 if (direction == MFRATE_FORWARD)
3963 *result = 1.0f;
3964 return S_OK;
3966 else
3967 return MF_E_REVERSE_UNSUPPORTED;
3970 rate = 0.0f;
3971 if (fastest)
3973 if (SUCCEEDED(hr = IMFRateSupport_GetFastestRate(rate_support, direction, thin, &rate)))
3974 *result = min(fabsf(rate), *result);
3976 else
3978 if (SUCCEEDED(hr = IMFRateSupport_GetSlowestRate(rate_support, direction, thin, &rate)))
3979 *result = max(fabsf(rate), *result);
3982 IMFRateSupport_Release(rate_support);
3984 return hr;
3987 static HRESULT session_get_presentation_rate(struct media_session *session, MFRATE_DIRECTION direction,
3988 BOOL thin, BOOL fastest, float *result)
3990 struct media_source *source;
3991 struct media_sink *sink;
3992 HRESULT hr = E_POINTER;
3993 float rate;
3995 rate = fastest ? FLT_MAX : 0.0f;
3997 EnterCriticalSection(&session->cs);
3999 if (session->presentation.topo_status != MF_TOPOSTATUS_INVALID)
4001 LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry)
4003 if (FAILED(hr = session_presentation_object_get_rate(source->object, direction, thin, fastest, &rate)))
4004 break;
4007 if (SUCCEEDED(hr))
4009 LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
4011 if (FAILED(hr = session_presentation_object_get_rate(sink->object, direction, thin, fastest, &rate)))
4012 break;
4017 LeaveCriticalSection(&session->cs);
4019 if (SUCCEEDED(hr))
4020 *result = direction == MFRATE_FORWARD ? rate : -rate;
4022 return hr;
4025 static HRESULT WINAPI session_rate_support_GetSlowestRate(IMFRateSupport *iface, MFRATE_DIRECTION direction,
4026 BOOL thin, float *rate)
4028 struct media_session *session = impl_session_from_IMFRateSupport(iface);
4030 TRACE("%p, %d, %d, %p.\n", iface, direction, thin, rate);
4032 return session_get_presentation_rate(session, direction, thin, FALSE, rate);
4035 static HRESULT WINAPI session_rate_support_GetFastestRate(IMFRateSupport *iface, MFRATE_DIRECTION direction,
4036 BOOL thin, float *rate)
4038 struct media_session *session = impl_session_from_IMFRateSupport(iface);
4040 TRACE("%p, %d, %d, %p.\n", iface, direction, thin, rate);
4042 return session_get_presentation_rate(session, direction, thin, TRUE, rate);
4045 static HRESULT WINAPI session_rate_support_IsRateSupported(IMFRateSupport *iface, BOOL thin, float rate,
4046 float *nearest_supported_rate)
4048 struct media_session *session = impl_session_from_IMFRateSupport(iface);
4049 HRESULT hr;
4051 TRACE("%p, %d, %f, %p.\n", iface, thin, rate, nearest_supported_rate);
4053 EnterCriticalSection(&session->cs);
4054 hr = session_is_presentation_rate_supported(session, thin, rate, nearest_supported_rate);
4055 LeaveCriticalSection(&session->cs);
4057 return hr;
4060 static const IMFRateSupportVtbl session_rate_support_vtbl =
4062 session_rate_support_QueryInterface,
4063 session_rate_support_AddRef,
4064 session_rate_support_Release,
4065 session_rate_support_GetSlowestRate,
4066 session_rate_support_GetFastestRate,
4067 session_rate_support_IsRateSupported,
4070 static HRESULT WINAPI session_rate_control_QueryInterface(IMFRateControl *iface, REFIID riid, void **obj)
4072 struct media_session *session = impl_session_from_IMFRateControl(iface);
4073 return IMFMediaSession_QueryInterface(&session->IMFMediaSession_iface, riid, obj);
4076 static ULONG WINAPI session_rate_control_AddRef(IMFRateControl *iface)
4078 struct media_session *session = impl_session_from_IMFRateControl(iface);
4079 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
4082 static ULONG WINAPI session_rate_control_Release(IMFRateControl *iface)
4084 struct media_session *session = impl_session_from_IMFRateControl(iface);
4085 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
4088 static HRESULT WINAPI session_rate_control_SetRate(IMFRateControl *iface, BOOL thin, float rate)
4090 struct media_session *session = impl_session_from_IMFRateControl(iface);
4091 struct session_op *op;
4092 HRESULT hr;
4094 TRACE("%p, %d, %f.\n", iface, thin, rate);
4096 if (FAILED(hr = create_session_op(SESSION_CMD_SET_RATE, &op)))
4097 return hr;
4099 op->set_rate.thin = thin;
4100 op->set_rate.rate = rate;
4101 hr = session_submit_command(session, op);
4102 IUnknown_Release(&op->IUnknown_iface);
4103 return hr;
4106 static HRESULT WINAPI session_rate_control_GetRate(IMFRateControl *iface, BOOL *thin, float *rate)
4108 struct media_session *session = impl_session_from_IMFRateControl(iface);
4110 TRACE("%p, %p, %p.\n", iface, thin, rate);
4112 return IMFRateControl_GetRate(session->clock_rate_control, thin, rate);
4115 static const IMFRateControlVtbl session_rate_control_vtbl =
4117 session_rate_control_QueryInterface,
4118 session_rate_control_AddRef,
4119 session_rate_control_Release,
4120 session_rate_control_SetRate,
4121 session_rate_control_GetRate,
4124 static HRESULT WINAPI node_attribute_editor_QueryInterface(IMFTopologyNodeAttributeEditor *iface,
4125 REFIID riid, void **obj)
4127 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
4129 if (IsEqualIID(riid, &IID_IMFTopologyNodeAttributeEditor) ||
4130 IsEqualIID(riid, &IID_IUnknown))
4132 *obj = iface;
4133 IMFTopologyNodeAttributeEditor_AddRef(iface);
4134 return S_OK;
4137 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
4138 *obj = NULL;
4139 return E_NOINTERFACE;
4142 static ULONG WINAPI node_attribute_editor_AddRef(IMFTopologyNodeAttributeEditor *iface)
4144 struct media_session *session = impl_session_from_IMFTopologyNodeAttributeEditor(iface);
4145 return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
4148 static ULONG WINAPI node_attribute_editor_Release(IMFTopologyNodeAttributeEditor *iface)
4150 struct media_session *session = impl_session_from_IMFTopologyNodeAttributeEditor(iface);
4151 return IMFMediaSession_Release(&session->IMFMediaSession_iface);
4154 static HRESULT WINAPI node_attribute_editor_UpdateNodeAttributes(IMFTopologyNodeAttributeEditor *iface,
4155 TOPOID id, DWORD count, MFTOPONODE_ATTRIBUTE_UPDATE *updates)
4157 FIXME("%p, %s, %lu, %p.\n", iface, wine_dbgstr_longlong(id), count, updates);
4159 return E_NOTIMPL;
4162 static const IMFTopologyNodeAttributeEditorVtbl node_attribute_editor_vtbl =
4164 node_attribute_editor_QueryInterface,
4165 node_attribute_editor_AddRef,
4166 node_attribute_editor_Release,
4167 node_attribute_editor_UpdateNodeAttributes,
4170 /***********************************************************************
4171 * MFCreateMediaSession (mf.@)
4173 HRESULT WINAPI MFCreateMediaSession(IMFAttributes *config, IMFMediaSession **session)
4175 BOOL without_quality_manager = FALSE;
4176 struct media_session *object;
4177 HRESULT hr;
4179 TRACE("%p, %p.\n", config, session);
4181 if (!(object = calloc(1, sizeof(*object))))
4182 return E_OUTOFMEMORY;
4184 object->IMFMediaSession_iface.lpVtbl = &mfmediasessionvtbl;
4185 object->IMFGetService_iface.lpVtbl = &session_get_service_vtbl;
4186 object->IMFRateSupport_iface.lpVtbl = &session_rate_support_vtbl;
4187 object->IMFRateControl_iface.lpVtbl = &session_rate_control_vtbl;
4188 object->IMFTopologyNodeAttributeEditor_iface.lpVtbl = &node_attribute_editor_vtbl;
4189 object->commands_callback.lpVtbl = &session_commands_callback_vtbl;
4190 object->sa_ready_callback.lpVtbl = &session_sa_ready_callback_vtbl;
4191 object->events_callback.lpVtbl = &session_events_callback_vtbl;
4192 object->sink_finalizer_callback.lpVtbl = &session_sink_finalizer_callback_vtbl;
4193 object->refcount = 1;
4194 list_init(&object->topologies);
4195 list_init(&object->commands);
4196 list_init(&object->presentation.sources);
4197 list_init(&object->presentation.sinks);
4198 list_init(&object->presentation.nodes);
4199 InitializeCriticalSection(&object->cs);
4201 if (FAILED(hr = MFCreateTopology(&object->presentation.current_topology)))
4202 goto failed;
4204 if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
4205 goto failed;
4207 if (FAILED(hr = MFCreatePresentationClock(&object->clock)))
4208 goto failed;
4210 if (FAILED(hr = MFCreateSystemTimeSource(&object->system_time_source)))
4211 goto failed;
4213 if (FAILED(hr = IMFPresentationClock_QueryInterface(object->clock, &IID_IMFRateControl,
4214 (void **)&object->clock_rate_control)))
4216 goto failed;
4219 if (config)
4221 GUID clsid;
4223 if (SUCCEEDED(IMFAttributes_GetGUID(config, &MF_SESSION_TOPOLOADER, &clsid)))
4225 if (FAILED(hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTopoLoader,
4226 (void **)&object->topo_loader)))
4228 WARN("Failed to create custom topology loader, hr %#lx.\n", hr);
4232 if (SUCCEEDED(IMFAttributes_GetGUID(config, &MF_SESSION_QUALITY_MANAGER, &clsid)))
4234 if (!(without_quality_manager = IsEqualGUID(&clsid, &GUID_NULL)))
4236 if (FAILED(hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IMFQualityManager,
4237 (void **)&object->quality_manager)))
4239 WARN("Failed to create custom quality manager, hr %#lx.\n", hr);
4245 if (!object->topo_loader && FAILED(hr = MFCreateTopoLoader(&object->topo_loader)))
4246 goto failed;
4248 if (!object->quality_manager && !without_quality_manager &&
4249 FAILED(hr = MFCreateStandardQualityManager(&object->quality_manager)))
4251 goto failed;
4254 if (object->quality_manager && FAILED(hr = IMFQualityManager_NotifyPresentationClock(object->quality_manager,
4255 object->clock)))
4257 goto failed;
4260 *session = &object->IMFMediaSession_iface;
4262 return S_OK;
4264 failed:
4265 IMFMediaSession_Release(&object->IMFMediaSession_iface);
4266 return hr;