gstavidemux.c 77.8 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 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
/**
 * SECTION:element-avidemux
 *
 * <refsect2>
 * <para>
 * Demuxes an .avi file into raw or compressed audio and/or video streams.
 * </para>
 * <para>
 * This element currently only supports pull-based scheduling. 
 * </para>
 * <title>Example launch line</title>
 * <para>
 * <programlisting>
 * gst-launch filesrc test.avi ! avidemux name=demux  demux.audio_00 ! decodebin ! audioconvert ! audioresample ! autoaudiosink   demux.video_00 ! queue ! decodebin ! ffmpegcolorspace ! videoscale ! autovideosink
 * </programlisting>
 * Play (parse and decode) an .avi file and try to output it to
 * an automatically detected soundcard and videosink. If the AVI file contains
 * compressed audio or video data, this will only work if you have the
 * right decoder elements/plugins installed.
 * </para>
 * </refsect2>
 *
 */

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

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

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

56 57 58
GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
#define GST_CAT_DEFAULT avidemux_debug

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
59 60
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_EVENT);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
61 62 63 64 65
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-msvideo")
    );
66

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
67 68 69
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);
70

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

73
#if 0
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
74
static const GstEventMask *gst_avi_demux_get_event_mask (GstPad * pad);
75
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
76
static gboolean gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event);
77 78

#if 0
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
79
static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
80
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
81
static const GstQueryType *gst_avi_demux_get_src_query_types (GstPad * pad);
82
static gboolean gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
83 84 85
static gboolean gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
86

87
static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, gboolean update);
88 89 90 91
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);
92 93
static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element,
    GstStateChange transition);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
94

95
static GstElementClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
96

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
97
GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
98
gst_avi_demux_get_type (void)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
99 100 101 102 103
{
  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
104
      sizeof (GstAviDemuxClass),
105
      (GBaseInitFunc) gst_avi_demux_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
106
      NULL,
107
      (GClassInitFunc) gst_avi_demux_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
108 109
      NULL,
      NULL,
110
      sizeof (GstAviDemux),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
111
      0,
112
      (GInstanceInitFunc) gst_avi_demux_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
113
    };
114 115

    avi_demux_type =
116
        g_type_register_static (GST_TYPE_ELEMENT,
117
        "GstAviDemux", &avi_demux_info, 0);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
118
  }
119

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
120 121 122
  return avi_demux_type;
}

123
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
124
gst_avi_demux_base_init (GstAviDemuxClass * klass)
125
{
126
  static const GstElementDetails gst_avi_demux_details =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
127 128 129 130 131 132
      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>");
133 134 135
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstPadTemplate *videosrctempl, *audiosrctempl;
  GstCaps *audcaps, *vidcaps;
136

137
  audcaps = gst_riff_create_audio_template_caps ();
138
  gst_caps_append (audcaps, gst_caps_new_simple ("audio/x-avi-unknown", NULL));
139
  audiosrctempl = gst_pad_template_new ("audio_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
140
      GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
141

David Schleef's avatar
David Schleef committed
142 143
  vidcaps = gst_riff_create_video_template_caps ();
  gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
144
  gst_caps_append (vidcaps, gst_caps_new_simple ("video/x-avi-unknown", NULL));
145
  videosrctempl = gst_pad_template_new ("video_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
146
      GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
147

148 149 150
  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
151
      gst_static_pad_template_get (&sink_templ));
152 153 154
  gst_element_class_set_details (element_class, &gst_avi_demux_details);
}

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
155
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
156
gst_avi_demux_class_init (GstAviDemuxClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
157
{
158
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
159

160
  GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
161
      0, "Demuxer for AVI streams");
162

163
  parent_class = g_type_class_peek_parent (klass);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
164

165
  gstelement_class->change_state = gst_avi_demux_change_state;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
166 167
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
168 169
static void
gst_avi_demux_init (GstAviDemux * avi)
170
{
171
  avi->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
172 173 174
  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);
175
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
Wim Taymans's avatar
Wim Taymans committed
176

177
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
178

179 180
  avi->index_entries = NULL;
  memset (&avi->stream, 0, sizeof (avi->stream));
Wim Taymans's avatar
Wim Taymans committed
181 182
}

183
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
184
gst_avi_demux_reset (GstAviDemux * avi)
185
{
186
  gint i;
187

188 189
  for (i = 0; i < avi->num_streams; i++) {
    g_free (avi->stream[i].strh);
190
    g_free (avi->stream[i].strf.data);
191 192
    if (avi->stream[i].name)
      g_free (avi->stream[i].name);
193 194 195 196
    if (avi->stream[i].initdata)
      gst_buffer_unref (avi->stream[i].initdata);
    if (avi->stream[i].extradata)
      gst_buffer_unref (avi->stream[i].extradata);
197 198
    if (avi->stream[i].pad)
      gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
199 200
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
201

202 203 204
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
205

206
  avi->state = GST_AVI_DEMUX_START;
207
  avi->offset = 0;
208

209 210
  g_free (avi->index_entries);
  avi->index_entries = NULL;
211
  avi->index_size = 0;
212
  avi->index_offset = 0;
213
  avi->current_entry = 0;
214 215
  g_free (avi->avih);
  avi->avih = NULL;
216

217 218 219
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
220 221 222 223 224

  avi->segment_rate = 1.0;
  avi->segment_flags = 0;
  avi->segment_start = -1;
  avi->segment_stop = -1;
225 226
}

227
static gst_avi_index_entry *
228
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
229
{
230 231
  gint i;
  gst_avi_index_entry *entry = NULL;
232

233 234
  for (i = start; i < avi->index_size; i++) {
    entry = &avi->index_entries[i];
235
    if (entry->stream_nr == stream_nr)
Ronald S. Bultje's avatar
Ronald S. Bultje committed
236
      break;
237
  }
238

239 240
  return entry;
}
241

242
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
243 244
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
    gint stream_nr, guint64 time, guint32 flags)
245 246 247
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
248

249 250
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%d",
      stream_nr, GST_TIME_ARGS (time), flags);
