gstavidemux.c 76.5 KB
Newer Older
1 2
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com>
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * 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
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
19
/* Element-Checklist-Version: 5 */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
20

21 22 23
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
24

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
25 26
#include <string.h>

27
#include "gst/riff/riff-media.h"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
28
#include "gstavidemux.h"
29
#include "avi-ids.h"
30
#include <gst/gst-i18n-plugin.h>
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
31

32 33 34
GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
#define GST_CAT_DEFAULT avidemux_debug

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
35 36
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_EVENT);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
37 38 39 40 41
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-msvideo")
    );
42

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
43 44 45
static void gst_avi_demux_base_init (GstAviDemuxClass * klass);
static void gst_avi_demux_class_init (GstAviDemuxClass * klass);
static void gst_avi_demux_init (GstAviDemux * avi);
46

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
47
static void gst_avi_demux_reset (GstAviDemux * avi);
48

49
#if 0
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
50
static const GstEventMask *gst_avi_demux_get_event_mask (GstPad * pad);
51
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
52
static gboolean gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event);
53 54

#if 0
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
55
static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
56
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
57
static const GstQueryType *gst_avi_demux_get_src_query_types (GstPad * pad);
58
static gboolean gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
59 60 61
static gboolean gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
Wim Taymans's avatar
Wim Taymans committed
62

63
static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, gboolean update);
64 65 66 67
static void gst_avi_demux_loop (GstPad * pad);
static gboolean gst_avi_demux_sink_activate (GstPad * sinkpad);
static gboolean gst_avi_demux_sink_activate_pull (GstPad * sinkpad,
    gboolean active);
68 69
static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element,
    GstStateChange transition);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
70

71
static GstElementClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
72

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
73
GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
74
gst_avi_demux_get_type (void)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
75 76 77 78 79
{
  static GType avi_demux_type = 0;

  if (!avi_demux_type) {
    static const GTypeInfo avi_demux_info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
80
      sizeof (GstAviDemuxClass),
81
      (GBaseInitFunc) gst_avi_demux_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
82
      NULL,
83
      (GClassInitFunc) gst_avi_demux_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
84 85
      NULL,
      NULL,
86
      sizeof (GstAviDemux),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
87
      0,
88
      (GInstanceInitFunc) gst_avi_demux_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
89
    };
90 91

    avi_demux_type =
92
        g_type_register_static (GST_TYPE_ELEMENT,
93
        "GstAviDemux", &avi_demux_info, 0);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
94
  }
95

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
96 97 98
  return avi_demux_type;
}

99
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
100
gst_avi_demux_base_init (GstAviDemuxClass * klass)
101
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
102 103 104 105 106 107 108
  static GstElementDetails gst_avi_demux_details =
      GST_ELEMENT_DETAILS ("Avi demuxer",
      "Codec/Demuxer",
      "Demultiplex an avi file into audio and video",
      "Erik Walthinsen <omega@cse.ogi.edu>\n"
      "Wim Taymans <wim.taymans@chello.be>\n"
      "Ronald Bultje <rbultje@ronald.bitfreak.net>");
109 110 111
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstPadTemplate *videosrctempl, *audiosrctempl;
  GstCaps *audcaps, *vidcaps;
112

113
  audcaps = gst_riff_create_audio_template_caps ();
114
  gst_caps_append (audcaps, gst_caps_new_simple ("audio/x-avi-unknown", NULL));
