gstavidemux.c 67.6 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 177
    gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
178

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

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

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

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

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

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

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

216 217
  return entry;
}
218

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

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

234
    i = entry->index_nr;
235

236 237 238 239
    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);
240
    if (entry->ts <= time && (entry->flags & flags) == flags)
241
      last_entry = entry;
242
  } while (entry->ts < time);
243

244 245
  return last_entry;
}
246

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
247

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

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

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

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

279
  avi_stream_context *stream = gst_pad_get_element_private (pad);
280

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

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

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

343 344 345 346
done:
  GST_LOG_OBJECT (avidemux, "Returning res:%d dest_format:%d dest_value:%lld",
      res, *dest_format, *dest_value);
  gst_object_unref (avidemux);
347
  return res;
348 349
}

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

359
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
360 361
}

362
static gboolean
363
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
364
{
365
  gboolean res = TRUE;
366
  GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
367

368
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
369

370 371 372 373 374
  if (!stream->strh || !stream->strf.data)
    return FALSE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:{
Wim Taymans's avatar
Wim Taymans committed
375
      gint64 pos = 0;
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390

      if (stream->strh->type == GST_RIFF_FCC_auds) {
        if (!stream->strh->samplesize) {
          pos = GST_SECOND * stream->current_frame *
              stream->strh->scale / stream->strh->rate;
        } else if (stream->strf.auds->av_bps != 0) {
          pos = ((gfloat) stream->current_byte) * GST_SECOND /
              stream->strf.auds->av_bps;
        } 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)
            pos = xlen * stream->current_frame / stream->total_frames;
391
          else
392 393
            pos = xlen * stream->current_byte / stream->total_bytes;
        } else {
394
          res = FALSE;
395 396 397 398 399 400 401 402
        }
      } else {
        if (stream->strh->rate != 0) {
          pos = ((gfloat) stream->current_frame * stream->strh->scale *
              GST_SECOND / stream->strh->rate);
        } else {
          pos = stream->current_frame * demux->avih->us_frame * GST_USECOND;
        }
Wim Taymans's avatar
Wim Taymans committed
403
      }
404
      if (res)
Wim Taymans's avatar
Wim Taymans committed
405 406 407 408 409 410 411 412 413 414
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
      break;
    }
    case GST_QUERY_DURATION:
    {
      gint64 len;

      len = (((gfloat) stream->strh->scale) * stream->strh->length /
          stream->strh->rate) * GST_SECOND;
      gst_query_set_duration (query, GST_FORMAT_TIME, len);
Wim Taymans's avatar
Wim Taymans committed
415
      break;
416
    }
Wim Taymans's avatar
Wim Taymans committed
417 418 419 420 421 422 423 424
    default:
      res = FALSE;
      break;
  }

  return res;
}

425
#if 0
426
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
427
gst_avi_demux_get_event_mask (GstPad * pad)
428 429
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
430 431
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
432 433 434 435
  };

  return masks;
}
436
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
437

Wim Taymans's avatar
Wim Taymans committed
438
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
439
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
440 441
{
  gboolean res = TRUE;
442
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Wim Taymans's avatar
Wim Taymans committed
443
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
444

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
445 446 447 448
  GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi,
      "have event type %d: %p on src pad", GST_EVENT_TYPE (event), event);
  if (!avi->index_entries) {
    GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi, "no index entries, returning");
449
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
450
  }
451

Wim Taymans's avatar
Wim Taymans committed
452 453 454 455
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
456 457 458
    {
      GstFormat format;
      GstSeekFlags flags;
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
      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);

      GST_DEBUG_OBJECT (avi, "seek format %d, %08x, start:%lld, stop:%lld",
          format, stream->strh->type, start, stop);

      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;
      }
484

485 486
      duration = (((gfloat) stream->strh->scale) * stream->strh->length /
          stream->strh->rate) * GST_SECOND;
487

488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
      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);
503

