From: Conor McCarthy <cmccarthy@codeweavers.com> It is assumed when calling wg_parser_stream_copy_buffer() the info in stream->map_info is for the buffer being copied. After a dynamic format change flushes stream->buffer, the null check in wg_parser_stream_copy_buffer() is insufficient because a new buffer may arrive before copying occurs. Assertion failure occurs if that buffer is smaller, and in any case, the copied data may be incompatible. This issue has been observed in SHOGUN: Total War Collection and Ys I & II Chronicles. --- dlls/winegstreamer/gst_private.h | 2 +- dlls/winegstreamer/main.c | 3 ++- dlls/winegstreamer/media_source.c | 2 +- dlls/winegstreamer/quartz_parser.c | 2 +- dlls/winegstreamer/unixlib.h | 4 +++- dlls/winegstreamer/wg_parser.c | 6 +++++- dlls/winegstreamer/wm_reader.c | 2 +- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 431e7930208..0ca8c8cfcbc 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -90,7 +90,7 @@ void wg_parser_stream_disable(wg_parser_stream_t stream); bool wg_parser_stream_get_buffer(wg_parser_t parser, wg_parser_stream_t stream, struct wg_parser_buffer *buffer); bool wg_parser_stream_copy_buffer(wg_parser_stream_t stream, - void *data, uint32_t offset, uint32_t size); + uint64_t serial_id, void *data, uint32_t offset, uint32_t size); void wg_parser_stream_release_buffer(wg_parser_stream_t stream); void wg_parser_stream_notify_qos(wg_parser_stream_t stream, bool underflow, double proportion, int64_t diff, uint64_t timestamp); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 7ff79bda4ab..bb2b7140fe7 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -329,11 +329,12 @@ bool wg_parser_stream_get_buffer(wg_parser_t parser, wg_parser_stream_t stream, } bool wg_parser_stream_copy_buffer(wg_parser_stream_t stream, - void *data, uint32_t offset, uint32_t size) + uint64_t serial_id, void *data, uint32_t offset, uint32_t size) { struct wg_parser_stream_copy_buffer_params params = { .stream = stream, + .serial_id = serial_id, .data = data, .offset = offset, .size = size, diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 05166d78cdb..ac813228040 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -726,7 +726,7 @@ static HRESULT media_stream_send_sample(struct media_stream *stream, const struc if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &data, NULL, NULL))) goto out; - if (!wg_parser_stream_copy_buffer(stream->wg_stream, data, 0, wg_buffer->size)) + if (!wg_parser_stream_copy_buffer(stream->wg_stream, wg_buffer->serial_id, data, 0, wg_buffer->size)) { hr = S_FALSE; wg_parser_stream_release_buffer(stream->wg_stream); diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index f09e8b9382a..fcece396585 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -1257,7 +1257,7 @@ static HRESULT send_sample(struct parser_source *pin, IMediaSample *sample, IMediaSample_GetPointer(sample, &ptr); - if (!wg_parser_stream_copy_buffer(pin->wg_stream, ptr, offset, size)) + if (!wg_parser_stream_copy_buffer(pin->wg_stream, buffer->serial_id, ptr, offset, size)) { /* The GStreamer pin has been flushed. */ return S_OK; diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 5d11934fac2..d801002c8a1 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -198,13 +198,14 @@ struct wg_sample struct wg_parser_buffer { + UINT64 serial_id; /* pts and duration are in 100-nanosecond units. */ UINT64 pts, duration; UINT32 size; UINT32 stream; UINT8 discontinuity, preroll, delta, has_pts, has_duration; }; -C_ASSERT(sizeof(struct wg_parser_buffer) == 32); +C_ASSERT(sizeof(struct wg_parser_buffer) == 40); typedef UINT64 wg_parser_t; typedef UINT64 wg_parser_stream_t; @@ -288,6 +289,7 @@ struct wg_parser_stream_get_buffer_params struct wg_parser_stream_copy_buffer_params { wg_parser_stream_t stream; + UINT64 serial_id; void *data; UINT32 offset; UINT32 size; diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 1a29015356e..7170ce43f8f 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -114,6 +114,7 @@ struct wg_parser_stream pthread_cond_t event_cond, event_empty_cond; GstBuffer *buffer; GstMapInfo map_info; + uint64_t buffer_serial_id; bool flushing, eos, enabled, has_tags, has_buffer, no_more_pads; @@ -367,6 +368,7 @@ static NTSTATUS wg_parser_stream_get_buffer(void *args) * that this will need modification to wg_parser_stream_notify_qos() as * well. */ + wg_buffer->serial_id = stream->buffer_serial_id; if ((wg_buffer->has_pts = GST_BUFFER_PTS_IS_VALID(buffer))) wg_buffer->pts = GST_BUFFER_PTS(buffer) / 100; if ((wg_buffer->has_duration = GST_BUFFER_DURATION_IS_VALID(buffer))) @@ -391,8 +393,9 @@ static NTSTATUS wg_parser_stream_copy_buffer(void *args) pthread_mutex_lock(&parser->mutex); - if (!stream->buffer) + if (!stream->buffer || stream->buffer_serial_id != params->serial_id) { + GST_DEBUG("Ignoring pre-flush copy op."); pthread_mutex_unlock(&parser->mutex); return VFW_E_WRONG_STATE; } @@ -754,6 +757,7 @@ static GstFlowReturn sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *bu } stream->buffer = buffer; + ++stream->buffer_serial_id; pthread_mutex_unlock(&parser->mutex); pthread_cond_signal(&stream->event_cond); diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index 4f41f1a1ceb..8cc2680cb4d 100644 --- a/dlls/winegstreamer/wm_reader.c +++ b/dlls/winegstreamer/wm_reader.c @@ -1802,7 +1802,7 @@ static HRESULT wm_reader_read_stream_sample(struct wm_reader *reader, struct wg_ ERR("Failed to get data pointer, hr %#lx.\n", hr); size = min(buffer->size - stream->current_buffer_offset, capacity); - if (!wg_parser_stream_copy_buffer(stream->wg_stream, data, stream->current_buffer_offset, size)) + if (!wg_parser_stream_copy_buffer(stream->wg_stream, buffer->serial_id, data, stream->current_buffer_offset, size)) { /* The GStreamer pin has been flushed. */ INSSBuffer_Release(*sample); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10256