251 252
  i = -1;
  do {
253
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
254 255
    if (!entry)
      return NULL;
256

257
    i = entry->index_nr;
258

259 260 261 262
    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);
263
    if (entry->ts <= time && (entry->flags & flags) == flags)
264
      last_entry = entry;
265
  } while (entry->ts < time);
266

267 268
  return last_entry;
}
269

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
270

271
#if 0
272
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
273
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
274
{
275
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
276

277 278 279 280 281 282 283 284 285 286 287
  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
288

289
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
290
      src_a_formats : src_v_formats);
291
}
292
#endif
293

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

302
  avi_stream_context *stream = gst_pad_get_element_private (pad);
303

304 305 306 307
  GST_LOG_OBJECT (avidemux,
      "Received  src_format:%d, src_value:%lld, dest_format:%d", src_format,
      src_value, *dest_format);

308 309
  if (src_format == *dest_format) {
    *dest_value = src_value;
310 311 312 313 314
    goto done;
  }
  if (!stream->strh || !stream->strf.data) {
    res = FALSE;
    goto done;
315
  }
316
  if (stream->strh->type == GST_RIFF_FCC_vids &&
317 318 319 320
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) {
    res = FALSE;
    goto done;
  }
321

322 323 324
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
325
        case GST_FORMAT_BYTES:
326 327 328
          *dest_value =
              gst_util_uint64_scale_int (src_value, stream->strf.auds->av_bps,
              GST_SECOND);
329 330
          break;
        case GST_FORMAT_DEFAULT:
331 332
          *dest_value = gst_util_uint64_scale (src_value, stream->strh->rate,
              stream->strh->scale * GST_SECOND);
333 334 335 336
          break;
        default:
          res = FALSE;
          break;
337
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
338
      break;
339 340
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
341
        case GST_FORMAT_TIME:
342
          if (stream->strf.auds->av_bps != 0) {
343 344
            *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
                stream->strf.auds->av_bps);
345 346
          } else
            res = FALSE;
347 348 349 350
          break;
        default:
          res = FALSE;
          break;
351 352
      }
      break;
353 354
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
355
        case GST_FORMAT_TIME:
356 357 358
          *dest_value =
              gst_util_uint64_scale (src_value,
              stream->strh->scale * GST_SECOND, stream->strh->rate);
359 360 361 362
          break;
        default:
          res = FALSE;
          break;
363
      }
Wim Taymans's avatar
Wim Taymans committed
364 365
      break;
    default:
