gstavidemux.c 176 KB
Newer Older
1
2
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com>
3
 * Copyright (C) <2006> Nokia Corporation (contact <stefan.kost@nokia.com>)
4
 * Copyright (C) <2009-2010> STEricsson <benjamin.gaignard@stericsson.com>
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
5
6
7
8
9
10
11
12
13
14
15
16
17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
18
19
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
20
 */
21
/* Element-Checklist-Version: 5 */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
22

23
24
25
26
/**
 * SECTION:element-avidemux
 *
 * Demuxes an .avi file into raw or compressed audio and/or video streams.
27
 *
Wim Taymans's avatar
Wim Taymans committed
28
 * This element supports both push and pull-based scheduling, depending on the
29
 * capabilities of the upstream elements.
30
31
 *
 * <refsect2>
32
 * <title>Example launch line</title>
33
 * |[
34
 * gst-launch-1.0 filesrc location=test.avi ! avidemux name=demux  demux.audio_00 ! decodebin ! audioconvert ! audioresample ! autoaudiosink   demux.video_00 ! queue ! decodebin ! videoconvert ! videoscale ! autovideosink
35
 * ]| Play (parse and decode) an .avi file and try to output it to
36
37
38
39
40
 * an automatically detected soundcard and videosink. If the AVI file contains
 * compressed audio or video data, this will only work if you have the
 * right decoder elements/plugins installed.
 * </refsect2>
 *
Wim Taymans's avatar
Wim Taymans committed
41
 * Last reviewed on 2006-12-29 (0.10.6)
42
43
 */

44
45
46
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
47

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
48
#include <string.h>
49
#include <stdio.h>
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
50

51
#include "gst/riff/riff-media.h"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
52
#include "gstavidemux.h"
53
#include "avi-ids.h"
54
#include <gst/gst-i18n-plugin.h>
55
#include <gst/base/gstadapter.h>
56
#include <gst/tag/tag.h>
Wim Taymans's avatar
Wim Taymans committed
57

58
#define DIV_ROUND_UP(s,v) (((s) + ((v)-1)) / (v))
Wim Taymans's avatar
Wim Taymans committed
59

Wim Taymans's avatar
Wim Taymans committed
60
#define GST_AVI_KEYFRAME (1 << 0)
Stefan Kost's avatar
Stefan Kost committed
61
62
63
64
65
#define ENTRY_IS_KEYFRAME(e) ((e)->flags == GST_AVI_KEYFRAME)
#define ENTRY_SET_KEYFRAME(e) ((e)->flags = GST_AVI_KEYFRAME)
#define ENTRY_UNSET_KEYFRAME(e) ((e)->flags = 0)


66
67
GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
#define GST_CAT_DEFAULT avidemux_debug
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
68

69
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
70
71
72
73
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-msvideo")
    );
74

75
76
77
78
79
80
81
#ifndef GST_DISABLE_GST_DEBUG
static const char *const snap_types[2][2] = {
  {"any", "before"},
  {"after", "nearest"},
};
#endif

82
static void gst_avi_demux_finalize (GObject * object);
83

84
static void gst_avi_demux_reset (GstAviDemux * avi);
85

86
#if 0
87
static const GstEventMask *gst_avi_demux_get_event_mask (GstPad * pad);
88
#endif
Wim Taymans's avatar
Wim Taymans committed
89
90
static gboolean gst_avi_demux_handle_src_event (GstPad * pad,
    GstObject * parent, GstEvent * event);
91
static gboolean gst_avi_demux_handle_sink_event (GstPad * pad,
Wim Taymans's avatar
Wim Taymans committed
92
    GstObject * parent, GstEvent * event);
93
static gboolean gst_avi_demux_push_event (GstAviDemux * avi, GstEvent * event);
94
95

#if 0
96
static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
97
#endif
Wim Taymans's avatar
Wim Taymans committed
98
99
static gboolean gst_avi_demux_handle_src_query (GstPad * pad,
    GstObject * parent, GstQuery * query);
100
101
102
static gboolean gst_avi_demux_src_convert (GstPad * pad, GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value);

103
104
105
static gboolean gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment);
static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad,
    GstEvent * event);
106
107
static gboolean gst_avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad,
    GstEvent * event);
108
static void gst_avi_demux_loop (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
109
110
static gboolean gst_avi_demux_sink_activate (GstPad * sinkpad,
    GstObject * parent);
Wim Taymans's avatar
Wim Taymans committed
111
112
static gboolean gst_avi_demux_sink_activate_mode (GstPad * sinkpad,
    GstObject * parent, GstPadMode mode, gboolean active);
Wim Taymans's avatar
Wim Taymans committed
113
114
static GstFlowReturn gst_avi_demux_chain (GstPad * pad, GstObject * parent,
    GstBuffer * buf);
115
#if 0
116
117
static void gst_avi_demux_set_index (GstElement * element, GstIndex * index);
static GstIndex *gst_avi_demux_get_index (GstElement * element);
118
#endif
119
120
static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element,
    GstStateChange transition);
