wine-wine/dlls/qcap/filewriter.c

338 lines
10 KiB
C

/*
* File writer filter
*
* Copyright (C) 2020 Zebediah Figura
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
#include "dshow.h"
#include "qcap_main.h"
#include "wine/debug.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(qcap);
struct file_writer
{
struct strmbase_filter filter;
IAMFilterMiscFlags IAMFilterMiscFlags_iface;
IFileSinkFilter IFileSinkFilter_iface;
struct strmbase_sink sink;
WCHAR *filename;
HANDLE file;
BOOL eos;
};
static inline struct file_writer *impl_from_strmbase_pin(struct strmbase_pin *iface)
{
return CONTAINING_RECORD(iface, struct file_writer, sink.pin);
}
static HRESULT file_writer_sink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
{
struct file_writer *filter = impl_from_strmbase_pin(iface);
if (IsEqualGUID(iid, &IID_IMemInputPin))
*out = &filter->sink.IMemInputPin_iface;
else
return E_NOINTERFACE;
IUnknown_AddRef((IUnknown *)*out);
return S_OK;
}
static HRESULT file_writer_sink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt)
{
struct file_writer *filter = impl_from_strmbase_pin(iface);
if (filter->filename && !IsEqualGUID(&mt->majortype, &MEDIATYPE_Stream))
return S_FALSE;
return S_OK;
}
static HRESULT WINAPI file_writer_sink_receive(struct strmbase_sink *iface, IMediaSample *sample)
{
struct file_writer *filter = impl_from_strmbase_pin(&iface->pin);
REFERENCE_TIME start, stop;
LARGE_INTEGER offset;
HRESULT hr;
DWORD size;
BYTE *data;
if ((hr = IMediaSample_GetTime(sample, &start, &stop)) != S_OK)
ERR("Failed to get sample time, hr %#x.\n", hr);
if ((hr = IMediaSample_GetPointer(sample, &data)) != S_OK)
ERR("Failed to get sample pointer, hr %#x.\n", hr);
offset.QuadPart = start;
if (!SetFilePointerEx(filter->file, offset, NULL, FILE_BEGIN)
|| !WriteFile(filter->file, data, stop - start, &size, NULL))
{
ERR("Failed to write file, error %u.\n", GetLastError());
return HRESULT_FROM_WIN32(hr);
}
if (size != stop - start)
ERR("Short write, %u/%u.\n", size, (DWORD)(stop - start));
return S_OK;
}
static void deliver_ec_complete(struct file_writer *filter)
{
IMediaEventSink *event_sink;
if (SUCCEEDED(IFilterGraph_QueryInterface(filter->filter.graph,
&IID_IMediaEventSink, (void **)&event_sink)))
{
IMediaEventSink_Notify(event_sink, EC_COMPLETE, S_OK,
(LONG_PTR)&filter->filter.IBaseFilter_iface);
IMediaEventSink_Release(event_sink);
}
}
static HRESULT file_writer_sink_eos(struct strmbase_sink *iface)
{
struct file_writer *filter = impl_from_strmbase_pin(&iface->pin);
EnterCriticalSection(&filter->filter.csFilter);
if (filter->filter.state == State_Running)
deliver_ec_complete(filter);
else
filter->eos = TRUE;
LeaveCriticalSection(&filter->filter.csFilter);
return S_OK;
}
static const struct strmbase_sink_ops sink_ops =
{
.base.pin_query_interface = file_writer_sink_query_interface,
.base.pin_query_accept = file_writer_sink_query_accept,
.pfnReceive = file_writer_sink_receive,
.sink_eos = file_writer_sink_eos,
};
static inline struct file_writer *impl_from_strmbase_filter(struct strmbase_filter *iface)
{
return CONTAINING_RECORD(iface, struct file_writer, filter);
}
static HRESULT file_writer_query_interface(struct strmbase_filter *iface, REFIID iid, void **out)
{
struct file_writer *filter = impl_from_strmbase_filter(iface);
if (IsEqualGUID(iid, &IID_IAMFilterMiscFlags))
*out = &filter->IAMFilterMiscFlags_iface;
else if (IsEqualGUID(iid, &IID_IFileSinkFilter))
*out = &filter->IFileSinkFilter_iface;
else
return E_NOINTERFACE;
IUnknown_AddRef((IUnknown *)*out);
return S_OK;
}
static struct strmbase_pin *file_writer_get_pin(struct strmbase_filter *iface, unsigned int index)
{
struct file_writer *filter = impl_from_strmbase_filter(iface);
if (!index)
return &filter->sink.pin;
return NULL;
}
static void file_writer_destroy(struct strmbase_filter *iface)
{
struct file_writer *filter = impl_from_strmbase_filter(iface);
heap_free(filter->filename);
strmbase_sink_cleanup(&filter->sink);
strmbase_filter_cleanup(&filter->filter);
heap_free(filter);
}
static HRESULT file_writer_init_stream(struct strmbase_filter *iface)
{
struct file_writer *filter = impl_from_strmbase_filter(iface);
HANDLE file;
if ((file = CreateFileW(filter->filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, CREATE_ALWAYS, 0, NULL)) == INVALID_HANDLE_VALUE)
{
ERR("Failed to create %s, error %u.\n", debugstr_w(filter->filename), GetLastError());
return HRESULT_FROM_WIN32(GetLastError());
}
filter->file = file;
return S_OK;
}
static HRESULT file_writer_start_stream(struct strmbase_filter *iface, REFERENCE_TIME start)
{
struct file_writer *filter = impl_from_strmbase_filter(iface);
if (filter->eos)
deliver_ec_complete(filter);
filter->eos = FALSE;
return S_OK;
}
static HRESULT file_writer_cleanup_stream(struct strmbase_filter *iface)
{
struct file_writer *filter = impl_from_strmbase_filter(iface);
CloseHandle(filter->file);
return S_OK;
}
static struct strmbase_filter_ops filter_ops =
{
.filter_query_interface = file_writer_query_interface,
.filter_get_pin = file_writer_get_pin,
.filter_destroy = file_writer_destroy,
.filter_init_stream = file_writer_init_stream,
.filter_start_stream = file_writer_start_stream,
.filter_cleanup_stream = file_writer_cleanup_stream,
};
static inline struct file_writer *impl_from_IFileSinkFilter(IFileSinkFilter *iface)
{
return CONTAINING_RECORD(iface, struct file_writer, IFileSinkFilter_iface);
}
static HRESULT WINAPI filesinkfilter_QueryInterface(IFileSinkFilter *iface, REFIID iid, void **out)
{
struct file_writer *filter = impl_from_IFileSinkFilter(iface);
return IUnknown_QueryInterface(filter->filter.outer_unk, iid, out);
}
static ULONG WINAPI filesinkfilter_AddRef(IFileSinkFilter *iface)
{
struct file_writer *filter = impl_from_IFileSinkFilter(iface);
return IUnknown_AddRef(filter->filter.outer_unk);
}
static ULONG WINAPI filesinkfilter_Release(IFileSinkFilter *iface)
{
struct file_writer *filter = impl_from_IFileSinkFilter(iface);
return IUnknown_Release(filter->filter.outer_unk);
}
static HRESULT WINAPI filesinkfilter_SetFileName(IFileSinkFilter *iface,
LPCOLESTR filename, const AM_MEDIA_TYPE *mt)
{
struct file_writer *filter = impl_from_IFileSinkFilter(iface);
WCHAR *new_filename;
TRACE("filter %p, filename %s, mt %p, stub!\n", filter, debugstr_w(filename), mt);
strmbase_dump_media_type(mt);
if (mt)
FIXME("Ignoring media type %p.\n", mt);
if (!(new_filename = heap_alloc((strlenW(filename) + 1) * sizeof(WCHAR))))
return E_OUTOFMEMORY;
strcpyW(new_filename, filename);
heap_free(filter->filename);
filter->filename = new_filename;
return S_OK;
}
static HRESULT WINAPI filesinkfilter_GetCurFile(IFileSinkFilter *iface,
LPOLESTR *filename, AM_MEDIA_TYPE *mt)
{
struct file_writer *filter = impl_from_IFileSinkFilter(iface);
FIXME("filter %p, filename %p, mt %p, stub!\n", filter, filename, mt);
return E_NOTIMPL;
}
static const IFileSinkFilterVtbl filesinkfilter_vtbl =
{
filesinkfilter_QueryInterface,
filesinkfilter_AddRef,
filesinkfilter_Release,
filesinkfilter_SetFileName,
filesinkfilter_GetCurFile,
};
static inline struct file_writer *impl_from_IAMFilterMiscFlags(IAMFilterMiscFlags *iface)
{
return CONTAINING_RECORD(iface, struct file_writer, IAMFilterMiscFlags_iface);
}
static HRESULT WINAPI misc_flags_QueryInterface(IAMFilterMiscFlags *iface, REFIID iid, void **out)
{
struct file_writer *filter = impl_from_IAMFilterMiscFlags(iface);
return IUnknown_QueryInterface(filter->filter.outer_unk, iid, out);
}
static ULONG WINAPI misc_flags_AddRef(IAMFilterMiscFlags *iface)
{
struct file_writer *filter = impl_from_IAMFilterMiscFlags(iface);
return IUnknown_AddRef(filter->filter.outer_unk);
}
static ULONG WINAPI misc_flags_Release(IAMFilterMiscFlags *iface)
{
struct file_writer *filter = impl_from_IAMFilterMiscFlags(iface);
return IUnknown_Release(filter->filter.outer_unk);
}
static ULONG WINAPI misc_flags_GetMiscFlags(IAMFilterMiscFlags *iface)
{
struct file_writer *filter = impl_from_IAMFilterMiscFlags(iface);
TRACE("filter %p.\n", filter);
return AM_FILTER_MISC_FLAGS_IS_RENDERER;
}
static const IAMFilterMiscFlagsVtbl misc_flags_vtbl =
{
misc_flags_QueryInterface,
misc_flags_AddRef,
misc_flags_Release,
misc_flags_GetMiscFlags,
};
HRESULT file_writer_create(IUnknown *outer, IUnknown **out)
{
static const WCHAR sink_name[] = {'i','n',0};
struct file_writer *object;
if (!(object = heap_alloc_zero(sizeof(*object))))
return E_OUTOFMEMORY;
strmbase_filter_init(&object->filter, outer, &CLSID_FileWriter, &filter_ops);
object->IFileSinkFilter_iface.lpVtbl = &filesinkfilter_vtbl;
object->IAMFilterMiscFlags_iface.lpVtbl = &misc_flags_vtbl;
strmbase_sink_init(&object->sink, &object->filter, sink_name, &sink_ops, NULL);
TRACE("Created file writer %p.\n", object);
*out = &object->filter.IUnknown_inner;
return S_OK;
}