gstffmpegdemux.c 20.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
 *
 * 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.
 */

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

#include <string.h>
25 26 27 28 29 30 31
#ifdef HAVE_FFMPEG_UNINSTALLED
#include <avformat.h>
#include <avi.h>
#else
#include <ffmpeg/avformat.h>
#include <ffmpeg/avi.h>
#endif
32 33 34

#include <gst/gst.h>

35
#include "gstffmpegcodecmap.h"
36 37 38 39 40 41 42 43 44 45

typedef struct _GstFFMpegDemux GstFFMpegDemux;

struct _GstFFMpegDemux {
  GstElement 		element;

  /* We need to keep track of our pads, so we do so here. */
  GstPad 		*sinkpad;

  AVFormatContext 	*context;
46
  gboolean		opened;
47 48

  GstPad		*srcpads[MAX_STREAMS];
49 50
  gboolean		handled[MAX_STREAMS];
  guint64		last_ts[MAX_STREAMS];
51
  gint			videopads, audiopads;
52 53
};

54 55
typedef struct _GstFFMpegDemuxClassParams {
  AVInputFormat 	*in_plugin;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
56
  GstCaps		*sinkcaps, *videosrccaps, *audiosrccaps;
57 58
} GstFFMpegDemuxClassParams;

59 60 61 62 63 64
typedef struct _GstFFMpegDemuxClass GstFFMpegDemuxClass;

struct _GstFFMpegDemuxClass {
  GstElementClass	 parent_class;

  AVInputFormat 	*in_plugin;
65 66 67
  GstPadTemplate	*sinktempl;
  GstPadTemplate	*videosrctempl;
  GstPadTemplate	*audiosrctempl;
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
};

#define GST_TYPE_FFMPEGDEC \
  (gst_ffmpegdec_get_type())
#define GST_FFMPEGDEC(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGDEC,GstFFMpegDemux))
#define GST_FFMPEGDEC_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGDEC,GstFFMpegDemuxClass))
#define GST_IS_FFMPEGDEC(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGDEC))
#define GST_IS_FFMPEGDEC_CLASS(obj) \
  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGDEC))

enum {
  /* FILL ME */
  LAST_SIGNAL
};

enum {
  ARG_0,
  /* FILL ME */
};

91
static GHashTable *global_plugins;
92 93 94

/* A number of functon prototypes are given so we can refer to them later. */
static void	gst_ffmpegdemux_class_init	(GstFFMpegDemuxClass *klass);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
95
static void	gst_ffmpegdemux_base_init	(GstFFMpegDemuxClass *klass);
96 97
static void	gst_ffmpegdemux_init		(GstFFMpegDemux *demux);
static void	gst_ffmpegdemux_dispose		(GObject    *object);
98 99 100

static void	gst_ffmpegdemux_loop		(GstElement *element);

101 102
static GstElementStateReturn
		gst_ffmpegdemux_change_state	(GstElement *element);
103 104 105 106 107

static GstElementClass *parent_class = NULL;

/*static guint gst_ffmpegdemux_signals[LAST_SIGNAL] = { 0 }; */

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
static const gchar *
gst_ffmpegdemux_averror (gint av_errno)
{
  const gchar *message = NULL;

  switch (av_errno) {
    default:
    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_INVALIDDATA:
      message = "Invalid data found";
      break;
    case AVERROR_NOMEM:
      message = "Not enough memory";
      break;
    case AVERROR_NOFMT:
      message = "Unknown format";
      break;
    case AVERROR_NOTSUPP:
      message = "Operation not supported";
      break;
  }

  return message;
}

Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
141 142 143 144 145 146
static void
gst_ffmpegdemux_base_init (GstFFMpegDemuxClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstFFMpegDemuxClassParams *params;
Benjamin Otte's avatar
Benjamin Otte committed
147
  GstElementDetails details;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
148 149 150 151
  GstPadTemplate *sinktempl, *audiosrctempl, *videosrctempl;

  params = g_hash_table_lookup (global_plugins,
		GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class)));
152 153 154 155
  if (!params)
    params = g_hash_table_lookup (global_plugins,
		GINT_TO_POINTER (0));
  g_assert (params);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
156 157

  /* construct the element details struct */
