GST_DEBUG_CATEGORY_STATIC (wavparse_debug);
#define GST_CAT_DEFAULT (wavparse_debug)
+#define GST_RIFF_TAG_Fake GST_MAKE_FOURCC ('F','a','k','e')
+
#define GST_BWF_TAG_iXML GST_MAKE_FOURCC ('i','X','M','L')
#define GST_BWF_TAG_qlty GST_MAKE_FOURCC ('q','l','t','y')
#define GST_BWF_TAG_mext GST_MAKE_FOURCC ('m','e','x','t')
#define GST_BWF_TAG_link GST_MAKE_FOURCC ('l','i','n','k')
#define GST_BWF_TAG_axml GST_MAKE_FOURCC ('a','x','m','l')
+/* Data size chunk of RF64,
+ * see http://tech.ebu.ch/docs/tech/tech3306-2009.pdf */
+#define GST_RS64_TAG_DS64 GST_MAKE_FOURCC ('d','s','6','4')
+
static void gst_wavparse_dispose (GObject * object);
static gboolean gst_wavparse_sink_activate (GstPad * sinkpad,
not_wav:
{
GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
- ("File is not a WAVE file: %" GST_FOURCC_FORMAT,
- GST_FOURCC_ARGS (doctype)));
+ ("File is not a WAVE file: 0x%" G_GINT32_MODIFIER "x", doctype));
return FALSE;
}
}
*bytepos = gst_util_uint64_scale_ceil (ts, (guint64) wav->bps, GST_SECOND);
return TRUE;
} else if (wav->fact) {
- guint64 bps =
- gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact);
+ guint64 bps = gst_util_uint64_scale (wav->datasize, wav->rate, wav->fact);
*bytepos = gst_util_uint64_scale_ceil (ts, bps, GST_SECOND);
return TRUE;
}
gboolean update;
GstSegment seeksegment = { 0, };
gint64 last_stop;
+ guint32 seqnum = 0;
if (event) {
GST_DEBUG_OBJECT (wav, "doing seek with event");
gst_event_parse_seek (event, &rate, &format, &flags,
&cur_type, &cur, &stop_type, &stop);
+ seqnum = gst_event_get_seqnum (event);
/* no negative rates yet */
if (rate < 0.0)
/* BYTE seek event */
event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, cur,
stop_type, stop);
+ gst_event_set_seqnum (event, seqnum);
res = gst_pad_push_event (wav->sinkpad, event);
}
return res;
* as it completes one iteration (and thus might block when the sink is
* blocking in preroll). */
if (flush) {
+ GstEvent *fevent;
GST_DEBUG_OBJECT (wav, "sending flush start");
- gst_pad_push_event (wav->srcpad, gst_event_new_flush_start ());
+
+ fevent = gst_event_new_flush_start ();
+ gst_event_set_seqnum (fevent, seqnum);
+ gst_pad_push_event (wav->sinkpad, gst_event_ref (fevent));
+ gst_pad_push_event (wav->srcpad, fevent);
} else {
gst_pad_pause_task (wav->sinkpad);
}
if (gst_pad_peer_query_duration (wav->sinkpad, bformat, &upstream_size))
wav->end_offset = MIN (wav->end_offset, upstream_size);
+ if (wav->datasize > 0 && wav->end_offset > wav->datastart + wav->datasize)
+ wav->end_offset = wav->datastart + wav->datasize;
+
/* this is the range of bytes we will use for playback */
wav->offset = MIN (wav->offset, wav->end_offset);
wav->dataleft = wav->end_offset - wav->offset;
/* prepare for streaming again */
if (flush) {
+ GstEvent *fevent;
+
/* if we sent a FLUSH_START, we now send a FLUSH_STOP */
GST_DEBUG_OBJECT (wav, "sending flush stop");
- gst_pad_push_event (wav->srcpad, gst_event_new_flush_stop (TRUE));
+
+ fevent = gst_event_new_flush_stop (TRUE);
+ gst_event_set_seqnum (fevent, seqnum);
+ gst_pad_push_event (wav->sinkpad, gst_event_ref (fevent));
+ gst_pad_push_event (wav->srcpad, fevent);
}
/* now we did the seek and can activate the new segment values */
if (wav->start_segment)
gst_event_unref (wav->start_segment);
wav->start_segment = gst_event_new_segment (&wav->segment);
+ gst_event_set_seqnum (wav->start_segment, seqnum);
/* mark discont if we are going to stream from another position. */
if (last_stop != wav->segment.position) {
return TRUE;
} else if (wav->fact) {
wav->duration =
- gst_util_uint64_scale_int_ceil (GST_SECOND, wav->fact, wav->rate);
+ gst_util_uint64_scale_ceil (GST_SECOND, wav->fact, wav->rate);
GST_INFO_OBJECT (wav, "Got duration (fact) %" GST_TIME_FORMAT,
GST_TIME_ARGS (wav->duration));
return TRUE;
while (size >= 8) {
ltag = GST_READ_UINT32_LE (data + offset);
lsize = GST_READ_UINT32_LE (data + offset + 4);
+
+ if (lsize + 8 > size) {
+ GST_WARNING_OBJECT (wav, "Invalid adtl size: %u + 8 > %u", lsize, size);
+ return FALSE;
+ }
+
switch (ltag) {
case GST_RIFF_TAG_labl:
gst_wavparse_labl_chunk (wav, data + offset, size);
#define MAX_BUFFER_SIZE 4096
+static gboolean
+parse_ds64 (GstWavParse * wav, GstBuffer * buf)
+{
+ GstMapInfo map;
+ guint32 dataSizeLow, dataSizeHigh;
+ guint32 sampleCountLow, sampleCountHigh;
+
+ gst_buffer_map (buf, &map, GST_MAP_READ);
+ dataSizeLow = GST_READ_UINT32_LE (map.data + 2 * 4);
+ dataSizeHigh = GST_READ_UINT32_LE (map.data + 3 * 4);
+ sampleCountLow = GST_READ_UINT32_LE (map.data + 4 * 4);
+ sampleCountHigh = GST_READ_UINT32_LE (map.data + 5 * 4);
+ gst_buffer_unmap (buf, &map);
+ if (dataSizeHigh != 0xFFFFFFFF && dataSizeLow != 0xFFFFFFFF) {
+ wav->datasize = ((guint64) dataSizeHigh << 32) | dataSizeLow;
+ }
+ if (sampleCountHigh != 0xFFFFFFFF && sampleCountLow != 0xFFFFFFFF) {
+ wav->fact = ((guint64) sampleCountHigh << 32) | sampleCountLow;
+ }
+
+ GST_DEBUG_OBJECT (wav, "Got 'ds64' TAG, datasize : %" G_GINT64_FORMAT
+ " fact: %" G_GINT64_FORMAT, wav->datasize, wav->fact);
+ return TRUE;
+}
+
static GstFlowReturn
gst_wavparse_stream_headers (GstWavParse * wav)
{
gboolean gotdata = FALSE;
GstCaps *caps = NULL;
gchar *codec_name = NULL;
- GstEvent **event_p;
gint64 upstream_size = 0;
GstStructure *s;
tag == GST_RIFF_TAG_id3 || tag == GST_RIFF_TAG_IDVX ||
tag == GST_BWF_TAG_iXML || tag == GST_BWF_TAG_qlty ||
tag == GST_BWF_TAG_mext || tag == GST_BWF_TAG_levl ||
- tag == GST_BWF_TAG_link || tag == GST_BWF_TAG_axml) {
+ tag == GST_BWF_TAG_link || tag == GST_BWF_TAG_axml ||
+ tag == GST_RIFF_TAG_Fake) {
GST_DEBUG_OBJECT (wav, "skipping %" GST_FOURCC_FORMAT " chunk",
GST_FOURCC_ARGS (tag));
gst_buffer_unref (buf);
continue;
}
+ if (tag == GST_RS64_TAG_DS64) {
+ if (!parse_ds64 (wav, buf))
+ goto fail;
+ else
+ continue;
+ }
+
if (tag != GST_RIFF_TAG_fmt)
goto invalid_wav;
}
GST_INFO_OBJECT (wav,
- "Got TAG: %" GST_FOURCC_FORMAT ", offset %" G_GUINT64_FORMAT,
- GST_FOURCC_ARGS (tag), wav->offset);
+ "Got TAG: %" GST_FOURCC_FORMAT ", offset %" G_GUINT64_FORMAT ", size %"
+ G_GUINT32_FORMAT, GST_FOURCC_ARGS (tag), wav->offset, size);
+
+ /* Maximum valid size is INT_MAX */
+ if (size & 0x80000000) {
+ GST_WARNING_OBJECT (wav, "Invalid size, clipping to 0x7fffffff");
+ size = 0x7fffffff;
+ }
+
+ /* Clip to upstream size if known */
+ if (wav->datasize > 0 && size + wav->offset > wav->datasize) {
+ GST_WARNING_OBJECT (wav, "Clipping chunk size to file size");
+ size = wav->datasize - wav->offset;
+ }
/* 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
*/
switch (tag) {
case GST_RIFF_TAG_data:{
+ guint64 size64;
+
GST_DEBUG_OBJECT (wav, "Got 'data' TAG, size : %u", size);
+ size64 = size;
if (wav->ignore_length) {
GST_DEBUG_OBJECT (wav, "Ignoring length");
- size = 0;
+ size64 = 0;
}
if (wav->streaming) {
gst_adapter_flush (wav->adapter, 8);
}
wav->offset += 8;
wav->datastart = wav->offset;
+ /* use size from ds64 chunk if available */
+ if (size64 == -1 && wav->datasize > 0) {
+ GST_DEBUG_OBJECT (wav, "Using ds64 datasize");
+ size64 = wav->datasize;
+ }
/* If size is zero, then the data chunk probably actually extends to
the end of the file */
- if (size == 0 && upstream_size) {
- size = upstream_size - wav->datastart;
+ if (size64 == 0 && upstream_size) {
+ size64 = upstream_size - wav->datastart;
}
/* Or the file might be truncated */
else if (upstream_size) {
- size = MIN (size, (upstream_size - wav->datastart));
+ size64 = MIN (size64, (upstream_size - wav->datastart));
}
- wav->datasize = (guint64) size;
- wav->dataleft = (guint64) size;
- wav->end_offset = size + wav->datastart;
+ wav->datasize = size64;
+ wav->dataleft = size64;
+ wav->end_offset = size64 + wav->datastart;
if (!wav->streaming) {
/* We will continue parsing tags 'till end */
- wav->offset += size;
+ wav->offset += size64;
}
- GST_DEBUG_OBJECT (wav, "datasize = %u", size);
+ GST_DEBUG_OBJECT (wav, "datasize = %" G_GUINT64_FORMAT, size64);
break;
}
case GST_RIFF_TAG_fact:{
- if (wav->format != GST_RIFF_WAVE_FORMAT_MPEGL12 &&
+ if (wav->fact == 0 &&
+ wav->format != GST_RIFF_WAVE_FORMAT_MPEGL12 &&
wav->format != GST_RIFF_WAVE_FORMAT_MPEGL3) {
const guint data_size = 4;
wav->fact = GUINT32_FROM_LE (wav->fact);
gst_buffer_unref (buf);
}
- GST_DEBUG_OBJECT (wav, "have fact %u", wav->fact);
+ GST_DEBUG_OBJECT (wav, "have fact %" G_GUINT64_FORMAT, wav->fact);
wav->offset += 8 + GST_ROUND_UP_2 (size);
break;
} else {
break;
}
case GST_RIFF_LIST_adtl:{
- const gint data_size = size;
+ const gint data_size = size - 4;
GST_INFO_OBJECT (wav, "Have 'adtl' LIST, size %u", data_size);
if (wav->streaming) {
const guint8 *data = NULL;
gst_adapter_flush (wav->adapter, 12);
+ wav->offset += 12;
data = gst_adapter_map (wav->adapter, data_size);
gst_wavparse_adtl_chunk (wav, data, data_size);
gst_adapter_unmap (wav->adapter);
gst_buffer_unref (buf);
buf = NULL;
+ wav->offset += 12;
if ((res =
- gst_pad_pull_range (wav->sinkpad, wav->offset + 12,
+ gst_pad_pull_range (wav->sinkpad, wav->offset,
data_size, &buf)) != GST_FLOW_OK)
goto header_read_error;
gst_buffer_map (buf, &map, GST_MAP_READ);
* the right newsegment event downstream. */
gst_wavparse_perform_seek (wav, wav->seek_event);
/* remove pending event */
- event_p = &wav->seek_event;
- gst_event_replace (event_p, NULL);
+ gst_event_replace (&wav->seek_event, NULL);
/* we just started, we are discont */
wav->discont = TRUE;
/* ERROR */
exit:
{
- if (codec_name)
- g_free (codec_name);
- if (header)
- g_free (header);
+ g_free (codec_name);
+ g_free (header);
if (caps)
gst_caps_unref (caps);
return res;
{
GstWavParse *wav = GST_WAVPARSE (element);
gboolean res = FALSE;
- GstEvent **event_p;
GST_DEBUG_OBJECT (wav, "received event %s", GST_EVENT_TYPE_NAME (event));
} else {
GST_DEBUG_OBJECT (wav, "queuing seek for later");
- event_p = &wav->seek_event;
- gst_event_replace (event_p, event);
+ gst_event_replace (&wav->seek_event, event);
/* we always return true */
res = TRUE;
s = gst_caps_get_structure (caps, 0);
if (!gst_structure_has_name (s, "audio/x-dts"))
return FALSE;
- if (prob >= GST_TYPE_FIND_LIKELY)
+ /* typefind behavior for DTS:
+ * MAXIMUM: multiple frame syncs detected, certainly DTS
+ * LIKELY: single frame sync at offset 0. Maybe DTS?
+ * POSSIBLE: single frame sync, not at offset 0. Highly unlikely
+ * to be DTS. */
+ if (prob > GST_TYPE_FIND_LIKELY)
return TRUE;
- /* DTS at non-0 offsets and without second sync may yield POSSIBLE .. */
- if (prob < GST_TYPE_FIND_POSSIBLE)
+ if (prob <= GST_TYPE_FIND_POSSIBLE)
return FALSE;
- /* .. in which case we want at least a valid-looking rate and channels */
+ /* for maybe, check for at least a valid-looking rate and channels */
if (!gst_structure_has_field (s, "channels"))
return FALSE;
/* and for extra assurance we could also check the rate from the DTS frame
guint av;
if ((av = gst_adapter_available (wav->adapter)) > 0) {
- wav->dataleft = av;
- wav->end_offset = wav->offset + av;
ret = gst_wavparse_stream_data (wav);
}
}
if (stop > 0) {
end_offset = stop;
- segment.stop -= wav->datastart;
- segment.stop = MAX (stop, 0);
+ stop -= wav->datastart;
+ stop = MAX (stop, 0);
}
if (wav->segment.format == GST_FORMAT_TIME) {
guint64 bps = wav->bps;
/* and set up streaming thread for next one */
wav->offset = offset;
wav->end_offset = end_offset;
- if (wav->end_offset > 0) {
+
+ if (wav->datasize > 0 && (wav->end_offset == -1
+ || wav->end_offset > wav->datastart + wav->datasize))
+ wav->end_offset = wav->datastart + wav->datasize;
+
+ if (wav->end_offset != -1) {
wav->dataleft = wav->end_offset - wav->offset;
} else {
/* infinity; upstream will EOS when done */
static const GstFormat *
gst_wavparse_get_formats (GstPad * pad)
{
- static GstFormat formats[] = {
+ static const GstFormat formats[] = {
GST_FORMAT_TIME,
GST_FORMAT_BYTES,
GST_FORMAT_DEFAULT, /* a "frame", ie a set of samples per Hz */