gstasfdemux.c 150 KB
Newer Older
1
/* GStreamer ASF/WMV/WMA demuxer
2
 * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
3
 * Copyright (C) 2006-2009 Tim-Philipp Müller <tim centricular net>
4
5
6
7
8
9
10
11
12
13
14
15
16
 *
 * 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
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
17
18
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
19
20
 */

21
22
23
24
25
26
27
28
29
/* TODO:
 *
 * - _loop():
 *   stop if at end of segment if != end of file, ie. demux->segment.stop
 *
 * - fix packet parsing:
 *   there's something wrong with timestamps for packets with keyframes,
 *   and durations too.
 */
30

31
32
33
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
34

35
#include <gst/gstutils.h>
36
#include <gst/base/gstbytereader.h>
Matej's avatar
Matej committed
37
#include <gst/base/gsttypefindhelper.h>
38
#include <gst/riff/riff-media.h>
39
#include <gst/tag/tag.h>
Jan Schmidt's avatar
Jan Schmidt committed
40
#include <gst/gst-i18n-plugin.h>
41
#include <gst/video/video.h>
42
#include <stdio.h>
43
#include <stdlib.h>
44
#include <string.h>
45

46
#include "gstasfdemux.h"
47
#include "asfheaders.h"
48
#include "asfpacket.h"
49

David Schleef's avatar
David Schleef committed
50
static GstStaticPadTemplate gst_asf_demux_sink_template =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
51
52
53
54
55
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-ms-asf")
    );
56

57
static GstStaticPadTemplate audio_src_template =
Wim Taymans's avatar
Wim Taymans committed
58
GST_STATIC_PAD_TEMPLATE ("audio_%u",
59
60
61
62
63
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);

static GstStaticPadTemplate video_src_template =
Wim Taymans's avatar
Wim Taymans committed
64
GST_STATIC_PAD_TEMPLATE ("video_%u",
65
66
67
68
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);

69
70
/* size of an ASF object header, ie. GUID (16 bytes) + object size (8 bytes) */
#define ASF_OBJECT_HEADER_SIZE  (16+8)
71

72
/* FIXME: get rid of this */
73
74
75
76
77
78
79
/* abuse this GstFlowReturn enum for internal usage */
#define ASF_FLOW_NEED_MORE_DATA  99

#define gst_asf_get_flow_name(flow)    \
  (flow == ASF_FLOW_NEED_MORE_DATA) ?  \
  "need-more-data" : gst_flow_get_name (flow)

80
GST_DEBUG_CATEGORY (asfdemux_dbg);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
81

82
static void gst_asf_demux_finalize (GObject * object);
83
84
static GstStateChangeReturn gst_asf_demux_change_state (GstElement * element,
    GstStateChange transition);
85
86
87
88
static gboolean gst_asf_demux_element_send_event (GstElement * element,
    GstEvent * event);
static gboolean gst_asf_demux_send_event_unlocked (GstASFDemux * demux,
    GstEvent * event);
Wim Taymans's avatar
Wim Taymans committed
89
90
static gboolean gst_asf_demux_handle_src_query (GstPad * pad,
    GstObject * parent, GstQuery * query);
Wim Taymans's avatar
Wim Taymans committed
91
92
93
94
static GstFlowReturn gst_asf_demux_chain (GstPad * pad, GstObject * parent,
    GstBuffer * buf);
static gboolean gst_asf_demux_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event);
95
96
static GstFlowReturn gst_asf_demux_process_object (GstASFDemux * demux,
    guint8 ** p_data, guint64 * p_size);
Wim Taymans's avatar
Wim Taymans committed
97
static gboolean gst_asf_demux_activate (GstPad * sinkpad, GstObject * parent);
Wim Taymans's avatar
Wim Taymans committed
98
99
static gboolean gst_asf_demux_activate_mode (GstPad * sinkpad,
    GstObject * parent, GstPadMode mode, gboolean active);
100
static void gst_asf_demux_loop (GstASFDemux * demux);
101
102
static void
gst_asf_demux_process_queued_extended_stream_objects (GstASFDemux * demux);
103
104
static gboolean gst_asf_demux_pull_headers (GstASFDemux * demux,
    GstFlowReturn * pflow);
105
static GstFlowReturn gst_asf_demux_pull_indices (GstASFDemux * demux);
106
107
108
static void gst_asf_demux_reset_stream_state_after_discont (GstASFDemux * asf);
static gboolean
gst_asf_demux_parse_data_object_start (GstASFDemux * demux, guint8 * data);
109
110
static void gst_asf_demux_descramble_buffer (GstASFDemux * demux,
    AsfStream * stream, GstBuffer ** p_buffer);
111
112
static void gst_asf_demux_activate_stream (GstASFDemux * demux,
    AsfStream * stream);
113
114
static GstStructure *gst_asf_demux_get_metadata_for_stream (GstASFDemux * d,
    guint stream_num);
115
116
static GstFlowReturn gst_asf_demux_push_complete_payloads (GstASFDemux * demux,
    gboolean force);
117

Thiago Santos's avatar
Thiago Santos committed
118
119
#define gst_asf_demux_parent_class parent_class
G_DEFINE_TYPE (GstASFDemux, gst_asf_demux, GST_TYPE_ELEMENT);
120

