ext/ffmpeg/gstffmpeg.c: Enable muxers (only mp4 muxer for now).

Original commit message from CVS:
* ext/ffmpeg/gstffmpeg.c: (plugin_init):
Enable muxers (only mp4 muxer for now).
* ext/ffmpeg/gstffmpegcodecmap.c:
(gst_ffmpeg_formatid_get_codecids), (gst_ffmpeg_caps_to_codecid):
* ext/ffmpeg/gstffmpegcodecmap.h:
Fix a bunch of typos in codec-id lookup (false/true return value
mixup), add a codec-id list retrieval function (because ffmpeg
does not provide one). With that, we can make valid pad templates.
* ext/ffmpeg/gstffmpegmux.c: (gst_ffmpegmux_init),
(gst_ffmpegmux_connect), (gst_ffmpegmux_loop),
(gst_ffmpegmux_change_state), (gst_ffmpegmux_get_id_caps),
(gst_ffmpegmux_register):
Fix for whatever changed since I last tried this. Works for MP4
muxing.
* ext/ffmpeg/gstffmpegprotocol.c: (gst_ffmpegdata_open),
(gst_ffmpegdata_peek), (gst_ffmpegdata_write),
(gst_ffmpegdata_seek), (gst_ffmpegdata_close):
Update obviously-untested write code...
This commit is contained in:
Ronald S. Bultje 2005-03-14 15:27:43 +00:00
parent 35bb7be72c
commit c74a433d1d
6 changed files with 195 additions and 52 deletions

View file

@ -1,3 +1,24 @@
2005-03-14 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* ext/ffmpeg/gstffmpeg.c: (plugin_init):
Enable muxers (only mp4 muxer for now).
* ext/ffmpeg/gstffmpegcodecmap.c:
(gst_ffmpeg_formatid_get_codecids), (gst_ffmpeg_caps_to_codecid):
* ext/ffmpeg/gstffmpegcodecmap.h:
Fix a bunch of typos in codec-id lookup (false/true return value
mixup), add a codec-id list retrieval function (because ffmpeg
does not provide one). With that, we can make valid pad templates.
* ext/ffmpeg/gstffmpegmux.c: (gst_ffmpegmux_init),
(gst_ffmpegmux_connect), (gst_ffmpegmux_loop),
(gst_ffmpegmux_change_state), (gst_ffmpegmux_get_id_caps),
(gst_ffmpegmux_register):
Fix for whatever changed since I last tried this. Works for MP4
muxing.
* ext/ffmpeg/gstffmpegprotocol.c: (gst_ffmpegdata_open),
(gst_ffmpegdata_peek), (gst_ffmpegdata_write),
(gst_ffmpegdata_seek), (gst_ffmpegdata_close):
Update obviously-untested write code...
2005-03-13 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* ext/ffmpeg/gstffmpegenc.c: (gst_ffmpegenc_chain_video):

View file

@ -80,7 +80,7 @@ plugin_init (GstPlugin * plugin)
gst_ffmpegenc_register (plugin);
gst_ffmpegdec_register (plugin);
gst_ffmpegdemux_register (plugin);
/*gst_ffmpegmux_register (plugin); */
gst_ffmpegmux_register (plugin);
gst_ffmpegcsp_register (plugin);
register_protocol (&gstreamer_protocol);

View file