121
122
123
124
static void gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi);
static void gst_avi_demux_get_buffer_info (GstAviDemux * avi,
    GstAviStream * stream, guint entry_n, GstClockTime * timestamp,
    GstClockTime * ts_end, guint64 * offset, guint64 * offset_end);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
125

126
static void gst_avi_demux_parse_idit (GstAviDemux * avi, GstBuffer * buf);
Stefan Sauer's avatar
Stefan Sauer committed
127
static void gst_avi_demux_parse_strd (GstAviDemux * avi, GstBuffer * buf);
128

129
/* GObject methods */
130

Wim Taymans's avatar
Wim Taymans committed
131
132
#define gst_avi_demux_parent_class parent_class
G_DEFINE_TYPE (GstAviDemux, gst_avi_demux, GST_TYPE_ELEMENT);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
133

134
static void
Wim Taymans's avatar
Wim Taymans committed
135
gst_avi_demux_class_init (GstAviDemuxClass * klass)
136
{
Wim Taymans's avatar
Wim Taymans committed
137
138
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
  GObjectClass *gobject_class = (GObjectClass *) klass;
139
140
  GstPadTemplate *videosrctempl, *audiosrctempl, *subsrctempl, *subpicsrctempl;
  GstCaps *audcaps, *vidcaps, *subcaps, *subpiccaps;;
141

Wim Taymans's avatar
Wim Taymans committed
142
143
144
145
146
147
148
  GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
      0, "Demuxer for AVI streams");

  gobject_class->finalize = gst_avi_demux_finalize;

  gstelement_class->change_state =
      GST_DEBUG_FUNCPTR (gst_avi_demux_change_state);
149
#if 0
Wim Taymans's avatar
Wim Taymans committed
150
151
  gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_avi_demux_set_index);
  gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_avi_demux_get_index);
152
#endif
Wim Taymans's avatar
Wim Taymans committed
153

154
  audcaps = gst_riff_create_audio_template_caps ();
155
  gst_caps_append (audcaps, gst_caps_new_empty_simple ("audio/x-avi-unknown"));