121
static void
Thiago Santos's avatar
Thiago Santos committed
122
gst_asf_demux_class_init (GstASFDemuxClass * klass)
Iain Holmes's avatar
Iain Holmes committed
123
{
124
  GObjectClass *gobject_class;
Thiago Santos's avatar
Thiago Santos committed
125
  GstElementClass *gstelement_class;
Iain Holmes's avatar
Iain Holmes committed
126

127
  gobject_class = G_OBJECT_CLASS (klass);
Thiago Santos's avatar
Thiago Santos committed
128
  gstelement_class = (GstElementClass *) klass;
129

130
131
  gobject_class->finalize = gst_asf_demux_finalize;

132
  gst_element_class_set_static_metadata (gstelement_class, "ASF Demuxer",
133
134
      "Codec/Demuxer",
      "Demultiplexes ASF Streams", "Owen Fraser-Green <owen@discobabe.net>");
Iain Holmes's avatar
Iain Holmes committed
135

136
137
138
139
140
141
  gst_element_class_add_static_pad_template (gstelement_class,
      &audio_src_template);
  gst_element_class_add_static_pad_template (gstelement_class,
      &video_src_template);
  gst_element_class_add_static_pad_template (gstelement_class,
      &gst_asf_demux_sink_template);
142

143
144
145
146
  gstelement_class->change_state =
      GST_DEBUG_FUNCPTR (gst_asf_demux_change_state);
  gstelement_class->send_event =
      GST_DEBUG_FUNCPTR (gst_asf_demux_element_send_event);
147
148
}

149
static void
150
gst_asf_demux_free_stream (GstASFDemux * demux, AsfStream * stream)
151
152
153
{
  gst_caps_replace (&stream->caps, NULL);
  if (stream->pending_tags) {
154
    gst_tag_list_unref (stream->pending_tags);
155
156
    stream->pending_tags = NULL;
  }
157
158
159
160
  if (stream->streamheader) {
    gst_buffer_unref (stream->streamheader);
    stream->streamheader = NULL;
  }
161
  if (stream->pad) {
Thiago Santos's avatar
Thiago Santos committed
162
    if (stream->active) {
163
      gst_element_remove_pad (GST_ELEMENT_CAST (demux), stream->pad);
Thiago Santos's avatar
Thiago Santos committed
164
165
      gst_flow_combiner_remove_pad (demux->flowcombiner, stream->pad);
    } else
166
      gst_object_unref (stream->pad);
167
168
    stream->pad = NULL;
  }
169

170
  if (stream->payloads) {
Edward Hervey's avatar
Edward Hervey committed
171
172
173
174
175
176
177
178
179
    while (stream->payloads->len > 0) {
      AsfPayload *payload;
      guint last;

      last = stream->payloads->len - 1;
      payload = &g_array_index (stream->payloads, AsfPayload, last);
      gst_buffer_replace (&payload->buf, NULL);
      g_array_remove_index (stream->payloads, last);
    }
180
181
182
    g_array_free (stream->payloads, TRUE);
    stream->payloads = NULL;
  }
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

  if (stream->payloads_rev) {
    while (stream->payloads_rev->len > 0) {
      AsfPayload *payload;
      guint last;

      last = stream->payloads_rev->len - 1;
      payload = &g_array_index (stream->payloads_rev, AsfPayload, last);
      gst_buffer_replace (&payload->buf, NULL);
      g_array_remove_index (stream->payloads_rev, last);
    }
    g_array_free (stream->payloads_rev, TRUE);
    stream->payloads_rev = NULL;
  }

198
199
200
201
  if (stream->ext_props.valid) {
    g_free (stream->ext_props.payload_extensions);
    stream->ext_props.payload_extensions = NULL;
  }
202
203
204
}