@ -107,6 +107,8 @@ gst_ffmpeg_set_palette (GstCaps *caps, AVCodecContext *context)
__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
@ -1325,6 +1327,24 @@ gst_ffmpeg_formatid_to_caps (const gchar * format_name)
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
@ -1398,7 +1418,7 @@ gst_ffmpeg_caps_to_codecid (const GstCaps * caps, AVCodecContext * context)
} 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;
@ -1419,8 +1439,8 @@ gst_ffmpeg_caps_to_codecid (const GstCaps * caps, AVCodecContext * context)
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:
@ -1468,7 +1488,7 @@ gst_ffmpeg_caps_to_codecid (const GstCaps * caps, AVCodecContext * context)
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)) {

View file

@ -87,6 +87,17 @@ gst_ffmpeg_caps_with_codectype (enum CodecType type,
GstCaps *
gst_ffmpeg_formatid_to_caps (const gchar *format_name);
/*
* _formatid_get_codecids () can be used to get the codecIDs
* (CODEC_ID_NONE-terminated list) that fit that specific
* output format.
*/
gboolean
gst_ffmpeg_formatid_get_codecids (const gchar *format_name,
enum CodecID ** video_codec_list,
enum CodecID ** audio_codec_list);
/*
* Since FFMpeg has such really cool and useful descriptions
* of its codecs, we use our own...
@ -118,7 +129,8 @@ gst_ffmpeg_avpicture_fill (AVPicture * picture,
*/
int
gst_ffmpeg_img_convert (AVPicture * dst, int dst_pix_fmt,
const AVPicture * src, int src_pix_fmt, int src_width, int src_height);
const AVPicture * src, int src_pix_fmt,
int src_width, int src_height);
#endif /* __GST_FFMPEG_CODECMAP_H__ */

View file

@ -45,6 +45,8 @@ struct _GstFFMpegMux
AVFormatContext *context;
gboolean opened;
GstTagList *tags;
GstPad *sinkpads[MAX_STREAMS];
gint videopads, audiopads;
GstBuffer *bufferqueue[MAX_STREAMS];
@ -189,6 +191,8 @@ gst_ffmpegmux_init (GstFFMpegMux * ffmpegmux)
ffmpegmux->videopads = 0;
ffmpegmux->audiopads = 0;
ffmpegmux->tags = NULL;
}
static void
@ -269,7 +273,6 @@ gst_ffmpegmux_connect (GstPad * pad, const GstCaps * caps)
/*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;
@ -318,6 +321,16 @@ gst_ffmpegmux_loop (GstElement * element)
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;
@ -330,6 +343,8 @@ gst_ffmpegmux_loop (GstElement * element)
/* 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++) {
@ -343,6 +358,58 @@ gst_ffmpegmux_loop (GstElement * element)
"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,
@ -352,7 +419,7 @@ gst_ffmpegmux_loop (GstElement * element)
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;
@ -362,7 +429,11 @@ gst_ffmpegmux_loop (GstElement * element)
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,
@ -401,6 +472,7 @@ gst_ffmpegmux_loop (GstElement * element)
/* 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;
@ -430,6 +502,10 @@ gst_ffmpegmux_change_state (GstElement * element)
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;
@ -443,6 +519,20 @@ gst_ffmpegmux_change_state (GstElement * element)
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)
@ -458,6 +548,9 @@ 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;
@ -471,35 +564,20 @@ gst_ffmpegmux_register (GstPlugin * plugin)
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);
@ -515,6 +593,11 @@ gst_ffmpegmux_register (GstPlugin * plugin)
/* 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;
}
@ -530,8 +613,15 @@ gst_ffmpegmux_register (GstPlugin * plugin)
/* 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;
}

View file

@ -39,7 +39,6 @@ struct _GstProtocolInfo
{
GstPad *pad;
int flags;
GstByteStream *bs;
gboolean eos;
};
@ -53,7 +52,6 @@ gst_ffmpegdata_open (URLContext * h, const char *filename, int flags)
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) {
@ -85,7 +83,6 @@ gst_ffmpegdata_open (URLContext * h, const char *filename, int flags)
h->priv_data = (void *) info;
h->is_streamed = FALSE;
h->flags = 0;
h->max_packet_size = 0;
return 0;
@ -102,7 +99,7 @@ gst_ffmpegdata_peek (URLContext * h, unsigned char *buf, int size)
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;
@ -202,7 +199,7 @@ gst_ffmpegdata_write (URLContext * h, unsigned char *buf, int size)
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);
@ -225,6 +222,7 @@ gst_ffmpegdata_seek (URLContext * h, offset_t pos, int whence)
info = (GstProtocolInfo *) h->priv_data;
if (h->flags == URL_RDONLY) {
/* get data (typefind hack) */
if (gst_bytestream_tell (info->bs) != gst_bytestream_length (info->bs)) {
gchar buf;
@ -239,6 +237,7 @@ gst_ffmpegdata_seek (URLContext * h, offset_t pos, int whence)
/* 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:
@ -255,7 +254,7 @@ gst_ffmpegdata_seek (URLContext * h, offset_t pos, int whence)
break;
}
switch (info->flags) {
switch (h->flags) {
case URL_RDONLY: {
GstEvent *event;
guint8 *data;
@ -303,7 +302,8 @@ gst_ffmpegdata_seek (URLContext * h, offset_t pos, int whence)
}
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) {
@ -331,7 +331,7 @@ gst_ffmpegdata_close (URLContext * h)
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);