gstffmpegdemux.c 55.5 KB
Newer Older
1
/* GStreamer
2 3
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>,
 *               <2006> Edward Hervey <bilboed@bilboed.com>
4
 *               <2006> Wim Taymans <wim@fluendo.com>
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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.
 */

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

#include <string.h>
27 28
#ifdef HAVE_FFMPEG_UNINSTALLED
#include <avformat.h>
29
#ifdef HAVE_AVI_H
30
#include <avi.h>
31
#endif
32
#else
33
#include <libavformat/avformat.h>
34
#ifdef HAVE_AVI_H
35 36
#include <ffmpeg/avi.h>
#endif
37
#endif
38 39
#include <gst/gst.h>

40
#include "gstffmpeg.h"
41
#include "gstffmpegcodecmap.h"
42
#include "gstffmpegutils.h"
43
#include "gstffmpegpipe.h"
44 45

typedef struct _GstFFMpegDemux GstFFMpegDemux;
46 47 48 49 50 51 52 53 54 55 56 57
typedef struct _GstFFStream GstFFStream;

struct _GstFFStream
{
  GstPad *pad;

  AVStream *avstream;

  gboolean unknown;
  GstClockTime last_ts;
  gboolean discont;
  gboolean eos;
58
  GstFlowReturn last_flow;
59
};
60

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
61 62 63
struct _GstFFMpegDemux
{
  GstElement element;
64 65

  /* We need to keep track of our pads, so we do so here. */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
66
  GstPad *sinkpad;
67

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
68 69
  AVFormatContext *context;
  gboolean opened;
70

71 72
  GstFFStream *streams[MAX_STREAMS];

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
73
  gint videopads, audiopads;
74

75 76
  GstClockTime start_time;
  GstClockTime duration;
77

78
  /* TRUE if working in pull-mode */
79 80
  gboolean seekable;

81 82 83
  /* TRUE if the avformat demuxer can reliably handle streaming mode */
  gboolean can_push;

84
  gboolean flushing;
85

86
  /* segment stuff */
87 88 89 90 91
  GstSegment segment;
  gboolean running;

  /* cached seek in READY */
  GstEvent *seek_event;
92 93 94 95 96

  /* push mode data */
  GstFFMpegPipe ffpipe;
  GstTask *task;
  GStaticRecMutex *task_lock;
97 98
};

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
99 100 101 102
typedef struct _GstFFMpegDemuxClassParams
{
  AVInputFormat *in_plugin;
  GstCaps *sinkcaps, *videosrccaps, *audiosrccaps;
103 104
} GstFFMpegDemuxClassParams;

105 106
typedef struct _GstFFMpegDemuxClass GstFFMpegDemuxClass;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
107 108 109
struct _GstFFMpegDemuxClass
{
  GstElementClass parent_class;
110

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
111 112 113 114
  AVInputFormat *in_plugin;
  GstPadTemplate *sinktempl;
  GstPadTemplate *videosrctempl;
  GstPadTemplate *audiosrctempl;
115 116
};

117
/* A number of function prototypes are given so we can refer to them later. */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
118 119 120
static void gst_ffmpegdemux_class_init (GstFFMpegDemuxClass * klass);
static void gst_ffmpegdemux_base_init (GstFFMpegDemuxClass * klass);
static void gst_ffmpegdemux_init (GstFFMpegDemux * demux);
121 122 123 124
static void gst_ffmpegdemux_finalize (GObject * object);

static gboolean gst_ffmpegdemux_sink_event (GstPad * sinkpad, GstEvent * event);
static GstFlowReturn gst_ffmpegdemux_chain (GstPad * sinkpad, GstBuffer * buf);
125

126
static void gst_ffmpegdemux_loop (GstFFMpegDemux * demux);
127
static gboolean gst_ffmpegdemux_sink_activate (GstPad * sinkpad);
128 129
static gboolean
gst_ffmpegdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
130 131
static gboolean
gst_ffmpegdemux_sink_activate_push (GstPad * sinkpad, gboolean active);
132

133
#if 0
134 135
static gboolean
gst_ffmpegdemux_src_convert (GstPad * pad,
136 137 138 139
    GstFormat src_fmt,
    gint64 src_value, GstFormat * dest_fmt, gint64 * dest_value);
#endif
static gboolean
140
gst_ffmpegdemux_send_event (GstElement * element, GstEvent * event);
141 142
static GstStateChangeReturn
gst_ffmpegdemux_change_state (GstElement * element, GstStateChange transition);
143

144 145
#define GST_FFDEMUX_PARAMS_QDATA g_quark_from_static_string("ffdemux-params")

