2 * Copyright 2020 Nikolay Sivov for CodeWeavers
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
24 #include "mfreadwrite.h"
26 #include "wine/mfinternal.h"
27 #include "mf_private.h"
29 #include "wine/debug.h"
30 #include "wine/list.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(mfplat
);
36 SINK_WRITER_STATE_INITIAL
= 0,
37 SINK_WRITER_STATE_WRITING
,
42 IUnknown IUnknown_iface
;
45 unsigned int marker_type
;
52 unsigned int marker_type
;
60 IMFStreamSink
*stream_sink
;
61 IMFTransform
*encoder
;
62 MF_SINK_WRITER_STATISTICS stats
;
68 IMFSinkWriter IMFSinkWriter_iface
;
69 IMFAsyncCallback events_callback
;
80 IMFPresentationClock
*clock
;
82 enum writer_state state
;
84 MF_SINK_WRITER_STATISTICS stats
;
86 IMFSinkWriterCallback
*callback
;
91 static struct sink_writer
*impl_from_IMFSinkWriter(IMFSinkWriter
*iface
)
93 return CONTAINING_RECORD(iface
, struct sink_writer
, IMFSinkWriter_iface
);
96 static struct sink_writer
*impl_from_events_callback_IMFAsyncCallback(IMFAsyncCallback
*iface
)
98 return CONTAINING_RECORD(iface
, struct sink_writer
, events_callback
);
101 static struct marker_context
*impl_from_marker_context_IUnknown(IUnknown
*iface
)
103 return CONTAINING_RECORD(iface
, struct marker_context
, IUnknown_iface
);
106 static HRESULT WINAPI
marker_context_QueryInterface(IUnknown
*iface
, REFIID riid
, void **out
)
108 if (IsEqualIID(riid
, &IID_IUnknown
))
111 IUnknown_AddRef(iface
);
116 return E_NOINTERFACE
;
119 static ULONG WINAPI
marker_context_AddRef(IUnknown
*iface
)
121 struct marker_context
*context
= impl_from_marker_context_IUnknown(iface
);
122 return InterlockedIncrement(&context
->refcount
);
125 static ULONG WINAPI
marker_context_Release(IUnknown
*iface
)
127 struct marker_context
*context
= impl_from_marker_context_IUnknown(iface
);
128 LONG refcount
= InterlockedDecrement(&context
->refcount
);
136 static const IUnknownVtbl marker_context_vtbl
=
138 marker_context_QueryInterface
,
139 marker_context_AddRef
,
140 marker_context_Release
,
143 static struct marker_context
*unsafe_impl_from_marker_context_IUnknown(IUnknown
*iface
)
147 assert(iface
->lpVtbl
== &marker_context_vtbl
);
148 return CONTAINING_RECORD(iface
, struct marker_context
, IUnknown_iface
);
151 static HRESULT
create_marker_context(unsigned int marker_type
, void *user_context
,
154 struct marker_context
*object
;
156 if (!(object
= calloc(1, sizeof(*object
))))
157 return E_OUTOFMEMORY
;
159 object
->IUnknown_iface
.lpVtbl
= &marker_context_vtbl
;
160 object
->refcount
= 1;
161 object
->marker_type
= marker_type
;
162 object
->user_context
= user_context
;
164 *ret
= &object
->IUnknown_iface
;
169 static void sink_writer_release_pending_item(struct pending_item
*item
)
171 list_remove(&item
->entry
);
173 IMFSample_Release(item
->sample
);
177 static HRESULT WINAPI
sink_writer_QueryInterface(IMFSinkWriter
*iface
, REFIID riid
, void **out
)
179 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), out
);
181 if (IsEqualIID(riid
, &IID_IMFSinkWriter
) ||
182 IsEqualIID(riid
, &IID_IUnknown
))
185 IMFSinkWriter_AddRef(iface
);
189 WARN("Unsupported %s.\n", debugstr_guid(riid
));
191 return E_NOINTERFACE
;
194 static ULONG WINAPI
sink_writer_AddRef(IMFSinkWriter
*iface
)
196 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
197 ULONG refcount
= InterlockedIncrement(&writer
->refcount
);
199 TRACE("%p, %lu.\n", iface
, refcount
);
204 static void sink_writer_drop_pending_items(struct stream
*stream
)
206 struct pending_item
*item
, *next
;
208 LIST_FOR_EACH_ENTRY_SAFE(item
, next
, &stream
->queue
, struct pending_item
, entry
)
210 sink_writer_release_pending_item(item
);
214 static ULONG WINAPI
sink_writer_Release(IMFSinkWriter
*iface
)
216 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
217 ULONG refcount
= InterlockedDecrement(&writer
->refcount
);
220 TRACE("%p, %lu.\n", iface
, refcount
);
225 IMFPresentationClock_Release(writer
->clock
);
227 IMFMediaSink_Release(writer
->sink
);
228 if (writer
->callback
)
229 IMFSinkWriterCallback_Release(writer
->callback
);
230 for (i
= 0; i
< writer
->streams
.count
; ++i
)
232 struct stream
*stream
= &writer
->streams
.items
[i
];
234 if (stream
->stream_sink
)
235 IMFStreamSink_Release(stream
->stream_sink
);
237 IMFTransform_Release(stream
->encoder
);
238 sink_writer_drop_pending_items(stream
);
240 DeleteCriticalSection(&writer
->cs
);
247 static HRESULT
sink_writer_add_stream(struct sink_writer
*writer
, IMFStreamSink
*stream_sink
, DWORD
*index
)
249 struct stream
*stream
;
253 if (!mf_array_reserve((void **)&writer
->streams
.items
, &writer
->streams
.capacity
, writer
->streams
.count
+ 1,
254 sizeof(*writer
->streams
.items
)))
256 return E_OUTOFMEMORY
;
259 if (FAILED(hr
= IMFStreamSink_GetIdentifier(stream_sink
, &id
))) return hr
;
261 *index
= writer
->streams
.count
++;
263 stream
= &writer
->streams
.items
[*index
];
264 memset(stream
, 0, sizeof(*stream
));
265 stream
->stream_sink
= stream_sink
;
266 IMFStreamSink_AddRef(stream_sink
);
267 stream
->stats
.cb
= sizeof(stream
->stats
);
268 list_init(&stream
->queue
);
270 writer
->streams
.next_id
= max(writer
->streams
.next_id
, id
);
275 static HRESULT WINAPI
sink_writer_AddStream(IMFSinkWriter
*iface
, IMFMediaType
*media_type
, DWORD
*index
)
277 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
278 HRESULT hr
= MF_E_INVALIDREQUEST
;
279 IMFStreamSink
*stream_sink
;
282 TRACE("%p, %p, %p.\n", iface
, media_type
, index
);
290 EnterCriticalSection(&writer
->cs
);
292 if (writer
->state
== SINK_WRITER_STATE_INITIAL
)
294 id
= writer
->streams
.next_id
+ 1;
295 if (SUCCEEDED(hr
= IMFMediaSink_AddStreamSink(writer
->sink
, id
, media_type
, &stream_sink
)))
297 if (FAILED(hr
= sink_writer_add_stream(writer
, stream_sink
, index
)))
298 IMFMediaSink_RemoveStreamSink(writer
->sink
, id
);
302 LeaveCriticalSection(&writer
->cs
);
307 static HRESULT WINAPI
sink_writer_SetInputMediaType(IMFSinkWriter
*iface
, DWORD index
, IMFMediaType
*type
,
308 IMFAttributes
*parameters
)
310 FIXME("%p, %lu, %p, %p.\n", iface
, index
, type
, parameters
);
315 static HRESULT
sink_writer_set_presentation_clock(struct sink_writer
*writer
)
317 IMFPresentationTimeSource
*time_source
= NULL
;
320 if (FAILED(hr
= MFCreatePresentationClock(&writer
->clock
))) return hr
;
322 if (FAILED(IMFMediaSink_QueryInterface(writer
->sink
, &IID_IMFPresentationTimeSource
, (void **)&time_source
)))
323 hr
= MFCreateSystemTimeSource(&time_source
);
325 if (SUCCEEDED(hr
= IMFPresentationClock_SetTimeSource(writer
->clock
, time_source
)))
326 hr
= IMFMediaSink_SetPresentationClock(writer
->sink
, writer
->clock
);
329 IMFPresentationTimeSource_Release(time_source
);
334 static HRESULT WINAPI
sink_writer_BeginWriting(IMFSinkWriter
*iface
)
336 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
340 TRACE("%p.\n", iface
);
342 EnterCriticalSection(&writer
->cs
);
344 if (!writer
->streams
.count
)
345 hr
= MF_E_INVALIDREQUEST
;
346 else if (writer
->state
!= SINK_WRITER_STATE_INITIAL
)
347 hr
= MF_E_INVALIDREQUEST
;
348 else if (SUCCEEDED(hr
= sink_writer_set_presentation_clock(writer
)))
350 for (i
= 0; i
< writer
->streams
.count
; ++i
)
352 struct stream
*stream
= &writer
->streams
.items
[i
];
354 if (FAILED(hr
= IMFStreamSink_BeginGetEvent(stream
->stream_sink
, &writer
->events_callback
,
355 (IUnknown
*)stream
->stream_sink
)))
357 WARN("Failed to subscribe to events for steam %u, hr %#lx.\n", i
, hr
);
361 IMFTransform_ProcessMessage(stream
->encoder
, MFT_MESSAGE_NOTIFY_BEGIN_STREAMING
, 0);
365 hr
= IMFPresentationClock_Start(writer
->clock
, 0);
367 writer
->state
= SINK_WRITER_STATE_WRITING
;
370 LeaveCriticalSection(&writer
->cs
);
375 static struct stream
* sink_writer_get_stream(const struct sink_writer
*writer
, DWORD index
)
377 if (index
>= writer
->streams
.count
) return NULL
;
378 return &writer
->streams
.items
[index
];
381 static HRESULT
sink_writer_get_buffer_length(IMFSample
*sample
, LONGLONG
*timestamp
, DWORD
*length
)
383 IMFMediaBuffer
*buffer
;
389 if (FAILED(hr
= IMFSample_ConvertToContiguousBuffer(sample
, &buffer
))) return hr
;
390 hr
= IMFMediaBuffer_GetCurrentLength(buffer
, length
);
391 IMFMediaBuffer_Release(buffer
);
392 if (FAILED(hr
)) return hr
;
393 if (!*length
) return E_INVALIDARG
;
394 IMFSample_GetSampleTime(sample
, timestamp
);
399 static HRESULT
sink_writer_place_marker(struct sink_writer
*writer
, struct stream
*stream
, unsigned int marker_type
,
400 LONGLONG timestamp
, void *user_context
)
402 PROPVARIANT value
, context
;
403 IUnknown
*context_obj
;
406 if (FAILED(hr
= create_marker_context(marker_type
, user_context
, &context_obj
))) return hr
;
408 context
.vt
= VT_UNKNOWN
;
409 context
.punkVal
= context_obj
;
411 value
.hVal
.QuadPart
= timestamp
;
412 hr
= IMFStreamSink_PlaceMarker(stream
->stream_sink
, marker_type
, marker_type
== MFSTREAMSINK_MARKER_TICK
? &value
: NULL
,
414 IUnknown_Release(context_obj
);
418 static HRESULT
sink_writer_queue_marker(struct sink_writer
*writer
, struct stream
*stream
, unsigned int marker_type
,
419 LONGLONG timestamp
, void *user_context
)
421 struct pending_item
*item
;
423 if (list_empty(&stream
->queue
))
424 return sink_writer_place_marker(writer
, stream
, marker_type
, timestamp
, user_context
);
426 if (!(item
= calloc(1, sizeof(*item
))))
427 return E_OUTOFMEMORY
;
429 item
->marker_type
= marker_type
;
430 item
->timestamp
= timestamp
;
431 list_add_tail(&stream
->queue
, &item
->entry
);
436 static HRESULT
sink_writer_send_stream_tick(struct sink_writer
*writer
, unsigned int index
, LONGLONG timestamp
)
438 struct stream
*stream
;
440 if (!(stream
= sink_writer_get_stream(writer
, index
))) return MF_E_INVALIDSTREAMNUMBER
;
442 writer
->stats
.llLastStreamTickReceived
= timestamp
;
443 writer
->stats
.qwNumStreamTicksReceived
++;
444 stream
->stats
.llLastStreamTickReceived
= timestamp
;
445 stream
->stats
.qwNumStreamTicksReceived
++;
447 return sink_writer_queue_marker(writer
, stream
, MFSTREAMSINK_MARKER_TICK
, timestamp
, NULL
);
450 static HRESULT
sink_writer_notify_end_of_segment(struct sink_writer
*writer
, unsigned int index
)
452 struct stream
*stream
;
454 if (!(stream
= sink_writer_get_stream(writer
, index
))) return MF_E_INVALIDSTREAMNUMBER
;
456 return sink_writer_queue_marker(writer
, stream
, MFSTREAMSINK_MARKER_ENDOFSEGMENT
, 0, NULL
);
459 static HRESULT
sink_writer_flush(struct sink_writer
*writer
, unsigned int index
)
461 struct stream
*stream
;
463 if (!(stream
= sink_writer_get_stream(writer
, index
))) return MF_E_INVALIDSTREAMNUMBER
;
465 sink_writer_drop_pending_items(stream
);
467 IMFStreamSink_Flush(stream
->stream_sink
);
470 IMFTransform_ProcessMessage(stream
->encoder
, MFT_MESSAGE_COMMAND_FLUSH
, 0);
472 return sink_writer_place_marker(writer
, stream
, MFSTREAMSINK_MARKER_ENDOFSEGMENT
, 0, NULL
);
475 static HRESULT
sink_writer_process_sample(struct sink_writer
*writer
, struct stream
*stream
)
477 struct pending_item
*item
, *next
;
482 if (list_empty(&stream
->queue
)) return S_OK
;
483 item
= LIST_ENTRY(list_head(&stream
->queue
), struct pending_item
, entry
);
484 if (!item
->sample
) return S_OK
;
486 IMFSample_AddRef((sample
= item
->sample
));
487 sink_writer_release_pending_item(item
);
489 writer
->stats
.dwNumOutstandingSinkSampleRequests
--;
490 stream
->stats
.dwNumOutstandingSinkSampleRequests
--;
492 if (FAILED(hr
= IMFSample_GetSampleTime(sample
, ×tamp
)))
494 IMFSample_Release(sample
);
499 writer
->stats
.llLastTimestampProcessed
= timestamp
;
500 stream
->stats
.llLastTimestampProcessed
= timestamp
;
502 hr
= IMFStreamSink_ProcessSample(stream
->stream_sink
, sample
);
503 IMFSample_Release(sample
);
504 if (FAILED(hr
)) return hr
;
506 LIST_FOR_EACH_ENTRY_SAFE(item
, next
, &stream
->queue
, struct pending_item
, entry
)
508 if (item
->sample
) break;
509 sink_writer_place_marker(writer
, stream
, item
->marker_type
, item
->timestamp
, item
->user_context
);
510 sink_writer_release_pending_item(item
);
516 static HRESULT
sink_writer_encode_sample(struct sink_writer
*writer
, struct stream
*stream
, IMFSample
*sample
)
518 struct pending_item
*item
;
520 /* FIXME: call the encoder, queue its output */
522 if (!(item
= calloc(1, sizeof(*item
))))
523 return E_OUTOFMEMORY
;
525 item
->sample
= sample
;
526 IMFSample_AddRef(item
->sample
);
527 list_add_tail(&stream
->queue
, &item
->entry
);
532 static HRESULT
sink_writer_write_sample(struct sink_writer
*writer
, struct stream
*stream
, IMFSample
*sample
)
538 if (FAILED(hr
= sink_writer_get_buffer_length(sample
, ×tamp
, &length
))) return hr
;
540 stream
->stats
.llLastTimestampReceived
= timestamp
;
541 stream
->stats
.qwNumSamplesReceived
++;
542 stream
->stats
.dwByteCountQueued
+= length
;
544 writer
->stats
.llLastTimestampReceived
= timestamp
;
545 writer
->stats
.qwNumSamplesReceived
++;
546 writer
->stats
.dwByteCountQueued
+= length
;
548 if (FAILED(hr
= sink_writer_encode_sample(writer
, stream
, sample
))) return hr
;
550 if (stream
->stats
.dwNumOutstandingSinkSampleRequests
)
551 hr
= sink_writer_process_sample(writer
, stream
);
556 static HRESULT WINAPI
sink_writer_WriteSample(IMFSinkWriter
*iface
, DWORD index
, IMFSample
*sample
)
558 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
559 struct stream
*stream
;
564 TRACE("%p, %lu, %p.\n", iface
, index
, sample
);
569 EnterCriticalSection(&writer
->cs
);
571 if (writer
->state
!= SINK_WRITER_STATE_WRITING
)
572 hr
= MF_E_INVALIDREQUEST
;
573 else if (!(stream
= sink_writer_get_stream(writer
, index
)))
575 hr
= MF_E_INVALIDSTREAMNUMBER
;
577 else if (SUCCEEDED(hr
= sink_writer_get_buffer_length(sample
, ×tamp
, &length
)))
579 stream
->stats
.llLastTimestampReceived
= timestamp
;
580 stream
->stats
.qwNumSamplesReceived
++;
581 stream
->stats
.dwByteCountQueued
+= length
;
583 writer
->stats
.llLastTimestampReceived
= timestamp
;
584 writer
->stats
.qwNumSamplesReceived
++;
585 writer
->stats
.dwByteCountQueued
+= length
;
587 hr
= sink_writer_write_sample(writer
, stream
, sample
);
590 LeaveCriticalSection(&writer
->cs
);
595 static HRESULT WINAPI
sink_writer_SendStreamTick(IMFSinkWriter
*iface
, DWORD index
, LONGLONG timestamp
)
597 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
600 TRACE("%p, %lu, %s.\n", iface
, index
, wine_dbgstr_longlong(timestamp
));
602 EnterCriticalSection(&writer
->cs
);
604 if (writer
->state
!= SINK_WRITER_STATE_WRITING
)
605 hr
= MF_E_INVALIDREQUEST
;
607 hr
= sink_writer_send_stream_tick(writer
, index
, timestamp
);
609 LeaveCriticalSection(&writer
->cs
);
614 static HRESULT WINAPI
sink_writer_PlaceMarker(IMFSinkWriter
*iface
, DWORD index
, void *user_context
)
616 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
617 struct stream
*stream
;
620 TRACE("%p, %lu, %p.\n", iface
, index
, user_context
);
622 EnterCriticalSection(&writer
->cs
);
624 if (!writer
->callback
)
625 hr
= MF_E_INVALIDREQUEST
;
626 else if (writer
->state
!= SINK_WRITER_STATE_WRITING
)
627 hr
= MF_E_INVALIDREQUEST
;
628 else if (!(stream
= sink_writer_get_stream(writer
, index
)))
629 hr
= MF_E_INVALIDSTREAMNUMBER
;
631 hr
= sink_writer_queue_marker(writer
, stream
, MFSTREAMSINK_MARKER_DEFAULT
, 0, user_context
);
633 LeaveCriticalSection(&writer
->cs
);
638 static HRESULT WINAPI
sink_writer_NotifyEndOfSegment(IMFSinkWriter
*iface
, DWORD index
)
640 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
644 TRACE("%p, %lu.\n", iface
, index
);
646 EnterCriticalSection(&writer
->cs
);
648 if (writer
->state
!= SINK_WRITER_STATE_WRITING
)
649 hr
= MF_E_INVALIDREQUEST
;
650 else if (index
== MF_SINK_WRITER_ALL_STREAMS
)
652 for (i
= 0; i
< writer
->streams
.count
; ++i
)
654 if (FAILED(hr
= sink_writer_notify_end_of_segment(writer
, index
)))
656 WARN("Failed to place a marker for stream %u.\n", i
);
662 hr
= sink_writer_notify_end_of_segment(writer
, index
);
664 LeaveCriticalSection(&writer
->cs
);
669 static HRESULT WINAPI
sink_writer_Flush(IMFSinkWriter
*iface
, DWORD index
)
671 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
675 TRACE("%p, %lu.\n", iface
, index
);
677 EnterCriticalSection(&writer
->cs
);
679 if (writer
->state
!= SINK_WRITER_STATE_WRITING
)
680 hr
= MF_E_INVALIDREQUEST
;
681 else if (index
== MF_SINK_WRITER_ALL_STREAMS
)
683 for (i
= 0; i
< writer
->streams
.count
; ++i
)
685 if (FAILED(hr
= sink_writer_flush(writer
, i
)))
687 WARN("Failed to flush stream %u.\n", i
);
693 hr
= sink_writer_flush(writer
, index
);
695 LeaveCriticalSection(&writer
->cs
);
700 static HRESULT WINAPI
sink_writer_Finalize(IMFSinkWriter
*iface
)
702 FIXME("%p.\n", iface
);
707 static HRESULT
sink_writer_get_service(void *object
, REFGUID service
, REFIID riid
, void **ret
)
709 IUnknown
*iface
= object
;
713 if (!iface
) return MF_E_UNSUPPORTED_SERVICE
;
715 if (IsEqualGUID(service
, &GUID_NULL
))
716 return IUnknown_QueryInterface(iface
, riid
, ret
);
718 if (FAILED(hr
= IUnknown_QueryInterface(iface
, &IID_IMFGetService
, (void **)&gs
)))
721 hr
= IMFGetService_GetService(gs
, service
, riid
, ret
);
722 IMFGetService_Release(gs
);
726 static HRESULT WINAPI
sink_writer_GetServiceForStream(IMFSinkWriter
*iface
, DWORD index
, REFGUID service
,
727 REFIID riid
, void **object
)
729 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
730 HRESULT hr
= E_UNEXPECTED
;
731 struct stream
*stream
;
733 TRACE("%p, %lu, %s, %s, %p.\n", iface
, index
, debugstr_guid(service
), debugstr_guid(riid
), object
);
735 EnterCriticalSection(&writer
->cs
);
737 if (index
== MF_SINK_WRITER_MEDIASINK
)
738 hr
= sink_writer_get_service(writer
->sink
, service
, riid
, object
);
739 else if ((stream
= sink_writer_get_stream(writer
, index
)))
742 hr
= sink_writer_get_service(stream
->encoder
, service
, riid
, object
);
744 hr
= sink_writer_get_service(stream
->stream_sink
, service
, riid
, object
);
747 hr
= MF_E_INVALIDSTREAMNUMBER
;
749 LeaveCriticalSection(&writer
->cs
);
754 static HRESULT WINAPI
sink_writer_GetStatistics(IMFSinkWriter
*iface
, DWORD index
, MF_SINK_WRITER_STATISTICS
*stats
)
756 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
757 struct stream
*stream
;
760 TRACE("%p, %lu, %p.\n", iface
, index
, stats
);
765 if (stats
->cb
!= sizeof(*stats
))
768 EnterCriticalSection(&writer
->cs
);
770 if (FAILED(writer
->status
))
772 else if (index
== MF_SINK_WRITER_ALL_STREAMS
)
773 *stats
= writer
->stats
;
774 else if ((stream
= sink_writer_get_stream(writer
, index
)))
775 *stats
= stream
->stats
;
777 hr
= MF_E_INVALIDSTREAMNUMBER
;
779 LeaveCriticalSection(&writer
->cs
);
784 static const IMFSinkWriterVtbl sink_writer_vtbl
=
786 sink_writer_QueryInterface
,
789 sink_writer_AddStream
,
790 sink_writer_SetInputMediaType
,
791 sink_writer_BeginWriting
,
792 sink_writer_WriteSample
,
793 sink_writer_SendStreamTick
,
794 sink_writer_PlaceMarker
,
795 sink_writer_NotifyEndOfSegment
,
797 sink_writer_Finalize
,
798 sink_writer_GetServiceForStream
,
799 sink_writer_GetStatistics
,
802 static HRESULT WINAPI
sink_writer_callback_QueryInterface(IMFAsyncCallback
*iface
,
803 REFIID riid
, void **out
)
805 if (IsEqualIID(riid
, &IID_IMFAsyncCallback
) ||
806 IsEqualIID(riid
, &IID_IUnknown
))
809 IMFAsyncCallback_AddRef(iface
);
813 WARN("Unsupported %s.\n", debugstr_guid(riid
));
815 return E_NOINTERFACE
;
818 static ULONG WINAPI
sink_writer_events_callback_AddRef(IMFAsyncCallback
*iface
)
820 struct sink_writer
*writer
= impl_from_events_callback_IMFAsyncCallback(iface
);
821 return IMFSinkWriter_AddRef(&writer
->IMFSinkWriter_iface
);
824 static ULONG WINAPI
sink_writer_events_callback_Release(IMFAsyncCallback
*iface
)
826 struct sink_writer
*writer
= impl_from_events_callback_IMFAsyncCallback(iface
);
827 return IMFSinkWriter_Release(&writer
->IMFSinkWriter_iface
);
830 static HRESULT WINAPI
sink_writer_callback_GetParameters(IMFAsyncCallback
*iface
,
831 DWORD
*flags
, DWORD
*queue
)
836 static struct stream
*sink_writer_get_stream_for_stream_sink(struct sink_writer
*writer
,
837 IMFStreamSink
*stream_sink
, DWORD
*index
)
841 for (i
= 0; i
< writer
->streams
.count
; ++i
)
843 if (writer
->streams
.items
[i
].stream_sink
== stream_sink
)
846 return &writer
->streams
.items
[i
];
853 static HRESULT WINAPI
sink_writer_events_callback_Invoke(IMFAsyncCallback
*iface
, IMFAsyncResult
*result
)
855 struct sink_writer
*writer
= impl_from_events_callback_IMFAsyncCallback(iface
);
856 struct marker_context
*context
;
857 IMFStreamSink
*stream_sink
;
858 MediaEventType event_type
;
859 struct stream
*stream
;
860 IMFMediaEvent
*event
;
866 TRACE("%p, %p.\n", iface
, result
);
868 stream_sink
= (IMFStreamSink
*)IMFAsyncResult_GetStateNoAddRef(result
);
870 if (FAILED(hr
= IMFStreamSink_EndGetEvent(stream_sink
, result
, &event
)))
873 IMFMediaEvent_GetType(event
, &event_type
);
874 IMFMediaEvent_GetStatus(event
, &status
);
876 TRACE("Got event %lu.\n", event_type
);
878 PropVariantInit(&value
);
880 EnterCriticalSection(&writer
->cs
);
882 if (writer
->status
== S_OK
&& FAILED(status
))
883 writer
->status
= status
;
885 if (writer
->status
== S_OK
&& (stream
= sink_writer_get_stream_for_stream_sink(writer
, stream_sink
, &index
)))
889 case MEStreamSinkRequestSample
:
891 timestamp
= MFGetSystemTime();
893 writer
->stats
.llLastSinkSampleRequest
= timestamp
;
894 writer
->stats
.dwNumOutstandingSinkSampleRequests
++;
895 stream
->stats
.llLastSinkSampleRequest
= timestamp
;
896 stream
->stats
.dwNumOutstandingSinkSampleRequests
++;
898 sink_writer_process_sample(writer
, stream
);
902 case MEStreamSinkMarker
:
903 if (FAILED(hr
= IMFMediaEvent_GetValue(event
, &value
))) break;
904 if (value
.vt
!= VT_UNKNOWN
|| !(context
= unsafe_impl_from_marker_context_IUnknown(value
.punkVal
))) break;
906 /* This relies on the fact that default marker type is only used for PlaceMarker(). */
907 if (context
->marker_type
== MFSTREAMSINK_MARKER_DEFAULT
)
908 IMFSinkWriterCallback_OnMarker(writer
->callback
, index
, context
->user_context
);
917 LeaveCriticalSection(&writer
->cs
);
919 PropVariantClear(&value
);
921 IMFMediaEvent_Release(event
);
923 IMFStreamSink_BeginGetEvent(stream_sink
, iface
, (IUnknown
*)stream_sink
);
928 static const IMFAsyncCallbackVtbl sink_writer_events_callback_vtbl
=
930 sink_writer_callback_QueryInterface
,
931 sink_writer_events_callback_AddRef
,
932 sink_writer_events_callback_Release
,
933 sink_writer_callback_GetParameters
,
934 sink_writer_events_callback_Invoke
,
937 static HRESULT
sink_writer_initialize_existing_streams(struct sink_writer
*writer
, IMFMediaSink
*sink
)
939 IMFStreamSink
*stream_sink
;
940 DWORD count
= 0, i
, index
;
943 if (FAILED(hr
= IMFMediaSink_GetStreamSinkCount(sink
, &count
))) return hr
;
944 if (!count
) return S_OK
;
946 for (i
= 0; i
< count
; ++i
)
948 if (FAILED(hr
= IMFMediaSink_GetStreamSinkByIndex(sink
, i
, &stream_sink
))) break;
949 hr
= sink_writer_add_stream(writer
, stream_sink
, &index
);
950 IMFStreamSink_Release(stream_sink
);
951 if (FAILED(hr
)) break;
957 HRESULT
create_sink_writer_from_sink(IMFMediaSink
*sink
, IMFAttributes
*attributes
,
958 REFIID riid
, void **out
)
960 struct sink_writer
*object
;
968 if (!(object
= calloc(1, sizeof(*object
))))
969 return E_OUTOFMEMORY
;
971 object
->IMFSinkWriter_iface
.lpVtbl
= &sink_writer_vtbl
;
972 object
->events_callback
.lpVtbl
= &sink_writer_events_callback_vtbl
;
973 object
->refcount
= 1;
975 IMFMediaSink_AddRef(sink
);
976 object
->stats
.cb
= sizeof(object
->stats
);
977 InitializeCriticalSection(&object
->cs
);
981 IMFAttributes_GetUnknown(attributes
, &MF_SINK_WRITER_ASYNC_CALLBACK
,
982 &IID_IMFSinkWriterCallback
, (void **)&object
->callback
);
985 if (FAILED(hr
= sink_writer_initialize_existing_streams(object
, sink
)))
987 IMFSinkWriter_Release(&object
->IMFSinkWriter_iface
);
991 hr
= IMFSinkWriter_QueryInterface(&object
->IMFSinkWriter_iface
, riid
, out
);
992 IMFSinkWriter_Release(&object
->IMFSinkWriter_iface
);
996 /***********************************************************************
997 * MFCreateSinkWriterFromMediaSink (mfreadwrite.@)
999 HRESULT WINAPI
MFCreateSinkWriterFromMediaSink(IMFMediaSink
*sink
, IMFAttributes
*attributes
, IMFSinkWriter
**writer
)
1001 TRACE("%p, %p, %p.\n", sink
, attributes
, writer
);
1004 return E_INVALIDARG
;
1006 return create_sink_writer_from_sink(sink
, attributes
, &IID_IMFSinkWriter
, (void **)writer
);
1009 static HRESULT
sink_writer_get_sink_factory_class(const WCHAR
*url
, IMFAttributes
*attributes
, CLSID
*clsid
)
1011 static const struct extension_map
1017 { L
".mp4", &MFTranscodeContainerType_MPEG4
},
1018 { L
".mp3", &MFTranscodeContainerType_MP3
},
1019 { L
".wav", &MFTranscodeContainerType_WAVE
},
1020 { L
".avi", &MFTranscodeContainerType_AVI
},
1024 const GUID
*container
;
1028 { &MFTranscodeContainerType_MPEG4
, &CLSID_MFMPEG4SinkClassFactory
},
1029 { &MFTranscodeContainerType_MP3
, &CLSID_MFMP3SinkClassFactory
},
1030 { &MFTranscodeContainerType_WAVE
, &CLSID_MFWAVESinkClassFactory
},
1031 { &MFTranscodeContainerType_AVI
, &CLSID_MFAVISinkClassFactory
},
1033 const WCHAR
*extension
;
1039 if (!attributes
|| FAILED(IMFAttributes_GetGUID(attributes
, &MF_TRANSCODE_CONTAINERTYPE
, &container
)))
1041 const struct extension_map
*map
= NULL
;
1043 if (FAILED(PathCchFindExtension(url
, PATHCCH_MAX_CCH
, &extension
))) return E_INVALIDARG
;
1044 if (!extension
|| !*extension
) return E_INVALIDARG
;
1046 for (i
= 0; i
< ARRAY_SIZE(ext_map
); ++i
)
1050 if (!wcsicmp(map
->ext
, extension
))
1056 WARN("Couldn't find container type for extension %s.\n", debugstr_w(extension
));
1057 return E_INVALIDARG
;
1060 container
= *map
->guid
;
1065 if (!attributes
) return E_INVALIDARG
;
1066 if (FAILED(IMFAttributes_GetGUID(attributes
, &MF_TRANSCODE_CONTAINERTYPE
, &container
))) return E_INVALIDARG
;
1069 for (i
= 0; i
< ARRAY_SIZE(class_map
); ++i
)
1071 if (IsEqualGUID(&container
, class_map
[i
].container
))
1073 *clsid
= *class_map
[i
].clsid
;
1078 WARN("Couldn't find factory class for container %s.\n", debugstr_guid(&container
));
1079 return E_INVALIDARG
;
1082 HRESULT
create_sink_writer_from_url(const WCHAR
*url
, IMFByteStream
*bytestream
, IMFAttributes
*attributes
,
1083 REFIID riid
, void **out
)
1085 IMFSinkClassFactory
*factory
;
1092 if (!url
&& !bytestream
)
1093 return E_INVALIDARG
;
1095 if (FAILED(hr
= sink_writer_get_sink_factory_class(url
, attributes
, &clsid
))) return hr
;
1097 if (FAILED(hr
= CoCreateInstance(&clsid
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMFSinkClassFactory
, (void **)&factory
)))
1099 WARN("Failed to create a sink factory, hr %#lx.\n", hr
);
1104 IMFByteStream_AddRef(bytestream
);
1105 else if (FAILED(hr
= MFCreateFile(MF_ACCESSMODE_WRITE
, MF_OPENMODE_DELETE_IF_EXIST
, 0, url
, &bytestream
)))
1107 WARN("Failed to create output file stream, hr %#lx.\n", hr
);
1108 IMFSinkClassFactory_Release(factory
);
1112 hr
= IMFSinkClassFactory_CreateMediaSink(factory
, bytestream
, NULL
, NULL
, &sink
);
1113 IMFSinkClassFactory_Release(factory
);
1114 IMFByteStream_Release(bytestream
);
1117 WARN("Failed to create a sink, hr %#lx.\n", hr
);
1121 hr
= create_sink_writer_from_sink(sink
, attributes
, riid
, out
);
1122 IMFMediaSink_Release(sink
);
1127 /***********************************************************************
1128 * MFCreateSinkWriterFromURL (mfreadwrite.@)
1130 HRESULT WINAPI
MFCreateSinkWriterFromURL(const WCHAR
*url
, IMFByteStream
*bytestream
, IMFAttributes
*attributes
,
1131 IMFSinkWriter
**writer
)
1133 TRACE("%s, %p, %p, %p.\n", debugstr_w(url
), bytestream
, attributes
, writer
);
1136 return E_INVALIDARG
;
1138 return create_sink_writer_from_url(url
, bytestream
, attributes
, &IID_IMFSinkWriter
, (void **)writer
);