115
  audiosrctempl = gst_pad_template_new ("audio_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
116
      GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
117

David Schleef's avatar
David Schleef committed
118 119
  vidcaps = gst_riff_create_video_template_caps ();
  gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
120
  gst_caps_append (vidcaps, gst_caps_new_simple ("video/x-avi-unknown", NULL));
121
  videosrctempl = gst_pad_template_new ("video_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
122
      GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
123

124 125 126
  gst_element_class_add_pad_template (element_class, audiosrctempl);
  gst_element_class_add_pad_template (element_class, videosrctempl);
  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
127
      gst_static_pad_template_get (&sink_templ));
128 129 130
  gst_element_class_set_details (element_class, &gst_avi_demux_details);
}

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
131
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
132
gst_avi_demux_class_init (GstAviDemuxClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
133
{
134
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
135

136
  GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
137
      0, "Demuxer for AVI streams");
138

139
  parent_class = g_type_class_peek_parent (klass);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
140

Wim Taymans's avatar
Wim Taymans committed
141
  gstelement_class->change_state = gst_avi_demux_change_state;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
142 143
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
144 145
static void
gst_avi_demux_init (GstAviDemux * avi)
146
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
147 148 149
  avi->sinkpad =
      gst_pad_new_from_template (gst_static_pad_template_get (&sink_templ),
      "sink");
150 151 152
  gst_pad_set_activate_function (avi->sinkpad, gst_avi_demux_sink_activate);
  gst_pad_set_activatepull_function (avi->sinkpad,
      gst_avi_demux_sink_activate_pull);
153
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
Wim Taymans's avatar
Wim Taymans committed
154

155
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
156

157 158
  avi->index_entries = NULL;
  memset (&avi->stream, 0, sizeof (avi->stream));
Wim Taymans's avatar
Wim Taymans committed
159 160
}

161
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
162
gst_avi_demux_reset (GstAviDemux * avi)
163
{
164
  gint i;
165

166 167
  for (i = 0; i < avi->num_streams; i++) {
    g_free (avi->stream[i].strh);
168
    g_free (avi->stream[i].strf.data);
169 170
    if (avi->stream[i].name)
      g_free (avi->stream[i].name);
171 172 173 174
    if (avi->stream[i].initdata)
      gst_buffer_unref (avi->stream[i].initdata);
    if (avi->stream[i].extradata)
      gst_buffer_unref (avi->stream[i].extradata);
175 176
    if (avi->stream[i].pad)
      gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
177 178
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
179

180 181 182
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
183

184
  avi->state = GST_AVI_DEMUX_START;
185
  avi->offset = 0;
186

187 188
  g_free (avi->index_entries);
  avi->index_entries = NULL;
189
  avi->index_size = 0;
190
  avi->index_offset = 0;
191
  avi->current_entry = 0;
192 193
  g_free (avi->avih);
  avi->avih = NULL;
194

195 196 197
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
198 199 200 201 202

  avi->segment_rate = 1.0;
  avi->segment_flags = 0;
  avi->segment_start = -1;
  avi->segment_stop = -1;
203 204
}

205
static gst_avi_index_entry *
206
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
207
{
208 209
  gint i;
  gst_avi_index_entry *entry = NULL;
210

211 212
  for (i = start; i < avi->index_size; i++) {
    entry = &avi->index_entries[i];
213
    if (entry->stream_nr == stream_nr)
Ronald S. Bultje's avatar
Ronald S. Bultje committed
214
      break;
215
  }
216

217 218
  return entry;
}
219

220
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
221 222
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
    gint stream_nr, guint64 time, guint32 flags)
223 224 225
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
226

227 228
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%d",
      stream_nr, GST_TIME_ARGS (time), flags);
229 230
  i = -1;
  do {
231
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
232 233
    if (!entry)
      return NULL;
234

235
    i = entry->index_nr;
236

237 238 239 240
    GST_LOG_OBJECT (avi,
        "looking at entry %d / ts:%" GST_TIME_FORMAT " / dur:%" GST_TIME_FORMAT
        " flags:%d", i, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur),
        entry->flags);
241
    if (entry->ts <= time && (entry->flags & flags) == flags)
242
      last_entry = entry;
243
  } while (entry->ts < time);
244

245 246
  return last_entry;
}
247

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
248

249
#if 0
250
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
251
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
252
{
253
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
254

255 256 257 258 259 260 261 262 263 264 265
  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
266

267
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
268
      src_a_formats : src_v_formats);
269
}
270
#endif
271