366
      res = FALSE;
367
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
368

369 370 371 372
done:
  GST_LOG_OBJECT (avidemux, "Returning res:%d dest_format:%d dest_value:%lld",
      res, *dest_format, *dest_value);
  gst_object_unref (avidemux);
373
  return res;
374 375
}

376
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
377
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
378
{
379 380
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
381
    GST_QUERY_DURATION,
382 383
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
384

385
  return src_types;
386 387
}

388
static gboolean
389
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
390
{
391
  gboolean res = TRUE;
392
  GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
393

394
  avi_stream_context *stream = gst_pad_get_element_private (pad);
395

396 397 398 399 400
  if (!stream->strh || !stream->strf.data)
    return FALSE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:{
Wim Taymans's avatar
Wim Taymans committed
401
      gint64 pos = 0;
402 403 404

      if (stream->strh->type == GST_RIFF_FCC_auds) {
        if (!stream->strh->samplesize) {
405 406
          pos = gst_util_uint64_scale_int ((gint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
407
        } else if (stream->strf.auds->av_bps != 0) {
408 409
          pos = gst_util_uint64_scale_int (stream->current_byte, GST_SECOND,
              stream->strf.auds->av_bps);
410 411 412 413 414 415
        } 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)
416 417
            pos = gst_util_uint64_scale_int (xlen, stream->current_frame,
                stream->total_frames);
418
          else
419 420
            pos = gst_util_uint64_scale_int (xlen, stream->current_byte,
                stream->total_bytes);
421
        } else {
422
          res = FALSE;
423 424 425
        }
      } else {
        if (stream->strh->rate != 0) {
426 427 428
          pos =
              gst_util_uint64_scale_int ((guint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
429 430 431
        } else {
          pos = stream->current_frame * demux->avih->us_frame * GST_USECOND;
        }
432
      }
433
      if (res)
Wim Taymans's avatar
Wim Taymans committed
434 435 436 437 438
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
      break;
    }
    case GST_QUERY_DURATION:
    {
439 440 441 442 443 444 445 446 447 448 449 450
      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
451

452 453 454 455 456 457
        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);
      }
458
      break;
459
    }
460
    default:
461
      res = gst_pad_query_default (pad, query);
462 463 464 465 466 467
      break;
  }

  return res;
}

468
#if 0
469
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
470
gst_avi_demux_get_event_mask (GstPad * pad)
471 472
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
473 474
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
475 476 477 478
  };

  return masks;
}
479
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
480

481
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
482
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
483 484
{
  gboolean res = TRUE;
485
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
486
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
487

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
488
  GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi,
489
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
490 491
  if (!avi->index_entries) {
    GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi, "no index entries, returning");
492
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
493
  }
494

495 496 497 498
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
499
    {
500 501
      /* FIXME, this seeking code is not correct, look at wavparse for 
       * a better example */
502 503
      GstFormat format;
      GstSeekFlags flags;
504 505 506 507 508 509 510 511 512 513 514 515
      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);

516 517 518
      GST_DEBUG_OBJECT (avi,
          "seek format %d, flags:%d, %08x, start:%lld, stop:%lld", format,
          flags, stream->strh->type, start, stop);
519 520 521 522 523 524 525 526 527 528 529

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

531 532 533 534
      if (!gst_pad_query_duration (stream->pad, &tformat, &duration)) {
        res = FALSE;
        goto done;
      }
535

536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
      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);
551

552 553 554
      switch (stop_type) {
        case GST_SEEK_TYPE_CUR:
          tstop = avi->segment_stop + tstop;
555
          break;
556 557 558 559 560 561 562 563
        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:
564
          break;
565
      }
566 567 568 569 570 571 572 573 574
      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);
575
      break;
576

577
    }
578 579 580 581
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
582

583
done:
Wim Taymans's avatar
Wim Taymans committed
584 585
  gst_event_unref (event);

586
  GST_DEBUG_OBJECT (avi, "returning %d", res);
587 588 589
  return res;
}

590 591 592 593 594 595 596 597 598 599
/**
 * 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).
600 601
 */

602
static gboolean
603
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
604
{
605
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
606

607
  if (!gst_riff_parse_file_header (element, buf, &doctype))
608
    return FALSE;
609

610 611 612 613 614 615 616 617
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

  /* ERRORS */
not_avi:
  {
618
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
619
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
620
            GST_FOURCC_ARGS (doctype)));
