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

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

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

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

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

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

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

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

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

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

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

62
static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, guint64 time);
63 64 65 66
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);
67 68
static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element,
    GstStateChange transition);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
69

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

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
72
GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
73
gst_avi_demux_get_type (void)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
74 75 76 77 78
{
  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
79
      sizeof (GstAviDemuxClass),
80
      (GBaseInitFunc) gst_avi_demux_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
81
      NULL,
82
      (GClassInitFunc) gst_avi_demux_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
83 84
      NULL,
      NULL,
85
      sizeof (GstAviDemux),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
86
      0,
87
      (GInstanceInitFunc) gst_avi_demux_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
88
    };
89 90

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

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

98
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
99
gst_avi_demux_base_init (GstAviDemuxClass * klass)
100
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
101 102 103 104 105 106 107
  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>");
108 109 110
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstPadTemplate *videosrctempl, *audiosrctempl;
  GstCaps *audcaps, *vidcaps;
111

112
  audcaps = gst_riff_create_audio_template_caps ();
113
  audiosrctempl = gst_pad_template_new ("audio_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
114
      GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
115

David Schleef's avatar
David Schleef committed
116 117
  vidcaps = gst_riff_create_video_template_caps ();
  gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
118
  videosrctempl = gst_pad_template_new ("video_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
119
      GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
120

121 122 123
  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
124
      gst_static_pad_template_get (&sink_templ));
125 126 127
  gst_element_class_set_details (element_class, &gst_avi_demux_details);
}

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

133
  GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
134
      0, "Demuxer for AVI streams");
135

136
  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
137

Wim Taymans's avatar
Wim Taymans committed
138
  gstelement_class->change_state = gst_avi_demux_change_state;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
139 140
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
141 142
static void
gst_avi_demux_init (GstAviDemux * avi)
143
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
144 145 146
  avi->sinkpad =
      gst_pad_new_from_template (gst_static_pad_template_get (&sink_templ),
      "sink");
147 148 149
  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);
150
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
Wim Taymans's avatar
Wim Taymans committed
151

152
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
153

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

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

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

176 177 178
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
179

180
  avi->state = GST_AVI_DEMUX_START;
181
  avi->offset = 0;
182

183 184
  g_free (avi->index_entries);
  avi->index_entries = NULL;
185
  avi->index_size = 0;
186
  avi->index_offset = 0;
187
  avi->current_entry = 0;
188 189
  g_free (avi->avih);
  avi->avih = NULL;
190

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
191
  avi->seek_offset = (guint64) - 1;
192 193 194
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
195 196
}

197
static gst_avi_index_entry *
198
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
199
{
200 201
  gint i;
  gst_avi_index_entry *entry = NULL;
202

203 204
  for (i = start; i < avi->index_size; i++) {
    entry = &avi->index_entries[i];
205
    if (entry->stream_nr == stream_nr)
Ronald S. Bultje's avatar
Ronald S. Bultje committed
206
      break;
207
  }
208

209 210
  return entry;
}
211

212
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
213 214
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
    gint stream_nr, guint64 time, guint32 flags)
215 216 217
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
218

219 220
  i = -1;
  do {
221
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
222 223
    if (!entry)
      return NULL;
224

225
    i = entry->index_nr;
226

227
    if (entry->ts <= time && (entry->flags & flags) == flags)
228
      last_entry = entry;
229
  } while (entry->ts < time);
230

231 232
  return last_entry;
}
233

234
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
235 236
gst_avi_demux_index_entry_for_byte (GstAviDemux * avi,
    gint stream_nr, guint64 byte, guint32 flags)
237 238 239
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
240

241 242
  i = -1;
  do {
243
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
244 245
    if (!entry)
      return NULL;
246

247
    i = entry->index_nr;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
248

249
    if (entry->bytes_before <= byte && (entry->flags & flags) == flags)
250
      last_entry = entry;
251
  } while (entry->bytes_before < byte);
252

253
  return last_entry;
254 255
}

256
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
257 258
gst_avi_demux_index_entry_for_frame (GstAviDemux * avi,
    gint stream_nr, guint32 frame, guint32 flags)
259
{
260 261
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
262

263 264
  i = -1;
  do {
265
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
266 267
    if (!entry)
      return NULL;
268

269
    i = entry->index_nr;
270

271
    if (entry->frames_before <= frame && (entry->flags & flags) == flags)
272
      last_entry = entry;
273
  } while (entry->frames_before < frame);
274

275
  return last_entry;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
276 277
}

278
#if 0
279
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
280
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
281
{
282
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
283

284 285 286 287 288 289 290 291 292 293 294
  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
295

296
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
297
      src_a_formats : src_v_formats);
298
}
299
#endif
300

301
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
302 303 304
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
305 306
{
  gboolean res = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
307

308
  avi_stream_context *stream = gst_pad_get_element_private (pad);
309

310 311 312 313 314 315
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }
  if (!stream->strh || !stream->strf.data)
    return FALSE;
316
  if (stream->strh->type == GST_RIFF_FCC_vids &&
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
317
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
318
    return FALSE;
319

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

364
  return res;
365 366
}

367
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
368
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
369
{
370 371 372 373
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
374

375
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
376 377
}

