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

21 22 23
#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

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
34
/* AviDemux signals and args */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
35 36
enum
{
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
37 38 39 40
  /* FILL ME */
  LAST_SIGNAL
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
41 42
enum
{
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
43
  ARG_0,
44 45
  ARG_STREAMINFO
      /* FILL ME */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
46 47
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
48 49 50 51 52
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-msvideo")
    );
53

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
54 55 56
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);
57

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
58 59
static void gst_avi_demux_reset (GstAviDemux * avi);
static void gst_avi_demux_loop (GstElement * element);
60

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
61 62
static gboolean gst_avi_demux_send_event (GstElement * element,
    GstEvent * event);
63

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
64 65 66 67 68 69 70 71 72
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
73

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
76 77
static void gst_avi_demux_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
78

79
static GstRiffReadClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
80

81
/*static guint gst_avi_demux_signals[LAST_SIGNAL] = { 0 }; */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
82 83

GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
84
gst_avi_demux_get_type (void)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
85 86 87 88 89
{
  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
90
      sizeof (GstAviDemuxClass),
91
      (GBaseInitFunc) gst_avi_demux_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
92
      NULL,
93
      (GClassInitFunc) gst_avi_demux_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
94 95
      NULL,
      NULL,
96
      sizeof (GstAviDemux),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
97
      0,
98
      (GInstanceInitFunc) gst_avi_demux_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
99
    };
100 101

    avi_demux_type =
102 103
        g_type_register_static (GST_TYPE_RIFF_READ,
        "GstAviDemux", &avi_demux_info, 0);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
104
  }
105

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
106 107 108
  return avi_demux_type;
}

109
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
110
gst_avi_demux_base_init (GstAviDemuxClass * klass)
111
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
112 113 114 115 116 117 118
  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>");
119 120 121
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstPadTemplate *videosrctempl, *audiosrctempl;
  GstCaps *audcaps, *vidcaps;
122

123
  audcaps = gst_riff_create_audio_template_caps ();
124
  audiosrctempl = gst_pad_template_new ("audio_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
125
      GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
126

David Schleef's avatar
David Schleef committed
127 128
  vidcaps = gst_riff_create_video_template_caps ();
  gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
129
  videosrctempl = gst_pad_template_new ("video_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
130
      GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
131

132 133 134
  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
135
      gst_static_pad_template_get (&sink_templ));
136 137 138
  gst_element_class_set_details (element_class, &gst_avi_demux_details);
}

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
139
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
140
gst_avi_demux_class_init (GstAviDemuxClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
141 142 143 144
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
145 146
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
147

148
  g_object_class_install_property (gobject_class, ARG_STREAMINFO,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
149
      g_param_spec_boxed ("streaminfo", "Streaminfo", "Streaminfo",
150
          GST_TYPE_CAPS, G_PARAM_READABLE));
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
151

152
  GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
153
      0, "Demuxer for AVI streams");
154 155

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

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
157
  gobject_class->get_property = gst_avi_demux_get_property;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
158

Wim Taymans's avatar
Wim Taymans committed
159
  gstelement_class->change_state = gst_avi_demux_change_state;
160
  gstelement_class->send_event = gst_avi_demux_send_event;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
161 162
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
163 164
static void
gst_avi_demux_init (GstAviDemux * avi)
165
{
166
  GST_FLAG_SET (avi, GST_ELEMENT_EVENT_AWARE);
167

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
168 169 170
  avi->sinkpad =
      gst_pad_new_from_template (gst_static_pad_template_get (&sink_templ),
      "sink");
171 172
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
  GST_RIFF_READ (avi)->sinkpad = avi->sinkpad;
Wim Taymans's avatar
Wim Taymans committed
173

174 175
  gst_element_set_loop_function (GST_ELEMENT (avi), gst_avi_demux_loop);
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
176

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

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

187 188 189
  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);
190
    gst_caps_free (avi->stream[i].caps);
191 192
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
193

194 195 196
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
197

198 199
  avi->state = GST_AVI_DEMUX_START;
  avi->level_up = 0;
200

201 202 203
  if (avi->index_entries) {
    g_free (avi->index_entries);
    avi->index_entries = NULL;
204
  }