272
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
273 274 275
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
276 277
{
  gboolean res = TRUE;
278
  GstAviDemux *avidemux = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
279

280
  avi_stream_context *stream = gst_pad_get_element_private (pad);
281

282 283 284 285
  GST_LOG_OBJECT (avidemux,
      "Received  src_format:%d, src_value:%lld, dest_format:%d", src_format,
      src_value, *dest_format);

286 287
  if (src_format == *dest_format) {
    *dest_value = src_value;
288 289 290 291 292
    goto done;
  }
  if (!stream->strh || !stream->strf.data) {
    res = FALSE;
    goto done;
293
  }
294
  if (stream->strh->type == GST_RIFF_FCC_vids &&
295 296 297 298
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) {
    res = FALSE;
    goto done;
  }
299

300 301 302
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
303
        case GST_FORMAT_BYTES:
304 305 306
          *dest_value =
              gst_util_uint64_scale_int (src_value, stream->strf.auds->av_bps,
              GST_SECOND);
307 308
          break;
        case GST_FORMAT_DEFAULT:
309 310
          *dest_value = gst_util_uint64_scale (src_value, stream->strh->rate,
              stream->strh->scale * GST_SECOND);
311 312 313 314
          break;
        default:
          res = FALSE;
          break;
315
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
316
      break;
317 318
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
319
        case GST_FORMAT_TIME:
320
          if (stream->strf.auds->av_bps != 0) {
321 322
            *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
                stream->strf.auds->av_bps);
323 324
          } else
            res = FALSE;
325 326 327 328
          break;
        default:
          res = FALSE;
          break;
329 330
      }
      break;
331 332
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
333
        case GST_FORMAT_TIME:
334 335 336
          *dest_value =
              gst_util_uint64_scale (src_value,
              stream->strh->scale * GST_SECOND, stream->strh->rate);
337 338 339 340
          break;
        default:
          res = FALSE;
          break;
341
      }
Wim Taymans's avatar
Wim Taymans committed
342 343
      break;
    default:
344
      res = FALSE;
345
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
346

347 348 349 350
done:
  GST_LOG_OBJECT (avidemux, "Returning res:%d dest_format:%d dest_value:%lld",
      res, *dest_format, *dest_value);
  gst_object_unref (avidemux);
351
  return res;
352 353
}

354
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
355
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
356
{
357 358
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
359
    GST_QUERY_DURATION,
360 361
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
362

363
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
364 365
}