static void
205
gst_asf_demux_reset (GstASFDemux * demux, gboolean chain_reset)
206
207
208
209
210
{
  GST_LOG_OBJECT (demux, "resetting");

  gst_segment_init (&demux->segment, GST_FORMAT_UNDEFINED);
  demux->segment_running = FALSE;
211
  if (demux->adapter && !chain_reset) {
212
213
214
215
216
    gst_adapter_clear (demux->adapter);
    g_object_unref (demux->adapter);
    demux->adapter = NULL;
  }
  if (demux->taglist) {
217
    gst_tag_list_unref (demux->taglist);
218
219
    demux->taglist = NULL;
  }
220
221
222
223
  if (demux->metadata) {
    gst_caps_unref (demux->metadata);
    demux->metadata = NULL;
  }
224
  demux->metadata = gst_caps_new_empty ();
225
226
227
228
  if (demux->global_metadata) {
    gst_structure_free (demux->global_metadata);
    demux->global_metadata = NULL;
  }
229
  demux->global_metadata = gst_structure_new_empty ("metadata");
230
231
232
233
  if (demux->mut_ex_streams) {
    g_slist_free (demux->mut_ex_streams);
    demux->mut_ex_streams = NULL;
  }
234

235
236
237
238
239
240
  demux->state = GST_ASF_DEMUX_STATE_HEADER;
  g_free (demux->objpath);
  demux->objpath = NULL;
  g_strfreev (demux->languages);
  demux->languages = NULL;
  demux->num_languages = 0;
241
242
  g_slist_foreach (demux->ext_stream_props, (GFunc) gst_mini_object_unref,
      NULL);
243
244
  g_slist_free (demux->ext_stream_props);
  demux->ext_stream_props = NULL;
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261

  while (demux->old_num_streams > 0) {
    gst_asf_demux_free_stream (demux,
        &demux->old_stream[demux->old_num_streams - 1]);
    --demux->old_num_streams;
  }
  memset (demux->old_stream, 0, sizeof (demux->old_stream));
  demux->old_num_streams = 0;

  /* when resetting for a new chained asf, we don't want to remove the pads
   * before adding the new ones */
  if (chain_reset) {
    memcpy (demux->old_stream, demux->stream, sizeof (demux->stream));
    demux->old_num_streams = demux->num_streams;
    demux->num_streams = 0;
  }

262
263
264
265
266
  while (demux->num_streams > 0) {
    gst_asf_demux_free_stream (demux, &demux->stream[demux->num_streams - 1]);
    --demux->num_streams;
  }
  memset (demux->stream, 0, sizeof (demux->stream));
267
268
269
270
  if (!chain_reset) {
    /* do not remove those for not adding pads with same name */
    demux->num_audio_streams = 0;
    demux->num_video_streams = 0;
271
272
    demux->have_group_id = FALSE;
    demux->group_id = G_MAXUINT;
273
  }
274
  demux->num_streams = 0;
275
  demux->activated_streams = FALSE;
276
  demux->first_ts = GST_CLOCK_TIME_NONE;
277
  demux->segment_ts = GST_CLOCK_TIME_NONE;
278
  demux->in_gap = 0;
279
280
  if (!chain_reset)
    gst_segment_init (&demux->in_segment, GST_FORMAT_UNDEFINED);
281
282
283
284
285
286
287
  demux->state = GST_ASF_DEMUX_STATE_HEADER;
  demux->seekable = FALSE;
  demux->broadcast = FALSE;
  demux->sidx_interval = 0;
  demux->sidx_num_entries = 0;
  g_free (demux->sidx_entries);
  demux->sidx_entries = NULL;
288
289

  demux->speed_packets = 1;
290

291
292
  demux->asf_3D_mode = GST_ASF_3D_NONE;

293
294
295
296
  if (chain_reset) {
    GST_LOG_OBJECT (demux, "Restarting");
    gst_segment_init (&demux->segment, GST_FORMAT_TIME);
    demux->need_newsegment = TRUE;
297
    demux->segment_seqnum = 0;
298
    demux->segment_running = FALSE;
299
    demux->keyunit_sync = FALSE;
300
    demux->accurate = FALSE;
301
302
303
    demux->data_size = 0;
    demux->data_offset = 0;
    demux->index_offset = 0;
304
305
  } else {
    demux->base_offset = 0;
306
  }
Matej's avatar
Matej committed
307
308
309

  g_slist_free (demux->other_streams);
  demux->other_streams = NULL;
310
311
}

312
static void
Thiago Santos's avatar
Thiago Santos committed
313
gst_asf_demux_init (GstASFDemux * demux)
314
{
315
  demux->sinkpad =
316
      gst_pad_new_from_static_template (&gst_asf_demux_sink_template, "sink");
317
318
319
320
  gst_pad_set_chain_function (demux->sinkpad,
      GST_DEBUG_FUNCPTR (gst_asf_demux_chain));
  gst_pad_set_event_function (demux->sinkpad,
      GST_DEBUG_FUNCPTR (gst_asf_demux_sink_event));
321
322
  gst_pad_set_activate_function (demux->sinkpad,
      GST_DEBUG_FUNCPTR (gst_asf_demux_activate));
Wim Taymans's avatar
Wim Taymans committed
323
324
  gst_pad_set_activatemode_function (demux->sinkpad,
      GST_DEBUG_FUNCPTR (gst_asf_demux_activate_mode));
325
  gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
326

327
  /* set initial state */
328
  gst_asf_demux_reset (demux, FALSE);
329
330
}

