gstavidemux.c 61.7 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 47
static void gst_avi_demux_reset (GstAviDemux * avi);
static void gst_avi_demux_loop (GstElement * element);
48

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
52 53 54 55 56 57 58 59 60
static const GstEventMask *gst_avi_demux_get_event_mask (GstPad * pad);
static gboolean gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event);
static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
static const GstQueryType *gst_avi_demux_get_src_query_types (GstPad * pad);
static gboolean gst_avi_demux_handle_src_query (GstPad * pad,
    GstQueryType type, GstFormat * format, gint64 * value);
static gboolean gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
Wim Taymans's avatar
Wim Taymans committed
61

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

64
static GstRiffReadClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
65

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

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

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
89 90 91
  return avi_demux_type;
}

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

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

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

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

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

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

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

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

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

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

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

151 152
  gst_element_set_loop_function (GST_ELEMENT (avi), gst_avi_demux_loop);
  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 165
  for (i = 0; i < avi->num_streams; i++) {
    g_free (avi->stream[i].strh);
    gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
166
    gst_caps_free (avi->stream[i].caps);
167 168
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
169

170 171 172
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
173

174 175
  avi->state = GST_AVI_DEMUX_START;
  avi->level_up = 0;
176

177 178 179
  if (avi->index_entries) {
    g_free (avi->index_entries);
    avi->index_entries = NULL;
180
  }
181
  avi->index_size = 0;
182
  avi->index_offset = 0;
183
  avi->current_entry = 0;
184

185 186
  avi->num_frames = 0;
  avi->us_per_frame = 0;
187

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
188
  avi->seek_offset = (guint64) - 1;
189 190
}

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

198 199
  for (i = start; i < avi->index_size; i++) {
    entry = &avi->index_entries[i];
200

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

206 207
  return entry;
}
208

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

216 217 218 219 220
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
221

222
    i = entry->index_nr;
223

224 225 226 227
    if (entry->ts <= time) {
      last_entry = entry;
    }
  } while (entry->ts <= time);
228

229 230
  return last_entry;
}
231

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

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

245
    i = entry->index_nr;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
246

247 248 249 250
    if (entry->bytes_before <= byte) {
      last_entry = entry;
    }
  } while (entry->bytes_before <= byte);
251

252
  return last_entry;
253 254
}

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

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

268
    i = entry->index_nr;
269

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

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

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

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

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

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

  /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); */
307
  avi_stream_context *stream = gst_pad_get_element_private (pad);
308

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

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

353
  return res;
354 355
}

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

365
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
366 367
}

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

  /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); */
376
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
377 378

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

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

  return res;
}

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

  return gst_caps_copy (stream->caps);
}

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

  pads = gst_element_get_pad_list (element);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
477
  while (pads) {
478 479 480
    GstPad *pad = GST_PAD (pads->data);

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

487
        return TRUE;
Wim Taymans's avatar
Wim Taymans committed
488
      }
489
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
490

491 492
    pads = g_list_next (pads);
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
493

Wim Taymans's avatar
Wim Taymans committed
494
  gst_event_unref (event);
495

496 497 498
  return FALSE;
}

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

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

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
517 518 519 520
  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");
521
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
522
  }
523

Wim Taymans's avatar
Wim Taymans committed
524 525 526 527
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
528 529
      GST_DEBUG_OBJECT (avi, "seek format %d, %08x",
          GST_EVENT_SEEK_FORMAT (event), stream->strh->type);
530

Wim Taymans's avatar
Wim Taymans committed
531
      switch (GST_EVENT_SEEK_FORMAT (event)) {
532 533 534
        case GST_FORMAT_BYTES:
        case GST_FORMAT_DEFAULT:
        case GST_FORMAT_TIME:{
535
          gst_avi_index_entry *entry = NULL;
536 537 538
          gint64 desired_offset = GST_EVENT_SEEK_OFFSET (event);
          guint32 flags;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
539 540
          GST_DEBUG_OBJECT (avi, "seeking to %" G_GINT64_FORMAT,
              desired_offset);
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558

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

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

  gst_event_unref (event);

Wim Taymans's avatar
Wim Taymans committed
585 586 587
  return res;
}

588 589 590 591
/*
 * "Open" a RIFF file.
 */

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

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

605 606 607 608 609 610 611
  return TRUE;
}

/*
 * Read 'avih' header.
 */

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

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

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

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

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

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

  gst_buffer_unref (buf);

  return TRUE;
}

679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869
/*
 * Read superindex/subindex (openDML-2).
 */

static gboolean
gst_avi_demux_read_superindex (GstAviDemux * avi,
    gint stream_nr, guint64 ** locations)
{
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 tag;
  GstBuffer *buf;
  guint8 *data;
  gint bpe = 16, num, i;
  guint64 *indexes;

  if (!gst_riff_read_data (riff, &tag, &buf))
    return FALSE;
  data = GST_BUFFER_DATA (buf);
  if (tag != GST_MAKE_FOURCC ('i', 'n', 'd', 'x') &&
      tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream_nr / 10,
          '0' + stream_nr % 10)) {
    g_warning ("Not an indx/ix## chunk");
    gst_buffer_unref (buf);
    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) {
    GST_WARNING ("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]);

  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]);
  }
  indexes[i] = 0;
  *locations = indexes;

  gst_buffer_unref (buf);

  return TRUE;
}