Wim Taymans's avatar
Wim Taymans committed
156
  audiosrctempl = gst_pad_template_new ("audio_%u",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
157
      GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
158

David Schleef's avatar
David Schleef committed
159
160
  vidcaps = gst_riff_create_video_template_caps ();
  gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
161
  gst_caps_append (vidcaps, gst_caps_new_empty_simple ("video/x-avi-unknown"));
Wim Taymans's avatar
Wim Taymans committed
162
  videosrctempl = gst_pad_template_new ("video_%u",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
163
      GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
164

165
  subcaps = gst_caps_new_empty_simple ("application/x-subtitle-avi");
Wim Taymans's avatar
Wim Taymans committed
166
  subsrctempl = gst_pad_template_new ("subtitle_%u",
167
      GST_PAD_SRC, GST_PAD_SOMETIMES, subcaps);
168
169
170
  subpiccaps = gst_caps_new_empty_simple ("subpicture/x-xsub");
  subpicsrctempl = gst_pad_template_new ("subpicture_%u",
      GST_PAD_SRC, GST_PAD_SOMETIMES, subpiccaps);
Wim Taymans's avatar
Wim Taymans committed
171
172
173
  gst_element_class_add_pad_template (gstelement_class, audiosrctempl);
  gst_element_class_add_pad_template (gstelement_class, videosrctempl);
  gst_element_class_add_pad_template (gstelement_class, subsrctempl);
174
  gst_element_class_add_pad_template (gstelement_class, subpicsrctempl);
Wim Taymans's avatar
Wim Taymans committed
175
  gst_element_class_add_pad_template (gstelement_class,
David Schleef's avatar
David Schleef committed
176
      gst_static_pad_template_get (&sink_templ));
Wim Taymans's avatar
Wim Taymans committed
177

178
  gst_element_class_set_static_metadata (gstelement_class, "Avi demuxer",
179
180
181
182
183
      "Codec/Demuxer",
      "Demultiplex an avi file into audio and video",
      "Erik Walthinsen <omega@cse.ogi.edu>, "
      "Wim Taymans <wim.taymans@chello.be>, "
      "Thijs Vermeir <thijsvermeir@gmail.com>");
184
185
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
186
187
static void
gst_avi_demux_init (GstAviDemux * avi)
188
{
189
  avi->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
190
191
  gst_pad_set_activate_function (avi->sinkpad,
      GST_DEBUG_FUNCPTR (gst_avi_demux_sink_activate));
Wim Taymans's avatar
Wim Taymans committed
192
193
  gst_pad_set_activatemode_function (avi->sinkpad,
      GST_DEBUG_FUNCPTR (gst_avi_demux_sink_activate_mode));
194
195
196
197
  gst_pad_set_chain_function (avi->sinkpad,
      GST_DEBUG_FUNCPTR (gst_avi_demux_chain));
  gst_pad_set_event_function (avi->sinkpad,
      GST_DEBUG_FUNCPTR (gst_avi_demux_handle_sink_event));
Wim Taymans's avatar
Wim Taymans committed
198
  gst_element_add_pad (GST_ELEMENT_CAST (avi), avi->sinkpad);
Wim Taymans's avatar
Wim Taymans committed
199

200
  avi->adapter = gst_adapter_new ();
Wim Taymans's avatar
Wim Taymans committed
201

202
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
203
204

  GST_OBJECT_FLAG_SET (avi, GST_ELEMENT_FLAG_INDEXABLE);
Wim Taymans's avatar
Wim Taymans committed
205
206
}

207
static void
208
gst_avi_demux_finalize (GObject * object)
209
210
211
{
  GstAviDemux *avi = GST_AVI_DEMUX (object);

212
213
214
  GST_DEBUG ("AVI: finalize");

  g_object_unref (avi->adapter);
215

216
  G_OBJECT_CLASS (parent_class)->finalize (object);
217
218
}

Wim Taymans's avatar
Wim Taymans committed
219
220
221
222
223
224
225
226
227
228
229
230
static void
gst_avi_demux_reset_stream (GstAviDemux * avi, GstAviStream * stream)
{
  g_free (stream->strh);
  g_free (stream->strf.data);
  g_free (stream->name);
  g_free (stream->index);
  g_free (stream->indexes);
  if (stream->initdata)
    gst_buffer_unref (stream->initdata);
  if (stream->extradata)
    gst_buffer_unref (stream->extradata);
231
232
  if (stream->rgb8_palette)
    gst_buffer_unref (stream->rgb8_palette);
Wim Taymans's avatar
Wim Taymans committed
233
  if (stream->pad) {
234
235
    if (stream->exposed) {
      gst_pad_set_active (stream->pad, FALSE);
Wim Taymans's avatar
Wim Taymans committed
236
      gst_element_remove_pad (GST_ELEMENT_CAST (avi), stream->pad);
237
238
    } else
      gst_object_unref (stream->pad);
Wim Taymans's avatar
Wim Taymans committed
239
240
  }
  if (stream->taglist) {
241
    gst_tag_list_unref (stream->taglist);
Wim Taymans's avatar
Wim Taymans committed
242
243
244
245
246
    stream->taglist = NULL;
  }
  memset (stream, 0, sizeof (GstAviStream));
}

247
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
248
gst_avi_demux_reset (GstAviDemux * avi)
249
{
250
  gint i;
251

252
253
  GST_DEBUG ("AVI: reset");

Wim Taymans's avatar
Wim Taymans committed
254
255
  for (i = 0; i < avi->num_streams; i++)
    gst_avi_demux_reset_stream (avi, &avi->stream[i]);
256

257
  avi->header_state = GST_AVI_DEMUX_HEADER_TAG_LIST;
258
259
260
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
261
  avi->num_t_streams = 0;
262
  avi->num_sp_streams = 0;
263
  avi->main_stream = -1;
264

265
266
267
  avi->have_group_id = FALSE;
  avi->group_id = G_MAXUINT;

268
  avi->state = GST_AVI_DEMUX_START;
269
  avi->offset = 0;
Wim Taymans's avatar
Wim Taymans committed
270
  avi->building_index = FALSE;
271

272
  avi->index_offset = 0;
273
274
  g_free (avi->avih);
  avi->avih = NULL;
275

276
#if 0
277
278
279
  if (avi->element_index)
    gst_object_unref (avi->element_index);
  avi->element_index = NULL;
280
#endif
281

Wim Taymans's avatar
Wim Taymans committed
282
283
284
  if (avi->seg_event) {
    gst_event_unref (avi->seg_event);
    avi->seg_event = NULL;
285
  }
Wim Taymans's avatar
Wim Taymans committed
286
287
288
289
  if (avi->seek_event) {
    gst_event_unref (avi->seek_event);
    avi->seek_event = NULL;
  }
290

291
  if (avi->globaltags)
292
    gst_tag_list_unref (avi->globaltags);
293
294
  avi->globaltags = NULL;

295
  avi->got_tags = TRUE;         /* we always want to push global tags */
296
  avi->have_eos = FALSE;
297
  avi->seekable = TRUE;
298

299
300
  gst_adapter_clear (avi->adapter);

301
  gst_segment_init (&avi->segment, GST_FORMAT_TIME);
302
303
}

304

305
306
/* GstElement methods */

307
#if 0
308
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
309
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
310
{
Wim Taymans's avatar
Wim Taymans committed
311
  GstAviStream *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
312

313
314
315
316
317
318
319
320
321
322
323
  static const GstFormat src_a_formats[] = {
    GST_FORMAT_TIME,
    GST_FORMAT_BYTES,
    GST_FORMAT_DEFAULT,
    0
  };
  static const GstFormat src_v_formats[] = {
    GST_FORMAT_TIME,
    GST_FORMAT_DEFAULT,
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
324

325
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
326
      src_a_formats : src_v_formats);
327
}
328
#endif
329

330
331
/* assumes stream->strf.auds->av_bps != 0 */
static inline GstClockTime
Wim Taymans's avatar
Wim Taymans committed
332
avi_stream_convert_bytes_to_time_unchecked (GstAviStream * stream,
333
334
    guint64 bytes)
{
Wim Taymans's avatar
Wim Taymans committed
335
336
  return gst_util_uint64_scale_int (bytes, GST_SECOND,
      stream->strf.auds->av_bps);
337
338
}

Wim Taymans's avatar
Wim Taymans committed
339
340
341
342
static inline guint64
avi_stream_convert_time_to_bytes_unchecked (GstAviStream * stream,
    GstClockTime time)
{
Wim Taymans's avatar
Wim Taymans committed
343
344
  return gst_util_uint64_scale_int (time, stream->strf.auds->av_bps,
      GST_SECOND);
Wim Taymans's avatar
Wim Taymans committed
345
346
}

347
348
/* assumes stream->strh->rate != 0 */
static inline GstClockTime
Wim Taymans's avatar
Wim Taymans committed
349
avi_stream_convert_frames_to_time_unchecked (GstAviStream * stream,
350
351
352
353
354
355
    guint64 frames)
{
  return gst_util_uint64_scale (frames, stream->strh->scale * GST_SECOND,
      stream->strh->rate);
}

Wim Taymans's avatar
Wim Taymans committed
356
357
358
359
360
361
362
363
static inline guint64
avi_stream_convert_time_to_frames_unchecked (GstAviStream * stream,
    GstClockTime time)
{
  return gst_util_uint64_scale (time, stream->strh->rate,
      stream->strh->scale * GST_SECOND);
}

364
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
365
366
367
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
368
{
Wim Taymans's avatar
Wim Taymans committed
369
  GstAviStream *stream = gst_pad_get_element_private (pad);
370
  gboolean res = TRUE;
371

372
  GST_LOG_OBJECT (pad,
373
374
375
      "Received  src_format:%s, src_value:%" G_GUINT64_FORMAT
      ", dest_format:%s", gst_format_get_name (src_format), src_value,
      gst_format_get_name (*dest_format));
376

377
  if (G_UNLIKELY (src_format == *dest_format)) {
378
    *dest_value = src_value;
379
380
    goto done;
  }
381
  if (G_UNLIKELY (!stream->strh || !stream->strf.data)) {
382
383
    res = FALSE;
    goto done;
384
  }
385
386
387
  if (G_UNLIKELY (stream->strh->type == GST_RIFF_FCC_vids &&
          (src_format == GST_FORMAT_BYTES
              || *dest_format == GST_FORMAT_BYTES))) {
388
389
390
    res = FALSE;
    goto done;
  }
391

392
393
394
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
395
        case GST_FORMAT_BYTES:
Wim Taymans's avatar
Wim Taymans committed
396
397
          *dest_value = gst_util_uint64_scale_int (src_value,
              stream->strf.auds->av_bps, GST_SECOND);
398
399
          break;
        case GST_FORMAT_DEFAULT:
Wim Taymans's avatar
Wim Taymans committed
400
401
          *dest_value =
              gst_util_uint64_scale_round (src_value, stream->strh->rate,
402
              stream->strh->scale * GST_SECOND);
403
404
405
406
          break;
        default:
          res = FALSE;
          break;
407
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
408
      break;
409
410
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
411
        case GST_FORMAT_TIME:
412
          if (stream->strf.auds->av_bps != 0) {
413
414
            *dest_value = avi_stream_convert_bytes_to_time_unchecked (stream,
                src_value);
415
416
          } else
            res = FALSE;
417
418
419
420
          break;
        default:
          res = FALSE;
          break;
421
422
      }
      break;
423
424
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
425
        case GST_FORMAT_TIME:
426
427
          *dest_value =
              avi_stream_convert_frames_to_time_unchecked (stream, src_value);
428
429
430
431
          break;
        default:
          res = FALSE;
          break;
432
      }
Wim Taymans's avatar
Wim Taymans committed
433
434
      break;
    default:
435
      res = FALSE;
436
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
437

438
done:
439
  GST_LOG_OBJECT (pad,
440
441
      "Returning res:%d dest_format:%s dest_value:%" G_GUINT64_FORMAT, res,
      gst_format_get_name (*dest_format), *dest_value);
442
  return res;
443
444
}

