gstavidemux.c 50.4 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"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
30

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

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
40 41 42
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);
43

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
44 45
static void gst_avi_demux_reset (GstAviDemux * avi);
static void gst_avi_demux_loop (GstElement * element);
46

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
47 48
static gboolean gst_avi_demux_send_event (GstElement * element,
    GstEvent * event);
49

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
50 51 52 53 54 55 56 57 58
static const GstEventMask *gst_avi_demux_get_event_mask (GstPad * pad);
static gboolean gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event);
static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
static const GstQueryType *gst_avi_demux_get_src_query_types (GstPad * pad);
static gboolean gst_avi_demux_handle_src_query (GstPad * pad,
    GstQueryType type, GstFormat * format, gint64 * value);
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
59

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
60
static GstElementStateReturn gst_avi_demux_change_state (GstElement * element);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
61

62
static GstRiffReadClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
63

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
64
GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
65
gst_avi_demux_get_type (void)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
66 67 68 69 70
{
  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
71
      sizeof (GstAviDemuxClass),
72
      (GBaseInitFunc) gst_avi_demux_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
73
      NULL,
74
      (GClassInitFunc) gst_avi_demux_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
75 76
      NULL,
      NULL,
77
      sizeof (GstAviDemux),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
78
      0,
79
      (GInstanceInitFunc) gst_avi_demux_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
80
    };
81 82

    avi_demux_type =
83 84
        g_type_register_static (GST_TYPE_RIFF_READ,
        "GstAviDemux", &avi_demux_info, 0);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
85
  }
86

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
87 88 89
  return avi_demux_type;
}

90
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
91
gst_avi_demux_base_init (GstAviDemuxClass * klass)
92
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
93 94 95 96 97 98 99
  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>");
100 101 102
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstPadTemplate *videosrctempl, *audiosrctempl;
  GstCaps *audcaps, *vidcaps;
103

104
  audcaps = gst_riff_create_audio_template_caps ();
105
  audiosrctempl = gst_pad_template_new ("audio_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
106
      GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
107

David Schleef's avatar
David Schleef committed
108 109
  vidcaps = gst_riff_create_video_template_caps ();
  gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
110
  videosrctempl = gst_pad_template_new ("video_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
111
      GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
112

113 114 115
  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
116
      gst_static_pad_template_get (&sink_templ));
117 118 119
  gst_element_class_set_details (element_class, &gst_avi_demux_details);
}

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
120
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
121
gst_avi_demux_class_init (GstAviDemuxClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
122 123 124 125
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
126 127
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
128

129
  GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
130
      0, "Demuxer for AVI streams");
131 132

  parent_class = g_type_class_ref (GST_TYPE_RIFF_READ);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
133

Wim Taymans's avatar
Wim Taymans committed
134
  gstelement_class->change_state = gst_avi_demux_change_state;
135
  gstelement_class->send_event = gst_avi_demux_send_event;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
136 137
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
138 139
static void
gst_avi_demux_init (GstAviDemux * avi)
140
{
141
  GST_FLAG_SET (avi, GST_ELEMENT_EVENT_AWARE);
142

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
143 144 145
  avi->sinkpad =
      gst_pad_new_from_template (gst_static_pad_template_get (&sink_templ),
      "sink");
146 147
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
  GST_RIFF_READ (avi)->sinkpad = avi->sinkpad;
Wim Taymans's avatar
Wim Taymans committed
148

149 150
  gst_element_set_loop_function (GST_ELEMENT (avi), gst_avi_demux_loop);
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
151

152 153
  avi->index_entries = NULL;
  memset (&avi->stream, 0, sizeof (avi->stream));
Wim Taymans's avatar
Wim Taymans committed
154 155
}