static gboolean
gst_avi_demux_read_subindexes (GstAviDemux * avi,
    GList ** index, GList ** alloc_list)
{
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint64 pos = gst_bytestream_tell (riff->bs),
      length = gst_bytestream_length (riff->bs), baseoff;
  GstEvent *event;
  GList *list = NULL;
  gst_avi_index_entry *entries, *entry;
  guint32 tag;
  GstBuffer *buf;
  guint8 *data;
  GstFormat format = GST_FORMAT_TIME;
  gint bpe, num, x, i, n;

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

    for (i = 0; stream->indexes[i] != 0; i++) {
      /* eos check again */
      if (stream->indexes[i] + 8 >= length) {
        GST_WARNING ("Subindex %d for stream %d doesn't exist", i, n);
        continue;
      }

      /* seek to that point */
      if (!(event = gst_riff_read_seek (riff, stream->indexes[i]))) {
        g_list_free (list);
        return FALSE;
      }
      gst_event_unref (event);
      if (gst_bytestream_peek_bytes (riff->bs, &data, 8) != 8) {
        g_list_free (list);
        return FALSE;
      }

      /* eos check again */
      if (GST_READ_UINT32_LE (&data[4]) + gst_bytestream_tell (riff->bs) >
          length) {
        GST_WARNING ("Subindex %d for stream %d lies outside file", i, n);
        continue;
      }

      /* now read */
      if (!gst_riff_read_data (riff, &tag, &buf))
        return FALSE;
      data = GST_BUFFER_DATA (buf);
      if (tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10,
              '0' + stream->num % 10)) {
        GST_WARNING ("Not an ix## chunk (" GST_FOURCC_FORMAT ")",
            GST_FOURCC_ARGS (tag));
        gst_buffer_unref (buf);
        continue;
      }

      /* We don't support index-data yet */
      if (data[3] & 0x80) {
        GST_ELEMENT_ERROR (avi, STREAM, NOT_IMPLEMENTED, (NULL),
            ("Subindex-is-data is not implemented"));
        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). */
      bpe = (data[2] & 0x01) ? 12 : 8;
      if (GST_READ_UINT16_LE (data) != bpe / 4 ||
          (data[2] & 0xfe) != 0x0 || data[3] != 0x1) {
        GST_WARNING ("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]);

      entries = g_new (gst_avi_index_entry, num);
      *alloc_list = g_list_append (*alloc_list, entries);
      for (x = 0; x < num; x++) {
        entry = &entries[x];

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

        /* 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;

        /* timestamps */
        if (stream->strh->samplesize && stream->strh->type == GST_RIFF_FCC_auds) {
          /* constant rate stream */
          gst_pad_convert (stream->pad, GST_FORMAT_BYTES,
              stream->total_bytes, &format, &entry->ts);
          gst_pad_convert (stream->pad, GST_FORMAT_BYTES,
              stream->total_bytes + entry->size, &format, &entry->dur);
        } else {
          /* VBR stream */
          gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT,
              stream->total_frames, &format, &entry->ts);
          gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT,
              stream->total_frames + 1, &format, &entry->dur);
        }
        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++;

        list = g_list_prepend (list, entry);
      }

      GST_LOG ("Read %d index entries in subindex %d for stream %d "
          "at location %" G_GUINT64_FORMAT, num, i, n, stream->indexes[i]);

      gst_buffer_unref (buf);
    }

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

  /* seek back */
  if (!(event = gst_riff_read_seek (riff, pos))) {
    g_list_free (list);
    return FALSE;
  }
  gst_event_unref (event);
  *index = g_list_reverse (list);

  return TRUE;
}

870 871 872 873 874
/*
 * Add a stream.
 */

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
875
gst_avi_demux_add_stream (GstAviDemux * avi)
876 877 878 879 880
{
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (avi);
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 tag;
  gst_riff_strh *strh;
881
  GstBuffer *extradata = NULL, *initdata = NULL;
882 883 884 885 886
  gchar *name = NULL, *padname = NULL;
  GstCaps *caps = NULL;
  GstPadTemplate *templ = NULL;
  GstPad *pad;
  avi_stream_context *stream;
887
  gint blockalign = 0, bitrate = 0, width = 0, height = 0;
888
  guint64 *locations = NULL;
889 890
  GstTagList *list = gst_tag_list_new ();
  gboolean have_tag = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
891 892
  union
  {
893 894 895
    gst_riff_strf_vids *vids;
    gst_riff_strf_auds *auds;
    gst_riff_strf_iavs *iavs;
896 897
  }
  strf;
898 899 900 901 902 903 904 905 906 907 908 909 910 911 912

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

  /* then comes a 'strf' of that specific type */
  if (!(tag = gst_riff_peek_tag (riff, NULL)))
    return FALSE;
  if (tag != GST_RIFF_TAG_strf) {
913
    GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
914
        ("Invalid AVI header (no strf as second tag)"));
915 916 917 918
    goto skip_stream;
  }
  switch (strh->type) {
    case GST_RIFF_FCC_vids:
919
      if (!gst_riff_read_strf_vids_with_data (riff, &strf.vids, &extradata))
920
        return FALSE;
921 922
      break;
    case GST_RIFF_FCC_auds:
923
      if (!gst_riff_read_strf_auds_with_data (riff, &strf.auds, &extradata))
924
        return FALSE;
925 926 927
      break;
    case GST_RIFF_FCC_iavs:
      if (!gst_riff_read_strf_iavs (riff, &strf.iavs))
928
        return FALSE;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
929