146 147
static GstElementClass *parent_class = NULL;

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
static const gchar *
gst_ffmpegdemux_averror (gint av_errno)
{
  const gchar *message = NULL;

  switch (av_errno) {
    case AVERROR_UNKNOWN:
      message = "Unknown error";
      break;
    case AVERROR_IO:
      message = "Input/output error";
      break;
    case AVERROR_NUMEXPECTED:
      message = "Number syntax expected in filename";
      break;
    case AVERROR_NOMEM:
      message = "Not enough memory";
      break;
    case AVERROR_NOFMT:
      message = "Unknown format";
      break;
    case AVERROR_NOTSUPP:
      message = "Operation not supported";
      break;
172 173 174
    default:
      message = "Unhandled error code received";
      break;
175 176 177 178 179
  }

  return message;
}

Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
180
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
181
gst_ffmpegdemux_base_init (GstFFMpegDemuxClass * klass)
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
182 183 184
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstFFMpegDemuxClassParams *params;
Benjamin Otte's avatar
Benjamin Otte committed
185
  GstElementDetails details;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
186 187
  GstPadTemplate *sinktempl, *audiosrctempl, *videosrctempl;

188
  params = (GstFFMpegDemuxClassParams *)
189 190
      g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass), GST_FFDEMUX_PARAMS_QDATA);
  g_assert (params != NULL);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
191 192

  /* construct the element details struct */
193
  details.longname = g_strdup_printf ("FFmpeg %s demuxer",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
194
      params->in_plugin->long_name);
195
  details.klass = "Codec/Demuxer";
196
  details.description = g_strdup_printf ("FFmpeg %s demuxer",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
197
      params->in_plugin->long_name);
198
  details.author = "Wim Taymans <wim@fluendo.com>, "
199 200
      "Ronald Bultje <rbultje@ronald.bitfreak.net>, "
      "Edward Hervey <bilboed@bilboed.com>";
Benjamin Otte's avatar
Benjamin Otte committed
201 202 203
  gst_element_class_set_details (element_class, &details);
  g_free (details.longname);
  g_free (details.description);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
204 205

  /* pad templates */
206
  gst_caps_ref (params->sinkcaps);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
207
  sinktempl = gst_pad_template_new ("sink",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
208
      GST_PAD_SINK, GST_PAD_ALWAYS, params->sinkcaps);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
209
  videosrctempl = gst_pad_template_new ("video_%02d",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
210
      GST_PAD_SRC, GST_PAD_SOMETIMES, params->videosrccaps);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
211
  audiosrctempl = gst_pad_template_new ("audio_%02d",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
212
      GST_PAD_SRC, GST_PAD_SOMETIMES, params->audiosrccaps);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
213

214 215 216
  params->videosrccaps = NULL;
  params->audiosrccaps = NULL;

Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
217 218 219 220 221 222 223 224 225 226
  gst_element_class_add_pad_template (element_class, videosrctempl);
  gst_element_class_add_pad_template (element_class, audiosrctempl);
  gst_element_class_add_pad_template (element_class, sinktempl);

  klass->in_plugin = params->in_plugin;
  klass->videosrctempl = videosrctempl;
  klass->audiosrctempl = audiosrctempl;
  klass->sinktempl = sinktempl;
}

227
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
228
gst_ffmpegdemux_class_init (GstFFMpegDemuxClass * klass)
229 230 231 232
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
233 234
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
235

236
  parent_class = g_type_class_peek_parent (klass);
237

238 239
  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_ffmpegdemux_finalize);

240
  gstelement_class->change_state = gst_ffmpegdemux_change_state;
241
  gstelement_class->send_event = gst_ffmpegdemux_send_event;
