gstavidemux.c 71.5 KB
Newer Older
1 2
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com>
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
19
/* Element-Checklist-Version: 5 */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
20

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

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

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

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

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

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

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

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

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

#if 0
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
55
static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
56
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
57
static const GstQueryType *gst_avi_demux_get_src_query_types (GstPad * pad);
58
static gboolean gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
59 60 61
static gboolean gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
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

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

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

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

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

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

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

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

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

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

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

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

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

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

217 218
  return entry;
}
219

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

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

235
    i = entry->index_nr;
236

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

245 246
  return last_entry;
}
247

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
248

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

255 256 257 258 259 260 261 262 263 264 265
  static const GstFormat src_a_formats[] = {
    GST_FORMAT_TIME,
    GST_FORMAT_BYTES,
    GST_FORMAT_DEFAULT,
    0
  };
  static const GstFormat src_v_formats[] = {
    GST_FORMAT_TIME,
    GST_FORMAT_DEFAULT,
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
266

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

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

280
  avi_stream_context *stream = gst_pad_get_element_private (pad);
281

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

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

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

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

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

360
  return src_types;
361 362
}

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

369
  avi_stream_context *stream = gst_pad_get_element_private (pad);
370

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

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

      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;
392
          else
393 394
            pos = xlen * stream->current_byte / stream->total_bytes;
        } else {
395
          res = FALSE;
396 397 398 399 400 401 402 403
        }
      } 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;
        }
404
      }
405
      if (res)
Wim Taymans's avatar
Wim Taymans committed
406 407 408 409 410 411 412 413 414 415
        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);
416
      break;
417
    }
418 419 420 421 422 423 424 425
    default:
      res = FALSE;
      break;
  }

  return res;
}

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

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

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

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

453 454 455 456
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
457 458 459
    {
      GstFormat format;
      GstSeekFlags flags;
460 461 462 463 464 465 466 467 468 469 470 471
      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);

472 473 474
      GST_DEBUG_OBJECT (avi,
          "seek format %d, flags:%d, %08x, start:%lld, stop:%lld", format,
          flags, stream->strh->type, start, stop);
475 476 477 478 479 480 481 482 483 484 485

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

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

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

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

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

537
done:
Wim Taymans's avatar
Wim Taymans committed
538 539
  gst_event_unref (event);

540 541 542
  return res;
}

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

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

560
  if (!gst_riff_parse_file_header (element, buf, &doctype))
561
    return FALSE;
562

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

570 571 572
  return TRUE;
}

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

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

610 611 612 613 614 615
  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);
616 617 618
    return FALSE;
  }

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

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

  return TRUE;
}

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

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

683 684 685 686 687 688 689 690
  *_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);
691 692 693 694 695 696 697 698
    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) {
699 700
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
701 702 703 704 705 706 707 708 709 710 711 712
        "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]);
  }
713 714
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
715 716 717 718 719 720

  gst_buffer_unref (buf);

  return TRUE;
}

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

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

751 752 753 754 755 756 757 758 759 760
  /* 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 */
  }
761

762 763 764 765 766 767 768
  /* 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;
  }
769

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

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

789 790
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1))
      break;
791

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

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

826 827
    entries_list = g_list_prepend (entries_list, entry);
  }
828

829
  GST_LOG_OBJECT (element, "Read %d index entries", x);
830

831 832 833 834 835 836 837 838
  gst_buffer_unref (buf);

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

840 841
  return TRUE;
}
842

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

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

855 856 857 858 859 860 861
    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),
862 863
            "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
            GST_FOURCC_ARGS (tag));
864 865 866 867 868 869 870 871 872 873
        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);
      }
874 875 876 877 878 879 880
    }

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

881 882 883 884 885 886 887 888 889 890 891 892
/**
 * 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.
893 894 895
 */

static gboolean
896
gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf)
897
{
898 899 900 901 902 903 904 905 906
  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;
907 908 909
  GstCaps *caps = NULL;
  GstPad *pad;

910 911 912 913 914 915
  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,
916
        "Failed to find strh chunk (tag: %" GST_FOURCC_FORMAT ")",
917 918 919 920 921 922 923 924 925
        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,
926
        "Failed to find strh chunk (size: %d, tag: %"
927 928 929 930 931
        GST_FOURCC_FORMAT ")", buf ? GST_BUFFER_SIZE (buf) : 0,
        GST_FOURCC_ARGS (tag));
    goto fail;
  } else {
    gboolean res = FALSE;
932

933 934 935 936 937 938 939 940 941 942 943 944 945 946 947
    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,
948
            "Don´t know how to handle stream type %" GST_FOURCC_FORMAT,
949 950
            GST_FOURCC_ARGS (stream->strh->type));
        break;