Benjamin Otte's avatar
Benjamin Otte committed
158
  details.longname = g_strdup_printf("FFMPEG %s demuxer",
159 160
				      params->in_plugin->long_name);
  details.klass = "Codec/Demuxer";
Benjamin Otte's avatar
Benjamin Otte committed
161
  details.description = g_strdup_printf("FFMPEG %s decoder",
162
					 params->in_plugin->long_name);
Benjamin Otte's avatar
Benjamin Otte committed
163 164 165 166 167
  details.author = "Wim Taymans <wim.taymans@chello.be>, "
		   "Ronald Bultje <rbultje@ronald.bitfreak.net>";
  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
168 169 170 171 172

  /* pad templates */
  sinktempl = gst_pad_template_new ("sink",
				    GST_PAD_SINK,
				    GST_PAD_ALWAYS,
David Schleef's avatar
David Schleef committed
173
				    params->sinkcaps);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
174 175 176
  videosrctempl = gst_pad_template_new ("video_%02d",
					GST_PAD_SRC,
					GST_PAD_SOMETIMES,
David Schleef's avatar
David Schleef committed
177
					params->videosrccaps);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
178 179 180
  audiosrctempl = gst_pad_template_new ("audio_%02d",
					GST_PAD_SRC,
					GST_PAD_SOMETIMES,
David Schleef's avatar
David Schleef committed
181
					params->audiosrccaps);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
182 183 184 185 186 187 188 189 190 191 192

  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;
}

193 194 195 196 197 198 199 200 201 202 203
static void
gst_ffmpegdemux_class_init (GstFFMpegDemuxClass *klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

  gobject_class = (GObjectClass*)klass;
  gstelement_class = (GstElementClass*)klass;

  parent_class = g_type_class_ref(GST_TYPE_ELEMENT);

204 205
  gstelement_class->change_state = gst_ffmpegdemux_change_state;
  gobject_class->dispose = gst_ffmpegdemux_dispose;
206 207 208
}

static void
209
gst_ffmpegdemux_init (GstFFMpegDemux *demux)
210
{
211
  GstFFMpegDemuxClass *oclass = (GstFFMpegDemuxClass *) (G_OBJECT_GET_CLASS (demux));
212

213
  demux->sinkpad = gst_pad_new_from_template (oclass->sinktempl,
214
						    "sink");
215 216
  gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
  gst_element_set_loop_function (GST_ELEMENT (demux),
217
				 gst_ffmpegdemux_loop);
218

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
  demux->opened = FALSE;

  memset (demux->srcpads, 0, sizeof (demux->srcpads));
  memset (demux->handled, FALSE, sizeof (demux->handled));
  memset (demux->last_ts, 0, sizeof (demux->last_ts));
  demux->videopads = 0;
  demux->audiopads = 0;
}

static void
gst_ffmpegdemux_close (GstFFMpegDemux *demux)
{
  gint n;

  if (!demux->opened)
    return;

  /* remove pads from ourselves */
  for (n = 0; n < MAX_STREAMS; n++) {
    if (demux->srcpads[n]) {
      gst_element_remove_pad (GST_ELEMENT (demux), demux->srcpads[n]);
      demux->srcpads[n] = NULL;
    }
    demux->handled[n] = FALSE;
    demux->last_ts[n] = 0;
  }
  demux->videopads = 0;
  demux->audiopads = 0;

  /* close demuxer context from ffmpeg */
  av_close_input_file (demux->context);
250

251
  demux->opened = FALSE;
252 253 254
}

static void
255
gst_ffmpegdemux_dispose (GObject *object)
256
{
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
  GstFFMpegDemux *demux = (GstFFMpegDemux *) demux;

  gst_ffmpegdemux_close (demux);
}

static AVStream *
gst_ffmpegdemux_stream_from_pad (GstPad *pad)
{
  GstFFMpegDemux *demux = (GstFFMpegDemux *) gst_pad_get_parent (pad);
  AVStream *stream = NULL;
  gint n;

  for (n = 0; n < MAX_STREAMS; n++) {
    if (demux->srcpads[n] == pad) {
      stream = demux->context->streams[n];
      break;
    }
  }

  return stream;
}

static const GstEventMask *
gst_ffmpegdemux_src_event_mask (GstPad *pad)
{
  static const GstEventMask masks[] = {
    { GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT },
    { 0, }
  };
                                                                                
  return masks;
}