504 505 506
      switch (stop_type) {
        case GST_SEEK_TYPE_CUR:
          tstop = avi->segment_stop + tstop;
507
          break;
508 509 510 511 512 513 514 515
        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:
516
          break;
Wim Taymans's avatar
Wim Taymans committed
517
      }
518 519 520 521 522 523 524 525 526
      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
527
      break;
528

529
    }
Wim Taymans's avatar
Wim Taymans committed
530 531 532 533
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
534

535
done:
Wim Taymans's avatar
Wim Taymans committed
536 537
  gst_event_unref (event);

Wim Taymans's avatar
Wim Taymans committed
538 539 540
  return res;
}

541 542 543 544 545 546 547 548 549 550
/**
 * 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).
551 552
 */

553
static gboolean
554
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
555
{
556
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
557

558
  if (!gst_riff_parse_file_header (element, buf, &doctype))
559
    return FALSE;
560

561
  if (doctype != GST_RIFF_RIFF_AVI) {
562 563 564
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
        ("File is not an AVI file: " GST_FOURCC_FORMAT,
            GST_FOURCC_ARGS (doctype)));
565 566
    return FALSE;
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
567

568 569 570
  return TRUE;
}

571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
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))
    return GST_FLOW_ERROR;

  avi->offset += 12;

  return GST_FLOW_OK;
}

/**
 * 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).
600 601
 */

602
static gboolean
603 604
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
605
{
606
  gst_riff_avih *avih;
607

608 609 610 611 612 613
  if (!buf || GST_BUFFER_SIZE (buf) < sizeof (gst_riff_avih)) {
    GST_ELEMENT_ERROR (element, STREAM, DEMUX, (NULL),
        ("Too small avih (%d available, %d needed)",
            GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih)));
    if (buf)
      gst_buffer_unref (buf);
614 615 616
    return FALSE;
  }

617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
  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
635 636

  /* debug stuff */
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
  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;
654 655 656 657 658
  gst_buffer_unref (buf);

  return TRUE;
}

659 660 661 662 663 664 665 666 667 668 669 670
/**
 * 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.
671 672 673
 */

static gboolean
674 675
gst_avi_demux_parse_superindex (GstElement * element,
    GstBuffer * buf, guint64 ** _indexes)
676
{
677
  guint8 *data = GST_BUFFER_DATA (buf);
678 679 680
  gint bpe = 16, num, i;
  guint64 *indexes;

681 682 683 684 685 686 687 688
  *_indexes = NULL;

  if (!buf || GST_BUFFER_SIZE (buf) < 24) {
    GST_ERROR_OBJECT (element,
        "Not enough data to parse superindex (%d available, %d needed)",
        GST_BUFFER_SIZE (buf), 24);
    if (buf)
      gst_buffer_unref (buf);
689 690 691 692 693 694 695 696
    return FALSE;
  }

  /* 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) {
697 698
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
699 700 701 702 703 704 705 706 707 708 709 710
        "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]);
  }
711 712
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
713 714 715 716 717 718

  gst_buffer_unref (buf);

  return TRUE;
}

719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
/**
 * 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.
 */

737
static gboolean
738 739
gst_avi_demux_parse_subindex (GstElement * element,
    GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list)
740
{
741 742 743
  guint8 *data = GST_BUFFER_DATA (buf);
  gint bpe, num, x;
  guint64 baseoff;
744
  gst_avi_index_entry *entries, *entry;
745
  GList *entries_list = NULL;
746
  GstFormat format = GST_FORMAT_TIME;
747
  gint64 tmp;
748

749 750 751 752 753 754 755 756 757 758
  /* check size */
  if (!buf || GST_BUFFER_SIZE (buf) < 24) {
    GST_ERROR_OBJECT (element,
        "Not enough data to parse subindex (%d available, %d needed)",
        GST_BUFFER_SIZE (buf), 24);
    if (buf)
      gst_buffer_unref (buf);
    *_entries_list = NULL;
    return TRUE;                /* continue */
  }
759

760 761 762 763 764 765 766
  /* We don't support index-data yet */
  if (data[3] & 0x80) {
    GST_ELEMENT_ERROR (element, STREAM, NOT_IMPLEMENTED, (NULL),
        ("Subindex-is-data is not implemented"));
    gst_buffer_unref (buf);
    return FALSE;
  }
767

768 769 770 771 772 773 774 775 776 777 778 779 780 781
  /* 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]);
782

783 784 785
  entries = g_new (gst_avi_index_entry, num);
  for (x = 0; x < num; x++) {
    entry = &entries[x];
786

787 788
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1))
      break;
789

790 791 792 793 794 795 796
    /* 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;
797

798 799 800 801
    /* 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,
802 803
          stream->total_bytes, &format, &tmp);
      entry->ts = tmp;
804
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES,
805 806
          stream->total_bytes + entry->size, &format, &tmp);
      entry->dur = tmp;
807 808 809
    } else {
      /* VBR stream */
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
810 811
          stream->total_frames, &format, &tmp);
      entry->ts = tmp;
812
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
813 814
          stream->total_frames + 1, &format, &tmp);
      entry->dur = tmp;