156
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
157
gst_avi_demux_reset (GstAviDemux * avi)
158
{
159
  gint i;
160

161 162 163
  for (i = 0; i < avi->num_streams; i++) {
    g_free (avi->stream[i].strh);
    gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
164
    gst_caps_free (avi->stream[i].caps);
165 166
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
167

168 169 170
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
171

172 173
  avi->state = GST_AVI_DEMUX_START;
  avi->level_up = 0;
174

175 176 177
  if (avi->index_entries) {
    g_free (avi->index_entries);
    avi->index_entries = NULL;
178
  }
179
  avi->index_size = 0;
180
  avi->current_entry = 0;
181

182 183
  avi->num_frames = 0;
  avi->us_per_frame = 0;
184

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
185
  avi->seek_offset = (guint64) - 1;
186 187
}

188
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
189 190
gst_avi_demux_index_next (GstAviDemux * avi,
    gint stream_nr, gint start, guint32 flags)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
191
{
192 193
  gint i;
  gst_avi_index_entry *entry = NULL;
194

195 196
  for (i = start; i < avi->index_size; i++) {
    entry = &avi->index_entries[i];
197

198
    if (entry->stream_nr == stream_nr && (entry->flags & flags) == flags) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
199
      break;
200 201
    }
  }
202

203 204
  return entry;
}
205

206
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
207 208
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
    gint stream_nr, guint64 time, guint32 flags)
209 210 211
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
212

213 214 215 216 217
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
218

219
    i = entry->index_nr;
220

221 222 223 224
    if (entry->ts <= time) {
      last_entry = entry;
    }
  } while (entry->ts <= time);
225

226 227
  return last_entry;
}
228

229
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
230 231
gst_avi_demux_index_entry_for_byte (GstAviDemux * avi,
    gint stream_nr, guint64 byte, guint32 flags)
232 233 234
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
235

236 237 238 239 240
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
241

242
    i = entry->index_nr;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
243

244 245 246 247
    if (entry->bytes_before <= byte) {
      last_entry = entry;
    }
  } while (entry->bytes_before <= byte);
248

249
  return last_entry;
250 251
}

252
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
253 254
gst_avi_demux_index_entry_for_frame (GstAviDemux * avi,
    gint stream_nr, guint32 frame, guint32 flags)
255
{
256 257
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
258

259 260 261 262 263
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
264

265
    i = entry->index_nr;
266

267 268 269 270
    if (entry->frames_before <= frame) {
      last_entry = entry;
    }
  } while (entry->frames_before <= frame);
271

272
  return last_entry;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
273 274
}

275
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
276
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
277
{
278
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
279

280 281 282 283 284 285 286 287 288 289 290
  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
291

292
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
293
      src_a_formats : src_v_formats);
294
}
295

296
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
297 298 299
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
300 301
{
  gboolean res = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
302 303

  /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); */
304
  avi_stream_context *stream = gst_pad_get_element_private (pad);
305

306
  if (stream->strh->type == GST_RIFF_FCC_vids &&
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
307
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
308
    return FALSE;
309

310 311 312
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
313
        case GST_FORMAT_BYTES:
314
          *dest_value = src_value * stream->bitrate / GST_SECOND;
315 316 317 318 319 320 321 322
          break;
        case GST_FORMAT_DEFAULT:
          *dest_value = src_value * stream->strh->rate /
              (stream->strh->scale * GST_SECOND);
          break;
        default:
          res = FALSE;
          break;
323
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
324
      break;
325 326
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
327
        case GST_FORMAT_TIME:
328
          *dest_value = ((gfloat) src_value) * GST_SECOND / stream->bitrate;
329 330 331 332
          break;
        default:
          res = FALSE;
          break;
333 334
      }
      break;
335 336
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
337 338 339 340 341 342 343
        case GST_FORMAT_TIME:
          *dest_value = ((((gfloat) src_value) * stream->strh->scale) /
              stream->strh->rate) * GST_SECOND;
          break;
        default:
          res = FALSE;
          break;
344
      }
Wim Taymans's avatar
Wim Taymans committed
345 346
      break;
    default:
347
      res = FALSE;
348
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
349

350
  return res;
351 352
}

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

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

365
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
366 367
gst_avi_demux_handle_src_query (GstPad * pad,
    GstQueryType type, GstFormat * format, gint64 * value)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
