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

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

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

27
#include "gst/riff/riff-media.h"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
28
#include "gstavidemux.h"
29
#include "avi-ids.h"
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
36
37
38
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-msvideo")
    );
39

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
44
45
static void gst_avi_demux_reset (GstAviDemux * avi);
static void gst_avi_demux_loop (GstElement * element);
46

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
50
51
52
53
54
55
56
57
58
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
59

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

62
static GstRiffReadClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
63

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

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

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
87
88
89
  return avi_demux_type;
}

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

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

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

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

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

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

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

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

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

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

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

149
150
  gst_element_set_loop_function (GST_ELEMENT (avi), gst_avi_demux_loop);
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
151

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

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

161
162
163
  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);
164
    gst_caps_free (avi->stream[i].caps);
165
166
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
167

168
169
170
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
171

172
173
  avi->state = GST_AVI_DEMUX_START;
  avi->level_up = 0;
174

175
176
177
  if (avi->index_entries) {
    g_free (avi->index_entries);
    avi->index_entries = NULL;
178
  }
179
  avi->index_size = 0;
180

181
182
  avi->num_frames = 0;
  avi->us_per_frame = 0;
183

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
184
  avi->seek_offset = (guint64) - 1;
185
186
}

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

194
195
  for (i = start; i < avi->index_size; i++) {
    entry = &avi->index_entries[i];
196

197
    if (entry->stream_nr == stream_nr && (entry->flags & flags) == flags) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
198
      break;
199
200
    }
  }
201

202
203
  return entry;
}
204

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

212
213
214
215
216
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
217

218
    i = entry->index_nr;
219

220
221
222
223
    if (entry->ts <= time) {
      last_entry = entry;
    }
  } while (entry->ts <= time);
224

225
226
  return last_entry;
}
227

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

235
236
237
238
239
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
240

241
    i = entry->index_nr;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
242

243
244
245
246
    if (entry->bytes_before <= byte) {
      last_entry = entry;
    }
  } while (entry->bytes_before <= byte);
247

248
  return last_entry;
249
250
}

251
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
252
253
gst_avi_demux_index_entry_for_frame (GstAviDemux * avi,
    gint stream_nr, guint32 frame, guint32 flags)
254
{
255
256
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
257

258
259
260
261
262
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
263

264
    i = entry->index_nr;
265

266
267
268
269
    if (entry->frames_before <= frame) {
      last_entry = entry;
    }
  } while (entry->frames_before <= frame);
270

271
  return last_entry;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
272
273
}

274
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
275
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
276
{
277
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
278

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

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

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

  /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); */
303
  avi_stream_context *stream = gst_pad_get_element_private (pad);
304

305
  if (stream->strh->type == GST_RIFF_FCC_vids &&
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
306
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
307
    return FALSE;
308

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

350
  return res;
351
352
}

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

362
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
363
364
}

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

  /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); */
373
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
374
375

  switch (type) {
Wim Taymans's avatar
Wim Taymans committed
376
    case GST_QUERY_TOTAL:
Wim Taymans's avatar
Wim Taymans committed
377
      switch (*format) {
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
        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
399
400
      }
      break;
Wim Taymans's avatar
Wim Taymans committed
401
    case GST_QUERY_POSITION:
Wim Taymans's avatar
Wim Taymans committed
402
      switch (*format) {
403
        case GST_FORMAT_TIME:
404
          if (stream->strh->type == GST_RIFF_FCC_auds) {
405
            if (stream->strh->samplesize == 1 && stream->blockalign != 0) {
406
407
408
              *value = stream->current_byte * GST_SECOND /
                  (stream->blockalign * stream->strh->rate);
            } else if (stream->bitrate != 0) {
409
410
              *value = ((gfloat) stream->current_byte) * GST_SECOND /
                  stream->bitrate;
411
412
413
414
415
416
            } else if (stream->total_frames != 0) {
              /* calculate timestamps based on video size */
              guint64 len = demux->us_per_frame * demux->num_frames *
                  GST_USECOND;

              *value = len * stream->current_frame / stream->total_frames;
417
            } else {
418
              *value = 0;
419
            }
420
          } else {
421
422
423
424
425
426
427
            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;
            }
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
          }
          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
443
444
445
446
447
448
449
450
451
452
      }
      break;
    default:
      res = FALSE;
      break;
  }

  return res;
}