621 622 623 624
    return FALSE;
  }
}

625 626 627 628 629 630 631 632 633 634
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))
635
    goto wrong_header;
636 637 638 639

  avi->offset += 12;

  return GST_FLOW_OK;
640 641 642 643 644 645 646 647

  /* ERRORS */
wrong_header:
  {
    GST_DEBUG_OBJECT (avi, "error parsing file header");
    gst_buffer_unref (buf);
    return GST_FLOW_ERROR;
  }
648 649 650 651 652 653 654 655 656 657 658 659 660 661
}

/**
 * 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).
662 663
 */

664
static gboolean
665 666
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
667
{
668
  gst_riff_avih *avih;
669

670 671 672 673 674
  if (buf == NULL)
    goto no_buffer;

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

676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
  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
694 695

  /* debug stuff */
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
  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;
713 714 715
  gst_buffer_unref (buf);

  return TRUE;
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730

  /* 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;
  }
731 732
}

733 734 735 736 737 738 739 740 741 742 743 744
/**
 * 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.
745 746 747
 */

static gboolean
748 749
gst_avi_demux_parse_superindex (GstElement * element,
    GstBuffer * buf, guint64 ** _indexes)
750
{
751
  guint8 *data;
752 753 754
  gint bpe = 16, num, i;
  guint64 *indexes;

755 756
  *_indexes = NULL;

757 758 759
  if (buf == NULL)
    goto no_buffer;

760
  if (GST_BUFFER_SIZE (buf) < 24)
761
    goto too_small;
762

763 764
  data = GST_BUFFER_DATA (buf);

765 766 767 768 769
  /* 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) {
770 771
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
772 773 774 775 776 777 778 779 780 781 782 783
        "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]);
  }
784 785
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
786 787 788 789

  gst_buffer_unref (buf);

  return TRUE;
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804

  /* 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;
  }
805 806
}

807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
/**
 * 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.
 */

825
static gboolean
826 827
gst_avi_demux_parse_subindex (GstElement * element,
    GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list)
828
{
829 830 831
  guint8 *data = GST_BUFFER_DATA (buf);
  gint bpe, num, x;
  guint64 baseoff;
832
  gst_avi_index_entry *entries, *entry;
833
  GList *entries_list = NULL;
834
  GstFormat format = GST_FORMAT_TIME;
835
  gint64 tmp;
836

837
  /* check size */
838 839 840 841 842
  if (buf == NULL)
    goto no_buffer;

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

844
  /* We don't support index-data yet */
845 846
  if (data[3] & 0x80)
    goto not_implemented;
847

848 849 850 851 852 853 854 855 856 857 858 859 860 861
  /* 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]);
862

863 864 865
  entries = g_new (gst_avi_index_entry, num);
  for (x = 0; x < num; x++) {
    entry = &entries[x];
866

867 868
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1))
      break;
869

870 871 872 873 874 875 876
    /* 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;
877

878 879 880 881
    /* 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,
882 883
          stream->total_bytes, &format, &tmp);
      entry->ts = tmp;
884
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES,
885 886
          stream->total_bytes + entry->size, &format, &tmp);
      entry->dur = tmp;
887 888 889
    } else {
      /* VBR stream */
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
890 891
          stream->total_frames, &format, &tmp);
      entry->ts = tmp;
892
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
893 894
          stream->total_frames + 1, &format, &tmp);
      entry->dur = tmp;
895 896 897 898 899 900 901 902
    }
    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++;
903

904 905
    entries_list = g_list_prepend (entries_list, entry);
  }
906

907
  GST_LOG_OBJECT (element, "Read %d index entries", x);
908

909 910 911 912 913 914 915 916
  gst_buffer_unref (buf);

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

918
  return TRUE;
919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941

  /* ERRORS */
no_buffer:
  {
    GST_ERROR_OBJECT (element, "No buffer");
    return TRUE;                /* continue */
  }
too_small:
  {
    GST_ERROR_OBJECT (element,
        "Not enough data to parse subindex (%d available, %d needed)",
        GST_BUFFER_SIZE (buf), 24);
    gst_buffer_unref (buf);
    *_entries_list = NULL;
    return TRUE;                /* continue */
  }