366
static gboolean
367
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
368
{
369
  gboolean res = TRUE;
370
  GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
371

372
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
373

374 375 376 377 378
  if (!stream->strh || !stream->strf.data)
    return FALSE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:{
Wim Taymans's avatar
Wim Taymans committed
379
      gint64 pos = 0;
380 381 382

      if (stream->strh->type == GST_RIFF_FCC_auds) {
        if (!stream->strh->samplesize) {
383 384
          pos = gst_util_uint64_scale_int ((gint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
385
        } else if (stream->strf.auds->av_bps != 0) {
386 387
          pos = gst_util_uint64_scale_int (stream->current_byte, GST_SECOND,
              stream->strf.auds->av_bps);
388 389 390 391 392 393
        } else if (stream->total_frames != 0 && stream->total_bytes != 0) {
          /* calculate timestamps based on video size */
          guint64 xlen = demux->avih->us_frame *
              demux->avih->tot_frames * GST_USECOND;

          if (!stream->strh->samplesize)
394 395
            pos = gst_util_uint64_scale_int (xlen, stream->current_frame,
                stream->total_frames);
396
          else
397 398
            pos = gst_util_uint64_scale_int (xlen, stream->current_byte,
                stream->total_bytes);
399
        } else {
400
          res = FALSE;
401 402 403
        }
      } else {
        if (stream->strh->rate != 0) {
404 405 406
          pos =
              gst_util_uint64_scale_int ((guint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
407 408 409
        } else {
          pos = stream->current_frame * demux->avih->us_frame * GST_USECOND;
        }
Wim Taymans's avatar
Wim Taymans committed
410
      }
411
      if (res)
Wim Taymans's avatar
Wim Taymans committed
412 413 414 415 416
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
      break;
    }
    case GST_QUERY_DURATION:
    {
417 418 419 420 421 422 423 424 425 426 427 428
      if (stream->strh->type != GST_RIFF_FCC_auds &&
          stream->strh->type != GST_RIFF_FCC_vids) {
        res = FALSE;
        break;
      }

      /* use duration from the index if we have an
       * index instead of trusting the stream header */
      if (GST_CLOCK_TIME_IS_VALID (stream->idx_duration)) {
        gst_query_set_duration (query, GST_FORMAT_TIME, stream->idx_duration);
      } else {
        gint64 len;
Wim Taymans's avatar
Wim Taymans committed
429

430 431 432 433 434 435
        len =
            gst_util_uint64_scale ((guint64) stream->strh->length *
            stream->strh->scale, GST_SECOND, stream->strh->rate);

        gst_query_set_duration (query, GST_FORMAT_TIME, len);
      }
Wim Taymans's avatar
Wim Taymans committed
436
      break;
437
    }
Wim Taymans's avatar
Wim Taymans committed
438
    default:
439
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
440 441 442 443 444 445
      break;
  }

  return res;
}

446
#if 0
447
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
448
gst_avi_demux_get_event_mask (GstPad * pad)
449 450
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
451 452
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
453 454 455 456
  };

  return masks;
}
457
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
458

Wim Taymans's avatar
Wim Taymans committed
459
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
460
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
461 462
{
  gboolean res = TRUE;
463
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Wim Taymans's avatar
Wim Taymans committed
464
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
465

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
466
  GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi,
467
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
468 469
  if (!avi->index_entries) {
    GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi, "no index entries, returning");
470
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
471
  }
472

Wim Taymans's avatar
Wim Taymans committed
473 474 475 476
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
477
    {
478 479
      /* FIXME, this seeking code is not correct, look at wavparse for 
       * a better example */
480 481
      GstFormat format;
      GstSeekFlags flags;
482 483 484 485 486 487 488 489 490 491 492 493
      gdouble rate;
      gint64 start, stop;
      gint64 tstart, tstop;
      gint64 duration;
      GstFormat tformat = GST_FORMAT_TIME;
      GstSeekType start_type, stop_type;
      gboolean update_start = TRUE;
      gboolean update_stop = TRUE;

      gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
          &stop_type, &stop);

494 495 496
      GST_DEBUG_OBJECT (avi,
          "seek format %d, flags:%d, %08x, start:%lld, stop:%lld", format,
          flags, stream->strh->type, start, stop);
497 498 499 500 501 502 503 504 505 506 507

      if (format != GST_FORMAT_TIME) {
        res &=
            gst_avi_demux_src_convert (pad, format, start, &tformat, &tstart);
        res &= gst_avi_demux_src_convert (pad, format, stop, &tformat, &tstop);
        if (!res)
          goto done;
      } else {
        tstart = start;
        tstop = stop;
      }
508

509 510 511 512
      if (!gst_pad_query_duration (stream->pad, &tformat, &duration)) {
        res = FALSE;
        goto done;
      }
513

514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
      switch (start_type) {
        case GST_SEEK_TYPE_CUR:
          tstart = avi->segment_start + tstart;
          break;
        case GST_SEEK_TYPE_END:
          tstart = duration + tstart;
          break;
        case GST_SEEK_TYPE_NONE:
          tstart = avi->segment_start;
          update_start = FALSE;
          break;
        case GST_SEEK_TYPE_SET:
          break;
      }
      tstart = CLAMP (tstart, 0, duration);
529

530 531 532
      switch (stop_type) {
        case GST_SEEK_TYPE_CUR:
          tstop = avi->segment_stop + tstop;
533
          break;
534 535 536 537 538 539 540 541
        case GST_SEEK_TYPE_END:
          tstop = duration + tstop;
          break;
        case GST_SEEK_TYPE_NONE:
          tstop = avi->segment_stop;
          update_stop = FALSE;
          break;
        case GST_SEEK_TYPE_SET:
542
          break;
Wim Taymans's avatar
Wim Taymans committed
543
      }
544 545 546 547 548 549 550 551 552
      tstop = CLAMP (tstop, 0, duration);

      /* now store the values */
      avi->segment_rate = rate;
      avi->segment_flags = flags;
      avi->segment_start = tstart;
      avi->segment_stop = tstop;

      gst_avi_demux_handle_seek (avi, update_start || update_stop);
Wim Taymans's avatar
Wim Taymans committed
553
      break;
554

555
    }