453
static GstCaps *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
454
gst_avi_demux_src_getcaps (GstPad * pad)
455
456
457
458
459
460
{
  avi_stream_context *stream = gst_pad_get_element_private (pad);

  return gst_caps_copy (stream->caps);
}

Wim Taymans's avatar
Wim Taymans committed
461
static gint32
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
462
gst_avi_demux_sync_streams (GstAviDemux * avi, guint64 time)
Wim Taymans's avatar
Wim Taymans committed
463
464
{
  gint i;
Wim Taymans's avatar
Wim Taymans committed
465
  guint32 min_index = G_MAXUINT;
Wim Taymans's avatar
Wim Taymans committed
466
467
468
  avi_stream_context *stream;
  gst_avi_index_entry *entry;

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

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

474
    entry = gst_avi_demux_index_entry_for_time (avi, stream->num, time,
475
        GST_RIFF_IF_KEYFRAME);
Wim Taymans's avatar
Wim Taymans committed
476
477
478
479
    if (entry) {
      min_index = MIN (entry->index_nr, min_index);
    }
  }
480
  GST_DEBUG ("first index at %d", min_index);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
481

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

487
    stream = &avi->stream[i];
Wim Taymans's avatar
Wim Taymans committed
488
489

    /* next entry */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
490
    next_entry = gst_avi_demux_index_next (avi, stream->num, min_index, 0);
Wim Taymans's avatar
Wim Taymans committed
491
    /* next entry with keyframe */
492
    entry = gst_avi_demux_index_next (avi, stream->num, min_index,
493
        GST_RIFF_IF_KEYFRAME);
Wim Taymans's avatar
Wim Taymans committed
494
495
496
497
498

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

499
    GST_DEBUG ("%d skip %d", stream->num, stream->skip);
Wim Taymans's avatar
Wim Taymans committed
500
  }
501

502
  GST_DEBUG ("final index at %d", min_index);
Wim Taymans's avatar
Wim Taymans committed
503
504
505
506

  return min_index;
}

507
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
508
gst_avi_demux_send_event (GstElement * element, GstEvent * event)
509
{
Wim Taymans's avatar
Wim Taymans committed
510
  const GList *pads;
511
512
513

  pads = gst_element_get_pad_list (element);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
514
  while (pads) {
515
516
517
    GstPad *pad = GST_PAD (pads->data);

    if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
Wim Taymans's avatar
Wim Taymans committed
518
519
520
521
      /* 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)) {
522
        gst_event_unref (event);
523

524
        return TRUE;
Wim Taymans's avatar
Wim Taymans committed
525
      }
526
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
527

528
529
    pads = g_list_next (pads);
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
530

Wim Taymans's avatar
Wim Taymans committed
531
  gst_event_unref (event);
532

533
534
535
  return FALSE;
}

536
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
537
gst_avi_demux_get_event_mask (GstPad * pad)
538
539
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
540
541
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
542
543
544
545
  };

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

Wim Taymans's avatar
Wim Taymans committed
547
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
548
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
549
550
{
  gboolean res = TRUE;
551
  GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Wim Taymans's avatar
Wim Taymans committed
552
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
553

Wim Taymans's avatar
Wim Taymans committed
554
555
556
557
  stream = gst_pad_get_element_private (pad);

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

Wim Taymans's avatar
Wim Taymans committed
561
      switch (GST_EVENT_SEEK_FORMAT (event)) {
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
        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
609
610
611
612
613
614
      }
      break;
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
615
616
617
618

done:
  gst_event_unref (event);

Wim Taymans's avatar
Wim Taymans committed
619
620
621
  return res;
}

622
623
624
625
626
/*
 * "Open" a RIFF file.
 */

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
627
gst_avi_demux_stream_init (GstAviDemux * avi)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
628
{
629
630
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
631

632
633
634
  if (!gst_riff_read_header (riff, &doctype))
    return FALSE;
  if (doctype != GST_RIFF_RIFF_AVI) {
635
    GST_ELEMENT_ERROR (avi, STREAM, WRONG_TYPE, (NULL), (NULL));
636
637
    return FALSE;
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
638

639
640
641
642
643
644
645
646
  return TRUE;
}

/*
 * Read 'avih' header.
 */

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
647
648
gst_avi_demux_stream_avih (GstAviDemux * avi,
    guint32 * flags, guint32 * streams)
649
650
651
652
{
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 tag;
  GstBuffer *buf;
653
  gst_riff_avih avih, *_avih;
654
655
656
657
658
659
660
661
662
663
664

  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)",
665
        GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih));
666
667
668
669
    gst_buffer_unref (buf);
    return FALSE;
  }