445
static gboolean
Wim Taymans's avatar
Wim Taymans committed
446
447
gst_avi_demux_handle_src_query (GstPad * pad, GstObject * parent,
    GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
448
{
449
  gboolean res = TRUE;
Wim Taymans's avatar
Wim Taymans committed
450
  GstAviDemux *avi = GST_AVI_DEMUX (parent);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
451

Wim Taymans's avatar
Wim Taymans committed
452
  GstAviStream *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
453

454
  if (!stream->strh || !stream->strf.data)
Wim Taymans's avatar
Wim Taymans committed
455
    return gst_pad_query_default (pad, parent, query);
456
457
458

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:{
Wim Taymans's avatar
Wim Taymans committed
459
      gint64 pos = 0;
460

461
      GST_DEBUG ("pos query for stream %u: frames %u, bytes %u",
Wim Taymans's avatar
Wim Taymans committed
462
          stream->num, stream->current_entry, stream->current_total);
463

Wim Taymans's avatar
Wim Taymans committed
464
      /* FIXME, this looks clumsy */
465
      if (stream->strh->type == GST_RIFF_FCC_auds) {
466
467
        if (stream->is_vbr) {
          /* VBR */
468
          pos = avi_stream_convert_frames_to_time_unchecked (stream,
469
              stream->current_entry);
470
          GST_DEBUG_OBJECT (avi, "VBR convert frame %u, time %"
Wim Taymans's avatar
Wim Taymans committed
471
              GST_TIME_FORMAT, stream->current_entry, GST_TIME_ARGS (pos));
472
        } else if (stream->strf.auds->av_bps != 0) {
473
          /* CBR */
474
475
          pos = avi_stream_convert_bytes_to_time_unchecked (stream,
              stream->current_total);
476
          GST_DEBUG_OBJECT (avi,
477
              "CBR convert bytes %u, time %" GST_TIME_FORMAT,
Wim Taymans's avatar
Wim Taymans committed
478
              stream->current_total, GST_TIME_ARGS (pos));
Wim Taymans's avatar
Wim Taymans committed
479
        } else if (stream->idx_n != 0 && stream->total_bytes != 0) {
480
          /* calculate timestamps based on percentage of length */
481
482
          guint64 xlen = avi->avih->us_frame *
              avi->avih->tot_frames * GST_USECOND;
483

484
          if (stream->is_vbr) {
Wim Taymans's avatar
Wim Taymans committed
485
            pos = gst_util_uint64_scale (xlen, stream->current_entry,
Wim Taymans's avatar
Wim Taymans committed
486
                stream->idx_n);
487
            GST_DEBUG_OBJECT (avi, "VBR perc convert frame %u, time %"
Wim Taymans's avatar
Wim Taymans committed
488
                GST_TIME_FORMAT, stream->current_entry, GST_TIME_ARGS (pos));
489
          } else {
Wim Taymans's avatar
Wim Taymans committed
490
            pos = gst_util_uint64_scale (xlen, stream->current_total,
491
                stream->total_bytes);
492
493
494
            GST_DEBUG_OBJECT (avi,
                "CBR perc convert bytes %u, time %" GST_TIME_FORMAT,
                stream->current_total, GST_TIME_ARGS (pos));
495
          }
496
        } else {
497
          /* we don't know */
498
          res = FALSE;
499
500
501
        }
      } else {
        if (stream->strh->rate != 0) {
Wim Taymans's avatar
Wim Taymans committed
502
          pos = gst_util_uint64_scale ((guint64) stream->current_entry *
503
              stream->strh->scale, GST_SECOND, (guint64) stream->strh->rate);
504
        } else {
Wim Taymans's avatar
Wim Taymans committed
505
          pos = stream->current_entry * avi->avih->us_frame * GST_USECOND;
506
        }
Wim Taymans's avatar
Wim Taymans committed
507
      }
508
509
      if (res) {
        GST_DEBUG ("pos query : %" GST_TIME_FORMAT, GST_TIME_ARGS (pos));
Wim Taymans's avatar
Wim Taymans committed
510
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
511
512
      } else
        GST_WARNING ("pos query failed");
Wim Taymans's avatar
Wim Taymans committed
513
514
515
516
      break;
    }
    case GST_QUERY_DURATION:
    {
517
      GstFormat fmt;
518
      GstClockTime duration;
519

520
      /* only act on audio or video streams */
521
      if (stream->strh->type != GST_RIFF_FCC_auds &&
522
523
          stream->strh->type != GST_RIFF_FCC_vids &&
          stream->strh->type != GST_RIFF_FCC_iavs) {
524
525
526
        res = FALSE;
        break;
      }
527

528
529
      /* take stream duration, fall back to avih duration */
      if ((duration = stream->duration) == -1)
530
531
        if ((duration = stream->hdr_duration) == -1)
          duration = avi->duration;
532

533
534
535
536
      gst_query_parse_duration (query, &fmt, NULL);

      switch (fmt) {
        case GST_FORMAT_TIME:
537
          gst_query_set_duration (query, fmt, duration);
538
539
          break;
        case GST_FORMAT_DEFAULT:
540
541
        {
          gint64 dur;
542
          GST_DEBUG_OBJECT (query, "total frames is %" G_GUINT32_FORMAT,
Wim Taymans's avatar
Wim Taymans committed
543
              stream->idx_n);
544

545
          if (stream->idx_n > 0)
Wim Taymans's avatar
Wim Taymans committed
546
            gst_query_set_duration (query, fmt, stream->idx_n);
547
          else if (gst_pad_query_convert (pad, GST_FORMAT_TIME,
548
                  duration, fmt, &dur))
549
            gst_query_set_duration (query, fmt, dur);
550
          break;
551
        }
552
553
554
555
        default:
          res = FALSE;
          break;
      }
Wim Taymans's avatar
Wim Taymans committed
556
      break;
557
    }
558
559
560
561
562
563
564
565
    case GST_QUERY_SEEKING:{
      GstFormat fmt;

      gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
      if (fmt == GST_FORMAT_TIME) {
        gboolean seekable = TRUE;

        if (avi->streaming) {
566
          seekable = avi->seekable;
567
568
569
570
571
572
573
574
        }

        gst_query_set_seeking (query, GST_FORMAT_TIME, seekable,
            0, stream->duration);
        res = TRUE;
      }
      break;
    }
575
576
577
578
579
580
581
582
583
    case GST_QUERY_CONVERT:{
      GstFormat src_fmt, dest_fmt;
      gint64 src_val, dest_val;

      gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
      if ((res = gst_avi_demux_src_convert (pad, src_fmt, src_val, &dest_fmt,
                  &dest_val)))
        gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
      else
Wim Taymans's avatar
Wim Taymans committed
584
        res = gst_pad_query_default (pad, parent, query);
585
586
      break;
    }
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
    case GST_QUERY_SEGMENT:
    {
      GstFormat format;
      gint64 start, stop;

      format = avi->segment.format;

      start =
          gst_segment_to_stream_time (&avi->segment, format,
          avi->segment.start);
      if ((stop = avi->segment.stop) == -1)
        stop = avi->segment.duration;
      else
        stop = gst_segment_to_stream_time (&avi->segment, format, stop);

      gst_query_set_segment (query, avi->segment.rate, format, start, stop);
      res = TRUE;
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
606
    default:
Wim Taymans's avatar
Wim Taymans committed
607
      res = gst_pad_query_default (pad, parent, query);
Wim Taymans's avatar
Wim Taymans committed
608
609
610
611
612
613
      break;
  }

  return res;
}

614
#if 0
615
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
616
gst_avi_demux_get_event_mask (GstPad * pad)
617
618
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
619
620
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
621
622
623
624
  };

  return masks;
}
625
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
626