Wim Taymans's avatar
Wim Taymans committed
556 557 558 559
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
560

561
done:
Wim Taymans's avatar
Wim Taymans committed
562 563
  gst_event_unref (event);

Wim Taymans's avatar
Wim Taymans committed
564 565 566
  return res;
}

567 568 569 570 571 572 573 574 575 576
/**
 * gst_avi_demux_parse_file_header:
 * @element: caller element (used for errors/debug).
 * @buf: input data to be used for parsing.
 *
 * "Open" a RIFF/AVI file. The buffer should be at least 12
 * bytes long. Discards buffer after use.
 *
 * Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise.
 *          Throws an error, caller should error out (fatal).
577 578
 */

579
static gboolean
580
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
581
{
582
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
583

584
  if (!gst_riff_parse_file_header (element, buf, &doctype))
585
    return FALSE;
586

587 588 589 590 591 592 593 594
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

  /* ERRORS */
not_avi:
  {
595
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
596
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
597
            GST_FOURCC_ARGS (doctype)));
598 599 600 601
    return FALSE;
  }
}

602 603 604 605 606 607 608 609 610 611
static GstFlowReturn
gst_avi_demux_stream_init (GstAviDemux * avi)
{
  GstFlowReturn res;
  GstBuffer *buf = NULL;

  if ((res = gst_pad_pull_range (avi->sinkpad,
              avi->offset, 12, &buf)) != GST_FLOW_OK)
    return res;
  else if (!gst_avi_demux_parse_file_header (GST_ELEMENT (avi), buf))
612
    goto wrong_header;
613 614 615 616

  avi->offset += 12;

  return GST_FLOW_OK;
617 618 619 620 621 622 623 624

  /* ERRORS */
wrong_header:
  {
    GST_DEBUG_OBJECT (avi, "error parsing file header");
    gst_buffer_unref (buf);
    return GST_FLOW_ERROR;
  }
625 626 627 628 629 630 631 632 633 634 635 636 637 638
}

/**
 * gst_avi_demux_parse_avih:
 * @element: caller element (used for errors/debug).
 * @buf: input data to be used for parsing.
 * @avih: pointer to structure (filled in by function) containing
 *        stream information (such as flags, number of streams, etc.).
 *
 * Read 'avih' header. Discards buffer after use.
 *
 * Returns: TRUE on success, FALSE otherwise. Throws an error if
 *          the header is invalid. The caller should error out
 *          (fatal).
639 640
 */

641
static gboolean
642 643
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
644
{
645
  gst_riff_avih *avih;
646

647 648 649 650 651
  if (buf == NULL)
    goto no_buffer;

  if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_avih))
    goto avih_too_small;
652

653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
  avih = g_memdup (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));

#if (G_BYTE_ORDER == G_BIG_ENDIAN)
  avih->us_frame = GUINT32_FROM_LE (avih->us_frame);
  avih->max_bps = GUINT32_FROM_LE (avih->max_bps);
  avih->pad_gran = GUINT32_FROM_LE (avih->pad_gran);
  avih->flags = GUINT32_FROM_LE (avih->flags);
  avih->tot_frames = GUINT32_FROM_LE (avih->tot_frames);
  avih->init_frames = GUINT32_FROM_LE (avih->init_frames);
  avih->streams = GUINT32_FROM_LE (avih->streams);
  avih->bufsize = GUINT32_FROM_LE (avih->bufsize);
  avih->width = GUINT32_FROM_LE (avih->width);
  avih->height = GUINT32_FROM_LE (avih->height);
  avih->scale = GUINT32_FROM_LE (avih->scale);
  avih->rate = GUINT32_FROM_LE (avih->rate);
  avih->start = GUINT32_FROM_LE (avih->start);
  avih->length = GUINT32_FROM_LE (avih->length);