331
static gboolean
Wim Taymans's avatar
Wim Taymans committed
332
gst_asf_demux_activate (GstPad * sinkpad, GstObject * parent)
333
{
Thiago Santos's avatar
Thiago Santos committed
334
335
336
337
338
339
340
341
342
343
  GstQuery *query;
  gboolean pull_mode;

  query = gst_query_new_scheduling ();

  if (!gst_pad_peer_query (sinkpad, query)) {
    gst_query_unref (query);
    goto activate_push;
  }

344
345
  pull_mode = gst_query_has_scheduling_mode_with_flags (query,
      GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
Wim Taymans's avatar
Wim Taymans committed
346
  gst_query_unref (query);
Thiago Santos's avatar
Thiago Santos committed
347
348
349
350
351

  if (!pull_mode)
    goto activate_push;

  GST_DEBUG_OBJECT (sinkpad, "activating pull");
Wim Taymans's avatar
Wim Taymans committed
352
  return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
Thiago Santos's avatar
Thiago Santos committed
353
354
355
356

activate_push:
  {
    GST_DEBUG_OBJECT (sinkpad, "activating push");
Wim Taymans's avatar
Wim Taymans committed
357
    return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
358
359
360
361
  }
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
362
363
gst_asf_demux_activate_mode (GstPad * sinkpad, GstObject * parent,
    GstPadMode mode, gboolean active)
364
{
Wim Taymans's avatar
Wim Taymans committed
365
  gboolean res;
366
367
  GstASFDemux *demux;

Wim Taymans's avatar
Wim Taymans committed
368
  demux = GST_ASF_DEMUX (parent);
369

Wim Taymans's avatar
Wim Taymans committed
370
371
372
373
374
375
376
377
378
379
380
381
  switch (mode) {
    case GST_PAD_MODE_PUSH:
      demux->state = GST_ASF_DEMUX_STATE_HEADER;
      demux->streaming = TRUE;
      res = TRUE;
      break;
    case GST_PAD_MODE_PULL:
      if (active) {
        demux->state = GST_ASF_DEMUX_STATE_HEADER;
        demux->streaming = FALSE;

        res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_asf_demux_loop,
Wim Taymans's avatar
Wim Taymans committed
382
            demux, NULL);
Wim Taymans's avatar
Wim Taymans committed
383
384
385
386
387
388
389
      } else {
        res = gst_pad_stop_task (sinkpad);
      }
      break;
    default:
      res = FALSE;
      break;
390
  }
Wim Taymans's avatar
Wim Taymans committed
391
  return res;
392
393
}

394
static gboolean
Wim Taymans's avatar
Wim Taymans committed
395
gst_asf_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
396
397
398
399
{
  GstASFDemux *demux;
  gboolean ret = TRUE;

Wim Taymans's avatar
Wim Taymans committed
400
  demux = GST_ASF_DEMUX (parent);
401

402
  GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
403
  switch (GST_EVENT_TYPE (event)) {
Thiago Santos's avatar
Thiago Santos committed
404
405
406
407
408
409
410
411
    case GST_EVENT_SEGMENT:{
      const GstSegment *segment;

      gst_event_parse_segment (event, &segment);

      if (segment->format == GST_FORMAT_BYTES) {
        if (demux->packet_size && segment->start > demux->data_offset)
          demux->packet = (segment->start - demux->data_offset) /
412
413
414
              demux->packet_size;
        else
          demux->packet = 0;
Thiago Santos's avatar
Thiago Santos committed
415
      } else if (segment->format == GST_FORMAT_TIME) {
416
417
418
        /* do not know packet position, not really a problem */
        demux->packet = -1;
      } else {
419
        GST_WARNING_OBJECT (demux, "unsupported newsegment format, ignoring");
420
421
422
423
        gst_event_unref (event);
        break;
      }

424
      /* record upstream segment for interpolation */
Thiago Santos's avatar
Thiago Santos committed
425
      if (segment->format != demux->in_segment.format)
426
        gst_segment_init (&demux->in_segment, GST_FORMAT_UNDEFINED);
Thiago Santos's avatar
Thiago Santos committed
427
      gst_segment_copy_into (segment, &demux->in_segment);
428

429
      /* in either case, clear some state and generate newsegment later on */
430
      GST_OBJECT_LOCK (demux);
431
      demux->segment_ts = GST_CLOCK_TIME_NONE;
432
      demux->in_gap = GST_CLOCK_TIME_NONE;
433
      demux->need_newsegment = TRUE;
434
      demux->segment_seqnum = gst_event_get_seqnum (event);
435
      gst_asf_demux_reset_stream_state_after_discont (demux);
436
437
438
439
440
      /* if we seek back after reaching EOS, go back to packet reading state */
      if (demux->data_offset > 0 && segment->start >= demux->data_offset
          && demux->state == GST_ASF_DEMUX_STATE_INDEX) {
        demux->state = GST_ASF_DEMUX_STATE_DATA;
      }
441
      GST_OBJECT_UNLOCK (demux);
442
443

      gst_event_unref (event);
444
445
446
      break;
    }
    case GST_EVENT_EOS:{
447
448
      GstFlowReturn flow;

449
450
451
452
453
454
      if (demux->state == GST_ASF_DEMUX_STATE_HEADER) {
        GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
            (_("This stream contains no data.")),
            ("got eos and didn't receive a complete header object"));
        break;
      }
455
      flow = gst_asf_demux_push_complete_payloads (demux, TRUE);
456
457
458
459
460
461
462
      if (!demux->activated_streams) {
        /* If we still haven't got activated streams, the file is most likely corrupt */
        GST_ELEMENT_ERROR (demux, STREAM, WRONG_TYPE,
            (_("This stream contains no data.")),
            ("got eos and didn't receive a complete header object"));
        break;
      }
463
      if (flow < GST_FLOW_EOS || flow == GST_FLOW_NOT_LINKED) {
464
        GST_ELEMENT_FLOW_ERROR (demux, flow);
465
466
467
        break;
      }

468
469
470
      GST_OBJECT_LOCK (demux);
      gst_adapter_clear (demux->adapter);
      GST_OBJECT_UNLOCK (demux);
471
      gst_asf_demux_send_event_unlocked (demux, event);
472
473
      break;
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
474

475
476
477
478
479
    case GST_EVENT_FLUSH_STOP:
      GST_OBJECT_LOCK (demux);
      gst_asf_demux_reset_stream_state_after_discont (demux);
      GST_OBJECT_UNLOCK (demux);
      gst_asf_demux_send_event_unlocked (demux, event);
480
481
482
      /* upon activation, latency is no longer introduced, e.g. after seek */
      if (demux->activated_streams)
        demux->latency = 0;
483
484
      break;

485
    default:
Wim Taymans's avatar
Wim Taymans committed
486
      ret = gst_pad_event_default (pad, parent, event);
487
488
      break;
  }
489

490
  return ret;
491
492
}

493
494
static gboolean
gst_asf_demux_seek_index_lookup (GstASFDemux * demux, guint * packet,
495
496
    GstClockTime seek_time, GstClockTime * p_idx_time, guint * speed,
    gboolean next, gboolean * eos)
497
{
498
  GstClockTime idx_time;
499
500
  guint idx;

501
502
503
  if (eos)
    *eos = FALSE;

504
  if (G_UNLIKELY (demux->sidx_num_entries == 0 || demux->sidx_interval == 0))
505
506
    return FALSE;

507
  idx = (guint) ((seek_time + demux->preroll) / demux->sidx_interval);
508

509
510
511
  if (next) {
    /* if we want the next keyframe, we have to go forward till we find
       a different packet number */
512
    guint idx2;
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
    if (idx >= demux->sidx_num_entries - 1) {
      /* If we get here, we're asking for next keyframe after the last one. There isn't one. */
      if (eos)
        *eos = TRUE;
      return FALSE;
    }
    for (idx2 = idx + 1; idx2 < demux->sidx_num_entries; ++idx2) {
      if (demux->sidx_entries[idx].packet != demux->sidx_entries[idx2].packet) {
        idx = idx2;
        break;
      }
    }
  }

  if (G_UNLIKELY (idx >= demux->sidx_num_entries)) {
    if (eos)
      *eos = TRUE;
530
    return FALSE;
531
  }
532

533
534
535
  *packet = demux->sidx_entries[idx].packet;
  if (speed)
    *speed = demux->sidx_entries[idx].count;
536

537
538
539
540
541
542
543
544
545
546
  /* so we get closer to the actual time of the packet ... actually, let's not
   * do this, since we throw away superfluous payloads before the seek position
   * anyway; this way, our key unit seek 'snap resolution' is a bit better
   * (ie. same as index resolution) */
  /*
     while (idx > 0 && demux->sidx_entries[idx-1] == demux->sidx_entries[idx])
     --idx;
   */

  idx_time = demux->sidx_interval * idx;
547
548
  if (G_LIKELY (idx_time >= demux->preroll))
    idx_time -= demux->preroll;
549

550
551
  GST_DEBUG_OBJECT (demux, "%" GST_TIME_FORMAT " => packet %u at %"
      GST_TIME_FORMAT, GST_TIME_ARGS (seek_time), *packet,
552
553
      GST_TIME_ARGS (idx_time));

554
  if (G_LIKELY (p_idx_time))
555
    *p_idx_time = idx_time;
556
557
558
559
560
561
562
563
564
565
566

  return TRUE;
}

static void
gst_asf_demux_reset_stream_state_after_discont (GstASFDemux * demux)
{
  guint n;

  gst_adapter_clear (demux->adapter);

Wim Taymans's avatar
Wim Taymans committed
567
568
  GST_DEBUG_OBJECT (demux, "reset stream state");

569
  gst_flow_combiner_reset (demux->flowcombiner);
570
571
  for (n = 0; n < demux->num_streams; n++) {
    demux->stream[n].discont = TRUE;
572
    demux->stream[n].first_buffer = TRUE;
573
574
575
576
577
578
579
580
581
582

    while (demux->stream[n].payloads->len > 0) {
      AsfPayload *payload;
      guint last;

      last = demux->stream[n].payloads->len - 1;
      payload = &g_array_index (demux->stream[n].payloads, AsfPayload, last);
      gst_buffer_replace (&payload->buf, NULL);
      g_array_remove_index (demux->stream[n].payloads, last);
    }
583
584
585
  }
}

Wim Taymans's avatar
Wim Taymans committed
586
587
588
589
590
591
592
593
594
595
596
static void
gst_asf_demux_mark_discont (GstASFDemux * demux)
{
  guint n;

  GST_DEBUG_OBJECT (demux, "Mark stream discont");

  for (n = 0; n < demux->num_streams; n++)
    demux->stream[n].discont = TRUE;
}

597
598
599
600
601
602
603
604
605
606
607
/* do a seek in push based mode */
static gboolean
gst_asf_demux_handle_seek_push (GstASFDemux * demux, GstEvent * event)
{
  gdouble rate;
  GstFormat format;
  GstSeekFlags flags;
  GstSeekType cur_type, stop_type;
  gint64 cur, stop;
  guint packet;
  gboolean res;
608
  GstEvent *byte_event;
609
610
611
612
613
614
615
616
617
618

  gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
      &stop_type, &stop);

  stop_type = GST_SEEK_TYPE_NONE;
  stop = -1;

  GST_DEBUG_OBJECT (demux, "seeking to %" GST_TIME_FORMAT, GST_TIME_ARGS (cur));

  /* determine packet, by index or by estimation */
619
620
621
622
623
  if (!gst_asf_demux_seek_index_lookup (demux, &packet, cur, NULL, NULL, FALSE,
          NULL)) {
    packet =
        (guint) gst_util_uint64_scale (demux->num_packets, cur,
        demux->play_time);
624
625
626
627
628
629
630
631
632
633
  }

  if (packet > demux->num_packets) {
    GST_DEBUG_OBJECT (demux, "could not determine packet to seek to, "
        "seek aborted.");
    return FALSE;
  }

  GST_DEBUG_OBJECT (demux, "seeking to packet %d", packet);

634
  cur = demux->data_offset + ((guint64) packet * demux->packet_size);
635
636
637
638

  GST_DEBUG_OBJECT (demux, "Pushing BYTE seek rate %g, "
      "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, cur, stop);
  /* BYTE seek event */
639
640
641
642
  byte_event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type,
      cur, stop_type, stop);
  gst_event_set_seqnum (byte_event, gst_event_get_seqnum (event));
  res = gst_pad_push_event (demux->sinkpad, byte_event);
643
644
645
646

  return res;
}