242 243 244
}

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
245
gst_ffmpegdemux_init (GstFFMpegDemux * demux)
246
{
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
247 248
  GstFFMpegDemuxClass *oclass =
      (GstFFMpegDemuxClass *) (G_OBJECT_GET_CLASS (demux));
Ronald S. Bultje's avatar
Ronald S. Bultje committed
249
  gint n;
250

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
251
  demux->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
252 253
  gst_pad_set_activate_function (demux->sinkpad,
      GST_DEBUG_FUNCPTR (gst_ffmpegdemux_sink_activate));
254
  gst_pad_set_activatepull_function (demux->sinkpad,
255
      GST_DEBUG_FUNCPTR (gst_ffmpegdemux_sink_activate_pull));
256
  gst_pad_set_activatepush_function (demux->sinkpad,
257
      GST_DEBUG_FUNCPTR (gst_ffmpegdemux_sink_activate_push));
258
  gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
259

260 261 262 263 264 265 266 267 268 269 270 271
  /* push based setup */
  /* the following are not used in pull-based mode, so safe to set anyway */
  gst_pad_set_event_function (demux->sinkpad,
      GST_DEBUG_FUNCPTR (gst_ffmpegdemux_sink_event));
  gst_pad_set_chain_function (demux->sinkpad,
      GST_DEBUG_FUNCPTR (gst_ffmpegdemux_chain));
  /* task for driving ffmpeg in loop function */
  demux->task = gst_task_create ((GstTaskFunction) gst_ffmpegdemux_loop, demux);
  demux->task_lock = g_new (GStaticRecMutex, 1);
  g_static_rec_mutex_init (demux->task_lock);
  gst_task_set_lock (demux->task, demux->task_lock);

272
  demux->opened = FALSE;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
273
  demux->context = NULL;
274

Ronald S. Bultje's avatar
Ronald S. Bultje committed
275
  for (n = 0; n < MAX_STREAMS; n++) {
276
    demux->streams[n] = NULL;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
277
  }
278 279
  demux->videopads = 0;
  demux->audiopads = 0;
280 281

  demux->seek_event = NULL;
282
  gst_segment_init (&demux->segment, GST_FORMAT_TIME);
283 284 285 286 287

  /* push based data */
  demux->ffpipe.tlock = g_mutex_new ();
  demux->ffpipe.cond = g_cond_new ();
  demux->ffpipe.adapter = gst_adapter_new ();
288 289 290 291 292 293

  /* blacklist unreliable push-based demuxers */
  if (strcmp (oclass->in_plugin->name, "ape"))
    demux->can_push = TRUE;
  else
    demux->can_push = FALSE;
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
}

static void
gst_ffmpegdemux_finalize (GObject * object)
{
  GstFFMpegDemux *demux;

  demux = (GstFFMpegDemux *) object;

  g_mutex_free (demux->ffpipe.tlock);
  g_cond_free (demux->ffpipe.cond);
  gst_object_unref (demux->ffpipe.adapter);

  gst_object_unref (demux->task);
  g_static_rec_mutex_free (demux->task_lock);
  g_free (demux->task_lock);

  G_OBJECT_CLASS (parent_class)->finalize (object);
312 313 314
}

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
315
gst_ffmpegdemux_close (GstFFMpegDemux * demux)
316 317
{
  gint n;
318
  GstEvent **event_p;
319 320 321 322 323 324

  if (!demux->opened)
    return;

  /* remove pads from ourselves */
  for (n = 0; n < MAX_STREAMS; n++) {
325 326 327
    GstFFStream *stream;

    stream = demux->streams[n];
328
    if (stream && stream->pad) {
329 330
      gst_element_remove_pad (GST_ELEMENT (demux), stream->pad);
      g_free (stream);
331
    }
332
    demux->streams[n] = NULL;
333 334 335 336 337 338
  }
  demux->videopads = 0;
  demux->audiopads = 0;

  /* close demuxer context from ffmpeg */
  av_close_input_file (demux->context);
Ronald S. Bultje's avatar
Ronald S. Bultje committed
339
  demux->context = NULL;
340

341
  GST_OBJECT_LOCK (demux);
342
  demux->opened = FALSE;
343 344 345 346 347
  event_p = &demux->seek_event;
  gst_event_replace (event_p, NULL);
  GST_OBJECT_UNLOCK (demux);

  gst_segment_init (&demux->segment, GST_FORMAT_TIME);
348 349
}

350 351 352 353 354 355 356
/* send an event to all the source pads .
 * Takes ownership of the event.
 *
 * Returns FALSE if none of the source pads handled the event.
 */
static gboolean
gst_ffmpegdemux_push_event (GstFFMpegDemux * demux, GstEvent * event)
357
{
358
  gboolean res;
359 360
  gint n;

361 362
  res = TRUE;

363
  for (n = 0; n < MAX_STREAMS; n++) {
364 365
    GstFFStream *s = demux->streams[n];

366
    if (s && s->pad) {
367 368
      gst_event_ref (event);
      res &= gst_pad_push_event (s->pad, event);
369 370
    }
  }
371
  gst_event_unref (event);
372

373
  return res;
374 375
}

376 377
/* set flags on all streams */
static void
378 379
gst_ffmpegdemux_set_flags (GstFFMpegDemux * demux, gboolean discont,
    gboolean eos)
380
{
381 382
  GstFFStream *s;
  gint n;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
383

384 385 386 387 388 389 390
  for (n = 0; n < MAX_STREAMS; n++) {
    if ((s = demux->streams[n])) {
      s->discont = discont;
      s->eos = eos;
    }
  }
}
391

