2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) 2012, 2013 Samsung Electronics Co., Ltd.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * * Modifications by Samsung Electronics Co., Ltd.
21 * 1. Add new function to get expected trailer size
29 #ifdef HAVE_FFMPEG_UNINSTALLED
32 #include <libavformat/avformat.h>
36 #include <gst/base/gstcollectpads.h>
38 #include "gstffmpeg.h"
39 #include "gstffmpegcodecmap.h"
40 #include "gstffmpegutils.h"
42 #ifndef GST_EXT_FFMUX_ENHANCEMENT
43 #define GST_EXT_FFMUX_ENHANCEMENT
44 #endif /* GST_EXT_FFMUX_ENHANCEMENT */
46 typedef struct _GstFFMpegMux GstFFMpegMux;
47 typedef struct _GstFFMpegMuxPad GstFFMpegMuxPad;
49 struct _GstFFMpegMuxPad
51 GstCollectData collect; /* we extend the CollectData */
60 GstCollectPads *collect;
61 /* We need to keep track of our pads, so we do so here. */
64 AVFormatContext *context;
67 gint videopads, audiopads;
68 #ifdef GST_EXT_FFMUX_ENHANCEMENT
69 guint expected_trailer_size, nb_video_frames, nb_audio_frames;
70 #endif /* GST_EXT_FFMUX_ENHANCEMENT */
73 /* event_function is the collectpads default eventfunction */
74 GstPadEventFunction event_function;
79 typedef struct _GstFFMpegMuxClass GstFFMpegMuxClass;
81 struct _GstFFMpegMuxClass
83 GstElementClass parent_class;
85 AVOutputFormat *in_plugin;
88 #define GST_TYPE_FFMPEGMUX \
89 (gst_ffmpegdec_get_type())
90 #define GST_FFMPEGMUX(obj) \
91 (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGMUX,GstFFMpegMux))
92 #define GST_FFMPEGMUX_CLASS(klass) \
93 (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGMUX,GstFFMpegMuxClass))
94 #define GST_IS_FFMPEGMUX(obj) \
95 (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGMUX))
96 #define GST_IS_FFMPEGMUX_CLASS(klass) \
97 (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGMUX))
116 #ifdef GST_EXT_FFMUX_ENHANCEMENT
117 PROP_EXPECTED_TRAILER_SIZE,
118 PROP_NUMBER_VIDEO_FRAMES,
119 PROP_NUMBER_AUDIO_FRAMES,
120 #endif /* GST_EXT_FFMUX_ENHANCEMENT */
123 /* A number of function prototypes are given so we can refer to them later. */
124 static void gst_ffmpegmux_class_init (GstFFMpegMuxClass * klass);
125 static void gst_ffmpegmux_base_init (gpointer g_class);
126 static void gst_ffmpegmux_init (GstFFMpegMux * ffmpegmux,
127 GstFFMpegMuxClass * g_class);
128 static void gst_ffmpegmux_finalize (GObject * object);
130 static gboolean gst_ffmpegmux_setcaps (GstPad * pad, GstCaps * caps);
131 static GstPad *gst_ffmpegmux_request_new_pad (GstElement * element,
132 GstPadTemplate * templ, const gchar * name);
133 static GstFlowReturn gst_ffmpegmux_collected (GstCollectPads * pads,
136 static gboolean gst_ffmpegmux_sink_event (GstPad * pad, GstEvent * event);
138 static GstStateChangeReturn gst_ffmpegmux_change_state (GstElement * element,
139 GstStateChange transition);
141 static void gst_ffmpegmux_set_property (GObject * object, guint prop_id,
142 const GValue * value, GParamSpec * pspec);
143 static void gst_ffmpegmux_get_property (GObject * object, guint prop_id,
144 GValue * value, GParamSpec * pspec);
146 static GstCaps *gst_ffmpegmux_get_id_caps (enum CodecID *id_list);
147 static void gst_ffmpeg_mux_simple_caps_set_int_list (GstCaps * caps,
148 const gchar * field, guint num, const gint * values);
150 #define GST_FFMUX_PARAMS_QDATA g_quark_from_static_string("ffmux-params")
152 #ifdef GST_EXT_FFMUX_ENHANCEMENT
153 static void gst_ffmpegmux_release_pad (GstElement * element, GstPad * pad);
156 static GstElementClass *parent_class = NULL;
158 /*static guint gst_ffmpegmux_signals[LAST_SIGNAL] = { 0 }; */
163 const char *replacement;
164 } GstFFMpegMuxReplacement;
167 gst_ffmpegmux_get_replacement (const char *name)
169 static const GstFFMpegMuxReplacement blacklist[] = {
171 {"matroska", "matroskamux"},
173 {"mpegts", "mpegtsmux"},
175 {"mpjpeg", "multipartmux"},
181 {"yuv4mpegpipe", "y4menc"},
183 {"adts", "aacparse"},
185 {"asf_stream", "asfmux"},
192 for (i = 0; i < sizeof (blacklist) / sizeof (blacklist[0]); i++) {
193 if (strcmp (blacklist[i].name, name) == 0) {
194 return blacklist[i].replacement;
202 gst_ffmpegmux_is_formatter (const char *name)
204 static const char *replace[] = {
209 for (i = 0; replace[i]; i++)
210 if (strcmp (replace[i], name) == 0)
215 #ifdef GST_EXT_FFMUX_ENHANCEMENT
217 /* trailer entry size */
218 #define ENTRY_SIZE_VIDEO_STTS 8
219 #define ENTRY_SIZE_VIDEO_STSS 4
220 #define ENTRY_SIZE_VIDEO_STSZ 4
221 #define ENTRY_SIZE_VIDEO_STCO 4
222 #define ENTRY_SIZE_AUDIO_STSZ 4
223 #define ENTRY_SIZE_AUDIO_STCO 4
226 #define MUX_ADTS_NAME "adts"
227 #define MUX_ADTS_SIZE_HEADER 8
228 #define MUX_ADTS_SIZE_ENTRY 7
231 #define MUX_OTHERS_SIZE_DEFAULT 248 /* ftyp + free + moov + mvhd + udta */
232 #define MUX_OTHERS_SIZE_HEADER_AMR (MUX_OTHERS_SIZE_DEFAULT + 410)
233 #define MUX_OTHERS_SIZE_HEADER_AAC (MUX_OTHERS_SIZE_DEFAULT + 432)
236 static void update_expected_trailer_size(GstFFMpegMux *ffmpegmux)
239 guint nb_video_frames = 0;
240 guint nb_video_i_frames = 0;
241 guint nb_stts_entry = 0;
242 guint nb_audio_frames = 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_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;
268 audio_codec_id = codec_context->codec_id;
275 ftyp = 32(H.264 video included) or 28(H.263 video included or audio only)
279 udta = 96(meta in case of audio only) or
280 84(loci in case of video only or video/audio)
282 - VIDEO:H.264 = 487(or 489) + (8*stts_count) + (8*frame) + (4*I-frame)
292 stsd = 134 (SPS 9, PPS 4) or 136 (SPS 111, PPS 4)
293 stts = 16 + (8*stts_count)
294 stss = 16 + (4*I-frame)
296 stsz = 20 + (4*frame)
297 stco = 16 + (4*frame)
299 - VIDEO:H.263 = 470 + + (8*stts_count) + (8*frame) + (4*I-frame)
309 stsd = 117 -> different from H.264
310 stts = 16 + (8*stts_count)
311 stss = 16 + (4*I-frame)
313 stsz = 20 + (4*frame)
314 stco = 16 + (4*frame)
316 - AUDIO:AAC = 432 + (8*audio_frame)
329 stsz = 20 + (4*frame)
330 stco = 16 + (4*frame)
332 - AUDIO:AMR = 410 + (4*audio_frame)
342 stsd = 69 -> different from AAC
345 stsz = 20 -> different from AAC
346 stco = 16 + (4*frame)
349 /* Calculate trailer size for video stream */
351 /* ftyp + free + moov + mvhd + udta : H.264 -> 240, H.263 -> 236 */
352 /* trak size except frame related : H.264 -> 489, H.263 -> 470 */
353 if (video_codec_id == CODEC_ID_H263 ) {
354 exp_size = 236 + 470;
356 exp_size = 240 + 489;
360 exp_size += (ENTRY_SIZE_VIDEO_STTS * nb_stts_entry) + \
361 (ENTRY_SIZE_VIDEO_STSS * nb_video_i_frames) + \
362 ((ENTRY_SIZE_VIDEO_STSZ + ENTRY_SIZE_VIDEO_STCO) * nb_video_frames);
366 /* Calculate trailer size for audio stream */
367 if (!strcmp(ffmpegmux->context->oformat->name, MUX_ADTS_NAME)) {
369 exp_size += MUX_ADTS_SIZE_HEADER + (MUX_ADTS_SIZE_ENTRY * nb_audio_frames);
371 /* others - ffmux_3gp/mp4/amr */
372 if (audio_codec_id == CODEC_ID_AMR_NB) {
374 exp_size += MUX_OTHERS_SIZE_HEADER_AMR + (ENTRY_SIZE_AUDIO_STCO * nb_audio_frames);
377 exp_size += MUX_OTHERS_SIZE_HEADER_AAC + ((ENTRY_SIZE_AUDIO_STSZ + ENTRY_SIZE_AUDIO_STCO) * nb_audio_frames);
382 ffmpegmux->expected_trailer_size = exp_size;
383 ffmpegmux->nb_video_frames = nb_video_frames;
384 ffmpegmux->nb_audio_frames = nb_audio_frames;
388 #endif /* GST_EXT_FFMUX_ENHANCEMENT */
391 gst_ffmpegmux_base_init (gpointer g_class)
393 GstFFMpegMuxClass *klass = (GstFFMpegMuxClass *) g_class;
394 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
395 GstPadTemplate *videosinktempl, *audiosinktempl, *srctempl;
396 AVOutputFormat *in_plugin;
397 GstCaps *srccaps, *audiosinkcaps, *videosinkcaps;
398 enum CodecID *video_ids = NULL, *audio_ids = NULL;
399 gchar *longname, *description;
400 const char *replacement;
401 gboolean is_formatter;
404 (AVOutputFormat *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
405 GST_FFMUX_PARAMS_QDATA);
406 g_assert (in_plugin != NULL);
408 /* construct the element details struct */
409 replacement = gst_ffmpegmux_get_replacement (in_plugin->name);
410 is_formatter = gst_ffmpegmux_is_formatter (in_plugin->name);
411 if (replacement != NULL) {
413 g_strdup_printf ("FFmpeg %s %s (not recommended, use %s instead)",
414 in_plugin->long_name, is_formatter ? "formatter" : "muxer",
417 g_strdup_printf ("FFmpeg %s %s (not recommended, use %s instead)",
418 in_plugin->long_name, is_formatter ? "formatter" : "muxer",
421 longname = g_strdup_printf ("FFmpeg %s %s", in_plugin->long_name,
422 is_formatter ? "formatter" : "muxer");
423 description = g_strdup_printf ("FFmpeg %s %s", in_plugin->long_name,
424 is_formatter ? "formatter" : "muxer");
426 gst_element_class_set_details_simple (element_class, longname,
427 is_formatter ? "Formatter/Metadata" : "Codec/Muxer", description,
428 "Wim Taymans <wim.taymans@chello.be>, "
429 "Ronald Bultje <rbultje@ronald.bitfreak.net>");
431 g_free (description);
433 /* Try to find the caps that belongs here */
434 srccaps = gst_ffmpeg_formatid_to_caps (in_plugin->name);
436 GST_DEBUG ("Couldn't get source caps for muxer '%s', skipping format",
441 if (!gst_ffmpeg_formatid_get_codecids (in_plugin->name,
442 &video_ids, &audio_ids, in_plugin)) {
443 gst_caps_unref (srccaps);
445 ("Couldn't get sink caps for muxer '%s'. Most likely because no input format mapping exists.",
450 videosinkcaps = video_ids ? gst_ffmpegmux_get_id_caps (video_ids) : NULL;
451 audiosinkcaps = audio_ids ? gst_ffmpegmux_get_id_caps (audio_ids) : NULL;
453 /* fix up allowed caps for some muxers */
454 /* FIXME : This should be in gstffmpegcodecmap.c ! */
455 if (strcmp (in_plugin->name, "flv") == 0) {
456 const gint rates[] = { 44100, 22050, 11025 };
458 gst_ffmpeg_mux_simple_caps_set_int_list (audiosinkcaps, "rate", 3, rates);
459 } else if (strcmp (in_plugin->name, "gif") == 0) {
461 gst_caps_unref (videosinkcaps);
464 gst_caps_from_string ("video/x-raw-rgb, bpp=(int)24, depth=(int)24");
468 srctempl = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, srccaps);
469 gst_element_class_add_pad_template (element_class, srctempl);
472 audiosinktempl = gst_pad_template_new ("audio_%d",
473 GST_PAD_SINK, GST_PAD_REQUEST, audiosinkcaps);
474 gst_element_class_add_pad_template (element_class, audiosinktempl);
478 videosinktempl = gst_pad_template_new ("video_%d",
479 GST_PAD_SINK, GST_PAD_REQUEST, videosinkcaps);
480 gst_element_class_add_pad_template (element_class, videosinktempl);
484 klass->in_plugin = in_plugin;
488 gst_ffmpegmux_class_init (GstFFMpegMuxClass * klass)
490 GObjectClass *gobject_class;
491 GstElementClass *gstelement_class;
493 gobject_class = (GObjectClass *) klass;
494 gstelement_class = (GstElementClass *) klass;
496 parent_class = g_type_class_peek_parent (klass);
498 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_ffmpegmux_set_property);
499 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_ffmpegmux_get_property);
501 g_object_class_install_property (gobject_class, PROP_PRELOAD,
502 g_param_spec_int ("preload", "preload",
503 "Set the initial demux-decode delay (in microseconds)", 0, G_MAXINT,
504 0, G_PARAM_READWRITE));
506 g_object_class_install_property (gobject_class, PROP_MAXDELAY,
507 g_param_spec_int ("maxdelay", "maxdelay",
508 "Set the maximum demux-decode delay (in microseconds)", 0, G_MAXINT,
509 0, G_PARAM_READWRITE));
511 gstelement_class->request_new_pad = gst_ffmpegmux_request_new_pad;
512 gstelement_class->change_state = gst_ffmpegmux_change_state;
513 gobject_class->finalize = gst_ffmpegmux_finalize;
515 #ifdef GST_EXT_FFMUX_ENHANCEMENT
516 gstelement_class->release_pad = gst_ffmpegmux_release_pad;
519 g_object_class_install_property (gobject_class, PROP_EXPECTED_TRAILER_SIZE,
520 g_param_spec_uint ("expected-trailer-size", "Expected Trailer Size",
521 "Expected trailer size (bytes)",
522 0, G_MAXUINT, 0, G_PARAM_READABLE));
523 g_object_class_install_property (gobject_class, PROP_NUMBER_VIDEO_FRAMES,
524 g_param_spec_uint ("number-video-frames", "Number of video frames",
525 "Current number of video frames",
526 0, G_MAXUINT, 0, G_PARAM_READABLE));
527 g_object_class_install_property (gobject_class, PROP_NUMBER_AUDIO_FRAMES,
528 g_param_spec_uint ("number-audio-frames", "Number of audio frames",
529 "Current number of audio frames",
530 0, G_MAXUINT, 0, G_PARAM_READABLE));
535 gst_ffmpegmux_init (GstFFMpegMux * ffmpegmux, GstFFMpegMuxClass * g_class)
537 GstElementClass *klass = GST_ELEMENT_CLASS (g_class);
538 GstFFMpegMuxClass *oclass = (GstFFMpegMuxClass *) klass;
539 GstPadTemplate *templ = gst_element_class_get_pad_template (klass, "src");
541 ffmpegmux->srcpad = gst_pad_new_from_template (templ, "src");
542 gst_pad_set_caps (ffmpegmux->srcpad, gst_pad_template_get_caps (templ));
543 gst_element_add_pad (GST_ELEMENT (ffmpegmux), ffmpegmux->srcpad);
545 ffmpegmux->collect = gst_collect_pads_new ();
546 gst_collect_pads_set_function (ffmpegmux->collect,
547 (GstCollectPadsFunction) gst_ffmpegmux_collected, ffmpegmux);
549 ffmpegmux->context = g_new0 (AVFormatContext, 1);
550 ffmpegmux->context->oformat = oclass->in_plugin;
551 ffmpegmux->context->nb_streams = 0;
552 g_snprintf (ffmpegmux->context->filename,
553 sizeof (ffmpegmux->context->filename),
554 "gstreamer://%p", ffmpegmux->srcpad);
555 ffmpegmux->opened = FALSE;
557 ffmpegmux->videopads = 0;
558 ffmpegmux->audiopads = 0;
559 ffmpegmux->preload = 0;
560 ffmpegmux->max_delay = 0;
562 #ifdef GST_EXT_FFMUX_ENHANCEMENT
563 ffmpegmux->expected_trailer_size = 0;
564 ffmpegmux->nb_video_frames = 0;
565 ffmpegmux->nb_audio_frames = 0;
566 #endif /* GST_EXT_FFMUX_ENHANCEMENT */
569 #ifdef GST_EXT_FFMUX_ENHANCEMENT
571 gst_ffmpegmux_release_pad (GstElement * element, GstPad * pad)
573 GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) element;
574 GstFFMpegMuxPad *collect_pad;
577 collect_pad = (GstFFMpegMuxPad *) gst_pad_get_element_private (pad);
579 GST_DEBUG("Release requested pad[%s:%s]", GST_DEBUG_PAD_NAME(pad));
580 st = ffmpegmux->context->streams[collect_pad->padnum];
585 if(st->codec->codec_type == CODEC_TYPE_VIDEO)
587 ffmpegmux->videopads--;
591 ffmpegmux->audiopads--;
595 ffmpegmux->context->nb_streams--;
598 gst_collect_pads_remove_pad (ffmpegmux->collect, pad);
599 gst_element_remove_pad (element, pad);
604 gst_ffmpegmux_set_property (GObject * object, guint prop_id,
605 const GValue * value, GParamSpec * pspec)
609 src = (GstFFMpegMux *) object;
613 src->preload = g_value_get_int (value);
616 src->max_delay = g_value_get_int (value);
619 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
625 gst_ffmpegmux_get_property (GObject * object, guint prop_id, GValue * value,
630 src = (GstFFMpegMux *) object;
634 g_value_set_int (value, src->preload);
637 g_value_set_int (value, src->max_delay);
639 #ifdef GST_EXT_FFMUX_ENHANCEMENT
640 case PROP_EXPECTED_TRAILER_SIZE:
641 g_value_set_uint (value, src->expected_trailer_size);
643 case PROP_NUMBER_VIDEO_FRAMES:
644 g_value_set_uint (value, src->nb_video_frames);
646 case PROP_NUMBER_AUDIO_FRAMES:
647 g_value_set_uint (value, src->nb_audio_frames);
649 #endif /* GST_EXT_FFMUX_ENHANCEMENT */
651 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
658 gst_ffmpegmux_finalize (GObject * object)
660 GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) object;
662 g_free (ffmpegmux->context);
663 gst_object_unref (ffmpegmux->collect);
665 if (G_OBJECT_CLASS (parent_class)->finalize)
666 G_OBJECT_CLASS (parent_class)->finalize (object);
670 gst_ffmpegmux_request_new_pad (GstElement * element,
671 GstPadTemplate * templ, const gchar * name)
673 GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) element;
674 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
675 GstFFMpegMuxPad *collect_pad;
680 gint bitrate = 0, framesize = 0;
682 g_return_val_if_fail (templ != NULL, NULL);
683 g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
684 g_return_val_if_fail (ffmpegmux->opened == FALSE, NULL);
686 /* figure out a name that *we* like */
687 if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
688 padname = g_strdup_printf ("video_%d", ffmpegmux->videopads++);
689 type = CODEC_TYPE_VIDEO;
692 } else if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
693 padname = g_strdup_printf ("audio_%d", ffmpegmux->audiopads++);
694 type = CODEC_TYPE_AUDIO;
695 bitrate = 285 * 1024;
697 g_warning ("ffmux: unknown pad template!");
702 pad = gst_pad_new_from_template (templ, padname);
703 collect_pad = (GstFFMpegMuxPad *)
704 gst_collect_pads_add_pad (ffmpegmux->collect, pad,
705 sizeof (GstFFMpegMuxPad));
706 collect_pad->padnum = ffmpegmux->context->nb_streams;
708 /* small hack to put our own event pad function and chain up to collect pad */
709 ffmpegmux->event_function = GST_PAD_EVENTFUNC (pad);
710 gst_pad_set_event_function (pad,
711 GST_DEBUG_FUNCPTR (gst_ffmpegmux_sink_event));
713 gst_pad_set_setcaps_function (pad, GST_DEBUG_FUNCPTR (gst_ffmpegmux_setcaps));
714 gst_element_add_pad (element, pad);
716 /* AVStream needs to be created */
717 st = av_new_stream (ffmpegmux->context, collect_pad->padnum);
718 st->codec->codec_type = type;
719 st->codec->codec_id = CODEC_ID_NONE; /* this is a check afterwards */
720 st->stream_copy = 1; /* we're not the actual encoder */
721 st->codec->bit_rate = bitrate;
722 st->codec->frame_size = framesize;
723 /* we fill in codec during capsnego */
725 /* we love debug output (c) (tm) (r) */
726 GST_DEBUG ("Created %s pad for ffmux_%s element",
727 padname, ((GstFFMpegMuxClass *) klass)->in_plugin->name);
734 * gst_ffmpegmux_setcaps
740 * Returns: #TRUE on success.
743 gst_ffmpegmux_setcaps (GstPad * pad, GstCaps * caps)
745 GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) (gst_pad_get_parent (pad));
746 GstFFMpegMuxPad *collect_pad;
749 collect_pad = (GstFFMpegMuxPad *) gst_pad_get_element_private (pad);
751 st = ffmpegmux->context->streams[collect_pad->padnum];
752 ffmpegmux->context->preload = ffmpegmux->preload;
753 ffmpegmux->context->max_delay = ffmpegmux->max_delay;
755 /* for the format-specific guesses, we'll go to
756 * our famous codec mapper */
757 if (gst_ffmpeg_caps_to_codecid (caps, st->codec) == CODEC_ID_NONE)
760 /* copy over the aspect ratios, ffmpeg expects the stream aspect to match the
762 st->sample_aspect_ratio = st->codec->sample_aspect_ratio;
764 #ifdef GST_EXT_FFMUX_ENHANCEMENT
765 /* ref counting bug fix */
766 gst_object_unref(ffmpegmux);
768 GST_LOG_OBJECT (pad, "accepted caps %" GST_PTR_FORMAT, caps);
774 #ifdef GST_EXT_FFMUX_ENHANCEMENT
775 /* ref counting bug fix */
776 gst_object_unref(ffmpegmux);
778 GST_LOG_OBJECT (pad, "rejecting caps %" GST_PTR_FORMAT, caps);
785 gst_ffmpegmux_sink_event (GstPad * pad, GstEvent * event)
787 GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) gst_pad_get_parent (pad);
790 switch (GST_EVENT_TYPE (event)) {
793 GstTagSetter *setter = GST_TAG_SETTER (ffmpegmux);
794 const GstTagMergeMode mode = gst_tag_setter_get_tag_merge_mode (setter);
796 gst_event_parse_tag (event, &taglist);
797 gst_tag_setter_merge_tags (setter, taglist, mode);
804 /* chaining up to collectpads default event function */
805 res = ffmpegmux->event_function (pad, event);
807 gst_object_unref (ffmpegmux);
812 gst_ffmpegmux_collected (GstCollectPads * pads, gpointer user_data)
814 GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) user_data;
816 GstFFMpegMuxPad *best_pad;
817 GstClockTime best_time;
818 const GstTagList *tags;
820 /* open "file" (gstreamer protocol to next element) */
821 if (!ffmpegmux->opened) {
822 int open_flags = URL_WRONLY;
824 /* we do need all streams to have started capsnego,
825 * or things will go horribly wrong */
826 for (collected = ffmpegmux->collect->data; collected;
827 collected = g_slist_next (collected)) {
828 GstFFMpegMuxPad *collect_pad = (GstFFMpegMuxPad *) collected->data;
829 AVStream *st = ffmpegmux->context->streams[collect_pad->padnum];
831 /* check whether the pad has successfully completed capsnego */
832 if (st->codec->codec_id == CODEC_ID_NONE) {
833 GST_ELEMENT_ERROR (ffmpegmux, CORE, NEGOTIATION, (NULL),
834 ("no caps set on stream %d (%s)", collect_pad->padnum,
835 (st->codec->codec_type == CODEC_TYPE_VIDEO) ?
837 return GST_FLOW_ERROR;
839 /* set framerate for audio */
840 if (st->codec->codec_type == CODEC_TYPE_AUDIO) {
841 switch (st->codec->codec_id) {
842 case CODEC_ID_PCM_S16LE:
843 case CODEC_ID_PCM_S16BE:
844 case CODEC_ID_PCM_U16LE:
845 case CODEC_ID_PCM_U16BE:
846 case CODEC_ID_PCM_S8:
847 case CODEC_ID_PCM_U8:
848 st->codec->frame_size = 1;
854 /* FIXME : This doesn't work for RAW AUDIO...
855 * in fact I'm wondering if it even works for any kind of audio... */
856 buffer = gst_collect_pads_peek (ffmpegmux->collect,
857 (GstCollectData *) collect_pad);
859 st->codec->frame_size =
860 st->codec->sample_rate *
861 GST_BUFFER_DURATION (buffer) / GST_SECOND;
862 gst_buffer_unref (buffer);
870 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (ffmpegmux));
875 /* get the interesting ones */
876 if (gst_tag_list_get_string (tags, GST_TAG_TITLE, &s)) {
877 strncpy (ffmpegmux->context->title, s,
878 sizeof (ffmpegmux->context->title));
880 if (gst_tag_list_get_string (tags, GST_TAG_ARTIST, &s)) {
881 strncpy (ffmpegmux->context->author, s,
882 sizeof (ffmpegmux->context->author));
884 if (gst_tag_list_get_string (tags, GST_TAG_COPYRIGHT, &s)) {
885 strncpy (ffmpegmux->context->copyright, s,
886 sizeof (ffmpegmux->context->copyright));
888 if (gst_tag_list_get_string (tags, GST_TAG_COMMENT, &s)) {
889 strncpy (ffmpegmux->context->comment, s,
890 sizeof (ffmpegmux->context->comment));
892 if (gst_tag_list_get_string (tags, GST_TAG_ALBUM, &s)) {
893 strncpy (ffmpegmux->context->album, s,
894 sizeof (ffmpegmux->context->album));
896 if (gst_tag_list_get_string (tags, GST_TAG_GENRE, &s)) {
897 strncpy (ffmpegmux->context->genre, s,
898 sizeof (ffmpegmux->context->genre));
900 if (gst_tag_list_get_int (tags, GST_TAG_TRACK_NUMBER, &i)) {
901 ffmpegmux->context->track = i;
905 /* set the streamheader flag for gstffmpegprotocol if codec supports it */
906 if (!strcmp (ffmpegmux->context->oformat->name, "flv")) {
907 open_flags |= GST_FFMPEG_URL_STREAMHEADER;
910 if (url_fopen (&ffmpegmux->context->pb,
911 ffmpegmux->context->filename, open_flags) < 0) {
912 GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, TOO_LAZY, (NULL),
913 ("Failed to open stream context in ffmux"));
914 return GST_FLOW_ERROR;
917 if (av_set_parameters (ffmpegmux->context, NULL) < 0) {
918 GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, INIT, (NULL),
919 ("Failed to initialize muxer"));
920 return GST_FLOW_ERROR;
923 /* now open the mux format */
924 if (av_write_header (ffmpegmux->context) < 0) {
925 GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, SETTINGS, (NULL),
926 ("Failed to write file header - check codec settings"));
927 return GST_FLOW_ERROR;
930 /* we're now opened */
931 ffmpegmux->opened = TRUE;
933 /* flush the header so it will be used as streamheader */
934 put_flush_packet (ffmpegmux->context->pb);
937 /* take the one with earliest timestamp,
938 * and push it forward */
940 best_time = GST_CLOCK_TIME_NONE;
941 for (collected = ffmpegmux->collect->data; collected;
942 collected = g_slist_next (collected)) {
943 GstFFMpegMuxPad *collect_pad = (GstFFMpegMuxPad *) collected->data;
944 GstBuffer *buffer = gst_collect_pads_peek (ffmpegmux->collect,
945 (GstCollectData *) collect_pad);
947 /* if there's no buffer, just continue */
948 if (buffer == NULL) {
952 /* if we have no buffer yet, just use the first one */
953 if (best_pad == NULL) {
954 best_pad = collect_pad;
955 best_time = GST_BUFFER_TIMESTAMP (buffer);
959 /* if we do have one, only use this one if it's older */
960 if (GST_BUFFER_TIMESTAMP (buffer) < best_time) {
961 best_time = GST_BUFFER_TIMESTAMP (buffer);
962 best_pad = collect_pad;
966 gst_buffer_unref (buffer);
968 /* Mux buffers with invalid timestamp first */
969 if (!GST_CLOCK_TIME_IS_VALID (best_time))
973 /* now handle the buffer, or signal EOS if we have
975 if (best_pad != NULL) {
978 gboolean need_free = FALSE;
979 #ifdef GST_EXT_FFMUX_ENHANCEMENT
980 av_init_packet(&pkt);
981 pkt.is_mux = 1; // true
983 /* push out current buffer */
984 buf = gst_collect_pads_pop (ffmpegmux->collect,
985 (GstCollectData *) best_pad);
987 ffmpegmux->context->streams[best_pad->padnum]->codec->frame_number++;
990 #ifdef GST_EXT_FFMUX_ENHANCEMENT
991 if(ffmpegmux->context->streams[best_pad->padnum]->codec->codec_type == CODEC_TYPE_VIDEO) {
992 pkt.pts = GST_TIME_AS_MSECONDS(GST_BUFFER_TIMESTAMP(buf));
994 pkt.pts = gst_ffmpeg_time_gst_to_ff(GST_BUFFER_TIMESTAMP(buf),
995 ffmpegmux->context->streams[best_pad->padnum]->time_base);
998 pkt.pts = gst_ffmpeg_time_gst_to_ff (GST_BUFFER_TIMESTAMP (buf),
999 ffmpegmux->context->streams[best_pad->padnum]->time_base);
1003 if (strcmp (ffmpegmux->context->oformat->name, "gif") == 0) {
1004 AVStream *st = ffmpegmux->context->streams[best_pad->padnum];
1008 pkt.size = st->codec->width * st->codec->height * 3;
1009 pkt.data = g_malloc (pkt.size);
1011 dst.data[0] = pkt.data;
1014 dst.linesize[0] = st->codec->width * 3;
1016 gst_ffmpeg_avpicture_fill (&src, GST_BUFFER_DATA (buf),
1017 PIX_FMT_RGB24, st->codec->width, st->codec->height);
1019 av_picture_copy (&dst, &src, PIX_FMT_RGB24,
1020 st->codec->width, st->codec->height);
1022 pkt.data = GST_BUFFER_DATA (buf);
1023 pkt.size = GST_BUFFER_SIZE (buf);
1026 pkt.stream_index = best_pad->padnum;
1029 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
1030 pkt.flags |= PKT_FLAG_KEY;
1032 #ifdef GST_EXT_FFMUX_ENHANCEMENT
1033 if (ffmpegmux->context->streams[best_pad->padnum]->codec->codec_type == CODEC_TYPE_VIDEO) {
1034 static int last_duration = -1;
1035 static int64_t last_dts = -1;
1036 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1037 pkt.duration = GST_TIME_AS_MSECONDS(GST_BUFFER_DURATION(buf));
1042 if (last_dts == -1) {
1044 ffmpegmux->context->streams[best_pad->padnum]->codec->stts_count++;
1046 /* check real duration : current dts - last dts */
1047 if (last_duration != (pkt.dts - last_dts)) {
1048 last_duration = pkt.dts - last_dts;
1049 ffmpegmux->context->streams[best_pad->padnum]->codec->stts_count++;
1054 if (!GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
1055 ffmpegmux->context->streams[best_pad->padnum]->codec->i_frame_number++;
1058 if (GST_BUFFER_DURATION_IS_VALID(buf)) {
1060 gst_ffmpeg_time_gst_to_ff (GST_BUFFER_DURATION (buf),
1061 ffmpegmux->context->streams[best_pad->padnum]->time_base);
1067 update_expected_trailer_size(ffmpegmux);
1069 if (GST_BUFFER_DURATION_IS_VALID (buf))
1071 gst_ffmpeg_time_gst_to_ff (GST_BUFFER_DURATION (buf),
1072 ffmpegmux->context->streams[best_pad->padnum]->time_base);
1076 av_write_frame (ffmpegmux->context, &pkt);
1077 gst_buffer_unref (buf);
1082 av_write_trailer (ffmpegmux->context);
1083 ffmpegmux->opened = FALSE;
1084 put_flush_packet (ffmpegmux->context->pb);
1085 url_fclose (ffmpegmux->context->pb);
1086 gst_pad_push_event (ffmpegmux->srcpad, gst_event_new_eos ());
1087 return GST_FLOW_UNEXPECTED;
1093 static GstStateChangeReturn
1094 gst_ffmpegmux_change_state (GstElement * element, GstStateChange transition)
1097 GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) (element);
1099 switch (transition) {
1100 case GST_STATE_CHANGE_NULL_TO_READY:
1102 case GST_STATE_CHANGE_READY_TO_PAUSED:
1103 gst_collect_pads_start (ffmpegmux->collect);
1105 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1107 case GST_STATE_CHANGE_PAUSED_TO_READY:
1108 gst_collect_pads_stop (ffmpegmux->collect);
1114 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1116 switch (transition) {
1117 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1119 case GST_STATE_CHANGE_PAUSED_TO_READY:
1120 gst_tag_setter_reset_tags (GST_TAG_SETTER (ffmpegmux));
1121 if (ffmpegmux->opened) {
1122 ffmpegmux->opened = FALSE;
1123 url_fclose (ffmpegmux->context->pb);
1125 #ifdef GST_EXT_FFMUX_ENHANCEMENT
1127 for(i=0; i < ffmpegmux->context->nb_streams; i++)
1129 ffmpegmux->context->streams[i]->start_time = AV_NOPTS_VALUE;
1130 ffmpegmux->context->streams[i]->duration = AV_NOPTS_VALUE;
1131 ffmpegmux->context->streams[i]->cur_dts = AV_NOPTS_VALUE;
1136 case GST_STATE_CHANGE_READY_TO_NULL:
1146 gst_ffmpegmux_get_id_caps (enum CodecID *id_list)
1151 caps = gst_caps_new_empty ();
1152 for (i = 0; id_list[i] != CODEC_ID_NONE; i++) {
1153 if ((t = gst_ffmpeg_codecid_to_caps (id_list[i], NULL, TRUE)))
1154 gst_caps_append (caps, t);
1156 if (gst_caps_is_empty (caps)) {
1157 gst_caps_unref (caps);
1164 /* set a list of integer values on the caps, e.g. for sample rates */
1166 gst_ffmpeg_mux_simple_caps_set_int_list (GstCaps * caps, const gchar * field,
1167 guint num, const gint * values)
1169 GValue list = { 0, };
1170 GValue val = { 0, };
1173 g_return_if_fail (GST_CAPS_IS_SIMPLE (caps));
1175 g_value_init (&list, GST_TYPE_LIST);
1176 g_value_init (&val, G_TYPE_INT);
1178 for (i = 0; i < num; ++i) {
1179 g_value_set_int (&val, values[i]);
1180 gst_value_list_append_value (&list, &val);
1183 gst_structure_set_value (gst_caps_get_structure (caps, 0), field, &list);
1185 g_value_unset (&val);
1186 g_value_unset (&list);
1190 gst_ffmpegmux_register (GstPlugin * plugin)
1192 GTypeInfo typeinfo = {
1193 sizeof (GstFFMpegMuxClass),
1194 (GBaseInitFunc) gst_ffmpegmux_base_init,
1196 (GClassInitFunc) gst_ffmpegmux_class_init,
1199 sizeof (GstFFMpegMux),
1201 (GInstanceInitFunc) gst_ffmpegmux_init,
1203 static const GInterfaceInfo tag_setter_info = {
1207 AVOutputFormat *in_plugin;
1209 in_plugin = av_oformat_next (NULL);
1211 GST_LOG ("Registering muxers");
1216 GstRank rank = GST_RANK_MARGINAL;
1218 if ((!strncmp (in_plugin->name, "u16", 3)) ||
1219 (!strncmp (in_plugin->name, "s16", 3)) ||
1220 (!strncmp (in_plugin->name, "u24", 3)) ||
1221 (!strncmp (in_plugin->name, "s24", 3)) ||
1222 (!strncmp (in_plugin->name, "u8", 2)) ||
1223 (!strncmp (in_plugin->name, "s8", 2)) ||
1224 (!strncmp (in_plugin->name, "u32", 3)) ||
1225 (!strncmp (in_plugin->name, "s32", 3)) ||
1226 (!strncmp (in_plugin->name, "f32", 3)) ||
1227 (!strncmp (in_plugin->name, "f64", 3)) ||
1228 (!strncmp (in_plugin->name, "raw", 3)) ||
1229 (!strncmp (in_plugin->name, "crc", 3)) ||
1230 (!strncmp (in_plugin->name, "null", 4)) ||
1231 (!strncmp (in_plugin->name, "gif", 3)) ||
1232 (!strncmp (in_plugin->name, "frame", 5)) ||
1233 (!strncmp (in_plugin->name, "image", 5)) ||
1234 (!strncmp (in_plugin->name, "mulaw", 5)) ||
1235 (!strncmp (in_plugin->name, "alaw", 4)) ||
1236 (!strncmp (in_plugin->name, "h26", 3)) ||
1237 (!strncmp (in_plugin->name, "rtp", 3)) ||
1238 (!strncmp (in_plugin->name, "ass", 3))
1240 GST_LOG ("Ignoring muxer %s", in_plugin->name);
1244 if ((!strncmp (in_plugin->long_name, "raw ", 4))) {
1245 GST_LOG ("Ignoring raw muxer %s", in_plugin->name);
1249 if (gst_ffmpegmux_get_replacement (in_plugin->name))
1250 rank = GST_RANK_NONE;
1252 /* FIXME : We need a fast way to know whether we have mappings for this
1255 /* construct the type */
1256 type_name = g_strdup_printf ("ffmux_%s", in_plugin->name);
1266 type = g_type_from_name (type_name);
1269 /* create the type now */
1270 type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
1271 g_type_set_qdata (type, GST_FFMUX_PARAMS_QDATA, (gpointer) in_plugin);
1272 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
1275 if (!gst_element_register (plugin, type_name, rank, type)) {
1283 in_plugin = av_oformat_next (in_plugin);
1286 GST_LOG ("Finished registering muxers");