gstffmpegdemux.c 14 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
  gint			videopads, audiopads;
50
51
};

52
53
54
55
56
57
58
59
typedef struct _GstFFMpegDemuxClassParams {
  AVInputFormat 	*in_plugin;
  GstPadTemplate	*sinktempl;
  GstPadTemplate	*videosrctempl;
  GstPadTemplate	*audiosrctempl;
  GstPluginFeature	*typefind_feature;
} GstFFMpegDemuxClassParams;

60
61
62
63
64
65
typedef struct _GstFFMpegDemuxClass GstFFMpegDemuxClass;

struct _GstFFMpegDemuxClass {
  GstElementClass	 parent_class;

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

#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 */
};

93
static GHashTable *global_plugins, *typefind;
94
95
96
97

/* A number of functon prototypes are given so we can refer to them later. */
static void	gst_ffmpegdemux_class_init	(GstFFMpegDemuxClass *klass);
static void	gst_ffmpegdemux_init		(GstFFMpegDemux *ffmpegdemux);
98
static void	gst_ffmpegdemux_dispose		(GObject *object);
99
100
101

static void	gst_ffmpegdemux_loop		(GstElement *element);

102
103
static GstElementStateReturn
		gst_ffmpegdemux_change_state	(GstElement *element);
104
105
106
107
108
109
110
111
112
113

static GstElementClass *parent_class = NULL;

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

static void
gst_ffmpegdemux_class_init (GstFFMpegDemuxClass *klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
114
  GstFFMpegDemuxClassParams *params;
115
116
117
118
119
120

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

  parent_class = g_type_class_ref(GST_TYPE_ELEMENT);

121
122
  params = g_hash_table_lookup (global_plugins,
		GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class)));
123

124
125
126
127
128
129
130
131
  klass->in_plugin = params->in_plugin;
  klass->typefind_feature = params->typefind_feature;
  klass->videosrctempl = params->videosrctempl;
  klass->audiosrctempl = params->audiosrctempl;
  klass->sinktempl = params->sinktempl;

  gstelement_class->change_state = gst_ffmpegdemux_change_state;
  gobject_class->dispose = gst_ffmpegdemux_dispose;
132
133
134
135
136
}

static void
gst_ffmpegdemux_init(GstFFMpegDemux *ffmpegdemux)
{
137
  GstFFMpegDemuxClass *oclass = (GstFFMpegDemuxClass*)(G_OBJECT_GET_CLASS (ffmpegdemux));
138

139
140
141
142
143
144
  ffmpegdemux->sinkpad = gst_pad_new_from_template (oclass->sinktempl,
						    "sink");
  gst_element_add_pad (GST_ELEMENT (ffmpegdemux),
		       ffmpegdemux->sinkpad);
  gst_element_set_loop_function (GST_ELEMENT (ffmpegdemux),
				 gst_ffmpegdemux_loop);
145

146
  ffmpegdemux->opened = FALSE;
147

148
149
  ffmpegdemux->videopads = 0;
  ffmpegdemux->audiopads = 0;
150
151
152
}

static void
153
gst_ffmpegdemux_dispose (GObject *object)
154
{
155
156
157
158
159
160
161
162
163
164
165
166
167
168
  GstFFMpegDemux *ffmpegdemux = (GstFFMpegDemux *) object;

  if (ffmpegdemux->opened) {
    av_close_input_file (ffmpegdemux->context);
    ffmpegdemux->opened = FALSE;
  }
}

static GstCaps*
gst_ffmpegdemux_typefind (GstBuffer *buffer,
			  gpointer   priv)
{
  GstFFMpegDemuxClassParams *params;
  AVInputFormat *in_plugin;
169
  gint res = 0;
170
171
172
  gint required = AVPROBE_SCORE_MAX * 0.8; /* 80% certainty enough? */
  
  params = g_hash_table_lookup (typefind, priv);
173

174
  in_plugin = params->in_plugin;
175

176
177
  if (in_plugin->read_probe) {
    AVProbeData probe_data;
178

179
180
181
    probe_data.filename = "";
    probe_data.buf = GST_BUFFER_DATA (buffer);
    probe_data.buf_size = GST_BUFFER_SIZE (buffer);
182

183
184
185
186
187
188
189
190
191
    res = in_plugin->read_probe (&probe_data);
    if (res >= required) {
      GstCaps *caps;
      caps = GST_PAD_TEMPLATE_CAPS (params->sinktempl);
      /* make sure we still hold a refcount to this caps */
      gst_caps_ref (caps);
      return caps;
    }
  }
192
	
193
194
  return NULL;
}
195