392 393 394 395 396 397
/* check if all streams are eos */
static gboolean
gst_ffmpegdemux_is_eos (GstFFMpegDemux * demux)
{
  GstFFStream *s;
  gint n;
398

399 400
  for (n = 0; n < MAX_STREAMS; n++) {
    if ((s = demux->streams[n])) {
401
      GST_DEBUG ("stream %d %p eos:%d", n, s, s->eos);
402
      if (!s->eos)
403
        return FALSE;
404
    }
405
  }
406 407
  return TRUE;
}
408

409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
/* Returns True if we at least outputted one buffer */
static gboolean
gst_ffmpegdemux_has_outputted (GstFFMpegDemux * demux)
{
  GstFFStream *s;
  gint n;

  for (n = 0; n < MAX_STREAMS; n++) {
    if ((s = demux->streams[n])) {
      if (GST_CLOCK_TIME_IS_VALID (s->last_ts))
        return TRUE;
    }
  }
  return FALSE;
}

425
static gboolean
426
gst_ffmpegdemux_do_seek (GstFFMpegDemux * demux, GstSegment * segment)
427 428 429 430 431 432 433 434 435 436
{
  gboolean ret;
  gint seekret;
  gint64 target;
  gint64 fftarget;
  AVStream *stream;
  gint index;

  /* find default index and fail if none is present */
  index = av_find_default_stream_index (demux->context);
437
  GST_LOG_OBJECT (demux, "default stream index %d", index);
438 439
  if (index < 0)
    return FALSE;
440

441
  ret = TRUE;
442

443 444 445 446 447 448
  /* get the stream for seeking */
  stream = demux->context->streams[index];
  /* initial seek position */
  target = segment->last_stop;
  /* convert target to ffmpeg time */
  fftarget = gst_ffmpeg_time_gst_to_ff (target, stream->time_base);
449

450 451
  GST_LOG_OBJECT (demux, "do seek to time %" GST_TIME_FORMAT,
      GST_TIME_ARGS (target));
452 453 454 455

  /* if we need to land on a keyframe, try to do so, we don't try to do a 
   * keyframe seek if we are not absolutely sure we have an index.*/
  if (segment->flags & GST_SEEK_FLAG_KEY_UNIT && demux->context->index_built) {
456 457
    gint keyframeidx;

458
    GST_LOG_OBJECT (demux, "looking for keyframe in ffmpeg for time %"
459
        GST_TIME_FORMAT, GST_TIME_ARGS (target));
460 461

    /* search in the index for the previous keyframe */
462 463
    keyframeidx =
        av_index_search_timestamp (stream, fftarget, AVSEEK_FLAG_BACKWARD);
464 465 466

    GST_LOG_OBJECT (demux, "keyframeidx: %d", keyframeidx);

467
    if (keyframeidx >= 0) {
468 469 470 471
      fftarget = stream->index_entries[keyframeidx].timestamp;
      target = gst_ffmpeg_time_ff_to_gst (fftarget, stream->time_base);

      GST_LOG_OBJECT (demux,
472 473
          "Found a keyframe at ffmpeg idx: %d timestamp :%" GST_TIME_FORMAT,
          keyframeidx, GST_TIME_ARGS (target));
474 475 476 477 478 479 480
    }
  }

  GST_DEBUG_OBJECT (demux,
      "About to call av_seek_frame (context, %d, %lld, 0) for time %"
      GST_TIME_FORMAT, index, fftarget, GST_TIME_ARGS (target));

481 482 483
  if ((seekret =
          av_seek_frame (demux->context, index, fftarget,
              AVSEEK_FLAG_BACKWARD)) < 0)
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
    goto seek_failed;

  GST_DEBUG_OBJECT (demux, "seek success, returned %d", seekret);

  segment->last_stop = target;
  segment->time = target;
  segment->start = target;

  return ret;

  /* ERRORS */
seek_failed:
  {
    GST_WARNING_OBJECT (demux, "Call to av_seek_frame failed : %d", seekret);
    return FALSE;
  }
}

static gboolean
503
gst_ffmpegdemux_perform_seek (GstFFMpegDemux * demux, GstEvent * event)
504 505 506 507 508 509 510 511 512 513 514
{
  gboolean res;
  gdouble rate;
  GstFormat format;
  GstSeekFlags flags;
  GstSeekType cur_type, stop_type;
  gint64 cur, stop;
  gboolean flush;
  gboolean update;
  GstSegment seeksegment;

515 516 517 518 519
  if (!demux->seekable) {
    GST_DEBUG_OBJECT (demux, "in push mode; ignoring seek");
    return FALSE;
  }

520 521 522 523 524 525 526 527 528 529 530 531 532 533
  GST_DEBUG_OBJECT (demux, "starting seek");

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

    /* we have to have a format as the segment format. Try to convert
     * if not. */
    if (demux->segment.format != format) {
      GstFormat fmt;

      fmt = demux->segment.format;
      res = TRUE;
      /* FIXME, use source pad */
534
      if (cur_type != GST_SEEK_TYPE_NONE && cur != -1)
535
        res = gst_pad_query_convert (demux->sinkpad, format, cur, &fmt, &cur);
536
      if (res && stop_type != GST_SEEK_TYPE_NONE && stop != -1)
537 538 539 540 541
        res = gst_pad_query_convert (demux->sinkpad, format, stop, &fmt, &stop);
      if (!res)
        goto no_format;

      format = fmt;
542 543
    }
  } else {
544
    flags = 0;
545 546
  }