static gboolean
gst_ffmpegdemux_src_event (GstPad   *pad,
			   GstEvent *event)
{
  GstFFMpegDemux *demux = (GstFFMpegDemux *) gst_pad_get_parent (pad);
  AVStream *stream = gst_ffmpegdemux_stream_from_pad (pad);
  gboolean res = TRUE;
  gint64 offset;

  if (!stream)
    return;

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
      offset = GST_EVENT_SEEK_OFFSET (event);
      switch (GST_EVENT_SEEK_FORMAT (event)) {
        case GST_FORMAT_DEFAULT:
          if (stream->codec.codec_type != CODEC_TYPE_VIDEO) {
            res = FALSE;
            break;
          } else {
            GstFormat fmt = GST_FORMAT_TIME;
            if (!(res = gst_pad_convert (pad, GST_FORMAT_DEFAULT, offset,
					 &fmt, &offset)))
              break;
          }
          /* fall-through */
        case GST_FORMAT_TIME:
          if (av_seek_frame (demux->context, stream->index,
			     offset / (GST_SECOND / AV_TIME_BASE)))
            res = FALSE;
          break;
        default:
          res = FALSE;
          break;
      }
      break;
    default:
      res = FALSE;
      break;
  }

  return res;
}

static const GstFormat *
gst_ffmpegdemux_src_format_list (GstPad *pad)
{
  AVStream *stream = gst_ffmpegdemux_stream_from_pad (pad);
  static const GstFormat src_v_formats[] = {
    GST_FORMAT_TIME,
    GST_FORMAT_DEFAULT,
    0
  }, src_a_formats[] = {
    GST_FORMAT_TIME,
    0
  };

  return (stream->codec.codec_type == CODEC_TYPE_VIDEO) ?
	src_v_formats : src_a_formats;
}

static const GstQueryType *
gst_ffmpegdemux_src_query_list (GstPad *pad)
{
  static const GstQueryType src_types[] = {
    GST_QUERY_TOTAL,
    GST_QUERY_POSITION,
    0
  };
                                                                                
  return src_types;
}

static gboolean
gst_ffmpegdemux_src_query (GstPad      *pad,
			   GstQueryType type,
			   GstFormat   *fmt,
			   gint64      *value)
{
  GstFFMpegDemux *demux = (GstFFMpegDemux *) gst_pad_get_parent (pad);
  AVStream *stream = gst_ffmpegdemux_stream_from_pad (pad);
  gboolean res = TRUE;
  gint n;

  if (!stream || (*fmt == GST_FORMAT_DEFAULT &&
		  stream->codec.codec_type != CODEC_TYPE_VIDEO))
    return FALSE;

  switch (type) {
    case GST_QUERY_TOTAL:
      switch (*fmt) {
        case GST_FORMAT_TIME:
          *value = stream->duration * (GST_SECOND / AV_TIME_BASE);
          break;
        case GST_FORMAT_DEFAULT:
          if (stream->codec_info_nb_frames) {
            *value = stream->codec_info_nb_frames;
            break;
          } /* else fall-through */
        default:
          res = FALSE;
          break;
      }
      break;
    case GST_QUERY_POSITION:
      switch (*fmt) {
        case GST_FORMAT_TIME:
          *value = demux->last_ts[stream->index];
          break;
        case GST_FORMAT_DEFAULT:
          res = gst_pad_convert (pad, GST_FORMAT_TIME,
				 demux->last_ts[stream->index],
				 fmt, value);
          break;
        default:
          res = FALSE;
          break;
      }
      break;
    default:
      res = FALSE;
      break;
  }

  return res;
}

static gboolean
gst_ffmpegdemux_src_convert (GstPad    *pad,
			     GstFormat  src_fmt,
			     gint64     src_value,
			     GstFormat *dest_fmt,
			     gint64    *dest_value)
{
  GstFFMpegDemux *demux = (GstFFMpegDemux *) gst_pad_get_parent (pad);
  AVStream *stream = gst_ffmpegdemux_stream_from_pad (pad);
  gboolean res = TRUE;

  if (!stream || stream->codec.codec_type != CODEC_TYPE_VIDEO)
    return FALSE;

  switch (src_fmt) {
    case GST_FORMAT_TIME:
      switch (*dest_fmt) {
        case GST_FORMAT_DEFAULT:
          *dest_value = src_value * stream->r_frame_rate /
			(GST_SECOND * stream->r_frame_rate_base);
          break;
        default:
          res = FALSE;
          break;
      }
      break;
    case GST_FORMAT_DEFAULT:
      switch (*dest_fmt) {
        case GST_FORMAT_TIME:
          *dest_value = src_value * GST_SECOND * stream->r_frame_rate_base /
			stream->r_frame_rate;
          break;
        default:
          res = FALSE;
          break;
      }
      break;
    default:
      res = FALSE;
      break;
  }

  return res;
}
462

