Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
v2: I did most changes, except the following "big" ones due to reasons given (I can still do/remove them if still requested). I figured looking at the big picture again might be different with the other changes in place:
I kept the loop in the test filter since it helps track down race conditions/regressions, but now I fill it with black pixels until the check_bitmap tests. I only fill it with the pattern in the same test patch that I verify it.
I still used the actual stretch algorithm that Windows uses. While I can certainly take this off, I'd have to remove all the stretch tests (they won't pass even on Windows, and having the stretching algorithm only in the tests seems pointless, as it's almost a copy-paste). Having considered this, let me know if I should still remove it, though.
Also, the graph state is checked in this patch now, and is indeed "paused" on Windows.
dlls/qedit/tests/mediadet.c | 424 ++++++++++++++++++++++++++++++++++-- 1 file changed, 411 insertions(+), 13 deletions(-)
diff --git a/dlls/qedit/tests/mediadet.c b/dlls/qedit/tests/mediadet.c index dc83bb9..0a05a65 100644 --- a/dlls/qedit/tests/mediadet.c +++ b/dlls/qedit/tests/mediadet.c @@ -136,6 +136,12 @@ struct testfilter struct strmbase_filter filter; struct strmbase_source source; IMediaSeeking IMediaSeeking_iface; + + BOOL bitmap_grab_mode; + const GUID *majortype; + const GUID *time_format; + LONGLONG cur_pos; + HANDLE thread; };
static inline struct testfilter *impl_from_strmbase_filter(struct strmbase_filter *iface) @@ -158,10 +164,95 @@ static void testfilter_destroy(struct strmbase_filter *iface) strmbase_filter_cleanup(&filter->filter); }
+static DWORD WINAPI testfilter_frame_thread(void *arg) +{ + REFERENCE_TIME start_time, end_time; + struct testfilter *filter = arg; + IMemAllocator *allocator; + IMediaSample *sample; + HRESULT hr; + BYTE *data; + + hr = IMemInputPin_GetAllocator(filter->source.pMemInputPin, &allocator); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + start_time = (filter->cur_pos == 0xdeadbeef) ? 0 : filter->cur_pos; + while (hr == S_OK) + { + hr = IMemAllocator_GetBuffer(allocator, &sample, NULL, NULL, 0); + if (hr == VFW_E_NOT_COMMITTED) + { + IMemAllocator_Commit(allocator); + hr = IMemAllocator_GetBuffer(allocator, &sample, NULL, NULL, 0); + } + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaSample_GetPointer(sample, &data); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + memset(data, 0, 640 * 480 * 3); + + hr = IMediaSample_SetActualDataLength(sample, 640 * 480 * 3); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + end_time = start_time + 400000; + hr = IMediaSample_SetTime(sample, &start_time, &end_time); + ok(hr == S_OK, "Got hr %#x.\n", hr); + start_time = end_time; + + if (winetest_debug > 1) trace("%04x: Sending frame.\n", GetCurrentThreadId()); + hr = IMemInputPin_Receive(filter->source.pMemInputPin, sample); + if (winetest_debug > 1) trace("%04x: Returned %#x.\n", GetCurrentThreadId(), hr); + + IMediaSample_Release(sample); + } + + IMemAllocator_Release(allocator); + return hr; +} + +static HRESULT testfilter_init_stream(struct strmbase_filter *iface) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface); + HRESULT hr; + + if (!filter->bitmap_grab_mode) return S_OK; + + hr = BaseOutputPinImpl_Active(&filter->source); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + filter->thread = CreateThread(NULL, 0, testfilter_frame_thread, filter, 0, NULL); + ok(filter->thread != NULL, "Failed to create thread: %#x.\n", GetLastError()); + + return S_OK; +} + +static HRESULT testfilter_cleanup_stream(struct strmbase_filter *iface) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface); + HRESULT hr; + + if (filter->thread) + { + WaitForSingleObject(filter->thread, INFINITE); + CloseHandle(filter->thread); + filter->thread = NULL; + } + if (!filter->bitmap_grab_mode) + return S_OK; + + hr = BaseOutputPinImpl_Inactive(&filter->source); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + return S_OK; +} + static const struct strmbase_filter_ops testfilter_ops = { .filter_get_pin = testfilter_get_pin, .filter_destroy = testfilter_destroy, + .filter_init_stream = testfilter_init_stream, + .filter_cleanup_stream = testfilter_cleanup_stream };
static inline struct testfilter *impl_from_strmbase_pin(struct strmbase_pin *iface) @@ -171,11 +262,13 @@ static inline struct testfilter *impl_from_strmbase_pin(struct strmbase_pin *ifa
static HRESULT testsource_get_media_type(struct strmbase_pin *iface, unsigned int index, AM_MEDIA_TYPE *mt) { + struct testfilter *filter = impl_from_strmbase_pin(iface); + static const VIDEOINFOHEADER source_format = { .bmiHeader.biSize = sizeof(BITMAPINFOHEADER), .bmiHeader.biWidth = 640, - .bmiHeader.biHeight = 480, + .bmiHeader.biHeight = -480, .bmiHeader.biPlanes = 1, .bmiHeader.biBitCount = 24, .bmiHeader.biCompression = BI_RGB, @@ -185,7 +278,7 @@ static HRESULT testsource_get_media_type(struct strmbase_pin *iface, unsigned in if (index) return S_FALSE;
- mt->majortype = MEDIATYPE_Video; + mt->majortype = *filter->majortype; mt->subtype = MEDIASUBTYPE_RGB24; mt->bFixedSizeSamples = TRUE; mt->bTemporalCompression = FALSE; @@ -211,10 +304,37 @@ static HRESULT testsource_query_interface(struct strmbase_pin *iface, REFIID iid return S_OK; }
+static HRESULT WINAPI testsource_DecideBufferSize(struct strmbase_source *iface, + IMemAllocator *allocator, ALLOCATOR_PROPERTIES *requested) +{ + ALLOCATOR_PROPERTIES actual; + + if (!requested->cbAlign) + requested->cbAlign = 1; + + if (requested->cbBuffer < 640 * 480 * 3) + requested->cbBuffer = 640 * 480 * 3; + + if (!requested->cBuffers) + requested->cBuffers = 1; + + return IMemAllocator_SetProperties(allocator, requested, &actual); +} + static HRESULT WINAPI testsource_DecideAllocator(struct strmbase_source *iface, IMemInputPin *peer, IMemAllocator **allocator) { - return S_OK; + ALLOCATOR_PROPERTIES props = {0}; + HRESULT hr; + + hr = BaseOutputPinImpl_InitAllocator(iface, allocator); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + IMemInputPin_GetAllocatorRequirements(peer, &props); + hr = testsource_DecideBufferSize(iface, *allocator, &props); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + return IMemInputPin_NotifyAllocator(peer, *allocator, FALSE); }
static const struct strmbase_source_ops testsource_ops = @@ -222,6 +342,7 @@ static const struct strmbase_source_ops testsource_ops = .base.pin_get_media_type = testsource_get_media_type, .base.pin_query_interface = testsource_query_interface, .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection, + .pfnDecideBufferSize = testsource_DecideBufferSize, .pfnDecideAllocator = testsource_DecideAllocator, };
@@ -250,8 +371,12 @@ static ULONG WINAPI testseek_Release(IMediaSeeking *iface)
static HRESULT WINAPI testseek_GetCapabilities(IMediaSeeking *iface, DWORD *caps) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + struct testfilter *filter = impl_from_IMediaSeeking(iface); + if (winetest_debug > 1) trace("IMediaSeeking_GetCapabilities()\n"); + + ok(filter->bitmap_grab_mode, "Unexpected call.\n"); + *caps = 0; + return S_OK; }
static HRESULT WINAPI testseek_CheckCapabilities(IMediaSeeking *iface, DWORD *caps) @@ -262,8 +387,12 @@ static HRESULT WINAPI testseek_CheckCapabilities(IMediaSeeking *iface, DWORD *ca
static HRESULT WINAPI testseek_IsFormatSupported(IMediaSeeking *iface, const GUID *format) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + struct testfilter *filter = impl_from_IMediaSeeking(iface); + if (winetest_debug > 1) trace("IMediaSeeking_IsFormatSupported(%s)\n", wine_dbgstr_guid(format)); + + ok(filter->bitmap_grab_mode, "Unexpected call.\n"); + ok(IsEqualGUID(format, &TIME_FORMAT_MEDIA_TIME), "Unexpected format %s.\n", wine_dbgstr_guid(format)); + return S_OK; }
static HRESULT WINAPI testseek_QueryPreferredFormat(IMediaSeeking *iface, GUID *format) @@ -274,14 +403,21 @@ static HRESULT WINAPI testseek_QueryPreferredFormat(IMediaSeeking *iface, GUID *
static HRESULT WINAPI testseek_GetTimeFormat(IMediaSeeking *iface, GUID *format) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + struct testfilter *filter = impl_from_IMediaSeeking(iface); + if (winetest_debug > 1) trace("IMediaSeeking_GetTimeFormat()\n"); + + ok(filter->bitmap_grab_mode, "Unexpected call.\n"); + *format = *filter->time_format; + return S_OK; }
static HRESULT WINAPI testseek_IsUsingTimeFormat(IMediaSeeking *iface, const GUID *format) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + struct testfilter *filter = impl_from_IMediaSeeking(iface); + if (winetest_debug > 1) trace("IMediaSeeking_IsUsingTimeFormat(%s)\n", wine_dbgstr_guid(format)); + + ok(filter->bitmap_grab_mode, "Unexpected call.\n"); + return IsEqualGUID(format, filter->time_format) ? S_OK : S_FALSE; }
static HRESULT WINAPI testseek_SetTimeFormat(IMediaSeeking *iface, const GUID *format) @@ -320,8 +456,41 @@ static HRESULT WINAPI testseek_ConvertTimeFormat(IMediaSeeking *iface, LONGLONG static HRESULT WINAPI testseek_SetPositions(IMediaSeeking *iface, LONGLONG *current, DWORD current_flags, LONGLONG *stop, DWORD stop_flags) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + struct testfilter *filter = impl_from_IMediaSeeking(iface); + + if (winetest_debug > 1) + trace("IMediaSeeking_SetPositions(0x%s, %#x, 0x%s, %#x)\n", + wine_dbgstr_longlong(*current), current_flags, wine_dbgstr_longlong(*stop), stop_flags); + + if (filter->bitmap_grab_mode) + { + ok(current_flags == (AM_SEEKING_AbsolutePositioning | AM_SEEKING_ReturnTime), + "Unexpected current_flags %#x.\n", current_flags); + if (*stop != *current) + { + ok(!*stop, "Unexpected stop position: 0x%s.\n", wine_dbgstr_longlong(*stop)); + ok(!stop_flags, "Unexpected stop_flags %#x.\n", stop_flags); + } + else + ok(stop_flags == AM_SEEKING_AbsolutePositioning || (!*current && !stop_flags), + "Unexpected stop_flags %#x.\n", stop_flags); + + if (filter->thread) + { + IPin_BeginFlush(filter->source.pin.peer); + WaitForSingleObject(filter->thread, INFINITE); + CloseHandle(filter->thread); + filter->cur_pos = *current; + IPin_EndFlush(filter->source.pin.peer); + + filter->thread = CreateThread(NULL, 0, testfilter_frame_thread, filter, 0, NULL); + ok(filter->thread != NULL, "Failed to create thread: %#x.\n", GetLastError()); + } + else + filter->cur_pos = *current; + } + + return S_OK; }
static HRESULT WINAPI testseek_GetPositions(IMediaSeeking *iface, LONGLONG *current, LONGLONG *stop) @@ -386,6 +555,9 @@ static void testfilter_init(struct testfilter *filter) strmbase_filter_init(&filter->filter, NULL, &clsid, &testfilter_ops); strmbase_source_init(&filter->source, &filter->filter, L"", &testsource_ops); filter->IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + filter->cur_pos = 0xdeadbeef; + filter->majortype = &MEDIATYPE_Video; + filter->time_format = &TIME_FORMAT_MEDIA_TIME; }
static WCHAR test_avi_filename[MAX_PATH]; @@ -1117,6 +1289,231 @@ static void test_COM_sg_enumpins(void) IBaseFilter_Release(bf); }
+static void test_bitmap_grab_mode(void) +{ + static const GUID *const time_formats[] = + { + &TIME_FORMAT_NONE, + &TIME_FORMAT_FRAME, + &TIME_FORMAT_SAMPLE, + &TIME_FORMAT_FIELD, + &TIME_FORMAT_BYTE, + &TIME_FORMAT_MEDIA_TIME + }; + char *buf = malloc(640 * 480 * 3); + struct testfilter testfilter; + FILTER_INFO filter_info; + IReferenceClock *clock; + IBaseFilter *filter; + IMediaDet *detector; + ISampleGrabber *sg; + FILTER_STATE state; + PIN_INFO pin_info; + AM_MEDIA_TYPE mt; + IMediaFilter *mf; + LONG count, size; + IPin *pin, *pin2; + double duration; + IUnknown *unk; + unsigned i; + HRESULT hr; + ULONG ref; + GUID guid; + BSTR str; + + hr = CoCreateInstance(&CLSID_MediaDet, NULL, CLSCTX_INPROC_SERVER, + &IID_IMediaDet, (void **)&detector); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaDet_EnterBitmapGrabMode(detector, 0.0); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + hr = IMediaDet_GetSampleGrabber(detector, &sg); + todo_wine ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + + /* EnterBitmapGrabMode only works with Video major type */ + testfilter_init(&testfilter); + testfilter.majortype = &MEDIATYPE_Audio; + hr = IMediaDet_put_Filter(detector, &testfilter.filter.IUnknown_inner); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaDet_EnterBitmapGrabMode(detector, 0.0); + todo_wine ok(hr == VFW_E_INVALIDMEDIATYPE, "Got hr %#x.\n", hr); + + ref = IMediaDet_Release(detector); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IBaseFilter_Release(&testfilter.filter.IBaseFilter_iface); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + /* EnterBitmapGrabMode only seeks once, and if SeekTime is non-negative */ + hr = CoCreateInstance(&CLSID_MediaDet, NULL, CLSCTX_INPROC_SERVER, + &IID_IMediaDet, (void **)&detector); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + testfilter_init(&testfilter); + testfilter.bitmap_grab_mode = TRUE; + hr = IMediaDet_put_Filter(detector, &testfilter.filter.IUnknown_inner); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaDet_EnterBitmapGrabMode(detector, -1.0); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(testfilter.cur_pos == 0xdeadbeef, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos)); + hr = IMediaDet_EnterBitmapGrabMode(detector, 1.0); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(testfilter.cur_pos == 0xdeadbeef, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos)); + + ref = IMediaDet_Release(detector); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IBaseFilter_Release(&testfilter.filter.IBaseFilter_iface); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + /* Time formats other than TIME_FORMAT_MEDIA_TIME return E_NOTIMPL */ + for (i = 0; i < ARRAY_SIZE(time_formats); i++) + { + hr = CoCreateInstance(&CLSID_MediaDet, NULL, CLSCTX_INPROC_SERVER, + &IID_IMediaDet, (void **)&detector); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + testfilter_init(&testfilter); + testfilter.bitmap_grab_mode = TRUE; + testfilter.time_format = time_formats[i]; + hr = IMediaDet_put_Filter(detector, &testfilter.filter.IUnknown_inner); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaDet_EnterBitmapGrabMode(detector, 1337.0); + if (time_formats[i] == &TIME_FORMAT_MEDIA_TIME) + { + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine ok(testfilter.cur_pos == 13370000000LL, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos)); + hr = IMediaDet_EnterBitmapGrabMode(detector, 1.0); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine ok(testfilter.cur_pos == 13370000000LL, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos)); + } + else + ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr); + + ref = IMediaDet_Release(detector); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IBaseFilter_Release(&testfilter.filter.IBaseFilter_iface); + ok(!ref, "Got outstanding refcount %d.\n", ref); + } + + hr = CoCreateInstance(&CLSID_MediaDet, NULL, CLSCTX_INPROC_SERVER, + &IID_IMediaDet, (void **)&detector); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + testfilter_init(&testfilter); + testfilter.bitmap_grab_mode = TRUE; + hr = IMediaDet_put_Filter(detector, &testfilter.filter.IUnknown_inner); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaDet_EnterBitmapGrabMode(detector, 0.0); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine ok(testfilter.cur_pos == 0, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos)); + hr = IMediaDet_EnterBitmapGrabMode(detector, 1.0); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine ok(testfilter.cur_pos == 0, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos)); + + /* These still work */ + hr = IMediaDet_get_Filter(detector, &unk); + ok(hr == S_OK, "Got hr %#x.\n", hr); + IUnknown_Release(unk); + hr = IMediaDet_get_Filename(detector, &str); + ok(hr == S_OK, "Got hr %#x.\n", hr); + SysFreeString(str); + hr = IMediaDet_get_CurrentStream(detector, &count); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(count == 0, "Got stream %d.\n", count); + + /* These don't work anymore */ + hr = IMediaDet_get_OutputStreams(detector, &count); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + hr = IMediaDet_get_FrameRate(detector, &duration); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + hr = IMediaDet_get_StreamLength(detector, &duration); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + hr = IMediaDet_get_StreamMediaType(detector, &mt); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + if (SUCCEEDED(hr)) FreeMediaType(&mt); + hr = IMediaDet_get_StreamType(detector, &guid); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + hr = IMediaDet_get_StreamTypeB(detector, &str); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + if (SUCCEEDED(hr)) SysFreeString(str); + hr = IMediaDet_put_CurrentStream(detector, 0); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + + /* Check the SampleGrabber */ + hr = IMediaDet_GetSampleGrabber(detector, &sg); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + if (hr != S_OK) goto no_grabber; + hr = ISampleGrabber_GetConnectedMediaType(sg, &mt); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Video), "Got major type %s.\n", debugstr_guid(&mt.majortype)); + ok(IsEqualGUID(&mt.subtype, &MEDIASUBTYPE_RGB24), "Got sub type %s.\n", debugstr_guid(&mt.subtype)); + ok(IsEqualGUID(&mt.formattype, &FORMAT_VideoInfo), "Got format type %s.\n", debugstr_guid(&mt.formattype)); + FreeMediaType(&mt); + + hr = ISampleGrabber_GetCurrentBuffer(sg, &size, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(size == 640 * 480 * 3, "Got size %d.\n", size); + hr = ISampleGrabber_GetCurrentBuffer(sg, &size, (LONG*)buf); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(size == 640 * 480 * 3, "Got size %d.\n", size); + hr = ISampleGrabber_QueryInterface(sg, &IID_IBaseFilter, (void**)&filter); + ok(hr == S_OK, "QueryInterface for IID_IBaseFilter failed: %#x\n", hr); + ISampleGrabber_Release(sg); + + hr = IBaseFilter_QueryFilterInfo(filter, &filter_info); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(!wcscmp(filter_info.achName, L"BitBucket"), "Got name %s.\n", debugstr_w(filter_info.achName)); + IFilterGraph_Release(filter_info.pGraph); + hr = IBaseFilter_FindPin(filter, L"Out", &pin); + ok(hr == S_OK, "Got hr %#x.\n", hr); + IBaseFilter_Release(filter); + + hr = IPin_ConnectedTo(pin, &pin2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IPin_QueryPinInfo(pin2, &pin_info); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(pin_info.pFilter != NULL, "Got NULL filter.\n"); + IPin_Release(pin2); + IPin_Release(pin); + + hr = IBaseFilter_QueryFilterInfo(pin_info.pFilter, &filter_info); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(!wcscmp(filter_info.achName, L"NullRenderer"), "Got name %s.\n", debugstr_w(filter_info.achName)); + hr = IFilterGraph_QueryInterface(filter_info.pGraph, &IID_IMediaFilter, (void**)&mf); + ok(hr == S_OK, "QueryInterface for IID_IMediaFilter failed: %#x\n", hr); + IFilterGraph_Release(filter_info.pGraph); + IBaseFilter_Release(pin_info.pFilter); + + /* The graph should be paused */ + hr = IMediaFilter_GetState(mf, INFINITE, &state); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(state == State_Paused, "Got state %d.\n", state); + hr = IMediaFilter_GetSyncSource(mf, &clock); + ok(SUCCEEDED(hr), "Got hr %#x.\n", hr); + ok(clock == NULL, "Got non-NULL clock.\n"); + IMediaFilter_Release(mf); + +no_grabber: + /* Changing filter resets bitmap grab mode */ + testfilter.bitmap_grab_mode = FALSE; + hr = IMediaDet_put_Filter(detector, &testfilter.filter.IUnknown_inner); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaDet_GetSampleGrabber(detector, &sg); + todo_wine ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + hr = IMediaDet_get_OutputStreams(detector, &count); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(count == 1, "Got %d streams.\n", count); + + ref = IMediaDet_Release(detector); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IBaseFilter_Release(&testfilter.filter.IBaseFilter_iface); + ok(!ref, "Got outstanding refcount %d.\n", ref); + free(buf); +} + START_TEST(mediadet) { IMediaDet *detector; @@ -1145,6 +1542,7 @@ START_TEST(mediadet) test_put_filter(); test_samplegrabber(); test_COM_sg_enumpins(); + test_bitmap_grab_mode();
ret = DeleteFileW(test_avi_filename); ok(ret, "Failed to delete file, error %u.\n", GetLastError());
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/qedit/mediadet.c | 165 ++++++++++++++++++++++++++++++++++-- dlls/qedit/tests/mediadet.c | 40 +++++---- 2 files changed, 178 insertions(+), 27 deletions(-)
diff --git a/dlls/qedit/mediadet.c b/dlls/qedit/mediadet.c index bacf520..5eb6aef 100644 --- a/dlls/qedit/mediadet.c +++ b/dlls/qedit/mediadet.c @@ -40,6 +40,7 @@ typedef struct MediaDetImpl { IGraphBuilder *graph; IBaseFilter *source; IBaseFilter *splitter; + ISampleGrabber *grabber; WCHAR *filename; LONG num_streams; LONG cur_stream; @@ -64,6 +65,8 @@ static void MD_cleanup(MediaDetImpl *This) This->source = NULL; if (This->splitter) IBaseFilter_Release(This->splitter); This->splitter = NULL; + if (This->grabber) ISampleGrabber_Release(This->grabber); + This->grabber = NULL; if (This->graph) IGraphBuilder_Release(This->graph); This->graph = NULL; free(This->filename); @@ -224,6 +227,71 @@ next: return hr; }
+static HRESULT seek_graph(MediaDetImpl *detector, double seek_time) +{ + FILTER_STATE state; + LONGLONG pos, stop; + IMediaControl *mc; + IMediaSeeking *ms; + IMediaFilter *mf; + GUID format; + HRESULT hr; + + if (FAILED(hr = IPin_QueryInterface(detector->cur_pin, &IID_IMediaSeeking, (void**)&ms))) + return hr; + + if (IMediaSeeking_IsUsingTimeFormat(ms, &TIME_FORMAT_MEDIA_TIME) != S_OK && + (FAILED(IMediaSeeking_GetTimeFormat(ms, &format)) || + !IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME))) + { + /* Windows doesn't implement it */ + hr = E_NOTIMPL; + } + IMediaSeeking_Release(ms); + if (FAILED(hr)) + return hr; + + if (FAILED(hr = IGraphBuilder_QueryInterface(detector->graph, &IID_IMediaControl, (void**)&mc))) + return hr; + + if (FAILED(hr = IMediaControl_Stop(mc))) + goto done; + + if (seek_time >= 0.0) + { + if (FAILED(hr = IGraphBuilder_QueryInterface(detector->graph, &IID_IMediaSeeking, (void**)&ms))) + goto done; + + stop = pos = seek_time * 10000000.0; + hr = IMediaSeeking_SetPositions(ms, &pos, AM_SEEKING_AbsolutePositioning, + &stop, AM_SEEKING_AbsolutePositioning); + IMediaSeeking_Release(ms); + if (FAILED(hr)) + goto done; + } + + if (FAILED(hr = IGraphBuilder_QueryInterface(detector->graph, &IID_IMediaFilter, (void**)&mf))) + goto done; + hr = IMediaFilter_SetSyncSource(mf, NULL); + IMediaFilter_Release(mf); + if (FAILED(hr)) goto done; + + if (FAILED(hr = IMediaControl_Pause(mc))) + goto done; + + /* Testing on Windows shows it waits up to 37500 ms */ + if (FAILED(hr = IMediaControl_GetState(mc, 37500, (OAFilterState*)&state))) + { + if (hr == VFW_S_STATE_INTERMEDIATE) hr = VFW_E_TIME_EXPIRED; + goto done; + } + + hr = S_OK; +done: + IMediaControl_Release(mc); + return hr; +} + /* MediaDet inner IUnknown */ static HRESULT WINAPI MediaDet_inner_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) { @@ -371,7 +439,7 @@ static HRESULT WINAPI MediaDet_get_OutputStreams(IMediaDet* iface, LONG *pVal)
TRACE("(%p)\n", This);
- if (!This->splitter) + if (This->grabber || !This->splitter) return E_INVALIDARG;
if (This->num_streams != -1) @@ -466,6 +534,9 @@ static HRESULT WINAPI MediaDet_put_CurrentStream(IMediaDet* iface, LONG newVal)
TRACE("(%p)->(%d)\n", This, newVal);
+ if (This->grabber) + return E_INVALIDARG; + if (This->num_streams == -1) { LONG n; @@ -495,6 +566,8 @@ static HRESULT WINAPI MediaDet_get_StreamType(IMediaDet *iface, GUID *majortype)
if (!majortype) return E_POINTER; + if (detector->grabber) + return E_INVALIDARG;
if (SUCCEEDED(hr = IMediaDet_get_StreamMediaType(iface, &mt))) { @@ -533,7 +606,7 @@ static HRESULT WINAPI MediaDet_get_StreamLength(IMediaDet *iface, double *length if (!length) return E_POINTER;
- if (!detector->cur_pin) + if (detector->grabber || !detector->cur_pin) return E_INVALIDARG;
if (SUCCEEDED(hr = IPin_QueryInterface(detector->cur_pin, @@ -647,7 +720,7 @@ static HRESULT WINAPI MediaDet_get_StreamMediaType(IMediaDet* iface, if (!pVal) return E_POINTER;
- if (!This->cur_pin) + if (This->grabber || !This->cur_pin) return E_INVALIDARG;
return get_pin_media_type(This->cur_pin, pVal); @@ -672,6 +745,8 @@ static HRESULT WINAPI MediaDet_get_FrameRate(IMediaDet* iface, double *pVal)
if (!pVal) return E_POINTER; + if (This->grabber) + return E_INVALIDARG;
hr = MediaDet_get_StreamMediaType(iface, &mt); if (FAILED(hr)) @@ -693,9 +768,87 @@ static HRESULT WINAPI MediaDet_get_FrameRate(IMediaDet* iface, double *pVal) static HRESULT WINAPI MediaDet_EnterBitmapGrabMode(IMediaDet* iface, double SeekTime) { - MediaDetImpl *This = impl_from_IMediaDet(iface); - FIXME("(%p)->(%f): not implemented!\n", This, SeekTime); - return E_NOTIMPL; + MediaDetImpl *detector = impl_from_IMediaDet(iface); + IPin *sg_inpin, *sg_outpin, *null_pin; + IBaseFilter *sg_filter, *null_filter; + ISampleGrabber *sg; + AM_MEDIA_TYPE mt; + GUID major_type; + IUnknown *unk; + HRESULT hr; + + TRACE("(%p)->(%.16e)\n", detector, SeekTime); + + if (detector->grabber) + return S_OK; + if (!detector->cur_pin) + return E_INVALIDARG; + if (FAILED(hr = get_pin_media_type(detector->cur_pin, &mt))) + return hr; + major_type = mt.majortype; + FreeMediaType(&mt); + if (!IsEqualGUID(&major_type, &MEDIATYPE_Video)) + return VFW_E_INVALIDMEDIATYPE; + + hr = sample_grabber_create(NULL, &unk); + if (FAILED(hr)) return hr; + IUnknown_QueryInterface(unk, &IID_IBaseFilter, (void**)&sg_filter); + IUnknown_Release(unk); + + hr = null_renderer_create(NULL, &unk); + if (FAILED(hr)) + { + IBaseFilter_Release(sg_filter); + return hr; + } + IUnknown_QueryInterface(unk, &IID_IBaseFilter, (void**)&null_filter); + IUnknown_Release(unk); + + memset(&mt, 0, sizeof(mt)); + mt.majortype = MEDIATYPE_Video; + mt.subtype = MEDIASUBTYPE_RGB24; + mt.formattype = FORMAT_VideoInfo; + + IBaseFilter_QueryInterface(sg_filter, &IID_ISampleGrabber, (void**)&sg); + ISampleGrabber_SetMediaType(sg, &mt); + ISampleGrabber_SetBufferSamples(sg, TRUE); + + IBaseFilter_FindPin(sg_filter, L"In", &sg_inpin); + IBaseFilter_FindPin(sg_filter, L"Out", &sg_outpin); + IBaseFilter_FindPin(null_filter, L"In", &null_pin); + + if (FAILED(hr = IGraphBuilder_AddFilter(detector->graph, sg_filter, L"BitBucket"))) + { + ISampleGrabber_Release(sg); + goto done; + } + + if (FAILED(hr = IGraphBuilder_AddFilter(detector->graph, null_filter, L"NullRenderer"))) + { + IGraphBuilder_RemoveFilter(detector->graph, sg_filter); + ISampleGrabber_Release(sg); + goto done; + } + + if (FAILED(hr = IGraphBuilder_Connect(detector->graph, detector->cur_pin, sg_inpin)) || + FAILED(hr = IGraphBuilder_ConnectDirect(detector->graph, sg_outpin, null_pin, NULL)) || + FAILED(hr = seek_graph(detector, SeekTime))) + { + IGraphBuilder_RemoveFilter(detector->graph, null_filter); + IGraphBuilder_RemoveFilter(detector->graph, sg_filter); + ISampleGrabber_Release(sg); + goto done; + } + + detector->grabber = sg; + +done: + IPin_Release(null_pin); + IPin_Release(sg_outpin); + IPin_Release(sg_inpin); + IBaseFilter_Release(null_filter); + IBaseFilter_Release(sg_filter); + return hr; }
static const IMediaDetVtbl IMediaDet_VTable = diff --git a/dlls/qedit/tests/mediadet.c b/dlls/qedit/tests/mediadet.c index 0a05a65..71d4a4b 100644 --- a/dlls/qedit/tests/mediadet.c +++ b/dlls/qedit/tests/mediadet.c @@ -1326,7 +1326,7 @@ static void test_bitmap_grab_mode(void) ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaDet_EnterBitmapGrabMode(detector, 0.0); - todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); hr = IMediaDet_GetSampleGrabber(detector, &sg); todo_wine ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
@@ -1337,7 +1337,7 @@ static void test_bitmap_grab_mode(void) ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaDet_EnterBitmapGrabMode(detector, 0.0); - todo_wine ok(hr == VFW_E_INVALIDMEDIATYPE, "Got hr %#x.\n", hr); + ok(hr == VFW_E_INVALIDMEDIATYPE, "Got hr %#x.\n", hr);
ref = IMediaDet_Release(detector); ok(!ref, "Got outstanding refcount %d.\n", ref); @@ -1355,10 +1355,10 @@ static void test_bitmap_grab_mode(void) ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaDet_EnterBitmapGrabMode(detector, -1.0); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(hr == S_OK, "Got hr %#x.\n", hr); ok(testfilter.cur_pos == 0xdeadbeef, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos)); hr = IMediaDet_EnterBitmapGrabMode(detector, 1.0); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(hr == S_OK, "Got hr %#x.\n", hr); ok(testfilter.cur_pos == 0xdeadbeef, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos));
ref = IMediaDet_Release(detector); @@ -1382,11 +1382,11 @@ static void test_bitmap_grab_mode(void) hr = IMediaDet_EnterBitmapGrabMode(detector, 1337.0); if (time_formats[i] == &TIME_FORMAT_MEDIA_TIME) { - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - todo_wine ok(testfilter.cur_pos == 13370000000LL, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos)); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(testfilter.cur_pos == 13370000000LL, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos)); hr = IMediaDet_EnterBitmapGrabMode(detector, 1.0); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - todo_wine ok(testfilter.cur_pos == 13370000000LL, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos)); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(testfilter.cur_pos == 13370000000LL, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos)); } else ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr); @@ -1407,11 +1407,11 @@ static void test_bitmap_grab_mode(void) ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaDet_EnterBitmapGrabMode(detector, 0.0); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - todo_wine ok(testfilter.cur_pos == 0, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos)); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(testfilter.cur_pos == 0, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos)); hr = IMediaDet_EnterBitmapGrabMode(detector, 1.0); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - todo_wine ok(testfilter.cur_pos == 0, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos)); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(testfilter.cur_pos == 0, "Current position was set to 0x%s.\n", wine_dbgstr_longlong(testfilter.cur_pos));
/* These still work */ hr = IMediaDet_get_Filter(detector, &unk); @@ -1426,21 +1426,19 @@ static void test_bitmap_grab_mode(void)
/* These don't work anymore */ hr = IMediaDet_get_OutputStreams(detector, &count); - todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); hr = IMediaDet_get_FrameRate(detector, &duration); - todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); hr = IMediaDet_get_StreamLength(detector, &duration); - todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); hr = IMediaDet_get_StreamMediaType(detector, &mt); - todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); - if (SUCCEEDED(hr)) FreeMediaType(&mt); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); hr = IMediaDet_get_StreamType(detector, &guid); - todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); hr = IMediaDet_get_StreamTypeB(detector, &str); - todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); - if (SUCCEEDED(hr)) SysFreeString(str); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); hr = IMediaDet_put_CurrentStream(detector, 0); - todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
/* Check the SampleGrabber */ hr = IMediaDet_GetSampleGrabber(detector, &sg);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/qedit/mediadet.c | 13 ++++++++++--- dlls/qedit/tests/mediadet.c | 8 +++----- 2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/dlls/qedit/mediadet.c b/dlls/qedit/mediadet.c index 5eb6aef..6a9b000 100644 --- a/dlls/qedit/mediadet.c +++ b/dlls/qedit/mediadet.c @@ -729,9 +729,16 @@ static HRESULT WINAPI MediaDet_get_StreamMediaType(IMediaDet* iface, static HRESULT WINAPI MediaDet_GetSampleGrabber(IMediaDet* iface, ISampleGrabber **ppVal) { - MediaDetImpl *This = impl_from_IMediaDet(iface); - FIXME("(%p)->(%p): not implemented!\n", This, ppVal); - return E_NOTIMPL; + MediaDetImpl *detector = impl_from_IMediaDet(iface); + + TRACE("(%p)->(%p)\n", detector, ppVal); + + if (!detector->grabber) + return E_NOINTERFACE; + + *ppVal = detector->grabber; + ISampleGrabber_AddRef(*ppVal); + return S_OK; }
static HRESULT WINAPI MediaDet_get_FrameRate(IMediaDet* iface, double *pVal) diff --git a/dlls/qedit/tests/mediadet.c b/dlls/qedit/tests/mediadet.c index 71d4a4b..18a4412 100644 --- a/dlls/qedit/tests/mediadet.c +++ b/dlls/qedit/tests/mediadet.c @@ -1328,7 +1328,7 @@ static void test_bitmap_grab_mode(void) hr = IMediaDet_EnterBitmapGrabMode(detector, 0.0); ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); hr = IMediaDet_GetSampleGrabber(detector, &sg); - todo_wine ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
/* EnterBitmapGrabMode only works with Video major type */ testfilter_init(&testfilter); @@ -1442,8 +1442,7 @@ static void test_bitmap_grab_mode(void)
/* Check the SampleGrabber */ hr = IMediaDet_GetSampleGrabber(detector, &sg); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - if (hr != S_OK) goto no_grabber; + ok(hr == S_OK, "Got hr %#x.\n", hr); hr = ISampleGrabber_GetConnectedMediaType(sg, &mt); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Video), "Got major type %s.\n", debugstr_guid(&mt.majortype)); @@ -1494,13 +1493,12 @@ static void test_bitmap_grab_mode(void) ok(clock == NULL, "Got non-NULL clock.\n"); IMediaFilter_Release(mf);
-no_grabber: /* Changing filter resets bitmap grab mode */ testfilter.bitmap_grab_mode = FALSE; hr = IMediaDet_put_Filter(detector, &testfilter.filter.IUnknown_inner); ok(hr == S_OK, "Got hr %#x.\n", hr); hr = IMediaDet_GetSampleGrabber(detector, &sg); - todo_wine ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); hr = IMediaDet_get_OutputStreams(detector, &count); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(count == 1, "Got %d streams.\n", count);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/qedit/tests/mediadet.c | 220 +++++++++++++++++++++++++++++++++++- 1 file changed, 218 insertions(+), 2 deletions(-)
diff --git a/dlls/qedit/tests/mediadet.c b/dlls/qedit/tests/mediadet.c index 18a4412..8c5ef4b 100644 --- a/dlls/qedit/tests/mediadet.c +++ b/dlls/qedit/tests/mediadet.c @@ -170,7 +170,9 @@ static DWORD WINAPI testfilter_frame_thread(void *arg) struct testfilter *filter = arg; IMemAllocator *allocator; IMediaSample *sample; + unsigned i; HRESULT hr; + DWORD fill; BYTE *data;
hr = IMemInputPin_GetAllocator(filter->source.pMemInputPin, &allocator); @@ -190,7 +192,13 @@ static DWORD WINAPI testfilter_frame_thread(void *arg) hr = IMediaSample_GetPointer(sample, &data); ok(hr == S_OK, "Got hr %#x.\n", hr);
- memset(data, 0, 640 * 480 * 3); + fill = (start_time / 10000 & 0xffffff) ^ 0xccaabb; + for (i = 0; i < 640 * 480 * 3; i += 3) + { + data[i] = fill ^ i; + data[i + 1] = fill >> 8 ^ i; + data[i + 2] = fill >> 16 ^ i; + }
hr = IMediaSample_SetActualDataLength(sample, 640 * 480 * 3); ok(hr == S_OK, "Got hr %#x.\n", hr); @@ -609,6 +617,157 @@ static BOOL unpack_avi_file(int id, WCHAR name[MAX_PATH]) return ret && written == size; }
+static void stretch_line(BYTE *dst, ULONG dst_width, BYTE *src, ULONG src_width) +{ + ULONG ratio, rem, drift, i = dst_width; + + if (dst_width < src_width) + { + ratio = src_width / dst_width; + rem = src_width % dst_width; + drift = 0; + while (i--) + { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst += 3; + src += ratio * 3; + if (drift < rem) + { + src += 3; + drift += dst_width; + } + drift -= rem; + } + } + else if (dst_width > src_width) + { + drift = dst_width - 1; + while (i--) + { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst += 3; + if (drift < src_width) + { + drift += dst_width; + src += 3; + } + drift -= src_width; + } + } + else + { + memcpy(dst, src, dst_width * 3); + dst += dst_width * 3; + } + + /* Fill the stride padding with zeros */ + i = (dst_width * 3) % 4; + if (i) + for (i = 4 - i; i--;) + *dst++ = 0; +} + +static void stretch_to_bitmap(BYTE *dst, ULONG dst_width, ULONG dst_height, BYTE *src, ULONG src_width, ULONG src_height) +{ + ULONG dst_stride = (dst_width * 3 + 3) & ~3; + ULONG src_stride = (src_width * 3 + 3) & ~3; + ULONG ratio, rem, drift, i = dst_height; + BYTE *dup; + + if (dst_height < src_height) + { + ratio = src_height / dst_height; + rem = src_height % dst_height; + drift = 0; + while (i--) + { + stretch_line(dst, dst_width, src, src_width); + dst += dst_stride; + src += ratio * src_stride; + if (drift < rem) + { + src += src_stride; + drift += dst_height; + } + drift -= rem; + } + } + else + { + drift = dst_height - 1; + while (i--) + { + stretch_line(dst, dst_width, src, src_width); + dup = dst; + dst += dst_stride; + while (drift >= src_height && i--) + { + memcpy(dst, dup, dst_stride); + dst += dst_stride; + drift -= src_height; + } + drift += dst_height - src_height; + src += src_stride; + } + } +} + +#define check_bitmap(buffer, width, height, seek_time) check_bitmap_(__LINE__, buffer, width, height, seek_time) +static void check_bitmap_(unsigned line, void *buffer, LONG width, LONG height, double seek_time) +{ + DWORD fill = (DWORD)(seek_time * 1000.0) ^ 0xccaabb; + BYTE *p = (BYTE*)buffer + sizeof(BITMAPINFOHEADER); + DWORD line_width = (width * 3 + 3) & ~3; + BYTE *img, *src = malloc(640 * 480 * 3); + const BITMAPINFOHEADER *h = buffer; + unsigned i, j, v; + + ok_(__FILE__,line)(h->biSize == sizeof(BITMAPINFOHEADER), "Got biSize %u.\n", h->biSize); + ok_(__FILE__,line)(h->biWidth == width, "Got biWidth %d.\n", h->biWidth); + ok_(__FILE__,line)(h->biHeight == height, "Got biHeight %d.\n", h->biHeight); + ok_(__FILE__,line)(h->biPlanes == 1, "Got biPlanes %d.\n", h->biPlanes); + ok_(__FILE__,line)(h->biBitCount == 24, "Got biBitCount %d.\n", h->biBitCount); + ok_(__FILE__,line)(h->biCompression == BI_RGB, "Got biCompression %d.\n", h->biCompression); + ok_(__FILE__,line)(h->biXPelsPerMeter == 0, "Got biXPelsPerMeter %d.\n", h->biXPelsPerMeter); + ok_(__FILE__,line)(h->biYPelsPerMeter == 0, "Got biYPelsPerMeter %d.\n", h->biYPelsPerMeter); + ok_(__FILE__,line)(h->biClrUsed == 0, "Got biClrUsed %d.\n", h->biClrUsed); + ok_(__FILE__,line)(h->biClrImportant == 0, "Got biClrImportant %d.\n", h->biClrImportant); + + /* The lines are reversed since our source was top-down */ + src = malloc(640 * 480 * 3); + img = src; + for (j = 640 * 480 * 3; j != 0;) + { + j -= 640 * 3; + v = j; + for (i = 0; i < 640 * 3; i += 3, v += 3) + { + img[i] = fill ^ v; + img[i + 1] = fill >> 8 ^ v; + img[i + 2] = fill >> 16 ^ v; + } + img += 640 * 3; + } + + img = malloc(line_width * height); + stretch_to_bitmap(img, width, height, src, 640, 480); + free(src); + + for (i = 0; i < line_width * height; i += 3) + if (p[i] != img[i] || p[i + 1] != img[i + 1] || p[i + 2] != img[i + 2]) + { + ok_(__FILE__,line)(0, "Wrong bitmap data at offset %u (got 0x%06x, expected 0x%06x).\n", + p + i - (BYTE*)buffer, p[i] | p[i + 1] << 8 | p[i + 2] << 16, + img[i] | img[i + 1] << 8 | img[i + 2] << 16); + break; + } + free(img); +} + static BOOL init_tests(void) { return unpack_avi_file(TEST_AVI_RES, test_avi_filename) @@ -1300,7 +1459,7 @@ static void test_bitmap_grab_mode(void) &TIME_FORMAT_BYTE, &TIME_FORMAT_MEDIA_TIME }; - char *buf = malloc(640 * 480 * 3); + char *buf = malloc(sizeof(BITMAPINFOHEADER) + 960 * 720 * 3); struct testfilter testfilter; FILTER_INFO filter_info; IReferenceClock *clock; @@ -1327,6 +1486,9 @@ static void test_bitmap_grab_mode(void)
hr = IMediaDet_EnterBitmapGrabMode(detector, 0.0); ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + size = sizeof(BITMAPINFOHEADER) + 640 * 480 * 3; + hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, buf, 640, 480); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); hr = IMediaDet_GetSampleGrabber(detector, &sg); ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
@@ -1493,6 +1655,42 @@ static void test_bitmap_grab_mode(void) ok(clock == NULL, "Got non-NULL clock.\n"); IMediaFilter_Release(mf);
+ /* Despite what MSDN states, size must be properly supplied on newer Windows versions */ + hr = IMediaDet_GetBitmapBits(detector, 0.0, NULL, NULL, 640, 480); + todo_wine ok(hr == E_POINTER, "Got hr %#x.\n", hr); + size = -1; + hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, buf, 640, 480); + todo_wine ok(hr == E_INVALIDARG || broken(hr == S_OK) /* WinXP */, "Got hr %#x.\n", hr); + size = 640 * 480 * 3; + hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, buf, 640, 480); + todo_wine ok(hr == E_OUTOFMEMORY || broken(hr == S_OK) /* WinXP */, "Got hr %#x.\n", hr); + ok(size == 640 * 480 * 3, "Got size %d.\n", size); + size = sizeof(BITMAPINFOHEADER) + 640 * 480 * 3; + hr = IMediaDet_GetBitmapBits(detector, -1.0, &size, buf, 640, 480); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + hr = IMediaDet_GetBitmapBits(detector, 2.5, &size, buf, 640, 480); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine check_bitmap(buf, 640, 480, 2.5); + + /* GetBitmapBits/WriteBitmapBits can scale the image */ + size = sizeof(BITMAPINFOHEADER) + 960 * 720 * 3; + hr = IMediaDet_GetBitmapBits(detector, 1.5, &size, buf, 131, 151); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(size == sizeof(BITMAPINFOHEADER) + 960 * 720 * 3, "Got size %d.\n", size); + todo_wine check_bitmap(buf, 131, 151, 1.5); + hr = IMediaDet_GetBitmapBits(detector, 4.0, NULL, buf, 503, 79); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine check_bitmap(buf, 503, 79, 4.0); + hr = IMediaDet_GetBitmapBits(detector, 1.52, NULL, buf, 139, 487); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine check_bitmap(buf, 139, 487, 1.52); + hr = IMediaDet_GetBitmapBits(detector, 2.12, NULL, buf, 640, 641); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine check_bitmap(buf, 640, 641, 2.12); + hr = IMediaDet_GetBitmapBits(detector, 3.25, NULL, buf, 960, 720); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine check_bitmap(buf, 960, 720, 3.25); + /* Changing filter resets bitmap grab mode */ testfilter.bitmap_grab_mode = FALSE; hr = IMediaDet_put_Filter(detector, &testfilter.filter.IUnknown_inner); @@ -1503,6 +1701,24 @@ static void test_bitmap_grab_mode(void) ok(hr == S_OK, "Got hr %#x.\n", hr); ok(count == 1, "Got %d streams.\n", count);
+ /* GetBitmapBits enables it only if it retrieves the image */ + testfilter.bitmap_grab_mode = TRUE; + hr = IMediaDet_GetBitmapBits(detector, 1.25, &size, NULL, 640, 480); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaDet_GetSampleGrabber(detector, &sg); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + hr = IMediaDet_get_OutputStreams(detector, &count); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(count == 1, "Got %d streams.\n", count); + + hr = IMediaDet_GetBitmapBits(detector, 1.25, NULL, buf, 640, 480); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaDet_GetSampleGrabber(detector, &sg); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + if (SUCCEEDED(hr)) ISampleGrabber_Release(sg); + hr = IMediaDet_get_OutputStreams(detector, &count); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + ref = IMediaDet_Release(detector); ok(!ref, "Got outstanding refcount %d.\n", ref); ref = IBaseFilter_Release(&testfilter.filter.IBaseFilter_iface);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/qedit/mediadet.c | 191 +++++++++++++++++++++++++++++++++++- dlls/qedit/tests/mediadet.c | 57 ++++++----- 2 files changed, 222 insertions(+), 26 deletions(-)
diff --git a/dlls/qedit/mediadet.c b/dlls/qedit/mediadet.c index 6a9b000..acf659b 100644 --- a/dlls/qedit/mediadet.c +++ b/dlls/qedit/mediadet.c @@ -292,6 +292,60 @@ done: return hr; }
+static void stretch_line(BYTE *dst, ULONG dst_width, BYTE *src, ULONG src_width) +{ + ULONG ratio, rem, drift, i = dst_width; + + if (dst_width < src_width) + { + ratio = src_width / dst_width; + rem = src_width % dst_width; + drift = 0; + while (i--) + { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst += 3; + src += ratio * 3; + if (drift < rem) + { + src += 3; + drift += dst_width; + } + drift -= rem; + } + } + else if (dst_width > src_width) + { + drift = dst_width - 1; + while (i--) + { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst += 3; + if (drift < src_width) + { + drift += dst_width; + src += 3; + } + drift -= src_width; + } + } + else + { + memcpy(dst, src, dst_width * 3); + dst += dst_width * 3; + } + + /* Fill the stride padding with zeros */ + i = (dst_width * 3) % 4; + if (i) + for (i = 4 - i; i--;) + *dst++ = 0; +} + /* MediaDet inner IUnknown */ static HRESULT WINAPI MediaDet_inner_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) { @@ -695,10 +749,139 @@ static HRESULT WINAPI MediaDet_GetBitmapBits(IMediaDet* iface, LONG *pBufferSize, char *pBuffer, LONG Width, LONG Height) { - MediaDetImpl *This = impl_from_IMediaDet(iface); - FIXME("(%p)->(%f %p %p %d %d): not implemented!\n", This, StreamTime, pBufferSize, pBuffer, - Width, Height); - return E_NOTIMPL; + MediaDetImpl *detector = impl_from_IMediaDet(iface); + LONG ratio, rem, stride = (Width * 3 + 3) & ~3; + LONG src_x, src_y, src_stride, size, buf_size; + ULONG src_width, src_height, drift, i; + BITMAPINFOHEADER hdr = { 0 }; + BYTE *buf, *dup, *dst, *src; + VIDEOINFOHEADER *info; + AM_MEDIA_TYPE mt; + HRESULT hr; + + TRACE("(%p)->(%.16e %p %p %d %d)\n", detector, StreamTime, pBufferSize, pBuffer, Width, Height); + + if (!pBuffer && !pBufferSize) return E_POINTER; + if (Width < 0 || Height < 0) return E_INVALIDARG; + if (!pBuffer) + { + *pBufferSize = sizeof(BITMAPINFOHEADER) + stride * Height; + return S_OK; + } + if (StreamTime < 0.0) return E_INVALIDARG; + if (pBufferSize) + { + if (*pBufferSize < 0) + return E_INVALIDARG; + if (*pBufferSize < sizeof(BITMAPINFOHEADER) + stride * Height) + return E_OUTOFMEMORY; + } + + if (detector->grabber) + hr = seek_graph(detector, StreamTime); + else + hr = IMediaDet_EnterBitmapGrabMode(&detector->IMediaDet_iface, StreamTime); + if (FAILED(hr)) + return hr; + + hr = ISampleGrabber_GetConnectedMediaType(detector->grabber, &mt); + if (FAILED(hr)) return hr; + + info = (VIDEOINFOHEADER*)mt.pbFormat; + if (!IsEqualGUID(&mt.majortype, &MEDIATYPE_Video) || + !IsEqualGUID(&mt.subtype, &MEDIASUBTYPE_RGB24) || + !IsEqualGUID(&mt.formattype, &FORMAT_VideoInfo) || + mt.cbFormat != sizeof(VIDEOINFOHEADER) || + info->bmiHeader.biBitCount != 24 || + info->bmiHeader.biCompression != BI_RGB) + { + FreeMediaType(&mt); + return VFW_E_INVALID_MEDIA_TYPE; + } + + src_x = src_y = 0; + src_width = info->bmiHeader.biWidth; + src_height = abs(info->bmiHeader.biHeight); + src_stride = (src_width * 3 + 3) & ~3; + buf_size = src_stride * src_height; + if (!IsRectEmpty(&info->rcTarget)) + { + src_x = max(info->rcTarget.left, 0); + src_y = max(info->rcTarget.top, 0); + src_width = min(info->rcTarget.right - src_x, src_width); + src_height = min(info->rcTarget.bottom - src_y, src_height); + } + if (info->bmiHeader.biHeight < 0) + { + src_y += info->bmiHeader.biHeight + 1; + src_stride = -src_stride; + } + FreeMediaType(&mt); + + if (!(buf = HeapAlloc(GetProcessHeap(), 0, buf_size))) + return E_OUTOFMEMORY; + size = buf_size; + hr = ISampleGrabber_GetCurrentBuffer(detector->grabber, &size, (LONG*)buf); + if (FAILED(hr)) goto err; + if (size != buf_size) + { + hr = E_UNEXPECTED; + goto err; + } + + hdr.biSize = sizeof(BITMAPINFOHEADER); + hdr.biWidth = Width; + hdr.biHeight = Height; + hdr.biPlanes = 1; + hdr.biBitCount = 24; + hdr.biCompression = BI_RGB; + memcpy(pBuffer, &hdr, sizeof(BITMAPINFOHEADER)); + + /* Copy and potentially stretch the image (differently than StretchBlt) */ + dst = (BYTE*)pBuffer + sizeof(BITMAPINFOHEADER); + src = buf + src_x * 3 + src_y * src_stride; + i = Height; + if (Height < src_height) + { + ratio = src_height / Height; + rem = src_height % Height; + drift = 0; + while (i--) + { + stretch_line(dst, Width, src, src_width); + dst += stride; + src += ratio * src_stride; + if (drift < rem) + { + src += src_stride; + drift += Height; + } + drift -= rem; + } + } + else + { + drift = Height - 1; + while (i--) + { + stretch_line(dst, Width, src, src_width); + dup = dst; + dst += stride; + while (drift >= src_height && i--) + { + memcpy(dst, dup, stride); + dst += stride; + drift -= src_height; + } + drift += Height - src_height; + src += src_stride; + } + } + + hr = S_OK; +err: + HeapFree(GetProcessHeap(), 0, buf); + return hr; }
static HRESULT WINAPI MediaDet_WriteBitmapBits(IMediaDet* iface, diff --git a/dlls/qedit/tests/mediadet.c b/dlls/qedit/tests/mediadet.c index 8c5ef4b..9e88d0a 100644 --- a/dlls/qedit/tests/mediadet.c +++ b/dlls/qedit/tests/mediadet.c @@ -1488,10 +1488,23 @@ static void test_bitmap_grab_mode(void) ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); size = sizeof(BITMAPINFOHEADER) + 640 * 480 * 3; hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, buf, 640, 480); - todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); hr = IMediaDet_GetSampleGrabber(detector, &sg); ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
+ /* Lines are rounded up to the bitmap stride */ + hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, NULL, 640, 480); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(size == sizeof(BITMAPINFOHEADER) + 640 * 480 * 3, "Got size %d.\n", size); + hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, NULL, 640, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(size == sizeof(BITMAPINFOHEADER), "Got size %d.\n", size); + hr = IMediaDet_GetBitmapBits(detector, -1.0, &size, NULL, 151, 131); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(size == sizeof(BITMAPINFOHEADER) + ((151 * 3 + 3) & ~3) * 131, "Got size %d.\n", size); + hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, NULL, -59, -79); + ok(hr == E_INVALIDARG || broken(hr == S_OK) /* WinXP */, "Got hr %#x.\n", hr); + /* EnterBitmapGrabMode only works with Video major type */ testfilter_init(&testfilter); testfilter.majortype = &MEDIATYPE_Audio; @@ -1657,39 +1670,39 @@ static void test_bitmap_grab_mode(void)
/* Despite what MSDN states, size must be properly supplied on newer Windows versions */ hr = IMediaDet_GetBitmapBits(detector, 0.0, NULL, NULL, 640, 480); - todo_wine ok(hr == E_POINTER, "Got hr %#x.\n", hr); + ok(hr == E_POINTER, "Got hr %#x.\n", hr); size = -1; hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, buf, 640, 480); - todo_wine ok(hr == E_INVALIDARG || broken(hr == S_OK) /* WinXP */, "Got hr %#x.\n", hr); + ok(hr == E_INVALIDARG || broken(hr == S_OK) /* WinXP */, "Got hr %#x.\n", hr); size = 640 * 480 * 3; hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, buf, 640, 480); - todo_wine ok(hr == E_OUTOFMEMORY || broken(hr == S_OK) /* WinXP */, "Got hr %#x.\n", hr); + ok(hr == E_OUTOFMEMORY || broken(hr == S_OK) /* WinXP */, "Got hr %#x.\n", hr); ok(size == 640 * 480 * 3, "Got size %d.\n", size); size = sizeof(BITMAPINFOHEADER) + 640 * 480 * 3; hr = IMediaDet_GetBitmapBits(detector, -1.0, &size, buf, 640, 480); - todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); hr = IMediaDet_GetBitmapBits(detector, 2.5, &size, buf, 640, 480); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - todo_wine check_bitmap(buf, 640, 480, 2.5); + ok(hr == S_OK, "Got hr %#x.\n", hr); + check_bitmap(buf, 640, 480, 2.5);
/* GetBitmapBits/WriteBitmapBits can scale the image */ size = sizeof(BITMAPINFOHEADER) + 960 * 720 * 3; hr = IMediaDet_GetBitmapBits(detector, 1.5, &size, buf, 131, 151); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(hr == S_OK, "Got hr %#x.\n", hr); ok(size == sizeof(BITMAPINFOHEADER) + 960 * 720 * 3, "Got size %d.\n", size); - todo_wine check_bitmap(buf, 131, 151, 1.5); + check_bitmap(buf, 131, 151, 1.5); hr = IMediaDet_GetBitmapBits(detector, 4.0, NULL, buf, 503, 79); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - todo_wine check_bitmap(buf, 503, 79, 4.0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + check_bitmap(buf, 503, 79, 4.0); hr = IMediaDet_GetBitmapBits(detector, 1.52, NULL, buf, 139, 487); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - todo_wine check_bitmap(buf, 139, 487, 1.52); + ok(hr == S_OK, "Got hr %#x.\n", hr); + check_bitmap(buf, 139, 487, 1.52); hr = IMediaDet_GetBitmapBits(detector, 2.12, NULL, buf, 640, 641); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - todo_wine check_bitmap(buf, 640, 641, 2.12); + ok(hr == S_OK, "Got hr %#x.\n", hr); + check_bitmap(buf, 640, 641, 2.12); hr = IMediaDet_GetBitmapBits(detector, 3.25, NULL, buf, 960, 720); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - todo_wine check_bitmap(buf, 960, 720, 3.25); + ok(hr == S_OK, "Got hr %#x.\n", hr); + check_bitmap(buf, 960, 720, 3.25);
/* Changing filter resets bitmap grab mode */ testfilter.bitmap_grab_mode = FALSE; @@ -1704,7 +1717,7 @@ static void test_bitmap_grab_mode(void) /* GetBitmapBits enables it only if it retrieves the image */ testfilter.bitmap_grab_mode = TRUE; hr = IMediaDet_GetBitmapBits(detector, 1.25, &size, NULL, 640, 480); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(hr == S_OK, "Got hr %#x.\n", hr); hr = IMediaDet_GetSampleGrabber(detector, &sg); ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); hr = IMediaDet_get_OutputStreams(detector, &count); @@ -1712,12 +1725,12 @@ static void test_bitmap_grab_mode(void) ok(count == 1, "Got %d streams.\n", count);
hr = IMediaDet_GetBitmapBits(detector, 1.25, NULL, buf, 640, 480); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(hr == S_OK, "Got hr %#x.\n", hr); hr = IMediaDet_GetSampleGrabber(detector, &sg); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - if (SUCCEEDED(hr)) ISampleGrabber_Release(sg); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ISampleGrabber_Release(sg); hr = IMediaDet_get_OutputStreams(detector, &count); - todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
ref = IMediaDet_Release(detector); ok(!ref, "Got outstanding refcount %d.\n", ref);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/qedit/tests/mediadet.c | 92 +++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+)
diff --git a/dlls/qedit/tests/mediadet.c b/dlls/qedit/tests/mediadet.c index 9e88d0a..dba9d56 100644 --- a/dlls/qedit/tests/mediadet.c +++ b/dlls/qedit/tests/mediadet.c @@ -768,6 +768,45 @@ static void check_bitmap_(unsigned line, void *buffer, LONG width, LONG height, free(img); }
+#define check_bitmap_file(filename, buffer, width, height, seek_time) check_bitmap_file_(__LINE__, filename, buffer, width, height, seek_time) +static void check_bitmap_file_(unsigned line, const WCHAR *filename, void *buffer, LONG width, LONG height, double seek_time) +{ + DWORD size_high, read, expected; + BITMAPFILEHEADER *hdr; + DWORD64 size; + HANDLE file; + + /* WriteBitmapBits rounds the width up to a multiple of 4 */ + width = (width + 3) & ~3; + expected = sizeof(BITMAPINFOHEADER) + width * 3 * height; + + file = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + ok_(__FILE__,line)(file != INVALID_HANDLE_VALUE, "CreateFile failed, error: %#x.\n", GetLastError()); + if (file == INVALID_HANDLE_VALUE) return; + + size = GetFileSize(file, &size_high); + size |= (DWORD64)size_high << 32; + ok_(__FILE__,line)(GetLastError() == NO_ERROR, "GetFileSize failed, error: %#x.\n", GetLastError()); + ok_(__FILE__,line)(size == expected + sizeof(BITMAPFILEHEADER), "Wrong size 0x%s (expected 0x%x).\n", + wine_dbgstr_longlong(size), expected); + + ok_(__FILE__,line)(ReadFile(file, buffer, sizeof(BITMAPFILEHEADER), &read, NULL), + "ReadFile failed, error: %#x.\n", GetLastError()); + ok_(__FILE__,line)(read == sizeof(BITMAPFILEHEADER), "ReadFile read %u bytes (expected %u).\n", + read, sizeof(BITMAPFILEHEADER)); + hdr = (BITMAPFILEHEADER*)buffer; + ok_(__FILE__,line)(hdr->bfType == 0x4d42, "Got bfType %04x.\n", hdr->bfType); + ok_(__FILE__,line)(hdr->bfSize == expected + sizeof(BITMAPFILEHEADER), "Got bfSize %u.\n", hdr->bfSize); + ok_(__FILE__,line)(hdr->bfOffBits == sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER), + "Got bfOffBits %u.\n", hdr->bfOffBits); + + ok_(__FILE__,line)(ReadFile(file, buffer, expected, &read, NULL), "ReadFile failed, error: %#x.\n", GetLastError()); + ok_(__FILE__,line)(read == expected, "ReadFile read %u bytes (expected %u).\n", read, expected); + + CloseHandle(file); + check_bitmap_(line, buffer, width, height, seek_time); +} + static BOOL init_tests(void) { return unpack_avi_file(TEST_AVI_RES, test_avi_filename) @@ -1460,6 +1499,7 @@ static void test_bitmap_grab_mode(void) &TIME_FORMAT_MEDIA_TIME }; char *buf = malloc(sizeof(BITMAPINFOHEADER) + 960 * 720 * 3); + WCHAR temp_path[MAX_PATH], filename[MAX_PATH]; struct testfilter testfilter; FILTER_INFO filter_info; IReferenceClock *clock; @@ -1480,6 +1520,10 @@ static void test_bitmap_grab_mode(void) GUID guid; BSTR str;
+ ok(GetTempPathW(MAX_PATH, temp_path), "GetTempPath failed, error: %#x.\n", GetLastError()); + ok(GetTempFileNameW(temp_path, L"DES", 0, filename), "GetTempFileName failed, error: %#x.\n", GetLastError()); + DeleteFileW(filename); + hr = CoCreateInstance(&CLSID_MediaDet, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaDet, (void **)&detector); ok(hr == S_OK, "Got hr %#x.\n", hr); @@ -1489,6 +1533,8 @@ static void test_bitmap_grab_mode(void) size = sizeof(BITMAPINFOHEADER) + 640 * 480 * 3; hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, buf, 640, 480); ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + hr = IMediaDet_WriteBitmapBits(detector, 0.0, 640, 480, filename); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); hr = IMediaDet_GetSampleGrabber(detector, &sg); ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
@@ -1704,6 +1750,24 @@ static void test_bitmap_grab_mode(void) ok(hr == S_OK, "Got hr %#x.\n", hr); check_bitmap(buf, 960, 720, 3.25);
+ hr = IMediaDet_WriteBitmapBits(detector, 0.0, 640, 480, NULL); + todo_wine ok(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), "Got hr %#x.\n", hr); + hr = IMediaDet_WriteBitmapBits(detector, 1.6, 640, 480, filename); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine check_bitmap_file(filename, buf, 640, 480, 1.6); + hr = IMediaDet_WriteBitmapBits(detector, 3.08, 487, 337, filename); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine check_bitmap_file(filename, buf, 487, 337, 3.08); + hr = IMediaDet_WriteBitmapBits(detector, 2.68, 11, 250, filename); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine check_bitmap_file(filename, buf, 11, 250, 2.68); + hr = IMediaDet_WriteBitmapBits(detector, 0.44, 441, 363, filename); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine check_bitmap_file(filename, buf, 441, 363, 0.44); + hr = IMediaDet_WriteBitmapBits(detector, 4.04, 809, 701, filename); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine check_bitmap_file(filename, buf, 809, 701, 4.04); + /* Changing filter resets bitmap grab mode */ testfilter.bitmap_grab_mode = FALSE; hr = IMediaDet_put_Filter(detector, &testfilter.filter.IUnknown_inner); @@ -1732,6 +1796,34 @@ static void test_bitmap_grab_mode(void) hr = IMediaDet_get_OutputStreams(detector, &count); ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
+ testfilter.bitmap_grab_mode = FALSE; + hr = IMediaDet_put_Filter(detector, &testfilter.filter.IUnknown_inner); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaDet_GetSampleGrabber(detector, &sg); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + hr = IMediaDet_get_OutputStreams(detector, &count); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(count == 1, "Got %d streams.\n", count); + + /* As does WriteBitmapBits */ + testfilter.bitmap_grab_mode = TRUE; + hr = IMediaDet_WriteBitmapBits(detector, 1.75, 640, 480, NULL); + todo_wine ok(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), "Got hr %#x.\n", hr); + hr = IMediaDet_GetSampleGrabber(detector, &sg); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + hr = IMediaDet_get_OutputStreams(detector, &count); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(count == 1, "Got %d streams.\n", count); + + hr = IMediaDet_WriteBitmapBits(detector, 1.75, 640, 480, filename); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaDet_GetSampleGrabber(detector, &sg); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + if (SUCCEEDED(hr)) ISampleGrabber_Release(sg); + hr = IMediaDet_get_OutputStreams(detector, &count); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + + DeleteFileW(filename); ref = IMediaDet_Release(detector); ok(!ref, "Got outstanding refcount %d.\n", ref); ref = IBaseFilter_Release(&testfilter.filter.IBaseFilter_iface);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/qedit/mediadet.c | 62 +++++++++++++++++++++++++++++++++++-- dlls/qedit/tests/mediadet.c | 35 ++++++++++----------- 2 files changed, 76 insertions(+), 21 deletions(-)
diff --git a/dlls/qedit/mediadet.c b/dlls/qedit/mediadet.c index acf659b..4062674 100644 --- a/dlls/qedit/mediadet.c +++ b/dlls/qedit/mediadet.c @@ -888,9 +888,65 @@ static HRESULT WINAPI MediaDet_WriteBitmapBits(IMediaDet* iface, double StreamTime, LONG Width, LONG Height, BSTR Filename) { - MediaDetImpl *This = impl_from_IMediaDet(iface); - FIXME("(%p)->(%f %d %d %p): not implemented!\n", This, StreamTime, Width, Height, Filename); - return E_NOTIMPL; + MediaDetImpl *detector = impl_from_IMediaDet(iface); + BITMAPFILEHEADER *hdr; + HANDLE file, mapping; + LONG size, size_hi; + HRESULT hr; + char *buf; + + TRACE("(%p)->(%.16e %d %d %s)\n", detector, StreamTime, Width, Height, debugstr_w(Filename)); + + if (!Filename) + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + + /* WriteBitmapBits rounds the width up to a multiple of 4 */ + Width = (Width + 3) & ~3; + size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + Width * 3 * Height; + + file = CreateFileW(Filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file == INVALID_HANDLE_VALUE) + return HRESULT_FROM_WIN32(GetLastError()); + + size_hi = 0; + if (SetFilePointer(file, size, &size_hi, FILE_BEGIN) == INVALID_SET_FILE_POINTER || + !SetEndOfFile(file) || + !(mapping = CreateFileMappingW(file, NULL, PAGE_READWRITE, 0, 0, NULL))) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + CloseHandle(file); + DeleteFileW(Filename); + return hr; + } + + if (!(buf = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0))) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto err; + } + + hdr = (BITMAPFILEHEADER*)buf; + hdr->bfType = 0x4d42; + hdr->bfSize = size; + hdr->bfReserved1 = 0; + hdr->bfReserved2 = 0; + hdr->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); + hr = IMediaDet_GetBitmapBits(&detector->IMediaDet_iface, StreamTime, NULL, + buf + sizeof(BITMAPFILEHEADER), Width, Height); + UnmapViewOfFile(buf); + if (FAILED(hr)) + goto err; + + CloseHandle(mapping); + CloseHandle(file); + return S_OK; + +err: + CloseHandle(mapping); + CloseHandle(file); + DeleteFileW(Filename); + return hr; }
static HRESULT WINAPI MediaDet_get_StreamMediaType(IMediaDet* iface, diff --git a/dlls/qedit/tests/mediadet.c b/dlls/qedit/tests/mediadet.c index dba9d56..6a09642 100644 --- a/dlls/qedit/tests/mediadet.c +++ b/dlls/qedit/tests/mediadet.c @@ -1522,7 +1522,6 @@ static void test_bitmap_grab_mode(void)
ok(GetTempPathW(MAX_PATH, temp_path), "GetTempPath failed, error: %#x.\n", GetLastError()); ok(GetTempFileNameW(temp_path, L"DES", 0, filename), "GetTempFileName failed, error: %#x.\n", GetLastError()); - DeleteFileW(filename);
hr = CoCreateInstance(&CLSID_MediaDet, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaDet, (void **)&detector); @@ -1534,7 +1533,7 @@ static void test_bitmap_grab_mode(void) hr = IMediaDet_GetBitmapBits(detector, 0.0, &size, buf, 640, 480); ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); hr = IMediaDet_WriteBitmapBits(detector, 0.0, 640, 480, filename); - todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); hr = IMediaDet_GetSampleGrabber(detector, &sg); ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
@@ -1751,22 +1750,22 @@ static void test_bitmap_grab_mode(void) check_bitmap(buf, 960, 720, 3.25);
hr = IMediaDet_WriteBitmapBits(detector, 0.0, 640, 480, NULL); - todo_wine ok(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), "Got hr %#x.\n", hr); + ok(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), "Got hr %#x.\n", hr); hr = IMediaDet_WriteBitmapBits(detector, 1.6, 640, 480, filename); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - todo_wine check_bitmap_file(filename, buf, 640, 480, 1.6); + ok(hr == S_OK, "Got hr %#x.\n", hr); + check_bitmap_file(filename, buf, 640, 480, 1.6); hr = IMediaDet_WriteBitmapBits(detector, 3.08, 487, 337, filename); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - todo_wine check_bitmap_file(filename, buf, 487, 337, 3.08); + ok(hr == S_OK, "Got hr %#x.\n", hr); + check_bitmap_file(filename, buf, 487, 337, 3.08); hr = IMediaDet_WriteBitmapBits(detector, 2.68, 11, 250, filename); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - todo_wine check_bitmap_file(filename, buf, 11, 250, 2.68); + ok(hr == S_OK, "Got hr %#x.\n", hr); + check_bitmap_file(filename, buf, 11, 250, 2.68); hr = IMediaDet_WriteBitmapBits(detector, 0.44, 441, 363, filename); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - todo_wine check_bitmap_file(filename, buf, 441, 363, 0.44); + ok(hr == S_OK, "Got hr %#x.\n", hr); + check_bitmap_file(filename, buf, 441, 363, 0.44); hr = IMediaDet_WriteBitmapBits(detector, 4.04, 809, 701, filename); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - todo_wine check_bitmap_file(filename, buf, 809, 701, 4.04); + ok(hr == S_OK, "Got hr %#x.\n", hr); + check_bitmap_file(filename, buf, 809, 701, 4.04);
/* Changing filter resets bitmap grab mode */ testfilter.bitmap_grab_mode = FALSE; @@ -1808,7 +1807,7 @@ static void test_bitmap_grab_mode(void) /* As does WriteBitmapBits */ testfilter.bitmap_grab_mode = TRUE; hr = IMediaDet_WriteBitmapBits(detector, 1.75, 640, 480, NULL); - todo_wine ok(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), "Got hr %#x.\n", hr); + ok(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), "Got hr %#x.\n", hr); hr = IMediaDet_GetSampleGrabber(detector, &sg); ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); hr = IMediaDet_get_OutputStreams(detector, &count); @@ -1816,12 +1815,12 @@ static void test_bitmap_grab_mode(void) ok(count == 1, "Got %d streams.\n", count);
hr = IMediaDet_WriteBitmapBits(detector, 1.75, 640, 480, filename); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(hr == S_OK, "Got hr %#x.\n", hr); hr = IMediaDet_GetSampleGrabber(detector, &sg); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); - if (SUCCEEDED(hr)) ISampleGrabber_Release(sg); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ISampleGrabber_Release(sg); hr = IMediaDet_get_OutputStreams(detector, &count); - todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
DeleteFileW(filename); ref = IMediaDet_Release(detector);
A new 1 second long AVI file was used as the others were too short and failed on Windows, for some reason.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/qedit/tests/mediadet.c | 41 ++++++++++++++++++++++++++++++++- dlls/qedit/tests/qedit.rc | 2 ++ dlls/qedit/tests/rc.h | 1 + dlls/qedit/tests/test_long.avi | Bin 0 -> 17412 bytes 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 dlls/qedit/tests/test_long.avi
diff --git a/dlls/qedit/tests/mediadet.c b/dlls/qedit/tests/mediadet.c index 6a09642..0f38a04 100644 --- a/dlls/qedit/tests/mediadet.c +++ b/dlls/qedit/tests/mediadet.c @@ -570,6 +570,7 @@ static void testfilter_init(struct testfilter *filter)
static WCHAR test_avi_filename[MAX_PATH]; static WCHAR test_sound_avi_filename[MAX_PATH]; +static WCHAR test_long_avi_filename[MAX_PATH];
static BOOL unpack_avi_file(int id, WCHAR name[MAX_PATH]) { @@ -810,7 +811,8 @@ static void check_bitmap_file_(unsigned line, const WCHAR *filename, void *buffe static BOOL init_tests(void) { return unpack_avi_file(TEST_AVI_RES, test_avi_filename) - && unpack_avi_file(TEST_SOUND_AVI_RES, test_sound_avi_filename); + && unpack_avi_file(TEST_SOUND_AVI_RES, test_sound_avi_filename) + && unpack_avi_file(TEST_LONG_AVI_RES, test_long_avi_filename); }
static void test_mediadet(void) @@ -1827,6 +1829,41 @@ static void test_bitmap_grab_mode(void) ok(!ref, "Got outstanding refcount %d.\n", ref); ref = IBaseFilter_Release(&testfilter.filter.IBaseFilter_iface); ok(!ref, "Got outstanding refcount %d.\n", ref); + + /* Exercise the bitmap grab mode on an avi file */ + hr = CoCreateInstance(&CLSID_MediaDet, NULL, CLSCTX_INPROC_SERVER, + &IID_IMediaDet, (void **)&detector); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + str = SysAllocString(test_long_avi_filename); + hr = IMediaDet_put_Filename(detector, str); + ok(hr == S_OK, "Got hr %#x.\n", hr); + SysFreeString(str); + + hr = IMediaDet_EnterBitmapGrabMode(detector, 0.0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaDet_get_OutputStreams(detector, &count); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + + str = SysAllocString(test_long_avi_filename); + hr = IMediaDet_put_Filename(detector, str); + ok(hr == S_OK, "Got hr %#x.\n", hr); + SysFreeString(str); + + hr = IMediaDet_GetSampleGrabber(detector, &sg); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + hr = IMediaDet_get_OutputStreams(detector, &count); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(count == 1, "Got %d streams.\n", count); + + hr = IMediaDet_GetBitmapBits(detector, 0.1, NULL, buf, 640, 480); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaDet_WriteBitmapBits(detector, 0.5, 21, 13, filename); + ok(hr == S_OK, "Got hr %#x.\n", hr); + DeleteFileW(filename); + + ref = IMediaDet_Release(detector); + ok(!ref, "Got outstanding refcount %d.\n", ref); free(buf); }
@@ -1864,6 +1901,8 @@ START_TEST(mediadet) ok(ret, "Failed to delete file, error %u.\n", GetLastError()); ret = DeleteFileW(test_sound_avi_filename); ok(ret, "Failed to delete file, error %u.\n", GetLastError()); + ret = DeleteFileW(test_long_avi_filename); + ok(ret, "Failed to delete file, error %u.\n", GetLastError());
CoUninitialize(); } diff --git a/dlls/qedit/tests/qedit.rc b/dlls/qedit/tests/qedit.rc index 733beac..85ec2f9 100644 --- a/dlls/qedit/tests/qedit.rc +++ b/dlls/qedit/tests/qedit.rc @@ -24,3 +24,5 @@ TEST_AVI_RES AVI_RES_TYPE test.avi /* @makedep: test_sound.avi */ TEST_SOUND_AVI_RES AVI_RES_TYPE test_sound.avi +/* @makedep: test_long.avi */ +TEST_LONG_AVI_RES AVI_RES_TYPE test_long.avi diff --git a/dlls/qedit/tests/rc.h b/dlls/qedit/tests/rc.h index 1de98d9..9639728 100644 --- a/dlls/qedit/tests/rc.h +++ b/dlls/qedit/tests/rc.h @@ -23,6 +23,7 @@
#define TEST_AVI_RES 1 #define TEST_SOUND_AVI_RES 2 +#define TEST_LONG_AVI_RES 3
#define AVI_RES_TYPE 256
diff --git a/dlls/qedit/tests/test_long.avi b/dlls/qedit/tests/test_long.avi new file mode 100644 index 0000000000000000000000000000000000000000..2e0fbc4e177c3f1a0e9f95ea1afb18f10c78da65 GIT binary patch literal 17412 zcmeI4u}T9$5QZn1Mp1|&60q=!6gEPX6t<!S&WMFZd-0GQBm@!(mRDI>T3X}*d<4N4 z@KJn#6oSq~Zpo!cyO8~s++}8Ovy=JQ`HSV)tGL<xITbm%jiZbBy8XQ=(n$v0yHVOX z7LoCDN#sBcZIxA#wSQBz*Loz<d)3UjW^=t(M23%pu3po*MrksP8%Ooop*BxGr+sH- zkzZ~3m`G>W-#_eYhh33g7Oy1hVsqK}`F4JD*{W*n{87E0+|Q4}hX4d1009U<00Izz z00bZa0SG`~xe3_*Kv_2)`bn>=pUz~t(_NUIv6!qZ9Ku5gKwwD;*gkDr_jh~!QEL07 zwx9Y*gFwIpGMZNc=up7LN9vA%*CHf%h7P%<h!HRWq`)r$bSU8BBXvi>Zxs?99db(% zBVYn3fnNgXP{74U>W+ZlDkM5O<dz~vzyweNzXZ^sfQygR9Ra^pNOW|_Ek%rg37`ai z37|s(7ayrR0)DHI=xOqFIM(;ZO^@_Ais^Y2_I`Elzo<RyZ|nIQv;Nyg(XVY4{r8=s W-`FksAA3drtXA~D^gxYF|HB{S_cjRt
literal 0 HcmV?d00001