547
  flush = flags & GST_SEEK_FLAG_FLUSH;
548

549 550 551 552 553 554 555 556
  /* send flush start */
  if (flush) {
    /* mark flushing so that the streaming thread can react on it */
    GST_OBJECT_LOCK (demux);
    demux->flushing = TRUE;
    GST_OBJECT_UNLOCK (demux);
    gst_pad_push_event (demux->sinkpad, gst_event_new_flush_start ());
    gst_ffmpegdemux_push_event (demux, gst_event_new_flush_start ());
557
  } else {
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
    gst_pad_pause_task (demux->sinkpad);
  }

  /* grab streaming lock, this should eventually be possible, either
   * because the task is paused or our streaming thread stopped
   * because our peer is flushing. */
  GST_PAD_STREAM_LOCK (demux->sinkpad);

  /* make copy into temp structure, we can only update the main one
   * when we actually could do the seek. */
  memcpy (&seeksegment, &demux->segment, sizeof (GstSegment));

  /* now configure the seek segment */
  if (event) {
    gst_segment_set_seek (&seeksegment, rate, format, flags,
        cur_type, cur, stop_type, stop, &update);
  }

  GST_DEBUG_OBJECT (demux, "segment configured from %" G_GINT64_FORMAT
      " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT,
      seeksegment.start, seeksegment.stop, seeksegment.last_stop);

  /* make the sinkpad available for data passing since we might need
   * it when doing the seek */
582
  if (flush) {
583 584 585
    GST_OBJECT_LOCK (demux);
    demux->flushing = FALSE;
    GST_OBJECT_UNLOCK (demux);
586 587 588
    gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop ());
  }

589 590 591 592 593
  /* do the seek, segment.last_stop contains new position. */
  res = gst_ffmpegdemux_do_seek (demux, &seeksegment);

  /* and prepare to continue streaming */
  if (flush) {
594
    gint n;
595

596 597 598
    /* send flush stop, peer will accept data and events again. We
     * are not yet providing data as we still have the STREAM_LOCK. */
    gst_ffmpegdemux_push_event (demux, gst_event_new_flush_stop ());
599 600 601 602
    for (n = 0; n < MAX_STREAMS; ++n) {
      if (demux->streams[n])
        demux->streams[n]->last_flow = GST_FLOW_OK;
    }
603 604 605 606
  } else if (res && demux->running) {
    /* we are running the current segment and doing a non-flushing seek,
     * close the segment first based on the last_stop. */
    GST_DEBUG_OBJECT (demux, "closing running segment %" G_GINT64_FORMAT
607 608
        " to %" G_GINT64_FORMAT, demux->segment.start,
        demux->segment.last_stop);
609 610 611 612

    gst_ffmpegdemux_push_event (demux,
        gst_event_new_new_segment (TRUE,
            demux->segment.rate, demux->segment.format,
613 614
            demux->segment.start, demux->segment.last_stop,
            demux->segment.time));
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
  }
  /* if successfull seek, we update our real segment and push
   * out the new segment. */
  if (res) {
    memcpy (&demux->segment, &seeksegment, sizeof (GstSegment));

    if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
      gst_element_post_message (GST_ELEMENT (demux),
          gst_message_new_segment_start (GST_OBJECT (demux),
              demux->segment.format, demux->segment.last_stop));
    }

    /* now send the newsegment */
    GST_DEBUG_OBJECT (demux, "Sending newsegment from %" G_GINT64_FORMAT
        " to %" G_GINT64_FORMAT, demux->segment.last_stop, demux->segment.stop);

    gst_ffmpegdemux_push_event (demux,
        gst_event_new_new_segment (FALSE,
            demux->segment.rate, demux->segment.format,
634 635
            demux->segment.last_stop, demux->segment.stop,
            demux->segment.time));
636 637 638 639 640 641 642 643
  }

  /* Mark discont on all srcpads and remove eos */
  gst_ffmpegdemux_set_flags (demux, TRUE, FALSE);

  /* and restart the task in case it got paused explicitely or by
   * the FLUSH_START event we pushed out. */
  demux->running = TRUE;