368
{
369
  gboolean res = TRUE;
370
  GstAviDemux *demux = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
371 372

  /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); */
373
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
374 375

  switch (type) {
Wim Taymans's avatar
Wim Taymans committed
376
    case GST_QUERY_TOTAL:
Wim Taymans's avatar
Wim Taymans committed
377
      switch (*format) {
378 379 380 381 382 383 384 385 386 387 388
        case GST_FORMAT_TIME:
          *value = (((gfloat) stream->strh->scale) * stream->strh->length /
              stream->strh->rate) * GST_SECOND;
          break;
        case GST_FORMAT_BYTES:
          if (stream->strh->type == GST_RIFF_FCC_auds) {
            *value = stream->total_bytes;
          } else
            res = FALSE;
          break;
        case GST_FORMAT_DEFAULT:
389 390 391 392 393 394
          if (stream->strh->type == GST_RIFF_FCC_auds) {
            if (!stream->strh->samplesize)
              *value = stream->total_frames;
            else
              *value = stream->total_bytes / stream->strh->samplesize;
          } else if (stream->strh->type == GST_RIFF_FCC_vids)
395 396 397 398 399 400 401
            *value = stream->strh->length;
          else
            res = FALSE;
          break;
        default:
          res = FALSE;
          break;
Wim Taymans's avatar
Wim Taymans committed
402 403
      }
      break;
Wim Taymans's avatar
Wim Taymans committed
404
    case GST_QUERY_POSITION:
Wim Taymans's avatar
Wim Taymans committed
405
      switch (*format) {
406
        case GST_FORMAT_TIME:
407
          if (stream->strh->type == GST_RIFF_FCC_auds) {
408 409 410
            if (!stream->strh->samplesize) {
              *value = GST_SECOND * stream->current_frame *
                  stream->strh->scale / stream->strh->rate;
411
            } else if (stream->bitrate != 0) {
412 413
              *value = ((gfloat) stream->current_byte) * GST_SECOND /
                  stream->bitrate;
414
            } else if (stream->total_frames != 0 && stream->total_bytes != 0) {
415 416 417 418
              /* calculate timestamps based on video size */
              guint64 len = demux->us_per_frame * demux->num_frames *
                  GST_USECOND;

419 420 421 422
              if (!stream->strh->samplesize)
                *value = len * stream->current_frame / stream->total_frames;
              else
                *value = len * stream->current_byte / stream->total_bytes;
423
            } else {
424
              res = FALSE;
425
            }
426
          } else {
427 428 429 430 431 432 433
            if (stream->strh->rate != 0) {
              *value = ((gfloat) stream->current_frame * stream->strh->scale *
                  GST_SECOND / stream->strh->rate);
            } else {
              *value = stream->current_frame * demux->us_per_frame *
                  GST_USECOND;
            }
434 435 436 437 438 439 440 441
          }
          break;
        case GST_FORMAT_BYTES:
          *value = stream->current_byte;
          break;
        case GST_FORMAT_DEFAULT:
          if (stream->strh->samplesize &&
              stream->strh->type == GST_RIFF_FCC_auds)
442
            *value = stream->current_byte / stream->strh->samplesize;
443 444 445 446 447 448
          else
            *value = stream->current_frame;
          break;
        default:
          res = FALSE;
          break;
Wim Taymans's avatar
Wim Taymans committed
449 450 451 452 453 454 455 456 457 458
      }
      break;
    default:
      res = FALSE;
      break;
  }

  return res;
}

459
static GstCaps *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
460
gst_avi_demux_src_getcaps (GstPad * pad)
461 462 463 464 465 466
{
  avi_stream_context *stream = gst_pad_get_element_private (pad);

  return gst_caps_copy (stream->caps);
}