627
#if 0
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
static guint64
gst_avi_demux_seek_streams (GstAviDemux * avi, guint64 offset, gboolean before)
{
  GstAviStream *stream;
  GstIndexEntry *entry;
  gint i;
  gint64 val, min = offset;

  for (i = 0; i < avi->num_streams; i++) {
    stream = &avi->stream[i];

    entry = gst_index_get_assoc_entry (avi->element_index, stream->index_id,
        before ? GST_INDEX_LOOKUP_BEFORE : GST_INDEX_LOOKUP_AFTER,
        GST_ASSOCIATION_FLAG_NONE, GST_FORMAT_BYTES, offset);

    if (before) {
      if (entry) {
645
        gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val);
646
647
648
649
650
651
652
653
654
        GST_DEBUG_OBJECT (avi, "stream %d, previous entry at %"
            G_GUINT64_FORMAT, i, val);
        if (val < min)
          min = val;
      }
      continue;
    }

    if (!entry) {
655
      GST_DEBUG_OBJECT (avi, "no position for stream %d, assuming at start", i);
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
      stream->current_entry = 0;
      stream->current_total = 0;
      continue;
    }

    gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val);
    GST_DEBUG_OBJECT (avi, "stream %d, next entry at %" G_GUINT64_FORMAT,
        i, val);

    gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &val);
    stream->current_total = val;
    gst_index_entry_assoc_map (entry, GST_FORMAT_DEFAULT, &val);
    stream->current_entry = val;
  }

  return min;
}
673
#endif
674