644
  gst_pad_start_task (demux->sinkpad, (GstTaskFunction) gst_ffmpegdemux_loop,
645
      demux->sinkpad);
646

647
  /* and release the lock again so we can continue streaming */
648 649
  GST_PAD_STREAM_UNLOCK (demux->sinkpad);

650 651 652 653 654 655 656 657
  return res;

  /* ERROR */
no_format:
  {
    GST_DEBUG_OBJECT (demux, "undefined format given, seek aborted.");
    return FALSE;
  }
658 659 660
}

static gboolean
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
661
gst_ffmpegdemux_src_event (GstPad * pad, GstEvent * event)
662
{
663 664 665
  GstFFMpegDemux *demux;
  AVStream *avstream;
  GstFFStream *stream;
666 667
  gboolean res = TRUE;

668
  if (!(stream = gst_pad_get_element_private (pad)))
669
    return FALSE;
670

671 672
  avstream = stream->avstream;
  demux = (GstFFMpegDemux *) gst_pad_get_parent (pad);
673

674 675 676
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
      res = gst_ffmpegdemux_perform_seek (demux, event);
677
      gst_event_unref (event);
678
      break;
679 680 681 682 683
    case GST_EVENT_LATENCY:
      res = gst_pad_push_event (demux->sinkpad, event);
      break;
    case GST_EVENT_NAVIGATION:
    case GST_EVENT_QOS:
684 685
    default:
      res = FALSE;
686
      gst_event_unref (event);
687 688
      break;
  }
689 690 691

  gst_object_unref (demux);

692
  return res;
693 694 695
}

static const GstQueryType *
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
696
gst_ffmpegdemux_src_query_list (GstPad * pad)
697 698
{
  static const GstQueryType src_types[] = {
699
    GST_QUERY_DURATION,
700
    GST_QUERY_POSITION,
701
    GST_QUERY_SEEKING,
702 703
    0
  };
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
704

705 706 707 708
  return src_types;
}

static gboolean
709
gst_ffmpegdemux_send_event (GstElement * element, GstEvent * event)
710 711 712 713 714 715 716 717
{
  GstFFMpegDemux *demux = (GstFFMpegDemux *) (element);
  gboolean res;

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
      GST_OBJECT_LOCK (demux);
      if (!demux->opened) {
718
        GstEvent **event_p;
719

720
        GST_DEBUG_OBJECT (demux, "caching seek event");
721 722
        event_p = &demux->seek_event;
        gst_event_replace (event_p, event);
723
        GST_OBJECT_UNLOCK (demux);
724

725 726
        res = TRUE;
      } else {
727
        GST_OBJECT_UNLOCK (demux);
728
        res = gst_ffmpegdemux_perform_seek (demux, event);
729 730 731 732 733 734 735
        gst_event_unref (event);
      }
      break;
    default:
      res = FALSE;
      break;
  }
736

737 738 739 740 741
  return res;
}

