4 * Copyright (C) 2020 Zebediah Figura
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "qcap_private.h"
23 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
27 struct strmbase_filter filter
;
28 IAMFilterMiscFlags IAMFilterMiscFlags_iface
;
29 IFileSinkFilter IFileSinkFilter_iface
;
31 struct strmbase_sink sink
;
39 static inline struct file_writer
*impl_from_strmbase_pin(struct strmbase_pin
*iface
)
41 return CONTAINING_RECORD(iface
, struct file_writer
, sink
.pin
);
44 static HRESULT
file_writer_sink_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
46 struct file_writer
*filter
= impl_from_strmbase_pin(iface
);
48 if (IsEqualGUID(iid
, &IID_IMemInputPin
))
49 *out
= &filter
->sink
.IMemInputPin_iface
;
53 IUnknown_AddRef((IUnknown
*)*out
);
57 static HRESULT
file_writer_sink_query_accept(struct strmbase_pin
*iface
, const AM_MEDIA_TYPE
*mt
)
59 struct file_writer
*filter
= impl_from_strmbase_pin(iface
);
61 if (filter
->filename
&& !IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Stream
))
66 static HRESULT WINAPI
file_writer_sink_receive(struct strmbase_sink
*iface
, IMediaSample
*sample
)
68 struct file_writer
*filter
= impl_from_strmbase_pin(&iface
->pin
);
69 REFERENCE_TIME start
, stop
;
75 if ((hr
= IMediaSample_GetTime(sample
, &start
, &stop
)) != S_OK
)
76 ERR("Failed to get sample time, hr %#lx.\n", hr
);
79 if ((hr
= IMediaSample_GetPointer(sample
, &data
)) != S_OK
)
80 ERR("Failed to get sample pointer, hr %#lx.\n", hr
);
82 offset
.QuadPart
= start
;
83 if (!SetFilePointerEx(filter
->file
, offset
, NULL
, FILE_BEGIN
)
84 || !WriteFile(filter
->file
, data
, size
, &ret_size
, NULL
))
86 ERR("Failed to write file, error %lu.\n", GetLastError());
87 return HRESULT_FROM_WIN32(hr
);
91 ERR("Short write, %lu/%lu.\n", ret_size
, size
);
96 static void deliver_ec_complete(struct file_writer
*filter
)
98 IMediaEventSink
*event_sink
;
100 if (SUCCEEDED(IFilterGraph_QueryInterface(filter
->filter
.graph
,
101 &IID_IMediaEventSink
, (void **)&event_sink
)))
103 IMediaEventSink_Notify(event_sink
, EC_COMPLETE
, S_OK
,
104 (LONG_PTR
)&filter
->filter
.IBaseFilter_iface
);
105 IMediaEventSink_Release(event_sink
);
109 static HRESULT
file_writer_sink_eos(struct strmbase_sink
*iface
)
111 struct file_writer
*filter
= impl_from_strmbase_pin(&iface
->pin
);
113 EnterCriticalSection(&filter
->filter
.filter_cs
);
115 if (filter
->filter
.state
== State_Running
)
116 deliver_ec_complete(filter
);
120 LeaveCriticalSection(&filter
->filter
.filter_cs
);
124 static const struct strmbase_sink_ops sink_ops
=
126 .base
.pin_query_interface
= file_writer_sink_query_interface
,
127 .base
.pin_query_accept
= file_writer_sink_query_accept
,
128 .pfnReceive
= file_writer_sink_receive
,
129 .sink_eos
= file_writer_sink_eos
,
132 static inline struct file_writer
*impl_from_strmbase_filter(struct strmbase_filter
*iface
)
134 return CONTAINING_RECORD(iface
, struct file_writer
, filter
);
137 static HRESULT
file_writer_query_interface(struct strmbase_filter
*iface
, REFIID iid
, void **out
)
139 struct file_writer
*filter
= impl_from_strmbase_filter(iface
);
141 if (IsEqualGUID(iid
, &IID_IAMFilterMiscFlags
))
142 *out
= &filter
->IAMFilterMiscFlags_iface
;
143 else if (IsEqualGUID(iid
, &IID_IFileSinkFilter
))
144 *out
= &filter
->IFileSinkFilter_iface
;
146 return E_NOINTERFACE
;
148 IUnknown_AddRef((IUnknown
*)*out
);
152 static struct strmbase_pin
*file_writer_get_pin(struct strmbase_filter
*iface
, unsigned int index
)
154 struct file_writer
*filter
= impl_from_strmbase_filter(iface
);
157 return &filter
->sink
.pin
;
161 static void file_writer_destroy(struct strmbase_filter
*iface
)
163 struct file_writer
*filter
= impl_from_strmbase_filter(iface
);
165 free(filter
->filename
);
166 strmbase_sink_cleanup(&filter
->sink
);
167 strmbase_filter_cleanup(&filter
->filter
);
171 static HRESULT
file_writer_init_stream(struct strmbase_filter
*iface
)
173 struct file_writer
*filter
= impl_from_strmbase_filter(iface
);
176 if ((file
= CreateFileW(filter
->filename
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
177 NULL
, CREATE_ALWAYS
, 0, NULL
)) == INVALID_HANDLE_VALUE
)
179 ERR("Failed to create %s, error %lu.\n", debugstr_w(filter
->filename
), GetLastError());
180 return HRESULT_FROM_WIN32(GetLastError());
186 static HRESULT
file_writer_start_stream(struct strmbase_filter
*iface
, REFERENCE_TIME start
)
188 struct file_writer
*filter
= impl_from_strmbase_filter(iface
);
191 deliver_ec_complete(filter
);
196 static HRESULT
file_writer_cleanup_stream(struct strmbase_filter
*iface
)
198 struct file_writer
*filter
= impl_from_strmbase_filter(iface
);
200 CloseHandle(filter
->file
);
204 static struct strmbase_filter_ops filter_ops
=
206 .filter_query_interface
= file_writer_query_interface
,
207 .filter_get_pin
= file_writer_get_pin
,
208 .filter_destroy
= file_writer_destroy
,
209 .filter_init_stream
= file_writer_init_stream
,
210 .filter_start_stream
= file_writer_start_stream
,
211 .filter_cleanup_stream
= file_writer_cleanup_stream
,
214 static inline struct file_writer
*impl_from_IFileSinkFilter(IFileSinkFilter
*iface
)
216 return CONTAINING_RECORD(iface
, struct file_writer
, IFileSinkFilter_iface
);
219 static HRESULT WINAPI
filesinkfilter_QueryInterface(IFileSinkFilter
*iface
, REFIID iid
, void **out
)
221 struct file_writer
*filter
= impl_from_IFileSinkFilter(iface
);
222 return IUnknown_QueryInterface(filter
->filter
.outer_unk
, iid
, out
);
225 static ULONG WINAPI
filesinkfilter_AddRef(IFileSinkFilter
*iface
)
227 struct file_writer
*filter
= impl_from_IFileSinkFilter(iface
);
228 return IUnknown_AddRef(filter
->filter
.outer_unk
);
231 static ULONG WINAPI
filesinkfilter_Release(IFileSinkFilter
*iface
)
233 struct file_writer
*filter
= impl_from_IFileSinkFilter(iface
);
234 return IUnknown_Release(filter
->filter
.outer_unk
);
237 static HRESULT WINAPI
filesinkfilter_SetFileName(IFileSinkFilter
*iface
,
238 LPCOLESTR filename
, const AM_MEDIA_TYPE
*mt
)
240 struct file_writer
*filter
= impl_from_IFileSinkFilter(iface
);
243 TRACE("filter %p, filename %s, mt %p.\n", filter
, debugstr_w(filename
), mt
);
244 strmbase_dump_media_type(mt
);
247 FIXME("Ignoring media type %p.\n", mt
);
249 if (!(new_filename
= wcsdup(filename
)))
250 return E_OUTOFMEMORY
;
252 free(filter
->filename
);
253 filter
->filename
= new_filename
;
257 static HRESULT WINAPI
filesinkfilter_GetCurFile(IFileSinkFilter
*iface
,
258 LPOLESTR
*filename
, AM_MEDIA_TYPE
*mt
)
260 struct file_writer
*filter
= impl_from_IFileSinkFilter(iface
);
262 FIXME("filter %p, filename %p, mt %p, stub!\n", filter
, filename
, mt
);
267 static const IFileSinkFilterVtbl filesinkfilter_vtbl
=
269 filesinkfilter_QueryInterface
,
270 filesinkfilter_AddRef
,
271 filesinkfilter_Release
,
272 filesinkfilter_SetFileName
,
273 filesinkfilter_GetCurFile
,
276 static inline struct file_writer
*impl_from_IAMFilterMiscFlags(IAMFilterMiscFlags
*iface
)
278 return CONTAINING_RECORD(iface
, struct file_writer
, IAMFilterMiscFlags_iface
);
281 static HRESULT WINAPI
misc_flags_QueryInterface(IAMFilterMiscFlags
*iface
, REFIID iid
, void **out
)
283 struct file_writer
*filter
= impl_from_IAMFilterMiscFlags(iface
);
284 return IUnknown_QueryInterface(filter
->filter
.outer_unk
, iid
, out
);
287 static ULONG WINAPI
misc_flags_AddRef(IAMFilterMiscFlags
*iface
)
289 struct file_writer
*filter
= impl_from_IAMFilterMiscFlags(iface
);
290 return IUnknown_AddRef(filter
->filter
.outer_unk
);
293 static ULONG WINAPI
misc_flags_Release(IAMFilterMiscFlags
*iface
)
295 struct file_writer
*filter
= impl_from_IAMFilterMiscFlags(iface
);
296 return IUnknown_Release(filter
->filter
.outer_unk
);
299 static ULONG WINAPI
misc_flags_GetMiscFlags(IAMFilterMiscFlags
*iface
)
301 struct file_writer
*filter
= impl_from_IAMFilterMiscFlags(iface
);
303 TRACE("filter %p.\n", filter
);
305 return AM_FILTER_MISC_FLAGS_IS_RENDERER
;
308 static const IAMFilterMiscFlagsVtbl misc_flags_vtbl
=
310 misc_flags_QueryInterface
,
313 misc_flags_GetMiscFlags
,
316 HRESULT
file_writer_create(IUnknown
*outer
, IUnknown
**out
)
318 struct file_writer
*object
;
320 if (!(object
= calloc(1, sizeof(*object
))))
321 return E_OUTOFMEMORY
;
323 strmbase_filter_init(&object
->filter
, outer
, &CLSID_FileWriter
, &filter_ops
);
324 object
->IFileSinkFilter_iface
.lpVtbl
= &filesinkfilter_vtbl
;
325 object
->IAMFilterMiscFlags_iface
.lpVtbl
= &misc_flags_vtbl
;
327 strmbase_sink_init(&object
->sink
, &object
->filter
, L
"in", &sink_ops
, NULL
);
329 TRACE("Created file writer %p.\n", object
);
330 *out
= &object
->filter
.IUnknown_inner
;