467
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
468
gst_avi_demux_send_event (GstElement * element, GstEvent * event)
469
{
Wim Taymans's avatar
Wim Taymans committed
470
  const GList *pads;
471 472 473

  pads = gst_element_get_pad_list (element);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
474
  while (pads) {
475 476 477
    GstPad *pad = GST_PAD (pads->data);

    if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
Wim Taymans's avatar
Wim Taymans committed
478 479 480 481
      /* we ref the event here as we might have to try again if the event
       * failed on this pad */
      gst_event_ref (event);
      if (gst_avi_demux_handle_src_event (pad, event)) {
482
        gst_event_unref (event);
483

484
        return TRUE;
Wim Taymans's avatar
Wim Taymans committed
485
      }
486
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
487

488 489
    pads = g_list_next (pads);
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
490

Wim Taymans's avatar
Wim Taymans committed
491
  gst_event_unref (event);
492

493 494 495
  return FALSE;
}

496
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
497
gst_avi_demux_get_event_mask (GstPad * pad)
498 499
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
500 501
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
502 503 504 505
  };

  return masks;
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
506

Wim Taymans's avatar
Wim Taymans committed
507
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
508
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
509 510
{
  gboolean res = TRUE;
511
  GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Wim Taymans's avatar
Wim Taymans committed
512
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
513

514 515 516
  if (!avi->index_entries)
    return FALSE;

Wim Taymans's avatar
Wim Taymans committed
517 518 519 520
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
521
      GST_DEBUG ("seek format %d, %08x", GST_EVENT_SEEK_FORMAT (event),
522
          stream->strh->type);
523

Wim Taymans's avatar
Wim Taymans committed
524
      switch (GST_EVENT_SEEK_FORMAT (event)) {
525 526 527
        case GST_FORMAT_BYTES:
        case GST_FORMAT_DEFAULT:
        case GST_FORMAT_TIME:{
528
          gst_avi_index_entry *entry = NULL;
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
          gint64 desired_offset = GST_EVENT_SEEK_OFFSET (event);
          guint32 flags;

          /* no seek on audio yet */
          if (stream->strh->type == GST_RIFF_FCC_auds) {
            res = FALSE;
            goto done;
          }
          GST_DEBUG ("seeking to %" G_GINT64_FORMAT, desired_offset);

          flags = GST_RIFF_IF_KEYFRAME;
          switch (GST_EVENT_SEEK_FORMAT (event)) {
            case GST_FORMAT_BYTES:
              entry = gst_avi_demux_index_entry_for_byte (avi, stream->num,
                  desired_offset, flags);
              break;
            case GST_FORMAT_DEFAULT:
              entry = gst_avi_demux_index_entry_for_frame (avi, stream->num,
                  desired_offset, flags);
              break;
            case GST_FORMAT_TIME:
              entry = gst_avi_demux_index_entry_for_time (avi, stream->num,
                  desired_offset, flags);
              break;
          }

          if (entry) {
556
            avi->seek_offset = entry->offset + avi->index_offset;
557
            avi->last_seek = entry->ts;
558 559 560
            avi->seek_flush =
                (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH);
            avi->seek_entry = entry->index_nr;
561 562 563 564 565
          } else {
            GST_DEBUG ("no index entry found for format=%d value=%"
                G_GINT64_FORMAT, GST_EVENT_SEEK_FORMAT (event), desired_offset);
            res = FALSE;
          }
566
          GST_LOG ("seek done\n");
567 568 569 570 571
          break;
        }
        default:
          res = FALSE;
          break;
Wim Taymans's avatar
Wim Taymans committed
572 573 574 575 576 577
      }
      break;
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
578 579 580 581

done:
  gst_event_unref (event);

Wim Taymans's avatar
Wim Taymans committed
582 583 584
  return res;
}

585 586 587 588
/*
 * "Open" a RIFF file.
 */