951
    }
952

953 954 955 956 957 958
    if (!res)
      goto fail;
  }

  /* read strd/strn */
  while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) {
959
    switch (tag) {
960
      case GST_RIFF_TAG_strd:
961 962 963
        if (stream->initdata)
          gst_buffer_unref (stream->initdata);
        stream->initdata = sub;
964
        break;
965
      case GST_RIFF_TAG_strn:
966
        g_free (stream->name);
967
        stream->name = g_new (gchar, GST_BUFFER_SIZE (sub) + 1);
968 969 970
        memcpy (stream->name, GST_BUFFER_DATA (sub), GST_BUFFER_SIZE (sub));
        stream->name[GST_BUFFER_SIZE (sub)] = '\0';
        gst_buffer_unref (sub);
971
        sub = NULL;
972
        break;
973
      default:
974
        if (tag == GST_MAKE_FOURCC ('i', 'n', 'd', 'x') ||
975 976 977 978
            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);
979 980
          break;
        }
981
        GST_WARNING_OBJECT (element,
982
            "Unknown stream header tag %" GST_FOURCC_FORMAT ", ignoring",
983 984
            GST_FOURCC_ARGS (tag));
        /* fall-through */
985
      case GST_RIFF_TAG_JUNK:
986
        gst_buffer_unref (sub);
987
        sub = NULL;
988
        break;
989 990 991
    }
  }

992
  /* we now have all info, let´s set up a pad and a caps and be done */
993
  /* create stream name + pad */
994
  switch (stream->strh->type) {
995 996 997 998 999
    case GST_RIFF_FCC_vids:{
      guint32 fourcc;

      fourcc = (stream->strf.vids->compression) ?
          stream->strf.vids->compression : stream->strh->fcc_handler;
1000 1001
      padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
      templ = gst_element_class_get_pad_template (klass, "video_%02d");
1002 1003 1004 1005 1006 1007
      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);
      }
1008
      tag_name = GST_TAG_VIDEO_CODEC;
1009 1010
      avi->num_v_streams++;
      break;
1011 1012
    }
    case GST_RIFF_FCC_auds:{
1013 1014
      padname = g_strdup_printf ("audio_%02d", avi->num_a_streams);
      templ = gst_element_class_get_pad_template (klass, "audio_%02d");
1015
      caps = gst_riff_create_audio_caps (stream->strf.auds->format,
1016 1017 1018 1019 1020 1021
          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);
      }
1022
      tag_name = GST_TAG_AUDIO_CODEC;
1023 1024
      avi->num_a_streams++;
      break;
1025 1026 1027 1028
    }
    case GST_RIFF_FCC_iavs:{
      guint32 fourcc = stream->strh->fcc_handler;

1029 1030
      padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
      templ = gst_element_class_get_pad_template (klass, "video_%02d");
1031 1032 1033 1034 1035 1036
      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);
      }
1037
      tag_name = GST_TAG_VIDEO_CODEC;
1038 1039
      avi->num_v_streams++;
      break;
1040
    }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
1041
    default:
1042 1043 1044 1045 1046 1047 1048
      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;
1049 1050 1051
  }

  /* set proper settings and add it */
1052 1053
  if (stream->pad)
    gst_object_unref (stream->pad);
1054
  pad = stream->pad = gst_pad_new_from_template (templ, padname);
1055 1056
  g_free (padname);

1057 1058
  gst_pad_use_fixed_caps (pad);
#if 0
1059 1060
  gst_pad_set_formats_function (pad, gst_avi_demux_get_src_formats);
  gst_pad_set_event_mask_function (pad, gst_avi_demux_get_event_mask);
1061
#endif
1062 1063 1064
  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);
1065
#if 0
1066
  gst_pad_set_convert_function (pad, gst_avi_demux_src_convert);
1067
#endif
1068 1069

  stream->num = avi->num_streams;
1070
  stream->total_bytes = 0;
1071 1072 1073 1074 1075 1076
  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++;
1077
  gst_pad_set_caps (pad, caps);
1078
  gst_pad_set_active (pad, TRUE);
1079
  gst_element_add_pad (GST_ELEMENT (avi), pad);
1080 1081
  GST_LOG_OBJECT (element, "Added pad %s with caps %" GST_PTR_FORMAT,
      GST_PAD_NAME (pad), caps);
1082

1083 1084 1085 1086 1087 1088 1089 1090
  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);
  }

1091 1092
  return TRUE;