196
197
198
199
200
static void
gst_ffmpegdemux_loop (GstElement *element)
{
  GstFFMpegDemux *ffmpegdemux = (GstFFMpegDemux *)(element);
  GstFFMpegDemuxClass *oclass = (GstFFMpegDemuxClass*)(G_OBJECT_GET_CLASS (ffmpegdemux));
201

202
203
204
205
206
207
208
209
210
  gint res;
  AVPacket pkt;
  AVFormatContext *ct;
  AVStream *st;
  GstPad *pad;

  /* open file if we didn't so already */
  if (!ffmpegdemux->opened) {
    res = av_open_input_file (&ffmpegdemux->context, 
211
			      g_strdup_printf ("gstreamer://%p",
212
213
214
					       ffmpegdemux->sinkpad),
			      oclass->in_plugin, 0, NULL);
    if (res < 0) {
215
216
217
      gst_element_gerror(GST_ELEMENT (ffmpegdemux), GST_ERROR_UNKNOWN,
        g_strdup ("unconverted error, file a bug"),
        g_strdup_printf ("Failed to open demuxer/file context"));
218
219
      return;
    }
220

221
222
    ffmpegdemux->opened = TRUE;
  }
223

224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
  /* shortcut to context */
  ct = ffmpegdemux->context;

  /* read a package */
  res = av_read_packet (ct, &pkt);
  if (res < 0) {
    if (url_feof (&ct->pb)) {
      int i;

      /* we're at the end of file - send an EOS to
       * each stream that we opened so far */
      for (i = 0; i < ct->nb_streams; i++) {
        GstPad *pad;
        GstEvent *event = gst_event_new (GST_EVENT_EOS);

        pad = ffmpegdemux->srcpads[i];
        if (GST_PAD_IS_USABLE (pad)) {
          gst_data_ref (GST_DATA (event));
          gst_pad_push (pad, GST_BUFFER (event));
        }
        gst_data_unref (GST_DATA (event));
245
246
      }
      gst_element_set_eos (element);
247
248
249
250
251
252
253

      /* FIXME: should we go into
       * should we close the context here?
       * either way, a new media stream needs an
       * event too */
    }
    return;
254
255
  }

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
  /* shortcut to stream */
  st = ct->streams[pkt.stream_index];

  /* create the pad/stream if we didn't do so already */
  if (st->codec_info_state == 0) {
    GstPadTemplate *templ = NULL;
    GstCaps *caps;
    gchar *padname;
    gint num;

    /* mark as handled */	
    st->codec_info_state = 1;

    /* find template */
    switch (st->codec.codec_type) {
      case CODEC_TYPE_VIDEO:
        templ = oclass->videosrctempl;
        num = ffmpegdemux->videopads++;
        break;
      case CODEC_TYPE_AUDIO:
        templ = oclass->audiosrctempl;
        num = ffmpegdemux->audiopads++;
        break;
      default:
        g_warning ("Unknown pad type %d",
		   st->codec.codec_type);
        return;
    }

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

    /* FIXME: convert() and query() functions for pad */

    /* store pad internally */
    ffmpegdemux->srcpads[pkt.stream_index] = pad;
    gst_element_add_pad (GST_ELEMENT (ffmpegdemux), pad);

    /* get caps that belongs to this stream */
    caps = gst_ffmpeg_codecid_to_caps (st->codec.codec_id,
				       &st->codec);
    if (gst_pad_try_set_caps (pad, caps) <= 0) {
301
      GST_DEBUG (
302
303
304
305
306
		 "Failed to set caps from ffdemuxer on next element");
      /* we continue here, in the next pad-is-usable check,
       * we'll return nonetheless */
    }
  }
307

308
309
  /* shortcut to pad belonging to this stream */
  pad = ffmpegdemux->srcpads[pkt.stream_index];
310

311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
  /* and handle the data by pushing it forward... */
  if (GST_PAD_IS_USABLE (pad)) {
    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;

    if (pkt.pts != AV_NOPTS_VALUE && ct->pts_den) {
      GST_BUFFER_TIMESTAMP (outbuf) = pkt.pts * GST_SECOND *
					ct->pts_num / ct->pts_den;
    }

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

    gst_pad_push (pad, outbuf);
    pkt.destruct (&pkt);
330
331
332
  }
}

333
334
static GstElementStateReturn
gst_ffmpegdemux_change_state (GstElement *element)
335
{
336
337
  GstFFMpegDemux *ffmpegdemux = (GstFFMpegDemux *)(element);
  gint transition = GST_STATE_TRANSITION (element);
338

339
340
341
342
343
344
  switch (transition) {
    case GST_STATE_PAUSED_TO_READY:
      if (ffmpegdemux->opened) {
        av_close_input_file (ffmpegdemux->context);
        ffmpegdemux->opened = FALSE;
      }
345
346
      break;
  }
347
348
349
350
351

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

  return GST_STATE_SUCCESS;
352
353
354
355
356
357
}