463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
static gboolean
gst_ffmpegdemux_add (GstFFMpegDemux *demux,
		     AVStream       *stream)
{
  GstFFMpegDemuxClass *oclass = (GstFFMpegDemuxClass *) G_OBJECT_GET_CLASS (demux);
  GstPadTemplate *templ = NULL;
  GstPad *pad;
  GstCaps *caps;
  gint num;
  gchar *padname;
                                                                                
  switch (stream->codec.codec_type) {
    case CODEC_TYPE_VIDEO:
      templ = oclass->videosrctempl;
      num = demux->videopads++;
      break;
    case CODEC_TYPE_AUDIO:
      templ = oclass->audiosrctempl;
      num = demux->audiopads++;
      break;
    default:
      GST_WARNING ("Unknown pad type %d", stream->codec.codec_type);
      break;
486
  }
487 488 489 490 491 492 493 494 495
  if (!templ)
    return FALSE;

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

  gst_pad_use_explicit_caps (pad);
496 497 498 499 500 501
  gst_pad_set_formats_function (pad, gst_ffmpegdemux_src_format_list);
  gst_pad_set_event_mask_function (pad, gst_ffmpegdemux_src_event_mask);
  gst_pad_set_event_function (pad, gst_ffmpegdemux_src_event);
  gst_pad_set_query_type_function (pad, gst_ffmpegdemux_src_query_list);
  gst_pad_set_query_function (pad, gst_ffmpegdemux_src_query);
  gst_pad_set_convert_function (pad, gst_ffmpegdemux_src_convert);
502 503 504 505 506

  /* store pad internally */
  demux->srcpads[stream->index] = pad;

  /* get caps that belongs to this stream */
507
  caps = gst_ffmpeg_codecid_to_caps (stream->codec.codec_id, &stream->codec, TRUE);
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
  gst_pad_set_explicit_caps (pad, caps);

  gst_element_add_pad (GST_ELEMENT (demux), pad);

  return TRUE;
}

static gboolean
gst_ffmpegdemux_open (GstFFMpegDemux *demux)
{
  GstFFMpegDemuxClass *oclass = (GstFFMpegDemuxClass *) G_OBJECT_GET_CLASS (demux);
  gchar *location;
  gint res;

  /* to be sure... */
  gst_ffmpegdemux_close (demux);

  /* open via our input protocol hack */
  location = g_strdup_printf ("gstreamer://%p", demux->sinkpad);
  res = av_open_input_file (&demux->context, location,
			    oclass->in_plugin, 0, NULL);
  g_free (location);
  if (res < 0) {
    GST_ELEMENT_ERROR (demux, LIBRARY, FAILED, (NULL),
		       (gst_ffmpegdemux_averror (res)));
    return FALSE;
  }

  /* open_input_file() automatically reads the header. We can now map each
   * created AVStream to a GstPad to make GStreamer handle it. */
  for (res = 0; res < demux->context->nb_streams; res++) {
    gst_ffmpegdemux_add (demux, demux->context->streams[res]);
    demux->handled[res] = TRUE;
  }

  demux->opened = TRUE;

  return TRUE;
546 547
}