1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107
fail:
  /* 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));
1108 1109
  avi->num_streams++;

1110
  return FALSE;
1111 1112
}

1113 1114 1115 1116 1117 1118 1119
/**
 * 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.
1120 1121
 */

1122 1123
static void
gst_avi_demux_parse_odml (GstElement * element, GstBuffer * buf)
1124
{
1125 1126 1127 1128
  GstAviDemux *avi = GST_AVI_DEMUX (element);
  guint32 tag = 0;
  guint offset = 4;
  GstBuffer *sub = NULL;
1129

1130
  while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) {
1131
    switch (tag) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1132
      case GST_RIFF_TAG_dmlh:{
1133 1134
        gst_riff_dmlh dmlh, *_dmlh;

1135
        if (sub && (GST_BUFFER_SIZE (sub) < sizeof (gst_riff_dmlh))) {
1136 1137
          GST_ERROR_OBJECT (element,
              "DMLH entry is too small (%d bytes, %d needed)",
1138
              GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_dmlh));
1139
          gst_buffer_unref (sub);
1140
          sub = NULL;
1141 1142 1143 1144 1145
          break;
        }
        _dmlh = (gst_riff_dmlh *) GST_BUFFER_DATA (buf);
        dmlh.totalframes = GUINT32_FROM_LE (_dmlh->totalframes);

1146 1147
        GST_INFO_OBJECT (element, "dmlh tag found:");
        GST_INFO_OBJECT (element, " totalframes: %u", dmlh.totalframes);
1148

1149
        avi->avih->tot_frames = dmlh.totalframes;
1150 1151 1152 1153
        if (sub) {
          gst_buffer_unref (sub);
          sub = NULL;
        }
1154
        break;
1155 1156 1157
      }

      default:
1158
        GST_WARNING_OBJECT (element,
1159
            "Unknown tag %" GST_FOURCC_FORMAT " in ODML header",
1160 1161
            GST_FOURCC_ARGS (tag));
        /* fall-through */
1162
      case GST_RIFF_TAG_JUNK:
1163 1164 1165 1166
        if (sub) {
          gst_buffer_unref (sub);
          sub = NULL;
        }
1167
        break;
1168 1169 1170
    }
  }

1171 1172
  if (buf)
    gst_buffer_unref (buf);
1173 1174
}

1175 1176 1177 1178 1179 1180 1181 1182 1183 1184
/**
 * gst_avi_demux_parse_index:
 * @element: calling element (used for debugging/errors).
 * @buf: buffer containing the full index.
 * @entries_list: list (returned by this function) containing the index
 *                entries parsed from the buffer. The first in the list
 *                is also a pointer to the allocated data and should be
 *                free'ed at some point.
 *
 * Read index entries from the provided buffer.
1185 1186
 */

1187 1188 1189
static void
gst_avi_demux_parse_index (GstElement * element,
    GstBuffer * buf, GList ** _entries_list)
1190
{
1191 1192 1193 1194 1195 1196
  GstAviDemux *avi = GST_AVI_DEMUX (element);
  guint64 pos_before = avi->offset;
  gst_avi_index_entry *entries = NULL;
  guint8 *data;
  GList *entries_list = NULL;
  guint i, num, n;
1197

1198 1199 1200
  if (!buf) {
    *_entries_list = NULL;
    return;
1201 1202
  }

1203 1204 1205
  data = GST_BUFFER_DATA (buf);
  num = GST_BUFFER_SIZE (buf) / sizeof (gst_riff_index_entry);
  entries = g_new (gst_avi_index_entry, num);
1206

1207
  for (i = 0, n = 0; i < num; i++) {
1208
    gst_riff_index_entry entry, *_entry;
1209 1210 1211 1212
    avi_stream_context *stream;
    gint stream_nr;
    gst_avi_index_entry *target;
    GstFormat format;
1213
    gint64 ts;
1214

1215
    _entry = &((gst_riff_index_entry *) data)[i];
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1216
    entry.id = GUINT32_FROM_LE (_entry->id);
1217
    entry.offset = GUINT32_FROM_LE (_entry->offset);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1218 1219
    entry.flags = GUINT32_FROM_LE (_entry->flags);
    entry.size = GUINT32_FROM_LE (_entry->size);
1220
    target = &entries[n];
1221

1222
    if (entry.id == GST_RIFF_rec || entry.id == 0 ||
1223
        (entry.offset == 0 && n > 0))
1224 1225
      continue;

1226
    stream_nr = CHUNKID_TO_STREAMNR (entry.id);
1227
    if (stream_nr >= avi->num_streams || stream_nr < 0) {
1228 1229
      GST_WARNING_OBJECT (element,
          "Index entry %d has invalid stream nr %d", i, stream_nr);