589
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
590
gst_avi_demux_stream_init (GstAviDemux * avi)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
591
{
592 593
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
594

595 596 597
  if (!gst_riff_read_header (riff, &doctype))
    return FALSE;
  if (doctype != GST_RIFF_RIFF_AVI) {
598
    GST_ELEMENT_ERROR (avi, STREAM, WRONG_TYPE, (NULL), (NULL));
599 600
    return FALSE;
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
601

602 603 604 605 606 607 608
  return TRUE;
}

/*
 * Read 'avih' header.
 */

609
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
610 611
gst_avi_demux_stream_avih (GstAviDemux * avi,
    guint32 * flags, guint32 * streams)
612 613 614 615
{
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 tag;
  GstBuffer *buf;
616
  gst_riff_avih avih, *_avih;
617 618 619 620 621 622 623 624 625 626 627

  if (!gst_riff_read_data (riff, &tag, &buf))
    return FALSE;

  if (tag != GST_RIFF_TAG_avih) {
    g_warning ("Not a avih chunk");
    gst_buffer_unref (buf);
    return FALSE;
  }
  if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_avih)) {
    g_warning ("Too small avih (%d available, %d needed)",
628
        GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih));
629 630 631 632
    gst_buffer_unref (buf);
    return FALSE;
  }

633
  _avih = (gst_riff_avih *) GST_BUFFER_DATA (buf);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
634 635 636 637 638
  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);
639
  avih.init_frames = GUINT32_FROM_LE (_avih->init_frames);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
640 641 642 643 644 645 646 647
  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);
648 649 650

  /* debug stuff */
  GST_INFO ("avih tag found:");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
651 652 653
  GST_INFO (" us_frame    %u", avih.us_frame);
  GST_INFO (" max_bps     %u", avih.max_bps);
  GST_INFO (" pad_gran    %u", avih.pad_gran);
654
  GST_INFO (" flags       0x%08x", avih.flags);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
655 656 657 658 659 660 661 662 663 664
  GST_INFO (" tot_frames  %u", avih.tot_frames);
  GST_INFO (" init_frames %u", avih.init_frames);
  GST_INFO (" streams     %u", avih.streams);
  GST_INFO (" bufsize     %u", avih.bufsize);
  GST_INFO (" width       %u", avih.width);
  GST_INFO (" height      %u", avih.height);
  GST_INFO (" scale       %u", avih.scale);
  GST_INFO (" rate        %u", avih.rate);
  GST_INFO (" start       %u", avih.start);
  GST_INFO (" length      %u", avih.length);
665 666 667 668 669

  avi->num_frames = avih.tot_frames;
  avi->us_per_frame = avih.us_frame;
  *streams = avih.streams;
  *flags = avih.flags;
670 671 672 673 674 675 676 677 678 679 680

  gst_buffer_unref (buf);

  return TRUE;
}

/*
 * Add a stream.
 */

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
681
gst_avi_demux_add_stream (GstAviDemux * avi)
682 683 684 685 686
{
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (avi);
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 tag;
  gst_riff_strh *strh;
687
  GstBuffer *extradata = NULL, *initdata = NULL;
688 689 690 691 692
  gchar *name = NULL, *padname = NULL;
  GstCaps *caps = NULL;
  GstPadTemplate *templ = NULL;
  GstPad *pad;
  avi_stream_context *stream;
693
  gint blockalign = 0, bitrate = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
694 695
  union
  {
696 697 698
    gst_riff_strf_vids *vids;
    gst_riff_strf_auds *auds;
    gst_riff_strf_iavs *iavs;
699 700
  }
  strf;
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715

  /* the stream starts with a 'strh' header */
  if (!(tag = gst_riff_peek_tag (riff, NULL)))
    return FALSE;
  if (tag != GST_RIFF_TAG_strh) {
    g_warning ("Invalid stream header (no strh at begin)");
    goto skip_stream;
  }
  if (!gst_riff_read_strh (riff, &strh))
    return FALSE;

  /* then comes a 'strf' of that specific type */
  if (!(tag = gst_riff_peek_tag (riff, NULL)))
    return FALSE;
  if (tag != GST_RIFF_TAG_strf) {
716
    GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
717
        ("Invalid AVI header (no strf as second tag)"));
718 719 720 721
    goto skip_stream;
  }
  switch (strh->type) {
    case GST_RIFF_FCC_vids:
722
      if (!gst_riff_read_strf_vids_with_data (riff, &strf.vids, &extradata))
723
        return FALSE;
724 725
      break;
    case GST_RIFF_FCC_auds:
726
      if (!gst_riff_read_strf_auds_with_data (riff, &strf.auds, &extradata))
727
        return FALSE;
728 729 730
      break;
    case GST_RIFF_FCC_iavs:
      if (!gst_riff_read_strf_iavs (riff, &strf.iavs))
731
        return FALSE;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
732
      break;
733 734
    default:
      g_warning ("Unknown stream type " GST_FOURCC_FORMAT,
735
          GST_FOURCC_ARGS (strh->type));
736 737
      goto skip_stream;
  }