647
static gboolean
648
gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event)
649
{
650
  GstClockTime idx_time;
651
652
653
654
655
  GstSegment segment;
  GstSeekFlags flags;
  GstSeekType cur_type, stop_type;
  GstFormat format;
  gboolean only_need_update;
656
  gboolean after, before, next;
657
658
659
  gboolean flush;
  gdouble rate;
  gint64 cur, stop;
660
  gint64 seek_time;
661
  guint packet, speed_count = 1;
662
  gboolean eos;
663
664
  guint32 seqnum;
  GstEvent *fevent;
665
  gint i;
666

667
668
669
670
671
672
673
674
  gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
      &stop_type, &stop);

  if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
    GST_LOG_OBJECT (demux, "seeking is only supported in TIME format");
    return FALSE;
  }

675
676
  if (G_UNLIKELY (demux->seekable == FALSE || demux->packet_size == 0 ||
          demux->num_packets == 0 || demux->play_time == 0)) {
677
678
679
680
    GST_LOG_OBJECT (demux, "stream is not seekable");
    return FALSE;
  }

681
  if (G_UNLIKELY (!demux->activated_streams)) {
682
683
684
685
    GST_LOG_OBJECT (demux, "streams not yet activated, ignoring seek");
    return FALSE;
  }

686
  if (G_UNLIKELY (rate <= 0.0)) {
687
688
689
690
691
    GST_LOG_OBJECT (demux, "backward playback");
    demux->seek_to_cur_pos = TRUE;
    for (i = 0; i < demux->num_streams; i++) {
      demux->stream[i].reverse_kf_ready = FALSE;
    }
692
  }