670
  _avih = (gst_riff_avih *) GST_BUFFER_DATA (buf);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
671
672
673
674
675
  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);
676
  avih.init_frames = GUINT32_FROM_LE (_avih->init_frames);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
677
678
679
680
681
682
683
684
  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);
685
686
687

  /* debug stuff */
  GST_INFO ("avih tag found:");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
688
689
690
  GST_INFO (" us_frame    %u", avih.us_frame);
  GST_INFO (" max_bps     %u", avih.max_bps);
  GST_INFO (" pad_gran    %u", avih.pad_gran);
691
  GST_INFO (" flags       0x%08x", avih.flags);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
692
693
694
695
696
697
698
699
700
701
  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);
702
703
704
705
706

  avi->num_frames = avih.tot_frames;
  avi->us_per_frame = avih.us_frame;
  *streams = avih.streams;
  *flags = avih.flags;
707
708
709
710
711
712
713
714
715
716
717

  gst_buffer_unref (buf);

  return TRUE;
}

/*
 * Add a stream.
 */

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
718
gst_avi_demux_add_stream (GstAviDemux * avi)
719
720
721
722
723
{
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (avi);
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 tag;
  gst_riff_strh *strh;
724
  GstBuffer *extradata = NULL, *initdata = NULL;
725
726
727
728
729
  gchar *name = NULL, *padname = NULL;
  GstCaps *caps = NULL;
  GstPadTemplate *templ = NULL;
  GstPad *pad;
  avi_stream_context *stream;
730
  gint blockalign = 0, bitrate = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
731
732
  union
  {
733
734
735
    gst_riff_strf_vids *vids;
    gst_riff_strf_auds *auds;
    gst_riff_strf_iavs *iavs;
736
737
  }
  strf;
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752

  /* 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) {
753
    GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
754
        ("Invalid AVI header (no strf as second tag)"));
755
756
757
758
    goto skip_stream;
  }
  switch (strh->type) {
    case GST_RIFF_FCC_vids:
759
      if (!gst_riff_read_strf_vids_with_data (riff, &strf.vids, &extradata))
760
        return FALSE;
761
762
      break;
    case GST_RIFF_FCC_auds:
763
      if (!gst_riff_read_strf_auds_with_data (riff, &strf.auds, &extradata))
764
        return FALSE;
765
766
767
      break;
    case GST_RIFF_FCC_iavs:
      if (!gst_riff_read_strf_iavs (riff, &strf.iavs))
768
        return FALSE;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
769
      break;
770
771
    default:
      g_warning ("Unknown stream type " GST_FOURCC_FORMAT,
772
          GST_FOURCC_ARGS (strh->type));
773
774
      goto skip_stream;
  }
Wim Taymans's avatar
Wim Taymans committed
775

776
777
778
779
780
781
782
783
  /* 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
784

785
    switch (tag) {
786
787
788
789
790
791
792
      case GST_RIFF_TAG_strd:
        if (initdata)
          gst_buffer_unref (initdata);
        if (!gst_riff_read_data (riff, &tag, &initdata))
          return FALSE;
        break;

793
      case GST_RIFF_TAG_strn:
794
795
796
797
798
        if (name)
          g_free (name);
        if (!gst_riff_read_ascii (riff, &tag, &name))
          return FALSE;
        break;
Wim Taymans's avatar
Wim Taymans committed
799

800
      default:
801
802
803
        GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " in AVI header",
            GST_FOURCC_ARGS (tag));
        /* fall-through */
804
805

      case GST_RIFF_TAG_JUNK:
806
807
808
        if (!gst_riff_read_skip (riff))
          return FALSE;
        break;
809
    }
Wim Taymans's avatar
Wim Taymans committed
810