#endif
671 672

  /* debug stuff */
673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
  GST_INFO_OBJECT (element, "avih tag found:");
  GST_INFO_OBJECT (element, " us_frame    %u", avih->us_frame);
  GST_INFO_OBJECT (element, " max_bps     %u", avih->max_bps);
  GST_INFO_OBJECT (element, " pad_gran    %u", avih->pad_gran);
  GST_INFO_OBJECT (element, " flags       0x%08x", avih->flags);
  GST_INFO_OBJECT (element, " tot_frames  %u", avih->tot_frames);
  GST_INFO_OBJECT (element, " init_frames %u", avih->init_frames);
  GST_INFO_OBJECT (element, " streams     %u", avih->streams);
  GST_INFO_OBJECT (element, " bufsize     %u", avih->bufsize);
  GST_INFO_OBJECT (element, " width       %u", avih->width);
  GST_INFO_OBJECT (element, " height      %u", avih->height);
  GST_INFO_OBJECT (element, " scale       %u", avih->scale);
  GST_INFO_OBJECT (element, " rate        %u", avih->rate);
  GST_INFO_OBJECT (element, " start       %u", avih->start);
  GST_INFO_OBJECT (element, " length      %u", avih->length);

  *_avih = avih;
690 691 692
  gst_buffer_unref (buf);

  return TRUE;
693 694 695 696 697 698 699 700 701 702 703 704 705 706 707

  /* ERRORS */
no_buffer:
  {
    GST_ELEMENT_ERROR (element, STREAM, DEMUX, (NULL), ("No buffer"));
    return FALSE;
  }
avih_too_small:
  {
    GST_ELEMENT_ERROR (element, STREAM, DEMUX, (NULL),
        ("Too small avih (%d available, %d needed)",
            GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih)));
    gst_buffer_unref (buf);
    return FALSE;
  }
708 709
}

710 711 712 713 714 715 716 717 718 719 720 721
/**
 * gst_avi_demux_parse_superindex:
 * @element: caller element (used for debugging/errors).
 * @buf: input data to use for parsing.
 * @locations: locations in the file (byte-offsets) that contain
 *             the actual indexes (see get_avi_demux_parse_subindex()).
 *             The array ends with GST_BUFFER_OFFSET_NONE.
 *
 * Reads superindex (openDML-2 spec stuff) from the provided data.
 *
 * Returns: TRUE on success, FALSE otherwise. Indexes should be skipped
 *          on error, but they are not fatal.
722 723 724
 */

static gboolean
725 726
gst_avi_demux_parse_superindex (GstElement * element,
    GstBuffer * buf, guint64 ** _indexes)
727
{
728
  guint8 *data = GST_BUFFER_DATA (buf);
729 730 731
  gint bpe = 16, num, i;
  guint64 *indexes;

732 733
  *_indexes = NULL;

734 735 736 737 738
  if (buf == NULL)
    goto no_buffer;

  if (!buf || GST_BUFFER_SIZE (buf) < 24)
    goto too_small;
739 740 741 742 743 744

  /* check type of index. The opendml2 specs state that
   * there should be 4 dwords per array entry. Type can be
   * either frame or field (and we don't care). */
  if (GST_READ_UINT16_LE (data) != 4 ||
      (data[2] & 0xfe) != 0x0 || data[3] != 0x0) {
745 746
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
747 748 749 750 751 752 753 754 755 756 757 758
        "size_entry %d (bytes) or flags 0x%02x/0x%02x",
        GST_READ_UINT16_LE (data), data[2], data[3]);
    bpe = GST_READ_UINT16_LE (data) * 4;
  }
  num = GST_READ_UINT32_LE (&data[4]);

  indexes = g_new (guint64, num + 1);
  for (i = 0; i < num; i++) {
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (i + 1))
      break;
    indexes[i] = GST_READ_UINT64_LE (&data[24 + bpe * i]);
  }
