gst/wavparse/gstwavparse.*: Use information from 'fact' chunk for length calculation of compressed samples. Calculate...

Original commit message from CVS:
* gst/wavparse/gstwavparse.c: (gst_wavparse_reset),
(gst_wavparse_other), (gst_wavparse_perform_seek),
(gst_wavparse_get_upstream_size), (gst_wavparse_stream_headers),
(gst_wavparse_add_src_pad), (gst_wavparse_stream_data),
(gst_wavparse_pad_query):
* gst/wavparse/gstwavparse.h:
Use information from 'fact' chunk for length calculation of compressed
samples. Calculate bps if bogus value is found in wav header (embeded
mp2/mp3).
This commit is contained in:
Stefan Kost 2006-07-24 13:40:56 +00:00
parent 162b374ae2
commit 26e4a48271
4 changed files with 146 additions and 42 deletions

View file

@ -1,3 +1,16 @@
2006-07-24 Stefan Kost,,, <set EMAIL_ADDRESS environment variable>
* gst/wavparse/gstwavparse.c: (gst_wavparse_reset),
(gst_wavparse_other), (gst_wavparse_perform_seek),
(gst_wavparse_get_upstream_size), (gst_wavparse_stream_headers),
(gst_wavparse_add_src_pad), (gst_wavparse_stream_data),
(gst_wavparse_pad_query):
* gst/wavparse/gstwavparse.h:
Use information from 'fact' chunk for length calculation of compressed
samples. Calculate bps if bogus value is found in wav header (embeded
mp2/mp3).
2006-07-24 Tim-Philipp Müller <tim at centricular dot net> 2006-07-24 Tim-Philipp Müller <tim at centricular dot net>
Based on patch by: Joni Valtanen <joni dot valtanen at movial fi> Based on patch by: Joni Valtanen <joni dot valtanen at movial fi>

2
common

@ -1 +1 @@
Subproject commit 743c74bf92546638d3f4272fd5525bf6ef71f794 Subproject commit ef97fb3278d98a1fdb32e5c6b2a7467116ffc160

View file

