2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
25 #ifdef HAVE_FFMPEG_UNINSTALLED
28 #include <libavformat/avformat.h>
32 #include <gst/base/gstcollectpads.h>
34 #include "gstffmpeg.h"
35 #include "gstffmpegcodecmap.h"
36 #include "gstffmpegutils.h"
40 #ifndef GST_EXT_FFMUX_ENHANCEMENT
41 #define GST_EXT_FFMUX_ENHANCEMENT
42 #endif /* GST_EXT_FFMUX_ENHANCEMENT */
44 typedef struct _GstFFMpegMux GstFFMpegMux;
45 typedef struct _GstFFMpegMuxPad GstFFMpegMuxPad;
47 struct _GstFFMpegMuxPad
49 GstCollectData collect; /* we extend the CollectData */
58 GstCollectPads *collect;
59 /* We need to keep track of our pads, so we do so here. */
62 AVFormatContext *context;
65 gint videopads, audiopads;
66 #ifdef GST_EXT_FFMUX_ENHANCEMENT
67 guint expected_trailer_size, nb_video_frames, nb_audio_frames;
68 #endif /* GST_EXT_FFMUX_ENHANCEMENT */
71 /* event_function is the collectpads default eventfunction */
72 GstPadEventFunction event_function;
77 typedef struct _GstFFMpegMuxClass GstFFMpegMuxClass;
79 struct _GstFFMpegMuxClass
81 GstElementClass parent_class;
83 AVOutputFormat *in_plugin;
86 #define GST_TYPE_FFMPEGMUX \
87 (gst_ffmpegdec_get_type())
88 #define GST_FFMPEGMUX(obj) \
89 (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGMUX,GstFFMpegMux))
90 #define GST_FFMPEGMUX_CLASS(klass) \
91 (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGMUX,GstFFMpegMuxClass))
92 #define GST_IS_FFMPEGMUX(obj) \
93 (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGMUX))
94 #define GST_IS_FFMPEGMUX_CLASS(klass) \
95 (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGMUX))
114 #ifdef GST_EXT_FFMUX_ENHANCEMENT
115 PROP_EXPECTED_TRAILER_SIZE,
116 PROP_NUMBER_VIDEO_FRAMES,
117 PROP_NUMBER_AUDIO_FRAMES,
118 #endif /* GST_EXT_FFMUX_ENHANCEMENT */
121 /* A number of function prototypes are given so we can refer to them later. */
122 static void gst_ffmpegmux_class_init (GstFFMpegMuxClass * klass);
123 static void gst_ffmpegmux_base_init (gpointer g_class);
124 static void gst_ffmpegmux_init (GstFFMpegMux * ffmpegmux,
125 GstFFMpegMuxClass * g_class);
126 static void gst_ffmpegmux_finalize (GObject * object);
128 static gboolean gst_ffmpegmux_setcaps (GstPad * pad, GstCaps * caps);
129 static GstPad *gst_ffmpegmux_request_new_pad (GstElement * element,
130 GstPadTemplate * templ, const gchar * name);
131 static GstFlowReturn gst_ffmpegmux_collected (GstCollectPads * pads,
134 static gboolean gst_ffmpegmux_sink_event (GstPad * pad, GstEvent * event);
136 static GstStateChangeReturn gst_ffmpegmux_change_state (GstElement * element,
137 GstStateChange transition);
139 static void gst_ffmpegmux_set_property (GObject * object, guint prop_id,
140 const GValue * value, GParamSpec * pspec);
141 static void gst_ffmpegmux_get_property (GObject * object, guint prop_id,
142 GValue * value, GParamSpec * pspec);
144 static GstCaps *gst_ffmpegmux_get_id_caps (enum CodecID *id_list);
145 static void gst_ffmpeg_mux_simple_caps_set_int_list (GstCaps * caps,
146 const gchar * field, guint num, const gint * values);
148 #define GST_FFMUX_PARAMS_QDATA g_quark_from_static_string("ffmux-params")
150 #ifdef GST_EXT_FFMUX_ENHANCEMENT
151 static void gst_ffmpegmux_release_pad (GstElement * element, GstPad * pad);
154 static GstElementClass *parent_class = NULL;
156 /*static guint gst_ffmpegmux_signals[LAST_SIGNAL] = { 0 }; */
161 const char *replacement;
162 } GstFFMpegMuxReplacement;
165 gst_ffmpegmux_get_replacement (const char *name)
167 static const GstFFMpegMuxReplacement blacklist[] = {
169 {"matroska", "matroskamux"},
171 {"mpegts", "mpegtsmux"},
173 {"mpjpeg", "multipartmux"},
179 {"yuv4mpegpipe", "y4menc"},
181 {"adts", "aacparse"},
183 {"asf_stream", "asfmux"},
190 for (i = 0; i < sizeof (blacklist) / sizeof (blacklist[0]); i++) {
191 if (strcmp (blacklist[i].name, name) == 0) {
192 return blacklist[i].replacement;
200 gst_ffmpegmux_is_formatter (const char *name)
202 static const char *replace[] = {
207 for (i = 0; replace[i]; i++)
208 if (strcmp (replace[i], name) == 0)
213 #ifdef GST_EXT_FFMUX_ENHANCEMENT
215 /* trailer entry size */
216 #define ENTRY_SIZE_VIDEO_STTS 8
217 #define ENTRY_SIZE_VIDEO_STSS 4
218 #define ENTRY_SIZE_VIDEO_STSZ 4
219 #define ENTRY_SIZE_VIDEO_STCO 4
220 #define ENTRY_SIZE_AUDIO_STTS 8
221 #define ENTRY_SIZE_AUDIO_STSZ 4
222 #define ENTRY_SIZE_AUDIO_STCO 4
225 #define MUX_ADTS_NAME "adts"
226 #define MUX_ADTS_SIZE_HEADER 8
227 #define MUX_ADTS_SIZE_ENTRY 7
230 #define MUX_OTHERS_SIZE_DEFAULT 248 /* ftyp + free + moov + mvhd + udta */
231 #define MUX_OTHERS_SIZE_HEADER_AMR (MUX_OTHERS_SIZE_DEFAULT + 410)
232 #define MUX_OTHERS_SIZE_HEADER_AAC (MUX_OTHERS_SIZE_DEFAULT + 424)
235 static void update_expected_trailer_size(GstFFMpegMux *ffmpegmux)
238 guint nb_video_frames = 0;
239 guint nb_video_i_frames = 0;
240 guint nb_video_stts_entry = 0;
241 guint nb_audio_frames = 0;
242 guint nb_audio_stts_entry = 0;
243 gboolean video_stream = FALSE;
244 gboolean audio_stream = FALSE;
246 AVCodecContext *codec_context = NULL;
247 enum CodecID video_codec_id;
248 enum CodecID audio_codec_id;
250 if (ffmpegmux == NULL) {
251 GST_WARNING("ffmpegmux is NULL");
255 for (i = 0 ; i < ffmpegmux->context->nb_streams ; i++) {
256 codec_context = ffmpegmux->context->streams[i]->codec;
257 if (codec_context->codec_type == CODEC_TYPE_VIDEO) {
258 nb_video_frames += codec_context->frame_number;
259 nb_video_i_frames += codec_context->i_frame_number;
260 nb_video_stts_entry += codec_context->stts_count;
263 video_codec_id = codec_context->codec_id;
264 } else if (codec_context->codec_type == CODEC_TYPE_AUDIO) {
265 nb_audio_frames += codec_context->frame_number;
266 nb_audio_stts_entry += codec_context->stts_count;
269 audio_codec_id = codec_context->codec_id;
276 ftyp = 32(H.264 video included) or 28(H.263 video included or audio only)
280 udta = 96(meta in case of audio only) or
281 84(loci in case of video only or video/audio)
283 - VIDEO:H.264 = 487(or 489) + (8*stts_count) + (8*frame) + (4*I-frame)
293 stsd = 134 (SPS 9, PPS 4) or 136 (SPS 111, PPS 4)
294 stts = 16 + (8*stts_count)
295 stss = 16 + (4*I-frame)
297 stsz = 20 + (4*frame)
298 stco = 16 + (4*frame)
300 - VIDEO:H.263 = 470 + + (8*stts_count) + (8*frame) + (4*I-frame)
310 stsd = 117 -> different from H.264
311 stts = 16 + (8*stts_count)
312 stss = 16 + (4*I-frame)
314 stsz = 20 + (4*frame)
315 stco = 16 + (4*frame)
317 - AUDIO:AAC = 424 + + (8*stts_count) + (8*audio_frame)
328 stts = 16 + (8*stts_count)
330 stsz = 20 + (4*frame)
331 stco = 16 + (4*frame)
333 - AUDIO:AMR = 410 + (4*audio_frame)
343 stsd = 69 -> different from AAC
344 stts = 24 -> different from AAC
346 stsz = 20 -> different from AAC
347 stco = 16 + (4*frame)
350 /* Calculate trailer size for video stream */
352 /* ftyp + free + moov + mvhd + udta : H.264 -> 240, H.263 -> 236 */
353 /* trak size except frame related : H.264 -> 489, H.263 -> 470 */
354 if (video_codec_id == CODEC_ID_H263 ) {
355 exp_size = 236 + 470;
357 exp_size = 240 + 489;
361 exp_size += (ENTRY_SIZE_VIDEO_STTS * nb_video_stts_entry) + \
362 (ENTRY_SIZE_VIDEO_STSS * nb_video_i_frames) + \
363 ((ENTRY_SIZE_VIDEO_STSZ + ENTRY_SIZE_VIDEO_STCO) * nb_video_frames);
367 /* Calculate trailer size for audio stream */
368 if (!strcmp(ffmpegmux->context->oformat->name, MUX_ADTS_NAME)) {
370 exp_size += MUX_ADTS_SIZE_HEADER + (MUX_ADTS_SIZE_ENTRY * nb_audio_frames);
372 /* others - ffmux_3gp/mp4/amr */
373 if (audio_codec_id == CODEC_ID_AMR_NB) {
375 exp_size += MUX_OTHERS_SIZE_HEADER_AMR + (ENTRY_SIZE_AUDIO_STCO * nb_audio_frames);
378 exp_size += MUX_OTHERS_SIZE_HEADER_AAC + \
379 (ENTRY_SIZE_AUDIO_STTS * nb_audio_stts_entry) + \
380 ((ENTRY_SIZE_AUDIO_STSZ + ENTRY_SIZE_AUDIO_STCO) * nb_audio_frames);
385 ffmpegmux->expected_trailer_size = exp_size;
386 ffmpegmux->nb_video_frames = nb_video_frames;
387 ffmpegmux->nb_audio_frames = nb_audio_frames;
391 #endif /* GST_EXT_FFMUX_ENHANCEMENT */
394 gst_ffmpegmux_base_init (gpointer g_class)
396 GstFFMpegMuxClass *klass = (GstFFMpegMuxClass *) g_class;
397 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
398 GstPadTemplate *videosinktempl, *audiosinktempl, *srctempl;
399 AVOutputFormat *in_plugin;
400 GstCaps *srccaps, *audiosinkcaps, *videosinkcaps;
401 enum CodecID *video_ids = NULL, *audio_ids = NULL;
402 gchar *longname, *description;
403 const char *replacement;
404 gboolean is_formatter;
407 (AVOutputFormat *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
408 GST_FFMUX_PARAMS_QDATA);
409 g_assert (in_plugin != NULL);
411 /* construct the element details struct */
412 replacement = gst_ffmpegmux_get_replacement (in_plugin->name);
413 is_formatter = gst_ffmpegmux_is_formatter (in_plugin->name);
414 if (replacement != NULL) {
416 g_strdup_printf ("FFmpeg %s %s (not recommended, use %s instead)",
417 in_plugin->long_name, is_formatter ? "formatter" : "muxer",
420 g_strdup_printf ("FFmpeg %s %s (not recommended, use %s instead)",
421 in_plugin->long_name, is_formatter ? "formatter" : "muxer",
424 longname = g_strdup_printf ("FFmpeg %s %s", in_plugin->long_name,
425 is_formatter ? "formatter" : "muxer");
426 description = g_strdup_printf ("FFmpeg %s %s", in_plugin->long_name,
427 is_formatter ? "formatter" : "muxer");
429 gst_element_class_set_details_simple (element_class, longname,
430 is_formatter ? "Formatter/Metadata" : "Codec/Muxer", description,
431 "Wim Taymans <wim.taymans@chello.be>, "
432 "Ronald Bultje <rbultje@ronald.bitfreak.net>");
434 g_free (description);
436 /* Try to find the caps that belongs here */
437 srccaps = gst_ffmpeg_formatid_to_caps (in_plugin->name);
439 GST_DEBUG ("Couldn't get source caps for muxer '%s', skipping format",
444 if (!gst_ffmpeg_formatid_get_codecids (in_plugin->name,
445 &video_ids, &audio_ids, in_plugin)) {
446 gst_caps_unref (srccaps);
448 ("Couldn't get sink caps for muxer '%s'. Most likely because no input format mapping exists.",
453 videosinkcaps = video_ids ? gst_ffmpegmux_get_id_caps (video_ids) : NULL;
454 audiosinkcaps = audio_ids ? gst_ffmpegmux_get_id_caps (audio_ids) : NULL;
456 /* fix up allowed caps for some muxers */
457 /* FIXME : This should be in gstffmpegcodecmap.c ! */
458 if (strcmp (in_plugin->name, "flv") == 0) {
459 const gint rates[] = { 44100, 22050, 11025 };
461 gst_ffmpeg_mux_simple_caps_set_int_list (audiosinkcaps, "rate", 3, rates);
462 } else if (strcmp (in_plugin->name, "gif") == 0) {
464 gst_caps_unref (videosinkcaps);
467 gst_caps_from_string ("video/x-raw-rgb, bpp=(int)24, depth=(int)24");
471 srctempl = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, srccaps);
472 gst_element_class_add_pad_template (element_class, srctempl);
475 audiosinktempl = gst_pad_template_new ("audio_%d",
476 GST_PAD_SINK, GST_PAD_REQUEST, audiosinkcaps);
477 gst_element_class_add_pad_template (element_class, audiosinktempl);
481 videosinktempl = gst_pad_template_new ("video_%d",
482 GST_PAD_SINK, GST_PAD_REQUEST, videosinkcaps);
483 gst_element_class_add_pad_template (element_class, videosinktempl);
487 klass->in_plugin = in_plugin;
491 gst_ffmpegmux_class_init (GstFFMpegMuxClass * klass)
493 GObjectClass *gobject_class;
494 GstElementClass *gstelement_class;
496 gobject_class = (GObjectClass *) klass;
497 gstelement_class = (GstElementClass *) klass;
499 parent_class = g_type_class_peek_parent (klass);
501 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_ffmpegmux_set_property);
502 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_ffmpegmux_get_property);
504 g_object_class_install_property (gobject_class, PROP_PRELOAD,
505 g_param_spec_int ("preload", "preload",
506 "Set the initial demux-decode delay (in microseconds)",
507 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
509 g_object_class_install_property (gobject_class, PROP_MAXDELAY,
510 g_param_spec_int ("maxdelay", "maxdelay",
511 "Set the maximum demux-decode delay (in microseconds)", 0, G_MAXINT,
512 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
514 gstelement_class->request_new_pad = gst_ffmpegmux_request_new_pad;
515 gstelement_class->change_state = gst_ffmpegmux_change_state;
516 gobject_class->finalize = gst_ffmpegmux_finalize;
518 #ifdef GST_EXT_FFMUX_ENHANCEMENT
519 gstelement_class->release_pad = gst_ffmpegmux_release_pad;
522 g_object_class_install_property (gobject_class, PROP_EXPECTED_TRAILER_SIZE,
523 g_param_spec_uint ("expected-trailer-size", "Expected Trailer Size",
524 "Expected trailer size (bytes)",
525 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
526 g_object_class_install_property (gobject_class, PROP_NUMBER_VIDEO_FRAMES,
527 g_param_spec_uint ("number-video-frames", "Number of video frames",
528 "Current number of video frames",
529 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
530 g_object_class_install_property (gobject_class, PROP_NUMBER_AUDIO_FRAMES,
531 g_param_spec_uint ("number-audio-frames", "Number of audio frames",
532 "Current number of audio frames",
533 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
538 gst_ffmpegmux_init (GstFFMpegMux * ffmpegmux, GstFFMpegMuxClass * g_class)
540 GstElementClass *klass = GST_ELEMENT_CLASS (g_class);
541 GstFFMpegMuxClass *oclass = (GstFFMpegMuxClass *) klass;
542 GstPadTemplate *templ = gst_element_class_get_pad_template (klass, "src");
544 ffmpegmux->srcpad = gst_pad_new_from_template (templ, "src");
545 gst_pad_set_caps (ffmpegmux->srcpad, gst_pad_template_get_caps (templ));
546 gst_element_add_pad (GST_ELEMENT (ffmpegmux), ffmpegmux->srcpad);
548 ffmpegmux->collect = gst_collect_pads_new ();
549 gst_collect_pads_set_function (ffmpegmux->collect,
550 (GstCollectPadsFunction) gst_ffmpegmux_collected, ffmpegmux);
552 ffmpegmux->context = g_new0 (AVFormatContext, 1);
553 ffmpegmux->context->oformat = oclass->in_plugin;
554 ffmpegmux->context->nb_streams = 0;
555 g_snprintf (ffmpegmux->context->filename,
556 sizeof (ffmpegmux->context->filename),
557 "gstreamer://%p", ffmpegmux->srcpad);
558 ffmpegmux->opened = FALSE;
560 ffmpegmux->videopads = 0;
561 ffmpegmux->audiopads = 0;
562 ffmpegmux->preload = 0;
563 ffmpegmux->max_delay = 0;
565 #ifdef GST_EXT_FFMUX_ENHANCEMENT
566 ffmpegmux->expected_trailer_size = 0;
567 ffmpegmux->nb_video_frames = 0;
568 ffmpegmux->nb_audio_frames = 0;
569 #endif /* GST_EXT_FFMUX_ENHANCEMENT */
572 #ifdef GST_EXT_FFMUX_ENHANCEMENT
574 gst_ffmpegmux_release_pad (GstElement * element, GstPad * pad)
576 GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) element;
577 GstFFMpegMuxPad *collect_pad;
580 collect_pad = (GstFFMpegMuxPad *) gst_pad_get_element_private (pad);
582 GST_DEBUG("Release requested pad[%s:%s]", GST_DEBUG_PAD_NAME(pad));
583 st = ffmpegmux->context->streams[collect_pad->padnum];
588 if(st->codec->codec_type == CODEC_TYPE_VIDEO)
590 ffmpegmux->videopads--;
594 ffmpegmux->audiopads--;
596 if(st->codec->extradata)
598 av_free(st->codec->extradata);
599 st->codec->extradata = NULL;
604 if(ffmpegmux->context->priv_data)
606 MOVMuxContext *mov = ffmpegmux->context->priv_data;
607 if(mov && mov->tracks)
609 for(i=0;i<ffmpegmux->context->nb_streams;i++)
611 MOVTrack *trk = &mov->tracks[i];
612 if(trk && trk->vosData)
614 av_free(trk->vosData);
618 av_free(mov->tracks);
621 av_free(ffmpegmux->context->priv_data);
622 ffmpegmux->context->priv_data = NULL;
624 ffmpegmux->context->nb_streams--;
628 gst_collect_pads_remove_pad (ffmpegmux->collect, pad);
629 gst_element_remove_pad (element, pad);
634 gst_ffmpegmux_set_property (GObject * object, guint prop_id,
635 const GValue * value, GParamSpec * pspec)
639 src = (GstFFMpegMux *) object;
643 src->preload = g_value_get_int (value);
646 src->max_delay = g_value_get_int (value);
649 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
655 gst_ffmpegmux_get_property (GObject * object, guint prop_id, GValue * value,
660 src = (GstFFMpegMux *) object;
664 g_value_set_int (value, src->preload);
667 g_value_set_int (value, src->max_delay);
669 #ifdef GST_EXT_FFMUX_ENHANCEMENT
670 case PROP_EXPECTED_TRAILER_SIZE:
671 g_value_set_uint (value, src->expected_trailer_size);
673 case PROP_NUMBER_VIDEO_FRAMES:
674 g_value_set_uint (value, src->nb_video_frames);
676 case PROP_NUMBER_AUDIO_FRAMES:
677 g_value_set_uint (value, src->nb_audio_frames);
679 #endif /* GST_EXT_FFMUX_ENHANCEMENT */
681 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
687 gst_ffmpegmux_free_metadata (GObject * object)
689 GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) object;
691 if(ffmpegmux->context->metadata)
693 for(i = 0;i < ffmpegmux->context->metadata->count; i++)
695 if(ffmpegmux->context->metadata->elems[i].value)
697 av_free(ffmpegmux->context->metadata->elems[i].value);
698 ffmpegmux->context->metadata->elems[i].value = NULL;
700 if(ffmpegmux->context->metadata->elems[i].key)
702 av_free(ffmpegmux->context->metadata->elems[i].key);
703 ffmpegmux->context->metadata->elems[i].key = NULL;
706 if(ffmpegmux->context->metadata->elems)
708 av_free(ffmpegmux->context->metadata->elems);
709 ffmpegmux->context->metadata->elems = NULL;
711 av_free(ffmpegmux->context->metadata);
712 ffmpegmux->context->metadata = NULL;
717 gst_ffmpegmux_finalize (GObject * object)
719 GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) object;
720 gst_ffmpegmux_free_metadata(object);
721 g_free (ffmpegmux->context);
722 gst_object_unref (ffmpegmux->collect);
724 if (G_OBJECT_CLASS (parent_class)->finalize)
725 G_OBJECT_CLASS (parent_class)->finalize (object);
729 gst_ffmpegmux_request_new_pad (GstElement * element,
730 GstPadTemplate * templ, const gchar * name)
732 GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) element;
733 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
734 GstFFMpegMuxPad *collect_pad;
739 gint bitrate = 0, framesize = 0;
741 g_return_val_if_fail (templ != NULL, NULL);
742 g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
743 g_return_val_if_fail (ffmpegmux->opened == FALSE, NULL);
745 /* figure out a name that *we* like */
746 if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
747 padname = g_strdup_printf ("video_%d", ffmpegmux->videopads++);
748 type = CODEC_TYPE_VIDEO;
751 } else if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
752 padname = g_strdup_printf ("audio_%d", ffmpegmux->audiopads++);
753 type = CODEC_TYPE_AUDIO;
754 bitrate = 285 * 1024;
756 g_warning ("ffmux: unknown pad template!");
761 pad = gst_pad_new_from_template (templ, padname);
762 collect_pad = (GstFFMpegMuxPad *)
763 gst_collect_pads_add_pad (ffmpegmux->collect, pad,
764 sizeof (GstFFMpegMuxPad));
765 collect_pad->padnum = ffmpegmux->context->nb_streams;
767 /* small hack to put our own event pad function and chain up to collect pad */
768 ffmpegmux->event_function = GST_PAD_EVENTFUNC (pad);
769 gst_pad_set_event_function (pad,
770 GST_DEBUG_FUNCPTR (gst_ffmpegmux_sink_event));
772 gst_pad_set_setcaps_function (pad, GST_DEBUG_FUNCPTR (gst_ffmpegmux_setcaps));
773 gst_element_add_pad (element, pad);
775 /* AVStream needs to be created */
776 st = av_new_stream (ffmpegmux->context, collect_pad->padnum);
777 st->codec->codec_type = type;
778 st->codec->codec_id = CODEC_ID_NONE; /* this is a check afterwards */
779 st->stream_copy = 1; /* we're not the actual encoder */
780 st->codec->bit_rate = bitrate;
781 st->codec->frame_size = framesize;
782 /* we fill in codec during capsnego */
784 /* we love debug output (c) (tm) (r) */
785 GST_DEBUG ("Created %s pad for ffmux_%s element",
786 padname, ((GstFFMpegMuxClass *) klass)->in_plugin->name);
793 * gst_ffmpegmux_setcaps
799 * Returns: #TRUE on success.
802 gst_ffmpegmux_setcaps (GstPad * pad, GstCaps * caps)
804 GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) (gst_pad_get_parent (pad));
805 GstFFMpegMuxPad *collect_pad;
808 collect_pad = (GstFFMpegMuxPad *) gst_pad_get_element_private (pad);
810 st = ffmpegmux->context->streams[collect_pad->padnum];
811 ffmpegmux->context->preload = ffmpegmux->preload;
812 ffmpegmux->context->max_delay = ffmpegmux->max_delay;
814 /* for the format-specific guesses, we'll go to
815 * our famous codec mapper */
816 if (gst_ffmpeg_caps_to_codecid (caps, st->codec) == CODEC_ID_NONE)
819 /* copy over the aspect ratios, ffmpeg expects the stream aspect to match the
821 st->sample_aspect_ratio = st->codec->sample_aspect_ratio;
823 #ifdef GST_EXT_FFMUX_ENHANCEMENT
824 /* ref counting bug fix */
825 gst_object_unref(ffmpegmux);
827 GST_LOG_OBJECT (pad, "accepted caps %" GST_PTR_FORMAT, caps);
833 #ifdef GST_EXT_FFMUX_ENHANCEMENT
834 /* ref counting bug fix */
835 gst_object_unref(ffmpegmux);
837 GST_LOG_OBJECT (pad, "rejecting caps %" GST_PTR_FORMAT, caps);
844 gst_ffmpegmux_sink_event (GstPad * pad, GstEvent * event)
846 GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) gst_pad_get_parent (pad);
849 switch (GST_EVENT_TYPE (event)) {
852 GstTagSetter *setter = GST_TAG_SETTER (ffmpegmux);
853 const GstTagMergeMode mode = gst_tag_setter_get_tag_merge_mode (setter);
855 gst_event_parse_tag (event, &taglist);
856 gst_tag_setter_merge_tags (setter, taglist, mode);
863 /* chaining up to collectpads default event function */
864 res = ffmpegmux->event_function (pad, event);
866 gst_object_unref (ffmpegmux);
871 gst_ffmpegmux_collected (GstCollectPads * pads, gpointer user_data)
873 GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) user_data;
875 GstFFMpegMuxPad *best_pad;
876 GstClockTime best_time;
877 const GstTagList *tags;
879 /* open "file" (gstreamer protocol to next element) */
880 if (!ffmpegmux->opened) {
881 int open_flags = URL_WRONLY;
883 /* we do need all streams to have started capsnego,
884 * or things will go horribly wrong */
885 for (collected = ffmpegmux->collect->data; collected;
886 collected = g_slist_next (collected)) {
887 GstFFMpegMuxPad *collect_pad = (GstFFMpegMuxPad *) collected->data;
888 AVStream *st = ffmpegmux->context->streams[collect_pad->padnum];
890 /* check whether the pad has successfully completed capsnego */
891 if (st->codec->codec_id == CODEC_ID_NONE) {
892 GST_ELEMENT_ERROR (ffmpegmux, CORE, NEGOTIATION, (NULL),
893 ("no caps set on stream %d (%s)", collect_pad->padnum,
894 (st->codec->codec_type == CODEC_TYPE_VIDEO) ?
896 return GST_FLOW_ERROR;
898 /* set framerate for audio */
899 if (st->codec->codec_type == CODEC_TYPE_AUDIO) {
900 switch (st->codec->codec_id) {
901 case CODEC_ID_PCM_S16LE:
902 case CODEC_ID_PCM_S16BE:
903 case CODEC_ID_PCM_U16LE:
904 case CODEC_ID_PCM_U16BE:
905 case CODEC_ID_PCM_S8:
906 case CODEC_ID_PCM_U8:
907 st->codec->frame_size = 1;
913 /* FIXME : This doesn't work for RAW AUDIO...
914 * in fact I'm wondering if it even works for any kind of audio... */
915 buffer = gst_collect_pads_peek (ffmpegmux->collect,
916 (GstCollectData *) collect_pad);
918 st->codec->frame_size =
919 st->codec->sample_rate *
920 GST_BUFFER_DURATION (buffer) / GST_SECOND;
921 gst_buffer_unref (buffer);
929 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (ffmpegmux));
934 /* get the interesting ones */
935 if (gst_tag_list_get_string (tags, GST_TAG_TITLE, &s)) {
936 strncpy (ffmpegmux->context->title, s,
937 sizeof (ffmpegmux->context->title));
939 if (gst_tag_list_get_string (tags, GST_TAG_ARTIST, &s)) {
940 strncpy (ffmpegmux->context->author, s,
941 sizeof (ffmpegmux->context->author));
943 if (gst_tag_list_get_string (tags, GST_TAG_COPYRIGHT, &s)) {
944 strncpy (ffmpegmux->context->copyright, s,
945 sizeof (ffmpegmux->context->copyright));
947 if (gst_tag_list_get_string (tags, GST_TAG_COMMENT, &s)) {
948 strncpy (ffmpegmux->context->comment, s,
949 sizeof (ffmpegmux->context->comment));
951 if (gst_tag_list_get_string (tags, GST_TAG_ALBUM, &s)) {
952 strncpy (ffmpegmux->context->album, s,
953 sizeof (ffmpegmux->context->album));
955 if (gst_tag_list_get_string (tags, GST_TAG_GENRE, &s)) {
956 strncpy (ffmpegmux->context->genre, s,
957 sizeof (ffmpegmux->context->genre));
959 if (gst_tag_list_get_int (tags, GST_TAG_TRACK_NUMBER, &i)) {
960 ffmpegmux->context->track = i;
964 /* set the streamheader flag for gstffmpegprotocol if codec supports it */
965 if (!strcmp (ffmpegmux->context->oformat->name, "flv")) {
966 open_flags |= GST_FFMPEG_URL_STREAMHEADER;
969 if (url_fopen (&ffmpegmux->context->pb,
970 ffmpegmux->context->filename, open_flags) < 0) {
971 GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, TOO_LAZY, (NULL),
972 ("Failed to open stream context in ffmux"));
973 return GST_FLOW_ERROR;
976 if (av_set_parameters (ffmpegmux->context, NULL) < 0) {
977 GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, INIT, (NULL),
978 ("Failed to initialize muxer"));
979 return GST_FLOW_ERROR;
982 /* now open the mux format */
983 if (av_write_header (ffmpegmux->context) < 0) {
984 GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, SETTINGS, (NULL),
985 ("Failed to write file header - check codec settings"));
986 return GST_FLOW_ERROR;
989 /* we're now opened */
990 ffmpegmux->opened = TRUE;
992 /* flush the header so it will be used as streamheader */
993 put_flush_packet (ffmpegmux->context->pb);
996 /* take the one with earliest timestamp,
997 * and push it forward */
999 best_time = GST_CLOCK_TIME_NONE;
1000 for (collected = ffmpegmux->collect->data; collected;
1001 collected = g_slist_next (collected)) {
1002 GstFFMpegMuxPad *collect_pad = (GstFFMpegMuxPad *) collected->data;
1003 GstBuffer *buffer = gst_collect_pads_peek (ffmpegmux->collect,
1004 (GstCollectData *) collect_pad);
1006 /* if there's no buffer, just continue */
1007 if (buffer == NULL) {
1011 /* if we have no buffer yet, just use the first one */
1012 if (best_pad == NULL) {
1013 best_pad = collect_pad;
1014 best_time = GST_BUFFER_TIMESTAMP (buffer);
1018 /* if we do have one, only use this one if it's older */
1019 if (GST_BUFFER_TIMESTAMP (buffer) < best_time) {
1020 best_time = GST_BUFFER_TIMESTAMP (buffer);
1021 best_pad = collect_pad;
1025 gst_buffer_unref (buffer);
1027 /* Mux buffers with invalid timestamp first */
1028 if (!GST_CLOCK_TIME_IS_VALID (best_time))
1032 /* now handle the buffer, or signal EOS if we have
1033 * no buffers left */
1034 if (best_pad != NULL) {
1037 gboolean need_free = FALSE;
1038 #ifdef GST_EXT_FFMUX_ENHANCEMENT
1039 av_init_packet(&pkt);
1040 pkt.is_mux = 1; // true
1042 /* push out current buffer */
1043 buf = gst_collect_pads_pop (ffmpegmux->collect,
1044 (GstCollectData *) best_pad);
1046 ffmpegmux->context->streams[best_pad->padnum]->codec->frame_number++;
1049 #ifdef GST_EXT_FFMUX_ENHANCEMENT
1050 if(ffmpegmux->context->streams[best_pad->padnum]->codec->codec_type == CODEC_TYPE_VIDEO) {
1051 pkt.pts = GST_TIME_AS_MSECONDS(GST_BUFFER_TIMESTAMP(buf));
1053 pkt.pts = gst_ffmpeg_time_gst_to_ff(GST_BUFFER_TIMESTAMP(buf),
1054 ffmpegmux->context->streams[best_pad->padnum]->time_base);
1057 pkt.pts = gst_ffmpeg_time_gst_to_ff (GST_BUFFER_TIMESTAMP (buf),
1058 ffmpegmux->context->streams[best_pad->padnum]->time_base);
1062 if (strcmp (ffmpegmux->context->oformat->name, "gif") == 0) {
1063 AVStream *st = ffmpegmux->context->streams[best_pad->padnum];
1067 pkt.size = st->codec->width * st->codec->height * 3;
1068 pkt.data = g_malloc (pkt.size);
1070 dst.data[0] = pkt.data;
1073 dst.linesize[0] = st->codec->width * 3;
1075 gst_ffmpeg_avpicture_fill (&src, GST_BUFFER_DATA (buf),
1076 PIX_FMT_RGB24, st->codec->width, st->codec->height);
1078 av_picture_copy (&dst, &src, PIX_FMT_RGB24,
1079 st->codec->width, st->codec->height);
1081 pkt.data = GST_BUFFER_DATA (buf);
1082 pkt.size = GST_BUFFER_SIZE (buf);
1085 pkt.stream_index = best_pad->padnum;
1088 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
1089 pkt.flags |= PKT_FLAG_KEY;
1091 #ifdef GST_EXT_FFMUX_ENHANCEMENT
1092 if (ffmpegmux->context->streams[best_pad->padnum]->codec->codec_type == CODEC_TYPE_VIDEO) {
1093 static int last_duration = -1;
1094 static int64_t last_dts = -1;
1095 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1096 pkt.duration = GST_TIME_AS_MSECONDS(GST_BUFFER_DURATION(buf));
1101 if (last_dts == -1) {
1103 ffmpegmux->context->streams[best_pad->padnum]->codec->stts_count++;
1105 /* check real duration : current dts - last dts */
1106 if (last_duration != (pkt.dts - last_dts)) {
1107 last_duration = pkt.dts - last_dts;
1108 ffmpegmux->context->streams[best_pad->padnum]->codec->stts_count++;
1113 if (!GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
1114 ffmpegmux->context->streams[best_pad->padnum]->codec->i_frame_number++;
1117 static int last_duration_audio = -1;
1118 static int64_t last_dts_audio = -1;
1120 if (GST_BUFFER_DURATION_IS_VALID(buf)) {
1121 if (last_dts_audio == -1) {
1123 ffmpegmux->context->streams[best_pad->padnum]->codec->stts_count++;
1125 /* check real duration : current dts - last dts */
1126 if (last_duration_audio != (pkt.dts - last_dts_audio)) {
1127 last_duration_audio = pkt.dts - last_dts_audio;
1128 ffmpegmux->context->streams[best_pad->padnum]->codec->stts_count++;
1131 last_dts_audio = pkt.dts;
1134 gst_ffmpeg_time_gst_to_ff (GST_BUFFER_DURATION (buf),
1135 ffmpegmux->context->streams[best_pad->padnum]->time_base);
1141 update_expected_trailer_size(ffmpegmux);
1143 if (GST_BUFFER_DURATION_IS_VALID (buf))
1145 gst_ffmpeg_time_gst_to_ff (GST_BUFFER_DURATION (buf),
1146 ffmpegmux->context->streams[best_pad->padnum]->time_base);
1150 av_write_frame (ffmpegmux->context, &pkt);
1151 gst_buffer_unref (buf);
1156 #ifdef GST_EXT_FFMUX_ENHANCEMENT
1157 GST_WARNING_OBJECT(ffmpegmux, "close down");
1158 #endif /* GST_EXT_FFMUX_ENHANCEMENT */
1159 av_write_trailer (ffmpegmux->context);
1160 ffmpegmux->opened = FALSE;
1161 put_flush_packet (ffmpegmux->context->pb);
1162 url_fclose (ffmpegmux->context->pb);
1163 gst_pad_push_event (ffmpegmux->srcpad, gst_event_new_eos ());
1164 #ifdef GST_EXT_FFMUX_ENHANCEMENT
1165 GST_WARNING_OBJECT(ffmpegmux, "send EOS done");
1166 #endif /* GST_EXT_FFMUX_ENHANCEMENT */
1167 return GST_FLOW_UNEXPECTED;
1173 static GstStateChangeReturn
1174 gst_ffmpegmux_change_state (GstElement * element, GstStateChange transition)
1177 GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) (element);
1179 switch (transition) {
1180 case GST_STATE_CHANGE_NULL_TO_READY:
1182 case GST_STATE_CHANGE_READY_TO_PAUSED:
1183 gst_collect_pads_start (ffmpegmux->collect);
1185 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1187 case GST_STATE_CHANGE_PAUSED_TO_READY:
1188 gst_collect_pads_stop (ffmpegmux->collect);
1194 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1196 switch (transition) {
1197 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1199 case GST_STATE_CHANGE_PAUSED_TO_READY:
1200 gst_tag_setter_reset_tags (GST_TAG_SETTER (ffmpegmux));
1201 if (ffmpegmux->opened) {
1202 ffmpegmux->opened = FALSE;
1203 url_fclose (ffmpegmux->context->pb);
1205 #ifdef GST_EXT_FFMUX_ENHANCEMENT
1207 for(i=0; i < ffmpegmux->context->nb_streams; i++)
1209 ffmpegmux->context->streams[i]->start_time = AV_NOPTS_VALUE;
1210 ffmpegmux->context->streams[i]->duration = AV_NOPTS_VALUE;
1211 ffmpegmux->context->streams[i]->cur_dts = AV_NOPTS_VALUE;
1216 case GST_STATE_CHANGE_READY_TO_NULL:
1226 gst_ffmpegmux_get_id_caps (enum CodecID *id_list)
1231 caps = gst_caps_new_empty ();
1232 for (i = 0; id_list[i] != CODEC_ID_NONE; i++) {
1233 if ((t = gst_ffmpeg_codecid_to_caps (id_list[i], NULL, TRUE)))
1234 gst_caps_append (caps, t);
1236 if (gst_caps_is_empty (caps)) {
1237 gst_caps_unref (caps);
1244 /* set a list of integer values on the caps, e.g. for sample rates */
1246 gst_ffmpeg_mux_simple_caps_set_int_list (GstCaps * caps, const gchar * field,
1247 guint num, const gint * values)
1249 GValue list = { 0, };
1250 GValue val = { 0, };
1253 g_return_if_fail (GST_CAPS_IS_SIMPLE (caps));
1255 g_value_init (&list, GST_TYPE_LIST);
1256 g_value_init (&val, G_TYPE_INT);
1258 for (i = 0; i < num; ++i) {
1259 g_value_set_int (&val, values[i]);
1260 gst_value_list_append_value (&list, &val);
1263 gst_structure_set_value (gst_caps_get_structure (caps, 0), field, &list);
1265 g_value_unset (&val);
1266 g_value_unset (&list);
1270 gst_ffmpegmux_register (GstPlugin * plugin)
1272 GTypeInfo typeinfo = {
1273 sizeof (GstFFMpegMuxClass),
1274 (GBaseInitFunc) gst_ffmpegmux_base_init,
1276 (GClassInitFunc) gst_ffmpegmux_class_init,
1279 sizeof (GstFFMpegMux),
1281 (GInstanceInitFunc) gst_ffmpegmux_init,
1283 static const GInterfaceInfo tag_setter_info = {
1287 AVOutputFormat *in_plugin;
1289 in_plugin = av_oformat_next (NULL);
1291 GST_LOG ("Registering muxers");
1296 GstRank rank = GST_RANK_MARGINAL;
1298 if ((!strncmp (in_plugin->name, "u16", 3)) ||
1299 (!strncmp (in_plugin->name, "s16", 3)) ||
1300 (!strncmp (in_plugin->name, "u24", 3)) ||
1301 (!strncmp (in_plugin->name, "s24", 3)) ||
1302 (!strncmp (in_plugin->name, "u8", 2)) ||
1303 (!strncmp (in_plugin->name, "s8", 2)) ||
1304 (!strncmp (in_plugin->name, "u32", 3)) ||
1305 (!strncmp (in_plugin->name, "s32", 3)) ||
1306 (!strncmp (in_plugin->name, "f32", 3)) ||
1307 (!strncmp (in_plugin->name, "f64", 3)) ||
1308 (!strncmp (in_plugin->name, "raw", 3)) ||
1309 (!strncmp (in_plugin->name, "crc", 3)) ||
1310 (!strncmp (in_plugin->name, "null", 4)) ||
1311 (!strncmp (in_plugin->name, "gif", 3)) ||
1312 (!strncmp (in_plugin->name, "frame", 5)) ||
1313 (!strncmp (in_plugin->name, "image", 5)) ||
1314 (!strncmp (in_plugin->name, "mulaw", 5)) ||
1315 (!strncmp (in_plugin->name, "alaw", 4)) ||
1316 (!strncmp (in_plugin->name, "h26", 3)) ||
1317 (!strncmp (in_plugin->name, "rtp", 3)) ||
1318 (!strncmp (in_plugin->name, "ass", 3))
1320 GST_LOG ("Ignoring muxer %s", in_plugin->name);
1324 if ((!strncmp (in_plugin->long_name, "raw ", 4))) {
1325 GST_LOG ("Ignoring raw muxer %s", in_plugin->name);
1329 if (gst_ffmpegmux_get_replacement (in_plugin->name))
1330 rank = GST_RANK_NONE;
1332 /* FIXME : We need a fast way to know whether we have mappings for this
1335 /* construct the type */
1336 type_name = g_strdup_printf ("ffmux_%s", in_plugin->name);
1346 type = g_type_from_name (type_name);
1349 /* create the type now */
1350 type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
1351 g_type_set_qdata (type, GST_FFMUX_PARAMS_QDATA, (gpointer) in_plugin);
1352 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
1355 if (!gst_element_register (plugin, type_name, rank, type)) {
1363 in_plugin = av_oformat_next (in_plugin);
1366 GST_LOG ("Finished registering muxers");