summaryrefslogtreecommitdiffstats
path: root/gst/mpegdemux
diff options
context:
space:
mode:
Diffstat (limited to 'gst/mpegdemux')
-rw-r--r--gst/mpegdemux/gstmpegdefs.h65
-rw-r--r--gst/mpegdemux/gstmpegdemux.c295
-rw-r--r--gst/mpegdemux/gstmpegdemux.h3
-rw-r--r--gst/mpegdemux/gstmpegtsdemux.c102
4 files changed, 252 insertions, 213 deletions
diff --git a/gst/mpegdemux/gstmpegdefs.h b/gst/mpegdemux/gstmpegdefs.h
index 375f1dc0..40551137 100644
--- a/gst/mpegdemux/gstmpegdefs.h
+++ b/gst/mpegdemux/gstmpegdefs.h
@@ -147,39 +147,52 @@
* 0x0F-0x7F ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved
* 0x80-0xFF User Private
*/
-#define ST_RESERVED 0x00
-#define ST_VIDEO_MPEG1 0x01
-#define ST_VIDEO_MPEG2 0x02
-#define ST_AUDIO_MPEG1 0x03
-#define ST_AUDIO_MPEG2 0x04
-#define ST_PRIVATE_SECTIONS 0x05
-#define ST_PRIVATE_DATA 0x06
-#define ST_MHEG 0x07
-#define ST_DSMCC 0x08
-#define ST_H222_1 0x09
+#define ST_RESERVED 0x00
+#define ST_VIDEO_MPEG1 0x01
+#define ST_VIDEO_MPEG2 0x02
+#define ST_AUDIO_MPEG1 0x03
+#define ST_AUDIO_MPEG2 0x04
+#define ST_PRIVATE_SECTIONS 0x05
+#define ST_PRIVATE_DATA 0x06
+#define ST_MHEG 0x07
+#define ST_DSMCC 0x08
+#define ST_H222_1 0x09
/* later extensions */
-#define ST_AUDIO_AAC 0x0f
-#define ST_VIDEO_MPEG4 0x10
-#define ST_VIDEO_H264 0x1b
+#define ST_AUDIO_AAC 0x0f
+#define ST_VIDEO_MPEG4 0x10
+#define ST_VIDEO_H264 0x1b
/* Un-official Dirac extension */
-#define ST_VIDEO_DIRAC 0xd1
+#define ST_VIDEO_DIRAC 0xd1
/* private stream types */
-#define ST_PS_AUDIO_AC3 0x81
-#define ST_PS_AUDIO_DTS 0x8a
-#define ST_PS_AUDIO_LPCM 0x8b
+#define ST_PS_AUDIO_AC3 0x81
+#define ST_PS_AUDIO_DTS 0x8a
+#define ST_PS_AUDIO_LPCM 0x8b
#define ST_PS_DVD_SUBPICTURE 0xff
-/* Blu-ray PGS subpictures */
+/* Blu-ray related */
+#define ST_BD_AUDIO_LPCM 0x80
+#define ST_BD_AUDIO_AC3 0x81
+#define ST_BD_AUDIO_DTS 0x82
+#define ST_BD_AUDIO_AC3_TRUE_HD 0x83
+#define ST_BD_AUDIO_AC3_PLUS 0x84
+#define ST_BD_AUDIO_DTS_HD 0x85
#define ST_BD_PGS_SUBPICTURE 0x90
+#define ST_BD_IGS 0x91
+#define ST_BD_SUBTITLE 0x92
+#define ST_BD_SECONDARY_AC3_PLUS 0xa1
+#define ST_BD_SECONDARY_DTS_HD 0xa2
+
+/* VC1 extension */
+#define ST_VIDEO_VC1 0xea
/* HDV AUX stream mapping
* 0xA0 ISO/IEC 61834-11
* 0xA1 ISO/IEC 61834-11
*/
-#define ST_HDV_AUX_A 0xa0
-#define ST_HDV_AUX_V 0xa1
+#define ST_HDV_AUX_A 0xa0
+#define ST_HDV_AUX_V 0xa1
/* Un-official time-code stream */
#define ST_PS_TIMECODE 0xd2
@@ -202,16 +215,16 @@
/* sync:4 == 00xx ! pts:3 ! 1 ! pts:15 ! 1 | pts:15 ! 1 */
#define READ_TS(data, target, lost_sync_label) \
if ((*data & 0x01) != 0x01) goto lost_sync_label; \
- target = ((guint64) (*data++ & 0x0E)) << 29; \
- target |= ((guint64) (*data++ )) << 22; \
+ target = ((guint64) (*data++ & 0x0E)) << 29; \
+ target |= ((guint64) (*data++ )) << 22; \
if ((*data & 0x01) != 0x01) goto lost_sync_label; \
- target |= ((guint64) (*data++ & 0xFE)) << 14; \
- target |= ((guint64) (*data++ )) << 7; \
+ target |= ((guint64) (*data++ & 0xFE)) << 14; \
+ target |= ((guint64) (*data++ )) << 7; \
if ((*data & 0x01) != 0x01) goto lost_sync_label; \
target |= ((guint64) (*data++ & 0xFE)) >> 1;
/* some extra GstFlowReturn values used internally */
-#define GST_FLOW_NEED_MORE_DATA -100
-#define GST_FLOW_LOST_SYNC -101
+#define GST_FLOW_NEED_MORE_DATA -100
+#define GST_FLOW_LOST_SYNC -101
#endif /* __GST_MPEG_DEFS_H__ */
diff --git a/gst/mpegdemux/gstmpegdemux.c b/gst/mpegdemux/gstmpegdemux.c
index 75d5960f..5fe27711 100644
--- a/gst/mpegdemux/gstmpegdemux.c
+++ b/gst/mpegdemux/gstmpegdemux.c
@@ -53,7 +53,7 @@
#define MAX_DVD_AUDIO_STREAMS 8
#define MAX_DVD_SUBPICTURE_STREAMS 32
-#define BLOCK_SZ 4096
+#define BLOCK_SZ 32768
#define SCAN_SCR_SZ 12
#define SCAN_PTS_SZ 80
@@ -77,42 +77,6 @@ typedef enum
GST_DEBUG_CATEGORY_STATIC (gstflupsdemux_debug);
#define GST_CAT_DEFAULT (gstflupsdemux_debug)
-#ifndef GST_CHECK_VERSION
-#define GST_CHECK_VERSION(major,minor,micro) \
- (GST_VERSION_MAJOR > (major) || \
- (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR > (minor)) || \
- (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR == (minor) && \
- GST_VERSION_MICRO >= (micro)))
-#endif
-
-#if !GST_CHECK_VERSION(0,10,9)
-#define GST_BUFFER_IS_DISCONT(buffer) \
- (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT))
-#endif
-
-#if GST_CHECK_VERSION(0,10,6)
-#define HAVE_NEWSEG_FULL
-#else
-static GstBuffer *
-gst_adapter_take_buffer (GstAdapter * adapter, guint nbytes)
-{
- GstBuffer *buf = NULL;
-
- if (G_UNLIKELY (nbytes > adapter->size))
- return NULL;
-
- buf = gst_buffer_new_and_alloc (nbytes);
-
- if (G_UNLIKELY (!buf))
- return NULL;
-
- /* Slow... */
- memcpy (GST_BUFFER_DATA (buf), gst_adapter_peek (adapter, nbytes), nbytes);
-
- return buf;
-}
-#endif
-
/* elementfactory information */
static GstElementDetails flups_demux_details = {
"The Fluendo MPEG Program Stream Demuxer",
@@ -176,7 +140,7 @@ static GstStaticPadTemplate audio_template =
GST_PAD_SRC,
GST_PAD_SOMETIMES,
GST_STATIC_CAPS ("audio/mpeg, "
- "mpegversion = (int) 1;"
+ "mpegversion = (int) { 1, 4 };"
"audio/x-private1-lpcm; "
"audio/x-private1-ac3;" "audio/x-private1-dts;" "audio/ac3")
);
@@ -211,6 +175,7 @@ static void gst_flups_demux_loop (GstPad * pad);
static gboolean gst_flups_demux_src_event (GstPad * pad, GstEvent * event);
static gboolean gst_flups_demux_src_query (GstPad * pad, GstQuery * query);
+static const GstQueryType *gst_flups_demux_src_query_type (GstPad * pad);
static GstStateChangeReturn gst_flups_demux_change_state (GstElement * element,
GstStateChange transition);
@@ -220,9 +185,9 @@ static inline gboolean gst_flups_demux_scan_forward_ts (GstFluPSDemux * demux,
static inline gboolean gst_flups_demux_scan_backward_ts (GstFluPSDemux * demux,
guint64 * pos, SCAN_MODE mode, guint64 * rts);
-static void gst_flups_demux_send_segment_updates (GstFluPSDemux * demux,
+static inline void gst_flups_demux_send_segment_updates (GstFluPSDemux * demux,
GstClockTime new_time);
-static void gst_flups_demux_clear_times (GstFluPSDemux * demux);
+static inline void gst_flups_demux_clear_times (GstFluPSDemux * demux);
static GstElementClass *parent_class = NULL;
@@ -316,6 +281,9 @@ gst_flups_demux_init (GstFluPSDemux * demux)
demux->streams =
g_malloc0 (sizeof (GstFluPSStream *) * (GST_FLUPS_DEMUX_MAX_STREAMS));
+ demux->streams_found =
+ g_malloc0 (sizeof (GstFluPSStream *) * (GST_FLUPS_DEMUX_MAX_STREAMS));
+ demux->found_count = 0;
}
@@ -324,6 +292,7 @@ gst_flups_demux_finalize (GstFluPSDemux * demux)
{
gst_flups_demux_reset (demux);
g_free (demux->streams);
+ g_free (demux->streams_found);
G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (demux));
}
@@ -346,6 +315,9 @@ gst_flups_demux_reset (GstFluPSDemux * demux)
demux->streams[i] = NULL;
}
}
+ memset (demux->streams_found, 0,
+ sizeof (GstFluPSStream *) * (GST_FLUPS_DEMUX_MAX_STREAMS));
+ demux->found_count = 0;
p_ev = &demux->lang_codes;
gst_event_replace (p_ev, NULL);
@@ -402,7 +374,12 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type)
case ST_PRIVATE_DATA:
case ST_MHEG:
case ST_DSMCC:
+ break;
case ST_AUDIO_AAC:
+ template = klass->audio_template;
+ name = g_strdup_printf ("audio_%02x", id);
+ caps = gst_caps_new_simple ("audio/mpeg",
+ "mpegversion", G_TYPE_INT, 4, NULL);
break;
case ST_VIDEO_H264:
template = klass->video_template;
@@ -454,6 +431,8 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type)
GST_DEBUG_FUNCPTR (gst_flups_demux_src_event));
gst_pad_set_query_function (stream->pad,
GST_DEBUG_FUNCPTR (gst_flups_demux_src_query));
+ gst_pad_set_query_type_function (stream->pad,
+ GST_DEBUG_FUNCPTR (gst_flups_demux_src_query_type));
gst_pad_use_fixed_caps (stream->pad);
gst_pad_set_caps (stream->pad, caps);
gst_caps_unref (caps);
@@ -480,6 +459,7 @@ gst_flups_demux_get_stream (GstFluPSDemux * demux, gint id, gint type)
gst_element_add_pad (GST_ELEMENT (demux), stream->pad);
demux->streams[id] = stream;
+ demux->streams_found[demux->found_count++] = stream;
}
return stream;
@@ -503,13 +483,13 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream,
goto no_stream;
/* timestamps */
- if (demux->next_pts != G_MAXUINT64)
+ if (G_UNLIKELY (demux->next_pts != G_MAXUINT64))
timestamp = MPEGTIME_TO_GSTTIME (demux->next_pts);
else
timestamp = GST_CLOCK_TIME_NONE;
/* discont */
- if (stream->need_segment) {
+ if (G_UNLIKELY (stream->need_segment)) {
gint64 time, start, stop;
GstEvent *newsegment;
@@ -545,7 +525,6 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream,
else
time = 0;
-#ifdef HAVE_NEWSEG_FULL
GST_INFO_OBJECT (demux, "sending new segment: rate %g applied_rate %g "
"start: %" GST_TIME_FORMAT ", stop: %" GST_TIME_FORMAT
", time: %" GST_TIME_FORMAT " to pad %" GST_PTR_FORMAT,
@@ -556,16 +535,6 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream,
newsegment = gst_event_new_new_segment_full (FALSE,
demux->sink_segment.rate, demux->sink_segment.applied_rate,
GST_FORMAT_TIME, start, stop, time);
-#else
- GST_INFO_OBJECT (demux, "sending new segment: rate %g "
- "start: %" GST_TIME_FORMAT ", stop: %" GST_TIME_FORMAT
- ", time: %" GST_TIME_FORMAT " to pad %" GST_PTR_FORMAT,
- demux->sink_segment.rate, GST_TIME_ARGS (start),
- GST_TIME_ARGS (stop), GST_TIME_ARGS (time), stream->pad);
-
- newsegment = gst_event_new_new_segment (FALSE,
- demux->sink_segment.rate, GST_FORMAT_TIME, start, stop, time);
-#endif
gst_pad_push_event (stream->pad, newsegment);
@@ -628,17 +597,17 @@ no_stream:
}
}
-static void
+static inline void
gst_flups_demux_mark_discont (GstFluPSDemux * demux, gboolean discont,
gboolean need_segment)
{
- gint id;
+ gint i, count = demux->found_count;
/* mark discont on all streams */
- for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
- GstFluPSStream *stream = demux->streams[id];
+ for (i = 0; i < count; i++) {
+ GstFluPSStream *stream = demux->streams_found[i];
- if (stream) {
+ if (G_LIKELY (stream)) {
stream->discont |= discont;
stream->need_segment |= need_segment;
GST_DEBUG_OBJECT (demux, "marked stream as discont %d, need_segment %d",
@@ -647,14 +616,14 @@ gst_flups_demux_mark_discont (GstFluPSDemux * demux, gboolean discont,
}
}
-static gboolean
+static inline gboolean
gst_flups_demux_send_event (GstFluPSDemux * demux, GstEvent * event)
{
- gint id;
+ gint i, count = demux->found_count;
gboolean ret = FALSE;
- for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
- GstFluPSStream *stream = demux->streams[id];
+ for (i = 0; i < count; i++) {
+ GstFluPSStream *stream = demux->streams_found[i];
if (stream && !stream->notlinked) {
(void) gst_event_ref (event);
@@ -715,7 +684,7 @@ gst_flups_demux_handle_dvd_event (GstFluPSDemux * demux, GstEvent * event)
break;
case 0x2:
case 0x3:
- /* MPEG audio without and with extension stream are
+ /* MPEG audio without and with extension stream are
* treated the same */
stream_id = 0xC0 + i;
temp = gst_flups_demux_get_stream (demux, stream_id, ST_AUDIO_MPEG1);
@@ -777,27 +746,27 @@ gst_flups_demux_flush (GstFluPSDemux * demux)
demux->bytes_since_scr = 0;
}
-static void
+static inline void
gst_flups_demux_clear_times (GstFluPSDemux * demux)
{
- gint id;
+ gint i, count = demux->found_count;
/* Clear the last ts for all streams */
- for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
- GstFluPSStream *stream = demux->streams[id];
+ for (i = 0; i < count; i++) {
+ GstFluPSStream *stream = demux->streams_found[i];
- if (stream) {
+ if (G_LIKELY (stream)) {
stream->last_seg_start = stream->last_ts = GST_CLOCK_TIME_NONE;
}
}
}
-static void
+static inline void
gst_flups_demux_send_segment_updates (GstFluPSDemux * demux,
GstClockTime new_time)
{
/* Advance all lagging streams by sending a segment update */
- gint id;
+ gint i, count = demux->found_count;
GstEvent *event = NULL;
/* FIXME: Handle reverse playback */
@@ -805,8 +774,8 @@ gst_flups_demux_send_segment_updates (GstFluPSDemux * demux,
if (new_time > demux->src_segment.stop)
return;
- for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
- GstFluPSStream *stream = demux->streams[id];
+ for (i = 0; i < count; i++) {
+ GstFluPSStream *stream = demux->streams_found[i];
if (stream) {
if (stream->last_ts == GST_CLOCK_TIME_NONE ||
@@ -840,17 +809,15 @@ gst_flups_demux_send_segment_updates (GstFluPSDemux * demux,
gst_event_unref (event);
}
-static void
+static inline void
gst_flups_demux_close_segment (GstFluPSDemux * demux)
{
- gint id;
+ gint i, count = demux->found_count;
GstEvent *event = NULL;
guint64 base_time;
-#if POST_10_10
GST_INFO_OBJECT (demux, "closing running segment %" GST_SEGMENT_FORMAT,
&demux->src_segment);
-#endif
/* FIXME: Need to send a different segment-close to each pad where the
* last_seg_start != clock_time_none, as that indicates a sparse-stream
@@ -859,7 +826,6 @@ gst_flups_demux_close_segment (GstFluPSDemux * demux)
if ((base_time = demux->base_time) == (guint64) - 1)
base_time = 0;
-
/* Close the current segment for a linear playback */
if (demux->src_segment.rate >= 0) {
/* for forward playback, we played from start to last_stop */
@@ -881,8 +847,8 @@ gst_flups_demux_close_segment (GstFluPSDemux * demux)
}
if (event) {
- for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
- GstFluPSStream *stream = demux->streams[id];
+ for (i = 0; i < count; i++) {
+ GstFluPSStream *stream = demux->streams_found[i];
if (stream && !stream->notlinked && !stream->need_segment) {
(void) gst_event_ref (event);
@@ -905,14 +871,7 @@ gst_flups_demux_close_segment (GstFluPSDemux * demux)
static inline gboolean
have_open_streams (GstFluPSDemux * demux)
{
- gint id;
-
- for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
- if (demux->streams[id])
- return TRUE;
- }
-
- return FALSE;
+ return (demux->streams_found[0] != NULL);
}
static gboolean
@@ -942,7 +901,6 @@ gst_flups_demux_sink_event (GstPad * pad, GstEvent * event)
/* Close current segment */
gst_flups_demux_close_segment (demux);
-#ifdef HAVE_NEWSEG_FULL
{
gdouble arate;
@@ -960,19 +918,6 @@ gst_flups_demux_sink_event (GstPad * pad, GstEvent * event)
}
}
-#else
- gst_event_parse_new_segment (event, &update, &rate, &format,
- &start, &stop, &time);
- gst_segment_set_newsegment (&demux->sink_segment, update, rate,
- format, start, stop, time);
- if (format == GST_FORMAT_BYTES && demux->scr_rate_n != G_MAXUINT64
- && demux->scr_rate_d != G_MAXUINT64) {
-
- gst_segment_set_newsegment (&demux->src_segment, update, rate,
- GST_FORMAT_TIME, BYTES_TO_GSTTIME (start), BYTES_TO_GSTTIME (stop),
- BYTES_TO_GSTTIME (time));
- }
-#endif
GST_INFO_OBJECT (demux, "received new segment: rate %g "
"format %d, start: %" G_GINT64_FORMAT ", stop: %" G_GINT64_FORMAT
@@ -1096,10 +1041,8 @@ gst_flups_demux_do_seek (GstFluPSDemux * demux, GstSegment * seeksegment)
scr = MAX (demux->first_scr, scr);
fscr = scr;
-#if POST_10_10
GST_INFO_OBJECT (demux, "sink segment configured %" GST_SEGMENT_FORMAT
", trying to go at SCR: %" G_GUINT64_FORMAT, &demux->sink_segment, scr);
-#endif
offset = MIN (gst_util_uint64_scale (scr, scr_rate_n, scr_rate_d),
demux->sink_segment.stop);
@@ -1156,6 +1099,7 @@ gst_flups_demux_handle_seek_pull (GstFluPSDemux * demux, GstEvent * event)
if (flush) {
/* Flush start up and downstream to make sure data flow and loops are
idle */
+ demux->flushing = TRUE;
gst_flups_demux_send_event (demux, gst_event_new_flush_start ());
gst_pad_push_event (demux->sinkpad, gst_event_new_flush_start ());
} else {
@@ -1168,25 +1112,22 @@ gst_flups_demux_handle_seek_pull (GstFluPSDemux * demux, GstEvent * event)
if (flush) {
/* Stop flushing upstream we need to pull */
+ demux->flushing = FALSE;
gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop ());
}
/* Work on a copy until we are sure the seek succeeded. */
memcpy (&seeksegment, &demux->src_segment, sizeof (GstSegment));
-#if POST_10_10
GST_DEBUG_OBJECT (demux, "segment before configure %" GST_SEGMENT_FORMAT,
&demux->src_segment);
-#endif
/* Apply the seek to our segment */
gst_segment_set_seek (&seeksegment, rate, format, flags,
start_type, start, stop_type, stop, &update);
-#if POST_10_10
GST_DEBUG_OBJECT (demux, "seek segment configured %" GST_SEGMENT_FORMAT,
&seeksegment);
-#endif
if (flush || seeksegment.last_stop != demux->src_segment.last_stop) {
/* Do the actual seeking */
@@ -1204,10 +1145,8 @@ gst_flups_demux_handle_seek_pull (GstFluPSDemux * demux, GstEvent * event)
/* update the rate in our src segment */
demux->sink_segment.rate = rate;
-#if POST_10_10
GST_DEBUG_OBJECT (demux, "seek segment adjusted %" GST_SEGMENT_FORMAT,
&seeksegment);
-#endif
if (flush) {
/* Stop flushing, the sinks are at time 0 now */
@@ -1283,6 +1222,19 @@ gst_flups_demux_src_event (GstPad * pad, GstEvent * event)
return res;
}
+static const GstQueryType *
+gst_flups_demux_src_query_type (GstPad * pad)
+{
+ static const GstQueryType types[] = {
+ GST_QUERY_POSITION,
+ GST_QUERY_DURATION,
+ GST_QUERY_SEEKING,
+ 0
+ };
+
+ return types;
+}
+
static gboolean
gst_flups_demux_src_query (GstPad * pad, GstQuery * query)
{
@@ -1349,7 +1301,7 @@ gst_flups_demux_src_query (GstPad * pad, GstQuery * query)
break;
}
- /* Upstream didn't know, so we can only answer TIME queries from
+ /* Upstream didn't know, so we can only answer TIME queries from
* here on */
if (format != GST_FORMAT_TIME) {
GST_DEBUG_OBJECT (demux, "duration not supported for format %d",
@@ -1423,7 +1375,7 @@ gst_flups_demux_src_query (GstPad * pad, GstQuery * query)
* have the SCR
*/
peerquery = gst_query_new_seeking (GST_FORMAT_BYTES);
- res = gst_pad_peer_query (demux->sinkpad, query);
+ res = gst_pad_peer_query (demux->sinkpad, peerquery);
if (!res || demux->scr_rate_n == G_MAXUINT64
|| demux->scr_rate_d == G_MAXUINT64) {
gst_query_set_seeking (query, fmt, FALSE, -1, -1);
@@ -1482,6 +1434,33 @@ gst_flups_demux_reset_psm (GstFluPSDemux * demux)
#undef FILL_TYPE
}
+/* ISO/IEC 13818-1:
+ * pack_header() {
+ * pack_start_code 32 bslbf -+
+ * '01' 2 bslbf |
+ * system_clock_reference_base [32..30] 3 bslbf |
+ * marker_bit 1 bslbf |
+ * system_clock_reference_base [29..15] 15 bslbf |
+ * marker_bit 1 bslbf |
+ * system_clock_reference_base [14..0] 15 bslbf |
+ * marker_bit 1 bslbf | 112 bits
+ * system_clock_reference_extension 9 ubslbf |
+ * marker_bit 1 bslbf |
+ * program_mux_rate 22 ubslbf |
+ * marker_bit 1 bslbf |
+ * marker_bit 1 bslbf |
+ * reserved 5 bslbf |
+ * pack_stuffing_length 3 ubslbf -+
+ *
+ * for (i = 0; i < pack_stuffing_length; i++) {
+ * stuffing_byte '1111 1111' 8 bslbf
+ * }
+ *
+ * 112 bits = 14 bytes, as max value for pack_stuffing_length is 7, then
+ * in total it's needed 14 + 7 = 21 bytes.
+ */
+#define PACK_START_SIZE 21
+
static GstFlowReturn
gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
{
@@ -1491,21 +1470,24 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
guint64 scr, scr_adjusted, new_rate;
guint64 scr_rate_n;
guint64 scr_rate_d;
+ guint avail = gst_adapter_available (demux->adapter);
GST_DEBUG ("parsing pack start");
- /* fixed length to begin with, start code and two scr values */
- length = 8 + 4;
-
- if (!(data = gst_adapter_peek (demux->adapter, length)))
+ if (G_UNLIKELY (avail < PACK_START_SIZE))
goto need_more_data;
+ data = gst_adapter_peek (demux->adapter, PACK_START_SIZE);
+
/* skip start code */
data += 4;
scr1 = GUINT32_FROM_BE (*(guint32 *) data);
scr2 = GUINT32_FROM_BE (*(guint32 *) (data + 4));
+ /* fixed length to begin with, start code and two scr values */
+ length = 8 + 4;
+
/* start parsing the stream */
if ((*data & 0xc0) == 0x40) {
guint32 scr_ext;
@@ -1517,13 +1499,11 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
/* mpeg2 has more data */
length += 2;
- if (gst_adapter_available (demux->adapter) < length)
- goto need_more_data;
/* :2=01 ! scr:3 ! marker:1==1 ! scr:15 ! marker:1==1 ! scr:15 */
/* check markers */
- if ((scr1 & 0xc4000400) != 0x44000400)
+ if (G_UNLIKELY ((scr1 & 0xc4000400) != 0x44000400))
goto lost_sync;
scr = ((guint64) scr1 & 0x38000000) << 3;
@@ -1532,7 +1512,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
scr |= ((guint64) scr2 & 0xf8000000) >> 27;
/* marker:1==1 ! scr_ext:9 ! marker:1==1 */
- if ((scr2 & 0x04010000) != 0x04010000)
+ if (G_UNLIKELY ((scr2 & 0x04010000) != 0x04010000))
goto lost_sync;
scr_ext = (scr2 & 0x03fe0000) >> 17;
@@ -1549,7 +1529,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
data += 6;
/* PMR:22 ! :2==11 ! reserved:5 ! stuffing_len:3 */
next32 = (GUINT32_FROM_BE ((*(guint32 *) data)));
- if ((next32 & 0x00000300) != 0x00000300)
+ if (G_UNLIKELY ((next32 & 0x00000300) != 0x00000300))
goto lost_sync;
new_rate = (next32 & 0xfffffc00) >> 10;
@@ -1558,6 +1538,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
GST_DEBUG_OBJECT (demux, "stuffing bytes: %d", stuffing_bytes);
data += 4;
+ length += stuffing_bytes;
while (stuffing_bytes--) {
if (*data++ != 0xff)
goto lost_sync;
@@ -1567,10 +1548,10 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
demux->is_mpeg2_pack = FALSE;
/* check markers */
- if ((scr1 & 0xf1000100) != 0x21000100)
+ if (G_UNLIKELY ((scr1 & 0xf1000100) != 0x21000100))
goto lost_sync;
- if ((scr2 & 0x01800001) != 0x01800001)
+ if (G_UNLIKELY ((scr2 & 0x01800001) != 0x01800001))
goto lost_sync;
/* :4=0010 ! scr:3 ! marker:1==1 ! scr:15 ! marker:1==1 ! scr:15 ! marker:1==1 */
@@ -1599,14 +1580,14 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
GST_TIME_ARGS (MPEGTIME_TO_GSTTIME ((guint64) scr)));
/* keep the first src in order to calculate delta time */
- if (demux->first_scr == G_MAXUINT64) {
+ if (G_UNLIKELY (demux->first_scr == G_MAXUINT64)) {
demux->first_scr = scr;
demux->first_scr_offset = demux->cur_scr_offset;
demux->base_time = MPEGTIME_TO_GSTTIME (demux->first_scr);
/* at begin consider the new_rate as the scr rate, bytes/clock ticks */
scr_rate_n = new_rate;
scr_rate_d = CLOCK_FREQ;
- } else if (demux->first_scr_offset != demux->cur_scr_offset) {
+ } else if (G_LIKELY (demux->first_scr_offset != demux->cur_scr_offset)) {
/* estimate byte rate related to the SCR */
scr_rate_n = demux->cur_scr_offset - demux->first_scr_offset;
scr_rate_d = scr_adjusted - demux->first_scr;
@@ -1625,7 +1606,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
scr_rate_n, scr_rate_d, (float) scr_rate_n / scr_rate_d);
/* adjustment of the SCR */
- if (demux->current_scr != G_MAXUINT64) {
+ if (G_LIKELY (demux->current_scr != G_MAXUINT64)) {
gint64 diff;
guint64 old_scr, old_mux_rate, bss, adjust = 0;
@@ -1636,8 +1617,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
/* Bytes since SCR is the amount we placed in the adapter since then
* (demux->bytes_since_scr) minus the amount remaining in the adapter,
* clamped to >= 0 */
- bss = MAX (0, (gint) (demux->bytes_since_scr -
- gst_adapter_available (demux->adapter)));
+ bss = MAX (0, (gint) (demux->bytes_since_scr - avail));
/* estimate the new SCR using the previous one according the notes
on point 2.5.2.2 of the ISO/IEC 13818-1 document */
@@ -1656,14 +1636,14 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
/* calculate the absolute deference between the last scr and
the new one */
- if (old_scr > scr_adjusted)
+ if (G_UNLIKELY (old_scr > scr_adjusted))
diff = old_scr - scr_adjusted;
else
diff = scr_adjusted - old_scr;
/* if the difference is more than 1 second we need to reconfigure
adjustment */
- if (diff > CLOCK_FREQ) {
+ if (G_UNLIKELY (diff > CLOCK_FREQ)) {
demux->scr_adjust = demux->next_scr - scr;
GST_DEBUG_OBJECT (demux, "discont found, diff: %" G_GINT64_FORMAT
", adjust %" G_GINT64_FORMAT, diff, demux->scr_adjust);
@@ -1684,7 +1664,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
/* Reset the bytes_since_scr value to count the data remaining in the
* adapter */
- demux->bytes_since_scr = gst_adapter_available (demux->adapter);
+ demux->bytes_since_scr = avail;
gst_adapter_flush (demux->adapter, length);
ADAPTER_OFFSET_FLUSH (length);
@@ -1702,6 +1682,32 @@ need_more_data:
}
}
+/* ISO/IEC 13818-1:
+ * system_header () {
+ * system_header_start_code 32 bslbf -+
+ * header_length 16 uimsbf |
+ * marker_bit 1 bslbf |
+ * rate_bound 22 uimsbf |
+ * marker_bit 1 bslbf |
+ * audio_bound 6 uimsbf |
+ * fixed_flag 1 bslbf |
+ * CSPS_flag 1 bslbf | 96 bits
+ * system_audio_lock_flag 1 bslbf |
+ * system_video_lock_flag 1 bslbf |
+ * marker_bit 1 bslbf |
+ * video_bound 5 uimsbf |
+ * packet_rate_restriction_flag 1 bslbf |
+ * reserved_bits 7 bslbf -+
+ * while (nextbits () = = '1') {
+ * stream_id 8 uimsbf -+
+ * '11' 2 bslbf | 24 bits
+ * P-STD_buffer_bound_scale 1 bslbf |
+ * P-STD_buffer_size_bound 13 uimsbf -+
+ * }
+ * }
+ * 96 bits = 12 bytes, 24 bits = 3 bytes.
+ */
+
static GstFlowReturn
gst_flups_demux_parse_sys_head (GstFluPSDemux * demux)
{
@@ -1941,7 +1947,15 @@ gst_flups_demux_parse_psm (GstFluPSDemux * demux)
GST_DEBUG_OBJECT (demux, "Stream type %02X with id %02X and %u bytes info",
stream_type, stream_id, stream_info_length);
- demux->psm[stream_id] = stream_type;
+ if (G_LIKELY (stream_id != 0xbd))
+ demux->psm[stream_id] = stream_type;
+ else {
+ /* Ignore stream type for private_stream_1 and discover it looking at
+ * the stream data.
+ * Fixes demuxing some clips with lpcm that was wrongly declared as
+ * mpeg audio */
+ GST_DEBUG_OBJECT (demux, "stream type for private_stream_1 ignored");
+ }
es_map_base += stream_info_length;
}
@@ -1993,7 +2007,7 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first,
if (start_code == ID_PRIVATE_STREAM_1 && datalen >= 2) {
guint8 nframes;
- /* VDR writes A52 streams without any header bytes
+ /* VDR writes A52 streams without any header bytes
* (see ftp://ftp.mplayerhq.hu/MPlayer/samples/MPEG-VOB/vdr-AC3) */
if (datalen >= 4) {
guint hdr = GST_READ_UINT32_BE (data);
@@ -2049,7 +2063,7 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first,
demux->current_stream = gst_flups_demux_get_stream (demux, id, stream_type);
}
- if (demux->current_stream == NULL) {
+ if (G_UNLIKELY (demux->current_stream == NULL)) {
GST_DEBUG_OBJECT (demux, "Dropping buffer for unknown stream id 0x%02x",
id);
goto done;
@@ -2105,7 +2119,7 @@ gst_flups_demux_resync (GstFluPSDemux * demux, gboolean save)
gboolean found;
avail = gst_adapter_available (demux->adapter);
- if (avail < 4)
+ if (G_UNLIKELY (avail < 4))
goto need_data;
/* Common case, read 4 bytes an check it */
@@ -2121,7 +2135,7 @@ gst_flups_demux_resync (GstFluPSDemux * demux, gboolean save)
return TRUE;
}
- /* Otherwise, we are starting at byte 4 and we need to search
+ /* Otherwise, we are starting at byte 4 and we need to search
the sync code in all available data in the adapter */
offset = 4;
if (offset >= avail)
@@ -2556,12 +2570,10 @@ gst_flups_sink_get_duration (GstFluPSDemux * demux)
gst_segment_set_last_stop (&demux->src_segment, GST_FORMAT_TIME,
demux->src_segment.start);
}
-#if POST_10_10
GST_INFO_OBJECT (demux, "sink segment configured %" GST_SEGMENT_FORMAT,
&demux->sink_segment);
GST_INFO_OBJECT (demux, "src segment configured %" GST_SEGMENT_FORMAT,
&demux->src_segment);
-#endif
res = TRUE;
@@ -2603,6 +2615,11 @@ gst_flups_demux_loop (GstPad * pad)
demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad));
+ if (G_UNLIKELY (demux->flushing)) {
+ ret = GST_FLOW_WRONG_STATE;
+ goto pause;
+ }
+
if (G_UNLIKELY (demux->sink_segment.format == GST_FORMAT_UNDEFINED))
gst_flups_sink_get_duration (demux);
@@ -2851,6 +2868,10 @@ gst_flups_demux_chain (GstPad * pad, GstBuffer * buffer)
save = TRUE;
while (gst_flups_demux_resync (demux, save)) {
gboolean ps_sync = TRUE;
+ if (G_UNLIKELY (demux->flushing)) {
+ ret = GST_FLOW_WRONG_STATE;
+ goto done;
+ }
/* now switch on last synced byte */
switch (demux->last_sync_code) {
diff --git a/gst/mpegdemux/gstmpegdemux.h b/gst/mpegdemux/gstmpegdemux.h
index 29b3d5e5..a8822350 100644
--- a/gst/mpegdemux/gstmpegdemux.h
+++ b/gst/mpegdemux/gstmpegdemux.h
@@ -101,6 +101,7 @@ struct _GstFluPSDemux
GstPad *sinkpad;
gboolean random_access; /* If we operate in pull mode */
+ gboolean flushing;
GstAdapter *adapter;
GstAdapter *rev_adapter;
@@ -136,6 +137,8 @@ struct _GstFluPSDemux
guint64 next_pts;
guint64 next_dts;
GstFluPSStream **streams;
+ GstFluPSStream **streams_found;
+ gint found_count;
gboolean need_no_more_pads;
/* Indicates an MPEG-2 stream */
diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c
index 8de7cc75..0a6f28a5 100644
--- a/gst/mpegdemux/gstmpegtsdemux.c
+++ b/gst/mpegdemux/gstmpegtsdemux.c
@@ -48,37 +48,17 @@
#include <string.h>
#include <stdlib.h>
-#ifdef USE_LIBOIL
#include <liboil/liboil.h>
-#endif
#include "gstmpegdefs.h"
#include "gstmpegtsdemux.h"
#include "flutspatinfo.h"
#include "flutspmtinfo.h"
-#ifndef GST_CHECK_VERSION
-#define GST_CHECK_VERSION(major,minor,micro) \
- (GST_VERSION_MAJOR > (major) || \
- (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR > (minor)) || \
- (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR == (minor) && \
- GST_VERSION_MICRO >= (micro)))
-#endif
-
-#ifndef GST_BUFFER_IS_DISCONT
-#define GST_BUFFER_IS_DISCONT(buffer) \
- (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT))
-#endif
-
GST_DEBUG_CATEGORY_STATIC (gstmpegtsdemux_debug);
#define GST_CAT_DEFAULT (gstmpegtsdemux_debug)
/* elementfactory information */
-#ifdef USE_LIBOIL
-#define LONGNAME "The Fluendo MPEG Transport stream demuxer (liboil build)"
-#else
-#define LONGNAME "The Fluendo MPEG Transport stream demuxer"
-#endif
#ifndef __always_inline
#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
@@ -95,7 +75,7 @@ GST_DEBUG_CATEGORY_STATIC (gstmpegtsdemux_debug);
#endif
static GstElementDetails mpegts_demux_details = {
- LONGNAME,
+ "The Fluendo MPEG Transport stream demuxer",
"Codec/Demuxer",
"Demultiplexes MPEG2 Transport Streams",
"Wim Taymans <wim@fluendo.com>"
@@ -139,7 +119,10 @@ enum
"mpegversion = (int) { 1, 2, 4 }, " \
"systemstream = (boolean) FALSE; " \
"video/x-h264;" \
- "video/x-dirac" \
+ "video/x-dirac;" \
+ "video/x-wmv," \
+ "wmvversion = (int) 3, " \
+ "format = (fourcc) WVC1" \
)
#define AUDIO_CAPS \
@@ -153,8 +136,9 @@ enum
"dynamic_range = (int) [ 0, 255 ], " \
"emphasis = (boolean) { FALSE, TRUE }, " \
"mute = (boolean) { FALSE, TRUE }; " \
- "audio/x-ac3;" \
- "audio/x-dts" \
+ "audio/x-ac3; audio/x-eac3;" \
+ "audio/x-dts;" \
+ "audio/x-private1-lpcm" \
)
/* Can also use the subpicture pads for text subtitles? */
@@ -212,6 +196,7 @@ static gboolean gst_mpegts_demux_sink_setcaps (GstPad * pad, GstCaps * caps);
static GstClock *gst_mpegts_demux_provide_clock (GstElement * element);
static gboolean gst_mpegts_demux_src_pad_query (GstPad * pad, GstQuery * query);
+static const GstQueryType *gst_mpegts_demux_src_pad_query_type (GstPad * pad);
static GstStateChangeReturn gst_mpegts_demux_change_state (GstElement * element,
GstStateChange transition);
@@ -355,9 +340,7 @@ gst_mpegts_demux_init (GstMpegTSDemux * demux)
demux->pcr[1] = -1;
demux->cache_duration = GST_CLOCK_TIME_NONE;
demux->base_pts = GST_CLOCK_TIME_NONE;
-#ifdef USE_LIBOIL
oil_init ();
-#endif
}
static void
@@ -402,7 +385,10 @@ gst_mpegts_demux_reset (GstMpegTSDemux * demux)
gst_section_filter_uninit (&stream->section_filter);
break;
}
-
+ if (stream->pes_buffer) {
+ gst_buffer_unref (stream->pes_buffer);
+ stream->pes_buffer = NULL;
+ }
g_free (stream);
demux->streams[i] = NULL;
}
@@ -577,6 +563,7 @@ gst_mpegts_stream_is_video (GstMpegTSStream * stream)
case ST_VIDEO_MPEG2:
case ST_VIDEO_MPEG4:
case ST_VIDEO_H264:
+ case ST_VIDEO_VC1:
return TRUE;
case ST_VIDEO_DIRAC:
return gst_mpegts_is_dirac_stream (stream);
@@ -673,10 +660,18 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id,
caps = gst_caps_new_simple ("video/x-dirac", NULL);
}
break;
- case ST_PS_AUDIO_AC3:
+ case ST_VIDEO_VC1:
+ template = klass->video_template;
+ name = g_strdup_printf ("video_%04x", stream->PID);
+ caps = gst_caps_new_simple ("video/x-wmv",
+ "wmvversion", G_TYPE_INT, 3,
+ "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'V', 'C', '1'),
+ NULL);
+ break;
+ case ST_BD_AUDIO_AC3:
template = klass->audio_template;
name = g_strdup_printf ("audio_%04x", stream->PID);
- caps = gst_caps_new_simple ("audio/x-ac3", NULL);
+ caps = gst_caps_new_simple ("audio/x-eac3", NULL);
break;
case ST_PS_AUDIO_DTS:
template = klass->audio_template;
@@ -688,6 +683,11 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id,
name = g_strdup_printf ("audio_%04x", stream->PID);
caps = gst_caps_new_simple ("audio/x-lpcm", NULL);
break;
+ case ST_BD_AUDIO_LPCM:
+ template = klass->audio_template;
+ name = g_strdup_printf ("audio_%04x", stream->PID);
+ caps = gst_caps_new_simple ("audio/x-private1-lpcm", NULL);
+ break;
case ST_PS_DVD_SUBPICTURE:
template = klass->subpicture_template;
name = g_strdup_printf ("subpicture_%04x", stream->PID);
@@ -713,6 +713,8 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id,
gst_caps_unref (caps);
gst_pad_set_query_function (stream->pad,
GST_DEBUG_FUNCPTR (gst_mpegts_demux_src_pad_query));
+ gst_pad_set_query_type_function (stream->pad,
+ GST_DEBUG_FUNCPTR (gst_mpegts_demux_src_pad_query_type));
gst_pad_set_event_function (stream->pad,
GST_DEBUG_FUNCPTR (gst_mpegts_demux_src_event));
g_free (name);
@@ -855,10 +857,6 @@ gst_mpegts_demux_send_tags_for_stream (GstMpegTSDemux * demux,
}
}
-#ifndef GST_FLOW_IS_SUCCESS
-#define GST_FLOW_IS_SUCCESS(ret) ((ret) >= GST_FLOW_OK)
-#endif
-
static GstFlowReturn
gst_mpegts_demux_combine_flows (GstMpegTSDemux * demux,
GstMpegTSStream * stream, GstFlowReturn ret)
@@ -1537,11 +1535,7 @@ gst_mpegts_stream_parse_private_section (GstMpegTSStream * stream,
/* just dump this down the pad */
if (gst_pad_alloc_buffer (stream->pad, 0, datalen, NULL, &buffer) ==
GST_FLOW_OK) {
-#ifdef USE_LIBOIL
oil_memcpy (buffer->data, data, datalen);
-#else
- memcpy (buffer->data, data, datalen);
-#endif
gst_pad_push (stream->pad, buffer);
}
@@ -2104,11 +2098,7 @@ gst_mpegts_stream_pes_buffer_push (GstMpegTSStream * stream,
stream->pes_buffer_used = 0;
}
out_data = GST_BUFFER_DATA (stream->pes_buffer) + stream->pes_buffer_used;
-#ifdef USE_LIBOIL
oil_memcpy (out_data, in_data, in_size);
-#else
- memcpy (out_data, in_data, in_size);
-#endif
stream->pes_buffer_used += in_size;
done:
return ret;
@@ -2136,11 +2126,7 @@ gst_mpegts_demux_push_fragment (GstMpegTSStream * stream,
{
GstFlowReturn ret;
GstBuffer *es_buf = gst_buffer_new_and_alloc (in_size);
-#ifdef USE_LIBOIL
oil_memcpy (GST_BUFFER_DATA (es_buf), in_data, in_size);
-#else
- memcpy (GST_BUFFER_DATA (es_buf), in_data, in_size);
-#endif
ret = gst_pes_filter_push (&stream->filter, es_buf);
/* If PES filter return ok then PES fragment buffering
@@ -2280,11 +2266,7 @@ gst_mpegts_demux_parse_stream (GstMpegTSDemux * demux, GstMpegTSStream * stream,
/* FIXME: try to use data directly instead of creating a buffer and
pushing in into adapter at section filter */
sec_buf = gst_buffer_new_and_alloc (datalen);
-#ifdef USE_LIBOIL
oil_memcpy (GST_BUFFER_DATA (sec_buf), data, datalen);
-#else
- memcpy (GST_BUFFER_DATA (sec_buf), data, datalen);
-#endif
if (gst_section_filter_push (&stream->section_filter,
payload_unit_start_indicator, continuity_counter, sec_buf)) {
GST_DEBUG_OBJECT (demux, "section finished");
@@ -2701,6 +2683,19 @@ gst_mpegts_demux_provide_clock (GstElement * element)
return NULL;
}
+static const GstQueryType *
+gst_mpegts_demux_src_pad_query_type (GstPad * pad)
+{
+ static const GstQueryType types[] = {
+ GST_QUERY_LATENCY,
+ GST_QUERY_DURATION,
+ GST_QUERY_SEEKING,
+ 0
+ };
+
+ return types;
+}
+
static gboolean
gst_mpegts_demux_src_pad_query (GstPad * pad, GstQuery * query)
{
@@ -2800,11 +2795,18 @@ gst_mpegts_demux_src_pad_query (GstPad * pad, GstQuery * query)
goto beach;
}
+ /* We can't say anything about seekability if we didn't
+ * have a second PCR yet because the bitrate is calculated
+ * from this
+ */
+ if (demux->bitrate == -1 && demux->pcr[1] == -1)
+ goto beach;
+
/* We can seek if upstream supports BYTES seeks and we
* have a bitrate
*/
peerquery = gst_query_new_seeking (GST_FORMAT_BYTES);
- res = gst_pad_peer_query (demux->sinkpad, query);
+ res = gst_pad_peer_query (demux->sinkpad, peerquery);
if (!res || demux->bitrate == -1) {
gst_query_set_seeking (query, fmt, FALSE, -1, -1);
} else {