__VA_ARGS__, NULL) \
: \
gst_caps_new_simple (mimetype, \
+ "rate", GST_TYPE_INT_RANGE, 8000, 96000, \
+ "channels", GST_TYPE_INT_RANGE, 1, 2, \
__VA_ARGS__, NULL)
/* Convert a FFMPEG codec ID and optional AVCodecContext
return caps;
}
+gboolean
+gst_ffmpeg_formatid_get_codecids (const gchar *format_name,
+ enum CodecID ** video_codec_list, enum CodecID ** audio_codec_list)
+{
+ if (!strcmp (format_name, "mp4")) {
+ static enum CodecID mp4_video_list[] = { CODEC_ID_MPEG4, CODEC_ID_NONE };
+ static enum CodecID mp4_audio_list[] = { CODEC_ID_AAC, CODEC_ID_NONE };
+
+ *video_codec_list = mp4_video_list;
+ *audio_codec_list = mp4_audio_list;
+ } else {
+ GST_WARNING ("Format %s not found", format_name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/* Convert a GstCaps to a FFMPEG codec ID. Size et all
* are omitted, that can be queried by the user itself,
* we're not eating the GstCaps or anything
} else if (!strcmp (mimetype, "video/x-dv")) {
gboolean sys_strm;
- if (!gst_structure_get_boolean (structure, "systemstream", &sys_strm) &&
+ if (gst_structure_get_boolean (structure, "systemstream", &sys_strm) &&
!sys_strm) {
id = CODEC_ID_DVVIDEO;
video = TRUE;
gboolean sys_strm;
gint mpegversion;
- if (!gst_structure_get_boolean (structure, "systemstream", &sys_strm) &&
- !gst_structure_get_int (structure, "mpegversion", &mpegversion) &&
+ if (gst_structure_get_boolean (structure, "systemstream", &sys_strm) &&
+ gst_structure_get_int (structure, "mpegversion", &mpegversion) &&
!sys_strm) {
switch (mpegversion) {
case 1:
switch (mpegversion) {
case 2: /* ffmpeg uses faad for both... */
case 4:
- id = CODEC_ID_MPEG4AAC;
+ id = CODEC_ID_AAC;
break;
case 1:
if (gst_structure_get_int (structure, "layer", &layer)) {
AVFormatContext *context;
gboolean opened;
+ GstTagList *tags;
+
GstPad *sinkpads[MAX_STREAMS];
gint videopads, audiopads;
GstBuffer *bufferqueue[MAX_STREAMS];
ffmpegmux->videopads = 0;
ffmpegmux->audiopads = 0;
+
+ ffmpegmux->tags = NULL;
}
static void
/*g_return_val_if_fail (ffmpegmux->opened == FALSE,
GST_PAD_LINK_REFUSED); */
-
for (i = 0; i < ffmpegmux->context->nb_streams; i++) {
if (pad == ffmpegmux->sinkpads[i]) {
break;
ffmpegmux->eos[i] = TRUE;
gst_event_unref (event);
break;
+ case GST_EVENT_TAG:
+ if (ffmpegmux->tags) {
+ gst_tag_list_insert (ffmpegmux->tags,
+ gst_event_tag_get_list (event), GST_TAG_MERGE_PREPEND);
+ } else {
+ ffmpegmux->tags =
+ gst_tag_list_copy (gst_event_tag_get_list (event));
+ }
+ gst_event_unref (event);
+ break;
default:
gst_pad_event_default (pad, event);
break;
/* open "file" (gstreamer protocol to next element) */
if (!ffmpegmux->opened) {
+ const GstTagList *iface_tags;
+
/* we do need all streams to have started capsnego,
* or things will go horribly wrong */
for (i = 0; i < ffmpegmux->context->nb_streams; i++) {
"video" : "audio"));
return;
}
+ if (st->codec.codec_type == CODEC_TYPE_AUDIO) {
+ st->codec.frame_size =
+ st->codec.sample_rate *
+ GST_BUFFER_DURATION (ffmpegmux->bufferqueue[i]) / GST_SECOND;
+ }
+ }
+
+ /* tags */
+ iface_tags = gst_tag_setter_get_list (GST_TAG_SETTER (ffmpegmux));
+ if (ffmpegmux->tags || iface_tags) {
+ GstTagList *tags;
+ gint i;
+ gchar *s;
+
+ if (iface_tags && ffmpegmux->tags) {
+ gst_tag_list_merge (iface_tags, ffmpegmux->tags,
+ GST_TAG_MERGE_APPEND);
+ } else if (iface_tags) {
+ tags = gst_tag_list_copy (iface_tags);
+ } else {
+ tags = gst_tag_list_copy (ffmpegmux->tags);
+ }
+
+ /* get the interesting ones */
+ if (gst_tag_list_get_string (tags, GST_TAG_TITLE, &s)) {
+ strncpy (ffmpegmux->context->title, s,
+ sizeof (ffmpegmux->context->title));
+ }
+ if (gst_tag_list_get_string (tags, GST_TAG_ARTIST, &s)) {
+ strncpy (ffmpegmux->context->author, s,
+ sizeof (ffmpegmux->context->author));
+ }
+ if (gst_tag_list_get_string (tags, GST_TAG_COPYRIGHT, &s)) {
+ strncpy (ffmpegmux->context->copyright, s,
+ sizeof (ffmpegmux->context->copyright));
+ }
+ if (gst_tag_list_get_string (tags, GST_TAG_COMMENT, &s)) {
+ strncpy (ffmpegmux->context->comment, s,
+ sizeof (ffmpegmux->context->comment));
+ }
+ if (gst_tag_list_get_string (tags, GST_TAG_ALBUM, &s)) {
+ strncpy (ffmpegmux->context->album, s,
+ sizeof (ffmpegmux->context->album));
+ }
+ if (gst_tag_list_get_string (tags, GST_TAG_GENRE, &s)) {
+ strncpy (ffmpegmux->context->genre, s,
+ sizeof (ffmpegmux->context->genre));
+ }
+ if (gst_tag_list_get_uint (tags, GST_TAG_TRACK_NUMBER, &i)) {
+ ffmpegmux->context->track = i;
+ }
+ gst_tag_list_free (tags);
}
if (url_fopen (&ffmpegmux->context->pb,
return;
}
- if (av_set_parameters (ffmpegmux->context, NULL)) {
+ if (av_set_parameters (ffmpegmux->context, NULL) < 0) {
GST_ELEMENT_ERROR (element, LIBRARY, INIT, (NULL),
("Failed to initialize muxer"));
return;
ffmpegmux->opened = TRUE;
/* now open the mux format */
- av_write_header (ffmpegmux->context);
+ if (av_write_header (ffmpegmux->context) < 0) {
+ GST_ELEMENT_ERROR (element, LIBRARY, SETTINGS, (NULL),
+ ("Failed to write file header - check codec settings"));
+ return;
+ }
}
/* take the one with earliest timestamp,
/* set time */
pkt.pts = GST_BUFFER_TIMESTAMP (buf) * AV_TIME_BASE / GST_SECOND;
+ pkt.dts = pkt.pts;
pkt.data = GST_BUFFER_DATA (buf);
pkt.size = GST_BUFFER_SIZE (buf);
pkt.stream_index = bufnum;
switch (transition) {
case GST_STATE_PAUSED_TO_READY:
+ if (ffmpegmux->tags) {
+ gst_tag_list_free (ffmpegmux->tags);
+ ffmpegmux->tags = NULL;
+ }
if (ffmpegmux->opened) {
url_fclose (&ffmpegmux->context->pb);
ffmpegmux->opened = FALSE;
return GST_STATE_SUCCESS;
}
+GstCaps *
+gst_ffmpegmux_get_id_caps (enum CodecID * id_list)
+{
+ GstCaps *caps, *t;
+ gint i;
+
+ caps = gst_caps_new_empty ();
+ for (i = 0; id_list[i] != CODEC_ID_NONE; i++) {
+ if ((t = gst_ffmpeg_codecid_to_caps (id_list[i], NULL, TRUE)))
+ gst_caps_append (caps, t);
+ }
+
+ return caps;
+}
gboolean
gst_ffmpegmux_register (GstPlugin * plugin)
0,
(GInstanceInitFunc) gst_ffmpegmux_init,
};
+ static const GInterfaceInfo tag_setter_info = {
+ NULL, NULL, NULL
+ };
GType type;
AVOutputFormat *in_plugin;
GstFFMpegMuxClassParams *params;
gchar *type_name;
gchar *p;
GstCaps *srccaps, *audiosinkcaps, *videosinkcaps;
+ enum CodecID *video_ids = NULL, *audio_ids = NULL;
/* Try to find the caps that belongs here */
srccaps = gst_ffmpeg_formatid_to_caps (in_plugin->name);
if (!srccaps) {
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 */
- audiosinkcaps = gst_caps_new_empty ();
- videosinkcaps = gst_caps_new_empty ();
- for (in_codec = first_avcodec; in_codec != NULL; in_codec = in_codec->next) {
- GstCaps *temp = gst_ffmpeg_codecid_to_caps (in_codec->id, NULL, TRUE);
-
- if (!temp) {
- continue;
- }
- switch (in_codec->type) {
- case CODEC_TYPE_VIDEO:
- gst_caps_append (videosinkcaps, temp);
- break;
- case CODEC_TYPE_AUDIO:
- gst_caps_append (audiosinkcaps, temp);
- break;
- default:
- gst_caps_free (temp);
- break;
- }
+ if (!gst_ffmpeg_formatid_get_codecids (in_plugin->name,
+ &video_ids, &audio_ids)) {
+ gst_caps_free (srccaps);
+ goto next;
}
+ videosinkcaps = video_ids ? gst_ffmpegmux_get_id_caps (video_ids) : NULL;
+ audiosinkcaps = audio_ids ? gst_ffmpegmux_get_id_caps (audio_ids) : NULL;
/* construct the type */
type_name = g_strdup_printf ("ffmux_%s", in_plugin->name);
/* if it's already registered, drop it */
if (g_type_from_name (type_name)) {
g_free (type_name);
+ gst_caps_free (srccaps);
+ if (audiosinkcaps)
+ gst_caps_free (audiosinkcaps);
+ if (videosinkcaps)
+ gst_caps_free (videosinkcaps);
goto next;
}
/* create the type now */
type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
+ g_type_add_interface_static (type, GST_TYPE_TAG_SETTER,
+ &tag_setter_info);
if (!gst_element_register (plugin, type_name, GST_RANK_NONE, type)) {
g_free (type_name);
+ gst_caps_free (srccaps);
+ if (audiosinkcaps)
+ gst_caps_free (audiosinkcaps);
+ if (videosinkcaps)
+ gst_caps_free (videosinkcaps);
return FALSE;
}
{
GstPad *pad;
- int flags;
GstByteStream *bs;
gboolean eos;
};
GST_LOG ("Opening %s", filename);
info = g_new0 (GstProtocolInfo, 1);
- info->flags = flags;
/* we don't support R/W together */
if (flags != URL_RDONLY && flags != URL_WRONLY) {
h->priv_data = (void *) info;
h->is_streamed = FALSE;
- h->flags = 0;
h->max_packet_size = 0;
return 0;
info = (GstProtocolInfo *) h->priv_data;
- g_return_val_if_fail (info->flags == URL_RDONLY, AVERROR_IO);
+ g_return_val_if_fail (h->flags == URL_RDONLY, AVERROR_IO);
bs = info->bs;
info = (GstProtocolInfo *) h->priv_data;
- g_return_val_if_fail (info->flags == URL_WRONLY, -EIO);
+ g_return_val_if_fail (h->flags == URL_WRONLY, -EIO);
/* create buffer and push data further */
outbuf = gst_buffer_new_and_alloc (size);
info = (GstProtocolInfo *) h->priv_data;
- /* get data (typefind hack) */
- if (gst_bytestream_tell (info->bs) != gst_bytestream_length (info->bs)) {
- gchar buf;
- gst_ffmpegdata_peek (h, &buf, 1);
- }
+ if (h->flags == URL_RDONLY) {
+ /* get data (typefind hack) */
+ if (gst_bytestream_tell (info->bs) != gst_bytestream_length (info->bs)) {
+ gchar buf;
+ gst_ffmpegdata_peek (h, &buf, 1);
+ }
- /* hack in ffmpeg to get filesize... */
- if (whence == SEEK_END && pos == -1)
- return gst_bytestream_length (info->bs) - 1;
- else if (whence == SEEK_END && pos == 0)
- return gst_bytestream_length (info->bs);
- /* another hack to get the current position... */
- else if (whence == SEEK_CUR && pos == 0)
- return gst_bytestream_tell (info->bs);
+ /* hack in ffmpeg to get filesize... */
+ if (whence == SEEK_END && pos == -1)
+ return gst_bytestream_length (info->bs) - 1;
+ else if (whence == SEEK_END && pos == 0)
+ return gst_bytestream_length (info->bs);
+ /* another hack to get the current position... */
+ else if (whence == SEEK_CUR && pos == 0)
+ return gst_bytestream_tell (info->bs);
+ }
switch (whence) {
case SEEK_SET:
break;
}
- switch (info->flags) {
+ switch (h->flags) {
case URL_RDONLY: {
GstEvent *event;
guint8 *data;
}
case URL_WRONLY:
- gst_pad_push (info->pad, GST_DATA (gst_event_new_seek (seek_type, pos)));
+ gst_pad_push (info->pad,
+ GST_DATA (gst_event_new_seek (seek_type | GST_FORMAT_BYTES, pos)));
/* this is screwy because there might be queues or scheduler-queued
* buffers... Argh! */
if (whence == SEEK_SET) {
GST_LOG ("Closing file");
- switch (info->flags) {
+ switch (h->flags) {
case URL_WRONLY:{
/* send EOS - that closes down the stream */
GstEvent *event = gst_event_new (GST_EVENT_EOS);