static gboolean
gst_ffmpegdemux_src_query (GstPad * pad, GstQuery * query)
742
{
743 744 745
  GstFFMpegDemux *demux;
  GstFFStream *stream;
  AVStream *avstream;
746
  gboolean res = FALSE;
747

748
  if (!(stream = gst_pad_get_element_private (pad)))
749 750
    return FALSE;

751 752 753 754
  avstream = stream->avstream;

  demux = (GstFFMpegDemux *) GST_PAD_PARENT (pad);

755
  switch (GST_QUERY_TYPE (query)) {
756
    case GST_QUERY_POSITION:
757 758
    {
      GstFormat format;
759 760
      gint64 timeposition;

761
      gst_query_parse_position (query, &format, NULL);
762 763

      timeposition = stream->last_ts;
764
      if (!(GST_CLOCK_TIME_IS_VALID (timeposition)))
765
        break;
766 767

      switch (format) {
768 769 770 771 772 773
        case GST_FORMAT_TIME:
          gst_query_set_position (query, GST_FORMAT_TIME, timeposition);
          res = TRUE;
          break;
        case GST_FORMAT_DEFAULT:
          gst_query_set_position (query, GST_FORMAT_DEFAULT,
774
              gst_util_uint64_scale (timeposition, avstream->r_frame_rate.num,
775
                  GST_SECOND * avstream->r_frame_rate.den));
776 777 778 779 780 781 782 783 784
          res = TRUE;
          break;
        case GST_FORMAT_BYTES:
          if (demux->videopads + demux->audiopads == 1 &&
              GST_PAD_PEER (demux->sinkpad) != NULL)
            res = gst_pad_query_default (pad, query);
          break;
        default:
          break;
785
      }
786
    }
787 788
      break;
    case GST_QUERY_DURATION:
789 790
    {
      GstFormat format;
791 792
      gint64 timeduration;

793
      gst_query_parse_duration (query, &format, NULL);
794 795 796

      timeduration =
          gst_ffmpeg_time_ff_to_gst (avstream->duration, avstream->time_base);
797
      if (!(GST_CLOCK_TIME_IS_VALID (timeduration))) {
798 799
        /* use duration of complete file if the stream duration is not known */
        timeduration = demux->duration;
800 801 802
        if (!(GST_CLOCK_TIME_IS_VALID (timeduration)))
          break;
      }
803 804

      switch (format) {
805 806 807 808 809 810
        case GST_FORMAT_TIME:
          gst_query_set_duration (query, GST_FORMAT_TIME, timeduration);
          res = TRUE;
          break;
        case GST_FORMAT_DEFAULT:
          gst_query_set_duration (query, GST_FORMAT_DEFAULT,
811
              gst_util_uint64_scale (timeduration, avstream->r_frame_rate.num,
812
                  GST_SECOND * avstream->r_frame_rate.den));
813 814 815 816 817 818 819 820 821
          res = TRUE;
          break;
        case GST_FORMAT_BYTES:
          if (demux->videopads + demux->audiopads == 1 &&
              GST_PAD_PEER (demux->sinkpad) != NULL)
            res = gst_pad_query_default (pad, query);
          break;
        default:
          break;
822
      }
823
    }
824
      break;
825
    case GST_QUERY_SEEKING:{
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840
      GstFormat format;
      gboolean seekable;
      gint64 dur = -1;

      gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
      seekable = demux->seekable;
      if (!gst_pad_query_duration (pad, &format, &dur)) {
        /* unlikely that we don't know duration but can seek */
        seekable = FALSE;
        dur = -1;
      }
      gst_query_set_seeking (query, format, seekable, 0, dur);
      res = TRUE;
      break;
    }
841 842 843 844
    default:
      /* FIXME : ADD GST_QUERY_CONVERT */
      res = gst_pad_query_default (pad, query);
      break;
845 846 847 848 849
  }

  return res;
}

850 851
#if 0
/* FIXME, reenable me */
852
static gboolean
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
853 854 855
gst_ffmpegdemux_src_convert (GstPad * pad,
    GstFormat src_fmt,
    gint64 src_value, GstFormat * dest_fmt, gint64 * dest_value)
856
{
857
  GstFFStream *stream;
858
  gboolean res = TRUE;
859 860 861 862
  AVStream *avstream;

  if (!(stream = gst_pad_get_element_private (pad)))
    return FALSE;
863

864 865
  avstream = stream->avstream;
  if (avstream->codec->codec_type != CODEC_TYPE_VIDEO)
866 867 868 869 870 871
    return FALSE;

  switch (src_fmt) {
    case GST_FORMAT_TIME:
      switch (*dest_fmt) {
        case GST_FORMAT_DEFAULT:
872 873 874
          *dest_value = gst_util_uint64_scale (src_value,
              avstream->r_frame_rate.num,
              GST_SECOND * avstream->r_frame_rate.den);
875 876 877 878 879 880 881 882 883
          break;
        default:
          res = FALSE;
          break;
      }
      break;
    case GST_FORMAT_DEFAULT:
      switch (*dest_fmt) {
        case GST_FORMAT_TIME:
884 885 886
          *dest_value = gst_util_uint64_scale (src_value,
              GST_SECOND * avstream->r_frame_rate.num,
              avstream->r_frame_rate.den);
887 888 889 890 891 892 893 894 895 896 897 898 899
          break;
        default:
          res = FALSE;
          break;
      }
      break;
    default:
      res = FALSE;
      break;
  }

  return res;
}
900
#endif
901

902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929
static GstFlowReturn
gst_ffmpegdemux_aggregated_flow (GstFFMpegDemux * demux)
{
  gint n;
  GstFlowReturn res = GST_FLOW_OK;
  gboolean have_ok = FALSE;

  for (n = 0; n < MAX_STREAMS; n++) {
    GstFFStream *s = demux->streams[n];

    if (s) {
      res = MIN (res, s->last_flow);

      if (GST_FLOW_IS_SUCCESS (s->last_flow))
        have_ok = TRUE;
    }
  }

  /* NOT_LINKED is OK, if at least one pad is linked */
  if (res == GST_FLOW_NOT_LINKED && have_ok)
    res = GST_FLOW_OK;

  GST_DEBUG_OBJECT (demux, "Returning aggregated value of %s",
      gst_flow_get_name (res));

  return res;
}