548 549 550
#define GST_FFMPEG_TYPE_FIND_SIZE 4096
static void
gst_ffmpegdemux_type_find (GstTypeFind *tf, gpointer priv)
551
{
552 553 554
  guint8 *data;
  GstFFMpegDemuxClassParams *params = (GstFFMpegDemuxClassParams *) priv;
  AVInputFormat *in_plugin = params->in_plugin;
555
  gint res = 0;
556
  
557
  if (in_plugin->read_probe &&
558
      (data = gst_type_find_peek (tf, 0, GST_FFMPEG_TYPE_FIND_SIZE)) != NULL) {
559
    AVProbeData probe_data;
560

561
    probe_data.filename = "";
562 563
    probe_data.buf = data;
    probe_data.buf_size = GST_FFMPEG_TYPE_FIND_SIZE;
564

565
    res = in_plugin->read_probe (&probe_data);
566
    res = res * GST_TYPE_FIND_MAXIMUM / AVPROBE_SCORE_MAX;
David Schleef's avatar
David Schleef committed
567 568
    if (res > 0) 
      gst_type_find_suggest (tf, res, params->sinkcaps);
569 570
  }
}
571

572 573 574
static void
gst_ffmpegdemux_loop (GstElement *element)
{
575
  GstFFMpegDemux *demux = (GstFFMpegDemux *)(element);
576 577 578 579 580
  gint res;
  AVPacket pkt;
  GstPad *pad;

  /* open file if we didn't so already */
581 582
  if (!demux->opened) {
    if (!gst_ffmpegdemux_open (demux))
583 584
      return;
  }
585

586
  /* read a package */
587
  res = av_read_packet (demux->context, &pkt);
588
  if (res < 0) {
589 590 591 592 593 594
    if (url_feof (&demux->context->pb)) {
      gst_pad_event_default (demux->sinkpad, gst_event_new (GST_EVENT_EOS));
      gst_ffmpegdemux_close (demux);
    } else {
      GST_ELEMENT_ERROR (demux, LIBRARY, FAILED, (NULL),
			 (gst_ffmpegdemux_averror (res)));
595 596
    }
    return;
597 598
  }

599 600 601 602
  /* for stream-generation-while-playing */
  if (!demux->handled[pkt.stream_index]) {
    gst_ffmpegdemux_add (demux, demux->context->streams[pkt.stream_index]);
    demux->handled[pkt.stream_index] = TRUE;
603
  }
604

605
  /* shortcut to pad belonging to this stream */
606
  pad = demux->srcpads[pkt.stream_index];
607

608
  /* and handle the data by pushing it forward... */
609
  if (pad && GST_PAD_IS_USABLE (pad)) {
610 611 612 613 614 615
    GstBuffer *outbuf;

    outbuf = gst_buffer_new_and_alloc (pkt.size);
    memcpy (GST_BUFFER_DATA (outbuf), pkt.data, pkt.size);
    GST_BUFFER_SIZE (outbuf) = pkt.size;

616 617 618
    if (pkt.pts != AV_NOPTS_VALUE && demux->context->pts_den)
      GST_BUFFER_TIMESTAMP (outbuf) = (double) pkt.pts * GST_SECOND *
			demux->context->pts_num / demux->context->pts_den;
619 620 621 622 623

    if (pkt.flags & PKT_FLAG_KEY) {
      GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_KEY_UNIT);
    }

624
    gst_pad_push (pad, GST_DATA (outbuf));
625
  }
626 627

  pkt.destruct (&pkt);
628 629
}

630 631
static GstElementStateReturn
gst_ffmpegdemux_change_state (GstElement *element)
632
{
633
  GstFFMpegDemux *demux = (GstFFMpegDemux *)(element);
634
  gint transition = GST_STATE_TRANSITION (element);
635

636 637
  switch (transition) {
    case GST_STATE_PAUSED_TO_READY:
638
      gst_ffmpegdemux_close (demux);
639 640
      break;
  }
641 642 643 644 645

  if (GST_ELEMENT_CLASS (parent_class)->change_state)
    return GST_ELEMENT_CLASS (parent_class)->change_state (element);

  return GST_STATE_SUCCESS;
646 647 648 649 650
}