693

694
  seqnum = gst_event_get_seqnum (event);
695
  flush = ((flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH);
696
697
  demux->accurate =
      ((flags & GST_SEEK_FLAG_ACCURATE) == GST_SEEK_FLAG_ACCURATE);
698
699
  demux->keyunit_sync =
      ((flags & GST_SEEK_FLAG_KEY_UNIT) == GST_SEEK_FLAG_KEY_UNIT);
700
701
702
  after = ((flags & GST_SEEK_FLAG_SNAP_AFTER) == GST_SEEK_FLAG_SNAP_AFTER);
  before = ((flags & GST_SEEK_FLAG_SNAP_BEFORE) == GST_SEEK_FLAG_SNAP_BEFORE);
  next = after && !before;
703

704
  if (G_UNLIKELY (demux->streaming)) {
705
706
707
708
709
    /* upstream might handle TIME seek, e.g. mms or rtsp, or not, e.g. http,
    * so first try to let it handle the seek event. */
    if (gst_pad_push_event (demux->sinkpad, gst_event_ref (event)))
        return TRUE;

710
711
712
713
714
715
    /* support it safely needs more segment handling, e.g. closing etc */
    if (!flush) {
      GST_LOG_OBJECT (demux, "streaming; non-flushing seek not supported");
      return FALSE;
    }
    /* we can (re)construct the start later on, but not the end */
716
717
718
    if (stop_type != GST_SEEK_TYPE_NONE &&
        (stop_type != GST_SEEK_TYPE_SET || GST_CLOCK_TIME_IS_VALID (stop))) {
      GST_LOG_OBJECT (demux, "streaming; end position must be NONE");
719
720
      return FALSE;
    }
721
    return gst_asf_demux_handle_seek_push (demux, event);
722
723
  }

724
  /* unlock the streaming thread */
725
  if (G_LIKELY (flush)) {
726
727
728
729
730
    fevent = gst_event_new_flush_start ();

    gst_event_set_seqnum (fevent, seqnum);
    gst_pad_push_event (demux->sinkpad, gst_event_ref (fevent));
    gst_asf_demux_send_event_unlocked (demux, fevent);
731
732
733
734
735
736
737
738
739
  } else {
    gst_pad_pause_task (demux->sinkpad);
  }

  /* grab the stream lock so that streaming cannot continue, for
   * non flushing seeks when the element is in PAUSED this could block
   * forever */
  GST_PAD_STREAM_LOCK (demux->sinkpad);

740
741
742
743
744
  if (G_LIKELY (flush)) {
    /* we now can stop flushing, since we have the stream lock now */
    fevent = gst_event_new_flush_stop (TRUE);
    gst_event_set_seqnum (fevent, seqnum);
    gst_pad_push_event (demux->sinkpad, gst_event_ref (fevent));
745
    gst_asf_demux_send_event_unlocked (demux, fevent);
746
  }
747

748
749
750
  /* operating on copy of segment until we know the seek worked */
  segment = demux->segment;

Thiago Santos's avatar
Thiago Santos committed
751
  gst_segment_do_seek (&segment, rate, format, flags, cur_type,
752
      cur, stop_type, stop, &only_need_update);
753

754
755
  GST_DEBUG_OBJECT (demux, "seeking to time %" GST_TIME_FORMAT ", segment: "
      "%" GST_SEGMENT_FORMAT, GST_TIME_ARGS (segment.start), &segment);
756

757
758
759
760
  if (cur_type != GST_SEEK_TYPE_SET)
    seek_time = segment.start;
  else
    seek_time = cur;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
761

Thiago Santos's avatar
Thiago Santos committed
762
  /* FIXME: should check the KEY_UNIT flag; need to adjust position to
763
   * real start of data and segment_start to indexed time for key unit seek*/
764
  if (G_UNLIKELY (!gst_asf_demux_seek_index_lookup (demux, &packet, seek_time,
765
766
767
768
769
770
771
772
              &idx_time, &speed_count, next, &eos))) {
    gint64 offset;

    if (eos) {
      demux->packet = demux->num_packets;
      goto skip;
    }

773
774
775
    /* First try to query our source to see if it can convert for us. This is
       the case when our source is an mms stream, notice that in this case
       gstmms will do a time based seek to get the byte offset, this is not a
Aaron Boxer's avatar
Aaron Boxer committed
776
       problem as the seek to this offset needs to happen anyway. */
777
    if (gst_pad_peer_query_convert (demux->sinkpad, GST_FORMAT_TIME, seek_time,
778
            GST_FORMAT_BYTES, &offset)) {
779
780
      packet = (offset - demux->data_offset) / demux->packet_size;
      GST_LOG_OBJECT (demux, "convert %" GST_TIME_FORMAT
781
782
783
784
          " to bytes query result: %" G_GINT64_FORMAT ", data_ofset: %"
          G_GINT64_FORMAT ", packet_size: %u," " resulting packet: %u\n",
          GST_TIME_ARGS (seek_time), offset, demux->data_offset,
          demux->packet_size, packet);
785
    } else {
786
787
788
789
      /* FIXME: For streams containing video, seek to an earlier position in
       * the hope of hitting a keyframe and let the sinks throw away the stuff
       * before the segment start. For audio-only this is unnecessary as every
       * frame is 'key'. */
790
      if (flush && (demux->accurate || (demux->keyunit_sync && !next))
791
          && demux->num_video_streams > 0) {
792
793
794
795
        seek_time -= 5 * GST_SECOND;
        if (seek_time < 0)
          seek_time = 0;
      }
796

797
798
      packet = (guint) gst_util_uint64_scale (demux->num_packets,
          seek_time, demux->play_time);
799

800
801
802
      if (packet > demux->num_packets)
        packet = demux->num_packets;
    }
803
  } else {
804
    if (G_LIKELY (demux->keyunit_sync && !demux->accurate)) {
805
806
807
808
      GST_DEBUG_OBJECT (demux, "key unit seek, adjust seek_time = %"
          GST_TIME_FORMAT " to index_time = %" GST_TIME_FORMAT,
          GST_TIME_ARGS (seek_time), GST_TIME_ARGS (idx_time));
      segment.start = idx_time;
Thiago Santos's avatar
Thiago Santos committed
809
      segment.position = idx_time;
810
811
      segment.time = idx_time;
    }
812
813
  }

814
  GST_DEBUG_OBJECT (demux, "seeking to packet %u (%d)", packet, speed_count);
815

816
817
  GST_OBJECT_LOCK (demux);
  demux->segment = segment;
818
819
820
821
822
823
824
  if (GST_ASF_DEMUX_IS_REVERSE_PLAYBACK (demux->segment)) {
    demux->packet = (gint64) gst_util_uint64_scale (demux->num_packets,
        stop, demux->play_time);
  } else {
    demux->packet = packet;
  }

825
  demux->need_newsegment = TRUE;
826
  demux->segment_seqnum = seqnum;
827
828
  demux->speed_packets =
      GST_ASF_DEMUX_IS_REVERSE_PLAYBACK (demux->segment) ? 1 : speed_count;
829
  gst_asf_demux_reset_stream_state_after_discont (demux);
830
831
  GST_OBJECT_UNLOCK (demux);

832
skip:
833
834
  /* restart our task since it might have been stopped when we did the flush */
  gst_pad_start_task (demux->sinkpad, (GstTaskFunction) gst_asf_demux_loop,
Wim Taymans's avatar
Wim Taymans committed
835
      demux, NULL);
836

837
838
839
840
  /* streaming can continue now */
  GST_PAD_STREAM_UNLOCK (demux->sinkpad);

  return TRUE;
841
842
}