930 931
static GstFFStream *
gst_ffmpegdemux_get_stream (GstFFMpegDemux * demux, AVStream * avstream)
932
{
933
  GstFFMpegDemuxClass *oclass;
934 935 936 937 938
  GstPadTemplate *templ = NULL;
  GstPad *pad;
  GstCaps *caps;
  gint num;
  gchar *padname;
939
  const gchar *codec;
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958
  AVCodecContext *ctx;
  GstFFStream *stream;

  ctx = avstream->codec;

  oclass = (GstFFMpegDemuxClass *) G_OBJECT_GET_CLASS (demux);

  if (demux->streams[avstream->index] != NULL)
    goto exists;

  /* create new stream */
  stream = g_new0 (GstFFStream, 1);
  demux->streams[avstream->index] = stream;

  /* mark stream as unknown */
  stream->unknown = TRUE;
  stream->discont = TRUE;
  stream->avstream = avstream;
  stream->last_ts = GST_CLOCK_TIME_NONE;
959
  stream->last_flow = GST_FLOW_OK;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
960

961
  switch (ctx->codec_type) {
962 963 964 965 966 967 968 969 970
    case CODEC_TYPE_VIDEO:
      templ = oclass->videosrctempl;
      num = demux->videopads++;
      break;
    case CODEC_TYPE_AUDIO:
      templ = oclass->audiosrctempl;
      num = demux->audiopads++;
      break;
    default:
971
      goto unknown_type;
972
  }
973 974 975 976 977 978 979 980

  /* get caps that belongs to this stream */
  caps = gst_ffmpeg_codecid_to_caps (ctx->codec_id, ctx, TRUE);
  if (caps == NULL)
    goto unknown_caps;

  /* stream is known now */
  stream->unknown = FALSE;
981 982 983 984 985 986

  /* create new pad for this stream */
  padname = g_strdup_printf (GST_PAD_TEMPLATE_NAME_TEMPLATE (templ), num);
  pad = gst_pad_new_from_template (templ, padname);
  g_free (padname);

987
  gst_pad_use_fixed_caps (pad);
988 989 990
  gst_pad_set_caps (pad, caps);
  gst_caps_unref (caps);

991 992
  gst_pad_set_query_type_function (pad, gst_ffmpegdemux_src_query_list);
  gst_pad_set_query_function (pad, gst_ffmpegdemux_src_query);
993
  gst_pad_set_event_function (pad, gst_ffmpegdemux_src_event);
994 995

  /* store pad internally */
996 997
  stream->pad = pad;
  gst_pad_set_element_private (pad, stream);
998

999
  /* transform some useful info to GstClockTime and remember */
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
  {
    GstClockTime tmp;

    /* FIXME, actually use the start_time in some way */
    tmp = gst_ffmpeg_time_ff_to_gst (avstream->start_time, avstream->time_base);
    GST_DEBUG_OBJECT (demux, "stream %d: start time: %" GST_TIME_FORMAT,
        avstream->index, GST_TIME_ARGS (tmp));

    tmp = gst_ffmpeg_time_ff_to_gst (avstream->duration, avstream->time_base);
    GST_DEBUG_OBJECT (demux, "stream %d: duration: %" GST_TIME_FORMAT,
        avstream->index, GST_TIME_ARGS (tmp));
  }

  demux->streams[avstream->index] = stream;
1014

1015 1016
  /* activate and add */
  gst_pad_set_active (pad, TRUE);
1017 1018
  gst_element_add_pad (GST_ELEMENT (demux), pad);

1019
  /* metadata */
1020
  if ((codec = gst_ffmpeg_get_codecid_longname (ctx->codec_id))) {
1021 1022 1023
    GstTagList *list = gst_tag_list_new ();

    gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
1024
        (ctx->codec_type == CODEC_TYPE_VIDEO) ?
1025
        GST_TAG_VIDEO_CODEC : GST_TAG_AUDIO_CODEC, codec, NULL);
1026
    gst_element_found_tags_for_pad (GST_ELEMENT (demux), pad, list);
1027 1028
  }

1029 1030 1031 1032 1033
  return stream;

  /* ERRORS */
exists:
  {
1034
    GST_DEBUG_OBJECT (demux, "Pad existed (stream %d)", avstream->index);
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
    return demux->streams[avstream->index];
  }
unknown_type:
  {
    GST_WARNING_OBJECT (demux, "Unknown pad type %d", ctx->codec_type);
    return stream;
  }
unknown_caps:
  {
    GST_WARNING_OBJECT (demux, "Unknown caps for codec %d", ctx->codec_id);
    return stream;
  }
1047 1048
}

1049 1050 1051
static gchar *
my_safe_copy (gchar * input)