@ -45,6 +45,11 @@
* Last reviewed on 2006-03-03 (0.10.3) * Last reviewed on 2006-03-03 (0.10.3)
*/ */
/*
* TODO:
* http://replaygain.hydrogenaudio.org/file_format_wav.html
*/
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
#endif #endif
@ -228,6 +233,7 @@ gst_wavparse_reset (GstWavParse * wavparse)
wavparse->channels = 0; wavparse->channels = 0;
wavparse->blockalign = 0; wavparse->blockalign = 0;
wavparse->bps = 0; wavparse->bps = 0;
wavparse->fact = 0;
wavparse->offset = 0; wavparse->offset = 0;
wavparse->end_offset = 0; wavparse->end_offset = 0;
wavparse->dataleft = 0; wavparse->dataleft = 0;
@ -693,7 +699,8 @@ gst_wavparse_other (GstWavParse * wav)
} }
} }
wav->datasize = (guint64) length; wav->datasize = (guint64) length;
break; GST_DEBUG_OBJECT (wav, "datasize = %ld", length)
break;
case GST_RIFF_TAG_cue: case GST_RIFF_TAG_cue:
if (!gst_riff_read_skip (wav)) { if (!gst_riff_read_skip (wav)) {
@ -775,7 +782,7 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
gint64 cur, stop, upstream_size; gint64 cur, stop, upstream_size;
gboolean flush; gboolean flush;
gboolean update; gboolean update;
GstSegment seeksegment; GstSegment seeksegment = { 0, };
if (event) { if (event) {
GST_DEBUG_OBJECT (wav, "doing seek with event"); GST_DEBUG_OBJECT (wav, "doing seek with event");
@ -825,23 +832,36 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
cur_type, cur, stop_type, stop, &update); cur_type, cur, stop_type, stop, &update);
} }
if ((stop = seeksegment.stop) == -1) if ((stop = seeksegment.stop) == GST_CLOCK_TIME_NONE)
stop = seeksegment.duration; stop = seeksegment.duration;
if (cur_type != GST_SEEK_TYPE_NONE) { GST_DEBUG_OBJECT (wav, "cur_type =%d", cur_type);
if ((cur_type != GST_SEEK_TYPE_NONE) &&
(seeksegment.last_stop != GST_CLOCK_TIME_NONE)) {
wav->offset = wav->offset =
gst_util_uint64_scale_int (seeksegment.last_stop, wav->bps, GST_SECOND); gst_util_uint64_scale_int (seeksegment.last_stop, wav->bps, GST_SECOND);
wav->offset -= wav->offset % wav->bytes_per_sample; GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
wav->offset -= (wav->offset % wav->bytes_per_sample);
GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
wav->offset += wav->datastart; wav->offset += wav->datastart;
GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
} else {
GST_DEBUG_OBJECT (wav, "last_stop == -1");
wav->offset = wav->datastart;
GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
} }
if (stop != -1) { if (stop != GST_CLOCK_TIME_NONE) {
wav->end_offset = gst_util_uint64_scale_int (stop, wav->bps, GST_SECOND); wav->end_offset = gst_util_uint64_scale_int (stop, wav->bps, GST_SECOND);
wav->end_offset += GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
wav->bytes_per_sample - (wav->end_offset % wav->bytes_per_sample); wav->end_offset -= (wav->end_offset % wav->bytes_per_sample);
GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
wav->end_offset += wav->datastart; wav->end_offset += wav->datastart;
GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
} else { } else {
GST_DEBUG_OBJECT (wav, "stop == -1");
wav->end_offset = wav->datasize + wav->datastart; wav->end_offset = wav->datasize + wav->datastart;
GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
} }
/* make sure filesize is not exceeded due to rounding errors or so, /* make sure filesize is not exceeded due to rounding errors or so,
@ -979,13 +999,20 @@ gst_wavparse_peek_chunk (GstWavParse * wav, guint32 * tag, guint32 * size)
} }
} }
/* FIXME: remove once -base 0.10.9 is out */ static gboolean
#ifndef GST_RIFF_TAG_bext gst_wavparse_get_upstream_size (GstWavParse * wav, gint64 * len)
#define GST_RIFF_TAG_bext GST_MAKE_FOURCC ('b', 'e', 'x', 't') {
#endif gboolean res = FALSE;
#ifndef GST_RIFF_TAG_BEXT GstFormat fmt = GST_FORMAT_BYTES;
#define GST_RIFF_TAG_BEXT GST_MAKE_FOURCC ('B', 'E', 'X', 'T') GstPad *peer;
#endif
if ((peer = gst_pad_get_peer (wav->sinkpad))) {
res = gst_pad_query_duration (peer, &fmt, len);
gst_object_unref (peer);
}
return res;
}
static GstFlowReturn static GstFlowReturn
gst_wavparse_stream_headers (GstWavParse * wav) gst_wavparse_stream_headers (GstWavParse * wav)
@ -1048,8 +1075,7 @@ gst_wavparse_stream_headers (GstWavParse * wav)
/* Note: gst_riff_create_audio_caps might need to fix values in /* Note: gst_riff_create_audio_caps might need to fix values in
* the header header depending on the format, so call it first */ * the header header depending on the format, so call it first */
caps = caps = gst_riff_create_audio_caps (header->format, NULL, header, extra,
gst_riff_create_audio_caps (header->format, NULL, header, extra,
NULL, &codec_name); NULL, &codec_name);
if (extra) if (extra)
@ -1058,24 +1084,29 @@ gst_wavparse_stream_headers (GstWavParse * wav)
wav->format = header->format; wav->format = header->format;
wav->rate = header->rate; wav->rate = header->rate;
wav->channels = header->channels; wav->channels = header->channels;
wav->blockalign = header->blockalign;
wav->depth = header->size;
wav->bps = header->av_bps;
g_free (header);
if (wav->channels == 0) if (wav->channels == 0)
goto no_channels; goto no_channels;
wav->blockalign = header->blockalign; if (wav->bps == 0 && (wav->format == GST_RIFF_WAVE_FORMAT_MPEGL12 ||
wav->width = (header->blockalign * 8) / header->channels; wav->format == GST_RIFF_WAVE_FORMAT_MPEGL3)) {
wav->depth = header->size; /* Note: ugly workaround for mp2/mp3 embedded in wav, that relies on the
wav->bps = header->av_bps; * bitrate inside the mpeg stream */
/* wav->bps = 1; */
if (wav->bps <= 0) GST_INFO ("WAV file with bps==0 and format=mp2/3");
goto no_bitrate; }
wav->width = (wav->blockalign * 8) / wav->channels;
wav->bytes_per_sample = wav->channels * wav->width / 8; wav->bytes_per_sample = wav->channels * wav->width / 8;
if (wav->bytes_per_sample <= 0) if (wav->bytes_per_sample <= 0)
goto no_bytes_per_sample; goto no_bytes_per_sample;
g_free (header);
if (!caps) if (!caps)
goto unknown_format; goto unknown_format;
@ -1083,6 +1114,11 @@ gst_wavparse_stream_headers (GstWavParse * wav)
GST_DEBUG_OBJECT (wav, "width = %u", (guint) wav->width); GST_DEBUG_OBJECT (wav, "width = %u", (guint) wav->width);
GST_DEBUG_OBJECT (wav, "depth = %u", (guint) wav->depth); GST_DEBUG_OBJECT (wav, "depth = %u", (guint) wav->depth);
GST_DEBUG_OBJECT (wav, "bps = %u", (guint) wav->bps); GST_DEBUG_OBJECT (wav, "bps = %u", (guint) wav->bps);
GST_DEBUG_OBJECT (wav, "frequency = %d", wav->rate);
GST_DEBUG_OBJECT (wav, "channels = %u", (guint) wav->channels);
GST_DEBUG_OBJECT (wav, "bytes_per_sample = %u", wav->bytes_per_sample);
GST_DEBUG_OBJECT (wav, "caps = %" GST_PTR_FORMAT, caps);
/* create pad later so we can sniff the first few bytes /* create pad later so we can sniff the first few bytes
* of the real data and correct our caps if necessary */ * of the real data and correct our caps if necessary */
@ -1101,12 +1137,12 @@ gst_wavparse_stream_headers (GstWavParse * wav)
codec_name = NULL; codec_name = NULL;
} }
GST_DEBUG_OBJECT (wav, "frequency %d, channels %d", wav->rate,
wav->channels);
} }
/* loop headers until we get data */ /* loop headers until we get data */
while (!gotdata) { while (!gotdata) {
gint64 upstream_size = 0;
if (wav->streaming) { if (wav->streaming) {
if (!gst_wavparse_peek_chunk_info (wav, &tag, &size)) if (!gst_wavparse_peek_chunk_info (wav, &tag, &size))
return GST_FLOW_OK; return GST_FLOW_OK;
@ -1119,21 +1155,21 @@ gst_wavparse_stream_headers (GstWavParse * wav)
size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4); size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
} }
gst_wavparse_get_upstream_size (wav, &upstream_size);
/* /*
wav is a st00pid format, we don't know for sure where data starts. wav is a st00pid format, we don't know for sure where data starts.
So we have to go bit by bit until we find the 'data' header So we have to go bit by bit until we find the 'data' header
*/ */
switch (tag) { switch (tag) {
/* TODO : Implement the various cases */ /* TODO : Implement the various cases */
case GST_RIFF_TAG_data:{ case GST_RIFF_TAG_data:{
GstFormat fmt; GstFormat fmt;
gint64 upstream_size;
GST_DEBUG_OBJECT (wav, "Got 'data' TAG, size : %d", size); GST_DEBUG_OBJECT (wav, "Got 'data' TAG, size : %d", size);
gotdata = TRUE;
if (wav->streaming) { if (wav->streaming) {
gst_adapter_flush (wav->adapter, 8); gst_adapter_flush (wav->adapter, 8);
gotdata = TRUE;
} else { } else {
gst_buffer_unref (buf); gst_buffer_unref (buf);
} }
@ -1141,12 +1177,41 @@ gst_wavparse_stream_headers (GstWavParse * wav)
wav->datastart = wav->offset; wav->datastart = wav->offset;
/* file might be truncated */ /* file might be truncated */
fmt = GST_FORMAT_BYTES; fmt = GST_FORMAT_BYTES;
if (gst_pad_query_peer_duration (wav->sinkpad, &fmt, &upstream_size)) { if (upstream_size) {
size = MIN (size, (upstream_size - wav->datastart)); size = MIN (size, (upstream_size - wav->datastart));
} }
wav->datasize = size; wav->datasize = (guint64) size;
wav->dataleft = size; wav->dataleft = (guint64) size;
wav->end_offset = size + wav->datastart; wav->end_offset = size + wav->datastart;
if (!wav->streaming) {
/* We will continue parsing tags 'till end */
wav->offset += size;
}
GST_DEBUG_OBJECT (wav, "datasize = %ld", size);
break;
}
case GST_RIFF_TAG_fact:{
/* number of samples (for compressed formats) */
if (wav->streaming) {
const guint8 *data = NULL;
if (gst_adapter_available (wav->adapter) < 8 + 4) {
return GST_FLOW_OK;
}
gst_adapter_flush (wav->adapter, 8);
data = gst_adapter_peek (wav->adapter, 4);
wav->fact = GST_READ_UINT32_LE (data);
gst_adapter_flush (wav->adapter, 4);
} else {
gst_buffer_unref (buf);
if ((res =
gst_pad_pull_range (wav->sinkpad, wav->offset + 8, 4,
&buf)) != GST_FLOW_OK)
goto header_read_error;
wav->fact = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
gst_buffer_unref (buf);
}
wav->offset += 8 + 4;
break; break;
} }
default: default:
@ -1163,10 +1228,24 @@ gst_wavparse_stream_headers (GstWavParse * wav)
gst_buffer_unref (buf); gst_buffer_unref (buf);
} }
} }
if (upstream_size && (wav->offset >= upstream_size)) {
/* Now we are gone through the whole file */
gotdata = TRUE;
}
} }
GST_DEBUG_OBJECT (wav, "Finished parsing headers"); GST_DEBUG_OBJECT (wav, "Finished parsing headers");
if (wav->bps <= 0 && wav->fact) {
wav->bps =
(guint32) gst_util_uint64_scale ((guint64) wav->rate, wav->datasize,
(guint64) wav->fact);
GST_DEBUG_OBJECT (wav, "calculated bps : %ld", wav->bps);
}
if (wav->bps <= 0)
goto no_bitrate;
duration = gst_util_uint64_scale_int (wav->datasize, GST_SECOND, wav->bps); duration = gst_util_uint64_scale_int (wav->datasize, GST_SECOND, wav->bps);
GST_DEBUG_OBJECT (wav, "Got duration %" GST_TIME_FORMAT, GST_DEBUG_OBJECT (wav, "Got duration %" GST_TIME_FORMAT,
GST_TIME_ARGS (duration)); GST_TIME_ARGS (duration));
@ -1317,7 +1396,7 @@ gst_wavparse_add_src_pad (GstWavParse * wav, GstBuffer * buf)
const guint8 dts_marker[] = { 0xFF, 0x1F, 0x00, 0xE8, 0xF1, 0x07 }; const guint8 dts_marker[] = { 0xFF, 0x1F, 0x00, 0xE8, 0xF1, 0x07 };
s = gst_caps_get_structure (wav->caps, 0); s = gst_caps_get_structure (wav->caps, 0);
if (gst_structure_has_name (s, "audio/x-raw-int") && if (s && gst_structure_has_name (s, "audio/x-raw-int") && buf &&
GST_BUFFER_SIZE (buf) > 6 && GST_BUFFER_SIZE (buf) > 6 &&
memcmp (GST_BUFFER_DATA (buf), dts_marker, 6) == 0) { memcmp (GST_BUFFER_DATA (buf), dts_marker, 6) == 0) {
@ -1465,6 +1544,10 @@ found_eos:
gst_message_new_segment_done (GST_OBJECT (wav), GST_FORMAT_TIME, gst_message_new_segment_done (GST_OBJECT (wav), GST_FORMAT_TIME,
stop)); stop));
} else { } else {
if (G_UNLIKELY (wav->first)) {
wav->first = FALSE;
gst_wavparse_add_src_pad (wav, NULL);
}
gst_pad_push_event (wav->srcpad, gst_event_new_eos ()); gst_pad_push_event (wav->srcpad, gst_event_new_eos ());
} }
return GST_FLOW_WRONG_STATE; return GST_FLOW_WRONG_STATE;
@ -1475,13 +1558,15 @@ pull_error:
if (res == GST_FLOW_UNEXPECTED) if (res == GST_FLOW_UNEXPECTED)
goto found_eos; goto found_eos;
GST_DEBUG_OBJECT (wav, "Error getting %" G_GINT64_FORMAT " bytes from the " GST_WARNING_OBJECT (wav,
"Error getting %" G_GINT64_FORMAT " bytes from the "
"sinkpad (dataleft = %" G_GINT64_FORMAT ")", desired, wav->dataleft); "sinkpad (dataleft = %" G_GINT64_FORMAT ")", desired, wav->dataleft);
return res; return res;
} }
push_error: push_error:
{ {
GST_DEBUG_OBJECT (wav, "Error pushing on srcpad"); GST_WARNING_OBJECT (wav, "Error pushing on srcpad %p, is linked? = %d",
wav->srcpad, gst_pad_is_linked (wav->srcpad));
return res; return res;
} }
} }
@ -1761,10 +1846,15 @@ gst_wavparse_pad_query (GstPad * pad, GstQuery * query)
gst_query_parse_duration (query, &format, NULL); gst_query_parse_duration (query, &format, NULL);
switch (format) { switch (format) {
case GST_FORMAT_TIME: case GST_FORMAT_TIME:{
res &= if (wav->fact) {
gst_wavparse_pad_convert (pad, GST_FORMAT_BYTES, endb, end = GST_SECOND * wav->fact / wav->rate;
&format, &end); } else {
res &=
gst_wavparse_pad_convert (pad, GST_FORMAT_BYTES, endb,
&format, &end);
}
}
break; break;
default: default:
format = GST_FORMAT_BYTES; format = GST_FORMAT_BYTES;

View file

@ -81,6 +81,7 @@ struct _GstWavParse {
guint16 blockalign; guint16 blockalign;
guint16 width; guint16 width;
guint32 bps; guint32 bps;
guint32 fact;
guint bytes_per_sample; guint bytes_per_sample;