811
812
    if (avi->level_up) {
      avi->level_up--;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
813
      break;
Wim Taymans's avatar
Wim Taymans committed
814
    }
815
816
817
818
819
  }

  /* create stream name + pad */
  switch (strh->type) {
    case GST_RIFF_FCC_vids:
820
821
822
    {
      char *codec_name = NULL;
      GstTagList *list = gst_tag_list_new ();
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
823

824
825
      padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
      templ = gst_element_class_get_pad_template (klass, "video_%02d");
826
827
      caps = gst_riff_create_video_caps_with_data (strf.vids->compression,
          strh, strf.vids, extradata, initdata, &codec_name);
828
      gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_VIDEO_CODEC,
829
          codec_name, NULL);
830
831
      gst_element_found_tags (GST_ELEMENT (avi), list);
      gst_tag_list_free (list);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
832
      if (codec_name)
833
        g_free (codec_name);
834
835
836
      g_free (strf.vids);
      avi->num_v_streams++;
      break;
837
    }
838
    case GST_RIFF_FCC_auds:
839
840
841
    {
      char *codec_name = NULL;
      GstTagList *list = gst_tag_list_new ();
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
842

843
844
      padname = g_strdup_printf ("audio_%02d", avi->num_a_streams);
      templ = gst_element_class_get_pad_template (klass, "audio_%02d");
845
846
847
      caps =
          gst_riff_create_audio_caps_with_data (strf.auds->format, strh,
          strf.auds, extradata, initdata, &codec_name);
848
      gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_AUDIO_CODEC,
849
          codec_name, NULL);
850
851
      gst_element_found_tags (GST_ELEMENT (avi), list);
      gst_tag_list_free (list);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
852
      if (codec_name)
853
        g_free (codec_name);
854
855
      blockalign = strf.auds->blockalign;
      bitrate = strf.auds->av_bps;
856
857
858
      g_free (strf.auds);
      avi->num_a_streams++;
      break;
859
    }
860
    case GST_RIFF_FCC_iavs:
861
862
863
    {
      char *codec_name = NULL;
      GstTagList *list = gst_tag_list_new ();
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
864

865
866
      padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
      templ = gst_element_class_get_pad_template (klass, "video_%02d");
867
      caps = gst_riff_create_iavs_caps (strh->fcc_handler, strh, strf.iavs,
868
          &codec_name);
869
      gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_VIDEO_CODEC,
870
          codec_name, NULL);
871
872
      gst_element_found_tags (GST_ELEMENT (avi), list);
      gst_tag_list_free (list);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
873
      if (codec_name)
874
        g_free (codec_name);
875
876
877
      g_free (strf.iavs);
      avi->num_v_streams++;
      break;
878
    }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
879
    default:
880
881
882
883
      g_assert (0);
  }

  /* set proper settings and add it */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
884
  pad = gst_pad_new_from_template (templ, padname);
885
886
887
888
889
890
891
892
  g_free (padname);

  gst_pad_set_formats_function (pad, gst_avi_demux_get_src_formats);
  gst_pad_set_event_mask_function (pad, gst_avi_demux_get_event_mask);
  gst_pad_set_event_function (pad, gst_avi_demux_handle_src_event);
  gst_pad_set_query_type_function (pad, gst_avi_demux_get_src_query_types);
  gst_pad_set_query_function (pad, gst_avi_demux_handle_src_query);
  gst_pad_set_convert_function (pad, gst_avi_demux_src_convert);
893
  gst_pad_set_getcaps_function (pad, gst_avi_demux_src_getcaps);
894
895

  stream = &avi->stream[avi->num_streams];
896
  stream->caps = caps ? caps : gst_caps_new_empty ();
897
898
899
900
901
902
903
904
905
906
  stream->pad = pad;
  stream->strh = strh;
  stream->num = avi->num_streams;
  stream->delay = 0LL;
  stream->total_bytes = 0LL;
  stream->total_frames = 0;
  stream->current_frame = 0;
  stream->current_byte = 0;
  stream->current_entry = -1;
  stream->skip = 0;
907
908
  stream->blockalign = blockalign;
  stream->bitrate = bitrate;
909
910
911
  gst_pad_set_element_private (pad, stream);
  avi->num_streams++;

912
  /* auto-negotiates */
Ronald S. Bultje's avatar