not_implemented:
  {
    GST_ELEMENT_ERROR (element, STREAM, NOT_IMPLEMENTED, (NULL),
        ("Subindex-is-data is not implemented"));
    gst_buffer_unref (buf);
    return FALSE;
  }
942
}
943

944 945 946 947 948 949 950 951
static void
gst_avi_demux_read_subindexes (GstAviDemux * avi,
    GList ** index, GList ** alloc_list)
{
  GList *list;
  guint32 tag;
  GstBuffer *buf;
  gint i, n;
952

953 954
  for (n = 0; n < avi->num_streams; n++) {
    avi_stream_context *stream = &avi->stream[n];
955

956 957 958 959 960 961 962
    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),
963 964
            "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
            GST_FOURCC_ARGS (tag));
965 966 967 968 969 970 971 972 973 974
        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);
      }
975 976 977 978 979 980 981
    }

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

982 983 984 985 986 987 988 989 990 991 992 993
/**
 * 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.
994 995 996
 */

static gboolean
997
gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf)
998
{
999 1000 1001 1002 1003 1004 1005 1006 1007
  GstAviDemux *avi = GST_AVI_DEMUX (element);
  avi_stream_context *stream = &avi->stream[avi->num_streams];
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
  GstPadTemplate *templ;
  GstBuffer *sub = NULL;
  guint offset = 4;
  guint32 tag = 0;
  gchar *codec_name = NULL, *padname = NULL;
  const gchar *tag_name;
1008 1009 1010
  GstCaps *caps = NULL;
  GstPad *pad;

1011 1012 1013 1014 1015 1016
  GST_DEBUG_OBJECT (element, "Parsing stream");

  /* read strh */
  if (!buf || !gst_riff_parse_chunk (element, buf, &offset, &tag, &sub) ||
      tag != GST_RIFF_TAG_strh) {
    GST_ERROR_OBJECT (element,
1017
        "Failed to find strh chunk (tag: %" GST_FOURCC_FORMAT ")",
1018 1019 1020 1021 1022 1023 1024 1025 1026
        buf ? GST_BUFFER_SIZE (buf) : 0, GST_FOURCC_ARGS (tag));
    goto fail;
  } else if (!gst_riff_parse_strh (element, sub, &stream->strh))
    goto fail;

  /* read strf */
  if (!gst_riff_parse_chunk (element, buf, &offset, &tag, &sub) ||
      tag != GST_RIFF_TAG_strf) {
    GST_ERROR_OBJECT (element,
1027
        "Failed to find strh chunk (size: %d, tag: %"
1028 1029 1030 1031 1032
        GST_FOURCC_FORMAT ")", buf ? GST_BUFFER_SIZE (buf) : 0,
        GST_FOURCC_ARGS (tag));
    goto fail;
  } else {
    gboolean res = FALSE;
1033

1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
    switch (stream->strh->type) {
      case GST_RIFF_FCC_vids:
        res = gst_riff_parse_strf_vids (element, sub,
            &stream->strf.vids, &stream->extradata);
        break;
      case GST_RIFF_FCC_auds:
        res = gst_riff_parse_strf_auds (element, sub,
            &stream->strf.auds, &stream->extradata);
        break;
      case GST_RIFF_FCC_iavs:
        res = gst_riff_parse_strf_iavs (element, sub,
            &stream->strf.iavs, &stream->extradata);
        break;
      default:
        GST_ERROR_OBJECT (element,
1049
            "Don´t know how to handle stream type %" GST_FOURCC_FORMAT,
1050 1051
            GST_FOURCC_ARGS (stream->strh->type));
        break;
1052
    }
1053

1054 1055 1056 1057 1058 1059
    if (!res)
      goto fail;
  }

  /* read strd/strn */
  while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) {
1060
    /* sub can be NULL if the chunk is empty */
1061
    switch (tag) {
1062
      case GST_RIFF_TAG_strd:
1063 1064 1065
        if (stream->initdata)
          gst_buffer_unref (stream->initdata);
        stream->initdata = sub;
1066
        break;
1067
      case GST_RIFF_TAG_strn:
1068
        g_free (stream->name);
1069 1070 1071 1072 1073 1074 1075 1076 1077
        if (sub != NULL) {
          stream->name = g_new (gchar, GST_BUFFER_SIZE (sub) + 1);
          memcpy (stream->name, GST_BUFFER_DATA (sub), GST_BUFFER_SIZE (sub));
          stream->name[GST_BUFFER_SIZE (sub)] = '\0';
          gst_buffer_unref (sub);
          sub = NULL;
        } else {
          stream->name = g_strdup ("");
        }
1078
        break;
1079
      default:
1080
        if (tag == GST_MAKE_FOURCC ('i', 'n', 'd', 'x') ||
1081 1082 1083 1084
            tag == GST_MAKE_FOURCC ('i', 'x', '0' + avi->num_streams / 10,
                '0' + avi->num_streams % 10)) {
          g_free (stream->indexes);
          gst_avi_demux_parse_superindex (element, sub, &stream->indexes);
1085 1086
          break;
        }
1087
        GST_WARNING_OBJECT (element,
1088
            "Unknown stream header tag %" GST_FOURCC_FORMAT ", ignoring",
1089 1090
            GST_FOURCC_ARGS (tag));
        /* fall-through */
1091
      case GST_RIFF_TAG_JUNK:
1092 1093 1094 1095
        if (sub != NULL) {
          gst_buffer_unref (sub);
          sub = NULL;
        }
1096
        break;
1097 1098 1099
    }
  }