205
  avi->index_size = 0;
206

207 208
  avi->num_frames = 0;
  avi->us_per_frame = 0;
209

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
210
  avi->seek_offset = (guint64) - 1;
211

212
  gst_caps_replace (&avi->streaminfo, NULL);
213 214 215
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
216
gst_avi_demux_streaminfo (GstAviDemux * avi)
217 218 219
{
  /* compression formats are added later - a bit hacky */

David Schleef's avatar
David Schleef committed
220 221
  gst_caps_replace (&avi->streaminfo,
      gst_caps_new_simple ("application/x-gst-streaminfo", NULL));
222

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
223
  /*g_object_notify(G_OBJECT(avi), "streaminfo"); */
224 225
}

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

233 234
  for (i = start; i < avi->index_size; i++) {
    entry = &avi->index_entries[i];
235

236
    if (entry->stream_nr == stream_nr && (entry->flags & flags) == flags) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
237
      break;
238 239
    }
  }
240

241 242
  return entry;
}
243

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

251 252 253 254 255
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
256

257
    i = entry->index_nr;
258

259 260 261 262
    if (entry->ts <= time) {
      last_entry = entry;
    }
  } while (entry->ts <= time);
263

264 265
  return last_entry;
}
266

267
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
268 269
gst_avi_demux_index_entry_for_byte (GstAviDemux * avi,
    gint stream_nr, guint64 byte, guint32 flags)
270 271 272
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
273

274 275 276 277 278
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
279

280
    i = entry->index_nr;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
281

282 283 284 285
    if (entry->bytes_before <= byte) {
      last_entry = entry;
    }
  } while (entry->bytes_before <= byte);
286

287
  return last_entry;
288 289
}

290
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
291 292
gst_avi_demux_index_entry_for_frame (GstAviDemux * avi,
    gint stream_nr, guint32 frame, guint32 flags)
293
{
294 295
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
296

297 298 299 300 301
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
302

303
    i = entry->index_nr;
304

305 306 307 308
    if (entry->frames_before <= frame) {
      last_entry = entry;
    }
  } while (entry->frames_before <= frame);
309

310
  return last_entry;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
311 312
}

313
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
314
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
315
{
316
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
317

318 319 320 321 322 323 324 325 326 327 328
  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
329

330
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
331
      src_a_formats : src_v_formats);
332
}
333

334
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
335 336 337
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
338 339
{
  gboolean res = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
340 341

  /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); */
342
  avi_stream_context *stream = gst_pad_get_element_private (pad);
343

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
344 345
  if (stream->strh->type != GST_RIFF_FCC_auds &&
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
346
    return FALSE;
347

348 349 350
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
351 352 353 354 355 356 357 358 359 360 361
        case GST_FORMAT_BYTES:
          *dest_value = src_value * stream->strh->rate /
              (stream->strh->scale * GST_SECOND);
          break;
        case GST_FORMAT_DEFAULT:
          *dest_value = src_value * stream->strh->rate /
              (stream->strh->scale * GST_SECOND);
          break;
        default:
          res = FALSE;
          break;
362
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
363
      break;
364 365
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
366 367 368 369 370 371
        case GST_FORMAT_TIME:
          *dest_value = ((gfloat) src_value) * GST_SECOND / stream->strh->rate;
          break;
        default:
          res = FALSE;
          break;
372 373
      }
      break;
374 375
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
376 377 378 379 380 381 382
        case GST_FORMAT_TIME:
          *dest_value = ((((gfloat) src_value) * stream->strh->scale) /
              stream->strh->rate) * GST_SECOND;
          break;
        default:
          res = FALSE;
          break;
383
      }
Wim Taymans's avatar
Wim Taymans committed
384 385
      break;
    default:
386
      res = FALSE;
387
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
388

389
  return res;
390 391
}