675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
static guint
gst_avi_demux_index_entry_offset_search (GstAviIndexEntry * entry,
    guint64 * offset)
{
  if (entry->offset < *offset)
    return -1;
  else if (entry->offset > *offset)
    return 1;
  return 0;
}

static guint64
gst_avi_demux_seek_streams_index (GstAviDemux * avi, guint64 offset,
    gboolean before)
{
  GstAviStream *stream;
  GstAviIndexEntry *entry;
  gint i;
  gint64 val, min = offset;
694
  guint index = 0;
695
696
697
698

  for (i = 0; i < avi->num_streams; i++) {
    stream = &avi->stream[i];

699
700
    /* compensate for chunk header */
    offset += 8;
701
702
703
704
705
    entry =
        gst_util_array_binary_search (stream->index, stream->idx_n,
        sizeof (GstAviIndexEntry),
        (GCompareDataFunc) gst_avi_demux_index_entry_offset_search,
        before ? GST_SEARCH_MODE_BEFORE : GST_SEARCH_MODE_AFTER, &offset, NULL);
706
    offset -= 8;
707
708
709
710
711
712

    if (entry)
      index = entry - stream->index;

    if (before) {
      if (entry) {
713
        val = stream->index[index].offset;
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
        GST_DEBUG_OBJECT (avi,
            "stream %d, previous entry at %" G_GUINT64_FORMAT, i, val);
        if (val < min)
          min = val;
      }
      continue;
    }

    if (!entry) {
      GST_DEBUG_OBJECT (avi, "no position for stream %d, assuming at start", i);
      stream->current_entry = 0;
      stream->current_total = 0;
      continue;
    }

729
    val = stream->index[index].offset - 8;
730
731
732
    GST_DEBUG_OBJECT (avi, "stream %d, next entry at %" G_GUINT64_FORMAT, i,
        val);

733
    stream->current_total = stream->index[index].total;
734
735
736
737
738
739
    stream->current_entry = index;
  }

  return min;
}