759 760
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
761 762 763 764

  gst_buffer_unref (buf);

  return TRUE;
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779

  /* ERRORS */
no_buffer:
  {
    GST_ERROR_OBJECT (element, "No buffer");
    return FALSE;
  }
too_small:
  {
    GST_ERROR_OBJECT (element,
        "Not enough data to parse superindex (%d available, %d needed)",
        GST_BUFFER_SIZE (buf), 24);
    gst_buffer_unref (buf);
    return FALSE;
  }
780 781
}

782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799
/**
 * gst_avi_demux_parse_subindex:
 * @element: caller element (used for errors/debug).
 * @buf: input data to use for parsing.
 * @stream: stream context.
 * @entries_list: a list (returned by the function) containing all the
 *           indexes parsed in this specific subindex. The first
 *           entry is also a pointer to allocated memory that needs
 *           to be free´ed. May be NULL if no supported indexes were
 *           found.
 *
 * Reads superindex (openDML-2 spec stuff) from the provided data.
 * The buffer will be discarded after use.
 *
 * Returns: TRUE on success, FALSE otherwise. Errors are fatal, we
 *          throw an error, caller should bail out asap.
 */

800
static gboolean
801 802
gst_avi_demux_parse_subindex (GstElement * element,
    GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list)
803
{
804 805 806
  guint8 *data = GST_BUFFER_DATA (buf);
  gint bpe, num, x;
  guint64 baseoff;
807
  gst_avi_index_entry *entries, *entry;
808
  GList *entries_list = NULL;
809
  GstFormat format = GST_FORMAT_TIME;
810
  gint64 tmp;
811

812
  /* check size */
813 814 815 816 817
  if (buf == NULL)
    goto no_buffer;

  if (GST_BUFFER_SIZE (buf) < 24)
    goto too_small;
818

819
  /* We don't support index-data yet */
820 821
  if (data[3] & 0x80)
    goto not_implemented;
822

823 824 825 826 827 828 829 830 831 832 833 834 835 836
  /* check type of index. The opendml2 specs state that
   * there should be 4 dwords per array entry. Type can be
   * either frame or field (and we don't care). */
  bpe = (data[2] & 0x01) ? 12 : 8;
  if (GST_READ_UINT16_LE (data) != bpe / 4 ||
      (data[2] & 0xfe) != 0x0 || data[3] != 0x1) {
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
        "size_entry %d (bytes) or flags 0x%02x/0x%02x",
        GST_READ_UINT16_LE (data), data[2], data[3]);
    bpe = GST_READ_UINT16_LE (data) * 4;
  }
  num = GST_READ_UINT32_LE (&data[4]);
  baseoff = GST_READ_UINT64_LE (&data[12]);
837

838 839 840
  entries = g_new (gst_avi_index_entry, num);
  for (x = 0; x < num; x++) {
    entry = &entries[x];
841

842 843
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1))
      break;
844

845 846 847 848 849 850 851
    /* fill in */
    entry->offset = baseoff + GST_READ_UINT32_LE (&data[24 + bpe * x]);
    entry->size = GST_READ_UINT32_LE (&data[24 + bpe * x + 4]);
    entry->flags = (entry->size & 0x80000000) ? 0 : GST_RIFF_IF_KEYFRAME;
    entry->size &= ~0x80000000;
    entry->index_nr = x;
    entry->stream_nr = stream->num;
852

853 854 855 856
    /* timestamps */
    if (stream->strh->samplesize && stream->strh->type == GST_RIFF_FCC_auds) {
      /* constant rate stream */
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES,
857 858
          stream->total_bytes, &format, &tmp);
      entry->ts = tmp;
859
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES,
Jan Schmidt's avatar