1100
  /* we now have all info, let´s set up a pad and a caps and be done */
1101
  /* create stream name + pad */
1102
  switch (stream->strh->type) {
1103 1104 1105 1106 1107
    case GST_RIFF_FCC_vids:{
      guint32 fourcc;

      fourcc = (stream->strf.vids->compression) ?
          stream->strf.vids->compression : stream->strh->fcc_handler;
1108 1109
      padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
      templ = gst_element_class_get_pad_template (klass, "video_%02d");
1110 1111 1112 1113 1114 1115
      caps = gst_riff_create_video_caps (fourcc, stream->strh,
          stream->strf.vids, stream->extradata, stream->initdata, &codec_name);
      if (!caps) {
        caps = gst_caps_new_simple ("video/x-avi-unknown", "fourcc",
            GST_TYPE_FOURCC, fourcc, NULL);
      }
1116
      tag_name = GST_TAG_VIDEO_CODEC;
1117 1118
      avi->num_v_streams++;
      break;
1119 1120
    }
    case GST_RIFF_FCC_auds:{
1121 1122
      padname = g_strdup_printf ("audio_%02d", avi->num_a_streams);
      templ = gst_element_class_get_pad_template (klass, "audio_%02d");
1123
      caps = gst_riff_create_audio_caps (stream->strf.auds->format,
1124 1125 1126 1127 1128 1129
          stream->strh, stream->strf.auds, stream->extradata,
          stream->initdata, &codec_name);
      if (!caps) {
        caps = gst_caps_new_simple ("audio/x-avi-unknown", "codec_id",
            G_TYPE_INT, stream->strf.auds->format, NULL);
      }
1130
      tag_name = GST_TAG_AUDIO_CODEC;
1131 1132
      avi->num_a_streams++;
      break;
1133 1134 1135 1136
    }
    case GST_RIFF_FCC_iavs:{
      guint32 fourcc = stream->strh->fcc_handler;

1137 1138
      padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
      templ = gst_element_class_get_pad_template (klass, "video_%02d");
1139 1140 1141 1142 1143 1144
      caps = gst_riff_create_iavs_caps (fourcc, stream->strh,
          stream->strf.iavs, stream->extradata, stream->initdata, &codec_name);
      if (!caps) {
        caps = gst_caps_new_simple ("video/x-avi-unknown", "fourcc",
            GST_TYPE_FOURCC, fourcc, NULL);
      }
1145
      tag_name = GST_TAG_VIDEO_CODEC;
1146 1147
      avi->num_v_streams++;
      break;
1148
    }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
1149
    default:
1150 1151 1152 1153 1154 1155 1156
      g_assert_not_reached ();
  }

  /* no caps means no stream */
  if (!caps) {
    GST_ERROR_OBJECT (element, "Did not find caps for stream %s", padname);
    goto fail;
1157 1158 1159
  }

  /* set proper settings and add it */