740
741
#define GST_AVI_SEEK_PUSH_DISPLACE     (4 * GST_SECOND)

742
static gboolean
Wim Taymans's avatar
Wim Taymans committed
743
744
gst_avi_demux_handle_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event)
745
746
{
  gboolean res = TRUE;
Wim Taymans's avatar
Wim Taymans committed
747
  GstAviDemux *avi = GST_AVI_DEMUX (parent);
748
749
750
751
752

  GST_DEBUG_OBJECT (avi,
      "have event type %s: %p on sink pad", GST_EVENT_TYPE_NAME (event), event);

  switch (GST_EVENT_TYPE (event)) {
753
    case GST_EVENT_SEGMENT:
754
    {
755
      gint64 boffset, offset = 0;
756
      GstSegment segment;
757
      GstEvent *segment_event;
758
759

      /* some debug output */
760
761
      gst_event_copy_segment (event, &segment);
      GST_DEBUG_OBJECT (avi, "received newsegment %" GST_SEGMENT_FORMAT,
762
763
764
765
766
767
768
769
770
          &segment);

      /* chain will send initial newsegment after pads have been added */
      if (avi->state != GST_AVI_DEMUX_MOVI) {
        GST_DEBUG_OBJECT (avi, "still starting, eating event");
        goto exit;
      }

      /* we only expect a BYTE segment, e.g. following a seek */
771
      if (segment.format != GST_FORMAT_BYTES) {
772
773
774
775
        GST_DEBUG_OBJECT (avi, "unsupported segment format, ignoring");
        goto exit;
      }

776
777
      if (avi->have_index) {
        GstAviIndexEntry *entry;
778
        guint i = 0, index = 0, k = 0;
779
780
        GstAviStream *stream;

781
        /* compensate chunk header, stored index offset points after header */
782
        boffset = segment.start + 8;
783
784
785
786
787
788
789
790
        /* find which stream we're on */
        do {
          stream = &avi->stream[i];

          /* find the index for start bytes offset */
          entry = gst_util_array_binary_search (stream->index,
              stream->idx_n, sizeof (GstAviIndexEntry),
              (GCompareDataFunc) gst_avi_demux_index_entry_offset_search,
791
              GST_SEARCH_MODE_AFTER, &boffset, NULL);
792

793
794
795
          if (entry == NULL)
            continue;
          index = entry - stream->index;
796

797
798
799
800
801
802
          /* we are on the stream with a chunk start offset closest to start */
          if (!offset || stream->index[index].offset < offset) {
            offset = stream->index[index].offset;
            k = i;
          }
          /* exact match needs no further searching */
803
          if (stream->index[index].offset == boffset)
804
805
            break;
        } while (++i < avi->num_streams);
806
        boffset -= 8;
807
808
809
810
811
812
813
814
        offset -= 8;
        stream = &avi->stream[k];

        /* so we have no idea what is to come, or where we are */
        if (!offset) {
          GST_WARNING_OBJECT (avi, "insufficient index data, forcing EOS");
          goto eos;
        }
815

816
        /* get the ts corresponding to start offset bytes for the stream */
817
        gst_avi_demux_get_buffer_info (avi, stream, index,
818
            (GstClockTime *) & segment.time, NULL, NULL, NULL);
819
#if 0
820
821
822
823
824
825
      } else if (avi->element_index) {
        GstIndexEntry *entry;

        /* Let's check if we have an index entry for this position */
        entry = gst_index_get_assoc_entry (avi->element_index, avi->index_id,
            GST_INDEX_LOOKUP_AFTER, GST_ASSOCIATION_FLAG_NONE,
826
            GST_FORMAT_BYTES, segment.start);
827
828
829
830
831
832

        /* we can not go where we have not yet been before ... */
        if (!entry) {
          GST_WARNING_OBJECT (avi, "insufficient index data, forcing EOS");
          goto eos;
        }
833

834
835
        gst_index_entry_assoc_map (entry, GST_FORMAT_TIME,
            (gint64 *) & segment.time);
836
        gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &offset);
837
#endif
838
839
      } else {
        GST_WARNING_OBJECT (avi, "no index data, forcing EOS");
840
841
842
        goto eos;
      }

843
844
845
      segment.format = GST_FORMAT_TIME;
      segment.start = segment.time;
      segment.stop = GST_CLOCK_TIME_NONE;
846
847
848
849
      segment.position = segment.start;

      /* rescue duration */
      segment.duration = avi->segment.duration;
850
851

      /* set up segment and send downstream */
852
853
854
      gst_segment_copy_into (&segment, &avi->segment);

      GST_DEBUG_OBJECT (avi, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
855
856
857
      segment_event = gst_event_new_segment (&segment);
      gst_event_set_seqnum (segment_event, gst_event_get_seqnum (event));
      gst_avi_demux_push_event (avi, segment_event);
858
859
860

      GST_DEBUG_OBJECT (avi, "next chunk expected at %" G_GINT64_FORMAT,
          boffset);
861
862

      /* adjust state for streaming thread accordingly */
863
864
      if (avi->have_index)
        gst_avi_demux_seek_streams_index (avi, offset, FALSE);
865
#if 0
866
867
      else
        gst_avi_demux_seek_streams (avi, offset, FALSE);
868
#endif
869
870

      /* set up streaming thread */
871
872
873
      g_assert (offset >= boffset);
      avi->offset = boffset;
      avi->todrop = offset - boffset;
874
875

    exit:
876
      gst_event_unref (event);
877
      res = TRUE;
878
      break;
879
880
881
882
883
    eos:
      /* set up for EOS */
      avi->have_eos = TRUE;
      goto exit;
    }
884
885
886
887
888
889
890
891
892
893
894
895
    case GST_EVENT_EOS:
    {
      if (avi->state != GST_AVI_DEMUX_MOVI) {
        gst_event_unref (event);
        GST_ELEMENT_ERROR (avi, STREAM, DEMUX,
            (NULL), ("got eos and didn't receive a complete header object"));
      } else if (!gst_avi_demux_push_event (avi, event)) {
        GST_ELEMENT_ERROR (avi, STREAM, DEMUX,
            (NULL), ("got eos but no streams (yet)"));
      }
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
896
897
898
899
900
901
902
903
904
905
906
907
    case GST_EVENT_FLUSH_STOP:
    {
      gint i;

      gst_adapter_clear (avi->adapter);
      avi->have_eos = FALSE;
      for (i = 0; i < avi->num_streams; i++) {
        avi->stream[i].last_flow = GST_FLOW_OK;
        avi->stream[i].discont = TRUE;
      }
      /* fall through to default case so that the event gets passed downstream */
    }
908
    default:
Wim Taymans's avatar
Wim Taymans committed
909
      res = gst_pad_event_default (pad, parent, event);
910
911
912
913
914
915
      break;
  }

  return res;
}

Wim Taymans's avatar
Wim Taymans committed
916
static gboolean
Wim Taymans's avatar
Wim Taymans committed
917
918
gst_avi_demux_handle_src_event (GstPad * pad, GstObject * parent,
    GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
919
920
{
  gboolean res = TRUE;
Wim Taymans's avatar
Wim Taymans committed
921
  GstAviDemux *avi = GST_AVI_DEMUX (parent);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
922

923
  GST_DEBUG_OBJECT (avi,
924
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Wim Taymans's avatar
Wim Taymans committed
925
926
927

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
928
929
930
      if (!avi->streaming) {
        res = gst_avi_demux_handle_seek (avi, pad, event);
      } else {
931
        res = gst_avi_demux_handle_seek_push (avi, pad, event);
932
      }
933
      gst_event_unref (event);
934
935
936
937
938
      break;
    case GST_EVENT_QOS:
    case GST_EVENT_NAVIGATION:
      res = FALSE;
      gst_event_unref (event);
Wim Taymans's avatar
Wim Taymans committed
939
940
      break;
    default:
Wim Taymans's avatar
Wim Taymans committed
941
      res = gst_pad_event_default (pad, parent, event);
Wim Taymans's avatar
Wim Taymans committed
942
943
      break;