Wim Taymans's avatar
Wim Taymans committed
738

739 740 741 742 743 744 745 746
  /* read other things */
  while (TRUE) {
    if (!(tag = gst_riff_peek_tag (riff, &avi->level_up)))
      return FALSE;
    else if (avi->level_up) {
      avi->level_up--;
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
747

748
    switch (tag) {
749 750 751 752 753 754 755
      case GST_RIFF_TAG_strd:
        if (initdata)
          gst_buffer_unref (initdata);
        if (!gst_riff_read_data (riff, &tag, &initdata))
          return FALSE;
        break;

756
      case GST_RIFF_TAG_strn:
757 758 759 760 761
        if (name)
          g_free (name);
        if (!gst_riff_read_ascii (riff, &tag, &name))
          return FALSE;
        break;
Wim Taymans's avatar
Wim Taymans committed
762

763
      default:
764 765 766
        GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " in AVI header",
            GST_FOURCC_ARGS (tag));
        /* fall-through */
767 768

      case GST_RIFF_TAG_JUNK:
769 770 771
        if (!gst_riff_read_skip (riff))
          return FALSE;
        break;
772
    }
Wim Taymans's avatar
Wim Taymans committed
773

774 775
    if (avi->level_up) {
      avi->level_up--;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
776
      break;
Wim Taymans's avatar
Wim Taymans committed
777
    }
778 779 780 781 782
  }

  /* create stream name + pad */
  switch (strh->type) {
    case GST_RIFF_FCC_vids:
783 784 785
    {
      char *codec_name = NULL;
      GstTagList *list = gst_tag_list_new ();
786
      guint32 tag;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
787

788 789
      padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
      templ = gst_element_class_get_pad_template (klass, "video_%02d");
790 791 792 793 794
      if (strf.vids->compression)
        tag = strf.vids->compression;
      else
        tag = strh->fcc_handler;
      caps = gst_riff_create_video_caps_with_data (tag,
795
          strh, strf.vids, extradata, initdata, &codec_name);
796
      gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_VIDEO_CODEC,
797
          codec_name, NULL);
798 799
      gst_element_found_tags (GST_ELEMENT (avi), list);
      gst_tag_list_free (list);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
800
      if (codec_name)
801
        g_free (codec_name);
802 803 804
      g_free (strf.vids);
      avi->num_v_streams++;
      break;
805
    }
806
    case GST_RIFF_FCC_auds:
807 808 809
    {
      char *codec_name = NULL;
      GstTagList *list = gst_tag_list_new ();
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
810

811 812
      padname = g_strdup_printf ("audio_%02d", avi->num_a_streams);
      templ = gst_element_class_get_pad_template (klass, "audio_%02d");
813 814 815
      caps =
          gst_riff_create_audio_caps_with_data (strf.auds->format, strh,
          strf.auds, extradata, initdata, &codec_name);
816
      gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_AUDIO_CODEC,
817
          codec_name, NULL);
818 819
      gst_element_found_tags (GST_ELEMENT (avi), list);
      gst_tag_list_free (list);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
820
      if (codec_name)
821
        g_free (codec_name);
822 823
      blockalign = strf.auds->blockalign;
      bitrate = strf.auds->av_bps;
824 825 826
      g_free (strf.auds);
      avi->num_a_streams++;
      break;
827
    }