378
static gboolean
379
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
380
{
381
  gboolean res = TRUE;
382
  GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
383

384
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
385

386 387 388 389 390
  if (!stream->strh || !stream->strf.data)
    return FALSE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:{
391
      gint64 len, pos = 0;
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408

      len = (((gfloat) stream->strh->scale) * stream->strh->length /
          stream->strh->rate) * GST_SECOND;
      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;
409
          else
410 411
            pos = xlen * stream->current_byte / stream->total_bytes;
        } else {
412
          res = FALSE;
413 414 415 416 417 418 419 420
        }
      } else {
        if (stream->strh->rate != 0) {
          pos = ((gfloat) stream->current_frame * stream->strh->scale *
              GST_SECOND / stream->strh->rate);
        } else {
          pos = stream->current_frame * demux->avih->us_frame * GST_USECOND;
        }
Wim Taymans's avatar
Wim Taymans committed
421
      }
422 423
      if (res)
        gst_query_set_position (query, GST_FORMAT_TIME, pos, len);
Wim Taymans's avatar
Wim Taymans committed
424
      break;
425
    }
Wim Taymans's avatar
Wim Taymans committed
426 427 428 429 430 431 432 433
    default:
      res = FALSE;
      break;
  }

  return res;
}

434
#if 0
435
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
436
gst_avi_demux_get_event_mask (GstPad * pad)
437 438
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
439 440
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
441 442 443 444
  };

  return masks;
}
445
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
446

Wim Taymans's avatar
Wim Taymans committed
447
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
448
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
449 450
{
  gboolean res = TRUE;
451
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Wim Taymans's avatar
Wim Taymans committed
452
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
453

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
454 455 456 457
  GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi,
      "have event type %d: %p on src pad", GST_EVENT_TYPE (event), event);
  if (!avi->index_entries) {
    GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi, "no index entries, returning");
458
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
459
  }
460

Wim Taymans's avatar
Wim Taymans committed
461 462 463 464
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
465 466 467 468 469 470 471 472
    {
      GstFormat format;
      GstSeekFlags flags;
      gint64 cur, stop;

      gst_event_parse_seek (event, NULL, &format, &flags, NULL, &cur, NULL,
          &stop);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
473
      GST_DEBUG_OBJECT (avi, "seek format %d, %08x",
474
          format, stream->strh->type);
475

476
      switch (format) {
477 478 479
        case GST_FORMAT_BYTES:
        case GST_FORMAT_DEFAULT:
        case GST_FORMAT_TIME:{
480
          gst_avi_index_entry *entry = NULL, *real;
481
          gint64 desired_offset = cur;
482 483
          guint32 flags;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
484 485
          GST_DEBUG_OBJECT (avi, "seeking to %" G_GINT64_FORMAT,
              desired_offset);
486 487

          flags = GST_RIFF_IF_KEYFRAME;
488
          switch (format) {
489
            case GST_FORMAT_BYTES:
490
              entry = gst_avi_demux_index_entry_for_byte (avi, 0,       //stream->num,
491
                  desired_offset, flags);
492 493
              real = gst_avi_demux_index_entry_for_byte (avi, stream->num,
                  desired_offset, 0);
494 495
              break;
            case GST_FORMAT_DEFAULT:
496
              entry = gst_avi_demux_index_entry_for_frame (avi, 0,      //stream->num,
497
                  desired_offset, flags);
498 499
              real = gst_avi_demux_index_entry_for_frame (avi, stream->num,
                  desired_offset, 0);
500 501
              break;
            case GST_FORMAT_TIME:
502
              entry = gst_avi_demux_index_entry_for_time (avi, 0,       //stream->num,
503
                  desired_offset, flags);
504 505
              real = gst_avi_demux_index_entry_for_time (avi, stream->num,
                  desired_offset, 0);
506
              break;
507 508
            default:
              break;
509 510
          }

511 512 513
          if (!(flags & GST_SEEK_FLAG_ACCURATE))
            real = entry;

514
          if (entry) {
515
            avi->seek_offset = entry->offset + avi->index_offset;
516
            avi->last_seek = real->ts;
517
            avi->seek_flush = flags & GST_SEEK_FLAG_FLUSH;
518
            avi->seek_entry = entry->index_nr;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
519
            GST_DEBUG_OBJECT (avi, "Will seek to entry %d", avi->seek_entry);
520
            res = gst_avi_demux_handle_seek (avi, real->ts);
521
          } else {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
522
            GST_DEBUG_OBJECT (avi, "no index entry found for format=%d value=%"
523
                G_GINT64_FORMAT, format, desired_offset);
524 525
            res = FALSE;
          }
526
          GST_LOG ("seek done");
527 528 529 530 531
          break;
        }
        default:
          res = FALSE;
          break;
Wim Taymans's avatar
Wim Taymans committed
532 533
      }
      break;
534
    }
Wim Taymans's avatar
Wim Taymans committed
535 536 537 538
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
539 540 541

  gst_event_unref (event);

Wim Taymans's avatar
Wim Taymans committed
542 543 544
  return res;
}

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

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

562
  if (!gst_riff_parse_file_header (element, buf, &doctype))
563
    return FALSE;
564

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

572 573 574
  return TRUE;
}

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 602 603
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).
604 605
 */

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

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

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

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

  return TRUE;
}

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

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

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

  gst_buffer_unref (buf);

  return TRUE;
}

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

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

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

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

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

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

791 792
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1))
      break;
793

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

802 803 804 805
    /* 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,
806 807
          stream->total_bytes, &format, &tmp);
      entry->ts = tmp;
808
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES,
809 810
          stream->total_bytes + entry->size, &format, &tmp);
      entry->dur = tmp;
811 812 813
    } else {
      /* VBR stream */
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
814 815
          stream->total_frames, &format, &tmp);
      entry->ts = tmp;
Ronald S. Bultje's avatar