843
static gboolean
Wim Taymans's avatar
Wim Taymans committed
844
845
gst_asf_demux_handle_src_event (GstPad * pad, GstObject * parent,
    GstEvent * event)
846
{
847
848
  GstASFDemux *demux;
  gboolean ret;
849

Wim Taymans's avatar
Wim Taymans committed
850
  demux = GST_ASF_DEMUX (parent);
851

852
853
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
854
      GST_LOG_OBJECT (pad, "seek event");
855
856
      ret = gst_asf_demux_handle_seek_event (demux, event);
      gst_event_unref (event);
857
      break;
858
859
860
861
862
863
    case GST_EVENT_QOS:
    case GST_EVENT_NAVIGATION:
      /* just drop these two silently */
      gst_event_unref (event);
      ret = FALSE;
      break;
864
    default:
865
      GST_LOG_OBJECT (pad, "%s event", GST_EVENT_TYPE_NAME (event));
Wim Taymans's avatar
Wim Taymans committed
866
      ret = gst_pad_event_default (pad, parent, event);
867
868
      break;
  }
869
870

  return ret;
871
872
}

873
874
static inline guint32
gst_asf_demux_identify_guid (const ASFGuidHash * guids, ASFGuid * guid)
875
{
876
  guint32 ret;
877

878
  ret = gst_asf_identify_guid (guids, guid);
879

880
881
882
  GST_LOG ("%s  0x%08x-0x%08x-0x%08x-0x%08x",
      gst_asf_get_guid_nick (guids, ret),
      guid->v1, guid->v2, guid->v3, guid->v4);
883

884
885
  return ret;
}
886

