On Mon, May 03, 2021 at 11:35:20AM -0500, Zebediah Figura (she/her) wrote:
On 5/3/21 7:53 AM, Connor McAdams wrote:
For mp3 audio streams, continuously call gst_pad_query_duration until the duration arrives. Fixes issue of incorrect song duration in Fallout: New Vegas.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com
dlls/winegstreamer/wg_parser.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-)
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 1653701506c..1dae3bfb922 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1504,13 +1504,22 @@ static gboolean src_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) return ret; } -static LONGLONG query_duration(GstPad *pad) +static LONGLONG query_duration(GstPad *pad, gboolean query_duration) { gint64 duration, byte_length; if (gst_pad_query_duration(pad, GST_FORMAT_TIME, &duration)) return duration / 100;
- if (query_duration)
- {
for (;;)
{
if (gst_pad_query_duration(pad, GST_FORMAT_TIME, &duration))
return duration / 100;
}
- }
We don't want to risk an infinite loop here, or busy-wait. We should wait for duration-changed with a time-out.
We also should be calling query_duration when polling, rather than gst_pad_query_duration(). That is, this loop should be in wg_parser_connect().
Using something like pthread_cond_timedwait? I was waiting on the duration-changed event prior to my current solution of continuously calling gst_pad_query_duration(), but it was still querying too early and getting incorrect results. If we want to continuously call query_duration(), should we have some way for it to return if it converted from byte length and try again in that circumstance? It seems that converting from byte length just doesn't work for this format.
WARN("Failed to query time duration; trying to convert from byte length.\n"); /* To accurately get a duration for the stream, we want to only consider the
@@ -1569,6 +1578,7 @@ static HRESULT CDECL wg_parser_connect(struct wg_parser *parser, uint64_t file_s for (i = 0; i < parser->stream_count; ++i) { struct wg_parser_stream *stream = parser->streams[i];
gboolean duration_wait = FALSE; while (!stream->has_caps && !parser->error) pthread_cond_wait(&parser->init_cond, &parser->mutex);
@@ -1577,12 +1587,25 @@ static HRESULT CDECL wg_parser_connect(struct wg_parser *parser, uint64_t file_s pthread_mutex_unlock(&parser->mutex); return E_FAIL; }
/*
* On MP3 audio streams, sometimes the duration value takes time to be
* set. In this case, we need to continuously call query_duration
* until we get it.
*/
if (input_format.major_type == WG_MAJOR_TYPE_AUDIO &&
input_format.u.audio.format >= WG_AUDIO_FORMAT_MPEG1_LAYER1 &&
input_format.u.audio.format <= WG_AUDIO_FORMAT_MPEG1_LAYER3)
{
duration_wait = TRUE;
}
I don't think there's really a good reason to limit this to MPEG audio. If there are other demuxers (now or in the future, or even in the past) that force us to wait for duration, we want to do so.
Good point, I can probably do something similar to how certain decoders are blacklisted/disabled, and instead have certain decoders have a wait duration wait behavior flag.
/* GStreamer doesn't actually provide any guarantees about when duration * is available, even for seekable streams. However, many elements (e.g. * avidemux, wavparse, qtdemux) in practice record duration before * fixing caps, so as a heuristic, wait until we get caps before trying * to query for duration. */
stream->duration = query_duration(stream->their_src);
stream->duration = query_duration(stream->their_src, duration_wait); } pthread_mutex_unlock(&parser->mutex);
Thank you very much for the review.