392
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
393
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
394
{
395 396 397 398 399
  static const GstQueryType src_types[] = {
    GST_QUERY_TOTAL,
    GST_QUERY_POSITION,
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
400

401
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
402 403
}

404
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
405 406
gst_avi_demux_handle_src_query (GstPad * pad,
    GstQueryType type, GstFormat * format, gint64 * value)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
407
{
408
  gboolean res = TRUE;
409
  GstAviDemux *demux = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
410 411

  /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); */
412
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
413 414

  switch (type) {
Wim Taymans's avatar
Wim Taymans committed
415
    case GST_QUERY_TOTAL:
Wim Taymans's avatar
Wim Taymans committed
416
      switch (*format) {
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
        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:
          if (stream->strh->type == GST_RIFF_FCC_auds)
            *value = stream->strh->length * stream->strh->samplesize;
          else if (stream->strh->type == GST_RIFF_FCC_vids)
            *value = stream->strh->length;
          else
            res = FALSE;
          break;
        default:
          res = FALSE;
          break;
Wim Taymans's avatar
Wim Taymans committed
438 439
      }
      break;
Wim Taymans's avatar
Wim Taymans committed
440
    case GST_QUERY_POSITION:
Wim Taymans's avatar
Wim Taymans committed
441
      switch (*format) {
442
        case GST_FORMAT_TIME:
443
          if (stream->strh->type == GST_RIFF_FCC_auds) {
444
            if (stream->strh->samplesize == 1 && stream->blockalign != 0) {
445 446 447 448 449 450
              *value = stream->current_byte * GST_SECOND /
                  (stream->blockalign * stream->strh->rate);
            } else if (stream->strh->rate != 0) {
              *value = (gfloat) stream->current_frame * stream->strh->scale *
                  GST_SECOND / stream->strh->rate;
            } else if (stream->bitrate != 0) {
451 452 453
              *value = ((gfloat) stream->current_byte) * GST_SECOND /
                  stream->bitrate;
            } else {
454
              *value = 0;
455
            }
456
          } else {
457 458 459 460 461 462 463
            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;
            }
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
          }
          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)
            *value = stream->current_byte * stream->strh->samplesize;
          else
            *value = stream->current_frame;
          break;
        default:
          res = FALSE;
          break;
Wim Taymans's avatar
Wim Taymans committed
479 480 481 482 483 484 485 486 487 488
      }
      break;
    default:
      res = FALSE;
      break;
  }

  return res;
}

489
static GstCaps *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
490
gst_avi_demux_src_getcaps (GstPad * pad)
491 492 493 494 495 496
{
  avi_stream_context *stream = gst_pad_get_element_private (pad);

  return gst_caps_copy (stream->caps);
}

Wim Taymans's avatar
Wim Taymans committed
497
static gint32
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
498
gst_avi_demux_sync_streams (GstAviDemux * avi, guint64 time)
Wim Taymans's avatar
Wim Taymans committed
499 500
{
  gint i;
Wim Taymans's avatar
Wim Taymans committed
501
  guint32 min_index = G_MAXUINT;
Wim Taymans's avatar
Wim Taymans committed
502 503 504
  avi_stream_context *stream;
  gst_avi_index_entry *entry;

505 506
  for (i = 0; i < avi->num_streams; i++) {
    stream = &avi->stream[i];
Wim Taymans's avatar
Wim Taymans committed
507

508
    GST_DEBUG ("finding %d for time %" G_GINT64_FORMAT, i, time);
Wim Taymans's avatar
Wim Taymans committed
509

510
    entry = gst_avi_demux_index_entry_for_time (avi, stream->num, time,
511
        GST_RIFF_IF_KEYFRAME);
Wim Taymans's avatar
Wim Taymans committed
512 513 514 515
    if (entry) {
      min_index = MIN (entry->index_nr, min_index);
    }
  }
516
  GST_DEBUG ("first index at %d", min_index);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
517

Wim Taymans's avatar
Wim Taymans committed
518 519
  /* now we know the entry we need to sync on. calculate number of frames to
   * skip fro there on and the stream stats */
520
  for (i = 0; i < avi->num_streams; i++) {
Wim Taymans's avatar
Wim Taymans committed
521
    gst_avi_index_entry *next_entry;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
522

523
    stream = &avi->stream[i];
Wim Taymans's avatar
Wim Taymans committed
524 525

    /* next entry */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
526
    next_entry = gst_avi_demux_index_next (avi, stream->num, min_index, 0);
Wim Taymans's avatar
Wim Taymans committed
527
    /* next entry with keyframe */
528
    entry = gst_avi_demux_index_next (avi, stream->num, min_index,
529
        GST_RIFF_IF_KEYFRAME);
Wim Taymans's avatar
Wim Taymans committed
530 531 532 533 534

    stream->current_byte = next_entry->bytes_before;
    stream->current_frame = next_entry->frames_before;
    stream->skip = entry->frames_before - next_entry->frames_before;

535
    GST_DEBUG ("%d skip %d", stream->num, stream->skip);
Wim Taymans's avatar
Wim Taymans committed
536
  }
537

538
  GST_DEBUG ("final index at %d", min_index);
Wim Taymans's avatar
Wim Taymans committed
539 540 541 542

  return min_index;
}