gboolean
gst_ffmpegdemux_register (GstPlugin *plugin)
{
David Schleef's avatar
David Schleef committed
651 652 653 654 655
  GType type;
  AVInputFormat *in_plugin;
  GstFFMpegDemuxClassParams *params;
  AVCodec *in_codec;
  gchar **extensions;
656 657
  GTypeInfo typeinfo = {
    sizeof(GstFFMpegDemuxClass),      
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
658
    (GBaseInitFunc)gst_ffmpegdemux_base_init,
659 660 661 662 663 664 665 666 667 668 669 670 671 672
    NULL,
    (GClassInitFunc)gst_ffmpegdemux_class_init,
    NULL,
    NULL,
    sizeof(GstFFMpegDemux),
    0,
    (GInstanceInitFunc)gst_ffmpegdemux_init,
  };
  
  in_plugin = first_iformat;

  global_plugins = g_hash_table_new (NULL, NULL);

  while (in_plugin) {
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
673
    gchar *type_name, *typefind_name;
674
    gchar *p, *name = NULL;
675 676
    GstCaps *sinkcaps, *audiosrccaps, *videosrccaps;

677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
    /* no emulators */
    if (!strncmp (in_plugin->long_name, "raw ", 4) ||
        !strncmp (in_plugin->long_name, "pcm ", 4) ||
        !strcmp (in_plugin->name, "audio_device") ||
        !strncmp (in_plugin->name, "image", 5) ||
        !strcmp (in_plugin->name, "mpegvideo") ||
        !strcmp (in_plugin->name, "mjpeg"))
      goto next;

    p = name = g_strdup (in_plugin->name);
    while (*p) {
      if (*p == '.' || *p == ',') *p = '_';
      p++;
    }

692
    /* Try to find the caps that belongs here */
693
    sinkcaps = gst_ffmpeg_formatid_to_caps (name);
694 695 696 697 698 699
    if (!sinkcaps) {
      goto next;
    }
    /* This is a bit ugly, but we just take all formats
     * for the pad template. We'll get an exact match
     * when we open the stream */
700 701
    audiosrccaps = gst_caps_new_empty ();
    videosrccaps = gst_caps_new_empty ();
702 703
    for (in_codec = first_avcodec; in_codec != NULL;
	 in_codec = in_codec->next) {
704
      GstCaps *temp = gst_ffmpeg_codecid_to_caps (in_codec->id, NULL, TRUE);
705 706 707 708 709
      if (!temp) {
        continue;
      }
      switch (in_codec->type) {
        case CODEC_TYPE_VIDEO:
David Schleef's avatar
David Schleef committed
710
          gst_caps_append (videosrccaps, temp);
711 712
          break;
        case CODEC_TYPE_AUDIO:
David Schleef's avatar
David Schleef committed
713
          gst_caps_append (audiosrccaps, temp);
714 715
          break;
        default:
David Schleef's avatar
David Schleef committed
716
          gst_caps_free (temp);
717 718 719
          break;
      }
    }
720 721

    /* construct the type */
722
    type_name = g_strdup_printf("ffdemux_%s", name);
723 724

    /* if it's already registered, drop it */
725 726
    if (g_type_from_name (type_name)) {
      g_free (type_name);
727 728
      goto next;
    }
729 730

    typefind_name = g_strdup_printf("fftype_%s", name);
731
    
732 733 734
    /* create a cache for these properties */
    params = g_new0 (GstFFMpegDemuxClassParams, 1);
    params->in_plugin = in_plugin;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
735 736 737
    params->sinkcaps = sinkcaps;
    params->videosrccaps = videosrccaps;
    params->audiosrccaps = audiosrccaps;
738

739 740 741 742 743
    g_hash_table_insert (global_plugins, 
		         GINT_TO_POINTER (0), 
			 (gpointer) params);

    /* create the type now */
744
    type = g_type_register_static (GST_TYPE_ELEMENT, type_name , &typeinfo, 0);
745

746 747 748
    g_hash_table_insert (global_plugins, 
		         GINT_TO_POINTER (type), 
			 (gpointer) params);
749

750 751 752 753 754
    if (in_plugin->extensions)
      extensions = g_strsplit (in_plugin->extensions, " ", 0);
    else
      extensions = NULL;

Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
755 756 757
    if (!gst_element_register (plugin, type_name, GST_RANK_MARGINAL, type) ||
        !gst_type_find_register (plugin, typefind_name, GST_RANK_MARGINAL,
				 gst_ffmpegdemux_type_find,
758 759
				 extensions, sinkcaps, params)) {
      g_warning ("Register of type ffdemux_%s failed", name);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
760
      return FALSE;
761 762 763 764
    }

    if (extensions)
      g_strfreev (extensions);
765 766

next:
767
    g_free (name);
768 769
    in_plugin = in_plugin->next;
  }
770
  g_hash_table_remove (global_plugins, GINT_TO_POINTER (0));
771 772 773

  return TRUE;
}