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
22 #define NONAMELESSUNION
26 #include "mfreadwrite.h"
28 #include "wine/mfinternal.h"
29 #include "mf_private.h"
31 #include "wine/debug.h"
32 #include "wine/list.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(mfplat
);
38 SINK_WRITER_STATE_INITIAL
= 0,
39 SINK_WRITER_STATE_WRITING
,
44 IUnknown IUnknown_iface
;
47 unsigned int marker_type
;
54 unsigned int marker_type
;
62 IMFStreamSink
*stream_sink
;
63 IMFTransform
*encoder
;
64 MF_SINK_WRITER_STATISTICS stats
;
70 IMFSinkWriter IMFSinkWriter_iface
;
71 IMFAsyncCallback events_callback
;
82 IMFPresentationClock
*clock
;
84 enum writer_state state
;
86 MF_SINK_WRITER_STATISTICS stats
;
88 IMFSinkWriterCallback
*callback
;
93 static struct sink_writer
*impl_from_IMFSinkWriter(IMFSinkWriter
*iface
)
95 return CONTAINING_RECORD(iface
, struct sink_writer
, IMFSinkWriter_iface
);
98 static struct sink_writer
*impl_from_events_callback_IMFAsyncCallback(IMFAsyncCallback
*iface
)
100 return CONTAINING_RECORD(iface
, struct sink_writer
, events_callback
);
103 static struct marker_context
*impl_from_marker_context_IUnknown(IUnknown
*iface
)
105 return CONTAINING_RECORD(iface
, struct marker_context
, IUnknown_iface
);
108 static HRESULT WINAPI
marker_context_QueryInterface(IUnknown
*iface
, REFIID riid
, void **out
)
110 if (IsEqualIID(riid
, &IID_IUnknown
))
113 IUnknown_AddRef(iface
);
118 return E_NOINTERFACE
;
121 static ULONG WINAPI
marker_context_AddRef(IUnknown
*iface
)
123 struct marker_context
*context
= impl_from_marker_context_IUnknown(iface
);
124 return InterlockedIncrement(&context
->refcount
);
127 static ULONG WINAPI
marker_context_Release(IUnknown
*iface
)
129 struct marker_context
*context
= impl_from_marker_context_IUnknown(iface
);
130 LONG refcount
= InterlockedDecrement(&context
->refcount
);
138 static const IUnknownVtbl marker_context_vtbl
=
140 marker_context_QueryInterface
,
141 marker_context_AddRef
,
142 marker_context_Release
,
145 static struct marker_context
*unsafe_impl_from_marker_context_IUnknown(IUnknown
*iface
)
149 assert(iface
->lpVtbl
== &marker_context_vtbl
);
150 return CONTAINING_RECORD(iface
, struct marker_context
, IUnknown_iface
);
153 static HRESULT
create_marker_context(unsigned int marker_type
, void *user_context
,
156 struct marker_context
*object
;
158 if (!(object
= calloc(1, sizeof(*object
))))
159 return E_OUTOFMEMORY
;
161 object
->IUnknown_iface
.lpVtbl
= &marker_context_vtbl
;
162 object
->refcount
= 1;
163 object
->marker_type
= marker_type
;
164 object
->user_context
= user_context
;
166 *ret
= &object
->IUnknown_iface
;
171 static void sink_writer_release_pending_item(struct pending_item
*item
)
173 list_remove(&item
->entry
);
175 IMFSample_Release(item
->sample
);
179 static HRESULT WINAPI
sink_writer_QueryInterface(IMFSinkWriter
*iface
, REFIID riid
, void **out
)
181 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), out
);
183 if (IsEqualIID(riid
, &IID_IMFSinkWriter
) ||
184 IsEqualIID(riid
, &IID_IUnknown
))
187 IMFSinkWriter_AddRef(iface
);
191 WARN("Unsupported %s.\n", debugstr_guid(riid
));
193 return E_NOINTERFACE
;
196 static ULONG WINAPI
sink_writer_AddRef(IMFSinkWriter
*iface
)
198 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
199 ULONG refcount
= InterlockedIncrement(&writer
->refcount
);
201 TRACE("%p, %lu.\n", iface
, refcount
);
206 static void sink_writer_drop_pending_items(struct stream
*stream
)
208 struct pending_item
*item
, *next
;
210 LIST_FOR_EACH_ENTRY_SAFE(item
, next
, &stream
->queue
, struct pending_item
, entry
)
212 sink_writer_release_pending_item(item
);
216 static ULONG WINAPI
sink_writer_Release(IMFSinkWriter
*iface
)
218 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
219 ULONG refcount
= InterlockedDecrement(&writer
->refcount
);
222 TRACE("%p, %lu.\n", iface
, refcount
);
227 IMFPresentationClock_Release(writer
->clock
);
229 IMFMediaSink_Release(writer
->sink
);
230 if (writer
->callback
)
231 IMFSinkWriterCallback_Release(writer
->callback
);
232 for (i
= 0; i
< writer
->streams
.count
; ++i
)
234 struct stream
*stream
= &writer
->streams
.items
[i
];
236 if (stream
->stream_sink
)
237 IMFStreamSink_Release(stream
->stream_sink
);
239 IMFTransform_Release(stream
->encoder
);
240 sink_writer_drop_pending_items(stream
);
242 DeleteCriticalSection(&writer
->cs
);
249 static HRESULT
sink_writer_add_stream(struct sink_writer
*writer
, IMFStreamSink
*stream_sink
, DWORD
*index
)
251 struct stream
*stream
;
255 if (!mf_array_reserve((void **)&writer
->streams
.items
, &writer
->streams
.capacity
, writer
->streams
.count
+ 1,
256 sizeof(*writer
->streams
.items
)))
258 return E_OUTOFMEMORY
;
261 if (FAILED(hr
= IMFStreamSink_GetIdentifier(stream_sink
, &id
))) return hr
;
263 *index
= writer
->streams
.count
++;
265 stream
= &writer
->streams
.items
[*index
];
266 memset(stream
, 0, sizeof(*stream
));
267 stream
->stream_sink
= stream_sink
;
268 IMFStreamSink_AddRef(stream_sink
);
269 stream
->stats
.cb
= sizeof(stream
->stats
);
270 list_init(&stream
->queue
);
272 writer
->streams
.next_id
= max(writer
->streams
.next_id
, id
);
277 static HRESULT WINAPI
sink_writer_AddStream(IMFSinkWriter
*iface
, IMFMediaType
*media_type
, DWORD
*index
)
279 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
280 HRESULT hr
= MF_E_INVALIDREQUEST
;
281 IMFStreamSink
*stream_sink
;
284 TRACE("%p, %p, %p.\n", iface
, media_type
, index
);
292 EnterCriticalSection(&writer
->cs
);
294 if (writer
->state
== SINK_WRITER_STATE_INITIAL
)
296 id
= writer
->streams
.next_id
+ 1;
297 if (SUCCEEDED(hr
= IMFMediaSink_AddStreamSink(writer
->sink
, id
, media_type
, &stream_sink
)))
299 if (FAILED(hr
= sink_writer_add_stream(writer
, stream_sink
, index
)))
300 IMFMediaSink_RemoveStreamSink(writer
->sink
, id
);
304 LeaveCriticalSection(&writer
->cs
);
309 static HRESULT WINAPI
sink_writer_SetInputMediaType(IMFSinkWriter
*iface
, DWORD index
, IMFMediaType
*type
,
310 IMFAttributes
*parameters
)
312 FIXME("%p, %lu, %p, %p.\n", iface
, index
, type
, parameters
);
317 static HRESULT
sink_writer_set_presentation_clock(struct sink_writer
*writer
)
319 IMFPresentationTimeSource
*time_source
= NULL
;
322 if (FAILED(hr
= MFCreatePresentationClock(&writer
->clock
))) return hr
;
324 if (FAILED(IMFMediaSink_QueryInterface(writer
->sink
, &IID_IMFPresentationTimeSource
, (void **)&time_source
)))
325 hr
= MFCreateSystemTimeSource(&time_source
);
327 if (SUCCEEDED(hr
= IMFPresentationClock_SetTimeSource(writer
->clock
, time_source
)))
328 hr
= IMFMediaSink_SetPresentationClock(writer
->sink
, writer
->clock
);
331 IMFPresentationTimeSource_Release(time_source
);
336 static HRESULT WINAPI
sink_writer_BeginWriting(IMFSinkWriter
*iface
)
338 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
342 TRACE("%p.\n", iface
);
344 EnterCriticalSection(&writer
->cs
);
346 if (!writer
->streams
.count
)
347 hr
= MF_E_INVALIDREQUEST
;
348 else if (writer
->state
!= SINK_WRITER_STATE_INITIAL
)
349 hr
= MF_E_INVALIDREQUEST
;
350 else if (SUCCEEDED(hr
= sink_writer_set_presentation_clock(writer
)))
352 for (i
= 0; i
< writer
->streams
.count
; ++i
)
354 struct stream
*stream
= &writer
->streams
.items
[i
];
356 if (FAILED(hr
= IMFStreamSink_BeginGetEvent(stream
->stream_sink
, &writer
->events_callback
,
357 (IUnknown
*)stream
->stream_sink
)))
359 WARN("Failed to subscribe to events for steam %u, hr %#lx.\n", i
, hr
);
363 IMFTransform_ProcessMessage(stream
->encoder
, MFT_MESSAGE_NOTIFY_BEGIN_STREAMING
, 0);
367 hr
= IMFPresentationClock_Start(writer
->clock
, 0);
369 writer
->state
= SINK_WRITER_STATE_WRITING
;
372 LeaveCriticalSection(&writer
->cs
);
377 static struct stream
* sink_writer_get_stream(const struct sink_writer
*writer
, DWORD index
)
379 if (index
>= writer
->streams
.count
) return NULL
;
380 return &writer
->streams
.items
[index
];
383 static HRESULT
sink_writer_get_buffer_length(IMFSample
*sample
, LONGLONG
*timestamp
, DWORD
*length
)
385 IMFMediaBuffer
*buffer
;
391 if (FAILED(hr
= IMFSample_ConvertToContiguousBuffer(sample
, &buffer
))) return hr
;
392 hr
= IMFMediaBuffer_GetCurrentLength(buffer
, length
);
393 IMFMediaBuffer_Release(buffer
);
394 if (FAILED(hr
)) return hr
;
395 if (!*length
) return E_INVALIDARG
;
396 IMFSample_GetSampleTime(sample
, timestamp
);
401 static HRESULT
sink_writer_place_marker(struct sink_writer
*writer
, struct stream
*stream
, unsigned int marker_type
,
402 LONGLONG timestamp
, void *user_context
)
404 PROPVARIANT value
, context
;
405 IUnknown
*context_obj
;
408 if (FAILED(hr
= create_marker_context(marker_type
, user_context
, &context_obj
))) return hr
;
410 context
.vt
= VT_UNKNOWN
;
411 context
.punkVal
= context_obj
;
413 value
.hVal
.QuadPart
= timestamp
;
414 hr
= IMFStreamSink_PlaceMarker(stream
->stream_sink
, marker_type
, marker_type
== MFSTREAMSINK_MARKER_TICK
? &value
: NULL
,
416 IUnknown_Release(context_obj
);
420 static HRESULT
sink_writer_queue_marker(struct sink_writer
*writer
, struct stream
*stream
, unsigned int marker_type
,
421 LONGLONG timestamp
, void *user_context
)
423 struct pending_item
*item
;
425 if (list_empty(&stream
->queue
))
426 return sink_writer_place_marker(writer
, stream
, marker_type
, timestamp
, user_context
);
428 if (!(item
= calloc(1, sizeof(*item
))))
429 return E_OUTOFMEMORY
;
431 item
->marker_type
= marker_type
;
432 item
->timestamp
= timestamp
;
433 list_add_tail(&stream
->queue
, &item
->entry
);
438 static HRESULT
sink_writer_send_stream_tick(struct sink_writer
*writer
, unsigned int index
, LONGLONG timestamp
)
440 struct stream
*stream
;
442 if (!(stream
= sink_writer_get_stream(writer
, index
))) return MF_E_INVALIDSTREAMNUMBER
;
444 writer
->stats
.llLastStreamTickReceived
= timestamp
;
445 writer
->stats
.qwNumStreamTicksReceived
++;
446 stream
->stats
.llLastStreamTickReceived
= timestamp
;
447 stream
->stats
.qwNumStreamTicksReceived
++;
449 return sink_writer_queue_marker(writer
, stream
, MFSTREAMSINK_MARKER_TICK
, timestamp
, NULL
);
452 static HRESULT
sink_writer_notify_end_of_segment(struct sink_writer
*writer
, unsigned int index
)
454 struct stream
*stream
;
456 if (!(stream
= sink_writer_get_stream(writer
, index
))) return MF_E_INVALIDSTREAMNUMBER
;
458 return sink_writer_queue_marker(writer
, stream
, MFSTREAMSINK_MARKER_ENDOFSEGMENT
, 0, NULL
);
461 static HRESULT
sink_writer_flush(struct sink_writer
*writer
, unsigned int index
)
463 struct stream
*stream
;
465 if (!(stream
= sink_writer_get_stream(writer
, index
))) return MF_E_INVALIDSTREAMNUMBER
;
467 sink_writer_drop_pending_items(stream
);
469 IMFStreamSink_Flush(stream
->stream_sink
);
472 IMFTransform_ProcessMessage(stream
->encoder
, MFT_MESSAGE_COMMAND_FLUSH
, 0);
474 return sink_writer_place_marker(writer
, stream
, MFSTREAMSINK_MARKER_ENDOFSEGMENT
, 0, NULL
);
477 static HRESULT
sink_writer_process_sample(struct sink_writer
*writer
, struct stream
*stream
)
479 struct pending_item
*item
, *next
;
484 if (list_empty(&stream
->queue
)) return S_OK
;
485 item
= LIST_ENTRY(list_head(&stream
->queue
), struct pending_item
, entry
);
486 if (!item
->sample
) return S_OK
;
488 IMFSample_AddRef((sample
= item
->sample
));
489 sink_writer_release_pending_item(item
);
491 writer
->stats
.dwNumOutstandingSinkSampleRequests
--;
492 stream
->stats
.dwNumOutstandingSinkSampleRequests
--;
494 if (FAILED(hr
= IMFSample_GetSampleTime(sample
, ×tamp
)))
496 IMFSample_Release(sample
);
501 writer
->stats
.llLastTimestampProcessed
= timestamp
;
502 stream
->stats
.llLastTimestampProcessed
= timestamp
;
504 hr
= IMFStreamSink_ProcessSample(stream
->stream_sink
, sample
);
505 IMFSample_Release(sample
);
506 if (FAILED(hr
)) return hr
;
508 LIST_FOR_EACH_ENTRY_SAFE(item
, next
, &stream
->queue
, struct pending_item
, entry
)
510 if (item
->sample
) break;
511 sink_writer_place_marker(writer
, stream
, item
->marker_type
, item
->timestamp
, item
->user_context
);
512 sink_writer_release_pending_item(item
);
518 static HRESULT
sink_writer_encode_sample(struct sink_writer
*writer
, struct stream
*stream
, IMFSample
*sample
)
520 struct pending_item
*item
;
522 /* FIXME: call the encoder, queue its output */
524 if (!(item
= calloc(1, sizeof(*item
))))
525 return E_OUTOFMEMORY
;
527 item
->sample
= sample
;
528 IMFSample_AddRef(item
->sample
);
529 list_add_tail(&stream
->queue
, &item
->entry
);
534 static HRESULT
sink_writer_write_sample(struct sink_writer
*writer
, struct stream
*stream
, IMFSample
*sample
)
540 if (FAILED(hr
= sink_writer_get_buffer_length(sample
, ×tamp
, &length
))) return hr
;
542 stream
->stats
.llLastTimestampReceived
= timestamp
;
543 stream
->stats
.qwNumSamplesReceived
++;
544 stream
->stats
.dwByteCountQueued
+= length
;
546 writer
->stats
.llLastTimestampReceived
= timestamp
;
547 writer
->stats
.qwNumSamplesReceived
++;
548 writer
->stats
.dwByteCountQueued
+= length
;
550 if (FAILED(hr
= sink_writer_encode_sample(writer
, stream
, sample
))) return hr
;
552 if (stream
->stats
.dwNumOutstandingSinkSampleRequests
)
553 hr
= sink_writer_process_sample(writer
, stream
);
558 static HRESULT WINAPI
sink_writer_WriteSample(IMFSinkWriter
*iface
, DWORD index
, IMFSample
*sample
)
560 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
561 struct stream
*stream
;
566 TRACE("%p, %lu, %p.\n", iface
, index
, sample
);
571 EnterCriticalSection(&writer
->cs
);
573 if (writer
->state
!= SINK_WRITER_STATE_WRITING
)
574 hr
= MF_E_INVALIDREQUEST
;
575 else if (!(stream
= sink_writer_get_stream(writer
, index
)))
577 hr
= MF_E_INVALIDSTREAMNUMBER
;
579 else if (SUCCEEDED(hr
= sink_writer_get_buffer_length(sample
, ×tamp
, &length
)))
581 stream
->stats
.llLastTimestampReceived
= timestamp
;
582 stream
->stats
.qwNumSamplesReceived
++;
583 stream
->stats
.dwByteCountQueued
+= length
;
585 writer
->stats
.llLastTimestampReceived
= timestamp
;
586 writer
->stats
.qwNumSamplesReceived
++;
587 writer
->stats
.dwByteCountQueued
+= length
;
589 hr
= sink_writer_write_sample(writer
, stream
, sample
);
592 LeaveCriticalSection(&writer
->cs
);
597 static HRESULT WINAPI
sink_writer_SendStreamTick(IMFSinkWriter
*iface
, DWORD index
, LONGLONG timestamp
)
599 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
602 TRACE("%p, %lu, %s.\n", iface
, index
, wine_dbgstr_longlong(timestamp
));
604 EnterCriticalSection(&writer
->cs
);
606 if (writer
->state
!= SINK_WRITER_STATE_WRITING
)
607 hr
= MF_E_INVALIDREQUEST
;
609 hr
= sink_writer_send_stream_tick(writer
, index
, timestamp
);
611 LeaveCriticalSection(&writer
->cs
);
616 static HRESULT WINAPI
sink_writer_PlaceMarker(IMFSinkWriter
*iface
, DWORD index
, void *user_context
)
618 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
619 struct stream
*stream
;
622 TRACE("%p, %lu, %p.\n", iface
, index
, user_context
);
624 EnterCriticalSection(&writer
->cs
);
626 if (!writer
->callback
)
627 hr
= MF_E_INVALIDREQUEST
;
628 else if (writer
->state
!= SINK_WRITER_STATE_WRITING
)
629 hr
= MF_E_INVALIDREQUEST
;
630 else if (!(stream
= sink_writer_get_stream(writer
, index
)))
631 hr
= MF_E_INVALIDSTREAMNUMBER
;
633 hr
= sink_writer_queue_marker(writer
, stream
, MFSTREAMSINK_MARKER_DEFAULT
, 0, user_context
);
635 LeaveCriticalSection(&writer
->cs
);
640 static HRESULT WINAPI
sink_writer_NotifyEndOfSegment(IMFSinkWriter
*iface
, DWORD index
)
642 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
646 TRACE("%p, %lu.\n", iface
, index
);
648 EnterCriticalSection(&writer
->cs
);
650 if (writer
->state
!= SINK_WRITER_STATE_WRITING
)
651 hr
= MF_E_INVALIDREQUEST
;
652 else if (index
== MF_SINK_WRITER_ALL_STREAMS
)
654 for (i
= 0; i
< writer
->streams
.count
; ++i
)
656 if (FAILED(hr
= sink_writer_notify_end_of_segment(writer
, index
)))
658 WARN("Failed to place a marker for stream %u.\n", i
);
664 hr
= sink_writer_notify_end_of_segment(writer
, index
);
666 LeaveCriticalSection(&writer
->cs
);
671 static HRESULT WINAPI
sink_writer_Flush(IMFSinkWriter
*iface
, DWORD index
)
673 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
677 TRACE("%p, %lu.\n", iface
, index
);
679 EnterCriticalSection(&writer
->cs
);
681 if (writer
->state
!= SINK_WRITER_STATE_WRITING
)
682 hr
= MF_E_INVALIDREQUEST
;
683 else if (index
== MF_SINK_WRITER_ALL_STREAMS
)
685 for (i
= 0; i
< writer
->streams
.count
; ++i
)
687 if (FAILED(hr
= sink_writer_flush(writer
, i
)))
689 WARN("Failed to flush stream %u.\n", i
);
695 hr
= sink_writer_flush(writer
, index
);
697 LeaveCriticalSection(&writer
->cs
);
702 static HRESULT WINAPI
sink_writer_Finalize(IMFSinkWriter
*iface
)
704 FIXME("%p.\n", iface
);
709 static HRESULT
sink_writer_get_service(void *object
, REFGUID service
, REFIID riid
, void **ret
)
711 IUnknown
*iface
= object
;
715 if (!iface
) return MF_E_UNSUPPORTED_SERVICE
;
717 if (IsEqualGUID(service
, &GUID_NULL
))
718 return IUnknown_QueryInterface(iface
, riid
, ret
);
720 if (FAILED(hr
= IUnknown_QueryInterface(iface
, &IID_IMFGetService
, (void **)&gs
)))
723 hr
= IMFGetService_GetService(gs
, service
, riid
, ret
);
724 IMFGetService_Release(gs
);
728 static HRESULT WINAPI
sink_writer_GetServiceForStream(IMFSinkWriter
*iface
, DWORD index
, REFGUID service
,
729 REFIID riid
, void **object
)
731 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
732 HRESULT hr
= E_UNEXPECTED
;
733 struct stream
*stream
;
735 TRACE("%p, %lu, %s, %s, %p.\n", iface
, index
, debugstr_guid(service
), debugstr_guid(riid
), object
);
737 EnterCriticalSection(&writer
->cs
);
739 if (index
== MF_SINK_WRITER_MEDIASINK
)
740 hr
= sink_writer_get_service(writer
->sink
, service
, riid
, object
);
741 else if ((stream
= sink_writer_get_stream(writer
, index
)))
744 hr
= sink_writer_get_service(stream
->encoder
, service
, riid
, object
);
746 hr
= sink_writer_get_service(stream
->stream_sink
, service
, riid
, object
);
749 hr
= MF_E_INVALIDSTREAMNUMBER
;
751 LeaveCriticalSection(&writer
->cs
);
756 static HRESULT WINAPI
sink_writer_GetStatistics(IMFSinkWriter
*iface
, DWORD index
, MF_SINK_WRITER_STATISTICS
*stats
)
758 struct sink_writer
*writer
= impl_from_IMFSinkWriter(iface
);
759 struct stream
*stream
;
762 TRACE("%p, %lu, %p.\n", iface
, index
, stats
);
767 if (stats
->cb
!= sizeof(*stats
))
770 EnterCriticalSection(&writer
->cs
);
772 if (FAILED(writer
->status
))
774 else if (index
== MF_SINK_WRITER_ALL_STREAMS
)
775 *stats
= writer
->stats
;
776 else if ((stream
= sink_writer_get_stream(writer
, index
)))
777 *stats
= stream
->stats
;
779 hr
= MF_E_INVALIDSTREAMNUMBER
;
781 LeaveCriticalSection(&writer
->cs
);
786 static const IMFSinkWriterVtbl sink_writer_vtbl
=
788 sink_writer_QueryInterface
,
791 sink_writer_AddStream
,
792 sink_writer_SetInputMediaType
,
793 sink_writer_BeginWriting
,
794 sink_writer_WriteSample
,
795 sink_writer_SendStreamTick
,
796 sink_writer_PlaceMarker
,
797 sink_writer_NotifyEndOfSegment
,
799 sink_writer_Finalize
,
800 sink_writer_GetServiceForStream
,
801 sink_writer_GetStatistics
,
804 static HRESULT WINAPI
sink_writer_callback_QueryInterface(IMFAsyncCallback
*iface
,
805 REFIID riid
, void **out
)
807 if (IsEqualIID(riid
, &IID_IMFAsyncCallback
) ||
808 IsEqualIID(riid
, &IID_IUnknown
))
811 IMFAsyncCallback_AddRef(iface
);
815 WARN("Unsupported %s.\n", debugstr_guid(riid
));
817 return E_NOINTERFACE
;
820 static ULONG WINAPI
sink_writer_events_callback_AddRef(IMFAsyncCallback
*iface
)
822 struct sink_writer
*writer
= impl_from_events_callback_IMFAsyncCallback(iface
);
823 return IMFSinkWriter_AddRef(&writer
->IMFSinkWriter_iface
);
826 static ULONG WINAPI
sink_writer_events_callback_Release(IMFAsyncCallback
*iface
)
828 struct sink_writer
*writer
= impl_from_events_callback_IMFAsyncCallback(iface
);
829 return IMFSinkWriter_Release(&writer
->IMFSinkWriter_iface
);
832 static HRESULT WINAPI
sink_writer_callback_GetParameters(IMFAsyncCallback
*iface
,
833 DWORD
*flags
, DWORD
*queue
)
838 static struct stream
*sink_writer_get_stream_for_stream_sink(struct sink_writer
*writer
,
839 IMFStreamSink
*stream_sink
, DWORD
*index
)
843 for (i
= 0; i
< writer
->streams
.count
; ++i
)
845 if (writer
->streams
.items
[i
].stream_sink
== stream_sink
)
848 return &writer
->streams
.items
[i
];
855 static HRESULT WINAPI
sink_writer_events_callback_Invoke(IMFAsyncCallback
*iface
, IMFAsyncResult
*result
)
857 struct sink_writer
*writer
= impl_from_events_callback_IMFAsyncCallback(iface
);
858 struct marker_context
*context
;
859 IMFStreamSink
*stream_sink
;
860 MediaEventType event_type
;
861 struct stream
*stream
;
862 IMFMediaEvent
*event
;
868 TRACE("%p, %p.\n", iface
, result
);
870 stream_sink
= (IMFStreamSink
*)IMFAsyncResult_GetStateNoAddRef(result
);
872 if (FAILED(hr
= IMFStreamSink_EndGetEvent(stream_sink
, result
, &event
)))
875 IMFMediaEvent_GetType(event
, &event_type
);
876 IMFMediaEvent_GetStatus(event
, &status
);
878 TRACE("Got event %lu.\n", event_type
);
880 PropVariantInit(&value
);
882 EnterCriticalSection(&writer
->cs
);
884 if (writer
->status
== S_OK
&& FAILED(status
))
885 writer
->status
= status
;
887 if (writer
->status
== S_OK
&& (stream
= sink_writer_get_stream_for_stream_sink(writer
, stream_sink
, &index
)))
891 case MEStreamSinkRequestSample
:
893 timestamp
= MFGetSystemTime();
895 writer
->stats
.llLastSinkSampleRequest
= timestamp
;
896 writer
->stats
.dwNumOutstandingSinkSampleRequests
++;
897 stream
->stats
.llLastSinkSampleRequest
= timestamp
;
898 stream
->stats
.dwNumOutstandingSinkSampleRequests
++;
900 sink_writer_process_sample(writer
, stream
);
904 case MEStreamSinkMarker
:
905 if (FAILED(hr
= IMFMediaEvent_GetValue(event
, &value
))) break;
906 if (value
.vt
!= VT_UNKNOWN
|| !(context
= unsafe_impl_from_marker_context_IUnknown(value
.punkVal
))) break;
908 /* This relies on the fact that default marker type is only used for PlaceMarker(). */
909 if (context
->marker_type
== MFSTREAMSINK_MARKER_DEFAULT
)
910 IMFSinkWriterCallback_OnMarker(writer
->callback
, index
, context
->user_context
);
919 LeaveCriticalSection(&writer
->cs
);
921 PropVariantClear(&value
);
923 IMFMediaEvent_Release(event
);
925 IMFStreamSink_BeginGetEvent(stream_sink
, iface
, (IUnknown
*)stream_sink
);
930 static const IMFAsyncCallbackVtbl sink_writer_events_callback_vtbl
=
932 sink_writer_callback_QueryInterface
,
933 sink_writer_events_callback_AddRef
,
934 sink_writer_events_callback_Release
,
935 sink_writer_callback_GetParameters
,
936 sink_writer_events_callback_Invoke
,
939 static HRESULT
sink_writer_initialize_existing_streams(struct sink_writer
*writer
, IMFMediaSink
*sink
)
941 IMFStreamSink
*stream_sink
;
942 DWORD count
= 0, i
, index
;
945 if (FAILED(hr
= IMFMediaSink_GetStreamSinkCount(sink
, &count
))) return hr
;
946 if (!count
) return S_OK
;
948 for (i
= 0; i
< count
; ++i
)
950 if (FAILED(hr
= IMFMediaSink_GetStreamSinkByIndex(sink
, i
, &stream_sink
))) break;
951 hr
= sink_writer_add_stream(writer
, stream_sink
, &index
);
952 IMFStreamSink_Release(stream_sink
);
953 if (FAILED(hr
)) break;
959 HRESULT
create_sink_writer_from_sink(IMFMediaSink
*sink
, IMFAttributes
*attributes
,
960 REFIID riid
, void **out
)
962 struct sink_writer
*object
;
970 if (!(object
= calloc(1, sizeof(*object
))))
971 return E_OUTOFMEMORY
;
973 object
->IMFSinkWriter_iface
.lpVtbl
= &sink_writer_vtbl
;
974 object
->events_callback
.lpVtbl
= &sink_writer_events_callback_vtbl
;
975 object
->refcount
= 1;
977 IMFMediaSink_AddRef(sink
);
978 object
->stats
.cb
= sizeof(object
->stats
);
979 InitializeCriticalSection(&object
->cs
);
983 IMFAttributes_GetUnknown(attributes
, &MF_SINK_WRITER_ASYNC_CALLBACK
,
984 &IID_IMFSinkWriterCallback
, (void **)&object
->callback
);
987 if (FAILED(hr
= sink_writer_initialize_existing_streams(object
, sink
)))
989 IMFSinkWriter_Release(&object
->IMFSinkWriter_iface
);
993 hr
= IMFSinkWriter_QueryInterface(&object
->IMFSinkWriter_iface
, riid
, out
);
994 IMFSinkWriter_Release(&object
->IMFSinkWriter_iface
);
998 /***********************************************************************
999 * MFCreateSinkWriterFromMediaSink (mfreadwrite.@)
1001 HRESULT WINAPI
MFCreateSinkWriterFromMediaSink(IMFMediaSink
*sink
, IMFAttributes
*attributes
, IMFSinkWriter
**writer
)
1003 TRACE("%p, %p, %p.\n", sink
, attributes
, writer
);
1006 return E_INVALIDARG
;
1008 return create_sink_writer_from_sink(sink
, attributes
, &IID_IMFSinkWriter
, (void **)writer
);
1011 static HRESULT
sink_writer_get_sink_factory_class(const WCHAR
*url
, IMFAttributes
*attributes
, CLSID
*clsid
)
1013 static const struct extension_map
1019 { L
".mp4", &MFTranscodeContainerType_MPEG4
},
1020 { L
".mp3", &MFTranscodeContainerType_MP3
},
1021 { L
".wav", &MFTranscodeContainerType_WAVE
},
1022 { L
".avi", &MFTranscodeContainerType_AVI
},
1026 const GUID
*container
;
1030 { &MFTranscodeContainerType_MPEG4
, &CLSID_MFMPEG4SinkClassFactory
},
1031 { &MFTranscodeContainerType_MP3
, &CLSID_MFMP3SinkClassFactory
},
1032 { &MFTranscodeContainerType_WAVE
, &CLSID_MFWAVESinkClassFactory
},
1033 { &MFTranscodeContainerType_AVI
, &CLSID_MFAVISinkClassFactory
},
1035 const WCHAR
*extension
;
1041 if (!attributes
|| FAILED(IMFAttributes_GetGUID(attributes
, &MF_TRANSCODE_CONTAINERTYPE
, &container
)))
1043 const struct extension_map
*map
= NULL
;
1045 if (FAILED(PathCchFindExtension(url
, PATHCCH_MAX_CCH
, &extension
))) return E_INVALIDARG
;
1046 if (!extension
|| !*extension
) return E_INVALIDARG
;
1048 for (i
= 0; i
< ARRAY_SIZE(ext_map
); ++i
)
1052 if (!wcsicmp(map
->ext
, extension
))
1058 WARN("Couldn't find container type for extension %s.\n", debugstr_w(extension
));
1059 return E_INVALIDARG
;
1062 container
= *map
->guid
;
1067 if (!attributes
) return E_INVALIDARG
;
1068 if (FAILED(IMFAttributes_GetGUID(attributes
, &MF_TRANSCODE_CONTAINERTYPE
, &container
))) return E_INVALIDARG
;
1071 for (i
= 0; i
< ARRAY_SIZE(class_map
); ++i
)
1073 if (IsEqualGUID(&container
, class_map
[i
].container
))
1075 *clsid
= *class_map
[i
].clsid
;
1080 WARN("Couldn't find factory class for container %s.\n", debugstr_guid(&container
));
1081 return E_INVALIDARG
;
1084 HRESULT
create_sink_writer_from_url(const WCHAR
*url
, IMFByteStream
*bytestream
, IMFAttributes
*attributes
,
1085 REFIID riid
, void **out
)
1087 IMFSinkClassFactory
*factory
;
1094 if (!url
&& !bytestream
)
1095 return E_INVALIDARG
;
1097 if (FAILED(hr
= sink_writer_get_sink_factory_class(url
, attributes
, &clsid
))) return hr
;
1099 if (FAILED(hr
= CoCreateInstance(&clsid
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMFSinkClassFactory
, (void **)&factory
)))
1101 WARN("Failed to create a sink factory, hr %#lx.\n", hr
);
1106 IMFByteStream_AddRef(bytestream
);
1107 else if (FAILED(hr
= MFCreateFile(MF_ACCESSMODE_WRITE
, MF_OPENMODE_DELETE_IF_EXIST
, 0, url
, &bytestream
)))
1109 WARN("Failed to create output file stream, hr %#lx.\n", hr
);
1110 IMFSinkClassFactory_Release(factory
);
1114 hr
= IMFSinkClassFactory_CreateMediaSink(factory
, bytestream
, NULL
, NULL
, &sink
);
1115 IMFSinkClassFactory_Release(factory
);
1116 IMFByteStream_Release(bytestream
);
1119 WARN("Failed to create a sink, hr %#lx.\n", hr
);
1123 hr
= create_sink_writer_from_sink(sink
, attributes
, riid
, out
);
1124 IMFMediaSink_Release(sink
);
1129 /***********************************************************************
1130 * MFCreateSinkWriterFromURL (mfreadwrite.@)
1132 HRESULT WINAPI
MFCreateSinkWriterFromURL(const WCHAR
*url
, IMFByteStream
*bytestream
, IMFAttributes
*attributes
,
1133 IMFSinkWriter
**writer
)
1135 TRACE("%s, %p, %p, %p.\n", debugstr_w(url
), bytestream
, attributes
, writer
);
1138 return E_INVALIDARG
;
1140 return create_sink_writer_from_url(url
, bytestream
, attributes
, &IID_IMFSinkWriter
, (void **)writer
);