815 816 817 818 819 820 821 822
    }
    entry->dur -= entry->ts;

    /* stream position */
    entry->bytes_before = stream->total_bytes;
    stream->total_bytes += entry->size;
    entry->frames_before = stream->total_frames;
    stream->total_frames++;
823

824 825
    entries_list = g_list_prepend (entries_list, entry);
  }
826

827
  GST_LOG_OBJECT (element, "Read %d index entries", x);
828

829 830 831 832 833 834 835 836
  gst_buffer_unref (buf);

  if (x > 0) {
    *_entries_list = g_list_reverse (entries_list);
  } else {
    *_entries_list = NULL;
    g_free (entries);
  }
837

838 839
  return TRUE;
}
840

841 842 843 844 845 846 847 848
static void
gst_avi_demux_read_subindexes (GstAviDemux * avi,
    GList ** index, GList ** alloc_list)
{
  GList *list;
  guint32 tag;
  GstBuffer *buf;
  gint i, n;
849

850 851
  for (n = 0; n < avi->num_streams; n++) {
    avi_stream_context *stream = &avi->stream[n];
852

853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870
    for (i = 0; stream->indexes[i] != GST_BUFFER_OFFSET_NONE; i++) {
      if (gst_riff_read_chunk (GST_ELEMENT (avi), avi->sinkpad,
              &stream->indexes[i], &tag, &buf) != GST_FLOW_OK)
        continue;
      else if (tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10,
              '0' + stream->num % 10)) {
        GST_ERROR_OBJECT (GST_ELEMENT (avi),
            "Not an ix## chunk (" GST_FOURCC_FORMAT ")", GST_FOURCC_ARGS (tag));
        gst_buffer_unref (buf);
        continue;
      }

      if (!gst_avi_demux_parse_subindex (GST_ELEMENT (avi), buf, stream, &list))
        continue;
      if (list) {
        *alloc_list = g_list_append (*alloc_list, list->data);
        *index = g_list_concat (*index, list);
      }
871 872 873 874 875 876 877
    }

    g_free (stream->indexes);
    stream->indexes = NULL;
  }
}

878 879 880 881 882 883 884 885 886 887 888 889
/**
 * gst_avi_demux_parse_stream:
 * @element: calling element (used for debugging/errors).
 * @buf: input buffer used to parse the stream.
 *
 * Parses all subchunks in a strl chunk (which defines a single
 * stream). Discards the buffer after use. This function will
 * increment the stream counter internally.
 *
 * Returns: whether the stream was identified successfully.
 *          Errors are not fatal. It does indicate the stream
 *          was skipped.
890 891 892
 */

static gboolean
893
gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf)