Along with !4450, this fixes WMV videos in microkiri (https://bugs.winehq.org/show_bug.cgi?id=9127#c102) and Wagamama High Spec Trial Edition (https://wagahigh.com/download_trial.php#normal ; ダウンロード means download).
-- v10: wmvcore/tests: Add tests for compressed output. winegstreamer: Implement compressed output support in WMSyncReader. winegstreamer: Leave pts/duration unchanged if they're not set. winegstreamer: Introduce mutex for wm_reader read_thread_shutdown. winegstreamer: Move file size to struct wm_reader. winegstreamer: Fill in a few more pieces of WMV format handling. winegstreamer: Add codec_data to WMVs.
From: Alfred Agrell floating@muncher.se
--- dlls/winegstreamer/quartz_parser.c | 14 +++++++++++--- dlls/winegstreamer/unixlib.h | 2 ++ dlls/winegstreamer/wg_format.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-)
diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index c41b0dca0f6..b9e328a0ba8 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -621,7 +621,7 @@ static bool amt_from_wg_format_video_cinepak(AM_MEDIA_TYPE *mt, const struct wg_
static bool amt_from_wg_format_video_wmv(AM_MEDIA_TYPE *mt, const struct wg_format *format) { - VIDEOINFO *video_format; + VIDEOINFOHEADER *video_format; uint32_t frame_time; const GUID *subtype;
@@ -647,7 +647,7 @@ static bool amt_from_wg_format_video_wmv(AM_MEDIA_TYPE *mt, const struct wg_form return false; }
- if (!(video_format = CoTaskMemAlloc(sizeof(*video_format)))) + if (!(video_format = CoTaskMemAlloc(sizeof(*video_format) + format->u.video_wmv.codec_data_len))) return false;
mt->majortype = MEDIATYPE_Video; @@ -656,7 +656,7 @@ static bool amt_from_wg_format_video_wmv(AM_MEDIA_TYPE *mt, const struct wg_form mt->bTemporalCompression = TRUE; mt->lSampleSize = 0; mt->formattype = FORMAT_VideoInfo; - mt->cbFormat = sizeof(VIDEOINFOHEADER); + mt->cbFormat = sizeof(*video_format) + format->u.video_wmv.codec_data_len; mt->pbFormat = (BYTE *)video_format;
memset(video_format, 0, sizeof(*video_format)); @@ -669,6 +669,7 @@ static bool amt_from_wg_format_video_wmv(AM_MEDIA_TYPE *mt, const struct wg_form video_format->bmiHeader.biHeight = format->u.video_wmv.height; video_format->bmiHeader.biPlanes = 1; video_format->bmiHeader.biCompression = mt->subtype.Data1; + memcpy(video_format+1, format->u.video_wmv.codec_data, format->u.video_wmv.codec_data_len);
return true; } @@ -997,6 +998,13 @@ static bool amt_to_wg_format_video_wmv(const AM_MEDIA_TYPE *mt, struct wg_format else format->u.video_wmv.format = WG_WMV_VIDEO_FORMAT_UNKNOWN;
+ format->u.video_wmv.codec_data_len = mt->cbFormat - sizeof(VIDEOINFOHEADER); + if (format->u.video_wmv.codec_data_len > sizeof(format->u.video_wmv.codec_data)) + { + ERR("Too big codec_data value (%u).\n", format->u.video_wmv.codec_data_len); + format->u.video_wmv.codec_data_len = 0; + } + memcpy(format->u.video_wmv.codec_data, video_format+1, format->u.video_wmv.codec_data_len); return true; }
diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 86bd380c351..aec59c314c2 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -157,6 +157,8 @@ struct wg_format wg_wmv_video_format format; int32_t width, height; uint32_t fps_n, fps_d; + uint32_t codec_data_len; + unsigned char codec_data[64]; } video_wmv; struct { diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index 274a6dec261..a66718fe928 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -300,6 +300,9 @@ static void wg_format_from_caps_video_wmv(struct wg_format *format, const GstCap gchar format_buffer[5] = {'W','M','V','0',0}; enum wg_wmv_video_format wmv_format; const gchar *wmv_format_str = NULL; + const GValue *codec_data_value; + GstBuffer *codec_data; + GstMapInfo map;
if (!gst_structure_get_int(structure, "width", &width)) { @@ -344,6 +347,19 @@ static void wg_format_from_caps_video_wmv(struct wg_format *format, const GstCap format->u.video_wmv.format = wmv_format; format->u.video_wmv.fps_n = fps_n; format->u.video_wmv.fps_d = fps_d; + + if ((codec_data_value = gst_structure_get_value(structure, "codec_data")) && (codec_data = gst_value_get_buffer(codec_data_value))) + { + gst_buffer_map(codec_data, &map, GST_MAP_READ); + if (map.size <= sizeof(format->u.video_wmv.codec_data)) + { + format->u.video_wmv.codec_data_len = map.size; + memcpy(format->u.video_wmv.codec_data, map.data, map.size); + } + else + GST_WARNING("Too big codec_data value (%u) in %" GST_PTR_FORMAT ".", (UINT)map.size, caps); + gst_buffer_unmap(codec_data, &map); + } }
static void wg_format_from_caps_video_mpeg1(struct wg_format *format, const GstCaps *caps) @@ -733,6 +749,7 @@ static GstCaps *wg_format_to_caps_video_wmv(const struct wg_format *format) { unsigned int wmv_version; const char *wmv_format; + GstBuffer *buffer; GstCaps *caps;
if (!(caps = gst_caps_new_empty_simple("video/x-wmv"))) @@ -780,6 +797,19 @@ static GstCaps *wg_format_to_caps_video_wmv(const struct wg_format *format) if (format->u.video_wmv.fps_d || format->u.video_wmv.fps_n) gst_caps_set_simple(caps, "framerate", GST_TYPE_FRACTION, format->u.video_wmv.fps_n, format->u.video_wmv.fps_d, NULL);
+ if (format->u.video_wmv.codec_data_len) + { + if (!(buffer = gst_buffer_new_and_alloc(format->u.video_wmv.codec_data_len))) + { + gst_caps_unref(caps); + return NULL; + } + + gst_buffer_fill(buffer, 0, format->u.video_wmv.codec_data, format->u.video_wmv.codec_data_len); + gst_caps_set_simple(caps, "codec_data", GST_TYPE_BUFFER, buffer, NULL); + gst_buffer_unref(buffer); + } + return caps; }
From: Alfred Agrell floating@muncher.se
--- dlls/winegstreamer/quartz_parser.c | 9 +++++++-- dlls/winegstreamer/wg_format.c | 7 ++++++- 2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index b9e328a0ba8..fdcea1017a9 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -400,6 +400,10 @@ unsigned int wg_format_get_max_size(const struct wg_format *format) return wg_format_get_max_size_video_raw(WG_VIDEO_FORMAT_YV12, format->u.video_mpeg1.width, format->u.video_mpeg1.height);
+ case WG_MAJOR_TYPE_VIDEO_WMV: + return wg_format_get_max_size_video_raw(WG_VIDEO_FORMAT_YV12, + format->u.video_wmv.width, format->u.video_wmv.height); + case WG_MAJOR_TYPE_AUDIO: { unsigned int rate = format->u.audio.rate, channels = format->u.audio.channels; @@ -454,7 +458,6 @@ unsigned int wg_format_get_max_size(const struct wg_format *format)
case WG_MAJOR_TYPE_AUDIO_MPEG4: case WG_MAJOR_TYPE_VIDEO_H264: - case WG_MAJOR_TYPE_VIDEO_WMV: case WG_MAJOR_TYPE_VIDEO_INDEO: FIXME("Format %u not implemented!\n", format->major_type); return 0; @@ -664,11 +667,13 @@ static bool amt_from_wg_format_video_wmv(AM_MEDIA_TYPE *mt, const struct wg_form video_format->rcTarget = video_format->rcSource; if ((frame_time = MulDiv(10000000, format->u.video_wmv.fps_d, format->u.video_wmv.fps_n)) != -1) video_format->AvgTimePerFrame = frame_time; - video_format->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + video_format->bmiHeader.biSize = sizeof(BITMAPINFOHEADER) + format->u.video_wmv.codec_data_len; video_format->bmiHeader.biWidth = format->u.video_wmv.width; video_format->bmiHeader.biHeight = format->u.video_wmv.height; video_format->bmiHeader.biPlanes = 1; video_format->bmiHeader.biCompression = mt->subtype.Data1; + video_format->bmiHeader.biBitCount = 24; + video_format->dwBitRate = 0; memcpy(video_format+1, format->u.video_wmv.codec_data, format->u.video_wmv.codec_data_len);
return true; diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index a66718fe928..9404a0290eb 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -893,7 +893,6 @@ bool wg_format_compare(const struct wg_format *a, const struct wg_format *b) case WG_MAJOR_TYPE_AUDIO_MPEG4: case WG_MAJOR_TYPE_AUDIO_WMA: case WG_MAJOR_TYPE_VIDEO_H264: - case WG_MAJOR_TYPE_VIDEO_WMV: case WG_MAJOR_TYPE_VIDEO_INDEO: case WG_MAJOR_TYPE_VIDEO_MPEG1: GST_FIXME("Format %u not implemented!", a->major_type); @@ -917,6 +916,12 @@ bool wg_format_compare(const struct wg_format *a, const struct wg_format *b) /* Do not compare FPS. */ return a->u.video_cinepak.width == b->u.video_cinepak.width && a->u.video_cinepak.height == b->u.video_cinepak.height; + + case WG_MAJOR_TYPE_VIDEO_WMV: + /* Do not compare FPS. */ + return a->u.video_wmv.format == b->u.video_wmv.format + && a->u.video_wmv.width == b->u.video_wmv.width + && a->u.video_wmv.height == b->u.video_wmv.height; }
assert(0);
From: Alfred Agrell floating@muncher.se
--- dlls/winegstreamer/wm_reader.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-)
diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index 882b6df1bbb..09f69bc6208 100644 --- a/dlls/winegstreamer/wm_reader.c +++ b/dlls/winegstreamer/wm_reader.c @@ -55,6 +55,7 @@ struct wm_reader
CRITICAL_SECTION cs; QWORD start_time; + QWORD file_size;
IStream *source_stream; HANDLE file; @@ -592,27 +593,12 @@ static DWORD CALLBACK read_thread(void *arg) IStream *stream = reader->source_stream; HANDLE file = reader->file; size_t buffer_size = 4096; - uint64_t file_size; + uint64_t file_size = reader->file_size; void *data;
if (!(data = malloc(buffer_size))) return 0;
- if (file) - { - LARGE_INTEGER size; - - GetFileSizeEx(file, &size); - file_size = size.QuadPart; - } - else - { - STATSTG stat; - - IStream_Stat(stream, &stat, STATFLAG_NONAME); - file_size = stat.cbSize.QuadPart; - } - TRACE("Starting read thread for reader %p.\n", reader);
while (!reader->read_thread_shutdown) @@ -1453,7 +1439,7 @@ static const IWMReaderTimecodeVtbl timecode_vtbl = timecode_GetTimecodeRangeBounds, };
-static HRESULT init_stream(struct wm_reader *reader, QWORD file_size) +static HRESULT init_stream(struct wm_reader *reader) { wg_parser_t wg_parser; HRESULT hr; @@ -1470,7 +1456,7 @@ static HRESULT init_stream(struct wm_reader *reader, QWORD file_size) goto out_destroy_parser; }
- if (FAILED(hr = wg_parser_connect(reader->wg_parser, file_size))) + if (FAILED(hr = wg_parser_connect(reader->wg_parser, reader->file_size))) { ERR("Failed to connect parser, hr %#lx.\n", hr); goto out_shutdown_thread; @@ -2134,8 +2120,9 @@ static HRESULT WINAPI reader_Open(IWMSyncReader2 *iface, const WCHAR *filename) }
reader->file = file; + reader->file_size = size.QuadPart;
- if (FAILED(hr = init_stream(reader, size.QuadPart))) + if (FAILED(hr = init_stream(reader))) reader->file = NULL;
LeaveCriticalSection(&reader->cs); @@ -2166,7 +2153,9 @@ static HRESULT WINAPI reader_OpenStream(IWMSyncReader2 *iface, IStream *stream) }
IStream_AddRef(reader->source_stream = stream); - if (FAILED(hr = init_stream(reader, stat.cbSize.QuadPart))) + reader->file_size = stat.cbSize.QuadPart; + + if (FAILED(hr = init_stream(reader))) { IStream_Release(stream); reader->source_stream = NULL;
From: Alfred Agrell floating@muncher.se
--- dlls/winegstreamer/wm_reader.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index 09f69bc6208..5c778ba2350 100644 --- a/dlls/winegstreamer/wm_reader.c +++ b/dlls/winegstreamer/wm_reader.c @@ -54,6 +54,7 @@ struct wm_reader LONG refcount;
CRITICAL_SECTION cs; + CRITICAL_SECTION shutdown_cs; QWORD start_time; QWORD file_size;
@@ -601,7 +602,7 @@ static DWORD CALLBACK read_thread(void *arg)
TRACE("Starting read thread for reader %p.\n", reader);
- while (!reader->read_thread_shutdown) + while (true) { LARGE_INTEGER large_offset; uint64_t offset; @@ -609,6 +610,14 @@ static DWORD CALLBACK read_thread(void *arg) uint32_t size; HRESULT hr;
+ EnterCriticalSection(&reader->shutdown_cs); + if (reader->read_thread_shutdown) + { + LeaveCriticalSection(&reader->shutdown_cs); + break; + } + LeaveCriticalSection(&reader->shutdown_cs); + if (!wg_parser_get_next_read_offset(reader->wg_parser, &offset, &size)) continue;
@@ -1450,6 +1459,7 @@ static HRESULT init_stream(struct wm_reader *reader)
reader->wg_parser = wg_parser; reader->read_thread_shutdown = false; + if (!(reader->read_thread = CreateThread(NULL, 0, read_thread, reader, 0, NULL))) { hr = E_OUTOFMEMORY; @@ -1517,7 +1527,9 @@ out_disconnect_parser: wg_parser_disconnect(reader->wg_parser);
out_shutdown_thread: + EnterCriticalSection(&reader->shutdown_cs); reader->read_thread_shutdown = true; + LeaveCriticalSection(&reader->shutdown_cs); WaitForSingleObject(reader->read_thread, INFINITE); CloseHandle(reader->read_thread); reader->read_thread = NULL; @@ -1728,6 +1740,8 @@ static ULONG WINAPI unknown_inner_Release(IUnknown *iface)
reader->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&reader->cs); + reader->shutdown_cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&reader->shutdown_cs);
free(reader); } @@ -1781,7 +1795,9 @@ static HRESULT WINAPI reader_Close(IWMSyncReader2 *iface)
wg_parser_disconnect(reader->wg_parser);
+ EnterCriticalSection(&reader->shutdown_cs); reader->read_thread_shutdown = true; + LeaveCriticalSection(&reader->shutdown_cs); WaitForSingleObject(reader->read_thread, INFINITE); CloseHandle(reader->read_thread); reader->read_thread = NULL; @@ -2557,6 +2573,8 @@ HRESULT WINAPI winegstreamer_create_wm_sync_reader(IUnknown *outer, void **out)
InitializeCriticalSection(&object->cs); object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": reader.cs"); + InitializeCriticalSection(&object->shutdown_cs); + object->shutdown_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": reader.shutdown_cs");
TRACE("Created reader %p.\n", object); *out = outer ? (void *)&object->IUnknown_inner : (void *)&object->IWMSyncReader2_iface;
From: Alfred Agrell floating@muncher.se
--- dlls/winegstreamer/wm_reader.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index 5c778ba2350..a92556e9aec 100644 --- a/dlls/winegstreamer/wm_reader.c +++ b/dlls/winegstreamer/wm_reader.c @@ -1660,13 +1660,20 @@ static HRESULT wm_reader_read_stream_sample(struct wm_reader *reader, struct wg_
wg_parser_stream_release_buffer(stream->wg_stream);
+ *pts = buffer->pts; + *duration = buffer->duration; + if (!buffer->has_pts) + { FIXME("Missing PTS.\n"); + *pts = 0; + } if (!buffer->has_duration) + { FIXME("Missing duration.\n"); + *duration = 0; + }
- *pts = buffer->pts; - *duration = buffer->duration; *flags = 0; if (buffer->discontinuity) *flags |= WM_SF_DISCONTINUITY;
From: Alfred Agrell floating@muncher.se
--- dlls/winegstreamer/wm_reader.c | 88 ++++++++++++++++++++++++++++++++-- dlls/wmvcore/tests/wmvcore.c | 61 ++++++++++++++--------- 2 files changed, 121 insertions(+), 28 deletions(-)
diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index a92556e9aec..4e3995a2c6e 100644 --- a/dlls/winegstreamer/wm_reader.c +++ b/dlls/winegstreamer/wm_reader.c @@ -30,10 +30,6 @@ struct wm_stream WMT_STREAM_SELECTION selection; WORD index; bool eos; - /* Note that we only pretend to read compressed samples, and instead output - * uncompressed samples regardless of whether we are configured to read - * compressed samples. Rather, the behaviour of the reader objects differs - * in nontrivial ways depending on this field. */ bool read_compressed;
IWMReaderAllocatorEx *output_allocator; @@ -1541,6 +1537,78 @@ out_destroy_parser: return hr; }
+static HRESULT reinit_stream(struct wm_reader *reader, bool read_compressed) +{ + wg_parser_t wg_parser; + HRESULT hr; + + wg_parser_disconnect(reader->wg_parser); + + EnterCriticalSection(&reader->shutdown_cs); + reader->read_thread_shutdown = true; + LeaveCriticalSection(&reader->shutdown_cs); + WaitForSingleObject(reader->read_thread, INFINITE); + CloseHandle(reader->read_thread); + reader->read_thread = NULL; + + wg_parser_destroy(reader->wg_parser); + reader->wg_parser = 0; + + if (!(wg_parser = wg_parser_create(WG_PARSER_DECODEBIN, read_compressed))) + return E_OUTOFMEMORY; + + reader->wg_parser = wg_parser; + reader->read_thread_shutdown = false; + + if (!(reader->read_thread = CreateThread(NULL, 0, read_thread, reader, 0, NULL))) + { + hr = E_OUTOFMEMORY; + goto out_destroy_parser; + } + + if (FAILED(hr = wg_parser_connect(reader->wg_parser, reader->file_size))) + { + ERR("Failed to connect parser, hr %#lx.\n", hr); + goto out_shutdown_thread; + } + + assert(reader->stream_count == wg_parser_get_stream_count(reader->wg_parser)); + + WORD i; + for (i = 0; i < reader->stream_count; ++i) + { + struct wm_stream *stream = &reader->streams[i]; + struct wg_format format; + + stream->wg_stream = wg_parser_get_stream(reader->wg_parser, i); + stream->reader = reader; + wg_parser_stream_get_preferred_format(stream->wg_stream, &format); + if (stream->selection == WMT_ON) + wg_parser_stream_enable(stream->wg_stream, read_compressed ? &format : &stream->format); + } + + /* We probably discarded events because streams weren't enabled yet. + * Now that they're all enabled seek back to the start again. */ + wg_parser_stream_seek(reader->streams[0].wg_stream, 1.0, 0, 0, + AM_SEEKING_AbsolutePositioning, AM_SEEKING_NoPositioning); + + return S_OK; + +out_shutdown_thread: + EnterCriticalSection(&reader->shutdown_cs); + reader->read_thread_shutdown = true; + LeaveCriticalSection(&reader->shutdown_cs); + WaitForSingleObject(reader->read_thread, INFINITE); + CloseHandle(reader->read_thread); + reader->read_thread = NULL; + +out_destroy_parser: + wg_parser_destroy(reader->wg_parser); + reader->wg_parser = 0; + + return hr; +} + static struct wm_stream *wm_reader_get_stream_by_stream_number(struct wm_reader *reader, WORD stream_number) { if (stream_number && stream_number <= reader->stream_count) @@ -2352,6 +2420,7 @@ static HRESULT WINAPI reader_SetReadStreamSamples(IWMSyncReader2 *iface, WORD st }
stream->read_compressed = compressed; + reinit_stream(reader, compressed);
LeaveCriticalSection(&reader->cs); return S_OK; @@ -2397,7 +2466,16 @@ static HRESULT WINAPI reader_SetStreamsSelected(IWMSyncReader2 *iface, FIXME("Ignoring selection %#x for stream %u; treating as enabled.\n", selections[i], stream_numbers[i]); TRACE("Enabling stream %u.\n", stream_numbers[i]); - wg_parser_stream_enable(stream->wg_stream, &stream->format); + if (stream->read_compressed) + { + struct wg_format format; + wg_parser_stream_get_preferred_format(stream->wg_stream, &format); + wg_parser_stream_enable(stream->wg_stream, &format); + } + else + { + wg_parser_stream_enable(stream->wg_stream, &stream->format); + } } }
diff --git a/dlls/wmvcore/tests/wmvcore.c b/dlls/wmvcore/tests/wmvcore.c index 1741299e654..c1114b711f9 100644 --- a/dlls/wmvcore/tests/wmvcore.c +++ b/dlls/wmvcore/tests/wmvcore.c @@ -455,6 +455,7 @@ struct teststream HANDLE file; DWORD input_tid; DWORD main_tid; + DWORD input_tid_changes; };
static struct teststream *impl_from_IStream(IStream *iface) @@ -494,12 +495,10 @@ static HRESULT WINAPI stream_Read(IStream *iface, void *data, ULONG size, ULONG if (winetest_debug > 2) trace("%04lx: IStream::Read(size %lu)\n", GetCurrentThreadId(), size);
- if (!stream->input_tid) - stream->input_tid = GetCurrentThreadId(); - else + if (stream->input_tid != GetCurrentThreadId()) { - todo_wine_if(stream->input_tid == stream->main_tid) - ok(stream->input_tid == GetCurrentThreadId(), "got wrong thread\n"); + ++stream->input_tid_changes; + stream->input_tid = GetCurrentThreadId(); }
ok(size > 0, "Got zero size.\n"); @@ -523,12 +522,10 @@ static HRESULT WINAPI stream_Seek(IStream *iface, LARGE_INTEGER offset, DWORD me if (winetest_debug > 2) trace("%04lx: IStream::Seek(offset %I64u, method %#lx)\n", GetCurrentThreadId(), offset.QuadPart, method);
- if (!stream->input_tid) - stream->input_tid = GetCurrentThreadId(); - else + if (stream->input_tid != GetCurrentThreadId()) { - todo_wine_if(stream->input_tid == stream->main_tid) - ok(stream->input_tid == GetCurrentThreadId(), "got wrong thread\n"); + ++stream->input_tid_changes; + stream->input_tid = GetCurrentThreadId(); }
GetFileSizeEx(stream->file, &size); @@ -588,12 +585,10 @@ static HRESULT WINAPI stream_Stat(IStream *iface, STATSTG *stat, DWORD flags) if (winetest_debug > 1) trace("%04lx: IStream::Stat(flags %#lx)\n", GetCurrentThreadId(), flags);
- if (!stream->input_tid) - stream->input_tid = GetCurrentThreadId(); - else + if (stream->input_tid != GetCurrentThreadId()) { - todo_wine_if(stream->input_tid == stream->main_tid) - ok(stream->input_tid == GetCurrentThreadId(), "got wrong thread\n"); + ++stream->input_tid_changes; + stream->input_tid = GetCurrentThreadId(); }
ok(flags == STATFLAG_NONAME, "Got flags %#lx.\n", flags); @@ -1173,6 +1168,7 @@ static void test_sync_reader_settings(void) IWMSyncReader_Release(reader);
ok(stream.refcount == 1, "Got outstanding refcount %ld.\n", stream.refcount); + todo_wine ok(stream.input_tid_changes == 1, "Changed thread %ld times.\n", stream.input_tid_changes); CloseHandle(stream.file); ret = DeleteFileW(filename); ok(ret, "Failed to delete %s, error %lu.\n", debugstr_w(filename), GetLastError()); @@ -1404,7 +1400,6 @@ static void test_sync_reader_streaming(void)
ok(stream.refcount == 1, "Got outstanding refcount %ld.\n", stream.refcount);
- stream.input_tid = 0; /* FIXME: currently required as Wine calls IStream_Stat synchronously in OpenStream */ SetFilePointer(stream.file, 0, NULL, FILE_BEGIN); hr = IWMSyncReader_OpenStream(reader, &stream.IStream_iface); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -1415,6 +1410,7 @@ static void test_sync_reader_streaming(void) ok(!ref, "Got outstanding refcount %ld.\n", ref);
ok(stream.refcount == 1, "Got outstanding refcount %ld.\n", stream.refcount); + todo_wine ok(stream.input_tid_changes == 1, "Changed thread %ld times.\n", stream.input_tid_changes); CloseHandle(stream.file); ret = DeleteFileW(filename); ok(ret, "Failed to delete %s, error %lu.\n", debugstr_w(filename), GetLastError()); @@ -1775,6 +1771,7 @@ static void test_sync_reader_types(void) ok(!ref, "Got outstanding refcount %ld.\n", ref);
ok(stream.refcount == 1, "Got outstanding refcount %ld.\n", stream.refcount); + todo_wine ok(stream.input_tid_changes == 1, "Changed thread %ld times.\n", stream.input_tid_changes); CloseHandle(stream.file); ret = DeleteFileW(filename); ok(ret, "Failed to delete %s, error %lu.\n", debugstr_w(filename), GetLastError()); @@ -2053,7 +2050,7 @@ static HRESULT WINAPI callback_OnSample(IWMReaderCallback *iface, DWORD output, GetTickCount(), GetCurrentThreadId(), output, time, duration, flags);
/* uncompressed samples are slightly out of order because of decoding delay */ - ok(callback->last_pts[output] <= time, "got time %I64d\n", time); + ok(callback->last_pts[output] <= time, "expected %I64d <= %I64d\n", callback->last_pts[output], time); callback->last_pts[output] = time; callback->next_pts[output] = time + duration;
@@ -2134,7 +2131,7 @@ static HRESULT WINAPI callback_advanced_OnStreamSample(IWMReaderCallbackAdvanced trace("%lu: %04lx: IWMReaderCallbackAdvanced::OnStreamSample(stream %u, pts %I64u, duration %I64u, flags %#lx)\n", GetTickCount(), GetCurrentThreadId(), stream_number, pts, duration, flags);
- ok(callback->last_pts[output] <= pts, "got pts %I64d\n", pts); + ok(callback->last_pts[output] <= pts, "expected %I64d <= %I64d\n", callback->last_pts[output], pts); callback->last_pts[output] = pts; callback->next_pts[output] = pts + duration;
@@ -2146,7 +2143,7 @@ static HRESULT WINAPI callback_advanced_OnStreamSample(IWMReaderCallbackAdvanced else { ok(callback->callback_tid == GetCurrentThreadId(), "got wrong thread\n"); - ok(callback->last_pts[1 - output] <= pts, "got pts %I64d\n", pts); + ok(callback->last_pts[1 - output] <= pts, "expected %I64d <= %I64d\n", callback->last_pts[1 - output], pts); }
if (!callback->output_tid[output]) @@ -2572,8 +2569,6 @@ static void run_async_reader(IWMReader *reader, IWMReaderAdvanced2 *advanced, st callback->last_pts[1] = 0; callback->next_pts[1] = 0; memset(callback->output_tid, 0, sizeof(callback->output_tid)); - if (callback->stream) - callback->stream->input_tid = 0;
check_async_set_output_setting(advanced, 0, L"DedicatedDeliveryThread", WMT_TYPE_BOOL, callback->dedicated_threads, S_OK); @@ -2709,6 +2704,17 @@ static void run_async_reader(IWMReader *reader, IWMReaderAdvanced2 *advanced, st todo_wine ok(hr == E_UNEXPECTED, "Got hr %#lx.\n", hr);
+ /* FIXME: native can switch mode without rewinding, but Wine can't */ + IWMReader_Stop(reader); + wait_stopped_callback(callback); + callback->last_pts[0] = 0; + callback->next_pts[0] = 0; + callback->last_pts[1] = 0; + callback->next_pts[1] = 0; + hr = IWMReader_Start(reader, 0, 0, 1.0f, (void *)0xfacade); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + wait_started_callback(callback); + callback->expect_time = 13460000; hr = IWMReaderAdvanced2_DeliverTime(advanced, 13460000); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -2716,7 +2722,7 @@ static void run_async_reader(IWMReader *reader, IWMReaderAdvanced2 *advanced, st todo_wine ok(callback->last_pts[0] == 13460000, "Got pts %I64d.\n", callback->last_pts[0]); todo_wine - ok(callback->next_pts[0] == 13930000, "Got pts %I64d.\n", callback->next_pts[0]); + ok(callback->next_pts[0] == 13920000, "Got pts %I64d.\n", callback->next_pts[0]); todo_wine ok(callback->last_pts[1] == 13260000, "Got pts %I64d.\n", callback->last_pts[1]); todo_wine @@ -2729,6 +2735,16 @@ static void run_async_reader(IWMReader *reader, IWMReaderAdvanced2 *advanced, st ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IWMReaderAdvanced2_SetReceiveStreamSamples(advanced, 2, TRUE); ok(hr == S_OK, "Got hr %#lx.\n", hr); + + IWMReader_Stop(reader); + wait_stopped_callback(callback); + callback->last_pts[0] = 0; + callback->next_pts[0] = 0; + callback->last_pts[1] = 0; + callback->next_pts[1] = 0; + hr = IWMReader_Start(reader, 0, 0, 1.0f, (void *)0xfacade); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + wait_started_callback(callback); }
callback->expect_time = test_wmv_duration * 2; @@ -3233,7 +3249,6 @@ static void test_async_reader_streaming(void) ok(callback.refcount > 1, "Got refcount %ld.\n", callback.refcount); wait_opened_callback(&callback);
- stream.input_tid = 0; /* FIXME: currently required as Wine calls IStream_Stat synchronously in OpenStream */ hr = IWMReaderAdvanced2_OpenStream(advanced, &stream.IStream_iface, &callback.IWMReaderCallback_iface, (void **)0xdeadbee0); ok(hr == E_UNEXPECTED, "Got hr %#lx.\n", hr);
From: Alfred Agrell floating@muncher.se
--- dlls/wmvcore/tests/wmvcore.c | 220 ++++++++++++++++++++++++++++++++++- 1 file changed, 219 insertions(+), 1 deletion(-)
diff --git a/dlls/wmvcore/tests/wmvcore.c b/dlls/wmvcore/tests/wmvcore.c index c1114b711f9..ff2ab4c21eb 100644 --- a/dlls/wmvcore/tests/wmvcore.c +++ b/dlls/wmvcore/tests/wmvcore.c @@ -38,7 +38,6 @@ static BOOL compare_media_types(const WM_MEDIA_TYPE *a, const WM_MEDIA_TYPE *b) { /* We can't use memcmp(), because WM_MEDIA_TYPE has a hole, which sometimes * contains junk. */ - return IsEqualGUID(&a->majortype, &b->majortype) && IsEqualGUID(&a->subtype, &b->subtype) && a->bFixedSizeSamples == b->bFixedSizeSamples @@ -1816,6 +1815,224 @@ static void test_sync_reader_file(void) ok(ret, "Failed to delete %s, error %lu.\n", debugstr_w(filename), GetLastError()); }
+static void test_stream_media_type(IWMProfile *profile, WORD stream_num, const WM_MEDIA_TYPE *expected) +{ + IWMStreamConfig *stream_config; + IWMMediaProps *media_props; + char mt_buffer[2000]; + WM_MEDIA_TYPE *mt; + DWORD ret_size; + HRESULT hr; + + hr = IWMProfile_GetStreamByNumber(profile, stream_num, &stream_config); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IWMStreamConfig_QueryInterface(stream_config, &IID_IWMMediaProps, (void**)&media_props); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + mt = (WM_MEDIA_TYPE *)mt_buffer; + memset(mt_buffer, 0xcc, sizeof(mt_buffer)); + ret_size = sizeof(mt_buffer); + hr = IWMMediaProps_GetMediaType(media_props, mt, &ret_size); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + if (IsEqualGUID(&expected->subtype, &MEDIASUBTYPE_WMV1)) + { + VIDEOINFOHEADER *vih = (void *)mt->pbFormat; + VIDEOINFOHEADER *expect_vih = (void *)expected->pbFormat; + todo_wine ok(vih->dwBitRate == expect_vih->dwBitRate, "Bitrates didn't match.\n"); + vih->dwBitRate = expect_vih->dwBitRate; + } + ok(compare_media_types(mt, expected), "Media types didn't match.\n"); + + IWMStreamConfig_Release(stream_config); + IWMMediaProps_Release(media_props); +} + +static void test_output_media_type(IWMSyncReader *reader, WORD stream_num, const GUID *majortype, const GUID *subtype) +{ + char mt_buffer[2000]; + WM_MEDIA_TYPE *mt = (WM_MEDIA_TYPE *)mt_buffer; + IWMOutputMediaProps *output_props; + DWORD output_number; + DWORD ret_size, ref; + HRESULT hr; + + hr = IWMSyncReader_GetOutputNumberForStream(reader, stream_num, &output_number); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IWMSyncReader_GetOutputProps(reader, output_number, &output_props); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + ret_size = sizeof(mt_buffer); + hr = IWMOutputMediaProps_GetMediaType(output_props, mt, &ret_size); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + ok(IsEqualGUID(&mt->majortype, majortype), "Expected major type %s, got %s.\n", + debugstr_guid(majortype), debugstr_guid(&mt->majortype)); + ok(IsEqualGUID(&mt->subtype, subtype), "Expected subtype %s, got %s.\n", + debugstr_guid(subtype), debugstr_guid(&mt->subtype)); + + ref = IWMOutputMediaProps_Release(output_props); + ok(!ref, "Got outstanding refcount %ld.\n", ref); +} + +static const VIDEOINFOHEADER vih_wmv1 = +{ + .rcSource = { 0, 0, 64, 48 }, + .rcTarget = { 0, 0, 64, 48 }, + .dwBitRate = 0x0002e418, + .dwBitErrorRate = 0, + .AvgTimePerFrame = 0, + .bmiHeader.biSize = sizeof(BITMAPINFOHEADER), + .bmiHeader.biWidth = 64, + .bmiHeader.biHeight = 48, + .bmiHeader.biPlanes = 1, + .bmiHeader.biBitCount = 0x18, + .bmiHeader.biCompression = MAKEFOURCC('W','M','V','1'), + .bmiHeader.biSizeImage = 0, + .bmiHeader.biXPelsPerMeter = 0, + .bmiHeader.biYPelsPerMeter = 0, +}; +static const WM_MEDIA_TYPE mt_wmv1 = +{ + /* MEDIATYPE_Video, MEDIASUBTYPE_WMV1, FORMAT_VideoInfo */ + .majortype = {0x73646976, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .subtype = {0x31564d57, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .bTemporalCompression = TRUE, + .formattype = {0x05589f80, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, + .cbFormat = sizeof(VIDEOINFOHEADER), + .pbFormat = (BYTE *)&vih_wmv1, +}; + +static const MSAUDIO1WAVEFORMAT wfx_msaudio1 = +{ + .wfx.wFormatTag = WAVE_FORMAT_MSAUDIO1, + .wfx.nChannels = 1, + .wfx.nSamplesPerSec = 44100, + .wfx.nAvgBytesPerSec = 16000, + .wfx.nBlockAlign = 0x02e7, + .wfx.wBitsPerSample = 0x0010, + .wfx.cbSize = MSAUDIO1_WFX_EXTRA_BYTES, + .wSamplesPerBlock = 0, + .wEncodeOptions = 1, +}; +static const WM_MEDIA_TYPE mt_msaudio1 = +{ + /* MEDIATYPE_Audio, MEDIASUBTYPE_MSAUDIO1, FORMAT_WaveFormatEx */ + .majortype = {0x73647561, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .subtype = {0x00000160, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .bFixedSizeSamples = TRUE, + .lSampleSize = 0x02e7, + .formattype = {0x05589f81, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, + .cbFormat = sizeof(MSAUDIO1WAVEFORMAT), + .pbFormat = (BYTE *)&wfx_msaudio1, +}; + +static void test_sync_reader_compressed_output(void) +{ + static const DWORD audio_sample_times[] = { + 0, 460000, 920000, 1390000, 1850000, 2320000, 2780000, 3250000, 3710000, 4180000, 4640000, 5100000, + 5570000, 6030000, 6500000, 6960000, 7430000, 7890000, 8350000, 8820000, 9280000, 9750000, 10210000, + 10680000, 11140000, 11610000, 12070000, 12530000, 13000000, 13460000, 13930000, 14390000, 14860000, + 15320000, 15790000, 16250000, 16710000, 17180000, 17640000, 18110000, 18570000, 19040000, 19500000, 19960000, + 99999999 + }; + static const DWORD video_sample_sizes[] = { + 1117, 8, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 1117, 8, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 1117, 8, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 1117, 8, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 1117, 8 + }; + + const WCHAR *filename = load_resource(L"test.wmv"); + DWORD flags, bytes_count; + QWORD sample_time, sample_duration; + IWMSyncReader *reader; + DWORD audio_idx = 0; + DWORD video_idx = 0; + IWMProfile *profile; + INSSBuffer *sample; + WORD stream_num; + HRESULT hr; + BYTE *data; + + hr = WMCreateSyncReader(NULL, 0, &reader); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + IWMSyncReader_QueryInterface(reader, &IID_IWMProfile, (void **)&profile); + + hr = IWMSyncReader_Open(reader, filename); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + test_stream_media_type(profile, 1, &mt_wmv1); + test_stream_media_type(profile, 2, &mt_msaudio1); + test_output_media_type(reader, 1, &MEDIATYPE_Video, &MEDIASUBTYPE_RGB24); + test_output_media_type(reader, 2, &MEDIATYPE_Audio, &MEDIASUBTYPE_PCM); + + hr = IWMSyncReader_SetReadStreamSamples(reader, 1, TRUE); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IWMSyncReader_SetReadStreamSamples(reader, 2, TRUE); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + test_stream_media_type(profile, 1, &mt_wmv1); + test_stream_media_type(profile, 2, &mt_msaudio1); + test_output_media_type(reader, 1, &MEDIATYPE_Video, &MEDIASUBTYPE_RGB24); + test_output_media_type(reader, 2, &MEDIATYPE_Audio, &MEDIASUBTYPE_PCM); + + while (video_idx < 50 || audio_idx < 44) + { + DWORD next_video_time = 460000 + video_idx * 400000; + DWORD next_audio_time = audio_sample_times[audio_idx]; + + winetest_push_context("%lu/%lu", video_idx, audio_idx); + hr = IWMSyncReader_GetNextSample(reader, 0, &sample, &sample_time, &sample_duration, &flags, NULL, &stream_num); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + /* we don't care about the buffer, but GetLength is unimplemented in Wine */ + hr = INSSBuffer_GetBufferAndLength(sample, &data, &bytes_count); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + if (next_video_time <= next_audio_time) + { + ok(stream_num == 1, "Got %lu\n", (DWORD)stream_num); + ok(sample_time == next_video_time, "Expected %lu, got %lu\n", next_video_time, (DWORD)sample_time); + todo_wine ok(sample_duration == 10000, "Got %lu\n", (DWORD)sample_duration); + + if (video_idx == 0) + ok(flags == (WM_SF_CLEANPOINT|WM_SF_DISCONTINUITY), "Got %lu\n", flags); + else if (video_sample_sizes[video_idx] == 1117) + ok(flags == WM_SF_CLEANPOINT, "Got %lu\n", flags); + else + ok(flags == 0, "Got %lu\n", flags); + ok(bytes_count == video_sample_sizes[video_idx], + "Expected %lu, got %lu\n", video_sample_sizes[video_idx], bytes_count); + video_idx++; + } + else + { + ok(stream_num == 2, "Got %lu\n", (DWORD)stream_num); + ok(sample_time == next_audio_time, "Expected %lu, got %lu\n", next_audio_time, (DWORD)sample_time); + todo_wine ok(sample_duration == 460000, "Got %lu\n", (DWORD)sample_duration); + + if (audio_idx == 0) + todo_wine ok(flags == (WM_SF_CLEANPOINT|WM_SF_DISCONTINUITY), "Got %lu\n", flags); + else + todo_wine ok(flags == WM_SF_CLEANPOINT, "Got %lu\n", flags); + ok(bytes_count == 743, "Got %lu\n", bytes_count); + + audio_idx++; + } + + INSSBuffer_Release(sample); + winetest_pop_context(); + } + + hr = IWMSyncReader_GetNextSample(reader, 0, &sample, &sample_time, &sample_duration, &flags, NULL, &stream_num); + ok(hr == NS_E_NO_MORE_SAMPLES, "Got hr %#lx.\n", hr); + + IWMSyncReader_Release(reader); + IWMProfile_Release(profile); +} + struct callback { IWMReaderCallback IWMReaderCallback_iface; @@ -3971,6 +4188,7 @@ START_TEST(wmvcore) test_sync_reader_streaming(); test_sync_reader_types(); test_sync_reader_file(); + test_sync_reader_compressed_output(); test_async_reader_settings(); test_async_reader_streaming(); test_async_reader_types();