1160 1161
  if (stream->pad)
    gst_object_unref (stream->pad);
1162
  pad = stream->pad = gst_pad_new_from_template (templ, padname);
1163
  stream->last_flow = GST_FLOW_OK;
1164
  stream->idx_duration = GST_CLOCK_TIME_NONE;
1165 1166
  g_free (padname);

1167 1168
  gst_pad_use_fixed_caps (pad);
#if 0
1169
  gst_pad_set_formats_function (pad, gst_avi_demux_get_src_formats);
1170
#endif
1171 1172 1173 1174 1175
  gst_pad_set_event_function (pad, gst_avi_demux_handle_src_event);
  gst_pad_set_query_type_function (pad, gst_avi_demux_get_src_query_types);
  gst_pad_set_query_function (pad, gst_avi_demux_handle_src_query);

  stream->num = avi->num_streams;
1176
  stream->total_bytes = 0;
1177 1178 1179 1180 1181 1182
  stream->total_frames = 0;
  stream->current_frame = 0;
  stream->current_byte = 0;
  stream->current_entry = -1;
  gst_pad_set_element_private (pad, stream);
  avi->num_streams++;
1183
  gst_pad_set_caps (pad, caps);
1184
  gst_pad_set_active (pad, TRUE);
1185
  gst_element_add_pad (GST_ELEMENT (avi), pad);
1186 1187
  GST_LOG_OBJECT (element, "Added pad %s with caps %" GST_PTR_FORMAT,
      GST_PAD_NAME (pad), caps);
1188

1189 1190 1191 1192 1193 1194 1195 1196
  if (codec_name) {
    GstTagList *list = gst_tag_list_new ();

    gst_tag_list_add (list, GST_TAG_MERGE_APPEND, tag_name, codec_name, NULL);
    gst_element_found_tags_for_pad (GST_ELEMENT (avi), pad, list);
    g_free (codec_name);
  }

1197 1198
  return TRUE;

1199
  /* ERRORS */
1200
fail:
1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216
  {
    /* unref any mem that may be in use */
    if (buf)
      gst_buffer_unref (buf);
    if (sub)
      gst_buffer_unref (sub);
    g_free (stream->strh);
    g_free (stream->strf.data);
    g_free (stream->name);
    g_free (stream->indexes);
    if (stream->initdata)
      gst_buffer_unref (stream->initdata);
    if (stream->extradata)
      gst_buffer_unref (stream->extradata);
    memset (stream, 0, sizeof (avi_stream_context));
    avi->num_streams++;
1217

1218 1219
    return FALSE;
  }
1220 1221
}

1222 1223 1224 1225 1226 1227 1228
/**
 * gst_avi_demux_parse_odml:
 * @element: calling element (used for debug/error).
 * @buf: input buffer to be used for parsing.
 *
 * Read an openDML-2.0 extension header. Fills in the frame number
 * in the avi demuxer object when reading succeeds.
1229 1230
 */

1231 1232
static void
gst_avi_demux_parse_odml (GstElement * element, GstBuffer * buf)
1233
{
1234 1235 1236 1237
  GstAviDemux *avi = GST_AVI_DEMUX (element);
  guint32 tag = 0;
  guint offset = 4;
  GstBuffer *sub = NULL;
1238

1239
  while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) {
1240
    switch (tag) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1241
      case GST_RIFF_TAG_dmlh:{
1242 1243
        gst_riff_dmlh dmlh, *_dmlh;

1244
        if (sub && (GST_BUFFER_SIZE (sub) < sizeof (gst_riff_dmlh))) {
1245 1246
          GST_ERROR_OBJECT (element,
              "DMLH entry is too small (%d bytes, %d needed)",
1247
              GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_dmlh));
1248
          gst_buffer_unref (sub);
1249
          sub = NULL;
1250 1251 1252 1253 1254
          break;
        }
        _dmlh = (gst_riff_dmlh *) GST_BUFFER_DATA (buf);
        dmlh.totalframes = GUINT32_FROM_LE (_dmlh->totalframes);

1255 1256
        GST_INFO_OBJECT (element, "dmlh tag found:");
        GST_INFO_OBJECT (element, " totalframes: %u", dmlh.totalframes);
1257

1258
        avi->avih->tot_frames = dmlh.totalframes;