From: Ziqing Hui zhui@codeweavers.com
--- dlls/mf/tests/mf.c | 302 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 8bcd0db4f70..469884edf58 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -41,6 +41,10 @@ #include "initguid.h" #include "evr9.h"
+#define H264_UNIT_TYPE_IDR 5 +#define H264_UNIT_TYPE_SPS 7 +#define H264_UNIT_TYPE_PPS 8 + #define DEFINE_EXPECT(func) \ static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
@@ -278,6 +282,80 @@ static void init_sink_node(IMFStreamSink *stream_sink, MF_CONNECT_METHOD method, } }
+static BOOL is_h264_unit_start(const BYTE *buffer) +{ + UINT32 value = *(UINT32 *)buffer; + return value == 0x01000000 || (value & 0xffffff) == 0x010000; +} + +static BOOL is_h264_unit_type(const BYTE *buffer, BYTE type) +{ + BYTE type_byte = (*(UINT32 *)buffer == 0x01000000) ? + *(buffer + 4): + *(buffer + 3); + return (type_byte & 0x1f) == type; +} + +static void next_h264_unit(const BYTE **data, DWORD *size) +{ + if (*size <= 4) + { + *data = NULL; + *size = 0; + return; + } + + ok(is_h264_unit_start(*data), "Invalid h264 buffer.\n"); + + *data += 4; + *size -= 4; + + while (*size >= 4) + { + if (is_h264_unit_start(*data)) + return; + + (*data)++; + (*size)--; + } + + *data = NULL; + *size = 0; +} + +static IMFSample *create_h264_sample(const BYTE *data, DWORD size) +{ + const BYTE *sample_data = data; + DWORD sample_size = size; + + next_h264_unit(&data, &size); + if (data) + sample_size -= size; + + return create_sample(sample_data, sample_size); +} + +static void get_h264_sequence_header(const BYTE *data, DWORD size, + const BYTE **sequence_header, DWORD *sequence_header_size) +{ + const BYTE *cur = data; + DWORD cur_size = size; + + while (cur && !is_h264_unit_type(cur, H264_UNIT_TYPE_SPS)) + next_h264_unit(&cur, &cur_size); + + ok(cur && is_h264_unit_type(cur, H264_UNIT_TYPE_SPS), "Unable to get SPS data.\n"); + *sequence_header = cur; + + next_h264_unit(&cur, &cur_size); + ok(cur && is_h264_unit_type(cur, H264_UNIT_TYPE_PPS), "Unable to get PPS data.\n"); + + next_h264_unit(&cur, &cur_size); + ok(!!cur, "No remaining data.\n"); + + *sequence_header_size = cur - *sequence_header; +} + DEFINE_EXPECT(test_source_BeginGetEvent); DEFINE_EXPECT(test_source_QueueEvent); DEFINE_EXPECT(test_source_Start); @@ -6755,6 +6833,117 @@ static void test_MFRequireProtectedEnvironment(void) ok(ref == 0, "Release returned %ld\n", ref); }
+struct test_media_sink_callback +{ + IMFAsyncCallback IMFAsyncCallback_iface; + HANDLE start_event; + HANDLE stop_event; + HANDLE finalize_event; + IMFMediaSink *media_sink; + IMFStreamSink *stream_sink; +}; + +static struct test_media_sink_callback *impl_from_media_sink_callback_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct test_media_sink_callback, IMFAsyncCallback_iface); +} + +static HRESULT WINAPI test_media_sink_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_media_sink_callback_AddRef(IMFAsyncCallback *iface) +{ + return 2; +} + +static ULONG WINAPI test_media_sink_callback_Release(IMFAsyncCallback *iface) +{ + return 1; +} + +static HRESULT WINAPI test_media_sink_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_media_sink_callback_finalize_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct test_media_sink_callback *callback = impl_from_media_sink_callback_IMFAsyncCallback(iface); + HRESULT hr; + + ok(!!callback->media_sink, "NULL media sink.\n"); + hr = IMFFinalizableMediaSink_EndFinalize((IMFFinalizableMediaSink *)callback->media_sink, result); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + SetEvent(callback->finalize_event); + + return S_OK; +} + +static HRESULT WINAPI test_media_sink_callback_stream_event_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct test_media_sink_callback *callback = impl_from_media_sink_callback_IMFAsyncCallback(iface); + IMFMediaEvent *event; + MediaEventType type; + HRESULT hr; + + ok(!!callback->stream_sink, "NULL stream sink.\n"); + + hr = IMFStreamSink_EndGetEvent(callback->stream_sink, result, &event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEvent_GetType(event, &type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + switch (type) + { + case MEStreamSinkStarted: + SetEvent(callback->start_event); + break; + case MEStreamSinkStopped: + SetEvent(callback->stop_event); + break; + default: + break; + } + + IMFMediaEvent_Release(event); + + IMFStreamSink_BeginGetEvent(callback->stream_sink, iface, NULL); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl test_media_sink_callback_finalize_vtbl = +{ + test_media_sink_callback_QueryInterface, + test_media_sink_callback_AddRef, + test_media_sink_callback_Release, + test_media_sink_callback_GetParameters, + test_media_sink_callback_finalize_Invoke, +}; + + +static const IMFAsyncCallbackVtbl test_media_sink_callback_event_vtbl = +{ + test_media_sink_callback_QueryInterface, + test_media_sink_callback_AddRef, + test_media_sink_callback_Release, + test_media_sink_callback_GetParameters, + test_media_sink_callback_stream_event_Invoke, +}; + static void test_mpeg4_media_sink(void) { IMFMediaSink *sink = NULL, *sink2 = NULL, *sink_audio = NULL, *sink_video = NULL, *sink_empty = NULL; @@ -7054,6 +7243,118 @@ static void test_mpeg4_media_sink(void) IMFMediaType_Release(audio_type); }
+static void test_mpeg4_media_sink_process(void) +{ + DWORD width = 96, height = 96, fps = 1, h264_data_size, ret, sequence_header_length = 0; + struct test_media_sink_callback finalize_callback, stream_event_callback; + const BYTE *h264_data, *sequence_header = NULL; + IMFPresentationTimeSource *time_source; + IMFPresentationClock *clock; + IMFStreamSink *stream_sink; + IMFByteStream *bytestream; + IMFMediaType *video_type; + IMFMediaSink *media_sink; + IMFSample *input_sample; + HRESULT hr; + + load_resource(L"h264data.bin", &h264_data, &h264_data_size); + get_h264_sequence_header(h264_data, h264_data_size, &sequence_header, &sequence_header_length); + + hr = MFCreateMediaType(&video_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(video_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(video_type, &MF_MT_SUBTYPE, &MFVideoFormat_H264); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT64(video_type, &MF_MT_FRAME_SIZE, ((UINT64)width << 32) | height); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetBlob(video_type, &MF_MT_MPEG_SEQUENCE_HEADER, sequence_header, sequence_header_length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT64(video_type, &MF_MT_FRAME_RATE, ((UINT64)fps << 32) | 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateTempFile(MF_ACCESSMODE_READWRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, &bytestream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateMPEG4MediaSink(bytestream, video_type, NULL, &media_sink); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (hr != S_OK) + { + IMFByteStream_Release(bytestream); + IMFMediaType_Release(video_type); + return; + } + + /* Set presentation clock. */ + hr = MFCreateSystemTimeSource(&time_source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreatePresentationClock(&clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationClock_SetTimeSource(clock, time_source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSink_SetPresentationClock(media_sink, clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Stream event handler. */ + hr = IMFMediaSink_GetStreamSinkById(media_sink, 1, &stream_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + stream_event_callback.IMFAsyncCallback_iface.lpVtbl = &test_media_sink_callback_event_vtbl; + stream_event_callback.start_event = CreateEventW(NULL, FALSE, FALSE, NULL); + stream_event_callback.stop_event = CreateEventW(NULL, FALSE, FALSE, NULL); + stream_event_callback.stream_sink = stream_sink; + hr = IMFStreamSink_BeginGetEvent(stream_sink, &stream_event_callback.IMFAsyncCallback_iface, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Start. */ + hr = IMFPresentationClock_Start(clock, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ret = WaitForSingleObject(stream_event_callback.start_event, 3000); + ok(!ret, "WaitForSingleObject returned %#lx.\n", ret); + + /* Find IDR frame. */ + while (!is_h264_unit_type(h264_data, H264_UNIT_TYPE_IDR)) + next_h264_unit(&h264_data, &h264_data_size); + + /* Process sample. */ + input_sample = create_h264_sample(h264_data, h264_data_size); + hr = IMFSample_SetSampleTime(input_sample, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSample_SetSampleDuration(input_sample, 10000000); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFStreamSink_ProcessSample(stream_sink, input_sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Finalize media sink. */ + hr = IMFPresentationClock_Stop(clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + WaitForSingleObject(stream_event_callback.stop_event, 3000); + + finalize_callback.IMFAsyncCallback_iface.lpVtbl = &test_media_sink_callback_finalize_vtbl; + finalize_callback.finalize_event = CreateEventW(NULL, FALSE, FALSE, NULL); + finalize_callback.media_sink = media_sink; + hr = IMFFinalizableMediaSink_BeginFinalize((IMFFinalizableMediaSink *)media_sink, + &finalize_callback.IMFAsyncCallback_iface, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ret = WaitForSingleObject(finalize_callback.finalize_event, 3000); + ok(!ret, "WaitForSingleObject returned %#lx.\n", ret); + + hr = IMFMediaSink_Shutdown(media_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ret = IMFMediaSink_Release(media_sink); + ok(ret == 0, "Release returned %lu.\n", ret); + + IMFSample_Release(input_sample); + CloseHandle(finalize_callback.finalize_event); + CloseHandle(stream_event_callback.stop_event); + CloseHandle(stream_event_callback.start_event); + IMFStreamSink_Release(stream_sink); + IMFPresentationClock_Release(clock); + IMFPresentationTimeSource_Release(time_source); + IMFByteStream_Release(bytestream); + IMFMediaType_Release(video_type); +} + START_TEST(mf) { init_functions(); @@ -7088,4 +7389,5 @@ START_TEST(mf) test_MFGetTopoNodeCurrentType(); test_MFRequireProtectedEnvironment(); test_mpeg4_media_sink(); + test_mpeg4_media_sink_process(); }