887
888
889
typedef struct
{
  AsfObjectID id;
890
  guint64 size;
891
892
} AsfObject;

893

894
895
896
897
/* Peek for an object.
 *
 * Returns FALSE is the object is corrupted (such as the reported
 * object size being greater than 2**32bits.
898
 */
899
900
static gboolean
asf_demux_peek_object (GstASFDemux * demux, const guint8 * data,
901
    guint data_len, AsfObject * object, gboolean expect)
902
903
{
  ASFGuid guid;
904
905
906

  /* Callers should have made sure that data_len is big enough */
  g_assert (data_len >= ASF_OBJECT_HEADER_SIZE);
907
908
909
910
911
912
913
914
915
916
917

  if (data_len < ASF_OBJECT_HEADER_SIZE)
    return FALSE;

  guid.v1 = GST_READ_UINT32_LE (data + 0);
  guid.v2 = GST_READ_UINT32_LE (data + 4);
  guid.v3 = GST_READ_UINT32_LE (data + 8);
  guid.v4 = GST_READ_UINT32_LE (data + 12);

  /* FIXME: make asf_demux_identify_object_guid() */
  object->id = gst_asf_demux_identify_guid (asf_object_guids, &guid);
918
  if (object->id == ASF_OBJ_UNDEFINED && expect) {
919
920
    GST_WARNING_OBJECT (demux, "Unknown object %08x-%08x-%08x-%08x",
        guid.v1, guid.v2, guid.v3, guid.v4);
921
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
922

923
924
925
926
927
928
929
930
  object->size = GST_READ_UINT64_LE (data + 16);
  if (object->id != ASF_OBJ_DATA && object->size >= G_MAXUINT) {
    GST_WARNING_OBJECT (demux,
        "ASF Object size corrupted (greater than 32bit)");
    return FALSE;
  }


931
  return TRUE;
932
}
933

934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
static void
gst_asf_demux_release_old_pads (GstASFDemux * demux)
{
  GST_DEBUG_OBJECT (demux, "Releasing old pads");

  while (demux->old_num_streams > 0) {
    gst_pad_push_event (demux->old_stream[demux->old_num_streams - 1].pad,
        gst_event_new_eos ());
    gst_asf_demux_free_stream (demux,
        &demux->old_stream[demux->old_num_streams - 1]);
    --demux->old_num_streams;
  }
  memset (demux->old_stream, 0, sizeof (demux->old_stream));
  demux->old_num_streams = 0;
}

950
static GstFlowReturn
951
gst_asf_demux_chain_headers (GstASFDemux * demux)
952
{
953
  AsfObject obj;
954
  guint8 *header_data, *data = NULL;
955
  const guint8 *cdata = NULL;
956
  guint64 header_size;
957
  GstFlowReturn flow = GST_FLOW_OK;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
958

Thiago Santos's avatar
Thiago Santos committed
959
  cdata = (guint8 *) gst_adapter_map (demux->adapter, ASF_OBJECT_HEADER_SIZE);
960
  if (cdata == NULL)
961
    goto need_more_data;
962

963
964
  if (!asf_demux_peek_object (demux, cdata, ASF_OBJECT_HEADER_SIZE, &obj, TRUE))
    goto parse_failed;
965
966
  if (obj.id != ASF_OBJ_HEADER)
    goto wrong_type;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
967

968
  GST_LOG_OBJECT (demux, "header size = %u", (guint) obj.size);
969

970
971
972
  /* + 50 for non-packet data at beginning of ASF_OBJ_DATA */
  if (gst_adapter_available (demux->adapter) < obj.size + 50)
    goto need_more_data;