543
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
544
gst_avi_demux_send_event (GstElement * element, GstEvent * event)
545
{
Wim Taymans's avatar
Wim Taymans committed
546
  const GList *pads;
547 548 549

  pads = gst_element_get_pad_list (element);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
550
  while (pads) {
551 552 553
    GstPad *pad = GST_PAD (pads->data);

    if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
Wim Taymans's avatar
Wim Taymans committed
554 555 556 557
      /* 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)) {
558
        gst_event_unref (event);
559

560
        return TRUE;
Wim Taymans's avatar
Wim Taymans committed
561
      }
562
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
563

564 565
    pads = g_list_next (pads);
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
566

Wim Taymans's avatar
Wim Taymans committed
567
  gst_event_unref (event);
568

569 570 571
  return FALSE;
}

572
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
573
gst_avi_demux_get_event_mask (GstPad * pad)
574 575
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
576 577
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
578 579 580 581
  };

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

Wim Taymans's avatar
Wim Taymans committed
583
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
584
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
585 586
{
  gboolean res = TRUE;
587
  GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Wim Taymans's avatar
Wim Taymans committed
588
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
589

Wim Taymans's avatar
Wim Taymans committed
590 591 592 593
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
594
      GST_DEBUG ("seek format %d, %08x", GST_EVENT_SEEK_FORMAT (event),
595
          stream->strh->type);
596

Wim Taymans's avatar
Wim Taymans committed
597
      switch (GST_EVENT_SEEK_FORMAT (event)) {
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
        case GST_FORMAT_BYTES:
        case GST_FORMAT_DEFAULT:
        case GST_FORMAT_TIME:{
          gst_avi_index_entry *seek_entry, *entry = NULL;
          gint64 desired_offset = GST_EVENT_SEEK_OFFSET (event);
          guint32 flags;
          guint64 min_index;

          /* no seek on audio yet */
          if (stream->strh->type == GST_RIFF_FCC_auds) {
            res = FALSE;
            goto done;
          }
          GST_DEBUG ("seeking to %" G_GINT64_FORMAT, desired_offset);

          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) {
            min_index = gst_avi_demux_sync_streams (avi, entry->ts);
            seek_entry = &avi->index_entries[min_index];

            avi->seek_offset = seek_entry->offset + avi->index_offset;
            avi->last_seek = entry->ts;
          } else {
            GST_DEBUG ("no index entry found for format=%d value=%"
                G_GINT64_FORMAT, GST_EVENT_SEEK_FORMAT (event), desired_offset);
            res = FALSE;
          }
          break;
        }
        default:
          res = FALSE;
          break;
Wim Taymans's avatar
Wim Taymans committed
645 646 647 648 649 650
      }
      break;
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
651 652 653 654

done:
  gst_event_unref (event);

Wim Taymans's avatar
Wim Taymans committed
655 656 657
  return res;
}

658 659 660 661 662
/*
 * "Open" a RIFF file.
 */

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
663
gst_avi_demux_stream_init (GstAviDemux * avi)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
664
{
665 666
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
667

668 669 670
  if (!gst_riff_read_header (riff, &doctype))
    return FALSE;
  if (doctype != GST_RIFF_RIFF_AVI) {
671
    GST_ELEMENT_ERROR (avi, STREAM, WRONG_TYPE, (NULL), (NULL));
672 673
    return FALSE;
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
674

675 676 677 678 679 680 681 682
  return TRUE;
}

/*
 * Read 'avih' header.
 */

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
683 684
gst_avi_demux_stream_avih (GstAviDemux * avi,
    guint32 * flags, guint32 * streams)
685 686 687 688
{
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 tag;
  GstBuffer *buf;
689
  gst_riff_avih avih, *_avih;
690 691 692 693 694 695 696 697 698 699 700

  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)",
701
        GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih));