gboolean
gst_ffmpegdemux_register (GstPlugin *plugin)
{
  GstElementFactory *factory;
358
359
  GstTypeFactory *type_factory;
  GstTypeDefinition *type_definition;
360
361
362
363
364
365
366
367
368
369
370
371
372
373
  GTypeInfo typeinfo = {
    sizeof(GstFFMpegDemuxClass),      
    NULL,
    NULL,
    (GClassInitFunc)gst_ffmpegdemux_class_init,
    NULL,
    NULL,
    sizeof(GstFFMpegDemux),
    0,
    (GInstanceInitFunc)gst_ffmpegdemux_init,
  };
  GType type;
  GstElementDetails *details;
  AVInputFormat *in_plugin;
374
375
  GstFFMpegDemuxClassParams *params;
  AVCodec *in_codec;
376
377
378
379
  
  in_plugin = first_iformat;

  global_plugins = g_hash_table_new (NULL, NULL);
380
  typefind = g_hash_table_new (NULL, NULL);
381
382
383
384

  while (in_plugin) {
    gchar *type_name;
    gchar *p;
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
    GstCaps *sinkcaps, *audiosrccaps, *videosrccaps;

    /* Try to find the caps that belongs here */
    sinkcaps = gst_ffmpeg_formatid_to_caps (in_plugin->name);
    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 */
    audiosrccaps = NULL;
    videosrccaps = NULL;
    for (in_codec = first_avcodec; in_codec != NULL;
	 in_codec = in_codec->next) {
      GstCaps *temp = gst_ffmpeg_codecid_to_caps (in_codec->id, NULL);
      if (!temp) {
        continue;
      }
      switch (in_codec->type) {
        case CODEC_TYPE_VIDEO:
          videosrccaps = gst_caps_append (videosrccaps, temp);
          break;
        case CODEC_TYPE_AUDIO:
          audiosrccaps = gst_caps_append (audiosrccaps, temp);
          break;
        default:
          gst_caps_unref (temp);
          break;
      }
    }
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431

    /* construct the type */
    type_name = g_strdup_printf("ffdemux_%s", in_plugin->name);

    p = type_name;

    while (*p) {
      if (*p == '.') *p = '_';
      p++;
    }

    /* if it's already registered, drop it */
    if (g_type_from_name(type_name)) {
      g_free(type_name);
      goto next;
    }

432
    /* create the type now */
433
434
435
    type = g_type_register_static(GST_TYPE_ELEMENT, type_name , &typeinfo, 0);

    /* construct the element details struct */
436
437
438
439
440
441
442
    details = g_new0 (GstElementDetails, 1);
    details->longname = g_strdup (in_plugin->long_name);
    details->klass = g_strdup ("Codec/Demuxer");
    details->license = g_strdup ("LGPL");
    details->description = g_strdup_printf ("FFMPEG %s demuxer",
					    in_plugin->name);
    details->version = g_strdup (VERSION);
443
444
    details->author = g_strdup ("The FFMPEG crew, "
				"Wim Taymans <wim.taymans@chello.be>, "
445
446
				"Ronald Bultje <rbultje@ronald.bitfreak.net>");
    details->copyright = g_strdup ("(c) 2002-2003");
447
448
449
450
451

    /* register the plugin with gstreamer */
    factory = gst_element_factory_new(type_name,type,details);
    g_return_val_if_fail(factory != NULL, FALSE);

452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
    /* typefind info */
    type_definition = g_new0 (GstTypeDefinition, 1);
    type_definition->name = g_strdup_printf ("fftype_%s",
					     in_plugin->name);
    type_definition->mime = g_strdup (gst_caps_get_mime (sinkcaps));
    type_definition->exts = g_strdup (in_plugin->extensions);
    type_definition->typefindfunc = gst_ffmpegdemux_typefind;

    type_factory = gst_type_factory_new (type_definition);

    /* create a cache for these properties */
    params = g_new0 (GstFFMpegDemuxClassParams, 1);
    params->in_plugin = in_plugin;
    params->typefind_feature = GST_PLUGIN_FEATURE (type_factory);
    params->sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
					      GST_PAD_ALWAYS,
					      sinkcaps, NULL);
    gst_element_factory_add_pad_template (factory,
					  params->sinktempl);
    params->audiosrctempl = gst_pad_template_new ("audio_%02d",
						  GST_PAD_SRC,
						  GST_PAD_SOMETIMES,
						  audiosrccaps, NULL);
    gst_element_factory_add_pad_template (factory,
					  params->audiosrctempl);
    params->videosrctempl = gst_pad_template_new ("video_%02d",
						  GST_PAD_SRC,
						  GST_PAD_SOMETIMES,
						  videosrccaps, NULL);
    gst_element_factory_add_pad_template (factory,
					  params->videosrctempl);

    g_hash_table_insert (global_plugins, 
		         GINT_TO_POINTER (type), 
			 (gpointer) params);
487

488
489
490
    g_hash_table_insert (typefind,
			 (gpointer) type_factory,
			 (gpointer) params);
491

492
    gst_element_factory_set_rank (factory, GST_ELEMENT_RANK_MARGINAL);
493
494
495

    /* The very last thing is to register the elementfactory with the plugin. */
    gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
496
    gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type_factory));
497
498
499
500
501
502
503

next:
    in_plugin = in_plugin->next;
  }

  return TRUE;
}