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(qcap
);
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 %#x.\n", hr
);
78 if ((hr
= IMediaSample_GetPointer(sample
, &data
)) != S_OK
)
79 ERR("Failed to get sample pointer, hr %#x.\n", hr
);
81 offset
.QuadPart
= start
;
82 if (!SetFilePointerEx(filter
->file
, offset
, NULL
, FILE_BEGIN
)
83 || !WriteFile(filter
->file
, data
, stop
- start
, &size
, NULL
))
85 ERR("Failed to write file, error %u.\n", GetLastError());
86 return HRESULT_FROM_WIN32(hr
);
89 if (size
!= stop
- start
)
90 ERR("Short write, %u/%u.\n", size
, (DWORD
)(stop
- start
));
95 static void deliver_ec_complete(struct file_writer
*filter
)
97 IMediaEventSink
*event_sink
;
99 if (SUCCEEDED(IFilterGraph_QueryInterface(filter
->filter
.graph
,
100 &IID_IMediaEventSink
, (void **)&event_sink
)))
102 IMediaEventSink_Notify(event_sink
, EC_COMPLETE
, S_OK
,
103 (LONG_PTR
)&filter
->filter
.IBaseFilter_iface
);
104 IMediaEventSink_Release(event_sink
);
108 static HRESULT
file_writer_sink_eos(struct strmbase_sink
*iface
)
110 struct file_writer
*filter
= impl_from_strmbase_pin(&iface
->pin
);
112 EnterCriticalSection(&filter
->filter
.csFilter
);
114 if (filter
->filter
.state
== State_Running
)
115 deliver_ec_complete(filter
);
119 LeaveCriticalSection(&filter
->filter
.csFilter
);
123 static const struct strmbase_sink_ops sink_ops
=
125 .base
.pin_query_interface
= file_writer_sink_query_interface
,
126 .base
.pin_query_accept
= file_writer_sink_query_accept
,
127 .pfnReceive
= file_writer_sink_receive
,
128 .sink_eos
= file_writer_sink_eos
,
131 static inline struct file_writer
*impl_from_strmbase_filter(struct strmbase_filter
*iface
)
133 return CONTAINING_RECORD(iface
, struct file_writer
, filter
);
136 static HRESULT
file_writer_query_interface(struct strmbase_filter
*iface
, REFIID iid
, void **out
)
138 struct file_writer
*filter
= impl_from_strmbase_filter(iface
);
140 if (IsEqualGUID(iid
, &IID_IAMFilterMiscFlags
))
141 *out
= &filter
->IAMFilterMiscFlags_iface
;
142 else if (IsEqualGUID(iid
, &IID_IFileSinkFilter
))
143 *out
= &filter
->IFileSinkFilter_iface
;
145 return E_NOINTERFACE
;
147 IUnknown_AddRef((IUnknown
*)*out
);
151 static struct strmbase_pin
*file_writer_get_pin(struct strmbase_filter
*iface
, unsigned int index
)
153 struct file_writer
*filter
= impl_from_strmbase_filter(iface
);
156 return &filter
->sink
.pin
;
160 static void file_writer_destroy(struct strmbase_filter
*iface
)
162 struct file_writer
*filter
= impl_from_strmbase_filter(iface
);
164 free(filter
->filename
);
165 strmbase_sink_cleanup(&filter
->sink
);
166 strmbase_filter_cleanup(&filter
->filter
);
170 static HRESULT
file_writer_init_stream(struct strmbase_filter
*iface
)
172 struct file_writer
*filter
= impl_from_strmbase_filter(iface
);
175 if ((file
= CreateFileW(filter
->filename
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
176 NULL
, CREATE_ALWAYS
, 0, NULL
)) == INVALID_HANDLE_VALUE
)
178 ERR("Failed to create %s, error %u.\n", debugstr_w(filter
->filename
), GetLastError());
179 return HRESULT_FROM_WIN32(GetLastError());
185 static HRESULT
file_writer_start_stream(struct strmbase_filter
*iface
, REFERENCE_TIME start
)
187 struct file_writer
*filter
= impl_from_strmbase_filter(iface
);
190 deliver_ec_complete(filter
);
195 static HRESULT
file_writer_cleanup_stream(struct strmbase_filter
*iface
)
197 struct file_writer
*filter
= impl_from_strmbase_filter(iface
);
199 CloseHandle(filter
->file
);
203 static struct strmbase_filter_ops filter_ops
=
205 .filter_query_interface
= file_writer_query_interface
,
206 .filter_get_pin
= file_writer_get_pin
,
207 .filter_destroy
= file_writer_destroy
,
208 .filter_init_stream
= file_writer_init_stream
,
209 .filter_start_stream
= file_writer_start_stream
,
210 .filter_cleanup_stream
= file_writer_cleanup_stream
,
213 static inline struct file_writer
*impl_from_IFileSinkFilter(IFileSinkFilter
*iface
)
215 return CONTAINING_RECORD(iface
, struct file_writer
, IFileSinkFilter_iface
);
218 static HRESULT WINAPI
filesinkfilter_QueryInterface(IFileSinkFilter
*iface
, REFIID iid
, void **out
)
220 struct file_writer
*filter
= impl_from_IFileSinkFilter(iface
);
221 return IUnknown_QueryInterface(filter
->filter
.outer_unk
, iid
, out
);
224 static ULONG WINAPI
filesinkfilter_AddRef(IFileSinkFilter
*iface
)
226 struct file_writer
*filter
= impl_from_IFileSinkFilter(iface
);
227 return IUnknown_AddRef(filter
->filter
.outer_unk
);
230 static ULONG WINAPI
filesinkfilter_Release(IFileSinkFilter
*iface
)
232 struct file_writer
*filter
= impl_from_IFileSinkFilter(iface
);
233 return IUnknown_Release(filter
->filter
.outer_unk
);
236 static HRESULT WINAPI
filesinkfilter_SetFileName(IFileSinkFilter
*iface
,
237 LPCOLESTR filename
, const AM_MEDIA_TYPE
*mt
)
239 struct file_writer
*filter
= impl_from_IFileSinkFilter(iface
);
242 TRACE("filter %p, filename %s, mt %p.\n", filter
, debugstr_w(filename
), mt
);
243 strmbase_dump_media_type(mt
);
246 FIXME("Ignoring media type %p.\n", mt
);
248 if (!(new_filename
= wcsdup(filename
)))
249 return E_OUTOFMEMORY
;
251 free(filter
->filename
);
252 filter
->filename
= new_filename
;
256 static HRESULT WINAPI
filesinkfilter_GetCurFile(IFileSinkFilter
*iface
,
257 LPOLESTR
*filename
, AM_MEDIA_TYPE
*mt
)
259 struct file_writer
*filter
= impl_from_IFileSinkFilter(iface
);
261 FIXME("filter %p, filename %p, mt %p, stub!\n", filter
, filename
, mt
);
266 static const IFileSinkFilterVtbl filesinkfilter_vtbl
=
268 filesinkfilter_QueryInterface
,
269 filesinkfilter_AddRef
,
270 filesinkfilter_Release
,
271 filesinkfilter_SetFileName
,
272 filesinkfilter_GetCurFile
,
275 static inline struct file_writer
*impl_from_IAMFilterMiscFlags(IAMFilterMiscFlags
*iface
)
277 return CONTAINING_RECORD(iface
, struct file_writer
, IAMFilterMiscFlags_iface
);
280 static HRESULT WINAPI
misc_flags_QueryInterface(IAMFilterMiscFlags
*iface
, REFIID iid
, void **out
)
282 struct file_writer
*filter
= impl_from_IAMFilterMiscFlags(iface
);
283 return IUnknown_QueryInterface(filter
->filter
.outer_unk
, iid
, out
);
286 static ULONG WINAPI
misc_flags_AddRef(IAMFilterMiscFlags
*iface
)
288 struct file_writer
*filter
= impl_from_IAMFilterMiscFlags(iface
);
289 return IUnknown_AddRef(filter
->filter
.outer_unk
);
292 static ULONG WINAPI
misc_flags_Release(IAMFilterMiscFlags
*iface
)
294 struct file_writer
*filter
= impl_from_IAMFilterMiscFlags(iface
);
295 return IUnknown_Release(filter
->filter
.outer_unk
);
298 static ULONG WINAPI
misc_flags_GetMiscFlags(IAMFilterMiscFlags
*iface
)
300 struct file_writer
*filter
= impl_from_IAMFilterMiscFlags(iface
);
302 TRACE("filter %p.\n", filter
);
304 return AM_FILTER_MISC_FLAGS_IS_RENDERER
;
307 static const IAMFilterMiscFlagsVtbl misc_flags_vtbl
=
309 misc_flags_QueryInterface
,
312 misc_flags_GetMiscFlags
,
315 HRESULT
file_writer_create(IUnknown
*outer
, IUnknown
**out
)
317 struct file_writer
*object
;
319 if (!(object
= calloc(1, sizeof(*object
))))
320 return E_OUTOFMEMORY
;
322 strmbase_filter_init(&object
->filter
, outer
, &CLSID_FileWriter
, &filter_ops
);
323 object
->IFileSinkFilter_iface
.lpVtbl
= &filesinkfilter_vtbl
;
324 object
->IAMFilterMiscFlags_iface
.lpVtbl
= &misc_flags_vtbl
;
326 strmbase_sink_init(&object
->sink
, &object
->filter
, L
"in", &sink_ops
, NULL
);
328 TRACE("Created file writer %p.\n", object
);
329 *out
= &object
->filter
.IUnknown_inner
;