702 703 704 705
    gst_buffer_unref (buf);
    return FALSE;
  }

706
  _avih = (gst_riff_avih *) GST_BUFFER_DATA (buf);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
707 708 709 710 711
  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);
712
  avih.init_frames = GUINT32_FROM_LE (_avih->init_frames);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
713 714 715 716 717 718 719 720
  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);
721 722 723

  /* debug stuff */
  GST_INFO ("avih tag found:");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
724 725 726
  GST_INFO (" us_frame    %u", avih.us_frame);
  GST_INFO (" max_bps     %u", avih.max_bps);
  GST_INFO (" pad_gran    %u", avih.pad_gran);
727
  GST_INFO (" flags       0x%08x", avih.flags);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
728 729 730 731 732 733 734 735 736 737
  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);
738 739 740 741 742

  avi->num_frames = avih.tot_frames;
  avi->us_per_frame = avih.us_frame;
  *streams = avih.streams;
  *flags = avih.flags;
743 744 745 746 747 748 749 750 751 752 753

  gst_buffer_unref (buf);

  return TRUE;
}

/*
 * Add a stream.
 */

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
754
gst_avi_demux_add_stream (GstAviDemux * avi)
755 756 757 758 759
{
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (avi);
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 tag;
  gst_riff_strh *strh;
760
  GstBuffer *extradata = NULL, *initdata = NULL;
761 762 763 764 765
  gchar *name = NULL, *padname = NULL;
  GstCaps *caps = NULL;
  GstPadTemplate *templ = NULL;
  GstPad *pad;
  avi_stream_context *stream;
766
  gint blockalign = 0, bitrate = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
767 768
  union
  {
769 770 771
    gst_riff_strf_vids *vids;
    gst_riff_strf_auds *auds;
    gst_riff_strf_iavs *iavs;
772 773
  }
  strf;
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788

  /* 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) {
789
    GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
790
        ("Invalid AVI header (no strf as second tag)"));
791 792 793 794
    goto skip_stream;
  }
  switch (strh->type) {
    case GST_RIFF_FCC_vids:
795
      if (!gst_riff_read_strf_vids_with_data (riff, &strf.vids, &extradata))
796
        return FALSE;
797 798
      break;
    case GST_RIFF_FCC_auds:
799
      if (!gst_riff_read_strf_auds_with_data (riff, &strf.auds, &extradata))
800
        return FALSE;
801 802 803
      break;
    case GST_RIFF_FCC_iavs:
      if (!gst_riff_read_strf_iavs (riff, &strf.iavs))
804
        return FALSE;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
805
      break;
806 807
    default:
      g_warning ("Unknown stream type " GST_FOURCC_FORMAT,
808
          GST_FOURCC_ARGS (strh->type));
809 810
      goto skip_stream;
  }
Wim Taymans's avatar
Wim Taymans committed
811

812 813 814 815 816 817 818 819
  /* read other things */
  while (TRUE) {
    if (!(tag = gst_riff_peek_tag (riff, &avi->level_up)))
      return FALSE;
    else if (avi->level_up) {
      avi->level_up--;
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
820

821
    switch (tag) {
822 823 824 825 826 827 828
      case GST_RIFF_TAG_strd:
        if (initdata)
          gst_buffer_unref (initdata);
        if (!gst_riff_read_data (riff, &tag, &initdata))
          return FALSE;
        break;

829
      case GST_RIFF_TAG_strn:
830 831 832 833 834
        if (name)
          g_free (name);
        if (!gst_riff_read_ascii (riff, &tag, &name))
          return FALSE;
        break;
Wim Taymans's avatar
Wim Taymans committed
835

836
      default:
837 838 839
        GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " in AVI header",
            GST_FOURCC_ARGS (tag));
        /* fall-through */
840 841

      case GST_RIFF_TAG_JUNK:
842 843 844
        if (!gst_riff_read_skip (riff))
          return FALSE;
        break;
845
    }